]> git.saurik.com Git - apple/xnu.git/blobdiff - bsd/net/if_loop.c
xnu-3789.41.3.tar.gz
[apple/xnu.git] / bsd / net / if_loop.c
index 6cfcb8a891964de37a5e9a19a725b407bcffbf09..3baa27434e19ed326f187519eb7e0605ba81aab6 100644 (file)
@@ -1,8 +1,8 @@
 /*
- * Copyright (c) 2000 Apple Computer, Inc. All rights reserved.
+ * Copyright (c) 2000-2013 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
  * 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,
@@ -22,7 +22,7 @@
  * 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@
  */
 /*
  *     @(#)if_loop.c   8.1 (Berkeley) 6/10/93
  * $FreeBSD: src/sys/net/if_loop.c,v 1.47.2.5 2001/07/03 11:01:41 ume Exp $
  */
+/*
+ * 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.
+ */
 
 /*
  * Loopback interface driver for protocol testing and timing.
 #include "loop.h"
 #if NLOOP > 0
 
+#if NLOOP != 1
+#error "More than one loopback interface is not supported."
+#endif
+
 #include <sys/param.h>
 #include <sys/systm.h>
 #include <sys/kernel.h>
 #include <sys/mbuf.h>
 #include <sys/socket.h>
 #include <sys/sockio.h>
+#include <sys/mcache.h>
+#include <sys/sysctl.h>
 
 #include <net/if.h>
 #include <net/if_types.h>
 #include <net/bpf.h>
 #include <sys/malloc.h>
 
-#if    INET
+#if INET
 #include <netinet/in.h>
 #include <netinet/in_var.h>
 #endif
 
-#if IPX
-#include <netipx/ipx.h>
-#include <netipx/ipx_if.h>
-#endif
-
 #if INET6
-#ifndef INET
+#if !INET
 #include <netinet/in.h>
 #endif
 #include <netinet6/in6_var.h>
 #include <net/dlil.h>
 #include <net/kpi_protocol.h>
 
-#if NETAT
-extern struct ifqueue atalkintrq;
+#if CONFIG_MACF_NET
+#include <security/mac_framework.h>
 #endif
 
-#include "bpfilter.h"
-#if NBPFILTER > 0
-#include <net/bpfdesc.h>
-#endif
+#include <pexpert/pexpert.h>
+
+#define        LOMTU           16384
+#define        LOSNDQ_MAXLEN   256
 
-#define NLOOP_ATTACHMENTS (NLOOP * 12)
+#define        LO_BPF_TAP_OUT(_m) {                                            \
+       if (lo_statics[0].bpf_callback != NULL) {                       \
+               bpf_tap_out(lo_ifp, DLT_NULL, _m,                       \
+                   &((struct loopback_header *)_m->m_pkthdr.pkt_hdr)-> \
+                   protocol, sizeof (u_int32_t));                      \
+       }                                                               \
+}
+
+#define        LO_BPF_TAP_OUT_MULTI(_m) {                                      \
+       if (lo_statics[0].bpf_callback != NULL) {                       \
+               struct mbuf *_n;                                        \
+               for (_n = _m; _n != NULL; _n = _n->m_nextpkt)           \
+                       LO_BPF_TAP_OUT(_n);                             \
+       }                                                               \
+}
 
 struct lo_statics_str {
-       int                             bpf_mode;
+       int             bpf_mode;
        bpf_packet_func bpf_callback;
 };
 
-void loopattach(void *dummy);
-
 static struct lo_statics_str lo_statics[NLOOP];
-int loopattach_done = 0; /* used to sync ip6_init2 loopback configuration */
-
-#ifdef TINY_LOMTU
-#define        LOMTU   (1024+512)
-#else
-#define LOMTU  16384
-#endif
+static int lo_txstart = 0;
 
-struct ifnet loif[NLOOP];
-struct ifnet *lo_ifp = &loif[0];
+struct ifnet *lo_ifp = NULL;
 
 struct loopback_header {
-       u_long          protocol;
+       protocol_family_t       protocol;
 };
 
-void lo_reg_if_mods(void);
-
 /* Local forward declerations */
