* [PATCH iproute2-next 0/9] ip: Add support for nexthop objects
@ 2019-05-30 3:17 David Ahern
2019-05-30 3:17 ` [PATCH iproute2-next 1/9] libnetlink: Set NLA_F_NESTED in rta_nest David Ahern
` (8 more replies)
0 siblings, 9 replies; 23+ messages in thread
From: David Ahern @ 2019-05-30 3:17 UTC (permalink / raw)
To: stephen; +Cc: netdev, David Ahern
From: David Ahern <dsahern@gmail.com>
This set adds support for nexthop objects to the ip command. The syntax
for nexthop objects is identical to the current 'ip route .. nexthop ...'
syntax making it easy to convert existing use cases.
David Ahern (9):
libnetlink: Set NLA_F_NESTED in rta_nest
lwtunnel: Pass encap and encap_type attributes to lwt_parse_encap
libnetlink: Add helper to add a group via setsockopt
uapi: Import nexthop object API
libnetlink: Add helper to create nexthop dump request
ip route: Export print_rt_flags and print_rta_if
ip: Add support for nexthop objects
ip route: Add option to use nexthop objects
ipmonitor: Add nexthop option to monitor
include/libnetlink.h | 7 +-
include/uapi/linux/nexthop.h | 56 +++++
ip/Makefile | 3 +-
ip/ip.c | 3 +-
ip/ip_common.h | 8 +-
ip/ipmonitor.c | 24 ++
ip/ipnexthop.c | 571 +++++++++++++++++++++++++++++++++++++++++++
ip/iproute.c | 25 +-
ip/iproute_lwtunnel.c | 7 +-
lib/libnetlink.c | 34 +++
10 files changed, 723 insertions(+), 15 deletions(-)
create mode 100644 include/uapi/linux/nexthop.h
create mode 100644 ip/ipnexthop.c
--
2.11.0
^ permalink raw reply [flat|nested] 23+ messages in thread* [PATCH iproute2-next 1/9] libnetlink: Set NLA_F_NESTED in rta_nest 2019-05-30 3:17 [PATCH iproute2-next 0/9] ip: Add support for nexthop objects David Ahern @ 2019-05-30 3:17 ` David Ahern 2019-05-30 17:43 ` Stephen Hemminger 2019-05-30 3:17 ` [PATCH iproute2-next 2/9] lwtunnel: Pass encap and encap_type attributes to lwt_parse_encap David Ahern ` (7 subsequent siblings) 8 siblings, 1 reply; 23+ messages in thread From: David Ahern @ 2019-05-30 3:17 UTC (permalink / raw) To: stephen; +Cc: netdev, David Ahern From: David Ahern <dsahern@gmail.com> Kernel now requires NLA_F_NESTED to be set on new nested attributes. Set NLA_F_NESTED in rta_nest. Signed-off-by: David Ahern <dsahern@gmail.com> --- lib/libnetlink.c | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/libnetlink.c b/lib/libnetlink.c index 0d48a3d43cf0..6ae51a9dba14 100644 --- a/lib/libnetlink.c +++ b/lib/libnetlink.c @@ -1336,6 +1336,7 @@ struct rtattr *rta_nest(struct rtattr *rta, int maxlen, int type) struct rtattr *nest = RTA_TAIL(rta); rta_addattr_l(rta, maxlen, type, NULL, 0); + nest->rta_type |= NLA_F_NESTED; return nest; } -- 2.11.0 ^ permalink raw reply related [flat|nested] 23+ messages in thread
* Re: [PATCH iproute2-next 1/9] libnetlink: Set NLA_F_NESTED in rta_nest 2019-05-30 3:17 ` [PATCH iproute2-next 1/9] libnetlink: Set NLA_F_NESTED in rta_nest David Ahern @ 2019-05-30 17:43 ` Stephen Hemminger 2019-05-30 17:50 ` David Ahern 0 siblings, 1 reply; 23+ messages in thread From: Stephen Hemminger @ 2019-05-30 17:43 UTC (permalink / raw) To: David Ahern; +Cc: netdev, David Ahern On Wed, 29 May 2019 20:17:38 -0700 David Ahern <dsahern@kernel.org> wrote: > From: David Ahern <dsahern@gmail.com> > > Kernel now requires NLA_F_NESTED to be set on new nested > attributes. Set NLA_F_NESTED in rta_nest. > > Signed-off-by: David Ahern <dsahern@gmail.com> > --- > lib/libnetlink.c | 1 + > 1 file changed, 1 insertion(+) > > diff --git a/lib/libnetlink.c b/lib/libnetlink.c > index 0d48a3d43cf0..6ae51a9dba14 100644 > --- a/lib/libnetlink.c > +++ b/lib/libnetlink.c > @@ -1336,6 +1336,7 @@ struct rtattr *rta_nest(struct rtattr *rta, int maxlen, int type) > struct rtattr *nest = RTA_TAIL(rta); > > rta_addattr_l(rta, maxlen, type, NULL, 0); > + nest->rta_type |= NLA_F_NESTED; > > return nest; > } I assume older kernels ignore the attribute? Also, how is this opt-in for running iproute2-next on old kernels? ^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH iproute2-next 1/9] libnetlink: Set NLA_F_NESTED in rta_nest 2019-05-30 17:43 ` Stephen Hemminger @ 2019-05-30 17:50 ` David Ahern 2019-05-31 6:35 ` Michal Kubecek 0 siblings, 1 reply; 23+ messages in thread From: David Ahern @ 2019-05-30 17:50 UTC (permalink / raw) To: Stephen Hemminger; +Cc: netdev On 5/30/19 11:43 AM, Stephen Hemminger wrote: > > I assume older kernels ignore the attribute? > > Also, how is this opt-in for running iproute2-next on old kernels? > from what I can see older kernel added the flag when generating a nest (users of nla_nest_start), but did not pay attention to the flag for messages received from userspace. ^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH iproute2-next 1/9] libnetlink: Set NLA_F_NESTED in rta_nest 2019-05-30 17:50 ` David Ahern @ 2019-05-31 6:35 ` Michal Kubecek 0 siblings, 0 replies; 23+ messages in thread From: Michal Kubecek @ 2019-05-31 6:35 UTC (permalink / raw) To: netdev; +Cc: David Ahern, Stephen Hemminger On Thu, May 30, 2019 at 11:50:59AM -0600, David Ahern wrote: > On 5/30/19 11:43 AM, Stephen Hemminger wrote: > > > > I assume older kernels ignore the attribute? > > > > Also, how is this opt-in for running iproute2-next on old kernels? > > from what I can see older kernel added the flag when generating a nest > (users of nla_nest_start), but did not pay attention to the flag for > messages received from userspace. Most of kernel generated messages do not set NLA_F_NESTED as nla_nest_start() did not set it automatically before commit ae0be8de9a53 ("netlink: make nla_nest_start() add NLA_F_NESTED flag") which only reached mainline in 5.2 merge window. Unfortunately we cannot simply start setting the flag everywhere as there may be userspace software using nla->type directly rather than through a wrapper masking out the flags. On the other hand, it's safe to set set NLA_F_NESTED in messages sent to kernel. When checking kernel tree for direct nla->type access, I found only few (~10) places doing that and with one exception, those were special cases, e.g. when attribute type was (ab)used as an array index. Most of the code (and IIRC all of rtnetlink) either uses parse functions or nla_type() accessor. Michal ^ permalink raw reply [flat|nested] 23+ messages in thread
* [PATCH iproute2-next 2/9] lwtunnel: Pass encap and encap_type attributes to lwt_parse_encap 2019-05-30 3:17 [PATCH iproute2-next 0/9] ip: Add support for nexthop objects David Ahern 2019-05-30 3:17 ` [PATCH iproute2-next 1/9] libnetlink: Set NLA_F_NESTED in rta_nest David Ahern @ 2019-05-30 3:17 ` David Ahern 2019-05-30 3:17 ` [PATCH iproute2-next 3/9] libnetlink: Add helper to add a group via setsockopt David Ahern ` (6 subsequent siblings) 8 siblings, 0 replies; 23+ messages in thread From: David Ahern @ 2019-05-30 3:17 UTC (permalink / raw) To: stephen; +Cc: netdev, David Ahern From: David Ahern <dsahern@gmail.com> lwt_parse_encap currently assumes the encap attribute is RTA_ENCAP and the type is RTA_ENCAP_TYPE. Change lwt_parse_encap to take these as input arguments for reuse by nexthop code which has the attributes as NHA_ENCAP and NHA_ENCAP_TYPE. Signed-off-by: David Ahern <dsahern@gmail.com> --- ip/ip_common.h | 3 ++- ip/iproute.c | 6 ++++-- ip/iproute_lwtunnel.c | 7 ++++--- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/ip/ip_common.h b/ip/ip_common.h index b4aa34a70c92..df279e4f7b9a 100644 --- a/ip/ip_common.h +++ b/ip/ip_common.h @@ -134,7 +134,8 @@ int bond_parse_xstats(struct link_util *lu, int argc, char **argv); int bond_print_xstats(struct nlmsghdr *n, void *arg); /* iproute_lwtunnel.c */ -int lwt_parse_encap(struct rtattr *rta, size_t len, int *argcp, char ***argvp); +int lwt_parse_encap(struct rtattr *rta, size_t len, int *argcp, char ***argvp, + int encap_attr, int encap_type_attr); void lwt_print_encap(FILE *fp, struct rtattr *encap_type, struct rtattr *encap); /* iplink_xdp.c */ diff --git a/ip/iproute.c b/ip/iproute.c index 2b3dcc5dbd53..440b1fc8b413 100644 --- a/ip/iproute.c +++ b/ip/iproute.c @@ -997,7 +997,8 @@ static int parse_one_nh(struct nlmsghdr *n, struct rtmsg *r, } else if (strcmp(*argv, "encap") == 0) { int old_len = rta->rta_len; - if (lwt_parse_encap(rta, len, &argc, &argv)) + if (lwt_parse_encap(rta, len, &argc, &argv, + RTA_ENCAP, RTA_ENCAP_TYPE)) return -1; rtnh->rtnh_len += rta->rta_len - old_len; } else if (strcmp(*argv, "as") == 0) { @@ -1416,7 +1417,8 @@ static int iproute_modify(int cmd, unsigned int flags, int argc, char **argv) rta->rta_type = RTA_ENCAP; rta->rta_len = RTA_LENGTH(0); - lwt_parse_encap(rta, sizeof(buf), &argc, &argv); + lwt_parse_encap(rta, sizeof(buf), &argc, &argv, + RTA_ENCAP, RTA_ENCAP_TYPE); if (rta->rta_len > RTA_LENGTH(0)) addraw_l(&req.n, 1024 diff --git a/ip/iproute_lwtunnel.c b/ip/iproute_lwtunnel.c index 03217b8f08f8..60f34a32a6e5 100644 --- a/ip/iproute_lwtunnel.c +++ b/ip/iproute_lwtunnel.c @@ -1111,7 +1111,8 @@ static int parse_encap_bpf(struct rtattr *rta, size_t len, int *argcp, return 0; } -int lwt_parse_encap(struct rtattr *rta, size_t len, int *argcp, char ***argvp) +int lwt_parse_encap(struct rtattr *rta, size_t len, int *argcp, char ***argvp, + int encap_attr, int encap_type_attr) { struct rtattr *nest; int argc = *argcp; @@ -1131,7 +1132,7 @@ int lwt_parse_encap(struct rtattr *rta, size_t len, int *argcp, char ***argvp) exit(-1); } - nest = rta_nest(rta, len, RTA_ENCAP); + nest = rta_nest(rta, len, encap_attr); switch (type) { case LWTUNNEL_ENCAP_MPLS: ret = parse_encap_mpls(rta, len, &argc, &argv); @@ -1164,7 +1165,7 @@ int lwt_parse_encap(struct rtattr *rta, size_t len, int *argcp, char ***argvp) rta_nest_end(rta, nest); - ret = rta_addattr16(rta, len, RTA_ENCAP_TYPE, type); + ret = rta_addattr16(rta, len, encap_type_attr, type); *argcp = argc; *argvp = argv; -- 2.11.0 ^ permalink raw reply related [flat|nested] 23+ messages in thread
* [PATCH iproute2-next 3/9] libnetlink: Add helper to add a group via setsockopt 2019-05-30 3:17 [PATCH iproute2-next 0/9] ip: Add support for nexthop objects David Ahern 2019-05-30 3:17 ` [PATCH iproute2-next 1/9] libnetlink: Set NLA_F_NESTED in rta_nest David Ahern 2019-05-30 3:17 ` [PATCH iproute2-next 2/9] lwtunnel: Pass encap and encap_type attributes to lwt_parse_encap David Ahern @ 2019-05-30 3:17 ` David Ahern 2019-05-30 3:17 ` [PATCH iproute2-next 4/9] uapi: Import nexthop object API David Ahern ` (5 subsequent siblings) 8 siblings, 0 replies; 23+ messages in thread From: David Ahern @ 2019-05-30 3:17 UTC (permalink / raw) To: stephen; +Cc: netdev, David Ahern From: David Ahern <dsahern@gmail.com> groups > 31 have to be joined using the setsockopt. Since the nexthop group is 32, add a helper to allow 'ip monitor' to listen for nexthop messages. Signed-off-by: David Ahern <dsahern@gmail.com> --- include/libnetlink.h | 3 ++- lib/libnetlink.c | 6 ++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/include/libnetlink.h b/include/libnetlink.h index 503b3ec11bb6..599b2c592f68 100644 --- a/include/libnetlink.h +++ b/include/libnetlink.h @@ -45,7 +45,8 @@ int rtnl_open(struct rtnl_handle *rth, unsigned int subscriptions) int rtnl_open_byproto(struct rtnl_handle *rth, unsigned int subscriptions, int protocol) __attribute__((warn_unused_result)); - +int rtnl_add_nl_group(struct rtnl_handle *rth, unsigned int group) + __attribute__((warn_unused_result)); void rtnl_close(struct rtnl_handle *rth); void rtnl_set_strict_dump(struct rtnl_handle *rth); diff --git a/lib/libnetlink.c b/lib/libnetlink.c index 6ae51a9dba14..eb85bbdf01ee 100644 --- a/lib/libnetlink.c +++ b/lib/libnetlink.c @@ -173,6 +173,12 @@ void rtnl_set_strict_dump(struct rtnl_handle *rth) rth->flags |= RTNL_HANDLE_F_STRICT_CHK; } +int rtnl_add_nl_group(struct rtnl_handle *rth, unsigned int group) +{ + return setsockopt(rth->fd, SOL_NETLINK, NETLINK_ADD_MEMBERSHIP, + &group, sizeof(group)); +} + void rtnl_close(struct rtnl_handle *rth) { if (rth->fd >= 0) { -- 2.11.0 ^ permalink raw reply related [flat|nested] 23+ messages in thread
* [PATCH iproute2-next 4/9] uapi: Import nexthop object API 2019-05-30 3:17 [PATCH iproute2-next 0/9] ip: Add support for nexthop objects David Ahern ` (2 preceding siblings ...) 2019-05-30 3:17 ` [PATCH iproute2-next 3/9] libnetlink: Add helper to add a group via setsockopt David Ahern @ 2019-05-30 3:17 ` David Ahern 2019-05-30 3:17 ` [PATCH iproute2-next 5/9] libnetlink: Add helper to create nexthop dump request David Ahern ` (4 subsequent siblings) 8 siblings, 0 replies; 23+ messages in thread From: David Ahern @ 2019-05-30 3:17 UTC (permalink / raw) To: stephen; +Cc: netdev, David Ahern From: David Ahern <dsahern@gmail.com> Add nexthop.h from kernel with the uapi for nexthop objects. Signed-off-by: David Ahern <dsahern@gmail.com> --- include/uapi/linux/nexthop.h | 56 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 include/uapi/linux/nexthop.h diff --git a/include/uapi/linux/nexthop.h b/include/uapi/linux/nexthop.h new file mode 100644 index 000000000000..b56c5b895476 --- /dev/null +++ b/include/uapi/linux/nexthop.h @@ -0,0 +1,56 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef _LINUX_NEXTHOP_H +#define _LINUX_NEXTHOP_H + +#include <linux/types.h> + +struct nhmsg { + unsigned char nh_family; + unsigned char nh_scope; /* return only */ + unsigned char nh_protocol; /* Routing protocol that installed nh */ + unsigned char resvd; + unsigned int nh_flags; /* RTNH_F flags */ +}; + +/* entry in a nexthop group */ +struct nexthop_grp { + __u32 id; /* nexthop id - must exist */ + __u8 weight; /* weight of this nexthop */ + __u8 resvd1; + __u16 resvd2; +}; + +enum { + NEXTHOP_GRP_TYPE_MPATH, /* default type if not specified */ + __NEXTHOP_GRP_TYPE_MAX, +}; + +#define NEXTHOP_GRP_TYPE_MAX (__NEXTHOP_GRP_TYPE_MAX - 1) + +enum { + NHA_UNSPEC, + NHA_ID, /* u32; id for nexthop. id == 0 means auto-assign */ + + NHA_GROUP, /* array of nexthop_grp */ + NHA_GROUP_TYPE, /* u16 one of NEXTHOP_GRP_TYPE */ + /* if NHA_GROUP attribute is added, no other attributes can be set */ + + NHA_BLACKHOLE, /* flag; nexthop used to blackhole packets */ + /* if NHA_BLACKHOLE is added, OIF, GATEWAY, ENCAP can not be set */ + + NHA_OIF, /* u32; nexthop device */ + NHA_GATEWAY, /* be32 (IPv4) or in6_addr (IPv6) gw address */ + NHA_ENCAP_TYPE, /* u16; lwt encap type */ + NHA_ENCAP, /* lwt encap data */ + + /* NHA_OIF can be appended to dump request to return only + * nexthops using given device + */ + NHA_GROUPS, /* flag; only return nexthop groups in dump */ + NHA_MASTER, /* u32; only return nexthops with given master dev */ + + __NHA_MAX, +}; + +#define NHA_MAX (__NHA_MAX - 1) +#endif -- 2.11.0 ^ permalink raw reply related [flat|nested] 23+ messages in thread
* [PATCH iproute2-next 5/9] libnetlink: Add helper to create nexthop dump request 2019-05-30 3:17 [PATCH iproute2-next 0/9] ip: Add support for nexthop objects David Ahern ` (3 preceding siblings ...) 2019-05-30 3:17 ` [PATCH iproute2-next 4/9] uapi: Import nexthop object API David Ahern @ 2019-05-30 3:17 ` David Ahern 2019-05-30 19:56 ` Roopa Prabhu 2019-05-30 3:17 ` [PATCH iproute2-next 6/9] ip route: Export print_rt_flags and print_rta_if David Ahern ` (3 subsequent siblings) 8 siblings, 1 reply; 23+ messages in thread From: David Ahern @ 2019-05-30 3:17 UTC (permalink / raw) To: stephen; +Cc: netdev, David Ahern From: David Ahern <dsahern@gmail.com> Add rtnl_nexthopdump_req to initiate a dump request of nexthop objects. Signed-off-by: David Ahern <dsahern@gmail.com> --- include/libnetlink.h | 4 ++++ lib/libnetlink.c | 27 +++++++++++++++++++++++++++ 2 files changed, 31 insertions(+) diff --git a/include/libnetlink.h b/include/libnetlink.h index 599b2c592f68..1ddba8dcd220 100644 --- a/include/libnetlink.h +++ b/include/libnetlink.h @@ -93,6 +93,10 @@ int rtnl_dump_request(struct rtnl_handle *rth, int type, void *req, int rtnl_dump_request_n(struct rtnl_handle *rth, struct nlmsghdr *n) __attribute__((warn_unused_result)); +int rtnl_nexthopdump_req(struct rtnl_handle *rth, int family, + req_filter_fn_t filter_fn) + __attribute__((warn_unused_result)); + struct rtnl_ctrl_data { int nsid; }; diff --git a/lib/libnetlink.c b/lib/libnetlink.c index eb85bbdf01ee..c1cdda3b8d4e 100644 --- a/lib/libnetlink.c +++ b/lib/libnetlink.c @@ -25,6 +25,7 @@ #include <linux/fib_rules.h> #include <linux/if_addrlabel.h> #include <linux/if_bridge.h> +#include <linux/nexthop.h> #include "libnetlink.h" @@ -252,6 +253,32 @@ int rtnl_open(struct rtnl_handle *rth, unsigned int subscriptions) return rtnl_open_byproto(rth, subscriptions, NETLINK_ROUTE); } +int rtnl_nexthopdump_req(struct rtnl_handle *rth, int family, + req_filter_fn_t filter_fn) +{ + struct { + struct nlmsghdr nlh; + struct nhmsg nhm; + char buf[128]; + } req = { + .nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg)), + .nlh.nlmsg_type = RTM_GETNEXTHOP, + .nlh.nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST, + .nlh.nlmsg_seq = rth->dump = ++rth->seq, + .nhm.nh_family = family, + }; + + if (filter_fn) { + int err; + + err = filter_fn(&req.nlh, sizeof(req)); + if (err) + return err; + } + + return send(rth->fd, &req, sizeof(req), 0); +} + int rtnl_addrdump_req(struct rtnl_handle *rth, int family, req_filter_fn_t filter_fn) { -- 2.11.0 ^ permalink raw reply related [flat|nested] 23+ messages in thread
* Re: [PATCH iproute2-next 5/9] libnetlink: Add helper to create nexthop dump request 2019-05-30 3:17 ` [PATCH iproute2-next 5/9] libnetlink: Add helper to create nexthop dump request David Ahern @ 2019-05-30 19:56 ` Roopa Prabhu 2019-05-30 19:59 ` David Ahern 0 siblings, 1 reply; 23+ messages in thread From: Roopa Prabhu @ 2019-05-30 19:56 UTC (permalink / raw) To: David Ahern; +Cc: Stephen Hemminger, netdev, David Ahern On Wed, May 29, 2019 at 9:04 PM David Ahern <dsahern@kernel.org> wrote: > > From: David Ahern <dsahern@gmail.com> > > Add rtnl_nexthopdump_req to initiate a dump request of nexthop objects. > > Signed-off-by: David Ahern <dsahern@gmail.com> > --- > include/libnetlink.h | 4 ++++ > lib/libnetlink.c | 27 +++++++++++++++++++++++++++ > 2 files changed, 31 insertions(+) > > diff --git a/include/libnetlink.h b/include/libnetlink.h > index 599b2c592f68..1ddba8dcd220 100644 > --- a/include/libnetlink.h > +++ b/include/libnetlink.h > @@ -93,6 +93,10 @@ int rtnl_dump_request(struct rtnl_handle *rth, int type, void *req, > int rtnl_dump_request_n(struct rtnl_handle *rth, struct nlmsghdr *n) > __attribute__((warn_unused_result)); > > +int rtnl_nexthopdump_req(struct rtnl_handle *rth, int family, > + req_filter_fn_t filter_fn) > + __attribute__((warn_unused_result)); > + > struct rtnl_ctrl_data { > int nsid; > }; > diff --git a/lib/libnetlink.c b/lib/libnetlink.c > index eb85bbdf01ee..c1cdda3b8d4e 100644 > --- a/lib/libnetlink.c > +++ b/lib/libnetlink.c > @@ -25,6 +25,7 @@ > #include <linux/fib_rules.h> > #include <linux/if_addrlabel.h> > #include <linux/if_bridge.h> > +#include <linux/nexthop.h> > > #include "libnetlink.h" > > @@ -252,6 +253,32 @@ int rtnl_open(struct rtnl_handle *rth, unsigned int subscriptions) > return rtnl_open_byproto(rth, subscriptions, NETLINK_ROUTE); > } > > +int rtnl_nexthopdump_req(struct rtnl_handle *rth, int family, > + req_filter_fn_t filter_fn) > +{ > + struct { > + struct nlmsghdr nlh; > + struct nhmsg nhm; > + char buf[128]; > + } req = { > + .nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg)), sizeof(struct nhmsg) ? > + .nlh.nlmsg_type = RTM_GETNEXTHOP, > + .nlh.nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST, > + .nlh.nlmsg_seq = rth->dump = ++rth->seq, > + .nhm.nh_family = family, > + }; > + > + if (filter_fn) { > + int err; > + > + err = filter_fn(&req.nlh, sizeof(req)); > + if (err) > + return err; > + } > + > + return send(rth->fd, &req, sizeof(req), 0); > +} > + > int rtnl_addrdump_req(struct rtnl_handle *rth, int family, > req_filter_fn_t filter_fn) > { > -- > 2.11.0 > ^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH iproute2-next 5/9] libnetlink: Add helper to create nexthop dump request 2019-05-30 19:56 ` Roopa Prabhu @ 2019-05-30 19:59 ` David Ahern 0 siblings, 0 replies; 23+ messages in thread From: David Ahern @ 2019-05-30 19:59 UTC (permalink / raw) To: Roopa Prabhu, David Ahern; +Cc: Stephen Hemminger, netdev On 5/30/19 1:56 PM, Roopa Prabhu wrote: >> diff --git a/lib/libnetlink.c b/lib/libnetlink.c >> index eb85bbdf01ee..c1cdda3b8d4e 100644 >> --- a/lib/libnetlink.c >> +++ b/lib/libnetlink.c >> @@ -25,6 +25,7 @@ >> #include <linux/fib_rules.h> >> #include <linux/if_addrlabel.h> >> #include <linux/if_bridge.h> >> +#include <linux/nexthop.h> >> >> #include "libnetlink.h" >> >> @@ -252,6 +253,32 @@ int rtnl_open(struct rtnl_handle *rth, unsigned int subscriptions) >> return rtnl_open_byproto(rth, subscriptions, NETLINK_ROUTE); >> } >> >> +int rtnl_nexthopdump_req(struct rtnl_handle *rth, int family, >> + req_filter_fn_t filter_fn) >> +{ >> + struct { >> + struct nlmsghdr nlh; >> + struct nhmsg nhm; >> + char buf[128]; >> + } req = { >> + .nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg)), > > sizeof(struct nhmsg) ? > good catch. fixed. ^ permalink raw reply [flat|nested] 23+ messages in thread
* [PATCH iproute2-next 6/9] ip route: Export print_rt_flags and print_rta_if 2019-05-30 3:17 [PATCH iproute2-next 0/9] ip: Add support for nexthop objects David Ahern ` (4 preceding siblings ...) 2019-05-30 3:17 ` [PATCH iproute2-next 5/9] libnetlink: Add helper to create nexthop dump request David Ahern @ 2019-05-30 3:17 ` David Ahern 2019-05-30 3:17 ` [PATCH iproute2-next 7/9] Add support for nexthop objects David Ahern ` (2 subsequent siblings) 8 siblings, 0 replies; 23+ messages in thread From: David Ahern @ 2019-05-30 3:17 UTC (permalink / raw) To: stephen; +Cc: netdev, David Ahern From: David Ahern <dsahern@gmail.com> Export print_rt_flags and print_rta_if for use by the nexthop command. Signed-off-by: David Ahern <dsahern@gmail.com> --- ip/ip_common.h | 3 ++- ip/iproute.c | 5 ++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/ip/ip_common.h b/ip/ip_common.h index df279e4f7b9a..1c90770be548 100644 --- a/ip/ip_common.h +++ b/ip/ip_common.h @@ -156,5 +156,6 @@ int name_is_vrf(const char *name); #endif void print_num(FILE *fp, unsigned int width, uint64_t count); - +void print_rt_flags(FILE *fp, unsigned int flags); +void print_rta_if(FILE *fp, const struct rtattr *rta, const char *prefix); #endif /* _IP_COMMON_H_ */ diff --git a/ip/iproute.c b/ip/iproute.c index 440b1fc8b413..c5a473704d95 100644 --- a/ip/iproute.c +++ b/ip/iproute.c @@ -349,7 +349,7 @@ static void print_rtax_features(FILE *fp, unsigned int features) "features", "%#llx ", of); } -static void print_rt_flags(FILE *fp, unsigned int flags) +void print_rt_flags(FILE *fp, unsigned int flags) { open_json_array(PRINT_JSON, is_json_context() ? "flags" : ""); @@ -394,8 +394,7 @@ static void print_rt_pref(FILE *fp, unsigned int pref) } } -static void print_rta_if(FILE *fp, const struct rtattr *rta, - const char *prefix) +void print_rta_if(FILE *fp, const struct rtattr *rta, const char *prefix) { const char *ifname = ll_index_to_name(rta_getattr_u32(rta)); -- 2.11.0 ^ permalink raw reply related [flat|nested] 23+ messages in thread
* [PATCH iproute2-next 7/9] Add support for nexthop objects 2019-05-30 3:17 [PATCH iproute2-next 0/9] ip: Add support for nexthop objects David Ahern ` (5 preceding siblings ...) 2019-05-30 3:17 ` [PATCH iproute2-next 6/9] ip route: Export print_rt_flags and print_rta_if David Ahern @ 2019-05-30 3:17 ` David Ahern 2019-05-30 17:45 ` Stephen Hemminger ` (3 more replies) 2019-05-30 3:17 ` [PATCH iproute2-next 8/9] ip route: Add option to use " David Ahern 2019-05-30 3:17 ` [PATCH iproute2-next 9/9] ipmonitor: Add nexthop option to monitor David Ahern 8 siblings, 4 replies; 23+ messages in thread From: David Ahern @ 2019-05-30 3:17 UTC (permalink / raw) To: stephen; +Cc: netdev, David Ahern From: David Ahern <dsahern@gmail.com> Add nexthop subcommand to ip. Implement basic commands for creating, deleting and dumping nexthop objects. Syntax follows 'nexthop' syntax from existing 'ip route' command. Signed-off-by: David Ahern <dsahern@gmail.com> --- ip/Makefile | 3 +- ip/ip.c | 3 +- ip/ip_common.h | 2 + ip/ipnexthop.c | 571 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 577 insertions(+), 2 deletions(-) create mode 100644 ip/ipnexthop.c diff --git a/ip/Makefile b/ip/Makefile index 7ce6e91a528c..5ab78d7d3b84 100644 --- a/ip/Makefile +++ b/ip/Makefile @@ -10,7 +10,8 @@ IPOBJ=ip.o ipaddress.o ipaddrlabel.o iproute.o iprule.o ipnetns.o \ link_iptnl.o link_gre6.o iplink_bond.o iplink_bond_slave.o iplink_hsr.o \ iplink_bridge.o iplink_bridge_slave.o ipfou.o iplink_ipvlan.o \ iplink_geneve.o iplink_vrf.o iproute_lwtunnel.o ipmacsec.o ipila.o \ - ipvrf.o iplink_xstats.o ipseg6.o iplink_netdevsim.o iplink_rmnet.o + ipvrf.o iplink_xstats.o ipseg6.o iplink_netdevsim.o iplink_rmnet.o \ + ipnexthop.o RTMONOBJ=rtmon.o diff --git a/ip/ip.c b/ip/ip.c index b71ae816e24d..b46fd8dd056c 100644 --- a/ip/ip.c +++ b/ip/ip.c @@ -50,7 +50,7 @@ static void usage(void) "where OBJECT := { link | address | addrlabel | route | rule | neigh | ntable |\n" " tunnel | tuntap | maddress | mroute | mrule | monitor | xfrm |\n" " netns | l2tp | fou | macsec | tcp_metrics | token | netconf | ila |\n" - " vrf | sr }\n" + " vrf | sr | nexthop }\n" " OPTIONS := { -V[ersion] | -s[tatistics] | -d[etails] | -r[esolve] |\n" " -h[uman-readable] | -iec | -j[son] | -p[retty] |\n" " -f[amily] { inet | inet6 | mpls | bridge | link } |\n" @@ -100,6 +100,7 @@ static const struct cmd { { "netconf", do_ipnetconf }, { "vrf", do_ipvrf}, { "sr", do_seg6 }, + { "nexthop", do_ipnh }, { "help", do_help }, { 0 } }; diff --git a/ip/ip_common.h b/ip/ip_common.h index 1c90770be548..5f73247ac488 100644 --- a/ip/ip_common.h +++ b/ip/ip_common.h @@ -47,6 +47,7 @@ int print_prefix(struct nlmsghdr *n, void *arg); int print_rule(struct nlmsghdr *n, void *arg); int print_netconf(struct rtnl_ctrl_data *ctrl, struct nlmsghdr *n, void *arg); +int print_nexthop(struct nlmsghdr *n, void *arg); void netns_map_init(void); void netns_nsid_socket_init(void); int print_nsid(struct nlmsghdr *n, void *arg); @@ -80,6 +81,7 @@ int do_ipvrf(int argc, char **argv); void vrf_reset(void); int netns_identify_pid(const char *pidstr, char *name, int len); int do_seg6(int argc, char **argv); +int do_ipnh(int argc, char **argv); int iplink_get(char *name, __u32 filt_mask); int iplink_ifla_xstats(int argc, char **argv); diff --git a/ip/ipnexthop.c b/ip/ipnexthop.c new file mode 100644 index 000000000000..84c2f01d7309 --- /dev/null +++ b/ip/ipnexthop.c @@ -0,0 +1,571 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * ip nexthop + * + * Copyright (c) 2017-19 Cumulus Networks + * Copyright (c) 2017-19 David Ahern <dsahern@gmail.com> + */ + +#include <linux/nexthop.h> +#include <stdio.h> +#include <string.h> +#include <rt_names.h> +#include <errno.h> + +#include "utils.h" +#include "ip_common.h" + +static struct +{ + unsigned int flushed; + unsigned int groups; + unsigned int ifindex; + unsigned int master; + char *flushb; + int flushp; + int flushe; +} filter; + +enum { + IPNH_LIST, + IPNH_FLUSH, +}; + +#define RTM_NHA(h) ((struct rtattr *)(((char *)(h)) + \ + NLMSG_ALIGN(sizeof(struct nhmsg)))) + +static void usage(void) __attribute__((noreturn)); + +static void usage(void) +{ + fprintf(stderr, + "Usage: ip nexthop { list | flush } SELECTOR\n" + " ip nexthop get id ID\n" + " ip nexthop { add | replace } NH\n" + " ip nexthop del id ID\n" + "SELECTOR := [ id ID ] [ dev DEV ] [ vrf NAME ] [ master DEV ] [ groups ]\n" + "NH := [ id ID ] [ via [ FAMILY ] ADDRESS ] [ dev DEV ]\n" + " [ group <id[,weight]>[/<id[,weight]>/...] ] [ NHFLAGS ]\n" + " [ encap ENCAPTYPE ENCAPHDR ]\n" + "NHFLAGS := [ onlink ]\n"); + exit(-1); +} + +static int nh_dump_filter(struct nlmsghdr *nlh, int reqlen) +{ + int err; + + if (filter.ifindex) { + err = addattr32(nlh, reqlen, NHA_OIF, filter.ifindex); + if (err) + return err; + } + + if (filter.groups) { + addattr_l(nlh, reqlen, NHA_GROUPS, NULL, 0); + if (err) + return err; + } + + if (filter.master) { + addattr32(nlh, reqlen, NHA_MASTER, filter.master); + if (err) + return err; + } + + return 0; +} + +struct rtnl_handle rth_del = { .fd = -1 }; + +static int delete_nexthop(__u32 id) +{ + struct { + struct nlmsghdr n; + struct nhmsg nhm; + char buf[64]; + } req = { + .n.nlmsg_len = NLMSG_LENGTH(sizeof(struct nhmsg)), + .n.nlmsg_flags = NLM_F_REQUEST, + .n.nlmsg_type = RTM_DELNEXTHOP, + .nhm.nh_family = AF_UNSPEC, + }; + + req.n.nlmsg_seq = ++rth_del.seq; + + addattr32(&req.n, sizeof(req), NHA_ID, id); + + if (rtnl_talk(&rth_del, &req.n, NULL) < 0) + return -1; + return 0; +} + +static int flush_nexthop(struct nlmsghdr *nlh, void *arg) +{ + struct nhmsg *nhm = NLMSG_DATA(nlh); + struct rtattr *tb[NHA_MAX+1]; + __u32 id = 0; + int len; + + len = nlh->nlmsg_len - NLMSG_SPACE(sizeof(*nhm)); + if (len < 0) { + fprintf(stderr, "BUG: wrong nlmsg len %d\n", len); + return -1; + } + + parse_rtattr(tb, NHA_MAX, RTM_NHA(nhm), len); + if (tb[NHA_ID]) + id = rta_getattr_u32(tb[NHA_ID]); + + if (id && !delete_nexthop(id)) + filter.flushed++; + + return 0; +} + +static int ipnh_flush(unsigned int all) +{ + int rc = -2; + + if (all) { + filter.groups = 1; + filter.ifindex = 0; + filter.master = 0; + } + + if (rtnl_open(&rth_del, 0) < 0) { + fprintf(stderr, "Cannot open rtnetlink\n"); + return EXIT_FAILURE; + } +again: + if (rtnl_nexthopdump_req(&rth, preferred_family, nh_dump_filter) < 0) { + perror("Cannot send dump request"); + goto out; + } + + if (rtnl_dump_filter(&rth, flush_nexthop, stdout) < 0) { + fprintf(stderr, "Dump terminated\n"); + goto out; + } + + /* if deleting all, then remove groups first */ + if (all && filter.groups) { + filter.groups = 0; + goto again; + } + + rc = 0; +out: + rtnl_close(&rth_del); + if (!filter.flushed) + printf("Nothing to flush\n"); + else + printf("Flushed %d nexthops\n", filter.flushed); + + return rc; +} + +static void print_nh_group(FILE *fp, const struct rtattr *grps_attr) +{ + struct nexthop_grp *nhg = RTA_DATA(grps_attr); + int num = RTA_PAYLOAD(grps_attr) / sizeof(*nhg); + int i; + + if (!num || num * sizeof(*nhg) != RTA_PAYLOAD(grps_attr)) { + fprintf(fp, "<invalid nexthop group>"); + return; + } + + open_json_array(PRINT_JSON, "group"); + print_string(PRINT_FP, NULL, "%s", "group "); + for (i = 0; i < num; ++i) { + open_json_object(NULL); + + if (i) + print_string(PRINT_FP, NULL, "%s", "/"); + + print_uint(PRINT_ANY, "id", "%u", nhg[i].id); + if (nhg[i].weight) + print_uint(PRINT_ANY, "weight", ",%u", nhg[i].weight); + + close_json_object(); + } + close_json_array(PRINT_JSON, NULL); +} + +static void print_nh_gateway(FILE *fp, const struct nhmsg *nhm, + const struct rtattr *rta) +{ + const char *gateway = format_host_rta(nhm->nh_family, rta); + + if (is_json_context()) + print_string(PRINT_JSON, "gateway", NULL, gateway); + else { + fprintf(fp, "via "); + print_color_string(PRINT_FP, ifa_family_color(nhm->nh_family), + NULL, "%s ", gateway); + } +} + +int print_nexthop(struct nlmsghdr *n, void *arg) +{ + struct nhmsg *nhm = NLMSG_DATA(n); + struct rtattr *tb[NHA_MAX+1]; + FILE *fp = (FILE *)arg; + int len; + + SPRINT_BUF(b1); + + if (n->nlmsg_type != RTM_DELNEXTHOP && + n->nlmsg_type != RTM_NEWNEXTHOP) { + fprintf(stderr, "Not a nexthop: %08x %08x %08x\n", + n->nlmsg_len, n->nlmsg_type, n->nlmsg_flags); + return -1; + } + + len = n->nlmsg_len - NLMSG_SPACE(sizeof(*nhm)); + if (len < 0) { + close_json_object(); + fprintf(stderr, "BUG: wrong nlmsg len %d\n", len); + return -1; + } + + parse_rtattr(tb, NHA_MAX, RTM_NHA(nhm), len); + + open_json_object(NULL); + + if (n->nlmsg_type == RTM_DELROUTE) + print_bool(PRINT_ANY, "deleted", "Deleted ", true); + + if (tb[NHA_ID]) + print_uint(PRINT_ANY, "id", "id %u ", + rta_getattr_u32(tb[NHA_ID])); + + if (tb[NHA_GROUP]) + print_nh_group(fp, tb[NHA_GROUP]); + + if (tb[NHA_ENCAP]) + lwt_print_encap(fp, tb[NHA_ENCAP_TYPE], tb[NHA_ENCAP]); + + if (tb[NHA_GATEWAY]) + print_nh_gateway(fp, nhm, tb[NHA_GATEWAY]); + + if (tb[NHA_OIF]) + print_rta_if(fp, tb[NHA_OIF], "dev"); + + if (nhm->nh_scope != RT_SCOPE_UNIVERSE || show_details > 0) { + print_string(PRINT_ANY, "scope", "scope %s ", + rtnl_rtscope_n2a(nhm->nh_scope, b1, sizeof(b1))); + } + + if (tb[NHA_BLACKHOLE]) + print_null(PRINT_ANY, "blackhole", "blackhole", NULL); + + if (nhm->nh_protocol != RTPROT_UNSPEC || show_details > 0) { + print_string(PRINT_ANY, "protocol", "proto %s ", + rtnl_rtprot_n2a(nhm->nh_protocol, b1, sizeof(b1))); + } + + if (tb[NHA_OIF]) + print_rt_flags(fp, nhm->nh_flags); + + print_string(PRINT_FP, NULL, "%s", "\n"); + close_json_object(); + fflush(fp); + + return 0; +} + +static int add_nh_group_attr(struct nlmsghdr *n, int maxlen, char *argv) +{ + struct nexthop_grp *grps; + int count = 0, i; + char *sep, *wsep; + + if (*argv != '\0') + count = 1; + + /* separator is '/' */ + sep = strchr(argv, '/'); + while (sep) { + count++; + sep = strchr(sep + 1, '/'); + } + + if (count == 0) + return -1; + + grps = calloc(count, sizeof(*grps)); + if (!grps) + return -1; + + for (i = 0; i < count; ++i) { + sep = strchr(argv, '/'); + if (sep) + *sep = '\0'; + + wsep = strchr(argv, ','); + if (wsep) + *wsep = '\0'; + + if (get_unsigned(&grps[i].id, argv, 0)) + return -1; + if (wsep) { + unsigned int tmp; + + wsep++; + if (get_unsigned(&tmp, wsep, 0)) + return -1; + if (tmp > 254) + return -1; + grps[i].weight = tmp; + } + + if (!sep) + break; + + argv = sep + 1; + } + + return addattr_l(n, maxlen, NHA_GROUP, grps, count * sizeof(*grps)); +} + +static int ipnh_modify(int cmd, unsigned int flags, int argc, char **argv) +{ + struct { + struct nlmsghdr n; + struct nhmsg nhm; + char buf[1024]; + } req = { + .n.nlmsg_len = NLMSG_LENGTH(sizeof(struct nhmsg)), + .n.nlmsg_flags = NLM_F_REQUEST | flags, + .n.nlmsg_type = cmd, + .nhm.nh_family = preferred_family, + }; + __u32 nh_flags = 0; + + while (argc > 0) { + if (!strcmp(*argv, "id")) { + __u32 id; + + NEXT_ARG(); + if (get_unsigned(&id, *argv, 0)) + invarg("invalid id value", *argv); + addattr32(&req.n, sizeof(req), NHA_ID, id); + } else if (!strcmp(*argv, "dev")) { + int ifindex; + + NEXT_ARG(); + ifindex = ll_name_to_index(*argv); + if (!ifindex) + invarg("Device does not exist\n", *argv); + addattr32(&req.n, sizeof(req), NHA_OIF, ifindex); + if (req.nhm.nh_family == AF_UNSPEC) + req.nhm.nh_family = AF_INET; + } else if (strcmp(*argv, "via") == 0) { + inet_prefix addr; + int family; + + NEXT_ARG(); + family = read_family(*argv); + if (family == AF_UNSPEC) + family = req.nhm.nh_family; + else + NEXT_ARG(); + get_addr(&addr, *argv, family); + if (req.nhm.nh_family == AF_UNSPEC) + req.nhm.nh_family = addr.family; + else if (req.nhm.nh_family != addr.family) + invarg("address family mismatch\n", *argv); + addattr_l(&req.n, sizeof(req), NHA_GATEWAY, + &addr.data, addr.bytelen); + } else if (strcmp(*argv, "encap") == 0) { + char buf[1024]; + struct rtattr *rta = (void *)buf; + + rta->rta_type = NHA_ENCAP; + rta->rta_len = RTA_LENGTH(0); + + lwt_parse_encap(rta, sizeof(buf), &argc, &argv, + NHA_ENCAP, NHA_ENCAP_TYPE); + + if (rta->rta_len > RTA_LENGTH(0)) { + addraw_l(&req.n, 1024, RTA_DATA(rta), + RTA_PAYLOAD(rta)); + } + } else if (!strcmp(*argv, "blackhole")) { + addattr_l(&req.n, sizeof(req), NHA_BLACKHOLE, NULL, 0); + if (req.nhm.nh_family == AF_UNSPEC) + req.nhm.nh_family = AF_INET; + } else if (!strcmp(*argv, "onlink")) { + nh_flags |= RTNH_F_ONLINK; + } else if (!strcmp(*argv, "group")) { + NEXT_ARG(); + + if (add_nh_group_attr(&req.n, sizeof(req), *argv)) + invarg("\"group\" value is invalid\n", *argv); + } else if (strcmp(*argv, "help") == 0) { + usage(); + } else { + invarg("", *argv); + } + argc--; argv++; + } + + req.nhm.nh_flags = nh_flags; + + if (rtnl_talk(&rth, &req.n, NULL) < 0) + return -2; + + return 0; +} + +static int ipnh_get_id(__u32 id) +{ + struct { + struct nlmsghdr n; + struct nhmsg nhm; + char buf[1024]; + } req = { + .n.nlmsg_len = NLMSG_LENGTH(sizeof(struct nhmsg)), + .n.nlmsg_flags = NLM_F_REQUEST, + .n.nlmsg_type = RTM_GETNEXTHOP, + .nhm.nh_family = preferred_family, + }; + struct nlmsghdr *answer; + + addattr32(&req.n, sizeof(req), NHA_ID, id); + + if (rtnl_talk(&rth, &req.n, &answer) < 0) + return -2; + + new_json_obj(json); + + if (print_nexthop(answer, (void *)stdout) < 0) { + free(answer); + return -1; + } + + delete_json_obj(); + fflush(stdout); + + free(answer); + + return 0; +} + +static int ipnh_list_flush(int argc, char **argv, int action) +{ + unsigned int all = (argc == 0); + + while (argc > 0) { + if (!matches(*argv, "dev")) { + NEXT_ARG(); + filter.ifindex = ll_name_to_index(*argv); + if (!filter.ifindex) + invarg("Device does not exist\n", *argv); + } else if (!matches(*argv, "groups")) { + filter.groups = 1; + } else if (!matches(*argv, "master")) { + NEXT_ARG(); + filter.master = ll_name_to_index(*argv); + if (!filter.master) + invarg("Device does not exist\n", *argv); + } else if (matches(*argv, "vrf") == 0) { + NEXT_ARG(); + if (!name_is_vrf(*argv)) + invarg("Invalid VRF\n", *argv); + filter.master = ll_name_to_index(*argv); + if (!filter.master) + invarg("VRF does not exist\n", *argv); + } else if (!strcmp(*argv, "id")) { + __u32 id; + + NEXT_ARG(); + if (get_unsigned(&id, *argv, 0)) + invarg("invalid id value", *argv); + return ipnh_get_id(id); + } else if (matches(*argv, "help") == 0) { + usage(); + } else { + invarg("", *argv); + } + argc--; argv++; + } + + if (action == IPNH_FLUSH) + return ipnh_flush(all); + + if (rtnl_nexthopdump_req(&rth, preferred_family, nh_dump_filter) < 0) { + perror("Cannot send dump request"); + return -2; + } + + new_json_obj(json); + + if (rtnl_dump_filter(&rth, print_nexthop, stdout) < 0) { + fprintf(stderr, "Dump terminated\n"); + return -2; + } + + delete_json_obj(); + fflush(stdout); + + return 0; +} + +static int ipnh_get(int argc, char **argv) +{ + __u32 id = 0; + + while (argc > 0) { + if (!strcmp(*argv, "id")) { + NEXT_ARG(); + if (get_unsigned(&id, *argv, 0)) + invarg("invalid id value", *argv); + } else { + usage(); + } + argc--; argv++; + } + + if (!id) { + usage(); + return -1; + } + + return ipnh_get_id(id); +} + +int do_ipnh(int argc, char **argv) +{ + if (argc < 1) + return ipnh_list_flush(0, NULL, IPNH_LIST); + + if (!matches(*argv, "add")) + return ipnh_modify(RTM_NEWNEXTHOP, NLM_F_CREATE|NLM_F_EXCL, + argc-1, argv+1); + if (!matches(*argv, "replace")) + return ipnh_modify(RTM_NEWNEXTHOP, NLM_F_CREATE|NLM_F_REPLACE, + argc-1, argv+1); + if (!matches(*argv, "delete")) + return ipnh_modify(RTM_DELNEXTHOP, 0, argc-1, argv+1); + + if (!matches(*argv, "list") || + !matches(*argv, "show") || + !matches(*argv, "lst")) + return ipnh_list_flush(argc-1, argv+1, IPNH_LIST); + + if (!matches(*argv, "get")) + return ipnh_get(argc-1, argv+1); + + if (!matches(*argv, "flush")) + return ipnh_list_flush(argc-1, argv+1, IPNH_FLUSH); + + if (!matches(*argv, "help")) + usage(); + + fprintf(stderr, + "Command \"%s\" is unknown, try \"ip nexthop help\".\n", *argv); + exit(-1); +} -- 2.11.0 ^ permalink raw reply related [flat|nested] 23+ messages in thread
* Re: [PATCH iproute2-next 7/9] Add support for nexthop objects 2019-05-30 3:17 ` [PATCH iproute2-next 7/9] Add support for nexthop objects David Ahern @ 2019-05-30 17:45 ` Stephen Hemminger 2019-05-30 17:52 ` David Ahern 2019-05-30 17:45 ` Stephen Hemminger ` (2 subsequent siblings) 3 siblings, 1 reply; 23+ messages in thread From: Stephen Hemminger @ 2019-05-30 17:45 UTC (permalink / raw) To: David Ahern; +Cc: netdev, David Ahern On Wed, 29 May 2019 20:17:44 -0700 David Ahern <dsahern@kernel.org> wrote: > +static struct > +{ kernel style is: static struct { ... ^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH iproute2-next 7/9] Add support for nexthop objects 2019-05-30 17:45 ` Stephen Hemminger @ 2019-05-30 17:52 ` David Ahern 0 siblings, 0 replies; 23+ messages in thread From: David Ahern @ 2019-05-30 17:52 UTC (permalink / raw) To: Stephen Hemminger, David Ahern; +Cc: netdev On 5/30/19 11:45 AM, Stephen Hemminger wrote: > On Wed, 29 May 2019 20:17:44 -0700 > David Ahern <dsahern@kernel.org> wrote: > >> +static struct >> +{ > > kernel style is: > > static struct { > ... > The style I used is consistent with existing code -- e.g., ip/iproute.c. If you want the above, I can do that. ^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH iproute2-next 7/9] Add support for nexthop objects 2019-05-30 3:17 ` [PATCH iproute2-next 7/9] Add support for nexthop objects David Ahern 2019-05-30 17:45 ` Stephen Hemminger @ 2019-05-30 17:45 ` Stephen Hemminger 2019-05-30 17:52 ` David Ahern 2019-05-30 17:52 ` Stephen Hemminger 2019-05-30 20:01 ` Roopa Prabhu 3 siblings, 1 reply; 23+ messages in thread From: Stephen Hemminger @ 2019-05-30 17:45 UTC (permalink / raw) To: David Ahern; +Cc: netdev, David Ahern On Wed, 29 May 2019 20:17:44 -0700 David Ahern <dsahern@kernel.org> wrote: > + > +struct rtnl_handle rth_del = { .fd = -1 }; > + can this be static? ^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH iproute2-next 7/9] Add support for nexthop objects 2019-05-30 17:45 ` Stephen Hemminger @ 2019-05-30 17:52 ` David Ahern 0 siblings, 0 replies; 23+ messages in thread From: David Ahern @ 2019-05-30 17:52 UTC (permalink / raw) To: Stephen Hemminger, David Ahern; +Cc: netdev On 5/30/19 11:45 AM, Stephen Hemminger wrote: > On Wed, 29 May 2019 20:17:44 -0700 > David Ahern <dsahern@kernel.org> wrote: > >> + >> +struct rtnl_handle rth_del = { .fd = -1 }; >> + > > can this be static? > oops, yes, it should be ^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH iproute2-next 7/9] Add support for nexthop objects 2019-05-30 3:17 ` [PATCH iproute2-next 7/9] Add support for nexthop objects David Ahern 2019-05-30 17:45 ` Stephen Hemminger 2019-05-30 17:45 ` Stephen Hemminger @ 2019-05-30 17:52 ` Stephen Hemminger 2019-05-30 17:55 ` David Ahern 2019-05-30 20:01 ` Roopa Prabhu 3 siblings, 1 reply; 23+ messages in thread From: Stephen Hemminger @ 2019-05-30 17:52 UTC (permalink / raw) To: David Ahern; +Cc: netdev, David Ahern On Wed, 29 May 2019 20:17:44 -0700 David Ahern <dsahern@kernel.org> wrote: > + > +static void print_nh_gateway(FILE *fp, const struct nhmsg *nhm, > + const struct rtattr *rta) > +{ > + const char *gateway = format_host_rta(nhm->nh_family, rta); > + > + if (is_json_context()) > + print_string(PRINT_JSON, "gateway", NULL, gateway); > + else { > + fprintf(fp, "via "); I was trying to get rid of all use of /fprintf(fp, / since it was indication of non-json code and fp is always stdout. Maybe print_string(PRINT_FP, NULL, "via ", NULL); print_color_string(PRINT_ANY, ifa_family_color(nhm->nh_family), "gateway", "%s ", format_host_rta(nhm->nh_family, rta)); ^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH iproute2-next 7/9] Add support for nexthop objects 2019-05-30 17:52 ` Stephen Hemminger @ 2019-05-30 17:55 ` David Ahern 0 siblings, 0 replies; 23+ messages in thread From: David Ahern @ 2019-05-30 17:55 UTC (permalink / raw) To: Stephen Hemminger, David Ahern; +Cc: netdev On 5/30/19 11:52 AM, Stephen Hemminger wrote: > On Wed, 29 May 2019 20:17:44 -0700 > David Ahern <dsahern@kernel.org> wrote: > >> + >> +static void print_nh_gateway(FILE *fp, const struct nhmsg *nhm, >> + const struct rtattr *rta) >> +{ >> + const char *gateway = format_host_rta(nhm->nh_family, rta); >> + >> + if (is_json_context()) >> + print_string(PRINT_JSON, "gateway", NULL, gateway); >> + else { >> + fprintf(fp, "via "); > > I was trying to get rid of all use of /fprintf(fp, / since it was > indication of non-json code and fp is always stdout. > > Maybe > print_string(PRINT_FP, NULL, "via ", NULL); > print_color_string(PRINT_ANY, ifa_family_color(nhm->nh_family), > "gateway", "%s ", format_host_rta(nhm->nh_family, rta)); > > > > The above is consistent with print_rta_gateway in ip/iproute.c. Since the only difference between the 2 is the header struct, I will change print_rta_gateway to take the address family over rtmsg and re-use it for both paths. ^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH iproute2-next 7/9] Add support for nexthop objects 2019-05-30 3:17 ` [PATCH iproute2-next 7/9] Add support for nexthop objects David Ahern ` (2 preceding siblings ...) 2019-05-30 17:52 ` Stephen Hemminger @ 2019-05-30 20:01 ` Roopa Prabhu 3 siblings, 0 replies; 23+ messages in thread From: Roopa Prabhu @ 2019-05-30 20:01 UTC (permalink / raw) To: David Ahern; +Cc: Stephen Hemminger, netdev, David Ahern On Wed, May 29, 2019 at 9:04 PM David Ahern <dsahern@kernel.org> wrote: > > From: David Ahern <dsahern@gmail.com> > > Add nexthop subcommand to ip. Implement basic commands for creating, > deleting and dumping nexthop objects. Syntax follows 'nexthop' syntax > from existing 'ip route' command. > > Signed-off-by: David Ahern <dsahern@gmail.com> If you are re-spinning, request you to pls add a usage example in the commit msg...or in the man-page. > --- > ip/Makefile | 3 +- > ip/ip.c | 3 +- > ip/ip_common.h | 2 + > ip/ipnexthop.c | 571 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ > 4 files changed, 577 insertions(+), 2 deletions(-) > create mode 100644 ip/ipnexthop.c > > diff --git a/ip/Makefile b/ip/Makefile > index 7ce6e91a528c..5ab78d7d3b84 100644 > --- a/ip/Makefile > +++ b/ip/Makefile > @@ -10,7 +10,8 @@ IPOBJ=ip.o ipaddress.o ipaddrlabel.o iproute.o iprule.o ipnetns.o \ > link_iptnl.o link_gre6.o iplink_bond.o iplink_bond_slave.o iplink_hsr.o \ > iplink_bridge.o iplink_bridge_slave.o ipfou.o iplink_ipvlan.o \ > iplink_geneve.o iplink_vrf.o iproute_lwtunnel.o ipmacsec.o ipila.o \ > - ipvrf.o iplink_xstats.o ipseg6.o iplink_netdevsim.o iplink_rmnet.o > + ipvrf.o iplink_xstats.o ipseg6.o iplink_netdevsim.o iplink_rmnet.o \ > + ipnexthop.o > > RTMONOBJ=rtmon.o > > diff --git a/ip/ip.c b/ip/ip.c > index b71ae816e24d..b46fd8dd056c 100644 > --- a/ip/ip.c > +++ b/ip/ip.c > @@ -50,7 +50,7 @@ static void usage(void) > "where OBJECT := { link | address | addrlabel | route | rule | neigh | ntable |\n" > " tunnel | tuntap | maddress | mroute | mrule | monitor | xfrm |\n" > " netns | l2tp | fou | macsec | tcp_metrics | token | netconf | ila |\n" > - " vrf | sr }\n" > + " vrf | sr | nexthop }\n" > " OPTIONS := { -V[ersion] | -s[tatistics] | -d[etails] | -r[esolve] |\n" > " -h[uman-readable] | -iec | -j[son] | -p[retty] |\n" > " -f[amily] { inet | inet6 | mpls | bridge | link } |\n" > @@ -100,6 +100,7 @@ static const struct cmd { > { "netconf", do_ipnetconf }, > { "vrf", do_ipvrf}, > { "sr", do_seg6 }, > + { "nexthop", do_ipnh }, > { "help", do_help }, > { 0 } > }; > diff --git a/ip/ip_common.h b/ip/ip_common.h > index 1c90770be548..5f73247ac488 100644 > --- a/ip/ip_common.h > +++ b/ip/ip_common.h > @@ -47,6 +47,7 @@ int print_prefix(struct nlmsghdr *n, void *arg); > int print_rule(struct nlmsghdr *n, void *arg); > int print_netconf(struct rtnl_ctrl_data *ctrl, > struct nlmsghdr *n, void *arg); > +int print_nexthop(struct nlmsghdr *n, void *arg); > void netns_map_init(void); > void netns_nsid_socket_init(void); > int print_nsid(struct nlmsghdr *n, void *arg); > @@ -80,6 +81,7 @@ int do_ipvrf(int argc, char **argv); > void vrf_reset(void); > int netns_identify_pid(const char *pidstr, char *name, int len); > int do_seg6(int argc, char **argv); > +int do_ipnh(int argc, char **argv); > > int iplink_get(char *name, __u32 filt_mask); > int iplink_ifla_xstats(int argc, char **argv); > diff --git a/ip/ipnexthop.c b/ip/ipnexthop.c > new file mode 100644 > index 000000000000..84c2f01d7309 > --- /dev/null > +++ b/ip/ipnexthop.c > @@ -0,0 +1,571 @@ > +// SPDX-License-Identifier: GPL-2.0 > +/* > + * ip nexthop > + * > + * Copyright (c) 2017-19 Cumulus Networks > + * Copyright (c) 2017-19 David Ahern <dsahern@gmail.com> > + */ > + > +#include <linux/nexthop.h> > +#include <stdio.h> > +#include <string.h> > +#include <rt_names.h> > +#include <errno.h> > + > +#include "utils.h" > +#include "ip_common.h" > + > +static struct > +{ > + unsigned int flushed; > + unsigned int groups; > + unsigned int ifindex; > + unsigned int master; > + char *flushb; > + int flushp; > + int flushe; > +} filter; > + > +enum { > + IPNH_LIST, > + IPNH_FLUSH, > +}; > + > +#define RTM_NHA(h) ((struct rtattr *)(((char *)(h)) + \ > + NLMSG_ALIGN(sizeof(struct nhmsg)))) > + > +static void usage(void) __attribute__((noreturn)); > + > +static void usage(void) > +{ > + fprintf(stderr, > + "Usage: ip nexthop { list | flush } SELECTOR\n" > + " ip nexthop get id ID\n" > + " ip nexthop { add | replace } NH\n" > + " ip nexthop del id ID\n" > + "SELECTOR := [ id ID ] [ dev DEV ] [ vrf NAME ] [ master DEV ] [ groups ]\n" > + "NH := [ id ID ] [ via [ FAMILY ] ADDRESS ] [ dev DEV ]\n" > + " [ group <id[,weight]>[/<id[,weight]>/...] ] [ NHFLAGS ]\n" > + " [ encap ENCAPTYPE ENCAPHDR ]\n" > + "NHFLAGS := [ onlink ]\n"); > + exit(-1); > +} > + > +static int nh_dump_filter(struct nlmsghdr *nlh, int reqlen) > +{ > + int err; > + > + if (filter.ifindex) { > + err = addattr32(nlh, reqlen, NHA_OIF, filter.ifindex); > + if (err) > + return err; > + } > + > + if (filter.groups) { > + addattr_l(nlh, reqlen, NHA_GROUPS, NULL, 0); > + if (err) > + return err; > + } > + > + if (filter.master) { > + addattr32(nlh, reqlen, NHA_MASTER, filter.master); > + if (err) > + return err; > + } > + > + return 0; > +} > + > +struct rtnl_handle rth_del = { .fd = -1 }; > + > +static int delete_nexthop(__u32 id) > +{ > + struct { > + struct nlmsghdr n; > + struct nhmsg nhm; > + char buf[64]; > + } req = { > + .n.nlmsg_len = NLMSG_LENGTH(sizeof(struct nhmsg)), > + .n.nlmsg_flags = NLM_F_REQUEST, > + .n.nlmsg_type = RTM_DELNEXTHOP, > + .nhm.nh_family = AF_UNSPEC, > + }; > + > + req.n.nlmsg_seq = ++rth_del.seq; > + > + addattr32(&req.n, sizeof(req), NHA_ID, id); > + > + if (rtnl_talk(&rth_del, &req.n, NULL) < 0) > + return -1; > + return 0; > +} > + > +static int flush_nexthop(struct nlmsghdr *nlh, void *arg) > +{ > + struct nhmsg *nhm = NLMSG_DATA(nlh); > + struct rtattr *tb[NHA_MAX+1]; > + __u32 id = 0; > + int len; > + > + len = nlh->nlmsg_len - NLMSG_SPACE(sizeof(*nhm)); > + if (len < 0) { > + fprintf(stderr, "BUG: wrong nlmsg len %d\n", len); > + return -1; > + } > + > + parse_rtattr(tb, NHA_MAX, RTM_NHA(nhm), len); > + if (tb[NHA_ID]) > + id = rta_getattr_u32(tb[NHA_ID]); > + > + if (id && !delete_nexthop(id)) > + filter.flushed++; > + > + return 0; > +} > + > +static int ipnh_flush(unsigned int all) > +{ > + int rc = -2; > + > + if (all) { > + filter.groups = 1; > + filter.ifindex = 0; > + filter.master = 0; > + } > + > + if (rtnl_open(&rth_del, 0) < 0) { > + fprintf(stderr, "Cannot open rtnetlink\n"); > + return EXIT_FAILURE; > + } > +again: > + if (rtnl_nexthopdump_req(&rth, preferred_family, nh_dump_filter) < 0) { > + perror("Cannot send dump request"); > + goto out; > + } > + > + if (rtnl_dump_filter(&rth, flush_nexthop, stdout) < 0) { > + fprintf(stderr, "Dump terminated\n"); > + goto out; > + } > + > + /* if deleting all, then remove groups first */ > + if (all && filter.groups) { > + filter.groups = 0; > + goto again; > + } > + > + rc = 0; > +out: > + rtnl_close(&rth_del); > + if (!filter.flushed) > + printf("Nothing to flush\n"); > + else > + printf("Flushed %d nexthops\n", filter.flushed); > + > + return rc; > +} > + > +static void print_nh_group(FILE *fp, const struct rtattr *grps_attr) > +{ > + struct nexthop_grp *nhg = RTA_DATA(grps_attr); > + int num = RTA_PAYLOAD(grps_attr) / sizeof(*nhg); > + int i; > + > + if (!num || num * sizeof(*nhg) != RTA_PAYLOAD(grps_attr)) { > + fprintf(fp, "<invalid nexthop group>"); > + return; > + } > + > + open_json_array(PRINT_JSON, "group"); > + print_string(PRINT_FP, NULL, "%s", "group "); > + for (i = 0; i < num; ++i) { > + open_json_object(NULL); > + > + if (i) > + print_string(PRINT_FP, NULL, "%s", "/"); > + > + print_uint(PRINT_ANY, "id", "%u", nhg[i].id); > + if (nhg[i].weight) > + print_uint(PRINT_ANY, "weight", ",%u", nhg[i].weight); > + > + close_json_object(); > + } > + close_json_array(PRINT_JSON, NULL); > +} > + > +static void print_nh_gateway(FILE *fp, const struct nhmsg *nhm, > + const struct rtattr *rta) > +{ > + const char *gateway = format_host_rta(nhm->nh_family, rta); > + > + if (is_json_context()) > + print_string(PRINT_JSON, "gateway", NULL, gateway); > + else { > + fprintf(fp, "via "); > + print_color_string(PRINT_FP, ifa_family_color(nhm->nh_family), > + NULL, "%s ", gateway); > + } > +} > + > +int print_nexthop(struct nlmsghdr *n, void *arg) > +{ > + struct nhmsg *nhm = NLMSG_DATA(n); > + struct rtattr *tb[NHA_MAX+1]; > + FILE *fp = (FILE *)arg; > + int len; > + > + SPRINT_BUF(b1); > + > + if (n->nlmsg_type != RTM_DELNEXTHOP && > + n->nlmsg_type != RTM_NEWNEXTHOP) { > + fprintf(stderr, "Not a nexthop: %08x %08x %08x\n", > + n->nlmsg_len, n->nlmsg_type, n->nlmsg_flags); > + return -1; > + } > + > + len = n->nlmsg_len - NLMSG_SPACE(sizeof(*nhm)); > + if (len < 0) { > + close_json_object(); > + fprintf(stderr, "BUG: wrong nlmsg len %d\n", len); > + return -1; > + } > + > + parse_rtattr(tb, NHA_MAX, RTM_NHA(nhm), len); > + > + open_json_object(NULL); > + > + if (n->nlmsg_type == RTM_DELROUTE) > + print_bool(PRINT_ANY, "deleted", "Deleted ", true); > + > + if (tb[NHA_ID]) > + print_uint(PRINT_ANY, "id", "id %u ", > + rta_getattr_u32(tb[NHA_ID])); > + > + if (tb[NHA_GROUP]) > + print_nh_group(fp, tb[NHA_GROUP]); > + > + if (tb[NHA_ENCAP]) > + lwt_print_encap(fp, tb[NHA_ENCAP_TYPE], tb[NHA_ENCAP]); > + > + if (tb[NHA_GATEWAY]) > + print_nh_gateway(fp, nhm, tb[NHA_GATEWAY]); > + > + if (tb[NHA_OIF]) > + print_rta_if(fp, tb[NHA_OIF], "dev"); > + > + if (nhm->nh_scope != RT_SCOPE_UNIVERSE || show_details > 0) { > + print_string(PRINT_ANY, "scope", "scope %s ", > + rtnl_rtscope_n2a(nhm->nh_scope, b1, sizeof(b1))); > + } > + > + if (tb[NHA_BLACKHOLE]) > + print_null(PRINT_ANY, "blackhole", "blackhole", NULL); > + > + if (nhm->nh_protocol != RTPROT_UNSPEC || show_details > 0) { > + print_string(PRINT_ANY, "protocol", "proto %s ", > + rtnl_rtprot_n2a(nhm->nh_protocol, b1, sizeof(b1))); > + } > + > + if (tb[NHA_OIF]) > + print_rt_flags(fp, nhm->nh_flags); > + > + print_string(PRINT_FP, NULL, "%s", "\n"); > + close_json_object(); > + fflush(fp); > + > + return 0; > +} > + > +static int add_nh_group_attr(struct nlmsghdr *n, int maxlen, char *argv) > +{ > + struct nexthop_grp *grps; > + int count = 0, i; > + char *sep, *wsep; > + > + if (*argv != '\0') > + count = 1; > + > + /* separator is '/' */ > + sep = strchr(argv, '/'); > + while (sep) { > + count++; > + sep = strchr(sep + 1, '/'); > + } > + > + if (count == 0) > + return -1; > + > + grps = calloc(count, sizeof(*grps)); > + if (!grps) > + return -1; > + > + for (i = 0; i < count; ++i) { > + sep = strchr(argv, '/'); > + if (sep) > + *sep = '\0'; > + > + wsep = strchr(argv, ','); > + if (wsep) > + *wsep = '\0'; > + > + if (get_unsigned(&grps[i].id, argv, 0)) > + return -1; > + if (wsep) { > + unsigned int tmp; > + > + wsep++; > + if (get_unsigned(&tmp, wsep, 0)) > + return -1; > + if (tmp > 254) > + return -1; > + grps[i].weight = tmp; > + } > + > + if (!sep) > + break; > + > + argv = sep + 1; > + } > + > + return addattr_l(n, maxlen, NHA_GROUP, grps, count * sizeof(*grps)); > +} > + > +static int ipnh_modify(int cmd, unsigned int flags, int argc, char **argv) > +{ > + struct { > + struct nlmsghdr n; > + struct nhmsg nhm; > + char buf[1024]; > + } req = { > + .n.nlmsg_len = NLMSG_LENGTH(sizeof(struct nhmsg)), > + .n.nlmsg_flags = NLM_F_REQUEST | flags, > + .n.nlmsg_type = cmd, > + .nhm.nh_family = preferred_family, > + }; > + __u32 nh_flags = 0; > + > + while (argc > 0) { > + if (!strcmp(*argv, "id")) { > + __u32 id; > + > + NEXT_ARG(); > + if (get_unsigned(&id, *argv, 0)) > + invarg("invalid id value", *argv); > + addattr32(&req.n, sizeof(req), NHA_ID, id); > + } else if (!strcmp(*argv, "dev")) { > + int ifindex; > + > + NEXT_ARG(); > + ifindex = ll_name_to_index(*argv); > + if (!ifindex) > + invarg("Device does not exist\n", *argv); > + addattr32(&req.n, sizeof(req), NHA_OIF, ifindex); > + if (req.nhm.nh_family == AF_UNSPEC) > + req.nhm.nh_family = AF_INET; > + } else if (strcmp(*argv, "via") == 0) { > + inet_prefix addr; > + int family; > + > + NEXT_ARG(); > + family = read_family(*argv); > + if (family == AF_UNSPEC) > + family = req.nhm.nh_family; > + else > + NEXT_ARG(); > + get_addr(&addr, *argv, family); > + if (req.nhm.nh_family == AF_UNSPEC) > + req.nhm.nh_family = addr.family; > + else if (req.nhm.nh_family != addr.family) > + invarg("address family mismatch\n", *argv); > + addattr_l(&req.n, sizeof(req), NHA_GATEWAY, > + &addr.data, addr.bytelen); > + } else if (strcmp(*argv, "encap") == 0) { > + char buf[1024]; > + struct rtattr *rta = (void *)buf; > + > + rta->rta_type = NHA_ENCAP; > + rta->rta_len = RTA_LENGTH(0); > + > + lwt_parse_encap(rta, sizeof(buf), &argc, &argv, > + NHA_ENCAP, NHA_ENCAP_TYPE); > + > + if (rta->rta_len > RTA_LENGTH(0)) { > + addraw_l(&req.n, 1024, RTA_DATA(rta), > + RTA_PAYLOAD(rta)); > + } > + } else if (!strcmp(*argv, "blackhole")) { > + addattr_l(&req.n, sizeof(req), NHA_BLACKHOLE, NULL, 0); > + if (req.nhm.nh_family == AF_UNSPEC) > + req.nhm.nh_family = AF_INET; > + } else if (!strcmp(*argv, "onlink")) { > + nh_flags |= RTNH_F_ONLINK; > + } else if (!strcmp(*argv, "group")) { > + NEXT_ARG(); > + > + if (add_nh_group_attr(&req.n, sizeof(req), *argv)) > + invarg("\"group\" value is invalid\n", *argv); > + } else if (strcmp(*argv, "help") == 0) { > + usage(); > + } else { > + invarg("", *argv); > + } > + argc--; argv++; > + } > + > + req.nhm.nh_flags = nh_flags; > + > + if (rtnl_talk(&rth, &req.n, NULL) < 0) > + return -2; > + > + return 0; > +} > + > +static int ipnh_get_id(__u32 id) > +{ > + struct { > + struct nlmsghdr n; > + struct nhmsg nhm; > + char buf[1024]; > + } req = { > + .n.nlmsg_len = NLMSG_LENGTH(sizeof(struct nhmsg)), > + .n.nlmsg_flags = NLM_F_REQUEST, > + .n.nlmsg_type = RTM_GETNEXTHOP, > + .nhm.nh_family = preferred_family, > + }; > + struct nlmsghdr *answer; > + > + addattr32(&req.n, sizeof(req), NHA_ID, id); > + > + if (rtnl_talk(&rth, &req.n, &answer) < 0) > + return -2; > + > + new_json_obj(json); > + > + if (print_nexthop(answer, (void *)stdout) < 0) { > + free(answer); > + return -1; > + } > + > + delete_json_obj(); > + fflush(stdout); > + > + free(answer); > + > + return 0; > +} > + > +static int ipnh_list_flush(int argc, char **argv, int action) > +{ > + unsigned int all = (argc == 0); > + > + while (argc > 0) { > + if (!matches(*argv, "dev")) { > + NEXT_ARG(); > + filter.ifindex = ll_name_to_index(*argv); > + if (!filter.ifindex) > + invarg("Device does not exist\n", *argv); > + } else if (!matches(*argv, "groups")) { > + filter.groups = 1; > + } else if (!matches(*argv, "master")) { > + NEXT_ARG(); > + filter.master = ll_name_to_index(*argv); > + if (!filter.master) > + invarg("Device does not exist\n", *argv); > + } else if (matches(*argv, "vrf") == 0) { > + NEXT_ARG(); > + if (!name_is_vrf(*argv)) > + invarg("Invalid VRF\n", *argv); > + filter.master = ll_name_to_index(*argv); > + if (!filter.master) > + invarg("VRF does not exist\n", *argv); > + } else if (!strcmp(*argv, "id")) { > + __u32 id; > + > + NEXT_ARG(); > + if (get_unsigned(&id, *argv, 0)) > + invarg("invalid id value", *argv); > + return ipnh_get_id(id); > + } else if (matches(*argv, "help") == 0) { > + usage(); > + } else { > + invarg("", *argv); > + } > + argc--; argv++; > + } > + > + if (action == IPNH_FLUSH) > + return ipnh_flush(all); > + > + if (rtnl_nexthopdump_req(&rth, preferred_family, nh_dump_filter) < 0) { > + perror("Cannot send dump request"); > + return -2; > + } > + > + new_json_obj(json); > + > + if (rtnl_dump_filter(&rth, print_nexthop, stdout) < 0) { > + fprintf(stderr, "Dump terminated\n"); > + return -2; > + } > + > + delete_json_obj(); > + fflush(stdout); > + > + return 0; > +} > + > +static int ipnh_get(int argc, char **argv) > +{ > + __u32 id = 0; > + > + while (argc > 0) { > + if (!strcmp(*argv, "id")) { > + NEXT_ARG(); > + if (get_unsigned(&id, *argv, 0)) > + invarg("invalid id value", *argv); > + } else { > + usage(); > + } > + argc--; argv++; > + } > + > + if (!id) { > + usage(); > + return -1; > + } > + > + return ipnh_get_id(id); > +} > + > +int do_ipnh(int argc, char **argv) > +{ > + if (argc < 1) > + return ipnh_list_flush(0, NULL, IPNH_LIST); > + > + if (!matches(*argv, "add")) > + return ipnh_modify(RTM_NEWNEXTHOP, NLM_F_CREATE|NLM_F_EXCL, > + argc-1, argv+1); > + if (!matches(*argv, "replace")) > + return ipnh_modify(RTM_NEWNEXTHOP, NLM_F_CREATE|NLM_F_REPLACE, > + argc-1, argv+1); > + if (!matches(*argv, "delete")) > + return ipnh_modify(RTM_DELNEXTHOP, 0, argc-1, argv+1); > + > + if (!matches(*argv, "list") || > + !matches(*argv, "show") || > + !matches(*argv, "lst")) > + return ipnh_list_flush(argc-1, argv+1, IPNH_LIST); > + > + if (!matches(*argv, "get")) > + return ipnh_get(argc-1, argv+1); > + > + if (!matches(*argv, "flush")) > + return ipnh_list_flush(argc-1, argv+1, IPNH_FLUSH); > + > + if (!matches(*argv, "help")) > + usage(); > + > + fprintf(stderr, > + "Command \"%s\" is unknown, try \"ip nexthop help\".\n", *argv); > + exit(-1); > +} > -- > 2.11.0 > ^ permalink raw reply [flat|nested] 23+ messages in thread
* [PATCH iproute2-next 8/9] ip route: Add option to use nexthop objects 2019-05-30 3:17 [PATCH iproute2-next 0/9] ip: Add support for nexthop objects David Ahern ` (6 preceding siblings ...) 2019-05-30 3:17 ` [PATCH iproute2-next 7/9] Add support for nexthop objects David Ahern @ 2019-05-30 3:17 ` David Ahern 2019-05-30 19:59 ` Roopa Prabhu 2019-05-30 3:17 ` [PATCH iproute2-next 9/9] ipmonitor: Add nexthop option to monitor David Ahern 8 siblings, 1 reply; 23+ messages in thread From: David Ahern @ 2019-05-30 3:17 UTC (permalink / raw) To: stephen; +Cc: netdev, David Ahern From: David Ahern <dsahern@gmail.com> Add nhid option for routes to use nexthop objects by id. Signed-off-by: David Ahern <dsahern@gmail.com> --- ip/iproute.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/ip/iproute.c b/ip/iproute.c index c5a473704d95..68f7f75f2336 100644 --- a/ip/iproute.c +++ b/ip/iproute.c @@ -80,7 +80,7 @@ static void usage(void) " [ table TABLE_ID ] [ proto RTPROTO ]\n" " [ scope SCOPE ] [ metric METRIC ]\n" " [ ttl-propagate { enabled | disabled } ]\n" - "INFO_SPEC := NH OPTIONS FLAGS [ nexthop NH ]...\n" + "INFO_SPEC := { NH | nhid ID } OPTIONS FLAGS [ nexthop NH ]...\n" "NH := [ encap ENCAPTYPE ENCAPHDR ] [ via [ FAMILY ] ADDRESS ]\n" " [ dev STRING ] [ weight NUMBER ] NHFLAGS\n" "FAMILY := [ inet | inet6 | mpls | bridge | link ]\n" @@ -809,6 +809,10 @@ int print_route(struct nlmsghdr *n, void *arg) print_string(PRINT_ANY, "src", "from %s ", b1); } + if (tb[RTA_NH_ID]) + print_uint(PRINT_ANY, "nhid", "nhid %u ", + rta_getattr_u32(tb[RTA_NH_ID])); + if (tb[RTA_NEWDST]) print_rta_newdst(fp, r, tb[RTA_NEWDST]); @@ -1080,6 +1084,7 @@ static int iproute_modify(int cmd, unsigned int flags, int argc, char **argv) int table_ok = 0; int raw = 0; int type_ok = 0; + __u32 nhid = 0; if (cmd != RTM_DELROUTE) { req.r.rtm_protocol = RTPROT_BOOT; @@ -1358,6 +1363,11 @@ static int iproute_modify(int cmd, unsigned int flags, int argc, char **argv) } else if (strcmp(*argv, "nexthop") == 0) { nhs_ok = 1; break; + } else if (!strcmp(*argv, "nhid")) { + NEXT_ARG(); + if (get_u32(&nhid, *argv, 0)) + invarg("\"id\" value is invalid\n", *argv); + addattr32(&req.n, sizeof(req), RTA_NH_ID, nhid); } else if (matches(*argv, "protocol") == 0) { __u32 prot; @@ -1520,7 +1530,7 @@ static int iproute_modify(int cmd, unsigned int flags, int argc, char **argv) req.r.rtm_type == RTN_UNSPEC) { if (cmd == RTM_DELROUTE) req.r.rtm_scope = RT_SCOPE_NOWHERE; - else if (!gw_ok && !nhs_ok) + else if (!gw_ok && !nhs_ok && !nhid) req.r.rtm_scope = RT_SCOPE_LINK; } } -- 2.11.0 ^ permalink raw reply related [flat|nested] 23+ messages in thread
* Re: [PATCH iproute2-next 8/9] ip route: Add option to use nexthop objects 2019-05-30 3:17 ` [PATCH iproute2-next 8/9] ip route: Add option to use " David Ahern @ 2019-05-30 19:59 ` Roopa Prabhu 0 siblings, 0 replies; 23+ messages in thread From: Roopa Prabhu @ 2019-05-30 19:59 UTC (permalink / raw) To: David Ahern; +Cc: Stephen Hemminger, netdev, David Ahern On Wed, May 29, 2019 at 9:04 PM David Ahern <dsahern@kernel.org> wrote: > > From: David Ahern <dsahern@gmail.com> > > Add nhid option for routes to use nexthop objects by id. > > Signed-off-by: David Ahern <dsahern@gmail.com> > --- man page needs an update ? :) > ip/iproute.c | 14 ++++++++++++-- > 1 file changed, 12 insertions(+), 2 deletions(-) > > diff --git a/ip/iproute.c b/ip/iproute.c > index c5a473704d95..68f7f75f2336 100644 > --- a/ip/iproute.c > +++ b/ip/iproute.c > @@ -80,7 +80,7 @@ static void usage(void) > " [ table TABLE_ID ] [ proto RTPROTO ]\n" > " [ scope SCOPE ] [ metric METRIC ]\n" > " [ ttl-propagate { enabled | disabled } ]\n" > - "INFO_SPEC := NH OPTIONS FLAGS [ nexthop NH ]...\n" > + "INFO_SPEC := { NH | nhid ID } OPTIONS FLAGS [ nexthop NH ]...\n" > "NH := [ encap ENCAPTYPE ENCAPHDR ] [ via [ FAMILY ] ADDRESS ]\n" > " [ dev STRING ] [ weight NUMBER ] NHFLAGS\n" > "FAMILY := [ inet | inet6 | mpls | bridge | link ]\n" > @@ -809,6 +809,10 @@ int print_route(struct nlmsghdr *n, void *arg) > print_string(PRINT_ANY, "src", "from %s ", b1); > } > > + if (tb[RTA_NH_ID]) > + print_uint(PRINT_ANY, "nhid", "nhid %u ", > + rta_getattr_u32(tb[RTA_NH_ID])); > + > if (tb[RTA_NEWDST]) > print_rta_newdst(fp, r, tb[RTA_NEWDST]); > > @@ -1080,6 +1084,7 @@ static int iproute_modify(int cmd, unsigned int flags, int argc, char **argv) > int table_ok = 0; > int raw = 0; > int type_ok = 0; > + __u32 nhid = 0; > > if (cmd != RTM_DELROUTE) { > req.r.rtm_protocol = RTPROT_BOOT; > @@ -1358,6 +1363,11 @@ static int iproute_modify(int cmd, unsigned int flags, int argc, char **argv) > } else if (strcmp(*argv, "nexthop") == 0) { > nhs_ok = 1; > break; > + } else if (!strcmp(*argv, "nhid")) { > + NEXT_ARG(); > + if (get_u32(&nhid, *argv, 0)) > + invarg("\"id\" value is invalid\n", *argv); > + addattr32(&req.n, sizeof(req), RTA_NH_ID, nhid); > } else if (matches(*argv, "protocol") == 0) { > __u32 prot; > > @@ -1520,7 +1530,7 @@ static int iproute_modify(int cmd, unsigned int flags, int argc, char **argv) > req.r.rtm_type == RTN_UNSPEC) { > if (cmd == RTM_DELROUTE) > req.r.rtm_scope = RT_SCOPE_NOWHERE; > - else if (!gw_ok && !nhs_ok) > + else if (!gw_ok && !nhs_ok && !nhid) > req.r.rtm_scope = RT_SCOPE_LINK; > } > } > -- > 2.11.0 > ^ permalink raw reply [flat|nested] 23+ messages in thread
* [PATCH iproute2-next 9/9] ipmonitor: Add nexthop option to monitor 2019-05-30 3:17 [PATCH iproute2-next 0/9] ip: Add support for nexthop objects David Ahern ` (7 preceding siblings ...) 2019-05-30 3:17 ` [PATCH iproute2-next 8/9] ip route: Add option to use " David Ahern @ 2019-05-30 3:17 ` David Ahern 8 siblings, 0 replies; 23+ messages in thread From: David Ahern @ 2019-05-30 3:17 UTC (permalink / raw) To: stephen; +Cc: netdev, David Ahern From: David Ahern <dsa@cumulusnetworks.com> Add capability to ip-monitor to listen and dump nexthop messages Signed-off-by: David Ahern <dsa@cumulusnetworks.com> --- ip/ipmonitor.c | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/ip/ipmonitor.c b/ip/ipmonitor.c index 9ecc7fd2011a..5da1256450b3 100644 --- a/ip/ipmonitor.c +++ b/ip/ipmonitor.c @@ -84,6 +84,12 @@ static int accept_msg(struct rtnl_ctrl_data *ctrl, } } + case RTM_NEWNEXTHOP: + case RTM_DELNEXTHOP: + print_headers(fp, "[NEXTHOP]", ctrl); + print_nexthop(n, arg); + return 0; + case RTM_NEWLINK: case RTM_DELLINK: ll_remember_index(n, NULL); @@ -173,6 +179,7 @@ int do_ipmonitor(int argc, char **argv) int lrule = 0; int lnsid = 0; int ifindex = 0; + int lnexthop = 1; groups |= nl_mgrp(RTNLGRP_LINK); groups |= nl_mgrp(RTNLGRP_IPV4_IFADDR); @@ -202,30 +209,42 @@ int do_ipmonitor(int argc, char **argv) } else if (matches(*argv, "link") == 0) { llink = 1; groups = 0; + lnexthop = 0; } else if (matches(*argv, "address") == 0) { laddr = 1; groups = 0; + lnexthop = 0; } else if (matches(*argv, "route") == 0) { lroute = 1; groups = 0; + lnexthop = 0; } else if (matches(*argv, "mroute") == 0) { lmroute = 1; groups = 0; + lnexthop = 0; } else if (matches(*argv, "prefix") == 0) { lprefix = 1; groups = 0; + lnexthop = 0; } else if (matches(*argv, "neigh") == 0) { lneigh = 1; groups = 0; + lnexthop = 0; } else if (matches(*argv, "netconf") == 0) { lnetconf = 1; groups = 0; + lnexthop = 0; } else if (matches(*argv, "rule") == 0) { lrule = 1; groups = 0; + lnexthop = 0; } else if (matches(*argv, "nsid") == 0) { lnsid = 1; groups = 0; + lnexthop = 0; + } else if (matches(*argv, "nexthop") == 0) { + groups = 0; + lnexthop = 1; } else if (strcmp(*argv, "all") == 0) { prefix_banner = 1; } else if (matches(*argv, "all-nsid") == 0) { @@ -313,6 +332,11 @@ int do_ipmonitor(int argc, char **argv) if (rtnl_open(&rth, groups) < 0) exit(1); + if (lnexthop && rtnl_add_nl_group(&rth, RTNLGRP_NEXTHOP) < 0) { + fprintf(stderr, "Failed to add nexthop group to list\n"); + exit(1); + } + if (listen_all_nsid && rtnl_listen_all_nsid(&rth) < 0) exit(1); -- 2.11.0 ^ permalink raw reply related [flat|nested] 23+ messages in thread
end of thread, other threads:[~2019-05-31 6:36 UTC | newest] Thread overview: 23+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2019-05-30 3:17 [PATCH iproute2-next 0/9] ip: Add support for nexthop objects David Ahern 2019-05-30 3:17 ` [PATCH iproute2-next 1/9] libnetlink: Set NLA_F_NESTED in rta_nest David Ahern 2019-05-30 17:43 ` Stephen Hemminger 2019-05-30 17:50 ` David Ahern 2019-05-31 6:35 ` Michal Kubecek 2019-05-30 3:17 ` [PATCH iproute2-next 2/9] lwtunnel: Pass encap and encap_type attributes to lwt_parse_encap David Ahern 2019-05-30 3:17 ` [PATCH iproute2-next 3/9] libnetlink: Add helper to add a group via setsockopt David Ahern 2019-05-30 3:17 ` [PATCH iproute2-next 4/9] uapi: Import nexthop object API David Ahern 2019-05-30 3:17 ` [PATCH iproute2-next 5/9] libnetlink: Add helper to create nexthop dump request David Ahern 2019-05-30 19:56 ` Roopa Prabhu 2019-05-30 19:59 ` David Ahern 2019-05-30 3:17 ` [PATCH iproute2-next 6/9] ip route: Export print_rt_flags and print_rta_if David Ahern 2019-05-30 3:17 ` [PATCH iproute2-next 7/9] Add support for nexthop objects David Ahern 2019-05-30 17:45 ` Stephen Hemminger 2019-05-30 17:52 ` David Ahern 2019-05-30 17:45 ` Stephen Hemminger 2019-05-30 17:52 ` David Ahern 2019-05-30 17:52 ` Stephen Hemminger 2019-05-30 17:55 ` David Ahern 2019-05-30 20:01 ` Roopa Prabhu 2019-05-30 3:17 ` [PATCH iproute2-next 8/9] ip route: Add option to use " David Ahern 2019-05-30 19:59 ` Roopa Prabhu 2019-05-30 3:17 ` [PATCH iproute2-next 9/9] ipmonitor: Add nexthop option to monitor David Ahern
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox; as well as URLs for NNTP newsgroup(s).