Commit | Line | Data |
---|---|---|
e48cfe4b | 1 | // SPDX-License-Identifier: GPL-2.0 |
594a116b KKD |
2 | static const char *__doc__ = |
3 | "XDP multi redirect tool, using BPF_MAP_TYPE_DEVMAP and BPF_F_BROADCAST flag for bpf_redirect_map\n" | |
4 | "Usage: xdp_redirect_map_multi <IFINDEX|IFNAME> <IFINDEX|IFNAME> ... <IFINDEX|IFNAME>\n"; | |
5 | ||
e48cfe4b HL |
6 | #include <linux/bpf.h> |
7 | #include <linux/if_link.h> | |
8 | #include <assert.h> | |
594a116b | 9 | #include <getopt.h> |
e48cfe4b HL |
10 | #include <errno.h> |
11 | #include <signal.h> | |
12 | #include <stdio.h> | |
13 | #include <stdlib.h> | |
14 | #include <string.h> | |
15 | #include <net/if.h> | |
16 | #include <unistd.h> | |
17 | #include <libgen.h> | |
e48cfe4b HL |
18 | #include <sys/ioctl.h> |
19 | #include <sys/types.h> | |
20 | #include <sys/socket.h> | |
21 | #include <netinet/in.h> | |
594a116b | 22 | #include <linux/if_ether.h> |
e48cfe4b HL |
23 | #include <bpf/bpf.h> |
24 | #include <bpf/libbpf.h> | |
594a116b KKD |
25 | #include "bpf_util.h" |
26 | #include "xdp_sample_user.h" | |
27 | #include "xdp_redirect_map_multi.skel.h" | |
e48cfe4b HL |
28 | |
29 | #define MAX_IFACE_NUM 32 | |
e48cfe4b | 30 | static int ifaces[MAX_IFACE_NUM] = {}; |
e48cfe4b | 31 | |
594a116b KKD |
32 | static int mask = SAMPLE_RX_CNT | SAMPLE_REDIRECT_ERR_MAP_CNT | |
33 | SAMPLE_EXCEPTION_CNT | SAMPLE_DEVMAP_XMIT_CNT | | |
34 | SAMPLE_DEVMAP_XMIT_CNT_MULTI | SAMPLE_SKIP_HEADING; | |
e48cfe4b | 35 | |
594a116b | 36 | DEFINE_SAMPLE_INIT(xdp_redirect_map_multi); |
e48cfe4b | 37 | |
594a116b KKD |
38 | static const struct option long_options[] = { |
39 | { "help", no_argument, NULL, 'h' }, | |
40 | { "skb-mode", no_argument, NULL, 'S' }, | |
41 | { "force", no_argument, NULL, 'F' }, | |
42 | { "load-egress", no_argument, NULL, 'X' }, | |
43 | { "stats", no_argument, NULL, 's' }, | |
44 | { "interval", required_argument, NULL, 'i' }, | |
45 | { "verbose", no_argument, NULL, 'v' }, | |
46 | {} | |
47 | }; | |
e48cfe4b | 48 | |
594a116b | 49 | static int update_mac_map(struct bpf_map *map) |
e48cfe4b | 50 | { |
594a116b | 51 | int mac_map_fd = bpf_map__fd(map); |
e48cfe4b HL |
52 | unsigned char mac_addr[6]; |
53 | unsigned int ifindex; | |
594a116b | 54 | int i, ret = -1; |
e48cfe4b HL |
55 | |
56 | for (i = 0; ifaces[i] > 0; i++) { | |
57 | ifindex = ifaces[i]; | |
58 | ||
59 | ret = get_mac_addr(ifindex, mac_addr); | |
60 | if (ret < 0) { | |
594a116b KKD |
61 | fprintf(stderr, "get interface %d mac failed\n", |
62 | ifindex); | |
e48cfe4b HL |
63 | return ret; |
64 | } | |
65 | ||
66 | ret = bpf_map_update_elem(mac_map_fd, &ifindex, mac_addr, 0); | |
594a116b KKD |
67 | if (ret < 0) { |
68 | fprintf(stderr, "Failed to update mac address for ifindex %d\n", | |
69 | ifindex); | |
e48cfe4b HL |
70 | return ret; |
71 | } | |
72 | } | |
73 | ||
74 | return 0; | |
75 | } | |
76 | ||
e48cfe4b HL |
77 | int main(int argc, char **argv) |
78 | { | |
594a116b KKD |
79 | struct bpf_devmap_val devmap_val = {}; |
80 | struct xdp_redirect_map_multi *skel; | |
81 | struct bpf_program *ingress_prog; | |
82 | bool xdp_devmap_attached = false; | |
83 | struct bpf_map *forward_map; | |
84 | int ret = EXIT_FAIL_OPTION; | |
85 | unsigned long interval = 2; | |
e48cfe4b | 86 | char ifname[IF_NAMESIZE]; |
e48cfe4b | 87 | unsigned int ifindex; |
594a116b KKD |
88 | bool generic = false; |
89 | bool force = false; | |
90 | bool tried = false; | |
91 | bool error = true; | |
92 | int i, opt; | |
93 | ||
94 | while ((opt = getopt_long(argc, argv, "hSFXi:vs", | |
95 | long_options, NULL)) != -1) { | |
e48cfe4b HL |
96 | switch (opt) { |
97 | case 'S': | |
594a116b KKD |
98 | generic = true; |
99 | /* devmap_xmit tracepoint not available */ | |
100 | mask &= ~(SAMPLE_DEVMAP_XMIT_CNT | | |
101 | SAMPLE_DEVMAP_XMIT_CNT_MULTI); | |
e48cfe4b HL |
102 | break; |
103 | case 'F': | |
594a116b | 104 | force = true; |
e48cfe4b HL |
105 | break; |
106 | case 'X': | |
594a116b KKD |
107 | xdp_devmap_attached = true; |
108 | break; | |
109 | case 'i': | |
110 | interval = strtoul(optarg, NULL, 0); | |
111 | break; | |
112 | case 'v': | |
113 | sample_switch_mode(); | |
e48cfe4b | 114 | break; |
594a116b KKD |
115 | case 's': |
116 | mask |= SAMPLE_REDIRECT_MAP_CNT; | |
117 | break; | |
118 | case 'h': | |
119 | error = false; | |
e48cfe4b | 120 | default: |
594a116b KKD |
121 | sample_usage(argv, long_options, __doc__, mask, error); |
122 | return ret; | |
e48cfe4b HL |
123 | } |
124 | } | |
125 | ||
594a116b KKD |
126 | if (argc <= optind + 1) { |
127 | sample_usage(argv, long_options, __doc__, mask, error); | |
128 | return ret; | |
e48cfe4b HL |
129 | } |
130 | ||
594a116b KKD |
131 | skel = xdp_redirect_map_multi__open(); |
132 | if (!skel) { | |
133 | fprintf(stderr, "Failed to xdp_redirect_map_multi__open: %s\n", | |
134 | strerror(errno)); | |
135 | ret = EXIT_FAIL_BPF; | |
136 | goto end; | |
e48cfe4b HL |
137 | } |
138 | ||
594a116b KKD |
139 | ret = sample_init_pre_load(skel); |
140 | if (ret < 0) { | |
141 | fprintf(stderr, "Failed to sample_init_pre_load: %s\n", strerror(-ret)); | |
142 | ret = EXIT_FAIL_BPF; | |
143 | goto end_destroy; | |
144 | } | |
145 | ||
146 | ret = EXIT_FAIL_OPTION; | |
e48cfe4b HL |
147 | for (i = 0; i < MAX_IFACE_NUM && argv[optind + i]; i++) { |
148 | ifaces[i] = if_nametoindex(argv[optind + i]); | |
149 | if (!ifaces[i]) | |
150 | ifaces[i] = strtoul(argv[optind + i], NULL, 0); | |
151 | if (!if_indextoname(ifaces[i], ifname)) { | |
594a116b KKD |
152 | fprintf(stderr, "Bad interface index or name\n"); |
153 | sample_usage(argv, long_options, __doc__, mask, true); | |
154 | goto end_destroy; | |
e48cfe4b HL |
155 | } |
156 | ||
594a116b KKD |
157 | skel->rodata->from_match[i] = ifaces[i]; |
158 | skel->rodata->to_match[i] = ifaces[i]; | |
e48cfe4b HL |
159 | } |
160 | ||
594a116b KKD |
161 | ret = xdp_redirect_map_multi__load(skel); |
162 | if (ret < 0) { | |
163 | fprintf(stderr, "Failed to xdp_redirect_map_multi__load: %s\n", | |
164 | strerror(errno)); | |
165 | ret = EXIT_FAIL_BPF; | |
166 | goto end_destroy; | |
e48cfe4b HL |
167 | } |
168 | ||
594a116b | 169 | if (xdp_devmap_attached) { |
e48cfe4b | 170 | /* Update mac_map with all egress interfaces' mac addr */ |
594a116b KKD |
171 | if (update_mac_map(skel->maps.mac_map) < 0) { |
172 | fprintf(stderr, "Updating mac address failed\n"); | |
173 | ret = EXIT_FAIL; | |
174 | goto end_destroy; | |
e48cfe4b | 175 | } |
594a116b | 176 | } |
e48cfe4b | 177 | |
594a116b KKD |
178 | ret = sample_init(skel, mask); |
179 | if (ret < 0) { | |
180 | fprintf(stderr, "Failed to initialize sample: %s\n", strerror(-ret)); | |
181 | ret = EXIT_FAIL; | |
182 | goto end_destroy; | |
e48cfe4b HL |
183 | } |
184 | ||
594a116b KKD |
185 | ingress_prog = skel->progs.xdp_redirect_map_native; |
186 | forward_map = skel->maps.forward_map_native; | |
e48cfe4b | 187 | |
e48cfe4b HL |
188 | for (i = 0; ifaces[i] > 0; i++) { |
189 | ifindex = ifaces[i]; | |
190 | ||
594a116b KKD |
191 | ret = EXIT_FAIL_XDP; |
192 | restart: | |
e48cfe4b | 193 | /* bind prog_fd to each interface */ |
594a116b KKD |
194 | if (sample_install_xdp(ingress_prog, ifindex, generic, force) < 0) { |
195 | if (generic && !tried) { | |
196 | fprintf(stderr, | |
197 | "Trying fallback to sizeof(int) as value_size for devmap in generic mode\n"); | |
198 | ingress_prog = skel->progs.xdp_redirect_map_general; | |
199 | forward_map = skel->maps.forward_map_general; | |
200 | tried = true; | |
201 | goto restart; | |
202 | } | |
203 | goto end_destroy; | |
e48cfe4b HL |
204 | } |
205 | ||
206 | /* Add all the interfaces to forward group and attach | |
594a116b | 207 | * egress devmap program if exist |
e48cfe4b HL |
208 | */ |
209 | devmap_val.ifindex = ifindex; | |
594a116b KKD |
210 | if (xdp_devmap_attached) |
211 | devmap_val.bpf_prog.fd = bpf_program__fd(skel->progs.xdp_devmap_prog); | |
212 | ret = bpf_map_update_elem(bpf_map__fd(forward_map), &ifindex, &devmap_val, 0); | |
213 | if (ret < 0) { | |
214 | fprintf(stderr, "Failed to update devmap value: %s\n", | |
215 | strerror(errno)); | |
216 | ret = EXIT_FAIL_BPF; | |
217 | goto end_destroy; | |
e48cfe4b HL |
218 | } |
219 | } | |
220 | ||
594a116b KKD |
221 | ret = sample_run(interval, NULL, NULL); |
222 | if (ret < 0) { | |
223 | fprintf(stderr, "Failed during sample run: %s\n", strerror(-ret)); | |
224 | ret = EXIT_FAIL; | |
225 | goto end_destroy; | |
226 | } | |
227 | ret = EXIT_OK; | |
228 | end_destroy: | |
229 | xdp_redirect_map_multi__destroy(skel); | |
230 | end: | |
231 | sample_exit(ret); | |
e48cfe4b | 232 | } |