]> git.saurik.com Git - apple/xnu.git/blobdiff - bsd/netinet6/ip6_output.c
xnu-792.18.15.tar.gz
[apple/xnu.git] / bsd / netinet6 / ip6_output.c
index 3d5eebd7818dbf9772ce4bfb3c5d6bd5924682c6..c8b578e6cb2909510c5e76affc99c2f1cb4182b3 100644 (file)
@@ -76,6 +76,7 @@
 #include <sys/systm.h>
 #include <sys/kernel.h>
 #include <sys/proc.h>
+#include <sys/kauth.h>
 
 #include <net/if.h>
 #include <net/route.h>
 #endif
 #include <netkey/key.h>
 extern int ipsec_bypass;
+extern lck_mtx_t *sadb_mutex;
+extern lck_mtx_t *nd6_mutex;
 #endif /* IPSEC */
 
 #include <netinet6/ip6_fw.h>
 
 #include <net/net_osdep.h>
 
+#include <netinet/kpi_ipfilter_var.h>
+
 #ifndef __APPLE__
 static MALLOC_DEFINE(M_IPMOPTS, "ip6_moptions", "internet multicast options");
 #endif
 
 
-static u_long  lo_dl_tag = 0;
 extern u_long  route_generation;
 
-
 struct ip6_exthdrs {
        struct mbuf *ip6e_ip6;
        struct mbuf *ip6e_hbh;
@@ -120,19 +123,20 @@ struct ip6_exthdrs {
        struct mbuf *ip6e_dest2;
 };
 
-static int ip6_pcbopts __P((struct ip6_pktopts **, struct mbuf *,
-                           struct socket *, struct sockopt *sopt));
-static int ip6_setmoptions __P((int, struct inpcb *, struct mbuf *));
-static int ip6_getmoptions __P((int, struct ip6_moptions *, struct mbuf **));
-static int ip6_copyexthdr __P((struct mbuf **, caddr_t, int));
-static int ip6_insertfraghdr __P((struct mbuf *, struct mbuf *, int,
-                                 struct ip6_frag **));
-static int ip6_insert_jumboopt __P((struct ip6_exthdrs *, u_int32_t));
-static int ip6_splithdr __P((struct mbuf *, struct ip6_exthdrs *));
+static int ip6_pcbopts(struct ip6_pktopts **, struct mbuf *,
+                           struct socket *, struct sockopt *sopt);
+static int ip6_setmoptions(int, struct inpcb *, struct mbuf *);
+static int ip6_getmoptions(int, struct ip6_moptions *, struct mbuf **);
+static int ip6_copyexthdr(struct mbuf **, caddr_t, int);
+static int ip6_insertfraghdr(struct mbuf *, struct mbuf *, int,
+                                 struct ip6_frag **);
+static int ip6_insert_jumboopt(struct ip6_exthdrs *, u_int32_t);
+static int ip6_splithdr(struct mbuf *, struct ip6_exthdrs *);
 
 extern int ip_createmoptions(struct ip_moptions **imop);
 extern int ip_addmembership(struct ip_moptions *imo, struct ip_mreq *mreq);
 extern int ip_dropmembership(struct ip_moptions *imo, struct ip_mreq *mreq);
+extern lck_mtx_t *ip6_mutex;
 
 /*
  * IP6 output. The packet in mbuf chain m contains a skeletal IP6
@@ -146,13 +150,14 @@ extern int ip_dropmembership(struct ip_moptions *imo, struct ip_mreq *mreq);
  * which is rt_rmx.rmx_mtu.
  */
 int
-ip6_output(m0, opt, ro, flags, im6o, ifpp)
-       struct mbuf *m0;
-       struct ip6_pktopts *opt;
-       struct route_in6 *ro;
-       int flags;
-       struct ip6_moptions *im6o;
-       struct ifnet **ifpp;            /* XXX: just for statistics */
+ip6_output(
+       struct mbuf *m0,
+       struct ip6_pktopts *opt,
+       struct route_in6 *ro,
+       int flags,
+       struct ip6_moptions *im6o,
+       struct ifnet **ifpp,    /* XXX: just for statistics */
+       int    locked)          
 {
        struct ip6_hdr *ip6, *mhip6;
        struct ifnet *ifp, *origifp;
@@ -169,20 +174,26 @@ ip6_output(m0, opt, ro, flags, im6o, ifpp)
        struct route_in6 *ro_pmtu = NULL;
        int hdrsplit = 0;
        int needipsec = 0;
+       ipfilter_t inject_filter_ref;
+       
 #if IPSEC
        int needipsectun = 0;
        struct socket *so = NULL;
        struct secpolicy *sp = NULL;
 
+       if (!locked)
+               lck_mtx_lock(ip6_mutex);
        /* for AH processing. stupid to have "socket" variable in IP layer... */
        if (ipsec_bypass == 0)
        {
                so = ipsec_getsocket(m);
                (void)ipsec_setsocket(m, NULL);
        }
-       ip6 = mtod(m, struct ip6_hdr *);
 #endif /* IPSEC */
 
+       ip6 = mtod(m, struct ip6_hdr *);
+       inject_filter_ref = ipf_get_inject_filter(m);
+
 #define MAKE_EXTHDR(hp, mp)                                            \
     do {                                                               \
        if (hp) {                                                       \
@@ -210,7 +221,8 @@ ip6_output(m0, opt, ro, flags, im6o, ifpp)
 #if IPSEC
        if (ipsec_bypass != 0)
                goto skip_ipsec;
-
+       
+       lck_mtx_lock(sadb_mutex);
        /* get a security policy for this packet */
        if (so == NULL)
                sp = ipsec6_getpolicybyaddr(m, IPSEC_DIR_OUTBOUND, 0, &error);
@@ -219,6 +231,7 @@ ip6_output(m0, opt, ro, flags, im6o, ifpp)
 
        if (sp == NULL) {
                ipsec6stat.out_inval++;
+               lck_mtx_unlock(sadb_mutex);
                goto freehdrs;
        }
 
@@ -231,6 +244,7 @@ ip6_output(m0, opt, ro, flags, im6o, ifpp)
                 * This packet is just discarded.
                 */
                ipsec6stat.out_polvio++;
+               lck_mtx_unlock(sadb_mutex);
                goto freehdrs;
 
        case IPSEC_POLICY_BYPASS:
@@ -243,6 +257,7 @@ ip6_output(m0, opt, ro, flags, im6o, ifpp)
                if (sp->req == NULL) {
                        /* acquire a policy */
                        error = key_spdacquire(sp);
+                       lck_mtx_unlock(sadb_mutex);
                        goto freehdrs;
                }
                needipsec = 1;
@@ -252,6 +267,7 @@ ip6_output(m0, opt, ro, flags, im6o, ifpp)
        default:
                printf("ip6_output: Invalid policy found. %d\n", sp->policy);
        }
+       lck_mtx_unlock(sadb_mutex);
        skip_ipsec:
 #endif /* IPSEC */
 
@@ -361,6 +377,61 @@ ip6_output(m0, opt, ro, flags, im6o, ifpp)
                MAKE_CHAIN(exthdrs.ip6e_rthdr, mprev,
                           nexthdrp, IPPROTO_ROUTING);
 
+               if (!TAILQ_EMPTY(&ipv6_filters)) {
+                       struct ipfilter *filter;
+                       int seen = (inject_filter_ref == 0);
+                       int     fixscope = 0;
+                       struct ipf_pktopts *ippo = 0, ipf_pktopts;
+                                               
+                       if (im6o != NULL && IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst)) {
+                               ippo = &ipf_pktopts;
+                               ippo->ippo_flags = IPPOF_MCAST_OPTS;
+                               ippo->ippo_mcast_ifnet = im6o->im6o_multicast_ifp;
+                               ippo->ippo_mcast_ttl = im6o->im6o_multicast_hlim;
+                               ippo->ippo_mcast_loop = im6o->im6o_multicast_loop;
+                       }
+
+                       /* Hack: embed the scope_id in the destination */
+                       if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_dst) &&
+                               (ip6->ip6_dst.s6_addr16[1] == 0) && (ro != NULL)) {
+                               fixscope = 1;
+                               ip6->ip6_dst.s6_addr16[1] = htons(ro->ro_dst.sin6_scope_id);
+                       }
+                       {
+                               lck_mtx_unlock(ip6_mutex);
+                               ipf_ref();
+                               TAILQ_FOREACH(filter, &ipv6_filters, ipf_link) {
+                                       /*
+                                        * No need to proccess packet twice if we've 
+                                        * already seen it
+                                        */
+                                       if (seen == 0) {
+                                               if ((struct ipfilter *)inject_filter_ref == filter)
+                                                       seen = 1;
+                                       } else if (filter->ipf_filter.ipf_output) {
+                                               errno_t result;
+                                               
+                                               result = filter->ipf_filter.ipf_output(filter->ipf_filter.cookie, (mbuf_t*)&m, ippo);
+                                               if (result == EJUSTRETURN) {
+                                                       ipf_unref();
+                                                       locked = 1; /* Don't want to take lock to unlock it right away */
+                                                       goto done;
+                                               }
+                                               if (result != 0) {
+                                                       ipf_unref();
+                                                       locked = 1; /* Don't want to take lock to unlock it right away */
+                                                       goto bad;
+                                               }
+                                       }
+                               }
+                               ipf_unref();
+                               lck_mtx_lock(ip6_mutex);
+                       }
+                       /* Hack: cleanup embedded scope_id if we put it there */
+                       if (fixscope)
+                               ip6->ip6_dst.s6_addr16[1] = 0;
+               }
+
 #if IPSEC
                if (!needipsec)
                        goto skip_ipsec2;
