1
2
3
4
5
6
7
8
9
10#include <stdio.h>
11#include <stdlib.h>
12#include <unistd.h>
13#include <netdb.h>
14#include <time.h>
15#include <fcntl.h>
16#include <sys/socket.h>
17#include <sys/time.h>
18#include <net/if.h>
19#include <netinet/in.h>
20#include <linux/if_bridge.h>
21#include <linux/if_ether.h>
22#include <linux/neighbour.h>
23#include <string.h>
24#include <limits.h>
25#include <stdbool.h>
26
27#include "json_print.h"
28#include "libnetlink.h"
29#include "br_common.h"
30#include "rt_names.h"
31#include "utils.h"
32
33static unsigned int filter_index, filter_dynamic, filter_master,
34 filter_state, filter_vlan;
35
36static void usage(void)
37{
38 fprintf(stderr,
39 "Usage: bridge fdb { add | append | del | replace } ADDR dev DEV\n"
40 " [ self ] [ master ] [ use ] [ router ] [ extern_learn ]\n"
41 " [ sticky ] [ local | static | dynamic ] [ vlan VID ]\n"
42 " { [ dst IPADDR ] [ port PORT] [ vni VNI ] | [ nhid NHID ] }\n"
43 " [ via DEV ] [ src_vni VNI ] [ activity_notify ]\n"
44 " [ inactive ] [ norefresh ]\n"
45 " bridge fdb [ show [ br BRDEV ] [ brport DEV ] [ vlan VID ]\n"
46 " [ state STATE ] [ dynamic ] ]\n"
47 " bridge fdb get [ to ] LLADDR [ br BRDEV ] { brport | dev } DEV\n"
48 " [ vlan VID ] [ vni VNI ] [ self ] [ master ] [ dynamic ]\n"
49 " bridge fdb flush dev DEV [ brport DEV ] [ vlan VID ] [ src_vni VNI ]\n"
50 " [ nhid NHID ] [ vni VNI ] [ port PORT ] [ dst IPADDR ] [ self ]\n"
51 " [ master ] [ [no]permanent | [no]static | [no]dynamic ]\n"
52 " [ [no]added_by_user ] [ [no]extern_learn ] [ [no]sticky ]\n"
53 " [ [no]offloaded ] [ [no]router ]\n");
54 exit(-1);
55}
56
57static const char *state_n2a(unsigned int s)
58{
59 static char buf[32];
60
61 if (s & NUD_PERMANENT)
62 return "permanent";
63
64 if (s & NUD_NOARP)
65 return "static";
66
67 if (s & NUD_STALE)
68 return "stale";
69
70 if (s & NUD_REACHABLE)
71 return "";
72
73 if (is_json_context())
74 sprintf(buf, "%#x", s);
75 else
76 sprintf(buf, "state=%#x", s);
77 return buf;
78}
79
80static int state_a2n(unsigned int *s, const char *arg)
81{
82 if (matches(arg, "permanent") == 0)
83 *s = NUD_PERMANENT;
84 else if (matches(arg, "static") == 0 || matches(arg, "temp") == 0)
85 *s = NUD_NOARP;
86 else if (matches(arg, "stale") == 0)
87 *s = NUD_STALE;
88 else if (matches(arg, "reachable") == 0 || matches(arg, "dynamic") == 0)
89 *s = NUD_REACHABLE;
90 else if (strcmp(arg, "all") == 0)
91 *s = ~0;
92 else if (get_unsigned(s, arg, 0))
93 return -1;
94
95 return 0;
96}
97
98static void fdb_print_flags(FILE *fp, unsigned int flags, __u32 ext_flags)
99{
100 open_json_array(PRINT_JSON,
101 is_json_context() ? "flags" : "");
102
103 if (flags & NTF_SELF)
104 print_string(PRINT_ANY, NULL, "%s ", "self");
105
106 if (flags & NTF_ROUTER)
107 print_string(PRINT_ANY, NULL, "%s ", "router");
108
109 if (flags & NTF_EXT_LEARNED)
110 print_string(PRINT_ANY, NULL, "%s ", "extern_learn");
111
112 if (flags & NTF_OFFLOADED)
113 print_string(PRINT_ANY, NULL, "%s ", "offload");
114
115 if (flags & NTF_MASTER)
116 print_string(PRINT_ANY, NULL, "%s ", "master");
117
118 if (flags & NTF_STICKY)
119 print_string(PRINT_ANY, NULL, "%s ", "sticky");
120
121 if (ext_flags & NTF_EXT_LOCKED)
122 print_string(PRINT_ANY, NULL, "%s ", "locked");
123
124 close_json_array(PRINT_JSON, NULL);
125}
126
127static void fdb_print_stats(FILE *fp, const struct nda_cacheinfo *ci)
128{
129 static int hz;
130
131 if (!hz)
132 hz = get_user_hz();
133
134 if (is_json_context()) {
135 print_uint(PRINT_JSON, "used", NULL,
136 ci->ndm_used / hz);
137 print_uint(PRINT_JSON, "updated", NULL,
138 ci->ndm_updated / hz);
139 } else {
140 fprintf(fp, "used %d/%d ", ci->ndm_used / hz,
141 ci->ndm_updated / hz);
142
143 }
144}
145
146static void fdb_print_ext_attrs(struct rtattr *nfea)
147{
148 struct rtattr *tb[NFEA_MAX + 1];
149
150 parse_rtattr_nested(tb, NFEA_MAX, nfea);
151
152 if (tb[NFEA_ACTIVITY_NOTIFY]) {
153 __u8 notify;
154
155 notify = rta_getattr_u8(tb[NFEA_ACTIVITY_NOTIFY]);
156 if (notify & FDB_NOTIFY_BIT)
157 print_bool(PRINT_ANY, "activity_notify",
158 "activity_notify ", true);
159 if (notify & FDB_NOTIFY_INACTIVE_BIT)
160 print_bool(PRINT_ANY, "inactive", "inactive ", true);
161 }
162}
163
164int print_fdb(struct nlmsghdr *n, void *arg)
165{
166 FILE *fp = arg;
167 struct ndmsg *r = NLMSG_DATA(n);
168 int len = n->nlmsg_len;
169 struct rtattr *tb[NDA_MAX+1];
170 __u32 ext_flags = 0;
171 __u16 vid = 0;
172
173 if (n->nlmsg_type != RTM_NEWNEIGH && n->nlmsg_type != RTM_DELNEIGH) {
174 fprintf(stderr, "Not RTM_NEWNEIGH: %08x %08x %08x\n",
175 n->nlmsg_len, n->nlmsg_type, n->nlmsg_flags);
176 return 0;
177 }
178
179 len -= NLMSG_LENGTH(sizeof(*r));
180 if (len < 0) {
181 fprintf(stderr, "BUG: wrong nlmsg len %d\n", len);
182 return -1;
183 }
184
185 if (r->ndm_family != AF_BRIDGE)
186 return 0;
187
188 if (filter_index && filter_index != r->ndm_ifindex)
189 return 0;
190
191 if (filter_state && !(r->ndm_state & filter_state))
192 return 0;
193
194 parse_rtattr_flags(tb, NDA_MAX, NDA_RTA(r),
195 n->nlmsg_len - NLMSG_LENGTH(sizeof(*r)),
196 NLA_F_NESTED);
197
198 if (tb[NDA_FLAGS_EXT])
199 ext_flags = rta_getattr_u32(tb[NDA_FLAGS_EXT]);
200
201 if (tb[NDA_VLAN])
202 vid = rta_getattr_u16(tb[NDA_VLAN]);
203
204 if (filter_vlan && filter_vlan != vid)
205 return 0;
206
207 if (filter_dynamic && (r->ndm_state & NUD_PERMANENT))
208 return 0;
209
210 print_headers(fp, "[NEIGH]");
211
212 open_json_object(NULL);
213 if (n->nlmsg_type == RTM_DELNEIGH)
214 print_bool(PRINT_ANY, "deleted", "Deleted ", true);
215
216 if (tb[NDA_LLADDR]) {
217 const char *lladdr;
218 SPRINT_BUF(b1);
219
220 lladdr = ll_addr_n2a(RTA_DATA(tb[NDA_LLADDR]),
221 RTA_PAYLOAD(tb[NDA_LLADDR]),
222 ll_index_to_type(r->ndm_ifindex),
223 b1, sizeof(b1));
224
225 print_color_string(PRINT_ANY, COLOR_MAC,
226 "mac", "%s ", lladdr);
227 }
228
229 if (!filter_index && r->ndm_ifindex) {
230 print_string(PRINT_FP, NULL, "dev ", NULL);
231
232 print_color_string(PRINT_ANY, COLOR_IFNAME,
233 "ifname", "%s ",
234 ll_index_to_name(r->ndm_ifindex));
235 }
236
237 if (tb[NDA_DST]) {
238 int family = AF_INET;
239 const char *dst;
240
241 if (RTA_PAYLOAD(tb[NDA_DST]) == sizeof(struct in6_addr))
242 family = AF_INET6;
243
244 dst = format_host(family,
245 RTA_PAYLOAD(tb[NDA_DST]),
246 RTA_DATA(tb[NDA_DST]));
247
248 print_string(PRINT_FP, NULL, "dst ", NULL);
249
250 print_color_string(PRINT_ANY,
251 ifa_family_color(family),
252 "dst", "%s ", dst);
253 }
254
255 if (vid)
256 print_uint(PRINT_ANY,
257 "vlan", "vlan %hu ", vid);
258
259 if (tb[NDA_PORT])
260 print_uint(PRINT_ANY,
261 "port", "port %u ",
262 rta_getattr_be16(tb[NDA_PORT]));
263
264 if (tb[NDA_VNI])
265 print_uint(PRINT_ANY,
266 "vni", "vni %u ",
267 rta_getattr_u32(tb[NDA_VNI]));
268
269 if (tb[NDA_SRC_VNI])
270 print_uint(PRINT_ANY,
271 "src_vni", "src_vni %u ",
272 rta_getattr_u32(tb[NDA_SRC_VNI]));
273
274 if (tb[NDA_IFINDEX]) {
275 unsigned int ifindex = rta_getattr_u32(tb[NDA_IFINDEX]);
276
277 if (tb[NDA_LINK_NETNSID])
278 print_uint(PRINT_ANY,
279 "viaIfIndex", "via ifindex %u ",
280 ifindex);
281 else
282 print_string(PRINT_ANY,
283 "viaIf", "via %s ",
284 ll_index_to_name(ifindex));
285 }
286
287 if (tb[NDA_NH_ID])
288 print_uint(PRINT_ANY, "nhid", "nhid %u ",
289 rta_getattr_u32(tb[NDA_NH_ID]));
290
291 if (tb[NDA_LINK_NETNSID])
292 print_uint(PRINT_ANY,
293 "linkNetNsId", "link-netnsid %d ",
294 rta_getattr_u32(tb[NDA_LINK_NETNSID]));
295
296 if (show_details && tb[NDA_FDB_EXT_ATTRS])
297 fdb_print_ext_attrs(tb[NDA_FDB_EXT_ATTRS]);
298
299 if (show_stats && tb[NDA_CACHEINFO])
300 fdb_print_stats(fp, RTA_DATA(tb[NDA_CACHEINFO]));
301
302 fdb_print_flags(fp, r->ndm_flags, ext_flags);
303
304
305 if (tb[NDA_MASTER])
306 print_string(PRINT_ANY, "master", "master %s ",
307 ll_index_to_name(rta_getattr_u32(tb[NDA_MASTER])));
308
309 print_string(PRINT_ANY, "state", "%s\n",
310 state_n2a(r->ndm_state));
311 close_json_object();
312 fflush(fp);
313 return 0;
314}
315
316static int fdb_linkdump_filter(struct nlmsghdr *nlh, int reqlen)
317{
318 int err;
319
320 if (filter_index) {
321 struct ifinfomsg *ifm = NLMSG_DATA(nlh);
322
323 ifm->ifi_index = filter_index;
324 }
325
326 if (filter_master) {
327 err = addattr32(nlh, reqlen, IFLA_MASTER, filter_master);
328 if (err)
329 return err;
330 }
331
332 return 0;
333}
334
335static int fdb_dump_filter(struct nlmsghdr *nlh, int reqlen)
336{
337 int err;
338
339 if (filter_index) {
340 struct ndmsg *ndm = NLMSG_DATA(nlh);
341
342 ndm->ndm_ifindex = filter_index;
343 }
344
345 if (filter_master) {
346 err = addattr32(nlh, reqlen, NDA_MASTER, filter_master);
347 if (err)
348 return err;
349 }
350
351 return 0;
352}
353
354static int fdb_show(int argc, char **argv)
355{
356 char *filter_dev = NULL;
357 char *br = NULL;
358 int rc;
359
360 while (argc > 0) {
361 if ((strcmp(*argv, "brport") == 0) || strcmp(*argv, "dev") == 0) {
362 NEXT_ARG();
363 filter_dev = *argv;
364 } else if (strcmp(*argv, "br") == 0) {
365 NEXT_ARG();
366 br = *argv;
367 } else if (strcmp(*argv, "vlan") == 0) {
368 NEXT_ARG();
369 if (filter_vlan)
370 duparg("vlan", *argv);
371 filter_vlan = atoi(*argv);
372 } else if (strcmp(*argv, "state") == 0) {
373 unsigned int state;
374
375 NEXT_ARG();
376 if (state_a2n(&state, *argv))
377 invarg("invalid state", *argv);
378 filter_state |= state;
379 } else if (strcmp(*argv, "dynamic") == 0) {
380 filter_dynamic = 1;
381 } else {
382 if (matches(*argv, "help") == 0)
383 usage();
384 }
385 argc--; argv++;
386 }
387
388 if (br) {
389 int br_ifindex = ll_name_to_index(br);
390
391 if (br_ifindex == 0) {
392 fprintf(stderr, "Cannot find bridge device \"%s\"\n", br);
393 return -1;
394 }
395 filter_master = br_ifindex;
396 }
397
398
399 if (filter_dev) {
400 filter_index = ll_name_to_index(filter_dev);
401 if (!filter_index)
402 return nodev(filter_dev);
403 }
404
405 if (rth.flags & RTNL_HANDLE_F_STRICT_CHK)
406 rc = rtnl_neighdump_req(&rth, PF_BRIDGE, fdb_dump_filter);
407 else
408 rc = rtnl_fdb_linkdump_req_filter_fn(&rth, fdb_linkdump_filter);
409 if (rc < 0) {
410 perror("Cannot send dump request");
411 exit(1);
412 }
413
414 new_json_obj(json);
415 if (rtnl_dump_filter(&rth, print_fdb, stdout) < 0) {
416 fprintf(stderr, "Dump terminated\n");
417 exit(1);
418 }
419 delete_json_obj();
420 fflush(stdout);
421
422 return 0;
423}
424
425static void fdb_add_ext_attrs(struct nlmsghdr *n, int maxlen,
426 bool activity_notify, bool inactive,
427 bool norefresh)
428{
429 struct rtattr *nest;
430
431 if (!activity_notify && !inactive && !norefresh)
432 return;
433
434 nest = addattr_nest(n, maxlen, NDA_FDB_EXT_ATTRS | NLA_F_NESTED);
435
436 if (activity_notify || inactive) {
437 __u8 notify = 0;
438
439 if (activity_notify)
440 notify |= FDB_NOTIFY_BIT;
441 if (inactive)
442 notify |= FDB_NOTIFY_INACTIVE_BIT;
443
444 addattr8(n, maxlen, NFEA_ACTIVITY_NOTIFY, notify);
445 }
446
447 if (norefresh)
448 addattr_l(n, maxlen, NFEA_DONT_REFRESH, NULL, 0);
449
450 addattr_nest_end(n, nest);
451}
452
453static int fdb_modify(int cmd, int flags, int argc, char **argv)
454{
455 struct {
456 struct nlmsghdr n;
457 struct ndmsg ndm;
458 char buf[256];
459 } req = {
460 .n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ndmsg)),
461 .n.nlmsg_flags = NLM_F_REQUEST | flags,
462 .n.nlmsg_type = cmd,
463 .ndm.ndm_family = PF_BRIDGE,
464 .ndm.ndm_state = NUD_NOARP,
465 };
466 bool activity_notify = false;
467 bool norefresh = false;
468 bool inactive = false;
469 char *addr = NULL;
470 char *d = NULL;
471 char abuf[ETH_ALEN];
472 int dst_ok = 0;
473 inet_prefix dst;
474 unsigned long port = 0;
475 unsigned long vni = ~0;
476 unsigned long src_vni = ~0;
477 unsigned int via = 0;
478 char *endptr;
479 short vid = -1;
480 __u32 nhid = 0;
481
482 while (argc > 0) {
483 if (strcmp(*argv, "dev") == 0) {
484 NEXT_ARG();
485 d = *argv;
486 } else if (strcmp(*argv, "dst") == 0) {
487 NEXT_ARG();
488 if (dst_ok)
489 duparg2("dst", *argv);
490 get_addr(&dst, *argv, preferred_family);
491 dst_ok = 1;
492 } else if (strcmp(*argv, "nhid") == 0) {
493 NEXT_ARG();
494 if (get_u32(&nhid, *argv, 0))
495 invarg("\"id\" value is invalid\n", *argv);
496 } else if (strcmp(*argv, "port") == 0) {
497
498 NEXT_ARG();
499 port = strtoul(*argv, &endptr, 0);
500 if (endptr && *endptr) {
501 struct servent *pse;
502
503 pse = getservbyname(*argv, "udp");
504 if (!pse)
505 invarg("invalid port\n", *argv);
506 port = ntohs(pse->s_port);
507 } else if (port > 0xffff)
508 invarg("invalid port\n", *argv);
509 } else if (strcmp(*argv, "vni") == 0) {
510 NEXT_ARG();
511 vni = strtoul(*argv, &endptr, 0);
512 if ((endptr && *endptr) ||
513 (vni >> 24) || vni == ULONG_MAX)
514 invarg("invalid VNI\n", *argv);
515 } else if (strcmp(*argv, "src_vni") == 0) {
516 NEXT_ARG();
517 src_vni = strtoul(*argv, &endptr, 0);
518 if ((endptr && *endptr) ||
519 (src_vni >> 24) || src_vni == ULONG_MAX)
520 invarg("invalid src VNI\n", *argv);
521 } else if (strcmp(*argv, "via") == 0) {
522 NEXT_ARG();
523 via = ll_name_to_index(*argv);
524 if (!via)
525 exit(nodev(*argv));
526 } else if (strcmp(*argv, "self") == 0) {
527 req.ndm.ndm_flags |= NTF_SELF;
528 } else if (matches(*argv, "master") == 0) {
529 req.ndm.ndm_flags |= NTF_MASTER;
530 } else if (matches(*argv, "router") == 0) {
531 req.ndm.ndm_flags |= NTF_ROUTER;
532 } else if (matches(*argv, "local") == 0 ||
533 matches(*argv, "permanent") == 0) {
534 req.ndm.ndm_state |= NUD_PERMANENT;
535 } else if (matches(*argv, "temp") == 0 ||
536 matches(*argv, "static") == 0) {
537 req.ndm.ndm_state |= NUD_REACHABLE;
538 } else if (matches(*argv, "dynamic") == 0) {
539 req.ndm.ndm_state |= NUD_REACHABLE;
540 req.ndm.ndm_state &= ~NUD_NOARP;
541 } else if (matches(*argv, "vlan") == 0) {
542 if (vid >= 0)
543 duparg2("vlan", *argv);
544 NEXT_ARG();
545 vid = atoi(*argv);
546 } else if (matches(*argv, "use") == 0) {
547 req.ndm.ndm_flags |= NTF_USE;
548 } else if (matches(*argv, "extern_learn") == 0) {
549 req.ndm.ndm_flags |= NTF_EXT_LEARNED;
550 } else if (matches(*argv, "sticky") == 0) {
551 req.ndm.ndm_flags |= NTF_STICKY;
552 } else if (strcmp(*argv, "activity_notify") == 0) {
553 activity_notify = true;
554 } else if (strcmp(*argv, "inactive") == 0) {
555 inactive = true;
556 } else if (strcmp(*argv, "norefresh") == 0) {
557 norefresh = true;
558 } else {
559 if (strcmp(*argv, "to") == 0)
560 NEXT_ARG();
561
562 if (matches(*argv, "help") == 0)
563 usage();
564 if (addr)
565 duparg2("to", *argv);
566 addr = *argv;
567 }
568 argc--; argv++;
569 }
570
571 if (d == NULL || addr == NULL) {
572 fprintf(stderr, "Device and address are required arguments.\n");
573 return -1;
574 }
575
576 if (nhid && (dst_ok || port || vni != ~0)) {
577 fprintf(stderr, "dst, port, vni are mutually exclusive with nhid\n");
578 return -1;
579 }
580
581
582 if (!(req.ndm.ndm_flags&(NTF_SELF|NTF_MASTER)))
583 req.ndm.ndm_flags |= NTF_SELF;
584
585
586 if (!(req.ndm.ndm_state&(NUD_PERMANENT|NUD_REACHABLE)))
587 req.ndm.ndm_state |= NUD_PERMANENT;
588
589 if (sscanf(addr, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx",
590 abuf, abuf+1, abuf+2,
591 abuf+3, abuf+4, abuf+5) != 6) {
592 fprintf(stderr, "Invalid mac address %s\n", addr);
593 return -1;
594 }
595
596 addattr_l(&req.n, sizeof(req), NDA_LLADDR, abuf, ETH_ALEN);
597 if (dst_ok)
598 addattr_l(&req.n, sizeof(req), NDA_DST, &dst.data, dst.bytelen);
599
600 if (vid >= 0)
601 addattr16(&req.n, sizeof(req), NDA_VLAN, vid);
602 if (nhid > 0)
603 addattr32(&req.n, sizeof(req), NDA_NH_ID, nhid);
604
605 if (port) {
606 unsigned short dport;
607
608 dport = htons((unsigned short)port);
609 addattr16(&req.n, sizeof(req), NDA_PORT, dport);
610 }
611 if (vni != ~0)
612 addattr32(&req.n, sizeof(req), NDA_VNI, vni);
613 if (src_vni != ~0)
614 addattr32(&req.n, sizeof(req), NDA_SRC_VNI, src_vni);
615 if (via)
616 addattr32(&req.n, sizeof(req), NDA_IFINDEX, via);
617
618 req.ndm.ndm_ifindex = ll_name_to_index(d);
619 if (!req.ndm.ndm_ifindex)
620 return nodev(d);
621
622 fdb_add_ext_attrs(&req.n, sizeof(req), activity_notify, inactive,
623 norefresh);
624
625 if (rtnl_talk(&rth, &req.n, NULL) < 0)
626 return -1;
627
628 return 0;
629}
630
631static int fdb_get(int argc, char **argv)
632{
633 struct {
634 struct nlmsghdr n;
635 struct ndmsg ndm;
636 char buf[1024];
637 } req = {
638 .n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ndmsg)),
639 .n.nlmsg_flags = NLM_F_REQUEST,
640 .n.nlmsg_type = RTM_GETNEIGH,
641 .ndm.ndm_family = AF_BRIDGE,
642 };
643 char *d = NULL, *br = NULL;
644 struct nlmsghdr *answer;
645 unsigned long vni = ~0;
646 char abuf[ETH_ALEN];
647 int br_ifindex = 0;
648 char *addr = NULL;
649 short vlan = -1;
650 char *endptr;
651 int ret;
652
653 while (argc > 0) {
654 if ((strcmp(*argv, "brport") == 0) || strcmp(*argv, "dev") == 0) {
655 NEXT_ARG();
656 d = *argv;
657 } else if (strcmp(*argv, "br") == 0) {
658 NEXT_ARG();
659 br = *argv;
660 } else if (strcmp(*argv, "dev") == 0) {
661 NEXT_ARG();
662 d = *argv;
663 } else if (strcmp(*argv, "vni") == 0) {
664 NEXT_ARG();
665 vni = strtoul(*argv, &endptr, 0);
666 if ((endptr && *endptr) ||
667 (vni >> 24) || vni == ULONG_MAX)
668 invarg("invalid VNI\n", *argv);
669 } else if (strcmp(*argv, "self") == 0) {
670 req.ndm.ndm_flags |= NTF_SELF;
671 } else if (matches(*argv, "master") == 0) {
672 req.ndm.ndm_flags |= NTF_MASTER;
673 } else if (matches(*argv, "vlan") == 0) {
674 if (vlan >= 0)
675 duparg2("vlan", *argv);
676 NEXT_ARG();
677 vlan = atoi(*argv);
678 } else if (matches(*argv, "dynamic") == 0) {
679 filter_dynamic = 1;
680 } else {
681 if (strcmp(*argv, "to") == 0)
682 NEXT_ARG();
683
684 if (matches(*argv, "help") == 0)
685 usage();
686 if (addr)
687 duparg2("to", *argv);
688 addr = *argv;
689 }
690 argc--; argv++;
691 }
692
693 if ((d == NULL && br == NULL) || addr == NULL) {
694 fprintf(stderr, "Device or master and address are required arguments.\n");
695 return -1;
696 }
697
698 if (sscanf(addr, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx",
699 abuf, abuf+1, abuf+2,
700 abuf+3, abuf+4, abuf+5) != 6) {
701 fprintf(stderr, "Invalid mac address %s\n", addr);
702 return -1;
703 }
704
705 addattr_l(&req.n, sizeof(req), NDA_LLADDR, abuf, ETH_ALEN);
706
707 if (vlan >= 0)
708 addattr16(&req.n, sizeof(req), NDA_VLAN, vlan);
709
710 if (vni != ~0)
711 addattr32(&req.n, sizeof(req), NDA_VNI, vni);
712
713 if (d) {
714 req.ndm.ndm_ifindex = ll_name_to_index(d);
715 if (!req.ndm.ndm_ifindex) {
716 fprintf(stderr, "Cannot find device \"%s\"\n", d);
717 return -1;
718 }
719 }
720
721 if (br) {
722 br_ifindex = ll_name_to_index(br);
723 if (!br_ifindex) {
724 fprintf(stderr, "Cannot find bridge device \"%s\"\n", br);
725 return -1;
726 }
727 addattr32(&req.n, sizeof(req), NDA_MASTER, br_ifindex);
728 }
729
730 if (rtnl_talk(&rth, &req.n, &answer) < 0)
731 return -2;
732
733
734
735
736
737 new_json_obj(json);
738 ret = 0;
739 if (print_fdb(answer, stdout) < 0) {
740 fprintf(stderr, "An error :-)\n");
741 ret = -1;
742 }
743 delete_json_obj();
744 free(answer);
745
746 return ret;
747}
748
749static int fdb_flush(int argc, char **argv)
750{
751 struct {
752 struct nlmsghdr n;
753 struct ndmsg ndm;
754 char buf[256];
755 } req = {
756 .n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ndmsg)),
757 .n.nlmsg_flags = NLM_F_REQUEST | NLM_F_BULK,
758 .n.nlmsg_type = RTM_DELNEIGH,
759 .ndm.ndm_family = PF_BRIDGE,
760 };
761 unsigned short ndm_state_mask = 0;
762 unsigned short ndm_flags_mask = 0;
763 short vid = -1, brport_ifidx = -1;
764 char *d = NULL, *brport = NULL;
765 unsigned short ndm_flags = 0;
766 unsigned short ndm_state = 0;
767 unsigned long src_vni = ~0;
768 unsigned long vni = ~0;
769 unsigned long port = 0;
770 inet_prefix dst;
771 int dst_ok = 0;
772 __u32 nhid = 0;
773 char *endptr;
774
775 while (argc > 0) {
776 if (strcmp(*argv, "dev") == 0) {
777 NEXT_ARG();
778 d = *argv;
779 } else if (strcmp(*argv, "master") == 0) {
780 ndm_flags |= NTF_MASTER;
781 } else if (strcmp(*argv, "self") == 0) {
782 ndm_flags |= NTF_SELF;
783 } else if (strcmp(*argv, "permanent") == 0) {
784 ndm_state |= NUD_PERMANENT;
785 ndm_state_mask |= NUD_PERMANENT;
786 } else if (strcmp(*argv, "nopermanent") == 0) {
787 ndm_state &= ~NUD_PERMANENT;
788 ndm_state_mask |= NUD_PERMANENT;
789 } else if (strcmp(*argv, "static") == 0) {
790 ndm_state |= NUD_NOARP;
791 ndm_state_mask |= NUD_NOARP | NUD_PERMANENT;
792 } else if (strcmp(*argv, "nostatic") == 0) {
793 ndm_state &= ~NUD_NOARP;
794 ndm_state_mask |= NUD_NOARP;
795 } else if (strcmp(*argv, "dynamic") == 0) {
796 ndm_state &= ~NUD_NOARP | NUD_PERMANENT;
797 ndm_state_mask |= NUD_NOARP | NUD_PERMANENT;
798 } else if (strcmp(*argv, "nodynamic") == 0) {
799 ndm_state |= NUD_NOARP;
800 ndm_state_mask |= NUD_NOARP;
801 } else if (strcmp(*argv, "added_by_user") == 0) {
802 ndm_flags |= NTF_USE;
803 ndm_flags_mask |= NTF_USE;
804 } else if (strcmp(*argv, "noadded_by_user") == 0) {
805 ndm_flags &= ~NTF_USE;
806 ndm_flags_mask |= NTF_USE;
807 } else if (strcmp(*argv, "extern_learn") == 0) {
808 ndm_flags |= NTF_EXT_LEARNED;
809 ndm_flags_mask |= NTF_EXT_LEARNED;
810 } else if (strcmp(*argv, "noextern_learn") == 0) {
811 ndm_flags &= ~NTF_EXT_LEARNED;
812 ndm_flags_mask |= NTF_EXT_LEARNED;
813 } else if (strcmp(*argv, "sticky") == 0) {
814 ndm_flags |= NTF_STICKY;
815 ndm_flags_mask |= NTF_STICKY;
816 } else if (strcmp(*argv, "nosticky") == 0) {
817 ndm_flags &= ~NTF_STICKY;
818 ndm_flags_mask |= NTF_STICKY;
819 } else if (strcmp(*argv, "offloaded") == 0) {
820 ndm_flags |= NTF_OFFLOADED;
821 ndm_flags_mask |= NTF_OFFLOADED;
822 } else if (strcmp(*argv, "nooffloaded") == 0) {
823 ndm_flags &= ~NTF_OFFLOADED;
824 ndm_flags_mask |= NTF_OFFLOADED;
825 } else if (strcmp(*argv, "router") == 0) {
826 ndm_flags |= NTF_ROUTER;
827 ndm_flags_mask |= NTF_ROUTER;
828 } else if (strcmp(*argv, "norouter") == 0) {
829 ndm_flags &= ~NTF_ROUTER;
830 ndm_flags_mask |= NTF_ROUTER;
831 } else if (strcmp(*argv, "brport") == 0) {
832 if (brport)
833 duparg2("brport", *argv);
834 NEXT_ARG();
835 brport = *argv;
836 } else if (strcmp(*argv, "vlan") == 0) {
837 if (vid >= 0)
838 duparg2("vlan", *argv);
839 NEXT_ARG();
840 vid = atoi(*argv);
841 } else if (strcmp(*argv, "src_vni") == 0) {
842 NEXT_ARG();
843 src_vni = strtoul(*argv, &endptr, 0);
844 if ((endptr && *endptr) ||
845 (src_vni >> 24) || src_vni == ULONG_MAX)
846 invarg("invalid src VNI\n", *argv);
847 } else if (strcmp(*argv, "nhid") == 0) {
848 NEXT_ARG();
849 if (get_u32(&nhid, *argv, 0))
850 invarg("\"nid\" value is invalid\n", *argv);
851 } else if (strcmp(*argv, "vni") == 0) {
852 NEXT_ARG();
853 vni = strtoul(*argv, &endptr, 0);
854 if ((endptr && *endptr) ||
855 (vni >> 24) || vni == ULONG_MAX)
856 invarg("invalid VNI\n", *argv);
857 } else if (strcmp(*argv, "port") == 0) {
858 NEXT_ARG();
859 port = strtoul(*argv, &endptr, 0);
860 if (endptr && *endptr) {
861 struct servent *pse;
862
863 pse = getservbyname(*argv, "udp");
864 if (!pse)
865 invarg("invalid port\n", *argv);
866 port = ntohs(pse->s_port);
867 } else if (port > 0xffff)
868 invarg("invalid port\n", *argv);
869 } else if (strcmp(*argv, "dst") == 0) {
870 NEXT_ARG();
871 if (dst_ok)
872 duparg2("dst", *argv);
873 get_addr(&dst, *argv, preferred_family);
874 dst_ok = 1;
875 } else if (strcmp(*argv, "help") == 0) {
876 NEXT_ARG();
877 } else {
878 fprintf(stderr, "bridge fdb: unknown command \"%s\"?\n",
879 *argv);
880 usage();
881 return -1;
882 }
883 argc--; argv++;
884 }
885
886 if (d == NULL) {
887 fprintf(stderr, "Device is a required argument.\n");
888 return -1;
889 }
890
891 req.ndm.ndm_ifindex = ll_name_to_index(d);
892 if (req.ndm.ndm_ifindex == 0) {
893 fprintf(stderr, "Cannot find bridge device \"%s\"\n", d);
894 return -1;
895 }
896
897 if (brport) {
898 brport_ifidx = ll_name_to_index(brport);
899 if (brport_ifidx == 0) {
900 fprintf(stderr, "Cannot find bridge port device \"%s\"\n",
901 brport);
902 return -1;
903 }
904 }
905
906 if (vid >= 4096) {
907 fprintf(stderr, "Invalid VLAN ID \"%hu\"\n", vid);
908 return -1;
909 }
910
911
912 if (!(ndm_flags & (NTF_SELF | NTF_MASTER)))
913 ndm_flags |= NTF_SELF;
914
915 req.ndm.ndm_flags = ndm_flags;
916 req.ndm.ndm_state = ndm_state;
917 if (brport_ifidx > -1)
918 addattr32(&req.n, sizeof(req), NDA_IFINDEX, brport_ifidx);
919 if (vid > -1)
920 addattr16(&req.n, sizeof(req), NDA_VLAN, vid);
921 if (src_vni != ~0)
922 addattr32(&req.n, sizeof(req), NDA_SRC_VNI, src_vni);
923 if (nhid > 0)
924 addattr32(&req.n, sizeof(req), NDA_NH_ID, nhid);
925 if (vni != ~0)
926 addattr32(&req.n, sizeof(req), NDA_VNI, vni);
927 if (port) {
928 unsigned short dport;
929
930 dport = htons((unsigned short)port);
931 addattr16(&req.n, sizeof(req), NDA_PORT, dport);
932 }
933 if (dst_ok)
934 addattr_l(&req.n, sizeof(req), NDA_DST, &dst.data, dst.bytelen);
935 if (ndm_flags_mask)
936 addattr8(&req.n, sizeof(req), NDA_NDM_FLAGS_MASK,
937 ndm_flags_mask);
938 if (ndm_state_mask)
939 addattr16(&req.n, sizeof(req), NDA_NDM_STATE_MASK,
940 ndm_state_mask);
941
942 if (rtnl_talk(&rth, &req.n, NULL) < 0)
943 return -1;
944
945 return 0;
946}
947
948int do_fdb(int argc, char **argv)
949{
950 ll_init_map(&rth);
951 timestamp = 0;
952
953 if (argc > 0) {
954 if (matches(*argv, "add") == 0)
955 return fdb_modify(RTM_NEWNEIGH, NLM_F_CREATE|NLM_F_EXCL, argc-1, argv+1);
956 if (matches(*argv, "append") == 0)
957 return fdb_modify(RTM_NEWNEIGH, NLM_F_CREATE|NLM_F_APPEND, argc-1, argv+1);
958 if (matches(*argv, "replace") == 0)
959 return fdb_modify(RTM_NEWNEIGH, NLM_F_CREATE|NLM_F_REPLACE, argc-1, argv+1);
960 if (matches(*argv, "delete") == 0)
961 return fdb_modify(RTM_DELNEIGH, 0, argc-1, argv+1);
962 if (matches(*argv, "get") == 0)
963 return fdb_get(argc-1, argv+1);
964 if (matches(*argv, "show") == 0 ||
965 matches(*argv, "lst") == 0 ||
966 matches(*argv, "list") == 0)
967 return fdb_show(argc-1, argv+1);
968 if (strcmp(*argv, "flush") == 0)
969 return fdb_flush(argc-1, argv+1);
970 if (matches(*argv, "help") == 0)
971 usage();
972 } else
973 return fdb_show(0, NULL);
974
975 fprintf(stderr, "Command \"%s\" is unknown, try \"bridge fdb help\".\n", *argv);
976 exit(-1);
977}
978