​内核调试技巧--systemtap定位丢包原因

作者:wqiangwang,腾讯 TEG 后台开发工程师

内核收发包,可能会由于backlog队列满、内存不足、包校验失败、特性开关如rpf、路由不可达、端口未监听等等因素将包丢弃。

在内核里面,数据包对应一个叫做skb(sk_buff结构)。当发生如上等原因丢包时,内核会调用***kfree_skb***把这个包释放(丢掉)。kfree_skb函数中已经埋下了trace点,并且通过__builtin_return_address(0)记录下了调用kfree_skb的函数地址并传给location参数,因此可以利用systemtap kernel.trace来跟踪kfree_skb获取丢包函数。考虑到该丢包函数可能调用了子函数,子函数继续调用子子函数,如此递归。为了揪出最深层的函数,本文通过举例几个丢包场景,来概述一种通用方法,来定位丢包原因及精确行号。

用例1:ospf hello组播报文被drop,导致ospf 邻居不能建立

方法:saddr是10.10.2.4的skb是我们关心的数据。

1、 drop_watch跟踪下kfree_skb,定位函数位置:

2、 查看ip_rcv_finish内核源码,编写stap脚本,通过pp()行号来跟踪执行流:

3、 从行号可知ip_route_input_noref返回错误,编写stap脚本,查看ip_route_input_noref的返回值:

返回-22,即-EINVAL。

4、 即ip_route_input_rcu返回错误,同样方法,通过pp()行号来跟踪执行流:

此路不通,看下原因:原来有些行号$saddr不能访问。

5、 此时需要码一码代码了,由于ospf的hello报文是组播,所以下图中红色方框的ipv4_is_multicast为真:

先看下__in_dev_get_rcu(dev)的返回值是否为null,同时也把dev->priv_flags的值打印出来:

__in_dev_get_rcu(dev)返回不为null,再看ip_check_mc_rcu函数的返回值:

返回值为0,同时通过打印的dev->priv_flags 为0x00029820,可判断netif_is_l3_slave为假:

另外ospf使用的组播ip为224.0.0.5或224.0.0.6,本例中使用的是224.0.0.5。因此ipv4_is_local_multicast为真:

最终精确定位到了ip_check_mc_rcu:

先看下in_dev->mc_hash是否为null,通过cast强转来访问结构体成员mc_hash:

则需要走如下分支:

需要遍历in_dev->mc_list,有两种方法,一种是通过crash工具查看,遍历in_dev->mc_list,输出im->multiaddr,如下:

既然我们的主题是stap,那么继续stap,这里通过嵌入c代码来遍历mc_list,输出multiaddr到dmesg:

存在接口加入了224.0.0.5组播组,革命尚未成功,继续跟踪下面的代码:

同样的方式,编写stap脚本或者crash通过struct展开im->sources来查看。

至此,一目了然,原来报文接收的接口没有加入组播组224.0.0.5。google一下:

https://www.cnblogs.com/my_life/articles/6077569.html

综述:那为什么ens5没有加入组播组呢,这要从ospf的原理来说起,ospf建立邻居的时候,是不需要指定接口的,那用于建立邻居的接口是如何选择的呢:实际上是根据指定的area network配置来选择的。当配置area network的时候,会查看系统当前路由,选择合适的接口加入组播组,进而创建邻居。

用例2:gre报文的version字段被置位,导致skb被drop。

方法:saddr是10.10.2.2的skb是我们关心的数据。

1、 依然是drop_watch跟踪下kfree_skb,定位函数位置:

2、 查看gre_rcv的源码,有两个内核模块都存在gre_rcv,由于上面的drop_watch已经定位为ffffffffc099411a,通过crash查看模块的加载地址,来确定调用的是哪一个模块的gre_rcv:

3、 依然是pp()行号来跟踪执行流,和上述不同的是,gre是模块的形式,使用stap的probe module的方式:

综述:这里大家要小心,这个存在”写”skb相关成员,不建议嵌入c代码的方式,去修改skb,如果真的要怎么做,记得恢复回来。

用例3: overlay报文根据路由找到出的vxlan隧道接口后,没有从underlay接口出,导致网络不通:

方法:saddr是172.16.14.2的skb是我们关心的数据。

1、 依然是drop_watch跟踪下kfree_skb,定位函数位置:

2、 查看vxlan_xmit_one,依然是pp()行号来跟踪执行流:

综述:从接口中取到underlay信息后,再去查找路由,由于underlay路由不存在,导致skb被drop。这个问题较简单,也可直接通过查看ip route定位。但是细心的你一定会发现一个有趣的问题,关键字overlay arp,欢迎读者来撩。

总结,丢包精确定位行的方法:

1、 drop_watch先定位函数。

2、 使用pp()定位行。必要的时候,编写一些脚本,直接抄写内核代码或者调用stap库就可以了。

3、 递归重复步骤1和2。

是不是跃跃欲试的感觉。

最后:

这里”rpf检查”,”accept_local检查”留给读者来尝试了。实际上systemtap可以做的更多,如内存泄露,系统调用失败,统计流量等等,github上也有很多实用的脚本。

参考链接:

https://cloud.tencent.com/developer/article/1631874

https://www.cnblogs.com/wanpengcoder/p/11768483.html

  • 0
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值