xref: /qemu/tools/ebpf/rss.bpf.c (revision 6e47f7cfcd78ed8e6f192cb0a4c61f209d0c2aaf)
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