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, |
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/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 of 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 | /* Notice: Kernel-side verifier requires that loading of |
46 | * ctx->data MUST happen _after_ helper bpf_xdp_adjust_meta(), |
47 | * as pkt-data pointers are invalidated. Helpers that require |
48 | * this are determined/marked by bpf_helper_changes_pkt_data() |
49 | */ |
50 | data = (void *)(unsigned long)ctx->data; |
51 | |
52 | /* Check data_meta have room for meta_info struct */ |
53 | meta = (void *)(unsigned long)ctx->data_meta; |
54 | if (meta + 1 > data) |
55 | return XDP_ABORTED; |
56 | |
57 | meta->mark = 42; |
58 | |
59 | return XDP_PASS; |
60 | } |
61 | |
62 | SEC("tc_mark" ) |
63 | int _tc_mark(struct __sk_buff *ctx) |
64 | { |
65 | void *data = (void *)(unsigned long)ctx->data; |
66 | void *data_end = (void *)(unsigned long)ctx->data_end; |
67 | void *data_meta = (void *)(unsigned long)ctx->data_meta; |
68 | struct meta_info *meta = data_meta; |
69 | |
70 | /* Check XDP gave us some data_meta */ |
71 | if (meta + 1 > data) { |
72 | ctx->mark = 41; |
73 | /* Skip "accept" if no data_meta is avail */ |
74 | return TC_ACT_OK; |
75 | } |
76 | |
77 | /* Hint: See func tc_cls_act_is_valid_access() for BPF_WRITE access */ |
78 | ctx->mark = meta->mark; /* Transfer XDP-mark to SKB-mark */ |
79 | |
80 | return TC_ACT_OK; |
81 | } |
82 | |
83 | /* Manually attaching these programs: |
84 | export DEV=ixgbe2 |
85 | export FILE=xdp2skb_meta_kern.o |
86 | |
87 | # via TC command |
88 | tc qdisc del dev $DEV clsact 2> /dev/null |
89 | tc qdisc add dev $DEV clsact |
90 | tc filter add dev $DEV ingress prio 1 handle 1 bpf da obj $FILE sec tc_mark |
91 | tc filter show dev $DEV ingress |
92 | |
93 | # XDP via IP command: |
94 | ip link set dev $DEV xdp off |
95 | ip link set dev $DEV xdp obj $FILE sec xdp_mark |
96 | |
97 | # Use iptable to "see" if SKBs are marked |
98 | iptables -I INPUT -p icmp -m mark --mark 41 # == 0x29 |
99 | iptables -I INPUT -p icmp -m mark --mark 42 # == 0x2a |
100 | |
101 | # Hint: catch XDP_ABORTED errors via |
102 | perf record -e xdp:* |
103 | perf script |
104 | |
105 | */ |
106 | |