in6_gif.c revision 272859
166458Sdfr/*-
266458Sdfr * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
366458Sdfr * All rights reserved.
466458Sdfr *
566458Sdfr * Redistribution and use in source and binary forms, with or without
666458Sdfr * modification, are permitted provided that the following conditions
766458Sdfr * are met:
866458Sdfr * 1. Redistributions of source code must retain the above copyright
966458Sdfr *    notice, this list of conditions and the following disclaimer.
1066458Sdfr * 2. Redistributions in binary form must reproduce the above copyright
1166458Sdfr *    notice, this list of conditions and the following disclaimer in the
1266458Sdfr *    documentation and/or other materials provided with the distribution.
1366458Sdfr * 3. Neither the name of the project nor the names of its contributors
1466458Sdfr *    may be used to endorse or promote products derived from this software
1566458Sdfr *    without specific prior written permission.
1666458Sdfr *
1766458Sdfr * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
1866458Sdfr * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1966458Sdfr * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2066458Sdfr * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
2166458Sdfr * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2266458Sdfr * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2366458Sdfr * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2466458Sdfr * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2566458Sdfr * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2666458Sdfr * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2766458Sdfr * SUCH DAMAGE.
2866458Sdfr *
2966458Sdfr *	$KAME: in6_gif.c,v 1.49 2001/05/14 14:02:17 itojun Exp $
30135581Smarcel */
3166458Sdfr
32185163Smarcel#include <sys/cdefs.h>
33185163Smarcel__FBSDID("$FreeBSD: stable/10/sys/netinet6/in6_gif.c 272859 2014-10-09 23:29:44Z hrs $");
34185163Smarcel
35185162Skmacy#include "opt_inet.h"
3666458Sdfr#include "opt_inet6.h"
3766458Sdfr
3866458Sdfr#include <sys/param.h>
3966458Sdfr#include <sys/systm.h>
4066458Sdfr#include <sys/socket.h>
4166458Sdfr#include <sys/sockio.h>
4266458Sdfr#include <sys/mbuf.h>
4366458Sdfr#include <sys/errno.h>
44135581Smarcel#include <sys/kernel.h>
45135581Smarcel#include <sys/queue.h>
46135581Smarcel#include <sys/syslog.h>
47135581Smarcel#include <sys/sysctl.h>
48135581Smarcel#include <sys/protosw.h>
49179382Smarcel#include <sys/malloc.h>
5096956Smarcel
5166458Sdfr#include <net/if.h>
5266458Sdfr#include <net/route.h>
5366458Sdfr
5466458Sdfr#include <netinet/in.h>
55135581Smarcel#include <netinet/in_systm.h>
56135581Smarcel#ifdef INET
5766458Sdfr#include <netinet/ip.h>
58135581Smarcel#endif
5996956Smarcel#include <netinet/ip_encap.h>
6096956Smarcel#ifdef INET6
6166458Sdfr#include <netinet/ip6.h>
6266458Sdfr#include <netinet6/ip6_var.h>
63135581Smarcel#include <netinet6/in6_gif.h>
64135581Smarcel#include <netinet6/in6_var.h>
6566458Sdfr#endif
66135581Smarcel#include <netinet6/ip6protosw.h>
6796956Smarcel#include <netinet/ip_ecn.h>
6896956Smarcel#ifdef INET6
6966458Sdfr#include <netinet6/ip6_ecn.h>
7066458Sdfr#endif
71135581Smarcel
72135581Smarcel#include <net/if_gif.h>
7366458Sdfr
74135581SmarcelVNET_DEFINE(int, ip6_gif_hlim) = GIF_HLIM;
7596956Smarcel#define	V_ip6_gif_hlim			VNET(ip6_gif_hlim)
7696956Smarcel
7766458SdfrSYSCTL_DECL(_net_inet6_ip6);
7866458SdfrSYSCTL_VNET_INT(_net_inet6_ip6, IPV6CTL_GIF_HLIM, gifhlim, CTLFLAG_RW,
79135581Smarcel    &VNET_NAME(ip6_gif_hlim), 0, "");
80135581Smarcel
8166458Sdfrstatic int gif_validate6(const struct ip6_hdr *, struct gif_softc *,
82135581Smarcel			 struct ifnet *);
8396956Smarcel
8496956Smarcelextern  struct domain inet6domain;
8566458Sdfrstruct ip6protosw in6_gif_protosw = {
8666458Sdfr	.pr_type =	SOCK_RAW,
87135581Smarcel	.pr_domain =	&inet6domain,
88135581Smarcel	.pr_protocol =	0,			/* IPPROTO_IPV[46] */
89135581Smarcel	.pr_flags =	PR_ATOMIC|PR_ADDR,
90135581Smarcel	.pr_input =	in6_gif_input,
91135581Smarcel	.pr_output =	rip6_output,
92135581Smarcel	.pr_ctloutput =	rip6_ctloutput,
93135581Smarcel	.pr_usrreqs =	&rip6_usrreqs
94135581Smarcel};
95135581Smarcel
96135581Smarcelint
97135581Smarcelin6_gif_output(struct ifnet *ifp,
98135581Smarcel    int family,			/* family of the packet to be encapsulate */
99135581Smarcel    struct mbuf *m)
100135581Smarcel{
101135581Smarcel	struct gif_softc *sc = ifp->if_softc;
102135581Smarcel	struct sockaddr_in6 *dst = (struct sockaddr_in6 *)&sc->gif_ro6.ro_dst;
103135581Smarcel	struct sockaddr_in6 *sin6_src = (struct sockaddr_in6 *)sc->gif_psrc;
104135581Smarcel	struct sockaddr_in6 *sin6_dst = (struct sockaddr_in6 *)sc->gif_pdst;
105135581Smarcel	struct ip6_hdr *ip6;
106135581Smarcel	struct etherip_header eiphdr;
107135581Smarcel	int error, len, proto;
108135581Smarcel	u_int8_t itos, otos;
109135581Smarcel
110135581Smarcel	GIF_LOCK_ASSERT(sc);
111135581Smarcel
112135581Smarcel	if (sin6_src == NULL || sin6_dst == NULL ||
113135581Smarcel	    sin6_src->sin6_family != AF_INET6 ||
114135581Smarcel	    sin6_dst->sin6_family != AF_INET6) {
115135581Smarcel		m_freem(m);
116135581Smarcel		return EAFNOSUPPORT;
117135581Smarcel	}
118135581Smarcel
119135581Smarcel	switch (family) {
120135581Smarcel#ifdef INET
121135581Smarcel	case AF_INET:
122135581Smarcel	    {
123135581Smarcel		struct ip *ip;
124135581Smarcel
125135581Smarcel		proto = IPPROTO_IPV4;
126135581Smarcel		if (m->m_len < sizeof(*ip)) {
127135581Smarcel			m = m_pullup(m, sizeof(*ip));
128135581Smarcel			if (!m)
129135581Smarcel				return ENOBUFS;
130135581Smarcel		}
131135581Smarcel		ip = mtod(m, struct ip *);
132135581Smarcel		itos = ip->ip_tos;
133135581Smarcel		break;
134135581Smarcel	    }
135135581Smarcel#endif
136135581Smarcel#ifdef INET6
13766458Sdfr	case AF_INET6:
138135581Smarcel	    {
139135581Smarcel		struct ip6_hdr *ip6;
140135581Smarcel		proto = IPPROTO_IPV6;
141135581Smarcel		if (m->m_len < sizeof(*ip6)) {
14266458Sdfr			m = m_pullup(m, sizeof(*ip6));
14367351Sjhb			if (!m)
14467351Sjhb				return ENOBUFS;
145171662Smarcel		}
146171662Smarcel		ip6 = mtod(m, struct ip6_hdr *);
147148067Sjhb		itos = (ntohl(ip6->ip6_flow) >> 20) & 0xff;
148171662Smarcel		break;
149171662Smarcel	    }
150171662Smarcel#endif
151135581Smarcel	case AF_LINK:
152135583Smarcel		proto = IPPROTO_ETHERIP;
153135581Smarcel
154135581Smarcel		/*
155135581Smarcel		 * GIF_SEND_REVETHIP (disabled by default) intentionally
156135581Smarcel		 * sends an EtherIP packet with revered version field in
157135581Smarcel		 * the header.  This is a knob for backward compatibility
158135581Smarcel		 * with FreeBSD 7.2R or prior.
159135581Smarcel		 */
160135583Smarcel		if ((sc->gif_options & GIF_SEND_REVETHIP)) {
161135581Smarcel			eiphdr.eip_ver = 0;
16266458Sdfr			eiphdr.eip_resvl = ETHERIP_VERSION;
163135583Smarcel			eiphdr.eip_resvh = 0;
164135581Smarcel		} else {
165135581Smarcel			eiphdr.eip_ver = ETHERIP_VERSION;
166135581Smarcel			eiphdr.eip_resvl = 0;
167135581Smarcel			eiphdr.eip_resvh = 0;
168135581Smarcel		}
169135581Smarcel		/* prepend Ethernet-in-IP header */
170135581Smarcel		M_PREPEND(m, sizeof(struct etherip_header), M_NOWAIT);
171135583Smarcel		if (m && m->m_len < sizeof(struct etherip_header))
172135581Smarcel			m = m_pullup(m, sizeof(struct etherip_header));
17366458Sdfr		if (m == NULL)
174135581Smarcel			return ENOBUFS;
175135581Smarcel		bcopy(&eiphdr, mtod(m, struct etherip_header *),
176135581Smarcel		    sizeof(struct etherip_header));
177135581Smarcel		itos = 0;
17866458Sdfr		break;
179135581Smarcel
180135581Smarcel	default:
181135581Smarcel#ifdef DEBUG
182135581Smarcel		printf("in6_gif_output: warning: unknown family %d passed\n",
18366458Sdfr			family);
184135581Smarcel#endif
185135581Smarcel		m_freem(m);
186135581Smarcel		return EAFNOSUPPORT;
187135581Smarcel	}
18866458Sdfr
189135581Smarcel	/* prepend new IP header */
190135581Smarcel	len = sizeof(struct ip6_hdr);
191135581Smarcel#ifndef __NO_STRICT_ALIGNMENT
192135581Smarcel	if (family == AF_LINK)
19366458Sdfr		len += ETHERIP_ALIGN;
19466458Sdfr#endif
19566458Sdfr	M_PREPEND(m, len, M_NOWAIT);
196135581Smarcel	if (m != NULL && m->m_len < len)
197135581Smarcel		m = m_pullup(m, len);
198135581Smarcel	if (m == NULL) {
199135581Smarcel		printf("ENOBUFS in in6_gif_output %d\n", __LINE__);
20066458Sdfr		return ENOBUFS;
201135581Smarcel	}
202135581Smarcel#ifndef __NO_STRICT_ALIGNMENT
203135581Smarcel	if (family == AF_LINK) {
204135581Smarcel		len = mtod(m, vm_offset_t) & 3;
20566458Sdfr		KASSERT(len == 0 || len == ETHERIP_ALIGN,
206135581Smarcel		    ("in6_gif_output: unexpected misalignment"));
207135581Smarcel		m->m_data += len;
208135581Smarcel		m->m_len -= ETHERIP_ALIGN;
209135581Smarcel	}
21066458Sdfr#endif
211135581Smarcel
212135581Smarcel	ip6 = mtod(m, struct ip6_hdr *);
213135581Smarcel	ip6->ip6_flow	= 0;
214135581Smarcel	ip6->ip6_vfc	&= ~IPV6_VERSION_MASK;
21566458Sdfr	ip6->ip6_vfc	|= IPV6_VERSION;
216135581Smarcel	ip6->ip6_plen	= htons((u_short)m->m_pkthdr.len);
217135581Smarcel	ip6->ip6_nxt	= proto;
218135581Smarcel	ip6->ip6_hlim	= V_ip6_gif_hlim;
219135581Smarcel	ip6->ip6_src	= sin6_src->sin6_addr;
220135581Smarcel	/* bidirectional configured tunnel mode */
221135581Smarcel	if (!IN6_IS_ADDR_UNSPECIFIED(&sin6_dst->sin6_addr))
222135581Smarcel		ip6->ip6_dst = sin6_dst->sin6_addr;
223135581Smarcel	else  {
224135581Smarcel		m_freem(m);
225135581Smarcel		return ENETUNREACH;
226135581Smarcel	}
227135581Smarcel	ip_ecn_ingress((ifp->if_flags & IFF_LINK1) ? ECN_ALLOWED : ECN_NOCARE,
22867351Sjhb		       &otos, &itos);
229135581Smarcel	ip6->ip6_flow &= ~htonl(0xff << 20);
230135581Smarcel	ip6->ip6_flow |= htonl((u_int32_t)otos << 20);
231135581Smarcel
232135581Smarcel	M_SETFIB(m, sc->gif_fibnum);
233135581Smarcel
234135581Smarcel	if (dst->sin6_family != sin6_dst->sin6_family ||
235135581Smarcel	     !IN6_ARE_ADDR_EQUAL(&dst->sin6_addr, &sin6_dst->sin6_addr)) {
236135581Smarcel		/* cache route doesn't match */
237135581Smarcel		bzero(dst, sizeof(*dst));
238135581Smarcel		dst->sin6_family = sin6_dst->sin6_family;
239135581Smarcel		dst->sin6_len = sizeof(struct sockaddr_in6);
240135581Smarcel		dst->sin6_addr = sin6_dst->sin6_addr;
24167351Sjhb		if (sc->gif_ro6.ro_rt) {
242135581Smarcel			RTFREE(sc->gif_ro6.ro_rt);
243135581Smarcel			sc->gif_ro6.ro_rt = NULL;
244135581Smarcel		}
245135581Smarcel#if 0
246135581Smarcel		GIF2IFP(sc)->if_mtu = GIF_MTU;
247135581Smarcel#endif
248135581Smarcel	}
249135581Smarcel
250135581Smarcel	if (sc->gif_ro6.ro_rt == NULL) {
251135581Smarcel		in6_rtalloc(&sc->gif_ro6, sc->gif_fibnum);
252135581Smarcel		if (sc->gif_ro6.ro_rt == NULL) {
253135581Smarcel			m_freem(m);
25467351Sjhb			return ENETUNREACH;
255135581Smarcel		}
256135581Smarcel
257135581Smarcel		/* if it constitutes infinite encapsulation, punt. */
258135581Smarcel		if (sc->gif_ro.ro_rt->rt_ifp == ifp) {
259135581Smarcel			m_freem(m);
260135581Smarcel			return ENETUNREACH;	/*XXX*/
261135581Smarcel		}
262135581Smarcel#if 0
263135581Smarcel		ifp->if_mtu = sc->gif_ro6.ro_rt->rt_ifp->if_mtu
264135581Smarcel			- sizeof(struct ip6_hdr);
265135581Smarcel#endif
266135581Smarcel	}
26767351Sjhb
268171662Smarcel	m->m_flags &= ~(M_BCAST|M_MCAST);
269148067Sjhb#ifdef IPV6_MINMTU
270148067Sjhb	/*
271148067Sjhb	 * force fragmentation to minimum MTU, to avoid path MTU discovery.
272148067Sjhb	 * it is too painful to ask for resend of inner packet, to achieve
273148067Sjhb	 * path MTU discovery for encapsulated packets.
274148067Sjhb	 */
275148067Sjhb	error = ip6_output(m, 0, &sc->gif_ro6, IPV6_MINMTU, 0, NULL, NULL);
276148067Sjhb#else
277148067Sjhb	error = ip6_output(m, 0, &sc->gif_ro6, 0, 0, NULL, NULL);
278148067Sjhb#endif
279148067Sjhb
280148067Sjhb	if (!(GIF2IFP(sc)->if_flags & IFF_LINK0) &&
281148067Sjhb	    sc->gif_ro6.ro_rt != NULL) {
282135581Smarcel		RTFREE(sc->gif_ro6.ro_rt);
283135581Smarcel		sc->gif_ro6.ro_rt = NULL;
28466458Sdfr	}
28566458Sdfr
28666458Sdfr	return (error);
28766458Sdfr}
28866458Sdfr
28966458Sdfrint
290135581Smarcelin6_gif_input(struct mbuf **mp, int *offp, int proto)
29166458Sdfr{
292135581Smarcel	struct mbuf *m = *mp;
29366458Sdfr	struct ifnet *gifp = NULL;
29466458Sdfr	struct gif_softc *sc;
29567351Sjhb	struct ip6_hdr *ip6;
296135581Smarcel	int af = 0;
29767351Sjhb	u_int32_t otos;
298135581Smarcel
29967351Sjhb	ip6 = mtod(m, struct ip6_hdr *);
30067351Sjhb
30166458Sdfr	sc = (struct gif_softc *)encap_getarg(m);
30266458Sdfr	if (sc == NULL) {
30366458Sdfr		m_freem(m);
30466458Sdfr		IP6STAT_INC(ip6s_nogif);
30566458Sdfr		return IPPROTO_DONE;
30666458Sdfr	}
307135581Smarcel
30866458Sdfr	gifp = GIF2IFP(sc);
309135581Smarcel	if (gifp == NULL || (gifp->if_flags & IFF_UP) == 0) {
31066458Sdfr		m_freem(m);
31166458Sdfr		IP6STAT_INC(ip6s_nogif);
31267351Sjhb		return IPPROTO_DONE;
313135581Smarcel	}
31467351Sjhb
315135581Smarcel	otos = ip6->ip6_flow;
31667351Sjhb	m_adj(m, *offp);
31767351Sjhb
318135581Smarcel	switch (proto) {
319135581Smarcel#ifdef INET
320135581Smarcel	case IPPROTO_IPV4:
321135581Smarcel	    {
322135581Smarcel		struct ip *ip;
323135581Smarcel		u_int8_t otos8;
324135581Smarcel		af = AF_INET;
325135581Smarcel		otos8 = (ntohl(otos) >> 20) & 0xff;
32666458Sdfr		if (m->m_len < sizeof(*ip)) {
327171662Smarcel			m = m_pullup(m, sizeof(*ip));
328171662Smarcel			if (!m)
329171662Smarcel				return IPPROTO_DONE;
330171662Smarcel		}
331171662Smarcel		ip = mtod(m, struct ip *);
332171662Smarcel		if (ip_ecn_egress((gifp->if_flags & IFF_LINK1) ?
333171662Smarcel				  ECN_ALLOWED : ECN_NOCARE,
334171662Smarcel				  &otos8, &ip->ip_tos) == 0) {
335135581Smarcel			m_freem(m);
336135581Smarcel			return IPPROTO_DONE;
33766937Sdfr		}
338135581Smarcel		break;
33966937Sdfr	    }
34066937Sdfr#endif /* INET */
34166937Sdfr#ifdef INET6
342135581Smarcel	case IPPROTO_IPV6:
34366937Sdfr	    {
34466937Sdfr		struct ip6_hdr *ip6;
345135581Smarcel		af = AF_INET6;
346135581Smarcel		if (m->m_len < sizeof(*ip6)) {
34766937Sdfr			m = m_pullup(m, sizeof(*ip6));
348135581Smarcel			if (!m)
34966937Sdfr				return IPPROTO_DONE;
35066937Sdfr		}
35166937Sdfr		ip6 = mtod(m, struct ip6_hdr *);
352135581Smarcel		if (ip6_ecn_egress((gifp->if_flags & IFF_LINK1) ?
35366937Sdfr				   ECN_ALLOWED : ECN_NOCARE,
35466937Sdfr				   &otos, &ip6->ip6_flow) == 0) {
355135581Smarcel			m_freem(m);
356135581Smarcel			return IPPROTO_DONE;
357173970Sjasone		}
35866937Sdfr		break;
359150627Sjhb	    }
360150627Sjhb#endif
361150627Sjhb	case IPPROTO_ETHERIP:
362150627Sjhb		af = AF_LINK;
363150627Sjhb		break;
364150627Sjhb
365150627Sjhb	default:
366150627Sjhb		IP6STAT_INC(ip6s_nogif);
367150627Sjhb		m_freem(m);
368150627Sjhb		return IPPROTO_DONE;
369150627Sjhb	}
370150627Sjhb
371150627Sjhb	gif_input(m, af, gifp);
372150627Sjhb	return IPPROTO_DONE;
373150627Sjhb}
374150627Sjhb
375150627Sjhb/*
376150627Sjhb * validate outer address.
377150627Sjhb */
378177276Spjdstatic int
379177276Spjdgif_validate6(const struct ip6_hdr *ip6, struct gif_softc *sc,
380177276Spjd    struct ifnet *ifp)
381177276Spjd{
382177276Spjd	struct sockaddr_in6 *src, *dst;
383177276Spjd
384177276Spjd	src = (struct sockaddr_in6 *)sc->gif_psrc;
385177276Spjd	dst = (struct sockaddr_in6 *)sc->gif_pdst;
386177276Spjd
387177276Spjd	/*
388177276Spjd	 * Check for address match.  Note that the check is for an incoming
389262004Smarcel	 * packet.  We should compare the *source* address in our configuration
390262004Smarcel	 * and the *destination* address of the packet, and vice versa.
391262004Smarcel	 */
392262004Smarcel	if (!IN6_ARE_ADDR_EQUAL(&src->sin6_addr, &ip6->ip6_dst) ||
393262004Smarcel	    !IN6_ARE_ADDR_EQUAL(&dst->sin6_addr, &ip6->ip6_src))
394262004Smarcel		return 0;
395262004Smarcel
396262004Smarcel	/* martian filters on outer source - done in ip6_input */
397262004Smarcel
398262004Smarcel	/* ingress filters on outer source */
399262004Smarcel	if ((GIF2IFP(sc)->if_flags & IFF_LINK2) == 0 && ifp) {
400262004Smarcel		struct sockaddr_in6 sin6;
401262004Smarcel		struct rtentry *rt;
402262004Smarcel
403262004Smarcel		bzero(&sin6, sizeof(sin6));
404262004Smarcel		sin6.sin6_family = AF_INET6;
405262004Smarcel		sin6.sin6_len = sizeof(struct sockaddr_in6);
406262004Smarcel		sin6.sin6_addr = ip6->ip6_src;
407262004Smarcel		sin6.sin6_scope_id = 0; /* XXX */
408262004Smarcel
409262004Smarcel		rt = in6_rtalloc1((struct sockaddr *)&sin6, 0, 0UL,
410262004Smarcel		    sc->gif_fibnum);
411262004Smarcel		if (!rt || rt->rt_ifp != ifp) {
412262004Smarcel#if 0
413262004Smarcel			char ip6buf[INET6_ADDRSTRLEN];
414262004Smarcel			log(LOG_WARNING, "%s: packet from %s dropped "
415262004Smarcel			    "due to ingress filter\n", if_name(GIF2IFP(sc)),
416262004Smarcel			    ip6_sprintf(ip6buf, &sin6.sin6_addr));
41766458Sdfr#endif
418			if (rt)
419				RTFREE_LOCKED(rt);
420			return 0;
421		}
422		RTFREE_LOCKED(rt);
423	}
424
425	return 128 * 2;
426}
427
428/*
429 * we know that we are in IFF_UP, outer address available, and outer family
430 * matched the physical addr family.  see gif_encapcheck().
431 * sanity check for arg should have been done in the caller.
432 */
433int
434gif_encapcheck6(const struct mbuf *m, int off, int proto, void *arg)
435{
436	struct ip6_hdr ip6;
437	struct gif_softc *sc;
438	struct ifnet *ifp;
439
440	/* sanity check done in caller */
441	sc = (struct gif_softc *)arg;
442
443	/* LINTED const cast */
444	m_copydata(m, 0, sizeof(ip6), (caddr_t)&ip6);
445	ifp = ((m->m_flags & M_PKTHDR) != 0) ? m->m_pkthdr.rcvif : NULL;
446
447	return gif_validate6(&ip6, sc, ifp);
448}
449
450int
451in6_gif_attach(struct gif_softc *sc)
452{
453	sc->encap_cookie6 = encap_attach_func(AF_INET6, -1, gif_encapcheck,
454	    (void *)&in6_gif_protosw, sc);
455	if (sc->encap_cookie6 == NULL)
456		return EEXIST;
457	return 0;
458}
459
460int
461in6_gif_detach(struct gif_softc *sc)
462{
463	int error;
464
465	error = encap_detach(sc->encap_cookie6);
466	if (error == 0)
467		sc->encap_cookie6 = NULL;
468	return error;
469}
470