1
2
3
4
5
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
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
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
355
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
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
629
630
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
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
866
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
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