1*4882a593Smuzhiyun /* SPDX-License-Identifier: GPL-2.0
2*4882a593Smuzhiyun * Copyright (c) 2017 Jesper Dangaard Brouer, Red Hat Inc.
3*4882a593Smuzhiyun *
4*4882a593Smuzhiyun * Example howto extract XDP RX-queue info
5*4882a593Smuzhiyun */
6*4882a593Smuzhiyun #include <uapi/linux/bpf.h>
7*4882a593Smuzhiyun #include <uapi/linux/if_ether.h>
8*4882a593Smuzhiyun #include <uapi/linux/in.h>
9*4882a593Smuzhiyun #include <bpf/bpf_helpers.h>
10*4882a593Smuzhiyun
11*4882a593Smuzhiyun /* Config setup from with userspace
12*4882a593Smuzhiyun *
13*4882a593Smuzhiyun * User-side setup ifindex in config_map, to verify that
14*4882a593Smuzhiyun * ctx->ingress_ifindex is correct (against configured ifindex)
15*4882a593Smuzhiyun */
16*4882a593Smuzhiyun struct config {
17*4882a593Smuzhiyun __u32 action;
18*4882a593Smuzhiyun int ifindex;
19*4882a593Smuzhiyun __u32 options;
20*4882a593Smuzhiyun };
21*4882a593Smuzhiyun enum cfg_options_flags {
22*4882a593Smuzhiyun NO_TOUCH = 0x0U,
23*4882a593Smuzhiyun READ_MEM = 0x1U,
24*4882a593Smuzhiyun SWAP_MAC = 0x2U,
25*4882a593Smuzhiyun };
26*4882a593Smuzhiyun
27*4882a593Smuzhiyun struct {
28*4882a593Smuzhiyun __uint(type, BPF_MAP_TYPE_ARRAY);
29*4882a593Smuzhiyun __type(key, int);
30*4882a593Smuzhiyun __type(value, struct config);
31*4882a593Smuzhiyun __uint(max_entries, 1);
32*4882a593Smuzhiyun } config_map SEC(".maps");
33*4882a593Smuzhiyun
34*4882a593Smuzhiyun /* Common stats data record (shared with userspace) */
35*4882a593Smuzhiyun struct datarec {
36*4882a593Smuzhiyun __u64 processed;
37*4882a593Smuzhiyun __u64 issue;
38*4882a593Smuzhiyun };
39*4882a593Smuzhiyun
40*4882a593Smuzhiyun struct {
41*4882a593Smuzhiyun __uint(type, BPF_MAP_TYPE_PERCPU_ARRAY);
42*4882a593Smuzhiyun __type(key, u32);
43*4882a593Smuzhiyun __type(value, struct datarec);
44*4882a593Smuzhiyun __uint(max_entries, 1);
45*4882a593Smuzhiyun } stats_global_map SEC(".maps");
46*4882a593Smuzhiyun
47*4882a593Smuzhiyun #define MAX_RXQs 64
48*4882a593Smuzhiyun
49*4882a593Smuzhiyun /* Stats per rx_queue_index (per CPU) */
50*4882a593Smuzhiyun struct {
51*4882a593Smuzhiyun __uint(type, BPF_MAP_TYPE_PERCPU_ARRAY);
52*4882a593Smuzhiyun __type(key, u32);
53*4882a593Smuzhiyun __type(value, struct datarec);
54*4882a593Smuzhiyun __uint(max_entries, MAX_RXQs + 1);
55*4882a593Smuzhiyun } rx_queue_index_map SEC(".maps");
56*4882a593Smuzhiyun
57*4882a593Smuzhiyun static __always_inline
swap_src_dst_mac(void * data)58*4882a593Smuzhiyun void swap_src_dst_mac(void *data)
59*4882a593Smuzhiyun {
60*4882a593Smuzhiyun unsigned short *p = data;
61*4882a593Smuzhiyun unsigned short dst[3];
62*4882a593Smuzhiyun
63*4882a593Smuzhiyun dst[0] = p[0];
64*4882a593Smuzhiyun dst[1] = p[1];
65*4882a593Smuzhiyun dst[2] = p[2];
66*4882a593Smuzhiyun p[0] = p[3];
67*4882a593Smuzhiyun p[1] = p[4];
68*4882a593Smuzhiyun p[2] = p[5];
69*4882a593Smuzhiyun p[3] = dst[0];
70*4882a593Smuzhiyun p[4] = dst[1];
71*4882a593Smuzhiyun p[5] = dst[2];
72*4882a593Smuzhiyun }
73*4882a593Smuzhiyun
74*4882a593Smuzhiyun SEC("xdp_prog0")
xdp_prognum0(struct xdp_md * ctx)75*4882a593Smuzhiyun int xdp_prognum0(struct xdp_md *ctx)
76*4882a593Smuzhiyun {
77*4882a593Smuzhiyun void *data_end = (void *)(long)ctx->data_end;
78*4882a593Smuzhiyun void *data = (void *)(long)ctx->data;
79*4882a593Smuzhiyun struct datarec *rec, *rxq_rec;
80*4882a593Smuzhiyun int ingress_ifindex;
81*4882a593Smuzhiyun struct config *config;
82*4882a593Smuzhiyun u32 key = 0;
83*4882a593Smuzhiyun
84*4882a593Smuzhiyun /* Global stats record */
85*4882a593Smuzhiyun rec = bpf_map_lookup_elem(&stats_global_map, &key);
86*4882a593Smuzhiyun if (!rec)
87*4882a593Smuzhiyun return XDP_ABORTED;
88*4882a593Smuzhiyun rec->processed++;
89*4882a593Smuzhiyun
90*4882a593Smuzhiyun /* Accessing ctx->ingress_ifindex, cause BPF to rewrite BPF
91*4882a593Smuzhiyun * instructions inside kernel to access xdp_rxq->dev->ifindex
92*4882a593Smuzhiyun */
93*4882a593Smuzhiyun ingress_ifindex = ctx->ingress_ifindex;
94*4882a593Smuzhiyun
95*4882a593Smuzhiyun config = bpf_map_lookup_elem(&config_map, &key);
96*4882a593Smuzhiyun if (!config)
97*4882a593Smuzhiyun return XDP_ABORTED;
98*4882a593Smuzhiyun
99*4882a593Smuzhiyun /* Simple test: check ctx provided ifindex is as expected */
100*4882a593Smuzhiyun if (ingress_ifindex != config->ifindex) {
101*4882a593Smuzhiyun /* count this error case */
102*4882a593Smuzhiyun rec->issue++;
103*4882a593Smuzhiyun return XDP_ABORTED;
104*4882a593Smuzhiyun }
105*4882a593Smuzhiyun
106*4882a593Smuzhiyun /* Update stats per rx_queue_index. Handle if rx_queue_index
107*4882a593Smuzhiyun * is larger than stats map can contain info for.
108*4882a593Smuzhiyun */
109*4882a593Smuzhiyun key = ctx->rx_queue_index;
110*4882a593Smuzhiyun if (key >= MAX_RXQs)
111*4882a593Smuzhiyun key = MAX_RXQs;
112*4882a593Smuzhiyun rxq_rec = bpf_map_lookup_elem(&rx_queue_index_map, &key);
113*4882a593Smuzhiyun if (!rxq_rec)
114*4882a593Smuzhiyun return XDP_ABORTED;
115*4882a593Smuzhiyun rxq_rec->processed++;
116*4882a593Smuzhiyun if (key == MAX_RXQs)
117*4882a593Smuzhiyun rxq_rec->issue++;
118*4882a593Smuzhiyun
119*4882a593Smuzhiyun /* Default: Don't touch packet data, only count packets */
120*4882a593Smuzhiyun if (unlikely(config->options & (READ_MEM|SWAP_MAC))) {
121*4882a593Smuzhiyun struct ethhdr *eth = data;
122*4882a593Smuzhiyun
123*4882a593Smuzhiyun if (eth + 1 > data_end)
124*4882a593Smuzhiyun return XDP_ABORTED;
125*4882a593Smuzhiyun
126*4882a593Smuzhiyun /* Avoid compiler removing this: Drop non 802.3 Ethertypes */
127*4882a593Smuzhiyun if (ntohs(eth->h_proto) < ETH_P_802_3_MIN)
128*4882a593Smuzhiyun return XDP_ABORTED;
129*4882a593Smuzhiyun
130*4882a593Smuzhiyun /* XDP_TX requires changing MAC-addrs, else HW may drop.
131*4882a593Smuzhiyun * Can also be enabled with --swapmac (for test purposes)
132*4882a593Smuzhiyun */
133*4882a593Smuzhiyun if (unlikely(config->options & SWAP_MAC))
134*4882a593Smuzhiyun swap_src_dst_mac(data);
135*4882a593Smuzhiyun }
136*4882a593Smuzhiyun
137*4882a593Smuzhiyun return config->action;
138*4882a593Smuzhiyun }
139*4882a593Smuzhiyun
140*4882a593Smuzhiyun char _license[] SEC("license") = "GPL";
141