X-Git-Url: https://git.saurik.com/apple/xnu.git/blobdiff_plain/3e170ce000f1506b7b5d2c5c7faec85ceabb573d..2a1bd2d3eef5c7a7bb14f4bb9fdbca9a96ee4752:/bsd/netinet6/in6.c diff --git a/bsd/netinet6/in6.c b/bsd/netinet6/in6.c index 40ece5dfc..dd680119e 100644 --- a/bsd/netinet6/in6.c +++ b/bsd/netinet6/in6.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2015 Apple Inc. All rights reserved. + * Copyright (c) 2003-2020 Apple Inc. All rights reserved. * * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ * @@ -105,9 +105,11 @@ #include #include #include +#include #include #include +#include #include #include #include @@ -118,6 +120,7 @@ #include #include #include +#include #include #include @@ -141,6 +144,9 @@ #include +#include +#include + #if PF #include #endif /* PF */ @@ -151,16 +157,19 @@ const struct in6_addr in6addr_any = IN6ADDR_ANY_INIT; const struct in6_addr in6addr_loopback = IN6ADDR_LOOPBACK_INIT; const struct in6_addr in6addr_nodelocal_allnodes = - IN6ADDR_NODELOCAL_ALLNODES_INIT; + IN6ADDR_NODELOCAL_ALLNODES_INIT; const struct in6_addr in6addr_linklocal_allnodes = - IN6ADDR_LINKLOCAL_ALLNODES_INIT; + IN6ADDR_LINKLOCAL_ALLNODES_INIT; const struct in6_addr in6addr_linklocal_allrouters = - IN6ADDR_LINKLOCAL_ALLROUTERS_INIT; + IN6ADDR_LINKLOCAL_ALLROUTERS_INIT; const struct in6_addr in6addr_linklocal_allv2routers = - IN6ADDR_LINKLOCAL_ALLV2ROUTERS_INIT; + IN6ADDR_LINKLOCAL_ALLV2ROUTERS_INIT; +const struct in6_addr in6addr_multicast_prefix = + IN6ADDR_MULTICAST_PREFIX; const struct in6_addr in6mask0 = IN6MASK0; const struct in6_addr in6mask7 = IN6MASK7; +const struct in6_addr in6mask8 = IN6MASK8; const struct in6_addr in6mask16 = IN6MASK16; const struct in6_addr in6mask32 = IN6MASK32; const struct in6_addr in6mask64 = IN6MASK64; @@ -168,7 +177,12 @@ const struct in6_addr in6mask96 = IN6MASK96; const struct in6_addr in6mask128 = IN6MASK128; const struct sockaddr_in6 sa6_any = { - sizeof (sa6_any), AF_INET6, 0, 0, IN6ADDR_ANY_INIT, 0 + .sin6_len = sizeof(sa6_any), + .sin6_family = AF_INET6, + .sin6_port = 0, + .sin6_flowinfo = 0, + .sin6_addr = IN6ADDR_ANY_INIT, + .sin6_scope_id = 0 }; static int in6ctl_associd(struct socket *, u_long, caddr_t); @@ -185,61 +199,66 @@ static int in6ctl_alifetime(struct in6_ifaddr *, u_long, struct in6_ifreq *, static int in6ctl_aifaddr(struct ifnet *, struct in6_aliasreq *); static void in6ctl_difaddr(struct ifnet *, struct in6_ifaddr *); static int in6_autoconf(struct ifnet *, int); -static int in6_setrouter(struct ifnet *, int); +static int in6_setrouter(struct ifnet *, ipv6_router_mode_t); static int in6_ifinit(struct ifnet *, struct in6_ifaddr *, int); static int in6_ifaupdate_aux(struct in6_ifaddr *, struct ifnet *, int); static void in6_unlink_ifa(struct in6_ifaddr *, struct ifnet *); -static struct in6_ifaddr *in6_ifaddr_alloc(int); +static struct in6_ifaddr *in6_ifaddr_alloc(zalloc_flags_t); static void in6_ifaddr_attached(struct ifaddr *); static void in6_ifaddr_detached(struct ifaddr *); static void in6_ifaddr_free(struct ifaddr *); static void in6_ifaddr_trace(struct ifaddr *, int); #if defined(__LP64__) -static void in6_llstartreq_32_to_64(struct in6_llstartreq_32 *, - struct in6_llstartreq_64 *); +static void in6_cgareq_32_to_64(const struct in6_cgareq_32 *, + struct in6_cgareq_64 *); #else -static void in6_llstartreq_64_to_32(struct in6_llstartreq_64 *, - struct in6_llstartreq_32 *); +static void in6_cgareq_64_to_32(const struct in6_cgareq_64 *, + struct in6_cgareq_32 *); #endif static struct in6_aliasreq *in6_aliasreq_to_native(void *, int, struct in6_aliasreq *); -static struct in6_llstartreq *in6_llstartreq_to_native(void *, int, - struct in6_llstartreq *); static int in6_to_kamescope(struct sockaddr_in6 *, struct ifnet *); - -static void in6_ifaddr_set_dadprogress(struct in6_ifaddr *); - static int in6_getassocids(struct socket *, uint32_t *, user_addr_t); static int in6_getconnids(struct socket *, sae_associd_t, uint32_t *, user_addr_t); -static int in6_getconninfo(struct socket *, sae_connid_t, uint32_t *, - uint32_t *, int32_t *, user_addr_t, socklen_t *, user_addr_t, socklen_t *, - uint32_t *, user_addr_t, uint32_t *); static void in6_if_up_dad_start(struct ifnet *); +#define IA6_HASH_INIT(ia) { \ + (ia)->ia6_hash.tqe_next = (void *)(uintptr_t)-1; \ + (ia)->ia6_hash.tqe_prev = (void *)(uintptr_t)-1; \ +} + +#define IA6_IS_HASHED(ia) \ + (!((ia)->ia6_hash.tqe_next == (void *)(uintptr_t)-1 || \ + (ia)->ia6_hash.tqe_prev == (void *)(uintptr_t)-1)) + +static void in6_iahash_remove(struct in6_ifaddr *); +static void in6_iahash_insert(struct in6_ifaddr *); +static void in6_iahash_insert_ptp(struct in6_ifaddr *); + extern lck_mtx_t *nd6_mutex; -#define IN6IFA_TRACE_HIST_SIZE 32 /* size of trace history */ +#define IN6IFA_TRACE_HIST_SIZE 32 /* size of trace history */ /* For gdb */ __private_extern__ unsigned int in6ifa_trace_hist_size = IN6IFA_TRACE_HIST_SIZE; struct in6_ifaddr_dbg { - struct in6_ifaddr in6ifa; /* in6_ifaddr */ - struct in6_ifaddr in6ifa_old; /* saved in6_ifaddr */ - u_int16_t in6ifa_refhold_cnt; /* # of IFA_ADDREF */ - u_int16_t in6ifa_refrele_cnt; /* # of IFA_REMREF */ + struct in6_ifaddr in6ifa; /* in6_ifaddr */ + struct in6_ifaddr in6ifa_old; /* saved in6_ifaddr */ + u_int16_t in6ifa_refhold_cnt; /* # of IFA_ADDREF */ + u_int16_t in6ifa_refrele_cnt; /* # of IFA_REMREF */ /* * Alloc and free callers. */ - ctrace_t in6ifa_alloc; - ctrace_t in6ifa_free; + ctrace_t in6ifa_alloc; + ctrace_t in6ifa_free; /* * Circular lists of IFA_ADDREF and IFA_REMREF callers. */ - ctrace_t in6ifa_refhold[IN6IFA_TRACE_HIST_SIZE]; - ctrace_t in6ifa_refrele[IN6IFA_TRACE_HIST_SIZE]; + ctrace_t in6ifa_refhold[IN6IFA_TRACE_HIST_SIZE]; + ctrace_t in6ifa_refrele[IN6IFA_TRACE_HIST_SIZE]; /* * Trash list linkage */ @@ -251,16 +270,15 @@ static TAILQ_HEAD(, in6_ifaddr_dbg) in6ifa_trash_head; static decl_lck_mtx_data(, in6ifa_trash_lock); #if DEBUG -static unsigned int in6ifa_debug = 1; /* debugging (enabled) */ +static unsigned int in6ifa_debug = 1; /* debugging (enabled) */ #else -static unsigned int in6ifa_debug; /* debugging (disabled) */ +static unsigned int in6ifa_debug; /* debugging (disabled) */ #endif /* !DEBUG */ -static unsigned int in6ifa_size; /* size of zone element */ -static struct zone *in6ifa_zone; /* zone for in6_ifaddr */ - -#define IN6IFA_ZONE_MAX 64 /* maximum elements in zone */ -#define IN6IFA_ZONE_NAME "in6_ifaddr" /* zone name */ +static struct zone *in6ifa_zone; /* zone for in6_ifaddr */ +#define IN6IFA_ZONE_NAME "in6_ifaddr" /* zone name */ +struct eventhandler_lists_ctxt in6_evhdlr_ctxt; +struct eventhandler_lists_ctxt in6_clat46_evhdlr_ctxt; /* * Subroutine for in6_ifaddloop() and in6_ifremloop(). * This routine does actual work. @@ -272,9 +290,9 @@ in6_ifloop_request(int cmd, struct ifaddr *ifa) struct rtentry *nrt = NULL; int e; - bzero(&all1_sa, sizeof (all1_sa)); + bzero(&all1_sa, sizeof(all1_sa)); all1_sa.sin6_family = AF_INET6; - all1_sa.sin6_len = sizeof (struct sockaddr_in6); + all1_sa.sin6_len = sizeof(struct sockaddr_in6); all1_sa.sin6_addr = in6mask128; /* @@ -288,7 +306,7 @@ in6_ifloop_request(int cmd, struct ifaddr *ifa) */ lck_mtx_lock(rnh_lock); e = rtrequest_locked(cmd, ifa->ifa_addr, ifa->ifa_addr, - (struct sockaddr *)&all1_sa, RTF_UP|RTF_HOST|RTF_LLINFO, &nrt); + (struct sockaddr *)&all1_sa, RTF_UP | RTF_HOST | RTF_LLINFO, &nrt); if (e != 0) { log(LOG_ERR, "in6_ifloop_request: " "%s operation failed for %s (errno=%d)\n", @@ -297,8 +315,9 @@ in6_ifloop_request(int cmd, struct ifaddr *ifa) e); } - if (nrt != NULL) + if (nrt != NULL) { RT_LOCK(nrt); + } /* * Make sure rt_ifa be equal to IFA, the second argument of the * function. @@ -347,8 +366,9 @@ in6_ifaddloop(struct ifaddr *ifa) * INET6 is set once during init; no need to hold lock. */ rt = rtalloc1(ifa->ifa_addr, 0, 0); - if (rt != NULL) + if (rt != NULL) { RT_LOCK(rt); + } if (rt == NULL || (rt->rt_flags & RTF_HOST) == 0 || (rt->rt_ifp->if_flags & IFF_LOOPBACK) == 0) { if (rt != NULL) { @@ -390,7 +410,7 @@ in6_ifremloop(struct ifaddr *ifa) * XXX: we should avoid such a configuration in IPv6... */ lck_rw_lock_exclusive(&in6_ifaddr_rwlock); - for (ia = in6_ifaddrs; ia; ia = ia->ia_next) { + TAILQ_FOREACH(ia, IN6ADDR_HASH(IFA_IN6(ifa)), ia6_hash) { IFA_LOCK(&ia->ia_ifa); if (IN6_ARE_ADDR_EQUAL(IFA_IN6(ifa), &ia->ia_addr.sin6_addr)) { ia_count++; @@ -430,25 +450,26 @@ in6_ifremloop(struct ifaddr *ifa) int -in6_mask2len(mask, lim0) - struct in6_addr *mask; - u_char *lim0; +in6_mask2len(struct in6_addr *mask, u_char *lim0) { int x = 0, y; u_char *lim = lim0, *p; /* ignore the scope_id part */ - if (lim0 == NULL || lim0 - (u_char *)mask > sizeof (*mask)) - lim = (u_char *)mask + sizeof (*mask); + if (lim0 == NULL || lim0 - (u_char *)mask > sizeof(*mask)) { + lim = (u_char *)mask + sizeof(*mask); + } for (p = (u_char *)mask; p < lim; x++, p++) { - if (*p != 0xff) + if (*p != 0xff) { break; + } } y = 0; if (p < lim) { for (y = 0; y < 8; y++) { - if ((*p & (0x80 >> y)) == 0) + if ((*p & (0x80 >> y)) == 0) { break; + } } } @@ -457,41 +478,44 @@ in6_mask2len(mask, lim0) * remaining bits. */ if (p < lim) { - if (y != 0 && (*p & (0x00ff >> y)) != 0) - return (-1); - for (p = p + 1; p < lim; p++) - if (*p != 0) - return (-1); + if (y != 0 && (*p & (0x00ff >> y)) != 0) { + return -1; + } + for (p = p + 1; p < lim; p++) { + if (*p != 0) { + return -1; + } + } } - return (x * 8 + y); + return x * 8 + y; } void -in6_len2mask(mask, len) - struct in6_addr *mask; - int len; +in6_len2mask(struct in6_addr *mask, int len) { int i; - bzero(mask, sizeof (*mask)); - for (i = 0; i < len / 8; i++) + bzero(mask, sizeof(*mask)); + for (i = 0; i < len / 8; i++) { mask->s6_addr8[i] = 0xff; - if (len % 8) + } + if (len % 8) { mask->s6_addr8[i] = (0xff00 >> (len % 8)) & 0xff; + } } void in6_aliasreq_64_to_32(struct in6_aliasreq_64 *src, struct in6_aliasreq_32 *dst) { - bzero(dst, sizeof (*dst)); - bcopy(src->ifra_name, dst->ifra_name, sizeof (dst->ifra_name)); + bzero(dst, sizeof(*dst)); + bcopy(src->ifra_name, dst->ifra_name, sizeof(dst->ifra_name)); dst->ifra_addr = src->ifra_addr; dst->ifra_dstaddr = src->ifra_dstaddr; dst->ifra_prefixmask = src->ifra_prefixmask; dst->ifra_flags = src->ifra_flags; - dst->ifra_lifetime.ia6t_expire = src->ifra_lifetime.ia6t_expire; - dst->ifra_lifetime.ia6t_preferred = src->ifra_lifetime.ia6t_preferred; + dst->ifra_lifetime.ia6t_expire = (u_int32_t)src->ifra_lifetime.ia6t_expire; + dst->ifra_lifetime.ia6t_preferred = (u_int32_t)src->ifra_lifetime.ia6t_preferred; dst->ifra_lifetime.ia6t_vltime = src->ifra_lifetime.ia6t_vltime; dst->ifra_lifetime.ia6t_pltime = src->ifra_lifetime.ia6t_pltime; } @@ -499,8 +523,8 @@ in6_aliasreq_64_to_32(struct in6_aliasreq_64 *src, struct in6_aliasreq_32 *dst) void in6_aliasreq_32_to_64(struct in6_aliasreq_32 *src, struct in6_aliasreq_64 *dst) { - bzero(dst, sizeof (*dst)); - bcopy(src->ifra_name, dst->ifra_name, sizeof (dst->ifra_name)); + bzero(dst, sizeof(*dst)); + bcopy(src->ifra_name, dst->ifra_name, sizeof(dst->ifra_name)); dst->ifra_addr = src->ifra_addr; dst->ifra_dstaddr = src->ifra_dstaddr; dst->ifra_prefixmask = src->ifra_prefixmask; @@ -512,42 +536,44 @@ in6_aliasreq_32_to_64(struct in6_aliasreq_32 *src, struct in6_aliasreq_64 *dst) } #if defined(__LP64__) -void -in6_llstartreq_32_to_64(struct in6_llstartreq_32 *src, - struct in6_llstartreq_64 *dst) +static void +in6_cgareq_32_to_64(const struct in6_cgareq_32 *src, + struct in6_cgareq_64 *dst) { - bzero(dst, sizeof (*dst)); - bcopy(src->llsr_name, dst->llsr_name, sizeof (dst->llsr_name)); - dst->llsr_flags = src->llsr_flags; - bcopy(src->llsr_cgaprep.cga_modifier.octets, - dst->llsr_cgaprep.cga_modifier.octets, - sizeof (dst->llsr_cgaprep.cga_modifier.octets)); - dst->llsr_cgaprep.cga_security_level = - src->llsr_cgaprep.cga_security_level; - dst->llsr_lifetime.ia6t_expire = src->llsr_lifetime.ia6t_expire; - dst->llsr_lifetime.ia6t_preferred = src->llsr_lifetime.ia6t_preferred; - dst->llsr_lifetime.ia6t_vltime = src->llsr_lifetime.ia6t_vltime; - dst->llsr_lifetime.ia6t_pltime = src->llsr_lifetime.ia6t_pltime; + bzero(dst, sizeof(*dst)); + bcopy(src->cgar_name, dst->cgar_name, sizeof(dst->cgar_name)); + dst->cgar_flags = src->cgar_flags; + bcopy(src->cgar_cgaprep.cga_modifier.octets, + dst->cgar_cgaprep.cga_modifier.octets, + sizeof(dst->cgar_cgaprep.cga_modifier.octets)); + dst->cgar_cgaprep.cga_security_level = + src->cgar_cgaprep.cga_security_level; + dst->cgar_lifetime.ia6t_expire = src->cgar_lifetime.ia6t_expire; + dst->cgar_lifetime.ia6t_preferred = src->cgar_lifetime.ia6t_preferred; + dst->cgar_lifetime.ia6t_vltime = src->cgar_lifetime.ia6t_vltime; + dst->cgar_lifetime.ia6t_pltime = src->cgar_lifetime.ia6t_pltime; + dst->cgar_collision_count = src->cgar_collision_count; } #endif #if !defined(__LP64__) -void -in6_llstartreq_64_to_32(struct in6_llstartreq_64 *src, - struct in6_llstartreq_32 *dst) +static void +in6_cgareq_64_to_32(const struct in6_cgareq_64 *src, + struct in6_cgareq_32 *dst) { - bzero(dst, sizeof (*dst)); - bcopy(src->llsr_name, dst->llsr_name, sizeof (dst->llsr_name)); - dst->llsr_flags = src->llsr_flags; - bcopy(src->llsr_cgaprep.cga_modifier.octets, - dst->llsr_cgaprep.cga_modifier.octets, - sizeof (dst->llsr_cgaprep.cga_modifier.octets)); - dst->llsr_cgaprep.cga_security_level = - src->llsr_cgaprep.cga_security_level; - dst->llsr_lifetime.ia6t_expire = src->llsr_lifetime.ia6t_expire; - dst->llsr_lifetime.ia6t_preferred = src->llsr_lifetime.ia6t_preferred; - dst->llsr_lifetime.ia6t_vltime = src->llsr_lifetime.ia6t_vltime; - dst->llsr_lifetime.ia6t_pltime = src->llsr_lifetime.ia6t_pltime; + bzero(dst, sizeof(*dst)); + bcopy(src->cgar_name, dst->cgar_name, sizeof(dst->cgar_name)); + dst->cgar_flags = src->cgar_flags; + bcopy(src->cgar_cgaprep.cga_modifier.octets, + dst->cgar_cgaprep.cga_modifier.octets, + sizeof(dst->cgar_cgaprep.cga_modifier.octets)); + dst->cgar_cgaprep.cga_security_level = + src->cgar_cgaprep.cga_security_level; + dst->cgar_lifetime.ia6t_expire = src->cgar_lifetime.ia6t_expire; + dst->cgar_lifetime.ia6t_preferred = src->cgar_lifetime.ia6t_preferred; + dst->cgar_lifetime.ia6t_vltime = src->cgar_lifetime.ia6t_vltime; + dst->cgar_lifetime.ia6t_pltime = src->cgar_lifetime.ia6t_pltime; + dst->cgar_collision_count = src->cgar_collision_count; } #endif @@ -555,38 +581,42 @@ static struct in6_aliasreq * in6_aliasreq_to_native(void *data, int data_is_64, struct in6_aliasreq *dst) { #if defined(__LP64__) - if (data_is_64) - bcopy(data, dst, sizeof (*dst)); - else + if (data_is_64) { + bcopy(data, dst, sizeof(*dst)); + } else { in6_aliasreq_32_to_64((struct in6_aliasreq_32 *)data, (struct in6_aliasreq_64 *)dst); + } #else - if (data_is_64) + if (data_is_64) { in6_aliasreq_64_to_32((struct in6_aliasreq_64 *)data, (struct in6_aliasreq_32 *)dst); - else - bcopy(data, dst, sizeof (*dst)); + } else { + bcopy(data, dst, sizeof(*dst)); + } #endif /* __LP64__ */ - return (dst); + return dst; } -static struct in6_llstartreq * -in6_llstartreq_to_native(void *data, int is64, struct in6_llstartreq *dst) +void +in6_cgareq_copy_from_user(const void *user_data, int user_is_64, + struct in6_cgareq *cgareq) { #if defined(__LP64__) - if (is64) - bcopy(data, dst, sizeof (*dst)); - else - in6_llstartreq_32_to_64((struct in6_llstartreq_32 *)data, - (struct in6_llstartreq_64 *)dst); + if (user_is_64) { + bcopy(user_data, cgareq, sizeof(*cgareq)); + } else { + in6_cgareq_32_to_64((const struct in6_cgareq_32 *)user_data, + (struct in6_cgareq_64 *)cgareq); + } #else - if (is64) - in6_llstartreq_64_to_32((struct in6_llstartreq_64 *)data, - (struct in6_llstartreq_32 *)dst); - else - bcopy(data, dst, sizeof (*dst)); + if (user_is_64) { + in6_cgareq_64_to_32((const struct in6_cgareq_64 *)user_data, + (struct in6_cgareq_32 *)cgareq); + } else { + bcopy(user_data, cgareq, sizeof(*cgareq)); + } #endif /* __LP64__ */ - return (dst); } static __attribute__((noinline)) int @@ -601,19 +631,21 @@ in6ctl_associd(struct socket *so, u_long cmd, caddr_t data) VERIFY(so != NULL); switch (cmd) { - case SIOCGASSOCIDS32: { /* struct so_aidreq32 */ - bcopy(data, &u.a32, sizeof (u.a32)); + case SIOCGASSOCIDS32: { /* struct so_aidreq32 */ + bcopy(data, &u.a32, sizeof(u.a32)); error = in6_getassocids(so, &u.a32.sar_cnt, u.a32.sar_aidp); - if (error == 0) - bcopy(&u.a32, data, sizeof (u.a32)); + if (error == 0) { + bcopy(&u.a32, data, sizeof(u.a32)); + } break; } - case SIOCGASSOCIDS64: { /* struct so_aidreq64 */ - bcopy(data, &u.a64, sizeof (u.a64)); + case SIOCGASSOCIDS64: { /* struct so_aidreq64 */ + bcopy(data, &u.a64, sizeof(u.a64)); error = in6_getassocids(so, &u.a64.sar_cnt, u.a64.sar_aidp); - if (error == 0) - bcopy(&u.a64, data, sizeof (u.a64)); + if (error == 0) { + bcopy(&u.a64, data, sizeof(u.a64)); + } break; } @@ -622,7 +654,7 @@ in6ctl_associd(struct socket *so, u_long cmd, caddr_t data) /* NOTREACHED */ } - return (error); + return error; } static __attribute__((noinline)) int @@ -637,21 +669,23 @@ in6ctl_connid(struct socket *so, u_long cmd, caddr_t data) VERIFY(so != NULL); switch (cmd) { - case SIOCGCONNIDS32: { /* struct so_cidreq32 */ - bcopy(data, &u.c32, sizeof (u.c32)); + case SIOCGCONNIDS32: { /* struct so_cidreq32 */ + bcopy(data, &u.c32, sizeof(u.c32)); error = in6_getconnids(so, u.c32.scr_aid, &u.c32.scr_cnt, u.c32.scr_cidp); - if (error == 0) - bcopy(&u.c32, data, sizeof (u.c32)); + if (error == 0) { + bcopy(&u.c32, data, sizeof(u.c32)); + } break; } - case SIOCGCONNIDS64: { /* struct so_cidreq64 */ - bcopy(data, &u.c64, sizeof (u.c64)); + case SIOCGCONNIDS64: { /* struct so_cidreq64 */ + bcopy(data, &u.c64, sizeof(u.c64)); error = in6_getconnids(so, u.c64.scr_aid, &u.c64.scr_cnt, u.c64.scr_cidp); - if (error == 0) - bcopy(&u.c64, data, sizeof (u.c64)); + if (error == 0) { + bcopy(&u.c64, data, sizeof(u.c64)); + } break; } @@ -660,7 +694,7 @@ in6ctl_connid(struct socket *so, u_long cmd, caddr_t data) /* NOTREACHED */ } - return (error); + return error; } static __attribute__((noinline)) int @@ -675,27 +709,29 @@ in6ctl_conninfo(struct socket *so, u_long cmd, caddr_t data) VERIFY(so != NULL); switch (cmd) { - case SIOCGCONNINFO32: { /* struct so_cinforeq32 */ - bcopy(data, &u.ci32, sizeof (u.ci32)); + case SIOCGCONNINFO32: { /* struct so_cinforeq32 */ + bcopy(data, &u.ci32, sizeof(u.ci32)); error = in6_getconninfo(so, u.ci32.scir_cid, &u.ci32.scir_flags, &u.ci32.scir_ifindex, &u.ci32.scir_error, u.ci32.scir_src, &u.ci32.scir_src_len, u.ci32.scir_dst, &u.ci32.scir_dst_len, &u.ci32.scir_aux_type, u.ci32.scir_aux_data, &u.ci32.scir_aux_len); - if (error == 0) - bcopy(&u.ci32, data, sizeof (u.ci32)); + if (error == 0) { + bcopy(&u.ci32, data, sizeof(u.ci32)); + } break; } - case SIOCGCONNINFO64: { /* struct so_cinforeq64 */ - bcopy(data, &u.ci64, sizeof (u.ci64)); + case SIOCGCONNINFO64: { /* struct so_cinforeq64 */ + bcopy(data, &u.ci64, sizeof(u.ci64)); error = in6_getconninfo(so, u.ci64.scir_cid, &u.ci64.scir_flags, &u.ci64.scir_ifindex, &u.ci64.scir_error, u.ci64.scir_src, &u.ci64.scir_src_len, u.ci64.scir_dst, &u.ci64.scir_dst_len, &u.ci64.scir_aux_type, u.ci64.scir_aux_data, &u.ci64.scir_aux_len); - if (error == 0) - bcopy(&u.ci64, data, sizeof (u.ci64)); + if (error == 0) { + bcopy(&u.ci64, data, sizeof(u.ci64)); + } break; } @@ -704,7 +740,7 @@ in6ctl_conninfo(struct socket *so, u_long cmd, caddr_t data) /* NOTREACHED */ } - return (error); + return error; } static __attribute__((noinline)) int @@ -717,8 +753,8 @@ in6ctl_llstart(struct ifnet *ifp, u_long cmd, caddr_t data) VERIFY(ifp != NULL); switch (cmd) { - case SIOCLL_START_32: /* struct in6_aliasreq_32 */ - case SIOCLL_START_64: /* struct in6_aliasreq_64 */ + case SIOCLL_START_32: /* struct in6_aliasreq_32 */ + case SIOCLL_START_64: /* struct in6_aliasreq_64 */ is64 = (cmd == SIOCLL_START_64); /* * Convert user ifra to the kernel form, when appropriate. @@ -733,18 +769,18 @@ in6ctl_llstart(struct ifnet *ifp, u_long cmd, caddr_t data) * be done here. They are currently done in in6_ifattach_aux() * for the interfaces that need it. */ - if ((ifp->if_eflags & IFEF_NOAUTOIPV6LL) != 0 && - ifra->ifra_addr.sin6_family == AF_INET6 && + if (ifra->ifra_addr.sin6_family == AF_INET6 && /* Only check ifra_dstaddr if valid */ (ifra->ifra_dstaddr.sin6_len == 0 || ifra->ifra_dstaddr.sin6_family == AF_INET6)) { - /* some interfaces may provide LinkLocal addresses */ + /* some interfaces may provide LinkLocal addresses */ error = in6_ifattach_aliasreq(ifp, NULL, ifra); } else { error = in6_ifattach_aliasreq(ifp, NULL, NULL); } - if (error == 0) + if (error == 0) { in6_if_up_dad_start(ifp); + } break; default: @@ -752,7 +788,7 @@ in6ctl_llstart(struct ifnet *ifp, u_long cmd, caddr_t data) /* NOTREACHED */ } - return (error); + return error; } static __attribute__((noinline)) int @@ -765,31 +801,32 @@ in6ctl_llstop(struct ifnet *ifp) /* Remove link local addresses from interface */ lck_rw_lock_exclusive(&in6_ifaddr_rwlock); - ia = in6_ifaddrs; - while (ia != NULL) { - if (ia->ia_ifa.ifa_ifp != ifp) { - ia = ia->ia_next; - continue; - } - IFA_LOCK(&ia->ia_ifa); - if (IN6_IS_ADDR_LINKLOCAL(&ia->ia_addr.sin6_addr)) { - IFA_ADDREF_LOCKED(&ia->ia_ifa); /* for us */ + boolean_t from_begining = TRUE; + while (from_begining) { + from_begining = FALSE; + TAILQ_FOREACH(ia, &in6_ifaddrhead, ia6_link) { + if (ia->ia_ifa.ifa_ifp != ifp) { + continue; + } + IFA_LOCK(&ia->ia_ifa); + if (IN6_IS_ADDR_LINKLOCAL(&ia->ia_addr.sin6_addr)) { + IFA_ADDREF_LOCKED(&ia->ia_ifa); /* for us */ + IFA_UNLOCK(&ia->ia_ifa); + lck_rw_done(&in6_ifaddr_rwlock); + in6_purgeaddr(&ia->ia_ifa); + IFA_REMREF(&ia->ia_ifa); /* for us */ + lck_rw_lock_exclusive(&in6_ifaddr_rwlock); + /* + * Purging the address caused in6_ifaddr_rwlock + * to be dropped and reacquired; + * therefore search again from the beginning + * of in6_ifaddrs list. + */ + from_begining = TRUE; + break; + } IFA_UNLOCK(&ia->ia_ifa); - lck_rw_done(&in6_ifaddr_rwlock); - in6_purgeaddr(&ia->ia_ifa); - IFA_REMREF(&ia->ia_ifa); /* for us */ - lck_rw_lock_exclusive(&in6_ifaddr_rwlock); - /* - * Purging the address caused in6_ifaddr_rwlock - * to be dropped and reacquired; - * therefore search again from the beginning - * of in6_ifaddrs list. - */ - ia = in6_ifaddrs; - continue; } - IFA_UNLOCK(&ia->ia_ifa); - ia = ia->ia_next; } lck_rw_done(&in6_ifaddr_rwlock); @@ -798,7 +835,7 @@ in6ctl_llstop(struct ifnet *ifp) pr0.ndpr_plen = 64; pr0.ndpr_ifp = ifp; pr0.ndpr_prefix.sin6_addr.s6_addr16[0] = IPV6_ADDR_INT16_ULL; - in6_setscope(&pr0.ndpr_prefix.sin6_addr, ifp, NULL); + (void)in6_setscope(&pr0.ndpr_prefix.sin6_addr, ifp, NULL); pr = nd6_prefix_lookup(&pr0, ND6_PREFIX_EXPIRY_UNSPEC); if (pr) { lck_mtx_lock(nd6_mutex); @@ -809,38 +846,42 @@ in6ctl_llstop(struct ifnet *ifp) lck_mtx_unlock(nd6_mutex); } - return (0); + return 0; } +/* + * This routine configures secure link local address + */ static __attribute__((noinline)) int in6ctl_cgastart(struct ifnet *ifp, u_long cmd, caddr_t data) { - struct in6_llstartreq llsr; + struct in6_cgareq llcgasr; int is64, error = 0; VERIFY(ifp != NULL); switch (cmd) { - case SIOCLL_CGASTART_32: /* struct in6_llstartreq_32 */ - case SIOCLL_CGASTART_64: /* struct in6_llstartreq_64 */ + case SIOCLL_CGASTART_32: /* struct in6_cgareq_32 */ + case SIOCLL_CGASTART_64: /* struct in6_cgareq_64 */ is64 = (cmd == SIOCLL_CGASTART_64); /* - * Convert user llstartreq to the kernel form, when appropriate. + * Convert user cgareq to the kernel form, when appropriate. * This allows the conversion between different data models * to be centralized, so that it can be passed around to other * routines that are expecting the kernel form. */ - in6_llstartreq_to_native(data, is64, &llsr); + in6_cgareq_copy_from_user(data, is64, &llcgasr); /* * NOTE: All the interface specific DLIL attachements * should be done here. They are currently done in - * in6_ifattach_llstartreq() for the interfaces that + * in6_ifattach_cgareq() for the interfaces that * need it. */ - error = in6_ifattach_llstartreq(ifp, &llsr); - if (error == 0) + error = in6_ifattach_llcgareq(ifp, &llcgasr); + if (error == 0) { in6_if_up_dad_start(ifp); + } break; default: @@ -848,7 +889,7 @@ in6ctl_cgastart(struct ifnet *ifp, u_long cmd, caddr_t data) /* NOTREACHED */ } - return (error); + return error; } /* @@ -865,20 +906,22 @@ in6ctl_gifaddr(struct ifnet *ifp, struct in6_ifaddr *ia, u_long cmd, VERIFY(ifp != NULL); - if (ia == NULL) - return (EADDRNOTAVAIL); + if (ia == NULL) { + return EADDRNOTAVAIL; + } switch (cmd) { - case SIOCGIFADDR_IN6: /* struct in6_ifreq */ + case SIOCGIFADDR_IN6: /* struct in6_ifreq */ IFA_LOCK(&ia->ia_ifa); - bcopy(&ia->ia_addr, &addr, sizeof (addr)); + bcopy(&ia->ia_addr, &addr, sizeof(addr)); IFA_UNLOCK(&ia->ia_ifa); - if ((error = sa6_recoverscope(&addr, TRUE)) != 0) + if ((error = sa6_recoverscope(&addr, TRUE)) != 0) { break; - bcopy(&addr, &ifr->ifr_addr, sizeof (addr)); + } + bcopy(&addr, &ifr->ifr_addr, sizeof(addr)); break; - case SIOCGIFDSTADDR_IN6: /* struct in6_ifreq */ + case SIOCGIFDSTADDR_IN6: /* struct in6_ifreq */ if (!(ifp->if_flags & IFF_POINTOPOINT)) { error = EINVAL; break; @@ -888,11 +931,12 @@ in6ctl_gifaddr(struct ifnet *ifp, struct in6_ifaddr *ia, u_long cmd, * an error? */ IFA_LOCK(&ia->ia_ifa); - bcopy(&ia->ia_dstaddr, &addr, sizeof (addr)); + bcopy(&ia->ia_dstaddr, &addr, sizeof(addr)); IFA_UNLOCK(&ia->ia_ifa); - if ((error = sa6_recoverscope(&addr, TRUE)) != 0) + if ((error = sa6_recoverscope(&addr, TRUE)) != 0) { break; - bcopy(&addr, &ifr->ifr_dstaddr, sizeof (addr)); + } + bcopy(&addr, &ifr->ifr_dstaddr, sizeof(addr)); break; default: @@ -900,7 +944,7 @@ in6ctl_gifaddr(struct ifnet *ifp, struct in6_ifaddr *ia, u_long cmd, /* NOTREACHED */ } - return (error); + return error; } /* @@ -917,29 +961,29 @@ in6ctl_gifstat(struct ifnet *ifp, u_long cmd, struct in6_ifreq *ifr) index = ifp->if_index; switch (cmd) { - case SIOCGIFSTAT_IN6: /* struct in6_ifreq */ + case SIOCGIFSTAT_IN6: /* struct in6_ifreq */ /* N.B.: if_inet6data is never freed once set. */ if (IN6_IFEXTRA(ifp) == NULL) { /* return (EAFNOSUPPORT)? */ bzero(&ifr->ifr_ifru.ifru_stat, - sizeof (ifr->ifr_ifru.ifru_stat)); + sizeof(ifr->ifr_ifru.ifru_stat)); } else { bcopy(&IN6_IFEXTRA(ifp)->in6_ifstat, &ifr->ifr_ifru.ifru_stat, - sizeof (ifr->ifr_ifru.ifru_stat)); + sizeof(ifr->ifr_ifru.ifru_stat)); } break; - case SIOCGIFSTAT_ICMP6: /* struct in6_ifreq */ + case SIOCGIFSTAT_ICMP6: /* struct in6_ifreq */ /* N.B.: if_inet6data is never freed once set. */ if (IN6_IFEXTRA(ifp) == NULL) { /* return (EAFNOSUPPORT)? */ - bzero(&ifr->ifr_ifru.ifru_stat, - sizeof (ifr->ifr_ifru.ifru_icmp6stat)); + bzero(&ifr->ifr_ifru.ifru_icmp6stat, + sizeof(ifr->ifr_ifru.ifru_icmp6stat)); } else { bcopy(&IN6_IFEXTRA(ifp)->icmp6_ifstat, &ifr->ifr_ifru.ifru_icmp6stat, - sizeof (ifr->ifr_ifru.ifru_icmp6stat)); + sizeof(ifr->ifr_ifru.ifru_icmp6stat)); } break; @@ -948,7 +992,7 @@ in6ctl_gifstat(struct ifnet *ifp, u_long cmd, struct in6_ifreq *ifr) /* NOTREACHED */ } - return (error); + return error; } /* @@ -965,44 +1009,45 @@ in6ctl_alifetime(struct in6_ifaddr *ia, u_long cmd, struct in6_ifreq *ifr, struct timeval caltime; int error = 0; - if (ia == NULL) - return (EADDRNOTAVAIL); + if (ia == NULL) { + return EADDRNOTAVAIL; + } switch (cmd) { - case SIOCGIFALIFETIME_IN6: /* struct in6_ifreq */ + case SIOCGIFALIFETIME_IN6: /* struct in6_ifreq */ IFA_LOCK(&ia->ia_ifa); /* retrieve time as calendar time (last arg is 1) */ in6ifa_getlifetime(ia, &ia6_lt, 1); if (p64) { struct in6_addrlifetime_64 lt; - bzero(<, sizeof (lt)); + bzero(<, sizeof(lt)); lt.ia6t_expire = ia6_lt.ia6t_expire; lt.ia6t_preferred = ia6_lt.ia6t_preferred; lt.ia6t_vltime = ia6_lt.ia6t_vltime; lt.ia6t_pltime = ia6_lt.ia6t_pltime; - bcopy(<, &ifr->ifr_ifru.ifru_lifetime, sizeof (lt)); + bcopy(<, &ifr->ifr_ifru.ifru_lifetime, sizeof(ifr->ifr_ifru.ifru_lifetime)); } else { struct in6_addrlifetime_32 lt; - bzero(<, sizeof (lt)); + bzero(<, sizeof(lt)); lt.ia6t_expire = (uint32_t)ia6_lt.ia6t_expire; lt.ia6t_preferred = (uint32_t)ia6_lt.ia6t_preferred; lt.ia6t_vltime = (uint32_t)ia6_lt.ia6t_vltime; lt.ia6t_pltime = (uint32_t)ia6_lt.ia6t_pltime; - bcopy(<, &ifr->ifr_ifru.ifru_lifetime, sizeof (lt)); + bcopy(<, &ifr->ifr_ifru.ifru_lifetime, sizeof(ifr->ifr_ifru.ifru_lifetime)); } IFA_UNLOCK(&ia->ia_ifa); break; - case SIOCSIFALIFETIME_IN6: /* struct in6_ifreq */ + case SIOCSIFALIFETIME_IN6: /* struct in6_ifreq */ getmicrotime(&caltime); /* sanity for overflow - beware unsigned */ if (p64) { struct in6_addrlifetime_64 lt; - bcopy(&ifr->ifr_ifru.ifru_lifetime, <, sizeof (lt)); + bcopy(&ifr->ifr_ifru.ifru_lifetime, <, sizeof(lt)); if (lt.ia6t_vltime != ND6_INFINITE_LIFETIME && lt.ia6t_vltime + caltime.tv_sec < caltime.tv_sec) { error = EINVAL; @@ -1016,7 +1061,7 @@ in6ctl_alifetime(struct in6_ifaddr *ia, u_long cmd, struct in6_ifreq *ifr, } else { struct in6_addrlifetime_32 lt; - bcopy(&ifr->ifr_ifru.ifru_lifetime, <, sizeof (lt)); + bcopy(&ifr->ifr_ifru.ifru_lifetime, <, sizeof(lt)); if (lt.ia6t_vltime != ND6_INFINITE_LIFETIME && lt.ia6t_vltime + caltime.tv_sec < caltime.tv_sec) { error = EINVAL; @@ -1033,7 +1078,7 @@ in6ctl_alifetime(struct in6_ifaddr *ia, u_long cmd, struct in6_ifreq *ifr, if (p64) { struct in6_addrlifetime_64 lt; - bcopy(&ifr->ifr_ifru.ifru_lifetime, <, sizeof (lt)); + bcopy(&ifr->ifr_ifru.ifru_lifetime, <, sizeof(lt)); ia6_lt.ia6t_expire = lt.ia6t_expire; ia6_lt.ia6t_preferred = lt.ia6t_preferred; ia6_lt.ia6t_vltime = lt.ia6t_vltime; @@ -1041,22 +1086,24 @@ in6ctl_alifetime(struct in6_ifaddr *ia, u_long cmd, struct in6_ifreq *ifr, } else { struct in6_addrlifetime_32 lt; - bcopy(&ifr->ifr_ifru.ifru_lifetime, <, sizeof (lt)); + bcopy(&ifr->ifr_ifru.ifru_lifetime, <, sizeof(lt)); ia6_lt.ia6t_expire = (uint32_t)lt.ia6t_expire; ia6_lt.ia6t_preferred = (uint32_t)lt.ia6t_preferred; ia6_lt.ia6t_vltime = lt.ia6t_vltime; ia6_lt.ia6t_pltime = lt.ia6t_pltime; } /* for sanity */ - if (ia6_lt.ia6t_vltime != ND6_INFINITE_LIFETIME) + if (ia6_lt.ia6t_vltime != ND6_INFINITE_LIFETIME) { ia6_lt.ia6t_expire = timenow + ia6_lt.ia6t_vltime; - else + } else { ia6_lt.ia6t_expire = 0; + } - if (ia6_lt.ia6t_pltime != ND6_INFINITE_LIFETIME) + if (ia6_lt.ia6t_pltime != ND6_INFINITE_LIFETIME) { ia6_lt.ia6t_preferred = timenow + ia6_lt.ia6t_pltime; - else + } else { ia6_lt.ia6t_preferred = 0; + } in6ifa_setlifetime(ia, &ia6_lt); IFA_UNLOCK(&ia->ia_ifa); @@ -1067,10 +1114,95 @@ in6ctl_alifetime(struct in6_ifaddr *ia, u_long cmd, struct in6_ifreq *ifr, /* NOTREACHED */ } - return (error); + return error; +} + +static int +in6ctl_clat46start(struct ifnet *ifp) +{ + struct nd_prefix *pr = NULL; + struct nd_prefix *next = NULL; + struct in6_ifaddr *ia6 = NULL; + int error = 0; + + if (ifp == lo_ifp) { + return EINVAL; + } + /* + * Traverse the list of prefixes and find the first non-linklocal + * prefix on the interface. + * For that found eligible prefix, configure a CLAT46 reserved address. + */ + lck_mtx_lock(nd6_mutex); + for (pr = nd_prefix.lh_first; pr; pr = next) { + next = pr->ndpr_next; + + NDPR_LOCK(pr); + if (pr->ndpr_ifp != ifp) { + NDPR_UNLOCK(pr); + continue; + } + + if (IN6_IS_ADDR_LINKLOCAL(&pr->ndpr_prefix.sin6_addr)) { + NDPR_UNLOCK(pr); + continue; /* XXX */ + } + + if (pr->ndpr_raf_auto == 0) { + NDPR_UNLOCK(pr); + continue; + } + + if (pr->ndpr_stateflags & NDPRF_DEFUNCT) { + NDPR_UNLOCK(pr); + continue; + } + + if ((pr->ndpr_stateflags & NDPRF_CLAT46) == 0 + && pr->ndpr_vltime != 0) { + NDPR_ADDREF(pr); /* Take reference for rest of the processing */ + NDPR_UNLOCK(pr); + break; + } else { + NDPR_UNLOCK(pr); + continue; + } + } + lck_mtx_unlock(nd6_mutex); + + if (pr != NULL) { + if ((ia6 = in6_pfx_newpersistaddr(pr, FALSE, &error, + TRUE, CLAT46_COLLISION_COUNT_OFFSET)) == NULL) { + nd6log0(error, + "Could not configure CLAT46 address on" + " interface %s.\n", ifp->if_xname); + } else { + IFA_LOCK(&ia6->ia_ifa); + NDPR_LOCK(pr); + ia6->ia6_ndpr = pr; + NDPR_ADDREF(pr); /* for addr reference */ + pr->ndpr_stateflags |= NDPRF_CLAT46; + pr->ndpr_addrcnt++; + VERIFY(pr->ndpr_addrcnt != 0); + NDPR_UNLOCK(pr); + IFA_UNLOCK(&ia6->ia_ifa); + IFA_REMREF(&ia6->ia_ifa); + ia6 = NULL; + /* + * A newly added address might affect the status + * of other addresses, so we check and update it. + * XXX: what if address duplication happens? + */ + lck_mtx_lock(nd6_mutex); + pfxlist_onlink_check(); + lck_mtx_unlock(nd6_mutex); + } + NDPR_REMREF(pr); + } + return error; } -#define ifa2ia6(ifa) ((struct in6_ifaddr *)(void *)(ifa)) +#define ifa2ia6(ifa) ((struct in6_ifaddr *)(void *)(ifa)) /* * Generic INET6 control operations (ioctl's). @@ -1105,35 +1237,37 @@ in6_control(struct socket *so, u_long cmd, caddr_t data, struct ifnet *ifp, * ioctls which don't require ifp, may require socket. */ switch (cmd) { - case SIOCAADDRCTL_POLICY: /* struct in6_addrpolicy */ - case SIOCDADDRCTL_POLICY: /* struct in6_addrpolicy */ - if (!privileged) - return (EPERM); - return (in6_src_ioctl(cmd, data)); - /* NOTREACHED */ - - case SIOCDRADD_IN6_32: /* struct in6_defrouter_32 */ - case SIOCDRADD_IN6_64: /* struct in6_defrouter_64 */ - case SIOCDRDEL_IN6_32: /* struct in6_defrouter_32 */ - case SIOCDRDEL_IN6_64: /* struct in6_defrouter_64 */ - if (!privileged) - return (EPERM); - return (defrtrlist_ioctl(cmd, data)); - /* NOTREACHED */ - - case SIOCGASSOCIDS32: /* struct so_aidreq32 */ - case SIOCGASSOCIDS64: /* struct so_aidreq64 */ - return (in6ctl_associd(so, cmd, data)); - /* NOTREACHED */ - - case SIOCGCONNIDS32: /* struct so_cidreq32 */ - case SIOCGCONNIDS64: /* struct so_cidreq64 */ - return (in6ctl_connid(so, cmd, data)); - /* NOTREACHED */ + case SIOCAADDRCTL_POLICY: /* struct in6_addrpolicy */ + case SIOCDADDRCTL_POLICY: /* struct in6_addrpolicy */ + if (!privileged) { + return EPERM; + } + return in6_src_ioctl(cmd, data); + /* NOTREACHED */ - case SIOCGCONNINFO32: /* struct so_cinforeq32 */ - case SIOCGCONNINFO64: /* struct so_cinforeq64 */ - return (in6ctl_conninfo(so, cmd, data)); + case SIOCDRADD_IN6_32: /* struct in6_defrouter_32 */ + case SIOCDRADD_IN6_64: /* struct in6_defrouter_64 */ + case SIOCDRDEL_IN6_32: /* struct in6_defrouter_32 */ + case SIOCDRDEL_IN6_64: /* struct in6_defrouter_64 */ + if (!privileged) { + return EPERM; + } + return defrtrlist_ioctl(cmd, data); + /* NOTREACHED */ + + case SIOCGASSOCIDS32: /* struct so_aidreq32 */ + case SIOCGASSOCIDS64: /* struct so_aidreq64 */ + return in6ctl_associd(so, cmd, data); + /* NOTREACHED */ + + case SIOCGCONNIDS32: /* struct so_cidreq32 */ + case SIOCGCONNIDS64: /* struct so_cidreq64 */ + return in6ctl_connid(so, cmd, data); + /* NOTREACHED */ + + case SIOCGCONNINFO32: /* struct so_cinforeq32 */ + case SIOCGCONNINFO64: /* struct so_cinforeq64 */ + return in6ctl_conninfo(so, cmd, data); /* NOTREACHED */ } @@ -1141,8 +1275,9 @@ in6_control(struct socket *so, u_long cmd, caddr_t data, struct ifnet *ifp, * The rest of ioctls require ifp; reject if we don't have one; * return ENXIO to be consistent with ifioctl(). */ - if (ifp == NULL) - return (ENXIO); + if (ifp == NULL) { + return ENXIO; + } /* * Unlock the socket since ifnet_ioctl() may be invoked by @@ -1158,7 +1293,7 @@ in6_control(struct socket *so, u_long cmd, caddr_t data, struct ifnet *ifp, * ioctls which require ifp but not interface address. */ switch (cmd) { - case SIOCAUTOCONF_START: /* struct in6_ifreq */ + case SIOCAUTOCONF_START: /* struct in6_ifreq */ if (!privileged) { error = EPERM; goto done; @@ -1166,7 +1301,7 @@ in6_control(struct socket *so, u_long cmd, caddr_t data, struct ifnet *ifp, error = in6_autoconf(ifp, TRUE); goto done; - case SIOCAUTOCONF_STOP: /* struct in6_ifreq */ + case SIOCAUTOCONF_STOP: /* struct in6_ifreq */ if (!privileged) { error = EPERM; goto done; @@ -1174,8 +1309,8 @@ in6_control(struct socket *so, u_long cmd, caddr_t data, struct ifnet *ifp, error = in6_autoconf(ifp, FALSE); goto done; - case SIOCLL_START_32: /* struct in6_aliasreq_32 */ - case SIOCLL_START_64: /* struct in6_aliasreq_64 */ + case SIOCLL_START_32: /* struct in6_aliasreq_32 */ + case SIOCLL_START_64: /* struct in6_aliasreq_64 */ if (!privileged) { error = EPERM; goto done; @@ -1183,7 +1318,7 @@ in6_control(struct socket *so, u_long cmd, caddr_t data, struct ifnet *ifp, error = in6ctl_llstart(ifp, cmd, data); goto done; - case SIOCLL_STOP: /* struct in6_ifreq */ + case SIOCLL_STOP: /* struct in6_ifreq */ if (!privileged) { error = EPERM; goto done; @@ -1191,19 +1326,57 @@ in6_control(struct socket *so, u_long cmd, caddr_t data, struct ifnet *ifp, error = in6ctl_llstop(ifp); goto done; - case SIOCSETROUTERMODE_IN6: /* struct in6_ifreq */ + case SIOCCLAT46_START: /* struct in6_ifreq */ + if (!privileged) { + error = EPERM; + goto done; + } + error = in6ctl_clat46start(ifp); + if (error == 0) { + if_set_eflags(ifp, IFEF_CLAT46); + } + goto done; + + case SIOCCLAT46_STOP: /* struct in6_ifreq */ if (!privileged) { error = EPERM; goto done; } - bcopy(&((struct in6_ifreq *)(void *)data)->ifr_intval, - &intval, sizeof (intval)); - error = in6_setrouter(ifp, intval); + /* + * Not much to be done here and it might not be needed + * It would usually be done when IPv6 configuration is being + * flushed. + * XXX Probably STOP equivalent is not needed here. + */ + if_clear_eflags(ifp, IFEF_CLAT46); + goto done; + case SIOCGETROUTERMODE_IN6: /* struct in6_ifreq */ + intval = ifp->if_ipv6_router_mode; + bcopy(&intval, &((struct in6_ifreq *)(void *)data)->ifr_intval, + sizeof(intval)); + goto done; + case SIOCSETROUTERMODE_IN6: /* struct in6_ifreq */ + if (!privileged) { + error = EPERM; + goto done; + } + bcopy(&((struct in6_ifreq *)(void *)data)->ifr_intval, + &intval, sizeof(intval)); + switch (intval) { + case IPV6_ROUTER_MODE_DISABLED: + case IPV6_ROUTER_MODE_EXCLUSIVE: + case IPV6_ROUTER_MODE_HYBRID: + break; + default: + error = EINVAL; + goto done; + } + error = in6_setrouter(ifp, (ipv6_router_mode_t)intval); goto done; - case SIOCPROTOATTACH_IN6_32: /* struct in6_aliasreq_32 */ - case SIOCPROTOATTACH_IN6_64: /* struct in6_aliasreq_64 */ + case SIOCPROTOATTACH_IN6_32: /* struct in6_aliasreq_32 */ + case SIOCPROTOATTACH_IN6_64: /* struct in6_aliasreq_64 */ if (!privileged) { error = EPERM; goto done; @@ -1211,7 +1384,7 @@ in6_control(struct socket *so, u_long cmd, caddr_t data, struct ifnet *ifp, error = in6_domifattach(ifp); goto done; - case SIOCPROTODETACH_IN6: /* struct in6_ifreq */ + case SIOCPROTODETACH_IN6: /* struct in6_ifreq */ if (!privileged) { error = EPERM; goto done; @@ -1219,63 +1392,69 @@ in6_control(struct socket *so, u_long cmd, caddr_t data, struct ifnet *ifp, /* Cleanup interface routes and addresses */ in6_purgeif(ifp); - if ((error = proto_unplumb(PF_INET6, ifp))) + if ((error = proto_unplumb(PF_INET6, ifp))) { log(LOG_ERR, "SIOCPROTODETACH_IN6: %s error=%d\n", if_name(ifp), error); + } goto done; - case SIOCSNDFLUSH_IN6: /* struct in6_ifreq */ - case SIOCSPFXFLUSH_IN6: /* struct in6_ifreq */ - case SIOCSRTRFLUSH_IN6: /* struct in6_ifreq */ - case SIOCSDEFIFACE_IN6_32: /* struct in6_ndifreq_32 */ - case SIOCSDEFIFACE_IN6_64: /* struct in6_ndifreq_64 */ - case SIOCSIFINFO_FLAGS: /* struct in6_ndireq */ + case SIOCSNDFLUSH_IN6: /* struct in6_ifreq */ + case SIOCSPFXFLUSH_IN6: /* struct in6_ifreq */ + case SIOCSRTRFLUSH_IN6: /* struct in6_ifreq */ + case SIOCSDEFIFACE_IN6_32: /* struct in6_ndifreq_32 */ + case SIOCSDEFIFACE_IN6_64: /* struct in6_ndifreq_64 */ + case SIOCSIFINFO_FLAGS: /* struct in6_ndireq */ + case SIOCGIFCGAPREP_IN6_32: /* struct in6_cgareq_32 */ + case SIOCGIFCGAPREP_IN6_64: /* struct in6_cgareq_64 */ + case SIOCSIFCGAPREP_IN6_32: /* struct in6_cgareq_32 */ + case SIOCSIFCGAPREP_IN6_64: /* struct in6_cgareq_32 */ if (!privileged) { error = EPERM; goto done; } - /* FALLTHRU */ - case OSIOCGIFINFO_IN6: /* struct in6_ondireq */ - case SIOCGIFINFO_IN6: /* struct in6_ondireq */ - case SIOCGDRLST_IN6_32: /* struct in6_drlist_32 */ - case SIOCGDRLST_IN6_64: /* struct in6_drlist_64 */ - case SIOCGPRLST_IN6_32: /* struct in6_prlist_32 */ - case SIOCGPRLST_IN6_64: /* struct in6_prlist_64 */ - case SIOCGNBRINFO_IN6_32: /* struct in6_nbrinfo_32 */ - case SIOCGNBRINFO_IN6_64: /* struct in6_nbrinfo_64 */ - case SIOCGDEFIFACE_IN6_32: /* struct in6_ndifreq_32 */ - case SIOCGDEFIFACE_IN6_64: /* struct in6_ndifreq_64 */ + OS_FALLTHROUGH; + case OSIOCGIFINFO_IN6: /* struct in6_ondireq */ + case SIOCGIFINFO_IN6: /* struct in6_ondireq */ + case SIOCGDRLST_IN6_32: /* struct in6_drlist_32 */ + case SIOCGDRLST_IN6_64: /* struct in6_drlist_64 */ + case SIOCGPRLST_IN6_32: /* struct in6_prlist_32 */ + case SIOCGPRLST_IN6_64: /* struct in6_prlist_64 */ + case SIOCGNBRINFO_IN6_32: /* struct in6_nbrinfo_32 */ + case SIOCGNBRINFO_IN6_64: /* struct in6_nbrinfo_64 */ + case SIOCGDEFIFACE_IN6_32: /* struct in6_ndifreq_32 */ + case SIOCGDEFIFACE_IN6_64: /* struct in6_ndifreq_64 */ error = nd6_ioctl(cmd, data, ifp); goto done; - case SIOCSIFPREFIX_IN6: /* struct in6_prefixreq (deprecated) */ - case SIOCDIFPREFIX_IN6: /* struct in6_prefixreq (deprecated) */ - case SIOCAIFPREFIX_IN6: /* struct in6_rrenumreq (deprecated) */ - case SIOCCIFPREFIX_IN6: /* struct in6_rrenumreq (deprecated) */ - case SIOCSGIFPREFIX_IN6: /* struct in6_rrenumreq (deprecated) */ - case SIOCGIFPREFIX_IN6: /* struct in6_prefixreq (deprecated) */ + case SIOCSIFPREFIX_IN6: /* struct in6_prefixreq (deprecated) */ + case SIOCDIFPREFIX_IN6: /* struct in6_prefixreq (deprecated) */ + case SIOCAIFPREFIX_IN6: /* struct in6_rrenumreq (deprecated) */ + case SIOCCIFPREFIX_IN6: /* struct in6_rrenumreq (deprecated) */ + case SIOCSGIFPREFIX_IN6: /* struct in6_rrenumreq (deprecated) */ + case SIOCGIFPREFIX_IN6: /* struct in6_prefixreq (deprecated) */ log(LOG_NOTICE, "prefix ioctls are now invalidated. " "please use ifconfig.\n"); error = EOPNOTSUPP; goto done; - case SIOCSSCOPE6: /* struct in6_ifreq (deprecated) */ - case SIOCGSCOPE6: /* struct in6_ifreq (deprecated) */ - case SIOCGSCOPE6DEF: /* struct in6_ifreq (deprecated) */ + case SIOCSSCOPE6: /* struct in6_ifreq (deprecated) */ + case SIOCGSCOPE6: /* struct in6_ifreq (deprecated) */ + case SIOCGSCOPE6DEF: /* struct in6_ifreq (deprecated) */ error = EOPNOTSUPP; goto done; - - case SIOCLL_CGASTART_32: /* struct in6_llstartreq_32 */ - case SIOCLL_CGASTART_64: /* struct in6_llstartreq_64 */ - if (!privileged) + + case SIOCLL_CGASTART_32: /* struct in6_cgareq_32 */ + case SIOCLL_CGASTART_64: /* struct in6_cgareq_64 */ + if (!privileged) { error = EPERM; - else + } else { error = in6ctl_cgastart(ifp, cmd, data); + } goto done; - case SIOCGIFSTAT_IN6: /* struct in6_ifreq */ - case SIOCGIFSTAT_ICMP6: /* struct in6_ifreq */ + case SIOCGIFSTAT_IN6: /* struct in6_ifreq */ + case SIOCGIFSTAT_ICMP6: /* struct in6_ifreq */ error = in6ctl_gifstat(ifp, cmd, ifr); goto done; } @@ -1284,9 +1463,9 @@ in6_control(struct socket *so, u_long cmd, caddr_t data, struct ifnet *ifp, * ioctls which require interface address; obtain sockaddr_in6. */ switch (cmd) { - case SIOCSIFADDR_IN6: /* struct in6_ifreq (deprecated) */ - case SIOCSIFDSTADDR_IN6: /* struct in6_ifreq (deprecated) */ - case SIOCSIFNETMASK_IN6: /* struct in6_ifreq (deprecated) */ + case SIOCSIFADDR_IN6: /* struct in6_ifreq (deprecated) */ + case SIOCSIFDSTADDR_IN6: /* struct in6_ifreq (deprecated) */ + case SIOCSIFNETMASK_IN6: /* struct in6_ifreq (deprecated) */ /* * Since IPv6 allows a node to assign multiple addresses * on a single interface, SIOCSIFxxx ioctls are deprecated. @@ -1295,12 +1474,12 @@ in6_control(struct socket *so, u_long cmd, caddr_t data, struct ifnet *ifp, error = EOPNOTSUPP; goto done; - case SIOCAIFADDR_IN6_32: /* struct in6_aliasreq_32 */ - case SIOCAIFADDR_IN6_64: /* struct in6_aliasreq_64 */ + case SIOCAIFADDR_IN6_32: /* struct in6_aliasreq_32 */ + case SIOCAIFADDR_IN6_64: /* struct in6_aliasreq_64 */ if (!privileged) { error = EPERM; goto done; - } + } /* * Convert user ifra to the kernel form, when appropriate. * This allows the conversion between different data models @@ -1309,25 +1488,38 @@ in6_control(struct socket *so, u_long cmd, caddr_t data, struct ifnet *ifp, */ ifra = in6_aliasreq_to_native(data, (cmd == SIOCAIFADDR_IN6_64), &sifra); - bcopy(&ifra->ifra_addr, &sin6, sizeof (sin6)); + bcopy(&ifra->ifra_addr, &sin6, sizeof(sin6)); sa6 = &sin6; break; - case SIOCDIFADDR_IN6: /* struct in6_ifreq */ - case SIOCSIFALIFETIME_IN6: /* struct in6_ifreq */ + case SIOCDIFADDR_IN6: /* struct in6_ifreq */ + case SIOCSIFALIFETIME_IN6: /* struct in6_ifreq */ if (!privileged) { error = EPERM; goto done; } - /* FALLTHRU */ - case SIOCGIFADDR_IN6: /* struct in6_ifreq */ - case SIOCGIFDSTADDR_IN6: /* struct in6_ifreq */ - case SIOCGIFNETMASK_IN6: /* struct in6_ifreq */ - case SIOCGIFAFLAG_IN6: /* struct in6_ifreq */ - case SIOCGIFALIFETIME_IN6: /* struct in6_ifreq */ - bcopy(&ifr->ifr_addr, &sin6, sizeof (sin6)); + OS_FALLTHROUGH; + case SIOCGIFADDR_IN6: /* struct in6_ifreq */ + case SIOCGIFDSTADDR_IN6: /* struct in6_ifreq */ + case SIOCGIFNETMASK_IN6: /* struct in6_ifreq */ + case SIOCGIFAFLAG_IN6: /* struct in6_ifreq */ + case SIOCGIFALIFETIME_IN6: /* struct in6_ifreq */ + bcopy(&ifr->ifr_addr, &sin6, sizeof(sin6)); sa6 = &sin6; break; + case SIOCGIFDSTADDR: + case SIOCSIFDSTADDR: + case SIOCGIFBRDADDR: + case SIOCSIFBRDADDR: + case SIOCGIFNETMASK: + case SIOCSIFNETMASK: + case SIOCGIFADDR: + case SIOCSIFADDR: + case SIOCAIFADDR: + case SIOCDIFADDR: + /* Do not handle these AF_INET commands in AF_INET6 path */ + error = EINVAL; + goto done; } /* @@ -1376,14 +1568,14 @@ in6_control(struct socket *so, u_long cmd, caddr_t data, struct ifnet *ifp, * SIOCDIFADDR_IN6/SIOCAIFADDR_IN6 specific tests. */ switch (cmd) { - case SIOCDIFADDR_IN6: /* struct in6_ifreq */ + case SIOCDIFADDR_IN6: /* struct in6_ifreq */ if (ia == NULL) { error = EADDRNOTAVAIL; goto done; } - /* FALLTHROUGH */ - case SIOCAIFADDR_IN6_32: /* struct in6_aliasreq_32 */ - case SIOCAIFADDR_IN6_64: /* struct in6_aliasreq_64 */ + OS_FALLTHROUGH; + case SIOCAIFADDR_IN6_32: /* struct in6_aliasreq_32 */ + case SIOCAIFADDR_IN6_64: /* struct in6_aliasreq_64 */ VERIFY(sa6 != NULL); /* * We always require users to specify a valid IPv6 address for @@ -1391,7 +1583,7 @@ in6_control(struct socket *so, u_long cmd, caddr_t data, struct ifnet *ifp, * since SIOCDIFADDR_IN6 falls thru above. */ if (sa6->sin6_family != AF_INET6 || - sa6->sin6_len != sizeof (struct sockaddr_in6)) { + sa6->sin6_len != sizeof(struct sockaddr_in6)) { error = EAFNOSUPPORT; goto done; } @@ -1402,42 +1594,42 @@ in6_control(struct socket *so, u_long cmd, caddr_t data, struct ifnet *ifp, * And finally process address-related ioctls. */ switch (cmd) { - case SIOCGIFADDR_IN6: /* struct in6_ifreq */ - /* This interface is basically deprecated. use SIOCGIFCONF. */ - /* FALLTHRU */ - case SIOCGIFDSTADDR_IN6: /* struct in6_ifreq */ + case SIOCGIFADDR_IN6: /* struct in6_ifreq */ + /* This interface is basically deprecated. use SIOCGIFCONF. */ + /* FALLTHRU */ + case SIOCGIFDSTADDR_IN6: /* struct in6_ifreq */ error = in6ctl_gifaddr(ifp, ia, cmd, ifr); break; - case SIOCGIFNETMASK_IN6: /* struct in6_ifreq */ + case SIOCGIFNETMASK_IN6: /* struct in6_ifreq */ if (ia != NULL) { IFA_LOCK(&ia->ia_ifa); bcopy(&ia->ia_prefixmask, &ifr->ifr_addr, - sizeof (struct sockaddr_in6)); + sizeof(struct sockaddr_in6)); IFA_UNLOCK(&ia->ia_ifa); } else { error = EADDRNOTAVAIL; } break; - case SIOCGIFAFLAG_IN6: /* struct in6_ifreq */ + case SIOCGIFAFLAG_IN6: /* struct in6_ifreq */ if (ia != NULL) { IFA_LOCK(&ia->ia_ifa); bcopy(&ia->ia6_flags, &ifr->ifr_ifru.ifru_flags6, - sizeof (ifr->ifr_ifru.ifru_flags6)); + sizeof(ifr->ifr_ifru.ifru_flags6)); IFA_UNLOCK(&ia->ia_ifa); } else { error = EADDRNOTAVAIL; } break; - case SIOCGIFALIFETIME_IN6: /* struct in6_ifreq */ - case SIOCSIFALIFETIME_IN6: /* struct in6_ifreq */ + case SIOCGIFALIFETIME_IN6: /* struct in6_ifreq */ + case SIOCSIFALIFETIME_IN6: /* struct in6_ifreq */ error = in6ctl_alifetime(ia, cmd, ifr, p64); break; - case SIOCAIFADDR_IN6_32: /* struct in6_aliasreq_32 */ - case SIOCAIFADDR_IN6_64: /* struct in6_aliasreq_64 */ + case SIOCAIFADDR_IN6_32: /* struct in6_aliasreq_32 */ + case SIOCAIFADDR_IN6_64: /* struct in6_aliasreq_64 */ error = in6ctl_aifaddr(ifp, ifra); break; @@ -1451,18 +1643,21 @@ in6_control(struct socket *so, u_long cmd, caddr_t data, struct ifnet *ifp, } done: - if (ia != NULL) + if (ia != NULL) { IFA_REMREF(&ia->ia_ifa); - if (so_unlocked) + } + if (so_unlocked) { socket_lock(so, 0); + } - return (error); + return error; } static __attribute__((noinline)) int in6ctl_aifaddr(struct ifnet *ifp, struct in6_aliasreq *ifra) { - int i, error, addtmp, plen; + int i, error, addtmp; + uint8_t plen; struct nd_prefix pr0, *pr; struct in6_ifaddr *ia; @@ -1474,8 +1669,9 @@ in6ctl_aifaddr(struct ifnet *ifp, struct in6_aliasreq *ifra) if (error == 0) { /* PF_INET6 wasn't previously attached */ error = in6_ifattach_aliasreq(ifp, NULL, NULL); - if (error != 0) + if (error != 0) { goto done; + } in6_if_up_dad_start(ifp); } else if (error != EEXIST) { @@ -1487,14 +1683,16 @@ in6ctl_aifaddr(struct ifnet *ifp, struct in6_aliasreq *ifra) * to the list. */ error = in6_update_ifa(ifp, ifra, 0, &ia); - if (error != 0) + if (error != 0) { goto done; + } VERIFY(ia != NULL); /* Now, make the prefix on-link on the interface. */ - plen = in6_mask2len(&ifra->ifra_prefixmask.sin6_addr, NULL); - if (plen == 128) + plen = (uint8_t)in6_mask2len(&ifra->ifra_prefixmask.sin6_addr, NULL); + if (plen == 128) { goto done; + } /* * NOTE: We'd rather create the prefix before the address, but we need @@ -1506,7 +1704,7 @@ in6ctl_aifaddr(struct ifnet *ifp, struct in6_aliasreq *ifra) * Convert mask to prefix length (prefixmask has already been validated * in in6_update_ifa(). */ - bzero(&pr0, sizeof (pr0)); + bzero(&pr0, sizeof(pr0)); pr0.ndpr_plen = plen; pr0.ndpr_ifp = ifp; pr0.ndpr_prefix = ifra->ifra_addr; @@ -1526,8 +1724,13 @@ in6ctl_aifaddr(struct ifnet *ifp, struct in6_aliasreq *ifra) */ pr0.ndpr_raf_onlink = 1; /* should be configurable? */ pr0.ndpr_raf_auto = !!(ifra->ifra_flags & IN6_IFF_AUTOCONF); - pr0.ndpr_vltime = ifra->ifra_lifetime.ia6t_vltime; - pr0.ndpr_pltime = ifra->ifra_lifetime.ia6t_pltime; + if (ifra->ifra_flags & (IN6_IFF_AUTOCONF | IN6_IFF_DYNAMIC)) { + pr0.ndpr_vltime = ifra->ifra_lifetime.ia6t_vltime; + pr0.ndpr_pltime = ifra->ifra_lifetime.ia6t_pltime; + } else { + pr0.ndpr_vltime = ND6_INFINITE_LIFETIME; + pr0.ndpr_pltime = ND6_INFINITE_LIFETIME; + } pr0.ndpr_stateflags |= NDPRF_STATIC; lck_mtx_init(&pr0.ndpr_lock, ifa_mtx_grp, ifa_mtx_attr); @@ -1538,8 +1741,9 @@ in6ctl_aifaddr(struct ifnet *ifp, struct in6_aliasreq *ifra) * route. */ error = nd6_prelist_add(&pr0, NULL, &pr, FALSE); - if (error != 0) + if (error != 0) { goto done; + } if (pr == NULL) { log(LOG_ERR, "%s: nd6_prelist_add okay, but" @@ -1558,7 +1762,7 @@ in6ctl_aifaddr(struct ifnet *ifp, struct in6_aliasreq *ifra) ++pr->ndpr_addrcnt; VERIFY(pr->ndpr_addrcnt != 0); ia->ia6_ndpr = pr; - NDPR_ADDREF_LOCKED(pr); /* for addr reference */ + NDPR_ADDREF(pr); /* for addr reference */ /* * If this is the first autoconf address from the prefix, @@ -1577,10 +1781,11 @@ in6ctl_aifaddr(struct ifnet *ifp, struct in6_aliasreq *ifra) if (addtmp) { int e; e = in6_tmpifadd(ia, 1); - if (e != 0) + if (e != 0) { log(LOG_NOTICE, "%s: failed to create a" " temporary address, error=%d\n", __func__, e); + } } /* @@ -1595,9 +1800,10 @@ in6ctl_aifaddr(struct ifnet *ifp, struct in6_aliasreq *ifra) NDPR_REMREF(pr); done: - if (ia != NULL) + if (ia != NULL) { IFA_REMREF(&ia->ia_ifa); - return (error); + } + return error; } static __attribute__((noinline)) void @@ -1619,9 +1825,9 @@ in6ctl_difaddr(struct ifnet *ifp, struct in6_ifaddr *ia) * Note that in6_purgeaddr() will decrement ndpr_addrcnt. */ IFA_LOCK(&ia->ia_ifa); - bzero(&pr0, sizeof (pr0)); + bzero(&pr0, sizeof(pr0)); pr0.ndpr_ifp = ifp; - pr0.ndpr_plen = in6_mask2len(&ia->ia_prefixmask.sin6_addr, NULL); + pr0.ndpr_plen = (uint8_t)in6_mask2len(&ia->ia_prefixmask.sin6_addr, NULL); if (pr0.ndpr_plen == 128) { IFA_UNLOCK(&ia->ia_ifa); goto purgeaddr; @@ -1659,8 +1865,9 @@ in6_autoconf(struct ifnet *ifp, int enable) VERIFY(ifp != NULL); - if (ifp->if_flags & IFF_LOOPBACK) - return (EINVAL); + if (ifp->if_flags & IFF_LOOPBACK) { + return EINVAL; + } if (enable) { /* @@ -1670,67 +1877,75 @@ in6_autoconf(struct ifnet *ifp, int enable) * SIOCAUTOCONF_START from being set in that mode. */ ifnet_lock_exclusive(ifp); - if (ifp->if_eflags & IFEF_IPV6_ROUTER) { - ifp->if_eflags &= ~IFEF_ACCEPT_RTADV; + if (ifp->if_ipv6_router_mode == IPV6_ROUTER_MODE_EXCLUSIVE) { + if_clear_eflags(ifp, IFEF_ACCEPT_RTADV); error = EBUSY; } else { - ifp->if_eflags |= IFEF_ACCEPT_RTADV; + if_set_eflags(ifp, IFEF_ACCEPT_RTADV); } ifnet_lock_done(ifp); } else { struct in6_ifaddr *ia = NULL; - ifnet_lock_exclusive(ifp); - ifp->if_eflags &= ~IFEF_ACCEPT_RTADV; - ifnet_lock_done(ifp); + if_clear_eflags(ifp, IFEF_ACCEPT_RTADV); /* Remove autoconfigured address from interface */ lck_rw_lock_exclusive(&in6_ifaddr_rwlock); - ia = in6_ifaddrs; - while (ia != NULL) { - if (ia->ia_ifa.ifa_ifp != ifp) { - ia = ia->ia_next; - continue; - } - IFA_LOCK(&ia->ia_ifa); - if (ia->ia6_flags & IN6_IFF_AUTOCONF) { - IFA_ADDREF_LOCKED(&ia->ia_ifa); /* for us */ + boolean_t from_begining = TRUE; + while (from_begining) { + from_begining = FALSE; + TAILQ_FOREACH(ia, &in6_ifaddrhead, ia6_link) { + if (ia->ia_ifa.ifa_ifp != ifp) { + continue; + } + IFA_LOCK(&ia->ia_ifa); + if (ia->ia6_flags & IN6_IFF_AUTOCONF) { + IFA_ADDREF_LOCKED(&ia->ia_ifa); /* for us */ + IFA_UNLOCK(&ia->ia_ifa); + lck_rw_done(&in6_ifaddr_rwlock); + in6_purgeaddr(&ia->ia_ifa); + IFA_REMREF(&ia->ia_ifa); /* for us */ + lck_rw_lock_exclusive(&in6_ifaddr_rwlock); + /* + * Purging the address caused in6_ifaddr_rwlock + * to be dropped and reacquired; + * therefore search again from the beginning + * of in6_ifaddrs list. + */ + from_begining = TRUE; + break; + } IFA_UNLOCK(&ia->ia_ifa); - lck_rw_done(&in6_ifaddr_rwlock); - in6_purgeaddr(&ia->ia_ifa); - IFA_REMREF(&ia->ia_ifa); /* for us */ - lck_rw_lock_exclusive(&in6_ifaddr_rwlock); - /* - * Purging the address caused in6_ifaddr_rwlock - * to be dropped and reacquired; - * therefore search again from the beginning - * of in6_ifaddrs list. - */ - ia = in6_ifaddrs; - continue; } - IFA_UNLOCK(&ia->ia_ifa); - ia = ia->ia_next; } lck_rw_done(&in6_ifaddr_rwlock); } - return (error); + return error; } /* - * Handle SIOCSETROUTERMODE_IN6 to set or clear the IPv6 router mode flag on - * the interface. Entering or exiting this mode will result in the removal of + * Handle SIOCSETROUTERMODE_IN6 to set the IPv6 router mode on the interface + * Entering or exiting IPV6_ROUTER_MODE_EXCLUSIVE will result in the removal of * autoconfigured IPv6 addresses on the interface. */ static __attribute__((noinline)) int -in6_setrouter(struct ifnet *ifp, int enable) +in6_setrouter(struct ifnet *ifp, ipv6_router_mode_t mode) { + int error = 0; + ipv6_router_mode_t prev_mode; + VERIFY(ifp != NULL); - if (ifp->if_flags & IFF_LOOPBACK) - return (ENODEV); + if (ifp->if_flags & IFF_LOOPBACK) { + return ENODEV; + } - if (enable) { + prev_mode = ifp->if_ipv6_router_mode; + if (prev_mode == mode) { + /* no mode change, there's nothing to do */ + return 0; + } + if (mode == IPV6_ROUTER_MODE_EXCLUSIVE) { struct nd_ifinfo *ndi = NULL; ndi = ND_IFINFO(ifp); @@ -1747,21 +1962,17 @@ in6_setrouter(struct ifnet *ifp, int enable) } } - ifnet_lock_exclusive(ifp); - if (enable) { - ifp->if_eflags |= IFEF_IPV6_ROUTER; - } else { - ifp->if_eflags &= ~IFEF_IPV6_ROUTER; - } - ifnet_lock_done(ifp); - + ifp->if_ipv6_router_mode = mode; lck_mtx_lock(nd6_mutex); - defrouter_select(ifp); + defrouter_select(ifp, NULL); lck_mtx_unlock(nd6_mutex); - - if_allmulti(ifp, enable); - - return (in6_autoconf(ifp, FALSE)); + if_allmulti(ifp, (mode == IPV6_ROUTER_MODE_EXCLUSIVE)); + if (mode == IPV6_ROUTER_MODE_EXCLUSIVE || + (prev_mode == IPV6_ROUTER_MODE_EXCLUSIVE + && mode == IPV6_ROUTER_MODE_DISABLED)) { + error = in6_autoconf(ifp, FALSE); + } + return error; } static int @@ -1774,24 +1985,32 @@ in6_to_kamescope(struct sockaddr_in6 *sin6, struct ifnet *ifp) tmp = *sin6; error = in6_recoverscope(&tmp, &sin6->sin6_addr, ifp); - if (error != 0) - return (error); + if (error != 0) { + return error; + } id = in6_addr2scopeid(ifp, &tmp.sin6_addr); - if (tmp.sin6_scope_id == 0) + if (tmp.sin6_scope_id == 0) { tmp.sin6_scope_id = id; - else if (tmp.sin6_scope_id != id) - return (EINVAL); /* scope ID mismatch. */ - + } else if (tmp.sin6_scope_id != id) { + return EINVAL; /* scope ID mismatch. */ + } error = in6_embedscope(&tmp.sin6_addr, &tmp, NULL, NULL, NULL); - if (error != 0) - return (error); + if (error != 0) { + return error; + } tmp.sin6_scope_id = 0; *sin6 = tmp; - return (0); + return 0; } +/* + * When the address is being configured we should clear out certain flags + * coming in from the caller. + */ +#define IN6_IFF_CLR_ADDR_FLAG_MASK (~(IN6_IFF_DEPRECATED | IN6_IFF_DETACHED | IN6_IFF_DUPLICATED)) + static int in6_ifaupdate_aux(struct in6_ifaddr *ia, struct ifnet *ifp, int ifaupflags) { @@ -1807,27 +2026,48 @@ in6_ifaupdate_aux(struct in6_ifaddr *ia, struct ifnet *ifp, int ifaupflags) ifa = &ia->ia_ifa; in6m_sol = NULL; - nd6log2((LOG_DEBUG, "%s - %s ifp %s ia6_flags 0x%x ifaupflags 0x%x\n", + nd6log2(debug, "%s - %s ifp %s ia6_flags 0x%x ifaupflags 0x%x\n", __func__, ip6_sprintf(&ia->ia_addr.sin6_addr), if_name(ia->ia_ifp), ia->ia6_flags, - ifaupflags)); + ifaupflags); + + /* + * Just to be safe, always clear certain flags when address + * is being configured + */ + ia->ia6_flags &= IN6_IFF_CLR_ADDR_FLAG_MASK; /* * Mark the address as tentative before joining multicast addresses, * so that corresponding MLD responses would not have a tentative * source address. */ - ia->ia6_flags &= ~IN6_IFF_DUPLICATED; /* safety */ - if (in6if_do_dad(ifp)) + if (in6if_do_dad(ifp)) { in6_ifaddr_set_dadprogress(ia); + /* + * Do not delay sending neighbor solicitations when using optimistic + * duplicate address detection, c.f. RFC 4429. + */ + if (ia->ia6_flags & IN6_IFF_OPTIMISTIC) { + ifaupflags &= ~IN6_IFAUPDATE_DADDELAY; + } else { + ifaupflags |= IN6_IFAUPDATE_DADDELAY; + } + } else { + /* + * If the interface has been marked to not perform + * DAD, make sure to reset DAD in progress flags + * that may come in from the caller. + */ + ia->ia6_flags &= ~IN6_IFF_DADPROGRESS; + } /* Join necessary multicast groups */ if ((ifp->if_flags & IFF_MULTICAST) != 0) { - /* join solicited multicast addr for new host id */ - bzero(&llsol, sizeof (struct in6_addr)); + bzero(&llsol, sizeof(struct in6_addr)); llsol.s6_addr32[0] = IPV6_ADDR_INT32_MLL; llsol.s6_addr32[1] = 0; llsol.s6_addr32[2] = htonl(1); @@ -1850,10 +2090,10 @@ in6_ifaupdate_aux(struct in6_ifaddr *ia, struct ifnet *ifp, int ifaupflags) } imm = in6_joingroup(ifp, &llsol, &error, delay); if (imm == NULL) { - nd6log((LOG_WARNING, + nd6log(info, "%s: addmulti failed for %s on %s (errno=%d)\n", __func__, ip6_sprintf(&llsol), if_name(ifp), - error)); + error); VERIFY(error != 0); goto unwind; } @@ -1865,22 +2105,22 @@ in6_ifaupdate_aux(struct in6_ifaddr *ia, struct ifnet *ifp, int ifaupflags) LIST_INSERT_HEAD(&ia->ia6_memberships, imm, i6mm_chain); IFA_UNLOCK(ifa); - bzero(&mltmask, sizeof (mltmask)); - mltmask.sin6_len = sizeof (struct sockaddr_in6); + bzero(&mltmask, sizeof(mltmask)); + mltmask.sin6_len = sizeof(struct sockaddr_in6); mltmask.sin6_family = AF_INET6; mltmask.sin6_addr = in6mask32; -#define MLTMASK_LEN 4 /* mltmask's masklen (=32bit=4octet) */ +#define MLTMASK_LEN 4 /* mltmask's masklen (=32bit=4octet) */ /* * join link-local all-nodes address */ - bzero(&mltaddr, sizeof (mltaddr)); - mltaddr.sin6_len = sizeof (struct sockaddr_in6); + bzero(&mltaddr, sizeof(mltaddr)); + mltaddr.sin6_len = sizeof(struct sockaddr_in6); mltaddr.sin6_family = AF_INET6; mltaddr.sin6_addr = in6addr_linklocal_allnodes; - if ((error = in6_setscope(&mltaddr.sin6_addr, ifp, NULL)) != 0) + if ((error = in6_setscope(&mltaddr.sin6_addr, ifp, NULL)) != 0) { goto unwind; /* XXX: should not fail */ - + } /* * XXX: do we really need this automatic routes? * We should probably reconsider this stuff. Most applications @@ -1902,18 +2142,19 @@ in6_ifaupdate_aux(struct in6_ifaddr *ia, struct ifnet *ifp, int ifaupflags) (struct sockaddr *)&ia->ia_addr, (struct sockaddr *)&mltmask, RTF_UP | RTF_CLONING, NULL, ia->ia_ifp->if_index); - if (error) + if (error) { goto unwind; + } } else { rtfree(rt); } imm = in6_joingroup(ifp, &mltaddr.sin6_addr, &error, 0); if (!imm) { - nd6log((LOG_WARNING, + nd6log(info, "%s: addmulti failed for %s on %s (errno=%d)\n", __func__, ip6_sprintf(&mltaddr.sin6_addr), - if_name(ifp), error)); + if_name(ifp), error); VERIFY(error != 0); goto unwind; } @@ -1924,7 +2165,7 @@ in6_ifaupdate_aux(struct in6_ifaddr *ia, struct ifnet *ifp, int ifaupflags) /* * join node information group address */ -#define hostnamelen strlen(hostname) +#define hostnamelen strlen(hostname) delay = 0; if ((ifaupflags & IN6_IFAUPDATE_DADDELAY)) { /* @@ -1933,16 +2174,18 @@ in6_ifaupdate_aux(struct in6_ifaddr *ia, struct ifnet *ifp, int ifaupflags) */ delay = random() % MAX_RTR_SOLICITATION_DELAY; } - if (in6_nigroup(ifp, hostname, hostnamelen, &mltaddr.sin6_addr) - == 0) { + lck_mtx_lock(&hostname_lock); + int n = in6_nigroup(ifp, hostname, hostnamelen, &mltaddr.sin6_addr); + lck_mtx_unlock(&hostname_lock); + if (n == 0) { imm = in6_joingroup(ifp, &mltaddr.sin6_addr, &error, delay); /* XXX jinmei */ if (!imm) { - nd6log((LOG_WARNING, + nd6log(info, "%s: addmulti failed for %s on %s " "(errno=%d)\n", __func__, ip6_sprintf(&mltaddr.sin6_addr), - if_name(ifp), error)); + if_name(ifp), error); /* XXX not very fatal, go on... */ error = 0; } else { @@ -1959,8 +2202,9 @@ in6_ifaupdate_aux(struct in6_ifaddr *ia, struct ifnet *ifp, int ifaupflags) * (ff01::1%ifN, and ff01::%ifN/32) */ mltaddr.sin6_addr = in6addr_nodelocal_allnodes; - if ((error = in6_setscope(&mltaddr.sin6_addr, ifp, NULL)) != 0) + if ((error = in6_setscope(&mltaddr.sin6_addr, ifp, NULL)) != 0) { goto unwind; /* XXX: should not fail */ + } /* XXX: again, do we really need the route? */ rt = rtalloc1_scoped((struct sockaddr *)&mltaddr, 0, 0UL, ia->ia_ifp->if_index); @@ -1977,42 +2221,82 @@ in6_ifaupdate_aux(struct in6_ifaddr *ia, struct ifnet *ifp, int ifaupflags) (struct sockaddr *)&ia->ia_addr, (struct sockaddr *)&mltmask, RTF_UP | RTF_CLONING, NULL, ia->ia_ifp->if_index); - if (error) + if (error) { goto unwind; - } else + } + } else { rtfree(rt); + } imm = in6_joingroup(ifp, &mltaddr.sin6_addr, &error, 0); if (!imm) { - nd6log((LOG_WARNING, + nd6log(info, "%s: addmulti failed for %s on %s (errno=%d)\n", __func__, ip6_sprintf(&mltaddr.sin6_addr), - if_name(ifp), error)); + if_name(ifp), error); VERIFY(error != 0); goto unwind; } IFA_LOCK(ifa); LIST_INSERT_HEAD(&ia->ia6_memberships, imm, i6mm_chain); IFA_UNLOCK(ifa); - } -#undef MLTMASK_LEN +#undef MLTMASK_LEN - /* Ensure nd6_service() is scheduled as soon as it's convenient */ - ++nd6_sched_timeout_want; + /* + * create a ff00::/8 route + */ + bzero(&mltmask, sizeof(mltmask)); + mltmask.sin6_len = sizeof(struct sockaddr_in6); + mltmask.sin6_family = AF_INET6; + mltmask.sin6_addr = in6mask8; +#define MLTMASK_LEN_8_BITS 1 /* ff00::/8 mltmask's masklen (=8bit=1octet) */ - /* - * Perform DAD, if needed. - * XXX It may be of use, if we can administratively - * disable DAD. - */ - IFA_LOCK_SPIN(ifa); - if (in6if_do_dad(ifp) && ((ifa->ifa_flags & IN6_IFF_NODAD) == 0) && - (ia->ia6_flags & IN6_IFF_DADPROGRESS)) { - int mindelay, maxdelay; - int *delayptr, delayval; + bzero(&mltaddr, sizeof(mltaddr)); + mltaddr.sin6_len = sizeof(struct sockaddr_in6); + mltaddr.sin6_family = AF_INET6; + mltaddr.sin6_addr = in6addr_multicast_prefix; - IFA_UNLOCK(ifa); - delayptr = NULL; + rt = rtalloc1_scoped((struct sockaddr *)&mltaddr, 0, 0UL, + ia->ia_ifp->if_index); + if (rt) { + if (memcmp(&mltaddr.sin6_addr, &((struct sockaddr_in6 *) + (void *)rt_key(rt))->sin6_addr, MLTMASK_LEN_8_BITS)) { + rtfree(rt); + rt = NULL; + } + } + if (!rt) { + error = rtrequest_scoped(RTM_ADD, + (struct sockaddr *)&mltaddr, + (struct sockaddr *)&ia->ia_addr, + (struct sockaddr *)&mltmask, RTF_UP | RTF_CLONING, + NULL, ia->ia_ifp->if_index); + if (error) { + goto unwind; + } + } else { + rtfree(rt); + } + } +#undef MLTMASK_LEN_8_BITS + + /* Ensure nd6_service() is scheduled as soon as it's convenient */ + ++nd6_sched_timeout_want; + + /* + * Perform DAD, if: + * * Interface is marked to perform DAD, AND + * * Address is not marked to skip DAD, AND + * * Address is in a pre-DAD state (Tentative or Optimistic) + */ + IFA_LOCK_SPIN(ifa); + if (in6if_do_dad(ifp) && (ia->ia6_flags & IN6_IFF_NODAD) == 0 && + (ia->ia6_flags & IN6_IFF_DADPROGRESS) != 0) { + int mindelay, maxdelay; + int *delayptr, delayval; + + IFA_UNLOCK(ifa); + delayptr = NULL; /* * Avoid the DAD delay if the caller wants us to skip it. * This is not compliant with RFC 2461, but it's only being @@ -2032,14 +2316,15 @@ in6_ifaupdate_aux(struct in6_ifaddr *ia, struct ifnet *ifp, int ifaupflags) if (in6m_sol != NULL) { IN6M_LOCK(in6m_sol); if (in6m_sol->in6m_state == - MLD_REPORTING_MEMBER) + MLD_REPORTING_MEMBER) { mindelay = in6m_sol->in6m_timer; + } IN6M_UNLOCK(in6m_sol); } maxdelay = MAX_RTR_SOLICITATION_DELAY * hz; - if (maxdelay - mindelay == 0) + if (maxdelay - mindelay == 0) { delayval = 0; - else { + } else { delayval = (random() % (maxdelay - mindelay)) + mindelay; @@ -2060,9 +2345,10 @@ unwind: done: /* release reference held for this routine */ - if (in6m_sol != NULL) + if (in6m_sol != NULL) { IN6M_REMREF(in6m_sol); - return (error); + } + return error; } /* @@ -2093,20 +2379,20 @@ in6_update_ifa(struct ifnet *ifp, struct in6_aliasreq *ifra, int ifaupflags, * the corresponding operation. */ if (ifra->ifra_addr.sin6_family != AF_INET6 || - ifra->ifra_addr.sin6_len != sizeof (struct sockaddr_in6)) { + ifra->ifra_addr.sin6_len != sizeof(struct sockaddr_in6)) { error = EAFNOSUPPORT; goto unwind; } /* Validate ifra_prefixmask.sin6_len is properly bounded. */ if (ifra->ifra_prefixmask.sin6_len == 0 || - ifra->ifra_prefixmask.sin6_len > sizeof (struct sockaddr_in6)) { + ifra->ifra_prefixmask.sin6_len > sizeof(struct sockaddr_in6)) { error = EINVAL; goto unwind; } /* Validate prefix length extracted from ifra_prefixmask structure. */ - plen = in6_mask2len(&ifra->ifra_prefixmask.sin6_addr, + plen = (uint8_t)in6_mask2len(&ifra->ifra_prefixmask.sin6_addr, (u_char *)&ifra->ifra_prefixmask + ifra->ifra_prefixmask.sin6_len); if (plen <= 0) { error = EINVAL; @@ -2137,14 +2423,34 @@ in6_update_ifa(struct ifnet *ifp, struct in6_aliasreq *ifra, int ifaupflags, * address already exists. If so, then we don't allocate and link a * new one here. */ - ia = in6ifa_ifpwithaddr(ifp, &ifra->ifra_addr.sin6_addr); - if (ia != NULL) + struct sockaddr_in6 lookup_address = ifra->ifra_addr; + if (IN6_IS_ADDR_LINKLOCAL(&lookup_address.sin6_addr)) { + if (lookup_address.sin6_addr.s6_addr16[1] == 0) { + /* link ID is not embedded by the user */ + lookup_address.sin6_addr.s6_addr16[1] = + htons(ifp->if_index); + } else if (lookup_address.sin6_addr.s6_addr16[1] != + htons(ifp->if_index)) { + error = EINVAL; /* link ID contradicts */ + goto done; + } + if (lookup_address.sin6_scope_id != 0 && + lookup_address.sin6_scope_id != + (u_int32_t)ifp->if_index) { + error = EINVAL; + goto done; + } + } + + ia = in6ifa_ifpwithaddr(ifp, &lookup_address.sin6_addr); + if (ia != NULL) { ifa = &ia->ia_ifa; + } /* * Validate destination address on interface types that require it. */ - if ((ifp->if_flags & (IFF_LOOPBACK|IFF_POINTOPOINT)) != 0) { + if ((ifp->if_flags & (IFF_LOOPBACK | IFF_POINTOPOINT)) != 0) { switch (ifra->ifra_dstaddr.sin6_family) { case AF_INET6: if (plen != 128) { @@ -2175,13 +2481,13 @@ in6_update_ifa(struct ifnet *ifp, struct in6_aliasreq *ifra, int ifaupflags, timenow = net_uptime(); if (ia == NULL) { - int how; + zalloc_flags_t how; /* Is this the first new IPv6 address for the interface? */ ifaupflags |= IN6_IFAUPDATE_NEWADDR; /* Allocate memory for IPv6 interface address structure. */ - how = !(ifaupflags & IN6_IFAUPDATE_NOWAIT) ? M_WAITOK : 0; + how = (ifaupflags & IN6_IFAUPDATE_NOWAIT) ? Z_NOWAIT : Z_WAITOK; ia = in6_ifaddr_alloc(how); if (ia == NULL) { error = ENOBUFS; @@ -2204,10 +2510,12 @@ in6_update_ifa(struct ifnet *ifp, struct in6_aliasreq *ifra, int ifaupflags, * Please enjoy the dancing sea turtle. */ IFA_ADDREF(ifa); /* for this and optionally for caller */ + IA6_HASH_INIT(ia); ifa->ifa_addr = (struct sockaddr *)&ia->ia_addr; if (ifra->ifra_dstaddr.sin6_family == AF_INET6 || - (ifp->if_flags & (IFF_POINTOPOINT | IFF_LOOPBACK)) != 0) + (ifp->if_flags & (IFF_POINTOPOINT | IFF_LOOPBACK)) != 0) { ifa->ifa_dstaddr = (struct sockaddr *)&ia->ia_dstaddr; + } ifa->ifa_netmask = (struct sockaddr *)&ia->ia_prefixmask; ifa->ifa_ifp = ifp; ifa->ifa_metric = ifp->if_metric; @@ -2215,19 +2523,21 @@ in6_update_ifa(struct ifnet *ifp, struct in6_aliasreq *ifra, int ifaupflags, LIST_INIT(&ia->ia6_memberships); ia->ia_addr.sin6_family = AF_INET6; - ia->ia_addr.sin6_len = sizeof (ia->ia_addr); + ia->ia_addr.sin6_len = sizeof(ia->ia_addr); ia->ia_addr.sin6_addr = ifra->ifra_addr.sin6_addr; ia->ia_prefixmask.sin6_family = AF_INET6; - ia->ia_prefixmask.sin6_len = sizeof (ia->ia_prefixmask); + ia->ia_prefixmask.sin6_len = sizeof(ia->ia_prefixmask); ia->ia_prefixmask.sin6_addr = ifra->ifra_prefixmask.sin6_addr; error = in6_to_kamescope(&ia->ia_addr, ifp); - if (error != 0) + if (error != 0) { goto unwind; + } if (ifa->ifa_dstaddr != NULL) { ia->ia_dstaddr = ifra->ifra_dstaddr; error = in6_to_kamescope(&ia->ia_dstaddr, ifp); - if (error != 0) + if (error != 0) { goto unwind; + } } /* Append to address chains */ @@ -2249,20 +2559,12 @@ in6_update_ifa(struct ifnet *ifp, struct in6_aliasreq *ifra, int ifaupflags, ifnet_lock_done(ifp); lck_rw_lock_exclusive(&in6_ifaddr_rwlock); - if (in6_ifaddrs != NULL) { - struct in6_ifaddr *iac; - for (iac = in6_ifaddrs; iac->ia_next != NULL; - iac = iac->ia_next) - continue; - iac->ia_next = ia; - } else { - in6_ifaddrs = ia; - } + TAILQ_INSERT_TAIL(&in6_ifaddrhead, ia, ia6_link); IFA_ADDREF(ifa); /* hold for in6_ifaddrs link */ lck_rw_done(&in6_ifaddr_rwlock); } else { ifa = &ia->ia_ifa; - ifaupflags &= ~(IN6_IFAUPDATE_NEWADDR|IN6_IFAUPDATE_1STADDR); + ifaupflags &= ~(IN6_IFAUPDATE_NEWADDR | IN6_IFAUPDATE_1STADDR); } VERIFY(ia != NULL && ifa == &ia->ia_ifa); @@ -2275,14 +2577,16 @@ in6_update_ifa(struct ifnet *ifp, struct in6_aliasreq *ifra, int ifaupflags, */ ia->ia6_updatetime = ia->ia6_createtime = timenow; ia6_lt = *lt; - if (ia6_lt.ia6t_vltime != ND6_INFINITE_LIFETIME) + if (ia6_lt.ia6t_vltime != ND6_INFINITE_LIFETIME) { ia6_lt.ia6t_expire = timenow + ia6_lt.ia6t_vltime; - else + } else { ia6_lt.ia6t_expire = 0; - if (ia6_lt.ia6t_pltime != ND6_INFINITE_LIFETIME) + } + if (ia6_lt.ia6t_pltime != ND6_INFINITE_LIFETIME) { ia6_lt.ia6t_preferred = timenow + ia6_lt.ia6t_pltime; - else + } else { ia6_lt.ia6t_preferred = 0; + } in6ifa_setlifetime(ia, &ia6_lt); /* @@ -2294,13 +2598,6 @@ in6_update_ifa(struct ifnet *ifp, struct in6_aliasreq *ifra, int ifaupflags, ia->ia6_lifetime.ia6ti_preferred = timenow; } - /* - * Do not delay sending neighbor solicitations when using optimistic - * duplicate address detection, c.f. RFC 4429. - */ - if ((ia->ia6_flags & IN6_IFF_OPTIMISTIC) == 0) - ifaupflags |= IN6_IFAUPDATE_DADDELAY; - /* * Update flag or prefix length */ @@ -2312,13 +2609,15 @@ in6_update_ifa(struct ifnet *ifp, struct in6_aliasreq *ifra, int ifaupflags, /* Further initialization of the interface address */ error = in6_ifinit(ifp, ia, ifaupflags); - if (error != 0) + if (error != 0) { goto unwind; + } /* Finish updating the address while other tasks are working with it */ error = in6_ifaupdate_aux(ia, ifp, ifaupflags); - if (error != 0) + if (error != 0) { goto unwind; + } /* Return success (optionally w/ address for caller). */ VERIFY(error == 0); @@ -2335,7 +2634,7 @@ unwind: done: *iar = ia; - return (error); + return error; } void @@ -2345,7 +2644,7 @@ in6_purgeaddr(struct ifaddr *ifa) struct in6_ifaddr *ia = (struct in6_ifaddr *)ifa; struct in6_multi_mship *imm; - lck_mtx_assert(nd6_mutex, LCK_MTX_ASSERT_NOTOWNED); + LCK_MTX_ASSERT(nd6_mutex, LCK_MTX_ASSERT_NOTOWNED); /* stop DAD processing */ nd6_dad_stop(ifa); @@ -2399,36 +2698,35 @@ in6_purgeaddr(struct ifaddr *ifa) static void in6_unlink_ifa(struct in6_ifaddr *ia, struct ifnet *ifp) { - struct in6_ifaddr *oia; + struct in6_ifaddr *nia; struct ifaddr *ifa; int unlinked; - lck_mtx_assert(nd6_mutex, LCK_MTX_ASSERT_NOTOWNED); + LCK_MTX_ASSERT(nd6_mutex, LCK_MTX_ASSERT_NOTOWNED); ifa = &ia->ia_ifa; IFA_ADDREF(ifa); ifnet_lock_exclusive(ifp); IFA_LOCK(ifa); - if (ifa->ifa_debug & IFD_ATTACHED) + if (ifa->ifa_debug & IFD_ATTACHED) { if_detach_ifa(ifp, ifa); + } IFA_UNLOCK(ifa); ifnet_lock_done(ifp); - unlinked = 1; + unlinked = 0; lck_rw_lock_exclusive(&in6_ifaddr_rwlock); - oia = ia; - if (oia == (ia = in6_ifaddrs)) { - in6_ifaddrs = ia->ia_next; - } else { - while (ia->ia_next && (ia->ia_next != oia)) - ia = ia->ia_next; - if (ia->ia_next) { - ia->ia_next = oia->ia_next; - } else { - /* search failed */ - log(LOG_NOTICE, "%s: search failed.\n", __func__); - unlinked = 0; + TAILQ_FOREACH(nia, &in6_ifaddrhead, ia6_link) { + if (ia == nia) { + TAILQ_REMOVE(&in6_ifaddrhead, ia, ia6_link); + IFA_LOCK(ifa); + if (IA6_IS_HASHED(ia)) { + in6_iahash_remove(ia); + } + IFA_UNLOCK(ifa); + unlinked = 1; + break; } } @@ -2439,26 +2737,42 @@ in6_unlink_ifa(struct in6_ifaddr *ia, struct ifnet *ifp) * of other (detached) addresses, call * pfxlist_onlink_check(). */ - ifa = &oia->ia_ifa; IFA_LOCK(ifa); - if (oia->ia6_ndpr == NULL) { - log(LOG_NOTICE, "in6_unlink_ifa: IPv6 address " - "0x%llx has no prefix\n", - (uint64_t)VM_KERNEL_ADDRPERM(oia)); - } else { - struct nd_prefix *pr = oia->ia6_ndpr; - oia->ia6_flags &= ~IN6_IFF_AUTOCONF; - oia->ia6_ndpr = NULL; - NDPR_LOCK(pr); - VERIFY(pr->ndpr_addrcnt != 0); - pr->ndpr_addrcnt--; - NDPR_UNLOCK(pr); - NDPR_REMREF(pr); /* release addr reference */ + /* + * Only log the below message for addresses other than + * link local. + * Only one LLA (auto-configured or statically) is allowed + * on an interface. + * LLA prefix, while added to the prefix list, is not + * reference countedi (as it is the only one). + * The prefix also never expires on its own as LLAs + * have infinite lifetime. + * + * For now quiece down the log message for LLAs. + */ + if (!IN6_IS_ADDR_LINKLOCAL(&ia->ia_addr.sin6_addr)) { + if (ia->ia6_ndpr == NULL) { + log(LOG_NOTICE, "in6_unlink_ifa: IPv6 address " + "0x%llx has no prefix\n", + (uint64_t)VM_KERNEL_ADDRPERM(ia)); + } else { + struct nd_prefix *pr = ia->ia6_ndpr; + ia->ia6_flags &= ~IN6_IFF_AUTOCONF; + ia->ia6_ndpr = NULL; + NDPR_LOCK(pr); + VERIFY(pr->ndpr_addrcnt != 0); + pr->ndpr_addrcnt--; + if (ia->ia6_flags & IN6_IFF_CLAT46) { + pr->ndpr_stateflags &= ~NDPRF_CLAT46; + } + NDPR_UNLOCK(pr); + NDPR_REMREF(pr); /* release addr reference */ + } } IFA_UNLOCK(ifa); lck_rw_done(&in6_ifaddr_rwlock); - if ((oia->ia6_flags & IN6_IFF_AUTOCONF) != 0) { + if ((ia->ia6_flags & IN6_IFF_AUTOCONF) != 0) { lck_mtx_lock(nd6_mutex); pfxlist_onlink_check(); lck_mtx_unlock(nd6_mutex); @@ -2468,8 +2782,9 @@ in6_unlink_ifa(struct in6_ifaddr *ia, struct ifnet *ifp) * Do this only if it's not already unlinked in the event that we lost * the race, since in6_ifaddr_rwlock was momentarily dropped above. */ - if (unlinked) + if (unlinked) { IFA_REMREF(ifa); + } /* release reference held for this routine */ IFA_REMREF(ifa); @@ -2483,30 +2798,34 @@ in6_purgeif(struct ifnet *ifp) { struct in6_ifaddr *ia; - if (ifp == NULL) + if (ifp == NULL) { return; + } - lck_mtx_assert(nd6_mutex, LCK_MTX_ASSERT_NOTOWNED); + LCK_MTX_ASSERT(nd6_mutex, LCK_MTX_ASSERT_NOTOWNED); lck_rw_lock_exclusive(&in6_ifaddr_rwlock); - ia = in6_ifaddrs; - while (ia != NULL) { - if (ia->ia_ifa.ifa_ifp != ifp) { - ia = ia->ia_next; - continue; + boolean_t from_begining = TRUE; + while (from_begining) { + from_begining = FALSE; + TAILQ_FOREACH(ia, &in6_ifaddrhead, ia6_link) { + if (ia->ia_ifa.ifa_ifp != ifp) { + continue; + } + IFA_ADDREF(&ia->ia_ifa); /* for us */ + lck_rw_done(&in6_ifaddr_rwlock); + in6_purgeaddr(&ia->ia_ifa); + IFA_REMREF(&ia->ia_ifa); /* for us */ + lck_rw_lock_exclusive(&in6_ifaddr_rwlock); + /* + * Purging the address would have caused + * in6_ifaddr_rwlock to be dropped and reacquired; + * therefore search again from the beginning + * of in6_ifaddrs list. + */ + from_begining = TRUE; + break; } - IFA_ADDREF(&ia->ia_ifa); /* for us */ - lck_rw_done(&in6_ifaddr_rwlock); - in6_purgeaddr(&ia->ia_ifa); - IFA_REMREF(&ia->ia_ifa); /* for us */ - lck_rw_lock_exclusive(&in6_ifaddr_rwlock); - /* - * Purging the address would have caused - * in6_ifaddr_rwlock to be dropped and reacquired; - * therefore search again from the beginning - * of in6_ifaddrs list. - */ - ia = in6_ifaddrs; } lck_rw_done(&in6_ifaddr_rwlock); @@ -2525,6 +2844,19 @@ in6_ifinit(struct ifnet *ifp, struct in6_ifaddr *ia, int ifaupflags) error = 0; ifa = &ia->ia_ifa; + lck_rw_lock_exclusive(&in6_ifaddr_rwlock); + IFA_LOCK(&ia->ia_ifa); + if (IA6_IS_HASHED(ia)) { + in6_iahash_remove(ia); + } + if ((ifp->if_flags & IFF_POINTOPOINT)) { + in6_iahash_insert_ptp(ia); + } else { + in6_iahash_insert(ia); + } + IFA_UNLOCK(&ia->ia_ifa); + lck_rw_done(&in6_ifaddr_rwlock); + /* * NOTE: SIOCSIFADDR is defined with struct ifreq as parameter, * but here we are sending it down to the interface with a pointer @@ -2533,8 +2865,9 @@ in6_ifinit(struct ifnet *ifp, struct in6_ifaddr *ia, int ifaupflags) if ((ifaupflags & IN6_IFAUPDATE_1STADDR) != 0) { error = ifnet_ioctl(ifp, PF_INET6, SIOCSIFADDR, ia); if (error != 0) { - if (error != EOPNOTSUPP) - return (error); + if (error != EOPNOTSUPP) { + goto failed; + } error = 0; } } @@ -2551,8 +2884,9 @@ in6_ifinit(struct ifnet *ifp, struct in6_ifaddr *ia, int ifaupflags) ia->ia_dstaddr.sin6_family == AF_INET6) { IFA_UNLOCK(ifa); error = rtinit(ifa, RTM_ADD, RTF_UP | RTF_HOST); - if (error != 0) - return (error); + if (error != 0) { + goto failed; + } IFA_LOCK(ifa); ia->ia_flags |= IFA_ROUTE; } @@ -2567,14 +2901,26 @@ in6_ifinit(struct ifnet *ifp, struct in6_ifaddr *ia, int ifaupflags) IFA_UNLOCK(ifa); /* Add ownaddr as loopback rtentry, if necessary (ex. on p2p link). */ - if ((ifaupflags & IN6_IFAUPDATE_NEWADDR) != 0) + if ((ifaupflags & IN6_IFAUPDATE_NEWADDR) != 0) { in6_ifaddloop(ifa); + } /* invalidate route caches */ routegenid_inet6_update(); VERIFY(error == 0); - return (0); + return 0; +failed: + VERIFY(error != 0); + lck_rw_lock_exclusive(&in6_ifaddr_rwlock); + IFA_LOCK(&ia->ia_ifa); + if (IA6_IS_HASHED(ia)) { + in6_iahash_remove(ia); + } + IFA_UNLOCK(&ia->ia_ifa); + lck_rw_done(&in6_ifaddr_rwlock); + + return error; } void @@ -2587,9 +2933,7 @@ in6_purgeaddrs(struct ifnet *ifp) * Find an IPv6 interface link-local address specific to an interface. */ struct in6_ifaddr * -in6ifa_ifpforlinklocal(ifp, ignoreflags) - struct ifnet *ifp; - int ignoreflags; +in6ifa_ifpforlinklocal(struct ifnet *ifp, int ignoreflags) { struct ifaddr *ifa; @@ -2607,7 +2951,32 @@ in6ifa_ifpforlinklocal(ifp, ignoreflags) IFA_UNLOCK(ifa); continue; } - IFA_ADDREF_LOCKED(ifa); /* for caller */ + IFA_ADDREF_LOCKED(ifa); /* for caller */ + IFA_UNLOCK(ifa); + break; + } + IFA_UNLOCK(ifa); + } + ifnet_lock_done(ifp); + + return (struct in6_ifaddr *)ifa; +} + +struct in6_ifaddr * +in6ifa_ifpwithflag(struct ifnet * ifp, int flag) +{ + struct ifaddr *ifa; + + ifnet_lock_shared(ifp); + TAILQ_FOREACH(ifa, &ifp->if_addrlist, ifa_list) + { + IFA_LOCK_SPIN(ifa); + if (ifa->ifa_addr->sa_family != AF_INET6) { + IFA_UNLOCK(ifa); + continue; + } + if ((((struct in6_ifaddr *)ifa)->ia6_flags & flag) == flag) { + IFA_ADDREF_LOCKED(ifa); IFA_UNLOCK(ifa); break; } @@ -2615,16 +2984,14 @@ in6ifa_ifpforlinklocal(ifp, ignoreflags) } ifnet_lock_done(ifp); - return ((struct in6_ifaddr *)ifa); + return (struct in6_ifaddr *)ifa; } /* * find the internet address corresponding to a given interface and address. */ struct in6_ifaddr * -in6ifa_ifpwithaddr(ifp, addr) - struct ifnet *ifp; - struct in6_addr *addr; +in6ifa_ifpwithaddr(struct ifnet *ifp, struct in6_addr *addr) { struct ifaddr *ifa; @@ -2637,7 +3004,7 @@ in6ifa_ifpwithaddr(ifp, addr) continue; } if (IN6_ARE_ADDR_EQUAL(addr, IFA_IN6(ifa))) { - IFA_ADDREF_LOCKED(ifa); /* for caller */ + IFA_ADDREF_LOCKED(ifa); /* for caller */ IFA_UNLOCK(ifa); break; } @@ -2645,7 +3012,7 @@ in6ifa_ifpwithaddr(ifp, addr) } ifnet_lock_done(ifp); - return ((struct in6_ifaddr *)ifa); + return (struct in6_ifaddr *)ifa; } struct in6_ifaddr * @@ -2654,10 +3021,10 @@ in6ifa_prproxyaddr(struct in6_addr *addr) struct in6_ifaddr *ia; lck_rw_lock_shared(&in6_ifaddr_rwlock); - for (ia = in6_ifaddrs; ia; ia = ia->ia_next) { + TAILQ_FOREACH(ia, IN6ADDR_HASH(addr), ia6_hash) { IFA_LOCK(&ia->ia_ifa); if (IN6_ARE_ADDR_EQUAL(addr, IFA_IN6(&ia->ia_ifa))) { - IFA_ADDREF_LOCKED(&ia->ia_ifa); /* for caller */ + IFA_ADDREF_LOCKED(&ia->ia_ifa); /* for caller */ IFA_UNLOCK(&ia->ia_ifa); break; } @@ -2670,7 +3037,7 @@ in6ifa_prproxyaddr(struct in6_addr *addr) ia = NULL; } - return (ia); + return ia; } void @@ -2694,22 +3061,26 @@ in6ifa_getlifetime(struct in6_ifaddr *ia6, struct in6_addrlifetime *t_dst, if (iscalendar) { if (t_src->ia6ti_expire != 0 && - t_src->ia6ti_vltime != ND6_INFINITE_LIFETIME) + t_src->ia6ti_vltime != ND6_INFINITE_LIFETIME) { t_dst->ia6t_expire = t_src->ia6ti_base_calendartime + t_src->ia6ti_expire - t_src->ia6ti_base_uptime; + } if (t_src->ia6ti_preferred != 0 && - t_src->ia6ti_pltime != ND6_INFINITE_LIFETIME) + t_src->ia6ti_pltime != ND6_INFINITE_LIFETIME) { t_dst->ia6t_preferred = t_src->ia6ti_base_calendartime + t_src->ia6ti_preferred - t_src->ia6ti_base_uptime; + } } else { if (t_src->ia6ti_expire != 0 && - t_src->ia6ti_vltime != ND6_INFINITE_LIFETIME) + t_src->ia6ti_vltime != ND6_INFINITE_LIFETIME) { t_dst->ia6t_expire = t_src->ia6ti_expire; + } if (t_src->ia6ti_preferred != 0 && - t_src->ia6ti_pltime != ND6_INFINITE_LIFETIME) + t_src->ia6ti_pltime != ND6_INFINITE_LIFETIME) { t_dst->ia6t_preferred = t_src->ia6ti_preferred; + } } } @@ -2757,17 +3128,20 @@ ip6_sprintf(const struct in6_addr *addr) for (i = 0; i < 8; i++) { if (dcolon == 1) { if (*a == 0) { - if (i == 7) + if (i == 7) { *cp++ = ':'; + } a++; continue; - } else + } else { dcolon = 2; + } } if (*a == 0) { if (dcolon == 0 && *(a + 1) == 0) { - if (i == 0) + if (i == 0) { *cp++ = ':'; + } *cp++ = ':'; dcolon = 1; } else { @@ -2791,13 +3165,14 @@ ip6_sprintf(const struct in6_addr *addr) *cp++ = digits[n]; zpad = 1; } - if ((n = *d & 0xf) != 0 || zpad) + if ((n = *d & 0xf) != 0 || zpad) { *cp++ = digits[n]; + } *cp++ = ':'; a++; } *--cp = 0; - return (ip6buf[ip6round]); + return ip6buf[ip6round]; } int @@ -2807,24 +3182,26 @@ in6addr_local(struct in6_addr *in6) struct sockaddr_in6 sin6; int local = 0; - if (IN6_IS_ADDR_LOOPBACK(in6) || IN6_IS_SCOPE_LINKLOCAL(in6)) - return (1); + if (IN6_IS_ADDR_LOOPBACK(in6) || IN6_IS_SCOPE_LINKLOCAL(in6)) { + return 1; + } sin6.sin6_family = AF_INET6; - sin6.sin6_len = sizeof (sin6); - bcopy(in6, &sin6.sin6_addr, sizeof (*in6)); + sin6.sin6_len = sizeof(sin6); + bcopy(in6, &sin6.sin6_addr, sizeof(*in6)); rt = rtalloc1((struct sockaddr *)&sin6, 0, 0); if (rt != NULL) { RT_LOCK_SPIN(rt); - if (rt->rt_gateway->sa_family == AF_LINK) + if (rt->rt_gateway->sa_family == AF_LINK) { local = 1; + } RT_UNLOCK(rt); rtfree(rt); } else { local = in6_localaddr(in6); } - return (local); + return local; } int @@ -2832,45 +3209,23 @@ in6_localaddr(struct in6_addr *in6) { struct in6_ifaddr *ia; - if (IN6_IS_ADDR_LOOPBACK(in6) || IN6_IS_ADDR_LINKLOCAL(in6)) - return (1); + if (IN6_IS_ADDR_LOOPBACK(in6) || IN6_IS_ADDR_LINKLOCAL(in6) || IN6_IS_ADDR_MC_UNICAST_BASED_LINKLOCAL(in6)) { + return 1; + } lck_rw_lock_shared(&in6_ifaddr_rwlock); - for (ia = in6_ifaddrs; ia; ia = ia->ia_next) { + TAILQ_FOREACH(ia, &in6_ifaddrhead, ia6_link) { IFA_LOCK_SPIN(&ia->ia_ifa); if (IN6_ARE_MASKED_ADDR_EQUAL(in6, &ia->ia_addr.sin6_addr, &ia->ia_prefixmask.sin6_addr)) { IFA_UNLOCK(&ia->ia_ifa); lck_rw_done(&in6_ifaddr_rwlock); - return (1); + return 1; } IFA_UNLOCK(&ia->ia_ifa); } lck_rw_done(&in6_ifaddr_rwlock); - return (0); -} - -int -in6_is_addr_deprecated(struct sockaddr_in6 *sa6) -{ - struct in6_ifaddr *ia; - - lck_rw_lock_shared(&in6_ifaddr_rwlock); - for (ia = in6_ifaddrs; ia; ia = ia->ia_next) { - IFA_LOCK_SPIN(&ia->ia_ifa); - if (IN6_ARE_ADDR_EQUAL(&ia->ia_addr.sin6_addr, - &sa6->sin6_addr) && - (ia->ia6_flags & IN6_IFF_DEPRECATED) != 0) { - IFA_UNLOCK(&ia->ia_ifa); - lck_rw_done(&in6_ifaddr_rwlock); - return (1); /* true */ - } - /* XXX: do we still have to go thru the rest of the list? */ - IFA_UNLOCK(&ia->ia_ifa); - } - - lck_rw_done(&in6_ifaddr_rwlock); - return (0); /* false */ + return 0; } /* @@ -2878,56 +3233,55 @@ in6_is_addr_deprecated(struct sockaddr_in6 *sa6) * hard coding... */ int -in6_matchlen(src, dst) -struct in6_addr *src, *dst; +in6_matchlen(struct in6_addr *src, struct in6_addr *dst) { int match = 0; u_char *s = (u_char *)src, *d = (u_char *)dst; u_char *lim = s + 16, r; - while (s < lim) + while (s < lim) { if ((r = (*d++ ^ *s++)) != 0) { while (r < 128) { match++; r <<= 1; } break; - } else + } else { match += 8; - return (match); + } + } + return match; } /* XXX: to be scope conscious */ int -in6_are_prefix_equal(p1, p2, len) - struct in6_addr *p1, *p2; - int len; +in6_are_prefix_equal(struct in6_addr *p1, struct in6_addr *p2, int len) { int bytelen, bitlen; /* sanity check */ if (0 > len || len > 128) { log(LOG_ERR, "%s: invalid prefix length(%d)\n", __func__, len); - return (0); + return 0; } bytelen = len / 8; bitlen = len % 8; - if (bcmp(&p1->s6_addr, &p2->s6_addr, bytelen)) - return (0); + if (bcmp(&p1->s6_addr, &p2->s6_addr, bytelen)) { + return 0; + } if (bitlen != 0 && p1->s6_addr[bytelen] >> (8 - bitlen) != - p2->s6_addr[bytelen] >> (8 - bitlen)) - return (0); + p2->s6_addr[bytelen] >> (8 - bitlen)) { + return 0; + } - return (1); + return 1; } void -in6_prefixlen2mask(maskp, len) - struct in6_addr *maskp; - int len; +in6_prefixlen2mask(struct in6_addr *maskp, int len) { u_char maskarray[8] = {0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff}; int bytelen, bitlen, i; @@ -2938,13 +3292,15 @@ in6_prefixlen2mask(maskp, len) return; } - bzero(maskp, sizeof (*maskp)); + bzero(maskp, sizeof(*maskp)); bytelen = len / 8; bitlen = len % 8; - for (i = 0; i < bytelen; i++) + for (i = 0; i < bytelen; i++) { maskp->s6_addr[i] = 0xff; - if (bitlen) + } + if (bitlen) { maskp->s6_addr[bytelen] = maskarray[bitlen - 1]; + } } /* @@ -2953,14 +3309,14 @@ in6_prefixlen2mask(maskp, len) struct in6_ifaddr * in6_ifawithscope(struct ifnet *oifp, struct in6_addr *dst) { - int dst_scope = in6_addrscope(dst), src_scope, best_scope = 0; + int dst_scope = in6_addrscope(dst), src_scope, best_scope = 0; int blen = -1; struct ifaddr *ifa; struct ifnet *ifp; struct in6_ifaddr *ifa_best = NULL; if (oifp == NULL) { - return (NULL); + return NULL; } /* @@ -2974,8 +3330,9 @@ in6_ifawithscope(struct ifnet *oifp, struct in6_addr *dst) * We can never take an address that breaks the scope zone * of the destination. */ - if (in6_addr2scopeid(ifp, dst) != in6_addr2scopeid(oifp, dst)) + if (in6_addr2scopeid(ifp, dst) != in6_addr2scopeid(oifp, dst)) { continue; + } ifnet_lock_shared(ifp); TAILQ_FOREACH(ifa, &ifp->if_addrlist, ifa_list) { @@ -2993,7 +3350,7 @@ in6_ifawithscope(struct ifnet *oifp, struct in6_addr *dst) * nor a duplicated address. */ if (((struct in6_ifaddr *)ifa)->ia6_flags & - IN6_IFF_NOTREADY) { + (IN6_IFF_NOTREADY | IN6_IFF_CLAT46)) { IFA_UNLOCK(ifa); continue; } @@ -3012,8 +3369,9 @@ in6_ifawithscope(struct ifnet *oifp, struct in6_addr *dst) * If this is the first address we find, * keep it anyway. */ - if (ifa_best == NULL) + if (ifa_best == NULL) { goto replace; + } /* * ifa_best is never NULL beyond this line except @@ -3036,8 +3394,9 @@ in6_ifawithscope(struct ifnet *oifp, struct in6_addr *dst) * scope(src) >= scope(dst)) */ if (IN6_ARE_SCOPE_CMP(best_scope, dst_scope) < 0 && - IN6_ARE_SCOPE_CMP(src_scope, dst_scope) >= 0) + IN6_ARE_SCOPE_CMP(src_scope, dst_scope) >= 0) { goto replace; /* (A) */ + } if (IN6_ARE_SCOPE_CMP(src_scope, dst_scope) < 0 && IN6_ARE_SCOPE_CMP(best_scope, dst_scope) >= 0) { IFA_UNLOCK(ifa); @@ -3078,8 +3437,9 @@ in6_ifawithscope(struct ifnet *oifp, struct in6_addr *dst) */ if ((ifa_best->ia6_flags & IN6_IFF_DEPRECATED) && (((struct in6_ifaddr *)ifa)->ia6_flags & - IN6_IFF_DEPRECATED) == 0) + IN6_IFF_DEPRECATED) == 0) { goto replace; + } /* * When we use temporary addresses described in @@ -3095,18 +3455,18 @@ in6_ifawithscope(struct ifnet *oifp, struct in6_addr *dst) ifat = (struct in6_ifaddr *)ifa; if ((ifa_best->ia6_flags & - (IN6_IFF_AUTOCONF|IN6_IFF_TEMPORARY)) + (IN6_IFF_AUTOCONF | IN6_IFF_TEMPORARY)) == IN6_IFF_AUTOCONF && (ifat->ia6_flags & - (IN6_IFF_AUTOCONF|IN6_IFF_TEMPORARY)) - == (IN6_IFF_AUTOCONF|IN6_IFF_TEMPORARY)) { + (IN6_IFF_AUTOCONF | IN6_IFF_TEMPORARY)) + == (IN6_IFF_AUTOCONF | IN6_IFF_TEMPORARY)) { goto replace; } if ((ifa_best->ia6_flags & - (IN6_IFF_AUTOCONF|IN6_IFF_TEMPORARY)) - == (IN6_IFF_AUTOCONF|IN6_IFF_TEMPORARY) && + (IN6_IFF_AUTOCONF | IN6_IFF_TEMPORARY)) + == (IN6_IFF_AUTOCONF | IN6_IFF_TEMPORARY) && (ifat->ia6_flags & - (IN6_IFF_AUTOCONF|IN6_IFF_TEMPORARY)) + (IN6_IFF_AUTOCONF | IN6_IFF_TEMPORARY)) == IN6_IFF_AUTOCONF) { IFA_UNLOCK(ifa); continue; @@ -3173,8 +3533,9 @@ in6_ifawithscope(struct ifnet *oifp, struct in6_addr *dst) IFA_UNLOCK(ifa); continue; } - if (bifp != oifp && ifp == oifp) /* (2) */ + if (bifp != oifp && ifp == oifp) { /* (2) */ goto replace; + } /* * Both bifp and ifp are on the outgoing @@ -3185,8 +3546,9 @@ in6_ifawithscope(struct ifnet *oifp, struct in6_addr *dst) */ tlen = in6_matchlen(IFA_IN6(ifa), dst); matchcmp = tlen - blen; - if (matchcmp > 0) /* (3) */ + if (matchcmp > 0) { /* (3) */ goto replace; + } IFA_UNLOCK(ifa); continue; /* (4) */ } @@ -3198,25 +3560,27 @@ in6_ifawithscope(struct ifnet *oifp, struct in6_addr *dst) goto replace; /* (6) */ } if (dscopecmp < 0) { - if (bscopecmp > 0) /* (7) */ + if (bscopecmp > 0) { /* (7) */ goto replace; + } IFA_UNLOCK(ifa); continue; /* (8) */ } /* now dscopecmp must be 0 */ - if (bscopecmp < 0) + if (bscopecmp < 0) { goto replace; /* (9) */ - + } replace: - IFA_ADDREF_LOCKED(ifa); /* for ifa_best */ + IFA_ADDREF_LOCKED(ifa); /* for ifa_best */ blen = tlen >= 0 ? tlen : in6_matchlen(IFA_IN6(ifa), dst); best_scope = in6_addrscope(&ifa2ia6(ifa)->ia_addr.sin6_addr); IFA_UNLOCK(ifa); - if (ifa_best) + if (ifa_best) { IFA_REMREF(&ifa_best->ia_ifa); + } ifa_best = (struct in6_ifaddr *)ifa; } ifnet_lock_done(ifp); @@ -3224,26 +3588,29 @@ replace: ifnet_head_done(); /* count statistics for future improvements */ - if (ifa_best == NULL) + if (ifa_best == NULL) { ip6stat.ip6s_sources_none++; - else { + } else { IFA_LOCK_SPIN(&ifa_best->ia_ifa); - if (oifp == ifa_best->ia_ifp) + if (oifp == ifa_best->ia_ifp) { ip6stat.ip6s_sources_sameif[best_scope]++; - else + } else { ip6stat.ip6s_sources_otherif[best_scope]++; + } - if (best_scope == dst_scope) + if (best_scope == dst_scope) { ip6stat.ip6s_sources_samescope[best_scope]++; - else + } else { ip6stat.ip6s_sources_otherscope[best_scope]++; + } - if ((ifa_best->ia6_flags & IN6_IFF_DEPRECATED) != 0) + if ((ifa_best->ia6_flags & IN6_IFF_DEPRECATED) != 0) { ip6stat.ip6s_sources_deprecated[best_scope]++; + } IFA_UNLOCK(&ifa_best->ia_ifa); } - return (ifa_best); + return ifa_best; } /* @@ -3253,10 +3620,10 @@ replace: struct in6_ifaddr * in6_ifawithifp(struct ifnet *ifp, struct in6_addr *dst) { - int dst_scope = in6_addrscope(dst), blen = -1, tlen; + int dst_scope = in6_addrscope(dst), blen = -1, tlen; struct ifaddr *ifa; struct in6_ifaddr *besta = NULL; - struct in6_ifaddr *dep[2]; /* last-resort: deprecated */ + struct in6_ifaddr *dep[2]; /* last-resort: deprecated */ dep[0] = dep[1] = NULL; @@ -3277,7 +3644,7 @@ in6_ifawithifp(struct ifnet *ifp, struct in6_addr *dst) IFA_UNLOCK(ifa); continue; /* XXX: is there any case to allow anycast? */ } - if (ifa2ia6(ifa)->ia6_flags & IN6_IFF_NOTREADY) { + if (ifa2ia6(ifa)->ia6_flags & (IN6_IFF_NOTREADY | IN6_IFF_CLAT46)) { IFA_UNLOCK(ifa); continue; /* don't use this interface */ } @@ -3287,10 +3654,11 @@ in6_ifawithifp(struct ifnet *ifp, struct in6_addr *dst) } if (ifa2ia6(ifa)->ia6_flags & IN6_IFF_DEPRECATED) { if (ip6_use_deprecated) { - IFA_ADDREF_LOCKED(ifa); /* for dep[0] */ + IFA_ADDREF_LOCKED(ifa); /* for dep[0] */ IFA_UNLOCK(ifa); - if (dep[0] != NULL) + if (dep[0] != NULL) { IFA_REMREF(&dep[0]->ia_ifa); + } dep[0] = (struct in6_ifaddr *)ifa; } else { IFA_UNLOCK(ifa); @@ -3307,14 +3675,14 @@ in6_ifawithifp(struct ifnet *ifp, struct in6_addr *dst) IFA_UNLOCK(ifa); IFA_LOCK(&besta->ia_ifa); blen = in6_matchlen( - &besta->ia_addr.sin6_addr, dst); + &besta->ia_addr.sin6_addr, dst); IFA_UNLOCK(&besta->ia_ifa); IFA_LOCK(ifa); } tlen = in6_matchlen(IFA_IN6(ifa), dst); if (tlen > blen) { blen = tlen; - IFA_ADDREF_LOCKED(ifa); /* for besta */ + IFA_ADDREF_LOCKED(ifa); /* for besta */ IFA_UNLOCK(ifa); IFA_REMREF(&besta->ia_ifa); besta = (struct in6_ifaddr *)ifa; @@ -3323,7 +3691,7 @@ in6_ifawithifp(struct ifnet *ifp, struct in6_addr *dst) } } else { besta = (struct in6_ifaddr *)ifa; - IFA_ADDREF_LOCKED(ifa); /* for besta */ + IFA_ADDREF_LOCKED(ifa); /* for besta */ IFA_UNLOCK(ifa); } } else { @@ -3332,9 +3700,10 @@ in6_ifawithifp(struct ifnet *ifp, struct in6_addr *dst) } if (besta) { ifnet_lock_done(ifp); - if (dep[0] != NULL) + if (dep[0] != NULL) { IFA_REMREF(&dep[0]->ia_ifa); - return (besta); + } + return besta; } TAILQ_FOREACH(ifa, &ifp->if_addrlist, ifa_list) { @@ -3347,7 +3716,7 @@ in6_ifawithifp(struct ifnet *ifp, struct in6_addr *dst) IFA_UNLOCK(ifa); continue; /* XXX: is there any case to allow anycast? */ } - if (ifa2ia6(ifa)->ia6_flags & IN6_IFF_NOTREADY) { + if (ifa2ia6(ifa)->ia6_flags & (IN6_IFF_NOTREADY | IN6_IFF_CLAT46)) { IFA_UNLOCK(ifa); continue; /* don't use this interface */ } @@ -3357,37 +3726,42 @@ in6_ifawithifp(struct ifnet *ifp, struct in6_addr *dst) } if (ifa2ia6(ifa)->ia6_flags & IN6_IFF_DEPRECATED) { if (ip6_use_deprecated) { - IFA_ADDREF_LOCKED(ifa); /* for dep[1] */ + IFA_ADDREF_LOCKED(ifa); /* for dep[1] */ IFA_UNLOCK(ifa); - if (dep[1] != NULL) + if (dep[1] != NULL) { IFA_REMREF(&dep[1]->ia_ifa); + } dep[1] = (struct in6_ifaddr *)ifa; } else { IFA_UNLOCK(ifa); } continue; } - IFA_ADDREF_LOCKED(ifa); /* for caller */ + IFA_ADDREF_LOCKED(ifa); /* for caller */ IFA_UNLOCK(ifa); ifnet_lock_done(ifp); - if (dep[0] != NULL) + if (dep[0] != NULL) { IFA_REMREF(&dep[0]->ia_ifa); - if (dep[1] != NULL) + } + if (dep[1] != NULL) { IFA_REMREF(&dep[1]->ia_ifa); - return ((struct in6_ifaddr *)ifa); + } + return (struct in6_ifaddr *)ifa; } ifnet_lock_done(ifp); /* use the last-resort values, that are, deprecated addresses */ if (dep[0]) { - if (dep[1] != NULL) + if (dep[1] != NULL) { IFA_REMREF(&dep[1]->ia_ifa); - return (dep[0]); + } + return dep[0]; + } + if (dep[1]) { + return dep[1]; } - if (dep[1]) - return (dep[1]); - return (NULL); + return NULL; } /* @@ -3397,6 +3771,13 @@ static void in6_if_up_dad_start(struct ifnet *ifp) { struct ifaddr *ifa; + struct nd_ifinfo *ndi = NULL; + + ndi = ND_IFINFO(ifp); + VERIFY((NULL != ndi) && (TRUE == ndi->initialized)); + if (!(ndi->flags & ND6_IFF_DAD)) { + return; + } /* start DAD on all the interface addresses */ ifnet_lock_exclusive(ifp); @@ -3410,7 +3791,7 @@ in6_if_up_dad_start(struct ifnet *ifp) } ia6 = (struct in6_ifaddr *)ifa; if (ia6->ia6_flags & IN6_IFF_DADPROGRESS) { - int delay = 0; /* delay ticks before DAD output */ + int delay = 0; /* delay ticks before DAD output */ IFA_UNLOCK(ifa); nd6_dad_start(ifa, &delay); } else { @@ -3424,8 +3805,17 @@ int in6if_do_dad( struct ifnet *ifp) { - if ((ifp->if_flags & IFF_LOOPBACK) != 0) - return (0); + struct nd_ifinfo *ndi = NULL; + + if ((ifp->if_flags & IFF_LOOPBACK) != 0) { + return 0; + } + + ndi = ND_IFINFO(ifp); + VERIFY((NULL != ndi) && (TRUE == ndi->initialized)); + if (!(ndi->flags & ND6_IFF_DAD)) { + return 0; + } /* * If we are using the alternative neighbor discovery @@ -3435,9 +3825,19 @@ in6if_do_dad( * for now, even when not marked as using the alternative * interface. This is for historical reasons. */ - if (ifp->if_eflags & - (IFEF_IPV6_ND6ALT|IFEF_LOCALNET_PRIVATE|IFEF_DIRECTLINK)) - return (0); + if (ifp->if_eflags & + (IFEF_IPV6_ND6ALT | IFEF_LOCALNET_PRIVATE | IFEF_DIRECTLINK)) { + return 0; + } + + if (ifp->if_family == IFNET_FAMILY_IPSEC || + ifp->if_family == IFNET_FAMILY_UTUN) { + /* + * Ignore DAD for tunneling virtual interfaces, which get + * their IPv6 address explicitly assigned. + */ + return 0; + } switch (ifp->if_type) { #if IFT_DUMMY @@ -3450,7 +3850,7 @@ in6if_do_dad( * interfaces. We should even omit it, because loop-backed * NS would confuse the DAD procedure. */ - return (0); + return 0; default: /* * Our DAD routine requires the interface up and running. @@ -3461,11 +3861,12 @@ in6if_do_dad( * XXX: we should rather mark "tentative" on such addresses, * and do DAD after the interface becomes ready. */ - if ((ifp->if_flags & (IFF_UP|IFF_RUNNING)) != - (IFF_UP|IFF_RUNNING)) - return (0); + if ((ifp->if_flags & (IFF_UP | IFF_RUNNING)) != + (IFF_UP | IFF_RUNNING)) { + return 0; + } - return (1); + return 1; } } @@ -3483,19 +3884,24 @@ in6_setmaxmtu(void) TAILQ_FOREACH(ifp, &ifnet_head, if_list) { struct nd_ifinfo *ndi = NULL; - if ((ndi = ND_IFINFO(ifp)) != NULL && !ndi->initialized) + if ((ndi = ND_IFINFO(ifp)) != NULL && !ndi->initialized) { ndi = NULL; - if (ndi != NULL) + } + if (ndi != NULL) { lck_mtx_lock(&ndi->lock); + } if ((ifp->if_flags & IFF_LOOPBACK) == 0 && - IN6_LINKMTU(ifp) > maxmtu) + IN6_LINKMTU(ifp) > maxmtu) { maxmtu = IN6_LINKMTU(ifp); - if (ndi != NULL) + } + if (ndi != NULL) { lck_mtx_unlock(&ndi->lock); + } } ifnet_head_done(); - if (maxmtu) /* update only when maxmtu is positive */ + if (maxmtu) { /* update only when maxmtu is positive */ in6_maxmtu = maxmtu; + } } /* * Provide the length of interface identifiers to be used for the link attached @@ -3508,43 +3914,45 @@ int in6_if2idlen(struct ifnet *ifp) { switch (ifp->if_type) { - case IFT_ETHER: /* RFC2464 */ - case IFT_IEEE8023ADLAG: /* IEEE802.3ad Link Aggregate */ + case IFT_ETHER: /* RFC2464 */ + case IFT_IEEE8023ADLAG: /* IEEE802.3ad Link Aggregate */ #ifdef IFT_PROPVIRTUAL - case IFT_PROPVIRTUAL: /* XXX: no RFC. treat it as ether */ + case IFT_PROPVIRTUAL: /* XXX: no RFC. treat it as ether */ #endif #ifdef IFT_L2VLAN - case IFT_L2VLAN: /* ditto */ + case IFT_L2VLAN: /* ditto */ #endif #ifdef IFT_IEEE80211 - case IFT_IEEE80211: /* ditto */ + case IFT_IEEE80211: /* ditto */ #endif #ifdef IFT_MIP - case IFT_MIP: /* ditto */ + case IFT_MIP: /* ditto */ #endif - return (64); - case IFT_FDDI: /* RFC2467 */ - return (64); - case IFT_ISO88025: /* RFC2470 (IPv6 over Token Ring) */ - return (64); - case IFT_PPP: /* RFC2472 */ - return (64); - case IFT_ARCNET: /* RFC2497 */ - return (64); - case IFT_FRELAY: /* RFC2590 */ - return (64); - case IFT_IEEE1394: /* RFC3146 */ - return (64); + return 64; + case IFT_FDDI: /* RFC2467 */ + return 64; + case IFT_ISO88025: /* RFC2470 (IPv6 over Token Ring) */ + return 64; + case IFT_PPP: /* RFC2472 */ + return 64; + case IFT_ARCNET: /* RFC2497 */ + return 64; + case IFT_FRELAY: /* RFC2590 */ + return 64; + case IFT_IEEE1394: /* RFC3146 */ + return 64; case IFT_GIF: - return (64); /* draft-ietf-v6ops-mech-v2-07 */ + return 64; /* draft-ietf-v6ops-mech-v2-07 */ case IFT_LOOP: - return (64); /* XXX: is this really correct? */ + return 64; /* XXX: is this really correct? */ case IFT_OTHER: - return (64); /* for utun interfaces */ + return 64; /* for utun interfaces */ case IFT_CELLULAR: - return (64); /* Packet Data over Cellular */ + return 64; /* Packet Data over Cellular */ case IFT_BRIDGE: - return (64); /* Transparent bridge interface */ + return 64; /* Transparent bridge interface */ + case IFT_6LOWPAN: + return 64; /* 6LoWPAN */ default: /* * Unknown link type: @@ -3559,7 +3967,7 @@ in6_if2idlen(struct ifnet *ifp) */ log(LOG_NOTICE, "%s: unknown link type (%d)\n", __func__, ifp->if_type); - return (64); + return 64; } } /* @@ -3569,8 +3977,8 @@ in6_if2idlen(struct ifnet *ifp) void in6_sin6_2_sin(struct sockaddr_in *sin, struct sockaddr_in6 *sin6) { - bzero(sin, sizeof (*sin)); - sin->sin_len = sizeof (struct sockaddr_in); + bzero(sin, sizeof(*sin)); + sin->sin_len = sizeof(struct sockaddr_in); sin->sin_family = AF_INET; sin->sin_port = sin6->sin6_port; sin->sin_addr.s_addr = sin6->sin6_addr.s6_addr32[3]; @@ -3580,8 +3988,8 @@ in6_sin6_2_sin(struct sockaddr_in *sin, struct sockaddr_in6 *sin6) void in6_sin_2_v4mapsin6(struct sockaddr_in *sin, struct sockaddr_in6 *sin6) { - bzero(sin6, sizeof (*sin6)); - sin6->sin6_len = sizeof (struct sockaddr_in6); + bzero(sin6, sizeof(*sin6)); + sin6->sin6_len = sizeof(struct sockaddr_in6); sin6->sin6_family = AF_INET6; sin6->sin6_port = sin->sin_port; sin6->sin6_addr.s6_addr32[0] = 0; @@ -3618,16 +4026,17 @@ in6_sin_2_v4mapsin6_in_sock(struct sockaddr **nam) struct sockaddr_in *sin_p; struct sockaddr_in6 *sin6_p; - MALLOC(sin6_p, struct sockaddr_in6 *, sizeof (*sin6_p), M_SONAME, + MALLOC(sin6_p, struct sockaddr_in6 *, sizeof(*sin6_p), M_SONAME, M_WAITOK); - if (sin6_p == NULL) - return (ENOBUFS); + if (sin6_p == NULL) { + return ENOBUFS; + } sin_p = (struct sockaddr_in *)(void *)*nam; in6_sin_2_v4mapsin6(sin_p, sin6_p); FREE(*nam, M_SONAME); *nam = (struct sockaddr *)sin6_p; - return (0); + return 0; } /* @@ -3646,28 +4055,30 @@ in6_post_msg(struct ifnet *ifp, u_int32_t event_code, struct in6_ifaddr *ifa, struct kev_in6_data in6_event_data; struct in6_addrlifetime ia6_lt; - bzero(&in6_event_data, sizeof (struct kev_in6_data)); - bzero(&ev_msg, sizeof (struct kev_msg)); - ev_msg.vendor_code = KEV_VENDOR_APPLE; - ev_msg.kev_class = KEV_NETWORK_CLASS; - ev_msg.kev_subclass = KEV_INET6_SUBCLASS; - ev_msg.event_code = event_code; - - IFA_LOCK(&ifa->ia_ifa); - in6_event_data.ia_addr = ifa->ia_addr; - in6_event_data.ia_net = ifa->ia_net; - in6_event_data.ia_dstaddr = ifa->ia_dstaddr; - in6_event_data.ia_prefixmask = ifa->ia_prefixmask; - in6_event_data.ia_plen = ifa->ia_plen; - in6_event_data.ia6_flags = (u_int32_t)ifa->ia6_flags; - - /* retrieve time as calendar time (last arg is 1) */ - in6ifa_getlifetime(ifa, &ia6_lt, 1); - in6_event_data.ia_lifetime.ia6t_expire = ia6_lt.ia6t_expire; - in6_event_data.ia_lifetime.ia6t_preferred = ia6_lt.ia6t_preferred; - in6_event_data.ia_lifetime.ia6t_vltime = ia6_lt.ia6t_vltime; - in6_event_data.ia_lifetime.ia6t_pltime = ia6_lt.ia6t_pltime; - IFA_UNLOCK(&ifa->ia_ifa); + bzero(&in6_event_data, sizeof(struct kev_in6_data)); + bzero(&ev_msg, sizeof(struct kev_msg)); + ev_msg.vendor_code = KEV_VENDOR_APPLE; + ev_msg.kev_class = KEV_NETWORK_CLASS; + ev_msg.kev_subclass = KEV_INET6_SUBCLASS; + ev_msg.event_code = event_code; + + if (ifa) { + IFA_LOCK(&ifa->ia_ifa); + in6_event_data.ia_addr = ifa->ia_addr; + in6_event_data.ia_net = ifa->ia_net; + in6_event_data.ia_dstaddr = ifa->ia_dstaddr; + in6_event_data.ia_prefixmask = ifa->ia_prefixmask; + in6_event_data.ia_plen = ifa->ia_plen; + in6_event_data.ia6_flags = (u_int32_t)ifa->ia6_flags; + + /* retrieve time as calendar time (last arg is 1) */ + in6ifa_getlifetime(ifa, &ia6_lt, 1); + in6_event_data.ia_lifetime.ia6t_expire = (u_int32_t)ia6_lt.ia6t_expire; + in6_event_data.ia_lifetime.ia6t_preferred = (u_int32_t)ia6_lt.ia6t_preferred; + in6_event_data.ia_lifetime.ia6t_vltime = ia6_lt.ia6t_vltime; + in6_event_data.ia_lifetime.ia6t_pltime = ia6_lt.ia6t_pltime; + IFA_UNLOCK(&ifa->ia_ifa); + } if (ifp != NULL) { (void) strlcpy(&in6_event_data.link_data.if_name[0], @@ -3676,15 +4087,16 @@ in6_post_msg(struct ifnet *ifp, u_int32_t event_code, struct in6_ifaddr *ifa, in6_event_data.link_data.if_unit = (u_int32_t)ifp->if_unit; } - if (mac != NULL) - memcpy(&in6_event_data.ia_mac, mac, + if (mac != NULL) { + memcpy(&in6_event_data.ia_mac, mac, sizeof(in6_event_data.ia_mac)); + } ev_msg.dv[0].data_ptr = &in6_event_data; - ev_msg.dv[0].data_length = sizeof (in6_event_data); + ev_msg.dv[0].data_length = sizeof(in6_event_data); ev_msg.dv[1].data_length = 0; - kev_post_msg(&ev_msg); + dlil_post_complete_msg(NULL, &ev_msg); } /* @@ -3696,35 +4108,28 @@ in6_ifaddr_init(void) in6_cga_init(); in6_multi_init(); - PE_parse_boot_argn("ifa_debug", &in6ifa_debug, sizeof (in6ifa_debug)); + PE_parse_boot_argn("ifa_debug", &in6ifa_debug, sizeof(in6ifa_debug)); - in6ifa_size = (in6ifa_debug == 0) ? sizeof (struct in6_ifaddr) : - sizeof (struct in6_ifaddr_dbg); + vm_size_t in6ifa_size = (in6ifa_debug == 0) ? sizeof(struct in6_ifaddr) : + sizeof(struct in6_ifaddr_dbg); - in6ifa_zone = zinit(in6ifa_size, IN6IFA_ZONE_MAX * in6ifa_size, - 0, IN6IFA_ZONE_NAME); - if (in6ifa_zone == NULL) { - panic("%s: failed allocating %s", __func__, IN6IFA_ZONE_NAME); - /* NOTREACHED */ - } - zone_change(in6ifa_zone, Z_EXPAND, TRUE); - zone_change(in6ifa_zone, Z_CALLERACCT, FALSE); + in6ifa_zone = zone_create(IN6IFA_ZONE_NAME, in6ifa_size, ZC_ZFREE_CLEARMEM); lck_mtx_init(&in6ifa_trash_lock, ifa_mtx_grp, ifa_mtx_attr); TAILQ_INIT(&in6ifa_trash_head); } static struct in6_ifaddr * -in6_ifaddr_alloc(int how) +in6_ifaddr_alloc(zalloc_flags_t how) { struct in6_ifaddr *in6ifa; - in6ifa = (how == M_WAITOK) ? zalloc(in6ifa_zone) : - zalloc_noblock(in6ifa_zone); + in6ifa = zalloc_flags(in6ifa_zone, how | Z_ZERO); if (in6ifa != NULL) { - bzero(in6ifa, in6ifa_size); in6ifa->ia_ifa.ifa_free = in6_ifaddr_free; in6ifa->ia_ifa.ifa_debug |= IFD_ALLOC; + in6ifa->ia_ifa.ifa_del_wc = &in6ifa->ia_ifa.ifa_debug; + in6ifa->ia_ifa.ifa_del_waiters = 0; ifa_lock_init(&in6ifa->ia_ifa); if (in6ifa_debug != 0) { struct in6_ifaddr_dbg *in6ifa_dbg = @@ -3737,7 +4142,7 @@ in6_ifaddr_alloc(int how) } } - return (in6ifa); + return in6ifa; } static void @@ -3757,7 +4162,7 @@ in6_ifaddr_free(struct ifaddr *ifa) (struct in6_ifaddr_dbg *)ifa; ctrace_record(&in6ifa_dbg->in6ifa_free); bcopy(&in6ifa_dbg->in6ifa, &in6ifa_dbg->in6ifa_old, - sizeof (struct in6_ifaddr)); + sizeof(struct in6_ifaddr)); if (ifa->ifa_debug & IFD_TRASHED) { /* Become a regular mutex, just in case */ IFA_CONVERT_LOCK(ifa); @@ -3770,7 +4175,7 @@ in6_ifaddr_free(struct ifaddr *ifa) } IFA_UNLOCK(ifa); ifa_lock_destroy(ifa); - bzero(ifa, sizeof (struct in6_ifaddr)); + bzero(ifa, sizeof(struct in6_ifaddr)); zfree(in6ifa_zone, ifa); } @@ -3841,87 +4246,6 @@ in6_ifaddr_trace(struct ifaddr *ifa, int refhold) ctrace_record(&tr[idx]); } -static void -in6_ifaddr_set_dadprogress(struct in6_ifaddr *ia) -{ - struct ifnet* ifp = ia->ia_ifp; - uint32_t flags = IN6_IFF_TENTATIVE; - uint32_t optdad = nd6_optimistic_dad; - - if (optdad) { - if ((ifp->if_eflags & IFEF_IPV6_ROUTER) != 0) { - optdad = 0; - } else { - struct nd_ifinfo *ndi = NULL; - - ndi = ND_IFINFO(ifp); - VERIFY (ndi != NULL && ndi->initialized); - lck_mtx_lock(&ndi->lock); - if ((ndi->flags & ND6_IFF_REPLICATED) != 0) { - optdad = 0; - } - lck_mtx_unlock(&ndi->lock); - } - } - - if (optdad) { - if ((optdad & ND6_OPTIMISTIC_DAD_LINKLOCAL) && - IN6_IS_ADDR_LINKLOCAL(&ia->ia_addr.sin6_addr)) - flags = IN6_IFF_OPTIMISTIC; - else if ((optdad & ND6_OPTIMISTIC_DAD_AUTOCONF) && - (ia->ia6_flags & IN6_IFF_AUTOCONF)) { - if (ia->ia6_flags & IN6_IFF_TEMPORARY) { - if (optdad & ND6_OPTIMISTIC_DAD_TEMPORARY) - flags = IN6_IFF_OPTIMISTIC; - } else if (ia->ia6_flags & IN6_IFF_SECURED) { - if (optdad & ND6_OPTIMISTIC_DAD_SECURED) - flags = IN6_IFF_OPTIMISTIC; - } else { - /* - * Keeping the behavior for temp and CGA - * SLAAC addresses to have a knob for optimistic - * DAD. - * Other than that if ND6_OPTIMISTIC_DAD_AUTOCONF - * is set, we should default to optimistic - * DAD. - * For now this means SLAAC addresses with interface - * identifier derived from modified EUI-64 bit - * identifiers. - */ - flags = IN6_IFF_OPTIMISTIC; - } - } else if ((optdad & ND6_OPTIMISTIC_DAD_DYNAMIC) && - (ia->ia6_flags & IN6_IFF_DYNAMIC)) { - if (ia->ia6_flags & IN6_IFF_TEMPORARY) { - if (optdad & ND6_OPTIMISTIC_DAD_TEMPORARY) - flags = IN6_IFF_OPTIMISTIC; - } else { - flags = IN6_IFF_OPTIMISTIC; - } - } else if ((optdad & ND6_OPTIMISTIC_DAD_MANUAL) && - (ia->ia6_flags & IN6_IFF_OPTIMISTIC)) { - /* - * rdar://17483438 - * Bypass tentative for address assignments - * not covered above (e.g. manual) upon request - */ - if (!IN6_IS_ADDR_LINKLOCAL(&ia->ia_addr.sin6_addr) && - !(ia->ia6_flags & IN6_IFF_AUTOCONF) && - !(ia->ia6_flags & IN6_IFF_DYNAMIC)) - flags = IN6_IFF_OPTIMISTIC; - } - } - - ia->ia6_flags &= ~(IN6_IFF_DUPLICATED | IN6_IFF_DADPROGRESS); - ia->ia6_flags |= flags; - - nd6log2((LOG_DEBUG, "%s - %s ifp %s ia6_flags 0x%x\n", - __func__, - ip6_sprintf(&ia->ia_addr.sin6_addr), - if_name(ia->ia_ifp), - ia->ia6_flags)); -} - /* * Handle SIOCGASSOCIDS ioctl for PF_INET6 domain. */ @@ -3931,18 +4255,20 @@ in6_getassocids(struct socket *so, uint32_t *cnt, user_addr_t aidp) struct in6pcb *in6p = sotoin6pcb(so); sae_associd_t aid; - if (in6p == NULL || in6p->inp_state == INPCB_STATE_DEAD) - return (EINVAL); + if (in6p == NULL || in6p->inp_state == INPCB_STATE_DEAD) { + return EINVAL; + } /* IN6PCB has no concept of association */ aid = SAE_ASSOCID_ANY; *cnt = 0; /* just asking how many there are? */ - if (aidp == USER_ADDR_NULL) - return (0); + if (aidp == USER_ADDR_NULL) { + return 0; + } - return (copyout(&aid, aidp, sizeof (aid))); + return copyout(&aid, aidp, sizeof(aid)); } /* @@ -3955,35 +4281,37 @@ in6_getconnids(struct socket *so, sae_associd_t aid, uint32_t *cnt, struct in6pcb *in6p = sotoin6pcb(so); sae_connid_t cid; - if (in6p == NULL || in6p->inp_state == INPCB_STATE_DEAD) - return (EINVAL); + if (in6p == NULL || in6p->inp_state == INPCB_STATE_DEAD) { + return EINVAL; + } - if (aid != SAE_ASSOCID_ANY && aid != SAE_ASSOCID_ALL) - return (EINVAL); + if (aid != SAE_ASSOCID_ANY && aid != SAE_ASSOCID_ALL) { + return EINVAL; + } /* if connected, return 1 connection count */ *cnt = ((so->so_state & SS_ISCONNECTED) ? 1 : 0); /* just asking how many there are? */ - if (cidp == USER_ADDR_NULL) - return (0); + if (cidp == USER_ADDR_NULL) { + return 0; + } /* if IN6PCB is connected, assign it connid 1 */ cid = ((*cnt != 0) ? 1 : SAE_CONNID_ANY); - return (copyout(&cid, cidp, sizeof (cid))); + return copyout(&cid, cidp, sizeof(cid)); } /* * Handle SIOCGCONNINFO ioctl for PF_INET6 domain. */ -static int +int in6_getconninfo(struct socket *so, sae_connid_t cid, uint32_t *flags, uint32_t *ifindex, int32_t *soerror, user_addr_t src, socklen_t *src_len, user_addr_t dst, socklen_t *dst_len, uint32_t *aux_type, user_addr_t aux_data, uint32_t *aux_len) { -#pragma unused(aux_data) struct in6pcb *in6p = sotoin6pcb(so); struct sockaddr_in6 sin6; struct ifnet *ifp = NULL; @@ -4008,17 +4336,21 @@ in6_getconninfo(struct socket *so, sae_connid_t cid, uint32_t *flags, *ifindex = ((ifp != NULL) ? ifp->if_index : 0); *soerror = so->so_error; *flags = 0; - if (so->so_state & SS_ISCONNECTED) + if (so->so_state & SS_ISCONNECTED) { *flags |= (CIF_CONNECTED | CIF_PREFERRED); - if (in6p->in6p_flags & INP_BOUND_IF) + } + if (in6p->in6p_flags & INP_BOUND_IF) { *flags |= CIF_BOUND_IF; - if (!(in6p->in6p_flags & INP_IN6ADDR_ANY)) + } + if (!(in6p->in6p_flags & INP_IN6ADDR_ANY)) { *flags |= CIF_BOUND_IP; - if (!(in6p->in6p_flags & INP_ANONPORT)) + } + if (!(in6p->in6p_flags & INP_ANONPORT)) { *flags |= CIF_BOUND_PORT; + } - bzero(&sin6, sizeof (sin6)); - sin6.sin6_len = sizeof (sin6); + bzero(&sin6, sizeof(sin6)); + sin6.sin6_len = sizeof(sin6); sin6.sin6_family = AF_INET6; /* source address and port */ @@ -4028,10 +4360,11 @@ in6_getconninfo(struct socket *so, sae_connid_t cid, uint32_t *flags, *src_len = sin6.sin6_len; } else { if (src != USER_ADDR_NULL) { - copy_len = min(*src_len, sizeof (sin6)); + copy_len = min(*src_len, sizeof(sin6)); error = copyout(&sin6, src, copy_len); - if (error != 0) + if (error != 0) { goto out; + } *src_len = copy_len; } } @@ -4043,37 +4376,40 @@ in6_getconninfo(struct socket *so, sae_connid_t cid, uint32_t *flags, *dst_len = sin6.sin6_len; } else { if (dst != USER_ADDR_NULL) { - copy_len = min(*dst_len, sizeof (sin6)); + copy_len = min(*dst_len, sizeof(sin6)); error = copyout(&sin6, dst, copy_len); - if (error != 0) + if (error != 0) { goto out; + } *dst_len = copy_len; } } - *aux_type = 0; - *aux_len = 0; if (SOCK_PROTO(so) == IPPROTO_TCP) { struct conninfo_tcp tcp_ci; *aux_type = CIAUX_TCP; if (*aux_len == 0) { - *aux_len = sizeof (tcp_ci); + *aux_len = sizeof(tcp_ci); } else { if (aux_data != USER_ADDR_NULL) { - copy_len = min(*aux_len, sizeof (tcp_ci)); - bzero(&tcp_ci, sizeof (tcp_ci)); + copy_len = min(*aux_len, sizeof(tcp_ci)); + bzero(&tcp_ci, sizeof(tcp_ci)); tcp_getconninfo(so, &tcp_ci); error = copyout(&tcp_ci, aux_data, copy_len); - if (error != 0) + if (error != 0) { goto out; + } *aux_len = copy_len; } } + } else { + *aux_type = 0; + *aux_len = 0; } out: - return (error); + return error; } /* @@ -4107,3 +4443,636 @@ in6ioctl_cassert(void) ; } } + +struct in6_llentry { + struct llentry base; +}; + +#define IN6_LLTBL_DEFAULT_HSIZE 32 +#define IN6_LLTBL_HASH(k, h) \ + ((((((((k) >> 8) ^ (k)) >> 8) ^ (k)) >> 8) ^ (k)) & ((h) - 1)) + +/* + * Do actual deallocation of @lle. + */ +static void +in6_lltable_destroy_lle_unlocked(struct llentry *lle) +{ + LLE_LOCK_DESTROY(lle); + LLE_REQ_DESTROY(lle); + FREE(lle, M_LLTABLE); +} + +/* + * Called by LLE_FREE_LOCKED when number of references + * drops to zero. + */ +static void +in6_lltable_destroy_lle(struct llentry *lle) +{ + LLE_WUNLOCK(lle); + /* XXX TBD */ + //thread_call_free(lle->lle_timer); + in6_lltable_destroy_lle_unlocked(lle); +} + + +static struct llentry * +in6_lltable_new(const struct in6_addr *addr6, uint16_t flags) +{ +#pragma unused(flags) + struct in6_llentry *lle; + + MALLOC(lle, struct in6_llentry *, sizeof(struct in6_llentry), M_LLTABLE, M_NOWAIT | M_ZERO); + if (lle == NULL) { /* NB: caller generates msg */ + return NULL; + } + + lle->base.r_l3addr.addr6 = *addr6; + lle->base.lle_refcnt = 1; + lle->base.lle_free = in6_lltable_destroy_lle; + LLE_LOCK_INIT(&lle->base); + LLE_REQ_INIT(&lle->base); +#if 0 + /* XXX TBD */ + lle->base.lle_timer = thread_call_allocate(nd6_llinfo_timer, lle); + + if (lle->base.lle_timer == NULL) { + printf("lle_timer thread call could not be allocated.\n"); + LLE_LOCK_DESTROY(&lle->base); + LLE_REQ_DESTROY(&lle->base); + FREE(lle, M_LLTABLE); + return NULL; + } +#endif + return &lle->base; +} + +static int +in6_lltable_match_prefix(const struct sockaddr *saddr, + const struct sockaddr *smask, uint16_t flags, struct llentry *lle) +{ + const struct in6_addr *addr, *mask, *lle_addr; + + addr = &((const struct sockaddr_in6 *)(const void *)saddr)->sin6_addr; + mask = &((const struct sockaddr_in6 *)(const void *)smask)->sin6_addr; + lle_addr = &lle->r_l3addr.addr6; + + if (IN6_ARE_MASKED_ADDR_EQUAL(lle_addr, addr, mask) == 0) { + return 0; + } + + if (lle->la_flags & LLE_IFADDR) { + /* + * Delete LLE_IFADDR records IFF address & flag matches. + * Note that addr is the interface address within prefix + * being matched. + */ + if (IN6_ARE_ADDR_EQUAL(addr, lle_addr) && + (flags & LLE_STATIC) != 0) { + return 1; + } + return 0; + } + + /* flags & LLE_STATIC means deleting both dynamic and static entries */ + if ((flags & LLE_STATIC) || !(lle->la_flags & LLE_STATIC)) { + return 1; + } + + return 0; +} + +static void +in6_lltable_free_entry(struct lltable *llt, struct llentry *lle) +{ + struct ifnet *ifp; + + LLE_WLOCK_ASSERT(lle); + KASSERT(llt != NULL, ("lltable is NULL")); + + /* Unlink entry from table */ + if ((lle->la_flags & LLE_LINKED) != 0) { + ifp = llt->llt_ifp; + if_afdata_wlock_assert(ifp, llt->llt_af); + lltable_unlink_entry(llt, lle); + } + +#if 0 + /* XXX TBD */ + if (thread_call_cancel(lle->lle_timer) == TRUE) { + LLE_REMREF(lle); + } +#endif + llentry_free(lle); +} + +static int +in6_lltable_rtcheck(struct ifnet *ifp, + uint16_t flags, const struct sockaddr *l3addr) +{ +#pragma unused(flags) + struct rtentry *rt; + + KASSERT(l3addr->sa_family == AF_INET6, + ("sin_family %d", l3addr->sa_family)); + /* XXX rtalloc1 should take a const param */ + rt = rtalloc1(__DECONST(struct sockaddr *, l3addr), 0, 0); + if (rt == NULL || (rt->rt_flags & RTF_GATEWAY) || rt->rt_ifp != ifp) { + struct ifaddr *ifa; + /* + * Create an ND6 cache for an IPv6 neighbor + * that is not covered by our own prefix. + */ + /* XXX ifaof_ifpforaddr should take a const param */ + ifa = ifaof_ifpforaddr(__DECONST(struct sockaddr *, l3addr), ifp); + if (ifa != NULL) { + IFA_REMREF(ifa); + if (rt != NULL) { + rtfree(rt); + } + return 0; + } + log(LOG_INFO, "IPv6 address: \"%s\" is not on the network\n", + ip6_sprintf(&((const struct sockaddr_in6 *)(const void *)l3addr)->sin6_addr)); + if (rt != NULL) { + rtfree(rt); + } + return EINVAL; + } + rtfree(rt); + return 0; +} + +static inline uint32_t +in6_lltable_hash_dst(const struct in6_addr *dst, uint32_t hsize) +{ + return IN6_LLTBL_HASH(dst->s6_addr32[3], hsize); +} + +static uint32_t +in6_lltable_hash(const struct llentry *lle, uint32_t hsize) +{ + return in6_lltable_hash_dst(&lle->r_l3addr.addr6, hsize); +} + +static void +in6_lltable_fill_sa_entry(const struct llentry *lle, struct sockaddr *sa) +{ + struct sockaddr_in6 *sin6; + + sin6 = (struct sockaddr_in6 *)(void *)sa; + bzero(sin6, sizeof(*sin6)); + sin6->sin6_family = AF_INET6; + sin6->sin6_len = sizeof(*sin6); + sin6->sin6_addr = lle->r_l3addr.addr6; +} + +static inline struct llentry * +in6_lltable_find_dst(struct lltable *llt, const struct in6_addr *dst) +{ + struct llentry *lle; + struct llentries *lleh; + u_int hashidx; + + hashidx = in6_lltable_hash_dst(dst, llt->llt_hsize); + lleh = &llt->lle_head[hashidx]; + LIST_FOREACH(lle, lleh, lle_next) { + if (lle->la_flags & LLE_DELETED) { + continue; + } + if (IN6_ARE_ADDR_EQUAL(&lle->r_l3addr.addr6, dst)) { + break; + } + } + + return lle; +} + +static void +in6_lltable_delete_entry(struct lltable *llt, struct llentry *lle) +{ +#pragma unused(llt) + lle->la_flags |= LLE_DELETED; + EVENTHANDLER_INVOKE(NULL, lle_event, lle, LLENTRY_DELETED); +#ifdef DIAGNOSTIC + log(LOG_INFO, "ifaddr cache = %p is deleted\n", lle); +#endif + llentry_free(lle); +} + +static struct llentry * +in6_lltable_alloc(struct lltable *llt, uint16_t flags, + const struct sockaddr *l3addr) +{ + const struct sockaddr_in6 *sin6 = (const struct sockaddr_in6 *)(const void *)l3addr; + struct ifnet *ifp = llt->llt_ifp; + struct llentry *lle; + + KASSERT(l3addr->sa_family == AF_INET6, + ("sin_family %d", l3addr->sa_family)); + + /* + * A route that covers the given address must have + * been installed 1st because we are doing a resolution, + * verify this. + */ + if (!(flags & LLE_IFADDR) && + in6_lltable_rtcheck(ifp, flags, l3addr) != 0) { + return NULL; + } + + lle = in6_lltable_new(&sin6->sin6_addr, flags); + if (lle == NULL) { + log(LOG_INFO, "lla_lookup: new lle malloc failed\n"); + return NULL; + } + lle->la_flags = (uint16_t)flags; + if ((flags & LLE_IFADDR) == LLE_IFADDR) { + lltable_set_entry_addr(ifp, lle, LLADDR(SDL(ifp->if_lladdr->ifa_addr))); + lle->la_flags |= LLE_STATIC; + } + + if ((lle->la_flags & LLE_STATIC) != 0) { + lle->ln_state = ND6_LLINFO_REACHABLE; + } + + return lle; +} + +static struct llentry * +in6_lltable_lookup(struct lltable *llt, uint16_t flags, + const struct sockaddr *l3addr) +{ + const struct sockaddr_in6 *sin6 = (const struct sockaddr_in6 *)(const void *)l3addr; + struct llentry *lle; + + IF_AFDATA_LOCK_ASSERT(llt->llt_ifp, llt->llt_af); + KASSERT(l3addr->sa_family == AF_INET6, + ("sin_family %d", l3addr->sa_family)); + + lle = in6_lltable_find_dst(llt, &sin6->sin6_addr); + + if (lle == NULL) { + return NULL; + } + + KASSERT((flags & (LLE_UNLOCKED | LLE_EXCLUSIVE)) != + (LLE_UNLOCKED | LLE_EXCLUSIVE), ("wrong lle request flags: 0x%X", + flags)); + + if (flags & LLE_UNLOCKED) { + return lle; + } + + if (flags & LLE_EXCLUSIVE) { + LLE_WLOCK(lle); + } else { + LLE_RLOCK(lle); + } + return lle; +} + +static int +in6_lltable_dump_entry(struct lltable *llt, struct llentry *lle, + struct sysctl_req *wr) +{ + struct ifnet *ifp = llt->llt_ifp; + /* XXX stack use */ + struct { + struct rt_msghdr rtm; + struct sockaddr_in6 sin6; + /* + * ndp.c assumes that sdl is word aligned + */ +#ifdef __LP64__ + uint32_t pad; +#endif + struct sockaddr_dl sdl; + } ndpc; + struct sockaddr_dl *sdl; + int error; + + bzero(&ndpc, sizeof(ndpc)); + /* skip deleted entries */ + if ((lle->la_flags & LLE_DELETED) == LLE_DELETED) { + return 0; + } + /* Skip if jailed and not a valid IP of the prison. */ + lltable_fill_sa_entry(lle, + (struct sockaddr *)&ndpc.sin6); + /* + * produce a msg made of: + * struct rt_msghdr; + * struct sockaddr_in6 (IPv6) + * struct sockaddr_dl; + */ + ndpc.rtm.rtm_msglen = sizeof(ndpc); + ndpc.rtm.rtm_version = RTM_VERSION; + ndpc.rtm.rtm_type = RTM_GET; + ndpc.rtm.rtm_flags = RTF_UP; + ndpc.rtm.rtm_addrs = RTA_DST | RTA_GATEWAY; + + /* publish */ + if (lle->la_flags & LLE_PUB) { + ndpc.rtm.rtm_flags |= RTF_ANNOUNCE; + } + sdl = &ndpc.sdl; + sdl->sdl_family = AF_LINK; + sdl->sdl_len = sizeof(*sdl); + sdl->sdl_index = ifp->if_index; + sdl->sdl_type = ifp->if_type; + if ((lle->la_flags & LLE_VALID) == LLE_VALID) { + sdl->sdl_alen = ifp->if_addrlen; + bcopy(&lle->ll_addr, LLADDR(sdl), ifp->if_addrlen); + } else { + sdl->sdl_alen = 0; + bzero(LLADDR(sdl), ifp->if_addrlen); + } + if (lle->la_expire != 0) { + clock_sec_t secs; + clock_usec_t usecs; + + clock_get_calendar_microtime(&secs, &usecs); + ndpc.rtm.rtm_rmx.rmx_expire = (int32_t)(lle->la_expire + + lle->lle_remtime / hz + + secs - net_uptime()); + } + ndpc.rtm.rtm_flags |= (RTF_HOST | RTF_LLDATA); + if (lle->la_flags & LLE_STATIC) { + ndpc.rtm.rtm_flags |= RTF_STATIC; + } + if (lle->la_flags & LLE_IFADDR) { + ndpc.rtm.rtm_flags |= RTF_PINNED; + } + if (lle->ln_router != 0) { + ndpc.rtm.rtm_flags |= RTF_GATEWAY; + } + ndpc.rtm.rtm_rmx.rmx_pksent = lle->la_asked; + /* Store state in rmx_weight value */ + ndpc.rtm.rtm_rmx.rmx_state = lle->ln_state; + ndpc.rtm.rtm_index = ifp->if_index; + error = SYSCTL_OUT(wr, &ndpc, sizeof(ndpc)); + + return error; +} + +struct lltable * +in6_lltattach(struct ifnet *ifp) +{ + struct lltable *llt; + + llt = lltable_allocate_htbl(IN6_LLTBL_DEFAULT_HSIZE); + llt->llt_af = AF_INET6; + llt->llt_ifp = ifp; + + llt->llt_lookup = in6_lltable_lookup; + llt->llt_alloc_entry = in6_lltable_alloc; + llt->llt_delete_entry = in6_lltable_delete_entry; + llt->llt_dump_entry = in6_lltable_dump_entry; + llt->llt_hash = in6_lltable_hash; + llt->llt_fill_sa_entry = in6_lltable_fill_sa_entry; + llt->llt_free_entry = in6_lltable_free_entry; + llt->llt_match_prefix = in6_lltable_match_prefix; + lltable_link(llt); + + return llt; +} + +void +in6_ip6_to_sockaddr(const struct in6_addr *ip6, u_int16_t port, + struct sockaddr_in6 *sin6, u_int32_t maxlen) +{ + if (maxlen < sizeof(struct sockaddr_in6)) { + return; + } + + *sin6 = (struct sockaddr_in6) { + .sin6_family = AF_INET6, + .sin6_len = sizeof(*sin6), + .sin6_port = port, + .sin6_addr = *ip6, + }; + + if (IN6_IS_SCOPE_EMBED(&sin6->sin6_addr)) { + sin6->sin6_scope_id = ntohs(sin6->sin6_addr.s6_addr16[1]); + sin6->sin6_addr.s6_addr16[1] = 0; + } +} + +/* IPv6 events */ +struct in6_event { + in6_evhdlr_code_t in6_event_code; + struct ifnet *in6_ifp; + struct in6_addr in6_address; + uint32_t val; +}; + +struct in6_event2kev in6_event2kev_array[IN6_EVENT_MAX] = { + { + .in6_event_code = IN6_ADDR_MARKED_DUPLICATED, + .in6_event_kev_subclass = KEV_ND6_SUBCLASS, + .in6_event_kev_code = KEV_ND6_DAD_FAILURE, + .in6_event_str = "IN6_ADDR_MARKED_DUPLICATED", + }, + { + .in6_event_code = IN6_ADDR_MARKED_DETACHED, + .in6_event_kev_subclass = KEV_ND6_SUBCLASS, + .in6_event_kev_code = KEV_ND6_ADDR_DETACHED, + .in6_event_str = "IN6_ADDR_MARKED_DETACHED", + }, + { + .in6_event_code = IN6_ADDR_MARKED_DEPRECATED, + .in6_event_kev_subclass = KEV_ND6_SUBCLASS, + .in6_event_kev_code = KEV_ND6_ADDR_DEPRECATED, + .in6_event_str = "IN6_ADDR_MARKED_DEPRECATED", + }, + { + .in6_event_code = IN6_NDP_RTR_EXPIRY, + .in6_event_kev_subclass = KEV_ND6_SUBCLASS, + .in6_event_kev_code = KEV_ND6_RTR_EXPIRED, + .in6_event_str = "IN6_NDP_RTR_EXPIRY", + }, + { + .in6_event_code = IN6_NDP_PFX_EXPIRY, + .in6_event_kev_subclass = KEV_ND6_SUBCLASS, + .in6_event_kev_code = KEV_ND6_PFX_EXPIRED, + .in6_event_str = "IN6_NDP_PFX_EXPIRY", + }, + { + .in6_event_code = IN6_NDP_ADDR_EXPIRY, + .in6_event_kev_subclass = KEV_ND6_SUBCLASS, + .in6_event_kev_code = KEV_ND6_ADDR_EXPIRED, + .in6_event_str = "IN6_NDP_ADDR_EXPIRY", + }, +}; + +void +in6_eventhdlr_callback(struct eventhandler_entry_arg arg0 __unused, + in6_evhdlr_code_t in6_ev_code, struct ifnet *ifp, + struct in6_addr *p_addr6, uint32_t val) +{ + struct kev_msg ev_msg; + struct kev_nd6_event nd6_event; + + bzero(&ev_msg, sizeof(ev_msg)); + bzero(&nd6_event, sizeof(nd6_event)); + + nd6log0(info, "%s Event %s received for %s\n", + __func__, in6_event2kev_array[in6_ev_code].in6_event_str, + ip6_sprintf(p_addr6)); + + ev_msg.vendor_code = KEV_VENDOR_APPLE; + ev_msg.kev_class = KEV_NETWORK_CLASS; + ev_msg.kev_subclass = + in6_event2kev_array[in6_ev_code].in6_event_kev_subclass; + ev_msg.event_code = + in6_event2kev_array[in6_ev_code].in6_event_kev_code; + + nd6_event.link_data.if_family = ifp->if_family; + nd6_event.link_data.if_unit = ifp->if_unit; + strlcpy(nd6_event.link_data.if_name, ifp->if_name, + sizeof(nd6_event.link_data.if_name)); + + VERIFY(p_addr6 != NULL); + bcopy(p_addr6, &nd6_event.in6_address, + sizeof(nd6_event.in6_address)); + nd6_event.val = val; + + ev_msg.dv[0].data_ptr = &nd6_event; + ev_msg.dv[0].data_length = sizeof(nd6_event); + + kev_post_msg(&ev_msg); +} + +static void +in6_event_callback(void *arg) +{ + struct in6_event *p_in6_ev = (struct in6_event *)arg; + + EVENTHANDLER_INVOKE(&in6_evhdlr_ctxt, in6_event, + p_in6_ev->in6_event_code, p_in6_ev->in6_ifp, + &p_in6_ev->in6_address, p_in6_ev->val); +} + +struct in6_event_nwk_wq_entry { + struct nwk_wq_entry nwk_wqe; + struct in6_event in6_ev_arg; +}; + +void +in6_event_enqueue_nwk_wq_entry(in6_evhdlr_code_t in6_event_code, + struct ifnet *ifp, struct in6_addr *p_addr6, + uint32_t val) +{ + struct in6_event_nwk_wq_entry *p_in6_ev = NULL; + + MALLOC(p_in6_ev, struct in6_event_nwk_wq_entry *, + sizeof(struct in6_event_nwk_wq_entry), + M_NWKWQ, M_WAITOK | M_ZERO); + + p_in6_ev->nwk_wqe.func = in6_event_callback; + p_in6_ev->nwk_wqe.is_arg_managed = TRUE; + p_in6_ev->nwk_wqe.arg = &p_in6_ev->in6_ev_arg; + + p_in6_ev->in6_ev_arg.in6_event_code = in6_event_code; + p_in6_ev->in6_ev_arg.in6_ifp = ifp; + if (p_addr6 != NULL) { + bcopy(p_addr6, &p_in6_ev->in6_ev_arg.in6_address, + sizeof(p_in6_ev->in6_ev_arg.in6_address)); + } + p_in6_ev->in6_ev_arg.val = val; + + nwk_wq_enqueue((struct nwk_wq_entry*)p_in6_ev); +} + +/* + * Caller must hold in6_ifaddr_rwlock as writer. + */ +static void +in6_iahash_remove(struct in6_ifaddr *ia) +{ + LCK_RW_ASSERT(&in6_ifaddr_rwlock, LCK_RW_ASSERT_EXCLUSIVE); + IFA_LOCK_ASSERT_HELD(&ia->ia_ifa); + + if (!IA6_IS_HASHED(ia)) { + panic("%s: attempt to remove wrong ia %p from ipv6 hash table\n", __func__, ia); + /* NOTREACHED */ + } + TAILQ_REMOVE(IN6ADDR_HASH(&ia->ia_addr.sin6_addr), ia, ia6_hash); + IA6_HASH_INIT(ia); + if (IFA_REMREF_LOCKED(&ia->ia_ifa) == NULL) { + panic("%s: unexpected (missing) refcnt ifa=%p", __func__, + &ia->ia_ifa); + /* NOTREACHED */ + } +} + +/* + * Caller must hold in6_ifaddr_rwlock as writer. + */ +static void +in6_iahash_insert(struct in6_ifaddr *ia) +{ + LCK_RW_ASSERT(&in6_ifaddr_rwlock, LCK_RW_ASSERT_EXCLUSIVE); + IFA_LOCK_ASSERT_HELD(&ia->ia_ifa); + + if (ia->ia_addr.sin6_family != AF_INET6) { + panic("%s: attempt to insert wrong ia %p into hash table\n", __func__, ia); + /* NOTREACHED */ + } else if (IA6_IS_HASHED(ia)) { + panic("%s: attempt to double-insert ia %p into hash table\n", __func__, ia); + /* NOTREACHED */ + } + TAILQ_INSERT_HEAD(IN6ADDR_HASH(&ia->ia_addr.sin6_addr), + ia, ia6_hash); + IFA_ADDREF_LOCKED(&ia->ia_ifa); +} + +/* + * Some point to point interfaces that are tunnels borrow the address from + * an underlying interface (e.g. VPN server). In order for source address + * selection logic to find the underlying interface first, we add the address + * of borrowing point to point interfaces at the end of the list. + * (see rdar://6733789) + * + * Caller must hold in6_ifaddr_rwlock as writer. + */ +static void +in6_iahash_insert_ptp(struct in6_ifaddr *ia) +{ + struct in6_ifaddr *tmp_ifa; + struct ifnet *tmp_ifp; + + LCK_RW_ASSERT(&in6_ifaddr_rwlock, LCK_RW_ASSERT_EXCLUSIVE); + IFA_LOCK_ASSERT_HELD(&ia->ia_ifa); + + if (ia->ia_addr.sin6_family != AF_INET6) { + panic("%s: attempt to insert wrong ia %p into hash table\n", __func__, ia); + /* NOTREACHED */ + } else if (IA6_IS_HASHED(ia)) { + panic("%s: attempt to double-insert ia %p into hash table\n", __func__, ia); + /* NOTREACHED */ + } + IFA_UNLOCK(&ia->ia_ifa); + TAILQ_FOREACH(tmp_ifa, IN6ADDR_HASH(&ia->ia_addr.sin6_addr), ia6_hash) { + IFA_LOCK(&tmp_ifa->ia_ifa); + /* ia->ia_addr won't change, so check without lock */ + if (IN6_ARE_ADDR_EQUAL(&tmp_ifa->ia_addr.sin6_addr, &ia->ia_addr.sin6_addr)) { + IFA_UNLOCK(&tmp_ifa->ia_ifa); + break; + } + IFA_UNLOCK(&tmp_ifa->ia_ifa); + } + tmp_ifp = (tmp_ifa == NULL) ? NULL : tmp_ifa->ia_ifp; + + IFA_LOCK(&ia->ia_ifa); + if (tmp_ifp == NULL) { + TAILQ_INSERT_HEAD(IN6ADDR_HASH(&ia->ia_addr.sin6_addr), + ia, ia6_hash); + } else { + TAILQ_INSERT_TAIL(IN6ADDR_HASH(&ia->ia_addr.sin6_addr), + ia, ia6_hash); + } + IFA_ADDREF_LOCKED(&ia->ia_ifa); +}