当前位置: 首页 > article >正文

定制setsockopt只设置一次实现指定sock的永久quickack

一、背景

在之前的博客 通过内核模块按fd强制tcp的quickack方法-CSDN博客 里,我们给出了通过kprobe来捕获setsockopt,进行定制化的quickack相关的quick的数值更新,这种方式用的kprobe来做,虽然可以不用动到内核镜像,但是从方案上肯定还是比较临时的,另外,这种方式需要每次recv前都要设置setsockopt,这也是很奇怪的行为。

当然,我们可以通过之前的博客 通过内核模块按fd强制tcp的quickack方法-CSDN博客 里的 2.4 一节里的方法,设置路由表来实现正式的方案,这当然是可以的,但是相对不够灵活,也不能根据一个具体的socket连接来指定使能和不使能quickack,本文是通过修改内核逻辑,增加一个force quickack的标志位,放到sock的priv数据里,来进行定制化。

在第二章里,我们给出源码修改的部分,和实验用的程序,在第三章里,我们进行原理和细节的阐述。

二、源码及实验结果

我们在 2.1 里展示内核源码修改的部分,在 2.2 里贴出客户端代码和服务端代码,其中客户端代码和之前的 通过内核模块按fd强制tcp的quickack方法-CSDN博客 博客里的客户端代码一致,服务端代码略有变化。在 2.3 里我们展示运行结果。

2.1 内核源码部分

在setsockopt时,当设置tcp的sockopt时:

在执行__tcp_sock_set_quickack时,使用inet_connection_sock里的icsk_ca_priv区域进行标志位设置:

#if 1
	if (val & 4)
		*((u64*)((u64)(inet_csk(sk)->icsk_ca_priv) + sizeof(inet_csk(sk)->icsk_ca_priv) - 8)) = 1;
#endif

在内核进行检查是否进行quickack时增加该标志位的判断:

#if 1
		|| *((u64*)((u64)(inet_csk(sk)->icsk_ca_priv) + sizeof(inet_csk(sk)->icsk_ca_priv) - 8))
#endif

为了做验证,我们修改了用于检查和判断是否进行quickack的最终出口部分的代码,增加了检查是否是我们刚加的标志位检查而进行quickack的打印:

#if 1
		if (*((u64*)((u64)(inet_csk(sk)->icsk_ca_priv) + sizeof(inet_csk(sk)->icsk_ca_priv) - 8))) {
			printk("send quickack by force quickack!\n");
		}
#endif

2.2 测试用的客户端代码和服务端代码

客户端代码:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>

#define PORT 80
#define BUFFER_SIZE 1024

int main() {
    int sock = 0;
    struct sockaddr_in serv_addr;
    char *message = "Hello from client";

    // 创建 socket
    if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
        printf("\n Socket creation error \n");
        return -1;
    }

    serv_addr.sin_family = AF_INET;
    serv_addr.sin_port = htons(PORT);

    // 转换 IPv4 和 IPv6 地址从文本到二进制
    if (inet_pton(AF_INET, "10.100.130.87", &serv_addr.sin_addr) <= 0) {
        printf("\nInvalid address/ Address not supported \n");
        return -1;
    }

    // 连接到服务器
    if (connect(sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) {
        printf("\nConnection Failed \n");
        return -1;
    }

    // 循环发送信息
    while (1) {
        send(sock, message, strlen(message), 0);
        printf("Message sent: %s\n", message);
        //sleep(1); // 每秒发送一次
        usleep(1);
    }

    close(sock);
    return 0;
}

服务端代码:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/tcp.h>

#define PORT 80
#define BUFFER_SIZE 1024

