xref: /qemu/tools/ebpf/rss.bpf.c (revision 197a137290103993b33f93c90e788ab4984f103a)
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 
79*197a1372SShreesh Adiga struct {
80*197a1372SShreesh Adiga     __uint(type, BPF_MAP_TYPE_ARRAY);
81*197a1372SShreesh Adiga     __uint(key_size, sizeof(__u32));
82*197a1372SShreesh Adiga     __uint(value_size, sizeof(struct rss_config_t));
83*197a1372SShreesh Adiga     __uint(max_entries, 1);
84*197a1372SShreesh Adiga } tap_rss_map_configurations SEC(".maps");
85f3fa412dSAndrew Melnychenko 
86*197a1372SShreesh Adiga struct {
87*197a1372SShreesh Adiga     __uint(type, BPF_MAP_TYPE_ARRAY);
88*197a1372SShreesh Adiga     __uint(key_size, sizeof(__u32));
89*197a1372SShreesh Adiga     __uint(value_size, sizeof(struct toeplitz_key_data_t));
90*197a1372SShreesh Adiga     __uint(max_entries, 1);
91*197a1372SShreesh Adiga } tap_rss_map_toeplitz_key SEC(".maps");
92f3fa412dSAndrew Melnychenko 
93*197a1372SShreesh Adiga struct {
94*197a1372SShreesh Adiga     __uint(type, BPF_MAP_TYPE_ARRAY);
95*197a1372SShreesh Adiga     __uint(key_size, sizeof(__u32));
96*197a1372SShreesh Adiga     __uint(value_size, sizeof(__u16));
97*197a1372SShreesh Adiga     __uint(max_entries, INDIRECTION_TABLE_SIZE);
98*197a1372SShreesh Adiga } tap_rss_map_indirection_table SEC(".maps");
99f3fa412dSAndrew Melnychenko 
100f3fa412dSAndrew Melnychenko static inline void net_rx_rss_add_chunk(__u8 *rss_input, size_t *bytes_written,
101f3fa412dSAndrew Melnychenko                                         const void *ptr, size_t size) {
102f3fa412dSAndrew Melnychenko     __builtin_memcpy(&rss_input[*bytes_written], ptr, size);
103f3fa412dSAndrew Melnychenko     *bytes_written += size;
104f3fa412dSAndrew Melnychenko }
105f3fa412dSAndrew Melnychenko 
106f3fa412dSAndrew Melnychenko static inline
107f3fa412dSAndrew Melnychenko void net_toeplitz_add(__u32 *result,
108f3fa412dSAndrew Melnychenko                       __u8 *input,
109f3fa412dSAndrew Melnychenko                       __u32 len
110f3fa412dSAndrew Melnychenko         , struct toeplitz_key_data_t *key) {
111f3fa412dSAndrew Melnychenko 
112f3fa412dSAndrew Melnychenko     __u32 accumulator = *result;
113f3fa412dSAndrew Melnychenko     __u32 leftmost_32_bits = key->leftmost_32_bits;
114f3fa412dSAndrew Melnychenko     __u32 byte;
115f3fa412dSAndrew Melnychenko 
116f3fa412dSAndrew Melnychenko     for (byte = 0; byte < HASH_CALCULATION_BUFFER_SIZE; byte++) {
117f3fa412dSAndrew Melnychenko         __u8 input_byte = input[byte];
118f3fa412dSAndrew Melnychenko         __u8 key_byte = key->next_byte[byte];
119f3fa412dSAndrew Melnychenko         __u8 bit;
120f3fa412dSAndrew Melnychenko 
121f3fa412dSAndrew Melnychenko         for (bit = 0; bit < 8; bit++) {
122f3fa412dSAndrew Melnychenko             if (input_byte & (1 << 7)) {
123f3fa412dSAndrew Melnychenko                 accumulator ^= leftmost_32_bits;
124f3fa412dSAndrew Melnychenko             }
125f3fa412dSAndrew Melnychenko 
126f3fa412dSAndrew Melnychenko             leftmost_32_bits =
127f3fa412dSAndrew Melnychenko                     (leftmost_32_bits << 1) | ((key_byte & (1 << 7)) >> 7);
128f3fa412dSAndrew Melnychenko 
129f3fa412dSAndrew Melnychenko             input_byte <<= 1;
130f3fa412dSAndrew Melnychenko             key_byte <<= 1;
131f3fa412dSAndrew Melnychenko         }
132f3fa412dSAndrew Melnychenko     }
133f3fa412dSAndrew Melnychenko 
134f3fa412dSAndrew Melnychenko     *result = accumulator;
135f3fa412dSAndrew Melnychenko }
136f3fa412dSAndrew Melnychenko 
137f3fa412dSAndrew Melnychenko 
138f3fa412dSAndrew Melnychenko static inline int ip6_extension_header_type(__u8 hdr_type)
139f3fa412dSAndrew Melnychenko {
140f3fa412dSAndrew Melnychenko     switch (hdr_type) {
141f3fa412dSAndrew Melnychenko     case IPPROTO_HOPOPTS:
142f3fa412dSAndrew Melnychenko     case IPPROTO_ROUTING:
143f3fa412dSAndrew Melnychenko     case IPPROTO_FRAGMENT:
144f3fa412dSAndrew Melnychenko     case IPPROTO_ICMPV6:
145f3fa412dSAndrew Melnychenko     case IPPROTO_NONE:
146f3fa412dSAndrew Melnychenko     case IPPROTO_DSTOPTS:
147f3fa412dSAndrew Melnychenko     case IPPROTO_MH:
148f3fa412dSAndrew Melnychenko         return 1;
149f3fa412dSAndrew Melnychenko     default:
150f3fa412dSAndrew Melnychenko         return 0;
151f3fa412dSAndrew Melnychenko     }
152f3fa412dSAndrew Melnychenko }
153f3fa412dSAndrew Melnychenko /*
154f3fa412dSAndrew Melnychenko  * According to
155f3fa412dSAndrew Melnychenko  * https://www.iana.org/assignments/ipv6-parameters/ipv6-parameters.xhtml
156f3fa412dSAndrew Melnychenko  * we expect that there are would be no more than 11 extensions in IPv6 header,
157f3fa412dSAndrew Melnychenko  * also there is 27 TLV options for Destination and Hop-by-hop extensions.
158f3fa412dSAndrew Melnychenko  * Need to choose reasonable amount of maximum extensions/options we may
159f3fa412dSAndrew Melnychenko  * check to find ext src/dst.
160f3fa412dSAndrew Melnychenko  */
161f3fa412dSAndrew Melnychenko #define IP6_EXTENSIONS_COUNT 11
162f3fa412dSAndrew Melnychenko #define IP6_OPTIONS_COUNT 30
163f3fa412dSAndrew Melnychenko 
164f3fa412dSAndrew Melnychenko static inline int parse_ipv6_ext(struct __sk_buff *skb,
165f3fa412dSAndrew Melnychenko         struct packet_hash_info_t *info,
166f3fa412dSAndrew Melnychenko         __u8 *l4_protocol, size_t *l4_offset)
167f3fa412dSAndrew Melnychenko {
168f3fa412dSAndrew Melnychenko     int err = 0;
169f3fa412dSAndrew Melnychenko 
170f3fa412dSAndrew Melnychenko     if (!ip6_extension_header_type(*l4_protocol)) {
171f3fa412dSAndrew Melnychenko         return 0;
172f3fa412dSAndrew Melnychenko     }
173f3fa412dSAndrew Melnychenko 
174f3fa412dSAndrew Melnychenko     struct ipv6_opt_hdr ext_hdr = {};
175f3fa412dSAndrew Melnychenko 
176f3fa412dSAndrew Melnychenko     for (unsigned int i = 0; i < IP6_EXTENSIONS_COUNT; ++i) {
177f3fa412dSAndrew Melnychenko 
178f3fa412dSAndrew Melnychenko         err = bpf_skb_load_bytes_relative(skb, *l4_offset, &ext_hdr,
179f3fa412dSAndrew Melnychenko                                     sizeof(ext_hdr), BPF_HDR_START_NET);
180f3fa412dSAndrew Melnychenko         if (err) {
181f3fa412dSAndrew Melnychenko             goto error;
182f3fa412dSAndrew Melnychenko         }
183f3fa412dSAndrew Melnychenko 
184f3fa412dSAndrew Melnychenko         if (*l4_protocol == IPPROTO_ROUTING) {
185f3fa412dSAndrew Melnychenko             struct ipv6_rt_hdr ext_rt = {};
186f3fa412dSAndrew Melnychenko 
187f3fa412dSAndrew Melnychenko             err = bpf_skb_load_bytes_relative(skb, *l4_offset, &ext_rt,
188f3fa412dSAndrew Melnychenko                                         sizeof(ext_rt), BPF_HDR_START_NET);
189f3fa412dSAndrew Melnychenko             if (err) {
190f3fa412dSAndrew Melnychenko                 goto error;
191f3fa412dSAndrew Melnychenko             }
192f3fa412dSAndrew Melnychenko 
193f3fa412dSAndrew Melnychenko             if ((ext_rt.type == IPV6_SRCRT_TYPE_2) &&
194f3fa412dSAndrew Melnychenko                     (ext_rt.hdrlen == sizeof(struct in6_addr) / 8) &&
195f3fa412dSAndrew Melnychenko                     (ext_rt.segments_left == 1)) {
196f3fa412dSAndrew Melnychenko 
197f3fa412dSAndrew Melnychenko                 err = bpf_skb_load_bytes_relative(skb,
198f3fa412dSAndrew Melnychenko                     *l4_offset + offsetof(struct rt2_hdr, addr),
199f3fa412dSAndrew Melnychenko                     &info->in6_ext_dst, sizeof(info->in6_ext_dst),
200f3fa412dSAndrew Melnychenko                     BPF_HDR_START_NET);
201f3fa412dSAndrew Melnychenko                 if (err) {
202f3fa412dSAndrew Melnychenko                     goto error;
203f3fa412dSAndrew Melnychenko                 }
204f3fa412dSAndrew Melnychenko 
205f3fa412dSAndrew Melnychenko                 info->is_ipv6_ext_dst = 1;
206f3fa412dSAndrew Melnychenko             }
207f3fa412dSAndrew Melnychenko 
208f3fa412dSAndrew Melnychenko         } else if (*l4_protocol == IPPROTO_DSTOPTS) {
209f3fa412dSAndrew Melnychenko             struct ipv6_opt_t {
210f3fa412dSAndrew Melnychenko                 __u8 type;
211f3fa412dSAndrew Melnychenko                 __u8 length;
212f3fa412dSAndrew Melnychenko             } __attribute__((packed)) opt = {};
213f3fa412dSAndrew Melnychenko 
214f3fa412dSAndrew Melnychenko             size_t opt_offset = sizeof(ext_hdr);
215f3fa412dSAndrew Melnychenko 
216f3fa412dSAndrew Melnychenko             for (unsigned int j = 0; j < IP6_OPTIONS_COUNT; ++j) {
217f3fa412dSAndrew Melnychenko                 err = bpf_skb_load_bytes_relative(skb, *l4_offset + opt_offset,
218f3fa412dSAndrew Melnychenko                                         &opt, sizeof(opt), BPF_HDR_START_NET);
219f3fa412dSAndrew Melnychenko                 if (err) {
220f3fa412dSAndrew Melnychenko                     goto error;
221f3fa412dSAndrew Melnychenko                 }
222f3fa412dSAndrew Melnychenko 
223f3fa412dSAndrew Melnychenko                 if (opt.type == IPV6_TLV_HAO) {
224f3fa412dSAndrew Melnychenko                     err = bpf_skb_load_bytes_relative(skb,
225f3fa412dSAndrew Melnychenko                         *l4_offset + opt_offset
226f3fa412dSAndrew Melnychenko                         + offsetof(struct ipv6_destopt_hao, addr),
227f3fa412dSAndrew Melnychenko                         &info->in6_ext_src, sizeof(info->in6_ext_src),
228f3fa412dSAndrew Melnychenko                         BPF_HDR_START_NET);
229f3fa412dSAndrew Melnychenko                     if (err) {
230f3fa412dSAndrew Melnychenko                         goto error;
231f3fa412dSAndrew Melnychenko                     }
232f3fa412dSAndrew Melnychenko 
233f3fa412dSAndrew Melnychenko                     info->is_ipv6_ext_src = 1;
234f3fa412dSAndrew Melnychenko                     break;
235f3fa412dSAndrew Melnychenko                 }
236f3fa412dSAndrew Melnychenko 
237f3fa412dSAndrew Melnychenko                 opt_offset += (opt.type == IPV6_TLV_PAD1) ?
238f3fa412dSAndrew Melnychenko                               1 : opt.length + sizeof(opt);
239f3fa412dSAndrew Melnychenko 
240f3fa412dSAndrew Melnychenko                 if (opt_offset + 1 >= ext_hdr.hdrlen * 8) {
241f3fa412dSAndrew Melnychenko                     break;
242f3fa412dSAndrew Melnychenko                 }
243f3fa412dSAndrew Melnychenko             }
244f3fa412dSAndrew Melnychenko         } else if (*l4_protocol == IPPROTO_FRAGMENT) {
245f3fa412dSAndrew Melnychenko             info->is_fragmented = true;
246f3fa412dSAndrew Melnychenko         }
247f3fa412dSAndrew Melnychenko 
248f3fa412dSAndrew Melnychenko         *l4_protocol = ext_hdr.nexthdr;
249f3fa412dSAndrew Melnychenko         *l4_offset += (ext_hdr.hdrlen + 1) * 8;
250f3fa412dSAndrew Melnychenko 
251f3fa412dSAndrew Melnychenko         if (!ip6_extension_header_type(ext_hdr.nexthdr)) {
252f3fa412dSAndrew Melnychenko             return 0;
253f3fa412dSAndrew Melnychenko         }
254f3fa412dSAndrew Melnychenko     }
255f3fa412dSAndrew Melnychenko 
256f3fa412dSAndrew Melnychenko     return 0;
257f3fa412dSAndrew Melnychenko error:
258f3fa412dSAndrew Melnychenko     return err;
259f3fa412dSAndrew Melnychenko }
260f3fa412dSAndrew Melnychenko 
261f3fa412dSAndrew Melnychenko static __be16 parse_eth_type(struct __sk_buff *skb)
262f3fa412dSAndrew Melnychenko {
263f3fa412dSAndrew Melnychenko     unsigned int offset = 12;
264f3fa412dSAndrew Melnychenko     __be16 ret = 0;
265f3fa412dSAndrew Melnychenko     int err = 0;
266f3fa412dSAndrew Melnychenko 
267f3fa412dSAndrew Melnychenko     err = bpf_skb_load_bytes_relative(skb, offset, &ret, sizeof(ret),
268f3fa412dSAndrew Melnychenko                                 BPF_HDR_START_MAC);
269f3fa412dSAndrew Melnychenko     if (err) {
270f3fa412dSAndrew Melnychenko         return 0;
271f3fa412dSAndrew Melnychenko     }
272f3fa412dSAndrew Melnychenko 
273f3fa412dSAndrew Melnychenko     switch (bpf_ntohs(ret)) {
274f3fa412dSAndrew Melnychenko     case ETH_P_8021AD:
275f3fa412dSAndrew Melnychenko         offset += 4;
276f3fa412dSAndrew Melnychenko     case ETH_P_8021Q:
277f3fa412dSAndrew Melnychenko         offset += 4;
278f3fa412dSAndrew Melnychenko         err = bpf_skb_load_bytes_relative(skb, offset, &ret, sizeof(ret),
279f3fa412dSAndrew Melnychenko                                     BPF_HDR_START_MAC);
280f3fa412dSAndrew Melnychenko     default:
281f3fa412dSAndrew Melnychenko         break;
282f3fa412dSAndrew Melnychenko     }
283f3fa412dSAndrew Melnychenko 
284f3fa412dSAndrew Melnychenko     if (err) {
285f3fa412dSAndrew Melnychenko         return 0;
286f3fa412dSAndrew Melnychenko     }
287f3fa412dSAndrew Melnychenko 
288f3fa412dSAndrew Melnychenko     return ret;
289f3fa412dSAndrew Melnychenko }
290f3fa412dSAndrew Melnychenko 
291f3fa412dSAndrew Melnychenko static inline int parse_packet(struct __sk_buff *skb,
292f3fa412dSAndrew Melnychenko         struct packet_hash_info_t *info)
293f3fa412dSAndrew Melnychenko {
294f3fa412dSAndrew Melnychenko     int err = 0;
295f3fa412dSAndrew Melnychenko 
296f3fa412dSAndrew Melnychenko     if (!info || !skb) {
297f3fa412dSAndrew Melnychenko         return -1;
298f3fa412dSAndrew Melnychenko     }
299f3fa412dSAndrew Melnychenko 
300f3fa412dSAndrew Melnychenko     size_t l4_offset = 0;
301f3fa412dSAndrew Melnychenko     __u8 l4_protocol = 0;
302f3fa412dSAndrew Melnychenko     __u16 l3_protocol = bpf_ntohs(parse_eth_type(skb));
303f3fa412dSAndrew Melnychenko     if (l3_protocol == 0) {
304f3fa412dSAndrew Melnychenko         err = -1;
305f3fa412dSAndrew Melnychenko         goto error;
306f3fa412dSAndrew Melnychenko     }
307f3fa412dSAndrew Melnychenko 
308f3fa412dSAndrew Melnychenko     if (l3_protocol == ETH_P_IP) {
309f3fa412dSAndrew Melnychenko         info->is_ipv4 = 1;
310f3fa412dSAndrew Melnychenko 
311f3fa412dSAndrew Melnychenko         struct iphdr ip = {};
312f3fa412dSAndrew Melnychenko         err = bpf_skb_load_bytes_relative(skb, 0, &ip, sizeof(ip),
313f3fa412dSAndrew Melnychenko                                     BPF_HDR_START_NET);
314f3fa412dSAndrew Melnychenko         if (err) {
315f3fa412dSAndrew Melnychenko             goto error;
316f3fa412dSAndrew Melnychenko         }
317f3fa412dSAndrew Melnychenko 
318f3fa412dSAndrew Melnychenko         info->in_src = ip.saddr;
319f3fa412dSAndrew Melnychenko         info->in_dst = ip.daddr;
320f3fa412dSAndrew Melnychenko         info->is_fragmented = !!ip.frag_off;
321f3fa412dSAndrew Melnychenko 
322f3fa412dSAndrew Melnychenko         l4_protocol = ip.protocol;
323f3fa412dSAndrew Melnychenko         l4_offset = ip.ihl * 4;
324f3fa412dSAndrew Melnychenko     } else if (l3_protocol == ETH_P_IPV6) {
325f3fa412dSAndrew Melnychenko         info->is_ipv6 = 1;
326f3fa412dSAndrew Melnychenko 
327f3fa412dSAndrew Melnychenko         struct ipv6hdr ip6 = {};
328f3fa412dSAndrew Melnychenko         err = bpf_skb_load_bytes_relative(skb, 0, &ip6, sizeof(ip6),
329f3fa412dSAndrew Melnychenko                                     BPF_HDR_START_NET);
330f3fa412dSAndrew Melnychenko         if (err) {
331f3fa412dSAndrew Melnychenko             goto error;
332f3fa412dSAndrew Melnychenko         }
333f3fa412dSAndrew Melnychenko 
334f3fa412dSAndrew Melnychenko         info->in6_src = ip6.saddr;
335f3fa412dSAndrew Melnychenko         info->in6_dst = ip6.daddr;
336f3fa412dSAndrew Melnychenko 
337f3fa412dSAndrew Melnychenko         l4_protocol = ip6.nexthdr;
338f3fa412dSAndrew Melnychenko         l4_offset = sizeof(ip6);
339f3fa412dSAndrew Melnychenko 
340f3fa412dSAndrew Melnychenko         err = parse_ipv6_ext(skb, info, &l4_protocol, &l4_offset);
341f3fa412dSAndrew Melnychenko         if (err) {
342f3fa412dSAndrew Melnychenko             goto error;
343f3fa412dSAndrew Melnychenko         }
344f3fa412dSAndrew Melnychenko     }
345f3fa412dSAndrew Melnychenko 
346f3fa412dSAndrew Melnychenko     if (l4_protocol != 0 && !info->is_fragmented) {
347f3fa412dSAndrew Melnychenko         if (l4_protocol == IPPROTO_TCP) {
348f3fa412dSAndrew Melnychenko             info->is_tcp = 1;
349f3fa412dSAndrew Melnychenko 
350f3fa412dSAndrew Melnychenko             struct tcphdr tcp = {};
351f3fa412dSAndrew Melnychenko             err = bpf_skb_load_bytes_relative(skb, l4_offset, &tcp, sizeof(tcp),
352f3fa412dSAndrew Melnychenko                                         BPF_HDR_START_NET);
353f3fa412dSAndrew Melnychenko             if (err) {
354f3fa412dSAndrew Melnychenko                 goto error;
355f3fa412dSAndrew Melnychenko             }
356f3fa412dSAndrew Melnychenko 
357f3fa412dSAndrew Melnychenko             info->src_port = tcp.source;
358f3fa412dSAndrew Melnychenko             info->dst_port = tcp.dest;
359f3fa412dSAndrew Melnychenko         } else if (l4_protocol == IPPROTO_UDP) { /* TODO: add udplite? */
360f3fa412dSAndrew Melnychenko             info->is_udp = 1;
361f3fa412dSAndrew Melnychenko 
362f3fa412dSAndrew Melnychenko             struct udphdr udp = {};
363f3fa412dSAndrew Melnychenko             err = bpf_skb_load_bytes_relative(skb, l4_offset, &udp, sizeof(udp),
364f3fa412dSAndrew Melnychenko                                         BPF_HDR_START_NET);
365f3fa412dSAndrew Melnychenko             if (err) {
366f3fa412dSAndrew Melnychenko                 goto error;
367f3fa412dSAndrew Melnychenko             }
368f3fa412dSAndrew Melnychenko 
369f3fa412dSAndrew Melnychenko             info->src_port = udp.source;
370f3fa412dSAndrew Melnychenko             info->dst_port = udp.dest;
371f3fa412dSAndrew Melnychenko         }
372f3fa412dSAndrew Melnychenko     }
373f3fa412dSAndrew Melnychenko 
374f3fa412dSAndrew Melnychenko     return 0;
375f3fa412dSAndrew Melnychenko 
376f3fa412dSAndrew Melnychenko error:
377f3fa412dSAndrew Melnychenko     return err;
378f3fa412dSAndrew Melnychenko }
379f3fa412dSAndrew Melnychenko 
380f3fa412dSAndrew Melnychenko static inline __u32 calculate_rss_hash(struct __sk_buff *skb,
381f3fa412dSAndrew Melnychenko         struct rss_config_t *config, struct toeplitz_key_data_t *toe)
382f3fa412dSAndrew Melnychenko {
383f3fa412dSAndrew Melnychenko     __u8 rss_input[HASH_CALCULATION_BUFFER_SIZE] = {};
384f3fa412dSAndrew Melnychenko     size_t bytes_written = 0;
385f3fa412dSAndrew Melnychenko     __u32 result = 0;
386f3fa412dSAndrew Melnychenko     int err = 0;
387f3fa412dSAndrew Melnychenko     struct packet_hash_info_t packet_info = {};
388f3fa412dSAndrew Melnychenko 
389f3fa412dSAndrew Melnychenko     err = parse_packet(skb, &packet_info);
390f3fa412dSAndrew Melnychenko     if (err) {
391f3fa412dSAndrew Melnychenko         return 0;
392f3fa412dSAndrew Melnychenko     }
393f3fa412dSAndrew Melnychenko 
394f3fa412dSAndrew Melnychenko     if (packet_info.is_ipv4) {
395f3fa412dSAndrew Melnychenko         if (packet_info.is_tcp &&
396f3fa412dSAndrew Melnychenko             config->hash_types & VIRTIO_NET_RSS_HASH_TYPE_TCPv4) {
397f3fa412dSAndrew Melnychenko 
398f3fa412dSAndrew Melnychenko             net_rx_rss_add_chunk(rss_input, &bytes_written,
399f3fa412dSAndrew Melnychenko                                  &packet_info.in_src,
400f3fa412dSAndrew Melnychenko                                  sizeof(packet_info.in_src));
401f3fa412dSAndrew Melnychenko             net_rx_rss_add_chunk(rss_input, &bytes_written,
402f3fa412dSAndrew Melnychenko                                  &packet_info.in_dst,
403f3fa412dSAndrew Melnychenko                                  sizeof(packet_info.in_dst));
404f3fa412dSAndrew Melnychenko             net_rx_rss_add_chunk(rss_input, &bytes_written,
405f3fa412dSAndrew Melnychenko                                  &packet_info.src_port,
406f3fa412dSAndrew Melnychenko                                  sizeof(packet_info.src_port));
407f3fa412dSAndrew Melnychenko             net_rx_rss_add_chunk(rss_input, &bytes_written,
408f3fa412dSAndrew Melnychenko                                  &packet_info.dst_port,
409f3fa412dSAndrew Melnychenko                                  sizeof(packet_info.dst_port));
410f3fa412dSAndrew Melnychenko         } else if (packet_info.is_udp &&
411f3fa412dSAndrew Melnychenko                    config->hash_types & VIRTIO_NET_RSS_HASH_TYPE_UDPv4) {
412f3fa412dSAndrew Melnychenko 
413f3fa412dSAndrew Melnychenko             net_rx_rss_add_chunk(rss_input, &bytes_written,
414f3fa412dSAndrew Melnychenko                                  &packet_info.in_src,
415f3fa412dSAndrew Melnychenko                                  sizeof(packet_info.in_src));
416f3fa412dSAndrew Melnychenko             net_rx_rss_add_chunk(rss_input, &bytes_written,
417f3fa412dSAndrew Melnychenko                                  &packet_info.in_dst,
418f3fa412dSAndrew Melnychenko                                  sizeof(packet_info.in_dst));
419f3fa412dSAndrew Melnychenko             net_rx_rss_add_chunk(rss_input, &bytes_written,
420f3fa412dSAndrew Melnychenko                                  &packet_info.src_port,
421f3fa412dSAndrew Melnychenko                                  sizeof(packet_info.src_port));
422f3fa412dSAndrew Melnychenko             net_rx_rss_add_chunk(rss_input, &bytes_written,
423f3fa412dSAndrew Melnychenko                                  &packet_info.dst_port,
424f3fa412dSAndrew Melnychenko                                  sizeof(packet_info.dst_port));
425f3fa412dSAndrew Melnychenko         } else if (config->hash_types & VIRTIO_NET_RSS_HASH_TYPE_IPv4) {
426f3fa412dSAndrew Melnychenko             net_rx_rss_add_chunk(rss_input, &bytes_written,
427f3fa412dSAndrew Melnychenko                                  &packet_info.in_src,
428f3fa412dSAndrew Melnychenko                                  sizeof(packet_info.in_src));
429f3fa412dSAndrew Melnychenko             net_rx_rss_add_chunk(rss_input, &bytes_written,
430f3fa412dSAndrew Melnychenko                                  &packet_info.in_dst,
431f3fa412dSAndrew Melnychenko                                  sizeof(packet_info.in_dst));
432f3fa412dSAndrew Melnychenko         }
433f3fa412dSAndrew Melnychenko     } else if (packet_info.is_ipv6) {
434f3fa412dSAndrew Melnychenko         if (packet_info.is_tcp &&
435f3fa412dSAndrew Melnychenko             config->hash_types & VIRTIO_NET_RSS_HASH_TYPE_TCPv6) {
436f3fa412dSAndrew Melnychenko 
437f3fa412dSAndrew Melnychenko             if (packet_info.is_ipv6_ext_src &&
438f3fa412dSAndrew Melnychenko                 config->hash_types & VIRTIO_NET_RSS_HASH_TYPE_TCP_EX) {
439f3fa412dSAndrew Melnychenko 
440f3fa412dSAndrew Melnychenko                 net_rx_rss_add_chunk(rss_input, &bytes_written,
441f3fa412dSAndrew Melnychenko                                      &packet_info.in6_ext_src,
442f3fa412dSAndrew Melnychenko                                      sizeof(packet_info.in6_ext_src));
443f3fa412dSAndrew Melnychenko             } else {
444f3fa412dSAndrew Melnychenko                 net_rx_rss_add_chunk(rss_input, &bytes_written,
445f3fa412dSAndrew Melnychenko                                      &packet_info.in6_src,
446f3fa412dSAndrew Melnychenko                                      sizeof(packet_info.in6_src));
447f3fa412dSAndrew Melnychenko             }
448f3fa412dSAndrew Melnychenko             if (packet_info.is_ipv6_ext_dst &&
449f3fa412dSAndrew Melnychenko                 config->hash_types & VIRTIO_NET_RSS_HASH_TYPE_TCP_EX) {
450f3fa412dSAndrew Melnychenko 
451f3fa412dSAndrew Melnychenko                 net_rx_rss_add_chunk(rss_input, &bytes_written,
452f3fa412dSAndrew Melnychenko                                      &packet_info.in6_ext_dst,
453f3fa412dSAndrew Melnychenko                                      sizeof(packet_info.in6_ext_dst));
454f3fa412dSAndrew Melnychenko             } else {
455f3fa412dSAndrew Melnychenko                 net_rx_rss_add_chunk(rss_input, &bytes_written,
456f3fa412dSAndrew Melnychenko                                      &packet_info.in6_dst,
457f3fa412dSAndrew Melnychenko                                      sizeof(packet_info.in6_dst));
458f3fa412dSAndrew Melnychenko             }
459f3fa412dSAndrew Melnychenko             net_rx_rss_add_chunk(rss_input, &bytes_written,
460f3fa412dSAndrew Melnychenko                                  &packet_info.src_port,
461f3fa412dSAndrew Melnychenko                                  sizeof(packet_info.src_port));
462f3fa412dSAndrew Melnychenko             net_rx_rss_add_chunk(rss_input, &bytes_written,
463f3fa412dSAndrew Melnychenko                                  &packet_info.dst_port,
464f3fa412dSAndrew Melnychenko                                  sizeof(packet_info.dst_port));
465f3fa412dSAndrew Melnychenko         } else if (packet_info.is_udp &&
466f3fa412dSAndrew Melnychenko                    config->hash_types & VIRTIO_NET_RSS_HASH_TYPE_UDPv6) {
467f3fa412dSAndrew Melnychenko 
468f3fa412dSAndrew Melnychenko             if (packet_info.is_ipv6_ext_src &&
469f3fa412dSAndrew Melnychenko                config->hash_types & VIRTIO_NET_RSS_HASH_TYPE_UDP_EX) {
470f3fa412dSAndrew Melnychenko 
471f3fa412dSAndrew Melnychenko                 net_rx_rss_add_chunk(rss_input, &bytes_written,
472f3fa412dSAndrew Melnychenko                                      &packet_info.in6_ext_src,
473f3fa412dSAndrew Melnychenko                                      sizeof(packet_info.in6_ext_src));
474f3fa412dSAndrew Melnychenko             } else {
475f3fa412dSAndrew Melnychenko                 net_rx_rss_add_chunk(rss_input, &bytes_written,
476f3fa412dSAndrew Melnychenko                                      &packet_info.in6_src,
477f3fa412dSAndrew Melnychenko                                      sizeof(packet_info.in6_src));
478f3fa412dSAndrew Melnychenko             }
479f3fa412dSAndrew Melnychenko             if (packet_info.is_ipv6_ext_dst &&
480f3fa412dSAndrew Melnychenko                config->hash_types & VIRTIO_NET_RSS_HASH_TYPE_UDP_EX) {
481f3fa412dSAndrew Melnychenko 
482f3fa412dSAndrew Melnychenko                 net_rx_rss_add_chunk(rss_input, &bytes_written,
483f3fa412dSAndrew Melnychenko                                      &packet_info.in6_ext_dst,
484f3fa412dSAndrew Melnychenko                                      sizeof(packet_info.in6_ext_dst));
485f3fa412dSAndrew Melnychenko             } else {
486f3fa412dSAndrew Melnychenko                 net_rx_rss_add_chunk(rss_input, &bytes_written,
487f3fa412dSAndrew Melnychenko                                      &packet_info.in6_dst,
488f3fa412dSAndrew Melnychenko                                      sizeof(packet_info.in6_dst));
489f3fa412dSAndrew Melnychenko             }
490f3fa412dSAndrew Melnychenko 
491f3fa412dSAndrew Melnychenko             net_rx_rss_add_chunk(rss_input, &bytes_written,
492f3fa412dSAndrew Melnychenko                                  &packet_info.src_port,
493f3fa412dSAndrew Melnychenko                                  sizeof(packet_info.src_port));
494f3fa412dSAndrew Melnychenko             net_rx_rss_add_chunk(rss_input, &bytes_written,
495f3fa412dSAndrew Melnychenko                                  &packet_info.dst_port,
496f3fa412dSAndrew Melnychenko                                  sizeof(packet_info.dst_port));
497f3fa412dSAndrew Melnychenko 
498f3fa412dSAndrew Melnychenko         } else if (config->hash_types & VIRTIO_NET_RSS_HASH_TYPE_IPv6) {
499f3fa412dSAndrew Melnychenko             if (packet_info.is_ipv6_ext_src &&
500f3fa412dSAndrew Melnychenko                config->hash_types & VIRTIO_NET_RSS_HASH_TYPE_IP_EX) {
501f3fa412dSAndrew Melnychenko 
502f3fa412dSAndrew Melnychenko                 net_rx_rss_add_chunk(rss_input, &bytes_written,
503f3fa412dSAndrew Melnychenko                                      &packet_info.in6_ext_src,
504f3fa412dSAndrew Melnychenko                                      sizeof(packet_info.in6_ext_src));
505f3fa412dSAndrew Melnychenko             } else {
506f3fa412dSAndrew Melnychenko                 net_rx_rss_add_chunk(rss_input, &bytes_written,
507f3fa412dSAndrew Melnychenko                                      &packet_info.in6_src,
508f3fa412dSAndrew Melnychenko                                      sizeof(packet_info.in6_src));
509f3fa412dSAndrew Melnychenko             }
510f3fa412dSAndrew Melnychenko             if (packet_info.is_ipv6_ext_dst &&
511f3fa412dSAndrew Melnychenko                 config->hash_types & VIRTIO_NET_RSS_HASH_TYPE_IP_EX) {
512f3fa412dSAndrew Melnychenko 
513f3fa412dSAndrew Melnychenko                 net_rx_rss_add_chunk(rss_input, &bytes_written,
514f3fa412dSAndrew Melnychenko                                      &packet_info.in6_ext_dst,
515f3fa412dSAndrew Melnychenko                                      sizeof(packet_info.in6_ext_dst));
516f3fa412dSAndrew Melnychenko             } else {
517f3fa412dSAndrew Melnychenko                 net_rx_rss_add_chunk(rss_input, &bytes_written,
518f3fa412dSAndrew Melnychenko                                      &packet_info.in6_dst,
519f3fa412dSAndrew Melnychenko                                      sizeof(packet_info.in6_dst));
520f3fa412dSAndrew Melnychenko             }
521f3fa412dSAndrew Melnychenko         }
522f3fa412dSAndrew Melnychenko     }
523f3fa412dSAndrew Melnychenko 
524f3fa412dSAndrew Melnychenko     if (bytes_written) {
525f3fa412dSAndrew Melnychenko         net_toeplitz_add(&result, rss_input, bytes_written, toe);
526f3fa412dSAndrew Melnychenko     }
527f3fa412dSAndrew Melnychenko 
528f3fa412dSAndrew Melnychenko     return result;
529f3fa412dSAndrew Melnychenko }
530f3fa412dSAndrew Melnychenko 
531f3fa412dSAndrew Melnychenko SEC("tun_rss_steering")
532f3fa412dSAndrew Melnychenko int tun_rss_steering_prog(struct __sk_buff *skb)
533f3fa412dSAndrew Melnychenko {
534f3fa412dSAndrew Melnychenko 
535f3fa412dSAndrew Melnychenko     struct rss_config_t *config;
536f3fa412dSAndrew Melnychenko     struct toeplitz_key_data_t *toe;
537f3fa412dSAndrew Melnychenko 
538f3fa412dSAndrew Melnychenko     __u32 key = 0;
539f3fa412dSAndrew Melnychenko     __u32 hash = 0;
540f3fa412dSAndrew Melnychenko 
541f3fa412dSAndrew Melnychenko     config = bpf_map_lookup_elem(&tap_rss_map_configurations, &key);
542f3fa412dSAndrew Melnychenko     toe = bpf_map_lookup_elem(&tap_rss_map_toeplitz_key, &key);
543f3fa412dSAndrew Melnychenko 
544f3fa412dSAndrew Melnychenko     if (config && toe) {
545f3fa412dSAndrew Melnychenko         if (!config->redirect) {
546f3fa412dSAndrew Melnychenko             return config->default_queue;
547f3fa412dSAndrew Melnychenko         }
548f3fa412dSAndrew Melnychenko 
549f3fa412dSAndrew Melnychenko         hash = calculate_rss_hash(skb, config, toe);
550f3fa412dSAndrew Melnychenko         if (hash) {
551f3fa412dSAndrew Melnychenko             __u32 table_idx = hash % config->indirections_len;
552f3fa412dSAndrew Melnychenko             __u16 *queue = 0;
553f3fa412dSAndrew Melnychenko 
554f3fa412dSAndrew Melnychenko             queue = bpf_map_lookup_elem(&tap_rss_map_indirection_table,
555f3fa412dSAndrew Melnychenko                                         &table_idx);
556f3fa412dSAndrew Melnychenko 
557f3fa412dSAndrew Melnychenko             if (queue) {
558f3fa412dSAndrew Melnychenko                 return *queue;
559f3fa412dSAndrew Melnychenko             }
560f3fa412dSAndrew Melnychenko         }
561f3fa412dSAndrew Melnychenko 
562f3fa412dSAndrew Melnychenko         return config->default_queue;
563f3fa412dSAndrew Melnychenko     }
564f3fa412dSAndrew Melnychenko 
565f3fa412dSAndrew Melnychenko     return -1;
566f3fa412dSAndrew Melnychenko }
567f3fa412dSAndrew Melnychenko 
568f3fa412dSAndrew Melnychenko char _license[] SEC("license") = "GPL v2";
569