xref: /netbsd-src/sys/net/if_gre.c (revision bada23909e740596d0a3785a73bd3583a9807fb8)
1 /*	$NetBSD: if_gre.c,v 1.7 1999/03/12 22:42:31 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@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 greattach(void)
124 {
125 
126 	register struct gre_softc *sc;
127 	register 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 struct ip ip_h;
164 struct mobile_h mob_h;
165 
166 int
167 gre_output(ifp, m, dst, rt)
168         struct ifnet *ifp;
169         register struct mbuf *m;
170         struct sockaddr *dst;
171         register 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 the
218 			 * destination address in the IP header. Else we also need
219 			 * to save and change the source
220 			 */
221 
222 			if (in_hosteq(inp->ip_src, sc->g_src)) {
223 				msiz=MOB_H_SIZ_S;
224 			} else {
225 				mob_h.proto |= MOB_H_SBIT;
226 				mob_h.osrc=inp->ip_src.s_addr;
227 				inp->ip_src.s_addr=sc->g_src.s_addr;
228 				msiz=MOB_H_SIZ_L;
229 			}
230 			HTONS(mob_h.proto);
231 			mob_h.hcrc=gre_in_cksum((u_short *)&mob_h,msiz);
232 
233 			if ((m->m_data - msiz) < m->m_pktdat) {
234 				/* need new mbuf */
235 				MGETHDR(m0, M_DONTWAIT, MT_HEADER);
236 				if (m0==NULL) {
237 					IF_DROP(&ifp->if_snd);
238 					m_freem(m);
239 					return(ENOBUFS);
240 				}
241 				m0->m_next=m;
242 				m->m_data += sizeof(struct ip);
243 				m->m_len -= sizeof(struct ip);
244 				m0->m_pkthdr.len=m->m_pkthdr.len+msiz;
245 				m0->m_len = msiz + sizeof(struct ip);
246 				m0->m_data += max_linkhdr;
247 				memcpy(mtod(m0, caddr_t), (caddr_t) inp, 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, 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 	}
325 	return(error);
326 
327 }
328 
329 int
330 gre_ioctl(ifp, cmd, data)
331 		struct ifnet *ifp;
332 		u_long cmd;
333 		caddr_t data;
334 {
335 
336 	register struct ifaddr *ifa = (struct ifaddr *)data;
337 	register struct ifreq *ifr = (struct ifreq *)data;
338 	register struct in_ifaddr *ia = (struct in_ifaddr *)data;
339 	register struct gre_softc *sc = ifp->if_softc;
340 	int s;
341 	struct sockaddr_in si;
342 	struct sockaddr *sa =NULL;
343 	int error;
344 
345 	error= 0;
346 
347 	s = splimp();
348 	switch(cmd) {
349 	case SIOCSIFADDR:
350 	case SIOCSIFDSTADDR:
351 		/*
352                  * set tunnel endpoints in case that we "only"
353                  * have ip over ip encapsulation. This allows to
354                  * set tunnel endpoints with ifconfig.
355                  */
356 		if (ifa->ifa_addr->sa_family == AF_INET) {
357 			sa = ifa->ifa_addr;
358 			sc->g_src = (satosin(sa))->sin_addr;
359 			sc->g_dst = ia->ia_dstaddr.sin_addr;
360 			if ((sc->g_src.s_addr != INADDR_ANY) &&
361 			    (sc->g_dst.s_addr != INADDR_ANY)) {
362 				if (sc->route.ro_rt != 0) /* free old route */
363 					RTFREE(sc->route.ro_rt);
364 				gre_compute_route(sc);
365 				ifp->if_flags |= IFF_UP;
366 			}
367 		}
368 		break;
369 	case SIOCSIFFLAGS:
370 		if ((sc->g_dst.s_addr== INADDR_ANY) ||
371 		    (sc->g_src.s_addr== INADDR_ANY))
372 			ifp->if_flags &= ~IFF_UP;
373 
374 		switch(ifr->ifr_flags & LINK_MASK) {
375 			case IFF_LINK0:
376 				sc->g_proto = IPPROTO_GRE;
377 				ifp->if_flags |= IFF_LINK0;
378 				ifp->if_flags &= ~(IFF_LINK1|IFF_LINK2);
379 				break;
380 			case IFF_LINK2:
381 				sc->g_proto = IPPROTO_MOBILE;
382 				ifp->if_flags |= IFF_LINK2;
383 				ifp->if_flags &= ~(IFF_LINK0|IFF_LINK1);
384 				break;
385 		}
386 		break;
387 	case SIOCSIFMTU:
388 		if (ifr->ifr_mtu > GREMTU || ifr->ifr_mtu < 576) {
389 			error = EINVAL;
390 			break;
391 		}
392 		ifp->if_mtu = ifr->ifr_mtu;
393 		break;
394 	case SIOCGIFMTU:
395 		ifr->ifr_mtu = sc->sc_if.if_mtu;
396 		break;
397 	case SIOCADDMULTI:
398 	case SIOCDELMULTI:
399 		if (ifr == 0 ) {
400 			error = EAFNOSUPPORT;
401 			break;
402 		}
403 		switch(ifr->ifr_addr.sa_family) {
404 #ifdef INET
405 		case AF_INET:
406 			break;
407 #endif
408 		default:
409 			error = EAFNOSUPPORT;
410 			break;
411 		}
412 		break;
413 	case GRESPROTO:
414 		sc->g_proto = ifr->ifr_flags;
415 		switch (sc->g_proto) {
416 		case IPPROTO_GRE :
417 			ifp->if_flags |= IFF_LINK0;
418 			ifp->if_flags &= ~(IFF_LINK1|IFF_LINK2);
419 			break;
420 		case IPPROTO_MOBILE :
421 			ifp->if_flags |= IFF_LINK2;
422 			ifp->if_flags &= ~(IFF_LINK1|IFF_LINK2);
423 			break;
424 		default:
425 			ifp->if_flags &= ~(IFF_LINK0|IFF_LINK1|IFF_LINK2);
426 		}
427 		break;
428 	case GREGPROTO:
429 		ifr->ifr_flags = sc->g_proto;
430 		break;
431 	case GRESADDRS:
432 	case GRESADDRD:
433 		/*
434 	         * set tunnel endpoints, compute a less specific route
435 	         * to the remote end and mark if as up
436                  */
437 		sa = &ifr->ifr_addr;
438 		if (cmd == GRESADDRS )
439 			sc->g_src = (satosin(sa))->sin_addr;
440 		if (cmd == GRESADDRD )
441 			sc->g_dst = (satosin(sa))->sin_addr;
442 		if ((sc->g_src.s_addr != INADDR_ANY) &&
443 		    (sc->g_dst.s_addr != INADDR_ANY)) {
444 			if (sc->route.ro_rt != 0) /* free old route */
445 				RTFREE(sc->route.ro_rt);
446 			gre_compute_route(sc);
447 			ifp->if_flags |= IFF_UP;
448 		}
449 		break;
450 	case GREGADDRS:
451 		si.sin_addr.s_addr = sc->g_src.s_addr;
452 		sa=sintosa(&si);
453 		ifr->ifr_addr = *sa;
454 		break;
455 	case GREGADDRD:
456 		si.sin_addr.s_addr = sc->g_dst.s_addr;
457 		sa=sintosa(&si);
458 		ifr->ifr_addr = *sa;
459 		break;
460 	default:
461 		error = EINVAL;
462 	}
463 
464 	splx(s);
465 	return(error);
466 }
467 
468 /*
469  * computes a route to our destination that is not the one
470  * which would be taken by ip_output(), as this one will loop back to
471  * us. If the interface is p2p as  a--->b, then a routing entry exists
472  * If we now send a packet to b (e.g. ping b), this will come down here
473  * gets src=a, dst=b tacked on and would from ip_ouput() sent back to
474  * if_gre.
475  * Goal here is to compute a route to b that is less specific than
476  * a-->b. We know that this one exists as in normal operation we have
477  * at least a default route which matches.
478  */
479 
480 void gre_compute_route(struct gre_softc *sc)
481 {
482 	struct route *ro;
483 	u_int32_t a,b,c;
484 
485 	ro=&sc->route;
486 
487 	memset(ro,0,sizeof(struct route));
488 	((struct sockaddr_in *)&ro->ro_dst)->sin_addr=sc->g_dst;
489 	ro->ro_dst.sa_family=AF_INET;
490 	ro->ro_dst.sa_len=sizeof(ro->ro_dst);
491 	/*
492 	 * toggle last bit, so our interface is not found, but a less
493          * specific route. I'd rather like to specify a shorter mask,
494  	 * but this is not possible. Should work though. XXX
495 	 * there is a simpler way ...
496          */
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=htonl(a);
503 
504 #ifdef DIAGNOSTIC
505 printf("%s: searching a route to ",sc->sc_if.if_xname);
506 gre_inet_ntoa(((struct sockaddr_in *)&ro->ro_dst)->sin_addr);
507 #endif
508 
509 	rtalloc(ro);
510 
511 	/*
512 	 * now change it back - else ip_output will just drop
513          * the route and search one to this interface ...
514          */
515 	((struct sockaddr_in *)&ro->ro_dst)->sin_addr=sc->g_dst;
516 
517 #ifdef DIAGNOSTIC
518 printf(", choosing %s with gateway ",ro->ro_rt->rt_ifp->if_xname);
519 gre_inet_ntoa(((struct sockaddr_in *)(ro->ro_rt->rt_gateway))->sin_addr);
520 printf("\n");
521 #endif
522 }
523 
524 /*
525  * do a checksum of a buffer - much like in_cksum, which operates on
526  * mbufs.
527  */
528 
529 u_short
530 gre_in_cksum(p, len)
531 	u_short *p;
532 	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(in)
561         struct in_addr in;
562 {
563         register 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