]> git.saurik.com Git - apple/xnu.git/blobdiff - bsd/netinet6/in6.c
xnu-7195.50.7.100.1.tar.gz
[apple/xnu.git] / bsd / netinet6 / in6.c
index 176bd11cc6367810288badcc7b7d00cad8144f86..dd680119e2e76a25d184b575e42d58b9acc47132 100644 (file)
@@ -164,9 +164,12 @@ const struct in6_addr in6addr_linklocal_allrouters =
     IN6ADDR_LINKLOCAL_ALLROUTERS_INIT;
 const struct in6_addr in6addr_linklocal_allv2routers =
     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;
@@ -196,26 +199,24 @@ 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_cgareq_32_to_64(struct in6_cgareq_32 *,
+static void in6_cgareq_32_to_64(const struct in6_cgareq_32 *,
     struct in6_cgareq_64 *);
 #else
-static void in6_cgareq_64_to_32(struct in6_cgareq_64 *,
+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_cgareq *in6_cgareq_to_native(void *, int,
-    struct in6_cgareq *);
 static int in6_to_kamescope(struct sockaddr_in6 *, struct ifnet *);
 static int in6_getassocids(struct socket *, uint32_t *, user_addr_t);
 static int in6_getconnids(struct socket *, sae_associd_t, uint32_t *,
@@ -273,10 +274,7 @@ static unsigned int in6ifa_debug = 1;           /* debugging (enabled) */
 #else
 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 */
 
 struct eventhandler_lists_ctxt in6_evhdlr_ctxt;
@@ -516,8 +514,8 @@ in6_aliasreq_64_to_32(struct in6_aliasreq_64 *src, struct in6_aliasreq_32 *dst)
        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;
 }
@@ -538,8 +536,8 @@ in6_aliasreq_32_to_64(struct in6_aliasreq_32 *src, struct in6_aliasreq_64 *dst)
 }
 
 #if defined(__LP64__)
-void
-in6_cgareq_32_to_64(struct in6_cgareq_32 *src,
+static void
+in6_cgareq_32_to_64(const struct in6_cgareq_32 *src,
     struct in6_cgareq_64 *dst)
 {
        bzero(dst, sizeof(*dst));
@@ -554,12 +552,13 @@ in6_cgareq_32_to_64(struct in6_cgareq_32 *src,
        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_cgareq_64_to_32(struct in6_cgareq_64 *src,
+static void
+in6_cgareq_64_to_32(const struct in6_cgareq_64 *src,
     struct in6_cgareq_32 *dst)
 {
        bzero(dst, sizeof(*dst));
@@ -574,6 +573,7 @@ in6_cgareq_64_to_32(struct in6_cgareq_64 *src,
        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
 
@@ -598,25 +598,25 @@ in6_aliasreq_to_native(void *data, int data_is_64, struct in6_aliasreq *dst)
        return dst;
 }
 
-static struct in6_cgareq *
-in6_cgareq_to_native(void *data, int is64, struct in6_cgareq *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));
+       if (user_is_64) {
+               bcopy(user_data, cgareq, sizeof(*cgareq));
        } else {
-               in6_cgareq_32_to_64((struct in6_cgareq_32 *)data,
-                   (struct in6_cgareq_64 *)dst);
+               in6_cgareq_32_to_64((const struct in6_cgareq_32 *)user_data,
+                   (struct in6_cgareq_64 *)cgareq);
        }
 #else
-       if (is64) {
-               in6_cgareq_64_to_32((struct in6_cgareq_64 *)data,
-                   (struct in6_cgareq_32 *)dst);
+       if (user_is_64) {
+               in6_cgareq_64_to_32((const struct in6_cgareq_64 *)user_data,
+                   (struct in6_cgareq_32 *)cgareq);
        } else {
-               bcopy(data, dst, sizeof(*dst));
+               bcopy(user_data, cgareq, sizeof(*cgareq));
        }
 #endif /* __LP64__ */
-       return dst;
 }
 
 static __attribute__((noinline)) int
@@ -870,7 +870,7 @@ in6ctl_cgastart(struct ifnet *ifp, u_long cmd, caddr_t data)
                 * to be centralized, so that it can be passed around to other
                 * routines that are expecting the kernel form.
                 */
-               in6_cgareq_to_native(data, is64, &llcgasr);
+               in6_cgareq_copy_from_user(data, is64, &llcgasr);
 
                /*
                 * NOTE: All the interface specific DLIL attachements
@@ -1160,7 +1160,7 @@ in6ctl_clat46start(struct ifnet *ifp)
 
                if ((pr->ndpr_stateflags & NDPRF_CLAT46) == 0
                    && pr->ndpr_vltime != 0) {
-                       NDPR_ADDREF_LOCKED(pr); /* Take reference for rest of the processing */
+                       NDPR_ADDREF(pr); /* Take reference for rest of the processing */
                        NDPR_UNLOCK(pr);
                        break;
                } else {
@@ -1171,14 +1171,16 @@ in6ctl_clat46start(struct ifnet *ifp)
        lck_mtx_unlock(nd6_mutex);
 
        if (pr != NULL) {
-               if ((ia6 = in6_pfx_newpersistaddr(pr, FALSE, &error, TRUE)) == NULL) {
-                       nd6log0(error, "Could not configure CLAT46 address on interface "
-                           "%s.\n", ifp->if_xname);
+               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_LOCKED(pr); /* for addr reference */
+                       NDPR_ADDREF(pr); /* for addr reference */
                        pr->ndpr_stateflags |= NDPRF_CLAT46;
                        pr->ndpr_addrcnt++;
                        VERIFY(pr->ndpr_addrcnt != 0);
@@ -1331,7 +1333,7 @@ in6_control(struct socket *so, u_long cmd, caddr_t data, struct ifnet *ifp,
                }
                error = in6ctl_clat46start(ifp);
                if (error == 0) {
-                       ifp->if_eflags |= IFEF_CLAT46;
+                       if_set_eflags(ifp, IFEF_CLAT46);
                }
                goto done;
 
@@ -1347,7 +1349,12 @@ in6_control(struct socket *so, u_long cmd, caddr_t data, struct ifnet *ifp,
                 * flushed.
                 * XXX Probably STOP equivalent is not needed here.
                 */
-               ifp->if_eflags &= ~IFEF_CLAT46;
+               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) {
@@ -1356,8 +1363,16 @@ in6_control(struct socket *so, u_long cmd, caddr_t data, struct ifnet *ifp,
                }
                bcopy(&((struct in6_ifreq *)(void *)data)->ifr_intval,
                    &intval, sizeof(intval));
-
-               error = in6_setrouter(ifp, 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 */
@@ -1389,13 +1404,15 @@ in6_control(struct socket *so, u_long cmd, caddr_t data, struct ifnet *ifp,
        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:        /* struct in6_ifreq */
-       case SIOCSIFCGAPREP_IN6:        /* struct in6_ifreq */
+       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 */
+               OS_FALLTHROUGH;
        case OSIOCGIFINFO_IN6:          /* struct in6_ondireq */
        case SIOCGIFINFO_IN6:           /* struct in6_ondireq */
        case SIOCGDRLST_IN6_32:         /* struct in6_drlist_32 */
@@ -1481,7 +1498,7 @@ in6_control(struct socket *so, u_long cmd, caddr_t data, struct ifnet *ifp,
                        error = EPERM;
                        goto done;
                }
-       /* FALLTHRU */
+               OS_FALLTHROUGH;
        case SIOCGIFADDR_IN6:           /* struct in6_ifreq */
        case SIOCGIFDSTADDR_IN6:        /* struct in6_ifreq */
        case SIOCGIFNETMASK_IN6:        /* struct in6_ifreq */
@@ -1556,7 +1573,7 @@ in6_control(struct socket *so, u_long cmd, caddr_t data, struct ifnet *ifp,
                        error = EADDRNOTAVAIL;
                        goto done;
                }
-       /* FALLTHROUGH */
+               OS_FALLTHROUGH;
        case SIOCAIFADDR_IN6_32:        /* struct in6_aliasreq_32 */
        case SIOCAIFADDR_IN6_64:        /* struct in6_aliasreq_64 */
                VERIFY(sa6 != NULL);
@@ -1639,7 +1656,8 @@ done:
 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;
 
@@ -1671,7 +1689,7 @@ in6ctl_aifaddr(struct ifnet *ifp, struct in6_aliasreq *ifra)
        VERIFY(ia != NULL);
 
        /* Now, make the prefix on-link on the interface. */
-       plen = in6_mask2len(&ifra->ifra_prefixmask.sin6_addr, NULL);
+       plen = (uint8_t)in6_mask2len(&ifra->ifra_prefixmask.sin6_addr, NULL);
        if (plen == 128) {
                goto done;
        }
@@ -1706,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);
 
@@ -1739,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,
@@ -1804,7 +1827,7 @@ in6ctl_difaddr(struct ifnet *ifp, struct in6_ifaddr *ia)
        IFA_LOCK(&ia->ia_ifa);
        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;
@@ -1854,19 +1877,17 @@ 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);
@@ -1903,20 +1924,28 @@ in6_autoconf(struct ifnet *ifp, int enable)
 }
 
 /*
- * 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 (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);
@@ -1933,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
@@ -2215,9 +2240,46 @@ in6_ifaupdate_aux(struct in6_ifaddr *ia, struct ifnet *ifp, int ifaupflags)
                IFA_LOCK(ifa);
                LIST_INSERT_HEAD(&ia->ia6_memberships, imm, i6mm_chain);
                IFA_UNLOCK(ifa);
-       }
 #undef  MLTMASK_LEN
 
+               /*
+                * 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) */
+
+               bzero(&mltaddr, sizeof(mltaddr));
+               mltaddr.sin6_len = sizeof(struct sockaddr_in6);
+               mltaddr.sin6_family = AF_INET6;
+               mltaddr.sin6_addr = in6addr_multicast_prefix;
+
+               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;
 
@@ -2330,7 +2392,7 @@ in6_update_ifa(struct ifnet *ifp, struct in6_aliasreq *ifra, int ifaupflags,
        }
 
        /* 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;
@@ -2361,7 +2423,26 @@ 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);
+       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;
        }
@@ -2400,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;
@@ -3128,7 +3209,7 @@ in6_localaddr(struct in6_addr *in6)
 {
        struct in6_ifaddr *ia;
 
-       if (IN6_IS_ADDR_LOOPBACK(in6) || IN6_IS_ADDR_LINKLOCAL(in6)) {
+       if (IN6_IS_ADDR_LOOPBACK(in6) || IN6_IS_ADDR_LINKLOCAL(in6) || IN6_IS_ADDR_MC_UNICAST_BASED_LINKLOCAL(in6)) {
                return 1;
        }
 
@@ -3992,8 +4073,8 @@ in6_post_msg(struct ifnet *ifp, u_int32_t event_code, struct in6_ifaddr *ifa,
 
                /* 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_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);
@@ -4029,31 +4110,22 @@ in6_ifaddr_init(void)
 
        PE_parse_boot_argn("ifa_debug", &in6ifa_debug, sizeof(in6ifa_debug));
 
-       in6ifa_size = (in6ifa_debug == 0) ? sizeof(struct in6_ifaddr) :
+       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;
@@ -4406,7 +4478,7 @@ in6_lltable_destroy_lle(struct llentry *lle)
 
 
 static struct llentry *
-in6_lltable_new(const struct in6_addr *addr6, u_int flags)
+in6_lltable_new(const struct in6_addr *addr6, uint16_t flags)
 {
 #pragma unused(flags)
        struct in6_llentry *lle;
@@ -4438,7 +4510,7 @@ in6_lltable_new(const struct in6_addr *addr6, u_int flags)
 
 static int
 in6_lltable_match_prefix(const struct sockaddr *saddr,
-    const struct sockaddr *smask, u_int flags, struct llentry *lle)
+    const struct sockaddr *smask, uint16_t flags, struct llentry *lle)
 {
        const struct in6_addr *addr, *mask, *lle_addr;
 
@@ -4497,7 +4569,7 @@ in6_lltable_free_entry(struct lltable *llt, struct llentry *lle)
 
 static int
 in6_lltable_rtcheck(struct ifnet *ifp,
-    u_int flags, const struct sockaddr *l3addr)
+    uint16_t flags, const struct sockaddr *l3addr)
 {
 #pragma unused(flags)
        struct rtentry *rt;
@@ -4590,7 +4662,7 @@ in6_lltable_delete_entry(struct lltable *llt, struct llentry *lle)
 }
 
 static struct llentry *
-in6_lltable_alloc(struct lltable *llt, u_int flags,
+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;
@@ -4615,7 +4687,7 @@ in6_lltable_alloc(struct lltable *llt, u_int flags,
                log(LOG_INFO, "lla_lookup: new lle malloc failed\n");
                return NULL;
        }
-       lle->la_flags = flags;
+       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;
@@ -4629,7 +4701,7 @@ in6_lltable_alloc(struct lltable *llt, u_int flags,
 }
 
 static struct llentry *
-in6_lltable_lookup(struct lltable *llt, u_int flags,
+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;
@@ -4722,9 +4794,9 @@ in6_lltable_dump_entry(struct lltable *llt, struct llentry *lle,
                clock_usec_t usecs;
 
                clock_get_calendar_microtime(&secs, &usecs);
-               ndpc.rtm.rtm_rmx.rmx_expire = lle->la_expire +
+               ndpc.rtm.rtm_rmx.rmx_expire = (int32_t)(lle->la_expire +
                    lle->lle_remtime / hz +
-                   secs - net_uptime();
+                   secs - net_uptime());
        }
        ndpc.rtm.rtm_flags |= (RTF_HOST | RTF_LLDATA);
        if (lle->la_flags & LLE_STATIC) {