1// SPDX-License-Identifier: GPL-2.0
2#include <vmlinux.h>
3#include <bpf/bpf_helpers.h>
4
5#define ETH_ALEN 6
6#define HDR_SZ (sizeof(struct ethhdr) + sizeof(struct ipv6hdr) + sizeof(struct udphdr))
7
8/**
9 * enum frame_mark - magics to distinguish page/packet paths
10 * @MARK_XMIT: page was recycled due to the frame being "xmitted" by the NIC.
11 * @MARK_IN: frame is being processed by the input XDP prog.
12 * @MARK_SKB: frame did hit the TC ingress hook as an skb.
13 */
14enum frame_mark {
15 MARK_XMIT = 0U,
16 MARK_IN = 0x42,
17 MARK_SKB = 0x45,
18};
19
20const volatile int ifindex_out;
21const volatile int ifindex_in;
22const volatile __u8 expect_dst[ETH_ALEN];
23volatile int pkts_seen_xdp = 0;
24volatile int pkts_seen_zero = 0;
25volatile int pkts_seen_tc = 0;
26volatile int retcode = XDP_REDIRECT;
27
28SEC("xdp")
29int xdp_redirect(struct xdp_md *xdp)
30{
31 __u32 *metadata = (void *)(long)xdp->data_meta;
32 void *data_end = (void *)(long)xdp->data_end;
33 void *data = (void *)(long)xdp->data;
34
35 __u8 *payload = data + HDR_SZ;
36 int ret = retcode;
37
38 if (payload + 1 > data_end)
39 return XDP_ABORTED;
40
41 if (xdp->ingress_ifindex != (__u32)ifindex_in)
42 return XDP_ABORTED;
43
44 if (metadata + 1 > data)
45 return XDP_ABORTED;
46
47 if (*metadata != 0x42)
48 return XDP_ABORTED;
49
50 if (*payload == MARK_XMIT)
51 pkts_seen_zero++;
52
53 *payload = MARK_IN;
54
55 if (bpf_xdp_adjust_meta(xdp, sizeof(__u64)))
56 return XDP_ABORTED;
57
58 if (retcode > XDP_PASS)
59 retcode--;
60
61 if (ret == XDP_REDIRECT)
62 return bpf_redirect(ifindex_out, 0);
63
64 return ret;
65}
66
67static bool check_pkt(void *data, void *data_end, const __u32 mark)
68{
69 struct ipv6hdr *iph = data + sizeof(struct ethhdr);
70 __u8 *payload = data + HDR_SZ;
71
72 if (payload + 1 > data_end)
73 return false;
74
75 if (iph->nexthdr != IPPROTO_UDP || *payload != MARK_IN)
76 return false;
77
78 /* reset the payload so the same packet doesn't get counted twice when
79 * it cycles back through the kernel path and out the dst veth
80 */
81 *payload = mark;
82 return true;
83}
84
85SEC("xdp")
86int xdp_count_pkts(struct xdp_md *xdp)
87{
88 void *data = (void *)(long)xdp->data;
89 void *data_end = (void *)(long)xdp->data_end;
90
91 if (check_pkt(data, data_end, MARK_XMIT))
92 pkts_seen_xdp++;
93
94 /* Return %XDP_DROP to recycle the data page with %MARK_XMIT, like
95 * it exited a physical NIC. Those pages will be counted in the
96 * pkts_seen_zero counter above.
97 */
98 return XDP_DROP;
99}
100
101SEC("tc")
102int tc_count_pkts(struct __sk_buff *skb)
103{
104 void *data = (void *)(long)skb->data;
105 void *data_end = (void *)(long)skb->data_end;
106
107 if (check_pkt(data, data_end, MARK_SKB))
108 pkts_seen_tc++;
109
110 /* Will be either recycled or freed, %MARK_SKB makes sure it won't
111 * hit any of the counters above.
112 */
113 return 0;
114}
115
116char _license[] SEC("license") = "GPL";
117

source code of linux/tools/testing/selftests/bpf/progs/test_xdp_do_redirect.c