iproute2/ip/ipnexthop.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * ip nexthop
   4 *
   5 * Copyright (c) 2017-19 David Ahern <dsahern@gmail.com>
   6 */
   7
   8#include <linux/nexthop.h>
   9#include <stdio.h>
  10#include <string.h>
  11#include <rt_names.h>
  12#include <errno.h>
  13
  14#include "utils.h"
  15#include "ip_common.h"
  16
  17static struct {
  18        unsigned int flushed;
  19        unsigned int groups;
  20        unsigned int ifindex;
  21        unsigned int master;
  22        unsigned int proto;
  23        unsigned int fdb;
  24        unsigned int id;
  25        unsigned int nhid;
  26} filter;
  27
  28enum {
  29        IPNH_LIST,
  30        IPNH_FLUSH,
  31};
  32
  33#define RTM_NHA(h)  ((struct rtattr *)(((char *)(h)) + \
  34                        NLMSG_ALIGN(sizeof(struct nhmsg))))
  35
  36static void usage(void) __attribute__((noreturn));
  37
  38static void usage(void)
  39{
  40        fprintf(stderr,
  41                "Usage: ip nexthop { list | flush } [ protocol ID ] SELECTOR\n"
  42                "       ip nexthop { add | replace } id ID NH [ protocol ID ]\n"
  43                "       ip nexthop { get | del } id ID\n"
  44                "       ip nexthop bucket list BUCKET_SELECTOR\n"
  45                "       ip nexthop bucket get id ID index INDEX\n"
  46                "SELECTOR := [ id ID ] [ dev DEV ] [ vrf NAME ] [ master DEV ]\n"
  47                "            [ groups ] [ fdb ]\n"
  48                "BUCKET_SELECTOR := SELECTOR | [ nhid ID ]\n"
  49                "NH := { blackhole | [ via ADDRESS ] [ dev DEV ] [ onlink ]\n"
  50                "        [ encap ENCAPTYPE ENCAPHDR ] |\n"
  51                "        group GROUP [ fdb ] [ type TYPE [ TYPE_ARGS ] ] }\n"
  52                "GROUP := [ <id[,weight]>/<id[,weight]>/... ]\n"
  53                "TYPE := { mpath | resilient }\n"
  54                "TYPE_ARGS := [ RESILIENT_ARGS ]\n"
  55                "RESILIENT_ARGS := [ buckets BUCKETS ] [ idle_timer IDLE ]\n"
  56                "                  [ unbalanced_timer UNBALANCED ]\n"
  57                "ENCAPTYPE := [ mpls ]\n"
  58                "ENCAPHDR := [ MPLSLABEL ]\n");
  59        exit(-1);
  60}
  61
  62static int nh_dump_filter(struct nlmsghdr *nlh, int reqlen)
  63{
  64        int err;
  65
  66        if (filter.ifindex) {
  67                err = addattr32(nlh, reqlen, NHA_OIF, filter.ifindex);
  68                if (err)
  69                        return err;
  70        }
  71
  72        if (filter.groups) {
  73                err = addattr_l(nlh, reqlen, NHA_GROUPS, NULL, 0);
  74                if (err)
  75                        return err;
  76        }
  77
  78        if (filter.master) {
  79                err = addattr32(nlh, reqlen, NHA_MASTER, filter.master);
  80                if (err)
  81                        return err;
  82        }
  83
  84        if (filter.fdb) {
  85                err = addattr_l(nlh, reqlen, NHA_FDB, NULL, 0);
  86                if (err)
  87                        return err;
  88        }
  89
  90        return 0;
  91}
  92
  93static int nh_dump_bucket_filter(struct nlmsghdr *nlh, int reqlen)
  94{
  95        struct rtattr *nest;
  96        int err = 0;
  97
  98        err = nh_dump_filter(nlh, reqlen);
  99        if (err)
 100                return err;
 101
 102        if (filter.id) {
 103                err = addattr32(nlh, reqlen, NHA_ID, filter.id);
 104                if (err)
 105                        return err;
 106        }
 107
 108        if (filter.nhid) {
 109                nest = addattr_nest(nlh, reqlen, NHA_RES_BUCKET);
 110                nest->rta_type |= NLA_F_NESTED;
 111
 112                err = addattr32(nlh, reqlen, NHA_RES_BUCKET_NH_ID,
 113                                filter.nhid);
 114                if (err)
 115                        return err;
 116
 117                addattr_nest_end(nlh, nest);
 118        }
 119
 120        return err;
 121}
 122
 123static struct rtnl_handle rth_del = { .fd = -1 };
 124
 125static int delete_nexthop(__u32 id)
 126{
 127        struct {
 128                struct nlmsghdr n;
 129                struct nhmsg    nhm;
 130                char            buf[64];
 131        } req = {
 132                .n.nlmsg_len = NLMSG_LENGTH(sizeof(struct nhmsg)),
 133                .n.nlmsg_flags = NLM_F_REQUEST,
 134                .n.nlmsg_type = RTM_DELNEXTHOP,
 135                .nhm.nh_family = AF_UNSPEC,
 136        };
 137
 138        req.n.nlmsg_seq = ++rth_del.seq;
 139
 140        addattr32(&req.n, sizeof(req), NHA_ID, id);
 141
 142        if (rtnl_talk(&rth_del, &req.n, NULL) < 0)
 143                return -1;
 144        return 0;
 145}
 146
 147static int flush_nexthop(struct nlmsghdr *nlh, void *arg)
 148{
 149        struct nhmsg *nhm = NLMSG_DATA(nlh);
 150        struct rtattr *tb[NHA_MAX+1];
 151        __u32 id = 0;
 152        int len;
 153
 154        len = nlh->nlmsg_len - NLMSG_SPACE(sizeof(*nhm));
 155        if (len < 0) {
 156                fprintf(stderr, "BUG: wrong nlmsg len %d\n", len);
 157                return -1;
 158        }
 159
 160        if (filter.proto && nhm->nh_protocol != filter.proto)
 161                return 0;
 162
 163        parse_rtattr(tb, NHA_MAX, RTM_NHA(nhm), len);
 164        if (tb[NHA_ID])
 165                id = rta_getattr_u32(tb[NHA_ID]);
 166
 167        if (id && !delete_nexthop(id))
 168                filter.flushed++;
 169
 170        return 0;
 171}
 172
 173static int ipnh_flush(unsigned int all)
 174{
 175        int rc = -2;
 176
 177        if (all) {
 178                filter.groups = 1;
 179                filter.ifindex = 0;
 180                filter.master = 0;
 181        }
 182
 183        if (rtnl_open(&rth_del, 0) < 0) {
 184                fprintf(stderr, "Cannot open rtnetlink\n");
 185                return EXIT_FAILURE;
 186        }
 187again:
 188        if (rtnl_nexthopdump_req(&rth, preferred_family, nh_dump_filter) < 0) {
 189                perror("Cannot send dump request");
 190                goto out;
 191        }
 192
 193        if (rtnl_dump_filter(&rth, flush_nexthop, stdout) < 0) {
 194                fprintf(stderr, "Dump terminated. Failed to flush nexthops\n");
 195                goto out;
 196        }
 197
 198        /* if deleting all, then remove groups first */
 199        if (all && filter.groups) {
 200                filter.groups = 0;
 201                goto again;
 202        }
 203
 204        rc = 0;
 205out:
 206        rtnl_close(&rth_del);
 207        if (!filter.flushed)
 208                printf("Nothing to flush\n");
 209        else
 210                printf("Flushed %d nexthops\n", filter.flushed);
 211
 212        return rc;
 213}
 214
 215static void print_nh_group(FILE *fp, const struct rtattr *grps_attr)
 216{
 217        struct nexthop_grp *nhg = RTA_DATA(grps_attr);
 218        int num = RTA_PAYLOAD(grps_attr) / sizeof(*nhg);
 219        int i;
 220
 221        if (!num || num * sizeof(*nhg) != RTA_PAYLOAD(grps_attr)) {
 222                fprintf(fp, "<invalid nexthop group>");
 223                return;
 224        }
 225
 226        open_json_array(PRINT_JSON, "group");
 227        print_string(PRINT_FP, NULL, "%s", "group ");
 228        for (i = 0; i < num; ++i) {
 229                open_json_object(NULL);
 230
 231                if (i)
 232                        print_string(PRINT_FP, NULL, "%s", "/");
 233
 234                print_uint(PRINT_ANY, "id", "%u", nhg[i].id);
 235                if (nhg[i].weight)
 236                        print_uint(PRINT_ANY, "weight", ",%u", nhg[i].weight + 1);
 237
 238                close_json_object();
 239        }
 240        print_string(PRINT_FP, NULL, "%s", " ");
 241        close_json_array(PRINT_JSON, NULL);
 242}
 243
 244static const char *nh_group_type_name(__u16 type)
 245{
 246        switch (type) {
 247        case NEXTHOP_GRP_TYPE_MPATH:
 248                return "mpath";
 249        case NEXTHOP_GRP_TYPE_RES:
 250                return "resilient";
 251        default:
 252                return "<unknown type>";
 253        }
 254}
 255
 256static void print_nh_group_type(FILE *fp, const struct rtattr *grp_type_attr)
 257{
 258        __u16 type = rta_getattr_u16(grp_type_attr);
 259
 260        if (type == NEXTHOP_GRP_TYPE_MPATH)
 261                /* Do not print type in order not to break existing output. */
 262                return;
 263
 264        print_string(PRINT_ANY, "type", "type %s ", nh_group_type_name(type));
 265}
 266
 267static void print_nh_res_group(FILE *fp, const struct rtattr *res_grp_attr)
 268{
 269        struct rtattr *tb[NHA_RES_GROUP_MAX + 1];
 270        struct rtattr *rta;
 271        struct timeval tv;
 272
 273        parse_rtattr_nested(tb, NHA_RES_GROUP_MAX, res_grp_attr);
 274
 275        open_json_object("resilient_args");
 276
 277        if (tb[NHA_RES_GROUP_BUCKETS])
 278                print_uint(PRINT_ANY, "buckets", "buckets %u ",
 279                           rta_getattr_u16(tb[NHA_RES_GROUP_BUCKETS]));
 280
 281        if (tb[NHA_RES_GROUP_IDLE_TIMER]) {
 282                rta = tb[NHA_RES_GROUP_IDLE_TIMER];
 283                __jiffies_to_tv(&tv, rta_getattr_u32(rta));
 284                print_tv(PRINT_ANY, "idle_timer", "idle_timer %g ", &tv);
 285        }
 286
 287        if (tb[NHA_RES_GROUP_UNBALANCED_TIMER]) {
 288                rta = tb[NHA_RES_GROUP_UNBALANCED_TIMER];
 289                __jiffies_to_tv(&tv, rta_getattr_u32(rta));
 290                print_tv(PRINT_ANY, "unbalanced_timer", "unbalanced_timer %g ",
 291                         &tv);
 292        }
 293
 294        if (tb[NHA_RES_GROUP_UNBALANCED_TIME]) {
 295                rta = tb[NHA_RES_GROUP_UNBALANCED_TIME];
 296                __jiffies_to_tv(&tv, rta_getattr_u32(rta));
 297                print_tv(PRINT_ANY, "unbalanced_time", "unbalanced_time %g ",
 298                         &tv);
 299        }
 300
 301        close_json_object();
 302}
 303
 304static void print_nh_res_bucket(FILE *fp, const struct rtattr *res_bucket_attr)
 305{
 306        struct rtattr *tb[NHA_RES_BUCKET_MAX + 1];
 307
 308        parse_rtattr_nested(tb, NHA_RES_BUCKET_MAX, res_bucket_attr);
 309
 310        open_json_object("bucket");
 311
 312        if (tb[NHA_RES_BUCKET_INDEX])
 313                print_uint(PRINT_ANY, "index", "index %u ",
 314                           rta_getattr_u16(tb[NHA_RES_BUCKET_INDEX]));
 315
 316        if (tb[NHA_RES_BUCKET_IDLE_TIME]) {
 317                struct rtattr *rta = tb[NHA_RES_BUCKET_IDLE_TIME];
 318                struct timeval tv;
 319
 320                __jiffies_to_tv(&tv, rta_getattr_u64(rta));
 321                print_tv(PRINT_ANY, "idle_time", "idle_time %g ", &tv);
 322        }
 323
 324        if (tb[NHA_RES_BUCKET_NH_ID])
 325                print_uint(PRINT_ANY, "nhid", "nhid %u ",
 326                           rta_getattr_u32(tb[NHA_RES_BUCKET_NH_ID]));
 327
 328        close_json_object();
 329}
 330
 331int print_nexthop(struct nlmsghdr *n, void *arg)
 332{
 333        struct nhmsg *nhm = NLMSG_DATA(n);
 334        struct rtattr *tb[NHA_MAX+1];
 335        FILE *fp = (FILE *)arg;
 336        int len;
 337
 338        SPRINT_BUF(b1);
 339
 340        if (n->nlmsg_type != RTM_DELNEXTHOP &&
 341            n->nlmsg_type != RTM_NEWNEXTHOP) {
 342                fprintf(stderr, "Not a nexthop: %08x %08x %08x\n",
 343                        n->nlmsg_len, n->nlmsg_type, n->nlmsg_flags);
 344                return -1;
 345        }
 346
 347        len = n->nlmsg_len - NLMSG_SPACE(sizeof(*nhm));
 348        if (len < 0) {
 349                close_json_object();
 350                fprintf(stderr, "BUG: wrong nlmsg len %d\n", len);
 351                return -1;
 352        }
 353
 354        if (filter.proto && filter.proto != nhm->nh_protocol)
 355                return 0;
 356
 357        parse_rtattr_flags(tb, NHA_MAX, RTM_NHA(nhm), len, NLA_F_NESTED);
 358
 359        open_json_object(NULL);
 360
 361        if (n->nlmsg_type == RTM_DELNEXTHOP)
 362                print_bool(PRINT_ANY, "deleted", "Deleted ", true);
 363
 364        if (tb[NHA_ID])
 365                print_uint(PRINT_ANY, "id", "id %u ",
 366                           rta_getattr_u32(tb[NHA_ID]));
 367
 368        if (tb[NHA_GROUP])
 369                print_nh_group(fp, tb[NHA_GROUP]);
 370
 371        if (tb[NHA_GROUP_TYPE])
 372                print_nh_group_type(fp, tb[NHA_GROUP_TYPE]);
 373
 374        if (tb[NHA_RES_GROUP])
 375                print_nh_res_group(fp, tb[NHA_RES_GROUP]);
 376
 377        if (tb[NHA_ENCAP])
 378                lwt_print_encap(fp, tb[NHA_ENCAP_TYPE], tb[NHA_ENCAP]);
 379
 380        if (tb[NHA_GATEWAY])
 381                print_rta_gateway(fp, nhm->nh_family, tb[NHA_GATEWAY]);
 382
 383        if (tb[NHA_OIF])
 384                print_rta_if(fp, tb[NHA_OIF], "dev");
 385
 386        if (nhm->nh_scope != RT_SCOPE_UNIVERSE || show_details > 0) {
 387                print_string(PRINT_ANY, "scope", "scope %s ",
 388                             rtnl_rtscope_n2a(nhm->nh_scope, b1, sizeof(b1)));
 389        }
 390
 391        if (tb[NHA_BLACKHOLE])
 392                print_null(PRINT_ANY, "blackhole", "blackhole ", NULL);
 393
 394        if (nhm->nh_protocol != RTPROT_UNSPEC || show_details > 0) {
 395                print_string(PRINT_ANY, "protocol", "proto %s ",
 396                             rtnl_rtprot_n2a(nhm->nh_protocol, b1, sizeof(b1)));
 397        }
 398
 399        print_rt_flags(fp, nhm->nh_flags);
 400
 401        if (tb[NHA_FDB])
 402                print_null(PRINT_ANY, "fdb", "fdb", NULL);
 403
 404        print_string(PRINT_FP, NULL, "%s", "\n");
 405        close_json_object();
 406        fflush(fp);
 407
 408        return 0;
 409}
 410
 411int print_nexthop_bucket(struct nlmsghdr *n, void *arg)
 412{
 413        struct nhmsg *nhm = NLMSG_DATA(n);
 414        struct rtattr *tb[NHA_MAX+1];
 415        FILE *fp = (FILE *)arg;
 416        int len;
 417
 418        if (n->nlmsg_type != RTM_DELNEXTHOPBUCKET &&
 419            n->nlmsg_type != RTM_NEWNEXTHOPBUCKET) {
 420                fprintf(stderr, "Not a nexthop bucket: %08x %08x %08x\n",
 421                        n->nlmsg_len, n->nlmsg_type, n->nlmsg_flags);
 422                return -1;
 423        }
 424
 425        len = n->nlmsg_len - NLMSG_SPACE(sizeof(*nhm));
 426        if (len < 0) {
 427                close_json_object();
 428                fprintf(stderr, "BUG: wrong nlmsg len %d\n", len);
 429                return -1;
 430        }
 431
 432        parse_rtattr_flags(tb, NHA_MAX, RTM_NHA(nhm), len, NLA_F_NESTED);
 433
 434        open_json_object(NULL);
 435
 436        if (n->nlmsg_type == RTM_DELNEXTHOP)
 437                print_bool(PRINT_ANY, "deleted", "Deleted ", true);
 438
 439        if (tb[NHA_ID])
 440                print_uint(PRINT_ANY, "id", "id %u ",
 441                           rta_getattr_u32(tb[NHA_ID]));
 442
 443        if (tb[NHA_RES_BUCKET])
 444                print_nh_res_bucket(fp, tb[NHA_RES_BUCKET]);
 445
 446        print_rt_flags(fp, nhm->nh_flags);
 447
 448        print_string(PRINT_FP, NULL, "%s", "\n");
 449        close_json_object();
 450        fflush(fp);
 451
 452        return 0;
 453}
 454
 455static int add_nh_group_attr(struct nlmsghdr *n, int maxlen, char *argv)
 456{
 457        struct nexthop_grp *grps = NULL;
 458        int count = 0, i;
 459        int err = -1;
 460        char *sep, *wsep;
 461
 462        if (*argv != '\0')
 463                count = 1;
 464
 465        /* separator is '/' */
 466        sep = strchr(argv, '/');
 467        while (sep) {
 468                count++;
 469                sep = strchr(sep + 1, '/');
 470        }
 471
 472        if (count == 0)
 473                goto out;
 474
 475        grps = calloc(count, sizeof(*grps));
 476        if (!grps)
 477                goto out;
 478
 479        for (i = 0; i < count; ++i) {
 480                sep = strchr(argv, '/');
 481                if (sep)
 482                        *sep = '\0';
 483
 484                wsep = strchr(argv, ',');
 485                if (wsep)
 486                        *wsep = '\0';
 487
 488                if (get_unsigned(&grps[i].id, argv, 0))
 489                        goto out;
 490                if (wsep) {
 491                        unsigned int w;
 492
 493                        wsep++;
 494                        if (get_unsigned(&w, wsep, 0) || w == 0 || w > 256)
 495                                invarg("\"weight\" is invalid\n", wsep);
 496                        grps[i].weight = w - 1;
 497                }
 498
 499                if (!sep)
 500                        break;
 501
 502                argv = sep + 1;
 503        }
 504
 505        err = addattr_l(n, maxlen, NHA_GROUP, grps, count * sizeof(*grps));
 506out:
 507        free(grps);
 508        return err;
 509}
 510
 511static int read_nh_group_type(const char *name)
 512{
 513        if (strcmp(name, "mpath") == 0)
 514                return NEXTHOP_GRP_TYPE_MPATH;
 515        else if (strcmp(name, "resilient") == 0)
 516                return NEXTHOP_GRP_TYPE_RES;
 517
 518        return __NEXTHOP_GRP_TYPE_MAX;
 519}
 520
 521static void parse_nh_group_type_res(struct nlmsghdr *n, int maxlen, int *argcp,
 522                                    char ***argvp)
 523{
 524        char **argv = *argvp;
 525        struct rtattr *nest;
 526        int argc = *argcp;
 527
 528        if (!NEXT_ARG_OK())
 529                return;
 530
 531        nest = addattr_nest(n, maxlen, NHA_RES_GROUP);
 532        nest->rta_type |= NLA_F_NESTED;
 533
 534        NEXT_ARG_FWD();
 535        while (argc > 0) {
 536                if (strcmp(*argv, "buckets") == 0) {
 537                        __u16 buckets;
 538
 539                        NEXT_ARG();
 540                        if (get_u16(&buckets, *argv, 0))
 541                                invarg("invalid buckets value", *argv);
 542
 543                        addattr16(n, maxlen, NHA_RES_GROUP_BUCKETS, buckets);
 544                } else if (strcmp(*argv, "idle_timer") == 0) {
 545                        __u32 idle_timer;
 546
 547                        NEXT_ARG();
 548                        if (get_unsigned(&idle_timer, *argv, 0) ||
 549                            idle_timer >= ~0UL / 100)
 550                                invarg("invalid idle timer value", *argv);
 551
 552                        addattr32(n, maxlen, NHA_RES_GROUP_IDLE_TIMER,
 553                                  idle_timer * 100);
 554                } else if (strcmp(*argv, "unbalanced_timer") == 0) {
 555                        __u32 unbalanced_timer;
 556
 557                        NEXT_ARG();
 558                        if (get_unsigned(&unbalanced_timer, *argv, 0) ||
 559                            unbalanced_timer >= ~0UL / 100)
 560                                invarg("invalid unbalanced timer value", *argv);
 561
 562                        addattr32(n, maxlen, NHA_RES_GROUP_UNBALANCED_TIMER,
 563                                  unbalanced_timer * 100);
 564                } else {
 565                        break;
 566                }
 567                argc--; argv++;
 568        }
 569
 570        /* argv is currently the first unparsed argument, but ipnh_modify()
 571         * will move to the next, so step back.
 572         */
 573        *argcp = argc + 1;
 574        *argvp = argv - 1;
 575
 576        addattr_nest_end(n, nest);
 577}
 578
 579static void parse_nh_group_type(struct nlmsghdr *n, int maxlen, int *argcp,
 580                                char ***argvp)
 581{
 582        char **argv = *argvp;
 583        int argc = *argcp;
 584        __u16 type;
 585
 586        NEXT_ARG();
 587        type = read_nh_group_type(*argv);
 588        if (type > NEXTHOP_GRP_TYPE_MAX)
 589                invarg("\"type\" value is invalid\n", *argv);
 590
 591        switch (type) {
 592        case NEXTHOP_GRP_TYPE_MPATH:
 593                /* No additional arguments */
 594                break;
 595        case NEXTHOP_GRP_TYPE_RES:
 596                parse_nh_group_type_res(n, maxlen, &argc, &argv);
 597                break;
 598        }
 599
 600        *argcp = argc;
 601        *argvp = argv;
 602
 603        addattr16(n, maxlen, NHA_GROUP_TYPE, type);
 604}
 605
 606static int ipnh_parse_id(const char *argv)
 607{
 608        __u32 id;
 609
 610        if (get_unsigned(&id, argv, 0))
 611                invarg("invalid id value", argv);
 612        return id;
 613}
 614
 615static int ipnh_modify(int cmd, unsigned int flags, int argc, char **argv)
 616{
 617        struct {
 618                struct nlmsghdr n;
 619                struct nhmsg    nhm;
 620                char            buf[1024];
 621        } req = {
 622                .n.nlmsg_len = NLMSG_LENGTH(sizeof(struct nhmsg)),
 623                .n.nlmsg_flags = NLM_F_REQUEST | flags,
 624                .n.nlmsg_type = cmd,
 625                .nhm.nh_family = preferred_family,
 626        };
 627        __u32 nh_flags = 0;
 628
 629        while (argc > 0) {
 630                if (!strcmp(*argv, "id")) {
 631                        NEXT_ARG();
 632                        addattr32(&req.n, sizeof(req), NHA_ID,
 633                                  ipnh_parse_id(*argv));
 634                } else if (!strcmp(*argv, "dev")) {
 635                        int ifindex;
 636
 637                        NEXT_ARG();
 638                        ifindex = ll_name_to_index(*argv);
 639                        if (!ifindex)
 640                                invarg("Device does not exist\n", *argv);
 641                        addattr32(&req.n, sizeof(req), NHA_OIF, ifindex);
 642                        if (req.nhm.nh_family == AF_UNSPEC)
 643                                req.nhm.nh_family = AF_INET;
 644                } else if (strcmp(*argv, "via") == 0) {
 645                        inet_prefix addr;
 646                        int family;
 647
 648                        NEXT_ARG();
 649                        family = read_family(*argv);
 650                        if (family == AF_UNSPEC)
 651                                family = req.nhm.nh_family;
 652                        else
 653                                NEXT_ARG();
 654                        get_addr(&addr, *argv, family);
 655                        if (req.nhm.nh_family == AF_UNSPEC)
 656                                req.nhm.nh_family = addr.family;
 657                        else if (req.nhm.nh_family != addr.family)
 658                                invarg("address family mismatch\n", *argv);
 659                        addattr_l(&req.n, sizeof(req), NHA_GATEWAY,
 660                                  &addr.data, addr.bytelen);
 661                } else if (strcmp(*argv, "encap") == 0) {
 662                        char buf[1024];
 663                        struct rtattr *rta = (void *)buf;
 664
 665                        rta->rta_type = NHA_ENCAP;
 666                        rta->rta_len = RTA_LENGTH(0);
 667
 668                        lwt_parse_encap(rta, sizeof(buf), &argc, &argv,
 669                                        NHA_ENCAP, NHA_ENCAP_TYPE);
 670
 671                        if (rta->rta_len > RTA_LENGTH(0)) {
 672                                addraw_l(&req.n, 1024, RTA_DATA(rta),
 673                                         RTA_PAYLOAD(rta));
 674                        }
 675                } else if (!strcmp(*argv, "blackhole")) {
 676                        addattr_l(&req.n, sizeof(req), NHA_BLACKHOLE, NULL, 0);
 677                        if (req.nhm.nh_family == AF_UNSPEC)
 678                                req.nhm.nh_family = AF_INET;
 679                } else if (!strcmp(*argv, "fdb")) {
 680                        addattr_l(&req.n, sizeof(req), NHA_FDB, NULL, 0);
 681                } else if (!strcmp(*argv, "onlink")) {
 682                        nh_flags |= RTNH_F_ONLINK;
 683                } else if (!strcmp(*argv, "group")) {
 684                        NEXT_ARG();
 685
 686                        if (add_nh_group_attr(&req.n, sizeof(req), *argv))
 687                                invarg("\"group\" value is invalid\n", *argv);
 688                } else if (!strcmp(*argv, "type")) {
 689                        parse_nh_group_type(&req.n, sizeof(req), &argc, &argv);
 690                } else if (matches(*argv, "protocol") == 0) {
 691                        __u32 prot;
 692
 693                        NEXT_ARG();
 694                        if (rtnl_rtprot_a2n(&prot, *argv))
 695                                invarg("\"protocol\" value is invalid\n", *argv);
 696                        req.nhm.nh_protocol = prot;
 697                } else if (strcmp(*argv, "help") == 0) {
 698                        usage();
 699                } else {
 700                        invarg("", *argv);
 701                }
 702                argc--; argv++;
 703        }
 704
 705        req.nhm.nh_flags = nh_flags;
 706
 707        if (rtnl_talk(&rth, &req.n, NULL) < 0)
 708                return -2;
 709
 710        return 0;
 711}
 712
 713static int ipnh_get_id(__u32 id)
 714{
 715        struct {
 716                struct nlmsghdr n;
 717                struct nhmsg    nhm;
 718                char            buf[1024];
 719        } req = {
 720                .n.nlmsg_len = NLMSG_LENGTH(sizeof(struct nhmsg)),
 721                .n.nlmsg_flags = NLM_F_REQUEST,
 722                .n.nlmsg_type  = RTM_GETNEXTHOP,
 723                .nhm.nh_family = preferred_family,
 724        };
 725        struct nlmsghdr *answer;
 726
 727        addattr32(&req.n, sizeof(req), NHA_ID, id);
 728
 729        if (rtnl_talk(&rth, &req.n, &answer) < 0)
 730                return -2;
 731
 732        new_json_obj(json);
 733
 734        if (print_nexthop(answer, (void *)stdout) < 0) {
 735                free(answer);
 736                return -1;
 737        }
 738
 739        delete_json_obj();
 740        fflush(stdout);
 741
 742        free(answer);
 743
 744        return 0;
 745}
 746
 747static int ipnh_list_flush_id(__u32 id, int action)
 748{
 749        int err;
 750
 751        if (action == IPNH_LIST)
 752                return ipnh_get_id(id);
 753
 754        if (rtnl_open(&rth_del, 0) < 0) {
 755                fprintf(stderr, "Cannot open rtnetlink\n");
 756                return EXIT_FAILURE;
 757        }
 758
 759        err = delete_nexthop(id);
 760        rtnl_close(&rth_del);
 761
 762        return err;
 763}
 764
 765static int ipnh_list_flush(int argc, char **argv, int action)
 766{
 767        unsigned int all = (argc == 0);
 768
 769        while (argc > 0) {
 770                if (!matches(*argv, "dev")) {
 771                        NEXT_ARG();
 772                        filter.ifindex = ll_name_to_index(*argv);
 773                        if (!filter.ifindex)
 774                                invarg("Device does not exist\n", *argv);
 775                } else if (!matches(*argv, "groups")) {
 776                        filter.groups = 1;
 777                } else if (!matches(*argv, "master")) {
 778                        NEXT_ARG();
 779                        filter.master = ll_name_to_index(*argv);
 780                        if (!filter.master)
 781                                invarg("Device does not exist\n", *argv);
 782                } else if (matches(*argv, "vrf") == 0) {
 783                        NEXT_ARG();
 784                        if (!name_is_vrf(*argv))
 785                                invarg("Invalid VRF\n", *argv);
 786                        filter.master = ll_name_to_index(*argv);
 787                        if (!filter.master)
 788                                invarg("VRF does not exist\n", *argv);
 789                } else if (!strcmp(*argv, "id")) {
 790                        NEXT_ARG();
 791                        return ipnh_list_flush_id(ipnh_parse_id(*argv), action);
 792                } else if (!matches(*argv, "protocol")) {
 793                        __u32 proto;
 794
 795                        NEXT_ARG();
 796                        if (get_unsigned(&proto, *argv, 0))
 797                                invarg("invalid protocol value", *argv);
 798                        filter.proto = proto;
 799                } else if (!matches(*argv, "fdb")) {
 800                        filter.fdb = 1;
 801                } else if (matches(*argv, "help") == 0) {
 802                        usage();
 803                } else {
 804                        invarg("", *argv);
 805                }
 806                argc--; argv++;
 807        }
 808
 809        if (action == IPNH_FLUSH)
 810                return ipnh_flush(all);
 811
 812        if (rtnl_nexthopdump_req(&rth, preferred_family, nh_dump_filter) < 0) {
 813                perror("Cannot send dump request");
 814                return -2;
 815        }
 816
 817        new_json_obj(json);
 818
 819        if (rtnl_dump_filter(&rth, print_nexthop, stdout) < 0) {
 820                fprintf(stderr, "Dump terminated\n");
 821                return -2;
 822        }
 823
 824        delete_json_obj();
 825        fflush(stdout);
 826
 827        return 0;
 828}
 829
 830static int ipnh_get(int argc, char **argv)
 831{
 832        __u32 id = 0;
 833
 834        while (argc > 0) {
 835                if (!strcmp(*argv, "id")) {
 836                        NEXT_ARG();
 837                        id = ipnh_parse_id(*argv);
 838                } else  {
 839                        usage();
 840                }
 841                argc--; argv++;
 842        }
 843
 844        if (!id) {
 845                usage();
 846                return -1;
 847        }
 848
 849        return ipnh_get_id(id);
 850}
 851
 852static int ipnh_bucket_list(int argc, char **argv)
 853{
 854        while (argc > 0) {
 855                if (!matches(*argv, "dev")) {
 856                        NEXT_ARG();
 857                        filter.ifindex = ll_name_to_index(*argv);
 858                        if (!filter.ifindex)
 859                                invarg("Device does not exist\n", *argv);
 860                } else if (!matches(*argv, "master")) {
 861                        NEXT_ARG();
 862                        filter.master = ll_name_to_index(*argv);
 863                        if (!filter.master)
 864                                invarg("Device does not exist\n", *argv);
 865                } else if (matches(*argv, "vrf") == 0) {
 866                        NEXT_ARG();
 867                        if (!name_is_vrf(*argv))
 868                                invarg("Invalid VRF\n", *argv);
 869                        filter.master = ll_name_to_index(*argv);
 870                        if (!filter.master)
 871                                invarg("VRF does not exist\n", *argv);
 872                } else if (!strcmp(*argv, "id")) {
 873                        NEXT_ARG();
 874                        filter.id = ipnh_parse_id(*argv);
 875                } else if (!strcmp(*argv, "nhid")) {
 876                        NEXT_ARG();
 877                        filter.nhid = ipnh_parse_id(*argv);
 878                } else if (matches(*argv, "help") == 0) {
 879                        usage();
 880                } else {
 881                        invarg("", *argv);
 882                }
 883                argc--; argv++;
 884        }
 885
 886        if (rtnl_nexthop_bucket_dump_req(&rth, preferred_family,
 887                                         nh_dump_bucket_filter) < 0) {
 888                perror("Cannot send dump request");
 889                return -2;
 890        }
 891
 892        new_json_obj(json);
 893
 894        if (rtnl_dump_filter(&rth, print_nexthop_bucket, stdout) < 0) {
 895                fprintf(stderr, "Dump terminated\n");
 896                return -2;
 897        }
 898
 899        delete_json_obj();
 900        fflush(stdout);
 901
 902        return 0;
 903}
 904
 905static int ipnh_bucket_get_id(__u32 id, __u16 bucket_index)
 906{
 907        struct {
 908                struct nlmsghdr n;
 909                struct nhmsg    nhm;
 910                char            buf[1024];
 911        } req = {
 912                .n.nlmsg_len = NLMSG_LENGTH(sizeof(struct nhmsg)),
 913                .n.nlmsg_flags = NLM_F_REQUEST,
 914                .n.nlmsg_type  = RTM_GETNEXTHOPBUCKET,
 915                .nhm.nh_family = preferred_family,
 916        };
 917        struct nlmsghdr *answer;
 918        struct rtattr *nest;
 919
 920        addattr32(&req.n, sizeof(req), NHA_ID, id);
 921
 922        nest = addattr_nest(&req.n, sizeof(req), NHA_RES_BUCKET);
 923        nest->rta_type |= NLA_F_NESTED;
 924
 925        addattr16(&req.n, sizeof(req), NHA_RES_BUCKET_INDEX, bucket_index);
 926
 927        addattr_nest_end(&req.n, nest);
 928
 929        if (rtnl_talk(&rth, &req.n, &answer) < 0)
 930                return -2;
 931
 932        new_json_obj(json);
 933
 934        if (print_nexthop_bucket(answer, (void *)stdout) < 0) {
 935                free(answer);
 936                return -1;
 937        }
 938
 939        delete_json_obj();
 940        fflush(stdout);
 941
 942        free(answer);
 943
 944        return 0;
 945}
 946
 947static int ipnh_bucket_get(int argc, char **argv)
 948{
 949        bool bucket_valid = false;
 950        __u16 bucket_index;
 951        __u32 id = 0;
 952
 953        while (argc > 0) {
 954                if (!strcmp(*argv, "id")) {
 955                        NEXT_ARG();
 956                        id = ipnh_parse_id(*argv);
 957                } else if (!strcmp(*argv, "index")) {
 958                        NEXT_ARG();
 959                        if (get_u16(&bucket_index, *argv, 0))
 960                                invarg("invalid bucket index value", *argv);
 961                        bucket_valid = true;
 962                } else  {
 963                        usage();
 964                }
 965                argc--; argv++;
 966        }
 967
 968        if (!id || !bucket_valid) {
 969                usage();
 970                return -1;
 971        }
 972
 973        return ipnh_bucket_get_id(id, bucket_index);
 974}
 975
 976static int do_ipnh_bucket(int argc, char **argv)
 977{
 978        if (argc < 1)
 979                return ipnh_bucket_list(0, NULL);
 980
 981        if (!matches(*argv, "list") ||
 982            !matches(*argv, "show") ||
 983            !matches(*argv, "lst"))
 984                return ipnh_bucket_list(argc-1, argv+1);
 985
 986        if (!matches(*argv, "get"))
 987                return ipnh_bucket_get(argc-1, argv+1);
 988
 989        if (!matches(*argv, "help"))
 990                usage();
 991
 992        fprintf(stderr,
 993                "Command \"%s\" is unknown, try \"ip nexthop help\".\n", *argv);
 994        exit(-1);
 995}
 996
 997int do_ipnh(int argc, char **argv)
 998{
 999        if (argc < 1)
1000                return ipnh_list_flush(0, NULL, IPNH_LIST);
1001
1002        if (!matches(*argv, "add"))
1003                return ipnh_modify(RTM_NEWNEXTHOP, NLM_F_CREATE|NLM_F_EXCL,
1004                                   argc-1, argv+1);
1005        if (!matches(*argv, "replace"))
1006                return ipnh_modify(RTM_NEWNEXTHOP, NLM_F_CREATE|NLM_F_REPLACE,
1007                                   argc-1, argv+1);
1008        if (!matches(*argv, "delete"))
1009                return ipnh_modify(RTM_DELNEXTHOP, 0, argc-1, argv+1);
1010
1011        if (!matches(*argv, "list") ||
1012            !matches(*argv, "show") ||
1013            !matches(*argv, "lst"))
1014                return ipnh_list_flush(argc-1, argv+1, IPNH_LIST);
1015
1016        if (!matches(*argv, "get"))
1017                return ipnh_get(argc-1, argv+1);
1018
1019        if (!matches(*argv, "flush"))
1020                return ipnh_list_flush(argc-1, argv+1, IPNH_FLUSH);
1021
1022        if (!matches(*argv, "bucket"))
1023                return do_ipnh_bucket(argc-1, argv+1);
1024
1025        if (!matches(*argv, "help"))
1026                usage();
1027
1028        fprintf(stderr,
1029                "Command \"%s\" is unknown, try \"ip nexthop help\".\n", *argv);
1030        exit(-1);
1031}
1032