]>
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 ] }\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
)));
267 print_rt_flags(fp
, nhm
->nh_flags
);
270 print_null(PRINT_ANY
, "fdb", "fdb", NULL
);
272 print_string(PRINT_FP
, NULL
, "%s", "\n");
279 static int add_nh_group_attr(struct nlmsghdr
*n
, int maxlen
, char *argv
)
281 struct nexthop_grp
*grps
;
288 /* separator is '/' */
289 sep
= strchr(argv
, '/');
292 sep
= strchr(sep
+ 1, '/');
298 grps
= calloc(count
, sizeof(*grps
));
302 for (i
= 0; i
< count
; ++i
) {
303 sep
= strchr(argv
, '/');
307 wsep
= strchr(argv
, ',');
311 if (get_unsigned(&grps
[i
].id
, argv
, 0))
317 if (get_unsigned(&w
, wsep
, 0) || w
== 0 || w
> 256)
318 invarg("\"weight\" is invalid\n", wsep
);
319 grps
[i
].weight
= w
- 1;
328 return addattr_l(n
, maxlen
, NHA_GROUP
, grps
, count
* sizeof(*grps
));
331 static int ipnh_modify(int cmd
, unsigned int flags
, int argc
, char **argv
)
338 .n
.nlmsg_len
= NLMSG_LENGTH(sizeof(struct nhmsg
)),
339 .n
.nlmsg_flags
= NLM_F_REQUEST
| flags
,
341 .nhm
.nh_family
= preferred_family
,
346 if (!strcmp(*argv
, "id")) {
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")) {
357 ifindex
= ll_name_to_index(*argv
);
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) {
368 family
= read_family(*argv
);
369 if (family
== AF_UNSPEC
)
370 family
= req
.nhm
.nh_family
;
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) {
382 struct rtattr
*rta
= (void *)buf
;
384 rta
->rta_type
= NHA_ENCAP
;
385 rta
->rta_len
= RTA_LENGTH(0);
387 lwt_parse_encap(rta
, sizeof(buf
), &argc
, &argv
,
388 NHA_ENCAP
, NHA_ENCAP_TYPE
);
390 if (rta
->rta_len
> RTA_LENGTH(0)) {
391 addraw_l(&req
.n
, 1024, RTA_DATA(rta
),
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
;
398 } else if (!strcmp(*argv
, "fdb")) {
399 addattr_l(&req
.n
, sizeof(req
), NHA_FDB
, NULL
, 0);
400 } else if (!strcmp(*argv
, "onlink")) {
401 nh_flags
|= RTNH_F_ONLINK
;
402 } else if (!strcmp(*argv
, "group")) {
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) {
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) {
422 req
.nhm
.nh_flags
= nh_flags
;
424 if (rtnl_talk(&rth
, &req
.n
, NULL
) < 0)
430 static int ipnh_get_id(__u32 id
)
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
,
442 struct nlmsghdr
*answer
;
444 addattr32(&req
.n
, sizeof(req
), NHA_ID
, id
);
446 if (rtnl_talk(&rth
, &req
.n
, &answer
) < 0)
451 if (print_nexthop(answer
, (void *)stdout
) < 0) {
464 static int ipnh_list_flush(int argc
, char **argv
, int action
)
466 unsigned int all
= (argc
== 0);
469 if (!matches(*argv
, "dev")) {
471 filter
.ifindex
= ll_name_to_index(*argv
);
473 invarg("Device does not exist\n", *argv
);
474 } else if (!matches(*argv
, "groups")) {
476 } else if (!matches(*argv
, "master")) {
478 filter
.master
= ll_name_to_index(*argv
);
480 invarg("Device does not exist\n", *argv
);
481 } else if (matches(*argv
, "vrf") == 0) {
483 if (!name_is_vrf(*argv
))
484 invarg("Invalid VRF\n", *argv
);
485 filter
.master
= ll_name_to_index(*argv
);
487 invarg("VRF does not exist\n", *argv
);
488 } else if (!strcmp(*argv
, "id")) {
492 if (get_unsigned(&id
, *argv
, 0))
493 invarg("invalid id value", *argv
);
494 return ipnh_get_id(id
);
495 } else if (!matches(*argv
, "protocol")) {
499 if (get_unsigned(&proto
, *argv
, 0))
500 invarg("invalid protocol value", *argv
);
501 filter
.proto
= proto
;
502 } else if (!matches(*argv
, "fdb")) {
504 } else if (matches(*argv
, "help") == 0) {
512 if (action
== IPNH_FLUSH
)
513 return ipnh_flush(all
);
515 if (rtnl_nexthopdump_req(&rth
, preferred_family
, nh_dump_filter
) < 0) {
516 perror("Cannot send dump request");
522 if (rtnl_dump_filter(&rth
, print_nexthop
, stdout
) < 0) {
523 fprintf(stderr
, "Dump terminated\n");
533 static int ipnh_get(int argc
, char **argv
)
538 if (!strcmp(*argv
, "id")) {
540 if (get_unsigned(&id
, *argv
, 0))
541 invarg("invalid id value", *argv
);
553 return ipnh_get_id(id
);
556 int do_ipnh(int argc
, char **argv
)
559 return ipnh_list_flush(0, NULL
, IPNH_LIST
);
561 if (!matches(*argv
, "add"))
562 return ipnh_modify(RTM_NEWNEXTHOP
, NLM_F_CREATE
|NLM_F_EXCL
,
564 if (!matches(*argv
, "replace"))
565 return ipnh_modify(RTM_NEWNEXTHOP
, NLM_F_CREATE
|NLM_F_REPLACE
,
567 if (!matches(*argv
, "delete"))
568 return ipnh_modify(RTM_DELNEXTHOP
, 0, argc
-1, argv
+1);
570 if (!matches(*argv
, "list") ||
571 !matches(*argv
, "show") ||
572 !matches(*argv
, "lst"))
573 return ipnh_list_flush(argc
-1, argv
+1, IPNH_LIST
);
575 if (!matches(*argv
, "get"))
576 return ipnh_get(argc
-1, argv
+1);
578 if (!matches(*argv
, "flush"))
579 return ipnh_list_flush(argc
-1, argv
+1, IPNH_FLUSH
);
581 if (!matches(*argv
, "help"))
585 "Command \"%s\" is unknown, try \"ip nexthop help\".\n", *argv
);