1241 lines
38 KiB
Diff
1241 lines
38 KiB
Diff
|
From e64058be3b97c5bd3e034fc4ece21e306ef6f90b Mon Sep 17 00:00:00 2001
|
||
|
From: Jay Vosburgh <jay.vosburgh@canonical.com>
|
||
|
Date: Wed, 1 Apr 2015 16:11:09 -0700
|
||
|
Subject: [PATCH] UBUNTU: SAUCE: fan: tunnel multiple mapping mode (v3)
|
||
|
|
||
|
Switch to a single tunnel for all mappings, this removes the limitations
|
||
|
on how many mappings each tunnel can handle, and therefore how many Fan
|
||
|
slices each local address may hold.
|
||
|
|
||
|
NOTE: This introduces a new kernel netlink interface which needs updated
|
||
|
iproute2 support.
|
||
|
|
||
|
BugLink: http://bugs.launchpad.net/bugs/1470091
|
||
|
Signed-off-by: Jay Vosburgh <jay.vosburgh@canonical.com>
|
||
|
Signed-off-by: Andy Whitcroft <apw@canonical.com>
|
||
|
Signed-off-by: Tim Gardner <tim.gardner@canonical.com>
|
||
|
|
||
|
Conflicts:
|
||
|
include/net/ip_tunnels.h
|
||
|
---
|
||
|
include/net/ip_tunnels.h | 15 ++++
|
||
|
include/uapi/linux/if_tunnel.h | 20 +++++
|
||
|
net/ipv4/ip_tunnel.c | 7 +-
|
||
|
net/ipv4/ipip.c | 186 +++++++++++++++++++++++++++++++++++++++--
|
||
|
4 files changed, 222 insertions(+), 6 deletions(-)
|
||
|
|
||
|
diff --git a/include/net/ip_tunnels.h b/include/net/ip_tunnels.h
|
||
|
index 62a750a..47fec59 100644
|
||
|
--- a/include/net/ip_tunnels.h
|
||
|
+++ b/include/net/ip_tunnels.h
|
||
|
@@ -91,6 +91,19 @@ struct ip_tunnel_dst {
|
||
|
};
|
||
|
|
||
|
struct metadata_dst;
|
||
|
+/* A fan overlay /8 (250.0.0.0/8, for example) maps to exactly one /16
|
||
|
+ * underlay (10.88.0.0/16, for example). Multiple local addresses within
|
||
|
+ * the /16 may be used, but a particular overlay may not span
|
||
|
+ * multiple underlay subnets.
|
||
|
+ *
|
||
|
+ * We store one underlay, indexed by the overlay's high order octet.
|
||
|
+ */
|
||
|
+#define FAN_OVERLAY_CNT 256
|
||
|
+
|
||
|
+struct ip_tunnel_fan {
|
||
|
+/* u32 __rcu *map;*/
|
||
|
+ u32 map[FAN_OVERLAY_CNT];
|
||
|
+};
|
||
|
|
||
|
struct ip_tunnel {
|
||
|
struct ip_tunnel __rcu *next;
|
||
|
@@ -123,6 +136,7 @@ struct ip_tunnel {
|
||
|
#endif
|
||
|
struct ip_tunnel_prl_entry __rcu *prl; /* potential router list */
|
||
|
unsigned int prl_count; /* # of entries in PRL */
|
||
|
+ struct ip_tunnel_fan fan;
|
||
|
int ip_tnl_net_id;
|
||
|
struct gro_cells gro_cells;
|
||
|
bool collect_md;
|
||
|
@@ -143,6 +157,7 @@ struct ip_tunnel {
|
||
|
#define TUNNEL_VXLAN_OPT __cpu_to_be16(0x1000)
|
||
|
|
||
|
#define TUNNEL_OPTIONS_PRESENT (TUNNEL_GENEVE_OPT | TUNNEL_VXLAN_OPT)
|
||
|
+#define TUNNEL_FAN __cpu_to_be16(0x4000)
|
||
|
|
||
|
struct tnl_ptk_info {
|
||
|
__be16 flags;
|
||
|
diff --git a/include/uapi/linux/if_tunnel.h b/include/uapi/linux/if_tunnel.h
|
||
|
index af4de90..85a3e4b 100644
|
||
|
--- a/include/uapi/linux/if_tunnel.h
|
||
|
+++ b/include/uapi/linux/if_tunnel.h
|
||
|
@@ -57,6 +57,10 @@ enum {
|
||
|
IFLA_IPTUN_ENCAP_FLAGS,
|
||
|
IFLA_IPTUN_ENCAP_SPORT,
|
||
|
IFLA_IPTUN_ENCAP_DPORT,
|
||
|
+
|
||
|
+ __IFLA_IPTUN_VENDOR_BREAK, /* Ensure new entries do not hit the below. */
|
||
|
+ IFLA_IPTUN_FAN_MAP = 33,
|
||
|
+
|
||
|
__IFLA_IPTUN_MAX,
|
||
|
};
|
||
|
#define IFLA_IPTUN_MAX (__IFLA_IPTUN_MAX - 1)
|
||
|
@@ -132,4 +136,20 @@ enum {
|
||
|
};
|
||
|
|
||
|
#define IFLA_VTI_MAX (__IFLA_VTI_MAX - 1)
|
||
|
+
|
||
|
+enum {
|
||
|
+ IFLA_FAN_UNSPEC,
|
||
|
+ IFLA_FAN_MAPPING,
|
||
|
+ __IFLA_FAN_MAX,
|
||
|
+};
|
||
|
+
|
||
|
+#define IFLA_FAN_MAX (__IFLA_FAN_MAX - 1)
|
||
|
+
|
||
|
+struct ip_tunnel_fan_map {
|
||
|
+ __be32 underlay;
|
||
|
+ __be32 overlay;
|
||
|
+ __u16 underlay_prefix;
|
||
|
+ __u16 overlay_prefix;
|
||
|
+};
|
||
|
+
|
||
|
#endif /* _UAPI_IF_TUNNEL_H_ */
|
||
|
diff --git a/net/ipv4/ip_tunnel.c b/net/ipv4/ip_tunnel.c
|
||
|
index cbb51f3..7a6174b 100644
|
||
|
--- a/net/ipv4/ip_tunnel.c
|
||
|
+++ b/net/ipv4/ip_tunnel.c
|
||
|
@@ -1110,6 +1110,11 @@ out:
|
||
|
}
|
||
|
EXPORT_SYMBOL_GPL(ip_tunnel_newlink);
|
||
|
|
||
|
+static int ip_tunnel_is_fan(struct ip_tunnel *tunnel)
|
||
|
+{
|
||
|
+ return tunnel->parms.i_flags & TUNNEL_FAN;
|
||
|
+}
|
||
|
+
|
||
|
int ip_tunnel_changelink(struct net_device *dev, struct nlattr *tb[],
|
||
|
struct ip_tunnel_parm *p)
|
||
|
{
|
||
|
@@ -1119,7 +1124,7 @@ int ip_tunnel_changelink(struct net_device *dev, struct nlattr *tb[],
|
||
|
struct ip_tunnel_net *itn = net_generic(net, tunnel->ip_tnl_net_id);
|
||
|
|
||
|
if (dev == itn->fb_tunnel_dev)
|
||
|
- return -EINVAL;
|
||
|
+ return ip_tunnel_is_fan(tunnel) ? 0 : -EINVAL;
|
||
|
|
||
|
t = ip_tunnel_find(itn, p, dev->type);
|
||
|
|
||
|
diff --git a/net/ipv4/ipip.c b/net/ipv4/ipip.c
|
||
|
index a09fb0d..56e8984 100644
|
||
|
--- a/net/ipv4/ipip.c
|
||
|
+++ b/net/ipv4/ipip.c
|
||
|
@@ -107,6 +107,7 @@
|
||
|
#include <linux/init.h>
|
||
|
#include <linux/netfilter_ipv4.h>
|
||
|
#include <linux/if_ether.h>
|
||
|
+#include <linux/inetdevice.h>
|
||
|
|
||
|
#include <net/sock.h>
|
||
|
#include <net/ip.h>
|
||
|
@@ -208,6 +209,40 @@ drop:
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
+static int ipip_tunnel_is_fan(struct ip_tunnel *tunnel)
|
||
|
+{
|
||
|
+ return tunnel->parms.i_flags & TUNNEL_FAN;
|
||
|
+}
|
||
|
+
|
||
|
+/*
|
||
|
+ * Determine fan tunnel endpoint to send packet to, based on the inner IP
|
||
|
+ * address. For an overlay (inner) address Y.A.B.C, the transformation is
|
||
|
+ * F.G.A.B, where "F" and "G" are the first two octets of the underlay
|
||
|
+ * network (the network portion of a /16), "A" and "B" are the low order
|
||
|
+ * two octets of the underlay network host (the host portion of a /16),
|
||
|
+ * and "Y" is a configured first octet of the overlay network.
|
||
|
+ *
|
||
|
+ * E.g., underlay host 10.88.3.4 with an overlay of 99 would host overlay
|
||
|
+ * subnet 99.3.4.0/24. An overlay network datagram from 99.3.4.5 to
|
||
|
+ * 99.6.7.8, would be directed to underlay host 10.88.6.7, which hosts
|
||
|
+ * overlay network 99.6.7.0/24.
|
||
|
+ */
|
||
|
+static int ipip_build_fan_iphdr(struct ip_tunnel *tunnel, struct sk_buff *skb, struct iphdr *iph)
|
||
|
+{
|
||
|
+ unsigned int overlay;
|
||
|
+ u32 daddr, underlay;
|
||
|
+
|
||
|
+ daddr = ntohl(ip_hdr(skb)->daddr);
|
||
|
+ overlay = daddr >> 24;
|
||
|
+ underlay = tunnel->fan.map[overlay];
|
||
|
+ if (!underlay)
|
||
|
+ return -EINVAL;
|
||
|
+
|
||
|
+ *iph = tunnel->parms.iph;
|
||
|
+ iph->daddr = htonl(underlay | ((daddr >> 8) & 0x0000ffff));
|
||
|
+ return 0;
|
||
|
+}
|
||
|
+
|
||
|
/*
|
||
|
* This function assumes it is being called from dev_queue_xmit()
|
||
|
* and that skb is filled properly by that function.
|
||
|
@@ -215,7 +250,8 @@ drop:
|
||
|
static netdev_tx_t ipip_tunnel_xmit(struct sk_buff *skb, struct net_device *dev)
|
||
|
{
|
||
|
struct ip_tunnel *tunnel = netdev_priv(dev);
|
||
|
- const struct iphdr *tiph = &tunnel->parms.iph;
|
||
|
+ const struct iphdr *tiph;
|
||
|
+ struct iphdr fiph;
|
||
|
|
||
|
if (unlikely(skb->protocol != htons(ETH_P_IP)))
|
||
|
goto tx_error;
|
||
|
@@ -224,6 +260,14 @@ static netdev_tx_t ipip_tunnel_xmit(struct sk_buff *skb, struct net_device *dev)
|
||
|
if (IS_ERR(skb))
|
||
|
goto out;
|
||
|
|
||
|
+ if (ipip_tunnel_is_fan(tunnel)) {
|
||
|
+ if (ipip_build_fan_iphdr(tunnel, skb, &fiph))
|
||
|
+ goto tx_error;
|
||
|
+ tiph = &fiph;
|
||
|
+ } else {
|
||
|
+ tiph = &tunnel->parms.iph;
|
||
|
+ }
|
||
|
+
|
||
|
skb_set_inner_ipproto(skb, IPPROTO_IPIP);
|
||
|
|
||
|
ip_tunnel_xmit(skb, dev, tiph, tiph->protocol);
|
||
|
@@ -375,21 +419,88 @@ static bool ipip_netlink_encap_parms(struct nlattr *data[],
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
+static void ipip_fan_free_map(struct ip_tunnel *t)
|
||
|
+{
|
||
|
+ memset(&t->fan.map, 0, sizeof(t->fan.map));
|
||
|
+}
|
||
|
+
|
||
|
+static int ipip_fan_set_map(struct ip_tunnel *t, struct ip_tunnel_fan_map *map)
|
||
|
+{
|
||
|
+ u32 overlay, overlay_mask, underlay, underlay_mask;
|
||
|
+
|
||
|
+ if ((map->underlay_prefix && map->underlay_prefix != 16) ||
|
||
|
+ (map->overlay_prefix && map->overlay_prefix != 8))
|
||
|
+ return -EINVAL;
|
||
|
+
|
||
|
+ overlay = ntohl(map->overlay);
|
||
|
+ overlay_mask = ntohl(inet_make_mask(map->overlay_prefix));
|
||
|
+
|
||
|
+ underlay = ntohl(map->underlay);
|
||
|
+ underlay_mask = ntohl(inet_make_mask(map->underlay_prefix));
|
||
|
+
|
||
|
+ if ((overlay & ~overlay_mask) || (underlay & ~underlay_mask))
|
||
|
+ return -EINVAL;
|
||
|
+
|
||
|
+ if (!(overlay & overlay_mask) && (underlay & underlay_mask))
|
||
|
+ return -EINVAL;
|
||
|
+
|
||
|
+ t->parms.i_flags |= TUNNEL_FAN;
|
||
|
+
|
||
|
+ /* Special case: overlay 0 and underlay 0 clears all mappings */
|
||
|
+ if (!overlay && !underlay) {
|
||
|
+ ipip_fan_free_map(t);
|
||
|
+ return 0;
|
||
|
+ }
|
||
|
+
|
||
|
+ overlay >>= (32 - map->overlay_prefix);
|
||
|
+ t->fan.map[overlay] = underlay;
|
||
|
+
|
||
|
+ return 0;
|
||
|
+}
|
||
|
+
|
||
|
+
|
||
|
+static int ipip_netlink_fan(struct nlattr *data[], struct ip_tunnel *t,
|
||
|
+ struct ip_tunnel_parm *parms)
|
||
|
+{
|
||
|
+ struct ip_tunnel_fan_map *map;
|
||
|
+ struct nlattr *attr;
|
||
|
+ int rem, rv;
|
||
|
+
|
||
|
+ if (!data[IFLA_IPTUN_FAN_MAP])
|
||
|
+ return 0;
|
||
|
+
|
||
|
+ if (parms->iph.daddr)
|
||
|
+ return -EINVAL;
|
||
|
+
|
||
|
+ nla_for_each_nested(attr, data[IFLA_IPTUN_FAN_MAP], rem) {
|
||
|
+ map = nla_data(attr);
|
||
|
+ rv = ipip_fan_set_map(t, map);
|
||
|
+ if (rv)
|
||
|
+ return rv;
|
||
|
+ }
|
||
|
+
|
||
|
+ return 0;
|
||
|
+}
|
||
|
+
|
||
|
static int ipip_newlink(struct net *src_net, struct net_device *dev,
|
||
|
struct nlattr *tb[], struct nlattr *data[])
|
||
|
{
|
||
|
struct ip_tunnel_parm p;
|
||
|
struct ip_tunnel_encap ipencap;
|
||
|
+ struct ip_tunnel *t = netdev_priv(dev);
|
||
|
+ int err;
|
||
|
|
||
|
if (ipip_netlink_encap_parms(data, &ipencap)) {
|
||
|
- struct ip_tunnel *t = netdev_priv(dev);
|
||
|
- int err = ip_tunnel_encap_setup(t, &ipencap);
|
||
|
+ err = ip_tunnel_encap_setup(t, &ipencap);
|
||
|
|
||
|
if (err < 0)
|
||
|
return err;
|
||
|
}
|
||
|
|
||
|
ipip_netlink_parms(data, &p);
|
||
|
+ err = ipip_netlink_fan(data, t, &p);
|
||
|
+ if (err < 0)
|
||
|
+ return err;
|
||
|
return ip_tunnel_newlink(dev, tb, &p);
|
||
|
}
|
||
|
|
||
|
@@ -398,16 +509,20 @@ static int ipip_changelink(struct net_device *dev, struct nlattr *tb[],
|
||
|
{
|
||
|
struct ip_tunnel_parm p;
|
||
|
struct ip_tunnel_encap ipencap;
|
||
|
+ struct ip_tunnel *t = netdev_priv(dev);
|
||
|
+ int err;
|
||
|
|
||
|
if (ipip_netlink_encap_parms(data, &ipencap)) {
|
||
|
- struct ip_tunnel *t = netdev_priv(dev);
|
||
|
- int err = ip_tunnel_encap_setup(t, &ipencap);
|
||
|
+ err = ip_tunnel_encap_setup(t, &ipencap);
|
||
|
|
||
|
if (err < 0)
|
||
|
return err;
|
||
|
}
|
||
|
|
||
|
ipip_netlink_parms(data, &p);
|
||
|
+ err = ipip_netlink_fan(data, t, &p);
|
||
|
+ if (err < 0)
|
||
|
+ return err;
|
||
|
|
||
|
if (((dev->flags & IFF_POINTOPOINT) && !p.iph.daddr) ||
|
||
|
(!(dev->flags & IFF_POINTOPOINT) && p.iph.daddr))
|
||
|
@@ -439,6 +554,8 @@ static size_t ipip_get_size(const struct net_device *dev)
|
||
|
nla_total_size(2) +
|
||
|
/* IFLA_IPTUN_ENCAP_DPORT */
|
||
|
nla_total_size(2) +
|
||
|
+ /* IFLA_IPTUN_FAN_MAP */
|
||
|
+ nla_total_size(sizeof(struct ip_tunnel_fan_map)) * 256 +
|
||
|
0;
|
||
|
}
|
||
|
|
||
|
@@ -466,6 +583,29 @@ static int ipip_fill_info(struct sk_buff *skb, const struct net_device *dev)
|
||
|
tunnel->encap.flags))
|
||
|
goto nla_put_failure;
|
||
|
|
||
|
+ if (tunnel->parms.i_flags & TUNNEL_FAN) {
|
||
|
+ struct nlattr *fan_nest;
|
||
|
+ int i;
|
||
|
+
|
||
|
+ fan_nest = nla_nest_start(skb, IFLA_IPTUN_FAN_MAP);
|
||
|
+ if (!fan_nest)
|
||
|
+ goto nla_put_failure;
|
||
|
+ for (i = 0; i < 256; i++) {
|
||
|
+ if (tunnel->fan.map[i]) {
|
||
|
+ struct ip_tunnel_fan_map map;
|
||
|
+
|
||
|
+ map.underlay = htonl(tunnel->fan.map[i]);
|
||
|
+ map.underlay_prefix = 16;
|
||
|
+ map.overlay = htonl(i << 24);
|
||
|
+ map.overlay_prefix = 8;
|
||
|
+ if (nla_put(skb, IFLA_FAN_MAPPING,
|
||
|
+ sizeof(map), &map))
|
||
|
+ goto nla_put_failure;
|
||
|
+ }
|
||
|
+ }
|
||
|
+ nla_nest_end(skb, fan_nest);
|
||
|
+ }
|
||
|
+
|
||
|
return 0;
|
||
|
|
||
|
nla_put_failure:
|
||
|
@@ -483,6 +623,9 @@ static const struct nla_policy ipip_policy[IFLA_IPTUN_MAX + 1] = {
|
||
|
[IFLA_IPTUN_ENCAP_FLAGS] = { .type = NLA_U16 },
|
||
|
[IFLA_IPTUN_ENCAP_SPORT] = { .type = NLA_U16 },
|
||
|
[IFLA_IPTUN_ENCAP_DPORT] = { .type = NLA_U16 },
|
||
|
+
|
||
|
+ [__IFLA_IPTUN_VENDOR_BREAK ... IFLA_IPTUN_MAX] = { .type = NLA_BINARY },
|
||
|
+ [IFLA_IPTUN_FAN_MAP] = { .type = NLA_NESTED },
|
||
|
};
|
||
|
|
||
|
static struct rtnl_link_ops ipip_link_ops __read_mostly = {
|
||
|
@@ -523,6 +666,23 @@ static struct pernet_operations ipip_net_ops = {
|
||
|
.size = sizeof(struct ip_tunnel_net),
|
||
|
};
|
||
|
|
||
|
+#ifdef CONFIG_SYSCTL
|
||
|
+static struct ctl_table_header *ipip_fan_header;
|
||
|
+static unsigned int ipip_fan_version = 3;
|
||
|
+
|
||
|
+static struct ctl_table ipip_fan_sysctls[] = {
|
||
|
+ {
|
||
|
+ .procname = "version",
|
||
|
+ .data = &ipip_fan_version,
|
||
|
+ .maxlen = sizeof(ipip_fan_version),
|
||
|
+ .mode = 0444,
|
||
|
+ .proc_handler = proc_dointvec,
|
||
|
+ },
|
||
|
+ {},
|
||
|
+};
|
||
|
+
|
||
|
+#endif /* CONFIG_SYSCTL */
|
||
|
+
|
||
|
static int __init ipip_init(void)
|
||
|
{
|
||
|
int err;
|
||
|
@@ -541,9 +701,22 @@ static int __init ipip_init(void)
|
||
|
if (err < 0)
|
||
|
goto rtnl_link_failed;
|
||
|
|
||
|
+#ifdef CONFIG_SYSCTL
|
||
|
+ ipip_fan_header = register_net_sysctl(&init_net, "net/fan",
|
||
|
+ ipip_fan_sysctls);
|
||
|
+ if (!ipip_fan_header) {
|
||
|
+ err = -ENOMEM;
|
||
|
+ goto sysctl_failed;
|
||
|
+ }
|
||
|
+#endif /* CONFIG_SYSCTL */
|
||
|
+
|
||
|
out:
|
||
|
return err;
|
||
|
|
||
|
+#ifdef CONFIG_SYSCTL
|
||
|
+sysctl_failed:
|
||
|
+ rtnl_link_unregister(&ipip_link_ops);
|
||
|
+#endif /* CONFIG_SYSCTL */
|
||
|
rtnl_link_failed:
|
||
|
xfrm4_tunnel_deregister(&ipip_handler, AF_INET);
|
||
|
xfrm_tunnel_failed:
|
||
|
@@ -553,6 +726,9 @@ xfrm_tunnel_failed:
|
||
|
|
||
|
static void __exit ipip_fini(void)
|
||
|
{
|
||
|
+#ifdef CONFIG_SYSCTL
|
||
|
+ unregister_net_sysctl_table(ipip_fan_header);
|
||
|
+#endif /* CONFIG_SYSCTL */
|
||
|
rtnl_link_unregister(&ipip_link_ops);
|
||
|
if (xfrm4_tunnel_deregister(&ipip_handler, AF_INET))
|
||
|
pr_info("%s: can't deregister tunnel\n", __func__);
|
||
|
--
|
||
|
2.7.4
|
||
|
|
||
|
From 14aba409d044e3a314c09c650e1c42de699700b8 Mon Sep 17 00:00:00 2001
|
||
|
From: Jay Vosburgh <jay.vosburgh@canonical.com>
|
||
|
Date: Wed, 11 Nov 2015 13:04:50 +0000
|
||
|
Subject: [PATCH] UBUNTU: SAUCE: fan: add VXLAN implementation
|
||
|
|
||
|
Generify the fan mapping support and utilise that to implement fan
|
||
|
mappings over vxlan transport.
|
||
|
|
||
|
Expose the existance of this functionality (when the module is loaded)
|
||
|
via an additional sysctl marker.
|
||
|
|
||
|
Signed-off-by: Jay Vosburgh <jay.vosburgh@canonical.com>
|
||
|
[apw@canonical.com: added feature marker for fan over vxlan.]
|
||
|
Signed-off-by: Andy Whitcroft <apw@canonical.com>
|
||
|
---
|
||
|
drivers/net/vxlan.c | 245 +++++++++++++++++++++++++++++++++++++++++
|
||
|
include/net/ip_tunnels.h | 19 +++-
|
||
|
include/net/vxlan.h | 2 +
|
||
|
include/uapi/linux/if_link.h | 1 +
|
||
|
include/uapi/linux/if_tunnel.h | 2 +-
|
||
|
net/ipv4/ip_tunnel.c | 7 +-
|
||
|
net/ipv4/ipip.c | 242 +++++++++++++++++++++++++++++++---------
|
||
|
7 files changed, 453 insertions(+), 65 deletions(-)
|
||
|
|
||
|
diff --git a/drivers/net/vxlan.c b/drivers/net/vxlan.c
|
||
|
index 405a7b6..a17cfd0 100644
|
||
|
--- a/drivers/net/vxlan.c
|
||
|
+++ b/drivers/net/vxlan.c
|
||
|
@@ -23,6 +23,7 @@
|
||
|
#include <linux/udp.h>
|
||
|
#include <linux/igmp.h>
|
||
|
#include <linux/etherdevice.h>
|
||
|
+#include <linux/inetdevice.h>
|
||
|
#include <linux/if_ether.h>
|
||
|
#include <linux/if_vlan.h>
|
||
|
#include <linux/hash.h>
|
||
|
@@ -106,6 +107,167 @@ static inline bool vxlan_collect_metadata(struct vxlan_sock *vs)
|
||
|
ip_tunnel_collect_metadata();
|
||
|
}
|
||
|
|
||
|
+static struct ip_fan_map *vxlan_fan_find_map(struct vxlan_dev *vxlan, __be32 daddr)
|
||
|
+{
|
||
|
+ struct ip_fan_map *fan_map;
|
||
|
+
|
||
|
+ rcu_read_lock();
|
||
|
+ list_for_each_entry_rcu(fan_map, &vxlan->fan.fan_maps, list) {
|
||
|
+ if (fan_map->overlay ==
|
||
|
+ (daddr & inet_make_mask(fan_map->overlay_prefix))) {
|
||
|
+ rcu_read_unlock();
|
||
|
+ return fan_map;
|
||
|
+ }
|
||
|
+ }
|
||
|
+ rcu_read_unlock();
|
||
|
+
|
||
|
+ return NULL;
|
||
|
+}
|
||
|
+
|
||
|
+static void vxlan_fan_flush_map(struct vxlan_dev *vxlan)
|
||
|
+{
|
||
|
+ struct ip_fan_map *fan_map;
|
||
|
+
|
||
|
+ list_for_each_entry_rcu(fan_map, &vxlan->fan.fan_maps, list) {
|
||
|
+ list_del_rcu(&fan_map->list);
|
||
|
+ kfree_rcu(fan_map, rcu);
|
||
|
+ }
|
||
|
+}
|
||
|
+
|
||
|
+static int vxlan_fan_del_map(struct vxlan_dev *vxlan, __be32 overlay)
|
||
|
+{
|
||
|
+ struct ip_fan_map *fan_map;
|
||
|
+
|
||
|
+ fan_map = vxlan_fan_find_map(vxlan, overlay);
|
||
|
+ if (!fan_map)
|
||
|
+ return -ENOENT;
|
||
|
+
|
||
|
+ list_del_rcu(&fan_map->list);
|
||
|
+ kfree_rcu(fan_map, rcu);
|
||
|
+
|
||
|
+ return 0;
|
||
|
+}
|
||
|
+
|
||
|
+static int vxlan_fan_add_map(struct vxlan_dev *vxlan, struct ifla_fan_map *map)
|
||
|
+{
|
||
|
+ __be32 overlay_mask, underlay_mask;
|
||
|
+ struct ip_fan_map *fan_map;
|
||
|
+
|
||
|
+ overlay_mask = inet_make_mask(map->overlay_prefix);
|
||
|
+ underlay_mask = inet_make_mask(map->underlay_prefix);
|
||
|
+
|
||
|
+ netdev_dbg(vxlan->dev, "vfam: map: o %x/%d u %x/%d om %x um %x\n",
|
||
|
+ map->overlay, map->overlay_prefix,
|
||
|
+ map->underlay, map->underlay_prefix,
|
||
|
+ overlay_mask, underlay_mask);
|
||
|
+
|
||
|
+ if ((map->overlay & ~overlay_mask) || (map->underlay & ~underlay_mask))
|
||
|
+ return -EINVAL;
|
||
|
+
|
||
|
+ if (!(map->overlay & overlay_mask) && (map->underlay & underlay_mask))
|
||
|
+ return -EINVAL;
|
||
|
+
|
||
|
+ /* Special case: overlay 0 and underlay 0: flush all mappings */
|
||
|
+ if (!map->overlay && !map->underlay) {
|
||
|
+ vxlan_fan_flush_map(vxlan);
|
||
|
+ return 0;
|
||
|
+ }
|
||
|
+
|
||
|
+ /* Special case: overlay set and underlay 0: clear map for overlay */
|
||
|
+ if (!map->underlay)
|
||
|
+ return vxlan_fan_del_map(vxlan, map->overlay);
|
||
|
+
|
||
|
+ if (vxlan_fan_find_map(vxlan, map->overlay))
|
||
|
+ return -EEXIST;
|
||
|
+
|
||
|
+ fan_map = kmalloc(sizeof(*fan_map), GFP_KERNEL);
|
||
|
+ fan_map->underlay = map->underlay;
|
||
|
+ fan_map->overlay = map->overlay;
|
||
|
+ fan_map->underlay_prefix = map->underlay_prefix;
|
||
|
+ fan_map->overlay_mask = ntohl(overlay_mask);
|
||
|
+ fan_map->overlay_prefix = map->overlay_prefix;
|
||
|
+
|
||
|
+ list_add_tail_rcu(&fan_map->list, &vxlan->fan.fan_maps);
|
||
|
+
|
||
|
+ return 0;
|
||
|
+}
|
||
|
+
|
||
|
+static int vxlan_parse_fan_map(struct nlattr *data[], struct vxlan_dev *vxlan)
|
||
|
+{
|
||
|
+ struct ifla_fan_map *map;
|
||
|
+ struct nlattr *attr;
|
||
|
+ int rem, rv;
|
||
|
+
|
||
|
+ nla_for_each_nested(attr, data[IFLA_IPTUN_FAN_MAP], rem) {
|
||
|
+ map = nla_data(attr);
|
||
|
+ rv = vxlan_fan_add_map(vxlan, map);
|
||
|
+ if (rv)
|
||
|
+ return rv;
|
||
|
+ }
|
||
|
+
|
||
|
+ return 0;
|
||
|
+}
|
||
|
+
|
||
|
+static int vxlan_fan_build_rdst(struct vxlan_dev *vxlan, struct sk_buff *skb,
|
||
|
+ struct vxlan_rdst *fan_rdst)
|
||
|
+{
|
||
|
+ struct ip_fan_map *f_map;
|
||
|
+ union vxlan_addr *va;
|
||
|
+ u32 daddr, underlay;
|
||
|
+ struct arphdr *arp;
|
||
|
+ void *arp_ptr;
|
||
|
+ struct ethhdr *eth;
|
||
|
+ struct iphdr *iph;
|
||
|
+
|
||
|
+ eth = eth_hdr(skb);
|
||
|
+ switch (eth->h_proto) {
|
||
|
+ case htons(ETH_P_IP):
|
||
|
+ iph = ip_hdr(skb);
|
||
|
+ if (!iph)
|
||
|
+ return -EINVAL;
|
||
|
+ daddr = iph->daddr;
|
||
|
+ break;
|
||
|
+ case htons(ETH_P_ARP):
|
||
|
+ arp = arp_hdr(skb);
|
||
|
+ if (!arp)
|
||
|
+ return -EINVAL;
|
||
|
+ arp_ptr = arp + 1;
|
||
|
+ netdev_dbg(vxlan->dev,
|
||
|
+ "vfbr: arp sha %pM sip %pI4 tha %pM tip %pI4\n",
|
||
|
+ arp_ptr, arp_ptr + skb->dev->addr_len,
|
||
|
+ arp_ptr + skb->dev->addr_len + 4,
|
||
|
+ arp_ptr + (skb->dev->addr_len * 2) + 4);
|
||
|
+ arp_ptr += (skb->dev->addr_len * 2) + 4;
|
||
|
+ memcpy(&daddr, arp_ptr, 4);
|
||
|
+ break;
|
||
|
+ default:
|
||
|
+ netdev_dbg(vxlan->dev, "vfbr: unknown eth p %x\n", eth->h_proto);
|
||
|
+ return -EINVAL;
|
||
|
+ }
|
||
|
+
|
||
|
+ f_map = vxlan_fan_find_map(vxlan, daddr);
|
||
|
+ if (!f_map)
|
||
|
+ return -EINVAL;
|
||
|
+
|
||
|
+ daddr = ntohl(daddr);
|
||
|
+ underlay = ntohl(f_map->underlay);
|
||
|
+ if (!underlay)
|
||
|
+ return -EINVAL;
|
||
|
+
|
||
|
+ memset(fan_rdst, 0, sizeof(*fan_rdst));
|
||
|
+ va = &fan_rdst->remote_ip;
|
||
|
+ va->sa.sa_family = AF_INET;
|
||
|
+ fan_rdst->remote_vni = vxlan->default_dst.remote_vni;
|
||
|
+ va->sin.sin_addr.s_addr = htonl(underlay |
|
||
|
+ ((daddr & ~f_map->overlay_mask) >>
|
||
|
+ (32 - f_map->overlay_prefix -
|
||
|
+ (32 - f_map->underlay_prefix))));
|
||
|
+ netdev_dbg(vxlan->dev, "vfbr: daddr %x ul %x dst %x\n",
|
||
|
+ daddr, underlay, va->sin.sin_addr.s_addr);
|
||
|
+
|
||
|
+ return 0;
|
||
|
+}
|
||
|
+
|
||
|
#if IS_ENABLED(CONFIG_IPV6)
|
||
|
static inline
|
||
|
bool vxlan_addr_equal(const union vxlan_addr *a, const union vxlan_addr *b)
|
||
|
@@ -2029,6 +2191,13 @@ static void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev,
|
||
|
goto rt_tx_error;
|
||
|
}
|
||
|
|
||
|
+ if (fan_has_map(&vxlan->fan) && rt->rt_flags & RTCF_LOCAL) {
|
||
|
+ netdev_dbg(dev, "discard fan to localhost %pI4\n",
|
||
|
+ &dst->sin.sin_addr.s_addr);
|
||
|
+ ip_rt_put(rt);
|
||
|
+ goto tx_free;
|
||
|
+ }
|
||
|
+
|
||
|
/* Bypass encapsulation if the destination is local */
|
||
|
if (rt->rt_flags & RTCF_LOCAL &&
|
||
|
!(rt->rt_flags & (RTCF_BROADCAST | RTCF_MULTICAST))) {
|
||
|
@@ -2169,6 +2338,20 @@ static netdev_tx_t vxlan_xmit(struct sk_buff *skb, struct net_device *dev)
|
||
|
return NETDEV_TX_OK;
|
||
|
}
|
||
|
|
||
|
+ if (fan_has_map(&vxlan->fan)) {
|
||
|
+ struct vxlan_rdst fan_rdst;
|
||
|
+
|
||
|
+ netdev_dbg(vxlan->dev, "vxlan_xmit p %x d %pM\n",
|
||
|
+ eth->h_proto, eth->h_dest);
|
||
|
+ if (vxlan_fan_build_rdst(vxlan, skb, &fan_rdst)) {
|
||
|
+ dev->stats.tx_dropped++;
|
||
|
+ kfree_skb(skb);
|
||
|
+ return NETDEV_TX_OK;
|
||
|
+ }
|
||
|
+ vxlan_xmit_one(skb, dev, &fan_rdst, 0);
|
||
|
+ return NETDEV_TX_OK;
|
||
|
+ }
|
||
|
+
|
||
|
f = vxlan_find_mac(vxlan, eth->h_dest);
|
||
|
did_rsc = false;
|
||
|
|
||
|
@@ -2532,6 +2715,8 @@ static void vxlan_setup(struct net_device *dev)
|
||
|
|
||
|
for (h = 0; h < FDB_HASH_SIZE; ++h)
|
||
|
INIT_HLIST_HEAD(&vxlan->fdb_head[h]);
|
||
|
+
|
||
|
+ INIT_LIST_HEAD(&vxlan->fan.fan_maps);
|
||
|
}
|
||
|
|
||
|
static const struct nla_policy vxlan_policy[IFLA_VXLAN_MAX + 1] = {
|
||
|
@@ -2881,6 +3066,7 @@ EXPORT_SYMBOL_GPL(vxlan_dev_create);
|
||
|
static int vxlan_newlink(struct net *src_net, struct net_device *dev,
|
||
|
struct nlattr *tb[], struct nlattr *data[])
|
||
|
{
|
||
|
+ struct vxlan_dev *vxlan = netdev_priv(dev);
|
||
|
struct vxlan_config conf;
|
||
|
int err;
|
||
|
|
||
|
@@ -2899,6 +3085,12 @@ static int vxlan_newlink(struct net *src_net, struct net_device *dev,
|
||
|
conf.remote_ip.sa.sa_family = AF_INET6;
|
||
|
}
|
||
|
|
||
|
+ if (data[IFLA_VXLAN_FAN_MAP]) {
|
||
|
+ err = vxlan_parse_fan_map(data, vxlan);
|
||
|
+ if (err)
|
||
|
+ return err;
|
||
|
+ }
|
||
|
+
|
||
|
if (data[IFLA_VXLAN_LOCAL]) {
|
||
|
conf.saddr.sin.sin_addr.s_addr = nla_get_in_addr(data[IFLA_VXLAN_LOCAL]);
|
||
|
conf.saddr.sa.sa_family = AF_INET;
|
||
|
@@ -3037,6 +3229,7 @@ static size_t vxlan_get_size(const struct net_device *dev)
|
||
|
nla_total_size(sizeof(__u8)) + /* IFLA_VXLAN_UDP_ZERO_CSUM6_RX */
|
||
|
nla_total_size(sizeof(__u8)) + /* IFLA_VXLAN_REMCSUM_TX */
|
||
|
nla_total_size(sizeof(__u8)) + /* IFLA_VXLAN_REMCSUM_RX */
|
||
|
+ nla_total_size(sizeof(struct ip_fan_map) * 256) +
|
||
|
0;
|
||
|
}
|
||
|
|
||
|
@@ -3083,6 +3276,26 @@ static int vxlan_fill_info(struct sk_buff *skb, const struct net_device *dev)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
+ if (fan_has_map(&vxlan->fan)) {
|
||
|
+ struct nlattr *fan_nest;
|
||
|
+ struct ip_fan_map *fan_map;
|
||
|
+
|
||
|
+ fan_nest = nla_nest_start(skb, IFLA_VXLAN_FAN_MAP);
|
||
|
+ if (!fan_nest)
|
||
|
+ goto nla_put_failure;
|
||
|
+ list_for_each_entry_rcu(fan_map, &vxlan->fan.fan_maps, list) {
|
||
|
+ struct ifla_fan_map map;
|
||
|
+
|
||
|
+ map.underlay = fan_map->underlay;
|
||
|
+ map.underlay_prefix = fan_map->underlay_prefix;
|
||
|
+ map.overlay = fan_map->overlay;
|
||
|
+ map.overlay_prefix = fan_map->overlay_prefix;
|
||
|
+ if (nla_put(skb, IFLA_FAN_MAPPING, sizeof(map), &map))
|
||
|
+ goto nla_put_failure;
|
||
|
+ }
|
||
|
+ nla_nest_end(skb, fan_nest);
|
||
|
+ }
|
||
|
+
|
||
|
if (nla_put_u8(skb, IFLA_VXLAN_TTL, vxlan->cfg.ttl) ||
|
||
|
nla_put_u8(skb, IFLA_VXLAN_TOS, vxlan->cfg.tos) ||
|
||
|
nla_put_u8(skb, IFLA_VXLAN_LEARNING,
|
||
|
@@ -3201,6 +3414,22 @@ static __net_init int vxlan_init_net(struct net *net)
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
+#ifdef CONFIG_SYSCTL
|
||
|
+static struct ctl_table_header *vxlan_fan_header;
|
||
|
+static unsigned int vxlan_fan_version = 4;
|
||
|
+
|
||
|
+static struct ctl_table vxlan_fan_sysctls[] = {
|
||
|
+ {
|
||
|
+ .procname = "vxlan",
|
||
|
+ .data = &vxlan_fan_version,
|
||
|
+ .maxlen = sizeof(vxlan_fan_version),
|
||
|
+ .mode = 0444,
|
||
|
+ .proc_handler = proc_dointvec,
|
||
|
+ },
|
||
|
+ {},
|
||
|
+};
|
||
|
+#endif /* CONFIG_SYSCTL */
|
||
|
+
|
||
|
static void __net_exit vxlan_exit_net(struct net *net)
|
||
|
{
|
||
|
struct vxlan_net *vn = net_generic(net, vxlan_net_id);
|
||
|
@@ -3256,7 +3485,20 @@ static int __init vxlan_init_module(void)
|
||
|
if (rc)
|
||
|
goto out3;
|
||
|
|
||
|
+#ifdef CONFIG_SYSCTL
|
||
|
+ vxlan_fan_header = register_net_sysctl(&init_net, "net/fan",
|
||
|
+ vxlan_fan_sysctls);
|
||
|
+ if (!vxlan_fan_header) {
|
||
|
+ rc = -ENOMEM;
|
||
|
+ goto sysctl_failed;
|
||
|
+ }
|
||
|
+#endif /* CONFIG_SYSCTL */
|
||
|
+
|
||
|
return 0;
|
||
|
+#ifdef CONFIG_SYSCTL
|
||
|
+sysctl_failed:
|
||
|
+ rtnl_link_unregister(&vxlan_link_ops);
|
||
|
+#endif /* CONFIG_SYSCTL */
|
||
|
out3:
|
||
|
unregister_netdevice_notifier(&vxlan_notifier_block);
|
||
|
out2:
|
||
|
@@ -3269,6 +3511,9 @@ late_initcall(vxlan_init_module);
|
||
|
|
||
|
static void __exit vxlan_cleanup_module(void)
|
||
|
{
|
||
|
+#ifdef CONFIG_SYSCTL
|
||
|
+ unregister_net_sysctl_table(vxlan_fan_header);
|
||
|
+#endif /* CONFIG_SYSCTL */
|
||
|
rtnl_link_unregister(&vxlan_link_ops);
|
||
|
unregister_netdevice_notifier(&vxlan_notifier_block);
|
||
|
destroy_workqueue(vxlan_wq);
|
||
|
diff --git a/include/net/ip_tunnels.h b/include/net/ip_tunnels.h
|
||
|
index 47fec59..28a38e5 100644
|
||
|
--- a/include/net/ip_tunnels.h
|
||
|
+++ b/include/net/ip_tunnels.h
|
||
|
@@ -100,9 +100,18 @@ struct metadata_dst;
|
||
|
*/
|
||
|
#define FAN_OVERLAY_CNT 256
|
||
|
|
||
|
+struct ip_fan_map {
|
||
|
+ __be32 underlay;
|
||
|
+ __be32 overlay;
|
||
|
+ u16 underlay_prefix;
|
||
|
+ u16 overlay_prefix;
|
||
|
+ u32 overlay_mask;
|
||
|
+ struct list_head list;
|
||
|
+ struct rcu_head rcu;
|
||
|
+};
|
||
|
+
|
||
|
struct ip_tunnel_fan {
|
||
|
-/* u32 __rcu *map;*/
|
||
|
- u32 map[FAN_OVERLAY_CNT];
|
||
|
+ struct list_head fan_maps;
|
||
|
};
|
||
|
|
||
|
struct ip_tunnel {
|
||
|
@@ -157,7 +166,11 @@ struct ip_tunnel {
|
||
|
#define TUNNEL_VXLAN_OPT __cpu_to_be16(0x1000)
|
||
|
|
||
|
#define TUNNEL_OPTIONS_PRESENT (TUNNEL_GENEVE_OPT | TUNNEL_VXLAN_OPT)
|
||
|
-#define TUNNEL_FAN __cpu_to_be16(0x4000)
|
||
|
+
|
||
|
+static inline int fan_has_map(const struct ip_tunnel_fan *fan)
|
||
|
+{
|
||
|
+ return !list_empty(&fan->fan_maps);
|
||
|
+}
|
||
|
|
||
|
struct tnl_ptk_info {
|
||
|
__be16 flags;
|
||
|
diff --git a/include/net/vxlan.h b/include/net/vxlan.h
|
||
|
index e289ada..542f421 100644
|
||
|
--- a/include/net/vxlan.h
|
||
|
+++ b/include/net/vxlan.h
|
||
|
@@ -161,6 +161,8 @@ struct vxlan_dev {
|
||
|
struct vxlan_rdst default_dst; /* default destination */
|
||
|
u32 flags; /* VXLAN_F_* in vxlan.h */
|
||
|
|
||
|
+ struct ip_tunnel_fan fan;
|
||
|
+
|
||
|
struct timer_list age_timer;
|
||
|
spinlock_t hash_lock;
|
||
|
unsigned int addrcnt;
|
||
|
diff --git a/include/uapi/linux/if_link.h b/include/uapi/linux/if_link.h
|
||
|
index 5ad5737..6cde3bf 100644
|
||
|
--- a/include/uapi/linux/if_link.h
|
||
|
+++ b/include/uapi/linux/if_link.h
|
||
|
@@ -443,6 +443,7 @@ enum {
|
||
|
IFLA_VXLAN_GBP,
|
||
|
IFLA_VXLAN_REMCSUM_NOPARTIAL,
|
||
|
IFLA_VXLAN_COLLECT_METADATA,
|
||
|
+ IFLA_VXLAN_FAN_MAP = 33,
|
||
|
__IFLA_VXLAN_MAX
|
||
|
};
|
||
|
#define IFLA_VXLAN_MAX (__IFLA_VXLAN_MAX - 1)
|
||
|
diff --git a/include/uapi/linux/if_tunnel.h b/include/uapi/linux/if_tunnel.h
|
||
|
index 85a3e4b..d36b150 100644
|
||
|
--- a/include/uapi/linux/if_tunnel.h
|
||
|
+++ b/include/uapi/linux/if_tunnel.h
|
||
|
@@ -145,7 +145,7 @@ enum {
|
||
|
|
||
|
#define IFLA_FAN_MAX (__IFLA_FAN_MAX - 1)
|
||
|
|
||
|
-struct ip_tunnel_fan_map {
|
||
|
+struct ifla_fan_map {
|
||
|
__be32 underlay;
|
||
|
__be32 overlay;
|
||
|
__u16 underlay_prefix;
|
||
|
diff --git a/net/ipv4/ip_tunnel.c b/net/ipv4/ip_tunnel.c
|
||
|
index 7a6174b..c821bf1 100644
|
||
|
--- a/net/ipv4/ip_tunnel.c
|
||
|
+++ b/net/ipv4/ip_tunnel.c
|
||
|
@@ -1110,11 +1110,6 @@ out:
|
||
|
}
|
||
|
EXPORT_SYMBOL_GPL(ip_tunnel_newlink);
|
||
|
|
||
|
-static int ip_tunnel_is_fan(struct ip_tunnel *tunnel)
|
||
|
-{
|
||
|
- return tunnel->parms.i_flags & TUNNEL_FAN;
|
||
|
-}
|
||
|
-
|
||
|
int ip_tunnel_changelink(struct net_device *dev, struct nlattr *tb[],
|
||
|
struct ip_tunnel_parm *p)
|
||
|
{
|
||
|
@@ -1124,7 +1119,7 @@ int ip_tunnel_changelink(struct net_device *dev, struct nlattr *tb[],
|
||
|
struct ip_tunnel_net *itn = net_generic(net, tunnel->ip_tnl_net_id);
|
||
|
|
||
|
if (dev == itn->fb_tunnel_dev)
|
||
|
- return ip_tunnel_is_fan(tunnel) ? 0 : -EINVAL;
|
||
|
+ return fan_has_map(&tunnel->fan) ? 0 : -EINVAL;
|
||
|
|
||
|
t = ip_tunnel_find(itn, p, dev->type);
|
||
|
|
||
|
diff --git a/net/ipv4/ipip.c b/net/ipv4/ipip.c
|
||
|
index 56e8984..3877b0e 100644
|
||
|
--- a/net/ipv4/ipip.c
|
||
|
+++ b/net/ipv4/ipip.c
|
||
|
@@ -108,6 +108,7 @@
|
||
|
#include <linux/netfilter_ipv4.h>
|
||
|
#include <linux/if_ether.h>
|
||
|
#include <linux/inetdevice.h>
|
||
|
+#include <linux/rculist.h>
|
||
|
|
||
|
#include <net/sock.h>
|
||
|
#include <net/ip.h>
|
||
|
@@ -209,37 +210,144 @@ drop:
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
-static int ipip_tunnel_is_fan(struct ip_tunnel *tunnel)
|
||
|
+static struct ip_fan_map *ipip_fan_find_map(struct ip_tunnel *t, __be32 daddr)
|
||
|
{
|
||
|
- return tunnel->parms.i_flags & TUNNEL_FAN;
|
||
|
+ struct ip_fan_map *fan_map;
|
||
|
+
|
||
|
+ rcu_read_lock();
|
||
|
+ list_for_each_entry_rcu(fan_map, &t->fan.fan_maps, list) {
|
||
|
+ if (fan_map->overlay ==
|
||
|
+ (daddr & inet_make_mask(fan_map->overlay_prefix))) {
|
||
|
+ rcu_read_unlock();
|
||
|
+ return fan_map;
|
||
|
+ }
|
||
|
+ }
|
||
|
+ rcu_read_unlock();
|
||
|
+
|
||
|
+ return NULL;
|
||
|
}
|
||
|
|
||
|
-/*
|
||
|
- * Determine fan tunnel endpoint to send packet to, based on the inner IP
|
||
|
- * address. For an overlay (inner) address Y.A.B.C, the transformation is
|
||
|
- * F.G.A.B, where "F" and "G" are the first two octets of the underlay
|
||
|
- * network (the network portion of a /16), "A" and "B" are the low order
|
||
|
- * two octets of the underlay network host (the host portion of a /16),
|
||
|
- * and "Y" is a configured first octet of the overlay network.
|
||
|
+/* Determine fan tunnel endpoint to send packet to, based on the inner IP
|
||
|
+ * address.
|
||
|
+ *
|
||
|
+ * Given a /8 overlay and /16 underlay, for an overlay (inner) address
|
||
|
+ * Y.A.B.C, the transformation is F.G.A.B, where "F" and "G" are the first
|
||
|
+ * two octets of the underlay network (the network portion of a /16), "A"
|
||
|
+ * and "B" are the low order two octets of the underlay network host (the
|
||
|
+ * host portion of a /16), and "Y" is a configured first octet of the
|
||
|
+ * overlay network.
|
||
|
+ *
|
||
|
+ * E.g., underlay host 10.88.3.4/16 with an overlay of 99.0.0.0/8 would
|
||
|
+ * host overlay subnet 99.3.4.0/24. An overlay network datagram from
|
||
|
+ * 99.3.4.5 to 99.6.7.8, would be directed to underlay host 10.88.6.7,
|
||
|
+ * which hosts overlay network subnet 99.6.7.0/24. This transformation is
|
||
|
+ * described in detail further below.
|
||
|
+ *
|
||
|
+ * Using netmasks for the overlay and underlay other than /8 and /16, as
|
||
|
+ * shown above, can yield larger (or smaller) overlay subnets, with the
|
||
|
+ * trade-off of allowing fewer (or more) underlay hosts to participate.
|
||
|
+ *
|
||
|
+ * The size of each overlay network subnet is defined by the total of the
|
||
|
+ * network mask of the overlay plus the size of host portion of the
|
||
|
+ * underlay network. In the above example, /8 + /16 = /24.
|
||
|
+ *
|
||
|
+ * E.g., consider underlay host 10.99.238.5/20 and overlay 99.0.0.0/8. In
|
||
|
+ * this case, the network portion of the underlay is 10.99.224.0/20, and
|
||
|
+ * the host portion is 0.0.14.5 (12 bits). To determine the overlay
|
||
|
+ * network subnet, the 12 bits of host portion are left shifted 12 bits
|
||
|
+ * (/20 - /8) and ORed with the overlay subnet prefix. This yields an
|
||
|
+ * overlay subnet of 99.224.80/20, composed of 8 bits overlay, followed by
|
||
|
+ * 12 bits underlay. This yields 12 bits in the overlay network portion,
|
||
|
+ * allowing for 4094 addresses in each overlay network subnet. The
|
||
|
+ * trade-off is that fewer hosts may participate in the underlay network,
|
||
|
+ * as its host address size has shrunk from 16 bits (65534 addresses) in
|
||
|
+ * the first example to 12 bits (4094 addresses) here.
|
||
|
+ *
|
||
|
+ * For fewer hosts per overlay subnet (permitting a larger number of
|
||
|
+ * underlay hosts to participate), the underlay netmask may be made
|
||
|
+ * smaller.
|
||
|
+ *
|
||
|
+ * E.g., underlay host 10.111.1.2/12 (network 10.96.0.0/12, host portion
|
||
|
+ * is 0.15.1.2, 20 bits) with an overlay of 33.0.0.0/8 would left shift
|
||
|
+ * the 20 bits of host by 4 (so that it's highest order bit is adjacent to
|
||
|
+ * the lowest order bit of the /8 overlay). This yields an overlay subnet
|
||
|
+ * of 33.240.16.32/28 (8 bits overlay, 20 bits from the host portion of
|
||
|
+ * the underlay). This provides more addresses for the underlay network
|
||
|
+ * (approximately 2^20), but each host's segment of the overlay provides
|
||
|
+ * only 4 bits of addresses (14 usable).
|
||
|
+ *
|
||
|
+ * It is also possible to adjust the overlay subnet.
|
||
|
+ *
|
||
|
+ * For an overlay of 240.0.0.0/5 and underlay of 10.88.0.0/20, consider
|
||
|
+ * underlay host 10.88.129.2; the 12 bits of host, 0.0.1.2, are left
|
||
|
+ * shifted 15 bits (/20 - /5), yielding an overlay network of
|
||
|
+ * 240.129.0.0/17. An underlay host of 10.88.244.215 would yield an
|
||
|
+ * overlay network of 242.107.128.0/17.
|
||
|
+ *
|
||
|
+ * For an overlay of 100.64.0.0/10 and underlay of 10.224.220.0/24, for
|
||
|
+ * underlay host 10.224.220.10, the underlay host portion (.10) is left
|
||
|
+ * shifted 14 bits, yielding an overlay network subnet of 100.66.128.0/18.
|
||
|
+ * This would permit 254 addresses on the underlay, with each overlay
|
||
|
+ * segment providing approximately 2^14 - 2 addresses (16382).
|
||
|
+ *
|
||
|
+ * For packets being encapsulated, the overlay network destination IP
|
||
|
+ * address is deconstructed into its overlay and underlay-derived
|
||
|
+ * portions. The underlay portion (determined by the overlay mask and
|
||
|
+ * overlay subnet mask) is right shifted according to the size of the
|
||
|
+ * underlay network mask. This value is then ORed with the network
|
||
|
+ * portion of the underlay network to produce the underlay network
|
||
|
+ * destination for the encapsulated datagram.
|
||
|
+ *
|
||
|
+ * For example, using the initial example of underlay 10.88.3.4/16 and
|
||
|
+ * overlay 99.0.0.0/8, with underlay host 10.88.3.4/16 providing overlay
|
||
|
+ * subnet 99.3.4.0/24 with specfic host 99.3.4.5. A datagram from
|
||
|
+ * 99.3.4.5 to 99.6.7.8 would first have the underlay host derived portion
|
||
|
+ * of the address extracted. This is a number of bits equal to underlay
|
||
|
+ * network host portion. In the destination address, the highest order of
|
||
|
+ * these bits is one bit lower than the lowest order bit from the overlay
|
||
|
+ * network mask.
|
||
|
+ *
|
||
|
+ * Using the sample value, 99.6.7.8, the overlay mask is /8, and the
|
||
|
+ * underlay mask is /16 (leaving 16 bits for the host portion). The bits
|
||
|
+ * to be shifted are the middle two octets, 0.6.7.0, as this is 99.6.7.8
|
||
|
+ * ANDed with the mask 0x00ffff00 (which is 16 bits, the highest order of
|
||
|
+ * which is 1 bit lower than the lowest order overlay address bit).
|
||
|
*
|
||
|
- * E.g., underlay host 10.88.3.4 with an overlay of 99 would host overlay
|
||
|
- * subnet 99.3.4.0/24. An overlay network datagram from 99.3.4.5 to
|
||
|
- * 99.6.7.8, would be directed to underlay host 10.88.6.7, which hosts
|
||
|
- * overlay network 99.6.7.0/24.
|
||
|
+ * These octets, 0.6.7.0, are then right shifted 8 bits, yielding 0.0.6.7.
|
||
|
+ * This value is then ORed with the underlay network portion,
|
||
|
+ * 10.88.0.0/16, providing 10.88.6.7 as the final underlay destination for
|
||
|
+ * the encapuslated datagram.
|
||
|
+ *
|
||
|
+ * Another transform using the final example: overlay 100.64.0.0/10 and
|
||
|
+ * underlay 10.224.220.0/24. Consider overlay address 100.66.128.1
|
||
|
+ * sending a datagram to 100.66.200.5. In this case, 8 bits (the host
|
||
|
+ * portion size of 10.224.220.0/24) beginning after the 100.64/10 overlay
|
||
|
+ * prefix are masked off, yielding 0.2.192.0. This is right shifted 14
|
||
|
+ * (32 - 10 - (32 - 24), i.e., the number of bits between the overlay
|
||
|
+ * network portion and the underlay host portion) bits, yielding 0.0.0.11.
|
||
|
+ * This is ORed with the underlay network portion, 10.224.220.0/24, giving
|
||
|
+ * the underlay destination of 10.224.220.11 for overlay destination
|
||
|
+ * 100.66.200.5.
|
||
|
*/
|
||
|
static int ipip_build_fan_iphdr(struct ip_tunnel *tunnel, struct sk_buff *skb, struct iphdr *iph)
|
||
|
{
|
||
|
- unsigned int overlay;
|
||
|
+ struct ip_fan_map *f_map;
|
||
|
u32 daddr, underlay;
|
||
|
|
||
|
+ f_map = ipip_fan_find_map(tunnel, ip_hdr(skb)->daddr);
|
||
|
+ if (!f_map)
|
||
|
+ return -ENOENT;
|
||
|
+
|
||
|
daddr = ntohl(ip_hdr(skb)->daddr);
|
||
|
- overlay = daddr >> 24;
|
||
|
- underlay = tunnel->fan.map[overlay];
|
||
|
+ underlay = ntohl(f_map->underlay);
|
||
|
if (!underlay)
|
||
|
return -EINVAL;
|
||
|
|
||
|
*iph = tunnel->parms.iph;
|
||
|
- iph->daddr = htonl(underlay | ((daddr >> 8) & 0x0000ffff));
|
||
|
+ iph->daddr = htonl(underlay |
|
||
|
+ ((daddr & ~f_map->overlay_mask) >>
|
||
|
+ (32 - f_map->overlay_prefix -
|
||
|
+ (32 - f_map->underlay_prefix))));
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
@@ -260,7 +368,7 @@ static netdev_tx_t ipip_tunnel_xmit(struct sk_buff *skb, struct net_device *dev)
|
||
|
if (IS_ERR(skb))
|
||
|
goto out;
|
||
|
|
||
|
- if (ipip_tunnel_is_fan(tunnel)) {
|
||
|
+ if (fan_has_map(&tunnel->fan)) {
|
||
|
if (ipip_build_fan_iphdr(tunnel, skb, &fiph))
|
||
|
goto tx_error;
|
||
|
tiph = &fiph;
|
||
|
@@ -325,6 +433,8 @@ static const struct net_device_ops ipip_netdev_ops = {
|
||
|
|
||
|
static void ipip_tunnel_setup(struct net_device *dev)
|
||
|
{
|
||
|
+ struct ip_tunnel *t = netdev_priv(dev);
|
||
|
+
|
||
|
dev->netdev_ops = &ipip_netdev_ops;
|
||
|
|
||
|
dev->type = ARPHRD_TUNNEL;
|
||
|
@@ -336,6 +446,7 @@ static void ipip_tunnel_setup(struct net_device *dev)
|
||
|
dev->features |= IPIP_FEATURES;
|
||
|
dev->hw_features |= IPIP_FEATURES;
|
||
|
ip_tunnel_setup(dev, ipip_net_id);
|
||
|
+ INIT_LIST_HEAD(&t->fan.fan_maps);
|
||
|
}
|
||
|
|
||
|
static int ipip_tunnel_init(struct net_device *dev)
|
||
|
@@ -419,41 +530,65 @@ static bool ipip_netlink_encap_parms(struct nlattr *data[],
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
-static void ipip_fan_free_map(struct ip_tunnel *t)
|
||
|
+static void ipip_fan_flush_map(struct ip_tunnel *t)
|
||
|
{
|
||
|
- memset(&t->fan.map, 0, sizeof(t->fan.map));
|
||
|
+ struct ip_fan_map *fan_map;
|
||
|
+
|
||
|
+ list_for_each_entry_rcu(fan_map, &t->fan.fan_maps, list) {
|
||
|
+ list_del_rcu(&fan_map->list);
|
||
|
+ kfree_rcu(fan_map, rcu);
|
||
|
+ }
|
||
|
}
|
||
|
|
||
|
-static int ipip_fan_set_map(struct ip_tunnel *t, struct ip_tunnel_fan_map *map)
|
||
|
+static int ipip_fan_del_map(struct ip_tunnel *t, __be32 overlay)
|
||
|
{
|
||
|
- u32 overlay, overlay_mask, underlay, underlay_mask;
|
||
|
+ struct ip_fan_map *fan_map;
|
||
|
|
||
|
- if ((map->underlay_prefix && map->underlay_prefix != 16) ||
|
||
|
- (map->overlay_prefix && map->overlay_prefix != 8))
|
||
|
- return -EINVAL;
|
||
|
+ fan_map = ipip_fan_find_map(t, overlay);
|
||
|
+ if (!fan_map)
|
||
|
+ return -ENOENT;
|
||
|
+
|
||
|
+ list_del_rcu(&fan_map->list);
|
||
|
+ kfree_rcu(fan_map, rcu);
|
||
|
|
||
|
- overlay = ntohl(map->overlay);
|
||
|
- overlay_mask = ntohl(inet_make_mask(map->overlay_prefix));
|
||
|
+ return 0;
|
||
|
+}
|
||
|
|
||
|
- underlay = ntohl(map->underlay);
|
||
|
- underlay_mask = ntohl(inet_make_mask(map->underlay_prefix));
|
||
|
+static int ipip_fan_add_map(struct ip_tunnel *t, struct ifla_fan_map *map)
|
||
|
+{
|
||
|
+ __be32 overlay_mask, underlay_mask;
|
||
|
+ struct ip_fan_map *fan_map;
|
||
|
|
||
|
- if ((overlay & ~overlay_mask) || (underlay & ~underlay_mask))
|
||
|
- return -EINVAL;
|
||
|
+ overlay_mask = inet_make_mask(map->overlay_prefix);
|
||
|
+ underlay_mask = inet_make_mask(map->underlay_prefix);
|
||
|
|
||
|
- if (!(overlay & overlay_mask) && (underlay & underlay_mask))
|
||
|
+ if ((map->overlay & ~overlay_mask) || (map->underlay & ~underlay_mask))
|
||
|
return -EINVAL;
|
||
|
|
||
|
- t->parms.i_flags |= TUNNEL_FAN;
|
||
|
+ if (!(map->overlay & overlay_mask) && (map->underlay & underlay_mask))
|
||
|
+ return -EINVAL;
|
||
|
|
||
|
- /* Special case: overlay 0 and underlay 0 clears all mappings */
|
||
|
- if (!overlay && !underlay) {
|
||
|
- ipip_fan_free_map(t);
|
||
|
+ /* Special case: overlay 0 and underlay 0: flush all mappings */
|
||
|
+ if (!map->overlay && !map->underlay) {
|
||
|
+ ipip_fan_flush_map(t);
|
||
|
return 0;
|
||
|
}
|
||
|
+
|
||
|
+ /* Special case: overlay set and underlay 0: clear map for overlay */
|
||
|
+ if (!map->underlay)
|
||
|
+ return ipip_fan_del_map(t, map->overlay);
|
||
|
+
|
||
|
+ if (ipip_fan_find_map(t, map->overlay))
|
||
|
+ return -EEXIST;
|
||
|
+
|
||
|
+ fan_map = kmalloc(sizeof(*fan_map), GFP_KERNEL);
|
||
|
+ fan_map->underlay = map->underlay;
|
||
|
+ fan_map->overlay = map->overlay;
|
||
|
+ fan_map->underlay_prefix = map->underlay_prefix;
|
||
|
+ fan_map->overlay_mask = ntohl(overlay_mask);
|
||
|
+ fan_map->overlay_prefix = map->overlay_prefix;
|
||
|
|
||
|
- overlay >>= (32 - map->overlay_prefix);
|
||
|
- t->fan.map[overlay] = underlay;
|
||
|
+ list_add_tail_rcu(&fan_map->list, &t->fan.fan_maps);
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
@@ -462,7 +597,7 @@ static int ipip_fan_set_map(struct ip_tunnel *t, struct ip_tunnel_fan_map *map)
|
||
|
static int ipip_netlink_fan(struct nlattr *data[], struct ip_tunnel *t,
|
||
|
struct ip_tunnel_parm *parms)
|
||
|
{
|
||
|
- struct ip_tunnel_fan_map *map;
|
||
|
+ struct ifla_fan_map *map;
|
||
|
struct nlattr *attr;
|
||
|
int rem, rv;
|
||
|
|
||
|
@@ -474,7 +609,7 @@ static int ipip_netlink_fan(struct nlattr *data[], struct ip_tunnel *t,
|
||
|
|
||
|
nla_for_each_nested(attr, data[IFLA_IPTUN_FAN_MAP], rem) {
|
||
|
map = nla_data(attr);
|
||
|
- rv = ipip_fan_set_map(t, map);
|
||
|
+ rv = ipip_fan_add_map(t, map);
|
||
|
if (rv)
|
||
|
return rv;
|
||
|
}
|
||
|
@@ -555,7 +690,7 @@ static size_t ipip_get_size(const struct net_device *dev)
|
||
|
/* IFLA_IPTUN_ENCAP_DPORT */
|
||
|
nla_total_size(2) +
|
||
|
/* IFLA_IPTUN_FAN_MAP */
|
||
|
- nla_total_size(sizeof(struct ip_tunnel_fan_map)) * 256 +
|
||
|
+ nla_total_size(sizeof(struct ifla_fan_map)) * 256 +
|
||
|
0;
|
||
|
}
|
||
|
|
||
|
@@ -583,25 +718,22 @@ static int ipip_fill_info(struct sk_buff *skb, const struct net_device *dev)
|
||
|
tunnel->encap.flags))
|
||
|
goto nla_put_failure;
|
||
|
|
||
|
- if (tunnel->parms.i_flags & TUNNEL_FAN) {
|
||
|
+ if (fan_has_map(&tunnel->fan)) {
|
||
|
struct nlattr *fan_nest;
|
||
|
- int i;
|
||
|
+ struct ip_fan_map *fan_map;
|
||
|
|
||
|
fan_nest = nla_nest_start(skb, IFLA_IPTUN_FAN_MAP);
|
||
|
if (!fan_nest)
|
||
|
goto nla_put_failure;
|
||
|
- for (i = 0; i < 256; i++) {
|
||
|
- if (tunnel->fan.map[i]) {
|
||
|
- struct ip_tunnel_fan_map map;
|
||
|
-
|
||
|
- map.underlay = htonl(tunnel->fan.map[i]);
|
||
|
- map.underlay_prefix = 16;
|
||
|
- map.overlay = htonl(i << 24);
|
||
|
- map.overlay_prefix = 8;
|
||
|
- if (nla_put(skb, IFLA_FAN_MAPPING,
|
||
|
- sizeof(map), &map))
|
||
|
- goto nla_put_failure;
|
||
|
- }
|
||
|
+ list_for_each_entry_rcu(fan_map, &tunnel->fan.fan_maps, list) {
|
||
|
+ struct ifla_fan_map map;
|
||
|
+
|
||
|
+ map.underlay = fan_map->underlay;
|
||
|
+ map.underlay_prefix = fan_map->underlay_prefix;
|
||
|
+ map.overlay = fan_map->overlay;
|
||
|
+ map.overlay_prefix = fan_map->overlay_prefix;
|
||
|
+ if (nla_put(skb, IFLA_FAN_MAPPING, sizeof(map), &map))
|
||
|
+ goto nla_put_failure;
|
||
|
}
|
||
|
nla_nest_end(skb, fan_nest);
|
||
|
}
|
||
|
--
|
||
|
2.7.4
|
||
|
|