From patchwork Mon Dec 11 20:38:35 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Tom Herbert X-Patchwork-Id: 847216 X-Patchwork-Delegate: davem@davemloft.net Return-Path: X-Original-To: patchwork-incoming@ozlabs.org Delivered-To: patchwork-incoming@ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=vger.kernel.org (client-ip=209.132.180.67; helo=vger.kernel.org; envelope-from=netdev-owner@vger.kernel.org; receiver=) Authentication-Results: ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=quantonium-net.20150623.gappssmtp.com header.i=@quantonium-net.20150623.gappssmtp.com header.b="Ke0pct/m"; dkim-atps=neutral Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 3ywZZ96t1fz9sRm for ; Tue, 12 Dec 2017 07:39:29 +1100 (AEDT) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1752576AbdLKUj2 (ORCPT ); Mon, 11 Dec 2017 15:39:28 -0500 Received: from mail-pg0-f65.google.com ([74.125.83.65]:39188 "EHLO mail-pg0-f65.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1752486AbdLKUjR (ORCPT ); Mon, 11 Dec 2017 15:39:17 -0500 Received: by mail-pg0-f65.google.com with SMTP id w7so11745277pgv.6 for ; Mon, 11 Dec 2017 12:39:17 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=quantonium-net.20150623.gappssmtp.com; s=20150623; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=MLYGr/snRyv96mDHSHrynFkIpRK/zKiFZWsImNI3xas=; b=Ke0pct/mcem69vKXkc+7MIbugezLH96XgLM/MjcubwTDYvdxmOLweD89fv5kQka7Xf aINZPveADYyMw8IgrZX/+7GPCAnFNbciqIUViqdqMgijJ9qKyKF6wNET2Luoyad8rwjv qCDdvA0kimPSnstwbTBcO+fiHY6DwCPbqy7mhU91PrqzVFASeXl940i4vs9BO4S/JjQq AWHEx7v7pNyH6+VFuzUFaXjSjMGqg1CCBaHzUF40SKvrPC4bpwE7brJ0OpwCSpx/qjyP yc3zHPVpHyEdBUxn/7aW2llN2drKVovv9mmzpv/ljXR4egSomdlAt6xfcIDRHeeUYaaH tUIA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=MLYGr/snRyv96mDHSHrynFkIpRK/zKiFZWsImNI3xas=; b=WjluQkTi2Mr0Rgldm7p7nG2QcBOzQWsdMaAPYcHqg8irkk4TI7eeoIf+2vtAeA4nds 1vE+Sh3oc5SL+XHYxdsQYrYYP0rr95G1HMvuqGlvMmaa0DV4HgGxOvUkvbafYFLu2Qm9 J4L6YYbu+F6lgjLNPvo8DIcH3HhBI+e7uD8yWfADTpPSz9OdMEgNEGj8qloqKMAV6Q+2 lI27XPSZcVC2DpaHe0VvelUM8QZ2YcA8xx5Yfm2PGiZbBTWzs3aNvavtWiIi8ZHtJISu Fqj6eZNG3wHAe0ByZW5Din+yf635CIPAelCi30j0IhPj4H74hhqSJ2qS9bKyhbTxqARt npmQ== X-Gm-Message-State: AKGB3mJwPcr0aBd8121poMdW5eSUaHzs50zVB+yKBapUxkbwt5pKaL7N +54VGoJ384vVWQ0aA7BJbiwcfw== X-Google-Smtp-Source: ACJfBovTggC1kOZ0t2n1q7i+N2n8e/X2Ko5X6wnnpdRDhDTD59ltpewul9Vu+roOnKm/2zQDzPe9ow== X-Received: by 10.84.213.8 with SMTP id f8mr1489473pli.76.1513024757077; Mon, 11 Dec 2017 12:39:17 -0800 (PST) Received: from localhost.localdomain (67-207-98-108.static.wiline.com. [67.207.98.108]) by smtp.gmail.com with ESMTPSA id t6sm26426790pfl.76.2017.12.11.12.39.15 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Mon, 11 Dec 2017 12:39:16 -0800 (PST) From: Tom Herbert To: davem@davemloft.net Cc: netdev@vger.kernel.org, roopa@cumulusnetworks.com, rohit@quantonium.net, Tom Herbert Subject: [PATCH v3 net-next 7/9] ila: Resolver mechanism Date: Mon, 11 Dec 2017 12:38:35 -0800 Message-Id: <20171211203837.2540-8-tom@quantonium.net> X-Mailer: git-send-email 2.11.0 In-Reply-To: <20171211203837.2540-1-tom@quantonium.net> References: <20171211203837.2540-1-tom@quantonium.net> Sender: netdev-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org 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 --- 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 #include #include +#include #include #include #include @@ -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 #include #include +#include #include #include #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 + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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); +}