xref: /openbsd-src/sys/netinet/ip_gre.c (revision 4c1e55dc91edd6e69ccc60ce855900fbc12cf34f)
1 /*      $OpenBSD: ip_gre.c,v 1.44 2011/07/09 00:47:18 henning Exp $ */
2 /*	$NetBSD: ip_gre.c,v 1.9 1999/10/25 19:18:11 drochner Exp $ */
3 
4 /*
5  * Copyright (c) 1998 The NetBSD Foundation, Inc.
6  * All rights reserved.
7  *
8  * This code is derived from software contributed to The NetBSD Foundation
9  * by Heiko W.Rupp <hwr@pilhuhn.de>
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following conditions
13  * are met:
14  * 1. Redistributions of source code must retain the above copyright
15  *    notice, this list of conditions and the following disclaimer.
16  * 2. Redistributions in binary form must reproduce the above copyright
17  *    notice, this list of conditions and the following disclaimer in the
18  *    documentation and/or other materials provided with the distribution.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
21  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
22  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
24  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30  * POSSIBILITY OF SUCH DAMAGE.
31  */
32 
33 /*
34  * decapsulate tunneled packets and send them on
35  * output half is in net/if_gre.[ch]
36  * This currently handles IPPROTO_GRE, IPPROTO_MOBILE
37  */
38 
39 
40 #include "gre.h"
41 #if NGRE > 0
42 
43 #include <sys/param.h>
44 #include <sys/systm.h>
45 #include <sys/mbuf.h>
46 #include <sys/protosw.h>
47 #include <sys/socket.h>
48 #include <sys/socketvar.h>
49 #include <sys/proc.h>
50 #include <sys/sysctl.h>
51 #include <net/if.h>
52 #include <net/netisr.h>
53 #include <net/route.h>
54 #include <net/bpf.h>
55 
56 #ifdef INET
57 #include <netinet/in.h>
58 #include <netinet/in_var.h>
59 #include <netinet/in_systm.h>
60 #include <netinet/ip.h>
61 #include <netinet/ip_var.h>
62 #include <netinet/ip_gre.h>
63 #include <netinet/if_ether.h>
64 #include <netinet/in_pcb.h>
65 #else
66 #error "ip_gre used without inet"
67 #endif
68 
69 #ifdef MPLS
70 #include <netmpls/mpls.h>
71 #endif
72 
73 #include "bpfilter.h"
74 #include "pf.h"
75 
76 #if NPF > 0
77 #include <net/pfvar.h>
78 #endif
79 
80 #ifdef PIPEX
81 #include <net/pipex.h>
82 #endif
83 
84 /* Needs IP headers. */
85 #include <net/if_gre.h>
86 
87 struct gre_softc *gre_lookup(struct mbuf *, u_int8_t);
88 int gre_input2(struct mbuf *, int, u_char);
89 
90 /*
91  * Decapsulate.
92  * Does the real work and is called from gre_input() (above)
93  * returns 0 if packet is not yet processed
94  * and 1 if it needs no further processing
95  * proto is the protocol number of the "calling" foo_input()
96  * routine.
97  */
98 
99 int
100 gre_input2(struct mbuf *m, int hlen, u_char proto)
101 {
102 	struct greip *gip;
103 	int s;
104 	struct ifqueue *ifq;
105 	struct gre_softc *sc;
106 	u_short flags;
107 	u_int af;
108 
109 	if ((sc = gre_lookup(m, proto)) == NULL) {
110 		/* No matching tunnel or tunnel is down. */
111 		return (0);
112 	}
113 
114 	if (m->m_len < sizeof(*gip)) {
115 		m = m_pullup(m, sizeof(*gip));
116 		if (m == NULL)
117 			return (ENOBUFS);
118 	}
119 	gip = mtod(m, struct greip *);
120 
121 	m->m_pkthdr.rcvif = &sc->sc_if;
122 	m->m_pkthdr.rdomain = sc->sc_if.if_rdomain;
123 
124 	sc->sc_if.if_ipackets++;
125 	sc->sc_if.if_ibytes += m->m_pkthdr.len;
126 
127 	switch (proto) {
128 	case IPPROTO_GRE:
129 		hlen += sizeof (struct gre_h);
130 
131 		/* process GRE flags as packet can be of variable len */
132 		flags = ntohs(gip->gi_flags);
133 
134 		/* Checksum & Offset are present */
135 		if ((flags & GRE_CP) | (flags & GRE_RP))
136 			hlen += 4;
137 
138 		/* We don't support routing fields (variable length) */
139 		if (flags & GRE_RP)
140 			return (0);
141 
142 		if (flags & GRE_KP)
143 			hlen += 4;
144 
145 		if (flags & GRE_SP)
146 			hlen += 4;
147 
148 		switch (ntohs(gip->gi_ptype)) { /* ethertypes */
149 		case GREPROTO_WCCP:
150 			/* WCCP/GRE:
151 			 *   So far as I can see (and test) it seems that Cisco's WCCP
152 			 *   GRE tunnel is precisely a IP-in-GRE tunnel that differs
153 			 *   only in its protocol number.  At least, it works for me.
154 			 *
155 			 *   The Internet Draft can be found if you look for
156 			 *     draft-forster-wrec-wccp-v1-00.txt
157 			 *
158 			 *   So yes, we're doing a fall-through (unless, of course,
159 			 *   net.inet.gre.wccp is 0).
160 			 */
161 			if (!gre_wccp)
162 				return (0);
163 		case ETHERTYPE_IP: /* shouldn't need a schednetisr(), as */
164 			ifq = &ipintrq;          /* we are in ip_input */
165 			af = AF_INET;
166 			break;
167 #ifdef INET6
168 		case ETHERTYPE_IPV6:
169 		        ifq = &ip6intrq;
170 			schednetisr(NETISR_IPV6);
171 			af = AF_INET6;
172 			break;
173 #endif
174 		case 0:
175 			/* keepalive reply, retrigger hold timer */
176 			gre_recv_keepalive(sc);
177 			m_freem(m);
178 			return (1);
179 #ifdef MPLS
180 		case ETHERTYPE_MPLS:
181 		case ETHERTYPE_MPLS_MCAST:
182 			ifq = &mplsintrq;
183 			schednetisr(NETISR_MPLS);
184 			af = AF_MPLS;
185 			break;
186 #endif
187 		default:	   /* others not yet supported */
188 			return (0);
189 		}
190 		break;
191 	default:
192 		/* others not yet supported */
193 		return (0);
194 	}
195 
196 	if (hlen > m->m_pkthdr.len) {
197 		m_freem(m);
198 		return (EINVAL);
199 	}
200 	m_adj(m, hlen);
201 
202 #if NBPFILTER > 0
203         if (sc->sc_if.if_bpf)
204 		bpf_mtap_af(sc->sc_if.if_bpf, af, m, BPF_DIRECTION_IN);
205 #endif
206 
207 #if NPF > 0
208 	pf_pkt_addr_changed(m);
209 #endif
210 
211 	s = splnet();		/* possible */
212 	IF_INPUT_ENQUEUE(ifq, m);
213 	splx(s);
214 
215 	return (1);	/* packet is done, no further processing needed */
216 }
217 
218 /*
219  * Decapsulate a packet and feed it back through ip_input (this
220  * routine is called whenever IP gets a packet with proto type
221  * IPPROTO_GRE and a local destination address).
222  */
223 void
224 gre_input(struct mbuf *m, ...)
225 {
226 	int hlen, ret;
227 	va_list ap;
228 
229 	va_start(ap, m);
230 	hlen = va_arg(ap, int);
231 	va_end(ap);
232 
233 	if (!gre_allow) {
234 	        m_freem(m);
235 		return;
236 	}
237 
238 #ifdef PIPEX
239 	if (pipex_enable) {
240 		struct pipex_session *session;
241 
242 		if ((session = pipex_pptp_lookup_session(m)) != NULL) {
243 			if (pipex_pptp_input(m, session) == NULL)
244 				return;
245 		}
246 	}
247 #endif
248 
249 	ret = gre_input2(m, hlen, IPPROTO_GRE);
250 	/*
251 	 * ret == 0: packet not processed, but input from here
252 	 * means no matching tunnel that is up is found.
253 	 * we inject it to raw ip socket to see if anyone picks it up.
254 	 * possible that we received a WCCPv1-style GRE packet
255 	 * but we're not set to accept them.
256 	 */
257 	if (!ret)
258 		rip_input(m, hlen, IPPROTO_GRE);
259 }
260 
261 /*
262  * Input routine for IPPROTO_MOBILE.
263  * This is a little bit different from the other modes, as the
264  * encapsulating header was not prepended, but instead inserted
265  * between IP header and payload.
266  */
267 
268 void
269 gre_mobile_input(struct mbuf *m, ...)
270 {
271 	struct ip *ip;
272 	struct mobip_h *mip;
273 	struct ifqueue *ifq;
274 	struct gre_softc *sc;
275 	int hlen, s;
276 	va_list ap;
277 	u_char osrc = 0;
278 	int msiz;
279 
280 	va_start(ap, m);
281 	hlen = va_arg(ap, int);
282 	va_end(ap);
283 
284 	if (!ip_mobile_allow) {
285 	        m_freem(m);
286 		return;
287 	}
288 
289 	if ((sc = gre_lookup(m, IPPROTO_MOBILE)) == NULL) {
290 		/* No matching tunnel or tunnel is down. */
291 		m_freem(m);
292 		return;
293 	}
294 
295 	if (m->m_len < sizeof(*mip)) {
296 		m = m_pullup(m, sizeof(*mip));
297 		if (m == NULL)
298 			return;
299 	}
300 	ip = mtod(m, struct ip *);
301 	mip = mtod(m, struct mobip_h *);
302 
303 	m->m_pkthdr.rcvif = &sc->sc_if;
304 
305 	sc->sc_if.if_ipackets++;
306 	sc->sc_if.if_ibytes += m->m_pkthdr.len;
307 
308 	if (ntohs(mip->mh.proto) & MOB_H_SBIT) {
309 		osrc = 1;
310 		msiz = MOB_H_SIZ_L;
311 		mip->mi.ip_src.s_addr = mip->mh.osrc;
312 	} else
313 		msiz = MOB_H_SIZ_S;
314 
315 	if (m->m_len < (ip->ip_hl << 2) + msiz) {
316 		m = m_pullup(m, (ip->ip_hl << 2) + msiz);
317 		if (m == NULL)
318 			return;
319 		ip = mtod(m, struct ip *);
320 		mip = mtod(m, struct mobip_h *);
321 	}
322 
323 	mip->mi.ip_dst.s_addr = mip->mh.odst;
324 	mip->mi.ip_p = (ntohs(mip->mh.proto) >> 8);
325 
326 	if (gre_in_cksum((u_short *) &mip->mh, msiz) != 0) {
327 		m_freem(m);
328 		return;
329 	}
330 
331 	bcopy(ip + (ip->ip_hl << 2) + msiz, ip + (ip->ip_hl << 2),
332 	      m->m_len - msiz - (ip->ip_hl << 2));
333 
334 	m->m_len -= msiz;
335 	ip->ip_len = htons(ntohs(ip->ip_len) - msiz);
336 	m->m_pkthdr.len -= msiz;
337 
338 	ip->ip_sum = 0;
339 	ip->ip_sum = in_cksum(m,(ip->ip_hl << 2));
340 
341 	ifq = &ipintrq;
342 
343 #if NBPFILTER > 0
344         if (sc->sc_if.if_bpf)
345 		bpf_mtap_af(sc->sc_if.if_bpf, AF_INET, m, BPF_DIRECTION_IN);
346 #endif
347 
348 	s = splnet();       /* possible */
349 	IF_INPUT_ENQUEUE(ifq, m);
350 	splx(s);
351 }
352 
353 /*
354  * Find the gre interface associated with our src/dst/proto set.
355  */
356 struct gre_softc *
357 gre_lookup(struct mbuf *m, u_int8_t proto)
358 {
359 	struct ip *ip = mtod(m, struct ip *);
360 	struct gre_softc *sc;
361 
362 	LIST_FOREACH(sc, &gre_softc_list, sc_list) {
363 		if ((sc->g_dst.s_addr == ip->ip_src.s_addr) &&
364 		    (sc->g_src.s_addr == ip->ip_dst.s_addr) &&
365 		    (sc->g_proto == proto) &&
366 		    (rtable_l2(sc->g_rtableid) ==
367 		    rtable_l2(m->m_pkthdr.rdomain)) &&
368 		    ((sc->sc_if.if_flags & IFF_UP) != 0))
369 			return (sc);
370 	}
371 
372 	return (NULL);
373 }
374 
375 int
376 gre_sysctl(int *name, u_int namelen, void *oldp, size_t *oldlenp, void *newp,
377     size_t newlen)
378 {
379         /* All sysctl names at this level are terminal. */
380         if (namelen != 1)
381                 return (ENOTDIR);
382 
383         switch (name[0]) {
384         case GRECTL_ALLOW:
385                 return (sysctl_int(oldp, oldlenp, newp, newlen, &gre_allow));
386         case GRECTL_WCCP:
387                 return (sysctl_int(oldp, oldlenp, newp, newlen, &gre_wccp));
388         default:
389                 return (ENOPROTOOPT);
390         }
391         /* NOTREACHED */
392 }
393 
394 int
395 ipmobile_sysctl(int *name, u_int namelen, void *oldp, size_t *oldlenp,
396     void *newp, size_t newlen)
397 {
398         /* All sysctl names at this level are terminal. */
399         if (namelen != 1)
400                 return (ENOTDIR);
401 
402         switch (name[0]) {
403         case MOBILEIPCTL_ALLOW:
404                 return (sysctl_int(oldp, oldlenp, newp, newlen,
405 				   &ip_mobile_allow));
406         default:
407                 return (ENOPROTOOPT);
408         }
409         /* NOTREACHED */
410 }
411 
412 int
413 gre_usrreq(struct socket *so, int req, struct mbuf *m, struct mbuf *nam,
414     struct mbuf *control, struct proc *p)
415 {
416 #ifdef  PIPEX
417 	struct inpcb *inp = sotoinpcb(so);
418 
419 	if (inp != NULL && inp->inp_pipex && req == PRU_SEND) {
420 		int s;
421 		struct sockaddr_in *sin4;
422 		struct in_addr *ina_dst;
423 		struct pipex_session *session;
424 
425 		s = splsoftnet();
426 		ina_dst = NULL;
427 		if ((so->so_state & SS_ISCONNECTED) != 0) {
428 			inp = sotoinpcb(so);
429 			if (inp)
430 				ina_dst = &inp->inp_laddr;
431 		} else if (nam) {
432 			sin4 = mtod(nam, struct sockaddr_in *);
433 			if (nam->m_len == sizeof(struct sockaddr_in) &&
434 			    sin4->sin_family == AF_INET)
435 				ina_dst = &sin4->sin_addr;
436 		}
437 		if (ina_dst != NULL &&
438 		    (session = pipex_pptp_userland_lookup_session_ipv4(m,
439 			    *ina_dst)))
440 			m = pipex_pptp_userland_output(m, session);
441 		splx(s);
442 
443 		if (m == NULL)
444 			return (ENOMEM);
445 	}
446 #endif
447 	return rip_usrreq(so, req, m, nam, control, p);
448 }
449 #endif /* if NGRE > 0 */
450