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