在 XDP 或者 tc-bpf 中,经常要直接访问网络包内容;而在访问网络包内容之前,总是需要先检查访问的内容是否在 data_end
之内。
So,学习一下 packet range check 相关 commit 的历史;以史为镜,可以更好地掌握知识。
如果判断一个网络包是不是 IPv4 的 TCP 包的目的端口是不是 8080,可以这样写:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
|
static __always_inline bool
__is_ipv4_tcp_udp(struct xdp_md *xdp)
{
void *data_end = (void *)(long)xdp->data_end;
void *data = (void *)(long)xdp->data;
struct ethhdr *eth;
struct iphdr *iph;
struct tcphdr *tcph;
eth = data;
if ((void *)eth + sizeof(*eth) > data_end)
return false;
if (eth->h_proto != bpf_htons(ETH_P_IP))
return false;
iph = data + sizeof(*eth);
if ((void *)iph + sizeof(*iph) > data_end)
return false;
if (iph->protocol != IPPROTO_TCP)
return false;
tcph = data + sizeof(*eth) + sizeof(*iph);
if ((void *)tcph + sizeof(*tcph) > data_end)
return false;
return tcph->dest == bpf_htons(8080);
}
|
不过,根据 “improve verifier packet range checks”,可以减少 2 次 packet range check,改为这样写:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
static __always_inline bool
__is_ipv4_tcp_udp(struct xdp_md *xdp)
{
void *data_end = (void *)(long)xdp->data_end;
void *data = (void *)(long)xdp->data;
struct ethhdr *eth;
struct iphdr *iph;
struct tcphdr *tcph;
eth = data;
iph = data + sizeof(*eth);
tcph = data + sizeof(*eth) + sizeof(*iph);
if ((void *)tcph + sizeof(*tcph) > data_end)
return false;
if (eth->h_proto != bpf_htons(ETH_P_IP) || iph->protocol != IPPROTO_TCP)
return false;
return tcph->dest == bpf_htons(8080);
}
|
该做法同样适用于 tc-bpf。
小结
简而言之,可以将多次 packet range check 合并为一次 packet range check,只需要检查偏移量最大的那一次即可。