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 RFC net-next 11/18] net: Initial nexthop code
Date: Fri, 31 Aug 2018 17:49:46 -0700 [thread overview]
Message-ID: <20180901004954.7145-12-dsahern@kernel.org> (raw)
In-Reply-To: <20180901004954.7145-1-dsahern@kernel.org>
From: David Ahern <dsahern@gmail.com>
Initial import of nexthop code.
- Add new RTM commands for nexthop objects.
- Add new uapi attributes for creating nexthops. Attributes are similar
to the current nexthop attributes for routes.
- Add basic helpers for ipv4 and ipv6 references to nexthop data
Similar to routes nexthops are configured per namespace, so add
netns_nexthop struct and add it to struct net.
Signed-off-by: David Ahern <dsahern@gmail.com>
---
include/net/net_namespace.h | 2 +
include/net/netns/nexthop.h | 18 +
include/net/nexthop.h | 121 +++++
include/uapi/linux/nexthop.h | 56 +++
include/uapi/linux/rtnetlink.h | 7 +
net/ipv4/Makefile | 2 +-
net/ipv4/nexthop.c | 1080 ++++++++++++++++++++++++++++++++++++++++
security/selinux/nlmsgtab.c | 5 +-
8 files changed, 1289 insertions(+), 2 deletions(-)
create mode 100644 include/net/netns/nexthop.h
create mode 100644 include/net/nexthop.h
create mode 100644 include/uapi/linux/nexthop.h
create mode 100644 net/ipv4/nexthop.c
diff --git a/include/net/net_namespace.h b/include/net/net_namespace.h
index 9b5fdc50519a..d3d678814b93 100644
--- a/include/net/net_namespace.h
+++ b/include/net/net_namespace.h
@@ -19,6 +19,7 @@
#include <net/netns/packet.h>
#include <net/netns/ipv4.h>
#include <net/netns/ipv6.h>
+#include <net/netns/nexthop.h>
#include <net/netns/ieee802154_6lowpan.h>
#include <net/netns/sctp.h>
#include <net/netns/dccp.h>
@@ -105,6 +106,7 @@ struct net {
struct netns_mib mib;
struct netns_packet packet;
struct netns_unix unx;
+ struct netns_nexthop nexthop;
struct netns_ipv4 ipv4;
#if IS_ENABLED(CONFIG_IPV6)
struct netns_ipv6 ipv6;
diff --git a/include/net/netns/nexthop.h b/include/net/netns/nexthop.h
new file mode 100644
index 000000000000..91627c35e9d3
--- /dev/null
+++ b/include/net/netns/nexthop.h
@@ -0,0 +1,18 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * nexthops in net namespaces
+ */
+
+#ifndef __NETNS_NEXTHOP_H__
+#define __NETNS_NEXTHOP_H__
+
+#include <linux/rbtree.h>
+
+struct netns_nexthop {
+ struct rb_root root; /* tree of nexthops by id */
+ struct hlist_head *devhash; /* nexthops by device */
+
+ unsigned int seq; /* protected by rtnl_mutex */
+ u32 last_id_allocated;
+};
+#endif
diff --git a/include/net/nexthop.h b/include/net/nexthop.h
new file mode 100644
index 000000000000..1c59d04d1da6
--- /dev/null
+++ b/include/net/nexthop.h
@@ -0,0 +1,121 @@
+/*
+ * Generic nexthop implementation
+ *
+ * Copyright (C) 2017-18 Cumulus Networks
+ * Copyright (c) 2017-18 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.
+ */
+
+#ifndef __LINUX_NEXTHOP_H
+#define __LINUX_NEXTHOP_H
+
+#include <linux/netdevice.h>
+#include <linux/types.h>
+#include <net/ip_fib.h>
+#include <net/ip6_fib.h>
+#include <net/netlink.h>
+
+#define NEXTHOP_VALID_USER_FLAGS RTNH_F_ONLINK
+
+struct nexthop;
+
+struct nh_info {
+ struct hlist_node dev_hash;
+ struct net *net;
+ struct nexthop *nh_parent;
+
+ u8 family;
+ u8 reject_nh:1,
+ has_gw:1,
+ unused:6;
+
+ union {
+ /* fib_nh used for device only nexthops as well */
+ struct fib_nh fib_nh;
+ struct fib6_nh fib6_nh;
+ };
+};
+
+struct nexthop {
+ struct rb_node rb_node;
+ struct list_head fi_list; /* v4 entries using nh */
+ struct list_head f6i_list; /* v6 entries using nh */
+
+ u32 id;
+
+ u8 protocol;
+ u8 nh_flags;
+
+ refcount_t refcnt;
+ struct rcu_head rcu;
+
+ union {
+ struct nh_info __rcu *nh_info;
+ };
+};
+
+struct nh_config {
+ u8 nh_family;
+ u8 nh_scope;
+ u8 nh_protocol;
+ u8 nh_blackhole;
+ u32 nh_flags;
+
+ u32 nh_id;
+ u32 tclassid;
+
+ int nh_ifindex;
+ struct net_device *dev;
+ u32 nh_table;
+ union {
+ __be32 ipv4;
+ struct in6_addr ipv6;
+ } gw;
+
+ u32 nlflags;
+ struct nl_info nlinfo;
+};
+
+void nexthop_get(struct nexthop *nh);
+void nexthop_put(struct nexthop *nh);
+
+/* caller is holding rtnl; no reference taken to nexthop */
+struct nexthop *nexthop_find_by_id(struct net *net, u32 id);
+
+static inline bool nexthop_cmp(struct nexthop *nh1, struct nexthop *nh2)
+{
+ return nh1 == nh2;
+}
+
+static inline int nexthop_num_path(struct nexthop *nh)
+{
+ return 1;
+}
+
+/* called with rcu lock */
+static inline bool nexthop_has_gw(struct nexthop *nh)
+{
+ struct nh_info *nhi;
+
+ nhi = rcu_dereference(nh->nh_info);
+ return !!nhi->has_gw;
+}
+
+/* called with rcu lock */
+static inline bool nexthop_is_blackhole(struct nexthop *nh)
+{
+ struct nh_info *nhi;
+
+ nhi = rcu_dereference(nh->nh_info);
+ return !!nhi->reject_nh;
+}
+#endif
diff --git a/include/uapi/linux/nexthop.h b/include/uapi/linux/nexthop.h
new file mode 100644
index 000000000000..f40ef188b529
--- /dev/null
+++ b/include/uapi/linux/nexthop.h
@@ -0,0 +1,56 @@
+#ifndef _UAPI_LINUX_NEXTHOP_H
+#define _UAPI_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 46399367627f..4a0615797e5e 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)
};
diff --git a/net/ipv4/Makefile b/net/ipv4/Makefile
index 7446b98661d8..2ee5129a070c 100644
--- a/net/ipv4/Makefile
+++ b/net/ipv4/Makefile
@@ -14,7 +14,7 @@ obj-y := route.o inetpeer.o protocol.o \
udp_offload.o arp.o icmp.o devinet.o af_inet.o igmp.o \
fib_frontend.o fib_semantics.o fib_trie.o fib_notifier.o \
inet_fragment.o ping.o ip_tunnel_core.o gre_offload.o \
- metrics.o netlink.o
+ metrics.o netlink.o nexthop.o
obj-$(CONFIG_BPFILTER) += bpfilter/
diff --git a/net/ipv4/nexthop.c b/net/ipv4/nexthop.c
new file mode 100644
index 000000000000..24c4aa383c9d
--- /dev/null
+++ b/net/ipv4/nexthop.c
@@ -0,0 +1,1080 @@
+/* Generic nexthop implementation
+ *
+ * Copyright (C) 2017-18 Cumulus Networks
+ * Copyright (c) 2017-18 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 <linux/inetdevice.h>
+#include <linux/nexthop.h>
+#include <linux/rtnetlink.h>
+#include <linux/slab.h>
+#include <net/nexthop.h>
+#include <net/route.h>
+#include <net/ip6_route.h>
+
+static void remove_nexthop(struct net *net, struct nexthop *nh,
+ bool skip_fib, struct nl_info *nlinfo);
+
+#define NH_DEV_HASHBITS 8
+#define NH_DEV_HASHSIZE (1U << NH_DEV_HASHBITS)
+
+static const struct nla_policy rtm_nh_policy[NHA_MAX + 1] = {
+ [NHA_ID] = { .type = NLA_U32 },
+ [NHA_OIF] = { .type = NLA_U32 },
+ [NHA_FLOW] = { .type = NLA_U32 },
+ [NHA_TABLE_ID] = { .type = NLA_U32 },
+ [NHA_BLACKHOLE] = { .type = NLA_FLAG },
+ [NHA_MASTER] = { .type = NLA_U32 },
+};
+
+static unsigned int nh_dev_hashfn(unsigned int val)
+{
+ unsigned int mask = NH_DEV_HASHSIZE - 1;
+
+ return (val ^
+ (val >> NH_DEV_HASHBITS) ^
+ (val >> (NH_DEV_HASHBITS * 2))) & mask;
+}
+
+static void nexthop_devhash_add(struct net *net, struct nh_info *nhi)
+{
+ struct hlist_head *head;
+ struct net_device *dev;
+ unsigned int hash;
+
+ if (nhi->family == AF_INET6)
+ dev = nhi->fib6_nh.nh_dev;
+ else
+ dev = nhi->fib_nh.nh_dev;
+
+ WARN_ON(!dev);
+
+ hash = nh_dev_hashfn(dev->ifindex);
+ head = &net->nexthop.devhash[hash];
+ hlist_add_head(&nhi->dev_hash, head);
+}
+
+static void nexthop_free_rcu(struct rcu_head *head)
+{
+ struct nexthop *nh = container_of(head, struct nexthop, rcu);
+ struct nh_info *nhi;
+
+ nhi = rcu_dereference_raw(nh->nh_info);
+ switch (nhi->family) {
+ case AF_INET:
+ case AF_UNSPEC:
+ fib_nh_release(nhi->net, &nhi->fib_nh);
+ break;
+ case AF_INET6:
+ fib6_nh_release(&nhi->fib6_nh);
+ break;
+ }
+ kfree(nhi);
+
+ kfree(nh);
+}
+
+static struct nexthop *nexthop_alloc(void)
+{
+ return kzalloc(sizeof(struct nexthop), GFP_KERNEL);
+}
+
+static void nh_base_seq_inc(struct net *net)
+{
+ while (++net->nexthop.seq == 0)
+ ;
+}
+
+void nexthop_put(struct nexthop *nh)
+{
+ if (refcount_dec_and_test(&nh->refcnt))
+ call_rcu(&nh->rcu, nexthop_free_rcu);
+}
+
+void nexthop_get(struct nexthop *nh)
+{
+ refcount_inc(&nh->refcnt);
+}
+
+/* no reference taken; rcu lock or rtnl must be held */
+struct nexthop *nexthop_find_by_id(struct net *net, u32 id)
+{
+ struct rb_node **pp, *parent = NULL, *next;
+
+ pp = &net->nexthop.root.rb_node;
+ while (1) {
+ struct nexthop *nh;
+
+ next = rtnl_dereference(*pp);
+ if (!next)
+ break;
+ parent = next;
+
+ nh = rb_entry(parent, struct nexthop, rb_node);
+ if (id < nh->id)
+ pp = &next->rb_left;
+ else if (id > nh->id)
+ pp = &next->rb_right;
+ else
+ return nh;
+ }
+ return NULL;
+}
+
+/* find an unused id - used for auto id allocation
+ * called with rtnl lock held
+ */
+static u32 nh_find_unused_id(struct net *net)
+{
+ u32 id_start = net->nexthop.last_id_allocated;
+
+ while (1) {
+ net->nexthop.last_id_allocated++;
+ if (net->nexthop.last_id_allocated == id_start)
+ break;
+
+ if (!nexthop_find_by_id(net, net->nexthop.last_id_allocated))
+ return net->nexthop.last_id_allocated;
+ }
+ return 0;
+}
+
+static size_t nh_nlmsg_size_ipv6(struct nh_info *nhi)
+{
+ size_t sz = 0;
+
+ sz = nla_total_size(sizeof(nhi->fib6_nh.nh_gw));
+
+ return sz;
+}
+
+static size_t nh_nlmsg_size_ipv4(struct nh_info *nhi)
+{
+ size_t sz;
+
+ sz = nla_total_size(4) /* NHA_GATEWAY */
+#ifdef CONFIG_IP_ROUTE_CLASSID
+ + nla_total_size(4) /* NHA_FLOW */
+#endif
+ + nla_total_size(4); /* NHA_SADDR_IPV4 */
+
+ return sz;
+}
+
+static size_t nh_nlmsg_size(struct nexthop *nh)
+{
+ struct nh_info *nhi = rtnl_dereference(nh->nh_info);
+ size_t sz = nla_total_size(4); /* NHA_ID */
+
+ /* covers NHA_BLACKHOLE since NHA_OIF and BLACKHOLE
+ * are mutually exclusive
+ */
+ sz += nla_total_size(4); /* NHA_OIF */
+
+ if (nhi->family == AF_INET)
+ sz += nh_nlmsg_size_ipv4(nhi);
+
+ else if (nhi->family == AF_INET6)
+ sz += nh_nlmsg_size_ipv6(nhi);
+
+ return sz;
+}
+
+static const struct net_device *nh_info_dev(const struct nh_info *nhi)
+{
+ switch (nhi->family) {
+ case AF_INET:
+ case AF_UNSPEC: /* dev only re-uses IPv4 struct */
+ return nhi->fib_nh.nh_dev;
+ case AF_INET6:
+ return nhi->fib6_nh.nh_dev;
+ }
+ return NULL;
+}
+
+static bool nh_info_uses_dev(const struct nh_info *nhi,
+ const struct net_device *dev)
+{
+ const struct net_device *nh_dev;
+
+ nh_dev = nh_info_dev(nhi);
+ if (nh_dev == dev || l3mdev_master_dev_rcu(nh_dev) == dev)
+ return true;
+
+ return false;
+}
+
+bool nexthop_uses_dev(const struct nexthop *nh, const struct net_device *dev)
+{
+ const struct nh_info *nhi;
+ bool dev_match = false;
+
+ nhi = rcu_dereference(nh->nh_info);
+ dev_match = nh_info_uses_dev(nhi, dev);
+
+ return dev_match;
+}
+
+static int nh_fill_node(struct sk_buff *skb, struct nexthop *nh,
+ int event, u32 portid, u32 seq, unsigned int nlflags)
+{
+ const struct net_device *dev;
+ struct fib6_nh *fib6_nh;
+ struct fib_nh *fib_nh;
+ struct nlmsghdr *nlh;
+ struct nh_info *nhi;
+ struct nhmsg *nhm;
+
+ nlh = nlmsg_put(skb, portid, seq, event, sizeof(*nhm), nlflags);
+ if (!nlh)
+ return -EMSGSIZE;
+
+ nhm = nlmsg_data(nlh);
+ nhm->nh_family = AF_UNSPEC;
+ nhm->nh_flags = nh->nh_flags;
+ nhm->nh_protocol = nh->protocol;
+ nhm->nh_scope = 0;
+ nhm->resvd = 0;
+
+ if (nla_put_u32(skb, NHA_ID, nh->id))
+ goto nla_put_failure;
+
+ nhi = rtnl_dereference(nh->nh_info);
+ if (nhi->reject_nh && nla_put_flag(skb, NHA_BLACKHOLE))
+ goto nla_put_failure;
+
+ dev = nh_info_dev(nhi);
+ if (dev && nla_put_u32(skb, NHA_OIF, dev->ifindex))
+ goto nla_put_failure;
+
+ nhm->nh_family = nhi->family;
+ switch (nhi->family) {
+ case AF_INET:
+ fib_nh = &nhi->fib_nh;
+
+ nhm->nh_scope = fib_nh->nh_scope;
+ if (nla_put_u32(skb, NHA_GATEWAY, fib_nh->nh_gw))
+ goto nla_put_failure;
+ if (nla_put_u32(skb, NHA_SADDR, fib_nh->nh_saddr))
+ goto nla_put_failure;
+#ifdef CONFIG_IP_ROUTE_CLASSID
+ if (fib_nh->nh_tclassid &&
+ nla_put_u32(skb, NHA_FLOW, fib_nh->nh_tclassid))
+ goto nla_put_failure;
+#endif
+ break;
+
+ case AF_INET6:
+ fib6_nh = &nhi->fib6_nh;
+ if (nla_put_in6_addr(skb, NHA_GATEWAY, &fib6_nh->nh_gw) < 0)
+ goto nla_put_failure;
+ break;
+ }
+
+ nlmsg_end(skb, nlh);
+ return 0;
+
+nla_put_failure:
+ return -EMSGSIZE;
+}
+
+static void nexthop_notify(int event, struct nexthop *nh, struct nl_info *info)
+{
+ unsigned int nlflags = info->nlh ? info->nlh->nlmsg_flags : 0;
+ u32 seq = info->nlh ? info->nlh->nlmsg_seq : 0;
+ struct sk_buff *skb;
+ int err = -ENOBUFS;
+
+ skb = nlmsg_new(nh_nlmsg_size(nh), gfp_any());
+ if (!skb)
+ goto errout;
+
+ err = nh_fill_node(skb, nh, event, info->portid, seq, nlflags);
+ if (err < 0) {
+ /* -EMSGSIZE implies BUG in nh_nlmsg_size() */
+ WARN_ON(err == -EMSGSIZE);
+ kfree_skb(skb);
+ goto errout;
+ }
+
+ rtnl_notify(skb, info->nl_net, info->portid, RTNLGRP_IPV4_ROUTE,
+ info->nlh, gfp_any());
+ return;
+errout:
+ if (err < 0)
+ rtnl_set_sk_err(info->nl_net, RTNLGRP_IPV4_ROUTE, err);
+}
+
+/* called on insert failure too */
+static void __remove_nexthop(struct net *net, struct nexthop *nh,
+ bool skip_fib, struct nl_info *nlinfo)
+{
+ const struct net_device *dev;
+ struct nh_info *nhi;
+
+ nhi = rtnl_dereference(nh->nh_info);
+ dev = nh_info_dev(nhi);
+ if (dev)
+ hlist_del(&nhi->dev_hash);
+}
+
+static void remove_nexthop(struct net *net, struct nexthop *nh,
+ bool skip_fib, struct nl_info *nlinfo)
+{
+ /* remove from the tree */
+ rb_erase(&nh->rb_node, &net->nexthop.root);
+
+ __remove_nexthop(net, nh, skip_fib, nlinfo);
+
+ nh_base_seq_inc(net);
+
+ nexthop_put(nh);
+
+ nexthop_notify(RTM_DELNEXTHOP, nh, nlinfo);
+}
+
+static int replace_nexthop(struct net *net, struct nexthop *old,
+ struct nexthop *new, struct netlink_ext_ack *extack)
+{
+ struct nh_info *oldi, *newi;
+
+ oldi = rtnl_dereference(old->nh_info);
+ newi = rtnl_dereference(new->nh_info);
+ rcu_assign_pointer(old->nh_info, newi);
+ rcu_assign_pointer(new->nh_info, oldi);
+
+ newi->nh_parent = old;
+ oldi->nh_parent = new;
+
+ old->protocol = new->protocol;
+ old->nh_flags = new->nh_flags;
+
+ rt_cache_flush(net);
+ // TO-DO: ipv6 equiv
+
+ __remove_nexthop(net, new, true, NULL);
+ nexthop_put(new);
+
+ return 0;
+}
+
+/* called with rtnl_lock held */
+static int insert_nexthop(struct net *net, struct nexthop *new_nh,
+ struct nh_config *cfg, struct netlink_ext_ack *extack)
+{
+ struct rb_node **pp, *parent = NULL, *next;
+ struct rb_root *root = &net->nexthop.root;
+ bool replace = !!(cfg->nlflags & NLM_F_REPLACE);
+ bool create = !!(cfg->nlflags & NLM_F_CREATE);
+ u32 new_id = new_nh->id;
+ int rc = -EEXIST;
+
+ pp = &root->rb_node;
+ while (1) {
+ struct nexthop *nh;
+
+ next = rtnl_dereference(*pp);
+ if (!next)
+ break;
+
+ parent = next;
+
+ nh = rb_entry(parent, struct nexthop, rb_node);
+ if (new_id < nh->id) {
+ pp = &next->rb_left;
+ } else if (new_id > nh->id) {
+ pp = &next->rb_right;
+ } else if (replace) {
+ rc = replace_nexthop(net, nh, new_nh, extack);
+ if (!rc)
+ new_nh = nh; /* send notification with old nh */
+ goto out;
+ } else {
+ /* id already exists and not a replace */
+ goto out;
+ }
+ }
+
+ if (replace && !create) {
+ NL_SET_ERR_MSG(extack, "Replace specified without create and no entry exists");
+ rc = -ENOENT;
+ goto out;
+ }
+
+ rb_link_node_rcu(&new_nh->rb_node, parent, pp);
+ rb_insert_color(&new_nh->rb_node, root);
+ rc = 0;
+out:
+ if (!rc) {
+ nh_base_seq_inc(net);
+ nexthop_notify(RTM_NEWNEXTHOP, new_nh, &cfg->nlinfo);
+ }
+
+ return rc;
+}
+
+/* rtnl */
+/* remove all nexthops tied to a device being deleted */
+static void nexthop_flush_dev(struct net_device *dev)
+{
+ unsigned int hash = nh_dev_hashfn(dev->ifindex);
+ struct net *net = dev_net(dev);
+ struct hlist_head *head = &net->nexthop.devhash[hash];
+ struct nl_info nlinfo = {
+ .nl_net = net,
+ };
+ struct hlist_node *n;
+ struct nh_info *nhi;
+
+ hlist_for_each_entry_safe(nhi, n, head, dev_hash) {
+ if (nh_info_dev(nhi) != dev)
+ continue;
+
+ remove_nexthop(net, nhi->nh_parent, false, &nlinfo);
+ }
+}
+
+/* rtnl */
+static void flush_all_nexthops(struct net *net)
+{
+ struct rb_root *root = &net->nexthop.root;
+ struct rb_node *node;
+ struct nexthop *nh;
+ struct nl_info nlinfo = {
+ .nl_net = net,
+ };
+
+ while ((node = rb_first(root))) {
+ nh = rb_entry(node, struct nexthop, rb_node);
+ remove_nexthop(net, nh, true, &nlinfo);
+ cond_resched();
+ }
+}
+
+static int nh_check_attr(struct nhmsg *nhm, struct nlattr *tb[],
+ struct net *net, struct netlink_ext_ack *extack)
+{
+ int err = -EINVAL;
+
+ if (nhm->nh_flags & ~NEXTHOP_VALID_USER_FLAGS) {
+ NL_SET_ERR_MSG(extack,
+ "Invalid nexthop flags in ancillary header");
+ goto out;
+ }
+
+ err = 0;
+out:
+ return err;
+}
+
+static int rtm_to_nh_config(struct net *net, struct sk_buff *skb,
+ struct nlmsghdr *nlh, struct nh_config *cfg,
+ struct netlink_ext_ack *extack)
+{
+ struct nhmsg *nhm = nlmsg_data(nlh);
+ struct nlattr *tb[NHA_MAX + 1];
+ int err;
+
+ err = nlmsg_parse(nlh, sizeof(*nhm), tb, NHA_MAX,
+ rtm_nh_policy, extack);
+ if (err < 0)
+ return err;
+
+ if (nhm->resvd) {
+ NL_SET_ERR_MSG(extack, "Invalid value in reserved field of ancillary header");
+ return -EINVAL;
+ }
+
+ err = nh_check_attr(nhm, tb, net, extack);
+ if (err < 0)
+ return err;
+
+ memset(cfg, 0, sizeof(*cfg));
+ cfg->nlflags = nlh->nlmsg_flags;
+ cfg->nlinfo.portid = NETLINK_CB(skb).portid;
+ cfg->nlinfo.nlh = nlh;
+ cfg->nlinfo.nl_net = net;
+
+ cfg->nh_family = nhm->nh_family;
+ cfg->nh_protocol = nhm->nh_protocol;
+ cfg->nh_flags = nhm->nh_flags;
+ cfg->nh_scope = nhm->nh_scope;
+
+ if (tb[NHA_ID])
+ cfg->nh_id = nla_get_u32(tb[NHA_ID]);
+
+ if (tb[NHA_OIF]) {
+ cfg->nh_ifindex = nla_get_u32(tb[NHA_OIF]);
+
+ if (cfg->nh_ifindex)
+ cfg->dev = __dev_get_by_index(net, cfg->nh_ifindex);
+
+ if (!cfg->dev) {
+ NL_SET_ERR_MSG(extack, "Invalid device index");
+ goto out;
+ } else if (!(cfg->dev->flags & IFF_UP)) {
+ NL_SET_ERR_MSG(extack, "Nexthop device is not up");
+ err = -ENETDOWN;
+ goto out;
+ } else if (!netif_carrier_ok(cfg->dev)) {
+ NL_SET_ERR_MSG(extack,
+ "Carrier for nexthop device is down");
+ err = -ENETDOWN;
+ goto out;
+ }
+
+ cfg->nh_table = l3mdev_fib_table(cfg->dev);
+ }
+
+ if (tb[NHA_TABLE_ID])
+ cfg->nh_table = nla_get_u32(tb[NHA_TABLE_ID]);
+
+ err = -EINVAL;
+ if (tb[NHA_FLOW]) {
+#ifndef CONFIG_IP_ROUTE_CLASSID
+ NL_SET_ERR_MSG(extack, "Classid not enabled in kernel");
+ goto out;
+#else
+ cfg->tclassid = nla_get_u32(tb[NHA_FLOW]);
+#endif
+ }
+
+ if (tb[NHA_GATEWAY]) {
+ struct nlattr *gwa = tb[NHA_GATEWAY];
+
+ switch (cfg->nh_family) {
+ case AF_INET:
+ if (nla_len(gwa) != sizeof(u32)) {
+ NL_SET_ERR_MSG(extack, "Invalid gateway");
+ goto out;
+ }
+ cfg->gw.ipv4 = nla_get_be32(gwa);
+ break;
+ case AF_INET6:
+ if (nla_len(gwa) != sizeof(struct in6_addr)) {
+ NL_SET_ERR_MSG(extack, "Invalid gateway");
+ goto out;
+ }
+ cfg->gw.ipv6 = nla_get_in6_addr(gwa);
+ break;
+ default:
+ NL_SET_ERR_MSG(extack,
+ "Unknown address family for gateway");
+ goto out;
+ }
+ } else {
+ cfg->nh_family = AF_UNSPEC;
+
+ /* device only nexthop (no gateway) */
+ if (cfg->nh_flags & RTNH_F_ONLINK) {
+ NL_SET_ERR_MSG(extack,
+ "ONLINK flag can not be set for nexthop without a gateway");
+ goto out;
+ }
+ cfg->nh_scope = RT_SCOPE_LINK;
+ }
+
+ if (tb[NHA_BLACKHOLE]) {
+ if (tb[NHA_GATEWAY]) {
+ NL_SET_ERR_MSG(extack,
+ "Blackhole attribute can not be used with gateway");
+ goto out;
+ }
+
+ cfg->nh_blackhole = 1;
+ }
+
+ if (cfg->tclassid && cfg->nh_family != AF_INET) {
+ NL_SET_ERR_MSG(extack,
+ "FLOW attribute only relevant for IPv4 nexthops");
+ goto out;
+ }
+
+ err = 0;
+out:
+ return err;
+}
+
+/* rtnl */
+static int rtm_get_nexthop(struct sk_buff *in_skb, struct nlmsghdr *nlh,
+ struct netlink_ext_ack *extack)
+{
+ struct net *net = sock_net(in_skb->sk);
+ struct nhmsg *nhm = nlmsg_data(nlh);
+ struct nlattr *tb[NHA_MAX + 1];
+ struct sk_buff *skb = NULL;
+ struct nexthop *nh;
+ int err;
+ u32 id;
+
+ err = nlmsg_parse(nlh, sizeof(*nhm), tb, NHA_MAX,
+ rtm_nh_policy, extack);
+ if (err < 0)
+ goto out;
+
+ err = -EINVAL;
+ if (!tb[NHA_ID]) {
+ NL_SET_ERR_MSG(extack, "Nexthop id is missing");
+ goto out;
+ }
+ id = nla_get_u32(tb[NHA_ID]);
+ if (!id) {
+ NL_SET_ERR_MSG(extack, "Invalid nexthop id");
+ goto out;
+ }
+
+ err = -ENOBUFS;
+ skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
+ if (!skb)
+ goto out;
+
+ err = -ENOENT;
+ nh = nexthop_find_by_id(net, id);
+ if (!nh)
+ goto errout_free;
+
+ err = nh_fill_node(skb, nh, RTM_NEWNEXTHOP, NETLINK_CB(in_skb).portid,
+ nlh->nlmsg_seq, 0);
+ if (err < 0)
+ goto errout_free;
+
+ err = rtnl_unicast(skb, net, NETLINK_CB(in_skb).portid);
+out:
+ return err;
+errout_free:
+ kfree_skb(skb);
+ goto out;
+}
+
+/* rtnl */
+static int rtm_del_nexthop(struct sk_buff *skb, struct nlmsghdr *nlh,
+ struct netlink_ext_ack *extack)
+{
+ struct net *net = sock_net(skb->sk);
+ struct nlattr *tb[NHA_MAX + 1];
+ struct nl_info nlinfo = {
+ .nlh = nlh,
+ .nl_net = net,
+ .portid = NETLINK_CB(skb).portid,
+ };
+ struct nexthop *nh;
+ struct nhmsg *nhm;
+ int err, i;
+ u32 id;
+
+ err = nlmsg_parse(nlh, sizeof(*nhm), tb, NHA_MAX,
+ rtm_nh_policy, extack);
+ if (err < 0)
+ return err;
+
+ nhm = nlmsg_data(nlh);
+
+ /* validate expected attribute and check for unexpected attributes */
+ for (i = 1; i < __NHA_MAX; ++i) {
+ switch (i) {
+ case NHA_ID:
+ if (!tb[NHA_ID]) {
+ NL_SET_ERR_MSG(extack, "Nexthop id is missing");
+ return -EINVAL;
+ }
+ break;
+ default:
+ if (!tb[i])
+ break;
+
+ NL_SET_ERR_MSG_ATTR(extack, tb[i],
+ "Unexpected attribute in request");
+ return -EINVAL;
+ }
+ }
+
+ id = nla_get_u32(tb[NHA_ID]);
+ nh = nexthop_find_by_id(net, id);
+ if (!nh)
+ return -ENOENT;
+
+ remove_nexthop(net, nh, false, &nlinfo);
+
+ return 0;
+}
+
+static int nh_create_ipv4(struct net *net, struct nexthop *nh,
+ struct nh_info *nhi, struct nh_config *cfg,
+ struct netlink_ext_ack *extack)
+{
+ struct fib_nh *fib_nh = &nhi->fib_nh;
+ struct fib_config fib_cfg = {
+ .fc_oif = cfg->nh_ifindex,
+ .fc_flow = cfg->tclassid,
+ .fc_gw = cfg->gw.ipv4,
+ .fc_flags = cfg->nh_flags,
+ };
+ int err;
+
+ err = fib_nh_init(net, fib_nh, &fib_cfg, 1, extack);
+ if (err)
+ goto out;
+
+ err = fib_check_nh(net, fib_nh, cfg->nh_table, cfg->nh_scope, extack);
+ if (!err) {
+ /* v4 code normally allows a nexthop device to have
+ * carrier down; this code does not allow it
+ */
+ if (fib_nh->nh_flags & RTNH_F_LINKDOWN) {
+ NL_SET_ERR_MSG(extack,
+ "Carrier for nexthop device is down");
+ dev_put(fib_nh->nh_dev);
+ err = -ENETDOWN;
+ goto out;
+ }
+
+ nh->nh_flags = fib_nh->nh_flags;
+ fib_info_update_nh_saddr(net, fib_nh, fib_nh->nh_scope);
+ }
+
+ nhi->has_gw = !!fib_nh->nh_gw;
+out:
+ return err;
+}
+
+static int nh_create_ipv6(struct net *net, struct nexthop *nh,
+ struct nh_info *nhi, struct nh_config *cfg,
+ struct netlink_ext_ack *extack)
+{
+ struct fib6_nh *fib6_nh = &nhi->fib6_nh;
+ struct fib6_config fib6_cfg = {
+ .fc_table = cfg->nh_table,
+ .fc_ifindex = cfg->nh_ifindex,
+ .fc_gateway = cfg->gw.ipv6,
+ .fc_flags = cfg->nh_flags,
+ };
+ int err;
+
+ if (!ipv6_addr_any(&cfg->gw.ipv6)) {
+ fib6_cfg.fc_flags |= RTF_GATEWAY;
+ nhi->has_gw = true;
+ }
+
+ err = ipv6_stub->fib6_nh_init(net, fib6_nh, &fib6_cfg, extack);
+ if (!err) {
+ if (fib6_cfg.fc_flags & RTF_REJECT) {
+ NL_SET_ERR_MSG(extack,
+ "Nexthop can not use RTF_REJECT");
+ err = -EINVAL;
+ goto out;
+ }
+
+ if (fib6_nh->nh_flags & RTNH_F_LINKDOWN) {
+ NL_SET_ERR_MSG(extack,
+ "Carrier for nexthop device is down");
+ err = -ENETDOWN;
+ goto out;
+ }
+
+ nh->nh_flags = fib6_nh->nh_flags;
+ }
+
+out:
+ if (err && fib6_nh->nh_dev)
+ dev_put(fib6_nh->nh_dev);
+ return err;
+}
+
+static int nh_create_unspec(struct net *net, struct nexthop *nh,
+ struct nh_info *nhi, struct nh_config *cfg,
+ struct netlink_ext_ack *extack)
+{
+ struct net_device *dev = cfg->dev;
+ int err = 0;
+
+ if (cfg->nh_blackhole) {
+ nhi->reject_nh = 1;
+ } else if (!dev) {
+ NL_SET_ERR_MSG(extack, "No device for nexthop");
+ err = -ENODEV;
+ } else {
+ /* leverage ipv4 infra for non-gw nexthop */
+ err = nh_create_ipv4(net, nh, nhi, cfg, extack);
+ }
+
+ return err;
+}
+
+static void nexthop_init_common(struct nexthop *nh)
+{
+ INIT_LIST_HEAD(&nh->fi_list);
+ INIT_LIST_HEAD(&nh->f6i_list);
+}
+
+static struct nexthop *nexthop_create(struct net *net, struct nh_config *cfg,
+ struct netlink_ext_ack *extack)
+{
+ struct nh_info *nhi;
+ struct nexthop *nh;
+ int err;
+
+ nh = nexthop_alloc();
+ if (!nh)
+ return ERR_PTR(-ENOMEM);
+
+ nhi = kzalloc(sizeof(*nhi), GFP_KERNEL);
+ if (!nhi) {
+ kfree(nh);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ nh->nh_flags = cfg->nh_flags;
+ nexthop_init_common(nh);
+
+ nhi->nh_parent = nh;
+ nhi->family = cfg->nh_family;
+ nhi->net = net;
+ switch (cfg->nh_family) {
+ case AF_INET:
+ err = nh_create_ipv4(net, nh, nhi, cfg, extack);
+ break;
+ case AF_INET6:
+ err = nh_create_ipv6(net, nh, nhi, cfg, extack);
+ break;
+ default:
+ err = nh_create_unspec(net, nh, nhi, cfg, extack);
+ }
+
+ if (err) {
+ kfree(nhi);
+ kfree(nh);
+ return ERR_PTR(err);
+ }
+
+ /* add the entry to the device based hash */
+ if (!nhi->reject_nh)
+ nexthop_devhash_add(net, nhi);
+
+ rcu_assign_pointer(nh->nh_info, nhi);
+
+ return nh;
+}
+
+/* called with rtnl lock held */
+static struct nexthop *nexthop_add(struct net *net, struct nh_config *cfg,
+ struct netlink_ext_ack *extack)
+{
+ struct nexthop *nh;
+ int err;
+
+ if (cfg->nlflags & NLM_F_REPLACE && !cfg->nh_id) {
+ NL_SET_ERR_MSG(extack, "Replace requires nexthop id");
+ return ERR_PTR(-EINVAL);
+ }
+
+ if (!cfg->nh_id) {
+ cfg->nh_id = nh_find_unused_id(net);
+ if (!cfg->nh_id) {
+ NL_SET_ERR_MSG(extack, "No unused id.");
+ return ERR_PTR(-EINVAL);
+ }
+ }
+
+ nh = nexthop_create(net, cfg, extack);
+ if (IS_ERR(nh))
+ return nh;
+
+ refcount_set(&nh->refcnt, 1);
+ nh->id = cfg->nh_id;
+ nh->protocol = cfg->nh_protocol;
+
+ err = insert_nexthop(net, nh, cfg, extack);
+ if (err)
+ goto out_err;
+
+ return nh;
+out_err:
+ __remove_nexthop(net, nh, true, NULL);
+ nexthop_put(nh);
+
+ return ERR_PTR(err);
+}
+
+/* rtnl */
+static int rtm_new_nexthop(struct sk_buff *skb, struct nlmsghdr *nlh,
+ struct netlink_ext_ack *extack)
+{
+ struct net *net = sock_net(skb->sk);
+ struct nh_config cfg;
+ struct nexthop *nh;
+ int err;
+
+ err = rtm_to_nh_config(net, skb, nlh, &cfg, extack);
+ if (!err) {
+ nh = nexthop_add(net, &cfg, extack);
+ if (IS_ERR(nh))
+ err = PTR_ERR(nh);
+ }
+
+ return err;
+}
+
+static bool nh_dump_filtered(struct nexthop *nh, int dev_idx,
+ int master_idx, u8 family)
+{
+ const struct net_device *dev;
+ const struct nh_info *nhi;
+
+ if (dev_idx || master_idx || family)
+ return true;
+
+ nhi = rtnl_dereference(nh->nh_info);
+ if (family && nhi->family != family)
+ return true;
+
+ dev = nh_info_dev(nhi);
+ if (dev_idx && (!dev || dev->ifindex != dev_idx))
+ return true;
+
+ if (master_idx) {
+ struct net_device *master;
+
+ master = netdev_master_upper_dev_get((struct net_device *)dev);
+ if (!master || master->ifindex != master_idx)
+ return true;
+ }
+ return false;
+}
+
+/* rtnl */
+static int rtm_dump_nexthop(struct sk_buff *skb, struct netlink_callback *cb)
+{
+ int dev_filter_idx = 0, master_idx = 0;
+ struct net *net = sock_net(skb->sk);
+ struct rb_root *root = &net->nexthop.root;
+ struct nlattr *tb[NHA_MAX + 1];
+ struct rb_node *node;
+ struct nhmsg *nhm;
+ int idx = 0;
+ int s_idx;
+ int err;
+
+ if (nlmsg_parse(cb->nlh, sizeof(*nhm), tb, NHA_MAX,
+ rtm_nh_policy, NULL) >= 0) {
+ if (tb[NHA_OIF])
+ dev_filter_idx = nla_get_u32(tb[NHA_OIF]);
+
+ if (tb[NHA_MASTER])
+ master_idx = nla_get_u32(tb[NHA_MASTER]);
+ }
+
+ nhm = nlmsg_data(cb->nlh);
+
+ s_idx = cb->args[0];
+ for (node = rb_first(root); node; node = rb_next(node)) {
+ struct nexthop *nh;
+
+ if (idx < s_idx)
+ goto cont;
+
+ nh = rb_entry(node, struct nexthop, rb_node);
+ if (nh_dump_filtered(nh, dev_filter_idx, master_idx,
+ nhm->nh_family))
+ goto cont;
+
+ err = nh_fill_node(skb, nh, RTM_NEWNEXTHOP,
+ NETLINK_CB(cb->skb).portid,
+ cb->nlh->nlmsg_seq, NLM_F_MULTI);
+ if (err < 0) {
+ if (likely(skb->len))
+ goto out;
+
+ goto out_err;
+ }
+cont:
+ idx++;
+ }
+
+out:
+ err = skb->len;
+out_err:
+ cb->args[0] = idx;
+ cb->seq = net->nexthop.seq;
+ nl_dump_check_consistent(cb, nlmsg_hdr(skb));
+
+ return err;
+}
+
+/* rtnl */
+static int nh_netdev_event(struct notifier_block *this,
+ unsigned long event, void *ptr)
+{
+ struct net_device *dev = netdev_notifier_info_to_dev(ptr);
+
+ switch (event) {
+ case NETDEV_DOWN:
+ case NETDEV_UNREGISTER:
+ nexthop_flush_dev(dev);
+ break;
+ case NETDEV_CHANGE:
+ if (!(dev_get_flags(dev) & (IFF_RUNNING | IFF_LOWER_UP)))
+ nexthop_flush_dev(dev);
+ break;
+ }
+ return NOTIFY_DONE;
+}
+
+static struct notifier_block nh_netdev_notifier = {
+ .notifier_call = nh_netdev_event,
+};
+
+static void __net_exit nexthop_net_exit(struct net *net)
+{
+ rtnl_lock();
+ flush_all_nexthops(net);
+ rtnl_unlock();
+ kfree(net->nexthop.devhash);
+}
+
+static int __net_init nexthop_net_init(struct net *net)
+{
+ size_t sz = sizeof(struct hlist_head) * NH_DEV_HASHSIZE;
+
+ net->nexthop.root = RB_ROOT;
+ net->nexthop.devhash = kzalloc(sz, GFP_KERNEL);
+ if (!net->nexthop.devhash)
+ return -ENOMEM;
+
+ return 0;
+}
+
+static struct pernet_operations nexthop_net_ops = {
+ .init = nexthop_net_init,
+ .exit = nexthop_net_exit,
+};
+
+static int __init nexthop_init(void)
+{
+ register_pernet_subsys(&nexthop_net_ops);
+
+ register_netdevice_notifier(&nh_netdev_notifier);
+
+ rtnl_register(PF_UNSPEC, RTM_NEWNEXTHOP, rtm_new_nexthop, NULL, 0);
+ rtnl_register(PF_UNSPEC, RTM_DELNEXTHOP, rtm_del_nexthop, NULL, 0);
+ rtnl_register(PF_UNSPEC, RTM_GETNEXTHOP, rtm_get_nexthop,
+ rtm_dump_nexthop, 0);
+
+ rtnl_register(PF_INET, RTM_NEWNEXTHOP, rtm_new_nexthop, NULL, 0);
+ rtnl_register(PF_INET, RTM_GETNEXTHOP, NULL, rtm_dump_nexthop, 0);
+
+ rtnl_register(PF_INET6, RTM_NEWNEXTHOP, rtm_new_nexthop, NULL, 0);
+ rtnl_register(PF_INET6, RTM_GETNEXTHOP, NULL, rtm_dump_nexthop, 0);
+
+ return 0;
+}
+subsys_initcall(nexthop_init);
diff --git a/security/selinux/nlmsgtab.c b/security/selinux/nlmsgtab.c
index 74b951f55608..7a852d5af14e 100644
--- a/security/selinux/nlmsgtab.c
+++ b/security/selinux/nlmsgtab.c
@@ -80,6 +80,9 @@ static const struct nlmsg_perm nlmsg_route_perms[] =
{ RTM_NEWSTATS, NETLINK_ROUTE_SOCKET__NLMSG_READ },
{ RTM_GETSTATS, NETLINK_ROUTE_SOCKET__NLMSG_READ },
{ RTM_NEWCACHEREPORT, NETLINK_ROUTE_SOCKET__NLMSG_READ },
+ { RTM_NEWNEXTHOP, NETLINK_ROUTE_SOCKET__NLMSG_WRITE },
+ { RTM_DELNEXTHOP, NETLINK_ROUTE_SOCKET__NLMSG_WRITE },
+ { RTM_GETNEXTHOP, NETLINK_ROUTE_SOCKET__NLMSG_READ },
};
static const struct nlmsg_perm nlmsg_tcpdiag_perms[] =
@@ -159,7 +162,7 @@ int selinux_nlmsg_lookup(u16 sclass, u16 nlmsg_type, u32 *perm)
switch (sclass) {
case SECCLASS_NETLINK_ROUTE_SOCKET:
/* RTM_MAX always point to RTM_SETxxxx, ie RTM_NEWxxx + 3 */
- BUILD_BUG_ON(RTM_MAX != (RTM_NEWCHAIN + 3));
+ BUILD_BUG_ON(RTM_MAX != (RTM_NEWNEXTHOP + 3));
err = nlmsg_perm(nlmsg_type, perm, nlmsg_route_perms,
sizeof(nlmsg_route_perms));
break;
--
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 ` dsahern [this message]
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 ` [PATCH iproute2-next] ip: Add support for nexthop objects dsahern
2018-09-01 20:37 ` 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-12-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.