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