Merge tag 'for-linus-5.14-ofs1' of git://git.kernel.org/pub/scm/linux/kernel/git...
[linux-2.6-microblaze.git] / samples / bpf / xdp_redirect_map_multi_user.c
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 }