]> git.saurik.com Git - apple/xnu.git/blobdiff - bsd/net/dlil.c
xnu-517.3.15.tar.gz
[apple/xnu.git] / bsd / net / dlil.c
index 001492dfba5612fc9331b76c0b4a59c6ac0f5416..fcdf70f3adf288dce9ec06fcee1a5dd47b8d09ba 100644 (file)
@@ -3,19 +3,22 @@
  *
  * @APPLE_LICENSE_HEADER_START@
  * 
- * The contents of this file constitute Original Code as defined in and
- * are subject to the Apple Public Source License Version 1.1 (the
- * "License").  You may not use this file except in compliance with the
- * License.  Please obtain a copy of the License at
- * http://www.apple.com/publicsource and read it before using this file.
+ * Copyright (c) 1999-2003 Apple Computer, Inc.  All Rights Reserved.
  * 
- * This Original Code and all software distributed under the License are
- * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * 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. 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 OR NON-INFRINGEMENT.  Please see the
- * License for the specific language governing rights and limitations
- * under the License.
+ * 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_LICENSE_HEADER_END@
  */
 #include <sys/kdebug.h>
 #include <string.h>
 
-#include <kern/thread.h>
 #include <kern/task.h>
+#include <kern/thread.h>
+#include <kern/sched_prim.h>
+
 #include <net/netisr.h>
 #include <net/if_types.h>
 
+#include <machine/machine_routines.h>
 
 #define DBG_LAYER_BEG          DLILDBG_CODE(DBG_DLIL_STATIC, 0)
 #define DBG_LAYER_END          DLILDBG_CODE(DBG_DLIL_STATIC, 2)
@@ -55,8 +61,8 @@
 #define DBG_FNC_DLIL_IFOUT      DLILDBG_CODE(DBG_DLIL_STATIC, (3 << 8))
 
 
-#define MAX_DL_TAGS 50
-#define MAX_DLIL_FILTERS 50
+#define MAX_DL_TAGS            16
+#define MAX_DLIL_FILTERS       16
 #define MAX_FRAME_TYPE_SIZE 4 /* LONGWORDS */
 #define MAX_LINKADDR       4 /* LONGWORDS */
 #define M_NKE M_IFADDR
@@ -71,6 +77,20 @@ struct dl_tag_str {
 };
 
 
