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