diff options
author | Hannes Frederic Sowa <hannes@stressinduktion.org> | 2015-03-23 18:36:01 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2015-03-23 22:12:08 -0400 |
commit | 622c81d57b392cc9be836670eb464a4dfaa9adfe (patch) | |
tree | 2b67f9ef0fcab0ffee39d624895124311769f42c | |
parent | 3d1bec99320d4e96897805440f8cf4f68eff226b (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.h | 1 | ||||
-rw-r--r-- | net/ipv6/addrconf.c | 130 |
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 { | |||
216 | enum in6_addr_gen_mode { | 216 | enum 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 | ||
132 | static int ipv6_generate_eui64(u8 *eui, struct net_device *dev); | 132 | static int ipv6_generate_eui64(u8 *eui, struct net_device *dev); |
133 | static int ipv6_count_addresses(struct inet6_dev *idev); | 133 | static int ipv6_count_addresses(struct inet6_dev *idev); |
134 | static 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 | ||
2831 | static 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 | |||
2847 | static 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 | |||
2880 | retry: | ||
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 | |||
2823 | static void addrconf_addr_gen(struct inet6_dev *idev, bool prefix_route) | 2911 | static 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 | |||
5130 | out: | 5252 | out: |
5131 | rtnl_unlock(); | 5253 | rtnl_unlock(); |
5132 | 5254 | ||