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