1
2
3
4
5
6#include <stdio.h>
7#include <stdlib.h>
8#include <unistd.h>
9#include <fcntl.h>
10#include <sys/socket.h>
11#include <net/if.h>
12#include <netinet/in.h>
13#include <linux/if_bridge.h>
14#include <linux/if_ether.h>
15#include <string.h>
16#include <arpa/inet.h>
17#include <netdb.h>
18#include <limits.h>
19
20#include "libnetlink.h"
21#include "utils.h"
22#include "br_common.h"
23#include "rt_names.h"
24#include "json_print.h"
25
26#ifndef MDBA_RTA
27#define MDBA_RTA(r) \
28 ((struct rtattr *)(((char *)(r)) + NLMSG_ALIGN(sizeof(struct br_port_msg))))
29#endif
30
31static unsigned int filter_index, filter_vlan;
32
33static void usage(void)
34{
35 fprintf(stderr,
36 "Usage: bridge mdb { add | del | replace } dev DEV port PORT grp GROUP [src SOURCE] [permanent | temp] [vid VID]\n"
37 " [ filter_mode { include | exclude } ] [ source_list SOURCE_LIST ] [ proto PROTO ] [ dst IPADDR ]\n"
38 " [ dst_port DST_PORT ] [ vni VNI ] [ src_vni SRC_VNI ] [ via DEV ]\n"
39 " bridge mdb {show} [ dev DEV ] [ vid VID ]\n"
40 " bridge mdb get dev DEV grp GROUP [ src SOURCE ] [ vid VID ] [ src_vni SRC_VNI ]\n"
41 " bridge mdb flush dev DEV [ port PORT ] [ vid VID ] [ src_vni SRC_VNI ] [ proto PROTO ]\n"
42 " [ [no]permanent ] [ dst IPADDR ] [ dst_port DST_PORT ] [ vni VNI ]\n");
43 exit(-1);
44}
45
46static bool is_temp_mcast_rtr(__u8 type)
47{
48 return type == MDB_RTR_TYPE_TEMP_QUERY || type == MDB_RTR_TYPE_TEMP;
49}
50
51static const char *format_timer(__u32 ticks, int align)
52{
53 struct timeval tv;
54 static char tbuf[32];
55
56 __jiffies_to_tv(&tv, ticks);
57 if (align)
58 snprintf(tbuf, sizeof(tbuf), "%4lu.%.2lu",
59 (unsigned long)tv.tv_sec,
60 (unsigned long)tv.tv_usec / 10000);
61 else
62 snprintf(tbuf, sizeof(tbuf), "%lu.%.2lu",
63 (unsigned long)tv.tv_sec,
64 (unsigned long)tv.tv_usec / 10000);
65
66 return tbuf;
67}
68
69void br_print_router_port_stats(struct rtattr *pattr)
70{
71 struct rtattr *tb[MDBA_ROUTER_PATTR_MAX + 1];
72
73 parse_rtattr(tb, MDBA_ROUTER_PATTR_MAX, MDB_RTR_RTA(RTA_DATA(pattr)),
74 RTA_PAYLOAD(pattr) - RTA_ALIGN(sizeof(uint32_t)));
75
76 if (tb[MDBA_ROUTER_PATTR_TIMER]) {
77 __u32 timer = rta_getattr_u32(tb[MDBA_ROUTER_PATTR_TIMER]);
78
79 print_string(PRINT_ANY, "timer", " %s",
80 format_timer(timer, 1));
81 }
82
83 if (tb[MDBA_ROUTER_PATTR_TYPE]) {
84 __u8 type = rta_getattr_u8(tb[MDBA_ROUTER_PATTR_TYPE]);
85
86 print_string(PRINT_ANY, "type", " %s",
87 is_temp_mcast_rtr(type) ? "temp" : "permanent");
88 }
89}
90
91static void br_print_router_ports(FILE *f, struct rtattr *attr,
92 const char *brifname)
93{
94 int rem = RTA_PAYLOAD(attr);
95 struct rtattr *i;
96
97 if (is_json_context())
98 open_json_array(PRINT_JSON, brifname);
99 else if (!show_stats)
100 fprintf(f, "router ports on %s: ", brifname);
101
102 for (i = RTA_DATA(attr); RTA_OK(i, rem); i = RTA_NEXT(i, rem)) {
103 uint32_t *port_ifindex = RTA_DATA(i);
104 const char *port_ifname = ll_index_to_name(*port_ifindex);
105
106 if (is_json_context()) {
107 open_json_object(NULL);
108 print_string(PRINT_JSON, "port", NULL, port_ifname);
109
110 if (show_stats)
111 br_print_router_port_stats(i);
112 close_json_object();
113 } else if (show_stats) {
114 fprintf(f, "router ports on %s: %s",
115 brifname, port_ifname);
116
117 br_print_router_port_stats(i);
118 fprintf(f, "\n");
119 } else {
120 fprintf(f, "%s ", port_ifname);
121 }
122 }
123
124 if (!show_stats)
125 print_nl();
126
127 close_json_array(PRINT_JSON, NULL);
128}
129
130static void print_src_entry(struct rtattr *src_attr, int af, const char *sep)
131{
132 struct rtattr *stb[MDBA_MDB_SRCATTR_MAX + 1];
133 SPRINT_BUF(abuf);
134 const char *addr;
135 __u32 timer_val;
136
137 parse_rtattr_nested(stb, MDBA_MDB_SRCATTR_MAX, src_attr);
138 if (!stb[MDBA_MDB_SRCATTR_ADDRESS] || !stb[MDBA_MDB_SRCATTR_TIMER])
139 return;
140
141 addr = inet_ntop(af, RTA_DATA(stb[MDBA_MDB_SRCATTR_ADDRESS]), abuf,
142 sizeof(abuf));
143 if (!addr)
144 return;
145 timer_val = rta_getattr_u32(stb[MDBA_MDB_SRCATTR_TIMER]);
146
147 open_json_object(NULL);
148 print_string(PRINT_FP, NULL, "%s", sep);
149 print_color_string(PRINT_ANY, ifa_family_color(af),
150 "address", "%s", addr);
151 print_string(PRINT_ANY, "timer", "/%s", format_timer(timer_val, 0));
152 close_json_object();
153}
154
155static void print_dst(const struct rtattr *dst_attr)
156{
157 SPRINT_BUF(abuf);
158 int af = AF_INET;
159 const void *dst;
160
161 if (RTA_PAYLOAD(dst_attr) == sizeof(struct in6_addr))
162 af = AF_INET6;
163
164 dst = (const void *)RTA_DATA(dst_attr);
165 print_color_string(PRINT_ANY, ifa_family_color(af),
166 "dst", " dst %s",
167 inet_ntop(af, dst, abuf, sizeof(abuf)));
168}
169
170static void print_mdb_entry(FILE *f, int ifindex, const struct br_mdb_entry *e,
171 struct nlmsghdr *n, struct rtattr **tb)
172{
173 const void *grp, *src;
174 const char *addr;
175 SPRINT_BUF(abuf);
176 const char *dev;
177 int af;
178
179 if (filter_vlan && e->vid != filter_vlan)
180 return;
181
182 if (!e->addr.proto) {
183 af = AF_PACKET;
184 grp = &e->addr.u.mac_addr;
185 } else if (e->addr.proto == htons(ETH_P_IP)) {
186 af = AF_INET;
187 grp = &e->addr.u.ip4;
188 } else {
189 af = AF_INET6;
190 grp = &e->addr.u.ip6;
191 }
192 dev = ll_index_to_name(ifindex);
193
194 open_json_object(NULL);
195
196 print_int(PRINT_JSON, "index", NULL, ifindex);
197 print_color_string(PRINT_ANY, COLOR_IFNAME, "dev", "dev %s", dev);
198 print_string(PRINT_ANY, "port", " port %s",
199 ll_index_to_name(e->ifindex));
200
201
202 addr = rt_addr_n2a_r(af, ETH_ALEN, grp, abuf, sizeof(abuf));
203 if (!addr)
204 return;
205
206 print_color_string(PRINT_ANY, ifa_family_color(af),
207 "grp", " grp %s", addr);
208
209 if (tb && tb[MDBA_MDB_EATTR_SOURCE]) {
210 src = (const void *)RTA_DATA(tb[MDBA_MDB_EATTR_SOURCE]);
211 print_color_string(PRINT_ANY, ifa_family_color(af),
212 "src", " src %s",
213 inet_ntop(af, src, abuf, sizeof(abuf)));
214 }
215 print_string(PRINT_ANY, "state", " %s",
216 (e->state & MDB_PERMANENT) ? "permanent" : "temp");
217 if (show_details && tb) {
218 if (tb[MDBA_MDB_EATTR_GROUP_MODE]) {
219 __u8 mode = rta_getattr_u8(tb[MDBA_MDB_EATTR_GROUP_MODE]);
220
221 print_string(PRINT_ANY, "filter_mode", " filter_mode %s",
222 mode == MCAST_INCLUDE ? "include" :
223 "exclude");
224 }
225 if (tb[MDBA_MDB_EATTR_SRC_LIST]) {
226 struct rtattr *i, *attr = tb[MDBA_MDB_EATTR_SRC_LIST];
227 const char *sep = " ";
228 int rem;
229
230 open_json_array(PRINT_ANY, is_json_context() ?
231 "source_list" :
232 " source_list");
233 rem = RTA_PAYLOAD(attr);
234 for (i = RTA_DATA(attr); RTA_OK(i, rem);
235 i = RTA_NEXT(i, rem)) {
236 print_src_entry(i, af, sep);
237 sep = ",";
238 }
239 close_json_array(PRINT_JSON, NULL);
240 }
241 if (tb[MDBA_MDB_EATTR_RTPROT]) {
242 __u8 rtprot = rta_getattr_u8(tb[MDBA_MDB_EATTR_RTPROT]);
243 SPRINT_BUF(rtb);
244
245 print_string(PRINT_ANY, "protocol", " proto %s",
246 rtnl_rtprot_n2a(rtprot, rtb, sizeof(rtb)));
247 }
248 }
249
250 open_json_array(PRINT_JSON, "flags");
251 if (e->flags & MDB_FLAGS_OFFLOAD)
252 print_string(PRINT_ANY, NULL, " %s", "offload");
253 if (e->flags & MDB_FLAGS_FAST_LEAVE)
254 print_string(PRINT_ANY, NULL, " %s", "fast_leave");
255 if (e->flags & MDB_FLAGS_STAR_EXCL)
256 print_string(PRINT_ANY, NULL, " %s", "added_by_star_ex");
257 if (e->flags & MDB_FLAGS_BLOCKED)
258 print_string(PRINT_ANY, NULL, " %s", "blocked");
259 if (e->flags & MDB_FLAGS_OFFLOAD_FAILED)
260 print_string(PRINT_ANY, NULL, " %s", "offload_failed");
261 close_json_array(PRINT_JSON, NULL);
262
263 if (e->vid)
264 print_uint(PRINT_ANY, "vid", " vid %u", e->vid);
265
266 if (tb[MDBA_MDB_EATTR_DST])
267 print_dst(tb[MDBA_MDB_EATTR_DST]);
268
269 if (tb[MDBA_MDB_EATTR_DST_PORT])
270 print_uint(PRINT_ANY, "dst_port", " dst_port %u",
271 rta_getattr_u16(tb[MDBA_MDB_EATTR_DST_PORT]));
272
273 if (tb[MDBA_MDB_EATTR_VNI])
274 print_uint(PRINT_ANY, "vni", " vni %u",
275 rta_getattr_u32(tb[MDBA_MDB_EATTR_VNI]));
276
277 if (tb[MDBA_MDB_EATTR_SRC_VNI])
278 print_uint(PRINT_ANY, "src_vni", " src_vni %u",
279 rta_getattr_u32(tb[MDBA_MDB_EATTR_SRC_VNI]));
280
281 if (tb[MDBA_MDB_EATTR_IFINDEX]) {
282 unsigned int ifindex;
283
284 ifindex = rta_getattr_u32(tb[MDBA_MDB_EATTR_IFINDEX]);
285 print_string(PRINT_ANY, "via", " via %s",
286 ll_index_to_name(ifindex));
287 }
288
289 if (show_stats && tb && tb[MDBA_MDB_EATTR_TIMER]) {
290 __u32 timer = rta_getattr_u32(tb[MDBA_MDB_EATTR_TIMER]);
291
292 print_string(PRINT_ANY, "timer", " %s",
293 format_timer(timer, 1));
294 }
295
296 print_nl();
297 close_json_object();
298}
299
300static void br_print_mdb_entry(FILE *f, int ifindex, struct rtattr *attr,
301 struct nlmsghdr *n)
302{
303 struct rtattr *etb[MDBA_MDB_EATTR_MAX + 1];
304 struct br_mdb_entry *e;
305 struct rtattr *i;
306 int rem;
307
308 rem = RTA_PAYLOAD(attr);
309 for (i = RTA_DATA(attr); RTA_OK(i, rem); i = RTA_NEXT(i, rem)) {
310 e = RTA_DATA(i);
311 parse_rtattr_flags(etb, MDBA_MDB_EATTR_MAX, MDB_RTA(RTA_DATA(i)),
312 RTA_PAYLOAD(i) - RTA_ALIGN(sizeof(*e)),
313 NLA_F_NESTED);
314 print_mdb_entry(f, ifindex, e, n, etb);
315 }
316}
317
318static void print_mdb_entries(FILE *fp, struct nlmsghdr *n,
319 int ifindex, struct rtattr *mdb)
320{
321 int rem = RTA_PAYLOAD(mdb);
322 struct rtattr *i;
323
324 for (i = RTA_DATA(mdb); RTA_OK(i, rem); i = RTA_NEXT(i, rem))
325 br_print_mdb_entry(fp, ifindex, i, n);
326}
327
328static void print_router_entries(FILE *fp, struct nlmsghdr *n,
329 int ifindex, struct rtattr *router)
330{
331 const char *brifname = ll_index_to_name(ifindex);
332
333 if (n->nlmsg_type == RTM_GETMDB) {
334 if (show_details)
335 br_print_router_ports(fp, router, brifname);
336 } else {
337 struct rtattr *i = RTA_DATA(router);
338 uint32_t *port_ifindex = RTA_DATA(i);
339 const char *port_name = ll_index_to_name(*port_ifindex);
340
341 if (is_json_context()) {
342 open_json_array(PRINT_JSON, brifname);
343 open_json_object(NULL);
344
345 print_string(PRINT_JSON, "port", NULL,
346 port_name);
347 close_json_object();
348 close_json_array(PRINT_JSON, NULL);
349 } else {
350 fprintf(fp, "router port dev %s master %s\n",
351 port_name, brifname);
352 }
353 }
354}
355
356static int __parse_mdb_nlmsg(struct nlmsghdr *n, struct rtattr **tb)
357{
358 struct br_port_msg *r = NLMSG_DATA(n);
359 int len = n->nlmsg_len;
360
361 if (n->nlmsg_type != RTM_GETMDB &&
362 n->nlmsg_type != RTM_NEWMDB &&
363 n->nlmsg_type != RTM_DELMDB) {
364 fprintf(stderr,
365 "Not RTM_GETMDB, RTM_NEWMDB or RTM_DELMDB: %08x %08x %08x\n",
366 n->nlmsg_len, n->nlmsg_type, n->nlmsg_flags);
367
368 return 0;
369 }
370
371 len -= NLMSG_LENGTH(sizeof(*r));
372 if (len < 0) {
373 fprintf(stderr, "BUG: wrong nlmsg len %d\n", len);
374 return -1;
375 }
376
377 if (filter_index && filter_index != r->ifindex)
378 return 0;
379
380 parse_rtattr(tb, MDBA_MAX, MDBA_RTA(r), n->nlmsg_len - NLMSG_LENGTH(sizeof(*r)));
381
382 return 1;
383}
384
385static int print_mdbs(struct nlmsghdr *n, void *arg)
386{
387 struct br_port_msg *r = NLMSG_DATA(n);
388 struct rtattr *tb[MDBA_MAX+1];
389 FILE *fp = arg;
390 int ret;
391
392 ret = __parse_mdb_nlmsg(n, tb);
393 if (ret != 1)
394 return ret;
395
396 if (tb[MDBA_MDB])
397 print_mdb_entries(fp, n, r->ifindex, tb[MDBA_MDB]);
398
399 return 0;
400}
401
402static int print_rtrs(struct nlmsghdr *n, void *arg)
403{
404 struct br_port_msg *r = NLMSG_DATA(n);
405 struct rtattr *tb[MDBA_MAX+1];
406 FILE *fp = arg;
407 int ret;
408
409 ret = __parse_mdb_nlmsg(n, tb);
410 if (ret != 1)
411 return ret;
412
413 if (tb[MDBA_ROUTER])
414 print_router_entries(fp, n, r->ifindex, tb[MDBA_ROUTER]);
415
416 return 0;
417}
418
419int print_mdb_mon(struct nlmsghdr *n, void *arg)
420{
421 struct br_port_msg *r = NLMSG_DATA(n);
422 struct rtattr *tb[MDBA_MAX+1];
423 FILE *fp = arg;
424 int ret;
425
426 ret = __parse_mdb_nlmsg(n, tb);
427 if (ret != 1)
428 return ret;
429
430 print_headers(fp, "[MDB]");
431
432 if (n->nlmsg_type == RTM_DELMDB)
433 print_bool(PRINT_ANY, "deleted", "Deleted ", true);
434
435 if (tb[MDBA_MDB])
436 print_mdb_entries(fp, n, r->ifindex, tb[MDBA_MDB]);
437
438 if (tb[MDBA_ROUTER])
439 print_router_entries(fp, n, r->ifindex, tb[MDBA_ROUTER]);
440
441 return 0;
442}
443
444static int mdb_show(int argc, char **argv)
445{
446 char *filter_dev = NULL;
447
448 while (argc > 0) {
449 if (strcmp(*argv, "dev") == 0) {
450 NEXT_ARG();
451 if (filter_dev)
452 duparg("dev", *argv);
453 filter_dev = *argv;
454 } else if (strcmp(*argv, "vid") == 0) {
455 NEXT_ARG();
456 if (filter_vlan)
457 duparg("vid", *argv);
458 filter_vlan = atoi(*argv);
459 }
460 argc--; argv++;
461 }
462
463 if (filter_dev) {
464 filter_index = ll_name_to_index(filter_dev);
465 if (!filter_index)
466 return nodev(filter_dev);
467 }
468
469 new_json_obj(json);
470 open_json_object(NULL);
471
472
473 if (rtnl_mdbdump_req(&rth, PF_BRIDGE) < 0) {
474 perror("Cannot send dump request");
475 delete_json_obj();
476 return -1;
477 }
478
479 open_json_array(PRINT_JSON, "mdb");
480 if (rtnl_dump_filter(&rth, print_mdbs, stdout) < 0) {
481 fprintf(stderr, "Dump terminated\n");
482 delete_json_obj();
483 return -1;
484 }
485 close_json_array(PRINT_JSON, NULL);
486
487
488 if (rtnl_mdbdump_req(&rth, PF_BRIDGE) < 0) {
489 perror("Cannot send dump request");
490 delete_json_obj();
491 return -1;
492 }
493
494 open_json_object("router");
495 if (rtnl_dump_filter(&rth, print_rtrs, stdout) < 0) {
496 fprintf(stderr, "Dump terminated\n");
497 delete_json_obj();
498 return -1;
499 }
500 close_json_object();
501
502 close_json_object();
503 delete_json_obj();
504 fflush(stdout);
505
506 return 0;
507}
508
509static int mdb_parse_grp(const char *grp, struct br_mdb_entry *e)
510{
511 if (inet_pton(AF_INET, grp, &e->addr.u.ip4)) {
512 e->addr.proto = htons(ETH_P_IP);
513 return 0;
514 }
515 if (inet_pton(AF_INET6, grp, &e->addr.u.ip6)) {
516 e->addr.proto = htons(ETH_P_IPV6);
517 return 0;
518 }
519 if (ll_addr_a2n((char *)e->addr.u.mac_addr, sizeof(e->addr.u.mac_addr),
520 grp) == ETH_ALEN) {
521 e->addr.proto = 0;
522 return 0;
523 }
524
525 return -1;
526}
527
528static int mdb_parse_src(struct nlmsghdr *n, int maxlen, const char *src)
529{
530 struct in6_addr src_ip6;
531 __be32 src_ip4;
532
533 if (inet_pton(AF_INET, src, &src_ip4)) {
534 addattr32(n, maxlen, MDBE_ATTR_SOURCE, src_ip4);
535 return 0;
536 }
537
538 if (inet_pton(AF_INET6, src, &src_ip6)) {
539 addattr_l(n, maxlen, MDBE_ATTR_SOURCE, &src_ip6,
540 sizeof(src_ip6));
541 return 0;
542 }
543
544 return -1;
545}
546
547static int mdb_parse_mode(struct nlmsghdr *n, int maxlen, const char *mode)
548{
549 if (strcmp(mode, "include") == 0) {
550 addattr8(n, maxlen, MDBE_ATTR_GROUP_MODE, MCAST_INCLUDE);
551 return 0;
552 }
553
554 if (strcmp(mode, "exclude") == 0) {
555 addattr8(n, maxlen, MDBE_ATTR_GROUP_MODE, MCAST_EXCLUDE);
556 return 0;
557 }
558
559 return -1;
560}
561
562static int mdb_parse_src_entry(struct nlmsghdr *n, int maxlen, char *src_entry)
563{
564 struct in6_addr src_ip6;
565 struct rtattr *nest;
566 __be32 src_ip4;
567
568 nest = addattr_nest(n, maxlen, MDBE_SRC_LIST_ENTRY | NLA_F_NESTED);
569
570 if (inet_pton(AF_INET, src_entry, &src_ip4))
571 addattr32(n, maxlen, MDBE_SRCATTR_ADDRESS, src_ip4);
572 else if (inet_pton(AF_INET6, src_entry, &src_ip6))
573 addattr_l(n, maxlen, MDBE_SRCATTR_ADDRESS, &src_ip6,
574 sizeof(src_ip6));
575 else
576 return -1;
577
578 addattr_nest_end(n, nest);
579
580 return 0;
581}
582
583static int mdb_parse_src_list(struct nlmsghdr *n, int maxlen, char *src_list)
584{
585 struct rtattr *nest;
586 char *sep;
587
588 nest = addattr_nest(n, maxlen, MDBE_ATTR_SRC_LIST | NLA_F_NESTED);
589
590 do {
591 sep = strchr(src_list, ',');
592 if (sep)
593 *sep = '\0';
594
595 if (mdb_parse_src_entry(n, maxlen, src_list)) {
596 fprintf(stderr, "Invalid source entry \"%s\" in source list\n",
597 src_list);
598 return -1;
599 }
600
601 src_list = sep + 1;
602 } while (sep);
603
604 addattr_nest_end(n, nest);
605
606 return 0;
607}
608
609static int mdb_parse_proto(struct nlmsghdr *n, int maxlen, const char *proto)
610{
611 __u32 proto_id;
612 int err;
613
614 err = rtnl_rtprot_a2n(&proto_id, proto);
615 if (err)
616 return err;
617
618 addattr8(n, maxlen, MDBE_ATTR_RTPROT, proto_id);
619
620 return 0;
621}
622
623static int mdb_parse_dst(struct nlmsghdr *n, int maxlen, const char *dst)
624{
625 struct in6_addr dst_ip6;
626 __be32 dst_ip4;
627
628 if (inet_pton(AF_INET, dst, &dst_ip4)) {
629 addattr32(n, maxlen, MDBE_ATTR_DST, dst_ip4);
630 return 0;
631 }
632
633 if (inet_pton(AF_INET6, dst, &dst_ip6)) {
634 addattr_l(n, maxlen, MDBE_ATTR_DST, &dst_ip6,
635 sizeof(dst_ip6));
636 return 0;
637 }
638
639 return -1;
640}
641
642static int mdb_parse_dst_port(struct nlmsghdr *n, int maxlen,
643 const char *dst_port)
644{
645 unsigned long port;
646 char *endptr;
647
648 port = strtoul(dst_port, &endptr, 0);
649 if (endptr && *endptr) {
650 struct servent *pse;
651
652 pse = getservbyname(dst_port, "udp");
653 if (!pse)
654 return -1;
655 port = ntohs(pse->s_port);
656 } else if (port > USHRT_MAX) {
657 return -1;
658 }
659
660 addattr16(n, maxlen, MDBE_ATTR_DST_PORT, port);
661
662 return 0;
663}
664
665static int mdb_parse_vni(struct nlmsghdr *n, int maxlen, const char *vni,
666 int attr_type)
667{
668 unsigned long vni_num;
669 char *endptr;
670
671 vni_num = strtoul(vni, &endptr, 0);
672 if ((endptr && *endptr) || vni_num == ULONG_MAX)
673 return -1;
674
675 addattr32(n, maxlen, attr_type, vni_num);
676
677 return 0;
678}
679
680static int mdb_parse_dev(struct nlmsghdr *n, int maxlen, const char *dev)
681{
682 unsigned int ifindex;
683
684 ifindex = ll_name_to_index(dev);
685 if (!ifindex)
686 return -1;
687
688 addattr32(n, maxlen, MDBE_ATTR_IFINDEX, ifindex);
689
690 return 0;
691}
692
693static int mdb_modify(int cmd, int flags, int argc, char **argv)
694{
695 struct {
696 struct nlmsghdr n;
697 struct br_port_msg bpm;
698 char buf[1024];
699 } req = {
700 .n.nlmsg_len = NLMSG_LENGTH(sizeof(struct br_port_msg)),
701 .n.nlmsg_flags = NLM_F_REQUEST | flags,
702 .n.nlmsg_type = cmd,
703 .bpm.family = PF_BRIDGE,
704 };
705 char *d = NULL, *p = NULL, *grp = NULL, *src = NULL, *mode = NULL;
706 char *dst_port = NULL, *vni = NULL, *src_vni = NULL, *via = NULL;
707 char *src_list = NULL, *proto = NULL, *dst = NULL;
708 struct br_mdb_entry entry = {};
709 bool set_attrs = false;
710 short vid = 0;
711
712 while (argc > 0) {
713 if (strcmp(*argv, "dev") == 0) {
714 NEXT_ARG();
715 d = *argv;
716 } else if (strcmp(*argv, "grp") == 0) {
717 NEXT_ARG();
718 grp = *argv;
719 } else if (strcmp(*argv, "port") == 0) {
720 NEXT_ARG();
721 p = *argv;
722 } else if (strcmp(*argv, "permanent") == 0) {
723 if (cmd == RTM_NEWMDB)
724 entry.state |= MDB_PERMANENT;
725 } else if (strcmp(*argv, "temp") == 0) {
726 ;
727 } else if (strcmp(*argv, "vid") == 0) {
728 NEXT_ARG();
729 vid = atoi(*argv);
730 } else if (strcmp(*argv, "src") == 0) {
731 NEXT_ARG();
732 src = *argv;
733 set_attrs = true;
734 } else if (strcmp(*argv, "filter_mode") == 0) {
735 NEXT_ARG();
736 mode = *argv;
737 set_attrs = true;
738 } else if (strcmp(*argv, "source_list") == 0) {
739 NEXT_ARG();
740 src_list = *argv;
741 set_attrs = true;
742 } else if (strcmp(*argv, "proto") == 0) {
743 NEXT_ARG();
744 proto = *argv;
745 set_attrs = true;
746 } else if (strcmp(*argv, "dst") == 0) {
747 NEXT_ARG();
748 dst = *argv;
749 set_attrs = true;
750 } else if (strcmp(*argv, "dst_port") == 0) {
751 NEXT_ARG();
752 dst_port = *argv;
753 set_attrs = true;
754 } else if (strcmp(*argv, "vni") == 0) {
755 NEXT_ARG();
756 vni = *argv;
757 set_attrs = true;
758 } else if (strcmp(*argv, "src_vni") == 0) {
759 NEXT_ARG();
760 src_vni = *argv;
761 set_attrs = true;
762 } else if (strcmp(*argv, "via") == 0) {
763 NEXT_ARG();
764 via = *argv;
765 set_attrs = true;
766 } else {
767 if (matches(*argv, "help") == 0)
768 usage();
769 }
770 argc--; argv++;
771 }
772
773 if (d == NULL || grp == NULL || p == NULL) {
774 fprintf(stderr, "Device, group address and port name are required arguments.\n");
775 return -1;
776 }
777
778 req.bpm.ifindex = ll_name_to_index(d);
779 if (!req.bpm.ifindex)
780 return nodev(d);
781
782 entry.ifindex = ll_name_to_index(p);
783 if (!entry.ifindex)
784 return nodev(p);
785
786 if (mdb_parse_grp(grp, &entry)) {
787 fprintf(stderr, "Invalid address \"%s\"\n", grp);
788 return -1;
789 }
790
791 entry.vid = vid;
792 addattr_l(&req.n, sizeof(req), MDBA_SET_ENTRY, &entry, sizeof(entry));
793 if (set_attrs) {
794 struct rtattr *nest = addattr_nest(&req.n, sizeof(req),
795 MDBA_SET_ENTRY_ATTRS);
796
797 nest->rta_type |= NLA_F_NESTED;
798
799 if (src && mdb_parse_src(&req.n, sizeof(req), src)) {
800 fprintf(stderr, "Invalid source address \"%s\"\n", src);
801 return -1;
802 }
803
804 if (mode && mdb_parse_mode(&req.n, sizeof(req), mode)) {
805 fprintf(stderr, "Invalid filter mode \"%s\"\n", mode);
806 return -1;
807 }
808
809 if (src_list && mdb_parse_src_list(&req.n, sizeof(req),
810 src_list))
811 return -1;
812
813 if (proto && mdb_parse_proto(&req.n, sizeof(req), proto)) {
814 fprintf(stderr, "Invalid protocol value \"%s\"\n",
815 proto);
816 return -1;
817 }
818
819 if (dst && mdb_parse_dst(&req.n, sizeof(req), dst)) {
820 fprintf(stderr, "Invalid underlay destination address \"%s\"\n",
821 dst);
822 return -1;
823 }
824
825 if (dst_port && mdb_parse_dst_port(&req.n, sizeof(req),
826 dst_port)) {
827 fprintf(stderr, "Invalid destination port \"%s\"\n", dst_port);
828 return -1;
829 }
830
831 if (vni && mdb_parse_vni(&req.n, sizeof(req), vni,
832 MDBE_ATTR_VNI)) {
833 fprintf(stderr, "Invalid destination VNI \"%s\"\n",
834 vni);
835 return -1;
836 }
837
838 if (src_vni && mdb_parse_vni(&req.n, sizeof(req), src_vni,
839 MDBE_ATTR_SRC_VNI)) {
840 fprintf(stderr, "Invalid source VNI \"%s\"\n", src_vni);
841 return -1;
842 }
843
844 if (via && mdb_parse_dev(&req.n, sizeof(req), via))
845 return nodev(via);
846
847 addattr_nest_end(&req.n, nest);
848 }
849
850 if (rtnl_talk(&rth, &req.n, NULL) < 0)
851 return -1;
852
853 return 0;
854}
855
856static int mdb_get(int argc, char **argv)
857{
858 struct {
859 struct nlmsghdr n;
860 struct br_port_msg bpm;
861 char buf[1024];
862 } req = {
863 .n.nlmsg_len = NLMSG_LENGTH(sizeof(struct br_port_msg)),
864 .n.nlmsg_flags = NLM_F_REQUEST,
865 .n.nlmsg_type = RTM_GETMDB,
866 .bpm.family = PF_BRIDGE,
867 };
868 char *d = NULL, *grp = NULL, *src = NULL, *src_vni = NULL;
869 struct br_mdb_entry entry = {};
870 struct nlmsghdr *answer;
871 bool get_attrs = false;
872 short vid = 0;
873 int ret = 0;
874
875 while (argc > 0) {
876 if (strcmp(*argv, "dev") == 0) {
877 NEXT_ARG();
878 d = *argv;
879 } else if (strcmp(*argv, "grp") == 0) {
880 NEXT_ARG();
881 grp = *argv;
882 } else if (strcmp(*argv, "vid") == 0) {
883 NEXT_ARG();
884 vid = atoi(*argv);
885 } else if (strcmp(*argv, "src") == 0) {
886 NEXT_ARG();
887 src = *argv;
888 get_attrs = true;
889 } else if (strcmp(*argv, "src_vni") == 0) {
890 NEXT_ARG();
891 src_vni = *argv;
892 get_attrs = true;
893 } else {
894 if (strcmp(*argv, "help") == 0)
895 usage();
896 }
897 argc--; argv++;
898 }
899
900 if (d == NULL || grp == NULL) {
901 fprintf(stderr, "Device and group address are required arguments.\n");
902 return -1;
903 }
904
905 req.bpm.ifindex = ll_name_to_index(d);
906 if (!req.bpm.ifindex)
907 return nodev(d);
908
909 if (mdb_parse_grp(grp, &entry)) {
910 fprintf(stderr, "Invalid address \"%s\"\n", grp);
911 return -1;
912 }
913
914 entry.vid = vid;
915 addattr_l(&req.n, sizeof(req), MDBA_GET_ENTRY, &entry, sizeof(entry));
916 if (get_attrs) {
917 struct rtattr *nest = addattr_nest(&req.n, sizeof(req),
918 MDBA_GET_ENTRY_ATTRS);
919
920 nest->rta_type |= NLA_F_NESTED;
921
922 if (src && mdb_parse_src(&req.n, sizeof(req), src)) {
923 fprintf(stderr, "Invalid source address \"%s\"\n", src);
924 return -1;
925 }
926
927 if (src_vni && mdb_parse_vni(&req.n, sizeof(req), src_vni,
928 MDBE_ATTR_SRC_VNI)) {
929 fprintf(stderr, "Invalid source VNI \"%s\"\n", src_vni);
930 return -1;
931 }
932
933 addattr_nest_end(&req.n, nest);
934 }
935
936 if (rtnl_talk(&rth, &req.n, &answer) < 0)
937 return -2;
938
939 new_json_obj(json);
940
941 if (print_mdbs(answer, stdout) < 0)
942 ret = -1;
943
944 delete_json_obj();
945 free(answer);
946
947 return ret;
948}
949
950static int mdb_flush(int argc, char **argv)
951{
952 struct {
953 struct nlmsghdr n;
954 struct br_port_msg bpm;
955 char buf[1024];
956 } req = {
957 .n.nlmsg_len = NLMSG_LENGTH(sizeof(struct br_port_msg)),
958 .n.nlmsg_flags = NLM_F_REQUEST | NLM_F_BULK,
959 .n.nlmsg_type = RTM_DELMDB,
960 .bpm.family = PF_BRIDGE,
961 };
962 char *d = NULL, *p = NULL, *src_vni = NULL, *proto = NULL, *dst = NULL;
963 char *dst_port = NULL, *vni = NULL;
964 struct br_mdb_entry entry = {};
965 unsigned short state_mask = 0;
966 bool set_attrs = false;
967 short vid = 0;
968
969 while (argc > 0) {
970 if (strcmp(*argv, "dev") == 0) {
971 NEXT_ARG();
972 d = *argv;
973 } else if (strcmp(*argv, "port") == 0) {
974 NEXT_ARG();
975 p = *argv;
976 } else if (strcmp(*argv, "vid") == 0) {
977 NEXT_ARG();
978 vid = atoi(*argv);
979 } else if (strcmp(*argv, "src_vni") == 0) {
980 NEXT_ARG();
981 src_vni = *argv;
982 set_attrs = true;
983 } else if (strcmp(*argv, "proto") == 0) {
984 NEXT_ARG();
985 proto = *argv;
986 set_attrs = true;
987 } else if (strcmp(*argv, "permanent") == 0) {
988 entry.state |= MDB_PERMANENT;
989 state_mask |= MDB_PERMANENT;
990 set_attrs = true;
991 } else if (strcmp(*argv, "nopermanent") == 0) {
992 entry.state &= ~MDB_PERMANENT;
993 state_mask |= MDB_PERMANENT;
994 set_attrs = true;
995 } else if (strcmp(*argv, "dst") == 0) {
996 NEXT_ARG();
997 dst = *argv;
998 set_attrs = true;
999 } else if (strcmp(*argv, "dst_port") == 0) {
1000 NEXT_ARG();
1001 dst_port = *argv;
1002 set_attrs = true;
1003 } else if (strcmp(*argv, "vni") == 0) {
1004 NEXT_ARG();
1005 vni = *argv;
1006 set_attrs = true;
1007 } else {
1008 if (strcmp(*argv, "help") == 0)
1009 usage();
1010 }
1011 argc--; argv++;
1012 }
1013
1014 if (d == NULL) {
1015 fprintf(stderr, "Device is a required argument.\n");
1016 return -1;
1017 }
1018
1019 req.bpm.ifindex = ll_name_to_index(d);
1020 if (!req.bpm.ifindex)
1021 return nodev(d);
1022
1023 if (p) {
1024 entry.ifindex = ll_name_to_index(p);
1025 if (!entry.ifindex)
1026 return nodev(p);
1027 }
1028
1029 entry.vid = vid;
1030 addattr_l(&req.n, sizeof(req), MDBA_SET_ENTRY, &entry, sizeof(entry));
1031 if (set_attrs) {
1032 struct rtattr *nest = addattr_nest(&req.n, sizeof(req),
1033 MDBA_SET_ENTRY_ATTRS);
1034
1035 nest->rta_type |= NLA_F_NESTED;
1036
1037 if (proto && mdb_parse_proto(&req.n, sizeof(req), proto)) {
1038 fprintf(stderr, "Invalid protocol value \"%s\"\n",
1039 proto);
1040 return -1;
1041 }
1042
1043 if (dst && mdb_parse_dst(&req.n, sizeof(req), dst)) {
1044 fprintf(stderr, "Invalid underlay destination address \"%s\"\n",
1045 dst);
1046 return -1;
1047 }
1048
1049 if (dst_port && mdb_parse_dst_port(&req.n, sizeof(req),
1050 dst_port)) {
1051 fprintf(stderr, "Invalid destination port \"%s\"\n", dst_port);
1052 return -1;
1053 }
1054
1055 if (vni && mdb_parse_vni(&req.n, sizeof(req), vni,
1056 MDBE_ATTR_VNI)) {
1057 fprintf(stderr, "Invalid destination VNI \"%s\"\n",
1058 vni);
1059 return -1;
1060 }
1061
1062 if (src_vni && mdb_parse_vni(&req.n, sizeof(req), src_vni,
1063 MDBE_ATTR_SRC_VNI)) {
1064 fprintf(stderr, "Invalid source VNI \"%s\"\n", src_vni);
1065 return -1;
1066 }
1067
1068 if (state_mask)
1069 addattr8(&req.n, sizeof(req), MDBE_ATTR_STATE_MASK,
1070 state_mask);
1071
1072 addattr_nest_end(&req.n, nest);
1073 }
1074
1075 if (rtnl_talk(&rth, &req.n, NULL) < 0)
1076 return -1;
1077
1078 return 0;
1079}
1080
1081int do_mdb(int argc, char **argv)
1082{
1083 ll_init_map(&rth);
1084 timestamp = 0;
1085
1086 if (argc > 0) {
1087 if (matches(*argv, "add") == 0)
1088 return mdb_modify(RTM_NEWMDB, NLM_F_CREATE|NLM_F_EXCL, argc-1, argv+1);
1089 if (strcmp(*argv, "replace") == 0)
1090 return mdb_modify(RTM_NEWMDB, NLM_F_CREATE|NLM_F_REPLACE, argc-1, argv+1);
1091 if (matches(*argv, "delete") == 0)
1092 return mdb_modify(RTM_DELMDB, 0, argc-1, argv+1);
1093
1094 if (matches(*argv, "show") == 0 ||
1095 matches(*argv, "lst") == 0 ||
1096 matches(*argv, "list") == 0)
1097 return mdb_show(argc-1, argv+1);
1098 if (strcmp(*argv, "get") == 0)
1099 return mdb_get(argc-1, argv+1);
1100 if (strcmp(*argv, "flush") == 0)
1101 return mdb_flush(argc-1, argv+1);
1102 if (matches(*argv, "help") == 0)
1103 usage();
1104 } else
1105 return mdb_show(0, NULL);
1106
1107 fprintf(stderr, "Command \"%s\" is unknown, try \"bridge mdb help\".\n", *argv);
1108 exit(-1);
1109}
1110