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