]> git.saurik.com Git - apple/xnu.git/blobdiff - bsd/net/if_gif.c
xnu-6153.141.1.tar.gz
[apple/xnu.git] / bsd / net / if_gif.c
index aa0d3df0a42e96a6015853f867769d89a82809df..9f9e6c574e74d9309de9e052002b133161edd7e2 100644 (file)
@@ -1,25 +1,32 @@
 /*
- * Copyright (c) 2000 Apple Computer, Inc. All rights reserved.
+ * Copyright (c) 2000-2020 Apple Inc. All rights reserved.
  *
- * @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.
- * 
- * This Original Code and all software distributed under the License are
- * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * @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 OR NON-INFRINGEMENT.  Please see the
- * License for the specific language governing rights and limitations
- * under the License.
- * 
- * @APPLE_LICENSE_HEADER_END@
+ * 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@
  */
-/*     $KAME: if_gif.c,v 1.15 2000/02/22 14:01:46 itojun Exp $ */
+/* $FreeBSD: src/sys/net/if_gif.c,v 1.4.2.6 2001/07/24 19:10:18 brooks Exp $ */
+/* $KAME: if_gif.c,v 1.47 2001/05/01 05:28:42 itojun Exp $ */
 
 /*
  * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  */
-
 /*
- * gif.c
+ * 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.
  */
-#if BSD310
-#include "opt_inet.h"
-#endif
 
 #include <sys/param.h>
 #include <sys/systm.h>
 #include <sys/errno.h>
 #include <sys/time.h>
 #include <sys/syslog.h>
+#include <sys/protosw.h>
 #include <kern/cpu_number.h>
+#include <kern/zalloc.h>
 
 #include <net/if.h>
 #include <net/if_types.h>
-#include <net/netisr.h>
 #include <net/route.h>
 #include <net/bpf.h>
+#include <net/kpi_protocol.h>
+#include <net/kpi_interface.h>
+#include <net/init.h>
 
-#if    INET
 #include <netinet/in.h>
 #include <netinet/in_systm.h>
-#include <netinet/in_var.h>
 #include <netinet/ip.h>
+#if     INET
+#include <netinet/in_var.h>
 #include <netinet/in_gif.h>
-#endif /* INET */
+#include <netinet/ip_var.h>
+#endif  /* INET */
 
 #if INET6
-#ifndef INET
-#include <netinet/in.h>
-#endif
 #include <netinet6/in6_var.h>
 #include <netinet/ip6.h>
 #include <netinet6/ip6_var.h>
 #include <net/dlil.h>
 #include <net/if_gif.h>
 
-#include "gif.h"
-#include "bpfilter.h"
-
 #include <net/net_osdep.h>
 
-#if NGIF > 0
+#if CONFIG_MACF_NET
+#include <security/mac_framework.h>
+#endif
 
-void gifattach __P((void *));
-int gif_pre_output __P((struct ifnet *, register struct mbuf **, struct sockaddr *,
-       register struct rtentry *, char *, char *, u_long));
+#define GIFNAME         "gif"
+#define GIFDEV          "if_gif"
 
-/*
- * gif global variable definitions
- */
-int ngif = NGIF;               /* number of interfaces */
-struct gif_softc *gif = 0;
-static struct if_proto *gif_array[NGIF];
-static gif_count = 0 ;
-#ifndef MAX_GIF_NEST
-/*
- * This macro controls the upper limitation on nesting of gif tunnels.
- * Since, setting a large value to this macro with a careless configuration
- * may introduce system crash, we don't allow any nestings by default.
- * If you need to configure nested gif tunnels, you can define this macro
- * in your kernel configuration file. However, if you do so, please be
- * careful to configure the tunnels so that it won't make a loop.
- */
-#define MAX_GIF_NEST 1
+#define GIF_MAXUNIT     IF_MAXUNIT
+#define GIF_ZONE_MAX_ELEM       MIN(IFNETS_MAX, GIF_MAXUNIT)
+
+/* gif lock variables */
+static lck_grp_t        *gif_mtx_grp;
+static lck_grp_attr_t   *gif_mtx_grp_attr;
+static lck_attr_t       *gif_mtx_attr;
+decl_lck_mtx_data(static, gif_mtx_data);
+static lck_mtx_t        *gif_mtx = &gif_mtx_data;
+
+TAILQ_HEAD(gifhead, gif_softc) gifs = TAILQ_HEAD_INITIALIZER(gifs);
+
+static int gif_encapcheck(const struct mbuf *, int, int, void *);
+static errno_t gif_output(ifnet_t ifp, mbuf_t m);
+static errno_t gif_input(ifnet_t ifp, protocol_family_t protocol_family,
+    mbuf_t m, char *frame_header);
+static errno_t gif_ioctl(ifnet_t ifp, u_long cmd, void *data);
+
+static int ngif = 0;            /* number of interfaces */
+
+#if INET
+static struct protosw in_gif_protosw =
+{
+       .pr_type =              SOCK_RAW,
+       .pr_protocol =          0, /* IPPROTO_IPV[46] */
+       .pr_flags =             PR_ATOMIC | PR_ADDR,
+       .pr_input =             in_gif_input,
+       .pr_usrreqs =           &rip_usrreqs,
+       .pr_unlock =            rip_unlock,
+};
+#endif
+#if INET6
+static struct ip6protosw in6_gif_protosw =
+{
+       .pr_type =              SOCK_RAW,
+       .pr_protocol =          0, /* IPPROTO_IPV[46] */
+       .pr_flags =             PR_ATOMIC | PR_ADDR,
+       .pr_input =             in6_gif_input,
+       .pr_usrreqs =           &rip6_usrreqs,
+       .pr_unlock =            rip_unlock,
+};
 #endif
-static int max_gif_nesting = MAX_GIF_NEST;
 
+static int gif_remove(struct ifnet *);
+static int gif_clone_create(struct if_clone *, uint32_t, void *);
+static int gif_clone_destroy(struct ifnet *);
+static void gif_delete_tunnel(struct gif_softc *);
+static void gif_detach(struct ifnet *);
 
