xref: /linux/tools/testing/selftests/bpf/progs/test_xdp_do_redirect.c (revision 06d07429858317ded2db7986113a9e0129cd599b)
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  */
14 enum frame_mark {
15 	MARK_XMIT	= 0U,
16 	MARK_IN		= 0x42,
17 	MARK_SKB	= 0x45,
18 };
19 
20 const volatile int ifindex_out;
21 const volatile int ifindex_in;
22 const volatile __u8 expect_dst[ETH_ALEN];
23 volatile int pkts_seen_xdp = 0;
24 volatile int pkts_seen_zero = 0;
25 volatile int pkts_seen_tc = 0;
26 volatile int retcode = XDP_REDIRECT;
27 
28 SEC("xdp")
xdp_redirect(struct xdp_md * xdp)29 int 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 
check_pkt(void * data,void * data_end,const __u32 mark)67 static 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 
85 SEC("xdp")
xdp_count_pkts(struct xdp_md * xdp)86 int 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 
101 SEC("tc")
tc_count_pkts(struct __sk_buff * skb)102 int 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 
116 char _license[] SEC("license") = "GPL";
117