Overhaul if_gre(4).

Split it into two modules: if_gre(4) for GRE encapsulation and
if_me(4) for minimal encapsulation within IP.

gre(4) changes:
* convert to if_transmit;
* rework locking: protect access to softc with rmlock,
  protect from concurrent ioctls with sx lock;
* correct interface accounting for outgoing datagramms (count only payload size);
* implement generic support for using IPv6 as delivery header;
* make implementation conform to the RFC 2784 and partially to RFC 2890;
* add support for GRE checksums - calculate for outgoing datagramms and check
  for inconming datagramms;
* add support for sending sequence number in GRE header;
* remove support of cached routes. This fixes problem, when gre(4) doesn't
  work at system startup. But this also removes support for having tunnels with
  the same addresses for inner and outer header.
* deprecate support for various GREXXX ioctls, that doesn't used in FreeBSD.
  Use our standard ioctls for tunnels.

me(4):
* implementation conform to RFC 2004;
* use if_transmit;
* use the same locking model as gre(4);

PR:		164475
Differential Revision:	D1023
No objections from:	net@
Relnotes:	yes
Sponsored by:	Yandex LLC
This commit is contained in:
Andrey V. Elsukov 2014-11-07 19:13:19 +00:00
parent 833e8dc5ab
commit f325335caf
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=274246
16 changed files with 1887 additions and 1346 deletions

View File