+static struct if_clone gif_cloner =
+    IF_CLONE_INITIALIZER(GIFNAME, gif_clone_create, gif_clone_destroy,
+    0, GIF_MAXUNIT, GIF_ZONE_MAX_ELEM, sizeof(struct gif_softc));
+/*
+ * Theory of operation: initially, one gif interface is created.
+ * Any time a gif interface is configured, if there are no other
+ * unconfigured gif interfaces, a new gif interface is created.
+ * BSD uses the clone mechanism to dynamically create more
+ * gif interfaces.
+ *
+ * We have some extra glue to support DLIL.
+ */
 
-#if 0
-int gif_demux(ifp, m, frame_header, proto)
-    struct ifnet *ifp;
-    struct mbuf  *m;
-    char         *frame_header;
-    struct if_proto **proto;
+/* GIF interface module support */
+static int
+gif_demux(
+       ifnet_t ifp,
+       __unused mbuf_t m,
+       __unused char *frame_header,
+       protocol_family_t *protocol_family)
 {
-    int i;
-    return 0;
+       struct gif_softc *sc = ifnet_softc(ifp);
+
+       GIF_LOCK(sc);
+       /* Only one protocol may be attached to a gif interface. */
+       *protocol_family = sc->gif_proto;
+       GIF_UNLOCK(sc);
+
+       return 0;
 }
 
-int gif_framer(ifp, m, dest, dest_linkaddr, frame_type)
-    struct ifnet    *ifp;
-    struct mbuf     **m;
-    struct sockaddr *dest;
-    char            *dest_linkaddr;
-    char            *frame_type;
-                
-{                
-    char  *to_ptr;
-                 
-        return 0;
+static errno_t
+gif_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 gif_softc *sc = ifnet_softc(ifp);
+
+       GIF_LOCK(sc);
+       if (sc->gif_proto != 0) {
+               printf("gif_add_proto: request add_proto for gif%d\n",
+                   ifnet_unit(ifp));
+       }
+
+       sc->gif_proto = protocol_family;
+       GIF_UNLOCK(sc);
+
+       return 0;
 }
