162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci 362306a36Sopenharmony_ci#include <stdbool.h> 462306a36Sopenharmony_ci#include <linux/bpf.h> 562306a36Sopenharmony_ci#include <linux/netdev.h> 662306a36Sopenharmony_ci#include <bpf/bpf_helpers.h> 762306a36Sopenharmony_ci#include <bpf/bpf_endian.h> 862306a36Sopenharmony_ci#include <bpf/bpf_tracing.h> 962306a36Sopenharmony_ci#include <linux/if_ether.h> 1062306a36Sopenharmony_ci#include <linux/ip.h> 1162306a36Sopenharmony_ci#include <linux/ipv6.h> 1262306a36Sopenharmony_ci#include <linux/in.h> 1362306a36Sopenharmony_ci#include <linux/in6.h> 1462306a36Sopenharmony_ci#include <linux/udp.h> 1562306a36Sopenharmony_ci#include <asm-generic/errno-base.h> 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci#include "xdp_features.h" 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci#define ipv6_addr_equal(a, b) ((a).s6_addr32[0] == (b).s6_addr32[0] && \ 2062306a36Sopenharmony_ci (a).s6_addr32[1] == (b).s6_addr32[1] && \ 2162306a36Sopenharmony_ci (a).s6_addr32[2] == (b).s6_addr32[2] && \ 2262306a36Sopenharmony_ci (a).s6_addr32[3] == (b).s6_addr32[3]) 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_cistruct net_device; 2562306a36Sopenharmony_cistruct bpf_prog; 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_cistruct xdp_cpumap_stats { 2862306a36Sopenharmony_ci unsigned int redirect; 2962306a36Sopenharmony_ci unsigned int pass; 3062306a36Sopenharmony_ci unsigned int drop; 3162306a36Sopenharmony_ci}; 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_cistruct { 3462306a36Sopenharmony_ci __uint(type, BPF_MAP_TYPE_ARRAY); 3562306a36Sopenharmony_ci __type(key, __u32); 3662306a36Sopenharmony_ci __type(value, __u32); 3762306a36Sopenharmony_ci __uint(max_entries, 1); 3862306a36Sopenharmony_ci} stats SEC(".maps"); 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_cistruct { 4162306a36Sopenharmony_ci __uint(type, BPF_MAP_TYPE_ARRAY); 4262306a36Sopenharmony_ci __type(key, __u32); 4362306a36Sopenharmony_ci __type(value, __u32); 4462306a36Sopenharmony_ci __uint(max_entries, 1); 4562306a36Sopenharmony_ci} dut_stats SEC(".maps"); 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_cistruct { 4862306a36Sopenharmony_ci __uint(type, BPF_MAP_TYPE_CPUMAP); 4962306a36Sopenharmony_ci __uint(key_size, sizeof(__u32)); 5062306a36Sopenharmony_ci __uint(value_size, sizeof(struct bpf_cpumap_val)); 5162306a36Sopenharmony_ci __uint(max_entries, 1); 5262306a36Sopenharmony_ci} cpu_map SEC(".maps"); 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_cistruct { 5562306a36Sopenharmony_ci __uint(type, BPF_MAP_TYPE_DEVMAP); 5662306a36Sopenharmony_ci __uint(key_size, sizeof(__u32)); 5762306a36Sopenharmony_ci __uint(value_size, sizeof(struct bpf_devmap_val)); 5862306a36Sopenharmony_ci __uint(max_entries, 1); 5962306a36Sopenharmony_ci} dev_map SEC(".maps"); 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ciconst volatile struct in6_addr tester_addr; 6262306a36Sopenharmony_ciconst volatile struct in6_addr dut_addr; 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_cistatic __always_inline int 6562306a36Sopenharmony_cixdp_process_echo_packet(struct xdp_md *xdp, bool dut) 6662306a36Sopenharmony_ci{ 6762306a36Sopenharmony_ci void *data_end = (void *)(long)xdp->data_end; 6862306a36Sopenharmony_ci void *data = (void *)(long)xdp->data; 6962306a36Sopenharmony_ci struct ethhdr *eh = data; 7062306a36Sopenharmony_ci struct tlv_hdr *tlv; 7162306a36Sopenharmony_ci struct udphdr *uh; 7262306a36Sopenharmony_ci __be16 port; 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci if (eh + 1 > (struct ethhdr *)data_end) 7562306a36Sopenharmony_ci return -EINVAL; 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci if (eh->h_proto == bpf_htons(ETH_P_IP)) { 7862306a36Sopenharmony_ci struct iphdr *ih = (struct iphdr *)(eh + 1); 7962306a36Sopenharmony_ci __be32 saddr = dut ? tester_addr.s6_addr32[3] 8062306a36Sopenharmony_ci : dut_addr.s6_addr32[3]; 8162306a36Sopenharmony_ci __be32 daddr = dut ? dut_addr.s6_addr32[3] 8262306a36Sopenharmony_ci : tester_addr.s6_addr32[3]; 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci ih = (struct iphdr *)(eh + 1); 8562306a36Sopenharmony_ci if (ih + 1 > (struct iphdr *)data_end) 8662306a36Sopenharmony_ci return -EINVAL; 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci if (saddr != ih->saddr) 8962306a36Sopenharmony_ci return -EINVAL; 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci if (daddr != ih->daddr) 9262306a36Sopenharmony_ci return -EINVAL; 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci if (ih->protocol != IPPROTO_UDP) 9562306a36Sopenharmony_ci return -EINVAL; 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci uh = (struct udphdr *)(ih + 1); 9862306a36Sopenharmony_ci } else if (eh->h_proto == bpf_htons(ETH_P_IPV6)) { 9962306a36Sopenharmony_ci struct in6_addr saddr = dut ? tester_addr : dut_addr; 10062306a36Sopenharmony_ci struct in6_addr daddr = dut ? dut_addr : tester_addr; 10162306a36Sopenharmony_ci struct ipv6hdr *ih6 = (struct ipv6hdr *)(eh + 1); 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci if (ih6 + 1 > (struct ipv6hdr *)data_end) 10462306a36Sopenharmony_ci return -EINVAL; 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci if (!ipv6_addr_equal(saddr, ih6->saddr)) 10762306a36Sopenharmony_ci return -EINVAL; 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci if (!ipv6_addr_equal(daddr, ih6->daddr)) 11062306a36Sopenharmony_ci return -EINVAL; 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci if (ih6->nexthdr != IPPROTO_UDP) 11362306a36Sopenharmony_ci return -EINVAL; 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci uh = (struct udphdr *)(ih6 + 1); 11662306a36Sopenharmony_ci } else { 11762306a36Sopenharmony_ci return -EINVAL; 11862306a36Sopenharmony_ci } 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci if (uh + 1 > (struct udphdr *)data_end) 12162306a36Sopenharmony_ci return -EINVAL; 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci port = dut ? uh->dest : uh->source; 12462306a36Sopenharmony_ci if (port != bpf_htons(DUT_ECHO_PORT)) 12562306a36Sopenharmony_ci return -EINVAL; 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci tlv = (struct tlv_hdr *)(uh + 1); 12862306a36Sopenharmony_ci if (tlv + 1 > data_end) 12962306a36Sopenharmony_ci return -EINVAL; 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci return bpf_htons(tlv->type) == CMD_ECHO ? 0 : -EINVAL; 13262306a36Sopenharmony_ci} 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_cistatic __always_inline int 13562306a36Sopenharmony_cixdp_update_stats(struct xdp_md *xdp, bool tx, bool dut) 13662306a36Sopenharmony_ci{ 13762306a36Sopenharmony_ci __u32 *val, key = 0; 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci if (xdp_process_echo_packet(xdp, tx)) 14062306a36Sopenharmony_ci return -EINVAL; 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci if (dut) 14362306a36Sopenharmony_ci val = bpf_map_lookup_elem(&dut_stats, &key); 14462306a36Sopenharmony_ci else 14562306a36Sopenharmony_ci val = bpf_map_lookup_elem(&stats, &key); 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci if (val) 14862306a36Sopenharmony_ci __sync_add_and_fetch(val, 1); 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci return 0; 15162306a36Sopenharmony_ci} 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci/* Tester */ 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ciSEC("xdp") 15662306a36Sopenharmony_ciint xdp_tester_check_tx(struct xdp_md *xdp) 15762306a36Sopenharmony_ci{ 15862306a36Sopenharmony_ci xdp_update_stats(xdp, true, false); 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci return XDP_PASS; 16162306a36Sopenharmony_ci} 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ciSEC("xdp") 16462306a36Sopenharmony_ciint xdp_tester_check_rx(struct xdp_md *xdp) 16562306a36Sopenharmony_ci{ 16662306a36Sopenharmony_ci xdp_update_stats(xdp, false, false); 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci return XDP_PASS; 16962306a36Sopenharmony_ci} 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci/* DUT */ 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ciSEC("xdp") 17462306a36Sopenharmony_ciint xdp_do_pass(struct xdp_md *xdp) 17562306a36Sopenharmony_ci{ 17662306a36Sopenharmony_ci xdp_update_stats(xdp, true, true); 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci return XDP_PASS; 17962306a36Sopenharmony_ci} 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ciSEC("xdp") 18262306a36Sopenharmony_ciint xdp_do_drop(struct xdp_md *xdp) 18362306a36Sopenharmony_ci{ 18462306a36Sopenharmony_ci if (xdp_update_stats(xdp, true, true)) 18562306a36Sopenharmony_ci return XDP_PASS; 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci return XDP_DROP; 18862306a36Sopenharmony_ci} 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ciSEC("xdp") 19162306a36Sopenharmony_ciint xdp_do_aborted(struct xdp_md *xdp) 19262306a36Sopenharmony_ci{ 19362306a36Sopenharmony_ci if (xdp_process_echo_packet(xdp, true)) 19462306a36Sopenharmony_ci return XDP_PASS; 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci return XDP_ABORTED; 19762306a36Sopenharmony_ci} 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ciSEC("xdp") 20062306a36Sopenharmony_ciint xdp_do_tx(struct xdp_md *xdp) 20162306a36Sopenharmony_ci{ 20262306a36Sopenharmony_ci void *data = (void *)(long)xdp->data; 20362306a36Sopenharmony_ci struct ethhdr *eh = data; 20462306a36Sopenharmony_ci __u8 tmp_mac[ETH_ALEN]; 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci if (xdp_update_stats(xdp, true, true)) 20762306a36Sopenharmony_ci return XDP_PASS; 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci __builtin_memcpy(tmp_mac, eh->h_source, ETH_ALEN); 21062306a36Sopenharmony_ci __builtin_memcpy(eh->h_source, eh->h_dest, ETH_ALEN); 21162306a36Sopenharmony_ci __builtin_memcpy(eh->h_dest, tmp_mac, ETH_ALEN); 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci return XDP_TX; 21462306a36Sopenharmony_ci} 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ciSEC("xdp") 21762306a36Sopenharmony_ciint xdp_do_redirect(struct xdp_md *xdp) 21862306a36Sopenharmony_ci{ 21962306a36Sopenharmony_ci if (xdp_process_echo_packet(xdp, true)) 22062306a36Sopenharmony_ci return XDP_PASS; 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci return bpf_redirect_map(&cpu_map, 0, 0); 22362306a36Sopenharmony_ci} 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ciSEC("tp_btf/xdp_exception") 22662306a36Sopenharmony_ciint BPF_PROG(xdp_exception, const struct net_device *dev, 22762306a36Sopenharmony_ci const struct bpf_prog *xdp, __u32 act) 22862306a36Sopenharmony_ci{ 22962306a36Sopenharmony_ci __u32 *val, key = 0; 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci val = bpf_map_lookup_elem(&dut_stats, &key); 23262306a36Sopenharmony_ci if (val) 23362306a36Sopenharmony_ci __sync_add_and_fetch(val, 1); 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci return 0; 23662306a36Sopenharmony_ci} 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ciSEC("tp_btf/xdp_cpumap_kthread") 23962306a36Sopenharmony_ciint BPF_PROG(tp_xdp_cpumap_kthread, int map_id, unsigned int processed, 24062306a36Sopenharmony_ci unsigned int drops, int sched, struct xdp_cpumap_stats *xdp_stats) 24162306a36Sopenharmony_ci{ 24262306a36Sopenharmony_ci __u32 *val, key = 0; 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci val = bpf_map_lookup_elem(&dut_stats, &key); 24562306a36Sopenharmony_ci if (val) 24662306a36Sopenharmony_ci __sync_add_and_fetch(val, 1); 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci return 0; 24962306a36Sopenharmony_ci} 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ciSEC("xdp/cpumap") 25262306a36Sopenharmony_ciint xdp_do_redirect_cpumap(struct xdp_md *xdp) 25362306a36Sopenharmony_ci{ 25462306a36Sopenharmony_ci void *data = (void *)(long)xdp->data; 25562306a36Sopenharmony_ci struct ethhdr *eh = data; 25662306a36Sopenharmony_ci __u8 tmp_mac[ETH_ALEN]; 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci if (xdp_process_echo_packet(xdp, true)) 25962306a36Sopenharmony_ci return XDP_PASS; 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci __builtin_memcpy(tmp_mac, eh->h_source, ETH_ALEN); 26262306a36Sopenharmony_ci __builtin_memcpy(eh->h_source, eh->h_dest, ETH_ALEN); 26362306a36Sopenharmony_ci __builtin_memcpy(eh->h_dest, tmp_mac, ETH_ALEN); 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci return bpf_redirect_map(&dev_map, 0, 0); 26662306a36Sopenharmony_ci} 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_cichar _license[] SEC("license") = "GPL"; 269