学习了 XDP metadata 和 AF_XDP,是否可以使用 metadata 将信息从 XDP 传给 AF_XDP 呢?
TL;DR 答案是可以的,复制网络包内容时将 metadata
一起进行复制了。
实验 demo
P.S. demo 源代码:ping latency injection with XDP metadata
以 “使用 AF_XDP 注入延时” 为例,将延时信息放在 metadata 里,然后在 AF_XDP 程序里读取 metadata 当作延时值。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
#define LATENCY_MS 200
SEC("xdp")
int xdp_fn(struct xdp_md *ctx)
{
// ...
__u32 *val;
const int siz = sizeof(*val);
if (bpf_xdp_adjust_meta(ctx, -siz) != 0)
return XDP_PASS;
data = ctx_ptr(ctx, data); // required to re-obtain data pointer
void *data_meta = ctx_ptr(ctx, data_meta);
val = (typeof(val))data_meta;
if ((void *)(val + 1) > data)
return XDP_PASS;
*val = LATENCY_MS; // 写入延时信息
return bpf_redirect_map(&xdp_sockets, 0, 0);
}
|
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
|
func delayPackets(xsk *xdp.Socket, descs []xdp.Desc) {
for _, desc := range descs {
desc := desc
latency := readLatency(xsk, desc) // 从 metadata 里读取延时信息
log.Printf("Delaying packet by %s", latency)
delayPacket(xsk, desc, latency)
}
}
func readLatency(xsk *xdp.Socket, desc xdp.Desc) time.Duration {
frame := xsk.GetFrame(desc)
sh := (*reflect.SliceHeader)(unsafe.Pointer(&frame))
sh.Data -= 4
sh.Len += 4
sh.Cap += 4
lat := *(*uint32)(unsafe.Pointer(&frame[0]))
return time.Duration(lat) * time.Millisecond
}
func delayPacket(xsk *xdp.Socket, desc xdp.Desc, latency time.Duration) {
_ = time.AfterFunc(latency, func() {
transformPacket(xsk.GetFrame(desc))
_ = xsk.Transmit([]xdp.Desc{desc})
})
}
|
实验效果:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
# ./metadata_xdp2afxdp -D enp0s8
2023/04/21 15:04:51 Attached XDP to enp0s8
2023/04/21 15:04:53 Delaying packet by 200ms
2023/04/21 15:04:54 Delaying packet by 200ms
2023/04/21 15:04:55 Delaying packet by 200ms
2023/04/21 15:04:56 Delaying packet by 200ms
2023/04/21 15:04:57 Delaying packet by 200ms
# ping 192.168.1.138
PING 192.168.1.138 (192.168.1.138): 56 data bytes
64 bytes from 192.168.1.138: icmp_seq=0 ttl=64 time=202.720 ms
64 bytes from 192.168.1.138: icmp_seq=1 ttl=64 time=201.904 ms
64 bytes from 192.168.1.138: icmp_seq=2 ttl=64 time=201.344 ms
64 bytes from 192.168.1.138: icmp_seq=3 ttl=64 time=201.270 ms
64 bytes from 192.168.1.138: icmp_seq=4 ttl=64 time=201.258 ms
|
底层源代码分析
从 eBPF Talk: XDP redirect to AF_XDP 中,我们知道 XDP redirect 到 AF_XDP 的底层实现会调用 xsk_copy_xdp(xsk_xdp, xdp, len);
进行网络包复制。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
// ${KERNEL}/net/xdp/xsk.c
static void xsk_copy_xdp(struct xdp_buff *to, struct xdp_buff *from, u32 len)
{
void *from_buf, *to_buf;
u32 metalen;
if (unlikely(xdp_data_meta_unsupported(from))) {
from_buf = from->data;
to_buf = to->data;
metalen = 0;
} else {
from_buf = from->data_meta;
metalen = from->data - from->data_meta;
to_buf = to->data - metalen;
}
memcpy(to_buf, from_buf, len + metalen);
}
|
xsk_copy_xdp
会先判断是否存在 metadata
,如果存在,则将 metadata
一起复制到 AF_XDP
的 buffer
中。
因此,在 AF_XDP 应用程序中,我们可以通过 xdp_desc
的 addr
- metadata length
来获取 metadata
的值。
AF_XDP 使用 COPY 模式时会调用 xsk_copy_xdp
函数复制网络包,使用 ZEROCOPY 模式时就不需要复制网络包了。
小结
通过 metadata
,我们可以将信息从 XDP 传给 AF_XDP,从而实现一些有趣的功能。