📄 addrconf.c
字号:
}static void addrconf_add_lroute(struct device *dev){ struct in6_addr addr; ipv6_addr_set(&addr, __constant_htonl(0xFE800000), 0, 0, 0); addrconf_prefix_route(&addr, 10, dev, 0, RTF_ADDRCONF);}static struct inet6_dev *addrconf_add_dev(struct device *dev){ struct inet6_dev *idev; 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 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 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); } if (ifp && valid_lft == 0) { ipv6_del_addr(ifp); ifp = NULL; } if (ifp) { int event = 0; 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); } }}/* * Set destination address. * Special case for SIT interfaces where we create a new "virtual" * device. */int addrconf_set_dstaddr(void *arg){ struct in6_ifreq ireq; struct 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*)&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(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 device *dev; int scope; 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); addrconf_lock(); if ((ifp = ipv6_add_addr(idev, pfx, scope)) != NULL) { ifp->prefix_len = plen; ifp->flags |= ADDR_PERMANENT; addrconf_dad_start(ifp); addrconf_unlock(); return 0; } addrconf_unlock(); return -ENOBUFS;}static int inet6_addr_del(int ifindex, struct in6_addr *pfx, int plen){ struct inet6_ifaddr *ifp; struct inet6_dev *idev; struct device *dev; if ((dev = dev_get_by_index(ifindex)) == NULL) return -ENODEV; if ((idev = ipv6_get_idev(dev)) == NULL) return -ENXIO; start_bh_atomic(); for (ifp = idev->addr_list; ifp; ifp=ifp->if_next) { if (ifp->prefix_len == plen && (!memcmp(pfx, &ifp->addr, sizeof(struct in6_addr)))) { ipv6_del_addr(ifp); end_bh_atomic(); /* If the last address is deleted administratively, disable IPv6 on this interface. */ if (idev->addr_list == NULL) addrconf_ifdown(idev->dev, 1); return 0; } } end_bh_atomic(); return -EADDRNOTAVAIL;}int addrconf_add_ifaddr(void *arg){ struct in6_ifreq ireq; int err; if (!capable(CAP_NET_ADMIN)) return -EPERM; if (copy_from_user(&ireq, arg, sizeof(struct in6_ifreq))) return -EFAULT; rtnl_lock(); err = inet6_addr_add(ireq.ifr6_ifindex, &ireq.ifr6_addr, ireq.ifr6_prefixlen); rtnl_unlock(); return err;}int addrconf_del_ifaddr(void *arg){ struct in6_ifreq ireq; int err; if (!capable(CAP_NET_ADMIN)) return -EPERM; if (copy_from_user(&ireq, arg, sizeof(struct in6_ifreq))) return -EFAULT; rtnl_lock(); err = inet6_addr_del(ireq.ifr6_ifindex, &ireq.ifr6_addr, ireq.ifr6_prefixlen); rtnl_unlock(); return err;}static void sit_add_v4_addrs(struct inet6_dev *idev){ struct inet6_ifaddr * ifp; struct in6_addr addr; struct device *dev; int scope; memset(&addr, 0, sizeof(struct in6_addr)); memcpy(&addr.s6_addr32[3], idev->dev->dev_addr, 4); if (idev->dev->flags&IFF_POINTOPOINT) { addr.s6_addr32[0] = __constant_htonl(0xfe800000); scope = IFA_LINK; } else { scope = IPV6_ADDR_COMPATv4; } if (addr.s6_addr32[3]) { addrconf_lock(); ifp = ipv6_add_addr(idev, &addr, scope); if (ifp) { ifp->flags |= ADDR_PERMANENT; ifp->prefix_len = 128; ipv6_ifa_notify(RTM_NEWADDR, ifp); } addrconf_unlock(); return; } for (dev = dev_base; dev != NULL; dev = dev->next) { if (dev->ip_ptr && (dev->flags & IFF_UP)) { struct in_device * in_dev = dev->ip_ptr; struct in_ifaddr * ifa; int flag = scope; for (ifa = in_dev->ifa_list; ifa; ifa = ifa->ifa_next) { addr.s6_addr32[3] = ifa->ifa_local; if (ifa->ifa_scope == RT_SCOPE_LINK) continue; if (ifa->ifa_scope >= RT_SCOPE_HOST) { if (idev->dev->flags&IFF_POINTOPOINT) continue; flag |= IFA_HOST; } addrconf_lock(); ifp = ipv6_add_addr(idev, &addr, flag); if (ifp) { if (idev->dev->flags&IFF_POINTOPOINT) ifp->prefix_len = 10; else ifp->prefix_len = 96; ifp->flags |= ADDR_PERMANENT; ipv6_ifa_notify(RTM_NEWADDR, ifp); } addrconf_unlock(); } } }}static void init_loopback(struct device *dev){ struct in6_addr addr; struct inet6_dev *idev; struct inet6_ifaddr * ifp; /* ::1 */ memset(&addr, 0, sizeof(struct in6_addr)); addr.s6_addr[15] = 1; if ((idev = ipv6_find_idev(dev)) == NULL) { printk(KERN_DEBUG "init loopback: add_dev failed\n"); return; } addrconf_lock(); ifp = ipv6_add_addr(idev, &addr, IFA_HOST); if (ifp) { ifp->flags |= ADDR_PERMANENT; ifp->prefix_len = 128; ipv6_ifa_notify(RTM_NEWADDR, ifp); } addrconf_unlock();}static void addrconf_add_linklocal(struct inet6_dev *idev, struct in6_addr *addr){ struct inet6_ifaddr * ifp; addrconf_lock(); ifp = ipv6_add_addr(idev, addr, IFA_LINK); if (ifp) { ifp->flags = ADDR_PERMANENT; ifp->prefix_len = 10; addrconf_dad_start(ifp); } addrconf_unlock();}static void addrconf_dev_config(struct device *dev){ struct in6_addr addr; struct inet6_dev * idev; if (dev->type != ARPHRD_ETHER) { /* Alas, we support only Ethernet autoconfiguration. */ return; } idev = addrconf_add_dev(dev); if (idev == NULL) return;#ifdef CONFIG_IPV6_EUI64 memset(&addr, 0, sizeof(struct in6_addr)); addr.s6_addr[0] = 0xFE; addr.s6_addr[1] = 0x80; if (ipv6_generate_eui64(addr.s6_addr + 8, dev) == 0) addrconf_add_linklocal(idev, &addr);#endif#ifndef CONFIG_IPV6_NO_PB memset(&addr, 0, sizeof(struct in6_addr)); addr.s6_addr[0] = 0xFE; addr.s6_addr[1] = 0x80; memcpy(addr.s6_addr + (sizeof(struct in6_addr) - dev->addr_len), dev->dev_addr, dev->addr_len); addrconf_add_linklocal(idev, &addr);#endif}static void addrconf_sit_config(struct device *dev){ struct inet6_dev *idev; /* * 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);}int addrconf_notify(struct notifier_block *this, unsigned long event, void * data){ struct device *dev; dev = (struct device *) data; switch(event) { case NETDEV_UP: switch(dev->type) { case ARPHRD_SIT: addrconf_sit_config(dev); break; case ARPHRD_LOOPBACK: init_loopback(dev); break; default: addrconf_dev_config(dev); break; };#ifdef CONFIG_IPV6_NETLINK rt6_sndmsg(RTMSG_NEWDEVICE, NULL, NULL, NULL, dev, 0, 0, 0, 0);#endif break; case NETDEV_CHANGEMTU: if (dev->mtu >= IPV6_MIN_MTU) { struct inet6_dev *idev; if ((idev = ipv6_get_idev(dev)) == NULL) break; idev->cnf.mtu6 = dev->mtu; rt6_mtu_change(dev, dev->mtu); break; } /* MTU falled under IPV6_MIN_MTU. Stop IPv6 on this interface. */ case NETDEV_DOWN: case NETDEV_UNREGISTER: /* * Remove all addresses from this interface. */ if (addrconf_ifdown(dev, event != NETDEV_DOWN) == 0) {#ifdef CONFIG_IPV6_NETLINK rt6_sndmsg(RTMSG_DELDEVICE, NULL, NULL, NULL, dev, 0, 0, 0, 0);#endif } break; case NETDEV_CHANGE: break; }; return NOTIFY_OK;}static int addrconf_ifdown(struct device *dev, int how){ struct inet6_dev *idev, **bidev; struct inet6_ifaddr *ifa, **bifa; int i, hash; rt6_ifdown(dev); neigh_ifdown(&nd_tbl, dev); idev = ipv6_get_idev(dev); if (idev == NULL) return -ENODEV; start_bh_atomic(); /* Discard address list */ idev->addr_list = NULL; /* * Clean addresses hash table */ for (i=0; i<16; i++) { bifa = &inet6_addr_lst[i]; while ((ifa = *bifa) != NULL) { if (ifa->idev == idev) { *bifa = ifa->lst_next; del_timer(&ifa->timer); ipv6_ifa_notify(RTM_DELADDR, ifa); kfree(ifa); continue; } bifa = &ifa->lst_next; } } /* Discard multicast list */ if (how == 1) ipv6_mc_destroy_dev(idev); else ipv6_mc_down(idev); /* Delete device from device hash table (if unregistered) */ if (how == 1) { hash = ipv6_devindex_hash(dev->ifindex); for (bidev = &inet6_dev_lst[hash]; (idev=*bidev) != NULL; bidev = &idev->next) { if (idev->dev == dev) { *bidev = idev->next; neigh_parms_release(&nd_tbl, idev->nd_parms);#ifdef CONFIG_SYSCTL addrconf_sysctl_unregister(&idev->cnf);#endif kfree(idev); break; } } } end_bh_atomic(); return 0;}static void addrconf_rs_timer(unsigned long data){ struct inet6_ifaddr *ifp; ifp = (struct inet6_ifaddr *) data; if (ifp->idev->cnf.forwarding) return; if (ifp->idev->if_flags & IF_RA_RCVD) { /* * Announcement received after solicitation * was sent */ return; } if (ifp->probes++ <= ifp->idev->cnf.rtr_solicits) { struct in6_addr all_routers; ipv6_addr_all_routers(&all_routers); ndisc_send_rs(ifp->idev->dev, &ifp->addr, &all_routers); ifp->timer.function = addrconf_rs_timer; ifp->timer.expires = (jiffies + ifp->idev->cnf.rtr_solicit_interval); add_timer(&ifp->timer); } else { struct in6_rtmsg rtmsg; printk(KERN_DEBUG "%s: no IPv6 routers present\n", ifp->idev->dev->name); memset(&rtmsg, 0, sizeof(struct in6_rtmsg)); rtmsg.rtmsg_type = RTMSG_NEWROUTE; rtmsg.rtmsg_metric = IP6_RT_PRIO_ADDRCONF; rtmsg.rtmsg_flags = (RTF_ALLONLINK | RTF_ADDRCONF | RTF_DEFAULT | RTF_UP);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -