]> git.saurik.com Git - apple/xnu.git/blobdiff - bsd/net/if_stf.c
xnu-6153.61.1.tar.gz
[apple/xnu.git] / bsd / net / if_stf.c
index 7bdf6faa5d39cb71a7652658d2f1492a0bb260ff..05e6087abb528330ae3173aee17f96c73528bfe5 100644 (file)
@@ -1,3 +1,31 @@
+/*
+ * Copyright (c) 2000-2018 Apple Inc. All rights reserved.
+ *
+ * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. The rights granted to you under the License
+ * may not be used to create, or enable the creation or redistribution of,
+ * unlawful or unlicensed copies of an Apple operating system, or to
+ * circumvent, violate, or enable the circumvention or violation of, any
+ * terms of an Apple operating system software license agreement.
+ *
+ * Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
+ */
+
 /*     $FreeBSD: src/sys/net/if_stf.c,v 1.1.2.6 2001/07/24 19:10:18 brooks Exp $       */
 /*     $KAME: if_stf.c,v 1.62 2001/06/07 22:32:16 itojun Exp $ */
 
  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  */
+/*
+ * NOTICE: This file was modified by SPARTA, Inc. in 2006 to introduce
+ * support for mandatory and extensible security protections.  This notice
+ * is included in support of clause 2.2 (b) of the Apple Public License,
+ * Version 2.0.
+ */
 
 /*
  * 6to4 interface, based on RFC3056.
@@ -60,7 +94,7 @@
  * ICMPv6:
  * - Redirects cannot be used due to the lack of link-local address.
  *
- * stf interface does not have, and will not need, a link-local address.  
+ * stf interface does not have, and will not need, a link-local address.
  * It seems to have no real benefit and does not help the above symptoms much.
  * Even if we assign link-locals to interface, we cannot really
  * use link-local unicast/multicast on top of 6to4 cloud (since there's no
 #include <sys/protosw.h>
 #include <sys/kernel.h>
 #include <sys/syslog.h>
-#include <machine/cpu.h>
 
 #include <sys/malloc.h>
 
+#include <kern/locks.h>
+
 #include <net/if.h>
 #include <net/route.h>
-#include <net/netisr.h>
 #include <net/if_types.h>
-#include <net/if_stf.h>
 
 #include <netinet/in.h>
 #include <netinet/in_systm.h>
 #include <netinet/ip_ecn.h>
 
 #include <netinet/ip_encap.h>
-#include <net/dlil.h>
+#include <net/kpi_interface.h>
+#include <net/kpi_protocol.h>
 
 
 #include <net/net_osdep.h>
 
 #include <net/bpf.h>
 
-#define IN6_IS_ADDR_6TO4(x)    (ntohs((x)->s6_addr16[0]) == 0x2002)
-#define GET_V4(x)      ((struct in_addr *)(&(x)->s6_addr16[1]))
+#if CONFIG_MACF_NET
+#include <security/mac_framework.h>
+#endif
+
+#define GET_V4(x) ((const struct in_addr *)(const void *)(&(x)->s6_addr16[1]))
+
+static lck_grp_t *stf_mtx_grp;
 
 struct stf_softc {
-       struct ifnet    sc_if;     /* common area */
-#ifdef __APPLE__
-       struct if_proto *stf_proto; /* dlil protocol attached */
-#endif
+       ifnet_t                         sc_if;     /* common area */
+       u_int32_t                               sc_protocol_family; /* dlil protocol attached */
        union {
                struct route  __sc_ro4;
                struct route_in6 __sc_ro6; /* just for safety */
        } __sc_ro46;
-#define sc_ro  __sc_ro46.__sc_ro4
+#define sc_ro   __sc_ro46.__sc_ro4
+       decl_lck_mtx_data(, sc_ro_mtx);
        const struct encaptab *encap_cookie;
+       bpf_tap_mode            tap_mode;
+       bpf_packet_func         tap_callback;
 };
 
-static struct stf_softc *stf;
-
-#ifdef __APPLE__
-void stfattach __P((void));
-int stf_pre_output __P((struct ifnet *, register struct mbuf **, struct sockaddr *,
-       caddr_t, char *, char *, u_long));
-static u_long stf_dl_tag=0;
-#endif
+void stfattach(void);
 
-#ifndef __APPLE__
-static MALLOC_DEFINE(M_STF, "stf", "6to4 Tunnel Interface");
-#endif
 static int ip_stf_ttl = 40;
+static int stf_init_done;
 
-extern  struct domain inetdomain;
-struct protosw in_stf_protosw =
-{ SOCK_RAW,    &inetdomain,    IPPROTO_IPV6,   PR_ATOMIC|PR_ADDR,
-  in_stf_input, rip_output,    0,              rip_ctloutput,
-  0,
-  0,            0,              0,              0,
-  0,
-  &rip_usrreqs
-};
+static void in_stf_input(struct mbuf *, int);
+static void stfinit(void);
 
-static int stf_encapcheck __P((const struct mbuf *, int, int, void *));
-static struct in6_ifaddr *stf_getsrcifa6 __P((struct ifnet *));
-int stf_pre_output __P((struct ifnet *, register struct mbuf **, struct sockaddr *,
-       caddr_t, char *, char *, u_long));
-static int stf_checkaddr4 __P((struct stf_softc *, struct in_addr *,
-       struct ifnet *));
-static int stf_checkaddr6 __P((struct stf_softc *, struct in6_addr *,
-       struct ifnet *));
-static void stf_rtrequest __P((int, struct rtentry *, struct sockaddr *));
-int stf_ioctl __P((struct ifnet *, u_long, void *));
+static struct protosw in_stf_protosw =
+{
+       .pr_type =              SOCK_RAW,
+       .pr_protocol =          IPPROTO_IPV6,
+       .pr_flags =             PR_ATOMIC | PR_ADDR,
+       .pr_input =             in_stf_input,
+       .pr_ctloutput =         rip_ctloutput,
+       .pr_usrreqs =           &rip_usrreqs,
+       .pr_unlock =            rip_unlock,
+};
 
