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