Home | History | Annotate | Line # | Download | only in net
if_gre.c revision 1.13
      1 /*	$NetBSD: if_gre.c,v 1.13 2000/11/19 18:48:44 martin Exp $ */
      2 
      3 /*
      4  * Copyright (c) 1998 The NetBSD Foundation, Inc.
      5  * All rights reserved.
      6  *
      7  * This code is derived from software contributed to The NetBSD Foundation
      8  * by Heiko W.Rupp <hwr (at) pilhuhn.de>
      9  *
     10  * Redistribution and use in source and binary forms, with or without
     11  * modification, are permitted provided that the following conditions
     12  * are met:
     13  * 1. Redistributions of source code must retain the above copyright
     14  *    notice, this list of conditions and the following disclaimer.
     15  * 2. Redistributions in binary form must reproduce the above copyright
     16  *    notice, this list of conditions and the following disclaimer in the
     17  *    documentation and/or other materials provided with the distribution.
     18  * 3. All advertising materials mentioning features or use of this software
     19  *    must display the following acknowledgement:
     20  *        This product includes software developed by the NetBSD
     21  *        Foundation, Inc. and its contributors.
     22  * 4. Neither the name of The NetBSD Foundation nor the names of its
     23  *    contributors may be used to endorse or promote products derived
     24  *    from this software without specific prior written permission.
     25  *
     26  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
     27  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     28  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     29  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
     30  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     31  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     32  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     33  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     34  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     35  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     36  * POSSIBILITY OF SUCH DAMAGE.
     37  */
     38 
     39 /*
     40  * Encapsulate L3 protocols into IP
     41  * See RFC 1701 and 1702 for more details.
     42  * If_gre is compatible with Cisco GRE tunnels, so you can
     43  * have a NetBSD box as the other end of a tunnel interface of a Cisco
     44  * router. See gre(4) for more details.
     45  * Also supported:  IP in IP encaps (proto 55) as of RFC 2004
     46  */
     47 
     48 #include "gre.h"
     49 #if NGRE > 0
     50 
     51 #include "opt_inet.h"
     52 #include "opt_ns.h"
     53 #include "bpfilter.h"
     54 
     55 #include <sys/param.h>
     56 #include <sys/malloc.h>
     57 #include <sys/mbuf.h>
     58 #include <sys/proc.h>
     59 #include <sys/protosw.h>
     60 #include <sys/socket.h>
     61 #include <sys/ioctl.h>
     62 #include <sys/queue.h>
     63 #if __NetBSD__
     64 #include <sys/systm.h>
     65 #endif
     66 
     67 #include <machine/cpu.h>
     68 
     69 #include <net/ethertypes.h>
     70 #include <net/if.h>
     71 #include <net/if_types.h>
     72 #include <net/netisr.h>
     73 #include <net/route.h>
     74 
     75 #ifdef INET
     76 #include <netinet/in.h>
     77 #include <netinet/in_systm.h>
     78 #include <netinet/in_var.h>
     79 #include <netinet/ip.h>
     80 #include <netinet/ip_var.h>
     81 #else
     82 #error "Huh? if_gre without inet?"
     83 #endif
     84 
     85 #ifdef NS
     86 #include <netns/ns.h>
     87 #include <netns/ns_if.h>
     88 #endif
     89 
     90 #ifdef NETATALK
     91 #include <netatalk/at.h>
     92 #include <netatalk/at_var.h>
     93 #include <netatalk/at_extern.h>
     94 #endif
     95 
     96 #if NBPFILTER > 0
     97 #include <sys/time.h>
     98 #include <net/bpf.h>
     99 #endif
    100 
    101 #include <net/if_gre.h>
    102 
    103 #define GREMTU 1450	/* XXX this is below the standard MTU of
    104                          1500 Bytes, allowing for headers,
    105                          but we should possibly do path mtu discovery
    106                          before changing if state to up to find the
    107                          correct value */
    108 #define LINK_MASK (IFF_LINK0|IFF_LINK1|IFF_LINK2)
    109 
    110 struct gre_softc_head gre_softc_list;
    111 
    112 int	gre_clone_create __P((struct if_clone *, int));
    113 void	gre_clone_destroy __P((struct ifnet *));
    114 
    115 struct if_clone gre_cloner =
    116     IF_CLONE_INITIALIZER("gre", gre_clone_create, gre_clone_destroy);
    117 
    118 void gre_compute_route(struct gre_softc *sc);
    119 #ifdef DIAGNOSTIC
    120 void gre_inet_ntoa(struct in_addr in);
    121 #endif
    122 
    123 void	greattach __P((int));
    124 
    125 /* ARGSUSED */
    126 void
    127 greattach(count)
    128 	int count;
    129 {
    130 
    131 	LIST_INIT(&gre_softc_list);
    132 	if_clone_attach(&gre_cloner);
    133 }
    134 
    135 int
    136 gre_clone_create(ifc, unit)
    137 	struct if_clone *ifc;
    138 	int unit;
    139 {
    140 	struct gre_softc *sc;
    141 
    142 	sc = malloc(sizeof(struct gre_softc), M_DEVBUF, M_WAITOK);
    143 	memset(sc, 0, sizeof(struct gre_softc));
    144 
    145 	sprintf(sc->sc_if.if_xname, "%s%d", ifc->ifc_name, unit);
    146 	sc->sc_if.if_softc = sc;
    147 	sc->sc_if.if_type =  IFT_OTHER;
    148 	sc->sc_if.if_addrlen = 4;
    149 	sc->sc_if.if_hdrlen = 24; /* IP + GRE */
    150 	sc->sc_if.if_mtu = GREMTU;
    151 	sc->sc_if.if_flags = IFF_POINTOPOINT|IFF_MULTICAST;
    152 	sc->sc_if.if_output = gre_output;
    153 	sc->sc_if.if_ioctl = gre_ioctl;
    154 	sc->g_dst.s_addr = sc->g_src.s_addr = INADDR_ANY;
    155 	sc->g_proto = IPPROTO_GRE;
    156 	if_attach(&sc->sc_if);
    157 #if NBPFILTER > 0
    158 	bpfattach(&sc->gre_bpf, &sc->sc_if, DLT_NULL, sizeof(u_int32_t));
    159 #endif
    160 	LIST_INSERT_HEAD(&gre_softc_list, sc, sc_list);
    161 	return (0);
    162 }
    163 
    164 void
    165 gre_clone_destroy(ifp)
    166 	struct ifnet *ifp;
    167 {
    168 	struct gre_softc *sc = ifp->if_softc;
    169 
    170 	LIST_REMOVE(sc, sc_list);
    171 #if NBPFILTER > 0
    172 	bpfdetach(ifp);
    173 #endif
    174 	if_detach(ifp);
    175 	free(sc, M_DEVBUF);
    176 }
    177 
    178 /*
    179  * The output routine. Takes a packet and encapsulates it in the protocol
    180  * given by sc->g_proto. See also RFC 1701 and RFC 2004
    181  */
    182 
    183 #if 0
    184 struct ip ip_h;
    185 #endif
    186 struct mobile_h mob_h;
    187 
    188 int
    189 gre_output(struct ifnet *ifp, struct mbuf *m, struct sockaddr *dst,
    190 	   struct rtentry *rt)
    191 {
    192 	int error = 0;
    193 	struct gre_softc *sc = ifp->if_softc;
    194 	struct greip *gh;
    195 	struct ip *inp;
    196 	u_char ttl, osrc;
    197 	u_short etype = 0;
    198 
    199 
    200 	gh = NULL;
    201 	inp = NULL;
    202 	osrc = 0;
    203 
    204 #if NBPFILTER >0
    205 	if (sc->gre_bpf) {
    206 		/* see comment of other if_foo.c files */
    207 		struct mbuf m0;
    208 		u_int af = dst->sa_family;
    209 
    210 		m0.m_next = m;
    211 		m0.m_len = 4;
    212 		m0.m_data = (char *)&af;
    213 
    214 		bpf_mtap(sc->gre_bpf, &m0);
    215 	}
    216 #endif
    217 
    218 	ttl = 255;
    219 
    220 	if (sc->g_proto == IPPROTO_MOBILE) {
    221 		if (dst->sa_family == AF_INET) {
    222 			struct mbuf *m0;
    223 			int msiz;
    224 
    225 			inp = mtod(m, struct ip *);
    226 
    227 			memset(&mob_h, 0, MOB_H_SIZ_L);
    228 			mob_h.proto = (inp->ip_p) << 8;
    229 			mob_h.odst = inp->ip_dst.s_addr;
    230 			inp->ip_dst.s_addr = sc->g_dst.s_addr;
    231 
    232 			/*
    233 			 * If the packet comes from our host, we only change
    234 			 * the destination address in the IP header.
    235 			 * Else we also need to save and change the source
    236 			 */
    237 			if (in_hosteq(inp->ip_src, sc->g_src)) {
    238 				msiz = MOB_H_SIZ_S;
    239 			} else {
    240 				mob_h.proto |= MOB_H_SBIT;
    241 				mob_h.osrc = inp->ip_src.s_addr;
    242 				inp->ip_src.s_addr = sc->g_src.s_addr;
    243 				msiz = MOB_H_SIZ_L;
    244 			}
    245 			HTONS(mob_h.proto);
    246 			mob_h.hcrc = gre_in_cksum((u_short *)&mob_h, msiz);
    247 
    248 			if ((m->m_data - msiz) < m->m_pktdat) {
    249 				/* need new mbuf */
    250 				MGETHDR(m0, M_DONTWAIT, MT_HEADER);
    251 				if (m0 == NULL) {
    252 					IF_DROP(&ifp->if_snd);
    253 					m_freem(m);
    254 					return (ENOBUFS);
    255 				}
    256 				m0->m_next = m;
    257 				m->m_data += sizeof(struct ip);
    258 				m->m_len -= sizeof(struct ip);
    259 				m0->m_pkthdr.len = m->m_pkthdr.len + msiz;
    260 				m0->m_len = msiz + sizeof(struct ip);
    261 				m0->m_data += max_linkhdr;
    262 				memcpy(mtod(m0, caddr_t), (caddr_t)inp,
    263 				       sizeof(struct ip));
    264 				m = m0;
    265 			} else {  /* we have some spave left in the old one */
    266 				m->m_data -= msiz;
    267 				m->m_len += msiz;
    268 				m->m_pkthdr.len += msiz;
    269 				memmove(mtod(m, caddr_t), inp,
    270 					sizeof(struct ip));
    271 			}
    272 			inp=mtod(m, struct ip *);
    273 			memcpy((caddr_t)(inp + 1), &mob_h, (unsigned)msiz);
    274 			NTOHS(inp->ip_len);
    275 			inp->ip_len += msiz;
    276 		} else {  /* AF_INET */
    277 			IF_DROP(&ifp->if_snd);
    278 			m_freem(m);
    279 			return (EINVAL);
    280 		}
    281 	} else if (sc->g_proto == IPPROTO_GRE) {
    282 		switch(dst->sa_family) {
    283 		case AF_INET:
    284 			inp = mtod(m, struct ip *);
    285 			ttl = inp->ip_ttl;
    286 			etype = ETHERTYPE_IP;
    287 			break;
    288 #ifdef NETATALK
    289 		case AF_APPLETALK:
    290 			etype = ETHERTYPE_ATALK;
    291 			break;
    292 #endif
    293 #ifdef NS
    294 		case AF_NS:
    295 			etype = ETHERTYPE_NS;
    296 			break;
    297 #endif
    298 		default:
    299 			IF_DROP(&ifp->if_snd);
    300 			m_freem(m);
    301 			return (EAFNOSUPPORT);
    302 		}
    303 		M_PREPEND(m, sizeof(struct greip), M_DONTWAIT);
    304 	} else {
    305 		error = EINVAL;
    306 		IF_DROP(&ifp->if_snd);
    307 		m_freem(m);
    308 		return (error);
    309 	}
    310 
    311 
    312 	if (m == NULL) {
    313 		IF_DROP(&ifp->if_snd);
    314 		return (ENOBUFS);
    315 	}
    316 
    317 	gh = mtod(m, struct greip *);
    318 	if (sc->g_proto == IPPROTO_GRE) {
    319 		/* we don't have any GRE flags for now */
    320 
    321 		memset((void *)&gh->gi_g, 0, sizeof(struct gre_h));
    322 		gh->gi_ptype = htons(etype);
    323 	}
    324 
    325 	gh->gi_pr = sc->g_proto;
    326 	if (sc->g_proto != IPPROTO_MOBILE) {
    327 		gh->gi_src = sc->g_src;
    328 		gh->gi_dst = sc->g_dst;
    329 		((struct ip*)gh)->ip_hl = (sizeof(struct ip)) >> 2;
    330 		((struct ip*)gh)->ip_ttl = ttl;
    331 		((struct ip*)gh)->ip_tos = inp->ip_tos;
    332 	    gh->gi_len = m->m_pkthdr.len;
    333 	}
    334 
    335 	ifp->if_opackets++;
    336 	ifp->if_obytes += m->m_pkthdr.len;
    337 	/* send it off */
    338 	error = ip_output(m, NULL, &sc->route, 0, NULL);
    339 	if (error)
    340 		ifp->if_oerrors++;
    341 	return (error);
    342 
    343 }
    344 
    345 int
    346 gre_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
    347 {
    348 	struct proc *p = curproc;	/* XXX */
    349 	struct ifaddr *ifa = (struct ifaddr *)data;
    350 	struct ifreq *ifr = (struct ifreq *)data;
    351 	struct in_ifaddr *ia = (struct in_ifaddr *)data;
    352 	struct gre_softc *sc = ifp->if_softc;
    353 	int s;
    354 	struct sockaddr_in si;
    355 	struct sockaddr *sa = NULL;
    356 	int error;
    357 
    358 	error = 0;
    359 
    360 	s = splimp();
    361 	switch(cmd) {
    362 	case SIOCSIFADDR:
    363 	case SIOCSIFDSTADDR:
    364 		if ((error = suser(p->p_ucred, &p->p_acflag)) != 0)
    365 			break;
    366 		/*
    367                  * set tunnel endpoints in case that we "only"
    368                  * have ip over ip encapsulation. This allows to
    369                  * set tunnel endpoints with ifconfig.
    370                  */
    371 		if (ifa->ifa_addr->sa_family == AF_INET) {
    372 			sa = ifa->ifa_addr;
    373 			sc->g_src = (satosin(sa))->sin_addr;
    374 			sc->g_dst = ia->ia_dstaddr.sin_addr;
    375 			if ((sc->g_src.s_addr != INADDR_ANY) &&
    376 			    (sc->g_dst.s_addr != INADDR_ANY)) {
    377 				if (sc->route.ro_rt != 0) /* free old route */
    378 					RTFREE(sc->route.ro_rt);
    379 				gre_compute_route(sc);
    380 				ifp->if_flags |= IFF_UP;
    381 			}
    382 		}
    383 		break;
    384 	case SIOCSIFFLAGS:
    385 		if ((error = suser(p->p_ucred, &p->p_acflag)) != 0)
    386 			break;
    387 		if ((sc->g_dst.s_addr == INADDR_ANY) ||
    388 		    (sc->g_src.s_addr == INADDR_ANY))
    389 			ifp->if_flags &= ~IFF_UP;
    390 
    391 		switch(ifr->ifr_flags & LINK_MASK) {
    392 			case IFF_LINK0:
    393 				sc->g_proto = IPPROTO_GRE;
    394 				ifp->if_flags |= IFF_LINK0;
    395 				ifp->if_flags &= ~(IFF_LINK1|IFF_LINK2);
    396 				break;
    397 			case IFF_LINK2:
    398 				sc->g_proto = IPPROTO_MOBILE;
    399 				ifp->if_flags |= IFF_LINK2;
    400 				ifp->if_flags &= ~(IFF_LINK0|IFF_LINK1);
    401 				break;
    402 		}
    403 		break;
    404 	case SIOCSIFMTU:
    405 		if ((error = suser(p->p_ucred, &p->p_acflag)) != 0)
    406 			break;
    407 		if (ifr->ifr_mtu > GREMTU || ifr->ifr_mtu < 576) {
    408 			error = EINVAL;
    409 			break;
    410 		}
    411 		ifp->if_mtu = ifr->ifr_mtu;
    412 		break;
    413 	case SIOCGIFMTU:
    414 		ifr->ifr_mtu = sc->sc_if.if_mtu;
    415 		break;
    416 	case SIOCADDMULTI:
    417 	case SIOCDELMULTI:
    418 		if ((error = suser(p->p_ucred, &p->p_acflag)) != 0)
    419 			break;
    420 		if (ifr == 0) {
    421 			error = EAFNOSUPPORT;
    422 			break;
    423 		}
    424 		switch (ifr->ifr_addr.sa_family) {
    425 #ifdef INET
    426 		case AF_INET:
    427 			break;
    428 #endif
    429 		default:
    430 			error = EAFNOSUPPORT;
    431 			break;
    432 		}
    433 		break;
    434 	case GRESPROTO:
    435 		if ((error = suser(p->p_ucred, &p->p_acflag)) != 0)
    436 			break;
    437 		sc->g_proto = ifr->ifr_flags;
    438 		switch (sc->g_proto) {
    439 		case IPPROTO_GRE :
    440 			ifp->if_flags |= IFF_LINK0;
    441 			ifp->if_flags &= ~(IFF_LINK1|IFF_LINK2);
    442 			break;
    443 		case IPPROTO_MOBILE :
    444 			ifp->if_flags |= IFF_LINK2;
    445 			ifp->if_flags &= ~(IFF_LINK1|IFF_LINK2);
    446 			break;
    447 		default:
    448 			ifp->if_flags &= ~(IFF_LINK0|IFF_LINK1|IFF_LINK2);
    449 		}
    450 		break;
    451 	case GREGPROTO:
    452 		ifr->ifr_flags = sc->g_proto;
    453 		break;
    454 	case GRESADDRS:
    455 	case GRESADDRD:
    456 		if ((error = suser(p->p_ucred, &p->p_acflag)) != 0)
    457 			break;
    458 		/*
    459 	         * set tunnel endpoints, compute a less specific route
    460 	         * to the remote end and mark if as up
    461                  */
    462 		sa = &ifr->ifr_addr;
    463 		if (cmd == GRESADDRS )
    464 			sc->g_src = (satosin(sa))->sin_addr;
    465 		if (cmd == GRESADDRD )
    466 			sc->g_dst = (satosin(sa))->sin_addr;
    467 		if ((sc->g_src.s_addr != INADDR_ANY) &&
    468 		    (sc->g_dst.s_addr != INADDR_ANY)) {
    469 			if (sc->route.ro_rt != 0) /* free old route */
    470 				RTFREE(sc->route.ro_rt);
    471 			gre_compute_route(sc);
    472 			ifp->if_flags |= IFF_UP;
    473 		}
    474 		break;
    475 	case GREGADDRS:
    476 		si.sin_addr.s_addr = sc->g_src.s_addr;
    477 		sa = sintosa(&si);
    478 		ifr->ifr_addr = *sa;
    479 		break;
    480 	case GREGADDRD:
    481 		si.sin_addr.s_addr = sc->g_dst.s_addr;
    482 		sa = sintosa(&si);
    483 		ifr->ifr_addr = *sa;
    484 		break;
    485 	default:
    486 		error = EINVAL;
    487 	}
    488 
    489 	splx(s);
    490 	return (error);
    491 }
    492 
    493 /*
    494  * computes a route to our destination that is not the one
    495  * which would be taken by ip_output(), as this one will loop back to
    496  * us. If the interface is p2p as  a--->b, then a routing entry exists
    497  * If we now send a packet to b (e.g. ping b), this will come down here
    498  * gets src=a, dst=b tacked on and would from ip_ouput() sent back to
    499  * if_gre.
    500  * Goal here is to compute a route to b that is less specific than
    501  * a-->b. We know that this one exists as in normal operation we have
    502  * at least a default route which matches.
    503  */
    504 
    505 void
    506 gre_compute_route(struct gre_softc *sc)
    507 {
    508 	struct route *ro;
    509 	u_int32_t a, b, c;
    510 
    511 	ro = &sc->route;
    512 
    513 	memset(ro, 0, sizeof(struct route));
    514 	((struct sockaddr_in *)&ro->ro_dst)->sin_addr = sc->g_dst;
    515 	ro->ro_dst.sa_family = AF_INET;
    516 	ro->ro_dst.sa_len = sizeof(ro->ro_dst);
    517 
    518 	/*
    519 	 * toggle last bit, so our interface is not found, but a less
    520          * specific route. I'd rather like to specify a shorter mask,
    521  	 * but this is not possible. Should work though. XXX
    522 	 * there is a simpler way ...
    523          */
    524 	if ((sc->sc_if.if_flags & IFF_LINK1) == 0) {
    525 		a = ntohl(sc->g_dst.s_addr);
    526 		b = a & 0x01;
    527 		c = a & 0xfffffffe;
    528 		b = b ^ 0x01;
    529 		a = b | c;
    530 		((struct sockaddr_in *)&ro->ro_dst)->sin_addr.s_addr
    531 			= htonl(a);
    532 	}
    533 
    534 #ifdef DIAGNOSTIC
    535 	printf("%s: searching a route to ", sc->sc_if.if_xname);
    536 	gre_inet_ntoa(((struct sockaddr_in *)&ro->ro_dst)->sin_addr);
    537 #endif
    538 
    539 	rtalloc(ro);
    540 
    541 	/*
    542 	 * now change it back - else ip_output will just drop
    543          * the route and search one to this interface ...
    544          */
    545 	if ((sc->sc_if.if_flags & IFF_LINK1) == 0)
    546 		((struct sockaddr_in *)&ro->ro_dst)->sin_addr = sc->g_dst;
    547 
    548 #ifdef DIAGNOSTIC
    549 	printf(", choosing %s with gateway ",ro->ro_rt->rt_ifp->if_xname);
    550 	gre_inet_ntoa(((struct sockaddr_in *)(ro->ro_rt->rt_gateway))->sin_addr);
    551 	printf("\n");
    552 #endif
    553 }
    554 
    555 /*
    556  * do a checksum of a buffer - much like in_cksum, which operates on
    557  * mbufs.
    558  */
    559 
    560 u_short
    561 gre_in_cksum(u_short *p, u_int len)
    562 {
    563 	u_int sum = 0;
    564 	int nwords = len >> 1;
    565 
    566 	while (nwords-- != 0)
    567 		sum += *p++;
    568 
    569 		if (len & 1) {
    570 			union {
    571 				u_short w;
    572 				u_char c[2];
    573 			} u;
    574 			u.c[0] = *(u_char *)p;
    575 			u.c[1] = 0;
    576 			sum += u.w;
    577 		}
    578 
    579 		/* end-around-carry */
    580 		sum = (sum >> 16) + (sum & 0xffff);
    581 		sum += (sum >> 16);
    582 		return (~sum);
    583 }
    584 
    585 
    586 /* while testing ... */
    587 #ifdef DIAGNOSTIC
    588 void
    589 gre_inet_ntoa(struct in_addr in)
    590 {
    591 	char *p;
    592 
    593 	p = (char *)&in;
    594 #define UC(b)   (((int)b)&0xff)
    595 	printf("%d.%d.%d.%d", UC(p[0]), UC(p[1]), UC(p[2]), UC(p[3]));
    596 }
    597 
    598 #endif
    599 #endif
    600 
    601