]> git.proxmox.com Git - mirror_iproute2.git/blame - bridge/mdb.c
vdpa: add .gitignore
[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
2de81d1e 44static const char *format_timer(__u32 ticks, int align)
c7c1a1ef
SH
45{
46 struct timeval tv;
47 static char tbuf[32];
48
49 __jiffies_to_tv(&tv, ticks);
2de81d1e
NA
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);
c7c1a1ef
SH
58
59 return tbuf;
60}
61
ba037267
NA
62static void __print_router_port_stats(FILE *f, struct rtattr *pattr)
63{
64 struct rtattr *tb[MDBA_ROUTER_PATTR_MAX + 1];
ba037267
NA
65
66 parse_rtattr(tb, MDBA_ROUTER_PATTR_MAX, MDB_RTR_RTA(RTA_DATA(pattr)),
67 RTA_PAYLOAD(pattr) - RTA_ALIGN(sizeof(uint32_t)));
c7c1a1ef 68
ba037267 69 if (tb[MDBA_ROUTER_PATTR_TIMER]) {
c7c1a1ef
SH
70 __u32 timer = rta_getattr_u32(tb[MDBA_ROUTER_PATTR_TIMER]);
71
72 print_string(PRINT_ANY, "timer", " %s",
2de81d1e 73 format_timer(timer, 1));
ba037267 74 }
c7c1a1ef 75
ba037267 76 if (tb[MDBA_ROUTER_PATTR_TYPE]) {
c7c1a1ef
SH
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");
ba037267
NA
81 }
82}
83
c7c1a1ef
SH
84static void br_print_router_ports(FILE *f, struct rtattr *attr,
85 const char *brifname)
e06c7f7e 86{
c7c1a1ef 87 int rem = RTA_PAYLOAD(attr);
e06c7f7e 88 struct rtattr *i;
e06c7f7e 89
c7c1a1ef
SH
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
44e0f6f3
NG
103 if (show_stats)
104 __print_router_port_stats(f, i);
c7c1a1ef
SH
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);
44e0f6f3 111 fprintf(f, "\n");
c7c1a1ef
SH
112 } else {
113 fprintf(f, "%s ", port_ifname);
114 }
e06c7f7e 115 }
92bba4ed
HL
116
117 if (!show_stats)
118 print_nl();
119
c7c1a1ef 120 close_json_array(PRINT_JSON, NULL);
44e0f6f3
NG
121}
122
2de81d1e
NA
123static 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
c7c1a1ef 148static void print_mdb_entry(FILE *f, int ifindex, const struct br_mdb_entry *e,
05d4f64d 149 struct nlmsghdr *n, struct rtattr **tb)
e06c7f7e 150{
547b3197 151 const void *grp, *src;
c29f65db 152 const char *addr;
e06c7f7e 153 SPRINT_BUF(abuf);
c7c1a1ef 154 const char *dev;
6aac8617
NA
155 int af;
156
24687d67
NA
157 if (filter_vlan && e->vid != filter_vlan)
158 return;
c7c1a1ef 159
c29f65db
VO
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 }
c7c1a1ef
SH
170 dev = ll_index_to_name(ifindex);
171
172 open_json_object(NULL);
173
a9661b8b
NA
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",
c7c1a1ef 177 ll_index_to_name(e->ifindex));
44e0f6f3 178
c29f65db
VO
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
c7c1a1ef 184 print_color_string(PRINT_ANY, ifa_family_color(af),
c29f65db
VO
185 "grp", " grp %s", addr);
186
547b3197
NA
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 }
a9661b8b 193 print_string(PRINT_ANY, "state", " %s",
c7c1a1ef 194 (e->state & MDB_PERMANENT) ? "permanent" : "temp");
2de81d1e
NA
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]);
c7c1a1ef 198
2de81d1e
NA
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 }
86588450
NA
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 }
1d28c480
NA
226 }
227
c7c1a1ef
SH
228 open_json_array(PRINT_JSON, "flags");
229 if (e->flags & MDB_FLAGS_OFFLOAD)
a9661b8b 230 print_string(PRINT_ANY, NULL, " %s", "offload");
f94e8b07
NA
231 if (e->flags & MDB_FLAGS_FAST_LEAVE)
232 print_string(PRINT_ANY, NULL, " %s", "fast_leave");
e331677e
NA
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");
c7c1a1ef
SH
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",
2de81d1e 246 format_timer(timer, 1));
05d4f64d 247 }
92bba4ed
HL
248
249 print_nl();
c7c1a1ef 250 close_json_object();
e06c7f7e
CW
251}
252
90d73159
NA
253static void br_print_mdb_entry(FILE *f, int ifindex, struct rtattr *attr,
254 struct nlmsghdr *n)
e06c7f7e 255{
05d4f64d
NA
256 struct rtattr *etb[MDBA_MDB_EATTR_MAX + 1];
257 struct br_mdb_entry *e;
e06c7f7e
CW
258 struct rtattr *i;
259 int rem;
e06c7f7e
CW
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);
2de81d1e
NA
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);
05d4f64d 267 print_mdb_entry(f, ifindex, e, n, etb);
e06c7f7e
CW
268 }
269}
270
c7c1a1ef
SH
271static 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
c7c1a1ef
SH
277 for (i = RTA_DATA(mdb); RTA_OK(i, rem); i = RTA_NEXT(i, rem))
278 br_print_mdb_entry(fp, ifindex, i, n);
c7c1a1ef
SH
279}
280
281static 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
c7c1a1ef
SH
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);
f5fc7387 292 const char *port_name = ll_index_to_name(*port_ifindex);
c7c1a1ef
SH
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,
f5fc7387 299 port_name);
c7c1a1ef
SH
300 close_json_object();
301 close_json_array(PRINT_JSON, NULL);
302 } else {
303 fprintf(fp, "router port dev %s master %s\n",
f5fc7387 304 port_name, brifname);
c7c1a1ef
SH
305 }
306 }
c7c1a1ef
SH
307}
308
b11b495e 309static int __parse_mdb_nlmsg(struct nlmsghdr *n, struct rtattr **tb)
e06c7f7e 310{
e06c7f7e
CW
311 struct br_port_msg *r = NLMSG_DATA(n);
312 int len = n->nlmsg_len;
e06c7f7e 313
c7c1a1ef
SH
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",
e06c7f7e
CW
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
b11b495e
NA
335 return 1;
336}
337
338static 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
355static 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
372int 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
c7c1a1ef
SH
383 if (n->nlmsg_type == RTM_DELMDB)
384 print_bool(PRINT_ANY, "deleted", "Deleted ", true);
e06c7f7e 385
c7c1a1ef
SH
386 if (tb[MDBA_MDB])
387 print_mdb_entries(fp, n, r->ifindex, tb[MDBA_MDB]);
e06c7f7e 388
c7c1a1ef
SH
389 if (tb[MDBA_ROUTER])
390 print_router_entries(fp, n, r->ifindex, tb[MDBA_ROUTER]);
4d45bf3b 391
e06c7f7e
CW
392 return 0;
393}
394
395static 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;
24687d67
NA
405 } else if (strcmp(*argv, "vid") == 0) {
406 NEXT_ARG();
407 if (filter_vlan)
408 duparg("vid", *argv);
409 filter_vlan = atoi(*argv);
e06c7f7e
CW
410 }
411 argc--; argv++;
412 }
413
414 if (filter_dev) {
7a14358b 415 filter_index = ll_name_to_index(filter_dev);
fe99adbc
SP
416 if (!filter_index)
417 return nodev(filter_dev);
e06c7f7e
CW
418 }
419
c7c1a1ef 420 new_json_obj(json);
b11b495e 421 open_json_object(NULL);
c7c1a1ef 422
b11b495e 423 /* get mdb entries */
9dbe6df4 424 if (rtnl_mdbdump_req(&rth, PF_BRIDGE) < 0) {
44e0f6f3
NG
425 perror("Cannot send dump request");
426 return -1;
427 }
428
b11b495e
NA
429 open_json_array(PRINT_JSON, "mdb");
430 if (rtnl_dump_filter(&rth, print_mdbs, stdout) < 0) {
44e0f6f3
NG
431 fprintf(stderr, "Dump terminated\n");
432 return -1;
433 }
b11b495e
NA
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 }
e06c7f7e 441
b11b495e
NA
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();
c7c1a1ef
SH
450 delete_json_obj();
451 fflush(stdout);
e06c7f7e
CW
452
453 return 0;
454}
455
c29f65db
VO
456static 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
9dca6767
CW
475static int mdb_modify(int cmd, int flags, int argc, char **argv)
476{
477 struct {
df4b043f 478 struct nlmsghdr n;
9dca6767 479 struct br_port_msg bpm;
df4b043f 480 char buf[1024];
d17b136f
PS
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 };
547b3197 487 char *d = NULL, *p = NULL, *grp = NULL, *src = NULL;
d17b136f 488 struct br_mdb_entry entry = {};
6aac8617 489 short vid = 0;
9dca6767 490
9dca6767
CW
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;
d8b75d1a
CW
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 */
6aac8617
NA
506 } else if (strcmp(*argv, "vid") == 0) {
507 NEXT_ARG();
508 vid = atoi(*argv);
547b3197
NA
509 } else if (strcmp(*argv, "src") == 0) {
510 NEXT_ARG();
511 src = *argv;
9dca6767 512 } else {
9dca6767
CW
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");
42ecedd4 521 return -1;
9dca6767
CW
522 }
523
524 req.bpm.ifindex = ll_name_to_index(d);
fe99adbc
SP
525 if (!req.bpm.ifindex)
526 return nodev(d);
9dca6767
CW
527
528 entry.ifindex = ll_name_to_index(p);
fe99adbc
SP
529 if (!entry.ifindex)
530 return nodev(p);
9dca6767 531
c29f65db
VO
532 if (mdb_parse_grp(grp, &entry)) {
533 fprintf(stderr, "Invalid address \"%s\"\n", grp);
534 return -1;
535 }
9dca6767 536
6aac8617 537 entry.vid = vid;
9dca6767 538 addattr_l(&req.n, sizeof(req), MDBA_SET_ENTRY, &entry, sizeof(entry));
547b3197
NA
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 }
9dca6767 557
86bf43c7 558 if (rtnl_talk(&rth, &req.n, NULL) < 0)
42ecedd4 559 return -1;
9dca6767
CW
560
561 return 0;
562}
563
e06c7f7e
CW
564int do_mdb(int argc, char **argv)
565{
566 ll_init_map(&rth);
567
568 if (argc > 0) {
9dca6767
CW
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
e06c7f7e
CW
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}