2020-11-10 17:50:19

by Dmytro Shytyi

[permalink] [raw]
Subject: Re: [PATCH net-next] net: Variable SLAAC: SLAAC with prefixes of arbitrary length in PIO

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 <[email protected]>
---
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
+ * <[email protected]> prefixes of arbitrary length.
* Janos Farkas : delete timer on ifdown
* <[email protected]>
* 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


2020-11-11 01:38:07

by kernel test robot

[permalink] [raw]
Subject: Re: Re: [PATCH net-next] net: Variable SLAAC: SLAAC with prefixes of arbitrary length in PIO

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 <[email protected]>

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/[email protected]


Attachments:
(No filename) (12.86 kB)
.config.gz (52.25 kB)
Download all attachments

2020-11-11 20:42:54

by Dmytro Shytyi

[permalink] [raw]
Subject: [PATCH net-next V2] net: Variable SLAAC: SLAAC with prefixes of arbitrary length in PIO

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 <[email protected]>
Reported-by: kernel test robot <[email protected]>
---
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
+ * <[email protected]> prefixes of arbitrary length.
* Janos Farkas : delete timer on ifdown
* <[email protected]>
* 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

2020-11-12 15:50:12

by Dmytro Shytyi

[permalink] [raw]
Subject: [PATCH net-next V3] net: Variable SLAAC: SLAAC with prefixes of arbitrary length in PIO

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 <[email protected]>
Reported-by: kernel test robot <[email protected]>
---
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
+ * <[email protected]> prefixes of arbitrary length.
* Janos Farkas : delete timer on ifdown
* <[email protected]>
* 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

2020-11-12 16:59:59

by Hideaki Yoshifuji

[permalink] [raw]
Subject: Re: [PATCH net-next V3] net: Variable SLAAC: SLAAC with prefixes of arbitrary length in PIO

Hi,

2020年11月13日(金) 0:46 Dmytro Shytyi <[email protected]>:
>
> 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 <[email protected]>
> Reported-by: kernel test robot <[email protected]>
> ---

> - 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

2020-11-13 00:23:59

by Jakub Kicinski

[permalink] [raw]
Subject: Re: [PATCH net-next V3] net: Variable SLAAC: SLAAC with prefixes of arbitrary length in PIO

On Thu, 12 Nov 2020 16:44:54 +0100 Dmytro Shytyi wrote:
> Reported-by: kernel test robot <[email protected]>

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

2020-11-13 00:26:16

by Jakub Kicinski

[permalink] [raw]
Subject: 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 <[email protected]>

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.

2020-11-13 00:35:02

by Li, Philip

[permalink] [raw]
Subject: RE: [kbuild-all] Re: [PATCH net-next] net: Variable SLAAC: SLAAC with prefixes of arbitrary length in PIO

> 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 <[email protected]>
>
> 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 <[email protected]>

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 -- [email protected]
> To unsubscribe send an email to [email protected]

2020-11-13 00:53:11

by Jakub Kicinski

[permalink] [raw]
Subject: Re: [kbuild-all] Re: [PATCH net-next] net: Variable SLAAC: SLAAC with prefixes of arbitrary length in PIO

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 <[email protected]>
> >
> > 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 <[email protected]>
>
> Or any wording change suggestion to make it more clear/friendly?

2020-11-13 01:03:26

by Li, Philip

[permalink] [raw]
Subject: RE: [kbuild-all] Re: [PATCH net-next] net: Variable SLAAC: SLAAC with prefixes of arbitrary length in PIO

>
> 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 <[email protected]>
> > >
> > > 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 <[email protected]>
> >
> > Or any wording change suggestion to make it more clear/friendly?

2020-11-13 01:46:01

by Dave Hansen

[permalink] [raw]
Subject: Re: [PATCH net-next] net: Variable SLAAC: SLAAC with prefixes of arbitrary length in PIO

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 <[email protected]>
> 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 <[email protected]> # 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

2020-11-13 01:53:12

by Dmytro Shytyi

[permalink] [raw]
Subject: Re: [PATCH net-next V3] net: Variable SLAAC: SLAAC with prefixes of arbitrary length in PIO

Hello,

---- On Thu, 12 Nov 2020 17:55:08 +0100 Hideaki Yoshifuji <[email protected]> wrote ----

> Hi,
>
> 2020年11月13日(金) 0:46 Dmytro Shytyi <[email protected]>:
> >
> > 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 <[email protected]>
> > Reported-by: kernel test robot <[email protected]>
> > ---
>
> > - 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
>

2020-11-13 01:56:42

by Dmytro Shytyi

[permalink] [raw]
Subject: Re: [PATCH net-next V3] net: Variable SLAAC: SLAAC with prefixes of arbitrary length in PIO

Hello,

---- On Fri, 13 Nov 2020 01:21:56 +0100 Jakub Kicinski <[email protected]> wrote ----

> On Thu, 12 Nov 2020 16:44:54 +0100 Dmytro Shytyi wrote:
> > Reported-by: kernel test robot <[email protected]>
>
> 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

2020-11-13 01:57:22

by Jakub Kicinski

[permalink] [raw]
Subject: Re: [PATCH net-next] net: Variable SLAAC: SLAAC with prefixes of arbitrary length in PIO

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 <[email protected]>
> > 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 <[email protected]> # 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?

2020-11-13 01:59:21

by Dmytro Shytyi

[permalink] [raw]
Subject: [PATCH net-next V4] net: Variable SLAAC: SLAAC with prefixes of arbitrary length in PIO

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 <[email protected]>
---
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
+ * <[email protected]> prefixes of arbitrary length.
* Janos Farkas : delete timer on ifdown
* <[email protected]>
* 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

