Commit | Line | Data |
---|---|---|
36e04a2d JDB |
1 | /* SPDX-License-Identifier: GPL-2.0 |
2 | * Copyright (c) 2018 Jesper Dangaard Brouer, Red Hat Inc. | |
3 | * | |
4 | * Example howto transfer info from XDP to SKB, e.g. skb->mark | |
5 | * ----------------------------------------------------------- | |
6 | * This uses the XDP data_meta infrastructure, and is a cooperation | |
7 | * between two bpf-programs (1) XDP and (2) clsact at TC-ingress hook. | |
8 | * | |
9 | * Notice: This example does not use the BPF C-loader (bpf_load.c), | |
10 | * but instead rely on the iproute2 TC tool for loading BPF-objects. | |
11 | */ | |
12 | #include <uapi/linux/bpf.h> | |
13 | #include <uapi/linux/pkt_cls.h> | |
14 | ||
15 | #include "bpf_helpers.h" | |
16 | ||
17 | /* | |
18 | * This struct is stored in the XDP 'data_meta' area, which is located | |
19 | * just in-front-of the raw packet payload data. The meaning is | |
20 | * specific to these two BPF programs that use it as a communication | |
21 | * channel. XDP adjust/increase the area via a bpf-helper, and TC use | |
22 | * boundary checks to see if data have been provided. | |
23 | * | |
24 | * The struct must be 4 byte aligned, which here is enforced by the | |
25 | * struct __attribute__((aligned(4))). | |
26 | */ | |
27 | struct meta_info { | |
28 | __u32 mark; | |
29 | } __attribute__((aligned(4))); | |
30 | ||
31 | SEC("xdp_mark") | |
32 | int _xdp_mark(struct xdp_md *ctx) | |
33 | { | |
34 | struct meta_info *meta; | |
35 | void *data, *data_end; | |
36 | int ret; | |
37 | ||
38 | /* Reserve space in-front data pointer for our meta info. | |
39 | * (Notice drivers not supporting data_meta will fail here!) | |
40 | */ | |
41 | ret = bpf_xdp_adjust_meta(ctx, -(int)sizeof(*meta)); | |
42 | if (ret < 0) | |
43 | return XDP_ABORTED; | |
44 | ||
45 | /* For some unknown reason, these ctx pointers must be read | |
46 | * after bpf_xdp_adjust_meta, else verifier will reject prog. | |
47 | */ | |
48 | data = (void *)(unsigned long)ctx->data; | |
49 | ||
50 | /* Check data_meta have room for meta_info struct */ | |
51 | meta = (void *)(unsigned long)ctx->data_meta; | |
52 | if (meta + 1 > data) | |
53 | return XDP_ABORTED; | |
54 | ||
55 | meta->mark = 42; | |
56 | ||
57 | return XDP_PASS; | |
58 | } | |
59 | ||
60 | SEC("tc_mark") | |
61 | int _tc_mark(struct __sk_buff *ctx) | |
62 | { | |
63 | void *data = (void *)(unsigned long)ctx->data; | |
64 | void *data_end = (void *)(unsigned long)ctx->data_end; | |
65 | void *data_meta = (void *)(unsigned long)ctx->data_meta; | |
66 | struct meta_info *meta = data_meta; | |
67 | ||
68 | /* Check XDP gave us some data_meta */ | |
69 | if (meta + 1 > data) { | |
70 | ctx->mark = 41; | |
71 | /* Skip "accept" if no data_meta is avail */ | |
72 | return TC_ACT_OK; | |
73 | } | |
74 | ||
75 | /* Hint: See func tc_cls_act_is_valid_access() for BPF_WRITE access */ | |
76 | ctx->mark = meta->mark; /* Transfer XDP-mark to SKB-mark */ | |
77 | ||
78 | return TC_ACT_OK; | |
79 | } | |
80 | ||
81 | /* Manually attaching these programs: | |
82 | export DEV=ixgbe2 | |
83 | export FILE=xdp2skb_meta_kern.o | |
84 | ||
85 | # via TC command | |
86 | tc qdisc del dev $DEV clsact 2> /dev/null | |
87 | tc qdisc add dev $DEV clsact | |
88 | tc filter add dev $DEV ingress prio 1 handle 1 bpf da obj $FILE sec tc_mark | |
89 | tc filter show dev $DEV ingress | |
90 | ||
91 | # XDP via IP command: | |
92 | ip link set dev $DEV xdp off | |
93 | ip link set dev $DEV xdp obj $FILE sec xdp_mark | |
94 | ||
95 | # Use iptable to "see" if SKBs are marked | |
96 | iptables -I INPUT -p icmp -m mark --mark 41 # == 0x29 | |
97 | iptables -I INPUT -p icmp -m mark --mark 42 # == 0x2a | |
98 | ||
99 | # Hint: catch XDP_ABORTED errors via | |
100 | perf record -e xdp:* | |
101 | perf script | |
102 | ||
103 | */ |