📄 addrconf.c
字号:
struct in6_addr addr; struct inet6_dev * idev; ASSERT_RTNL(); if ((dev->type != ARPHRD_ETHER) && (dev->type != ARPHRD_FDDI) && (dev->type != ARPHRD_IEEE802_TR) && (dev->type != ARPHRD_ARCNET) && (dev->type != ARPHRD_INFINIBAND)) { /* Alas, we support only Ethernet autoconfiguration. */ return; } idev = addrconf_add_dev(dev); if (idev == NULL) return; memset(&addr, 0, sizeof(struct in6_addr)); addr.s6_addr32[0] = htonl(0xFE800000); if (ipv6_generate_eui64(addr.s6_addr + 8, dev) == 0) addrconf_add_linklocal(idev, &addr);}#if defined(CONFIG_IPV6_SIT) || defined(CONFIG_IPV6_SIT_MODULE)static void addrconf_sit_config(struct net_device *dev){ struct inet6_dev *idev; ASSERT_RTNL(); /* * Configure the tunnel with one of our IPv4 * addresses... we should configure all of * our v4 addrs in the tunnel */ if ((idev = ipv6_find_idev(dev)) == NULL) { printk(KERN_DEBUG "init sit: add_dev failed\n"); return; } sit_add_v4_addrs(idev); if (dev->flags&IFF_POINTOPOINT) { addrconf_add_mroute(dev); addrconf_add_lroute(dev); } else sit_route_add(dev);}#endifstatic inline intipv6_inherit_linklocal(struct inet6_dev *idev, struct net_device *link_dev){ struct in6_addr lladdr; if (!ipv6_get_lladdr(link_dev, &lladdr, IFA_F_TENTATIVE)) { addrconf_add_linklocal(idev, &lladdr); return 0; } return -1;}static void ip6_tnl_add_linklocal(struct inet6_dev *idev){ struct net_device *link_dev; /* first try to inherit the link-local address from the link device */ if (idev->dev->iflink && (link_dev = __dev_get_by_index(&init_net, idev->dev->iflink))) { if (!ipv6_inherit_linklocal(idev, link_dev)) return; } /* then try to inherit it from any device */ for_each_netdev(&init_net, link_dev) { if (!ipv6_inherit_linklocal(idev, link_dev)) return; } printk(KERN_DEBUG "init ip6-ip6: add_linklocal failed\n");}/* * Autoconfigure tunnel with a link-local address so routing protocols, * DHCPv6, MLD etc. can be run over the virtual link */static void addrconf_ip6_tnl_config(struct net_device *dev){ struct inet6_dev *idev; ASSERT_RTNL(); if ((idev = addrconf_add_dev(dev)) == NULL) { printk(KERN_DEBUG "init ip6-ip6: add_dev failed\n"); return; } ip6_tnl_add_linklocal(idev);}static int addrconf_notify(struct notifier_block *this, unsigned long event, void * data){ struct net_device *dev = (struct net_device *) data; struct inet6_dev *idev = __in6_dev_get(dev); int run_pending = 0; int err; if (dev->nd_net != &init_net) return NOTIFY_DONE; switch(event) { case NETDEV_REGISTER: if (!idev && dev->mtu >= IPV6_MIN_MTU) { idev = ipv6_add_dev(dev); if (!idev) return notifier_from_errno(-ENOMEM); } break; case NETDEV_UP: case NETDEV_CHANGE: if (dev->flags & IFF_SLAVE) break; if (event == NETDEV_UP) { if (!addrconf_qdisc_ok(dev)) { /* device is not ready yet. */ printk(KERN_INFO "ADDRCONF(NETDEV_UP): %s: " "link is not ready\n", dev->name); break; } if (!idev && dev->mtu >= IPV6_MIN_MTU) idev = ipv6_add_dev(dev); if (idev) idev->if_flags |= IF_READY; } else { if (!addrconf_qdisc_ok(dev)) { /* device is still not ready. */ break; } if (idev) { if (idev->if_flags & IF_READY) { /* device is already configured. */ break; } idev->if_flags |= IF_READY; } printk(KERN_INFO "ADDRCONF(NETDEV_CHANGE): %s: " "link becomes ready\n", dev->name); run_pending = 1; } switch(dev->type) {#if defined(CONFIG_IPV6_SIT) || defined(CONFIG_IPV6_SIT_MODULE) case ARPHRD_SIT: addrconf_sit_config(dev); break;#endif case ARPHRD_TUNNEL6: addrconf_ip6_tnl_config(dev); break; case ARPHRD_LOOPBACK: init_loopback(dev); break; default: addrconf_dev_config(dev); break; } if (idev) { if (run_pending) addrconf_dad_run(idev); /* If the MTU changed during the interface down, when the interface up, the changed MTU must be reflected in the idev as well as routers. */ if (idev->cnf.mtu6 != dev->mtu && dev->mtu >= IPV6_MIN_MTU) { rt6_mtu_change(dev, dev->mtu); idev->cnf.mtu6 = dev->mtu; } idev->tstamp = jiffies; inet6_ifinfo_notify(RTM_NEWLINK, idev); /* If the changed mtu during down is lower than IPV6_MIN_MTU stop IPv6 on this interface. */ if (dev->mtu < IPV6_MIN_MTU) addrconf_ifdown(dev, event != NETDEV_DOWN); } break; case NETDEV_CHANGEMTU: if (idev && dev->mtu >= IPV6_MIN_MTU) { rt6_mtu_change(dev, dev->mtu); idev->cnf.mtu6 = dev->mtu; break; } if (!idev && dev->mtu >= IPV6_MIN_MTU) { idev = ipv6_add_dev(dev); if (idev) break; } /* MTU falled under IPV6_MIN_MTU. Stop IPv6 on this interface. */ case NETDEV_DOWN: case NETDEV_UNREGISTER: /* * Remove all addresses from this interface. */ addrconf_ifdown(dev, event != NETDEV_DOWN); break; case NETDEV_CHANGENAME: if (idev) { snmp6_unregister_dev(idev);#ifdef CONFIG_SYSCTL addrconf_sysctl_unregister(&idev->cnf); neigh_sysctl_unregister(idev->nd_parms); neigh_sysctl_register(dev, idev->nd_parms, NET_IPV6, NET_IPV6_NEIGH, "ipv6", &ndisc_ifinfo_sysctl_change, NULL); addrconf_sysctl_register(idev, &idev->cnf);#endif err = snmp6_register_dev(idev); if (err) return notifier_from_errno(err); } break; } return NOTIFY_OK;}/* * addrconf module should be notified of a device going up */static struct notifier_block ipv6_dev_notf = { .notifier_call = addrconf_notify, .priority = 0};static int addrconf_ifdown(struct net_device *dev, int how){ struct inet6_dev *idev; struct inet6_ifaddr *ifa, **bifa; int i; ASSERT_RTNL(); if (dev == init_net.loopback_dev && how == 1) how = 0; rt6_ifdown(dev); neigh_ifdown(&nd_tbl, dev); idev = __in6_dev_get(dev); if (idev == NULL) return -ENODEV; /* Step 1: remove reference to ipv6 device from parent device. Do not dev_put! */ if (how == 1) { idev->dead = 1; /* protected by rtnl_lock */ rcu_assign_pointer(dev->ip6_ptr, NULL); /* Step 1.5: remove snmp6 entry */ snmp6_unregister_dev(idev); } /* Step 2: clear hash table */ for (i=0; i<IN6_ADDR_HSIZE; i++) { bifa = &inet6_addr_lst[i]; write_lock_bh(&addrconf_hash_lock); while ((ifa = *bifa) != NULL) { if (ifa->idev == idev) { *bifa = ifa->lst_next; ifa->lst_next = NULL; addrconf_del_timer(ifa); in6_ifa_put(ifa); continue; } bifa = &ifa->lst_next; } write_unlock_bh(&addrconf_hash_lock); } write_lock_bh(&idev->lock); /* Step 3: clear flags for stateless addrconf */ if (how != 1) idev->if_flags &= ~(IF_RS_SENT|IF_RA_RCVD|IF_READY); /* Step 4: clear address list */#ifdef CONFIG_IPV6_PRIVACY if (how == 1 && del_timer(&idev->regen_timer)) in6_dev_put(idev); /* clear tempaddr list */ while ((ifa = idev->tempaddr_list) != NULL) { idev->tempaddr_list = ifa->tmp_next; ifa->tmp_next = NULL; ifa->dead = 1; write_unlock_bh(&idev->lock); spin_lock_bh(&ifa->lock); if (ifa->ifpub) { in6_ifa_put(ifa->ifpub); ifa->ifpub = NULL; } spin_unlock_bh(&ifa->lock); in6_ifa_put(ifa); write_lock_bh(&idev->lock); }#endif while ((ifa = idev->addr_list) != NULL) { idev->addr_list = ifa->if_next; ifa->if_next = NULL; ifa->dead = 1; addrconf_del_timer(ifa); write_unlock_bh(&idev->lock); __ipv6_ifa_notify(RTM_DELADDR, ifa); atomic_notifier_call_chain(&inet6addr_chain, NETDEV_DOWN, ifa); in6_ifa_put(ifa); write_lock_bh(&idev->lock); } write_unlock_bh(&idev->lock); /* Step 5: Discard multicast list */ if (how == 1) ipv6_mc_destroy_dev(idev); else ipv6_mc_down(idev); idev->tstamp = jiffies; /* Shot the device (if unregistered) */ if (how == 1) {#ifdef CONFIG_SYSCTL addrconf_sysctl_unregister(&idev->cnf); neigh_sysctl_unregister(idev->nd_parms);#endif neigh_parms_release(&nd_tbl, idev->nd_parms); neigh_ifdown(&nd_tbl, dev); in6_dev_put(idev); } return 0;}static void addrconf_rs_timer(unsigned long data){ struct inet6_ifaddr *ifp = (struct inet6_ifaddr *) data; if (ifp->idev->cnf.forwarding) goto out; if (ifp->idev->if_flags & IF_RA_RCVD) { /* * Announcement received after solicitation * was sent */ goto out; } spin_lock(&ifp->lock); if (ifp->probes++ < ifp->idev->cnf.rtr_solicits) { struct in6_addr all_routers; /* The wait after the last probe can be shorter */ addrconf_mod_timer(ifp, AC_RS, (ifp->probes == ifp->idev->cnf.rtr_solicits) ? ifp->idev->cnf.rtr_solicit_delay : ifp->idev->cnf.rtr_solicit_interval); spin_unlock(&ifp->lock); ipv6_addr_all_routers(&all_routers); ndisc_send_rs(ifp->idev->dev, &ifp->addr, &all_routers); } else { spin_unlock(&ifp->lock); /* * Note: we do not support deprecated "all on-link" * assumption any longer. */ printk(KERN_DEBUG "%s: no IPv6 routers present\n", ifp->idev->dev->name); }out: in6_ifa_put(ifp);}/* * Duplicate Address Detection */static void addrconf_dad_kick(struct inet6_ifaddr *ifp){ unsigned long rand_num; struct inet6_dev *idev = ifp->idev; if (ifp->flags & IFA_F_OPTIMISTIC) rand_num = 0; else rand_num = net_random() % (idev->cnf.rtr_solicit_delay ? : 1); ifp->probes = idev->cnf.dad_transmits; addrconf_mod_timer(ifp, AC_DAD, rand_num);}static void addrconf_dad_start(struct inet6_ifaddr *ifp, u32 flags){ struct inet6_dev *idev = ifp->idev; struct net_device *dev = idev->dev; addrconf_join_solict(dev, &ifp->addr); net_srandom(ifp->addr.s6_addr32[3]); read_lock_bh(&idev->lock); if (ifp->dead) goto out; spin_lock_bh(&ifp->lock); if (dev->flags&(IFF_NOARP|IFF_LOOPBACK) || !(ifp->flags&IFA_F_TENTATIVE) || ifp->flags & IFA_F_NODAD) { ifp->flags &= ~(IFA_F_TENTATIVE|IFA_F_OPTIMISTIC); spin_unlock_bh(&ifp->lock); read_unlock_bh(&idev->lock); addrconf_dad_completed(ifp); return; } if (!(idev->if_flags & IF_READY)) { spin_unlock_bh(&ifp->lock); read_unlock_bh(&idev->lock); /* * If the defice is not ready: * - keep it tentative if it is a permanent address. * - otherwise, kill it. */ in6_ifa_hold(ifp); addrconf_dad_stop(ifp); return; } /* * Optimistic nodes can start receiving * Frames right away */ if(ifp->flags & IFA_F_OPTIMISTIC) ip6_ins_rt(ifp->rt); addrconf_dad_kick(ifp); spin_unlock_bh(&ifp->lock);out: read_unlock_bh(&idev->lock);}static void addrconf_dad_timer(unsigned long data){ struct inet6_ifaddr *ifp = (struct inet6_ifaddr *) data; struct inet6_dev *idev = ifp->idev; struct in6_addr unspec; struct in6_addr mcaddr; read_lock_bh(&idev->lock); if (idev->dead) { read_unlock_bh(&idev->lock); goto out; } spin_lock_bh(&ifp->lock); if (ifp->probes == 0) { /* * DAD was successful */ ifp->flags &= ~(IFA_F_TENTATIVE|IFA_F_OPTIMISTIC); spin_unlock_bh(&ifp->lock); read_unlock_bh(&idev->lock); addrconf_dad_completed(ifp); goto out; } ifp->probes--; addrconf_mod_timer(ifp, AC_DAD, ifp->idev->nd_parms->retrans_time); spin_unlock_bh(&ifp->lock); read_unlock_bh(&idev->lock); /* send a neighbour solicitation for our addr */ memset(&unspec, 0, sizeof(unspec)); addrconf_addr_solict_mult(&ifp->addr, &mcaddr); ndisc_send_ns(ifp->idev->dev, NULL, &ifp->addr, &mcaddr, &unspec);out: in6_ifa_put(ifp);}static void addrconf_dad_completed(struct inet6_ifaddr *ifp){ struct net_device * dev = ifp->idev->dev; /* * Configure the address for reception. Now it is valid. */ ipv6_ifa_notify(RTM_NEWADDR, ifp); /* If added prefix is link local and forwarding is off, start sending router solicitations. */ if (ifp->idev->cnf.forwarding == 0 && ifp->idev->cnf.rtr_solicits > 0 && (dev->flags&IFF_LOOPBACK) == 0 && (
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -