Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 622c81d5 authored by Hannes Frederic Sowa's avatar Hannes Frederic Sowa Committed by David S. Miller
Browse files

ipv6: generation of stable privacy addresses for link-local and autoconf



This patch implements the stable privacy address generation for
link-local and autoconf addresses as specified in RFC7217.

  RID = F(Prefix, Net_Iface, Network_ID, DAD_Counter, secret_key)

is the RID (random identifier). As the hash function F we chose one
round of sha1. Prefix will be either the link-local prefix or the
router advertised one. As Net_Iface we use the MAC address of the
device. DAD_Counter and secret_key are implemented as specified.

We don't use Network_ID, as it couples the code too closely to other
subsystems. It is specified as optional in the RFC.

As Net_Iface we only use the MAC address: we simply have no stable
identifier in the kernel we could possibly use: because this code might
run very early, we cannot depend on names, as they might be changed by
user space early on during the boot process.

A new address generation mode is introduced,
IN6_ADDR_GEN_MODE_STABLE_PRIVACY. With iproute2 one can switch back to
none or eui64 address configuration mode although the stable_secret is
already set.

We refuse writes to ipv6/conf/all/stable_secret but only allow
ipv6/conf/default/stable_secret and the interface specific file to be
written to. The default stable_secret is used as the parameter for the
namespace, the interface specific can overwrite the secret, e.g. when
switching a network configuration from one system to another while
inheriting the secret.

Cc: Erik Kline <ek@google.com>
Cc: Fernando Gont <fgont@si6networks.com>
Cc: Lorenzo Colitti <lorenzo@google.com>
Cc: YOSHIFUJI Hideaki/吉藤英明 <hideaki.yoshifuji@miraclelinux.com>
Signed-off-by: default avatarHannes Frederic Sowa <hannes@stressinduktion.org>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 3d1bec99
Loading
Loading
Loading
Loading
+1 −0
Original line number Original line Diff line number Diff line
@@ -216,6 +216,7 @@ enum {
enum in6_addr_gen_mode {
enum in6_addr_gen_mode {
	IN6_ADDR_GEN_MODE_EUI64,
	IN6_ADDR_GEN_MODE_EUI64,
	IN6_ADDR_GEN_MODE_NONE,
	IN6_ADDR_GEN_MODE_NONE,
	IN6_ADDR_GEN_MODE_STABLE_PRIVACY,
};
};


/* Bridge section */
/* Bridge section */
+126 −4
Original line number Original line Diff line number Diff line
@@ -131,6 +131,9 @@ static void ipv6_regen_rndid(unsigned long data);


static int ipv6_generate_eui64(u8 *eui, struct net_device *dev);
static int ipv6_generate_eui64(u8 *eui, struct net_device *dev);
static int ipv6_count_addresses(struct inet6_dev *idev);
static int ipv6_count_addresses(struct inet6_dev *idev);
static int ipv6_generate_stable_address(struct in6_addr *addr,
					u8 dad_count,
					const struct inet6_dev *idev);


/*
/*
 *	Configured unicast address hash table
 *	Configured unicast address hash table
@@ -2302,6 +2305,11 @@ void addrconf_prefix_rcv(struct net_device *dev, u8 *opt, int len, bool sllao)
				       in6_dev->token.s6_addr + 8, 8);
				       in6_dev->token.s6_addr + 8, 8);
				read_unlock_bh(&in6_dev->lock);
				read_unlock_bh(&in6_dev->lock);
				tokenized = true;
				tokenized = true;
			} else if (in6_dev->addr_gen_mode ==
				   IN6_ADDR_GEN_MODE_STABLE_PRIVACY &&
				   !ipv6_generate_stable_address(&addr, 0,
								 in6_dev)) {
				goto ok;
			} else if (ipv6_generate_eui64(addr.s6_addr + 8, dev) &&
			} else if (ipv6_generate_eui64(addr.s6_addr + 8, dev) &&
				   ipv6_inherit_eui64(addr.s6_addr + 8, in6_dev)) {
				   ipv6_inherit_eui64(addr.s6_addr + 8, in6_dev)) {
				in6_dev_put(in6_dev);
				in6_dev_put(in6_dev);
@@ -2820,12 +2828,98 @@ static void addrconf_add_linklocal(struct inet6_dev *idev, const struct in6_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_generate_stable_address(struct in6_addr *address,
					u8 dad_count,
					const struct inet6_dev *idev)
{
	static const int idgen_retries = 3;

	static DEFINE_SPINLOCK(lock);
	static __u32 digest[SHA_DIGEST_WORDS];
	static __u32 workspace[SHA_WORKSPACE_WORDS];

	static union {
		char __data[SHA_MESSAGE_BYTES];
		struct {
			struct in6_addr secret;
			__be64 prefix;
			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 (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);

	sha_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 = ((__be64)address->s6_addr32[0] << 32) |
		       (__be64)address->s6_addr32[1];
	data.secret = secret;
	data.dad_count = dad_count;

	sha_transform(digest, data.__data, workspace);

	temp = *address;
	temp.s6_addr32[2] = digest[0];
	temp.s6_addr32[3] = digest[1];

	spin_unlock_bh(&lock);

	if (ipv6_reserved_interfaceid(temp)) {
		dad_count++;
		if (dad_count > idgen_retries)
			return -1;
		goto retry;
	}

	*address = temp;
	return 0;
}

static void addrconf_addr_gen(struct inet6_dev *idev, bool prefix_route)
static void addrconf_addr_gen(struct inet6_dev *idev, bool prefix_route)
{
{
	if (idev->addr_gen_mode == IN6_ADDR_GEN_MODE_EUI64) {
	struct in6_addr addr;
	struct in6_addr addr;


	ipv6_addr_set(&addr, htonl(0xFE800000), 0, 0, 0);
	ipv6_addr_set(&addr, htonl(0xFE800000), 0, 0, 0);

	if (idev->addr_gen_mode == IN6_ADDR_GEN_MODE_STABLE_PRIVACY) {
		if (!ipv6_generate_stable_address(&addr, 0, idev))
			addrconf_add_linklocal(idev, &addr);
		else if (prefix_route)
			addrconf_prefix_route(&addr, 64, idev->dev, 0, 0);
	} else if (idev->addr_gen_mode == IN6_ADDR_GEN_MODE_EUI64) {
		/* addrconf_add_linklocal also adds a prefix_route and we
		/* addrconf_add_linklocal also adds a prefix_route and we
		 * only need to care about prefix routes if ipv6_generate_eui64
		 * only need to care about prefix routes if ipv6_generate_eui64
		 * couldn't generate one.
		 * couldn't generate one.
@@ -4675,8 +4769,15 @@ static int inet6_set_link_af(struct net_device *dev, const struct nlattr *nla)
		u8 mode = nla_get_u8(tb[IFLA_INET6_ADDR_GEN_MODE]);
		u8 mode = nla_get_u8(tb[IFLA_INET6_ADDR_GEN_MODE]);


		if (mode != IN6_ADDR_GEN_MODE_EUI64 &&
		if (mode != IN6_ADDR_GEN_MODE_EUI64 &&
		    mode != IN6_ADDR_GEN_MODE_NONE)
		    mode != IN6_ADDR_GEN_MODE_NONE &&
		    mode != IN6_ADDR_GEN_MODE_STABLE_PRIVACY)
			return -EINVAL;
			return -EINVAL;

		if (mode == IN6_ADDR_GEN_MODE_STABLE_PRIVACY &&
		    !idev->cnf.stable_secret.initialized &&
		    !dev_net(dev)->ipv6.devconf_dflt->stable_secret.initialized)
			return -EINVAL;

		idev->addr_gen_mode = mode;
		idev->addr_gen_mode = mode;
		err = 0;
		err = 0;
	}
	}
@@ -5093,8 +5194,12 @@ static int addrconf_sysctl_stable_secret(struct ctl_table *ctl, int write,
	struct in6_addr addr;
	struct in6_addr addr;
	char str[IPV6_MAX_STRLEN];
	char str[IPV6_MAX_STRLEN];
	struct ctl_table lctl = *ctl;
	struct ctl_table lctl = *ctl;
	struct net *net = ctl->extra2;
	struct ipv6_stable_secret *secret = ctl->data;
	struct ipv6_stable_secret *secret = ctl->data;


	if (&net->ipv6.devconf_all->stable_secret == ctl->data)
		return -EIO;

	lctl.maxlen = IPV6_MAX_STRLEN;
	lctl.maxlen = IPV6_MAX_STRLEN;
	lctl.data = str;
	lctl.data = str;


@@ -5127,6 +5232,23 @@ static int addrconf_sysctl_stable_secret(struct ctl_table *ctl, int write,
	secret->initialized = true;
	secret->initialized = true;
	secret->secret = addr;
	secret->secret = addr;


	if (&net->ipv6.devconf_dflt->stable_secret == ctl->data) {
		struct net_device *dev;

		for_each_netdev(net, dev) {
			struct inet6_dev *idev = __in6_dev_get(dev);

			if (idev) {
				idev->addr_gen_mode =
					IN6_ADDR_GEN_MODE_STABLE_PRIVACY;
			}
		}
	} else {
		struct inet6_dev *idev = ctl->extra1;

		idev->addr_gen_mode = IN6_ADDR_GEN_MODE_STABLE_PRIVACY;
	}

out:
out:
	rtnl_unlock();
	rtnl_unlock();