aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHannes Frederic Sowa <hannes@stressinduktion.org>2015-03-23 18:36:01 -0400
committerDavid S. Miller <davem@davemloft.net>2015-03-23 22:12:08 -0400
commit622c81d57b392cc9be836670eb464a4dfaa9adfe (patch)
tree2b67f9ef0fcab0ffee39d624895124311769f42c
parent3d1bec99320d4e96897805440f8cf4f68eff226b (diff)
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: Hannes Frederic Sowa <hannes@stressinduktion.org> Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r--include/uapi/linux/if_link.h1
-rw-r--r--net/ipv6/addrconf.c130
2 files changed, 127 insertions, 4 deletions
diff --git a/include/uapi/linux/if_link.h b/include/uapi/linux/if_link.h
index f5f5edd5ae5f..7ffb18df01ca 100644
--- a/include/uapi/linux/if_link.h
+++ b/include/uapi/linux/if_link.h
@@ -216,6 +216,7 @@ enum {
216enum in6_addr_gen_mode { 216enum in6_addr_gen_mode {
217 IN6_ADDR_GEN_MODE_EUI64, 217 IN6_ADDR_GEN_MODE_EUI64,
218 IN6_ADDR_GEN_MODE_NONE, 218 IN6_ADDR_GEN_MODE_NONE,
219 IN6_ADDR_GEN_MODE_STABLE_PRIVACY,
219}; 220};
220 221
221/* Bridge section */ 222/* Bridge section */
diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c
index 5b967c8a617a..6813268ce8b8 100644
--- a/net/ipv6/addrconf.c
+++ b/net/ipv6/addrconf.c
@@ -131,6 +131,9 @@ static void ipv6_regen_rndid(unsigned long data);
131 131
132static int ipv6_generate_eui64(u8 *eui, struct net_device *dev); 132static int ipv6_generate_eui64(u8 *eui, struct net_device *dev);
133static int ipv6_count_addresses(struct inet6_dev *idev); 133static int ipv6_count_addresses(struct inet6_dev *idev);
134static int ipv6_generate_stable_address(struct in6_addr *addr,
135 u8 dad_count,
136 const struct inet6_dev *idev);
134 137
135/* 138/*
136 * Configured unicast address hash table 139 * Configured unicast address hash table
@@ -2302,6 +2305,11 @@ void addrconf_prefix_rcv(struct net_device *dev, u8 *opt, int len, bool sllao)
2302 in6_dev->token.s6_addr + 8, 8); 2305 in6_dev->token.s6_addr + 8, 8);
2303 read_unlock_bh(&in6_dev->lock); 2306 read_unlock_bh(&in6_dev->lock);
2304 tokenized = true; 2307 tokenized = true;
2308 } else if (in6_dev->addr_gen_mode ==
2309 IN6_ADDR_GEN_MODE_STABLE_PRIVACY &&
2310 !ipv6_generate_stable_address(&addr, 0,
2311 in6_dev)) {
2312 goto ok;
2305 } else if (ipv6_generate_eui64(addr.s6_addr + 8, dev) && 2313 } else if (ipv6_generate_eui64(addr.s6_addr + 8, dev) &&
2306 ipv6_inherit_eui64(addr.s6_addr + 8, in6_dev)) { 2314 ipv6_inherit_eui64(addr.s6_addr + 8, in6_dev)) {
2307 in6_dev_put(in6_dev); 2315 in6_dev_put(in6_dev);
@@ -2820,12 +2828,98 @@ static void addrconf_add_linklocal(struct inet6_dev *idev, const struct in6_addr
2820 } 2828 }
2821} 2829}
2822 2830
2831static bool ipv6_reserved_interfaceid(struct in6_addr address)
2832{
2833 if ((address.s6_addr32[2] | address.s6_addr32[3]) == 0)
2834 return true;
2835
2836 if (address.s6_addr32[2] == htonl(0x02005eff) &&
2837 ((address.s6_addr32[3] & htonl(0xfe000000)) == htonl(0xfe000000)))
2838 return true;
2839
2840 if (address.s6_addr32[2] == htonl(0xfdffffff) &&
2841 ((address.s6_addr32[3] & htonl(0xffffff80)) == htonl(0xffffff80)))
2842 return true;
2843
2844 return false;
2845}
2846
2847static int ipv6_generate_stable_address(struct in6_addr *address,
2848 u8 dad_count,
2849 const struct inet6_dev *idev)
2850{
2851 static const int idgen_retries = 3;
2852
2853 static DEFINE_SPINLOCK(lock);
2854 static __u32 digest[SHA_DIGEST_WORDS];
2855 static __u32 workspace[SHA_WORKSPACE_WORDS];
2856
2857 static union {
2858 char __data[SHA_MESSAGE_BYTES];
2859 struct {
2860 struct in6_addr secret;
2861 __be64 prefix;
2862 unsigned char hwaddr[MAX_ADDR_LEN];
2863 u8 dad_count;
2864 } __packed;
2865 } data;
2866
2867 struct in6_addr secret;
2868 struct in6_addr temp;
2869 struct net *net = dev_net(idev->dev);
2870
2871 BUILD_BUG_ON(sizeof(data.__data) != sizeof(data));
2872
2873 if (idev->cnf.stable_secret.initialized)
2874 secret = idev->cnf.stable_secret.secret;
2875 else if (net->ipv6.devconf_dflt->stable_secret.initialized)
2876 secret = net->ipv6.devconf_dflt->stable_secret.secret;
2877 else
2878 return -1;
2879
2880retry:
2881 spin_lock_bh(&lock);
2882
2883 sha_init(digest);
2884 memset(&data, 0, sizeof(data));
2885 memset(workspace, 0, sizeof(workspace));
2886 memcpy(data.hwaddr, idev->dev->perm_addr, idev->dev->addr_len);
2887 data.prefix = ((__be64)address->s6_addr32[0] << 32) |
2888 (__be64)address->s6_addr32[1];
2889 data.secret = secret;
2890 data.dad_count = dad_count;
2891
2892 sha_transform(digest, data.__data, workspace);
2893
2894 temp = *address;
2895 temp.s6_addr32[2] = digest[0];
2896 temp.s6_addr32[3] = digest[1];
2897
2898 spin_unlock_bh(&lock);
2899
2900 if (ipv6_reserved_interfaceid(temp)) {
2901 dad_count++;
2902 if (dad_count > idgen_retries)
2903 return -1;
2904 goto retry;
2905 }
2906
2907 *address = temp;
2908 return 0;
2909}
2910
2823static void addrconf_addr_gen(struct inet6_dev *idev, bool prefix_route) 2911static void addrconf_addr_gen(struct inet6_dev *idev, bool prefix_route)
2824{ 2912{
2825 if (idev->addr_gen_mode == IN6_ADDR_GEN_MODE_EUI64) { 2913 struct in6_addr addr;
2826 struct in6_addr addr; 2914
2915 ipv6_addr_set(&addr, htonl(0xFE800000), 0, 0, 0);
2827 2916
2828 ipv6_addr_set(&addr, htonl(0xFE800000), 0, 0, 0); 2917 if (idev->addr_gen_mode == IN6_ADDR_GEN_MODE_STABLE_PRIVACY) {
2918 if (!ipv6_generate_stable_address(&addr, 0, idev))
2919 addrconf_add_linklocal(idev, &addr);
2920 else if (prefix_route)
2921 addrconf_prefix_route(&addr, 64, idev->dev, 0, 0);
2922 } else if (idev->addr_gen_mode == IN6_ADDR_GEN_MODE_EUI64) {
2829 /* addrconf_add_linklocal also adds a prefix_route and we 2923 /* addrconf_add_linklocal also adds a prefix_route and we
2830 * only need to care about prefix routes if ipv6_generate_eui64 2924 * only need to care about prefix routes if ipv6_generate_eui64
2831 * couldn't generate one. 2925 * couldn't generate one.
@@ -4675,8 +4769,15 @@ static int inet6_set_link_af(struct net_device *dev, const struct nlattr *nla)
4675 u8 mode = nla_get_u8(tb[IFLA_INET6_ADDR_GEN_MODE]); 4769 u8 mode = nla_get_u8(tb[IFLA_INET6_ADDR_GEN_MODE]);
4676 4770
4677 if (mode != IN6_ADDR_GEN_MODE_EUI64 && 4771 if (mode != IN6_ADDR_GEN_MODE_EUI64 &&
4678 mode != IN6_ADDR_GEN_MODE_NONE) 4772 mode != IN6_ADDR_GEN_MODE_NONE &&
4773 mode != IN6_ADDR_GEN_MODE_STABLE_PRIVACY)
4774 return -EINVAL;
4775
4776 if (mode == IN6_ADDR_GEN_MODE_STABLE_PRIVACY &&
4777 !idev->cnf.stable_secret.initialized &&
4778 !dev_net(dev)->ipv6.devconf_dflt->stable_secret.initialized)
4679 return -EINVAL; 4779 return -EINVAL;
4780
4680 idev->addr_gen_mode = mode; 4781 idev->addr_gen_mode = mode;
4681 err = 0; 4782 err = 0;
4682 } 4783 }
@@ -5093,8 +5194,12 @@ static int addrconf_sysctl_stable_secret(struct ctl_table *ctl, int write,
5093 struct in6_addr addr; 5194 struct in6_addr addr;
5094 char str[IPV6_MAX_STRLEN]; 5195 char str[IPV6_MAX_STRLEN];
5095 struct ctl_table lctl = *ctl; 5196 struct ctl_table lctl = *ctl;
5197 struct net *net = ctl->extra2;
5096 struct ipv6_stable_secret *secret = ctl->data; 5198 struct ipv6_stable_secret *secret = ctl->data;
5097 5199
5200 if (&net->ipv6.devconf_all->stable_secret == ctl->data)
5201 return -EIO;
5202
5098 lctl.maxlen = IPV6_MAX_STRLEN; 5203 lctl.maxlen = IPV6_MAX_STRLEN;
5099 lctl.data = str; 5204 lctl.data = str;
5100 5205
@@ -5127,6 +5232,23 @@ static int addrconf_sysctl_stable_secret(struct ctl_table *ctl, int write,
5127 secret->initialized = true; 5232 secret->initialized = true;
5128 secret->secret = addr; 5233 secret->secret = addr;
5129 5234
5235 if (&net->ipv6.devconf_dflt->stable_secret == ctl->data) {
5236 struct net_device *dev;
5237
5238 for_each_netdev(net, dev) {
5239 struct inet6_dev *idev = __in6_dev_get(dev);
5240
5241 if (idev) {
5242 idev->addr_gen_mode =
5243 IN6_ADDR_GEN_MODE_STABLE_PRIVACY;
5244 }
5245 }
5246 } else {
5247 struct inet6_dev *idev = ctl->extra1;
5248
5249 idev->addr_gen_mode = IN6_ADDR_GEN_MODE_STABLE_PRIVACY;
5250 }
5251
5130out: 5252out:
5131 rtnl_unlock(); 5253 rtnl_unlock();
5132 5254