]> git.proxmox.com Git - mirror_iproute2.git/blob - bridge/mdb.c
vdpa: add .gitignore
[mirror_iproute2.git] / bridge / mdb.c
1 /* SPDX-License-Identifier: GPL-2.0 */
2 /*
3 * Get mdb table with netlink
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
18 #include "libnetlink.h"
19 #include "br_common.h"
20 #include "rt_names.h"
21 #include "utils.h"
22 #include "json_print.h"
23
24 #ifndef MDBA_RTA
25 #define MDBA_RTA(r) \
26 ((struct rtattr *)(((char *)(r)) + NLMSG_ALIGN(sizeof(struct br_port_msg))))
27 #endif
28
29 static unsigned int filter_index, filter_vlan;
30
31 static void usage(void)
32 {
33 fprintf(stderr,
34 "Usage: bridge mdb { add | del } dev DEV port PORT grp GROUP [src SOURCE] [permanent | temp] [vid VID]\n"
35 " bridge mdb {show} [ dev DEV ] [ vid VID ]\n");
36 exit(-1);
37 }
38
39 static bool is_temp_mcast_rtr(__u8 type)
40 {
41 return type == MDB_RTR_TYPE_TEMP_QUERY || type == MDB_RTR_TYPE_TEMP;
42 }
43
44 static const char *format_timer(__u32 ticks, int align)
45 {
46 struct timeval tv;
47 static char tbuf[32];
48
49 __jiffies_to_tv(&tv, ticks);
50 if (align)
51 snprintf(tbuf, sizeof(tbuf), "%4lu.%.2lu",
52 (unsigned long)tv.tv_sec,
53 (unsigned long)tv.tv_usec / 10000);
54 else
55 snprintf(tbuf, sizeof(tbuf), "%lu.%.2lu",
56 (unsigned long)tv.tv_sec,
57 (unsigned long)tv.tv_usec / 10000);
58
59 return tbuf;
60 }
61
62 static void __print_router_port_stats(FILE *f, struct rtattr *pattr)
63 {
64 struct rtattr *tb[MDBA_ROUTER_PATTR_MAX + 1];
65
66 parse_rtattr(tb, MDBA_ROUTER_PATTR_MAX, MDB_RTR_RTA(RTA_DATA(pattr)),
67 RTA_PAYLOAD(pattr) - RTA_ALIGN(sizeof(uint32_t)));
68
69 if (tb[MDBA_ROUTER_PATTR_TIMER]) {
70 __u32 timer = rta_getattr_u32(tb[MDBA_ROUTER_PATTR_TIMER]);
71
72 print_string(PRINT_ANY, "timer", " %s",
73 format_timer(timer, 1));
74 }
75
76 if (tb[MDBA_ROUTER_PATTR_TYPE]) {
77 __u8 type = rta_getattr_u8(tb[MDBA_ROUTER_PATTR_TYPE]);
78
79 print_string(PRINT_ANY, "type", " %s",
80 is_temp_mcast_rtr(type) ? "temp" : "permanent");
81 }
82 }
83
84 static void br_print_router_ports(FILE *f, struct rtattr *attr,
85 const char *brifname)
86 {
87 int rem = RTA_PAYLOAD(attr);
88 struct rtattr *i;
89
90 if (is_json_context())
91 open_json_array(PRINT_JSON, brifname);
92 else if (!show_stats)
93 fprintf(f, "router ports on %s: ", brifname);
94
95 for (i = RTA_DATA(attr); RTA_OK(i, rem); i = RTA_NEXT(i, rem)) {
96 uint32_t *port_ifindex = RTA_DATA(i);
97 const char *port_ifname = ll_index_to_name(*port_ifindex);
98
99 if (is_json_context()) {
100 open_json_object(NULL);
101 print_string(PRINT_JSON, "port", NULL, port_ifname);
102
103 if (show_stats)
104 __print_router_port_stats(f, i);
105 close_json_object();
106 } else if (show_stats) {
107 fprintf(f, "router ports on %s: %s",
108 brifname, port_ifname);
109
110 __print_router_port_stats(f, i);
111 fprintf(f, "\n");
112 } else {
113 fprintf(f, "%s ", port_ifname);
114 }
115 }
116
117 if (!show_stats)
118 print_nl();
119
120 close_json_array(PRINT_JSON, NULL);
121 }
122
123 static void print_src_entry(struct rtattr *src_attr, int af, const char *sep)
124 {
125 struct rtattr *stb[MDBA_MDB_SRCATTR_MAX + 1];
126 SPRINT_BUF(abuf);
127 const char *addr;
128 __u32 timer_val;
129
130 parse_rtattr_nested(stb, MDBA_MDB_SRCATTR_MAX, src_attr);
131 if (!stb[MDBA_MDB_SRCATTR_ADDRESS] || !stb[MDBA_MDB_SRCATTR_TIMER])
132 return;
133
134 addr = inet_ntop(af, RTA_DATA(stb[MDBA_MDB_SRCATTR_ADDRESS]), abuf,
135 sizeof(abuf));
136 if (!addr)
137 return;
138 timer_val = rta_getattr_u32(stb[MDBA_MDB_SRCATTR_TIMER]);
139
140 open_json_object(NULL);
141 print_string(PRINT_FP, NULL, "%s", sep);
142 print_color_string(PRINT_ANY, ifa_family_color(af),
143 "address", "%s", addr);
144 print_string(PRINT_ANY, "timer", "/%s", format_timer(timer_val, 0));
145 close_json_object();
146 }
147
148 static void print_mdb_entry(FILE *f, int ifindex, const struct br_mdb_entry *e,
149 struct nlmsghdr *n, struct rtattr **tb)
150 {
151 const void *grp, *src;
152 const char *addr;
153 SPRINT_BUF(abuf);
154 const char *dev;
155 int af;
156
157 if (filter_vlan && e->vid != filter_vlan)
158 return;
159
160 if (!e->addr.proto) {
161 af = AF_PACKET;
162 grp = &e->addr.u.mac_addr;
163 } else if (e->addr.proto == htons(ETH_P_IP)) {
164 af = AF_INET;
165 grp = &e->addr.u.ip4;
166 } else {
167 af = AF_INET6;
168 grp = &e->addr.u.ip6;
169 }
170 dev = ll_index_to_name(ifindex);
171
172 open_json_object(NULL);
173
174 print_int(PRINT_JSON, "index", NULL, ifindex);
175 print_color_string(PRINT_ANY, COLOR_IFNAME, "dev", "dev %s", dev);
176 print_string(PRINT_ANY, "port", " port %s",
177 ll_index_to_name(e->ifindex));
178
179 /* The ETH_ALEN argument is ignored for all cases but AF_PACKET */
180 addr = rt_addr_n2a_r(af, ETH_ALEN, grp, abuf, sizeof(abuf));
181 if (!addr)
182 return;
183
184 print_color_string(PRINT_ANY, ifa_family_color(af),
185 "grp", " grp %s", addr);
186
187 if (tb && tb[MDBA_MDB_EATTR_SOURCE]) {
188 src = (const void *)RTA_DATA(tb[MDBA_MDB_EATTR_SOURCE]);
189 print_color_string(PRINT_ANY, ifa_family_color(af),
190 "src", " src %s",
191 inet_ntop(af, src, abuf, sizeof(abuf)));
192 }
193 print_string(PRINT_ANY, "state", " %s",
194 (e->state & MDB_PERMANENT) ? "permanent" : "temp");
195 if (show_details && tb) {
196 if (tb[MDBA_MDB_EATTR_GROUP_MODE]) {
197 __u8 mode = rta_getattr_u8(tb[MDBA_MDB_EATTR_GROUP_MODE]);
198
199 print_string(PRINT_ANY, "filter_mode", " filter_mode %s",
200 mode == MCAST_INCLUDE ? "include" :
201 "exclude");
202 }
203 if (tb[MDBA_MDB_EATTR_SRC_LIST]) {
204 struct rtattr *i, *attr = tb[MDBA_MDB_EATTR_SRC_LIST];
205 const char *sep = " ";
206 int rem;
207
208 open_json_array(PRINT_ANY, is_json_context() ?
209 "source_list" :
210 " source_list");
211 rem = RTA_PAYLOAD(attr);
212 for (i = RTA_DATA(attr); RTA_OK(i, rem);
213 i = RTA_NEXT(i, rem)) {
214 print_src_entry(i, af, sep);
215 sep = ",";
216 }
217 close_json_array(PRINT_JSON, NULL);
218 }
219 if (tb[MDBA_MDB_EATTR_RTPROT]) {
220 __u8 rtprot = rta_getattr_u8(tb[MDBA_MDB_EATTR_RTPROT]);
221 SPRINT_BUF(rtb);
222
223 print_string(PRINT_ANY, "protocol", " proto %s ",
224 rtnl_rtprot_n2a(rtprot, rtb, sizeof(rtb)));
225 }
226 }
227
228 open_json_array(PRINT_JSON, "flags");
229 if (e->flags & MDB_FLAGS_OFFLOAD)
230 print_string(PRINT_ANY, NULL, " %s", "offload");
231 if (e->flags & MDB_FLAGS_FAST_LEAVE)
232 print_string(PRINT_ANY, NULL, " %s", "fast_leave");
233 if (e->flags & MDB_FLAGS_STAR_EXCL)
234 print_string(PRINT_ANY, NULL, " %s", "added_by_star_ex");
235 if (e->flags & MDB_FLAGS_BLOCKED)
236 print_string(PRINT_ANY, NULL, " %s", "blocked");
237 close_json_array(PRINT_JSON, NULL);
238
239 if (e->vid)
240 print_uint(PRINT_ANY, "vid", " vid %u", e->vid);
241
242 if (show_stats && tb && tb[MDBA_MDB_EATTR_TIMER]) {
243 __u32 timer = rta_getattr_u32(tb[MDBA_MDB_EATTR_TIMER]);
244
245 print_string(PRINT_ANY, "timer", " %s",
246 format_timer(timer, 1));
247 }
248
249 print_nl();
250 close_json_object();
251 }
252
253 static void br_print_mdb_entry(FILE *f, int ifindex, struct rtattr *attr,
254 struct nlmsghdr *n)
255 {
256 struct rtattr *etb[MDBA_MDB_EATTR_MAX + 1];
257 struct br_mdb_entry *e;
258 struct rtattr *i;
259 int rem;
260
261 rem = RTA_PAYLOAD(attr);
262 for (i = RTA_DATA(attr); RTA_OK(i, rem); i = RTA_NEXT(i, rem)) {
263 e = RTA_DATA(i);
264 parse_rtattr_flags(etb, MDBA_MDB_EATTR_MAX, MDB_RTA(RTA_DATA(i)),
265 RTA_PAYLOAD(i) - RTA_ALIGN(sizeof(*e)),
266 NLA_F_NESTED);
267 print_mdb_entry(f, ifindex, e, n, etb);
268 }
269 }
270
271 static void print_mdb_entries(FILE *fp, struct nlmsghdr *n,
272 int ifindex, struct rtattr *mdb)
273 {
274 int rem = RTA_PAYLOAD(mdb);
275 struct rtattr *i;
276
277 for (i = RTA_DATA(mdb); RTA_OK(i, rem); i = RTA_NEXT(i, rem))
278 br_print_mdb_entry(fp, ifindex, i, n);
279 }
280
281 static void print_router_entries(FILE *fp, struct nlmsghdr *n,
282 int ifindex, struct rtattr *router)
283 {
284 const char *brifname = ll_index_to_name(ifindex);
285
286 if (n->nlmsg_type == RTM_GETMDB) {
287 if (show_details)
288 br_print_router_ports(fp, router, brifname);
289 } else {
290 struct rtattr *i = RTA_DATA(router);
291 uint32_t *port_ifindex = RTA_DATA(i);
292 const char *port_name = ll_index_to_name(*port_ifindex);
293
294 if (is_json_context()) {
295 open_json_array(PRINT_JSON, brifname);
296 open_json_object(NULL);
297
298 print_string(PRINT_JSON, "port", NULL,
299 port_name);
300 close_json_object();
301 close_json_array(PRINT_JSON, NULL);
302 } else {
303 fprintf(fp, "router port dev %s master %s\n",
304 port_name, brifname);
305 }
306 }
307 }
308
309 static int __parse_mdb_nlmsg(struct nlmsghdr *n, struct rtattr **tb)
310 {
311 struct br_port_msg *r = NLMSG_DATA(n);
312 int len = n->nlmsg_len;
313
314 if (n->nlmsg_type != RTM_GETMDB &&
315 n->nlmsg_type != RTM_NEWMDB &&
316 n->nlmsg_type != RTM_DELMDB) {
317 fprintf(stderr,
318 "Not RTM_GETMDB, RTM_NEWMDB or RTM_DELMDB: %08x %08x %08x\n",
319 n->nlmsg_len, n->nlmsg_type, n->nlmsg_flags);
320
321 return 0;
322 }
323
324 len -= NLMSG_LENGTH(sizeof(*r));
325 if (len < 0) {
326 fprintf(stderr, "BUG: wrong nlmsg len %d\n", len);
327 return -1;
328 }
329
330 if (filter_index && filter_index != r->ifindex)
331 return 0;
332
333 parse_rtattr(tb, MDBA_MAX, MDBA_RTA(r), n->nlmsg_len - NLMSG_LENGTH(sizeof(*r)));
334
335 return 1;
336 }
337
338 static int print_mdbs(struct nlmsghdr *n, void *arg)
339 {
340 struct br_port_msg *r = NLMSG_DATA(n);
341 struct rtattr *tb[MDBA_MAX+1];
342 FILE *fp = arg;
343 int ret;
344
345 ret = __parse_mdb_nlmsg(n, tb);
346 if (ret != 1)
347 return ret;
348
349 if (tb[MDBA_MDB])
350 print_mdb_entries(fp, n, r->ifindex, tb[MDBA_MDB]);
351
352 return 0;
353 }
354
355 static int print_rtrs(struct nlmsghdr *n, void *arg)
356 {
357 struct br_port_msg *r = NLMSG_DATA(n);
358 struct rtattr *tb[MDBA_MAX+1];
359 FILE *fp = arg;
360 int ret;
361
362 ret = __parse_mdb_nlmsg(n, tb);
363 if (ret != 1)
364 return ret;
365
366 if (tb[MDBA_ROUTER])
367 print_router_entries(fp, n, r->ifindex, tb[MDBA_ROUTER]);
368
369 return 0;
370 }
371
372 int print_mdb_mon(struct nlmsghdr *n, void *arg)
373 {
374 struct br_port_msg *r = NLMSG_DATA(n);
375 struct rtattr *tb[MDBA_MAX+1];
376 FILE *fp = arg;
377 int ret;
378
379 ret = __parse_mdb_nlmsg(n, tb);
380 if (ret != 1)
381 return ret;
382
383 if (n->nlmsg_type == RTM_DELMDB)
384 print_bool(PRINT_ANY, "deleted", "Deleted ", true);
385
386 if (tb[MDBA_MDB])
387 print_mdb_entries(fp, n, r->ifindex, tb[MDBA_MDB]);
388
389 if (tb[MDBA_ROUTER])
390 print_router_entries(fp, n, r->ifindex, tb[MDBA_ROUTER]);
391
392 return 0;
393 }
394
395 static int mdb_show(int argc, char **argv)
396 {
397 char *filter_dev = NULL;
398
399 while (argc > 0) {
400 if (strcmp(*argv, "dev") == 0) {
401 NEXT_ARG();
402 if (filter_dev)
403 duparg("dev", *argv);
404 filter_dev = *argv;
405 } else if (strcmp(*argv, "vid") == 0) {
406 NEXT_ARG();
407 if (filter_vlan)
408 duparg("vid", *argv);
409 filter_vlan = atoi(*argv);
410 }
411 argc--; argv++;
412 }
413
414 if (filter_dev) {
415 filter_index = ll_name_to_index(filter_dev);
416 if (!filter_index)
417 return nodev(filter_dev);
418 }
419
420 new_json_obj(json);
421 open_json_object(NULL);
422
423 /* get mdb entries */
424 if (rtnl_mdbdump_req(&rth, PF_BRIDGE) < 0) {
425 perror("Cannot send dump request");
426 return -1;
427 }
428
429 open_json_array(PRINT_JSON, "mdb");
430 if (rtnl_dump_filter(&rth, print_mdbs, stdout) < 0) {
431 fprintf(stderr, "Dump terminated\n");
432 return -1;
433 }
434 close_json_array(PRINT_JSON, NULL);
435
436 /* get router ports */
437 if (rtnl_mdbdump_req(&rth, PF_BRIDGE) < 0) {
438 perror("Cannot send dump request");
439 return -1;
440 }
441
442 open_json_object("router");
443 if (rtnl_dump_filter(&rth, print_rtrs, stdout) < 0) {
444 fprintf(stderr, "Dump terminated\n");
445 return -1;
446 }
447 close_json_object();
448
449 close_json_object();
450 delete_json_obj();
451 fflush(stdout);
452
453 return 0;
454 }
455
456 static int mdb_parse_grp(const char *grp, struct br_mdb_entry *e)
457 {
458 if (inet_pton(AF_INET, grp, &e->addr.u.ip4)) {
459 e->addr.proto = htons(ETH_P_IP);
460 return 0;
461 }
462 if (inet_pton(AF_INET6, grp, &e->addr.u.ip6)) {
463 e->addr.proto = htons(ETH_P_IPV6);
464 return 0;
465 }
466 if (ll_addr_a2n((char *)e->addr.u.mac_addr, sizeof(e->addr.u.mac_addr),
467 grp) == ETH_ALEN) {
468 e->addr.proto = 0;
469 return 0;
470 }
471
472 return -1;
473 }
474
475 static int mdb_modify(int cmd, int flags, int argc, char **argv)
476 {
477 struct {
478 struct nlmsghdr n;
479 struct br_port_msg bpm;
480 char buf[1024];
481 } req = {
482 .n.nlmsg_len = NLMSG_LENGTH(sizeof(struct br_port_msg)),
483 .n.nlmsg_flags = NLM_F_REQUEST | flags,
484 .n.nlmsg_type = cmd,
485 .bpm.family = PF_BRIDGE,
486 };
487 char *d = NULL, *p = NULL, *grp = NULL, *src = NULL;
488 struct br_mdb_entry entry = {};
489 short vid = 0;
490
491 while (argc > 0) {
492 if (strcmp(*argv, "dev") == 0) {
493 NEXT_ARG();
494 d = *argv;
495 } else if (strcmp(*argv, "grp") == 0) {
496 NEXT_ARG();
497 grp = *argv;
498 } else if (strcmp(*argv, "port") == 0) {
499 NEXT_ARG();
500 p = *argv;
501 } else if (strcmp(*argv, "permanent") == 0) {
502 if (cmd == RTM_NEWMDB)
503 entry.state |= MDB_PERMANENT;
504 } else if (strcmp(*argv, "temp") == 0) {
505 ;/* nothing */
506 } else if (strcmp(*argv, "vid") == 0) {
507 NEXT_ARG();
508 vid = atoi(*argv);
509 } else if (strcmp(*argv, "src") == 0) {
510 NEXT_ARG();
511 src = *argv;
512 } else {
513 if (matches(*argv, "help") == 0)
514 usage();
515 }
516 argc--; argv++;
517 }
518
519 if (d == NULL || grp == NULL || p == NULL) {
520 fprintf(stderr, "Device, group address and port name are required arguments.\n");
521 return -1;
522 }
523
524 req.bpm.ifindex = ll_name_to_index(d);
525 if (!req.bpm.ifindex)
526 return nodev(d);
527
528 entry.ifindex = ll_name_to_index(p);
529 if (!entry.ifindex)
530 return nodev(p);
531
532 if (mdb_parse_grp(grp, &entry)) {
533 fprintf(stderr, "Invalid address \"%s\"\n", grp);
534 return -1;
535 }
536
537 entry.vid = vid;
538 addattr_l(&req.n, sizeof(req), MDBA_SET_ENTRY, &entry, sizeof(entry));
539 if (src) {
540 struct rtattr *nest = addattr_nest(&req.n, sizeof(req),
541 MDBA_SET_ENTRY_ATTRS);
542 struct in6_addr src_ip6;
543 __be32 src_ip4;
544
545 nest->rta_type |= NLA_F_NESTED;
546 if (!inet_pton(AF_INET, src, &src_ip4)) {
547 if (!inet_pton(AF_INET6, src, &src_ip6)) {
548 fprintf(stderr, "Invalid source address \"%s\"\n", src);
549 return -1;
550 }
551 addattr_l(&req.n, sizeof(req), MDBE_ATTR_SOURCE, &src_ip6, sizeof(src_ip6));
552 } else {
553 addattr32(&req.n, sizeof(req), MDBE_ATTR_SOURCE, src_ip4);
554 }
555 addattr_nest_end(&req.n, nest);
556 }
557
558 if (rtnl_talk(&rth, &req.n, NULL) < 0)
559 return -1;
560
561 return 0;
562 }
563
564 int do_mdb(int argc, char **argv)
565 {
566 ll_init_map(&rth);
567
568 if (argc > 0) {
569 if (matches(*argv, "add") == 0)
570 return mdb_modify(RTM_NEWMDB, NLM_F_CREATE|NLM_F_EXCL, argc-1, argv+1);
571 if (matches(*argv, "delete") == 0)
572 return mdb_modify(RTM_DELMDB, 0, argc-1, argv+1);
573
574 if (matches(*argv, "show") == 0 ||
575 matches(*argv, "lst") == 0 ||
576 matches(*argv, "list") == 0)
577 return mdb_show(argc-1, argv+1);
578 if (matches(*argv, "help") == 0)
579 usage();
580 } else
581 return mdb_show(0, NULL);
582
583 fprintf(stderr, "Command \"%s\" is unknown, try \"bridge mdb help\".\n", *argv);
584 exit(-1);
585 }