summary refs log tree commit diff
diff options
context:
space:
mode:
authorCharles Strahan <charles@cstrahan.com>2016-04-09 19:36:02 -0400
committerCharles Strahan <charles@cstrahan.com>2016-04-10 16:07:03 -0400
commitad7b1e24c20d60199525f3849115fa7e96bc5174 (patch)
treea0d97c245b6cb33106cbb2f35749c34e063a4057
parentaec8daed8685c885f2e8c2701c4a013c607d163d (diff)
downloadnixpkgs-ad7b1e24c20d60199525f3849115fa7e96bc5174.tar
nixpkgs-ad7b1e24c20d60199525f3849115fa7e96bc5174.tar.gz
nixpkgs-ad7b1e24c20d60199525f3849115fa7e96bc5174.tar.bz2
nixpkgs-ad7b1e24c20d60199525f3849115fa7e96bc5174.tar.lz
nixpkgs-ad7b1e24c20d60199525f3849115fa7e96bc5174.tar.xz
nixpkgs-ad7b1e24c20d60199525f3849115fa7e96bc5174.tar.zst
nixpkgs-ad7b1e24c20d60199525f3849115fa7e96bc5174.zip
fan-networking: updated patches from Ubuntu
This pulls in updated Fan Networking patches from Ubuntu.
(https://wiki.ubuntu.com/FanNetworking)

closes #14328
-rw-r--r--pkgs/os-specific/linux/fanctl/default.nix46
-rw-r--r--pkgs/os-specific/linux/fanctl/robustness.patch85
-rw-r--r--pkgs/os-specific/linux/iproute/1000-ubuntu-poc-fan-driver.patch65
-rw-r--r--pkgs/os-specific/linux/iproute/1001-ubuntu-poc-fan-driver-v3.patch (renamed from pkgs/os-specific/linux/iproute/ubuntu-fan.patch)87
-rw-r--r--pkgs/os-specific/linux/iproute/1002-ubuntu-poc-fan-driver-vxlan.patch177
-rw-r--r--pkgs/os-specific/linux/iproute/default.nix8
-rw-r--r--pkgs/os-specific/linux/kernel/patches.nix9
-rw-r--r--pkgs/os-specific/linux/kernel/ubuntu-fan-3.patch616
-rw-r--r--pkgs/os-specific/linux/kernel/ubuntu-fan-4.4.patch1240
-rw-r--r--pkgs/os-specific/linux/kernel/ubuntu-fan-4.patch616
10 files changed, 1626 insertions, 1323 deletions
diff --git a/pkgs/os-specific/linux/fanctl/default.nix b/pkgs/os-specific/linux/fanctl/default.nix
index 61e100f4c9b..cb188b56c06 100644
--- a/pkgs/os-specific/linux/fanctl/default.nix
+++ b/pkgs/os-specific/linux/fanctl/default.nix
@@ -1,41 +1,39 @@
-{ stdenv, lib, fetchbzr, makeWrapper, bridge-utils, iproute, dnsmasq, iptables, kmod, utillinux }:
+{ stdenv, lib, fetchurl, gnugrep, glibc, gawk, coreutils, bridge-utils, iproute
+, dnsmasq, iptables, kmod, utillinux, gnused }:
 
-let stateDir = "/var/lib/fan-networking";
-in stdenv.mkDerivation rec {
+stdenv.mkDerivation rec {
   name = "fanctl-${version}";
 
-  version = "0.3.0";
+  version = "0.9.0";
 
-  src = fetchbzr {
-    url = "https://code.launchpad.net/~ubuntu-branches/ubuntu/vivid/ubuntu-fan/vivid-updates";
-    rev = 2;
-    sha256 = "1vcr2rg99g7sx1zynhiggjzc9y9z591i4535hbm21dysy3cisp7i";
+  src = fetchurl {
+    url = "https://launchpad.net/ubuntu/+archive/primary/+files/ubuntu-fan_${version}.tar.xz";
+    sha256 = "03dv5zzb8fkl9kkbhznxm48d6j3fjms74fn0s1zip2gz53l1s14n";
   };
 
-  buildInputs = [ makeWrapper ];
+  # The Ubuntu package creates a number of state/config directories upon
+  # installation, and so the fanctl script expects those directories to exist
+  # before being used. Instead, we patch the fanctl script to gracefully handle
+  # the fact that the directories might not exist yet.
+  # Also, when dnsmasq is given --conf-file="", it will still attempt to read
+  # /etc/dnsmasq.conf; if that file does not exist, dnsmasq subsequently fails,
+  # so we'll use /dev/null, which actually works as intended.
+  patches = [ ./robustness.patch ];
 
-  # When given --conf-file="", dnsmasq still attempts to read /etc/dnsmasq.conf;
-  # if that files does not exist, dnsmasq subsequently fails,
-  # so we'll use /dev/null.
-  #
-  # Also, make sure the state directory before starting dnsmasq.
-  buildPhase = ''
+  postPatch = ''
     substituteInPlace fanctl \
-      --replace '--conf-file= ' \
-                '--conf-file=/dev/null ' \
-      --replace '/var/lib/misc' \
-                '${stateDir}'
-
-    sed -i '/dnsmasq -u/i \
-    mkdir -p ${stateDir}' fanctl
+      --replace '@PATH@' \
+                '${lib.makeSearchPath "bin" [
+                     gnugrep gawk coreutils bridge-utils iproute dnsmasq
+                     iptables kmod utillinux gnused
+                     glibc # needed for getent
+                   ]}'
   '';
 
   installPhase = ''
     mkdir -p $out/bin $out/man/man8
     cp fanctl.8 $out/man/man8
     cp fanctl $out/bin
-    wrapProgram $out/bin/fanctl --prefix PATH : \
-      ${lib.makeSearchPath "bin" [ bridge-utils iproute dnsmasq iptables kmod utillinux ]};
   '';
 
   meta = with lib; {
diff --git a/pkgs/os-specific/linux/fanctl/robustness.patch b/pkgs/os-specific/linux/fanctl/robustness.patch
new file mode 100644
index 00000000000..7a70a784e3e
--- /dev/null
+++ b/pkgs/os-specific/linux/fanctl/robustness.patch
@@ -0,0 +1,85 @@
+diff --git a/fanctl b/fanctl
+index 4338b75..84cf987 100755
+--- a/fanctl
++++ b/fanctl
+@@ -5,6 +5,8 @@
+ #   fanctl down 15 10.1.0.1
+ #
+ 
++export PATH="@PATH@"
++
+ usage()
+ {
+ 	echo "Usage: $0 <cmd> [<options>...]" 1>&2
+@@ -23,8 +25,8 @@ run()
+ 	"$@"
+ }
+ 
+-state_dir="/run/ubuntu-fan"
+-lconfig_dir="/var/lib/ubuntu-fan/config"
++state_dir="/run/fan-networking"
++lconfig_dir="/var/lib/fan-networking/config"
+ 
+ __ip_split()
+ {
+@@ -931,12 +933,12 @@ dhcp_reconfigure()
+ 		--strict-order \
+ 		--bind-interfaces \
+ 		--pid-file="$state_dir/dnsmasq-$C_bridge_state.pid" \
+-		--conf-file= \
++		--conf-file=/dev/null \
+ 		$dhcp_flags \
+ 		--dhcp-no-override \
+ 		--except-interface=lo \
+ 		--interface="$C_bridge" \
+-		--dhcp-leasefile=/var/lib/misc/dnsmasq."$C_bridge_state".leases \
++		--dhcp-leasefile=/var/lib/fan-networking/dnsmasq."$C_bridge_state".leases \
+ 		--dhcp-authoritative \
+ 	    || $fail "$C_bridge: failed to start dnsmasq"
+ 
+@@ -1559,21 +1561,23 @@ cmd_config()
+ 
+ 	case "$cmd" in
+ 	list|ls)
+-		ls -1 "$lconfig_dir" | \
+-		while read config
+-		do
+-			case "$config" in
+-			*.conf)		;;
+-			*)		continue ;;
+-			esac
++		if [ -d $lconfig_dir ]; then
++			ls -1 "$lconfig_dir" | \
++			while read config
++			do
++				case "$config" in
++				*.conf)		;;
++				*)		continue ;;
++				esac
+ 
+-			config=$( echo "$config" | sed \
+-				-e 's/.conf$//' \
+-				-e 's/--/ /g' \
+-				-e 's@-@/@g'
+-			)
+-			echo "$config"
+-		done
++				config=$( echo "$config" | sed \
++					-e 's/.conf$//' \
++					-e 's/--/ /g' \
++					-e 's@-@/@g'
++				)
++				echo "$config"
++			done
++		fi
+ 		;;
+ 	show)
+ 		cmd_decode_init
+@@ -1588,6 +1592,7 @@ cmd_config()
+ 		[ -f "$uconfig" ] && cat "$uconfig"
+ 		;;
+ 	set)
++		mkdir -p $lconfig_dir || fail "could not create config directory ($lconfig_dir)"
+ 		cmd_decode_init
+ 		if ! cmd_decode_config "config set" "$@"; then
+ 			fail "invalid config"
diff --git a/pkgs/os-specific/linux/iproute/1000-ubuntu-poc-fan-driver.patch b/pkgs/os-specific/linux/iproute/1000-ubuntu-poc-fan-driver.patch
new file mode 100644
index 00000000000..ca91b816000
--- /dev/null
+++ b/pkgs/os-specific/linux/iproute/1000-ubuntu-poc-fan-driver.patch
@@ -0,0 +1,65 @@
+Description: POC fan driver support
+ POC Fan driver support
+Author: Jay Vosburgh <jay.vosburgh@canonical.com>
+
+Index: iproute2-4.1.1/include/linux/if_tunnel.h
+===================================================================
+--- iproute2-4.1.1.orig/include/linux/if_tunnel.h
++++ iproute2-4.1.1/include/linux/if_tunnel.h
+@@ -57,6 +57,9 @@ enum {
+ 	IFLA_IPTUN_ENCAP_FLAGS,
+ 	IFLA_IPTUN_ENCAP_SPORT,
+ 	IFLA_IPTUN_ENCAP_DPORT,
++
++	IFLA_IPTUN_FAN_UNDERLAY = 32,
++
+ 	__IFLA_IPTUN_MAX,
+ };
+ #define IFLA_IPTUN_MAX	(__IFLA_IPTUN_MAX - 1)
+Index: iproute2-4.1.1/ip/link_iptnl.c
+===================================================================
+--- iproute2-4.1.1.orig/ip/link_iptnl.c
++++ iproute2-4.1.1/ip/link_iptnl.c
+@@ -66,6 +66,7 @@ static int iptunnel_parse_opt(struct lin
+ 	__u32 link = 0;
+ 	__u32 laddr = 0;
+ 	__u32 raddr = 0;
++	__u32 underlay = 0;
+ 	__u8 ttl = 0;
+ 	__u8 tos = 0;
+ 	__u8 pmtudisc = 1;
+@@ -174,6 +175,9 @@ get_failed:
+ 				raddr = get_addr32(*argv);
+ 			else
+ 				raddr = 0;
++		} else if (strcmp(*argv, "underlay") == 0) {
++			NEXT_ARG();
++			underlay = get_addr32(*argv);
+ 		} else if (strcmp(*argv, "local") == 0) {
+ 			NEXT_ARG();
+ 			if (strcmp(*argv, "any"))
+@@ -318,6 +322,9 @@ get_failed:
+ 		}
+ 	}
+ 
++	if (underlay)
++		addattr32(n, 1024, IFLA_IPTUN_FAN_UNDERLAY, underlay);
++
+ 	return 0;
+ }
+ 
+@@ -349,6 +356,14 @@ static void iptunnel_print_opt(struct li
+ 
+ 	fprintf(f, "local %s ", local);
+ 
++	if (tb[IFLA_IPTUN_FAN_UNDERLAY]) {
++		unsigned addr = rta_getattr_u32(tb[IFLA_IPTUN_FAN_UNDERLAY]);
++
++		if (addr)
++			fprintf(f, "underlay %s ",
++				format_host(AF_INET, 4, &addr, s1, sizeof(s1)));
++	}
++
+ 	if (tb[IFLA_IPTUN_LINK] && rta_getattr_u32(tb[IFLA_IPTUN_LINK])) {
+ 		unsigned link = rta_getattr_u32(tb[IFLA_IPTUN_LINK]);
+ 		const char *n = if_indextoname(link, s2);
diff --git a/pkgs/os-specific/linux/iproute/ubuntu-fan.patch b/pkgs/os-specific/linux/iproute/1001-ubuntu-poc-fan-driver-v3.patch
index e55425c2ce6..a940b341ea0 100644
--- a/pkgs/os-specific/linux/iproute/ubuntu-fan.patch
+++ b/pkgs/os-specific/linux/iproute/1001-ubuntu-poc-fan-driver-v3.patch
@@ -1,27 +1,19 @@
-This provides support for Ubuntu's Fan Networking [1].
-
-These patches were pulled from:
-https://code.launchpad.net/~ubuntu-branches/ubuntu/vivid/iproute2/vivid-proposed
-
-See revisions 18 and 19.
-
-[1] https://wiki.ubuntu.com/FanNetworking
-
-diff --git a/include/linux/if_tunnel.h b/include/linux/if_tunnel.h
-index 102ce7a..7b8f0e5 100644
---- a/include/linux/if_tunnel.h
-+++ b/include/linux/if_tunnel.h
-@@ -57,6 +57,9 @@ enum {
- 	IFLA_IPTUN_ENCAP_FLAGS,
- 	IFLA_IPTUN_ENCAP_SPORT,
+Description: Fan driver support v3
+ Fan driver support v3
+Author: Jay Vosburgh <jay.vosburgh@canonical.com>
+Index: iproute2-4.1.1/include/linux/if_tunnel.h
+===================================================================
+--- iproute2-4.1.1.orig/include/linux/if_tunnel.h
++++ iproute2-4.1.1/include/linux/if_tunnel.h
+@@ -59,6 +59,7 @@ enum {
  	IFLA_IPTUN_ENCAP_DPORT,
-+
-+	IFLA_IPTUN_FAN_UNDERLAY = 32,
+ 
+ 	IFLA_IPTUN_FAN_UNDERLAY = 32,
 +	IFLA_IPTUN_FAN_MAP = 33,
+ 
  	__IFLA_IPTUN_MAX,
  };
- #define IFLA_IPTUN_MAX	(__IFLA_IPTUN_MAX - 1)
-@@ -131,4 +134,20 @@ enum {
+@@ -134,4 +135,20 @@ enum {
  };
  
  #define IFLA_VTI_MAX	(__IFLA_VTI_MAX - 1)
@@ -42,10 +34,10 @@ index 102ce7a..7b8f0e5 100644
 +};
 +
  #endif /* _IF_TUNNEL_H_ */
-diff --git a/ip/link_iptnl.c b/ip/link_iptnl.c
-index 9d6bc98..ec3f05d 100644
---- a/ip/link_iptnl.c
-+++ b/ip/link_iptnl.c
+Index: iproute2-4.1.1/ip/link_iptnl.c
+===================================================================
+--- iproute2-4.1.1.orig/ip/link_iptnl.c
++++ iproute2-4.1.1/ip/link_iptnl.c
 @@ -49,6 +49,42 @@ static void usage(int sit)
  	print_usage(stderr, sit);
  	exit(-1);
@@ -89,21 +81,10 @@ index 9d6bc98..ec3f05d 100644
  
  static int iptunnel_parse_opt(struct link_util *lu, int argc, char **argv,
  			      struct nlmsghdr *n)
-@@ -66,6 +102,7 @@ static int iptunnel_parse_opt(struct link_util *lu, int argc, char **argv,
- 	__u32 link = 0;
- 	__u32 laddr = 0;
- 	__u32 raddr = 0;
-+	__u32 underlay = 0;
- 	__u8 ttl = 0;
- 	__u8 tos = 0;
- 	__u8 pmtudisc = 1;
-@@ -174,6 +211,13 @@ get_failed:
- 				raddr = get_addr32(*argv);
- 			else
- 				raddr = 0;
-+		} else if (strcmp(*argv, "underlay") == 0) {
-+			NEXT_ARG();
-+			underlay = get_addr32(*argv);
+@@ -178,6 +214,10 @@ get_failed:
+ 		} else if (strcmp(*argv, "underlay") == 0) {
+ 			NEXT_ARG();
+ 			underlay = get_addr32(*argv);
 +		} else if (strcmp(*argv, "fan-map") == 0) {
 +			NEXT_ARG();
 +			if (fan_parse_map(&argc, &argv, n))
@@ -111,13 +92,7 @@ index 9d6bc98..ec3f05d 100644
  		} else if (strcmp(*argv, "local") == 0) {
  			NEXT_ARG();
  			if (strcmp(*argv, "any"))
-@@ -318,9 +362,32 @@ get_failed:
- 		}
- 	}
- 
-+	if (underlay)
-+		addattr32(n, 1024, IFLA_IPTUN_FAN_UNDERLAY, underlay);
-+
+@@ -328,6 +368,28 @@ get_failed:
  	return 0;
  }
  
@@ -127,16 +102,18 @@ index 9d6bc98..ec3f05d 100644
 +	struct ip_tunnel_fan_map *m;
 +	struct rtattr *i;
 +	int rem;
++	int p;
 +
 +	fprintf(f, "fan-map ");
 +
 +	rem = RTA_PAYLOAD(attr);
 +	for (i = RTA_DATA(attr); RTA_OK(i, rem); i = RTA_NEXT(i, rem)) {
++		p = RTA_PAYLOAD(i);
 +		m = RTA_DATA(i);
 +		fprintf(f, "%s/%d:%s/%d ",
-+			rt_addr_n2a(AF_INET, sizeof(m->overlay), &m->overlay, b1, INET_ADDRSTRLEN),
++			rt_addr_n2a(AF_INET, p, &m->overlay, b1, INET_ADDRSTRLEN),
 +			m->overlay_prefix,
-+			rt_addr_n2a(AF_INET, sizeof(m->overlay), &m->underlay, b2, INET_ADDRSTRLEN),
++			rt_addr_n2a(AF_INET, p, &m->underlay, b2, INET_ADDRSTRLEN),
 +			m->underlay_prefix);
 +	}
 +}
