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:
parent
833e8dc5ab
commit
f325335caf
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/head/; revision=274246
@ -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",
|
||||
|
@ -252,6 +252,7 @@ MAN= aac.4 \
|
||||
malo.4 \
|
||||
mcd.4 \
|
||||
md.4 \
|
||||
me.4 \
|
||||
mem.4 \
|
||||
meteor.4 \
|
||||
mfi.4 \
|
||||
|
@ -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
85
share/man/man4/me.4
Normal 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
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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>
|
||||
|
8
sys/modules/if_me/Makefile
Normal file
8
sys/modules/if_me/Makefile
Normal file
@ -0,0 +1,8 @@
|
||||
# $FreeBSD$
|
||||
|
||||
.PATH: ${.CURDIR}/../../net
|
||||
|
||||
KMOD= if_me
|
||||
SRCS= if_me.c
|
||||
|
||||
.include <bsd.kmod.mk>
|
1419
sys/net/if_gre.c
1419
sys/net/if_gre.c
File diff suppressed because it is too large
Load Diff
212
sys/net/if_gre.h
212
sys/net/if_gre.h
@ -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)
|
||||
#define GREGOPTS _IOWR('i', 109, struct ifreq)
|
||||
#define GRESOPTS _IOW('i', 110, 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 GRE_ENABLE_CSUM 0x0001
|
||||
#define GRE_ENABLE_SEQ 0x0002
|
||||
#define GRE_OPTMASK (GRE_ENABLE_CSUM|GRE_ENABLE_SEQ)
|
||||
|
||||
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)
|
||||
|
||||
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
647
sys/net/if_me.c
Normal 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);
|
@ -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;
|
||||
struct ip *ip;
|
||||
|
||||
if ((sc = gre_lookup(m, proto)) == NULL) {
|
||||
/* No matching tunnel or tunnel is down. */
|
||||
return (m);
|
||||
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 greip) + sizeof(struct ip))
|
||||
return (0);
|
||||
|
||||
GRE_RLOCK(sc);
|
||||
if (sc->gre_family == 0)
|
||||
goto bad;
|
||||
|
||||
KASSERT(sc->gre_family == AF_INET,
|
||||
("wrong gre_family: %d", sc->gre_family));
|
||||
|
||||
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;
|
||||
|
||||
GRE_RUNLOCK(sc);
|
||||
return (32 * 2);
|
||||
bad:
|
||||
GRE_RUNLOCK(sc);
|
||||
return (0);
|
||||
}
|
||||
|
||||
if (m->m_len < sizeof(*gip)) {
|
||||
m = m_pullup(m, sizeof(*gip));
|
||||
if (m == NULL)
|
||||
return (NULL);
|
||||
}
|
||||
gip = mtod(m, struct greip *);
|
||||
int
|
||||
in_gre_output(struct mbuf *m, int af, int hlen)
|
||||
{
|
||||
struct greip *gi;
|
||||
|
||||
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;
|
||||
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 ETHERTYPE_IPV6:
|
||||
isr = NETISR_IPV6;
|
||||
af = AF_INET6;
|
||||
case AF_INET6:
|
||||
gi->gi_ip.ip_tos = 0; /* XXX */
|
||||
gi->gi_ip.ip_id = ip_newid();
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
/* Others not yet supported. */
|
||||
return (m);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
/* Others not yet supported. */
|
||||
return (m);
|
||||
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));
|
||||
}
|
||||
|
||||
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)
|
||||
in_gre_attach(struct gre_softc *sc)
|
||||
{
|
||||
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;
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
ip->ip_len = htons(ntohs(ip->ip_len) + sizeof(struct ip) - msiz);
|
||||
|
||||
ip->ip_sum = 0;
|
||||
ip->ip_sum = in_cksum(m, (ip->ip_hl << 2));
|
||||
|
||||
if (bpf_peers_present(GRE2IFP(sc)->if_bpf)) {
|
||||
u_int32_t af = AF_INET;
|
||||
bpf_mtap2(GRE2IFP(sc)->if_bpf, &af, sizeof(af), m);
|
||||
}
|
||||
|
||||
if ((GRE2IFP(sc)->if_flags & IFF_MONITOR) != 0) {
|
||||
m_freem(m);
|
||||
return (IPPROTO_DONE);
|
||||
}
|
||||
|
||||
m->m_pkthdr.rcvif = GRE2IFP(sc);
|
||||
|
||||
netisr_queue(NETISR_IP, m);
|
||||
return (IPPROTO_DONE);
|
||||
}
|
||||
|
||||
/*
|
||||
* 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)
|
||||
{
|
||||
struct ip *ip = mtod(m, struct ip *);
|
||||
struct gre_softc *sc;
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
GRE_LIST_UNLOCK();
|
||||
|
||||
return (NULL);
|
||||
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);
|
||||
}
|
||||
|
@ -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 */
|
@ -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
151
sys/netinet6/ip6_gre.c
Normal 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);
|
||||
}
|
Loading…
Reference in New Issue
Block a user