@@ -385,8 +456,12 @@ ip6_output(m0, opt, ro, flags, im6o, ifpp)
 
                bzero(&state, sizeof(state));
                state.m = m;
+               lck_mtx_unlock(ip6_mutex);
+               lck_mtx_lock(sadb_mutex);
                error = ipsec6_output_trans(&state, nexthdrp, mprev, sp, flags,
                        &needipsectun);
+               lck_mtx_unlock(sadb_mutex);
+               lck_mtx_lock(ip6_mutex);
                m = state.m;
                if (error) {
                        /* mbuf is already reclaimed in ipsec6_output_trans. */
@@ -512,9 +587,11 @@ skip_ipsec2:;
                state.m = m;
                state.ro = (struct route *)ro;
                state.dst = (struct sockaddr *)dst;
-
+               lck_mtx_unlock(ip6_mutex);
+               lck_mtx_lock(sadb_mutex);
                error = ipsec6_output_tunnel(&state, sp, flags);
-
+               lck_mtx_unlock(sadb_mutex);
+               lck_mtx_lock(ip6_mutex);
                m = state.m;
                ro = (struct route_in6 *)state.ro;
                dst = (struct sockaddr_in6 *)state.dst;
@@ -537,6 +614,7 @@ skip_ipsec2:;
                                error = 0;
                                break;
                        }
