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