本周遇到点问题,发现某个进程触发netlink请求后,会长时间hang(1~2s),需要进行深入归因排查。
归因的主要思路是先找到hang的位置,即找到现场的调用栈,由于触发时会出现秒级的hang行为,此处不需要太过复杂的机制,周期性cat proc目录的stack文件即可。
在找到调用栈后,就需要反查到底是哪些进程触发netlink请求了,这里的难点在于,进程的hang行为并非由于当前线程导致的,因此需要一种全局维度的机制来进行综合判断。
不少读者应该会有种想法,要是能够通过tcpdump抓包就好了,因为通常而言,进程会将nlmsg的port id字段直接设置为当前的pid,较易分析。
如果你有这种想法,恭喜你,本文将会向你介绍这种机制。
如果你调研过netlink的实现方式应该不难发现,它也是用skbuff来承载nlmsg结构体的,而该结构体中通常会包含进程信息,因而广义上来说,netlink流量也是网络流量的一种,我们没有理由不为其开发一种dump机制,来协助进行问题定位。
这个机制就是netlink monitor,内核模块名就是nlmon,使用方式也很简单,大致分为如下几步:
- 挂载nlmon内核模块
- 创建nlmon设备
- nlmon设备设置link up
- tcpdump抓包
netlink monitor是一个内核模块,一般情况下是不会默认加载的,毕竟主要还是用于问题定位的,并且也是性能有损的。
modprobe nlmon
我们查看下模块的挂载情况,如下:
# lsmod | grep nlmon
nlmon 16384 0
挂载模块后,为了能够正常抓包,还需要创建一个virtual interface,供pcap挂载filter,这里使用的是ip命令:
ip link add dev nlmon0 type nlmon
如果没有报错,你就能够看到新创建的interface了:
# ip link
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
4: ens1f0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP mode DEFAULT group default qlen 1000
link/ether b8:59:9f:f0:80:88 brd ff:ff:ff:ff:ff:ff
5: ens1f1: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
link/ether b8:59:9f:f0:80:89 brd ff:ff:ff:ff:ff:ff
6: virbr0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN mode DEFAULT group default qlen 1000
link/ether 52:54:00:db:b3:66 brd ff:ff:ff:ff:ff:ff
7: virbr0-nic: <BROADCAST,MULTICAST> mtu 1500 qdisc fq_codel master virbr0 state DOWN mode DEFAULT group default qlen 1000
link/ether 52:54:00:db:b3:66 brd ff:ff:ff:ff:ff:ff
8: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN mode DEFAULT group default
link/ether 02:42:3f:3d:4c:b8 brd ff:ff:ff:ff:ff:ff
9: nlmon0: <NOARP> mtu 3776 qdisc noop state DOWN mode DEFAULT group default qlen 1000
link/netlink
新创建的设备默认是link down状态,使用前需要先link up:
ip link set dev nlmon0 up
至此,你就可以用tcpdump抓netlink流量了:
tcpdump -i nlmon0 -w nlmsg.pcap
由于tcpdump默认不带netlink的parser,因此只能dump成pcap文件,然后用wireshark之类的工具进行解析。
例如,我们执行ip addr
后,抓到的netlink流量解析如下:
最后,记得在排查完毕后,删除interface以及卸载模块:
ip link del dev nlmon0
rmmod nlmon
Happy Coding!
请问,这个能抓取返回的数据?比如get-route数时
抓route相关netlink的方法有很多,如果是调试工具,直接用strace会更快。
这篇文章所描述的方法,主要用于抓取Host侧的所有netlink调用,可以反查调用类型以及监控调用方,在现场排查问题,取证及监控场景使用会更好。