浅析netlink流量抓包

本周遇到点问题,发现某个进程触发netlink请求后,会长时间hang(1~2s),需要进行深入归因排查。

归因的主要思路是先找到hang的位置,即找到现场的调用栈,由于触发时会出现秒级的hang行为,此处不需要太过复杂的机制,周期性cat proc目录的stack文件即可。

在找到调用栈后,就需要反查到底是哪些进程触发netlink请求了,这里的难点在于,进程的hang行为并非由于当前线程导致的,因此需要一种全局维度的机制来进行综合判断。

不少读者应该会有种想法,要是能够通过tcpdump抓包就好了,因为通常而言,进程会将nlmsg的port id字段直接设置为当前的pid,较易分析。

如果你有这种想法,恭喜你,本文将会向你介绍这种机制。

如果你调研过netlink的实现方式应该不难发现,它也是用skbuff来承载nlmsg结构体的,而该结构体中通常会包含进程信息,因而广义上来说,netlink流量也是网络流量的一种,我们没有理由不为其开发一种dump机制,来协助进行问题定位。

这个机制就是netlink monitor,内核模块名就是nlmon,使用方式也很简单,大致分为如下几步:

  1. 挂载nlmon内核模块
  2. 创建nlmon设备
  3. nlmon设备设置link up
  4. 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流量解析如下:

nldump-sample.jpg

最后,记得在排查完毕后,删除interface以及卸载模块:

ip link del dev nlmon0
rmmod nlmon

Happy Coding!

说两句: