Changeset View
Changeset View
Standalone View
Standalone View
sys/netinet6/in6.c
Show First 20 Lines • Show All 145 Lines • ▼ Show 20 Lines | |||||
const struct in6_addr in6mask96 = IN6MASK96; | const struct in6_addr in6mask96 = IN6MASK96; | ||||
const struct in6_addr in6mask128 = IN6MASK128; | const struct in6_addr in6mask128 = IN6MASK128; | ||||
const struct sockaddr_in6 sa6_any = | const struct sockaddr_in6 sa6_any = | ||||
{ sizeof(sa6_any), AF_INET6, 0, 0, IN6ADDR_ANY_INIT, 0 }; | { sizeof(sa6_any), AF_INET6, 0, 0, IN6ADDR_ANY_INIT, 0 }; | ||||
static int in6_notify_ifa(struct ifnet *, struct in6_ifaddr *, | static int in6_notify_ifa(struct ifnet *, struct in6_ifaddr *, | ||||
struct in6_aliasreq *, int); | struct in6_aliasreq *, int); | ||||
static int in6_ifaddprefix(struct in6_ifaddr *); | |||||
static int in6_ifremprefix(struct in6_ifaddr *); | |||||
static void in6_unlink_ifa(struct in6_ifaddr *, struct ifnet *); | static void in6_unlink_ifa(struct in6_ifaddr *, struct ifnet *); | ||||
static int in6_validate_ifra(struct ifnet *, struct in6_aliasreq *, | static int in6_validate_ifra(struct ifnet *, struct in6_aliasreq *, | ||||
struct in6_ifaddr *, int); | struct in6_ifaddr *, int); | ||||
static struct in6_ifaddr *in6_alloc_ifa(struct ifnet *, | static struct in6_ifaddr *in6_alloc_ifa(struct ifnet *, | ||||
struct in6_aliasreq *, int flags); | struct in6_aliasreq *, int flags); | ||||
static int in6_update_ifa_internal(struct ifnet *, struct in6_aliasreq *, | static int in6_update_ifa_internal(struct ifnet *, struct in6_aliasreq *, | ||||
struct in6_ifaddr *, int, int); | struct in6_ifaddr *, int, int); | ||||
static int in6_broadcast_ifa(struct ifnet *, struct in6_aliasreq *, | static int in6_broadcast_ifa(struct ifnet *, struct in6_aliasreq *, | ||||
struct in6_ifaddr *, int); | struct in6_ifaddr *, int); | ||||
static int in6_handle_rtrequest(int cmd, struct in6_ifaddr *ia, | |||||
int flags); | |||||
#define ifa2ia6(ifa) ((struct in6_ifaddr *)(ifa)) | #define ifa2ia6(ifa) ((struct in6_ifaddr *)(ifa)) | ||||
#define ia62ifa(ia6) (&((ia6)->ia_ifa)) | #define ia62ifa(ia6) (&((ia6)->ia_ifa)) | ||||
void | void | ||||
in6_newaddrmsg(struct in6_ifaddr *ia, int cmd) | in6_newaddrmsg(struct in6_ifaddr *ia, int cmd) | ||||
{ | { | ||||
struct rt_addrinfo info; | struct rt_addrinfo info; | ||||
Show All 21 Lines | if (cmd == RTM_ADD) { | ||||
rt_addrmsg(cmd, &ia->ia_ifa, fibnum); | rt_addrmsg(cmd, &ia->ia_ifa, fibnum); | ||||
rt_routemsg_info(cmd, &info, fibnum); | rt_routemsg_info(cmd, &info, fibnum); | ||||
} else if (cmd == RTM_DELETE) { | } else if (cmd == RTM_DELETE) { | ||||
rt_routemsg_info(cmd, &info, fibnum); | rt_routemsg_info(cmd, &info, fibnum); | ||||
rt_addrmsg(cmd, &ia->ia_ifa, fibnum); | rt_addrmsg(cmd, &ia->ia_ifa, fibnum); | ||||
} | } | ||||
} | } | ||||
/* Add prefix route for the network. */ | |||||
static int | |||||
in6_ifaddprefix(struct in6_ifaddr *ia) | |||||
{ | |||||
int error, flags = 0; | |||||
if (in6_mask2len(&ia->ia_prefixmask.sin6_addr, NULL) == 128) { | |||||
if (ia->ia_dstaddr.sin6_family != AF_INET6) | |||||
/* We don't need to install a host route. */ | |||||
return 0; | |||||
flags |= RTF_HOST; | |||||
} | |||||
if ((error = in6_handle_rtrequest(RTM_ADD, ia, flags)) == 0) | |||||
ia->ia_flags |= IFA_ROUTE; | |||||
else if (error == EEXIST) | |||||
/* Existance of the route is not an error. */ | |||||
error = 0; | |||||
return error; | |||||
} | |||||
/* | |||||
* Delete network prefix route if present. | |||||
* Re-add it to another address if the prefix matches. | |||||
*/ | |||||
static int | |||||
in6_ifremprefix(struct in6_ifaddr *target) | |||||
{ | |||||
int error, flags = 0; | |||||
struct rm_priotracker in6_ifa_tracker; | |||||
struct in6_ifaddr *ia; | |||||
if ((target->ia_flags & IFA_ROUTE) == 0) | |||||
return 0; | |||||
if (in6_mask2len(&target->ia_prefixmask.sin6_addr, NULL) == 128 && | |||||
target->ia_dstaddr.sin6_family == AF_INET6) | |||||
flags |= RTF_HOST; | |||||
IN6_IFADDR_RLOCK(&in6_ifa_tracker); | |||||
CK_STAILQ_FOREACH(ia, &V_in6_ifaddrhead, ia_link) { | |||||
if (target->ia_dstaddr.sin6_len) { | |||||
if (ia->ia_dstaddr.sin6_len == 0 || | |||||
!IN6_ARE_ADDR_EQUAL(&ia->ia_dstaddr.sin6_addr, | |||||
&target->ia_dstaddr.sin6_addr)) | |||||
continue; | |||||
} else { | |||||
if (!IN6_ARE_MASKED_ADDR_EQUAL(&ia->ia_addr.sin6_addr, | |||||
&target->ia_addr.sin6_addr, | |||||
&target->ia_prefixmask.sin6_addr)) | |||||
continue; | |||||
} | |||||
/* | |||||
* if we got a matching prefix route, move IFA_ROUTE to him | |||||
*/ | |||||
if ((ia->ia_flags & IFA_ROUTE) == 0) { | |||||
ifa_ref(&ia->ia_ifa); | |||||
IN6_IFADDR_RUNLOCK(&in6_ifa_tracker); | |||||
in6_handle_rtrequest(RTM_DELETE, target, flags); | |||||
target->ia_flags &= ~IFA_ROUTE; | |||||
error = in6_ifaddprefix(ia); | |||||
ifa_free(&ia->ia_ifa); | |||||
return error; | |||||
} | |||||
} | |||||
IN6_IFADDR_RUNLOCK(&in6_ifa_tracker); | |||||
/* | |||||
* noone seem to have prefix route. remove it. | |||||
*/ | |||||
in6_handle_rtrequest(RTM_DELETE, target, flags); | |||||
target->ia_flags &= ~IFA_ROUTE; | |||||
return 0; | |||||
} | |||||
int | int | ||||
in6_mask2len(struct in6_addr *mask, u_char *lim0) | in6_mask2len(struct in6_addr *mask, u_char *lim0) | ||||
{ | { | ||||
int x = 0, y; | int x = 0, y; | ||||
u_char *lim = lim0, *p; | u_char *lim = lim0, *p; | ||||
/* ignore the scope_id part */ | /* ignore the scope_id part */ | ||||
if (lim0 == NULL || lim0 - (u_char *)mask > sizeof(*mask)) | if (lim0 == NULL || lim0 - (u_char *)mask > sizeof(*mask)) | ||||
▲ Show 20 Lines • Show All 345 Lines • ▼ Show 20 Lines | if (ia->ia6_lifetime.ia6t_pltime != ND6_INFINITE_LIFETIME) { | ||||
ia->ia6_lifetime.ia6t_pltime; | ia->ia6_lifetime.ia6t_pltime; | ||||
} else | } else | ||||
retlt->ia6t_preferred = maxexpire; | retlt->ia6t_preferred = maxexpire; | ||||
} | } | ||||
break; | break; | ||||
case SIOCAIFADDR_IN6: | case SIOCAIFADDR_IN6: | ||||
{ | { | ||||
struct nd_prefixctl pr0; | |||||
struct nd_prefix *pr; | |||||
/* | /* | ||||
* first, make or update the interface address structure, | * make or update the interface address structure, | ||||
* and link it to the list. | * and link it to the list. | ||||
*/ | */ | ||||
if ((error = in6_update_ifa(ifp, ifra, ia, 0)) != 0) | if ((error = in6_update_ifa(ifp, ifra, ia, 0)) != 0) | ||||
goto out; | goto out; | ||||
if (ia != NULL) { | if (ia != NULL) { | ||||
if (ia->ia_ifa.ifa_carp) | if (ia->ia_ifa.ifa_carp) | ||||
(*carp_detach_p)(&ia->ia_ifa, true); | (*carp_detach_p)(&ia->ia_ifa, true); | ||||
ifa_free(&ia->ia_ifa); | ifa_free(&ia->ia_ifa); | ||||
Show All 15 Lines | if (cmd == ocmd && ifra->ifra_vhid > 0) { | ||||
error = EPROTONOSUPPORT; | error = EPROTONOSUPPORT; | ||||
if (error) | if (error) | ||||
goto out; | goto out; | ||||
else | else | ||||
carp_attached = 1; | carp_attached = 1; | ||||
} | } | ||||
/* | /* | ||||
* then, make the prefix on-link on the interface. | |||||
* XXX: we'd rather create the prefix before the address, but | |||||
* we need at least one address to install the corresponding | |||||
* interface route, so we configure the address first. | |||||
*/ | |||||
/* | |||||
* convert mask to prefix length (prefixmask has already | |||||
* been validated in in6_update_ifa(). | |||||
*/ | |||||
bzero(&pr0, sizeof(pr0)); | |||||
pr0.ndpr_ifp = ifp; | |||||
pr0.ndpr_plen = in6_mask2len(&ifra->ifra_prefixmask.sin6_addr, | |||||
NULL); | |||||
if (pr0.ndpr_plen == 128) { | |||||
/* we don't need to install a host route. */ | |||||
goto aifaddr_out; | |||||
} | |||||
pr0.ndpr_prefix = ifra->ifra_addr; | |||||
/* apply the mask for safety. */ | |||||
IN6_MASK_ADDR(&pr0.ndpr_prefix.sin6_addr, | |||||
&ifra->ifra_prefixmask.sin6_addr); | |||||
/* | |||||
* XXX: since we don't have an API to set prefix (not address) | |||||
* lifetimes, we just use the same lifetimes as addresses. | |||||
* The (temporarily) installed lifetimes can be overridden by | |||||
* later advertised RAs (when accept_rtadv is non 0), which is | |||||
* an intended behavior. | |||||
*/ | |||||
pr0.ndpr_raf_onlink = 1; /* should be configurable? */ | |||||
pr0.ndpr_raf_auto = | |||||
((ifra->ifra_flags & IN6_IFF_AUTOCONF) != 0); | |||||
pr0.ndpr_vltime = ifra->ifra_lifetime.ia6t_vltime; | |||||
pr0.ndpr_pltime = ifra->ifra_lifetime.ia6t_pltime; | |||||
/* add the prefix if not yet. */ | |||||
if ((pr = nd6_prefix_lookup(&pr0)) == NULL) { | |||||
/* | |||||
* nd6_prelist_add will install the corresponding | |||||
* interface route. | |||||
*/ | |||||
if ((error = nd6_prelist_add(&pr0, NULL, &pr)) != 0) { | |||||
if (carp_attached) | |||||
(*carp_detach_p)(&ia->ia_ifa, false); | |||||
goto out; | |||||
} | |||||
} | |||||
/* relate the address to the prefix */ | |||||
if (ia->ia6_ndpr == NULL) { | |||||
ia->ia6_ndpr = pr; | |||||
pr->ndpr_addrcnt++; | |||||
/* | |||||
* If this is the first autoconf address from the | |||||
* prefix, create a temporary address as well | |||||
* (when required). | |||||
*/ | |||||
if ((ia->ia6_flags & IN6_IFF_AUTOCONF) && | |||||
V_ip6_use_tempaddr && pr->ndpr_addrcnt == 1) { | |||||
int e; | |||||
if ((e = in6_tmpifadd(ia, 1, 0)) != 0) { | |||||
log(LOG_NOTICE, "in6_control: failed " | |||||
"to create a temporary address, " | |||||
"errno=%d\n", e); | |||||
} | |||||
} | |||||
} | |||||
nd6_prefix_rele(pr); | |||||
/* | |||||
* this might affect the status of autoconfigured addresses, | |||||
* that is, this address might make other addresses detached. | |||||
*/ | |||||
pfxlist_onlink_check(); | |||||
aifaddr_out: | |||||
/* | |||||
* Try to clear the flag when a new IPv6 address is added | * Try to clear the flag when a new IPv6 address is added | ||||
* onto an IFDISABLED interface and it succeeds. | * onto an IFDISABLED interface and it succeeds. | ||||
*/ | */ | ||||
if (ND_IFINFO(ifp)->flags & ND6_IFF_IFDISABLED) { | if (ND_IFINFO(ifp)->flags & ND6_IFF_IFDISABLED) { | ||||
struct in6_ndireq nd; | struct in6_ndireq nd; | ||||
memset(&nd, 0, sizeof(nd)); | memset(&nd, 0, sizeof(nd)); | ||||
nd.ndi.flags = ND_IFINFO(ifp)->flags; | nd.ndi.flags = ND_IFINFO(ifp)->flags; | ||||
▲ Show 20 Lines • Show All 347 Lines • ▼ Show 20 Lines | nd6log((LOG_INFO, | ||||
ip6_sprintf(ip6buf, &ifra->ifra_addr.sin6_addr))); | ip6_sprintf(ip6buf, &ifra->ifra_addr.sin6_addr))); | ||||
if (ia == NULL) | if (ia == NULL) | ||||
return (0); /* there's nothing to do */ | return (0); /* there's nothing to do */ | ||||
} | } | ||||
/* Check prefix mask */ | /* Check prefix mask */ | ||||
if (ia != NULL && ifra->ifra_prefixmask.sin6_len != 0) { | if (ia != NULL && ifra->ifra_prefixmask.sin6_len != 0) { | ||||
if (ia->ia_prefixmask.sin6_len != 0) { | |||||
/* | /* | ||||
* We prohibit changing the prefix length of an existing | * We prohibit changing the prefix length of an | ||||
* address, because | * existing address, because | ||||
* + such an operation should be rare in IPv6, and | * + such an operation should be rare in IPv6, and | ||||
* + the operation would confuse prefix management. | * + the operation would confuse prefix management. | ||||
*/ | */ | ||||
if (ia->ia_prefixmask.sin6_len != 0 && | if (ia->ia6_ndpr != NULL && | ||||
in6_mask2len(&ia->ia_prefixmask.sin6_addr, NULL) != plen) { | in6_mask2len(&ia->ia_prefixmask.sin6_addr, NULL) != | ||||
nd6log((LOG_INFO, "in6_validate_ifa: the prefix length " | plen) { | ||||
"of an existing %s address should not be changed\n", | nd6log((LOG_INFO, "in6_validate_ifa: the " | ||||
"prefix length of an existing %s address " | |||||
"should not be changed\n", | |||||
ip6_sprintf(ip6buf, &ia->ia_addr.sin6_addr))); | ip6_sprintf(ip6buf, &ia->ia_addr.sin6_addr))); | ||||
return (EINVAL); | return (EINVAL); | ||||
} | } | ||||
} | } | ||||
} | |||||
return (0); | return (0); | ||||
} | } | ||||
/* | /* | ||||
* Allocate a new ifaddr and link it into chains. | * Allocate a new ifaddr and link it into chains. | ||||
*/ | */ | ||||
static struct in6_ifaddr * | static struct in6_ifaddr * | ||||
Show All 26 Lines | ia->ia_ifa.ifa_dstaddr = | ||||
(struct sockaddr *)&ia->ia_dstaddr; | (struct sockaddr *)&ia->ia_dstaddr; | ||||
} else { | } else { | ||||
ia->ia_ifa.ifa_dstaddr = NULL; | ia->ia_ifa.ifa_dstaddr = NULL; | ||||
} | } | ||||
/* set prefix mask if any */ | /* set prefix mask if any */ | ||||
ia->ia_ifa.ifa_netmask = (struct sockaddr *)&ia->ia_prefixmask; | ia->ia_ifa.ifa_netmask = (struct sockaddr *)&ia->ia_prefixmask; | ||||
if (ifra->ifra_prefixmask.sin6_len != 0) { | if (ifra->ifra_prefixmask.sin6_len != 0) { | ||||
if (ia->ia_prefixmask.sin6_len != 0) { | |||||
if (!IN6_ARE_ADDR_EQUAL(&ia->ia_prefixmask.sin6_addr, | |||||
&ifra->ifra_prefixmask.sin6_addr)) | |||||
in6_ifremprefix(ia); | |||||
} | |||||
ia->ia_prefixmask.sin6_family = AF_INET6; | ia->ia_prefixmask.sin6_family = AF_INET6; | ||||
ia->ia_prefixmask.sin6_len = ifra->ifra_prefixmask.sin6_len; | ia->ia_prefixmask.sin6_len = ifra->ifra_prefixmask.sin6_len; | ||||
ia->ia_prefixmask.sin6_addr = ifra->ifra_prefixmask.sin6_addr; | ia->ia_prefixmask.sin6_addr = ifra->ifra_prefixmask.sin6_addr; | ||||
} | } | ||||
ia->ia_ifp = ifp; | ia->ia_ifp = ifp; | ||||
ifa_ref(&ia->ia_ifa); /* if_addrhead */ | ifa_ref(&ia->ia_ifa); /* if_addrhead */ | ||||
IF_ADDR_WLOCK(ifp); | IF_ADDR_WLOCK(ifp); | ||||
▲ Show 20 Lines • Show All 143 Lines • ▼ Show 20 Lines | in6_broadcast_ifa(struct ifnet *ifp, struct in6_aliasreq *ifra, | ||||
} | } | ||||
in6_newaddrmsg(ia, RTM_ADD); | in6_newaddrmsg(ia, RTM_ADD); | ||||
ifa_free(&ia->ia_ifa); | ifa_free(&ia->ia_ifa); | ||||
return (error); | return (error); | ||||
} | } | ||||
/* | /* | ||||
* Adds or deletes interface route for p2p ifa. | * Adds or deletes interface route for ifa. | ||||
* Returns 0 on success or errno. | * Returns 0 on success or errno. | ||||
*/ | */ | ||||
static int | static int | ||||
in6_handle_dstaddr_rtrequest(int cmd, struct in6_ifaddr *ia) | in6_handle_rtrequest(int cmd, struct in6_ifaddr *ia, int flags) | ||||
{ | { | ||||
struct epoch_tracker et; | struct epoch_tracker et; | ||||
struct ifaddr *ifa = &ia->ia_ifa; | struct ifaddr *ifa = &ia->ia_ifa; | ||||
int error; | int error; | ||||
/* Prepare gateway */ | /* Prepare gateway */ | ||||
struct sockaddr_dl_short sdl = { | struct sockaddr_dl_short sdl = { | ||||
.sdl_family = AF_LINK, | .sdl_family = AF_LINK, | ||||
.sdl_len = sizeof(struct sockaddr_dl_short), | .sdl_len = sizeof(struct sockaddr_dl_short), | ||||
.sdl_type = ifa->ifa_ifp->if_type, | .sdl_type = ifa->ifa_ifp->if_type, | ||||
.sdl_index = ifa->ifa_ifp->if_index, | .sdl_index = ifa->ifa_ifp->if_index, | ||||
}; | }; | ||||
struct sockaddr_in6 dst = { | struct sockaddr_in6 *dst6 = (flags & RTF_HOST) ? | ||||
.sin6_family = AF_INET6, | &ia->ia_dstaddr : &ia->ia_addr; | ||||
.sin6_len = sizeof(struct sockaddr_in6), | struct sockaddr_in6 *netmask6 = (flags & RTF_HOST) ? | ||||
.sin6_addr = ia->ia_dstaddr.sin6_addr, | NULL : &ia->ia_prefixmask; | ||||
}; | |||||
struct rt_addrinfo info = { | struct rt_addrinfo info = { | ||||
.rti_ifa = ifa, | .rti_ifa = ifa, | ||||
.rti_flags = RTF_PINNED | RTF_HOST, | .rti_flags = flags | | ||||
(ifa->ifa_flags & ~IFA_RTSELF) | RTF_PINNED, | |||||
.rti_info = { | .rti_info = { | ||||
[RTAX_DST] = (struct sockaddr *)&dst, | [RTAX_DST] = (struct sockaddr *)dst6, | ||||
[RTAX_NETMASK] = (struct sockaddr *)netmask6, | |||||
[RTAX_GATEWAY] = (struct sockaddr *)&sdl, | [RTAX_GATEWAY] = (struct sockaddr *)&sdl, | ||||
}, | }, | ||||
}; | }; | ||||
/* Don't set additional per-gw filters on removal */ | /* Don't set additional per-gw filters on removal */ | ||||
NET_EPOCH_ENTER(et); | NET_EPOCH_ENTER(et); | ||||
error = rib_handle_ifaddr_info(ifa->ifa_ifp->if_fib, cmd, &info); | error = rib_handle_ifaddr_info(ifa->ifa_ifp->if_fib, cmd, &info); | ||||
NET_EPOCH_EXIT(et); | NET_EPOCH_EXIT(et); | ||||
return (error); | return (error); | ||||
} | } | ||||
void | void | ||||
in6_purgeaddr(struct ifaddr *ifa) | in6_purgeaddr(struct ifaddr *ifa) | ||||
{ | { | ||||
struct ifnet *ifp = ifa->ifa_ifp; | struct ifnet *ifp = ifa->ifa_ifp; | ||||
struct in6_ifaddr *ia = (struct in6_ifaddr *) ifa; | struct in6_ifaddr *ia = (struct in6_ifaddr *) ifa; | ||||
struct in6_multi_mship *imm; | struct in6_multi_mship *imm; | ||||
int plen, error; | int error; | ||||
if (ifa->ifa_carp) | if (ifa->ifa_carp) | ||||
(*carp_detach_p)(ifa, false); | (*carp_detach_p)(ifa, false); | ||||
/* | /* | ||||
* Remove the loopback route to the interface address. | * Remove the loopback route to the interface address. | ||||
* The check for the current setting of "nd6_useloopback" | * The check for the current setting of "nd6_useloopback" | ||||
* is not needed. | * is not needed. | ||||
Show All 10 Lines | in6_purgeaddr(struct ifaddr *ifa) | ||||
/* Leave multicast groups. */ | /* Leave multicast groups. */ | ||||
while ((imm = LIST_FIRST(&ia->ia6_memberships)) != NULL) { | while ((imm = LIST_FIRST(&ia->ia6_memberships)) != NULL) { | ||||
LIST_REMOVE(imm, i6mm_chain); | LIST_REMOVE(imm, i6mm_chain); | ||||
if (imm->i6mm_maddr != NULL) | if (imm->i6mm_maddr != NULL) | ||||
in6_leavegroup(imm->i6mm_maddr, NULL); | in6_leavegroup(imm->i6mm_maddr, NULL); | ||||
free(imm, M_IP6MADDR); | free(imm, M_IP6MADDR); | ||||
} | } | ||||
/* Check if we need to remove p2p route */ | |||||
plen = in6_mask2len(&ia->ia_prefixmask.sin6_addr, NULL); /* XXX */ | |||||
if (ia->ia_dstaddr.sin6_family != AF_INET6) | |||||
plen = 0; | |||||
if ((ia->ia_flags & IFA_ROUTE) && plen == 128) { | |||||
error = in6_handle_dstaddr_rtrequest(RTM_DELETE, ia); | |||||
if (error != 0) | |||||
log(LOG_INFO, "%s: err=%d, destination address delete " | |||||
"failed\n", __func__, error); | |||||
ia->ia_flags &= ~IFA_ROUTE; | |||||
} | |||||
/* Delete any network route. */ | |||||
in6_ifremprefix(ia); | |||||
in6_newaddrmsg(ia, RTM_DELETE); | in6_newaddrmsg(ia, RTM_DELETE); | ||||
in6_unlink_ifa(ia, ifp); | in6_unlink_ifa(ia, ifp); | ||||
} | } | ||||
/* | /* | ||||
* Removes @ia from the corresponding interfaces and unlinks corresponding | * Removes @ia from the corresponding interfaces and unlinks corresponding | ||||
* prefix if no addresses are using it anymore. | * prefix if no addresses are using it anymore. | ||||
*/ | */ | ||||
void | void | ||||
in6_purgeifaddr(struct in6_ifaddr *ia) | in6_purgeifaddr(struct in6_ifaddr *ia) | ||||
{ | { | ||||
struct nd_prefix *pr; | struct nd_prefix *pr; | ||||
/* | /* | ||||
* If the address being deleted is the only one that owns | * If the address being deleted is the only one that owns | ||||
* the corresponding prefix, expire the prefix as well. | * the corresponding prefix, expire the prefix as well. | ||||
* XXX: theoretically, we don't have to worry about such | |||||
* relationship, since we separate the address management | |||||
* and the prefix management. We do this, however, to provide | |||||
* as much backward compatibility as possible in terms of | |||||
* the ioctl operation. | |||||
* Note that in6_purgeaddr() will decrement ndpr_addrcnt. | * Note that in6_purgeaddr() will decrement ndpr_addrcnt. | ||||
*/ | */ | ||||
pr = ia->ia6_ndpr; | pr = ia->ia6_ndpr; | ||||
in6_purgeaddr(&ia->ia_ifa); | in6_purgeaddr(&ia->ia_ifa); | ||||
if (pr != NULL && pr->ndpr_addrcnt == 0) { | if (pr != NULL && pr->ndpr_addrcnt == 0) { | ||||
ND6_WLOCK(); | ND6_WLOCK(); | ||||
nd6_prefix_unlink(pr, NULL); | nd6_prefix_unlink(pr, NULL); | ||||
ND6_WUNLOCK(); | ND6_WUNLOCK(); | ||||
nd6_prefix_del(pr); | nd6_prefix_del(pr); | ||||
} | } | ||||
} | } | ||||
static void | static void | ||||
in6_unlink_ifa(struct in6_ifaddr *ia, struct ifnet *ifp) | in6_unlink_ifa(struct in6_ifaddr *ia, struct ifnet *ifp) | ||||
{ | { | ||||
char ip6buf[INET6_ADDRSTRLEN]; | |||||
int remove_lle; | int remove_lle; | ||||
IF_ADDR_WLOCK(ifp); | IF_ADDR_WLOCK(ifp); | ||||
CK_STAILQ_REMOVE(&ifp->if_addrhead, &ia->ia_ifa, ifaddr, ifa_link); | CK_STAILQ_REMOVE(&ifp->if_addrhead, &ia->ia_ifa, ifaddr, ifa_link); | ||||
IF_ADDR_WUNLOCK(ifp); | IF_ADDR_WUNLOCK(ifp); | ||||
ifa_free(&ia->ia_ifa); /* if_addrhead */ | ifa_free(&ia->ia_ifa); /* if_addrhead */ | ||||
/* | /* | ||||
* Defer the release of what might be the last reference to the | * Defer the release of what might be the last reference to the | ||||
* in6_ifaddr so that it can't be freed before the remainder of the | * in6_ifaddr so that it can't be freed before the remainder of the | ||||
* cleanup. | * cleanup. | ||||
*/ | */ | ||||
IN6_IFADDR_WLOCK(); | IN6_IFADDR_WLOCK(); | ||||
CK_STAILQ_REMOVE(&V_in6_ifaddrhead, ia, in6_ifaddr, ia_link); | CK_STAILQ_REMOVE(&V_in6_ifaddrhead, ia, in6_ifaddr, ia_link); | ||||
CK_LIST_REMOVE(ia, ia6_hash); | CK_LIST_REMOVE(ia, ia6_hash); | ||||
IN6_IFADDR_WUNLOCK(); | IN6_IFADDR_WUNLOCK(); | ||||
/* | /* | ||||
* Release the reference to the base prefix. There should be a | * Release the reference to the ND prefix. | ||||
* positive reference. | |||||
*/ | */ | ||||
remove_lle = 0; | remove_lle = 0; | ||||
if (ia->ia6_ndpr == NULL) { | if (ia->ia6_ndpr != NULL) { | ||||
nd6log((LOG_NOTICE, | |||||
"in6_unlink_ifa: autoconf'ed address " | |||||
"%s has no prefix\n", ip6_sprintf(ip6buf, IA6_IN6(ia)))); | |||||
} else { | |||||
ia->ia6_ndpr->ndpr_addrcnt--; | ia->ia6_ndpr->ndpr_addrcnt--; | ||||
/* Do not delete lles within prefix if refcont != 0 */ | /* Do not delete lles within prefix if refcont != 0 */ | ||||
if (ia->ia6_ndpr->ndpr_addrcnt == 0) | if (ia->ia6_ndpr->ndpr_addrcnt == 0) | ||||
remove_lle = 1; | remove_lle = 1; | ||||
ia->ia6_ndpr = NULL; | ia->ia6_ndpr = NULL; | ||||
} | } | ||||
nd6_rem_ifa_lle(ia, remove_lle); | nd6_rem_ifa_lle(ia, remove_lle); | ||||
Show All 14 Lines | |||||
* 1) Notifies device handler on the first IPv6 address assignment | * 1) Notifies device handler on the first IPv6 address assignment | ||||
* 2) Handle routing table changes for P2P links and route | * 2) Handle routing table changes for P2P links and route | ||||
* 3) Handle routing table changes for address host route | * 3) Handle routing table changes for address host route | ||||
*/ | */ | ||||
static int | static int | ||||
in6_notify_ifa(struct ifnet *ifp, struct in6_ifaddr *ia, | in6_notify_ifa(struct ifnet *ifp, struct in6_ifaddr *ia, | ||||
struct in6_aliasreq *ifra, int hostIsNew) | struct in6_aliasreq *ifra, int hostIsNew) | ||||
{ | { | ||||
int error = 0, plen, ifacount = 0; | int error = 0, ifacount = 0; | ||||
struct ifaddr *ifa; | struct ifaddr *ifa; | ||||
struct sockaddr_in6 *pdst; | struct sockaddr_in6 *pdst; | ||||
char ip6buf[INET6_ADDRSTRLEN]; | |||||
/* | /* | ||||
* Give the interface a chance to initialize | * Give the interface a chance to initialize | ||||
* if this is its first address, | * if this is its first address, | ||||
*/ | */ | ||||
if (hostIsNew != 0) { | if (hostIsNew != 0) { | ||||
struct epoch_tracker et; | struct epoch_tracker et; | ||||
NET_EPOCH_ENTER(et); | NET_EPOCH_ENTER(et); | ||||
CK_STAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) { | CK_STAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) { | ||||
if (ifa->ifa_addr->sa_family != AF_INET6) | if (ifa->ifa_addr->sa_family != AF_INET6) | ||||
continue; | continue; | ||||
ifacount++; | ifacount++; | ||||
} | } | ||||
NET_EPOCH_EXIT(et); | NET_EPOCH_EXIT(et); | ||||
} | } | ||||
if (ifacount <= 1 && ifp->if_ioctl) { | if (ifacount <= 1 && ifp->if_ioctl) { | ||||
error = (*ifp->if_ioctl)(ifp, SIOCSIFADDR, (caddr_t)ia); | error = (*ifp->if_ioctl)(ifp, SIOCSIFADDR, (caddr_t)ia); | ||||
if (error) | if (error) | ||||
goto done; | goto done; | ||||
} | } | ||||
/* | /* Set destination address. */ | ||||
* 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. | |||||
*/ | |||||
pdst = &ifra->ifra_dstaddr; | pdst = &ifra->ifra_dstaddr; | ||||
if (pdst->sin6_family == AF_INET6 && | if (pdst->sin6_family == AF_INET6) { | ||||
!IN6_ARE_ADDR_EQUAL(&pdst->sin6_addr, &ia->ia_dstaddr.sin6_addr)) { | if (!IN6_ARE_ADDR_EQUAL(&pdst->sin6_addr, | ||||
if ((ia->ia_flags & IFA_ROUTE) != 0 && | &ia->ia_dstaddr.sin6_addr)) | ||||
(in6_handle_dstaddr_rtrequest(RTM_DELETE, ia) != 0)) { | in6_ifremprefix(ia); | ||||
nd6log((LOG_ERR, "in6_update_ifa_internal: failed to " | |||||
"remove a route to the old destination: %s\n", | |||||
ip6_sprintf(ip6buf, &ia->ia_addr.sin6_addr))); | |||||
/* proceed anyway... */ | |||||
} else | |||||
ia->ia_flags &= ~IFA_ROUTE; | |||||
ia->ia_dstaddr = *pdst; | ia->ia_dstaddr = *pdst; | ||||
} | } | ||||
/* | /* | ||||
* If a new destination address is specified for a point-to-point | |||||
* interface, install a route to the destination as an interface | |||||
* direct route. | |||||
* XXX: the logic below rejects assigning multiple addresses on a p2p | |||||
* interface that share the same destination. | |||||
*/ | |||||
plen = in6_mask2len(&ia->ia_prefixmask.sin6_addr, NULL); /* XXX */ | |||||
if (!(ia->ia_flags & IFA_ROUTE) && plen == 128 && | |||||
ia->ia_dstaddr.sin6_family == AF_INET6) { | |||||
/* | |||||
* Handle the case for ::1 . | |||||
*/ | |||||
if (ifp->if_flags & IFF_LOOPBACK) | |||||
ia->ia_flags |= IFA_RTSELF; | |||||
error = in6_handle_dstaddr_rtrequest(RTM_ADD, ia); | |||||
if (error) | |||||
goto done; | |||||
ia->ia_flags |= IFA_ROUTE; | |||||
} | |||||
/* | |||||
* add a loopback route to self if not exists | * add a loopback route to self if not exists | ||||
*/ | */ | ||||
if (!(ia->ia_flags & IFA_RTSELF) && V_nd6_useloopback) { | if (!(ia->ia_flags & IFA_RTSELF) && V_nd6_useloopback) { | ||||
error = ifa_add_loopback_route((struct ifaddr *)ia, | error = ifa_add_loopback_route((struct ifaddr *)ia, | ||||
(struct sockaddr *)&ia->ia_addr); | (struct sockaddr *)&ia->ia_addr); | ||||
if (error == 0) | if (error) | ||||
goto done; | |||||
ia->ia_flags |= IFA_RTSELF; | ia->ia_flags |= IFA_RTSELF; | ||||
} | |||||
/* Add the network prefix route. */ | |||||
if ((error = in6_ifaddprefix(ia)) != 0) { | |||||
if (hostIsNew != 0) { | |||||
if (ifa_del_loopback_route((struct ifaddr *)ia, | |||||
(struct sockaddr *)&ia->ia_addr) == 0) | |||||
ia->ia_flags &= ~IFA_RTSELF; | |||||
} | |||||
} | } | ||||
done: | done: | ||||
WITNESS_WARN(WARN_GIANTOK | WARN_SLEEPOK, NULL, | WITNESS_WARN(WARN_GIANTOK | WARN_SLEEPOK, NULL, | ||||
"Invoking IPv6 network device address event may sleep"); | "Invoking IPv6 network device address event may sleep"); | ||||
ifa_ref(&ia->ia_ifa); | ifa_ref(&ia->ia_ifa); | ||||
EVENTHANDLER_INVOKE(ifaddr_event_ext, ifp, &ia->ia_ifa, | EVENTHANDLER_INVOKE(ifaddr_event_ext, ifp, &ia->ia_ifa, | ||||
IFADDR_EVENT_ADD); | IFADDR_EVENT_ADD); | ||||
▲ Show 20 Lines • Show All 1,081 Lines • Show Last 20 Lines |