| /* 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"; |