iproute2/bridge/vni.c
<<
>>
Prefs
   1/* SPDX-License-Identifier: GPL-2.0 */
   2/*
   3 * Command to manage vnifiltering on a vxlan device
   4 *
   5 * Authors:     Roopa Prabhu <roopa@nvidia.com>
   6 */
   7#include <stdio.h>
   8#include <stdlib.h>
   9#include <unistd.h>
  10#include <string.h>
  11#include <fcntl.h>
  12#include <sys/socket.h>
  13#include <sys/time.h>
  14#include <net/if.h>
  15#include <netinet/in.h>
  16#include <linux/if_link.h>
  17#include <linux/if_bridge.h>
  18#include <linux/if_ether.h>
  19
  20#include "json_print.h"
  21#include "libnetlink.h"
  22#include "br_common.h"
  23#include "utils.h"
  24
  25static unsigned int filter_index;
  26
  27/* max len of "<start>-<end>" */
  28#define VXLAN_ID_LEN 17
  29
  30static void usage(void)
  31{
  32        fprintf(stderr,
  33                "Usage: bridge vni { add | del } vni VNI\n"
  34                "               [ { group | remote } IP_ADDRESS ]\n"
  35                "               dev DEV\n"
  36                "       bridge vni { show } [ dev DEV ]\n"
  37                "\n"
  38                "Where: VNI     := 0-16777215\n"
  39               );
  40        exit(-1);
  41}
  42
  43static int parse_vni_filter(const char *argv, struct nlmsghdr *n, int reqsize,
  44                            inet_prefix *group)
  45{
  46        char *vnilist = strdupa(argv);
  47        char *vni = strtok(vnilist, ",");
  48        int group_type = AF_UNSPEC;
  49        struct rtattr *nlvlist_e;
  50        char *v;
  51
  52        if (group && is_addrtype_inet(group))
  53                group_type = (group->family == AF_INET) ?  VXLAN_VNIFILTER_ENTRY_GROUP :
  54                                                     VXLAN_VNIFILTER_ENTRY_GROUP6;
  55
  56        while (vni != NULL) {
  57                __u32 vni_start = 0, vni_end = 0;
  58
  59                v = strchr(vni, '-');
  60                if (v) {
  61                        *v = '\0';
  62                        v++;
  63                        vni_start = atoi(vni);
  64                        vni_end = atoi(v);
  65                } else {
  66                        vni_start = atoi(vni);
  67                }
  68                nlvlist_e = addattr_nest(n, reqsize, VXLAN_VNIFILTER_ENTRY |
  69                                         NLA_F_NESTED);
  70                addattr32(n, 1024, VXLAN_VNIFILTER_ENTRY_START, vni_start);
  71                if (vni_end)
  72                        addattr32(n, 1024, VXLAN_VNIFILTER_ENTRY_END, vni_end);
  73                if (group)
  74                        addattr_l(n, 1024, group_type, group->data, group->bytelen);
  75                addattr_nest_end(n, nlvlist_e);
  76                vni = strtok(NULL, ",");
  77        }
  78
  79        return 0;
  80}
  81
  82static int vni_modify(int cmd, int argc, char **argv)
  83{
  84        struct {
  85                struct nlmsghdr n;
  86                struct tunnel_msg       tmsg;
  87                char                    buf[1024];
  88        } req = {
  89                .n.nlmsg_len = NLMSG_LENGTH(sizeof(struct tunnel_msg)),
  90                .n.nlmsg_flags = NLM_F_REQUEST,
  91                .n.nlmsg_type = cmd,
  92                .tmsg.family = PF_BRIDGE,
  93        };
  94        bool daddr_present = false;
  95        inet_prefix daddr;
  96        char *vni = NULL;
  97        char *d = NULL;
  98
  99        while (argc > 0) {
 100                if (strcmp(*argv, "dev") == 0) {
 101                        NEXT_ARG();
 102                        d = *argv;
 103                } else if (strcmp(*argv, "vni") == 0) {
 104                        NEXT_ARG();
 105                        if (vni)
 106                                duparg("vni", *argv);
 107                        vni = *argv;
 108                } else if (strcmp(*argv, "group") == 0) {
 109                        NEXT_ARG();
 110                        if (daddr_present)
 111                                duparg("destination", *argv);
 112                        get_addr(&daddr, *argv, AF_UNSPEC);
 113                        if (!is_addrtype_inet_multi(&daddr))
 114                                invarg("invalid group address", *argv);
 115                        daddr_present = true;
 116                } else if (strcmp(*argv, "remote") == 0) {
 117                        NEXT_ARG();
 118                        if (daddr_present)
 119                                duparg("destination", *argv);
 120                        get_addr(&daddr, *argv, AF_UNSPEC);
 121                        daddr_present = true;
 122                } else {
 123                        if (strcmp(*argv, "help") == 0)
 124                                usage();
 125                }
 126                argc--; argv++;
 127        }
 128
 129        if (d == NULL || vni == NULL) {
 130                fprintf(stderr, "Device and VNI ID are required arguments.\n");
 131                return -1;
 132        }
 133
 134        parse_vni_filter(vni, &req.n, sizeof(req),
 135                         (daddr_present ? &daddr : NULL));
 136
 137        req.tmsg.ifindex = ll_name_to_index(d);
 138        if (req.tmsg.ifindex == 0) {
 139                fprintf(stderr, "Cannot find vxlan device \"%s\"\n", d);
 140                return -1;
 141        }
 142
 143        if (rtnl_talk(&rth, &req.n, NULL) < 0)
 144                return -1;
 145
 146        return 0;
 147}
 148
 149static void open_vni_port(int ifi_index)
 150{
 151        open_json_object(NULL);
 152        print_color_string(PRINT_ANY, COLOR_IFNAME, "ifname",
 153                           "%-" textify(IFNAMSIZ) "s  ",
 154                           ll_index_to_name(ifi_index));
 155        open_json_array(PRINT_JSON, "vnis");
 156}
 157
 158static void close_vni_port(void)
 159{
 160        close_json_array(PRINT_JSON, NULL);
 161        close_json_object();
 162}
 163
 164static void print_vnifilter_entry_stats(struct rtattr *stats_attr)
 165{
 166        struct rtattr *stb[VNIFILTER_ENTRY_STATS_MAX+1];
 167        __u64 stat;
 168
 169        open_json_object("stats");
 170        parse_rtattr_flags(stb, VNIFILTER_ENTRY_STATS_MAX, RTA_DATA(stats_attr),
 171                           RTA_PAYLOAD(stats_attr), NLA_F_NESTED);
 172
 173        print_nl();
 174        print_string(PRINT_FP, NULL, "%-" textify(IFNAMSIZ) "s    RX: ",
 175                     "");
 176
 177        if (stb[VNIFILTER_ENTRY_STATS_RX_BYTES]) {
 178                stat = rta_getattr_u64(stb[VNIFILTER_ENTRY_STATS_RX_BYTES]);
 179                print_lluint(PRINT_ANY, "rx_bytes", "bytes %llu ", stat);
 180        }
 181        if (stb[VNIFILTER_ENTRY_STATS_RX_PKTS]) {
 182                stat = rta_getattr_u64(stb[VNIFILTER_ENTRY_STATS_RX_PKTS]);
 183                print_lluint(PRINT_ANY, "rx_pkts", "pkts %llu ", stat);
 184        }
 185        if (stb[VNIFILTER_ENTRY_STATS_RX_DROPS]) {
 186                stat = rta_getattr_u64(stb[VNIFILTER_ENTRY_STATS_RX_DROPS]);
 187                print_lluint(PRINT_ANY, "rx_drops", "drops %llu ", stat);
 188        }
 189        if (stb[VNIFILTER_ENTRY_STATS_RX_ERRORS]) {
 190                stat = rta_getattr_u64(stb[VNIFILTER_ENTRY_STATS_RX_ERRORS]);
 191                print_lluint(PRINT_ANY, "rx_errors", "errors %llu ", stat);
 192        }
 193
 194        print_nl();
 195        print_string(PRINT_FP, NULL, "%-" textify(IFNAMSIZ) "s    TX: ",
 196                     "");
 197
 198        if (stb[VNIFILTER_ENTRY_STATS_TX_BYTES]) {
 199                stat = rta_getattr_u64(stb[VNIFILTER_ENTRY_STATS_TX_BYTES]);
 200                print_lluint(PRINT_ANY, "tx_bytes", "bytes %llu ", stat);
 201        }
 202        if (stb[VNIFILTER_ENTRY_STATS_TX_PKTS]) {
 203                stat = rta_getattr_u64(stb[VNIFILTER_ENTRY_STATS_TX_PKTS]);
 204                print_lluint(PRINT_ANY, "tx_pkts", "pkts %llu ", stat);
 205        }
 206        if (stb[VNIFILTER_ENTRY_STATS_TX_DROPS]) {
 207                stat = rta_getattr_u64(stb[VNIFILTER_ENTRY_STATS_TX_DROPS]);
 208                print_lluint(PRINT_ANY, "tx_drops", "drops %llu ", stat);
 209        }
 210        if (stb[VNIFILTER_ENTRY_STATS_TX_ERRORS]) {
 211                stat = rta_getattr_u64(stb[VNIFILTER_ENTRY_STATS_TX_ERRORS]);
 212                print_lluint(PRINT_ANY, "tx_errors", "errors %llu ", stat);
 213        }
 214        close_json_object();
 215}
 216
 217static void print_vni(struct rtattr *t, int ifindex)
 218{
 219        struct rtattr *ttb[VXLAN_VNIFILTER_ENTRY_MAX+1];
 220        __u32 vni_start = 0;
 221        unsigned int width;
 222        __u32 vni_end;
 223
 224        parse_rtattr_flags(ttb, VXLAN_VNIFILTER_ENTRY_MAX, RTA_DATA(t),
 225                           RTA_PAYLOAD(t), NLA_F_NESTED);
 226
 227        if (ttb[VXLAN_VNIFILTER_ENTRY_START])
 228                vni_start = rta_getattr_u32(ttb[VXLAN_VNIFILTER_ENTRY_START]);
 229
 230        if (ttb[VXLAN_VNIFILTER_ENTRY_END])
 231                vni_end = rta_getattr_u32(ttb[VXLAN_VNIFILTER_ENTRY_END]);
 232        else
 233                vni_end = vni_start;
 234
 235        open_json_object(NULL);
 236        width = print_range("vni", vni_start, vni_end);
 237        if (!is_json_context())
 238                printf("%-*s  ", VXLAN_ID_LEN - width, "");
 239
 240        if (ttb[VXLAN_VNIFILTER_ENTRY_GROUP]) {
 241                __be32 addr = rta_getattr_u32(ttb[VXLAN_VNIFILTER_ENTRY_GROUP]);
 242
 243                if (addr) {
 244                        if (IN_MULTICAST(ntohl(addr)))
 245                                print_string(PRINT_ANY,
 246                                             "group",
 247                                             "%s",
 248                                             format_host(AF_INET, 4, &addr));
 249                        else
 250                                print_string(PRINT_ANY,
 251                                             "remote",
 252                                             "%s",
 253                                             format_host(AF_INET, 4, &addr));
 254                }
 255        } else if (ttb[VXLAN_VNIFILTER_ENTRY_GROUP6]) {
 256                struct in6_addr addr;
 257
 258                memcpy(&addr, RTA_DATA(ttb[VXLAN_VNIFILTER_ENTRY_GROUP6]), sizeof(struct in6_addr));
 259                if (!IN6_IS_ADDR_UNSPECIFIED(&addr)) {
 260                        if (IN6_IS_ADDR_MULTICAST(&addr))
 261                                print_string(PRINT_ANY,
 262                                             "group",
 263                                             "%s",
 264                                             format_host(AF_INET6,
 265                                                         sizeof(struct in6_addr),
 266                                                         &addr));
 267                        else
 268                                print_string(PRINT_ANY,
 269                                             "remote",
 270                                             "%s",
 271                                             format_host(AF_INET6,
 272                                                         sizeof(struct in6_addr),
 273                                                         &addr));
 274                }
 275        }
 276
 277        if (ttb[VXLAN_VNIFILTER_ENTRY_STATS])
 278                print_vnifilter_entry_stats(ttb[VXLAN_VNIFILTER_ENTRY_STATS]);
 279
 280        close_json_object();
 281        print_nl();
 282}
 283
 284int print_vnifilter_rtm(struct nlmsghdr *n, void *arg)
 285{
 286        struct tunnel_msg *tmsg = NLMSG_DATA(n);
 287        int len = n->nlmsg_len;
 288        bool opened = false;
 289        struct rtattr *t;
 290        FILE *fp = arg;
 291        int rem;
 292
 293        if (n->nlmsg_type != RTM_NEWTUNNEL &&
 294            n->nlmsg_type != RTM_DELTUNNEL &&
 295            n->nlmsg_type != RTM_GETTUNNEL) {
 296                fprintf(stderr, "Unknown vni tunnel rtm msg: %08x %08x %08x\n",
 297                        n->nlmsg_len, n->nlmsg_type, n->nlmsg_flags);
 298                return 0;
 299        }
 300
 301        len -= NLMSG_LENGTH(sizeof(*tmsg));
 302        if (len < 0) {
 303                fprintf(stderr, "BUG: wrong nlmsg len %d\n", len);
 304                return -1;
 305        }
 306
 307        if (tmsg->family != AF_BRIDGE)
 308                return 0;
 309
 310        if (filter_index && filter_index != tmsg->ifindex)
 311                return 0;
 312
 313        print_headers(fp, "[TUNNEL]");
 314
 315        if (n->nlmsg_type == RTM_DELTUNNEL)
 316                print_bool(PRINT_ANY, "deleted", "Deleted ", true);
 317
 318        rem = len;
 319        for (t = TUNNEL_RTA(tmsg); RTA_OK(t, rem); t = RTA_NEXT(t, rem)) {
 320                if (rta_type(t) != VXLAN_VNIFILTER_ENTRY)
 321                        continue;
 322
 323                if (!opened) {
 324                        open_vni_port(tmsg->ifindex);
 325                        opened = true;
 326                } else {
 327                        print_string(PRINT_FP, NULL, "%-" textify(IFNAMSIZ) "s  ", "");
 328                }
 329
 330                print_vni(t, tmsg->ifindex);
 331        }
 332
 333        if (opened)
 334                close_vni_port();
 335
 336        fflush(stdout);
 337        return 0;
 338}
 339
 340static int vni_show(int argc, char **argv)
 341{
 342        char *filter_dev = NULL;
 343        __u8 flags = 0;
 344        int ret = 0;
 345
 346        while (argc > 0) {
 347                if (strcmp(*argv, "dev") == 0) {
 348                        NEXT_ARG();
 349                        if (filter_dev)
 350                                duparg("dev", *argv);
 351                        filter_dev = *argv;
 352                }
 353                argc--; argv++;
 354        }
 355
 356        if (filter_dev) {
 357                filter_index = ll_name_to_index(filter_dev);
 358                if (!filter_index)
 359                        return nodev(filter_dev);
 360        }
 361
 362        new_json_obj(json);
 363
 364        if (show_stats)
 365                flags = TUNNEL_MSG_FLAG_STATS;
 366
 367        if (rtnl_tunneldump_req(&rth, PF_BRIDGE, filter_index, flags) < 0) {
 368                perror("Cannot send dump request");
 369                exit(1);
 370        }
 371
 372        if (!is_json_context())
 373                printf("%-" textify(IFNAMSIZ) "s  %-"
 374                       textify(VXLAN_ID_LEN) "s  group/remote\n", "dev",
 375                       "vni");
 376
 377        ret = rtnl_dump_filter(&rth, print_vnifilter_rtm, NULL);
 378        if (ret < 0) {
 379                fprintf(stderr, "Dump ternminated\n");
 380                exit(1);
 381        }
 382
 383        delete_json_obj();
 384        fflush(stdout);
 385        return 0;
 386}
 387
 388int do_vni(int argc, char **argv)
 389{
 390        ll_init_map(&rth);
 391        timestamp = 0;
 392
 393        if (argc > 0) {
 394                if (strcmp(*argv, "add") == 0)
 395                        return vni_modify(RTM_NEWTUNNEL, argc-1, argv+1);
 396                if (strcmp(*argv, "delete") == 0 ||
 397                    strcmp(*argv, "del") == 0)
 398                        return vni_modify(RTM_DELTUNNEL, argc-1, argv+1);
 399                if (strcmp(*argv, "show") == 0 ||
 400                    strcmp(*argv, "lst") == 0 ||
 401                    strcmp(*argv, "list") == 0)
 402                        return vni_show(argc-1, argv+1);
 403                if (strcmp(*argv, "help") == 0)
 404                        usage();
 405        } else {
 406                return vni_show(0, NULL);
 407        }
 408
 409        fprintf(stderr, "Command \"%s\" is unknown, try \"bridge vni help\".\n", *argv);
 410        exit(-1);
 411}
 412