+static int stf_encapcheck(const struct mbuf *, int, int, void *);
+static struct in6_ifaddr *stf_getsrcifa6(struct ifnet *);
+int stf_pre_output(struct ifnet *, protocol_family_t, struct mbuf **,
+    const struct sockaddr *, void *, char *, char *);
+static int stf_checkaddr4(struct stf_softc *, const struct in_addr *,
+    struct ifnet *);
+static int stf_checkaddr6(struct stf_softc *, struct in6_addr *,
+    struct ifnet *);
+static void stf_rtrequest(int, struct rtentry *, struct sockaddr *);
+static errno_t stf_ioctl(ifnet_t ifp, u_long cmd, void *data);
+static errno_t stf_output(ifnet_t ifp, mbuf_t m);
 
-static
-int  stf_add_if(struct ifnet *ifp)
+static void
+stfinit(void)
 {
-    ifp->if_demux  = 0;
-    ifp->if_framer = 0;
-    return 0;
+       if (!stf_init_done) {
+               stf_mtx_grp = lck_grp_alloc_init("stf", LCK_GRP_ATTR_NULL);
+               stf_init_done = 1;
+       }
 }
 
-static 
-int  stf_del_if(struct ifnet *ifp)
+/*
+ * gif_input is the input handler for IP and IPv6 attached to gif
+ */
+static errno_t
+stf_media_input(
+       __unused ifnet_t        ifp,
+       protocol_family_t       protocol_family,
+       mbuf_t                          m,
+       __unused char           *frame_header)
 {
-    return 0;
+       if (proto_input(protocol_family, m) != 0) {
+               m_freem(m);
+       }
+
+       return 0;
 }
 
