if_gif.c revision 123922
1/*	$FreeBSD: head/sys/net/if_gif.c 123922 2003-12-28 03:56:00Z sam $	*/
2/*	$KAME: if_gif.c,v 1.87 2001/10/19 08:50:27 itojun Exp $	*/
3
4/*
5 * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 *    notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 * 3. Neither the name of the project nor the names of its contributors
17 *    may be used to endorse or promote products derived from this software
18 *    without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGE.
31 */
32
33#include "opt_inet.h"
34#include "opt_inet6.h"
35#include "opt_mac.h"
36
37#include <sys/param.h>
38#include <sys/systm.h>
39#include <sys/kernel.h>
40#include <sys/mac.h>
41#include <sys/malloc.h>
42#include <sys/mbuf.h>
43#include <sys/socket.h>
44#include <sys/sockio.h>
45#include <sys/errno.h>
46#include <sys/time.h>
47#include <sys/sysctl.h>
48#include <sys/syslog.h>
49#include <sys/protosw.h>
50#include <sys/conf.h>
51#include <machine/cpu.h>
52
53#include <net/if.h>
54#include <net/if_types.h>
55#include <net/netisr.h>
56#include <net/route.h>
57#include <net/bpf.h>
58
59#include <netinet/in.h>
60#include <netinet/in_systm.h>
61#include <netinet/ip.h>
62#ifdef	INET
63#include <netinet/in_var.h>
64#include <netinet/in_gif.h>
65#include <netinet/ip_var.h>
66#endif	/* INET */
67
68#ifdef INET6
69#ifndef INET
70#include <netinet/in.h>
71#endif
72#include <netinet6/in6_var.h>
73#include <netinet/ip6.h>
74#include <netinet6/ip6_var.h>
75#include <netinet6/in6_gif.h>
76#include <netinet6/ip6protosw.h>
77#endif /* INET6 */
78
79#include <netinet/ip_encap.h>
80#include <net/if_gif.h>
81
82#include <net/net_osdep.h>
83
84#define GIFNAME		"gif"
85
86static MALLOC_DEFINE(M_GIF, "gif", "Generic Tunnel Interface");
87static LIST_HEAD(, gif_softc) gif_softc_list;
88
89void	(*ng_gif_input_p)(struct ifnet *ifp, struct mbuf **mp, int af);
90void	(*ng_gif_input_orphan_p)(struct ifnet *ifp, struct mbuf *m, int af);
91void	(*ng_gif_attach_p)(struct ifnet *ifp);
92void	(*ng_gif_detach_p)(struct ifnet *ifp);
93
94int	gif_clone_create(struct if_clone *, int);
95void	gif_clone_destroy(struct ifnet *);
96
97struct if_clone gif_cloner = IF_CLONE_INITIALIZER("gif",
98    gif_clone_create, gif_clone_destroy, 0, IF_MAXUNIT);
99
100static int gifmodevent(module_t, int, void *);
101
102SYSCTL_DECL(_net_link);
103SYSCTL_NODE(_net_link, IFT_GIF, gif, CTLFLAG_RW, 0,
104    "Generic Tunnel Interface");
105#ifndef MAX_GIF_NEST
106/*
107 * This macro controls the default upper limitation on nesting of gif tunnels.
108 * Since, setting a large value to this macro with a careless configuration
109 * may introduce system crash, we don't allow any nestings by default.
110 * If you need to configure nested gif tunnels, you can define this macro
111 * in your kernel configuration file.  However, if you do so, please be
112 * careful to configure the tunnels so that it won't make a loop.
113 */
114#define MAX_GIF_NEST 1
115#endif
116static int max_gif_nesting = MAX_GIF_NEST;
117SYSCTL_INT(_net_link_gif, OID_AUTO, max_nesting, CTLFLAG_RW,
118    &max_gif_nesting, 0, "Max nested tunnels");
119
120/*
121 * By default, we disallow creation of multiple tunnels between the same
122 * pair of addresses.  Some applications require this functionality so
123 * we allow control over this check here.
124 */
125#ifdef XBONEHACK
126static int parallel_tunnels = 1;
127#else
128static int parallel_tunnels = 0;
129#endif
130SYSCTL_INT(_net_link_gif, OID_AUTO, parallel_tunnels, CTLFLAG_RW,
131    &parallel_tunnels, 0, "Allow parallel tunnels?");
132
133int
134gif_clone_create(ifc, unit)
135	struct if_clone *ifc;
136	int unit;
137{
138	struct gif_softc *sc;
139
140	sc = malloc (sizeof(struct gif_softc), M_GIF, M_WAITOK);
141	bzero(sc, sizeof(struct gif_softc));
142
143	sc->gif_if.if_softc = sc;
144	if_initname(&sc->gif_if, ifc->ifc_name, unit);
145
146	gifattach0(sc);
147
148	LIST_INSERT_HEAD(&gif_softc_list, sc, gif_list);
149	return (0);
150}
151
152void
153gifattach0(sc)
154	struct gif_softc *sc;
155{
156
157	sc->encap_cookie4 = sc->encap_cookie6 = NULL;
158
159	sc->gif_if.if_addrlen = 0;
160	sc->gif_if.if_mtu    = GIF_MTU;
161	sc->gif_if.if_flags  = IFF_POINTOPOINT | IFF_MULTICAST;
162#if 0
163	/* turn off ingress filter */
164	sc->gif_if.if_flags  |= IFF_LINK2;
165#endif
166	sc->gif_if.if_ioctl  = gif_ioctl;
167	sc->gif_if.if_output = gif_output;
168	sc->gif_if.if_type   = IFT_GIF;
169	sc->gif_if.if_snd.ifq_maxlen = IFQ_MAXLEN;
170	if_attach(&sc->gif_if);
171	bpfattach(&sc->gif_if, DLT_NULL, sizeof(u_int));
172	if (ng_gif_attach_p != NULL)
173		(*ng_gif_attach_p)(&sc->gif_if);
174}
175
176void
177gif_clone_destroy(ifp)
178	struct ifnet *ifp;
179{
180	int err;
181	struct gif_softc *sc = ifp->if_softc;
182
183	gif_delete_tunnel(&sc->gif_if);
184	LIST_REMOVE(sc, gif_list);
185#ifdef INET6
186	if (sc->encap_cookie6 != NULL) {
187		err = encap_detach(sc->encap_cookie6);
188		KASSERT(err == 0, ("Unexpected error detaching encap_cookie6"));
189	}
190#endif
191#ifdef INET
192	if (sc->encap_cookie4 != NULL) {
193		err = encap_detach(sc->encap_cookie4);
194		KASSERT(err == 0, ("Unexpected error detaching encap_cookie4"));
195	}
196#endif
197
198	if (ng_gif_detach_p != NULL)
199		(*ng_gif_detach_p)(ifp);
200	bpfdetach(ifp);
201	if_detach(ifp);
202
203	free(sc, M_GIF);
204}
205
206static int
207gifmodevent(mod, type, data)
208	module_t mod;
209	int type;
210	void *data;
211{
212
213	switch (type) {
214	case MOD_LOAD:
215		LIST_INIT(&gif_softc_list);
216		if_clone_attach(&gif_cloner);
217
218#ifdef INET6
219		ip6_gif_hlim = GIF_HLIM;
220#endif
221
222		break;
223	case MOD_UNLOAD:
224		if_clone_detach(&gif_cloner);
225
226		while (!LIST_EMPTY(&gif_softc_list))
227			gif_clone_destroy(&LIST_FIRST(&gif_softc_list)->gif_if);
228
229#ifdef INET6
230		ip6_gif_hlim = 0;
231#endif
232		break;
233	}
234	return 0;
235}
236
237static moduledata_t gif_mod = {
238	"if_gif",
239	gifmodevent,
240	0
241};
242
243DECLARE_MODULE(if_gif, gif_mod, SI_SUB_PSEUDO, SI_ORDER_ANY);
244MODULE_VERSION(if_gif, 1);
245
246int
247gif_encapcheck(m, off, proto, arg)
248	const struct mbuf *m;
249	int off;
250	int proto;
251	void *arg;
252{
253	struct ip ip;
254	struct gif_softc *sc;
255
256	sc = (struct gif_softc *)arg;
257	if (sc == NULL)
258		return 0;
259
260	if ((sc->gif_if.if_flags & IFF_UP) == 0)
261		return 0;
262
263	/* no physical address */
264	if (!sc->gif_psrc || !sc->gif_pdst)
265		return 0;
266
267	switch (proto) {
268#ifdef INET
269	case IPPROTO_IPV4:
270		break;
271#endif
272#ifdef INET6
273	case IPPROTO_IPV6:
274		break;
275#endif
276	default:
277		return 0;
278	}
279
280	/* Bail on short packets */
281	if (m->m_pkthdr.len < sizeof(ip))
282		return 0;
283
284	m_copydata(m, 0, sizeof(ip), (caddr_t)&ip);
285
286	switch (ip.ip_v) {
287#ifdef INET
288	case 4:
289		if (sc->gif_psrc->sa_family != AF_INET ||
290		    sc->gif_pdst->sa_family != AF_INET)
291			return 0;
292		return gif_encapcheck4(m, off, proto, arg);
293#endif
294#ifdef INET6
295	case 6:
296		if (m->m_pkthdr.len < sizeof(struct ip6_hdr))
297			return 0;
298		if (sc->gif_psrc->sa_family != AF_INET6 ||
299		    sc->gif_pdst->sa_family != AF_INET6)
300			return 0;
301		return gif_encapcheck6(m, off, proto, arg);
302#endif
303	default:
304		return 0;
305	}
306}
307
308int
309gif_output(ifp, m, dst, rt)
310	struct ifnet *ifp;
311	struct mbuf *m;
312	struct sockaddr *dst;
313	struct rtentry *rt;	/* added in net2 */
314{
315	struct gif_softc *sc = (struct gif_softc*)ifp;
316	int error = 0;
317	static int called = 0;	/* XXX: MUTEX */
318
319#ifdef MAC
320	error = mac_check_ifnet_transmit(ifp, m);
321	if (error) {
322		m_freem(m);
323		goto end;
324	}
325#endif
326
327	/*
328	 * gif may cause infinite recursion calls when misconfigured.
329	 * We'll prevent this by introducing upper limit.
330	 * XXX: this mechanism may introduce another problem about
331	 *      mutual exclusion of the variable CALLED, especially if we
332	 *      use kernel thread.
333	 */
334	if (++called > max_gif_nesting) {
335		log(LOG_NOTICE,
336		    "gif_output: recursively called too many times(%d)\n",
337		    called);
338		m_freem(m);
339		error = EIO;	/* is there better errno? */
340		goto end;
341	}
342
343	m->m_flags &= ~(M_BCAST|M_MCAST);
344	if (!(ifp->if_flags & IFF_UP) ||
345	    sc->gif_psrc == NULL || sc->gif_pdst == NULL) {
346		m_freem(m);
347		error = ENETDOWN;
348		goto end;
349	}
350
351	if (ifp->if_bpf) {
352		u_int32_t af = dst->sa_family;
353		bpf_mtap2(ifp->if_bpf, &af, sizeof(af), m);
354	}
355	ifp->if_opackets++;
356	ifp->if_obytes += m->m_pkthdr.len;
357
358	/* inner AF-specific encapsulation */
359
360	/* XXX should we check if our outer source is legal? */
361
362	/* dispatch to output logic based on outer AF */
363	switch (sc->gif_psrc->sa_family) {
364#ifdef INET
365	case AF_INET:
366		error = in_gif_output(ifp, dst->sa_family, m);
367		break;
368#endif
369#ifdef INET6
370	case AF_INET6:
371		error = in6_gif_output(ifp, dst->sa_family, m);
372		break;
373#endif
374	default:
375		m_freem(m);
376		error = ENETDOWN;
377		goto end;
378	}
379
380  end:
381	called = 0;		/* reset recursion counter */
382	if (error)
383		ifp->if_oerrors++;
384	return error;
385}
386
387void
388gif_input(m, af, ifp)
389	struct mbuf *m;
390	int af;
391	struct ifnet *ifp;
392{
393	int isr;
394
395	if (ifp == NULL) {
396		/* just in case */
397		m_freem(m);
398		return;
399	}
400
401	m->m_pkthdr.rcvif = ifp;
402
403#ifdef MAC
404	mac_create_mbuf_from_ifnet(ifp, m);
405#endif
406
407	if (ifp->if_bpf) {
408		u_int32_t af1 = af;
409		bpf_mtap2(ifp->if_bpf, &af1, sizeof(af1), m);
410	}
411
412	if (ng_gif_input_p != NULL) {
413		(*ng_gif_input_p)(ifp, &m, af);
414		if (m == NULL)
415			return;
416	}
417
418	/*
419	 * Put the packet to the network layer input queue according to the
420	 * specified address family.
421	 * Note: older versions of gif_input directly called network layer
422	 * input functions, e.g. ip6_input, here.  We changed the policy to
423	 * prevent too many recursive calls of such input functions, which
424	 * might cause kernel panic.  But the change may introduce another
425	 * problem; if the input queue is full, packets are discarded.
426	 * The kernel stack overflow really happened, and we believed
427	 * queue-full rarely occurs, so we changed the policy.
428	 */
429	switch (af) {
430#ifdef INET
431	case AF_INET:
432		isr = NETISR_IP;
433		break;
434#endif
435#ifdef INET6
436	case AF_INET6:
437		isr = NETISR_IPV6;
438		break;
439#endif
440	default:
441		if (ng_gif_input_orphan_p != NULL)
442			(*ng_gif_input_orphan_p)(ifp, m, af);
443		else
444			m_freem(m);
445		return;
446	}
447
448	ifp->if_ipackets++;
449	ifp->if_ibytes += m->m_pkthdr.len;
450	netisr_dispatch(isr, m);
451}
452
453/* XXX how should we handle IPv6 scope on SIOC[GS]IFPHYADDR? */
454int
455gif_ioctl(ifp, cmd, data)
456	struct ifnet *ifp;
457	u_long cmd;
458	caddr_t data;
459{
460	struct gif_softc *sc  = (struct gif_softc*)ifp;
461	struct ifreq     *ifr = (struct ifreq*)data;
462	int error = 0, size;
463	struct sockaddr *dst, *src;
464#ifdef	SIOCSIFMTU /* xxx */
465	u_long mtu;
466#endif
467
468	switch (cmd) {
469	case SIOCSIFADDR:
470		ifp->if_flags |= IFF_UP;
471		break;
472
473	case SIOCSIFDSTADDR:
474		break;
475
476	case SIOCADDMULTI:
477	case SIOCDELMULTI:
478		break;
479
480#ifdef	SIOCSIFMTU /* xxx */
481	case SIOCGIFMTU:
482		break;
483
484	case SIOCSIFMTU:
485		mtu = ifr->ifr_mtu;
486		if (mtu < GIF_MTU_MIN || mtu > GIF_MTU_MAX)
487			return (EINVAL);
488		ifp->if_mtu = mtu;
489		break;
490#endif /* SIOCSIFMTU */
491
492#ifdef INET
493	case SIOCSIFPHYADDR:
494#endif
495#ifdef INET6
496	case SIOCSIFPHYADDR_IN6:
497#endif /* INET6 */
498	case SIOCSLIFPHYADDR:
499		switch (cmd) {
500#ifdef INET
501		case SIOCSIFPHYADDR:
502			src = (struct sockaddr *)
503				&(((struct in_aliasreq *)data)->ifra_addr);
504			dst = (struct sockaddr *)
505				&(((struct in_aliasreq *)data)->ifra_dstaddr);
506			break;
507#endif
508#ifdef INET6
509		case SIOCSIFPHYADDR_IN6:
510			src = (struct sockaddr *)
511				&(((struct in6_aliasreq *)data)->ifra_addr);
512			dst = (struct sockaddr *)
513				&(((struct in6_aliasreq *)data)->ifra_dstaddr);
514			break;
515#endif
516		case SIOCSLIFPHYADDR:
517			src = (struct sockaddr *)
518				&(((struct if_laddrreq *)data)->addr);
519			dst = (struct sockaddr *)
520				&(((struct if_laddrreq *)data)->dstaddr);
521			break;
522		default:
523			return EINVAL;
524		}
525
526		/* sa_family must be equal */
527		if (src->sa_family != dst->sa_family)
528			return EINVAL;
529
530		/* validate sa_len */
531		switch (src->sa_family) {
532#ifdef INET
533		case AF_INET:
534			if (src->sa_len != sizeof(struct sockaddr_in))
535				return EINVAL;
536			break;
537#endif
538#ifdef INET6
539		case AF_INET6:
540			if (src->sa_len != sizeof(struct sockaddr_in6))
541				return EINVAL;
542			break;
543#endif
544		default:
545			return EAFNOSUPPORT;
546		}
547		switch (dst->sa_family) {
548#ifdef INET
549		case AF_INET:
550			if (dst->sa_len != sizeof(struct sockaddr_in))
551				return EINVAL;
552			break;
553#endif
554#ifdef INET6
555		case AF_INET6:
556			if (dst->sa_len != sizeof(struct sockaddr_in6))
557				return EINVAL;
558			break;
559#endif
560		default:
561			return EAFNOSUPPORT;
562		}
563
564		/* check sa_family looks sane for the cmd */
565		switch (cmd) {
566		case SIOCSIFPHYADDR:
567			if (src->sa_family == AF_INET)
568				break;
569			return EAFNOSUPPORT;
570#ifdef INET6
571		case SIOCSIFPHYADDR_IN6:
572			if (src->sa_family == AF_INET6)
573				break;
574			return EAFNOSUPPORT;
575#endif /* INET6 */
576		case SIOCSLIFPHYADDR:
577			/* checks done in the above */
578			break;
579		}
580
581		error = gif_set_tunnel(&sc->gif_if, src, dst);
582		break;
583
584#ifdef SIOCDIFPHYADDR
585	case SIOCDIFPHYADDR:
586		gif_delete_tunnel(&sc->gif_if);
587		break;
588#endif
589
590	case SIOCGIFPSRCADDR:
591#ifdef INET6
592	case SIOCGIFPSRCADDR_IN6:
593#endif /* INET6 */
594		if (sc->gif_psrc == NULL) {
595			error = EADDRNOTAVAIL;
596			goto bad;
597		}
598		src = sc->gif_psrc;
599		switch (cmd) {
600#ifdef INET
601		case SIOCGIFPSRCADDR:
602			dst = &ifr->ifr_addr;
603			size = sizeof(ifr->ifr_addr);
604			break;
605#endif /* INET */
606#ifdef INET6
607		case SIOCGIFPSRCADDR_IN6:
608			dst = (struct sockaddr *)
609				&(((struct in6_ifreq *)data)->ifr_addr);
610			size = sizeof(((struct in6_ifreq *)data)->ifr_addr);
611			break;
612#endif /* INET6 */
613		default:
614			error = EADDRNOTAVAIL;
615			goto bad;
616		}
617		if (src->sa_len > size)
618			return EINVAL;
619		bcopy((caddr_t)src, (caddr_t)dst, src->sa_len);
620		break;
621
622	case SIOCGIFPDSTADDR:
623#ifdef INET6
624	case SIOCGIFPDSTADDR_IN6:
625#endif /* INET6 */
626		if (sc->gif_pdst == NULL) {
627			error = EADDRNOTAVAIL;
628			goto bad;
629		}
630		src = sc->gif_pdst;
631		switch (cmd) {
632#ifdef INET
633		case SIOCGIFPDSTADDR:
634			dst = &ifr->ifr_addr;
635			size = sizeof(ifr->ifr_addr);
636			break;
637#endif /* INET */
638#ifdef INET6
639		case SIOCGIFPDSTADDR_IN6:
640			dst = (struct sockaddr *)
641				&(((struct in6_ifreq *)data)->ifr_addr);
642			size = sizeof(((struct in6_ifreq *)data)->ifr_addr);
643			break;
644#endif /* INET6 */
645		default:
646			error = EADDRNOTAVAIL;
647			goto bad;
648		}
649		if (src->sa_len > size)
650			return EINVAL;
651		bcopy((caddr_t)src, (caddr_t)dst, src->sa_len);
652		break;
653
654	case SIOCGLIFPHYADDR:
655		if (sc->gif_psrc == NULL || sc->gif_pdst == NULL) {
656			error = EADDRNOTAVAIL;
657			goto bad;
658		}
659
660		/* copy src */
661		src = sc->gif_psrc;
662		dst = (struct sockaddr *)
663			&(((struct if_laddrreq *)data)->addr);
664		size = sizeof(((struct if_laddrreq *)data)->addr);
665		if (src->sa_len > size)
666			return EINVAL;
667		bcopy((caddr_t)src, (caddr_t)dst, src->sa_len);
668
669		/* copy dst */
670		src = sc->gif_pdst;
671		dst = (struct sockaddr *)
672			&(((struct if_laddrreq *)data)->dstaddr);
673		size = sizeof(((struct if_laddrreq *)data)->dstaddr);
674		if (src->sa_len > size)
675			return EINVAL;
676		bcopy((caddr_t)src, (caddr_t)dst, src->sa_len);
677		break;
678
679	case SIOCSIFFLAGS:
680		/* if_ioctl() takes care of it */
681		break;
682
683	default:
684		error = EINVAL;
685		break;
686	}
687 bad:
688	return error;
689}
690
691int
692gif_set_tunnel(ifp, src, dst)
693	struct ifnet *ifp;
694	struct sockaddr *src;
695	struct sockaddr *dst;
696{
697	struct gif_softc *sc = (struct gif_softc *)ifp;
698	struct gif_softc *sc2;
699	struct sockaddr *osrc, *odst, *sa;
700	int s;
701	int error = 0;
702
703	s = splnet();
704
705	LIST_FOREACH(sc2, &gif_softc_list, gif_list) {
706		if (sc2 == sc)
707			continue;
708		if (!sc2->gif_pdst || !sc2->gif_psrc)
709			continue;
710		if (sc2->gif_pdst->sa_family != dst->sa_family ||
711		    sc2->gif_pdst->sa_len != dst->sa_len ||
712		    sc2->gif_psrc->sa_family != src->sa_family ||
713		    sc2->gif_psrc->sa_len != src->sa_len)
714			continue;
715
716		/*
717		 * Disallow parallel tunnels unless instructed
718		 * otherwise.
719		 */
720		if (!parallel_tunnels &&
721		    bcmp(sc2->gif_pdst, dst, dst->sa_len) == 0 &&
722		    bcmp(sc2->gif_psrc, src, src->sa_len) == 0) {
723			error = EADDRNOTAVAIL;
724			goto bad;
725		}
726
727		/* XXX both end must be valid? (I mean, not 0.0.0.0) */
728	}
729
730	/* XXX we can detach from both, but be polite just in case */
731	if (sc->gif_psrc)
732		switch (sc->gif_psrc->sa_family) {
733#ifdef INET
734		case AF_INET:
735			(void)in_gif_detach(sc);
736			break;
737#endif
738#ifdef INET6
739		case AF_INET6:
740			(void)in6_gif_detach(sc);
741			break;
742#endif
743		}
744
745	osrc = sc->gif_psrc;
746	sa = (struct sockaddr *)malloc(src->sa_len, M_IFADDR, M_WAITOK);
747	bcopy((caddr_t)src, (caddr_t)sa, src->sa_len);
748	sc->gif_psrc = sa;
749
750	odst = sc->gif_pdst;
751	sa = (struct sockaddr *)malloc(dst->sa_len, M_IFADDR, M_WAITOK);
752	bcopy((caddr_t)dst, (caddr_t)sa, dst->sa_len);
753	sc->gif_pdst = sa;
754
755	switch (sc->gif_psrc->sa_family) {
756#ifdef INET
757	case AF_INET:
758		error = in_gif_attach(sc);
759		break;
760#endif
761#ifdef INET6
762	case AF_INET6:
763		error = in6_gif_attach(sc);
764		break;
765#endif
766	}
767	if (error) {
768		/* rollback */
769		free((caddr_t)sc->gif_psrc, M_IFADDR);
770		free((caddr_t)sc->gif_pdst, M_IFADDR);
771		sc->gif_psrc = osrc;
772		sc->gif_pdst = odst;
773		goto bad;
774	}
775
776	if (osrc)
777		free((caddr_t)osrc, M_IFADDR);
778	if (odst)
779		free((caddr_t)odst, M_IFADDR);
780
781	if (sc->gif_psrc && sc->gif_pdst)
782		ifp->if_flags |= IFF_RUNNING;
783	else
784		ifp->if_flags &= ~IFF_RUNNING;
785	splx(s);
786
787	return 0;
788
789 bad:
790	if (sc->gif_psrc && sc->gif_pdst)
791		ifp->if_flags |= IFF_RUNNING;
792	else
793		ifp->if_flags &= ~IFF_RUNNING;
794	splx(s);
795
796	return error;
797}
798
799void
800gif_delete_tunnel(ifp)
801	struct ifnet *ifp;
802{
803	struct gif_softc *sc = (struct gif_softc *)ifp;
804	int s;
805
806	s = splnet();
807
808	if (sc->gif_psrc) {
809		free((caddr_t)sc->gif_psrc, M_IFADDR);
810		sc->gif_psrc = NULL;
811	}
812	if (sc->gif_pdst) {
813		free((caddr_t)sc->gif_pdst, M_IFADDR);
814		sc->gif_pdst = NULL;
815	}
816	/* it is safe to detach from both */
817#ifdef INET
818	(void)in_gif_detach(sc);
819#endif
820#ifdef INET6
821	(void)in6_gif_detach(sc);
822#endif
823
824	if (sc->gif_psrc && sc->gif_pdst)
825		ifp->if_flags |= IFF_RUNNING;
826	else
827		ifp->if_flags &= ~IFF_RUNNING;
828	splx(s);
829}
830