xref: /linux/tools/testing/selftests/net/lib/xdp_metadata.bpf.c (revision 91a4855d6c03e770e42f17c798a36a3c46e63de2)
1*e3cdf6cfSChris J Arges // SPDX-License-Identifier: GPL-2.0
2*e3cdf6cfSChris J Arges 
3*e3cdf6cfSChris J Arges #include <stddef.h>
4*e3cdf6cfSChris J Arges #include <linux/bpf.h>
5*e3cdf6cfSChris J Arges #include <linux/in.h>
6*e3cdf6cfSChris J Arges #include <linux/if_ether.h>
7*e3cdf6cfSChris J Arges #include <linux/ip.h>
8*e3cdf6cfSChris J Arges #include <linux/ipv6.h>
9*e3cdf6cfSChris J Arges #include <linux/udp.h>
10*e3cdf6cfSChris J Arges #include <linux/tcp.h>
11*e3cdf6cfSChris J Arges #include <bpf/bpf_endian.h>
12*e3cdf6cfSChris J Arges #include <bpf/bpf_helpers.h>
13*e3cdf6cfSChris J Arges 
14*e3cdf6cfSChris J Arges enum {
15*e3cdf6cfSChris J Arges 	XDP_PORT = 1,
16*e3cdf6cfSChris J Arges 	XDP_PROTO = 4,
17*e3cdf6cfSChris J Arges } xdp_map_setup_keys;
18*e3cdf6cfSChris J Arges 
19*e3cdf6cfSChris J Arges struct {
20*e3cdf6cfSChris J Arges 	__uint(type, BPF_MAP_TYPE_ARRAY);
21*e3cdf6cfSChris J Arges 	__uint(max_entries, 5);
22*e3cdf6cfSChris J Arges 	__type(key, __u32);
23*e3cdf6cfSChris J Arges 	__type(value, __s32);
24*e3cdf6cfSChris J Arges } map_xdp_setup SEC(".maps");
25*e3cdf6cfSChris J Arges 
26*e3cdf6cfSChris J Arges /* RSS hash results: key 0 = hash, key 1 = hash type,
27*e3cdf6cfSChris J Arges  * key 2 = packet count, key 3 = error count.
28*e3cdf6cfSChris J Arges  */
29*e3cdf6cfSChris J Arges enum {
30*e3cdf6cfSChris J Arges 	RSS_KEY_HASH = 0,
31*e3cdf6cfSChris J Arges 	RSS_KEY_TYPE = 1,
32*e3cdf6cfSChris J Arges 	RSS_KEY_PKT_CNT = 2,
33*e3cdf6cfSChris J Arges 	RSS_KEY_ERR_CNT = 3,
34*e3cdf6cfSChris J Arges };
35*e3cdf6cfSChris J Arges 
36*e3cdf6cfSChris J Arges struct {
37*e3cdf6cfSChris J Arges 	__uint(type, BPF_MAP_TYPE_ARRAY);
38*e3cdf6cfSChris J Arges 	__type(key, __u32);
39*e3cdf6cfSChris J Arges 	__type(value, __u32);
40*e3cdf6cfSChris J Arges 	__uint(max_entries, 4);
41*e3cdf6cfSChris J Arges } map_rss SEC(".maps");
42*e3cdf6cfSChris J Arges 
43*e3cdf6cfSChris J Arges /* Mirror of enum xdp_rss_hash_type from include/net/xdp.h.
44*e3cdf6cfSChris J Arges  * Needed because the enum is not part of UAPI headers.
45*e3cdf6cfSChris J Arges  */
46*e3cdf6cfSChris J Arges enum xdp_rss_hash_type {
47*e3cdf6cfSChris J Arges 	XDP_RSS_L3_IPV4 = 1U << 0,
48*e3cdf6cfSChris J Arges 	XDP_RSS_L3_IPV6 = 1U << 1,
49*e3cdf6cfSChris J Arges 	XDP_RSS_L3_DYNHDR = 1U << 2,
50*e3cdf6cfSChris J Arges 	XDP_RSS_L4 = 1U << 3,
51*e3cdf6cfSChris J Arges 	XDP_RSS_L4_TCP = 1U << 4,
52*e3cdf6cfSChris J Arges 	XDP_RSS_L4_UDP = 1U << 5,
53*e3cdf6cfSChris J Arges 	XDP_RSS_L4_SCTP = 1U << 6,
54*e3cdf6cfSChris J Arges 	XDP_RSS_L4_IPSEC = 1U << 7,
55*e3cdf6cfSChris J Arges 	XDP_RSS_L4_ICMP = 1U << 8,
56*e3cdf6cfSChris J Arges };
57*e3cdf6cfSChris J Arges 
58*e3cdf6cfSChris J Arges extern int bpf_xdp_metadata_rx_hash(const struct xdp_md *ctx, __u32 *hash,
59*e3cdf6cfSChris J Arges 				    enum xdp_rss_hash_type *rss_type) __ksym;
60*e3cdf6cfSChris J Arges 
61*e3cdf6cfSChris J Arges static __always_inline __u16 get_dest_port(void *l4, void *data_end,
62*e3cdf6cfSChris J Arges 					   __u8 protocol)
63*e3cdf6cfSChris J Arges {
64*e3cdf6cfSChris J Arges 	if (protocol == IPPROTO_UDP) {
65*e3cdf6cfSChris J Arges 		struct udphdr *udp = l4;
66*e3cdf6cfSChris J Arges 
67*e3cdf6cfSChris J Arges 		if ((void *)(udp + 1) > data_end)
68*e3cdf6cfSChris J Arges 			return 0;
69*e3cdf6cfSChris J Arges 		return udp->dest;
70*e3cdf6cfSChris J Arges 	} else if (protocol == IPPROTO_TCP) {
71*e3cdf6cfSChris J Arges 		struct tcphdr *tcp = l4;
72*e3cdf6cfSChris J Arges 
73*e3cdf6cfSChris J Arges 		if ((void *)(tcp + 1) > data_end)
74*e3cdf6cfSChris J Arges 			return 0;
75*e3cdf6cfSChris J Arges 		return tcp->dest;
76*e3cdf6cfSChris J Arges 	}
77*e3cdf6cfSChris J Arges 
78*e3cdf6cfSChris J Arges 	return 0;
79*e3cdf6cfSChris J Arges }
80*e3cdf6cfSChris J Arges 
81*e3cdf6cfSChris J Arges SEC("xdp")
82*e3cdf6cfSChris J Arges int xdp_rss_hash(struct xdp_md *ctx)
83*e3cdf6cfSChris J Arges {
84*e3cdf6cfSChris J Arges 	void *data_end = (void *)(long)ctx->data_end;
85*e3cdf6cfSChris J Arges 	void *data = (void *)(long)ctx->data;
86*e3cdf6cfSChris J Arges 	enum xdp_rss_hash_type rss_type = 0;
87*e3cdf6cfSChris J Arges 	struct ethhdr *eth = data;
88*e3cdf6cfSChris J Arges 	__u8 l4_proto = 0;
89*e3cdf6cfSChris J Arges 	__u32 hash = 0;
90*e3cdf6cfSChris J Arges 	__u32 key, val;
91*e3cdf6cfSChris J Arges 	void *l4 = NULL;
92*e3cdf6cfSChris J Arges 	__u32 *cnt;
93*e3cdf6cfSChris J Arges 	int ret;
94*e3cdf6cfSChris J Arges 
95*e3cdf6cfSChris J Arges 	if ((void *)(eth + 1) > data_end)
96*e3cdf6cfSChris J Arges 		return XDP_PASS;
97*e3cdf6cfSChris J Arges 
98*e3cdf6cfSChris J Arges 	if (eth->h_proto == bpf_htons(ETH_P_IP)) {
99*e3cdf6cfSChris J Arges 		struct iphdr *iph = (void *)(eth + 1);
100*e3cdf6cfSChris J Arges 
101*e3cdf6cfSChris J Arges 		if ((void *)(iph + 1) > data_end)
102*e3cdf6cfSChris J Arges 			return XDP_PASS;
103*e3cdf6cfSChris J Arges 		l4_proto = iph->protocol;
104*e3cdf6cfSChris J Arges 		l4 = (void *)(iph + 1);
105*e3cdf6cfSChris J Arges 	} else if (eth->h_proto == bpf_htons(ETH_P_IPV6)) {
106*e3cdf6cfSChris J Arges 		struct ipv6hdr *ip6h = (void *)(eth + 1);
107*e3cdf6cfSChris J Arges 
108*e3cdf6cfSChris J Arges 		if ((void *)(ip6h + 1) > data_end)
109*e3cdf6cfSChris J Arges 			return XDP_PASS;
110*e3cdf6cfSChris J Arges 		l4_proto = ip6h->nexthdr;
111*e3cdf6cfSChris J Arges 		l4 = (void *)(ip6h + 1);
112*e3cdf6cfSChris J Arges 	}
113*e3cdf6cfSChris J Arges 
114*e3cdf6cfSChris J Arges 	if (!l4)
115*e3cdf6cfSChris J Arges 		return XDP_PASS;
116*e3cdf6cfSChris J Arges 
117*e3cdf6cfSChris J Arges 	/* Filter on the configured protocol (map_xdp_setup key XDP_PROTO).
118*e3cdf6cfSChris J Arges 	 * When set, only process packets matching the requested L4 protocol.
119*e3cdf6cfSChris J Arges 	 */
120*e3cdf6cfSChris J Arges 	key = XDP_PROTO;
121*e3cdf6cfSChris J Arges 	__s32 *proto_cfg = bpf_map_lookup_elem(&map_xdp_setup, &key);
122*e3cdf6cfSChris J Arges 
123*e3cdf6cfSChris J Arges 	if (proto_cfg && *proto_cfg != 0 && l4_proto != (__u8)*proto_cfg)
124*e3cdf6cfSChris J Arges 		return XDP_PASS;
125*e3cdf6cfSChris J Arges 
126*e3cdf6cfSChris J Arges 	/* Filter on the configured port (map_xdp_setup key XDP_PORT).
127*e3cdf6cfSChris J Arges 	 * Only applies to protocols with ports (UDP, TCP).
128*e3cdf6cfSChris J Arges 	 */
129*e3cdf6cfSChris J Arges 	key = XDP_PORT;
130*e3cdf6cfSChris J Arges 	__s32 *port_cfg = bpf_map_lookup_elem(&map_xdp_setup, &key);
131*e3cdf6cfSChris J Arges 
132*e3cdf6cfSChris J Arges 	if (port_cfg && *port_cfg != 0) {
133*e3cdf6cfSChris J Arges 		__u16 dest = get_dest_port(l4, data_end, l4_proto);
134*e3cdf6cfSChris J Arges 
135*e3cdf6cfSChris J Arges 		if (!dest || bpf_ntohs(dest) != (__u16)*port_cfg)
136*e3cdf6cfSChris J Arges 			return XDP_PASS;
137*e3cdf6cfSChris J Arges 	}
138*e3cdf6cfSChris J Arges 
139*e3cdf6cfSChris J Arges 	ret = bpf_xdp_metadata_rx_hash(ctx, &hash, &rss_type);
140*e3cdf6cfSChris J Arges 	if (ret < 0) {
141*e3cdf6cfSChris J Arges 		key = RSS_KEY_ERR_CNT;
142*e3cdf6cfSChris J Arges 		cnt = bpf_map_lookup_elem(&map_rss, &key);
143*e3cdf6cfSChris J Arges 		if (cnt)
144*e3cdf6cfSChris J Arges 			__sync_fetch_and_add(cnt, 1);
145*e3cdf6cfSChris J Arges 		return XDP_PASS;
146*e3cdf6cfSChris J Arges 	}
147*e3cdf6cfSChris J Arges 
148*e3cdf6cfSChris J Arges 	key = RSS_KEY_HASH;
149*e3cdf6cfSChris J Arges 	bpf_map_update_elem(&map_rss, &key, &hash, BPF_ANY);
150*e3cdf6cfSChris J Arges 
151*e3cdf6cfSChris J Arges 	key = RSS_KEY_TYPE;
152*e3cdf6cfSChris J Arges 	val = (__u32)rss_type;
153*e3cdf6cfSChris J Arges 	bpf_map_update_elem(&map_rss, &key, &val, BPF_ANY);
154*e3cdf6cfSChris J Arges 
155*e3cdf6cfSChris J Arges 	key = RSS_KEY_PKT_CNT;
156*e3cdf6cfSChris J Arges 	cnt = bpf_map_lookup_elem(&map_rss, &key);
157*e3cdf6cfSChris J Arges 	if (cnt)
158*e3cdf6cfSChris J Arges 		__sync_fetch_and_add(cnt, 1);
159*e3cdf6cfSChris J Arges 
160*e3cdf6cfSChris J Arges 	return XDP_PASS;
161*e3cdf6cfSChris J Arges }
162*e3cdf6cfSChris J Arges 
163*e3cdf6cfSChris J Arges char _license[] SEC("license") = "GPL";
164