]> git.saurik.com Git - apple/xnu.git/blobdiff - bsd/net/if_gif.c
xnu-344.tar.gz
[apple/xnu.git] / bsd / net / if_gif.c
index aa0d3df0a42e96a6015853f867769d89a82809df..6ada7aa5e18b30bbcae9522b9434b4fbe1a180a7 100644 (file)
@@ -19,7 +19,8 @@
  * 
  * @APPLE_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.
  * SUCH DAMAGE.
  */
 
-/*
- * gif.c
- */
-#if BSD310
-#include "opt_inet.h"
-#endif
-
 #include <sys/param.h>
 #include <sys/systm.h>
 #include <sys/kernel.h>
@@ -67,6 +61,7 @@
 #include <sys/errno.h>
 #include <sys/time.h>
 #include <sys/syslog.h>
+#include <sys/protosw.h>
 #include <kern/cpu_number.h>
 
 #include <net/if.h>
 #include <net/route.h>
 #include <net/bpf.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>
+#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
+#define GIFNAME                "gif"
+#define GIFDEV         "if_gif"
+#define GIF_MAXUNIT    0x7fff  /* ifp->if_unit is only 15 bits */
 
-void gifattach __P((void *));
+#ifndef __APPLE__
+static MALLOC_DEFINE(M_GIF, "gif", "Generic Tunnel Interface");
+#endif
+
+TAILQ_HEAD(gifhead, gif_softc) gifs = TAILQ_HEAD_INITIALIZER(gifs);
+
+#ifdef __APPLE__
+void gifattach __P((void));
 int gif_pre_output __P((struct ifnet *, register struct mbuf **, struct sockaddr *,
-       register struct rtentry *, char *, char *, u_long));
+       caddr_t, char *, char *, u_long));
+static void gif_create_dev(void);
+static int gif_encapcheck(const struct mbuf*, int, int, void*);
+
+
+int ngif = 0;          /* number of interfaces */
+#endif
+
+#if INET
+struct protosw in_gif_protosw =
+{ SOCK_RAW,    0,      0/*IPPROTO_IPV[46]*/,   PR_ATOMIC|PR_ADDR,
+  in_gif_input,        0,      0,              0,
+  0,
+  0,           0,              0,              0,
+  0,
+  &rip_usrreqs
+};
+#endif
+#if INET6
+struct ip6protosw in6_gif_protosw =
+{ SOCK_RAW,    0,      0/*IPPROTO_IPV[46]*/,   PR_ATOMIC|PR_ADDR,
+  in6_gif_input,
+  0,   0,              0,
+  0,
+  0,           0,              0,              0,
+  0,
+  &rip6_usrreqs
+};
+#endif
 
-/*
- * 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.
@@ -131,38 +151,40 @@ static int max_gif_nesting = MAX_GIF_NEST;
 
 
 
-#if 0
+#ifdef __APPLE__
+/*
+ * 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.
+ */
+
+/* GIF interface module support */
 int gif_demux(ifp, m, frame_header, proto)
     struct ifnet *ifp;
     struct mbuf  *m;
     char         *frame_header;
     struct if_proto **proto;
 {
-    int i;
-    return 0;
+       struct gif_softc* gif = (struct gif_softc*)ifp->if_softc;
+       
+       /* Only one protocol may be attached to a gif interface. */
+       *proto = gif->gif_proto;
+       
+       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;
-}
-#endif 
 static
 int  gif_add_if(struct ifnet *ifp) 
