1139823Simp/*-
2103026Ssobomax * Copyright (c) 1998 The NetBSD Foundation, Inc.
3274246Sae * Copyright (c) 2014 Andrey V. Elsukov <ae@FreeBSD.org>
4103026Ssobomax * All rights reserved.
5103026Ssobomax *
6103026Ssobomax * This code is derived from software contributed to The NetBSD Foundation
7103026Ssobomax * by Heiko W.Rupp <hwr@pilhuhn.de>
8103026Ssobomax *
9148613Sbz * IPv6-over-GRE contributed by Gert Doering <gert@greenie.muc.de>
10148613Sbz *
11103026Ssobomax * Redistribution and use in source and binary forms, with or without
12103026Ssobomax * modification, are permitted provided that the following conditions
13103026Ssobomax * are met:
14103026Ssobomax * 1. Redistributions of source code must retain the above copyright
15103026Ssobomax *    notice, this list of conditions and the following disclaimer.
16103026Ssobomax * 2. Redistributions in binary form must reproduce the above copyright
17103026Ssobomax *    notice, this list of conditions and the following disclaimer in the
18103026Ssobomax *    documentation and/or other materials provided with the distribution.
19103026Ssobomax *
20103026Ssobomax * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
21103026Ssobomax * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
22103026Ssobomax * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23103026Ssobomax * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
24103026Ssobomax * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25103026Ssobomax * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26103026Ssobomax * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27103026Ssobomax * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28103026Ssobomax * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29103026Ssobomax * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30103026Ssobomax * POSSIBILITY OF SUCH DAMAGE.
31274246Sae *
32274246Sae * $NetBSD: if_gre.c,v 1.49 2003/12/11 00:22:29 itojun Exp $
33103026Ssobomax */
34103026Ssobomax
35274246Sae#include <sys/cdefs.h>
36274246Sae__FBSDID("$FreeBSD: stable/11/sys/net/if_gre.c 332288 2018-04-08 16:54:07Z brooks $");
37103026Ssobomax
38103026Ssobomax#include "opt_inet.h"
39122699Sbms#include "opt_inet6.h"
40103026Ssobomax
41103026Ssobomax#include <sys/param.h>
42219206Sbz#include <sys/jail.h>
43103026Ssobomax#include <sys/kernel.h>
44274246Sae#include <sys/lock.h>
45223223Sbz#include <sys/libkern.h>
46103026Ssobomax#include <sys/malloc.h>
47129880Sphk#include <sys/module.h>
48103026Ssobomax#include <sys/mbuf.h>
49164033Srwatson#include <sys/priv.h>
50178888Sjulian#include <sys/proc.h>
51103026Ssobomax#include <sys/protosw.h>
52274246Sae#include <sys/rmlock.h>
53103026Ssobomax#include <sys/socket.h>
54103026Ssobomax#include <sys/sockio.h>
55274246Sae#include <sys/sx.h>
56103026Ssobomax#include <sys/sysctl.h>
57274246Sae#include <sys/syslog.h>
58103344Sbde#include <sys/systm.h>
59103026Ssobomax
60103026Ssobomax#include <net/ethernet.h>
61103026Ssobomax#include <net/if.h>
62257176Sglebius#include <net/if_var.h>
63130933Sbrooks#include <net/if_clone.h>
64103026Ssobomax#include <net/if_types.h>
65274246Sae#include <net/netisr.h>
66196019Srwatson#include <net/vnet.h>
67282809Sae#include <net/route.h>
68103026Ssobomax
69274246Sae#include <netinet/in.h>
70103026Ssobomax#ifdef INET
71103026Ssobomax#include <netinet/in_systm.h>
72103026Ssobomax#include <netinet/in_var.h>
73103026Ssobomax#include <netinet/ip.h>
74103026Ssobomax#include <netinet/ip_var.h>
75103026Ssobomax#endif
76103026Ssobomax
77274246Sae#ifdef INET6
78274246Sae#include <netinet/ip6.h>
79274246Sae#include <netinet6/in6_var.h>
80274246Sae#include <netinet6/ip6_var.h>
81274246Sae#include <netinet6/scope6_var.h>
82274246Sae#endif
83274246Sae
84274246Sae#include <netinet/ip_encap.h>
85103026Ssobomax#include <net/bpf.h>
86103026Ssobomax#include <net/if_gre.h>
87103026Ssobomax
88274246Sae#include <machine/in_cksum.h>
89274246Sae#include <security/mac/mac_framework.h>
90103026Ssobomax
91317403Sae#define	GREMTU			1476
92241610Sglebiusstatic const char grename[] = "gre";
93241610Sglebiusstatic MALLOC_DEFINE(M_GRE, grename, "Generic Routing Encapsulation");
94274246Saestatic VNET_DEFINE(struct mtx, gre_mtx);
95274246Sae#define	V_gre_mtx	VNET(gre_mtx)
96274246Sae#define	GRE_LIST_LOCK_INIT(x)		mtx_init(&V_gre_mtx, "gre_mtx", NULL, \
97274246Sae					    MTX_DEF)
98274246Sae#define	GRE_LIST_LOCK_DESTROY(x)	mtx_destroy(&V_gre_mtx)
99274246Sae#define	GRE_LIST_LOCK(x)		mtx_lock(&V_gre_mtx)
100274246Sae#define	GRE_LIST_UNLOCK(x)		mtx_unlock(&V_gre_mtx)
101103026Ssobomax
102274246Saestatic VNET_DEFINE(LIST_HEAD(, gre_softc), gre_softc_list);
103274246Sae#define	V_gre_softc_list	VNET(gre_softc_list)
104274246Saestatic struct sx gre_ioctl_sx;
105274246SaeSX_SYSINIT(gre_ioctl_sx, &gre_ioctl_sx, "gre_ioctl");
106274246Sae
107160195Ssamstatic int	gre_clone_create(struct if_clone *, int, caddr_t);
108105300Salfredstatic void	gre_clone_destroy(struct ifnet *);
109271918Shrsstatic VNET_DEFINE(struct if_clone *, gre_cloner);
110271918Shrs#define	V_gre_cloner	VNET(gre_cloner)
111241610Sglebius
112274246Saestatic void	gre_qflush(struct ifnet *);
113274246Saestatic int	gre_transmit(struct ifnet *, struct mbuf *);
114103032Ssobomaxstatic int	gre_ioctl(struct ifnet *, u_long, caddr_t);
115249925Sglebiusstatic int	gre_output(struct ifnet *, struct mbuf *,
116249925Sglebius		    const struct sockaddr *, struct route *);
117103026Ssobomax
118274246Saestatic void	gre_updatehdr(struct gre_softc *);
119274246Saestatic int	gre_set_tunnel(struct ifnet *, struct sockaddr *,
120274246Sae    struct sockaddr *);
121274246Saestatic void	gre_delete_tunnel(struct ifnet *);
122103026Ssobomax
123103026SsobomaxSYSCTL_DECL(_net_link);
124227309Sedstatic SYSCTL_NODE(_net_link, IFT_TUNNEL, gre, CTLFLAG_RW, 0,
125103026Ssobomax    "Generic Routing Encapsulation");
126103026Ssobomax#ifndef MAX_GRE_NEST
127103026Ssobomax/*
128103026Ssobomax * This macro controls the default upper limitation on nesting of gre tunnels.
129103026Ssobomax * Since, setting a large value to this macro with a careless configuration
130103026Ssobomax * may introduce system crash, we don't allow any nestings by default.
131103026Ssobomax * If you need to configure nested gre tunnels, you can define this macro
132103026Ssobomax * in your kernel configuration file.  However, if you do so, please be
133103026Ssobomax * careful to configure the tunnels so that it won't make a loop.
134103026Ssobomax */
135103026Ssobomax#define MAX_GRE_NEST 1
136103026Ssobomax#endif
137274246Sae
138271918Shrsstatic VNET_DEFINE(int, max_gre_nesting) = MAX_GRE_NEST;
139271918Shrs#define	V_max_gre_nesting	VNET(max_gre_nesting)
140271918ShrsSYSCTL_INT(_net_link_gre, OID_AUTO, max_nesting, CTLFLAG_RW | CTLFLAG_VNET,
141271918Shrs    &VNET_NAME(max_gre_nesting), 0, "Max nested tunnels");
142103026Ssobomax
143103032Ssobomaxstatic void
144271918Shrsvnet_gre_init(const void *unused __unused)
145103026Ssobomax{
146271918Shrs	LIST_INIT(&V_gre_softc_list);
147271918Shrs	GRE_LIST_LOCK_INIT();
148271918Shrs	V_gre_cloner = if_clone_simple(grename, gre_clone_create,
149241610Sglebius	    gre_clone_destroy, 0);
150103026Ssobomax}
151271918ShrsVNET_SYSINIT(vnet_gre_init, SI_SUB_PROTO_IFATTACHDOMAIN, SI_ORDER_ANY,
152271918Shrs    vnet_gre_init, NULL);
153103026Ssobomax
154271918Shrsstatic void
155271918Shrsvnet_gre_uninit(const void *unused __unused)
156271918Shrs{
157271918Shrs
158271918Shrs	if_clone_detach(V_gre_cloner);
159271918Shrs	GRE_LIST_LOCK_DESTROY();
160271918Shrs}
161271918ShrsVNET_SYSUNINIT(vnet_gre_uninit, SI_SUB_PROTO_IFATTACHDOMAIN, SI_ORDER_ANY,
162271918Shrs    vnet_gre_uninit, NULL);
163271918Shrs
164103032Ssobomaxstatic int
165271918Shrsgre_clone_create(struct if_clone *ifc, int unit, caddr_t params)
166103026Ssobomax{
167103026Ssobomax	struct gre_softc *sc;
168103026Ssobomax
169131673Sbms	sc = malloc(sizeof(struct gre_softc), M_GRE, M_WAITOK | M_ZERO);
170274246Sae	sc->gre_fibnum = curthread->td_proc->p_fibnum;
171147643Sbz	GRE2IFP(sc) = if_alloc(IFT_TUNNEL);
172274246Sae	GRE_LOCK_INIT(sc);
173147643Sbz	GRE2IFP(sc)->if_softc = sc;
174241610Sglebius	if_initname(GRE2IFP(sc), grename, unit);
175147643Sbz
176317403Sae	GRE2IFP(sc)->if_mtu = GREMTU;
177147256Sbrooks	GRE2IFP(sc)->if_flags = IFF_POINTOPOINT|IFF_MULTICAST;
178147256Sbrooks	GRE2IFP(sc)->if_output = gre_output;
179147256Sbrooks	GRE2IFP(sc)->if_ioctl = gre_ioctl;
180274246Sae	GRE2IFP(sc)->if_transmit = gre_transmit;
181274246Sae	GRE2IFP(sc)->if_qflush = gre_qflush;
182288575Shrs	GRE2IFP(sc)->if_capabilities |= IFCAP_LINKSTATE;
183288575Shrs	GRE2IFP(sc)->if_capenable |= IFCAP_LINKSTATE;
184147256Sbrooks	if_attach(GRE2IFP(sc));
185147256Sbrooks	bpfattach(GRE2IFP(sc), DLT_NULL, sizeof(u_int32_t));
186271918Shrs	GRE_LIST_LOCK();
187274246Sae	LIST_INSERT_HEAD(&V_gre_softc_list, sc, gre_list);
188271918Shrs	GRE_LIST_UNLOCK();
189103026Ssobomax	return (0);
190103026Ssobomax}
191103026Ssobomax
192103032Ssobomaxstatic void
193271918Shrsgre_clone_destroy(struct ifnet *ifp)
194127307Srwatson{
195274246Sae	struct gre_softc *sc;
196127307Srwatson
197274246Sae	sx_xlock(&gre_ioctl_sx);
198274246Sae	sc = ifp->if_softc;
199274246Sae	gre_delete_tunnel(ifp);
200271918Shrs	GRE_LIST_LOCK();
201274246Sae	LIST_REMOVE(sc, gre_list);
202271918Shrs	GRE_LIST_UNLOCK();
203151266Sthompsa	bpfdetach(ifp);
204151266Sthompsa	if_detach(ifp);
205274246Sae	ifp->if_softc = NULL;
206274246Sae	sx_xunlock(&gre_ioctl_sx);
207274246Sae
208151266Sthompsa	if_free(ifp);
209274246Sae	GRE_LOCK_DESTROY(sc);
210151266Sthompsa	free(sc, M_GRE);
211127307Srwatson}
212127307Srwatson
213103032Ssobomaxstatic int
214274246Saegre_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
215103026Ssobomax{
216274246Sae	GRE_RLOCK_TRACKER;
217274246Sae	struct ifreq *ifr = (struct ifreq *)data;
218274246Sae	struct sockaddr *src, *dst;
219274246Sae	struct gre_softc *sc;
220274246Sae#ifdef INET
221274246Sae	struct sockaddr_in *sin = NULL;
222274246Sae#endif
223274246Sae#ifdef INET6
224274246Sae	struct sockaddr_in6 *sin6 = NULL;
225274246Sae#endif
226274246Sae	uint32_t opt;
227274246Sae	int error;
228103026Ssobomax
229274246Sae	switch (cmd) {
230274246Sae	case SIOCSIFMTU:
231274246Sae		 /* XXX: */
232274246Sae		if (ifr->ifr_mtu < 576)
233274246Sae			return (EINVAL);
234317403Sae		ifp->if_mtu = ifr->ifr_mtu;
235317403Sae		return (0);
236274246Sae	case SIOCSIFADDR:
237274246Sae		ifp->if_flags |= IFF_UP;
238274246Sae	case SIOCSIFFLAGS:
239274246Sae	case SIOCADDMULTI:
240274246Sae	case SIOCDELMULTI:
241274246Sae		return (0);
242274246Sae	case GRESADDRS:
243274246Sae	case GRESADDRD:
244274246Sae	case GREGADDRS:
245274246Sae	case GREGADDRD:
246274246Sae	case GRESPROTO:
247274246Sae	case GREGPROTO:
248274246Sae		return (EOPNOTSUPP);
249103026Ssobomax	}
250274246Sae	src = dst = NULL;
251274246Sae	sx_xlock(&gre_ioctl_sx);
252274246Sae	sc = ifp->if_softc;
253274246Sae	if (sc == NULL) {
254274246Sae		error = ENXIO;
255103026Ssobomax		goto end;
256103026Ssobomax	}
257274246Sae	error = 0;
258274246Sae	switch (cmd) {
259274246Sae	case SIOCSIFPHYADDR:
260274246Sae#ifdef INET6
261274246Sae	case SIOCSIFPHYADDR_IN6:
262274246Sae#endif
263274246Sae		error = EINVAL;
264274246Sae		switch (cmd) {
265274246Sae#ifdef INET
266274246Sae		case SIOCSIFPHYADDR:
267274246Sae			src = (struct sockaddr *)
268274246Sae				&(((struct in_aliasreq *)data)->ifra_addr);
269274246Sae			dst = (struct sockaddr *)
270274246Sae				&(((struct in_aliasreq *)data)->ifra_dstaddr);
271103026Ssobomax			break;
272274246Sae#endif
273148613Sbz#ifdef INET6
274274246Sae		case SIOCSIFPHYADDR_IN6:
275274246Sae			src = (struct sockaddr *)
276274246Sae				&(((struct in6_aliasreq *)data)->ifra_addr);
277274246Sae			dst = (struct sockaddr *)
278274246Sae				&(((struct in6_aliasreq *)data)->ifra_dstaddr);
279148613Sbz			break;
280148613Sbz#endif
281103026Ssobomax		default:
282103026Ssobomax			error = EAFNOSUPPORT;
283103026Ssobomax			goto end;
284103026Ssobomax		}
285274246Sae		/* sa_family must be equal */
286274246Sae		if (src->sa_family != dst->sa_family ||
287274246Sae		    src->sa_len != dst->sa_len)
288274246Sae			goto end;
289103026Ssobomax
290274246Sae		/* validate sa_len */
291274246Sae		switch (src->sa_family) {
292164033Srwatson#ifdef INET
293164033Srwatson		case AF_INET:
294274246Sae			if (src->sa_len != sizeof(struct sockaddr_in))
295274246Sae				goto end;
296164033Srwatson			break;
297164033Srwatson#endif
298164033Srwatson#ifdef INET6
299164033Srwatson		case AF_INET6:
300274246Sae			if (src->sa_len != sizeof(struct sockaddr_in6))
301274246Sae				goto end;
302164033Srwatson			break;
303164033Srwatson#endif
304164033Srwatson		default:
305164033Srwatson			error = EAFNOSUPPORT;
306274246Sae			goto end;
307164033Srwatson		}
308274246Sae		/* check sa_family looks sane for the cmd */
309274246Sae		error = EAFNOSUPPORT;
310274246Sae		switch (cmd) {
311274246Sae#ifdef INET
312274246Sae		case SIOCSIFPHYADDR:
313274246Sae			if (src->sa_family == AF_INET)
314274246Sae				break;
315274246Sae			goto end;
316274246Sae#endif
317274246Sae#ifdef INET6
318274246Sae		case SIOCSIFPHYADDR_IN6:
319274246Sae			if (src->sa_family == AF_INET6)
320274246Sae				break;
321274246Sae			goto end;
322274246Sae#endif
323103026Ssobomax		}
324274246Sae		error = EADDRNOTAVAIL;
325274246Sae		switch (src->sa_family) {
326103026Ssobomax#ifdef INET
327103026Ssobomax		case AF_INET:
328274246Sae			if (satosin(src)->sin_addr.s_addr == INADDR_ANY ||
329274246Sae			    satosin(dst)->sin_addr.s_addr == INADDR_ANY)
330274246Sae				goto end;
331103026Ssobomax			break;
332103026Ssobomax#endif
333148613Sbz#ifdef INET6
334148613Sbz		case AF_INET6:
335274246Sae			if (IN6_IS_ADDR_UNSPECIFIED(&satosin6(src)->sin6_addr)
336274246Sae			    ||
337274246Sae			    IN6_IS_ADDR_UNSPECIFIED(&satosin6(dst)->sin6_addr))
338274246Sae				goto end;
339274246Sae			/*
340274246Sae			 * Check validity of the scope zone ID of the
341274246Sae			 * addresses, and convert it into the kernel
342274246Sae			 * internal form if necessary.
343274246Sae			 */
344274246Sae			error = sa6_embedscope(satosin6(src), 0);
345274246Sae			if (error != 0)
346274246Sae				goto end;
347274246Sae			error = sa6_embedscope(satosin6(dst), 0);
348274246Sae			if (error != 0)
349274246Sae				goto end;
350148613Sbz#endif
351297793Spfg		}
352274246Sae		error = gre_set_tunnel(ifp, src, dst);
353274246Sae		break;
354274246Sae	case SIOCDIFPHYADDR:
355274246Sae		gre_delete_tunnel(ifp);
356274246Sae		break;
357274246Sae	case SIOCGIFPSRCADDR:
358274246Sae	case SIOCGIFPDSTADDR:
359274246Sae#ifdef INET6
360274246Sae	case SIOCGIFPSRCADDR_IN6:
361274246Sae	case SIOCGIFPDSTADDR_IN6:
362274246Sae#endif
363274246Sae		if (sc->gre_family == 0) {
364274246Sae			error = EADDRNOTAVAIL;
365103026Ssobomax			break;
366103026Ssobomax		}
367274246Sae		GRE_RLOCK(sc);
368274246Sae		switch (cmd) {
369274246Sae#ifdef INET
370274246Sae		case SIOCGIFPSRCADDR:
371274246Sae		case SIOCGIFPDSTADDR:
372274246Sae			if (sc->gre_family != AF_INET) {
373274246Sae				error = EADDRNOTAVAIL;
374274246Sae				break;
375274246Sae			}
376274246Sae			sin = (struct sockaddr_in *)&ifr->ifr_addr;
377274246Sae			memset(sin, 0, sizeof(*sin));
378274246Sae			sin->sin_family = AF_INET;
379274246Sae			sin->sin_len = sizeof(*sin);
380103026Ssobomax			break;
381274246Sae#endif
382274246Sae#ifdef INET6
383274246Sae		case SIOCGIFPSRCADDR_IN6:
384274246Sae		case SIOCGIFPDSTADDR_IN6:
385274246Sae			if (sc->gre_family != AF_INET6) {
386274246Sae				error = EADDRNOTAVAIL;
387274246Sae				break;
388274246Sae			}
389274246Sae			sin6 = (struct sockaddr_in6 *)
390274246Sae				&(((struct in6_ifreq *)data)->ifr_addr);
391274246Sae			memset(sin6, 0, sizeof(*sin6));
392274246Sae			sin6->sin6_family = AF_INET6;
393274246Sae			sin6->sin6_len = sizeof(*sin6);
394103026Ssobomax			break;
395274246Sae#endif
396103026Ssobomax		}
397274246Sae		if (error == 0) {
398274246Sae			switch (cmd) {
399103026Ssobomax#ifdef INET
400274246Sae			case SIOCGIFPSRCADDR:
401274246Sae				sin->sin_addr = sc->gre_oip.ip_src;
402274246Sae				break;
403274246Sae			case SIOCGIFPDSTADDR:
404274246Sae				sin->sin_addr = sc->gre_oip.ip_dst;
405274246Sae				break;
406103026Ssobomax#endif
407274246Sae#ifdef INET6
408274246Sae			case SIOCGIFPSRCADDR_IN6:
409274246Sae				sin6->sin6_addr = sc->gre_oip6.ip6_src;
410274246Sae				break;
411274246Sae			case SIOCGIFPDSTADDR_IN6:
412274246Sae				sin6->sin6_addr = sc->gre_oip6.ip6_dst;
413274246Sae				break;
414103026Ssobomax#endif
415274246Sae			}
416103026Ssobomax		}
417274246Sae		GRE_RUNLOCK(sc);
418219206Sbz		if (error != 0)
419219206Sbz			break;
420274246Sae		switch (cmd) {
421274246Sae#ifdef INET
422274246Sae		case SIOCGIFPSRCADDR:
423274246Sae		case SIOCGIFPDSTADDR:
424274246Sae			error = prison_if(curthread->td_ucred,
425274246Sae			    (struct sockaddr *)sin);
426274246Sae			if (error != 0)
427274246Sae				memset(sin, 0, sizeof(*sin));
428219206Sbz			break;
429274246Sae#endif
430122699Sbms#ifdef INET6
431274246Sae		case SIOCGIFPSRCADDR_IN6:
432274246Sae		case SIOCGIFPDSTADDR_IN6:
433274246Sae			error = prison_if(curthread->td_ucred,
434274246Sae			    (struct sockaddr *)sin6);
435274246Sae			if (error == 0)
436274246Sae				error = sa6_recoverscope(sin6);
437274246Sae			if (error != 0)
438274246Sae				memset(sin6, 0, sizeof(*sin6));
439122699Sbms#endif
440103026Ssobomax		}
441103026Ssobomax		break;
442282809Sae	case SIOCGTUNFIB:
443282809Sae		ifr->ifr_fib = sc->gre_fibnum;
444282809Sae		break;
445282809Sae	case SIOCSTUNFIB:
446282809Sae		if ((error = priv_check(curthread, PRIV_NET_GRE)) != 0)
447282809Sae			break;
448282809Sae		if (ifr->ifr_fib >= rt_numfibs)
449282809Sae			error = EINVAL;
450282809Sae		else
451282809Sae			sc->gre_fibnum = ifr->ifr_fib;
452282809Sae		break;
453274246Sae	case GRESKEY:
454274246Sae		if ((error = priv_check(curthread, PRIV_NET_GRE)) != 0)
455103026Ssobomax			break;
456332288Sbrooks		if ((error = copyin(ifr_data_get_ptr(ifr), &opt,
457332288Sbrooks		    sizeof(opt))) != 0)
458274246Sae			break;
459274246Sae		if (sc->gre_key != opt) {
460274246Sae			GRE_WLOCK(sc);
461274246Sae			sc->gre_key = opt;
462274246Sae			gre_updatehdr(sc);
463274246Sae			GRE_WUNLOCK(sc);
464103026Ssobomax		}
465103026Ssobomax		break;
466274246Sae	case GREGKEY:
467332288Sbrooks		error = copyout(&sc->gre_key, ifr_data_get_ptr(ifr),
468282809Sae		    sizeof(sc->gre_key));
469274246Sae		break;
470274246Sae	case GRESOPTS:
471274246Sae		if ((error = priv_check(curthread, PRIV_NET_GRE)) != 0)
472179894Sthompsa			break;
473332288Sbrooks		if ((error = copyin(ifr_data_get_ptr(ifr), &opt,
474332288Sbrooks		    sizeof(opt))) != 0)
475179894Sthompsa			break;
476274246Sae		if (opt & ~GRE_OPTMASK)
477179894Sthompsa			error = EINVAL;
478274246Sae		else {
479274246Sae			if (sc->gre_options != opt) {
480274246Sae				GRE_WLOCK(sc);
481274246Sae				sc->gre_options = opt;
482274246Sae				gre_updatehdr(sc);
483274246Sae				GRE_WUNLOCK(sc);
484274246Sae			}
485179894Sthompsa		}
486179894Sthompsa		break;
487274246Sae
488274246Sae	case GREGOPTS:
489332288Sbrooks		error = copyout(&sc->gre_options, ifr_data_get_ptr(ifr),
490274246Sae		    sizeof(sc->gre_options));
491179894Sthompsa		break;
492103026Ssobomax	default:
493103026Ssobomax		error = EINVAL;
494103026Ssobomax		break;
495103026Ssobomax	}
496274246Saeend:
497274246Sae	sx_xunlock(&gre_ioctl_sx);
498103026Ssobomax	return (error);
499103026Ssobomax}
500103026Ssobomax
501274246Saestatic void
502274246Saegre_updatehdr(struct gre_softc *sc)
503103026Ssobomax{
504274246Sae	struct grehdr *gh = NULL;
505274246Sae	uint32_t *opts;
506274246Sae	uint16_t flags;
507103026Ssobomax
508274246Sae	GRE_WLOCK_ASSERT(sc);
509274246Sae	switch (sc->gre_family) {
510274246Sae#ifdef INET
511274246Sae	case AF_INET:
512274246Sae		sc->gre_hlen = sizeof(struct greip);
513274246Sae		sc->gre_oip.ip_v = IPPROTO_IPV4;
514274246Sae		sc->gre_oip.ip_hl = sizeof(struct ip) >> 2;
515274246Sae		sc->gre_oip.ip_p = IPPROTO_GRE;
516274246Sae		gh = &sc->gre_gihdr->gi_gre;
517274246Sae		break;
518274246Sae#endif
519274246Sae#ifdef INET6
520274246Sae	case AF_INET6:
521274246Sae		sc->gre_hlen = sizeof(struct greip6);
522274246Sae		sc->gre_oip6.ip6_vfc = IPV6_VERSION;
523274246Sae		sc->gre_oip6.ip6_nxt = IPPROTO_GRE;
524274246Sae		gh = &sc->gre_gi6hdr->gi6_gre;
525274246Sae		break;
526274246Sae#endif
527274246Sae	default:
528274246Sae		return;
529274246Sae	}
530274246Sae	flags = 0;
531274246Sae	opts = gh->gre_opts;
532274246Sae	if (sc->gre_options & GRE_ENABLE_CSUM) {
533274246Sae		flags |= GRE_FLAGS_CP;
534274246Sae		sc->gre_hlen += 2 * sizeof(uint16_t);
535274246Sae		*opts++ = 0;
536274246Sae	}
537274246Sae	if (sc->gre_key != 0) {
538274246Sae		flags |= GRE_FLAGS_KP;
539274246Sae		sc->gre_hlen += sizeof(uint32_t);
540274246Sae		*opts++ = htonl(sc->gre_key);
541274246Sae	}
542274246Sae	if (sc->gre_options & GRE_ENABLE_SEQ) {
543274246Sae		flags |= GRE_FLAGS_SP;
544274246Sae		sc->gre_hlen += sizeof(uint32_t);
545274246Sae		*opts++ = 0;
546274246Sae	} else
547274246Sae		sc->gre_oseq = 0;
548274246Sae	gh->gre_flags = htons(flags);
549274246Sae}
550103026Ssobomax
551274246Saestatic void
552274246Saegre_detach(struct gre_softc *sc)
553274246Sae{
554103026Ssobomax
555274246Sae	sx_assert(&gre_ioctl_sx, SA_XLOCKED);
556274246Sae	if (sc->gre_ecookie != NULL)
557274246Sae		encap_detach(sc->gre_ecookie);
558274246Sae	sc->gre_ecookie = NULL;
559274246Sae}
560274246Sae
561274246Saestatic int
562274246Saegre_set_tunnel(struct ifnet *ifp, struct sockaddr *src,
563274246Sae    struct sockaddr *dst)
564274246Sae{
565274246Sae	struct gre_softc *sc, *tsc;
566274246Sae#ifdef INET6
567274246Sae	struct ip6_hdr *ip6;
568274246Sae#endif
569274246Sae#ifdef INET
570274246Sae	struct ip *ip;
571274246Sae#endif
572274246Sae	void *hdr;
573274246Sae	int error;
574274246Sae
575274246Sae	sx_assert(&gre_ioctl_sx, SA_XLOCKED);
576274246Sae	GRE_LIST_LOCK();
577274246Sae	sc = ifp->if_softc;
578274246Sae	LIST_FOREACH(tsc, &V_gre_softc_list, gre_list) {
579274246Sae		if (tsc == sc || tsc->gre_family != src->sa_family)
580274246Sae			continue;
581274246Sae#ifdef INET
582274246Sae		if (tsc->gre_family == AF_INET &&
583274246Sae		    tsc->gre_oip.ip_src.s_addr ==
584274246Sae		    satosin(src)->sin_addr.s_addr &&
585274246Sae		    tsc->gre_oip.ip_dst.s_addr ==
586274246Sae		    satosin(dst)->sin_addr.s_addr) {
587274246Sae			GRE_LIST_UNLOCK();
588274246Sae			return (EADDRNOTAVAIL);
589274246Sae		}
590274246Sae#endif
591274246Sae#ifdef INET6
592274246Sae		if (tsc->gre_family == AF_INET6 &&
593274246Sae		    IN6_ARE_ADDR_EQUAL(&tsc->gre_oip6.ip6_src,
594274246Sae		    &satosin6(src)->sin6_addr) &&
595274246Sae		    IN6_ARE_ADDR_EQUAL(&tsc->gre_oip6.ip6_dst,
596274246Sae			&satosin6(dst)->sin6_addr)) {
597274246Sae			GRE_LIST_UNLOCK();
598274246Sae			return (EADDRNOTAVAIL);
599274246Sae		}
600274246Sae#endif
601103026Ssobomax	}
602274246Sae	GRE_LIST_UNLOCK();
603103026Ssobomax
604274246Sae	switch (src->sa_family) {
605274246Sae#ifdef INET
606274246Sae	case AF_INET:
607274246Sae		hdr = ip = malloc(sizeof(struct greip) +
608274246Sae		    3 * sizeof(uint32_t), M_GRE, M_WAITOK | M_ZERO);
609274246Sae		ip->ip_src = satosin(src)->sin_addr;
610274246Sae		ip->ip_dst = satosin(dst)->sin_addr;
611274246Sae		break;
612103026Ssobomax#endif
613274246Sae#ifdef INET6
614274246Sae	case AF_INET6:
615274246Sae		hdr = ip6 = malloc(sizeof(struct greip6) +
616274246Sae		    3 * sizeof(uint32_t), M_GRE, M_WAITOK | M_ZERO);
617274246Sae		ip6->ip6_src = satosin6(src)->sin6_addr;
618274246Sae		ip6->ip6_dst = satosin6(dst)->sin6_addr;
619274246Sae		break;
620274246Sae#endif
621274246Sae	default:
622274246Sae		return (EAFNOSUPPORT);
623274246Sae	}
624288529Sae	if (sc->gre_family != 0)
625274246Sae		gre_detach(sc);
626274246Sae	GRE_WLOCK(sc);
627274246Sae	if (sc->gre_family != 0)
628274246Sae		free(sc->gre_hdr, M_GRE);
629274246Sae	sc->gre_family = src->sa_family;
630274246Sae	sc->gre_hdr = hdr;
631274246Sae	sc->gre_oseq = 0;
632274246Sae	sc->gre_iseq = UINT32_MAX;
633274246Sae	gre_updatehdr(sc);
634274246Sae	GRE_WUNLOCK(sc);
635103026Ssobomax
636274289Sbz	error = 0;
637274246Sae	switch (src->sa_family) {
638274246Sae#ifdef INET
639274246Sae	case AF_INET:
640274246Sae		error = in_gre_attach(sc);
641274246Sae		break;
642274246Sae#endif
643274246Sae#ifdef INET6
644274246Sae	case AF_INET6:
645274246Sae		error = in6_gre_attach(sc);
646274246Sae		break;
647274246Sae#endif
648274246Sae	}
649288575Shrs	if (error == 0) {
650274246Sae		ifp->if_drv_flags |= IFF_DRV_RUNNING;
651288575Shrs		if_link_state_change(ifp, LINK_STATE_UP);
652288575Shrs	}
653274246Sae	return (error);
654274246Sae}
655103026Ssobomax
656274246Saestatic void
657274246Saegre_delete_tunnel(struct ifnet *ifp)
658274246Sae{
659274246Sae	struct gre_softc *sc = ifp->if_softc;
660274246Sae	int family;
661274246Sae
662274246Sae	GRE_WLOCK(sc);
663274246Sae	family = sc->gre_family;
664274246Sae	sc->gre_family = 0;
665274246Sae	GRE_WUNLOCK(sc);
666274246Sae	if (family != 0) {
667274246Sae		gre_detach(sc);
668274246Sae		free(sc->gre_hdr, M_GRE);
669274246Sae	}
670274246Sae	ifp->if_drv_flags &= ~IFF_DRV_RUNNING;
671288575Shrs	if_link_state_change(ifp, LINK_STATE_DOWN);
672274246Sae}
673274246Sae
674274246Saeint
675274246Saegre_input(struct mbuf **mp, int *offp, int proto)
676274246Sae{
677274246Sae	struct gre_softc *sc;
678274246Sae	struct grehdr *gh;
679274246Sae	struct ifnet *ifp;
680274246Sae	struct mbuf *m;
681292972Saraujo	uint32_t *opts;
682292972Saraujo#ifdef notyet
683292972Saraujo	uint32_t key;
684292972Saraujo#endif
685274246Sae	uint16_t flags;
686274246Sae	int hlen, isr, af;
687274246Sae
688274246Sae	m = *mp;
689274246Sae	sc = encap_getarg(m);
690274246Sae	KASSERT(sc != NULL, ("encap_getarg returned NULL"));
691274246Sae
692274246Sae	ifp = GRE2IFP(sc);
693290116Sae	hlen = *offp + sizeof(struct grehdr) + 4 * sizeof(uint32_t);
694290116Sae	if (m->m_pkthdr.len < hlen)
695290116Sae		goto drop;
696290116Sae	if (m->m_len < hlen) {
697290116Sae		m = m_pullup(m, hlen);
698290116Sae		if (m == NULL)
699290116Sae			goto drop;
700290116Sae	}
701274246Sae	gh = (struct grehdr *)mtodo(m, *offp);
702274246Sae	flags = ntohs(gh->gre_flags);
703274246Sae	if (flags & ~GRE_FLAGS_MASK)
704274246Sae		goto drop;
705274246Sae	opts = gh->gre_opts;
706274246Sae	hlen = 2 * sizeof(uint16_t);
707274246Sae	if (flags & GRE_FLAGS_CP) {
708274246Sae		/* reserved1 field must be zero */
709274246Sae		if (((uint16_t *)opts)[1] != 0)
710274246Sae			goto drop;
711274246Sae		if (in_cksum_skip(m, m->m_pkthdr.len, *offp) != 0)
712274246Sae			goto drop;
713274246Sae		hlen += 2 * sizeof(uint16_t);
714274246Sae		opts++;
715274246Sae	}
716274246Sae	if (flags & GRE_FLAGS_KP) {
717292972Saraujo#ifdef notyet
718292972Saraujo        /*
719292972Saraujo         * XXX: The current implementation uses the key only for outgoing
720292972Saraujo         * packets. But we can check the key value here, or even in the
721292972Saraujo         * encapcheck function.
722292972Saraujo         */
723274246Sae		key = ntohl(*opts);
724292972Saraujo#endif
725274246Sae		hlen += sizeof(uint32_t);
726274246Sae		opts++;
727292972Saraujo    }
728292972Saraujo#ifdef notyet
729274246Sae	} else
730274246Sae		key = 0;
731292972Saraujo
732274246Sae	if (sc->gre_key != 0 && (key != sc->gre_key || key != 0))
733274246Sae		goto drop;
734292972Saraujo#endif
735274246Sae	if (flags & GRE_FLAGS_SP) {
736292972Saraujo#ifdef notyet
737292972Saraujo		seq = ntohl(*opts);
738292972Saraujo#endif
739274246Sae		hlen += sizeof(uint32_t);
740274246Sae	}
741274246Sae	switch (ntohs(gh->gre_proto)) {
742274246Sae	case ETHERTYPE_WCCP:
743274246Sae		/*
744274246Sae		 * For WCCP skip an additional 4 bytes if after GRE header
745274246Sae		 * doesn't follow an IP header.
746274246Sae		 */
747274246Sae		if (flags == 0 && (*(uint8_t *)gh->gre_opts & 0xF0) != 0x40)
748274246Sae			hlen += sizeof(uint32_t);
749274246Sae		/* FALLTHROUGH */
750274246Sae	case ETHERTYPE_IP:
751274246Sae		isr = NETISR_IP;
752274246Sae		af = AF_INET;
753274246Sae		break;
754274246Sae	case ETHERTYPE_IPV6:
755274246Sae		isr = NETISR_IPV6;
756274246Sae		af = AF_INET6;
757274246Sae		break;
758274246Sae	default:
759274246Sae		goto drop;
760274246Sae	}
761274246Sae	m_adj(m, *offp + hlen);
762274246Sae	m_clrprotoflags(m);
763274246Sae	m->m_pkthdr.rcvif = ifp;
764282809Sae	M_SETFIB(m, ifp->if_fib);
765274246Sae#ifdef MAC
766274246Sae	mac_ifnet_create_mbuf(ifp, m);
767103026Ssobomax#endif
768274246Sae	BPF_MTAP2(ifp, &af, sizeof(af), m);
769274246Sae	if_inc_counter(ifp, IFCOUNTER_IPACKETS, 1);
770274246Sae	if_inc_counter(ifp, IFCOUNTER_IBYTES, m->m_pkthdr.len);
771274246Sae	if ((ifp->if_flags & IFF_MONITOR) != 0)
772274246Sae		m_freem(m);
773274246Sae	else
774274246Sae		netisr_dispatch(isr, m);
775274246Sae	return (IPPROTO_DONE);
776274246Saedrop:
777274246Sae	if_inc_counter(ifp, IFCOUNTER_IERRORS, 1);
778274246Sae	m_freem(m);
779274246Sae	return (IPPROTO_DONE);
780274246Sae}
781274246Sae
782274246Sae#define	MTAG_GRE	1307983903
783274246Saestatic int
784274246Saegre_check_nesting(struct ifnet *ifp, struct mbuf *m)
785274246Sae{
786274246Sae	struct m_tag *mtag;
787274246Sae	int count;
788274246Sae
789274246Sae	count = 1;
790274246Sae	mtag = NULL;
791276902Sae	while ((mtag = m_tag_locate(m, MTAG_GRE, 0, mtag)) != NULL) {
792274246Sae		if (*(struct ifnet **)(mtag + 1) == ifp) {
793274246Sae			log(LOG_NOTICE, "%s: loop detected\n", ifp->if_xname);
794274246Sae			return (EIO);
795274246Sae		}
796274246Sae		count++;
797103026Ssobomax	}
798274246Sae	if (count > V_max_gre_nesting) {
799274246Sae		log(LOG_NOTICE,
800274246Sae		    "%s: if_output recursively called too many times(%d)\n",
801274246Sae		    ifp->if_xname, count);
802274246Sae		return (EIO);
803274246Sae	}
804274246Sae	mtag = m_tag_alloc(MTAG_GRE, 0, sizeof(struct ifnet *), M_NOWAIT);
805274246Sae	if (mtag == NULL)
806274246Sae		return (ENOMEM);
807274246Sae	*(struct ifnet **)(mtag + 1) = ifp;
808274246Sae	m_tag_prepend(m, mtag);
809274246Sae	return (0);
810274246Sae}
811103026Ssobomax
812274246Saestatic int
813274246Saegre_output(struct ifnet *ifp, struct mbuf *m, const struct sockaddr *dst,
814274246Sae   struct route *ro)
815274246Sae{
816274246Sae	uint32_t af;
817274246Sae	int error;
818103026Ssobomax
819274246Sae#ifdef MAC
820274246Sae	error = mac_ifnet_check_transmit(ifp, m);
821274246Sae	if (error != 0)
822274246Sae		goto drop;
823103026Ssobomax#endif
824274246Sae	if ((ifp->if_flags & IFF_MONITOR) != 0 ||
825274246Sae	    (ifp->if_flags & IFF_UP) == 0) {
826274246Sae		error = ENETDOWN;
827274246Sae		goto drop;
828274246Sae	}
829103026Ssobomax
830274246Sae	error = gre_check_nesting(ifp, m);
831274246Sae	if (error != 0)
832274246Sae		goto drop;
833274246Sae
834274246Sae	m->m_flags &= ~(M_BCAST|M_MCAST);
835274246Sae	if (dst->sa_family == AF_UNSPEC)
836274246Sae		bcopy(dst->sa_data, &af, sizeof(af));
837274246Sae	else
838274246Sae		af = dst->sa_family;
839274246Sae	BPF_MTAP2(ifp, &af, sizeof(af), m);
840274246Sae	m->m_pkthdr.csum_data = af;	/* save af for if_transmit */
841274246Sae	return (ifp->if_transmit(ifp, m));
842274246Saedrop:
843274246Sae	m_freem(m);
844274246Sae	if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
845274246Sae	return (error);
846103026Ssobomax}
847103026Ssobomax
848274246Saestatic void
849274246Saegre_setseqn(struct grehdr *gh, uint32_t seq)
850103026Ssobomax{
851274246Sae	uint32_t *opts;
852274246Sae	uint16_t flags;
853103026Ssobomax
854274246Sae	opts = gh->gre_opts;
855274246Sae	flags = ntohs(gh->gre_flags);
856274246Sae	KASSERT((flags & GRE_FLAGS_SP) != 0,
857274246Sae	    ("gre_setseqn called, but GRE_FLAGS_SP isn't set "));
858274246Sae	if (flags & GRE_FLAGS_CP)
859274246Sae		opts++;
860274246Sae	if (flags & GRE_FLAGS_KP)
861274246Sae		opts++;
862274246Sae	*opts = htonl(seq);
863274246Sae}
864103026Ssobomax
865274246Saestatic int
866274246Saegre_transmit(struct ifnet *ifp, struct mbuf *m)
867274246Sae{
868274246Sae	GRE_RLOCK_TRACKER;
869274246Sae	struct gre_softc *sc;
870274246Sae	struct grehdr *gh;
871274246Sae	uint32_t iaf, oaf, oseq;
872274246Sae	int error, hlen, olen, plen;
873274246Sae	int want_seq, want_csum;
874274246Sae
875274246Sae	plen = 0;
876274246Sae	sc = ifp->if_softc;
877274246Sae	if (sc == NULL) {
878274246Sae		error = ENETDOWN;
879274246Sae		m_freem(m);
880274246Sae		goto drop;
881103026Ssobomax	}
882274246Sae	GRE_RLOCK(sc);
883274246Sae	if (sc->gre_family == 0) {
884274246Sae		GRE_RUNLOCK(sc);
885274246Sae		error = ENETDOWN;
886274246Sae		m_freem(m);
887274246Sae		goto drop;
888274246Sae	}
889274246Sae	iaf = m->m_pkthdr.csum_data;
890274246Sae	oaf = sc->gre_family;
891274246Sae	hlen = sc->gre_hlen;
892274246Sae	want_seq = (sc->gre_options & GRE_ENABLE_SEQ) != 0;
893274246Sae	if (want_seq)
894274246Sae		oseq = sc->gre_oseq++; /* XXX */
895274289Sbz	else
896274289Sbz		oseq = 0;		/* Make compiler happy. */
897274246Sae	want_csum = (sc->gre_options & GRE_ENABLE_CSUM) != 0;
898274246Sae	M_SETFIB(m, sc->gre_fibnum);
899274246Sae	M_PREPEND(m, hlen, M_NOWAIT);
900274246Sae	if (m == NULL) {
901274246Sae		GRE_RUNLOCK(sc);
902274246Sae		error = ENOBUFS;
903274246Sae		goto drop;
904274246Sae	}
905274246Sae	bcopy(sc->gre_hdr, mtod(m, void *), hlen);
906274246Sae	GRE_RUNLOCK(sc);
907274246Sae	switch (oaf) {
908274246Sae#ifdef INET
909274246Sae	case AF_INET:
910274246Sae		olen = sizeof(struct ip);
911274246Sae		break;
912274246Sae#endif
913274246Sae#ifdef INET6
914274246Sae	case AF_INET6:
915274246Sae		olen = sizeof(struct ip6_hdr);
916274246Sae		break;
917274246Sae#endif
918274246Sae	default:
919274246Sae		error = ENETDOWN;
920274246Sae		goto drop;
921274246Sae	}
922274246Sae	gh = (struct grehdr *)mtodo(m, olen);
923274246Sae	switch (iaf) {
924274246Sae#ifdef INET
925274246Sae	case AF_INET:
926274246Sae		gh->gre_proto = htons(ETHERTYPE_IP);
927274246Sae		break;
928274246Sae#endif
929274246Sae#ifdef INET6
930274246Sae	case AF_INET6:
931274246Sae		gh->gre_proto = htons(ETHERTYPE_IPV6);
932274246Sae		break;
933274246Sae#endif
934274246Sae	default:
935274246Sae		error = ENETDOWN;
936274246Sae		goto drop;
937274246Sae	}
938274246Sae	if (want_seq)
939274246Sae		gre_setseqn(gh, oseq);
940274246Sae	if (want_csum) {
941274246Sae		*(uint16_t *)gh->gre_opts = in_cksum_skip(m,
942274246Sae		    m->m_pkthdr.len, olen);
943274246Sae	}
944274246Sae	plen = m->m_pkthdr.len - hlen;
945274246Sae	switch (oaf) {
946274246Sae#ifdef INET
947274246Sae	case AF_INET:
948274246Sae		error = in_gre_output(m, iaf, hlen);
949274246Sae		break;
950274246Sae#endif
951274246Sae#ifdef INET6
952274246Sae	case AF_INET6:
953274246Sae		error = in6_gre_output(m, iaf, hlen);
954274246Sae		break;
955274246Sae#endif
956274246Sae	default:
957274246Sae		m_freem(m);
958274246Sae		error = ENETDOWN;
959297793Spfg	}
960274246Saedrop:
961274246Sae	if (error)
962274246Sae		if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
963274246Sae	else {
964274246Sae		if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1);
965274246Sae		if_inc_counter(ifp, IFCOUNTER_OBYTES, plen);
966274246Sae	}
967274246Sae	return (error);
968274246Sae}
969103026Ssobomax
970274246Saestatic void
971274246Saegre_qflush(struct ifnet *ifp __unused)
972274246Sae{
973274246Sae
974103026Ssobomax}
975103026Ssobomax
976103026Ssobomaxstatic int
977103026Ssobomaxgremodevent(module_t mod, int type, void *data)
978103026Ssobomax{
979103026Ssobomax
980103026Ssobomax	switch (type) {
981103026Ssobomax	case MOD_LOAD:
982103026Ssobomax	case MOD_UNLOAD:
983103026Ssobomax		break;
984132199Sphk	default:
985271918Shrs		return (EOPNOTSUPP);
986103026Ssobomax	}
987271918Shrs	return (0);
988103026Ssobomax}
989103026Ssobomax
990103026Ssobomaxstatic moduledata_t gre_mod = {
991103026Ssobomax	"if_gre",
992103026Ssobomax	gremodevent,
993241394Skevlo	0
994103026Ssobomax};
995103026Ssobomax
996103026SsobomaxDECLARE_MODULE(if_gre, gre_mod, SI_SUB_PSEUDO, SI_ORDER_ANY);
997103026SsobomaxMODULE_VERSION(if_gre, 1);
998