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        if (e->flags & MDB_FLAGS_OFFLOAD_FAILED)
 260                print_string(PRINT_ANY, NULL, " %s", "offload_failed");
 261        close_json_array(PRINT_JSON, NULL);
 262
 263        if (e->vid)
 264                print_uint(PRINT_ANY, "vid", " vid %u", e->vid);
 265
 266        if (tb[MDBA_MDB_EATTR_DST])
 267                print_dst(tb[MDBA_MDB_EATTR_DST]);
 268
 269        if (tb[MDBA_MDB_EATTR_DST_PORT])
 270                print_uint(PRINT_ANY, "dst_port", " dst_port %u",
 271                           rta_getattr_u16(tb[MDBA_MDB_EATTR_DST_PORT]));
 272
 273        if (tb[MDBA_MDB_EATTR_VNI])
 274                print_uint(PRINT_ANY, "vni", " vni %u",
 275                           rta_getattr_u32(tb[MDBA_MDB_EATTR_VNI]));
 276
 277        if (tb[MDBA_MDB_EATTR_SRC_VNI])
 278                print_uint(PRINT_ANY, "src_vni", " src_vni %u",
 279                           rta_getattr_u32(tb[MDBA_MDB_EATTR_SRC_VNI]));
 280
 281        if (tb[MDBA_MDB_EATTR_IFINDEX]) {
 282                unsigned int ifindex;
 283
 284                ifindex = rta_getattr_u32(tb[MDBA_MDB_EATTR_IFINDEX]);
 285                print_string(PRINT_ANY, "via", " via %s",
 286                             ll_index_to_name(ifindex));
 287        }
 288
 289        if (show_stats && tb && tb[MDBA_MDB_EATTR_TIMER]) {
 290                __u32 timer = rta_getattr_u32(tb[MDBA_MDB_EATTR_TIMER]);
 291
 292                print_string(PRINT_ANY, "timer", " %s",
 293                             format_timer(timer, 1));
 294        }
 295
 296        print_nl();
 297        close_json_object();
 298}
 299
 300static void br_print_mdb_entry(FILE *f, int ifindex, struct rtattr *attr,
 301                               struct nlmsghdr *n)
 302{
 303        struct rtattr *etb[MDBA_MDB_EATTR_MAX + 1];
 304        struct br_mdb_entry *e;
 305        struct rtattr *i;
 306        int rem;
 307
 308        rem = RTA_PAYLOAD(attr);
 309        for (i = RTA_DATA(attr); RTA_OK(i, rem); i = RTA_NEXT(i, rem)) {
 310                e = RTA_DATA(i);
 311                parse_rtattr_flags(etb, MDBA_MDB_EATTR_MAX, MDB_RTA(RTA_DATA(i)),
 312                                   RTA_PAYLOAD(i) - RTA_ALIGN(sizeof(*e)),
 313                                   NLA_F_NESTED);
 314                print_mdb_entry(f, ifindex, e, n, etb);
 315        }
 316}
 317
 318static void print_mdb_entries(FILE *fp, struct nlmsghdr *n,
 319                              int ifindex,  struct rtattr *mdb)
 320{
 321        int rem = RTA_PAYLOAD(mdb);
 322        struct rtattr *i;
 323
 324        for (i = RTA_DATA(mdb); RTA_OK(i, rem); i = RTA_NEXT(i, rem))
 325                br_print_mdb_entry(fp, ifindex, i, n);
 326}
 327
 328static void print_router_entries(FILE *fp, struct nlmsghdr *n,
 329                                 int ifindex, struct rtattr *router)
 330{
 331        const char *brifname = ll_index_to_name(ifindex);
 332
 333        if (n->nlmsg_type == RTM_GETMDB) {
 334                if (show_details)
 335                        br_print_router_ports(fp, router, brifname);
 336        } else {
 337                struct rtattr *i = RTA_DATA(router);
 338                uint32_t *port_ifindex = RTA_DATA(i);
 339                const char *port_name = ll_index_to_name(*port_ifindex);
 340
 341                if (is_json_context()) {
 342                        open_json_array(PRINT_JSON, brifname);
 343                        open_json_object(NULL);
 344
 345                        print_string(PRINT_JSON, "port", NULL,
 346                                     port_name);
 347                        close_json_object();
 348                        close_json_array(PRINT_JSON, NULL);
 349                } else {
 350                        fprintf(fp, "router port dev %s master %s\n",
 351                                port_name, brifname);
 352                }
 353        }
 354}
 355
 356static int __parse_mdb_nlmsg(struct nlmsghdr *n, struct rtattr **tb)
 357{
 358        struct br_port_msg *r = NLMSG_DATA(n);
 359        int len = n->nlmsg_len;
 360
 361        if (n->nlmsg_type != RTM_GETMDB &&
 362            n->nlmsg_type != RTM_NEWMDB &&
 363            n->nlmsg_type != RTM_DELMDB) {
 364                fprintf(stderr,
 365                        "Not RTM_GETMDB, RTM_NEWMDB or RTM_DELMDB: %08x %08x %08x\n",
 366                        n->nlmsg_len, n->nlmsg_type, n->nlmsg_flags);
 367
 368                return 0;
 369        }
 370
 371        len -= NLMSG_LENGTH(sizeof(*r));
 372        if (len < 0) {
 373                fprintf(stderr, "BUG: wrong nlmsg len %d\n", len);
 374                return -1;
 375        }
 376
 377        if (filter_index && filter_index != r->ifindex)
 378                return 0;
 379
 380        parse_rtattr(tb, MDBA_MAX, MDBA_RTA(r), n->nlmsg_len - NLMSG_LENGTH(sizeof(*r)));
 381
 382        return 1;
 383}
 384
 385static int print_mdbs(struct nlmsghdr *n, void *arg)
 386{
 387        struct br_port_msg *r = NLMSG_DATA(n);
 388        struct rtattr *tb[MDBA_MAX+1];
 389        FILE *fp = arg;
 390        int ret;
 391
 392        ret = __parse_mdb_nlmsg(n, tb);
 393        if (ret != 1)
 394                return ret;
 395
 396        if (tb[MDBA_MDB])
 397                print_mdb_entries(fp, n, r->ifindex, tb[MDBA_MDB]);
 398
 399        return 0;
 400}
 401
 402static int print_rtrs(struct nlmsghdr *n, void *arg)
 403{
 404        struct br_port_msg *r = NLMSG_DATA(n);
 405        struct rtattr *tb[MDBA_MAX+1];
 406        FILE *fp = arg;
 407        int ret;
 408
 409        ret = __parse_mdb_nlmsg(n, tb);
 410        if (ret != 1)
 411                return ret;
 412
 413        if (tb[MDBA_ROUTER])
 414                print_router_entries(fp, n, r->ifindex, tb[MDBA_ROUTER]);
 415
 416        return 0;
 417}
 418
 419int print_mdb_mon(struct nlmsghdr *n, void *arg)
 420{
 421        struct br_port_msg *r = NLMSG_DATA(n);
 422        struct rtattr *tb[MDBA_MAX+1];
 423        FILE *fp = arg;
 424        int ret;
 425
 426        ret = __parse_mdb_nlmsg(n, tb);
 427        if (ret != 1)
 428                return ret;
 429
 430        print_headers(fp, "[MDB]");
 431
 432        if (n->nlmsg_type == RTM_DELMDB)
 433                print_bool(PRINT_ANY, "deleted", "Deleted ", true);
 434
 435        if (tb[MDBA_MDB])
 436                print_mdb_entries(fp, n, r->ifindex, tb[MDBA_MDB]);
 437
 438        if (tb[MDBA_ROUTER])
 439                print_router_entries(fp, n, r->ifindex, tb[MDBA_ROUTER]);
 440
 441        return 0;
 442}
 443
 444static int mdb_show(int argc, char **argv)
 445{
 446        char *filter_dev = NULL;
 447
 448        while (argc > 0) {
 449                if (strcmp(*argv, "dev") == 0) {
 450                        NEXT_ARG();
 451                        if (filter_dev)
 452                                duparg("dev", *argv);
 453                        filter_dev = *argv;
 454                } else if (strcmp(*argv, "vid") == 0) {
 455                        NEXT_ARG();
 456                        if (filter_vlan)
 457                                duparg("vid", *argv);
 458                        filter_vlan = atoi(*argv);
 459                }
 460                argc--; argv++;
 461        }
 462
 463        if (filter_dev) {
 464                filter_index = ll_name_to_index(filter_dev);
 465                if (!filter_index)
 466                        return nodev(filter_dev);
 467        }
 468
 469        new_json_obj(json);
 470        open_json_object(NULL);
 471
 472        /* get mdb entries */
 473        if (rtnl_mdbdump_req(&rth, PF_BRIDGE) < 0) {
 474                perror("Cannot send dump request");
 475                delete_json_obj();
 476                return -1;
 477        }
 478
 479        open_json_array(PRINT_JSON, "mdb");
 480        if (rtnl_dump_filter(&rth, print_mdbs, stdout) < 0) {
 481                fprintf(stderr, "Dump terminated\n");
 482                delete_json_obj();
 483                return -1;
 484        }
 485        close_json_array(PRINT_JSON, NULL);
 486
 487        /* get router ports */
 488        if (rtnl_mdbdump_req(&rth, PF_BRIDGE) < 0) {
 489                perror("Cannot send dump request");
 490                delete_json_obj();
 491                return -1;
 492        }
 493
 494        open_json_object("router");
 495        if (rtnl_dump_filter(&rth, print_rtrs, stdout) < 0) {
 496                fprintf(stderr, "Dump terminated\n");
 497                delete_json_obj();
 498                return -1;
 499        }
 500        close_json_object();
 501
 502        close_json_object();
 503        delete_json_obj();
 504        fflush(stdout);
 505
 506        return 0;
 507}
 508
 509static int mdb_parse_grp(const char *grp, struct br_mdb_entry *e)
 510{
 511        if (inet_pton(AF_INET, grp, &e->addr.u.ip4)) {
 512                e->addr.proto = htons(ETH_P_IP);
 513                return 0;
 514        }
 515        if (inet_pton(AF_INET6, grp, &e->addr.u.ip6)) {
 516                e->addr.proto = htons(ETH_P_IPV6);
 517                return 0;
 518        }
 519        if (ll_addr_a2n((char *)e->addr.u.mac_addr, sizeof(e->addr.u.mac_addr),
 520                        grp) == ETH_ALEN) {
 521                e->addr.proto = 0;
 522                return 0;
 523        }
 524
 525        return -1;
 526}
 527
 528static int mdb_parse_src(struct nlmsghdr *n, int maxlen, const char *src)
 529{
 530        struct in6_addr src_ip6;
 531        __be32 src_ip4;
 532
 533        if (inet_pton(AF_INET, src, &src_ip4)) {
 534                addattr32(n, maxlen, MDBE_ATTR_SOURCE, src_ip4);
 535                return 0;
 536        }
 537
 538        if (inet_pton(AF_INET6, src, &src_ip6)) {
 539                addattr_l(n, maxlen, MDBE_ATTR_SOURCE, &src_ip6,
 540                          sizeof(src_ip6));
 541                return 0;
 542        }
 543
 544        return -1;
 545}
 546
 547static int mdb_parse_mode(struct nlmsghdr *n, int maxlen, const char *mode)
 548{
 549        if (strcmp(mode, "include") == 0) {
 550                addattr8(n, maxlen, MDBE_ATTR_GROUP_MODE, MCAST_INCLUDE);
 551                return 0;
 552        }
 553
 554        if (strcmp(mode, "exclude") == 0) {
 555                addattr8(n, maxlen, MDBE_ATTR_GROUP_MODE, MCAST_EXCLUDE);
 556                return 0;
 557        }
 558
 559        return -1;
 560}
 561
 562static int mdb_parse_src_entry(struct nlmsghdr *n, int maxlen, char *src_entry)
 563{
 564        struct in6_addr src_ip6;
 565        struct rtattr *nest;
 566        __be32 src_ip4;
 567
 568        nest = addattr_nest(n, maxlen, MDBE_SRC_LIST_ENTRY | NLA_F_NESTED);
 569
 570        if (inet_pton(AF_INET, src_entry, &src_ip4))
 571                addattr32(n, maxlen, MDBE_SRCATTR_ADDRESS, src_ip4);
 572        else if (inet_pton(AF_INET6, src_entry, &src_ip6))
 573                addattr_l(n, maxlen, MDBE_SRCATTR_ADDRESS, &src_ip6,
 574                          sizeof(src_ip6));
 575        else
 576                return -1;
 577
 578        addattr_nest_end(n, nest);
 579
 580        return 0;
 581}
 582
 583static int mdb_parse_src_list(struct nlmsghdr *n, int maxlen, char *src_list)
 584{
 585        struct rtattr *nest;
 586        char *sep;
 587
 588        nest = addattr_nest(n, maxlen, MDBE_ATTR_SRC_LIST | NLA_F_NESTED);
 589
 590        do {
 591                sep = strchr(src_list, ',');
 592                if (sep)
 593                        *sep = '\0';
 594
 595                if (mdb_parse_src_entry(n, maxlen, src_list)) {
 596                        fprintf(stderr, "Invalid source entry \"%s\" in source list\n",
 597                                src_list);
 598                        return -1;
 599                }
 600
 601                src_list = sep + 1;
 602        } while (sep);
 603
 604        addattr_nest_end(n, nest);
 605
 606        return 0;
 607}
 608
 609static int mdb_parse_proto(struct nlmsghdr *n, int maxlen, const char *proto)
 610{
 611        __u32 proto_id;
 612        int err;
 613
 614        err = rtnl_rtprot_a2n(&proto_id, proto);
 615        if (err)
 616                return err;
 617
 618        addattr8(n, maxlen, MDBE_ATTR_RTPROT, proto_id);
 619
 620        return 0;
 621}
 622
 623static int mdb_parse_dst(struct nlmsghdr *n, int maxlen, const char *dst)
 624{
 625        struct in6_addr dst_ip6;
 626        __be32 dst_ip4;
 627
 628        if (inet_pton(AF_INET, dst, &dst_ip4)) {
 629                addattr32(n, maxlen, MDBE_ATTR_DST, dst_ip4);
 630                return 0;
 631        }
 632
 633        if (inet_pton(AF_INET6, dst, &dst_ip6)) {
 634                addattr_l(n, maxlen, MDBE_ATTR_DST, &dst_ip6,
 635                          sizeof(dst_ip6));
 636                return 0;
 637        }
 638
 639        return -1;
 640}
 641
 642static int mdb_parse_dst_port(struct nlmsghdr *n, int maxlen,
 643                              const char *dst_port)
 644{
 645        unsigned long port;
 646        char *endptr;
 647
 648        port = strtoul(dst_port, &endptr, 0);
 649        if (endptr && *endptr) {
 650                struct servent *pse;
 651
 652                pse = getservbyname(dst_port, "udp");
 653                if (!pse)
 654                        return -1;
 655                port = ntohs(pse->s_port);
 656        } else if (port > USHRT_MAX) {
 657                return -1;
 658        }
 659
 660        addattr16(n, maxlen, MDBE_ATTR_DST_PORT, port);
 661
 662        return 0;
 663}
 664
 665static int mdb_parse_vni(struct nlmsghdr *n, int maxlen, const char *vni,
 666                         int attr_type)
 667{
 668        unsigned long vni_num;
 669        char *endptr;
 670
 671        vni_num = strtoul(vni, &endptr, 0);
 672        if ((endptr && *endptr) || vni_num == ULONG_MAX)
 673                return -1;
 674
 675        addattr32(n, maxlen, attr_type, vni_num);
 676
 677        return 0;
 678}
 679
 680static int mdb_parse_dev(struct nlmsghdr *n, int maxlen, const char *dev)
 681{
 682        unsigned int ifindex;
 683
 684        ifindex = ll_name_to_index(dev);
 685        if (!ifindex)
 686                return -1;
 687
 688        addattr32(n, maxlen, MDBE_ATTR_IFINDEX, ifindex);
 689
 690        return 0;
 691}
 692
 693static int mdb_modify(int cmd, int flags, int argc, char **argv)
 694{
 695        struct {
 696                struct nlmsghdr n;
 697                struct br_port_msg      bpm;
 698                char                    buf[1024];
 699        } req = {
 700                .n.nlmsg_len = NLMSG_LENGTH(sizeof(struct br_port_msg)),
 701                .n.nlmsg_flags = NLM_F_REQUEST | flags,
 702                .n.nlmsg_type = cmd,
 703                .bpm.family = PF_BRIDGE,
 704        };
 705        char *d = NULL, *p = NULL, *grp = NULL, *src = NULL, *mode = NULL;
 706        char *dst_port = NULL, *vni = NULL, *src_vni = NULL, *via = NULL;
 707        char *src_list = NULL, *proto = NULL, *dst = NULL;
 708        struct br_mdb_entry entry = {};
 709        bool set_attrs = false;
 710        short vid = 0;
 711
 712        while (argc > 0) {
 713                if (strcmp(*argv, "dev") == 0) {
 714                        NEXT_ARG();
 715                        d = *argv;
 716                } else if (strcmp(*argv, "grp") == 0) {
 717                        NEXT_ARG();
 718                        grp = *argv;
 719                } else if (strcmp(*argv, "port") == 0) {
 720                        NEXT_ARG();
 721                        p = *argv;
 722                } else if (strcmp(*argv, "permanent") == 0) {
 723                        if (cmd == RTM_NEWMDB)
 724                                entry.state |= MDB_PERMANENT;
 725                } else if (strcmp(*argv, "temp") == 0) {
 726                        ;/* nothing */
 727                } else if (strcmp(*argv, "vid") == 0) {
 728                        NEXT_ARG();
 729                        vid = atoi(*argv);
 730                } else if (strcmp(*argv, "src") == 0) {
 731                        NEXT_ARG();
 732                        src = *argv;
 733                        set_attrs = true;
 734                } else if (strcmp(*argv, "filter_mode") == 0) {
 735                        NEXT_ARG();
 736                        mode = *argv;
 737                        set_attrs = true;
 738                } else if (strcmp(*argv, "source_list") == 0) {
 739                        NEXT_ARG();
 740                        src_list = *argv;
 741                        set_attrs = true;
 742                } else if (strcmp(*argv, "proto") == 0) {
 743                        NEXT_ARG();
 744                        proto = *argv;
 745                        set_attrs = true;
 746                } else if (strcmp(*argv, "dst") == 0) {
 747                        NEXT_ARG();
 748                        dst = *argv;
 749                        set_attrs = true;
 750                } else if (strcmp(*argv, "dst_port") == 0) {
 751                        NEXT_ARG();
 752                        dst_port = *argv;
 753                        set_attrs = true;
 754                } else if (strcmp(*argv, "vni") == 0) {
 755                        NEXT_ARG();
 756                        vni = *argv;
 757                        set_attrs = true;
 758                } else if (strcmp(*argv, "src_vni") == 0) {
 759                        NEXT_ARG();
 760                        src_vni = *argv;
 761                        set_attrs = true;
 762                } else if (strcmp(*argv, "via") == 0) {
 763                        NEXT_ARG();
 764                        via = *argv;
 765                        set_attrs = true;
 766                } else {
 767                        if (matches(*argv, "help") == 0)
 768                                usage();
 769                }
 770                argc--; argv++;
 771        }
 772
 773        if (d == NULL || grp == NULL || p == NULL) {
 774                fprintf(stderr, "Device, group address and port name are required arguments.\n");
 775                return -1;
 776        }
 777
 778        req.bpm.ifindex = ll_name_to_index(d);
 779        if (!req.bpm.ifindex)
 780                return nodev(d);
 781
 782        entry.ifindex = ll_name_to_index(p);
 783        if (!entry.ifindex)
 784                return nodev(p);
 785
 786        if (mdb_parse_grp(grp, &entry)) {
 787                fprintf(stderr, "Invalid address \"%s\"\n", grp);
 788                return -1;
 789        }
 790
 791        entry.vid = vid;
 792        addattr_l(&req.n, sizeof(req), MDBA_SET_ENTRY, &entry, sizeof(entry));
 793        if (set_attrs) {
 794                struct rtattr *nest = addattr_nest(&req.n, sizeof(req),
 795                                                   MDBA_SET_ENTRY_ATTRS);
 796
 797                nest->rta_type |= NLA_F_NESTED;
 798
 799                if (src && mdb_parse_src(&req.n, sizeof(req), src)) {
 800                        fprintf(stderr, "Invalid source address \"%s\"\n", src);
 801                        return -1;
 802                }
 803
 804                if (mode && mdb_parse_mode(&req.n, sizeof(req), mode)) {
 805                        fprintf(stderr, "Invalid filter mode \"%s\"\n", mode);
 806                        return -1;
 807                }
 808
 809                if (src_list && mdb_parse_src_list(&req.n, sizeof(req),
 810                                                   src_list))
 811                        return -1;
 812
 813                if (proto && mdb_parse_proto(&req.n, sizeof(req), proto)) {
 814                        fprintf(stderr, "Invalid protocol value \"%s\"\n",
 815                                proto);
 816                        return -1;
 817                }
 818
 819                if (dst && mdb_parse_dst(&req.n, sizeof(req), dst)) {
 820                        fprintf(stderr, "Invalid underlay destination address \"%s\"\n",
 821                                dst);
 822                        return -1;
 823                }
 824
 825                if (dst_port && mdb_parse_dst_port(&req.n, sizeof(req),
 826                                                   dst_port)) {
 827                        fprintf(stderr, "Invalid destination port \"%s\"\n", dst_port);
 828                        return -1;
 829                }
 830
 831                if (vni && mdb_parse_vni(&req.n, sizeof(req), vni,
 832                                         MDBE_ATTR_VNI)) {
 833                        fprintf(stderr, "Invalid destination VNI \"%s\"\n",
 834                                vni);
 835                        return -1;
 836                }
 837
 838                if (src_vni && mdb_parse_vni(&req.n, sizeof(req), src_vni,
 839                                             MDBE_ATTR_SRC_VNI)) {
 840                        fprintf(stderr, "Invalid source VNI \"%s\"\n", src_vni);
 841                        return -1;
 842                }
 843
 844                if (via && mdb_parse_dev(&req.n, sizeof(req), via))
 845                        return nodev(via);
 846
 847                addattr_nest_end(&req.n, nest);
 848        }
 849
 850        if (rtnl_talk(&rth, &req.n, NULL) < 0)
 851                return -1;
 852
 853        return 0;
 854}
 855
 856static int mdb_get(int argc, char **argv)
 857{
 858        struct {
 859                struct nlmsghdr n;
 860                struct br_port_msg      bpm;
 861                char                    buf[1024];
 862        } req = {
 863                .n.nlmsg_len = NLMSG_LENGTH(sizeof(struct br_port_msg)),
 864                .n.nlmsg_flags = NLM_F_REQUEST,
 865                .n.nlmsg_type = RTM_GETMDB,
 866                .bpm.family = PF_BRIDGE,
 867        };
 868        char *d = NULL, *grp = NULL, *src = NULL, *src_vni = NULL;
 869        struct br_mdb_entry entry = {};
 870        struct nlmsghdr *answer;
 871        bool get_attrs = false;
 872        short vid = 0;
 873        int ret = 0;
 874
 875        while (argc > 0) {
 876                if (strcmp(*argv, "dev") == 0) {
 877                        NEXT_ARG();
 878                        d = *argv;
 879                } else if (strcmp(*argv, "grp") == 0) {
 880                        NEXT_ARG();
 881                        grp = *argv;
 882                } else if (strcmp(*argv, "vid") == 0) {
 883                        NEXT_ARG();
 884                        vid = atoi(*argv);
 885                } else if (strcmp(*argv, "src") == 0) {
 886                        NEXT_ARG();
 887                        src = *argv;
 888                        get_attrs = true;
 889                } else if (strcmp(*argv, "src_vni") == 0) {
 890                        NEXT_ARG();
 891                        src_vni = *argv;
 892                        get_attrs = true;
 893                } else {
 894                        if (strcmp(*argv, "help") == 0)
 895                                usage();
 896                }
 897                argc--; argv++;
 898        }
 899
 900        if (d == NULL || grp == NULL) {
 901                fprintf(stderr, "Device and group address are required arguments.\n");
 902                return -1;
 903        }
 904
 905        req.bpm.ifindex = ll_name_to_index(d);
 906        if (!req.bpm.ifindex)
 907                return nodev(d);
 908
 909        if (mdb_parse_grp(grp, &entry)) {
 910                fprintf(stderr, "Invalid address \"%s\"\n", grp);
 911                return -1;
 912        }
 913
 914        entry.vid = vid;
 915        addattr_l(&req.n, sizeof(req), MDBA_GET_ENTRY, &entry, sizeof(entry));
 916        if (get_attrs) {
 917                struct rtattr *nest = addattr_nest(&req.n, sizeof(req),
 918                                                   MDBA_GET_ENTRY_ATTRS);
 919
 920                nest->rta_type |= NLA_F_NESTED;
 921
 922                if (src && mdb_parse_src(&req.n, sizeof(req), src)) {
 923                        fprintf(stderr, "Invalid source address \"%s\"\n", src);
 924                        return -1;
 925                }
 926
 927                if (src_vni && mdb_parse_vni(&req.n, sizeof(req), src_vni,
 928                                             MDBE_ATTR_SRC_VNI)) {
 929                        fprintf(stderr, "Invalid source VNI \"%s\"\n", src_vni);
 930                        return -1;
 931                }
 932
 933                addattr_nest_end(&req.n, nest);
 934        }
 935
 936        if (rtnl_talk(&rth, &req.n, &answer) < 0)
 937                return -2;
 938
 939        new_json_obj(json);
 940
 941        if (print_mdbs(answer, stdout) < 0)
 942                ret = -1;
 943
 944        delete_json_obj();
 945        free(answer);
 946
 947        return ret;
 948}
 949
 950static int mdb_flush(int argc, char **argv)
 951{
 952        struct {
 953                struct nlmsghdr n;
 954                struct br_port_msg      bpm;
 955                char                    buf[1024];
 956        } req = {
 957                .n.nlmsg_len = NLMSG_LENGTH(sizeof(struct br_port_msg)),
 958                .n.nlmsg_flags = NLM_F_REQUEST | NLM_F_BULK,
 959                .n.nlmsg_type = RTM_DELMDB,
 960                .bpm.family = PF_BRIDGE,
 961        };
 962        char *d = NULL, *p = NULL, *src_vni = NULL, *proto = NULL, *dst = NULL;
 963        char *dst_port = NULL, *vni = NULL;
 964        struct br_mdb_entry entry = {};
 965        unsigned short state_mask = 0;
 966        bool set_attrs = false;
 967        short vid = 0;
 968
 969        while (argc > 0) {
 970                if (strcmp(*argv, "dev") == 0) {
 971                        NEXT_ARG();
 972                        d = *argv;
 973                } else if (strcmp(*argv, "port") == 0) {
 974                        NEXT_ARG();
 975                        p = *argv;
 976                } else if (strcmp(*argv, "vid") == 0) {
 977                        NEXT_ARG();
 978                        vid = atoi(*argv);
 979                } else if (strcmp(*argv, "src_vni") == 0) {
 980                        NEXT_ARG();
 981                        src_vni = *argv;
 982                        set_attrs = true;
 983                } else if (strcmp(*argv, "proto") == 0) {
 984                        NEXT_ARG();
 985                        proto = *argv;
 986                        set_attrs = true;
 987                } else if (strcmp(*argv, "permanent") == 0) {
 988                        entry.state |= MDB_PERMANENT;
 989                        state_mask |= MDB_PERMANENT;
 990                        set_attrs = true;
 991                } else if (strcmp(*argv, "nopermanent") == 0) {
 992                        entry.state &= ~MDB_PERMANENT;
 993                        state_mask |= MDB_PERMANENT;
 994                        set_attrs = true;
 995                } else if (strcmp(*argv, "dst") == 0) {
 996                        NEXT_ARG();
 997                        dst = *argv;
 998                        set_attrs = true;
 999                } else if (strcmp(*argv, "dst_port") == 0) {
1000                        NEXT_ARG();
1001                        dst_port = *argv;
1002                        set_attrs = true;
1003                } else if (strcmp(*argv, "vni") == 0) {
1004                        NEXT_ARG();
1005                        vni = *argv;
1006                        set_attrs = true;
1007                } else {
1008                        if (strcmp(*argv, "help") == 0)
1009                                usage();
1010                }
1011                argc--; argv++;
1012        }
1013
1014        if (d == NULL) {
1015                fprintf(stderr, "Device is a required argument.\n");
1016                return -1;
1017        }
1018
1019        req.bpm.ifindex = ll_name_to_index(d);
1020        if (!req.bpm.ifindex)
1021                return nodev(d);
1022
1023        if (p) {
1024                entry.ifindex = ll_name_to_index(p);
1025                if (!entry.ifindex)
1026                        return nodev(p);
1027        }
1028
1029        entry.vid = vid;
1030        addattr_l(&req.n, sizeof(req), MDBA_SET_ENTRY, &entry, sizeof(entry));
1031        if (set_attrs) {
1032                struct rtattr *nest = addattr_nest(&req.n, sizeof(req),
1033                                                   MDBA_SET_ENTRY_ATTRS);
1034
1035                nest->rta_type |= NLA_F_NESTED;
1036
1037                if (proto && mdb_parse_proto(&req.n, sizeof(req), proto)) {
1038                        fprintf(stderr, "Invalid protocol value \"%s\"\n",
1039                                proto);
1040                        return -1;
1041                }
1042
1043                if (dst && mdb_parse_dst(&req.n, sizeof(req), dst)) {
1044                        fprintf(stderr, "Invalid underlay destination address \"%s\"\n",
1045                                dst);
1046                        return -1;
1047                }
1048
1049                if (dst_port && mdb_parse_dst_port(&req.n, sizeof(req),
1050                                                   dst_port)) {
1051                        fprintf(stderr, "Invalid destination port \"%s\"\n", dst_port);
1052                        return -1;
1053                }
1054
1055                if (vni && mdb_parse_vni(&req.n, sizeof(req), vni,
1056                                         MDBE_ATTR_VNI)) {
1057                        fprintf(stderr, "Invalid destination VNI \"%s\"\n",
1058                                vni);
1059                        return -1;
1060                }
1061
1062                if (src_vni && mdb_parse_vni(&req.n, sizeof(req), src_vni,
1063                                             MDBE_ATTR_SRC_VNI)) {
1064                        fprintf(stderr, "Invalid source VNI \"%s\"\n", src_vni);
1065                        return -1;
1066                }
1067
1068                if (state_mask)
1069                        addattr8(&req.n, sizeof(req), MDBE_ATTR_STATE_MASK,
1070                                 state_mask);
1071
1072                addattr_nest_end(&req.n, nest);
1073        }
1074
1075        if (rtnl_talk(&rth, &req.n, NULL) < 0)
1076                return -1;
1077
1078        return 0;
1079}
1080
1081int do_mdb(int argc, char **argv)
1082{
1083        ll_init_map(&rth);
1084        timestamp = 0;
1085
1086        if (argc > 0) {
1087                if (matches(*argv, "add") == 0)
1088                        return mdb_modify(RTM_NEWMDB, NLM_F_CREATE|NLM_F_EXCL, argc-1, argv+1);
1089                if (strcmp(*argv, "replace") == 0)
1090                        return mdb_modify(RTM_NEWMDB, NLM_F_CREATE|NLM_F_REPLACE, argc-1, argv+1);
1091                if (matches(*argv, "delete") == 0)
1092                        return mdb_modify(RTM_DELMDB, 0, argc-1, argv+1);
1093
1094                if (matches(*argv, "show") == 0 ||
1095                    matches(*argv, "lst") == 0 ||
1096                    matches(*argv, "list") == 0)
1097                        return mdb_show(argc-1, argv+1);
1098                if (strcmp(*argv, "get") == 0)
1099                        return mdb_get(argc-1, argv+1);
1100                if (strcmp(*argv, "flush") == 0)
1101                        return mdb_flush(argc-1, argv+1);
1102                if (matches(*argv, "help") == 0)
1103                        usage();
1104        } else
1105                return mdb_show(0, NULL);
1106
1107        fprintf(stderr, "Command \"%s\" is unknown, try \"bridge mdb help\".\n", *argv);
1108        exit(-1);
1109}
1110