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