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