📄 in6.c
字号:
#endif
#ifdef MEASURE_PERFORMANCE
int new_ifa = 0;
#endif
struct rtentry *rt;
/* Validate parameters */
if (ifp == NULL || ifra == NULL) /* this maybe redundant */
return(EINVAL);
/*
* The destination address for a p2p link must have a family
* of AF_UNSPEC or AF_INET6.
*/
if ((ifp->if_flags & IFF_POINTOPOINT) != 0 &&
ifra->ifra_dstaddr.sin6_family != AF_INET6 &&
ifra->ifra_dstaddr.sin6_family != AF_UNSPEC)
return(EAFNOSUPPORT);
/*
* validate ifra_prefixmask. don't check sin6_family, netmask
* does not carry fields other than sin6_len.
*/
if (ifra->ifra_prefixmask.sin6_len > sizeof(struct sockaddr_in6))
return(EINVAL);
/*
* Because the IPv6 address architecture is classless, we require
* users to specify a (non 0) prefix length (mask) for a new address.
* We also require the prefix (when specified) mask is valid, and thus
* reject a non-consecutive mask.
*/
if (ia == NULL && ifra->ifra_prefixmask.sin6_len == 0)
return(EINVAL);
if (ifra->ifra_prefixmask.sin6_len != 0) {
plen = in6_mask2len(&ifra->ifra_prefixmask.sin6_addr,
(u_char *)&ifra->ifra_prefixmask +
ifra->ifra_prefixmask.sin6_len);
if (plen <= 0)
return(EINVAL);
}
else {
/*
* In this case, ia must not be NULL. We just use its prefix
* length.
*/
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);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -