xref: /netbsd-src/sys/net/if_gif.c (revision 8b0f9554ff8762542c4defc4f70e1eb76fb508fa)
1 /*	$NetBSD: if_gif.c,v 1.72 2007/10/08 16:18:05 ad Exp $	*/
2 /*	$KAME: if_gif.c,v 1.76 2001/08/20 02:01:02 kjc 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 <sys/cdefs.h>
34 __KERNEL_RCSID(0, "$NetBSD: if_gif.c,v 1.72 2007/10/08 16:18:05 ad Exp $");
35 
36 #include "opt_inet.h"
37 #include "opt_iso.h"
38 
39 #include <sys/param.h>
40 #include <sys/systm.h>
41 #include <sys/kernel.h>
42 #include <sys/mbuf.h>
43 #include <sys/socket.h>
44 #include <sys/sockio.h>
45 #include <sys/errno.h>
46 #include <sys/ioctl.h>
47 #include <sys/time.h>
48 #include <sys/syslog.h>
49 #include <sys/proc.h>
50 #include <sys/protosw.h>
51 #include <sys/kauth.h>
52 #include <sys/cpu.h>
53 #include <sys/intr.h>
54 
55 #include <net/if.h>
56 #include <net/if_types.h>
57 #include <net/netisr.h>
58 #include <net/route.h>
59 #include <net/bpf.h>
60 
61 #include <netinet/in.h>
62 #include <netinet/in_systm.h>
63 #include <netinet/ip.h>
64 #ifdef	INET
65 #include <netinet/in_var.h>
66 #endif	/* INET */
67 #include <netinet/in_gif.h>
68 
69 #ifdef INET6
70 #ifndef INET
71 #include <netinet/in.h>
72 #endif
73 #include <netinet6/in6_var.h>
74 #include <netinet/ip6.h>
75 #include <netinet6/ip6_var.h>
76 #include <netinet6/in6_gif.h>
77 #include <netinet6/ip6protosw.h>
78 #endif /* INET6 */
79 
80 #ifdef ISO
81 #include <netiso/iso.h>
82 #include <netiso/iso_var.h>
83 #endif
84 
85 #include <netinet/ip_encap.h>
86 #include <net/if_gif.h>
87 
88 #include "bpfilter.h"
89 
90 #include <net/net_osdep.h>
91 
92 void	gifattach(int);
93 static void	gifintr(void *);
94 #ifdef ISO
95 static struct mbuf *gif_eon_encap(struct mbuf *);
96 static struct mbuf *gif_eon_decap(struct ifnet *, struct mbuf *);
97 #endif
98 
99 /*
100  * gif global variable definitions
101  */
102 LIST_HEAD(, gif_softc) gif_softc_list;	/* XXX should be static */
103 
104 static int	gif_clone_create(struct if_clone *, int);
105 static int	gif_clone_destroy(struct ifnet *);
106 
107 static struct if_clone gif_cloner =
108     IF_CLONE_INITIALIZER("gif", gif_clone_create, gif_clone_destroy);
109 
110 #ifndef MAX_GIF_NEST
111 /*
112  * This macro controls the upper limitation on nesting of gif tunnels.
113  * Since, setting a large value to this macro with a careless configuration
114  * may introduce system crash, we don't allow any nestings by default.
115  * If you need to configure nested gif tunnels, you can define this macro
116  * in your kernel configuration file.  However, if you do so, please be
117  * careful to configure the tunnels so that it won't make a loop.
118  */
119 #define MAX_GIF_NEST 1
120 #endif
121 static int max_gif_nesting = MAX_GIF_NEST;
122 
123 /* ARGSUSED */
124 void
125 gifattach(int count)
126 {
127 
128 	LIST_INIT(&gif_softc_list);
129 	if_clone_attach(&gif_cloner);
130 }
131 
132 static int
133 gif_clone_create(struct if_clone *ifc, int unit)
134 {
135 	struct gif_softc *sc;
136 
137 	sc = malloc(sizeof(struct gif_softc), M_DEVBUF, M_WAITOK);
138 	memset(sc, 0, sizeof(struct gif_softc));
139 
140 	snprintf(sc->gif_if.if_xname, sizeof(sc->gif_if.if_xname), "%s%d",
141 	    ifc->ifc_name, unit);
142 
143 	gifattach0(sc);
144 
145 	LIST_INSERT_HEAD(&gif_softc_list, sc, gif_list);
146 	return (0);
147 }
148 
149 void
150 gifattach0(struct gif_softc *sc)
151 {
152 
153 	sc->encap_cookie4 = sc->encap_cookie6 = NULL;
154 
155 	sc->gif_if.if_addrlen = 0;
156 	sc->gif_if.if_mtu    = GIF_MTU;
157 	sc->gif_if.if_flags  = IFF_POINTOPOINT | IFF_MULTICAST;
158 	sc->gif_if.if_ioctl  = gif_ioctl;
159 	sc->gif_if.if_output = gif_output;
160 	sc->gif_if.if_type   = IFT_GIF;
161 	sc->gif_if.if_dlt    = DLT_NULL;
162 	IFQ_SET_READY(&sc->gif_if.if_snd);
163 	if_attach(&sc->gif_if);
164 	if_alloc_sadl(&sc->gif_if);
165 #if NBPFILTER > 0
166 	bpfattach(&sc->gif_if, DLT_NULL, sizeof(u_int));
167 #endif
168 }
169 
170 static int
171 gif_clone_destroy(struct ifnet *ifp)
172 {
173 	struct gif_softc *sc = (void *) ifp;
174 
175 	gif_delete_tunnel(&sc->gif_if);
176 	LIST_REMOVE(sc, gif_list);
177 #ifdef INET6
178 	encap_detach(sc->encap_cookie6);
179 #endif
180 #ifdef INET
181 	encap_detach(sc->encap_cookie4);
182 #endif
183 
184 #if NBPFILTER > 0
185 	bpfdetach(ifp);
186 #endif
187 	if_detach(ifp);
188 	rtcache_free(&sc->gif_ro);
189 
190 	free(sc, M_DEVBUF);
191 
192 	return (0);
193 }
194 
195 #ifdef GIF_ENCAPCHECK
196 int
197 gif_encapcheck(struct mbuf *m, int off, int proto, void *arg)
198 {
199 	struct ip ip;
200 	struct gif_softc *sc;
201 
202 	sc = (struct gif_softc *)arg;
203 	if (sc == NULL)
204 		return 0;
205 
206 	if ((sc->gif_if.if_flags & IFF_UP) == 0)
207 		return 0;
208 
209 	/* no physical address */
210 	if (!sc->gif_psrc || !sc->gif_pdst)
211 		return 0;
212 
213 	switch (proto) {
214 #ifdef INET
215 	case IPPROTO_IPV4:
216 		break;
217 #endif
218 #ifdef INET6
219 	case IPPROTO_IPV6:
220 		break;
221 #endif
222 #ifdef ISO
223 	case IPPROTO_EON:
224 		break;
225 #endif
226 	default:
227 		return 0;
228 	}
229 
230 	/* Bail on short packets */
231 	KASSERT(m->m_flags & M_PKTHDR);
232 	if (m->m_pkthdr.len < sizeof(ip))
233 		return 0;
234 
235 	m_copydata(m, 0, sizeof(ip), (void *)&ip);
236 
237 	switch (ip.ip_v) {
238 #ifdef INET
239 	case 4:
240 		if (sc->gif_psrc->sa_family != AF_INET ||
241 		    sc->gif_pdst->sa_family != AF_INET)
242 			return 0;
243 		return gif_encapcheck4(m, off, proto, arg);
244 #endif
245 #ifdef INET6
246 	case 6:
247 		if (m->m_pkthdr.len < sizeof(struct ip6_hdr))
248 			return 0;
249 		if (sc->gif_psrc->sa_family != AF_INET6 ||
250 		    sc->gif_pdst->sa_family != AF_INET6)
251 			return 0;
252 		return gif_encapcheck6(m, off, proto, arg);
253 #endif
254 	default:
255 		return 0;
256 	}
257 }
258 #endif
259 
260 int
261 gif_output(struct ifnet *ifp, struct mbuf *m, const struct sockaddr *dst,
262     struct rtentry *rt)
263 {
264 	struct gif_softc *sc = (struct gif_softc*)ifp;
265 	int error = 0;
266 	static int called = 0;	/* XXX: MUTEX */
267 	ALTQ_DECL(struct altq_pktattr pktattr;)
268 	int s;
269 
270 	IFQ_CLASSIFY(&ifp->if_snd, m, dst->sa_family, &pktattr);
271 
272 	/*
273 	 * gif may cause infinite recursion calls when misconfigured.
274 	 * We'll prevent this by introducing upper limit.
275 	 * XXX: this mechanism may introduce another problem about
276 	 *      mutual exclusion of the variable CALLED, especially if we
277 	 *      use kernel thread.
278 	 */
279 	if (++called > max_gif_nesting) {
280 		log(LOG_NOTICE,
281 		    "gif_output: recursively called too many times(%d)\n",
282 		    called);
283 		m_freem(m);
284 		error = EIO;	/* is there better errno? */
285 		goto end;
286 	}
287 
288 	m->m_flags &= ~(M_BCAST|M_MCAST);
289 	if (!(ifp->if_flags & IFF_UP) ||
290 	    sc->gif_psrc == NULL || sc->gif_pdst == NULL) {
291 		m_freem(m);
292 		error = ENETDOWN;
293 		goto end;
294 	}
295 
296 	/* inner AF-specific encapsulation */
297 	switch (dst->sa_family) {
298 #ifdef ISO
299 	case AF_ISO:
300 		m = gif_eon_encap(m);
301 		if (!m) {
302 			error = ENOBUFS;
303 			goto end;
304 		}
305 		break;
306 #endif
307 	default:
308 		break;
309 	}
310 
311 	/* XXX should we check if our outer source is legal? */
312 
313 	/* use DLT_NULL encapsulation here to pass inner af type */
314 	M_PREPEND(m, sizeof(int), M_DONTWAIT);
315 	if (!m) {
316 		error = ENOBUFS;
317 		goto end;
318 	}
319 	*mtod(m, int *) = dst->sa_family;
320 
321 	s = splnet();
322 	IFQ_ENQUEUE(&ifp->if_snd, m, &pktattr, error);
323 	if (error) {
324 		splx(s);
325 		goto end;
326 	}
327 	splx(s);
328 
329 	softint_schedule(sc->gif_si);
330 	error = 0;
331 
332   end:
333 	called = 0;		/* reset recursion counter */
334 	if (error)
335 		ifp->if_oerrors++;
336 	return error;
337 }
338 
339 static void
340 gifintr(void *arg)
341 {
342 	struct gif_softc *sc;
343 	struct ifnet *ifp;
344 	struct mbuf *m;
345 	int family;
346 	int len;
347 	int s;
348 	int error;
349 
350 	sc = (struct gif_softc *)arg;
351 	ifp = &sc->gif_if;
352 
353 	/* output processing */
354 	while (1) {
355 		s = splnet();
356 		IFQ_DEQUEUE(&sc->gif_if.if_snd, m);
357 		splx(s);
358 		if (m == NULL)
359 			break;
360 
361 		/* grab and chop off inner af type */
362 		if (sizeof(int) > m->m_len) {
363 			m = m_pullup(m, sizeof(int));
364 			if (!m) {
365 				ifp->if_oerrors++;
366 				continue;
367 			}
368 		}
369 		family = *mtod(m, int *);
370 #if NBPFILTER > 0
371 		if (ifp->if_bpf)
372 			bpf_mtap(ifp->if_bpf, m);
373 #endif
374 		m_adj(m, sizeof(int));
375 
376 		len = m->m_pkthdr.len;
377 
378 		/* dispatch to output logic based on outer AF */
379 		switch (sc->gif_psrc->sa_family) {
380 #ifdef INET
381 		case AF_INET:
382 			error = in_gif_output(ifp, family, m);
383 			break;
384 #endif
385 #ifdef INET6
386 		case AF_INET6:
387 			error = in6_gif_output(ifp, family, m);
388 			break;
389 #endif
390 		default:
391 			m_freem(m);
392 			error = ENETDOWN;
393 			break;
394 		}
395 
396 		if (error)
397 			ifp->if_oerrors++;
398 		else {
399 			ifp->if_opackets++;
400 			ifp->if_obytes += len;
401 		}
402 	}
403 }
404 
405 void
406 gif_input(struct mbuf *m, int af, struct ifnet *ifp)
407 {
408 	int s, isr;
409 	struct ifqueue *ifq = NULL;
410 
411 	if (ifp == NULL) {
412 		/* just in case */
413 		m_freem(m);
414 		return;
415 	}
416 
417 	m->m_pkthdr.rcvif = ifp;
418 
419 #if NBPFILTER > 0
420 	if (ifp->if_bpf)
421 		bpf_mtap_af(ifp->if_bpf, af, m);
422 #endif /*NBPFILTER > 0*/
423 
424 	/*
425 	 * Put the packet to the network layer input queue according to the
426 	 * specified address family.
427 	 * Note: older versions of gif_input directly called network layer
428 	 * input functions, e.g. ip6_input, here.  We changed the policy to
429 	 * prevent too many recursive calls of such input functions, which
430 	 * might cause kernel panic.  But the change may introduce another
431 	 * problem; if the input queue is full, packets are discarded.
432 	 * The kernel stack overflow really happened, and we believed
433 	 * queue-full rarely occurs, so we changed the policy.
434 	 */
435 	switch (af) {
436 #ifdef INET
437 	case AF_INET:
438 		ifq = &ipintrq;
439 		isr = NETISR_IP;
440 		break;
441 #endif
442 #ifdef INET6
443 	case AF_INET6:
444 		ifq = &ip6intrq;
445 		isr = NETISR_IPV6;
446 		break;
447 #endif
448 #ifdef ISO
449 	case AF_ISO:
450 		m = gif_eon_decap(ifp, m);
451 		if (!m)
452 			return;
453 		ifq = &clnlintrq;
454 		isr = NETISR_ISO;
455 		break;
456 #endif
457 	default:
458 		m_freem(m);
459 		return;
460 	}
461 
462 	s = splnet();
463 	if (IF_QFULL(ifq)) {
464 		IF_DROP(ifq);	/* update statistics */
465 		m_freem(m);
466 		splx(s);
467 		return;
468 	}
469 	ifp->if_ipackets++;
470 	ifp->if_ibytes += m->m_pkthdr.len;
471 	IF_ENQUEUE(ifq, m);
472 	/* we need schednetisr since the address family may change */
473 	schednetisr(isr);
474 	splx(s);
475 }
476 
477 /* XXX how should we handle IPv6 scope on SIOC[GS]IFPHYADDR? */
478 int
479 gif_ioctl(struct ifnet *ifp, u_long cmd, void *data)
480 {
481 	struct lwp *l = curlwp;	/* XXX */
482 	struct gif_softc *sc  = (struct gif_softc*)ifp;
483 	struct ifreq     *ifr = (struct ifreq*)data;
484 	int error = 0, size;
485 	struct sockaddr *dst, *src;
486 #ifdef SIOCSIFMTU
487 	u_long mtu;
488 #endif
489 
490 	switch (cmd) {
491 	case SIOCSIFMTU:
492 	case SIOCSLIFPHYADDR:
493 #ifdef SIOCDIFPHYADDR
494 	case SIOCDIFPHYADDR:
495 #endif
496 		if ((error = kauth_authorize_network(l->l_cred,
497 		    KAUTH_NETWORK_INTERFACE,
498 		    KAUTH_REQ_NETWORK_INTERFACE_SETPRIV, ifp, (void *)cmd,
499 		    NULL)) != 0)
500 			return (error);
501 		/* FALLTHROUGH */
502 	default:
503 		break;
504 	}
505 
506 	switch (cmd) {
507 	case SIOCSIFADDR:
508 		ifp->if_flags |= IFF_UP;
509 		break;
510 
511 	case SIOCSIFDSTADDR:
512 		break;
513 
514 	case SIOCADDMULTI:
515 	case SIOCDELMULTI:
516 		switch (ifr->ifr_addr.sa_family) {
517 #ifdef INET
518 		case AF_INET:	/* IP supports Multicast */
519 			break;
520 #endif /* INET */
521 #ifdef INET6
522 		case AF_INET6:	/* IP6 supports Multicast */
523 			break;
524 #endif /* INET6 */
525 		default:  /* Other protocols doesn't support Multicast */
526 			error = EAFNOSUPPORT;
527 			break;
528 		}
529 		break;
530 
531 #ifdef	SIOCSIFMTU /* xxx */
532 	case SIOCGIFMTU:
533 		break;
534 
535 	case SIOCSIFMTU:
536 		mtu = ifr->ifr_mtu;
537 		if (mtu < GIF_MTU_MIN || mtu > GIF_MTU_MAX)
538 			return (EINVAL);
539 		ifp->if_mtu = mtu;
540 		break;
541 #endif /* SIOCSIFMTU */
542 
543 #ifdef INET
544 	case SIOCSIFPHYADDR:
545 #endif
546 #ifdef INET6
547 	case SIOCSIFPHYADDR_IN6:
548 #endif /* INET6 */
549 	case SIOCSLIFPHYADDR:
550 		switch (cmd) {
551 #ifdef INET
552 		case SIOCSIFPHYADDR:
553 			src = (struct sockaddr *)
554 				&(((struct in_aliasreq *)data)->ifra_addr);
555 			dst = (struct sockaddr *)
556 				&(((struct in_aliasreq *)data)->ifra_dstaddr);
557 			break;
558 #endif
559 #ifdef INET6
560 		case SIOCSIFPHYADDR_IN6:
561 			src = (struct sockaddr *)
562 				&(((struct in6_aliasreq *)data)->ifra_addr);
563 			dst = (struct sockaddr *)
564 				&(((struct in6_aliasreq *)data)->ifra_dstaddr);
565 			break;
566 #endif
567 		case SIOCSLIFPHYADDR:
568 			src = (struct sockaddr *)
569 				&(((struct if_laddrreq *)data)->addr);
570 			dst = (struct sockaddr *)
571 				&(((struct if_laddrreq *)data)->dstaddr);
572 			break;
573 		default:
574 			return EINVAL;
575 		}
576 
577 		/* sa_family must be equal */
578 		if (src->sa_family != dst->sa_family)
579 			return EINVAL;
580 
581 		/* validate sa_len */
582 		switch (src->sa_family) {
583 #ifdef INET
584 		case AF_INET:
585 			if (src->sa_len != sizeof(struct sockaddr_in))
586 				return EINVAL;
587 			break;
588 #endif
589 #ifdef INET6
590 		case AF_INET6:
591 			if (src->sa_len != sizeof(struct sockaddr_in6))
592 				return EINVAL;
593 			break;
594 #endif
595 		default:
596 			return EAFNOSUPPORT;
597 		}
598 		switch (dst->sa_family) {
599 #ifdef INET
600 		case AF_INET:
601 			if (dst->sa_len != sizeof(struct sockaddr_in))
602 				return EINVAL;
603 			break;
604 #endif
605 #ifdef INET6
606 		case AF_INET6:
607 			if (dst->sa_len != sizeof(struct sockaddr_in6))
608 				return EINVAL;
609 			break;
610 #endif
611 		default:
612 			return EAFNOSUPPORT;
613 		}
614 
615 		/* check sa_family looks sane for the cmd */
616 		switch (cmd) {
617 		case SIOCSIFPHYADDR:
618 			if (src->sa_family == AF_INET)
619 				break;
620 			return EAFNOSUPPORT;
621 #ifdef INET6
622 		case SIOCSIFPHYADDR_IN6:
623 			if (src->sa_family == AF_INET6)
624 				break;
625 			return EAFNOSUPPORT;
626 #endif /* INET6 */
627 		case SIOCSLIFPHYADDR:
628 			/* checks done in the above */
629 			break;
630 		}
631 
632 		error = gif_set_tunnel(&sc->gif_if, src, dst);
633 		break;
634 
635 #ifdef SIOCDIFPHYADDR
636 	case SIOCDIFPHYADDR:
637 		gif_delete_tunnel(&sc->gif_if);
638 		break;
639 #endif
640 
641 	case SIOCGIFPSRCADDR:
642 #ifdef INET6
643 	case SIOCGIFPSRCADDR_IN6:
644 #endif /* INET6 */
645 		if (sc->gif_psrc == NULL) {
646 			error = EADDRNOTAVAIL;
647 			goto bad;
648 		}
649 		src = sc->gif_psrc;
650 		switch (cmd) {
651 #ifdef INET
652 		case SIOCGIFPSRCADDR:
653 			dst = &ifr->ifr_addr;
654 			size = sizeof(ifr->ifr_addr);
655 			break;
656 #endif /* INET */
657 #ifdef INET6
658 		case SIOCGIFPSRCADDR_IN6:
659 			dst = (struct sockaddr *)
660 				&(((struct in6_ifreq *)data)->ifr_addr);
661 			size = sizeof(((struct in6_ifreq *)data)->ifr_addr);
662 			break;
663 #endif /* INET6 */
664 		default:
665 			error = EADDRNOTAVAIL;
666 			goto bad;
667 		}
668 		if (src->sa_len > size)
669 			return EINVAL;
670 		memcpy(dst, src, src->sa_len);
671 		break;
672 
673 	case SIOCGIFPDSTADDR:
674 #ifdef INET6
675 	case SIOCGIFPDSTADDR_IN6:
676 #endif /* INET6 */
677 		if (sc->gif_pdst == NULL) {
678 			error = EADDRNOTAVAIL;
679 			goto bad;
680 		}
681 		src = sc->gif_pdst;
682 		switch (cmd) {
683 #ifdef INET
684 		case SIOCGIFPDSTADDR:
685 			dst = &ifr->ifr_addr;
686 			size = sizeof(ifr->ifr_addr);
687 			break;
688 #endif /* INET */
689 #ifdef INET6
690 		case SIOCGIFPDSTADDR_IN6:
691 			dst = (struct sockaddr *)
692 				&(((struct in6_ifreq *)data)->ifr_addr);
693 			size = sizeof(((struct in6_ifreq *)data)->ifr_addr);
694 			break;
695 #endif /* INET6 */
696 		default:
697 			error = EADDRNOTAVAIL;
698 			goto bad;
699 		}
700 		if (src->sa_len > size)
701 			return EINVAL;
702 		memcpy(dst, src, src->sa_len);
703 		break;
704 
705 	case SIOCGLIFPHYADDR:
706 		if (sc->gif_psrc == NULL || sc->gif_pdst == NULL) {
707 			error = EADDRNOTAVAIL;
708 			goto bad;
709 		}
710 
711 		/* copy src */
712 		src = sc->gif_psrc;
713 		dst = (struct sockaddr *)
714 			&(((struct if_laddrreq *)data)->addr);
715 		size = sizeof(((struct if_laddrreq *)data)->addr);
716 		if (src->sa_len > size)
717 			return EINVAL;
718 		memcpy(dst, src, src->sa_len);
719 
720 		/* copy dst */
721 		src = sc->gif_pdst;
722 		dst = (struct sockaddr *)
723 			&(((struct if_laddrreq *)data)->dstaddr);
724 		size = sizeof(((struct if_laddrreq *)data)->dstaddr);
725 		if (src->sa_len > size)
726 			return EINVAL;
727 		memcpy(dst, src, src->sa_len);
728 		break;
729 
730 	case SIOCSIFFLAGS:
731 		/* if_ioctl() takes care of it */
732 		break;
733 
734 	default:
735 		error = EINVAL;
736 		break;
737 	}
738  bad:
739 	return error;
740 }
741 
742 int
743 gif_set_tunnel(struct ifnet *ifp, struct sockaddr *src, struct sockaddr *dst)
744 {
745 	struct gif_softc *sc = (struct gif_softc *)ifp;
746 	struct gif_softc *sc2;
747 	struct sockaddr *osrc, *odst;
748 	int s;
749 	int error;
750 
751 	s = splsoftnet();
752 
753 	LIST_FOREACH(sc2, &gif_softc_list, gif_list) {
754 		if (sc2 == sc)
755 			continue;
756 		if (!sc2->gif_pdst || !sc2->gif_psrc)
757 			continue;
758 		/* can't configure same pair of address onto two gifs */
759 		if (sockaddr_cmp(sc2->gif_pdst, dst) == 0 &&
760 		    sockaddr_cmp(sc2->gif_psrc, src) == 0) {
761 			error = EADDRNOTAVAIL;
762 			goto bad;
763 		}
764 
765 		/* XXX both end must be valid? (I mean, not 0.0.0.0) */
766 	}
767 
768 	if (sc->gif_si) {
769 		softint_disestablish(sc->gif_si);
770 		sc->gif_si = NULL;
771 	}
772 
773 	/* XXX we can detach from both, but be polite just in case */
774 	if (sc->gif_psrc)
775 		switch (sc->gif_psrc->sa_family) {
776 #ifdef INET
777 		case AF_INET:
778 			(void)in_gif_detach(sc);
779 			break;
780 #endif
781 #ifdef INET6
782 		case AF_INET6:
783 			(void)in6_gif_detach(sc);
784 			break;
785 #endif
786 		}
787 
788 	sc->gif_si = softint_establish(SOFTINT_NET, gifintr, sc);
789 	if (sc->gif_si == NULL) {
790 		error = ENOMEM;
791 		goto bad;
792 	}
793 
794 	osrc = sc->gif_psrc;
795 	sc->gif_psrc = sockaddr_dup(src, M_WAITOK);
796 
797 	odst = sc->gif_pdst;
798 	sc->gif_pdst = sockaddr_dup(dst, M_WAITOK);
799 
800 	switch (sc->gif_psrc->sa_family) {
801 #ifdef INET
802 	case AF_INET:
803 		error = in_gif_attach(sc);
804 		break;
805 #endif
806 #ifdef INET6
807 	case AF_INET6:
808 		error = in6_gif_attach(sc);
809 		break;
810 #endif
811 	default:
812 		error = EINVAL;
813 		break;
814 	}
815 	if (error) {
816 		/* rollback */
817 		sockaddr_free(sc->gif_psrc);
818 		sockaddr_free(sc->gif_pdst);
819 		sc->gif_psrc = osrc;
820 		sc->gif_pdst = odst;
821 		goto bad;
822 	}
823 
824 	if (osrc)
825 		sockaddr_free(osrc);
826 	if (odst)
827 		sockaddr_free(odst);
828 
829 	if (sc->gif_psrc && sc->gif_pdst)
830 		ifp->if_flags |= IFF_RUNNING;
831 	else
832 		ifp->if_flags &= ~IFF_RUNNING;
833 	splx(s);
834 
835 	return 0;
836 
837  bad:
838 	if (sc->gif_si) {
839 		softint_disestablish(sc->gif_si);
840 		sc->gif_si = NULL;
841 	}
842 	if (sc->gif_psrc && sc->gif_pdst)
843 		ifp->if_flags |= IFF_RUNNING;
844 	else
845 		ifp->if_flags &= ~IFF_RUNNING;
846 	splx(s);
847 
848 	return error;
849 }
850 
851 void
852 gif_delete_tunnel(struct ifnet *ifp)
853 {
854 	struct gif_softc *sc = (struct gif_softc *)ifp;
855 	int s;
856 
857 	s = splsoftnet();
858 
859 	if (sc->gif_si) {
860 		softint_disestablish(sc->gif_si);
861 		sc->gif_si = NULL;
862 	}
863 	if (sc->gif_psrc) {
864 		sockaddr_free(sc->gif_psrc);
865 		sc->gif_psrc = NULL;
866 	}
867 	if (sc->gif_pdst) {
868 		sockaddr_free(sc->gif_pdst);
869 		sc->gif_pdst = NULL;
870 	}
871 	/* it is safe to detach from both */
872 #ifdef INET
873 	(void)in_gif_detach(sc);
874 #endif
875 #ifdef INET6
876 	(void)in6_gif_detach(sc);
877 #endif
878 
879 	if (sc->gif_psrc && sc->gif_pdst)
880 		ifp->if_flags |= IFF_RUNNING;
881 	else
882 		ifp->if_flags &= ~IFF_RUNNING;
883 	splx(s);
884 }
885 
886 #ifdef ISO
887 struct eonhdr {
888 	u_int8_t version;
889 	u_int8_t class;
890 	u_int16_t cksum;
891 };
892 
893 /*
894  * prepend EON header to ISO PDU
895  */
896 static struct mbuf *
897 gif_eon_encap(struct mbuf *m)
898 {
899 	struct eonhdr *ehdr;
900 
901 	M_PREPEND(m, sizeof(*ehdr), M_DONTWAIT);
902 	if (m && m->m_len < sizeof(*ehdr))
903 		m = m_pullup(m, sizeof(*ehdr));
904 	if (m == NULL)
905 		return NULL;
906 	ehdr = mtod(m, struct eonhdr *);
907 	ehdr->version = 1;
908 	ehdr->class = 0;		/* always unicast */
909 #if 0
910 	/* calculate the checksum of the eonhdr */
911 	{
912 		struct mbuf mhead;
913 		memset(&mhead, 0, sizeof(mhead));
914 		ehdr->cksum = 0;
915 		mhead.m_data = (void *)ehdr;
916 		mhead.m_len = sizeof(*ehdr);
917 		mhead.m_next = 0;
918 		iso_gen_csum(&mhead, offsetof(struct eonhdr, cksum),
919 		    mhead.m_len);
920 	}
921 #else
922 	/* since the data is always constant we'll just plug the value in */
923 	ehdr->cksum = htons(0xfc02);
924 #endif
925 	return m;
926 }
927 
928 /*
929  * remove EON header and check checksum
930  */
931 static struct mbuf *
932 gif_eon_decap(struct ifnet *ifp, struct mbuf *m)
933 {
934 	struct eonhdr *ehdr;
935 
936 	if (m->m_len < sizeof(*ehdr) &&
937 	    (m = m_pullup(m, sizeof(*ehdr))) == NULL) {
938 		ifp->if_ierrors++;
939 		return NULL;
940 	}
941 	if (iso_check_csum(m, sizeof(struct eonhdr))) {
942 		m_freem(m);
943 		return NULL;
944 	}
945 	m_adj(m, sizeof(*ehdr));
946 	return m;
947 }
948 #endif /*ISO*/
949