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 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 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 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 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 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 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 383*72fa42cfSAkihiko Odaki static inline bool calculate_rss_hash(struct __sk_buff *skb, 384*72fa42cfSAkihiko Odaki struct rss_config_t *config, 385*72fa42cfSAkihiko Odaki struct toeplitz_key_data_t *toe, 386*72fa42cfSAkihiko 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) { 395*72fa42cfSAkihiko 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 528*72fa42cfSAkihiko Odaki if (!bytes_written) { 529*72fa42cfSAkihiko Odaki return false; 530f3fa412dSAndrew Melnychenko } 531f3fa412dSAndrew Melnychenko 532*72fa42cfSAkihiko Odaki net_toeplitz_add(result, rss_input, bytes_written, toe); 533*72fa42cfSAkihiko Odaki 534*72fa42cfSAkihiko Odaki return true; 535f3fa412dSAndrew Melnychenko } 536f3fa412dSAndrew Melnychenko 5370cc14182SAndrew Melnychenko SEC("socket") 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 550f3fa412dSAndrew Melnychenko if (config && toe) { 551f3fa412dSAndrew Melnychenko if (!config->redirect) { 552f3fa412dSAndrew Melnychenko return config->default_queue; 553f3fa412dSAndrew Melnychenko } 554f3fa412dSAndrew Melnychenko 555*72fa42cfSAkihiko Odaki if (calculate_rss_hash(skb, config, toe, &hash)) { 556f3fa412dSAndrew Melnychenko __u32 table_idx = hash % config->indirections_len; 557f3fa412dSAndrew Melnychenko __u16 *queue = 0; 558f3fa412dSAndrew Melnychenko 559f3fa412dSAndrew Melnychenko queue = bpf_map_lookup_elem(&tap_rss_map_indirection_table, 560f3fa412dSAndrew Melnychenko &table_idx); 561f3fa412dSAndrew Melnychenko 562f3fa412dSAndrew Melnychenko if (queue) { 563f3fa412dSAndrew Melnychenko return *queue; 564f3fa412dSAndrew Melnychenko } 565f3fa412dSAndrew Melnychenko } 566f3fa412dSAndrew Melnychenko 567f3fa412dSAndrew Melnychenko return config->default_queue; 568f3fa412dSAndrew Melnychenko } 569f3fa412dSAndrew Melnychenko 570f3fa412dSAndrew Melnychenko return -1; 571f3fa412dSAndrew Melnychenko } 572f3fa412dSAndrew Melnychenko 573f3fa412dSAndrew Melnychenko char _license[] SEC("license") = "GPL v2"; 574