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