-#endif 
-static
-int  gif_add_if(struct ifnet *ifp) 
-{                   
-    ifp->if_demux  = 0;
-    ifp->if_framer = 0;
-    return 0;
-}       
-        
-static
-int  gif_del_if(struct ifnet *ifp)
-{       
-    return 0;
-}   
-
-static
-int  gif_add_proto(struct ddesc_head_str *desc_head, struct if_proto *proto, u_long dl_tag)
-{   
-    int i;
-
-    for (i=0; i < gif_count; i++)
-        if (gif_array[i] == 0) {
-            gif_array[gif_count] = proto;
-            return 0;
-        }
-
-    if ((i == gif_count) && (gif_count == NGIF))
-       panic("gif_add_proto -- Too many attachments\n");
-
-    gif_array[gif_count++] = proto;
-
-    return (0);
+
+static errno_t
+gif_del_proto(
+       ifnet_t ifp,
+       protocol_family_t protocol_family)
+{
+       struct gif_softc *sc = ifnet_softc(ifp);
+
+       GIF_LOCK(sc);
+       if (sc->gif_proto == protocol_family) {
+               sc->gif_proto = 0;
+       }
+       GIF_UNLOCK(sc);
+
+       return 0;
 }
 
-static
-int  gif_del_proto(struct if_proto *proto, u_long dl_tag)
-{       
-    int i;
+/* Glue code to attach inet to a gif interface through DLIL */
+static errno_t
+gif_attach_proto_family(
+       ifnet_t ifp,
+       protocol_family_t protocol_family)
+{
+       struct ifnet_attach_proto_param reg;
+       errno_t stat;
+
+       bzero(&reg, sizeof(reg));
+       reg.input = gif_input;
 
-    for (i=0; i < gif_count; i++)
-        if (gif_array[i] == proto) {
-            gif_array[i] = 0;   
-            return 0;
-        }
+       stat = ifnet_attach_protocol(ifp, protocol_family, &reg);
+       if (stat && stat != EEXIST) {
+               printf("gif_attach_proto_family can't attach interface  \
+                   fam=%d\n", protocol_family);
+       }
 
-    return ENOENT;
+       return stat;
 }
 
-int gif_shutdown()
+/* Function to setup the first gif interface */
+void
+gif_init(void)
 {
-    return 0;
+       errno_t result;
+
+       /* Initialize the list of interfaces */
+       TAILQ_INIT(&gifs);
+
+       /* Initialize the gif global lock */
+       gif_mtx_grp_attr = lck_grp_attr_alloc_init();
+       gif_mtx_grp = lck_grp_alloc_init("gif", gif_mtx_grp_attr);
+       gif_mtx_attr = lck_attr_alloc_init();
+       lck_mtx_init(gif_mtx, gif_mtx_grp, gif_mtx_attr);
+
+       /* Register protocol registration functions */
+       result = proto_register_plumber(PF_INET, APPLE_IF_FAM_GIF,
+           gif_attach_proto_family, NULL);
+       if (result != 0) {
+               printf("proto_register_plumber failed for AF_INET error=%d\n",
+                   result);
+       }
+
+       result = proto_register_plumber(PF_INET6, APPLE_IF_FAM_GIF,
+           gif_attach_proto_family, NULL);
+       if (result != 0) {
+               printf("proto_register_plumber failed for AF_INET6 error=%d\n",
+                   result);
+       }
+
+       result = if_clone_attach(&gif_cloner);
+       if (result != 0) {
+               panic("%s: if_clone_attach() failed, error %d\n", __func__, result);
+       }
+
+       gif_clone_create(&gif_cloner, 0, NULL);
 }
 
-void gif_reg_if_mods()
+static errno_t
+gif_set_bpf_tap(
+       ifnet_t ifp,
+       bpf_tap_mode mode,
+       bpf_packet_func callback)
 {
-     struct dlil_ifmod_reg_str  gif_ifmod;
+       struct gif_softc *sc = ifnet_softc(ifp);
 
-     gif_ifmod.add_if = gif_add_if;
-     gif_ifmod.del_if = gif_del_if;
-     gif_ifmod.add_proto = gif_add_proto;
-     gif_ifmod.del_proto = gif_del_proto;
-     gif_ifmod.ifmod_ioctl = 0;
-     gif_ifmod.shutdown    = gif_shutdown;
+       GIF_LOCK(sc);
+       sc->tap_mode = mode;
+       sc->tap_callback = callback;
+       GIF_UNLOCK(sc);
 
-    if (dlil_reg_if_modules(APPLE_IF_FAM_GIF, &gif_ifmod))
-        panic("Couldn't register gif modules\n");
+       return 0;
+}
 
-} 
+static void
+gif_detach(struct ifnet *ifp)
+{
+       struct gif_softc *sc = ifp->if_softc;
+       lck_mtx_destroy(&sc->gif_lock, gif_mtx_grp);
+       if_clone_softc_deallocate(&gif_cloner, sc);
+       ifp->if_softc = NULL;
+       (void) ifnet_release(ifp);
+}
 
-u_long  gif_attach_inet(struct ifnet *ifp)
+static int
+gif_clone_create(struct if_clone *ifc, uint32_t unit, __unused void *params)
 {
-    struct dlil_proto_reg_str   reg;
-    struct dlil_demux_desc      desc;
-    u_long                      dl_tag=0;
-    short native=0;     
-    int   stat;  
-    int i;      
-        
-    for (i=0; i < gif_count; i++) {
-        if (gif_array[i] && (gif_array[i]->ifp == ifp) &&
-            (gif_array[i]->protocol_family == PF_INET)) {
+       struct gif_softc *sc = NULL;
+       struct ifnet_init_eparams gif_init_params;
+       errno_t error = 0;
+
+       lck_mtx_lock(gif_mtx);
+
+       /* Can't create more than GIF_MAXUNIT */
+       if (ngif >= GIF_MAXUNIT) {
+               error = ENXIO;
+               goto done;
+       }
+
+       sc = if_clone_softc_allocate(&gif_cloner);
+       if (sc == NULL) {
+               log(LOG_ERR, "gif_clone_create: failed to allocate gif%d\n",
+                   unit);
+               error = ENOBUFS;
+               goto done;
+       }
+
+       /* use the interface name as the unique id for ifp recycle */
+       snprintf(sc->gif_ifname, sizeof(sc->gif_ifname), "%s%d",
+           ifc->ifc_name, unit);
+
+       lck_mtx_init(&sc->gif_lock, gif_mtx_grp, gif_mtx_attr);
+
+       bzero(&gif_init_params, sizeof(gif_init_params));
+       gif_init_params.ver = IFNET_INIT_CURRENT_VERSION;
+       gif_init_params.len = sizeof(gif_init_params);
+       gif_init_params.flags = IFNET_INIT_LEGACY;
+       gif_init_params.uniqueid = sc->gif_ifname;
+       gif_init_params.uniqueid_len = strlen(sc->gif_ifname);
+       gif_init_params.name = GIFNAME;
+       gif_init_params.unit = unit;
+       gif_init_params.type = IFT_GIF;
+       gif_init_params.family = IFNET_FAMILY_GIF;
+       gif_init_params.output = gif_output;
+       gif_init_params.demux = gif_demux;
+       gif_init_params.add_proto = gif_add_proto;
+       gif_init_params.del_proto = gif_del_proto;
+       gif_init_params.softc = sc;
+       gif_init_params.ioctl = gif_ioctl;
+       gif_init_params.set_bpf_tap = gif_set_bpf_tap;
+       gif_init_params.detach = gif_detach;
+
+       error = ifnet_allocate_extended(&gif_init_params, &sc->gif_if);
+       if (error != 0) {
+               printf("gif_clone_create, ifnet_allocate failed - %d\n", error);
+               if_clone_softc_deallocate(&gif_cloner, sc);
+               error = ENOBUFS;
+               goto done;
+       }
+
+       sc->encap_cookie4 = sc->encap_cookie6 = NULL;
+#if INET
+       sc->encap_cookie4 = encap_attach_func(AF_INET, -1,
+           gif_encapcheck, &in_gif_protosw, sc);
+       if (sc->encap_cookie4 == NULL) {
+               printf("%s: unable to attach encap4\n", if_name(sc->gif_if));
+               ifnet_release(sc->gif_if);
+               if_clone_softc_deallocate(&gif_cloner, sc);
+               error = ENOBUFS;
+               goto done;
+       }
+#endif
+#if INET6
+       sc->encap_cookie6 = encap_attach_func(AF_INET6, -1,
+           gif_encapcheck, (struct protosw *)&in6_gif_protosw, sc);
+       if (sc->encap_cookie6 == NULL) {
+               if (sc->encap_cookie4) {
+                       encap_detach(sc->encap_cookie4);
+                       sc->encap_cookie4 = NULL;
+               }
+               printf("%s: unable to attach encap6\n", if_name(sc->gif_if));
+               ifnet_release(sc->gif_if);
+               if_clone_softc_deallocate(&gif_cloner, sc);
+               error = ENOBUFS;
+               goto done;
+       }
+#endif
+       sc->gif_called = 0;
+       ifnet_set_mtu(sc->gif_if, GIF_MTU);
+       ifnet_set_flags(sc->gif_if, IFF_POINTOPOINT | IFF_MULTICAST, 0xffff);
 #if 0
-               kprintf("gif_attach for %s%d found dl_tag=%d\n", 
-                       ifp->if_name, ifp->if_unit, gif_array[i]->dl_tag);
+       /* turn off ingress filter */
+       sc->gif_if.if_flags  |= IFF_LINK2;
 #endif
-               return gif_array[i]->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            = gif_input;
-    reg.pre_output       = gif_pre_output;
-    reg.event            = 0;
-    reg.offer            = 0;
-    reg.ioctl            = gif_ioctl;
-    reg.default_proto    = 0;
-    reg.protocol_family  = PF_INET;
-
-    stat = dlil_attach_protocol(&reg, &dl_tag);
-    if (stat) {
-        panic("gif_attach_inet can't attach interface\n");
-    }
-
-    return dl_tag;
+       sc->gif_flags |= IFGIF_DETACHING;
+       error = ifnet_attach(sc->gif_if, NULL);
+       if (error != 0) {
+               printf("gif_clone_create - ifnet_attach failed - %d\n", error);
+               ifnet_release(sc->gif_if);
+               if (sc->encap_cookie4) {
+                       encap_detach(sc->encap_cookie4);
+                       sc->encap_cookie4 = NULL;
+               }
+               if (sc->encap_cookie6) {
+                       encap_detach(sc->encap_cookie6);
+                       sc->encap_cookie6 = NULL;
+               }
+               if_clone_softc_deallocate(&gif_cloner, sc);
+               goto done;
+       }
+#if CONFIG_MACF_NET
+       mac_ifnet_label_init(&sc->gif_if);
+#endif
+       bpfattach(sc->gif_if, DLT_NULL, sizeof(u_int));
+       sc->gif_flags &= ~IFGIF_DETACHING;
+       TAILQ_INSERT_TAIL(&gifs, sc, gif_link);
+       ngif++;
+done:
+       lck_mtx_unlock(gif_mtx);
+
+       return error;
 }
 
-void
-gifattach(dummy)
-       void *dummy;
+static int
+gif_remove(struct ifnet *ifp)
 {
-       register struct gif_softc *sc;
-       register int i;
-
-       gif_reg_if_mods(); /* DLIL modules */
-
-       gif = sc = _MALLOC (ngif * sizeof(struct gif_softc), M_DEVBUF, M_WAIT);
-       bzero(sc, ngif * sizeof(struct gif_softc));
-       for (i = 0; i < ngif; sc++, i++) {
-               sc->gif_if.if_name   = "gif";
-               sc->gif_if.if_unit   = i;
-               sc->gif_if.if_family = APPLE_IF_FAM_GIF;
-               sc->gif_if.if_mtu    = GIF_MTU;
-               sc->gif_if.if_flags  = IFF_POINTOPOINT | IFF_MULTICAST;
-               sc->gif_if.if_ioctl  = gif_ioctl;
-               sc->gif_if.if_output = NULL;
-               sc->gif_if.if_type   = IFT_GIF;
-               dlil_if_attach(&sc->gif_if);
-#if 0
-               kprintf("gifattach: Attaching gif%d sc=%x gif_if=%x\n", i, sc, &sc->gif_if);
-#endif
-#if NBPFILTER > 0
-#ifdef HAVE_OLD_BPF
-               bpfattach(&sc->gif_if, DLT_NULL, sizeof(u_int));
-#else
-               bpfattach(&sc->gif_if.if_bpf, &sc->gif_if, DLT_NULL, sizeof(u_int));
+       int error = 0;
+       struct gif_softc *sc = NULL;
+       const struct encaptab *encap_cookie4 = NULL;
+       const struct encaptab *encap_cookie6 = NULL;
+
+       lck_mtx_lock(gif_mtx);
+       sc = ifp->if_softc;
+
+       if (sc == NULL) {
+               error = EINVAL;
+               goto done;
+       }
+
+       GIF_LOCK(sc);
+       if (sc->gif_flags & IFGIF_DETACHING) {
+               error = EINVAL;
+               goto done;
+       }
+
+       sc->gif_flags |= IFGIF_DETACHING;
+       TAILQ_REMOVE(&gifs, sc, gif_link);
+       ngif--;
+
+       gif_delete_tunnel(sc);
+#ifdef INET6
+       encap_cookie6 = sc->encap_cookie6;
 #endif
+#ifdef INET
+       encap_cookie4 = sc->encap_cookie4;
 #endif
+done:
+       if (sc != NULL) {
+               GIF_UNLOCK(sc);
        }
+       lck_mtx_unlock(gif_mtx);
+
+       if (encap_cookie6 != NULL) {
+               error = encap_detach(encap_cookie6);
+               KASSERT(error == 0, ("gif_clone_destroy: Unexpected "
+                   "error detaching encap_cookie6"));
+       }
+
+       if (encap_cookie4 != NULL) {
+               error = encap_detach(encap_cookie4);
+               KASSERT(error == 0, ("gif_clone_destroy: Unexpected "
+                   "error detaching encap_cookie4"));
+       }
+
+       return error;
 }
 
-#ifdef __FreeBSD__
-PSEUDO_SET(gifattach, if_gif);
-#endif
+static int
+gif_clone_destroy(struct ifnet *ifp)
+{
+       int error = 0;
+
+       error = gif_remove(ifp);
+       if (error != 0) {
+               printf("gif_clone_destroy: gif remove failed %d\n", error);
+               return error;
+       }
 
-int
-gif_pre_output(ifp, m0, dst, rt, frame, address, dl_tag)
-       struct ifnet *ifp;
-       struct mbuf **m0;
-       struct sockaddr *dst;
-       struct rtentry *rt;     /* added in net2 */
-       char *frame;
-       char *address;
-       u_long dl_tag;
+       error = ifnet_set_flags(ifp, 0, IFF_UP);
+       if (error != 0) {
+               printf("gif_clone_destroy: ifnet_set_flags failed %d\n", error);
+       }
+
+       error = ifnet_detach(ifp);
+       if (error != 0) {
+               panic("gif_clone_destroy: ifnet_detach(%p) failed %d\n", ifp,
+                   error);
+       }
+       return 0;
+}
+
+static int
+gif_encapcheck(
+       const struct mbuf *m,
+       int off,
+       int proto,
+       void *arg)
 {
-       register struct gif_softc *sc = (struct gif_softc*)ifp;
-       register struct mbuf * m = *m0;
        int error = 0;
-       static int called = 0;  /* XXX: MUTEX */
+       struct ip ip;
+       struct gif_softc *sc;
 
-       /*
-        * gif may cause infinite recursion calls when misconfigured.
-        * We'll prevent this by introducing upper limit.
-        * XXX: this mechanism may introduce another problem about
-        *      mutual exclusion of the variable CALLED, especially if we
-        *      use kernel thread.
-        */
-       if (++called > max_gif_nesting) {
-               log(LOG_NOTICE,
-                   "gif_output: recursively called too many times(%d)\n",
-                   called);
-               m_freem(m);
-               error = EIO;    /* is there better errno? */
-               goto end;
+       sc = (struct gif_softc *)arg;
+       if (sc == NULL) {
+               return error;
        }
 
-       getmicrotime(&ifp->if_lastchange);
-       m->m_flags &= ~(M_BCAST|M_MCAST);
-       if (!(ifp->if_flags & IFF_UP) ||
-#if 0  
-           sc->gif_flags & GIFF_INUSE ||
-#endif
-           sc->gif_psrc == NULL || sc->gif_pdst == NULL) {
-               m_freem(m);
-               error = ENETDOWN;
-               printf("gif_output: packed discarded ENETDOWN\n");
-               goto end;
+       GIF_LOCK(sc);
+       if ((ifnet_flags(sc->gif_if) & IFF_UP) == 0) {
+               goto done;
+       }
+
+       /* no physical address */
+       if (!sc->gif_psrc || !sc->gif_pdst) {
+               goto done;
        }
 
-#if NBPFILTER > 0
-       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;
-               u_int af = dst->sa_family;
-
-               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);
+       switch (proto) {
+#if INET
+       case IPPROTO_IPV4:
+               break;
 #endif
+#if INET6
+       case IPPROTO_IPV6:
+               break;
+#endif
+       default:
+               goto done;
        }
+
+       mbuf_copydata((struct mbuf *)(size_t)m, 0, sizeof(ip), &ip);
+
+       switch (ip.ip_v) {
+#if INET
+       case 4:
+               if (sc->gif_psrc->sa_family != AF_INET ||
+                   sc->gif_pdst->sa_family != AF_INET) {
+                       goto done;
+               }
+               error = gif_encapcheck4(m, off, proto, arg);
 #endif
-       ifp->if_opackets++;     
-       ifp->if_obytes += m->m_pkthdr.len;
-#if 0
-       s = splnet();
-       sc->gif_flags |= GIFF_INUSE;
+#if INET6
+       case 6:
+               if (sc->gif_psrc->sa_family != AF_INET6 ||
+                   sc->gif_pdst->sa_family != AF_INET6) {
+                       goto done;
+               }
+               error = gif_encapcheck6(m, off, proto, arg);
 #endif
+       default:
+               goto done;
+       }
+done:
+       GIF_UNLOCK(sc);
+       return error;
+}
+
+static errno_t
+gif_output(
+       ifnet_t ifp,
+       mbuf_t m)
+{
+       struct gif_softc *sc = ifnet_softc(ifp);
+       struct sockaddr *gif_psrc;
+       struct sockaddr *gif_pdst;
+       int error = 0;
+
+       GIF_LOCK(sc);
+       gif_psrc = sc->gif_psrc;
+       gif_pdst = sc->gif_pdst;
+       GIF_UNLOCK(sc);
+
+       /*
+        * max_gif_nesting check used to live here. It doesn't anymore
+        * because there is no guaruntee that we won't be called
+        * concurrently from more than one thread.
+        */
+       m->m_flags &= ~(M_BCAST | M_MCAST);
+       if (!(ifnet_flags(ifp) & IFF_UP) ||
+           gif_psrc == NULL || gif_pdst == NULL) {
+               ifnet_touch_lastchange(ifp);
+               m_freem(m);     /* free it here not in dlil_output */
+               error = ENETDOWN;
+               goto end;
+       }
+
+       bpf_tap_out(ifp, 0, m, &sc->gif_proto, sizeof(sc->gif_proto));
+
+       GIF_LOCK(sc);
+
+       /* inner AF-specific encapsulation */
+
+       /* XXX should we check if our outer source is legal? */
+
+       /*
+        * Save the length as m may be free by the output functions
+        * as they call m_pullup
+        */
+       u_int32_t bytes_out = m->m_pkthdr.len;
 
+       /* dispatch to output logic based on outer AF */
        switch (sc->gif_psrc->sa_family) {
 #if INET
        case AF_INET:
-               error = in_gif_output(ifp, dst->sa_family, m, rt);
-               if (error) 
-                       printf("in_gif_output returned error=%d\n", error);
+               error = in_gif_output(ifp, sc->gif_proto, m, NULL);
                break;
 #endif
 #if INET6
        case AF_INET6:
-               error = in6_gif_output(ifp, dst->sa_family, m, rt);
-               if (error) 
-                       printf("in6_gif_output returned error=%d\n", error);
+               error = in6_gif_output(ifp, sc->gif_proto, m, NULL);
                break;
 #endif
        default:
-               m_freem(m);             
                error = ENETDOWN;
+               break;
        }
-#if 0
-       sc->gif_flags &= ~GIFF_INUSE;
-       splx(s);
-#endif
 
-  end:
-       called = 0;             /* reset recursion counter */
-       if (error) ifp->if_oerrors++;
-       return EJUSTRETURN;
+       GIF_UNLOCK(sc);
+end:
+       if (error) {
+               /* the mbuf was freed either by in_gif_output or in here */
+               ifnet_stat_increment_out(ifp, 0, 0, 1);
+       } else {
+               ifnet_stat_increment_out(ifp, 1, bytes_out, 0);
+       }
+       if (error == 0) {
+               error = EJUSTRETURN; /* if no error, packet got sent already */
+       }
+       return error;
 }
 
-void
-gif_input(m, af, gifp)
-       struct mbuf *m;
-       int af;
-       struct ifnet *gifp;
+/*
+ * gif_input is the input handler for IP and IPv6 attached to gif
+ */
+static errno_t
+gif_input(
+       ifnet_t ifp,
+       protocol_family_t protocol_family,
+       mbuf_t m,
+       __unused char *frame_header)
 {
-       int s, isr;
-       register struct ifqueue *ifq = 0;
+       struct gif_softc *sc = ifnet_softc(ifp);
 
-       if (gifp == NULL) {
-               /* just in case */
-               m_freem(m);
-               return;
-       }
-
-       if (m->m_pkthdr.rcvif)
-               m->m_pkthdr.rcvif = gifp;
-       
-#if NBPFILTER > 0
-       if (gifp->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;
-               u_int af = AF_INET6;
-               
-               m0.m_next = m;
-               m0.m_len = 4;
-               m0.m_data = (char *)&af;
-               
-#ifdef HAVE_OLD_BPF
-               bpf_mtap(gifp, &m0);
-#else
-               bpf_mtap(gifp->if_bpf, &m0);
-#endif
-       }
-#endif /*NBPFILTER > 0*/
+       bpf_tap_in(ifp, 0, m, &sc->gif_proto, sizeof(sc->gif_proto));
 
        /*
         * Put the packet to the network layer input queue according to the
@@ -465,230 +681,381 @@ gif_input(m, af, gifp)
         * it occurs more times than we thought, we may change the policy
         * again.
         */
-       switch (af) {
-#if INET
-       case AF_INET:
-               ifq = &ipintrq;
-               isr = NETISR_IP;
-               break;
-#endif
-#if INET6
-       case AF_INET6:
-               ifq = &ip6intrq;
-               isr = NETISR_IPV6;
-               break;
-#endif
-       default:
-               m_freem(m);
-               return;
-       }
-
-       s = splimp();
-       if (IF_QFULL(ifq)) {
-               IF_DROP(ifq);   /* update statistics */
+       int32_t pktlen = m->m_pkthdr.len;
+       if (proto_input(protocol_family, m) != 0) {
+               ifnet_stat_increment_in(ifp, 0, 0, 1);
                m_freem(m);
-               splx(s);
-               return;
+       } else {
+               ifnet_stat_increment_in(ifp, 1, pktlen, 0);
        }
-       IF_ENQUEUE(ifq, m);
-       /* we need schednetisr since the address family may change */
-       schednetisr(isr);
-       gifp->if_ipackets++;
-       gifp->if_ibytes += m->m_pkthdr.len;
-       splx(s);
 
-       return;
+       return 0;
 }
 
 /* XXX how should we handle IPv6 scope on SIOC[GS]IFPHYADDR? */
-int
-gif_ioctl(ifp, cmd, data)
-       struct ifnet *ifp;
-       u_long cmd;
-       caddr_t data;
+static errno_t
+gif_ioctl(
+       ifnet_t                 ifp,
+       u_long                  cmd,
+       void                    *data)
 {
-       struct gif_softc *sc  = (struct gif_softc*)ifp;
-       struct ifreq     *ifr = (struct ifreq*)data;
+       struct gif_softc *sc  = ifnet_softc(ifp);
+       struct ifreq *ifr = (struct ifreq *)data;
        int error = 0, size;
-       struct sockaddr *dst, *src;
-       int i;
+       struct sockaddr *dst = NULL, *src = NULL;
+       struct sockaddr *sa;
+       struct ifnet *ifp2;
        struct gif_softc *sc2;
-               
+
        switch (cmd) {
        case SIOCSIFADDR:
                break;
-               
+
        case SIOCSIFDSTADDR:
                break;
 
        case SIOCADDMULTI:
        case SIOCDELMULTI:
- /* Called from if_addmulti() with data == NULL if __FreeBSD__ >= 3 */
-#if !defined(__APPLE__)
-               switch (ifr->ifr_addr.sa_family) {
-#ifdef INET
-               case AF_INET:   /* IP supports Multicast */
-                       break;
-#endif /* INET */
-#ifdef INET6
-               case AF_INET6:  /* IP6 supports Multicast */
-                       break;
-#endif /* INET6 */
-               default:  /* Other protocols doesn't support Multicast */
-                       error = EAFNOSUPPORT;
-                       break;
-               }
-#endif /*not FreeBSD3*/
                break;
 
-#ifdef SIOCSIFMTU /* xxx */
-#ifndef __OpenBSD__
+#ifdef  SIOCSIFMTU /* xxx */
        case SIOCGIFMTU:
                break;
+
        case SIOCSIFMTU:
-               {
-#ifdef __bsdi__
-                       short mtu;
-                       mtu = *(short *)ifr->ifr_data;
-#else
-                       u_long mtu;
-                       mtu = ifr->ifr_mtu;
+       {
+               u_int32_t mtu;
+               mtu = ifr->ifr_mtu;
+               if (mtu < GIF_MTU_MIN || mtu > GIF_MTU_MAX) {
+                       return EINVAL;
+               }
+               ifnet_set_mtu(ifp, mtu);
+       }
+       break;
+#endif /* SIOCSIFMTU */
+
+       case SIOCSIFPHYADDR:
+#if INET6
+       case SIOCSIFPHYADDR_IN6_32:
+       case SIOCSIFPHYADDR_IN6_64:
+#endif /* INET6 */
+               switch (cmd) {
+#if INET
+               case SIOCSIFPHYADDR:
+                       src = (struct sockaddr *)
+                           &(((struct in_aliasreq *)data)->ifra_addr);
+                       dst = (struct sockaddr *)
+                           &(((struct in_aliasreq *)data)->ifra_dstaddr);
+                       break;
+#endif
+#if INET6
+               case SIOCSIFPHYADDR_IN6_32: {
+                       struct in6_aliasreq_32 *ifra_32 =
+                           (struct in6_aliasreq_32 *)data;
+
+                       src = (struct sockaddr *)&ifra_32->ifra_addr;
+                       dst = (struct sockaddr *)&ifra_32->ifra_dstaddr;
+                       break;
+               }
+
+               case SIOCSIFPHYADDR_IN6_64: {
+                       struct in6_aliasreq_64 *ifra_64 =
+                           (struct in6_aliasreq_64 *)data;
+
+                       src = (struct sockaddr *)&ifra_64->ifra_addr;
+                       dst = (struct sockaddr *)&ifra_64->ifra_dstaddr;
+                       break;
+               }
 #endif
-                       if (mtu < GIF_MTU_MIN || mtu > GIF_MTU_MAX) {
-                               return (EINVAL);
+               }
+
+               /* sa_family must be equal */
+               if (src->sa_family != dst->sa_family) {
+                       return EINVAL;
+               }
+
+               /* validate sa_len */
+               switch (src->sa_family) {
+#if INET
+               case AF_INET:
+                       if (src->sa_len != sizeof(struct sockaddr_in)) {
+                               return EINVAL;
+                       }
+                       break;
+#endif
+#if INET6
+               case AF_INET6:
+                       if (src->sa_len != sizeof(struct sockaddr_in6)) {
+                               return EINVAL;
                        }
-                       ifp->if_mtu = mtu;
+                       break;
+#endif
+               default:
+                       return EAFNOSUPPORT;
                }
-               break;
+               switch (dst->sa_family) {
+#if INET
+               case AF_INET:
+                       if (dst->sa_len != sizeof(struct sockaddr_in)) {
+                               return EINVAL;
+                       }
+                       break;
 #endif
-#endif /* SIOCSIFMTU */
+#if INET6
+               case AF_INET6:
+                       if (dst->sa_len != sizeof(struct sockaddr_in6)) {
+                               return EINVAL;
+                       }
+                       break;
+#endif
+               default:
+                       return EAFNOSUPPORT;
+               }
 
-       case SIOCSIFPHYADDR:
+               /* check sa_family looks sane for the cmd */
+               switch (cmd) {
+               case SIOCSIFPHYADDR:
+                       if (src->sa_family == AF_INET) {
+                               break;
+                       }
+                       return EAFNOSUPPORT;
 #if INET6
-       case SIOCSIFPHYADDR_IN6:
+               case SIOCSIFPHYADDR_IN6_32:
+               case SIOCSIFPHYADDR_IN6_64:
+                       if (src->sa_family == AF_INET6) {
+                               break;
+                       }
+                       return EAFNOSUPPORT;
 #endif /* INET6 */
-               /* can't configure same pair of address onto two gif */
-               src = (struct sockaddr *)
-                       &(((struct in_aliasreq *)data)->ifra_addr);
-               dst = (struct sockaddr *)
-                       &(((struct in_aliasreq *)data)->ifra_dstaddr);
-               for (i = 0; i < ngif; i++) {
-                       sc2 = gif + i;
-                       if (sc2 == sc)
+               }
+
+#define GIF_ORDERED_LOCK(sc, sc2)       \
+       if (sc < sc2) {                 \
+               GIF_LOCK(sc);           \
+               GIF_LOCK(sc2);          \
+       } else {                        \
+               GIF_LOCK(sc2);          \
+               GIF_LOCK(sc);           \
+       }
+
+#define GIF_ORDERED_UNLOCK(sc, sc2)     \
+       if (sc > sc2) {                 \
+               GIF_UNLOCK(sc);         \
+               GIF_UNLOCK(sc2);        \
+       } else {                        \
+               GIF_UNLOCK(sc2);        \
+               GIF_UNLOCK(sc);         \
+       }
+
+               ifnet_head_lock_shared();
+               TAILQ_FOREACH(ifp2, &ifnet_head, if_link) {
+                       if (strcmp(ifnet_name(ifp2), GIFNAME) != 0) {
+                               continue;
+                       }
+                       sc2 = ifnet_softc(ifp2);
+                       if (sc2 == sc) {
                                continue;
-                       if (!sc2->gif_pdst || !sc2->gif_psrc)
+                       }
+                       /* lock sc and sc2 in increasing order of ifnet index */
+                       GIF_ORDERED_LOCK(sc, sc2);
+                       if (!sc2->gif_pdst || !sc2->gif_psrc) {
+                               GIF_ORDERED_UNLOCK(sc, sc2);
                                continue;
-                       if (sc2->gif_pdst->sa_family == dst->sa_family &&
-                           sc2->gif_pdst->sa_len == dst->sa_family &&
-                           bcmp(sc2->gif_pdst, dst, dst->sa_len) == 0 &&
-                           sc2->gif_psrc->sa_family == src->sa_family &&
-                           sc2->gif_psrc->sa_len == src->sa_family &&
+                       }
+                       if (sc2->gif_pdst->sa_family != dst->sa_family ||
+                           sc2->gif_pdst->sa_len != dst->sa_len ||
+                           sc2->gif_psrc->sa_family != src->sa_family ||
+                           sc2->gif_psrc->sa_len != src->sa_len) {
+                               GIF_ORDERED_UNLOCK(sc, sc2);
+                               continue;
+                       }
+#ifndef XBONEHACK
+                       /* can't configure same pair of address onto two gifs */
+                       if (bcmp(sc2->gif_pdst, dst, dst->sa_len) == 0 &&
                            bcmp(sc2->gif_psrc, src, src->sa_len) == 0) {
+                               GIF_ORDERED_UNLOCK(sc, sc2);
                                error = EADDRNOTAVAIL;
+                               ifnet_head_done();
                                goto bad;
                        }
-               }
+#endif
 
-               switch (ifr->ifr_addr.sa_family) {
-#if INET
-               case AF_INET:
-                       return in_gif_ioctl(ifp, cmd, data);
-#endif /* INET */
+                       /* can't configure multiple multi-dest interfaces */
+#define multidest(x) \
+       (((struct sockaddr_in *)(void *)(x))->sin_addr.s_addr == INADDR_ANY)
 #if INET6
-               case AF_INET6:
-                       return in6_gif_ioctl(ifp, cmd, data);
-#endif /* INET6 */
-               default:
-                       error = EPROTOTYPE;
-                       goto bad;
-                       break;
+#define multidest6(x) \
+       (IN6_IS_ADDR_UNSPECIFIED(&((struct sockaddr_in6 *)      \
+           (void *)(x))->sin6_addr))
+#endif
+                       if (dst->sa_family == AF_INET &&
+                           multidest(dst) && multidest(sc2->gif_pdst)) {
+                               GIF_ORDERED_UNLOCK(sc, sc2);
+                               error = EADDRNOTAVAIL;
+                               ifnet_head_done();
+                               goto bad;
+                       }
+#if INET6
+                       if (dst->sa_family == AF_INET6 &&
+                           multidest6(dst) && multidest6(sc2->gif_pdst)) {
+                               GIF_ORDERED_UNLOCK(sc, sc2);
+                               error = EADDRNOTAVAIL;
+                               ifnet_head_done();
+                               goto bad;
+                       }
+#endif
+                       GIF_ORDERED_UNLOCK(sc, sc2);
                }
+               ifnet_head_done();
+
+               GIF_LOCK(sc);
+               if (sc->gif_psrc) {
+                       FREE(sc->gif_psrc, M_IFADDR);
+               }
+               sa = (struct sockaddr *)_MALLOC(src->sa_len, M_IFADDR,
+                   M_WAITOK);
+               if (sa == NULL) {
+                       GIF_UNLOCK(sc);
+                       return ENOBUFS;
+               }
+               bcopy((caddr_t)src, (caddr_t)sa, src->sa_len);
+               sc->gif_psrc = sa;
+
+               if (sc->gif_pdst) {
+                       FREE(sc->gif_pdst, M_IFADDR);
+               }
+               sa = (struct sockaddr *)_MALLOC(dst->sa_len, M_IFADDR,
+                   M_WAITOK);
+               if (sa == NULL) {
+                       GIF_UNLOCK(sc);
+                       return ENOBUFS;
+               }
+               bcopy((caddr_t)dst, (caddr_t)sa, dst->sa_len);
+               sc->gif_pdst = sa;
+               GIF_UNLOCK(sc);
+
+               ifnet_set_flags(ifp, IFF_RUNNING | IFF_UP, IFF_RUNNING |
+                   IFF_UP);
+
+               error = 0;
                break;
-                       
+
+#ifdef SIOCDIFPHYADDR
+       case SIOCDIFPHYADDR:
+               GIF_LOCK(sc);
+               if (sc->gif_psrc) {
+                       FREE(sc->gif_psrc, M_IFADDR);
+                       sc->gif_psrc = NULL;
+               }
+               if (sc->gif_pdst) {
+                       FREE(sc->gif_pdst, M_IFADDR);
+                       sc->gif_pdst = NULL;
+               }
+               GIF_UNLOCK(sc);
+               /* change the IFF_{UP, RUNNING} flag as well? */
+               break;
+#endif
+
        case SIOCGIFPSRCADDR:
 #if INET6
        case SIOCGIFPSRCADDR_IN6:
 #endif /* INET6 */
+               GIF_LOCK(sc);
                if (sc->gif_psrc == NULL) {
+                       GIF_UNLOCK(sc);
                        error = EADDRNOTAVAIL;
                        goto bad;
                }
                src = sc->gif_psrc;
-               switch (sc->gif_psrc->sa_family) {
+               switch (cmd) {
 #if INET
-               case AF_INET:
+               case SIOCGIFPSRCADDR:
                        dst = &ifr->ifr_addr;
-                       size = sizeof(struct sockaddr_in);
+                       size = sizeof(ifr->ifr_addr);
                        break;
 #endif /* INET */
 #if INET6
-               case AF_INET6:
+               case SIOCGIFPSRCADDR_IN6:
                        dst = (struct sockaddr *)
-                               &(((struct in6_ifreq *)data)->ifr_addr);
-                       size = sizeof(struct sockaddr_in6);
+                           &(((struct in6_ifreq *)data)->ifr_addr);
+                       size = sizeof(((struct in6_ifreq *)data)->ifr_addr);
                        break;
 #endif /* INET6 */
                default:
+                       GIF_UNLOCK(sc);
                        error = EADDRNOTAVAIL;
                        goto bad;
                }
-               bcopy((caddr_t)src, (caddr_t)dst, size);
+               if (src->sa_len > size) {
+                       GIF_UNLOCK(sc);
+                       return EINVAL;
+               }
+               bcopy((caddr_t)src, (caddr_t)dst, src->sa_len);
+               GIF_UNLOCK(sc);
                break;
-                       
+
        case SIOCGIFPDSTADDR:
 #if INET6
        case SIOCGIFPDSTADDR_IN6:
 #endif /* INET6 */
+               GIF_LOCK(sc);
                if (sc->gif_pdst == NULL) {
+                       GIF_UNLOCK(sc);
                        error = EADDRNOTAVAIL;
                        goto bad;
                }
                src = sc->gif_pdst;
-               switch (sc->gif_pdst->sa_family) {
+               switch (cmd) {
 #if INET
-               case AF_INET:
+               case SIOCGIFPDSTADDR:
                        dst = &ifr->ifr_addr;
-                       size = sizeof(struct sockaddr_in);
+                       size = sizeof(ifr->ifr_addr);
                        break;
 #endif /* INET */
 #if INET6
-               case AF_INET6:
+               case SIOCGIFPDSTADDR_IN6:
                        dst = (struct sockaddr *)
-                               &(((struct in6_ifreq *)data)->ifr_addr);
-                       size = sizeof(struct sockaddr_in6);
+                           &(((struct in6_ifreq *)data)->ifr_addr);
+                       size = sizeof(((struct in6_ifreq *)data)->ifr_addr);
                        break;
 #endif /* INET6 */
                default:
                        error = EADDRNOTAVAIL;
+                       GIF_UNLOCK(sc);
                        goto bad;
                }
-               bcopy((caddr_t)src, (caddr_t)dst, size);
+               if (src->sa_len > size) {
+                       GIF_UNLOCK(sc);
+                       return EINVAL;
+               }
+               bcopy((caddr_t)src, (caddr_t)dst, src->sa_len);
+               GIF_UNLOCK(sc);
                break;
 
        case SIOCSIFFLAGS:
-               if (sc->gif_psrc == NULL)
-                       break;
-               switch (sc->gif_psrc->sa_family) {
-#if INET
-               case AF_INET:
-                       return in_gif_ioctl(ifp, cmd, data);
-#endif /* INET */
-#if INET6
-               case AF_INET6:
-                       return in6_gif_ioctl(ifp, cmd, data);
-#endif /* INET6 */
-               default:
-                       error = EPROTOTYPE;
-                       goto bad;
-                       break;
-               }
+               /* if_ioctl() takes care of it */
                break;
 
        default:
-               error = EINVAL;
+               error = EOPNOTSUPP;
                break;
        }
- bad:
+bad:
        return error;
 }
-#endif /*NGIF > 0*/
+
+static void
+gif_delete_tunnel(struct gif_softc *sc)
+{
+       GIF_LOCK_ASSERT(sc);
+       if (sc->gif_psrc) {
+               FREE(sc->gif_psrc, M_IFADDR);
+               sc->gif_psrc = NULL;
+       }
+       if (sc->gif_pdst) {
+               FREE(sc->gif_pdst, M_IFADDR);
+               sc->gif_pdst = NULL;
+       }
+       ROUTE_RELEASE(&sc->gif_ro);
+       /* change the IFF_UP flag as well? */
+}