1f3fa412dSAndrew Melnychenko /*
2f3fa412dSAndrew Melnychenko * eBPF RSS program
3f3fa412dSAndrew Melnychenko *
4f3fa412dSAndrew Melnychenko * Developed by Daynix Computing LTD (http://www.daynix.com)
5f3fa412dSAndrew Melnychenko *
6f3fa412dSAndrew Melnychenko * Authors:
7f3fa412dSAndrew Melnychenko * Andrew Melnychenko <andrew@daynix.com>
8f3fa412dSAndrew Melnychenko * Yuri Benditovich <yuri.benditovich@daynix.com>
9f3fa412dSAndrew Melnychenko *
10f3fa412dSAndrew Melnychenko * This work is licensed under the terms of the GNU GPL, version 2. See
11f3fa412dSAndrew Melnychenko * the COPYING file in the top-level directory.
12f3fa412dSAndrew Melnychenko *
13f3fa412dSAndrew Melnychenko * Prepare:
14f3fa412dSAndrew Melnychenko * Requires llvm, clang, bpftool, linux kernel tree
15f3fa412dSAndrew Melnychenko *
16f3fa412dSAndrew Melnychenko * Build rss.bpf.skeleton.h:
17f3fa412dSAndrew Melnychenko * make -f Makefile.ebpf clean all
18f3fa412dSAndrew Melnychenko */
19f3fa412dSAndrew Melnychenko
20f3fa412dSAndrew Melnychenko #include <stddef.h>
21f3fa412dSAndrew Melnychenko #include <stdbool.h>
22f3fa412dSAndrew Melnychenko #include <linux/bpf.h>
23f3fa412dSAndrew Melnychenko
24f3fa412dSAndrew Melnychenko #include <linux/in.h>
25f3fa412dSAndrew Melnychenko #include <linux/if_ether.h>
26f3fa412dSAndrew Melnychenko #include <linux/ip.h>
27f3fa412dSAndrew Melnychenko #include <linux/ipv6.h>
28f3fa412dSAndrew Melnychenko
29f3fa412dSAndrew Melnychenko #include <linux/udp.h>
30f3fa412dSAndrew Melnychenko #include <linux/tcp.h>
31f3fa412dSAndrew Melnychenko
32f3fa412dSAndrew Melnychenko #include <bpf/bpf_helpers.h>
33f3fa412dSAndrew Melnychenko #include <bpf/bpf_endian.h>
34f3fa412dSAndrew Melnychenko #include <linux/virtio_net.h>
35f3fa412dSAndrew Melnychenko
36f3fa412dSAndrew Melnychenko #define INDIRECTION_TABLE_SIZE 128
37f3fa412dSAndrew Melnychenko #define HASH_CALCULATION_BUFFER_SIZE 36
38f3fa412dSAndrew Melnychenko
39f3fa412dSAndrew Melnychenko struct rss_config_t {
40f3fa412dSAndrew Melnychenko __u8 redirect;
41f3fa412dSAndrew Melnychenko __u8 populate_hash;
42f3fa412dSAndrew Melnychenko __u32 hash_types;
43f3fa412dSAndrew Melnychenko __u16 indirections_len;
44f3fa412dSAndrew Melnychenko __u16 default_queue;
45f3fa412dSAndrew Melnychenko } __attribute__((packed));
46f3fa412dSAndrew Melnychenko
47f3fa412dSAndrew Melnychenko struct toeplitz_key_data_t {
48f3fa412dSAndrew Melnychenko __u32 leftmost_32_bits;
49f3fa412dSAndrew Melnychenko __u8 next_byte[HASH_CALCULATION_BUFFER_SIZE];
50f3fa412dSAndrew Melnychenko };
51f3fa412dSAndrew Melnychenko
52f3fa412dSAndrew Melnychenko struct packet_hash_info_t {
53f3fa412dSAndrew Melnychenko __u8 is_ipv4;
54f3fa412dSAndrew Melnychenko __u8 is_ipv6;
55f3fa412dSAndrew Melnychenko __u8 is_udp;
56f3fa412dSAndrew Melnychenko __u8 is_tcp;
57f3fa412dSAndrew Melnychenko __u8 is_ipv6_ext_src;
58f3fa412dSAndrew Melnychenko __u8 is_ipv6_ext_dst;
59f3fa412dSAndrew Melnychenko __u8 is_fragmented;
60f3fa412dSAndrew Melnychenko
61f3fa412dSAndrew Melnychenko __u16 src_port;
62f3fa412dSAndrew Melnychenko __u16 dst_port;
63f3fa412dSAndrew Melnychenko
64f3fa412dSAndrew Melnychenko union {
65f3fa412dSAndrew Melnychenko struct {
66f3fa412dSAndrew Melnychenko __be32 in_src;
67f3fa412dSAndrew Melnychenko __be32 in_dst;
68f3fa412dSAndrew Melnychenko };
69f3fa412dSAndrew Melnychenko
70f3fa412dSAndrew Melnychenko struct {
71f3fa412dSAndrew Melnychenko struct in6_addr in6_src;
72f3fa412dSAndrew Melnychenko struct in6_addr in6_dst;
73f3fa412dSAndrew Melnychenko struct in6_addr in6_ext_src;
74f3fa412dSAndrew Melnychenko struct in6_addr in6_ext_dst;
75f3fa412dSAndrew Melnychenko };
76f3fa412dSAndrew Melnychenko };
77f3fa412dSAndrew Melnychenko };
78f3fa412dSAndrew Melnychenko
79197a1372SShreesh Adiga struct {
80197a1372SShreesh Adiga __uint(type, BPF_MAP_TYPE_ARRAY);
81197a1372SShreesh Adiga __uint(key_size, sizeof(__u32));
82197a1372SShreesh Adiga __uint(value_size, sizeof(struct rss_config_t));
83197a1372SShreesh Adiga __uint(max_entries, 1);
840cc14182SAndrew Melnychenko __uint(map_flags, BPF_F_MMAPABLE);
85197a1372SShreesh Adiga } tap_rss_map_configurations SEC(".maps");
86f3fa412dSAndrew Melnychenko
87197a1372SShreesh Adiga struct {
88197a1372SShreesh Adiga __uint(type, BPF_MAP_TYPE_ARRAY);
89197a1372SShreesh Adiga __uint(key_size, sizeof(__u32));
90197a1372SShreesh Adiga __uint(value_size, sizeof(struct toeplitz_key_data_t));
91197a1372SShreesh Adiga __uint(max_entries, 1);
920cc14182SAndrew Melnychenko __uint(map_flags, BPF_F_MMAPABLE);
93197a1372SShreesh Adiga } tap_rss_map_toeplitz_key SEC(".maps");
94f3fa412dSAndrew Melnychenko
95197a1372SShreesh Adiga struct {
96197a1372SShreesh Adiga __uint(type, BPF_MAP_TYPE_ARRAY);
97197a1372SShreesh Adiga __uint(key_size, sizeof(__u32));
98197a1372SShreesh Adiga __uint(value_size, sizeof(__u16));
99197a1372SShreesh Adiga __uint(max_entries, INDIRECTION_TABLE_SIZE);
1000cc14182SAndrew Melnychenko __uint(map_flags, BPF_F_MMAPABLE);
101197a1372SShreesh Adiga } tap_rss_map_indirection_table SEC(".maps");
102f3fa412dSAndrew Melnychenko
net_rx_rss_add_chunk(__u8 * rss_input,size_t * bytes_written,const void * ptr,size_t size)103f3fa412dSAndrew Melnychenko static inline void net_rx_rss_add_chunk(__u8 *rss_input, size_t *bytes_written,
104f3fa412dSAndrew Melnychenko const void *ptr, size_t size) {
105f3fa412dSAndrew Melnychenko __builtin_memcpy(&rss_input[*bytes_written], ptr, size);
106f3fa412dSAndrew Melnychenko *bytes_written += size;
107f3fa412dSAndrew Melnychenko }
108f3fa412dSAndrew Melnychenko
109f3fa412dSAndrew Melnychenko static inline
net_toeplitz_add(__u32 * result,__u8 * input,__u32 len,struct toeplitz_key_data_t * key)110f3fa412dSAndrew Melnychenko void net_toeplitz_add(__u32 *result,
111f3fa412dSAndrew Melnychenko __u8 *input,
112f3fa412dSAndrew Melnychenko __u32 len
113f3fa412dSAndrew Melnychenko , struct toeplitz_key_data_t *key) {
114f3fa412dSAndrew Melnychenko
115f3fa412dSAndrew Melnychenko __u32 accumulator = *result;
116f3fa412dSAndrew Melnychenko __u32 leftmost_32_bits = key->leftmost_32_bits;
117f3fa412dSAndrew Melnychenko __u32 byte;
118f3fa412dSAndrew Melnychenko
119f3fa412dSAndrew Melnychenko for (byte = 0; byte < HASH_CALCULATION_BUFFER_SIZE; byte++) {
120f3fa412dSAndrew Melnychenko __u8 input_byte = input[byte];
121f3fa412dSAndrew Melnychenko __u8 key_byte = key->next_byte[byte];
122f3fa412dSAndrew Melnychenko __u8 bit;
123f3fa412dSAndrew Melnychenko
124f3fa412dSAndrew Melnychenko for (bit = 0; bit < 8; bit++) {
125f3fa412dSAndrew Melnychenko if (input_byte & (1 << 7)) {
126f3fa412dSAndrew Melnychenko accumulator ^= leftmost_32_bits;
127f3fa412dSAndrew Melnychenko }
128f3fa412dSAndrew Melnychenko
129f3fa412dSAndrew Melnychenko leftmost_32_bits =
130f3fa412dSAndrew Melnychenko (leftmost_32_bits << 1) | ((key_byte & (1 << 7)) >> 7);
131f3fa412dSAndrew Melnychenko
132f3fa412dSAndrew Melnychenko input_byte <<= 1;
133f3fa412dSAndrew Melnychenko key_byte <<= 1;
134f3fa412dSAndrew Melnychenko }
135f3fa412dSAndrew Melnychenko }
136f3fa412dSAndrew Melnychenko
137f3fa412dSAndrew Melnychenko *result = accumulator;
138f3fa412dSAndrew Melnychenko }
139f3fa412dSAndrew Melnychenko
140f3fa412dSAndrew Melnychenko
ip6_extension_header_type(__u8 hdr_type)141f3fa412dSAndrew Melnychenko static inline int ip6_extension_header_type(__u8 hdr_type)
142f3fa412dSAndrew Melnychenko {
143f3fa412dSAndrew Melnychenko switch (hdr_type) {
144f3fa412dSAndrew Melnychenko case IPPROTO_HOPOPTS:
145f3fa412dSAndrew Melnychenko case IPPROTO_ROUTING:
146f3fa412dSAndrew Melnychenko case IPPROTO_FRAGMENT:
147f3fa412dSAndrew Melnychenko case IPPROTO_ICMPV6:
148f3fa412dSAndrew Melnychenko case IPPROTO_NONE:
149f3fa412dSAndrew Melnychenko case IPPROTO_DSTOPTS:
150f3fa412dSAndrew Melnychenko case IPPROTO_MH:
151f3fa412dSAndrew Melnychenko return 1;
152f3fa412dSAndrew Melnychenko default:
153f3fa412dSAndrew Melnychenko return 0;
154f3fa412dSAndrew Melnychenko }
155f3fa412dSAndrew Melnychenko }
156f3fa412dSAndrew Melnychenko /*
157f3fa412dSAndrew Melnychenko * According to
158f3fa412dSAndrew Melnychenko * https://www.iana.org/assignments/ipv6-parameters/ipv6-parameters.xhtml
159f3fa412dSAndrew Melnychenko * we expect that there are would be no more than 11 extensions in IPv6 header,
160f3fa412dSAndrew Melnychenko * also there is 27 TLV options for Destination and Hop-by-hop extensions.
161f3fa412dSAndrew Melnychenko * Need to choose reasonable amount of maximum extensions/options we may
162f3fa412dSAndrew Melnychenko * check to find ext src/dst.
163f3fa412dSAndrew Melnychenko */
164f3fa412dSAndrew Melnychenko #define IP6_EXTENSIONS_COUNT 11
165f3fa412dSAndrew Melnychenko #define IP6_OPTIONS_COUNT 30
166f3fa412dSAndrew Melnychenko
parse_ipv6_ext(struct __sk_buff * skb,struct packet_hash_info_t * info,__u8 * l4_protocol,size_t * l4_offset)167f3fa412dSAndrew Melnychenko static inline int parse_ipv6_ext(struct __sk_buff *skb,
168f3fa412dSAndrew Melnychenko struct packet_hash_info_t *info,
169f3fa412dSAndrew Melnychenko __u8 *l4_protocol, size_t *l4_offset)
170f3fa412dSAndrew Melnychenko {
171f3fa412dSAndrew Melnychenko int err = 0;
172f3fa412dSAndrew Melnychenko
173f3fa412dSAndrew Melnychenko if (!ip6_extension_header_type(*l4_protocol)) {
174f3fa412dSAndrew Melnychenko return 0;
175f3fa412dSAndrew Melnychenko }
176f3fa412dSAndrew Melnychenko
177f3fa412dSAndrew Melnychenko struct ipv6_opt_hdr ext_hdr = {};
178f3fa412dSAndrew Melnychenko
179f3fa412dSAndrew Melnychenko for (unsigned int i = 0; i < IP6_EXTENSIONS_COUNT; ++i) {
180f3fa412dSAndrew Melnychenko
181f3fa412dSAndrew Melnychenko err = bpf_skb_load_bytes_relative(skb, *l4_offset, &ext_hdr,
182f3fa412dSAndrew Melnychenko sizeof(ext_hdr), BPF_HDR_START_NET);
183f3fa412dSAndrew Melnychenko if (err) {
184f3fa412dSAndrew Melnychenko goto error;
185f3fa412dSAndrew Melnychenko }
186f3fa412dSAndrew Melnychenko
187f3fa412dSAndrew Melnychenko if (*l4_protocol == IPPROTO_ROUTING) {
188f3fa412dSAndrew Melnychenko struct ipv6_rt_hdr ext_rt = {};
189f3fa412dSAndrew Melnychenko
190f3fa412dSAndrew Melnychenko err = bpf_skb_load_bytes_relative(skb, *l4_offset, &ext_rt,
191f3fa412dSAndrew Melnychenko sizeof(ext_rt), BPF_HDR_START_NET);
192f3fa412dSAndrew Melnychenko if (err) {
193f3fa412dSAndrew Melnychenko goto error;
194f3fa412dSAndrew Melnychenko }
195f3fa412dSAndrew Melnychenko
196f3fa412dSAndrew Melnychenko if ((ext_rt.type == IPV6_SRCRT_TYPE_2) &&
197f3fa412dSAndrew Melnychenko (ext_rt.hdrlen == sizeof(struct in6_addr) / 8) &&
198f3fa412dSAndrew Melnychenko (ext_rt.segments_left == 1)) {
199f3fa412dSAndrew Melnychenko
200f3fa412dSAndrew Melnychenko err = bpf_skb_load_bytes_relative(skb,
201f3fa412dSAndrew Melnychenko *l4_offset + offsetof(struct rt2_hdr, addr),
202f3fa412dSAndrew Melnychenko &info->in6_ext_dst, sizeof(info->in6_ext_dst),
203f3fa412dSAndrew Melnychenko BPF_HDR_START_NET);
204f3fa412dSAndrew Melnychenko if (err) {
205f3fa412dSAndrew Melnychenko goto error;
206f3fa412dSAndrew Melnychenko }
207f3fa412dSAndrew Melnychenko
208f3fa412dSAndrew Melnychenko info->is_ipv6_ext_dst = 1;
209f3fa412dSAndrew Melnychenko }
210f3fa412dSAndrew Melnychenko
211f3fa412dSAndrew Melnychenko } else if (*l4_protocol == IPPROTO_DSTOPTS) {
212f3fa412dSAndrew Melnychenko struct ipv6_opt_t {
213f3fa412dSAndrew Melnychenko __u8 type;
214f3fa412dSAndrew Melnychenko __u8 length;
215f3fa412dSAndrew Melnychenko } __attribute__((packed)) opt = {};
216f3fa412dSAndrew Melnychenko
217f3fa412dSAndrew Melnychenko size_t opt_offset = sizeof(ext_hdr);
218f3fa412dSAndrew Melnychenko
219f3fa412dSAndrew Melnychenko for (unsigned int j = 0; j < IP6_OPTIONS_COUNT; ++j) {
220f3fa412dSAndrew Melnychenko err = bpf_skb_load_bytes_relative(skb, *l4_offset + opt_offset,
221f3fa412dSAndrew Melnychenko &opt, sizeof(opt), BPF_HDR_START_NET);
222f3fa412dSAndrew Melnychenko if (err) {
223f3fa412dSAndrew Melnychenko goto error;
224f3fa412dSAndrew Melnychenko }
225f3fa412dSAndrew Melnychenko
226f3fa412dSAndrew Melnychenko if (opt.type == IPV6_TLV_HAO) {
227f3fa412dSAndrew Melnychenko err = bpf_skb_load_bytes_relative(skb,
228f3fa412dSAndrew Melnychenko *l4_offset + opt_offset
229f3fa412dSAndrew Melnychenko + offsetof(struct ipv6_destopt_hao, addr),
230f3fa412dSAndrew Melnychenko &info->in6_ext_src, sizeof(info->in6_ext_src),
231f3fa412dSAndrew Melnychenko BPF_HDR_START_NET);
232f3fa412dSAndrew Melnychenko if (err) {
233f3fa412dSAndrew Melnychenko goto error;
234f3fa412dSAndrew Melnychenko }
235f3fa412dSAndrew Melnychenko
236f3fa412dSAndrew Melnychenko info->is_ipv6_ext_src = 1;
237f3fa412dSAndrew Melnychenko break;
238f3fa412dSAndrew Melnychenko }
239f3fa412dSAndrew Melnychenko
240f3fa412dSAndrew Melnychenko opt_offset += (opt.type == IPV6_TLV_PAD1) ?
241f3fa412dSAndrew Melnychenko 1 : opt.length + sizeof(opt);
242f3fa412dSAndrew Melnychenko
243f3fa412dSAndrew Melnychenko if (opt_offset + 1 >= ext_hdr.hdrlen * 8) {
244f3fa412dSAndrew Melnychenko break;
245f3fa412dSAndrew Melnychenko }
246f3fa412dSAndrew Melnychenko }
247f3fa412dSAndrew Melnychenko } else if (*l4_protocol == IPPROTO_FRAGMENT) {
248f3fa412dSAndrew Melnychenko info->is_fragmented = true;
249f3fa412dSAndrew Melnychenko }
250f3fa412dSAndrew Melnychenko
251f3fa412dSAndrew Melnychenko *l4_protocol = ext_hdr.nexthdr;
252f3fa412dSAndrew Melnychenko *l4_offset += (ext_hdr.hdrlen + 1) * 8;
253f3fa412dSAndrew Melnychenko
254f3fa412dSAndrew Melnychenko if (!ip6_extension_header_type(ext_hdr.nexthdr)) {
255f3fa412dSAndrew Melnychenko return 0;
256f3fa412dSAndrew Melnychenko }
257f3fa412dSAndrew Melnychenko }
258f3fa412dSAndrew Melnychenko
259f3fa412dSAndrew Melnychenko return 0;
260f3fa412dSAndrew Melnychenko error:
261f3fa412dSAndrew Melnychenko return err;
262f3fa412dSAndrew Melnychenko }
263f3fa412dSAndrew Melnychenko
parse_eth_type(struct __sk_buff * skb)264f3fa412dSAndrew Melnychenko static __be16 parse_eth_type(struct __sk_buff *skb)
265f3fa412dSAndrew Melnychenko {
266f3fa412dSAndrew Melnychenko unsigned int offset = 12;
267f3fa412dSAndrew Melnychenko __be16 ret = 0;
268f3fa412dSAndrew Melnychenko int err = 0;
269f3fa412dSAndrew Melnychenko
270f3fa412dSAndrew Melnychenko err = bpf_skb_load_bytes_relative(skb, offset, &ret, sizeof(ret),
271f3fa412dSAndrew Melnychenko BPF_HDR_START_MAC);
272f3fa412dSAndrew Melnychenko if (err) {
273f3fa412dSAndrew Melnychenko return 0;
274f3fa412dSAndrew Melnychenko }
275f3fa412dSAndrew Melnychenko
276f3fa412dSAndrew Melnychenko switch (bpf_ntohs(ret)) {
277f3fa412dSAndrew Melnychenko case ETH_P_8021AD:
278f3fa412dSAndrew Melnychenko offset += 4;
279f3fa412dSAndrew Melnychenko case ETH_P_8021Q:
280f3fa412dSAndrew Melnychenko offset += 4;
281f3fa412dSAndrew Melnychenko err = bpf_skb_load_bytes_relative(skb, offset, &ret, sizeof(ret),
282f3fa412dSAndrew Melnychenko BPF_HDR_START_MAC);
283f3fa412dSAndrew Melnychenko default:
284f3fa412dSAndrew Melnychenko break;
285f3fa412dSAndrew Melnychenko }
286f3fa412dSAndrew Melnychenko
287f3fa412dSAndrew Melnychenko if (err) {
288f3fa412dSAndrew Melnychenko return 0;
289f3fa412dSAndrew Melnychenko }
290f3fa412dSAndrew Melnychenko
291f3fa412dSAndrew Melnychenko return ret;
292f3fa412dSAndrew Melnychenko }
293f3fa412dSAndrew Melnychenko
parse_packet(struct __sk_buff * skb,struct packet_hash_info_t * info)294f3fa412dSAndrew Melnychenko static inline int parse_packet(struct __sk_buff *skb,
295f3fa412dSAndrew Melnychenko struct packet_hash_info_t *info)
296f3fa412dSAndrew Melnychenko {
297f3fa412dSAndrew Melnychenko int err = 0;
298f3fa412dSAndrew Melnychenko
299f3fa412dSAndrew Melnychenko if (!info || !skb) {
300f3fa412dSAndrew Melnychenko return -1;
301f3fa412dSAndrew Melnychenko }
302f3fa412dSAndrew Melnychenko
303f3fa412dSAndrew Melnychenko size_t l4_offset = 0;
304f3fa412dSAndrew Melnychenko __u8 l4_protocol = 0;
305f3fa412dSAndrew Melnychenko __u16 l3_protocol = bpf_ntohs(parse_eth_type(skb));
306f3fa412dSAndrew Melnychenko if (l3_protocol == 0) {
307f3fa412dSAndrew Melnychenko err = -1;
308f3fa412dSAndrew Melnychenko goto error;
309f3fa412dSAndrew Melnychenko }
310f3fa412dSAndrew Melnychenko
311f3fa412dSAndrew Melnychenko if (l3_protocol == ETH_P_IP) {
312f3fa412dSAndrew Melnychenko info->is_ipv4 = 1;
313f3fa412dSAndrew Melnychenko
314f3fa412dSAndrew Melnychenko struct iphdr ip = {};
315f3fa412dSAndrew Melnychenko err = bpf_skb_load_bytes_relative(skb, 0, &ip, sizeof(ip),
316f3fa412dSAndrew Melnychenko BPF_HDR_START_NET);
317f3fa412dSAndrew Melnychenko if (err) {
318f3fa412dSAndrew Melnychenko goto error;
319f3fa412dSAndrew Melnychenko }
320f3fa412dSAndrew Melnychenko
321f3fa412dSAndrew Melnychenko info->in_src = ip.saddr;
322f3fa412dSAndrew Melnychenko info->in_dst = ip.daddr;
3230cc14182SAndrew Melnychenko info->is_fragmented = !!(bpf_ntohs(ip.frag_off) & (0x2000 | 0x1fff));
324f3fa412dSAndrew Melnychenko
325f3fa412dSAndrew Melnychenko l4_protocol = ip.protocol;
326f3fa412dSAndrew Melnychenko l4_offset = ip.ihl * 4;
327f3fa412dSAndrew Melnychenko } else if (l3_protocol == ETH_P_IPV6) {
328f3fa412dSAndrew Melnychenko info->is_ipv6 = 1;
329f3fa412dSAndrew Melnychenko
330f3fa412dSAndrew Melnychenko struct ipv6hdr ip6 = {};
331f3fa412dSAndrew Melnychenko err = bpf_skb_load_bytes_relative(skb, 0, &ip6, sizeof(ip6),
332f3fa412dSAndrew Melnychenko BPF_HDR_START_NET);
333f3fa412dSAndrew Melnychenko if (err) {
334f3fa412dSAndrew Melnychenko goto error;
335f3fa412dSAndrew Melnychenko }
336f3fa412dSAndrew Melnychenko
337f3fa412dSAndrew Melnychenko info->in6_src = ip6.saddr;
338f3fa412dSAndrew Melnychenko info->in6_dst = ip6.daddr;
339f3fa412dSAndrew Melnychenko
340f3fa412dSAndrew Melnychenko l4_protocol = ip6.nexthdr;
341f3fa412dSAndrew Melnychenko l4_offset = sizeof(ip6);
342f3fa412dSAndrew Melnychenko
343f3fa412dSAndrew Melnychenko err = parse_ipv6_ext(skb, info, &l4_protocol, &l4_offset);
344f3fa412dSAndrew Melnychenko if (err) {
345f3fa412dSAndrew Melnychenko goto error;
346f3fa412dSAndrew Melnychenko }
347f3fa412dSAndrew Melnychenko }
348f3fa412dSAndrew Melnychenko
349f3fa412dSAndrew Melnychenko if (l4_protocol != 0 && !info->is_fragmented) {
350f3fa412dSAndrew Melnychenko if (l4_protocol == IPPROTO_TCP) {
351f3fa412dSAndrew Melnychenko info->is_tcp = 1;
352f3fa412dSAndrew Melnychenko
353f3fa412dSAndrew Melnychenko struct tcphdr tcp = {};
354f3fa412dSAndrew Melnychenko err = bpf_skb_load_bytes_relative(skb, l4_offset, &tcp, sizeof(tcp),
355f3fa412dSAndrew Melnychenko BPF_HDR_START_NET);
356f3fa412dSAndrew Melnychenko if (err) {
357f3fa412dSAndrew Melnychenko goto error;
358f3fa412dSAndrew Melnychenko }
359f3fa412dSAndrew Melnychenko
360f3fa412dSAndrew Melnychenko info->src_port = tcp.source;
361f3fa412dSAndrew Melnychenko info->dst_port = tcp.dest;
362f3fa412dSAndrew Melnychenko } else if (l4_protocol == IPPROTO_UDP) { /* TODO: add udplite? */
363f3fa412dSAndrew Melnychenko info->is_udp = 1;
364f3fa412dSAndrew Melnychenko
365f3fa412dSAndrew Melnychenko struct udphdr udp = {};
366f3fa412dSAndrew Melnychenko err = bpf_skb_load_bytes_relative(skb, l4_offset, &udp, sizeof(udp),
367f3fa412dSAndrew Melnychenko BPF_HDR_START_NET);
368f3fa412dSAndrew Melnychenko if (err) {
369f3fa412dSAndrew Melnychenko goto error;
370f3fa412dSAndrew Melnychenko }
371f3fa412dSAndrew Melnychenko
372f3fa412dSAndrew Melnychenko info->src_port = udp.source;
373f3fa412dSAndrew Melnychenko info->dst_port = udp.dest;
374f3fa412dSAndrew Melnychenko }
375f3fa412dSAndrew Melnychenko }
376f3fa412dSAndrew Melnychenko
377f3fa412dSAndrew Melnychenko return 0;
378f3fa412dSAndrew Melnychenko
379f3fa412dSAndrew Melnychenko error:
380f3fa412dSAndrew Melnychenko return err;
381f3fa412dSAndrew Melnychenko }
382f3fa412dSAndrew Melnychenko
calculate_rss_hash(struct __sk_buff * skb,struct rss_config_t * config,struct toeplitz_key_data_t * toe,__u32 * result)38372fa42cfSAkihiko Odaki static inline bool calculate_rss_hash(struct __sk_buff *skb,
38472fa42cfSAkihiko Odaki struct rss_config_t *config,
38572fa42cfSAkihiko Odaki struct toeplitz_key_data_t *toe,
38672fa42cfSAkihiko Odaki __u32 *result)
387f3fa412dSAndrew Melnychenko {
388f3fa412dSAndrew Melnychenko __u8 rss_input[HASH_CALCULATION_BUFFER_SIZE] = {};
389f3fa412dSAndrew Melnychenko size_t bytes_written = 0;
390f3fa412dSAndrew Melnychenko int err = 0;
391f3fa412dSAndrew Melnychenko struct packet_hash_info_t packet_info = {};
392f3fa412dSAndrew Melnychenko
393f3fa412dSAndrew Melnychenko err = parse_packet(skb, &packet_info);
394f3fa412dSAndrew Melnychenko if (err) {
39572fa42cfSAkihiko Odaki return false;
396f3fa412dSAndrew Melnychenko }
397f3fa412dSAndrew Melnychenko
398f3fa412dSAndrew Melnychenko if (packet_info.is_ipv4) {
399f3fa412dSAndrew Melnychenko if (packet_info.is_tcp &&
400f3fa412dSAndrew Melnychenko config->hash_types & VIRTIO_NET_RSS_HASH_TYPE_TCPv4) {
401f3fa412dSAndrew Melnychenko
402f3fa412dSAndrew Melnychenko net_rx_rss_add_chunk(rss_input, &bytes_written,
403f3fa412dSAndrew Melnychenko &packet_info.in_src,
404f3fa412dSAndrew Melnychenko sizeof(packet_info.in_src));
405f3fa412dSAndrew Melnychenko net_rx_rss_add_chunk(rss_input, &bytes_written,
406f3fa412dSAndrew Melnychenko &packet_info.in_dst,
407f3fa412dSAndrew Melnychenko sizeof(packet_info.in_dst));
408f3fa412dSAndrew Melnychenko net_rx_rss_add_chunk(rss_input, &bytes_written,
409f3fa412dSAndrew Melnychenko &packet_info.src_port,
410f3fa412dSAndrew Melnychenko sizeof(packet_info.src_port));
411f3fa412dSAndrew Melnychenko net_rx_rss_add_chunk(rss_input, &bytes_written,
412f3fa412dSAndrew Melnychenko &packet_info.dst_port,
413f3fa412dSAndrew Melnychenko sizeof(packet_info.dst_port));
414f3fa412dSAndrew Melnychenko } else if (packet_info.is_udp &&
415f3fa412dSAndrew Melnychenko config->hash_types & VIRTIO_NET_RSS_HASH_TYPE_UDPv4) {
416f3fa412dSAndrew Melnychenko
417f3fa412dSAndrew Melnychenko net_rx_rss_add_chunk(rss_input, &bytes_written,
418f3fa412dSAndrew Melnychenko &packet_info.in_src,
419f3fa412dSAndrew Melnychenko sizeof(packet_info.in_src));
420f3fa412dSAndrew Melnychenko net_rx_rss_add_chunk(rss_input, &bytes_written,
421f3fa412dSAndrew Melnychenko &packet_info.in_dst,
422f3fa412dSAndrew Melnychenko sizeof(packet_info.in_dst));
423f3fa412dSAndrew Melnychenko net_rx_rss_add_chunk(rss_input, &bytes_written,
424f3fa412dSAndrew Melnychenko &packet_info.src_port,
425f3fa412dSAndrew Melnychenko sizeof(packet_info.src_port));
426f3fa412dSAndrew Melnychenko net_rx_rss_add_chunk(rss_input, &bytes_written,
427f3fa412dSAndrew Melnychenko &packet_info.dst_port,
428f3fa412dSAndrew Melnychenko sizeof(packet_info.dst_port));
429f3fa412dSAndrew Melnychenko } else if (config->hash_types & VIRTIO_NET_RSS_HASH_TYPE_IPv4) {
430f3fa412dSAndrew Melnychenko net_rx_rss_add_chunk(rss_input, &bytes_written,
431f3fa412dSAndrew Melnychenko &packet_info.in_src,
432f3fa412dSAndrew Melnychenko sizeof(packet_info.in_src));
433f3fa412dSAndrew Melnychenko net_rx_rss_add_chunk(rss_input, &bytes_written,
434f3fa412dSAndrew Melnychenko &packet_info.in_dst,
435f3fa412dSAndrew Melnychenko sizeof(packet_info.in_dst));
436f3fa412dSAndrew Melnychenko }
437f3fa412dSAndrew Melnychenko } else if (packet_info.is_ipv6) {
438f3fa412dSAndrew Melnychenko if (packet_info.is_tcp &&
439f3fa412dSAndrew Melnychenko config->hash_types & VIRTIO_NET_RSS_HASH_TYPE_TCPv6) {
440f3fa412dSAndrew Melnychenko
441f3fa412dSAndrew Melnychenko if (packet_info.is_ipv6_ext_src &&
442f3fa412dSAndrew Melnychenko config->hash_types & VIRTIO_NET_RSS_HASH_TYPE_TCP_EX) {
443f3fa412dSAndrew Melnychenko
444f3fa412dSAndrew Melnychenko net_rx_rss_add_chunk(rss_input, &bytes_written,
445f3fa412dSAndrew Melnychenko &packet_info.in6_ext_src,
446f3fa412dSAndrew Melnychenko sizeof(packet_info.in6_ext_src));
447f3fa412dSAndrew Melnychenko } else {
448f3fa412dSAndrew Melnychenko net_rx_rss_add_chunk(rss_input, &bytes_written,
449f3fa412dSAndrew Melnychenko &packet_info.in6_src,
450f3fa412dSAndrew Melnychenko sizeof(packet_info.in6_src));
451f3fa412dSAndrew Melnychenko }
452f3fa412dSAndrew Melnychenko if (packet_info.is_ipv6_ext_dst &&
453f3fa412dSAndrew Melnychenko config->hash_types & VIRTIO_NET_RSS_HASH_TYPE_TCP_EX) {
454f3fa412dSAndrew Melnychenko
455f3fa412dSAndrew Melnychenko net_rx_rss_add_chunk(rss_input, &bytes_written,
456f3fa412dSAndrew Melnychenko &packet_info.in6_ext_dst,
457f3fa412dSAndrew Melnychenko sizeof(packet_info.in6_ext_dst));
458f3fa412dSAndrew Melnychenko } else {
459f3fa412dSAndrew Melnychenko net_rx_rss_add_chunk(rss_input, &bytes_written,
460f3fa412dSAndrew Melnychenko &packet_info.in6_dst,
461f3fa412dSAndrew Melnychenko sizeof(packet_info.in6_dst));
462f3fa412dSAndrew Melnychenko }
463f3fa412dSAndrew Melnychenko net_rx_rss_add_chunk(rss_input, &bytes_written,
464f3fa412dSAndrew Melnychenko &packet_info.src_port,
465f3fa412dSAndrew Melnychenko sizeof(packet_info.src_port));
466f3fa412dSAndrew Melnychenko net_rx_rss_add_chunk(rss_input, &bytes_written,
467f3fa412dSAndrew Melnychenko &packet_info.dst_port,
468f3fa412dSAndrew Melnychenko sizeof(packet_info.dst_port));
469f3fa412dSAndrew Melnychenko } else if (packet_info.is_udp &&
470f3fa412dSAndrew Melnychenko config->hash_types & VIRTIO_NET_RSS_HASH_TYPE_UDPv6) {
471f3fa412dSAndrew Melnychenko
472f3fa412dSAndrew Melnychenko if (packet_info.is_ipv6_ext_src &&
473f3fa412dSAndrew Melnychenko config->hash_types & VIRTIO_NET_RSS_HASH_TYPE_UDP_EX) {
474f3fa412dSAndrew Melnychenko
475f3fa412dSAndrew Melnychenko net_rx_rss_add_chunk(rss_input, &bytes_written,
476f3fa412dSAndrew Melnychenko &packet_info.in6_ext_src,
477f3fa412dSAndrew Melnychenko sizeof(packet_info.in6_ext_src));
478f3fa412dSAndrew Melnychenko } else {
479f3fa412dSAndrew Melnychenko net_rx_rss_add_chunk(rss_input, &bytes_written,
480f3fa412dSAndrew Melnychenko &packet_info.in6_src,
481f3fa412dSAndrew Melnychenko sizeof(packet_info.in6_src));
482f3fa412dSAndrew Melnychenko }
483f3fa412dSAndrew Melnychenko if (packet_info.is_ipv6_ext_dst &&
484f3fa412dSAndrew Melnychenko config->hash_types & VIRTIO_NET_RSS_HASH_TYPE_UDP_EX) {
485f3fa412dSAndrew Melnychenko
486f3fa412dSAndrew Melnychenko net_rx_rss_add_chunk(rss_input, &bytes_written,
487f3fa412dSAndrew Melnychenko &packet_info.in6_ext_dst,
488f3fa412dSAndrew Melnychenko sizeof(packet_info.in6_ext_dst));
489f3fa412dSAndrew Melnychenko } else {
490f3fa412dSAndrew Melnychenko net_rx_rss_add_chunk(rss_input, &bytes_written,
491f3fa412dSAndrew Melnychenko &packet_info.in6_dst,
492f3fa412dSAndrew Melnychenko sizeof(packet_info.in6_dst));
493f3fa412dSAndrew Melnychenko }
494f3fa412dSAndrew Melnychenko
495f3fa412dSAndrew Melnychenko net_rx_rss_add_chunk(rss_input, &bytes_written,
496f3fa412dSAndrew Melnychenko &packet_info.src_port,
497f3fa412dSAndrew Melnychenko sizeof(packet_info.src_port));
498f3fa412dSAndrew Melnychenko net_rx_rss_add_chunk(rss_input, &bytes_written,
499f3fa412dSAndrew Melnychenko &packet_info.dst_port,
500f3fa412dSAndrew Melnychenko sizeof(packet_info.dst_port));
501f3fa412dSAndrew Melnychenko
502f3fa412dSAndrew Melnychenko } else if (config->hash_types & VIRTIO_NET_RSS_HASH_TYPE_IPv6) {
503f3fa412dSAndrew Melnychenko if (packet_info.is_ipv6_ext_src &&
504f3fa412dSAndrew Melnychenko config->hash_types & VIRTIO_NET_RSS_HASH_TYPE_IP_EX) {
505f3fa412dSAndrew Melnychenko
506f3fa412dSAndrew Melnychenko net_rx_rss_add_chunk(rss_input, &bytes_written,
507f3fa412dSAndrew Melnychenko &packet_info.in6_ext_src,
508f3fa412dSAndrew Melnychenko sizeof(packet_info.in6_ext_src));
509f3fa412dSAndrew Melnychenko } else {
510f3fa412dSAndrew Melnychenko net_rx_rss_add_chunk(rss_input, &bytes_written,
511f3fa412dSAndrew Melnychenko &packet_info.in6_src,
512f3fa412dSAndrew Melnychenko sizeof(packet_info.in6_src));
513f3fa412dSAndrew Melnychenko }
514f3fa412dSAndrew Melnychenko if (packet_info.is_ipv6_ext_dst &&
515f3fa412dSAndrew Melnychenko config->hash_types & VIRTIO_NET_RSS_HASH_TYPE_IP_EX) {
516f3fa412dSAndrew Melnychenko
517f3fa412dSAndrew Melnychenko net_rx_rss_add_chunk(rss_input, &bytes_written,
518f3fa412dSAndrew Melnychenko &packet_info.in6_ext_dst,
519f3fa412dSAndrew Melnychenko sizeof(packet_info.in6_ext_dst));
520f3fa412dSAndrew Melnychenko } else {
521f3fa412dSAndrew Melnychenko net_rx_rss_add_chunk(rss_input, &bytes_written,
522f3fa412dSAndrew Melnychenko &packet_info.in6_dst,
523f3fa412dSAndrew Melnychenko sizeof(packet_info.in6_dst));
524f3fa412dSAndrew Melnychenko }
525f3fa412dSAndrew Melnychenko }
526f3fa412dSAndrew Melnychenko }
527f3fa412dSAndrew Melnychenko
52872fa42cfSAkihiko Odaki if (!bytes_written) {
52972fa42cfSAkihiko Odaki return false;
530f3fa412dSAndrew Melnychenko }
531f3fa412dSAndrew Melnychenko
53272fa42cfSAkihiko Odaki net_toeplitz_add(result, rss_input, bytes_written, toe);
53372fa42cfSAkihiko Odaki
53472fa42cfSAkihiko Odaki return true;
535f3fa412dSAndrew Melnychenko }
536f3fa412dSAndrew Melnychenko
5370cc14182SAndrew Melnychenko SEC("socket")
tun_rss_steering_prog(struct __sk_buff * skb)538f3fa412dSAndrew Melnychenko int tun_rss_steering_prog(struct __sk_buff *skb)
539f3fa412dSAndrew Melnychenko {
540f3fa412dSAndrew Melnychenko
541f3fa412dSAndrew Melnychenko struct rss_config_t *config;
542f3fa412dSAndrew Melnychenko struct toeplitz_key_data_t *toe;
543f3fa412dSAndrew Melnychenko
544f3fa412dSAndrew Melnychenko __u32 key = 0;
545f3fa412dSAndrew Melnychenko __u32 hash = 0;
546f3fa412dSAndrew Melnychenko
547f3fa412dSAndrew Melnychenko config = bpf_map_lookup_elem(&tap_rss_map_configurations, &key);
548f3fa412dSAndrew Melnychenko toe = bpf_map_lookup_elem(&tap_rss_map_toeplitz_key, &key);
549f3fa412dSAndrew Melnychenko
550*f5c69e7aSAkihiko Odaki if (!config || !toe) {
551*f5c69e7aSAkihiko Odaki return 0;
552f3fa412dSAndrew Melnychenko }
553f3fa412dSAndrew Melnychenko
554*f5c69e7aSAkihiko Odaki if (config->redirect && calculate_rss_hash(skb, config, toe, &hash)) {
555f3fa412dSAndrew Melnychenko __u32 table_idx = hash % config->indirections_len;
556f3fa412dSAndrew Melnychenko __u16 *queue = 0;
557f3fa412dSAndrew Melnychenko
558f3fa412dSAndrew Melnychenko queue = bpf_map_lookup_elem(&tap_rss_map_indirection_table,
559f3fa412dSAndrew Melnychenko &table_idx);
560f3fa412dSAndrew Melnychenko
561f3fa412dSAndrew Melnychenko if (queue) {
562f3fa412dSAndrew Melnychenko return *queue;
563f3fa412dSAndrew Melnychenko }
564f3fa412dSAndrew Melnychenko }
565f3fa412dSAndrew Melnychenko
566f3fa412dSAndrew Melnychenko return config->default_queue;
567f3fa412dSAndrew Melnychenko }
568f3fa412dSAndrew Melnychenko
569f3fa412dSAndrew Melnychenko char _license[] SEC("license") = "GPL v2";
570