+void loopattach(void);
+static errno_t lo_demux(struct ifnet *, struct mbuf *, char *,
+    protocol_family_t *);
+static errno_t
+lo_framer(struct ifnet *, struct mbuf **, const struct sockaddr *,
+    const char *, const char *, u_int32_t *, u_int32_t *);
+static errno_t lo_add_proto(struct ifnet *, protocol_family_t,
+    const struct ifnet_demux_desc *, u_int32_t);
+static errno_t lo_del_proto(struct ifnet *, protocol_family_t);
+static int lo_output(struct ifnet *, struct mbuf *);
+static errno_t lo_pre_enqueue(struct ifnet *, struct mbuf *);
+static void lo_start(struct ifnet *);
+static errno_t lo_pre_output(struct ifnet *, protocol_family_t, struct mbuf **,
+    const struct sockaddr *, void *, char *, char *);
+static errno_t lo_input(struct ifnet *, protocol_family_t, struct mbuf *);
+static void lo_rtrequest(int, struct rtentry *, struct sockaddr *);
+static errno_t lo_ioctl(struct ifnet *, u_long, void *);
+static errno_t lo_attach_proto(struct ifnet *, protocol_family_t);
+static void lo_reg_if_mods(void);
+static errno_t lo_set_bpf_tap(struct ifnet *, bpf_tap_mode, bpf_packet_func);
+static int sysctl_dequeue_max SYSCTL_HANDLER_ARGS;
+static int sysctl_sched_model SYSCTL_HANDLER_ARGS;
+static int sysctl_dequeue_scidx SYSCTL_HANDLER_ARGS;
+
+SYSCTL_DECL(_net_link);
+
+SYSCTL_NODE(_net_link, OID_AUTO, loopback, CTLFLAG_RW | CTLFLAG_LOCKED, 0,
+    "loopback interface");
+
+#define        LO_BW_SLEEP     10
+static u_int32_t lo_bw_sleep_usec = LO_BW_SLEEP;
+SYSCTL_UINT(_net_link_loopback, OID_AUTO, bw_sleep_usec,
+    CTLFLAG_RW | CTLFLAG_LOCKED, &lo_bw_sleep_usec, LO_BW_SLEEP, "");
+
+static u_int32_t lo_bw_measure = 0;
+SYSCTL_UINT(_net_link_loopback, OID_AUTO, bw_measure,
+    CTLFLAG_RW | CTLFLAG_LOCKED, &lo_bw_measure, 0, "");
+
+static u_int32_t lo_dequeue_max = LOSNDQ_MAXLEN;
+SYSCTL_PROC(_net_link_loopback, OID_AUTO, max_dequeue,
+    CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_LOCKED, &lo_dequeue_max, LOSNDQ_MAXLEN,
+    sysctl_dequeue_max, "I", "Maximum number of packets dequeued at a time");
+
+static u_int32_t lo_sched_model = IFNET_SCHED_MODEL_NORMAL;
+SYSCTL_PROC(_net_link_loopback, OID_AUTO, sched_model,
+    CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_LOCKED, &lo_sched_model,
+    IFNET_SCHED_MODEL_NORMAL, sysctl_sched_model, "I", "Scheduling model");
+
+static u_int32_t lo_dequeue_sc = MBUF_SC_BE;
+static int lo_dequeue_scidx = MBUF_SCIDX(MBUF_SC_BE);
+SYSCTL_PROC(_net_link_loopback, OID_AUTO, dequeue_sc,
+    CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_LOCKED, &lo_dequeue_scidx,
+    MBUF_SC_BE, sysctl_dequeue_scidx, "I", "Dequeue a specific SC index");
 
 static errno_t
-lo_demux(
-    __unused ifnet_t   ifp,
-    __unused mbuf_t            m,
-    char                               *frame_header,
-    protocol_family_t  *protocol_family)
+lo_demux(struct ifnet *ifp, struct mbuf *m, char *frame_header,
+    protocol_family_t *protocol_family)
 {
-       struct loopback_header *header = (struct loopback_header *)frame_header;
-       
+#pragma unused(ifp, m)
+       struct loopback_header *header =
+           (struct loopback_header *)(void *)frame_header;
+
        *protocol_family = header->protocol;
-       
-       return 0;
-}
 
+       return (0);
+}
 
 static errno_t
-lo_framer(
-    __unused ifnet_t                           ifp,
-    mbuf_t                                                     *m,
-    __unused const struct sockaddr     *dest,
-    __unused const char                *dest_linkaddr,
-    const char                                         *frame_type)
+lo_framer(struct ifnet *ifp, struct mbuf **m, const struct sockaddr *dest,
+    const char *dest_linkaddr, const char *frame_type,
+    u_int32_t *prepend_len, u_int32_t *postpend_len)
 {
+#pragma unused(ifp, dest, dest_linkaddr)
        struct loopback_header  *header;
 
-       M_PREPEND(*m, sizeof(struct loopback_header), M_WAITOK);
-       header = mtod(*m, struct loopback_header*);
-       header->protocol = *(const u_long*)frame_type;
-       return 0;
+       M_PREPEND(*m, sizeof (struct loopback_header), M_WAITOK, 1);
+       if (*m == NULL) {
+               /* Tell caller not to try to free passed-in mbuf */
+               return (EJUSTRETURN);
+       }
+
+       if (prepend_len != NULL)
+               *prepend_len = sizeof (struct loopback_header);
+       if (postpend_len != NULL)
+               *postpend_len = 0;
+
+       header = mtod(*m, struct loopback_header *);
+       bcopy(frame_type, &header->protocol, sizeof (u_int32_t));
+       return (0);
 }
 
 static errno_t
