📄 in6.c
字号:
plen = in6_mask2len(&ia->ia_prefixmask.sin6_addr, NULL); } /* * If the destination address on a p2p interface is specified, * and the address is a scoped one, validate/set the scope * zone identifier. */ dst6 = ifra->ifra_dstaddr; if ((ifp->if_flags & (IFF_POINTOPOINT|IFF_LOOPBACK)) && (dst6.sin6_family == AF_INET6)) { int64_t zoneid;#ifndef SCOPEDROUTING if ((error = in6_recoverscope(&dst6, &ifra->ifra_dstaddr.sin6_addr, ifp)) != 0) return(error);#endif if ((zoneid = in6_addr2zoneid(ifp, &dst6.sin6_addr)) < 0) return(EINVAL); if (dst6.sin6_scope_id == 0) /* user omit to specify the ID. */ dst6.sin6_scope_id = zoneid; else if (dst6.sin6_scope_id != zoneid) return(EINVAL); /* scope ID mismatch. */ if ((error = in6_embedscope(&dst6.sin6_addr, &dst6)) != 0) return(error);#ifndef SCOPEDROUTING dst6.sin6_scope_id = 0; /* XXX */#endif } /* * The destination address can be specified only for a p2p or a * loopback interface. If specified, the corresponding prefix length * must be 128. */ if (ifra->ifra_dstaddr.sin6_family == AF_INET6) {#ifdef FORCE_P2PPLEN int i;#endif if ((ifp->if_flags & (IFF_POINTOPOINT|IFF_LOOPBACK)) == 0) { /* XXX: noisy message */ log(LOG_INFO, "in6_update_ifa: a destination can be " "specified for a p2p or a loopback IF only\n"); return(EINVAL); } if (plen != 128) { log(LOG_INFO, "in6_update_ifa: prefixlen should be " "128 when dstaddr is specified\n");#ifdef FORCE_P2PPLEN /* * To be compatible with old configurations, * such as ifconfig gif0 inet6 2001::1 2001::2 * prefixlen 126, we override the specified * prefixmask as if the prefix length was 128. */ ifra->ifra_prefixmask.sin6_len = sizeof(struct sockaddr_in6); for (i = 0; i < 4; i++) ifra->ifra_prefixmask.sin6_addr.s6_addr32[i] = 0xffffffff; plen = 128;#else return(EINVAL);#endif } } /* lifetime consistency check */ lt = &ifra->ifra_lifetime; if (lt->ia6t_pltime > lt->ia6t_vltime) return(EINVAL); if (lt->ia6t_vltime == 0) { /* * the following log might be noisy, but this is a typical * configuration mistake or a tool's bug. */ log(LOG_INFO, "in6_update_ifa: valid lifetime is 0 for %s\n", ip6_sprintf(&ifra->ifra_addr.sin6_addr)); if (ia == NULL) return(0); /* there's nothing to do */ } /* * If this is a new address, allocate a new ifaddr and link it * into chains. */ if (ia == NULL) { hostIsNew = 1; /* * When in6_update_ifa() is called in a process of a received * RA, it is called under an interrupt context. So, we should * call malloc with M_NOWAIT. */ ia = (struct in6_ifaddr *) malloc(sizeof(*ia), M_IFADDR, M_NOWAIT); if (ia == NULL) return (ENOBUFS); bzero((caddr_t)ia, sizeof(*ia)); LIST_INIT(&ia->ia6_memberships); /* Initialize the address and masks, and put time stamp */ ia->ia_ifa.ifa_addr = (struct sockaddr *)&ia->ia_addr; ia->ia_addr.sin6_family = AF_INET6; ia->ia_addr.sin6_len = sizeof(ia->ia_addr); ia->ia6_createtime = ia->ia6_updatetime = time_second; if ((ifp->if_flags & (IFF_POINTOPOINT | IFF_LOOPBACK)) != 0) { /* * XXX: some functions expect that ifa_dstaddr is not * NULL for p2p interfaces. */ ia->ia_ifa.ifa_dstaddr = (struct sockaddr *)&ia->ia_dstaddr; } else { ia->ia_ifa.ifa_dstaddr = NULL; } ia->ia_ifa.ifa_netmask = (struct sockaddr *)&ia->ia_prefixmask; ia->ia_ifp = ifp; if ((oia = in6_ifaddr) != NULL) { for ( ; oia->ia_next; oia = oia->ia_next) continue; oia->ia_next = ia; } else in6_ifaddr = ia;#ifdef __NetBSD__ /* gain a refcnt for the link from in6_ifaddr */ IFAREF(&ia->ia_ifa);#endif#if defined(__bsdi__) || (defined(__FreeBSD__) && __FreeBSD__ < 3) if ((ifa = ifp->if_addrlist) != NULL) { for ( ; ifa->ifa_next; ifa = ifa->ifa_next) continue; ifa->ifa_next = ia62ifa(ia); } else ifp->if_addrlist = ia62ifa(ia);#else TAILQ_INSERT_TAIL(&ifp->if_addrlist, &ia->ia_ifa, ifa_list); log_(LOG_ADDR) { diag_printf("%s.%d - After inserting %p into list %p\n", __FUNCTION__, __LINE__, &ia->ia_ifa, &ifp->if_addrlist); _show_ifp(ifp); }#endif#ifdef __NetBSD__ /* gain another refcnt for the link from if_addrlist */ IFAREF(&ia->ia_ifa);#endif#ifdef MEASURE_PERFORMANCE new_ifa = 1;#endif } /* set prefix mask */ if (ifra->ifra_prefixmask.sin6_len) { /* * We prohibit changing the prefix length of an existing * address, because * + such an operation should be rare in IPv6, and * + the operation would confuse prefix management. */ if (ia->ia_prefixmask.sin6_len && in6_mask2len(&ia->ia_prefixmask.sin6_addr, NULL) != plen) { log(LOG_INFO, "in6_update_ifa: the prefix length of an" " existing (%s) address should not be changed\n", ip6_sprintf(&ia->ia_addr.sin6_addr)); error = EINVAL; goto unlink; } ia->ia_prefixmask = ifra->ifra_prefixmask; } /* * If a new destination address is specified, scrub the old one and * install the new destination. Note that the interface must be * p2p or loopback (see the check above.) */ if (dst6.sin6_family == AF_INET6 && !IN6_ARE_ADDR_EQUAL(&dst6.sin6_addr, &ia->ia_dstaddr.sin6_addr)) { int e; if ((ia->ia_flags & IFA_ROUTE) != 0 && (e = rtinit(&(ia->ia_ifa), (int)RTM_DELETE, RTF_HOST)) != 0) { log(LOG_ERR, "in6_update_ifa: failed to remove " "a route to the old destination: %s\n", ip6_sprintf(&ia->ia_addr.sin6_addr)); /* proceed anyway... */ } else ia->ia_flags &= ~IFA_ROUTE; ia->ia_dstaddr = dst6; } /* * Set lifetimes. We do not refer to ia6t_expire and ia6t_preferred * to see if the address is deprecated or invalidated, but initialize * these members for applications. */ ia->ia6_lifetime = ifra->ifra_lifetime; if (ia->ia6_lifetime.ia6t_vltime != ND6_INFINITE_LIFETIME) { ia->ia6_lifetime.ia6t_expire = time_second + ia->ia6_lifetime.ia6t_vltime; } else ia->ia6_lifetime.ia6t_expire = 0; if (ia->ia6_lifetime.ia6t_pltime != ND6_INFINITE_LIFETIME) { ia->ia6_lifetime.ia6t_preferred = time_second + ia->ia6_lifetime.ia6t_pltime; } else ia->ia6_lifetime.ia6t_preferred = 0; /* reset the interface and routing table appropriately. */ if ((error = in6_ifinit(ifp, ia, &ifra->ifra_addr, hostIsNew)) != 0) goto unlink; /* * Make the address tentative before joining multicast addresses, * so that corresponding MLD responses would not have a tentative * source address. */ ia->ia6_flags = ifra->ifra_flags; ia->ia6_flags &= ~IN6_IFF_DUPLICATED; /* safety */#ifdef MIP6 if (hostIsNew && in6if_do_dad(ifp) && mip6_ifa_need_dad(ia))#else /* MIP6 */ if (hostIsNew && in6if_do_dad(ifp))#endif /* MIP6 */ ia->ia6_flags |= IN6_IFF_TENTATIVE; /* * Beyond this point, we should call in6_purgeaddr upon an error, * not just go to unlink. */ if ((ifp->if_flags & IFF_MULTICAST) != 0) { struct sockaddr_in6 mltaddr, mltmask; if (hostIsNew) { /* * join solicited multicast addr for new host id */ struct in6_addr llsol; bzero(&llsol, sizeof(struct in6_addr)); llsol.s6_addr16[0] = htons(0xff02); llsol.s6_addr16[1] = htons(ifp->if_index); llsol.s6_addr32[1] = 0; llsol.s6_addr32[2] = htonl(1); llsol.s6_addr32[3] = ifra->ifra_addr.sin6_addr.s6_addr32[3]; llsol.s6_addr8[12] = 0xff; imm = in6_joingroup(ifp, &llsol, &error); if (imm) { LIST_INSERT_HEAD(&ia->ia6_memberships, imm, i6mm_chain); } else { log(LOG_ERR, "in6_update_ifa: addmulti failed for " "%s on %s (errno=%d)\n", ip6_sprintf(&llsol), if_name(ifp), error); goto cleanup; } } bzero(&mltmask, sizeof(mltmask)); mltmask.sin6_len = sizeof(struct sockaddr_in6); mltmask.sin6_family = AF_INET6; mltmask.sin6_addr = in6mask32; /* * join link-local all-nodes address */ bzero(&mltaddr, sizeof(mltaddr)); mltaddr.sin6_len = sizeof(struct sockaddr_in6); mltaddr.sin6_family = AF_INET6; mltaddr.sin6_addr = in6addr_linklocal_allnodes; mltaddr.sin6_addr.s6_addr16[1] = htons(ifp->if_index);#ifdef __FreeBSD__ rt = rtalloc1((struct sockaddr *)&mltaddr, 0, 0UL);#else rt = rtalloc1((struct sockaddr *)&mltaddr, 0);#endif if (rt) { /* 32bit came from "mltmask" */ if (memcmp(&mltaddr.sin6_addr, &((struct sockaddr_in6 *)rt_key(rt))->sin6_addr, 32 / 8)) { RTFREE(rt); rt = NULL; } } if (!rt) {#if (defined(__bsdi__) && _BSDI_VERSION >= 199802) struct rt_addrinfo info; bzero(&info, sizeof(info)); info.rti_info[RTAX_DST] = (struct sockaddr *)&mltaddr; info.rti_info[RTAX_GATEWAY] = (struct sockaddr *)&ia->ia_addr; info.rti_info[RTAX_NETMASK] = (struct sockaddr *)&mltmask; info.rti_info[RTAX_IFA] = (struct sockaddr *)&ia->ia_addr; /* XXX: we need RTF_CLONING to fake nd6_rtrequest */ info.rti_flags = RTF_UP | RTF_CLONING; error = rtrequest1(RTM_ADD, &info, NULL);#else error = rtrequest(RTM_ADD, (struct sockaddr *)&mltaddr, (struct sockaddr *)&ia->ia_addr, (struct sockaddr *)&mltmask, RTF_UP | RTF_CLONING, (struct rtentry **)0);#endif if (error) goto cleanup; } else { RTFREE(rt); } imm = in6_joingroup(ifp, &mltaddr.sin6_addr, &error); if (imm) { LIST_INSERT_HEAD(&ia->ia6_memberships, imm, i6mm_chain); } else { log(LOG_WARNING, "in6_update_ifa: addmulti failed for " "%s on %s (errno=%d)\n", ip6_sprintf(&mltaddr.sin6_addr), if_name(ifp), error); goto cleanup; } /* * join node information group address */#ifdef __FreeBSD__#define hostnamelen strlen(hostname)#endif if (in6_nigroup(ifp, hostname, hostnamelen, &mltaddr.sin6_addr) == 0) { imm = in6_joingroup(ifp, &mltaddr.sin6_addr, &error); if (imm) { LIST_INSERT_HEAD(&ia->ia6_memberships, imm, i6mm_chain); } else { log(LOG_WARNING, "in6_update_ifa: " "addmulti failed for " "%s on %s (errno=%d)\n", ip6_sprintf(&mltaddr.sin6_addr), if_name(ifp), error); /* XXX not very fatal, go on... */ } }#ifdef __FreeBSD__#undef hostnamelen#endif /* * join interface-local all-nodes address, on loopback. * (ff01::1%ifN, and ff01::%ifN/32) */ mltaddr.sin6_addr = in6addr_nodelocal_allnodes; mltaddr.sin6_addr.s6_addr16[1] = htons(ifp->if_index);#ifdef __FreeBSD__ rt = rtalloc1((struct sockaddr *)&mltaddr, 0, 0UL);#else rt = rtalloc1((struct sockaddr *)&mltaddr, 0);#endif if (rt) { /* 32bit came from "mltmask" */ if (memcmp(&mltaddr.sin6_addr, &((struct sockaddr_in6 *)rt_key(rt))->sin6_addr, 32 / 8)) { RTFREE(rt); rt = NULL; } } if (!rt) {#if (defined(__bsdi__) && _BSDI_VERSION >= 199802) struct rt_addrinfo info; bzero(&info, sizeof(info)); info.rti_info[RTAX_DST] = (struct sockaddr *)&mltaddr; info.rti_info[RTAX_GATEWAY] = (struct sockaddr *)&ia->ia_addr; info.rti_info[RTAX_NETMASK] = (struct sockaddr *)&mltmask; info.rti_info[RTAX_IFA] = (struct sockaddr *)&ia->ia_addr; info.rti_flags = RTF_UP | RTF_CLONING; error = rtrequest1(RTM_ADD, &info, NULL);#else error = rtrequest(RTM_ADD, (struct sockaddr *)&mltaddr, (struct sockaddr *)&ia->ia_addr, (struct sockaddr *)&mltmask, RTF_UP | RTF_CLONING, (struct rtentry **)0);#endif if (error) goto cleanup; } else { RTFREE(rt); } imm = in6_joingroup(ifp, &mltaddr.sin6_addr, &error); if (imm) { LIST_INSERT_HEAD(&ia->ia6_memberships, imm, i6mm_chain); } else { log(LOG_WARNING, "in6_update_ifa: " "addmulti failed for %s on %s " "(errno=%d)\n", ip6_sprintf(&mltaddr.sin6_addr), if_name(ifp), error); goto cleanup; } }#ifdef MEASURE_PERFORMANCE { int s = splnet(); if (new_ifa) in6h_addifa(ia); else in6h_rebuild(0); splx(s); }#endif /* * make sure to initialize ND6 information. this is to workaround * issues with interfaces with IPv6 addresses, which have never brought * up. We are assuming that it is safe to nd6_ifattach multiple times. */ nd6_ifattach(ifp); /* * Perform DAD, if needed. * XXX It may be of use, if we can administratively * disable DAD. */#ifdef MIP6 if (hostIsNew && in6if_do_dad(ifp) && mip6_ifa_need_dad(ia) && (ifra->ifra_flags & IN6_IFF_NODAD) == 0)#else /* MIP6 */ if (hostIsNew && in6if_do_dad(ifp) && (ifra->ifra_flags & IN6_IFF_NODAD) == 0)#endif /* MIP6 */ { nd6_dad_start((struct ifaddr *)ia, NULL); } return(error); unlink: /* * XXX: if a change of an existing address failed, keep the entry * anyway. */ if (hostIsNew) in6_unlink_ifa(ia, ifp); return(error); cleanup: in6_purgeaddr(&ia->ia_ifa); return error;}voidin6_purgeaddr(ifa) struct ifaddr *ifa;{ struct ifnet *ifp = ifa->ifa_ifp; struct in6_ifaddr *ia = (struct in6_ifaddr *) ifa;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -