/* * Get/set/delete fdb table with netlink * * Authors: Stephen Hemminger */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "libnetlink.h" #include "br_common.h" #include "utils.h" int filter_index; static void usage(void) { fprintf(stderr, "Usage: br fdb { add | del | replace } ADDR dev DEV\n"); fprintf(stderr, " br fdb {show} [ dev DEV ]\n"); exit(-1); } static const char *state_n2a(unsigned s) { static char buf[32]; if (s & NUD_PERMANENT) return "local"; if (s & NUD_NOARP) return "static"; if (s & NUD_STALE) return "stale"; if (s & NUD_REACHABLE) return ""; sprintf(buf, "state=%#x", s); return buf; } static char *fmt_time(char *b, size_t l, unsigned long tick) { static int hz; if (hz == 0) hz = __get_user_hz(); snprintf(b, l, "%lu.%02lu", tick / hz, ((tick % hz) * hz) / 100); return b; } int print_fdb(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg) { struct ndmsg *r = NLMSG_DATA(n); int len = n->nlmsg_len; struct rtattr * tb[NDA_MAX+1]; const __u8 *addr = NULL; char b1[32]; len -= NLMSG_LENGTH(sizeof(*r)); if (len < 0) { fprintf(stderr, "BUG: wrong nlmsg len %d\n", len); return -1; } if (r->ndm_family != AF_BRIDGE) return 0; if (filter_index && filter_index != r->ndm_ifindex) return 0; parse_rtattr(tb, NDA_MAX, NDA_RTA(r), n->nlmsg_len - NLMSG_LENGTH(sizeof(*r))); if (n->nlmsg_type == RTM_DELNEIGH) printf("Deleted "); if (tb[NDA_LLADDR]) addr = RTA_DATA(tb[NDA_LLADDR]); else { fprintf(stderr, "missing lladdr\n"); return -1; } printf("%s\t%.2x:%.2x:%.2x:%.2x:%.2x:%.2x\t%s", ll_index_to_name(r->ndm_ifindex), addr[0], addr[1], addr[2], addr[3], addr[4], addr[5], state_n2a(r->ndm_state)); if (show_stats && tb[NDA_CACHEINFO]) { struct nda_cacheinfo *ci = RTA_DATA(tb[NDA_CACHEINFO]); printf("\t%8s", fmt_time(b1, sizeof(b1), ci->ndm_updated)); printf(" %8s", fmt_time(b1, sizeof(b1), ci->ndm_used)); } printf("\n"); return 0; } static int fdb_show(int argc, char **argv) { char *filter_dev = NULL; while (argc > 0) { if (strcmp(*argv, "dev") == 0) { NEXT_ARG(); if (filter_dev) duparg("dev", *argv); filter_dev = *argv; } argc--; argv++; } if (filter_dev) { if ((filter_index = if_nametoindex(filter_dev)) == 0) { fprintf(stderr, "Cannot find device \"%s\"\n", filter_dev); return -1; } } if (rtnl_wilddump_request(&rth, PF_BRIDGE, RTM_GETNEIGH) < 0) { perror("Cannot send dump request"); exit(1); } printf("port\tmac addr\t\tflags%s\n", show_stats ? "\t updated used" : ""); if (rtnl_dump_filter(&rth, print_fdb, NULL) < 0) { fprintf(stderr, "Dump terminated\n"); exit(1); } return 0; } static int fdb_modify(int cmd, int flags, int argc, char **argv) { struct { struct nlmsghdr n; struct ndmsg ndm; char buf[256]; } req; char *addr = NULL; char *d = NULL; char abuf[ETH_ALEN]; memset(&req, 0, sizeof(req)); req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ndmsg)); req.n.nlmsg_flags = NLM_F_REQUEST|flags; req.n.nlmsg_type = cmd; req.ndm.ndm_family = PF_BRIDGE; req.ndm.ndm_state = NUD_NOARP; while (argc > 0) { if (strcmp(*argv, "dev") == 0) { NEXT_ARG(); d = *argv; } else if (strcmp(*argv, "local") == 0) { req.ndm.ndm_state = NUD_PERMANENT; } else if (strcmp(*argv, "temp") == 0) { req.ndm.ndm_state = NUD_REACHABLE; } else { if (strcmp(*argv, "to") == 0) { NEXT_ARG(); } if (matches(*argv, "help") == 0) { NEXT_ARG(); } if (addr) duparg2("to", *argv); addr = *argv; } argc--; argv++; } if (d == NULL || addr == NULL) { fprintf(stderr, "Device and address are required arguments.\n"); exit(-1); } if (sscanf(addr, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx", abuf, abuf+1, abuf+2, abuf+3, abuf+4, abuf+5) != 6) { fprintf(stderr, "Invalid mac address %s\n", addr); exit(-1); } addattr_l(&req.n, sizeof(req), NDA_LLADDR, abuf, ETH_ALEN); req.ndm.ndm_ifindex = ll_name_to_index(d); if (req.ndm.ndm_ifindex == 0) { fprintf(stderr, "Cannot find device \"%s\"\n", d); return -1; } if (rtnl_talk(&rth, &req.n, 0, 0, NULL) < 0) exit(2); return 0; } int do_fdb(int argc, char **argv) { ll_init_map(&rth); if (argc > 0) { if (matches(*argv, "add") == 0) return fdb_modify(RTM_NEWNEIGH, NLM_F_CREATE|NLM_F_EXCL, argc-1, argv+1); if (matches(*argv, "change") == 0) return fdb_modify(RTM_NEWNEIGH, NLM_F_REPLACE, argc-1, argv+1); if (matches(*argv, "replace") == 0) return fdb_modify(RTM_NEWNEIGH, NLM_F_CREATE|NLM_F_REPLACE, argc-1, argv+1); if (matches(*argv, "delete") == 0) return fdb_modify(RTM_DELNEIGH, 0, argc-1, argv+1); if (matches(*argv, "show") == 0 || matches(*argv, "lst") == 0 || matches(*argv, "list") == 0) return fdb_show(argc-1, argv+1); if (matches(*argv, "help") == 0) usage(); } else return fdb_show(0, NULL); fprintf(stderr, "Command \"%s\" is unknown, try \"ip neigh help\".\n", *argv); exit(-1); }