-{                   
-    ifp->if_demux  = 0;
+{
+    ifp->if_demux  = gif_demux;
     ifp->if_framer = 0;
     return 0;
 }       
-        
+
 static
 int  gif_del_if(struct ifnet *ifp)
 {       
@@ -171,46 +193,39 @@ int  gif_del_if(struct ifnet *ifp)
 
 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;
-        }
+{
+       /* Only one protocol may be attached at a time */
+       struct gif_softc* gif = (struct gif_softc*)proto->ifp;
 
-    if ((i == gif_count) && (gif_count == NGIF))
-       panic("gif_add_proto -- Too many attachments\n");
+       if (gif->gif_proto != NULL)
+               printf("gif_add_proto: request add_proto for gif%d\n", gif->gif_if.if_unit);
 
-    gif_array[gif_count++] = proto;
+       gif->gif_proto = proto;
 
-    return (0);
+       return 0;
 }
 
 static
 int  gif_del_proto(struct if_proto *proto, u_long dl_tag)
-{       
-    int i;
-
-    for (i=0; i < gif_count; i++)
-        if (gif_array[i] == proto) {
-            gif_array[i] = 0;   
-            return 0;
-        }
+{
+       if (((struct gif_softc*)proto->ifp)->gif_proto == proto)
+               ((struct gif_softc*)proto->ifp)->gif_proto = NULL;
+       else
+               return ENOENT;
 
-    return ENOENT;
+       return 0;
 }
 
 int gif_shutdown()
 {
-    return 0;
+       return 0;
 }
 
 void gif_reg_if_mods()
 {
      struct dlil_ifmod_reg_str  gif_ifmod;
 
+     bzero(&gif_ifmod, sizeof(gif_ifmod));
      gif_ifmod.add_if = gif_add_if;
      gif_ifmod.del_if = gif_del_if;
      gif_ifmod.add_proto = gif_add_proto;
@@ -221,28 +236,22 @@ void gif_reg_if_mods()
     if (dlil_reg_if_modules(APPLE_IF_FAM_GIF, &gif_ifmod))
         panic("Couldn't register gif modules\n");
 
-} 
+}
 
-u_long  gif_attach_inet(struct ifnet *ifp)
+/* Glue code to attach inet to a gif interface through DLIL */
+
+u_long  gif_attach_proto_family(struct ifnet *ifp, int af)
 {
     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)) {
-#if 0
-               kprintf("gif_attach for %s%d found dl_tag=%d\n", 
-                       ifp->if_name, ifp->if_unit, gif_array[i]->dl_tag);
-#endif
-               return gif_array[i]->dl_tag;
-           
-        }
-    }
+    int   stat;
+
+       /* Check if we're already attached */
+       stat = dlil_find_dltag(ifp->if_family, ifp->if_unit, af, &dl_tag);
+       if (stat == 0)
+               return dl_tag;
 
     TAILQ_INIT(&reg.demux_desc_head);
     desc.type = DLIL_DESC_RAW;
@@ -257,67 +266,185 @@ u_long  gif_attach_inet(struct ifnet *ifp)
     reg.pre_output       = gif_pre_output;
     reg.event            = 0;
     reg.offer            = 0;
-    reg.ioctl            = gif_ioctl;
+    reg.ioctl            = 0;
     reg.default_proto    = 0;
-    reg.protocol_family  = PF_INET;
+    reg.protocol_family  = af;
 
     stat = dlil_attach_protocol(&reg, &dl_tag);
     if (stat) {
-        panic("gif_attach_inet can't attach interface\n");
+        panic("gif_attach_proto_family can't attach interface fam=%d\n", af);
     }
 
     return dl_tag;
 }
 
+u_long  gif_detach_proto_family(struct ifnet *ifp, int af)
+{
+    u_long      ip_dl_tag = 0;
+    int         stat;
+
+    stat = dlil_find_dltag(ifp->if_family, ifp->if_unit, af, &ip_dl_tag);
+    if (stat == 0) {
+        stat = dlil_detach_protocol(ip_dl_tag);
+        if (stat) {
+            printf("WARNING: gif_detach can't detach IP fam=%d from interface\n", af);
+       }
+    }
+    return (stat);
+}
+
+#endif
+
+/* Function to setup the first gif interface */
 void
-gifattach(dummy)
-       void *dummy;
+gifattach(void)
 {
-       register struct gif_softc *sc;
-       register int i;
+       /* Init the list of interfaces */
+       TAILQ_INIT(&gifs);
 
        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);
