]>
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"
29 #define RTM_NHA(h) ((struct rtattr *)(((char *)(h)) + \
30 NLMSG_ALIGN(sizeof(struct nhmsg))))
32 static void usage(void) __attribute__((noreturn
));
34 static void usage(void)
37 "Usage: ip nexthop { list | flush } SELECTOR\n"
38 " ip nexthop { add | replace } id ID NH [ protocol ID ]\n"
39 " ip nexthop { get| del } id ID\n"
40 "SELECTOR := [ id ID ] [ dev DEV ] [ vrf NAME ] [ master DEV ]\n"
42 "NH := { blackhole | [ via ADDRESS ] [ dev DEV ] [ onlink ]\n"
43 " [ encap ENCAPTYPE ENCAPHDR ] | group GROUP ] }\n"
44 "GROUP := [ id[,weight]>/<id[,weight]>/... ]\n"
45 "ENCAPTYPE := [ mpls ]\n"
46 "ENCAPHDR := [ MPLSLABEL ]\n");
50 static int nh_dump_filter(struct nlmsghdr
*nlh
, int reqlen
)
55 err
= addattr32(nlh
, reqlen
, NHA_OIF
, filter
.ifindex
);
61 addattr_l(nlh
, reqlen
, NHA_GROUPS
, NULL
, 0);
67 addattr32(nlh
, reqlen
, NHA_MASTER
, filter
.master
);
75 static struct rtnl_handle rth_del
= { .fd
= -1 };
77 static int delete_nexthop(__u32 id
)
84 .n
.nlmsg_len
= NLMSG_LENGTH(sizeof(struct nhmsg
)),
85 .n
.nlmsg_flags
= NLM_F_REQUEST
,
86 .n
.nlmsg_type
= RTM_DELNEXTHOP
,
87 .nhm
.nh_family
= AF_UNSPEC
,
90 req
.n
.nlmsg_seq
= ++rth_del
.seq
;
92 addattr32(&req
.n
, sizeof(req
), NHA_ID
, id
);
94 if (rtnl_talk(&rth_del
, &req
.n
, NULL
) < 0)
99 static int flush_nexthop(struct nlmsghdr
*nlh
, void *arg
)
101 struct nhmsg
*nhm
= NLMSG_DATA(nlh
);
102 struct rtattr
*tb
[NHA_MAX
+1];
106 len
= nlh
->nlmsg_len
- NLMSG_SPACE(sizeof(*nhm
));
108 fprintf(stderr
, "BUG: wrong nlmsg len %d\n", len
);
112 parse_rtattr(tb
, NHA_MAX
, RTM_NHA(nhm
), len
);
114 id
= rta_getattr_u32(tb
[NHA_ID
]);
116 if (id
&& !delete_nexthop(id
))
122 static int ipnh_flush(unsigned int all
)
132 if (rtnl_open(&rth_del
, 0) < 0) {
133 fprintf(stderr
, "Cannot open rtnetlink\n");
137 if (rtnl_nexthopdump_req(&rth
, preferred_family
, nh_dump_filter
) < 0) {
138 perror("Cannot send dump request");
142 if (rtnl_dump_filter(&rth
, flush_nexthop
, stdout
) < 0) {
143 fprintf(stderr
, "Dump terminated. Failed to flush nexthops\n");
147 /* if deleting all, then remove groups first */
148 if (all
&& filter
.groups
) {
155 rtnl_close(&rth_del
);
157 printf("Nothing to flush\n");
159 printf("Flushed %d nexthops\n", filter
.flushed
);
164 static void print_nh_group(FILE *fp
, const struct rtattr
*grps_attr
)
166 struct nexthop_grp
*nhg
= RTA_DATA(grps_attr
);
167 int num
= RTA_PAYLOAD(grps_attr
) / sizeof(*nhg
);
170 if (!num
|| num
* sizeof(*nhg
) != RTA_PAYLOAD(grps_attr
)) {
171 fprintf(fp
, "<invalid nexthop group>");
175 open_json_array(PRINT_JSON
, "group");
176 print_string(PRINT_FP
, NULL
, "%s", "group ");
177 for (i
= 0; i
< num
; ++i
) {
178 open_json_object(NULL
);
181 print_string(PRINT_FP
, NULL
, "%s", "/");
183 print_uint(PRINT_ANY
, "id", "%u", nhg
[i
].id
);
185 print_uint(PRINT_ANY
, "weight", ",%u", nhg
[i
].weight
+ 1);
189 print_string(PRINT_FP
, NULL
, "%s", " ");
190 close_json_array(PRINT_JSON
, NULL
);
193 int print_nexthop(struct nlmsghdr
*n
, void *arg
)
195 struct nhmsg
*nhm
= NLMSG_DATA(n
);
196 struct rtattr
*tb
[NHA_MAX
+1];
197 FILE *fp
= (FILE *)arg
;
202 if (n
->nlmsg_type
!= RTM_DELNEXTHOP
&&
203 n
->nlmsg_type
!= RTM_NEWNEXTHOP
) {
204 fprintf(stderr
, "Not a nexthop: %08x %08x %08x\n",
205 n
->nlmsg_len
, n
->nlmsg_type
, n
->nlmsg_flags
);
209 len
= n
->nlmsg_len
- NLMSG_SPACE(sizeof(*nhm
));
212 fprintf(stderr
, "BUG: wrong nlmsg len %d\n", len
);
216 parse_rtattr(tb
, NHA_MAX
, RTM_NHA(nhm
), len
);
218 open_json_object(NULL
);
220 if (n
->nlmsg_type
== RTM_DELROUTE
)
221 print_bool(PRINT_ANY
, "deleted", "Deleted ", true);
224 print_uint(PRINT_ANY
, "id", "id %u ",
225 rta_getattr_u32(tb
[NHA_ID
]));
228 print_nh_group(fp
, tb
[NHA_GROUP
]);
231 lwt_print_encap(fp
, tb
[NHA_ENCAP_TYPE
], tb
[NHA_ENCAP
]);
234 print_rta_gateway(fp
, nhm
->nh_family
, tb
[NHA_GATEWAY
]);
237 print_rta_if(fp
, tb
[NHA_OIF
], "dev");
239 if (nhm
->nh_scope
!= RT_SCOPE_UNIVERSE
|| show_details
> 0) {
240 print_string(PRINT_ANY
, "scope", "scope %s ",
241 rtnl_rtscope_n2a(nhm
->nh_scope
, b1
, sizeof(b1
)));
244 if (tb
[NHA_BLACKHOLE
])
245 print_null(PRINT_ANY
, "blackhole", "blackhole", NULL
);
247 if (nhm
->nh_protocol
!= RTPROT_UNSPEC
|| show_details
> 0) {
248 print_string(PRINT_ANY
, "protocol", "proto %s ",
249 rtnl_rtprot_n2a(nhm
->nh_protocol
, b1
, sizeof(b1
)));
253 print_rt_flags(fp
, nhm
->nh_flags
);
255 print_string(PRINT_FP
, NULL
, "%s", "\n");
262 static int add_nh_group_attr(struct nlmsghdr
*n
, int maxlen
, char *argv
)
264 struct nexthop_grp
*grps
;
271 /* separator is '/' */
272 sep
= strchr(argv
, '/');
275 sep
= strchr(sep
+ 1, '/');
281 grps
= calloc(count
, sizeof(*grps
));
285 for (i
= 0; i
< count
; ++i
) {
286 sep
= strchr(argv
, '/');
290 wsep
= strchr(argv
, ',');
294 if (get_unsigned(&grps
[i
].id
, argv
, 0))
300 if (get_unsigned(&w
, wsep
, 0) || w
== 0 || w
> 256)
301 invarg("\"weight\" is invalid\n", wsep
);
302 grps
[i
].weight
= w
- 1;
311 return addattr_l(n
, maxlen
, NHA_GROUP
, grps
, count
* sizeof(*grps
));
314 static int ipnh_modify(int cmd
, unsigned int flags
, int argc
, char **argv
)
321 .n
.nlmsg_len
= NLMSG_LENGTH(sizeof(struct nhmsg
)),
322 .n
.nlmsg_flags
= NLM_F_REQUEST
| flags
,
324 .nhm
.nh_family
= preferred_family
,
329 if (!strcmp(*argv
, "id")) {
333 if (get_unsigned(&id
, *argv
, 0))
334 invarg("invalid id value", *argv
);
335 addattr32(&req
.n
, sizeof(req
), NHA_ID
, id
);
336 } else if (!strcmp(*argv
, "dev")) {
340 ifindex
= ll_name_to_index(*argv
);
342 invarg("Device does not exist\n", *argv
);
343 addattr32(&req
.n
, sizeof(req
), NHA_OIF
, ifindex
);
344 if (req
.nhm
.nh_family
== AF_UNSPEC
)
345 req
.nhm
.nh_family
= AF_INET
;
346 } else if (strcmp(*argv
, "via") == 0) {
351 family
= read_family(*argv
);
352 if (family
== AF_UNSPEC
)
353 family
= req
.nhm
.nh_family
;
356 get_addr(&addr
, *argv
, family
);
357 if (req
.nhm
.nh_family
== AF_UNSPEC
)
358 req
.nhm
.nh_family
= addr
.family
;
359 else if (req
.nhm
.nh_family
!= addr
.family
)
360 invarg("address family mismatch\n", *argv
);
361 addattr_l(&req
.n
, sizeof(req
), NHA_GATEWAY
,
362 &addr
.data
, addr
.bytelen
);
363 } else if (strcmp(*argv
, "encap") == 0) {
365 struct rtattr
*rta
= (void *)buf
;
367 rta
->rta_type
= NHA_ENCAP
;
368 rta
->rta_len
= RTA_LENGTH(0);
370 lwt_parse_encap(rta
, sizeof(buf
), &argc
, &argv
,
371 NHA_ENCAP
, NHA_ENCAP_TYPE
);
373 if (rta
->rta_len
> RTA_LENGTH(0)) {
374 addraw_l(&req
.n
, 1024, RTA_DATA(rta
),
377 } else if (!strcmp(*argv
, "blackhole")) {
378 addattr_l(&req
.n
, sizeof(req
), NHA_BLACKHOLE
, NULL
, 0);
379 if (req
.nhm
.nh_family
== AF_UNSPEC
)
380 req
.nhm
.nh_family
= AF_INET
;
381 } else if (!strcmp(*argv
, "onlink")) {
382 nh_flags
|= RTNH_F_ONLINK
;
383 } else if (!strcmp(*argv
, "group")) {
386 if (add_nh_group_attr(&req
.n
, sizeof(req
), *argv
))
387 invarg("\"group\" value is invalid\n", *argv
);
388 } else if (matches(*argv
, "protocol") == 0) {
392 if (rtnl_rtprot_a2n(&prot
, *argv
))
393 invarg("\"protocol\" value is invalid\n", *argv
);
394 req
.nhm
.nh_protocol
= prot
;
395 } else if (strcmp(*argv
, "help") == 0) {
403 req
.nhm
.nh_flags
= nh_flags
;
405 if (rtnl_talk(&rth
, &req
.n
, NULL
) < 0)
411 static int ipnh_get_id(__u32 id
)
418 .n
.nlmsg_len
= NLMSG_LENGTH(sizeof(struct nhmsg
)),
419 .n
.nlmsg_flags
= NLM_F_REQUEST
,
420 .n
.nlmsg_type
= RTM_GETNEXTHOP
,
421 .nhm
.nh_family
= preferred_family
,
423 struct nlmsghdr
*answer
;
425 addattr32(&req
.n
, sizeof(req
), NHA_ID
, id
);
427 if (rtnl_talk(&rth
, &req
.n
, &answer
) < 0)
432 if (print_nexthop(answer
, (void *)stdout
) < 0) {
445 static int ipnh_list_flush(int argc
, char **argv
, int action
)
447 unsigned int all
= (argc
== 0);
450 if (!matches(*argv
, "dev")) {
452 filter
.ifindex
= ll_name_to_index(*argv
);
454 invarg("Device does not exist\n", *argv
);
455 } else if (!matches(*argv
, "groups")) {
457 } else if (!matches(*argv
, "master")) {
459 filter
.master
= ll_name_to_index(*argv
);
461 invarg("Device does not exist\n", *argv
);
462 } else if (matches(*argv
, "vrf") == 0) {
464 if (!name_is_vrf(*argv
))
465 invarg("Invalid VRF\n", *argv
);
466 filter
.master
= ll_name_to_index(*argv
);
468 invarg("VRF does not exist\n", *argv
);
469 } else if (!strcmp(*argv
, "id")) {
473 if (get_unsigned(&id
, *argv
, 0))
474 invarg("invalid id value", *argv
);
475 return ipnh_get_id(id
);
476 } else if (matches(*argv
, "help") == 0) {
484 if (action
== IPNH_FLUSH
)
485 return ipnh_flush(all
);
487 if (rtnl_nexthopdump_req(&rth
, preferred_family
, nh_dump_filter
) < 0) {
488 perror("Cannot send dump request");
494 if (rtnl_dump_filter(&rth
, print_nexthop
, stdout
) < 0) {
495 fprintf(stderr
, "Dump terminated\n");
505 static int ipnh_get(int argc
, char **argv
)
510 if (!strcmp(*argv
, "id")) {
512 if (get_unsigned(&id
, *argv
, 0))
513 invarg("invalid id value", *argv
);
525 return ipnh_get_id(id
);
528 int do_ipnh(int argc
, char **argv
)
531 return ipnh_list_flush(0, NULL
, IPNH_LIST
);
533 if (!matches(*argv
, "add"))
534 return ipnh_modify(RTM_NEWNEXTHOP
, NLM_F_CREATE
|NLM_F_EXCL
,
536 if (!matches(*argv
, "replace"))
537 return ipnh_modify(RTM_NEWNEXTHOP
, NLM_F_CREATE
|NLM_F_REPLACE
,
539 if (!matches(*argv
, "delete"))
540 return ipnh_modify(RTM_DELNEXTHOP
, 0, argc
-1, argv
+1);
542 if (!matches(*argv
, "list") ||
543 !matches(*argv
, "show") ||
544 !matches(*argv
, "lst"))
545 return ipnh_list_flush(argc
-1, argv
+1, IPNH_LIST
);
547 if (!matches(*argv
, "get"))
548 return ipnh_get(argc
-1, argv
+1);
550 if (!matches(*argv
, "flush"))
551 return ipnh_list_flush(argc
-1, argv
+1, IPNH_FLUSH
);
553 if (!matches(*argv
, "help"))
557 "Command \"%s\" is unknown, try \"ip nexthop help\".\n", *argv
);