2020-11-13 02:05:11

by Dave Hansen

[permalink] [raw]
Subject: Re: [PATCH net-next] net: Variable SLAAC: SLAAC with prefixes of arbitrary length in PIO

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 <[email protected]> # 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.

2020-11-13 12:40:42

by Hideaki Yoshifuji

[permalink] [raw]
Subject: Re: [PATCH net-next V4] net: Variable SLAAC: SLAAC with prefixes of arbitrary length in PIO

Hi,

2020年11月13日(金) 10:57 Dmytro Shytyi <[email protected]>:
>
> 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 <[email protected]>
> ---
> 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.

2020-11-13 19:14:52

by Dmytro Shytyi

[permalink] [raw]
Subject: Re: [PATCH net-next V4] net: Variable SLAAC: SLAAC with prefixes of arbitrary length in PIO

Hello,

---- On Fri, 13 Nov 2020 13:38:02 +0100 Hideaki Yoshifuji <[email protected]> wrote ----

> Hi,
>
> 2020年11月13日(金) 10:57 Dmytro Shytyi <[email protected]>:
> >
> > 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 <[email protected]>
> > ---
> > 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...

2020-11-13 19:40:29

by Dmytro Shytyi

[permalink] [raw]
Subject: [PATCH net-next V5] net: Variable SLAAC: SLAAC with prefixes of arbitrary length in PIO

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 <[email protected]>
---
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
+ * <[email protected]> prefixes of arbitrary length.
* Janos Farkas : delete timer on ifdown
* <[email protected]>
* 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

2020-11-17 20:48:10

by Jakub Kicinski

[permalink] [raw]
Subject: Re: [PATCH net-next V5] net: Variable SLAAC: SLAAC with prefixes of arbitrary length in PIO

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 <[email protected]>
> ---
> 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
> + * <[email protected]> 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
> * <[email protected]>
> * 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

2020-11-18 13:45:01

by Dmytro Shytyi

[permalink] [raw]
Subject: Re: [PATCH net-next V5] net: Variable SLAAC: SLAAC with prefixes of arbitrary length in PIO

Hello,

---- On Tue, 17 Nov 2020 21:43:48 +0100 Jakub Kicinski <[email protected]> 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 <[email protected]>
> > ---
> > 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
> > + * <[email protected]> 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
> > * <[email protected]>
> > * 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.

2020-11-18 15:52:15

by Jakub Kicinski

[permalink] [raw]
Subject: Re: [PATCH net-next V5] net: Variable SLAAC: SLAAC with prefixes of arbitrary length in PIO

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?

2020-11-18 16:03:31

by Dmytro Shytyi

[permalink] [raw]
Subject: Re: [PATCH net-next V5] net: Variable SLAAC: SLAAC with prefixes of arbitrary length in PIO

Hello,

---- On Wed, 18 Nov 2020 16:50:27 +0100 Jakub Kicinski <[email protected]> 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.

2020-11-19 13:41:32

by Dmytro Shytyi

[permalink] [raw]
Subject: [PATCH net-next V6] net: Variable SLAAC: SLAAC with prefixes of arbitrary length in PIO

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 <[email protected]>
---
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)

2020-11-19 18:48:24

by Jakub Kicinski

[permalink] [raw]
Subject: Re: [PATCH net-next V6] net: Variable SLAAC: SLAAC with prefixes of arbitrary length in PIO

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?

2020-11-19 19:35:35

by Dmytro Shytyi

[permalink] [raw]
Subject: Re: [PATCH net-next V6] net: Variable SLAAC: SLAAC with prefixes of arbitrary length in PIO


---- On Thu, 19 Nov 2020 19:44:13 +0100 Jakub Kicinski <[email protected]> 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

2020-11-20 01:23:52

by Jakub Kicinski

[permalink] [raw]
Subject: Re: [PATCH net-next V6] net: Variable SLAAC: SLAAC with prefixes of arbitrary length in PIO

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 :)

2020-11-20 09:29:20

by Dmytro Shytyi

[permalink] [raw]
Subject: [PATCH net-next V7] net: Variable SLAAC: SLAAC with prefixes of arbitrary length in PIO

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 <[email protected]>
---
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)

2020-11-24 04:32:50

by Hideaki Yoshifuji

[permalink] [raw]
Subject: Re: [PATCH net-next V7] net: Variable SLAAC: SLAAC with prefixes of arbitrary length in PIO

Hi,

2020年11月20日(金) 18:28 Dmytro Shytyi <[email protected]>:
>
> 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 <[email protected]>
> ---
> 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)

2020-11-27 16:57:45

by Dmytro Shytyi

[permalink] [raw]
Subject: Re: [PATCH net-next V7] net: Variable SLAAC: SLAAC with prefixes of arbitrary length in PIO

Hello,

---- On Mon, 23 Nov 2020 14:26:27 +0100 Hideaki Yoshifuji <[email protected]> wrote ----

> Hi,
>
> 2020年11月20日(金) 18:28 Dmytro Shytyi <[email protected]>:
> >
> > 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 <[email protected]>
> > ---
> > 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)
>

2020-12-09 08:01:19

by Dmytro Shytyi

[permalink] [raw]
Subject: [PATCH net-next V8] net: Variable SLAAC: SLAAC with prefixes of arbitrary length in PIO

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 <[email protected]>
---
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 */
}

2020-12-16 00:04:41

by David Miller

[permalink] [raw]
Subject: Re: [PATCH net-next V8] net: Variable SLAAC: SLAAC with prefixes of arbitrary length in PIO

From: Dmytro Shytyi <[email protected]>
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 <[email protected]>
> ---
> 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.