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 <stdint.h>
  10#include <stdio.h>
  11#include <string.h>
  12#include <rt_names.h>
  13#include <errno.h>
  14
  15#include "utils.h"
  16#include "ip_common.h"
  17#include "nh_common.h"
  18
  19static struct {
  20        unsigned int flushed;
  21        unsigned int groups;
  22        unsigned int ifindex;
  23        unsigned int master;
  24        unsigned int proto;
  25        unsigned int fdb;
  26        unsigned int id;
  27        unsigned int nhid;
  28} filter;
  29
  30enum {
  31        IPNH_LIST,
  32        IPNH_FLUSH,
  33};
  34
  35#define RTM_NHA(h)  ((struct rtattr *)(((char *)(h)) + \
  36                        NLMSG_ALIGN(sizeof(struct nhmsg))))
  37
  38static struct hlist_head nh_cache[NH_CACHE_SIZE];
  39static struct rtnl_handle nh_cache_rth = { .fd = -1 };
  40
  41static void usage(void) __attribute__((noreturn));
  42
  43static void usage(void)
  44{
  45        fprintf(stderr,
  46                "Usage: ip nexthop { list | flush } [ protocol ID ] SELECTOR\n"
  47                "       ip nexthop { add | replace } id ID NH [ protocol ID ]\n"
  48                "       ip nexthop { get | del } id ID\n"
  49                "       ip nexthop bucket list BUCKET_SELECTOR\n"
  50                "       ip nexthop bucket get id ID index INDEX\n"
  51                "SELECTOR := [ id ID ] [ dev DEV ] [ vrf NAME ] [ master DEV ]\n"
  52                "            [ groups ] [ fdb ]\n"
  53                "BUCKET_SELECTOR := SELECTOR | [ nhid ID ]\n"
  54                "NH := { blackhole | [ via ADDRESS ] [ dev DEV ] [ onlink ]\n"
  55                "        [ encap ENCAPTYPE ENCAPHDR ] |\n"
  56                "        group GROUP [ fdb ] [ type TYPE [ TYPE_ARGS ] ] }\n"
  57                "GROUP := [ <id[,weight]>/<id[,weight]>/... ]\n"
  58                "TYPE := { mpath | resilient }\n"
  59                "TYPE_ARGS := [ RESILIENT_ARGS ]\n"
  60                "RESILIENT_ARGS := [ buckets BUCKETS ] [ idle_timer IDLE ]\n"
  61                "                  [ unbalanced_timer UNBALANCED ]\n"
  62                "ENCAPTYPE := [ mpls ]\n"
  63                "ENCAPHDR := [ MPLSLABEL ]\n");
  64        exit(-1);
  65}
  66
  67static int nh_dump_filter(struct nlmsghdr *nlh, int reqlen)
  68{
  69        int err;
  70
  71        if (filter.ifindex) {
  72                err = addattr32(nlh, reqlen, NHA_OIF, filter.ifindex);
  73                if (err)
  74                        return err;
  75        }
  76
  77        if (filter.groups) {
  78                err = addattr_l(nlh, reqlen, NHA_GROUPS, NULL, 0);
  79                if (err)
  80                        return err;
  81        }
  82
  83        if (filter.master) {
  84                err = addattr32(nlh, reqlen, NHA_MASTER, filter.master);
  85                if (err)
  86                        return err;
  87        }
  88
  89        if (filter.fdb) {
  90                err = addattr_l(nlh, reqlen, NHA_FDB, NULL, 0);
  91                if (err)
  92                        return err;
  93        }
  94
  95        return 0;
  96}
  97
  98static int nh_dump_bucket_filter(struct nlmsghdr *nlh, int reqlen)
  99{
 100        struct rtattr *nest;
 101        int err = 0;
 102
 103        err = nh_dump_filter(nlh, reqlen);
 104        if (err)
 105                return err;
 106
 107        if (filter.id) {
 108                err = addattr32(nlh, reqlen, NHA_ID, filter.id);
 109                if (err)
 110                        return err;
 111        }
 112
 113        if (filter.nhid) {
 114                nest = addattr_nest(nlh, reqlen, NHA_RES_BUCKET);
 115                nest->rta_type |= NLA_F_NESTED;
 116
 117                err = addattr32(nlh, reqlen, NHA_RES_BUCKET_NH_ID,
 118                                filter.nhid);
 119                if (err)
 120                        return err;
 121
 122                addattr_nest_end(nlh, nest);
 123        }
 124
 125        return err;
 126}
 127
 128static struct rtnl_handle rth_del = { .fd = -1 };
 129
 130static int delete_nexthop(__u32 id)
 131{
 132        struct {
 133                struct nlmsghdr n;
 134                struct nhmsg    nhm;
 135                char            buf[64];
 136        } req = {
 137                .n.nlmsg_len = NLMSG_LENGTH(sizeof(struct nhmsg)),
 138                .n.nlmsg_flags = NLM_F_REQUEST,
 139                .n.nlmsg_type = RTM_DELNEXTHOP,
 140                .nhm.nh_family = AF_UNSPEC,
 141        };
 142
 143        req.n.nlmsg_seq = ++rth_del.seq;
 144
 145        addattr32(&req.n, sizeof(req), NHA_ID, id);
 146
 147        if (rtnl_talk(&rth_del, &req.n, NULL) < 0)
 148                return -1;
 149        return 0;
 150}
 151
 152static int flush_nexthop(struct nlmsghdr *nlh, void *arg)
 153{
 154        struct nhmsg *nhm = NLMSG_DATA(nlh);
 155        struct rtattr *tb[NHA_MAX+1];
 156        __u32 id = 0;
 157        int len;
 158
 159        len = nlh->nlmsg_len - NLMSG_SPACE(sizeof(*nhm));
 160        if (len < 0) {
 161                fprintf(stderr, "BUG: wrong nlmsg len %d\n", len);
 162                return -1;
 163        }
 164
 165        if (filter.proto && nhm->nh_protocol != filter.proto)
 166                return 0;
 167
 168        parse_rtattr(tb, NHA_MAX, RTM_NHA(nhm), len);
 169        if (tb[NHA_ID])
 170                id = rta_getattr_u32(tb[NHA_ID]);
 171
 172        if (id && !delete_nexthop(id))
 173                filter.flushed++;
 174
 175        return 0;
 176}
 177
 178static int ipnh_flush(unsigned int all)
 179{
 180        int rc = -2;
 181
 182        if (all) {
 183                filter.groups = 1;
 184                filter.ifindex = 0;
 185                filter.master = 0;
 186        }
 187
 188        if (rtnl_open(&rth_del, 0) < 0) {
 189                fprintf(stderr, "Cannot open rtnetlink\n");
 190                return EXIT_FAILURE;
 191        }
 192again:
 193        if (rtnl_nexthopdump_req(&rth, preferred_family, nh_dump_filter) < 0) {
 194                perror("Cannot send dump request");
 195                goto out;
 196        }
 197
 198        if (rtnl_dump_filter(&rth, flush_nexthop, stdout) < 0) {
 199                fprintf(stderr, "Dump terminated. Failed to flush nexthops\n");
 200                goto out;
 201        }
 202
 203        /* if deleting all, then remove groups first */
 204        if (all && filter.groups) {
 205                filter.groups = 0;
 206                goto again;
 207        }
 208
 209        rc = 0;
 210out:
 211        rtnl_close(&rth_del);
 212        if (!filter.flushed)
 213                printf("Nothing to flush\n");
 214        else
 215                printf("Flushed %d nexthops\n", filter.flushed);
 216
 217        return rc;
 218}
 219
 220static bool __valid_nh_group_attr(const struct rtattr *g_attr)
 221{
 222        int num = RTA_PAYLOAD(g_attr) / sizeof(struct nexthop_grp);
 223
 224        return num && num * sizeof(struct nexthop_grp) == RTA_PAYLOAD(g_attr);
 225}
 226
 227static void print_nh_group(const struct nh_entry *nhe)
 228{
 229        int i;
 230
 231        open_json_array(PRINT_JSON, "group");
 232        print_string(PRINT_FP, NULL, "%s", "group ");
 233        for (i = 0; i < nhe->nh_groups_cnt; ++i) {
 234                open_json_object(NULL);
 235
 236                if (i)
 237                        print_string(PRINT_FP, NULL, "%s", "/");
 238
 239                print_uint(PRINT_ANY, "id", "%u", nhe->nh_groups[i].id);
 240                if (nhe->nh_groups[i].weight)
 241                        print_uint(PRINT_ANY, "weight", ",%u",
 242                                   nhe->nh_groups[i].weight + 1);
 243
 244                close_json_object();
 245        }
 246        print_string(PRINT_FP, NULL, "%s", " ");
 247        close_json_array(PRINT_JSON, NULL);
 248}
 249
 250static const char *nh_group_type_name(__u16 type)
 251{
 252        switch (type) {
 253        case NEXTHOP_GRP_TYPE_MPATH:
 254                return "mpath";
 255        case NEXTHOP_GRP_TYPE_RES:
 256                return "resilient";
 257        default:
 258                return "<unknown type>";
 259        }
 260}
 261
 262static void print_nh_group_type(__u16 nh_grp_type)
 263{
 264        if (nh_grp_type == NEXTHOP_GRP_TYPE_MPATH)
 265                /* Do not print type in order not to break existing output. */
 266                return;
 267
 268        print_string(PRINT_ANY, "type", "type %s ", nh_group_type_name(nh_grp_type));
 269}
 270
 271static void parse_nh_res_group_rta(const struct rtattr *res_grp_attr,
 272                                   struct nha_res_grp *res_grp)
 273{
 274        struct rtattr *tb[NHA_RES_GROUP_MAX + 1];
 275        struct rtattr *rta;
 276
 277        memset(res_grp, 0, sizeof(*res_grp));
 278        parse_rtattr_nested(tb, NHA_RES_GROUP_MAX, res_grp_attr);
 279
 280        if (tb[NHA_RES_GROUP_BUCKETS])
 281                res_grp->buckets = rta_getattr_u16(tb[NHA_RES_GROUP_BUCKETS]);
 282
 283        if (tb[NHA_RES_GROUP_IDLE_TIMER]) {
 284                rta = tb[NHA_RES_GROUP_IDLE_TIMER];
 285                res_grp->idle_timer = rta_getattr_u32(rta);
 286        }
 287
 288        if (tb[NHA_RES_GROUP_UNBALANCED_TIMER]) {
 289                rta = tb[NHA_RES_GROUP_UNBALANCED_TIMER];
 290                res_grp->unbalanced_timer = rta_getattr_u32(rta);
 291        }
 292
 293        if (tb[NHA_RES_GROUP_UNBALANCED_TIME]) {
 294                rta = tb[NHA_RES_GROUP_UNBALANCED_TIME];
 295                res_grp->unbalanced_time = rta_getattr_u64(rta);
 296        }
 297}
 298
 299static void print_nh_res_group(const struct nha_res_grp *res_grp)
 300{
 301        struct timeval tv;
 302
 303        open_json_object("resilient_args");
 304
 305        print_uint(PRINT_ANY, "buckets", "buckets %u ", res_grp->buckets);
 306
 307         __jiffies_to_tv(&tv, res_grp->idle_timer);
 308        print_tv(PRINT_ANY, "idle_timer", "idle_timer %g ", &tv);
 309
 310        __jiffies_to_tv(&tv, res_grp->unbalanced_timer);
 311        print_tv(PRINT_ANY, "unbalanced_timer", "unbalanced_timer %g ", &tv);
 312
 313        __jiffies_to_tv(&tv, res_grp->unbalanced_time);
 314        print_tv(PRINT_ANY, "unbalanced_time", "unbalanced_time %g ", &tv);
 315
 316        close_json_object();
 317}
 318
 319static void print_nh_res_bucket(FILE *fp, const struct rtattr *res_bucket_attr)
 320{
 321        struct rtattr *tb[NHA_RES_BUCKET_MAX + 1];
 322
 323        parse_rtattr_nested(tb, NHA_RES_BUCKET_MAX, res_bucket_attr);
 324
 325        open_json_object("bucket");
 326
 327        if (tb[NHA_RES_BUCKET_INDEX])
 328                print_uint(PRINT_ANY, "index", "index %u ",
 329                           rta_getattr_u16(tb[NHA_RES_BUCKET_INDEX]));
 330
 331        if (tb[NHA_RES_BUCKET_IDLE_TIME]) {
 332                struct rtattr *rta = tb[NHA_RES_BUCKET_IDLE_TIME];
 333                struct timeval tv;
 334
 335                __jiffies_to_tv(&tv, rta_getattr_u64(rta));
 336                print_tv(PRINT_ANY, "idle_time", "idle_time %g ", &tv);
 337        }
 338
 339        if (tb[NHA_RES_BUCKET_NH_ID])
 340                print_uint(PRINT_ANY, "nhid", "nhid %u ",
 341                           rta_getattr_u32(tb[NHA_RES_BUCKET_NH_ID]));
 342
 343        close_json_object();
 344}
 345
 346static void ipnh_destroy_entry(struct nh_entry *nhe)
 347{
 348        if (nhe->nh_encap)
 349                free(nhe->nh_encap);
 350        if (nhe->nh_groups)
 351                free(nhe->nh_groups);
 352}
 353
 354/* parse nhmsg into nexthop entry struct which must be destroyed by
 355 * ipnh_destroy_enty when it's not needed anymore
 356 */
 357static int ipnh_parse_nhmsg(FILE *fp, const struct nhmsg *nhm, int len,
 358                            struct nh_entry *nhe)
 359{
 360        struct rtattr *tb[NHA_MAX+1];
 361        int err = 0;
 362
 363        memset(nhe, 0, sizeof(*nhe));
 364        parse_rtattr_flags(tb, NHA_MAX, RTM_NHA(nhm), len, NLA_F_NESTED);
 365
 366        if (tb[NHA_ID])
 367                nhe->nh_id = rta_getattr_u32(tb[NHA_ID]);
 368
 369        if (tb[NHA_OIF])
 370                nhe->nh_oif = rta_getattr_u32(tb[NHA_OIF]);
 371
 372        if (tb[NHA_GROUP_TYPE])
 373                nhe->nh_grp_type = rta_getattr_u16(tb[NHA_GROUP_TYPE]);
 374
 375        if (tb[NHA_GATEWAY]) {
 376                if (RTA_PAYLOAD(tb[NHA_GATEWAY]) > sizeof(nhe->nh_gateway)) {
 377                        fprintf(fp, "<nexthop id %u invalid gateway length %lu>\n",
 378                                nhe->nh_id, RTA_PAYLOAD(tb[NHA_GATEWAY]));
 379                        err = -EINVAL;
 380                        goto out_err;
 381                }
 382                nhe->nh_gateway_len = RTA_PAYLOAD(tb[NHA_GATEWAY]);
 383                memcpy(&nhe->nh_gateway, RTA_DATA(tb[NHA_GATEWAY]),
 384                       RTA_PAYLOAD(tb[NHA_GATEWAY]));
 385        }
 386
 387        if (tb[NHA_ENCAP]) {
 388                nhe->nh_encap = malloc(RTA_LENGTH(RTA_PAYLOAD(tb[NHA_ENCAP])));
 389                if (!nhe->nh_encap) {
 390                        err = -ENOMEM;
 391                        goto out_err;
 392                }
 393                memcpy(nhe->nh_encap, tb[NHA_ENCAP],
 394                       RTA_LENGTH(RTA_PAYLOAD(tb[NHA_ENCAP])));
 395                memcpy(&nhe->nh_encap_type, tb[NHA_ENCAP_TYPE],
 396                       sizeof(nhe->nh_encap_type));
 397        }
 398
 399        if (tb[NHA_GROUP]) {
 400                if (!__valid_nh_group_attr(tb[NHA_GROUP])) {
 401                        fprintf(fp, "<nexthop id %u invalid nexthop group>",
 402                                nhe->nh_id);
 403                        err = -EINVAL;
 404                        goto out_err;
 405                }
 406
 407                nhe->nh_groups = malloc(RTA_PAYLOAD(tb[NHA_GROUP]));
 408                if (!nhe->nh_groups) {
 409                        err = -ENOMEM;
 410                        goto out_err;
 411                }
 412                nhe->nh_groups_cnt = RTA_PAYLOAD(tb[NHA_GROUP]) /
 413                                     sizeof(struct nexthop_grp);
 414                memcpy(nhe->nh_groups, RTA_DATA(tb[NHA_GROUP]),
 415                       RTA_PAYLOAD(tb[NHA_GROUP]));
 416        }
 417
 418        if (tb[NHA_RES_GROUP]) {
 419                parse_nh_res_group_rta(tb[NHA_RES_GROUP], &nhe->nh_res_grp);
 420                nhe->nh_has_res_grp = true;
 421        }
 422
 423        nhe->nh_blackhole = !!tb[NHA_BLACKHOLE];
 424        nhe->nh_fdb = !!tb[NHA_FDB];
 425
 426        nhe->nh_family = nhm->nh_family;
 427        nhe->nh_protocol = nhm->nh_protocol;
 428        nhe->nh_scope = nhm->nh_scope;
 429        nhe->nh_flags = nhm->nh_flags;
 430
 431        return 0;
 432
 433out_err:
 434        ipnh_destroy_entry(nhe);
 435        return err;
 436}
 437
 438static void __print_nexthop_entry(FILE *fp, const char *jsobj,
 439                                  struct nh_entry *nhe,
 440                                  bool deleted)
 441{
 442        SPRINT_BUF(b1);
 443
 444        open_json_object(jsobj);
 445
 446        if (deleted)
 447                print_bool(PRINT_ANY, "deleted", "Deleted ", true);
 448
 449        print_uint(PRINT_ANY, "id", "id %u ", nhe->nh_id);
 450
 451        if (nhe->nh_groups)
 452                print_nh_group(nhe);
 453
 454        print_nh_group_type(nhe->nh_grp_type);
 455
 456        if (nhe->nh_has_res_grp)
 457                print_nh_res_group(&nhe->nh_res_grp);
 458
 459        if (nhe->nh_encap)
 460                lwt_print_encap(fp, &nhe->nh_encap_type.rta, nhe->nh_encap);
 461
 462        if (nhe->nh_gateway_len)
 463                __print_rta_gateway(fp, nhe->nh_family,
 464                                    format_host(nhe->nh_family,
 465                                    nhe->nh_gateway_len,
 466                                    &nhe->nh_gateway));
 467
 468        if (nhe->nh_oif)
 469                print_rta_ifidx(fp, nhe->nh_oif, "dev");
 470
 471        if (nhe->nh_scope != RT_SCOPE_UNIVERSE || show_details > 0) {
 472                print_string(PRINT_ANY, "scope", "scope %s ",
 473                             rtnl_rtscope_n2a(nhe->nh_scope, b1, sizeof(b1)));
 474        }
 475
 476        if (nhe->nh_blackhole)
 477                print_null(PRINT_ANY, "blackhole", "blackhole ", NULL);
 478
 479        if (nhe->nh_protocol != RTPROT_UNSPEC || show_details > 0) {
 480                print_string(PRINT_ANY, "protocol", "proto %s ",
 481                             rtnl_rtprot_n2a(nhe->nh_protocol, b1, sizeof(b1)));
 482        }
 483
 484        print_rt_flags(fp, nhe->nh_flags);
 485
 486        if (nhe->nh_fdb)
 487                print_null(PRINT_ANY, "fdb", "fdb", NULL);
 488
 489        close_json_object();
 490}
 491
 492static int  __ipnh_get_id(struct rtnl_handle *rthp, __u32 nh_id,
 493                          struct nlmsghdr **answer)
 494{
 495        struct {
 496                struct nlmsghdr n;
 497                struct nhmsg    nhm;
 498                char            buf[1024];
 499        } req = {
 500                .n.nlmsg_len    = NLMSG_LENGTH(sizeof(struct nhmsg)),
 501                .n.nlmsg_flags  = NLM_F_REQUEST,
 502                .n.nlmsg_type   = RTM_GETNEXTHOP,
 503                .nhm.nh_family  = preferred_family,
 504        };
 505
 506        addattr32(&req.n, sizeof(req), NHA_ID, nh_id);
 507
 508        return rtnl_talk(rthp, &req.n, answer);
 509}
 510
 511static struct hlist_head *ipnh_cache_head(__u32 nh_id)
 512{
 513        nh_id ^= nh_id >> 20;
 514        nh_id ^= nh_id >> 10;
 515
 516        return &nh_cache[nh_id % NH_CACHE_SIZE];
 517}
 518
 519static void ipnh_cache_link_entry(struct nh_entry *nhe)
 520{
 521        struct hlist_head *head = ipnh_cache_head(nhe->nh_id);
 522
 523        hlist_add_head(&nhe->nh_hash, head);
 524}
 525
 526static void ipnh_cache_unlink_entry(struct nh_entry *nhe)
 527{
 528        hlist_del(&nhe->nh_hash);
 529}
 530
 531static struct nh_entry *ipnh_cache_get(__u32 nh_id)
 532{
 533        struct hlist_head *head = ipnh_cache_head(nh_id);
 534        struct nh_entry *nhe;
 535        struct hlist_node *n;
 536
 537        hlist_for_each(n, head) {
 538                nhe = container_of(n, struct nh_entry, nh_hash);
 539                if (nhe->nh_id == nh_id)
 540                        return nhe;
 541        }
 542
 543        return NULL;
 544}
 545
 546static int __ipnh_cache_parse_nlmsg(const struct nlmsghdr *n,
 547                                    struct nh_entry *nhe)
 548{
 549        int err, len;
 550
 551        len = n->nlmsg_len - NLMSG_SPACE(sizeof(struct nhmsg));
 552        if (len < 0) {
 553                fprintf(stderr, "BUG: wrong nlmsg len %d\n", len);
 554                return -EINVAL;
 555        }
 556
 557        err = ipnh_parse_nhmsg(stderr, NLMSG_DATA(n), len, nhe);
 558        if (err) {
 559                fprintf(stderr, "Error parsing nexthop: %s\n", strerror(-err));
 560                return err;
 561        }
 562
 563        return 0;
 564}
 565
 566static struct nh_entry *ipnh_cache_add(__u32 nh_id)
 567{
 568        struct nlmsghdr *answer = NULL;
 569        struct nh_entry *nhe = NULL;
 570
 571        if (nh_cache_rth.fd < 0 && rtnl_open(&nh_cache_rth, 0) < 0) {
 572                nh_cache_rth.fd = -1;
 573                goto out;
 574        }
 575
 576        if (__ipnh_get_id(&nh_cache_rth, nh_id, &answer) < 0)
 577                goto out;
 578
 579        nhe = malloc(sizeof(*nhe));
 580        if (!nhe)
 581                goto out;
 582
 583        if (__ipnh_cache_parse_nlmsg(answer, nhe))
 584                goto out_free_nhe;
 585
 586        ipnh_cache_link_entry(nhe);
 587
 588out:
 589        if (answer)
 590                free(answer);
 591
 592        return nhe;
 593
 594out_free_nhe:
 595        free(nhe);
 596        nhe = NULL;
 597        goto out;
 598}
 599
 600static void ipnh_cache_del(struct nh_entry *nhe)
 601{
 602        ipnh_cache_unlink_entry(nhe);
 603        ipnh_destroy_entry(nhe);
 604        free(nhe);
 605}
 606
 607/* update, add or delete a nexthop entry based on nlmsghdr */
 608static int ipnh_cache_process_nlmsg(const struct nlmsghdr *n,
 609                                    struct nh_entry *new_nhe)
 610{
 611        struct nh_entry *nhe;
 612
 613        nhe = ipnh_cache_get(new_nhe->nh_id);
 614        switch (n->nlmsg_type) {
 615        case RTM_DELNEXTHOP:
 616                if (nhe)
 617                        ipnh_cache_del(nhe);
 618                ipnh_destroy_entry(new_nhe);
 619                break;
 620        case RTM_NEWNEXTHOP:
 621                if (!nhe) {
 622                        nhe = malloc(sizeof(*nhe));
 623                        if (!nhe) {
 624                                ipnh_destroy_entry(new_nhe);
 625                                return -1;
 626                        }
 627                } else {
 628                        /* this allows us to save 1 allocation on updates by
 629                         * reusing the old nh entry, but we need to cleanup its
 630                         * internal storage
 631                         */
 632                        ipnh_cache_unlink_entry(nhe);
 633                        ipnh_destroy_entry(nhe);
 634                }
 635                memcpy(nhe, new_nhe, sizeof(*nhe));
 636                ipnh_cache_link_entry(nhe);
 637                break;
 638        }
 639
 640        return 0;
 641}
 642
 643void print_cache_nexthop_id(FILE *fp, const char *fp_prefix, const char *jsobj,
 644                            __u32 nh_id)
 645{
 646        struct nh_entry *nhe = ipnh_cache_get(nh_id);
 647
 648        if (!nhe) {
 649                nhe = ipnh_cache_add(nh_id);
 650                if (!nhe)
 651                        return;
 652        }
 653
 654        if (fp_prefix)
 655                print_string(PRINT_FP, NULL, "%s", fp_prefix);
 656        __print_nexthop_entry(fp, jsobj, nhe, false);
 657}
 658
 659int print_cache_nexthop(struct nlmsghdr *n, void *arg, bool process_cache)
 660{
 661        struct nhmsg *nhm = NLMSG_DATA(n);
 662        FILE *fp = (FILE *)arg;
 663        struct nh_entry nhe;
 664        int len, err;
 665
 666        if (n->nlmsg_type != RTM_DELNEXTHOP &&
 667            n->nlmsg_type != RTM_NEWNEXTHOP) {
 668                fprintf(stderr, "Not a nexthop: %08x %08x %08x\n",
 669                        n->nlmsg_len, n->nlmsg_type, n->nlmsg_flags);
 670                return -1;
 671        }
 672
 673        len = n->nlmsg_len - NLMSG_SPACE(sizeof(*nhm));
 674        if (len < 0) {
 675                close_json_object();
 676                fprintf(stderr, "BUG: wrong nlmsg len %d\n", len);
 677                return -1;
 678        }
 679
 680        if (filter.proto && filter.proto != nhm->nh_protocol)
 681                return 0;
 682
 683        err = ipnh_parse_nhmsg(fp, nhm, len, &nhe);
 684        if (err) {
 685                close_json_object();
 686                fprintf(stderr, "Error parsing nexthop: %s\n", strerror(-err));
 687                return -1;
 688        }
 689        __print_nexthop_entry(fp, NULL, &nhe, n->nlmsg_type == RTM_DELNEXTHOP);
 690        print_string(PRINT_FP, NULL, "%s", "\n");
 691        fflush(fp);
 692
 693        if (process_cache)
 694                ipnh_cache_process_nlmsg(n, &nhe);
 695        else
 696                ipnh_destroy_entry(&nhe);
 697
 698        return 0;
 699}
 700
 701static int print_nexthop_nocache(struct nlmsghdr *n, void *arg)
 702{
 703        return print_cache_nexthop(n, arg, false);
 704}
 705
 706int print_nexthop_bucket(struct nlmsghdr *n, void *arg)
 707{
 708        struct nhmsg *nhm = NLMSG_DATA(n);
 709        struct rtattr *tb[NHA_MAX+1];
 710        FILE *fp = (FILE *)arg;
 711        int len;
 712
 713        if (n->nlmsg_type != RTM_DELNEXTHOPBUCKET &&
 714            n->nlmsg_type != RTM_NEWNEXTHOPBUCKET) {
 715                fprintf(stderr, "Not a nexthop bucket: %08x %08x %08x\n",
 716                        n->nlmsg_len, n->nlmsg_type, n->nlmsg_flags);
 717                return -1;
 718        }
 719
 720        len = n->nlmsg_len - NLMSG_SPACE(sizeof(*nhm));
 721        if (len < 0) {
 722                close_json_object();
 723                fprintf(stderr, "BUG: wrong nlmsg len %d\n", len);
 724                return -1;
 725        }
 726
 727        parse_rtattr_flags(tb, NHA_MAX, RTM_NHA(nhm), len, NLA_F_NESTED);
 728
 729        open_json_object(NULL);
 730
 731        if (n->nlmsg_type == RTM_DELNEXTHOP)
 732                print_bool(PRINT_ANY, "deleted", "Deleted ", true);
 733
 734        if (tb[NHA_ID])
 735                print_uint(PRINT_ANY, "id", "id %u ",
 736                           rta_getattr_u32(tb[NHA_ID]));
 737
 738        if (tb[NHA_RES_BUCKET])
 739                print_nh_res_bucket(fp, tb[NHA_RES_BUCKET]);
 740
 741        print_rt_flags(fp, nhm->nh_flags);
 742
 743        print_string(PRINT_FP, NULL, "%s", "\n");
 744        close_json_object();
 745        fflush(fp);
 746
 747        return 0;
 748}
 749
 750static int add_nh_group_attr(struct nlmsghdr *n, int maxlen, char *argv)
 751{
 752        struct nexthop_grp *grps = NULL;
 753        int count = 0, i;
 754        int err = -1;
 755        char *sep, *wsep;
 756
 757        if (*argv != '\0')
 758                count = 1;
 759
 760        /* separator is '/' */
 761        sep = strchr(argv, '/');
 762        while (sep) {
 763                count++;
 764                sep = strchr(sep + 1, '/');
 765        }
 766
 767        if (count == 0)
 768                goto out;
 769
 770        grps = calloc(count, sizeof(*grps));
 771        if (!grps)
 772                goto out;
 773
 774        for (i = 0; i < count; ++i) {
 775                sep = strchr(argv, '/');
 776                if (sep)
 777                        *sep = '\0';
 778
 779                wsep = strchr(argv, ',');
 780                if (wsep)
 781                        *wsep = '\0';
 782
 783                if (get_unsigned(&grps[i].id, argv, 0))
 784                        goto out;
 785                if (wsep) {
 786                        unsigned int w;
 787
 788                        wsep++;
 789                        if (get_unsigned(&w, wsep, 0) || w == 0 || w > 256)
 790                                invarg("\"weight\" is invalid\n", wsep);
 791                        grps[i].weight = w - 1;
 792                }
 793
 794                if (!sep)
 795                        break;
 796
 797                argv = sep + 1;
 798        }
 799
 800        err = addattr_l(n, maxlen, NHA_GROUP, grps, count * sizeof(*grps));
 801out:
 802        free(grps);
 803        return err;
 804}
 805
 806static int read_nh_group_type(const char *name)
 807{
 808        if (strcmp(name, "mpath") == 0)
 809                return NEXTHOP_GRP_TYPE_MPATH;
 810        else if (strcmp(name, "resilient") == 0)
 811                return NEXTHOP_GRP_TYPE_RES;
 812
 813        return __NEXTHOP_GRP_TYPE_MAX;
 814}
 815
 816static void parse_nh_group_type_res(struct nlmsghdr *n, int maxlen, int *argcp,
 817                                    char ***argvp)
 818{
 819        char **argv = *argvp;
 820        struct rtattr *nest;
 821        int argc = *argcp;
 822
 823        if (!NEXT_ARG_OK())
 824                return;
 825
 826        nest = addattr_nest(n, maxlen, NHA_RES_GROUP);
 827        nest->rta_type |= NLA_F_NESTED;
 828
 829        NEXT_ARG_FWD();
 830        while (argc > 0) {
 831                if (strcmp(*argv, "buckets") == 0) {
 832                        __u16 buckets;
 833
 834                        NEXT_ARG();
 835                        if (get_u16(&buckets, *argv, 0))
 836                                invarg("invalid buckets value", *argv);
 837
 838                        addattr16(n, maxlen, NHA_RES_GROUP_BUCKETS, buckets);
 839                } else if (strcmp(*argv, "idle_timer") == 0) {
 840                        __u32 idle_timer;
 841
 842                        NEXT_ARG();
 843                        if (get_unsigned(&idle_timer, *argv, 0) ||
 844                            idle_timer >= UINT32_MAX / 100)
 845                                invarg("invalid idle timer value", *argv);
 846
 847                        addattr32(n, maxlen, NHA_RES_GROUP_IDLE_TIMER,
 848                                  idle_timer * 100);
 849                } else if (strcmp(*argv, "unbalanced_timer") == 0) {
 850                        __u32 unbalanced_timer;
 851
 852                        NEXT_ARG();
 853                        if (get_unsigned(&unbalanced_timer, *argv, 0) ||
 854                            unbalanced_timer >= UINT32_MAX / 100)
 855                                invarg("invalid unbalanced timer value", *argv);
 856
 857                        addattr32(n, maxlen, NHA_RES_GROUP_UNBALANCED_TIMER,
 858                                  unbalanced_timer * 100);
 859                } else {
 860                        break;
 861                }
 862                argc--; argv++;
 863        }
 864
 865        /* argv is currently the first unparsed argument, but ipnh_modify()
 866         * will move to the next, so step back.
 867         */
 868        *argcp = argc + 1;
 869        *argvp = argv - 1;
 870
 871        addattr_nest_end(n, nest);
 872}
 873
 874static void parse_nh_group_type(struct nlmsghdr *n, int maxlen, int *argcp,
 875                                char ***argvp)
 876{
 877        char **argv = *argvp;
 878        int argc = *argcp;
 879        __u16 type;
 880
 881        NEXT_ARG();
 882        type = read_nh_group_type(*argv);
 883        if (type > NEXTHOP_GRP_TYPE_MAX)
 884                invarg("\"type\" value is invalid\n", *argv);
 885
 886        switch (type) {
 887        case NEXTHOP_GRP_TYPE_MPATH:
 888                /* No additional arguments */
 889                break;
 890        case NEXTHOP_GRP_TYPE_RES:
 891                parse_nh_group_type_res(n, maxlen, &argc, &argv);
 892                break;
 893        }
 894
 895        *argcp = argc;
 896        *argvp = argv;
 897
 898        addattr16(n, maxlen, NHA_GROUP_TYPE, type);
 899}
 900
 901static int ipnh_parse_id(const char *argv)
 902{
 903        __u32 id;
 904
 905        if (get_unsigned(&id, argv, 0))
 906                invarg("invalid id value", argv);
 907        return id;
 908}
 909
 910static int ipnh_modify(int cmd, unsigned int flags, int argc, char **argv)
 911{
 912        struct {
 913                struct nlmsghdr n;
 914                struct nhmsg    nhm;
 915                char            buf[1024];
 916        } req = {
 917                .n.nlmsg_len = NLMSG_LENGTH(sizeof(struct nhmsg)),
 918                .n.nlmsg_flags = NLM_F_REQUEST | flags,
 919                .n.nlmsg_type = cmd,
 920                .nhm.nh_family = preferred_family,
 921        };
 922        __u32 nh_flags = 0;
 923
 924        while (argc > 0) {
 925                if (!strcmp(*argv, "id")) {
 926                        NEXT_ARG();
 927                        addattr32(&req.n, sizeof(req), NHA_ID,
 928                                  ipnh_parse_id(*argv));
 929                } else if (!strcmp(*argv, "dev")) {
 930                        int ifindex;
 931
 932                        NEXT_ARG();
 933                        ifindex = ll_name_to_index(*argv);
 934                        if (!ifindex)
 935                                invarg("Device does not exist\n", *argv);
 936                        addattr32(&req.n, sizeof(req), NHA_OIF, ifindex);
 937                        if (req.nhm.nh_family == AF_UNSPEC)
 938                                req.nhm.nh_family = AF_INET;
 939                } else if (strcmp(*argv, "via") == 0) {
 940                        inet_prefix addr;
 941                        int family;
 942
 943                        NEXT_ARG();
 944                        family = read_family(*argv);
 945                        if (family == AF_UNSPEC)
 946                                family = req.nhm.nh_family;
 947                        else
 948                                NEXT_ARG();
 949                        get_addr(&addr, *argv, family);
 950                        if (req.nhm.nh_family == AF_UNSPEC)
 951                                req.nhm.nh_family = addr.family;
 952                        else if (req.nhm.nh_family != addr.family)
 953                                invarg("address family mismatch\n", *argv);
 954                        addattr_l(&req.n, sizeof(req), NHA_GATEWAY,
 955                                  &addr.data, addr.bytelen);
 956                } else if (strcmp(*argv, "encap") == 0) {
 957                        char buf[1024];
 958                        struct rtattr *rta = (void *)buf;
 959
 960                        rta->rta_type = NHA_ENCAP;
 961                        rta->rta_len = RTA_LENGTH(0);
 962
 963                        lwt_parse_encap(rta, sizeof(buf), &argc, &argv,
 964                                        NHA_ENCAP, NHA_ENCAP_TYPE);
 965
 966                        if (rta->rta_len > RTA_LENGTH(0)) {
 967                                addraw_l(&req.n, 1024, RTA_DATA(rta),
 968                                         RTA_PAYLOAD(rta));
 969                        }
 970                } else if (!strcmp(*argv, "blackhole")) {
 971                        addattr_l(&req.n, sizeof(req), NHA_BLACKHOLE, NULL, 0);
 972                        if (req.nhm.nh_family == AF_UNSPEC)
 973                                req.nhm.nh_family = AF_INET;
 974                } else if (!strcmp(*argv, "fdb")) {
 975                        addattr_l(&req.n, sizeof(req), NHA_FDB, NULL, 0);
 976                } else if (!strcmp(*argv, "onlink")) {
 977                        nh_flags |= RTNH_F_ONLINK;
 978                } else if (!strcmp(*argv, "group")) {
 979                        NEXT_ARG();
 980
 981                        if (add_nh_group_attr(&req.n, sizeof(req), *argv))
 982                                invarg("\"group\" value is invalid\n", *argv);
 983                } else if (!strcmp(*argv, "type")) {
 984                        parse_nh_group_type(&req.n, sizeof(req), &argc, &argv);
 985                } else if (matches(*argv, "protocol") == 0) {
 986                        __u32 prot;
 987
 988                        NEXT_ARG();
 989                        if (rtnl_rtprot_a2n(&prot, *argv))
 990                                invarg("\"protocol\" value is invalid\n", *argv);
 991                        req.nhm.nh_protocol = prot;
 992                } else if (strcmp(*argv, "help") == 0) {
 993                        usage();
 994                } else {
 995                        invarg("", *argv);
 996                }
 997                argc--; argv++;
 998        }
 999
1000        req.nhm.nh_flags = nh_flags;
1001
1002        if (rtnl_talk(&rth, &req.n, NULL) < 0)
1003                return -2;
1004
1005        return 0;
1006}
1007
1008static int ipnh_get_id(__u32 id)
1009{
1010        struct nlmsghdr *answer;
1011
1012        if (__ipnh_get_id(&rth, id, &answer) < 0)
1013                return -2;
1014
1015        new_json_obj(json);
1016
1017        if (print_nexthop_nocache(answer, (void *)stdout) < 0) {
1018                free(answer);
1019                return -1;
1020        }
1021
1022        delete_json_obj();
1023        fflush(stdout);
1024
1025        free(answer);
1026
1027        return 0;
1028}
1029
1030static int ipnh_list_flush_id(__u32 id, int action)
1031{
1032        int err;
1033
1034        if (action == IPNH_LIST)
1035                return ipnh_get_id(id);
1036
1037        if (rtnl_open(&rth_del, 0) < 0) {
1038                fprintf(stderr, "Cannot open rtnetlink\n");
1039                return EXIT_FAILURE;
1040        }
1041
1042        err = delete_nexthop(id);
1043        rtnl_close(&rth_del);
1044
1045        return err;
1046}
1047
1048static int ipnh_list_flush(int argc, char **argv, int action)
1049{
1050        unsigned int all = (argc == 0);
1051
1052        while (argc > 0) {
1053                if (!matches(*argv, "dev")) {
1054                        NEXT_ARG();
1055                        filter.ifindex = ll_name_to_index(*argv);
1056                        if (!filter.ifindex)
1057                                invarg("Device does not exist\n", *argv);
1058                } else if (!matches(*argv, "groups")) {
1059                        filter.groups = 1;
1060                } else if (!matches(*argv, "master")) {
1061                        NEXT_ARG();
1062                        filter.master = ll_name_to_index(*argv);
1063                        if (!filter.master)
1064                                invarg("Device does not exist\n", *argv);
1065                } else if (matches(*argv, "vrf") == 0) {
1066                        NEXT_ARG();
1067                        if (!name_is_vrf(*argv))
1068                                invarg("Invalid VRF\n", *argv);
1069                        filter.master = ll_name_to_index(*argv);
1070                        if (!filter.master)
1071                                invarg("VRF does not exist\n", *argv);
1072                } else if (!strcmp(*argv, "id")) {
1073                        NEXT_ARG();
1074                        return ipnh_list_flush_id(ipnh_parse_id(*argv), action);
1075                } else if (!matches(*argv, "protocol")) {
1076                        __u32 proto;
1077
1078                        NEXT_ARG();
1079                        if (get_unsigned(&proto, *argv, 0))
1080                                invarg("invalid protocol value", *argv);
1081                        filter.proto = proto;
1082                } else if (!matches(*argv, "fdb")) {
1083                        filter.fdb = 1;
1084                } else if (matches(*argv, "help") == 0) {
1085                        usage();
1086                } else {
1087                        invarg("", *argv);
1088                }
1089                argc--; argv++;
1090        }
1091
1092        if (action == IPNH_FLUSH)
1093                return ipnh_flush(all);
1094
1095        if (rtnl_nexthopdump_req(&rth, preferred_family, nh_dump_filter) < 0) {
1096                perror("Cannot send dump request");
1097                return -2;
1098        }
1099
1100        new_json_obj(json);
1101
1102        if (rtnl_dump_filter(&rth, print_nexthop_nocache, stdout) < 0) {
1103                fprintf(stderr, "Dump terminated\n");
1104                return -2;
1105        }
1106
1107        delete_json_obj();
1108        fflush(stdout);
1109
1110        return 0;
1111}
1112
1113static int ipnh_get(int argc, char **argv)
1114{
1115        __u32 id = 0;
1116
1117        while (argc > 0) {
1118                if (!strcmp(*argv, "id")) {
1119                        NEXT_ARG();
1120                        id = ipnh_parse_id(*argv);
1121                } else  {
1122                        usage();
1123                }
1124                argc--; argv++;
1125        }
1126
1127        if (!id) {
1128                usage();
1129                return -1;
1130        }
1131
1132        return ipnh_get_id(id);
1133}
1134
1135static int ipnh_bucket_list(int argc, char **argv)
1136{
1137        while (argc > 0) {
1138                if (!matches(*argv, "dev")) {
1139                        NEXT_ARG();
1140                        filter.ifindex = ll_name_to_index(*argv);
1141                        if (!filter.ifindex)
1142                                invarg("Device does not exist\n", *argv);
1143                } else if (!matches(*argv, "master")) {
1144                        NEXT_ARG();
1145                        filter.master = ll_name_to_index(*argv);
1146                        if (!filter.master)
1147                                invarg("Device does not exist\n", *argv);
1148                } else if (matches(*argv, "vrf") == 0) {
1149                        NEXT_ARG();
1150                        if (!name_is_vrf(*argv))
1151                                invarg("Invalid VRF\n", *argv);
1152                        filter.master = ll_name_to_index(*argv);
1153                        if (!filter.master)
1154                                invarg("VRF does not exist\n", *argv);
1155                } else if (!strcmp(*argv, "id")) {
1156                        NEXT_ARG();
1157                        filter.id = ipnh_parse_id(*argv);
1158                } else if (!strcmp(*argv, "nhid")) {
1159                        NEXT_ARG();
1160                        filter.nhid = ipnh_parse_id(*argv);
1161                } else if (matches(*argv, "help") == 0) {
1162                        usage();
1163                } else {
1164                        invarg("", *argv);
1165                }
1166                argc--; argv++;
1167        }
1168
1169        if (rtnl_nexthop_bucket_dump_req(&rth, preferred_family,
1170                                         nh_dump_bucket_filter) < 0) {
1171                perror("Cannot send dump request");
1172                return -2;
1173        }
1174
1175        new_json_obj(json);
1176
1177        if (rtnl_dump_filter(&rth, print_nexthop_bucket, stdout) < 0) {
1178                fprintf(stderr, "Dump terminated\n");
1179                return -2;
1180        }
1181
1182        delete_json_obj();
1183        fflush(stdout);
1184
1185        return 0;
1186}
1187
1188static int ipnh_bucket_get_id(__u32 id, __u16 bucket_index)
1189{
1190        struct {
1191                struct nlmsghdr n;
1192                struct nhmsg    nhm;
1193                char            buf[1024];
1194        } req = {
1195                .n.nlmsg_len = NLMSG_LENGTH(sizeof(struct nhmsg)),
1196                .n.nlmsg_flags = NLM_F_REQUEST,
1197                .n.nlmsg_type  = RTM_GETNEXTHOPBUCKET,
1198                .nhm.nh_family = preferred_family,
1199        };
1200        struct nlmsghdr *answer;
1201        struct rtattr *nest;
1202
1203        addattr32(&req.n, sizeof(req), NHA_ID, id);
1204
1205        nest = addattr_nest(&req.n, sizeof(req), NHA_RES_BUCKET);
1206        nest->rta_type |= NLA_F_NESTED;
1207
1208        addattr16(&req.n, sizeof(req), NHA_RES_BUCKET_INDEX, bucket_index);
1209
1210        addattr_nest_end(&req.n, nest);
1211
1212        if (rtnl_talk(&rth, &req.n, &answer) < 0)
1213                return -2;
1214
1215        new_json_obj(json);
1216
1217        if (print_nexthop_bucket(answer, (void *)stdout) < 0) {
1218                free(answer);
1219                return -1;
1220        }
1221
1222        delete_json_obj();
1223        fflush(stdout);
1224
1225        free(answer);
1226
1227        return 0;
1228}
1229
1230static int ipnh_bucket_get(int argc, char **argv)
1231{
1232        bool bucket_valid = false;
1233        __u16 bucket_index;
1234        __u32 id = 0;
1235
1236        while (argc > 0) {
1237                if (!strcmp(*argv, "id")) {
1238                        NEXT_ARG();
1239                        id = ipnh_parse_id(*argv);
1240                } else if (!strcmp(*argv, "index")) {
1241                        NEXT_ARG();
1242                        if (get_u16(&bucket_index, *argv, 0))
1243                                invarg("invalid bucket index value", *argv);
1244                        bucket_valid = true;
1245                } else  {
1246                        usage();
1247                }
1248                argc--; argv++;
1249        }
1250
1251        if (!id || !bucket_valid) {
1252                usage();
1253                return -1;
1254        }
1255
1256        return ipnh_bucket_get_id(id, bucket_index);
1257}
1258
1259static int do_ipnh_bucket(int argc, char **argv)
1260{
1261        if (argc < 1)
1262                return ipnh_bucket_list(0, NULL);
1263
1264        if (!matches(*argv, "list") ||
1265            !matches(*argv, "show") ||
1266            !matches(*argv, "lst"))
1267                return ipnh_bucket_list(argc-1, argv+1);
1268
1269        if (!matches(*argv, "get"))
1270                return ipnh_bucket_get(argc-1, argv+1);
1271
1272        if (!matches(*argv, "help"))
1273                usage();
1274
1275        fprintf(stderr,
1276                "Command \"%s\" is unknown, try \"ip nexthop help\".\n", *argv);
1277        exit(-1);
1278}
1279
1280int do_ipnh(int argc, char **argv)
1281{
1282        if (argc < 1)
1283                return ipnh_list_flush(0, NULL, IPNH_LIST);
1284
1285        if (!matches(*argv, "add"))
1286                return ipnh_modify(RTM_NEWNEXTHOP, NLM_F_CREATE|NLM_F_EXCL,
1287                                   argc-1, argv+1);
1288        if (!matches(*argv, "replace"))
1289                return ipnh_modify(RTM_NEWNEXTHOP, NLM_F_CREATE|NLM_F_REPLACE,
1290                                   argc-1, argv+1);
1291        if (!matches(*argv, "delete"))
1292                return ipnh_modify(RTM_DELNEXTHOP, 0, argc-1, argv+1);
1293
1294        if (!matches(*argv, "list") ||
1295            !matches(*argv, "show") ||
1296            !matches(*argv, "lst"))
1297                return ipnh_list_flush(argc-1, argv+1, IPNH_LIST);
1298
1299        if (!matches(*argv, "get"))
1300                return ipnh_get(argc-1, argv+1);
1301
1302        if (!matches(*argv, "flush"))
1303                return ipnh_list_flush(argc-1, argv+1, IPNH_FLUSH);
1304
1305        if (!matches(*argv, "bucket"))
1306                return do_ipnh_bucket(argc-1, argv+1);
1307
1308        if (!matches(*argv, "help"))
1309                usage();
1310
1311        fprintf(stderr,
1312                "Command \"%s\" is unknown, try \"ip nexthop help\".\n", *argv);
1313        exit(-1);
1314}
1315