int main() {
    int server_fd, new_socket;
    struct sockaddr_in address;
    int addrlen = sizeof(address);
    char buffer[BUFFER_SIZE] = {0};

    // 创建 socket
    if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) {
        perror("Socket failed");
        exit(EXIT_FAILURE);
    }

    {
        // int optval = 1; // 启用 QUICKACK 选项
        // socklen_t optlen = sizeof(optval);
        // // 设置 TCP_QUICKACK 选项
        // if (setsockopt(server_fd, IPPROTO_TCP, TCP_QUICKACK, &optval, optlen) < 0) {
        //     perror("setsockopt");
        //     close(server_fd);
        //     exit(EXIT_FAILURE);
        // }
    }

    // 设置地址结构
    address.sin_family = AF_INET;
    address.sin_addr.s_addr = inet_addr("10.100.130.87");
    address.sin_port = htons(PORT);

    // 绑定 socket
    if (bind(server_fd, (struct sockaddr *)&address, sizeof(address)) < 0) {
        perror("Bind failed");
        close(server_fd);
        exit(EXIT_FAILURE);
    }

    // 开始监听
    if (listen(server_fd, 3) < 0) {
        perror("Listen failed");
        close(server_fd);
        exit(EXIT_FAILURE);
    }

    printf("Server is listening on %s:%d\n", "10.100.130.87", PORT);

    // 循环接受信息
    while (1) {
        if ((new_socket = accept(server_fd, (struct sockaddr *)&address, (socklen_t*)&addrlen)) < 0) {
            perror("Accept failed");
            continue;
        }

        printf("Connected to client\n");

        {
            int optval = 5; // 启用 QUICKACK 选项
            socklen_t optlen = sizeof(optval);
            // 设置 TCP_QUICKACK 选项
            if (setsockopt(new_socket, IPPROTO_TCP, TCP_QUICKACK, &optval, optlen) < 0) {
                perror("setsockopt");
                close(new_socket);
                exit(EXIT_FAILURE);
            }
        }

        // 接受信息
        while (1) {

            // {
            //     int optval = 1; // 启用 QUICKACK 选项
            //     socklen_t optlen = sizeof(optval);
            //     // 设置 TCP_QUICKACK 选项
            //     if (setsockopt(server_fd, IPPROTO_TCP, TCP_NODELAY, &optval, optlen) < 0) {
            //         perror("setsockopt");
            //         close(server_fd);
            //         exit(EXIT_FAILURE);
            //     }
            // }

            int valread = read(new_socket, buffer, BUFFER_SIZE);
            if (valread > 0) {
                buffer[valread] = '\0';  // 确保字符串以 null 结束
                printf("Received: %s\n", buffer);
            } else {
                break; // 客户端关闭连接
            }
        }

        printf("Client disconnected\n");
        close(new_socket);
    }

    close(server_fd);
    return 0;
}

2.3 运行结果

运行服务端程序:

./server_forcequickack

再运行客户端程序:

./client

从dmesg里可以看到我们增加的打印(确认是我们加的force quickack标志位导致的判断出需要quickack):

三、原理阐述

关于quickack的细节和原理参考之前的 通过内核模块按fd强制tcp的quickack方法-CSDN博客 博客。这里讲的是这篇博客说的定制setsockopt避免每次进行setsockopt的部分。

其实就是用的inet_connection_sock的icsk_ca_priv部分:

我们通过下面内核ko模块去捕获所有设置了setsockopt时的该icsk_ca_priv的内容情况。

下面的内核源码部分是之前的  通过内核模块按fd强制tcp的quickack方法-CSDN博客 博客里,打开了下面截图里的红色框出部分的打印:

上图中的是用于打印一段内存段的内容,有关内核里一些其他调试用的宏,有之前的博客做过整理 内核调试日志相关函数及宏_如何在ftrace中打印dump stack-CSDN博客 。

用来抓取该icsk_ca_priv内容的内核模块源码:

#include <linux/module.h>
#include <linux/capability.h>
#include <linux/sched.h>
#include <linux/uaccess.h>
#include <linux/proc_fs.h>
#include <linux/ctype.h>
#include <linux/seq_file.h>
#include <linux/poll.h>
#include <linux/types.h>
#include <linux/ioctl.h>
#include <linux/errno.h>
#include <linux/stddef.h>
#include <linux/lockdep.h>
#include <linux/kthread.h>
#include <linux/sched.h>
#include <linux/delay.h>
#include <linux/wait.h>
#include <linux/init.h>
#include <asm/atomic.h>
#include <trace/events/workqueue.h>
#include <linux/sched/clock.h>
#include <linux/string.h>
#include <linux/mm.h>
#include <linux/interrupt.h>
#include <linux/tracepoint.h>
#include <trace/events/osmonitor.h>
#include <trace/events/sched.h>
#include <trace/events/irq.h>
#include <trace/events/kmem.h>
#include <linux/ptrace.h>
#include <linux/uaccess.h>
#include <asm/processor.h>
#include <linux/sched/task_stack.h>
#include <linux/nmi.h>
#include <asm/apic.h>
#include <linux/version.h>
#include <linux/sched/mm.h>
#include <asm/irq_regs.h>
#include <linux/kallsyms.h>
#include <linux/kprobes.h>
#include <linux/stop_machine.h>