@ -23,52 +23,50 @@
* THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef lint
static const char rcsid[] =
"$FreeBSD$";
#endif
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/sockio.h>
#include <stdlib.h>
#include <unistd.h>
#include <net/ethernet.h>
#include <net/if.h>
#include <net/if_gre.h>
#include <net/route.h>
#include <ctype.h>
#include <limits.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <err.h>
#include <errno.h>
#include "ifconfig.h"
#define GREBITS "\020\01ENABLE_CSUM\02ENABLE_SEQ"
static void gre_status(int s);
static void
gre_status(int s)
{
int grekey = 0;
uint32_t opts = 0;
ifr.ifr_data = (caddr_t)&grekey;
ifr.ifr_data = (caddr_t)&opts;
if (ioctl(s, GREGKEY, &ifr) == 0)
if (grekey != 0)
printf("\tgrekey: %d\n", grekey);
if (opts != 0)
printf("\tgrekey: 0x%x (%u)\n", opts, opts);
opts = 0;
if (ioctl(s, GREGOPTS, &ifr) != 0 || opts == 0)
return;
printb("\toptions", opts, GREBITS);
putchar('\n');
}
static void
setifgrekey(const char *val, int dummy __unused, int s,
const struct afswtch *afp)
{
uint32_t grekey = atol(val);
uint32_t grekey = strtol(val, NULL, 0);
strncpy(ifr.ifr_name, name, sizeof (ifr.ifr_name));
ifr.ifr_data = (caddr_t)&grekey;
@ -76,8 +74,35 @@ setifgrekey(const char *val, int dummy __unused, int s,
warn("ioctl (set grekey)");
}
static void
setifgreopts(const char *val, int d, int s, const struct afswtch *afp)
{
uint32_t opts;
ifr.ifr_data = (caddr_t)&opts;
if (ioctl(s, GREGOPTS, &ifr) == -1) {
warn("ioctl(GREGOPTS)");
return;
}
if (d < 0)
opts &= ~(-d);
else
opts |= d;
if (ioctl(s, GRESOPTS, &ifr) == -1) {
warn("ioctl(GIFSOPTS)");
return;
}
}
static struct cmd gre_cmds[] = {
DEF_CMD_ARG("grekey", setifgrekey),
DEF_CMD("enable_csum", GRE_ENABLE_CSUM, setifgreopts),
DEF_CMD("-enable_csum",-GRE_ENABLE_CSUM,setifgreopts),
DEF_CMD("enable_seq", GRE_ENABLE_SEQ, setifgreopts),
DEF_CMD("-enable_seq",-GRE_ENABLE_SEQ, setifgreopts),
};
static struct afswtch af_gre = {
.af_name = "af_gre",

View File

@ -252,6 +252,7 @@ MAN= aac.4 \
malo.4 \
mcd.4 \
md.4 \
me.4 \
mem.4 \
meteor.4 \
mfi.4 \

View File

@ -29,7 +29,7 @@
.\"
.\" $FreeBSD$
.\"
.Dd June 20, 2008
.Dd November 7, 2014
.Dt GRE 4
.Os
.Sh NAME
@ -68,162 +68,30 @@ and
.Cm destroy
subcommands.
.Pp
This driver currently supports the following modes of operation:
.Bl -tag -width indent
.It "GRE encapsulation (IP protocol number 47)"
Encapsulated datagrams are
prepended an outer datagram and a GRE header.
This driver corresponds to RFC 2784.
Encapsulated datagrams are prepended an outer datagram and a GRE header.
The GRE header specifies
the type of the encapsulated datagram and thus allows for tunneling other
protocols than IP.
GRE mode is also the default tunnel mode on Cisco routers.
This is also the default mode of operation of the
.Nm
interfaces.
As part of the GRE mode,
.Nm
also supports Cisco WCCP protocol, both version 1 and version 2.
Since there is no reliable way to distinguish between WCCP versions, it
should be configured manually using the
.Cm link2
flag.
If the
.Cm link2
flag is not set (default), then WCCP version 1 is selected.
.It "MOBILE encapsulation (IP protocol number 55)"
Datagrams are
encapsulated into IP, but with a shorter encapsulation.
The original
IP header is modified and the modifications are inserted between the
so modified header and the original payload.
Like
.Xr gif 4 ,
only for IP-in-IP encapsulation.
.El
.Pp
The
.Nm
interfaces support a number of
.Xr ioctl 2 Ns s ,
such as:
.Bl -tag -width ".Dv GRESADDRS"
.It Dv GRESADDRS
Set the IP address of the local tunnel end.
This is the source address
set by or displayed by
.Xr ifconfig 8
for the
.Nm
interface.
.It Dv GRESADDRD
Set the IP address of the remote tunnel end.
This is the destination address
set by or displayed by
.Xr ifconfig 8
for the
.Nm
interface.
.It Dv GREGADDRS
Query the IP address that is set for the local tunnel end.
This is the
address the encapsulation header carries as local address (i.e., the real
address of the tunnel start point).
.It Dv GREGADDRD
Query the IP address that is set for the remote tunnel end.
This is the
address the encapsulated packets are sent to (i.e., the real address of
the remote tunnel endpoint).
.It Dv GRESPROTO
Set the operation mode to the specified IP protocol value.
The
protocol is passed to the interface in
.Po Vt "struct ifreq" Pc Ns Li -> Ns Va ifr_flags .
The operation mode can also be given as
.Pp
.Bl -tag -width ".Cm -link0" -compact
.It Cm link0
.Dv IPPROTO_GRE
.It Cm -link0
.Dv IPPROTO_MOBILE
.El
.Pp
to
.Xr ifconfig 8 .
.Pp
The
.Cm link1
flag is not used to choose encapsulation, but to modify the
internal route search for the remote tunnel endpoint, see the
.Sx BUGS
section below.
.It Dv GREGPROTO
Query operation mode.
.It Dv GRESKEY
interfaces support a number of additional parameters to the
.Xr ifconfig 8 :
.Bl -tag -width "enable_csum"
.It Ar grekey
Set the GRE key used for outgoing packets.
A value of 0 disables the key option.
.It Dv GREGKEY
Get the GRE key currently used for outgoing packets.
0 means no outgoing key.
.It Ar enable_csum
Enables checksum calculation for outgoing packets.
.It Ar enable_seq
Enables use of sequence number field in the GRE header for outgoing packets.
.El
.Pp
Note that the IP addresses of the tunnel endpoints may be the same as the
ones defined with
.Xr ifconfig 8
for the interface (as if IP is encapsulated), but need not be.
.Sh EXAMPLES
Configuration example:
.Bd -literal
Host X-- Host A ----------------tunnel---------- Cisco D------Host E
\\ |
\\ /
+------Host B----------Host C----------+
.Ed
.Pp
On host A
.Pq Fx :
.Bd -literal -offset indent
route add default B
ifconfig greN create
ifconfig greN A D netmask 0xffffffff linkX up
ifconfig greN tunnel A D
route add E D
.Ed
.Pp
On Host D (Cisco):
.Bd -literal -offset indent
Interface TunnelX
ip unnumbered D ! e.g. address from Ethernet interface
tunnel source D ! e.g. address from Ethernet interface
tunnel destination A
ip route C <some interface and mask>
ip route A mask C
ip route X mask tunnelX
.Ed
.Pp
OR
.Pp
On Host D
.Pq Fx :
.Bd -literal -offset indent
route add default C
ifconfig greN create
ifconfig greN D A
ifconfig greN tunnel D A
.Ed
.Pp
If all goes well, you should see packets flowing ;-)
.Pp
If you want to reach Host A over the tunnel (from Host D (Cisco)), then
you have to have an alias on Host A for e.g.\& the Ethernet interface like:
.Pp
.Dl "ifconfig <etherif> alias Y"
.Pp
and on the Cisco:
.Pp
.Dl "ip route Y mask tunnelX"
.Pp
A similar setup can be used to create a link between two private networks
(for example in the 192.168 subnet) over the Internet:
.Bd -literal
192.168.1.* --- Router A -------tunnel-------- Router B --- 192.168.2.*
\\ /
@ -238,29 +106,22 @@ Assuming router A has the (external) IP address A and the internal address
On router A:
.Bd -literal -offset indent
ifconfig greN create
ifconfig greN 192.168.1.1 192.168.2.1 link1
ifconfig greN tunnel A B
ifconfig greN inet 192.168.1.1 192.168.2.1
ifconfig greN inet tunnel A B
route add -net 192.168.2 -netmask 255.255.255.0 192.168.2.1
.Ed
.Pp
On router B:
.Bd -literal -offset indent
ifconfig greN create
ifconfig greN 192.168.2.1 192.168.1.1 link1
ifconfig greN tunnel B A
ifconfig greN inet 192.168.2.1 192.168.1.1
ifconfig greN inet tunnel B A
route add -net 192.168.1 -netmask 255.255.255.0 192.168.1.1
.Ed
.Pp
Note that this is a safe situation where the
.Cm link1
flag (as discussed in the
.Sx BUGS
section below) may (and probably should) be set.
.Sh NOTES
The MTU of
.Nm
interfaces is set to 1476 by default, to match the value used by Cisco routers.
If grekey is set this is lowered to 1472.
This may not be an optimal value, depending on the link between the two tunnel
endpoints.
It can be adjusted via
@ -268,25 +129,8 @@ It can be adjusted via
.Pp
For correct operation, the
.Nm
device needs a route to the destination that is less specific than the
one over the tunnel.
(Basically, there needs to be a route to the decapsulating host that
does not run over the tunnel, as this would be a loop.)
If the addresses are ambiguous, doing the
.Nm ifconfig Cm tunnel
step before the
.Xr ifconfig 8
call to set the
.Nm
IP addresses will help to find a route outside the tunnel.
.Pp
In order to tell
.Xr ifconfig 8
to actually mark the interface as
.Dq up ,
the keyword
.Cm up
must be given last on its command line.
device needs a route to the decapsulating host that does not run over the tunnel,
as this would be a loop.
.Pp
The kernel must be set to forward datagrams by setting the
.Va net.inet.ip.forwarding
@ -296,41 +140,20 @@ variable to non-zero.
.Xr gif 4 ,
.Xr inet 4 ,
.Xr ip 4 ,
.Xr me 4 ,
.Xr netintro 4 ,
.\" Xr options 4 ,
.Xr protocols 5 ,
.Xr ifconfig 8 ,
.Xr sysctl 8
.Pp
A description of GRE encapsulation can be found in RFC 1701 and RFC 1702.
.Pp
A description of MOBILE encapsulation can be found in RFC 2004.
A description of GRE encapsulation can be found in RFC 2784 and RFC 2890.
.Sh AUTHORS
.An Andrey V. Elsukov Aq Mt ae@FreeBSD.org
.An Heiko W.Rupp Aq Mt hwr@pilhuhn.de
.Sh BUGS
The
.Fn compute_route
code in
.Pa if_gre.c
toggles the last bit of the
IP-address to provoke the search for a less specific route than the
one directly over the tunnel to prevent loops.
This is possibly not the best solution.
.Pp
To avoid the address munging described above, turn on the
.Cm link1
flag on the
.Xr ifconfig 8
command line.
This implies that the GRE packet destination and the ifconfig remote host
are not the same IP addresses, and that the GRE destination does not route
over the
.Nm
interface itself.
.Pp
The current implementation uses the key only for outgoing packets.
Incoming packets with a different key or without a key will be treated as if they
would belong to this interface.
.Pp
RFC1701 is not fully supported, however all unsupported features have been
deprecated in RFC2784.
The sequence number field also used only for outgoing packets.

85
share/man/man4/me.4 Normal file
View File

@ -0,0 +1,85 @@
.\" Copyright (c) Andrey V. Elsukov <ae@FreeBSD.org>
.\" All rights reserved.
.\"
.\" Redistribution and use in source and binary forms, with or without
.\" modification, are permitted provided that the following conditions
.\" are met:
.\" 1. Redistributions of source code must retain the above copyright
.\" notice, this list of conditions and the following disclaimer.
.\" 2. Redistributions in binary form must reproduce the above copyright
.\" notice, this list of conditions and the following disclaimer in the
.\" documentation and/or other materials provided with the distribution.
.\"
.\" THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
.\" $FreeBSD$
.\"
.Dd November 7, 2014
.Dt ME 4
.Os
.Sh NAME
.Nm me
.Nd encapsulating network device
.Sh SYNOPSIS
To compile the
driver into the kernel, place the following line in the kernel
configuration file:
.Bd -ragged -offset indent
.Cd "device me"
.Ed
.Pp
Alternatively, to load the
driver as a module at boot time, place the following line in
.Xr loader.conf 5 :
.Bd -literal -offset indent
if_me_load="YES"
.Ed
.Sh DESCRIPTION
The
.Nm
network interface pseudo device encapsulates datagrams
into IP.
These encapsulated datagrams are routed to a destination host,
where they are decapsulated and further routed to their final destination.
.Pp
.Nm
interfaces are dynamically created and destroyed with the
.Xr ifconfig 8
.Cm create
and
.Cm destroy
subcommands.
.Pp
This driver corresponds to RFC 2004.
Datagrams are encapsulated into IP with a shorter encapsulation.
The original
IP header is modified and the modifications are inserted between the
so modified header and the original payload.
The protocol number 55 is used for outer header.
.Sh NOTES
.Pp
For correct operation, the
.Nm
device needs a route to the decapsulating host that does not run over the tunnel,
as this would be a loop.
.Sh SEE ALSO
.Xr gif 4 ,
.Xr gre 4 ,
.Xr inet 4 ,
.Xr ip 4 ,
.Xr netintro 4 ,
.Xr protocols 5 ,
.Xr ifconfig 8 ,
.Xr sysctl 8
.Sh AUTHORS
.An Andrey V. Elsukov Aq Mt ae@FreeBSD.org

View File

@ -879,12 +879,15 @@ device tun
# The `gif' device implements IPv6 over IP4 tunneling,
# IPv4 over IPv6 tunneling, IPv4 over IPv4 tunneling and
# IPv6 over IPv6 tunneling.
# The `gre' device implements two types of IP4 over IP4 tunneling:
# GRE and MOBILE, as specified in the RFC1701 and RFC2004.
# The `gre' device implements GRE (Generic Routing Encapsulation) tunneling,
# as specified in the RFC 2784 and RFC 2890.
# The `me' device implements Minimal Encapsulation within IPv4 as
# specified in the RFC 2004.
# The XBONEHACK option allows the same pair of addresses to be configured on
# multiple gif interfaces.
device gif
device gre
device me
options XBONEHACK
# The `faith' device captures packets sent to it and diverts them

View File

@ -3234,11 +3234,12 @@ net/if_fddisubr.c optional fddi
net/if_fwsubr.c optional fwip
net/if_gif.c optional gif inet | gif inet6 | \
netgraph_gif inet | netgraph_gif inet6
net/if_gre.c optional gre inet
net/if_gre.c optional gre inet | gre inet6
net/if_iso88025subr.c optional token
net/if_lagg.c optional lagg
net/if_loop.c optional loop
net/if_llatbl.c standard
net/if_me.c optional me inet
net/if_media.c standard
net/if_mib.c standard
net/if_spppfr.c optional sppp | netgraph_sppp
@ -3473,6 +3474,7 @@ netinet6/in6_proto.c optional inet6
netinet6/in6_rmx.c optional inet6
netinet6/in6_src.c optional inet6
netinet6/ip6_forward.c optional inet6
netinet6/ip6_gre.c optional gre inet6
netinet6/ip6_id.c optional inet6
netinet6/ip6_input.c optional inet6
netinet6/ip6_mroute.c optional mrouting inet6

View File

@ -146,8 +146,9 @@ SUBDIR= \
if_edsc \
if_epair \
if_faith \
if_gif \
${_if_gif} \
${_if_gre} \
${_if_me} \
if_lagg \
${_if_ndis} \
if_stf \
@ -403,10 +404,12 @@ _random= random
defined(ALL_MODULES)
_carp= carp
_toecore= toecore
_if_gif= if_gif
_if_gre= if_gre
.endif
.if ${MK_INET_SUPPORT} != "no" || defined(ALL_MODULES)
_if_gre= if_gre
_if_me= if_me
_ipdivert= ipdivert
_ipfw= ipfw
.endif

View File

@ -1,8 +1,17 @@
# $FreeBSD$
.PATH: ${.CURDIR}/../../net ${.CURDIR}/../../netinet ${.CURDIR}/../../netinet6
.include "${.CURDIR}/../../conf/kern.opts.mk"
KMOD= if_gre
SRCS= if_gre.c ip_gre.c opt_inet.h opt_inet6.h
SRCS= if_gre.c opt_inet.h opt_inet6.h
.if ${MK_INET_SUPPORT} != "no"
SRCS+= ip_gre.c
.endif
.if ${MK_INET6_SUPPORT} != "no"
SRCS+= ip6_gre.c
.endif
.include <bsd.kmod.mk>

View File

@ -0,0 +1,8 @@
# $FreeBSD$
.PATH: ${.CURDIR}/../../net
KMOD= if_me
SRCS= if_me.c
.include <bsd.kmod.mk>

File diff suppressed because it is too large Load Diff

View File

@ -1,8 +1,6 @@
/* $NetBSD: if_gre.h,v 1.13 2003/11/10 08:51:52 wiz Exp $ */
/* $FreeBSD$ */
/*-
* Copyright (c) 1998 The NetBSD Foundation, Inc.
* Copyright (c) 2014 Andrey V. Elsukov <ae@FreeBSD.org>
* All rights reserved
*
* This code is derived from software contributed to The NetBSD Foundation
@ -28,166 +26,102 @@
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* $NetBSD: if_gre.h,v 1.13 2003/11/10 08:51:52 wiz Exp $
* $FreeBSD$
*/
#ifndef _NET_IF_GRE_H
#define _NET_IF_GRE_H
#ifndef _NET_IF_GRE_H_
#define _NET_IF_GRE_H_
#include <sys/ioccom.h>
#ifdef _KERNEL
#include <sys/queue.h>
/* GRE header according to RFC 2784 and RFC 2890 */
struct grehdr {
uint16_t gre_flags; /* GRE flags */
#define GRE_FLAGS_CP 0x8000 /* checksum present */
#define GRE_FLAGS_KP 0x2000 /* key present */
#define GRE_FLAGS_SP 0x1000 /* sequence present */
#define GRE_FLAGS_MASK (GRE_FLAGS_CP|GRE_FLAGS_KP|GRE_FLAGS_SP)
uint16_t gre_proto; /* protocol type */
uint32_t gre_opts[0]; /* optional fields */
} __packed;
/*
* Version of the WCCP, need to be configured manually since
* header for version 2 is the same but IP payload is prepended
* with additional 4-bytes field.
*/
typedef enum {
WCCP_V1 = 0,
WCCP_V2
} wccp_ver_t;
#ifdef INET
struct greip {
struct ip gi_ip;
struct grehdr gi_gre;
} __packed;
#endif
#ifdef INET6
struct greip6 {
struct ip6_hdr gi6_ip6;
struct grehdr gi6_gre;
} __packed;
#endif
struct gre_softc {
struct ifnet *sc_ifp;
LIST_ENTRY(gre_softc) sc_list;
int gre_unit;
int gre_flags;
u_int gre_fibnum; /* use this fib for envelopes */
struct in_addr g_src; /* source address of gre packets */
struct in_addr g_dst; /* destination address of gre packets */
struct route route; /* routing entry that determines, where a
encapsulated packet should go */
u_char g_proto; /* protocol of encapsulator */
const struct encaptab *encap; /* encapsulation cookie */
uint32_t key; /* key included in outgoing GRE packets */
/* zero means none */
wccp_ver_t wccp_ver; /* version of the WCCP */
struct ifnet *gre_ifp;
LIST_ENTRY(gre_softc) gre_list;
struct rmlock gre_lock;
int gre_family; /* AF of delivery header */
uint32_t gre_iseq;
uint32_t gre_oseq;
uint32_t gre_key;
uint32_t gre_options;
uint32_t gre_mtu;
u_int gre_fibnum;
u_int gre_hlen; /* header size */
union {
void *hdr;
#ifdef INET
struct greip *gihdr;
#endif
#ifdef INET6
struct greip6 *gi6hdr;
#endif
} gre_uhdr;
const struct encaptab *gre_ecookie;
};
#define GRE2IFP(sc) ((sc)->sc_ifp)
#define GRE2IFP(sc) ((sc)->gre_ifp)
#define GRE_LOCK_INIT(sc) rm_init(&(sc)->gre_lock, "gre softc")
#define GRE_LOCK_DESTROY(sc) rm_destroy(&(sc)->gre_lock)
#define GRE_RLOCK_TRACKER struct rm_priotracker gre_tracker
#define GRE_RLOCK(sc) rm_rlock(&(sc)->gre_lock, &gre_tracker)
#define GRE_RUNLOCK(sc) rm_runlock(&(sc)->gre_lock, &gre_tracker)
#define GRE_RLOCK_ASSERT(sc) rm_assert(&(sc)->gre_lock, RA_RLOCKED)
#define GRE_WLOCK(sc) rm_wlock(&(sc)->gre_lock)
#define GRE_WUNLOCK(sc) rm_wunlock(&(sc)->gre_lock)
#define GRE_WLOCK_ASSERT(sc) rm_assert(&(sc)->gre_lock, RA_WLOCKED)
struct gre_h {
u_int16_t flags; /* GRE flags */
u_int16_t ptype; /* protocol type of payload typically
Ether protocol type*/
uint32_t options[0]; /* optional options */
/*
* from here on: fields are optional, presence indicated by flags
*
u_int_16 checksum checksum (one-complements of GRE header
and payload
Present if (ck_pres | rt_pres == 1).
Valid if (ck_pres == 1).
u_int_16 offset offset from start of routing filed to
first octet of active SRE (see below).
Present if (ck_pres | rt_pres == 1).
Valid if (rt_pres == 1).
u_int_32 key inserted by encapsulator e.g. for
authentication
Present if (key_pres ==1 ).
u_int_32 seq_num Sequence number to allow for packet order
Present if (seq_pres ==1 ).
struct gre_sre[] routing Routing fileds (see below)
Present if (rt_pres == 1)
*/
} __packed;
struct greip {
struct ip gi_i;
struct gre_h gi_g;
} __packed;
#define gi_pr gi_i.ip_p
#define gi_len gi_i.ip_len
#define gi_src gi_i.ip_src
#define gi_dst gi_i.ip_dst
#define gi_ptype gi_g.ptype
#define gi_flags gi_g.flags
#define gi_options gi_g.options
#define GRE_CP 0x8000 /* Checksum Present */
#define GRE_RP 0x4000 /* Routing Present */
#define GRE_KP 0x2000 /* Key Present */
#define GRE_SP 0x1000 /* Sequence Present */
#define GRE_SS 0x0800 /* Strict Source Route */
#define gre_hdr gre_uhdr.hdr
#define gre_gihdr gre_uhdr.gihdr
#define gre_gi6hdr gre_uhdr.gi6hdr
#define gre_oip gre_gihdr->gi_ip
#define gre_oip6 gre_gi6hdr->gi6_ip6
/*
* CISCO uses special type for GRE tunnel created as part of WCCP
* connection, while in fact those packets are just IPv4 encapsulated
* into GRE.
*/
#define WCCP_PROTOCOL_TYPE 0x883E
/*
* gre_sre defines a Source route Entry. These are needed if packets
* should be routed over more than one tunnel hop by hop
*/
struct gre_sre {
u_int16_t sre_family; /* address family */
u_char sre_offset; /* offset to first octet of active entry */
u_char sre_length; /* number of octets in the SRE.
sre_lengthl==0 -> last entry. */
u_char *sre_rtinfo; /* the routing information */
};
struct greioctl {
int unit;
struct in_addr addr;
};
/* for mobile encaps */
struct mobile_h {
u_int16_t proto; /* protocol and S-bit */
u_int16_t hcrc; /* header checksum */
u_int32_t odst; /* original destination address */
u_int32_t osrc; /* original source addr, if S-bit set */
} __packed;
struct mobip_h {
struct ip mi;
struct mobile_h mh;
} __packed;
#define MOB_H_SIZ_S (sizeof(struct mobile_h) - sizeof(u_int32_t))
#define MOB_H_SIZ_L (sizeof(struct mobile_h))
#define MOB_H_SBIT 0x0080
#define GRE_TTL 30
#define ETHERTYPE_WCCP 0x883E
#endif /* _KERNEL */
/*
* ioctls needed to manipulate the interface
*/
#define GRESADDRS _IOW('i', 101, struct ifreq)
#define GRESADDRD _IOW('i', 102, struct ifreq)
#define GREGADDRS _IOWR('i', 103, struct ifreq)
#define GREGADDRD _IOWR('i', 104, struct ifreq)
#define GRESPROTO _IOW('i' , 105, struct ifreq)
#define GREGPROTO _IOWR('i', 106, struct ifreq)
#define GREGKEY _IOWR('i', 107, struct ifreq)
#define GRESKEY _IOW('i', 108, struct ifreq)
#ifdef _KERNEL
LIST_HEAD(gre_softc_head, gre_softc);
VNET_DECLARE(struct gre_softc_head, gre_softc_list);
#define V_gre_softc_list VNET(gre_softc_list)
#define GREGKEY _IOWR('i', 107, struct ifreq)
#define GRESKEY _IOW('i', 108, struct ifreq)
#define GREGOPTS _IOWR('i', 109, struct ifreq)
#define GRESOPTS _IOW('i', 110, struct ifreq)
VNET_DECLARE(struct mtx, gre_mtx);
#define V_gre_mtx VNET(gre_mtx)
#define GRE_LIST_LOCK_INIT(x) mtx_init(&V_gre_mtx, "gre_mtx", NULL, \
MTX_DEF)
#define GRE_LIST_LOCK_DESTROY(x) mtx_destroy(&V_gre_mtx)
#define GRE_LIST_LOCK(x) mtx_lock(&V_gre_mtx)
#define GRE_LIST_UNLOCK(x) mtx_unlock(&V_gre_mtx)
#define GRE_ENABLE_CSUM 0x0001
#define GRE_ENABLE_SEQ 0x0002
#define GRE_OPTMASK (GRE_ENABLE_CSUM|GRE_ENABLE_SEQ)
u_int16_t gre_in_cksum(u_int16_t *, u_int);
#endif /* _KERNEL */
#endif
#endif /* _NET_IF_GRE_H_ */

