xref: /f-stack/dpdk/drivers/net/tap/tap_bpf_program.c (revision 2d9fd380)
1d30ea906Sjfb8856606 /* SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
2d30ea906Sjfb8856606  * Copyright 2017 Mellanox Technologies, Ltd
3d30ea906Sjfb8856606  */
4d30ea906Sjfb8856606 
5d30ea906Sjfb8856606 #include <stdint.h>
6d30ea906Sjfb8856606 #include <stdbool.h>
7d30ea906Sjfb8856606 #include <sys/types.h>
8d30ea906Sjfb8856606 #include <sys/socket.h>
9d30ea906Sjfb8856606 #include <asm/types.h>
10d30ea906Sjfb8856606 #include <linux/in.h>
11d30ea906Sjfb8856606 #include <linux/if.h>
12d30ea906Sjfb8856606 #include <linux/if_ether.h>
13d30ea906Sjfb8856606 #include <linux/ip.h>
14d30ea906Sjfb8856606 #include <linux/ipv6.h>
15d30ea906Sjfb8856606 #include <linux/if_tunnel.h>
16d30ea906Sjfb8856606 #include <linux/filter.h>
17d30ea906Sjfb8856606 #include <linux/bpf.h>
18d30ea906Sjfb8856606 
19d30ea906Sjfb8856606 #include "tap_rss.h"
20d30ea906Sjfb8856606 
21d30ea906Sjfb8856606 /** Create IPv4 address */
22d30ea906Sjfb8856606 #define IPv4(a, b, c, d) ((__u32)(((a) & 0xff) << 24) | \
23d30ea906Sjfb8856606 		(((b) & 0xff) << 16) | \
24d30ea906Sjfb8856606 		(((c) & 0xff) << 8)  | \
25d30ea906Sjfb8856606 		((d) & 0xff))
26d30ea906Sjfb8856606 
27d30ea906Sjfb8856606 #define PORT(a, b) ((__u16)(((a) & 0xff) << 8) | \
28d30ea906Sjfb8856606 		((b) & 0xff))
29d30ea906Sjfb8856606 
30d30ea906Sjfb8856606 /*
31d30ea906Sjfb8856606  * The queue number is offset by a unique QUEUE_OFFSET, to distinguish
32d30ea906Sjfb8856606  * packets that have gone through this rule (skb->cb[1] != 0) from others.
33d30ea906Sjfb8856606  */
34d30ea906Sjfb8856606 #define QUEUE_OFFSET		0x7cafe800
35d30ea906Sjfb8856606 #define PIN_GLOBAL_NS		2
36d30ea906Sjfb8856606 
37d30ea906Sjfb8856606 #define KEY_IDX			0
38d30ea906Sjfb8856606 #define BPF_MAP_ID_KEY	1
39d30ea906Sjfb8856606 
40d30ea906Sjfb8856606 struct vlan_hdr {
41d30ea906Sjfb8856606 	__be16 proto;
42d30ea906Sjfb8856606 	__be16 tci;
43d30ea906Sjfb8856606 };
44d30ea906Sjfb8856606 
45d30ea906Sjfb8856606 struct bpf_elf_map __attribute__((section("maps"), used))
46d30ea906Sjfb8856606 map_keys = {
47d30ea906Sjfb8856606 	.type           =       BPF_MAP_TYPE_HASH,
48d30ea906Sjfb8856606 	.id             =       BPF_MAP_ID_KEY,
49d30ea906Sjfb8856606 	.size_key       =       sizeof(__u32),
50d30ea906Sjfb8856606 	.size_value     =       sizeof(struct rss_key),
51d30ea906Sjfb8856606 	.max_elem       =       256,
52d30ea906Sjfb8856606 	.pinning        =       PIN_GLOBAL_NS,
53d30ea906Sjfb8856606 };
54d30ea906Sjfb8856606 
55d30ea906Sjfb8856606 __section("cls_q") int
match_q(struct __sk_buff * skb)56d30ea906Sjfb8856606 match_q(struct __sk_buff *skb)
57d30ea906Sjfb8856606 {
58d30ea906Sjfb8856606 	__u32 queue = skb->cb[1];
59d30ea906Sjfb8856606 	volatile __u32 q = 0xdeadbeef;
60d30ea906Sjfb8856606 	__u32 match_queue = QUEUE_OFFSET + q;
61d30ea906Sjfb8856606 
62d30ea906Sjfb8856606 	/* printt("match_q$i() queue = %d\n", queue); */
63d30ea906Sjfb8856606 
64d30ea906Sjfb8856606 	if (queue != match_queue)
65d30ea906Sjfb8856606 		return TC_ACT_OK;
66d30ea906Sjfb8856606 
67d30ea906Sjfb8856606 	/* queue match */
68d30ea906Sjfb8856606 	skb->cb[1] = 0;
69d30ea906Sjfb8856606 	return TC_ACT_UNSPEC;
70d30ea906Sjfb8856606 }
71d30ea906Sjfb8856606 
72d30ea906Sjfb8856606 
73d30ea906Sjfb8856606 struct ipv4_l3_l4_tuple {
74d30ea906Sjfb8856606 	__u32    src_addr;
75d30ea906Sjfb8856606 	__u32    dst_addr;
76d30ea906Sjfb8856606 	__u16    dport;
77d30ea906Sjfb8856606 	__u16    sport;
78*2d9fd380Sjfb8856606 } __rte_packed;
79d30ea906Sjfb8856606 
80d30ea906Sjfb8856606 struct ipv6_l3_l4_tuple {
81d30ea906Sjfb8856606 	__u8        src_addr[16];
82d30ea906Sjfb8856606 	__u8        dst_addr[16];
83d30ea906Sjfb8856606 	__u16       dport;
84d30ea906Sjfb8856606 	__u16       sport;
85*2d9fd380Sjfb8856606 } __rte_packed;
86d30ea906Sjfb8856606 
87d30ea906Sjfb8856606 static const __u8 def_rss_key[TAP_RSS_HASH_KEY_SIZE] = {
88d30ea906Sjfb8856606 	0xd1, 0x81, 0xc6, 0x2c,
89d30ea906Sjfb8856606 	0xf7, 0xf4, 0xdb, 0x5b,
90d30ea906Sjfb8856606 	0x19, 0x83, 0xa2, 0xfc,
91d30ea906Sjfb8856606 	0x94, 0x3e, 0x1a, 0xdb,
92d30ea906Sjfb8856606 	0xd9, 0x38, 0x9e, 0x6b,
93d30ea906Sjfb8856606 	0xd1, 0x03, 0x9c, 0x2c,
94d30ea906Sjfb8856606 	0xa7, 0x44, 0x99, 0xad,
95d30ea906Sjfb8856606 	0x59, 0x3d, 0x56, 0xd9,
96d30ea906Sjfb8856606 	0xf3, 0x25, 0x3c, 0x06,
97d30ea906Sjfb8856606 	0x2a, 0xdc, 0x1f, 0xfc,
98d30ea906Sjfb8856606 };
99d30ea906Sjfb8856606 
100d30ea906Sjfb8856606 static __u32  __attribute__((always_inline))
rte_softrss_be(const __u32 * input_tuple,const uint8_t * rss_key,__u8 input_len)101d30ea906Sjfb8856606 rte_softrss_be(const __u32 *input_tuple, const uint8_t *rss_key,
102d30ea906Sjfb8856606 		__u8 input_len)
103d30ea906Sjfb8856606 {
104d30ea906Sjfb8856606 	__u32 i, j, hash = 0;
105d30ea906Sjfb8856606 #pragma unroll
106d30ea906Sjfb8856606 	for (j = 0; j < input_len; j++) {
107d30ea906Sjfb8856606 #pragma unroll
108d30ea906Sjfb8856606 		for (i = 0; i < 32; i++) {
1091646932aSjfb8856606 			if (input_tuple[j] & (1U << (31 - i))) {
110d30ea906Sjfb8856606 				hash ^= ((const __u32 *)def_rss_key)[j] << i |
111d30ea906Sjfb8856606 				(__u32)((uint64_t)
112d30ea906Sjfb8856606 				(((const __u32 *)def_rss_key)[j + 1])
113d30ea906Sjfb8856606 					>> (32 - i));
114d30ea906Sjfb8856606 			}
115d30ea906Sjfb8856606 		}
116d30ea906Sjfb8856606 	}
117d30ea906Sjfb8856606 	return hash;
118d30ea906Sjfb8856606 }
119d30ea906Sjfb8856606 
120d30ea906Sjfb8856606 static int __attribute__((always_inline))
rss_l3_l4(struct __sk_buff * skb)121d30ea906Sjfb8856606 rss_l3_l4(struct __sk_buff *skb)
122d30ea906Sjfb8856606 {
123d30ea906Sjfb8856606 	void *data_end = (void *)(long)skb->data_end;
124d30ea906Sjfb8856606 	void *data = (void *)(long)skb->data;
125d30ea906Sjfb8856606 	__u16 proto = (__u16)skb->protocol;
126d30ea906Sjfb8856606 	__u32 key_idx = 0xdeadbeef;
127d30ea906Sjfb8856606 	__u32 hash;
128d30ea906Sjfb8856606 	struct rss_key *rsskey;
129d30ea906Sjfb8856606 	__u64 off = ETH_HLEN;
130d30ea906Sjfb8856606 	int j;
131d30ea906Sjfb8856606 	__u8 *key = 0;
132d30ea906Sjfb8856606 	__u32 len;
133d30ea906Sjfb8856606 	__u32 queue = 0;
134d30ea906Sjfb8856606 
135d30ea906Sjfb8856606 	rsskey = map_lookup_elem(&map_keys, &key_idx);
136d30ea906Sjfb8856606 	if (!rsskey) {
137d30ea906Sjfb8856606 		printt("hash(): rss key is not configured\n");
138d30ea906Sjfb8856606 		return TC_ACT_OK;
139d30ea906Sjfb8856606 	}
140d30ea906Sjfb8856606 	key = (__u8 *)rsskey->key;
141d30ea906Sjfb8856606 
142d30ea906Sjfb8856606 	/* Get correct proto for 802.1ad */
143d30ea906Sjfb8856606 	if (skb->vlan_present && skb->vlan_proto == htons(ETH_P_8021AD)) {
144d30ea906Sjfb8856606 		if (data + ETH_ALEN * 2 + sizeof(struct vlan_hdr) +
145d30ea906Sjfb8856606 		    sizeof(proto) > data_end)
146d30ea906Sjfb8856606 			return TC_ACT_OK;
147d30ea906Sjfb8856606 		proto = *(__u16 *)(data + ETH_ALEN * 2 +
148d30ea906Sjfb8856606 				   sizeof(struct vlan_hdr));
149d30ea906Sjfb8856606 		off += sizeof(struct vlan_hdr);
150d30ea906Sjfb8856606 	}
151d30ea906Sjfb8856606 
152d30ea906Sjfb8856606 	if (proto == htons(ETH_P_IP)) {
153d30ea906Sjfb8856606 		if (data + off + sizeof(struct iphdr) + sizeof(__u32)
154d30ea906Sjfb8856606 			> data_end)
155d30ea906Sjfb8856606 			return TC_ACT_OK;
156d30ea906Sjfb8856606 
157d30ea906Sjfb8856606 		__u8 *src_dst_addr = data + off + offsetof(struct iphdr, saddr);
158d30ea906Sjfb8856606 		__u8 *src_dst_port = data + off + sizeof(struct iphdr);
159d30ea906Sjfb8856606 		struct ipv4_l3_l4_tuple v4_tuple = {
160d30ea906Sjfb8856606 			.src_addr = IPv4(*(src_dst_addr + 0),
161d30ea906Sjfb8856606 					*(src_dst_addr + 1),
162d30ea906Sjfb8856606 					*(src_dst_addr + 2),
163d30ea906Sjfb8856606 					*(src_dst_addr + 3)),
164d30ea906Sjfb8856606 			.dst_addr = IPv4(*(src_dst_addr + 4),
165d30ea906Sjfb8856606 					*(src_dst_addr + 5),
166d30ea906Sjfb8856606 					*(src_dst_addr + 6),
167d30ea906Sjfb8856606 					*(src_dst_addr + 7)),
168d30ea906Sjfb8856606 			.sport = PORT(*(src_dst_port + 0),
169d30ea906Sjfb8856606 					*(src_dst_port + 1)),
170d30ea906Sjfb8856606 			.dport = PORT(*(src_dst_port + 2),
171d30ea906Sjfb8856606 					*(src_dst_port + 3)),
172d30ea906Sjfb8856606 		};
173d30ea906Sjfb8856606 		__u8 input_len = sizeof(v4_tuple) / sizeof(__u32);
174d30ea906Sjfb8856606 		if (rsskey->hash_fields & (1 << HASH_FIELD_IPV4_L3))
175d30ea906Sjfb8856606 			input_len--;
176d30ea906Sjfb8856606 		hash = rte_softrss_be((__u32 *)&v4_tuple, key, 3);
177d30ea906Sjfb8856606 	} else if (proto == htons(ETH_P_IPV6)) {
178d30ea906Sjfb8856606 		if (data + off + sizeof(struct ipv6hdr) +
179d30ea906Sjfb8856606 					sizeof(__u32) > data_end)
180d30ea906Sjfb8856606 			return TC_ACT_OK;
181d30ea906Sjfb8856606 		__u8 *src_dst_addr = data + off +
182d30ea906Sjfb8856606 					offsetof(struct ipv6hdr, saddr);
183d30ea906Sjfb8856606 		__u8 *src_dst_port = data + off +
184d30ea906Sjfb8856606 					sizeof(struct ipv6hdr);
185d30ea906Sjfb8856606 		struct ipv6_l3_l4_tuple v6_tuple;
186d30ea906Sjfb8856606 		for (j = 0; j < 4; j++)
187d30ea906Sjfb8856606 			*((uint32_t *)&v6_tuple.src_addr + j) =
188d30ea906Sjfb8856606 				__builtin_bswap32(*((uint32_t *)
189d30ea906Sjfb8856606 						src_dst_addr + j));
190d30ea906Sjfb8856606 		for (j = 0; j < 4; j++)
191d30ea906Sjfb8856606 			*((uint32_t *)&v6_tuple.dst_addr + j) =
192d30ea906Sjfb8856606 				__builtin_bswap32(*((uint32_t *)
193d30ea906Sjfb8856606 						src_dst_addr + 4 + j));
194d30ea906Sjfb8856606 		v6_tuple.sport = PORT(*(src_dst_port + 0),
195d30ea906Sjfb8856606 			      *(src_dst_port + 1));
196d30ea906Sjfb8856606 		v6_tuple.dport = PORT(*(src_dst_port + 2),
197d30ea906Sjfb8856606 			      *(src_dst_port + 3));
198d30ea906Sjfb8856606 
199d30ea906Sjfb8856606 		__u8 input_len = sizeof(v6_tuple) / sizeof(__u32);
200d30ea906Sjfb8856606 		if (rsskey->hash_fields & (1 << HASH_FIELD_IPV6_L3))
201d30ea906Sjfb8856606 			input_len--;
202d30ea906Sjfb8856606 		hash = rte_softrss_be((__u32 *)&v6_tuple, key, 9);
203d30ea906Sjfb8856606 	} else {
204d30ea906Sjfb8856606 		return TC_ACT_PIPE;
205d30ea906Sjfb8856606 	}
206d30ea906Sjfb8856606 
207d30ea906Sjfb8856606 	queue = rsskey->queues[(hash % rsskey->nb_queues) &
208d30ea906Sjfb8856606 				       (TAP_MAX_QUEUES - 1)];
209d30ea906Sjfb8856606 	skb->cb[1] = QUEUE_OFFSET + queue;
210d30ea906Sjfb8856606 	/* printt(">>>>> rss_l3_l4 hash=0x%x queue=%u\n", hash, queue); */
211d30ea906Sjfb8856606 
212d30ea906Sjfb8856606 	return TC_ACT_RECLASSIFY;
213d30ea906Sjfb8856606 }
214d30ea906Sjfb8856606 
215d30ea906Sjfb8856606 #define RSS(L)						\
216d30ea906Sjfb8856606 	__section(#L) int				\
217d30ea906Sjfb8856606 		L ## _hash(struct __sk_buff *skb)	\
218d30ea906Sjfb8856606 	{						\
219d30ea906Sjfb8856606 		return rss_ ## L (skb);			\
220d30ea906Sjfb8856606 	}
221d30ea906Sjfb8856606 
222d30ea906Sjfb8856606 RSS(l3_l4)
223d30ea906Sjfb8856606 
224d30ea906Sjfb8856606 BPF_LICENSE("Dual BSD/GPL");
225