+                       lck_mtx_unlock(sadb_mutex);
                        goto bad;
                }
 
@@ -554,16 +632,18 @@ skip_ipsec2:;
                 * if an interface is specified from an upper layer,
                 * ifp must point it.
                 */
+               lck_mtx_lock(rt_mtx);
                if (ro->ro_rt == 0) {
                        /*
                         * non-bsdi always clone routes, if parent is
                         * PRF_CLONING.
                         */
-                       rtalloc((struct route *)ro);
+                       rtalloc_ign_locked((struct route *)ro, 0UL);
                }
                if (ro->ro_rt == 0) {
                        ip6stat.ip6s_noroute++;
                        error = EHOSTUNREACH;
+                       lck_mtx_unlock(rt_mtx);
                        /* XXX in6_ifstat_inc(ifp, ifs6_out_discard); */
                        goto bad;
                }
@@ -572,6 +652,7 @@ skip_ipsec2:;
                ro->ro_rt->rt_use++;
                if (ro->ro_rt->rt_flags & RTF_GATEWAY)
                        dst = (struct sockaddr_in6 *)ro->ro_rt->rt_gateway;
+               lck_mtx_unlock(rt_mtx);
                m->m_flags &= ~(M_BCAST | M_MCAST);     /* just in case */
 
                in6_ifstat_inc(ifp, ifs6_out_request);