647
sys/net/if_me.c Normal file
View File

@ -0,0 +1,647 @@
/*-
* Copyright (c) 2014 Andrey V. Elsukov <ae@FreeBSD.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/jail.h>
#include <sys/kernel.h>
#include <sys/lock.h>
#include <sys/libkern.h>
#include <sys/malloc.h>
#include <sys/module.h>
#include <sys/mbuf.h>
#include <sys/priv.h>
#include <sys/proc.h>
#include <sys/protosw.h>
#include <sys/rmlock.h>
#include <sys/socket.h>
#include <sys/sockio.h>
#include <sys/sx.h>
#include <sys/sysctl.h>
#include <sys/syslog.h>
#include <sys/systm.h>
#include <net/bpf.h>
#include <net/ethernet.h>
#include <net/if.h>
#include <net/if_var.h>
#include <net/if_clone.h>
#include <net/if_types.h>
#include <net/netisr.h>
#include <net/vnet.h>
#include <netinet/in.h>
#include <netinet/in_systm.h>
#include <netinet/in_var.h>
#include <netinet/ip.h>
#include <netinet/ip_var.h>
#include <netinet/ip_encap.h>
#include <machine/in_cksum.h>
#include <security/mac/mac_framework.h>
#define MEMTU 1500
static const char mename[] = "me";
static MALLOC_DEFINE(M_IFME, mename, "Minimal Encapsulation for IP");
static VNET_DEFINE(struct mtx, me_mtx);
#define V_me_mtx VNET(me_mtx)
/* Minimal forwarding header RFC 2004 */
struct mobhdr {
uint8_t mob_proto; /* protocol */
uint8_t mob_flags; /* flags */
#define MOB_FLAGS_SP 0x80 /* source present */
uint16_t mob_csum; /* header checksum */
struct in_addr mob_dst; /* original destination address */
struct in_addr mob_src; /* original source addr (optional) */
} __packed;
struct me_softc {
struct ifnet *me_ifp;
LIST_ENTRY(me_softc) me_list;
struct rmlock me_lock;
u_int me_fibnum;
const struct encaptab *me_ecookie;
struct in_addr me_src;
struct in_addr me_dst;
};
#define ME2IFP(sc) ((sc)->me_ifp)
#define ME_READY(sc) ((sc)->me_src.s_addr != 0)
#define ME_LOCK_INIT(sc) rm_init(&(sc)->me_lock, "me softc")
#define ME_LOCK_DESTROY(sc) rm_destroy(&(sc)->me_lock)
#define ME_RLOCK_TRACKER struct rm_priotracker me_tracker
#define ME_RLOCK(sc) rm_rlock(&(sc)->me_lock, &me_tracker)
#define ME_RUNLOCK(sc) rm_runlock(&(sc)->me_lock, &me_tracker)
#define ME_RLOCK_ASSERT(sc) rm_assert(&(sc)->me_lock, RA_RLOCKED)
#define ME_WLOCK(sc) rm_wlock(&(sc)->me_lock)
#define ME_WUNLOCK(sc) rm_wunlock(&(sc)->me_lock)
#define ME_WLOCK_ASSERT(sc) rm_assert(&(sc)->me_lock, RA_WLOCKED)
#define ME_LIST_LOCK_INIT(x) mtx_init(&V_me_mtx, "me_mtx", NULL, MTX_DEF)
#define ME_LIST_LOCK_DESTROY(x) mtx_destroy(&V_me_mtx)
#define ME_LIST_LOCK(x) mtx_lock(&V_me_mtx)
#define ME_LIST_UNLOCK(x) mtx_unlock(&V_me_mtx)
static VNET_DEFINE(LIST_HEAD(, me_softc), me_softc_list);
#define V_me_softc_list VNET(me_softc_list)
static struct sx me_ioctl_sx;
SX_SYSINIT(me_ioctl_sx, &me_ioctl_sx, "me_ioctl");
static int me_clone_create(struct if_clone *, int, caddr_t);
static void me_clone_destroy(struct ifnet *);
static VNET_DEFINE(struct if_clone *, me_cloner);
#define V_me_cloner VNET(me_cloner)
static void me_qflush(struct ifnet *);
static int me_transmit(struct ifnet *, struct mbuf *);
static int me_ioctl(struct ifnet *, u_long, caddr_t);
static int me_output(struct ifnet *, struct mbuf *,
const struct sockaddr *, struct route *);
static int me_input(struct mbuf **, int *, int);
static int me_set_tunnel(struct ifnet *, struct sockaddr_in *,
struct sockaddr_in *);
static void me_delete_tunnel(struct ifnet *);
SYSCTL_DECL(_net_link);
static SYSCTL_NODE(_net_link, IFT_TUNNEL, me, CTLFLAG_RW, 0,
"Minimal Encapsulation for IP (RFC 2004)");
#ifndef MAX_ME_NEST
#define MAX_ME_NEST 1
#endif
static VNET_DEFINE(int, max_me_nesting) = MAX_ME_NEST;
#define V_max_me_nesting VNET(max_me_nesting)
SYSCTL_INT(_net_link_me, OID_AUTO, max_nesting, CTLFLAG_RW | CTLFLAG_VNET,
&VNET_NAME(max_me_nesting), 0, "Max nested tunnels");
extern struct domain inetdomain;
static const struct protosw in_mobile_protosw = {
.pr_type = SOCK_RAW,
.pr_domain = &inetdomain,
.pr_protocol = IPPROTO_MOBILE,
.pr_flags = PR_ATOMIC|PR_ADDR,
.pr_input = me_input,
.pr_output = rip_output,
.pr_ctlinput = rip_ctlinput,
.pr_ctloutput = rip_ctloutput,
.pr_usrreqs = &rip_usrreqs
};
static void
vnet_me_init(const void *unused __unused)
{
LIST_INIT(&V_me_softc_list);
ME_LIST_LOCK_INIT();
V_me_cloner = if_clone_simple(mename, me_clone_create,
me_clone_destroy, 0);
}
VNET_SYSINIT(vnet_me_init, SI_SUB_PROTO_IFATTACHDOMAIN, SI_ORDER_ANY,
vnet_me_init, NULL);
static void
vnet_me_uninit(const void *unused __unused)
{
if_clone_detach(V_me_cloner);
ME_LIST_LOCK_DESTROY();
}
VNET_SYSUNINIT(vnet_me_uninit, SI_SUB_PROTO_IFATTACHDOMAIN, SI_ORDER_ANY,
vnet_me_uninit, NULL);
static int
me_clone_create(struct if_clone *ifc, int unit, caddr_t params)
{
struct me_softc *sc;
sc = malloc(sizeof(struct me_softc), M_IFME, M_WAITOK | M_ZERO);
sc->me_fibnum = curthread->td_proc->p_fibnum;
ME2IFP(sc) = if_alloc(IFT_TUNNEL);
ME_LOCK_INIT(sc);
ME2IFP(sc)->if_softc = sc;
if_initname(ME2IFP(sc), mename, unit);
ME2IFP(sc)->if_mtu = MEMTU - sizeof(struct mobhdr);
ME2IFP(sc)->if_flags = IFF_POINTOPOINT|IFF_MULTICAST;
ME2IFP(sc)->if_output = me_output;
ME2IFP(sc)->if_ioctl = me_ioctl;
ME2IFP(sc)->if_transmit = me_transmit;
ME2IFP(sc)->if_qflush = me_qflush;
if_attach(ME2IFP(sc));
bpfattach(ME2IFP(sc), DLT_NULL, sizeof(u_int32_t));
ME_LIST_LOCK();
LIST_INSERT_HEAD(&V_me_softc_list, sc, me_list);
ME_LIST_UNLOCK();
return (0);
}
static void
me_clone_destroy(struct ifnet *ifp)
{
struct me_softc *sc;
sx_xlock(&me_ioctl_sx);
sc = ifp->if_softc;
me_delete_tunnel(ifp);
ME_LIST_LOCK();
LIST_REMOVE(sc, me_list);
ME_LIST_UNLOCK();
bpfdetach(ifp);
if_detach(ifp);
ifp->if_softc = NULL;
sx_xunlock(&me_ioctl_sx);
if_free(ifp);
ME_LOCK_DESTROY(sc);
free(sc, M_IFME);
}
static int
me_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
{
ME_RLOCK_TRACKER;
struct ifreq *ifr = (struct ifreq *)data;
struct sockaddr_in *src, *dst;
struct me_softc *sc;
int error;
switch (cmd) {
case SIOCSIFMTU:
if (ifr->ifr_mtu < 576)
return (EINVAL);
ifp->if_mtu = ifr->ifr_mtu - sizeof(struct mobhdr);
return (0);
case SIOCSIFADDR:
ifp->if_flags |= IFF_UP;
case SIOCSIFFLAGS:
case SIOCADDMULTI:
case SIOCDELMULTI:
return (0);
}
sx_xlock(&me_ioctl_sx);
sc = ifp->if_softc;
if (sc == NULL) {
error = ENXIO;
goto end;
}
error = 0;
switch (cmd) {
case SIOCSIFPHYADDR:
src = (struct sockaddr_in *)
&(((struct in_aliasreq *)data)->ifra_addr);
dst = (struct sockaddr_in *)
&(((struct in_aliasreq *)data)->ifra_dstaddr);
if (src->sin_family != dst->sin_family ||
src->sin_family != AF_INET ||
src->sin_len != dst->sin_len ||
src->sin_len != sizeof(struct sockaddr_in)) {
error = EINVAL;
break;
}
if (src->sin_addr.s_addr == INADDR_ANY ||
dst->sin_addr.s_addr == INADDR_ANY) {
error = EADDRNOTAVAIL;
break;
}
error = me_set_tunnel(ifp, src, dst);
break;
case SIOCDIFPHYADDR:
me_delete_tunnel(ifp);
break;
case SIOCGIFPSRCADDR:
case SIOCGIFPDSTADDR:
ME_RLOCK(sc);
if (!ME_READY(sc)) {
error = EADDRNOTAVAIL;
ME_RUNLOCK(sc);
break;
}
src = (struct sockaddr_in *)&ifr->ifr_addr;
memset(src, 0, sizeof(*src));
src->sin_family = AF_INET;
src->sin_len = sizeof(*src);
switch (cmd) {
case SIOCGIFPSRCADDR:
src->sin_addr = sc->me_src;
break;
case SIOCGIFPDSTADDR:
src->sin_addr = sc->me_dst;
break;
}
ME_RUNLOCK(sc);
error = prison_if(curthread->td_ucred, sintosa(src));
if (error != 0)
memset(src, 0, sizeof(*src));
break;
default:
error = EINVAL;
break;
}
end:
sx_xunlock(&me_ioctl_sx);
return (error);
}
static int
me_encapcheck(const struct mbuf *m, int off, int proto, void *arg)
{
ME_RLOCK_TRACKER;
struct me_softc *sc;
struct ip *ip;
int ret;
sc = (struct me_softc *)arg;
if ((ME2IFP(sc)->if_flags & IFF_UP) == 0)
return (0);
M_ASSERTPKTHDR(m);
if (m->m_pkthdr.len < sizeof(struct ip) + sizeof(struct mobhdr) -
sizeof(struct in_addr))
return (0);
ret = 0;
ME_RLOCK(sc);
if (ME_READY(sc)) {
ip = mtod(m, struct ip *);
if (sc->me_src.s_addr == ip->ip_dst.s_addr &&
sc->me_dst.s_addr == ip->ip_src.s_addr)
ret = 32 * 2;
}
ME_RUNLOCK(sc);
return (ret);
}
static int
me_set_tunnel(struct ifnet *ifp, struct sockaddr_in *src,
struct sockaddr_in *dst)
{
struct me_softc *sc, *tsc;
sx_assert(&me_ioctl_sx, SA_XLOCKED);
ME_LIST_LOCK();
sc = ifp->if_softc;
LIST_FOREACH(tsc, &V_me_softc_list, me_list) {
if (tsc == sc || !ME_READY(tsc))
continue;
if (tsc->me_src.s_addr == src->sin_addr.s_addr &&
tsc->me_dst.s_addr == dst->sin_addr.s_addr) {
ME_LIST_UNLOCK();
return (EADDRNOTAVAIL);
}
}
ME_LIST_UNLOCK();
ME_WLOCK(sc);
sc->me_dst = dst->sin_addr;
sc->me_src = src->sin_addr;
ME_WUNLOCK(sc);
if (sc->me_ecookie == NULL)
sc->me_ecookie = encap_attach_func(AF_INET, IPPROTO_MOBILE,
me_encapcheck, &in_mobile_protosw, sc);
if (sc->me_ecookie != NULL)
ifp->if_drv_flags |= IFF_DRV_RUNNING;
return (0);
}
static void
me_delete_tunnel(struct ifnet *ifp)
{
struct me_softc *sc = ifp->if_softc;
sx_assert(&me_ioctl_sx, SA_XLOCKED);
if (sc->me_ecookie != NULL)
encap_detach(sc->me_ecookie);
sc->me_ecookie = NULL;
ME_WLOCK(sc);
sc->me_src.s_addr = 0;
sc->me_dst.s_addr = 0;
ME_WUNLOCK(sc);
ifp->if_drv_flags &= ~IFF_DRV_RUNNING;
}
static uint16_t
me_in_cksum(uint16_t *p, int nwords)
{
uint32_t sum = 0;
while (nwords-- > 0)
sum += *p++;
sum = (sum >> 16) + (sum & 0xffff);
sum += (sum >> 16);
return (~sum);
}
int
me_input(struct mbuf **mp, int *offp, int proto)
{
struct me_softc *sc;
struct mobhdr *mh;
struct ifnet *ifp;
struct mbuf *m;
struct ip *ip;
int hlen;
m = *mp;
sc = encap_getarg(m);
KASSERT(sc != NULL, ("encap_getarg returned NULL"));
ifp = ME2IFP(sc);
/* checks for short packets */
hlen = sizeof(struct mobhdr);
if (m->m_pkthdr.len < sizeof(struct ip) + hlen)
hlen -= sizeof(struct in_addr);
if (m->m_len < sizeof(struct ip) + hlen)
m = m_pullup(m, sizeof(struct ip) + hlen);
if (m == NULL)
goto drop;
mh = (struct mobhdr *)mtodo(m, sizeof(struct ip));
/* check for wrong flags */
if (mh->mob_flags & (~MOB_FLAGS_SP)) {
m_freem(m);
goto drop;
}
if (mh->mob_flags) {
if (hlen != sizeof(struct mobhdr)) {
m_freem(m);
goto drop;
}
} else
hlen = sizeof(struct mobhdr) - sizeof(struct in_addr);
/* check mobile header checksum */
if (me_in_cksum((uint16_t *)mh, hlen / sizeof(uint16_t)) != 0) {
m_freem(m);
goto drop;
}
#ifdef MAC
mac_ifnet_create_mbuf(ifp, m);
#endif
ip = mtod(m, struct ip *);
ip->ip_dst = mh->mob_dst;
ip->ip_p = mh->mob_proto;
ip->ip_sum = 0;
ip->ip_len = htons(m->m_pkthdr.len - hlen);
if (mh->mob_flags)
ip->ip_src = mh->mob_src;
memmove(mtodo(m, hlen), ip, sizeof(struct ip));
m_adj(m, hlen);
m_clrprotoflags(m);
m->m_pkthdr.rcvif = ifp;
m->m_pkthdr.csum_flags |= (CSUM_IP_CHECKED | CSUM_IP_VALID);
M_SETFIB(m, sc->me_fibnum);
hlen = AF_INET;
BPF_MTAP2(ifp, &hlen, sizeof(hlen), m);
if_inc_counter(ifp, IFCOUNTER_IPACKETS, 1);
if_inc_counter(ifp, IFCOUNTER_IBYTES, m->m_pkthdr.len);
if ((ifp->if_flags & IFF_MONITOR) != 0)
m_freem(m);
else
netisr_dispatch(NETISR_IP, m);
return (IPPROTO_DONE);
drop:
if_inc_counter(ifp, IFCOUNTER_IERRORS, 1);
return (IPPROTO_DONE);
}
#define MTAG_ME 1414491977
static int
me_check_nesting(struct ifnet *ifp, struct mbuf *m)
{
struct m_tag *mtag;
int count;
count = 1;
mtag = NULL;
while ((mtag = m_tag_locate(m, MTAG_ME, 0, NULL)) != NULL) {
if (*(struct ifnet **)(mtag + 1) == ifp) {
log(LOG_NOTICE, "%s: loop detected\n", ifp->if_xname);
return (EIO);
}
count++;
}
if (count > V_max_me_nesting) {
log(LOG_NOTICE,
"%s: if_output recursively called too many times(%d)\n",
ifp->if_xname, count);
return (EIO);
}
mtag = m_tag_alloc(MTAG_ME, 0, sizeof(struct ifnet *), M_NOWAIT);
if (mtag == NULL)
return (ENOMEM);
*(struct ifnet **)(mtag + 1) = ifp;
m_tag_prepend(m, mtag);
return (0);
}
static int
me_output(struct ifnet *ifp, struct mbuf *m, const struct sockaddr *dst,
struct route *ro)
{
uint32_t af;
int error;
#ifdef MAC
error = mac_ifnet_check_transmit(ifp, m);
if (error != 0)
goto drop;
#endif
if ((ifp->if_flags & IFF_MONITOR) != 0 ||
(ifp->if_flags & IFF_UP) == 0) {
error = ENETDOWN;
goto drop;
}
error = me_check_nesting(ifp, m);
if (error != 0)
goto drop;
m->m_flags &= ~(M_BCAST|M_MCAST);
if (dst->sa_family == AF_UNSPEC)
bcopy(dst->sa_data, &af, sizeof(af));
else
af = dst->sa_family;
if (af != AF_INET) {
error = EAFNOSUPPORT;
goto drop;
}
BPF_MTAP2(ifp, &af, sizeof(af), m);
return (ifp->if_transmit(ifp, m));
drop:
m_freem(m);
if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
return (error);
}
static int
me_transmit(struct ifnet *ifp, struct mbuf *m)
{
ME_RLOCK_TRACKER;
struct mobhdr mh;
struct me_softc *sc;
struct ip *ip;
int error, hlen, plen;
sc = ifp->if_softc;
if (sc == NULL) {
error = ENETDOWN;
m_freem(m);
goto drop;
}
if (m->m_len < sizeof(struct ip))
m = m_pullup(m, sizeof(struct ip));
if (m == NULL) {
error = ENOBUFS;
goto drop;
}
ip = mtod(m, struct ip *);
/* Fragmented datagramms shouldn't be encapsulated */
if (ip->ip_off & htons(IP_MF | IP_OFFMASK)) {
error = EINVAL;
m_freem(m);
goto drop;
}
mh.mob_proto = ip->ip_p;
mh.mob_src = ip->ip_src;
mh.mob_dst = ip->ip_dst;
ME_RLOCK(sc);
if (!ME_READY(sc)) {
ME_RUNLOCK(sc);
error = ENETDOWN;
m_freem(m);
goto drop;
}
if (in_hosteq(sc->me_src, ip->ip_src)) {
hlen = sizeof(struct mobhdr) - sizeof(struct in_addr);
mh.mob_flags = 0;
} else {
hlen = sizeof(struct mobhdr);
mh.mob_flags = MOB_FLAGS_SP;
}
plen = m->m_pkthdr.len;
ip->ip_src = sc->me_src;
ip->ip_dst = sc->me_dst;
M_SETFIB(m, sc->me_fibnum);
ME_RUNLOCK(sc);
M_PREPEND(m, hlen, M_NOWAIT);
if (m == NULL) {
error = ENOBUFS;
goto drop;
}
if (m->m_len < sizeof(struct ip) + hlen)
m = m_pullup(m, sizeof(struct ip) + hlen);
if (m == NULL) {
error = ENOBUFS;
goto drop;
}
memmove(mtod(m, void *), mtodo(m, hlen), sizeof(struct ip));
ip = mtod(m, struct ip *);
ip->ip_len = htons(m->m_pkthdr.len);
ip->ip_p = IPPROTO_MOBILE;
ip->ip_sum = 0;
mh.mob_csum = 0;
mh.mob_csum = me_in_cksum((uint16_t *)&mh, hlen / sizeof(uint16_t));
bcopy(&mh, mtodo(m, sizeof(struct ip)), hlen);
error = ip_output(m, NULL, NULL, IP_FORWARDING, NULL, NULL);
drop:
if (error)
if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
else {
if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1);
if_inc_counter(ifp, IFCOUNTER_OBYTES, plen);
}
return (error);
}
static void
me_qflush(struct ifnet *ifp __unused)
{
}
static int
memodevent(module_t mod, int type, void *data)
{
switch (type) {
case MOD_LOAD:
case MOD_UNLOAD:
break;
default:
return (EOPNOTSUPP);
}
return (0);
}
static moduledata_t me_mod = {
"if_me",
memodevent,
0
};
DECLARE_MODULE(if_me, me_mod, SI_SUB_PSEUDO, SI_ORDER_ANY);
MODULE_VERSION(if_me, 1);

