From: dsahern@kernel.org
To: netdev@vger.kernel.org
Cc: roopa@cumulusnetworks.com, sharpd@cumulusnetworks.com,
idosch@mellanox.com, davem@davemloft.net,
David Ahern <dsahern@gmail.com>
Subject: [PATCH iproute2-next] ip: Add support for nexthop objects
Date: Fri, 31 Aug 2018 17:49:54 -0700 [thread overview]
Message-ID: <20180901004954.7145-20-dsahern@kernel.org> (raw)
In-Reply-To: <20180901004954.7145-1-dsahern@kernel.org>
From: David Ahern <dsahern@gmail.com>
Signed-off-by: David Ahern <dsahern@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 */
+};
+
+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.
+ */
+enum {
+ NHA_UNSPEC,
+ NHA_ID, /* u32 */
+ NHA_GROUP, /* array of nexthop_grp */
+ NHA_GROUP_TYPE, /* u16 one of NEXTHOP_GRP_TYPE;
+ * default is NEXTHOP_GRP_TYPE_MPATH */
+
+ /* if NHA_GROUP attribute is added, no other attributes can be set */
+
+ NHA_BLACKHOLE, /* flag; nexthop used to blackhole packets */
+ NHA_OIF, /* u32 */
+ NHA_FLOW, /* u32 */
+
+ NHA_TABLE_ID, /* u32 - table id to validate gateway */
+ NHA_GATEWAY, /* be32 (IPv4) or in6_addr (IPv6) gw address */
+
+ /* Dump control attributes */
+ NHA_GROUPS, /* flag; only return nexthop groups in dump */
+ NHA_MASTER, /* u32; only return nexthops with given master dev */
+
+ NHA_SADDR, /* return only: IPv4 or IPv6 source address */
+
+ __NHA_MAX,
+};
+
+#define NHA_MAX (__NHA_MAX - 1)
+#endif
diff --git a/include/uapi/linux/rtnetlink.h b/include/uapi/linux/rtnetlink.h
index 8c1d600bfa33..158114245b6c 100644
--- a/include/uapi/linux/rtnetlink.h
+++ b/include/uapi/linux/rtnetlink.h
@@ -157,6 +157,13 @@ enum {
RTM_GETCHAIN,
#define RTM_GETCHAIN RTM_GETCHAIN
+ RTM_NEWNEXTHOP = 104,
+#define RTM_NEWNEXTHOP RTM_NEWNEXTHOP
+ RTM_DELNEXTHOP,
+#define RTM_DELNEXTHOP RTM_DELNEXTHOP
+ RTM_GETNEXTHOP,
+#define RTM_GETNEXTHOP RTM_GETNEXTHOP
+
__RTM_MAX,
#define RTM_MAX (((__RTM_MAX + 3) & ~3) - 1)
};
@@ -342,6 +349,7 @@ enum rtattr_type_t {
RTA_IP_PROTO,
RTA_SPORT,
RTA_DPORT,
+ RTA_NH_ID,
__RTA_MAX
};
diff --git a/ip/Makefile b/ip/Makefile
index a88f93665ee6..7df818dbe23a 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 58c643df8a36..963ef140c7c4 100644
--- a/ip/ip.c
+++ b/ip/ip.c
@@ -51,7 +51,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 | ipx | dnet | mpls | bridge | link } |\n"
@@ -101,6 +101,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 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 <dsa@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.
+ */
+
+#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>
+#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);
+ }
+ }
+}
+
+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(const struct sockaddr_nl *who, 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], tb[NHA_GROUP_TYPE]);
+
+ if (tb[NHA_GATEWAY])
+ print_nh_gateway(fp, nhm, tb[NHA_GATEWAY]);
+
+ if (tb[NHA_SADDR]) {
+ const char *psrc;
+
+ psrc = rt_addr_n2a_rta(nhm->nh_family, tb[NHA_SADDR]);
+ if (is_json_context())
+ print_string(PRINT_JSON, "src", NULL, psrc);
+ else {
+ fprintf(fp, "src ");
+ print_color_string(PRINT_FP,
+ ifa_family_color(nhm->nh_family),
+ NULL, "%s ", psrc);
+ }
+ }
+
+ if (tb[NHA_OIF])
+ print_rta_if(fp, tb[NHA_OIF], "dev");
+
+ 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 (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_OIF])
+ print_rt_flags(fp, nhm->nh_flags);
+
+ if (tb[NHA_FLOW])
+ print_rta_flow(fp, tb[NHA_FLOW]);
+
+ 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) {
+ wsep++;
+ if (get_unsigned(&grps[i].weight, wsep, 0))
+ return -1;
+ }
+
+ 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 = AF_UNSPEC,
+ };
+ __u32 nh_flags = 0;
+ __u16 gtype = NEXTHOP_GRP_TYPE_MAX + 1;
+
+ 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);
+ } 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, "blackhole")) {
+ addattr_l(&req.n, sizeof(req), NHA_BLACKHOLE, NULL, 0);
+ } else if (!strcmp(*argv, "onlink")) {
+ nh_flags |= RTNH_F_ONLINK;
+ } else if (!strcmp(*argv, "realms")) {
+ __u32 realm;
+
+ NEXT_ARG();
+ if (get_rt_realms_or_raw(&realm, *argv))
+ invarg("\"realm\" value is invalid\n", *argv);
+ addattr32(&req.n, sizeof(req), NHA_FLOW, realm);
+ } 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, "multipath") ||
+ !strcmp(*argv, "mpath")) {
+ gtype = NEXTHOP_GRP_TYPE_MPATH;
+ } else if (!strcmp(*argv, "table")) {
+ __u32 tb_id;
+
+ NEXT_ARG();
+ if (get_unsigned(&tb_id, *argv, 0))
+ invarg("invalid table value", *argv);
+ addattr32(&req.n, sizeof(req), NHA_TABLE_ID, tb_id);
+ } else if (strcmp(*argv, "vrf") == 0) {
+ __u32 tb_id;
+
+ NEXT_ARG();
+ tb_id = ipvrf_get_table(*argv);
+ if (tb_id == 0)
+ invarg("Invalid VRF", *argv);
+ addattr32(&req.n, sizeof(req), NHA_TABLE_ID, tb_id);
+ } else if (strcmp(*argv, "help") == 0) {
+ usage();
+ } else {
+ invarg("", *argv);
+ }
+ argc--; argv++;
+ }
+
+ if (gtype <= NEXTHOP_GRP_TYPE_MAX)
+ addattr16(&req.n, sizeof(req), NHA_GROUP_TYPE, gtype);
+
+ 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(NULL, 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)
+{
+ struct {
+ struct nlmsghdr n;
+ struct nhmsg nhm;
+ char buf[256];
+ } req = {
+ .n.nlmsg_len = NLMSG_LENGTH(sizeof(struct nhmsg)),
+ .n.nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST,
+ .n.nlmsg_type = RTM_GETNEXTHOP,
+ .n.nlmsg_seq = ++rth.seq,
+ .nhm.nh_family = preferred_family,
+ };
+ unsigned int master = 0;
+ unsigned int all = (argc == 0);
+
+ rth.dump = req.n.nlmsg_seq;
+
+ while (argc > 0) {
+ if (!matches(*argv, "dev")) {
+ unsigned 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);
+ } else if (!matches(*argv, "groups")) {
+ addattr_l(&req.n, sizeof(req), NHA_GROUPS, NULL, 0);
+ } else if (!matches(*argv, "master")) {
+ NEXT_ARG();
+ master = ll_name_to_index(*argv);
+ if (!master)
+ invarg("Device does not exist\n", *argv);
+ } else if (matches(*argv, "vrf") == 0) {
+ NEXT_ARG();
+ master = ll_name_to_index(*argv);
+ if (!master)
+ invarg("VRF does not exist\n", *argv);
+ if (!name_is_vrf(*argv))
+ invarg("Invalid VRF\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 (master)
+ addattr32(&req.n, sizeof(req), NHA_MASTER, master);
+
+ if (action == IPNH_FLUSH)
+ return ipnh_flush(&req, req.n.nlmsg_len, all);
+
+ if (send(rth.fd, &req, req.n.nlmsg_len, 0) < 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);
+ }
+ if (matches(*argv, "help") == 0)
+ 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, "change") || !strcmp(*argv, "chg"))
+ return ipnh_modify(RTM_NEWNEXTHOP, NLM_F_REPLACE,
+ 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);
+}
diff --git a/ip/iproute.c b/ip/iproute.c
index 30833414a3f7..0af72c2eccca 100644
--- a/ip/iproute.c
+++ b/ip/iproute.c
@@ -349,7 +349,7 @@ static void print_rtax_features(FILE *fp, unsigned int features)
"features", "0x%x ", 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,8 @@ 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));
@@ -492,7 +492,7 @@ static void print_rta_cacheinfo(FILE *fp, const struct rta_cacheinfo *ci)
}
}
-static void print_rta_flow(FILE *fp, const struct rtattr *rta)
+void print_rta_flow(FILE *fp, const struct rtattr *rta)
{
__u32 to = rta_getattr_u32(rta);
__u32 from = to >> 16;
@@ -823,6 +823,10 @@ int print_route(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg)
rtnl_dsfield_n2a(r->rtm_tos, b1, sizeof(b1)));
}
+ if (tb[RTA_NH_ID])
+ print_uint(PRINT_ANY, "nhid", "nhid %u ",
+ rta_getattr_u32(tb[RTA_NH_ID]));
+
if (tb[RTA_GATEWAY] && filter.rvia.bitlen != host_len)
print_rta_gateway(fp, r, tb[RTA_GATEWAY]);
@@ -1351,6 +1355,13 @@ 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")) {
+ __u32 id;
+
+ NEXT_ARG();
+ if (get_u32(&id, *argv, 0))
+ invarg("\"id\" value is invalid\n", *argv);
+ addattr32(&req.n, sizeof(req), RTA_NH_ID, id);
} else if (matches(*argv, "protocol") == 0) {
__u32 prot;
--
2.11.0
next prev parent reply other threads:[~2018-09-01 5:20 UTC|newest]
Thread overview: 28+ messages / expand[flat|nested] mbox.gz Atom feed top
2018-09-01 0:49 [PATCH RFC net-next 00/18] net: Improve route scalability via support for nexthop objects dsahern
2018-09-01 0:49 ` [PATCH RFC net-next 01/18] net: Rename net/nexthop.h net/rtnh.h dsahern
2018-09-01 0:49 ` [PATCH RFC net-next 02/18] net: ipv4: export fib_good_nh and fib_flush dsahern
2018-09-01 0:49 ` [PATCH RFC net-next 03/18] net/ipv4: export fib_info_update_nh_saddr dsahern
2018-09-01 0:49 ` [PATCH RFC net-next 04/18] net/ipv4: export fib_check_nh dsahern
2018-09-01 0:49 ` [PATCH RFC net-next 05/18] net/ipv4: Define fib_get_nhs when CONFIG_IP_ROUTE_MULTIPATH is disabled dsahern
2018-09-01 0:49 ` [PATCH RFC net-next 06/18] net/ipv4: Create init and release helpers for fib_nh dsahern
2018-09-01 0:49 ` [PATCH RFC net-next 07/18] net: ipv4: Add fib_nh to fib_result dsahern
2018-09-01 0:49 ` [PATCH RFC net-next 08/18] net/ipv4: Move device validation to helper dsahern
2018-09-01 0:49 ` [PATCH RFC net-next 09/18] net/ipv6: Create init and release helpers for fib6_nh dsahern
2018-09-01 0:49 ` [PATCH RFC net-next 10/18] net/ipv6: Make fib6_nh optional at the end of fib6_info dsahern
2018-09-01 0:49 ` [PATCH RFC net-next 11/18] net: Initial nexthop code dsahern
2018-09-01 0:49 ` [PATCH RFC net-next 12/18] net/ipv4: Add nexthop helpers for ipv4 integration dsahern
2018-09-01 0:49 ` [PATCH RFC net-next 13/18] net/ipv4: Convert existing use of fib_info to new helpers dsahern
2018-09-01 0:49 ` [PATCH RFC net-next 14/18] net/ipv4: Allow routes to use nexthop objects dsahern
2018-09-01 0:49 ` [PATCH RFC net-next 15/18] net/ipv6: Use helpers to access fib6_nh data dsahern
2018-09-01 0:49 ` [PATCH RFC net-next 16/18] net/ipv6: Allow routes to use nexthop objects dsahern
2018-09-01 0:49 ` [PATCH RFC net-next 17/18] net: Add support for nexthop groups dsahern
2018-09-01 0:49 ` [PATCH RFC net-next 18/18] net/ipv4: Optimization for fib_info lookup dsahern
2018-09-01 20:43 ` Stephen Hemminger
2018-09-04 15:27 ` David Ahern
2018-09-01 0:49 ` dsahern [this message]
2018-09-01 20:37 ` [PATCH iproute2-next] ip: Add support for nexthop objects Stephen Hemminger
2018-09-04 15:30 ` David Ahern
2018-09-02 17:34 ` [PATCH RFC net-next 00/18] net: Improve route scalability via " David Miller
2018-09-04 15:57 ` David Ahern
2018-12-11 12:52 ` Jan Maria Matejka
2018-12-12 20:27 ` David Ahern
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20180901004954.7145-20-dsahern@kernel.org \
--to=dsahern@kernel.org \
--cc=davem@davemloft.net \
--cc=dsahern@gmail.com \
--cc=idosch@mellanox.com \
--cc=netdev@vger.kernel.org \
--cc=roopa@cumulusnetworks.com \
--cc=sharpd@cumulusnetworks.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.