xref: /freebsd/sys/net/if_gif.c (revision 5521ff5a4d1929056e7ffc982fac3341ca54df7c)
1 /*	$FreeBSD$	*/
2 /*	$KAME: if_gif.c,v 1.47 2001/05/01 05:28:42 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/syslog.h>
46 #include <sys/protosw.h>
47 #include <sys/conf.h>
48 #include <machine/bus.h>	/* XXX: Shouldn't really be required! */
49 #include <sys/rman.h>
50 #include <machine/cpu.h>
51 
52 #include <net/if.h>
53 #include <net/if_types.h>
54 #include <net/netisr.h>
55 #include <net/route.h>
56 #include <net/bpf.h>
57 
58 #include <netinet/in.h>
59 #include <netinet/in_systm.h>
60 #include <netinet/ip.h>
61 #ifdef	INET
62 #include <netinet/in_var.h>
63 #include <netinet/in_gif.h>
64 #include <netinet/ip_var.h>
65 #include <netinet/ipprotosw.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 #define GIFDEV		"if_gif"
86 #define GIF_MAXUNIT	0x7fff	/* ifp->if_unit is only 15 bits */
87 
88 static MALLOC_DEFINE(M_GIF, "gif", "Generic Tunnel Interface");
89 static struct rman gifunits[1];
90 TAILQ_HEAD(gifhead, gif_softc) gifs = TAILQ_HEAD_INITIALIZER(gifs);
91 
92 int	gif_clone_create __P((struct if_clone *, int *));
93 void	gif_clone_destroy __P((struct ifnet *));
94 
95 struct if_clone gif_cloner =
96     IF_CLONE_INITIALIZER("gif", gif_clone_create, gif_clone_destroy);
97 
98 static int gifmodevent __P((module_t, int, void *));
99 void gif_delete_tunnel __P((struct gif_softc *));
100 static int gif_encapcheck __P((const struct mbuf *, int, int, void *));
101 
102 #ifdef INET
103 extern  struct domain inetdomain;
104 struct ipprotosw in_gif_protosw =
105 { SOCK_RAW,	&inetdomain,	0/*IPPROTO_IPV[46]*/,	PR_ATOMIC|PR_ADDR,
106   in_gif_input,	rip_output,	0,		rip_ctloutput,
107   0,
108   0,		0,		0,		0,
109   &rip_usrreqs
110 };
111 #endif
112 #ifdef INET6
113 extern  struct domain6 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 #ifndef MAX_GIF_NEST
124 /*
125  * This macro controls the upper limitation on nesting of gif tunnels.
126  * Since, setting a large value to this macro with a careless configuration
127  * may introduce system crash, we don't allow any nestings by default.
128  * If you need to configure nested gif tunnels, you can define this macro
129  * in your kernel configuration file. However, if you do so, please be
130  * careful to configure the tunnels so that it won't make a loop.
131  */
132 #define MAX_GIF_NEST 1
133 #endif
134 static int max_gif_nesting = MAX_GIF_NEST;
135 
136 int
137 gif_clone_create(ifc, unit)
138 	struct if_clone *ifc;
139 	int *unit;
140 {
141 	struct resource *r;
142 	struct gif_softc *sc;
143 
144 	if (*unit > GIF_MAXUNIT)
145 		return (ENXIO);
146 
147 	if (*unit < 0) {
148 		r = rman_reserve_resource(gifunits, 0, GIF_MAXUNIT, 1,
149 		    RF_ALLOCATED | RF_ACTIVE, NULL);
150 		if (r == NULL)
151 			return (ENOSPC);
152 		*unit = rman_get_start(r);
153 	} else {
154 		r = rman_reserve_resource(gifunits, *unit, *unit, 1,
155 		    RF_ALLOCATED | RF_ACTIVE, NULL);
156 		if (r == NULL)
157 			return (EEXIST);
158 	}
159 
160 	sc = malloc (sizeof(struct gif_softc), M_GIF, M_WAITOK);
161 	bzero(sc, sizeof(struct gif_softc));
162 
163 	sc->gif_if.if_softc = sc;
164 	sc->gif_if.if_name = GIFNAME;
165 	sc->gif_if.if_unit = *unit;
166 	sc->r_unit = r;
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 	TAILQ_INSERT_TAIL(&gifs, sc, gif_link);
205 	return (0);
206 }
207 
208 void
209 gif_clone_destroy(ifp)
210 	struct ifnet *ifp;
211 {
212 	int err;
213 	struct gif_softc *sc = ifp->if_softc;
214 
215 	gif_delete_tunnel(sc);
216 	TAILQ_REMOVE(&gifs, sc, gif_link);
217 	if (sc->encap_cookie4 != NULL) {
218 		err = encap_detach(sc->encap_cookie4);
219 		KASSERT(err == 0, ("Unexpected error detaching encap_cookie4"));
220 	}
221 	if (sc->encap_cookie6 != NULL) {
222 		err = encap_detach(sc->encap_cookie6);
223 		KASSERT(err == 0, ("Unexpected error detaching encap_cookie6"));
224 	}
225 
226 	bpfdetach(ifp);
227 	if_detach(ifp);
228 
229 	err = rman_release_resource(sc->r_unit);
230 	KASSERT(err == 0, ("Unexpected error freeing resource"));
231 
232 	free(sc, M_GIF);
233 }
234 
235 static int
236 gifmodevent(mod, type, data)
237 	module_t mod;
238 	int type;
239 	void *data;
240 {
241 	int err;
242 
243 	switch (type) {
244 	case MOD_LOAD:
245 		gifunits->rm_type = RMAN_ARRAY;
246 		gifunits->rm_descr = "configurable if_gif units";
247 		err = rman_init(gifunits);
248 		if (err != 0)
249 			return (err);
250 		err = rman_manage_region(gifunits, 0, GIF_MAXUNIT);
251 		if (err != 0) {
252 			printf("%s: gifunits: rman_manage_region: Failed %d\n",
253 			    GIFNAME, err);
254 			rman_fini(gifunits);
255 			return (err);
256 		}
257 		if_clone_attach(&gif_cloner);
258 
259 #ifdef INET6
260 		ip6_gif_hlim = GIF_HLIM;
261 #endif
262 
263 		break;
264 	case MOD_UNLOAD:
265 		if_clone_detach(&gif_cloner);
266 
267 		while (!TAILQ_EMPTY(&gifs))
268 			gif_clone_destroy(&TAILQ_FIRST(&gifs)->gif_if);
269 
270 		err = rman_fini(gifunits);
271 		if (err != 0)
272 			return (err);
273 #ifdef INET6
274 		ip6_gif_hlim = 0;
275 #endif
276 		break;
277 	}
278 	return 0;
279 }
280 
281 static moduledata_t gif_mod = {
282 	"if_gif",
283 	gifmodevent,
284 	0
285 };
286 
287 DECLARE_MODULE(if_gif, gif_mod, SI_SUB_PSEUDO, SI_ORDER_ANY);
288 
289 static int
290 gif_encapcheck(m, off, proto, arg)
291 	const struct mbuf *m;
292 	int off;
293 	int proto;
294 	void *arg;
295 {
296 	struct ip ip;
297 	struct gif_softc *sc;
298 
299 	sc = (struct gif_softc *)arg;
300 	if (sc == NULL)
301 		return 0;
302 
303 	if ((sc->gif_if.if_flags & IFF_UP) == 0)
304 		return 0;
305 
306 	/* no physical address */
307 	if (!sc->gif_psrc || !sc->gif_pdst)
308 		return 0;
309 
310 	switch (proto) {
311 #ifdef INET
312 	case IPPROTO_IPV4:
313 		break;
314 #endif
315 #ifdef INET6
316 	case IPPROTO_IPV6:
317 		break;
318 #endif
319 	default:
320 		return 0;
321 	}
322 
323 	/* LINTED const cast */
324 	m_copydata((struct mbuf *)m, 0, sizeof(ip), (caddr_t)&ip);
325 
326 	switch (ip.ip_v) {
327 #ifdef INET
328 	case 4:
329 		if (sc->gif_psrc->sa_family != AF_INET ||
330 		    sc->gif_pdst->sa_family != AF_INET)
331 			return 0;
332 		return gif_encapcheck4(m, off, proto, arg);
333 #endif
334 #ifdef INET6
335 	case 6:
336 		if (sc->gif_psrc->sa_family != AF_INET6 ||
337 		    sc->gif_pdst->sa_family != AF_INET6)
338 			return 0;
339 		return gif_encapcheck6(m, off, proto, arg);
340 #endif
341 	default:
342 		return 0;
343 	}
344 }
345 
346 int
347 gif_output(ifp, m, dst, rt)
348 	struct ifnet *ifp;
349 	struct mbuf *m;
350 	struct sockaddr *dst;
351 	struct rtentry *rt;	/* added in net2 */
352 {
353 	struct gif_softc *sc = (struct gif_softc*)ifp;
354 	int error = 0;
355 	static int called = 0;	/* XXX: MUTEX */
356 
357 	/*
358 	 * gif may cause infinite recursion calls when misconfigured.
359 	 * We'll prevent this by introducing upper limit.
360 	 * XXX: this mechanism may introduce another problem about
361 	 *      mutual exclusion of the variable CALLED, especially if we
362 	 *      use kernel thread.
363 	 */
364 	if (++called > max_gif_nesting) {
365 		log(LOG_NOTICE,
366 		    "gif_output: recursively called too many times(%d)\n",
367 		    called);
368 		m_freem(m);
369 		error = EIO;	/* is there better errno? */
370 		goto end;
371 	}
372 
373 	m->m_flags &= ~(M_BCAST|M_MCAST);
374 	if (!(ifp->if_flags & IFF_UP) ||
375 	    sc->gif_psrc == NULL || sc->gif_pdst == NULL) {
376 		m_freem(m);
377 		error = ENETDOWN;
378 		goto end;
379 	}
380 
381 	if (ifp->if_bpf) {
382 		/*
383 		 * We need to prepend the address family as
384 		 * a four byte field.  Cons up a dummy header
385 		 * to pacify bpf.  This is safe because bpf
386 		 * will only read from the mbuf (i.e., it won't
387 		 * try to free it or keep a pointer a to it).
388 		 */
389 		struct mbuf m0;
390 		u_int32_t af = dst->sa_family;
391 
392 		m0.m_next = m;
393 		m0.m_len = 4;
394 		m0.m_data = (char *)&af;
395 
396 		bpf_mtap(ifp, &m0);
397 	}
398 	ifp->if_opackets++;
399 	ifp->if_obytes += m->m_pkthdr.len;
400 
401 	/* inner AF-specific encapsulation */
402 
403 	/* XXX should we check if our outer source is legal? */
404 
405 	/* dispatch to output logic based on outer AF */
406 	switch (sc->gif_psrc->sa_family) {
407 #ifdef INET
408 	case AF_INET:
409 		error = in_gif_output(ifp, dst->sa_family, m, rt);
410 		break;
411 #endif
412 #ifdef INET6
413 	case AF_INET6:
414 		error = in6_gif_output(ifp, dst->sa_family, m, rt);
415 		break;
416 #endif
417 	default:
418 		m_freem(m);
419 		error = ENETDOWN;
420 		goto end;
421 	}
422 
423   end:
424 	called = 0;		/* reset recursion counter */
425 	if (error)
426 		ifp->if_oerrors++;
427 	return error;
428 }
429 
430 void
431 gif_input(m, af, gifp)
432 	struct mbuf *m;
433 	int af;
434 	struct ifnet *gifp;
435 {
436 	int isr;
437 	struct ifqueue *ifq = 0;
438 
439 	if (gifp == NULL) {
440 		/* just in case */
441 		m_freem(m);
442 		return;
443 	}
444 
445 	m->m_pkthdr.rcvif = gifp;
446 
447 	if (gifp->if_bpf) {
448 		/*
449 		 * We need to prepend the address family as
450 		 * a four byte field.  Cons up a dummy header
451 		 * to pacify bpf.  This is safe because bpf
452 		 * will only read from the mbuf (i.e., it won't
453 		 * try to free it or keep a pointer a to it).
454 		 */
455 		struct mbuf m0;
456 		u_int32_t af1 = af;
457 
458 		m0.m_next = m;
459 		m0.m_len = 4;
460 		m0.m_data = (char *)&af1;
461 
462 		bpf_mtap(gifp, &m0);
463 	}
464 
465 	/*
466 	 * Put the packet to the network layer input queue according to the
467 	 * specified address family.
468 	 * Note: older versions of gif_input directly called network layer
469 	 * input functions, e.g. ip6_input, here. We changed the policy to
470 	 * prevent too many recursive calls of such input functions, which
471 	 * might cause kernel panic. But the change may introduce another
472 	 * problem; if the input queue is full, packets are discarded.
473 	 * We believed it rarely occurs and changed the policy. If we find
474 	 * it occurs more times than we thought, we may change the policy
475 	 * again.
476 	 */
477 	switch (af) {
478 #ifdef INET
479 	case AF_INET:
480 		ifq = &ipintrq;
481 		isr = NETISR_IP;
482 		break;
483 #endif
484 #ifdef INET6
485 	case AF_INET6:
486 		ifq = &ip6intrq;
487 		isr = NETISR_IPV6;
488 		break;
489 #endif
490 	default:
491 		m_freem(m);
492 		return;
493 	}
494 
495 	gifp->if_ipackets++;
496 	gifp->if_ibytes += m->m_pkthdr.len;
497 	(void) IF_HANDOFF(ifq, m, NULL);
498 	/* we need schednetisr since the address family may change */
499 	schednetisr(isr);
500 
501 	return;
502 }
503 
504 /* XXX how should we handle IPv6 scope on SIOC[GS]IFPHYADDR? */
505 int
506 gif_ioctl(ifp, cmd, data)
507 	struct ifnet *ifp;
508 	u_long cmd;
509 	caddr_t data;
510 {
511 	struct gif_softc *sc  = (struct gif_softc*)ifp;
512 	struct ifreq     *ifr = (struct ifreq*)data;
513 	int error = 0, size;
514 	struct sockaddr *dst, *src;
515 	struct sockaddr *sa;
516 	int s;
517 	struct ifnet *ifp2;
518 	struct gif_softc *sc2;
519 
520 	switch (cmd) {
521 	case SIOCSIFADDR:
522 		break;
523 
524 	case SIOCSIFDSTADDR:
525 		break;
526 
527 	case SIOCADDMULTI:
528 	case SIOCDELMULTI:
529 		break;
530 
531 #ifdef	SIOCSIFMTU /* xxx */
532 	case SIOCGIFMTU:
533 		break;
534 
535 	case SIOCSIFMTU:
536 		{
537 			u_long mtu;
538 			mtu = ifr->ifr_mtu;
539 			if (mtu < GIF_MTU_MIN || mtu > GIF_MTU_MAX) {
540 				return (EINVAL);
541 			}
542 			ifp->if_mtu = mtu;
543 		}
544 		break;
545 #endif /* SIOCSIFMTU */
546 
547 	case SIOCSIFPHYADDR:
548 #ifdef INET6
549 	case SIOCSIFPHYADDR_IN6:
550 #endif /* INET6 */
551 	case SIOCSLIFPHYADDR:
552 		switch (cmd) {
553 #ifdef INET
554 		case SIOCSIFPHYADDR:
555 			src = (struct sockaddr *)
556 				&(((struct in_aliasreq *)data)->ifra_addr);
557 			dst = (struct sockaddr *)
558 				&(((struct in_aliasreq *)data)->ifra_dstaddr);
559 			break;
560 #endif
561 #ifdef INET6
562 		case SIOCSIFPHYADDR_IN6:
563 			src = (struct sockaddr *)
564 				&(((struct in6_aliasreq *)data)->ifra_addr);
565 			dst = (struct sockaddr *)
566 				&(((struct in6_aliasreq *)data)->ifra_dstaddr);
567 			break;
568 #endif
569 		case SIOCSLIFPHYADDR:
570 			src = (struct sockaddr *)
571 				&(((struct if_laddrreq *)data)->addr);
572 			dst = (struct sockaddr *)
573 				&(((struct if_laddrreq *)data)->dstaddr);
574 		}
575 
576 		/* sa_family must be equal */
577 		if (src->sa_family != dst->sa_family)
578 			return EINVAL;
579 
580 		/* validate sa_len */
581 		switch (src->sa_family) {
582 #ifdef INET
583 		case AF_INET:
584 			if (src->sa_len != sizeof(struct sockaddr_in))
585 				return EINVAL;
586 			break;
587 #endif
588 #ifdef INET6
589 		case AF_INET6:
590 			if (src->sa_len != sizeof(struct sockaddr_in6))
591 				return EINVAL;
592 			break;
593 #endif
594 		default:
595 			return EAFNOSUPPORT;
596 		}
597 		switch (dst->sa_family) {
598 #ifdef INET
599 		case AF_INET:
600 			if (dst->sa_len != sizeof(struct sockaddr_in))
601 				return EINVAL;
602 			break;
603 #endif
604 #ifdef INET6
605 		case AF_INET6:
606 			if (dst->sa_len != sizeof(struct sockaddr_in6))
607 				return EINVAL;
608 			break;
609 #endif
610 		default:
611 			return EAFNOSUPPORT;
612 		}
613 
614 		/* check sa_family looks sane for the cmd */
615 		switch (cmd) {
616 		case SIOCSIFPHYADDR:
617 			if (src->sa_family == AF_INET)
618 				break;
619 			return EAFNOSUPPORT;
620 #ifdef INET6
621 		case SIOCSIFPHYADDR_IN6:
622 			if (src->sa_family == AF_INET6)
623 				break;
624 			return EAFNOSUPPORT;
625 #endif /* INET6 */
626 		case SIOCSLIFPHYADDR:
627 			/* checks done in the above */
628 			break;
629 		}
630 
631 		TAILQ_FOREACH(ifp2, &ifnet, if_link) {
632 			if (strcmp(ifp2->if_name, GIFNAME) != 0)
633 				continue;
634 			sc2 = ifp2->if_softc;
635 			if (sc2 == sc)
636 				continue;
637 			if (!sc2->gif_pdst || !sc2->gif_psrc)
638 				continue;
639 			if (sc2->gif_pdst->sa_family != dst->sa_family ||
640 			    sc2->gif_pdst->sa_len != dst->sa_len ||
641 			    sc2->gif_psrc->sa_family != src->sa_family ||
642 			    sc2->gif_psrc->sa_len != src->sa_len)
643 				continue;
644 #ifndef XBONEHACK
645 			/* can't configure same pair of address onto two gifs */
646 			if (bcmp(sc2->gif_pdst, dst, dst->sa_len) == 0 &&
647 			    bcmp(sc2->gif_psrc, src, src->sa_len) == 0) {
648 				error = EADDRNOTAVAIL;
649 				goto bad;
650 			}
651 #endif
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