@@ -144,18 +121,10 @@ index 9d6bc98..ec3f05d 100644
  static void iptunnel_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[])
  {
  	char s1[1024];
-@@ -349,6 +416,17 @@ static void iptunnel_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[
- 
- 	fprintf(f, "local %s ", local);
+@@ -364,6 +426,9 @@ static void iptunnel_print_opt(struct li
+ 				format_host(AF_INET, 4, &addr, s1, sizeof(s1)));
+ 	}
  
-+	if (tb[IFLA_IPTUN_FAN_UNDERLAY]) {
-+		unsigned addr = rta_getattr_u32(tb[IFLA_IPTUN_FAN_UNDERLAY]);
-+
-+		if (addr)
-+			fprintf(f, "underlay %s ",
-+				format_host(AF_INET, 4, &addr, s1, sizeof(s1)));
-+	}
-+
 +	if (tb[IFLA_IPTUN_FAN_MAP])
 +		fan_print_map(f, tb[IFLA_IPTUN_FAN_MAP]);
 +
diff --git a/pkgs/os-specific/linux/iproute/1002-ubuntu-poc-fan-driver-vxlan.patch b/pkgs/os-specific/linux/iproute/1002-ubuntu-poc-fan-driver-vxlan.patch
new file mode 100644
index 00000000000..2ddc2840bdd
--- /dev/null
+++ b/pkgs/os-specific/linux/iproute/1002-ubuntu-poc-fan-driver-vxlan.patch
@@ -0,0 +1,177 @@
+Description: Fan driver support VXLAN (p4)
+ Fan driver setup support for vxlan interfaces.
+
+Index: iproute2-4.3.0/include/linux/if_link.h
+===================================================================
+--- iproute2-4.3.0.orig/include/linux/if_link.h
++++ iproute2-4.3.0/include/linux/if_link.h
+@@ -392,6 +392,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)
+Index: iproute2-4.3.0/include/linux/if_tunnel.h
+===================================================================
+--- iproute2-4.3.0.orig/include/linux/if_tunnel.h
++++ iproute2-4.3.0/include/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;
+Index: iproute2-4.3.0/ip/iplink_vxlan.c
+===================================================================
+--- iproute2-4.3.0.orig/ip/iplink_vxlan.c
++++ iproute2-4.3.0/ip/iplink_vxlan.c
+@@ -15,7 +15,10 @@
+ #include <net/if.h>
+ #include <linux/ip.h>
+ #include <linux/if_link.h>
++#include <linux/types.h>
+ #include <arpa/inet.h>
++#include <linux/in6.h>
++#include <linux/if_tunnel.h>
+ 
+ #include "rt_names.h"
+ #include "utils.h"
+@@ -43,6 +46,45 @@ static void explain(void)
+ 	print_explain(stderr);
+ }
+ 
++static int fan_parse_map(int *argcp, char ***argvp, struct nlmsghdr *n)
++{
++	inet_prefix underlay, overlay;
++	struct ifla_fan_map map;
++	struct rtattr *nest;
++	char **argv = *argvp;
++	int argc = *argcp;
++
++	nest = addattr_nest(n, 1024, IFLA_VXLAN_FAN_MAP);
++	while (argc > 0) {
++		char *colon = strchr(*argv, ':');
++
++		if (!colon) {
++			PREV_ARG();
++			break;
++		}
++		*colon = '\0';
++
++		if (get_prefix(&overlay, *argv, AF_INET))
++			invarg("invalid fan-map overlay", *argv);
++		if (get_prefix(&underlay, colon + 1, AF_INET))
++			invarg("invalid fan-map underlay", colon + 1);
++
++		memcpy(&map.underlay, underlay.data, 4);
++		map.underlay_prefix = underlay.bitlen;
++		memcpy(&map.overlay, overlay.data, 4);
++		map.overlay_prefix = overlay.bitlen;
++
++		argc--, argv++;
++
++		addattr_l(n, 1024, IFLA_FAN_MAPPING, &map, sizeof(map));
++	}
++	addattr_nest_end(n, nest);
++
++	*argcp = argc;
++	*argvp = argv;
++	return 0;
++}
++
+ static int vxlan_parse_opt(struct link_util *lu, int argc, char **argv,
+ 			  struct nlmsghdr *n)
+ {
+@@ -201,6 +243,10 @@ static int vxlan_parse_opt(struct link_u
+ 			udp6zerocsumrx = 0;
+ 		} else if (!matches(*argv, "gbp")) {
+ 			gbp = 1;
++		} else if (!matches(*argv, "fan-map")) {
++			NEXT_ARG();
++			if (fan_parse_map(&argc, &argv, n))
++				invarg("invalid fan-map", *argv);
+ 		} else if (matches(*argv, "help") == 0) {
+ 			explain();
+ 			return -1;
+@@ -279,6 +325,28 @@ static int vxlan_parse_opt(struct link_u
+ 	return 0;
+ }
+ 
++static void fan_print_map(FILE *f, struct rtattr *attr)
++{
++	char b1[INET_ADDRSTRLEN], b2[INET_ADDRSTRLEN];
++	struct ifla_fan_map *m;
++	struct rtattr *i;
++	int rem;
++	int p;
++
++	fprintf(f, "fan-map ");
++
++	rem = RTA_PAYLOAD(attr);
++	for (i = RTA_DATA(attr); RTA_OK(i, rem); i = RTA_NEXT(i, rem)) {
++		p = RTA_PAYLOAD(i);
++		m = RTA_DATA(i);
++		fprintf(f, "%s/%d:%s/%d ",
++			rt_addr_n2a(AF_INET, p, &m->overlay, b1, INET_ADDRSTRLEN),
++			m->overlay_prefix,
++			rt_addr_n2a(AF_INET, p, &m->underlay, b2, INET_ADDRSTRLEN),
++			m->underlay_prefix);
++	}
++}
++
+ static void vxlan_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[])
+ {
+ 	__u32 vni;
+@@ -321,6 +389,9 @@ static void vxlan_print_opt(struct link_
+ 		}
+ 	}
+ 
++	if (tb[IFLA_VXLAN_FAN_MAP])
++		fan_print_map(f, tb[IFLA_VXLAN_FAN_MAP]);
++
+ 	if (tb[IFLA_VXLAN_LOCAL]) {
+ 		__be32 addr = rta_getattr_u32(tb[IFLA_VXLAN_LOCAL]);
+ 		if (addr)
+Index: iproute2-4.3.0/ip/link_iptnl.c
+===================================================================
+--- iproute2-4.3.0.orig/ip/link_iptnl.c
++++ iproute2-4.3.0/ip/link_iptnl.c
+@@ -49,10 +49,11 @@ static void usage(int sit)
+ 	print_usage(stderr, sit);
+ 	exit(-1);
+ }
++
+ static int fan_parse_map(int *argcp, char ***argvp, struct nlmsghdr *n)
+ {
+ 	inet_prefix underlay, overlay;
+-	struct ip_tunnel_fan_map map;
++	struct ifla_fan_map map;
+ 	struct rtattr *nest;
+ 	char **argv = *argvp;
+ 	int argc = *argcp;
+@@ -61,8 +62,10 @@ static int fan_parse_map(int *argcp, cha
+ 	while (argc > 0) {
+ 		char *colon = strchr(*argv, ':');
+ 
+-		if (!colon)
++		if (!colon) {
++			PREV_ARG();
+ 			break;
++		}
+ 		*colon = '\0';
+ 
+ 		if (get_prefix(&overlay, *argv, AF_INET))
+@@ -371,7 +374,7 @@ get_failed:
+ static void fan_print_map(FILE *f, struct rtattr *attr)
+ {
+ 	char b1[INET_ADDRSTRLEN], b2[INET_ADDRSTRLEN];
+-	struct ip_tunnel_fan_map *m;
++	struct ifla_fan_map *m;
+ 	struct rtattr *i;
+ 	int rem;
+ 	int p;
diff --git a/pkgs/os-specific/linux/iproute/default.nix b/pkgs/os-specific/linux/iproute/default.nix
index 891d39d24dd..5f328bd90c8 100644
--- a/pkgs/os-specific/linux/iproute/default.nix
+++ b/pkgs/os-specific/linux/iproute/default.nix
@@ -10,7 +10,13 @@ stdenv.mkDerivation rec {
     sha256 = "159988vv3fd78bzhisfl1dl4dd7km3vjzs2d8899a0vcvn412fzh";
   };
 
-  patches = lib.optionals enableFan [ ./ubuntu-fan.patch ];
+  patches = lib.optionals enableFan [
+    # These patches were pulled from:
+    # https://launchpad.net/ubuntu/xenial/+source/iproute2
+    ./1000-ubuntu-poc-fan-driver.patch
+    ./1001-ubuntu-poc-fan-driver-v3.patch
+    ./1002-ubuntu-poc-fan-driver-vxlan.patch
+  ];
 
   preConfigure = ''
     patchShebangs ./configure
diff --git a/pkgs/os-specific/linux/kernel/patches.nix b/pkgs/os-specific/linux/kernel/patches.nix
index a8fb1598af7..a23ee7823a6 100644
--- a/pkgs/os-specific/linux/kernel/patches.nix
+++ b/pkgs/os-specific/linux/kernel/patches.nix
@@ -71,14 +71,9 @@ rec {
       patch = ./mips-ext3-n32.patch;
     };
 
-  ubuntu_fan =
+  ubuntu_fan_4_4 =
     { name = "ubuntu-fan";
-      patch = ./ubuntu-fan-3.patch;
-    };
-
-  ubuntu_fan_4 =
-    { name = "ubuntu-fan";
-      patch = ./ubuntu-fan-4.patch;
+      patch = ./ubuntu-fan-4.4.patch;
     };
 
   ubuntu_unprivileged_overlayfs =
diff --git a/pkgs/os-specific/linux/kernel/ubuntu-fan-3.patch b/pkgs/os-specific/linux/kernel/ubuntu-fan-3.patch
deleted file mode 100644
index c80950d0614..00000000000
--- a/pkgs/os-specific/linux/kernel/ubuntu-fan-3.patch
+++ /dev/null
@@ -1,616 +0,0 @@
-From f3c956096902669c3529cb01d40deb0c759ed94f 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: Proof of concept implementation (v2)
-
-Modification to ipip tunnel driver to accept a new netlink option,
-IFLA_IPTUN_FAN_UNDERLAY, which provides a /16 network prefix and enables
-TX side destination address remapping for traffic entering the tunnel
-(to be encapsulated).
-
-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.
-
-Includes net.fan.version sysctl as a sentinel for availability of the
-fan functionality.
-
-NOTE: this requires an updated iproute2 to facilitate configuration of
-the fan.
-
-BugLink: http://bugs.launchpad.net/bugs/1439706
-Signed-off-by: Jay Vosburgh <jay.vosburgh@canonical.com>
-[apw@canonical.com: move IFLA_IPTUN_FAN_UNDERLAY up to avoid clashing
- with future feature additions.]
-Signed-off-by: Andy Whitcroft <apw@canonical.com>
----
- include/net/ip_tunnels.h       |   6 +++
- include/uapi/linux/if_tunnel.h |   4 ++
- net/ipv4/ipip.c                | 112 +++++++++++++++++++++++++++++++++++++++--
- 3 files changed, 117 insertions(+), 5 deletions(-)
-
-diff --git a/include/net/ip_tunnels.h b/include/net/ip_tunnels.h
-index 25a59eb..d7eada2 100644
---- a/include/net/ip_tunnels.h
-+++ b/include/net/ip_tunnels.h
-@@ -51,6 +51,11 @@ struct ip_tunnel_dst {
- 	__be32				 saddr;
- };
- 
-+/* Underlay address prefix for ipip fan mode */
-+struct ip_tunnel_fan {
-+	u32			underlay;
-+};
-+
- struct ip_tunnel {
- 	struct ip_tunnel __rcu	*next;
- 	struct hlist_node hash_node;
-@@ -82,6 +87,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;
- };
-diff --git a/include/uapi/linux/if_tunnel.h b/include/uapi/linux/if_tunnel.h
-index bd3cc11..8f7d269 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_UNDERLAY=32,
-+
- 	__IFLA_IPTUN_MAX,
- };
- #define IFLA_IPTUN_MAX	(__IFLA_IPTUN_MAX - 1)
-diff --git a/net/ipv4/ipip.c b/net/ipv4/ipip.c
-index 40403114..e3c27cd 100644
---- a/net/ipv4/ipip.c
-+++ b/net/ipv4/ipip.c
-@@ -209,13 +209,38 @@ drop:
- }
- 
- /*
-+ * 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 void ipip_build_fan_iphdr(struct ip_tunnel *tunnel, struct sk_buff *skb, struct iphdr *iph)
-+{
-+	u32 daddr;
-+
-+	*iph = tunnel->parms.iph;
-+
-+	daddr = ntohl(ip_hdr(skb)->daddr);
-+	iph->daddr = htonl((tunnel->fan.underlay & 0xffff0000) |
-+			   ((daddr >> 8) & 0x0000ffff));
-+}
-+
-+/*
-  *	This function assumes it is being called from dev_queue_xmit()
-  *	and that skb is filled properly by that function.
-  */
- 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 +249,13 @@ static netdev_tx_t ipip_tunnel_xmit(struct sk_buff *skb, struct net_device *dev)
- 	if (IS_ERR(skb))
- 		goto out;
- 
-+	if (tunnel->fan.underlay) {
-+		ipip_build_fan_iphdr(tunnel, skb, &fiph);
-+		tiph = &fiph;
-+	} else {
-+		tiph = &tunnel->parms.iph;
-+	}
-+
- 	skb_set_inner_ipproto(skb, IPPROTO_IPIP);
- 
- 	ip_tunnel_xmit(skb, dev, tiph, tiph->protocol);
-@@ -377,21 +409,44 @@ static bool ipip_netlink_encap_parms(struct nlattr *data[],
- 	return ret;
- }
- 
-+static int ipip_netlink_fan(struct nlattr *data[], struct ip_tunnel *t,
-+			    struct ip_tunnel_parm *parms)
-+{
-+	u32 net = t->fan.underlay;
-+
-+	if (!data[IFLA_IPTUN_FAN_UNDERLAY])
-+		goto err_check;
-+
-+	net = ntohl(nla_get_be32(data[IFLA_IPTUN_FAN_UNDERLAY])) & 0xffff0000;
-+
-+err_check:
-+	if (parms->iph.daddr && net)
-+		return -EINVAL;
-+
-+	t->fan.underlay = net;
-+
-+	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);
- }
- 
-@@ -400,16 +455,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))
-@@ -441,6 +500,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_UNDERLAY */
-+		nla_total_size(4) +
- 		0;
- }
- 
-@@ -468,6 +529,11 @@ static int ipip_fill_info(struct sk_buff *skb, const struct net_device *dev)
- 			tunnel->encap.flags))
- 		goto nla_put_failure;
- 
-+	if (tunnel->fan.underlay)
-+		if (nla_put_be32(skb, IFLA_IPTUN_FAN_UNDERLAY,
-+				 htonl(tunnel->fan.underlay)))
-+			goto nla_put_failure;
-+
- 	return 0;
- 
- nla_put_failure:
-@@ -485,6 +551,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_UNDERLAY]	= { .type = NLA_U32 },
- };
- 
- static struct rtnl_link_ops ipip_link_ops __read_mostly = {
-@@ -524,6 +593,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 = 1;
-+
-+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;
-@@ -542,9 +628,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:
-@@ -554,6 +653,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.4.1
-
-From 4ea8011656dfdd76e7a2391bdad47c06f85a9d02 Mon Sep 17 00:00:00 2001
-From: Andy Whitcroft <apw@canonical.com>
-Date: Tue, 21 Jul 2015 16:52:10 +0100
-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>
-Acked-by: Tim Gardner <tim.gardner@canonical.com>
-Acked-by: Brad Figg <brad.figg@canonical.com>
-Signed-off-by: Brad Figg <brad.figg@canonical.com>
----
- include/net/ip_tunnels.h       |  14 ++++-
- include/uapi/linux/if_tunnel.h |  20 ++++++-
- net/ipv4/ip_tunnel.c           |   7 ++-
- net/ipv4/ipip.c                | 120 +++++++++++++++++++++++++++++++++--------
- 4 files changed, 133 insertions(+), 28 deletions(-)
-
-diff --git a/include/net/ip_tunnels.h b/include/net/ip_tunnels.h
-index d7eada2..2f7bc8c 100644
---- a/include/net/ip_tunnels.h
-+++ b/include/net/ip_tunnels.h
-@@ -51,9 +51,18 @@ struct ip_tunnel_dst {
- 	__be32				 saddr;
- };
- 
--/* Underlay address prefix for ipip fan mode */
-+/* 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			underlay;
-+/*	u32 __rcu	*map;*/
-+	u32		map[FAN_OVERLAY_CNT];
- };
- 
- struct ip_tunnel {
-@@ -104,6 +113,7 @@ struct ip_tunnel {
- #define TUNNEL_OAM		__cpu_to_be16(0x0200)
- #define TUNNEL_CRIT_OPT		__cpu_to_be16(0x0400)
- #define TUNNEL_OPTIONS_PRESENT	__cpu_to_be16(0x0800)
-+#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 8f7d269..9625934 100644
---- a/include/uapi/linux/if_tunnel.h
-+++ b/include/uapi/linux/if_tunnel.h
-@@ -58,8 +58,8 @@ enum {
- 	IFLA_IPTUN_ENCAP_SPORT,
- 	IFLA_IPTUN_ENCAP_DPORT,
- 
--	__IFLA_IPTUN_VENDOR_BREAK,		/* Ensure new entries do not hit the below. */
--	IFLA_IPTUN_FAN_UNDERLAY=32,
-+	__IFLA_IPTUN_VENDOR_BREAK, /* Ensure new entries do not hit the below. */
-+	IFLA_IPTUN_FAN_MAP = 33,
- 
- 	__IFLA_IPTUN_MAX,
- };
-@@ -135,4 +135,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 d3e4479..60bd10f 100644
---- a/net/ipv4/ip_tunnel.c
-+++ b/net/ipv4/ip_tunnel.c
-@@ -1078,6 +1078,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)
- {
-@@ -1087,7 +1092,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 e3c27cd..d6ebc66 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,11 @@ 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
-@@ -221,15 +227,20 @@ drop:
-  * 99.6.7.8, would be directed to underlay host 10.88.6.7, which hosts
-  * overlay network 99.6.7.0/24.
-  */
--static void ipip_build_fan_iphdr(struct ip_tunnel *tunnel, struct sk_buff *skb, struct iphdr *iph)
-+static int ipip_build_fan_iphdr(struct ip_tunnel *tunnel, struct sk_buff *skb, struct iphdr *iph)
- {
--	u32 daddr;
--
--	*iph = tunnel->parms.iph;
-+	unsigned int overlay;
-+	u32 daddr, underlay;
- 
- 	daddr = ntohl(ip_hdr(skb)->daddr);
--	iph->daddr = htonl((tunnel->fan.underlay & 0xffff0000) |
--			   ((daddr >> 8) & 0x0000ffff));
-+	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;
- }
- 
- /*
-@@ -249,8 +260,9 @@ static netdev_tx_t ipip_tunnel_xmit(struct sk_buff *skb, struct net_device *dev)
- 	if (IS_ERR(skb))
- 		goto out;
- 
--	if (tunnel->fan.underlay) {
--		ipip_build_fan_iphdr(tunnel, skb, &fiph);
-+	if (ipip_tunnel_is_fan(tunnel)) {
-+		if (ipip_build_fan_iphdr(tunnel, skb, &fiph))
-+			goto tx_error;
- 		tiph = &fiph;
- 	} else {
- 		tiph = &tunnel->parms.iph;
-@@ -409,21 +421,65 @@ 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)
- {
--	u32 net = t->fan.underlay;
--
--	if (!data[IFLA_IPTUN_FAN_UNDERLAY])
--		goto err_check;
-+	struct ip_tunnel_fan_map *map;
-+	struct nlattr *attr;
-+	int rem, rv;
- 
--	net = ntohl(nla_get_be32(data[IFLA_IPTUN_FAN_UNDERLAY])) & 0xffff0000;
-+	if (!data[IFLA_IPTUN_FAN_MAP])
-+		return 0;
- 
--err_check:
--	if (parms->iph.daddr && net)
-+	if (parms->iph.daddr)
- 		return -EINVAL;
- 
--	t->fan.underlay = net;
-+	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;
- }
-@@ -500,8 +556,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_UNDERLAY */
--		nla_total_size(4) +
-+		/* IFLA_IPTUN_FAN_MAP */
-+		nla_total_size(sizeof(struct ip_tunnel_fan_map)) * 256 +
- 		0;
- }
- 
-@@ -529,10 +585,28 @@ static int ipip_fill_info(struct sk_buff *skb, const struct net_device *dev)
- 			tunnel->encap.flags))
- 		goto nla_put_failure;
- 
--	if (tunnel->fan.underlay)
--		if (nla_put_be32(skb, IFLA_IPTUN_FAN_UNDERLAY,
--				 htonl(tunnel->fan.underlay)))
-+	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;
- 
-@@ -553,7 +627,7 @@ static const struct nla_policy ipip_policy[IFLA_IPTUN_MAX + 1] = {
- 	[IFLA_IPTUN_ENCAP_DPORT]	= { .type = NLA_U16 },
- 
- 	[__IFLA_IPTUN_VENDOR_BREAK ... IFLA_IPTUN_MAX]	= { .type = NLA_BINARY },
--	[IFLA_IPTUN_FAN_UNDERLAY]	= { .type = NLA_U32 },
-+	[IFLA_IPTUN_FAN_MAP]		= { .type = NLA_NESTED },
- };
- 
- static struct rtnl_link_ops ipip_link_ops __read_mostly = {
-@@ -595,7 +669,7 @@ static struct pernet_operations ipip_net_ops = {
- 
- #ifdef CONFIG_SYSCTL
- static struct ctl_table_header *ipip_fan_header;
--static unsigned int ipip_fan_version = 1;
-+static unsigned int ipip_fan_version = 3;
- 
- static struct ctl_table ipip_fan_sysctls[] = {
- 	{
--- 
-2.4.1
-
diff --git a/pkgs/os-specific/linux/kernel/ubuntu-fan-4.4.patch b/pkgs/os-specific/linux/kernel/ubuntu-fan-4.4.patch
new file mode 100644
index 00000000000..39150ad790d
--- /dev/null
+++ b/pkgs/os-specific/linux/kernel/ubuntu-fan-4.4.patch
@@ -0,0 +1,1240 @@
+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
+
diff --git a/pkgs/os-specific/linux/kernel/ubuntu-fan-4.patch b/pkgs/os-specific/linux/kernel/ubuntu-fan-4.patch
deleted file mode 100644
index 0050af6c252..00000000000
--- a/pkgs/os-specific/linux/kernel/ubuntu-fan-4.patch
+++ /dev/null
@@ -1,616 +0,0 @@
-From f3c956096902669c3529cb01d40deb0c759ed94f 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: Proof of concept implementation (v2)
-
-Modification to ipip tunnel driver to accept a new netlink option,
-IFLA_IPTUN_FAN_UNDERLAY, which provides a /16 network prefix and enables
-TX side destination address remapping for traffic entering the tunnel
-(to be encapsulated).
-
-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.
-
-Includes net.fan.version sysctl as a sentinel for availability of the
-fan functionality.
-
-NOTE: this requires an updated iproute2 to facilitate configuration of
-the fan.
-
-BugLink: http://bugs.launchpad.net/bugs/1439706
-Signed-off-by: Jay Vosburgh <jay.vosburgh@canonical.com>
-[apw@canonical.com: move IFLA_IPTUN_FAN_UNDERLAY up to avoid clashing
- with future feature additions.]
-Signed-off-by: Andy Whitcroft <apw@canonical.com>
----
- include/net/ip_tunnels.h       |   6 +++
- include/uapi/linux/if_tunnel.h |   4 ++
- net/ipv4/ipip.c                | 112 +++++++++++++++++++++++++++++++++++++++--
- 3 files changed, 117 insertions(+), 5 deletions(-)
-
-diff --git a/include/net/ip_tunnels.h b/include/net/ip_tunnels.h
-index 25a59eb..d7eada2 100644
---- a/include/net/ip_tunnels.h
-+++ b/include/net/ip_tunnels.h
-@@ -51,6 +51,11 @@ struct ip_tunnel_dst {
- 	__be32				 saddr;
- };
- 
-+/* Underlay address prefix for ipip fan mode */
-+struct ip_tunnel_fan {
-+	u32			underlay;
-+};
-+
- struct ip_tunnel {
- 	struct ip_tunnel __rcu	*next;
- 	struct hlist_node hash_node;
-@@ -82,6 +87,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;
- };
-diff --git a/include/uapi/linux/if_tunnel.h b/include/uapi/linux/if_tunnel.h
-index bd3cc11..8f7d269 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_UNDERLAY=32,
-+
- 	__IFLA_IPTUN_MAX,
- };
- #define IFLA_IPTUN_MAX	(__IFLA_IPTUN_MAX - 1)
-diff --git a/net/ipv4/ipip.c b/net/ipv4/ipip.c
-index 40403114..e3c27cd 100644
---- a/net/ipv4/ipip.c
-+++ b/net/ipv4/ipip.c
-@@ -209,13 +209,38 @@ drop:
- }
- 
- /*
-+ * 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 void ipip_build_fan_iphdr(struct ip_tunnel *tunnel, struct sk_buff *skb, struct iphdr *iph)
-+{
-+	u32 daddr;
-+
-+	*iph = tunnel->parms.iph;
-+
-+	daddr = ntohl(ip_hdr(skb)->daddr);
-+	iph->daddr = htonl((tunnel->fan.underlay & 0xffff0000) |
-+			   ((daddr >> 8) & 0x0000ffff));
-+}
-+
-+/*
-  *	This function assumes it is being called from dev_queue_xmit()
-  *	and that skb is filled properly by that function.
-  */
- 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 +249,13 @@ static netdev_tx_t ipip_tunnel_xmit(struct sk_buff *skb, struct net_device *dev)
- 	if (IS_ERR(skb))
- 		goto out;
- 
-+	if (tunnel->fan.underlay) {
-+		ipip_build_fan_iphdr(tunnel, skb, &fiph);
-+		tiph = &fiph;
-+	} else {
-+		tiph = &tunnel->parms.iph;
-+	}
-+
- 	skb_set_inner_ipproto(skb, IPPROTO_IPIP);
- 
- 	ip_tunnel_xmit(skb, dev, tiph, tiph->protocol);
-@@ -377,21 +409,44 @@ static bool ipip_netlink_encap_parms(struct nlattr *data[],
- 	return ret;
- }
- 
-+static int ipip_netlink_fan(struct nlattr *data[], struct ip_tunnel *t,
-+			    struct ip_tunnel_parm *parms)
-+{
-+	u32 net = t->fan.underlay;
-+
-+	if (!data[IFLA_IPTUN_FAN_UNDERLAY])
-+		goto err_check;
-+
-+	net = ntohl(nla_get_be32(data[IFLA_IPTUN_FAN_UNDERLAY])) & 0xffff0000;
-+
-+err_check:
-+	if (parms->iph.daddr && net)
-+		return -EINVAL;
-+
-+	t->fan.underlay = net;
-+
-+	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);
- }
- 
-@@ -400,16 +455,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))
-@@ -441,6 +500,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_UNDERLAY */
-+		nla_total_size(4) +
- 		0;
- }
- 
-@@ -468,6 +529,11 @@ static int ipip_fill_info(struct sk_buff *skb, const struct net_device *dev)
- 			tunnel->encap.flags))
- 		goto nla_put_failure;
- 
-+	if (tunnel->fan.underlay)
-+		if (nla_put_be32(skb, IFLA_IPTUN_FAN_UNDERLAY,
-+				 htonl(tunnel->fan.underlay)))
-+			goto nla_put_failure;
-+
- 	return 0;
- 
- nla_put_failure:
-@@ -485,6 +551,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_UNDERLAY]	= { .type = NLA_U32 },
- };
- 
- static struct rtnl_link_ops ipip_link_ops __read_mostly = {
-@@ -524,6 +593,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 = 1;
-+
-+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;
-@@ -542,9 +628,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:
-@@ -554,6 +653,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.4.1
-
-From 4ea8011656dfdd76e7a2391bdad47c06f85a9d02 Mon Sep 17 00:00:00 2001
-From: Andy Whitcroft <apw@canonical.com>
-Date: Tue, 21 Jul 2015 16:52:10 +0100
-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>
-Acked-by: Tim Gardner <tim.gardner@canonical.com>
-Acked-by: Brad Figg <brad.figg@canonical.com>
-Signed-off-by: Brad Figg <brad.figg@canonical.com>
----
- include/net/ip_tunnels.h       |  14 ++++-
- include/uapi/linux/if_tunnel.h |  20 ++++++-
- net/ipv4/ip_tunnel.c           |   7 ++-
- net/ipv4/ipip.c                | 120 +++++++++++++++++++++++++++++++++--------
- 4 files changed, 133 insertions(+), 28 deletions(-)
-
-diff --git a/include/net/ip_tunnels.h b/include/net/ip_tunnels.h
-index d7eada2..2f7bc8c 100644
---- a/include/net/ip_tunnels.h
-+++ b/include/net/ip_tunnels.h
-@@ -51,9 +51,18 @@ struct ip_tunnel_dst {
- 	__be32				 saddr;
- };
- 
--/* Underlay address prefix for ipip fan mode */
-+/* 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			underlay;
-+/*	u32 __rcu	*map;*/
-+	u32		map[FAN_OVERLAY_CNT];
- };
- 
- struct ip_tunnel {
-@@ -104,6 +113,7 @@ struct ip_tunnel {
- #define TUNNEL_OAM		__cpu_to_be16(0x0200)
- #define TUNNEL_CRIT_OPT		__cpu_to_be16(0x0400)
- #define TUNNEL_GENEVE_OPT	__cpu_to_be16(0x0800)
- #define TUNNEL_VXLAN_OPT	__cpu_to_be16(0x1000)
-+#define TUNNEL_FAN		__cpu_to_be16(0x4000)
- 
- #define TUNNEL_OPTIONS_PRESENT	(TUNNEL_GENEVE_OPT | TUNNEL_VXLAN_OPT)
-diff --git a/include/uapi/linux/if_tunnel.h b/include/uapi/linux/if_tunnel.h
-index 8f7d269..9625934 100644
---- a/include/uapi/linux/if_tunnel.h
-+++ b/include/uapi/linux/if_tunnel.h
-@@ -58,8 +58,8 @@ enum {
- 	IFLA_IPTUN_ENCAP_SPORT,
- 	IFLA_IPTUN_ENCAP_DPORT,
- 
--	__IFLA_IPTUN_VENDOR_BREAK,		/* Ensure new entries do not hit the below. */
--	IFLA_IPTUN_FAN_UNDERLAY=32,
-+	__IFLA_IPTUN_VENDOR_BREAK, /* Ensure new entries do not hit the below. */
-+	IFLA_IPTUN_FAN_MAP = 33,
- 
- 	__IFLA_IPTUN_MAX,
- };
-@@ -135,4 +135,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 d3e4479..60bd10f 100644
---- a/net/ipv4/ip_tunnel.c
-+++ b/net/ipv4/ip_tunnel.c
-@@ -1078,6 +1078,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)
- {
-@@ -1087,7 +1092,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 e3c27cd..d6ebc66 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,11 @@ 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
-@@ -221,15 +227,20 @@ drop:
-  * 99.6.7.8, would be directed to underlay host 10.88.6.7, which hosts
-  * overlay network 99.6.7.0/24.
-  */
--static void ipip_build_fan_iphdr(struct ip_tunnel *tunnel, struct sk_buff *skb, struct iphdr *iph)
-+static int ipip_build_fan_iphdr(struct ip_tunnel *tunnel, struct sk_buff *skb, struct iphdr *iph)
- {
--	u32 daddr;
--
--	*iph = tunnel->parms.iph;
-+	unsigned int overlay;
-+	u32 daddr, underlay;
- 
- 	daddr = ntohl(ip_hdr(skb)->daddr);
--	iph->daddr = htonl((tunnel->fan.underlay & 0xffff0000) |
--			   ((daddr >> 8) & 0x0000ffff));
-+	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;
- }
- 
- /*
-@@ -249,8 +260,9 @@ static netdev_tx_t ipip_tunnel_xmit(struct sk_buff *skb, struct net_device *dev)
- 	if (IS_ERR(skb))
- 		goto out;
- 
--	if (tunnel->fan.underlay) {
--		ipip_build_fan_iphdr(tunnel, skb, &fiph);
-+	if (ipip_tunnel_is_fan(tunnel)) {
-+		if (ipip_build_fan_iphdr(tunnel, skb, &fiph))
-+			goto tx_error;
- 		tiph = &fiph;
- 	} else {
- 		tiph = &tunnel->parms.iph;
-@@ -409,21 +421,65 @@ 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)
- {
--	u32 net = t->fan.underlay;
--
--	if (!data[IFLA_IPTUN_FAN_UNDERLAY])
--		goto err_check;
-+	struct ip_tunnel_fan_map *map;
-+	struct nlattr *attr;
-+	int rem, rv;
- 
--	net = ntohl(nla_get_be32(data[IFLA_IPTUN_FAN_UNDERLAY])) & 0xffff0000;
-+	if (!data[IFLA_IPTUN_FAN_MAP])
-+		return 0;
- 
--err_check:
--	if (parms->iph.daddr && net)
-+	if (parms->iph.daddr)
- 		return -EINVAL;
- 
--	t->fan.underlay = net;
-+	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;
- }
-@@ -500,8 +556,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_UNDERLAY */
--		nla_total_size(4) +
-+		/* IFLA_IPTUN_FAN_MAP */
-+		nla_total_size(sizeof(struct ip_tunnel_fan_map)) * 256 +
- 		0;
- }
- 
-@@ -529,10 +585,28 @@ static int ipip_fill_info(struct sk_buff *skb, const struct net_device *dev)
- 			tunnel->encap.flags))
- 		goto nla_put_failure;
- 
--	if (tunnel->fan.underlay)
--		if (nla_put_be32(skb, IFLA_IPTUN_FAN_UNDERLAY,
--				 htonl(tunnel->fan.underlay)))
-+	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;
- 
-@@ -553,7 +627,7 @@ static const struct nla_policy ipip_policy[IFLA_IPTUN_MAX + 1] = {
- 	[IFLA_IPTUN_ENCAP_DPORT]	= { .type = NLA_U16 },
- 
- 	[__IFLA_IPTUN_VENDOR_BREAK ... IFLA_IPTUN_MAX]	= { .type = NLA_BINARY },
--	[IFLA_IPTUN_FAN_UNDERLAY]	= { .type = NLA_U32 },
-+	[IFLA_IPTUN_FAN_MAP]		= { .type = NLA_NESTED },
- };
- 
- static struct rtnl_link_ops ipip_link_ops __read_mostly = {
-@@ -595,7 +669,7 @@ static struct pernet_operations ipip_net_ops = {
- 
- #ifdef CONFIG_SYSCTL
- static struct ctl_table_header *ipip_fan_header;
--static unsigned int ipip_fan_version = 1;
-+static unsigned int ipip_fan_version = 3;
- 
- static struct ctl_table ipip_fan_sysctls[] = {
- 	{
--- 
-2.4.1
-