+struct dlil_ifnet {
+    /* ifnet and drvr_ext are used by the stack and drivers
+    drvr_ext extends the public ifnet and must follow dl_if */
+    struct ifnet       dl_if;                  /* public ifnet */
+    void               *drvr_ext[4];   /* driver reserved (e.g arpcom extension for enet) */ 
+    
+    /* dlil private fields */
+    TAILQ_ENTRY(dlil_ifnet) dl_if_link;        /* dlil_ifnet are link together */
+                                                               /* it is not the ifnet list */
+    void               *if_uniqueid;   /* unique id identifying the interface */
+    size_t             if_uniqueid_len;/* length of the unique id */
+    char               if_namestorage[IFNAMSIZ]; /* interface name storage for detached interfaces */
+};
+
 struct dlil_stats_str {
     int           inject_pr_in1;    
     int           inject_pr_in2;
@@ -103,6 +123,7 @@ struct if_family_str {
 
     int (*add_if)(struct ifnet *ifp);
     int (*del_if)(struct ifnet *ifp);
+    int (*init_if)(struct ifnet *ifp);
     int (*add_proto)(struct ddesc_head_str *demux_desc_head,
                     struct if_proto  *proto, u_long dl_tag);
     int (*del_proto)(struct if_proto  *proto, u_long dl_tag);
@@ -111,32 +132,55 @@ struct if_family_str {
 };
 
 
+struct proto_family_str {
+       TAILQ_ENTRY(proto_family_str) proto_fam_next;
+       u_long  proto_family;
+       u_long  if_family;
+
+       int (*attach_proto)(struct ifnet *ifp, u_long *dl_tag);
+       int (*detach_proto)(struct ifnet *ifp, u_long dl_tag);
+};
+
+
 
 struct dlil_stats_str dlil_stats;
 
 static
-struct dlil_filter_id_str dlil_filters[MAX_DLIL_FILTERS+1];
+struct dlil_filter_id_str *dlil_filters;
 
 static
-struct dl_tag_str    dl_tag_array[MAX_DL_TAGS+1];
+struct dl_tag_str *dl_tag_array;
+
+static
+TAILQ_HEAD(, dlil_ifnet) dlil_ifnet_head;
 
 static 
 TAILQ_HEAD(, if_family_str) if_family_head;
 
+static 
+TAILQ_HEAD(, proto_family_str) proto_family_head;
+
 static             ifnet_inited = 0;
+static u_long  dl_tag_nb = 0; 
+static u_long  dlil_filters_nb = 0; 
 
 int dlil_initialized = 0;
 decl_simple_lock_data(, dlil_input_lock)
 int dlil_input_thread_wakeup = 0;
-int dlil_expand_mcl;
 static struct mbuf *dlil_input_mbuf_head = NULL;
 static struct mbuf *dlil_input_mbuf_tail = NULL;
+#if NLOOP > 1
+#error dlil_input() needs to be revised to support more than on loopback interface
+#endif
 static struct mbuf *dlil_input_loop_head = NULL;
 static struct mbuf *dlil_input_loop_tail = NULL;
+extern struct ifmultihead ifma_lostlist;
 
 static void dlil_input_thread(void);
 extern void run_netisr(void);
+extern void bpfdetach(struct ifnet*);
 
+int dlil_expand_mcl;
 
 /*
  * Internal functions.
@@ -155,6 +199,20 @@ struct if_family_str *find_family_module(u_long if_family)
     return mod;
 }
 
+static 
+struct proto_family_str *find_proto_module(u_long proto_family, u_long if_family)
+{
+       struct proto_family_str  *mod = NULL;
+
+       TAILQ_FOREACH(mod, &proto_family_head, proto_fam_next) {
+               if ((mod->proto_family == (proto_family & 0xffff)) 
+                       && (mod->if_family == (if_family & 0xffff))) 
+                       break;
+               }
+
+       return mod;
+}
+
 
 /*
  * Public functions.
@@ -172,10 +230,11 @@ struct ifnet *ifbyfamily(u_long family, short unit)
     return 0;
 }
 
-struct if_proto *dlttoproto(dl_tag)
-    u_long dl_tag;
+struct if_proto *dlttoproto(u_long dl_tag)
 {
-    return dl_tag_array[dl_tag].proto;
+    if (dl_tag < dl_tag_nb && dl_tag_array[dl_tag].ifp)
+       return dl_tag_array[dl_tag].proto;
+    return 0;
 }
 
 
@@ -223,27 +282,6 @@ int  dlil_find_dltag(u_long if_family, short unit, u_long proto_family, u_long *
 }
 
 
-int dlil_get_next_dl_tag(u_long current_tag, struct dl_tag_attr_str *next)
-{
-    int i;
-
-    for (i = (current_tag+1); i < MAX_DL_TAGS; i++)
-       if (dl_tag_array[i].ifp) {
-           next->dl_tag   = i;
-           next->if_flags = dl_tag_array[i].ifp->if_flags;
-           next->if_unit  = dl_tag_array[i].ifp->if_unit;
-           next->protocol_family = dl_tag_array[i].proto->protocol_family;
-           next->if_family = dl_tag_array[i].ifp->if_family;
-           return 0;
-       }
-
-    /*
-     * If we got here, there are no more entries
-     */
-
-    return ENOENT;
-} 
-
 void dlil_post_msg(struct ifnet *ifp, u_long event_subclass, u_long event_code, 
                   struct net_event_data *event_data, u_long event_data_len) 
 {
@@ -284,14 +322,27 @@ dlil_init()
 {
     int i;
 
-    printf("dlil_init\n");
-
+    TAILQ_INIT(&dlil_ifnet_head);
     TAILQ_INIT(&if_family_head);
-    for (i=0; i < MAX_DL_TAGS; i++)
-       dl_tag_array[i].ifp = 0;
+    TAILQ_INIT(&proto_family_head);
 
-    for (i=0; i < MAX_DLIL_FILTERS; i++)
-       dlil_filters[i].type = 0;
+    // create the dl tag array
+    MALLOC(dl_tag_array, void *, sizeof(struct dl_tag_str) * MAX_DL_TAGS, M_NKE, M_WAITOK);
+    if (dl_tag_array == 0) {
+        printf("dlil_init tags array allocation failed\n");
+        return;        //very bad
+    }
+    bzero(dl_tag_array, sizeof(struct dl_tag_str) * MAX_DL_TAGS);
+    dl_tag_nb = MAX_DL_TAGS;
+
+    // create the dl filters array
+    MALLOC(dlil_filters, void *, sizeof(struct dlil_filter_id_str) * MAX_DLIL_FILTERS, M_NKE, M_WAITOK);
+    if (dlil_filters == 0) {
+        printf("dlil_init filters array allocation failed\n");
+        return;        //very bad
+    }
+    bzero(dlil_filters, sizeof(struct dlil_filter_id_str) * MAX_DLIL_FILTERS);
+    dlil_filters_nb = MAX_DLIL_FILTERS;
 
     bzero(&dlil_stats, sizeof(dlil_stats));
 
@@ -303,16 +354,29 @@ dlil_init()
     (void) kernel_thread(kernel_task, dlil_input_thread);
 }
 
-
 u_long get_new_filter_id()
 {
     u_long i;
-
-    for (i=1; i < MAX_DLIL_FILTERS; i++)
+    u_char *p;
+    
+    for (i=1; i < dlil_filters_nb; i++)
        if (dlil_filters[i].type == 0)
-           return i;
-    return 0;
+           break;
+
+    if (i == dlil_filters_nb) {
+        // expand the filters array by MAX_DLIL_FILTERS
+        MALLOC(p, u_char *, sizeof(struct dlil_filter_id_str) * (dlil_filters_nb + MAX_DLIL_FILTERS), M_NKE, M_WAITOK);
+        if (p == 0)
+            return 0;
+
+        bcopy(dlil_filters, p, sizeof(struct dlil_filter_id_str) * dlil_filters_nb);
+        bzero(p + sizeof(struct dlil_filter_id_str) * dlil_filters_nb, sizeof(struct dlil_filter_id_str) * MAX_DL_TAGS);
+        dlil_filters_nb += MAX_DLIL_FILTERS;
+        FREE(dlil_filters, M_NKE);
+        dlil_filters = (struct dlil_filter_id_str *)p;
+    }
+    
+    return i;
 }
 
 
@@ -322,24 +386,36 @@ int   dlil_attach_interface_filter(struct ifnet *ifp,
                                   int                     insertion_point)
 {
     int s;
-    int retval;
+    int retval = 0;
     struct dlil_filterq_entry  *tmp_ptr;
     struct dlil_filterq_entry  *if_filt;
     struct dlil_filterq_head *fhead = (struct dlil_filterq_head *) &ifp->if_flt_head;
     boolean_t funnel_state;
 
-
     MALLOC(tmp_ptr, struct dlil_filterq_entry *, sizeof(*tmp_ptr), M_NKE, M_WAITOK);
     if (tmp_ptr == NULL)
-       return (ENOBUFS);
+        return (ENOBUFS);
 
     bcopy((caddr_t) if_filter, (caddr_t) &tmp_ptr->variants.if_filter, 
          sizeof(struct dlil_if_flt_str));
 
     funnel_state = thread_funnel_set(network_flock, TRUE);
-
     s = splnet();
 
+    *filter_id = get_new_filter_id();
+    if (*filter_id == 0) {
+       FREE(tmp_ptr, M_NKE);
+       retval = ENOMEM;
+        goto end;
+    }
+    
+    dlil_filters[*filter_id].filter_ptr = tmp_ptr;
+    dlil_filters[*filter_id].head = (struct dlil_filterq_head *) &ifp->if_flt_head;
+    dlil_filters[*filter_id].type = DLIL_IF_FILTER;
+    dlil_filters[*filter_id].ifp = ifp;
+    tmp_ptr->filter_id = *filter_id;
+    tmp_ptr->type         = DLIL_IF_FILTER;
+
     if (insertion_point != DLIL_LAST_FILTER) {
        TAILQ_FOREACH(if_filt, fhead, que)
            if (insertion_point == if_filt->filter_id) {
@@ -350,22 +426,7 @@ int   dlil_attach_interface_filter(struct ifnet *ifp,
     else 
        TAILQ_INSERT_TAIL(fhead, tmp_ptr, que);
 
-    if (*filter_id = get_new_filter_id()) {
-       dlil_filters[*filter_id].filter_ptr = tmp_ptr;
-       dlil_filters[*filter_id].head = (struct dlil_filterq_head *) &ifp->if_flt_head;
-       dlil_filters[*filter_id].type = DLIL_IF_FILTER;
-       dlil_filters[*filter_id].ifp = ifp;
-       tmp_ptr->filter_id = *filter_id;
-       tmp_ptr->type      = DLIL_IF_FILTER;
-       retval = 0;
-    }
-    else {
-       kprintf("dlil_attach_interface_filter - can't alloc filter_id\n");
-       TAILQ_REMOVE(fhead, tmp_ptr, que);
-       FREE(tmp_ptr, M_NKE);
-       retval = ENOMEM;
-    }
-
+end:
     splx(s);
     thread_funnel_set(network_flock, funnel_state);
     return retval;
@@ -377,28 +438,39 @@ int   dlil_attach_protocol_filter(u_long                   dl_tag,
                                  u_long                         *filter_id,
                                  int                            insertion_point)
 {
-    struct dlil_filterq_entry  *tmp_ptr;
-    struct dlil_filterq_entry  *pr_filt;
+    struct dlil_filterq_entry  *tmp_ptr, *pr_filt;
     int s;
-    int retval;
+    int retval = 0;
     boolean_t funnel_state;
-
-    if (dl_tag > MAX_DL_TAGS)
-       return ERANGE;
-
-    if (dl_tag_array[dl_tag].ifp == 0)
-       return ENOENT;
+    
+    if (dl_tag >= dl_tag_nb || dl_tag_array[dl_tag].ifp == 0)
+        return (ENOENT);
 
     MALLOC(tmp_ptr, struct dlil_filterq_entry *, sizeof(*tmp_ptr), M_NKE, M_WAITOK);
     if (tmp_ptr == NULL)
-       return (ENOBUFS);
+        return (ENOBUFS);
 
     bcopy((caddr_t) pr_filter, (caddr_t) &tmp_ptr->variants.pr_filter, 
          sizeof(struct dlil_pr_flt_str));
 
     funnel_state = thread_funnel_set(network_flock, TRUE);
-
     s = splnet();
+
+    *filter_id = get_new_filter_id();
+    if (*filter_id == 0) {
+       FREE(tmp_ptr, M_NKE);
+       retval =  ENOMEM;
+        goto end;
+    }
+    
+    dlil_filters[*filter_id].filter_ptr = tmp_ptr; 
+    dlil_filters[*filter_id].head = dl_tag_array[dl_tag].pr_flt_head;
+    dlil_filters[*filter_id].type = DLIL_PR_FILTER;
+    dlil_filters[*filter_id].proto = dl_tag_array[dl_tag].proto;
+    dlil_filters[*filter_id].ifp   = dl_tag_array[dl_tag].ifp;
+    tmp_ptr->filter_id = *filter_id;
+    tmp_ptr->type         = DLIL_PR_FILTER;
+
     if (insertion_point != DLIL_LAST_FILTER) {
        TAILQ_FOREACH(pr_filt, dl_tag_array[dl_tag].pr_flt_head, que)
            if (insertion_point == pr_filt->filter_id) {
@@ -409,24 +481,7 @@ int   dlil_attach_protocol_filter(u_long                    dl_tag,
     else 
        TAILQ_INSERT_TAIL(dl_tag_array[dl_tag].pr_flt_head, tmp_ptr, que);
 
-    
-    if (*filter_id = get_new_filter_id()) {
-       dlil_filters[*filter_id].filter_ptr = tmp_ptr; 
-       dlil_filters[*filter_id].head = dl_tag_array[dl_tag].pr_flt_head;
-       dlil_filters[*filter_id].type = DLIL_PR_FILTER;
-       dlil_filters[*filter_id].proto = dl_tag_array[dl_tag].proto;
-       dlil_filters[*filter_id].ifp   = dl_tag_array[dl_tag].ifp;
-       tmp_ptr->filter_id = *filter_id;
-       tmp_ptr->type      = DLIL_PR_FILTER;
-       retval = 0;
-    }
-    else {
-       kprintf("dlil_attach_protocol_filter - can't alloc filter_id\n");
-       TAILQ_REMOVE(dl_tag_array[dl_tag].pr_flt_head, tmp_ptr, que);
-       FREE(tmp_ptr, M_NKE);
-       retval =  ENOMEM;
-    }
-
+end:
     splx(s);
     thread_funnel_set(network_flock, funnel_state);
     return retval;
@@ -437,23 +492,18 @@ int
 dlil_detach_filter(u_long      filter_id)
 {
     struct dlil_filter_id_str *flt;
-    int s;
+    int s, retval = 0;
     boolean_t funnel_state;
 
-    if (filter_id > MAX_DLIL_FILTERS) {
-       kprintf("dlil_detach_filter - Bad filter_id value %d\n", filter_id);
-       return ERANGE;
-    }
-
     funnel_state = thread_funnel_set(network_flock, TRUE);
     s = splnet();
-    flt = &dlil_filters[filter_id];
-    if (flt->type == 0) {
-       kprintf("dlil_detach_filter - no such filter_id %d\n", filter_id);
-       thread_funnel_set(network_flock, funnel_state);
-       return ENOENT;
+    
+    if (filter_id >= dlil_filters_nb || dlil_filters[filter_id].type == 0) {
+        retval = ENOENT;
+       goto end;
     }
 
+    flt = &dlil_filters[filter_id];
 
     if (flt->type == DLIL_IF_FILTER) {
        if (IFILT(flt->filter_ptr).filter_detach)
@@ -469,18 +519,18 @@ dlil_detach_filter(u_long filter_id)
     TAILQ_REMOVE(flt->head, flt->filter_ptr, que);
     FREE(flt->filter_ptr, M_NKE);
     flt->type = 0;
+
+end:
     splx(s);
     thread_funnel_set(network_flock, funnel_state);
-    return 0;
+    return retval;
 }
 
-
 void
 dlil_input_thread_continue(void)
 {
     while (1) {
         struct mbuf *m, *m_loop;
-       int expand_mcl;
 
         usimple_lock(&dlil_input_lock);
         m = dlil_input_mbuf_head;
@@ -491,16 +541,6 @@ dlil_input_thread_continue(void)
         dlil_input_loop_tail = NULL;
         usimple_unlock(&dlil_input_lock);
        
-       MBUF_LOCK();
-       expand_mcl = dlil_expand_mcl;
-       dlil_expand_mcl = 0;
-       MBUF_UNLOCK();
-       if (expand_mcl) {
-               caddr_t p;
-               MCLALLOC(p, M_WAIT);
-               if (p) MCLFREE(p);
-       }
-
         /*
          * NOTE warning %%% attention !!!!
          * We should think about putting some thread starvation safeguards if 
@@ -519,11 +559,10 @@ dlil_input_thread_continue(void)
         while (m) {
             struct mbuf *m0 = m->m_nextpkt;
             void *header = m->m_pkthdr.header;
-            struct ifnet *ifp = (struct ifnet *) m->m_pkthdr.aux;
+            struct ifnet *ifp = &loif[0];
 
             m->m_nextpkt = NULL;
             m->m_pkthdr.header = NULL;
-            m->m_pkthdr.aux = NULL;
             (void) dlil_input_packet(ifp, m, header);
             m = m0;
         }
@@ -535,11 +574,7 @@ dlil_input_thread_continue(void)
             dlil_input_loop_head == NULL &&
             netisr == 0) {
             assert_wait(&dlil_input_thread_wakeup, THREAD_UNINT);
-#if defined (__i386__)
-            thread_block(0);
-#else
-            thread_block(dlil_input_thread_continue);
-#endif
+            (void) thread_block(dlil_input_thread_continue);
         /* NOTREACHED */
         }
     }
@@ -547,17 +582,10 @@ dlil_input_thread_continue(void)
 
 void dlil_input_thread(void)
 {
-    register thread_t self = current_thread();
-    extern void stack_privilege(thread_t thread);
-
-    printf("dlil_input_thread %x\n", self);
+    register thread_t self = current_act();
 
-    /*
-     *      Make sure that this thread
-     *      always has a kernel stack, and
-     *      bind it to the master cpu.
-     */
-    stack_privilege(self);
+    ml_thread_policy(self, MACHINE_GROUP,
+                                               (MACHINE_NETWORK_GROUP|MACHINE_NETWORK_NETISR));
 
     /* The dlil thread is always funneled */
     thread_funnel_set(network_flock, TRUE);
@@ -864,39 +892,16 @@ dlil_output(u_long                dl_tag,
     char                        dst_linkaddr_buffer[MAX_LINKADDR * 4];
     struct dlil_filterq_head    *fhead;
 
-
     KERNEL_DEBUG(DBG_FNC_DLIL_OUTPUT | DBG_FUNC_START,0,0,0,0,0);
 
-    /*
-     * Temporary hackery until all the existing protocols can become fully
-     * "dl_tag aware". Some things only have the ifp, so this handles that
-     * case for the time being.
-     */
-
-    if (dl_tag > MAX_DL_TAGS) {
-       /* dl_tag is really an ifnet pointer! */
-
-       ifp = (struct ifnet *) dl_tag;
-       dl_tag = ifp->if_data.default_proto;
-       if (dl_tag)
-           proto = dl_tag_array[dl_tag].proto;
-       else
-           retval = ENOENT;
-    }
-    else {
-       if ((dl_tag == 0) || (dl_tag_array[dl_tag].ifp == 0))
-           retval = ENOENT;
-       else {
-           ifp = dl_tag_array[dl_tag].ifp;
-           proto = dl_tag_array[dl_tag].proto;
-       }
-    }
-    
-    if (retval) {
-       m_freem(m);
-       return retval;
+    if (dl_tag >= dl_tag_nb || dl_tag_array[dl_tag].ifp == 0) {
+       m_freem(m);
+        return ENOENT;
     }
 
+    ifp = dl_tag_array[dl_tag].ifp;
+    proto = dl_tag_array[dl_tag].proto;
+
     frame_type    = frame_type_buffer;
     dst_linkaddr   = dst_linkaddr_buffer;
 
@@ -956,10 +961,11 @@ dlil_output(u_long                dl_tag,
 #if BRIDGE
     if (do_bridge) {
        struct mbuf *m0 = m ;
+       struct ether_header *eh = mtod(m, struct ether_header *);
        
        if (m->m_pkthdr.rcvif)
            m->m_pkthdr.rcvif = NULL ;
-       ifp = bridge_dst_lookup(m);
+       ifp = bridge_dst_lookup(eh);
        bdg_forward(&m0, ifp);
        if (m0)
            m_freem(m0);
@@ -1026,15 +1032,13 @@ dlil_ioctl(u_long       proto_fam,
      struct dlil_filterq_head   *fhead;
      int                        retval  = EOPNOTSUPP;
      int                         retval2 = EOPNOTSUPP;
-     u_long                     dl_tag;
+     u_long                     dl_tag;
      struct if_family_str    *if_family;
 
 
      if (proto_fam) {
-         retval = dlil_find_dltag(ifp->if_family, ifp->if_unit,
-                                  proto_fam, &dl_tag);
-
-         if (retval == 0) {
+         if (dlil_find_dltag(ifp->if_family, ifp->if_unit,
+                             proto_fam, &dl_tag) == 0) {
               if (dl_tag_array[dl_tag].ifp != ifp)
                    return ENOENT;
        
@@ -1067,8 +1071,6 @@ dlil_ioctl(u_long proto_fam,
               else
                    retval = EOPNOTSUPP;
          }
-         else
-              retval = 0;
      }
 
      if ((retval) && (retval != EOPNOTSUPP)) {
@@ -1131,12 +1133,11 @@ dlil_attach_protocol(struct dlil_proto_reg_str   *proto,
     struct if_proto  *ifproto;
     u_long          i;
     struct if_family_str *if_family;
-    int                     error;
     struct dlil_proto_head  *tmp;
     struct kev_dl_proto_data   ev_pr_data;
-    int         s;
+    int         s, retval = 0;
     boolean_t funnel_state;
-
+    u_char *p;
 
     if ((proto->protocol_family == 0) || (proto->interface_family == 0))
        return EINVAL;
@@ -1147,36 +1148,42 @@ dlil_attach_protocol(struct dlil_proto_reg_str   *proto,
     if ((!if_family) || (if_family->flags & DLIL_SHUTDOWN)) {
        kprintf("dlil_attach_protocol -- no interface family module %d", 
               proto->interface_family);
-       splx(s);
-       thread_funnel_set(network_flock, funnel_state);
-       return ENOENT;
+       retval = ENOENT;
+        goto end;
     }
 
     ifp = ifbyfamily(proto->interface_family, proto->unit_number);
     if (!ifp) {
        kprintf("dlil_attach_protocol -- no such interface %d unit %d\n", 
               proto->interface_family, proto->unit_number);
-       splx(s);
-       thread_funnel_set(network_flock, funnel_state);
-       return ENOENT;
+       retval = ENOENT;
+        goto end;
     }
 
     if (dlil_find_dltag(proto->interface_family, proto->unit_number,
                        proto->protocol_family, &i) == 0) {
-        thread_funnel_set(network_flock, funnel_state);
-        return EEXIST;
+       retval = EEXIST;
+        goto end;
     }
 
-    for (i=1; i < MAX_DL_TAGS; i++)
+    for (i=1; i < dl_tag_nb; i++)
        if (dl_tag_array[i].ifp == 0)
            break;
 
-    if (i >= MAX_DL_TAGS) {
-       splx(s);
-       thread_funnel_set(network_flock, funnel_state);
-       return ENOMEM;
+    if (i == dl_tag_nb) {
+        // expand the tag array by MAX_DL_TAGS
+        MALLOC(p, u_char *, sizeof(struct dl_tag_str) * (dl_tag_nb + MAX_DL_TAGS), M_NKE, M_WAITOK);
+        if (p == 0) {
+            retval = ENOBUFS;
+            goto end;
+        }
+        bcopy(dl_tag_array, p, sizeof(struct dl_tag_str) * dl_tag_nb);
+        bzero(p + sizeof(struct dl_tag_str) * dl_tag_nb, sizeof(struct dl_tag_str) * MAX_DL_TAGS);
+        dl_tag_nb += MAX_DL_TAGS;
+        FREE(dl_tag_array, M_NKE);
+        dl_tag_array = (struct dl_tag_str *)p;
     }
-
+    
     /*
      * Allocate and init a new if_proto structure
      */
@@ -1184,8 +1191,8 @@ dlil_attach_protocol(struct dlil_proto_reg_str     *proto,
     ifproto = _MALLOC(sizeof(struct if_proto), M_IFADDR, M_WAITOK);
     if (!ifproto) {
        printf("ERROR - DLIL failed if_proto allocation\n");
-       thread_funnel_set(network_flock, funnel_state);
-       return ENOMEM;
+       retval = ENOMEM;
+        goto end;
     }
     
     bzero(ifproto, sizeof(struct if_proto));
@@ -1216,13 +1223,11 @@ dlil_attach_protocol(struct dlil_proto_reg_str   *proto,
      * Call family module add_proto routine so it can refine the
      * demux descriptors as it wishes.
      */
-    error = (*if_family->add_proto)(&proto->demux_desc_head, ifproto, *dl_tag);
-    if (error) {
-       dl_tag_array[*dl_tag].ifp = 0;
+    retval = (*if_family->add_proto)(&proto->demux_desc_head, ifproto, *dl_tag);
+    if (retval) {
+       dl_tag_array[i].ifp = 0;
        FREE(ifproto, M_IFADDR);
-       splx(s);
-       thread_funnel_set(network_flock, funnel_state);
-       return error;
+        goto end;
     }
 
     /*
@@ -1242,9 +1247,10 @@ dlil_attach_protocol(struct dlil_proto_reg_str    *proto,
                  (struct net_event_data *)&ev_pr_data, 
                  sizeof(struct kev_dl_proto_data));
 
+end:
     splx(s);
     thread_funnel_set(network_flock, funnel_state);
-    return 0;
+    return retval;
 }
 
 
@@ -1258,22 +1264,17 @@ dlil_detach_protocol(u_long     dl_tag)
     struct dlil_proto_head  *tmp; 
     struct if_family_str   *if_family;
     struct dlil_filterq_entry *filter;
-    int s, retval;
+    int s, retval = 0;
     struct dlil_filterq_head *fhead;
     struct kev_dl_proto_data   ev_pr_data;
     boolean_t funnel_state;
 
-
-    if (dl_tag > MAX_DL_TAGS) 
-       return ERANGE;
-
     funnel_state = thread_funnel_set(network_flock, TRUE);
-
     s = splnet();
-    if (dl_tag_array[dl_tag].ifp == 0) {
-       splx(s);
-       thread_funnel_set(network_flock, funnel_state);
-       return ENOENT;
+
+    if (dl_tag >= dl_tag_nb || dl_tag_array[dl_tag].ifp == 0) {
+       retval = ENOENT;
+       goto end;
     }
 
     ifp = dl_tag_array[dl_tag].ifp;
@@ -1281,9 +1282,8 @@ dlil_detach_protocol(u_long       dl_tag)
 
     if_family = find_family_module(ifp->if_family);
     if (if_family == NULL) {
-        splx(s);
-        thread_funnel_set(network_flock, funnel_state);
-        return ENOENT;
+       retval = ENOENT;
+       goto end;
     }
 
     tmp = (struct dlil_proto_head *) &ifp->proto_head;
@@ -1311,6 +1311,13 @@ dlil_detach_protocol(u_long      dl_tag)
        
     /* the reserved field carries the number of protocol still attached (subject to change) */
     ev_pr_data.proto_family   = proto->protocol_family;
+
+    /*
+     * Cleanup routes that may still be in the routing table for that interface/protocol pair.
+     */
+
+    if_rtproto_del(ifp, proto->protocol_family);
+
     TAILQ_REMOVE(tmp, proto, next);
     FREE(proto, M_IFADDR);
 
@@ -1320,9 +1327,7 @@ dlil_detach_protocol(u_long       dl_tag)
                  (struct net_event_data *)&ev_pr_data, 
                  sizeof(struct kev_dl_proto_data));
 
-    if (ifp->refcnt == 0 && (ifp->if_eflags & IFEF_DETACH_DISABLED) == 0) {
-       if (ifp->if_flags & IFF_UP) 
-           printf("WARNING - dlil_detach_protocol - ifp refcnt 0, but IF still up\n");
+    if (ifp->refcnt == 0) {
 
        TAILQ_REMOVE(&ifnet, ifp, if_link);
 
@@ -1359,9 +1364,10 @@ dlil_detach_protocol(u_long      dl_tag)
        (*ifp->if_free)(ifp);
     }
 
+end:
     splx(s);
     thread_funnel_set(network_flock, funnel_state);
-    return 0;
+    return retval;
 }
 
 
@@ -1395,33 +1401,42 @@ dlil_if_attach(struct ifnet     *ifp)
        return ENODEV;
     }
 
+    if (ifp->refcnt == 0) {
+        /*
+        * Call the family module to fill in the appropriate fields in the
+        * ifnet structure.
+        */
+        
+        stat = (*if_family->add_if)(ifp);
+        if (stat) {
+            splx(s);
+            kprintf("dlil_if_attach -- add_if failed with %d\n", stat);
+            thread_funnel_set(network_flock, funnel_state);
+            return stat;
+        }
+       if_family->refcnt++;
 
-    /*
-     * Call the family module to fill in the appropriate fields in the
-     * ifnet structure.
-     */
-
-    stat = (*if_family->add_if)(ifp);
-    if (stat) {
-       splx(s);
-       kprintf("dlil_if_attach -- add_if failed with %d\n", stat);
-       thread_funnel_set(network_flock, funnel_state);
-       return stat;
+        /*
+        * Add the ifp to the interface list.
+        */
+    
+        tmp = (struct dlil_proto_head *) &ifp->proto_head;
+        TAILQ_INIT(tmp);
+        
+        ifp->if_data.default_proto = 0;
+        ifp->offercnt = 0;
+        TAILQ_INIT(&ifp->if_flt_head);
+        old_if_attach(ifp);
+        
+        if (if_family->init_if) {
+            stat = (*if_family->init_if)(ifp);
+            if (stat) {
+                kprintf("dlil_if_attach -- init_if failed with %d\n", stat);
+            }
+        }
     }
-
-    /*
-     * Add the ifp to the interface list.
-     */
-
-    tmp = (struct dlil_proto_head *) &ifp->proto_head;
-    TAILQ_INIT(tmp);
     
-    ifp->if_data.default_proto = 0;
-    ifp->refcnt = 1;
-    ifp->offercnt = 0;
-    TAILQ_INIT(&ifp->if_flt_head);
-    old_if_attach(ifp);
-    if_family->refcnt++;
+    ifp->refcnt++;
 
     dlil_post_msg(ifp, KEV_DL_SUBCLASS, KEV_DL_IF_ATTACHED, 0, 0);
 
@@ -1434,59 +1449,69 @@ dlil_if_attach(struct ifnet     *ifp)
 int
 dlil_if_detach(struct ifnet *ifp)
 {
-    struct if_proto  *proto;
-    struct dlil_filterq_entry *if_filter;
-    struct if_family_str    *if_family;
-    struct dlil_filterq_head *fhead = (struct dlil_filterq_head *) &ifp->if_flt_head;
-    int s;
-    struct kev_msg   ev_msg;
-    boolean_t funnel_state;
-
-    funnel_state = thread_funnel_set(network_flock, TRUE);
-    s = splnet();
-    if (ifp->if_flags & IFF_UP)
-       printf("WARNING - dlil_if_detach called for UP interface\n");
-
-    if_family = find_family_module(ifp->if_family);
-
-    if (!if_family) {
-       kprintf("Attempt to detach interface without family module - %s\n", 
-              ifp->if_name);
-       splx(s);
-       thread_funnel_set(network_flock, funnel_state);
-       return ENODEV;
-    }
-
-    while (if_filter = TAILQ_FIRST(fhead)) 
-          dlil_detach_filter(if_filter->filter_id);
-
-    ifp->refcnt--;
-
-    if (ifp->refcnt == 0 && (ifp->if_eflags & IFEF_DETACH_DISABLED) == 0) {
+       struct if_proto  *proto;
+       struct dlil_filterq_entry *if_filter;
+       struct if_family_str    *if_family;
+       struct dlil_filterq_head *fhead = (struct dlil_filterq_head *) &ifp->if_flt_head;
+       struct kev_msg   ev_msg;
+       boolean_t funnel_state;
+       
+       funnel_state = thread_funnel_set(network_flock, TRUE);
+       
+       if_family = find_family_module(ifp->if_family);
+       
+       if (!if_family) {
+               kprintf("Attempt to detach interface without family module - %s\n", 
+                               ifp->if_name);
+               thread_funnel_set(network_flock, funnel_state);
+               return ENODEV;
+       }
+       
+       while (if_filter = TAILQ_FIRST(fhead)) 
+               dlil_detach_filter(if_filter->filter_id);
+       
+       ifp->refcnt--;
+       
+       if (ifp->refcnt > 0) {
+               dlil_post_msg(ifp, KEV_DL_SUBCLASS, KEV_DL_IF_DETACHING, 0, 0);
+               thread_funnel_set(network_flock, funnel_state);
+               return DLIL_WAIT_FOR_FREE;
+       }
+       
+       while (ifp->if_multiaddrs.lh_first) {
+               struct ifmultiaddr *ifma = ifp->if_multiaddrs.lh_first;
+               
+               /*
+                * When the interface is gone, we will no
+                * longer be listening on these multicasts.
+                * Various bits of the stack may be referencing
+                * these multicasts, so we can't just free them.
+                * We place them on a list so they may be cleaned
+                * up later as the other bits of the stack release
+                * them.
+                */
+               LIST_REMOVE(ifma, ifma_link);
+               ifma->ifma_ifp = NULL;
+               LIST_INSERT_HEAD(&ifma_lostlist, ifma, ifma_link);
+       }
+       
+       /* Let BPF know the interface is detaching. */
+       bpfdetach(ifp);
        TAILQ_REMOVE(&ifnet, ifp, if_link);
        
        (*if_family->del_if)(ifp);
-
+       
        if (--if_family->refcnt == 0) {
-           if (if_family->shutdown)
-               (*if_family->shutdown)();
-           
-           TAILQ_REMOVE(&if_family_head, if_family, if_fam_next);
-           FREE(if_family, M_IFADDR);
+               if (if_family->shutdown)
+                       (*if_family->shutdown)();
+               
+               TAILQ_REMOVE(&if_family_head, if_family, if_fam_next);
+               FREE(if_family, M_IFADDR);
        }
-
-        dlil_post_msg(ifp, KEV_DL_SUBCLASS, KEV_DL_IF_DETACHED, 0, 0);
-       splx(s);
+       
+       dlil_post_msg(ifp, KEV_DL_SUBCLASS, KEV_DL_IF_DETACHED, 0, 0);
        thread_funnel_set(network_flock, funnel_state);
        return 0;
-    }
-    else
-    {
-        dlil_post_msg(ifp, KEV_DL_SUBCLASS, KEV_DL_IF_DETACHING, 0, 0);
-       splx(s);
-       thread_funnel_set(network_flock, funnel_state);
-       return DLIL_WAIT_FOR_FREE;
-    }
 }
 
 
@@ -1516,6 +1541,24 @@ dlil_reg_if_modules(u_long  interface_family,
        thread_funnel_set(network_flock, funnel_state);
        return EINVAL;
     }
+    
+    /*
+     * The following is a gross hack to keep from breaking
+     * Vicomsoft's internet gateway on Jaguar. Vicomsoft
+     * does not zero the reserved fields in dlil_ifmod_reg_str.
+     * As a result, we have to zero any function that used to
+     * be reserved fields at the time Vicomsoft built their
+     * kext. Radar #2974305
+     */
+    if (ifmod->reserved[0] != 0 || ifmod->reserved[1] != 0 || ifmod->reserved[2]) {
+       if (interface_family == 123) {  /* Vicom */
+                       ifmod->init_if = 0;
+               } else {
+                       splx(s);
+                       thread_funnel_set(network_flock, funnel_state);
+                       return EINVAL;
+               }
+    }
 
     if_family = (struct if_family_str *) _MALLOC(sizeof(struct if_family_str), M_IFADDR, M_WAITOK);
     if (!if_family) {
@@ -1531,6 +1574,7 @@ dlil_reg_if_modules(u_long  interface_family,
     if_family->shutdown                = ifmod->shutdown;
     if_family->add_if          = ifmod->add_if;
     if_family->del_if          = ifmod->del_if;
+    if_family->init_if         = ifmod->init_if;
     if_family->add_proto       = ifmod->add_proto;
     if_family->del_proto       = ifmod->del_proto;
     if_family->ifmod_ioctl      = ifmod->ifmod_ioctl;
@@ -1546,7 +1590,7 @@ dlil_reg_if_modules(u_long  interface_family,
 int dlil_dereg_if_modules(u_long interface_family)
 {
     struct if_family_str  *if_family;
-    int s;
+    int s, ret = 0;
     boolean_t funnel_state;
 
     funnel_state = thread_funnel_set(network_flock, TRUE);
@@ -1565,16 +1609,138 @@ int dlil_dereg_if_modules(u_long interface_family)
        TAILQ_REMOVE(&if_family_head, if_family, if_fam_next);
        FREE(if_family, M_IFADDR);
     }  
-    else
+    else {
        if_family->flags |= DLIL_SHUTDOWN;
+        ret = DLIL_WAIT_FOR_FREE;
+    }
 
     splx(s);
     thread_funnel_set(network_flock, funnel_state);
-    return 0;
+    return ret;
 }
                                            
            
 
+int
+dlil_reg_proto_module(u_long protocol_family, u_long  interface_family, 
+                   struct dlil_protomod_reg_str  *protomod_reg)
+{
+       struct proto_family_str *proto_family;
+       int s;
+       boolean_t funnel_state;
+
+
+       funnel_state = thread_funnel_set(network_flock, TRUE);
+       s = splnet();
+       if (find_proto_module(protocol_family, interface_family))  {
+               splx(s);
+               thread_funnel_set(network_flock, funnel_state);
+               return EEXIST;
+       }
+    
+       if (protomod_reg->reserved[0] != 0 || protomod_reg->reserved[1] != 0
+           || protomod_reg->reserved[2] != 0 || protomod_reg->reserved[3] !=0) {
+               splx(s);
+               thread_funnel_set(network_flock, funnel_state);
+               return EINVAL;
+       }
+
+       if (protomod_reg->attach_proto == NULL) {
+               splx(s);
+               thread_funnel_set(network_flock, funnel_state);
+               return EINVAL;
+       }
+
+       proto_family = (struct proto_family_str *) _MALLOC(sizeof(struct proto_family_str), M_IFADDR, M_WAITOK);
+       if (!proto_family) {
+               splx(s);
+               thread_funnel_set(network_flock, funnel_state);
+               return ENOMEM;
+       }
+
+       bzero(proto_family, sizeof(struct proto_family_str));
+       proto_family->proto_family      = protocol_family;
+       proto_family->if_family         = interface_family & 0xffff;
+       proto_family->attach_proto      = protomod_reg->attach_proto;
+       proto_family->detach_proto      = protomod_reg->detach_proto;
+
+       TAILQ_INSERT_TAIL(&proto_family_head, proto_family, proto_fam_next);
+       splx(s);
+       thread_funnel_set(network_flock, funnel_state);
+       return 0;
+}
+
+int dlil_dereg_proto_module(u_long protocol_family, u_long interface_family)
+{
+       struct proto_family_str  *proto_family;
+       int s, ret = 0;
+       boolean_t funnel_state;
+
+       funnel_state = thread_funnel_set(network_flock, TRUE);
+       s = splnet();
+       proto_family = find_proto_module(protocol_family, interface_family);
+       if (proto_family == 0) {
+               splx(s);
+               thread_funnel_set(network_flock, funnel_state);
+               return ENOENT;
+       }
+
+       TAILQ_REMOVE(&proto_family_head, proto_family, proto_fam_next);
+       FREE(proto_family, M_IFADDR);
+
+       splx(s);
+       thread_funnel_set(network_flock, funnel_state);
+       return ret;
+}
+
+int dlil_plumb_protocol(u_long protocol_family, struct ifnet *ifp, u_long *dl_tag)
+{
+       struct proto_family_str  *proto_family;
+       int s, ret = 0;
+       boolean_t funnel_state;
+
+       funnel_state = thread_funnel_set(network_flock, TRUE);
+       s = splnet();
+       proto_family = find_proto_module(protocol_family, ifp->if_family);
+       if (proto_family == 0) {
+               splx(s);
+               thread_funnel_set(network_flock, funnel_state);
+               return ENOENT;
+       }
+
+       ret = (*proto_family->attach_proto)(ifp, dl_tag);
+
+       splx(s);
+       thread_funnel_set(network_flock, funnel_state);
+       return ret;
+}
+
+
+int dlil_unplumb_protocol(u_long protocol_family, struct ifnet *ifp)
+{
+       struct proto_family_str  *proto_family;
+       int s, ret = 0;
+       u_long tag;
+       boolean_t funnel_state;
+
+       funnel_state = thread_funnel_set(network_flock, TRUE);
+       s = splnet();
+
+       ret = dlil_find_dltag(ifp->if_family, ifp->if_unit, protocol_family, &tag);
+
+       if (ret == 0) {
+               proto_family = find_proto_module(protocol_family, ifp->if_family);
+               if (proto_family && proto_family->detach_proto)
+                       ret = (*proto_family->detach_proto)(ifp, tag);
+               else
+                       ret = dlil_detach_protocol(tag);
+       }
+    
+       splx(s);
+       thread_funnel_set(network_flock, funnel_state);
+       return ret;
+}
+                                                   
 
 
 /*
@@ -1601,12 +1767,9 @@ dlil_inject_if_input(struct mbuf *m, char *frame_header, u_long from_id)
     struct dlil_filterq_head    *fhead;
     int                                 match_found;
 
-
     dlil_stats.inject_if_in1++;
-    if (from_id > MAX_DLIL_FILTERS)
-       return ERANGE;
 
-    if (dlil_filters[from_id].type != DLIL_IF_FILTER)
+    if (from_id >= dlil_filters_nb || dlil_filters[from_id].type != DLIL_IF_FILTER)
        return ENOENT;
 
     ifp = dlil_filters[from_id].ifp;
@@ -1739,19 +1902,14 @@ dlil_inject_pr_input(struct mbuf *m, char *frame_header, u_long from_id)
     struct if_proto             *ifproto = 0;
     int                                 match_found;
     struct ifnet                *ifp;
-    
 
     dlil_stats.inject_pr_in1++;
-    if (from_id > MAX_DLIL_FILTERS)
-       return ERANGE;
-
-    if (dlil_filters[from_id].type != DLIL_PR_FILTER)
+    if (from_id >= dlil_filters_nb || dlil_filters[from_id].type != DLIL_PR_FILTER)
        return ENOENT;
 
     ifproto = dlil_filters[from_id].proto;
     ifp          = dlil_filters[from_id].ifp;
 
-
 /*
  * Call any attached protocol filters.
  */
@@ -1813,7 +1971,6 @@ dlil_inject_pr_output(struct mbuf         *m,
     int                                 match_found;
     u_long                      dl_tag;
 
-
     dlil_stats.inject_pr_out1++;
     if (raw == 0) { 
        if (frame_type)
@@ -1827,15 +1984,11 @@ dlil_inject_pr_output(struct mbuf               *m,
            return EINVAL;
     }
 
-    if (from_id > MAX_DLIL_FILTERS)
-       return ERANGE;
-
-    if (dlil_filters[from_id].type != DLIL_PR_FILTER)
+    if (from_id >= dlil_filters_nb || dlil_filters[from_id].type != DLIL_PR_FILTER)
        return ENOENT;
 
     ifp          = dlil_filters[from_id].ifp;
     dl_tag = dlil_filters[from_id].proto->dl_tag;
-    
 
     frame_type    = frame_type_buffer;
     dst_linkaddr   = dst_linkaddr_buffer;
@@ -1888,10 +2041,11 @@ dlil_inject_pr_output(struct mbuf               *m,
 #if BRIDGE
     if (do_bridge) {
        struct mbuf *m0 = m ;
+       struct ether_header *eh = mtod(m, struct ether_header *);
        
        if (m->m_pkthdr.rcvif)
            m->m_pkthdr.rcvif = NULL ;
-       ifp = bridge_dst_lookup(m);
+       ifp = bridge_dst_lookup(eh);
        bdg_forward(&m0, ifp);
        if (m0)
            m_freem(m0);
@@ -1954,12 +2108,8 @@ dlil_inject_if_output(struct mbuf *m, u_long from_id)
     struct dlil_filterq_head    *fhead;
     int                                 match_found;
 
-
     dlil_stats.inject_if_out1++;
-    if (from_id > MAX_DLIL_FILTERS)
-       return ERANGE;
-
-    if (dlil_filters[from_id].type != DLIL_IF_FILTER)
+    if (from_id > dlil_filters_nb || dlil_filters[from_id].type != DLIL_IF_FILTER)
        return ENOENT;
 
     ifp = dlil_filters[from_id].ifp;
@@ -2010,3 +2160,127 @@ dlil_inject_if_output(struct mbuf *m, u_long from_id)
     else 
        return retval;
 }
+
+static
+int dlil_recycle_ioctl(struct ifnet *ifnet_ptr, u_long ioctl_code, void *ioctl_arg)
+{
+
+    return EOPNOTSUPP;
+}
+
+static
+int dlil_recycle_output(struct ifnet *ifnet_ptr, struct mbuf *m)
+{
+
+    m_freem(m);
+    return 0;
+}
+
+static
+int dlil_recycle_free(struct ifnet *ifnet_ptr)
+{
+    return 0;
+}
+
+static
+int dlil_recycle_set_bpf_tap(struct ifnet *ifp, int mode, 
+                       int (*bpf_callback)(struct ifnet *, struct mbuf *))
+{
+    /* XXX not sure what to do here */
+    return 0;
+}
+
+int dlil_if_acquire(u_long family, void *uniqueid, size_t uniqueid_len, 
+                       struct ifnet **ifp)
+{
+    struct ifnet       *ifp1 = NULL;
+    struct dlil_ifnet  *dlifp1 = NULL;
+    int        s, ret = 0;
+    boolean_t  funnel_state;
+
+    funnel_state = thread_funnel_set(network_flock, TRUE);
+    s = splnet();
+
+    TAILQ_FOREACH(dlifp1, &dlil_ifnet_head, dl_if_link) {
+        
+        ifp1 = (struct ifnet *)dlifp1;
+            
+               if (ifp1->if_family == family)  {
+        
+            /* same uniqueid and same len or no unique id specified */
+            if ((uniqueid_len == dlifp1->if_uniqueid_len)
+                && !bcmp(uniqueid, dlifp1->if_uniqueid, uniqueid_len)) {
+                
+                               /* check for matching interface in use */
+                               if (ifp1->if_eflags & IFEF_INUSE) {
+                                       if (uniqueid_len) {
+                                               ret = EBUSY;
+                                               goto end;
+                                       }
+                               }
+                               else {
+       
+                                       ifp1->if_eflags |= (IFEF_INUSE + IFEF_REUSE);
+                                       *ifp = ifp1;
+                                       goto end;
+               }
+            }
+        }
+    }
+
+    /* no interface found, allocate a new one */
+    MALLOC(dlifp1, struct dlil_ifnet *, sizeof(*dlifp1), M_NKE, M_WAITOK);
+    if (dlifp1 == 0) {
+        ret = ENOMEM;
+        goto end;
+    }
+    
+    bzero(dlifp1, sizeof(*dlifp1));
+    
+    if (uniqueid_len) {
+        MALLOC(dlifp1->if_uniqueid, void *, uniqueid_len, M_NKE, M_WAITOK);
+        if (dlifp1->if_uniqueid == 0) {
+            FREE(dlifp1, M_NKE);
+            ret = ENOMEM;
+           goto end;
+        }
+        bcopy(uniqueid, dlifp1->if_uniqueid, uniqueid_len);
+        dlifp1->if_uniqueid_len = uniqueid_len;
+    }
+
+    ifp1 = (struct ifnet *)dlifp1;
+    ifp1->if_eflags |= IFEF_INUSE;
+
+    TAILQ_INSERT_TAIL(&dlil_ifnet_head, dlifp1, dl_if_link);
+     
+     *ifp = ifp1;
+
+end:
+
+    splx(s);
+    thread_funnel_set(network_flock, funnel_state);
+    return ret;
+}
+
+void dlil_if_release(struct ifnet *ifp)
+{
+    struct dlil_ifnet  *dlifp = (struct dlil_ifnet *)ifp;
+    int        s;
+    boolean_t  funnel_state;
+
+    funnel_state = thread_funnel_set(network_flock, TRUE);
+    s = splnet();
+    
+    ifp->if_eflags &= ~IFEF_INUSE;
+    ifp->if_ioctl = dlil_recycle_ioctl;
+    ifp->if_output = dlil_recycle_output;
+    ifp->if_free = dlil_recycle_free;
+    ifp->if_set_bpf_tap = dlil_recycle_set_bpf_tap;
+
+    strncpy(dlifp->if_namestorage, ifp->if_name, IFNAMSIZ);
+    ifp->if_name = dlifp->if_namestorage;
+    
+    splx(s);
+    thread_funnel_set(network_flock, funnel_state);
+}
+