主要分析Stable Privacy Address在kernel中的实现code
Kernel Version: 4.6
无状态地址配置过程
一般情况下,在对应的网卡启动的时候,如果支持IPV6,且软件的IPV6功能使能,就会自动配置一个Link local地址,这个link local地址就是以fe80开头,同时有了link local地址之后,系统就会发送一个RS的packet到网络中去,如果路由器支持IPV6,收到这个RS之后,会回复一个携带prefix信息的RA包,Host收到RA包之后,提取prefix,生成interface id,组成一个global地址,这个过程就是无状态地址配置的过程.
RS packet :
从图中看出,属于ICMPV6协议的封包,使用link local address发送,目的地址是路由器多播组
RA packet:
Host收到RA之后,就提取prefix,组成一个global地址
Kernel 实现分析
在kernel中专门提供了这样一个patch增加了对RFC7217的支持。Patch:ipv6: generation of stable privacy addresses for link-local and autoconf
@@ -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); read_unlock_bh(&in6_dev->lock); 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) && ipv6_inherit_eui64(addr.s6_addr + 8, in6_dev)) { in6_dev_put(in6_dev);
在收到prefix的时候,会判断下addr_gen_mode 生成地址的模式,如果是IN6_ADDR_GEN_MODE_STABLE_PRIVACY, 那么就调用ipv6_generate_stable_address 生成地址stable address.
static int ipv6_generate_stable_address(struct in6_addr *address, u8 dad_count, const struct inet6_dev *idev) { static DEFINE_SPINLOCK(lock); static __u32 digest[SHA_DIGEST_WORDS]; //160bit消息摘要 static __u32 workspace[SHA_WORKSPACE_WORDS]; static union { char __data[SHA_MESSAGE_BYTES]; //512bit data block : 128bit secret + 64bit prefix + 256bit HW + 8bit DAD_Count; 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); // 取得网卡的device结构体 BUILD_BUG_ON(sizeof(data.__data) != sizeof(data)); //获取初始化的scret 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; //如果没有scret,直接返回-1 retry: spin_lock_bh(&lock); sha_init(digest); //初始化SHA-1的原始消息摘要值 memset(&data, 0, sizeof(data)); //清0,data区域 memset(workspace, 0, sizeof(workspace));//清0,workspace memcpy(data.hwaddr, idev->dev->perm_addr, idev->dev->addr_len); //将设备的mac地址复制到data中 data.prefix[0] = address->s6_addr32[0]; //prefix信息 data.prefix[1] = address->s6_addr32[1]; data.secret = secret; // secret 信息 到data data.dad_count = dad_count; //dad_count 传入的值,赋值给data sha_transform(digest, data.__data, workspace); //使用SHA-1算法进行加密,并产生160bit的摘要信息 temp = *address; //这个地方就是使用160bit的摘要信息的前64bit,赋值给IPV6地址的后64bit,这样就组成了一个prefix + interface id的完整地址 temp.s6_addr32[2] = (__force __be32)digest[0]; temp.s6_addr32[3] = (__force __be32)digest[1]; spin_unlock_bh(&lock); //判断是否是保留的interface id,这些interface id不能被使用 if (ipv6_reserved_interfaceid(temp)) { dad_count++;// 如果和保留interface id 一致,则dad_count加1 //如果dad_count > idgen_retries 生成stable address失败,否则retry if (dad_count > dev_net(idev->dev)->ipv6.sysctl.idgen_retries) return -1; goto retry; } *address = temp; return 0; }从ipv6_generate_stable_address函数中可以总结出以下步骤:
1. 初始化消息摘要
2. 填充需要加密的512数据块:128bit secret + 64bit prefix + 256bit HW + 8bit DAD_Count 这个地方不足512字节,应该后面的补0
3. 使用SHA-1对data进行加密,并产生160bit的消息摘要
4. 将消息摘要的前64bit,放置到IPV6地址的后64bit
5. 检查生成的interface id是否保留的interface id,如果是保留的,需要重新生成或者直接失败
6. 生成整个地址,返回。
1-4步都是设计到SHA-1算法,如果不了解可以参考文章: IPv6详解:SHA1算法实现及详解 了解FIPS
180-4的算法流程
初始化消息摘要,这个地方就是将初始值复制给160bit的消息摘要
/** * sha_init - initialize the vectors for a SHA1 digest * @buf: vector to initialize */ void sha_init(__u32 *buf) { buf[0] = 0x67452301; buf[1] = 0xefcdab89; buf[2] = 0x98badcfe; buf[3] = 0x10325476; buf[4] = 0xc3d2e1f0; }
sha_transform
这个函数是SHA-1的核心算法,对单个的512 block使用SHA-1算法进行转换,这个函数最后产生一个160bit的消息摘要,但是这个地方不要与文章 IPv6详解:SHA1算法实现及详解
中的FIS 180-4 SHA-1算法混淆,两者差异的地方在于Linux kernel实现的不再进行补位和补长度的操作,应该不够512个字节就会填0,剩下的步骤2者一致
1 .首先将512 data block分成 80个双字,W(0~79),如下提供了2个宏,在t为0~15的时候,使用SHA_SRC, 计算出W0~W15
在16~79的时候,使用SHA_MAX计算出W16~W79
/* * Where do we get the source from? The first 16 iterations get it from * the input data, the next mix it from the 512-bit array. */ #define SHA_SRC(t) get_unaligned_be32((__u32 *)data + t) #define SHA_MIX(t) rol32(W(t+13) ^ W(t+8) ^ W(t+2) ^ W(t), 1)2. 有了W0~W79, 分别使用函数对每个双字进行计算,更新A,B,C,D,E,计算的宏如下,看起来还是比较麻烦的,不同的T使用不同的常量
#define SHA_ROUND(t, input, fn, constant, A, B, C, D, E) do { \ __u32 TEMP = input(t); setW(t, TEMP); \ E += TEMP + rol32(A,5) + (fn) + (constant); \ B = ror32(B, 2); } while (0) #define T_0_15(t, A, B, C, D, E) SHA_ROUND(t, SHA_SRC, (((C^D)&B)^D) , 0x5a827999, A, B, C, D, E ) #define T_16_19(t, A, B, C, D, E) SHA_ROUND(t, SHA_MIX, (((C^D)&B)^D) , 0x5a827999, A, B, C, D, E ) #define T_20_39(t, A, B, C, D, E) SHA_ROUND(t, SHA_MIX, (B^C^D) , 0x6ed9eba1, A, B, C, D, E ) #define T_40_59(t, A, B, C, D, E) SHA_ROUND(t, SHA_MIX, ((B&C)+(D&(B^C))) , 0x8f1bbcdc, A, B, C, D, E ) #define T_60_79(t, A, B, C, D, E) SHA_ROUND(t, SHA_MIX, (B^C^D) , 0xca62c1d6, A, B, C, D, E )具体的算法如下,比较长,进行80次运算
void sha_transform(__u32 *digest, const char *data, __u32 *array) { __u32 A, B, C, D, E; A = digest[0]; B = digest[1]; C = digest[2]; D = digest[3]; E = digest[4]; /* Round 1 - iterations 0-16 take their input from 'data' */ T_0_15( 0, A, B, C, D, E); T_0_15( 1, E, A, B, C, D); T_0_15( 2, D, E, A, B, C); T_0_15( 3, C, D, E, A, B); T_0_15( 4, B, C, D, E, A); T_0_15( 5, A, B, C, D, E); T_0_15( 6, E, A, B, C, D); T_0_15( 7, D, E, A, B, C); T_0_15( 8, C, D, E, A, B); T_0_15( 9, B, C, D, E, A); T_0_15(10, A, B, C, D, E); T_0_15(11, E, A, B, C, D); T_0_15(12, D, E, A, B, C); T_0_15(13, C, D, E, A, B); T_0_15(14, B, C, D, E, A); T_0_15(15, A, B, C, D, E); /* Round 1 - tail. Input from 512-bit mixing array */ T_16_19(16, E, A, B, C, D); T_16_19(17, D, E, A, B, C); T_16_19(18, C, D, E, A, B); T_16_19(19, B, C, D, E, A); /* Round 2 */ T_20_39(20, A, B, C, D, E); T_20_39(21, E, A, B, C, D); T_20_39(22, D, E, A, B, C); T_20_39(23, C, D, E, A, B); T_20_39(24, B, C, D, E, A); T_20_39(25, A, B, C, D, E); T_20_39(26, E, A, B, C, D); T_20_39(27, D, E, A, B, C); T_20_39(28, C, D, E, A, B); T_20_39(29, B, C, D, E, A); T_20_39(30, A, B, C, D, E); T_20_39(31, E, A, B, C, D); T_20_39(32, D, E, A, B, C); T_20_39(33, C, D, E, A, B); T_20_39(34, B, C, D, E, A); T_20_39(35, A, B, C, D, E); T_20_39(36, E, A, B, C, D); T_20_39(37, D, E, A, B, C); T_20_39(38, C, D, E, A, B); T_20_39(39, B, C, D, E, A); /* Round 3 */ T_40_59(40, A, B, C, D, E); T_40_59(41, E, A, B, C, D); T_40_59(42, D, E, A, B, C); T_40_59(43, C, D, E, A, B); T_40_59(44, B, C, D, E, A); T_40_59(45, A, B, C, D, E); T_40_59(46, E, A, B, C, D); T_40_59(47, D, E, A, B, C); T_40_59(48, C, D, E, A, B); T_40_59(49, B, C, D, E, A); T_40_59(50, A, B, C, D, E); T_40_59(51, E, A, B, C, D); T_40_59(52, D, E, A, B, C); T_40_59(53, C, D, E, A, B); T_40_59(54, B, C, D, E, A); T_40_59(55, A, B, C, D, E); T_40_59(56, E, A, B, C, D); T_40_59(57, D, E, A, B, C); T_40_59(58, C, D, E, A, B); T_40_59(59, B, C, D, E, A); /* Round 4 */ T_60_79(60, A, B, C, D, E); T_60_79(61, E, A, B, C, D); T_60_79(62, D, E, A, B, C); T_60_79(63, C, D, E, A, B); T_60_79(64, B, C, D, E, A); T_60_79(65, A, B, C, D, E); T_60_79(66, E, A, B, C, D); T_60_79(67, D, E, A, B, C); T_60_79(68, C, D, E, A, B); T_60_79(69, B, C, D, E, A); T_60_79(70, A, B, C, D, E); T_60_79(71, E, A, B, C, D); T_60_79(72, D, E, A, B, C); T_60_79(73, C, D, E, A, B); T_60_79(74, B, C, D, E, A); T_60_79(75, A, B, C, D, E); T_60_79(76, E, A, B, C, D); T_60_79(77, D, E, A, B, C); T_60_79(78, C, D, E, A, B); T_60_79(79, B, C, D, E, A); digest[0] += A; digest[1] += B; digest[2] += C; digest[3] += D; digest[4] += E; }计算完80次,生成的A,B,C,D,E,然后加上原始的,就是160bit的输出。更详细的算法参考:IPv6详解:SHA1算法实现及详解
检查是否是保留interface id
分配的地址如下之中情况,认为分配失败。
static bool ipv6_reserved_interfaceid(struct in6_addr address) { // interface id 为0 ,认为失败 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; }
DAD失败的处理
生成地址之后,会进行DAD验证是否是重复地址,如果是重复的地址,需要重新生成
void addrconf_dad_failure(struct inet6_ifaddr *ifp) { struct in6_addr addr; struct inet6_dev *idev = ifp->idev; struct net *net = dev_net(ifp->idev->dev); if (addrconf_dad_end(ifp)) { in6_ifa_put(ifp); return; } //进入这个函数说明DAD失败 net_info_ratelimited("%s: IPv6 duplicate address %pI6c detected!\n", ifp->idev->dev->name, &ifp->addr); spin_lock_bh(&ifp->lock); //如果地址flag有IFA_F_STABLE_PRIVACY,说明是stable address if (ifp->flags & IFA_F_STABLE_PRIVACY) { int scope = ifp->scope; u32 flags = ifp->flags; struct in6_addr new_addr; struct inet6_ifaddr *ifp2; u32 valid_lft, preferred_lft; int pfxlen = ifp->prefix_len; int retries = ifp->stable_privacy_retry + 1; //retry 次数加一 //如果重传次数大于idgen_retries,就彻底失败 if (retries > net->ipv6.sysctl.idgen_retries) { net_info_ratelimited("%s: privacy stable address generation failed because of DAD conflicts!\n", ifp->idev->dev->name); goto errdad; } //否则重新生成ipv6_generate_stable_address,retries 为DAD_Counter new_addr = ifp->addr; if (ipv6_generate_stable_address(&new_addr, retries, idev)) goto errdad; valid_lft = ifp->valid_lft; preferred_lft = ifp->prefered_lft; spin_unlock_bh(&ifp->lock); if (idev->cnf.max_addresses && ipv6_count_addresses(idev) >= idev->cnf.max_addresses) goto lock_errdad; net_info_ratelimited("%s: generating new stable privacy address because of DAD conflict\n", ifp->idev->dev->name); //重新设置到接口中 ifp2 = ipv6_add_addr(idev, &new_addr, NULL, pfxlen, scope, flags, valid_lft, preferred_lft); if (IS_ERR(ifp2)) goto lock_errdad; spin_lock_bh(&ifp2->lock); ifp2->stable_privacy_retry = retries; ifp2->state = INET6_IFADDR_STATE_PREDAD; spin_unlock_bh(&ifp2->lock); addrconf_mod_dad_work(ifp2, net->ipv6.sysctl.idgen_delay); in6_ifa_put(ifp2); lock_errdad: spin_lock_bh(&ifp->lock); } else if (idev->cnf.accept_dad > 1 && !idev->cnf.disable_ipv6) { addr.s6_addr32[0] = htonl(0xfe800000); addr.s6_addr32[1] = 0; if (!ipv6_generate_eui64(addr.s6_addr + 8, idev->dev) && ipv6_addr_equal(&ifp->addr, &addr)) { /* DAD failed for link-local based on MAC address */ idev->cnf.disable_ipv6 = 1; pr_info("%s: IPv6 being disabled!\n", ifp->idev->dev->name); } } errdad: /* transition from _POSTDAD to _ERRDAD */ ifp->state = INET6_IFADDR_STATE_ERRDAD; spin_unlock_bh(&ifp->lock); //再次进行dad检查 addrconf_mod_dad_work(ifp, 0); }从上述代码可以看出基本流程是按照RFC7217的说明:
如果DAD失败,就会在DAD_Counter满足idgen_retries,就会重新生成, DAD_Counter增加的地方有两个:
1. 刚生成地址时,检查是否是保留interface id,如果是,DAD_Counter 加一,重新生成stable address
2. DAD失败之后,重新生成stable address,也会加一
作者:wdscq1234 发表于2016/10/16 10:45:42 原文链接
阅读:55 评论:0 查看评论