[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-Id: <20171211203837.2540-8-tom@quantonium.net>
Date: Mon, 11 Dec 2017 12:38:35 -0800
From: Tom Herbert <tom@...ntonium.net>
To: davem@...emloft.net
Cc: netdev@...r.kernel.org, roopa@...ulusnetworks.com,
rohit@...ntonium.net, Tom Herbert <tom@...ntonium.net>
Subject: [PATCH v3 net-next 7/9] ila: Resolver mechanism
Implement an ILA resolver. This uses LWT to implement the hook to a
userspace resolver and tracks pending unresolved address using the
backend net resolver.
The idea is that the kernel sets an ILA resolver route to the
SIR prefix, something like:
ip route add 3333::/64 encap ila-resolve \
via 2401:db00:20:911a::27:0 dev eth0
When a packet hits the route the address is looked up in a resolver
table. If the entry is created (no entry with the address already
exists) then an rtnl message is generated with group
RTNLGRP_ILA_NOTIFY and type RTM_ADDR_RESOLVE. A userspace
daemon can listen for such messages and perform an ILA resolution
protocol to determine the ILA mapping. If the mapping is resolved
then a /128 ila encap router is set so that host can perform
ILA translation and send directly to destination.
Signed-off-by: Tom Herbert <tom@...ntonium.net>
---
include/uapi/linux/ila.h | 9 ++
include/uapi/linux/lwtunnel.h | 1 +
include/uapi/linux/rtnetlink.h | 8 +-
net/core/lwtunnel.c | 2 +
net/ipv6/Kconfig | 1 +
net/ipv6/ila/Makefile | 2 +-
net/ipv6/ila/ila.h | 11 ++
net/ipv6/ila/ila_lwt.c | 8 ++
net/ipv6/ila/ila_main.c | 14 +++
net/ipv6/ila/ila_resolver.c | 244 +++++++++++++++++++++++++++++++++++++++++
10 files changed, 298 insertions(+), 2 deletions(-)
create mode 100644 net/ipv6/ila/ila_resolver.c
diff --git a/include/uapi/linux/ila.h b/include/uapi/linux/ila.h
index db45d3e49a12..66557265bf5b 100644
--- a/include/uapi/linux/ila.h
+++ b/include/uapi/linux/ila.h
@@ -65,4 +65,13 @@ enum {
ILA_HOOK_ROUTE_INPUT,
};
+enum {
+ ILA_NOTIFY_ATTR_UNSPEC,
+ ILA_NOTIFY_ATTR_TIMEOUT, /* u32 */
+
+ __ILA_NOTIFY_ATTR_MAX,
+};
+
+#define ILA_NOTIFY_ATTR_MAX (__ILA_NOTIFY_ATTR_MAX - 1)
+
#endif /* _UAPI_LINUX_ILA_H */
diff --git a/include/uapi/linux/lwtunnel.h b/include/uapi/linux/lwtunnel.h
index de696ca12f2c..2eac16f8323f 100644
--- a/include/uapi/linux/lwtunnel.h
+++ b/include/uapi/linux/lwtunnel.h
@@ -13,6 +13,7 @@ enum lwtunnel_encap_types {
LWTUNNEL_ENCAP_SEG6,
LWTUNNEL_ENCAP_BPF,
LWTUNNEL_ENCAP_SEG6_LOCAL,
+ LWTUNNEL_ENCAP_ILA_NOTIFY,
__LWTUNNEL_ENCAP_MAX,
};
diff --git a/include/uapi/linux/rtnetlink.h b/include/uapi/linux/rtnetlink.h
index d8b5f80c2ea6..8d358a300d8a 100644
--- a/include/uapi/linux/rtnetlink.h
+++ b/include/uapi/linux/rtnetlink.h
@@ -13,7 +13,8 @@
*/
#define RTNL_FAMILY_IPMR 128
#define RTNL_FAMILY_IP6MR 129
-#define RTNL_FAMILY_MAX 129
+#define RTNL_FAMILY_ILA 130
+#define RTNL_FAMILY_MAX 130
/****
* Routing/neighbour discovery messages.
@@ -150,6 +151,9 @@ enum {
RTM_NEWCACHEREPORT = 96,
#define RTM_NEWCACHEREPORT RTM_NEWCACHEREPORT
+ RTM_ADDR_RESOLVE = 98,
+#define RTM_ADDR_RESOLVE RTM_ADDR_RESOLVE
+
__RTM_MAX,
#define RTM_MAX (((__RTM_MAX + 3) & ~3) - 1)
};
@@ -676,6 +680,8 @@ enum rtnetlink_groups {
#define RTNLGRP_IPV4_MROUTE_R RTNLGRP_IPV4_MROUTE_R
RTNLGRP_IPV6_MROUTE_R,
#define RTNLGRP_IPV6_MROUTE_R RTNLGRP_IPV6_MROUTE_R
+ RTNLGRP_ILA_NOTIFY,
+#define RTNLGRP_ILA_NOTIFY RTNLGRP_ILA_NOTIFY
__RTNLGRP_MAX
};
#define RTNLGRP_MAX (__RTNLGRP_MAX - 1)
diff --git a/net/core/lwtunnel.c b/net/core/lwtunnel.c
index b3f2f77dfe72..16b04d05e9b9 100644
--- a/net/core/lwtunnel.c
+++ b/net/core/lwtunnel.c
@@ -46,6 +46,8 @@ static const char *lwtunnel_encap_str(enum lwtunnel_encap_types encap_type)
return "BPF";
case LWTUNNEL_ENCAP_SEG6_LOCAL:
return "SEG6LOCAL";
+ case LWTUNNEL_ENCAP_ILA_NOTIFY:
+ return "ILA-NOTIFY";
case LWTUNNEL_ENCAP_IP6:
case LWTUNNEL_ENCAP_IP:
case LWTUNNEL_ENCAP_NONE:
diff --git a/net/ipv6/Kconfig b/net/ipv6/Kconfig
index ea71e4b0ab7a..5b0a6e1bd7cc 100644
--- a/net/ipv6/Kconfig
+++ b/net/ipv6/Kconfig
@@ -110,6 +110,7 @@ config IPV6_ILA
tristate "IPv6: Identifier Locator Addressing (ILA)"
depends on NETFILTER
select LWTUNNEL
+ select NET_RESOLVER
---help---
Support for IPv6 Identifier Locator Addressing (ILA).
diff --git a/net/ipv6/ila/Makefile b/net/ipv6/ila/Makefile
index b7739aba6e68..3ec2d65ceee2 100644
--- a/net/ipv6/ila/Makefile
+++ b/net/ipv6/ila/Makefile
@@ -4,4 +4,4 @@
obj-$(CONFIG_IPV6_ILA) += ila.o
-ila-objs := ila_main.o ila_common.o ila_lwt.o ila_xlat.o
+ila-objs := ila_main.o ila_common.o ila_lwt.o ila_xlat.o ila_resolver.o
diff --git a/net/ipv6/ila/ila.h b/net/ipv6/ila/ila.h
index 1f747bcbec29..02a800c71796 100644
--- a/net/ipv6/ila/ila.h
+++ b/net/ipv6/ila/ila.h
@@ -15,6 +15,7 @@
#include <linux/ip.h>
#include <linux/kernel.h>
#include <linux/module.h>
+#include <linux/rhashtable.h>
#include <linux/socket.h>
#include <linux/skbuff.h>
#include <linux/types.h>
@@ -112,6 +113,9 @@ struct ila_net {
unsigned int locks_mask;
bool hooks_registered;
} xlat;
+ struct {
+ struct net_rslv *nrslv;
+ } rslv;
};
int ila_lwt_init(void);
@@ -120,6 +124,11 @@ void ila_lwt_fini(void);
int ila_xlat_init_net(struct net *net);
void ila_xlat_exit_net(struct net *net);
+int ila_rslv_init(void);
+void ila_rslv_fini(void);
+int ila_rslv_init_net(struct net *net);
+void ila_rslv_exit_net(struct net *net);
+
int ila_xlat_nl_cmd_add_mapping(struct sk_buff *skb, struct genl_info *info);
int ila_xlat_nl_cmd_del_mapping(struct sk_buff *skb, struct genl_info *info);
int ila_xlat_nl_cmd_get_mapping(struct sk_buff *skb, struct genl_info *info);
@@ -132,4 +141,6 @@ extern unsigned int ila_net_id;
extern struct genl_family ila_nl_family;
+void ila_rslv_resolved(struct ila_net *ilan, struct ila_addr *iaddr);
+
#endif /* __ILA_H */
diff --git a/net/ipv6/ila/ila_lwt.c b/net/ipv6/ila/ila_lwt.c
index 9f1e46a1468e..20dddc7ea8d0 100644
--- a/net/ipv6/ila/ila_lwt.c
+++ b/net/ipv6/ila/ila_lwt.c
@@ -12,6 +12,7 @@
#include <net/ip6_fib.h>
#include <net/ip6_route.h>
#include <net/lwtunnel.h>
+#include <net/netns/generic.h>
#include <net/protocol.h>
#include <uapi/linux/ila.h>
#include "ila.h"
@@ -250,6 +251,13 @@ static int ila_build_state(struct net *net, struct nlattr *nla,
*ts = newts;
+ if (cfg6->fc_dst_len >= sizeof(struct ila_addr)) {
+ struct ila_net *ilan = net_generic(net, ila_net_id);
+
+ /* Cancel any pending resolution on this address */
+ ila_rslv_resolved(ilan, iaddr);
+ }
+
return 0;
}
diff --git a/net/ipv6/ila/ila_main.c b/net/ipv6/ila/ila_main.c
index 18fac76b9520..411d3d112157 100644
--- a/net/ipv6/ila/ila_main.c
+++ b/net/ipv6/ila/ila_main.c
@@ -64,14 +64,21 @@ static __net_init int ila_init_net(struct net *net)
if (err)
goto ila_xlat_init_fail;
+ err = ila_rslv_init_net(net);
+ if (err)
+ goto resolver_init_fail;
+
return 0;
ila_xlat_init_fail:
+ ila_xlat_exit_net(net);
+resolver_init_fail:
return err;
}
static __net_exit void ila_exit_net(struct net *net)
{
+ ila_rslv_exit_net(net);
ila_xlat_exit_net(net);
}
@@ -98,8 +105,14 @@ static int __init ila_init(void)
if (ret)
goto fail_lwt;
+ ret = ila_rslv_init();
+ if (ret)
+ goto fail_rslv;
+
return 0;
+fail_rslv:
+ ila_lwt_fini();
fail_lwt:
genl_unregister_family(&ila_nl_family);
register_family_fail:
@@ -110,6 +123,7 @@ static int __init ila_init(void)
static void __exit ila_fini(void)
{
+ ila_rslv_fini();
ila_lwt_fini();
genl_unregister_family(&ila_nl_family);
unregister_pernet_device(&ila_net_ops);
diff --git a/net/ipv6/ila/ila_resolver.c b/net/ipv6/ila/ila_resolver.c
new file mode 100644
index 000000000000..8b9a3c5305a4
--- /dev/null
+++ b/net/ipv6/ila/ila_resolver.c
@@ -0,0 +1,244 @@
+/*
+ * net/core/ila_resolver.c - ILA address resolver
+ *
+ * Copyright (c) 2017 Tom Herbert <tom@...ntonium.net>
+ *
+ * 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.
+ */
+#include <linux/errno.h>
+#include <linux/ip.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/netlink.h>
+#include <linux/skbuff.h>
+#include <linux/types.h>
+#include <net/ip6_fib.h>
+#include <net/lwtunnel.h>
+#include <net/netns/generic.h>
+#include <net/resolver.h>
+#include <uapi/linux/ila.h>
+#include "ila.h"
+
+struct ila_notify_params {
+ unsigned int timeout;
+};
+
+static inline struct ila_notify_params *ila_notify_params_lwtunnel(
+ struct lwtunnel_state *lwstate)
+{
+ return (struct ila_notify_params *)lwstate->data;
+}
+
+static int ila_fill_notify(struct sk_buff *skb, struct in6_addr *addr,
+ u32 pid, u32 seq, int event, int flags)
+{
+ struct nlmsghdr *nlh;
+ struct rtmsg *rtm;
+
+ nlh = nlmsg_put(skb, pid, seq, event, sizeof(*rtm), flags);
+ if (!nlh)
+ return -EMSGSIZE;
+
+ rtm = nlmsg_data(nlh);
+ rtm->rtm_family = AF_INET6;
+ rtm->rtm_dst_len = 128;
+ rtm->rtm_src_len = 0;
+ rtm->rtm_tos = 0;
+ rtm->rtm_table = RT6_TABLE_UNSPEC;
+ rtm->rtm_type = RTN_UNICAST;
+ rtm->rtm_scope = RT_SCOPE_UNIVERSE;
+
+ if (nla_put_in6_addr(skb, RTA_DST, addr)) {
+ nlmsg_cancel(skb, nlh);
+ return -EMSGSIZE;
+ }
+
+ nlmsg_end(skb, nlh);
+ return 0;
+}
+
+static size_t ila_rslv_msgsize(void)
+{
+ size_t len =
+ NLMSG_ALIGN(sizeof(struct rtmsg))
+ + nla_total_size(16) /* RTA_DST */
+ ;
+
+ return len;
+}
+
+void ila_rslv_notify(struct net *net, struct sk_buff *skb)
+{
+ struct ipv6hdr *ip6h = ipv6_hdr(skb);
+ struct sk_buff *nlskb;
+ int err = 0;
+
+ /* Send ILA notification to user */
+ nlskb = nlmsg_new(ila_rslv_msgsize(), GFP_KERNEL);
+ if (!nlskb)
+ goto errout;
+
+ err = ila_fill_notify(nlskb, &ip6h->daddr, 0, 0, RTM_ADDR_RESOLVE,
+ NLM_F_MULTI);
+ if (err < 0) {
+ WARN_ON(err == -EMSGSIZE);
+ kfree_skb(nlskb);
+ goto errout;
+ }
+ rtnl_notify(nlskb, net, 0, RTNLGRP_ILA_NOTIFY, NULL, GFP_ATOMIC);
+ return;
+
+errout:
+ if (err < 0)
+ rtnl_set_sk_err(net, RTNLGRP_ILA_NOTIFY, err);
+}
+
+static int ila_rslv_output(struct net *net, struct sock *sk,
+ struct sk_buff *skb)
+{
+ struct ila_net *ilan = net_generic(net, ila_net_id);
+ struct dst_entry *dst = skb_dst(skb);
+ struct ipv6hdr *ip6h = ipv6_hdr(skb);
+ struct ila_notify_params *p;
+
+ p = ila_notify_params_lwtunnel(dst->lwtstate);
+
+ /* Net resolver create function returns zero only when a new
+ * entry is create (returns -EEXIST is entry already in table)..
+ */
+ if (!net_rslv_lookup_and_create(ilan->rslv.nrslv, &ip6h->daddr,
+ p->timeout))
+ ila_rslv_notify(net, skb);
+
+ return dst->lwtstate->orig_output(net, sk, skb);
+}
+
+void ila_rslv_resolved(struct ila_net *ilan, struct ila_addr *iaddr)
+{
+ if (ilan->rslv.nrslv)
+ net_rslv_resolved(ilan->rslv.nrslv, iaddr);
+}
+
+static int ila_rslv_input(struct sk_buff *skb)
+{
+ struct dst_entry *dst = skb_dst(skb);
+
+ return dst->lwtstate->orig_input(skb);
+}
+
+static const struct nla_policy ila_notify_nl_policy[ILA_NOTIFY_ATTR_MAX + 1] = {
+ [ILA_NOTIFY_ATTR_TIMEOUT] = { .type = NLA_U32, },
+};
+
+static int ila_rslv_build_state(struct net *net, struct nlattr *nla,
+ unsigned int family, const void *cfg,
+ struct lwtunnel_state **ts,
+ struct netlink_ext_ack *extack)
+{
+ struct ila_notify_params *p;
+ struct nlattr *tb[ILA_NOTIFY_ATTR_MAX + 1];
+ struct lwtunnel_state *newts;
+ size_t encap_len = sizeof(*p);
+ int ret;
+
+ if (family != AF_INET6)
+ return -EINVAL;
+
+ ret = nla_parse_nested(tb, ILA_NOTIFY_ATTR_MAX, nla,
+ ila_notify_nl_policy, extack);
+
+ if (ret < 0)
+ return ret;
+
+ newts = lwtunnel_state_alloc(encap_len);
+ if (!newts)
+ return -ENOMEM;
+
+ newts->type = LWTUNNEL_ENCAP_ILA_NOTIFY;
+ newts->flags |= LWTUNNEL_STATE_OUTPUT_REDIRECT |
+ LWTUNNEL_STATE_INPUT_REDIRECT;
+
+ p = ila_notify_params_lwtunnel(newts);
+
+ if (tb[ILA_NOTIFY_ATTR_TIMEOUT])
+ p->timeout = msecs_to_jiffies(nla_get_u32(
+ tb[ILA_NOTIFY_ATTR_TIMEOUT]));
+
+ *ts = newts;
+
+ return 0;
+}
+
+static int ila_rslv_fill_encap_info(struct sk_buff *skb,
+ struct lwtunnel_state *lwtstate)
+{
+ struct ila_notify_params *p = ila_notify_params_lwtunnel(lwtstate);
+
+ if (nla_put_u32(skb, ILA_NOTIFY_ATTR_TIMEOUT,
+ (__force u32)jiffies_to_msecs(p->timeout)))
+ goto nla_put_failure;
+
+ return 0;
+
+nla_put_failure:
+ return -EMSGSIZE;
+}
+
+static int ila_rslv_nlsize(struct lwtunnel_state *lwtstate)
+{
+ return nla_total_size(sizeof(u32)) + /* ILA_NOTIFY_ATTR_TIMEOUT */
+ 0;
+}
+
+static int ila_rslv_cmp(struct lwtunnel_state *a, struct lwtunnel_state *b)
+{
+ return 0;
+}
+
+static const struct lwtunnel_encap_ops ila_rslv_ops = {
+ .build_state = ila_rslv_build_state,
+ .output = ila_rslv_output,
+ .input = ila_rslv_input,
+ .fill_encap = ila_rslv_fill_encap_info,
+ .get_encap_size = ila_rslv_nlsize,
+ .cmp_encap = ila_rslv_cmp,
+};
+
+#define ILA_MAX_SIZE 8192
+
+int ila_rslv_init_net(struct net *net)
+{
+ struct ila_net *ilan = net_generic(net, ila_net_id);
+ struct net_rslv *nrslv;
+
+ nrslv = net_rslv_create(sizeof(struct ila_addr),
+ sizeof(struct ila_addr), ILA_MAX_SIZE, NULL);
+
+ if (IS_ERR(nrslv))
+ return PTR_ERR(nrslv);
+
+ ilan->rslv.nrslv = nrslv;
+
+ return 0;
+}
+
+void ila_rslv_exit_net(struct net *net)
+{
+ struct ila_net *ilan = net_generic(net, ila_net_id);
+
+ if (ilan->rslv.nrslv)
+ net_rslv_destroy(ilan->rslv.nrslv);
+}
+
+int ila_rslv_init(void)
+{
+ return lwtunnel_encap_add_ops(&ila_rslv_ops, LWTUNNEL_ENCAP_ILA_NOTIFY);
+}
+
+void ila_rslv_fini(void)
+{
+ lwtunnel_encap_del_ops(&ila_rslv_ops, LWTUNNEL_ENCAP_ILA_NOTIFY);
+}
--
2.11.0
Powered by blists - more mailing lists