Home | History | Annotate | Line # | Download | only in net
if_gre.c revision 1.8.4.1
      1 /*	$NetBSD: if_gre.c,v 1.8.4.1 1999/11/15 00:42:12 fvdl 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/proc.h>
     57 #include <sys/malloc.h>
     58 #include <sys/mbuf.h>
     59 #include <sys/buf.h>
     60 #include <sys/dkstat.h>
     61 #include <sys/protosw.h>
     62 #include <sys/socket.h>
     63 #include <sys/ioctl.h>
     64 #include <sys/sockio.h>
     65 #include <sys/file.h>
     66 #include <sys/tty.h>
     67 #include <sys/kernel.h>
     68 #include <sys/conf.h>
     69 #if __NetBSD__
     70 #include <sys/systm.h>
     71 #endif
     72 
     73 #include <machine/cpu.h>
     74 
     75 #include <net/ethertypes.h>
     76 #include <net/if.h>
     77 #include <net/if_types.h>
     78 #include <net/netisr.h>
     79 #include <net/route.h>
     80 
     81 #ifdef INET
     82 #include <netinet/in.h>
     83 #include <netinet/in_systm.h>
     84 #include <netinet/in_var.h>
     85 #include <netinet/ip.h>
     86 #include <netinet/ip_var.h>
     87 #else
     88 #error "Huh? if_gre without inet?"
     89 #endif
     90 
     91 #ifdef NS
     92 #include <netns/ns.h>
     93 #include <netns/ns_if.h>
     94 #endif
     95 
     96 #ifdef NETATALK
     97 #include <netatalk/at.h>
     98 #include <netatalk/at_var.h>
     99 #include <netatalk/at_extern.h>
    100 #endif
    101 
    102 #if NBPFILTER > 0
    103 #include <sys/time.h>
    104 #include <net/bpf.h>
    105 #endif
    106 
    107 #include <net/if_gre.h>
    108 
    109 #define GREMTU 1450	/* XXX this is below the standard MTU of
    110                          1500 Bytes, allowing for headers,
    111                          but we should possibly do path mtu discovery
    112                          before changing if state to up to find the
    113                          correct value */
    114 #define LINK_MASK (IFF_LINK0|IFF_LINK1|IFF_LINK2)
    115 
    116 struct gre_softc gre_softc[NGRE];
    117 
    118 
    119 void gre_compute_route(struct gre_softc *sc);
    120 #ifdef DIAGNOSTIC
    121 void gre_inet_ntoa(struct in_addr in);
    122 #endif
    123 
    124 void
    125 greattach(void)
    126 {
    127 	struct gre_softc *sc;
    128 	int i;
    129 
    130 	i = 0 ;
    131 	for (sc = gre_softc ; i < NGRE ; sc++ ) {
    132 		sprintf(sc->sc_if.if_xname, "gre%d", i++);
    133 		sc->sc_if.if_softc = sc;
    134 		sc->sc_if.if_type =  IFT_OTHER;
    135 		sc->sc_if.if_addrlen = 4;
    136 		sc->sc_if.if_hdrlen = 24; /* IP + GRE */
    137 		sc->sc_if.if_mtu = GREMTU;
    138 		sc->sc_if.if_flags = IFF_POINTOPOINT|IFF_MULTICAST;
    139 		sc->sc_if.if_output = gre_output;
    140 		sc->sc_if.if_ioctl = gre_ioctl;
    141 		sc->sc_if.if_collisions = 0;
    142 		sc->sc_if.if_ierrors = 0;
    143 		sc->sc_if.if_oerrors = 0;
    144 		sc->sc_if.if_ipackets = 0;
    145 		sc->sc_if.if_opackets = 0;
    146 		sc->g_dst.s_addr = sc->g_src.s_addr=INADDR_ANY;
    147 		sc->g_proto = IPPROTO_GRE;
    148 		if_attach(&sc->sc_if);
    149 #if 0
    150 #if NBPFILTER > 0
    151 		bpfattach(&sc->gre_bpf, &sc->sc_if, DLT_RAW, sizeof(u_int32_t) );
    152 #endif
    153 #endif
    154 
    155 	}
    156 }
    157 
    158 
    159 /*
    160  * The output routine. Takes a packet and encapsulates it in the protocol
    161  * given by sc->g_proto. See also RFC 1701 and RFC 2004
    162  */
    163 
    164 #if 0
    165 struct ip ip_h;
    166 #endif
    167 struct mobile_h mob_h;
    168 
    169 int
    170 gre_output(struct ifnet *ifp, struct mbuf *m, struct sockaddr *dst,
    171 	   struct rtentry *rt)
    172 {
    173 	int error = 0;
    174 	struct gre_softc *sc = (struct gre_softc *)(ifp->if_softc);
    175 	struct greip *gh;
    176 	struct ip *inp;
    177 	u_char ttl, osrc;
    178 	u_short etype = 0;
    179 
    180 
    181 	gh = NULL;
    182 	inp = NULL;
    183 	osrc = 0;
    184 
    185 #if 0
    186 #if NBPFILTER >0
    187 
    188 	if (sc->gre_bpf) {
    189 		/* see comment of other if_foo.c files */
    190 		struct mbuf m0;
    191 		u_int af = dst->sa_family;
    192 
    193 		m0.m_next = m;
    194 		m0.m_len = 4;
    195 		m0.m_data = (char *)&af;
    196 
    197 		bpf_mtap(ifp->if_bpf, &m0);
    198 	}
    199 #endif
    200 #endif
    201 
    202 	ttl = 255;
    203 
    204 	if (sc->g_proto == IPPROTO_MOBILE) {
    205 		if (dst->sa_family == AF_INET) {
    206 			struct mbuf *m0;
    207 			int msiz;
    208 
    209 			inp = mtod(m, struct ip *);
    210 
    211 			memset(&mob_h, 0, MOB_H_SIZ_L);
    212 			mob_h.proto = (inp->ip_p) << 8;
    213 			mob_h.odst = inp->ip_dst.s_addr;
    214 			inp->ip_dst.s_addr = sc->g_dst.s_addr;
    215 
    216 			/*
    217 			 * If the packet comes from our host, we only change
    218 			 * the destination address in the IP header.
    219 			 * Else we also need to save and change the source
    220 			 */
    221 			if (in_hosteq(inp->ip_src, sc->g_src)) {
    222 				msiz = MOB_H_SIZ_S;
    223 			} else {
    224 				mob_h.proto |= MOB_H_SBIT;
    225 				mob_h.osrc = inp->ip_src.s_addr;
    226 				inp->ip_src.s_addr = sc->g_src.s_addr;
    227 				msiz = MOB_H_SIZ_L;
    228 			}
    229 			HTONS(mob_h.proto);
    230 			mob_h.hcrc = gre_in_cksum((u_short *)&mob_h, msiz);
    231 
    232 			if ((m->m_data - msiz) < m->m_pktdat) {
    233 				/* need new mbuf */
    234 				MGETHDR(m0, M_DONTWAIT, MT_HEADER);
    235 				if (m0 == NULL) {
    236 					IF_DROP(&ifp->if_snd);
    237 					m_freem(m);
    238 					return (ENOBUFS);
    239 				}
    240 				m0->m_next = m;
    241 				m->m_data += sizeof(struct ip);
    242 				m->m_len -= sizeof(struct ip);
    243 				m0->m_pkthdr.len = m->m_pkthdr.len + msiz;
    244 				m0->m_len = msiz + sizeof(struct ip);
    245 				m0->m_data += max_linkhdr;
    246 				memcpy(mtod(m0, caddr_t), (caddr_t)inp,
    247 				       sizeof(struct ip));
    248 				m = m0;
    249 			} else {  /* we have some spave left in the old one */
    250 				m->m_data -= msiz;
    251 				m->m_len += msiz;
    252 				m->m_pkthdr.len += msiz;
    253 				memmove(mtod(m, caddr_t), inp,
    254 					sizeof(struct ip));
    255 			}
    256 			inp=mtod(m, struct ip *);
    257 			memcpy((caddr_t)(inp + 1), &mob_h, (unsigned)msiz);
    258 			NTOHS(inp->ip_len);
    259 			inp->ip_len += msiz;
    260 		} else {  /* AF_INET */
    261 			IF_DROP(&ifp->if_snd);
    262 			m_freem(m);
    263 			return (EINVAL);
    264 		}
    265 	} else if (sc->g_proto == IPPROTO_GRE) {
    266 		switch(dst->sa_family) {
    267 		case AF_INET:
    268 			inp = mtod(m, struct ip *);
    269 			ttl = inp->ip_ttl;
    270 			etype = ETHERTYPE_IP;
    271 			break;
    272 #ifdef NETATALK
    273 		case AF_APPLETALK:
    274 			etype = ETHERTYPE_ATALK;
    275 			break;
    276 #endif
    277 #ifdef NS
    278 		case AF_NS:
    279 			etype = ETHERTYPE_NS;
    280 			break;
    281 #endif
    282 		default:
    283 			IF_DROP(&ifp->if_snd);
    284 			m_freem(m);
    285 			return (EAFNOSUPPORT);
    286 		}
    287 		M_PREPEND(m, sizeof(struct greip), M_DONTWAIT);
    288 	} else {
    289 		error = EINVAL;
    290 		IF_DROP(&ifp->if_snd);
    291 		m_freem(m);
    292 		return (error);
    293 	}
    294 
    295 
    296 	if (m == NULL) {
    297 		IF_DROP(&ifp->if_snd);
    298 		return (ENOBUFS);
    299 	}
    300 
    301 	gh = mtod(m, struct greip *);
    302 	if (sc->g_proto == IPPROTO_GRE) {
    303 		/* we don't have any GRE flags for now */
    304 
    305 		memset((void *)&gh->gi_g, 0, sizeof(struct gre_h));
    306 		gh->gi_ptype = htons(etype);
    307 	}
    308 
    309 	gh->gi_pr = sc->g_proto;
    310 	if (sc->g_proto != IPPROTO_MOBILE) {
    311 		gh->gi_src = sc->g_src;
    312 		gh->gi_dst = sc->g_dst;
    313 		((struct ip*)gh)->ip_hl = (sizeof(struct ip)) >> 2;
    314 		((struct ip*)gh)->ip_ttl = ttl;
    315 		((struct ip*)gh)->ip_tos = inp->ip_tos;
    316 	    gh->gi_len = m->m_pkthdr.len;
    317 	}
    318 
    319 	ifp->if_opackets++;
    320 	ifp->if_obytes += m->m_pkthdr.len;
    321 	/* send it off */
    322 	error = ip_output(m, NULL, &sc->route, 0, NULL);
    323 	if (error)
    324 		ifp->if_oerrors++;
    325 	return (error);
    326 
    327 }
    328 
    329 int
    330 gre_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
    331 {
    332 
    333 	struct ifaddr *ifa = (struct ifaddr *)data;
    334 	struct ifreq *ifr = (struct ifreq *)data;
    335 	struct in_ifaddr *ia = (struct in_ifaddr *)data;
    336 	struct gre_softc *sc = ifp->if_softc;
    337 	int s;
    338 	struct sockaddr_in si;
    339 	struct sockaddr *sa = NULL;
    340 	int error;
    341 
    342 	error = 0;
    343 
    344 	s = splimp();
    345 	switch(cmd) {
    346 	case SIOCSIFADDR:
    347 	case SIOCSIFDSTADDR:
    348 		/*
    349                  * set tunnel endpoints in case that we "only"
    350                  * have ip over ip encapsulation. This allows to
    351                  * set tunnel endpoints with ifconfig.
    352                  */
    353 		if (ifa->ifa_addr->sa_family == AF_INET) {
    354 			sa = ifa->ifa_addr;
    355 			sc->g_src = (satosin(sa))->sin_addr;
    356 			sc->g_dst = ia->ia_dstaddr.sin_addr;
    357 			if ((sc->g_src.s_addr != INADDR_ANY) &&
    358 			    (sc->g_dst.s_addr != INADDR_ANY)) {
    359 				if (sc->route.ro_rt != 0) /* free old route */
    360 					RTFREE(sc->route.ro_rt);
    361 				gre_compute_route(sc);
    362 				ifp->if_flags |= IFF_UP;
    363 			}
    364 		}
    365 		break;
    366 	case SIOCSIFFLAGS:
    367 		if ((sc->g_dst.s_addr == INADDR_ANY) ||
    368 		    (sc->g_src.s_addr == INADDR_ANY))
    369 			ifp->if_flags &= ~IFF_UP;
    370 
    371 		switch(ifr->ifr_flags & LINK_MASK) {
    372 			case IFF_LINK0:
    373 				sc->g_proto = IPPROTO_GRE;
    374 				ifp->if_flags |= IFF_LINK0;
    375 				ifp->if_flags &= ~(IFF_LINK1|IFF_LINK2);
    376 				break;
    377 			case IFF_LINK2:
    378 				sc->g_proto = IPPROTO_MOBILE;
    379 				ifp->if_flags |= IFF_LINK2;
    380 				ifp->if_flags &= ~(IFF_LINK0|IFF_LINK1);
    381 				break;
    382 		}
    383 		break;
    384 	case SIOCSIFMTU:
    385 		if (ifr->ifr_mtu > GREMTU || ifr->ifr_mtu < 576) {
    386 			error = EINVAL;
    387 			break;
    388 		}
    389 		ifp->if_mtu = ifr->ifr_mtu;
    390 		break;
    391 	case SIOCGIFMTU:
    392 		ifr->ifr_mtu = sc->sc_if.if_mtu;
    393 		break;
    394 	case SIOCADDMULTI:
    395 	case SIOCDELMULTI:
    396 		if (ifr == 0) {
    397 			error = EAFNOSUPPORT;
    398 			break;
    399 		}
    400 		switch (ifr->ifr_addr.sa_family) {
    401 #ifdef INET
    402 		case AF_INET:
    403 			break;
    404 #endif
    405 		default:
    406 			error = EAFNOSUPPORT;
    407 			break;
    408 		}
    409 		break;
    410 	case GRESPROTO:
    411 		sc->g_proto = ifr->ifr_flags;
    412 		switch (sc->g_proto) {
    413 		case IPPROTO_GRE :
    414 			ifp->if_flags |= IFF_LINK0;
    415 			ifp->if_flags &= ~(IFF_LINK1|IFF_LINK2);
    416 			break;
    417 		case IPPROTO_MOBILE :
    418 			ifp->if_flags |= IFF_LINK2;
    419 			ifp->if_flags &= ~(IFF_LINK1|IFF_LINK2);
    420 			break;
    421 		default:
    422 			ifp->if_flags &= ~(IFF_LINK0|IFF_LINK1|IFF_LINK2);
    423 		}
    424 		break;
    425 	case GREGPROTO:
    426 		ifr->ifr_flags = sc->g_proto;
    427 		break;
    428 	case GRESADDRS:
    429 	case GRESADDRD:
    430 		/*
    431 	         * set tunnel endpoints, compute a less specific route
    432 	         * to the remote end and mark if as up
    433                  */
    434 		sa = &ifr->ifr_addr;
    435 		if (cmd == GRESADDRS )
    436 			sc->g_src = (satosin(sa))->sin_addr;
    437 		if (cmd == GRESADDRD )
    438 			sc->g_dst = (satosin(sa))->sin_addr;
    439 		if ((sc->g_src.s_addr != INADDR_ANY) &&
    440 		    (sc->g_dst.s_addr != INADDR_ANY)) {
    441 			if (sc->route.ro_rt != 0) /* free old route */
    442 				RTFREE(sc->route.ro_rt);
    443 			gre_compute_route(sc);
    444 			ifp->if_flags |= IFF_UP;
    445 		}
    446 		break;
    447 	case GREGADDRS:
    448 		si.sin_addr.s_addr = sc->g_src.s_addr;
    449 		sa = sintosa(&si);
    450 		ifr->ifr_addr = *sa;
    451 		break;
    452 	case GREGADDRD:
    453 		si.sin_addr.s_addr = sc->g_dst.s_addr;
    454 		sa = sintosa(&si);
    455 		ifr->ifr_addr = *sa;
    456 		break;
    457 	default:
    458 		error = EINVAL;
    459 	}
    460 
    461 	splx(s);
    462 	return (error);
    463 }
    464 
    465 /*
    466  * computes a route to our destination that is not the one
    467  * which would be taken by ip_output(), as this one will loop back to
    468  * us. If the interface is p2p as  a--->b, then a routing entry exists
    469  * If we now send a packet to b (e.g. ping b), this will come down here
    470  * gets src=a, dst=b tacked on and would from ip_ouput() sent back to
    471  * if_gre.
    472  * Goal here is to compute a route to b that is less specific than
    473  * a-->b. We know that this one exists as in normal operation we have
    474  * at least a default route which matches.
    475  */
    476 
    477 void
    478 gre_compute_route(struct gre_softc *sc)
    479 {
    480 	struct route *ro;
    481 	u_int32_t a, b, c;
    482 
    483 	ro = &sc->route;
    484 
    485 	memset(ro, 0, sizeof(struct route));
    486 	((struct sockaddr_in *)&ro->ro_dst)->sin_addr = sc->g_dst;
    487 	ro->ro_dst.sa_family = AF_INET;
    488 	ro->ro_dst.sa_len = sizeof(ro->ro_dst);
    489 
    490 	/*
    491 	 * toggle last bit, so our interface is not found, but a less
    492          * specific route. I'd rather like to specify a shorter mask,
    493  	 * but this is not possible. Should work though. XXX
    494 	 * there is a simpler way ...
    495          */
    496 	if ((sc->sc_if.if_flags & IFF_LINK1) == 0) {
    497 		a = ntohl(sc->g_dst.s_addr);
    498 		b = a & 0x01;
    499 		c = a & 0xfffffffe;
    500 		b = b ^ 0x01;
    501 		a = b | c;
    502 		((struct sockaddr_in *)&ro->ro_dst)->sin_addr.s_addr
    503 			= htonl(a);
    504 	}
    505 
    506 #ifdef DIAGNOSTIC
    507 	printf("%s: searching a route to ", sc->sc_if.if_xname);
    508 	gre_inet_ntoa(((struct sockaddr_in *)&ro->ro_dst)->sin_addr);
    509 #endif
    510 
    511 	rtalloc(ro);
    512 
    513 	/*
    514 	 * now change it back - else ip_output will just drop
    515          * the route and search one to this interface ...
    516          */
    517 	if ((sc->sc_if.if_flags & IFF_LINK1) == 0)
    518 		((struct sockaddr_in *)&ro->ro_dst)->sin_addr = sc->g_dst;
    519 
    520 #ifdef DIAGNOSTIC
    521 	printf(", choosing %s with gateway ",ro->ro_rt->rt_ifp->if_xname);
    522 	gre_inet_ntoa(((struct sockaddr_in *)(ro->ro_rt->rt_gateway))->sin_addr);
    523 	printf("\n");
    524 #endif
    525 }
    526 
    527 /*
    528  * do a checksum of a buffer - much like in_cksum, which operates on
    529  * mbufs.
    530  */
    531 
    532 u_short
    533 gre_in_cksum(u_short *p, u_int len)
    534 {
    535 	u_int sum = 0;
    536 	int nwords = len >> 1;
    537 
    538 	while (nwords-- != 0)
    539 		sum += *p++;
    540 
    541 		if (len & 1) {
    542 			union {
    543 				u_short w;
    544 				u_char c[2];
    545 			} u;
    546 			u.c[0] = *(u_char *)p;
    547 			u.c[1] = 0;
    548 			sum += u.w;
    549 		}
    550 
    551 		/* end-around-carry */
    552 		sum = (sum >> 16) + (sum & 0xffff);
    553 		sum += (sum >> 16);
    554 		return (~sum);
    555 }
    556 
    557 
    558 /* while testing ... */
    559 #ifdef DIAGNOSTIC
    560 void
    561 gre_inet_ntoa(struct in_addr in)
    562 {
    563 	char *p;
    564 
    565 	p = (char *)&in;
    566 #define UC(b)   (((int)b)&0xff)
    567 	printf("%d.%d.%d.%d", UC(p[0]), UC(p[1]), UC(p[2]), UC(p[3]));
    568 }
    569 
    570 #endif
    571 #endif
    572 
    573