]> git.saurik.com Git - apple/xnu.git/blobdiff - bsd/netinet6/in6_gif.c
xnu-4903.270.47.tar.gz
[apple/xnu.git] / bsd / netinet6 / in6_gif.c
index c756948dc0931496996f50aef9728a6b113c24c4..f98fc60e3ce4b7c8c09064862d15cb57f0ae2e3a 100644 (file)
@@ -1,4 +1,33 @@
-/*     $KAME: in6_gif.c,v 1.27 2000/03/25 07:23:43 sumikawa Exp $      */
+/*
+ * Copyright (c) 2009-2016 Apple Inc. All rights reserved.
+ *
+ * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. The rights granted to you under the License
+ * may not be used to create, or enable the creation or redistribution of,
+ * unlawful or unlicensed copies of an Apple operating system, or to
+ * circumvent, violate, or enable the circumvention or violation of, any
+ * terms of an Apple operating system software license agreement.
+ *
+ * Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
+ */
+
+/* $FreeBSD: src/sys/netinet6/in6_gif.c,v 1.2.2.3 2001/07/03 11:01:52 ume Exp $ */
+/* $KAME: in6_gif.c,v 1.49 2001/05/14 14:02:17 itojun Exp $ */
 
 /*
  * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
  * SUCH DAMAGE.
  */
 
-/*
- * in6_gif.c
- */
-
-#if defined(__FreeBSD__) && __FreeBSD__ >= 3
-#include "opt_inet.h"
-#endif
 
 #include <sys/param.h>
 #include <sys/systm.h>
@@ -43,9 +65,9 @@
 #include <sys/sockio.h>
 #include <sys/mbuf.h>
 #include <sys/errno.h>
-#if !defined(__FreeBSD__) || __FreeBSD__ < 3
-#include <sys/ioctl.h>
-#endif
+#include <sys/queue.h>
+#include <sys/syslog.h>
+
 #include <sys/malloc.h>
 #include <sys/protosw.h>
 
 #include <netinet6/ip6_var.h>
 #include <netinet6/in6_gif.h>
 #include <netinet6/in6_var.h>
-#include <netinet6/ip6protosw.h>
 #endif
 #include <netinet/ip_ecn.h>
+#if INET6
+#include <netinet6/ip6_ecn.h>
+#endif
 
 #include <net/if_gif.h>
 
 #include <net/net_osdep.h>
 
-#if INET6
-extern struct ip6protosw in6_gif_protosw;
-#endif
-
 int