+       /* Create first device */
+       gif_create_dev();
+}
+
+/* Creates another gif device if there are none free */
+static void
+gif_create_dev(void)
+{
+       struct gif_softc *sc;
+       
+       
+       /* Can't create more than GIF_MAXUNIT */
+       if (ngif >= GIF_MAXUNIT)
+               return;
+       
+       /* Check for unused gif interface */
+       TAILQ_FOREACH(sc, &gifs, gif_link) {
+               /* If unused, return, no need to create a new interface */
+               if ((sc->gif_if.if_flags & IFF_RUNNING) == 0)
+                       return;
+       }
+
+       sc = _MALLOC(sizeof(struct gif_softc), M_DEVBUF, M_WAITOK);
+       if (sc == NULL) {
+               log(LOG_ERR, "gifattach: failed to allocate gif%d\n", ngif);
+               return;
+       }
+
+       bzero(sc, sizeof(struct gif_softc));
+       sc->gif_if.if_softc     = sc;
+       sc->gif_if.if_name      = GIFNAME;
+       sc->gif_if.if_unit      = ngif;
+       
+       sc->encap_cookie4 = sc->encap_cookie6 = NULL;
+#ifdef 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));
+               FREE(sc, M_DEVBUF);
+               return;
+       }
+#endif
+#ifdef 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));
+               FREE(sc, M_DEVBUF);
+               return;
+       }
+#endif
+       
+       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;
 #if 0
-               kprintf("gifattach: Attaching gif%d sc=%x gif_if=%x\n", i, sc, &sc->gif_if);
+       /* turn off ingress filter */
+       sc->gif_if.if_flags  |= IFF_LINK2;
 #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));
+       sc->gif_if.if_ioctl     = gif_ioctl;
+       sc->gif_if.if_output = NULL;    /* pre_output returns error or EJUSTRETURN */
+       sc->gif_if.if_type   = IFT_GIF;
+       dlil_if_attach(&sc->gif_if);
+       bpfattach(&sc->gif_if, DLT_NULL, sizeof(u_int));
+       TAILQ_INSERT_TAIL(&gifs, sc, gif_link);
+       ngif++;
+}
+
+static int
+gif_encapcheck(m, off, proto, arg)
+       const struct mbuf *m;
+       int off;
+       int proto;
+       void *arg;
+{
+       struct ip ip;
+       struct gif_softc *sc;
+
+       sc = (struct gif_softc *)arg;
+       if (sc == NULL)
+               return 0;
+
+       if ((sc->gif_if.if_flags & IFF_UP) == 0)
+               return 0;
+
+       /* no physical address */
+       if (!sc->gif_psrc || !sc->gif_pdst)
+               return 0;
+
+       switch (proto) {
+#if INET
+       case IPPROTO_IPV4:
+               break;
 #endif
+#if INET6
+       case IPPROTO_IPV6:
+               break;
 #endif
+       default:
+               return 0;
        }
-}
 
-#ifdef __FreeBSD__
-PSEUDO_SET(gifattach, if_gif);
+       /* LINTED const cast */
+       m_copydata((struct mbuf *)m, 0, sizeof(ip), (caddr_t)&ip);
+
+       switch (ip.ip_v) {
+#if INET
+       case 4:
+               if (sc->gif_psrc->sa_family != AF_INET ||
+                   sc->gif_pdst->sa_family != AF_INET)
+                       return 0;
+               return gif_encapcheck4(m, off, proto, arg);
+#endif
+#if INET6
+       case 6:
+               if (sc->gif_psrc->sa_family != AF_INET6 ||
+                   sc->gif_pdst->sa_family != AF_INET6)
+                       return 0;
+               return gif_encapcheck6(m, off, proto, arg);
 #endif
+       default:
+               return 0;
+       }
+}
 
 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 */
+       caddr_t rt;
        char *frame;
        char *address;
        u_long dl_tag;
 {
-       register struct gif_softc *sc = (struct gif_softc*)ifp;
+       struct gif_softc *sc = (struct gif_softc*)ifp;
        register struct mbuf * m = *m0;
        int error = 0;
        static int called = 0;  /* XXX: MUTEX */
@@ -333,7 +460,6 @@ gif_pre_output(ifp, m0, dst, rt, frame, address, dl_tag)
                log(LOG_NOTICE,
                    "gif_output: recursively called too many times(%d)\n",
                    called);
-               m_freem(m);
                error = EIO;    /* is there better errno? */
                goto end;
        }
