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 close_json_array(PRINT_JSON, NULL);
260
261 if (e->vid)
262 print_uint(PRINT_ANY, "vid", " vid %u", e->vid);
263
264 if (tb[MDBA_MDB_EATTR_DST])
265 print_dst(tb[MDBA_MDB_EATTR_DST]);
266
267 if (tb[MDBA_MDB_EATTR_DST_PORT])
268 print_uint(PRINT_ANY, "dst_port", " dst_port %u",
269 rta_getattr_u16(tb[MDBA_MDB_EATTR_DST_PORT]));
270
271 if (tb[MDBA_MDB_EATTR_VNI])
272 print_uint(PRINT_ANY, "vni", " vni %u",
273 rta_getattr_u32(tb[MDBA_MDB_EATTR_VNI]));
274
275 if (tb[MDBA_MDB_EATTR_SRC_VNI])
276 print_uint(PRINT_ANY, "src_vni", " src_vni %u",
277 rta_getattr_u32(tb[MDBA_MDB_EATTR_SRC_VNI]));
278
279 if (tb[MDBA_MDB_EATTR_IFINDEX]) {
280 unsigned int ifindex;
281
282 ifindex = rta_getattr_u32(tb[MDBA_MDB_EATTR_IFINDEX]);
283 print_string(PRINT_ANY, "via", " via %s",
284 ll_index_to_name(ifindex));
285 }
286
287 if (show_stats && tb && tb[MDBA_MDB_EATTR_TIMER]) {
288 __u32 timer = rta_getattr_u32(tb[MDBA_MDB_EATTR_TIMER]);
289
290 print_string(PRINT_ANY, "timer", " %s",
291 format_timer(timer, 1));
292 }
293
294 print_nl();
295 close_json_object();
296}
297
298static void br_print_mdb_entry(FILE *f, int ifindex, struct rtattr *attr,
299 struct nlmsghdr *n)
300{
301 struct rtattr *etb[MDBA_MDB_EATTR_MAX + 1];
302 struct br_mdb_entry *e;
303 struct rtattr *i;
304 int rem;
305
306 rem = RTA_PAYLOAD(attr);
307 for (i = RTA_DATA(attr); RTA_OK(i, rem); i = RTA_NEXT(i, rem)) {
308 e = RTA_DATA(i);
309 parse_rtattr_flags(etb, MDBA_MDB_EATTR_MAX, MDB_RTA(RTA_DATA(i)),
310 RTA_PAYLOAD(i) - RTA_ALIGN(sizeof(*e)),
311 NLA_F_NESTED);
312 print_mdb_entry(f, ifindex, e, n, etb);
313 }
314}
315
316static void print_mdb_entries(FILE *fp, struct nlmsghdr *n,
317 int ifindex, struct rtattr *mdb)
318{
319 int rem = RTA_PAYLOAD(mdb);
320 struct rtattr *i;
321
322 for (i = RTA_DATA(mdb); RTA_OK(i, rem); i = RTA_NEXT(i, rem))
323 br_print_mdb_entry(fp, ifindex, i, n);
324}
325
326static void print_router_entries(FILE *fp, struct nlmsghdr *n,
327 int ifindex, struct rtattr *router)
328{
329 const char *brifname = ll_index_to_name(ifindex);
330
331 if (n->nlmsg_type == RTM_GETMDB) {
332 if (show_details)
333 br_print_router_ports(fp, router, brifname);
334 } else {
335 struct rtattr *i = RTA_DATA(router);
336 uint32_t *port_ifindex = RTA_DATA(i);
337 const char *port_name = ll_index_to_name(*port_ifindex);
338
339 if (is_json_context()) {
340 open_json_array(PRINT_JSON, brifname);
341 open_json_object(NULL);
342
343 print_string(PRINT_JSON, "port", NULL,
344 port_name);
345 close_json_object();
346 close_json_array(PRINT_JSON, NULL);
347 } else {
348 fprintf(fp, "router port dev %s master %s\n",
349 port_name, brifname);
350 }
351 }
352}
353
354static int __parse_mdb_nlmsg(struct nlmsghdr *n, struct rtattr **tb)
355{
356 struct br_port_msg *r = NLMSG_DATA(n);
357 int len = n->nlmsg_len;
358
359 if (n->nlmsg_type != RTM_GETMDB &&
360 n->nlmsg_type != RTM_NEWMDB &&
361 n->nlmsg_type != RTM_DELMDB) {
362 fprintf(stderr,
363 "Not RTM_GETMDB, RTM_NEWMDB or RTM_DELMDB: %08x %08x %08x\n",
364 n->nlmsg_len, n->nlmsg_type, n->nlmsg_flags);
365
366 return 0;
367 }
368
369 len -= NLMSG_LENGTH(sizeof(*r));
370 if (len < 0) {
371 fprintf(stderr, "BUG: wrong nlmsg len %d\n", len);
372 return -1;
373 }
374
375 if (filter_index && filter_index != r->ifindex)
376 return 0;
377
378 parse_rtattr(tb, MDBA_MAX, MDBA_RTA(r), n->nlmsg_len - NLMSG_LENGTH(sizeof(*r)));
379
380 return 1;
381}
382
383static int print_mdbs(struct nlmsghdr *n, void *arg)
384{
385 struct br_port_msg *r = NLMSG_DATA(n);
386 struct rtattr *tb[MDBA_MAX+1];
387 FILE *fp = arg;
388 int ret;
389
390 ret = __parse_mdb_nlmsg(n, tb);
391 if (ret != 1)
392 return ret;
393
394 if (tb[MDBA_MDB])
395 print_mdb_entries(fp, n, r->ifindex, tb[MDBA_MDB]);
396
397 return 0;
398}
399
400static int print_rtrs(struct nlmsghdr *n, void *arg)
401{
402 struct br_port_msg *r = NLMSG_DATA(n);
403 struct rtattr *tb[MDBA_MAX+1];
404 FILE *fp = arg;
405 int ret;
406
407 ret = __parse_mdb_nlmsg(n, tb);
408 if (ret != 1)
409 return ret;
410
411 if (tb[MDBA_ROUTER])
412 print_router_entries(fp, n, r->ifindex, tb[MDBA_ROUTER]);
413
414 return 0;
415}
416
417int print_mdb_mon(struct nlmsghdr *n, void *arg)
418{
419 struct br_port_msg *r = NLMSG_DATA(n);
420 struct rtattr *tb[MDBA_MAX+1];
421 FILE *fp = arg;
422 int ret;
423
424 ret = __parse_mdb_nlmsg(n, tb);
425 if (ret != 1)
426 return ret;
427
428 print_headers(fp, "[MDB]");
429
430 if (n->nlmsg_type == RTM_DELMDB)
431 print_bool(PRINT_ANY, "deleted", "Deleted ", true);
432
433 if (tb[MDBA_MDB])
434 print_mdb_entries(fp, n, r->ifindex, tb[MDBA_MDB]);
435
436 if (tb[MDBA_ROUTER])
437 print_router_entries(fp, n, r->ifindex, tb[MDBA_ROUTER]);
438
439 return 0;
440}
441
442static int mdb_show(int argc, char **argv)
443{
444 char *filter_dev = NULL;
445
446 while (argc > 0) {
447 if (strcmp(*argv, "dev") == 0) {
448 NEXT_ARG();
449 if (filter_dev)
450 duparg("dev", *argv);
451 filter_dev = *argv;
452 } else if (strcmp(*argv, "vid") == 0) {
453 NEXT_ARG();
454 if (filter_vlan)
455 duparg("vid", *argv);
456 filter_vlan = atoi(*argv);
457 }
458 argc--; argv++;
459 }
460
461 if (filter_dev) {
462 filter_index = ll_name_to_index(filter_dev);
463 if (!filter_index)
464 return nodev(filter_dev);
465 }
466
467 new_json_obj(json);
468 open_json_object(NULL);
469
470
471 if (rtnl_mdbdump_req(&rth, PF_BRIDGE) < 0) {
472 perror("Cannot send dump request");
473 delete_json_obj();
474 return -1;
475 }
476
477 open_json_array(PRINT_JSON, "mdb");
478 if (rtnl_dump_filter(&rth, print_mdbs, stdout) < 0) {
479 fprintf(stderr, "Dump terminated\n");
480 delete_json_obj();
481 return -1;
482 }
483 close_json_array(PRINT_JSON, NULL);
484
485
486 if (rtnl_mdbdump_req(&rth, PF_BRIDGE) < 0) {
487 perror("Cannot send dump request");
488 delete_json_obj();
489 return -1;
490 }
491
492 open_json_object("router");
493 if (rtnl_dump_filter(&rth, print_rtrs, stdout) < 0) {
494 fprintf(stderr, "Dump terminated\n");
495 delete_json_obj();
496 return -1;
497 }
498 close_json_object();
499
500 close_json_object();
501 delete_json_obj();
502 fflush(stdout);
503
504 return 0;
505}
506
507static int mdb_parse_grp(const char *grp, struct br_mdb_entry *e)
508{
509 if (inet_pton(AF_INET, grp, &e->addr.u.ip4)) {
510 e->addr.proto = htons(ETH_P_IP);
511 return 0;
512 }
513 if (inet_pton(AF_INET6, grp, &e->addr.u.ip6)) {
514 e->addr.proto = htons(ETH_P_IPV6);
515 return 0;
516 }
517 if (ll_addr_a2n((char *)e->addr.u.mac_addr, sizeof(e->addr.u.mac_addr),
518 grp) == ETH_ALEN) {
519 e->addr.proto = 0;
520 return 0;
521 }
522
523 return -1;
524}
525
526static int mdb_parse_src(struct nlmsghdr *n, int maxlen, const char *src)
527{
528 struct in6_addr src_ip6;
529 __be32 src_ip4;
530
531 if (inet_pton(AF_INET, src, &src_ip4)) {
532 addattr32(n, maxlen, MDBE_ATTR_SOURCE, src_ip4);
533 return 0;
534 }
535
536 if (inet_pton(AF_INET6, src, &src_ip6)) {
537 addattr_l(n, maxlen, MDBE_ATTR_SOURCE, &src_ip6,
538 sizeof(src_ip6));
539 return 0;
540 }
541
542 return -1;
543}
544
545static int mdb_parse_mode(struct nlmsghdr *n, int maxlen, const char *mode)
546{
547 if (strcmp(mode, "include") == 0) {
548 addattr8(n, maxlen, MDBE_ATTR_GROUP_MODE, MCAST_INCLUDE);
549 return 0;
550 }
551
552 if (strcmp(mode, "exclude") == 0) {
553 addattr8(n, maxlen, MDBE_ATTR_GROUP_MODE, MCAST_EXCLUDE);
554 return 0;
555 }
556
557 return -1;
558}
559
560static int mdb_parse_src_entry(struct nlmsghdr *n, int maxlen, char *src_entry)
561{
562 struct in6_addr src_ip6;
563 struct rtattr *nest;
564 __be32 src_ip4;
565
566 nest = addattr_nest(n, maxlen, MDBE_SRC_LIST_ENTRY | NLA_F_NESTED);
567
568 if (inet_pton(AF_INET, src_entry, &src_ip4))
569 addattr32(n, maxlen, MDBE_SRCATTR_ADDRESS, src_ip4);
570 else if (inet_pton(AF_INET6, src_entry, &src_ip6))
571 addattr_l(n, maxlen, MDBE_SRCATTR_ADDRESS, &src_ip6,
572 sizeof(src_ip6));
573 else
574 return -1;
575
576 addattr_nest_end(n, nest);
577
578 return 0;
579}
580
581static int mdb_parse_src_list(struct nlmsghdr *n, int maxlen, char *src_list)
582{
583 struct rtattr *nest;
584 char *sep;
585
586 nest = addattr_nest(n, maxlen, MDBE_ATTR_SRC_LIST | NLA_F_NESTED);
587
588 do {
589 sep = strchr(src_list, ',');
590 if (sep)
591 *sep = '\0';
592
593 if (mdb_parse_src_entry(n, maxlen, src_list)) {
594 fprintf(stderr, "Invalid source entry \"%s\" in source list\n",
595 src_list);
596 return -1;
597 }
598
599 src_list = sep + 1;
600 } while (sep);
601
602 addattr_nest_end(n, nest);
603
604 return 0;
605}
606
607static int mdb_parse_proto(struct nlmsghdr *n, int maxlen, const char *proto)
608{
609 __u32 proto_id;
610 int err;
611
612 err = rtnl_rtprot_a2n(&proto_id, proto);
613 if (err)
614 return err;
615
616 addattr8(n, maxlen, MDBE_ATTR_RTPROT, proto_id);
617
618 return 0;
619}
620
621static int mdb_parse_dst(struct nlmsghdr *n, int maxlen, const char *dst)
622{
623 struct in6_addr dst_ip6;
624 __be32 dst_ip4;
625
626 if (inet_pton(AF_INET, dst, &dst_ip4)) {
627 addattr32(n, maxlen, MDBE_ATTR_DST, dst_ip4);
628 return 0;
629 }
630
631 if (inet_pton(AF_INET6, dst, &dst_ip6)) {
632 addattr_l(n, maxlen, MDBE_ATTR_DST, &dst_ip6,
633 sizeof(dst_ip6));
634 return 0;
635 }
636
637 return -1;
638}
639
640static int mdb_parse_dst_port(struct nlmsghdr *n, int maxlen,
641 const char *dst_port)
642{
643 unsigned long port;
644 char *endptr;
645
646 port = strtoul(dst_port, &endptr, 0);
647 if (endptr && *endptr) {
648 struct servent *pse;
649
650 pse = getservbyname(dst_port, "udp");
651 if (!pse)
652 return -1;
653 port = ntohs(pse->s_port);
654 } else if (port > USHRT_MAX) {
655 return -1;
656 }
657
658 addattr16(n, maxlen, MDBE_ATTR_DST_PORT, port);
659
660 return 0;
661}
662
663static int mdb_parse_vni(struct nlmsghdr *n, int maxlen, const char *vni,
664 int attr_type)
665{
666 unsigned long vni_num;
667 char *endptr;
668
669 vni_num = strtoul(vni, &endptr, 0);
670 if ((endptr && *endptr) || vni_num == ULONG_MAX)
671 return -1;
672
673 addattr32(n, maxlen, attr_type, vni_num);
674
675 return 0;
676}
677
678static int mdb_parse_dev(struct nlmsghdr *n, int maxlen, const char *dev)
679{
680 unsigned int ifindex;
681
682 ifindex = ll_name_to_index(dev);
683 if (!ifindex)
684 return -1;
685
686 addattr32(n, maxlen, MDBE_ATTR_IFINDEX, ifindex);
687
688 return 0;
689}
690
691static int mdb_modify(int cmd, int flags, int argc, char **argv)
692{
693 struct {
694 struct nlmsghdr n;
695 struct br_port_msg bpm;
696 char buf[1024];
697 } req = {
698 .n.nlmsg_len = NLMSG_LENGTH(sizeof(struct br_port_msg)),
699 .n.nlmsg_flags = NLM_F_REQUEST | flags,
700 .n.nlmsg_type = cmd,
701 .bpm.family = PF_BRIDGE,
702 };
703 char *d = NULL, *p = NULL, *grp = NULL, *src = NULL, *mode = NULL;
704 char *dst_port = NULL, *vni = NULL, *src_vni = NULL, *via = NULL;
705 char *src_list = NULL, *proto = NULL, *dst = NULL;
706 struct br_mdb_entry entry = {};
707 bool set_attrs = false;
708 short vid = 0;
709
710 while (argc > 0) {
711 if (strcmp(*argv, "dev") == 0) {
712 NEXT_ARG();
713 d = *argv;
714 } else if (strcmp(*argv, "grp") == 0) {
715 NEXT_ARG();
716 grp = *argv;
717 } else if (strcmp(*argv, "port") == 0) {
718 NEXT_ARG();
719 p = *argv;
720 } else if (strcmp(*argv, "permanent") == 0) {
721 if (cmd == RTM_NEWMDB)
722 entry.state |= MDB_PERMANENT;
723 } else if (strcmp(*argv, "temp") == 0) {
724 ;
725 } else if (strcmp(*argv, "vid") == 0) {
726 NEXT_ARG();
727 vid = atoi(*argv);
728 } else if (strcmp(*argv, "src") == 0) {
729 NEXT_ARG();
730 src = *argv;
731 set_attrs = true;
732 } else if (strcmp(*argv, "filter_mode") == 0) {
733 NEXT_ARG();
734 mode = *argv;
735 set_attrs = true;
736 } else if (strcmp(*argv, "source_list") == 0) {
737 NEXT_ARG();
738 src_list = *argv;
739 set_attrs = true;
740 } else if (strcmp(*argv, "proto") == 0) {
741 NEXT_ARG();
742 proto = *argv;
743 set_attrs = true;
744 } else if (strcmp(*argv, "dst") == 0) {
745 NEXT_ARG();
746 dst = *argv;
747 set_attrs = true;
748 } else if (strcmp(*argv, "dst_port") == 0) {
749 NEXT_ARG();
750 dst_port = *argv;
751 set_attrs = true;
752 } else if (strcmp(*argv, "vni") == 0) {
753 NEXT_ARG();
754 vni = *argv;
755 set_attrs = true;
756 } else if (strcmp(*argv, "src_vni") == 0) {
757 NEXT_ARG();
758 src_vni = *argv;
759 set_attrs = true;
760 } else if (strcmp(*argv, "via") == 0) {
761 NEXT_ARG();
762 via = *argv;
763 set_attrs = true;
764 } else {
765 if (matches(*argv, "help") == 0)
766 usage();
767 }
768 argc--; argv++;
769 }
770
771 if (d == NULL || grp == NULL || p == NULL) {
772 fprintf(stderr, "Device, group address and port name are required arguments.\n");
773 return -1;
774 }
775
776 req.bpm.ifindex = ll_name_to_index(d);
777 if (!req.bpm.ifindex)
778 return nodev(d);
779
780 entry.ifindex = ll_name_to_index(p);
781 if (!entry.ifindex)
782 return nodev(p);
783
784 if (mdb_parse_grp(grp, &entry)) {
785 fprintf(stderr, "Invalid address \"%s\"\n", grp);
786 return -1;
787 }
788
789 entry.vid = vid;
790 addattr_l(&req.n, sizeof(req), MDBA_SET_ENTRY, &entry, sizeof(entry));
791 if (set_attrs) {
792 struct rtattr *nest = addattr_nest(&req.n, sizeof(req),
793 MDBA_SET_ENTRY_ATTRS);
794
795 nest->rta_type |= NLA_F_NESTED;
796
797 if (src && mdb_parse_src(&req.n, sizeof(req), src)) {
798 fprintf(stderr, "Invalid source address \"%s\"\n", src);
799 return -1;
800 }
801
802 if (mode && mdb_parse_mode(&req.n, sizeof(req), mode)) {
803 fprintf(stderr, "Invalid filter mode \"%s\"\n", mode);
804 return -1;
805 }
806
807 if (src_list && mdb_parse_src_list(&req.n, sizeof(req),
808 src_list))
809 return -1;
810
811 if (proto && mdb_parse_proto(&req.n, sizeof(req), proto)) {
812 fprintf(stderr, "Invalid protocol value \"%s\"\n",
813 proto);
814 return -1;
815 }
816
817 if (dst && mdb_parse_dst(&req.n, sizeof(req), dst)) {
818 fprintf(stderr, "Invalid underlay destination address \"%s\"\n",
819 dst);
820 return -1;
821 }
822
823 if (dst_port && mdb_parse_dst_port(&req.n, sizeof(req),
824 dst_port)) {
825 fprintf(stderr, "Invalid destination port \"%s\"\n", dst_port);
826 return -1;
827 }
828
829 if (vni && mdb_parse_vni(&req.n, sizeof(req), vni,
830 MDBE_ATTR_VNI)) {
831 fprintf(stderr, "Invalid destination VNI \"%s\"\n",
832 vni);
833 return -1;
834 }
835
836 if (src_vni && mdb_parse_vni(&req.n, sizeof(req), src_vni,
837 MDBE_ATTR_SRC_VNI)) {
838 fprintf(stderr, "Invalid source VNI \"%s\"\n", src_vni);
839 return -1;
840 }
841
842 if (via && mdb_parse_dev(&req.n, sizeof(req), via))
843 return nodev(via);
844
845 addattr_nest_end(&req.n, nest);
846 }
847
848 if (rtnl_talk(&rth, &req.n, NULL) < 0)
849 return -1;
850
851 return 0;
852}
853
854static int mdb_get(int argc, char **argv)
855{
856 struct {
857 struct nlmsghdr n;
858 struct br_port_msg bpm;
859 char buf[1024];
860 } req = {
861 .n.nlmsg_len = NLMSG_LENGTH(sizeof(struct br_port_msg)),
862 .n.nlmsg_flags = NLM_F_REQUEST,
863 .n.nlmsg_type = RTM_GETMDB,
864 .bpm.family = PF_BRIDGE,
865 };
866 char *d = NULL, *grp = NULL, *src = NULL, *src_vni = NULL;
867 struct br_mdb_entry entry = {};
868 struct nlmsghdr *answer;
869 bool get_attrs = false;
870 short vid = 0;
871 int ret = 0;
872
873 while (argc > 0) {
874 if (strcmp(*argv, "dev") == 0) {
875 NEXT_ARG();
876 d = *argv;
877 } else if (strcmp(*argv, "grp") == 0) {
878 NEXT_ARG();
879 grp = *argv;
880 } else if (strcmp(*argv, "vid") == 0) {
881 NEXT_ARG();
882 vid = atoi(*argv);
883 } else if (strcmp(*argv, "src") == 0) {
884 NEXT_ARG();
885 src = *argv;
886 get_attrs = true;
887 } else if (strcmp(*argv, "src_vni") == 0) {
888 NEXT_ARG();
889 src_vni = *argv;
890 get_attrs = true;
891 } else {
892 if (strcmp(*argv, "help") == 0)
893 usage();
894 }
895 argc--; argv++;
896 }
897
898 if (d == NULL || grp == NULL) {
899 fprintf(stderr, "Device and group address are required arguments.\n");
900 return -1;
901 }
902
903 req.bpm.ifindex = ll_name_to_index(d);
904 if (!req.bpm.ifindex)
905 return nodev(d);
906
907 if (mdb_parse_grp(grp, &entry)) {
908 fprintf(stderr, "Invalid address \"%s\"\n", grp);
909 return -1;
910 }
911
912 entry.vid = vid;
913 addattr_l(&req.n, sizeof(req), MDBA_GET_ENTRY, &entry, sizeof(entry));
914 if (get_attrs) {
915 struct rtattr *nest = addattr_nest(&req.n, sizeof(req),
916 MDBA_GET_ENTRY_ATTRS);
917
918 nest->rta_type |= NLA_F_NESTED;
919
920 if (src && mdb_parse_src(&req.n, sizeof(req), src)) {
921 fprintf(stderr, "Invalid source address \"%s\"\n", src);
922 return -1;
923 }
924
925 if (src_vni && mdb_parse_vni(&req.n, sizeof(req), src_vni,
926 MDBE_ATTR_SRC_VNI)) {
927 fprintf(stderr, "Invalid source VNI \"%s\"\n", src_vni);
928 return -1;
929 }
930
931 addattr_nest_end(&req.n, nest);
932 }
933
934 if (rtnl_talk(&rth, &req.n, &answer) < 0)
935 return -2;
936
937 new_json_obj(json);
938
939 if (print_mdbs(answer, stdout) < 0)
940 ret = -1;
941
942 delete_json_obj();
943 free(answer);
944
945 return ret;
946}
947
948static int mdb_flush(int argc, char **argv)
949{
950 struct {
951 struct nlmsghdr n;
952 struct br_port_msg bpm;
953 char buf[1024];
954 } req = {
955 .n.nlmsg_len = NLMSG_LENGTH(sizeof(struct br_port_msg)),
956 .n.nlmsg_flags = NLM_F_REQUEST | NLM_F_BULK,
957 .n.nlmsg_type = RTM_DELMDB,
958 .bpm.family = PF_BRIDGE,
959 };
960 char *d = NULL, *p = NULL, *src_vni = NULL, *proto = NULL, *dst = NULL;
961 char *dst_port = NULL, *vni = NULL;
962 struct br_mdb_entry entry = {};
963 unsigned short state_mask = 0;
964 bool set_attrs = false;
965 short vid = 0;
966
967 while (argc > 0) {
968 if (strcmp(*argv, "dev") == 0) {
969 NEXT_ARG();
970 d = *argv;
971 } else if (strcmp(*argv, "port") == 0) {
972 NEXT_ARG();
973 p = *argv;
974 } else if (strcmp(*argv, "vid") == 0) {
975 NEXT_ARG();
976 vid = atoi(*argv);
977 } else if (strcmp(*argv, "src_vni") == 0) {
978 NEXT_ARG();
979 src_vni = *argv;
980 set_attrs = true;
981 } else if (strcmp(*argv, "proto") == 0) {
982 NEXT_ARG();
983 proto = *argv;
984 set_attrs = true;
985 } else if (strcmp(*argv, "permanent") == 0) {
986 entry.state |= MDB_PERMANENT;
987 state_mask |= MDB_PERMANENT;
988 set_attrs = true;
989 } else if (strcmp(*argv, "nopermanent") == 0) {
990 entry.state &= ~MDB_PERMANENT;
991 state_mask |= MDB_PERMANENT;
992 set_attrs = true;
993 } else if (strcmp(*argv, "dst") == 0) {
994 NEXT_ARG();
995 dst = *argv;
996 set_attrs = true;
997 } else if (strcmp(*argv, "dst_port") == 0) {
998 NEXT_ARG();
999 dst_port = *argv;
1000 set_attrs = true;
1001 } else if (strcmp(*argv, "vni") == 0) {
1002 NEXT_ARG();
1003 vni = *argv;
1004 set_attrs = true;
1005 } else {
1006 if (strcmp(*argv, "help") == 0)
1007 usage();
1008 }
1009 argc--; argv++;
1010 }
1011
1012 if (d == NULL) {
1013 fprintf(stderr, "Device is a required argument.\n");
1014 return -1;
1015 }
1016
1017 req.bpm.ifindex = ll_name_to_index(d);
1018 if (!req.bpm.ifindex)
1019 return nodev(d);
1020
1021 if (p) {
1022 entry.ifindex = ll_name_to_index(p);
1023 if (!entry.ifindex)
1024 return nodev(p);
1025 }
1026
1027 entry.vid = vid;
1028 addattr_l(&req.n, sizeof(req), MDBA_SET_ENTRY, &entry, sizeof(entry));
1029 if (set_attrs) {
1030 struct rtattr *nest = addattr_nest(&req.n, sizeof(req),
1031 MDBA_SET_ENTRY_ATTRS);
1032
1033 nest->rta_type |= NLA_F_NESTED;
1034
1035 if (proto && mdb_parse_proto(&req.n, sizeof(req), proto)) {
1036 fprintf(stderr, "Invalid protocol value \"%s\"\n",
1037 proto);
1038 return -1;
1039 }
1040
1041 if (dst && mdb_parse_dst(&req.n, sizeof(req), dst)) {
1042 fprintf(stderr, "Invalid underlay destination address \"%s\"\n",
1043 dst);
1044 return -1;
1045 }
1046
1047 if (dst_port && mdb_parse_dst_port(&req.n, sizeof(req),
1048 dst_port)) {
1049 fprintf(stderr, "Invalid destination port \"%s\"\n", dst_port);
1050 return -1;
1051 }
1052
1053 if (vni && mdb_parse_vni(&req.n, sizeof(req), vni,
1054 MDBE_ATTR_VNI)) {
1055 fprintf(stderr, "Invalid destination VNI \"%s\"\n",
1056 vni);
1057 return -1;
1058 }
1059
1060 if (src_vni && mdb_parse_vni(&req.n, sizeof(req), src_vni,
1061 MDBE_ATTR_SRC_VNI)) {
1062 fprintf(stderr, "Invalid source VNI \"%s\"\n", src_vni);
1063 return -1;
1064 }
1065
1066 if (state_mask)
1067 addattr8(&req.n, sizeof(req), MDBE_ATTR_STATE_MASK,
1068 state_mask);
1069
1070 addattr_nest_end(&req.n, nest);
1071 }
1072
1073 if (rtnl_talk(&rth, &req.n, NULL) < 0)
1074 return -1;
1075
1076 return 0;
1077}
1078
1079int do_mdb(int argc, char **argv)
1080{
1081 ll_init_map(&rth);
1082 timestamp = 0;
1083
1084 if (argc > 0) {
1085 if (matches(*argv, "add") == 0)
1086 return mdb_modify(RTM_NEWMDB, NLM_F_CREATE|NLM_F_EXCL, argc-1, argv+1);
1087 if (strcmp(*argv, "replace") == 0)
1088 return mdb_modify(RTM_NEWMDB, NLM_F_CREATE|NLM_F_REPLACE, argc-1, argv+1);
1089 if (matches(*argv, "delete") == 0)
1090 return mdb_modify(RTM_DELMDB, 0, argc-1, argv+1);
1091
1092 if (matches(*argv, "show") == 0 ||
1093 matches(*argv, "lst") == 0 ||
1094 matches(*argv, "list") == 0)
1095 return mdb_show(argc-1, argv+1);
1096 if (strcmp(*argv, "get") == 0)
1097 return mdb_get(argc-1, argv+1);
1098 if (strcmp(*argv, "flush") == 0)
1099 return mdb_flush(argc-1, argv+1);
1100 if (matches(*argv, "help") == 0)
1101 usage();
1102 } else
1103 return mdb_show(0, NULL);
1104
1105 fprintf(stderr, "Command \"%s\" is unknown, try \"bridge mdb help\".\n", *argv);
1106 exit(-1);
1107}
1108