Netdev Archive on lore.kernel.org
help / color / mirror / Atom feed
* Re: [PATCH net-next] net: Variable SLAAC: SLAAC with prefixes of arbitrary length in PIO
       [not found] <175b25d0c79.f8ce5734515834.1635475016968827598@shytyi.net>
@ 2020-11-10 17:45 ` Dmytro Shytyi
  2020-11-11  1:34   ` kernel test robot
  0 siblings, 1 reply; 49+ messages in thread
From: Dmytro Shytyi @ 2020-11-10 17:45 UTC (permalink / raw)
  To: kuba, kuznet, yoshfuji, liuhangbin, davem, netdev, linux-kernel

Variable SLAAC: SLAAC with prefixes of arbitrary length in PIO (randomly
generated hostID or stable privacy + privacy extensions).
The main problem is that SLAAC RA or PD allocates a /64 by the Wireless
carrier 4G, 5G to a mobile hotspot, however segmentation of the /64 via
SLAAC is required so that downstream interfaces can be further subnetted.
Example: uCPE device (4G + WI-FI enabled) receives /64 via Wireless, and
assigns /72 to VNF-Firewall, /72 to WIFI, /72 to VNF-Router, /72 to
Load-Balancer and /72 to wired connected devices.
IETF document that defines problem statement:
draft-mishra-v6ops-variable-slaac-problem-stmt
IETF document that specifies variable slaac:
draft-mishra-6man-variable-slaac

Signed-off-by: Dmytro Shytyi <dmytro@shytyi.net>
---
diff -rupN net-next-5.10.0-rc2/include/net/if_inet6.h net-next-patch-v2-5.10.0-rc2/include/net/if_inet6.h
--- net-next-5.10.0-rc2/include/net/if_inet6.h	2020-11-10 08:46:00.195180579 +0100
+++ net-next-patch-v2-5.10.0-rc2/include/net/if_inet6.h	2020-11-10 12:43:26.866166351 +0100
@@ -22,6 +22,12 @@
 #define IF_RS_SENT	0x10
 #define IF_READY	0x80000000
 
+/* Variable SLAAC (Contact: Dmytro Shytyi)
+ * draft-mishra-6man-variable-slaac
+ * draft-mishra-v6ops-variable-slaac-problem-stmt
+ */
+#define IF_RA_VAR_PLEN	0x08
+
 /* prefix flags */
 #define IF_PREFIX_ONLINK	0x01
 #define IF_PREFIX_AUTOCONF	0x02
diff -rupN net-next-5.10.0-rc2/include/uapi/linux/icmpv6.h net-next-patch-v2-5.10.0-rc2/include/uapi/linux/icmpv6.h
--- net-next-5.10.0-rc2/include/uapi/linux/icmpv6.h	2020-11-10 08:46:00.351849525 +0100
+++ net-next-patch-v2-5.10.0-rc2/include/uapi/linux/icmpv6.h	2020-11-10 12:43:26.866166351 +0100
@@ -42,7 +42,9 @@ struct icmp6hdr {
                 struct icmpv6_nd_ra {
 			__u8		hop_limit;
 #if defined(__LITTLE_ENDIAN_BITFIELD)
-			__u8		reserved:3,
+			__u8		reserved:1,
+					slaac_var_plen:1,
+					proxy:1,
 					router_pref:2,
 					home_agent:1,
 					other:1,
@@ -53,7 +55,9 @@ struct icmp6hdr {
 					other:1,
 					home_agent:1,
 					router_pref:2,
-					reserved:3;
+					proxy:1,
+					slaac_var_plen:1,
+					reserved:1;
 #else
 #error	"Please fix <asm/byteorder.h>"
 #endif
@@ -78,9 +82,9 @@ struct icmp6hdr {
 #define icmp6_addrconf_other	icmp6_dataun.u_nd_ra.other
 #define icmp6_rt_lifetime	icmp6_dataun.u_nd_ra.rt_lifetime
 #define icmp6_router_pref	icmp6_dataun.u_nd_ra.router_pref
+#define icmp6_slaac_var_plen	icmp6_dataun.u_nd_ra.slaac_var_plen
 };
 
-
 #define ICMPV6_ROUTER_PREF_LOW		0x3
 #define ICMPV6_ROUTER_PREF_MEDIUM	0x0
 #define ICMPV6_ROUTER_PREF_HIGH		0x1
diff -rupN net-next-5.10.0-rc2/net/ipv6/addrconf.c net-next-patch-v2-5.10.0-rc2/net/ipv6/addrconf.c
--- net-next-5.10.0-rc2/net/ipv6/addrconf.c	2020-11-10 08:46:01.075193379 +0100
+++ net-next-patch-v2-5.10.0-rc2/net/ipv6/addrconf.c	2020-11-10 13:09:55.762384640 +0100
@@ -11,6 +11,8 @@
 /*
  *	Changes:
  *
+ *	Dmytro Shytyi			:	Variable SLAAC: SLAAC with
+ *	<dmytro@shytyi.net>			prefixes of arbitrary length.
  *	Janos Farkas			:	delete timer on ifdown
  *	<chexum@bankinf.banki.hu>
  *	Andi Kleen			:	kill double kfree on module
@@ -142,7 +144,12 @@ static int ipv6_count_addresses(const st
 static int ipv6_generate_stable_address(struct in6_addr *addr,
 					u8 dad_count,
 					const struct inet6_dev *idev);
-
+static int ipv6_generate_address_variable_plen(struct in6_addr *address,
+					       u8 dad_count,
+					       const struct inet6_dev *idev,
+					       unsigned int rcvd_prfx_len,
+					       bool stable_privacy_mode);
+unsigned char reverse_bits(unsigned char num);
 #define IN6_ADDR_HSIZE_SHIFT	8
 #define IN6_ADDR_HSIZE		(1 << IN6_ADDR_HSIZE_SHIFT)
 /*
@@ -1314,9 +1321,21 @@ static int ipv6_create_tempaddr(struct i
 	struct inet6_ifaddr *ift;
 	struct ifa6_config cfg;
 	long max_desync_factor;
-	struct in6_addr addr;
+
+	struct in6_addr temp, addr;
+
 	int ret = 0;
 
+	__int128 host_id;
+	__int128 net_prfx;
+	__int128 ipv6addr;
+	__int128 mask_128;
+	__int128 mask_host_id;
+	__int128 mask_net_prfx;
+	int i;
+	unsigned char mask_host_id_arr[128];
+
+	memset(&mask_128, 0xFF, 16);
 	write_lock_bh(&idev->lock);
 
 retry:
@@ -1340,9 +1359,30 @@ retry:
 		goto out;
 	}
 	in6_ifa_hold(ifp);
-	memcpy(addr.s6_addr, ifp->addr.s6_addr, 8);
-	ipv6_gen_rnd_iid(&addr);
 
+	if (ifp->prefix_len == 64) {
+		memcpy(addr.s6_addr, ifp->addr.s6_addr, 8);
+		ipv6_gen_rnd_iid(&addr);
+	} else if (ifp->prefix_len > 0 && ifp->prefix_len <= 128) {
+		memcpy(addr.s6_addr, ifp->addr.s6_addr, 16);
+		get_random_bytes(temp.s6_addr32, 16);
+
+		memcpy(&host_id, temp.s6_addr32, sizeof(host_id));
+		memcpy(&net_prfx, addr.s6_addr, sizeof(net_prfx));
+
+		mask_host_id = ifp->prefix_len != 128 ? (mask_128 << ifp->prefix_len) : 0x0;
+		memcpy(mask_host_id_arr, &mask_host_id, 16);
+		for (i = 0; i < 128; i++)
+			mask_host_id_arr[i] = reverse_bits(mask_host_id_arr[i]);
+		memcpy(&mask_host_id, mask_host_id_arr, 16);
+		host_id = host_id & mask_host_id;
+
+		mask_net_prfx = mask_128 ^ mask_host_id;
+		net_prfx = net_prfx & mask_net_prfx;
+
+		ipv6addr = net_prfx | host_id;
+		memcpy(addr.s6_addr, &ipv6addr, 16);
+	}
 	age = (now - ifp->tstamp) / HZ;
 
 	regen_advance = idev->cnf.regen_max_retry *
@@ -2576,9 +2616,61 @@ int addrconf_prefix_rcv_add_addr(struct
 				 u32 addr_flags, bool sllao, bool tokenized,
 				 __u32 valid_lft, u32 prefered_lft)
 {
-	struct inet6_ifaddr *ifp = ipv6_get_ifaddr(net, addr, dev, 1);
+	struct inet6_ifaddr *ifp = NULL;
 	int create = 0;
 
+	if ((in6_dev->if_flags & IF_RA_VAR_PLEN) == IF_RA_VAR_PLEN &&
+	    in6_dev->cnf.addr_gen_mode != IN6_ADDR_GEN_MODE_STABLE_PRIVACY) {
+		struct inet6_ifaddr *result = NULL;
+		struct inet6_ifaddr *result_base = NULL;
+		__int128 mask_128;
+		__int128 mask_net_prfx;
+		__int128 net_prfx;
+		__int128 curr_net_prfx;
+		int hostid_len;
+		int i;
+		unsigned char mask_host_id_arr[128];
+
+		memset(&mask_128, 0xFF, 16);
+		result_base = result;
+		rcu_read_lock();
+		list_for_each_entry_rcu(ifp, &in6_dev->addr_list, if_list) {
+			if (!net_eq(dev_net(ifp->idev->dev), net))
+				continue;
+			/* 128bit network prefix mask calculation */
+			hostid_len = 128 - pinfo->prefix_len;
+			mask_net_prfx = pinfo->prefix_len != 128 ? (mask_128 << pinfo->prefix_len) : 0x0;
+			mask_net_prfx = ~mask_net_prfx;
+			memcpy(mask_host_id_arr, &mask_net_prfx, 16);
+			for (i = 0; i < 128; i++)
+				mask_host_id_arr[i] = reverse_bits(mask_host_id_arr[i]);
+			memcpy(&mask_net_prfx, mask_host_id_arr, 16);
+
+			/* Received/new IPv6 prefix */
+			memcpy(&net_prfx, pinfo->prefix.s6_addr32, 16);
+			net_prfx &= mask_net_prfx;
+
+			/* Configured/old IPv6 prefix */
+			memcpy(&curr_net_prfx, ifp->addr.s6_addr32, 16);
+			curr_net_prfx &=  mask_net_prfx;
+
+			/* IPv6 prefixes comparison */
+			if ((net_prfx ^ curr_net_prfx) == 0 &&
+			    pinfo->prefix_len == ifp->prefix_len) {
+				result = ifp;
+				in6_ifa_hold(ifp);
+				break;
+			}
+		}
+		rcu_read_unlock();
+		if (result_base != result)
+			ifp = result;
+		else
+			ifp = NULL;
+	} else {
+		ifp = ipv6_get_ifaddr(net, addr, dev, 1);
+	}
+
 	if (!ifp && valid_lft) {
 		int max_addresses = in6_dev->cnf.max_addresses;
 		struct ifa6_config cfg = {
@@ -2781,9 +2873,35 @@ void addrconf_prefix_rcv(struct net_devi
 				dev_addr_generated = true;
 			}
 			goto ok;
+		goto put;
+		} else if (((in6_dev->if_flags & IF_RA_VAR_PLEN) == IF_RA_VAR_PLEN) &&
+			  pinfo->prefix_len > 0 && pinfo->prefix_len <= 128) {
+			/* SLAAC with prefixes of arbitrary length (Variable SLAAC).
+			 * draft-mishra-6man-variable-slaac
+			 * draft-mishra-v6ops-variable-slaac-problem-stmt
+			 * Contact: Dmytro Shytyi.
+			 */
+			memcpy(&addr, &pinfo->prefix, 16);
+			if (in6_dev->cnf.addr_gen_mode == IN6_ADDR_GEN_MODE_STABLE_PRIVACY) {
+				if (!ipv6_generate_address_variable_plen(&addr,
+									 0,
+									 in6_dev,
+									 pinfo->prefix_len,
+									 true)) {
+					addr_flags |= IFA_F_STABLE_PRIVACY;
+					goto ok;
+			}
+			} else if (!ipv6_generate_address_variable_plen(&addr,
+									0,
+									in6_dev,
+									pinfo->prefix_len,
+									false)) {
+				goto ok;
+			}
+		} else {
+			net_dbg_ratelimited("IPv6: Prefix with unexpected length %d\n",
+					    pinfo->prefix_len);
 		}
-		net_dbg_ratelimited("IPv6 addrconf: prefix with wrong length %d\n",
-				    pinfo->prefix_len);
 		goto put;
 
 ok:
@@ -3264,6 +3382,119 @@ retry:
 	return 0;
 }
 
+unsigned char reverse_bits(unsigned char num)
+{
+	unsigned char count = sizeof(num) * 8 - 1;
+	unsigned char reverse_num = num;
+
+	num >>= 1;
+	while (num) {
+		reverse_num <<= 1;
+		reverse_num |= num & 1;
+		num >>= 1;
+		count--;
+	}
+	reverse_num <<= count;
+	return reverse_num;
+}
+
+static int ipv6_generate_address_variable_plen(struct in6_addr *address,
+					       u8 dad_count,
+					       const struct inet6_dev *idev,
+					       unsigned int rcvd_prfx_len,
+					       bool stable_privacy_mode)
+{
+	static DEFINE_SPINLOCK(lock);
+	static __u32 digest[SHA1_DIGEST_WORDS];
+	static __u32 workspace[SHA1_WORKSPACE_WORDS];
+
+	static union {
+		char __data[SHA1_BLOCK_SIZE];
+		struct {
+			struct in6_addr secret;
+			__be32 prefix[2];
+			unsigned char hwaddr[MAX_ADDR_LEN];
+			u8 dad_count;
+		} __packed;
+	} data;
+
+	struct in6_addr secret;
+	struct in6_addr temp;
+	struct net *net = dev_net(idev->dev);
+	__int128 host_id;
+	__int128 net_prfx;
+	__int128 ipv6addr;
+	__int128 mask_128;
+	__int128 mask_host_id;
+	__int128 mask_net_prfx;
+	int i;
+	unsigned char mask_host_id_arr[128];
+
+	memset(&mask_128, 0xFF, 16);
+	BUILD_BUG_ON(sizeof(data.__data) != sizeof(data));
+
+	if (stable_privacy_mode) {
+		if (idev->cnf.stable_secret.initialized)
+			secret = idev->cnf.stable_secret.secret;
+		else if (net->ipv6.devconf_dflt->stable_secret.initialized)
+			secret = net->ipv6.devconf_dflt->stable_secret.secret;
+		else
+			return -1;
+	}
+
+retry:
+	spin_lock_bh(&lock);
+	if (stable_privacy_mode) {
+		sha1_init(digest);
+		memset(&data, 0, sizeof(data));
+		memset(workspace, 0, sizeof(workspace));
+		memcpy(data.hwaddr, idev->dev->perm_addr, idev->dev->addr_len);
+		data.prefix[0] = address->s6_addr32[0];
+		data.prefix[1] = address->s6_addr32[1];
+		data.secret = secret;
+		data.dad_count = dad_count;
+
+		sha1_transform(digest, data.__data, workspace);
+
+		temp = *address;
+		temp.s6_addr32[0] = (__force __be32)digest[0];
+		temp.s6_addr32[1] = (__force __be32)digest[1];
+		temp.s6_addr32[2] = (__force __be32)digest[2];
+		temp.s6_addr32[3] = (__force __be32)digest[3];
+	} else {
+		temp = *address;
+		get_random_bytes(temp.s6_addr32, 16);
+	}
+	spin_unlock_bh(&lock);
+
+	if (ipv6_reserved_interfaceid(temp)) {
+		dad_count++;
+		if (dad_count > dev_net(idev->dev)->ipv6.sysctl.idgen_retries)
+			return -1;
+		goto retry;
+	}
+
+	memcpy(&host_id, temp.s6_addr32, 16);
+	memcpy(&net_prfx, address->s6_addr32, 16);
+
+	mask_host_id = rcvd_prfx_len != 128 ? (mask_128 << rcvd_prfx_len) : 0x0;
+	memcpy(mask_host_id_arr, &mask_host_id, 16);
+	for (i = 0; i < 128; i++)
+		mask_host_id_arr[i] = reverse_bits(mask_host_id_arr[i]);
+	memcpy(&mask_host_id, mask_host_id_arr, 16);
+	host_id = host_id & mask_host_id;
+
+	mask_net_prfx = mask_128 ^ mask_host_id;
+	net_prfx = net_prfx & mask_net_prfx;
+
+	ipv6addr = net_prfx | host_id;
+	memcpy(temp.s6_addr32, &ipv6addr, 16);
+
+	*address = temp;
+
+	return 0;
+}
+
 static void ipv6_gen_mode_random_init(struct inet6_dev *idev)
 {
 	struct ipv6_stable_secret *s = &idev->cnf.stable_secret;
diff -rupN net-next-5.10.0-rc2/net/ipv6/ndisc.c net-next-patch-v2-5.10.0-rc2/net/ipv6/ndisc.c
--- net-next-5.10.0-rc2/net/ipv6/ndisc.c	2020-11-10 08:46:01.091860289 +0100
+++ net-next-patch-v2-5.10.0-rc2/net/ipv6/ndisc.c	2020-11-10 12:43:26.869499720 +0100
@@ -1244,6 +1244,8 @@ static void ndisc_router_discovery(struc
 		in6_dev->if_flags |= IF_RA_RCVD;
 	}
 
+	in6_dev->if_flags |= ra_msg->icmph.icmp6_slaac_var_plen ?
+					IF_RA_VAR_PLEN : 0;
 	/*
 	 * Remember the managed/otherconf flags from most recently
 	 * received RA message (RFC 2462) -- yoshfuji

^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: Re: [PATCH net-next] net: Variable SLAAC: SLAAC with prefixes of arbitrary length in PIO
  2020-11-10 17:45 ` [PATCH net-next] net: Variable SLAAC: SLAAC with prefixes of arbitrary length in PIO Dmytro Shytyi
@ 2020-11-11  1:34   ` kernel test robot
  2020-11-11 20:37     ` [PATCH net-next V2] " Dmytro Shytyi
                       ` (2 more replies)
  0 siblings, 3 replies; 49+ messages in thread
From: kernel test robot @ 2020-11-11  1:34 UTC (permalink / raw)
  To: Dmytro Shytyi, kuba, kuznet, yoshfuji, liuhangbin, davem, netdev,
	linux-kernel
  Cc: kbuild-all

[-- Attachment #1: Type: text/plain, Size: 12862 bytes --]

Hi Dmytro,

Thank you for the patch! Yet something to improve:

[auto build test ERROR on net-next/master]

url:    https://github.com/0day-ci/linux/commits/Dmytro-Shytyi/Re-PATCH-net-next-net-Variable-SLAAC-SLAAC-with-prefixes-of-arbitrary-length-in-PIO/20201111-014800
base:   https://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next.git 8be33ecfc1ffd2da20cc29e957e4cb6eb99310cb
config: sh-allmodconfig (attached as .config)
compiler: sh4-linux-gcc (GCC) 9.3.0
reproduce (this is a W=1 build):
        wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # https://github.com/0day-ci/linux/commit/0d851d20831574b490bbb131cb68f722dc2419ca
        git remote add linux-review https://github.com/0day-ci/linux
        git fetch --no-tags linux-review Dmytro-Shytyi/Re-PATCH-net-next-net-Variable-SLAAC-SLAAC-with-prefixes-of-arbitrary-length-in-PIO/20201111-014800
        git checkout 0d851d20831574b490bbb131cb68f722dc2419ca
        # save the attached .config to linux build tree
        COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-9.3.0 make.cross ARCH=sh 

If you fix the issue, kindly add following tag as appropriate
Reported-by: kernel test robot <lkp@intel.com>

All error/warnings (new ones prefixed by >>):

   net/ipv6/addrconf.c: In function 'ipv6_create_tempaddr':
>> net/ipv6/addrconf.c:1329:2: error: expected expression before '__int128'
    1329 |  __int128 host_id;
         |  ^~~~~~~~
   net/ipv6/addrconf.c:1330:2: error: expected expression before '__int128'
    1330 |  __int128 net_prfx;
         |  ^~~~~~~~
   net/ipv6/addrconf.c:1331:2: error: expected expression before '__int128'
    1331 |  __int128 ipv6addr;
         |  ^~~~~~~~
   net/ipv6/addrconf.c:1332:2: error: expected expression before '__int128'
    1332 |  __int128 mask_128;
         |  ^~~~~~~~
   net/ipv6/addrconf.c:1333:2: error: expected expression before '__int128'
    1333 |  __int128 mask_host_id;
         |  ^~~~~~~~
   net/ipv6/addrconf.c:1334:2: error: expected expression before '__int128'
    1334 |  __int128 mask_net_prfx;
         |  ^~~~~~~~
>> net/ipv6/addrconf.c:1335:2: warning: ISO C90 forbids mixed declarations and code [-Wdeclaration-after-statement]
    1335 |  int i;
         |  ^~~
>> net/ipv6/addrconf.c:1338:10: error: 'mask_128' undeclared (first use in this function)
    1338 |  memset(&mask_128, 0xFF, 16);
         |          ^~~~~~~~
   net/ipv6/addrconf.c:1338:10: note: each undeclared identifier is reported only once for each function it appears in
>> net/ipv6/addrconf.c:1370:11: error: 'host_id' undeclared (first use in this function)
    1370 |   memcpy(&host_id, temp.s6_addr32, sizeof(host_id));
         |           ^~~~~~~
>> net/ipv6/addrconf.c:1371:11: error: 'net_prfx' undeclared (first use in this function)
    1371 |   memcpy(&net_prfx, addr.s6_addr, sizeof(net_prfx));
         |           ^~~~~~~~
>> net/ipv6/addrconf.c:1373:3: error: 'mask_host_id' undeclared (first use in this function); did you mean 'mask_host_id_arr'?
    1373 |   mask_host_id = ifp->prefix_len != 128 ? (mask_128 << ifp->prefix_len) : 0x0;
         |   ^~~~~~~~~~~~
         |   mask_host_id_arr
>> net/ipv6/addrconf.c:1380:3: error: 'mask_net_prfx' undeclared (first use in this function)
    1380 |   mask_net_prfx = mask_128 ^ mask_host_id;
         |   ^~~~~~~~~~~~~
>> net/ipv6/addrconf.c:1383:3: error: 'ipv6addr' undeclared (first use in this function); did you mean 'ipv6_hdr'?
    1383 |   ipv6addr = net_prfx | host_id;
         |   ^~~~~~~~
         |   ipv6_hdr
   net/ipv6/addrconf.c: In function 'addrconf_prefix_rcv_add_addr':
   net/ipv6/addrconf.c:2626:3: error: expected expression before '__int128'
    2626 |   __int128 mask_128;
         |   ^~~~~~~~
   net/ipv6/addrconf.c:2627:3: error: expected expression before '__int128'
    2627 |   __int128 mask_net_prfx;
         |   ^~~~~~~~
   net/ipv6/addrconf.c:2628:3: error: expected expression before '__int128'
    2628 |   __int128 net_prfx;
         |   ^~~~~~~~
   net/ipv6/addrconf.c:2629:3: error: expected expression before '__int128'
    2629 |   __int128 curr_net_prfx;
         |   ^~~~~~~~
   net/ipv6/addrconf.c:2630:3: warning: ISO C90 forbids mixed declarations and code [-Wdeclaration-after-statement]
    2630 |   int hostid_len;
         |   ^~~
   net/ipv6/addrconf.c:2634:11: error: 'mask_128' undeclared (first use in this function)
    2634 |   memset(&mask_128, 0xFF, 16);
         |           ^~~~~~~~
   net/ipv6/addrconf.c:2642:4: error: 'mask_net_prfx' undeclared (first use in this function)
    2642 |    mask_net_prfx = pinfo->prefix_len != 128 ? (mask_128 << pinfo->prefix_len) : 0x0;
         |    ^~~~~~~~~~~~~
   net/ipv6/addrconf.c:2650:12: error: 'net_prfx' undeclared (first use in this function)
    2650 |    memcpy(&net_prfx, pinfo->prefix.s6_addr32, 16);
         |            ^~~~~~~~
>> net/ipv6/addrconf.c:2654:12: error: 'curr_net_prfx' undeclared (first use in this function)
    2654 |    memcpy(&curr_net_prfx, ifp->addr.s6_addr32, 16);
         |            ^~~~~~~~~~~~~
>> net/ipv6/addrconf.c:2630:7: warning: variable 'hostid_len' set but not used [-Wunused-but-set-variable]
    2630 |   int hostid_len;
         |       ^~~~~~~~~~
   net/ipv6/addrconf.c: In function 'ipv6_generate_address_variable_plen':
   net/ipv6/addrconf.c:3424:2: error: expected expression before '__int128'
    3424 |  __int128 host_id;
         |  ^~~~~~~~
   net/ipv6/addrconf.c:3425:2: error: expected expression before '__int128'
    3425 |  __int128 net_prfx;
         |  ^~~~~~~~
   net/ipv6/addrconf.c:3426:2: error: expected expression before '__int128'
    3426 |  __int128 ipv6addr;
         |  ^~~~~~~~
   net/ipv6/addrconf.c:3427:2: error: expected expression before '__int128'
    3427 |  __int128 mask_128;
         |  ^~~~~~~~
   net/ipv6/addrconf.c:3428:2: error: expected expression before '__int128'
    3428 |  __int128 mask_host_id;
         |  ^~~~~~~~
   net/ipv6/addrconf.c:3429:2: error: expected expression before '__int128'
    3429 |  __int128 mask_net_prfx;
         |  ^~~~~~~~
   net/ipv6/addrconf.c:3430:2: warning: ISO C90 forbids mixed declarations and code [-Wdeclaration-after-statement]
    3430 |  int i;
         |  ^~~
   net/ipv6/addrconf.c:3433:10: error: 'mask_128' undeclared (first use in this function)
    3433 |  memset(&mask_128, 0xFF, 16);
         |          ^~~~~~~~
   net/ipv6/addrconf.c:3477:10: error: 'host_id' undeclared (first use in this function)
    3477 |  memcpy(&host_id, temp.s6_addr32, 16);
         |          ^~~~~~~
   net/ipv6/addrconf.c:3478:10: error: 'net_prfx' undeclared (first use in this function)
    3478 |  memcpy(&net_prfx, address->s6_addr32, 16);
         |          ^~~~~~~~
   net/ipv6/addrconf.c:3480:2: error: 'mask_host_id' undeclared (first use in this function); did you mean 'mask_host_id_arr'?
    3480 |  mask_host_id = rcvd_prfx_len != 128 ? (mask_128 << rcvd_prfx_len) : 0x0;
         |  ^~~~~~~~~~~~
         |  mask_host_id_arr
   net/ipv6/addrconf.c:3487:2: error: 'mask_net_prfx' undeclared (first use in this function)
    3487 |  mask_net_prfx = mask_128 ^ mask_host_id;
         |  ^~~~~~~~~~~~~
   net/ipv6/addrconf.c:3490:2: error: 'ipv6addr' undeclared (first use in this function); did you mean 'ipv6_hdr'?
    3490 |  ipv6addr = net_prfx | host_id;
         |  ^~~~~~~~
         |  ipv6_hdr

vim +/__int128 +1329 net/ipv6/addrconf.c

  1313	
  1314	static int ipv6_create_tempaddr(struct inet6_ifaddr *ifp, bool block)
  1315	{
  1316		struct inet6_dev *idev = ifp->idev;
  1317		unsigned long tmp_tstamp, age;
  1318		unsigned long regen_advance;
  1319		unsigned long now = jiffies;
  1320		s32 cnf_temp_preferred_lft;
  1321		struct inet6_ifaddr *ift;
  1322		struct ifa6_config cfg;
  1323		long max_desync_factor;
  1324	
  1325		struct in6_addr temp, addr;
  1326	
  1327		int ret = 0;
  1328	
> 1329		__int128 host_id;
  1330		__int128 net_prfx;
  1331		__int128 ipv6addr;
  1332		__int128 mask_128;
  1333		__int128 mask_host_id;
  1334		__int128 mask_net_prfx;
> 1335		int i;
  1336		unsigned char mask_host_id_arr[128];
  1337	
> 1338		memset(&mask_128, 0xFF, 16);
  1339		write_lock_bh(&idev->lock);
  1340	
  1341	retry:
  1342		in6_dev_hold(idev);
  1343		if (idev->cnf.use_tempaddr <= 0) {
  1344			write_unlock_bh(&idev->lock);
  1345			pr_info("%s: use_tempaddr is disabled\n", __func__);
  1346			in6_dev_put(idev);
  1347			ret = -1;
  1348			goto out;
  1349		}
  1350		spin_lock_bh(&ifp->lock);
  1351		if (ifp->regen_count++ >= idev->cnf.regen_max_retry) {
  1352			idev->cnf.use_tempaddr = -1;	/*XXX*/
  1353			spin_unlock_bh(&ifp->lock);
  1354			write_unlock_bh(&idev->lock);
  1355			pr_warn("%s: regeneration time exceeded - disabled temporary address support\n",
  1356				__func__);
  1357			in6_dev_put(idev);
  1358			ret = -1;
  1359			goto out;
  1360		}
  1361		in6_ifa_hold(ifp);
  1362	
  1363		if (ifp->prefix_len == 64) {
  1364			memcpy(addr.s6_addr, ifp->addr.s6_addr, 8);
  1365			ipv6_gen_rnd_iid(&addr);
  1366		} else if (ifp->prefix_len > 0 && ifp->prefix_len <= 128) {
  1367			memcpy(addr.s6_addr, ifp->addr.s6_addr, 16);
  1368			get_random_bytes(temp.s6_addr32, 16);
  1369	
> 1370			memcpy(&host_id, temp.s6_addr32, sizeof(host_id));
> 1371			memcpy(&net_prfx, addr.s6_addr, sizeof(net_prfx));
  1372	
> 1373			mask_host_id = ifp->prefix_len != 128 ? (mask_128 << ifp->prefix_len) : 0x0;
  1374			memcpy(mask_host_id_arr, &mask_host_id, 16);
  1375			for (i = 0; i < 128; i++)
  1376				mask_host_id_arr[i] = reverse_bits(mask_host_id_arr[i]);
  1377			memcpy(&mask_host_id, mask_host_id_arr, 16);
  1378			host_id = host_id & mask_host_id;
  1379	
> 1380			mask_net_prfx = mask_128 ^ mask_host_id;
  1381			net_prfx = net_prfx & mask_net_prfx;
  1382	
> 1383			ipv6addr = net_prfx | host_id;
  1384			memcpy(addr.s6_addr, &ipv6addr, 16);
  1385		}
  1386		age = (now - ifp->tstamp) / HZ;
  1387	
  1388		regen_advance = idev->cnf.regen_max_retry *
  1389				idev->cnf.dad_transmits *
  1390				max(NEIGH_VAR(idev->nd_parms, RETRANS_TIME), HZ/100) / HZ;
  1391	
  1392		/* recalculate max_desync_factor each time and update
  1393		 * idev->desync_factor if it's larger
  1394		 */
  1395		cnf_temp_preferred_lft = READ_ONCE(idev->cnf.temp_prefered_lft);
  1396		max_desync_factor = min_t(__u32,
  1397					  idev->cnf.max_desync_factor,
  1398					  cnf_temp_preferred_lft - regen_advance);
  1399	
  1400		if (unlikely(idev->desync_factor > max_desync_factor)) {
  1401			if (max_desync_factor > 0) {
  1402				get_random_bytes(&idev->desync_factor,
  1403						 sizeof(idev->desync_factor));
  1404				idev->desync_factor %= max_desync_factor;
  1405			} else {
  1406				idev->desync_factor = 0;
  1407			}
  1408		}
  1409	
  1410		memset(&cfg, 0, sizeof(cfg));
  1411		cfg.valid_lft = min_t(__u32, ifp->valid_lft,
  1412				      idev->cnf.temp_valid_lft + age);
  1413		cfg.preferred_lft = cnf_temp_preferred_lft + age - idev->desync_factor;
  1414		cfg.preferred_lft = min_t(__u32, ifp->prefered_lft, cfg.preferred_lft);
  1415	
  1416		cfg.plen = ifp->prefix_len;
  1417		tmp_tstamp = ifp->tstamp;
  1418		spin_unlock_bh(&ifp->lock);
  1419	
  1420		write_unlock_bh(&idev->lock);
  1421	
  1422		/* A temporary address is created only if this calculated Preferred
  1423		 * Lifetime is greater than REGEN_ADVANCE time units.  In particular,
  1424		 * an implementation must not create a temporary address with a zero
  1425		 * Preferred Lifetime.
  1426		 * Use age calculation as in addrconf_verify to avoid unnecessary
  1427		 * temporary addresses being generated.
  1428		 */
  1429		age = (now - tmp_tstamp + ADDRCONF_TIMER_FUZZ_MINUS) / HZ;
  1430		if (cfg.preferred_lft <= regen_advance + age) {
  1431			in6_ifa_put(ifp);
  1432			in6_dev_put(idev);
  1433			ret = -1;
  1434			goto out;
  1435		}
  1436	
  1437		cfg.ifa_flags = IFA_F_TEMPORARY;
  1438		/* set in addrconf_prefix_rcv() */
  1439		if (ifp->flags & IFA_F_OPTIMISTIC)
  1440			cfg.ifa_flags |= IFA_F_OPTIMISTIC;
  1441	
  1442		cfg.pfx = &addr;
  1443		cfg.scope = ipv6_addr_scope(cfg.pfx);
  1444	
  1445		ift = ipv6_add_addr(idev, &cfg, block, NULL);
  1446		if (IS_ERR(ift)) {
  1447			in6_ifa_put(ifp);
  1448			in6_dev_put(idev);
  1449			pr_info("%s: retry temporary address regeneration\n", __func__);
  1450			write_lock_bh(&idev->lock);
  1451			goto retry;
  1452		}
  1453	
  1454		spin_lock_bh(&ift->lock);
  1455		ift->ifpub = ifp;
  1456		ift->cstamp = now;
  1457		ift->tstamp = tmp_tstamp;
  1458		spin_unlock_bh(&ift->lock);
  1459	
  1460		addrconf_dad_start(ift);
  1461		in6_ifa_put(ift);
  1462		in6_dev_put(idev);
  1463	out:
  1464		return ret;
  1465	}
  1466	

---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/kbuild-all@lists.01.org

[-- Attachment #2: .config.gz --]
[-- Type: application/gzip, Size: 53504 bytes --]

^ permalink raw reply	[flat|nested] 49+ messages in thread

* [PATCH net-next V2] net: Variable SLAAC: SLAAC with prefixes of arbitrary length in PIO
  2020-11-11  1:34   ` kernel test robot
@ 2020-11-11 20:37     ` Dmytro Shytyi
  2020-11-12 15:44     ` [PATCH net-next V3] " Dmytro Shytyi
  2020-11-13  0:24     ` [PATCH net-next] " Jakub Kicinski
  2 siblings, 0 replies; 49+ messages in thread
From: Dmytro Shytyi @ 2020-11-11 20:37 UTC (permalink / raw)
  To: kuba, kuznet, yoshfuji, liuhangbin, davem, netdev, linux-kernel

Variable SLAAC: SLAAC with prefixes of arbitrary length in PIO (randomly
generated hostID or stable privacy + privacy extensions).
The main problem is that SLAAC RA or PD allocates a /64 by the Wireless
carrier 4G, 5G to a mobile hotspot, however segmentation of the /64 via
SLAAC is required so that downstream interfaces can be further subnetted.
Example: uCPE device (4G + WI-FI enabled) receives /64 via Wireless, and
assigns /72 to VNF-Firewall, /72 to WIFI, /72 to VNF-Router, /72 to
Load-Balancer and /72 to wired connected devices.
IETF document that defines problem statement:
draft-mishra-v6ops-variable-slaac-problem-stmt
IETF document that specifies variable slaac:
draft-mishra-6man-variable-slaac

Signed-off-by: Dmytro Shytyi <dmytro@shytyi.net>
Reported-by: kernel test robot <lkp@intel.com>
---
diff -rupN net-next-5.10.0-rc2/include/net/if_inet6.h net-next-patch-5.10.0-rc2/include/net/if_inet6.h
--- net-next-5.10.0-rc2/include/net/if_inet6.h	2020-11-10 08:46:00.195180579 +0100
+++ net-next-patch-5.10.0-rc2/include/net/if_inet6.h	2020-11-11 18:11:05.627550135 +0100
@@ -22,6 +22,12 @@
 #define IF_RS_SENT	0x10
 #define IF_READY	0x80000000
 
+/* Variable SLAAC (Contact: Dmytro Shytyi)
+ * draft-mishra-6man-variable-slaac
+ * draft-mishra-v6ops-variable-slaac-problem-stmt
+ */
+#define IF_RA_VAR_PLEN	0x08
+
 /* prefix flags */
 #define IF_PREFIX_ONLINK	0x01
 #define IF_PREFIX_AUTOCONF	0x02
diff -rupN net-next-5.10.0-rc2/include/uapi/linux/icmpv6.h net-next-patch-5.10.0-rc2/include/uapi/linux/icmpv6.h
--- net-next-5.10.0-rc2/include/uapi/linux/icmpv6.h	2020-11-10 08:46:00.351849525 +0100
+++ net-next-patch-5.10.0-rc2/include/uapi/linux/icmpv6.h	2020-11-11 18:11:05.627550135 +0100
@@ -42,7 +42,9 @@ struct icmp6hdr {
                 struct icmpv6_nd_ra {
 			__u8		hop_limit;
 #if defined(__LITTLE_ENDIAN_BITFIELD)
-			__u8		reserved:3,
+			__u8		reserved:1,
+					slaac_var_plen:1,
+					proxy:1,
 					router_pref:2,
 					home_agent:1,
 					other:1,
@@ -53,7 +55,9 @@ struct icmp6hdr {
 					other:1,
 					home_agent:1,
 					router_pref:2,
-					reserved:3;
+					proxy:1,
+					slaac_var_plen:1,
+					reserved:1;
 #else
 #error	"Please fix <asm/byteorder.h>"
 #endif
@@ -78,9 +82,9 @@ struct icmp6hdr {
 #define icmp6_addrconf_other	icmp6_dataun.u_nd_ra.other
 #define icmp6_rt_lifetime	icmp6_dataun.u_nd_ra.rt_lifetime
 #define icmp6_router_pref	icmp6_dataun.u_nd_ra.router_pref
+#define icmp6_slaac_var_plen	icmp6_dataun.u_nd_ra.slaac_var_plen
 };
 
-
 #define ICMPV6_ROUTER_PREF_LOW		0x3
 #define ICMPV6_ROUTER_PREF_MEDIUM	0x0
 #define ICMPV6_ROUTER_PREF_HIGH		0x1
diff -rupN net-next-5.10.0-rc2/net/ipv6/addrconf.c net-next-patch-5.10.0-rc2/net/ipv6/addrconf.c
--- net-next-5.10.0-rc2/net/ipv6/addrconf.c	2020-11-10 08:46:01.075193379 +0100
+++ net-next-patch-5.10.0-rc2/net/ipv6/addrconf.c	2020-11-11 19:47:08.529992394 +0100
@@ -11,6 +11,8 @@
 /*
  *	Changes:
  *
+ *	Dmytro Shytyi			:	Variable SLAAC: SLAAC with
+ *	<dmytro@shytyi.net>			prefixes of arbitrary length.
  *	Janos Farkas			:	delete timer on ifdown
  *	<chexum@bankinf.banki.hu>
  *	Andi Kleen			:	kill double kfree on module
@@ -142,7 +144,12 @@ static int ipv6_count_addresses(const st
 static int ipv6_generate_stable_address(struct in6_addr *addr,
 					u8 dad_count,
 					const struct inet6_dev *idev);
-
+static int ipv6_generate_address_variable_plen(struct in6_addr *address,
+					       u8 dad_count,
+					       const struct inet6_dev *idev,
+					       unsigned int rcvd_prfx_len,
+					       bool stable_privacy_mode);
+unsigned char reverse_bits(unsigned char num);
 #define IN6_ADDR_HSIZE_SHIFT	8
 #define IN6_ADDR_HSIZE		(1 << IN6_ADDR_HSIZE_SHIFT)
 /*
@@ -1314,11 +1321,26 @@ static int ipv6_create_tempaddr(struct i
 	struct inet6_ifaddr *ift;
 	struct ifa6_config cfg;
 	long max_desync_factor;
+
 	struct in6_addr addr;
-	int ret = 0;
 
-	write_lock_bh(&idev->lock);
+	int ret;
+#if defined(CONFIG_ARCH_SUPPORTS_INT128)
+	__int128 host_id;
+	__int128 net_prfx;
+	__int128 ipv6addr;
+	__int128 mask_128;
+	__int128 mask_host_id;
+	__int128 mask_net_prfx;
+	struct in6_addr temp;
+	int i;
+	unsigned char mask_host_id_arr[128];
+
 
+	memset(&mask_128, 0xFF, 16);
+	write_lock_bh(&idev->lock);
+#endif
+	ret = 0;
 retry:
 	in6_dev_hold(idev);
 	if (idev->cnf.use_tempaddr <= 0) {
@@ -1340,9 +1362,32 @@ retry:
 		goto out;
 	}
 	in6_ifa_hold(ifp);
-	memcpy(addr.s6_addr, ifp->addr.s6_addr, 8);
-	ipv6_gen_rnd_iid(&addr);
 
+	if (ifp->prefix_len == 64) {
+		memcpy(addr.s6_addr, ifp->addr.s6_addr, 8);
+		ipv6_gen_rnd_iid(&addr);
+	} else if (ifp->prefix_len > 0 && ifp->prefix_len <= 128) {
+#if defined(CONFIG_ARCH_SUPPORTS_INT128)
+		memcpy(addr.s6_addr, ifp->addr.s6_addr, 16);
+		get_random_bytes(temp.s6_addr32, 16);
+
+		memcpy(&host_id, temp.s6_addr32, sizeof(host_id));
+		memcpy(&net_prfx, addr.s6_addr, sizeof(net_prfx));
+
+		mask_host_id = ifp->prefix_len != 128 ? (mask_128 << ifp->prefix_len) : 0x0;
+		memcpy(mask_host_id_arr, &mask_host_id, 16);
+		for (i = 0; i < 128; i++)
+			mask_host_id_arr[i] = reverse_bits(mask_host_id_arr[i]);
+		memcpy(&mask_host_id, mask_host_id_arr, 16);
+		host_id = host_id & mask_host_id;
+
+		mask_net_prfx = mask_128 ^ mask_host_id;
+		net_prfx = net_prfx & mask_net_prfx;
+
+		ipv6addr = net_prfx | host_id;
+		memcpy(addr.s6_addr, &ipv6addr, 16);
+#endif
+	}
 	age = (now - ifp->tstamp) / HZ;
 
 	regen_advance = idev->cnf.regen_max_retry *
@@ -1398,7 +1443,11 @@ retry:
 	/* set in addrconf_prefix_rcv() */
 	if (ifp->flags & IFA_F_OPTIMISTIC)
 		cfg.ifa_flags |= IFA_F_OPTIMISTIC;
-
+
+	if (in6_pton("::", -1, addr.s6_addr, -1, NULL)) {
+		ret = -1;
+		goto out;
+	}
 	cfg.pfx = &addr;
 	cfg.scope = ipv6_addr_scope(cfg.pfx);
 
@@ -2576,9 +2625,64 @@ int addrconf_prefix_rcv_add_addr(struct
 				 u32 addr_flags, bool sllao, bool tokenized,
 				 __u32 valid_lft, u32 prefered_lft)
 {
-	struct inet6_ifaddr *ifp = ipv6_get_ifaddr(net, addr, dev, 1);
+	struct inet6_ifaddr *ifp = NULL;
 	int create = 0;
 
+	if ((in6_dev->if_flags & IF_RA_VAR_PLEN) == IF_RA_VAR_PLEN &&
+	    in6_dev->cnf.addr_gen_mode != IN6_ADDR_GEN_MODE_STABLE_PRIVACY) {
+#if defined(CONFIG_ARCH_SUPPORTS_INT128)
+		struct inet6_ifaddr *result = NULL;
+		struct inet6_ifaddr *result_base = NULL;
+		__int128 mask_128;
+		__int128 mask_net_prfx;
+		__int128 net_prfx;
+		__int128 curr_net_prfx;
+		int hostid_len;
+		int i;
+		unsigned char mask_host_id_arr[128];
+
+		memset(&mask_128, 0xFF, 16);
+		result_base = result;
+		rcu_read_lock();
+		list_for_each_entry_rcu(ifp, &in6_dev->addr_list, if_list) {
+			if (!net_eq(dev_net(ifp->idev->dev), net))
+				continue;
+			/* 128bit network prefix mask calculation */
+			hostid_len = 128 - pinfo->prefix_len;
+			mask_net_prfx =
+				pinfo->prefix_len != 128 ? (mask_128 << pinfo->prefix_len) : 0x0;
+			mask_net_prfx = ~mask_net_prfx;
+			memcpy(mask_host_id_arr, &mask_net_prfx, 16);
+			for (i = 0; i < 128; i++)
+				mask_host_id_arr[i] = reverse_bits(mask_host_id_arr[i]);
+			memcpy(&mask_net_prfx, mask_host_id_arr, 16);
+
+			/* Received/new IPv6 prefix */
+			memcpy(&net_prfx, pinfo->prefix.s6_addr32, 16);
+			net_prfx &= mask_net_prfx;
+
+			/* Configured/old IPv6 prefix */
+			memcpy(&curr_net_prfx, ifp->addr.s6_addr32, 16);
+			curr_net_prfx &=  mask_net_prfx;
+
+			/* IPv6 prefixes comparison */
+			if ((net_prfx ^ curr_net_prfx) == 0 &&
+			    pinfo->prefix_len == ifp->prefix_len) {
+				result = ifp;
+				in6_ifa_hold(ifp);
+				break;
+			}
+		}
+		rcu_read_unlock();
+		if (result_base != result)
+			ifp = result;
+		else
+			ifp = NULL;
+#endif
+	} else {
+		ifp = ipv6_get_ifaddr(net, addr, dev, 1);
+	}
+
 	if (!ifp && valid_lft) {
 		int max_addresses = in6_dev->cnf.max_addresses;
 		struct ifa6_config cfg = {
@@ -2781,9 +2885,35 @@ void addrconf_prefix_rcv(struct net_devi
 				dev_addr_generated = true;
 			}
 			goto ok;
+		goto put;
+		} else if (((in6_dev->if_flags & IF_RA_VAR_PLEN) == IF_RA_VAR_PLEN) &&
+			  pinfo->prefix_len > 0 && pinfo->prefix_len <= 128) {
+			/* SLAAC with prefixes of arbitrary length (Variable SLAAC).
+			 * draft-mishra-6man-variable-slaac
+			 * draft-mishra-v6ops-variable-slaac-problem-stmt
+			 * Contact: Dmytro Shytyi.
+			 */
+			memcpy(&addr, &pinfo->prefix, 16);
+			if (in6_dev->cnf.addr_gen_mode == IN6_ADDR_GEN_MODE_STABLE_PRIVACY) {
+				if (!ipv6_generate_address_variable_plen(&addr,
+									 0,
+									 in6_dev,
+									 pinfo->prefix_len,
+									 true)) {
+					addr_flags |= IFA_F_STABLE_PRIVACY;
+					goto ok;
+			}
+			} else if (!ipv6_generate_address_variable_plen(&addr,
+									0,
+									in6_dev,
+									pinfo->prefix_len,
+									false)) {
+				goto ok;
+			}
+		} else {
+			net_dbg_ratelimited("IPv6: Prefix with unexpected length %d\n",
+					    pinfo->prefix_len);
 		}
-		net_dbg_ratelimited("IPv6 addrconf: prefix with wrong length %d\n",
-				    pinfo->prefix_len);
 		goto put;
 
 ok:
@@ -3264,6 +3394,120 @@ retry:
 	return 0;
 }
 
+unsigned char reverse_bits(unsigned char num)
+{
+	unsigned char count = sizeof(num) * 8 - 1;
+	unsigned char reverse_num = num;
+
+	num >>= 1;
+	while (num) {
+		reverse_num <<= 1;
+		reverse_num |= num & 1;
+		num >>= 1;
+		count--;
+	}
+	reverse_num <<= count;
+	return reverse_num;
+}
+
+static int ipv6_generate_address_variable_plen(struct in6_addr *address,
+					       u8 dad_count,
+					       const struct inet6_dev *idev,
+					       unsigned int rcvd_prfx_len,
+					       bool stable_privacy_mode)
+{
+#if defined(CONFIG_ARCH_SUPPORTS_INT128)
+	static DEFINE_SPINLOCK(lock);
+	static __u32 digest[SHA1_DIGEST_WORDS];
+	static __u32 workspace[SHA1_WORKSPACE_WORDS];
+
+	static union {
+		char __data[SHA1_BLOCK_SIZE];
+		struct {
+			struct in6_addr secret;
+			__be32 prefix[2];
+			unsigned char hwaddr[MAX_ADDR_LEN];
+			u8 dad_count;
+		} __packed;
+	} data;
+
+	struct in6_addr secret;
+	struct in6_addr temp;
+	struct net *net = dev_net(idev->dev);
+	__int128 host_id;
+	__int128 net_prfx;
+	__int128 ipv6addr;
+	__int128 mask_128;
+	__int128 mask_host_id;
+	__int128 mask_net_prfx;
+	int i;
+	unsigned char mask_host_id_arr[128];
+
+	memset(&mask_128, 0xFF, 16);
+	BUILD_BUG_ON(sizeof(data.__data) != sizeof(data));
+
+	if (stable_privacy_mode) {
+		if (idev->cnf.stable_secret.initialized)
+			secret = idev->cnf.stable_secret.secret;
+		else if (net->ipv6.devconf_dflt->stable_secret.initialized)
+			secret = net->ipv6.devconf_dflt->stable_secret.secret;
+		else
+			return -1;
+	}
+
+retry:
+	spin_lock_bh(&lock);
+	if (stable_privacy_mode) {
+		sha1_init(digest);
+		memset(&data, 0, sizeof(data));
+		memset(workspace, 0, sizeof(workspace));
+		memcpy(data.hwaddr, idev->dev->perm_addr, idev->dev->addr_len);
+		data.prefix[0] = address->s6_addr32[0];
+		data.prefix[1] = address->s6_addr32[1];
+		data.secret = secret;
+		data.dad_count = dad_count;
+
+		sha1_transform(digest, data.__data, workspace);
+
+		temp = *address;
+		temp.s6_addr32[0] = (__force __be32)digest[0];
+		temp.s6_addr32[1] = (__force __be32)digest[1];
+		temp.s6_addr32[2] = (__force __be32)digest[2];
+		temp.s6_addr32[3] = (__force __be32)digest[3];
+	} else {
+		temp = *address;
+		get_random_bytes(temp.s6_addr32, 16);
+	}
+	spin_unlock_bh(&lock);
+
+	if (ipv6_reserved_interfaceid(temp)) {
+		dad_count++;
+		if (dad_count > dev_net(idev->dev)->ipv6.sysctl.idgen_retries)
+			return -1;
+		goto retry;
+	}
+
+	memcpy(&host_id, temp.s6_addr32, 16);
+	memcpy(&net_prfx, address->s6_addr32, 16);
+
+	mask_host_id = rcvd_prfx_len != 128 ? (mask_128 << rcvd_prfx_len) : 0x0;
+	memcpy(mask_host_id_arr, &mask_host_id, 16);
+	for (i = 0; i < 128; i++)
+		mask_host_id_arr[i] = reverse_bits(mask_host_id_arr[i]);
+	memcpy(&mask_host_id, mask_host_id_arr, 16);
+	host_id = host_id & mask_host_id;
+
+	mask_net_prfx = mask_128 ^ mask_host_id;
+	net_prfx = net_prfx & mask_net_prfx;
+
+	ipv6addr = net_prfx | host_id;
+	memcpy(temp.s6_addr32, &ipv6addr, 16);
+
+	*address = temp;
+#endif
+	return 0;
+}
+
 static void ipv6_gen_mode_random_init(struct inet6_dev *idev)
 {
 	struct ipv6_stable_secret *s = &idev->cnf.stable_secret;
diff -rupN net-next-5.10.0-rc2/net/ipv6/ndisc.c net-next-patch-5.10.0-rc2/net/ipv6/ndisc.c
--- net-next-5.10.0-rc2/net/ipv6/ndisc.c	2020-11-10 08:46:01.091860289 +0100
+++ net-next-patch-5.10.0-rc2/net/ipv6/ndisc.c	2020-11-11 18:11:05.630883513 +0100
@@ -1244,6 +1244,8 @@ static void ndisc_router_discovery(struc
 		in6_dev->if_flags |= IF_RA_RCVD;
 	}
 
+	in6_dev->if_flags |= ra_msg->icmph.icmp6_slaac_var_plen ?
+					IF_RA_VAR_PLEN : 0;
 	/*
 	 * Remember the managed/otherconf flags from most recently
 	 * received RA message (RFC 2462) -- yoshfuji

^ permalink raw reply	[flat|nested] 49+ messages in thread

* [PATCH net-next V3] net: Variable SLAAC: SLAAC with prefixes of arbitrary length in PIO
  2020-11-11  1:34   ` kernel test robot
  2020-11-11 20:37     ` [PATCH net-next V2] " Dmytro Shytyi
@ 2020-11-12 15:44     ` Dmytro Shytyi
  2020-11-12 16:55       ` Hideaki Yoshifuji
                         ` (2 more replies)
  2020-11-13  0:24     ` [PATCH net-next] " Jakub Kicinski
  2 siblings, 3 replies; 49+ messages in thread
From: Dmytro Shytyi @ 2020-11-12 15:44 UTC (permalink / raw)
  To: kuba, kuznet, yoshfuji, liuhangbin, davem, netdev, linux-kernel

Variable SLAAC: SLAAC with prefixes of arbitrary length in PIO (randomly
generated hostID or stable privacy + privacy extensions).
The main problem is that SLAAC RA or PD allocates a /64 by the Wireless
carrier 4G, 5G to a mobile hotspot, however segmentation of the /64 via
SLAAC is required so that downstream interfaces can be further subnetted.
Example: uCPE device (4G + WI-FI enabled) receives /64 via Wireless, and
assigns /72 to VNF-Firewall, /72 to WIFI, /72 to VNF-Router, /72 to
Load-Balancer and /72 to wired connected devices.
IETF document that defines problem statement:
draft-mishra-v6ops-variable-slaac-problem-stmt
IETF document that specifies variable slaac:
draft-mishra-6man-variable-slaac

Signed-off-by: Dmytro Shytyi <dmytro@shytyi.net>
Reported-by: kernel test robot <lkp@intel.com>
---
diff -rupN net-next-5.10.0-rc2/include/net/if_inet6.h net-next-patch-5.10.0-rc2/include/net/if_inet6.h
--- net-next-5.10.0-rc2/include/net/if_inet6.h	2020-11-10 08:46:00.195180579 +0100
+++ net-next-patch-5.10.0-rc2/include/net/if_inet6.h	2020-11-11 18:11:05.627550135 +0100
@@ -22,6 +22,12 @@
 #define IF_RS_SENT	0x10
 #define IF_READY	0x80000000
 
+/* Variable SLAAC (Contact: Dmytro Shytyi)
+ * draft-mishra-6man-variable-slaac
+ * draft-mishra-v6ops-variable-slaac-problem-stmt
+ */
+#define IF_RA_VAR_PLEN	0x08
+
 /* prefix flags */
 #define IF_PREFIX_ONLINK	0x01
 #define IF_PREFIX_AUTOCONF	0x02
diff -rupN net-next-5.10.0-rc2/include/uapi/linux/icmpv6.h net-next-patch-5.10.0-rc2/include/uapi/linux/icmpv6.h
--- net-next-5.10.0-rc2/include/uapi/linux/icmpv6.h	2020-11-10 08:46:00.351849525 +0100
+++ net-next-patch-5.10.0-rc2/include/uapi/linux/icmpv6.h	2020-11-11 18:11:05.627550135 +0100
@@ -42,7 +42,9 @@ struct icmp6hdr {
                 struct icmpv6_nd_ra {
 			__u8		hop_limit;
 #if defined(__LITTLE_ENDIAN_BITFIELD)
-			__u8		reserved:3,
+			__u8		reserved:1,
+					slaac_var_plen:1,
+					proxy:1,
 					router_pref:2,
 					home_agent:1,
 					other:1,
@@ -53,7 +55,9 @@ struct icmp6hdr {
 					other:1,
 					home_agent:1,
 					router_pref:2,
-					reserved:3;
+					proxy:1,
+					slaac_var_plen:1,
+					reserved:1;
 #else
 #error	"Please fix <asm/byteorder.h>"
 #endif
@@ -78,9 +82,9 @@ struct icmp6hdr {
 #define icmp6_addrconf_other	icmp6_dataun.u_nd_ra.other
 #define icmp6_rt_lifetime	icmp6_dataun.u_nd_ra.rt_lifetime
 #define icmp6_router_pref	icmp6_dataun.u_nd_ra.router_pref
+#define icmp6_slaac_var_plen	icmp6_dataun.u_nd_ra.slaac_var_plen
 };
 
-
 #define ICMPV6_ROUTER_PREF_LOW		0x3
 #define ICMPV6_ROUTER_PREF_MEDIUM	0x0
 #define ICMPV6_ROUTER_PREF_HIGH		0x1
diff -rupN net-next-5.10.0-rc2/net/ipv6/addrconf.c net-next-patch-5.10.0-rc2/net/ipv6/addrconf.c
--- net-next-5.10.0-rc2/net/ipv6/addrconf.c	2020-11-10 08:46:01.075193379 +0100
+++ net-next-patch-5.10.0-rc2/net/ipv6/addrconf.c	2020-11-12 16:27:26.765361712 +0100
@@ -11,6 +11,8 @@
 /*
  *	Changes:
  *
+ *	Dmytro Shytyi			:	Variable SLAAC: SLAAC with
+ *	<dmytro@shytyi.net>			prefixes of arbitrary length.
  *	Janos Farkas			:	delete timer on ifdown
  *	<chexum@bankinf.banki.hu>
  *	Andi Kleen			:	kill double kfree on module
@@ -142,7 +144,12 @@ static int ipv6_count_addresses(const st
 static int ipv6_generate_stable_address(struct in6_addr *addr,
 					u8 dad_count,
 					const struct inet6_dev *idev);
-
+static int ipv6_generate_address_variable_plen(struct in6_addr *address,
+					       u8 dad_count,
+					       const struct inet6_dev *idev,
+					       unsigned int rcvd_prfx_len,
+					       bool stable_privacy_mode);
+unsigned char reverse_bits(unsigned char num);
 #define IN6_ADDR_HSIZE_SHIFT	8
 #define IN6_ADDR_HSIZE		(1 << IN6_ADDR_HSIZE_SHIFT)
 /*
@@ -1314,11 +1321,26 @@ static int ipv6_create_tempaddr(struct i
 	struct inet6_ifaddr *ift;
 	struct ifa6_config cfg;
 	long max_desync_factor;
+
 	struct in6_addr addr;
-	int ret = 0;
 
-	write_lock_bh(&idev->lock);
+	int ret;
+#if defined(CONFIG_ARCH_SUPPORTS_INT128)
+	__int128 host_id;
+	__int128 net_prfx;
+	__int128 ipv6addr;
+	__int128 mask_128;
+	__int128 mask_host_id;
+	__int128 mask_net_prfx;
+	struct in6_addr temp;
+	int i;
+	unsigned char mask_host_id_arr[128];
+
 
+	memset(&mask_128, 0xFF, 16);
+#endif
+	write_lock_bh(&idev->lock);
+	ret = 0;
 retry:
 	in6_dev_hold(idev);
 	if (idev->cnf.use_tempaddr <= 0) {
@@ -1340,9 +1362,32 @@ retry:
 		goto out;
 	}
 	in6_ifa_hold(ifp);
-	memcpy(addr.s6_addr, ifp->addr.s6_addr, 8);
-	ipv6_gen_rnd_iid(&addr);
 
+	if (ifp->prefix_len == 64) {
+		memcpy(addr.s6_addr, ifp->addr.s6_addr, 8);
+		ipv6_gen_rnd_iid(&addr);
+	} else if (ifp->prefix_len > 0 && ifp->prefix_len <= 128) {
+#if defined(CONFIG_ARCH_SUPPORTS_INT128)
+		memcpy(addr.s6_addr, ifp->addr.s6_addr, 16);
+		get_random_bytes(temp.s6_addr32, 16);
+
+		memcpy(&host_id, temp.s6_addr32, sizeof(host_id));
+		memcpy(&net_prfx, addr.s6_addr, sizeof(net_prfx));
+
+		mask_host_id = ifp->prefix_len != 128 ? (mask_128 << ifp->prefix_len) : 0x0;
+		memcpy(mask_host_id_arr, &mask_host_id, 16);
+		for (i = 0; i < 128; i++)
+			mask_host_id_arr[i] = reverse_bits(mask_host_id_arr[i]);
+		memcpy(&mask_host_id, mask_host_id_arr, 16);
+		host_id = host_id & mask_host_id;
+
+		mask_net_prfx = mask_128 ^ mask_host_id;
+		net_prfx = net_prfx & mask_net_prfx;
+
+		ipv6addr = net_prfx | host_id;
+		memcpy(addr.s6_addr, &ipv6addr, 16);
+#endif
+	}
 	age = (now - ifp->tstamp) / HZ;
 
 	regen_advance = idev->cnf.regen_max_retry *
@@ -1398,7 +1443,11 @@ retry:
 	/* set in addrconf_prefix_rcv() */
 	if (ifp->flags & IFA_F_OPTIMISTIC)
 		cfg.ifa_flags |= IFA_F_OPTIMISTIC;
-
+        
+	if (in6_pton("::", -1, addr.s6_addr, -1, NULL)) {
+		ret = -1;
+		goto out;
+	}
 	cfg.pfx = &addr;
 	cfg.scope = ipv6_addr_scope(cfg.pfx);
 
@@ -2576,9 +2625,64 @@ int addrconf_prefix_rcv_add_addr(struct
 				 u32 addr_flags, bool sllao, bool tokenized,
 				 __u32 valid_lft, u32 prefered_lft)
 {
-	struct inet6_ifaddr *ifp = ipv6_get_ifaddr(net, addr, dev, 1);
+	struct inet6_ifaddr *ifp = NULL;
 	int create = 0;
 
+	if ((in6_dev->if_flags & IF_RA_VAR_PLEN) == IF_RA_VAR_PLEN &&
+	    in6_dev->cnf.addr_gen_mode != IN6_ADDR_GEN_MODE_STABLE_PRIVACY) {
+#if defined(CONFIG_ARCH_SUPPORTS_INT128)
+		struct inet6_ifaddr *result = NULL;
+		struct inet6_ifaddr *result_base = NULL;
+		__int128 mask_128;
+		__int128 mask_net_prfx;
+		__int128 net_prfx;
+		__int128 curr_net_prfx;
+		int hostid_len;
+		int i;
+		unsigned char mask_host_id_arr[128];
+
+		memset(&mask_128, 0xFF, 16);
+		result_base = result;
+		rcu_read_lock();
+		list_for_each_entry_rcu(ifp, &in6_dev->addr_list, if_list) {
+			if (!net_eq(dev_net(ifp->idev->dev), net))
+				continue;
+			/* 128bit network prefix mask calculation */
+			hostid_len = 128 - pinfo->prefix_len;
+			mask_net_prfx =
+				pinfo->prefix_len != 128 ? (mask_128 << pinfo->prefix_len) : 0x0;
+			mask_net_prfx = ~mask_net_prfx;
+			memcpy(mask_host_id_arr, &mask_net_prfx, 16);
+			for (i = 0; i < 128; i++)
+				mask_host_id_arr[i] = reverse_bits(mask_host_id_arr[i]);
+			memcpy(&mask_net_prfx, mask_host_id_arr, 16);
+
+			/* Received/new IPv6 prefix */
+			memcpy(&net_prfx, pinfo->prefix.s6_addr32, 16);
+			net_prfx &= mask_net_prfx;
+
+			/* Configured/old IPv6 prefix */
+			memcpy(&curr_net_prfx, ifp->addr.s6_addr32, 16);
+			curr_net_prfx &=  mask_net_prfx;
+
+			/* IPv6 prefixes comparison */
+			if ((net_prfx ^ curr_net_prfx) == 0 &&
+			    pinfo->prefix_len == ifp->prefix_len) {
+				result = ifp;
+				in6_ifa_hold(ifp);
+				break;
+			}
+		}
+		rcu_read_unlock();
+		if (result_base != result)
+			ifp = result;
+		else
+			ifp = NULL;
+#endif
+	} else {
+		ifp = ipv6_get_ifaddr(net, addr, dev, 1);
+	}
+
 	if (!ifp && valid_lft) {
 		int max_addresses = in6_dev->cnf.max_addresses;
 		struct ifa6_config cfg = {
@@ -2781,9 +2885,35 @@ void addrconf_prefix_rcv(struct net_devi
 				dev_addr_generated = true;
 			}
 			goto ok;
+		goto put;
+		} else if (((in6_dev->if_flags & IF_RA_VAR_PLEN) == IF_RA_VAR_PLEN) &&
+			  pinfo->prefix_len > 0 && pinfo->prefix_len <= 128) {
+			/* SLAAC with prefixes of arbitrary length (Variable SLAAC).
+			 * draft-mishra-6man-variable-slaac
+			 * draft-mishra-v6ops-variable-slaac-problem-stmt
+			 * Contact: Dmytro Shytyi.
+			 */
+			memcpy(&addr, &pinfo->prefix, 16);
+			if (in6_dev->cnf.addr_gen_mode == IN6_ADDR_GEN_MODE_STABLE_PRIVACY) {
+				if (!ipv6_generate_address_variable_plen(&addr,
+									 0,
+									 in6_dev,
+									 pinfo->prefix_len,
+									 true)) {
+					addr_flags |= IFA_F_STABLE_PRIVACY;
+					goto ok;
+			}
+			} else if (!ipv6_generate_address_variable_plen(&addr,
+									0,
+									in6_dev,
+									pinfo->prefix_len,
+									false)) {
+				goto ok;
+			}
+		} else {
+			net_dbg_ratelimited("IPv6: Prefix with unexpected length %d\n",
+					    pinfo->prefix_len);
 		}
-		net_dbg_ratelimited("IPv6 addrconf: prefix with wrong length %d\n",
-				    pinfo->prefix_len);
 		goto put;
 
 ok:
@@ -3264,6 +3394,120 @@ retry:
 	return 0;
 }
 
+unsigned char reverse_bits(unsigned char num)
+{
+	unsigned char count = sizeof(num) * 8 - 1;
+	unsigned char reverse_num = num;
+
+	num >>= 1;
+	while (num) {
+		reverse_num <<= 1;
+		reverse_num |= num & 1;
+		num >>= 1;
+		count--;
+	}
+	reverse_num <<= count;
+	return reverse_num;
+}
+
+static int ipv6_generate_address_variable_plen(struct in6_addr *address,
+					       u8 dad_count,
+					       const struct inet6_dev *idev,
+					       unsigned int rcvd_prfx_len,
+					       bool stable_privacy_mode)
+{
+#if defined(CONFIG_ARCH_SUPPORTS_INT128)
+	static DEFINE_SPINLOCK(lock);
+	static __u32 digest[SHA1_DIGEST_WORDS];
+	static __u32 workspace[SHA1_WORKSPACE_WORDS];
+
+	static union {
+		char __data[SHA1_BLOCK_SIZE];
+		struct {
+			struct in6_addr secret;
+			__be32 prefix[2];
+			unsigned char hwaddr[MAX_ADDR_LEN];
+			u8 dad_count;
+		} __packed;
+	} data;
+
+	struct in6_addr secret;
+	struct in6_addr temp;
+	struct net *net = dev_net(idev->dev);
+	__int128 host_id;
+	__int128 net_prfx;
+	__int128 ipv6addr;
+	__int128 mask_128;
+	__int128 mask_host_id;
+	__int128 mask_net_prfx;
+	int i;
+	unsigned char mask_host_id_arr[128];
+
+	memset(&mask_128, 0xFF, 16);
+	BUILD_BUG_ON(sizeof(data.__data) != sizeof(data));
+
+	if (stable_privacy_mode) {
+		if (idev->cnf.stable_secret.initialized)
+			secret = idev->cnf.stable_secret.secret;
+		else if (net->ipv6.devconf_dflt->stable_secret.initialized)
+			secret = net->ipv6.devconf_dflt->stable_secret.secret;
+		else
+			return -1;
+	}
+
+retry:
+	spin_lock_bh(&lock);
+	if (stable_privacy_mode) {
+		sha1_init(digest);
+		memset(&data, 0, sizeof(data));
+		memset(workspace, 0, sizeof(workspace));
+		memcpy(data.hwaddr, idev->dev->perm_addr, idev->dev->addr_len);
+		data.prefix[0] = address->s6_addr32[0];
+		data.prefix[1] = address->s6_addr32[1];
+		data.secret = secret;
+		data.dad_count = dad_count;
+
+		sha1_transform(digest, data.__data, workspace);
+
+		temp = *address;
+		temp.s6_addr32[0] = (__force __be32)digest[0];
+		temp.s6_addr32[1] = (__force __be32)digest[1];
+		temp.s6_addr32[2] = (__force __be32)digest[2];
+		temp.s6_addr32[3] = (__force __be32)digest[3];
+	} else {
+		temp = *address;
+		get_random_bytes(temp.s6_addr32, 16);
+	}
+	spin_unlock_bh(&lock);
+
+	if (ipv6_reserved_interfaceid(temp)) {
+		dad_count++;
+		if (dad_count > dev_net(idev->dev)->ipv6.sysctl.idgen_retries)
+			return -1;
+		goto retry;
+	}
+
+	memcpy(&host_id, temp.s6_addr32, 16);
+	memcpy(&net_prfx, address->s6_addr32, 16);
+
+	mask_host_id = rcvd_prfx_len != 128 ? (mask_128 << rcvd_prfx_len) : 0x0;
+	memcpy(mask_host_id_arr, &mask_host_id, 16);
+	for (i = 0; i < 128; i++)
+		mask_host_id_arr[i] = reverse_bits(mask_host_id_arr[i]);
+	memcpy(&mask_host_id, mask_host_id_arr, 16);
+	host_id = host_id & mask_host_id;
+
+	mask_net_prfx = mask_128 ^ mask_host_id;
+	net_prfx = net_prfx & mask_net_prfx;
+
+	ipv6addr = net_prfx | host_id;
+	memcpy(temp.s6_addr32, &ipv6addr, 16);
+
+	*address = temp;
+#endif
+	return 0;
+}
+
 static void ipv6_gen_mode_random_init(struct inet6_dev *idev)
 {
 	struct ipv6_stable_secret *s = &idev->cnf.stable_secret;
diff -rupN net-next-5.10.0-rc2/net/ipv6/ndisc.c net-next-patch-5.10.0-rc2/net/ipv6/ndisc.c
--- net-next-5.10.0-rc2/net/ipv6/ndisc.c	2020-11-10 08:46:01.091860289 +0100
+++ net-next-patch-5.10.0-rc2/net/ipv6/ndisc.c	2020-11-11 18:11:05.630883513 +0100
@@ -1244,6 +1244,8 @@ static void ndisc_router_discovery(struc
 		in6_dev->if_flags |= IF_RA_RCVD;
 	}
 
+	in6_dev->if_flags |= ra_msg->icmph.icmp6_slaac_var_plen ?
+					IF_RA_VAR_PLEN : 0;
 	/*
 	 * Remember the managed/otherconf flags from most recently
 	 * received RA message (RFC 2462) -- yoshfuji

^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH net-next V3] net: Variable SLAAC: SLAAC with prefixes of arbitrary length in PIO
  2020-11-12 15:44     ` [PATCH net-next V3] " Dmytro Shytyi
@ 2020-11-12 16:55       ` Hideaki Yoshifuji
  2020-11-13  1:50         ` Dmytro Shytyi
  2020-11-13  0:21       ` Jakub Kicinski
  2020-11-13  1:56       ` [PATCH net-next V4] " Dmytro Shytyi
  2 siblings, 1 reply; 49+ messages in thread
From: Hideaki Yoshifuji @ 2020-11-12 16:55 UTC (permalink / raw)
  To: Dmytro Shytyi
  Cc: kuba, kuznet, yoshfuji, liuhangbin, davem, netdev, linux-kernel,
	Hideaki Yoshifuji

Hi,

2020年11月13日(金) 0:46 Dmytro Shytyi <dmytro@shytyi.net>:
>
> Variable SLAAC: SLAAC with prefixes of arbitrary length in PIO (randomly
> generated hostID or stable privacy + privacy extensions).
> The main problem is that SLAAC RA or PD allocates a /64 by the Wireless
> carrier 4G, 5G to a mobile hotspot, however segmentation of the /64 via
> SLAAC is required so that downstream interfaces can be further subnetted.
> Example: uCPE device (4G + WI-FI enabled) receives /64 via Wireless, and
> assigns /72 to VNF-Firewall, /72 to WIFI, /72 to VNF-Router, /72 to
> Load-Balancer and /72 to wired connected devices.
> IETF document that defines problem statement:
> draft-mishra-v6ops-variable-slaac-problem-stmt
> IETF document that specifies variable slaac:
> draft-mishra-6man-variable-slaac
>
> Signed-off-by: Dmytro Shytyi <dmytro@shytyi.net>
> Reported-by: kernel test robot <lkp@intel.com>
> ---

> -       write_lock_bh(&idev->lock);
> +       int ret;
> +#if defined(CONFIG_ARCH_SUPPORTS_INT128)
> +       __int128 host_id;
> +       __int128 net_prfx;
:

No, this does not help anything.
Please do not rely on __int128.

--yoshfuji

^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH net-next V3] net: Variable SLAAC: SLAAC with prefixes of arbitrary length in PIO
  2020-11-12 15:44     ` [PATCH net-next V3] " Dmytro Shytyi
  2020-11-12 16:55       ` Hideaki Yoshifuji
@ 2020-11-13  0:21       ` Jakub Kicinski
  2020-11-13  1:50         ` Dmytro Shytyi
  2020-11-13  1:56       ` [PATCH net-next V4] " Dmytro Shytyi
  2 siblings, 1 reply; 49+ messages in thread
From: Jakub Kicinski @ 2020-11-13  0:21 UTC (permalink / raw)
  To: Dmytro Shytyi; +Cc: kuznet, yoshfuji, liuhangbin, davem, netdev, linux-kernel

On Thu, 12 Nov 2020 16:44:54 +0100 Dmytro Shytyi wrote:
> Reported-by: kernel test robot <lkp@intel.com>

You don't have to add the reported by tag just because the bot pointed
out issues in the previous version.

^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH net-next] net: Variable SLAAC: SLAAC with prefixes of arbitrary length in PIO
  2020-11-11  1:34   ` kernel test robot
  2020-11-11 20:37     ` [PATCH net-next V2] " Dmytro Shytyi
  2020-11-12 15:44     ` [PATCH net-next V3] " Dmytro Shytyi
@ 2020-11-13  0:24     ` Jakub Kicinski
  2020-11-13  0:32       ` [kbuild-all] " Li, Philip
  2020-11-13  1:43       ` Dave Hansen
  2 siblings, 2 replies; 49+ messages in thread
From: Jakub Kicinski @ 2020-11-13  0:24 UTC (permalink / raw)
  To: kernel test robot
  Cc: Dmytro Shytyi, kuznet, yoshfuji, liuhangbin, davem, netdev,
	linux-kernel, kbuild-all

On Wed, 11 Nov 2020 09:34:24 +0800 kernel test robot wrote:
> If you fix the issue, kindly add following tag as appropriate
> Reported-by: kernel test robot <lkp@intel.com>

Good people of kernel test robot, could you please rephrase this to say
that the tag is only appropriate if someone is sending a fix up/follow
up patch?

Folks keep adding those tags on the next revisions of the their patches
which is quite misleading.

^ permalink raw reply	[flat|nested] 49+ messages in thread

* RE: [kbuild-all] Re: [PATCH net-next] net: Variable SLAAC: SLAAC with prefixes of arbitrary length in PIO
  2020-11-13  0:24     ` [PATCH net-next] " Jakub Kicinski
@ 2020-11-13  0:32       ` Li, Philip
  2020-11-13  0:51         ` Jakub Kicinski
  2020-11-13  1:43       ` Dave Hansen
  1 sibling, 1 reply; 49+ messages in thread
From: Li, Philip @ 2020-11-13  0:32 UTC (permalink / raw)
  To: Jakub Kicinski, lkp
  Cc: Dmytro Shytyi, kuznet, yoshfuji, liuhangbin, davem, netdev,
	linux-kernel, kbuild-all

> Subject: [kbuild-all] Re: [PATCH net-next] net: Variable SLAAC: SLAAC with
> prefixes of arbitrary length in PIO
> 
> On Wed, 11 Nov 2020 09:34:24 +0800 kernel test robot wrote:
> > If you fix the issue, kindly add following tag as appropriate
> > Reported-by: kernel test robot <lkp@intel.com>
> 
> Good people of kernel test robot, could you please rephrase this to say
> that the tag is only appropriate if someone is sending a fix up/follow
> up patch?
Thanks for the input, based on your suggestion how about

Kindly add below tag as appropriate if you send a fix up/follow up patch
Reported-by: kernel test robot <lkp@intel.com>

Or any wording change suggestion to make it more clear/friendly?

Thanks

> 
> Folks keep adding those tags on the next revisions of the their patches
> which is quite misleading.
> _______________________________________________
> kbuild-all mailing list -- kbuild-all@lists.01.org
> To unsubscribe send an email to kbuild-all-leave@lists.01.org

^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [kbuild-all] Re: [PATCH net-next] net: Variable SLAAC: SLAAC with prefixes of arbitrary length in PIO
  2020-11-13  0:32       ` [kbuild-all] " Li, Philip
@ 2020-11-13  0:51         ` Jakub Kicinski
  2020-11-13  1:01           ` Li, Philip
  0 siblings, 1 reply; 49+ messages in thread
From: Jakub Kicinski @ 2020-11-13  0:51 UTC (permalink / raw)
  To: Li, Philip
  Cc: lkp, Dmytro Shytyi, kuznet, yoshfuji, liuhangbin, davem, netdev,
	linux-kernel, kbuild-all

On Fri, 13 Nov 2020 00:32:55 +0000 Li, Philip wrote:
> > Subject: [kbuild-all] Re: [PATCH net-next] net: Variable SLAAC: SLAAC with
> > prefixes of arbitrary length in PIO
> > 
> > On Wed, 11 Nov 2020 09:34:24 +0800 kernel test robot wrote:  
> > > If you fix the issue, kindly add following tag as appropriate
> > > Reported-by: kernel test robot <lkp@intel.com>  
> > 
> > Good people of kernel test robot, could you please rephrase this to say
> > that the tag is only appropriate if someone is sending a fix up/follow
> > up patch?  
> Thanks for the input, based on your suggestion how about
> 
> Kindly add below tag as appropriate if you send a fix up/follow up patch

I'm not sure myself how best to phrase it, I'm not a native speaker.
How about:

Kindly add below tag if you send a new patch solely addressing this issue

> Reported-by: kernel test robot <lkp@intel.com>
> 
> Or any wording change suggestion to make it more clear/friendly?


^ permalink raw reply	[flat|nested] 49+ messages in thread

* RE: [kbuild-all] Re: [PATCH net-next] net: Variable SLAAC: SLAAC with prefixes of arbitrary length in PIO
  2020-11-13  0:51         ` Jakub Kicinski
@ 2020-11-13  1:01           ` Li, Philip
  0 siblings, 0 replies; 49+ messages in thread
From: Li, Philip @ 2020-11-13  1:01 UTC (permalink / raw)
  To: Jakub Kicinski
  Cc: lkp, Dmytro Shytyi, kuznet, yoshfuji, liuhangbin, davem, netdev,
	linux-kernel, kbuild-all

> 
> On Fri, 13 Nov 2020 00:32:55 +0000 Li, Philip wrote:
> > > Subject: [kbuild-all] Re: [PATCH net-next] net: Variable SLAAC: SLAAC
> with
> > > prefixes of arbitrary length in PIO
> > >
> > > On Wed, 11 Nov 2020 09:34:24 +0800 kernel test robot wrote:
> > > > If you fix the issue, kindly add following tag as appropriate
> > > > Reported-by: kernel test robot <lkp@intel.com>
> > >
> > > Good people of kernel test robot, could you please rephrase this to say
> > > that the tag is only appropriate if someone is sending a fix up/follow
> > > up patch?
> > Thanks for the input, based on your suggestion how about
> >
> > Kindly add below tag as appropriate if you send a fix up/follow up patch
> 
> I'm not sure myself how best to phrase it, I'm not a native speaker.
> How about:
> 
> Kindly add below tag if you send a new patch solely addressing this issue
Thanks, we will consider this, and provide update next week to gather
more inputs. If anyone has further suggestion, it will be very welcome.

There did have some confusion and discussed earlier actually regarding
when/how to add the Reported-by, thus we use appropriate to let developer
decide the best choice for his own situation. But if it leads to confusion,
we will keep looking for a better way.

BTW: if we just remove this message line, and leave below Reported-by only, would
it be a good choice? 

> 
> > Reported-by: kernel test robot <lkp@intel.com>
> >
> > Or any wording change suggestion to make it more clear/friendly?


^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH net-next] net: Variable SLAAC: SLAAC with prefixes of arbitrary length in PIO
  2020-11-13  0:24     ` [PATCH net-next] " Jakub Kicinski
  2020-11-13  0:32       ` [kbuild-all] " Li, Philip
@ 2020-11-13  1:43       ` Dave Hansen
  2020-11-13  1:53         ` Jakub Kicinski
  1 sibling, 1 reply; 49+ messages in thread
From: Dave Hansen @ 2020-11-13  1:43 UTC (permalink / raw)
  To: Jakub Kicinski, kernel test robot
  Cc: Dmytro Shytyi, kuznet, yoshfuji, liuhangbin, davem, netdev,
	linux-kernel, kbuild-all

On 11/12/20 4:24 PM, Jakub Kicinski wrote:
> On Wed, 11 Nov 2020 09:34:24 +0800 kernel test robot wrote:
>> If you fix the issue, kindly add following tag as appropriate
>> Reported-by: kernel test robot <lkp@intel.com>
> Good people of kernel test robot, could you please rephrase this to say
> that the tag is only appropriate if someone is sending a fix up/follow
> up patch?
> 
> Folks keep adding those tags on the next revisions of the their patches
> which is quite misleading.

I think it's still fair for the lkp folks to get *some* credit for
reporting these bugs.  I mean, the stated reason[1] for it existing is:

	The Reported-by tag gives credit to people who find bugs and
	report them and it hopefully inspires them to help us again in
	the future.

I do agree, though, that it's confusing *what* they reported, especially
if the patch in question is fixing something *else*.  Rather than invent
a new tag, maybe a comment would suffice:

Reported-by: kernel test robot <lkp@intel.com> # bug in earlier revision

1.
https://www.kernel.org/doc/html/v4.17/process/submitting-patches.html#using-reported-by-tested-by-reviewed-by-suggested-by-and-fixes


^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH net-next V3] net: Variable SLAAC: SLAAC with prefixes of arbitrary length in PIO
  2020-11-12 16:55       ` Hideaki Yoshifuji
@ 2020-11-13  1:50         ` Dmytro Shytyi
  0 siblings, 0 replies; 49+ messages in thread
From: Dmytro Shytyi @ 2020-11-13  1:50 UTC (permalink / raw)
  To: Hideaki Yoshifuji
  Cc: kuba, kuznet, yoshfuji, liuhangbin, davem, netdev, linux-kernel

Hello,

---- On Thu, 12 Nov 2020 17:55:08 +0100 Hideaki Yoshifuji <hideaki.yoshifuji@miraclelinux.com> wrote ----

 > Hi, 
 >  
 > 2020年11月13日(金) 0:46 Dmytro Shytyi <dmytro@shytyi.net>: 
 > > 
 > > Variable SLAAC: SLAAC with prefixes of arbitrary length in PIO (randomly 
 > > generated hostID or stable privacy + privacy extensions). 
 > > The main problem is that SLAAC RA or PD allocates a /64 by the Wireless 
 > > carrier 4G, 5G to a mobile hotspot, however segmentation of the /64 via 
 > > SLAAC is required so that downstream interfaces can be further subnetted. 
 > > Example: uCPE device (4G + WI-FI enabled) receives /64 via Wireless, and 
 > > assigns /72 to VNF-Firewall, /72 to WIFI, /72 to VNF-Router, /72 to 
 > > Load-Balancer and /72 to wired connected devices. 
 > > IETF document that defines problem statement: 
 > > draft-mishra-v6ops-variable-slaac-problem-stmt 
 > > IETF document that specifies variable slaac: 
 > > draft-mishra-6man-variable-slaac 
 > > 
 > > Signed-off-by: Dmytro Shytyi <dmytro@shytyi.net> 
 > > Reported-by: kernel test robot <lkp@intel.com> 
 > > --- 
 >  
 > > -       write_lock_bh(&idev->lock); 
 > > +       int ret; 
 > > +#if defined(CONFIG_ARCH_SUPPORTS_INT128) 
 > > +       __int128 host_id; 
 > > +       __int128 net_prfx; 
 > : 
 >  
 > No, this does not help anything. 
 > Please do not rely on __int128. 

[Dmytro] Understood. Thank you.

 > --yoshfuji 
 > 

^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH net-next V3] net: Variable SLAAC: SLAAC with prefixes of arbitrary length in PIO
  2020-11-13  0:21       ` Jakub Kicinski
@ 2020-11-13  1:50         ` Dmytro Shytyi
  0 siblings, 0 replies; 49+ messages in thread
From: Dmytro Shytyi @ 2020-11-13  1:50 UTC (permalink / raw)
  To: Jakub Kicinski; +Cc: kuznet, yoshfuji, liuhangbin, davem, netdev, linux-kernel

Hello,
          
---- On Fri, 13 Nov 2020 01:21:56 +0100 Jakub Kicinski <kuba@kernel.org> wrote ----

 > On Thu, 12 Nov 2020 16:44:54 +0100 Dmytro Shytyi wrote: 
 > > Reported-by: kernel test robot <lkp@intel.com> 
 >  
 > You don't have to add the reported by tag just because the bot pointed 
 > out issues in the previous version. 
 > 
[Dmytro] Understood. Thank you for the comment.
                    
Dmytro SHYTYI


^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH net-next] net: Variable SLAAC: SLAAC with prefixes of arbitrary length in PIO
  2020-11-13  1:43       ` Dave Hansen
@ 2020-11-13  1:53         ` Jakub Kicinski
  2020-11-13  2:01           ` Dave Hansen
  0 siblings, 1 reply; 49+ messages in thread
From: Jakub Kicinski @ 2020-11-13  1:53 UTC (permalink / raw)
  To: Dave Hansen
  Cc: kernel test robot, Dmytro Shytyi, kuznet, yoshfuji, liuhangbin,
	davem, netdev, linux-kernel, kbuild-all

On Thu, 12 Nov 2020 17:43:56 -0800 Dave Hansen wrote:
> On 11/12/20 4:24 PM, Jakub Kicinski wrote:
> > On Wed, 11 Nov 2020 09:34:24 +0800 kernel test robot wrote:  
> >> If you fix the issue, kindly add following tag as appropriate
> >> Reported-by: kernel test robot <lkp@intel.com>  
> > Good people of kernel test robot, could you please rephrase this to say
> > that the tag is only appropriate if someone is sending a fix up/follow
> > up patch?
> > 
> > Folks keep adding those tags on the next revisions of the their patches
> > which is quite misleading.  
> 
> I think it's still fair for the lkp folks to get *some* credit for
> reporting these bugs.  I mean, the stated reason[1] for it existing is:
> 
> 	The Reported-by tag gives credit to people who find bugs and
> 	report them and it hopefully inspires them to help us again in
> 	the future.
> 
> I do agree, though, that it's confusing *what* they reported, especially
> if the patch in question is fixing something *else*.  Rather than invent
> a new tag, maybe a comment would suffice:
> 
> Reported-by: kernel test robot <lkp@intel.com> # bug in earlier revision

Fine by me, although its not common to add Reported-by tags for people
who point out issues in review, so why add a tag for the bot?

^ permalink raw reply	[flat|nested] 49+ messages in thread

* [PATCH net-next V4] net: Variable SLAAC: SLAAC with prefixes of arbitrary length in PIO
  2020-11-12 15:44     ` [PATCH net-next V3] " Dmytro Shytyi
  2020-11-12 16:55       ` Hideaki Yoshifuji
  2020-11-13  0:21       ` Jakub Kicinski
@ 2020-11-13  1:56       ` Dmytro Shytyi
  2020-11-13 12:38         ` Hideaki Yoshifuji
  2020-11-13 19:36         ` [PATCH net-next V5] " Dmytro Shytyi
  2 siblings, 2 replies; 49+ messages in thread
From: Dmytro Shytyi @ 2020-11-13  1:56 UTC (permalink / raw)
  To: kuba, kuznet, yoshfuji, liuhangbin, davem, netdev, linux-kernel

Variable SLAAC: SLAAC with prefixes of arbitrary length in PIO (randomly
generated hostID or stable privacy + privacy extensions).
The main problem is that SLAAC RA or PD allocates a /64 by the Wireless
carrier 4G, 5G to a mobile hotspot, however segmentation of the /64 via
SLAAC is required so that downstream interfaces can be further subnetted.
Example: uCPE device (4G + WI-FI enabled) receives /64 via Wireless, and
assigns /72 to VNF-Firewall, /72 to WIFI, /72 to VNF-Router, /72 to
Load-Balancer and /72 to wired connected devices.
IETF document that defines problem statement:
draft-mishra-v6ops-variable-slaac-problem-stmt
IETF document that specifies variable slaac:
draft-mishra-6man-variable-slaac

Signed-off-by: Dmytro Shytyi <dmytro@shytyi.net>
---
diff -rupN net-next-5.10.0-rc2/include/net/if_inet6.h net-next-patch-5.10.0-rc2/include/net/if_inet6.h
--- net-next-5.10.0-rc2/include/net/if_inet6.h	2020-11-10 08:46:00.195180579 +0100
+++ net-next-patch-5.10.0-rc2/include/net/if_inet6.h	2020-11-11 18:11:05.627550135 +0100
@@ -22,6 +22,12 @@
 #define IF_RS_SENT	0x10
 #define IF_READY	0x80000000
 
+/* Variable SLAAC (Contact: Dmytro Shytyi)
+ * draft-mishra-6man-variable-slaac
+ * draft-mishra-v6ops-variable-slaac-problem-stmt
+ */
+#define IF_RA_VAR_PLEN	0x08
+
 /* prefix flags */
 #define IF_PREFIX_ONLINK	0x01
 #define IF_PREFIX_AUTOCONF	0x02
diff -rupN net-next-5.10.0-rc2/include/uapi/linux/icmpv6.h net-next-patch-5.10.0-rc2/include/uapi/linux/icmpv6.h
--- net-next-5.10.0-rc2/include/uapi/linux/icmpv6.h	2020-11-10 08:46:00.351849525 +0100
+++ net-next-patch-5.10.0-rc2/include/uapi/linux/icmpv6.h	2020-11-11 18:11:05.627550135 +0100
@@ -42,7 +42,9 @@ struct icmp6hdr {
                 struct icmpv6_nd_ra {
 			__u8		hop_limit;
 #if defined(__LITTLE_ENDIAN_BITFIELD)
-			__u8		reserved:3,
+			__u8		reserved:1,
+					slaac_var_plen:1,
+					proxy:1,
 					router_pref:2,
 					home_agent:1,
 					other:1,
@@ -53,7 +55,9 @@ struct icmp6hdr {
 					other:1,
 					home_agent:1,
 					router_pref:2,
-					reserved:3;
+					proxy:1,
+					slaac_var_plen:1,
+					reserved:1;
 #else
 #error	"Please fix <asm/byteorder.h>"
 #endif
@@ -78,9 +82,9 @@ struct icmp6hdr {
 #define icmp6_addrconf_other	icmp6_dataun.u_nd_ra.other
 #define icmp6_rt_lifetime	icmp6_dataun.u_nd_ra.rt_lifetime
 #define icmp6_router_pref	icmp6_dataun.u_nd_ra.router_pref
+#define icmp6_slaac_var_plen	icmp6_dataun.u_nd_ra.slaac_var_plen
 };
 
-
 #define ICMPV6_ROUTER_PREF_LOW		0x3
 #define ICMPV6_ROUTER_PREF_MEDIUM	0x0
 #define ICMPV6_ROUTER_PREF_HIGH		0x1
diff -rupN net-next-5.10.0-rc2/net/ipv6/addrconf.c net-next-patch-5.10.0-rc2/net/ipv6/addrconf.c
--- net-next-5.10.0-rc2/net/ipv6/addrconf.c	2020-11-10 08:46:01.075193379 +0100
+++ net-next-patch-5.10.0-rc2/net/ipv6/addrconf.c	2020-11-13 02:30:25.552724864 +0100
@@ -11,6 +11,8 @@
 /*
  *	Changes:
  *
+ *	Dmytro Shytyi			:	Variable SLAAC: SLAAC with
+ *	<dmytro@shytyi.net>			prefixes of arbitrary length.
  *	Janos Farkas			:	delete timer on ifdown
  *	<chexum@bankinf.banki.hu>
  *	Andi Kleen			:	kill double kfree on module
@@ -142,7 +144,12 @@ static int ipv6_count_addresses(const st
 static int ipv6_generate_stable_address(struct in6_addr *addr,
 					u8 dad_count,
 					const struct inet6_dev *idev);
-
+static int ipv6_generate_address_variable_plen(struct in6_addr *address,
+					       u8 dad_count,
+					       const struct inet6_dev *idev,
+					       unsigned int rcvd_prfx_len,
+					       bool stable_privacy_mode);
+static void ipv6_plen_to_mask(int plen, struct in6_addr *net_mask);
 #define IN6_ADDR_HSIZE_SHIFT	8
 #define IN6_ADDR_HSIZE		(1 << IN6_ADDR_HSIZE_SHIFT)
 /*
@@ -1315,10 +1322,14 @@ static int ipv6_create_tempaddr(struct i
 	struct ifa6_config cfg;
 	long max_desync_factor;
 	struct in6_addr addr;
-	int ret = 0;
+	int ret;
+	struct in6_addr net_mask;
+	struct in6_addr temp;
+	struct in6_addr ipv6addr;
+	int i;
 
 	write_lock_bh(&idev->lock);
-
+	ret = 0;
 retry:
 	in6_dev_hold(idev);
 	if (idev->cnf.use_tempaddr <= 0) {
@@ -1340,9 +1351,26 @@ retry:
 		goto out;
 	}
 	in6_ifa_hold(ifp);
-	memcpy(addr.s6_addr, ifp->addr.s6_addr, 8);
-	ipv6_gen_rnd_iid(&addr);
 
+	if (ifp->prefix_len == 64) {
+		memcpy(addr.s6_addr, ifp->addr.s6_addr, 8);
+		ipv6_gen_rnd_iid(&addr);
+	} else if (ifp->prefix_len > 0 && ifp->prefix_len <= 128) {
+		memcpy(addr.s6_addr32, ifp->addr.s6_addr, 16);
+		get_random_bytes(temp.s6_addr32, 16);
+
+		/* tranfrom prefix len into mask */
+		ipv6_plen_to_mask(ifp->prefix_len, &net_mask);
+
+		for (i = 0; i < 4; i++) {
+			/* network prefix */
+			ipv6addr.s6_addr32[i] = addr.s6_addr32[i] & net_mask.s6_addr32[i];
+			/* host id */
+			ipv6addr.s6_addr32[i] |= temp.s6_addr32[i] & ~net_mask.s6_addr32[i];
+		}
+
+		memcpy(addr.s6_addr, ipv6addr.s6_addr32, 16);
+	}
 	age = (now - ifp->tstamp) / HZ;
 
 	regen_advance = idev->cnf.regen_max_retry *
@@ -2576,9 +2604,57 @@ int addrconf_prefix_rcv_add_addr(struct
 				 u32 addr_flags, bool sllao, bool tokenized,
 				 __u32 valid_lft, u32 prefered_lft)
 {
-	struct inet6_ifaddr *ifp = ipv6_get_ifaddr(net, addr, dev, 1);
+	struct inet6_ifaddr *ifp = NULL;
 	int create = 0;
 
+	if ((in6_dev->if_flags & IF_RA_VAR_PLEN) == IF_RA_VAR_PLEN &&
+	    in6_dev->cnf.addr_gen_mode != IN6_ADDR_GEN_MODE_STABLE_PRIVACY) {
+		struct inet6_ifaddr *result = NULL;
+		struct inet6_ifaddr *result_base = NULL;
+		struct in6_addr net_mask;
+		struct in6_addr net_prfx;
+		struct in6_addr curr_net_prfx;
+		bool prfxs_equal;
+		int i;
+
+		result_base = result;
+		rcu_read_lock();
+		list_for_each_entry_rcu(ifp, &in6_dev->addr_list, if_list) {
+			if (!net_eq(dev_net(ifp->idev->dev), net))
+				continue;
+
+			/* tranfrom prefix len into mask */
+			ipv6_plen_to_mask(pinfo->prefix_len, &net_mask);
+			/* Prepare network prefixes */
+			for (i = 0; i < 4; i++) {
+				/* Received/new network prefix */
+				net_prfx.s6_addr32[i] =
+					pinfo->prefix.s6_addr32[i] & net_mask.s6_addr32[i];
+				/* Configured/old IPv6 prefix */
+				curr_net_prfx.s6_addr32[i] =
+					ifp->addr.s6_addr32[i] & net_mask.s6_addr32[i];
+			}
+			/* Compare network prefixes */
+			prfxs_equal = 1;
+			for (i = 0; i < 4; i++) {
+				if ((net_prfx.s6_addr32[i] ^ curr_net_prfx.s6_addr32[i]) != 0)
+					prfxs_equal = 0;
+			}
+			if (prfxs_equal && pinfo->prefix_len == ifp->prefix_len) {
+				result = ifp;
+				in6_ifa_hold(ifp);
+				break;
+			}
+		}
+		rcu_read_unlock();
+		if (result_base != result)
+			ifp = result;
+		else
+			ifp = NULL;
+	} else {
+		ifp = ipv6_get_ifaddr(net, addr, dev, 1);
+	}
+
 	if (!ifp && valid_lft) {
 		int max_addresses = in6_dev->cnf.max_addresses;
 		struct ifa6_config cfg = {
@@ -2781,9 +2857,35 @@ void addrconf_prefix_rcv(struct net_devi
 				dev_addr_generated = true;
 			}
 			goto ok;
+		goto put;
+		} else if (((in6_dev->if_flags & IF_RA_VAR_PLEN) == IF_RA_VAR_PLEN) &&
+			  pinfo->prefix_len > 0 && pinfo->prefix_len <= 128) {
+			/* SLAAC with prefixes of arbitrary length (Variable SLAAC).
+			 * draft-mishra-6man-variable-slaac
+			 * draft-mishra-v6ops-variable-slaac-problem-stmt
+			 * Contact: Dmytro Shytyi.
+			 */
+			memcpy(&addr, &pinfo->prefix, 16);
+			if (in6_dev->cnf.addr_gen_mode == IN6_ADDR_GEN_MODE_STABLE_PRIVACY) {
+				if (!ipv6_generate_address_variable_plen(&addr,
+									 0,
+									 in6_dev,
+									 pinfo->prefix_len,
+									 true)) {
+					addr_flags |= IFA_F_STABLE_PRIVACY;
+					goto ok;
+			}
+			} else if (!ipv6_generate_address_variable_plen(&addr,
+									0,
+									in6_dev,
+									pinfo->prefix_len,
+									false)) {
+				goto ok;
+			}
+		} else {
+			net_dbg_ratelimited("IPv6: Prefix with unexpected length %d\n",
+					    pinfo->prefix_len);
 		}
-		net_dbg_ratelimited("IPv6 addrconf: prefix with wrong length %d\n",
-				    pinfo->prefix_len);
 		goto put;
 
 ok:
@@ -3264,6 +3366,118 @@ retry:
 	return 0;
 }
 
+static void ipv6_plen_to_mask(int plen, struct in6_addr *net_mask)
+{
+	int i, plen_bytes;
+	char bit_array[7] = {0b10000000,
+			     0b11000000,
+			     0b11100000,
+			     0b11110000,
+			     0b11111000,
+			     0b11111100,
+			     0b11111110};
+
+	if (plen <= 0 || plen > 128)
+		pr_err("Unexpected plen: %d", plen);
+
+	memset(net_mask, 0x00, sizeof(*net_mask));
+
+	/* We set all bits == 1 of s6_addr[i] */
+	plen_bytes = plen / 8;
+	for (i = 0; i < plen_bytes; i++)
+		net_mask->s6_addr[i] = 0xff;
+
+	/* We add bits from the bit_array to
+	 * netmask starting from plen_bytes position
+	 */
+	if (plen % 8)
+		net_mask->s6_addr[plen_bytes] = bit_array[(plen % 8) - 1];
+	memcpy(net_mask->s6_addr32, net_mask->s6_addr, 16);
+}
+
+static int ipv6_generate_address_variable_plen(struct in6_addr *address,
+					       u8 dad_count,
+					       const struct inet6_dev *idev,
+					       unsigned int rcvd_prfx_len,
+					       bool stable_privacy_mode)
+{
+	static DEFINE_SPINLOCK(lock);
+	static __u32 digest[SHA1_DIGEST_WORDS];
+	static __u32 workspace[SHA1_WORKSPACE_WORDS];
+
+	static union {
+		char __data[SHA1_BLOCK_SIZE];
+		struct {
+			struct in6_addr secret;
+			__be32 prefix[2];
+			unsigned char hwaddr[MAX_ADDR_LEN];
+			u8 dad_count;
+		} __packed;
+	} data;
+
+	struct in6_addr ipv6_temp_addr;
+	struct in6_addr net_mask;
+	struct in6_addr secret;
+	struct in6_addr temp;
+	struct net *net = dev_net(idev->dev);
+	int i;
+
+	BUILD_BUG_ON(sizeof(data.__data) != sizeof(data));
+
+	if (stable_privacy_mode) {
+		if (idev->cnf.stable_secret.initialized)
+			secret = idev->cnf.stable_secret.secret;
+		else if (net->ipv6.devconf_dflt->stable_secret.initialized)
+			secret = net->ipv6.devconf_dflt->stable_secret.secret;
+		else
+			return -1;
+	}
+
+retry:
+	spin_lock_bh(&lock);
+	if (stable_privacy_mode) {
+		sha1_init(digest);
+		memset(&data, 0, sizeof(data));
+		memset(workspace, 0, sizeof(workspace));
+		memcpy(data.hwaddr, idev->dev->perm_addr, idev->dev->addr_len);
+		data.prefix[0] = address->s6_addr32[0];
+		data.prefix[1] = address->s6_addr32[1];
+		data.secret = secret;
+		data.dad_count = dad_count;
+
+		sha1_transform(digest, data.__data, workspace);
+
+		temp = *address;
+		temp.s6_addr32[0] = (__force __be32)digest[0];
+		temp.s6_addr32[1] = (__force __be32)digest[1];
+		temp.s6_addr32[2] = (__force __be32)digest[2];
+		temp.s6_addr32[3] = (__force __be32)digest[3];
+	} else {
+		temp = *address;
+		get_random_bytes(temp.s6_addr32, 16);
+	}
+	spin_unlock_bh(&lock);
+
+	if (ipv6_reserved_interfaceid(temp)) {
+		dad_count++;
+		if (dad_count > dev_net(idev->dev)->ipv6.sysctl.idgen_retries)
+			return -1;
+		goto retry;
+	}
+
+	/* convert plen to mask */
+	ipv6_plen_to_mask(rcvd_prfx_len, &net_mask);
+	for (i = 0; i < 4; i++) {
+		/* network prefix */
+		ipv6_temp_addr.s6_addr32[i] = address->s6_addr32[i] & net_mask.s6_addr32[i];
+		/* host id */
+		ipv6_temp_addr.s6_addr32[i] |= temp.s6_addr32[i] & ~net_mask.s6_addr32[i];
+	}
+
+	*address = ipv6_temp_addr;
+	return 0;
+}
+
 static void ipv6_gen_mode_random_init(struct inet6_dev *idev)
 {
 	struct ipv6_stable_secret *s = &idev->cnf.stable_secret;
diff -rupN net-next-5.10.0-rc2/net/ipv6/ndisc.c net-next-patch-5.10.0-rc2/net/ipv6/ndisc.c
--- net-next-5.10.0-rc2/net/ipv6/ndisc.c	2020-11-10 08:46:01.091860289 +0100
+++ net-next-patch-5.10.0-rc2/net/ipv6/ndisc.c	2020-11-11 18:11:05.630883513 +0100
@@ -1244,6 +1244,8 @@ static void ndisc_router_discovery(struc
 		in6_dev->if_flags |= IF_RA_RCVD;
 	}
 
+	in6_dev->if_flags |= ra_msg->icmph.icmp6_slaac_var_plen ?
+					IF_RA_VAR_PLEN : 0;
 	/*
 	 * Remember the managed/otherconf flags from most recently
 	 * received RA message (RFC 2462) -- yoshfuji

^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH net-next] net: Variable SLAAC: SLAAC with prefixes of arbitrary length in PIO
  2020-11-13  1:53         ` Jakub Kicinski
@ 2020-11-13  2:01           ` Dave Hansen
  0 siblings, 0 replies; 49+ messages in thread
From: Dave Hansen @ 2020-11-13  2:01 UTC (permalink / raw)
  To: Jakub Kicinski
  Cc: kernel test robot, Dmytro Shytyi, kuznet, yoshfuji, liuhangbin,
	davem, netdev, linux-kernel, kbuild-all

On 11/12/20 5:53 PM, Jakub Kicinski wrote:
>> I do agree, though, that it's confusing *what* they reported, especially
>> if the patch in question is fixing something *else*.  Rather than invent
>> a new tag, maybe a comment would suffice:
>>
>> Reported-by: kernel test robot <lkp@intel.com> # bug in earlier revision
> Fine by me, although its not common to add Reported-by tags for people
> who point out issues in review, so why add a tag for the bot?

If for no other reason, it helps Philip and team generate accurate data
on how effective and helpful the bot is.  That, in turn, plays a role in
making sure that Intel keeps funding it.

^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH net-next V4] net: Variable SLAAC: SLAAC with prefixes of arbitrary length in PIO
  2020-11-13  1:56       ` [PATCH net-next V4] " Dmytro Shytyi
@ 2020-11-13 12:38         ` Hideaki Yoshifuji
  2020-11-13 19:09           ` Dmytro Shytyi
  2020-11-13 19:36         ` [PATCH net-next V5] " Dmytro Shytyi
  1 sibling, 1 reply; 49+ messages in thread
From: Hideaki Yoshifuji @ 2020-11-13 12:38 UTC (permalink / raw)
  To: Dmytro Shytyi
  Cc: kuba, kuznet, yoshfuji, liuhangbin, davem, netdev, linux-kernel,
	Hideaki Yoshifuji

Hi,

2020年11月13日(金) 10:57 Dmytro Shytyi <dmytro@shytyi.net>:
>
> Variable SLAAC: SLAAC with prefixes of arbitrary length in PIO (randomly
> generated hostID or stable privacy + privacy extensions).
> The main problem is that SLAAC RA or PD allocates a /64 by the Wireless
> carrier 4G, 5G to a mobile hotspot, however segmentation of the /64 via
> SLAAC is required so that downstream interfaces can be further subnetted.
> Example: uCPE device (4G + WI-FI enabled) receives /64 via Wireless, and
> assigns /72 to VNF-Firewall, /72 to WIFI, /72 to VNF-Router, /72 to
> Load-Balancer and /72 to wired connected devices.
> IETF document that defines problem statement:
> draft-mishra-v6ops-variable-slaac-problem-stmt
> IETF document that specifies variable slaac:
> draft-mishra-6man-variable-slaac
>
> Signed-off-by: Dmytro Shytyi <dmytro@shytyi.net>
> ---
> diff -rupN net-next-5.10.0-rc2/net/ipv6/addrconf.c net-next-patch-5.10.0-rc2/net/ipv6/addrconf.c
> --- net-next-5.10.0-rc2/net/ipv6/addrconf.c     2020-11-10 08:46:01.075193379 +0100
> +++ net-next-patch-5.10.0-rc2/net/ipv6/addrconf.c       2020-11-13 02:30:25.552724864 +0100
> @@ -1315,10 +1322,14 @@ static int ipv6_create_tempaddr(struct i
>         struct ifa6_config cfg;
>         long max_desync_factor;
>         struct in6_addr addr;
> -       int ret = 0;
> +       int ret;
> +       struct in6_addr net_mask;
> +       struct in6_addr temp;
> +       struct in6_addr ipv6addr;
> +       int i;
>
>         write_lock_bh(&idev->lock);
> -
> +       ret = 0;
>  retry:
>         in6_dev_hold(idev);
>         if (idev->cnf.use_tempaddr <= 0) {
> @@ -1340,9 +1351,26 @@ retry:
>                 goto out;
>         }
>         in6_ifa_hold(ifp);
> -       memcpy(addr.s6_addr, ifp->addr.s6_addr, 8);
> -       ipv6_gen_rnd_iid(&addr);
>
> +       if (ifp->prefix_len == 64) {
> +               memcpy(addr.s6_addr, ifp->addr.s6_addr, 8);
> +               ipv6_gen_rnd_iid(&addr);
> +       } else if (ifp->prefix_len > 0 && ifp->prefix_len <= 128) {
> +               memcpy(addr.s6_addr32, ifp->addr.s6_addr, 16);
> +               get_random_bytes(temp.s6_addr32, 16);
> +
> +               /* tranfrom prefix len into mask */
> +               ipv6_plen_to_mask(ifp->prefix_len, &net_mask);
> +
> +               for (i = 0; i < 4; i++) {
> +                       /* network prefix */
> +                       ipv6addr.s6_addr32[i] = addr.s6_addr32[i] & net_mask.s6_addr32[i];
> +                       /* host id */
> +                       ipv6addr.s6_addr32[i] |= temp.s6_addr32[i] & ~net_mask.s6_addr32[i];
> +               }
> +
> +               memcpy(addr.s6_addr, ipv6addr.s6_addr32, 16);
>

ipv6_addr_copy() and then ipv6_addr_prefix_copy()

+       }
>         age = (now - ifp->tstamp) / HZ;
>
>         regen_advance = idev->cnf.regen_max_retry *
> @@ -2576,9 +2604,57 @@ int addrconf_prefix_rcv_add_addr(struct
>                                  u32 addr_flags, bool sllao, bool tokenized,
>                                  __u32 valid_lft, u32 prefered_lft)
>  {
> -       struct inet6_ifaddr *ifp = ipv6_get_ifaddr(net, addr, dev, 1);
> +       struct inet6_ifaddr *ifp = NULL;
>         int create = 0;
>
> +       if ((in6_dev->if_flags & IF_RA_VAR_PLEN) == IF_RA_VAR_PLEN &&
> +           in6_dev->cnf.addr_gen_mode != IN6_ADDR_GEN_MODE_STABLE_PRIVACY) {
> +               struct inet6_ifaddr *result = NULL;
> +               struct inet6_ifaddr *result_base = NULL;
> +               struct in6_addr net_mask;
> +               struct in6_addr net_prfx;
> +               struct in6_addr curr_net_prfx;
> +               bool prfxs_equal;
> +               int i;
> +
> +               result_base = result;
> +               rcu_read_lock();
> +               list_for_each_entry_rcu(ifp, &in6_dev->addr_list, if_list) {
> +                       if (!net_eq(dev_net(ifp->idev->dev), net))
> +                               continue;
> +
> +                       /* tranfrom prefix len into mask */
> +                       ipv6_plen_to_mask(pinfo->prefix_len, &net_mask);
> +                       /* Prepare network prefixes */
> +                       for (i = 0; i < 4; i++) {
> +                               /* Received/new network prefix */
> +                               net_prfx.s6_addr32[i] =
> +                                       pinfo->prefix.s6_addr32[i] & net_mask.s6_addr32[i];
> +                               /* Configured/old IPv6 prefix */
> +                               curr_net_prfx.s6_addr32[i] =
> +                                       ifp->addr.s6_addr32[i] & net_mask.s6_addr32[i];
> +                       }
> +                       /* Compare network prefixes */
> +                       prfxs_equal = 1;
> +                       for (i = 0; i < 4; i++) {
> +                               if ((net_prfx.s6_addr32[i] ^ curr_net_prfx.s6_addr32[i]) != 0)
> +                                       prfxs_equal = 0;
> +                       }

ipv6_prefix_equal()

> +                       if (prfxs_equal && pinfo->prefix_len == ifp->prefix_len) {
> +                               result = ifp;
> +                               in6_ifa_hold(ifp);
> +                               break;
> +                       }
> +               }
> +               rcu_read_unlock();
> +               if (result_base != result)
> +                       ifp = result;
> +               else
> +                       ifp = NULL;
> +       } else {
> +               ifp = ipv6_get_ifaddr(net, addr, dev, 1);
> +       }
> +
>         if (!ifp && valid_lft) {
>                 int max_addresses = in6_dev->cnf.max_addresses;
>                 struct ifa6_config cfg = {
> @@ -2781,9 +2857,35 @@ void addrconf_prefix_rcv(struct net_devi
>                                 dev_addr_generated = true;
>                         }
>                         goto ok;
> +               goto put;
> +               } else if (((in6_dev->if_flags & IF_RA_VAR_PLEN) == IF_RA_VAR_PLEN) &&
> +                         pinfo->prefix_len > 0 && pinfo->prefix_len <= 128) {
> +                       /* SLAAC with prefixes of arbitrary length (Variable SLAAC).
> +                        * draft-mishra-6man-variable-slaac
> +                        * draft-mishra-v6ops-variable-slaac-problem-stmt
> +                        * Contact: Dmytro Shytyi.
> +                        */
> +                       memcpy(&addr, &pinfo->prefix, 16);
> +                       if (in6_dev->cnf.addr_gen_mode == IN6_ADDR_GEN_MODE_STABLE_PRIVACY) {
> +                               if (!ipv6_generate_address_variable_plen(&addr,
> +                                                                        0,
> +                                                                        in6_dev,
> +                                                                        pinfo->prefix_len,
> +                                                                        true)) {
> +                                       addr_flags |= IFA_F_STABLE_PRIVACY;
> +                                       goto ok;
> +                       }
> +                       } else if (!ipv6_generate_address_variable_plen(&addr,
> +                                                                       0,
> +                                                                       in6_dev,
> +                                                                       pinfo->prefix_len,
> +                                                                       false)) {
> +                               goto ok;
> +                       }
> +               } else {
> +                       net_dbg_ratelimited("IPv6: Prefix with unexpected length %d\n",
> +                                           pinfo->prefix_len);
>                 }
> -               net_dbg_ratelimited("IPv6 addrconf: prefix with wrong length %d\n",
> -                                   pinfo->prefix_len);
>                 goto put;
>
>  ok:
> @@ -3264,6 +3366,118 @@ retry:
>         return 0;
>  }
>
> +static void ipv6_plen_to_mask(int plen, struct in6_addr *net_mask)
> +{
> +       int i, plen_bytes;
> +       char bit_array[7] = {0b10000000,
> +                            0b11000000,
> +                            0b11100000,
> +                            0b11110000,
> +                            0b11111000,
> +                            0b11111100,
> +                            0b11111110};
> +
> +       if (plen <= 0 || plen > 128)
> +               pr_err("Unexpected plen: %d", plen);
> +
> +       memset(net_mask, 0x00, sizeof(*net_mask));
> +
> +       /* We set all bits == 1 of s6_addr[i] */
> +       plen_bytes = plen / 8;
> +       for (i = 0; i < plen_bytes; i++)
> +               net_mask->s6_addr[i] = 0xff;
> +
> +       /* We add bits from the bit_array to
> +        * netmask starting from plen_bytes position
> +        */
> +       if (plen % 8)
> +               net_mask->s6_addr[plen_bytes] = bit_array[(plen % 8) - 1];
> +       memcpy(net_mask->s6_addr32, net_mask->s6_addr, 16);
> +}

I don't think we need this function.
If needed, we could introduce ipv6_addr_host() (like ipv6_addr_prefix())
in include/net/ipv6.h.

^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH net-next V4] net: Variable SLAAC: SLAAC with prefixes of arbitrary length in PIO
  2020-11-13 12:38         ` Hideaki Yoshifuji
@ 2020-11-13 19:09           ` Dmytro Shytyi
  0 siblings, 0 replies; 49+ messages in thread
From: Dmytro Shytyi @ 2020-11-13 19:09 UTC (permalink / raw)
  To: Hideaki Yoshifuji
  Cc: kuba, kuznet, yoshfuji, liuhangbin, davem, netdev, linux-kernel

Hello,

---- On Fri, 13 Nov 2020 13:38:02 +0100 Hideaki Yoshifuji <hideaki.yoshifuji@miraclelinux.com> wrote ----

 > Hi, 
 >  
 > 2020年11月13日(金) 10:57 Dmytro Shytyi <dmytro@shytyi.net>: 
 > > 
 > > Variable SLAAC: SLAAC with prefixes of arbitrary length in PIO (randomly 
 > > generated hostID or stable privacy + privacy extensions). 
 > > The main problem is that SLAAC RA or PD allocates a /64 by the Wireless 
 > > carrier 4G, 5G to a mobile hotspot, however segmentation of the /64 via 
 > > SLAAC is required so that downstream interfaces can be further subnetted. 
 > > Example: uCPE device (4G + WI-FI enabled) receives /64 via Wireless, and 
 > > assigns /72 to VNF-Firewall, /72 to WIFI, /72 to VNF-Router, /72 to 
 > > Load-Balancer and /72 to wired connected devices. 
 > > IETF document that defines problem statement: 
 > > draft-mishra-v6ops-variable-slaac-problem-stmt 
 > > IETF document that specifies variable slaac: 
 > > draft-mishra-6man-variable-slaac 
 > > 
 > > Signed-off-by: Dmytro Shytyi <dmytro@shytyi.net> 
 > > --- 
 > > diff -rupN net-next-5.10.0-rc2/net/ipv6/addrconf.c net-next-patch-5.10.0-rc2/net/ipv6/addrconf.c 
 > > --- net-next-5.10.0-rc2/net/ipv6/addrconf.c     2020-11-10 08:46:01.075193379 +0100 
 > > +++ net-next-patch-5.10.0-rc2/net/ipv6/addrconf.c       2020-11-13 02:30:25.552724864 +0100 
 > > @@ -1315,10 +1322,14 @@ static int ipv6_create_tempaddr(struct i 
 > >         struct ifa6_config cfg; 
 > >         long max_desync_factor; 
 > >         struct in6_addr addr; 
 > > -       int ret = 0; 
 > > +       int ret; 
 > > +       struct in6_addr net_mask; 
 > > +       struct in6_addr temp; 
 > > +       struct in6_addr ipv6addr; 
 > > +       int i; 
 > > 
 > >         write_lock_bh(&idev->lock); 
 > > - 
 > > +       ret = 0; 
 > >  retry: 
 > >         in6_dev_hold(idev); 
 > >         if (idev->cnf.use_tempaddr <= 0) { 
 > > @@ -1340,9 +1351,26 @@ retry: 
 > >                 goto out; 
 > >         } 
 > >         in6_ifa_hold(ifp); 
 > > -       memcpy(addr.s6_addr, ifp->addr.s6_addr, 8); 
 > > -       ipv6_gen_rnd_iid(&addr); 
 > > 
 > > +       if (ifp->prefix_len == 64) { 
 > > +               memcpy(addr.s6_addr, ifp->addr.s6_addr, 8); 
 > > +               ipv6_gen_rnd_iid(&addr); 
 > > +       } else if (ifp->prefix_len > 0 && ifp->prefix_len <= 128) { 
 > > +               memcpy(addr.s6_addr32, ifp->addr.s6_addr, 16); 
 > > +               get_random_bytes(temp.s6_addr32, 16); 
 > > + 
 > > +               /* tranfrom prefix len into mask */ 
 > > +               ipv6_plen_to_mask(ifp->prefix_len, &net_mask); 
 > > + 
 > > +               for (i = 0; i < 4; i++) { 
 > > +                       /* network prefix */ 
 > > +                       ipv6addr.s6_addr32[i] = addr.s6_addr32[i] & net_mask.s6_addr32[i]; 
 > > +                       /* host id */ 
 > > +                       ipv6addr.s6_addr32[i] |= temp.s6_addr32[i] & ~net_mask.s6_addr32[i]; 
 > > +               } 
 > > + 
 > > +               memcpy(addr.s6_addr, ipv6addr.s6_addr32, 16); 
 > > 
 >  
 > ipv6_addr_copy() and then ipv6_addr_prefix_copy() 

 [Dmytro] Understood. Migrating to ipv6_addr_prefix_copy()

 > +       } 
 > >         age = (now - ifp->tstamp) / HZ; 
 > > 
 > >         regen_advance = idev->cnf.regen_max_retry * 
 > > @@ -2576,9 +2604,57 @@ int addrconf_prefix_rcv_add_addr(struct 
 > >                                  u32 addr_flags, bool sllao, bool tokenized, 
 > >                                  __u32 valid_lft, u32 prefered_lft) 
 > >  { 
 > > -       struct inet6_ifaddr *ifp = ipv6_get_ifaddr(net, addr, dev, 1); 
 > > +       struct inet6_ifaddr *ifp = NULL; 
 > >         int create = 0; 
 > > 
 > > +       if ((in6_dev->if_flags & IF_RA_VAR_PLEN) == IF_RA_VAR_PLEN && 
 > > +           in6_dev->cnf.addr_gen_mode != IN6_ADDR_GEN_MODE_STABLE_PRIVACY) { 
 > > +               struct inet6_ifaddr *result = NULL; 
 > > +               struct inet6_ifaddr *result_base = NULL; 
 > > +               struct in6_addr net_mask; 
 > > +               struct in6_addr net_prfx; 
 > > +               struct in6_addr curr_net_prfx; 
 > > +               bool prfxs_equal; 
 > > +               int i; 
 > > + 
 > > +               result_base = result; 
 > > +               rcu_read_lock(); 
 > > +               list_for_each_entry_rcu(ifp, &in6_dev->addr_list, if_list) { 
 > > +                       if (!net_eq(dev_net(ifp->idev->dev), net)) 
 > > +                               continue; 
 > > + 
 > > +                       /* tranfrom prefix len into mask */ 
 > > +                       ipv6_plen_to_mask(pinfo->prefix_len, &net_mask); 
 > > +                       /* Prepare network prefixes */ 
 > > +                       for (i = 0; i < 4; i++) { 
 > > +                               /* Received/new network prefix */ 
 > > +                               net_prfx.s6_addr32[i] = 
 > > +                                       pinfo->prefix.s6_addr32[i] & net_mask.s6_addr32[i]; 
 > > +                               /* Configured/old IPv6 prefix */ 
 > > +                               curr_net_prfx.s6_addr32[i] = 
 > > +                                       ifp->addr.s6_addr32[i] & net_mask.s6_addr32[i]; 
 > > +                       } 
 > > +                       /* Compare network prefixes */ 
 > > +                       prfxs_equal = 1; 
 > > +                       for (i = 0; i < 4; i++) { 
 > > +                               if ((net_prfx.s6_addr32[i] ^ curr_net_prfx.s6_addr32[i]) != 0) 
 > > +                                       prfxs_equal = 0; 
 > > +                       } 
 >  
 > ipv6_prefix_equal()
 [Dmytro] Understood. Migrating to ipv6_prefix_equal()
 >  
 > > +                       if (prfxs_equal && pinfo->prefix_len == ifp->prefix_len) { 
 > > +                               result = ifp; 
 > > +                               in6_ifa_hold(ifp); 
 > > +                               break; 
 > > +                       } 
 > > +               } 
 > > +               rcu_read_unlock(); 
 > > +               if (result_base != result) 
 > > +                       ifp = result; 
 > > +               else 
 > > +                       ifp = NULL; 
 > > +       } else { 
 > > +               ifp = ipv6_get_ifaddr(net, addr, dev, 1); 
 > > +       } 
 > > + 
 > >         if (!ifp && valid_lft) { 
 > >                 int max_addresses = in6_dev->cnf.max_addresses; 
 > >                 struct ifa6_config cfg = { 
 > > @@ -2781,9 +2857,35 @@ void addrconf_prefix_rcv(struct net_devi 
 > >                                 dev_addr_generated = true; 
 > >                         } 
 > >                         goto ok; 
 > > +               goto put; 
 > > +               } else if (((in6_dev->if_flags & IF_RA_VAR_PLEN) == IF_RA_VAR_PLEN) && 
 > > +                         pinfo->prefix_len > 0 && pinfo->prefix_len <= 128) { 
 > > +                       /* SLAAC with prefixes of arbitrary length (Variable SLAAC). 
 > > +                        * draft-mishra-6man-variable-slaac 
 > > +                        * draft-mishra-v6ops-variable-slaac-problem-stmt 
 > > +                        * Contact: Dmytro Shytyi. 
 > > +                        */ 
 > > +                       memcpy(&addr, &pinfo->prefix, 16); 
 > > +                       if (in6_dev->cnf.addr_gen_mode == IN6_ADDR_GEN_MODE_STABLE_PRIVACY) { 
 > > +                               if (!ipv6_generate_address_variable_plen(&addr, 
 > > +                                                                        0, 
 > > +                                                                        in6_dev, 
 > > +                                                                        pinfo->prefix_len, 
 > > +                                                                        true)) { 
 > > +                                       addr_flags |= IFA_F_STABLE_PRIVACY; 
 > > +                                       goto ok; 
 > > +                       } 
 > > +                       } else if (!ipv6_generate_address_variable_plen(&addr, 
 > > +                                                                       0, 
 > > +                                                                       in6_dev, 
 > > +                                                                       pinfo->prefix_len, 
 > > +                                                                       false)) { 
 > > +                               goto ok; 
 > > +                       } 
 > > +               } else { 
 > > +                       net_dbg_ratelimited("IPv6: Prefix with unexpected length %d\n", 
 > > +                                           pinfo->prefix_len); 
 > >                 } 
 > > -               net_dbg_ratelimited("IPv6 addrconf: prefix with wrong length %d\n", 
 > > -                                   pinfo->prefix_len); 
 > >                 goto put; 
 > > 
 > >  ok: 
 > > @@ -3264,6 +3366,118 @@ retry: 
 > >         return 0; 
 > >  } 
 > > 
 > > +static void ipv6_plen_to_mask(int plen, struct in6_addr *net_mask) 
 > > +{ 
 > > +       int i, plen_bytes; 
 > > +       char bit_array[7] = {0b10000000, 
 > > +                            0b11000000, 
 > > +                            0b11100000, 
 > > +                            0b11110000, 
 > > +                            0b11111000, 
 > > +                            0b11111100, 
 > > +                            0b11111110}; 
 > > + 
 > > +       if (plen <= 0 || plen > 128) 
 > > +               pr_err("Unexpected plen: %d", plen); 
 > > + 
 > > +       memset(net_mask, 0x00, sizeof(*net_mask)); 
 > > + 
 > > +       /* We set all bits == 1 of s6_addr[i] */ 
 > > +       plen_bytes = plen / 8; 
 > > +       for (i = 0; i < plen_bytes; i++) 
 > > +               net_mask->s6_addr[i] = 0xff; 
 > > + 
 > > +       /* We add bits from the bit_array to 
 > > +        * netmask starting from plen_bytes position 
 > > +        */ 
 > > +       if (plen % 8) 
 > > +               net_mask->s6_addr[plen_bytes] = bit_array[(plen % 8) - 1]; 
 > > +       memcpy(net_mask->s6_addr32, net_mask->s6_addr, 16); 
 > > +} 
 >  
 > I don't think we need this function.
[Dmytro] Indeed. 
 > If needed, we could introduce ipv6_addr_host() (like ipv6_addr_prefix()) 
 > in include/net/ipv6.h. 
[Dmytro] Maybe, we will see...

^ permalink raw reply	[flat|nested] 49+ messages in thread

* [PATCH net-next V5] net: Variable SLAAC: SLAAC with prefixes of arbitrary length in PIO
  2020-11-13  1:56       ` [PATCH net-next V4] " Dmytro Shytyi
  2020-11-13 12:38         ` Hideaki Yoshifuji
@ 2020-11-13 19:36         ` Dmytro Shytyi
  2020-11-17 20:43           ` Jakub Kicinski
  1 sibling, 1 reply; 49+ messages in thread
From: Dmytro Shytyi @ 2020-11-13 19:36 UTC (permalink / raw)
  To: kuba, kuznet, yoshfuji, liuhangbin, davem, netdev, linux-kernel

Variable SLAAC: SLAAC with prefixes of arbitrary length in PIO (randomly
generated hostID or stable privacy + privacy extensions).
The main problem is that SLAAC RA or PD allocates a /64 by the Wireless
carrier 4G, 5G to a mobile hotspot, however segmentation of the /64 via
SLAAC is required so that downstream interfaces can be further subnetted.
Example: uCPE device (4G + WI-FI enabled) receives /64 via Wireless, and
assigns /72 to VNF-Firewall, /72 to WIFI, /72 to VNF-Router, /72 to
Load-Balancer and /72 to wired connected devices.
IETF document that defines problem statement:
draft-mishra-v6ops-variable-slaac-problem-stmt
IETF document that specifies variable slaac:
draft-mishra-6man-variable-slaac

Signed-off-by: Dmytro Shytyi <dmytro@shytyi.net>
---
diff -rupN net-next-5.10.0-rc2/include/net/if_inet6.h net-next-patch-5.10.0-rc2/include/net/if_inet6.h
--- net-next-5.10.0-rc2/include/net/if_inet6.h	2020-11-10 08:46:00.195180579 +0100
+++ net-next-patch-5.10.0-rc2/include/net/if_inet6.h	2020-11-11 18:11:05.627550135 +0100
@@ -22,6 +22,12 @@
 #define IF_RS_SENT	0x10
 #define IF_READY	0x80000000
 
+/* Variable SLAAC (Contact: Dmytro Shytyi)
+ * draft-mishra-6man-variable-slaac
+ * draft-mishra-v6ops-variable-slaac-problem-stmt
+ */
+#define IF_RA_VAR_PLEN	0x08
+
 /* prefix flags */
 #define IF_PREFIX_ONLINK	0x01
 #define IF_PREFIX_AUTOCONF	0x02
diff -rupN net-next-5.10.0-rc2/include/uapi/linux/icmpv6.h net-next-patch-5.10.0-rc2/include/uapi/linux/icmpv6.h
--- net-next-5.10.0-rc2/include/uapi/linux/icmpv6.h	2020-11-10 08:46:00.351849525 +0100
+++ net-next-patch-5.10.0-rc2/include/uapi/linux/icmpv6.h	2020-11-11 18:11:05.627550135 +0100
@@ -42,7 +42,9 @@ struct icmp6hdr {
                 struct icmpv6_nd_ra {
 			__u8		hop_limit;
 #if defined(__LITTLE_ENDIAN_BITFIELD)
-			__u8		reserved:3,
+			__u8		reserved:1,
+					slaac_var_plen:1,
+					proxy:1,
 					router_pref:2,
 					home_agent:1,
 					other:1,
@@ -53,7 +55,9 @@ struct icmp6hdr {
 					other:1,
 					home_agent:1,
 					router_pref:2,
-					reserved:3;
+					proxy:1,
+					slaac_var_plen:1,
+					reserved:1;
 #else
 #error	"Please fix <asm/byteorder.h>"
 #endif
@@ -78,9 +82,9 @@ struct icmp6hdr {
 #define icmp6_addrconf_other	icmp6_dataun.u_nd_ra.other
 #define icmp6_rt_lifetime	icmp6_dataun.u_nd_ra.rt_lifetime
 #define icmp6_router_pref	icmp6_dataun.u_nd_ra.router_pref
+#define icmp6_slaac_var_plen	icmp6_dataun.u_nd_ra.slaac_var_plen
 };
 
-
 #define ICMPV6_ROUTER_PREF_LOW		0x3
 #define ICMPV6_ROUTER_PREF_MEDIUM	0x0
 #define ICMPV6_ROUTER_PREF_HIGH		0x1
diff -rupN net-next-5.10.0-rc2/net/ipv6/addrconf.c net-next-patch-5.10.0-rc2/net/ipv6/addrconf.c
--- net-next-5.10.0-rc2/net/ipv6/addrconf.c	2020-11-10 08:46:01.075193379 +0100
+++ net-next-patch-5.10.0-rc2/net/ipv6/addrconf.c	2020-11-13 19:50:04.401227310 +0100
@@ -11,6 +11,8 @@
 /*
  *	Changes:
  *
+ *	Dmytro Shytyi			:	Variable SLAAC: SLAAC with
+ *	<dmytro@shytyi.net>			prefixes of arbitrary length.
  *	Janos Farkas			:	delete timer on ifdown
  *	<chexum@bankinf.banki.hu>
  *	Andi Kleen			:	kill double kfree on module
@@ -142,7 +144,11 @@ static int ipv6_count_addresses(const st
 static int ipv6_generate_stable_address(struct in6_addr *addr,
 					u8 dad_count,
 					const struct inet6_dev *idev);
-
+static int ipv6_generate_address_variable_plen(struct in6_addr *address,
+					       u8 dad_count,
+					       const struct inet6_dev *idev,
+					       unsigned int rcvd_prfx_len,
+					       bool stable_privacy_mode);
 #define IN6_ADDR_HSIZE_SHIFT	8
 #define IN6_ADDR_HSIZE		(1 << IN6_ADDR_HSIZE_SHIFT)
 /*
@@ -1315,10 +1321,11 @@ static int ipv6_create_tempaddr(struct i
 	struct ifa6_config cfg;
 	long max_desync_factor;
 	struct in6_addr addr;
-	int ret = 0;
+	int ret;
+	struct in6_addr temp;
 
 	write_lock_bh(&idev->lock);
-
+	ret = 0;
 retry:
 	in6_dev_hold(idev);
 	if (idev->cnf.use_tempaddr <= 0) {
@@ -1340,9 +1347,16 @@ retry:
 		goto out;
 	}
 	in6_ifa_hold(ifp);
-	memcpy(addr.s6_addr, ifp->addr.s6_addr, 8);
-	ipv6_gen_rnd_iid(&addr);
 
+	if (ifp->prefix_len == 64) {
+		memcpy(addr.s6_addr, ifp->addr.s6_addr, 8);
+		ipv6_gen_rnd_iid(&addr);
+	} else if (ifp->prefix_len > 0 && ifp->prefix_len <= 128) {
+		memcpy(addr.s6_addr32, ifp->addr.s6_addr, 16);
+		get_random_bytes(temp.s6_addr32, 16);
+		ipv6_addr_prefix_copy(&temp, &addr, ifp->prefix_len);
+		memcpy(addr.s6_addr, temp.s6_addr, 16);
+	}
 	age = (now - ifp->tstamp) / HZ;
 
 	regen_advance = idev->cnf.regen_max_retry *
@@ -2576,9 +2590,42 @@ int addrconf_prefix_rcv_add_addr(struct
 				 u32 addr_flags, bool sllao, bool tokenized,
 				 __u32 valid_lft, u32 prefered_lft)
 {
-	struct inet6_ifaddr *ifp = ipv6_get_ifaddr(net, addr, dev, 1);
+	struct inet6_ifaddr *ifp = NULL;
 	int create = 0;
 
+	if ((in6_dev->if_flags & IF_RA_VAR_PLEN) == IF_RA_VAR_PLEN &&
+	    in6_dev->cnf.addr_gen_mode != IN6_ADDR_GEN_MODE_STABLE_PRIVACY) {
+		struct inet6_ifaddr *result = NULL;
+		struct inet6_ifaddr *result_base = NULL;
+		struct in6_addr curr_net_prfx;
+		struct in6_addr net_prfx;
+		bool prfxs_equal;
+
+		result_base = result;
+		rcu_read_lock();
+		list_for_each_entry_rcu(ifp, &in6_dev->addr_list, if_list) {
+			if (!net_eq(dev_net(ifp->idev->dev), net))
+				continue;
+			ipv6_addr_prefix_copy(&net_prfx, &pinfo->prefix, pinfo->prefix_len);
+			ipv6_addr_prefix_copy(&curr_net_prfx, &ifp->addr, pinfo->prefix_len);
+			prfxs_equal =
+				ipv6_prefix_equal(&net_prfx, &curr_net_prfx, pinfo->prefix_len);
+
+			if (prfxs_equal && pinfo->prefix_len == ifp->prefix_len) {
+				result = ifp;
+				in6_ifa_hold(ifp);
+				break;
+			}
+		}
+		rcu_read_unlock();
+		if (result_base != result)
+			ifp = result;
+		else
+			ifp = NULL;
+	} else {
+		ifp = ipv6_get_ifaddr(net, addr, dev, 1);
+	}
+
 	if (!ifp && valid_lft) {
 		int max_addresses = in6_dev->cnf.max_addresses;
 		struct ifa6_config cfg = {
@@ -2781,9 +2828,34 @@ void addrconf_prefix_rcv(struct net_devi
 				dev_addr_generated = true;
 			}
 			goto ok;
+		} else if (((in6_dev->if_flags & IF_RA_VAR_PLEN) == IF_RA_VAR_PLEN) &&
+			  pinfo->prefix_len > 0 && pinfo->prefix_len <= 128) {
+			/* SLAAC with prefixes of arbitrary length (Variable SLAAC).
+			 * draft-mishra-6man-variable-slaac
+			 * draft-mishra-v6ops-variable-slaac-problem-stmt
+			 * Contact: Dmytro Shytyi.
+			 */
+			memcpy(&addr, &pinfo->prefix, 16);
+			if (in6_dev->cnf.addr_gen_mode == IN6_ADDR_GEN_MODE_STABLE_PRIVACY) {
+				if (!ipv6_generate_address_variable_plen(&addr,
+									 0,
+									 in6_dev,
+									 pinfo->prefix_len,
+									 true)) {
+					addr_flags |= IFA_F_STABLE_PRIVACY;
+					goto ok;
+			}
+			} else if (!ipv6_generate_address_variable_plen(&addr,
+									0,
+									in6_dev,
+									pinfo->prefix_len,
+									false)) {
+				goto ok;
+			}
+		} else {
+			net_dbg_ratelimited("IPv6: Prefix with unexpected length %d\n",
+					    pinfo->prefix_len);
 		}
-		net_dbg_ratelimited("IPv6 addrconf: prefix with wrong length %d\n",
-				    pinfo->prefix_len);
 		goto put;
 
 ok:
@@ -3263,6 +3335,77 @@ retry:
 	*address = temp;
 	return 0;
 }
+
+static int ipv6_generate_address_variable_plen(struct in6_addr *address,
+					       u8 dad_count,
+					       const struct inet6_dev *idev,
+					       unsigned int rcvd_prfx_len,
+					       bool stable_privacy_mode)
+{
+	static DEFINE_SPINLOCK(lock);
+	static __u32 digest[SHA1_DIGEST_WORDS];
+	static __u32 workspace[SHA1_WORKSPACE_WORDS];
+
+	static union {
+		char __data[SHA1_BLOCK_SIZE];
+		struct {
+			struct in6_addr secret;
+			__be32 prefix[2];
+			unsigned char hwaddr[MAX_ADDR_LEN];
+			u8 dad_count;
+		} __packed;
+	} data;
+
+	struct in6_addr secret;
+	struct in6_addr temp;
+	struct net *net = dev_net(idev->dev);
+
+	BUILD_BUG_ON(sizeof(data.__data) != sizeof(data));
+
+	if (stable_privacy_mode) {
+		if (idev->cnf.stable_secret.initialized)
+			secret = idev->cnf.stable_secret.secret;
+		else if (net->ipv6.devconf_dflt->stable_secret.initialized)
+			secret = net->ipv6.devconf_dflt->stable_secret.secret;
+		else
+			return -1;
+	}
+
+retry:
+	spin_lock_bh(&lock);
+	if (stable_privacy_mode) {
+		sha1_init(digest);
+		memset(&data, 0, sizeof(data));
+		memset(workspace, 0, sizeof(workspace));
+		memcpy(data.hwaddr, idev->dev->perm_addr, idev->dev->addr_len);
+		data.prefix[0] = address->s6_addr32[0];
+		data.prefix[1] = address->s6_addr32[1];
+		data.secret = secret;
+		data.dad_count = dad_count;
+
+		sha1_transform(digest, data.__data, workspace);
+
+		temp = *address;
+		temp.s6_addr32[0] = (__force __be32)digest[0];
+		temp.s6_addr32[1] = (__force __be32)digest[1];
+		temp.s6_addr32[2] = (__force __be32)digest[2];
+		temp.s6_addr32[3] = (__force __be32)digest[3];
+	} else {
+		temp = *address;
+		get_random_bytes(temp.s6_addr32, 16);
+	}
+	spin_unlock_bh(&lock);
+
+	if (ipv6_reserved_interfaceid(temp)) {
+		dad_count++;
+		if (dad_count > dev_net(idev->dev)->ipv6.sysctl.idgen_retries)
+			return -1;
+		goto retry;
+	}
+	ipv6_addr_prefix_copy(&temp, address, rcvd_prfx_len);
+	*address = temp;
+	return 0;
+}
 
 static void ipv6_gen_mode_random_init(struct inet6_dev *idev)
 {
diff -rupN net-next-5.10.0-rc2/net/ipv6/ndisc.c net-next-patch-5.10.0-rc2/net/ipv6/ndisc.c
--- net-next-5.10.0-rc2/net/ipv6/ndisc.c	2020-11-10 08:46:01.091860289 +0100
+++ net-next-patch-5.10.0-rc2/net/ipv6/ndisc.c	2020-11-11 18:11:05.630883513 +0100
@@ -1244,6 +1244,8 @@ static void ndisc_router_discovery(struc
 		in6_dev->if_flags |= IF_RA_RCVD;
 	}
 
+	in6_dev->if_flags |= ra_msg->icmph.icmp6_slaac_var_plen ?
+					IF_RA_VAR_PLEN : 0;
 	/*
 	 * Remember the managed/otherconf flags from most recently
 	 * received RA message (RFC 2462) -- yoshfuji

^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH net-next V5] net: Variable SLAAC: SLAAC with prefixes of arbitrary length in PIO
  2020-11-13 19:36         ` [PATCH net-next V5] " Dmytro Shytyi
@ 2020-11-17 20:43           ` Jakub Kicinski
  2020-11-18 13:41             ` Dmytro Shytyi
  2020-11-19 13:37             ` [PATCH net-next V6] " Dmytro Shytyi
  0 siblings, 2 replies; 49+ messages in thread
From: Jakub Kicinski @ 2020-11-17 20:43 UTC (permalink / raw)
  To: Dmytro Shytyi; +Cc: kuznet, yoshfuji, liuhangbin, davem, netdev, linux-kernel

On Fri, 13 Nov 2020 20:36:58 +0100 Dmytro Shytyi wrote:
> Variable SLAAC: SLAAC with prefixes of arbitrary length in PIO (randomly
> generated hostID or stable privacy + privacy extensions).
> The main problem is that SLAAC RA or PD allocates a /64 by the Wireless
> carrier 4G, 5G to a mobile hotspot, however segmentation of the /64 via
> SLAAC is required so that downstream interfaces can be further subnetted.
> Example: uCPE device (4G + WI-FI enabled) receives /64 via Wireless, and
> assigns /72 to VNF-Firewall, /72 to WIFI, /72 to VNF-Router, /72 to
> Load-Balancer and /72 to wired connected devices.
> IETF document that defines problem statement:
> draft-mishra-v6ops-variable-slaac-problem-stmt
> IETF document that specifies variable slaac:
> draft-mishra-6man-variable-slaac
> 
> Signed-off-by: Dmytro Shytyi <dmytro@shytyi.net>
> ---
> diff -rupN net-next-5.10.0-rc2/include/net/if_inet6.h net-next-patch-5.10.0-rc2/include/net/if_inet6.h
> --- net-next-5.10.0-rc2/include/net/if_inet6.h	2020-11-10 08:46:00.195180579 +0100
> +++ net-next-patch-5.10.0-rc2/include/net/if_inet6.h	2020-11-11 18:11:05.627550135 +0100
> @@ -22,6 +22,12 @@
>  #define IF_RS_SENT	0x10
>  #define IF_READY	0x80000000
>  
> +/* Variable SLAAC (Contact: Dmytro Shytyi)
> + * draft-mishra-6man-variable-slaac
> + * draft-mishra-v6ops-variable-slaac-problem-stmt
> + */
> +#define IF_RA_VAR_PLEN	0x08
> +
>  /* prefix flags */
>  #define IF_PREFIX_ONLINK	0x01
>  #define IF_PREFIX_AUTOCONF	0x02
> diff -rupN net-next-5.10.0-rc2/include/uapi/linux/icmpv6.h net-next-patch-5.10.0-rc2/include/uapi/linux/icmpv6.h
> --- net-next-5.10.0-rc2/include/uapi/linux/icmpv6.h	2020-11-10 08:46:00.351849525 +0100
> +++ net-next-patch-5.10.0-rc2/include/uapi/linux/icmpv6.h	2020-11-11 18:11:05.627550135 +0100
> @@ -42,7 +42,9 @@ struct icmp6hdr {
>                  struct icmpv6_nd_ra {
>  			__u8		hop_limit;
>  #if defined(__LITTLE_ENDIAN_BITFIELD)
> -			__u8		reserved:3,
> +			__u8		reserved:1,
> +					slaac_var_plen:1,
> +					proxy:1,

What's the status of your draft? I'm not too familiar with the IETF
process, but I'm not sure we should change uAPI headers before the
draft reaches reasonable consensus.

I'd appreciate extra opinions here.

>  					router_pref:2,
>  					home_agent:1,
>  					other:1,
> @@ -53,7 +55,9 @@ struct icmp6hdr {
>  					other:1,
>  					home_agent:1,
>  					router_pref:2,
> -					reserved:3;
> +					proxy:1,
> +					slaac_var_plen:1,
> +					reserved:1;
>  #else
>  #error	"Please fix <asm/byteorder.h>"
>  #endif
> @@ -78,9 +82,9 @@ struct icmp6hdr {
>  #define icmp6_addrconf_other	icmp6_dataun.u_nd_ra.other
>  #define icmp6_rt_lifetime	icmp6_dataun.u_nd_ra.rt_lifetime
>  #define icmp6_router_pref	icmp6_dataun.u_nd_ra.router_pref
> +#define icmp6_slaac_var_plen	icmp6_dataun.u_nd_ra.slaac_var_plen
>  };
>  
> -
>  #define ICMPV6_ROUTER_PREF_LOW		0x3
>  #define ICMPV6_ROUTER_PREF_MEDIUM	0x0
>  #define ICMPV6_ROUTER_PREF_HIGH		0x1
> diff -rupN net-next-5.10.0-rc2/net/ipv6/addrconf.c net-next-patch-5.10.0-rc2/net/ipv6/addrconf.c
> --- net-next-5.10.0-rc2/net/ipv6/addrconf.c	2020-11-10 08:46:01.075193379 +0100
> +++ net-next-patch-5.10.0-rc2/net/ipv6/addrconf.c	2020-11-13 19:50:04.401227310 +0100
> @@ -11,6 +11,8 @@
>  /*
>   *	Changes:
>   *
> + *	Dmytro Shytyi			:	Variable SLAAC: SLAAC with
> + *	<dmytro@shytyi.net>			prefixes of arbitrary length.

Please don't add your name to those headers. We have git now,
authorship if clearly preserved. 

>   *	Janos Farkas			:	delete timer on ifdown
>   *	<chexum@bankinf.banki.hu>
>   *	Andi Kleen			:	kill double kfree on module
> @@ -142,7 +144,11 @@ static int ipv6_count_addresses(const st
>  static int ipv6_generate_stable_address(struct in6_addr *addr,
>  					u8 dad_count,
>  					const struct inet6_dev *idev);
> -
> +static int ipv6_generate_address_variable_plen(struct in6_addr *address,
> +					       u8 dad_count,
> +					       const struct inet6_dev *idev,
> +					       unsigned int rcvd_prfx_len,
> +					       bool stable_privacy_mode);

Can you reorder the code to avoid the fwd declaration?

Also please try to shorten the name of this function.

>  #define IN6_ADDR_HSIZE_SHIFT	8
>  #define IN6_ADDR_HSIZE		(1 << IN6_ADDR_HSIZE_SHIFT)
>  /*
> @@ -1315,10 +1321,11 @@ static int ipv6_create_tempaddr(struct i
>  	struct ifa6_config cfg;
>  	long max_desync_factor;
>  	struct in6_addr addr;
> -	int ret = 0;
> +	int ret;
> +	struct in6_addr temp;

Please keep the reverse xmas tree ordering of variables.
Lines should be ordered longest to shortest.

>  
>  	write_lock_bh(&idev->lock);
> -
> +	ret = 0;

Why did you decide to move the init from the definition?

>  retry:
>  	in6_dev_hold(idev);
>  	if (idev->cnf.use_tempaddr <= 0) {
> @@ -1340,9 +1347,16 @@ retry:
>  		goto out;
>  	}
>  	in6_ifa_hold(ifp);
> -	memcpy(addr.s6_addr, ifp->addr.s6_addr, 8);
> -	ipv6_gen_rnd_iid(&addr);
>  
> +	if (ifp->prefix_len == 64) {
> +		memcpy(addr.s6_addr, ifp->addr.s6_addr, 8);
> +		ipv6_gen_rnd_iid(&addr);
> +	} else if (ifp->prefix_len > 0 && ifp->prefix_len <= 128) {
> +		memcpy(addr.s6_addr32, ifp->addr.s6_addr, 16);
> +		get_random_bytes(temp.s6_addr32, 16);
> +		ipv6_addr_prefix_copy(&temp, &addr, ifp->prefix_len);
> +		memcpy(addr.s6_addr, temp.s6_addr, 16);
> +	}
>  	age = (now - ifp->tstamp) / HZ;
>  
>  	regen_advance = idev->cnf.regen_max_retry *
> @@ -2576,9 +2590,42 @@ int addrconf_prefix_rcv_add_addr(struct
>  				 u32 addr_flags, bool sllao, bool tokenized,
>  				 __u32 valid_lft, u32 prefered_lft)
>  {
> -	struct inet6_ifaddr *ifp = ipv6_get_ifaddr(net, addr, dev, 1);
> +	struct inet6_ifaddr *ifp = NULL;
>  	int create = 0;
>  
> +	if ((in6_dev->if_flags & IF_RA_VAR_PLEN) == IF_RA_VAR_PLEN &&
> +	    in6_dev->cnf.addr_gen_mode != IN6_ADDR_GEN_MODE_STABLE_PRIVACY) {
> +		struct inet6_ifaddr *result = NULL;
> +		struct inet6_ifaddr *result_base = NULL;
> +		struct in6_addr curr_net_prfx;
> +		struct in6_addr net_prfx;
> +		bool prfxs_equal;
> +
> +		result_base = result;
> +		rcu_read_lock();
> +		list_for_each_entry_rcu(ifp, &in6_dev->addr_list, if_list) {
> +			if (!net_eq(dev_net(ifp->idev->dev), net))
> +				continue;
> +			ipv6_addr_prefix_copy(&net_prfx, &pinfo->prefix, pinfo->prefix_len);
> +			ipv6_addr_prefix_copy(&curr_net_prfx, &ifp->addr, pinfo->prefix_len);
> +			prfxs_equal =
> +				ipv6_prefix_equal(&net_prfx, &curr_net_prfx, pinfo->prefix_len);
> +
> +			if (prfxs_equal && pinfo->prefix_len == ifp->prefix_len) {
> +				result = ifp;
> +				in6_ifa_hold(ifp);
> +				break;
> +			}
> +		}
> +		rcu_read_unlock();
> +		if (result_base != result)
> +			ifp = result;
> +		else
> +			ifp = NULL;

Could this be a helper of its own?

> +	} else {
> +		ifp = ipv6_get_ifaddr(net, addr, dev, 1);
> +	}
> +
>  	if (!ifp && valid_lft) {
>  		int max_addresses = in6_dev->cnf.max_addresses;
>  		struct ifa6_config cfg = {
> @@ -2781,9 +2828,34 @@ void addrconf_prefix_rcv(struct net_devi
>  				dev_addr_generated = true;
>  			}
>  			goto ok;
> +		} else if (((in6_dev->if_flags & IF_RA_VAR_PLEN) == IF_RA_VAR_PLEN) &&
> +			  pinfo->prefix_len > 0 && pinfo->prefix_len <= 128) {
> +			/* SLAAC with prefixes of arbitrary length (Variable SLAAC).
> +			 * draft-mishra-6man-variable-slaac
> +			 * draft-mishra-v6ops-variable-slaac-problem-stmt
> +			 * Contact: Dmytro Shytyi.
> +			 */
> +			memcpy(&addr, &pinfo->prefix, 16);
> +			if (in6_dev->cnf.addr_gen_mode == IN6_ADDR_GEN_MODE_STABLE_PRIVACY) {
> +				if (!ipv6_generate_address_variable_plen(&addr,
> +									 0,
> +									 in6_dev,
> +									 pinfo->prefix_len,
> +									 true)) {
> +					addr_flags |= IFA_F_STABLE_PRIVACY;
> +					goto ok;
> +			}
> +			} else if (!ipv6_generate_address_variable_plen(&addr,
> +									0,
> +									in6_dev,
> +									pinfo->prefix_len,
> +									false)) {
> +				goto ok;
> +			}
> +		} else {
> +			net_dbg_ratelimited("IPv6: Prefix with unexpected length %d\n",
> +					    pinfo->prefix_len);
>  		}
> -		net_dbg_ratelimited("IPv6 addrconf: prefix with wrong length %d\n",
> -				    pinfo->prefix_len);
>  		goto put;
>  
>  ok:
> @@ -3263,6 +3335,77 @@ retry:
>  	*address = temp;
>  	return 0;
>  }
> +
> +static int ipv6_generate_address_variable_plen(struct in6_addr *address,
> +					       u8 dad_count,
> +					       const struct inet6_dev *idev,
> +					       unsigned int rcvd_prfx_len,
> +					       bool stable_privacy_mode)
> +{
> +	static DEFINE_SPINLOCK(lock);
> +	static __u32 digest[SHA1_DIGEST_WORDS];
> +	static __u32 workspace[SHA1_WORKSPACE_WORDS];
> +
> +	static union {
> +		char __data[SHA1_BLOCK_SIZE];
> +		struct {
> +			struct in6_addr secret;
> +			__be32 prefix[2];
> +			unsigned char hwaddr[MAX_ADDR_LEN];
> +			u8 dad_count;
> +		} __packed;
> +	} data;
> +
> +	struct in6_addr secret;
> +	struct in6_addr temp;
> +	struct net *net = dev_net(idev->dev);

Please dont add empty lines between variable declarations.

> +	BUILD_BUG_ON(sizeof(data.__data) != sizeof(data));
> +
> +	if (stable_privacy_mode) {
> +		if (idev->cnf.stable_secret.initialized)
> +			secret = idev->cnf.stable_secret.secret;
> +		else if (net->ipv6.devconf_dflt->stable_secret.initialized)
> +			secret = net->ipv6.devconf_dflt->stable_secret.secret;
> +		else
> +			return -1;
> +	}
> +
> +retry:
> +	spin_lock_bh(&lock);
> +	if (stable_privacy_mode) {
> +		sha1_init(digest);
> +		memset(&data, 0, sizeof(data));
> +		memset(workspace, 0, sizeof(workspace));
> +		memcpy(data.hwaddr, idev->dev->perm_addr, idev->dev->addr_len);
> +		data.prefix[0] = address->s6_addr32[0];
> +		data.prefix[1] = address->s6_addr32[1];
> +		data.secret = secret;
> +		data.dad_count = dad_count;
> +
> +		sha1_transform(digest, data.__data, workspace);
> +
> +		temp = *address;
> +		temp.s6_addr32[0] = (__force __be32)digest[0];
> +		temp.s6_addr32[1] = (__force __be32)digest[1];
> +		temp.s6_addr32[2] = (__force __be32)digest[2];
> +		temp.s6_addr32[3] = (__force __be32)digest[3];
> +	} else {
> +		temp = *address;
> +		get_random_bytes(temp.s6_addr32, 16);
> +	}
> +	spin_unlock_bh(&lock);
> +
> +	if (ipv6_reserved_interfaceid(temp)) {
> +		dad_count++;
> +		if (dad_count > dev_net(idev->dev)->ipv6.sysctl.idgen_retries)
> +			return -1;
> +		goto retry;
> +	}
> +	ipv6_addr_prefix_copy(&temp, address, rcvd_prfx_len);
> +	*address = temp;
> +	return 0;
> +}
>  
>  static void ipv6_gen_mode_random_init(struct inet6_dev *idev)
>  {
> diff -rupN net-next-5.10.0-rc2/net/ipv6/ndisc.c net-next-patch-5.10.0-rc2/net/ipv6/ndisc.c
> --- net-next-5.10.0-rc2/net/ipv6/ndisc.c	2020-11-10 08:46:01.091860289 +0100
> +++ net-next-patch-5.10.0-rc2/net/ipv6/ndisc.c	2020-11-11 18:11:05.630883513 +0100
> @@ -1244,6 +1244,8 @@ static void ndisc_router_discovery(struc
>  		in6_dev->if_flags |= IF_RA_RCVD;
>  	}
>  
> +	in6_dev->if_flags |= ra_msg->icmph.icmp6_slaac_var_plen ?
> +					IF_RA_VAR_PLEN : 0;
>  	/*
>  	 * Remember the managed/otherconf flags from most recently
>  	 * received RA message (RFC 2462) -- yoshfuji


^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH net-next V5] net: Variable SLAAC: SLAAC with prefixes of arbitrary length in PIO
  2020-11-17 20:43           ` Jakub Kicinski
@ 2020-11-18 13:41             ` Dmytro Shytyi
  2020-11-18 15:50               ` Jakub Kicinski
  2020-11-19 13:37             ` [PATCH net-next V6] " Dmytro Shytyi
  1 sibling, 1 reply; 49+ messages in thread
From: Dmytro Shytyi @ 2020-11-18 13:41 UTC (permalink / raw)
  To: Jakub Kicinski; +Cc: kuznet, yoshfuji, liuhangbin, davem, netdev, linux-kernel

Hello,

---- On Tue, 17 Nov 2020 21:43:48 +0100 Jakub Kicinski <kuba@kernel.org> wrote ----

 > On Fri, 13 Nov 2020 20:36:58 +0100 Dmytro Shytyi wrote: 
 > > Variable SLAAC: SLAAC with prefixes of arbitrary length in PIO (randomly 
 > > generated hostID or stable privacy + privacy extensions). 
 > > The main problem is that SLAAC RA or PD allocates a /64 by the Wireless 
 > > carrier 4G, 5G to a mobile hotspot, however segmentation of the /64 via 
 > > SLAAC is required so that downstream interfaces can be further subnetted. 
 > > Example: uCPE device (4G + WI-FI enabled) receives /64 via Wireless, and 
 > > assigns /72 to VNF-Firewall, /72 to WIFI, /72 to VNF-Router, /72 to 
 > > Load-Balancer and /72 to wired connected devices. 
 > > IETF document that defines problem statement: 
 > > draft-mishra-v6ops-variable-slaac-problem-stmt 
 > > IETF document that specifies variable slaac: 
 > > draft-mishra-6man-variable-slaac 
 > > 
 > > Signed-off-by: Dmytro Shytyi <dmytro@shytyi.net> 
 > > --- 
 > > diff -rupN net-next-5.10.0-rc2/include/net/if_inet6.h net-next-patch-5.10.0-rc2/include/net/if_inet6.h 
 > > --- net-next-5.10.0-rc2/include/net/if_inet6.h    2020-11-10 08:46:00.195180579 +0100 
 > > +++ net-next-patch-5.10.0-rc2/include/net/if_inet6.h    2020-11-11 18:11:05.627550135 +0100 
 > > @@ -22,6 +22,12 @@ 
 > >  #define IF_RS_SENT    0x10 
 > >  #define IF_READY    0x80000000 
 > > 
 > > +/* Variable SLAAC (Contact: Dmytro Shytyi) 
 > > + * draft-mishra-6man-variable-slaac 
 > > + * draft-mishra-v6ops-variable-slaac-problem-stmt 
 > > + */ 
 > > +#define IF_RA_VAR_PLEN    0x08 
 > > + 
 > >  /* prefix flags */ 
 > >  #define IF_PREFIX_ONLINK    0x01 
 > >  #define IF_PREFIX_AUTOCONF    0x02 
 > > diff -rupN net-next-5.10.0-rc2/include/uapi/linux/icmpv6.h net-next-patch-5.10.0-rc2/include/uapi/linux/icmpv6.h 
 > > --- net-next-5.10.0-rc2/include/uapi/linux/icmpv6.h    2020-11-10 08:46:00.351849525 +0100 
 > > +++ net-next-patch-5.10.0-rc2/include/uapi/linux/icmpv6.h    2020-11-11 18:11:05.627550135 +0100 
 > > @@ -42,7 +42,9 @@ struct icmp6hdr { 
 > >                  struct icmpv6_nd_ra { 
 > >              __u8        hop_limit; 
 > >  #if defined(__LITTLE_ENDIAN_BITFIELD) 
 > > -            __u8        reserved:3, 
 > > +            __u8        reserved:1, 
 > > +                    slaac_var_plen:1, 
 > > +                    proxy:1, 
 >  
 > What's the status of your draft? I'm not too familiar with the IETF 
 > process, but I'm not sure we should change uAPI headers before the 
 > draft reaches reasonable consensus. 
 >  
 > I'd appreciate extra opinions here. 

Okay, we may avoid modification of the uAPI headers as plen (prefix length !=64) itself serves as a flag to activate the "Variable SLAAC"
I will modify the patch accordingly.

 > >                      router_pref:2, 
 > >                      home_agent:1, 
 > >                      other:1, 
 > > @@ -53,7 +55,9 @@ struct icmp6hdr { 
 > >                      other:1, 
 > >                      home_agent:1, 
 > >                      router_pref:2, 
 > > -                    reserved:3; 
 > > +                    proxy:1, 
 > > +                    slaac_var_plen:1, 
 > > +                    reserved:1; 
 > >  #else 
 > >  #error    "Please fix <asm/byteorder.h>" 
 > >  #endif 
 > > @@ -78,9 +82,9 @@ struct icmp6hdr { 
 > >  #define icmp6_addrconf_other    icmp6_dataun.u_nd_ra.other 
 > >  #define icmp6_rt_lifetime    icmp6_dataun.u_nd_ra.rt_lifetime 
 > >  #define icmp6_router_pref    icmp6_dataun.u_nd_ra.router_pref 
 > > +#define icmp6_slaac_var_plen    icmp6_dataun.u_nd_ra.slaac_var_plen 
 > >  }; 
 > > 
 > > - 
 > >  #define ICMPV6_ROUTER_PREF_LOW        0x3 
 > >  #define ICMPV6_ROUTER_PREF_MEDIUM    0x0 
 > >  #define ICMPV6_ROUTER_PREF_HIGH        0x1 
 > > diff -rupN net-next-5.10.0-rc2/net/ipv6/addrconf.c net-next-patch-5.10.0-rc2/net/ipv6/addrconf.c 
 > > --- net-next-5.10.0-rc2/net/ipv6/addrconf.c    2020-11-10 08:46:01.075193379 +0100 
 > > +++ net-next-patch-5.10.0-rc2/net/ipv6/addrconf.c    2020-11-13 19:50:04.401227310 +0100 
 > > @@ -11,6 +11,8 @@ 
 > >  /* 
 > >   *    Changes: 
 > >   * 
 > > + *    Dmytro Shytyi            :    Variable SLAAC: SLAAC with 
 > > + *    <dmytro@shytyi.net>            prefixes of arbitrary length. 
 >  
 > Please don't add your name to those headers. We have git now, 
 > authorship if clearly preserved. 

Understood.

 > >   *    Janos Farkas            :    delete timer on ifdown 
 > >   *    <chexum@bankinf.banki.hu> 
 > >   *    Andi Kleen            :    kill double kfree on module 
 > > @@ -142,7 +144,11 @@ static int ipv6_count_addresses(const st 
 > >  static int ipv6_generate_stable_address(struct in6_addr *addr, 
 > >                      u8 dad_count, 
 > >                      const struct inet6_dev *idev); 
 > > - 
 > > +static int ipv6_generate_address_variable_plen(struct in6_addr *address, 
 > > +                           u8 dad_count, 
 > > +                           const struct inet6_dev *idev, 
 > > +                           unsigned int rcvd_prfx_len, 
 > > +                           bool stable_privacy_mode); 
 >  
 > Can you reorder the code to avoid the fwd declaration? 
 > Also please try to shorten the name of this function. 

Understood.

 > >  #define IN6_ADDR_HSIZE_SHIFT    8 
 > >  #define IN6_ADDR_HSIZE        (1 << IN6_ADDR_HSIZE_SHIFT) 
 > >  /* 
 > > @@ -1315,10 +1321,11 @@ static int ipv6_create_tempaddr(struct i 
 > >      struct ifa6_config cfg; 
 > >      long max_desync_factor; 
 > >      struct in6_addr addr; 
 > > -    int ret = 0; 
 > > +    int ret; 
 > > +    struct in6_addr temp; 
 >  
 > Please keep the reverse xmas tree ordering of variables. 
 > Lines should be ordered longest to shortest. 
 
Understood.

 > > 
 > >      write_lock_bh(&idev->lock); 
 > > - 
 > > +    ret = 0; 
 >  
 > Why did you decide to move the init from the definition? 
 >  
 > >  retry: 
 > >      in6_dev_hold(idev); 
 > >      if (idev->cnf.use_tempaddr <= 0) { 
 > > @@ -1340,9 +1347,16 @@ retry: 
 > >          goto out; 
 > >      } 
 > >      in6_ifa_hold(ifp); 
 > > -    memcpy(addr.s6_addr, ifp->addr.s6_addr, 8); 
 > > -    ipv6_gen_rnd_iid(&addr); 
 > > 
 > > +    if (ifp->prefix_len == 64) { 
 > > +        memcpy(addr.s6_addr, ifp->addr.s6_addr, 8); 
 > > +        ipv6_gen_rnd_iid(&addr); 
 > > +    } else if (ifp->prefix_len > 0 && ifp->prefix_len <= 128) { 
 > > +        memcpy(addr.s6_addr32, ifp->addr.s6_addr, 16); 
 > > +        get_random_bytes(temp.s6_addr32, 16); 
 > > +        ipv6_addr_prefix_copy(&temp, &addr, ifp->prefix_len); 
 > > +        memcpy(addr.s6_addr, temp.s6_addr, 16); 
 > > +    } 
 > >      age = (now - ifp->tstamp) / HZ; 
 > > 
 > >      regen_advance = idev->cnf.regen_max_retry * 
 > > @@ -2576,9 +2590,42 @@ int addrconf_prefix_rcv_add_addr(struct 
 > >                   u32 addr_flags, bool sllao, bool tokenized, 
 > >                   __u32 valid_lft, u32 prefered_lft) 
 > >  { 
 > > -    struct inet6_ifaddr *ifp = ipv6_get_ifaddr(net, addr, dev, 1); 
 > > +    struct inet6_ifaddr *ifp = NULL; 
 > >      int create = 0; 
 > > 
 > > +    if ((in6_dev->if_flags & IF_RA_VAR_PLEN) == IF_RA_VAR_PLEN && 
 > > +        in6_dev->cnf.addr_gen_mode != IN6_ADDR_GEN_MODE_STABLE_PRIVACY) { 
 > > +        struct inet6_ifaddr *result = NULL; 
 > > +        struct inet6_ifaddr *result_base = NULL; 
 > > +        struct in6_addr curr_net_prfx; 
 > > +        struct in6_addr net_prfx; 
 > > +        bool prfxs_equal; 
 > > + 
 > > +        result_base = result; 
 > > +        rcu_read_lock(); 
 > > +        list_for_each_entry_rcu(ifp, &in6_dev->addr_list, if_list) { 
 > > +            if (!net_eq(dev_net(ifp->idev->dev), net)) 
 > > +                continue; 
 > > +            ipv6_addr_prefix_copy(&net_prfx, &pinfo->prefix, pinfo->prefix_len); 
 > > +            ipv6_addr_prefix_copy(&curr_net_prfx, &ifp->addr, pinfo->prefix_len); 
 > > +            prfxs_equal = 
 > > +                ipv6_prefix_equal(&net_prfx, &curr_net_prfx, pinfo->prefix_len); 
 > > + 
 > > +            if (prfxs_equal && pinfo->prefix_len == ifp->prefix_len) { 
 > > +                result = ifp; 
 > > +                in6_ifa_hold(ifp); 
 > > +                break; 
 > > +            } 
 > > +        } 
 > > +        rcu_read_unlock(); 
 > > +        if (result_base != result) 
 > > +            ifp = result; 
 > > +        else 
 > > +            ifp = NULL; 
 >  
 > Could this be a helper of its own? 

Explain the thought please. It is not clear for me.

 > > +    } else { 
 > > +        ifp = ipv6_get_ifaddr(net, addr, dev, 1); 
 > > +    } 
 > > + 
 > >      if (!ifp && valid_lft) { 
 > >          int max_addresses = in6_dev->cnf.max_addresses; 
 > >          struct ifa6_config cfg = { 
 > > @@ -2781,9 +2828,34 @@ void addrconf_prefix_rcv(struct net_devi 
 > >                  dev_addr_generated = true; 
 > >              } 
 > >              goto ok; 
 > > +        } else if (((in6_dev->if_flags & IF_RA_VAR_PLEN) == IF_RA_VAR_PLEN) && 
 > > +              pinfo->prefix_len > 0 && pinfo->prefix_len <= 128) { 
 > > +            /* SLAAC with prefixes of arbitrary length (Variable SLAAC). 
 > > +             * draft-mishra-6man-variable-slaac 
 > > +             * draft-mishra-v6ops-variable-slaac-problem-stmt 
 > > +             * Contact: Dmytro Shytyi. 
 > > +             */ 
 > > +            memcpy(&addr, &pinfo->prefix, 16); 
 > > +            if (in6_dev->cnf.addr_gen_mode == IN6_ADDR_GEN_MODE_STABLE_PRIVACY) { 
 > > +                if (!ipv6_generate_address_variable_plen(&addr, 
 > > +                                     0, 
 > > +                                     in6_dev, 
 > > +                                     pinfo->prefix_len, 
 > > +                                     true)) { 
 > > +                    addr_flags |= IFA_F_STABLE_PRIVACY; 
 > > +                    goto ok; 
 > > +            } 
 > > +            } else if (!ipv6_generate_address_variable_plen(&addr, 
 > > +                                    0, 
 > > +                                    in6_dev, 
 > > +                                    pinfo->prefix_len, 
 > > +                                    false)) { 
 > > +                goto ok; 
 > > +            } 
 > > +        } else { 
 > > +            net_dbg_ratelimited("IPv6: Prefix with unexpected length %d\n", 
 > > +                        pinfo->prefix_len); 
 > >          } 
 > > -        net_dbg_ratelimited("IPv6 addrconf: prefix with wrong length %d\n", 
 > > -                    pinfo->prefix_len); 
 > >          goto put; 
 > > 
 > >  ok: 
 > > @@ -3263,6 +3335,77 @@ retry: 
 > >      *address = temp; 
 > >      return 0; 
 > >  } 
 > > + 
 > > +static int ipv6_generate_address_variable_plen(struct in6_addr *address, 
 > > +                           u8 dad_count, 
 > > +                           const struct inet6_dev *idev, 
 > > +                           unsigned int rcvd_prfx_len, 
 > > +                           bool stable_privacy_mode) 
 > > +{ 
 > > +    static DEFINE_SPINLOCK(lock); 
 > > +    static __u32 digest[SHA1_DIGEST_WORDS]; 
 > > +    static __u32 workspace[SHA1_WORKSPACE_WORDS]; 
 > > + 
 > > +    static union { 
 > > +        char __data[SHA1_BLOCK_SIZE]; 
 > > +        struct { 
 > > +            struct in6_addr secret; 
 > > +            __be32 prefix[2]; 
 > > +            unsigned char hwaddr[MAX_ADDR_LEN]; 
 > > +            u8 dad_count; 
 > > +        } __packed; 
 > > +    } data; 
 > > + 
 > > +    struct in6_addr secret; 
 > > +    struct in6_addr temp; 
 > > +    struct net *net = dev_net(idev->dev); 
 >  
 > Please dont add empty lines between variable declarations. 
   
Understood.

 > > +    BUILD_BUG_ON(sizeof(data.__data) != sizeof(data)); 
 > > + 
 > > +    if (stable_privacy_mode) { 
 > > +        if (idev->cnf.stable_secret.initialized) 
 > > +            secret = idev->cnf.stable_secret.secret; 
 > > +        else if (net->ipv6.devconf_dflt->stable_secret.initialized) 
 > > +            secret = net->ipv6.devconf_dflt->stable_secret.secret; 
 > > +        else 
 > > +            return -1; 
 > > +    } 
 > > + 
 > > +retry: 
 > > +    spin_lock_bh(&lock); 
 > > +    if (stable_privacy_mode) { 
 > > +        sha1_init(digest); 
 > > +        memset(&data, 0, sizeof(data)); 
 > > +        memset(workspace, 0, sizeof(workspace)); 
 > > +        memcpy(data.hwaddr, idev->dev->perm_addr, idev->dev->addr_len); 
 > > +        data.prefix[0] = address->s6_addr32[0]; 
 > > +        data.prefix[1] = address->s6_addr32[1]; 
 > > +        data.secret = secret; 
 > > +        data.dad_count = dad_count; 
 > > + 
 > > +        sha1_transform(digest, data.__data, workspace); 
 > > + 
 > > +        temp = *address; 
 > > +        temp.s6_addr32[0] = (__force __be32)digest[0]; 
 > > +        temp.s6_addr32[1] = (__force __be32)digest[1]; 
 > > +        temp.s6_addr32[2] = (__force __be32)digest[2]; 
 > > +        temp.s6_addr32[3] = (__force __be32)digest[3]; 
 > > +    } else { 
 > > +        temp = *address; 
 > > +        get_random_bytes(temp.s6_addr32, 16); 
 > > +    } 
 > > +    spin_unlock_bh(&lock); 
 > > + 
 > > +    if (ipv6_reserved_interfaceid(temp)) { 
 > > +        dad_count++; 
 > > +        if (dad_count > dev_net(idev->dev)->ipv6.sysctl.idgen_retries) 
 > > +            return -1; 
 > > +        goto retry; 
 > > +    } 
 > > +    ipv6_addr_prefix_copy(&temp, address, rcvd_prfx_len); 
 > > +    *address = temp; 
 > > +    return 0; 
 > > +} 
 > > 
 > >  static void ipv6_gen_mode_random_init(struct inet6_dev *idev) 
 > >  { 
 > > diff -rupN net-next-5.10.0-rc2/net/ipv6/ndisc.c net-next-patch-5.10.0-rc2/net/ipv6/ndisc.c 
 > > --- net-next-5.10.0-rc2/net/ipv6/ndisc.c    2020-11-10 08:46:01.091860289 +0100 
 > > +++ net-next-patch-5.10.0-rc2/net/ipv6/ndisc.c    2020-11-11 18:11:05.630883513 +0100 
 > > @@ -1244,6 +1244,8 @@ static void ndisc_router_discovery(struc 
 > >          in6_dev->if_flags |= IF_RA_RCVD; 
 > >      } 
 > > 
 > > +    in6_dev->if_flags |= ra_msg->icmph.icmp6_slaac_var_plen ? 
 > > +                    IF_RA_VAR_PLEN : 0; 
 > >      /* 
 > >       * Remember the managed/otherconf flags from most recently 
 > >       * received RA message (RFC 2462) -- yoshfuji 
 >  
 > 
Best,
Dmytro SHYTYI.

^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH net-next V5] net: Variable SLAAC: SLAAC with prefixes of arbitrary length in PIO
  2020-11-18 13:41             ` Dmytro Shytyi
@ 2020-11-18 15:50               ` Jakub Kicinski
  2020-11-18 15:56                 ` Dmytro Shytyi
  0 siblings, 1 reply; 49+ messages in thread
From: Jakub Kicinski @ 2020-11-18 15:50 UTC (permalink / raw)
  To: Dmytro Shytyi; +Cc: kuznet, yoshfuji, liuhangbin, davem, netdev, linux-kernel

On Wed, 18 Nov 2020 14:41:03 +0100 Dmytro Shytyi wrote:
>  > > @@ -2576,9 +2590,42 @@ int addrconf_prefix_rcv_add_addr(struct 
>  > >                   u32 addr_flags, bool sllao, bool tokenized, 
>  > >                   __u32 valid_lft, u32 prefered_lft) 
>  > >  { 
>  > > -    struct inet6_ifaddr *ifp = ipv6_get_ifaddr(net, addr, dev, 1); 
>  > > +    struct inet6_ifaddr *ifp = NULL; 
>  > >      int create = 0; 
>  > > 
>  > > +    if ((in6_dev->if_flags & IF_RA_VAR_PLEN) == IF_RA_VAR_PLEN && 
>  > > +        in6_dev->cnf.addr_gen_mode != IN6_ADDR_GEN_MODE_STABLE_PRIVACY) { 
>  > > +        struct inet6_ifaddr *result = NULL; 
>  > > +        struct inet6_ifaddr *result_base = NULL; 
>  > > +        struct in6_addr curr_net_prfx; 
>  > > +        struct in6_addr net_prfx; 
>  > > +        bool prfxs_equal; 
>  > > + 
>  > > +        result_base = result; 
>  > > +        rcu_read_lock(); 
>  > > +        list_for_each_entry_rcu(ifp, &in6_dev->addr_list, if_list) { 
>  > > +            if (!net_eq(dev_net(ifp->idev->dev), net)) 
>  > > +                continue; 
>  > > +            ipv6_addr_prefix_copy(&net_prfx, &pinfo->prefix, pinfo->prefix_len); 
>  > > +            ipv6_addr_prefix_copy(&curr_net_prfx, &ifp->addr, pinfo->prefix_len); 
>  > > +            prfxs_equal = 
>  > > +                ipv6_prefix_equal(&net_prfx, &curr_net_prfx, pinfo->prefix_len); 
>  > > + 
>  > > +            if (prfxs_equal && pinfo->prefix_len == ifp->prefix_len) { 
>  > > +                result = ifp; 
>  > > +                in6_ifa_hold(ifp); 
>  > > +                break; 
>  > > +            } 
>  > > +        } 
>  > > +        rcu_read_unlock(); 
>  > > +        if (result_base != result) 
>  > > +            ifp = result; 
>  > > +        else 
>  > > +            ifp = NULL;   
>  >  
>  > Could this be a helper of its own?   
> 
> Explain the thought please. It is not clear for me.

At the look of it the code under this if statement looks relatively
self-contained, and has quite a few local variables. Rather than making
the surrounding function longer would it be possible to factor it out
into a helper function?

^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH net-next V5] net: Variable SLAAC: SLAAC with prefixes of arbitrary length in PIO
  2020-11-18 15:50               ` Jakub Kicinski
@ 2020-11-18 15:56                 ` Dmytro Shytyi
  0 siblings, 0 replies; 49+ messages in thread
From: Dmytro Shytyi @ 2020-11-18 15:56 UTC (permalink / raw)
  To: Jakub Kicinski; +Cc: kuznet, yoshfuji, liuhangbin, davem, netdev, linux-kernel

Hello,

---- On Wed, 18 Nov 2020 16:50:27 +0100 Jakub Kicinski <kuba@kernel.org> wrote ----

 > On Wed, 18 Nov 2020 14:41:03 +0100 Dmytro Shytyi wrote: 
 > >  > > @@ -2576,9 +2590,42 @@ int addrconf_prefix_rcv_add_addr(struct 
 > >  > >                   u32 addr_flags, bool sllao, bool tokenized, 
 > >  > >                   __u32 valid_lft, u32 prefered_lft) 
 > >  > >  { 
 > >  > > -    struct inet6_ifaddr *ifp = ipv6_get_ifaddr(net, addr, dev, 1); 
 > >  > > +    struct inet6_ifaddr *ifp = NULL; 
 > >  > >      int create = 0; 
 > >  > > 
 > >  > > +    if ((in6_dev->if_flags & IF_RA_VAR_PLEN) == IF_RA_VAR_PLEN && 
 > >  > > +        in6_dev->cnf.addr_gen_mode != IN6_ADDR_GEN_MODE_STABLE_PRIVACY) { 
 > >  > > +        struct inet6_ifaddr *result = NULL; 
 > >  > > +        struct inet6_ifaddr *result_base = NULL; 
 > >  > > +        struct in6_addr curr_net_prfx; 
 > >  > > +        struct in6_addr net_prfx; 
 > >  > > +        bool prfxs_equal; 
 > >  > > + 
 > >  > > +        result_base = result; 
 > >  > > +        rcu_read_lock(); 
 > >  > > +        list_for_each_entry_rcu(ifp, &in6_dev->addr_list, if_list) { 
 > >  > > +            if (!net_eq(dev_net(ifp->idev->dev), net)) 
 > >  > > +                continue; 
 > >  > > +            ipv6_addr_prefix_copy(&net_prfx, &pinfo->prefix, pinfo->prefix_len); 
 > >  > > +            ipv6_addr_prefix_copy(&curr_net_prfx, &ifp->addr, pinfo->prefix_len); 
 > >  > > +            prfxs_equal = 
 > >  > > +                ipv6_prefix_equal(&net_prfx, &curr_net_prfx, pinfo->prefix_len); 
 > >  > > + 
 > >  > > +            if (prfxs_equal && pinfo->prefix_len == ifp->prefix_len) { 
 > >  > > +                result = ifp; 
 > >  > > +                in6_ifa_hold(ifp); 
 > >  > > +                break; 
 > >  > > +            } 
 > >  > > +        } 
 > >  > > +        rcu_read_unlock(); 
 > >  > > +        if (result_base != result) 
 > >  > > +            ifp = result; 
 > >  > > +        else 
 > >  > > +            ifp = NULL; 
 > >  > 
 > >  > Could this be a helper of its own? 
 > > 
 > > Explain the thought please. It is not clear for me. 
 >  
 > At the look of it the code under this if statement looks relatively 
 > self-contained, and has quite a few local variables. Rather than making 
 > the surrounding function longer would it be possible to factor it out 
 > into a helper function? 
 > 
Understood.
Thanks.

Best regards,
Dmytro SHYTYI.

^ permalink raw reply	[flat|nested] 49+ messages in thread

* [PATCH net-next V6] net: Variable SLAAC: SLAAC with prefixes of arbitrary length in PIO
  2020-11-17 20:43           ` Jakub Kicinski
  2020-11-18 13:41             ` Dmytro Shytyi
@ 2020-11-19 13:37             ` Dmytro Shytyi
  2020-11-19 18:44               ` Jakub Kicinski
  1 sibling, 1 reply; 49+ messages in thread
From: Dmytro Shytyi @ 2020-11-19 13:37 UTC (permalink / raw)
  To: Jakub Kicinski, yoshfuji, kuznet, liuhangbin, davem, netdev,
	linux-kernel

Variable SLAAC: SLAAC with prefixes of arbitrary length in PIO (randomly
generated hostID or stable privacy + privacy extensions).
The main problem is that SLAAC RA or PD allocates a /64 by the Wireless
carrier 4G, 5G to a mobile hotspot, however segmentation of the /64 via
SLAAC is required so that downstream interfaces can be further subnetted.
Example: uCPE device (4G + WI-FI enabled) receives /64 via Wireless, and
assigns /72 to VNF-Firewall, /72 to WIFI, /72 to VNF-Router, /72 to
Load-Balancer and /72 to wired connected devices.
IETF document that defines problem statement:
draft-mishra-v6ops-variable-slaac-problem-stmt
IETF document that specifies variable slaac:
draft-mishra-6man-variable-slaac

Signed-off-by: Dmytro Shytyi <dmytro@shytyi.net>
---
diff -rupN net-next-5.10.0-rc2/net/ipv6/addrconf.c net-next-patch-5.10.0-rc2/net/ipv6/addrconf.c
--- net-next-5.10.0-rc2/net/ipv6/addrconf.c	2020-11-10 08:46:01.075193379 +0100
+++ net-next-patch-5.10.0-rc2/net/ipv6/addrconf.c	2020-11-19 12:27:58.252392820 +0100
@@ -142,7 +142,6 @@ static int ipv6_count_addresses(const st
 static int ipv6_generate_stable_address(struct in6_addr *addr,
 					u8 dad_count,
 					const struct inet6_dev *idev);
-
 #define IN6_ADDR_HSIZE_SHIFT	8
 #define IN6_ADDR_HSIZE		(1 << IN6_ADDR_HSIZE_SHIFT)
 /*
@@ -1315,6 +1314,7 @@ static int ipv6_create_tempaddr(struct i
 	struct ifa6_config cfg;
 	long max_desync_factor;
 	struct in6_addr addr;
+	struct in6_addr temp;
 	int ret = 0;
 
 	write_lock_bh(&idev->lock);
@@ -1340,9 +1340,16 @@ retry:
 		goto out;
 	}
 	in6_ifa_hold(ifp);
-	memcpy(addr.s6_addr, ifp->addr.s6_addr, 8);
-	ipv6_gen_rnd_iid(&addr);
 
+	if (ifp->prefix_len == 64) {
+		memcpy(addr.s6_addr, ifp->addr.s6_addr, 8);
+		ipv6_gen_rnd_iid(&addr);
+	} else if (ifp->prefix_len > 0 && ifp->prefix_len <= 128) {
+		memcpy(addr.s6_addr32, ifp->addr.s6_addr, 16);
+		get_random_bytes(temp.s6_addr32, 16);
+		ipv6_addr_prefix_copy(&temp, &addr, ifp->prefix_len);
+		memcpy(addr.s6_addr, temp.s6_addr, 16);
+	}
 	age = (now - ifp->tstamp) / HZ;
 
 	regen_advance = idev->cnf.regen_max_retry *
@@ -2569,6 +2576,41 @@ static bool is_addr_mode_generate_stable
 	       idev->cnf.addr_gen_mode == IN6_ADDR_GEN_MODE_RANDOM;
 }
 
+struct inet6_ifaddr *ipv6_cmp_rcvd_prsnt_prfxs(struct inet6_ifaddr *ifp,
+					       struct inet6_dev *in6_dev,
+					       struct net *net,
+					       const struct prefix_info *pinfo)
+{
+	struct inet6_ifaddr *result_base = NULL;
+	struct inet6_ifaddr *result = NULL;
+	struct in6_addr curr_net_prfx;
+	struct in6_addr net_prfx;
+	bool prfxs_equal;
+
+	result_base = result;
+	rcu_read_lock();
+	list_for_each_entry_rcu(ifp, &in6_dev->addr_list, if_list) {
+		if (!net_eq(dev_net(ifp->idev->dev), net))
+			continue;
+		ipv6_addr_prefix_copy(&net_prfx, &pinfo->prefix, pinfo->prefix_len);
+		ipv6_addr_prefix_copy(&curr_net_prfx, &ifp->addr, pinfo->prefix_len);
+		prfxs_equal =
+			ipv6_prefix_equal(&net_prfx, &curr_net_prfx, pinfo->prefix_len);
+		if (prfxs_equal && pinfo->prefix_len == ifp->prefix_len) {
+			result = ifp;
+			in6_ifa_hold(ifp);
+			break;
+		}
+	}
+	rcu_read_unlock();
+	if (result_base != result)
+		ifp = result;
+	else
+		ifp = NULL;
+
+	return ifp;
+}
+
 int addrconf_prefix_rcv_add_addr(struct net *net, struct net_device *dev,
 				 const struct prefix_info *pinfo,
 				 struct inet6_dev *in6_dev,
@@ -2576,9 +2618,17 @@ int addrconf_prefix_rcv_add_addr(struct
 				 u32 addr_flags, bool sllao, bool tokenized,
 				 __u32 valid_lft, u32 prefered_lft)
 {
-	struct inet6_ifaddr *ifp = ipv6_get_ifaddr(net, addr, dev, 1);
+	struct inet6_ifaddr *ifp = NULL;
+	int plen = pinfo->prefix_len;
 	int create = 0;
 
+	if (plen > 0 && plen <= 128 && plen != 64 &&
+	    in6_dev->cnf.addr_gen_mode != IN6_ADDR_GEN_MODE_STABLE_PRIVACY) {
+		ifp = ipv6_cmp_rcvd_prsnt_prfxs(ifp, in6_dev, net, pinfo);
+	} else if (plen == 64) {
+		ifp = ipv6_get_ifaddr(net, addr, dev, 1);
+	}
+
 	if (!ifp && valid_lft) {
 		int max_addresses = in6_dev->cnf.max_addresses;
 		struct ifa6_config cfg = {
@@ -2657,6 +2707,91 @@ int addrconf_prefix_rcv_add_addr(struct
 }
 EXPORT_SYMBOL_GPL(addrconf_prefix_rcv_add_addr);
 
+static bool ipv6_reserved_interfaceid(struct in6_addr address)
+{
+	if ((address.s6_addr32[2] | address.s6_addr32[3]) == 0)
+		return true;
+
+	if (address.s6_addr32[2] == htonl(0x02005eff) &&
+	    ((address.s6_addr32[3] & htonl(0xfe000000)) == htonl(0xfe000000)))
+		return true;
+
+	if (address.s6_addr32[2] == htonl(0xfdffffff) &&
+	    ((address.s6_addr32[3] & htonl(0xffffff80)) == htonl(0xffffff80)))
+		return true;
+
+	return false;
+}
+
+static int ipv6_gen_addr_var_plen(struct in6_addr *address,
+				  u8 dad_count,
+				  const struct inet6_dev *idev,
+				  unsigned int rcvd_prfx_len,
+				  bool stable_privacy_mode)
+{
+	static union {
+		char __data[SHA1_BLOCK_SIZE];
+		struct {
+			struct in6_addr secret;
+			__be32 prefix[2];
+			unsigned char hwaddr[MAX_ADDR_LEN];
+			u8 dad_count;
+		} __packed;
+	} data;
+	static __u32 workspace[SHA1_WORKSPACE_WORDS];
+	static __u32 digest[SHA1_DIGEST_WORDS];
+	struct net *net = dev_net(idev->dev);
+	static DEFINE_SPINLOCK(lock);
+	struct in6_addr secret;
+	struct in6_addr temp;
+
+	BUILD_BUG_ON(sizeof(data.__data) != sizeof(data));
+
+	if (stable_privacy_mode) {
+		if (idev->cnf.stable_secret.initialized)
+			secret = idev->cnf.stable_secret.secret;
+		else if (net->ipv6.devconf_dflt->stable_secret.initialized)
+			secret = net->ipv6.devconf_dflt->stable_secret.secret;
+		else
+			return -1;
+	}
+
+retry:
+	spin_lock_bh(&lock);
+	if (stable_privacy_mode) {
+		sha1_init(digest);
+		memset(&data, 0, sizeof(data));
+		memset(workspace, 0, sizeof(workspace));
+		memcpy(data.hwaddr, idev->dev->perm_addr, idev->dev->addr_len);
+		data.prefix[0] = address->s6_addr32[0];
+		data.prefix[1] = address->s6_addr32[1];
+		data.secret = secret;
+		data.dad_count = dad_count;
+
+		sha1_transform(digest, data.__data, workspace);
+
+		temp = *address;
+		temp.s6_addr32[0] = (__force __be32)digest[0];
+		temp.s6_addr32[1] = (__force __be32)digest[1];
+		temp.s6_addr32[2] = (__force __be32)digest[2];
+		temp.s6_addr32[3] = (__force __be32)digest[3];
+	} else {
+		temp = *address;
+		get_random_bytes(temp.s6_addr32, 16);
+	}
+	spin_unlock_bh(&lock);
+
+	if (ipv6_reserved_interfaceid(temp)) {
+		dad_count++;
+		if (dad_count > dev_net(idev->dev)->ipv6.sysctl.idgen_retries)
+			return -1;
+		goto retry;
+	}
+	ipv6_addr_prefix_copy(&temp, address, rcvd_prfx_len);
+	*address = temp;
+	return 0;
+}
+
 void addrconf_prefix_rcv(struct net_device *dev, u8 *opt, int len, bool sllao)
 {
 	struct prefix_info *pinfo;
@@ -2781,9 +2916,33 @@ void addrconf_prefix_rcv(struct net_devi
 				dev_addr_generated = true;
 			}
 			goto ok;
+		} else if (pinfo->prefix_len != 64 &&
+			   pinfo->prefix_len > 0 && pinfo->prefix_len <= 128) {
+			/* SLAAC with prefixes of arbitrary length (Variable SLAAC).
+			 * draft-mishra-6man-variable-slaac
+			 * draft-mishra-v6ops-variable-slaac-problem-stmt
+			 */
+			memcpy(&addr, &pinfo->prefix, 16);
+			if (in6_dev->cnf.addr_gen_mode == IN6_ADDR_GEN_MODE_STABLE_PRIVACY) {
+				if (!ipv6_gen_addr_var_plen(&addr,
+							    0,
+							    in6_dev,
+							    pinfo->prefix_len,
+							    true)) {
+					addr_flags |= IFA_F_STABLE_PRIVACY;
+					goto ok;
+				}
+			} else if (!ipv6_gen_addr_var_plen(&addr,
+							   0,
+							   in6_dev,
+							   pinfo->prefix_len,
+							   false)) {
+				goto ok;
+			}
+		} else {
+			net_dbg_ratelimited("IPv6: Prefix with unexpected length %d\n",
+					    pinfo->prefix_len);
 		}
-		net_dbg_ratelimited("IPv6 addrconf: prefix with wrong length %d\n",
-				    pinfo->prefix_len);
 		goto put;
 
 ok:
@@ -3186,22 +3345,6 @@ void addrconf_add_linklocal(struct inet6
 }
 EXPORT_SYMBOL_GPL(addrconf_add_linklocal);
 
-static bool ipv6_reserved_interfaceid(struct in6_addr address)
-{
-	if ((address.s6_addr32[2] | address.s6_addr32[3]) == 0)
-		return true;
-
-	if (address.s6_addr32[2] == htonl(0x02005eff) &&
-	    ((address.s6_addr32[3] & htonl(0xfe000000)) == htonl(0xfe000000)))
-		return true;
-
-	if (address.s6_addr32[2] == htonl(0xfdffffff) &&
-	    ((address.s6_addr32[3] & htonl(0xffffff80)) == htonl(0xffffff80)))
-		return true;
-
-	return false;
-}
-
 static int ipv6_generate_stable_address(struct in6_addr *address,
 					u8 dad_count,
 					const struct inet6_dev *idev)

^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH net-next V6] net: Variable SLAAC: SLAAC with prefixes of arbitrary length in PIO
  2020-11-19 13:37             ` [PATCH net-next V6] " Dmytro Shytyi
@ 2020-11-19 18:44               ` Jakub Kicinski
  2020-11-19 19:31                 ` Dmytro Shytyi
  0 siblings, 1 reply; 49+ messages in thread
From: Jakub Kicinski @ 2020-11-19 18:44 UTC (permalink / raw)
  To: Dmytro Shytyi; +Cc: yoshfuji, kuznet, liuhangbin, davem, netdev, linux-kernel

On Thu, 19 Nov 2020 14:37:35 +0100 Dmytro Shytyi wrote:
> +struct inet6_ifaddr *ipv6_cmp_rcvd_prsnt_prfxs(struct inet6_ifaddr *ifp,
> +					       struct inet6_dev *in6_dev,
> +					       struct net *net,
> +					       const struct prefix_info *pinfo)
> +{
> +	struct inet6_ifaddr *result_base = NULL;
> +	struct inet6_ifaddr *result = NULL;
> +	struct in6_addr curr_net_prfx;
> +	struct in6_addr net_prfx;
> +	bool prfxs_equal;
> +
> +	result_base = result;
> +	rcu_read_lock();
> +	list_for_each_entry_rcu(ifp, &in6_dev->addr_list, if_list) {
> +		if (!net_eq(dev_net(ifp->idev->dev), net))
> +			continue;
> +		ipv6_addr_prefix_copy(&net_prfx, &pinfo->prefix, pinfo->prefix_len);
> +		ipv6_addr_prefix_copy(&curr_net_prfx, &ifp->addr, pinfo->prefix_len);
> +		prfxs_equal =
> +			ipv6_prefix_equal(&net_prfx, &curr_net_prfx, pinfo->prefix_len);
> +		if (prfxs_equal && pinfo->prefix_len == ifp->prefix_len) {
> +			result = ifp;
> +			in6_ifa_hold(ifp);
> +			break;
> +		}
> +	}
> +	rcu_read_unlock();
> +	if (result_base != result)
> +		ifp = result;
> +	else
> +		ifp = NULL;
> +
> +	return ifp;
> +}

Thanks for adding the helper! Looks like it needs a touch up:

net/ipv6/addrconf.c:2579:22: warning: no previous prototype for ‘ipv6_cmp_rcvd_prsnt_prfxs’ [-Wmissing-prototypes]
 2579 | struct inet6_ifaddr *ipv6_cmp_rcvd_prsnt_prfxs(struct inet6_ifaddr *ifp,
      |                      ^~~~~~~~~~~~~~~~~~~~~~~~~
net/ipv6/addrconf.c:2579:21: warning: symbol 'ipv6_cmp_rcvd_prsnt_prfxs' was not declared. Should it be static?

^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH net-next V6] net: Variable SLAAC: SLAAC with prefixes of arbitrary length in PIO
  2020-11-19 18:44               ` Jakub Kicinski
@ 2020-11-19 19:31                 ` Dmytro Shytyi
  2020-11-20  1:20                   ` Jakub Kicinski
  2020-11-20  9:26                   ` [PATCH net-next V7] " Dmytro Shytyi
  0 siblings, 2 replies; 49+ messages in thread
From: Dmytro Shytyi @ 2020-11-19 19:31 UTC (permalink / raw)
  To: Jakub Kicinski; +Cc: yoshfuji, kuznet, liuhangbin, davem, netdev, linux-kernel


---- On Thu, 19 Nov 2020 19:44:13 +0100 Jakub Kicinski <kuba@kernel.org> wrote ----

 > On Thu, 19 Nov 2020 14:37:35 +0100 Dmytro Shytyi wrote: 
 > > +struct inet6_ifaddr *ipv6_cmp_rcvd_prsnt_prfxs(struct inet6_ifaddr *ifp, 
 > > +                           struct inet6_dev *in6_dev, 
 > > +                           struct net *net, 
 > > +                           const struct prefix_info *pinfo) 
 > > +{ 
 > > +    struct inet6_ifaddr *result_base = NULL; 
 > > +    struct inet6_ifaddr *result = NULL; 
 > > +    struct in6_addr curr_net_prfx; 
 > > +    struct in6_addr net_prfx; 
 > > +    bool prfxs_equal; 
 > > + 
 > > +    result_base = result; 
 > > +    rcu_read_lock(); 
 > > +    list_for_each_entry_rcu(ifp, &in6_dev->addr_list, if_list) { 
 > > +        if (!net_eq(dev_net(ifp->idev->dev), net)) 
 > > +            continue; 
 > > +        ipv6_addr_prefix_copy(&net_prfx, &pinfo->prefix, pinfo->prefix_len); 
 > > +        ipv6_addr_prefix_copy(&curr_net_prfx, &ifp->addr, pinfo->prefix_len); 
 > > +        prfxs_equal = 
 > > +            ipv6_prefix_equal(&net_prfx, &curr_net_prfx, pinfo->prefix_len); 
 > > +        if (prfxs_equal && pinfo->prefix_len == ifp->prefix_len) { 
 > > +            result = ifp; 
 > > +            in6_ifa_hold(ifp); 
 > > +            break; 
 > > +        } 
 > > +    } 
 > > +    rcu_read_unlock(); 
 > > +    if (result_base != result) 
 > > +        ifp = result; 
 > > +    else 
 > > +        ifp = NULL; 
 > > + 
 > > +    return ifp; 
 > > +} 
 >  
 > Thanks for adding the helper! Looks like it needs a touch up: 
 
Understood. Thank you for pointing this out. I think I did not catch this warning as my Makefile didn't include "-Wmissing-prototypes"

 > net/ipv6/addrconf.c:2579:22: warning: no previous prototype for ‘ipv6_cmp_rcvd_prsnt_prfxs’ [-Wmissing-prototypes] 
 >  2579 | struct inet6_ifaddr *ipv6_cmp_rcvd_prsnt_prfxs(struct inet6_ifaddr *ifp, 
 >  |                      ^~~~~~~~~~~~~~~~~~~~~~~~~ 
 > net/ipv6/addrconf.c:2579:21: warning: symbol 'ipv6_cmp_rcvd_prsnt_prfxs' was not declared. Should it be static? 
 > 

Hideaki Yoshifuji helped to improve this patch with suggestions. @Hideaki, should I add "Reported-by" tag in this case?
Jakub Kicinski also helped to find errors and help with improvement. @Jakub, should I add "Reported-by" tag in this case? 

Thanks,
Dmytro SHYTYI


^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH net-next V6] net: Variable SLAAC: SLAAC with prefixes of arbitrary length in PIO
  2020-11-19 19:31                 ` Dmytro Shytyi
@ 2020-11-20  1:20                   ` Jakub Kicinski
  2020-11-20  9:26                   ` [PATCH net-next V7] " Dmytro Shytyi
  1 sibling, 0 replies; 49+ messages in thread
From: Jakub Kicinski @ 2020-11-20  1:20 UTC (permalink / raw)
  To: Dmytro Shytyi; +Cc: yoshfuji, kuznet, liuhangbin, davem, netdev, linux-kernel

On Thu, 19 Nov 2020 20:31:41 +0100 Dmytro Shytyi wrote:
>  > Thanks for adding the helper! Looks like it needs a touch up:   
>  
> Understood. Thank you for pointing this out. I think I did not catch this warning as my Makefile didn't include "-Wmissing-prototypes"
> 
>  > net/ipv6/addrconf.c:2579:22: warning: no previous prototype for ‘ipv6_cmp_rcvd_prsnt_prfxs’ [-Wmissing-prototypes] 
>  >  2579 | struct inet6_ifaddr *ipv6_cmp_rcvd_prsnt_prfxs(struct inet6_ifaddr *ifp, 
>  >  |                      ^~~~~~~~~~~~~~~~~~~~~~~~~ 
>  > net/ipv6/addrconf.c:2579:21: warning: symbol 'ipv6_cmp_rcvd_prsnt_prfxs' was not declared. Should it be static? 
>  >   
> 
> Hideaki Yoshifuji helped to improve this patch with suggestions. @Hideaki, should I add "Reported-by" tag in this case?
> Jakub Kicinski also helped to find errors and help with improvement. @Jakub, should I add "Reported-by" tag in this case? 

No need for a tag for me, it would be great if Hideaki was
willing to provide his acked-by or reviewed-by though :)

^ permalink raw reply	[flat|nested] 49+ messages in thread

* [PATCH net-next V7] net: Variable SLAAC: SLAAC with prefixes of arbitrary length in PIO
  2020-11-19 19:31                 ` Dmytro Shytyi
  2020-11-20  1:20                   ` Jakub Kicinski
@ 2020-11-20  9:26                   ` Dmytro Shytyi
  2020-11-23 13:26                     ` Hideaki Yoshifuji
  2020-12-09  3:27                     ` [PATCH net-next V8] " Dmytro Shytyi
  1 sibling, 2 replies; 49+ messages in thread
From: Dmytro Shytyi @ 2020-11-20  9:26 UTC (permalink / raw)
  To: Jakub Kicinski, yoshfuji, kuznet, liuhangbin, davem, netdev,
	linux-kernel

Variable SLAAC: SLAAC with prefixes of arbitrary length in PIO (randomly
generated hostID or stable privacy + privacy extensions).
The main problem is that SLAAC RA or PD allocates a /64 by the Wireless
carrier 4G, 5G to a mobile hotspot, however segmentation of the /64 via
SLAAC is required so that downstream interfaces can be further subnetted.
Example: uCPE device (4G + WI-FI enabled) receives /64 via Wireless, and
assigns /72 to VNF-Firewall, /72 to WIFI, /72 to VNF-Router, /72 to
Load-Balancer and /72 to wired connected devices.
IETF document that defines problem statement:
draft-mishra-v6ops-variable-slaac-problem-stmt
IETF document that specifies variable slaac:
draft-mishra-6man-variable-slaac

Signed-off-by: Dmytro Shytyi <dmytro@shytyi.net>
---
diff -rupN net-next-5.10.0-rc2/net/ipv6/addrconf.c net-next-patch-5.10.0-rc2/net/ipv6/addrconf.c
--- net-next-5.10.0-rc2/net/ipv6/addrconf.c	2020-11-10 08:46:01.075193379 +0100
+++ net-next-patch-5.10.0-rc2/net/ipv6/addrconf.c	2020-11-19 21:26:39.770872898 +0100
@@ -142,7 +142,6 @@ static int ipv6_count_addresses(const st
 static int ipv6_generate_stable_address(struct in6_addr *addr,
 					u8 dad_count,
 					const struct inet6_dev *idev);
-
 #define IN6_ADDR_HSIZE_SHIFT	8
 #define IN6_ADDR_HSIZE		(1 << IN6_ADDR_HSIZE_SHIFT)
 /*
@@ -1315,6 +1314,7 @@ static int ipv6_create_tempaddr(struct i
 	struct ifa6_config cfg;
 	long max_desync_factor;
 	struct in6_addr addr;
+	struct in6_addr temp;
 	int ret = 0;
 
 	write_lock_bh(&idev->lock);
@@ -1340,9 +1340,16 @@ retry:
 		goto out;
 	}
 	in6_ifa_hold(ifp);
-	memcpy(addr.s6_addr, ifp->addr.s6_addr, 8);
-	ipv6_gen_rnd_iid(&addr);
 
+	if (ifp->prefix_len == 64) {
+		memcpy(addr.s6_addr, ifp->addr.s6_addr, 8);
+		ipv6_gen_rnd_iid(&addr);
+	} else if (ifp->prefix_len > 0 && ifp->prefix_len <= 128) {
+		memcpy(addr.s6_addr32, ifp->addr.s6_addr, 16);
+		get_random_bytes(temp.s6_addr32, 16);
+		ipv6_addr_prefix_copy(&temp, &addr, ifp->prefix_len);
+		memcpy(addr.s6_addr, temp.s6_addr, 16);
+	}
 	age = (now - ifp->tstamp) / HZ;
 
 	regen_advance = idev->cnf.regen_max_retry *
@@ -2569,6 +2576,41 @@ static bool is_addr_mode_generate_stable
 	       idev->cnf.addr_gen_mode == IN6_ADDR_GEN_MODE_RANDOM;
 }
 
+static struct inet6_ifaddr *ipv6_cmp_rcvd_prsnt_prfxs(struct inet6_ifaddr *ifp,
+						      struct inet6_dev *in6_dev,
+						      struct net *net,
+						      const struct prefix_info *pinfo)
+{
+	struct inet6_ifaddr *result_base = NULL;
+	struct inet6_ifaddr *result = NULL;
+	struct in6_addr curr_net_prfx;
+	struct in6_addr net_prfx;
+	bool prfxs_equal;
+
+	result_base = result;
+	rcu_read_lock();
+	list_for_each_entry_rcu(ifp, &in6_dev->addr_list, if_list) {
+		if (!net_eq(dev_net(ifp->idev->dev), net))
+			continue;
+		ipv6_addr_prefix_copy(&net_prfx, &pinfo->prefix, pinfo->prefix_len);
+		ipv6_addr_prefix_copy(&curr_net_prfx, &ifp->addr, pinfo->prefix_len);
+		prfxs_equal =
+			ipv6_prefix_equal(&net_prfx, &curr_net_prfx, pinfo->prefix_len);
+		if (prfxs_equal && pinfo->prefix_len == ifp->prefix_len) {
+			result = ifp;
+			in6_ifa_hold(ifp);
+			break;
+		}
+	}
+	rcu_read_unlock();
+	if (result_base != result)
+		ifp = result;
+	else
+		ifp = NULL;
+
+	return ifp;
+}
+
 int addrconf_prefix_rcv_add_addr(struct net *net, struct net_device *dev,
 				 const struct prefix_info *pinfo,
 				 struct inet6_dev *in6_dev,
@@ -2576,9 +2618,16 @@ int addrconf_prefix_rcv_add_addr(struct
 				 u32 addr_flags, bool sllao, bool tokenized,
 				 __u32 valid_lft, u32 prefered_lft)
 {
-	struct inet6_ifaddr *ifp = ipv6_get_ifaddr(net, addr, dev, 1);
+	struct inet6_ifaddr *ifp = NULL;
+	int plen = pinfo->prefix_len;
 	int create = 0;
 
+	if (plen > 0 && plen <= 128 && plen != 64 &&
+	    in6_dev->cnf.addr_gen_mode != IN6_ADDR_GEN_MODE_STABLE_PRIVACY)
+		ifp = ipv6_cmp_rcvd_prsnt_prfxs(ifp, in6_dev, net, pinfo);
+	else
+		ifp = ipv6_get_ifaddr(net, addr, dev, 1);
+
 	if (!ifp && valid_lft) {
 		int max_addresses = in6_dev->cnf.max_addresses;
 		struct ifa6_config cfg = {
@@ -2657,6 +2706,91 @@ int addrconf_prefix_rcv_add_addr(struct
 }
 EXPORT_SYMBOL_GPL(addrconf_prefix_rcv_add_addr);
 
+static bool ipv6_reserved_interfaceid(struct in6_addr address)
+{
+	if ((address.s6_addr32[2] | address.s6_addr32[3]) == 0)
+		return true;
+
+	if (address.s6_addr32[2] == htonl(0x02005eff) &&
+	    ((address.s6_addr32[3] & htonl(0xfe000000)) == htonl(0xfe000000)))
+		return true;
+
+	if (address.s6_addr32[2] == htonl(0xfdffffff) &&
+	    ((address.s6_addr32[3] & htonl(0xffffff80)) == htonl(0xffffff80)))
+		return true;
+
+	return false;
+}
+
+static int ipv6_gen_addr_var_plen(struct in6_addr *address,
+				  u8 dad_count,
+				  const struct inet6_dev *idev,
+				  unsigned int rcvd_prfx_len,
+				  bool stable_privacy_mode)
+{
+	static union {
+		char __data[SHA1_BLOCK_SIZE];
+		struct {
+			struct in6_addr secret;
+			__be32 prefix[2];
+			unsigned char hwaddr[MAX_ADDR_LEN];
+			u8 dad_count;
+		} __packed;
+	} data;
+	static __u32 workspace[SHA1_WORKSPACE_WORDS];
+	static __u32 digest[SHA1_DIGEST_WORDS];
+	struct net *net = dev_net(idev->dev);
+	static DEFINE_SPINLOCK(lock);
+	struct in6_addr secret;
+	struct in6_addr temp;
+
+	BUILD_BUG_ON(sizeof(data.__data) != sizeof(data));
+
+	if (stable_privacy_mode) {
+		if (idev->cnf.stable_secret.initialized)
+			secret = idev->cnf.stable_secret.secret;
+		else if (net->ipv6.devconf_dflt->stable_secret.initialized)
+			secret = net->ipv6.devconf_dflt->stable_secret.secret;
+		else
+			return -1;
+	}
+
+retry:
+	spin_lock_bh(&lock);
+	if (stable_privacy_mode) {
+		sha1_init(digest);
+		memset(&data, 0, sizeof(data));
+		memset(workspace, 0, sizeof(workspace));
+		memcpy(data.hwaddr, idev->dev->perm_addr, idev->dev->addr_len);
+		data.prefix[0] = address->s6_addr32[0];
+		data.prefix[1] = address->s6_addr32[1];
+		data.secret = secret;
+		data.dad_count = dad_count;
+
+		sha1_transform(digest, data.__data, workspace);
+
+		temp = *address;
+		temp.s6_addr32[0] = (__force __be32)digest[0];
+		temp.s6_addr32[1] = (__force __be32)digest[1];
+		temp.s6_addr32[2] = (__force __be32)digest[2];
+		temp.s6_addr32[3] = (__force __be32)digest[3];
+	} else {
+		temp = *address;
+		get_random_bytes(temp.s6_addr32, 16);
+	}
+	spin_unlock_bh(&lock);
+
+	if (ipv6_reserved_interfaceid(temp)) {
+		dad_count++;
+		if (dad_count > dev_net(idev->dev)->ipv6.sysctl.idgen_retries)
+			return -1;
+		goto retry;
+	}
+	ipv6_addr_prefix_copy(&temp, address, rcvd_prfx_len);
+	*address = temp;
+	return 0;
+}
+
 void addrconf_prefix_rcv(struct net_device *dev, u8 *opt, int len, bool sllao)
 {
 	struct prefix_info *pinfo;
@@ -2781,9 +2915,33 @@ void addrconf_prefix_rcv(struct net_devi
 				dev_addr_generated = true;
 			}
 			goto ok;
+		} else if (pinfo->prefix_len != 64 &&
+			   pinfo->prefix_len > 0 && pinfo->prefix_len <= 128) {
+			/* SLAAC with prefixes of arbitrary length (Variable SLAAC).
+			 * draft-mishra-6man-variable-slaac
+			 * draft-mishra-v6ops-variable-slaac-problem-stmt
+			 */
+			memcpy(&addr, &pinfo->prefix, 16);
+			if (in6_dev->cnf.addr_gen_mode == IN6_ADDR_GEN_MODE_STABLE_PRIVACY) {
+				if (!ipv6_gen_addr_var_plen(&addr,
+							    0,
+							    in6_dev,
+							    pinfo->prefix_len,
+							    true)) {
+					addr_flags |= IFA_F_STABLE_PRIVACY;
+					goto ok;
+				}
+			} else if (!ipv6_gen_addr_var_plen(&addr,
+							   0,
+							   in6_dev,
+							   pinfo->prefix_len,
+							   false)) {
+				goto ok;
+			}
+		} else {
+			net_dbg_ratelimited("IPv6: Prefix with unexpected length %d\n",
+					    pinfo->prefix_len);
 		}
-		net_dbg_ratelimited("IPv6 addrconf: prefix with wrong length %d\n",
-				    pinfo->prefix_len);
 		goto put;
 
 ok:
@@ -3186,22 +3344,6 @@ void addrconf_add_linklocal(struct inet6
 }
 EXPORT_SYMBOL_GPL(addrconf_add_linklocal);
 
-static bool ipv6_reserved_interfaceid(struct in6_addr address)
-{
-	if ((address.s6_addr32[2] | address.s6_addr32[3]) == 0)
-		return true;
-
-	if (address.s6_addr32[2] == htonl(0x02005eff) &&
-	    ((address.s6_addr32[3] & htonl(0xfe000000)) == htonl(0xfe000000)))
-		return true;
-
-	if (address.s6_addr32[2] == htonl(0xfdffffff) &&
-	    ((address.s6_addr32[3] & htonl(0xffffff80)) == htonl(0xffffff80)))
-		return true;
-
-	return false;
-}
-
 static int ipv6_generate_stable_address(struct in6_addr *address,
 					u8 dad_count,
 					const struct inet6_dev *idev)

^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH net-next V7] net: Variable SLAAC: SLAAC with prefixes of arbitrary length in PIO
  2020-11-20  9:26                   ` [PATCH net-next V7] " Dmytro Shytyi
@ 2020-11-23 13:26                     ` Hideaki Yoshifuji
  2020-11-27 16:54                       ` Dmytro Shytyi
  2020-12-09  3:27                     ` [PATCH net-next V8] " Dmytro Shytyi
  1 sibling, 1 reply; 49+ messages in thread
From: Hideaki Yoshifuji @ 2020-11-23 13:26 UTC (permalink / raw)
  To: Dmytro Shytyi
  Cc: Jakub Kicinski, yoshfuji, kuznet, liuhangbin, davem, netdev,
	linux-kernel, Hideaki Yoshifuji

Hi,

2020年11月20日(金) 18:28 Dmytro Shytyi <dmytro@shytyi.net>:
>
> Variable SLAAC: SLAAC with prefixes of arbitrary length in PIO (randomly
> generated hostID or stable privacy + privacy extensions).
> The main problem is that SLAAC RA or PD allocates a /64 by the Wireless
> carrier 4G, 5G to a mobile hotspot, however segmentation of the /64 via
> SLAAC is required so that downstream interfaces can be further subnetted.
> Example: uCPE device (4G + WI-FI enabled) receives /64 via Wireless, and
> assigns /72 to VNF-Firewall, /72 to WIFI, /72 to VNF-Router, /72 to
> Load-Balancer and /72 to wired connected devices.
> IETF document that defines problem statement:
> draft-mishra-v6ops-variable-slaac-problem-stmt
> IETF document that specifies variable slaac:
> draft-mishra-6man-variable-slaac
>
> Signed-off-by: Dmytro Shytyi <dmytro@shytyi.net>
> ---
> diff -rupN net-next-5.10.0-rc2/net/ipv6/addrconf.c net-next-patch-5.10.0-rc2/net/ipv6/addrconf.c
> --- net-next-5.10.0-rc2/net/ipv6/addrconf.c     2020-11-10 08:46:01.075193379 +0100
> +++ net-next-patch-5.10.0-rc2/net/ipv6/addrconf.c       2020-11-19 21:26:39.770872898 +0100
> @@ -142,7 +142,6 @@ static int ipv6_count_addresses(const st
>  static int ipv6_generate_stable_address(struct in6_addr *addr,
>                                         u8 dad_count,
>                                         const struct inet6_dev *idev);
> -

Do not remove this line.
>  #define IN6_ADDR_HSIZE_SHIFT   8
>  #define IN6_ADDR_HSIZE         (1 << IN6_ADDR_HSIZE_SHIFT)
>  /*
> @@ -1315,6 +1314,7 @@ static int ipv6_create_tempaddr(struct i
>         struct ifa6_config cfg;
>         long max_desync_factor;
>         struct in6_addr addr;
> +       struct in6_addr temp;
>         int ret = 0;
>
>         write_lock_bh(&idev->lock);
> @@ -1340,9 +1340,16 @@ retry:
>                 goto out;
>         }
>         in6_ifa_hold(ifp);
> -       memcpy(addr.s6_addr, ifp->addr.s6_addr, 8);
> -       ipv6_gen_rnd_iid(&addr);
>
> +       if (ifp->prefix_len == 64) {
> +               memcpy(addr.s6_addr, ifp->addr.s6_addr, 8);
> +               ipv6_gen_rnd_iid(&addr);
> +       } else if (ifp->prefix_len > 0 && ifp->prefix_len <= 128) {
> +               memcpy(addr.s6_addr32, ifp->addr.s6_addr, 16);
> +               get_random_bytes(temp.s6_addr32, 16);
> +               ipv6_addr_prefix_copy(&temp, &addr, ifp->prefix_len);
> +               memcpy(addr.s6_addr, temp.s6_addr, 16);
> +       }

I do not understand why you are copying many times.
ipv6_addr_copy(), get_random_bytes(), and then ipv6_addr_prefix_copy
is enough, no?

>         age = (now - ifp->tstamp) / HZ;
>
>         regen_advance = idev->cnf.regen_max_retry *
> @@ -2569,6 +2576,41 @@ static bool is_addr_mode_generate_stable
>                idev->cnf.addr_gen_mode == IN6_ADDR_GEN_MODE_RANDOM;
>  }
>
> +static struct inet6_ifaddr *ipv6_cmp_rcvd_prsnt_prfxs(struct inet6_ifaddr *ifp,
> +                                                     struct inet6_dev *in6_dev,
> +                                                     struct net *net,
> +                                                     const struct prefix_info *pinfo)
> +{
> +       struct inet6_ifaddr *result_base = NULL;
> +       struct inet6_ifaddr *result = NULL;
> +       struct in6_addr curr_net_prfx;
> +       struct in6_addr net_prfx;
> +       bool prfxs_equal;
> +
> +       result_base = result;
> +       rcu_read_lock();
> +       list_for_each_entry_rcu(ifp, &in6_dev->addr_list, if_list) {
> +               if (!net_eq(dev_net(ifp->idev->dev), net))
> +                       continue;
> +               ipv6_addr_prefix_copy(&net_prfx, &pinfo->prefix, pinfo->prefix_len);
> +               ipv6_addr_prefix_copy(&curr_net_prfx, &ifp->addr, pinfo->prefix_len);
> +               prfxs_equal =
> +                       ipv6_prefix_equal(&net_prfx, &curr_net_prfx, pinfo->prefix_len);
> +               if (prfxs_equal && pinfo->prefix_len == ifp->prefix_len) {
> +                       result = ifp;
> +                       in6_ifa_hold(ifp);
> +                       break;
> +               }

I guess we can compare them with ipv6_prefix_equal()
directly because the code assumes "pinfo->prefix_len" and ifp->prefix_len are
equal.

> +       }
> +       rcu_read_unlock();
> +       if (result_base != result)
> +               ifp = result;
> +       else
> +               ifp = NULL;
> +
> +       return ifp;
> +}
> +
>  int addrconf_prefix_rcv_add_addr(struct net *net, struct net_device *dev,
>                                  const struct prefix_info *pinfo,
>                                  struct inet6_dev *in6_dev,
> @@ -2576,9 +2618,16 @@ int addrconf_prefix_rcv_add_addr(struct
>                                  u32 addr_flags, bool sllao, bool tokenized,
>                                  __u32 valid_lft, u32 prefered_lft)
>  {
> -       struct inet6_ifaddr *ifp = ipv6_get_ifaddr(net, addr, dev, 1);
> +       struct inet6_ifaddr *ifp = NULL;
> +       int plen = pinfo->prefix_len;
>         int create = 0;
>
> +       if (plen > 0 && plen <= 128 && plen != 64 &&
> +           in6_dev->cnf.addr_gen_mode != IN6_ADDR_GEN_MODE_STABLE_PRIVACY)
> +               ifp = ipv6_cmp_rcvd_prsnt_prfxs(ifp, in6_dev, net, pinfo);
> +       else
> +               ifp = ipv6_get_ifaddr(net, addr, dev, 1);
> +
>         if (!ifp && valid_lft) {
>                 int max_addresses = in6_dev->cnf.max_addresses;
>                 struct ifa6_config cfg = {

I am wondering if we should enable this feature by default at this moment
because the spec is personal internet draft and some test suites might
consider this feature violates standards.

> @@ -2657,6 +2706,91 @@ int addrconf_prefix_rcv_add_addr(struct
>  }
>  EXPORT_SYMBOL_GPL(addrconf_prefix_rcv_add_addr);
>
> +static bool ipv6_reserved_interfaceid(struct in6_addr address)
> +{
> +       if ((address.s6_addr32[2] | address.s6_addr32[3]) == 0)
> +               return true;
> +
> +       if (address.s6_addr32[2] == htonl(0x02005eff) &&
> +           ((address.s6_addr32[3] & htonl(0xfe000000)) == htonl(0xfe000000)))
> +               return true;
> +
> +       if (address.s6_addr32[2] == htonl(0xfdffffff) &&
> +           ((address.s6_addr32[3] & htonl(0xffffff80)) == htonl(0xffffff80)))
> +               return true;
> +
> +       return false;
> +}
> +
> +static int ipv6_gen_addr_var_plen(struct in6_addr *address,
> +                                 u8 dad_count,
> +                                 const struct inet6_dev *idev,
> +                                 unsigned int rcvd_prfx_len,
> +                                 bool stable_privacy_mode)
> +{
> +       static union {
> +               char __data[SHA1_BLOCK_SIZE];
> +               struct {
> +                       struct in6_addr secret;
> +                       __be32 prefix[2];
> +                       unsigned char hwaddr[MAX_ADDR_LEN];
> +                       u8 dad_count;
> +               } __packed;
> +       } data;
> +       static __u32 workspace[SHA1_WORKSPACE_WORDS];
> +       static __u32 digest[SHA1_DIGEST_WORDS];
> +       struct net *net = dev_net(idev->dev);
> +       static DEFINE_SPINLOCK(lock);
> +       struct in6_addr secret;
> +       struct in6_addr temp;
> +
> +       BUILD_BUG_ON(sizeof(data.__data) != sizeof(data));
> +
> +       if (stable_privacy_mode) {
> +               if (idev->cnf.stable_secret.initialized)
> +                       secret = idev->cnf.stable_secret.secret;
> +               else if (net->ipv6.devconf_dflt->stable_secret.initialized)
> +                       secret = net->ipv6.devconf_dflt->stable_secret.secret;
> +               else
> +                       return -1;
> +       }
> +
> +retry:
> +       spin_lock_bh(&lock);
> +       if (stable_privacy_mode) {
> +               sha1_init(digest);
> +               memset(&data, 0, sizeof(data));
> +               memset(workspace, 0, sizeof(workspace));
> +               memcpy(data.hwaddr, idev->dev->perm_addr, idev->dev->addr_len);
> +               data.prefix[0] = address->s6_addr32[0];
> +               data.prefix[1] = address->s6_addr32[1];
> +               data.secret = secret;
> +               data.dad_count = dad_count;
> +
> +               sha1_transform(digest, data.__data, workspace);
> +
> +               temp = *address;
> +               temp.s6_addr32[0] = (__force __be32)digest[0];
> +               temp.s6_addr32[1] = (__force __be32)digest[1];
> +               temp.s6_addr32[2] = (__force __be32)digest[2];
> +               temp.s6_addr32[3] = (__force __be32)digest[3];

I do not understand why you copy *address and then overwrite
by digest?

> +       } else {
> +               temp = *address;
> +               get_random_bytes(temp.s6_addr32, 16);
> +       }
> +       spin_unlock_bh(&lock);
> +
> +       if (ipv6_reserved_interfaceid(temp)) {
> +               dad_count++;
> +               if (dad_count > dev_net(idev->dev)->ipv6.sysctl.idgen_retries)
> +                       return -1;
> +               goto retry;
> +       }
> +       ipv6_addr_prefix_copy(&temp, address, rcvd_prfx_len);
> +       *address = temp;
> +       return 0;
> +}
> +
>  void addrconf_prefix_rcv(struct net_device *dev, u8 *opt, int len, bool sllao)
>  {
>         struct prefix_info *pinfo;
> @@ -2781,9 +2915,33 @@ void addrconf_prefix_rcv(struct net_devi
>                                 dev_addr_generated = true;
>                         }
>                         goto ok;
> +               } else if (pinfo->prefix_len != 64 &&
> +                          pinfo->prefix_len > 0 && pinfo->prefix_len <= 128) {
> +                       /* SLAAC with prefixes of arbitrary length (Variable SLAAC).
> +                        * draft-mishra-6man-variable-slaac
> +                        * draft-mishra-v6ops-variable-slaac-problem-stmt
> +                        */
> +                       memcpy(&addr, &pinfo->prefix, 16);
> +                       if (in6_dev->cnf.addr_gen_mode == IN6_ADDR_GEN_MODE_STABLE_PRIVACY) {
> +                               if (!ipv6_gen_addr_var_plen(&addr,
> +                                                           0,
> +                                                           in6_dev,
> +                                                           pinfo->prefix_len,
> +                                                           true)) {
> +                                       addr_flags |= IFA_F_STABLE_PRIVACY;
> +                                       goto ok;
> +                               }
> +                       } else if (!ipv6_gen_addr_var_plen(&addr,
> +                                                          0,
> +                                                          in6_dev,
> +                                                          pinfo->prefix_len,
> +                                                          false)) {
> +                               goto ok;
> +                       }
> +               } else {
> +                       net_dbg_ratelimited("IPv6: Prefix with unexpected length %d\n",
> +                                           pinfo->prefix_len);
>                 }
> -               net_dbg_ratelimited("IPv6 addrconf: prefix with wrong length %d\n",
> -                                   pinfo->prefix_len);
>                 goto put;
>
>  ok:
> @@ -3186,22 +3344,6 @@ void addrconf_add_linklocal(struct inet6
>  }
>  EXPORT_SYMBOL_GPL(addrconf_add_linklocal);
>
> -static bool ipv6_reserved_interfaceid(struct in6_addr address)
> -{
> -       if ((address.s6_addr32[2] | address.s6_addr32[3]) == 0)
> -               return true;
> -
> -       if (address.s6_addr32[2] == htonl(0x02005eff) &&
> -           ((address.s6_addr32[3] & htonl(0xfe000000)) == htonl(0xfe000000)))
> -               return true;
> -
> -       if (address.s6_addr32[2] == htonl(0xfdffffff) &&
> -           ((address.s6_addr32[3] & htonl(0xffffff80)) == htonl(0xffffff80)))
> -               return true;
> -
> -       return false;
> -}
> -
>  static int ipv6_generate_stable_address(struct in6_addr *address,
>                                         u8 dad_count,
>                                         const struct inet6_dev *idev)

^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH net-next V7] net: Variable SLAAC: SLAAC with prefixes of arbitrary length in PIO
  2020-11-23 13:26                     ` Hideaki Yoshifuji
@ 2020-11-27 16:54                       ` Dmytro Shytyi
  0 siblings, 0 replies; 49+ messages in thread
From: Dmytro Shytyi @ 2020-11-27 16:54 UTC (permalink / raw)
  To: Hideaki Yoshifuji
  Cc: Jakub Kicinski, yoshfuji, kuznet, liuhangbin, davem, netdev,
	linux-kernel

Hello,

---- On Mon, 23 Nov 2020 14:26:27 +0100 Hideaki Yoshifuji <hideaki.yoshifuji@miraclelinux.com> wrote ----

 > Hi, 
 >  
 > 2020年11月20日(金) 18:28 Dmytro Shytyi <dmytro@shytyi.net>: 
 > > 
 > > Variable SLAAC: SLAAC with prefixes of arbitrary length in PIO (randomly 
 > > generated hostID or stable privacy + privacy extensions). 
 > > The main problem is that SLAAC RA or PD allocates a /64 by the Wireless 
 > > carrier 4G, 5G to a mobile hotspot, however segmentation of the /64 via 
 > > SLAAC is required so that downstream interfaces can be further subnetted. 
 > > Example: uCPE device (4G + WI-FI enabled) receives /64 via Wireless, and 
 > > assigns /72 to VNF-Firewall, /72 to WIFI, /72 to VNF-Router, /72 to 
 > > Load-Balancer and /72 to wired connected devices. 
 > > IETF document that defines problem statement: 
 > > draft-mishra-v6ops-variable-slaac-problem-stmt 
 > > IETF document that specifies variable slaac: 
 > > draft-mishra-6man-variable-slaac 
 > > 
 > > Signed-off-by: Dmytro Shytyi <dmytro@shytyi.net> 
 > > --- 
 > > diff -rupN net-next-5.10.0-rc2/net/ipv6/addrconf.c net-next-patch-5.10.0-rc2/net/ipv6/addrconf.c 
 > > --- net-next-5.10.0-rc2/net/ipv6/addrconf.c     2020-11-10 08:46:01.075193379 +0100 
 > > +++ net-next-patch-5.10.0-rc2/net/ipv6/addrconf.c       2020-11-19 21:26:39.770872898 +0100 
 > > @@ -142,7 +142,6 @@ static int ipv6_count_addresses(const st 
 > >  static int ipv6_generate_stable_address(struct in6_addr *addr, 
 > >                                         u8 dad_count, 
 > >                                         const struct inet6_dev *idev); 
 > > - 
 >  
 > Do not remove this line. 
[Dmytro]
Understood.

 > >  #define IN6_ADDR_HSIZE_SHIFT   8 
 > >  #define IN6_ADDR_HSIZE         (1 << IN6_ADDR_HSIZE_SHIFT) 
 > >  /* 
 > > @@ -1315,6 +1314,7 @@ static int ipv6_create_tempaddr(struct i 
 > >         struct ifa6_config cfg; 
 > >         long max_desync_factor; 
 > >         struct in6_addr addr; 
 > > +       struct in6_addr temp; 
 > >         int ret = 0; 
 > > 
 > >         write_lock_bh(&idev->lock); 
 > > @@ -1340,9 +1340,16 @@ retry: 
 > >                 goto out; 
 > >         } 
 > >         in6_ifa_hold(ifp); 
 > > -       memcpy(addr.s6_addr, ifp->addr.s6_addr, 8); 
 > > -       ipv6_gen_rnd_iid(&addr); 
 > > 
 > > +       if (ifp->prefix_len == 64) { 
 > > +               memcpy(addr.s6_addr, ifp->addr.s6_addr, 8); 
 > > +               ipv6_gen_rnd_iid(&addr); 
 > > +       } else if (ifp->prefix_len > 0 && ifp->prefix_len <= 128) { 
 > > +               memcpy(addr.s6_addr32, ifp->addr.s6_addr, 16); 
 > > +               get_random_bytes(temp.s6_addr32, 16); 
 > > +               ipv6_addr_prefix_copy(&temp, &addr, ifp->prefix_len); 
 > > +               memcpy(addr.s6_addr, temp.s6_addr, 16); 
 > > +       } 
 >  
 > I do not understand why you are copying many times. 
 > ipv6_addr_copy(), get_random_bytes(), and then ipv6_addr_prefix_copy 
 > is enough, no? 
[Dmytro]

I do not see any definition of ipv6_addr_copy() in v5.10-rc5.

Indeed we can try do "if case" something like this:
if (ifp->prefix_len > 0 && ifp->prefix_len <= 128) {
               get_random_bytes(addr.s6_addr, 16); 
               ipv6_addr_prefix_copy(&addr, &ifp->addr, ifp->prefix_len);  
} 

 > >         age = (now - ifp->tstamp) / HZ; 
 > > 
 > >         regen_advance = idev->cnf.regen_max_retry * 
 > > @@ -2569,6 +2576,41 @@ static bool is_addr_mode_generate_stable 
 > >                idev->cnf.addr_gen_mode == IN6_ADDR_GEN_MODE_RANDOM; 
 > >  } 
 > > 
 > > +static struct inet6_ifaddr *ipv6_cmp_rcvd_prsnt_prfxs(struct inet6_ifaddr *ifp, 
 > > +                                                     struct inet6_dev *in6_dev, 
 > > +                                                     struct net *net, 
 > > +                                                     const struct prefix_info *pinfo) 
 > > +{ 
 > > +       struct inet6_ifaddr *result_base = NULL; 
 > > +       struct inet6_ifaddr *result = NULL; 
 > > +       struct in6_addr curr_net_prfx; 
 > > +       struct in6_addr net_prfx; 
 > > +       bool prfxs_equal; 
 > > + 
 > > +       result_base = result; 
 > > +       rcu_read_lock(); 
 > > +       list_for_each_entry_rcu(ifp, &in6_dev->addr_list, if_list) { 
 > > +               if (!net_eq(dev_net(ifp->idev->dev), net)) 
 > > +                       continue; 
 > > +               ipv6_addr_prefix_copy(&net_prfx, &pinfo->prefix, pinfo->prefix_len); 
 > > +               ipv6_addr_prefix_copy(&curr_net_prfx, &ifp->addr, pinfo->prefix_len); 
 > > +               prfxs_equal = 
 > > +                       ipv6_prefix_equal(&net_prfx, &curr_net_prfx, pinfo->prefix_len); 
 > > +               if (prfxs_equal && pinfo->prefix_len == ifp->prefix_len) { 
 > > +                       result = ifp; 
 > > +                       in6_ifa_hold(ifp); 
 > > +                       break; 
 > > +               } 
 >  
 > I guess we can compare them with ipv6_prefix_equal() 
 > directly because the code assumes "pinfo->prefix_len" and ifp->prefix_len are 
 > equal. 

[Dmytro]
 Understood. 

 > > +       } 
 > > +       rcu_read_unlock(); 
 > > +       if (result_base != result) 
 > > +               ifp = result; 
 > > +       else 
 > > +               ifp = NULL; 
 > > + 
 > > +       return ifp; 
 > > +} 
 > > + 
 > >  int addrconf_prefix_rcv_add_addr(struct net *net, struct net_device *dev, 
 > >                                  const struct prefix_info *pinfo, 
 > >                                  struct inet6_dev *in6_dev, 
 > > @@ -2576,9 +2618,16 @@ int addrconf_prefix_rcv_add_addr(struct 
 > >                                  u32 addr_flags, bool sllao, bool tokenized, 
 > >                                  __u32 valid_lft, u32 prefered_lft) 
 > >  { 
 > > -       struct inet6_ifaddr *ifp = ipv6_get_ifaddr(net, addr, dev, 1); 
 > > +       struct inet6_ifaddr *ifp = NULL; 
 > > +       int plen = pinfo->prefix_len; 
 > >         int create = 0; 
 > > 
 > > +       if (plen > 0 && plen <= 128 && plen != 64 && 
 > > +           in6_dev->cnf.addr_gen_mode != IN6_ADDR_GEN_MODE_STABLE_PRIVACY) 
 > > +               ifp = ipv6_cmp_rcvd_prsnt_prfxs(ifp, in6_dev, net, pinfo); 
 > > +       else 
 > > +               ifp = ipv6_get_ifaddr(net, addr, dev, 1); 
 > > + 
 > >         if (!ifp && valid_lft) { 
 > >                 int max_addresses = in6_dev->cnf.max_addresses; 
 > >                 struct ifa6_config cfg = { 
 >  
 > I am wondering if we should enable this feature by default at this moment 
 > because the spec is personal internet draft and some test suites might 
 > consider this feature violates standards. 

[Dmytro]
1. By default the /64 plen is send by the router in RA. 
plen ==/64 is default behaviour for me. 
We are NOT replacing plen == /64 with this patch. 
Variable SLAAC is more like an additional functionality.
If and only IF router sends plen !=64 the patch functionality MAY be activated otherwise it is "dormant".

2. After a discussion with my colleague we come up with the next ideas:

- the implementation of IIDs whose length is arbitrary. The RFC7217, as
implemented here optionally, allows for IIDs of any length. The IETF
consensus status of that RFC is on the Standards Track, 
and the status is "PROPOSED STANDARD" (the next consensus level that
this RFC could head for is DRAFT STANDARD; preceding levels through
which the document already went successfully: are Individual Draft
submission, WG adoption, Last Call, AUTH48, published).

- the linux kernel supports IPv6 NAT in the mainline - 'masquerading'.
The feature IPv6 NAT is not supported at all by the IETF: the
consensus level is something like complete rejection. Yet it is fully
supported in the kernel. 

- the openbsd (not freebsd) implementations already support RFC 7217
IIDs of arbitrary length: send an RA with plen 65 and the receiving
Host will form an IID of length 63 and an IPv6 address. Why linux would
not allow this?

3. Possible solutions:
3.a) enable this feature as additional functionality, only when plen !=64, and keep it "dormant" by default.

3.b) Add sysctl net.ipv6.conf.enp0s3.variable_slaac = 1

3.c) A possibility is that this feature will be present in the make menuconfig.


 > > @@ -2657,6 +2706,91 @@ int addrconf_prefix_rcv_add_addr(struct 
 > >  } 
 > >  EXPORT_SYMBOL_GPL(addrconf_prefix_rcv_add_addr); 
 > > 
 > > +static bool ipv6_reserved_interfaceid(struct in6_addr address) 
 > > +{ 
 > > +       if ((address.s6_addr32[2] | address.s6_addr32[3]) == 0) 
 > > +               return true; 
 > > + 
 > > +       if (address.s6_addr32[2] == htonl(0x02005eff) && 
 > > +           ((address.s6_addr32[3] & htonl(0xfe000000)) == htonl(0xfe000000))) 
 > > +               return true; 
 > > + 
 > > +       if (address.s6_addr32[2] == htonl(0xfdffffff) && 
 > > +           ((address.s6_addr32[3] & htonl(0xffffff80)) == htonl(0xffffff80))) 
 > > +               return true; 
 > > + 
 > > +       return false; 
 > > +} 
 > > + 
 > > +static int ipv6_gen_addr_var_plen(struct in6_addr *address, 
 > > +                                 u8 dad_count, 
 > > +                                 const struct inet6_dev *idev, 
 > > +                                 unsigned int rcvd_prfx_len, 
 > > +                                 bool stable_privacy_mode) 
 > > +{ 
 > > +       static union { 
 > > +               char __data[SHA1_BLOCK_SIZE]; 
 > > +               struct { 
 > > +                       struct in6_addr secret; 
 > > +                       __be32 prefix[2]; 
 > > +                       unsigned char hwaddr[MAX_ADDR_LEN]; 
 > > +                       u8 dad_count; 
 > > +               } __packed; 
 > > +       } data; 
 > > +       static __u32 workspace[SHA1_WORKSPACE_WORDS]; 
 > > +       static __u32 digest[SHA1_DIGEST_WORDS]; 
 > > +       struct net *net = dev_net(idev->dev); 
 > > +       static DEFINE_SPINLOCK(lock); 
 > > +       struct in6_addr secret; 
 > > +       struct in6_addr temp; 
 > > + 
 > > +       BUILD_BUG_ON(sizeof(data.__data) != sizeof(data)); 
 > > + 
 > > +       if (stable_privacy_mode) { 
 > > +               if (idev->cnf.stable_secret.initialized) 
 > > +                       secret = idev->cnf.stable_secret.secret; 
 > > +               else if (net->ipv6.devconf_dflt->stable_secret.initialized) 
 > > +                       secret = net->ipv6.devconf_dflt->stable_secret.secret; 
 > > +               else 
 > > +                       return -1; 
 > > +       } 
 > > + 
 > > +retry: 
 > > +       spin_lock_bh(&lock); 
 > > +       if (stable_privacy_mode) { 
 > > +               sha1_init(digest); 
 > > +               memset(&data, 0, sizeof(data)); 
 > > +               memset(workspace, 0, sizeof(workspace)); 
 > > +               memcpy(data.hwaddr, idev->dev->perm_addr, idev->dev->addr_len); 
 > > +               data.prefix[0] = address->s6_addr32[0]; 
 > > +               data.prefix[1] = address->s6_addr32[1]; 
 > > +               data.secret = secret; 
 > > +               data.dad_count = dad_count; 
 > > + 
 > > +               sha1_transform(digest, data.__data, workspace); 
 > > + 
 > > +               temp = *address; 
 > > +               temp.s6_addr32[0] = (__force __be32)digest[0]; 
 > > +               temp.s6_addr32[1] = (__force __be32)digest[1]; 
 > > +               temp.s6_addr32[2] = (__force __be32)digest[2]; 
 > > +               temp.s6_addr32[3] = (__force __be32)digest[3]; 
 >  
 > I do not understand why you copy *address and then overwrite 
 > by digest? 

[Dmytro]
Originally it comes from "ipv6_generate_stable_address()".
it is present there because only 64bits of temp are replaced by digest.
In this case, we replace 128 bits thus I agree: it is misleading. I will fix that.


 > > +       } else { 
 > > +               temp = *address; 
 > > +               get_random_bytes(temp.s6_addr32, 16); 
 > > +       } 
 > > +       spin_unlock_bh(&lock); 
 > > + 
 > > +       if (ipv6_reserved_interfaceid(temp)) { 
 > > +               dad_count++; 
 > > +               if (dad_count > dev_net(idev->dev)->ipv6.sysctl.idgen_retries) 
 > > +                       return -1; 
 > > +               goto retry; 
 > > +       } 
 > > +       ipv6_addr_prefix_copy(&temp, address, rcvd_prfx_len); 
 > > +       *address = temp; 
 > > +       return 0; 
 > > +} 
 > > + 
 > >  void addrconf_prefix_rcv(struct net_device *dev, u8 *opt, int len, bool sllao) 
 > >  { 
 > >         struct prefix_info *pinfo; 
 > > @@ -2781,9 +2915,33 @@ void addrconf_prefix_rcv(struct net_devi 
 > >                                 dev_addr_generated = true; 
 > >                         } 
 > >                         goto ok; 
 > > +               } else if (pinfo->prefix_len != 64 && 
 > > +                          pinfo->prefix_len > 0 && pinfo->prefix_len <= 128) { 
 > > +                       /* SLAAC with prefixes of arbitrary length (Variable SLAAC). 
 > > +                        * draft-mishra-6man-variable-slaac 
 > > +                        * draft-mishra-v6ops-variable-slaac-problem-stmt 
 > > +                        */ 
 > > +                       memcpy(&addr, &pinfo->prefix, 16); 
 > > +                       if (in6_dev->cnf.addr_gen_mode == IN6_ADDR_GEN_MODE_STABLE_PRIVACY) { 
 > > +                               if (!ipv6_gen_addr_var_plen(&addr, 
 > > +                                                           0, 
 > > +                                                           in6_dev, 
 > > +                                                           pinfo->prefix_len, 
 > > +                                                           true)) { 
 > > +                                       addr_flags |= IFA_F_STABLE_PRIVACY; 
 > > +                                       goto ok; 
 > > +                               } 
 > > +                       } else if (!ipv6_gen_addr_var_plen(&addr, 
 > > +                                                          0, 
 > > +                                                          in6_dev, 
 > > +                                                          pinfo->prefix_len, 
 > > +                                                          false)) { 
 > > +                               goto ok; 
 > > +                       } 
 > > +               } else { 
 > > +                       net_dbg_ratelimited("IPv6: Prefix with unexpected length %d\n", 
 > > +                                           pinfo->prefix_len); 
 > >                 } 
 > > -               net_dbg_ratelimited("IPv6 addrconf: prefix with wrong length %d\n", 
 > > -                                   pinfo->prefix_len); 
 > >                 goto put; 
 > > 
 > >  ok: 
 > > @@ -3186,22 +3344,6 @@ void addrconf_add_linklocal(struct inet6 
 > >  } 
 > >  EXPORT_SYMBOL_GPL(addrconf_add_linklocal); 
 > > 
 > > -static bool ipv6_reserved_interfaceid(struct in6_addr address) 
 > > -{ 
 > > -       if ((address.s6_addr32[2] | address.s6_addr32[3]) == 0) 
 > > -               return true; 
 > > - 
 > > -       if (address.s6_addr32[2] == htonl(0x02005eff) && 
 > > -           ((address.s6_addr32[3] & htonl(0xfe000000)) == htonl(0xfe000000))) 
 > > -               return true; 
 > > - 
 > > -       if (address.s6_addr32[2] == htonl(0xfdffffff) && 
 > > -           ((address.s6_addr32[3] & htonl(0xffffff80)) == htonl(0xffffff80))) 
 > > -               return true; 
 > > - 
 > > -       return false; 
 > > -} 
 > > - 
 > >  static int ipv6_generate_stable_address(struct in6_addr *address, 
 > >                                         u8 dad_count, 
 > >                                         const struct inet6_dev *idev) 
 > 

^ permalink raw reply	[flat|nested] 49+ messages in thread

* [PATCH net-next V8] net: Variable SLAAC: SLAAC with prefixes of arbitrary length in PIO
  2020-11-20  9:26                   ` [PATCH net-next V7] " Dmytro Shytyi
  2020-11-23 13:26                     ` Hideaki Yoshifuji
@ 2020-12-09  3:27                     ` Dmytro Shytyi
  2020-12-16  0:00                       ` David Miller
  2020-12-16 22:01                       ` [PATCH net-next V9] " Dmytro Shytyi
  1 sibling, 2 replies; 49+ messages in thread
From: Dmytro Shytyi @ 2020-12-09  3:27 UTC (permalink / raw)
  To: Jakub Kicinski, yoshfuji, kuznet, liuhangbin, davem, netdev,
	linux-kernel

Variable SLAAC [Can be activated via sysctl]: 
SLAAC with prefixes of arbitrary length in PIO (randomly
generated hostID or stable privacy + privacy extensions).
The main problem is that SLAAC RA or PD allocates a /64 by the Wireless
carrier 4G, 5G to a mobile hotspot, however segmentation of the /64 via
SLAAC is required so that downstream interfaces can be further subnetted.
Example: uCPE device (4G + WI-FI enabled) receives /64 via Wireless, and
assigns /72 to VNF-Firewall, /72 to WIFI, /72 to VNF-Router, /72 to
Load-Balancer and /72 to wired connected devices.
IETF document that defines problem statement:
draft-mishra-v6ops-variable-slaac-problem-stmt
IETF document that specifies variable slaac:
draft-mishra-6man-variable-slaac

Signed-off-by: Dmytro Shytyi <dmytro@shytyi.net>
---
diff --git a/include/linux/ipv6.h b/include/linux/ipv6.h
index dda61d150a13..67ca3925463c 100644
--- a/include/linux/ipv6.h
+++ b/include/linux/ipv6.h
@@ -75,6 +75,7 @@ struct ipv6_devconf {
 	__s32		disable_policy;
 	__s32           ndisc_tclass;
 	__s32		rpl_seg_enabled;
+	__s32		variable_slaac;
 
 	struct ctl_table_header *sysctl_header;
 };
diff --git a/include/uapi/linux/ipv6.h b/include/uapi/linux/ipv6.h
index 13e8751bf24a..f2af4f9fba2d 100644
--- a/include/uapi/linux/ipv6.h
+++ b/include/uapi/linux/ipv6.h
@@ -189,7 +189,8 @@ enum {
 	DEVCONF_ACCEPT_RA_RT_INFO_MIN_PLEN,
 	DEVCONF_NDISC_TCLASS,
 	DEVCONF_RPL_SEG_ENABLED,
-	DEVCONF_MAX
+	DEVCONF_MAX,
+	DEVCONF_VARIABLE_SLAAC
 };
 
 
diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c
index eff2cacd5209..07afe4ce984e 100644
--- a/net/ipv6/addrconf.c
+++ b/net/ipv6/addrconf.c
@@ -236,6 +236,7 @@ static struct ipv6_devconf ipv6_devconf __read_mostly = {
 	.addr_gen_mode		= IN6_ADDR_GEN_MODE_EUI64,
 	.disable_policy		= 0,
 	.rpl_seg_enabled	= 0,
+	.variable_slaac		= 0,
 };
 
 static struct ipv6_devconf ipv6_devconf_dflt __read_mostly = {
@@ -291,6 +292,7 @@ static struct ipv6_devconf ipv6_devconf_dflt __read_mostly = {
 	.addr_gen_mode		= IN6_ADDR_GEN_MODE_EUI64,
 	.disable_policy		= 0,
 	.rpl_seg_enabled	= 0,
+	.variable_slaac		= 0,
 };
 
 /* Check if link is ready: is it up and is a valid qdisc available */
@@ -1340,9 +1342,15 @@ static int ipv6_create_tempaddr(struct inet6_ifaddr *ifp, bool block)
 		goto out;
 	}
 	in6_ifa_hold(ifp);
-	memcpy(addr.s6_addr, ifp->addr.s6_addr, 8);
-	ipv6_gen_rnd_iid(&addr);
 
+	if (ifp->prefix_len == 64) {
+		memcpy(addr.s6_addr, ifp->addr.s6_addr, 8);
+		ipv6_gen_rnd_iid(&addr);
+	} else if (ifp->prefix_len > 0 && ifp->prefix_len <= 128 &&
+		   idev->cnf.variable_slaac) {
+		get_random_bytes(addr.s6_addr, 16);
+		ipv6_addr_prefix_copy(&addr, &ifp->addr, ifp->prefix_len);
+	}
 	age = (now - ifp->tstamp) / HZ;
 
 	regen_advance = idev->cnf.regen_max_retry *
@@ -2569,6 +2577,37 @@ static bool is_addr_mode_generate_stable(struct inet6_dev *idev)
 	       idev->cnf.addr_gen_mode == IN6_ADDR_GEN_MODE_RANDOM;
 }
 
+static struct inet6_ifaddr *ipv6_cmp_rcvd_prsnt_prfxs(struct inet6_ifaddr *ifp,
+						      struct inet6_dev *in6_dev,
+						      struct net *net,
+						      const struct prefix_info *pinfo)
+{
+	struct inet6_ifaddr *result_base = NULL;
+	struct inet6_ifaddr *result = NULL;
+	bool prfxs_equal;
+
+	result_base = result;
+	rcu_read_lock();
+	list_for_each_entry_rcu(ifp, &in6_dev->addr_list, if_list) {
+		if (!net_eq(dev_net(ifp->idev->dev), net))
+			continue;
+		prfxs_equal =
+			ipv6_prefix_equal(&pinfo->prefix, &ifp->addr, pinfo->prefix_len);
+		if (prfxs_equal && pinfo->prefix_len == ifp->prefix_len) {
+			result = ifp;
+			in6_ifa_hold(ifp);
+			break;
+		}
+	}
+	rcu_read_unlock();
+	if (result_base != result)
+		ifp = result;
+	else
+		ifp = NULL;
+
+	return ifp;
+}
+
 int addrconf_prefix_rcv_add_addr(struct net *net, struct net_device *dev,
 				 const struct prefix_info *pinfo,
 				 struct inet6_dev *in6_dev,
@@ -2576,9 +2615,17 @@ int addrconf_prefix_rcv_add_addr(struct net *net, struct net_device *dev,
 				 u32 addr_flags, bool sllao, bool tokenized,
 				 __u32 valid_lft, u32 prefered_lft)
 {
-	struct inet6_ifaddr *ifp = ipv6_get_ifaddr(net, addr, dev, 1);
+	struct inet6_ifaddr *ifp = NULL;
+	int plen = pinfo->prefix_len;
 	int create = 0;
 
+	if (plen > 0 && plen <= 128 && plen != 64 &&
+	    in6_dev->cnf.addr_gen_mode != IN6_ADDR_GEN_MODE_STABLE_PRIVACY &&
+	    in6_dev->cnf.variable_slaac)
+		ifp = ipv6_cmp_rcvd_prsnt_prfxs(ifp, in6_dev, net, pinfo);
+	else
+		ifp = ipv6_get_ifaddr(net, addr, dev, 1);
+
 	if (!ifp && valid_lft) {
 		int max_addresses = in6_dev->cnf.max_addresses;
 		struct ifa6_config cfg = {
@@ -2657,6 +2704,90 @@ int addrconf_prefix_rcv_add_addr(struct net *net, struct net_device *dev,
 }
 EXPORT_SYMBOL_GPL(addrconf_prefix_rcv_add_addr);
 
+static bool ipv6_reserved_interfaceid(struct in6_addr address)
+{
+	if ((address.s6_addr32[2] | address.s6_addr32[3]) == 0)
+		return true;
+
+	if (address.s6_addr32[2] == htonl(0x02005eff) &&
+	    ((address.s6_addr32[3] & htonl(0xfe000000)) == htonl(0xfe000000)))
+		return true;
+
+	if (address.s6_addr32[2] == htonl(0xfdffffff) &&
+	    ((address.s6_addr32[3] & htonl(0xffffff80)) == htonl(0xffffff80)))
+		return true;
+
+	return false;
+}
+
+static int ipv6_gen_addr_var_plen(struct in6_addr *address,
+				  u8 dad_count,
+				  const struct inet6_dev *idev,
+				  unsigned int rcvd_prfx_len,
+				  bool stable_privacy_mode)
+{
+	static union {
+		char __data[SHA1_BLOCK_SIZE];
+		struct {
+			struct in6_addr secret;
+			__be32 prefix[2];
+			unsigned char hwaddr[MAX_ADDR_LEN];
+			u8 dad_count;
+		} __packed;
+	} data;
+	static __u32 workspace[SHA1_WORKSPACE_WORDS];
+	static __u32 digest[SHA1_DIGEST_WORDS];
+	struct net *net = dev_net(idev->dev);
+	static DEFINE_SPINLOCK(lock);
+	struct in6_addr secret;
+	struct in6_addr temp;
+
+	BUILD_BUG_ON(sizeof(data.__data) != sizeof(data));
+
+	if (stable_privacy_mode) {
+		if (idev->cnf.stable_secret.initialized)
+			secret = idev->cnf.stable_secret.secret;
+		else if (net->ipv6.devconf_dflt->stable_secret.initialized)
+			secret = net->ipv6.devconf_dflt->stable_secret.secret;
+		else
+			return -1;
+	}
+
+retry:
+	spin_lock_bh(&lock);
+	if (stable_privacy_mode) {
+		sha1_init(digest);
+		memset(&data, 0, sizeof(data));
+		memset(workspace, 0, sizeof(workspace));
+		memcpy(data.hwaddr, idev->dev->perm_addr, idev->dev->addr_len);
+		data.prefix[0] = address->s6_addr32[0];
+		data.prefix[1] = address->s6_addr32[1];
+		data.secret = secret;
+		data.dad_count = dad_count;
+
+		sha1_transform(digest, data.__data, workspace);
+
+		temp.s6_addr32[0] = (__force __be32)digest[0];
+		temp.s6_addr32[1] = (__force __be32)digest[1];
+		temp.s6_addr32[2] = (__force __be32)digest[2];
+		temp.s6_addr32[3] = (__force __be32)digest[3];
+	} else {
+		get_random_bytes(temp.s6_addr32, 16);
+	}
+
+	spin_unlock_bh(&lock);
+
+	if (ipv6_reserved_interfaceid(temp)) {
+		dad_count++;
+		if (dad_count > dev_net(idev->dev)->ipv6.sysctl.idgen_retries)
+			return -1;
+		goto retry;
+	}
+	ipv6_addr_prefix_copy(&temp, address, rcvd_prfx_len);
+	*address = temp;
+	return 0;
+}
+
 void addrconf_prefix_rcv(struct net_device *dev, u8 *opt, int len, bool sllao)
 {
 	struct prefix_info *pinfo;
@@ -2781,9 +2912,34 @@ void addrconf_prefix_rcv(struct net_device *dev, u8 *opt, int len, bool sllao)
 				dev_addr_generated = true;
 			}
 			goto ok;
+		} else if (pinfo->prefix_len != 64 &&
+			   pinfo->prefix_len > 0 && pinfo->prefix_len <= 128 &&
+			   in6_dev->cnf.variable_slaac) {
+			/* SLAAC with prefixes of arbitrary length (Variable SLAAC).
+			 * draft-mishra-6man-variable-slaac
+			 * draft-mishra-v6ops-variable-slaac-problem-stmt
+			 */
+			memcpy(&addr, &pinfo->prefix, 16);
+			if (in6_dev->cnf.addr_gen_mode == IN6_ADDR_GEN_MODE_STABLE_PRIVACY) {
+				if (!ipv6_gen_addr_var_plen(&addr,
+							    0,
+							    in6_dev,
+							    pinfo->prefix_len,
+							    true)) {
+					addr_flags |= IFA_F_STABLE_PRIVACY;
+					goto ok;
+				}
+			} else if (!ipv6_gen_addr_var_plen(&addr,
+							   0,
+							   in6_dev,
+							   pinfo->prefix_len,
+							   false)) {
+				goto ok;
+			}
+		} else {
+			net_dbg_ratelimited("IPv6: Prefix with unexpected length %d\n",
+					    pinfo->prefix_len);
 		}
-		net_dbg_ratelimited("IPv6 addrconf: prefix with wrong length %d\n",
-				    pinfo->prefix_len);
 		goto put;
 
 ok:
@@ -3186,22 +3342,6 @@ void addrconf_add_linklocal(struct inet6_dev *idev,
 }
 EXPORT_SYMBOL_GPL(addrconf_add_linklocal);
 
-static bool ipv6_reserved_interfaceid(struct in6_addr address)
-{
-	if ((address.s6_addr32[2] | address.s6_addr32[3]) == 0)
-		return true;
-
-	if (address.s6_addr32[2] == htonl(0x02005eff) &&
-	    ((address.s6_addr32[3] & htonl(0xfe000000)) == htonl(0xfe000000)))
-		return true;
-
-	if (address.s6_addr32[2] == htonl(0xfdffffff) &&
-	    ((address.s6_addr32[3] & htonl(0xffffff80)) == htonl(0xffffff80)))
-		return true;
-
-	return false;
-}
-
 static int ipv6_generate_stable_address(struct in6_addr *address,
 					u8 dad_count,
 					const struct inet6_dev *idev)
@@ -5517,6 +5657,7 @@ static inline void ipv6_store_devconf(struct ipv6_devconf *cnf,
 	array[DEVCONF_DISABLE_POLICY] = cnf->disable_policy;
 	array[DEVCONF_NDISC_TCLASS] = cnf->ndisc_tclass;
 	array[DEVCONF_RPL_SEG_ENABLED] = cnf->rpl_seg_enabled;
+	array[DEVCONF_VARIABLE_SLAAC] = cnf->variable_slaac;
 }
 
 static inline size_t inet6_ifla6_size(void)
@@ -6897,6 +7038,13 @@ static const struct ctl_table addrconf_sysctl[] = {
 		.mode		= 0644,
 		.proc_handler	= proc_dointvec,
 	},
+	{
+		.procname	= "variable_slaac",
+		.data		= &ipv6_devconf.variable_slaac,
+		.maxlen		= sizeof(int),
+		.mode		= 0644,
+		.proc_handler	= proc_dointvec,
+	},
 	{
 		/* sentinel */
 	}

^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH net-next V8] net: Variable SLAAC: SLAAC with prefixes of arbitrary length in PIO
  2020-12-09  3:27                     ` [PATCH net-next V8] " Dmytro Shytyi
@ 2020-12-16  0:00                       ` David Miller
  2020-12-16 14:01                         ` Dmytro Shytyi
  2020-12-16 22:01                       ` [PATCH net-next V9] " Dmytro Shytyi
  1 sibling, 1 reply; 49+ messages in thread
From: David Miller @ 2020-12-16  0:00 UTC (permalink / raw)
  To: dmytro; +Cc: kuba, yoshfuji, kuznet, liuhangbin, netdev, linux-kernel

From: Dmytro Shytyi <dmytro@shytyi.net>
Date: Wed, 09 Dec 2020 04:27:54 +0100

> Variable SLAAC [Can be activated via sysctl]: 
> SLAAC with prefixes of arbitrary length in PIO (randomly
> generated hostID or stable privacy + privacy extensions).
> The main problem is that SLAAC RA or PD allocates a /64 by the Wireless
> carrier 4G, 5G to a mobile hotspot, however segmentation of the /64 via
> SLAAC is required so that downstream interfaces can be further subnetted.
> Example: uCPE device (4G + WI-FI enabled) receives /64 via Wireless, and
> assigns /72 to VNF-Firewall, /72 to WIFI, /72 to VNF-Router, /72 to
> Load-Balancer and /72 to wired connected devices.
> IETF document that defines problem statement:
> draft-mishra-v6ops-variable-slaac-problem-stmt
> IETF document that specifies variable slaac:
> draft-mishra-6man-variable-slaac
> 
> Signed-off-by: Dmytro Shytyi <dmytro@shytyi.net>
> ---
> diff --git a/include/linux/ipv6.h b/include/linux/ipv6.h
> index dda61d150a13..67ca3925463c 100644
> --- a/include/linux/ipv6.h
> +++ b/include/linux/ipv6.h
> @@ -75,6 +75,7 @@ struct ipv6_devconf {
>  	__s32		disable_policy;
>  	__s32           ndisc_tclass;
>  	__s32		rpl_seg_enabled;
> +	__s32		variable_slaac;
>  
>  	struct ctl_table_header *sysctl_header;
>  };
> diff --git a/include/uapi/linux/ipv6.h b/include/uapi/linux/ipv6.h
> index 13e8751bf24a..f2af4f9fba2d 100644
> --- a/include/uapi/linux/ipv6.h
> +++ b/include/uapi/linux/ipv6.h
> @@ -189,7 +189,8 @@ enum {
>  	DEVCONF_ACCEPT_RA_RT_INFO_MIN_PLEN,
>  	DEVCONF_NDISC_TCLASS,
>  	DEVCONF_RPL_SEG_ENABLED,
> -	DEVCONF_MAX
> +	DEVCONF_MAX,
> +	DEVCONF_VARIABLE_SLAAC
>  };
>  
>  
> diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c
> index eff2cacd5209..07afe4ce984e 100644
> --- a/net/ipv6/addrconf.c
> +++ b/net/ipv6/addrconf.c
> @@ -236,6 +236,7 @@ static struct ipv6_devconf ipv6_devconf __read_mostly = {
>  	.addr_gen_mode		= IN6_ADDR_GEN_MODE_EUI64,
>  	.disable_policy		= 0,
>  	.rpl_seg_enabled	= 0,
> +	.variable_slaac		= 0,
>  };
>  
>  static struct ipv6_devconf ipv6_devconf_dflt __read_mostly = {
> @@ -291,6 +292,7 @@ static struct ipv6_devconf ipv6_devconf_dflt __read_mostly = {
>  	.addr_gen_mode		= IN6_ADDR_GEN_MODE_EUI64,
>  	.disable_policy		= 0,
>  	.rpl_seg_enabled	= 0,
> +	.variable_slaac		= 0,
>  };
>  
>  /* Check if link is ready: is it up and is a valid qdisc available */
> @@ -1340,9 +1342,15 @@ static int ipv6_create_tempaddr(struct inet6_ifaddr *ifp, bool block)
>  		goto out;
>  	}
>  	in6_ifa_hold(ifp);
> -	memcpy(addr.s6_addr, ifp->addr.s6_addr, 8);
> -	ipv6_gen_rnd_iid(&addr);
>  
> +	if (ifp->prefix_len == 64) {
> +		memcpy(addr.s6_addr, ifp->addr.s6_addr, 8);
> +		ipv6_gen_rnd_iid(&addr);
> +	} else if (ifp->prefix_len > 0 && ifp->prefix_len <= 128 &&
> +		   idev->cnf.variable_slaac) {
> +		get_random_bytes(addr.s6_addr, 16);
> +		ipv6_addr_prefix_copy(&addr, &ifp->addr, ifp->prefix_len);
> +	}
>  	age = (now - ifp->tstamp) / HZ;
>  
>  	regen_advance = idev->cnf.regen_max_retry *
> @@ -2569,6 +2577,37 @@ static bool is_addr_mode_generate_stable(struct inet6_dev *idev)
>  	       idev->cnf.addr_gen_mode == IN6_ADDR_GEN_MODE_RANDOM;
>  }
>  
> +static struct inet6_ifaddr *ipv6_cmp_rcvd_prsnt_prfxs(struct inet6_ifaddr *ifp,
> +						      struct inet6_dev *in6_dev,
> +						      struct net *net,
> +						      const struct prefix_info *pinfo)
> +{
> +	struct inet6_ifaddr *result_base = NULL;
> +	struct inet6_ifaddr *result = NULL;
> +	bool prfxs_equal;
> +
> +	result_base = result;

This is NULL, are you sure you didn't mewan to init this to 'ifp'
 or similar instead?

Thanks.

^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH net-next V8] net: Variable SLAAC: SLAAC with prefixes of arbitrary length in PIO
  2020-12-16  0:00                       ` David Miller
@ 2020-12-16 14:01                         ` Dmytro Shytyi
  2020-12-16 17:28                           ` Jakub Kicinski
  0 siblings, 1 reply; 49+ messages in thread
From: Dmytro Shytyi @ 2020-12-16 14:01 UTC (permalink / raw)
  To: David Miller; +Cc: kuba, yoshfuji, kuznet, liuhangbin, netdev, linux-kernel

Hello David,

Thank you for your comment. 
Asnwers in-line.

Take care,
                              
Dmytro SHYTYI


---- On Wed, 16 Dec 2020 01:00:49 +0100 David Miller <davem@davemloft.net> wrote ----

 > From: Dmytro Shytyi <dmytro@shytyi.net> 
 > Date: Wed, 09 Dec 2020 04:27:54 +0100 
 >  
 > > Variable SLAAC [Can be activated via sysctl]: 
 > > SLAAC with prefixes of arbitrary length in PIO (randomly 
 > > generated hostID or stable privacy + privacy extensions). 
 > > The main problem is that SLAAC RA or PD allocates a /64 by the Wireless 
 > > carrier 4G, 5G to a mobile hotspot, however segmentation of the /64 via 
 > > SLAAC is required so that downstream interfaces can be further subnetted. 
 > > Example: uCPE device (4G + WI-FI enabled) receives /64 via Wireless, and 
 > > assigns /72 to VNF-Firewall, /72 to WIFI, /72 to VNF-Router, /72 to 
 > > Load-Balancer and /72 to wired connected devices. 
 > > IETF document that defines problem statement: 
 > > draft-mishra-v6ops-variable-slaac-problem-stmt 
 > > IETF document that specifies variable slaac: 
 > > draft-mishra-6man-variable-slaac 
 > > 
 > > Signed-off-by: Dmytro Shytyi <dmytro@shytyi.net> 
 > > --- 
 > > diff --git a/include/linux/ipv6.h b/include/linux/ipv6.h 
 > > index dda61d150a13..67ca3925463c 100644 
 > > --- a/include/linux/ipv6.h 
 > > +++ b/include/linux/ipv6.h 
 > > @@ -75,6 +75,7 @@ struct ipv6_devconf { 
 > >      __s32        disable_policy; 
 > >      __s32           ndisc_tclass; 
 > >      __s32        rpl_seg_enabled; 
 > > +    __s32        variable_slaac; 
 > > 
 > >      struct ctl_table_header *sysctl_header; 
 > >  }; 
 > > diff --git a/include/uapi/linux/ipv6.h b/include/uapi/linux/ipv6.h 
 > > index 13e8751bf24a..f2af4f9fba2d 100644 
 > > --- a/include/uapi/linux/ipv6.h 
 > > +++ b/include/uapi/linux/ipv6.h 
 > > @@ -189,7 +189,8 @@ enum { 
 > >      DEVCONF_ACCEPT_RA_RT_INFO_MIN_PLEN, 
 > >      DEVCONF_NDISC_TCLASS, 
 > >      DEVCONF_RPL_SEG_ENABLED, 
 > > -    DEVCONF_MAX 
 > > +    DEVCONF_MAX, 
 > > +    DEVCONF_VARIABLE_SLAAC 
 > >  }; 
 > > 
 > > 
 > > diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c 
 > > index eff2cacd5209..07afe4ce984e 100644 
 > > --- a/net/ipv6/addrconf.c 
 > > +++ b/net/ipv6/addrconf.c 
 > > @@ -236,6 +236,7 @@ static struct ipv6_devconf ipv6_devconf __read_mostly = { 
 > >      .addr_gen_mode        = IN6_ADDR_GEN_MODE_EUI64, 
 > >      .disable_policy        = 0, 
 > >      .rpl_seg_enabled    = 0, 
 > > +    .variable_slaac        = 0, 
 > >  }; 
 > > 
 > >  static struct ipv6_devconf ipv6_devconf_dflt __read_mostly = { 
 > > @@ -291,6 +292,7 @@ static struct ipv6_devconf ipv6_devconf_dflt __read_mostly = { 
 > >      .addr_gen_mode        = IN6_ADDR_GEN_MODE_EUI64, 
 > >      .disable_policy        = 0, 
 > >      .rpl_seg_enabled    = 0, 
 > > +    .variable_slaac        = 0, 
 > >  }; 
 > > 
 > >  /* Check if link is ready: is it up and is a valid qdisc available */ 
 > > @@ -1340,9 +1342,15 @@ static int ipv6_create_tempaddr(struct inet6_ifaddr *ifp, bool block) 
 > >          goto out; 
 > >      } 
 > >      in6_ifa_hold(ifp); 
 > > -    memcpy(addr.s6_addr, ifp->addr.s6_addr, 8); 
 > > -    ipv6_gen_rnd_iid(&addr); 
 > > 
 > > +    if (ifp->prefix_len == 64) { 
 > > +        memcpy(addr.s6_addr, ifp->addr.s6_addr, 8); 
 > > +        ipv6_gen_rnd_iid(&addr); 
 > > +    } else if (ifp->prefix_len > 0 && ifp->prefix_len <= 128 && 
 > > +           idev->cnf.variable_slaac) { 
 > > +        get_random_bytes(addr.s6_addr, 16); 
 > > +        ipv6_addr_prefix_copy(&addr, &ifp->addr, ifp->prefix_len); 
 > > +    } 
 > >      age = (now - ifp->tstamp) / HZ; 
 > > 
 > >      regen_advance = idev->cnf.regen_max_retry * 
 > > @@ -2569,6 +2577,37 @@ static bool is_addr_mode_generate_stable(struct inet6_dev *idev) 
 > >             idev->cnf.addr_gen_mode == IN6_ADDR_GEN_MODE_RANDOM; 
 > >  } 
 > > 
 > > +static struct inet6_ifaddr *ipv6_cmp_rcvd_prsnt_prfxs(struct inet6_ifaddr *ifp, 
 > > +                              struct inet6_dev *in6_dev, 
 > > +                              struct net *net, 
 > > +                              const struct prefix_info *pinfo) 
 > > +{ 
 > > +    struct inet6_ifaddr *result_base = NULL; 
 > > +    struct inet6_ifaddr *result = NULL; 
 > > +    bool prfxs_equal; 
 > > + 
 > > +    result_base = result; 
 >  
 > This is NULL, are you sure you didn't mewan to init this to 'ifp' 
 >  or similar instead? 

[Dmytro] I put the entire function to comment below the instructions.
[Dmytro]:
+static struct inet6_ifaddr *ipv6_cmp_rcvd_prsnt_prfxs(struct inet6_ifaddr *ifp,
+                         struct inet6_dev *in6_dev,
+                         struct net *net,
+                         const struct prefix_info *pinfo)
+{
+    struct inet6_ifaddr *result_base = NULL;
+    struct inet6_ifaddr *result = NULL;
+    bool prfxs_equal;
+
+    result_base = result;
+    rcu_read_lock();
+    list_for_each_entry_rcu(ifp, &in6_dev->addr_list, if_list) {
+        if (!net_eq(dev_net(ifp->idev->dev), net))
+            continue;
+        prfxs_equal =
+            ipv6_prefix_equal(&pinfo->prefix, &ifp->addr, pinfo->prefix_len);
+        if (prfxs_equal && pinfo->prefix_len == ifp->prefix_len) {
+            result = ifp;
+            in6_ifa_hold(ifp);
+            break;
+        }
+    }
+    rcu_read_unlock();
+    if (result_base != result)
+        ifp = result;
+    else
+        ifp = NULL;
+
+    return ifp;
+}
+

[Dmytro]:
1st initial stage is :
+    result_base = result;

2nd stage is (as you mention, 'result' will be assigned to 'ifp', in the process):
+            result = ifp;

3rd stage is to compare if  "result_base" and "result" are not equal (and take required action).
 if (result_base != result)
+        ifp = result;
+    else
+        ifp = NULL;

Looks more/less ok for me.

Thanks.

 > Thanks. 
 > 


^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH net-next V8] net: Variable SLAAC: SLAAC with prefixes of arbitrary length in PIO
  2020-12-16 14:01                         ` Dmytro Shytyi
@ 2020-12-16 17:28                           ` Jakub Kicinski
  2020-12-16 21:56                             ` Dmytro Shytyi
  0 siblings, 1 reply; 49+ messages in thread
From: Jakub Kicinski @ 2020-12-16 17:28 UTC (permalink / raw)
  To: Dmytro Shytyi
  Cc: David Miller, yoshfuji, kuznet, liuhangbin, netdev, linux-kernel

On Wed, 16 Dec 2020 15:01:33 +0100 Dmytro Shytyi wrote:
> Hello David,
> 
> Thank you for your comment. 
> Asnwers in-line.
> 
> Take care,
>                               
> Dmytro SHYTYI
> 
> 
> ---- On Wed, 16 Dec 2020 01:00:49 +0100 David Miller <davem@davemloft.net> wrote ----
> 
>  > From: Dmytro Shytyi <dmytro@shytyi.net> 
>  > Date: Wed, 09 Dec 2020 04:27:54 +0100 
>  >    
>  > > Variable SLAAC [Can be activated via sysctl]: 
>  > > SLAAC with prefixes of arbitrary length in PIO (randomly 
>  > > generated hostID or stable privacy + privacy extensions). 
>  > > The main problem is that SLAAC RA or PD allocates a /64 by the Wireless 
>  > > carrier 4G, 5G to a mobile hotspot, however segmentation of the /64 via 
>  > > SLAAC is required so that downstream interfaces can be further subnetted. 
>  > > Example: uCPE device (4G + WI-FI enabled) receives /64 via Wireless, and 
>  > > assigns /72 to VNF-Firewall, /72 to WIFI, /72 to VNF-Router, /72 to 
>  > > Load-Balancer and /72 to wired connected devices. 
>  > > IETF document that defines problem statement: 
>  > > draft-mishra-v6ops-variable-slaac-problem-stmt 
>  > > IETF document that specifies variable slaac: 
>  > > draft-mishra-6man-variable-slaac 
>  > > 
>  > > Signed-off-by: Dmytro Shytyi <dmytro@shytyi.net> 
>  > > --- 
>  > > diff --git a/include/linux/ipv6.h b/include/linux/ipv6.h 
>  > > index dda61d150a13..67ca3925463c 100644 
>  > > --- a/include/linux/ipv6.h 
>  > > +++ b/include/linux/ipv6.h 
>  > > @@ -75,6 +75,7 @@ struct ipv6_devconf { 
>  > >      __s32        disable_policy; 
>  > >      __s32           ndisc_tclass; 
>  > >      __s32        rpl_seg_enabled; 
>  > > +    __s32        variable_slaac; 
>  > > 
>  > >      struct ctl_table_header *sysctl_header; 
>  > >  }; 
>  > > diff --git a/include/uapi/linux/ipv6.h b/include/uapi/linux/ipv6.h 
>  > > index 13e8751bf24a..f2af4f9fba2d 100644 
>  > > --- a/include/uapi/linux/ipv6.h 
>  > > +++ b/include/uapi/linux/ipv6.h 
>  > > @@ -189,7 +189,8 @@ enum { 
>  > >      DEVCONF_ACCEPT_RA_RT_INFO_MIN_PLEN, 
>  > >      DEVCONF_NDISC_TCLASS, 
>  > >      DEVCONF_RPL_SEG_ENABLED, 
>  > > -    DEVCONF_MAX 
>  > > +    DEVCONF_MAX, 
>  > > +    DEVCONF_VARIABLE_SLAAC 
>  > >  }; 
>  > > 
>  > > 
>  > > diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c 
>  > > index eff2cacd5209..07afe4ce984e 100644 
>  > > --- a/net/ipv6/addrconf.c 
>  > > +++ b/net/ipv6/addrconf.c 
>  > > @@ -236,6 +236,7 @@ static struct ipv6_devconf ipv6_devconf __read_mostly = { 
>  > >      .addr_gen_mode        = IN6_ADDR_GEN_MODE_EUI64, 
>  > >      .disable_policy        = 0, 
>  > >      .rpl_seg_enabled    = 0, 
>  > > +    .variable_slaac        = 0, 
>  > >  }; 
>  > > 
>  > >  static struct ipv6_devconf ipv6_devconf_dflt __read_mostly = { 
>  > > @@ -291,6 +292,7 @@ static struct ipv6_devconf ipv6_devconf_dflt __read_mostly = { 
>  > >      .addr_gen_mode        = IN6_ADDR_GEN_MODE_EUI64, 
>  > >      .disable_policy        = 0, 
>  > >      .rpl_seg_enabled    = 0, 
>  > > +    .variable_slaac        = 0, 
>  > >  }; 
>  > > 
>  > >  /* Check if link is ready: is it up and is a valid qdisc available */ 
>  > > @@ -1340,9 +1342,15 @@ static int ipv6_create_tempaddr(struct inet6_ifaddr *ifp, bool block) 
>  > >          goto out; 
>  > >      } 
>  > >      in6_ifa_hold(ifp); 
>  > > -    memcpy(addr.s6_addr, ifp->addr.s6_addr, 8); 
>  > > -    ipv6_gen_rnd_iid(&addr); 
>  > > 
>  > > +    if (ifp->prefix_len == 64) { 
>  > > +        memcpy(addr.s6_addr, ifp->addr.s6_addr, 8); 
>  > > +        ipv6_gen_rnd_iid(&addr); 
>  > > +    } else if (ifp->prefix_len > 0 && ifp->prefix_len <= 128 && 
>  > > +           idev->cnf.variable_slaac) { 
>  > > +        get_random_bytes(addr.s6_addr, 16); 
>  > > +        ipv6_addr_prefix_copy(&addr, &ifp->addr, ifp->prefix_len); 
>  > > +    } 
>  > >      age = (now - ifp->tstamp) / HZ; 
>  > > 
>  > >      regen_advance = idev->cnf.regen_max_retry * 
>  > > @@ -2569,6 +2577,37 @@ static bool is_addr_mode_generate_stable(struct inet6_dev *idev) 
>  > >             idev->cnf.addr_gen_mode == IN6_ADDR_GEN_MODE_RANDOM; 
>  > >  } 
>  > > 
>  > > +static struct inet6_ifaddr *ipv6_cmp_rcvd_prsnt_prfxs(struct inet6_ifaddr *ifp, 
>  > > +                              struct inet6_dev *in6_dev, 
>  > > +                              struct net *net, 
>  > > +                              const struct prefix_info *pinfo) 
>  > > +{ 
>  > > +    struct inet6_ifaddr *result_base = NULL; 
>  > > +    struct inet6_ifaddr *result = NULL; 
>  > > +    bool prfxs_equal; 
>  > > + 
>  > > +    result_base = result;   
>  >  
>  > This is NULL, are you sure you didn't mewan to init this to 'ifp' 
>  >  or similar instead?   
> 
> [Dmytro] I put the entire function to comment below the instructions.
> [Dmytro]:
> +static struct inet6_ifaddr *ipv6_cmp_rcvd_prsnt_prfxs(struct inet6_ifaddr *ifp,
> +                         struct inet6_dev *in6_dev,
> +                         struct net *net,
> +                         const struct prefix_info *pinfo)
> +{
> +    struct inet6_ifaddr *result_base = NULL;
> +    struct inet6_ifaddr *result = NULL;
> +    bool prfxs_equal;
> +
> +    result_base = result;
> +    rcu_read_lock();
> +    list_for_each_entry_rcu(ifp, &in6_dev->addr_list, if_list) {
> +        if (!net_eq(dev_net(ifp->idev->dev), net))
> +            continue;
> +        prfxs_equal =
> +            ipv6_prefix_equal(&pinfo->prefix, &ifp->addr, pinfo->prefix_len);
> +        if (prfxs_equal && pinfo->prefix_len == ifp->prefix_len) {
> +            result = ifp;
> +            in6_ifa_hold(ifp);
> +            break;
> +        }
> +    }
> +    rcu_read_unlock();
> +    if (result_base != result)
> +        ifp = result;
> +    else
> +        ifp = NULL;
> +
> +    return ifp;
> +}
> +
> 
> [Dmytro]:
> 1st initial stage is :
> +    result_base = result;
> 
> 2nd stage is (as you mention, 'result' will be assigned to 'ifp', in the process):
> +            result = ifp;
> 
> 3rd stage is to compare if  "result_base" and "result" are not equal (and take required action).
>  if (result_base != result)
> +        ifp = result;
> +    else
> +        ifp = NULL;
> 
> Looks more/less ok for me.

I think I see what you're trying to do here. Use result_base as a
"marker" or the base value?

But I'd say it makes the code harder to follow. It looks like this:

	result_base = NULL;
	result = NULL;

	result_base = result
	lock()
	for ...
		/* search logic */
	unlock()
	
	if (result == result_base)
		ifp = result
	else
		ifp = NULL
	return NULL

This would be a lot simpler, and functionally equivalent:

	result = NULL

	lock()
	for ...
		/* search logic */
	unlock()

	return result

Right?

^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH net-next V8] net: Variable SLAAC: SLAAC with prefixes of arbitrary length in PIO
  2020-12-16 17:28                           ` Jakub Kicinski
@ 2020-12-16 21:56                             ` Dmytro Shytyi
  0 siblings, 0 replies; 49+ messages in thread
From: Dmytro Shytyi @ 2020-12-16 21:56 UTC (permalink / raw)
  To: Jakub Kicinski
  Cc: David Miller, yoshfuji, kuznet, liuhangbin, netdev, linux-kernel

Hello Jakub,

---- On Wed, 16 Dec 2020 18:28:31 +0100 Jakub Kicinski <kuba@kernel.org> wrote ----

 > On Wed, 16 Dec 2020 15:01:33 +0100 Dmytro Shytyi wrote: 
 > > Hello David, 
 > > 
 > > Thank you for your comment. 
 > > Asnwers in-line. 
 > > 
 > > Take care, 
 > > 
 > > Dmytro SHYTYI 
 > > 
 > > 
 > > ---- On Wed, 16 Dec 2020 01:00:49 +0100 David Miller <davem@davemloft.net> wrote ---- 
 > > 
 > >  > From: Dmytro Shytyi <dmytro@shytyi.net> 
 > >  > Date: Wed, 09 Dec 2020 04:27:54 +0100 
 > >  > 
 > >  > > Variable SLAAC [Can be activated via sysctl]: 
 > >  > > SLAAC with prefixes of arbitrary length in PIO (randomly 
 > >  > > generated hostID or stable privacy + privacy extensions). 
 > >  > > The main problem is that SLAAC RA or PD allocates a /64 by the Wireless 
 > >  > > carrier 4G, 5G to a mobile hotspot, however segmentation of the /64 via 
 > >  > > SLAAC is required so that downstream interfaces can be further subnetted. 
 > >  > > Example: uCPE device (4G + WI-FI enabled) receives /64 via Wireless, and 
 > >  > > assigns /72 to VNF-Firewall, /72 to WIFI, /72 to VNF-Router, /72 to 
 > >  > > Load-Balancer and /72 to wired connected devices. 
 > >  > > IETF document that defines problem statement: 
 > >  > > draft-mishra-v6ops-variable-slaac-problem-stmt 
 > >  > > IETF document that specifies variable slaac: 
 > >  > > draft-mishra-6man-variable-slaac 
 > >  > > 
 > >  > > Signed-off-by: Dmytro Shytyi <dmytro@shytyi.net> 
 > >  > > --- 
 > >  > > diff --git a/include/linux/ipv6.h b/include/linux/ipv6.h 
 > >  > > index dda61d150a13..67ca3925463c 100644 
 > >  > > --- a/include/linux/ipv6.h 
 > >  > > +++ b/include/linux/ipv6.h 
 > >  > > @@ -75,6 +75,7 @@ struct ipv6_devconf { 
 > >  > >      __s32        disable_policy; 
 > >  > >      __s32           ndisc_tclass; 
 > >  > >      __s32        rpl_seg_enabled; 
 > >  > > +    __s32        variable_slaac; 
 > >  > > 
 > >  > >      struct ctl_table_header *sysctl_header; 
 > >  > >  }; 
 > >  > > diff --git a/include/uapi/linux/ipv6.h b/include/uapi/linux/ipv6.h 
 > >  > > index 13e8751bf24a..f2af4f9fba2d 100644 
 > >  > > --- a/include/uapi/linux/ipv6.h 
 > >  > > +++ b/include/uapi/linux/ipv6.h 
 > >  > > @@ -189,7 +189,8 @@ enum { 
 > >  > >      DEVCONF_ACCEPT_RA_RT_INFO_MIN_PLEN, 
 > >  > >      DEVCONF_NDISC_TCLASS, 
 > >  > >      DEVCONF_RPL_SEG_ENABLED, 
 > >  > > -    DEVCONF_MAX 
 > >  > > +    DEVCONF_MAX, 
 > >  > > +    DEVCONF_VARIABLE_SLAAC 
 > >  > >  }; 
 > >  > > 
 > >  > > 
 > >  > > diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c 
 > >  > > index eff2cacd5209..07afe4ce984e 100644 
 > >  > > --- a/net/ipv6/addrconf.c 
 > >  > > +++ b/net/ipv6/addrconf.c 
 > >  > > @@ -236,6 +236,7 @@ static struct ipv6_devconf ipv6_devconf __read_mostly = { 
 > >  > >      .addr_gen_mode        = IN6_ADDR_GEN_MODE_EUI64, 
 > >  > >      .disable_policy        = 0, 
 > >  > >      .rpl_seg_enabled    = 0, 
 > >  > > +    .variable_slaac        = 0, 
 > >  > >  }; 
 > >  > > 
 > >  > >  static struct ipv6_devconf ipv6_devconf_dflt __read_mostly = { 
 > >  > > @@ -291,6 +292,7 @@ static struct ipv6_devconf ipv6_devconf_dflt __read_mostly = { 
 > >  > >      .addr_gen_mode        = IN6_ADDR_GEN_MODE_EUI64, 
 > >  > >      .disable_policy        = 0, 
 > >  > >      .rpl_seg_enabled    = 0, 
 > >  > > +    .variable_slaac        = 0, 
 > >  > >  }; 
 > >  > > 
 > >  > >  /* Check if link is ready: is it up and is a valid qdisc available */ 
 > >  > > @@ -1340,9 +1342,15 @@ static int ipv6_create_tempaddr(struct inet6_ifaddr *ifp, bool block) 
 > >  > >          goto out; 
 > >  > >      } 
 > >  > >      in6_ifa_hold(ifp); 
 > >  > > -    memcpy(addr.s6_addr, ifp->addr.s6_addr, 8); 
 > >  > > -    ipv6_gen_rnd_iid(&addr); 
 > >  > > 
 > >  > > +    if (ifp->prefix_len == 64) { 
 > >  > > +        memcpy(addr.s6_addr, ifp->addr.s6_addr, 8); 
 > >  > > +        ipv6_gen_rnd_iid(&addr); 
 > >  > > +    } else if (ifp->prefix_len > 0 && ifp->prefix_len <= 128 && 
 > >  > > +           idev->cnf.variable_slaac) { 
 > >  > > +        get_random_bytes(addr.s6_addr, 16); 
 > >  > > +        ipv6_addr_prefix_copy(&addr, &ifp->addr, ifp->prefix_len); 
 > >  > > +    } 
 > >  > >      age = (now - ifp->tstamp) / HZ; 
 > >  > > 
 > >  > >      regen_advance = idev->cnf.regen_max_retry * 
 > >  > > @@ -2569,6 +2577,37 @@ static bool is_addr_mode_generate_stable(struct inet6_dev *idev) 
 > >  > >             idev->cnf.addr_gen_mode == IN6_ADDR_GEN_MODE_RANDOM; 
 > >  > >  } 
 > >  > > 
 > >  > > +static struct inet6_ifaddr *ipv6_cmp_rcvd_prsnt_prfxs(struct inet6_ifaddr *ifp, 
 > >  > > +                              struct inet6_dev *in6_dev, 
 > >  > > +                              struct net *net, 
 > >  > > +                              const struct prefix_info *pinfo) 
 > >  > > +{ 
 > >  > > +    struct inet6_ifaddr *result_base = NULL; 
 > >  > > +    struct inet6_ifaddr *result = NULL; 
 > >  > > +    bool prfxs_equal; 
 > >  > > + 
 > >  > > +    result_base = result; 
 > >  > 
 > >  > This is NULL, are you sure you didn't mewan to init this to 'ifp' 
 > >  >  or similar instead? 
 > > 
 > > [Dmytro] I put the entire function to comment below the instructions. 
 > > [Dmytro]: 
 > > +static struct inet6_ifaddr *ipv6_cmp_rcvd_prsnt_prfxs(struct inet6_ifaddr *ifp, 
 > > +                         struct inet6_dev *in6_dev, 
 > > +                         struct net *net, 
 > > +                         const struct prefix_info *pinfo) 
 > > +{ 
 > > +    struct inet6_ifaddr *result_base = NULL; 
 > > +    struct inet6_ifaddr *result = NULL; 
 > > +    bool prfxs_equal; 
 > > + 
 > > +    result_base = result; 
 > > +    rcu_read_lock(); 
 > > +    list_for_each_entry_rcu(ifp, &in6_dev->addr_list, if_list) { 
 > > +        if (!net_eq(dev_net(ifp->idev->dev), net)) 
 > > +            continue; 
 > > +        prfxs_equal = 
 > > +            ipv6_prefix_equal(&pinfo->prefix, &ifp->addr, pinfo->prefix_len); 
 > > +        if (prfxs_equal && pinfo->prefix_len == ifp->prefix_len) { 
 > > +            result = ifp; 
 > > +            in6_ifa_hold(ifp); 
 > > +            break; 
 > > +        } 
 > > +    } 
 > > +    rcu_read_unlock(); 
 > > +    if (result_base != result) 
 > > +        ifp = result; 
 > > +    else 
 > > +        ifp = NULL; 
 > > + 
 > > +    return ifp; 
 > > +} 
 > > + 
 > > 
 > > [Dmytro]: 
 > > 1st initial stage is : 
 > > +    result_base = result; 
 > > 
 > > 2nd stage is (as you mention, 'result' will be assigned to 'ifp', in the process): 
 > > +            result = ifp; 
 > > 
 > > 3rd stage is to compare if  "result_base" and "result" are not equal (and take required action). 
 > >  if (result_base != result) 
 > > +        ifp = result; 
 > > +    else 
 > > +        ifp = NULL; 
 > > 
 > > Looks more/less ok for me. 
 >  
 > I think I see what you're trying to do here. Use result_base as a 
 > "marker" or the base value? 
 >  
 > But I'd say it makes the code harder to follow. It looks like this: 
 >  
 >     result_base = NULL; 
 >     result = NULL; 
 >  
 >     result_base = result 
 >     lock() 
 >     for ... 
 >         /* search logic */ 
 >     unlock() 
 >      
 >     if (result == result_base) 
 >         ifp = result 
 >     else 
 >         ifp = NULL 
 >     return NULL 
 >  
 > This would be a lot simpler, and functionally equivalent: 
 >  
 >     result = NULL 
 >  
 >     lock() 
 >     for ... 
 >         /* search logic */ 
 >     unlock() 
 >  
 >     return result 
 >  
 > Right? 
 > 
[Dmytro]: I see and I agree. Understood.


^ permalink raw reply	[flat|nested] 49+ messages in thread

* [PATCH net-next V9] net: Variable SLAAC: SLAAC with prefixes of arbitrary length in PIO
  2020-12-09  3:27                     ` [PATCH net-next V8] " Dmytro Shytyi
  2020-12-16  0:00                       ` David Miller
@ 2020-12-16 22:01                       ` Dmytro Shytyi
  2020-12-19  2:03                         ` Jakub Kicinski
  2021-10-13 23:03                         ` [PATCH net-next V10] " Dmytro Shytyi
  1 sibling, 2 replies; 49+ messages in thread
From: Dmytro Shytyi @ 2020-12-16 22:01 UTC (permalink / raw)
  To: Jakub Kicinski, yoshfuji, kuznet, liuhangbin, davem, netdev,
	linux-kernel

Variable SLAAC [Can be activated via sysctl]: 
SLAAC with prefixes of arbitrary length in PIO (randomly
generated hostID or stable privacy + privacy extensions).
The main problem is that SLAAC RA or PD allocates a /64 by the Wireless
carrier 4G, 5G to a mobile hotspot, however segmentation of the /64 via
SLAAC is required so that downstream interfaces can be further subnetted.
Example: uCPE device (4G + WI-FI enabled) receives /64 via Wireless, and
assigns /72 to VNF-Firewall, /72 to WIFI, /72 to Load-Balancer 
and /72 to wired connected devices.
IETF document that defines problem statement:
draft-mishra-v6ops-variable-slaac-problem-stmt
IETF document that specifies variable slaac:
draft-mishra-6man-variable-slaac

Signed-off-by: Dmytro Shytyi <dmytro@shytyi.net>
---
diff --git a/include/linux/ipv6.h b/include/linux/ipv6.h
index dda61d150a13..67ca3925463c 100644
--- a/include/linux/ipv6.h
+++ b/include/linux/ipv6.h
@@ -75,6 +75,7 @@ struct ipv6_devconf {
 	__s32		disable_policy;
 	__s32           ndisc_tclass;
 	__s32		rpl_seg_enabled;
+	__s32		variable_slaac;
 
 	struct ctl_table_header *sysctl_header;
 };
diff --git a/include/uapi/linux/ipv6.h b/include/uapi/linux/ipv6.h
index 13e8751bf24a..f2af4f9fba2d 100644
--- a/include/uapi/linux/ipv6.h
+++ b/include/uapi/linux/ipv6.h
@@ -189,7 +189,8 @@ enum {
 	DEVCONF_ACCEPT_RA_RT_INFO_MIN_PLEN,
 	DEVCONF_NDISC_TCLASS,
 	DEVCONF_RPL_SEG_ENABLED,
-	DEVCONF_MAX
+	DEVCONF_MAX,
+	DEVCONF_VARIABLE_SLAAC
 };
 
 
diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c
index eff2cacd5209..4afaf2bc8d8b 100644
--- a/net/ipv6/addrconf.c
+++ b/net/ipv6/addrconf.c
@@ -236,6 +236,7 @@ static struct ipv6_devconf ipv6_devconf __read_mostly = {
 	.addr_gen_mode		= IN6_ADDR_GEN_MODE_EUI64,
 	.disable_policy		= 0,
 	.rpl_seg_enabled	= 0,
+	.variable_slaac		= 0,
 };
 
 static struct ipv6_devconf ipv6_devconf_dflt __read_mostly = {
@@ -291,6 +292,7 @@ static struct ipv6_devconf ipv6_devconf_dflt __read_mostly = {
 	.addr_gen_mode		= IN6_ADDR_GEN_MODE_EUI64,
 	.disable_policy		= 0,
 	.rpl_seg_enabled	= 0,
+	.variable_slaac		= 0,
 };
 
 /* Check if link is ready: is it up and is a valid qdisc available */
@@ -1340,9 +1342,15 @@ static int ipv6_create_tempaddr(struct inet6_ifaddr *ifp, bool block)
 		goto out;
 	}
 	in6_ifa_hold(ifp);
-	memcpy(addr.s6_addr, ifp->addr.s6_addr, 8);
-	ipv6_gen_rnd_iid(&addr);
 
+	if (ifp->prefix_len == 64) {
+		memcpy(addr.s6_addr, ifp->addr.s6_addr, 8);
+		ipv6_gen_rnd_iid(&addr);
+	} else if (ifp->prefix_len > 0 && ifp->prefix_len <= 128 &&
+		   idev->cnf.variable_slaac) {
+		get_random_bytes(addr.s6_addr, 16);
+		ipv6_addr_prefix_copy(&addr, &ifp->addr, ifp->prefix_len);
+	}
 	age = (now - ifp->tstamp) / HZ;
 
 	regen_advance = idev->cnf.regen_max_retry *
@@ -2569,6 +2577,31 @@ static bool is_addr_mode_generate_stable(struct inet6_dev *idev)
 	       idev->cnf.addr_gen_mode == IN6_ADDR_GEN_MODE_RANDOM;
 }
 
+static struct inet6_ifaddr *ipv6_cmp_rcvd_prsnt_prfxs(struct inet6_ifaddr *ifp,
+						      struct inet6_dev *in6_dev,
+						      struct net *net,
+						      const struct prefix_info *pinfo)
+{
+	struct inet6_ifaddr *result = NULL;
+	bool prfxs_equal;
+
+	rcu_read_lock();
+	list_for_each_entry_rcu(ifp, &in6_dev->addr_list, if_list) {
+		if (!net_eq(dev_net(ifp->idev->dev), net))
+			continue;
+		prfxs_equal =
+			ipv6_prefix_equal(&pinfo->prefix, &ifp->addr, pinfo->prefix_len);
+		if (prfxs_equal && pinfo->prefix_len == ifp->prefix_len) {
+			result = ifp;
+			in6_ifa_hold(ifp);
+			break;
+		}
+	}
+	rcu_read_unlock();
+
+	return result;
+}
+
 int addrconf_prefix_rcv_add_addr(struct net *net, struct net_device *dev,
 				 const struct prefix_info *pinfo,
 				 struct inet6_dev *in6_dev,
@@ -2576,9 +2609,17 @@ int addrconf_prefix_rcv_add_addr(struct net *net, struct net_device *dev,
 				 u32 addr_flags, bool sllao, bool tokenized,
 				 __u32 valid_lft, u32 prefered_lft)
 {
-	struct inet6_ifaddr *ifp = ipv6_get_ifaddr(net, addr, dev, 1);
+	struct inet6_ifaddr *ifp = NULL;
+	int plen = pinfo->prefix_len;
 	int create = 0;
 
+	if (plen > 0 && plen <= 128 && plen != 64 &&
+	    in6_dev->cnf.addr_gen_mode != IN6_ADDR_GEN_MODE_STABLE_PRIVACY &&
+	    in6_dev->cnf.variable_slaac)
+		ifp = ipv6_cmp_rcvd_prsnt_prfxs(ifp, in6_dev, net, pinfo);
+	else
+		ifp = ipv6_get_ifaddr(net, addr, dev, 1);
+
 	if (!ifp && valid_lft) {
 		int max_addresses = in6_dev->cnf.max_addresses;
 		struct ifa6_config cfg = {
@@ -2657,6 +2698,90 @@ int addrconf_prefix_rcv_add_addr(struct net *net, struct net_device *dev,
 }
 EXPORT_SYMBOL_GPL(addrconf_prefix_rcv_add_addr);
 
+static bool ipv6_reserved_interfaceid(struct in6_addr address)
+{
+	if ((address.s6_addr32[2] | address.s6_addr32[3]) == 0)
+		return true;
+
+	if (address.s6_addr32[2] == htonl(0x02005eff) &&
+	    ((address.s6_addr32[3] & htonl(0xfe000000)) == htonl(0xfe000000)))
+		return true;
+
+	if (address.s6_addr32[2] == htonl(0xfdffffff) &&
+	    ((address.s6_addr32[3] & htonl(0xffffff80)) == htonl(0xffffff80)))
+		return true;
+
+	return false;
+}
+
+static int ipv6_gen_addr_var_plen(struct in6_addr *address,
+				  u8 dad_count,
+				  const struct inet6_dev *idev,
+				  unsigned int rcvd_prfx_len,
+				  bool stable_privacy_mode)
+{
+	static union {
+		char __data[SHA1_BLOCK_SIZE];
+		struct {
+			struct in6_addr secret;
+			__be32 prefix[2];
+			unsigned char hwaddr[MAX_ADDR_LEN];
+			u8 dad_count;
+		} __packed;
+	} data;
+	static __u32 workspace[SHA1_WORKSPACE_WORDS];
+	static __u32 digest[SHA1_DIGEST_WORDS];
+	struct net *net = dev_net(idev->dev);
+	static DEFINE_SPINLOCK(lock);
+	struct in6_addr secret;
+	struct in6_addr temp;
+
+	BUILD_BUG_ON(sizeof(data.__data) != sizeof(data));
+
+	if (stable_privacy_mode) {
+		if (idev->cnf.stable_secret.initialized)
+			secret = idev->cnf.stable_secret.secret;
+		else if (net->ipv6.devconf_dflt->stable_secret.initialized)
+			secret = net->ipv6.devconf_dflt->stable_secret.secret;
+		else
+			return -1;
+	}
+
+retry:
+	spin_lock_bh(&lock);
+	if (stable_privacy_mode) {
+		sha1_init(digest);
+		memset(&data, 0, sizeof(data));
+		memset(workspace, 0, sizeof(workspace));
+		memcpy(data.hwaddr, idev->dev->perm_addr, idev->dev->addr_len);
+		data.prefix[0] = address->s6_addr32[0];
+		data.prefix[1] = address->s6_addr32[1];
+		data.secret = secret;
+		data.dad_count = dad_count;
+
+		sha1_transform(digest, data.__data, workspace);
+
+		temp.s6_addr32[0] = (__force __be32)digest[0];
+		temp.s6_addr32[1] = (__force __be32)digest[1];
+		temp.s6_addr32[2] = (__force __be32)digest[2];
+		temp.s6_addr32[3] = (__force __be32)digest[3];
+	} else {
+		get_random_bytes(temp.s6_addr32, 16);
+	}
+
+	spin_unlock_bh(&lock);
+
+	if (ipv6_reserved_interfaceid(temp)) {
+		dad_count++;
+		if (dad_count > dev_net(idev->dev)->ipv6.sysctl.idgen_retries)
+			return -1;
+		goto retry;
+	}
+	ipv6_addr_prefix_copy(&temp, address, rcvd_prfx_len);
+	*address = temp;
+	return 0;
+}
+
 void addrconf_prefix_rcv(struct net_device *dev, u8 *opt, int len, bool sllao)
 {
 	struct prefix_info *pinfo;
@@ -2781,9 +2906,34 @@ void addrconf_prefix_rcv(struct net_device *dev, u8 *opt, int len, bool sllao)
 				dev_addr_generated = true;
 			}
 			goto ok;
+		} else if (pinfo->prefix_len != 64 &&
+			   pinfo->prefix_len > 0 && pinfo->prefix_len <= 128 &&
+			   in6_dev->cnf.variable_slaac) {
+			/* SLAAC with prefixes of arbitrary length (Variable SLAAC).
+			 * draft-mishra-6man-variable-slaac
+			 * draft-mishra-v6ops-variable-slaac-problem-stmt
+			 */
+			memcpy(&addr, &pinfo->prefix, 16);
+			if (in6_dev->cnf.addr_gen_mode == IN6_ADDR_GEN_MODE_STABLE_PRIVACY) {
+				if (!ipv6_gen_addr_var_plen(&addr,
+							    0,
+							    in6_dev,
+							    pinfo->prefix_len,
+							    true)) {
+					addr_flags |= IFA_F_STABLE_PRIVACY;
+					goto ok;
+				}
+			} else if (!ipv6_gen_addr_var_plen(&addr,
+							   0,
+							   in6_dev,
+							   pinfo->prefix_len,
+							   false)) {
+				goto ok;
+			}
+		} else {
+			net_dbg_ratelimited("IPv6: Prefix with unexpected length %d\n",
+					    pinfo->prefix_len);
 		}
-		net_dbg_ratelimited("IPv6 addrconf: prefix with wrong length %d\n",
-				    pinfo->prefix_len);
 		goto put;
 
 ok:
@@ -3186,22 +3336,6 @@ void addrconf_add_linklocal(struct inet6_dev *idev,
 }
 EXPORT_SYMBOL_GPL(addrconf_add_linklocal);
 
-static bool ipv6_reserved_interfaceid(struct in6_addr address)
-{
-	if ((address.s6_addr32[2] | address.s6_addr32[3]) == 0)
-		return true;
-
-	if (address.s6_addr32[2] == htonl(0x02005eff) &&
-	    ((address.s6_addr32[3] & htonl(0xfe000000)) == htonl(0xfe000000)))
-		return true;
-
-	if (address.s6_addr32[2] == htonl(0xfdffffff) &&
-	    ((address.s6_addr32[3] & htonl(0xffffff80)) == htonl(0xffffff80)))
-		return true;
-
-	return false;
-}
-
 static int ipv6_generate_stable_address(struct in6_addr *address,
 					u8 dad_count,
 					const struct inet6_dev *idev)
@@ -5517,6 +5651,7 @@ static inline void ipv6_store_devconf(struct ipv6_devconf *cnf,
 	array[DEVCONF_DISABLE_POLICY] = cnf->disable_policy;
 	array[DEVCONF_NDISC_TCLASS] = cnf->ndisc_tclass;
 	array[DEVCONF_RPL_SEG_ENABLED] = cnf->rpl_seg_enabled;
+	array[DEVCONF_VARIABLE_SLAAC] = cnf->variable_slaac;
 }
 
 static inline size_t inet6_ifla6_size(void)
@@ -6897,6 +7032,13 @@ static const struct ctl_table addrconf_sysctl[] = {
 		.mode		= 0644,
 		.proc_handler	= proc_dointvec,
 	},
+	{
+		.procname	= "variable_slaac",
+		.data		= &ipv6_devconf.variable_slaac,
+		.maxlen		= sizeof(int),
+		.mode		= 0644,
+		.proc_handler	= proc_dointvec,
+	},
 	{
 		/* sentinel */
 	}

^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH net-next V9] net: Variable SLAAC: SLAAC with prefixes of arbitrary length in PIO
  2020-12-16 22:01                       ` [PATCH net-next V9] " Dmytro Shytyi
@ 2020-12-19  2:03                         ` Jakub Kicinski
  2020-12-19  2:40                           ` Maciej Żenczykowski
  2021-07-10 19:24                           ` Dmytro Shytyi
  2021-10-13 23:03                         ` [PATCH net-next V10] " Dmytro Shytyi
  1 sibling, 2 replies; 49+ messages in thread
From: Jakub Kicinski @ 2020-12-19  2:03 UTC (permalink / raw)
  To: Dmytro Shytyi
  Cc: yoshfuji, liuhangbin, davem, netdev, David Ahern, Joel Scherpelz,
	Maciej Żenczykowski

It'd be great if someone more familiar with our IPv6 code could take a
look. Adding some folks to the CC.

On Wed, 16 Dec 2020 23:01:29 +0100 Dmytro Shytyi wrote:
> Variable SLAAC [Can be activated via sysctl]: 
> SLAAC with prefixes of arbitrary length in PIO (randomly
> generated hostID or stable privacy + privacy extensions).
> The main problem is that SLAAC RA or PD allocates a /64 by the Wireless
> carrier 4G, 5G to a mobile hotspot, however segmentation of the /64 via
> SLAAC is required so that downstream interfaces can be further subnetted.
> Example: uCPE device (4G + WI-FI enabled) receives /64 via Wireless, and
> assigns /72 to VNF-Firewall, /72 to WIFI, /72 to Load-Balancer 
> and /72 to wired connected devices.
> IETF document that defines problem statement:
> draft-mishra-v6ops-variable-slaac-problem-stmt
> IETF document that specifies variable slaac:
> draft-mishra-6man-variable-slaac
> 
> Signed-off-by: Dmytro Shytyi <dmytro@shytyi.net>

The RFC mentions checking a flag in RA, but I don't see that in this
patch, could you explain?

> diff --git a/include/uapi/linux/ipv6.h b/include/uapi/linux/ipv6.h
> index 13e8751bf24a..f2af4f9fba2d 100644
> --- a/include/uapi/linux/ipv6.h
> +++ b/include/uapi/linux/ipv6.h
> @@ -189,7 +189,8 @@ enum {
>  	DEVCONF_ACCEPT_RA_RT_INFO_MIN_PLEN,
>  	DEVCONF_NDISC_TCLASS,
>  	DEVCONF_RPL_SEG_ENABLED,
> -	DEVCONF_MAX
> +	DEVCONF_MAX,

MAX should be the last field, no? Isn't it used for sizing tables?

> +	DEVCONF_VARIABLE_SLAAC
>  };
>  
>  
> diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c
> index eff2cacd5209..4afaf2bc8d8b 100644
> --- a/net/ipv6/addrconf.c
> +++ b/net/ipv6/addrconf.c
> @@ -236,6 +236,7 @@ static struct ipv6_devconf ipv6_devconf __read_mostly = {
>  	.addr_gen_mode		= IN6_ADDR_GEN_MODE_EUI64,
>  	.disable_policy		= 0,
>  	.rpl_seg_enabled	= 0,
> +	.variable_slaac		= 0,
>  };
>  
>  static struct ipv6_devconf ipv6_devconf_dflt __read_mostly = {
> @@ -291,6 +292,7 @@ static struct ipv6_devconf ipv6_devconf_dflt __read_mostly = {
>  	.addr_gen_mode		= IN6_ADDR_GEN_MODE_EUI64,
>  	.disable_policy		= 0,
>  	.rpl_seg_enabled	= 0,
> +	.variable_slaac		= 0,
>  };
>  
>  /* Check if link is ready: is it up and is a valid qdisc available */
> @@ -1340,9 +1342,15 @@ static int ipv6_create_tempaddr(struct inet6_ifaddr *ifp, bool block)
>  		goto out;
>  	}
>  	in6_ifa_hold(ifp);
> -	memcpy(addr.s6_addr, ifp->addr.s6_addr, 8);
> -	ipv6_gen_rnd_iid(&addr);
>  
> +	if (ifp->prefix_len == 64) {
> +		memcpy(addr.s6_addr, ifp->addr.s6_addr, 8);
> +		ipv6_gen_rnd_iid(&addr);
> +	} else if (ifp->prefix_len > 0 && ifp->prefix_len <= 128 &&
> +		   idev->cnf.variable_slaac) {
> +		get_random_bytes(addr.s6_addr, 16);
> +		ipv6_addr_prefix_copy(&addr, &ifp->addr, ifp->prefix_len);
> +	}
>  	age = (now - ifp->tstamp) / HZ;
>  
>  	regen_advance = idev->cnf.regen_max_retry *
> @@ -2569,6 +2577,31 @@ static bool is_addr_mode_generate_stable(struct inet6_dev *idev)
>  	       idev->cnf.addr_gen_mode == IN6_ADDR_GEN_MODE_RANDOM;
>  }
>  
> +static struct inet6_ifaddr *ipv6_cmp_rcvd_prsnt_prfxs(struct inet6_ifaddr *ifp,
> +						      struct inet6_dev *in6_dev,
> +						      struct net *net,
> +						      const struct prefix_info *pinfo)
> +{
> +	struct inet6_ifaddr *result = NULL;
> +	bool prfxs_equal;
> +
> +	rcu_read_lock();
> +	list_for_each_entry_rcu(ifp, &in6_dev->addr_list, if_list) {
> +		if (!net_eq(dev_net(ifp->idev->dev), net))
> +			continue;
> +		prfxs_equal =
> +			ipv6_prefix_equal(&pinfo->prefix, &ifp->addr, pinfo->prefix_len);
> +		if (prfxs_equal && pinfo->prefix_len == ifp->prefix_len) {
> +			result = ifp;
> +			in6_ifa_hold(ifp);
> +			break;
> +		}
> +	}
> +	rcu_read_unlock();
> +
> +	return result;
> +}
> +
>  int addrconf_prefix_rcv_add_addr(struct net *net, struct net_device *dev,
>  				 const struct prefix_info *pinfo,
>  				 struct inet6_dev *in6_dev,
> @@ -2576,9 +2609,17 @@ int addrconf_prefix_rcv_add_addr(struct net *net, struct net_device *dev,
>  				 u32 addr_flags, bool sllao, bool tokenized,
>  				 __u32 valid_lft, u32 prefered_lft)
>  {
> -	struct inet6_ifaddr *ifp = ipv6_get_ifaddr(net, addr, dev, 1);
> +	struct inet6_ifaddr *ifp = NULL;
> +	int plen = pinfo->prefix_len;
>  	int create = 0;
>  
> +	if (plen > 0 && plen <= 128 && plen != 64 &&
> +	    in6_dev->cnf.addr_gen_mode != IN6_ADDR_GEN_MODE_STABLE_PRIVACY &&
> +	    in6_dev->cnf.variable_slaac)
> +		ifp = ipv6_cmp_rcvd_prsnt_prfxs(ifp, in6_dev, net, pinfo);
> +	else
> +		ifp = ipv6_get_ifaddr(net, addr, dev, 1);
> +
>  	if (!ifp && valid_lft) {
>  		int max_addresses = in6_dev->cnf.max_addresses;
>  		struct ifa6_config cfg = {
> @@ -2657,6 +2698,90 @@ int addrconf_prefix_rcv_add_addr(struct net *net, struct net_device *dev,
>  }
>  EXPORT_SYMBOL_GPL(addrconf_prefix_rcv_add_addr);
>  
> +static bool ipv6_reserved_interfaceid(struct in6_addr address)
> +{
> +	if ((address.s6_addr32[2] | address.s6_addr32[3]) == 0)
> +		return true;
> +
> +	if (address.s6_addr32[2] == htonl(0x02005eff) &&
> +	    ((address.s6_addr32[3] & htonl(0xfe000000)) == htonl(0xfe000000)))
> +		return true;
> +
> +	if (address.s6_addr32[2] == htonl(0xfdffffff) &&
> +	    ((address.s6_addr32[3] & htonl(0xffffff80)) == htonl(0xffffff80)))
> +		return true;
> +
> +	return false;
> +}
> +
> +static int ipv6_gen_addr_var_plen(struct in6_addr *address,
> +				  u8 dad_count,
> +				  const struct inet6_dev *idev,
> +				  unsigned int rcvd_prfx_len,
> +				  bool stable_privacy_mode)
> +{
> +	static union {
> +		char __data[SHA1_BLOCK_SIZE];
> +		struct {
> +			struct in6_addr secret;
> +			__be32 prefix[2];
> +			unsigned char hwaddr[MAX_ADDR_LEN];
> +			u8 dad_count;
> +		} __packed;
> +	} data;
> +	static __u32 workspace[SHA1_WORKSPACE_WORDS];
> +	static __u32 digest[SHA1_DIGEST_WORDS];
> +	struct net *net = dev_net(idev->dev);
> +	static DEFINE_SPINLOCK(lock);
> +	struct in6_addr secret;
> +	struct in6_addr temp;
> +
> +	BUILD_BUG_ON(sizeof(data.__data) != sizeof(data));
> +
> +	if (stable_privacy_mode) {
> +		if (idev->cnf.stable_secret.initialized)
> +			secret = idev->cnf.stable_secret.secret;
> +		else if (net->ipv6.devconf_dflt->stable_secret.initialized)
> +			secret = net->ipv6.devconf_dflt->stable_secret.secret;
> +		else
> +			return -1;
> +	}
> +
> +retry:
> +	spin_lock_bh(&lock);
> +	if (stable_privacy_mode) {
> +		sha1_init(digest);
> +		memset(&data, 0, sizeof(data));
> +		memset(workspace, 0, sizeof(workspace));
> +		memcpy(data.hwaddr, idev->dev->perm_addr, idev->dev->addr_len);
> +		data.prefix[0] = address->s6_addr32[0];
> +		data.prefix[1] = address->s6_addr32[1];
> +		data.secret = secret;
> +		data.dad_count = dad_count;
> +
> +		sha1_transform(digest, data.__data, workspace);
> +
> +		temp.s6_addr32[0] = (__force __be32)digest[0];
> +		temp.s6_addr32[1] = (__force __be32)digest[1];
> +		temp.s6_addr32[2] = (__force __be32)digest[2];
> +		temp.s6_addr32[3] = (__force __be32)digest[3];
> +	} else {
> +		get_random_bytes(temp.s6_addr32, 16);
> +	}
> +
> +	spin_unlock_bh(&lock);

Is there a reason this code declares all this state on the stack and
protects it with a lock rather than just allocating the memory with
kmalloc()?

> +	if (ipv6_reserved_interfaceid(temp)) {
> +		dad_count++;
> +		if (dad_count > dev_net(idev->dev)->ipv6.sysctl.idgen_retries)
> +			return -1;
> +		goto retry;
> +	}
> +	ipv6_addr_prefix_copy(&temp, address, rcvd_prfx_len);
> +	*address = temp;
> +	return 0;
> +}
> +
>  void addrconf_prefix_rcv(struct net_device *dev, u8 *opt, int len, bool sllao)
>  {
>  	struct prefix_info *pinfo;
> @@ -2781,9 +2906,34 @@ void addrconf_prefix_rcv(struct net_device *dev, u8 *opt, int len, bool sllao)
>  				dev_addr_generated = true;
>  			}
>  			goto ok;
> +		} else if (pinfo->prefix_len != 64 &&
> +			   pinfo->prefix_len > 0 && pinfo->prefix_len <= 128 &&
> +			   in6_dev->cnf.variable_slaac) {
> +			/* SLAAC with prefixes of arbitrary length (Variable SLAAC).
> +			 * draft-mishra-6man-variable-slaac
> +			 * draft-mishra-v6ops-variable-slaac-problem-stmt
> +			 */
> +			memcpy(&addr, &pinfo->prefix, 16);
> +			if (in6_dev->cnf.addr_gen_mode == IN6_ADDR_GEN_MODE_STABLE_PRIVACY) {
> +				if (!ipv6_gen_addr_var_plen(&addr,
> +							    0,
> +							    in6_dev,
> +							    pinfo->prefix_len,
> +							    true)) {
> +					addr_flags |= IFA_F_STABLE_PRIVACY;
> +					goto ok;
> +				}
> +			} else if (!ipv6_gen_addr_var_plen(&addr,
> +							   0,
> +							   in6_dev,
> +							   pinfo->prefix_len,
> +							   false)) {
> +				goto ok;
> +			}
> +		} else {
> +			net_dbg_ratelimited("IPv6: Prefix with unexpected length %d\n",
> +					    pinfo->prefix_len);
>  		}
> -		net_dbg_ratelimited("IPv6 addrconf: prefix with wrong length %d\n",
> -				    pinfo->prefix_len);
>  		goto put;
>  
>  ok:
> @@ -3186,22 +3336,6 @@ void addrconf_add_linklocal(struct inet6_dev *idev,
>  }
>  EXPORT_SYMBOL_GPL(addrconf_add_linklocal);
>  
> -static bool ipv6_reserved_interfaceid(struct in6_addr address)
> -{
> -	if ((address.s6_addr32[2] | address.s6_addr32[3]) == 0)
> -		return true;
> -
> -	if (address.s6_addr32[2] == htonl(0x02005eff) &&
> -	    ((address.s6_addr32[3] & htonl(0xfe000000)) == htonl(0xfe000000)))
> -		return true;
> -
> -	if (address.s6_addr32[2] == htonl(0xfdffffff) &&
> -	    ((address.s6_addr32[3] & htonl(0xffffff80)) == htonl(0xffffff80)))
> -		return true;
> -
> -	return false;
> -}
> -
>  static int ipv6_generate_stable_address(struct in6_addr *address,
>  					u8 dad_count,
>  					const struct inet6_dev *idev)
> @@ -5517,6 +5651,7 @@ static inline void ipv6_store_devconf(struct ipv6_devconf *cnf,
>  	array[DEVCONF_DISABLE_POLICY] = cnf->disable_policy;
>  	array[DEVCONF_NDISC_TCLASS] = cnf->ndisc_tclass;
>  	array[DEVCONF_RPL_SEG_ENABLED] = cnf->rpl_seg_enabled;
> +	array[DEVCONF_VARIABLE_SLAAC] = cnf->variable_slaac;
>  }
>  
>  static inline size_t inet6_ifla6_size(void)
> @@ -6897,6 +7032,13 @@ static const struct ctl_table addrconf_sysctl[] = {
>  		.mode		= 0644,
>  		.proc_handler	= proc_dointvec,
>  	},
> +	{
> +		.procname	= "variable_slaac",
> +		.data		= &ipv6_devconf.variable_slaac,
> +		.maxlen		= sizeof(int),
> +		.mode		= 0644,
> +		.proc_handler	= proc_dointvec,
> +	},
>  	{
>  		/* sentinel */
>  	}


^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH net-next V9] net: Variable SLAAC: SLAAC with prefixes of arbitrary length in PIO
  2020-12-19  2:03                         ` Jakub Kicinski
@ 2020-12-19  2:40                           ` Maciej Żenczykowski
  2021-07-12 13:39                             ` Dmytro Shytyi
  2021-07-10 19:24                           ` Dmytro Shytyi
  1 sibling, 1 reply; 49+ messages in thread
From: Maciej Żenczykowski @ 2020-12-19  2:40 UTC (permalink / raw)
  To: Jakub Kicinski
  Cc: Dmytro Shytyi, yoshfuji, liuhangbin, davem, netdev, David Ahern,
	Joel Scherpelz

On Fri, Dec 18, 2020 at 6:03 PM Jakub Kicinski <kuba@kernel.org> wrote:
>
> It'd be great if someone more familiar with our IPv6 code could take a
> look. Adding some folks to the CC.
>
> On Wed, 16 Dec 2020 23:01:29 +0100 Dmytro Shytyi wrote:
> > Variable SLAAC [Can be activated via sysctl]:
> > SLAAC with prefixes of arbitrary length in PIO (randomly
> > generated hostID or stable privacy + privacy extensions).
> > The main problem is that SLAAC RA or PD allocates a /64 by the Wireless
> > carrier 4G, 5G to a mobile hotspot, however segmentation of the /64 via
> > SLAAC is required so that downstream interfaces can be further subnetted.
> > Example: uCPE device (4G + WI-FI enabled) receives /64 via Wireless, and
> > assigns /72 to VNF-Firewall, /72 to WIFI, /72 to Load-Balancer
> > and /72 to wired connected devices.
> > IETF document that defines problem statement:
> > draft-mishra-v6ops-variable-slaac-problem-stmt
> > IETF document that specifies variable slaac:
> > draft-mishra-6man-variable-slaac
> >
> > Signed-off-by: Dmytro Shytyi <dmytro@shytyi.net>
>
> The RFC mentions checking a flag in RA, but I don't see that in this
> patch, could you explain?

IMHO acceptance of this should *definitely* wait for the RFC to be
accepted/published/standardized (whatever is the right term).

I'm not at all convinced that will happen - this still seems like a
very fresh *draft* of an rfc,
and I'm *sure* it will be argued about.

This sort of functionality will not be particularly useful without
widespread industry
adoption across *all* major operating systems (Windows, Mac/iOS,
Linux/Android, FreeBSD, etc.)
Additionally rollout will take years (due to need for OS updates), so
waiting a few
more months/quarters for the RFC to actually be agreed upon (assuming
it ever is),
will not hurt us.

An implementation that is incompatible with the published RFC will
hurt us more then help us.

Maciej Żenczykowski, Kernel Networking Developer @ Google

^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH net-next V9] net: Variable SLAAC: SLAAC with prefixes of arbitrary length in PIO
  2020-12-19  2:03                         ` Jakub Kicinski
  2020-12-19  2:40                           ` Maciej Żenczykowski
@ 2021-07-10 19:24                           ` Dmytro Shytyi
  2021-07-12 13:23                             ` Dmytro Shytyi
  1 sibling, 1 reply; 49+ messages in thread
From: Dmytro Shytyi @ 2021-07-10 19:24 UTC (permalink / raw)
  To: Jakub Kicinski
  Cc: yoshfuji, liuhangbin, davem, netdev, David Ahern, Joel Scherpelz,
	"Maciej Żenczykowski"

Hello Jakub,


---- On Sat, 19 Dec 2020 03:03:23 +0100 Jakub Kicinski <kuba@kernel.org> wrote ----

 > It'd be great if someone more familiar with our IPv6 code could take a 
 > look. Adding some folks to the CC. 
 >  
 > On Wed, 16 Dec 2020 23:01:29 +0100 Dmytro Shytyi wrote: 
 > > Variable SLAAC [Can be activated via sysctl]: 
 > > SLAAC with prefixes of arbitrary length in PIO (randomly 
 > > generated hostID or stable privacy + privacy extensions). 
 > > The main problem is that SLAAC RA or PD allocates a /64 by the Wireless 
 > > carrier 4G, 5G to a mobile hotspot, however segmentation of the /64 via 
 > > SLAAC is required so that downstream interfaces can be further subnetted. 
 > > Example: uCPE device (4G + WI-FI enabled) receives /64 via Wireless, and 
 > > assigns /72 to VNF-Firewall, /72 to WIFI, /72 to Load-Balancer 
 > > and /72 to wired connected devices. 
 > > IETF document that defines problem statement: 
 > > draft-mishra-v6ops-variable-slaac-problem-stmt 
 > > IETF document that specifies variable slaac: 
 > > draft-mishra-6man-variable-slaac 
 > > 
 > > Signed-off-by: Dmytro Shytyi <dmytro@shytyi.net> 
 >  
 > The RFC mentions checking a flag in RA, but I don't see that in this 
 > patch, could you explain? 
[Dmytro]:
Yes, I can. Please check the most recent revision of "draft-mishra-6man-variable-slaac" section 11 ( Variable SLAAC implementation).
You may find in this document in section 11 the next information:

"The linux implementation for Variable SLAAC contains a parameter that
   can be controlled in the command line (a sysctl).  This parameter has
   two potential values: 0 and 1; by default it is set to 0.  The value
   of 0 means that the stack acts as previously: it does not accept a
   prefix of a length other than 64 for the SLAAC process.  The valye of
   1 makes that prefixes of lengths other than 64 are accepted for the
   SLAAC mechanism of forming addresses."


This is done for multiple purposes. One of them is to disable this functionality (VSLAAC) by default.
 
 >  
 > > diff --git a/include/uapi/linux/ipv6.h b/include/uapi/linux/ipv6.h 
 > > index 13e8751bf24a..f2af4f9fba2d 100644 
 > > --- a/include/uapi/linux/ipv6.h 
 > > +++ b/include/uapi/linux/ipv6.h 
 > > @@ -189,7 +189,8 @@ enum { 
 > >      DEVCONF_ACCEPT_RA_RT_INFO_MIN_PLEN, 
 > >      DEVCONF_NDISC_TCLASS, 
 > >      DEVCONF_RPL_SEG_ENABLED, 
 > > -    DEVCONF_MAX 
 > > +    DEVCONF_MAX, 
 >  
 > MAX should be the last field, no? Isn't it used for sizing tables? 
 >  
 > > +    DEVCONF_VARIABLE_SLAAC 
 > >  }; 
 > > 
 > > 
 > > diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c 
 > > index eff2cacd5209..4afaf2bc8d8b 100644 
 > > --- a/net/ipv6/addrconf.c 
 > > +++ b/net/ipv6/addrconf.c 
 > > @@ -236,6 +236,7 @@ static struct ipv6_devconf ipv6_devconf __read_mostly = { 
 > >      .addr_gen_mode        = IN6_ADDR_GEN_MODE_EUI64, 
 > >      .disable_policy        = 0, 
 > >      .rpl_seg_enabled    = 0, 
 > > +    .variable_slaac        = 0, 
 > >  }; 
 > > 
 > >  static struct ipv6_devconf ipv6_devconf_dflt __read_mostly = { 
 > > @@ -291,6 +292,7 @@ static struct ipv6_devconf ipv6_devconf_dflt __read_mostly = { 
 > >      .addr_gen_mode        = IN6_ADDR_GEN_MODE_EUI64, 
 > >      .disable_policy        = 0, 
 > >      .rpl_seg_enabled    = 0, 
 > > +    .variable_slaac        = 0, 
 > >  }; 
 > > 
 > >  /* Check if link is ready: is it up and is a valid qdisc available */ 
 > > @@ -1340,9 +1342,15 @@ static int ipv6_create_tempaddr(struct inet6_ifaddr *ifp, bool block) 
 > >          goto out; 
 > >      } 
 > >      in6_ifa_hold(ifp); 
 > > -    memcpy(addr.s6_addr, ifp->addr.s6_addr, 8); 
 > > -    ipv6_gen_rnd_iid(&addr); 
 > > 
 > > +    if (ifp->prefix_len == 64) { 
 > > +        memcpy(addr.s6_addr, ifp->addr.s6_addr, 8); 
 > > +        ipv6_gen_rnd_iid(&addr); 
 > > +    } else if (ifp->prefix_len > 0 && ifp->prefix_len <= 128 && 
 > > +           idev->cnf.variable_slaac) { 
 > > +        get_random_bytes(addr.s6_addr, 16); 
 > > +        ipv6_addr_prefix_copy(&addr, &ifp->addr, ifp->prefix_len); 
 > > +    } 
 > >      age = (now - ifp->tstamp) / HZ; 
 > > 
 > >      regen_advance = idev->cnf.regen_max_retry * 
 > > @@ -2569,6 +2577,31 @@ static bool is_addr_mode_generate_stable(struct inet6_dev *idev) 
 > >             idev->cnf.addr_gen_mode == IN6_ADDR_GEN_MODE_RANDOM; 
 > >  } 
 > > 
 > > +static struct inet6_ifaddr *ipv6_cmp_rcvd_prsnt_prfxs(struct inet6_ifaddr *ifp, 
 > > +                              struct inet6_dev *in6_dev, 
 > > +                              struct net *net, 
 > > +                              const struct prefix_info *pinfo) 
 > > +{ 
 > > +    struct inet6_ifaddr *result = NULL; 
 > > +    bool prfxs_equal; 
 > > + 
 > > +    rcu_read_lock(); 
 > > +    list_for_each_entry_rcu(ifp, &in6_dev->addr_list, if_list) { 
 > > +        if (!net_eq(dev_net(ifp->idev->dev), net)) 
 > > +            continue; 
 > > +        prfxs_equal = 
 > > +            ipv6_prefix_equal(&pinfo->prefix, &ifp->addr, pinfo->prefix_len); 
 > > +        if (prfxs_equal && pinfo->prefix_len == ifp->prefix_len) { 
 > > +            result = ifp; 
 > > +            in6_ifa_hold(ifp); 
 > > +            break; 
 > > +        } 
 > > +    } 
 > > +    rcu_read_unlock(); 
 > > + 
 > > +    return result; 
 > > +} 
 > > + 
 > >  int addrconf_prefix_rcv_add_addr(struct net *net, struct net_device *dev, 
 > >                   const struct prefix_info *pinfo, 
 > >                   struct inet6_dev *in6_dev, 
 > > @@ -2576,9 +2609,17 @@ int addrconf_prefix_rcv_add_addr(struct net *net, struct net_device *dev, 
 > >                   u32 addr_flags, bool sllao, bool tokenized, 
 > >                   __u32 valid_lft, u32 prefered_lft) 
 > >  { 
 > > -    struct inet6_ifaddr *ifp = ipv6_get_ifaddr(net, addr, dev, 1); 
 > > +    struct inet6_ifaddr *ifp = NULL; 
 > > +    int plen = pinfo->prefix_len; 
 > >      int create = 0; 
 > > 
 > > +    if (plen > 0 && plen <= 128 && plen != 64 && 
 > > +        in6_dev->cnf.addr_gen_mode != IN6_ADDR_GEN_MODE_STABLE_PRIVACY && 
 > > +        in6_dev->cnf.variable_slaac) 
 > > +        ifp = ipv6_cmp_rcvd_prsnt_prfxs(ifp, in6_dev, net, pinfo); 
 > > +    else 
 > > +        ifp = ipv6_get_ifaddr(net, addr, dev, 1); 
 > > + 
 > >      if (!ifp && valid_lft) { 
 > >          int max_addresses = in6_dev->cnf.max_addresses; 
 > >          struct ifa6_config cfg = { 
 > > @@ -2657,6 +2698,90 @@ int addrconf_prefix_rcv_add_addr(struct net *net, struct net_device *dev, 
 > >  } 
 > >  EXPORT_SYMBOL_GPL(addrconf_prefix_rcv_add_addr); 
 > > 
 > > +static bool ipv6_reserved_interfaceid(struct in6_addr address) 
 > > +{ 
 > > +    if ((address.s6_addr32[2] | address.s6_addr32[3]) == 0) 
 > > +        return true; 
 > > + 
 > > +    if (address.s6_addr32[2] == htonl(0x02005eff) && 
 > > +        ((address.s6_addr32[3] & htonl(0xfe000000)) == htonl(0xfe000000))) 
 > > +        return true; 
 > > + 
 > > +    if (address.s6_addr32[2] == htonl(0xfdffffff) && 
 > > +        ((address.s6_addr32[3] & htonl(0xffffff80)) == htonl(0xffffff80))) 
 > > +        return true; 
 > > + 
 > > +    return false; 
 > > +} 
 > > + 
 > > +static int ipv6_gen_addr_var_plen(struct in6_addr *address, 
 > > +                  u8 dad_count, 
 > > +                  const struct inet6_dev *idev, 
 > > +                  unsigned int rcvd_prfx_len, 
 > > +                  bool stable_privacy_mode) 
 > > +{ 
 > > +    static union { 
 > > +        char __data[SHA1_BLOCK_SIZE]; 
 > > +        struct { 
 > > +            struct in6_addr secret; 
 > > +            __be32 prefix[2]; 
 > > +            unsigned char hwaddr[MAX_ADDR_LEN]; 
 > > +            u8 dad_count; 
 > > +        } __packed; 
 > > +    } data; 
 > > +    static __u32 workspace[SHA1_WORKSPACE_WORDS]; 
 > > +    static __u32 digest[SHA1_DIGEST_WORDS]; 
 > > +    struct net *net = dev_net(idev->dev); 
 > > +    static DEFINE_SPINLOCK(lock); 
 > > +    struct in6_addr secret; 
 > > +    struct in6_addr temp; 
 > > + 
 > > +    BUILD_BUG_ON(sizeof(data.__data) != sizeof(data)); 
 > > + 
 > > +    if (stable_privacy_mode) { 
 > > +        if (idev->cnf.stable_secret.initialized) 
 > > +            secret = idev->cnf.stable_secret.secret; 
 > > +        else if (net->ipv6.devconf_dflt->stable_secret.initialized) 
 > > +            secret = net->ipv6.devconf_dflt->stable_secret.secret; 
 > > +        else 
 > > +            return -1; 
 > > +    } 
 > > + 
 > > +retry: 
 > > +    spin_lock_bh(&lock); 
 > > +    if (stable_privacy_mode) { 
 > > +        sha1_init(digest); 
 > > +        memset(&data, 0, sizeof(data)); 
 > > +        memset(workspace, 0, sizeof(workspace)); 
 > > +        memcpy(data.hwaddr, idev->dev->perm_addr, idev->dev->addr_len); 
 > > +        data.prefix[0] = address->s6_addr32[0]; 
 > > +        data.prefix[1] = address->s6_addr32[1]; 
 > > +        data.secret = secret; 
 > > +        data.dad_count = dad_count; 
 > > + 
 > > +        sha1_transform(digest, data.__data, workspace); 
 > > + 
 > > +        temp.s6_addr32[0] = (__force __be32)digest[0]; 
 > > +        temp.s6_addr32[1] = (__force __be32)digest[1]; 
 > > +        temp.s6_addr32[2] = (__force __be32)digest[2]; 
 > > +        temp.s6_addr32[3] = (__force __be32)digest[3]; 
 > > +    } else { 
 > > +        get_random_bytes(temp.s6_addr32, 16); 
 > > +    } 
 > > + 
 > > +    spin_unlock_bh(&lock); 
 >  
 > Is there a reason this code declares all this state on the stack and 
 > protects it with a lock rather than just allocating the memory with 
 > kmalloc()? 
 >  
[Dmytro]:
I assumed that "stable_privacy_mode" is might comming from user context (sysctl net.ipv6.conf.enp0s3.addr_gen_mode=3). 

And according to this https://www.kernel.org/doc/htmldocs/kernel-locking/lock-user-bh.html where it is said:
"If a softirq shares data with user context, you have two problems. Firstly, the current user context can be interrupted by a softirq, and secondly, the critical region could be entered from another CPU. This is where spin_lock_bh() (include/linux/spinlock.h) is used. It disables softirqs on that CPU, then grabs the lock."

Thus it might be a place for the spin_lock_bh() and spin_unlock_bh().


 > > +    if (ipv6_reserved_interfaceid(temp)) { 
 > > +        dad_count++; 
 > > +        if (dad_count > dev_net(idev->dev)->ipv6.sysctl.idgen_retries) 
 > > +            return -1; 
 > > +        goto retry; 
 > > +    } 
 > > +    ipv6_addr_prefix_copy(&temp, address, rcvd_prfx_len); 
 > > +    *address = temp; 
 > > +    return 0; 
 > > +} 
 > > + 
 > >  void addrconf_prefix_rcv(struct net_device *dev, u8 *opt, int len, bool sllao) 
 > >  { 
 > >      struct prefix_info *pinfo; 
 > > @@ -2781,9 +2906,34 @@ void addrconf_prefix_rcv(struct net_device *dev, u8 *opt, int len, bool sllao) 
 > >                  dev_addr_generated = true; 
 > >              } 
 > >              goto ok; 
 > > +        } else if (pinfo->prefix_len != 64 && 
 > > +               pinfo->prefix_len > 0 && pinfo->prefix_len <= 128 && 
 > > +               in6_dev->cnf.variable_slaac) { 
 > > +            /* SLAAC with prefixes of arbitrary length (Variable SLAAC). 
 > > +             * draft-mishra-6man-variable-slaac 
 > > +             * draft-mishra-v6ops-variable-slaac-problem-stmt 
 > > +             */ 
 > > +            memcpy(&addr, &pinfo->prefix, 16); 
 > > +            if (in6_dev->cnf.addr_gen_mode == IN6_ADDR_GEN_MODE_STABLE_PRIVACY) { 
 > > +                if (!ipv6_gen_addr_var_plen(&addr, 
 > > +                                0, 
 > > +                                in6_dev, 
 > > +                                pinfo->prefix_len, 
 > > +                                true)) { 
 > > +                    addr_flags |= IFA_F_STABLE_PRIVACY; 
 > > +                    goto ok; 
 > > +                } 
 > > +            } else if (!ipv6_gen_addr_var_plen(&addr, 
 > > +                               0, 
 > > +                               in6_dev, 
 > > +                               pinfo->prefix_len, 
 > > +                               false)) { 
 > > +                goto ok; 
 > > +            } 
 > > +        } else { 
 > > +            net_dbg_ratelimited("IPv6: Prefix with unexpected length %d\n", 
 > > +                        pinfo->prefix_len); 
 > >          } 
 > > -        net_dbg_ratelimited("IPv6 addrconf: prefix with wrong length %d\n", 
 > > -                    pinfo->prefix_len); 
 > >          goto put; 
 > > 
 > >  ok: 
 > > @@ -3186,22 +3336,6 @@ void addrconf_add_linklocal(struct inet6_dev *idev, 
 > >  } 
 > >  EXPORT_SYMBOL_GPL(addrconf_add_linklocal); 
 > > 
 > > -static bool ipv6_reserved_interfaceid(struct in6_addr address) 
 > > -{ 
 > > -    if ((address.s6_addr32[2] | address.s6_addr32[3]) == 0) 
 > > -        return true; 
 > > - 
 > > -    if (address.s6_addr32[2] == htonl(0x02005eff) && 
 > > -        ((address.s6_addr32[3] & htonl(0xfe000000)) == htonl(0xfe000000))) 
 > > -        return true; 
 > > - 
 > > -    if (address.s6_addr32[2] == htonl(0xfdffffff) && 
 > > -        ((address.s6_addr32[3] & htonl(0xffffff80)) == htonl(0xffffff80))) 
 > > -        return true; 
 > > - 
 > > -    return false; 
 > > -} 
 > > - 
 > >  static int ipv6_generate_stable_address(struct in6_addr *address, 
 > >                      u8 dad_count, 
 > >                      const struct inet6_dev *idev) 
 > > @@ -5517,6 +5651,7 @@ static inline void ipv6_store_devconf(struct ipv6_devconf *cnf, 
 > >      array[DEVCONF_DISABLE_POLICY] = cnf->disable_policy; 
 > >      array[DEVCONF_NDISC_TCLASS] = cnf->ndisc_tclass; 
 > >      array[DEVCONF_RPL_SEG_ENABLED] = cnf->rpl_seg_enabled; 
 > > +    array[DEVCONF_VARIABLE_SLAAC] = cnf->variable_slaac; 
 > >  } 
 > > 
 > >  static inline size_t inet6_ifla6_size(void) 
 > > @@ -6897,6 +7032,13 @@ static const struct ctl_table addrconf_sysctl[] = { 
 > >          .mode        = 0644, 
 > >          .proc_handler    = proc_dointvec, 
 > >      }, 
 > > +    { 
 > > +        .procname    = "variable_slaac", 
 > > +        .data        = &ipv6_devconf.variable_slaac, 
 > > +        .maxlen        = sizeof(int), 
 > > +        .mode        = 0644, 
 > > +        .proc_handler    = proc_dointvec, 
 > > +    }, 
 > >      { 
 > >          /* sentinel */ 
 > >      } 
 >  
 > 

Take care,
Dmytro SHYTYI


^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH net-next V9] net: Variable SLAAC: SLAAC with prefixes of arbitrary length in PIO
  2021-07-10 19:24                           ` Dmytro Shytyi
@ 2021-07-12 13:23                             ` Dmytro Shytyi
  0 siblings, 0 replies; 49+ messages in thread
From: Dmytro Shytyi @ 2021-07-12 13:23 UTC (permalink / raw)
  To: Jakub Kicinski
  Cc: yoshfuji, liuhangbin, davem, netdev, David Ahern, Joel Scherpelz,
	"Maciej Żenczykowski"

Hello Jakub,


---- On Sat, 10 Jul 2021 21:24:46 +0200 Dmytro Shytyi <dmytro@shytyi.net> wrote ----

 > Hello Jakub, 
 >  
 >  
 > ---- On Sat, 19 Dec 2020 03:03:23 +0100 Jakub Kicinski <kuba@kernel.org> wrote ---- 
 >  
 >  > It'd be great if someone more familiar with our IPv6 code could take a 
 >  > look. Adding some folks to the CC. 
 >  > 
 >  > On Wed, 16 Dec 2020 23:01:29 +0100 Dmytro Shytyi wrote: 
 >  > > Variable SLAAC [Can be activated via sysctl]: 
 >  > > SLAAC with prefixes of arbitrary length in PIO (randomly 
 >  > > generated hostID or stable privacy + privacy extensions). 
 >  > > The main problem is that SLAAC RA or PD allocates a /64 by the Wireless 
 >  > > carrier 4G, 5G to a mobile hotspot, however segmentation of the /64 via 
 >  > > SLAAC is required so that downstream interfaces can be further subnetted. 
 >  > > Example: uCPE device (4G + WI-FI enabled) receives /64 via Wireless, and 
 >  > > assigns /72 to VNF-Firewall, /72 to WIFI, /72 to Load-Balancer 
 >  > > and /72 to wired connected devices. 
 >  > > IETF document that defines problem statement: 
 >  > > draft-mishra-v6ops-variable-slaac-problem-stmt 
 >  > > IETF document that specifies variable slaac: 
 >  > > draft-mishra-6man-variable-slaac 
 >  > > 
 >  > > Signed-off-by: Dmytro Shytyi <dmytro@shytyi.net> 
 >  > 
 >  > The RFC mentions checking a flag in RA, but I don't see that in this 
 >  > patch, could you explain? 
 > [Dmytro]: 
 > Yes, I can. Please check the most recent revision of "draft-mishra-6man-variable-slaac" section 11 ( Variable SLAAC implementation). 
 > You may find in this document in section 11 the next information: 
 >  
 > "The linux implementation for Variable SLAAC contains a parameter that 
 >  can be controlled in the command line (a sysctl).  This parameter has 
 >  two potential values: 0 and 1; by default it is set to 0.  The value 
 >  of 0 means that the stack acts as previously: it does not accept a 
 >  prefix of a length other than 64 for the SLAAC process.  The valye of 
 >  1 makes that prefixes of lengths other than 64 are accepted for the 
 >  SLAAC mechanism of forming addresses." 
 >  
 >  
 > This is done for multiple purposes. One of them is to disable this functionality (VSLAAC) by default.
[Dmytro]:

In other words, the most recent RFC mentions checking sysctl parameter instead of a flag (S-bit) in RA.

 >  > 
 >  > > diff --git a/include/uapi/linux/ipv6.h b/include/uapi/linux/ipv6.h 
 >  > > index 13e8751bf24a..f2af4f9fba2d 100644 
 >  > > --- a/include/uapi/linux/ipv6.h 
 >  > > +++ b/include/uapi/linux/ipv6.h 
 >  > > @@ -189,7 +189,8 @@ enum { 
 >  > >      DEVCONF_ACCEPT_RA_RT_INFO_MIN_PLEN, 
 >  > >      DEVCONF_NDISC_TCLASS, 
 >  > >      DEVCONF_RPL_SEG_ENABLED, 
 >  > > -    DEVCONF_MAX 
 >  > > +    DEVCONF_MAX, 
 >  > 
 >  > MAX should be the last field, no? Isn't it used for sizing tables? 
 >  > 
 >  > > +    DEVCONF_VARIABLE_SLAAC 
 >  > >  }; 
 >  > > 
 >  > > 
 >  > > diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c 
 >  > > index eff2cacd5209..4afaf2bc8d8b 100644 
 >  > > --- a/net/ipv6/addrconf.c 
 >  > > +++ b/net/ipv6/addrconf.c 
 >  > > @@ -236,6 +236,7 @@ static struct ipv6_devconf ipv6_devconf __read_mostly = { 
 >  > >      .addr_gen_mode        = IN6_ADDR_GEN_MODE_EUI64, 
 >  > >      .disable_policy        = 0, 
 >  > >      .rpl_seg_enabled    = 0, 
 >  > > +    .variable_slaac        = 0, 
 >  > >  }; 
 >  > > 
 >  > >  static struct ipv6_devconf ipv6_devconf_dflt __read_mostly = { 
 >  > > @@ -291,6 +292,7 @@ static struct ipv6_devconf ipv6_devconf_dflt __read_mostly = { 
 >  > >      .addr_gen_mode        = IN6_ADDR_GEN_MODE_EUI64, 
 >  > >      .disable_policy        = 0, 
 >  > >      .rpl_seg_enabled    = 0, 
 >  > > +    .variable_slaac        = 0, 
 >  > >  }; 
 >  > > 
 >  > >  /* Check if link is ready: is it up and is a valid qdisc available */ 
 >  > > @@ -1340,9 +1342,15 @@ static int ipv6_create_tempaddr(struct inet6_ifaddr *ifp, bool block) 
 >  > >          goto out; 
 >  > >      } 
 >  > >      in6_ifa_hold(ifp); 
 >  > > -    memcpy(addr.s6_addr, ifp->addr.s6_addr, 8); 
 >  > > -    ipv6_gen_rnd_iid(&addr); 
 >  > > 
 >  > > +    if (ifp->prefix_len == 64) { 
 >  > > +        memcpy(addr.s6_addr, ifp->addr.s6_addr, 8); 
 >  > > +        ipv6_gen_rnd_iid(&addr); 
 >  > > +    } else if (ifp->prefix_len > 0 && ifp->prefix_len <= 128 && 
 >  > > +           idev->cnf.variable_slaac) { 
 >  > > +        get_random_bytes(addr.s6_addr, 16); 
 >  > > +        ipv6_addr_prefix_copy(&addr, &ifp->addr, ifp->prefix_len); 
 >  > > +    } 
 >  > >      age = (now - ifp->tstamp) / HZ; 
 >  > > 
 >  > >      regen_advance = idev->cnf.regen_max_retry * 
 >  > > @@ -2569,6 +2577,31 @@ static bool is_addr_mode_generate_stable(struct inet6_dev *idev) 
 >  > >             idev->cnf.addr_gen_mode == IN6_ADDR_GEN_MODE_RANDOM; 
 >  > >  } 
 >  > > 
 >  > > +static struct inet6_ifaddr *ipv6_cmp_rcvd_prsnt_prfxs(struct inet6_ifaddr *ifp, 
 >  > > +                              struct inet6_dev *in6_dev, 
 >  > > +                              struct net *net, 
 >  > > +                              const struct prefix_info *pinfo) 
 >  > > +{ 
 >  > > +    struct inet6_ifaddr *result = NULL; 
 >  > > +    bool prfxs_equal; 
 >  > > + 
 >  > > +    rcu_read_lock(); 
 >  > > +    list_for_each_entry_rcu(ifp, &in6_dev->addr_list, if_list) { 
 >  > > +        if (!net_eq(dev_net(ifp->idev->dev), net)) 
 >  > > +            continue; 
 >  > > +        prfxs_equal = 
 >  > > +            ipv6_prefix_equal(&pinfo->prefix, &ifp->addr, pinfo->prefix_len); 
 >  > > +        if (prfxs_equal && pinfo->prefix_len == ifp->prefix_len) { 
 >  > > +            result = ifp; 
 >  > > +            in6_ifa_hold(ifp); 
 >  > > +            break; 
 >  > > +        } 
 >  > > +    } 
 >  > > +    rcu_read_unlock(); 
 >  > > + 
 >  > > +    return result; 
 >  > > +} 
 >  > > + 
 >  > >  int addrconf_prefix_rcv_add_addr(struct net *net, struct net_device *dev, 
 >  > >                   const struct prefix_info *pinfo, 
 >  > >                   struct inet6_dev *in6_dev, 
 >  > > @@ -2576,9 +2609,17 @@ int addrconf_prefix_rcv_add_addr(struct net *net, struct net_device *dev, 
 >  > >                   u32 addr_flags, bool sllao, bool tokenized, 
 >  > >                   __u32 valid_lft, u32 prefered_lft) 
 >  > >  { 
 >  > > -    struct inet6_ifaddr *ifp = ipv6_get_ifaddr(net, addr, dev, 1); 
 >  > > +    struct inet6_ifaddr *ifp = NULL; 
 >  > > +    int plen = pinfo->prefix_len; 
 >  > >      int create = 0; 
 >  > > 
 >  > > +    if (plen > 0 && plen <= 128 && plen != 64 && 
 >  > > +        in6_dev->cnf.addr_gen_mode != IN6_ADDR_GEN_MODE_STABLE_PRIVACY && 
 >  > > +        in6_dev->cnf.variable_slaac) 
 >  > > +        ifp = ipv6_cmp_rcvd_prsnt_prfxs(ifp, in6_dev, net, pinfo); 
 >  > > +    else 
 >  > > +        ifp = ipv6_get_ifaddr(net, addr, dev, 1); 
 >  > > + 
 >  > >      if (!ifp && valid_lft) { 
 >  > >          int max_addresses = in6_dev->cnf.max_addresses; 
 >  > >          struct ifa6_config cfg = { 
 >  > > @@ -2657,6 +2698,90 @@ int addrconf_prefix_rcv_add_addr(struct net *net, struct net_device *dev, 
 >  > >  } 
 >  > >  EXPORT_SYMBOL_GPL(addrconf_prefix_rcv_add_addr); 
 >  > > 
 >  > > +static bool ipv6_reserved_interfaceid(struct in6_addr address) 
 >  > > +{ 
 >  > > +    if ((address.s6_addr32[2] | address.s6_addr32[3]) == 0) 
 >  > > +        return true; 
 >  > > + 
 >  > > +    if (address.s6_addr32[2] == htonl(0x02005eff) && 
 >  > > +        ((address.s6_addr32[3] & htonl(0xfe000000)) == htonl(0xfe000000))) 
 >  > > +        return true; 
 >  > > + 
 >  > > +    if (address.s6_addr32[2] == htonl(0xfdffffff) && 
 >  > > +        ((address.s6_addr32[3] & htonl(0xffffff80)) == htonl(0xffffff80))) 
 >  > > +        return true; 
 >  > > + 
 >  > > +    return false; 
 >  > > +} 
 >  > > + 
 >  > > +static int ipv6_gen_addr_var_plen(struct in6_addr *address, 
 >  > > +                  u8 dad_count, 
 >  > > +                  const struct inet6_dev *idev, 
 >  > > +                  unsigned int rcvd_prfx_len, 
 >  > > +                  bool stable_privacy_mode) 
 >  > > +{ 
 >  > > +    static union { 
 >  > > +        char __data[SHA1_BLOCK_SIZE]; 
 >  > > +        struct { 
 >  > > +            struct in6_addr secret; 
 >  > > +            __be32 prefix[2]; 
 >  > > +            unsigned char hwaddr[MAX_ADDR_LEN]; 
 >  > > +            u8 dad_count; 
 >  > > +        } __packed; 
 >  > > +    } data; 
 >  > > +    static __u32 workspace[SHA1_WORKSPACE_WORDS]; 
 >  > > +    static __u32 digest[SHA1_DIGEST_WORDS]; 
 >  > > +    struct net *net = dev_net(idev->dev); 
 >  > > +    static DEFINE_SPINLOCK(lock); 
 >  > > +    struct in6_addr secret; 
 >  > > +    struct in6_addr temp; 
 >  > > + 
 >  > > +    BUILD_BUG_ON(sizeof(data.__data) != sizeof(data)); 
 >  > > + 
 >  > > +    if (stable_privacy_mode) { 
 >  > > +        if (idev->cnf.stable_secret.initialized) 
 >  > > +            secret = idev->cnf.stable_secret.secret; 
 >  > > +        else if (net->ipv6.devconf_dflt->stable_secret.initialized) 
 >  > > +            secret = net->ipv6.devconf_dflt->stable_secret.secret; 
 >  > > +        else 
 >  > > +            return -1; 
 >  > > +    } 
 >  > > + 
 >  > > +retry: 
 >  > > +    spin_lock_bh(&lock); 
 >  > > +    if (stable_privacy_mode) { 
 >  > > +        sha1_init(digest); 
 >  > > +        memset(&data, 0, sizeof(data)); 
 >  > > +        memset(workspace, 0, sizeof(workspace)); 
 >  > > +        memcpy(data.hwaddr, idev->dev->perm_addr, idev->dev->addr_len); 
 >  > > +        data.prefix[0] = address->s6_addr32[0]; 
 >  > > +        data.prefix[1] = address->s6_addr32[1]; 
 >  > > +        data.secret = secret; 
 >  > > +        data.dad_count = dad_count; 
 >  > > + 
 >  > > +        sha1_transform(digest, data.__data, workspace); 
 >  > > + 
 >  > > +        temp.s6_addr32[0] = (__force __be32)digest[0]; 
 >  > > +        temp.s6_addr32[1] = (__force __be32)digest[1]; 
 >  > > +        temp.s6_addr32[2] = (__force __be32)digest[2]; 
 >  > > +        temp.s6_addr32[3] = (__force __be32)digest[3]; 
 >  > > +    } else { 
 >  > > +        get_random_bytes(temp.s6_addr32, 16); 
 >  > > +    } 
 >  > > + 
 >  > > +    spin_unlock_bh(&lock); 
 >  > 
 >  > Is there a reason this code declares all this state on the stack and 
 >  > protects it with a lock rather than just allocating the memory with 
 >  > kmalloc()? 
 >  > 
 > [Dmytro]: 
 > I assumed that "stable_privacy_mode" is might comming from user context (sysctl net.ipv6.conf.enp0s3.addr_gen_mode=3). 
 >  
 > And according to this https://www.kernel.org/doc/htmldocs/kernel-locking/lock-user-bh.html where it is said: 
 > "If a softirq shares data with user context, you have two problems. Firstly, the current user context can be interrupted by a softirq, and secondly, the critical region could be entered from another CPU. This is where spin_lock_bh() (include/linux/spinlock.h) is used. It disables softirqs on that CPU, then grabs the lock." 
 >  
 > Thus it might be a place for the spin_lock_bh() and spin_unlock_bh(). 
 >  
 >  
 >  > > +    if (ipv6_reserved_interfaceid(temp)) { 
 >  > > +        dad_count++; 
 >  > > +        if (dad_count > dev_net(idev->dev)->ipv6.sysctl.idgen_retries) 
 >  > > +            return -1; 
 >  > > +        goto retry; 
 >  > > +    } 
 >  > > +    ipv6_addr_prefix_copy(&temp, address, rcvd_prfx_len); 
 >  > > +    *address = temp; 
 >  > > +    return 0; 
 >  > > +} 
 >  > > + 
 >  > >  void addrconf_prefix_rcv(struct net_device *dev, u8 *opt, int len, bool sllao) 
 >  > >  { 
 >  > >      struct prefix_info *pinfo; 
 >  > > @@ -2781,9 +2906,34 @@ void addrconf_prefix_rcv(struct net_device *dev, u8 *opt, int len, bool sllao) 
 >  > >                  dev_addr_generated = true; 
 >  > >              } 
 >  > >              goto ok; 
 >  > > +        } else if (pinfo->prefix_len != 64 && 
 >  > > +               pinfo->prefix_len > 0 && pinfo->prefix_len <= 128 && 
 >  > > +               in6_dev->cnf.variable_slaac) { 
 >  > > +            /* SLAAC with prefixes of arbitrary length (Variable SLAAC). 
 >  > > +             * draft-mishra-6man-variable-slaac 
 >  > > +             * draft-mishra-v6ops-variable-slaac-problem-stmt 
 >  > > +             */ 
 >  > > +            memcpy(&addr, &pinfo->prefix, 16); 
 >  > > +            if (in6_dev->cnf.addr_gen_mode == IN6_ADDR_GEN_MODE_STABLE_PRIVACY) { 
 >  > > +                if (!ipv6_gen_addr_var_plen(&addr, 
 >  > > +                                0, 
 >  > > +                                in6_dev, 
 >  > > +                                pinfo->prefix_len, 
 >  > > +                                true)) { 
 >  > > +                    addr_flags |= IFA_F_STABLE_PRIVACY; 
 >  > > +                    goto ok; 
 >  > > +                } 
 >  > > +            } else if (!ipv6_gen_addr_var_plen(&addr, 
 >  > > +                               0, 
 >  > > +                               in6_dev, 
 >  > > +                               pinfo->prefix_len, 
 >  > > +                               false)) { 
 >  > > +                goto ok; 
 >  > > +            } 
 >  > > +        } else { 
 >  > > +            net_dbg_ratelimited("IPv6: Prefix with unexpected length %d\n", 
 >  > > +                        pinfo->prefix_len); 
 >  > >          } 
 >  > > -        net_dbg_ratelimited("IPv6 addrconf: prefix with wrong length %d\n", 
 >  > > -                    pinfo->prefix_len); 
 >  > >          goto put; 
 >  > > 
 >  > >  ok: 
 >  > > @@ -3186,22 +3336,6 @@ void addrconf_add_linklocal(struct inet6_dev *idev, 
 >  > >  } 
 >  > >  EXPORT_SYMBOL_GPL(addrconf_add_linklocal); 
 >  > > 
 >  > > -static bool ipv6_reserved_interfaceid(struct in6_addr address) 
 >  > > -{ 
 >  > > -    if ((address.s6_addr32[2] | address.s6_addr32[3]) == 0) 
 >  > > -        return true; 
 >  > > - 
 >  > > -    if (address.s6_addr32[2] == htonl(0x02005eff) && 
 >  > > -        ((address.s6_addr32[3] & htonl(0xfe000000)) == htonl(0xfe000000))) 
 >  > > -        return true; 
 >  > > - 
 >  > > -    if (address.s6_addr32[2] == htonl(0xfdffffff) && 
 >  > > -        ((address.s6_addr32[3] & htonl(0xffffff80)) == htonl(0xffffff80))) 
 >  > > -        return true; 
 >  > > - 
 >  > > -    return false; 
 >  > > -} 
 >  > > - 
 >  > >  static int ipv6_generate_stable_address(struct in6_addr *address, 
 >  > >                      u8 dad_count, 
 >  > >                      const struct inet6_dev *idev) 
 >  > > @@ -5517,6 +5651,7 @@ static inline void ipv6_store_devconf(struct ipv6_devconf *cnf, 
 >  > >      array[DEVCONF_DISABLE_POLICY] = cnf->disable_policy; 
 >  > >      array[DEVCONF_NDISC_TCLASS] = cnf->ndisc_tclass; 
 >  > >      array[DEVCONF_RPL_SEG_ENABLED] = cnf->rpl_seg_enabled; 
 >  > > +    array[DEVCONF_VARIABLE_SLAAC] = cnf->variable_slaac; 
 >  > >  } 
 >  > > 
 >  > >  static inline size_t inet6_ifla6_size(void) 
 >  > > @@ -6897,6 +7032,13 @@ static const struct ctl_table addrconf_sysctl[] = { 
 >  > >          .mode        = 0644, 
 >  > >          .proc_handler    = proc_dointvec, 
 >  > >      }, 
 >  > > +    { 
 >  > > +        .procname    = "variable_slaac", 
 >  > > +        .data        = &ipv6_devconf.variable_slaac, 
 >  > > +        .maxlen        = sizeof(int), 
 >  > > +        .mode        = 0644, 
 >  > > +        .proc_handler    = proc_dointvec, 
 >  > > +    }, 
 >  > >      { 
 >  > >          /* sentinel */ 
 >  > >      } 
 >  > 
 >  > 
 >  
 > Take care, 
 > Dmytro SHYTYI 
 >  
 > 

^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH net-next V9] net: Variable SLAAC: SLAAC with prefixes of arbitrary length in PIO
  2020-12-19  2:40                           ` Maciej Żenczykowski
@ 2021-07-12 13:39                             ` Dmytro Shytyi
  2021-07-12 16:42                               ` Dmytro Shytyi
  0 siblings, 1 reply; 49+ messages in thread
From: Dmytro Shytyi @ 2021-07-12 13:39 UTC (permalink / raw)
  To: "Maciej Żenczykowski"
  Cc: Jakub Kicinski, yoshfuji, liuhangbin, davem, netdev, David Ahern,
	Joel Scherpelz

Hello Maciej,


---- On Sat, 19 Dec 2020 03:40:50 +0100 Maciej Żenczykowski <maze@google.com> wrote ----

 > On Fri, Dec 18, 2020 at 6:03 PM Jakub Kicinski <kuba@kernel.org> wrote: 
 > > 
 > > It'd be great if someone more familiar with our IPv6 code could take a 
 > > look. Adding some folks to the CC. 
 > > 
 > > On Wed, 16 Dec 2020 23:01:29 +0100 Dmytro Shytyi wrote: 
 > > > Variable SLAAC [Can be activated via sysctl]: 
 > > > SLAAC with prefixes of arbitrary length in PIO (randomly 
 > > > generated hostID or stable privacy + privacy extensions). 
 > > > The main problem is that SLAAC RA or PD allocates a /64 by the Wireless 
 > > > carrier 4G, 5G to a mobile hotspot, however segmentation of the /64 via 
 > > > SLAAC is required so that downstream interfaces can be further subnetted. 
 > > > Example: uCPE device (4G + WI-FI enabled) receives /64 via Wireless, and 
 > > > assigns /72 to VNF-Firewall, /72 to WIFI, /72 to Load-Balancer 
 > > > and /72 to wired connected devices. 
 > > > IETF document that defines problem statement: 
 > > > draft-mishra-v6ops-variable-slaac-problem-stmt 
 > > > IETF document that specifies variable slaac: 
 > > > draft-mishra-6man-variable-slaac 
 > > > 
 > > > Signed-off-by: Dmytro Shytyi <dmytro@shytyi.net> 
 > > 

 > IMHO acceptance of this should *definitely* wait for the RFC to be 
 > accepted/published/standardized (whatever is the right term). 

[Dmytro]:
There is an implementation of Variable SLAAC in the OpenBSD Operating System.
 
 > I'm not at all convinced that will happen - this still seems like a 
 > very fresh *draft* of an rfc, 
 > and I'm *sure* it will be argued about. 

 [Dmytro]
By default, VSLAAC is disabled, so there are _*no*_ impact on network behavior by default.

 > This sort of functionality will not be particularly useful without 
 > widespread industry 

[Dmytro]:
There are use-cases that can profit from radvd-like software and VSLAAC directly. 

 > adoption across *all* major operating systems (Windows, Mac/iOS, 
 > Linux/Android, FreeBSD, etc.) 

[Dmytro]:
It should be considered to provide users an _*opportunity*_ to get the required feature.
Solution (as an option) present in linux is better, than _no solution_ in linux. 

 > An implementation that is incompatible with the published RFC will 
 > hurt us more then help us. 

 [Dmytro]:
Compatible implementation follows the recent version of document: https://datatracker.ietf.org/doc/draft-mishra-6man-variable-slaac/ The sysctl usage described in the document is used in the implementation to activate/deactivate VSLAAC. By default it is disabled, so there is _*no*_ impact on network behavior by default. 

 > Maciej Żenczykowski, Kernel Networking Developer @ Google 
 > 

Take care,
Dmytro.

^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH net-next V9] net: Variable SLAAC: SLAAC with prefixes of arbitrary length in PIO
  2021-07-12 13:39                             ` Dmytro Shytyi
@ 2021-07-12 16:42                               ` Dmytro Shytyi
  2021-07-12 17:51                                 ` Erik Kline
  0 siblings, 1 reply; 49+ messages in thread
From: Dmytro Shytyi @ 2021-07-12 16:42 UTC (permalink / raw)
  To: Jakub Kicinski, "Maciej Żenczykowski", yoshfuji
  Cc: liuhangbin, davem, netdev, David Ahern, Joel Scherpelz

Hello Jakub, Maciej, Yoshfuji and others,

After discussion with co-authors about this particular point "Internet Draft/RFC" we think the following: 
Indeed RFC status shows large agreement among IETF members. And that is the best indicator of a maturity level.
And that is the best to implement the feature in a stable mainline kernel.

At this time VSLAAC is an individual proposal Internet Draft reflecting the opinion of all authors.
It is not adopted by any IETF working group. At the same time we consider submission to 3GPP.

The features in the kernel have optionally "Y/N/M" and status "EXPERIMENTAL/STABLE".
One possibility could be VSLAAC as "N", "EXPERIMENTAL" on the linux-next branch.

Could you consider this possibility more?

If you doubt VSLAAC introducing non-64 bits IID lengths, then one might wonder whether linux supports IIDs of _arbitrary length_,
as specified in the RFC 7217 with maturity level "Standards Track"?

Best regards,
Dmytro Shytyi et al.

---- On Mon, 12 Jul 2021 15:39:27 +0200 Dmytro Shytyi <dmytro@shytyi.net> wrote ----

 > Hello Maciej, 
 >  
 >  
 > ---- On Sat, 19 Dec 2020 03:40:50 +0100 Maciej Żenczykowski <maze@google.com> wrote ---- 
 >  
 >  > On Fri, Dec 18, 2020 at 6:03 PM Jakub Kicinski <kuba@kernel.org> wrote: 
 >  > > 
 >  > > It'd be great if someone more familiar with our IPv6 code could take a 
 >  > > look. Adding some folks to the CC. 
 >  > > 
 >  > > On Wed, 16 Dec 2020 23:01:29 +0100 Dmytro Shytyi wrote: 
 >  > > > Variable SLAAC [Can be activated via sysctl]: 
 >  > > > SLAAC with prefixes of arbitrary length in PIO (randomly 
 >  > > > generated hostID or stable privacy + privacy extensions). 
 >  > > > The main problem is that SLAAC RA or PD allocates a /64 by the Wireless 
 >  > > > carrier 4G, 5G to a mobile hotspot, however segmentation of the /64 via 
 >  > > > SLAAC is required so that downstream interfaces can be further subnetted. 
 >  > > > Example: uCPE device (4G + WI-FI enabled) receives /64 via Wireless, and 
 >  > > > assigns /72 to VNF-Firewall, /72 to WIFI, /72 to Load-Balancer 
 >  > > > and /72 to wired connected devices. 
 >  > > > IETF document that defines problem statement: 
 >  > > > draft-mishra-v6ops-variable-slaac-problem-stmt 
 >  > > > IETF document that specifies variable slaac: 
 >  > > > draft-mishra-6man-variable-slaac 
 >  > > > 
 >  > > > Signed-off-by: Dmytro Shytyi <dmytro@shytyi.net> 
 >  > > 
 >  
 >  > IMHO acceptance of this should *definitely* wait for the RFC to be 
 >  > accepted/published/standardized (whatever is the right term). 
 >  
 > [Dmytro]: 
 > There is an implementation of Variable SLAAC in the OpenBSD Operating System. 
 >  
 >  > I'm not at all convinced that will happen - this still seems like a 
 >  > very fresh *draft* of an rfc, 
 >  > and I'm *sure* it will be argued about. 
 >  
 >  [Dmytro] 
 > By default, VSLAAC is disabled, so there are _*no*_ impact on network behavior by default. 
 >  
 >  > This sort of functionality will not be particularly useful without 
 >  > widespread industry 
 >  
 > [Dmytro]: 
 > There are use-cases that can profit from radvd-like software and VSLAAC directly. 
 >  
 >  > adoption across *all* major operating systems (Windows, Mac/iOS, 
 >  > Linux/Android, FreeBSD, etc.) 
 >  
 > [Dmytro]: 
 > It should be considered to provide users an _*opportunity*_ to get the required feature. 
 > Solution (as an option) present in linux is better, than _no solution_ in linux. 
 >  
 >  > An implementation that is incompatible with the published RFC will 
 >  > hurt us more then help us. 
 >  
 >  [Dmytro]: 
 > Compatible implementation follows the recent version of document: https://datatracker.ietf.org/doc/draft-mishra-6man-variable-slaac/ The sysctl usage described in the document is used in the implementation to activate/deactivate VSLAAC. By default it is disabled, so there is _*no*_ impact on network behavior by default. 
 >  
 >  > Maciej Żenczykowski, Kernel Networking Developer @ Google 
 >  > 
 >  
 > Take care, 
 > Dmytro. 
 > 


^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH net-next V9] net: Variable SLAAC: SLAAC with prefixes of arbitrary length in PIO
  2021-07-12 16:42                               ` Dmytro Shytyi
@ 2021-07-12 17:51                                 ` Erik Kline
  2021-07-13 18:47                                   ` Dmytro Shytyi
  0 siblings, 1 reply; 49+ messages in thread
From: Erik Kline @ 2021-07-12 17:51 UTC (permalink / raw)
  To: Dmytro Shytyi
  Cc: Jakub Kicinski, Maciej Żenczykowski, yoshfuji, liuhangbin,
	davem, netdev, David Ahern, Joel Scherpelz

VSLAAC is indeed quite contentious in the IETF, in large part because
it enables a race to the bottom problem for which there is no solution
in sight.

I don't think this should be accepted.  It's not in the same category
of some other Y/N/M things where there are issues of kernel size,
absence of some underlying physical support or not, etc.


On Mon, Jul 12, 2021 at 9:42 AM Dmytro Shytyi <dmytro@shytyi.net> wrote:
>
> Hello Jakub, Maciej, Yoshfuji and others,
>
> After discussion with co-authors about this particular point "Internet Draft/RFC" we think the following:
> Indeed RFC status shows large agreement among IETF members. And that is the best indicator of a maturity level.
> And that is the best to implement the feature in a stable mainline kernel.
>
> At this time VSLAAC is an individual proposal Internet Draft reflecting the opinion of all authors.
> It is not adopted by any IETF working group. At the same time we consider submission to 3GPP.
>
> The features in the kernel have optionally "Y/N/M" and status "EXPERIMENTAL/STABLE".
> One possibility could be VSLAAC as "N", "EXPERIMENTAL" on the linux-next branch.
>
> Could you consider this possibility more?
>
> If you doubt VSLAAC introducing non-64 bits IID lengths, then one might wonder whether linux supports IIDs of _arbitrary length_,
> as specified in the RFC 7217 with maturity level "Standards Track"?
>
> Best regards,
> Dmytro Shytyi et al.
>
> ---- On Mon, 12 Jul 2021 15:39:27 +0200 Dmytro Shytyi <dmytro@shytyi.net> wrote ----
>
>  > Hello Maciej,
>  >
>  >
>  > ---- On Sat, 19 Dec 2020 03:40:50 +0100 Maciej Żenczykowski <maze@google.com> wrote ----
>  >
>  >  > On Fri, Dec 18, 2020 at 6:03 PM Jakub Kicinski <kuba@kernel.org> wrote:
>  >  > >
>  >  > > It'd be great if someone more familiar with our IPv6 code could take a
>  >  > > look. Adding some folks to the CC.
>  >  > >
>  >  > > On Wed, 16 Dec 2020 23:01:29 +0100 Dmytro Shytyi wrote:
>  >  > > > Variable SLAAC [Can be activated via sysctl]:
>  >  > > > SLAAC with prefixes of arbitrary length in PIO (randomly
>  >  > > > generated hostID or stable privacy + privacy extensions).
>  >  > > > The main problem is that SLAAC RA or PD allocates a /64 by the Wireless
>  >  > > > carrier 4G, 5G to a mobile hotspot, however segmentation of the /64 via
>  >  > > > SLAAC is required so that downstream interfaces can be further subnetted.
>  >  > > > Example: uCPE device (4G + WI-FI enabled) receives /64 via Wireless, and
>  >  > > > assigns /72 to VNF-Firewall, /72 to WIFI, /72 to Load-Balancer
>  >  > > > and /72 to wired connected devices.
>  >  > > > IETF document that defines problem statement:
>  >  > > > draft-mishra-v6ops-variable-slaac-problem-stmt
>  >  > > > IETF document that specifies variable slaac:
>  >  > > > draft-mishra-6man-variable-slaac
>  >  > > >
>  >  > > > Signed-off-by: Dmytro Shytyi <dmytro@shytyi.net>
>  >  > >
>  >
>  >  > IMHO acceptance of this should *definitely* wait for the RFC to be
>  >  > accepted/published/standardized (whatever is the right term).
>  >
>  > [Dmytro]:
>  > There is an implementation of Variable SLAAC in the OpenBSD Operating System.
>  >
>  >  > I'm not at all convinced that will happen - this still seems like a
>  >  > very fresh *draft* of an rfc,
>  >  > and I'm *sure* it will be argued about.
>  >
>  >  [Dmytro]
>  > By default, VSLAAC is disabled, so there are _*no*_ impact on network behavior by default.
>  >
>  >  > This sort of functionality will not be particularly useful without
>  >  > widespread industry
>  >
>  > [Dmytro]:
>  > There are use-cases that can profit from radvd-like software and VSLAAC directly.
>  >
>  >  > adoption across *all* major operating systems (Windows, Mac/iOS,
>  >  > Linux/Android, FreeBSD, etc.)
>  >
>  > [Dmytro]:
>  > It should be considered to provide users an _*opportunity*_ to get the required feature.
>  > Solution (as an option) present in linux is better, than _no solution_ in linux.
>  >
>  >  > An implementation that is incompatible with the published RFC will
>  >  > hurt us more then help us.
>  >
>  >  [Dmytro]:
>  > Compatible implementation follows the recent version of document: https://datatracker.ietf.org/doc/draft-mishra-6man-variable-slaac/ The sysctl usage described in the document is used in the implementation to activate/deactivate VSLAAC. By default it is disabled, so there is _*no*_ impact on network behavior by default.
>  >
>  >  > Maciej Żenczykowski, Kernel Networking Developer @ Google
>  >  >
>  >
>  > Take care,
>  > Dmytro.
>  >
>

^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH net-next V9] net: Variable SLAAC: SLAAC with prefixes of arbitrary length in PIO
  2021-07-12 17:51                                 ` Erik Kline
@ 2021-07-13 18:47                                   ` Dmytro Shytyi
  0 siblings, 0 replies; 49+ messages in thread
From: Dmytro Shytyi @ 2021-07-13 18:47 UTC (permalink / raw)
  To: Erik Kline
  Cc: Jakub Kicinski, "Maciej Żenczykowski",
	yoshfuji, liuhangbin, davem, netdev, David Ahern, Joel Scherpelz

In this case, there is another possibility as well: in order to avoid
opening a race to the bottom condition, the VSLAAC code could be
modified to not permit IIDs of length shorter than 64.

What do you think about this possibility?
  
________________
Dmytro SHYTYI

---- On Mon, 12 Jul 2021 19:51:19 +0200 Erik Kline <ek@google.com> wrote ----

 > VSLAAC is indeed quite contentious in the IETF, in large part because 
 > it enables a race to the bottom problem for which there is no solution 
 > in sight. 
 >  
 > I don't think this should be accepted.  It's not in the same category 
 > of some other Y/N/M things where there are issues of kernel size, 
 > absence of some underlying physical support or not, etc. 
 >  
 >  
 > On Mon, Jul 12, 2021 at 9:42 AM Dmytro Shytyi <dmytro@shytyi.net> wrote: 
 > > 
 > > Hello Jakub, Maciej, Yoshfuji and others, 
 > > 
 > > After discussion with co-authors about this particular point "Internet Draft/RFC" we think the following: 
 > > Indeed RFC status shows large agreement among IETF members. And that is the best indicator of a maturity level. 
 > > And that is the best to implement the feature in a stable mainline kernel. 
 > > 
 > > At this time VSLAAC is an individual proposal Internet Draft reflecting the opinion of all authors. 
 > > It is not adopted by any IETF working group. At the same time we consider submission to 3GPP. 
 > > 
 > > The features in the kernel have optionally "Y/N/M" and status "EXPERIMENTAL/STABLE". 
 > > One possibility could be VSLAAC as "N", "EXPERIMENTAL" on the linux-next branch. 
 > > 
 > > Could you consider this possibility more? 
 > > 
 > > If you doubt VSLAAC introducing non-64 bits IID lengths, then one might wonder whether linux supports IIDs of _arbitrary length_, 
 > > as specified in the RFC 7217 with maturity level "Standards Track"? 
 > > 
 > > Best regards, 
 > > Dmytro Shytyi et al. 
 > > 
 > > ---- On Mon, 12 Jul 2021 15:39:27 +0200 Dmytro Shytyi <dmytro@shytyi.net> wrote ---- 
 > > 
 > >  > Hello Maciej, 
 > >  > 
 > >  > 
 > >  > ---- On Sat, 19 Dec 2020 03:40:50 +0100 Maciej Żenczykowski <maze@google.com> wrote ---- 
 > >  > 
 > >  >  > On Fri, Dec 18, 2020 at 6:03 PM Jakub Kicinski <kuba@kernel.org> wrote: 
 > >  >  > > 
 > >  >  > > It'd be great if someone more familiar with our IPv6 code could take a 
 > >  >  > > look. Adding some folks to the CC. 
 > >  >  > > 
 > >  >  > > On Wed, 16 Dec 2020 23:01:29 +0100 Dmytro Shytyi wrote: 
 > >  >  > > > Variable SLAAC [Can be activated via sysctl]: 
 > >  >  > > > SLAAC with prefixes of arbitrary length in PIO (randomly 
 > >  >  > > > generated hostID or stable privacy + privacy extensions). 
 > >  >  > > > The main problem is that SLAAC RA or PD allocates a /64 by the Wireless 
 > >  >  > > > carrier 4G, 5G to a mobile hotspot, however segmentation of the /64 via 
 > >  >  > > > SLAAC is required so that downstream interfaces can be further subnetted. 
 > >  >  > > > Example: uCPE device (4G + WI-FI enabled) receives /64 via Wireless, and 
 > >  >  > > > assigns /72 to VNF-Firewall, /72 to WIFI, /72 to Load-Balancer 
 > >  >  > > > and /72 to wired connected devices. 
 > >  >  > > > IETF document that defines problem statement: 
 > >  >  > > > draft-mishra-v6ops-variable-slaac-problem-stmt 
 > >  >  > > > IETF document that specifies variable slaac: 
 > >  >  > > > draft-mishra-6man-variable-slaac 
 > >  >  > > > 
 > >  >  > > > Signed-off-by: Dmytro Shytyi <dmytro@shytyi.net> 
 > >  >  > > 
 > >  > 
 > >  >  > IMHO acceptance of this should *definitely* wait for the RFC to be 
 > >  >  > accepted/published/standardized (whatever is the right term). 
 > >  > 
 > >  > [Dmytro]: 
 > >  > There is an implementation of Variable SLAAC in the OpenBSD Operating System. 
 > >  > 
 > >  >  > I'm not at all convinced that will happen - this still seems like a 
 > >  >  > very fresh *draft* of an rfc, 
 > >  >  > and I'm *sure* it will be argued about. 
 > >  > 
 > >  >  [Dmytro] 
 > >  > By default, VSLAAC is disabled, so there are _*no*_ impact on network behavior by default. 
 > >  > 
 > >  >  > This sort of functionality will not be particularly useful without 
 > >  >  > widespread industry 
 > >  > 
 > >  > [Dmytro]: 
 > >  > There are use-cases that can profit from radvd-like software and VSLAAC directly. 
 > >  > 
 > >  >  > adoption across *all* major operating systems (Windows, Mac/iOS, 
 > >  >  > Linux/Android, FreeBSD, etc.) 
 > >  > 
 > >  > [Dmytro]: 
 > >  > It should be considered to provide users an _*opportunity*_ to get the required feature. 
 > >  > Solution (as an option) present in linux is better, than _no solution_ in linux. 
 > >  > 
 > >  >  > An implementation that is incompatible with the published RFC will 
 > >  >  > hurt us more then help us. 
 > >  > 
 > >  >  [Dmytro]: 
 > >  > Compatible implementation follows the recent version of document: https://datatracker.ietf.org/doc/draft-mishra-6man-variable-slaac/ The sysctl usage described in the document is used in the implementation to activate/deactivate VSLAAC. By default it is disabled, so there is _*no*_ impact on network behavior by default. 
 > >  > 
 > >  >  > Maciej Żenczykowski, Kernel Networking Developer @ Google 
 > >  >  > 
 > >  > 
 > >  > Take care, 
 > >  > Dmytro. 
 > >  > 
 > > 
 > 


^ permalink raw reply	[flat|nested] 49+ messages in thread

* [PATCH net-next V10] net: Variable SLAAC: SLAAC with prefixes of arbitrary length in PIO
  2020-12-16 22:01                       ` [PATCH net-next V9] " Dmytro Shytyi
  2020-12-19  2:03                         ` Jakub Kicinski
@ 2021-10-13 23:03                         ` Dmytro Shytyi
  2021-10-13 23:20                           ` Dmytro Shytyi
  2021-10-14 13:20                           ` kernel test robot
  1 sibling, 2 replies; 49+ messages in thread
From: Dmytro Shytyi @ 2021-10-13 23:03 UTC (permalink / raw)
  To: Jakub Kicinski, yoshfuji, kuznet, liuhangbin, davem, netdev,
	linux-kernel, ek, Jscherpelz

Variable SLAAC:
[Disabled by default. Can be activated via sysctl]
["Race to the bottom" problem is solved]
SLAAC with prefixes of arbitrary length in PIO (randomly 
generated hostID or stable privacy + privacy extensions). 
The main problem is that SLAAC RA allocates a /64 by the Wireless       
carrier 4G, 5G to a mobile hotspot, however segmentation of shorter net-
work prefix (ex. /48) is required so that downstream interfaces can be further
subnetted. 
Example: uCPE device (4G + WI-FI enabled) receives /48 via Wireless, and 
assigns /56 to VNF-Firewall, /56 to WIFI, /56 to Load-Balancer 
and /56 to wired connected devices. 
IETF document that defines problem statement: 
draft-mishra-v6ops-variable-slaac-problem-stmt 
IETF document that specifies variable slaac: 
draft-mishra-6man-variable-slaac 
 
Signed-off-by: Dmytro Shytyi <dmytro@shytyi.net> 
--- 
diff --git a/include/linux/ipv6.h b/include/linux/ipv6.h
index ef4a69865737..076d99874797 100644
--- a/include/linux/ipv6.h
+++ b/include/linux/ipv6.h
@@ -79,6 +79,7 @@ struct ipv6_devconf {
 	__u32		ioam6_id;
 	__u32		ioam6_id_wide;
 	__u8		ioam6_enabled;
+	__s32		variable_slaac;
 
 	struct ctl_table_header *sysctl_header;
 };
diff --git a/include/uapi/linux/ipv6.h b/include/uapi/linux/ipv6.h
index b243a53fa985..25606c267809 100644
--- a/include/uapi/linux/ipv6.h
+++ b/include/uapi/linux/ipv6.h
@@ -193,6 +193,7 @@ enum {
 	DEVCONF_IOAM6_ENABLED,
 	DEVCONF_IOAM6_ID,
 	DEVCONF_IOAM6_ID_WIDE,
+	DEVCONF_VARIABLE_SLAAC,
 	DEVCONF_MAX
 };
 
diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c
index c6a90b7bbb70..34a12d7f4fb8 100644
--- a/net/ipv6/addrconf.c
+++ b/net/ipv6/addrconf.c
@@ -241,6 +241,7 @@ static struct ipv6_devconf ipv6_devconf __read_mostly = {
 	.ioam6_enabled		= 0,
 	.ioam6_id               = IOAM6_DEFAULT_IF_ID,
 	.ioam6_id_wide		= IOAM6_DEFAULT_IF_ID_WIDE,
+	.variable_slaac		= 0,
 };
 
 static struct ipv6_devconf ipv6_devconf_dflt __read_mostly = {
@@ -300,6 +301,7 @@ static struct ipv6_devconf ipv6_devconf_dflt __read_mostly = {
 	.ioam6_enabled		= 0,
 	.ioam6_id               = IOAM6_DEFAULT_IF_ID,
 	.ioam6_id_wide		= IOAM6_DEFAULT_IF_ID_WIDE,
+	.variable_slaac		= 0,
 };
 
 /* Check if link is ready: is it up and is a valid qdisc available */
@@ -1349,9 +1351,15 @@ static int ipv6_create_tempaddr(struct inet6_ifaddr *ifp, bool block)
 		goto out;
 	}
 	in6_ifa_hold(ifp);
-	memcpy(addr.s6_addr, ifp->addr.s6_addr, 8);
-	ipv6_gen_rnd_iid(&addr);
 
+	if (ifp->prefix_len == 64) {
+		memcpy(addr.s6_addr, ifp->addr.s6_addr, 8);
+		ipv6_gen_rnd_iid(&addr);
+	} else if (ifp->prefix_len > 0 && ifp->prefix_len < 64 &&
+		   idev->cnf.variable_slaac) {
+		get_random_bytes(addr.s6_addr, 16);
+		ipv6_addr_prefix_copy(&addr, &ifp->addr, ifp->prefix_len);
+	}
 	age = (now - ifp->tstamp) / HZ;
 
 	regen_advance = idev->cnf.regen_max_retry *
@@ -2579,6 +2587,31 @@ static bool is_addr_mode_generate_stable(struct inet6_dev *idev)
 	       idev->cnf.addr_gen_mode == IN6_ADDR_GEN_MODE_RANDOM;
 }
 
+static struct inet6_ifaddr *ipv6_cmp_rcvd_prsnt_prfxs(struct inet6_ifaddr *ifp,
+						      struct inet6_dev *in6_dev,
+						      struct net *net,
+						      const struct prefix_info *pinfo)
+{
+	struct inet6_ifaddr *result = NULL;
+	bool prfxs_equal;
+
+	rcu_read_lock();
+	list_for_each_entry_rcu(ifp, &in6_dev->addr_list, if_list) {
+		if (!net_eq(dev_net(ifp->idev->dev), net))
+			continue;
+		prfxs_equal =
+			ipv6_prefix_equal(&pinfo->prefix, &ifp->addr, pinfo->prefix_len);
+		if (prfxs_equal && pinfo->prefix_len == ifp->prefix_len) {
+			result = ifp;
+			in6_ifa_hold(ifp);
+			break;
+		}
+	}
+	rcu_read_unlock();
+
+	return result;
+}
+
 int addrconf_prefix_rcv_add_addr(struct net *net, struct net_device *dev,
 				 const struct prefix_info *pinfo,
 				 struct inet6_dev *in6_dev,
@@ -2586,9 +2619,17 @@ int addrconf_prefix_rcv_add_addr(struct net *net, struct net_device *dev,
 				 u32 addr_flags, bool sllao, bool tokenized,
 				 __u32 valid_lft, u32 prefered_lft)
 {
-	struct inet6_ifaddr *ifp = ipv6_get_ifaddr(net, addr, dev, 1);
+	struct inet6_ifaddr *ifp = NULL;
+	int plen = pinfo->prefix_len;
 	int create = 0;
 
+	if (plen > 0 && plen < 64 &&
+	    in6_dev->cnf.addr_gen_mode != IN6_ADDR_GEN_MODE_STABLE_PRIVACY &&
+	    in6_dev->cnf.variable_slaac)
+		ifp = ipv6_cmp_rcvd_prsnt_prfxs(ifp, in6_dev, net, pinfo);
+	else
+		ifp = ipv6_get_ifaddr(net, addr, dev, 1);
+
 	if (!ifp && valid_lft) {
 		int max_addresses = in6_dev->cnf.max_addresses;
 		struct ifa6_config cfg = {
@@ -2667,6 +2708,94 @@ int addrconf_prefix_rcv_add_addr(struct net *net, struct net_device *dev,
 }
 EXPORT_SYMBOL_GPL(addrconf_prefix_rcv_add_addr);
 
+static bool ipv6_reserved_interfaceid(struct in6_addr address)
+{
+	if ((address.s6_addr32[2] | address.s6_addr32[3]) == 0)
+		return true;
+
+	if (address.s6_addr32[2] == htonl(0x02005eff) &&
+	    ((address.s6_addr32[3] & htonl(0xfe000000)) == htonl(0xfe000000)))
+		return true;
+
+	if (address.s6_addr32[2] == htonl(0xfdffffff) &&
+	    ((address.s6_addr32[3] & htonl(0xffffff80)) == htonl(0xffffff80)))
+		return true;
+
+	return false;
+}
+
+static int ipv6_gen_addr_var_plen(struct in6_addr *address,
+				  u8 dad_count,
+				  const struct inet6_dev *idev,
+				  unsigned int rcvd_prfx_len,
+				  bool stable_privacy_mode)
+{
+	union data_union {
+		char __data[SHA1_BLOCK_SIZE];
+		struct {
+			struct in6_addr secret;
+			__be32 prefix[2];
+			unsigned char hwaddr[MAX_ADDR_LEN];
+			u8 dad_count;
+		} __packed;
+	};
+	union data_union *data;
+	struct in6_addr *secret;
+	struct in6_addr *temp;
+	struct net *net;
+	int *workspace;
+	int *digest;
+
+	workspace = kmalloc_array(SHA1_WORKSPACE_WORDS, sizeof(__u32), GFP_KERNEL);
+	digest = kmalloc_array(SHA1_DIGEST_WORDS, sizeof(__u32), GFP_KERNEL);
+	data = kmalloc(sizeof(*data), GFP_KERNEL);
+	secret = kmalloc(sizeof(*secret), GFP_KERNEL);
+	temp = kmalloc(sizeof(*temp), GFP_KERNEL);
+	net = dev_net(idev->dev);
+
+	BUILD_BUG_ON(sizeof(data->__data) != sizeof(*data));
+
+	if (stable_privacy_mode) {
+		if (idev->cnf.stable_secret.initialized)
+			*secret = idev->cnf.stable_secret.secret;
+		else if (net->ipv6.devconf_dflt->stable_secret.initialized)
+			*secret = net->ipv6.devconf_dflt->stable_secret.secret;
+		else
+			return -1;
+	}
+
+retry:
+	if (stable_privacy_mode) {
+		sha1_init(digest);
+		memset(data, 0, sizeof(*data));
+		memset(workspace, 0, sizeof(*workspace));
+		memcpy(data->hwaddr, idev->dev->perm_addr, idev->dev->addr_len);
+		data->prefix[0] = address->s6_addr32[0];
+		data->prefix[1] = address->s6_addr32[1];
+		data->secret = *secret;
+		data->dad_count = dad_count;
+
+		sha1_transform(digest, data->__data, workspace);
+
+		temp->s6_addr32[0] = (__force __be32)digest[0];
+		temp->s6_addr32[1] = (__force __be32)digest[1];
+		temp->s6_addr32[2] = (__force __be32)digest[2];
+		temp->s6_addr32[3] = (__force __be32)digest[3];
+	} else {
+		get_random_bytes(temp->s6_addr32, 16);
+	}
+
+	if (ipv6_reserved_interfaceid(*temp)) {
+		dad_count++;
+		if (dad_count > dev_net(idev->dev)->ipv6.sysctl.idgen_retries)
+			return -1;
+		goto retry;
+	}
+	ipv6_addr_prefix_copy(temp, address, rcvd_prfx_len);
+	*address = *temp;
+	return 0;
+}
+
 void addrconf_prefix_rcv(struct net_device *dev, u8 *opt, int len, bool sllao)
 {
 	struct prefix_info *pinfo;
@@ -2791,9 +2920,33 @@ void addrconf_prefix_rcv(struct net_device *dev, u8 *opt, int len, bool sllao)
 				dev_addr_generated = true;
 			}
 			goto ok;
+		} else if (pinfo->prefix_len > 0 && pinfo->prefix_len < 64 &&
+			   in6_dev->cnf.variable_slaac) {
+			/* SLAAC with prefixes of arbitrary length (Variable SLAAC).
+			 * draft-mishra-6man-variable-slaac
+			 * draft-mishra-v6ops-variable-slaac-problem-stmt
+			 */
+			memcpy(&addr, &pinfo->prefix, 16);
+			if (in6_dev->cnf.addr_gen_mode == IN6_ADDR_GEN_MODE_STABLE_PRIVACY) {
+				if (!ipv6_gen_addr_var_plen(&addr,
+							    0,
+							    in6_dev,
+							    pinfo->prefix_len,
+							    true)) {
+					addr_flags |= IFA_F_STABLE_PRIVACY;
+					goto ok;
+				}
+			} else if (!ipv6_gen_addr_var_plen(&addr,
+							   0,
+							   in6_dev,
+							   pinfo->prefix_len,
+							   false)) {
+				goto ok;
+			}
+		} else {
+			net_dbg_ratelimited("IPv6: Prefix with unexpected length %d\n",
+					    pinfo->prefix_len);
 		}
-		net_dbg_ratelimited("IPv6 addrconf: prefix with wrong length %d\n",
-				    pinfo->prefix_len);
 		goto put;
 
 ok:
@@ -5542,6 +5679,7 @@ static inline void ipv6_store_devconf(struct ipv6_devconf *cnf,
 	array[DEVCONF_IOAM6_ENABLED] = cnf->ioam6_enabled;
 	array[DEVCONF_IOAM6_ID] = cnf->ioam6_id;
 	array[DEVCONF_IOAM6_ID_WIDE] = cnf->ioam6_id_wide;
+	array[DEVCONF_VARIABLE_SLAAC] = cnf->variable_slaac;
 }
 
 static inline size_t inet6_ifla6_size(void)
@@ -6905,6 +7043,13 @@ static const struct ctl_table addrconf_sysctl[] = {
 		.proc_handler	= proc_dointvec,
 
 	},
+	{
+		.procname	= "variable_slaac",
+		.data		= &ipv6_devconf.variable_slaac,
+		.maxlen		= sizeof(int),
+		.mode		= 0644,
+		.proc_handler	= proc_dointvec,
+	},
 	{
 		.procname	= "seg6_enabled",
 		.data		= &ipv6_devconf.seg6_enabled,


^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH net-next V10] net: Variable SLAAC: SLAAC with prefixes of arbitrary length in PIO
  2021-10-13 23:03                         ` [PATCH net-next V10] " Dmytro Shytyi
@ 2021-10-13 23:20                           ` Dmytro Shytyi
  2021-10-14 18:26                             ` Erik Kline
  2021-10-14 13:20                           ` kernel test robot
  1 sibling, 1 reply; 49+ messages in thread
From: Dmytro Shytyi @ 2021-10-13 23:20 UTC (permalink / raw)
  To: Jakub Kicinski, yoshfuji, kuznet, liuhangbin, davem, netdev,
	linux-kernel, ek, Jscherpelz

Hello Jakub, Eric, All,

Jakub,
> +retry:
> +    spin_lock_bh(&lock);
> +    if (stable_privacy_mode) {
> +        sha1_init(digest);
> +        memset(&data, 0, sizeof(data));
> +        memset(workspace, 0, sizeof(workspace));
> +        memcpy(data.hwaddr, idev->dev->perm_addr, idev->dev->addr_len);
> +        data.prefix[0] = address->s6_addr32[0];
> +        data.prefix[1] = address->s6_addr32[1];
> +        data.secret = secret;
> +        data.dad_count = dad_count;
> +
> +        sha1_transform(digest, data.__data, workspace);
> +
> +        temp.s6_addr32[0] = (__force __be32)digest[0];
> +        temp.s6_addr32[1] = (__force __be32)digest[1];
> +        temp.s6_addr32[2] = (__force __be32)digest[2];
> +        temp.s6_addr32[3] = (__force __be32)digest[3];
> +    } else {
> +        get_random_bytes(temp.s6_addr32, 16);
> +    }
> +
> +    spin_unlock_bh(&lock);

>Is there a reason this code declares all this state on the stack and
>protects it with a lock rather than just allocating the memory with
>kmalloc()?

As you proposed earlier, in "PATCH net-next V10" the memory is allocated with kmalloc.

Eric,
This patch solves "Race to the bottom"  problem  in  VSLAAC. 

Thank you!
                              
Best Regards,
Dmytro SHYTYI

---- On Thu, 14 Oct 2021 01:03:00 +0200 Dmytro Shytyi <dmytro@shytyi.net> wrote ----

 > Variable SLAAC: 
 > [Disabled by default. Can be activated via sysctl] 
 > ["Race to the bottom" problem is solved] 
 > SLAAC with prefixes of arbitrary length in PIO (randomly 
 > generated hostID or stable privacy + privacy extensions). 
 > The main problem is that SLAAC RA allocates a /64 by the Wireless 
 > carrier 4G, 5G to a mobile hotspot, however segmentation of shorter net- 
 > work prefix (ex. /48) is required so that downstream interfaces can be further 
 > subnetted. 
 > Example: uCPE device (4G + WI-FI enabled) receives /48 via Wireless, and 
 > assigns /56 to VNF-Firewall, /56 to WIFI, /56 to Load-Balancer 
 > and /56 to wired connected devices. 
 > IETF document that defines problem statement: 
 > draft-mishra-v6ops-variable-slaac-problem-stmt 
 > IETF document that specifies variable slaac: 
 > draft-mishra-6man-variable-slaac 
 >  
 > Signed-off-by: Dmytro Shytyi <dmytro@shytyi.net> 
 > --- 
 > diff --git a/include/linux/ipv6.h b/include/linux/ipv6.h 
 > index ef4a69865737..076d99874797 100644 
 > --- a/include/linux/ipv6.h 
 > +++ b/include/linux/ipv6.h 
 > @@ -79,6 +79,7 @@ struct ipv6_devconf { 
 >      __u32        ioam6_id; 
 >      __u32        ioam6_id_wide; 
 >      __u8        ioam6_enabled; 
 > +    __s32        variable_slaac; 
 >  
 >      struct ctl_table_header *sysctl_header; 
 >  }; 
 > diff --git a/include/uapi/linux/ipv6.h b/include/uapi/linux/ipv6.h 
 > index b243a53fa985..25606c267809 100644 
 > --- a/include/uapi/linux/ipv6.h 
 > +++ b/include/uapi/linux/ipv6.h 
 > @@ -193,6 +193,7 @@ enum { 
 >      DEVCONF_IOAM6_ENABLED, 
 >      DEVCONF_IOAM6_ID, 
 >      DEVCONF_IOAM6_ID_WIDE, 
 > +    DEVCONF_VARIABLE_SLAAC, 
 >      DEVCONF_MAX 
 >  }; 
 >  
 > diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c 
 > index c6a90b7bbb70..34a12d7f4fb8 100644 
 > --- a/net/ipv6/addrconf.c 
 > +++ b/net/ipv6/addrconf.c 
 > @@ -241,6 +241,7 @@ static struct ipv6_devconf ipv6_devconf __read_mostly = { 
 >      .ioam6_enabled        = 0, 
 >      .ioam6_id               = IOAM6_DEFAULT_IF_ID, 
 >      .ioam6_id_wide        = IOAM6_DEFAULT_IF_ID_WIDE, 
 > +    .variable_slaac        = 0, 
 >  }; 
 >  
 >  static struct ipv6_devconf ipv6_devconf_dflt __read_mostly = { 
 > @@ -300,6 +301,7 @@ static struct ipv6_devconf ipv6_devconf_dflt __read_mostly = { 
 >      .ioam6_enabled        = 0, 
 >      .ioam6_id               = IOAM6_DEFAULT_IF_ID, 
 >      .ioam6_id_wide        = IOAM6_DEFAULT_IF_ID_WIDE, 
 > +    .variable_slaac        = 0, 
 >  }; 
 >  
 >  /* Check if link is ready: is it up and is a valid qdisc available */ 
 > @@ -1349,9 +1351,15 @@ static int ipv6_create_tempaddr(struct inet6_ifaddr *ifp, bool block) 
 >          goto out; 
 >      } 
 >      in6_ifa_hold(ifp); 
 > -    memcpy(addr.s6_addr, ifp->addr.s6_addr, 8); 
 > -    ipv6_gen_rnd_iid(&addr); 
 >  
 > +    if (ifp->prefix_len == 64) { 
 > +        memcpy(addr.s6_addr, ifp->addr.s6_addr, 8); 
 > +        ipv6_gen_rnd_iid(&addr); 
 > +    } else if (ifp->prefix_len > 0 && ifp->prefix_len < 64 && 
 > +           idev->cnf.variable_slaac) { 
 > +        get_random_bytes(addr.s6_addr, 16); 
 > +        ipv6_addr_prefix_copy(&addr, &ifp->addr, ifp->prefix_len); 
 > +    } 
 >      age = (now - ifp->tstamp) / HZ; 
 >  
 >      regen_advance = idev->cnf.regen_max_retry * 
 > @@ -2579,6 +2587,31 @@ static bool is_addr_mode_generate_stable(struct inet6_dev *idev) 
 >             idev->cnf.addr_gen_mode == IN6_ADDR_GEN_MODE_RANDOM; 
 >  } 
 >  
 > +static struct inet6_ifaddr *ipv6_cmp_rcvd_prsnt_prfxs(struct inet6_ifaddr *ifp, 
 > +                              struct inet6_dev *in6_dev, 
 > +                              struct net *net, 
 > +                              const struct prefix_info *pinfo) 
 > +{ 
 > +    struct inet6_ifaddr *result = NULL; 
 > +    bool prfxs_equal; 
 > + 
 > +    rcu_read_lock(); 
 > +    list_for_each_entry_rcu(ifp, &in6_dev->addr_list, if_list) { 
 > +        if (!net_eq(dev_net(ifp->idev->dev), net)) 
 > +            continue; 
 > +        prfxs_equal = 
 > +            ipv6_prefix_equal(&pinfo->prefix, &ifp->addr, pinfo->prefix_len); 
 > +        if (prfxs_equal && pinfo->prefix_len == ifp->prefix_len) { 
 > +            result = ifp; 
 > +            in6_ifa_hold(ifp); 
 > +            break; 
 > +        } 
 > +    } 
 > +    rcu_read_unlock(); 
 > + 
 > +    return result; 
 > +} 
 > + 
 >  int addrconf_prefix_rcv_add_addr(struct net *net, struct net_device *dev, 
 >                   const struct prefix_info *pinfo, 
 >                   struct inet6_dev *in6_dev, 
 > @@ -2586,9 +2619,17 @@ int addrconf_prefix_rcv_add_addr(struct net *net, struct net_device *dev, 
 >                   u32 addr_flags, bool sllao, bool tokenized, 
 >                   __u32 valid_lft, u32 prefered_lft) 
 >  { 
 > -    struct inet6_ifaddr *ifp = ipv6_get_ifaddr(net, addr, dev, 1); 
 > +    struct inet6_ifaddr *ifp = NULL; 
 > +    int plen = pinfo->prefix_len; 
 >      int create = 0; 
 >  
 > +    if (plen > 0 && plen < 64 && 
 > +        in6_dev->cnf.addr_gen_mode != IN6_ADDR_GEN_MODE_STABLE_PRIVACY && 
 > +        in6_dev->cnf.variable_slaac) 
 > +        ifp = ipv6_cmp_rcvd_prsnt_prfxs(ifp, in6_dev, net, pinfo); 
 > +    else 
 > +        ifp = ipv6_get_ifaddr(net, addr, dev, 1); 
 > + 
 >      if (!ifp && valid_lft) { 
 >          int max_addresses = in6_dev->cnf.max_addresses; 
 >          struct ifa6_config cfg = { 
 > @@ -2667,6 +2708,94 @@ int addrconf_prefix_rcv_add_addr(struct net *net, struct net_device *dev, 
 >  } 
 >  EXPORT_SYMBOL_GPL(addrconf_prefix_rcv_add_addr); 
 >  
 > +static bool ipv6_reserved_interfaceid(struct in6_addr address) 
 > +{ 
 > +    if ((address.s6_addr32[2] | address.s6_addr32[3]) == 0) 
 > +        return true; 
 > + 
 > +    if (address.s6_addr32[2] == htonl(0x02005eff) && 
 > +        ((address.s6_addr32[3] & htonl(0xfe000000)) == htonl(0xfe000000))) 
 > +        return true; 
 > + 
 > +    if (address.s6_addr32[2] == htonl(0xfdffffff) && 
 > +        ((address.s6_addr32[3] & htonl(0xffffff80)) == htonl(0xffffff80))) 
 > +        return true; 
 > + 
 > +    return false; 
 > +} 
 > + 
 > +static int ipv6_gen_addr_var_plen(struct in6_addr *address, 
 > +                  u8 dad_count, 
 > +                  const struct inet6_dev *idev, 
 > +                  unsigned int rcvd_prfx_len, 
 > +                  bool stable_privacy_mode) 
 > +{ 
 > +    union data_union { 
 > +        char __data[SHA1_BLOCK_SIZE]; 
 > +        struct { 
 > +            struct in6_addr secret; 
 > +            __be32 prefix[2]; 
 > +            unsigned char hwaddr[MAX_ADDR_LEN]; 
 > +            u8 dad_count; 
 > +        } __packed; 
 > +    }; 
 > +    union data_union *data; 
 > +    struct in6_addr *secret; 
 > +    struct in6_addr *temp; 
 > +    struct net *net; 
 > +    int *workspace; 
 > +    int *digest; 
 > + 
 > +    workspace = kmalloc_array(SHA1_WORKSPACE_WORDS, sizeof(__u32), GFP_KERNEL); 
 > +    digest = kmalloc_array(SHA1_DIGEST_WORDS, sizeof(__u32), GFP_KERNEL); 
 > +    data = kmalloc(sizeof(*data), GFP_KERNEL); 
 > +    secret = kmalloc(sizeof(*secret), GFP_KERNEL); 
 > +    temp = kmalloc(sizeof(*temp), GFP_KERNEL); 
 > +    net = dev_net(idev->dev); 
 > + 
 > +    BUILD_BUG_ON(sizeof(data->__data) != sizeof(*data)); 
 > + 
 > +    if (stable_privacy_mode) { 
 > +        if (idev->cnf.stable_secret.initialized) 
 > +            *secret = idev->cnf.stable_secret.secret; 
 > +        else if (net->ipv6.devconf_dflt->stable_secret.initialized) 
 > +            *secret = net->ipv6.devconf_dflt->stable_secret.secret; 
 > +        else 
 > +            return -1; 
 > +    } 
 > + 
 > +retry: 
 > +    if (stable_privacy_mode) { 
 > +        sha1_init(digest); 
 > +        memset(data, 0, sizeof(*data)); 
 > +        memset(workspace, 0, sizeof(*workspace)); 
 > +        memcpy(data->hwaddr, idev->dev->perm_addr, idev->dev->addr_len); 
 > +        data->prefix[0] = address->s6_addr32[0]; 
 > +        data->prefix[1] = address->s6_addr32[1]; 
 > +        data->secret = *secret; 
 > +        data->dad_count = dad_count; 
 > + 
 > +        sha1_transform(digest, data->__data, workspace); 
 > + 
 > +        temp->s6_addr32[0] = (__force __be32)digest[0]; 
 > +        temp->s6_addr32[1] = (__force __be32)digest[1]; 
 > +        temp->s6_addr32[2] = (__force __be32)digest[2]; 
 > +        temp->s6_addr32[3] = (__force __be32)digest[3]; 
 > +    } else { 
 > +        get_random_bytes(temp->s6_addr32, 16); 
 > +    } 
 > + 
 > +    if (ipv6_reserved_interfaceid(*temp)) { 
 > +        dad_count++; 
 > +        if (dad_count > dev_net(idev->dev)->ipv6.sysctl.idgen_retries) 
 > +            return -1; 
 > +        goto retry; 
 > +    } 
 > +    ipv6_addr_prefix_copy(temp, address, rcvd_prfx_len); 
 > +    *address = *temp; 
 > +    return 0; 
 > +} 
 > + 
 >  void addrconf_prefix_rcv(struct net_device *dev, u8 *opt, int len, bool sllao) 
 >  { 
 >      struct prefix_info *pinfo; 
 > @@ -2791,9 +2920,33 @@ void addrconf_prefix_rcv(struct net_device *dev, u8 *opt, int len, bool sllao) 
 >                  dev_addr_generated = true; 
 >              } 
 >              goto ok; 
 > +        } else if (pinfo->prefix_len > 0 && pinfo->prefix_len < 64 && 
 > +               in6_dev->cnf.variable_slaac) { 
 > +            /* SLAAC with prefixes of arbitrary length (Variable SLAAC). 
 > +             * draft-mishra-6man-variable-slaac 
 > +             * draft-mishra-v6ops-variable-slaac-problem-stmt 
 > +             */ 
 > +            memcpy(&addr, &pinfo->prefix, 16); 
 > +            if (in6_dev->cnf.addr_gen_mode == IN6_ADDR_GEN_MODE_STABLE_PRIVACY) { 
 > +                if (!ipv6_gen_addr_var_plen(&addr, 
 > +                                0, 
 > +                                in6_dev, 
 > +                                pinfo->prefix_len, 
 > +                                true)) { 
 > +                    addr_flags |= IFA_F_STABLE_PRIVACY; 
 > +                    goto ok; 
 > +                } 
 > +            } else if (!ipv6_gen_addr_var_plen(&addr, 
 > +                               0, 
 > +                               in6_dev, 
 > +                               pinfo->prefix_len, 
 > +                               false)) { 
 > +                goto ok; 
 > +            } 
 > +        } else { 
 > +            net_dbg_ratelimited("IPv6: Prefix with unexpected length %d\n", 
 > +                        pinfo->prefix_len); 
 >          } 
 > -        net_dbg_ratelimited("IPv6 addrconf: prefix with wrong length %d\n", 
 > -                    pinfo->prefix_len); 
 >          goto put; 
 >  
 >  ok: 
 > @@ -5542,6 +5679,7 @@ static inline void ipv6_store_devconf(struct ipv6_devconf *cnf, 
 >      array[DEVCONF_IOAM6_ENABLED] = cnf->ioam6_enabled; 
 >      array[DEVCONF_IOAM6_ID] = cnf->ioam6_id; 
 >      array[DEVCONF_IOAM6_ID_WIDE] = cnf->ioam6_id_wide; 
 > +    array[DEVCONF_VARIABLE_SLAAC] = cnf->variable_slaac; 
 >  } 
 >  
 >  static inline size_t inet6_ifla6_size(void) 
 > @@ -6905,6 +7043,13 @@ static const struct ctl_table addrconf_sysctl[] = { 
 >          .proc_handler    = proc_dointvec, 
 >  
 >      }, 
 > +    { 
 > +        .procname    = "variable_slaac", 
 > +        .data        = &ipv6_devconf.variable_slaac, 
 > +        .maxlen        = sizeof(int), 
 > +        .mode        = 0644, 
 > +        .proc_handler    = proc_dointvec, 
 > +    }, 
 >      { 
 >          .procname    = "seg6_enabled", 
 >          .data        = &ipv6_devconf.seg6_enabled, 
 >  
 > 

^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH net-next V10] net: Variable SLAAC: SLAAC with prefixes of arbitrary length in PIO
  2021-10-13 23:03                         ` [PATCH net-next V10] " Dmytro Shytyi
  2021-10-13 23:20                           ` Dmytro Shytyi
@ 2021-10-14 13:20                           ` kernel test robot
  1 sibling, 0 replies; 49+ messages in thread
From: kernel test robot @ 2021-10-14 13:20 UTC (permalink / raw)
  To: Dmytro Shytyi, Jakub Kicinski, yoshfuji, kuznet, liuhangbin,
	davem, netdev, linux-kernel, ek, Jscherpelz
  Cc: llvm, kbuild-all

[-- Attachment #1: Type: text/plain, Size: 3278 bytes --]

Hi Dmytro,

Thank you for the patch! Yet something to improve:

[auto build test ERROR on net-next/master]

url:    https://github.com/0day-ci/linux/commits/Dmytro-Shytyi/net-Variable-SLAAC-SLAAC-with-prefixes-of-arbitrary-length-in-PIO/20211014-072015
base:   https://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next.git 39e222bfd7f37e7a98069869375b903d7096c113
config: x86_64-randconfig-r013-20211014 (attached as .config)
compiler: clang version 14.0.0 (https://github.com/llvm/llvm-project 6c76d0101193aa4eb891a6954ff047eda2f9cf71)
reproduce (this is a W=1 build):
        wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # https://github.com/0day-ci/linux/commit/780991c4b4a62473ddc2bd73ea399061a1c41afd
        git remote add linux-review https://github.com/0day-ci/linux
        git fetch --no-tags linux-review Dmytro-Shytyi/net-Variable-SLAAC-SLAAC-with-prefixes-of-arbitrary-length-in-PIO/20211014-072015
        git checkout 780991c4b4a62473ddc2bd73ea399061a1c41afd
        # save the attached .config to linux build tree
        mkdir build_dir
        COMPILER_INSTALL_PATH=$HOME/0day COMPILER=clang make.cross W=1 O=build_dir ARCH=x86_64 SHELL=/bin/bash

If you fix the issue, kindly add following tag as appropriate
Reported-by: kernel test robot <lkp@intel.com>

All errors (new ones prefixed by >>):

>> net/ipv6/addrconf.c:3355:13: error: redefinition of 'ipv6_reserved_interfaceid'
   static bool ipv6_reserved_interfaceid(struct in6_addr address)
               ^
   net/ipv6/addrconf.c:2711:13: note: previous definition is here
   static bool ipv6_reserved_interfaceid(struct in6_addr address)
               ^
   1 error generated.


vim +/ipv6_reserved_interfaceid +3355 net/ipv6/addrconf.c

^1da177e4c3f41 Linus Torvalds       2005-04-16  3354  
622c81d57b392c Hannes Frederic Sowa 2015-03-23 @3355  static bool ipv6_reserved_interfaceid(struct in6_addr address)
622c81d57b392c Hannes Frederic Sowa 2015-03-23  3356  {
622c81d57b392c Hannes Frederic Sowa 2015-03-23  3357  	if ((address.s6_addr32[2] | address.s6_addr32[3]) == 0)
622c81d57b392c Hannes Frederic Sowa 2015-03-23  3358  		return true;
622c81d57b392c Hannes Frederic Sowa 2015-03-23  3359  
622c81d57b392c Hannes Frederic Sowa 2015-03-23  3360  	if (address.s6_addr32[2] == htonl(0x02005eff) &&
622c81d57b392c Hannes Frederic Sowa 2015-03-23  3361  	    ((address.s6_addr32[3] & htonl(0xfe000000)) == htonl(0xfe000000)))
622c81d57b392c Hannes Frederic Sowa 2015-03-23  3362  		return true;
622c81d57b392c Hannes Frederic Sowa 2015-03-23  3363  
622c81d57b392c Hannes Frederic Sowa 2015-03-23  3364  	if (address.s6_addr32[2] == htonl(0xfdffffff) &&
622c81d57b392c Hannes Frederic Sowa 2015-03-23  3365  	    ((address.s6_addr32[3] & htonl(0xffffff80)) == htonl(0xffffff80)))
622c81d57b392c Hannes Frederic Sowa 2015-03-23  3366  		return true;
622c81d57b392c Hannes Frederic Sowa 2015-03-23  3367  
622c81d57b392c Hannes Frederic Sowa 2015-03-23  3368  	return false;
622c81d57b392c Hannes Frederic Sowa 2015-03-23  3369  }
622c81d57b392c Hannes Frederic Sowa 2015-03-23  3370  

---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/kbuild-all@lists.01.org

[-- Attachment #2: .config.gz --]
[-- Type: application/gzip, Size: 31559 bytes --]

^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH net-next V10] net: Variable SLAAC: SLAAC with prefixes of arbitrary length in PIO
  2021-10-13 23:20                           ` Dmytro Shytyi
@ 2021-10-14 18:26                             ` Erik Kline
  2021-10-14 21:36                               ` Dmytro Shytyi
  0 siblings, 1 reply; 49+ messages in thread
From: Erik Kline @ 2021-10-14 18:26 UTC (permalink / raw)
  To: Dmytro Shytyi
  Cc: Jakub Kicinski, yoshfuji, kuznet, liuhangbin, davem, netdev,
	linux-kernel, Jscherpelz

> This patch solves "Race to the bottom"  problem  in  VSLAAC.

How exactly does this "solve" the fundamental problem?

^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH net-next V10] net: Variable SLAAC: SLAAC with prefixes of arbitrary length in PIO
  2021-10-14 18:26                             ` Erik Kline
@ 2021-10-14 21:36                               ` Dmytro Shytyi
  0 siblings, 0 replies; 49+ messages in thread
From: Dmytro Shytyi @ 2021-10-14 21:36 UTC (permalink / raw)
  To: Erik Kline
  Cc: Jakub Kicinski, yoshfuji, kuznet, liuhangbin, davem, netdev,
	linux-kernel, Jscherpelz

 > > This patch solves "Race to the bottom"  problem  in  VSLAAC. 
 >  
 > How exactly does this "solve" the fundamental problem? 
 > 

VSLAAC is replaced by the SLAAC starting from /64 
 
if (pinfo->prefix_len == 64) {
64_bit_addr_gen
} else if (pinfo->prefix_len > 0 && pinfo->prefix_len < 64 &&
              in6_dev->cnf.variable_slaac) {
variable_bit_addr_gen
 } else {
             net_dbg_ratelimited("IPv6: Prefix with unexpected length %d\n",
                                            pinfo->prefix_len);
 }

I meant to say that this is no longer an issue in VSLAAC particular context, considering /128 bits the "bottom".
In this version of the patch, we are not reaching the /128 bit "bottom".

Best regards,
Dmytro SHYTYI.

^ permalink raw reply	[flat|nested] 49+ messages in thread

end of thread, other threads:[~2021-10-14 21:37 UTC | newest]

Thread overview: 49+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
     [not found] <175b25d0c79.f8ce5734515834.1635475016968827598@shytyi.net>
2020-11-10 17:45 ` [PATCH net-next] net: Variable SLAAC: SLAAC with prefixes of arbitrary length in PIO Dmytro Shytyi
2020-11-11  1:34   ` kernel test robot
2020-11-11 20:37     ` [PATCH net-next V2] " Dmytro Shytyi
2020-11-12 15:44     ` [PATCH net-next V3] " Dmytro Shytyi
2020-11-12 16:55       ` Hideaki Yoshifuji
2020-11-13  1:50         ` Dmytro Shytyi
2020-11-13  0:21       ` Jakub Kicinski
2020-11-13  1:50         ` Dmytro Shytyi
2020-11-13  1:56       ` [PATCH net-next V4] " Dmytro Shytyi
2020-11-13 12:38         ` Hideaki Yoshifuji
2020-11-13 19:09           ` Dmytro Shytyi
2020-11-13 19:36         ` [PATCH net-next V5] " Dmytro Shytyi
2020-11-17 20:43           ` Jakub Kicinski
2020-11-18 13:41             ` Dmytro Shytyi
2020-11-18 15:50               ` Jakub Kicinski
2020-11-18 15:56                 ` Dmytro Shytyi
2020-11-19 13:37             ` [PATCH net-next V6] " Dmytro Shytyi
2020-11-19 18:44               ` Jakub Kicinski
2020-11-19 19:31                 ` Dmytro Shytyi
2020-11-20  1:20                   ` Jakub Kicinski
2020-11-20  9:26                   ` [PATCH net-next V7] " Dmytro Shytyi
2020-11-23 13:26                     ` Hideaki Yoshifuji
2020-11-27 16:54                       ` Dmytro Shytyi
2020-12-09  3:27                     ` [PATCH net-next V8] " Dmytro Shytyi
2020-12-16  0:00                       ` David Miller
2020-12-16 14:01                         ` Dmytro Shytyi
2020-12-16 17:28                           ` Jakub Kicinski
2020-12-16 21:56                             ` Dmytro Shytyi
2020-12-16 22:01                       ` [PATCH net-next V9] " Dmytro Shytyi
2020-12-19  2:03                         ` Jakub Kicinski
2020-12-19  2:40                           ` Maciej Żenczykowski
2021-07-12 13:39                             ` Dmytro Shytyi
2021-07-12 16:42                               ` Dmytro Shytyi
2021-07-12 17:51                                 ` Erik Kline
2021-07-13 18:47                                   ` Dmytro Shytyi
2021-07-10 19:24                           ` Dmytro Shytyi
2021-07-12 13:23                             ` Dmytro Shytyi
2021-10-13 23:03                         ` [PATCH net-next V10] " Dmytro Shytyi
2021-10-13 23:20                           ` Dmytro Shytyi
2021-10-14 18:26                             ` Erik Kline
2021-10-14 21:36                               ` Dmytro Shytyi
2021-10-14 13:20                           ` kernel test robot
2020-11-13  0:24     ` [PATCH net-next] " Jakub Kicinski
2020-11-13  0:32       ` [kbuild-all] " Li, Philip
2020-11-13  0:51         ` Jakub Kicinski
2020-11-13  1:01           ` Li, Philip
2020-11-13  1:43       ` Dave Hansen
2020-11-13  1:53         ` Jakub Kicinski
2020-11-13  2:01           ` Dave Hansen

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).