1*e3cdf6cfSChris J Arges // SPDX-License-Identifier: GPL-2.0 2*e3cdf6cfSChris J Arges 3*e3cdf6cfSChris J Arges #include <stddef.h> 4*e3cdf6cfSChris J Arges #include <linux/bpf.h> 5*e3cdf6cfSChris J Arges #include <linux/in.h> 6*e3cdf6cfSChris J Arges #include <linux/if_ether.h> 7*e3cdf6cfSChris J Arges #include <linux/ip.h> 8*e3cdf6cfSChris J Arges #include <linux/ipv6.h> 9*e3cdf6cfSChris J Arges #include <linux/udp.h> 10*e3cdf6cfSChris J Arges #include <linux/tcp.h> 11*e3cdf6cfSChris J Arges #include <bpf/bpf_endian.h> 12*e3cdf6cfSChris J Arges #include <bpf/bpf_helpers.h> 13*e3cdf6cfSChris J Arges 14*e3cdf6cfSChris J Arges enum { 15*e3cdf6cfSChris J Arges XDP_PORT = 1, 16*e3cdf6cfSChris J Arges XDP_PROTO = 4, 17*e3cdf6cfSChris J Arges } xdp_map_setup_keys; 18*e3cdf6cfSChris J Arges 19*e3cdf6cfSChris J Arges struct { 20*e3cdf6cfSChris J Arges __uint(type, BPF_MAP_TYPE_ARRAY); 21*e3cdf6cfSChris J Arges __uint(max_entries, 5); 22*e3cdf6cfSChris J Arges __type(key, __u32); 23*e3cdf6cfSChris J Arges __type(value, __s32); 24*e3cdf6cfSChris J Arges } map_xdp_setup SEC(".maps"); 25*e3cdf6cfSChris J Arges 26*e3cdf6cfSChris J Arges /* RSS hash results: key 0 = hash, key 1 = hash type, 27*e3cdf6cfSChris J Arges * key 2 = packet count, key 3 = error count. 28*e3cdf6cfSChris J Arges */ 29*e3cdf6cfSChris J Arges enum { 30*e3cdf6cfSChris J Arges RSS_KEY_HASH = 0, 31*e3cdf6cfSChris J Arges RSS_KEY_TYPE = 1, 32*e3cdf6cfSChris J Arges RSS_KEY_PKT_CNT = 2, 33*e3cdf6cfSChris J Arges RSS_KEY_ERR_CNT = 3, 34*e3cdf6cfSChris J Arges }; 35*e3cdf6cfSChris J Arges 36*e3cdf6cfSChris J Arges struct { 37*e3cdf6cfSChris J Arges __uint(type, BPF_MAP_TYPE_ARRAY); 38*e3cdf6cfSChris J Arges __type(key, __u32); 39*e3cdf6cfSChris J Arges __type(value, __u32); 40*e3cdf6cfSChris J Arges __uint(max_entries, 4); 41*e3cdf6cfSChris J Arges } map_rss SEC(".maps"); 42*e3cdf6cfSChris J Arges 43*e3cdf6cfSChris J Arges /* Mirror of enum xdp_rss_hash_type from include/net/xdp.h. 44*e3cdf6cfSChris J Arges * Needed because the enum is not part of UAPI headers. 45*e3cdf6cfSChris J Arges */ 46*e3cdf6cfSChris J Arges enum xdp_rss_hash_type { 47*e3cdf6cfSChris J Arges XDP_RSS_L3_IPV4 = 1U << 0, 48*e3cdf6cfSChris J Arges XDP_RSS_L3_IPV6 = 1U << 1, 49*e3cdf6cfSChris J Arges XDP_RSS_L3_DYNHDR = 1U << 2, 50*e3cdf6cfSChris J Arges XDP_RSS_L4 = 1U << 3, 51*e3cdf6cfSChris J Arges XDP_RSS_L4_TCP = 1U << 4, 52*e3cdf6cfSChris J Arges XDP_RSS_L4_UDP = 1U << 5, 53*e3cdf6cfSChris J Arges XDP_RSS_L4_SCTP = 1U << 6, 54*e3cdf6cfSChris J Arges XDP_RSS_L4_IPSEC = 1U << 7, 55*e3cdf6cfSChris J Arges XDP_RSS_L4_ICMP = 1U << 8, 56*e3cdf6cfSChris J Arges }; 57*e3cdf6cfSChris J Arges 58*e3cdf6cfSChris J Arges extern int bpf_xdp_metadata_rx_hash(const struct xdp_md *ctx, __u32 *hash, 59*e3cdf6cfSChris J Arges enum xdp_rss_hash_type *rss_type) __ksym; 60*e3cdf6cfSChris J Arges 61*e3cdf6cfSChris J Arges static __always_inline __u16 get_dest_port(void *l4, void *data_end, 62*e3cdf6cfSChris J Arges __u8 protocol) 63*e3cdf6cfSChris J Arges { 64*e3cdf6cfSChris J Arges if (protocol == IPPROTO_UDP) { 65*e3cdf6cfSChris J Arges struct udphdr *udp = l4; 66*e3cdf6cfSChris J Arges 67*e3cdf6cfSChris J Arges if ((void *)(udp + 1) > data_end) 68*e3cdf6cfSChris J Arges return 0; 69*e3cdf6cfSChris J Arges return udp->dest; 70*e3cdf6cfSChris J Arges } else if (protocol == IPPROTO_TCP) { 71*e3cdf6cfSChris J Arges struct tcphdr *tcp = l4; 72*e3cdf6cfSChris J Arges 73*e3cdf6cfSChris J Arges if ((void *)(tcp + 1) > data_end) 74*e3cdf6cfSChris J Arges return 0; 75*e3cdf6cfSChris J Arges return tcp->dest; 76*e3cdf6cfSChris J Arges } 77*e3cdf6cfSChris J Arges 78*e3cdf6cfSChris J Arges return 0; 79*e3cdf6cfSChris J Arges } 80*e3cdf6cfSChris J Arges 81*e3cdf6cfSChris J Arges SEC("xdp") 82*e3cdf6cfSChris J Arges int xdp_rss_hash(struct xdp_md *ctx) 83*e3cdf6cfSChris J Arges { 84*e3cdf6cfSChris J Arges void *data_end = (void *)(long)ctx->data_end; 85*e3cdf6cfSChris J Arges void *data = (void *)(long)ctx->data; 86*e3cdf6cfSChris J Arges enum xdp_rss_hash_type rss_type = 0; 87*e3cdf6cfSChris J Arges struct ethhdr *eth = data; 88*e3cdf6cfSChris J Arges __u8 l4_proto = 0; 89*e3cdf6cfSChris J Arges __u32 hash = 0; 90*e3cdf6cfSChris J Arges __u32 key, val; 91*e3cdf6cfSChris J Arges void *l4 = NULL; 92*e3cdf6cfSChris J Arges __u32 *cnt; 93*e3cdf6cfSChris J Arges int ret; 94*e3cdf6cfSChris J Arges 95*e3cdf6cfSChris J Arges if ((void *)(eth + 1) > data_end) 96*e3cdf6cfSChris J Arges return XDP_PASS; 97*e3cdf6cfSChris J Arges 98*e3cdf6cfSChris J Arges if (eth->h_proto == bpf_htons(ETH_P_IP)) { 99*e3cdf6cfSChris J Arges struct iphdr *iph = (void *)(eth + 1); 100*e3cdf6cfSChris J Arges 101*e3cdf6cfSChris J Arges if ((void *)(iph + 1) > data_end) 102*e3cdf6cfSChris J Arges return XDP_PASS; 103*e3cdf6cfSChris J Arges l4_proto = iph->protocol; 104*e3cdf6cfSChris J Arges l4 = (void *)(iph + 1); 105*e3cdf6cfSChris J Arges } else if (eth->h_proto == bpf_htons(ETH_P_IPV6)) { 106*e3cdf6cfSChris J Arges struct ipv6hdr *ip6h = (void *)(eth + 1); 107*e3cdf6cfSChris J Arges 108*e3cdf6cfSChris J Arges if ((void *)(ip6h + 1) > data_end) 109*e3cdf6cfSChris J Arges return XDP_PASS; 110*e3cdf6cfSChris J Arges l4_proto = ip6h->nexthdr; 111*e3cdf6cfSChris J Arges l4 = (void *)(ip6h + 1); 112*e3cdf6cfSChris J Arges } 113*e3cdf6cfSChris J Arges 114*e3cdf6cfSChris J Arges if (!l4) 115*e3cdf6cfSChris J Arges return XDP_PASS; 116*e3cdf6cfSChris J Arges 117*e3cdf6cfSChris J Arges /* Filter on the configured protocol (map_xdp_setup key XDP_PROTO). 118*e3cdf6cfSChris J Arges * When set, only process packets matching the requested L4 protocol. 119*e3cdf6cfSChris J Arges */ 120*e3cdf6cfSChris J Arges key = XDP_PROTO; 121*e3cdf6cfSChris J Arges __s32 *proto_cfg = bpf_map_lookup_elem(&map_xdp_setup, &key); 122*e3cdf6cfSChris J Arges 123*e3cdf6cfSChris J Arges if (proto_cfg && *proto_cfg != 0 && l4_proto != (__u8)*proto_cfg) 124*e3cdf6cfSChris J Arges return XDP_PASS; 125*e3cdf6cfSChris J Arges 126*e3cdf6cfSChris J Arges /* Filter on the configured port (map_xdp_setup key XDP_PORT). 127*e3cdf6cfSChris J Arges * Only applies to protocols with ports (UDP, TCP). 128*e3cdf6cfSChris J Arges */ 129*e3cdf6cfSChris J Arges key = XDP_PORT; 130*e3cdf6cfSChris J Arges __s32 *port_cfg = bpf_map_lookup_elem(&map_xdp_setup, &key); 131*e3cdf6cfSChris J Arges 132*e3cdf6cfSChris J Arges if (port_cfg && *port_cfg != 0) { 133*e3cdf6cfSChris J Arges __u16 dest = get_dest_port(l4, data_end, l4_proto); 134*e3cdf6cfSChris J Arges 135*e3cdf6cfSChris J Arges if (!dest || bpf_ntohs(dest) != (__u16)*port_cfg) 136*e3cdf6cfSChris J Arges return XDP_PASS; 137*e3cdf6cfSChris J Arges } 138*e3cdf6cfSChris J Arges 139*e3cdf6cfSChris J Arges ret = bpf_xdp_metadata_rx_hash(ctx, &hash, &rss_type); 140*e3cdf6cfSChris J Arges if (ret < 0) { 141*e3cdf6cfSChris J Arges key = RSS_KEY_ERR_CNT; 142*e3cdf6cfSChris J Arges cnt = bpf_map_lookup_elem(&map_rss, &key); 143*e3cdf6cfSChris J Arges if (cnt) 144*e3cdf6cfSChris J Arges __sync_fetch_and_add(cnt, 1); 145*e3cdf6cfSChris J Arges return XDP_PASS; 146*e3cdf6cfSChris J Arges } 147*e3cdf6cfSChris J Arges 148*e3cdf6cfSChris J Arges key = RSS_KEY_HASH; 149*e3cdf6cfSChris J Arges bpf_map_update_elem(&map_rss, &key, &hash, BPF_ANY); 150*e3cdf6cfSChris J Arges 151*e3cdf6cfSChris J Arges key = RSS_KEY_TYPE; 152*e3cdf6cfSChris J Arges val = (__u32)rss_type; 153*e3cdf6cfSChris J Arges bpf_map_update_elem(&map_rss, &key, &val, BPF_ANY); 154*e3cdf6cfSChris J Arges 155*e3cdf6cfSChris J Arges key = RSS_KEY_PKT_CNT; 156*e3cdf6cfSChris J Arges cnt = bpf_map_lookup_elem(&map_rss, &key); 157*e3cdf6cfSChris J Arges if (cnt) 158*e3cdf6cfSChris J Arges __sync_fetch_and_add(cnt, 1); 159*e3cdf6cfSChris J Arges 160*e3cdf6cfSChris J Arges return XDP_PASS; 161*e3cdf6cfSChris J Arges } 162*e3cdf6cfSChris J Arges 163*e3cdf6cfSChris J Arges char _license[] SEC("license") = "GPL"; 164