On Fri, 31 Aug 2018 17:49:54 -0700 dsah...@kernel.org wrote: > From: David Ahern <dsah...@gmail.com> > > Signed-off-by: David Ahern <dsah...@gmail.com> > --- > include/uapi/linux/nexthop.h | 56 ++++ > include/uapi/linux/rtnetlink.h | 8 + > ip/Makefile | 3 +- > ip/ip.c | 3 +- > ip/ip_common.h | 7 +- > ip/ipmonitor.c | 6 + > ip/ipnexthop.c | 652 > +++++++++++++++++++++++++++++++++++++++++ > ip/iproute.c | 19 +- > 8 files changed, 747 insertions(+), 7 deletions(-) > create mode 100644 include/uapi/linux/nexthop.h > create mode 100644 ip/ipnexthop.c > > diff --git a/include/uapi/linux/nexthop.h b/include/uapi/linux/nexthop.h > new file mode 100644 > index 000000000000..335182e8229a > --- /dev/null > +++ b/include/uapi/linux/nexthop.h > @@ -0,0 +1,56 @@ > +#ifndef __LINUX_NEXTHOP_H > +#define __LINUX_NEXTHOP_H > + > +#include <linux/types.h> > + > +struct nhmsg { > + unsigned char nh_family; > + unsigned char nh_scope; /* one of RT_SCOPE */ > + unsigned char nh_protocol; /* Routing protocol that installed nh */ > + unsigned char resvd; > + unsigned int nh_flags; /* RTNH_F flags */ > +};
Why not use __u8 and __u32 for these? > +struct nexthop_grp { > + __u32 id; > + __u32 weight; > +}; > + > +enum { > + NEXTHOP_GRP_TYPE_MPATH, /* default type if not specified */ > + __NEXTHOP_GRP_TYPE_MAX, > +}; > + > +#define NEXTHOP_GRP_TYPE_MAX (__NEXTHOP_GRP_TYPE_MAX - 1) > + > + > +/* NHA_ID 32-bit id for nexthop. id must be greater than 0. > + * id == 0 means assign an unused id. > + */ Don't use dave's preferred comment style in this file. The reset of the file uses standard comments. ... > diff --git a/ip/ip_common.h b/ip/ip_common.h > index 200be5e23dd1..2971c1586c4e 100644 > --- a/ip/ip_common.h > +++ b/ip/ip_common.h > @@ -56,6 +56,8 @@ int print_rule(const struct sockaddr_nl *who, > int print_netconf(const struct sockaddr_nl *who, > struct rtnl_ctrl_data *ctrl, > struct nlmsghdr *n, void *arg); > +int print_nexthop(const struct sockaddr_nl *who, > + struct nlmsghdr *n, void *arg); > void netns_map_init(void); > void netns_nsid_socket_init(void); > int print_nsid(const struct sockaddr_nl *who, > @@ -90,6 +92,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(unsigned int flags, char *name, __u32 filt_mask); > int iplink_ifla_xstats(int argc, char **argv); > @@ -165,5 +168,7 @@ int name_is_vrf(const char *name); > #endif > > void print_num(FILE *fp, unsigned int width, uint64_t count); > - > +void print_rta_flow(FILE *fp, const struct rtattr *rta); > +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/ipmonitor.c b/ip/ipmonitor.c > index a93b62cd6624..de129626683b 100644 > --- a/ip/ipmonitor.c > +++ b/ip/ipmonitor.c > @@ -84,6 +84,12 @@ static int accept_msg(const struct sockaddr_nl *who, > } > } > > + case RTM_NEWNEXTHOP: > + case RTM_DELNEXTHOP: > + print_headers(fp, "[NEXTHOP]", ctrl); > + print_nexthop(who, n, arg); > + return 0; > + > case RTM_NEWLINK: > case RTM_DELLINK: > ll_remember_index(who, n, NULL); > diff --git a/ip/ipnexthop.c b/ip/ipnexthop.c > new file mode 100644 > index 000000000000..9fa4b7292426 > --- /dev/null > +++ b/ip/ipnexthop.c > @@ -0,0 +1,652 @@ > +/* > + * ip nexthop > + * > + * Copyright (C) 2017 Cumulus Networks > + * Copyright (c) 2017 David Ahern <d...@cumulusnetworks.com> > + * > + * This program is free software; you can redistribute it and/or modify > + * it under the terms of the GNU General Public License as published by > + * the Free Software Foundation; either version 2 of the License, or > + * (at your option) any later version. > + * > + * This program is distributed in the hope that it will be useful, > + * but WITHOUT ANY WARRANTY; without even the implied warranty of > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > + * GNU General Public License for more details. > + */ > Please use SPDX and not GPL boilerplate in new files. > +#include <stdio.h> > +#include <stdlib.h> > +#include <unistd.h> > +#include <string.h> > +#include <sys/socket.h> > +#include <netinet/in.h> > +#include <netinet/ip.h> > +#include <errno.h> > +#include <linux/nexthop.h> > +#include <libmnl/libmnl.h> Is this code really using libmnl? > +#include <rt_names.h> > + > +#include "utils.h" > +#include "ip_common.h" > + > +static struct > +{ > + unsigned int flushed; > + unsigned int groups; > + 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"); > + fprintf(stderr, " ip nexthop get [ id ID ]\n"); > + fprintf(stderr, " ip nexthop { add | del | change | replace } > NH\n"); > + fprintf(stderr, "SELECTOR := [ id ID ] [ dev DEV ] [ table TABLE ] [ > vrf NAME ]\n"); > + fprintf(stderr, "NH := [ encap ENCAPTYPE ENCAPHDR ] [ via [ FAMILY ] > ADDRESS ]\n"); > + fprintf(stderr, " [ id ID ] [ dev STRING ] [ weight NUMBER ]\n"); > + fprintf(stderr, " [ table TABLE ] [ vrf VRF ] NHFLAGS\n"); > + fprintf(stderr, "NHFLAGS := [ onlink | pervasive ]\n"); > + fprintf(stderr, "ENCAPTYPE := [ mpls | ip | ip6 ]\n"); > + fprintf(stderr, "ENCAPHDR := [ MPLSLABEL ]\n"); > + exit(-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.seq; > + > + addattr32(&req.n, sizeof(req), NHA_ID, id); > + > + if (rtnl_talk(&rth, &req.n, NULL) < 0) > + return -1; > + > + filter.flushed++; > + return 0; > +} > + > +struct nh_entry { > + __u32 id; > + unsigned int group; > + struct nh_entry *next; > +}; > + > +struct nh_entry *first, *last; > + > +static int flush_nexthop(const struct sockaddr_nl *who, > + struct nlmsghdr *nlh, void *arg) > +{ > + struct nhmsg *nhm = NLMSG_DATA(nlh); > + struct rtattr *tb[NHA_MAX+1]; > + struct nh_entry *nh; > + __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) > + return 0; > + > + nh = malloc(sizeof(*nh)); > + if (!nh) > + return -1; > + > + nh->id = id; > + nh->group = tb[NHA_GROUP] != NULL; > + nh->next = NULL; > + if (!first) > + first = nh; > + else > + last->next = nh; > + > + last = nh; > + return 0; > +} > + > +static int ipnh_flush(void *req, __u32 len, unsigned int all) > +{ > + struct nh_entry *nh; > + > + if (send(rth.fd, req, len, 0) < 0) { > + perror("Cannot send dump request"); > + return -2; > + } > + > + if (rtnl_dump_filter(&rth, flush_nexthop, stdout) < 0) { > + fprintf(stderr, "Dump terminated\n"); > + return -2; > + } > + > + /* if deleting all, then remove groups first */ > + if (all) { > + nh = first; > + while (nh) { > + if (nh->group) > + delete_nexthop(nh->id); > + nh = nh->next; > + } > + } > + > + nh = first; > + while (nh) { > + if (!all || !nh->group) > + delete_nexthop(nh->id); > + nh = nh->next; > + } > + > + if (!filter.flushed) > + printf("Nothing to flush\n"); > + else > + printf("Flushed %d nexthops\n", filter.flushed); > + > + return 0; > +} > + > +static char *nh_group_type_to_str(__u16 group_type, char *buf, size_t len) > +{ > + static const char *typestr[NEXTHOP_GRP_TYPE_MAX + 1] = { > + "multipath", /* NEXTHOP_GRP_TYPE_MPATH */ > + }; > + > + if (group_type < ARRAY_SIZE(typestr)) > + snprintf(buf, len-1, "%s", typestr[group_type]); > + else > + snprintf(buf, len-1, "<%u>", group_type); > + > + buf[len-1] = '\0'; > + > + return buf; > +} > + > +static void print_nh_group(FILE *fp, const struct rtattr *grps_attr, > + const struct rtattr *gtype) > +{ > + struct nexthop_grp *nhg = RTA_DATA(grps_attr); > + int num = RTA_PAYLOAD(grps_attr) / sizeof(*nhg); > + __u16 group_type = NEXTHOP_GRP_TYPE_MPATH; > + int i; > + > + SPRINT_BUF(b1); > + > + if (!num || num * sizeof(*nhg) != RTA_PAYLOAD(grps_attr)) { > + fprintf(fp, "<invalid nexthop group>"); > + return; > + } > + > + if (gtype) > + group_type = rta_getattr_u16(gtype); > + > + if (is_json_context()) { > + open_json_array(PRINT_JSON, "group"); > + for (i = 0; i < num; ++i) { > + open_json_object(NULL); > + print_uint(PRINT_ANY, "id", "id %u ", nhg[i].id); > + print_uint(PRINT_ANY, "weight", "weight %u ", > nhg[i].weight); > + close_json_object(); > + } > + close_json_array(PRINT_JSON, NULL); > + print_string(PRINT_ANY, "type", "type %s ", > + nh_group_type_to_str(group_type, b1, sizeof(b1))); > + } else { > + fprintf(fp, "group "); > + for (i = 0; i < num; ++i) { > + if (i) > + fprintf(fp, "/"); > + fprintf(fp, "%u", nhg[i].id); > + if (num > 1 && nhg[i].weight > 1) > + fprintf(fp, ",%u", nhg[i].weight); > + } > + } > +} I think this could be done by using json_print cleverly rather than having to use is_json_contex(). That would avoid repeating code. You are only decoding group type in the json version, why not both?