14b4f2229SPuranjay Mohan // SPDX-License-Identifier: GPL-2.0 24b4f2229SPuranjay Mohan /* Copyright (c) 2026 Meta Platforms, Inc. and affiliates. */ 34b4f2229SPuranjay Mohan 44b4f2229SPuranjay Mohan #include <stddef.h> 54b4f2229SPuranjay Mohan #include <stdbool.h> 64b4f2229SPuranjay Mohan #include <linux/bpf.h> 74b4f2229SPuranjay Mohan #include <linux/if_ether.h> 84b4f2229SPuranjay Mohan #include <linux/ip.h> 94b4f2229SPuranjay Mohan #include <linux/ipv6.h> 104b4f2229SPuranjay Mohan #include <linux/in.h> 114b4f2229SPuranjay Mohan #include <linux/tcp.h> 124b4f2229SPuranjay Mohan #include <linux/udp.h> 134b4f2229SPuranjay Mohan #include <bpf/bpf_helpers.h> 144b4f2229SPuranjay Mohan #include <bpf/bpf_endian.h> 154b4f2229SPuranjay Mohan #include "bpf_compiler.h" 164b4f2229SPuranjay Mohan #include "xdp_lb_bench_common.h" 174b4f2229SPuranjay Mohan #include "bench_bpf_timing.bpf.h" 184b4f2229SPuranjay Mohan 194b4f2229SPuranjay Mohan #ifndef IPPROTO_FRAGMENT 204b4f2229SPuranjay Mohan #define IPPROTO_FRAGMENT 44 214b4f2229SPuranjay Mohan #endif 224b4f2229SPuranjay Mohan 234b4f2229SPuranjay Mohan /* jhash helpers */ 244b4f2229SPuranjay Mohan 254b4f2229SPuranjay Mohan static inline __u32 rol32(__u32 word, unsigned int shift) 264b4f2229SPuranjay Mohan { 274b4f2229SPuranjay Mohan return (word << shift) | (word >> ((-shift) & 31)); 284b4f2229SPuranjay Mohan } 294b4f2229SPuranjay Mohan 304b4f2229SPuranjay Mohan #define __jhash_mix(a, b, c) \ 314b4f2229SPuranjay Mohan { \ 324b4f2229SPuranjay Mohan a -= c; a ^= rol32(c, 4); c += b; \ 334b4f2229SPuranjay Mohan b -= a; b ^= rol32(a, 6); a += c; \ 344b4f2229SPuranjay Mohan c -= b; c ^= rol32(b, 8); b += a; \ 354b4f2229SPuranjay Mohan a -= c; a ^= rol32(c, 16); c += b; \ 364b4f2229SPuranjay Mohan b -= a; b ^= rol32(a, 19); a += c; \ 374b4f2229SPuranjay Mohan c -= b; c ^= rol32(b, 4); b += a; \ 384b4f2229SPuranjay Mohan } 394b4f2229SPuranjay Mohan 404b4f2229SPuranjay Mohan #define __jhash_final(a, b, c) \ 414b4f2229SPuranjay Mohan { \ 424b4f2229SPuranjay Mohan c ^= b; c -= rol32(b, 14); \ 434b4f2229SPuranjay Mohan a ^= c; a -= rol32(c, 11); \ 444b4f2229SPuranjay Mohan b ^= a; b -= rol32(a, 25); \ 454b4f2229SPuranjay Mohan c ^= b; c -= rol32(b, 16); \ 464b4f2229SPuranjay Mohan a ^= c; a -= rol32(c, 4); \ 474b4f2229SPuranjay Mohan b ^= a; b -= rol32(a, 14); \ 484b4f2229SPuranjay Mohan c ^= b; c -= rol32(b, 24); \ 494b4f2229SPuranjay Mohan } 504b4f2229SPuranjay Mohan 514b4f2229SPuranjay Mohan #define JHASH_INITVAL 0xdeadbeef 524b4f2229SPuranjay Mohan 534b4f2229SPuranjay Mohan static inline __u32 __jhash_nwords(__u32 a, __u32 b, __u32 c, __u32 initval) 544b4f2229SPuranjay Mohan { 554b4f2229SPuranjay Mohan a += initval; 564b4f2229SPuranjay Mohan b += initval; 574b4f2229SPuranjay Mohan c += initval; 584b4f2229SPuranjay Mohan __jhash_final(a, b, c); 594b4f2229SPuranjay Mohan return c; 604b4f2229SPuranjay Mohan } 614b4f2229SPuranjay Mohan 624b4f2229SPuranjay Mohan static inline __u32 jhash_2words(__u32 a, __u32 b, __u32 initval) 634b4f2229SPuranjay Mohan { 644b4f2229SPuranjay Mohan return __jhash_nwords(a, b, 0, initval + JHASH_INITVAL + (2 << 2)); 654b4f2229SPuranjay Mohan } 664b4f2229SPuranjay Mohan 674b4f2229SPuranjay Mohan static inline __u32 jhash2_4words(const __u32 *k, __u32 initval) 684b4f2229SPuranjay Mohan { 694b4f2229SPuranjay Mohan __u32 a, b, c; 704b4f2229SPuranjay Mohan 714b4f2229SPuranjay Mohan a = b = c = JHASH_INITVAL + (4 << 2) + initval; 724b4f2229SPuranjay Mohan 734b4f2229SPuranjay Mohan a += k[0]; b += k[1]; c += k[2]; 744b4f2229SPuranjay Mohan __jhash_mix(a, b, c); 754b4f2229SPuranjay Mohan 764b4f2229SPuranjay Mohan a += k[3]; 774b4f2229SPuranjay Mohan __jhash_final(a, b, c); 784b4f2229SPuranjay Mohan 794b4f2229SPuranjay Mohan return c; 804b4f2229SPuranjay Mohan } 814b4f2229SPuranjay Mohan 824b4f2229SPuranjay Mohan static __always_inline void ipv4_csum(struct iphdr *iph) 834b4f2229SPuranjay Mohan { 844b4f2229SPuranjay Mohan __u16 *next_iph = (__u16 *)iph; 854b4f2229SPuranjay Mohan __u32 csum = 0; 864b4f2229SPuranjay Mohan int i; 874b4f2229SPuranjay Mohan 884b4f2229SPuranjay Mohan __pragma_loop_unroll_full 894b4f2229SPuranjay Mohan for (i = 0; i < (int)(sizeof(*iph) >> 1); i++) 904b4f2229SPuranjay Mohan csum += *next_iph++; 914b4f2229SPuranjay Mohan 924b4f2229SPuranjay Mohan csum = (csum & 0xffff) + (csum >> 16); 934b4f2229SPuranjay Mohan csum = (csum & 0xffff) + (csum >> 16); 944b4f2229SPuranjay Mohan iph->check = ~csum; 954b4f2229SPuranjay Mohan } 964b4f2229SPuranjay Mohan 974b4f2229SPuranjay Mohan struct { 984b4f2229SPuranjay Mohan __uint(type, BPF_MAP_TYPE_HASH); 994b4f2229SPuranjay Mohan __uint(max_entries, 64); 1004b4f2229SPuranjay Mohan __type(key, struct vip_definition); 1014b4f2229SPuranjay Mohan __type(value, struct vip_meta); 1024b4f2229SPuranjay Mohan } vip_map SEC(".maps"); 1034b4f2229SPuranjay Mohan 1044b4f2229SPuranjay Mohan struct lru_inner_map { 1054b4f2229SPuranjay Mohan __uint(type, BPF_MAP_TYPE_LRU_HASH); 1064b4f2229SPuranjay Mohan __type(key, struct flow_key); 1074b4f2229SPuranjay Mohan __type(value, struct real_pos_lru); 1084b4f2229SPuranjay Mohan __uint(max_entries, DEFAULT_LRU_SIZE); 1094b4f2229SPuranjay Mohan } lru_inner SEC(".maps"); 1104b4f2229SPuranjay Mohan 1114b4f2229SPuranjay Mohan struct { 1124b4f2229SPuranjay Mohan __uint(type, BPF_MAP_TYPE_ARRAY_OF_MAPS); 1134b4f2229SPuranjay Mohan __type(key, __u32); 1144b4f2229SPuranjay Mohan __type(value, __u32); 1154b4f2229SPuranjay Mohan __uint(max_entries, BENCH_NR_CPUS); 1164b4f2229SPuranjay Mohan __array(values, struct lru_inner_map); 1174b4f2229SPuranjay Mohan } lru_mapping SEC(".maps"); 1184b4f2229SPuranjay Mohan 1194b4f2229SPuranjay Mohan struct { 1204b4f2229SPuranjay Mohan __uint(type, BPF_MAP_TYPE_ARRAY); 1214b4f2229SPuranjay Mohan __uint(max_entries, CH_RINGS_SIZE); 1224b4f2229SPuranjay Mohan __type(key, __u32); 1234b4f2229SPuranjay Mohan __type(value, __u32); 1244b4f2229SPuranjay Mohan } ch_rings SEC(".maps"); 1254b4f2229SPuranjay Mohan 1264b4f2229SPuranjay Mohan struct { 1274b4f2229SPuranjay Mohan __uint(type, BPF_MAP_TYPE_ARRAY); 1284b4f2229SPuranjay Mohan __uint(max_entries, MAX_REALS); 1294b4f2229SPuranjay Mohan __type(key, __u32); 1304b4f2229SPuranjay Mohan __type(value, struct real_definition); 1314b4f2229SPuranjay Mohan } reals SEC(".maps"); 1324b4f2229SPuranjay Mohan 1334b4f2229SPuranjay Mohan struct { 1344b4f2229SPuranjay Mohan __uint(type, BPF_MAP_TYPE_PERCPU_ARRAY); 1354b4f2229SPuranjay Mohan __uint(max_entries, STATS_SIZE); 1364b4f2229SPuranjay Mohan __type(key, __u32); 1374b4f2229SPuranjay Mohan __type(value, struct lb_stats); 1384b4f2229SPuranjay Mohan } stats SEC(".maps"); 1394b4f2229SPuranjay Mohan 1404b4f2229SPuranjay Mohan struct { 1414b4f2229SPuranjay Mohan __uint(type, BPF_MAP_TYPE_PERCPU_ARRAY); 1424b4f2229SPuranjay Mohan __uint(max_entries, MAX_REALS); 1434b4f2229SPuranjay Mohan __type(key, __u32); 1444b4f2229SPuranjay Mohan __type(value, struct lb_stats); 1454b4f2229SPuranjay Mohan } reals_stats SEC(".maps"); 1464b4f2229SPuranjay Mohan 1474b4f2229SPuranjay Mohan struct { 1484b4f2229SPuranjay Mohan __uint(type, BPF_MAP_TYPE_ARRAY); 1494b4f2229SPuranjay Mohan __uint(max_entries, 1); 1504b4f2229SPuranjay Mohan __type(key, __u32); 1514b4f2229SPuranjay Mohan __type(value, struct ctl_value); 1524b4f2229SPuranjay Mohan } ctl_array SEC(".maps"); 1534b4f2229SPuranjay Mohan 1544b4f2229SPuranjay Mohan struct { 1554b4f2229SPuranjay Mohan __uint(type, BPF_MAP_TYPE_ARRAY); 1564b4f2229SPuranjay Mohan __uint(max_entries, 1); 1574b4f2229SPuranjay Mohan __type(key, __u32); 1584b4f2229SPuranjay Mohan __type(value, struct vip_definition); 1594b4f2229SPuranjay Mohan } vip_miss_stats SEC(".maps"); 1604b4f2229SPuranjay Mohan 1614b4f2229SPuranjay Mohan struct { 1624b4f2229SPuranjay Mohan __uint(type, BPF_MAP_TYPE_PERCPU_ARRAY); 1634b4f2229SPuranjay Mohan __uint(max_entries, MAX_REALS); 1644b4f2229SPuranjay Mohan __type(key, __u32); 1654b4f2229SPuranjay Mohan __type(value, __u32); 1664b4f2229SPuranjay Mohan } lru_miss_stats SEC(".maps"); 1674b4f2229SPuranjay Mohan 1684b4f2229SPuranjay Mohan volatile __u32 flow_mask; 1694b4f2229SPuranjay Mohan volatile __u32 cold_lru; 1704b4f2229SPuranjay Mohan __u32 batch_gen; 1714b4f2229SPuranjay Mohan 1724b4f2229SPuranjay Mohan /* 1734b4f2229SPuranjay Mohan * old_eth MUST be read BEFORE writing the outer header because 1744b4f2229SPuranjay Mohan * bpf_xdp_adjust_head makes them overlap. 1754b4f2229SPuranjay Mohan */ 1764b4f2229SPuranjay Mohan static __always_inline int encap_v4(struct xdp_md *xdp, __be32 saddr, __be32 daddr, 1774b4f2229SPuranjay Mohan __u16 payload_len, const __u8 *dst_mac) 1784b4f2229SPuranjay Mohan { 1794b4f2229SPuranjay Mohan struct ethhdr *new_eth, *old_eth; 1804b4f2229SPuranjay Mohan void *data, *data_end; 1814b4f2229SPuranjay Mohan struct iphdr *iph; 1824b4f2229SPuranjay Mohan 1834b4f2229SPuranjay Mohan if (bpf_xdp_adjust_head(xdp, -(int)sizeof(struct iphdr))) 1844b4f2229SPuranjay Mohan return -1; 1854b4f2229SPuranjay Mohan 1864b4f2229SPuranjay Mohan data = (void *)(long)xdp->data; 1874b4f2229SPuranjay Mohan data_end = (void *)(long)xdp->data_end; 1884b4f2229SPuranjay Mohan 1894b4f2229SPuranjay Mohan new_eth = data; 1904b4f2229SPuranjay Mohan iph = data + sizeof(struct ethhdr); 1914b4f2229SPuranjay Mohan old_eth = data + sizeof(struct iphdr); 1924b4f2229SPuranjay Mohan 1934b4f2229SPuranjay Mohan if (new_eth + 1 > data_end || old_eth + 1 > data_end || iph + 1 > data_end) 1944b4f2229SPuranjay Mohan return -1; 1954b4f2229SPuranjay Mohan 1964b4f2229SPuranjay Mohan __builtin_memcpy(new_eth->h_source, old_eth->h_dest, sizeof(new_eth->h_source)); 1974b4f2229SPuranjay Mohan __builtin_memcpy(new_eth->h_dest, dst_mac, sizeof(new_eth->h_dest)); 1984b4f2229SPuranjay Mohan new_eth->h_proto = bpf_htons(ETH_P_IP); 1994b4f2229SPuranjay Mohan 2004b4f2229SPuranjay Mohan __builtin_memset(iph, 0, sizeof(*iph)); 2014b4f2229SPuranjay Mohan iph->version = 4; 2024b4f2229SPuranjay Mohan iph->ihl = sizeof(*iph) >> 2; 2034b4f2229SPuranjay Mohan iph->protocol = IPPROTO_IPIP; 2044b4f2229SPuranjay Mohan iph->tot_len = bpf_htons(payload_len + sizeof(*iph)); 2054b4f2229SPuranjay Mohan iph->ttl = 64; 2064b4f2229SPuranjay Mohan iph->saddr = saddr; 2074b4f2229SPuranjay Mohan iph->daddr = daddr; 2084b4f2229SPuranjay Mohan ipv4_csum(iph); 2094b4f2229SPuranjay Mohan 2104b4f2229SPuranjay Mohan return 0; 2114b4f2229SPuranjay Mohan } 2124b4f2229SPuranjay Mohan 2134b4f2229SPuranjay Mohan static __always_inline int encap_v6(struct xdp_md *xdp, const __be32 saddr[4], 2144b4f2229SPuranjay Mohan const __be32 daddr[4], __u8 nexthdr, __u16 payload_len, 2154b4f2229SPuranjay Mohan const __u8 *dst_mac) 2164b4f2229SPuranjay Mohan { 2174b4f2229SPuranjay Mohan struct ethhdr *new_eth, *old_eth; 2184b4f2229SPuranjay Mohan void *data, *data_end; 2194b4f2229SPuranjay Mohan struct ipv6hdr *ip6h; 2204b4f2229SPuranjay Mohan 2214b4f2229SPuranjay Mohan if (bpf_xdp_adjust_head(xdp, -(int)sizeof(struct ipv6hdr))) 2224b4f2229SPuranjay Mohan return -1; 2234b4f2229SPuranjay Mohan 2244b4f2229SPuranjay Mohan data = (void *)(long)xdp->data; 2254b4f2229SPuranjay Mohan data_end = (void *)(long)xdp->data_end; 2264b4f2229SPuranjay Mohan 2274b4f2229SPuranjay Mohan new_eth = data; 2284b4f2229SPuranjay Mohan ip6h = data + sizeof(struct ethhdr); 2294b4f2229SPuranjay Mohan old_eth = data + sizeof(struct ipv6hdr); 2304b4f2229SPuranjay Mohan 2314b4f2229SPuranjay Mohan if (new_eth + 1 > data_end || old_eth + 1 > data_end || ip6h + 1 > data_end) 2324b4f2229SPuranjay Mohan return -1; 2334b4f2229SPuranjay Mohan 2344b4f2229SPuranjay Mohan __builtin_memcpy(new_eth->h_source, old_eth->h_dest, sizeof(new_eth->h_source)); 2354b4f2229SPuranjay Mohan __builtin_memcpy(new_eth->h_dest, dst_mac, sizeof(new_eth->h_dest)); 2364b4f2229SPuranjay Mohan new_eth->h_proto = bpf_htons(ETH_P_IPV6); 2374b4f2229SPuranjay Mohan 2384b4f2229SPuranjay Mohan __builtin_memset(ip6h, 0, sizeof(*ip6h)); 2394b4f2229SPuranjay Mohan ip6h->version = 6; 2404b4f2229SPuranjay Mohan ip6h->nexthdr = nexthdr; 2414b4f2229SPuranjay Mohan ip6h->payload_len = bpf_htons(payload_len); 2424b4f2229SPuranjay Mohan ip6h->hop_limit = 64; 2434b4f2229SPuranjay Mohan __builtin_memcpy(&ip6h->saddr, saddr, sizeof(ip6h->saddr)); 2444b4f2229SPuranjay Mohan __builtin_memcpy(&ip6h->daddr, daddr, sizeof(ip6h->daddr)); 2454b4f2229SPuranjay Mohan 2464b4f2229SPuranjay Mohan return 0; 2474b4f2229SPuranjay Mohan } 2484b4f2229SPuranjay Mohan 2494b4f2229SPuranjay Mohan static __always_inline void update_stats(void *map, __u32 key, __u16 bytes) 2504b4f2229SPuranjay Mohan { 2514b4f2229SPuranjay Mohan struct lb_stats *st = bpf_map_lookup_elem(map, &key); 2524b4f2229SPuranjay Mohan 2534b4f2229SPuranjay Mohan if (st) { 2544b4f2229SPuranjay Mohan st->v1 += 1; 2554b4f2229SPuranjay Mohan st->v2 += bytes; 2564b4f2229SPuranjay Mohan } 2574b4f2229SPuranjay Mohan } 2584b4f2229SPuranjay Mohan 2594b4f2229SPuranjay Mohan static __always_inline void count_action(int action) 2604b4f2229SPuranjay Mohan { 2614b4f2229SPuranjay Mohan struct lb_stats *st; 2624b4f2229SPuranjay Mohan __u32 key; 2634b4f2229SPuranjay Mohan 2644b4f2229SPuranjay Mohan if (action == XDP_TX) 2654b4f2229SPuranjay Mohan key = STATS_XDP_TX; 2664b4f2229SPuranjay Mohan else if (action == XDP_PASS) 2674b4f2229SPuranjay Mohan key = STATS_XDP_PASS; 2684b4f2229SPuranjay Mohan else 2694b4f2229SPuranjay Mohan key = STATS_XDP_DROP; 2704b4f2229SPuranjay Mohan 2714b4f2229SPuranjay Mohan st = bpf_map_lookup_elem(&stats, &key); 2724b4f2229SPuranjay Mohan if (st) 2734b4f2229SPuranjay Mohan st->v1 += 1; 2744b4f2229SPuranjay Mohan } 2754b4f2229SPuranjay Mohan 2764b4f2229SPuranjay Mohan static __always_inline bool is_under_flood(void) 2774b4f2229SPuranjay Mohan { 2784b4f2229SPuranjay Mohan __u32 key = STATS_NEW_CONN; 2794b4f2229SPuranjay Mohan struct lb_stats *conn_st = bpf_map_lookup_elem(&stats, &key); 2804b4f2229SPuranjay Mohan __u64 cur_time; 2814b4f2229SPuranjay Mohan 2824b4f2229SPuranjay Mohan if (!conn_st) 2834b4f2229SPuranjay Mohan return true; 2844b4f2229SPuranjay Mohan 2854b4f2229SPuranjay Mohan cur_time = bpf_ktime_get_ns(); 2864b4f2229SPuranjay Mohan if ((cur_time - conn_st->v2) > ONE_SEC) { 2874b4f2229SPuranjay Mohan conn_st->v1 = 1; 2884b4f2229SPuranjay Mohan conn_st->v2 = cur_time; 2894b4f2229SPuranjay Mohan } else { 2904b4f2229SPuranjay Mohan conn_st->v1 += 1; 2914b4f2229SPuranjay Mohan if (conn_st->v1 > MAX_CONN_RATE) 2924b4f2229SPuranjay Mohan return true; 2934b4f2229SPuranjay Mohan } 2944b4f2229SPuranjay Mohan return false; 2954b4f2229SPuranjay Mohan } 2964b4f2229SPuranjay Mohan 2974b4f2229SPuranjay Mohan static __always_inline struct real_definition *connection_table_lookup(void *lru_map, 2984b4f2229SPuranjay Mohan struct flow_key *flow, 2994b4f2229SPuranjay Mohan __u32 *out_pos) 3004b4f2229SPuranjay Mohan { 3014b4f2229SPuranjay Mohan struct real_pos_lru *dst_lru; 3024b4f2229SPuranjay Mohan struct real_definition *real; 3034b4f2229SPuranjay Mohan __u32 key; 3044b4f2229SPuranjay Mohan 3054b4f2229SPuranjay Mohan dst_lru = bpf_map_lookup_elem(lru_map, flow); 3064b4f2229SPuranjay Mohan if (!dst_lru) 3074b4f2229SPuranjay Mohan return NULL; 3084b4f2229SPuranjay Mohan 3094b4f2229SPuranjay Mohan /* UDP connections use atime-based timeout instead of FIN/RST */ 3104b4f2229SPuranjay Mohan if (flow->proto == IPPROTO_UDP) { 3114b4f2229SPuranjay Mohan __u64 cur_time = bpf_ktime_get_ns(); 3124b4f2229SPuranjay Mohan 3134b4f2229SPuranjay Mohan if (cur_time - dst_lru->atime > LRU_UDP_TIMEOUT) 3144b4f2229SPuranjay Mohan return NULL; 3154b4f2229SPuranjay Mohan dst_lru->atime = cur_time; 3164b4f2229SPuranjay Mohan } 3174b4f2229SPuranjay Mohan 3184b4f2229SPuranjay Mohan key = dst_lru->pos; 3194b4f2229SPuranjay Mohan *out_pos = key; 3204b4f2229SPuranjay Mohan real = bpf_map_lookup_elem(&reals, &key); 3214b4f2229SPuranjay Mohan return real; 3224b4f2229SPuranjay Mohan } 3234b4f2229SPuranjay Mohan 3244b4f2229SPuranjay Mohan static __always_inline bool get_packet_dst(struct real_definition **real, struct flow_key *flow, 3254b4f2229SPuranjay Mohan struct vip_meta *vip_info, bool is_v6, void *lru_map, 3264b4f2229SPuranjay Mohan bool is_rst, __u32 *out_pos) 3274b4f2229SPuranjay Mohan { 3284b4f2229SPuranjay Mohan bool under_flood; 3294b4f2229SPuranjay Mohan __u32 hash, ch_key; 3304b4f2229SPuranjay Mohan __u32 *ch_val; 3314b4f2229SPuranjay Mohan __u32 real_pos; 3324b4f2229SPuranjay Mohan 3334b4f2229SPuranjay Mohan under_flood = is_under_flood(); 3344b4f2229SPuranjay Mohan 3354b4f2229SPuranjay Mohan if (is_v6) { 3364b4f2229SPuranjay Mohan __u32 src_hash = jhash2_4words((__u32 *)flow->srcv6, MAX_VIPS); 3374b4f2229SPuranjay Mohan 3384b4f2229SPuranjay Mohan hash = jhash_2words(src_hash, flow->ports, CH_RING_SIZE); 3394b4f2229SPuranjay Mohan } else { 3404b4f2229SPuranjay Mohan hash = jhash_2words(flow->src, flow->ports, CH_RING_SIZE); 3414b4f2229SPuranjay Mohan } 3424b4f2229SPuranjay Mohan 3434b4f2229SPuranjay Mohan ch_key = CH_RING_SIZE * vip_info->vip_num + hash % CH_RING_SIZE; 3444b4f2229SPuranjay Mohan ch_val = bpf_map_lookup_elem(&ch_rings, &ch_key); 3454b4f2229SPuranjay Mohan if (!ch_val) 3464b4f2229SPuranjay Mohan return false; 3474b4f2229SPuranjay Mohan real_pos = *ch_val; 3484b4f2229SPuranjay Mohan 3494b4f2229SPuranjay Mohan *real = bpf_map_lookup_elem(&reals, &real_pos); 3504b4f2229SPuranjay Mohan if (!(*real)) 3514b4f2229SPuranjay Mohan return false; 3524b4f2229SPuranjay Mohan 3534b4f2229SPuranjay Mohan if (!(vip_info->flags & F_LRU_BYPASS) && !under_flood && !is_rst) { 3544b4f2229SPuranjay Mohan struct real_pos_lru new_lru = { .pos = real_pos }; 3554b4f2229SPuranjay Mohan 3564b4f2229SPuranjay Mohan if (flow->proto == IPPROTO_UDP) 3574b4f2229SPuranjay Mohan new_lru.atime = bpf_ktime_get_ns(); 3584b4f2229SPuranjay Mohan bpf_map_update_elem(lru_map, flow, &new_lru, BPF_ANY); 3594b4f2229SPuranjay Mohan } 3604b4f2229SPuranjay Mohan 3614b4f2229SPuranjay Mohan *out_pos = real_pos; 3624b4f2229SPuranjay Mohan return true; 3634b4f2229SPuranjay Mohan } 3644b4f2229SPuranjay Mohan 3654b4f2229SPuranjay Mohan static __always_inline void update_vip_lru_miss_stats(struct vip_definition *vip, bool is_v6, 3664b4f2229SPuranjay Mohan __u32 real_idx) 3674b4f2229SPuranjay Mohan { 3684b4f2229SPuranjay Mohan struct vip_definition *miss_vip; 3694b4f2229SPuranjay Mohan __u32 key = 0; 3704b4f2229SPuranjay Mohan __u32 *cnt; 3714b4f2229SPuranjay Mohan 3724b4f2229SPuranjay Mohan miss_vip = bpf_map_lookup_elem(&vip_miss_stats, &key); 3734b4f2229SPuranjay Mohan if (!miss_vip) 3744b4f2229SPuranjay Mohan return; 3754b4f2229SPuranjay Mohan 3764b4f2229SPuranjay Mohan if (is_v6) { 3774b4f2229SPuranjay Mohan if (miss_vip->vipv6[0] != vip->vipv6[0] || miss_vip->vipv6[1] != vip->vipv6[1] || 3784b4f2229SPuranjay Mohan miss_vip->vipv6[2] != vip->vipv6[2] || miss_vip->vipv6[3] != vip->vipv6[3]) 3794b4f2229SPuranjay Mohan return; 3804b4f2229SPuranjay Mohan } else { 3814b4f2229SPuranjay Mohan if (miss_vip->vip != vip->vip) 3824b4f2229SPuranjay Mohan return; 3834b4f2229SPuranjay Mohan } 3844b4f2229SPuranjay Mohan 3854b4f2229SPuranjay Mohan if (miss_vip->port != vip->port || miss_vip->proto != vip->proto) 3864b4f2229SPuranjay Mohan return; 3874b4f2229SPuranjay Mohan 3884b4f2229SPuranjay Mohan cnt = bpf_map_lookup_elem(&lru_miss_stats, &real_idx); 3894b4f2229SPuranjay Mohan if (cnt) 3904b4f2229SPuranjay Mohan *cnt += 1; 3914b4f2229SPuranjay Mohan } 3924b4f2229SPuranjay Mohan 3934b4f2229SPuranjay Mohan static __noinline int process_packet(struct xdp_md *xdp) 3944b4f2229SPuranjay Mohan { 3954b4f2229SPuranjay Mohan void *data = (void *)(long)xdp->data; 3964b4f2229SPuranjay Mohan void *data_end = (void *)(long)xdp->data_end; 3974b4f2229SPuranjay Mohan struct ethhdr *eth = data; 3984b4f2229SPuranjay Mohan struct real_definition *dst = NULL; 3994b4f2229SPuranjay Mohan struct vip_definition vip_def = {}; 4004b4f2229SPuranjay Mohan struct ctl_value *cval; 4014b4f2229SPuranjay Mohan struct flow_key flow = {}; 4024b4f2229SPuranjay Mohan struct vip_meta *vip_info; 4034b4f2229SPuranjay Mohan struct lb_stats *data_stats; 4044b4f2229SPuranjay Mohan struct udphdr *uh; 4054b4f2229SPuranjay Mohan __be32 tnl_src[4]; 4064b4f2229SPuranjay Mohan void *lru_map; 4074b4f2229SPuranjay Mohan void *l4; 4084b4f2229SPuranjay Mohan __u16 payload_len; 4094b4f2229SPuranjay Mohan __u32 real_pos = 0, cpu_num, key; 4104b4f2229SPuranjay Mohan __u8 proto; 4114b4f2229SPuranjay Mohan int action = XDP_DROP; 4124b4f2229SPuranjay Mohan bool is_v6, is_syn = false, is_rst = false; 4134b4f2229SPuranjay Mohan 4144b4f2229SPuranjay Mohan if (eth + 1 > data_end) 4154b4f2229SPuranjay Mohan goto out; 4164b4f2229SPuranjay Mohan 4174b4f2229SPuranjay Mohan if (eth->h_proto == bpf_htons(ETH_P_IPV6)) { 4184b4f2229SPuranjay Mohan is_v6 = true; 4194b4f2229SPuranjay Mohan } else if (eth->h_proto == bpf_htons(ETH_P_IP)) { 4204b4f2229SPuranjay Mohan is_v6 = false; 4214b4f2229SPuranjay Mohan } else { 4224b4f2229SPuranjay Mohan action = XDP_PASS; 4234b4f2229SPuranjay Mohan goto out; 4244b4f2229SPuranjay Mohan } 4254b4f2229SPuranjay Mohan 4264b4f2229SPuranjay Mohan if (is_v6) { 4274b4f2229SPuranjay Mohan struct ipv6hdr *ip6h = (void *)(eth + 1); 4284b4f2229SPuranjay Mohan 4294b4f2229SPuranjay Mohan if (ip6h + 1 > data_end) 4304b4f2229SPuranjay Mohan goto out; 4314b4f2229SPuranjay Mohan if (ip6h->nexthdr == IPPROTO_FRAGMENT) 4324b4f2229SPuranjay Mohan goto out; 4334b4f2229SPuranjay Mohan 4344b4f2229SPuranjay Mohan payload_len = sizeof(struct ipv6hdr) + bpf_ntohs(ip6h->payload_len); 4354b4f2229SPuranjay Mohan proto = ip6h->nexthdr; 4364b4f2229SPuranjay Mohan 4374b4f2229SPuranjay Mohan __builtin_memcpy(flow.srcv6, &ip6h->saddr, sizeof(flow.srcv6)); 4384b4f2229SPuranjay Mohan __builtin_memcpy(flow.dstv6, &ip6h->daddr, sizeof(flow.dstv6)); 4394b4f2229SPuranjay Mohan __builtin_memcpy(vip_def.vipv6, &ip6h->daddr, sizeof(vip_def.vipv6)); 4404b4f2229SPuranjay Mohan l4 = (void *)(ip6h + 1); 4414b4f2229SPuranjay Mohan } else { 4424b4f2229SPuranjay Mohan struct iphdr *iph = (void *)(eth + 1); 4434b4f2229SPuranjay Mohan 4444b4f2229SPuranjay Mohan if (iph + 1 > data_end) 4454b4f2229SPuranjay Mohan goto out; 4464b4f2229SPuranjay Mohan if (iph->ihl != 5) 4474b4f2229SPuranjay Mohan goto out; 4484b4f2229SPuranjay Mohan if (iph->frag_off & bpf_htons(PCKT_FRAGMENTED)) 4494b4f2229SPuranjay Mohan goto out; 4504b4f2229SPuranjay Mohan 4514b4f2229SPuranjay Mohan payload_len = bpf_ntohs(iph->tot_len); 4524b4f2229SPuranjay Mohan proto = iph->protocol; 4534b4f2229SPuranjay Mohan 4544b4f2229SPuranjay Mohan flow.src = iph->saddr; 4554b4f2229SPuranjay Mohan flow.dst = iph->daddr; 4564b4f2229SPuranjay Mohan vip_def.vip = iph->daddr; 4574b4f2229SPuranjay Mohan l4 = (void *)(iph + 1); 4584b4f2229SPuranjay Mohan } 4594b4f2229SPuranjay Mohan 4604b4f2229SPuranjay Mohan /* TCP and UDP share the same port layout at offset 0 */ 4614b4f2229SPuranjay Mohan if (proto != IPPROTO_TCP && proto != IPPROTO_UDP) { 4624b4f2229SPuranjay Mohan action = XDP_PASS; 4634b4f2229SPuranjay Mohan goto out; 4644b4f2229SPuranjay Mohan } 4654b4f2229SPuranjay Mohan 4664b4f2229SPuranjay Mohan uh = l4; 4674b4f2229SPuranjay Mohan if ((void *)(uh + 1) > data_end) 4684b4f2229SPuranjay Mohan goto out; 4694b4f2229SPuranjay Mohan flow.port16[0] = uh->source; 4704b4f2229SPuranjay Mohan flow.port16[1] = uh->dest; 4714b4f2229SPuranjay Mohan 4724b4f2229SPuranjay Mohan if (proto == IPPROTO_TCP) { 4734b4f2229SPuranjay Mohan struct tcphdr *th = l4; 4744b4f2229SPuranjay Mohan 4754b4f2229SPuranjay Mohan if ((void *)(th + 1) > data_end) 4764b4f2229SPuranjay Mohan goto out; 4774b4f2229SPuranjay Mohan is_syn = th->syn; 4784b4f2229SPuranjay Mohan is_rst = th->rst; 4794b4f2229SPuranjay Mohan } 4804b4f2229SPuranjay Mohan 4814b4f2229SPuranjay Mohan flow.proto = proto; 4824b4f2229SPuranjay Mohan vip_def.port = flow.port16[1]; 4834b4f2229SPuranjay Mohan vip_def.proto = proto; 4844b4f2229SPuranjay Mohan 4854b4f2229SPuranjay Mohan vip_info = bpf_map_lookup_elem(&vip_map, &vip_def); 4864b4f2229SPuranjay Mohan if (!vip_info) { 4874b4f2229SPuranjay Mohan action = XDP_PASS; 4884b4f2229SPuranjay Mohan goto out; 4894b4f2229SPuranjay Mohan } 4904b4f2229SPuranjay Mohan 4914b4f2229SPuranjay Mohan key = STATS_LRU; 4924b4f2229SPuranjay Mohan data_stats = bpf_map_lookup_elem(&stats, &key); 4934b4f2229SPuranjay Mohan if (!data_stats) 4944b4f2229SPuranjay Mohan goto out; 4954b4f2229SPuranjay Mohan data_stats->v1 += 1; 4964b4f2229SPuranjay Mohan 4974b4f2229SPuranjay Mohan cpu_num = bpf_get_smp_processor_id(); 4984b4f2229SPuranjay Mohan lru_map = bpf_map_lookup_elem(&lru_mapping, &cpu_num); 4994b4f2229SPuranjay Mohan if (!lru_map) 5004b4f2229SPuranjay Mohan goto out; 5014b4f2229SPuranjay Mohan 5024b4f2229SPuranjay Mohan if (!(vip_info->flags & F_LRU_BYPASS) && !is_syn) 5034b4f2229SPuranjay Mohan dst = connection_table_lookup(lru_map, &flow, &real_pos); 5044b4f2229SPuranjay Mohan 5054b4f2229SPuranjay Mohan if (!dst) { 5064b4f2229SPuranjay Mohan if (flow.proto == IPPROTO_TCP) { 5074b4f2229SPuranjay Mohan struct lb_stats *miss_st; 5084b4f2229SPuranjay Mohan 5094b4f2229SPuranjay Mohan key = STATS_LRU_MISS; 5104b4f2229SPuranjay Mohan miss_st = bpf_map_lookup_elem(&stats, &key); 5114b4f2229SPuranjay Mohan if (miss_st) 5124b4f2229SPuranjay Mohan miss_st->v1 += 1; 5134b4f2229SPuranjay Mohan } 5144b4f2229SPuranjay Mohan 5154b4f2229SPuranjay Mohan if (!get_packet_dst(&dst, &flow, vip_info, is_v6, lru_map, is_rst, &real_pos)) 5164b4f2229SPuranjay Mohan goto out; 5174b4f2229SPuranjay Mohan 5184b4f2229SPuranjay Mohan update_vip_lru_miss_stats(&vip_def, is_v6, real_pos); 5194b4f2229SPuranjay Mohan data_stats->v2 += 1; 5204b4f2229SPuranjay Mohan } 5214b4f2229SPuranjay Mohan 5224b4f2229SPuranjay Mohan key = 0; 5234b4f2229SPuranjay Mohan cval = bpf_map_lookup_elem(&ctl_array, &key); 5244b4f2229SPuranjay Mohan if (!cval) 5254b4f2229SPuranjay Mohan goto out; 5264b4f2229SPuranjay Mohan 5274b4f2229SPuranjay Mohan update_stats(&stats, vip_info->vip_num, payload_len); 5284b4f2229SPuranjay Mohan update_stats(&reals_stats, real_pos, payload_len); 5294b4f2229SPuranjay Mohan 5304b4f2229SPuranjay Mohan if (is_v6) { 5314b4f2229SPuranjay Mohan create_encap_ipv6_src(flow.port16[0], flow.srcv6[0], tnl_src); 5324b4f2229SPuranjay Mohan if (encap_v6(xdp, tnl_src, dst->dstv6, IPPROTO_IPV6, payload_len, cval->mac)) 5334b4f2229SPuranjay Mohan goto out; 5344b4f2229SPuranjay Mohan } else if (dst->flags & F_IPV6) { 5354b4f2229SPuranjay Mohan create_encap_ipv6_src(flow.port16[0], flow.src, tnl_src); 5364b4f2229SPuranjay Mohan if (encap_v6(xdp, tnl_src, dst->dstv6, IPPROTO_IPIP, payload_len, cval->mac)) 5374b4f2229SPuranjay Mohan goto out; 5384b4f2229SPuranjay Mohan } else { 5394b4f2229SPuranjay Mohan if (encap_v4(xdp, create_encap_ipv4_src(flow.port16[0], flow.src), dst->dst, 5404b4f2229SPuranjay Mohan payload_len, cval->mac)) 5414b4f2229SPuranjay Mohan goto out; 5424b4f2229SPuranjay Mohan } 5434b4f2229SPuranjay Mohan 5444b4f2229SPuranjay Mohan action = XDP_TX; 5454b4f2229SPuranjay Mohan 5464b4f2229SPuranjay Mohan out: 5474b4f2229SPuranjay Mohan count_action(action); 5484b4f2229SPuranjay Mohan return action; 5494b4f2229SPuranjay Mohan } 5504b4f2229SPuranjay Mohan 5514b4f2229SPuranjay Mohan static __always_inline int strip_encap(struct xdp_md *xdp, const struct ethhdr *saved_eth) 5524b4f2229SPuranjay Mohan { 5534b4f2229SPuranjay Mohan void *data = (void *)(long)xdp->data; 5544b4f2229SPuranjay Mohan void *data_end = (void *)(long)xdp->data_end; 5554b4f2229SPuranjay Mohan struct ethhdr *eth = data; 5564b4f2229SPuranjay Mohan int hdr_sz; 5574b4f2229SPuranjay Mohan 5584b4f2229SPuranjay Mohan if (eth + 1 > data_end) 5594b4f2229SPuranjay Mohan return -1; 5604b4f2229SPuranjay Mohan 5614b4f2229SPuranjay Mohan hdr_sz = (eth->h_proto == bpf_htons(ETH_P_IPV6)) ? (int)sizeof(struct ipv6hdr) 5624b4f2229SPuranjay Mohan : (int)sizeof(struct iphdr); 5634b4f2229SPuranjay Mohan 5644b4f2229SPuranjay Mohan if (bpf_xdp_adjust_head(xdp, hdr_sz)) 5654b4f2229SPuranjay Mohan return -1; 5664b4f2229SPuranjay Mohan 5674b4f2229SPuranjay Mohan data = (void *)(long)xdp->data; 5684b4f2229SPuranjay Mohan data_end = (void *)(long)xdp->data_end; 5694b4f2229SPuranjay Mohan eth = data; 5704b4f2229SPuranjay Mohan 5714b4f2229SPuranjay Mohan if (eth + 1 > data_end) 5724b4f2229SPuranjay Mohan return -1; 5734b4f2229SPuranjay Mohan 5744b4f2229SPuranjay Mohan __builtin_memcpy(eth, saved_eth, sizeof(*saved_eth)); 5754b4f2229SPuranjay Mohan return 0; 5764b4f2229SPuranjay Mohan } 5774b4f2229SPuranjay Mohan 5784b4f2229SPuranjay Mohan static __always_inline void randomize_src(struct xdp_md *xdp, int saddr_off, __u32 *rand_state) 5794b4f2229SPuranjay Mohan { 5804b4f2229SPuranjay Mohan void *data = (void *)(long)xdp->data; 5814b4f2229SPuranjay Mohan void *data_end = (void *)(long)xdp->data_end; 5824b4f2229SPuranjay Mohan __u32 *saddr = data + saddr_off; 5834b4f2229SPuranjay Mohan 5844b4f2229SPuranjay Mohan *rand_state ^= *rand_state << 13; 5854b4f2229SPuranjay Mohan *rand_state ^= *rand_state >> 17; 5864b4f2229SPuranjay Mohan *rand_state ^= *rand_state << 5; 5874b4f2229SPuranjay Mohan 5884b4f2229SPuranjay Mohan if ((void *)(saddr + 1) <= data_end) 5894b4f2229SPuranjay Mohan *saddr = *rand_state & flow_mask; 5904b4f2229SPuranjay Mohan } 5914b4f2229SPuranjay Mohan 5924b4f2229SPuranjay Mohan SEC("xdp") 5934b4f2229SPuranjay Mohan int xdp_lb_bench(struct xdp_md *xdp) 5944b4f2229SPuranjay Mohan { 5954b4f2229SPuranjay Mohan void *data = (void *)(long)xdp->data; 5964b4f2229SPuranjay Mohan void *data_end = (void *)(long)xdp->data_end; 5974b4f2229SPuranjay Mohan struct ethhdr *eth = data; 5984b4f2229SPuranjay Mohan struct ethhdr saved_eth; 5994b4f2229SPuranjay Mohan __u32 rand_state = 0; 6004b4f2229SPuranjay Mohan __u32 batch_hash = 0; 6014b4f2229SPuranjay Mohan int saddr_off = 0; 6024b4f2229SPuranjay Mohan bool is_v6; 6034b4f2229SPuranjay Mohan 6044b4f2229SPuranjay Mohan if (eth + 1 > data_end) 6054b4f2229SPuranjay Mohan return XDP_DROP; 6064b4f2229SPuranjay Mohan 6074b4f2229SPuranjay Mohan __builtin_memcpy(&saved_eth, eth, sizeof(saved_eth)); 6084b4f2229SPuranjay Mohan 6094b4f2229SPuranjay Mohan is_v6 = (saved_eth.h_proto == bpf_htons(ETH_P_IPV6)); 6104b4f2229SPuranjay Mohan 6114b4f2229SPuranjay Mohan saddr_off = sizeof(struct ethhdr) + (is_v6 ? offsetof(struct ipv6hdr, saddr) : 6124b4f2229SPuranjay Mohan offsetof(struct iphdr, saddr)); 6134b4f2229SPuranjay Mohan 6144b4f2229SPuranjay Mohan if (flow_mask) 6154b4f2229SPuranjay Mohan rand_state = bpf_get_prandom_u32() | 1; 6164b4f2229SPuranjay Mohan 6174b4f2229SPuranjay Mohan if (cold_lru) { 6184b4f2229SPuranjay Mohan __u32 *saddr = data + saddr_off; 6194b4f2229SPuranjay Mohan 6204b4f2229SPuranjay Mohan batch_gen++; 621*fa747e9fSPuranjay Mohan batch_hash = (batch_gen + bpf_get_smp_processor_id()) * KNUTH_HASH_MULT; 6224b4f2229SPuranjay Mohan if ((void *)(saddr + 1) <= data_end) 6234b4f2229SPuranjay Mohan *saddr ^= batch_hash; 6244b4f2229SPuranjay Mohan } 6254b4f2229SPuranjay Mohan 6264b4f2229SPuranjay Mohan return BENCH_BPF_LOOP( 6274b4f2229SPuranjay Mohan process_packet(xdp), 6284b4f2229SPuranjay Mohan ({ 6294b4f2229SPuranjay Mohan if (__bench_result == XDP_TX) { 6304b4f2229SPuranjay Mohan if (strip_encap(xdp, &saved_eth)) 6314b4f2229SPuranjay Mohan return XDP_DROP; 6324b4f2229SPuranjay Mohan if (rand_state) 6334b4f2229SPuranjay Mohan randomize_src(xdp, saddr_off, &rand_state); 6344b4f2229SPuranjay Mohan } 6354b4f2229SPuranjay Mohan if (cold_lru) { 6364b4f2229SPuranjay Mohan void *d = (void *)(long)xdp->data; 6374b4f2229SPuranjay Mohan void *de = (void *)(long)xdp->data_end; 6384b4f2229SPuranjay Mohan __u32 *__sa = d + saddr_off; 6394b4f2229SPuranjay Mohan 6404b4f2229SPuranjay Mohan if ((void *)(__sa + 1) <= de) 6414b4f2229SPuranjay Mohan *__sa ^= batch_hash; 6424b4f2229SPuranjay Mohan } 6434b4f2229SPuranjay Mohan }) 6444b4f2229SPuranjay Mohan ); 6454b4f2229SPuranjay Mohan } 6464b4f2229SPuranjay Mohan 6474b4f2229SPuranjay Mohan char _license[] SEC("license") = "GPL"; 648