@@ -341,17 +467,11 @@ gif_pre_output(ifp, m0, dst, rt, frame, address, dl_tag)
        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;
        }
 
-#if NBPFILTER > 0
        if (ifp->if_bpf) {
                /*
                 * We need to prepend the address family as
@@ -361,64 +481,58 @@ gif_pre_output(ifp, m0, dst, rt, frame, address, dl_tag)
                 * try to free it or keep a pointer a to it).
                 */
                struct mbuf m0;
-               u_int af = dst->sa_family;
+               u_int32_t 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);
-#endif
        }
-#endif
        ifp->if_opackets++;     
        ifp->if_obytes += m->m_pkthdr.len;
-#if 0
-       s = splnet();
-       sc->gif_flags |= GIFF_INUSE;
-#endif
 
+       /* inner AF-specific encapsulation */
+
+       /* XXX should we check if our outer source is legal? */
+
+       /* 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, dst->sa_family, m, (struct rtentry*)rt);
                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, dst->sa_family, m, (struct rtentry*)rt);
                break;
 #endif
        default:
-               m_freem(m);             
                error = ENETDOWN;
+               goto end;
        }
-#if 0
-       sc->gif_flags &= ~GIFF_INUSE;
-       splx(s);
-#endif
 
   end:
        called = 0;             /* reset recursion counter */
-       if (error) ifp->if_oerrors++;
-       return EJUSTRETURN;
+       if (error)
+               ifp->if_oerrors++;
+       if (error == 0) 
+               error = EJUSTRETURN; /* if no error, packet got sent already */
+       return error;
 }
 
-void
-gif_input(m, af, gifp)
+int
+gif_input(m, frame_header, gifp, dl_tag, sync_ok)
        struct mbuf *m;
-       int af;
-       struct ifnet *gifp;
+       char* frame_header;
+       struct ifnet* gifp;
+       u_long dl_tag;
+       int sync_ok;
 {
        int s, isr;
-       register struct ifqueue *ifq = 0;
+       struct ifqueue *ifq = 0;
+       int af;
 
        if (gifp == NULL) {
                /* just in case */
@@ -426,10 +540,12 @@ gif_input(m, af, gifp)
                return;
        }
 
+       /* Assume packet is of type of protocol attached to this interface */
+       af = ((struct gif_softc*)(gifp->if_softc))->gif_proto->protocol_family;
+
        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
@@ -439,19 +555,14 @@ gif_input(m, af, gifp)
                 * try to free it or keep a pointer a to it).
                 */
                struct mbuf m0;
-               u_int af = AF_INET6;
+               u_int32_t af1 = af;
                
                m0.m_next = m;
                m0.m_len = 4;
-               m0.m_data = (char *)&af;
+               m0.m_data = (char *)&af1;
                
-#ifdef HAVE_OLD_BPF
                bpf_mtap(gifp, &m0);
-#else
-               bpf_mtap(gifp->if_bpf, &m0);
-#endif
        }
-#endif /*NBPFILTER > 0*/
 
        /*
         * Put the packet to the network layer input queue according to the
@@ -480,7 +591,7 @@ gif_input(m, af, gifp)
 #endif
        default:
                m_freem(m);
-               return;
+               return (EJUSTRETURN);
        }
 
        s = splimp();
@@ -488,7 +599,7 @@ gif_input(m, af, gifp)
                IF_DROP(ifq);   /* update statistics */
                m_freem(m);
                splx(s);
-               return;
+               return (EJUSTRETURN);
        }
        IF_ENQUEUE(ifq, m);
        /* we need schednetisr since the address family may change */
@@ -497,7 +608,7 @@ gif_input(m, af, gifp)
        gifp->if_ibytes += m->m_pkthdr.len;
        splx(s);
 
-       return;
+       return (0);
 }
 
 /* XXX how should we handle IPv6 scope on SIOC[GS]IFPHYADDR? */
@@ -505,13 +616,15 @@ int
 gif_ioctl(ifp, cmd, data)
        struct ifnet *ifp;
        u_long cmd;
