void addrconf_prefix_rcv(struct net_device *dev, u8 *opt, int len) { struct prefix_info *pinfo; struct rt6_info *rt; __u32 valid_lft; __u32 prefered_lft; int addr_type; unsigned long rt_expires; struct inet6_dev *in6_dev; pinfo = (struct prefix_info *) opt; if (len < sizeof(struct prefix_info)) { ADBG(("addrconf: prefix option too short\n")); return; } /* * Validation checks ([ADDRCONF], page 19) */ addr_type = ipv6_addr_type(&pinfo->prefix); if (addr_type & (IPV6_ADDR_MULTICAST|IPV6_ADDR_LINKLOCAL)) return; valid_lft = ntohl(pinfo->valid); prefered_lft = ntohl(pinfo->prefered); if (prefered_lft > valid_lft) { if (net_ratelimit()) printk(KERN_WARNING "addrconf: prefix option has invalid lifetime\n"); return; } in6_dev = in6_dev_get(dev); if (in6_dev == NULL) { if (net_ratelimit()) printk(KERN_DEBUG "addrconf: device %s not configured\n", dev->name); return; } /* * Two things going on here: * 1) Add routes for on-link prefixes * 2) Configure prefixes with the auto flag set */ /* Avoid arithemtic overflow. Really, we could save rt_expires in seconds, likely valid_lft, but it would require division in fib gc, that it not good. */ if (valid_lft >= 0x7FFFFFFF/HZ) rt_expires = 0; else rt_expires = jiffies + valid_lft * HZ; rt = rt6_lookup(&pinfo->prefix, NULL, dev->ifindex, 1); if (rt && ((rt->rt6i_flags & (RTF_GATEWAY | RTF_DEFAULT)) == 0)) { if (rt->rt6i_flags&RTF_EXPIRES) { if (pinfo->onlink == 0 || valid_lft == 0) { ip6_del_rt(rt); rt = NULL; } else { rt->rt6i_expires = rt_expires; } } } else if (pinfo->onlink && valid_lft) { addrconf_prefix_route(&pinfo->prefix, pinfo->prefix_len, dev, rt_expires, RTF_ADDRCONF|RTF_EXPIRES); } if (rt) dst_release(&rt->u.dst); /* Try to figure out our local address for this prefix */ if (pinfo->autoconf && in6_dev->cnf.autoconf) { struct inet6_ifaddr * ifp; struct in6_addr addr; int plen; plen = pinfo->prefix_len >> 3; if (pinfo->prefix_len == 64) { memcpy(&addr, &pinfo->prefix, 8); if (ipv6_generate_eui64(addr.s6_addr + 8, dev) && ipv6_inherit_eui64(addr.s6_addr + 8, in6_dev)) { in6_dev_put(in6_dev); return; } goto ok; } if (net_ratelimit()) printk(KERN_DEBUG "IPv6 addrconf: prefix with wrong length %d\n", pinfo->prefix_len); in6_dev_put(in6_dev); return; ok: ifp = ipv6_get_ifaddr(&addr, dev); if (ifp == NULL && valid_lft) { ifp = ipv6_add_addr(in6_dev, &addr, pinfo->prefix_len, addr_type&IPV6_ADDR_SCOPE_MASK, 0); if (ifp == NULL) { in6_dev_put(in6_dev); return; } addrconf_dad_start(ifp); } if (ifp && valid_lft == 0) { ipv6_del_addr(ifp); ifp = NULL; } if (ifp) { int flags; spin_lock(&ifp->lock); ifp->valid_lft = valid_lft; ifp->prefered_lft = prefered_lft; ifp->tstamp = jiffies; flags = ifp->flags; ifp->flags &= ~IFA_F_DEPRECATED; spin_unlock(&ifp->lock); if (!(flags&IFA_F_TENTATIVE)) ipv6_ifa_notify((flags&IFA_F_DEPRECATED) ? 0 : RTM_NEWADDR, ifp); in6_ifa_put(ifp); } } in6_dev_put(in6_dev); }
void addrconf_prefix_rcv(struct device *dev, u8 *opt, int len) { struct prefix_info *pinfo; struct rt6_info *rt; __u32 valid_lft; __u32 prefered_lft; int addr_type; unsigned long rt_expires; struct inet6_dev *in6_dev = ipv6_get_idev(dev); if (in6_dev == NULL) { printk(KERN_DEBUG "addrconf: device %s not configured\n", dev->name); return; } pinfo = (struct prefix_info *) opt; if (len < sizeof(struct prefix_info)) { ADBG(("addrconf: prefix option too short\n")); return; } /* * Validation checks ([ADDRCONF], page 19) */ addr_type = ipv6_addr_type(&pinfo->prefix); if (addr_type & (IPV6_ADDR_MULTICAST|IPV6_ADDR_LINKLOCAL)) return; valid_lft = ntohl(pinfo->valid); prefered_lft = ntohl(pinfo->prefered); if (prefered_lft > valid_lft) { printk(KERN_WARNING "addrconf: prefix option has invalid lifetime\n"); return; } /* * Two things going on here: * 1) Add routes for on-link prefixes * 2) Configure prefixes with the auto flag set */ /* Avoid arithemtic overflow. Really, we could save rt_expires in seconds, likely valid_lft, but it would require division in fib gc, that it not good. */ if (valid_lft >= 0x7FFFFFFF/HZ) rt_expires = 0; else rt_expires = jiffies + valid_lft * HZ; rt = rt6_lookup(&pinfo->prefix, NULL, dev->ifindex, 1); if (rt && ((rt->rt6i_flags & (RTF_GATEWAY | RTF_DEFAULT)) == 0)) { if (rt->rt6i_flags&RTF_EXPIRES) { if (pinfo->onlink == 0 || valid_lft == 0) { ip6_del_rt(rt); } else { rt->rt6i_expires = rt_expires; } } } else if (pinfo->onlink && valid_lft) { addrconf_prefix_route(&pinfo->prefix, pinfo->prefix_len, dev, rt_expires, RTF_ADDRCONF|RTF_EXPIRES); } if (rt) dst_release(&rt->u.dst); /* Try to figure out our local address for this prefix */ if (pinfo->autoconf && in6_dev->cnf.autoconf) { struct inet6_ifaddr * ifp; struct in6_addr addr; int newif = 0; int plen; plen = pinfo->prefix_len >> 3; #ifdef CONFIG_IPV6_EUI64 if (pinfo->prefix_len == 64) { memcpy(&addr, &pinfo->prefix, 8); if (ipv6_generate_eui64(addr.s6_addr + 8, dev) && ipv6_inherit_eui64(addr.s6_addr + 8, in6_dev)) return; goto ok; } #endif #ifndef CONFIG_IPV6_NO_PB if (pinfo->prefix_len == ((sizeof(struct in6_addr) - dev->addr_len)<<3)) { memcpy(&addr, &pinfo->prefix, plen); memcpy(addr.s6_addr + plen, dev->dev_addr, dev->addr_len); goto ok; } #endif printk(KERN_DEBUG "IPv6 addrconf: prefix with wrong length %d\n", pinfo->prefix_len); return; ok: ifp = ipv6_chk_addr(&addr, dev, 1); if ((ifp == NULL || (ifp->flags&ADDR_INVALID)) && valid_lft) { if (ifp == NULL) ifp = ipv6_add_addr(in6_dev, &addr, addr_type & IPV6_ADDR_SCOPE_MASK); if (ifp == NULL) return; ifp->prefix_len = pinfo->prefix_len; addrconf_dad_start(ifp); newif = 1; } if (ifp && valid_lft == 0) { ipv6_del_addr(ifp); ifp = NULL; } if (ifp) { int event = 0; #ifdef CONFIG_IPV6_ACONF_MIN_VALID_LFT #define TWO_HOURS 7200 if (newif) goto lft_ok; if (valid_lft > TWO_HOURS || valid_lft > ifp->valid_lft) { goto lft_ok; } else if (ifp->valid_lft <= TWO_HOURS && valid_lft <= ifp->valid_lft) { ; } else { valid_lft = TWO_HOURS; if (valid_lft < prefered_lft) prefered_lft = valid_lft; goto lft_ok; } goto skip_lft; lft_ok: #endif ifp->valid_lft = valid_lft; ifp->prefered_lft = prefered_lft; ifp->tstamp = jiffies; if (ifp->flags & ADDR_INVALID) event = RTM_NEWADDR; ifp->flags &= ~(ADDR_DEPRECATED|ADDR_INVALID); ipv6_ifa_notify(event, ifp); #ifdef CONFIG_IPV6_ACONF_MIN_VALID_LFT skip_lft: #endif } } }