@@ -652,12 +733,14 @@ skip_ipsec2:;
                 * ``net'' ff00::/8).
                 */
                if (ifp == NULL) {
+                       lck_mtx_lock(rt_mtx);
                        if (ro->ro_rt == 0) {
-                               ro->ro_rt = rtalloc1((struct sockaddr *)
+                               ro->ro_rt = rtalloc1_locked((struct sockaddr *)
                                                &ro->ro_dst, 0, 0UL);
                        }
                        if (ro->ro_rt == 0) {
                                ip6stat.ip6s_noroute++;
+                               lck_mtx_unlock(rt_mtx);
                                error = EHOSTUNREACH;
                                /* XXX in6_ifstat_inc(ifp, ifs6_out_discard) */
                                goto bad;
@@ -665,6 +748,7 @@ skip_ipsec2:;
                        ia = ifatoia6(ro->ro_rt->rt_ifa);
                        ifp = ro->ro_rt->rt_ifp;
                        ro->ro_rt->rt_use++;
+                       lck_mtx_unlock(rt_mtx);
                }
 
                if ((flags & IPV6_FORWARDING) == 0)
@@ -680,7 +764,9 @@ skip_ipsec2:;
                        error = ENETUNREACH;
                        goto bad;
                }
+               ifnet_lock_shared(ifp);
                IN6_LOOKUP_MULTI(ip6->ip6_dst, ifp, in6m);
+               ifnet_lock_done(ifp);
                if (in6m != NULL &&
                   (im6o == NULL || im6o->im6o_multicast_loop)) {
                        /*
@@ -836,7 +922,7 @@ skip_ipsec2:;
                u_short port = 0;
                m->m_pkthdr.rcvif = NULL;       /* XXX */
                /* If ipfw says divert, we have to just drop packet */
-               if ((*ip6_fw_chk_ptr)(&ip6, ifp, &port, &m)) {
+               if (ip6_fw_chk_ptr(&ip6, ifp, &port, &m)) {
                        m_freem(m);
                        goto done;
                }
@@ -914,7 +1000,7 @@ skip_ipsec2:;
                ipsec_delaux(m);
 #endif
 
-               error = nd6_output(ifp, origifp, m, dst, ro->ro_rt);
+               error = nd6_output(ifp, origifp, m, dst, ro->ro_rt, 1);
                goto done;
        } else if (mtu < IPV6_MMTU) {
                /*
@@ -1011,7 +1097,8 @@ skip_ipsec2:;
                        }
                        m_cat(m, m_frgpart);
                        m->m_pkthdr.len = len + hlen + sizeof(*ip6f);
-                       m->m_pkthdr.rcvif = (struct ifnet *)0;
+                       m->m_pkthdr.rcvif = 0;
+                       m->m_pkthdr.socket_id = m0->m_pkthdr.socket_id;
                        ip6f->ip6f_reserved = 0;
                        ip6f->ip6f_ident = id;
                        ip6f->ip6f_nxt = nextproto;
@@ -1044,7 +1131,8 @@ sendorfree:
                        /* clean ipsec history once it goes out of the node */
                        ipsec_delaux(m);
 #endif
-                       error = nd6_output(ifp, origifp, m, dst, ro->ro_rt);
+                       error = nd6_output(ifp, origifp, m, dst, ro->ro_rt, 1);
+
                } else
                        m_freem(m);
        }
@@ -1053,6 +1141,8 @@ sendorfree:
                ip6stat.ip6s_fragmented++;
 
 done:
+       if (!locked)
+               lck_mtx_unlock(ip6_mutex);
        if (ro == &ip6route && ro->ro_rt) { /* brace necessary for rtfree */
                rtfree(ro->ro_rt);
        } else if (ro_pmtu == &ip6route && ro_pmtu->ro_rt) {
@@ -1060,8 +1150,11 @@ done:
        }
 
 #if IPSEC
-       if (sp != NULL)
+       if (sp != NULL) {
+               lck_mtx_lock(sadb_mutex);
                key_freesp(sp);
+               lck_mtx_unlock(sadb_mutex);
+       }
 #endif /* IPSEC */
 
        return(error);
@@ -1267,6 +1360,7 @@ ip6_ctloutput(so, sopt)
        int optlen;
        struct proc *p;
 
+       level = error = optval = 0;
        if (sopt == NULL) 
                panic("ip6_ctloutput: arg soopt is NULL");
        else {
@@ -1276,9 +1370,8 @@ ip6_ctloutput(so, sopt)
                optlen = sopt->sopt_valsize;
                p = sopt->sopt_p;
        }
-       error = optval = 0;
 
-       privileged = (p == 0 || suser(p->p_ucred, &p->p_acflag)) ? 0 : 1;
+       privileged = (p == 0 || proc_suser(p)) ? 0 : 1;
 
        if (level == IPPROTO_IPV6) {
                switch (op) {
@@ -1289,6 +1382,10 @@ ip6_ctloutput(so, sopt)
                        {
                                struct mbuf *m;
 
+                               if (sopt->sopt_valsize > MCLBYTES) {
+                                       error = EMSGSIZE;
+                                       break;
+                               }
                                error = soopt_getm(sopt, &m); /* XXX */
                                if (error != NULL)
                                        break;
@@ -1481,6 +1578,10 @@ do { \
                                size_t len = 0;
                                struct mbuf *m;
 
+                                if (sopt->sopt_valsize > MCLBYTES) {
+                                        error = EMSGSIZE;
+                                        break;
+                                }
                                if ((error = soopt_getm(sopt, &m)) != 0) /* XXX */
                                        break;
                                if ((error = soopt_mcopyin(sopt, m)) != 0) /* XXX */
@@ -1489,8 +1590,10 @@ do { \
                                        req = mtod(m, caddr_t);
                                        len = m->m_len;
                                }
+                               lck_mtx_lock(sadb_mutex);
                                error = ipsec6_set_policy(in6p, optname, req,
                                                          len, privileged);
+                               lck_mtx_unlock(sadb_mutex);
                                m_freem(m);
                            }
                                break;
@@ -1575,8 +1678,8 @@ do { \
                        case IPV6_HOPOPTS:
                        case IPV6_RTHDR:
                        case IPV6_DSTOPTS:
-                               if (optname == IPV6_HOPOPTS ||
-                                   optname == IPV6_DSTOPTS ||
+                               if ((optname == IPV6_HOPOPTS ||
+                                   optname == IPV6_DSTOPTS) &&
                                    !privileged)
                                        return(EPERM);
                                switch (optname) {
@@ -1628,6 +1731,10 @@ do { \
                                struct mbuf *m = NULL;
                                struct mbuf **mp = &m;
 
+                                if (sopt->sopt_valsize > MCLBYTES) {
+                                        error = EMSGSIZE;
+                                        break;
+                                }
                                error = soopt_getm(sopt, &m); /* XXX */
                                if (error != NULL)
                                        break;
@@ -1638,7 +1745,9 @@ do { \
                                        req = mtod(m, caddr_t);
                                        len = m->m_len;
                                }
+                               lck_mtx_lock(sadb_mutex);
                                error = ipsec6_get_policy(in6p, req, len, mp);
+                               lck_mtx_unlock(sadb_mutex);
                                if (error == 0)
                                        error = soopt_mcopyout(sopt, m); /*XXX*/
                                if (error == 0 && m)
@@ -1708,7 +1817,7 @@ ip6_pcbopts(pktopt, m, so, sopt)
        }
 
        /*  set options specified by user. */
-       if (p && !suser(p->p_ucred, &p->p_acflag))
+       if (p && !proc_suser(p))
                priv = 1;
        if ((error = ip6_setpktoptions(m, opt, priv, 1)) != 0) {
                ip6_clearpktopts(opt, 1, -1); /* XXX: discard all options */
@@ -1857,10 +1966,10 @@ ip6_freepcbopts(pktopt)
  * Set the IP6 multicast options in response to user setsockopt().
  */
 static int
-ip6_setmoptions(optname, in6p, m)
-       int optname;
-       struct inpcb* in6p;
-       struct mbuf *m;
+ip6_setmoptions(
+       int optname,
+       struct inpcb* in6p,
+       struct mbuf *m)
 {
        int error = 0;
        u_int loop, ifindex;
@@ -1993,7 +2102,7 @@ ip6_setmoptions(optname, in6p, m)
                         * all multicast addresses. Only super user is allowed
                         * to do this.
                         */
-                       if (suser(p->p_ucred, &p->p_acflag))
+                       if (suser(kauth_cred_get(), 0))
                        {
                                error = EACCES;
                                break;
@@ -2010,12 +2119,14 @@ ip6_setmoptions(optname, in6p, m)
 
                                ifp = ifindex2ifnet[mreq->ipv6mr_interface];
 
+                               lck_mtx_lock(rt_mtx);
                                TAILQ_FOREACH(ifa, &in_ifaddrhead, ia_link) {
                                        if (ifa->ia_ifp == ifp) {
                                                v4req.imr_interface = IA_SIN(ifa)->sin_addr;
                                                break;
                                        }
                                }
+                               lck_mtx_unlock(rt_mtx);
                                
                                if (v4req.imr_multiaddr.s_addr == 0) {
                                        /* Interface has no IPv4 address. */
@@ -2081,6 +2192,7 @@ ip6_setmoptions(optname, in6p, m)
                /*
                 * See if the membership already exists.
                 */
+               lck_mtx_lock(nd6_mutex);
                for (imm = im6o->im6o_memberships.lh_first;
                     imm != NULL; imm = imm->i6mm_chain.le_next)
                        if (imm->i6mm_maddr->in6m_ifp == ifp &&
@@ -2089,6 +2201,7 @@ ip6_setmoptions(optname, in6p, m)
                                break;
                if (imm != NULL) {
                        error = EADDRINUSE;
+                       lck_mtx_unlock(nd6_mutex);
                        break;
                }
                /*
@@ -2098,14 +2211,17 @@ ip6_setmoptions(optname, in6p, m)
                imm = _MALLOC(sizeof(*imm), M_IPMADDR, M_WAITOK);
                if (imm == NULL) {
                        error = ENOBUFS;
+                       lck_mtx_unlock(nd6_mutex);
                        break;
                }
                if ((imm->i6mm_maddr =
-                    in6_addmulti(&mreq->ipv6mr_multiaddr, ifp, &error)) == NULL) {
+                    in6_addmulti(&mreq->ipv6mr_multiaddr, ifp, &error, 1)) == NULL) {
                        FREE(imm, M_IPMADDR);
+                       lck_mtx_unlock(nd6_mutex);
                        break;
                }
                LIST_INSERT_HEAD(&im6o->im6o_memberships, imm, i6mm_chain);
+               lck_mtx_unlock(nd6_mutex);
                break;
 
        case IPV6_LEAVE_GROUP:
@@ -2130,7 +2246,7 @@ ip6_setmoptions(optname, in6p, m)
                ifp = ifindex2ifnet[mreq->ipv6mr_interface];
                
                if (IN6_IS_ADDR_UNSPECIFIED(&mreq->ipv6mr_multiaddr)) {
-                       if (suser(p->p_ucred, &p->p_acflag)) {
+                       if (suser(kauth_cred_get(), 0)) {
                                error = EACCES;
                                break;
                        }
@@ -2143,12 +2259,14 @@ ip6_setmoptions(optname, in6p, m)
                        if (ifp != NULL) {
                                struct in_ifaddr *ifa;
                                
+                               lck_mtx_lock(rt_mtx);
                                TAILQ_FOREACH(ifa, &in_ifaddrhead, ia_link) {
                                        if (ifa->ia_ifp == ifp) {
                                                v4req.imr_interface = IA_SIN(ifa)->sin_addr;
                                                break;
                                        }
                                }
+                               lck_mtx_unlock(rt_mtx);
                        }
                        
                        error = ip_dropmembership(imo, &v4req);
@@ -2168,6 +2286,7 @@ ip6_setmoptions(optname, in6p, m)
                /*
                 * Find the membership in the membership list.
                 */
+               lck_mtx_lock(nd6_mutex);
                for (imm = im6o->im6o_memberships.lh_first;
                     imm != NULL; imm = imm->i6mm_chain.le_next) {
                        if ((ifp == NULL ||
@@ -2179,6 +2298,7 @@ ip6_setmoptions(optname, in6p, m)
                if (imm == NULL) {
                        /* Unable to resolve interface */
                        error = EADDRNOTAVAIL;
+                       lck_mtx_unlock(nd6_mutex);
                        break;
                }
                /*
@@ -2186,7 +2306,8 @@ ip6_setmoptions(optname, in6p, m)
                 * membership points.
                 */
                LIST_REMOVE(imm, i6mm_chain);
-               in6_delmulti(imm->i6mm_maddr);
+               in6_delmulti(imm->i6mm_maddr, 1);
+               lck_mtx_unlock(nd6_mutex);
                FREE(imm, M_IPMADDR);
                break;
 
@@ -2198,6 +2319,7 @@ ip6_setmoptions(optname, in6p, m)
        /*
         * If all options have default values, no need to keep the mbuf.
         */
+       lck_mtx_lock(nd6_mutex);
        if (im6o->im6o_multicast_ifp == NULL &&
            im6o->im6o_multicast_hlim == ip6_defmcasthlim &&
            im6o->im6o_multicast_loop == IPV6_DEFAULT_MULTICAST_LOOP &&
@@ -2213,6 +2335,7 @@ ip6_setmoptions(optname, in6p, m)
                ip_freemoptions(imo);
                in6p->inp_moptions = 0;
        }
+       lck_mtx_unlock(nd6_mutex);
 
        return(error);
 }
@@ -2275,13 +2398,15 @@ ip6_freemoptions(im6o)
 
        if (im6o == NULL)
                return;
-
+       
+       lck_mtx_lock(nd6_mutex);
        while ((imm = im6o->im6o_memberships.lh_first) != NULL) {
                LIST_REMOVE(imm, i6mm_chain);
                if (imm->i6mm_maddr)
-                       in6_delmulti(imm->i6mm_maddr);
+                       in6_delmulti(imm->i6mm_maddr, 1);
                FREE(imm, M_IPMADDR);
        }
+       lck_mtx_unlock(nd6_mutex);
        FREE(im6o, M_IPMOPTS);
 }
 
@@ -2360,8 +2485,12 @@ ip6_setpktoptions(control, opt, priv, needcopy)
                                ia6 = (struct in6_ifaddr *)ifa_ifwithaddr(sin6tosa(&sin6));
                                if (ia6 == NULL ||
                                    (ia6->ia6_flags & (IN6_IFF_ANYCAST |
-                                                      IN6_IFF_NOTREADY)) != 0)
+                                                      IN6_IFF_NOTREADY)) != 0) {
+                                       if (ia6) ifafree(&ia6->ia_ifa);
                                        return(EADDRNOTAVAIL);
+                               }
+                               ifafree(&ia6->ia_ifa);
+                               ia6 = NULL;
                        }
                        break;
 
@@ -2507,10 +2636,10 @@ ip6_setpktoptions(control, opt, priv, needcopy)
  * pointer that might NOT be &loif -- easier than replicating that code here.
  */
 void
-ip6_mloopback(ifp, m, dst)
-       struct ifnet *ifp;
-       struct mbuf *m;
-       struct sockaddr_in6 *dst;
+ip6_mloopback(
+       struct ifnet *ifp,
+       struct mbuf *m,
+       struct sockaddr_in6 *dst)
 {
        struct mbuf *copym;
        struct ip6_hdr *ip6;
@@ -2552,16 +2681,15 @@ ip6_mloopback(ifp, m, dst)
 
        /* Makes sure the HW checksum flags are cleaned before sending the packet */
 
-       copym->m_pkthdr.rcvif = (struct ifnet *)0;
+       copym->m_pkthdr.rcvif = 0;
        copym->m_pkthdr.csum_data = 0;
        copym->m_pkthdr.csum_flags = 0;
 
-       if (lo_dl_tag == 0)
-               dlil_find_dltag(APPLE_IF_FAM_LOOPBACK, 0, PF_INET, &lo_dl_tag);
-
-       if (lo_dl_tag) {
+       if (lo_ifp) {
                copym->m_pkthdr.rcvif = ifp;
-               dlil_output(lo_dl_tag, copym, 0, (struct sockaddr *)dst, 0);
+               lck_mtx_unlock(ip6_mutex);
+               dlil_output(lo_ifp, PF_INET6, copym, 0, (struct sockaddr *)dst, 0);
+               lck_mtx_lock(ip6_mutex);
        } else
                m_free(copym);
 #else