]> git.proxmox.com Git - mirror_iproute2.git/blame - ip/ipnexthop.c
Merge git://git.kernel.org/pub/scm/network/iproute2/iproute2-next
[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 40 " ip nexthop { add | replace } id ID NH [ protocol ID ]\n"
043e03a3 41 " ip nexthop { get | del } id ID\n"
63df8e85 42 "SELECTOR := [ id ID ] [ dev DEV ] [ vrf NAME ] [ master DEV ]\n"
a56d1746 43 " [ groups ] [ fdb ]\n"
63df8e85 44 "NH := { blackhole | [ via ADDRESS ] [ dev DEV ] [ onlink ]\n"
043e03a3
IS
45 " [ encap ENCAPTYPE ENCAPHDR ] | group GROUP [ fdb ] }\n"
46 "GROUP := [ <id[,weight]>/<id[,weight]>/... ]\n"
63df8e85
DA
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
07886789 266 print_rt_flags(fp, nhm->nh_flags);
63df8e85 267
a56d1746
RP
268 if (tb[NHA_FDB])
269 print_null(PRINT_ANY, "fdb", "fdb", NULL);
270
63df8e85
DA
271 print_string(PRINT_FP, NULL, "%s", "\n");
272 close_json_object();
273 fflush(fp);
274
275 return 0;
276}
277
278static int add_nh_group_attr(struct nlmsghdr *n, int maxlen, char *argv)
279{
280 struct nexthop_grp *grps;
281 int count = 0, i;
282 char *sep, *wsep;
283
284 if (*argv != '\0')
285 count = 1;
286
287 /* separator is '/' */
288 sep = strchr(argv, '/');
289 while (sep) {
290 count++;
291 sep = strchr(sep + 1, '/');
292 }
293
294 if (count == 0)
295 return -1;
296
297 grps = calloc(count, sizeof(*grps));
298 if (!grps)
299 return -1;
300
301 for (i = 0; i < count; ++i) {
302 sep = strchr(argv, '/');
303 if (sep)
304 *sep = '\0';
305
306 wsep = strchr(argv, ',');
307 if (wsep)
308 *wsep = '\0';
309
310 if (get_unsigned(&grps[i].id, argv, 0))
311 return -1;
312 if (wsep) {
313 unsigned int w;
314
315 wsep++;
316 if (get_unsigned(&w, wsep, 0) || w == 0 || w > 256)
317 invarg("\"weight\" is invalid\n", wsep);
318 grps[i].weight = w - 1;
319 }
320
321 if (!sep)
322 break;
323
324 argv = sep + 1;
325 }
326
327 return addattr_l(n, maxlen, NHA_GROUP, grps, count * sizeof(*grps));
328}
329
330static int ipnh_modify(int cmd, unsigned int flags, int argc, char **argv)
331{
332 struct {
333 struct nlmsghdr n;
334 struct nhmsg nhm;
335 char buf[1024];
336 } req = {
337 .n.nlmsg_len = NLMSG_LENGTH(sizeof(struct nhmsg)),
338 .n.nlmsg_flags = NLM_F_REQUEST | flags,
339 .n.nlmsg_type = cmd,
340 .nhm.nh_family = preferred_family,
341 };
342 __u32 nh_flags = 0;
343
344 while (argc > 0) {
345 if (!strcmp(*argv, "id")) {
346 __u32 id;
347
348 NEXT_ARG();
349 if (get_unsigned(&id, *argv, 0))
350 invarg("invalid id value", *argv);
351 addattr32(&req.n, sizeof(req), NHA_ID, id);
352 } else if (!strcmp(*argv, "dev")) {
353 int ifindex;
354
355 NEXT_ARG();
356 ifindex = ll_name_to_index(*argv);
357 if (!ifindex)
358 invarg("Device does not exist\n", *argv);
359 addattr32(&req.n, sizeof(req), NHA_OIF, ifindex);
360 if (req.nhm.nh_family == AF_UNSPEC)
361 req.nhm.nh_family = AF_INET;
362 } else if (strcmp(*argv, "via") == 0) {
363 inet_prefix addr;
364 int family;
365
366 NEXT_ARG();
367 family = read_family(*argv);
368 if (family == AF_UNSPEC)
369 family = req.nhm.nh_family;
370 else
371 NEXT_ARG();
372 get_addr(&addr, *argv, family);
373 if (req.nhm.nh_family == AF_UNSPEC)
374 req.nhm.nh_family = addr.family;
375 else if (req.nhm.nh_family != addr.family)
376 invarg("address family mismatch\n", *argv);
377 addattr_l(&req.n, sizeof(req), NHA_GATEWAY,
378 &addr.data, addr.bytelen);
379 } else if (strcmp(*argv, "encap") == 0) {
380 char buf[1024];
381 struct rtattr *rta = (void *)buf;
382
383 rta->rta_type = NHA_ENCAP;
384 rta->rta_len = RTA_LENGTH(0);
385
386 lwt_parse_encap(rta, sizeof(buf), &argc, &argv,
387 NHA_ENCAP, NHA_ENCAP_TYPE);
388
389 if (rta->rta_len > RTA_LENGTH(0)) {
390 addraw_l(&req.n, 1024, RTA_DATA(rta),
391 RTA_PAYLOAD(rta));
392 }
393 } else if (!strcmp(*argv, "blackhole")) {
394 addattr_l(&req.n, sizeof(req), NHA_BLACKHOLE, NULL, 0);
395 if (req.nhm.nh_family == AF_UNSPEC)
396 req.nhm.nh_family = AF_INET;
a56d1746
RP
397 } else if (!strcmp(*argv, "fdb")) {
398 addattr_l(&req.n, sizeof(req), NHA_FDB, NULL, 0);
63df8e85
DA
399 } else if (!strcmp(*argv, "onlink")) {
400 nh_flags |= RTNH_F_ONLINK;
401 } else if (!strcmp(*argv, "group")) {
402 NEXT_ARG();
403
404 if (add_nh_group_attr(&req.n, sizeof(req), *argv))
405 invarg("\"group\" value is invalid\n", *argv);
406 } else if (matches(*argv, "protocol") == 0) {
407 __u32 prot;
408
409 NEXT_ARG();
410 if (rtnl_rtprot_a2n(&prot, *argv))
411 invarg("\"protocol\" value is invalid\n", *argv);
412 req.nhm.nh_protocol = prot;
413 } else if (strcmp(*argv, "help") == 0) {
414 usage();
415 } else {
416 invarg("", *argv);
417 }
418 argc--; argv++;
419 }
420
421 req.nhm.nh_flags = nh_flags;
422
423 if (rtnl_talk(&rth, &req.n, NULL) < 0)
424 return -2;
425
426 return 0;
427}
428
429static int ipnh_get_id(__u32 id)
430{
431 struct {
432 struct nlmsghdr n;
433 struct nhmsg nhm;
434 char buf[1024];
435 } req = {
436 .n.nlmsg_len = NLMSG_LENGTH(sizeof(struct nhmsg)),
437 .n.nlmsg_flags = NLM_F_REQUEST,
438 .n.nlmsg_type = RTM_GETNEXTHOP,
439 .nhm.nh_family = preferred_family,
440 };
441 struct nlmsghdr *answer;
442
443 addattr32(&req.n, sizeof(req), NHA_ID, id);
444
445 if (rtnl_talk(&rth, &req.n, &answer) < 0)
446 return -2;
447
448 new_json_obj(json);
449
450 if (print_nexthop(answer, (void *)stdout) < 0) {
451 free(answer);
452 return -1;
453 }
454
455 delete_json_obj();
456 fflush(stdout);
457
458 free(answer);
459
460 return 0;
461}
462
463static int ipnh_list_flush(int argc, char **argv, int action)
464{
465 unsigned int all = (argc == 0);
466
467 while (argc > 0) {
468 if (!matches(*argv, "dev")) {
469 NEXT_ARG();
470 filter.ifindex = ll_name_to_index(*argv);
471 if (!filter.ifindex)
472 invarg("Device does not exist\n", *argv);
473 } else if (!matches(*argv, "groups")) {
474 filter.groups = 1;
475 } else if (!matches(*argv, "master")) {
476 NEXT_ARG();
477 filter.master = ll_name_to_index(*argv);
478 if (!filter.master)
479 invarg("Device does not exist\n", *argv);
480 } else if (matches(*argv, "vrf") == 0) {
481 NEXT_ARG();
482 if (!name_is_vrf(*argv))
483 invarg("Invalid VRF\n", *argv);
484 filter.master = ll_name_to_index(*argv);
485 if (!filter.master)
486 invarg("VRF does not exist\n", *argv);
487 } else if (!strcmp(*argv, "id")) {
488 __u32 id;
489
490 NEXT_ARG();
491 if (get_unsigned(&id, *argv, 0))
492 invarg("invalid id value", *argv);
493 return ipnh_get_id(id);
84b91683
DS
494 } else if (!matches(*argv, "protocol")) {
495 __u32 proto;
496
497 NEXT_ARG();
498 if (get_unsigned(&proto, *argv, 0))
499 invarg("invalid protocol value", *argv);
500 filter.proto = proto;
a56d1746
RP
501 } else if (!matches(*argv, "fdb")) {
502 filter.fdb = 1;
63df8e85
DA
503 } else if (matches(*argv, "help") == 0) {
504 usage();
505 } else {
506 invarg("", *argv);
507 }
508 argc--; argv++;
509 }
510
511 if (action == IPNH_FLUSH)
512 return ipnh_flush(all);
513
514 if (rtnl_nexthopdump_req(&rth, preferred_family, nh_dump_filter) < 0) {
515 perror("Cannot send dump request");
516 return -2;
517 }
518
519 new_json_obj(json);
520
521 if (rtnl_dump_filter(&rth, print_nexthop, stdout) < 0) {
522 fprintf(stderr, "Dump terminated\n");
523 return -2;
524 }
525
526 delete_json_obj();
527 fflush(stdout);
528
529 return 0;
530}
531
532static int ipnh_get(int argc, char **argv)
533{
534 __u32 id = 0;
535
536 while (argc > 0) {
537 if (!strcmp(*argv, "id")) {
538 NEXT_ARG();
539 if (get_unsigned(&id, *argv, 0))
540 invarg("invalid id value", *argv);
541 } else {
542 usage();
543 }
544 argc--; argv++;
545 }
546
547 if (!id) {
548 usage();
549 return -1;
550 }
551
552 return ipnh_get_id(id);
553}
554
555int do_ipnh(int argc, char **argv)
556{
557 if (argc < 1)
558 return ipnh_list_flush(0, NULL, IPNH_LIST);
559
560 if (!matches(*argv, "add"))
561 return ipnh_modify(RTM_NEWNEXTHOP, NLM_F_CREATE|NLM_F_EXCL,
562 argc-1, argv+1);
563 if (!matches(*argv, "replace"))
564 return ipnh_modify(RTM_NEWNEXTHOP, NLM_F_CREATE|NLM_F_REPLACE,
565 argc-1, argv+1);
566 if (!matches(*argv, "delete"))
567 return ipnh_modify(RTM_DELNEXTHOP, 0, argc-1, argv+1);
568
569 if (!matches(*argv, "list") ||
570 !matches(*argv, "show") ||
571 !matches(*argv, "lst"))
572 return ipnh_list_flush(argc-1, argv+1, IPNH_LIST);
573
574 if (!matches(*argv, "get"))
575 return ipnh_get(argc-1, argv+1);
576
577 if (!matches(*argv, "flush"))
578 return ipnh_list_flush(argc-1, argv+1, IPNH_FLUSH);
579
580 if (!matches(*argv, "help"))
581 usage();
582
583 fprintf(stderr,
584 "Command \"%s\" is unknown, try \"ip nexthop help\".\n", *argv);
585 exit(-1);
586}