📄 addrconf.c
字号:
__ipv6_dev_ac_dec(ifp->idev, &addr);}static int ipv6_generate_eui64(u8 *eui, struct net_device *dev){ switch (dev->type) { case ARPHRD_ETHER: case ARPHRD_FDDI: case ARPHRD_IEEE802_TR: if (dev->addr_len != ETH_ALEN) return -1; memcpy(eui, dev->dev_addr, 3); memcpy(eui + 5, dev->dev_addr+3, 3); eui[3] = 0xFF; eui[4] = 0xFE; eui[0] ^= 2; return 0; case ARPHRD_ARCNET: /* XXX: inherit EUI-64 from other interface -- yoshfuji */ if (dev->addr_len != ARCNET_ALEN) return -1; memset(eui, 0, 7); eui[7] = *(u8*)dev->dev_addr; return 0; } return -1;}static int ipv6_inherit_eui64(u8 *eui, struct inet6_dev *idev){ int err = -1; struct inet6_ifaddr *ifp; read_lock_bh(&idev->lock); for (ifp=idev->addr_list; ifp; ifp=ifp->if_next) { if (ifp->scope == IFA_LINK && !(ifp->flags&IFA_F_TENTATIVE)) { memcpy(eui, ifp->addr.s6_addr+8, 8); err = 0; break; } } read_unlock_bh(&idev->lock); return err;}#ifdef CONFIG_IPV6_PRIVACY/* (re)generation of randomized interface identifier (RFC 3041 3.2, 3.5) */static int __ipv6_regen_rndid(struct inet6_dev *idev){ struct net_device *dev; struct scatterlist sg[2]; sg[0].page = virt_to_page(idev->entropy); sg[0].offset = offset_in_page(idev->entropy); sg[0].length = 8; sg[1].page = virt_to_page(idev->work_eui64); sg[1].offset = offset_in_page(idev->work_eui64); sg[1].length = 8; dev = idev->dev; if (ipv6_generate_eui64(idev->work_eui64, dev)) { printk(KERN_INFO "__ipv6_regen_rndid(idev=%p): cannot get EUI64 identifier; use random bytes.\n", idev); get_random_bytes(idev->work_eui64, sizeof(idev->work_eui64)); }regen: spin_lock(&md5_tfm_lock); if (unlikely(md5_tfm == NULL)) { spin_unlock(&md5_tfm_lock); return -1; } crypto_digest_init(md5_tfm); crypto_digest_update(md5_tfm, sg, 2); crypto_digest_final(md5_tfm, idev->work_digest); spin_unlock(&md5_tfm_lock); memcpy(idev->rndid, &idev->work_digest[0], 8); idev->rndid[0] &= ~0x02; memcpy(idev->entropy, &idev->work_digest[8], 8); /* * <draft-ietf-ipngwg-temp-addresses-v2-00.txt>: * check if generated address is not inappropriate * * - Reserved subnet anycast (RFC 2526) * 11111101 11....11 1xxxxxxx * - ISATAP (draft-ietf-ngtrans-isatap-13.txt) 5.1 * 00-00-5E-FE-xx-xx-xx-xx * - value 0 * - XXX: already assigned to an address on the device */ if (idev->rndid[0] == 0xfd && (idev->rndid[1]&idev->rndid[2]&idev->rndid[3]&idev->rndid[4]&idev->rndid[5]&idev->rndid[6]) == 0xff && (idev->rndid[7]&0x80)) goto regen; if ((idev->rndid[0]|idev->rndid[1]) == 0) { if (idev->rndid[2] == 0x5e && idev->rndid[3] == 0xfe) goto regen; if ((idev->rndid[2]|idev->rndid[3]|idev->rndid[4]|idev->rndid[5]|idev->rndid[6]|idev->rndid[7]) == 0x00) goto regen; } return 0;}static void ipv6_regen_rndid(unsigned long data){ struct inet6_dev *idev = (struct inet6_dev *) data; unsigned long expires; read_lock_bh(&addrconf_lock); write_lock_bh(&idev->lock); if (idev->dead) goto out; if (__ipv6_regen_rndid(idev) < 0) goto out; expires = jiffies + idev->cnf.temp_prefered_lft * HZ - idev->cnf.regen_max_retry * idev->cnf.dad_transmits * idev->nd_parms->retrans_time - desync_factor; if (time_before(expires, jiffies)) { printk(KERN_WARNING "ipv6_regen_rndid(): too short regeneration interval; timer disabled for %s.\n", idev->dev->name); goto out; } if (!mod_timer(&idev->regen_timer, expires)) in6_dev_hold(idev);out: write_unlock_bh(&idev->lock); read_unlock_bh(&addrconf_lock); in6_dev_put(idev);}static int __ipv6_try_regen_rndid(struct inet6_dev *idev, struct in6_addr *tmpaddr) { int ret = 0; if (tmpaddr && memcmp(idev->rndid, &tmpaddr->s6_addr[8], 8) == 0) ret = __ipv6_regen_rndid(idev); return ret;}#endif/* * Add prefix route. */static voidaddrconf_prefix_route(struct in6_addr *pfx, int plen, struct net_device *dev, unsigned long expires, unsigned flags){ struct in6_rtmsg rtmsg; memset(&rtmsg, 0, sizeof(rtmsg)); ipv6_addr_copy(&rtmsg.rtmsg_dst, pfx); rtmsg.rtmsg_dst_len = plen; rtmsg.rtmsg_metric = IP6_RT_PRIO_ADDRCONF; rtmsg.rtmsg_ifindex = dev->ifindex; rtmsg.rtmsg_info = expires; rtmsg.rtmsg_flags = RTF_UP|flags; rtmsg.rtmsg_type = RTMSG_NEWROUTE; /* Prevent useless cloning on PtP SIT. This thing is done here expecting that the whole class of non-broadcast devices need not cloning. */ if (dev->type == ARPHRD_SIT && (dev->flags&IFF_POINTOPOINT)) rtmsg.rtmsg_flags |= RTF_NONEXTHOP; ip6_route_add(&rtmsg, NULL, NULL);}/* Create "default" multicast route to the interface */static void addrconf_add_mroute(struct net_device *dev){ struct in6_rtmsg rtmsg; memset(&rtmsg, 0, sizeof(rtmsg)); ipv6_addr_set(&rtmsg.rtmsg_dst, htonl(0xFF000000), 0, 0, 0); rtmsg.rtmsg_dst_len = 8; rtmsg.rtmsg_metric = IP6_RT_PRIO_ADDRCONF; rtmsg.rtmsg_ifindex = dev->ifindex; rtmsg.rtmsg_flags = RTF_UP; rtmsg.rtmsg_type = RTMSG_NEWROUTE; ip6_route_add(&rtmsg, NULL, NULL);}static void sit_route_add(struct net_device *dev){ struct in6_rtmsg rtmsg; memset(&rtmsg, 0, sizeof(rtmsg)); rtmsg.rtmsg_type = RTMSG_NEWROUTE; rtmsg.rtmsg_metric = IP6_RT_PRIO_ADDRCONF; /* prefix length - 96 bits "::d.d.d.d" */ rtmsg.rtmsg_dst_len = 96; rtmsg.rtmsg_flags = RTF_UP|RTF_NONEXTHOP; rtmsg.rtmsg_ifindex = dev->ifindex; ip6_route_add(&rtmsg, NULL, NULL);}static void addrconf_add_lroute(struct net_device *dev){ struct in6_addr addr; ipv6_addr_set(&addr, htonl(0xFE800000), 0, 0, 0); addrconf_prefix_route(&addr, 64, dev, 0, 0);}static struct inet6_dev *addrconf_add_dev(struct net_device *dev){ struct inet6_dev *idev; ASSERT_RTNL(); if ((idev = ipv6_find_idev(dev)) == NULL) return NULL; /* Add default multicast route */ addrconf_add_mroute(dev); /* Add link local route */ addrconf_add_lroute(dev); return idev;}void addrconf_prefix_rcv(struct net_device *dev, u8 *opt, int len){ struct prefix_info *pinfo; __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 arithmetic 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; if (pinfo->onlink) { struct rt6_info *rt; 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 (valid_lft == 0) { ip6_del_rt(rt, NULL, NULL); rt = NULL; } else { rt->rt6i_expires = rt_expires; } } } else if (valid_lft) { addrconf_prefix_route(&pinfo->prefix, pinfo->prefix_len, dev, rt_expires, RTF_ADDRCONF|RTF_EXPIRES|RTF_PREFIX_RT); } 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 create = 0, update_lft = 0; 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, 1); if (ifp == NULL && valid_lft) { int max_addresses = in6_dev->cnf.max_addresses; /* Do not allow to create too much of autoconfigured * addresses; this would be too easy way to crash kernel. */ if (!max_addresses || ipv6_count_addresses(in6_dev) < max_addresses) ifp = ipv6_add_addr(in6_dev, &addr, pinfo->prefix_len, addr_type&IPV6_ADDR_SCOPE_MASK, 0); if (!ifp || IS_ERR(ifp)) { in6_dev_put(in6_dev); return; } update_lft = create = 1; ifp->cstamp = jiffies; addrconf_dad_start(ifp, RTF_ADDRCONF|RTF_PREFIX_RT); } if (ifp) { int flags; unsigned long now;#ifdef CONFIG_IPV6_PRIVACY struct inet6_ifaddr *ift;#endif u32 stored_lft; /* update lifetime (RFC2462 5.5.3 e) */ spin_lock(&ifp->lock); now = jiffies; if (ifp->valid_lft > (now - ifp->tstamp) / HZ) stored_lft = ifp->valid_lft - (now - ifp->tstamp) / HZ; else stored_lft = 0; if (!update_lft && stored_lft) { if (valid_lft > MIN_VALID_LIFETIME || valid_lft > stored_lft) update_lft = 1; else if (stored_lft <= MIN_VALID_LIFETIME) { /* valid_lft <= stored_lft is always true */ /* XXX: IPsec */ update_lft = 0; } else { valid_lft = MIN_VALID_LIFETIME; if (valid_lft < prefered_lft) prefered_lft = valid_lft; update_lft = 1; } } if (update_lft) { ifp->valid_lft = valid_lft; ifp->prefered_lft = prefered_lft; ifp->tstamp = now; flags = ifp->flags; ifp->flags &= ~IFA_F_DEPRECATED; spin_unlock(&ifp->lock); if (!(flags&IFA_F_TENTATIVE)) ipv6_ifa_notify(0, ifp); } else spin_unlock(&ifp->lock);#ifdef CONFIG_IPV6_PRIVACY read_lock_bh(&in6_dev->lock); /* update all temporary addresses in the list */ for (ift=in6_dev->tempaddr_list; ift; ift=ift->tmp_next) { /* * When adjusting the lifetimes of an existing * temporary address, only lower the lifetimes. * Implementations must not increase the * lifetimes of an existing temporary address * when processing a Prefix Information Option. */ spin_lock(&ift->lock); flags = ift->flags; if (ift->valid_lft > valid_lft && ift->valid_lft - valid_lft > (jiffies - ift->tstamp) / HZ) ift->valid_lft = valid_lft + (jiffies - ift->tstamp) / HZ; if (ift->prefered_lft > prefered_lft && ift->prefered_lft - prefered_lft > (jiffies - ift->tstamp) / HZ) ift->prefered_lft = prefered_lft + (jiffies - ift->tstamp) / HZ; spin_unlock(&ift->lock); if (!(flags&IFA_F_TENTATIVE)) ipv6_ifa_notify(0, ift); } if (create && in6_dev->cnf.use_tempaddr > 0) { /* * When a new public address is created as described in [ADDRCONF], * also create a new temporary address. */ read_unlock_bh(&in6_dev->lock); ipv6_create_tempaddr(ifp, NULL); } else { read_unlock_bh(&in6_dev->lock); }#endif in6_ifa_put(ifp); addrconf_verify(0); } } inet6_prefix_notify(RTM_NEWPREFIX, in6_dev, pinfo); in6_dev_put(in6_dev);}/* * Set destination address. * Special case for SIT interfaces where we create a new "virtual" * device. */int addrconf_set_dstaddr(void __user *arg){ struct in6_ifreq ireq; struct net_device *dev; int err = -EINVAL; rtnl_lock(); err = -EFAULT; if (copy_from_user(&ireq, arg, sizeof(struct in6_ifreq))) goto err_exit; dev = __dev_get_by_index(ireq.ifr6_ifindex); err = -ENODEV; if (dev == NULL) goto err_exit; if (dev->type == ARPHRD_SIT) { struct ifreq ifr; mm_segment_t oldfs; struct ip_tunnel_parm p; err = -EADDRNOTAVAIL; if (!(ipv6_addr_type(&ireq.ifr6_addr) & IPV6_ADDR_COMPATv4)) goto err_exit; memset(&p, 0, sizeof(p)); p.iph.daddr = ireq.ifr6_addr.s6_addr32[3]; p.iph.saddr = 0; p.iph.version = 4; p.iph.ihl = 5; p.iph.protocol = IPPROTO_IPV6; p.iph.ttl = 64; ifr.ifr_ifru.ifru_data = (void __user *)&p; oldfs = get_fs(); set_fs(KERNEL_DS); err = dev->do_ioctl(dev, &ifr, SIOCADDTUNNEL); set_fs(oldfs); if (err == 0) { err = -ENOBUFS; if ((dev = __dev_get_by_name(p.name)) == NULL) goto err_exit; err = dev_open(dev); } }err_exit: rtnl_unlock(); return err;}/* * Manual configuration of address on an interface */static int inet6_addr_add(int ifindex, struct in6_addr *pfx, int plen){ struct inet6_ifaddr *ifp; struct inet6_dev *idev; struct net_device *dev; int scope; ASSERT_RTNL(); if ((dev = __dev_get_by_index(ifindex)) == NULL) return -ENODEV; if (!(dev->flags&IFF_UP)) return -ENETDOWN; if ((idev = addrconf_add_dev(dev)) == NULL) return -ENOBUFS; scope = ipv6_addr_scope(pfx); ifp = ipv6_add_addr(idev, pfx, plen, scope, IFA_F_PERMANENT); if (!IS_ERR(ifp)) { addrconf_dad_start(ifp, 0); in6_ifa_put(ifp); return 0; } return PTR_ERR(ifp);}static int inet6_addr_del(int ifindex, struct in6_addr *pfx, int plen){ struct inet6_ifaddr *ifp;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -