📄 addrconf.c
字号:
ADBG(("ipv6_add_addr: already assigned\n")); err = -EEXIST; goto out; } ifa = kzalloc(sizeof(struct inet6_ifaddr), GFP_ATOMIC); if (ifa == NULL) { ADBG(("ipv6_add_addr: malloc failed\n")); err = -ENOBUFS; goto out; } rt = addrconf_dst_alloc(idev, addr, 0); if (IS_ERR(rt)) { err = PTR_ERR(rt); goto out; } ipv6_addr_copy(&ifa->addr, addr); spin_lock_init(&ifa->lock); init_timer(&ifa->timer); ifa->timer.data = (unsigned long) ifa; ifa->scope = scope; ifa->prefix_len = pfxlen; ifa->flags = flags | IFA_F_TENTATIVE; ifa->cstamp = ifa->tstamp = jiffies; ifa->rt = rt; /* * part one of RFC 4429, section 3.3 * We should not configure an address as * optimistic if we do not yet know the link * layer address of our nexhop router */ if (rt->rt6i_nexthop == NULL) ifa->flags &= ~IFA_F_OPTIMISTIC; ifa->idev = idev; in6_dev_hold(idev); /* For caller */ in6_ifa_hold(ifa); /* Add to big hash table */ hash = ipv6_addr_hash(addr); ifa->lst_next = inet6_addr_lst[hash]; inet6_addr_lst[hash] = ifa; in6_ifa_hold(ifa); write_unlock(&addrconf_hash_lock); write_lock(&idev->lock); /* Add to inet6_dev unicast addr list. */ ipv6_link_dev_addr(idev, ifa);#ifdef CONFIG_IPV6_PRIVACY if (ifa->flags&IFA_F_TEMPORARY) { ifa->tmp_next = idev->tempaddr_list; idev->tempaddr_list = ifa; in6_ifa_hold(ifa); }#endif in6_ifa_hold(ifa); write_unlock(&idev->lock);out2: rcu_read_unlock_bh(); if (likely(err == 0)) atomic_notifier_call_chain(&inet6addr_chain, NETDEV_UP, ifa); else { kfree(ifa); ifa = ERR_PTR(err); } return ifa;out: write_unlock(&addrconf_hash_lock); goto out2;}/* This function wants to get referenced ifp and releases it before return */static void ipv6_del_addr(struct inet6_ifaddr *ifp){ struct inet6_ifaddr *ifa, **ifap; struct inet6_dev *idev = ifp->idev; int hash; int deleted = 0, onlink = 0; unsigned long expires = jiffies; hash = ipv6_addr_hash(&ifp->addr); ifp->dead = 1; write_lock_bh(&addrconf_hash_lock); for (ifap = &inet6_addr_lst[hash]; (ifa=*ifap) != NULL; ifap = &ifa->lst_next) { if (ifa == ifp) { *ifap = ifa->lst_next; __in6_ifa_put(ifp); ifa->lst_next = NULL; break; } } write_unlock_bh(&addrconf_hash_lock); write_lock_bh(&idev->lock);#ifdef CONFIG_IPV6_PRIVACY if (ifp->flags&IFA_F_TEMPORARY) { for (ifap = &idev->tempaddr_list; (ifa=*ifap) != NULL; ifap = &ifa->tmp_next) { if (ifa == ifp) { *ifap = ifa->tmp_next; if (ifp->ifpub) { in6_ifa_put(ifp->ifpub); ifp->ifpub = NULL; } __in6_ifa_put(ifp); ifa->tmp_next = NULL; break; } } }#endif for (ifap = &idev->addr_list; (ifa=*ifap) != NULL;) { if (ifa == ifp) { *ifap = ifa->if_next; __in6_ifa_put(ifp); ifa->if_next = NULL; if (!(ifp->flags & IFA_F_PERMANENT) || onlink > 0) break; deleted = 1; continue; } else if (ifp->flags & IFA_F_PERMANENT) { if (ipv6_prefix_equal(&ifa->addr, &ifp->addr, ifp->prefix_len)) { if (ifa->flags & IFA_F_PERMANENT) { onlink = 1; if (deleted) break; } else { unsigned long lifetime; if (!onlink) onlink = -1; spin_lock(&ifa->lock); lifetime = min_t(unsigned long, ifa->valid_lft, 0x7fffffffUL/HZ); if (time_before(expires, ifa->tstamp + lifetime * HZ)) expires = ifa->tstamp + lifetime * HZ; spin_unlock(&ifa->lock); } } } ifap = &ifa->if_next; } write_unlock_bh(&idev->lock); ipv6_ifa_notify(RTM_DELADDR, ifp); atomic_notifier_call_chain(&inet6addr_chain, NETDEV_DOWN, ifp); addrconf_del_timer(ifp); /* * Purge or update corresponding prefix * * 1) we don't purge prefix here if address was not permanent. * prefix is managed by its own lifetime. * 2) if there're no addresses, delete prefix. * 3) if there're still other permanent address(es), * corresponding prefix is still permanent. * 4) otherwise, update prefix lifetime to the * longest valid lifetime among the corresponding * addresses on the device. * Note: subsequent RA will update lifetime. * * --yoshfuji */ if ((ifp->flags & IFA_F_PERMANENT) && onlink < 1) { struct in6_addr prefix; struct rt6_info *rt; ipv6_addr_prefix(&prefix, &ifp->addr, ifp->prefix_len); rt = rt6_lookup(&prefix, NULL, ifp->idev->dev->ifindex, 1); if (rt && ((rt->rt6i_flags & (RTF_GATEWAY | RTF_DEFAULT)) == 0)) { if (onlink == 0) { ip6_del_rt(rt); rt = NULL; } else if (!(rt->rt6i_flags & RTF_EXPIRES)) { rt->rt6i_expires = expires; rt->rt6i_flags |= RTF_EXPIRES; } } dst_release(&rt->u.dst); } in6_ifa_put(ifp);}#ifdef CONFIG_IPV6_PRIVACYstatic int ipv6_create_tempaddr(struct inet6_ifaddr *ifp, struct inet6_ifaddr *ift){ struct inet6_dev *idev = ifp->idev; struct in6_addr addr, *tmpaddr; unsigned long tmp_prefered_lft, tmp_valid_lft, tmp_cstamp, tmp_tstamp; int tmp_plen; int ret = 0; int max_addresses; u32 addr_flags; write_lock(&idev->lock); if (ift) { spin_lock_bh(&ift->lock); memcpy(&addr.s6_addr[8], &ift->addr.s6_addr[8], 8); spin_unlock_bh(&ift->lock); tmpaddr = &addr; } else { tmpaddr = NULL; }retry: in6_dev_hold(idev); if (idev->cnf.use_tempaddr <= 0) { write_unlock(&idev->lock); printk(KERN_INFO "ipv6_create_tempaddr(): use_tempaddr is disabled.\n"); in6_dev_put(idev); ret = -1; goto out; } spin_lock_bh(&ifp->lock); if (ifp->regen_count++ >= idev->cnf.regen_max_retry) { idev->cnf.use_tempaddr = -1; /*XXX*/ spin_unlock_bh(&ifp->lock); write_unlock(&idev->lock); printk(KERN_WARNING "ipv6_create_tempaddr(): regeneration time exceeded. disabled temporary address support.\n"); in6_dev_put(idev); ret = -1; goto out; } in6_ifa_hold(ifp); memcpy(addr.s6_addr, ifp->addr.s6_addr, 8); if (__ipv6_try_regen_rndid(idev, tmpaddr) < 0) { spin_unlock_bh(&ifp->lock); write_unlock(&idev->lock); printk(KERN_WARNING "ipv6_create_tempaddr(): regeneration of randomized interface id failed.\n"); in6_ifa_put(ifp); in6_dev_put(idev); ret = -1; goto out; } memcpy(&addr.s6_addr[8], idev->rndid, 8); tmp_valid_lft = min_t(__u32, ifp->valid_lft, idev->cnf.temp_valid_lft); tmp_prefered_lft = min_t(__u32, ifp->prefered_lft, idev->cnf.temp_prefered_lft - desync_factor / HZ); tmp_plen = ifp->prefix_len; max_addresses = idev->cnf.max_addresses; tmp_cstamp = ifp->cstamp; tmp_tstamp = ifp->tstamp; spin_unlock_bh(&ifp->lock); write_unlock(&idev->lock); addr_flags = IFA_F_TEMPORARY; /* set in addrconf_prefix_rcv() */ if (ifp->flags & IFA_F_OPTIMISTIC) addr_flags |= IFA_F_OPTIMISTIC; ift = !max_addresses || ipv6_count_addresses(idev) < max_addresses ? ipv6_add_addr(idev, &addr, tmp_plen, ipv6_addr_type(&addr)&IPV6_ADDR_SCOPE_MASK, addr_flags) : NULL; if (!ift || IS_ERR(ift)) { in6_ifa_put(ifp); in6_dev_put(idev); printk(KERN_INFO "ipv6_create_tempaddr(): retry temporary address regeneration.\n"); tmpaddr = &addr; write_lock(&idev->lock); goto retry; } spin_lock_bh(&ift->lock); ift->ifpub = ifp; ift->valid_lft = tmp_valid_lft; ift->prefered_lft = tmp_prefered_lft; ift->cstamp = tmp_cstamp; ift->tstamp = tmp_tstamp; spin_unlock_bh(&ift->lock); addrconf_dad_start(ift, 0); in6_ifa_put(ift); in6_dev_put(idev);out: return ret;}#endif/* * Choose an appropriate source address (RFC3484) */struct ipv6_saddr_score { int addr_type; unsigned int attrs; int matchlen; int scope; unsigned int rule;};#define IPV6_SADDR_SCORE_LOCAL 0x0001#define IPV6_SADDR_SCORE_PREFERRED 0x0004#define IPV6_SADDR_SCORE_HOA 0x0008#define IPV6_SADDR_SCORE_OIF 0x0010#define IPV6_SADDR_SCORE_LABEL 0x0020#define IPV6_SADDR_SCORE_PRIVACY 0x0040static inline int ipv6_saddr_preferred(int type){ if (type & (IPV6_ADDR_MAPPED|IPV6_ADDR_COMPATv4| IPV6_ADDR_LOOPBACK|IPV6_ADDR_RESERVED)) return 1; return 0;}/* static matching label */static inline int ipv6_saddr_label(const struct in6_addr *addr, int type){ /* * prefix (longest match) label * ----------------------------- * ::1/128 0 * ::/0 1 * 2002::/16 2 * ::/96 3 * ::ffff:0:0/96 4 * fc00::/7 5 * 2001::/32 6 */ if (type & IPV6_ADDR_LOOPBACK) return 0; else if (type & IPV6_ADDR_COMPATv4) return 3; else if (type & IPV6_ADDR_MAPPED) return 4; else if (addr->s6_addr32[0] == htonl(0x20010000)) return 6; else if (addr->s6_addr16[0] == htons(0x2002)) return 2; else if ((addr->s6_addr[0] & 0xfe) == 0xfc) return 5; return 1;}int ipv6_dev_get_saddr(struct net_device *daddr_dev, struct in6_addr *daddr, struct in6_addr *saddr){ struct ipv6_saddr_score hiscore; struct inet6_ifaddr *ifa_result = NULL; int daddr_type = __ipv6_addr_type(daddr); int daddr_scope = __ipv6_addr_src_scope(daddr_type); u32 daddr_label = ipv6_saddr_label(daddr, daddr_type); struct net_device *dev; memset(&hiscore, 0, sizeof(hiscore)); read_lock(&dev_base_lock); rcu_read_lock(); for_each_netdev(&init_net, dev) { struct inet6_dev *idev; struct inet6_ifaddr *ifa; /* Rule 0: Candidate Source Address (section 4) * - multicast and link-local destination address, * the set of candidate source address MUST only * include addresses assigned to interfaces * belonging to the same link as the outgoing * interface. * (- For site-local destination addresses, the * set of candidate source addresses MUST only * include addresses assigned to interfaces * belonging to the same site as the outgoing * interface.) */ if ((daddr_type & IPV6_ADDR_MULTICAST || daddr_scope <= IPV6_ADDR_SCOPE_LINKLOCAL) && daddr_dev && dev != daddr_dev) continue; idev = __in6_dev_get(dev); if (!idev) continue; read_lock_bh(&idev->lock); for (ifa = idev->addr_list; ifa; ifa = ifa->if_next) { struct ipv6_saddr_score score; score.addr_type = __ipv6_addr_type(&ifa->addr); /* Rule 0: * - Tentative Address (RFC2462 section 5.4) * - A tentative address is not considered * "assigned to an interface" in the traditional * sense, unless it is also flagged as optimistic. * - Candidate Source Address (section 4) * - In any case, anycast addresses, multicast * addresses, and the unspecified address MUST * NOT be included in a candidate set. */ if ((ifa->flags & IFA_F_TENTATIVE) && (!(ifa->flags & IFA_F_OPTIMISTIC))) continue; if (unlikely(score.addr_type == IPV6_ADDR_ANY || score.addr_type & IPV6_ADDR_MULTICAST)) { LIMIT_NETDEBUG(KERN_DEBUG "ADDRCONF: unspecified / multicast address " "assigned as unicast address on %s", dev->name); continue; } score.attrs = 0; score.matchlen = 0; score.scope = 0; score.rule = 0; if (ifa_result == NULL) { /* record it if the first available entry */ goto record_it; } /* Rule 1: Prefer same address */ if (hiscore.rule < 1) { if (ipv6_addr_equal(&ifa_result->addr, daddr)) hiscore.attrs |= IPV6_SADDR_SCORE_LOCAL; hiscore.rule++; } if (ipv6_addr_equal(&ifa->addr, daddr)) { score.attrs |= IPV6_SADDR_SCORE_LOCAL; if (!(hiscore.attrs & IPV6_SADDR_SCORE_LOCAL)) { score.rule = 1; goto record_it; } } else { if (hiscore.attrs & IPV6_SADDR_SCORE_LOCAL) continue; } /* Rule 2: Prefer appropriate scope */ if (hiscore.rule < 2) { hiscore.scope = __ipv6_addr_src_scope(hiscore.addr_type); hiscore.rule++; } score.scope = __ipv6_addr_src_scope(score.addr_type); if (hiscore.scope < score.scope) { if (hiscore.scope < daddr_scope) { score.rule = 2; goto record_it; } else continue; } else if (score.scope < hiscore.scope) { if (score.scope < daddr_scope) break; /* addresses sorted by scope */ else { score.rule = 2; goto record_it; } } /* Rule 3: Avoid deprecated and optimistic addresses */ if (hiscore.rule < 3) { if (ipv6_saddr_preferred(hiscore.addr_type) || (((ifa_result->flags & (IFA_F_DEPRECATED|IFA_F_OPTIMISTIC)) == 0))) hiscore.attrs |= IPV6_SADDR_SCORE_PREFERRED; hiscore.rule++; } if (ipv6_saddr_preferred(score.addr_type) || (((ifa->flags & (IFA_F_DEPRECATED|IFA_F_OPTIMISTIC)) == 0))) { score.attrs |= IPV6_SADDR_SCORE_PREFERRED; if (!(hiscore.attrs & IPV6_SADDR_SCORE_PREFERRED)) { score.rule = 3; goto record_it; } } else { if (hiscore.attrs & IPV6_SADDR_SCORE_PREFERRED) continue; } /* Rule 4: Prefer home address */#if defined(CONFIG_IPV6_MIP6) || defined(CONFIG_IPV6_MIP6_MODULE) if (hiscore.rule < 4) { if (ifa_result->flags & IFA_F_HOMEADDRESS) hiscore.attrs |= IPV6_SADDR_SCORE_HOA; hiscore.rule++; } if (ifa->flags & IFA_F_HOMEADDRESS) { score.attrs |= IPV6_SADDR_SCORE_HOA; if (!(ifa_result->flags & IFA_F_HOMEADDRESS)) { score.rule = 4; goto record_it; } } else { if (hiscore.attrs & IPV6_SADDR_SCORE_HOA) continue; }#else if (hiscore.rule < 4) hiscore.rule++;#endif /* Rule 5: Prefer outgoing interface */ if (hiscore.rule < 5) { if (daddr_dev == NULL || daddr_dev == ifa_result->idev->dev) hiscore.attrs |= IPV6_SADDR_SCORE_OIF; hiscore.rule++; } if (daddr_dev == NULL || daddr_dev == ifa->idev->dev) { score.attrs |= IPV6_SADDR_SCORE_OIF; if (!(hiscore.attrs & IPV6_SADDR_SCORE_OIF)) { score.rule = 5; goto record_it; }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -