前言
在进行TCP/IP相关的开发过程中,经常需要查证丢包、错包、流不通等问题。由于TCP/IP转发面涉及软件和硬件、并且软件流程上函数多、分支多,无论是增加打印或是分析流程,都比较困难。
本系列文章会将常用的定位手段,做一简单的总结和说明,各位可根据问题的情况,选取一种或结合使用。
日志方式简介
- 虽然打印这种方式比较Low,但在有的情况下反而是最直接和有效的方式,有的情况下更是开发人员的杀手锏。
直接打印报文
- 直接在需要的地方调用类似下面的print_skb的函数即可。
void print_skb(struct sk_buff *skb) { if (skb) { char *buf = skb->data; int len = skb->len; int i = 0; printk("[%s:%d]Packet length = %#4x\n", __FUNCTION__, __LINE__, len); for (i = 0; i < len; i++){ if (i % 16 == 0) printk("%#4.4x", i); if (i % 2 == 0) printk(" "); printk("%2.2x", ((unsigned char *)buf)[i]); if (i % 16 == 15) printk("\n"); } printk("\n\n"); } }
值得一提的是,在有的内核版本中,上述代码打印不全,skb->data需要换成skb->mac_header。
另外,这种打印方式,如报文太多,会刷屏,可能将系统打宕机。
条件打印
- 根据报文中特定字段进行打印,需要在skb结构体中增加一个字段:
struct sk_buff { unsigned int dbg_flag; }
具体在某个位置,首先判断某skb是否需要被打印,比如要跟踪DHCP报文,则在某个入口或点的位置识别DHCP报文并将该标志位置上,具体可参考下面的代码段:
struct ethhdr *mh = (struct ethhdr *)(skb->mac_header); if(mh->h_proto != htons(ETH_P_IP)) { return; } struct iphdr *iph = (struct iphdr *)ip_hdr(skb); if(iph && iph->protocol != IPPROTO_UDP) { return ; } // ip header --> iph->ihl*4; struct udphdr *udph = (struct udphdr *) ((char *)iph + iph->ihl*4); if(udph && udph->source == htons(68) && udph->dest == htons(67)) { skb->dbg_flag = 1; }
之后,在后续其他点上,就可以直接根据这个flag来控制打印:
if (1 == skb->dbg_flag) { print_skb(skb); }
开关控制打印
- 通过某proc文件修改内核某变量,然后根据该变量来控制打印。
echo 1>/proc/net/logctl //假设logctl文件对应g_logctl变量
代码中的具体控制:
if (1 == g_logctl) { print_skb(skb); }
ratelimit限速打印
- 限速判断函数可以用net_ratelimit或printk_ratelimit;
if (net_ratelimit()) { print_skb(skb); } if (printk_ratelimit()) { print_skb(skb); }
这种打印,有个缺陷,有可能会被限速抑制掉而打印不出来,此时会有下面这种打印出现:
__ratelimit: 250 callbacks suppressed
具体的限速参数,可使用下面方式修改:
[qxhgd@localhost ~]$ cat /proc/sys/kernel/printk_ratelimit 5 [qxhgd@localhost ~]$ cat /proc/sys/kernel/printk_ratelimit_burst 10 # printk_ratelimit默认允许在5s内最多打印10条消息出来: /proc/sys/kernel/printk_ratelimit (多长时间)和 /proc/sys/kernel/printk_ratelimit_burst (在上述时间段内最多允许的消息数量)
自定义限速打印
- 也可以根据需要,使用系统时间来自行控制打印,比如下面代码段可控制每秒最多打印一个报文:
unsigned long g_lastTime = 0; #全局变量 if (jiffies - g_lastTime > 1 * HZ) { print_skb(skb); g_lastTime = jiffies; }