在 Linux 内核协议栈设备层,有 2 个 tracepoint 可以用来统计网络包的延迟:
net:net_dev_xmit
:设备层发送 skb 的 tracepoint。net:netif_receive_skb
:设备层接收 skb 的 tracepoint。
因此,不管是向外发起请求、还是接收请求,都可以通过这 2 个 tracepoint 来统计网络包的延迟。
利用这 2 个 tracepoint,写了个 skbdist
工具,可以统计网络包的延迟分布。
如果怀疑服务器响应慢,可以用 skbdist
来统计网络包的延迟分布,看看是不是真的慢。
skbdist 效果
向外发起请求,统计网络包的延迟分布:
$ ping -c1 8.8.8.8
PING 8.8.8.8 (8.8.8.8) 56(84) bytes of data.
64 bytes from 8.8.8.8: icmp_seq=1 ttl=128 time=160 ms
--- 8.8.8.8 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 159.706/159.706/159.706/0.000 ms
$ sudo ./skbdist -i ens33 icmp
2024/07/14 12:45:36 Attached tracepoint/net/netif_receive_skb
2024/07/14 12:45:36 Attached tracepoint/net/net_dev_xmit
Ctrl+C to show results..
^C
192.168.241.133:0 -> 8.8.8.8:0 (ICMP) (total 1 records) :
µs : count distribution
0 -> 1 : 0 | |
...
131072 -> 262143 : 1 |****************************************|```
接收外部请求,统计网络包的延迟分布:
$ ping -c1 192.168.241.133
PING 192.168.241.133 (192.168.241.133): 56 data bytes
64 bytes from 192.168.241.133: icmp_seq=0 ttl=64 time=0.360 ms
--- 192.168.241.133 ping statistics ---
1 packets transmitted, 1 packets received, 0.0% packet loss
round-trip min/avg/max/stddev = 0.360/0.360/0.360/0.000 ms
$ sudo ./skbdist -i ens33 icmp
2024/07/14 12:45:52 Attached tracepoint/net/netif_receive_skb
2024/07/14 12:45:52 Attached tracepoint/net/net_dev_xmit
Ctrl+C to show results..
^C
192.168.241.133:0 -> 192.168.241.1:0 (ICMP) (total 1 records) :
µs : count distribution
0 -> 1 : 0 | |
2 -> 3 : 0 | |
4 -> 7 : 0 | |
8 -> 15 : 0 | |
16 -> 31 : 0 | |
32 -> 63 : 1 |****************************************|
skbdist 实现
先看看 skbdist
使用的 2 个 tracepoint:
$ cat /sys/kernel/debug/tracing/events/net/netif_receive_skb/format
name: netif_receive_skb
ID: 1622
format:
field:unsigned short common_type; offset:0; size:2; signed:0;
field:unsigned char common_flags; offset:2; size:1; signed:0;
field:unsigned char common_preempt_count; offset:3; size:1; signed:0;
field:int common_pid; offset:4; size:4; signed:1;
field:void * skbaddr; offset:8; size:8; signed:0;
field:unsigned int len; offset:16; size:4; signed:0;
field:__data_loc char[] name; offset:20; size:4; signed:0;
print fmt: "dev=%s skbaddr=%p len=%u", __get_str(name), REC->skbaddr, REC->len
$ cat /sys/kernel/debug/tracing/events/net/net_dev_xmit/format
name: net_dev_xmit
ID: 1625
format:
field:unsigned short common_type; offset:0; size:2; signed:0;
field:unsigned char common_flags; offset:2; size:1; signed:0;
field:unsigned char common_preempt_count; offset:3; size:1; signed:0;
field:int common_pid; offset:4; size:4; signed:1;
field:void * skbaddr; offset:8; size:8; signed:0;
field:unsigned int len; offset:16; size:4; signed:0;
field:int rc; offset:20; size:4; signed:1;
field:__data_loc char[] name; offset:24; size:4; signed:0;
print fmt: "dev=%s skbaddr=%p len=%u rc=%d", __get_str(name), REC->skbaddr, REC->len, REC->rc
因为不需要知道其中的 name
,所以 bpf 里只需要:
struct tp_netif_receive_skb_args {
__u64 unused;
void * skbaddr;
unsigned int len;
};
SEC("tracepoint/net/netif_receive_skb")
int tracepoint__netif_receive_skb(struct tp_netif_receive_skb_args *args)
{
return handle_skb((struct sk_buff *) args->skbaddr, true);
}
struct tp_net_dev_xmit_args {
__u64 unused;
void * skbaddr;
unsigned int len;
int rc;
};
SEC("tracepoint/net/net_dev_xmit")
int tracepoint__net_dev_xmit(struct tp_net_dev_xmit_args *args)
{
return handle_skb((struct sk_buff *) args->skbaddr, false);
}
handle_skb
函数会计算 skb 的延迟,并统计到 bpf_map
里。
NOTE: 对于收包,需要将 IP 层的地址和 4 层的端口信息互换一下位置,保持跟发包方向的信息一致。
skbdist 支持 pcap-filter(7)
skbdist
不使用 --addr
/--port
等参数,而是像 tcpdump
一样支持 pcap-filter(7)[1],可以更灵活地过滤网络包。
因此,在过滤网络包的时候,可以使用 icmp
、tcp port 80
、udp port 53
等等,而不使用 src 1.1.1.1
、 udp src port 43
这样带有方向的 pcap-filter(7)
。
skbdist 统计 skb 长度
与此同时,skbdist
也统计了 skb 的长度分布:
$ sudo ./skbdist -i ens33 tcp port 8080
2024/07/14 13:06:06 Attached tracepoint/net/netif_receive_skb
2024/07/14 13:06:06 Attached tracepoint/net/net_dev_xmit
Ctrl+C to show results..
^C
Send SKB lengths (total 5 pkts) :
byte : count distribution
0 -> 1 : 0 | |
2 -> 3 : 0 | |
4 -> 7 : 0 | |
8 -> 15 : 0 | |
16 -> 31 : 0 | |
32 -> 63 : 0 | |
64 -> 127 : 3 |****************************************|
128 -> 255 : 1 |************* |
256 -> 511 : 0 | |
512 -> 1023 : 1 |************* |
Receive SKB lengths (total 6 pkts) :
byte : count distribution
0 -> 1 : 0 | |
2 -> 3 : 0 | |
4 -> 7 : 0 | |
8 -> 15 : 0 | |
16 -> 31 : 0 | |
32 -> 63 : 4 |****************************************|
64 -> 127 : 1 |********** |
128 -> 255 : 1 |********** |
192.168.241.133:8080 -> 192.168.241.1:53402 (TCP) (total 5 records) :
µs : count distribution
0 -> 1 : 1 |******************** |
2 -> 3 : 0 | |
4 -> 7 : 0 | |
8 -> 15 : 0 | |
16 -> 31 : 0 | |
32 -> 63 : 1 |******************** |
64 -> 127 : 2 |****************************************|
128 -> 255 : 0 | |
256 -> 511 : 0 | |
512 -> 1023 : 0 | |
1024 -> 2047 : 0 | |
2048 -> 4095 : 0 | |
4096 -> 8191 : 0 | |
8192 -> 16383 : 1 |******************** |
skbdist
源代码:skbdist[2].
总结
利用设备层的 2 个 tracepoint net:net_dev_xmit
和 net:netif_receive_skb
,可以统计网络包的延迟分布,以及 skb 的长度分布。
pcap-filter(7): https://www.tcpdump.org/manpages/pcap-filter.7.html
[2]skbdist: https://github.com/Asphaltt/skbdist