xref: /openbsd-src/sys/netinet/ip_gre.c (revision f2da64fbbbf1b03f09f390ab01267c93dfd77c4c)
1 /*      $OpenBSD: ip_gre.c,v 1.59 2016/03/04 22:38:23 sashan 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/sysctl.h>
50 #include <net/if.h>
51 #include <net/netisr.h>
52 #include <net/route.h>
53 #include <net/bpf.h>
54 
55 #include <netinet/in.h>
56 #include <netinet/ip.h>
57 #include <netinet/ip_var.h>
58 #include <netinet/ip_gre.h>
59 #include <netinet/if_ether.h>
60 #include <netinet/in_pcb.h>
61 
62 #ifdef MPLS
63 #include <netmpls/mpls.h>
64 #endif
65 
66 #include "bpfilter.h"
67 #include "pf.h"
68 
69 #if NPF > 0
70 #include <net/pfvar.h>
71 #endif
72 
73 #ifdef PIPEX
74 #include <net/pipex.h>
75 #endif
76 
77 /* Needs IP headers. */
78 #include <net/if_gre.h>
79 
80 struct gre_softc *gre_lookup(struct mbuf *, u_int8_t);
81 int gre_input2(struct mbuf *, int, u_char);
82 
83 /*
84  * Decapsulate.
85  * Does the real work and is called from gre_input() (above)
86  * returns 0 if packet is not yet processed
87  * and 1 if it needs no further processing
88  * proto is the protocol number of the "calling" foo_input()
89  * routine.
90  */
91 
92 int
93 gre_input2(struct mbuf *m, int hlen, u_char proto)
94 {
95 	struct greip *gip;
96 	struct niqueue *ifq;
97 	struct gre_softc *sc;
98 	u_short flags;
99 	u_int af;
100 
101 	if ((sc = gre_lookup(m, proto)) == NULL) {
102 		/* No matching tunnel or tunnel is down. */
103 		return (0);
104 	}
105 
106 	if (m->m_len < sizeof(*gip)) {
107 		m = m_pullup(m, sizeof(*gip));
108 		if (m == NULL)
109 			return (ENOBUFS);
110 	}
111 	gip = mtod(m, struct greip *);
112 
113 	m->m_pkthdr.ph_ifidx = sc->sc_if.if_index;
114 	m->m_pkthdr.ph_rtableid = sc->sc_if.if_rdomain;
115 
116 	sc->sc_if.if_ipackets++;
117 	sc->sc_if.if_ibytes += m->m_pkthdr.len;
118 
119 	switch (proto) {
120 	case IPPROTO_GRE:
121 		hlen += sizeof (struct gre_h);
122 
123 		/* process GRE flags as packet can be of variable len */
124 		flags = ntohs(gip->gi_flags);
125 
126 		/* Checksum & Offset are present */
127 		if ((flags & GRE_CP) | (flags & GRE_RP))
128 			hlen += 4;
129 
130 		/* We don't support routing fields (variable length) */
131 		if (flags & GRE_RP)
132 			return (0);
133 
134 		if (flags & GRE_KP)
135 			hlen += 4;
136 
137 		if (flags & GRE_SP)
138 			hlen += 4;
139 
140 		switch (ntohs(gip->gi_ptype)) { /* ethertypes */
141 		case GREPROTO_WCCP:
142 			/* WCCP/GRE:
143 			 *   So far as I can see (and test) it seems that Cisco's WCCP
144 			 *   GRE tunnel is precisely a IP-in-GRE tunnel that differs
145 			 *   only in its protocol number.  At least, it works for me.
146 			 *
147 			 *   The Internet Drafts can be found if you look for
148 			 *   the following:
149 			 *     draft-forster-wrec-wccp-v1-00.txt
150 			 *     draft-wilson-wrec-wccp-v2-01.txt
151 			 *
152 			 *   So yes, we're doing a fall-through (unless, of course,
153 			 *   net.inet.gre.wccp is 0).
154 			 */
155 			if (!gre_wccp)
156 				return (0);
157 			/*
158 			 * For WCCPv2, additionally skip the 4 byte
159 			 * redirect header.
160 			 */
161 			if (gre_wccp == 2)
162 				hlen += 4;
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 			af = AF_INET6;
171 			break;
172 #endif
173 		case 0:
174 			/* keepalive reply, retrigger hold timer */
175 			gre_recv_keepalive(sc);
176 			m_freem(m);
177 			return (1);
178 #ifdef MPLS
179 		case ETHERTYPE_MPLS:
180 		case ETHERTYPE_MPLS_MCAST:
181 			mpls_input(m);
182 			return (1);
183 #endif
184 		default:	   /* others not yet supported */
185 			return (0);
186 		}
187 		break;
188 	default:
189 		/* others not yet supported */
190 		return (0);
191 	}
192 
193 	if (hlen > m->m_pkthdr.len) {
194 		m_freem(m);
195 		return (EINVAL);
196 	}
197 	m_adj(m, hlen);
198 
199 #if NBPFILTER > 0
200         if (sc->sc_if.if_bpf)
201 		bpf_mtap_af(sc->sc_if.if_bpf, af, m, BPF_DIRECTION_IN);
202 #endif
203 
204 #if NPF > 0
205 	pf_pkt_addr_changed(m);
206 #endif
207 
208 	niq_enqueue(ifq, m);
209 
210 	return (1);	/* packet is done, no further processing needed */
211 }
212 
213 /*
214  * Decapsulate a packet and feed it back through ip_input (this
215  * routine is called whenever IP gets a packet with proto type
216  * IPPROTO_GRE and a local destination address).
217  */
218 void
219 gre_input(struct mbuf *m, ...)
220 {
221 	int hlen, ret;
222 	va_list ap;
223 
224 	va_start(ap, m);
225 	hlen = va_arg(ap, int);
226 	va_end(ap);
227 
228 	if (!gre_allow) {
229 	        m_freem(m);
230 		return;
231 	}
232 
233 #ifdef PIPEX
234 	if (pipex_enable) {
235 		struct pipex_session *session;
236 
237 		if ((session = pipex_pptp_lookup_session(m)) != NULL) {
238 			if (pipex_pptp_input(m, session) == NULL)
239 				return;
240 		}
241 	}
242 #endif
243 
244 	ret = gre_input2(m, hlen, IPPROTO_GRE);
245 	/*
246 	 * ret == 0: packet not processed, but input from here
247 	 * means no matching tunnel that is up is found.
248 	 * we inject it to raw ip socket to see if anyone picks it up.
249 	 * possible that we received a WCCPv1-style GRE packet
250 	 * but we're not set to accept them.
251 	 */
252 	if (!ret)
253 		rip_input(m, hlen, IPPROTO_GRE);
254 }
255 
256 /*
257  * Input routine for IPPROTO_MOBILE.
258  * This is a little bit different from the other modes, as the
259  * encapsulating header was not prepended, but instead inserted
260  * between IP header and payload.
261  */
262 
263 void
264 gre_mobile_input(struct mbuf *m, ...)
265 {
266 	struct ip *ip;
267 	struct mobip_h *mip;
268 	struct gre_softc *sc;
269 	int hlen;
270 	va_list ap;
271 	u_char osrc = 0;
272 	int msiz;
273 
274 	va_start(ap, m);
275 	hlen = va_arg(ap, int);
276 	va_end(ap);
277 
278 	if (!ip_mobile_allow) {
279 	        m_freem(m);
280 		return;
281 	}
282 
283 	if ((sc = gre_lookup(m, IPPROTO_MOBILE)) == NULL) {
284 		/* No matching tunnel or tunnel is down. */
285 		m_freem(m);
286 		return;
287 	}
288 
289 	if (m->m_len < sizeof(*mip)) {
290 		m = m_pullup(m, sizeof(*mip));
291 		if (m == NULL)
292 			return;
293 	}
294 	ip = mtod(m, struct ip *);
295 	mip = mtod(m, struct mobip_h *);
296 
297 	m->m_pkthdr.ph_ifidx = sc->sc_if.if_index;
298 
299 	sc->sc_if.if_ipackets++;
300 	sc->sc_if.if_ibytes += m->m_pkthdr.len;
301 
302 	if (ntohs(mip->mh.proto) & MOB_H_SBIT) {
303 		osrc = 1;
304 		msiz = MOB_H_SIZ_L;
305 		mip->mi.ip_src.s_addr = mip->mh.osrc;
306 	} else
307 		msiz = MOB_H_SIZ_S;
308 
309 	if (m->m_len < (ip->ip_hl << 2) + msiz) {
310 		m = m_pullup(m, (ip->ip_hl << 2) + msiz);
311 		if (m == NULL)
312 			return;
313 		ip = mtod(m, struct ip *);
314 		mip = mtod(m, struct mobip_h *);
315 	}
316 
317 	mip->mi.ip_dst.s_addr = mip->mh.odst;
318 	mip->mi.ip_p = (ntohs(mip->mh.proto) >> 8);
319 
320 	if (gre_in_cksum((u_short *) &mip->mh, msiz) != 0) {
321 		m_freem(m);
322 		return;
323 	}
324 
325 	memmove(ip + (ip->ip_hl << 2), ip + (ip->ip_hl << 2) + msiz,
326 	      m->m_len - msiz - (ip->ip_hl << 2));
327 
328 	m->m_len -= msiz;
329 	ip->ip_len = htons(ntohs(ip->ip_len) - msiz);
330 	m->m_pkthdr.len -= msiz;
331 
332 	ip->ip_sum = 0;
333 	ip->ip_sum = in_cksum(m,(ip->ip_hl << 2));
334 
335 #if NBPFILTER > 0
336         if (sc->sc_if.if_bpf)
337 		bpf_mtap_af(sc->sc_if.if_bpf, AF_INET, m, BPF_DIRECTION_IN);
338 #endif
339 
340 #if NPF > 0
341 	pf_pkt_addr_changed(m);
342 #endif
343 
344 	niq_enqueue(&ipintrq, m);
345 }
346 
347 /*
348  * Find the gre interface associated with our src/dst/proto set.
349  */
350 struct gre_softc *
351 gre_lookup(struct mbuf *m, u_int8_t proto)
352 {
353 	struct ip *ip = mtod(m, struct ip *);
354 	struct gre_softc *sc;
355 
356 	LIST_FOREACH(sc, &gre_softc_list, sc_list) {
357 		if ((sc->g_dst.s_addr == ip->ip_src.s_addr) &&
358 		    (sc->g_src.s_addr == ip->ip_dst.s_addr) &&
359 		    (sc->g_proto == proto) &&
360 		    (rtable_l2(sc->g_rtableid) ==
361 		    rtable_l2(m->m_pkthdr.ph_rtableid)) &&
362 		    ((sc->sc_if.if_flags & IFF_UP) != 0))
363 			return (sc);
364 	}
365 
366 	return (NULL);
367 }
368 
369 int
370 gre_sysctl(int *name, u_int namelen, void *oldp, size_t *oldlenp, void *newp,
371     size_t newlen)
372 {
373         /* All sysctl names at this level are terminal. */
374         if (namelen != 1)
375                 return (ENOTDIR);
376 
377         switch (name[0]) {
378         case GRECTL_ALLOW:
379                 return (sysctl_int(oldp, oldlenp, newp, newlen, &gre_allow));
380         case GRECTL_WCCP:
381                 return (sysctl_int(oldp, oldlenp, newp, newlen, &gre_wccp));
382         default:
383                 return (ENOPROTOOPT);
384         }
385         /* NOTREACHED */
386 }
387 
388 int
389 ipmobile_sysctl(int *name, u_int namelen, void *oldp, size_t *oldlenp,
390     void *newp, size_t newlen)
391 {
392         /* All sysctl names at this level are terminal. */
393         if (namelen != 1)
394                 return (ENOTDIR);
395 
396         switch (name[0]) {
397         case MOBILEIPCTL_ALLOW:
398                 return (sysctl_int(oldp, oldlenp, newp, newlen,
399 				   &ip_mobile_allow));
400         default:
401                 return (ENOPROTOOPT);
402         }
403         /* NOTREACHED */
404 }
405 
406 int
407 gre_usrreq(struct socket *so, int req, struct mbuf *m, struct mbuf *nam,
408     struct mbuf *control, struct proc *p)
409 {
410 #ifdef  PIPEX
411 	struct inpcb *inp = sotoinpcb(so);
412 
413 	if (inp != NULL && inp->inp_pipex && req == PRU_SEND) {
414 		int s;
415 		struct sockaddr_in *sin4;
416 		struct in_addr *ina_dst;
417 		struct pipex_session *session;
418 
419 		s = splsoftnet();
420 		ina_dst = NULL;
421 		if ((so->so_state & SS_ISCONNECTED) != 0) {
422 			inp = sotoinpcb(so);
423 			if (inp)
424 				ina_dst = &inp->inp_laddr;
425 		} else if (nam) {
426 			sin4 = mtod(nam, struct sockaddr_in *);
427 			if (nam->m_len == sizeof(struct sockaddr_in) &&
428 			    sin4->sin_family == AF_INET)
429 				ina_dst = &sin4->sin_addr;
430 		}
431 		if (ina_dst != NULL &&
432 		    (session = pipex_pptp_userland_lookup_session_ipv4(m,
433 			    *ina_dst)))
434 			m = pipex_pptp_userland_output(m, session);
435 		splx(s);
436 
437 		if (m == NULL)
438 			return (ENOMEM);
439 	}
440 #endif
441 	return rip_usrreq(so, req, m, nam, control, p);
442 }
443 #endif /* if NGRE > 0 */
444