51c8224b4ccc3d35ecc925c4a75362bd0cfa2237
[linux-2.6-microblaze.git] / tools / testing / selftests / bpf / xdp_redirect_multi.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 #define MAX_INDEX_NUM 1024
25
26 static __u32 xdp_flags = XDP_FLAGS_UPDATE_IF_NOEXIST;
27 static int ifaces[MAX_IFACE_NUM] = {};
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 int get_mac_addr(unsigned int ifindex, void *mac_addr)
47 {
48         char ifname[IF_NAMESIZE];
49         struct ifreq ifr;
50         int fd, ret = -1;
51
52         fd = socket(AF_INET, SOCK_DGRAM, 0);
53         if (fd < 0)
54                 return ret;
55
56         if (!if_indextoname(ifindex, ifname))
57                 goto err_out;
58
59         strcpy(ifr.ifr_name, ifname);
60
61         if (ioctl(fd, SIOCGIFHWADDR, &ifr) != 0)
62                 goto err_out;
63
64         memcpy(mac_addr, ifr.ifr_hwaddr.sa_data, 6 * sizeof(char));
65         ret = 0;
66
67 err_out:
68         close(fd);
69         return ret;
70 }
71
72 static void usage(const char *prog)
73 {
74         fprintf(stderr,
75                 "usage: %s [OPTS] <IFNAME|IFINDEX> <IFNAME|IFINDEX> ...\n"
76                 "OPTS:\n"
77                 "    -S    use skb-mode\n"
78                 "    -N    enforce native mode\n"
79                 "    -F    force loading prog\n"
80                 "    -X    load xdp program on egress\n",
81                 prog);
82 }
83
84 int main(int argc, char **argv)
85 {
86         int prog_fd, group_all, mac_map;
87         struct bpf_program *ingress_prog, *egress_prog;
88         int i, err, ret, opt, egress_prog_fd = 0;
89         struct bpf_devmap_val devmap_val;
90         bool attach_egress_prog = false;
91         unsigned char mac_addr[6];
92         char ifname[IF_NAMESIZE];
93         struct bpf_object *obj;
94         unsigned int ifindex;
95         char filename[256];
96
97         while ((opt = getopt(argc, argv, "SNFX")) != -1) {
98                 switch (opt) {
99                 case 'S':
100                         xdp_flags |= XDP_FLAGS_SKB_MODE;
101                         break;
102                 case 'N':
103                         /* default, set below */
104                         break;
105                 case 'F':
106                         xdp_flags &= ~XDP_FLAGS_UPDATE_IF_NOEXIST;
107                         break;
108                 case 'X':
109                         attach_egress_prog = true;
110                         break;
111                 default:
112                         usage(basename(argv[0]));
113                         return 1;
114                 }
115         }
116
117         if (!(xdp_flags & XDP_FLAGS_SKB_MODE)) {
118                 xdp_flags |= XDP_FLAGS_DRV_MODE;
119         } else if (attach_egress_prog) {
120                 printf("Load xdp program on egress with SKB mode not supported yet\n");
121                 goto err_out;
122         }
123
124         if (optind == argc) {
125                 printf("usage: %s <IFNAME|IFINDEX> <IFNAME|IFINDEX> ...\n", argv[0]);
126                 goto err_out;
127         }
128
129         printf("Get interfaces:");
130         for (i = 0; i < MAX_IFACE_NUM && argv[optind + i]; i++) {
131                 ifaces[i] = if_nametoindex(argv[optind + i]);
132                 if (!ifaces[i])
133                         ifaces[i] = strtoul(argv[optind + i], NULL, 0);
134                 if (!if_indextoname(ifaces[i], ifname)) {
135                         perror("Invalid interface name or i");
136                         goto err_out;
137                 }
138                 if (ifaces[i] > MAX_INDEX_NUM) {
139                         printf(" interface index too large\n");
140                         goto err_out;
141                 }
142                 printf(" %d", ifaces[i]);
143         }
144         printf("\n");
145
146         snprintf(filename, sizeof(filename), "%s_kern.o", argv[0]);
147         obj = bpf_object__open_file(filename, NULL);
148         err = libbpf_get_error(obj);
149         if (err)
150                 goto err_out;
151         err = bpf_object__load(obj);
152         if (err)
153                 goto err_out;
154         prog_fd = bpf_program__fd(bpf_object__next_program(obj, NULL));
155
156         if (attach_egress_prog)
157                 group_all = bpf_object__find_map_fd_by_name(obj, "map_egress");
158         else
159                 group_all = bpf_object__find_map_fd_by_name(obj, "map_all");
160         mac_map = bpf_object__find_map_fd_by_name(obj, "mac_map");
161
162         if (group_all < 0 || mac_map < 0) {
163                 printf("bpf_object__find_map_fd_by_name failed\n");
164                 goto err_out;
165         }
166
167         if (attach_egress_prog) {
168                 /* Find ingress/egress prog for 2nd xdp prog */
169                 ingress_prog = bpf_object__find_program_by_name(obj, "xdp_redirect_map_all_prog");
170                 egress_prog = bpf_object__find_program_by_name(obj, "xdp_devmap_prog");
171                 if (!ingress_prog || !egress_prog) {
172                         printf("finding ingress/egress_prog in obj file failed\n");
173                         goto err_out;
174                 }
175                 prog_fd = bpf_program__fd(ingress_prog);
176                 egress_prog_fd = bpf_program__fd(egress_prog);
177                 if (prog_fd < 0 || egress_prog_fd < 0) {
178                         printf("find egress_prog fd failed\n");
179                         goto err_out;
180                 }
181         }
182
183         signal(SIGINT, int_exit);
184         signal(SIGTERM, int_exit);
185
186         /* Init forward multicast groups and exclude group */
187         for (i = 0; ifaces[i] > 0; i++) {
188                 ifindex = ifaces[i];
189
190                 if (attach_egress_prog) {
191                         ret = get_mac_addr(ifindex, mac_addr);
192                         if (ret < 0) {
193                                 printf("get interface %d mac failed\n", ifindex);
194                                 goto err_out;
195                         }
196                         ret = bpf_map_update_elem(mac_map, &ifindex, mac_addr, 0);
197                         if (ret) {
198                                 perror("bpf_update_elem mac_map failed\n");
199                                 goto err_out;
200                         }
201                 }
202
203                 /* Add all the interfaces to group all */
204                 devmap_val.ifindex = ifindex;
205                 devmap_val.bpf_prog.fd = egress_prog_fd;
206                 ret = bpf_map_update_elem(group_all, &ifindex, &devmap_val, 0);
207                 if (ret) {
208                         perror("bpf_map_update_elem");
209                         goto err_out;
210                 }
211
212                 /* bind prog_fd to each interface */
213                 ret = bpf_set_link_xdp_fd(ifindex, prog_fd, xdp_flags);
214                 if (ret) {
215                         printf("Set xdp fd failed on %d\n", ifindex);
216                         goto err_out;
217                 }
218         }
219
220         /* sleep some time for testing */
221         sleep(999);
222
223         return 0;
224
225 err_out:
226         return 1;
227 }