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