if_gif.c revision 102968
1/*	$FreeBSD: head/sys/net/if_gif.c 102968 2002-09-05 15:35:38Z sobomax $	*/
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 *);
101void gif_delete_tunnel(struct gif_softc *);
102static int gif_encapcheck(const struct mbuf *, int, int, void *);
103
104#ifdef INET
105extern  struct domain inetdomain;
106struct protosw in_gif_protosw =
107{ SOCK_RAW,	&inetdomain,	0/*IPPROTO_IPV[46]*/,	PR_ATOMIC|PR_ADDR,
108  in_gif_input,	(pr_output_t*)rip_output, 0,		rip_ctloutput,
109  0,
110  0,		0,		0,		0,
111  &rip_usrreqs
112};
113#endif
114#ifdef INET6
115extern  struct domain inet6domain;
116struct ip6protosw in6_gif_protosw =
117{ SOCK_RAW,	&inet6domain,	0/*IPPROTO_IPV[46]*/,	PR_ATOMIC|PR_ADDR,
118  in6_gif_input, rip6_output,	0,		rip6_ctloutput,
119  0,
120  0,		0,		0,		0,
121  &rip6_usrreqs
122};
123#endif
124
125SYSCTL_DECL(_net_link);
126SYSCTL_NODE(_net_link, IFT_GIF, gif, CTLFLAG_RW, 0,
127    "Generic Tunnel Interface");
128#ifndef MAX_GIF_NEST
129/*
130 * This macro controls the default upper limitation on nesting of gif tunnels.
131 * Since, setting a large value to this macro with a careless configuration
132 * may introduce system crash, we don't allow any nestings by default.
133 * If you need to configure nested gif tunnels, you can define this macro
134 * in your kernel configuration file.  However, if you do so, please be
135 * careful to configure the tunnels so that it won't make a loop.
136 */
137#define MAX_GIF_NEST 1
138#endif
139static int max_gif_nesting = MAX_GIF_NEST;
140SYSCTL_INT(_net_link_gif, OID_AUTO, max_nesting, CTLFLAG_RW,
141    &max_gif_nesting, 0, "Max nested tunnels");
142
143/*
144 * By default, we disallow creation of multiple tunnels between the same
145 * pair of addresses.  Some applications require this functionality so
146 * we allow control over this check here.
147 */
148#ifdef XBONEHACK
149static int parallel_tunnels = 1;
150#else
151static int parallel_tunnels = 0;
152#endif
153SYSCTL_INT(_net_link_gif, OID_AUTO, parallel_tunnels, CTLFLAG_RW,
154    &parallel_tunnels, 0, "Allow parallel tunnels?");
155
156int
157gif_clone_create(ifc, unit)
158	struct if_clone *ifc;
159	int unit;
160{
161	struct gif_softc *sc;
162
163	sc = malloc (sizeof(struct gif_softc), M_GIF, M_WAITOK);
164	bzero(sc, sizeof(struct gif_softc));
165
166	sc->gif_if.if_softc = sc;
167	sc->gif_if.if_name = GIFNAME;
168	sc->gif_if.if_unit = unit;
169
170	sc->encap_cookie4 = sc->encap_cookie6 = NULL;
171#ifdef INET
172	sc->encap_cookie4 = encap_attach_func(AF_INET, -1,
173	    gif_encapcheck, (struct protosw*)&in_gif_protosw, sc);
174	if (sc->encap_cookie4 == NULL) {
175		printf("%s: unable to attach encap4\n", if_name(&sc->gif_if));
176		free(sc, M_GIF);
177		return (EIO);	/* XXX */
178	}
179#endif
180#ifdef INET6
181	sc->encap_cookie6 = encap_attach_func(AF_INET6, -1,
182	    gif_encapcheck, (struct protosw *)&in6_gif_protosw, sc);
183	if (sc->encap_cookie6 == NULL) {
184		if (sc->encap_cookie4) {
185			encap_detach(sc->encap_cookie4);
186			sc->encap_cookie4 = NULL;
187		}
188		printf("%s: unable to attach encap6\n", if_name(&sc->gif_if));
189		free(sc, M_GIF);
190		return (EIO);	/* XXX */
191	}
192#endif
193
194	sc->gif_if.if_mtu    = GIF_MTU;
195	sc->gif_if.if_flags  = IFF_POINTOPOINT | IFF_MULTICAST;
196#if 0
197	/* turn off ingress filter */
198	sc->gif_if.if_flags  |= IFF_LINK2;
199#endif
200	sc->gif_if.if_ioctl  = gif_ioctl;
201	sc->gif_if.if_output = gif_output;
202	sc->gif_if.if_type   = IFT_GIF;
203	sc->gif_if.if_snd.ifq_maxlen = IFQ_MAXLEN;
204	sc->called = 0;
205	if_attach(&sc->gif_if);
206	bpfattach(&sc->gif_if, DLT_NULL, sizeof(u_int));
207	if (ng_gif_attach_p != NULL)
208		(*ng_gif_attach_p)(&sc->gif_if);
209	LIST_INSERT_HEAD(&gif_softc_list, sc, gif_link);
210	return (0);
211}
212
213void
214gif_clone_destroy(ifp)
215	struct ifnet *ifp;
216{
217	int err;
218	struct gif_softc *sc = ifp->if_softc;
219
220	gif_delete_tunnel(sc);
221	LIST_REMOVE(sc, gif_link);
222	if (sc->encap_cookie4 != NULL) {
223		err = encap_detach(sc->encap_cookie4);
224		KASSERT(err == 0, ("Unexpected error detaching encap_cookie4"));
225	}
226	if (sc->encap_cookie6 != NULL) {
227		err = encap_detach(sc->encap_cookie6);
228		KASSERT(err == 0, ("Unexpected error detaching encap_cookie6"));
229	}
230
231	if (ng_gif_detach_p != NULL)
232		(*ng_gif_detach_p)(ifp);
233	bpfdetach(ifp);
234	if_detach(ifp);
235
236	free(sc, M_GIF);
237}
238
239static int
240gifmodevent(mod, type, data)
241	module_t mod;
242	int type;
243	void *data;
244{
245
246	switch (type) {
247	case MOD_LOAD:
248		LIST_INIT(&gif_softc_list);
249		if_clone_attach(&gif_cloner);
250
251#ifdef INET6
252		ip6_gif_hlim = GIF_HLIM;
253#endif
254
255		break;
256	case MOD_UNLOAD:
257		if_clone_detach(&gif_cloner);
258
259		while (!LIST_EMPTY(&gif_softc_list))
260			gif_clone_destroy(&LIST_FIRST(&gif_softc_list)->gif_if);
261
262#ifdef INET6
263		ip6_gif_hlim = 0;
264#endif
265		break;
266	}
267	return 0;
268}
269
270static moduledata_t gif_mod = {
271	"if_gif",
272	gifmodevent,
273	0
274};
275
276DECLARE_MODULE(if_gif, gif_mod, SI_SUB_PSEUDO, SI_ORDER_ANY);
277MODULE_VERSION(if_gif, 1);
278
279static int
280gif_encapcheck(m, off, proto, arg)
281	const struct mbuf *m;
282	int off;
283	int proto;
284	void *arg;
285{
286	struct ip ip;
287	struct gif_softc *sc;
288
289	sc = (struct gif_softc *)arg;
290	if (sc == NULL)
291		return 0;
292
293	if ((sc->gif_if.if_flags & IFF_UP) == 0)
294		return 0;
295
296	/* no physical address */
297	if (!sc->gif_psrc || !sc->gif_pdst)
298		return 0;
299
300	switch (proto) {
301#ifdef INET
302	case IPPROTO_IPV4:
303		break;
304#endif
305#ifdef INET6
306	case IPPROTO_IPV6:
307		break;
308#endif
309	default:
310		return 0;
311	}
312
313	m_copydata(m, 0, sizeof(ip), (caddr_t)&ip);
314
315	switch (ip.ip_v) {
316#ifdef INET
317	case 4:
318		if (sc->gif_psrc->sa_family != AF_INET ||
319		    sc->gif_pdst->sa_family != AF_INET)
320			return 0;
321		return gif_encapcheck4(m, off, proto, arg);
322#endif
323#ifdef INET6
324	case 6:
325		if (sc->gif_psrc->sa_family != AF_INET6 ||
326		    sc->gif_pdst->sa_family != AF_INET6)
327			return 0;
328		return gif_encapcheck6(m, off, proto, arg);
329#endif
330	default:
331		return 0;
332	}
333}
334
335int
336gif_output(ifp, m, dst, rt)
337	struct ifnet *ifp;
338	struct mbuf *m;
339	struct sockaddr *dst;
340	struct rtentry *rt;	/* added in net2 */
341{
342	struct gif_softc *sc = (struct gif_softc*)ifp;
343	int error = 0;
344
345#ifdef MAC
346	error = mac_check_ifnet_transmit(ifp, m);
347	if (error) {
348		m_freem(m);
349		goto end;
350	}
351#endif
352
353	/*
354	 * gif may cause infinite recursion calls when misconfigured.
355	 * We'll prevent this by introducing upper limit.
356	 */
357	if (++(sc->called) > max_gif_nesting) {
358		log(LOG_NOTICE,
359		    "gif_output: recursively called too many times(%d)\n",
360		    sc->called);
361		m_freem(m);
362		error = EIO;	/* is there better errno? */
363		goto end;
364	}
365
366	m->m_flags &= ~(M_BCAST|M_MCAST);
367	if (!(ifp->if_flags & IFF_UP) ||
368	    sc->gif_psrc == NULL || sc->gif_pdst == NULL) {
369		m_freem(m);
370		error = ENETDOWN;
371		goto end;
372	}
373
374	if (ifp->if_bpf) {
375		/*
376		 * We need to prepend the address family as
377		 * a four byte field.  Cons up a dummy header
378		 * to pacify bpf.  This is safe because bpf
379		 * will only read from the mbuf (i.e., it won't
380		 * try to free it or keep a pointer a to it).
381		 */
382		struct mbuf m0;
383		u_int32_t af = dst->sa_family;
384
385		m0.m_next = m;
386		m0.m_len = 4;
387		m0.m_data = (char *)&af;
388
389		bpf_mtap(ifp, &m0);
390	}
391	ifp->if_opackets++;
392	ifp->if_obytes += m->m_pkthdr.len;
393
394	/* inner AF-specific encapsulation */
395
396	/* XXX should we check if our outer source is legal? */
397
398	/* dispatch to output logic based on outer AF */
399	switch (sc->gif_psrc->sa_family) {
400#ifdef INET
401	case AF_INET:
402		error = in_gif_output(ifp, dst->sa_family, m, rt);
403		break;
404#endif
405#ifdef INET6
406	case AF_INET6:
407		error = in6_gif_output(ifp, dst->sa_family, m, rt);
408		break;
409#endif
410	default:
411		m_freem(m);
412		error = ENETDOWN;
413		goto end;
414	}
415
416  end:
417	sc->called = 0;		/* reset recursion counter */
418	if (error)
419		ifp->if_oerrors++;
420	return error;
421}
422
423void
424gif_input(m, af, gifp)
425	struct mbuf *m;
426	int af;
427	struct ifnet *gifp;
428{
429	int isr;
430	struct ifqueue *ifq = 0;
431
432	if (gifp == NULL) {
433		/* just in case */
434		m_freem(m);
435		return;
436	}
437
438	m->m_pkthdr.rcvif = gifp;
439
440#ifdef MAC
441	mac_create_mbuf_from_ifnet(gifp, m);
442#endif
443
444	if (gifp->if_bpf) {
445		/*
446		 * We need to prepend the address family as
447		 * a four byte field.  Cons up a dummy header
448		 * to pacify bpf.  This is safe because bpf
449		 * will only read from the mbuf (i.e., it won't
450		 * try to free it or keep a pointer a to it).
451		 */
452		struct mbuf m0;
453		u_int32_t af1 = af;
454
455		m0.m_next = m;
456		m0.m_len = 4;
457		m0.m_data = (char *)&af1;
458
459		bpf_mtap(gifp, &m0);
460	}
461
462	if (ng_gif_input_p != NULL) {
463		(*ng_gif_input_p)(gifp, &m, af);
464		if (m == NULL)
465			return;
466	}
467
468	/*
469	 * Put the packet to the network layer input queue according to the
470	 * specified address family.
471	 * Note: older versions of gif_input directly called network layer
472	 * input functions, e.g. ip6_input, here.  We changed the policy to
473	 * prevent too many recursive calls of such input functions, which
474	 * might cause kernel panic.  But the change may introduce another
475	 * problem; if the input queue is full, packets are discarded.
476	 * The kernel stack overflow really happened, and we believed
477	 * queue-full rarely occurs, so we changed the policy.
478	 */
479	switch (af) {
480#ifdef INET
481	case AF_INET:
482		ifq = &ipintrq;
483		isr = NETISR_IP;
484		break;
485#endif
486#ifdef INET6
487	case AF_INET6:
488		ifq = &ip6intrq;
489		isr = NETISR_IPV6;
490		break;
491#endif
492	default:
493		if (ng_gif_input_orphan_p != NULL)
494			(*ng_gif_input_orphan_p)(gifp, m, af);
495		else
496			m_freem(m);
497		return;
498	}
499
500	gifp->if_ipackets++;
501	gifp->if_ibytes += m->m_pkthdr.len;
502	(void) IF_HANDOFF(ifq, m, NULL);
503	/* we need schednetisr since the address family may change */
504	schednetisr(isr);
505
506	return;
507}
508
509/* XXX how should we handle IPv6 scope on SIOC[GS]IFPHYADDR? */
510int
511gif_ioctl(ifp, cmd, data)
512	struct ifnet *ifp;
513	u_long cmd;
514	caddr_t data;
515{
516	struct gif_softc *sc  = (struct gif_softc*)ifp;
517	struct ifreq     *ifr = (struct ifreq*)data;
518	int error = 0, size;
519	struct sockaddr *dst, *src;
520	struct sockaddr *sa;
521	int s;
522	struct ifnet *ifp2;
523	struct gif_softc *sc2;
524
525	switch (cmd) {
526	case SIOCSIFADDR:
527		break;
528
529	case SIOCSIFDSTADDR:
530		break;
531
532	case SIOCADDMULTI:
533	case SIOCDELMULTI:
534		break;
535
536#ifdef	SIOCSIFMTU /* xxx */
537	case SIOCGIFMTU:
538		break;
539
540	case SIOCSIFMTU:
541		{
542			u_long mtu;
543			mtu = ifr->ifr_mtu;
544			if (mtu < GIF_MTU_MIN || mtu > GIF_MTU_MAX) {
545				return (EINVAL);
546			}
547			ifp->if_mtu = mtu;
548		}
549		break;
550#endif /* SIOCSIFMTU */
551
552	case SIOCSIFPHYADDR:
553#ifdef INET6
554	case SIOCSIFPHYADDR_IN6:
555#endif /* INET6 */
556	case SIOCSLIFPHYADDR:
557		switch (cmd) {
558#ifdef INET
559		case SIOCSIFPHYADDR:
560			src = (struct sockaddr *)
561				&(((struct in_aliasreq *)data)->ifra_addr);
562			dst = (struct sockaddr *)
563				&(((struct in_aliasreq *)data)->ifra_dstaddr);
564			break;
565#endif
566#ifdef INET6
567		case SIOCSIFPHYADDR_IN6:
568			src = (struct sockaddr *)
569				&(((struct in6_aliasreq *)data)->ifra_addr);
570			dst = (struct sockaddr *)
571				&(((struct in6_aliasreq *)data)->ifra_dstaddr);
572			break;
573#endif
574		case SIOCSLIFPHYADDR:
575			src = (struct sockaddr *)
576				&(((struct if_laddrreq *)data)->addr);
577			dst = (struct sockaddr *)
578				&(((struct if_laddrreq *)data)->dstaddr);
579		default:
580			error = EADDRNOTAVAIL;
581			goto bad;
582		}
583
584		/* sa_family must be equal */
585		if (src->sa_family != dst->sa_family)
586			return EINVAL;
587
588		/* validate sa_len */
589		switch (src->sa_family) {
590#ifdef INET
591		case AF_INET:
592			if (src->sa_len != sizeof(struct sockaddr_in))
593				return EINVAL;
594			break;
595#endif
596#ifdef INET6
597		case AF_INET6:
598			if (src->sa_len != sizeof(struct sockaddr_in6))
599				return EINVAL;
600			break;
601#endif
602		default:
603			return EAFNOSUPPORT;
604		}
605		switch (dst->sa_family) {
606#ifdef INET
607		case AF_INET:
608			if (dst->sa_len != sizeof(struct sockaddr_in))
609				return EINVAL;
610			break;
611#endif
612#ifdef INET6
613		case AF_INET6:
614			if (dst->sa_len != sizeof(struct sockaddr_in6))
615				return EINVAL;
616			break;
617#endif
618		default:
619			return EAFNOSUPPORT;
620		}
621
622		/* check sa_family looks sane for the cmd */
623		switch (cmd) {
624		case SIOCSIFPHYADDR:
625			if (src->sa_family == AF_INET)
626				break;
627			return EAFNOSUPPORT;
628#ifdef INET6
629		case SIOCSIFPHYADDR_IN6:
630			if (src->sa_family == AF_INET6)
631				break;
632			return EAFNOSUPPORT;
633#endif /* INET6 */
634		case SIOCSLIFPHYADDR:
635			/* checks done in the above */
636			break;
637		}
638
639		TAILQ_FOREACH(ifp2, &ifnet, if_link) {
640			if (strcmp(ifp2->if_name, GIFNAME) != 0)
641				continue;
642			sc2 = ifp2->if_softc;
643			if (sc2 == sc)
644				continue;
645			if (!sc2->gif_pdst || !sc2->gif_psrc)
646				continue;
647			if (sc2->gif_pdst->sa_family != dst->sa_family ||
648			    sc2->gif_pdst->sa_len != dst->sa_len ||
649			    sc2->gif_psrc->sa_family != src->sa_family ||
650			    sc2->gif_psrc->sa_len != src->sa_len)
651				continue;
652
653			/*
654			 * Disallow parallel tunnels unless instructed
655			 * otherwise.
656			 */
657			if (!parallel_tunnels &&
658			    bcmp(sc2->gif_pdst, dst, dst->sa_len) == 0 &&
659			    bcmp(sc2->gif_psrc, src, src->sa_len) == 0) {
660				error = EADDRNOTAVAIL;
661				goto bad;
662			}
663
664			/* can't configure multiple multi-dest interfaces */
665#define multidest(x) \
666	(((struct sockaddr_in *)(x))->sin_addr.s_addr == INADDR_ANY)
667#ifdef INET6
668#define multidest6(x) \
669	(IN6_IS_ADDR_UNSPECIFIED(&((struct sockaddr_in6 *)(x))->sin6_addr))
670#endif
671			if (dst->sa_family == AF_INET &&
672			    multidest(dst) && multidest(sc2->gif_pdst)) {
673				error = EADDRNOTAVAIL;
674				goto bad;
675			}
676#ifdef INET6
677			if (dst->sa_family == AF_INET6 &&
678			    multidest6(dst) && multidest6(sc2->gif_pdst)) {
679				error = EADDRNOTAVAIL;
680				goto bad;
681			}
682#endif
683		}
684
685		if (sc->gif_psrc)
686			free((caddr_t)sc->gif_psrc, M_IFADDR);
687		sa = (struct sockaddr *)malloc(src->sa_len, M_IFADDR, M_WAITOK);
688		bcopy((caddr_t)src, (caddr_t)sa, src->sa_len);
689		sc->gif_psrc = sa;
690
691		if (sc->gif_pdst)
692			free((caddr_t)sc->gif_pdst, M_IFADDR);
693		sa = (struct sockaddr *)malloc(dst->sa_len, M_IFADDR, M_WAITOK);
694		bcopy((caddr_t)dst, (caddr_t)sa, dst->sa_len);
695		sc->gif_pdst = sa;
696
697		ifp->if_flags |= IFF_RUNNING;
698		s = splimp();
699		if_up(ifp);	/* mark interface UP and send up RTM_IFINFO */
700		splx(s);
701
702		error = 0;
703		break;
704
705#ifdef SIOCDIFPHYADDR
706	case SIOCDIFPHYADDR:
707		if (sc->gif_psrc) {
708			free((caddr_t)sc->gif_psrc, M_IFADDR);
709			sc->gif_psrc = NULL;
710		}
711		if (sc->gif_pdst) {
712			free((caddr_t)sc->gif_pdst, M_IFADDR);
713			sc->gif_pdst = NULL;
714		}
715		/* change the IFF_{UP, RUNNING} flag as well? */
716		break;
717#endif
718
719	case SIOCGIFPSRCADDR:
720#ifdef INET6
721	case SIOCGIFPSRCADDR_IN6:
722#endif /* INET6 */
723		if (sc->gif_psrc == NULL) {
724			error = EADDRNOTAVAIL;
725			goto bad;
726		}
727		src = sc->gif_psrc;
728		switch (cmd) {
729#ifdef INET
730		case SIOCGIFPSRCADDR:
731			dst = &ifr->ifr_addr;
732			size = sizeof(ifr->ifr_addr);
733			break;
734#endif /* INET */
735#ifdef INET6
736		case SIOCGIFPSRCADDR_IN6:
737			dst = (struct sockaddr *)
738				&(((struct in6_ifreq *)data)->ifr_addr);
739			size = sizeof(((struct in6_ifreq *)data)->ifr_addr);
740			break;
741#endif /* INET6 */
742		default:
743			error = EADDRNOTAVAIL;
744			goto bad;
745		}
746		if (src->sa_len > size)
747			return EINVAL;
748		bcopy((caddr_t)src, (caddr_t)dst, src->sa_len);
749		break;
750
751	case SIOCGIFPDSTADDR:
752#ifdef INET6
753	case SIOCGIFPDSTADDR_IN6:
754#endif /* INET6 */
755		if (sc->gif_pdst == NULL) {
756			error = EADDRNOTAVAIL;
757			goto bad;
758		}
759		src = sc->gif_pdst;
760		switch (cmd) {
761#ifdef INET
762		case SIOCGIFPDSTADDR:
763			dst = &ifr->ifr_addr;
764			size = sizeof(ifr->ifr_addr);
765			break;
766#endif /* INET */
767#ifdef INET6
768		case SIOCGIFPDSTADDR_IN6:
769			dst = (struct sockaddr *)
770				&(((struct in6_ifreq *)data)->ifr_addr);
771			size = sizeof(((struct in6_ifreq *)data)->ifr_addr);
772			break;
773#endif /* INET6 */
774		default:
775			error = EADDRNOTAVAIL;
776			goto bad;
777		}
778		if (src->sa_len > size)
779			return EINVAL;
780		bcopy((caddr_t)src, (caddr_t)dst, src->sa_len);
781		break;
782
783	case SIOCGLIFPHYADDR:
784		if (sc->gif_psrc == NULL || sc->gif_pdst == NULL) {
785			error = EADDRNOTAVAIL;
786			goto bad;
787		}
788
789		/* copy src */
790		src = sc->gif_psrc;
791		dst = (struct sockaddr *)
792			&(((struct if_laddrreq *)data)->addr);
793		size = sizeof(((struct if_laddrreq *)data)->addr);
794		if (src->sa_len > size)
795			return EINVAL;
796		bcopy((caddr_t)src, (caddr_t)dst, src->sa_len);
797
798		/* copy dst */
799		src = sc->gif_pdst;
800		dst = (struct sockaddr *)
801			&(((struct if_laddrreq *)data)->dstaddr);
802		size = sizeof(((struct if_laddrreq *)data)->dstaddr);
803		if (src->sa_len > size)
804			return EINVAL;
805		bcopy((caddr_t)src, (caddr_t)dst, src->sa_len);
806		break;
807
808	case SIOCSIFFLAGS:
809		/* if_ioctl() takes care of it */
810		break;
811
812	default:
813		error = EINVAL;
814		break;
815	}
816 bad:
817	return error;
818}
819
820void
821gif_delete_tunnel(sc)
822	struct gif_softc *sc;
823{
824	/* XXX: NetBSD protects this function with splsoftnet() */
825
826	if (sc->gif_psrc) {
827		free((caddr_t)sc->gif_psrc, M_IFADDR);
828		sc->gif_psrc = NULL;
829	}
830	if (sc->gif_pdst) {
831		free((caddr_t)sc->gif_pdst, M_IFADDR);
832		sc->gif_pdst = NULL;
833	}
834	/* change the IFF_UP flag as well? */
835}
836