]>
git.proxmox.com Git - mirror_iproute2.git/blob - ip/ipnexthop.c
1 // SPDX-License-Identifier: GPL-2.0
5 * Copyright (c) 2017-19 David Ahern <dsahern@gmail.com>
8 #include <linux/nexthop.h>
15 #include "ip_common.h"
31 #define RTM_NHA(h) ((struct rtattr *)(((char *)(h)) + \
32 NLMSG_ALIGN(sizeof(struct nhmsg))))
34 static void usage(void) __attribute__((noreturn
));
36 static void usage(void)
39 "Usage: ip nexthop { list | flush } [ protocol ID ] SELECTOR\n"
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"
43 " [ groups ] [ fdb ]\n"
44 "NH := { blackhole | [ via ADDRESS ] [ dev DEV ] [ onlink ]\n"
45 " [ encap ENCAPTYPE ENCAPHDR ] | group GROUP [ fdb ] }\n"
46 "GROUP := [ <id[,weight]>/<id[,weight]>/... ]\n"
47 "ENCAPTYPE := [ mpls ]\n"
48 "ENCAPHDR := [ MPLSLABEL ]\n");
52 static int nh_dump_filter(struct nlmsghdr
*nlh
, int reqlen
)
57 err
= addattr32(nlh
, reqlen
, NHA_OIF
, filter
.ifindex
);
63 err
= addattr_l(nlh
, reqlen
, NHA_GROUPS
, NULL
, 0);
69 err
= addattr32(nlh
, reqlen
, NHA_MASTER
, filter
.master
);
75 err
= addattr_l(nlh
, reqlen
, NHA_FDB
, NULL
, 0);
83 static struct rtnl_handle rth_del
= { .fd
= -1 };
85 static int delete_nexthop(__u32 id
)
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
,
98 req
.n
.nlmsg_seq
= ++rth_del
.seq
;
100 addattr32(&req
.n
, sizeof(req
), NHA_ID
, id
);
102 if (rtnl_talk(&rth_del
, &req
.n
, NULL
) < 0)
107 static int flush_nexthop(struct nlmsghdr
*nlh
, void *arg
)
109 struct nhmsg
*nhm
= NLMSG_DATA(nlh
);
110 struct rtattr
*tb
[NHA_MAX
+1];
114 len
= nlh
->nlmsg_len
- NLMSG_SPACE(sizeof(*nhm
));
116 fprintf(stderr
, "BUG: wrong nlmsg len %d\n", len
);
120 if (filter
.proto
&& nhm
->nh_protocol
!= filter
.proto
)
123 parse_rtattr(tb
, NHA_MAX
, RTM_NHA(nhm
), len
);
125 id
= rta_getattr_u32(tb
[NHA_ID
]);
127 if (id
&& !delete_nexthop(id
))
133 static int ipnh_flush(unsigned int all
)
143 if (rtnl_open(&rth_del
, 0) < 0) {
144 fprintf(stderr
, "Cannot open rtnetlink\n");
148 if (rtnl_nexthopdump_req(&rth
, preferred_family
, nh_dump_filter
) < 0) {
149 perror("Cannot send dump request");
153 if (rtnl_dump_filter(&rth
, flush_nexthop
, stdout
) < 0) {
154 fprintf(stderr
, "Dump terminated. Failed to flush nexthops\n");
158 /* if deleting all, then remove groups first */
159 if (all
&& filter
.groups
) {
166 rtnl_close(&rth_del
);
168 printf("Nothing to flush\n");
170 printf("Flushed %d nexthops\n", filter
.flushed
);
175 static void print_nh_group(FILE *fp
, const struct rtattr
*grps_attr
)
177 struct nexthop_grp
*nhg
= RTA_DATA(grps_attr
);
178 int num
= RTA_PAYLOAD(grps_attr
) / sizeof(*nhg
);
181 if (!num
|| num
* sizeof(*nhg
) != RTA_PAYLOAD(grps_attr
)) {
182 fprintf(fp
, "<invalid nexthop group>");
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
);
192 print_string(PRINT_FP
, NULL
, "%s", "/");
194 print_uint(PRINT_ANY
, "id", "%u", nhg
[i
].id
);
196 print_uint(PRINT_ANY
, "weight", ",%u", nhg
[i
].weight
+ 1);
200 print_string(PRINT_FP
, NULL
, "%s", " ");
201 close_json_array(PRINT_JSON
, NULL
);
204 int print_nexthop(struct nlmsghdr
*n
, void *arg
)
206 struct nhmsg
*nhm
= NLMSG_DATA(n
);
207 struct rtattr
*tb
[NHA_MAX
+1];
208 FILE *fp
= (FILE *)arg
;
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
);
220 len
= n
->nlmsg_len
- NLMSG_SPACE(sizeof(*nhm
));
223 fprintf(stderr
, "BUG: wrong nlmsg len %d\n", len
);
227 if (filter
.proto
&& filter
.proto
!= nhm
->nh_protocol
)
230 parse_rtattr(tb
, NHA_MAX
, RTM_NHA(nhm
), len
);
232 open_json_object(NULL
);
234 if (n
->nlmsg_type
== RTM_DELNEXTHOP
)
235 print_bool(PRINT_ANY
, "deleted", "Deleted ", true);
238 print_uint(PRINT_ANY
, "id", "id %u ",
239 rta_getattr_u32(tb
[NHA_ID
]));
242 print_nh_group(fp
, tb
[NHA_GROUP
]);
245 lwt_print_encap(fp
, tb
[NHA_ENCAP_TYPE
], tb
[NHA_ENCAP
]);
248 print_rta_gateway(fp
, nhm
->nh_family
, tb
[NHA_GATEWAY
]);
251 print_rta_if(fp
, tb
[NHA_OIF
], "dev");
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
)));
258 if (tb
[NHA_BLACKHOLE
])
259 print_null(PRINT_ANY
, "blackhole", "blackhole ", NULL
);
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
)));
266 print_rt_flags(fp
, nhm
->nh_flags
);
269 print_null(PRINT_ANY
, "fdb", "fdb", NULL
);
271 print_string(PRINT_FP
, NULL
, "%s", "\n");
278 static int add_nh_group_attr(struct nlmsghdr
*n
, int maxlen
, char *argv
)
280 struct nexthop_grp
*grps
;
287 /* separator is '/' */
288 sep
= strchr(argv
, '/');
291 sep
= strchr(sep
+ 1, '/');
297 grps
= calloc(count
, sizeof(*grps
));
301 for (i
= 0; i
< count
; ++i
) {
302 sep
= strchr(argv
, '/');
306 wsep
= strchr(argv
, ',');
310 if (get_unsigned(&grps
[i
].id
, argv
, 0))
316 if (get_unsigned(&w
, wsep
, 0) || w
== 0 || w
> 256)
317 invarg("\"weight\" is invalid\n", wsep
);
318 grps
[i
].weight
= w
- 1;
327 return addattr_l(n
, maxlen
, NHA_GROUP
, grps
, count
* sizeof(*grps
));
330 static int ipnh_modify(int cmd
, unsigned int flags
, int argc
, char **argv
)
337 .n
.nlmsg_len
= NLMSG_LENGTH(sizeof(struct nhmsg
)),
338 .n
.nlmsg_flags
= NLM_F_REQUEST
| flags
,
340 .nhm
.nh_family
= preferred_family
,
345 if (!strcmp(*argv
, "id")) {
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")) {
356 ifindex
= ll_name_to_index(*argv
);
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) {
367 family
= read_family(*argv
);
368 if (family
== AF_UNSPEC
)
369 family
= req
.nhm
.nh_family
;
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) {
381 struct rtattr
*rta
= (void *)buf
;
383 rta
->rta_type
= NHA_ENCAP
;
384 rta
->rta_len
= RTA_LENGTH(0);
386 lwt_parse_encap(rta
, sizeof(buf
), &argc
, &argv
,
387 NHA_ENCAP
, NHA_ENCAP_TYPE
);
389 if (rta
->rta_len
> RTA_LENGTH(0)) {
390 addraw_l(&req
.n
, 1024, RTA_DATA(rta
),
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
;
397 } else if (!strcmp(*argv
, "fdb")) {
398 addattr_l(&req
.n
, sizeof(req
), NHA_FDB
, NULL
, 0);
399 } else if (!strcmp(*argv
, "onlink")) {
400 nh_flags
|= RTNH_F_ONLINK
;
401 } else if (!strcmp(*argv
, "group")) {
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) {
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) {
421 req
.nhm
.nh_flags
= nh_flags
;
423 if (rtnl_talk(&rth
, &req
.n
, NULL
) < 0)
429 static int ipnh_get_id(__u32 id
)
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
,
441 struct nlmsghdr
*answer
;
443 addattr32(&req
.n
, sizeof(req
), NHA_ID
, id
);
445 if (rtnl_talk(&rth
, &req
.n
, &answer
) < 0)
450 if (print_nexthop(answer
, (void *)stdout
) < 0) {
463 static int ipnh_list_flush(int argc
, char **argv
, int action
)
465 unsigned int all
= (argc
== 0);
468 if (!matches(*argv
, "dev")) {
470 filter
.ifindex
= ll_name_to_index(*argv
);
472 invarg("Device does not exist\n", *argv
);
473 } else if (!matches(*argv
, "groups")) {
475 } else if (!matches(*argv
, "master")) {
477 filter
.master
= ll_name_to_index(*argv
);
479 invarg("Device does not exist\n", *argv
);
480 } else if (matches(*argv
, "vrf") == 0) {
482 if (!name_is_vrf(*argv
))
483 invarg("Invalid VRF\n", *argv
);
484 filter
.master
= ll_name_to_index(*argv
);
486 invarg("VRF does not exist\n", *argv
);
487 } else if (!strcmp(*argv
, "id")) {
491 if (get_unsigned(&id
, *argv
, 0))
492 invarg("invalid id value", *argv
);
493 return ipnh_get_id(id
);
494 } else if (!matches(*argv
, "protocol")) {
498 if (get_unsigned(&proto
, *argv
, 0))
499 invarg("invalid protocol value", *argv
);
500 filter
.proto
= proto
;
501 } else if (!matches(*argv
, "fdb")) {
503 } else if (matches(*argv
, "help") == 0) {
511 if (action
== IPNH_FLUSH
)
512 return ipnh_flush(all
);
514 if (rtnl_nexthopdump_req(&rth
, preferred_family
, nh_dump_filter
) < 0) {
515 perror("Cannot send dump request");
521 if (rtnl_dump_filter(&rth
, print_nexthop
, stdout
) < 0) {
522 fprintf(stderr
, "Dump terminated\n");
532 static int ipnh_get(int argc
, char **argv
)
537 if (!strcmp(*argv
, "id")) {
539 if (get_unsigned(&id
, *argv
, 0))
540 invarg("invalid id value", *argv
);
552 return ipnh_get_id(id
);
555 int do_ipnh(int argc
, char **argv
)
558 return ipnh_list_flush(0, NULL
, IPNH_LIST
);
560 if (!matches(*argv
, "add"))
561 return ipnh_modify(RTM_NEWNEXTHOP
, NLM_F_CREATE
|NLM_F_EXCL
,
563 if (!matches(*argv
, "replace"))
564 return ipnh_modify(RTM_NEWNEXTHOP
, NLM_F_CREATE
|NLM_F_REPLACE
,
566 if (!matches(*argv
, "delete"))
567 return ipnh_modify(RTM_DELNEXTHOP
, 0, argc
-1, argv
+1);
569 if (!matches(*argv
, "list") ||
570 !matches(*argv
, "show") ||
571 !matches(*argv
, "lst"))
572 return ipnh_list_flush(argc
-1, argv
+1, IPNH_LIST
);
574 if (!matches(*argv
, "get"))
575 return ipnh_get(argc
-1, argv
+1);
577 if (!matches(*argv
, "flush"))
578 return ipnh_list_flush(argc
-1, argv
+1, IPNH_FLUSH
);
580 if (!matches(*argv
, "help"))
584 "Command \"%s\" is unknown, try \"ip nexthop help\".\n", *argv
);