iproute2/bridge/mdb.c
<<
>>
Prefs
   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#include <netdb.h>
  18#include <limits.h>
  19
  20#include "libnetlink.h"
  21#include "utils.h"
  22#include "br_common.h"
  23#include "rt_names.h"
  24#include "json_print.h"
  25
  26#ifndef MDBA_RTA
  27#define MDBA_RTA(r) \
  28        ((struct rtattr *)(((char *)(r)) + NLMSG_ALIGN(sizeof(struct br_port_msg))))
  29#endif
  30
  31static unsigned int filter_index, filter_vlan;
  32
  33static void usage(void)
  34{
  35        fprintf(stderr,
  36                "Usage: bridge mdb { add | del | replace } dev DEV port PORT grp GROUP [src SOURCE] [permanent | temp] [vid VID]\n"
  37                "              [ filter_mode { include | exclude } ] [ source_list SOURCE_LIST ] [ proto PROTO ] [ dst IPADDR ]\n"
  38                "              [ dst_port DST_PORT ] [ vni VNI ] [ src_vni SRC_VNI ] [ via DEV ]\n"
  39                "       bridge mdb {show} [ dev DEV ] [ vid VID ]\n"
  40                "       bridge mdb get dev DEV grp GROUP [ src SOURCE ] [ vid VID ] [ src_vni SRC_VNI ]\n"
  41                "       bridge mdb flush dev DEV [ port PORT ] [ vid VID ] [ src_vni SRC_VNI ] [ proto PROTO ]\n"
  42                "              [ [no]permanent ] [ dst IPADDR ] [ dst_port DST_PORT ] [ vni VNI ]\n");
  43        exit(-1);
  44}
  45
  46static bool is_temp_mcast_rtr(__u8 type)
  47{
  48        return type == MDB_RTR_TYPE_TEMP_QUERY || type == MDB_RTR_TYPE_TEMP;
  49}
  50
  51static const char *format_timer(__u32 ticks, int align)
  52{
  53        struct timeval tv;
  54        static char tbuf[32];
  55
  56        __jiffies_to_tv(&tv, ticks);
  57        if (align)
  58                snprintf(tbuf, sizeof(tbuf), "%4lu.%.2lu",
  59                         (unsigned long)tv.tv_sec,
  60                         (unsigned long)tv.tv_usec / 10000);
  61        else
  62                snprintf(tbuf, sizeof(tbuf), "%lu.%.2lu",
  63                         (unsigned long)tv.tv_sec,
  64                         (unsigned long)tv.tv_usec / 10000);
  65
  66        return tbuf;
  67}
  68
  69void br_print_router_port_stats(struct rtattr *pattr)
  70{
  71        struct rtattr *tb[MDBA_ROUTER_PATTR_MAX + 1];
  72
  73        parse_rtattr(tb, MDBA_ROUTER_PATTR_MAX, MDB_RTR_RTA(RTA_DATA(pattr)),
  74                     RTA_PAYLOAD(pattr) - RTA_ALIGN(sizeof(uint32_t)));
  75
  76        if (tb[MDBA_ROUTER_PATTR_TIMER]) {
  77                __u32 timer = rta_getattr_u32(tb[MDBA_ROUTER_PATTR_TIMER]);
  78
  79                print_string(PRINT_ANY, "timer", " %s",
  80                             format_timer(timer, 1));
  81        }
  82
  83        if (tb[MDBA_ROUTER_PATTR_TYPE]) {
  84                __u8 type = rta_getattr_u8(tb[MDBA_ROUTER_PATTR_TYPE]);
  85
  86                print_string(PRINT_ANY, "type", " %s",
  87                             is_temp_mcast_rtr(type) ? "temp" : "permanent");
  88        }
  89}
  90
  91static void br_print_router_ports(FILE *f, struct rtattr *attr,
  92                                  const char *brifname)
  93{
  94        int rem = RTA_PAYLOAD(attr);
  95        struct rtattr *i;
  96
  97        if (is_json_context())
  98                open_json_array(PRINT_JSON, brifname);
  99        else if (!show_stats)
 100                fprintf(f, "router ports on %s: ", brifname);
 101
 102        for (i = RTA_DATA(attr); RTA_OK(i, rem); i = RTA_NEXT(i, rem)) {
 103                uint32_t *port_ifindex = RTA_DATA(i);
 104                const char *port_ifname = ll_index_to_name(*port_ifindex);
 105
 106                if (is_json_context()) {
 107                        open_json_object(NULL);
 108                        print_string(PRINT_JSON, "port", NULL, port_ifname);
 109
 110                        if (show_stats)
 111                                br_print_router_port_stats(i);
 112                        close_json_object();
 113                } else if (show_stats) {
 114                        fprintf(f, "router ports on %s: %s",
 115                                brifname, port_ifname);
 116
 117                        br_print_router_port_stats(i);
 118                        fprintf(f, "\n");
 119                } else {
 120                        fprintf(f, "%s ", port_ifname);
 121                }
 122        }
 123
 124        if (!show_stats)
 125                print_nl();
 126
 127        close_json_array(PRINT_JSON, NULL);
 128}
 129
 130static void print_src_entry(struct rtattr *src_attr, int af, const char *sep)
 131{
 132        struct rtattr *stb[MDBA_MDB_SRCATTR_MAX + 1];
 133        SPRINT_BUF(abuf);
 134        const char *addr;
 135        __u32 timer_val;
 136
 137        parse_rtattr_nested(stb, MDBA_MDB_SRCATTR_MAX, src_attr);
 138        if (!stb[MDBA_MDB_SRCATTR_ADDRESS] || !stb[MDBA_MDB_SRCATTR_TIMER])
 139                return;
 140
 141        addr = inet_ntop(af, RTA_DATA(stb[MDBA_MDB_SRCATTR_ADDRESS]), abuf,
 142                         sizeof(abuf));
 143        if (!addr)
 144                return;
 145        timer_val = rta_getattr_u32(stb[MDBA_MDB_SRCATTR_TIMER]);
 146
 147        open_json_object(NULL);
 148        print_string(PRINT_FP, NULL, "%s", sep);
 149        print_color_string(PRINT_ANY, ifa_family_color(af),
 150                           "address", "%s", addr);
 151        print_string(PRINT_ANY, "timer", "/%s", format_timer(timer_val, 0));
 152        close_json_object();
 153}
 154
 155static void print_dst(const struct rtattr *dst_attr)
 156{
 157        SPRINT_BUF(abuf);
 158        int af = AF_INET;
 159        const void *dst;
 160
 161        if (RTA_PAYLOAD(dst_attr) == sizeof(struct in6_addr))
 162                af = AF_INET6;
 163
 164        dst = (const void *)RTA_DATA(dst_attr);
 165        print_color_string(PRINT_ANY, ifa_family_color(af),
 166                           "dst", " dst %s",
 167                           inet_ntop(af, dst, abuf, sizeof(abuf)));
 168}
 169
 170static void print_mdb_entry(FILE *f, int ifindex, const struct br_mdb_entry *e,
 171                            struct nlmsghdr *n, struct rtattr **tb)
 172{
 173        const void *grp, *src;
 174        const char *addr;
 175        SPRINT_BUF(abuf);
 176        const char *dev;
 177        int af;
 178
 179        if (filter_vlan && e->vid != filter_vlan)
 180                return;
 181
 182        if (!e->addr.proto) {
 183                af = AF_PACKET;
 184                grp = &e->addr.u.mac_addr;
 185        } else if (e->addr.proto == htons(ETH_P_IP)) {
 186                af = AF_INET;
 187                grp = &e->addr.u.ip4;
 188        } else {
 189                af = AF_INET6;
 190                grp = &e->addr.u.ip6;
 191        }
 192        dev = ll_index_to_name(ifindex);
 193
 194        open_json_object(NULL);
 195
 196        print_int(PRINT_JSON, "index", NULL, ifindex);
 197        print_color_string(PRINT_ANY, COLOR_IFNAME, "dev", "dev %s", dev);
 198        print_string(PRINT_ANY, "port", " port %s",
 199                     ll_index_to_name(e->ifindex));
 200
 201        /* The ETH_ALEN argument is ignored for all cases but AF_PACKET */
 202        addr = rt_addr_n2a_r(af, ETH_ALEN, grp, abuf, sizeof(abuf));
 203        if (!addr)
 204                return;
 205
 206        print_color_string(PRINT_ANY, ifa_family_color(af),
 207                            "grp", " grp %s", addr);
 208
 209        if (tb && tb[MDBA_MDB_EATTR_SOURCE]) {
 210                src = (const void *)RTA_DATA(tb[MDBA_MDB_EATTR_SOURCE]);
 211                print_color_string(PRINT_ANY, ifa_family_color(af),
 212                                   "src", " src %s",
 213                                   inet_ntop(af, src, abuf, sizeof(abuf)));
 214        }
 215        print_string(PRINT_ANY, "state", " %s",
 216                           (e->state & MDB_PERMANENT) ? "permanent" : "temp");
 217        if (show_details && tb) {
 218                if (tb[MDBA_MDB_EATTR_GROUP_MODE]) {
 219                        __u8 mode = rta_getattr_u8(tb[MDBA_MDB_EATTR_GROUP_MODE]);
 220
 221                        print_string(PRINT_ANY, "filter_mode", " filter_mode %s",
 222                                     mode == MCAST_INCLUDE ? "include" :
 223                                                             "exclude");
 224                }
 225                if (tb[MDBA_MDB_EATTR_SRC_LIST]) {
 226                        struct rtattr *i, *attr = tb[MDBA_MDB_EATTR_SRC_LIST];
 227                        const char *sep = " ";
 228                        int rem;
 229
 230                        open_json_array(PRINT_ANY, is_json_context() ?
 231                                                                "source_list" :
 232                                                                " source_list");
 233                        rem = RTA_PAYLOAD(attr);
 234                        for (i = RTA_DATA(attr); RTA_OK(i, rem);
 235                             i = RTA_NEXT(i, rem)) {
 236                                print_src_entry(i, af, sep);
 237                                sep = ",";
 238                        }
 239                        close_json_array(PRINT_JSON, NULL);
 240                }
 241                if (tb[MDBA_MDB_EATTR_RTPROT]) {
 242                        __u8 rtprot = rta_getattr_u8(tb[MDBA_MDB_EATTR_RTPROT]);
 243                        SPRINT_BUF(rtb);
 244
 245                        print_string(PRINT_ANY, "protocol", " proto %s",
 246                                     rtnl_rtprot_n2a(rtprot, rtb, sizeof(rtb)));
 247                }
 248        }
 249
 250        open_json_array(PRINT_JSON, "flags");
 251        if (e->flags & MDB_FLAGS_OFFLOAD)
 252                print_string(PRINT_ANY, NULL, " %s", "offload");
 253        if (e->flags & MDB_FLAGS_FAST_LEAVE)
 254                print_string(PRINT_ANY, NULL, " %s", "fast_leave");
 255        if (e->flags & MDB_FLAGS_STAR_EXCL)
 256                print_string(PRINT_ANY, NULL, " %s", "added_by_star_ex");
 257        if (e->flags & MDB_FLAGS_BLOCKED)
 258                print_string(PRINT_ANY, NULL, " %s", "blocked");
 259        close_json_array(PRINT_JSON, NULL);
 260
 261        if (e->vid)
 262                print_uint(PRINT_ANY, "vid", " vid %u", e->vid);
 263
 264        if (tb[MDBA_MDB_EATTR_DST])
 265                print_dst(tb[MDBA_MDB_EATTR_DST]);
 266
 267        if (tb[MDBA_MDB_EATTR_DST_PORT])
 268                print_uint(PRINT_ANY, "dst_port", " dst_port %u",
 269                           rta_getattr_u16(tb[MDBA_MDB_EATTR_DST_PORT]));
 270
 271        if (tb[MDBA_MDB_EATTR_VNI])
 272                print_uint(PRINT_ANY, "vni", " vni %u",
 273                           rta_getattr_u32(tb[MDBA_MDB_EATTR_VNI]));
 274
 275        if (tb[MDBA_MDB_EATTR_SRC_VNI])
 276                print_uint(PRINT_ANY, "src_vni", " src_vni %u",
 277                           rta_getattr_u32(tb[MDBA_MDB_EATTR_SRC_VNI]));
 278
 279        if (tb[MDBA_MDB_EATTR_IFINDEX]) {
 280                unsigned int ifindex;
 281
 282                ifindex = rta_getattr_u32(tb[MDBA_MDB_EATTR_IFINDEX]);
 283                print_string(PRINT_ANY, "via", " via %s",
 284                             ll_index_to_name(ifindex));
 285        }
 286
 287        if (show_stats && tb && tb[MDBA_MDB_EATTR_TIMER]) {
 288                __u32 timer = rta_getattr_u32(tb[MDBA_MDB_EATTR_TIMER]);
 289
 290                print_string(PRINT_ANY, "timer", " %s",
 291                             format_timer(timer, 1));
 292        }
 293
 294        print_nl();
 295        close_json_object();
 296}
 297
 298static void br_print_mdb_entry(FILE *f, int ifindex, struct rtattr *attr,
 299                               struct nlmsghdr *n)
 300{
 301        struct rtattr *etb[MDBA_MDB_EATTR_MAX + 1];
 302        struct br_mdb_entry *e;
 303        struct rtattr *i;
 304        int rem;
 305
 306        rem = RTA_PAYLOAD(attr);
 307        for (i = RTA_DATA(attr); RTA_OK(i, rem); i = RTA_NEXT(i, rem)) {
 308                e = RTA_DATA(i);
 309                parse_rtattr_flags(etb, MDBA_MDB_EATTR_MAX, MDB_RTA(RTA_DATA(i)),
 310                                   RTA_PAYLOAD(i) - RTA_ALIGN(sizeof(*e)),
 311                                   NLA_F_NESTED);
 312                print_mdb_entry(f, ifindex, e, n, etb);
 313        }
 314}
 315
 316static void print_mdb_entries(FILE *fp, struct nlmsghdr *n,
 317                              int ifindex,  struct rtattr *mdb)
 318{
 319        int rem = RTA_PAYLOAD(mdb);
 320        struct rtattr *i;
 321
 322        for (i = RTA_DATA(mdb); RTA_OK(i, rem); i = RTA_NEXT(i, rem))
 323                br_print_mdb_entry(fp, ifindex, i, n);
 324}
 325
 326static void print_router_entries(FILE *fp, struct nlmsghdr *n,
 327                                 int ifindex, struct rtattr *router)
 328{
 329        const char *brifname = ll_index_to_name(ifindex);
 330
 331        if (n->nlmsg_type == RTM_GETMDB) {
 332                if (show_details)
 333                        br_print_router_ports(fp, router, brifname);
 334        } else {
 335                struct rtattr *i = RTA_DATA(router);
 336                uint32_t *port_ifindex = RTA_DATA(i);
 337                const char *port_name = ll_index_to_name(*port_ifindex);
 338
 339                if (is_json_context()) {
 340                        open_json_array(PRINT_JSON, brifname);
 341                        open_json_object(NULL);
 342
 343                        print_string(PRINT_JSON, "port", NULL,
 344                                     port_name);
 345                        close_json_object();
 346                        close_json_array(PRINT_JSON, NULL);
 347                } else {
 348                        fprintf(fp, "router port dev %s master %s\n",
 349                                port_name, brifname);
 350                }
 351        }
 352}
 353
 354static int __parse_mdb_nlmsg(struct nlmsghdr *n, struct rtattr **tb)
 355{
 356        struct br_port_msg *r = NLMSG_DATA(n);
 357        int len = n->nlmsg_len;
 358
 359        if (n->nlmsg_type != RTM_GETMDB &&
 360            n->nlmsg_type != RTM_NEWMDB &&
 361            n->nlmsg_type != RTM_DELMDB) {
 362                fprintf(stderr,
 363                        "Not RTM_GETMDB, RTM_NEWMDB or RTM_DELMDB: %08x %08x %08x\n",
 364                        n->nlmsg_len, n->nlmsg_type, n->nlmsg_flags);
 365
 366                return 0;
 367        }
 368
 369        len -= NLMSG_LENGTH(sizeof(*r));
 370        if (len < 0) {
 371                fprintf(stderr, "BUG: wrong nlmsg len %d\n", len);
 372                return -1;
 373        }
 374
 375        if (filter_index && filter_index != r->ifindex)
 376                return 0;
 377
 378        parse_rtattr(tb, MDBA_MAX, MDBA_RTA(r), n->nlmsg_len - NLMSG_LENGTH(sizeof(*r)));
 379
 380        return 1;
 381}
 382
 383static int print_mdbs(struct nlmsghdr *n, void *arg)
 384{
 385        struct br_port_msg *r = NLMSG_DATA(n);
 386        struct rtattr *tb[MDBA_MAX+1];
 387        FILE *fp = arg;
 388        int ret;
 389
 390        ret = __parse_mdb_nlmsg(n, tb);
 391        if (ret != 1)
 392                return ret;
 393
 394        if (tb[MDBA_MDB])
 395                print_mdb_entries(fp, n, r->ifindex, tb[MDBA_MDB]);
 396
 397        return 0;
 398}
 399
 400static int print_rtrs(struct nlmsghdr *n, void *arg)
 401{
 402        struct br_port_msg *r = NLMSG_DATA(n);
 403        struct rtattr *tb[MDBA_MAX+1];
 404        FILE *fp = arg;
 405        int ret;
 406
 407        ret = __parse_mdb_nlmsg(n, tb);
 408        if (ret != 1)
 409                return ret;
 410
 411        if (tb[MDBA_ROUTER])
 412                print_router_entries(fp, n, r->ifindex, tb[MDBA_ROUTER]);
 413
 414        return 0;
 415}
 416
 417int print_mdb_mon(struct nlmsghdr *n, void *arg)
 418{
 419        struct br_port_msg *r = NLMSG_DATA(n);
 420        struct rtattr *tb[MDBA_MAX+1];
 421        FILE *fp = arg;
 422        int ret;
 423
 424        ret = __parse_mdb_nlmsg(n, tb);
 425        if (ret != 1)
 426                return ret;
 427
 428        print_headers(fp, "[MDB]");
 429
 430        if (n->nlmsg_type == RTM_DELMDB)
 431                print_bool(PRINT_ANY, "deleted", "Deleted ", true);
 432
 433        if (tb[MDBA_MDB])
 434                print_mdb_entries(fp, n, r->ifindex, tb[MDBA_MDB]);
 435
 436        if (tb[MDBA_ROUTER])
 437                print_router_entries(fp, n, r->ifindex, tb[MDBA_ROUTER]);
 438
 439        return 0;
 440}
 441
 442static int mdb_show(int argc, char **argv)
 443{
 444        char *filter_dev = NULL;
 445
 446        while (argc > 0) {
 447                if (strcmp(*argv, "dev") == 0) {
 448                        NEXT_ARG();
 449                        if (filter_dev)
 450                                duparg("dev", *argv);
 451                        filter_dev = *argv;
 452                } else if (strcmp(*argv, "vid") == 0) {
 453                        NEXT_ARG();
 454                        if (filter_vlan)
 455                                duparg("vid", *argv);
 456                        filter_vlan = atoi(*argv);
 457                }
 458                argc--; argv++;
 459        }
 460
 461        if (filter_dev) {
 462                filter_index = ll_name_to_index(filter_dev);
 463                if (!filter_index)
 464                        return nodev(filter_dev);
 465        }
 466
 467        new_json_obj(json);
 468        open_json_object(NULL);
 469
 470        /* get mdb entries */
 471        if (rtnl_mdbdump_req(&rth, PF_BRIDGE) < 0) {
 472                perror("Cannot send dump request");
 473                delete_json_obj();
 474                return -1;
 475        }
 476
 477        open_json_array(PRINT_JSON, "mdb");
 478        if (rtnl_dump_filter(&rth, print_mdbs, stdout) < 0) {
 479                fprintf(stderr, "Dump terminated\n");
 480                delete_json_obj();
 481                return -1;
 482        }
 483        close_json_array(PRINT_JSON, NULL);
 484
 485        /* get router ports */
 486        if (rtnl_mdbdump_req(&rth, PF_BRIDGE) < 0) {
 487                perror("Cannot send dump request");
 488                delete_json_obj();
 489                return -1;
 490        }
 491
 492        open_json_object("router");
 493        if (rtnl_dump_filter(&rth, print_rtrs, stdout) < 0) {
 494                fprintf(stderr, "Dump terminated\n");
 495                delete_json_obj();
 496                return -1;
 497        }
 498        close_json_object();
 499
 500        close_json_object();
 501        delete_json_obj();
 502        fflush(stdout);
 503
 504        return 0;
 505}
 506
 507static int mdb_parse_grp(const char *grp, struct br_mdb_entry *e)
 508{
 509        if (inet_pton(AF_INET, grp, &e->addr.u.ip4)) {
 510                e->addr.proto = htons(ETH_P_IP);
 511                return 0;
 512        }
 513        if (inet_pton(AF_INET6, grp, &e->addr.u.ip6)) {
 514                e->addr.proto = htons(ETH_P_IPV6);
 515                return 0;
 516        }
 517        if (ll_addr_a2n((char *)e->addr.u.mac_addr, sizeof(e->addr.u.mac_addr),
 518                        grp) == ETH_ALEN) {
 519                e->addr.proto = 0;
 520                return 0;
 521        }
 522
 523        return -1;
 524}
 525
 526static int mdb_parse_src(struct nlmsghdr *n, int maxlen, const char *src)
 527{
 528        struct in6_addr src_ip6;
 529        __be32 src_ip4;
 530
 531        if (inet_pton(AF_INET, src, &src_ip4)) {
 532                addattr32(n, maxlen, MDBE_ATTR_SOURCE, src_ip4);
 533                return 0;
 534        }
 535
 536        if (inet_pton(AF_INET6, src, &src_ip6)) {
 537                addattr_l(n, maxlen, MDBE_ATTR_SOURCE, &src_ip6,
 538                          sizeof(src_ip6));
 539                return 0;
 540        }
 541
 542        return -1;
 543}
 544
 545static int mdb_parse_mode(struct nlmsghdr *n, int maxlen, const char *mode)
 546{
 547        if (strcmp(mode, "include") == 0) {
 548                addattr8(n, maxlen, MDBE_ATTR_GROUP_MODE, MCAST_INCLUDE);
 549                return 0;
 550        }
 551
 552        if (strcmp(mode, "exclude") == 0) {
 553                addattr8(n, maxlen, MDBE_ATTR_GROUP_MODE, MCAST_EXCLUDE);
 554                return 0;
 555        }
 556
 557        return -1;
 558}
 559
 560static int mdb_parse_src_entry(struct nlmsghdr *n, int maxlen, char *src_entry)
 561{
 562        struct in6_addr src_ip6;
 563        struct rtattr *nest;
 564        __be32 src_ip4;
 565
 566        nest = addattr_nest(n, maxlen, MDBE_SRC_LIST_ENTRY | NLA_F_NESTED);
 567
 568        if (inet_pton(AF_INET, src_entry, &src_ip4))
 569                addattr32(n, maxlen, MDBE_SRCATTR_ADDRESS, src_ip4);
 570        else if (inet_pton(AF_INET6, src_entry, &src_ip6))
 571                addattr_l(n, maxlen, MDBE_SRCATTR_ADDRESS, &src_ip6,
 572                          sizeof(src_ip6));
 573        else
 574                return -1;
 575
 576        addattr_nest_end(n, nest);
 577
 578        return 0;
 579}
 580
 581static int mdb_parse_src_list(struct nlmsghdr *n, int maxlen, char *src_list)
 582{
 583        struct rtattr *nest;
 584        char *sep;
 585
 586        nest = addattr_nest(n, maxlen, MDBE_ATTR_SRC_LIST | NLA_F_NESTED);
 587
 588        do {
 589                sep = strchr(src_list, ',');
 590                if (sep)
 591                        *sep = '\0';
 592
 593                if (mdb_parse_src_entry(n, maxlen, src_list)) {
 594                        fprintf(stderr, "Invalid source entry \"%s\" in source list\n",
 595                                src_list);
 596                        return -1;
 597                }
 598
 599                src_list = sep + 1;
 600        } while (sep);
 601
 602        addattr_nest_end(n, nest);
 603
 604        return 0;
 605}
 606
 607static int mdb_parse_proto(struct nlmsghdr *n, int maxlen, const char *proto)
 608{
 609        __u32 proto_id;
 610        int err;
 611
 612        err = rtnl_rtprot_a2n(&proto_id, proto);
 613        if (err)
 614                return err;
 615
 616        addattr8(n, maxlen, MDBE_ATTR_RTPROT, proto_id);
 617
 618        return 0;
 619}
 620
 621static int mdb_parse_dst(struct nlmsghdr *n, int maxlen, const char *dst)
 622{
 623        struct in6_addr dst_ip6;
 624        __be32 dst_ip4;
 625
 626        if (inet_pton(AF_INET, dst, &dst_ip4)) {
 627                addattr32(n, maxlen, MDBE_ATTR_DST, dst_ip4);
 628                return 0;
 629        }
 630
 631        if (inet_pton(AF_INET6, dst, &dst_ip6)) {
 632                addattr_l(n, maxlen, MDBE_ATTR_DST, &dst_ip6,
 633                          sizeof(dst_ip6));
 634                return 0;
 635        }
 636
 637        return -1;
 638}
 639
 640static int mdb_parse_dst_port(struct nlmsghdr *n, int maxlen,
 641                              const char *dst_port)
 642{
 643        unsigned long port;
 644        char *endptr;
 645
 646        port = strtoul(dst_port, &endptr, 0);
 647        if (endptr && *endptr) {
 648                struct servent *pse;
 649
 650                pse = getservbyname(dst_port, "udp");
 651                if (!pse)
 652                        return -1;
 653                port = ntohs(pse->s_port);
 654        } else if (port > USHRT_MAX) {
 655                return -1;
 656        }
 657
 658        addattr16(n, maxlen, MDBE_ATTR_DST_PORT, port);
 659
 660        return 0;
 661}
 662
 663static int mdb_parse_vni(struct nlmsghdr *n, int maxlen, const char *vni,
 664                         int attr_type)
 665{
 666        unsigned long vni_num;
 667        char *endptr;
 668
 669        vni_num = strtoul(vni, &endptr, 0);
 670        if ((endptr && *endptr) || vni_num == ULONG_MAX)
 671                return -1;
 672
 673        addattr32(n, maxlen, attr_type, vni_num);
 674
 675        return 0;
 676}
 677
 678static int mdb_parse_dev(struct nlmsghdr *n, int maxlen, const char *dev)
 679{
 680        unsigned int ifindex;
 681
 682        ifindex = ll_name_to_index(dev);
 683        if (!ifindex)
 684                return -1;
 685
 686        addattr32(n, maxlen, MDBE_ATTR_IFINDEX, ifindex);
 687
 688        return 0;
 689}
 690
 691static int mdb_modify(int cmd, int flags, int argc, char **argv)
 692{
 693        struct {
 694                struct nlmsghdr n;
 695                struct br_port_msg      bpm;
 696                char                    buf[1024];
 697        } req = {
 698                .n.nlmsg_len = NLMSG_LENGTH(sizeof(struct br_port_msg)),
 699                .n.nlmsg_flags = NLM_F_REQUEST | flags,
 700                .n.nlmsg_type = cmd,
 701                .bpm.family = PF_BRIDGE,
 702        };
 703        char *d = NULL, *p = NULL, *grp = NULL, *src = NULL, *mode = NULL;
 704        char *dst_port = NULL, *vni = NULL, *src_vni = NULL, *via = NULL;
 705        char *src_list = NULL, *proto = NULL, *dst = NULL;
 706        struct br_mdb_entry entry = {};
 707        bool set_attrs = false;
 708        short vid = 0;
 709
 710        while (argc > 0) {
 711                if (strcmp(*argv, "dev") == 0) {
 712                        NEXT_ARG();
 713                        d = *argv;
 714                } else if (strcmp(*argv, "grp") == 0) {
 715                        NEXT_ARG();
 716                        grp = *argv;
 717                } else if (strcmp(*argv, "port") == 0) {
 718                        NEXT_ARG();
 719                        p = *argv;
 720                } else if (strcmp(*argv, "permanent") == 0) {
 721                        if (cmd == RTM_NEWMDB)
 722                                entry.state |= MDB_PERMANENT;
 723                } else if (strcmp(*argv, "temp") == 0) {
 724                        ;/* nothing */
 725                } else if (strcmp(*argv, "vid") == 0) {
 726                        NEXT_ARG();
 727                        vid = atoi(*argv);
 728                } else if (strcmp(*argv, "src") == 0) {
 729                        NEXT_ARG();
 730                        src = *argv;
 731                        set_attrs = true;
 732                } else if (strcmp(*argv, "filter_mode") == 0) {
 733                        NEXT_ARG();
 734                        mode = *argv;
 735                        set_attrs = true;
 736                } else if (strcmp(*argv, "source_list") == 0) {
 737                        NEXT_ARG();
 738                        src_list = *argv;
 739                        set_attrs = true;
 740                } else if (strcmp(*argv, "proto") == 0) {
 741                        NEXT_ARG();
 742                        proto = *argv;
 743                        set_attrs = true;
 744                } else if (strcmp(*argv, "dst") == 0) {
 745                        NEXT_ARG();
 746                        dst = *argv;
 747                        set_attrs = true;
 748                } else if (strcmp(*argv, "dst_port") == 0) {
 749                        NEXT_ARG();
 750                        dst_port = *argv;
 751                        set_attrs = true;
 752                } else if (strcmp(*argv, "vni") == 0) {
 753                        NEXT_ARG();
 754                        vni = *argv;
 755                        set_attrs = true;
 756                } else if (strcmp(*argv, "src_vni") == 0) {
 757                        NEXT_ARG();
 758                        src_vni = *argv;
 759                        set_attrs = true;
 760                } else if (strcmp(*argv, "via") == 0) {
 761                        NEXT_ARG();
 762                        via = *argv;
 763                        set_attrs = true;
 764                } else {
 765                        if (matches(*argv, "help") == 0)
 766                                usage();
 767                }
 768                argc--; argv++;
 769        }
 770
 771        if (d == NULL || grp == NULL || p == NULL) {
 772                fprintf(stderr, "Device, group address and port name are required arguments.\n");
 773                return -1;
 774        }
 775
 776        req.bpm.ifindex = ll_name_to_index(d);
 777        if (!req.bpm.ifindex)
 778                return nodev(d);
 779
 780        entry.ifindex = ll_name_to_index(p);
 781        if (!entry.ifindex)
 782                return nodev(p);
 783
 784        if (mdb_parse_grp(grp, &entry)) {
 785                fprintf(stderr, "Invalid address \"%s\"\n", grp);
 786                return -1;
 787        }
 788
 789        entry.vid = vid;
 790        addattr_l(&req.n, sizeof(req), MDBA_SET_ENTRY, &entry, sizeof(entry));
 791        if (set_attrs) {
 792                struct rtattr *nest = addattr_nest(&req.n, sizeof(req),
 793                                                   MDBA_SET_ENTRY_ATTRS);
 794
 795                nest->rta_type |= NLA_F_NESTED;
 796
 797                if (src && mdb_parse_src(&req.n, sizeof(req), src)) {
 798                        fprintf(stderr, "Invalid source address \"%s\"\n", src);
 799                        return -1;
 800                }
 801
 802                if (mode && mdb_parse_mode(&req.n, sizeof(req), mode)) {
 803                        fprintf(stderr, "Invalid filter mode \"%s\"\n", mode);
 804                        return -1;
 805                }
 806
 807                if (src_list && mdb_parse_src_list(&req.n, sizeof(req),
 808                                                   src_list))
 809                        return -1;
 810
 811                if (proto && mdb_parse_proto(&req.n, sizeof(req), proto)) {
 812                        fprintf(stderr, "Invalid protocol value \"%s\"\n",
 813                                proto);
 814                        return -1;
 815                }
 816
 817                if (dst && mdb_parse_dst(&req.n, sizeof(req), dst)) {
 818                        fprintf(stderr, "Invalid underlay destination address \"%s\"\n",
 819                                dst);
 820                        return -1;
 821                }
 822
 823                if (dst_port && mdb_parse_dst_port(&req.n, sizeof(req),
 824                                                   dst_port)) {
 825                        fprintf(stderr, "Invalid destination port \"%s\"\n", dst_port);
 826                        return -1;
 827                }
 828
 829                if (vni && mdb_parse_vni(&req.n, sizeof(req), vni,
 830                                         MDBE_ATTR_VNI)) {
 831                        fprintf(stderr, "Invalid destination VNI \"%s\"\n",
 832                                vni);
 833                        return -1;
 834                }
 835
 836                if (src_vni && mdb_parse_vni(&req.n, sizeof(req), src_vni,
 837                                             MDBE_ATTR_SRC_VNI)) {
 838                        fprintf(stderr, "Invalid source VNI \"%s\"\n", src_vni);
 839                        return -1;
 840                }
 841
 842                if (via && mdb_parse_dev(&req.n, sizeof(req), via))
 843                        return nodev(via);
 844
 845                addattr_nest_end(&req.n, nest);
 846        }
 847
 848        if (rtnl_talk(&rth, &req.n, NULL) < 0)
 849                return -1;
 850
 851        return 0;
 852}
 853
 854static int mdb_get(int argc, char **argv)
 855{
 856        struct {
 857                struct nlmsghdr n;
 858                struct br_port_msg      bpm;
 859                char                    buf[1024];
 860        } req = {
 861                .n.nlmsg_len = NLMSG_LENGTH(sizeof(struct br_port_msg)),
 862                .n.nlmsg_flags = NLM_F_REQUEST,
 863                .n.nlmsg_type = RTM_GETMDB,
 864                .bpm.family = PF_BRIDGE,
 865        };
 866        char *d = NULL, *grp = NULL, *src = NULL, *src_vni = NULL;
 867        struct br_mdb_entry entry = {};
 868        struct nlmsghdr *answer;
 869        bool get_attrs = false;
 870        short vid = 0;
 871        int ret = 0;
 872
 873        while (argc > 0) {
 874                if (strcmp(*argv, "dev") == 0) {
 875                        NEXT_ARG();
 876                        d = *argv;
 877                } else if (strcmp(*argv, "grp") == 0) {
 878                        NEXT_ARG();
 879                        grp = *argv;
 880                } else if (strcmp(*argv, "vid") == 0) {
 881                        NEXT_ARG();
 882                        vid = atoi(*argv);
 883                } else if (strcmp(*argv, "src") == 0) {
 884                        NEXT_ARG();
 885                        src = *argv;
 886                        get_attrs = true;
 887                } else if (strcmp(*argv, "src_vni") == 0) {
 888                        NEXT_ARG();
 889                        src_vni = *argv;
 890                        get_attrs = true;
 891                } else {
 892                        if (strcmp(*argv, "help") == 0)
 893                                usage();
 894                }
 895                argc--; argv++;
 896        }
 897
 898        if (d == NULL || grp == NULL) {
 899                fprintf(stderr, "Device and group address are required arguments.\n");
 900                return -1;
 901        }
 902
 903        req.bpm.ifindex = ll_name_to_index(d);
 904        if (!req.bpm.ifindex)
 905                return nodev(d);
 906
 907        if (mdb_parse_grp(grp, &entry)) {
 908                fprintf(stderr, "Invalid address \"%s\"\n", grp);
 909                return -1;
 910        }
 911
 912        entry.vid = vid;
 913        addattr_l(&req.n, sizeof(req), MDBA_GET_ENTRY, &entry, sizeof(entry));
 914        if (get_attrs) {
 915                struct rtattr *nest = addattr_nest(&req.n, sizeof(req),
 916                                                   MDBA_GET_ENTRY_ATTRS);
 917
 918                nest->rta_type |= NLA_F_NESTED;
 919
 920                if (src && mdb_parse_src(&req.n, sizeof(req), src)) {
 921                        fprintf(stderr, "Invalid source address \"%s\"\n", src);
 922                        return -1;
 923                }
 924
 925                if (src_vni && mdb_parse_vni(&req.n, sizeof(req), src_vni,
 926                                             MDBE_ATTR_SRC_VNI)) {
 927                        fprintf(stderr, "Invalid source VNI \"%s\"\n", src_vni);
 928                        return -1;
 929                }
 930
 931                addattr_nest_end(&req.n, nest);
 932        }
 933
 934        if (rtnl_talk(&rth, &req.n, &answer) < 0)
 935                return -2;
 936
 937        new_json_obj(json);
 938
 939        if (print_mdbs(answer, stdout) < 0)
 940                ret = -1;
 941
 942        delete_json_obj();
 943        free(answer);
 944
 945        return ret;
 946}
 947
 948static int mdb_flush(int argc, char **argv)
 949{
 950        struct {
 951                struct nlmsghdr n;
 952                struct br_port_msg      bpm;
 953                char                    buf[1024];
 954        } req = {
 955                .n.nlmsg_len = NLMSG_LENGTH(sizeof(struct br_port_msg)),
 956                .n.nlmsg_flags = NLM_F_REQUEST | NLM_F_BULK,
 957                .n.nlmsg_type = RTM_DELMDB,
 958                .bpm.family = PF_BRIDGE,
 959        };
 960        char *d = NULL, *p = NULL, *src_vni = NULL, *proto = NULL, *dst = NULL;
 961        char *dst_port = NULL, *vni = NULL;
 962        struct br_mdb_entry entry = {};
 963        unsigned short state_mask = 0;
 964        bool set_attrs = false;
 965        short vid = 0;
 966
 967        while (argc > 0) {
 968                if (strcmp(*argv, "dev") == 0) {
 969                        NEXT_ARG();
 970                        d = *argv;
 971                } else if (strcmp(*argv, "port") == 0) {
 972                        NEXT_ARG();
 973                        p = *argv;
 974                } else if (strcmp(*argv, "vid") == 0) {
 975                        NEXT_ARG();
 976                        vid = atoi(*argv);
 977                } else if (strcmp(*argv, "src_vni") == 0) {
 978                        NEXT_ARG();
 979                        src_vni = *argv;
 980                        set_attrs = true;
 981                } else if (strcmp(*argv, "proto") == 0) {
 982                        NEXT_ARG();
 983                        proto = *argv;
 984                        set_attrs = true;
 985                } else if (strcmp(*argv, "permanent") == 0) {
 986                        entry.state |= MDB_PERMANENT;
 987                        state_mask |= MDB_PERMANENT;
 988                        set_attrs = true;
 989                } else if (strcmp(*argv, "nopermanent") == 0) {
 990                        entry.state &= ~MDB_PERMANENT;
 991                        state_mask |= MDB_PERMANENT;
 992                        set_attrs = true;
 993                } else if (strcmp(*argv, "dst") == 0) {
 994                        NEXT_ARG();
 995                        dst = *argv;
 996                        set_attrs = true;
 997                } else if (strcmp(*argv, "dst_port") == 0) {
 998                        NEXT_ARG();
 999                        dst_port = *argv;
1000                        set_attrs = true;
1001                } else if (strcmp(*argv, "vni") == 0) {
1002                        NEXT_ARG();
1003                        vni = *argv;
1004                        set_attrs = true;
1005                } else {
1006                        if (strcmp(*argv, "help") == 0)
1007                                usage();
1008                }
1009                argc--; argv++;
1010        }
1011
1012        if (d == NULL) {
1013                fprintf(stderr, "Device is a required argument.\n");
1014                return -1;
1015        }
1016
1017        req.bpm.ifindex = ll_name_to_index(d);
1018        if (!req.bpm.ifindex)
1019                return nodev(d);
1020
1021        if (p) {
1022                entry.ifindex = ll_name_to_index(p);
1023                if (!entry.ifindex)
1024                        return nodev(p);
1025        }
1026
1027        entry.vid = vid;
1028        addattr_l(&req.n, sizeof(req), MDBA_SET_ENTRY, &entry, sizeof(entry));
1029        if (set_attrs) {
1030                struct rtattr *nest = addattr_nest(&req.n, sizeof(req),
1031                                                   MDBA_SET_ENTRY_ATTRS);
1032
1033                nest->rta_type |= NLA_F_NESTED;
1034
1035                if (proto && mdb_parse_proto(&req.n, sizeof(req), proto)) {
1036                        fprintf(stderr, "Invalid protocol value \"%s\"\n",
1037                                proto);
1038                        return -1;
1039                }
1040
1041                if (dst && mdb_parse_dst(&req.n, sizeof(req), dst)) {
1042                        fprintf(stderr, "Invalid underlay destination address \"%s\"\n",
1043                                dst);
1044                        return -1;
1045                }
1046
1047                if (dst_port && mdb_parse_dst_port(&req.n, sizeof(req),
1048                                                   dst_port)) {
1049                        fprintf(stderr, "Invalid destination port \"%s\"\n", dst_port);
1050                        return -1;
1051                }
1052
1053                if (vni && mdb_parse_vni(&req.n, sizeof(req), vni,
1054                                         MDBE_ATTR_VNI)) {
1055                        fprintf(stderr, "Invalid destination VNI \"%s\"\n",
1056                                vni);
1057                        return -1;
1058                }
1059
1060                if (src_vni && mdb_parse_vni(&req.n, sizeof(req), src_vni,
1061                                             MDBE_ATTR_SRC_VNI)) {
1062                        fprintf(stderr, "Invalid source VNI \"%s\"\n", src_vni);
1063                        return -1;
1064                }
1065
1066                if (state_mask)
1067                        addattr8(&req.n, sizeof(req), MDBE_ATTR_STATE_MASK,
1068                                 state_mask);
1069
1070                addattr_nest_end(&req.n, nest);
1071        }
1072
1073        if (rtnl_talk(&rth, &req.n, NULL) < 0)
1074                return -1;
1075
1076        return 0;
1077}
1078
1079int do_mdb(int argc, char **argv)
1080{
1081        ll_init_map(&rth);
1082        timestamp = 0;
1083
1084        if (argc > 0) {
1085                if (matches(*argv, "add") == 0)
1086                        return mdb_modify(RTM_NEWMDB, NLM_F_CREATE|NLM_F_EXCL, argc-1, argv+1);
1087                if (strcmp(*argv, "replace") == 0)
1088                        return mdb_modify(RTM_NEWMDB, NLM_F_CREATE|NLM_F_REPLACE, argc-1, argv+1);
1089                if (matches(*argv, "delete") == 0)
1090                        return mdb_modify(RTM_DELMDB, 0, argc-1, argv+1);
1091
1092                if (matches(*argv, "show") == 0 ||
1093                    matches(*argv, "lst") == 0 ||
1094                    matches(*argv, "list") == 0)
1095                        return mdb_show(argc-1, argv+1);
1096                if (strcmp(*argv, "get") == 0)
1097                        return mdb_get(argc-1, argv+1);
1098                if (strcmp(*argv, "flush") == 0)
1099                        return mdb_flush(argc-1, argv+1);
1100                if (matches(*argv, "help") == 0)
1101                        usage();
1102        } else
1103                return mdb_show(0, NULL);
1104
1105        fprintf(stderr, "Command \"%s\" is unknown, try \"bridge mdb help\".\n", *argv);
1106        exit(-1);
1107}
1108