-lo_add_proto(
-       __unused struct ifnet                   *ifp,
-       __unused u_long                                 protocol_family,
-       __unused struct ddesc_head_str  *demux_desc_head)
+lo_add_proto(struct ifnet *interface, protocol_family_t protocol_family,
+    const struct ifnet_demux_desc *demux_array, u_int32_t demux_count)
 {
-    return 0;
+#pragma unused(interface, protocol_family, demux_array, demux_count)
+       return (0);
 }
 
-
 static errno_t
-lo_del_proto(
-       __unused ifnet_t                        ifp,
-       __unused protocol_family_t      protocol)
+lo_del_proto(struct ifnet *ifp, protocol_family_t protocol)
 {
-       return 0;
+#pragma unused(ifp, protocol)
+       return (0);
 }
 
-static int
-lo_output(
-       struct ifnet *ifp,
-       struct mbuf *m)
+static void
+lo_tx_compl(struct ifnet *ifp, struct mbuf *m)
 {
+       errno_t error;
 
-       if ((m->m_flags & M_PKTHDR) == 0)
-               panic("lo_output: no HDR");
-
-       /*
-        * Don't overwrite the rcvif field if it is in use.
-        *  This is used to match multicast packets, sent looping
-        *  back, with the appropriate group record on input.
-        */
-       if (m->m_pkthdr.rcvif == NULL)
-               m->m_pkthdr.rcvif = ifp;
-
-       ifp->if_ibytes += m->m_pkthdr.len;
-       ifp->if_obytes += m->m_pkthdr.len;
-
-       ifp->if_opackets++;
-       ifp->if_ipackets++;
-
-       m->m_pkthdr.header = mtod(m, char *);
-       m->m_pkthdr.csum_data = 0xffff; /* loopback checksums are always OK */
-       m->m_pkthdr.csum_flags = CSUM_DATA_VALID | CSUM_PSEUDO_HDR | 
-                                                        CSUM_IP_CHECKED | CSUM_IP_VALID;
-       m_adj(m, sizeof(struct loopback_header));
-
-#if NBPFILTER > 0
-       if (lo_statics[ifp->if_unit].bpf_mode != BPF_TAP_DISABLE) {
-               struct mbuf m0, *n;
-
-               n = m;
-               if (ifp->if_bpf->bif_dlt == DLT_NULL) {
-                       struct loopback_header  *header;
-                       /*
-                        * 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).
-                        */
-                       header = (struct loopback_header*)m->m_pkthdr.header;
-                       m0.m_next = m;
-                       m0.m_len = 4;
-                       m0.m_data = (char *)&header->protocol;
-                       n = &m0;
-               }
+       if ((ifp->if_xflags & IFXF_TIMESTAMP_ENABLED) != 0) {
+               boolean_t requested;
+
+               error = mbuf_get_timestamp_requested(m, &requested);
+               if (requested) {
+                       struct timespec now;
+                       u_int64_t ts;
+
+                       nanouptime(&now);
+                       net_timernsec(&now, &ts);
 
-               lo_statics[ifp->if_unit].bpf_callback(ifp, n);
+                       error = mbuf_set_timestamp(m, ts, TRUE);
+                       if (error != 0)
+                               printf("%s: mbuf_set_timestamp() failed %d\n",
+                                       __func__, error);
+               }
        }
-#endif
+       error = mbuf_set_status(m, KERN_SUCCESS);
+       if (error != 0)
+               printf("%s: mbuf_set_status() failed %d\n",
+                       __func__, error);
 
-       return dlil_input(ifp, m, m);
+       ifnet_tx_compl(ifp, m);
 }
 
+/*
+ * Output callback.
+ *
+ * This routine is called only when lo_txstart is disabled.
+ */
+static int
+lo_output(struct ifnet *ifp, struct mbuf *m_list)
+{
+       struct mbuf *m, *m_tail = NULL;
+       struct ifnet_stat_increment_param s;
+       u_int32_t cnt = 0, len = 0;
+
+       bzero(&s, sizeof(s));
+
+       for (m = m_list; m; m = m->m_nextpkt) {
+               VERIFY(m->m_flags & M_PKTHDR);
+               cnt++;
+               len += m->m_pkthdr.len;
+
+               /*
+                * Don't overwrite the rcvif field if it is in use.
+                *  This is used to match multicast packets, sent looping
+                *  back, with the appropriate group record on input.
+                */
+               if (m->m_pkthdr.rcvif == NULL)
+                       m->m_pkthdr.rcvif = ifp;
+
+               m->m_pkthdr.pkt_flags |= PKTF_LOOP;
+               m->m_pkthdr.pkt_hdr = mtod(m, char *);
+
+               /* loopback checksums are always OK */
+               m->m_pkthdr.csum_data = 0xffff;
+               m->m_pkthdr.csum_flags =
+                   CSUM_DATA_VALID | CSUM_PSEUDO_HDR |
+                   CSUM_IP_CHECKED | CSUM_IP_VALID;
+
+               m_adj(m, sizeof (struct loopback_header));
+
+               LO_BPF_TAP_OUT(m);
+               if (m->m_nextpkt == NULL) {
+                       m_tail = m;
+               }
+               lo_tx_compl(ifp, m);
+       }
+
+       s.packets_in = cnt;
+       s.packets_out = cnt;
+       s.bytes_in = len;
+       s.bytes_out = len;
+
+       return (ifnet_input_extended(ifp, m_list, m_tail, &s));
+}
 
 /*
- * This is a common pre-output route used by INET and INET6. This could
- * (should?) be split into separate pre-output routines for each protocol.
+ * Pre-enqueue callback.
+ *
+ * This routine is called only when lo_txstart is enabled.
  */
+static errno_t
+lo_pre_enqueue(struct ifnet *ifp, struct mbuf *m0)
+{
+       struct mbuf *m = m0, *n;
+       int error = 0;
 
-static int
-lo_pre_output(
-       __unused struct ifnet   *ifp,
-       u_long                  protocol_family,
-       struct mbuf             **m,
-       __unused const struct sockaddr  *dst,
-       caddr_t                 route,
-       char                    *frame_type,
-       __unused char   *dst_addr)
+       while (m != NULL) {
+               VERIFY(m->m_flags & M_PKTHDR);
+
+               n = m->m_nextpkt;
+               m->m_nextpkt = NULL;
+
+               /*
+                * Don't overwrite the rcvif field if it is in use.
+                *  This is used to match multicast packets, sent looping
+                *  back, with the appropriate group record on input.
+                */
+               if (m->m_pkthdr.rcvif == NULL)
+                       m->m_pkthdr.rcvif = ifp;
+
+               m->m_pkthdr.pkt_flags |= PKTF_LOOP;
+               m->m_pkthdr.pkt_hdr = mtod(m, char *);
+
+               /* loopback checksums are always OK */
+               m->m_pkthdr.csum_data = 0xffff;
+               m->m_pkthdr.csum_flags =
+                   CSUM_DATA_VALID | CSUM_PSEUDO_HDR |
+                   CSUM_IP_CHECKED | CSUM_IP_VALID;
+
+               m_adj(m, sizeof (struct loopback_header));
+
+               /*
+                * Let the callee free it in case of error,
+                * and perform any necessary accounting.
+                */
+               (void) ifnet_enqueue(ifp, m);
+
+               m = n;
+       }
+
+       return (error);
+}
 
+/*
+ * Start output callback.
+ *
+ * This routine is invoked by the start worker thread; because we never call
+ * it directly, there is no need do deploy any serialization mechanism other
+ * than what's already used by the worker thread, i.e. this is already single
+ * threaded.
+ *
+ * This routine is called only when lo_txstart is enabled.
+ */
+static void
+lo_start(struct ifnet *ifp)
 {
-       register struct rtentry *rt = (struct rtentry *) route;
+       struct ifnet_stat_increment_param s;
+
+       bzero(&s, sizeof (s));
+
+       for (;;) {
+               struct mbuf *m = NULL, *m_tail = NULL;
+               u_int32_t cnt, len = 0;
+               int sleep_chan = 0;
+               struct timespec ts;
+
+               if (lo_sched_model == IFNET_SCHED_MODEL_NORMAL) {
+                       if (ifnet_dequeue_multi(ifp, lo_dequeue_max, &m,
+                           &m_tail, &cnt, &len) != 0)
+                               break;
+               } else {
+                       if (ifnet_dequeue_service_class_multi(ifp,
+                           lo_dequeue_sc, lo_dequeue_max, &m,
+                           &m_tail, &cnt, &len) != 0)
+                               break;
+               }
+
+               LO_BPF_TAP_OUT_MULTI(m);
+
+               if (lo_bw_measure) {
+                       if (cnt >= if_bw_measure_size)
+                               ifnet_transmit_burst_start(ifp, m);
+                       if (lo_bw_sleep_usec > 0) {
+                               bzero(&ts, sizeof(ts));
+                               ts.tv_nsec = (lo_bw_sleep_usec << 10) * cnt;
+
+                               /* Add msleep with timeout */
+                               (void) msleep(&sleep_chan, NULL,
+                                   PSOCK, "lo_start", &ts);
+                       }
+                       if (cnt >= if_bw_measure_size)
+                               ifnet_transmit_burst_end(ifp, m_tail);
+               }
+               lo_tx_compl(ifp, m);
 
-       if (((*m)->m_flags & M_PKTHDR) == 0)
-               panic("looutput no HDR");
+               /* stats are required for extended variant */
+               s.packets_in = cnt;
+               s.packets_out = cnt;
+               s.bytes_in = len;
+               s.bytes_out = len;
 
-       if (rt && rt->rt_flags & (RTF_REJECT|RTF_BLACKHOLE)) {
-               if (rt->rt_flags & RTF_BLACKHOLE) {
-                       m_freem(*m);
-                       return EJUSTRETURN;
+               (void) ifnet_input_extended(ifp, m, m_tail, &s);
+       }
+}
+
+/*
+ * This is a common pre-output route used by INET and INET6. This could
+ * (should?) be split into separate pre-output routines for each protocol.
+ */
+static errno_t
+lo_pre_output(struct ifnet *ifp, protocol_family_t protocol_family,
+    struct mbuf **m, const struct sockaddr *dst, void *route, char *frame_type,
+    char *dst_addr)
+{
+#pragma unused(ifp, dst, dst_addr)
+       struct rtentry *rt = route;
+
+       VERIFY((*m)->m_flags & M_PKTHDR);
+
+       (*m)->m_flags |= M_LOOP;
+
+       if (rt != NULL) {
+               u_int32_t rt_flags = rt->rt_flags;
+               if (rt_flags & (RTF_REJECT | RTF_BLACKHOLE)) {
+                       if (rt_flags & RTF_BLACKHOLE) {
+                               m_freem(*m);
+                               return (EJUSTRETURN);
+                       } else {
+                               return ((rt_flags & RTF_HOST) ?
+                                   EHOSTUNREACH : ENETUNREACH);
+                       }
                }
-               else
-                       return ((rt->rt_flags & RTF_HOST) ? EHOSTUNREACH : ENETUNREACH);
        }
-       
-       *(u_long *)frame_type = protocol_family;
 
-       return 0;
+       bcopy(&protocol_family, frame_type, sizeof (protocol_family));
+
+       return (0);
 }
 
 /*
  *  lo_input - This should work for all attached protocols that use the
  *             ifq/schednetisr input mechanism.
  */
-static int
-lo_input(
-       struct mbuf                             *m,
-       __unused char                   *fh,
-       __unused struct ifnet   *ifp,
-       __unused u_long                 protocol_family,
-       __unused int                    sync_ok)
+static errno_t
+lo_input(struct ifnet *ifp, protocol_family_t protocol_family, struct mbuf *m)
 {
+#pragma unused(ifp, protocol_family)
+
+       if ((ifp->if_xflags & IFXF_TIMESTAMP_ENABLED) != 0) {
+               errno_t error;
+               struct timespec now;
+               u_int64_t ts;
+
+               nanouptime(&now);
+               net_timernsec(&now, &ts);
+
+               error = mbuf_set_timestamp(m, ts, TRUE);
+               if (error != 0)
+                       printf("%s: mbuf_set_timestamp() failed %d\n",
+                               __func__, error);
+       }
+
        if (proto_input(protocol_family, m) != 0)
                m_freem(m);
        return (0);
 }
 
-
-
-
 /* ARGSUSED */
 static void
-lortrequest(
-       __unused int cmd,
-       struct rtentry *rt,
-       __unused struct sockaddr *sa)
+lo_rtrequest(int cmd, struct rtentry *rt, struct sockaddr *sa)
 {
-       if (rt) {
+#pragma unused(cmd, sa)
+       if (rt != NULL) {
+               RT_LOCK_ASSERT_HELD(rt);
                rt->rt_rmx.rmx_mtu = rt->rt_ifp->if_mtu; /* for ISO */
                /*
                 * For optimal performance, the send and receive buffers
                 * should be at least twice the MTU plus a little more for
                 * overhead.
                 */
-               rt->rt_rmx.rmx_recvpipe = 
-                       rt->rt_rmx.rmx_sendpipe = 3 * LOMTU;
+               rt->rt_rmx.rmx_recvpipe = rt->rt_rmx.rmx_sendpipe = 3 * LOMTU;
        }
 }
 
@@ -324,29 +530,30 @@ lortrequest(
  * Process an ioctl request.
  */
 static errno_t
-loioctl(
-       ifnet_t         ifp,
-       u_int32_t       cmd,
-       void*           data)
+lo_ioctl(struct ifnet *ifp, u_long cmd, void *data)
 {
-       register struct ifaddr *ifa;
-       register struct ifreq *ifr = (struct ifreq *)data;
-       register int error = 0;
+       int error = 0;
 
        switch (cmd) {
 
-       case SIOCSIFADDR:
-               ifnet_set_flags(ifp, IFF_UP | IFF_RUNNING, IFF_UP | IFF_RUNNING);
-               ifa = (struct ifaddr *)data;
-               ifa->ifa_rtrequest = lortrequest;
+       case SIOCSIFADDR: {             /* struct ifaddr pointer */
+               struct ifaddr *ifa = data;
+
+               ifnet_set_flags(ifp, IFF_UP|IFF_RUNNING, IFF_UP|IFF_RUNNING);
+               IFA_LOCK_SPIN(ifa);
+               ifa->ifa_rtrequest = lo_rtrequest;
+               IFA_UNLOCK(ifa);
                /*
                 * Everything else is done at a higher level.
                 */
                break;
+       }
 
-       case SIOCADDMULTI:
-       case SIOCDELMULTI:
-               if (ifr == 0) {
+       case SIOCADDMULTI:              /* struct ifreq */
+       case SIOCDELMULTI: {            /* struct ifreq */
+               struct ifreq *ifr = data;
+
+               if (ifr == NULL) {
                        error = EAFNOSUPPORT;           /* XXX */
                        break;
                }
@@ -366,12 +573,18 @@ loioctl(
                        break;
                }
                break;
+       }
+
+       case SIOCSIFMTU: {              /* struct ifreq */
+               struct ifreq *ifr = data;
 
-       case SIOCSIFMTU:
-               ifp->if_mtu = ifr->ifr_mtu;
+               bcopy(&ifr->ifr_mtu, &ifp->if_mtu, sizeof (int));
                break;
+       }
 
-       case SIOCSIFFLAGS:
+       case SIOCSIFFLAGS:              /* struct ifreq */
+       case SIOCSIFTIMESTAMPENABLE:
+       case SIOCSIFTIMESTAMPDISABLE:
                break;
 
        default:
@@ -383,98 +596,210 @@ loioctl(
 #endif /* NLOOP > 0 */
 
 
-static int  lo_attach_proto(struct ifnet *ifp, u_long protocol_family)
+static errno_t
+lo_attach_proto(struct ifnet *ifp, protocol_family_t protocol_family)
 {
-       struct dlil_proto_reg_str   reg;
-       int   stat =0 ;
-       
-       bzero(&reg, sizeof(reg));
-       TAILQ_INIT(&reg.demux_desc_head);
-       reg.interface_family = ifp->if_family;
-       reg.unit_number      = ifp->if_unit;
-       reg.input                        = lo_input;
-       reg.pre_output       = lo_pre_output;
-       reg.protocol_family  = protocol_family;
-       
-       stat = dlil_attach_protocol(&reg);
-
-       if (stat && stat != EEXIST) {
-               printf("lo_attach_proto: dlil_attach_protocol for %d returned=%d\n",
-                          protocol_family, stat);
+       struct ifnet_attach_proto_param_v2      proto;
+       errno_t                                                 result = 0;
+
+       bzero(&proto, sizeof (proto));
+       proto.input = lo_input;
+       proto.pre_output = lo_pre_output;
+
+       result = ifnet_attach_protocol_v2(ifp, protocol_family, &proto);
+
+       if (result && result != EEXIST) {
+               printf("lo_attach_proto: ifnet_attach_protocol for %u "
+                   "returned=%d\n", protocol_family, result);
        }
-       
-       return stat;
+
+       return (result);
 }
 
-void lo_reg_if_mods()
+static void
+lo_reg_if_mods(void)
 {
-     int error;
+       int error;
 
        /* Register protocol registration functions */
-       if ((error = dlil_reg_proto_module(PF_INET, APPLE_IF_FAM_LOOPBACK, lo_attach_proto, NULL)) != 0)
-               printf("dlil_reg_proto_module failed for AF_INET error=%d\n", error);
-
-       if ((error = dlil_reg_proto_module(PF_INET6, APPLE_IF_FAM_LOOPBACK, lo_attach_proto, NULL)) != 0)
-               printf("dlil_reg_proto_module failed for AF_INET6 error=%d\n", error);
+       if ((error = proto_register_plumber(PF_INET,
+           APPLE_IF_FAM_LOOPBACK, lo_attach_proto, NULL)) != 0)
+               printf("proto_register_plumber failed for AF_INET "
+                   "error=%d\n", error);
+
+       if ((error = proto_register_plumber(PF_INET6,
+           APPLE_IF_FAM_LOOPBACK, lo_attach_proto, NULL)) != 0)
+               printf("proto_register_plumber failed for AF_INET6 "
+                   "error=%d\n", error);
 }
 
 static errno_t
-lo_set_bpf_tap(
-       ifnet_t                 ifp,
-       bpf_tap_mode    mode,
-       bpf_packet_func bpf_callback)
+lo_set_bpf_tap(struct ifnet *ifp, bpf_tap_mode mode,
+    bpf_packet_func bpf_callback)
 {
+       VERIFY(ifp == lo_ifp);
 
-  /*
-   * NEED MUTEX HERE XXX
-   */
-       if (mode == BPF_TAP_DISABLE) {
-               lo_statics[ifp->if_unit].bpf_mode = mode;
-               lo_statics[ifp->if_unit].bpf_callback = bpf_callback;
-       }
-       else {
-               lo_statics[ifp->if_unit].bpf_callback = bpf_callback;
-               lo_statics[ifp->if_unit].bpf_mode = mode;               
+       lo_statics[0].bpf_mode = mode;
+
+       switch (mode) {
+               case BPF_TAP_DISABLE:
+               case BPF_TAP_INPUT:
+                       lo_statics[0].bpf_callback = NULL;
+                       break;
+
+               case BPF_TAP_OUTPUT:
+               case BPF_TAP_INPUT_OUTPUT:
+                       lo_statics[0].bpf_callback = bpf_callback;
+                       break;
        }
 
-       return 0;
+       return (0);
 }
 
-
 /* ARGSUSED */
 void
-loopattach(
-       __unused void *dummy)
+loopattach(void)
 {
-       struct ifnet *ifp;
-       int i = 0;
+       struct ifnet_init_eparams lo_init;
+       errno_t result = 0;
+
+       PE_parse_boot_argn("lo_txstart", &lo_txstart, sizeof (lo_txstart));
 
        lo_reg_if_mods();
 
-       for (ifp = loif; i < NLOOP; ifp++) {
-               lo_statics[i].bpf_callback = 0;
-               lo_statics[i].bpf_mode      = BPF_TAP_DISABLE;
-               bzero(ifp, sizeof(struct ifnet));
-               ifp->if_name = "lo";
-               ifp->if_family = APPLE_IF_FAM_LOOPBACK;
-               ifp->if_unit = i++;
-               ifp->if_mtu = LOMTU;
-               ifp->if_flags = IFF_LOOPBACK | IFF_MULTICAST;
-               ifp->if_ioctl = loioctl;
-               ifp->if_demux = lo_demux;
-               ifp->if_framer = lo_framer;
-               ifp->if_add_proto = lo_add_proto;
-               ifp->if_del_proto = lo_del_proto;
-               ifp->if_set_bpf_tap = lo_set_bpf_tap;
-               ifp->if_output = lo_output;
-               ifp->if_type = IFT_LOOP;
-               ifp->if_hwassist = IF_HWASSIST_CSUM_IP | IF_HWASSIST_CSUM_TCP | IF_HWASSIST_CSUM_UDP;
-               ifp->if_hdrlen = sizeof(struct loopback_header);
-               lo_ifp = ifp;
-               dlil_if_attach(ifp);
-#if NBPFILTER > 0
-               bpfattach(ifp, DLT_NULL, sizeof(u_int));
+       lo_statics[0].bpf_callback = NULL;
+       lo_statics[0].bpf_mode = BPF_TAP_DISABLE;
+
+       bzero(&lo_init, sizeof (lo_init));
+       lo_init.ver                     = IFNET_INIT_CURRENT_VERSION;
+       lo_init.len                     = sizeof (lo_init);
+       lo_init.sndq_maxlen             = LOSNDQ_MAXLEN;
+       if (lo_txstart) {
+               lo_init.flags           = 0;
+               lo_init.pre_enqueue     = lo_pre_enqueue;
+               lo_init.start           = lo_start;
+               lo_init.output_sched_model = lo_sched_model;
+       } else {
+               lo_init.flags           = IFNET_INIT_LEGACY;
+               lo_init.output          = lo_output;
+       }
+       lo_init.name                    = "lo";
+       lo_init.unit                    = 0;
+       lo_init.family                  = IFNET_FAMILY_LOOPBACK;
+       lo_init.type                    = IFT_LOOP;
+       lo_init.demux                   = lo_demux;
+       lo_init.add_proto               = lo_add_proto;
+       lo_init.del_proto               = lo_del_proto;
+       lo_init.framer_extended         = lo_framer;
+       lo_init.softc                   = &lo_statics[0];
+       lo_init.ioctl                   = lo_ioctl;
+       lo_init.set_bpf_tap             = lo_set_bpf_tap;
+
+       result = ifnet_allocate_extended(&lo_init, &lo_ifp);
+       if (result != 0) {
+               panic("%s: couldn't allocate loopback ifnet (%d)\n",
+                   __func__, result);
+               /* NOTREACHED */
+       }
+
+       ifnet_set_mtu(lo_ifp, LOMTU);
+       ifnet_set_flags(lo_ifp, IFF_LOOPBACK | IFF_MULTICAST,
+           IFF_LOOPBACK | IFF_MULTICAST);
+       ifnet_set_offload(lo_ifp,
+           IFNET_CSUM_IP | IFNET_CSUM_TCP | IFNET_CSUM_UDP |
+           IFNET_CSUM_TCPIPV6 | IFNET_CSUM_UDPIPV6 | IFNET_IPV6_FRAGMENT |
+           IFNET_CSUM_FRAGMENT | IFNET_IP_FRAGMENT | IFNET_MULTIPAGES |
+           IFNET_TX_STATUS | IFNET_SW_TIMESTAMP);
+       ifnet_set_hdrlen(lo_ifp, sizeof (struct loopback_header));
+       ifnet_set_eflags(lo_ifp, IFEF_SENDLIST, IFEF_SENDLIST);
+
+#if CONFIG_MACF_NET
+       mac_ifnet_label_init(ifp);
 #endif
+
+       result = ifnet_attach(lo_ifp, NULL);
+       if (result != 0) {
+               panic("%s: couldn't attach loopback ifnet (%d)\n",
+                   __func__, result);
+               /* NOTREACHED */
        }
-       loopattach_done = 1;
+       bpfattach(lo_ifp, DLT_NULL, sizeof (u_int32_t));
+}
+
+static int
+sysctl_dequeue_max SYSCTL_HANDLER_ARGS
+{
+#pragma unused(arg1, arg2)
+       u_int32_t i;
+       int err;
+
+       i = lo_dequeue_max;
+
+       err = sysctl_handle_int(oidp, &i, 0, req);
+       if (err != 0 || req->newptr == USER_ADDR_NULL)
+               return (err);
+
+       if (i < 1)
+               i = 1;
+       else if (i > LOSNDQ_MAXLEN)
+               i = LOSNDQ_MAXLEN;
+
+       lo_dequeue_max = i;
+
+       return (err);
+}
+
+static int
+sysctl_sched_model SYSCTL_HANDLER_ARGS
+{
+#pragma unused(arg1, arg2)
+       u_int32_t i;
+       int err;
+
+       i = lo_sched_model;
+
+       err = sysctl_handle_int(oidp, &i, 0, req);
+       if (err != 0 || req->newptr == USER_ADDR_NULL)
+               return (err);
+
+       switch (i) {
+       case IFNET_SCHED_MODEL_NORMAL:
+       case IFNET_SCHED_MODEL_DRIVER_MANAGED:
+       case IFNET_SCHED_MODEL_FQ_CODEL:
+               break;
+
+       default:
+               err = EINVAL;
+               break;
+       }
+
+       if (err == 0 && (err = ifnet_set_output_sched_model(lo_ifp, i)) == 0)
+               lo_sched_model = i;
+
+       return (err);
+}
+
+static int
+sysctl_dequeue_scidx SYSCTL_HANDLER_ARGS
+{
+#pragma unused(arg1, arg2)
+       u_int32_t i;
+       int err;
+
+       i = lo_dequeue_scidx;
+
+       err = sysctl_handle_int(oidp, &i, 0, req);
+       if (err != 0 || req->newptr == USER_ADDR_NULL)
+               return (err);
+
+       if (!MBUF_VALID_SCIDX(i))
+               return (EINVAL);
+
+       if (lo_sched_model != IFNET_SCHED_MODEL_DRIVER_MANAGED)
+               return (ENODEV);
+
+       lo_dequeue_sc = m_service_class_from_idx(i);
+       lo_dequeue_scidx = MBUF_SCIDX(lo_dequeue_sc);
+
+       return (err);
 }