-       caddr_t data;
+       void* data;
 {
        struct gif_softc *sc  = (struct gif_softc*)ifp;
        struct ifreq     *ifr = (struct ifreq*)data;
        int error = 0, size;
        struct sockaddr *dst, *src;
-       int i;
+       struct sockaddr *sa;
+       int s;
+       struct ifnet *ifp2;
        struct gif_softc *sc2;
                
        switch (cmd) {
@@ -523,87 +636,191 @@ gif_ioctl(ifp, cmd, data)
 
        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__
        case SIOCGIFMTU:
                break;
+
        case SIOCSIFMTU:
                {
-#ifdef __bsdi__
-                       short mtu;
-                       mtu = *(short *)ifr->ifr_data;
-#else
                        u_long mtu;
                        mtu = ifr->ifr_mtu;
-#endif
                        if (mtu < GIF_MTU_MIN || mtu > GIF_MTU_MAX) {
                                return (EINVAL);
                        }
                        ifp->if_mtu = mtu;
                }
                break;
-#endif
 #endif /* SIOCSIFMTU */
 
        case SIOCSIFPHYADDR:
 #if INET6
        case SIOCSIFPHYADDR_IN6:
 #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;
+       case SIOCSLIFPHYADDR:
+               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:
+                       src = (struct sockaddr *)
+                               &(((struct in6_aliasreq *)data)->ifra_addr);
+                       dst = (struct sockaddr *)
+                               &(((struct in6_aliasreq *)data)->ifra_dstaddr);
+                       break;
+#endif
+               case SIOCSLIFPHYADDR:
+                       src = (struct sockaddr *)
+                               &(((struct if_laddrreq *)data)->addr);
+                       dst = (struct sockaddr *)
+                               &(((struct if_laddrreq *)data)->dstaddr);
+               }
+
+               /* 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;
+                       break;
+#endif
+               default:
+                       return EAFNOSUPPORT;
+               }
+               switch (dst->sa_family) {
+#if INET
+               case AF_INET:
+                       if (dst->sa_len != sizeof(struct sockaddr_in))
+                               return EINVAL;
+                       break;
+#endif
+#if INET6
+               case AF_INET6:
+                       if (dst->sa_len != sizeof(struct sockaddr_in6))
+                               return EINVAL;
+                       break;
+#endif
+               default:
+                       return EAFNOSUPPORT;
+               }
+
+               /* 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:
+                       if (src->sa_family == AF_INET6)
+                               break;
+                       return EAFNOSUPPORT;
+#endif /* INET6 */
+               case SIOCSLIFPHYADDR:
+                       /* checks done in the above */
+                       break;
+               }
+
+               TAILQ_FOREACH(ifp2, &ifnet, if_link) {
+                       if (strcmp(ifp2->if_name, GIFNAME) != 0)
+                               continue;
+                       sc2 = ifp2->if_softc;
                        if (sc2 == sc)
                                continue;
                        if (!sc2->gif_pdst || !sc2->gif_psrc)
                                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)
+                               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) {
                                error = EADDRNOTAVAIL;
                                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 *)(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 *)(x))->sin6_addr))
+#endif
+                       if (dst->sa_family == AF_INET &&
+                           multidest(dst) && multidest(sc2->gif_pdst)) {
+                               error = EADDRNOTAVAIL;
+                               goto bad;
+                       }
+#if INET6
+                       if (dst->sa_family == AF_INET6 &&
+                           multidest6(dst) && multidest6(sc2->gif_pdst)) {
+                               error = EADDRNOTAVAIL;
+                               goto bad;
+                       }
+#endif
                }
+
+               if (sc->gif_psrc)
+                       FREE((caddr_t)sc->gif_psrc, M_IFADDR);
+               sa = (struct sockaddr *)_MALLOC(src->sa_len, M_IFADDR, M_WAITOK);
+               bcopy((caddr_t)src, (caddr_t)sa, src->sa_len);
+               sc->gif_psrc = sa;
+
+               if (sc->gif_pdst)
+                       FREE((caddr_t)sc->gif_pdst, M_IFADDR);
+               sa = (struct sockaddr *)_MALLOC(dst->sa_len, M_IFADDR, M_WAITOK);
+               bcopy((caddr_t)dst, (caddr_t)sa, dst->sa_len);
+               sc->gif_pdst = sa;
+
+               ifp->if_flags |= IFF_RUNNING;
+
+               gif_attach_proto_family(ifp, src->sa_family);
+
+               s = splimp();
+               if_up(ifp);     /* mark interface UP and send up RTM_IFINFO */
+#ifdef __APPLE__
+               /* Make sure at least one unused device is still available */
+               gif_create_dev();
+#endif
+               splx(s);
+
+               error = 0;
                break;