MODULE_LICENSE("GPL");
MODULE_AUTHOR("zhaoxin");
MODULE_DESCRIPTION("Module for debug quickack.");
MODULE_VERSION("1.0");



struct kern_tracepoint {
    void *callback;
    struct tracepoint *ptr;
    bool bregister;
};
static void clear_kern_tracepoint(struct kern_tracepoint *tp)
{
    if (tp->bregister) {
        tracepoint_probe_unregister(tp->ptr, tp->callback, NULL);
    }
}

#define INIT_KERN_TRACEPOINT(tracepoint_name) \
    static struct kern_tracepoint mykern_##tracepoint_name = {.callback = NULL, .ptr = NULL, .bregister = false};


#define TRACEPOINT_CHECK_AND_SET(tracepoint_name)                                             \
    static void tracepoint_name##_tracepoint_check_and_set(struct tracepoint *tp, void *priv) \
    {                                                                                \
        if (!strcmp(#tracepoint_name, tp->name))                                     \
        {                                                                            \
            ((struct kern_tracepoint *)priv)->ptr = tp;                          \
            return;                                                                  \
        }                                                                            \
    }

//INIT_KERN_TRACEPOINT(sched_switch)
//TRACEPOINT_CHECK_AND_SET(sched_switch)
//INIT_KERN_TRACEPOINT(sched_waking)
//TRACEPOINT_CHECK_AND_SET(sched_waking)


typedef unsigned long (*kallsyms_lookup_name_func)(const char *name);
kallsyms_lookup_name_func _kallsyms_lookup_name_func;

void* get_func_by_symbol_name_kallsyms_lookup_name(void)
{
    int ret;
    void* pfunc = NULL;
	struct kprobe kp;
	memset(&kp, 0, sizeof(kp));
	kp.symbol_name = "kallsyms_lookup_name";
	kp.pre_handler = NULL;
	kp.addr = NULL;	// 作为强调,提示使用symbol_name
	ret = register_kprobe(&kp);
	if (ret < 0) {
		printk("register_kprobe fail!\n");
		return NULL;
	}
	printk("register_kprobe succeed!\n");
    pfunc = (void*)kp.addr;
	unregister_kprobe(&kp);
    return pfunc;
}

void* get_func_by_symbol_name(const char* i_symbol)
{
    if (_kallsyms_lookup_name_func == NULL) {
        return NULL;
    }
    return _kallsyms_lookup_name_func(i_symbol);
}

#include <uapi/linux/rtnetlink.h>
#include <net/sock.h>
#include <net/inet_connection_sock.h>


#include <linux/netdevice.h>
#include <linux/inetdevice.h>
#include <linux/ip.h>
#include <net/dst.h>
#include <net/route.h>
#include <net/tcp.h>
#include <linux/inet.h>
#include <linux/sockptr.h>

// void print_dst_entry(struct dst_entry *dst) {
//     struct rtable *rt = (struct rtable *)dst;
//     struct in_device *in_dev;
 
//     if (!dst)
//         return;
 
//     in_dev = __in_dev_get_rcu(rt->u.dst.dev);
 
//     printk(KERN_INFO "Dst Entry Info:\n");
//     printk(KERN_INFO "  Input Device: %s\n", rt->u.dst.dev->name);
//     printk(KERN_INFO "  Output Device: %s\n", rt->u.dst.dev->name);
//     if (in_dev) {
//         printk(KERN_INFO "  In Device MTU: %d\n", in_dev->mtu);
//         printk(KERN_INFO "  In Device Output MTU: %d\n", in_dev->output_mtu);
//     }
//     printk(KERN_INFO "  Expires: %ld\n", dst->expires);
//     printk(KERN_INFO "  Flags: 0x%lx\n", dst->flags);
//     printk(KERN_INFO "  Last Use: %lu\n", dst->lastuse);
//     printk(KERN_INFO "  Obsolete: %lu\n", dst->obsolete);
//     printk(KERN_INFO "  Hash Chain: %p\n", dst->dn.next);
//     printk(KERN_INFO "  Input Hash: 0x%lx\n", dst->hash);
//     printk(KERN_INFO "  Output Hash: 0x%lx\n", dst->child_mask);
//     printk(KERN_INFO "  Reference Count: %d\n", atomic_read(&dst->__refcnt));
//     printk(KERN_INFO "  Use Count: %d\n", dst->use);
//     printk(KERN_INFO "  Wireless Use Count: %d\n", dst->wireless_ref);
//     printk(KERN_INFO "  Last Metric Update: %lu\n", dst->last_metric_update);
//     printk(KERN_INFO "  Protocol Specific Data: %p\n", dst->input);
//     printk(KERN_INFO "  Optimistic ACK Prediction: %d\n", tcp_hdr(rt->u.dst.xfrm)->ack_seq - 1);
// }

void print_rtable_info(struct rtable *rt) {
    if (!rt) {
        printk(KERN_ERR "rtable is NULL\n");
        return;
    }

    // 打印 rtable 的基本信息
    printk(KERN_INFO "Route Entry Information:\n");
    //printk(KERN_INFO "Destination Address: %pI4\n", &rt->dst.dest.addr);
    printk(KERN_INFO "Gateway: %pI4\n", &rt->rt_gw4);
    printk(KERN_INFO "Flags: 0x%x\n", rt->rt_flags);
    printk(KERN_INFO "Type: %u\n", rt->rt_type);
    printk(KERN_INFO "Input Interface: %d\n", rt->rt_iif);
    printk(KERN_INFO "Uses Gateway: %u\n", rt->rt_uses_gateway);
    printk(KERN_INFO "MTU: %u\n", rt->rt_pmtu);
    printk(KERN_INFO "MTU Locked: %u\n", rt->rt_mtu_locked);
    printk(KERN_INFO "Generation ID: %d\n", rt->rt_genid);
}

// void print_dst_entry_info(struct dst_entry *dst) {
//     if (!dst) {
//         printk(KERN_ERR "dst_entry is NULL\n");
//         return;
//     }

//     // 打印 dst_entry 的基本信息
//     printk(KERN_INFO "Destination Address: %pI4\n", &dst->dest.addr);
//     printk(KERN_INFO "Flags: 0x%x\n", dst->flags);
//     //printk(KERN_INFO "Reference Count: %d\n", atomic_read(&dst->refcnt));
    
//     // 如果是 IPv4 路由表
//     // {
//     //     struct rtable *rt = (struct rtable *)dst; // 转换为 rtable
//     //     printk(KERN_INFO "Gateway: %pI4\n", &rt->rt_gw);
//     //     printk(KERN_INFO "Interface: %s\n", rt->dev->name);
//     //     printk(KERN_INFO "Metric: %u\n", rt->rt_metric);
//     // }

//     // 可以添加更多字段的信息
// }

int _notquickmodecount = 0;

static bool tcp_in_quickack_mode(struct sock *sk)
{
    struct inet_sock *inet = inet_sk(sk);
	const struct inet_connection_sock *icsk = inet_csk(sk);
	const struct dst_entry *dst = __sk_dst_get(sk);
    u32 dst_metric_ret = 0;
    u32 dst_v = 0;
    bool ret;

    if (dst) {
        dst_v = 1;
        dst_metric_ret = dst_metric(dst, RTAX_QUICKACK);
    }

	ret = (dst && dst_metric(dst, RTAX_QUICKACK)) ||
		(icsk->icsk_ack.quick && !inet_csk_in_pingpong_mode(sk));
    if (ret) {
        printk("pid[%d]quick mode[%u], dst[%u]dst_metric_ret[%u]icsk->icsk_ack.quick[%u]quickmode[%u]src[%pI4]dst[%pI4]src_port[%u]dst_port[%u]\n", 
            current->pid, ret ? 1 : 0,
            dst_v, dst_metric_ret, (u32)icsk->icsk_ack.quick, (!inet_csk_in_pingpong_mode(sk))?1:0,
            &sk->sk_rcv_saddr, &sk->sk_daddr, ntohs(inet->inet_sport), ntohs(inet->inet_dport));
    }
    else {
        if (_notquickmodecount > 10) {

        }
        else {
            _notquickmodecount++;
            printk("pid[%d]quick mode[%u], dst[%u]dst_metric_ret[%u]icsk->icsk_ack.quick[%u]quickmode[%u]src[%pI4]dst[%pI4]src_port[%u]dst_port[%u]\n", 
                current->pid, ret ? 1 : 0,
                dst_v, dst_metric_ret, (u32)icsk->icsk_ack.quick, (!inet_csk_in_pingpong_mode(sk))?1:0,
                &sk->sk_rcv_saddr, &sk->sk_daddr, ntohs(inet->inet_sport), ntohs(inet->inet_dport));
        }
    }
    return ret;
}

static int haslog = 0;

struct kprobe _kp;
struct kprobe _kp1;
 
// __tcp_ack_snd_check
int kprobecb_tcp_ack_snd_check(struct kprobe* i_k, struct pt_regs* i_p)
{
    //printk("kprobecb_tcp_ack_snd_check enter");
    //unsigned long arg1 = regs->di;
    struct sock *sk = (struct sock *) i_p->di;
    struct inet_sock *inet = inet_sk(sk);
    __be32 target_ip, dst_ip;
    target_ip = in_aton("10.100.130.87");
    dst_ip = in_aton("10.100.130.103");
    if (sk->sk_rcv_saddr == target_ip 
        && sk->sk_daddr == dst_ip) {
        if (tcp_in_quickack_mode(sk)) {
            //haslog = 1;
            //printk("quick mode, src[%pI4]dst[%pI4]src_port[%u]dst_port[%u]\n", &sk->sk_rcv_saddr, &sk->sk_daddr, ntohs(inet->inet_sport), ntohs(inet->inet_dport));
            //printk(KERN_INFO "Destination IP: %pI4\n", &sk->sk_daddr);
            //print_rtable_info((struct rtable *)__sk_dst_get(sk));
        }
        else {
            //printk("NOT quick mode, src[%pI4]dst[%pI4]src_port[%u]dst_port[%u]\n", &sk->sk_rcv_saddr, &sk->sk_daddr, ntohs(inet->inet_sport), ntohs(inet->inet_dport));
            //printk("NOT quick mode\n");
        }
    }
    return 0;
}

#define MY_KERNEL_KLOG_INFO_HEXDUMP( addr, size) \
    do {    \
        print_hex_dump(KERN_INFO, "hex_dump:", DUMP_PREFIX_NONE, 32, 4, addr, size, true);  \
    } while (0)

static void tcp_quickack_config(struct sock *sk)
{
	struct inet_connection_sock *icsk = inet_csk(sk);
    
    if (icsk->icsk_ack.quick != 16) {
        icsk->icsk_ack.quick = 16;
        printk("pid[%d]set quick = 16\n", current->pid);
        
    }
	// unsigned int quickacks = tcp_sk(sk)->rcv_wnd / (2 * icsk->icsk_ack.rcv_mss);

	// if (quickacks == 0)
	// 	quickacks = 2;
	// quickacks = min(quickacks, max_quickacks);
	// if (quickacks > icsk->icsk_ack.quick)
	// 	icsk->icsk_ack.quick = quickacks;
}

#if 0
void kprobecb_tcp_sock_set_quickack(struct kprobe* i_k, struct pt_regs* i_p,
    unsigned long i_flags)
{
    struct sock *sk = (struct sock*) i_p->di;
    int* pval = i_p->r10;
    int val;
    int len = i_p->r8;
    //tcp_enter_quickack_mode(sk, 16);
    //printk("val=%d\n", val);
    printk("len[%d]\n", len);
    if (len == 4) {
        val = *pval;
        if ((val & 1) && val != 1) {
            printk("111 val=%d\n", val);
            //tcp_enter_quickack_mode(sk, 16);
            //tcp_incr_quickack(sk, 16u);
        }
    }

}
#endif

static int handler_pre(struct pt_regs *regs) {
    // 打印 pt_regs 中的内容
    printk(KERN_INFO "pt_regs contents:\n");
    printk(KERN_INFO "RIP: 0x%lx\n", regs->ip);
    printk(KERN_INFO "RSP: 0x%lx\n", regs->sp);
    printk(KERN_INFO "RBP: 0x%lx\n", regs->bp);
    printk(KERN_INFO "RAX: 0x%lx\n", regs->ax);
    printk(KERN_INFO "RBX: 0x%lx\n", regs->bx);
    printk(KERN_INFO "RCX: 0x%lx\n", regs->cx);
    printk(KERN_INFO "RDX: 0x%lx\n", regs->dx);
    printk(KERN_INFO "RSI: 0x%lx\n", regs->si);
    printk(KERN_INFO "RDI: 0x%lx\n", regs->di);
    printk(KERN_INFO "R8: 0x%lx\n", regs->r8);
    printk(KERN_INFO "R9: 0x%lx\n", regs->r9);
    printk(KERN_INFO "R10: 0x%lx\n", regs->r10);
    printk(KERN_INFO "R11: 0x%lx\n", regs->r11);
    printk(KERN_INFO "R12: 0x%lx\n", regs->r12);
    printk(KERN_INFO "R13: 0x%lx\n", regs->r13);
    printk(KERN_INFO "R14: 0x%lx\n", regs->r14);
    printk(KERN_INFO "R15: 0x%lx\n", regs->r15);

    return 0; // 继续执行被探测的函数
}

int kprobecb_tcp_sock_set_quickack(struct kprobe* i_k, struct pt_regs* i_p)
{
    struct sock *sk = (struct sock*) i_p->di;
    struct inet_connection_sock *icsk = inet_csk(sk);
    //sockptr_t pval = (sockptr_t)i_p->cx;
    int val;
    int len = i_p->r9;

    

    //tcp_enter_quickack_mode(sk, 16);
    //printk("val=%d\n", val);
    // printk("len[%d][0x%llx][0x%llx][0x%llx][0x%llx][0x%llx]\n", len,
    //     i_p->di, i_p->si, i_p->dx, i_p->cx, i_p->r9);
    
    if (len == 4) {
        copy_from_user(&val, i_p->cx, 4);
        //memcpy(&val, i_p->cx, 4);
        // if (copy_from_sockptr(&val, pval, sizeof(val))) {
        //     return 0;
        // }
        if (i_p->dx == 12) {
            if ((val & 1) && val != 1) {
                //printk("111 val=%d\n", val);
                //handler_pre(i_p);
                //tcp_enter_quickack_mode(sk, 16);
                tcp_quickack_config(sk);

                MY_KERNEL_KLOG_INFO_HEXDUMP(icsk->icsk_ca_priv, 104);
                printk("0x%llx\n",
                    *((u64*)((u64)(inet_csk(sk)->icsk_ca_priv) + sizeof(inet_csk(sk)->icsk_ca_priv) - 8)));
                
            }
        }
        
    }
    return 0;
}
 
int kprobe_register_func_tcp_ack_snd_check(void)
{
    int ret;
    memset(&_kp, 0, sizeof(_kp));
    _kp.symbol_name = "__tcp_ack_snd_check";
    _kp.pre_handler = kprobecb_tcp_ack_snd_check;
    ret = register_kprobe(&_kp);
	if (ret < 0) {
		printk("register_kprobe fail!\n");
		return -1;
	}
    return 0;
}

int kprobe_register_func_tcp_sock_set_quickack(void)
{
    int ret;
    memset(&_kp1, 0, sizeof(_kp1));
    _kp1.symbol_name = "tcp_setsockopt";
    _kp1.pre_handler = kprobecb_tcp_sock_set_quickack;
    ret = register_kprobe(&_kp1);
	if (ret < 0) {
		printk("register_kprobe fail!\n");
		return -1;
	}
    return 0;
}
 
void kprobe_unregister_func_tcp_ack_snd_check(void)
{
    unregister_kprobe(&_kp);
}

void kprobe_unregister_func_tcp_sock_set_quickack(void)
{
    unregister_kprobe(&_kp1);
}


static int __init testquickack_init(void)
{
    _kallsyms_lookup_name_func = get_func_by_symbol_name_kallsyms_lookup_name();

    kprobe_register_func_tcp_ack_snd_check();
    kprobe_register_func_tcp_sock_set_quickack();
    
#if 0
    mykern_sched_waking.callback = cb_sched_waking;
    for_each_kernel_tracepoint(sched_waking_tracepoint_check_and_set, &mykern_sched_waking);
    if (!mykern_sched_waking.ptr) {
        printk(KERN_ERR "mykern_sched_waking register failed!\n");
        return -1;
    }
    else {
        printk(KERN_INFO "mykern_sched_waking register succeeded!\n");
    }
    tracepoint_probe_register(mykern_sched_waking.ptr, mykern_sched_waking.callback, NULL);
    mykern_sched_waking.bregister = 1;
#endif

    return 0;
}

static void __exit testquickack_exit(void)
{
    //clear_kern_tracepoint(&mykern_sched_waking);

    //tracepoint_synchronize_unregister();
    
    kprobe_unregister_func_tcp_ack_snd_check();
    kprobe_unregister_func_tcp_sock_set_quickack();
}

module_init(testquickack_init);
module_exit(testquickack_exit);

从下图中抓到的情况可以看到icsk_ca_priv的最后8个字节是没有使用的,且都是0:

所以,我们用这8个字节来存放我们的定制化setsockopt的标志位。

为了打印这个icsk_ca_priv的状态,我们借用tcp_setsockopt的probe的逻辑,传值3,这样不会触发设置icsk_ca_priv的逻辑,但是可以打印icsk_ca_priv的内容。进行打印的源码逻辑:

可以从下图中看到,我们打印我们通过setsockopt进行设置force quickack的sock的icsk_ca_priv的内容就可以看到最后一个u64被赋值成了1:


http://www.kler.cn/a/512003.html

相关文章:

  • python3GUI--仿崩坏三二次元登录页面(附下载地址) By:PyQt5
  • C#中System.Text.Json:从入门到精通的实用指南
  • Java开发提速秘籍:巧用Apache Commons Lang工具库
  • 第3章:Python TDD更新测试用例测试Dollar类
  • 战场物联网:通信挑战与最新解决方案综述
  • 【Linux系统编程】—— 深度解析进程等待与终止:系统高效运行的关键
  • 如何在Nginx服务器上配置访问静态文件目录并提供文件下载功能
  • 实用技巧:快速修复电脑dxgidebug.dll缺失
  • 什么是报文的大端和小端,有没有什么记忆口诀?
  • WPF基础 | 初探 WPF:理解其核心架构与开发环境搭建
  • javaEE初阶(计算机是如何工作的(2) )
  • 用Zig开发Web后端独特好处
  • k8s 部署kafka单机和集群
  • 使用 Parcel 和 NPM 脚本进行打包
  • 【大数据】机器学习------聚类
  • 常见的图形库概览-03-D3.js 入门例子
  • 计算机系统原理:一些断言
  • Transformer详解:Attention机制原理
  • Vue2:el-tree用scope slot为每一个节点添加一个鼠标悬浮时出现的右对齐的按钮
  • C# 事件(Event)详解
  • C++和OpenGL实现3D游戏编程【连载21】——父物体和子物体模式实现
  • PyTorch框架——基于WebUI:Gradio深度学习ShuffleNetv2神经网络蔬菜图像识别分类系统
  • 【深度学习】傅里叶变换
  • WPS不登录无法使用基本功能的解决方案
  • MySQL快速入门——库的操作
  • Golang学习笔记_28——工厂方法模式