]> git.proxmox.com Git - mirror_iproute2.git/blame - ip/ipnexthop.c
ip route: Print "trap" nexthop indication
[mirror_iproute2.git] / ip / ipnexthop.c
CommitLineData
63df8e85
DA
1// SPDX-License-Identifier: GPL-2.0
2/*
3 * ip nexthop
4 *
5 * Copyright (c) 2017-19 David Ahern <dsahern@gmail.com>
6 */
7
8#include <linux/nexthop.h>
9#include <stdio.h>
10#include <string.h>
11#include <rt_names.h>
12#include <errno.h>
13
14#include "utils.h"
15#include "ip_common.h"
16
17static struct {
18 unsigned int flushed;
19 unsigned int groups;
20 unsigned int ifindex;
21 unsigned int master;
84b91683 22 unsigned int proto;
a56d1746 23 unsigned int fdb;
63df8e85
DA
24} filter;
25
26enum {
27 IPNH_LIST,
28 IPNH_FLUSH,
29};
30
31#define RTM_NHA(h) ((struct rtattr *)(((char *)(h)) + \
32 NLMSG_ALIGN(sizeof(struct nhmsg))))
33
34static void usage(void) __attribute__((noreturn));
35
36static void usage(void)
37{
38 fprintf(stderr,
84b91683 39 "Usage: ip nexthop { list | flush } [ protocol ID ] SELECTOR\n"
63df8e85
DA
40 " ip nexthop { add | replace } id ID NH [ protocol ID ]\n"
41 " ip nexthop { get| del } id ID\n"
42 "SELECTOR := [ id ID ] [ dev DEV ] [ vrf NAME ] [ master DEV ]\n"
a56d1746 43 " [ groups ] [ fdb ]\n"
63df8e85
DA
44 "NH := { blackhole | [ via ADDRESS ] [ dev DEV ] [ onlink ]\n"
45 " [ encap ENCAPTYPE ENCAPHDR ] | group GROUP ] }\n"
46 "GROUP := [ id[,weight]>/<id[,weight]>/... ]\n"
47 "ENCAPTYPE := [ mpls ]\n"
48 "ENCAPHDR := [ MPLSLABEL ]\n");
49 exit(-1);
50}
51
52static int nh_dump_filter(struct nlmsghdr *nlh, int reqlen)
53{
54 int err;
55
56 if (filter.ifindex) {
57 err = addattr32(nlh, reqlen, NHA_OIF, filter.ifindex);
58 if (err)
59 return err;
60 }
61
62 if (filter.groups) {
d9b86843 63 err = addattr_l(nlh, reqlen, NHA_GROUPS, NULL, 0);
63df8e85
DA
64 if (err)
65 return err;
66 }
67
68 if (filter.master) {
d9b86843 69 err = addattr32(nlh, reqlen, NHA_MASTER, filter.master);
63df8e85
DA
70 if (err)
71 return err;
72 }
73
a56d1746
RP
74 if (filter.fdb) {
75 err = addattr_l(nlh, reqlen, NHA_FDB, NULL, 0);
76 if (err)
77 return err;
78 }
79
63df8e85
DA
80 return 0;
81}
82
83static struct rtnl_handle rth_del = { .fd = -1 };
84
85static int delete_nexthop(__u32 id)
86{
87 struct {
88 struct nlmsghdr n;
89 struct nhmsg nhm;
90 char buf[64];
91 } req = {
92 .n.nlmsg_len = NLMSG_LENGTH(sizeof(struct nhmsg)),
93 .n.nlmsg_flags = NLM_F_REQUEST,
94 .n.nlmsg_type = RTM_DELNEXTHOP,
95 .nhm.nh_family = AF_UNSPEC,
96 };
97
98 req.n.nlmsg_seq = ++rth_del.seq;
99
100 addattr32(&req.n, sizeof(req), NHA_ID, id);
101
102 if (rtnl_talk(&rth_del, &req.n, NULL) < 0)
103 return -1;
104 return 0;
105}
106
107static int flush_nexthop(struct nlmsghdr *nlh, void *arg)
108{
109 struct nhmsg *nhm = NLMSG_DATA(nlh);
110 struct rtattr *tb[NHA_MAX+1];
111 __u32 id = 0;
112 int len;
113
114 len = nlh->nlmsg_len - NLMSG_SPACE(sizeof(*nhm));
115 if (len < 0) {
116 fprintf(stderr, "BUG: wrong nlmsg len %d\n", len);
117 return -1;
118 }
119
84b91683
DS
120 if (filter.proto && nhm->nh_protocol != filter.proto)
121 return 0;
122
63df8e85
DA
123 parse_rtattr(tb, NHA_MAX, RTM_NHA(nhm), len);
124 if (tb[NHA_ID])
125 id = rta_getattr_u32(tb[NHA_ID]);
126
127 if (id && !delete_nexthop(id))
128 filter.flushed++;
129
130 return 0;
131}
132
133static int ipnh_flush(unsigned int all)
134{
135 int rc = -2;
136
137 if (all) {
138 filter.groups = 1;
139 filter.ifindex = 0;
140 filter.master = 0;
141 }
142
143 if (rtnl_open(&rth_del, 0) < 0) {
144 fprintf(stderr, "Cannot open rtnetlink\n");
145 return EXIT_FAILURE;
146 }
147again:
148 if (rtnl_nexthopdump_req(&rth, preferred_family, nh_dump_filter) < 0) {
149 perror("Cannot send dump request");
150 goto out;
151 }
152
153 if (rtnl_dump_filter(&rth, flush_nexthop, stdout) < 0) {
154 fprintf(stderr, "Dump terminated. Failed to flush nexthops\n");
155 goto out;
156 }
157
158 /* if deleting all, then remove groups first */
159 if (all && filter.groups) {
160 filter.groups = 0;
161 goto again;
162 }
163
164 rc = 0;
165out:
166 rtnl_close(&rth_del);
167 if (!filter.flushed)
168 printf("Nothing to flush\n");
169 else
170 printf("Flushed %d nexthops\n", filter.flushed);
171
172 return rc;
173}
174
175static void print_nh_group(FILE *fp, const struct rtattr *grps_attr)
176{
177 struct nexthop_grp *nhg = RTA_DATA(grps_attr);
178 int num = RTA_PAYLOAD(grps_attr) / sizeof(*nhg);
179 int i;
180
181 if (!num || num * sizeof(*nhg) != RTA_PAYLOAD(grps_attr)) {
182 fprintf(fp, "<invalid nexthop group>");
183 return;
184 }
185
186 open_json_array(PRINT_JSON, "group");
187 print_string(PRINT_FP, NULL, "%s", "group ");
188 for (i = 0; i < num; ++i) {
189 open_json_object(NULL);
190
191 if (i)
192 print_string(PRINT_FP, NULL, "%s", "/");
193
194 print_uint(PRINT_ANY, "id", "%u", nhg[i].id);
195 if (nhg[i].weight)
196 print_uint(PRINT_ANY, "weight", ",%u", nhg[i].weight + 1);
197
198 close_json_object();
199 }
dba639a5 200 print_string(PRINT_FP, NULL, "%s", " ");
63df8e85
DA
201 close_json_array(PRINT_JSON, NULL);
202}
203
204int print_nexthop(struct nlmsghdr *n, void *arg)
205{
206 struct nhmsg *nhm = NLMSG_DATA(n);
207 struct rtattr *tb[NHA_MAX+1];
208 FILE *fp = (FILE *)arg;
209 int len;
210
211 SPRINT_BUF(b1);
212
213 if (n->nlmsg_type != RTM_DELNEXTHOP &&
214 n->nlmsg_type != RTM_NEWNEXTHOP) {
215 fprintf(stderr, "Not a nexthop: %08x %08x %08x\n",
216 n->nlmsg_len, n->nlmsg_type, n->nlmsg_flags);
217 return -1;
218 }
219
220 len = n->nlmsg_len - NLMSG_SPACE(sizeof(*nhm));
221 if (len < 0) {
222 close_json_object();
223 fprintf(stderr, "BUG: wrong nlmsg len %d\n", len);
224 return -1;
225 }
226
84b91683
DS
227 if (filter.proto && filter.proto != nhm->nh_protocol)
228 return 0;
229
63df8e85
DA
230 parse_rtattr(tb, NHA_MAX, RTM_NHA(nhm), len);
231
232 open_json_object(NULL);
233
2c78aba2 234 if (n->nlmsg_type == RTM_DELNEXTHOP)
63df8e85
DA
235 print_bool(PRINT_ANY, "deleted", "Deleted ", true);
236
237 if (tb[NHA_ID])
238 print_uint(PRINT_ANY, "id", "id %u ",
239 rta_getattr_u32(tb[NHA_ID]));
240
241 if (tb[NHA_GROUP])
242 print_nh_group(fp, tb[NHA_GROUP]);
243
244 if (tb[NHA_ENCAP])
245 lwt_print_encap(fp, tb[NHA_ENCAP_TYPE], tb[NHA_ENCAP]);
246
247 if (tb[NHA_GATEWAY])
248 print_rta_gateway(fp, nhm->nh_family, tb[NHA_GATEWAY]);
249
250 if (tb[NHA_OIF])
251 print_rta_if(fp, tb[NHA_OIF], "dev");
252
253 if (nhm->nh_scope != RT_SCOPE_UNIVERSE || show_details > 0) {
254 print_string(PRINT_ANY, "scope", "scope %s ",
255 rtnl_rtscope_n2a(nhm->nh_scope, b1, sizeof(b1)));
256 }
257
258 if (tb[NHA_BLACKHOLE])
2caa8012 259 print_null(PRINT_ANY, "blackhole", "blackhole ", NULL);
63df8e85
DA
260
261 if (nhm->nh_protocol != RTPROT_UNSPEC || show_details > 0) {
262 print_string(PRINT_ANY, "protocol", "proto %s ",
263 rtnl_rtprot_n2a(nhm->nh_protocol, b1, sizeof(b1)));
264 }
265
266 if (tb[NHA_OIF])
267 print_rt_flags(fp, nhm->nh_flags);
268
a56d1746
RP
269 if (tb[NHA_FDB])
270 print_null(PRINT_ANY, "fdb", "fdb", NULL);
271
63df8e85
DA
272 print_string(PRINT_FP, NULL, "%s", "\n");
273 close_json_object();
274 fflush(fp);
275
276 return 0;
277}
278
279static int add_nh_group_attr(struct nlmsghdr *n, int maxlen, char *argv)
280{
281 struct nexthop_grp *grps;
282 int count = 0, i;
283 char *sep, *wsep;
284
285 if (*argv != '\0')
286 count = 1;
287
288 /* separator is '/' */
289 sep = strchr(argv, '/');
290 while (sep) {
291 count++;
292 sep = strchr(sep + 1, '/');
293 }
294
295 if (count == 0)
296 return -1;
297
298 grps = calloc(count, sizeof(*grps));
299 if (!grps)
300 return -1;
301
302 for (i = 0; i < count; ++i) {
303 sep = strchr(argv, '/');
304 if (sep)
305 *sep = '\0';
306
307 wsep = strchr(argv, ',');
308 if (wsep)
309 *wsep = '\0';
310
311 if (get_unsigned(&grps[i].id, argv, 0))
312 return -1;
313 if (wsep) {
314 unsigned int w;
315
316 wsep++;
317 if (get_unsigned(&w, wsep, 0) || w == 0 || w > 256)
318 invarg("\"weight\" is invalid\n", wsep);
319 grps[i].weight = w - 1;
320 }
321
322 if (!sep)
323 break;
324
325 argv = sep + 1;
326 }
327
328 return addattr_l(n, maxlen, NHA_GROUP, grps, count * sizeof(*grps));
329}
330
331static int ipnh_modify(int cmd, unsigned int flags, int argc, char **argv)
332{
333 struct {
334 struct nlmsghdr n;
335 struct nhmsg nhm;
336 char buf[1024];
337 } req = {
338 .n.nlmsg_len = NLMSG_LENGTH(sizeof(struct nhmsg)),
339 .n.nlmsg_flags = NLM_F_REQUEST | flags,
340 .n.nlmsg_type = cmd,
341 .nhm.nh_family = preferred_family,
342 };
343 __u32 nh_flags = 0;
344
345 while (argc > 0) {
346 if (!strcmp(*argv, "id")) {
347 __u32 id;
348
349 NEXT_ARG();
350 if (get_unsigned(&id, *argv, 0))
351 invarg("invalid id value", *argv);
352 addattr32(&req.n, sizeof(req), NHA_ID, id);
353 } else if (!strcmp(*argv, "dev")) {
354 int ifindex;
355
356 NEXT_ARG();
357 ifindex = ll_name_to_index(*argv);
358 if (!ifindex)
359 invarg("Device does not exist\n", *argv);
360 addattr32(&req.n, sizeof(req), NHA_OIF, ifindex);
361 if (req.nhm.nh_family == AF_UNSPEC)
362 req.nhm.nh_family = AF_INET;
363 } else if (strcmp(*argv, "via") == 0) {
364 inet_prefix addr;
365 int family;
366
367 NEXT_ARG();
368 family = read_family(*argv);
369 if (family == AF_UNSPEC)
370 family = req.nhm.nh_family;
371 else
372 NEXT_ARG();
373 get_addr(&addr, *argv, family);
374 if (req.nhm.nh_family == AF_UNSPEC)
375 req.nhm.nh_family = addr.family;
376 else if (req.nhm.nh_family != addr.family)
377 invarg("address family mismatch\n", *argv);
378 addattr_l(&req.n, sizeof(req), NHA_GATEWAY,
379 &addr.data, addr.bytelen);
380 } else if (strcmp(*argv, "encap") == 0) {
381 char buf[1024];
382 struct rtattr *rta = (void *)buf;
383
384 rta->rta_type = NHA_ENCAP;
385 rta->rta_len = RTA_LENGTH(0);
386
387 lwt_parse_encap(rta, sizeof(buf), &argc, &argv,
388 NHA_ENCAP, NHA_ENCAP_TYPE);
389
390 if (rta->rta_len > RTA_LENGTH(0)) {
391 addraw_l(&req.n, 1024, RTA_DATA(rta),
392 RTA_PAYLOAD(rta));
393 }
394 } else if (!strcmp(*argv, "blackhole")) {
395 addattr_l(&req.n, sizeof(req), NHA_BLACKHOLE, NULL, 0);
396 if (req.nhm.nh_family == AF_UNSPEC)
397 req.nhm.nh_family = AF_INET;
a56d1746
RP
398 } else if (!strcmp(*argv, "fdb")) {
399 addattr_l(&req.n, sizeof(req), NHA_FDB, NULL, 0);
63df8e85
DA
400 } else if (!strcmp(*argv, "onlink")) {
401 nh_flags |= RTNH_F_ONLINK;
402 } else if (!strcmp(*argv, "group")) {
403 NEXT_ARG();
404
405 if (add_nh_group_attr(&req.n, sizeof(req), *argv))
406 invarg("\"group\" value is invalid\n", *argv);
407 } else if (matches(*argv, "protocol") == 0) {
408 __u32 prot;
409
410 NEXT_ARG();
411 if (rtnl_rtprot_a2n(&prot, *argv))
412 invarg("\"protocol\" value is invalid\n", *argv);
413 req.nhm.nh_protocol = prot;
414 } else if (strcmp(*argv, "help") == 0) {
415 usage();
416 } else {
417 invarg("", *argv);
418 }
419 argc--; argv++;
420 }
421
422 req.nhm.nh_flags = nh_flags;
423
424 if (rtnl_talk(&rth, &req.n, NULL) < 0)
425 return -2;
426
427 return 0;
428}
429
430static int ipnh_get_id(__u32 id)
431{
432 struct {
433 struct nlmsghdr n;
434 struct nhmsg nhm;
435 char buf[1024];
436 } req = {
437 .n.nlmsg_len = NLMSG_LENGTH(sizeof(struct nhmsg)),
438 .n.nlmsg_flags = NLM_F_REQUEST,
439 .n.nlmsg_type = RTM_GETNEXTHOP,
440 .nhm.nh_family = preferred_family,
441 };
442 struct nlmsghdr *answer;
443
444 addattr32(&req.n, sizeof(req), NHA_ID, id);
445
446 if (rtnl_talk(&rth, &req.n, &answer) < 0)
447 return -2;
448
449 new_json_obj(json);
450
451 if (print_nexthop(answer, (void *)stdout) < 0) {
452 free(answer);
453 return -1;
454 }
455
456 delete_json_obj();
457 fflush(stdout);
458
459 free(answer);
460
461 return 0;
462}
463
464static int ipnh_list_flush(int argc, char **argv, int action)
465{
466 unsigned int all = (argc == 0);
467
468 while (argc > 0) {
469 if (!matches(*argv, "dev")) {
470 NEXT_ARG();
471 filter.ifindex = ll_name_to_index(*argv);
472 if (!filter.ifindex)
473 invarg("Device does not exist\n", *argv);
474 } else if (!matches(*argv, "groups")) {
475 filter.groups = 1;
476 } else if (!matches(*argv, "master")) {
477 NEXT_ARG();
478 filter.master = ll_name_to_index(*argv);
479 if (!filter.master)
480 invarg("Device does not exist\n", *argv);
481 } else if (matches(*argv, "vrf") == 0) {
482 NEXT_ARG();
483 if (!name_is_vrf(*argv))
484 invarg("Invalid VRF\n", *argv);
485 filter.master = ll_name_to_index(*argv);
486 if (!filter.master)
487 invarg("VRF does not exist\n", *argv);
488 } else if (!strcmp(*argv, "id")) {
489 __u32 id;
490
491 NEXT_ARG();
492 if (get_unsigned(&id, *argv, 0))
493 invarg("invalid id value", *argv);
494 return ipnh_get_id(id);
84b91683
DS
495 } else if (!matches(*argv, "protocol")) {
496 __u32 proto;
497
498 NEXT_ARG();
499 if (get_unsigned(&proto, *argv, 0))
500 invarg("invalid protocol value", *argv);
501 filter.proto = proto;
a56d1746
RP
502 } else if (!matches(*argv, "fdb")) {
503 filter.fdb = 1;
63df8e85
DA
504 } else if (matches(*argv, "help") == 0) {
505 usage();
506 } else {
507 invarg("", *argv);
508 }
509 argc--; argv++;
510 }
511
512 if (action == IPNH_FLUSH)
513 return ipnh_flush(all);
514
515 if (rtnl_nexthopdump_req(&rth, preferred_family, nh_dump_filter) < 0) {
516 perror("Cannot send dump request");
517 return -2;
518 }
519
520 new_json_obj(json);
521
522 if (rtnl_dump_filter(&rth, print_nexthop, stdout) < 0) {
523 fprintf(stderr, "Dump terminated\n");
524 return -2;
525 }
526
527 delete_json_obj();
528 fflush(stdout);
529
530 return 0;
531}
532
533static int ipnh_get(int argc, char **argv)
534{
535 __u32 id = 0;
536
537 while (argc > 0) {
538 if (!strcmp(*argv, "id")) {
539 NEXT_ARG();
540 if (get_unsigned(&id, *argv, 0))
541 invarg("invalid id value", *argv);
542 } else {
543 usage();
544 }
545 argc--; argv++;
546 }
547
548 if (!id) {
549 usage();
550 return -1;
551 }
552
553 return ipnh_get_id(id);
554}
555
556int do_ipnh(int argc, char **argv)
557{
558 if (argc < 1)
559 return ipnh_list_flush(0, NULL, IPNH_LIST);
560
561 if (!matches(*argv, "add"))
562 return ipnh_modify(RTM_NEWNEXTHOP, NLM_F_CREATE|NLM_F_EXCL,
563 argc-1, argv+1);
564 if (!matches(*argv, "replace"))
565 return ipnh_modify(RTM_NEWNEXTHOP, NLM_F_CREATE|NLM_F_REPLACE,
566 argc-1, argv+1);
567 if (!matches(*argv, "delete"))
568 return ipnh_modify(RTM_DELNEXTHOP, 0, argc-1, argv+1);
569
570 if (!matches(*argv, "list") ||
571 !matches(*argv, "show") ||
572 !matches(*argv, "lst"))
573 return ipnh_list_flush(argc-1, argv+1, IPNH_LIST);
574
575 if (!matches(*argv, "get"))
576 return ipnh_get(argc-1, argv+1);
577
578 if (!matches(*argv, "flush"))
579 return ipnh_list_flush(argc-1, argv+1, IPNH_FLUSH);
580
581 if (!matches(*argv, "help"))
582 usage();
583
584 fprintf(stderr,
585 "Command \"%s\" is unknown, try \"ip nexthop help\".\n", *argv);
586 exit(-1);
587}