1
2
3
4
5
6
7#include <stdio.h>
8#include <stdlib.h>
9#include <unistd.h>
10#include <string.h>
11#include <fcntl.h>
12#include <sys/socket.h>
13#include <sys/time.h>
14#include <net/if.h>
15#include <netinet/in.h>
16#include <linux/if_link.h>
17#include <linux/if_bridge.h>
18#include <linux/if_ether.h>
19
20#include "json_print.h"
21#include "libnetlink.h"
22#include "br_common.h"
23#include "utils.h"
24
25static unsigned int filter_index;
26
27
28#define VXLAN_ID_LEN 17
29
30static void usage(void)
31{
32 fprintf(stderr,
33 "Usage: bridge vni { add | del } vni VNI\n"
34 " [ { group | remote } IP_ADDRESS ]\n"
35 " dev DEV\n"
36 " bridge vni { show } [ dev DEV ]\n"
37 "\n"
38 "Where: VNI := 0-16777215\n"
39 );
40 exit(-1);
41}
42
43static int parse_vni_filter(const char *argv, struct nlmsghdr *n, int reqsize,
44 inet_prefix *group)
45{
46 char *vnilist = strdupa(argv);
47 char *vni = strtok(vnilist, ",");
48 int group_type = AF_UNSPEC;
49 struct rtattr *nlvlist_e;
50 char *v;
51
52 if (group && is_addrtype_inet(group))
53 group_type = (group->family == AF_INET) ? VXLAN_VNIFILTER_ENTRY_GROUP :
54 VXLAN_VNIFILTER_ENTRY_GROUP6;
55
56 while (vni != NULL) {
57 __u32 vni_start = 0, vni_end = 0;
58
59 v = strchr(vni, '-');
60 if (v) {
61 *v = '\0';
62 v++;
63 vni_start = atoi(vni);
64 vni_end = atoi(v);
65 } else {
66 vni_start = atoi(vni);
67 }
68 nlvlist_e = addattr_nest(n, reqsize, VXLAN_VNIFILTER_ENTRY |
69 NLA_F_NESTED);
70 addattr32(n, 1024, VXLAN_VNIFILTER_ENTRY_START, vni_start);
71 if (vni_end)
72 addattr32(n, 1024, VXLAN_VNIFILTER_ENTRY_END, vni_end);
73 if (group)
74 addattr_l(n, 1024, group_type, group->data, group->bytelen);
75 addattr_nest_end(n, nlvlist_e);
76 vni = strtok(NULL, ",");
77 }
78
79 return 0;
80}
81
82static int vni_modify(int cmd, int argc, char **argv)
83{
84 struct {
85 struct nlmsghdr n;
86 struct tunnel_msg tmsg;
87 char buf[1024];
88 } req = {
89 .n.nlmsg_len = NLMSG_LENGTH(sizeof(struct tunnel_msg)),
90 .n.nlmsg_flags = NLM_F_REQUEST,
91 .n.nlmsg_type = cmd,
92 .tmsg.family = PF_BRIDGE,
93 };
94 bool daddr_present = false;
95 inet_prefix daddr;
96 char *vni = NULL;
97 char *d = NULL;
98
99 while (argc > 0) {
100 if (strcmp(*argv, "dev") == 0) {
101 NEXT_ARG();
102 d = *argv;
103 } else if (strcmp(*argv, "vni") == 0) {
104 NEXT_ARG();
105 if (vni)
106 duparg("vni", *argv);
107 vni = *argv;
108 } else if (strcmp(*argv, "group") == 0) {
109 NEXT_ARG();
110 if (daddr_present)
111 duparg("destination", *argv);
112 get_addr(&daddr, *argv, AF_UNSPEC);
113 if (!is_addrtype_inet_multi(&daddr))
114 invarg("invalid group address", *argv);
115 daddr_present = true;
116 } else if (strcmp(*argv, "remote") == 0) {
117 NEXT_ARG();
118 if (daddr_present)
119 duparg("destination", *argv);
120 get_addr(&daddr, *argv, AF_UNSPEC);
121 daddr_present = true;
122 } else {
123 if (strcmp(*argv, "help") == 0)
124 usage();
125 }
126 argc--; argv++;
127 }
128
129 if (d == NULL || vni == NULL) {
130 fprintf(stderr, "Device and VNI ID are required arguments.\n");
131 return -1;
132 }
133
134 parse_vni_filter(vni, &req.n, sizeof(req),
135 (daddr_present ? &daddr : NULL));
136
137 req.tmsg.ifindex = ll_name_to_index(d);
138 if (req.tmsg.ifindex == 0) {
139 fprintf(stderr, "Cannot find vxlan device \"%s\"\n", d);
140 return -1;
141 }
142
143 if (rtnl_talk(&rth, &req.n, NULL) < 0)
144 return -1;
145
146 return 0;
147}
148
149static void open_vni_port(int ifi_index)
150{
151 open_json_object(NULL);
152 print_color_string(PRINT_ANY, COLOR_IFNAME, "ifname",
153 "%-" textify(IFNAMSIZ) "s ",
154 ll_index_to_name(ifi_index));
155 open_json_array(PRINT_JSON, "vnis");
156}
157
158static void close_vni_port(void)
159{
160 close_json_array(PRINT_JSON, NULL);
161 close_json_object();
162}
163
164static void print_vnifilter_entry_stats(struct rtattr *stats_attr)
165{
166 struct rtattr *stb[VNIFILTER_ENTRY_STATS_MAX+1];
167 __u64 stat;
168
169 open_json_object("stats");
170 parse_rtattr_flags(stb, VNIFILTER_ENTRY_STATS_MAX, RTA_DATA(stats_attr),
171 RTA_PAYLOAD(stats_attr), NLA_F_NESTED);
172
173 print_nl();
174 print_string(PRINT_FP, NULL, "%-" textify(IFNAMSIZ) "s RX: ",
175 "");
176
177 if (stb[VNIFILTER_ENTRY_STATS_RX_BYTES]) {
178 stat = rta_getattr_u64(stb[VNIFILTER_ENTRY_STATS_RX_BYTES]);
179 print_lluint(PRINT_ANY, "rx_bytes", "bytes %llu ", stat);
180 }
181 if (stb[VNIFILTER_ENTRY_STATS_RX_PKTS]) {
182 stat = rta_getattr_u64(stb[VNIFILTER_ENTRY_STATS_RX_PKTS]);
183 print_lluint(PRINT_ANY, "rx_pkts", "pkts %llu ", stat);
184 }
185 if (stb[VNIFILTER_ENTRY_STATS_RX_DROPS]) {
186 stat = rta_getattr_u64(stb[VNIFILTER_ENTRY_STATS_RX_DROPS]);
187 print_lluint(PRINT_ANY, "rx_drops", "drops %llu ", stat);
188 }
189 if (stb[VNIFILTER_ENTRY_STATS_RX_ERRORS]) {
190 stat = rta_getattr_u64(stb[VNIFILTER_ENTRY_STATS_RX_ERRORS]);
191 print_lluint(PRINT_ANY, "rx_errors", "errors %llu ", stat);
192 }
193
194 print_nl();
195 print_string(PRINT_FP, NULL, "%-" textify(IFNAMSIZ) "s TX: ",
196 "");
197
198 if (stb[VNIFILTER_ENTRY_STATS_TX_BYTES]) {
199 stat = rta_getattr_u64(stb[VNIFILTER_ENTRY_STATS_TX_BYTES]);
200 print_lluint(PRINT_ANY, "tx_bytes", "bytes %llu ", stat);
201 }
202 if (stb[VNIFILTER_ENTRY_STATS_TX_PKTS]) {
203 stat = rta_getattr_u64(stb[VNIFILTER_ENTRY_STATS_TX_PKTS]);
204 print_lluint(PRINT_ANY, "tx_pkts", "pkts %llu ", stat);
205 }
206 if (stb[VNIFILTER_ENTRY_STATS_TX_DROPS]) {
207 stat = rta_getattr_u64(stb[VNIFILTER_ENTRY_STATS_TX_DROPS]);
208 print_lluint(PRINT_ANY, "tx_drops", "drops %llu ", stat);
209 }
210 if (stb[VNIFILTER_ENTRY_STATS_TX_ERRORS]) {
211 stat = rta_getattr_u64(stb[VNIFILTER_ENTRY_STATS_TX_ERRORS]);
212 print_lluint(PRINT_ANY, "tx_errors", "errors %llu ", stat);
213 }
214 close_json_object();
215}
216
217static void print_vni(struct rtattr *t, int ifindex)
218{
219 struct rtattr *ttb[VXLAN_VNIFILTER_ENTRY_MAX+1];
220 __u32 vni_start = 0;
221 unsigned int width;
222 __u32 vni_end;
223
224 parse_rtattr_flags(ttb, VXLAN_VNIFILTER_ENTRY_MAX, RTA_DATA(t),
225 RTA_PAYLOAD(t), NLA_F_NESTED);
226
227 if (ttb[VXLAN_VNIFILTER_ENTRY_START])
228 vni_start = rta_getattr_u32(ttb[VXLAN_VNIFILTER_ENTRY_START]);
229
230 if (ttb[VXLAN_VNIFILTER_ENTRY_END])
231 vni_end = rta_getattr_u32(ttb[VXLAN_VNIFILTER_ENTRY_END]);
232 else
233 vni_end = vni_start;
234
235 open_json_object(NULL);
236 width = print_range("vni", vni_start, vni_end);
237 if (!is_json_context())
238 printf("%-*s ", VXLAN_ID_LEN - width, "");
239
240 if (ttb[VXLAN_VNIFILTER_ENTRY_GROUP]) {
241 __be32 addr = rta_getattr_u32(ttb[VXLAN_VNIFILTER_ENTRY_GROUP]);
242
243 if (addr) {
244 if (IN_MULTICAST(ntohl(addr)))
245 print_string(PRINT_ANY,
246 "group",
247 "%s",
248 format_host(AF_INET, 4, &addr));
249 else
250 print_string(PRINT_ANY,
251 "remote",
252 "%s",
253 format_host(AF_INET, 4, &addr));
254 }
255 } else if (ttb[VXLAN_VNIFILTER_ENTRY_GROUP6]) {
256 struct in6_addr addr;
257
258 memcpy(&addr, RTA_DATA(ttb[VXLAN_VNIFILTER_ENTRY_GROUP6]), sizeof(struct in6_addr));
259 if (!IN6_IS_ADDR_UNSPECIFIED(&addr)) {
260 if (IN6_IS_ADDR_MULTICAST(&addr))
261 print_string(PRINT_ANY,
262 "group",
263 "%s",
264 format_host(AF_INET6,
265 sizeof(struct in6_addr),
266 &addr));
267 else
268 print_string(PRINT_ANY,
269 "remote",
270 "%s",
271 format_host(AF_INET6,
272 sizeof(struct in6_addr),
273 &addr));
274 }
275 }
276
277 if (ttb[VXLAN_VNIFILTER_ENTRY_STATS])
278 print_vnifilter_entry_stats(ttb[VXLAN_VNIFILTER_ENTRY_STATS]);
279
280 close_json_object();
281 print_nl();
282}
283
284int print_vnifilter_rtm(struct nlmsghdr *n, void *arg)
285{
286 struct tunnel_msg *tmsg = NLMSG_DATA(n);
287 int len = n->nlmsg_len;
288 bool opened = false;
289 struct rtattr *t;
290 FILE *fp = arg;
291 int rem;
292
293 if (n->nlmsg_type != RTM_NEWTUNNEL &&
294 n->nlmsg_type != RTM_DELTUNNEL &&
295 n->nlmsg_type != RTM_GETTUNNEL) {
296 fprintf(stderr, "Unknown vni tunnel rtm msg: %08x %08x %08x\n",
297 n->nlmsg_len, n->nlmsg_type, n->nlmsg_flags);
298 return 0;
299 }
300
301 len -= NLMSG_LENGTH(sizeof(*tmsg));
302 if (len < 0) {
303 fprintf(stderr, "BUG: wrong nlmsg len %d\n", len);
304 return -1;
305 }
306
307 if (tmsg->family != AF_BRIDGE)
308 return 0;
309
310 if (filter_index && filter_index != tmsg->ifindex)
311 return 0;
312
313 print_headers(fp, "[TUNNEL]");
314
315 if (n->nlmsg_type == RTM_DELTUNNEL)
316 print_bool(PRINT_ANY, "deleted", "Deleted ", true);
317
318 rem = len;
319 for (t = TUNNEL_RTA(tmsg); RTA_OK(t, rem); t = RTA_NEXT(t, rem)) {
320 if (rta_type(t) != VXLAN_VNIFILTER_ENTRY)
321 continue;
322
323 if (!opened) {
324 open_vni_port(tmsg->ifindex);
325 opened = true;
326 } else {
327 print_string(PRINT_FP, NULL, "%-" textify(IFNAMSIZ) "s ", "");
328 }
329
330 print_vni(t, tmsg->ifindex);
331 }
332
333 if (opened)
334 close_vni_port();
335
336 fflush(stdout);
337 return 0;
338}
339
340static int vni_show(int argc, char **argv)
341{
342 char *filter_dev = NULL;
343 __u8 flags = 0;
344 int ret = 0;
345
346 while (argc > 0) {
347 if (strcmp(*argv, "dev") == 0) {
348 NEXT_ARG();
349 if (filter_dev)
350 duparg("dev", *argv);
351 filter_dev = *argv;
352 }
353 argc--; argv++;
354 }
355
356 if (filter_dev) {
357 filter_index = ll_name_to_index(filter_dev);
358 if (!filter_index)
359 return nodev(filter_dev);
360 }
361
362 new_json_obj(json);
363
364 if (show_stats)
365 flags = TUNNEL_MSG_FLAG_STATS;
366
367 if (rtnl_tunneldump_req(&rth, PF_BRIDGE, filter_index, flags) < 0) {
368 perror("Cannot send dump request");
369 exit(1);
370 }
371
372 if (!is_json_context())
373 printf("%-" textify(IFNAMSIZ) "s %-"
374 textify(VXLAN_ID_LEN) "s group/remote\n", "dev",
375 "vni");
376
377 ret = rtnl_dump_filter(&rth, print_vnifilter_rtm, NULL);
378 if (ret < 0) {
379 fprintf(stderr, "Dump ternminated\n");
380 exit(1);
381 }
382
383 delete_json_obj();
384 fflush(stdout);
385 return 0;
386}
387
388int do_vni(int argc, char **argv)
389{
390 ll_init_map(&rth);
391 timestamp = 0;
392
393 if (argc > 0) {
394 if (strcmp(*argv, "add") == 0)
395 return vni_modify(RTM_NEWTUNNEL, argc-1, argv+1);
396 if (strcmp(*argv, "delete") == 0 ||
397 strcmp(*argv, "del") == 0)
398 return vni_modify(RTM_DELTUNNEL, argc-1, argv+1);
399 if (strcmp(*argv, "show") == 0 ||
400 strcmp(*argv, "lst") == 0 ||
401 strcmp(*argv, "list") == 0)
402 return vni_show(argc-1, argv+1);
403 if (strcmp(*argv, "help") == 0)
404 usage();
405 } else {
406 return vni_show(0, NULL);
407 }
408
409 fprintf(stderr, "Command \"%s\" is unknown, try \"bridge vni help\".\n", *argv);
410 exit(-1);
411}
412