-static
-int  stf_add_proto(struct ddesc_head_str *desc_head, struct if_proto *proto, u_long dl_tag)
-{       
+
+
+static errno_t
+stf_add_proto(
+       ifnet_t                                                                 ifp,
+       protocol_family_t                                               protocol_family,
+       __unused const struct ifnet_demux_desc  *demux_array,
+       __unused u_int32_t                                              demux_count)
+{
        /* Only one protocol may be attached at a time */
-       struct stf_softc* stf = (struct stf_softc*)proto->ifp;
-       if (stf->stf_proto == NULL)
-               stf->stf_proto = proto;
-       else {
+       struct stf_softc* stf = ifnet_softc(ifp);
+       if (stf->sc_protocol_family == 0) {
+               stf->sc_protocol_family = protocol_family;
+       else {
                printf("stf_add_proto: stf already has a proto\n");
-               return (EBUSY);
+               return EBUSY;
        }
 
-       return (0);
+       return 0;
 }
 
-static
-int  stf_del_proto(struct if_proto *proto, u_long dl_tag)
-{   
-       if (((struct stf_softc*)proto->ifp)->stf_proto == proto)
-               ((struct stf_softc*)proto->ifp)->stf_proto = NULL;
-       else
-               return ENOENT;
+static errno_t
+stf_del_proto(
+       ifnet_t                         ifp,
+       protocol_family_t       protocol_family)
+{
+       if (((struct stf_softc*)ifnet_softc(ifp))->sc_protocol_family == protocol_family) {
+               ((struct stf_softc*)ifnet_softc(ifp))->sc_protocol_family = 0;
+       }
 
        return 0;
 }
 
-int stf_shutdown()
+static errno_t
+stf_attach_inet6(
+       ifnet_t                         ifp,
+       protocol_family_t       protocol_family)
 {
-       return 0;
+       struct ifnet_attach_proto_param     reg;
+       errno_t                                                     stat;
+
+       if (protocol_family != PF_INET6) {
+               return EPROTONOSUPPORT;
+       }
+
+       bzero(&reg, sizeof(reg));
+       reg.input = stf_media_input;
+       reg.pre_output = stf_pre_output;
+
+       stat = ifnet_attach_protocol(ifp, protocol_family, &reg);
+       if (stat && stat != EEXIST) {
+               printf("stf_attach_proto_family can't attach interface fam=%d\n",
+                   protocol_family);
+       }
+
+       return stat;
 }
 
-void stf_reg_if_mods()
-{   
-     struct dlil_ifmod_reg_str  stf_ifmod;
-
-     bzero(&stf_ifmod, sizeof(stf_ifmod));
-     stf_ifmod.add_if  = stf_add_if;
-     stf_ifmod.del_if  = stf_del_if;
-     stf_ifmod.add_proto = stf_add_proto;
-     stf_ifmod.del_proto = stf_del_proto;
-     stf_ifmod.ifmod_ioctl = 0;
-     stf_ifmod.shutdown    = stf_shutdown;
-
-    
-    if (dlil_reg_if_modules(APPLE_IF_FAM_STF, &stf_ifmod))
-        panic("Couldn't register stf modules\n");
-    
-}   
-    
-u_long  stf_attach_inet6(struct ifnet *ifp)
-{       
-    struct dlil_proto_reg_str   reg;
-    struct dlil_demux_desc      desc;
-    short native=0;
-    int   stat, i;
-
-    if (stf_dl_tag != 0)
-               return stf_dl_tag;
-
-    TAILQ_INIT(&reg.demux_desc_head); 
-    desc.type = DLIL_DESC_RAW;
-    desc.variants.bitmask.proto_id_length = 0;
-    desc.variants.bitmask.proto_id = 0;
-    desc.variants.bitmask.proto_id_mask = 0;
-    desc.native_type = (char *) &native;
-    TAILQ_INSERT_TAIL(&reg.demux_desc_head, &desc, next);
-    reg.interface_family = ifp->if_family;
-    reg.unit_number      = ifp->if_unit;
-    reg.input            = 0;
-    reg.pre_output       = stf_pre_output;
-    reg.event            = 0;
-    reg.offer            = 0;
-    reg.ioctl            = 0;
-    reg.default_proto    = 0;
-    reg.protocol_family  = PF_INET6;
-
-    stat = dlil_attach_protocol(&reg, &stf_dl_tag);
-    if (stat) {
-        panic("stf_attach_inet6 can't attach interface\n");
-    }
-
-    return stf_dl_tag;
+static errno_t
+stf_demux(
+       ifnet_t                                 ifp,
+       __unused mbuf_t                 m,
+       __unused char                   *frame_ptr,
+       protocol_family_t               *protocol_family)
+{
+       struct stf_softc* stf = ifnet_softc(ifp);
+       *protocol_family = stf->sc_protocol_family;
+       return 0;
 }
 
-u_long  stf_detach_inet6(struct ifnet *ifp)
+static errno_t
+stf_set_bpf_tap(
+       ifnet_t                 ifp,
+       bpf_tap_mode    mode,
+       bpf_packet_func callback)
 {
-    u_long      ip_dl_tag = 0;
-    int         stat;
+       struct stf_softc        *sc = ifnet_softc(ifp);
 
-    stat = dlil_find_dltag(ifp->if_family, ifp->if_unit, AF_INET6, &ip_dl_tag);
-    if (stat == 0) {
-        stat = dlil_detach_protocol(ip_dl_tag);
-        if (stat) {
-            printf("WARNING: stf_detach can't detach IP AF_INET6 from interface\n");
-       }
-    }
-    return (stat);
-}
+       sc->tap_mode = mode;
+       sc->tap_callback = callback;
 
+       return 0;
+}
 
 void
 stfattach(void)
 {
-       struct ifnet *ifp;
        struct stf_softc *sc;
-       int i, error;
-
-
-       int err;
+       int error;
        const struct encaptab *p;
+       struct ifnet_init_eparams       stf_init;
+
+       stfinit();
 
-       stf_reg_if_mods(); /* DLIL modules */
+       error = proto_register_plumber(PF_INET6, APPLE_IF_FAM_STF,
+           stf_attach_inet6, NULL);
+       if (error != 0) {
+               printf("proto_register_plumber failed for AF_INET6 error=%d\n", error);
+       }
 
-       sc = _MALLOC(sizeof(struct stf_softc), M_DEVBUF, M_WAITOK);
+       sc = _MALLOC(sizeof(struct stf_softc), M_DEVBUF, M_WAITOK | M_ZERO);
        if (sc == 0) {
                printf("stf softc attach failed\n" );
                return;
        }
-               
-       bzero(sc, sizeof(*sc));
-       sc->sc_if.if_name = "stf";
-       sc->sc_if.if_unit = 0;
 
        p = encap_attach_func(AF_INET, IPPROTO_IPV6, stf_encapcheck,
            &in_stf_protosw, sc);
        if (p == NULL) {
-               printf("%s: attach failed\n", if_name(&sc->sc_if));
+               printf("sftattach encap_attach_func failed\n");
                FREE(sc, M_DEVBUF);
                return;
        }
        sc->encap_cookie = p;
-       sc->sc_if.if_mtu    = IPV6_MMTU;
-       sc->sc_if.if_flags  = 0;
-       sc->sc_if.if_ioctl  = stf_ioctl;
-       sc->sc_if.if_output = NULL; /* processing done in pre_output */
-       sc->sc_if.if_type   = IFT_STF;
-       sc->sc_if.if_family= APPLE_IF_FAM_STF;
+       lck_mtx_init(&sc->sc_ro_mtx, stf_mtx_grp, LCK_ATTR_NULL);
+
+       bzero(&stf_init, sizeof(stf_init));
+       stf_init.ver = IFNET_INIT_CURRENT_VERSION;
+       stf_init.len = sizeof(stf_init);
+       stf_init.flags = IFNET_INIT_LEGACY;
+       stf_init.name = "stf";
+       stf_init.unit = 0;
+       stf_init.type = IFT_STF;
+       stf_init.family = IFNET_FAMILY_STF;
+       stf_init.output = stf_output;
+       stf_init.demux = stf_demux;
+       stf_init.add_proto = stf_add_proto;
+       stf_init.del_proto = stf_del_proto;
+       stf_init.softc = sc;
+       stf_init.ioctl = stf_ioctl;
+       stf_init.set_bpf_tap = stf_set_bpf_tap;
+
+       error = ifnet_allocate_extended(&stf_init, &sc->sc_if);
+       if (error != 0) {
+               printf("stfattach, ifnet_allocate failed - %d\n", error);
+               encap_detach(sc->encap_cookie);
+               lck_mtx_destroy(&sc->sc_ro_mtx, stf_mtx_grp);
+               FREE(sc, M_DEVBUF);
+               return;
+       }
+       ifnet_set_mtu(sc->sc_if, IPV6_MMTU);
+       ifnet_set_flags(sc->sc_if, 0, 0xffff); /* clear all flags */
 #if 0
        /* turn off ingress filter */
-       sc->sc_if.if_flags  |= IFF_LINK2;
+       ifnet_set_flags(sc->sc_if, IFF_LINK2, IFF_LINK2);
+#endif
+
+#if CONFIG_MACF_NET
+       mac_ifnet_label_init(&sc->sc_if);
 #endif
-       sc->sc_if.if_snd.ifq_maxlen = IFQ_MAXLEN;
-
-       if (error = dlil_if_attach(&sc->sc_if))
-               printf("stfattach: can't dlil_if_attach error=%d\n");
-       else 
-               bpfattach(&sc->sc_if, DLT_NULL, sizeof(u_int));
-       
-       return ;
+
+       error = ifnet_attach(sc->sc_if, NULL);
+       if (error != 0) {
+               printf("stfattach: ifnet_attach returned error=%d\n", error);
+               encap_detach(sc->encap_cookie);
+               ifnet_release(sc->sc_if);
+               lck_mtx_destroy(&sc->sc_ro_mtx, stf_mtx_grp);
+               FREE(sc, M_DEVBUF);
+               return;
+       }
+
+       bpfattach(sc->sc_if, DLT_NULL, sizeof(u_int));
+
+       return;
 }
 
 static int
-stf_encapcheck(m, off, proto, arg)
-       const struct mbuf *m;
-       int off;
-       int proto;
-       void *arg;
+stf_encapcheck(
+       const struct mbuf *m,
+       __unused int off,
+       int proto,
+       void *arg)
 {
        struct ip ip;
        struct in6_ifaddr *ia6;
@@ -342,38 +404,46 @@ stf_encapcheck(m, off, proto, arg)
        struct in_addr a, b;
 
        sc = (struct stf_softc *)arg;
-       if (sc == NULL)
+       if (sc == NULL) {
                return 0;
+       }
 
-       if ((sc->sc_if.if_flags & IFF_UP) == 0)
+       if ((ifnet_flags(sc->sc_if) & IFF_UP) == 0) {
                return 0;
+       }
 
        /* IFF_LINK0 means "no decapsulation" */
-       if ((sc->sc_if.if_flags & IFF_LINK0) != 0)
+       if ((ifnet_flags(sc->sc_if) & IFF_LINK0) != 0) {
                return 0;
+       }
 
-       if (proto != IPPROTO_IPV6)
+       if (proto != IPPROTO_IPV6) {
                return 0;
+       }
 
-       /* LINTED const cast */
-       m_copydata((struct mbuf *)m, 0, sizeof(ip), (caddr_t)&ip);
+       mbuf_copydata((struct mbuf *)(size_t)m, 0, sizeof(ip), &ip);
 
-       if (ip.ip_v != 4)
+       if (ip.ip_v != 4) {
                return 0;
+       }
 
-       ia6 = stf_getsrcifa6(&sc->sc_if);
-       if (ia6 == NULL)
+       ia6 = stf_getsrcifa6(sc->sc_if);
+       if (ia6 == NULL) {
                return 0;
+       }
 
        /*
         * check if IPv4 dst matches the IPv4 address derived from the
         * local 6to4 address.
         * success on: dst = 10.1.1.1, ia6->ia_addr = 2002:0a01:0101:...
         */
+       IFA_LOCK(&ia6->ia_ifa);
        if (bcmp(GET_V4(&ia6->ia_addr.sin6_addr), &ip.ip_dst,
-           sizeof(ip.ip_dst)) != 0)
+           sizeof(ip.ip_dst)) != 0) {
+               IFA_UNLOCK(&ia6->ia_ifa);
+               IFA_REMREF(&ia6->ia_ifa);
                return 0;
-
+       }
        /*
         * check if IPv4 src matches the IPv4 address derived from the
         * local 6to4 address masked by prefixmask.
@@ -385,77 +455,101 @@ stf_encapcheck(m, off, proto, arg)
        a.s_addr &= GET_V4(&ia6->ia_prefixmask.sin6_addr)->s_addr;
        b = ip.ip_src;
        b.s_addr &= GET_V4(&ia6->ia_prefixmask.sin6_addr)->s_addr;
-       if (a.s_addr != b.s_addr)
+       if (a.s_addr != b.s_addr) {
+               IFA_UNLOCK(&ia6->ia_ifa);
+               IFA_REMREF(&ia6->ia_ifa);
                return 0;
-
+       }
        /* stf interface makes single side match only */
+       IFA_UNLOCK(&ia6->ia_ifa);
+       IFA_REMREF(&ia6->ia_ifa);
        return 32;
 }
 
 static struct in6_ifaddr *
-stf_getsrcifa6(ifp)
-       struct ifnet *ifp;
+stf_getsrcifa6(struct ifnet *ifp)
 {
        struct ifaddr *ia;
        struct in_ifaddr *ia4;
        struct sockaddr_in6 *sin6;
        struct in_addr in;
 
-       for (ia = ifp->if_addrlist.tqh_first;
-            ia;
-            ia = ia->ifa_list.tqe_next)
-       {
-               if (ia->ifa_addr == NULL)
+       ifnet_lock_shared(ifp);
+       for (ia = ifp->if_addrlist.tqh_first; ia; ia = ia->ifa_list.tqe_next) {
+               IFA_LOCK(ia);
+               if (ia->ifa_addr == NULL) {
+                       IFA_UNLOCK(ia);
                        continue;
-               if (ia->ifa_addr->sa_family != AF_INET6)
+               }
+               if (ia->ifa_addr->sa_family != AF_INET6) {
+                       IFA_UNLOCK(ia);
                        continue;
-               sin6 = (struct sockaddr_in6 *)ia->ifa_addr;
-               if (!IN6_IS_ADDR_6TO4(&sin6->sin6_addr))
+               }
+               sin6 = (struct sockaddr_in6 *)(void *)ia->ifa_addr;
+               if (!IN6_IS_ADDR_6TO4(&sin6->sin6_addr)) {
+                       IFA_UNLOCK(ia);
                        continue;
-
+               }
                bcopy(GET_V4(&sin6->sin6_addr), &in, sizeof(in));
+               IFA_UNLOCK(ia);
+               lck_rw_lock_shared(in_ifaddr_rwlock);
                for (ia4 = TAILQ_FIRST(&in_ifaddrhead);
-                    ia4;
-                    ia4 = TAILQ_NEXT(ia4, ia_link))
-               {
-                       if (ia4->ia_addr.sin_addr.s_addr == in.s_addr)
+                   ia4;
+                   ia4 = TAILQ_NEXT(ia4, ia_link)) {
+                       IFA_LOCK(&ia4->ia_ifa);
+                       if (ia4->ia_addr.sin_addr.s_addr == in.s_addr) {
+                               IFA_UNLOCK(&ia4->ia_ifa);
                                break;
+                       }
+                       IFA_UNLOCK(&ia4->ia_ifa);
                }
-               if (ia4 == NULL)
+               lck_rw_done(in_ifaddr_rwlock);
+               if (ia4 == NULL) {
                        continue;
+               }
 
+               IFA_ADDREF(ia);         /* for caller */
+               ifnet_lock_done(ifp);
                return (struct in6_ifaddr *)ia;
        }
+       ifnet_lock_done(ifp);
 
        return NULL;
 }
 
 int
-stf_pre_output(ifp, m0, dst, rt, frame_type, address, dl_tag)
-       struct ifnet *ifp;
-       register struct mbuf **m0;
-       struct sockaddr *dst;
-       caddr_t                 rt;
-       char                 *frame_type;
-       char                 *address;
-       u_long               dl_tag;
+stf_pre_output(
+       struct ifnet    *ifp,
+       __unused protocol_family_t  protocol_family,
+       struct mbuf     **m0,
+       const struct sockaddr   *dst,
+       __unused void *route,
+       __unused char *desk_linkaddr,
+       __unused char *frame_type)
 {
-       register struct mbuf *m = *m0;
+       struct mbuf *m = *m0;
        struct stf_softc *sc;
-       struct sockaddr_in6 *dst6;
-       struct in_addr *in4;
-       struct sockaddr_in *dst4;
+       const struct sockaddr_in6 *dst6;
+       const struct in_addr *in4;
        u_int8_t tos;
        struct ip *ip;
        struct ip6_hdr *ip6;
        struct in6_ifaddr *ia6;
-       int error = 0 ;
+       struct sockaddr_in      *dst4;
+       struct ip_out_args ipoa;
+       errno_t result = 0;
+
+       bzero(&ipoa, sizeof(ipoa));
+       ipoa.ipoa_boundif = IFSCOPE_NONE;
+       ipoa.ipoa_flags = IPOAF_SELECT_SRCIF;
+       ipoa.ipoa_sotc = SO_TC_UNSPEC;
+       ipoa.ipoa_netsvctype = _NET_SERVICE_TYPE_UNSPEC;
 
-       sc = (struct stf_softc*)ifp;
-       dst6 = (struct sockaddr_in6 *)dst;
+       sc = ifnet_softc(ifp);
+       dst6 = (const struct sockaddr_in6 *)(const void *)dst;
 
        /* just in case */
-       if ((ifp->if_flags & IFF_UP) == 0) {
+       if ((ifnet_flags(ifp) & IFF_UP) == 0) {
                printf("stf: IFF_DOWN\n");
                return ENETDOWN;
        }
@@ -470,10 +564,13 @@ stf_pre_output(ifp, m0, dst, rt, frame_type, address, dl_tag)
                return ENETDOWN;
        }
 
-       if (m->m_len < sizeof(*ip6)) {
+       if (mbuf_len(m) < sizeof(*ip6)) {
                m = m_pullup(m, sizeof(*ip6));
-               if (!m)
+               if (!m) {
+                       *m0 = NULL; /* makes sure this won't be double freed */
+                       IFA_REMREF(&ia6->ia_ifa);
                        return ENOBUFS;
+               }
        }
        ip6 = mtod(m, struct ip6_hdr *);
        tos = (ntohl(ip6->ip6_flow) >> 20) & 0xff;
@@ -482,82 +579,88 @@ stf_pre_output(ifp, m0, dst, rt, frame_type, address, dl_tag)
         * Pickup the right outer dst addr from the list of candidates.
         * ip6_dst has priority as it may be able to give us shorter IPv4 hops.
         */
-       if (IN6_IS_ADDR_6TO4(&ip6->ip6_dst))
+       if (IN6_IS_ADDR_6TO4(&ip6->ip6_dst)) {
                in4 = GET_V4(&ip6->ip6_dst);
-       else if (IN6_IS_ADDR_6TO4(&dst6->sin6_addr))
+       } else if (IN6_IS_ADDR_6TO4(&dst6->sin6_addr)) {
                in4 = GET_V4(&dst6->sin6_addr);
-       else {
+       } else {
+               IFA_REMREF(&ia6->ia_ifa);
                return ENETUNREACH;
        }
 
        if (ifp->if_bpf) {
-               /*
-                * We need to prepend the address family as
-                * a four byte field.  Cons up a dummy header
-                * to pacify bpf.  This is safe because bpf
-                * will only read from the mbuf (i.e., it won't
-                * try to free it or keep a pointer a to it).
-                */
-               struct mbuf m0;
+               /* We need to prepend the address family as a four byte field. */
                u_int32_t af = AF_INET6;
-               
-               m0.m_next = m;
-               m0.m_len = 4;
-               m0.m_data = (char *)&af;
-               
-               bpf_mtap(ifp, &m0);
+
+               bpf_tap_out(ifp, 0, m, &af, sizeof(af));
        }
 
-       M_PREPEND(m, sizeof(struct ip), M_DONTWAIT);
-       if (m && m->m_len < sizeof(struct ip))
+       M_PREPEND(m, sizeof(struct ip), M_DONTWAIT, 1);
+       if (m && mbuf_len(m) < sizeof(struct ip)) {
                m = m_pullup(m, sizeof(struct ip));
-       if (m == NULL)
+       }
+       if (m == NULL) {
+               *m0 = NULL;
+               IFA_REMREF(&ia6->ia_ifa);
                return ENOBUFS;
+       }
        ip = mtod(m, struct ip *);
 
        bzero(ip, sizeof(*ip));
 
+       IFA_LOCK_SPIN(&ia6->ia_ifa);
        bcopy(GET_V4(&((struct sockaddr_in6 *)&ia6->ia_addr)->sin6_addr),
            &ip->ip_src, sizeof(ip->ip_src));
+       IFA_UNLOCK(&ia6->ia_ifa);
        bcopy(in4, &ip->ip_dst, sizeof(ip->ip_dst));
        ip->ip_p = IPPROTO_IPV6;
        ip->ip_ttl = ip_stf_ttl;
-       ip->ip_len = m->m_pkthdr.len;   /*host order*/
-       if (ifp->if_flags & IFF_LINK1)
-               ip_ecn_ingress(ECN_ALLOWED, &ip->ip_tos, &tos);
-       else
+       ip->ip_len = m->m_pkthdr.len;   /*host order*/
+       if (ifp->if_flags & IFF_LINK1) {
+               ip_ecn_ingress(ECN_NORMAL, &ip->ip_tos, &tos);
+       } else {
                ip_ecn_ingress(ECN_NOCARE, &ip->ip_tos, &tos);
+       }
 
-       dst4 = (struct sockaddr_in *)&sc->sc_ro.ro_dst;
-       if (dst4->sin_family != AF_INET ||
+       lck_mtx_lock(&sc->sc_ro_mtx);
+       dst4 = (struct sockaddr_in *)(void *)&sc->sc_ro.ro_dst;
+       if (ROUTE_UNUSABLE(&sc->sc_ro) || dst4->sin_family != AF_INET ||
            bcmp(&dst4->sin_addr, &ip->ip_dst, sizeof(ip->ip_dst)) != 0) {
-               /* cache route doesn't match */
+               ROUTE_RELEASE(&sc->sc_ro);
+               /* cache route doesn't match: always the case during the first use */
                dst4->sin_family = AF_INET;
                dst4->sin_len = sizeof(struct sockaddr_in);
                bcopy(&ip->ip_dst, &dst4->sin_addr, sizeof(dst4->sin_addr));
-               if (sc->sc_ro.ro_rt) {
-                       RTFREE(sc->sc_ro.ro_rt);
-                       sc->sc_ro.ro_rt = NULL;
-               }
        }
 
-       if (sc->sc_ro.ro_rt == NULL) {
-               rtalloc(&sc->sc_ro);
-               if (sc->sc_ro.ro_rt == NULL) {
-                       return ENETUNREACH;
-               }
-       }
+       result = ip_output(m, NULL, &sc->sc_ro, IP_OUTARGS, NULL, &ipoa);
+       lck_mtx_unlock(&sc->sc_ro_mtx);
 
-       error = ip_output(m, NULL, &sc->sc_ro, 0, NULL);
-       if (error == 0)
-               return EJUSTRETURN;
+       /* Assumption: ip_output will free mbuf on errors */
+       /* All the output processing is done here, don't let stf_output be called */
+       if (result == 0) {
+               result = EJUSTRETURN;
+       }
+       *m0 = NULL;
+       IFA_REMREF(&ia6->ia_ifa);
+       return result;
+}
+static errno_t
+stf_output(
+       __unused ifnet_t        ifp,
+       __unused mbuf_t m)
+{
+       /* All processing is done in stf_pre_output
+        * this shouldn't be called as the pre_output returns "EJUSTRETURN"
+        */
+       return 0;
 }
 
 static int
-stf_checkaddr4(sc, in, inifp)
-       struct stf_softc *sc;
-       struct in_addr *in;
-       struct ifnet *inifp;    /* incoming interface */
+stf_checkaddr4(
+       struct stf_softc *sc,
+       const struct in_addr *in,
+       struct ifnet *inifp)    /* incoming interface */
 {
        struct in_ifaddr *ia4;
 
@@ -565,8 +668,9 @@ stf_checkaddr4(sc, in, inifp)
         * reject packets with the following address:
         * 224.0.0.0/4 0.0.0.0/8 127.0.0.0/8 255.0.0.0/8
         */
-       if (IN_MULTICAST(ntohl(in->s_addr)))
+       if (IN_MULTICAST(ntohl(in->s_addr))) {
                return -1;
+       }
        switch ((ntohl(in->s_addr) & 0xff000000) >> 24) {
        case 0: case 127: case 255:
                return -1;
@@ -575,20 +679,28 @@ stf_checkaddr4(sc, in, inifp)
        /*
         * reject packets with broadcast
         */
+       lck_rw_lock_shared(in_ifaddr_rwlock);
        for (ia4 = TAILQ_FIRST(&in_ifaddrhead);
-            ia4;
-            ia4 = TAILQ_NEXT(ia4, ia_link))
-       {
-               if ((ia4->ia_ifa.ifa_ifp->if_flags & IFF_BROADCAST) == 0)
+           ia4;
+           ia4 = TAILQ_NEXT(ia4, ia_link)) {
+               IFA_LOCK(&ia4->ia_ifa);
+               if ((ia4->ia_ifa.ifa_ifp->if_flags & IFF_BROADCAST) == 0) {
+                       IFA_UNLOCK(&ia4->ia_ifa);
                        continue;
-               if (in->s_addr == ia4->ia_broadaddr.sin_addr.s_addr)
+               }
+               if (in->s_addr == ia4->ia_broadaddr.sin_addr.s_addr) {
+                       IFA_UNLOCK(&ia4->ia_ifa);
+                       lck_rw_done(in_ifaddr_rwlock);
                        return -1;
+               }
+               IFA_UNLOCK(&ia4->ia_ifa);
        }
+       lck_rw_done(in_ifaddr_rwlock);
 
        /*
         * perform ingress filter
         */
-       if (sc && (sc->sc_if.if_flags & IFF_LINK2) == 0 && inifp) {
+       if (sc && (ifnet_flags(sc->sc_if) & IFF_LINK2) == 0 && inifp) {
                struct sockaddr_in sin;
                struct rtentry *rt;
 
@@ -596,17 +708,23 @@ stf_checkaddr4(sc, in, inifp)
                sin.sin_family = AF_INET;
                sin.sin_len = sizeof(struct sockaddr_in);
                sin.sin_addr = *in;
-               rt = rtalloc1((struct sockaddr *)&sin, 0, 0UL);
-               if (!rt || rt->rt_ifp != inifp) {
+               rt = rtalloc1((struct sockaddr *)&sin, 0, 0);
+               if (rt != NULL) {
+                       RT_LOCK(rt);
+               }
+               if (rt == NULL || rt->rt_ifp != inifp) {
 #if 1
                        log(LOG_WARNING, "%s: packet from 0x%x dropped "
-                           "due to ingress filter\n", if_name(&sc->sc_if),
+                           "due to ingress filter\n", if_name(sc->sc_if),
                            (u_int32_t)ntohl(sin.sin_addr.s_addr));
 #endif
-                       if (rt)
+                       if (rt != NULL) {
+                               RT_UNLOCK(rt);
                                rtfree(rt);
+                       }
                        return -1;
                }
+               RT_UNLOCK(rt);
                rtfree(rt);
        }
 
@@ -614,16 +732,17 @@ stf_checkaddr4(sc, in, inifp)
 }
 
 static int
-stf_checkaddr6(sc, in6, inifp)
-       struct stf_softc *sc;
-       struct in6_addr *in6;
-       struct ifnet *inifp;    /* incoming interface */
+stf_checkaddr6(
+       struct stf_softc *sc,
+       struct in6_addr *in6,
+       struct ifnet *inifp)    /* incoming interface */
 {
        /*
         * check 6to4 addresses
         */
-       if (IN6_IS_ADDR_6TO4(in6))
+       if (IN6_IS_ADDR_6TO4(in6)) {
                return stf_checkaddr4(sc, GET_V4(in6), inifp);
+       }
 
        /*
         * reject anything that look suspicious.  the test is implemented
@@ -631,29 +750,29 @@ stf_checkaddr6(sc, in6, inifp)
         * (1) reject bad packets earlier, and
         * (2) to be safe against future ip6_input change.
         */
-       if (IN6_IS_ADDR_V4COMPAT(in6) || IN6_IS_ADDR_V4MAPPED(in6))
+       if (IN6_IS_ADDR_V4COMPAT(in6) || IN6_IS_ADDR_V4MAPPED(in6)) {
                return -1;
+       }
 
        return 0;
 }
 
-void
-in_stf_input(m, off)
-       struct mbuf *m;
-       int off;
+static void
+in_stf_input(
+       struct mbuf *m,
+       int off)
 {
        struct stf_softc *sc;
        struct ip *ip;
-       struct ip6_hdr *ip6;
+       struct ip6_hdr ip6;
        u_int8_t otos, itos;
-       int s, isr, proto;
-       struct ifqueue *ifq = NULL;
+       int proto;
        struct ifnet *ifp;
+       struct ifnet_stat_increment_param       stats;
 
        ip = mtod(m, struct ip *);
        proto = ip->ip_p;
 
-
        if (proto != IPPROTO_IPV6) {
                m_freem(m);
                return;
@@ -663,12 +782,16 @@ in_stf_input(m, off)
 
        sc = (struct stf_softc *)encap_getarg(m);
 
-       if (sc == NULL || (sc->sc_if.if_flags & IFF_UP) == 0) {
+       if (sc == NULL || (ifnet_flags(sc->sc_if) & IFF_UP) == 0) {
                m_freem(m);
                return;
        }
 
-       ifp = &sc->sc_if;
+       ifp = sc->sc_if;
+
+#if MAC_LABEL
+       mac_mbuf_label_associate_ifnet(ifp, m);
+#endif
 
        /*
         * perform sanity check against outer src/dst.
@@ -681,55 +804,35 @@ in_stf_input(m, off)
        }
 
        otos = ip->ip_tos;
-       m_adj(m, off);
-
-       if (m->m_len < sizeof(*ip6)) {
-               m = m_pullup(m, sizeof(*ip6));
-               if (!m)
-                       return;
-       }
-       ip6 = mtod(m, struct ip6_hdr *);
+       mbuf_copydata(m, off, sizeof(ip6), &ip6);
 
        /*
         * perform sanity check against inner src/dst.
         * for source, perform ingress filter as well.
         */
-       if (stf_checkaddr6(sc, &ip6->ip6_dst, NULL) < 0 ||
-           stf_checkaddr6(sc, &ip6->ip6_src, m->m_pkthdr.rcvif) < 0) {
+       if (stf_checkaddr6(sc, &ip6.ip6_dst, NULL) < 0 ||
+           stf_checkaddr6(sc, &ip6.ip6_src, m->m_pkthdr.rcvif) < 0) {
                m_freem(m);
                return;
        }
 
-       itos = (ntohl(ip6->ip6_flow) >> 20) & 0xff;
-       if ((ifp->if_flags & IFF_LINK1) != 0)
-               ip_ecn_egress(ECN_ALLOWED, &otos, &itos);
-       else
+       itos = (ntohl(ip6.ip6_flow) >> 20) & 0xff;
+       if ((ifnet_flags(ifp) & IFF_LINK1) != 0) {
+               ip_ecn_egress(ECN_NORMAL, &otos, &itos);
+       } else {
                ip_ecn_egress(ECN_NOCARE, &otos, &itos);
-       ip6->ip6_flow &= ~htonl(0xff << 20);
-       ip6->ip6_flow |= htonl((u_int32_t)itos << 20);
+       }
+       ip6.ip6_flow &= ~htonl(0xff << 20);
+       ip6.ip6_flow |= htonl((u_int32_t)itos << 20);
 
        m->m_pkthdr.rcvif = ifp;
-       
+       mbuf_pkthdr_setheader(m, mbuf_data(m));
+       mbuf_adj(m, off);
+
        if (ifp->if_bpf) {
-               /*
-                * We need to prepend the address family as
-                * a four byte field.  Cons up a dummy header
-                * to pacify bpf.  This is safe because bpf
-                * will only read from the mbuf (i.e., it won't
-                * try to free it or keep a pointer a to it).
-                */
-               struct mbuf m0;
+               /* We need to prepend the address family as a four byte field. */
                u_int32_t af = AF_INET6;
-               
-               m0.m_next = m;
-               m0.m_len = 4;
-               m0.m_data = (char *)&af;
-               
-#ifdef HAVE_OLD_BPF
-               bpf_mtap(ifp, &m0);
-#else
-               bpf_mtap(ifp->if_bpf, &m0);
-#endif
+               bpf_tap_in(ifp, 0, m, &af, sizeof(af));
        }
 
        /*
@@ -738,40 +841,32 @@ in_stf_input(m, off)
         * See net/if_gif.c for possible issues with packet processing
         * reorder due to extra queueing.
         */
-       ifq = &ip6intrq;
-       isr = NETISR_IPV6;
+       bzero(&stats, sizeof(stats));
+       stats.packets_in = 1;
+       stats.bytes_in = mbuf_pkthdr_len(m);
+       mbuf_pkthdr_setrcvif(m, ifp);
+       ifnet_input(ifp, m, &stats);
 
-       s = splimp();
-       if (IF_QFULL(ifq)) {
-               IF_DROP(ifq);   /* update statistics */
-               m_freem(m);
-               splx(s);
-               return;
-       }
-       IF_ENQUEUE(ifq, m);
-       schednetisr(isr);
-       ifp->if_ipackets++;
-       ifp->if_ibytes += m->m_pkthdr.len;
-       splx(s);
+       return;
 }
 
-/* ARGSUSED */
 static void
-stf_rtrequest(cmd, rt, sa)
-       int cmd;
-       struct rtentry *rt;
-       struct sockaddr *sa;
+stf_rtrequest(
+       __unused int cmd,
+       struct rtentry *rt,
+       __unused struct sockaddr *sa)
 {
-
-       if (rt)
+       if (rt != NULL) {
+               RT_LOCK_ASSERT_HELD(rt);
                rt->rt_rmx.rmx_mtu = IPV6_MMTU;
+       }
 }
 
-int
-stf_ioctl(ifp, cmd, data)
-       struct ifnet *ifp;
-       u_long cmd;
-       void *data;
+static errno_t
+stf_ioctl(
+       ifnet_t         ifp,
+       u_long          cmd,
+       void            *data)
 {
        struct ifaddr *ifa;
        struct ifreq *ifr;
@@ -782,29 +877,45 @@ stf_ioctl(ifp, cmd, data)
        switch (cmd) {
        case SIOCSIFADDR:
                ifa = (struct ifaddr *)data;
-               if (ifa == NULL || ifa->ifa_addr->sa_family != AF_INET6) {
+               if (ifa == NULL) {
+                       error = EAFNOSUPPORT;
+                       break;
+               }
+               IFA_LOCK(ifa);
+               if (ifa->ifa_addr->sa_family != AF_INET6) {
+                       IFA_UNLOCK(ifa);
                        error = EAFNOSUPPORT;
                        break;
                }
-               sin6 = (struct sockaddr_in6 *)ifa->ifa_addr;
+               sin6 = (struct sockaddr_in6 *)(void *)ifa->ifa_addr;
                if (IN6_IS_ADDR_6TO4(&sin6->sin6_addr)) {
-                       ifa->ifa_rtrequest = stf_rtrequest;
-                       ifp->if_flags |= IFF_UP;
-               } else
+                       if (!(ifnet_flags( ifp ) & IFF_UP)) {
+                               /* do this only if the interface is not already up */
+                               ifa->ifa_rtrequest = stf_rtrequest;
+                               IFA_UNLOCK(ifa);
+                               ifnet_set_flags(ifp, IFF_UP, IFF_UP);
+                       } else {
+                               IFA_UNLOCK(ifa);
+                       }
+               } else {
+                       IFA_UNLOCK(ifa);
                        error = EINVAL;
+               }
+               IFA_LOCK_ASSERT_NOTHELD(ifa);
                break;
 
        case SIOCADDMULTI:
        case SIOCDELMULTI:
                ifr = (struct ifreq *)data;
-               if (ifr && ifr->ifr_addr.sa_family == AF_INET6)
+               if (ifr && ifr->ifr_addr.sa_family == AF_INET6) {
                        ;
-               else
+               } else {
                        error = EAFNOSUPPORT;
+               }
                break;
 
        default:
-               error = EINVAL;
+               error = EOPNOTSUPP;
                break;
        }