1
2
3
4
5
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
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
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
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
571
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
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