]> git.proxmox.com Git - mirror_iproute2.git/blame - bridge/mdb.c
bridge: mdb: add support for source address
[mirror_iproute2.git] / bridge / mdb.c
CommitLineData
6054c1eb 1/* SPDX-License-Identifier: GPL-2.0 */
e06c7f7e
CW
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"
c7c1a1ef 22#include "json_print.h"
e06c7f7e
CW
23
24#ifndef MDBA_RTA
25#define MDBA_RTA(r) \
df4b043f 26 ((struct rtattr *)(((char *)(r)) + NLMSG_ALIGN(sizeof(struct br_port_msg))))
e06c7f7e
CW
27#endif
28
24687d67 29static unsigned int filter_index, filter_vlan;
e06c7f7e
CW
30
31static void usage(void)
32{
8589eb4e 33 fprintf(stderr,
547b3197 34 "Usage: bridge mdb { add | del } dev DEV port PORT grp GROUP [src SOURCE] [permanent | temp] [vid VID]\n"
8589eb4e 35 " bridge mdb {show} [ dev DEV ] [ vid VID ]\n");
e06c7f7e
CW
36 exit(-1);
37}
38
ba037267
NA
39static bool is_temp_mcast_rtr(__u8 type)
40{
41 return type == MDB_RTR_TYPE_TEMP_QUERY || type == MDB_RTR_TYPE_TEMP;
42}
43
c7c1a1ef
SH
44static const char *format_timer(__u32 ticks)
45{
46 struct timeval tv;
47 static char tbuf[32];
48
49 __jiffies_to_tv(&tv, ticks);
50 snprintf(tbuf, sizeof(tbuf), "%4lu.%.2lu",
51 (unsigned long)tv.tv_sec,
52 (unsigned long)tv.tv_usec / 10000);
53
54 return tbuf;
55}
56
ba037267
NA
57static void __print_router_port_stats(FILE *f, struct rtattr *pattr)
58{
59 struct rtattr *tb[MDBA_ROUTER_PATTR_MAX + 1];
ba037267
NA
60
61 parse_rtattr(tb, MDBA_ROUTER_PATTR_MAX, MDB_RTR_RTA(RTA_DATA(pattr)),
62 RTA_PAYLOAD(pattr) - RTA_ALIGN(sizeof(uint32_t)));
c7c1a1ef 63
ba037267 64 if (tb[MDBA_ROUTER_PATTR_TIMER]) {
c7c1a1ef
SH
65 __u32 timer = rta_getattr_u32(tb[MDBA_ROUTER_PATTR_TIMER]);
66
67 print_string(PRINT_ANY, "timer", " %s",
68 format_timer(timer));
ba037267 69 }
c7c1a1ef 70
ba037267 71 if (tb[MDBA_ROUTER_PATTR_TYPE]) {
c7c1a1ef
SH
72 __u8 type = rta_getattr_u8(tb[MDBA_ROUTER_PATTR_TYPE]);
73
74 print_string(PRINT_ANY, "type", " %s",
75 is_temp_mcast_rtr(type) ? "temp" : "permanent");
ba037267
NA
76 }
77}
78
c7c1a1ef
SH
79static void br_print_router_ports(FILE *f, struct rtattr *attr,
80 const char *brifname)
e06c7f7e 81{
c7c1a1ef 82 int rem = RTA_PAYLOAD(attr);
e06c7f7e 83 struct rtattr *i;
e06c7f7e 84
c7c1a1ef
SH
85 if (is_json_context())
86 open_json_array(PRINT_JSON, brifname);
87 else if (!show_stats)
88 fprintf(f, "router ports on %s: ", brifname);
89
90 for (i = RTA_DATA(attr); RTA_OK(i, rem); i = RTA_NEXT(i, rem)) {
91 uint32_t *port_ifindex = RTA_DATA(i);
92 const char *port_ifname = ll_index_to_name(*port_ifindex);
93
94 if (is_json_context()) {
95 open_json_object(NULL);
96 print_string(PRINT_JSON, "port", NULL, port_ifname);
97
44e0f6f3
NG
98 if (show_stats)
99 __print_router_port_stats(f, i);
c7c1a1ef
SH
100 close_json_object();
101 } else if (show_stats) {
102 fprintf(f, "router ports on %s: %s",
103 brifname, port_ifname);
104
105 __print_router_port_stats(f, i);
44e0f6f3 106 fprintf(f, "\n");
c7c1a1ef
SH
107 } else {
108 fprintf(f, "%s ", port_ifname);
109 }
e06c7f7e 110 }
92bba4ed
HL
111
112 if (!show_stats)
113 print_nl();
114
c7c1a1ef 115 close_json_array(PRINT_JSON, NULL);
44e0f6f3
NG
116}
117
c7c1a1ef 118static void print_mdb_entry(FILE *f, int ifindex, const struct br_mdb_entry *e,
05d4f64d 119 struct nlmsghdr *n, struct rtattr **tb)
e06c7f7e 120{
547b3197 121 const void *grp, *src;
e06c7f7e 122 SPRINT_BUF(abuf);
c7c1a1ef 123 const char *dev;
6aac8617
NA
124 int af;
125
24687d67
NA
126 if (filter_vlan && e->vid != filter_vlan)
127 return;
c7c1a1ef 128
6aac8617 129 af = e->addr.proto == htons(ETH_P_IP) ? AF_INET : AF_INET6;
547b3197 130 grp = af == AF_INET ? (const void *)&e->addr.u.ip4 :
6aac8617 131 (const void *)&e->addr.u.ip6;
c7c1a1ef
SH
132 dev = ll_index_to_name(ifindex);
133
134 open_json_object(NULL);
135
a9661b8b
NA
136 print_int(PRINT_JSON, "index", NULL, ifindex);
137 print_color_string(PRINT_ANY, COLOR_IFNAME, "dev", "dev %s", dev);
138 print_string(PRINT_ANY, "port", " port %s",
c7c1a1ef 139 ll_index_to_name(e->ifindex));
44e0f6f3 140
c7c1a1ef 141 print_color_string(PRINT_ANY, ifa_family_color(af),
a9661b8b 142 "grp", " grp %s",
547b3197
NA
143 inet_ntop(af, grp, abuf, sizeof(abuf)));
144 if (tb && tb[MDBA_MDB_EATTR_SOURCE]) {
145 src = (const void *)RTA_DATA(tb[MDBA_MDB_EATTR_SOURCE]);
146 print_color_string(PRINT_ANY, ifa_family_color(af),
147 "src", " src %s",
148 inet_ntop(af, src, abuf, sizeof(abuf)));
149 }
a9661b8b 150 print_string(PRINT_ANY, "state", " %s",
c7c1a1ef
SH
151 (e->state & MDB_PERMANENT) ? "permanent" : "temp");
152
153 open_json_array(PRINT_JSON, "flags");
154 if (e->flags & MDB_FLAGS_OFFLOAD)
a9661b8b 155 print_string(PRINT_ANY, NULL, " %s", "offload");
c7c1a1ef
SH
156 close_json_array(PRINT_JSON, NULL);
157
158 if (e->vid)
159 print_uint(PRINT_ANY, "vid", " vid %u", e->vid);
160
161 if (show_stats && tb && tb[MDBA_MDB_EATTR_TIMER]) {
162 __u32 timer = rta_getattr_u32(tb[MDBA_MDB_EATTR_TIMER]);
163
164 print_string(PRINT_ANY, "timer", " %s",
165 format_timer(timer));
05d4f64d 166 }
92bba4ed
HL
167
168 print_nl();
c7c1a1ef 169 close_json_object();
e06c7f7e
CW
170}
171
90d73159
NA
172static void br_print_mdb_entry(FILE *f, int ifindex, struct rtattr *attr,
173 struct nlmsghdr *n)
e06c7f7e 174{
05d4f64d
NA
175 struct rtattr *etb[MDBA_MDB_EATTR_MAX + 1];
176 struct br_mdb_entry *e;
e06c7f7e
CW
177 struct rtattr *i;
178 int rem;
e06c7f7e
CW
179
180 rem = RTA_PAYLOAD(attr);
181 for (i = RTA_DATA(attr); RTA_OK(i, rem); i = RTA_NEXT(i, rem)) {
182 e = RTA_DATA(i);
05d4f64d
NA
183 parse_rtattr(etb, MDBA_MDB_EATTR_MAX, MDB_RTA(RTA_DATA(i)),
184 RTA_PAYLOAD(i) - RTA_ALIGN(sizeof(*e)));
185 print_mdb_entry(f, ifindex, e, n, etb);
e06c7f7e
CW
186 }
187}
188
c7c1a1ef
SH
189static void print_mdb_entries(FILE *fp, struct nlmsghdr *n,
190 int ifindex, struct rtattr *mdb)
191{
192 int rem = RTA_PAYLOAD(mdb);
193 struct rtattr *i;
194
c7c1a1ef
SH
195 for (i = RTA_DATA(mdb); RTA_OK(i, rem); i = RTA_NEXT(i, rem))
196 br_print_mdb_entry(fp, ifindex, i, n);
c7c1a1ef
SH
197}
198
199static void print_router_entries(FILE *fp, struct nlmsghdr *n,
200 int ifindex, struct rtattr *router)
201{
202 const char *brifname = ll_index_to_name(ifindex);
203
c7c1a1ef
SH
204 if (n->nlmsg_type == RTM_GETMDB) {
205 if (show_details)
206 br_print_router_ports(fp, router, brifname);
207 } else {
208 struct rtattr *i = RTA_DATA(router);
209 uint32_t *port_ifindex = RTA_DATA(i);
f5fc7387 210 const char *port_name = ll_index_to_name(*port_ifindex);
c7c1a1ef
SH
211
212 if (is_json_context()) {
213 open_json_array(PRINT_JSON, brifname);
214 open_json_object(NULL);
215
216 print_string(PRINT_JSON, "port", NULL,
f5fc7387 217 port_name);
c7c1a1ef
SH
218 close_json_object();
219 close_json_array(PRINT_JSON, NULL);
220 } else {
221 fprintf(fp, "router port dev %s master %s\n",
f5fc7387 222 port_name, brifname);
c7c1a1ef
SH
223 }
224 }
c7c1a1ef
SH
225}
226
b11b495e 227static int __parse_mdb_nlmsg(struct nlmsghdr *n, struct rtattr **tb)
e06c7f7e 228{
e06c7f7e
CW
229 struct br_port_msg *r = NLMSG_DATA(n);
230 int len = n->nlmsg_len;
e06c7f7e 231
c7c1a1ef
SH
232 if (n->nlmsg_type != RTM_GETMDB &&
233 n->nlmsg_type != RTM_NEWMDB &&
234 n->nlmsg_type != RTM_DELMDB) {
235 fprintf(stderr,
236 "Not RTM_GETMDB, RTM_NEWMDB or RTM_DELMDB: %08x %08x %08x\n",
e06c7f7e
CW
237 n->nlmsg_len, n->nlmsg_type, n->nlmsg_flags);
238
239 return 0;
240 }
241
242 len -= NLMSG_LENGTH(sizeof(*r));
243 if (len < 0) {
244 fprintf(stderr, "BUG: wrong nlmsg len %d\n", len);
245 return -1;
246 }
247
248 if (filter_index && filter_index != r->ifindex)
249 return 0;
250
251 parse_rtattr(tb, MDBA_MAX, MDBA_RTA(r), n->nlmsg_len - NLMSG_LENGTH(sizeof(*r)));
252
b11b495e
NA
253 return 1;
254}
255
256static int print_mdbs(struct nlmsghdr *n, void *arg)
257{
258 struct br_port_msg *r = NLMSG_DATA(n);
259 struct rtattr *tb[MDBA_MAX+1];
260 FILE *fp = arg;
261 int ret;
262
263 ret = __parse_mdb_nlmsg(n, tb);
264 if (ret != 1)
265 return ret;
266
267 if (tb[MDBA_MDB])
268 print_mdb_entries(fp, n, r->ifindex, tb[MDBA_MDB]);
269
270 return 0;
271}
272
273static int print_rtrs(struct nlmsghdr *n, void *arg)
274{
275 struct br_port_msg *r = NLMSG_DATA(n);
276 struct rtattr *tb[MDBA_MAX+1];
277 FILE *fp = arg;
278 int ret;
279
280 ret = __parse_mdb_nlmsg(n, tb);
281 if (ret != 1)
282 return ret;
283
284 if (tb[MDBA_ROUTER])
285 print_router_entries(fp, n, r->ifindex, tb[MDBA_ROUTER]);
286
287 return 0;
288}
289
290int print_mdb_mon(struct nlmsghdr *n, void *arg)
291{
292 struct br_port_msg *r = NLMSG_DATA(n);
293 struct rtattr *tb[MDBA_MAX+1];
294 FILE *fp = arg;
295 int ret;
296
297 ret = __parse_mdb_nlmsg(n, tb);
298 if (ret != 1)
299 return ret;
300
c7c1a1ef
SH
301 if (n->nlmsg_type == RTM_DELMDB)
302 print_bool(PRINT_ANY, "deleted", "Deleted ", true);
e06c7f7e 303
c7c1a1ef
SH
304 if (tb[MDBA_MDB])
305 print_mdb_entries(fp, n, r->ifindex, tb[MDBA_MDB]);
e06c7f7e 306
c7c1a1ef
SH
307 if (tb[MDBA_ROUTER])
308 print_router_entries(fp, n, r->ifindex, tb[MDBA_ROUTER]);
4d45bf3b 309
e06c7f7e
CW
310 return 0;
311}
312
313static int mdb_show(int argc, char **argv)
314{
315 char *filter_dev = NULL;
316
317 while (argc > 0) {
318 if (strcmp(*argv, "dev") == 0) {
319 NEXT_ARG();
320 if (filter_dev)
321 duparg("dev", *argv);
322 filter_dev = *argv;
24687d67
NA
323 } else if (strcmp(*argv, "vid") == 0) {
324 NEXT_ARG();
325 if (filter_vlan)
326 duparg("vid", *argv);
327 filter_vlan = atoi(*argv);
e06c7f7e
CW
328 }
329 argc--; argv++;
330 }
331
332 if (filter_dev) {
7a14358b 333 filter_index = ll_name_to_index(filter_dev);
fe99adbc
SP
334 if (!filter_index)
335 return nodev(filter_dev);
e06c7f7e
CW
336 }
337
c7c1a1ef 338 new_json_obj(json);
b11b495e 339 open_json_object(NULL);
c7c1a1ef 340
b11b495e 341 /* get mdb entries */
9dbe6df4 342 if (rtnl_mdbdump_req(&rth, PF_BRIDGE) < 0) {
44e0f6f3
NG
343 perror("Cannot send dump request");
344 return -1;
345 }
346
b11b495e
NA
347 open_json_array(PRINT_JSON, "mdb");
348 if (rtnl_dump_filter(&rth, print_mdbs, stdout) < 0) {
44e0f6f3
NG
349 fprintf(stderr, "Dump terminated\n");
350 return -1;
351 }
b11b495e
NA
352 close_json_array(PRINT_JSON, NULL);
353
354 /* get router ports */
355 if (rtnl_mdbdump_req(&rth, PF_BRIDGE) < 0) {
356 perror("Cannot send dump request");
357 return -1;
358 }
e06c7f7e 359
b11b495e
NA
360 open_json_object("router");
361 if (rtnl_dump_filter(&rth, print_rtrs, stdout) < 0) {
362 fprintf(stderr, "Dump terminated\n");
363 return -1;
364 }
365 close_json_object();
366
367 close_json_object();
c7c1a1ef
SH
368 delete_json_obj();
369 fflush(stdout);
e06c7f7e
CW
370
371 return 0;
372}
373
9dca6767
CW
374static int mdb_modify(int cmd, int flags, int argc, char **argv)
375{
376 struct {
df4b043f 377 struct nlmsghdr n;
9dca6767 378 struct br_port_msg bpm;
df4b043f 379 char buf[1024];
d17b136f
PS
380 } req = {
381 .n.nlmsg_len = NLMSG_LENGTH(sizeof(struct br_port_msg)),
382 .n.nlmsg_flags = NLM_F_REQUEST | flags,
383 .n.nlmsg_type = cmd,
384 .bpm.family = PF_BRIDGE,
385 };
547b3197 386 char *d = NULL, *p = NULL, *grp = NULL, *src = NULL;
d17b136f 387 struct br_mdb_entry entry = {};
6aac8617 388 short vid = 0;
9dca6767 389
9dca6767
CW
390 while (argc > 0) {
391 if (strcmp(*argv, "dev") == 0) {
392 NEXT_ARG();
393 d = *argv;
394 } else if (strcmp(*argv, "grp") == 0) {
395 NEXT_ARG();
396 grp = *argv;
d8b75d1a
CW
397 } else if (strcmp(*argv, "port") == 0) {
398 NEXT_ARG();
399 p = *argv;
400 } else if (strcmp(*argv, "permanent") == 0) {
401 if (cmd == RTM_NEWMDB)
402 entry.state |= MDB_PERMANENT;
403 } else if (strcmp(*argv, "temp") == 0) {
404 ;/* nothing */
6aac8617
NA
405 } else if (strcmp(*argv, "vid") == 0) {
406 NEXT_ARG();
407 vid = atoi(*argv);
547b3197
NA
408 } else if (strcmp(*argv, "src") == 0) {
409 NEXT_ARG();
410 src = *argv;
9dca6767 411 } else {
9dca6767
CW
412 if (matches(*argv, "help") == 0)
413 usage();
414 }
415 argc--; argv++;
416 }
417
418 if (d == NULL || grp == NULL || p == NULL) {
419 fprintf(stderr, "Device, group address and port name are required arguments.\n");
42ecedd4 420 return -1;
9dca6767
CW
421 }
422
423 req.bpm.ifindex = ll_name_to_index(d);
fe99adbc
SP
424 if (!req.bpm.ifindex)
425 return nodev(d);
9dca6767
CW
426
427 entry.ifindex = ll_name_to_index(p);
fe99adbc
SP
428 if (!entry.ifindex)
429 return nodev(p);
9dca6767
CW
430
431 if (!inet_pton(AF_INET, grp, &entry.addr.u.ip4)) {
432 if (!inet_pton(AF_INET6, grp, &entry.addr.u.ip6)) {
433 fprintf(stderr, "Invalid address \"%s\"\n", grp);
434 return -1;
435 } else
436 entry.addr.proto = htons(ETH_P_IPV6);
437 } else
438 entry.addr.proto = htons(ETH_P_IP);
439
6aac8617 440 entry.vid = vid;
9dca6767 441 addattr_l(&req.n, sizeof(req), MDBA_SET_ENTRY, &entry, sizeof(entry));
547b3197
NA
442 if (src) {
443 struct rtattr *nest = addattr_nest(&req.n, sizeof(req),
444 MDBA_SET_ENTRY_ATTRS);
445 struct in6_addr src_ip6;
446 __be32 src_ip4;
447
448 nest->rta_type |= NLA_F_NESTED;
449 if (!inet_pton(AF_INET, src, &src_ip4)) {
450 if (!inet_pton(AF_INET6, src, &src_ip6)) {
451 fprintf(stderr, "Invalid source address \"%s\"\n", src);
452 return -1;
453 }
454 addattr_l(&req.n, sizeof(req), MDBE_ATTR_SOURCE, &src_ip6, sizeof(src_ip6));
455 } else {
456 addattr32(&req.n, sizeof(req), MDBE_ATTR_SOURCE, src_ip4);
457 }
458 addattr_nest_end(&req.n, nest);
459 }
9dca6767 460
86bf43c7 461 if (rtnl_talk(&rth, &req.n, NULL) < 0)
42ecedd4 462 return -1;
9dca6767
CW
463
464 return 0;
465}
466
e06c7f7e
CW
467int do_mdb(int argc, char **argv)
468{
469 ll_init_map(&rth);
470
471 if (argc > 0) {
9dca6767
CW
472 if (matches(*argv, "add") == 0)
473 return mdb_modify(RTM_NEWMDB, NLM_F_CREATE|NLM_F_EXCL, argc-1, argv+1);
474 if (matches(*argv, "delete") == 0)
475 return mdb_modify(RTM_DELMDB, 0, argc-1, argv+1);
476
e06c7f7e
CW
477 if (matches(*argv, "show") == 0 ||
478 matches(*argv, "lst") == 0 ||
479 matches(*argv, "list") == 0)
480 return mdb_show(argc-1, argv+1);
481 if (matches(*argv, "help") == 0)
482 usage();
483 } else
484 return mdb_show(0, NULL);
485
486 fprintf(stderr, "Command \"%s\" is unknown, try \"bridge mdb help\".\n", *argv);
487 exit(-1);
488}