+
+#ifdef SIOCDIFPHYADDR
+       case SIOCDIFPHYADDR:
+               if (sc->gif_psrc) {
+                       FREE((caddr_t)sc->gif_psrc, M_IFADDR);
+                       sc->gif_psrc = NULL;
+               }
+               if (sc->gif_pdst) {
+                       FREE((caddr_t)sc->gif_pdst, M_IFADDR);
+                       sc->gif_pdst = NULL;
+               }
+               /* change the IFF_{UP, RUNNING} flag as well? */
+               break;
+#endif
                        
        case SIOCGIFPSRCADDR:
 #if INET6
@@ -614,25 +831,27 @@ gif_ioctl(ifp, cmd, data)
                        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);
+                       size = sizeof(((struct in6_ifreq *)data)->ifr_addr);
                        break;
 #endif /* INET6 */
                default:
                        error = EADDRNOTAVAIL;
                        goto bad;
                }
-               bcopy((caddr_t)src, (caddr_t)dst, size);
+               if (src->sa_len > size)
+                       return EINVAL;
+               bcopy((caddr_t)src, (caddr_t)dst, src->sa_len);
                break;
                        
        case SIOCGIFPDSTADDR:
@@ -644,51 +863,79 @@ gif_ioctl(ifp, cmd, data)
                        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);
+                       size = sizeof(((struct in6_ifreq *)data)->ifr_addr);
                        break;
 #endif /* INET6 */
                default:
                        error = EADDRNOTAVAIL;
                        goto bad;
                }
-               bcopy((caddr_t)src, (caddr_t)dst, size);
+               if (src->sa_len > size)
+                       return EINVAL;
+               bcopy((caddr_t)src, (caddr_t)dst, src->sa_len);
                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;
+       case SIOCGLIFPHYADDR:
+               if (sc->gif_psrc == NULL || sc->gif_pdst == NULL) {
+                       error = EADDRNOTAVAIL;
                        goto bad;
-                       break;
                }
+
+               /* copy src */
+               src = sc->gif_psrc;
+               dst = (struct sockaddr *)
+                       &(((struct if_laddrreq *)data)->addr);
+               size = sizeof(((struct if_laddrreq *)data)->addr);
+               if (src->sa_len > size)
+                       return EINVAL;
+               bcopy((caddr_t)src, (caddr_t)dst, src->sa_len);
+
+               /* copy dst */
+               src = sc->gif_pdst;
+               dst = (struct sockaddr *)
+                       &(((struct if_laddrreq *)data)->dstaddr);
+               size = sizeof(((struct if_laddrreq *)data)->dstaddr);
+               if (src->sa_len > size)
+                       return EINVAL;
+               bcopy((caddr_t)src, (caddr_t)dst, src->sa_len);
+               break;
+
+       case SIOCSIFFLAGS:
+               /* if_ioctl() takes care of it */
                break;
 
        default:
-               error = EINVAL;
+               error = EOPNOTSUPP;
                break;
        }
  bad:
        return error;
 }
-#endif /*NGIF > 0*/
+
+void
+gif_delete_tunnel(sc)
+       struct gif_softc *sc;
+{
+       /* XXX: NetBSD protects this function with splsoftnet() */
+
+       if (sc->gif_psrc) {
+               FREE((caddr_t)sc->gif_psrc, M_IFADDR);
+               sc->gif_psrc = NULL;
+       }
+       if (sc->gif_pdst) {
+               FREE((caddr_t)sc->gif_pdst, M_IFADDR);
+               sc->gif_pdst = NULL;
+       }
+       /* change the IFF_UP flag as well? */
+}