View File

@ -1,7 +1,6 @@
/* $NetBSD: ip_gre.c,v 1.29 2003/09/05 23:02:43 itojun Exp $ */
/*-
* Copyright (c) 1998 The NetBSD Foundation, Inc.
* Copyright (c) 2014 Andrey V. Elsukov <ae@FreeBSD.org>
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
@ -29,12 +28,8 @@
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/*
* deencapsulate tunneled packets and send them on
* output half is in net/if_gre.[ch]
* This currently handles IPPROTO_GRE, IPPROTO_MOBILE
*
* $NetBSD: ip_gre.c,v 1.29 2003/09/05 23:02:43 itojun Exp $
*/
#include <sys/cdefs.h>
@ -52,281 +47,126 @@ __FBSDID("$FreeBSD$");
#include <sys/errno.h>
#include <sys/time.h>
#include <sys/kernel.h>
#include <sys/syslog.h>
#include <net/bpf.h>
#include <sys/lock.h>
#include <sys/rmlock.h>
#include <sys/sysctl.h>
#include <net/ethernet.h>
#include <net/if.h>
#include <net/if_var.h>
#include <net/netisr.h>
#include <net/route.h>
#include <net/raw_cb.h>
#include <net/vnet.h>
#ifdef INET
#include <netinet/in.h>
#include <netinet/in_var.h>
#include <netinet/in_systm.h>
#include <netinet/ip.h>
#include <netinet/ip_encap.h>
#include <netinet/ip_var.h>
#include <netinet/ip_gre.h>
#include <machine/in_cksum.h>
#else
#error "ip_gre requires INET"
#ifdef INET6
#include <netinet/ip6.h>
#endif
/* Needs IP headers. */
#include <net/if_gre.h>
#include <machine/stdarg.h>
extern struct domain inetdomain;
extern int gre_input(struct mbuf **, int *, int);
#if 1
void gre_inet_ntoa(struct in_addr in); /* XXX */
#endif
int in_gre_attach(struct gre_softc *);
int in_gre_output(struct mbuf *, int, int);
static struct gre_softc *gre_lookup(struct mbuf *, u_int8_t);
static const struct protosw in_gre_protosw = {
.pr_type = SOCK_RAW,
.pr_domain = &inetdomain,
.pr_protocol = IPPROTO_GRE,
.pr_flags = PR_ATOMIC|PR_ADDR,
.pr_input = gre_input,
.pr_output = rip_output,
.pr_ctlinput = rip_ctlinput,
.pr_ctloutput = rip_ctloutput,
.pr_usrreqs = &rip_usrreqs
};
static struct mbuf *gre_input2(struct mbuf *, int, u_char);
#define GRE_TTL 30
VNET_DEFINE(int, ip_gre_ttl) = GRE_TTL;
#define V_ip_gre_ttl VNET(ip_gre_ttl)
SYSCTL_INT(_net_inet_ip, OID_AUTO, grettl, CTLFLAG_VNET | CTLFLAG_RW,
&VNET_NAME(ip_gre_ttl), 0, "");
/*
* De-encapsulate a packet and feed it back through ip input (this
* routine is called whenever IP gets a packet with proto type
* IPPROTO_GRE and a local destination address).
* This really is simple
*/
int
gre_input(struct mbuf **mp, int *offp, int proto)
static int
in_gre_encapcheck(const struct mbuf *m, int off, int proto, void *arg)
{
struct mbuf *m;
int off;
m = *mp;
off = *offp;
*mp = NULL;
m = gre_input2(m, off, proto);
/*
* If no matching tunnel that is up is found. We inject
* the mbuf to raw ip socket to see if anyone picks it up.
*/
if (m != NULL) {
*mp = m;
rip_input(mp, offp, proto);
}
return (IPPROTO_DONE);
}
/*
* Decapsulate. Does the real work and is called from gre_input()
* (above). Returns an mbuf back if packet is not yet processed,
* and NULL if it needs no further processing. proto is the protocol
* number of the "calling" foo_input() routine.
*/
static struct mbuf *
gre_input2(struct mbuf *m ,int hlen, u_char proto)
{
struct greip *gip;
int isr;
GRE_RLOCK_TRACKER;
struct gre_softc *sc;
u_int16_t flags;
u_int32_t af;
if ((sc = gre_lookup(m, proto)) == NULL) {
/* No matching tunnel or tunnel is down. */
return (m);
}
if (m->m_len < sizeof(*gip)) {
m = m_pullup(m, sizeof(*gip));
if (m == NULL)
return (NULL);
}
gip = mtod(m, struct greip *);
if_inc_counter(GRE2IFP(sc), IFCOUNTER_IPACKETS, 1);
if_inc_counter(GRE2IFP(sc), IFCOUNTER_IBYTES, m->m_pkthdr.len);
switch (proto) {
case IPPROTO_GRE:
hlen += sizeof(struct gre_h);
/* process GRE flags as packet can be of variable len */
flags = ntohs(gip->gi_flags);
/* Checksum & Offset are present */
if ((flags & GRE_CP) | (flags & GRE_RP))
hlen += 4;
/* We don't support routing fields (variable length) */
if (flags & GRE_RP)
return (m);
if (flags & GRE_KP)
hlen += 4;
if (flags & GRE_SP)
hlen += 4;
switch (ntohs(gip->gi_ptype)) { /* ethertypes */
case WCCP_PROTOCOL_TYPE:
if (sc->wccp_ver == WCCP_V2)
hlen += 4;
/* FALLTHROUGH */
case ETHERTYPE_IP: /* shouldn't need a schednetisr(), */
isr = NETISR_IP;/* as we are in ip_input */
af = AF_INET;
break;
#ifdef INET6
case ETHERTYPE_IPV6:
isr = NETISR_IPV6;
af = AF_INET6;
break;
#endif
default:
/* Others not yet supported. */
return (m);
}
break;
default:
/* Others not yet supported. */
return (m);
}
if (hlen > m->m_pkthdr.len) {
m_freem(m);
return (NULL);
}
/* Unlike NetBSD, in FreeBSD m_adj() adjusts m->m_pkthdr.len as well */
m_adj(m, hlen);
if (bpf_peers_present(GRE2IFP(sc)->if_bpf)) {
bpf_mtap2(GRE2IFP(sc)->if_bpf, &af, sizeof(af), m);
}
if ((GRE2IFP(sc)->if_flags & IFF_MONITOR) != 0) {
m_freem(m);
return(NULL);
}
m->m_pkthdr.rcvif = GRE2IFP(sc);
m_clrprotoflags(m);
netisr_queue(isr, m);
/* Packet is done, no further processing needed. */
return (NULL);
}
/*
* input routine for IPPRPOTO_MOBILE
* This is a little bit diffrent from the other modes, as the
* encapsulating header was not prepended, but instead inserted
* between IP header and payload
*/
int
gre_mobile_input(struct mbuf **mp, int *offp, int proto)
{
struct ip *ip;
struct mobip_h *mip;
struct mbuf *m;
struct gre_softc *sc;
int msiz;
m = *mp;
if ((sc = gre_lookup(m, IPPROTO_MOBILE)) == NULL) {
/* No matching tunnel or tunnel is down. */
m_freem(m);
return (IPPROTO_DONE);
}
if (m->m_len < sizeof(*mip)) {
m = m_pullup(m, sizeof(*mip));
if (m == NULL)
return (IPPROTO_DONE);
}
ip = mtod(m, struct ip *);
mip = mtod(m, struct mobip_h *);
if_inc_counter(GRE2IFP(sc), IFCOUNTER_IPACKETS, 1);
if_inc_counter(GRE2IFP(sc), IFCOUNTER_IBYTES, m->m_pkthdr.len);
if (ntohs(mip->mh.proto) & MOB_H_SBIT) {
msiz = MOB_H_SIZ_L;
mip->mi.ip_src.s_addr = mip->mh.osrc;
} else
msiz = MOB_H_SIZ_S;
if (m->m_len < (ip->ip_hl << 2) + msiz) {
m = m_pullup(m, (ip->ip_hl << 2) + msiz);
if (m == NULL)
return (IPPROTO_DONE);
ip = mtod(m, struct ip *);
mip = mtod(m, struct mobip_h *);
}
mip->mi.ip_dst.s_addr = mip->mh.odst;
mip->mi.ip_p = (ntohs(mip->mh.proto) >> 8);
if (gre_in_cksum((u_int16_t *)&mip->mh, msiz) != 0) {
m_freem(m);
return (IPPROTO_DONE);
}
bcopy((caddr_t)(ip) + (ip->ip_hl << 2) + msiz, (caddr_t)(ip) +
(ip->ip_hl << 2), m->m_len - msiz - (ip->ip_hl << 2));
m->m_len -= msiz;
m->m_pkthdr.len -= msiz;
sc = (struct gre_softc *)arg;
if ((GRE2IFP(sc)->if_flags & IFF_UP) == 0)
return (0);
M_ASSERTPKTHDR(m);
/*
* On FreeBSD, rip_input() supplies us with ip->ip_len
* decreased by the lengh of IP header, however, ip_input()
* expects it to be full size of IP packet, so adjust accordingly.
* We expect that payload contains at least IPv4
* or IPv6 packet.
*/
ip->ip_len = htons(ntohs(ip->ip_len) + sizeof(struct ip) - msiz);
if (m->m_pkthdr.len < sizeof(struct greip) + sizeof(struct ip))
return (0);
ip->ip_sum = 0;
ip->ip_sum = in_cksum(m, (ip->ip_hl << 2));
GRE_RLOCK(sc);
if (sc->gre_family == 0)
goto bad;
if (bpf_peers_present(GRE2IFP(sc)->if_bpf)) {
u_int32_t af = AF_INET;
bpf_mtap2(GRE2IFP(sc)->if_bpf, &af, sizeof(af), m);
}
KASSERT(sc->gre_family == AF_INET,
("wrong gre_family: %d", sc->gre_family));
if ((GRE2IFP(sc)->if_flags & IFF_MONITOR) != 0) {
m_freem(m);
return (IPPROTO_DONE);
}
ip = mtod(m, struct ip *);
if (sc->gre_oip.ip_src.s_addr != ip->ip_dst.s_addr ||
sc->gre_oip.ip_dst.s_addr != ip->ip_src.s_addr)
goto bad;
m->m_pkthdr.rcvif = GRE2IFP(sc);
netisr_queue(NETISR_IP, m);
return (IPPROTO_DONE);
GRE_RUNLOCK(sc);
return (32 * 2);
bad:
GRE_RUNLOCK(sc);
return (0);
}
/*
* Find the gre interface associated with our src/dst/proto set.
*
* XXXRW: Need some sort of drain/refcount mechanism so that the softc
* reference remains valid after it's returned from gre_lookup(). Right
* now, I'm thinking it should be reference-counted with a gre_dropref()
* when the caller is done with the softc. This is complicated by how
* to handle destroying the gre softc; probably using a gre_drain() in
* in_gre.c during destroy.
*/
static struct gre_softc *
gre_lookup(struct mbuf *m, u_int8_t proto)
int
in_gre_output(struct mbuf *m, int af, int hlen)
{
struct ip *ip = mtod(m, struct ip *);
struct gre_softc *sc;
struct greip *gi;
GRE_LIST_LOCK();
for (sc = LIST_FIRST(&V_gre_softc_list); sc != NULL;
sc = LIST_NEXT(sc, sc_list)) {
if ((sc->g_dst.s_addr == ip->ip_src.s_addr) &&
(sc->g_src.s_addr == ip->ip_dst.s_addr) &&
(sc->g_proto == proto) &&
((GRE2IFP(sc)->if_flags & IFF_UP) != 0)) {
GRE_LIST_UNLOCK();
return (sc);
}
gi = mtod(m, struct greip *);
switch (af) {
case AF_INET:
/*
* gre_transmit() has used M_PREPEND() that doesn't guarantee
* m_data is contiguous more than hlen bytes. Use m_copydata()
* here to avoid m_pullup().
*/
m_copydata(m, hlen + offsetof(struct ip, ip_tos),
sizeof(u_char), &gi->gi_ip.ip_tos);
m_copydata(m, hlen + offsetof(struct ip, ip_id),
sizeof(u_short), (caddr_t)&gi->gi_ip.ip_id);
break;
#ifdef INET6
case AF_INET6:
gi->gi_ip.ip_tos = 0; /* XXX */
gi->gi_ip.ip_id = ip_newid();
break;
#endif
}
GRE_LIST_UNLOCK();
return (NULL);
gi->gi_ip.ip_ttl = V_ip_gre_ttl;
gi->gi_ip.ip_len = htons(m->m_pkthdr.len);
return (ip_output(m, NULL, NULL, IP_FORWARDING, NULL, NULL));
}
int
in_gre_attach(struct gre_softc *sc)
{
KASSERT(sc->gre_ecookie == NULL, ("gre_ecookie isn't NULL"));
sc->gre_ecookie = encap_attach_func(AF_INET, IPPROTO_GRE,
in_gre_encapcheck, &in_gre_protosw, sc);
if (sc->gre_ecookie == NULL)
return (EEXIST);
return (0);
}

View File

@ -1,36 +0,0 @@
/* $NetBSD: ip_gre.h,v 1.5 2002/06/09 16:33:40 itojun Exp $ */
/* $FreeBSD$ */
/*-
* Copyright (c) 1998 The NetBSD Foundation, Inc.
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
* by Heiko W.Rupp <hwr@pilhuhn.de>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#ifdef _KERNEL
int gre_input(struct mbuf **, int *, int);
int gre_mobile_input(struct mbuf **, int *, int);
#endif /* _KERNEL */

View File

@ -329,6 +329,17 @@ struct protosw inet6sw[] = {
.pr_init = encap_init,
.pr_usrreqs = &rip6_usrreqs
},
{
.pr_type = SOCK_RAW,
.pr_domain = &inet6domain,
.pr_protocol = IPPROTO_GRE,
.pr_flags = PR_ATOMIC|PR_ADDR|PR_LASTHDR,
.pr_input = encap6_input,
.pr_output = rip6_output,
.pr_ctloutput = rip6_ctloutput,
.pr_init = encap_init,
.pr_usrreqs = &rip6_usrreqs
},
{
.pr_type = SOCK_RAW,
.pr_domain = &inet6domain,

151
sys/netinet6/ip6_gre.c Normal file
View File

@ -0,0 +1,151 @@
/*-
* Copyright (c) 2014 Andrey V. Elsukov <ae@FreeBSD.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include "opt_inet.h"
#include "opt_inet6.h"
#include <sys/param.h>
#include <sys/lock.h>
#include <sys/rmlock.h>
#include <sys/systm.h>
#include <sys/socket.h>
#include <sys/sockio.h>
#include <sys/mbuf.h>
#include <sys/errno.h>
#include <sys/kernel.h>
#include <sys/queue.h>
#include <sys/syslog.h>
#include <sys/sysctl.h>
#include <sys/protosw.h>
#include <sys/malloc.h>
#include <net/if.h>
#include <net/if_var.h>
#include <net/vnet.h>
#include <netinet/in.h>
#include <netinet/in_systm.h>
#ifdef INET
#include <net/ethernet.h>
#include <netinet/ip.h>
#endif
#include <netinet/ip_encap.h>
#include <netinet/ip6.h>
#include <netinet6/ip6_var.h>
#include <netinet6/in6_var.h>
#include <net/if_gre.h>
extern struct domain inet6domain;
extern int gre_input(struct mbuf **, int *, int);
int in6_gre_attach(struct gre_softc *);
int in6_gre_output(struct mbuf *, int, int);
struct protosw in6_gre_protosw = {
.pr_type = SOCK_RAW,
.pr_domain = &inet6domain,
.pr_protocol = IPPROTO_GRE,
.pr_flags = PR_ATOMIC|PR_ADDR,
.pr_input = gre_input,
.pr_output = rip6_output,
.pr_ctloutput = rip6_ctloutput,
.pr_usrreqs = &rip6_usrreqs
};
VNET_DEFINE(int, ip6_gre_hlim) = IPV6_DEFHLIM;
#define V_ip6_gre_hlim VNET(ip6_gre_hlim)
SYSCTL_DECL(_net_inet6_ip6);
SYSCTL_INT(_net_inet6_ip6, OID_AUTO, grehlim, CTLFLAG_VNET | CTLFLAG_RW,
&VNET_NAME(ip6_gre_hlim), 0, "Default hop limit for encapsulated packets");
static int
in6_gre_encapcheck(const struct mbuf *m, int off, int proto, void *arg)
{
GRE_RLOCK_TRACKER;
struct gre_softc *sc;
struct ip6_hdr *ip6;
sc = (struct gre_softc *)arg;
if ((GRE2IFP(sc)->if_flags & IFF_UP) == 0)
return (0);
M_ASSERTPKTHDR(m);
/*
* We expect that payload contains at least IPv4
* or IPv6 packet.
*/
if (m->m_pkthdr.len < sizeof(struct greip6) +
#ifdef INET
sizeof(struct ip))
#else
sizeof(struct ip6_hdr))
#endif
return (0);
GRE_RLOCK(sc);
if (sc->gre_family == 0)
goto bad;
KASSERT(sc->gre_family == AF_INET6,
("wrong gre_family: %d", sc->gre_family));
ip6 = mtod(m, struct ip6_hdr *);
if (!IN6_ARE_ADDR_EQUAL(&sc->gre_oip6.ip6_src, &ip6->ip6_dst) ||
!IN6_ARE_ADDR_EQUAL(&sc->gre_oip6.ip6_dst, &ip6->ip6_src))
goto bad;
GRE_RUNLOCK(sc);
return (128 * 2);
bad:
GRE_RUNLOCK(sc);
return (0);
}
int
in6_gre_output(struct mbuf *m, int af, int hlen)
{
struct greip6 *gi6;
gi6 = mtod(m, struct greip6 *);
gi6->gi6_ip6.ip6_hlim = V_ip6_gre_hlim;
return (ip6_output(m, NULL, NULL, IPV6_MINMTU, NULL, NULL, NULL));
}
int
in6_gre_attach(struct gre_softc *sc)
{
KASSERT(sc->gre_ecookie == NULL, ("gre_ecookie isn't NULL"));
sc->gre_ecookie = encap_attach_func(AF_INET6, IPPROTO_GRE,
in6_gre_encapcheck, &in6_gre_protosw, sc);
if (sc->gre_ecookie == NULL)
return (EEXIST);
return (0);
}