-in6_gif_output(ifp, family, m, rt)
-       struct ifnet *ifp;
-       int family; /* family of the packet to be encapsulate. */
-       struct mbuf *m;
-       struct rtentry *rt;
+in6_gif_output(
+       struct ifnet *ifp,
+       int family, /* family of the packet to be encapsulate. */
+       struct mbuf *m,
+       __unused struct rtentry *rt)
 {
-       struct gif_softc *sc = (struct gif_softc*)ifp;
+       struct gif_softc *sc = ifnet_softc(ifp);
        struct sockaddr_in6 *dst = (struct sockaddr_in6 *)&sc->gif_ro6.ro_dst;
-       struct sockaddr_in6 *sin6_src = (struct sockaddr_in6 *)sc->gif_psrc;
-       struct sockaddr_in6 *sin6_dst = (struct sockaddr_in6 *)sc->gif_pdst;
+       struct sockaddr_in6 *sin6_src = (struct sockaddr_in6 *)
+           (void *)sc->gif_psrc;
+       struct sockaddr_in6 *sin6_dst = (struct sockaddr_in6 *)
+           (void *)sc->gif_pdst;
        struct ip6_hdr *ip6;
        int proto;
        u_int8_t itos, otos;
 
+       GIF_LOCK_ASSERT(sc);
+
        if (sin6_src == NULL || sin6_dst == NULL ||
            sin6_src->sin6_family != AF_INET6 ||
            sin6_dst->sin6_family != AF_INET6) {
@@ -100,101 +124,85 @@ in6_gif_output(ifp, family, m, rt)
        switch (family) {
 #if INET
        case AF_INET:
-           {
+       {
                struct ip *ip;
 
                proto = IPPROTO_IPV4;
-               if (m->m_len < sizeof(*ip)) {
+               if (mbuf_len(m) < sizeof(*ip)) {
                        m = m_pullup(m, sizeof(*ip));
-                       if (!m)
+                       if (!m) {
                                return ENOBUFS;
+                       }
                }
                ip = mtod(m, struct ip *);
                itos = ip->ip_tos;
                break;
-           }
+       }
 #endif
 #if INET6
        case AF_INET6:
-           {
-               struct ip6_hdr *ip6;
+       {
                proto = IPPROTO_IPV6;
-               if (m->m_len < sizeof(*ip6)) {
+               if (mbuf_len(m) < sizeof(*ip6)) {
                        m = m_pullup(m, sizeof(*ip6));
-                       if (!m)
+                       if (!m) {
                                return ENOBUFS;
+                       }
                }
                ip6 = mtod(m, struct ip6_hdr *);
                itos = (ntohl(ip6->ip6_flow) >> 20) & 0xff;
                break;
-           }
+       }
 #endif
        default:
 #if DEBUG
                printf("in6_gif_output: warning: unknown family %d passed\n",
-                       family);
+                   family);
 #endif
                m_freem(m);
                return EAFNOSUPPORT;
        }
-       
+
        /* prepend new IP header */
-       M_PREPEND(m, sizeof(struct ip6_hdr), M_DONTWAIT);
-       if (m && m->m_len < sizeof(struct ip6_hdr))
+       M_PREPEND(m, sizeof(struct ip6_hdr), M_DONTWAIT, 1);
+       if (m && mbuf_len(m) < sizeof(struct ip6_hdr)) {
                m = m_pullup(m, sizeof(struct ip6_hdr));
+       }
        if (m == NULL) {
                printf("ENOBUFS in in6_gif_output %d\n", __LINE__);
                return ENOBUFS;
        }
 
        ip6 = mtod(m, struct ip6_hdr *);
-       ip6->ip6_flow   = 0;
-       ip6->ip6_vfc    &= ~IPV6_VERSION_MASK;
-       ip6->ip6_vfc    |= IPV6_VERSION;
-       ip6->ip6_plen   = htons((u_short)m->m_pkthdr.len);
-       ip6->ip6_nxt    = proto;
-       ip6->ip6_hlim   = ip6_gif_hlim;
-       ip6->ip6_src    = sin6_src->sin6_addr;
-       if (ifp->if_flags & IFF_LINK0) {
-               /* multi-destination mode */
-               if (!IN6_IS_ADDR_UNSPECIFIED(&sin6_dst->sin6_addr))
-                       ip6->ip6_dst = sin6_dst->sin6_addr;
-               else if (rt) {
-                       if (family != AF_INET6) {
-                               m_freem(m);
-                               return EINVAL;  /*XXX*/
-                       }
-                       ip6->ip6_dst = ((struct sockaddr_in6 *)(rt->rt_gateway))->sin6_addr;
-               } else {
-                       m_freem(m);
-                       return ENETUNREACH;
-               }
+       ip6->ip6_flow   = 0;
+       ip6->ip6_vfc    &= ~IPV6_VERSION_MASK;
+       ip6->ip6_vfc    |= IPV6_VERSION;
+       ip6->ip6_plen   = htons((u_short)m->m_pkthdr.len);
+       ip6->ip6_nxt    = proto;
+       ip6->ip6_hlim   = ip6_gif_hlim;
+       ip6->ip6_src    = sin6_src->sin6_addr;
+       /* bidirectional configured tunnel mode */
+       if (!IN6_IS_ADDR_UNSPECIFIED(&sin6_dst->sin6_addr)) {
+               ip6->ip6_dst = sin6_dst->sin6_addr;
        } else {
-               /* bidirectional configured tunnel mode */
-               if (!IN6_IS_ADDR_UNSPECIFIED(&sin6_dst->sin6_addr))
-                       ip6->ip6_dst = sin6_dst->sin6_addr;
-               else  {
-                       m_freem(m);
-                       return ENETUNREACH;
-               }
-       }
-       if (ifp->if_flags & IFF_LINK1) {
-               otos = 0;
-               ip_ecn_ingress(ECN_ALLOWED, &otos, &itos);
-               ip6->ip6_flow |= htonl((u_int32_t)otos << 20);
+               m_freem(m);
+               return ENETUNREACH;
        }
-
-       if (dst->sin6_family != sin6_dst->sin6_family ||
-            !IN6_ARE_ADDR_EQUAL(&dst->sin6_addr, &sin6_dst->sin6_addr)) {
-               /* cache route doesn't match */
+       ip_ecn_ingress((ifp->if_flags & IFF_LINK1) ? ECN_NORMAL : ECN_NOCARE,
+           &otos, &itos);
+       ip6->ip6_flow &= ~htonl(0xff << 20);
+       ip6->ip6_flow |= htonl((u_int32_t)otos << 20);
+
+       if (ROUTE_UNUSABLE(&sc->gif_ro6) ||
+           dst->sin6_family != sin6_dst->sin6_family ||
+           !IN6_ARE_ADDR_EQUAL(&dst->sin6_addr, &sin6_dst->sin6_addr) ||
+           (sc->gif_ro6.ro_rt != NULL && sc->gif_ro6.ro_rt->rt_ifp == ifp)) {
+               /* cache route doesn't match or recursive route */
                bzero(dst, sizeof(*dst));
                dst->sin6_family = sin6_dst->sin6_family;
                dst->sin6_len = sizeof(struct sockaddr_in6);
                dst->sin6_addr = sin6_dst->sin6_addr;
-               if (sc->gif_ro6.ro_rt) {
-                       RTFREE(sc->gif_ro6.ro_rt);
-                       sc->gif_ro6.ro_rt = NULL;
-               }
+               ROUTE_RELEASE(&sc->gif_ro6);
 #if 0
                sc->gif_if.if_mtu = GIF_MTU;
 #endif
@@ -206,67 +214,47 @@ in6_gif_output(ifp, family, m, rt)
                        m_freem(m);
                        return ENETUNREACH;
                }
+               RT_LOCK(sc->gif_ro6.ro_rt);
+               /* if it constitutes infinite encapsulation, punt. */
+               if (sc->gif_ro6.ro_rt->rt_ifp == ifp) {
+                       RT_UNLOCK(sc->gif_ro6.ro_rt);
+                       m_freem(m);
+                       return ENETUNREACH; /* XXX */
+               }
 #if 0
                ifp->if_mtu = sc->gif_ro6.ro_rt->rt_ifp->if_mtu
-                       - sizeof(struct ip6_hdr);
+                   - sizeof(struct ip6_hdr);
 #endif
+               RT_UNLOCK(sc->gif_ro6.ro_rt);
        }
-       
-       return(ip6_output(m, 0, &sc->gif_ro6, 0, 0, NULL));
+
+#if IPV6_MINMTU
+       /*
+        * force fragmentation to minimum MTU, to avoid path MTU discovery.
+        * it is too painful to ask for resend of inner packet, to achieve
+        * path MTU discovery for encapsulated packets.
+        */
+       return ip6_output(m, 0, &sc->gif_ro6, IPV6_MINMTU, 0, NULL, NULL);
+#else
+       return ip6_output(m, 0, &sc->gif_ro6, 0, 0, NULL, NULL);
+#endif
 }
 
-int in6_gif_input(mp, offp, proto)
-       struct mbuf **mp;
-       int *offp, proto;
+int
+in6_gif_input(struct mbuf **mp, int *offp, int proto)
 {
        struct mbuf *m = *mp;
-#if 0
-       struct gif_softc *sc;
-#endif
        struct ifnet *gifp = NULL;
        struct ip6_hdr *ip6;
-#if 0
-       int i;
-#endif
        int af = 0;
        u_int32_t otos;
+       int egress_success = 0;
 
        ip6 = mtod(m, struct ip6_hdr *);
 
-#if 0
-#define satoin6(sa)    (((struct sockaddr_in6 *)(sa))->sin6_addr)
-       for (i = 0, sc = gif; i < ngif; i++, sc++) {
-               if (sc->gif_psrc == NULL ||
-                   sc->gif_pdst == NULL ||
-                   sc->gif_psrc->sa_family != AF_INET6 ||
-                   sc->gif_pdst->sa_family != AF_INET6) {
-                       continue;
-               }
-               if ((sc->gif_if.if_flags & IFF_UP) == 0)
-                       continue;
-               if ((sc->gif_if.if_flags & IFF_LINK0) &&
-                   IN6_ARE_ADDR_EQUAL(&satoin6(sc->gif_psrc), &ip6->ip6_dst) &&
-                   IN6_IS_ADDR_UNSPECIFIED(&satoin6(sc->gif_pdst))) {
-                       gifp = &sc->gif_if;
-                       continue;
-               }
-               if (IN6_ARE_ADDR_EQUAL(&satoin6(sc->gif_psrc), &ip6->ip6_dst) &&
-                   IN6_ARE_ADDR_EQUAL(&satoin6(sc->gif_pdst), &ip6->ip6_src)) {
-                       gifp = &sc->gif_if;
-                       break;
-               }
-       }
-#else
-       gifp = (struct ifnet *)encap_getarg(m);
-#endif
+       gifp = ((struct gif_softc *)encap_getarg(m))->gif_if;
 
-       if (gifp == NULL) {
-               m_freem(m);
-               ip6stat.ip6s_nogif++;
-               return IPPROTO_DONE;
-       }
-
-       if ((gifp->if_flags & IFF_UP) == 0) {
+       if (gifp == NULL || (gifp->if_flags & IFF_UP) == 0) {
                m_freem(m);
                ip6stat.ip6s_nogif++;
                return IPPROTO_DONE;
@@ -278,182 +266,158 @@ int in6_gif_input(mp, offp, proto)
        switch (proto) {
 #if INET
        case IPPROTO_IPV4:
-           {
+       {
                struct ip *ip;
-               u_int8_t otos8;
+               u_int8_t otos8, old_tos;
+               int sum;
+
                af = AF_INET;
                otos8 = (ntohl(otos) >> 20) & 0xff;
-               if (m->m_len < sizeof(*ip)) {
+               if (mbuf_len(m) < sizeof(*ip)) {
                        m = m_pullup(m, sizeof(*ip));
-                       if (!m)
+                       if (!m) {
                                return IPPROTO_DONE;
+                       }
                }
                ip = mtod(m, struct ip *);
-               if (gifp->if_flags & IFF_LINK1)
-                       ip_ecn_egress(ECN_ALLOWED, &otos8, &ip->ip_tos);
+               if (gifp->if_flags & IFF_LINK1) {
+                       old_tos = ip->ip_tos;
+                       egress_success = ip_ecn_egress(ECN_NORMAL, &otos8, &ip->ip_tos);
+                       if (old_tos != ip->ip_tos) {
+                               sum = ~ntohs(ip->ip_sum) & 0xffff;
+                               sum += (~old_tos & 0xffff) + ip->ip_tos;
+                               sum = (sum >> 16) + (sum & 0xffff);
+                               sum += (sum >> 16); /* add carry */
+                               ip->ip_sum = htons(~sum & 0xffff);
+                       }
+               } else {
+                       egress_success = ip_ecn_egress(ECN_NOCARE, &otos8, &ip->ip_tos);
+               }
                break;
-           }
+       }
 #endif /* INET */
 #if INET6
        case IPPROTO_IPV6:
-           {
-               struct ip6_hdr *ip6;
+       {
                af = AF_INET6;
-               if (m->m_len < sizeof(*ip6)) {
+               if (mbuf_len(m) < sizeof(*ip6)) {
                        m = m_pullup(m, sizeof(*ip6));
-                       if (!m)
+                       if (!m) {
                                return IPPROTO_DONE;
+                       }
                }
                ip6 = mtod(m, struct ip6_hdr *);
-               if (gifp->if_flags & IFF_LINK1)
-                       ip6_ecn_egress(ECN_ALLOWED, &otos, &ip6->ip6_flow);
+               if (gifp->if_flags & IFF_LINK1) {
+                       egress_success = ip6_ecn_egress(ECN_NORMAL, &otos, &ip6->ip6_flow);
+               } else {
+                       egress_success = ip6_ecn_egress(ECN_NOCARE, &otos, &ip6->ip6_flow);
+               }
                break;
-           }
+       }
 #endif
        default:
                ip6stat.ip6s_nogif++;
                m_freem(m);
                return IPPROTO_DONE;
        }
-               
-       gif_input(m, af, gifp);
+
+       if (egress_success == 0) {
+               ip6stat.ip6s_nogif++;
+               m_freem(m);
+               return IPPROTO_DONE;
+       }
+
+       /* Replace the rcvif by gifp for ifnet_input to route it correctly */
+       if (m->m_pkthdr.rcvif) {
+               m->m_pkthdr.rcvif = gifp;
+       }
+
+       ifnet_input(gifp, m, NULL);
        return IPPROTO_DONE;
 }
 
-int
-in6_gif_ioctl(ifp, cmd, data)
-       struct ifnet *ifp;
-#if defined(__FreeBSD__) && __FreeBSD__ < 3
-       int cmd;
-#else
-       u_long cmd;
-#endif
-       caddr_t data;
+/*
+ * validate outer address.
+ */
+static int
+gif_validate6(
+       const struct ip6_hdr *ip6,
+       struct gif_softc *sc,
+       struct ifnet *ifp)
 {
-       struct gif_softc *sc  = (struct gif_softc*)ifp;
-       struct ifreq     *ifr = (struct ifreq*)data;
-       int error = 0, size;
-       struct sockaddr *sa, *dst, *src;
-       const struct encaptab *p;
-       struct sockaddr_in6 smask6, dmask6;
-               
-       switch (cmd) {
-       case SIOCSIFFLAGS:
-               /*
-                * whenever we change our idea about multi-destination mode
-                * we need to update encap attachment.
-                */
-               if (((ifp->if_flags ^ sc->gif_oflags) & IFF_LINK0) == 0)
-                       break;
-               if (sc->gif_psrc == NULL || sc->gif_pdst == NULL ||
-                   sc->gif_psrc->sa_family != sc->gif_pdst->sa_family)
-                       break;
-               bzero(&smask6, sizeof(smask6));
-               smask6.sin6_addr.s6_addr32[0] = ~0;
-               smask6.sin6_addr.s6_addr32[1] = ~0;
-               smask6.sin6_addr.s6_addr32[2] = ~0;
-               smask6.sin6_addr.s6_addr32[3] = ~0;
-#if 0  /* we'll need to do this soon */
-               smask6.sin6_scope_id = ~0;
-#endif
-               dmask6 = smask6;
-               if ((ifp->if_flags & IFF_LINK0) == 0 &&
-                   IN6_IS_ADDR_UNSPECIFIED(&((struct sockaddr_in6 *)dst)->sin6_addr)) {
-                       bzero(&dmask6, sizeof(dmask6));
-#if 0  /* we'll need to do this soon */
-                       dmask6.sin6_scope_id = ~0;
-#endif
-               }
-               p = encap_attach(sc->gif_psrc->sa_family, -1, sc->gif_psrc,
-                       (struct sockaddr *)&smask6, sc->gif_pdst,
-                       (struct sockaddr *)&dmask6,
-                       (struct protosw *)&in6_gif_protosw, &sc->gif_if);
-               if (p == NULL) {
-                       error = EINVAL;
-                       goto bad;
-               }
-               if (sc->encap_cookie != NULL)
-                       (void)encap_detach(sc->encap_cookie);
-               sc->encap_cookie = p;
-               sc->gif_oflags = ifp->if_flags;
+       struct sockaddr_in6 *src, *dst;
+
+       src = (struct sockaddr_in6 *)(void *)sc->gif_psrc;
+       dst = (struct sockaddr_in6 *)(void *)sc->gif_pdst;
+
+       /*
+        * Check for address match.  Note that the check is for an incoming
+        * packet.  We should compare the *source* address in our configuration
+        * and the *destination* address of the packet, and vice versa.
+        */
+       if (!IN6_ARE_ADDR_EQUAL(&src->sin6_addr, &ip6->ip6_dst) ||
+           !IN6_ARE_ADDR_EQUAL(&dst->sin6_addr, &ip6->ip6_src)) {
+               return 0;
+       }
 
-               break;
+       /* martian filters on outer source - done in ip6_input */
 
-#if INET6
-       case SIOCSIFPHYADDR_IN6:
-#endif
-               switch (ifr->ifr_addr.sa_family) {
-#if INET6
-               case AF_INET6:
-                       src = (struct sockaddr *)
-                               &(((struct in6_aliasreq *)data)->ifra_addr);
-                       dst = (struct sockaddr *)
-                               &(((struct in6_aliasreq *)data)->ifra_dstaddr);
-
-                       bzero(&smask6, sizeof(smask6));
-                       smask6.sin6_addr.s6_addr32[0] = ~0;
-                       smask6.sin6_addr.s6_addr32[1] = ~0;
-                       smask6.sin6_addr.s6_addr32[2] = ~0;
-                       smask6.sin6_addr.s6_addr32[3] = ~0;
-#if 0  /* we'll need to do this soon */
-                       smask6.sin6_scope_id = ~0;
-#endif
-                       dmask6 = smask6;
-                       if ((ifp->if_flags & IFF_LINK0) == 0 &&
-                           IN6_IS_ADDR_UNSPECIFIED(&((struct sockaddr_in6 *)dst)->sin6_addr)) {
-                               bzero(&dmask6, sizeof(dmask6));
-#if 0  /* we'll need to do this soon */
-                               dmask6.sin6_scope_id = ~0;
+       /* ingress filters on outer source */
+       if ((ifnet_flags(sc->gif_if) & IFF_LINK2) == 0 && ifp) {
+               struct sockaddr_in6 sin6;
+               struct rtentry *rt;
+
+               bzero(&sin6, sizeof(sin6));
+               sin6.sin6_family = AF_INET6;
+               sin6.sin6_len = sizeof(struct sockaddr_in6);
+               sin6.sin6_addr = ip6->ip6_src;
+
+               rt = rtalloc1((struct sockaddr *)&sin6, 0, 0);
+               if (rt != NULL) {
+                       RT_LOCK(rt);
+               }
+               if (!rt || rt->rt_ifp != ifp) {
+#if 0
+                       log(LOG_WARNING, "%s: packet from %s dropped "
+                           "due to ingress filter\n", if_name(&sc->gif_if),
+                           ip6_sprintf(&sin6.sin6_addr));
 #endif
+                       if (rt != NULL) {
+                               RT_UNLOCK(rt);
+                               rtfree(rt);
                        }
-                       size = sizeof(struct sockaddr_in6);
-                       break;
-#endif /* INET6 */
-               default:
-                       error = EAFNOSUPPORT;
-                       goto bad;
+                       return 0;
                }
+               RT_UNLOCK(rt);
+               rtfree(rt);
+       }
 
-               if (sc->encap_cookie != NULL)
-                       (void)encap_detach(sc->encap_cookie);
-               if (sc->gif_psrc != NULL) {
-                       _FREE((caddr_t)sc->gif_psrc, M_IFADDR);
-                       sc->gif_psrc = NULL;
-               }
-               if (sc->gif_pdst != NULL) {
-                       _FREE((caddr_t)sc->gif_pdst, M_IFADDR);
-                       sc->gif_pdst = NULL;
-               }
+       return 128 * 2;
+}
 
-               p = encap_attach(ifr->ifr_addr.sa_family, -1, src,
-                       (struct sockaddr *)&smask6, dst,
-                       (struct sockaddr *)&dmask6,
-                       (struct protosw *)&in6_gif_protosw, &sc->gif_if);
-               if (p == NULL) {
-                       error = EINVAL;
-                       goto bad;
-               }
-               sc->encap_cookie = p;
-               sc->gif_oflags = ifp->if_flags;
-
-               sa = (struct sockaddr *)_MALLOC(size, M_IFADDR, M_WAITOK);
-               bcopy((caddr_t)src, (caddr_t)sa, size);
-               sc->gif_psrc = sa;
-               
-               sa = (struct sockaddr *)_MALLOC(size, M_IFADDR, M_WAITOK);
-               bcopy((caddr_t)dst, (caddr_t)sa, size);
-               sc->gif_pdst = sa;
-               
-               ifp->if_flags |= IFF_UP;
-               if_up(ifp);             /* send up RTM_IFINFO */
-
-               error = 0;
-               break;
-       default:
-               error = EINVAL;
-               goto bad;
-       }
+/*
+ * we know that we are in IFF_UP, outer address available, and outer family
+ * matched the physical addr family.  see gif_encapcheck().
+ * sanity check for arg should have been done in the caller.
+ */
+int
+gif_encapcheck6(
+       const struct mbuf *m,
+       __unused int off,
+       __unused int proto,
+       void *arg)
+{
+       struct ip6_hdr ip6;
+       struct gif_softc *sc;
+       struct ifnet *ifp;
+
+       /* sanity check done in caller */
+       sc = (struct gif_softc *)arg;
+
+       GIF_LOCK_ASSERT(sc);
+
+       mbuf_copydata((struct mbuf *)(size_t)m, 0, sizeof(ip6), &ip6);
+       ifp = ((m->m_flags & M_PKTHDR) != 0) ? m->m_pkthdr.rcvif : NULL;
 
- bad:
-       return error;
+       return gif_validate6(&ip6, sc, ifp);
 }