]>
Commit | Line | Data |
---|---|---|
e48cfe4b HL |
1 | // SPDX-License-Identifier: GPL-2.0 |
2 | #include <linux/bpf.h> | |
3 | #include <linux/if_link.h> | |
4 | #include <assert.h> | |
5 | #include <errno.h> | |
6 | #include <signal.h> | |
7 | #include <stdio.h> | |
8 | #include <stdlib.h> | |
9 | #include <string.h> | |
10 | #include <net/if.h> | |
11 | #include <unistd.h> | |
12 | #include <libgen.h> | |
13 | #include <sys/resource.h> | |
14 | #include <sys/ioctl.h> | |
15 | #include <sys/types.h> | |
16 | #include <sys/socket.h> | |
17 | #include <netinet/in.h> | |
18 | ||
19 | #include "bpf_util.h" | |
20 | #include <bpf/bpf.h> | |
21 | #include <bpf/libbpf.h> | |
22 | ||
23 | #define MAX_IFACE_NUM 32 | |
24 | ||
25 | static __u32 xdp_flags = XDP_FLAGS_UPDATE_IF_NOEXIST; | |
26 | static int ifaces[MAX_IFACE_NUM] = {}; | |
27 | static int rxcnt_map_fd; | |
28 | ||
29 | static void int_exit(int sig) | |
30 | { | |
31 | __u32 prog_id = 0; | |
32 | int i; | |
33 | ||
34 | for (i = 0; ifaces[i] > 0; i++) { | |
35 | if (bpf_get_link_xdp_id(ifaces[i], &prog_id, xdp_flags)) { | |
36 | printf("bpf_get_link_xdp_id failed\n"); | |
37 | exit(1); | |
38 | } | |
39 | if (prog_id) | |
40 | bpf_set_link_xdp_fd(ifaces[i], -1, xdp_flags); | |
41 | } | |
42 | ||
43 | exit(0); | |
44 | } | |
45 | ||
46 | static void poll_stats(int interval) | |
47 | { | |
48 | unsigned int nr_cpus = bpf_num_possible_cpus(); | |
49 | __u64 values[nr_cpus], prev[nr_cpus]; | |
50 | ||
51 | memset(prev, 0, sizeof(prev)); | |
52 | ||
53 | while (1) { | |
54 | __u64 sum = 0; | |
55 | __u32 key = 0; | |
56 | int i; | |
57 | ||
58 | sleep(interval); | |
59 | assert(bpf_map_lookup_elem(rxcnt_map_fd, &key, values) == 0); | |
60 | for (i = 0; i < nr_cpus; i++) | |
61 | sum += (values[i] - prev[i]); | |
62 | if (sum) | |
63 | printf("Forwarding %10llu pkt/s\n", sum / interval); | |
64 | memcpy(prev, values, sizeof(values)); | |
65 | } | |
66 | } | |
67 | ||
68 | static int get_mac_addr(unsigned int ifindex, void *mac_addr) | |
69 | { | |
70 | char ifname[IF_NAMESIZE]; | |
71 | struct ifreq ifr; | |
72 | int fd, ret = -1; | |
73 | ||
74 | fd = socket(AF_INET, SOCK_DGRAM, 0); | |
75 | if (fd < 0) | |
76 | return ret; | |
77 | ||
78 | if (!if_indextoname(ifindex, ifname)) | |
79 | goto err_out; | |
80 | ||
81 | strcpy(ifr.ifr_name, ifname); | |
82 | ||
83 | if (ioctl(fd, SIOCGIFHWADDR, &ifr) != 0) | |
84 | goto err_out; | |
85 | ||
86 | memcpy(mac_addr, ifr.ifr_hwaddr.sa_data, 6 * sizeof(char)); | |
87 | ret = 0; | |
88 | ||
89 | err_out: | |
90 | close(fd); | |
91 | return ret; | |
92 | } | |
93 | ||
94 | static int update_mac_map(struct bpf_object *obj) | |
95 | { | |
96 | int i, ret = -1, mac_map_fd; | |
97 | unsigned char mac_addr[6]; | |
98 | unsigned int ifindex; | |
99 | ||
100 | mac_map_fd = bpf_object__find_map_fd_by_name(obj, "mac_map"); | |
101 | if (mac_map_fd < 0) { | |
102 | printf("find mac map fd failed\n"); | |
103 | return ret; | |
104 | } | |
105 | ||
106 | for (i = 0; ifaces[i] > 0; i++) { | |
107 | ifindex = ifaces[i]; | |
108 | ||
109 | ret = get_mac_addr(ifindex, mac_addr); | |
110 | if (ret < 0) { | |
111 | printf("get interface %d mac failed\n", ifindex); | |
112 | return ret; | |
113 | } | |
114 | ||
115 | ret = bpf_map_update_elem(mac_map_fd, &ifindex, mac_addr, 0); | |
116 | if (ret) { | |
117 | perror("bpf_update_elem mac_map_fd"); | |
118 | return ret; | |
119 | } | |
120 | } | |
121 | ||
122 | return 0; | |
123 | } | |
124 | ||
125 | static void usage(const char *prog) | |
126 | { | |
127 | fprintf(stderr, | |
128 | "usage: %s [OPTS] <IFNAME|IFINDEX> <IFNAME|IFINDEX> ...\n" | |
129 | "OPTS:\n" | |
130 | " -S use skb-mode\n" | |
131 | " -N enforce native mode\n" | |
132 | " -F force loading prog\n" | |
133 | " -X load xdp program on egress\n", | |
134 | prog); | |
135 | } | |
136 | ||
137 | int main(int argc, char **argv) | |
138 | { | |
139 | int i, ret, opt, forward_map_fd, max_ifindex = 0; | |
140 | struct bpf_program *ingress_prog, *egress_prog; | |
141 | int ingress_prog_fd, egress_prog_fd = 0; | |
142 | struct bpf_devmap_val devmap_val; | |
143 | bool attach_egress_prog = false; | |
144 | char ifname[IF_NAMESIZE]; | |
145 | struct bpf_map *mac_map; | |
146 | struct bpf_object *obj; | |
147 | unsigned int ifindex; | |
148 | char filename[256]; | |
149 | ||
150 | while ((opt = getopt(argc, argv, "SNFX")) != -1) { | |
151 | switch (opt) { | |
152 | case 'S': | |
153 | xdp_flags |= XDP_FLAGS_SKB_MODE; | |
154 | break; | |
155 | case 'N': | |
156 | /* default, set below */ | |
157 | break; | |
158 | case 'F': | |
159 | xdp_flags &= ~XDP_FLAGS_UPDATE_IF_NOEXIST; | |
160 | break; | |
161 | case 'X': | |
162 | attach_egress_prog = true; | |
163 | break; | |
164 | default: | |
165 | usage(basename(argv[0])); | |
166 | return 1; | |
167 | } | |
168 | } | |
169 | ||
170 | if (!(xdp_flags & XDP_FLAGS_SKB_MODE)) { | |
171 | xdp_flags |= XDP_FLAGS_DRV_MODE; | |
172 | } else if (attach_egress_prog) { | |
173 | printf("Load xdp program on egress with SKB mode not supported yet\n"); | |
174 | return 1; | |
175 | } | |
176 | ||
177 | if (optind == argc) { | |
178 | printf("usage: %s <IFNAME|IFINDEX> <IFNAME|IFINDEX> ...\n", argv[0]); | |
179 | return 1; | |
180 | } | |
181 | ||
182 | printf("Get interfaces"); | |
183 | for (i = 0; i < MAX_IFACE_NUM && argv[optind + i]; i++) { | |
184 | ifaces[i] = if_nametoindex(argv[optind + i]); | |
185 | if (!ifaces[i]) | |
186 | ifaces[i] = strtoul(argv[optind + i], NULL, 0); | |
187 | if (!if_indextoname(ifaces[i], ifname)) { | |
188 | perror("Invalid interface name or i"); | |
189 | return 1; | |
190 | } | |
191 | ||
192 | /* Find the largest index number */ | |
193 | if (ifaces[i] > max_ifindex) | |
194 | max_ifindex = ifaces[i]; | |
195 | ||
196 | printf(" %d", ifaces[i]); | |
197 | } | |
198 | printf("\n"); | |
199 | ||
200 | snprintf(filename, sizeof(filename), "%s_kern.o", argv[0]); | |
201 | ||
202 | obj = bpf_object__open(filename); | |
203 | if (libbpf_get_error(obj)) { | |
204 | printf("ERROR: opening BPF object file failed\n"); | |
205 | obj = NULL; | |
206 | goto err_out; | |
207 | } | |
208 | ||
209 | /* Reset the map size to max ifindex + 1 */ | |
210 | if (attach_egress_prog) { | |
211 | mac_map = bpf_object__find_map_by_name(obj, "mac_map"); | |
212 | ret = bpf_map__resize(mac_map, max_ifindex + 1); | |
213 | if (ret < 0) { | |
214 | printf("ERROR: reset mac map size failed\n"); | |
215 | goto err_out; | |
216 | } | |
217 | } | |
218 | ||
219 | /* load BPF program */ | |
220 | if (bpf_object__load(obj)) { | |
221 | printf("ERROR: loading BPF object file failed\n"); | |
222 | goto err_out; | |
223 | } | |
224 | ||
225 | if (xdp_flags & XDP_FLAGS_SKB_MODE) { | |
226 | ingress_prog = bpf_object__find_program_by_name(obj, "xdp_redirect_map_general"); | |
227 | forward_map_fd = bpf_object__find_map_fd_by_name(obj, "forward_map_general"); | |
228 | } else { | |
229 | ingress_prog = bpf_object__find_program_by_name(obj, "xdp_redirect_map_native"); | |
230 | forward_map_fd = bpf_object__find_map_fd_by_name(obj, "forward_map_native"); | |
231 | } | |
232 | if (!ingress_prog || forward_map_fd < 0) { | |
233 | printf("finding ingress_prog/forward_map in obj file failed\n"); | |
234 | goto err_out; | |
235 | } | |
236 | ||
237 | ingress_prog_fd = bpf_program__fd(ingress_prog); | |
238 | if (ingress_prog_fd < 0) { | |
239 | printf("find ingress_prog fd failed\n"); | |
240 | goto err_out; | |
241 | } | |
242 | ||
243 | rxcnt_map_fd = bpf_object__find_map_fd_by_name(obj, "rxcnt"); | |
244 | if (rxcnt_map_fd < 0) { | |
245 | printf("bpf_object__find_map_fd_by_name failed\n"); | |
246 | goto err_out; | |
247 | } | |
248 | ||
249 | if (attach_egress_prog) { | |
250 | /* Update mac_map with all egress interfaces' mac addr */ | |
251 | if (update_mac_map(obj) < 0) { | |
252 | printf("Error: update mac map failed"); | |
253 | goto err_out; | |
254 | } | |
255 | ||
256 | /* Find egress prog fd */ | |
257 | egress_prog = bpf_object__find_program_by_name(obj, "xdp_devmap_prog"); | |
258 | if (!egress_prog) { | |
259 | printf("finding egress_prog in obj file failed\n"); | |
260 | goto err_out; | |
261 | } | |
262 | egress_prog_fd = bpf_program__fd(egress_prog); | |
263 | if (egress_prog_fd < 0) { | |
264 | printf("find egress_prog fd failed\n"); | |
265 | goto err_out; | |
266 | } | |
267 | } | |
268 | ||
269 | /* Remove attached program when program is interrupted or killed */ | |
270 | signal(SIGINT, int_exit); | |
271 | signal(SIGTERM, int_exit); | |
272 | ||
273 | /* Init forward multicast groups */ | |
274 | for (i = 0; ifaces[i] > 0; i++) { | |
275 | ifindex = ifaces[i]; | |
276 | ||
277 | /* bind prog_fd to each interface */ | |
278 | ret = bpf_set_link_xdp_fd(ifindex, ingress_prog_fd, xdp_flags); | |
279 | if (ret) { | |
280 | printf("Set xdp fd failed on %d\n", ifindex); | |
281 | goto err_out; | |
282 | } | |
283 | ||
284 | /* Add all the interfaces to forward group and attach | |
285 | * egress devmap programe if exist | |
286 | */ | |
287 | devmap_val.ifindex = ifindex; | |
288 | devmap_val.bpf_prog.fd = egress_prog_fd; | |
289 | ret = bpf_map_update_elem(forward_map_fd, &ifindex, &devmap_val, 0); | |
290 | if (ret) { | |
291 | perror("bpf_map_update_elem forward_map"); | |
292 | goto err_out; | |
293 | } | |
294 | } | |
295 | ||
296 | poll_stats(2); | |
297 | ||
298 | return 0; | |
299 | ||
300 | err_out: | |
301 | return 1; | |
302 | } |