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 */
16struct config {
17	__u32 action;
18	int ifindex;
19	__u32 options;
20};
21enum cfg_options_flags {
22	NO_TOUCH = 0x0U,
23	READ_MEM = 0x1U,
24	SWAP_MAC = 0x2U,
25};
26
27struct {
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) */
35struct datarec {
36	__u64 processed;
37	__u64 issue;
38};
39
40struct {
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) */
50struct {
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
57static __always_inline
58void 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
74SEC("xdp_prog0")
75int  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
140char _license[] SEC("license") = "GPL";
141