通过内核模块按fd强制tcp的quickack方法

server/2025/1/20 14:06:34/

一、背景

tcp的quickack功能是为了让ack迅速回发,快速响应,减少网络通讯时延,属于一个优化项,但是tcp的quickack是有配额限制的,配置是16个quick,也就是短时间内quickack了16次以后,这个配额为0了以后,就算当前是quickack模式(非pingpong模式),系统仍然可能不进行quickack发送。为什么用“可能”呢?因为还有一些其他的条件检查,这些条件是或的关系,只要有一条满足了,系统就会quickack,其中,最常满足的是通过设置路由规则的quickack,让相关的tcp连接的fd都能强制进行quickack,就算quickack配额已经为0了,也并不影响quickack的发送。

在下面第二章里,我们贴出了实现的源码,并进行成果展示,同时也会给出路由方式进行设置的方式和结果展示,在第三章里,会讲解第二章里的源码的实现原理。

在后面的博客里,我们会进行setsockopt的定制,增加一个force quickack的选项,避免反复的通过setsockopt来触发增加quickack配额。

另外,如果需求可以通过增加路由规则来满足那也没必要这么麻烦,直接参考 2.4 一节里的方法即可。通过增加路由规则的方式的不足在于它不能按照fd来进行指定fd的quickack强制,要强制就对所有适用于增加的这条路由规则的tcp连接一起起效了。

二、源码及成果展示

2.1 通过内核模块方式捕获setsockopt系统调用进行quickack配额填充的源码

源码如下:

#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_nameret = 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);}}}
#endifstatic 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;//MY_KERNEL_KLOG_INFO_HEXDUMP(icsk->icsk_ca_priv, 104);//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);}}}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 0mykern_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;
#endifreturn 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);

2.2 服务端和客户端的源码

server端的代码如下:

#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 1024int main() {int server_fd, new_socket;struct sockaddr_in address;int addrlen = sizeof(address);char buffer[BUFFER_SIZE] = {0};// 创建 socketif ((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);// 绑定 socketif (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");// 接受信息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 optval = 3; // 启用 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);}}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;
}

client端的代码如下:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>#define PORT 80
#define BUFFER_SIZE 1024int main() {int sock = 0;struct sockaddr_in serv_addr;char *message = "Hello from client";// 创建 socketif ((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;
}

2.3 成果展示

先在server的机器上,insmod 2.1一节里的源码编出的ko后,运行server程序,然后,在client的机器上运行client程序,注意上面 2.1 一节和 2.2 一节里的ip地址需要根据实际情况来改变。

server刚运行起来以后的打印:

运行client后,client的打印:

server这边的打印:

再看dmesg里的打印:

可以从上图中看到,server这边每次调用recv接受client这边发来的消息前,会进行依次setsockopt的系统调用从而触发了dmesg里的kprobe的逻辑,对应的内核的代码逻辑是:

这样,在判断是否进行quickack时,就会得到quickack的mode是1的结果,因为quickack被我们反复的设置回16了:

2.4 通过路由配置的方法和成果展示

首先强调一下,通过增加路由规则的方式的不足在于它不能按照fd来进行指定fd的quickack强制,要强制就对所有适用于增加的这条路由规则的tcp连接一起起效了。

反过来说,如果不需要细粒度的针对路由规则适用的tcp连接里部分使用quickack部分不适用quickack进行针对性配置的话,那么用本节的方法也是更加规范和更加通用。

适用本节的方法,还需要考虑网卡link down/up的变化的情况,link up以后,需要重新设置路由规则,避免路由规则丢失导致不能quickack。

2.4.1 路由配置方式

根据当前测试环境的网络,如下方式配置:

ip route add 10.100.130.0/24 quickack 1 dev enp0s31f6

设置后可以通过ip -d route来查看是否设置进去了,如下图,成功设置了quickack:

2.4.2 路由配置后可以看到quickack的检查一直都是1的

在做验证前,把服务端代码里的setsockopt的逻辑去掉:

重新启动server,并启动client后,可以如下图的dmesg里的打印看到,quickack mode一直是检查是1的:

三、实现原理

这一章我们讲的是 2.1 一节里展示的源码里的实现原理。

事实上,它是用kprobe方式来hook setsockopt系统调用,过滤出如果设置的是TCP_QUICKACK的type的话,根据设置指定一个一般不会去设置的值,去做额外的逻辑,补充quickack配额,但是这是需要客户端不断地进行setsockopt系统调用的。

下面的实现的原理介绍省去了之前的博客多次提到的tracepoint及kprobe获取函数指针的原理说明,有关这两个话题,参考之前的 内核tracepoint的注册回调及添加的方法_tracepoint 自定义回调-CSDN博客 和 内核模块里访问struct rq及获取rq_clock_task时间的方法-CSDN博客 博客。

3.1 内核里是通过__tcp_ack_snd_check函数来判断是否需要quickack

内核里判断tcp是否需要quickack,是在tcp_input.c里的__tcp_ack_snd_check函数里,如下图:

上图里的关键的函数是tcp_in_quickack_mode这个函数:

这个函数有两种情况回返回进行quickack:

1)当前的sock连接有使能quickack的路由规则与之对应,RTAX_QUICKACK这个标志位,我们可以通过strace来跟踪 2.4 一节里介绍的路由设置命令,可以跟踪到这个标志位设置。

2)当前的sock连接是设置的quickack模式的且quick计数不是0,实测发现,目前默认是适用quickack模式的,并不需要额外去做这样的设置,pingpong mode是false也就是使用quickack mode。除了使用quickack模式,还需要满足quick计数不是0的条件,而quick计数会在每次recv收后减一,减到0以后,上图中的(icsk->icsk_ack.quick && !inet_csk_in_pingpong_mode(sk))就是0了

另外,需要说明的是,为什么我们不直接kprobe tcp_in_quickack_mode这个函数,而是kprobe __tcp_ack_snd_check函数,是因为tcp_in_quickack_mode这个函数在kallsyms里是没有的:

而__tcp_ack_snd_check在kallsyms里是有的:

3.2 通过kprobe捕获tcp_setsockopt,进行quick计数的特殊处理

用户态是通过如下方式进行的设置:

这样,内核态在捕获tcp_setsockopt时,可以根据传下来的数值来进行特殊处理,一般来说,我们设置quickack的on/off时,就传0或者1的,我们设置3就是为了区分这种常规的设置,这样我们可以发现是我们的特殊设置,如下逻辑就可以判断出来(另外,下图中的i_p-dx == 12的12表示的是TCP_QUICKACK):

要注意,内核里是默认的以最后一个bit来判断是否使用quickack模式的:

所以,我们特殊的设置值也遵守这个基本规则,最后一个bit仍然是1,让bit1以上部分非0,来做特殊处理的判断条件。

上面逻辑里如何通过pt_regs拿到各个入参,有两个注意事项,我们会择机在后面的博客里详细展开:

1)当入参的大小大于了64bit后,要根据情况,后移传递参数用的寄存器,为了强调,我们在后面的博客里展开

2)在使用用户态数据时,需要考虑用户态的数据可能还没有缺页异常(还未映射),在内核态里直接使用(比如用memcpy)的话,就会出现内核态错误,使用copy_from_user可以避免这样的问题,详细原因见后面的博客


http://www.ppmy.cn/server/159902.html

相关文章

NSIS 创建一键安装程序

nsis 安装redis 、mysql 、jdk navicat、 notepad、 环境变量只能设置一次 &#xff0c;多次 只有最后一次生效 ; 函数&#xff1a;检查目录是否已在 PATH 中 Function setPath; 首先获取当前的 PATH 环境变量ReadEnvStr $0 PATH; 检查是否成功读取 PATH 环境变量StrCmp $0 &q…

【C++】list容器

目录 学习途径 list的使用 list的一些构造 迭代器说明 接口使用 迭代器失效问题 list和vector对比 模拟实现list 迭代器的模拟&#xff08;重点&#xff09; List.h文件 学习途径 在学习list之前&#xff0c;我们可以查询一些相关文档来学习&#xff01; 文档详情&a…

Redis学习笔记1【数据类型和常用命令】

Redis学习笔记 基础语法 1.数据类型 String: 最基本的类型&#xff0c;可以存储任何数据&#xff0c;例如文本或数字。示例值为 hello world。Hash: 用于存储键值对&#xff0c;适合存储对象或结构体。示例值为 {"name": "Jack", "age": 21}。…

sourceinsight主题配置 sourceinsight绿色护眼主题下载

良好的开发环境对提高效率和舒适度至关重要。Source Insight 作为一种流行的源代码编辑器,用户可以根据个人需求更换主题。本文将为您介绍“sourceinsight主题配置 sourceinsight绿色护眼主题下载”,帮助您使用中这一功能,并提供绿色护眼主题的下载资源。 图1:Source Insig…

02内存结构篇(D2_剖析运行数据区)

目录 学习前言 一、程序计数器 1. 作用 2. 存储的数据 3. 异常 三、Java虚拟机栈 1. 栈帧 1.1. 局部变量表 存储内容 存储容量 其他 1.2. 操作数栈 作用 存储内容 存储容量 1.3. 动态连接 1.4. 方法返回 1.5. 附加信息 2. 栈异常 四、本地方法栈 1. 本地方…

Spring Web MVC综合案例

承接上篇文章——Spring Web MVC探秘&#xff0c;在了解Spring Web MVC背后的工作机制之后&#xff0c;我们接下来通过三个实战项目&#xff0c;来进一步巩固一下前面的知识。 一、计算器 效果展示&#xff1a;访问路径&#xff1a;http://127.0.0.1:8080/calc.html 前端代码&a…

强推未发表!3D图!Transformer-LSTM+NSGAII工艺参数优化、工程设计优化!

目录 效果一览基本介绍程序设计参考资料 效果一览 基本介绍 1.Transformer-LSTMNSGAII多目标优化算法&#xff0c;工艺参数优化、工程设计优化&#xff01;&#xff08;Matlab完整源码和数据&#xff09; Transformer-LSTM模型的架构&#xff1a;输入层&#xff1a;多个变量作…

模块化架构与微服务架构,哪种更适合桌面软件开发?

前言 在现代软件开发中&#xff0c;架构设计扮演着至关重要的角色。两种常见的架构设计方法是模块化架构与微服务架构。它们各自有独特的优势和适用场景&#xff0c;尤其在C#桌面软件开发领域&#xff0c;模块化架构往往更加具有实践性。本文将对这两种架构进行对比&#xff0…