|  | /* SPDX-License-Identifier: GPL-2.0 | 
|  | * Copyright (c) 2017 Jesper Dangaard Brouer, Red Hat Inc. | 
|  | * | 
|  | *  Example howto extract XDP RX-queue info | 
|  | */ | 
|  | #include <uapi/linux/bpf.h> | 
|  | #include <uapi/linux/if_ether.h> | 
|  | #include <uapi/linux/in.h> | 
|  | #include "bpf_helpers.h" | 
|  |  | 
|  | /* Config setup from with userspace | 
|  | * | 
|  | * User-side setup ifindex in config_map, to verify that | 
|  | * ctx->ingress_ifindex is correct (against configured ifindex) | 
|  | */ | 
|  | struct config { | 
|  | __u32 action; | 
|  | int ifindex; | 
|  | __u32 options; | 
|  | }; | 
|  | enum cfg_options_flags { | 
|  | NO_TOUCH = 0x0U, | 
|  | READ_MEM = 0x1U, | 
|  | SWAP_MAC = 0x2U, | 
|  | }; | 
|  | struct bpf_map_def SEC("maps") config_map = { | 
|  | .type		= BPF_MAP_TYPE_ARRAY, | 
|  | .key_size	= sizeof(int), | 
|  | .value_size	= sizeof(struct config), | 
|  | .max_entries	= 1, | 
|  | }; | 
|  |  | 
|  | /* Common stats data record (shared with userspace) */ | 
|  | struct datarec { | 
|  | __u64 processed; | 
|  | __u64 issue; | 
|  | }; | 
|  |  | 
|  | struct bpf_map_def SEC("maps") stats_global_map = { | 
|  | .type		= BPF_MAP_TYPE_PERCPU_ARRAY, | 
|  | .key_size	= sizeof(u32), | 
|  | .value_size	= sizeof(struct datarec), | 
|  | .max_entries	= 1, | 
|  | }; | 
|  |  | 
|  | #define MAX_RXQs 64 | 
|  |  | 
|  | /* Stats per rx_queue_index (per CPU) */ | 
|  | struct bpf_map_def SEC("maps") rx_queue_index_map = { | 
|  | .type		= BPF_MAP_TYPE_PERCPU_ARRAY, | 
|  | .key_size	= sizeof(u32), | 
|  | .value_size	= sizeof(struct datarec), | 
|  | .max_entries	= MAX_RXQs + 1, | 
|  | }; | 
|  |  | 
|  | static __always_inline | 
|  | void swap_src_dst_mac(void *data) | 
|  | { | 
|  | unsigned short *p = data; | 
|  | unsigned short dst[3]; | 
|  |  | 
|  | dst[0] = p[0]; | 
|  | dst[1] = p[1]; | 
|  | dst[2] = p[2]; | 
|  | p[0] = p[3]; | 
|  | p[1] = p[4]; | 
|  | p[2] = p[5]; | 
|  | p[3] = dst[0]; | 
|  | p[4] = dst[1]; | 
|  | p[5] = dst[2]; | 
|  | } | 
|  |  | 
|  | SEC("xdp_prog0") | 
|  | int  xdp_prognum0(struct xdp_md *ctx) | 
|  | { | 
|  | void *data_end = (void *)(long)ctx->data_end; | 
|  | void *data     = (void *)(long)ctx->data; | 
|  | struct datarec *rec, *rxq_rec; | 
|  | int ingress_ifindex; | 
|  | struct config *config; | 
|  | u32 key = 0; | 
|  |  | 
|  | /* Global stats record */ | 
|  | rec = bpf_map_lookup_elem(&stats_global_map, &key); | 
|  | if (!rec) | 
|  | return XDP_ABORTED; | 
|  | rec->processed++; | 
|  |  | 
|  | /* Accessing ctx->ingress_ifindex, cause BPF to rewrite BPF | 
|  | * instructions inside kernel to access xdp_rxq->dev->ifindex | 
|  | */ | 
|  | ingress_ifindex = ctx->ingress_ifindex; | 
|  |  | 
|  | config = bpf_map_lookup_elem(&config_map, &key); | 
|  | if (!config) | 
|  | return XDP_ABORTED; | 
|  |  | 
|  | /* Simple test: check ctx provided ifindex is as expected */ | 
|  | if (ingress_ifindex != config->ifindex) { | 
|  | /* count this error case */ | 
|  | rec->issue++; | 
|  | return XDP_ABORTED; | 
|  | } | 
|  |  | 
|  | /* Update stats per rx_queue_index. Handle if rx_queue_index | 
|  | * is larger than stats map can contain info for. | 
|  | */ | 
|  | key = ctx->rx_queue_index; | 
|  | if (key >= MAX_RXQs) | 
|  | key = MAX_RXQs; | 
|  | rxq_rec = bpf_map_lookup_elem(&rx_queue_index_map, &key); | 
|  | if (!rxq_rec) | 
|  | return XDP_ABORTED; | 
|  | rxq_rec->processed++; | 
|  | if (key == MAX_RXQs) | 
|  | rxq_rec->issue++; | 
|  |  | 
|  | /* Default: Don't touch packet data, only count packets */ | 
|  | if (unlikely(config->options & (READ_MEM|SWAP_MAC))) { | 
|  | struct ethhdr *eth = data; | 
|  |  | 
|  | if (eth + 1 > data_end) | 
|  | return XDP_ABORTED; | 
|  |  | 
|  | /* Avoid compiler removing this: Drop non 802.3 Ethertypes */ | 
|  | if (ntohs(eth->h_proto) < ETH_P_802_3_MIN) | 
|  | return XDP_ABORTED; | 
|  |  | 
|  | /* XDP_TX requires changing MAC-addrs, else HW may drop. | 
|  | * Can also be enabled with --swapmac (for test purposes) | 
|  | */ | 
|  | if (unlikely(config->options & SWAP_MAC)) | 
|  | swap_src_dst_mac(data); | 
|  | } | 
|  |  | 
|  | return config->action; | 
|  | } | 
|  |  | 
|  | char _license[] SEC("license") = "GPL"; |