Merge projects/ipsec into head/.

Small summary
 -------------

o Almost all IPsec releated code was moved into sys/netipsec.
o New kernel modules added: ipsec.ko and tcpmd5.ko. New kernel
  option IPSEC_SUPPORT added. It enables support for loading
  and unloading of ipsec.ko and tcpmd5.ko kernel modules.
o IPSEC_NAT_T option was removed. Now NAT-T support is enabled by
  default. The UDP_ENCAP_ESPINUDP_NON_IKE encapsulation type
  support was removed. Added TCP/UDP checksum handling for
  inbound packets that were decapsulated by transport mode SAs.
  setkey(8) modified to show run-time NAT-T configuration of SA.
o New network pseudo interface if_ipsec(4) added. For now it is
  build as part of ipsec.ko module (or with IPSEC kernel).
  It implements IPsec virtual tunnels to create route-based VPNs.
o The network stack now invokes IPsec functions using special
  methods. The only one header file <netipsec/ipsec_support.h>
  should be included to declare all the needed things to work
  with IPsec.
o All IPsec protocols handlers (ESP/AH/IPCOMP protosw) were removed.
  Now these protocols are handled directly via IPsec methods.
o TCP_SIGNATURE support was reworked to be more close to RFC.
o PF_KEY SADB was reworked:
  - now all security associations stored in the single SPI namespace,
    and all SAs MUST have unique SPI.
  - several hash tables added to speed up lookups in SADB.
  - SADB now uses rmlock to protect access, and concurrent threads
    can do SA lookups in the same time.
  - many PF_KEY message handlers were reworked to reflect changes
    in SADB.
  - SADB_UPDATE message was extended to support new PF_KEY headers:
    SADB_X_EXT_NEW_ADDRESS_SRC and SADB_X_EXT_NEW_ADDRESS_DST. They
    can be used by IKE daemon to change SA addresses.
o ipsecrequest and secpolicy structures were cardinally changed to
  avoid locking protection for ipsecrequest. Now we support
  only limited number (4) of bundled SAs, but they are supported
  for both INET and INET6.
o INPCB security policy cache was introduced. Each PCB now caches
  used security policies to avoid SP lookup for each packet.
o For inbound security policies added the mode, when the kernel does
  check for full history of applied IPsec transforms.
o References counting rules for security policies and security
  associations were changed. The proper SA locking added into xform
  code.
o xform code was also changed. Now it is possible to unregister xforms.
  tdb_xxx structures were changed and renamed to reflect changes in
  SADB/SPDB, and changed rules for locking and refcounting.

Reviewed by:	gnn, wblock
Obtained from:	Yandex LLC
Relnotes:	yes
Sponsored by:	Yandex LLC
Differential Revision:	https://reviews.freebsd.org/D9352
This commit is contained in:
ae 2017-02-06 08:49:57 +00:00
parent 9eb72f4508
commit 0fb6ad528e
81 changed files with 9277 additions and 7621 deletions

View File

@ -131,7 +131,7 @@ ssize_t drainbuf(int, unsigned char *, size_t *);
ssize_t fillbuf(int, unsigned char *, size_t *);
#ifdef IPSEC
void add_ipsec_policy(int, char *);
void add_ipsec_policy(int, int, char *);
char *ipsec_policy[2];
#endif
@ -642,12 +642,6 @@ remote_connect(const char *host, const char *port, struct addrinfo hints)
if ((s = socket(res0->ai_family, res0->ai_socktype,
res0->ai_protocol)) < 0)
continue;
#ifdef IPSEC
if (ipsec_policy[0] != NULL)
add_ipsec_policy(s, ipsec_policy[0]);
if (ipsec_policy[1] != NULL)
add_ipsec_policy(s, ipsec_policy[1]);
#endif
if (rtableid >= 0 && (setsockopt(s, SOL_SOCKET, SO_SETFIB,
&rtableid, sizeof(rtableid)) == -1))
@ -765,12 +759,7 @@ local_listen(char *host, char *port, struct addrinfo hints)
ret = setsockopt(s, SOL_SOCKET, SO_REUSEPORT, &x, sizeof(x));
if (ret == -1)
err(1, NULL);
#ifdef IPSEC
if (ipsec_policy[0] != NULL)
add_ipsec_policy(s, ipsec_policy[0]);
if (ipsec_policy[1] != NULL)
add_ipsec_policy(s, ipsec_policy[1]);
#endif
if (FreeBSD_Oflag) {
if (setsockopt(s, IPPROTO_TCP, TCP_NOOPT,
&FreeBSD_Oflag, sizeof(FreeBSD_Oflag)) == -1)
@ -1235,6 +1224,12 @@ set_common_sockopts(int s, int af)
&FreeBSD_Oflag, sizeof(FreeBSD_Oflag)) == -1)
err(1, "disable TCP options");
}
#ifdef IPSEC
if (ipsec_policy[0] != NULL)
add_ipsec_policy(s, af, ipsec_policy[0]);
if (ipsec_policy[1] != NULL)
add_ipsec_policy(s, af, ipsec_policy[1]);
#endif
}
int
@ -1360,7 +1355,7 @@ help(void)
#ifdef IPSEC
void
add_ipsec_policy(int s, char *policy)
add_ipsec_policy(int s, int af, char *policy)
{
char *raw;
int e;
@ -1369,8 +1364,12 @@ add_ipsec_policy(int s, char *policy)
if (raw == NULL)
errx(1, "ipsec_set_policy `%s': %s", policy,
ipsec_strerror());
e = setsockopt(s, IPPROTO_IP, IP_IPSEC_POLICY, raw,
ipsec_get_policylen(raw));
if (af == AF_INET)
e = setsockopt(s, IPPROTO_IP, IP_IPSEC_POLICY, raw,
ipsec_get_policylen(raw));
if (af == AF_INET6)
e = setsockopt(s, IPPROTO_IPV6, IPV6_IPSEC_POLICY, raw,
ipsec_get_policylen(raw));
if (e < 0)
err(1, "ipsec policy cannot be configured");
free(raw);

View File

@ -1776,21 +1776,17 @@ pfkey_align(msg, mhp)
case SADB_EXT_SPIRANGE:
case SADB_X_EXT_POLICY:
case SADB_X_EXT_SA2:
case SADB_X_EXT_SA_REPLAY:
mhp[ext->sadb_ext_type] = (caddr_t)ext;
break;
case SADB_X_EXT_NAT_T_TYPE:
case SADB_X_EXT_NAT_T_SPORT:
case SADB_X_EXT_NAT_T_DPORT:
/* case SADB_X_EXT_NAT_T_OA: is OAI */
case SADB_X_EXT_NAT_T_OAI:
case SADB_X_EXT_NAT_T_OAR:
case SADB_X_EXT_NAT_T_FRAG:
if (feature_present("ipsec_natt")) {
mhp[ext->sadb_ext_type] = (caddr_t)ext;
break;
}
/* FALLTHROUGH */
case SADB_X_EXT_SA_REPLAY:
case SADB_X_EXT_NEW_ADDRESS_SRC:
case SADB_X_EXT_NEW_ADDRESS_DST:
mhp[ext->sadb_ext_type] = (caddr_t)ext;
break;
default:
__ipsec_errcode = EIPSEC_INVAL_EXTTYPE;
return -1;

View File

@ -220,6 +220,9 @@ pfkey_sadump(m)
struct sadb_ident *m_sid, *m_did;
struct sadb_sens *m_sens;
struct sadb_x_sa_replay *m_sa_replay;
struct sadb_x_nat_t_type *natt_type;
struct sadb_x_nat_t_port *natt_sport, *natt_dport;
struct sadb_address *natt_oai, *natt_oar;
/* check pfkey message. */
if (pfkey_align(m, mhp)) {
@ -245,33 +248,46 @@ pfkey_sadump(m)
m_did = (struct sadb_ident *)mhp[SADB_EXT_IDENTITY_DST];
m_sens = (struct sadb_sens *)mhp[SADB_EXT_SENSITIVITY];
m_sa_replay = (struct sadb_x_sa_replay *)mhp[SADB_X_EXT_SA_REPLAY];
natt_type = (struct sadb_x_nat_t_type *)mhp[SADB_X_EXT_NAT_T_TYPE];
natt_sport = (struct sadb_x_nat_t_port *)mhp[SADB_X_EXT_NAT_T_SPORT];
natt_dport = (struct sadb_x_nat_t_port *)mhp[SADB_X_EXT_NAT_T_DPORT];
natt_oai = (struct sadb_address *)mhp[SADB_X_EXT_NAT_T_OAI];
natt_oar = (struct sadb_address *)mhp[SADB_X_EXT_NAT_T_OAR];
/* source address */
if (m_saddr == NULL) {
printf("no ADDRESS_SRC extension.\n");
return;
}
printf("%s ", str_ipaddr((struct sockaddr *)(m_saddr + 1)));
printf("%s", str_ipaddr((struct sockaddr *)(m_saddr + 1)));
if (natt_type != NULL && natt_sport != NULL)
printf("[%u]", ntohs(natt_sport->sadb_x_nat_t_port_port));
/* destination address */
if (m_daddr == NULL) {
printf("no ADDRESS_DST extension.\n");
printf("\nno ADDRESS_DST extension.\n");
return;
}
printf("%s ", str_ipaddr((struct sockaddr *)(m_daddr + 1)));
printf(" %s", str_ipaddr((struct sockaddr *)(m_daddr + 1)));
if (natt_type != NULL && natt_dport != NULL)
printf("[%u]", ntohs(natt_dport->sadb_x_nat_t_port_port));
/* SA type */
if (m_sa == NULL) {
printf("no SA extension.\n");
printf("\nno SA extension.\n");
return;
}
if (m_sa2 == NULL) {
printf("no SA2 extension.\n");
printf("\nno SA2 extension.\n");
return;
}
printf("\n\t");
GETMSGSTR(str_satype, m->sadb_msg_satype);
if (m->sadb_msg_satype == SADB_SATYPE_ESP && natt_type != NULL)
printf("esp-udp ");
else
GETMSGSTR(str_satype, m->sadb_msg_satype);
printf("mode=");
GETMSGSTR(str_mode, m_sa2->sadb_x_sa2_mode);
@ -282,6 +298,18 @@ pfkey_sadump(m)
(u_int32_t)m_sa2->sadb_x_sa2_reqid,
(u_int32_t)m_sa2->sadb_x_sa2_reqid);
/* other NAT-T information */
if (natt_type != NULL && (natt_oai != NULL || natt_oar != NULL)) {
printf("\tNAT:");
if (natt_oai != NULL)
printf(" OAI=%s",
str_ipaddr((struct sockaddr *)(natt_oai + 1)));
if (natt_oar != NULL)
printf(" OAR=%s",
str_ipaddr((struct sockaddr *)(natt_oar + 1)));
printf("\n");
}
/* encryption key */
if (m->sadb_msg_satype == SADB_X_SATYPE_IPCOMP) {
printf("\tC: ");

View File

@ -34,6 +34,7 @@ SRCS+= ifvlan.c # SIOC[GS]ETVLAN support
SRCS+= ifvxlan.c # VXLAN support
SRCS+= ifgre.c # GRE keys etc
SRCS+= ifgif.c # GIF reversed header workaround
SRCS+= ifipsec.c # IPsec VTI
SRCS+= sfp.c # SFP/SFP+ information
LIBADD+= m

101
sbin/ifconfig/ifipsec.c Normal file
View File

@ -0,0 +1,101 @@
/*-
* Copyright (c) 2016 Yandex LLC
* Copyright (c) 2016 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/ioctl.h>
#include <sys/socket.h>
#include <sys/sockio.h>
#include <sys/stdint.h>
#include <stdlib.h>
#include <unistd.h>
#include <net/ethernet.h>
#include <net/if.h>
#include <net/if_ipsec.h>
#include <net/route.h>
#include <ctype.h>
#include <stdio.h>
#include <string.h>
#include <err.h>
#include <errno.h>
#include "ifconfig.h"
static void
ipsec_status(int s)
{
uint32_t reqid;
ifr.ifr_data = (caddr_t)&reqid;
if (ioctl(s, IPSECGREQID, &ifr) == -1)
return;
printf("\treqid: %u\n", reqid);
}
static
DECL_CMD_FUNC(setreqid, val, arg)
{
char *ep;
uint32_t v;
v = strtoul(val, &ep, 0);
if (*ep != '\0') {
warn("Invalid reqid value %s", val);
return;
}
ifr.ifr_data = (char *)&v;
if (ioctl(s, IPSECSREQID, &ifr) == -1) {
warn("ioctl(IPSECSREQID)");
return;
}
}
static struct cmd ipsec_cmds[] = {
DEF_CMD_ARG("reqid", setreqid),
};
static struct afswtch af_ipsec = {
.af_name = "af_ipsec",
.af_af = AF_UNSPEC,
.af_other_status = ipsec_status,
};
static __constructor void
ipsec_ctor(void)
{
size_t i;
for (i = 0; i < nitems(ipsec_cmds); i++)
cmd_register(&ipsec_cmds[i]);
af_register(&af_ipsec);
#undef N
}

View File

@ -29,7 +29,7 @@
.\"
.\" $FreeBSD$
.\"
.Dd October 3, 2016
.Dd February 6, 2017
.Dt SETKEY 8
.Os
.\"
@ -270,8 +270,6 @@ must be a decimal number, or a hexadecimal number with
prefix.
SPI values between 0 and 255 are reserved for future use by IANA
and they cannot be used.
TCP-MD5 associations must use 0x1000 and therefore only have per-host
granularity at this time.
.\"
.Pp
.It Ar extensions

View File

@ -201,6 +201,7 @@ MAN= aac.4 \
icmp.4 \
icmp6.4 \
ida.4 \
if_ipsec.4 \
ifmib.4 \
ig4.4 \
igb.4 \

141
share/man/man4/if_ipsec.4 Normal file
View File

@ -0,0 +1,141 @@
.\" Copyright (c) 2017 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 February 6, 2017
.Dt if_ipsec 4
.Os
.Sh NAME
.Nm if_ipsec
.Nd IPsec virtual tunneling interface
.Sh SYNOPSIS
The
.Cm if_ipsec
network interface is a part of the
.Fx
IPsec implementation.
To compile it into the kernel, place this line in the kernel
configuration file:
.Bd -ragged -offset indent
.Cd "options IPSEC"
.Ed
.Pp
It can also be loaded as part of the
.Cm ipsec
kernel module if the kernel was compiled with
.Bd -ragged -offset indent
.Cd "options IPSEC_SUPPORT"
.Ed
.Sh DESCRIPTION
The
.Nm
network interface is targeted for creating route-based VPNs.
It can tunnel IPv4 and IPv6 traffic over either IPv4 or IPv6 and secure
it with ESP.
.Pp
.Nm
interfaces are dynamically created and destroyed with the
.Xr ifconfig 8
.Cm create
and
.Cm destroy
subcommands.
The administrator must configure IPsec
.Cm tunnel
endpoint addresses.
These addresses will be used for the outer IP header of ESP packets.
The administrator can also configure the protocol and addresses for the inner
IP header with
.Xr ifconfig 8 ,
and modify the routing table to route the packets through the
.Nm
interface.
.Pp
When the
.Nm
interface is configured, it automatically creates special security policies.
These policies can be used to acquire security associations from the IKE daemon,
which are needed for establishing an IPsec tunnel.
It is also possible to create needed security associations manually with the
.Xr setkey 8
utility.
.Pp
Each
.Nm
interface has an additional numeric configuration option
.Cm reqid Ar id .
This
.Ar id
is used to distinguish traffic and security policies between several
.Nm
interfaces.
The
.Cm reqid
can be specified on interface creation and changed later.
If not specified, it is automatically assigned.
Note that changing
.Cm reqid
will lead to generation of new security policies, and this
may require creating new security associations.
.Sh EXAMPLES
The example below shows manual configuration of an IPsec tunnel
between two FreeBSD hosts.
Host A has the IP address 192.168.0.3, and host B has the IP address
192.168.0.5.
.Pp
On host A:
.Bd -literal -offset indent
ifconfig ipsec0 create reqid 100
ifconfig ipsec0 inet tunnel 192.168.0.3 192.168.0.5
ifconfig ipsec0 inet 172.16.0.3/16 172.16.0.5
setkey -c
add 192.168.0.3 192.168.0.5 esp 10000 -m tunnel -u 100 -E rijndael-cbc "VerySecureKey!!1";
add 192.168.0.5 192.168.0.3 esp 10001 -m tunnel -u 100 -E rijndael-cbc "VerySecureKey!!2";
^D
.Ed
.Pp
On host B:
.Bd -literal -offset indent
ifconfig ipsec0 create reqid 200
ifconfig ipsec0 inet tunnel 192.168.0.5 192.168.0.3
ifconfig ipsec0 inet 172.16.0.5/16 172.16.0.3
setkey -c
add 192.168.0.3 192.168.0.5 esp 10000 -m tunnel -u 200 -E rijndael-cbc "VerySecureKey!!1";
add 192.168.0.5 192.168.0.3 esp 10001 -m tunnel -u 200 -E rijndael-cbc "VerySecureKey!!2";
^D
.Ed
.Pp
Note the value 100 on host A and value 200 on host B are used as reqid.
The same value must be used as identifier of the policy entry in the
.Xr setkey 8
command.
.Sh SEE ALSO
.Xr gif 4 ,
.Xr gre 4 ,
.Xr ipsec 4 ,
.Xr ifconfig 8 ,
.Xr setkey 8
.Sh AUTHORS
.An Andrey V. Elsukov Aq Mt ae@FreeBSD.org

View File

@ -29,7 +29,7 @@
.\"
.\" $FreeBSD$
.\"
.Dd November 29, 2009
.Dd February 6, 2017
.Dt IPSEC 4
.Os
.Sh NAME
@ -37,6 +37,7 @@
.Nd Internet Protocol Security protocol
.Sh SYNOPSIS
.Cd "options IPSEC"
.Cd "options IPSEC_SUPPORT"
.Cd "device crypto"
.Pp
.In sys/types.h
@ -151,6 +152,16 @@ Refer to
.Xr setkey 8
on how to use it.
.Pp
Depending on the socket's address family, IPPROTO_IP or IPPROTO_IPV6
transport level and IP_IPSEC_POLICY or IPV6_IPSEC_POLICY socket options
may be used to configure per-socket security policies.
A properly-formed IPsec policy specification structure can be
created using
.Xr ipsec_set_policy 3
function and used as socket option value for the
.Xr setsockopt 2
call.
.Pp
When setting policies using the
.Xr setkey 8
command, the
@ -228,6 +239,8 @@ for tweaking the kernel's IPsec behavior:
.It "net.inet.ipsec.dfbit integer yes"
.It "net.inet.ipsec.ecn integer yes"
.It "net.inet.ipsec.debug integer yes"
.It "net.inet.ipsec.natt_cksum_policy integer yes"
.It "net.inet.ipsec.check_policy_history integer yes"
.It "net.inet6.ipsec6.ecn integer yes"
.It "net.inet6.ipsec6.debug integer yes"
.El
@ -270,6 +283,23 @@ talks more about the behavior.
.It Li ipsec.debug
If set to non-zero, debug messages will be generated via
.Xr syslog 3 .
.It Li ipsec.natt_cksum_policy
Controls how the kernel handles TCP and UDP checksums when ESP in UDP
encapsulation is used for IPsec transport mode.
If set to a non-zero value, the kernel fully recomputes checksums for
inbound TCP segments and UDP datagrams after they are decapsulated and
decrypted.
If set to 0 and original addresses were configured for corresponding SA
by the IKE daemon, the kernel incrementally recomputes checksums for
inbound TCP segments and UDP datagrams.
If addresses were not configured, the checksums are ignored.
.It Li ipsec.check_policy_history
Enables strict policy checking for inbound packets.
By default, inbound security policies check that packets handled by IPsec
have been decrypted and authenticated.
If this variable is set to a non-zero value, each packet handled by IPsec
is checked against the history of IPsec security associations.
The IPsec security protocol, mode, and SA addresses must match.
.El
.Pp
Variables under the
@ -305,6 +335,7 @@ routines from looking into the IP payload.
.Xr ipsec_set_policy 3 ,
.Xr crypto 4 ,
.Xr enc 4 ,
.Xr if_ipsec 4 ,
.Xr icmp6 4 ,
.Xr intro 4 ,
.Xr ip6 4 ,

View File

@ -34,7 +34,7 @@
.\" From: @(#)tcp.4 8.1 (Berkeley) 6/5/93
.\" $FreeBSD$
.\"
.Dd Jan 29, 2017
.Dd February 6, 2017
.Dt TCP 4
.Os
.Sh NAME
@ -272,33 +272,27 @@ or the internal send buffer is filled.
This option enables the use of MD5 digests (also known as TCP-MD5)
on writes to the specified socket.
Outgoing traffic is digested;
digests on incoming traffic are verified if the
.Va net.inet.tcp.signature_verify_input
sysctl is nonzero.
The current default behavior for the system is to respond to a system
advertising this option with TCP-MD5; this may change.
digests on incoming traffic are verified.
When this option is enabled on a socket, all inbound and outgoing
TCP segments must be signed with MD5 digests.
.Pp
One common use for this in a
.Fx
router deployment is to enable
based routers to interwork with Cisco equipment at peering points.
Support for this feature conforms to RFC 2385.
Only IPv4
.Pq Dv AF_INET
sessions are supported.
.Pp
In order for this option to function correctly, it is necessary for the
administrator to add a tcp-md5 key entry to the system's security
associations database (SADB) using the
.Xr setkey 8
utility.
This entry must have an SPI of 0x1000 and can therefore only be specified
on a per-host basis at this time.
This entry can only be specified on a per-host basis at this time.
.Pp
If an SADB entry cannot be found for the destination, the outgoing traffic
will have an invalid digest option prepended, and the following error message
will be visible on the system console:
.Em "tcp_signature_compute: SADB lookup failed for %d.%d.%d.%d" .
If an SADB entry cannot be found for the destination,
the system does not send any outgoing segments and drops any inbound segments.
.Pp
Each dropped segment is taken into account in the TCP protocol statistics.
.El
.Pp
The option level for the

View File

@ -28,7 +28,7 @@
.\" @(#)udp.4 8.1 (Berkeley) 6/5/93
.\" $FreeBSD$
.\"
.Dd June 5, 1993
.Dd February 6, 2017
.Dt UDP 4
.Os
.Sh NAME
@ -99,6 +99,17 @@ transport level may be used with
.Tn UDP ;
see
.Xr ip 4 .
.Tn UDP_ENCAP
socket option may be used at the
.Tn IPPROTO_UDP
level to encapsulate
.Tn ESP
packets in
.Tn UDP .
Only one value is supported for this option:
.Tn UDP_ENCAP_ESPINUDP
from RFC 3948, defined in
.In netinet/udp.h .
.Sh MIB VARIABLES
The
.Nm
@ -158,7 +169,8 @@ exists.
.Xr blackhole 4 ,
.Xr inet 4 ,
.Xr intro 4 ,
.Xr ip 4
.Xr ip 4 ,
.Xr udplite 4
.Sh HISTORY
The
.Nm

View File

@ -627,12 +627,12 @@ options TCP_OFFLOAD # TCP offload support.
# In order to enable IPSEC you MUST also add device crypto to
# your kernel configuration
options IPSEC #IP security (requires device crypto)
# Option IPSEC_SUPPORT does not enable IPsec, but makes it possible to
# load it as a kernel module. You still MUST add device crypto to your kernel
# configuration.
options IPSEC_SUPPORT
#options IPSEC_DEBUG #debug for IP security
#
# Set IPSEC_NAT_T to enable NAT-Traversal support. This enables
# optional UDP encapsulation of ESP packets.
#
options IPSEC_NAT_T #NAT-T support, UDP encap of ESP
#
# SMB/CIFS requester
@ -1027,7 +1027,8 @@ options ACCEPT_FILTER_HTTP
# carried in TCP option 19. This option is commonly used to protect
# TCP sessions (e.g. BGP) where IPSEC is not available nor desirable.
# This is enabled on a per-socket basis using the TCP_MD5SIG socket option.
# This requires the use of 'device crypto' and 'options IPSEC'.
# This requires the use of 'device crypto' and either 'options IPSEC' or
# 'options IPSEC_SUPPORT'.
options TCP_SIGNATURE #include support for RFC 2385
# DUMMYNET enables the "dummynet" bandwidth limiter. You need IPFIREWALL

View File

@ -587,22 +587,24 @@ contrib/ngatm/netnatm/sig/sig_unimsgcpy.c optional ngatm_uni \
compile-with "${NORMAL_C} -I$S/contrib/ngatm"
contrib/ngatm/netnatm/sig/sig_verify.c optional ngatm_uni \
compile-with "${NORMAL_C} -I$S/contrib/ngatm"
crypto/blowfish/bf_ecb.c optional ipsec
crypto/blowfish/bf_skey.c optional crypto | ipsec
crypto/camellia/camellia.c optional crypto | ipsec
crypto/camellia/camellia-api.c optional crypto | ipsec
crypto/des/des_ecb.c optional crypto | ipsec | netsmb
crypto/des/des_setkey.c optional crypto | ipsec | netsmb
crypto/blowfish/bf_ecb.c optional ipsec | ipsec_support
crypto/blowfish/bf_skey.c optional crypto | ipsec | ipsec_support
crypto/camellia/camellia.c optional crypto | ipsec | ipsec_support
crypto/camellia/camellia-api.c optional crypto | ipsec | ipsec_support
crypto/des/des_ecb.c optional crypto | ipsec | ipsec_support | netsmb
crypto/des/des_setkey.c optional crypto | ipsec | ipsec_support | netsmb
crypto/rc4/rc4.c optional netgraph_mppc_encryption | kgssapi
crypto/rijndael/rijndael-alg-fst.c optional crypto | ekcd | geom_bde | \
ipsec | random !random_loadable | wlan_ccmp
ipsec | ipsec_support | random !random_loadable | wlan_ccmp
crypto/rijndael/rijndael-api-fst.c optional ekcd | geom_bde | random !random_loadable
crypto/rijndael/rijndael-api.c optional crypto | ipsec | wlan_ccmp
crypto/rijndael/rijndael-api.c optional crypto | ipsec | ipsec_support | \
wlan_ccmp
crypto/sha1.c optional carp | crypto | ipsec | \
netgraph_mppc_encryption | sctp
crypto/sha2/sha256c.c optional crypto | ekcd | geom_bde | ipsec | random !random_loadable | \
sctp | zfs
crypto/sha2/sha512c.c optional crypto | geom_bde | ipsec | zfs
ipsec_support | netgraph_mppc_encryption | sctp
crypto/sha2/sha256c.c optional crypto | ekcd | geom_bde | ipsec | \
ipsec_support | random !random_loadable | sctp | zfs
crypto/sha2/sha512c.c optional crypto | geom_bde | ipsec | \
ipsec_support | zfs
crypto/skein/skein.c optional crypto | zfs
crypto/skein/skein_block.c optional crypto | zfs
crypto/siphash/siphash.c optional inet | inet6
@ -3879,8 +3881,7 @@ libkern/strtouq.c standard
libkern/strvalid.c standard
libkern/timingsafe_bcmp.c standard
libkern/zlib.c optional crypto | geom_uzip | ipsec | \
mxge | netgraph_deflate | \
ddb_ctf | gzio
ipsec_support | mxge | netgraph_deflate | ddb_ctf | gzio
net/altq/altq_cbq.c optional altq
net/altq/altq_cdnr.c optional altq
net/altq/altq_codel.c optional altq
@ -3916,6 +3917,7 @@ 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 | gre inet6
net/if_ipsec.c optional inet ipsec | inet6 ipsec
net/if_iso88025subr.c optional token
net/if_lagg.c optional lagg
net/if_loop.c optional loop
@ -4104,7 +4106,6 @@ netinet/ip_encap.c optional inet | inet6
netinet/ip_fastfwd.c optional inet
netinet/ip_icmp.c optional inet | inet6
netinet/ip_input.c optional inet
netinet/ip_ipsec.c optional inet ipsec
netinet/ip_mroute.c optional mrouting inet
netinet/ip_options.c optional inet
netinet/ip_output.c optional inet
@ -4174,7 +4175,6 @@ netinet6/ip6_id.c optional inet6
netinet6/ip6_input.c optional inet6
netinet6/ip6_mroute.c optional mrouting inet6
netinet6/ip6_output.c optional inet6
netinet6/ip6_ipsec.c optional inet6 ipsec
netinet6/mld6.c optional inet6
netinet6/nd6.c optional inet6
netinet6/nd6_nbr.c optional inet6
@ -4187,15 +4187,25 @@ netinet6/udp6_usrreq.c optional inet6
netipsec/ipsec.c optional ipsec inet | ipsec inet6
netipsec/ipsec_input.c optional ipsec inet | ipsec inet6
netipsec/ipsec_mbuf.c optional ipsec inet | ipsec inet6
netipsec/ipsec_mod.c optional ipsec inet | ipsec inet6
netipsec/ipsec_output.c optional ipsec inet | ipsec inet6
netipsec/key.c optional ipsec inet | ipsec inet6
netipsec/key_debug.c optional ipsec inet | ipsec inet6
netipsec/keysock.c optional ipsec inet | ipsec inet6
netipsec/ipsec_pcb.c optional ipsec inet | ipsec inet6 | \
ipsec_support inet | ipsec_support inet6
netipsec/key.c optional ipsec inet | ipsec inet6 | \
ipsec_support inet | ipsec_support inet6
netipsec/key_debug.c optional ipsec inet | ipsec inet6 | \
ipsec_support inet | ipsec_support inet6
netipsec/keysock.c optional ipsec inet | ipsec inet6 | \
ipsec_support inet | ipsec_support inet6
netipsec/subr_ipsec.c optional ipsec inet | ipsec inet6 | \
ipsec_support inet | ipsec_support inet6
netipsec/udpencap.c optional ipsec inet
netipsec/xform_ah.c optional ipsec inet | ipsec inet6
netipsec/xform_esp.c optional ipsec inet | ipsec inet6
netipsec/xform_ipcomp.c optional ipsec inet | ipsec inet6
netipsec/xform_tcp.c optional ipsec inet tcp_signature | \
ipsec inet6 tcp_signature
ipsec inet6 tcp_signature | ipsec_support inet tcp_signature | \
ipsec_support inet6 tcp_signature
netnatm/natm.c optional natm
netnatm/natm_pcb.c optional natm
netnatm/natm_proto.c optional natm
@ -4547,18 +4557,18 @@ ofed/drivers/infiniband/hw/mthca/mthca_uar.c optional mthca \
compile-with "${OFED_C}"
# crypto support
opencrypto/cast.c optional crypto | ipsec
opencrypto/criov.c optional crypto | ipsec
opencrypto/crypto.c optional crypto | ipsec
opencrypto/cast.c optional crypto | ipsec | ipsec_support
opencrypto/criov.c optional crypto | ipsec | ipsec_support
opencrypto/crypto.c optional crypto | ipsec | ipsec_support
opencrypto/cryptodev.c optional cryptodev
opencrypto/cryptodev_if.m optional crypto | ipsec
opencrypto/cryptosoft.c optional crypto | ipsec
opencrypto/cryptodeflate.c optional crypto | ipsec
opencrypto/gmac.c optional crypto | ipsec
opencrypto/gfmult.c optional crypto | ipsec
opencrypto/rmd160.c optional crypto | ipsec
opencrypto/skipjack.c optional crypto | ipsec
opencrypto/xform.c optional crypto | ipsec
opencrypto/cryptodev_if.m optional crypto | ipsec | ipsec_support
opencrypto/cryptosoft.c optional crypto | ipsec | ipsec_support
opencrypto/cryptodeflate.c optional crypto | ipsec | ipsec_support
opencrypto/gmac.c optional crypto | ipsec | ipsec_support
opencrypto/gfmult.c optional crypto | ipsec | ipsec_support
opencrypto/rmd160.c optional crypto | ipsec | ipsec_support
opencrypto/skipjack.c optional crypto | ipsec | ipsec_support
opencrypto/xform.c optional crypto | ipsec | ipsec_support
rpc/auth_none.c optional krpc | nfslockd | nfscl | nfsd
rpc/auth_unix.c optional krpc | nfslockd | nfscl | nfsd
rpc/authunix_prot.c optional krpc | nfslockd | nfscl | nfsd

View File

@ -180,8 +180,9 @@ aesni_wrap.o optional aesni \
compile-with "${CC} -c ${CFLAGS:C/^-O2$/-O3/:N-nostdinc} ${WERROR} ${NO_WCAST_QUAL} ${PROF} -mmmx -msse -msse4 -maes ${.IMPSRC}" \
no-implicit-rule \
clean "aesni_wrap.o"
crypto/blowfish/bf_enc.c optional crypto | ipsec
crypto/des/des_enc.c optional crypto | ipsec | netsmb
crypto/blowfish/bf_enc.c optional crypto | ipsec | ipsec_support
crypto/des/des_enc.c optional crypto | ipsec | \
ipsec_support | netsmb
crypto/via/padlock.c optional padlock
crypto/via/padlock_cipher.c optional padlock
crypto/via/padlock_hash.c optional padlock

View File

@ -112,8 +112,8 @@ cddl/compat/opensolaris/kern/opensolaris_atomic.c optional zfs | dtrace compile-
cddl/dev/dtrace/arm/dtrace_asm.S optional dtrace compile-with "${DTRACE_S}"
cddl/dev/dtrace/arm/dtrace_subr.c optional dtrace compile-with "${DTRACE_C}"
cddl/dev/fbt/arm/fbt_isa.c optional dtrace_fbt | dtraceall compile-with "${FBT_C}"
crypto/blowfish/bf_enc.c optional crypto | ipsec
crypto/des/des_enc.c optional crypto | ipsec | netsmb
crypto/blowfish/bf_enc.c optional crypto | ipsec | ipsec_support
crypto/des/des_enc.c optional crypto | ipsec | ipsec_support | netsmb
dev/cpufreq/cpufreq_dt.c optional cpufreq fdt
dev/dwc/if_dwc.c optional dwc
dev/dwc/if_dwc_if.m optional dwc

View File

@ -142,8 +142,8 @@ armv8_crypto_wrap.o optional armv8crypto \
compile-with "${CC} -c ${CFLAGS:C/^-O2$/-O3/:N-nostdinc:N-mgeneral-regs-only} ${WERROR} ${NO_WCAST_QUAL} ${PROF} -march=armv8a+crypto ${.IMPSRC}" \
no-implicit-rule \
clean "armv8_crypto_wrap.o"
crypto/blowfish/bf_enc.c optional crypto | ipsec
crypto/des/des_enc.c optional crypto | ipsec | netsmb
crypto/blowfish/bf_enc.c optional crypto | ipsec | ipsec_support
crypto/des/des_enc.c optional crypto | ipsec | ipsec_support | netsmb
dev/acpica/acpi_if.m optional acpi
dev/ahci/ahci_generic.c optional ahci
dev/cpufreq/cpufreq_dt.c optional cpufreq fdt

View File

@ -143,7 +143,7 @@ compat/svr4/svr4_syscallnames.c optional compat_svr4
compat/svr4/svr4_sysent.c optional compat_svr4
compat/svr4/svr4_sysvec.c optional compat_svr4
compat/svr4/svr4_termios.c optional compat_svr4
bf_enc.o optional crypto | ipsec \
bf_enc.o optional crypto | ipsec | ipsec_support \
dependency "$S/crypto/blowfish/arch/i386/bf_enc.S $S/crypto/blowfish/arch/i386/bf_enc_586.S $S/crypto/blowfish/arch/i386/bf_enc_686.S" \
compile-with "${CC} -c -I$S/crypto/blowfish/arch/i386 ${ASM_CFLAGS} ${WERROR} ${.IMPSRC}" \
no-implicit-rule
@ -159,7 +159,7 @@ aesni_wrap.o optional aesni \
compile-with "${CC} -c ${CFLAGS:C/^-O2$/-O3/:N-nostdinc} ${WERROR} ${NO_WCAST_QUAL} ${PROF} -mmmx -msse -msse4 -maes ${.IMPSRC}" \
no-implicit-rule \
clean "aesni_wrap.o"
crypto/des/arch/i386/des_enc.S optional crypto | ipsec | netsmb
crypto/des/arch/i386/des_enc.S optional crypto | ipsec | ipsec_support | netsmb
crypto/via/padlock.c optional padlock
crypto/via/padlock_cipher.c optional padlock
crypto/via/padlock_hash.c optional padlock

View File

@ -83,8 +83,10 @@ mips/mips/sc_machdep.c optional sc
dev/uart/uart_cpu_fdt.c optional uart fdt
# crypto support -- use generic
crypto/blowfish/bf_enc.c optional crypto | ipsec
crypto/des/des_enc.c optional crypto | ipsec | netsmb
crypto/blowfish/bf_enc.c optional crypto | ipsec | \
ipsec_support
crypto/des/des_enc.c optional crypto | ipsec | \
ipsec_support | netsmb
# AP common nvram interface MIPS specific, but maybe should be more generic
dev/nvram2env/nvram2env_mips.c optional nvram2env

View File

@ -20,8 +20,8 @@ cddl/contrib/opensolaris/common/atomic/powerpc64/opensolaris_atomic.S optional z
cddl/dev/dtrace/powerpc/dtrace_asm.S optional dtrace compile-with "${DTRACE_S}"
cddl/dev/dtrace/powerpc/dtrace_subr.c optional dtrace compile-with "${DTRACE_C}"
cddl/dev/fbt/powerpc/fbt_isa.c optional dtrace_fbt | dtraceall compile-with "${FBT_C}"
crypto/blowfish/bf_enc.c optional crypto | ipsec
crypto/des/des_enc.c optional crypto | ipsec | netsmb
crypto/blowfish/bf_enc.c optional crypto | ipsec | ipsec_support
crypto/des/des_enc.c optional crypto | ipsec | ipsec_support | netsmb
dev/bm/if_bm.c optional bm powermac
dev/adb/adb_bus.c optional adb
dev/adb/adb_kbd.c optional adb

View File

@ -3,8 +3,8 @@ cddl/compat/opensolaris/kern/opensolaris_atomic.c optional zfs | dtrace compile-
cddl/dev/dtrace/riscv/dtrace_asm.S optional dtrace compile-with "${DTRACE_S}"
cddl/dev/dtrace/riscv/dtrace_subr.c optional dtrace compile-with "${DTRACE_C}"
cddl/dev/fbt/riscv/fbt_isa.c optional dtrace_fbt | dtraceall compile-with "${FBT_C}"
crypto/blowfish/bf_enc.c optional crypto | ipsec
crypto/des/des_enc.c optional crypto | ipsec | netsmb
crypto/blowfish/bf_enc.c optional crypto | ipsec | ipsec_support
crypto/des/des_enc.c optional crypto | ipsec | ipsec_support | netsmb
dev/ofw/ofw_cpu.c optional fdt
dev/uart/uart_cpu_fdt.c optional uart fdt
dev/xilinx/axi_quad_spi.c optional xilinx_spi

View File

@ -23,8 +23,8 @@ ukbdmap.h optional ukbd_dflt_keymap \
clean "ukbdmap.h"
#
cddl/contrib/opensolaris/common/atomic/sparc64/opensolaris_atomic.S optional zfs compile-with "${ZFS_S}"
crypto/blowfish/bf_enc.c optional crypto | ipsec
crypto/des/des_enc.c optional crypto | ipsec | netsmb
crypto/blowfish/bf_enc.c optional crypto | ipsec | ipsec_support
crypto/des/des_enc.c optional crypto | ipsec | ipsec_support | netsmb
dev/atkbdc/atkbd.c optional atkbd atkbdc
dev/atkbdc/atkbd_atkbdc.c optional atkbd atkbdc
dev/atkbdc/atkbdc.c optional atkbdc

View File

@ -34,6 +34,7 @@ __DEFAULT_YES_OPTIONS = \
INET \
INET6 \
IPFILTER \
IPSEC_SUPPORT \
ISCSI \
KERNEL_SYMBOLS \
NETGRAPH \

View File

@ -428,7 +428,7 @@ IPFIREWALL_VERBOSE opt_ipfw.h
IPFIREWALL_VERBOSE_LIMIT opt_ipfw.h
IPSEC opt_ipsec.h
IPSEC_DEBUG opt_ipsec.h
IPSEC_NAT_T opt_ipsec.h
IPSEC_SUPPORT opt_ipsec.h
IPSTEALTH
KRPC
LIBALIAS
@ -451,7 +451,7 @@ TCP_HHOOK opt_inet.h
TCP_OFFLOAD opt_inet.h # Enable code to dispatch TCP offloading
TCP_RFC7413 opt_inet.h
TCP_RFC7413_MAX_KEYS opt_inet.h
TCP_SIGNATURE opt_inet.h
TCP_SIGNATURE opt_ipsec.h
VLAN_ARRAY opt_vlan.h
XBONEHACK
FLOWTABLE opt_route.h

View File

@ -177,6 +177,7 @@ SUBDIR= \
ip6_mroute_mod \
ip_mroute_mod \
${_ips} \
${_ipsec} \
${_ipw} \
${_ipwfw} \
${_isci} \
@ -357,6 +358,7 @@ SUBDIR= \
sysvipc \
${_ti} \
${_tcp_fastpath} \
${_tcpmd5} \
tests/framework \
tests/callout_test \
tl \
@ -447,6 +449,10 @@ _toecore= toecore
_if_enc= if_enc
_if_gif= if_gif
_if_gre= if_gre
.if ${MK_IPSEC_SUPPORT} != "no"
_ipsec= ipsec
_tcpmd5= tcp/tcpmd5
.endif
.endif
.if (${MK_INET_SUPPORT} != "no" && ${MK_INET6_SUPPORT} != "no") || \

View File

@ -0,0 +1,14 @@
# $FreeBSD$
.PATH: ${.CURDIR}/../../net ${.CURDIR}/../../netipsec
KMOD= ipsec
SRCS= if_ipsec.c ipsec.c ipsec_input.c ipsec_mbuf.c ipsec_mod.c \
ipsec_output.c xform_ah.c xform_esp.c xform_ipcomp.c \
opt_inet.h opt_inet6.h opt_ipsec.h opt_sctp.h
SRCS.INET= udpencap.c
opt_ipsec.h:
@echo "#define IPSEC_SUPPORT 1" > ${.TARGET}
.include <bsd.kmod.mk>

View File

@ -0,0 +1,11 @@
# $FreeBSD$
.PATH: ${.CURDIR}/../../../netipsec
KMOD= tcpmd5
SRCS= xform_tcp.c opt_inet.h opt_inet6.h opt_ipsec.h
opt_ipsec.h:
@echo "#define IPSEC_SUPPORT 1" > ${.TARGET}
.include <bsd.kmod.mk>

1000
sys/net/if_ipsec.c Normal file

File diff suppressed because it is too large Load Diff

39
sys/net/if_ipsec.h Normal file
View File

@ -0,0 +1,39 @@
/*-
* Copyright (c) 2016 Yandex LLC
* Copyright (c) 2016 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.
*
* $FreeBSD$
*/
#ifndef _NET_IF_IPSEC_H_
#define _NET_IF_IPSEC_H_
#define IPSEC_MTU 1400
#define IPSEC_MTU_MIN 1280
#define IPSEC_MTU_MAX 8192
#define IPSECGREQID _IOWR('i', 160, struct ifreq)
#define IPSECSREQID _IOW('i', 161, struct ifreq)
#endif /* _NET_IF_IPSEC_H_ */

View File

@ -320,7 +320,9 @@ _Static_assert(sizeof(struct sadb_x_sa_replay) == 8, "struct size mismatch");
#define SADB_X_EXT_NAT_T_OAR 24 /* Peer's NAT_OA for dst of SA. */
#define SADB_X_EXT_NAT_T_FRAG 25 /* Manual MTU override. */
#define SADB_X_EXT_SA_REPLAY 26 /* Replay window override. */
#define SADB_EXT_MAX 26
#define SADB_X_EXT_NEW_ADDRESS_SRC 27
#define SADB_X_EXT_NEW_ADDRESS_DST 28
#define SADB_EXT_MAX 28
#define SADB_SATYPE_UNSPEC 0
#define SADB_SATYPE_AH 2

View File

@ -98,11 +98,7 @@ __FBSDID("$FreeBSD$");
#include <netinet6/ip6_var.h>
#endif /* INET6 */
#ifdef IPSEC
#include <netipsec/ipsec.h>
#include <netipsec/key.h>
#endif /* IPSEC */
#include <netipsec/ipsec_support.h>
#include <security/mac/mac_framework.h>
@ -305,8 +301,8 @@ in_pcballoc(struct socket *so, struct inpcbinfo *pcbinfo)
goto out;
mac_inpcb_create(so, inp);
#endif
#ifdef IPSEC
error = ipsec_init_policy(so, &inp->inp_sp);
#if defined(IPSEC) || defined(IPSEC_SUPPORT)
error = ipsec_init_pcbpolicy(inp);
if (error != 0) {
#ifdef MAC
mac_inpcb_destroy(inp);
@ -1284,7 +1280,7 @@ in_pcbfree(struct inpcb *inp)
INP_WLOCK_ASSERT(inp);
/* XXXRW: Do as much as possible here. */
#ifdef IPSEC
#if defined(IPSEC) || defined(IPSEC_SUPPORT)
if (inp->inp_sp != NULL)
ipsec_delete_pcbpolicy(inp);
#endif

View File

@ -90,10 +90,6 @@ __FBSDID("$FreeBSD$");
static struct pr_usrreqs nousrreqs;
#ifdef IPSEC
#include <netipsec/ipsec.h>
#endif /* IPSEC */
#ifdef SCTP
#include <netinet/in_pcb.h>
#include <netinet/sctp_pcb.h>
@ -222,34 +218,6 @@ struct protosw inetsw[] = {
.pr_ctloutput = rip_ctloutput,
.pr_usrreqs = &rip_usrreqs
},
#ifdef IPSEC
{
.pr_type = SOCK_RAW,
.pr_domain = &inetdomain,
.pr_protocol = IPPROTO_AH,
.pr_flags = PR_ATOMIC|PR_ADDR,
.pr_input = ah4_input,
.pr_ctlinput = ah4_ctlinput,
.pr_usrreqs = &nousrreqs
},
{
.pr_type = SOCK_RAW,
.pr_domain = &inetdomain,
.pr_protocol = IPPROTO_ESP,
.pr_flags = PR_ATOMIC|PR_ADDR,
.pr_input = esp4_input,
.pr_ctlinput = esp4_ctlinput,
.pr_usrreqs = &nousrreqs
},
{
.pr_type = SOCK_RAW,
.pr_domain = &inetdomain,
.pr_protocol = IPPROTO_IPCOMP,
.pr_flags = PR_ATOMIC|PR_ADDR,
.pr_input = ipcomp4_input,
.pr_usrreqs = &nousrreqs
},
#endif /* IPSEC */
{
.pr_type = SOCK_RAW,
.pr_domain = &inetdomain,
@ -366,7 +334,7 @@ SYSCTL_NODE(_net_inet, IPPROTO_TCP, tcp, CTLFLAG_RW, 0, "TCP");
SYSCTL_NODE(_net_inet, IPPROTO_SCTP, sctp, CTLFLAG_RW, 0, "SCTP");
#endif
SYSCTL_NODE(_net_inet, IPPROTO_IGMP, igmp, CTLFLAG_RW, 0, "IGMP");
#ifdef IPSEC
#if defined(IPSEC) || defined(IPSEC_SUPPORT)
/* XXX no protocol # to use, pick something "reserved" */
SYSCTL_NODE(_net_inet, 253, ipsec, CTLFLAG_RW, 0, "IPSEC");
SYSCTL_NODE(_net_inet, IPPROTO_AH, ah, CTLFLAG_RW, 0, "AH");

View File

@ -77,13 +77,10 @@ __FBSDID("$FreeBSD$");
#include <netinet/ip_options.h>
#include <machine/in_cksum.h>
#include <netinet/ip_carp.h>
#ifdef IPSEC
#include <netinet/ip_ipsec.h>
#include <netipsec/ipsec.h>
#include <netipsec/key.h>
#endif /* IPSEC */
#include <netinet/in_rss.h>
#include <netipsec/ipsec_support.h>
#include <sys/socketvar.h>
#include <security/mac/mac_framework.h>
@ -430,6 +427,12 @@ ip_direct_input(struct mbuf *m)
ip = mtod(m, struct ip *);
hlen = ip->ip_hl << 2;
#if defined(IPSEC) || defined(IPSEC_SUPPORT)
if (IPSEC_ENABLED(ipv4)) {
if (IPSEC_INPUT(ipv4, m, hlen, ip->ip_p) != 0)
return;
}
#endif /* IPSEC */
IPSTAT_INC(ips_delivered);
(*inetsw[ip_protox[ip->ip_p]].pr_input)(&m, &hlen, ip->ip_p);
return;
@ -559,11 +562,11 @@ ip_input(struct mbuf *m)
* ip pointer.
*/
if (V_ipforwarding != 0
#ifdef IPSEC
&& !key_havesp(IPSEC_DIR_INBOUND)
&& !key_havesp(IPSEC_DIR_OUTBOUND)
#if defined(IPSEC) || defined(IPSEC_SUPPORT)
&& (!IPSEC_ENABLED(ipv4) ||
IPSEC_CAPS(ipv4, m, IPSEC_CAP_OPERABLE) == 0)
#endif
) {
) {
if ((m = ip_tryforward(m)) == NULL)
return;
if (m->m_flags & M_FASTFWD_OURS) {
@ -572,13 +575,16 @@ ip_input(struct mbuf *m)
goto ours;
}
}
#ifdef IPSEC
#if defined(IPSEC) || defined(IPSEC_SUPPORT)
/*
* Bypass packet filtering for packets previously handled by IPsec.
*/
if (ip_ipsec_filtertunnel(m))
goto passin;
if (IPSEC_ENABLED(ipv4) &&
IPSEC_CAPS(ipv4, m, IPSEC_CAP_BYPASS_FILTER) != 0)
goto passin;
#endif
/*
* Run through list of hooks for input packets.
*
@ -802,14 +808,11 @@ ip_input(struct mbuf *m)
hlen = ip->ip_hl << 2;
}
#ifdef IPSEC
/*
* enforce IPsec policy checking if we are seeing last header.
* note that we do not visit this with protocols with pcb layer
* code - like udp/tcp/raw ip.
*/
if (ip_ipsec_input(m, ip->ip_p) != 0)
goto bad;
#if defined(IPSEC) || defined(IPSEC_SUPPORT)
if (IPSEC_ENABLED(ipv4)) {
if (IPSEC_INPUT(ipv4, m, hlen, ip->ip_p) != 0)
return;
}
#endif /* IPSEC */
/*
@ -951,24 +954,14 @@ ip_forward(struct mbuf *m, int srcrt)
m_freem(m);
return;
}
#ifdef IPSEC
if (ip_ipsec_fwd(m) != 0) {
IPSTAT_INC(ips_cantforward);
m_freem(m);
if (
#ifdef IPSTEALTH
V_ipstealth == 0 &&
#endif
ip->ip_ttl <= IPTTLDEC) {
icmp_error(m, ICMP_TIMXCEED, ICMP_TIMXCEED_INTRANS, 0, 0);
return;
}
#endif /* IPSEC */
#ifdef IPSTEALTH
if (!V_ipstealth) {
#endif
if (ip->ip_ttl <= IPTTLDEC) {
icmp_error(m, ICMP_TIMXCEED, ICMP_TIMXCEED_INTRANS,
0, 0);
return;
}
#ifdef IPSTEALTH
}
#endif
bzero(&ro, sizeof(ro));
sin = (struct sockaddr_in *)&ro.ro_dst;
@ -987,19 +980,6 @@ ip_forward(struct mbuf *m, int srcrt)
ifa_ref(&ia->ia_ifa);
} else
ia = NULL;
#ifndef IPSEC
/*
* 'ia' may be NULL if there is no route for this destination.
* In case of IPsec, Don't discard it just yet, but pass it to
* ip_output in case of outgoing IPsec policy.
*/
if (!srcrt && ia == NULL) {
icmp_error(m, ICMP_UNREACH, ICMP_UNREACH_HOST, 0, 0);
RO_RTFREE(&ro);
return;
}
#endif
/*
* Save the IP header and at most 8 bytes of the payload,
* in case we need to generate an ICMP message to the src.
@ -1032,15 +1012,22 @@ ip_forward(struct mbuf *m, int srcrt)
mcopy->m_pkthdr.len = mcopy->m_len;
m_copydata(m, 0, mcopy->m_len, mtod(mcopy, caddr_t));
}
#ifdef IPSTEALTH
if (!V_ipstealth) {
if (V_ipstealth == 0)
#endif
ip->ip_ttl -= IPTTLDEC;
#ifdef IPSTEALTH
#if defined(IPSEC) || defined(IPSEC_SUPPORT)
if (IPSEC_ENABLED(ipv4)) {
if ((error = IPSEC_FORWARD(ipv4, m)) != 0) {
/* mbuf consumed by IPsec */
m_freem(mcopy);
if (error != EINPROGRESS)
IPSTAT_INC(ips_cantforward);
return;
}
/* No IPsec processing required */
}
#endif
#endif /* IPSEC */
/*
* If forwarding packet using same interface that it came in on,
* perhaps should send a redirect to sender to shortcut a hop.
@ -1118,14 +1105,6 @@ ip_forward(struct mbuf *m, int srcrt)
case EMSGSIZE:
type = ICMP_UNREACH;
code = ICMP_UNREACH_NEEDFRAG;
#ifdef IPSEC
/*
* If IPsec is configured for this path,
* override any possibly mtu value set by ip_output.
*/
mtu = ip_ipsec_mtu(mcopy, mtu);
#endif /* IPSEC */
/*
* If the MTU was set before make sure we are below the
* interface MTU.

View File

@ -1,245 +0,0 @@
/*-
* Copyright (c) 1982, 1986, 1988, 1993
* The Regents of the University of California. 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.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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.
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include "opt_ipsec.h"
#include "opt_sctp.h"
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/errno.h>
#include <sys/kernel.h>
#include <sys/malloc.h>
#include <sys/mbuf.h>
#include <sys/protosw.h>
#include <sys/socket.h>
#include <sys/socketvar.h>
#include <sys/sysctl.h>
#include <net/if.h>
#include <net/if_var.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/in_pcb.h>
#include <netinet/ip_var.h>
#include <netinet/ip_options.h>
#include <netinet/ip_ipsec.h>
#ifdef SCTP
#include <netinet/sctp_crc32.h>
#endif
#include <machine/in_cksum.h>
#include <netipsec/ipsec.h>
#include <netipsec/xform.h>
#include <netipsec/key.h>
extern struct protosw inetsw[];
static VNET_DEFINE(int, ip4_ipsec_filtertunnel) = 0;
#define V_ip4_ipsec_filtertunnel VNET(ip4_ipsec_filtertunnel)
SYSCTL_DECL(_net_inet_ipsec);
SYSCTL_INT(_net_inet_ipsec, OID_AUTO, filtertunnel,
CTLFLAG_VNET | CTLFLAG_RW, &VNET_NAME(ip4_ipsec_filtertunnel), 0,
"If set filter packets from an IPsec tunnel.");
/*
* Check if we have to jump over firewall processing for this packet.
* Called from ip_input().
* 1 = jump over firewall, 0 = packet goes through firewall.
*/
int
ip_ipsec_filtertunnel(struct mbuf *m)
{
/*
* Bypass packet filtering for packets previously handled by IPsec.
*/
if (!V_ip4_ipsec_filtertunnel &&
m_tag_find(m, PACKET_TAG_IPSEC_IN_DONE, NULL) != NULL)
return 1;
return 0;
}
/*
* Check if this packet has an active SA and needs to be dropped instead
* of forwarded.
* Called from ip_forward().
* 1 = drop packet, 0 = forward packet.
*/
int
ip_ipsec_fwd(struct mbuf *m)
{
return (ipsec4_in_reject(m, NULL));
}
/*
* Check if protocol type doesn't have a further header and do IPSEC
* decryption or reject right now. Protocols with further headers get
* their IPSEC treatment within the protocol specific processing.
* Called from ip_input().
* 1 = drop packet, 0 = continue processing packet.
*/
int
ip_ipsec_input(struct mbuf *m, int nxt)
{
/*
* enforce IPsec policy checking if we are seeing last header.
* note that we do not visit this with protocols with pcb layer
* code - like udp/tcp/raw ip.
*/
if ((inetsw[ip_protox[nxt]].pr_flags & PR_LASTHDR) != 0)
return (ipsec4_in_reject(m, NULL));
return (0);
}
/*
* Compute the MTU for a forwarded packet that gets IPSEC encapsulated.
* Called from ip_forward().
* Returns MTU suggestion for ICMP needfrag reply.
*/
int
ip_ipsec_mtu(struct mbuf *m, int mtu)
{
/*
* If the packet is routed over IPsec tunnel, tell the
* originator the tunnel MTU.
* tunnel MTU = if MTU - sizeof(IP) - ESP/AH hdrsiz
* XXX quickhack!!!
*/
return (mtu - ipsec_hdrsiz(m, IPSEC_DIR_OUTBOUND, NULL));
}
/*
*
* Called from ip_output().
* 1 = drop packet, 0 = continue processing packet,
* -1 = packet was reinjected and stop processing packet
*/
int
ip_ipsec_output(struct mbuf **m, struct inpcb *inp, int *error)
{
struct secpolicy *sp;
if (!key_havesp(IPSEC_DIR_OUTBOUND))
return 0;
/*
* Check the security policy (SP) for the packet and, if
* required, do IPsec-related processing. There are two
* cases here; the first time a packet is sent through
* it will be untagged and handled by ipsec4_checkpolicy.
* If the packet is resubmitted to ip_output (e.g. after
* AH, ESP, etc. processing), there will be a tag to bypass
* the lookup and related policy checking.
*/
if (m_tag_find(*m, PACKET_TAG_IPSEC_OUT_DONE, NULL) != NULL) {
*error = 0;
return (0);
}
sp = ipsec4_checkpolicy(*m, IPSEC_DIR_OUTBOUND, error, inp);
/*
* There are four return cases:
* sp != NULL apply IPsec policy
* sp == NULL, error == 0 no IPsec handling needed
* sp == NULL, error == -EINVAL discard packet w/o error
* sp == NULL, error != 0 discard packet, report error
*/
if (sp != NULL) {
/*
* Do delayed checksums now because we send before
* this is done in the normal processing path.
*/
if ((*m)->m_pkthdr.csum_flags & CSUM_DELAY_DATA) {
in_delayed_cksum(*m);
(*m)->m_pkthdr.csum_flags &= ~CSUM_DELAY_DATA;
}
#ifdef SCTP
if ((*m)->m_pkthdr.csum_flags & CSUM_SCTP) {
struct ip *ip = mtod(*m, struct ip *);
sctp_delayed_cksum(*m, (uint32_t)(ip->ip_hl << 2));
(*m)->m_pkthdr.csum_flags &= ~CSUM_SCTP;
}
#endif
/* NB: callee frees mbuf */
*error = ipsec4_process_packet(*m, sp->req);
KEY_FREESP(&sp);
if (*error == EJUSTRETURN) {
/*
* We had a SP with a level of 'use' and no SA. We
* will just continue to process the packet without
* IPsec processing and return without error.
*/
*error = 0;
goto done;
}
/*
* Preserve KAME behaviour: ENOENT can be returned
* when an SA acquire is in progress. Don't propagate
* this to user-level; it confuses applications.
*
* XXX this will go away when the SADB is redone.
*/
if (*error == ENOENT)
*error = 0;
goto reinjected;
} else { /* sp == NULL */
if (*error != 0) {
/*
* Hack: -EINVAL is used to signal that a packet
* should be silently discarded. This is typically
* because we asked key management for an SA and
* it was delayed (e.g. kicked up to IKE).
*/
if (*error == -EINVAL)
*error = 0;
goto bad;
}
/* No IPsec processing for this packet. */
}
done:
return (0);
reinjected:
return (-1);
bad:
if (sp != NULL)
KEY_FREESP(&sp);
return 1;
}

View File

@ -1,40 +0,0 @@
/*-
* Copyright (c) 1982, 1986, 1988, 1993
* The Regents of the University of California. 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.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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$
*/
#ifndef _NETINET_IP_IPSEC_H_
#define _NETINET_IP_IPSEC_H_
int ip_ipsec_filtertunnel(struct mbuf *);
int ip_ipsec_fwd(struct mbuf *);
int ip_ipsec_input(struct mbuf *, int);
int ip_ipsec_mtu(struct mbuf *, int);
int ip_ipsec_output(struct mbuf **, struct inpcb *, int *);
#endif

View File

@ -84,10 +84,7 @@ __FBSDID("$FreeBSD$");
#include <netinet/sctp_crc32.h>
#endif
#ifdef IPSEC
#include <netinet/ip_ipsec.h>
#include <netipsec/ipsec.h>
#endif /* IPSEC*/
#include <netipsec/ipsec_support.h>
#include <machine/in_cksum.h>
@ -228,7 +225,7 @@ ip_output(struct mbuf *m, struct mbuf *opt, struct route *ro, int flags,
struct rtentry *rte; /* cache for ro->ro_rt */
uint32_t fibnum;
int have_ia_ref;
#ifdef IPSEC
#if defined(IPSEC) || defined(IPSEC_SUPPORT)
int no_route_but_check_spd = 0;
#endif
M_ASSERTPKTHDR(m);
@ -384,7 +381,7 @@ ip_output(struct mbuf *m, struct mbuf *opt, struct route *ro, int flags,
(rte->rt_flags & RTF_UP) == 0 ||
rte->rt_ifp == NULL ||
!RT_LINK_IS_UP(rte->rt_ifp)) {
#ifdef IPSEC
#if defined(IPSEC) || defined(IPSEC_SUPPORT)
/*
* There is no route for this packet, but it is
* possible that a matching SPD entry exists.
@ -556,15 +553,13 @@ ip_output(struct mbuf *m, struct mbuf *opt, struct route *ro, int flags,
}
sendit:
#ifdef IPSEC
switch(ip_ipsec_output(&m, inp, &error)) {
case 1:
goto bad;
case -1:
goto done;
case 0:
default:
break; /* Continue with packet processing. */
#if defined(IPSEC) || defined(IPSEC_SUPPORT)
if (IPSEC_ENABLED(ipv4)) {
if ((error = IPSEC_OUTPUT(ipv4, m, inp)) != 0) {
if (error == EINPROGRESS)
error = 0;
goto done;
}
}
/*
* Check if there was a route for this packet; return error if not.
@ -1230,23 +1225,13 @@ ip_ctloutput(struct socket *so, struct sockopt *sopt)
INP_WUNLOCK(inp);
break;
#ifdef IPSEC
#if defined(IPSEC) || defined(IPSEC_SUPPORT)
case IP_IPSEC_POLICY:
{
caddr_t req;
struct mbuf *m;
if ((error = soopt_getm(sopt, &m)) != 0) /* XXX */
if (IPSEC_ENABLED(ipv4)) {
error = IPSEC_PCBCTL(ipv4, inp, sopt);
break;
if ((error = soopt_mcopyin(sopt, m)) != 0) /* XXX */
break;
req = mtod(m, caddr_t);
error = ipsec_set_policy(inp, sopt->sopt_name, req,
m->m_len, (sopt->sopt_td != NULL) ?
sopt->sopt_td->td_ucred : NULL);
m_freem(m);
break;
}
}
/* FALLTHROUGH */
#endif /* IPSEC */
default:
@ -1389,24 +1374,13 @@ ip_ctloutput(struct socket *so, struct sockopt *sopt)
error = inp_getmoptions(inp, sopt);
break;
#ifdef IPSEC
#if defined(IPSEC) || defined(IPSEC_SUPPORT)
case IP_IPSEC_POLICY:
{
struct mbuf *m = NULL;
caddr_t req = NULL;
size_t len = 0;
if (m != NULL) {
req = mtod(m, caddr_t);
len = m->m_len;
if (IPSEC_ENABLED(ipv4)) {
error = IPSEC_PCBCTL(ipv4, inp, sopt);
break;
}
error = ipsec_get_policy(sotoinpcb(so), req, len, &m);
if (error == 0)
error = soopt_mcopyout(sopt, m); /* XXX */
if (error == 0)
m_freem(m);
break;
}
/* FALLTHROUGH */
#endif /* IPSEC */
default:

View File

@ -73,9 +73,7 @@ __FBSDID("$FreeBSD$");
#include <netinet/ip_mroute.h>
#include <netinet/ip_icmp.h>
#ifdef IPSEC
#include <netipsec/ipsec.h>
#endif /*IPSEC*/
#include <netipsec/ipsec_support.h>
#include <machine/stdarg.h>
#include <security/mac/mac_framework.h>
@ -236,10 +234,11 @@ rip_append(struct inpcb *last, struct ip *ip, struct mbuf *n,
INP_LOCK_ASSERT(last);
#ifdef IPSEC
#if defined(IPSEC) || defined(IPSEC_SUPPORT)
/* check AH/ESP integrity. */
if (ipsec4_in_reject(n, last)) {
policyfail = 1;
if (IPSEC_ENABLED(ipv4)) {
if (IPSEC_CHECK_POLICY(ipv4, n, last) != 0)
policyfail = 1;
}
#endif /* IPSEC */
#ifdef MAC

View File

@ -5790,7 +5790,7 @@ sctp_common_input_processing(struct mbuf **mm, int iphlen, int offset, int lengt
} else if (stcb == NULL) {
inp_decr = inp;
}
#ifdef IPSEC
#if defined(IPSEC) || defined(IPSEC_SUPPORT)
/*-
* I very much doubt any of the IPSEC stuff will work but I have no
* idea, so I will leave it in place.
@ -5799,17 +5799,23 @@ sctp_common_input_processing(struct mbuf **mm, int iphlen, int offset, int lengt
switch (dst->sa_family) {
#ifdef INET
case AF_INET:
if (ipsec4_in_reject(m, &inp->ip_inp.inp)) {
SCTP_STAT_INCR(sctps_hdrops);
goto out;
if (IPSEC_ENABLED(ipv4)) {
if (IPSEC_CHECK_POLICY(ipv4, m,
&inp->ip_inp.inp) != 0) {
SCTP_STAT_INCR(sctps_hdrops);
goto out;
}
}
break;
#endif
#ifdef INET6
case AF_INET6:
if (ipsec6_in_reject(m, &inp->ip_inp.inp)) {
SCTP_STAT_INCR(sctps_hdrops);
goto out;
if (IPSEC_ENABLED(ipv6)) {
if (IPSEC_CHECK_POLICY(ipv6, m,
&inp->ip_inp.inp) != 0) {
SCTP_STAT_INCR(sctps_hdrops);
goto out;
}
}
break;
#endif
@ -5817,7 +5823,7 @@ sctp_common_input_processing(struct mbuf **mm, int iphlen, int offset, int lengt
break;
}
}
#endif
#endif /* IPSEC */
SCTPDBG(SCTP_DEBUG_INPUT1, "Ok, Common input processing called, m:%p iphlen:%d offset:%d length:%d stcb:%p\n",
(void *)m, iphlen, offset, length, (void *)stcb);
if (stcb) {

View File

@ -82,16 +82,10 @@ __FBSDID("$FreeBSD$");
#include <netinet/ip_icmp.h>
#include <netinet/icmp_var.h>
#ifdef IPSEC
#include <netipsec/ipsec.h>
#include <netipsec/key.h>
#endif /* IPSEC */
#include <netipsec/ipsec_support.h>
#ifdef INET6
#include <sys/domain.h>
#ifdef IPSEC
#include <netipsec/ipsec6.h>
#endif
#include <netinet/ip6.h>
#include <netinet6/ip6_var.h>
#include <netinet6/in6_pcb.h>

View File

@ -2469,8 +2469,8 @@ sctp_inpcb_alloc(struct socket *so, uint32_t vrf_id)
SCTP_INP_INFO_WUNLOCK();
return (ENOBUFS);
}
#ifdef IPSEC
error = ipsec_init_policy(so, &inp->ip_inp.inp.inp_sp);
#if defined(IPSEC) || defined(IPSEC_SUPPORT)
error = ipsec_init_pcbpolicy(&inp->ip_inp.inp);
if (error != 0) {
crfree(inp->ip_inp.inp.inp_cred);
SCTP_ZONE_FREE(SCTP_BASE_INFO(ipi_zone_ep), inp);
@ -2504,7 +2504,7 @@ sctp_inpcb_alloc(struct socket *so, uint32_t vrf_id)
SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_PCB, EOPNOTSUPP);
so->so_pcb = NULL;
crfree(inp->ip_inp.inp.inp_cred);
#ifdef IPSEC
#if defined(IPSEC) || defined(IPSEC_SUPPORT)
ipsec_delete_pcbpolicy(&inp->ip_inp.inp);
#endif
SCTP_ZONE_FREE(SCTP_BASE_INFO(ipi_zone_ep), inp);
@ -2527,7 +2527,7 @@ sctp_inpcb_alloc(struct socket *so, uint32_t vrf_id)
SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_PCB, ENOBUFS);
so->so_pcb = NULL;
crfree(inp->ip_inp.inp.inp_cred);
#ifdef IPSEC
#if defined(IPSEC) || defined(IPSEC_SUPPORT)
ipsec_delete_pcbpolicy(&inp->ip_inp.inp);
#endif
SCTP_ZONE_FREE(SCTP_BASE_INFO(ipi_zone_ep), inp);
@ -3641,7 +3641,7 @@ sctp_inpcb_free(struct sctp_inpcb *inp, int immediate, int from)
* macro here since le_next will get freed as part of the
* sctp_free_assoc() call.
*/
#ifdef IPSEC
#if defined(IPSEC) || defined(IPSEC_SUPPORT)
ipsec_delete_pcbpolicy(ip_pcb);
#endif
if (ip_pcb->inp_options) {

View File

@ -120,10 +120,7 @@ __FBSDID("$FreeBSD$");
#include <netinet/tcp_offload.h>
#endif
#ifdef IPSEC
#include <netipsec/ipsec.h>
#include <netipsec/ipsec6.h>
#endif /*IPSEC*/
#include <netipsec/ipsec_support.h>
#include <machine/in_cksum.h>
@ -487,20 +484,6 @@ cc_post_recovery(struct tcpcb *tp, struct tcphdr *th)
tp->t_bytes_acked = 0;
}
#ifdef TCP_SIGNATURE
static inline int
tcp_signature_verify_input(struct mbuf *m, int off0, int tlen, int optlen,
struct tcpopt *to, struct tcphdr *th, u_int tcpbflag)
{
int ret;
tcp_fields_to_net(th);
ret = tcp_signature_verify(m, off0, tlen, optlen, to, th, tcpbflag);
tcp_fields_to_host(th);
return (ret);
}
#endif
/*
* Indicate whether this ack should be delayed. We can delay the ack if
* following conditions are met:
@ -611,9 +594,6 @@ tcp_input(struct mbuf **mp, int *offp, int proto)
int drop_hdrlen;
int thflags;
int rstreason = 0; /* For badport_bandlim accounting purposes */
#ifdef TCP_SIGNATURE
uint8_t sig_checked = 0;
#endif
uint8_t iptos;
struct m_tag *fwd_tag = NULL;
#ifdef INET6
@ -944,15 +924,22 @@ tcp_input(struct mbuf **mp, int *offp, int proto)
inp->inp_flowid = m->m_pkthdr.flowid;
inp->inp_flowtype = M_HASHTYPE_GET(m);
}
#ifdef IPSEC
#if defined(IPSEC) || defined(IPSEC_SUPPORT)
#ifdef INET6
if (isipv6 && ipsec6_in_reject(m, inp)) {
goto dropunlock;
} else
#endif /* INET6 */
if (ipsec4_in_reject(m, inp) != 0) {
if (isipv6 && IPSEC_ENABLED(ipv6) &&
IPSEC_CHECK_POLICY(ipv6, m, inp) != 0) {
goto dropunlock;
}
#ifdef INET
else
#endif
#endif /* INET6 */
#ifdef INET
if (IPSEC_ENABLED(ipv4) &&
IPSEC_CHECK_POLICY(ipv4, m, inp) != 0) {
goto dropunlock;
}
#endif /* INET */
#endif /* IPSEC */
/*
@ -1135,7 +1122,16 @@ tcp_input(struct mbuf **mp, int *offp, int proto)
* NB: syncache_expand() doesn't unlock
* inp and tcpinfo locks.
*/
if (!syncache_expand(&inc, &to, th, &so, m)) {
rstreason = syncache_expand(&inc, &to, th, &so, m);
if (rstreason < 0) {
/*
* A failing TCP MD5 signature comparison
* must result in the segment being dropped
* and must not produce any response back
* to the sender.
*/
goto dropunlock;
} else if (rstreason == 0) {
/*
* No syncache entry or ACK was not
* for our SYN/ACK. Send a RST.
@ -1187,26 +1183,6 @@ tcp_input(struct mbuf **mp, int *offp, int proto)
tp = intotcpcb(inp);
KASSERT(tp->t_state == TCPS_SYN_RECEIVED,
("%s: ", __func__));
#ifdef TCP_SIGNATURE
if (sig_checked == 0) {
tcp_dooptions(&to, optp, optlen,
(thflags & TH_SYN) ? TO_SYN : 0);
if (!tcp_signature_verify_input(m, off0, tlen,
optlen, &to, th, tp->t_flags)) {
/*
* In SYN_SENT state if it receives an
* RST, it is allowed for further
* processing.
*/
if ((thflags & TH_RST) == 0 ||
(tp->t_state == TCPS_SYN_SENT) == 0)
goto dropunlock;
}
sig_checked = 1;
}
#endif
/*
* Process the segment and the data it
* contains. tcp_do_segment() consumes
@ -1436,26 +1412,18 @@ tcp_input(struct mbuf **mp, int *offp, int proto)
*/
goto dropunlock;
}
#ifdef TCP_SIGNATURE
if (sig_checked == 0) {
tcp_dooptions(&to, optp, optlen,
(thflags & TH_SYN) ? TO_SYN : 0);
if (!tcp_signature_verify_input(m, off0, tlen, optlen, &to,
th, tp->t_flags)) {
/*
* In SYN_SENT state if it receives an RST, it is
* allowed for further processing.
*/
if ((thflags & TH_RST) == 0 ||
(tp->t_state == TCPS_SYN_SENT) == 0)
goto dropunlock;
#if defined(IPSEC_SUPPORT) || defined(TCP_SIGNATURE)
if (tp->t_flags & TF_SIGNATURE) {
tcp_dooptions(&to, optp, optlen, thflags);
if ((to.to_flags & TOF_SIGNATURE) == 0) {
TCPSTAT_INC(tcps_sig_err_nosigopt);
goto dropunlock;
}
sig_checked = 1;
if (!TCPMD5_ENABLED() ||
TCPMD5_INPUT(m, th, to.to_signature) != 0)
goto dropunlock;
}
#endif
TCP_PROBE5(receive, NULL, tp, m, tp, th);
/*
@ -1632,6 +1600,13 @@ tcp_do_segment(struct mbuf *m, struct tcphdr *th, struct socket *so,
(th->th_off << 2) - sizeof(struct tcphdr),
(thflags & TH_SYN) ? TO_SYN : 0);
#if defined(IPSEC_SUPPORT) || defined(TCP_SIGNATURE)
if ((tp->t_flags & TF_SIGNATURE) != 0 &&
(to.to_flags & TOF_SIGNATURE) == 0) {
TCPSTAT_INC(tcps_sig_err_sigopt);
/* XXX: should drop? */
}
#endif
/*
* If echoed timestamp is later than the current time,
* fall back to non RFC1323 RTT calculation. Normalize
@ -3418,20 +3393,19 @@ tcp_dooptions(struct tcpopt *to, u_char *cp, int cnt, int flags)
(char *)&to->to_tsecr, sizeof(to->to_tsecr));
to->to_tsecr = ntohl(to->to_tsecr);
break;
#ifdef TCP_SIGNATURE
/*
* XXX In order to reply to a host which has set the
* TCP_SIGNATURE option in its initial SYN, we have to
* record the fact that the option was observed here
* for the syncache code to perform the correct response.
*/
case TCPOPT_SIGNATURE:
/*
* In order to reply to a host which has set the
* TCP_SIGNATURE option in its initial SYN, we have
* to record the fact that the option was observed
* here for the syncache code to perform the correct
* response.
*/
if (optlen != TCPOLEN_SIGNATURE)
continue;
to->to_flags |= TOF_SIGNATURE;
to->to_signature = cp + 2;
break;
#endif
case TCPOPT_SACK_PERMITTED:
if (optlen != TCPOLEN_SACK_PERMITTED)
continue;

View File

@ -90,9 +90,7 @@ __FBSDID("$FreeBSD$");
#include <netinet/tcp_offload.h>
#endif
#ifdef IPSEC
#include <netipsec/ipsec.h>
#endif /*IPSEC*/
#include <netipsec/ipsec_support.h>
#include <machine/in_cksum.h>
@ -200,7 +198,7 @@ tcp_output(struct tcpcb *tp)
struct tcphdr *th;
u_char opt[TCP_MAXOLEN];
unsigned ipoptlen, optlen, hdrlen;
#ifdef IPSEC
#if defined(IPSEC) || defined(IPSEC_SUPPORT)
unsigned ipsec_optlen = 0;
#endif
int idle, sendalot;
@ -551,14 +549,23 @@ tcp_output(struct tcpcb *tp)
* the right thing below to provide length of just ip options and thus
* checking for ipoptlen is enough to decide if ip options are present.
*/
#ifdef IPSEC
#if defined(IPSEC) || defined(IPSEC_SUPPORT)
/*
* Pre-calculate here as we save another lookup into the darknesses
* of IPsec that way and can actually decide if TSO is ok.
*/
ipsec_optlen = ipsec_hdrsiz_tcp(tp);
#ifdef INET6
if (isipv6 && IPSEC_ENABLED(ipv6))
ipsec_optlen = IPSEC_HDRSIZE(ipv6, tp->t_inpcb);
#ifdef INET
else
#endif
#endif /* INET6 */
#ifdef INET
if (IPSEC_ENABLED(ipv4))
ipsec_optlen = IPSEC_HDRSIZE(ipv4, tp->t_inpcb);
#endif /* INET */
#endif /* IPSEC */
#ifdef INET6
if (isipv6)
ipoptlen = ip6_optlen(tp->t_inpcb);
@ -569,7 +576,7 @@ tcp_output(struct tcpcb *tp)
offsetof(struct ipoption, ipopt_list);
else
ipoptlen = 0;
#ifdef IPSEC
#if defined(IPSEC) || defined(IPSEC_SUPPORT)
ipoptlen += ipsec_optlen;
#endif
@ -839,8 +846,12 @@ tcp_output(struct tcpcb *tp)
to.to_sacks = (u_char *)tp->sackblks;
}
}
#ifdef TCP_SIGNATURE
#if defined(IPSEC_SUPPORT) || defined(TCP_SIGNATURE)
/* TCP-MD5 (RFC2385). */
/*
* Check that TCP_MD5SIG is enabled in tcpcb to
* account the size needed to set this TCP option.
*/
if (tp->t_flags & TF_SIGNATURE)
to.to_flags |= TOF_SIGNATURE;
#endif /* TCP_SIGNATURE */
@ -1253,20 +1264,31 @@ tcp_output(struct tcpcb *tp)
*/
tp->snd_up = tp->snd_una; /* drag it along */
#ifdef TCP_SIGNATURE
if (to.to_flags & TOF_SIGNATURE) {
int sigoff = to.to_signature - opt;
tcp_signature_compute(m, 0, len, optlen,
(u_char *)(th + 1) + sigoff, IPSEC_DIR_OUTBOUND);
}
#endif
/*
* Put TCP length in extended header, and then
* checksum extended header and data.
*/
m->m_pkthdr.len = hdrlen + len; /* in6_cksum() need this */
m->m_pkthdr.csum_data = offsetof(struct tcphdr, th_sum);
#if defined(IPSEC_SUPPORT) || defined(TCP_SIGNATURE)
if (to.to_flags & TOF_SIGNATURE) {
/*
* Calculate MD5 signature and put it into the place
* determined before.
* NOTE: since TCP options buffer doesn't point into
* mbuf's data, calculate offset and use it.
*/
if (!TCPMD5_ENABLED() || TCPMD5_OUTPUT(m, th,
(u_char *)(th + 1) + (to.to_signature - opt)) != 0) {
/*
* Do not send segment if the calculation of MD5
* digest has failed.
*/
goto out;
}
}
#endif
#ifdef INET6
if (isipv6) {
/*
@ -1304,7 +1326,7 @@ tcp_output(struct tcpcb *tp)
m->m_pkthdr.tso_segsz = tp->t_maxseg - optlen;
}
#ifdef IPSEC
#if defined(IPSEC) || defined(IPSEC_SUPPORT)
KASSERT(len + hdrlen + ipoptlen - ipsec_optlen == m_length(m, NULL),
("%s: mbuf chain shorter than expected: %d + %u + %u - %u != %u",
__func__, len, hdrlen, ipoptlen, ipsec_optlen, m_length(m, NULL)));
@ -1563,6 +1585,9 @@ tcp_output(struct tcpcb *tp)
}
SOCKBUF_UNLOCK_ASSERT(&so->so_snd); /* Check gotos. */
switch (error) {
case EACCES:
tp->t_softerror = error;
return (0);
case EPERM:
tp->t_softerror = error;
return (error);
@ -1730,7 +1755,6 @@ tcp_addoptions(struct tcpopt *to, u_char *optp)
bcopy((u_char *)&to->to_tsecr, optp, sizeof(to->to_tsecr));
optp += sizeof(to->to_tsecr);
break;
#ifdef TCP_SIGNATURE
case TOF_SIGNATURE:
{
int siglen = TCPOLEN_SIGNATURE - 2;
@ -1739,8 +1763,10 @@ tcp_addoptions(struct tcpopt *to, u_char *optp)
optlen += TCPOLEN_NOP;
*optp++ = TCPOPT_NOP;
}
if (TCP_MAXOLEN - optlen < TCPOLEN_SIGNATURE)
if (TCP_MAXOLEN - optlen < TCPOLEN_SIGNATURE) {
to->to_flags &= ~TOF_SIGNATURE;
continue;
}
optlen += TCPOLEN_SIGNATURE;
*optp++ = TCPOPT_SIGNATURE;
*optp++ = TCPOLEN_SIGNATURE;
@ -1749,7 +1775,6 @@ tcp_addoptions(struct tcpopt *to, u_char *optp)
*optp++ = 0;
break;
}
#endif
case TOF_SACK:
{
int sackblks = 0;

View File

@ -56,7 +56,6 @@ __FBSDID("$FreeBSD$");
#include "opt_inet.h"
#include "opt_inet6.h"
#include "opt_ipsec.h"
#include "opt_tcpdebug.h"
#include <sys/param.h>
@ -117,11 +116,6 @@ __FBSDID("$FreeBSD$");
#include <netinet/tcp_offload.h>
#endif
#ifdef IPSEC
#include <netipsec/ipsec.h>
#include <netipsec/ipsec6.h>
#endif /*IPSEC*/
#include <machine/in_cksum.h>
#include <security/mac/mac_framework.h>

View File

@ -118,15 +118,7 @@ __FBSDID("$FreeBSD$");
#include <netinet/tcp_offload.h>
#endif
#ifdef IPSEC
#include <netipsec/ipsec.h>
#include <netipsec/xform.h>
#ifdef INET6
#include <netipsec/ipsec6.h>
#endif
#include <netipsec/key.h>
#include <sys/syslog.h>
#endif /*IPSEC*/
#include <netipsec/ipsec_support.h>
#include <machine/in_cksum.h>
#include <sys/md5.h>
@ -233,12 +225,6 @@ static int tcp_soreceive_stream;
SYSCTL_INT(_net_inet_tcp, OID_AUTO, soreceive_stream, CTLFLAG_RDTUN,
&tcp_soreceive_stream, 0, "Using soreceive_stream for TCP sockets");
#ifdef TCP_SIGNATURE
static int tcp_sig_checksigs = 1;
SYSCTL_INT(_net_inet_tcp, OID_AUTO, signature_verify_input, CTLFLAG_RW,
&tcp_sig_checksigs, 0, "Verify RFC2385 digests on inbound traffic");
#endif
VNET_DEFINE(uma_zone_t, sack_hole_zone);
#define V_sack_hole_zone VNET(sack_hole_zone)
@ -1064,12 +1050,11 @@ tcp_respond(struct tcpcb *tp, void *ipgen, struct tcphdr *th, struct mbuf *m,
to.to_tsecr = tp->ts_recent;
to.to_flags |= TOF_TS;
}
#ifdef TCP_SIGNATURE
#if defined(IPSEC_SUPPORT) || defined(TCP_SIGNATURE)
/* TCP-MD5 (RFC2385). */
if (tp->t_flags & TF_SIGNATURE)
to.to_flags |= TOF_SIGNATURE;
#endif
/* Add the options. */
tlen += optlen = tcp_addoptions(&to, optp);
@ -1125,10 +1110,13 @@ tcp_respond(struct tcpcb *tp, void *ipgen, struct tcphdr *th, struct mbuf *m,
nth->th_win = htons((u_short)win);
nth->th_urp = 0;
#ifdef TCP_SIGNATURE
#if defined(IPSEC_SUPPORT) || defined(TCP_SIGNATURE)
if (to.to_flags & TOF_SIGNATURE) {
tcp_signature_compute(m, 0, 0, optlen, to.to_signature,
IPSEC_DIR_OUTBOUND);
if (!TCPMD5_ENABLED() ||
TCPMD5_OUTPUT(m, nth, to.to_signature) != 0) {
m_freem(m);
return;
}
}
#endif
@ -2501,7 +2489,7 @@ tcp_maxseg(const struct tcpcb *tp)
optlen = TCPOLEN_TSTAMP_APPA;
else
optlen = 0;
#ifdef TCP_SIGNATURE
#if defined(IPSEC_SUPPORT) || defined(TCP_SIGNATURE)
if (tp->t_flags & TF_SIGNATURE)
optlen += PAD(TCPOLEN_SIGNATURE);
#endif
@ -2517,7 +2505,7 @@ tcp_maxseg(const struct tcpcb *tp)
optlen = PAD(TCPOLEN_MAXSEG);
if (tp->t_flags & TF_REQ_SCALE)
optlen += PAD(TCPOLEN_WINDOW);
#ifdef TCP_SIGNATURE
#if defined(IPSEC_SUPPORT) || defined(TCP_SIGNATURE)
if (tp->t_flags & TF_SIGNATURE)
optlen += PAD(TCPOLEN_SIGNATURE);
#endif
@ -2529,343 +2517,6 @@ tcp_maxseg(const struct tcpcb *tp)
return (tp->t_maxseg - optlen);
}
#ifdef IPSEC
/* compute ESP/AH header size for TCP, including outer IP header. */
size_t
ipsec_hdrsiz_tcp(struct tcpcb *tp)
{
struct inpcb *inp;
struct mbuf *m;
size_t hdrsiz;
struct ip *ip;
#ifdef INET6
struct ip6_hdr *ip6;
#endif
struct tcphdr *th;
if ((tp == NULL) || ((inp = tp->t_inpcb) == NULL) ||
(!key_havesp(IPSEC_DIR_OUTBOUND)))
return (0);
m = m_gethdr(M_NOWAIT, MT_DATA);
if (!m)
return (0);
#ifdef INET6
if ((inp->inp_vflag & INP_IPV6) != 0) {
ip6 = mtod(m, struct ip6_hdr *);
th = (struct tcphdr *)(ip6 + 1);
m->m_pkthdr.len = m->m_len =
sizeof(struct ip6_hdr) + sizeof(struct tcphdr);
tcpip_fillheaders(inp, ip6, th);
hdrsiz = ipsec_hdrsiz(m, IPSEC_DIR_OUTBOUND, inp);
} else
#endif /* INET6 */
{
ip = mtod(m, struct ip *);
th = (struct tcphdr *)(ip + 1);
m->m_pkthdr.len = m->m_len = sizeof(struct tcpiphdr);
tcpip_fillheaders(inp, ip, th);
hdrsiz = ipsec_hdrsiz(m, IPSEC_DIR_OUTBOUND, inp);
}
m_free(m);
return (hdrsiz);
}
#endif /* IPSEC */
#ifdef TCP_SIGNATURE
/*
* Callback function invoked by m_apply() to digest TCP segment data
* contained within an mbuf chain.
*/
static int
tcp_signature_apply(void *fstate, void *data, u_int len)
{
MD5Update(fstate, (u_char *)data, len);
return (0);
}
/*
* XXX The key is retrieved from the system's PF_KEY SADB, by keying a
* search with the destination IP address, and a 'magic SPI' to be
* determined by the application. This is hardcoded elsewhere to 1179
*/
struct secasvar *
tcp_get_sav(struct mbuf *m, u_int direction)
{
union sockaddr_union dst;
struct secasvar *sav;
struct ip *ip;
#ifdef INET6
struct ip6_hdr *ip6;
char ip6buf[INET6_ADDRSTRLEN];
#endif
/* Extract the destination from the IP header in the mbuf. */
bzero(&dst, sizeof(union sockaddr_union));
ip = mtod(m, struct ip *);
#ifdef INET6
ip6 = NULL; /* Make the compiler happy. */
#endif
switch (ip->ip_v) {
#ifdef INET
case IPVERSION:
dst.sa.sa_len = sizeof(struct sockaddr_in);
dst.sa.sa_family = AF_INET;
dst.sin.sin_addr = (direction == IPSEC_DIR_INBOUND) ?
ip->ip_src : ip->ip_dst;
break;
#endif
#ifdef INET6
case (IPV6_VERSION >> 4):
ip6 = mtod(m, struct ip6_hdr *);
dst.sa.sa_len = sizeof(struct sockaddr_in6);
dst.sa.sa_family = AF_INET6;
dst.sin6.sin6_addr = (direction == IPSEC_DIR_INBOUND) ?
ip6->ip6_src : ip6->ip6_dst;
break;
#endif
default:
return (NULL);
/* NOTREACHED */
break;
}
/* Look up an SADB entry which matches the address of the peer. */
sav = KEY_ALLOCSA(&dst, IPPROTO_TCP, htonl(TCP_SIG_SPI));
if (sav == NULL) {
ipseclog((LOG_ERR, "%s: SADB lookup failed for %s\n", __func__,
(ip->ip_v == IPVERSION) ? inet_ntoa(dst.sin.sin_addr) :
#ifdef INET6
(ip->ip_v == (IPV6_VERSION >> 4)) ?
ip6_sprintf(ip6buf, &dst.sin6.sin6_addr) :
#endif
"(unsupported)"));
}
return (sav);
}
/*
* Compute TCP-MD5 hash of a TCP segment. (RFC2385)
*
* Parameters:
* m pointer to head of mbuf chain
* len length of TCP segment data, excluding options
* optlen length of TCP segment options
* buf pointer to storage for computed MD5 digest
* sav pointer to security assosiation
*
* We do this over ip, tcphdr, segment data, and the key in the SADB.
* When called from tcp_input(), we can be sure that th_sum has been
* zeroed out and verified already.
*
* Releases reference to SADB key before return.
*
* Return 0 if successful, otherwise return -1.
*
*/
int
tcp_signature_do_compute(struct mbuf *m, int len, int optlen,
u_char *buf, struct secasvar *sav)
{
#ifdef INET
struct ippseudo ippseudo;
#endif
MD5_CTX ctx;
int doff;
struct ip *ip;
#ifdef INET
struct ipovly *ipovly;
#endif
struct tcphdr *th;
#ifdef INET6
struct ip6_hdr *ip6;
struct in6_addr in6;
uint32_t plen;
uint16_t nhdr;
#endif
u_short savecsum;
KASSERT(m != NULL, ("NULL mbuf chain"));
KASSERT(buf != NULL, ("NULL signature pointer"));
/* Extract the destination from the IP header in the mbuf. */
ip = mtod(m, struct ip *);
#ifdef INET6
ip6 = NULL; /* Make the compiler happy. */
#endif
MD5Init(&ctx);
/*
* Step 1: Update MD5 hash with IP(v6) pseudo-header.
*
* XXX The ippseudo header MUST be digested in network byte order,
* or else we'll fail the regression test. Assume all fields we've
* been doing arithmetic on have been in host byte order.
* XXX One cannot depend on ipovly->ih_len here. When called from
* tcp_output(), the underlying ip_len member has not yet been set.
*/
switch (ip->ip_v) {
#ifdef INET
case IPVERSION:
ipovly = (struct ipovly *)ip;
ippseudo.ippseudo_src = ipovly->ih_src;
ippseudo.ippseudo_dst = ipovly->ih_dst;
ippseudo.ippseudo_pad = 0;
ippseudo.ippseudo_p = IPPROTO_TCP;
ippseudo.ippseudo_len = htons(len + sizeof(struct tcphdr) +
optlen);
MD5Update(&ctx, (char *)&ippseudo, sizeof(struct ippseudo));
th = (struct tcphdr *)((u_char *)ip + sizeof(struct ip));
doff = sizeof(struct ip) + sizeof(struct tcphdr) + optlen;
break;
#endif
#ifdef INET6
/*
* RFC 2385, 2.0 Proposal
* For IPv6, the pseudo-header is as described in RFC 2460, namely the
* 128-bit source IPv6 address, 128-bit destination IPv6 address, zero-
* extended next header value (to form 32 bits), and 32-bit segment
* length.
* Note: Upper-Layer Packet Length comes before Next Header.
*/
case (IPV6_VERSION >> 4):
ip6 = mtod(m, struct ip6_hdr *);
in6 = ip6->ip6_src;
in6_clearscope(&in6);
MD5Update(&ctx, (char *)&in6, sizeof(struct in6_addr));
in6 = ip6->ip6_dst;
in6_clearscope(&in6);
MD5Update(&ctx, (char *)&in6, sizeof(struct in6_addr));
plen = htonl(len + sizeof(struct tcphdr) + optlen);
MD5Update(&ctx, (char *)&plen, sizeof(uint32_t));
nhdr = 0;
MD5Update(&ctx, (char *)&nhdr, sizeof(uint8_t));
MD5Update(&ctx, (char *)&nhdr, sizeof(uint8_t));
MD5Update(&ctx, (char *)&nhdr, sizeof(uint8_t));
nhdr = IPPROTO_TCP;
MD5Update(&ctx, (char *)&nhdr, sizeof(uint8_t));
th = (struct tcphdr *)((u_char *)ip6 + sizeof(struct ip6_hdr));
doff = sizeof(struct ip6_hdr) + sizeof(struct tcphdr) + optlen;
break;
#endif
default:
KEY_FREESAV(&sav);
return (-1);
/* NOTREACHED */
break;
}
/*
* Step 2: Update MD5 hash with TCP header, excluding options.
* The TCP checksum must be set to zero.
*/
savecsum = th->th_sum;
th->th_sum = 0;
MD5Update(&ctx, (char *)th, sizeof(struct tcphdr));
th->th_sum = savecsum;
/*
* Step 3: Update MD5 hash with TCP segment data.
* Use m_apply() to avoid an early m_pullup().
*/
if (len > 0)
m_apply(m, doff, len, tcp_signature_apply, &ctx);
/*
* Step 4: Update MD5 hash with shared secret.
*/
MD5Update(&ctx, sav->key_auth->key_data, _KEYLEN(sav->key_auth));
MD5Final(buf, &ctx);
key_sa_recordxfer(sav, m);
KEY_FREESAV(&sav);
return (0);
}
/*
* Compute TCP-MD5 hash of a TCP segment. (RFC2385)
*
* Return 0 if successful, otherwise return -1.
*/
int
tcp_signature_compute(struct mbuf *m, int _unused, int len, int optlen,
u_char *buf, u_int direction)
{
struct secasvar *sav;
if ((sav = tcp_get_sav(m, direction)) == NULL)
return (-1);
return (tcp_signature_do_compute(m, len, optlen, buf, sav));
}
/*
* Verify the TCP-MD5 hash of a TCP segment. (RFC2385)
*
* Parameters:
* m pointer to head of mbuf chain
* len length of TCP segment data, excluding options
* optlen length of TCP segment options
* buf pointer to storage for computed MD5 digest
* direction direction of flow (IPSEC_DIR_INBOUND or OUTBOUND)
*
* Return 1 if successful, otherwise return 0.
*/
int
tcp_signature_verify(struct mbuf *m, int off0, int tlen, int optlen,
struct tcpopt *to, struct tcphdr *th, u_int tcpbflag)
{
char tmpdigest[TCP_SIGLEN];
if (tcp_sig_checksigs == 0)
return (1);
if ((tcpbflag & TF_SIGNATURE) == 0) {
if ((to->to_flags & TOF_SIGNATURE) != 0) {
/*
* If this socket is not expecting signature but
* the segment contains signature just fail.
*/
TCPSTAT_INC(tcps_sig_err_sigopt);
TCPSTAT_INC(tcps_sig_rcvbadsig);
return (0);
}
/* Signature is not expected, and not present in segment. */
return (1);
}
/*
* If this socket is expecting signature but the segment does not
* contain any just fail.
*/
if ((to->to_flags & TOF_SIGNATURE) == 0) {
TCPSTAT_INC(tcps_sig_err_nosigopt);
TCPSTAT_INC(tcps_sig_rcvbadsig);
return (0);
}
if (tcp_signature_compute(m, off0, tlen, optlen, &tmpdigest[0],
IPSEC_DIR_INBOUND) == -1) {
TCPSTAT_INC(tcps_sig_err_buildsig);
TCPSTAT_INC(tcps_sig_rcvbadsig);
return (0);
}
if (bcmp(to->to_signature, &tmpdigest[0], TCP_SIGLEN) != 0) {
TCPSTAT_INC(tcps_sig_rcvbadsig);
return (0);
}
TCPSTAT_INC(tcps_sig_rcvgoodsig);
return (1);
}
#endif /* TCP_SIGNATURE */
static int
sysctl_drop(SYSCTL_HANDLER_ARGS)
{

View File

@ -96,13 +96,7 @@ __FBSDID("$FreeBSD$");
#include <netinet/toecore.h>
#endif
#ifdef IPSEC
#include <netipsec/ipsec.h>
#ifdef INET6
#include <netipsec/ipsec6.h>
#endif
#include <netipsec/key.h>
#endif /*IPSEC*/
#include <netipsec/ipsec_support.h>
#include <machine/in_cksum.h>
@ -744,11 +738,6 @@ syncache_socket(struct syncache *sc, struct socket *lso, struct mbuf *m)
INP_HASH_WUNLOCK(&V_tcbinfo);
goto abort;
}
#ifdef IPSEC
/* Copy old policy into new socket's. */
if (ipsec_copy_policy(sotoinpcb(lso)->inp_sp, inp->inp_sp))
printf("syncache_socket: could not copy policy\n");
#endif
#ifdef INET6
if (sc->sc_inc.inc_flags & INC_ISIPV6) {
struct inpcb *oinp = sotoinpcb(lso);
@ -830,6 +819,11 @@ syncache_socket(struct syncache *sc, struct socket *lso, struct mbuf *m)
}
}
#endif /* INET */
#if defined(IPSEC) || defined(IPSEC_SUPPORT)
/* Copy old policy into new socket's. */
if (ipsec_copy_pcbpolicy(sotoinpcb(lso), inp) != 0)
printf("syncache_socket: could not copy policy\n");
#endif
INP_HASH_WUNLOCK(&V_tcbinfo);
tp = intotcpcb(inp);
tcp_state_change(tp, TCPS_SYN_RECEIVED);
@ -880,7 +874,7 @@ syncache_socket(struct syncache *sc, struct socket *lso, struct mbuf *m)
tp->ts_recent_age = tcp_ts_getticks();
tp->ts_offset = sc->sc_tsoff;
}
#ifdef TCP_SIGNATURE
#if defined(IPSEC_SUPPORT) || defined(TCP_SIGNATURE)
if (sc->sc_flags & SCF_SIGNATURE)
tp->t_flags |= TF_SIGNATURE;
#endif
@ -1004,7 +998,57 @@ syncache_expand(struct in_conninfo *inc, struct tcpopt *to, struct tcphdr *th,
"(probably spoofed)\n", s, __func__);
goto failed;
}
#if defined(IPSEC_SUPPORT) || defined(TCP_SIGNATURE)
/* If received ACK has MD5 signature, check it. */
if ((to->to_flags & TOF_SIGNATURE) != 0 &&
(!TCPMD5_ENABLED() ||
TCPMD5_INPUT(m, th, to->to_signature) != 0)) {
/* Drop the ACK. */
if ((s = tcp_log_addrs(inc, th, NULL, NULL))) {
log(LOG_DEBUG, "%s; %s: Segment rejected, "
"MD5 signature doesn't match.\n",
s, __func__);
free(s, M_TCPLOG);
}
TCPSTAT_INC(tcps_sig_err_sigopt);
return (-1); /* Do not send RST */
}
#endif /* TCP_SIGNATURE */
} else {
#if defined(IPSEC_SUPPORT) || defined(TCP_SIGNATURE)
/*
* If listening socket requested TCP digests, check that
* received ACK has signature and it is correct.
* If not, drop the ACK and leave sc entry in th cache,
* because SYN was received with correct signature.
*/
if (sc->sc_flags & SCF_SIGNATURE) {
if ((to->to_flags & TOF_SIGNATURE) == 0) {
/* No signature */
TCPSTAT_INC(tcps_sig_err_nosigopt);
SCH_UNLOCK(sch);
if ((s = tcp_log_addrs(inc, th, NULL, NULL))) {
log(LOG_DEBUG, "%s; %s: Segment "
"rejected, MD5 signature wasn't "
"provided.\n", s, __func__);
free(s, M_TCPLOG);
}
return (-1); /* Do not send RST */
}
if (!TCPMD5_ENABLED() ||
TCPMD5_INPUT(m, th, to->to_signature) != 0) {
/* Doesn't match or no SA */
SCH_UNLOCK(sch);
if ((s = tcp_log_addrs(inc, th, NULL, NULL))) {
log(LOG_DEBUG, "%s; %s: Segment "
"rejected, MD5 signature doesn't "
"match.\n", s, __func__);
free(s, M_TCPLOG);
}
return (-1); /* Do not send RST */
}
}
#endif /* TCP_SIGNATURE */
/*
* Pull out the entry to unlock the bucket row.
*
@ -1274,6 +1318,22 @@ syncache_add(struct in_conninfo *inc, struct tcpopt *to, struct tcphdr *th,
ipopts = NULL;
#endif
#if defined(IPSEC_SUPPORT) || defined(TCP_SIGNATURE)
/*
* If listening socket requested TCP digests, check that received
* SYN has signature and it is correct. If signature doesn't match
* or TCP_SIGNATURE support isn't enabled, drop the packet.
*/
if (ltflags & TF_SIGNATURE) {
if ((to->to_flags & TOF_SIGNATURE) == 0) {
TCPSTAT_INC(tcps_sig_err_nosigopt);
goto done;
}
if (!TCPMD5_ENABLED() ||
TCPMD5_INPUT(m, th, to->to_signature) != 0)
goto done;
}
#endif /* TCP_SIGNATURE */
/*
* See if we already have an entry for this connection.
* If we do, resend the SYN,ACK, and reset the retransmit timer.
@ -1449,15 +1509,15 @@ syncache_add(struct in_conninfo *inc, struct tcpopt *to, struct tcphdr *th,
sc->sc_flags |= SCF_WINSCALE;
}
}
#ifdef TCP_SIGNATURE
#if defined(IPSEC_SUPPORT) || defined(TCP_SIGNATURE)
/*
* If listening socket requested TCP digests, OR received SYN
* contains the option, flag this in the syncache so that
* syncache_respond() will do the right thing with the SYN+ACK.
* If listening socket requested TCP digests, flag this in the
* syncache so that syncache_respond() will do the right thing
* with the SYN+ACK.
*/
if (to->to_flags & TOF_SIGNATURE || ltflags & TF_SIGNATURE)
if (ltflags & TF_SIGNATURE)
sc->sc_flags |= SCF_SIGNATURE;
#endif
#endif /* TCP_SIGNATURE */
if (to->to_flags & TOF_SACKPERM)
sc->sc_flags |= SCF_SACK;
if (to->to_flags & TOF_MSS)
@ -1548,10 +1608,6 @@ syncache_respond(struct syncache *sc, struct syncache_head *sch, int locked,
#ifdef INET6
struct ip6_hdr *ip6 = NULL;
#endif
#ifdef TCP_SIGNATURE
struct secasvar *sav;
#endif
hlen =
#ifdef INET6
(sc->sc_inc.inc_flags & INC_ISIPV6) ? sizeof(struct ip6_hdr) :
@ -1660,32 +1716,10 @@ syncache_respond(struct syncache *sc, struct syncache_head *sch, int locked,
}
if (sc->sc_flags & SCF_SACK)
to.to_flags |= TOF_SACKPERM;
#ifdef TCP_SIGNATURE
sav = NULL;
if (sc->sc_flags & SCF_SIGNATURE) {
sav = tcp_get_sav(m, IPSEC_DIR_OUTBOUND);
if (sav != NULL)
to.to_flags |= TOF_SIGNATURE;
else {
/*
* We've got SCF_SIGNATURE flag
* inherited from listening socket,
* but no SADB key for given source
* address. Assume signature is not
* required and remove signature flag
* instead of silently dropping
* connection.
*/
if (locked == 0)
SCH_LOCK(sch);
sc->sc_flags &= ~SCF_SIGNATURE;
if (locked == 0)
SCH_UNLOCK(sch);
}
}
#if defined(IPSEC_SUPPORT) || defined(TCP_SIGNATURE)
if (sc->sc_flags & SCF_SIGNATURE)
to.to_flags |= TOF_SIGNATURE;
#endif
#ifdef TCP_RFC7413
if (sc->sc_tfo_cookie) {
to.to_flags |= TOF_FASTOPEN;
@ -1701,18 +1735,25 @@ syncache_respond(struct syncache *sc, struct syncache_head *sch, int locked,
th->th_off = (sizeof(struct tcphdr) + optlen) >> 2;
m->m_len += optlen;
m->m_pkthdr.len += optlen;
#ifdef TCP_SIGNATURE
if (sc->sc_flags & SCF_SIGNATURE)
tcp_signature_do_compute(m, 0, optlen,
to.to_signature, sav);
#endif
#ifdef INET6
if (sc->sc_inc.inc_flags & INC_ISIPV6)
ip6->ip6_plen = htons(ntohs(ip6->ip6_plen) + optlen);
else
#endif
ip->ip_len = htons(ntohs(ip->ip_len) + optlen);
#if defined(IPSEC_SUPPORT) || defined(TCP_SIGNATURE)
if (sc->sc_flags & SCF_SIGNATURE) {
KASSERT(to.to_flags & TOF_SIGNATURE,
("tcp_addoptions() didn't set tcp_signature"));
/* NOTE: to.to_signature is inside of mbuf */
if (!TCPMD5_ENABLED() ||
TCPMD5_OUTPUT(m, th, to.to_signature) != 0) {
m_freem(m);
return (EACCES);
}
}
#endif
} else
optlen = 0;

View File

@ -41,6 +41,7 @@ __FBSDID("$FreeBSD$");
#include "opt_ddb.h"
#include "opt_inet.h"
#include "opt_inet6.h"
#include "opt_ipsec.h"
#include "opt_tcpdebug.h"
#include <sys/param.h>
@ -101,6 +102,7 @@ __FBSDID("$FreeBSD$");
#ifdef TCP_OFFLOAD
#include <netinet/tcp_offload.h>
#endif
#include <netipsec/ipsec_support.h>
/*
* TCP protocol interface to socket abstraction.
@ -1552,21 +1554,17 @@ tcp_default_ctloutput(struct socket *so, struct sockopt *sopt, struct inpcb *inp
switch (sopt->sopt_dir) {
case SOPT_SET:
switch (sopt->sopt_name) {
#ifdef TCP_SIGNATURE
#if defined(IPSEC_SUPPORT) || defined(TCP_SIGNATURE)
case TCP_MD5SIG:
INP_WUNLOCK(inp);
error = sooptcopyin(sopt, &optval, sizeof optval,
sizeof optval);
if (!TCPMD5_ENABLED()) {
INP_WUNLOCK(inp);
return (ENOPROTOOPT);
}
error = TCPMD5_PCBCTL(inp, sopt);
if (error)
return (error);
INP_WLOCK_RECHECK(inp);
if (optval > 0)
tp->t_flags |= TF_SIGNATURE;
else
tp->t_flags &= ~TF_SIGNATURE;
goto unlock_and_done;
#endif /* TCP_SIGNATURE */
#endif /* IPSEC */
case TCP_NODELAY:
case TCP_NOOPT:
@ -1792,11 +1790,13 @@ tcp_default_ctloutput(struct socket *so, struct sockopt *sopt, struct inpcb *inp
case SOPT_GET:
tp = intotcpcb(inp);
switch (sopt->sopt_name) {
#ifdef TCP_SIGNATURE
#if defined(IPSEC_SUPPORT) || defined(TCP_SIGNATURE)
case TCP_MD5SIG:
optval = (tp->t_flags & TF_SIGNATURE) ? 1 : 0;
INP_WUNLOCK(inp);
error = sooptcopyout(sopt, &optval, sizeof optval);
if (!TCPMD5_ENABLED()) {
INP_WUNLOCK(inp);
return (ENOPROTOOPT);
}
error = TCPMD5_PCBCTL(inp, sopt);
break;
#endif

View File

@ -363,21 +363,6 @@ struct tcpcb {
#define TCPOOB_HAVEDATA 0x01
#define TCPOOB_HADDATA 0x02
#ifdef TCP_SIGNATURE
/*
* Defines which are needed by the xform_tcp module and tcp_[in|out]put
* for SADB verification and lookup.
*/
#define TCP_SIGLEN 16 /* length of computed digest in bytes */
#define TCP_KEYLEN_MIN 1 /* minimum length of TCP-MD5 key */
#define TCP_KEYLEN_MAX 80 /* maximum length of TCP-MD5 key */
/*
* Only a single SA per host may be specified at this time. An SPI is
* needed in order for the KEY_ALLOCSA() lookup to work.
*/
#define TCP_SIG_SPI 0x1000
#endif /* TCP_SIGNATURE */
/*
* Flags for PLPMTU handling, t_flags2
*/
@ -614,7 +599,7 @@ struct tcpstat {
/* TCP_SIGNATURE related stats */
uint64_t tcps_sig_rcvgoodsig; /* Total matching signature received */
uint64_t tcps_sig_rcvbadsig; /* Total bad signature received */
uint64_t tcps_sig_err_buildsig; /* Mismatching signature received */
uint64_t tcps_sig_err_buildsig; /* Failed to make signature */
uint64_t tcps_sig_err_sigopt; /* No signature expected by socket */
uint64_t tcps_sig_err_nosigopt; /* No signature provided by segment */
@ -835,17 +820,6 @@ void tcp_tw_zone_change(void);
int tcp_twcheck(struct inpcb *, struct tcpopt *, struct tcphdr *,
struct mbuf *, int);
void tcp_setpersist(struct tcpcb *);
#ifdef TCP_SIGNATURE
struct secasvar;
struct secasvar *tcp_get_sav(struct mbuf *, u_int);
int tcp_signature_do_compute(struct mbuf *, int, int, u_char *,
struct secasvar *);
int tcp_signature_compute(struct mbuf *, int, int, int, u_char *, u_int);
int tcp_signature_verify(struct mbuf *, int, int, int, struct tcpopt *,
struct tcphdr *, u_int);
int tcp_signature_check(struct mbuf *m, int off0, int tlen, int optlen,
struct tcpopt *to, struct tcphdr *th, u_int tcpbflag);
#endif
void tcp_slowtimo(void);
struct tcptemp *
tcpip_maketemplate(struct inpcb *);
@ -889,17 +863,6 @@ tcp_fields_to_host(struct tcphdr *th)
th->th_urp = ntohs(th->th_urp);
}
#ifdef TCP_SIGNATURE
static inline void
tcp_fields_to_net(struct tcphdr *th)
{
th->th_seq = htonl(th->th_seq);
th->th_ack = htonl(th->th_ack);
th->th_win = htons(th->th_win);
th->th_urp = htons(th->th_urp);
}
#endif
#endif /* _KERNEL */
#endif /* _NETINET_TCP_VAR_H_ */

View File

@ -58,7 +58,7 @@ struct udphdr {
*/
/* Encapsulation types. */
#define UDP_ENCAP_ESPINUDP_NON_IKE 1 /* draft-ietf-ipsec-nat-t-ike-00/01 */
#define UDP_ENCAP_ESPINUDP 2 /* draft-ietf-ipsec-udp-encaps-02+ */
#define UDP_ENCAP_ESPINUDP 2 /* RFC3948 */
/* Default ESP in UDP encapsulation port. */
#define UDP_ENCAP_ESPINUDP_PORT 500

View File

@ -92,10 +92,7 @@ __FBSDID("$FreeBSD$");
#include <netinet/udplite.h>
#include <netinet/in_rss.h>
#ifdef IPSEC
#include <netipsec/ipsec.h>
#include <netipsec/esp.h>
#endif
#include <netipsec/ipsec_support.h>
#include <machine/in_cksum.h>
@ -172,15 +169,6 @@ static int udp_output(struct inpcb *, struct mbuf *, struct sockaddr *,
struct mbuf *, struct thread *);
#endif
#ifdef IPSEC
#ifdef IPSEC_NAT_T
#define UF_ESPINUDP_ALL (UF_ESPINUDP_NON_IKE|UF_ESPINUDP)
#ifdef INET
static struct mbuf *udp4_espdecap(struct inpcb *, struct mbuf *, int);
#endif
#endif /* IPSEC_NAT_T */
#endif /* IPSEC */
static void
udp_zone_change(void *tag)
{
@ -339,21 +327,18 @@ udp_append(struct inpcb *inp, struct ip *ip, struct mbuf *n, int off,
off += sizeof(struct udphdr);
#ifdef IPSEC
#if defined(IPSEC) || defined(IPSEC_SUPPORT)
/* Check AH/ESP integrity. */
if (ipsec4_in_reject(n, inp)) {
if (IPSEC_ENABLED(ipv4) &&
IPSEC_CHECK_POLICY(ipv4, n, inp) != 0) {
m_freem(n);
return (0);
}
#ifdef IPSEC_NAT_T
up = intoudpcb(inp);
KASSERT(up != NULL, ("%s: udpcb NULL", __func__));
if (up->u_flags & UF_ESPINUDP_ALL) { /* IPSec UDP encaps. */
n = udp4_espdecap(inp, n, off);
if (n == NULL) /* Consumed. */
return (0);
if (up->u_flags & UF_ESPINUDP) {/* IPSec UDP encaps. */
if (IPSEC_ENABLED(ipv4) &&
UDPENCAP_INPUT(n, off, AF_INET) != 0)
return (0); /* Consumed. */
}
#endif /* IPSEC_NAT_T */
#endif /* IPSEC */
#ifdef MAC
if (mac_inpcb_check_deliver(inp, n) != 0) {
@ -1021,42 +1006,17 @@ udp_ctloutput(struct socket *so, struct sockopt *sopt)
switch (sopt->sopt_dir) {
case SOPT_SET:
switch (sopt->sopt_name) {
#if defined(IPSEC) || defined(IPSEC_SUPPORT)
#ifdef INET
case UDP_ENCAP:
INP_WUNLOCK(inp);
error = sooptcopyin(sopt, &optval, sizeof optval,
sizeof optval);
if (error)
break;
inp = sotoinpcb(so);
KASSERT(inp != NULL, ("%s: inp == NULL", __func__));
INP_WLOCK(inp);
#ifdef IPSEC_NAT_T
up = intoudpcb(inp);
KASSERT(up != NULL, ("%s: up == NULL", __func__));
#endif
switch (optval) {
case 0:
/* Clear all UDP encap. */
#ifdef IPSEC_NAT_T
up->u_flags &= ~UF_ESPINUDP_ALL;
#endif
break;
#ifdef IPSEC_NAT_T
case UDP_ENCAP_ESPINUDP:
case UDP_ENCAP_ESPINUDP_NON_IKE:
up->u_flags &= ~UF_ESPINUDP_ALL;
if (optval == UDP_ENCAP_ESPINUDP)
up->u_flags |= UF_ESPINUDP;
else if (optval == UDP_ENCAP_ESPINUDP_NON_IKE)
up->u_flags |= UF_ESPINUDP_NON_IKE;
break;
#endif
default:
error = EINVAL;
break;
if (!IPSEC_ENABLED(ipv4)) {
INP_WUNLOCK(inp);
return (ENOPROTOOPT);
}
INP_WUNLOCK(inp);
error = UDPENCAP_PCBCTL(inp, sopt);
break;
#endif /* INET */
#endif /* IPSEC */
case UDPLITE_SEND_CSCOV:
case UDPLITE_RECV_CSCOV:
if (!isudplite) {
@ -1093,15 +1053,17 @@ udp_ctloutput(struct socket *so, struct sockopt *sopt)
break;
case SOPT_GET:
switch (sopt->sopt_name) {
#ifdef IPSEC_NAT_T
#if defined(IPSEC) || defined(IPSEC_SUPPORT)
#ifdef INET
case UDP_ENCAP:
up = intoudpcb(inp);
KASSERT(up != NULL, ("%s: up == NULL", __func__));
optval = up->u_flags & UF_ESPINUDP_ALL;
INP_WUNLOCK(inp);
error = sooptcopyout(sopt, &optval, sizeof optval);
if (!IPSEC_ENABLED(ipv4)) {
INP_WUNLOCK(inp);
return (ENOPROTOOPT);
}
error = UDPENCAP_PCBCTL(inp, sopt);
break;
#endif
#endif /* INET */
#endif /* IPSEC */
case UDPLITE_SEND_CSCOV:
case UDPLITE_RECV_CSCOV:
if (!isudplite) {
@ -1584,142 +1546,6 @@ udp_output(struct inpcb *inp, struct mbuf *m, struct sockaddr *addr,
return (error);
}
#if defined(IPSEC) && defined(IPSEC_NAT_T)
/*
* Potentially decap ESP in UDP frame. Check for an ESP header
* and optional marker; if present, strip the UDP header and
* push the result through IPSec.
*
* Returns mbuf to be processed (potentially re-allocated) or
* NULL if consumed and/or processed.
*/
static struct mbuf *
udp4_espdecap(struct inpcb *inp, struct mbuf *m, int off)
{
size_t minlen, payload, skip, iphlen;
caddr_t data;
struct udpcb *up;
struct m_tag *tag;
struct udphdr *udphdr;
struct ip *ip;
INP_RLOCK_ASSERT(inp);
/*
* Pull up data so the longest case is contiguous:
* IP/UDP hdr + non ESP marker + ESP hdr.
*/
minlen = off + sizeof(uint64_t) + sizeof(struct esp);
if (minlen > m->m_pkthdr.len)
minlen = m->m_pkthdr.len;
if ((m = m_pullup(m, minlen)) == NULL) {
IPSECSTAT_INC(ips_in_inval);
return (NULL); /* Bypass caller processing. */
}
data = mtod(m, caddr_t); /* Points to ip header. */
payload = m->m_len - off; /* Size of payload. */
if (payload == 1 && data[off] == '\xff')
return (m); /* NB: keepalive packet, no decap. */
up = intoudpcb(inp);
KASSERT(up != NULL, ("%s: udpcb NULL", __func__));
KASSERT((up->u_flags & UF_ESPINUDP_ALL) != 0,
("u_flags 0x%x", up->u_flags));
/*
* Check that the payload is large enough to hold an
* ESP header and compute the amount of data to remove.
*
* NB: the caller has already done a pullup for us.
* XXX can we assume alignment and eliminate bcopys?
*/
if (up->u_flags & UF_ESPINUDP_NON_IKE) {
/*
* draft-ietf-ipsec-nat-t-ike-0[01].txt and
* draft-ietf-ipsec-udp-encaps-(00/)01.txt, ignoring
* possible AH mode non-IKE marker+non-ESP marker
* from draft-ietf-ipsec-udp-encaps-00.txt.
*/
uint64_t marker;
if (payload <= sizeof(uint64_t) + sizeof(struct esp))
return (m); /* NB: no decap. */
bcopy(data + off, &marker, sizeof(uint64_t));
if (marker != 0) /* Non-IKE marker. */
return (m); /* NB: no decap. */
skip = sizeof(uint64_t) + sizeof(struct udphdr);
} else {
uint32_t spi;
if (payload <= sizeof(struct esp)) {
IPSECSTAT_INC(ips_in_inval);
m_freem(m);
return (NULL); /* Discard. */
}
bcopy(data + off, &spi, sizeof(uint32_t));
if (spi == 0) /* Non-ESP marker. */
return (m); /* NB: no decap. */
skip = sizeof(struct udphdr);
}
/*
* Setup a PACKET_TAG_IPSEC_NAT_T_PORT tag to remember
* the UDP ports. This is required if we want to select
* the right SPD for multiple hosts behind same NAT.
*
* NB: ports are maintained in network byte order everywhere
* in the NAT-T code.
*/
tag = m_tag_get(PACKET_TAG_IPSEC_NAT_T_PORTS,
2 * sizeof(uint16_t), M_NOWAIT);
if (tag == NULL) {
IPSECSTAT_INC(ips_in_nomem);
m_freem(m);
return (NULL); /* Discard. */
}
iphlen = off - sizeof(struct udphdr);
udphdr = (struct udphdr *)(data + iphlen);
((uint16_t *)(tag + 1))[0] = udphdr->uh_sport;
((uint16_t *)(tag + 1))[1] = udphdr->uh_dport;
m_tag_prepend(m, tag);
/*
* Remove the UDP header (and possibly the non ESP marker)
* IP header length is iphlen
* Before:
* <--- off --->
* +----+------+-----+
* | IP | UDP | ESP |
* +----+------+-----+
* <-skip->
* After:
* +----+-----+
* | IP | ESP |
* +----+-----+
* <-skip->
*/
ovbcopy(data, data + skip, iphlen);
m_adj(m, skip);
ip = mtod(m, struct ip *);
ip->ip_len = htons(ntohs(ip->ip_len) - skip);
ip->ip_p = IPPROTO_ESP;
/*
* We cannot yet update the cksums so clear any
* h/w cksum flags as they are no longer valid.
*/
if (m->m_pkthdr.csum_flags & CSUM_DATA_VALID)
m->m_pkthdr.csum_flags &= ~(CSUM_DATA_VALID|CSUM_PSEUDO_HDR);
(void) ipsec_common_input(m, iphlen, offsetof(struct ip, ip_p),
AF_INET, ip->ip_p);
return (NULL); /* NB: consumed, bypass processing. */
}
#endif /* defined(IPSEC) && defined(IPSEC_NAT_T) */
static void
udp_abort(struct socket *so)
{

View File

@ -432,10 +432,7 @@ struct route_in6 {
#define IPV6_BINDV6ONLY IPV6_V6ONLY
#endif
#if 1 /* IPSEC */
#define IPV6_IPSEC_POLICY 28 /* struct; get/set security policy */
#endif /* IPSEC */
/* 29; unused; was IPV6_FAITH */
#if 1 /* IPV6FIREWALL */
#define IPV6_FW_ADD 30 /* add a firewall rule to chain */

View File

@ -121,11 +121,6 @@ __FBSDID("$FreeBSD$");
#include <netinet6/sctp6_var.h>
#endif /* SCTP */
#ifdef IPSEC
#include <netipsec/ipsec.h>
#include <netipsec/ipsec6.h>
#endif /* IPSEC */
#include <netinet6/ip6protosw.h>
/*
@ -276,33 +271,6 @@ struct protosw inet6sw[] = {
.pr_input = frag6_input,
.pr_usrreqs = &nousrreqs
},
#ifdef IPSEC
{
.pr_type = SOCK_RAW,
.pr_domain = &inet6domain,
.pr_protocol = IPPROTO_AH,
.pr_flags = PR_ATOMIC|PR_ADDR,
.pr_input = ipsec6_common_input,
.pr_usrreqs = &nousrreqs,
},
{
.pr_type = SOCK_RAW,
.pr_domain = &inet6domain,
.pr_protocol = IPPROTO_ESP,
.pr_flags = PR_ATOMIC|PR_ADDR,
.pr_input = ipsec6_common_input,
.pr_ctlinput = esp6_ctlinput,
.pr_usrreqs = &nousrreqs,
},
{
.pr_type = SOCK_RAW,
.pr_domain = &inet6domain,
.pr_protocol = IPPROTO_IPCOMP,
.pr_flags = PR_ATOMIC|PR_ADDR,
.pr_input = ipsec6_common_input,
.pr_usrreqs = &nousrreqs,
},
#endif /* IPSEC */
#ifdef INET
{
.pr_type = SOCK_RAW,
@ -470,7 +438,7 @@ SYSCTL_NODE(_net_inet6, IPPROTO_TCP, tcp6, CTLFLAG_RW, 0, "TCP6");
#ifdef SCTP
SYSCTL_NODE(_net_inet6, IPPROTO_SCTP, sctp6, CTLFLAG_RW, 0, "SCTP6");
#endif
#ifdef IPSEC
#if defined(IPSEC) || defined(IPSEC_SUPPORT)
SYSCTL_NODE(_net_inet6, IPPROTO_ESP, ipsec6, CTLFLAG_RW, 0, "IPSEC6");
#endif /* IPSEC */

View File

@ -69,12 +69,7 @@ __FBSDID("$FreeBSD$");
#include <netinet/in_pcb.h>
#ifdef IPSEC
#include <netinet6/ip6_ipsec.h>
#include <netipsec/ipsec.h>
#include <netipsec/ipsec6.h>
#include <netipsec/key.h>
#endif /* IPSEC */
#include <netipsec/ipsec_support.h>
/*
* Forward a packet. If some error occurs return the sender
@ -100,9 +95,6 @@ ip6_forward(struct mbuf *m, int srcrt)
struct ifnet *origifp; /* maybe unnecessary */
u_int32_t inzone, outzone;
struct in6_addr src_in6, dst_in6, odst;
#ifdef IPSEC
struct secpolicy *sp = NULL;
#endif
struct m_tag *fwd_tag;
char ip6bufs[INET6_ADDRSTRLEN], ip6bufd[INET6_ADDRSTRLEN];
@ -130,32 +122,17 @@ ip6_forward(struct mbuf *m, int srcrt)
m_freem(m);
return;
}
#ifdef IPSEC
/*
* Check if this packet has an active SA and needs to be dropped
* instead of forwarded.
*/
if (ip6_ipsec_fwd(m) != 0) {
IP6STAT_INC(ip6s_cantforward);
m_freem(m);
return;
}
#endif /* IPSEC */
if (
#ifdef IPSTEALTH
if (!V_ip6stealth) {
V_ip6stealth == 0 &&
#endif
if (ip6->ip6_hlim <= IPV6_HLIMDEC) {
ip6->ip6_hlim <= IPV6_HLIMDEC) {
/* XXX in6_ifstat_inc(rt->rt_ifp, ifs6_in_discard) */
icmp6_error(m, ICMP6_TIME_EXCEEDED,
ICMP6_TIME_EXCEED_TRANSIT, 0);
ICMP6_TIME_EXCEED_TRANSIT, 0);
return;
}
ip6->ip6_hlim -= IPV6_HLIMDEC;
#ifdef IPSTEALTH
}
#endif
/*
* Save at most ICMPV6_PLD_MAXLEN (= the min IPv6 MTU -
@ -168,167 +145,22 @@ ip6_forward(struct mbuf *m, int srcrt)
*/
mcopy = m_copym(m, 0, imin(m->m_pkthdr.len, ICMPV6_PLD_MAXLEN),
M_NOWAIT);
#ifdef IPSTEALTH
if (V_ip6stealth == 0)
#endif
ip6->ip6_hlim -= IPV6_HLIMDEC;
#ifdef IPSEC
/* get a security policy for this packet */
sp = ipsec_getpolicybyaddr(m, IPSEC_DIR_OUTBOUND, &error);
if (sp == NULL) {
IPSEC6STAT_INC(ips_out_inval);
IP6STAT_INC(ip6s_cantforward);
if (mcopy) {
#if 0
/* XXX: what icmp ? */
#else
#if defined(IPSEC) || defined(IPSEC_SUPPORT)
if (IPSEC_ENABLED(ipv6)) {
if ((error = IPSEC_FORWARD(ipv6, m)) != 0) {
/* mbuf consumed by IPsec */
m_freem(mcopy);
#endif
}
m_freem(m);
return;
}
error = 0;
/* check policy */
switch (sp->policy) {
case IPSEC_POLICY_DISCARD:
/*
* This packet is just discarded.
*/
IPSEC6STAT_INC(ips_out_polvio);
IP6STAT_INC(ip6s_cantforward);
KEY_FREESP(&sp);
if (mcopy) {
#if 0
/* XXX: what icmp ? */
#else
m_freem(mcopy);
#endif
}
m_freem(m);
return;
case IPSEC_POLICY_BYPASS:
case IPSEC_POLICY_NONE:
/* no need to do IPsec. */
KEY_FREESP(&sp);
goto skip_ipsec;
case IPSEC_POLICY_IPSEC:
if (sp->req == NULL) {
/* XXX should be panic ? */
printf("ip6_forward: No IPsec request specified.\n");
IP6STAT_INC(ip6s_cantforward);
KEY_FREESP(&sp);
if (mcopy) {
#if 0
/* XXX: what icmp ? */
#else
m_freem(mcopy);
#endif
}
m_freem(m);
if (error != EINPROGRESS)
IP6STAT_INC(ip6s_cantforward);
return;
}
/* do IPsec */
break;
case IPSEC_POLICY_ENTRUST:
default:
/* should be panic ?? */
printf("ip6_forward: Invalid policy found. %d\n", sp->policy);
KEY_FREESP(&sp);
goto skip_ipsec;
/* No IPsec processing required */
}
{
struct ipsecrequest *isr = NULL;
/*
* when the kernel forwards a packet, it is not proper to apply
* IPsec transport mode to the packet. This check avoid from this.
* at present, if there is even a transport mode SA request in the
* security policy, the kernel does not apply IPsec to the packet.
* this check is not enough because the following case is valid.
* ipsec esp/tunnel/xxx-xxx/require esp/transport//require;
*/
for (isr = sp->req; isr; isr = isr->next) {
if (isr->saidx.mode == IPSEC_MODE_ANY)
goto doipsectunnel;
if (isr->saidx.mode == IPSEC_MODE_TUNNEL)
goto doipsectunnel;
}
/*
* if there's no need for tunnel mode IPsec, skip.
*/
if (!isr)
goto skip_ipsec;
doipsectunnel:
/*
* All the extension headers will become inaccessible
* (since they can be encrypted).
* Don't panic, we need no more updates to extension headers
* on inner IPv6 packet (since they are now encapsulated).
*
* IPv6 [ESP|AH] IPv6 [extension headers] payload
*/
/*
* If we need to encapsulate the packet, do it here
* ipsec6_proces_packet will send the packet using ip6_output
*/
error = ipsec6_process_packet(m, sp->req);
/* Release SP if an error occurred */
if (error != 0)
KEY_FREESP(&sp);
if (error == EJUSTRETURN) {
/*
* We had a SP with a level of 'use' and no SA. We
* will just continue to process the packet without
* IPsec processing.
*/
error = 0;
goto skip_ipsec;
}
if (error) {
/* mbuf is already reclaimed in ipsec6_process_packet. */
switch (error) {
case EHOSTUNREACH:
case ENETUNREACH:
case EMSGSIZE:
case ENOBUFS:
case ENOMEM:
break;
default:
printf("ip6_output (ipsec): error code %d\n", error);
/* FALLTHROUGH */
case ENOENT:
/* don't show these error codes to the user */
break;
}
IP6STAT_INC(ip6s_cantforward);
if (mcopy) {
#if 0
/* XXX: what icmp ? */
#else
m_freem(mcopy);
#endif
}
return;
} else {
/*
* In the FAST IPSec case we have already
* re-injected the packet and it has been freed
* by the ipsec_done() function. So, just clean
* up after ourselves.
*/
m = NULL;
goto freecopy;
}
}
skip_ipsec:
#endif
again:
bzero(&rin6, sizeof(struct route_in6));
@ -540,34 +372,9 @@ ip6_forward(struct mbuf *m, int srcrt)
/* See if the size was changed by the packet filter. */
if (m->m_pkthdr.len > IN6_LINKMTU(rt->rt_ifp)) {
in6_ifstat_inc(rt->rt_ifp, ifs6_in_toobig);
if (mcopy) {
u_long mtu;
#ifdef IPSEC
size_t ipsechdrsiz;
#endif /* IPSEC */
mtu = IN6_LINKMTU(rt->rt_ifp);
#ifdef IPSEC
/*
* When we do IPsec tunnel ingress, we need to play
* with the link value (decrement IPsec header size
* from mtu value). The code is much simpler than v4
* case, as we have the outgoing interface for
* encapsulated packet as "rt->rt_ifp".
*/
ipsechdrsiz = ipsec_hdrsiz(mcopy, IPSEC_DIR_OUTBOUND,
NULL);
if (ipsechdrsiz < mtu)
mtu -= ipsechdrsiz;
/*
* if mtu becomes less than minimum MTU,
* tell minimum MTU (and I'll need to fragment it).
*/
if (mtu < IPV6_MMTU)
mtu = IPV6_MMTU;
#endif /* IPSEC */
icmp6_error(mcopy, ICMP6_PACKET_TOO_BIG, 0, mtu);
}
if (mcopy)
icmp6_error(mcopy, ICMP6_PACKET_TOO_BIG, 0,
IN6_LINKMTU(rt->rt_ifp));
goto bad;
}

View File

@ -118,12 +118,7 @@ __FBSDID("$FreeBSD$");
#include <netinet6/nd6.h>
#include <netinet6/in6_rss.h>
#ifdef IPSEC
#include <netipsec/key.h>
#include <netipsec/ipsec.h>
#include <netinet6/ip6_ipsec.h>
#include <netipsec/ipsec6.h>
#endif /* IPSEC */
#include <netipsec/ipsec_support.h>
#include <netinet6/ip6protosw.h>
@ -525,14 +520,11 @@ ip6_direct_input(struct mbuf *m)
goto bad;
}
#ifdef IPSEC
/*
* enforce IPsec policy checking if we are seeing last header.
* note that we do not visit this with protocols with pcb layer
* code - like udp/tcp/raw ip.
*/
if (ip6_ipsec_input(m, nxt))
goto bad;
#if defined(IPSEC) || defined(IPSEC_SUPPORT)
if (IPSEC_ENABLED(ipv6)) {
if (IPSEC_INPUT(ipv6, m, off, nxt) != 0)
return;
}
#endif /* IPSEC */
nxt = (*inet6sw[ip6_protox[nxt]].pr_input)(&m, &off, nxt);
@ -563,7 +555,7 @@ ip6_input(struct mbuf *m)
if ((ND_IFINFO(rcvif)->flags & ND6_IFF_IFDISABLED))
goto bad;
#ifdef IPSEC
#if defined(IPSEC) || defined(IPSEC_SUPPORT)
/*
* should the inner packet be considered authentic?
* see comment in ah4_input().
@ -735,9 +727,9 @@ ip6_input(struct mbuf *m)
* ip6 pointer.
*/
if (V_ip6_forwarding != 0
#ifdef IPSEC
&& !key_havesp(IPSEC_DIR_INBOUND)
&& !key_havesp(IPSEC_DIR_OUTBOUND)
#if defined(IPSEC) || defined(IPSEC_SUPPORT)
&& (!IPSEC_ENABLED(ipv6) ||
IPSEC_CAPS(ipv6, m, IPSEC_CAP_OPERABLE) == 0)
#endif
) {
if ((m = ip6_tryforward(m)) == NULL)
@ -749,12 +741,13 @@ ip6_input(struct mbuf *m)
goto hbhcheck;
}
}
#ifdef IPSEC
#if defined(IPSEC) || defined(IPSEC_SUPPORT)
/*
* Bypass packet filtering for packets previously handled by IPsec.
*/
if (ip6_ipsec_filtertunnel(m))
goto passin;
if (IPSEC_ENABLED(ipv6) &&
IPSEC_CAPS(ipv6, m, IPSEC_CAP_BYPASS_FILTER) != 0)
goto passin;
#endif
/*
* Run through list of hooks for input packets.
@ -962,14 +955,11 @@ ip6_input(struct mbuf *m)
goto bad;
}
#ifdef IPSEC
/*
* enforce IPsec policy checking if we are seeing last header.
* note that we do not visit this with protocols with pcb layer
* code - like udp/tcp/raw ip.
*/
if (ip6_ipsec_input(m, nxt))
goto bad;
#if defined(IPSEC) || defined(IPSEC_SUPPORT)
if (IPSEC_ENABLED(ipv6)) {
if (IPSEC_INPUT(ipv6, m, off, nxt) != 0)
return;
}
#endif /* IPSEC */
nxt = (*inet6sw[ip6_protox[nxt]].pr_input)(&m, &off, nxt);

View File

@ -1,293 +0,0 @@
/*-
* Copyright (c) 1982, 1986, 1988, 1993
* The Regents of the University of California. 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.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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.
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include "opt_inet.h"
#include "opt_sctp.h"
#include "opt_ipsec.h"
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/mac.h>
#include <sys/malloc.h>
#include <sys/mbuf.h>
#include <sys/protosw.h>
#include <sys/socket.h>
#include <sys/socketvar.h>
#include <sys/sysctl.h>
#include <sys/syslog.h>
#include <net/if.h>
#include <net/if_var.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/ip6.h>
#include <netinet/in_pcb.h>
#include <netinet/ip_var.h>
#include <netinet/ip_options.h>
#ifdef SCTP
#include <netinet/sctp_crc32.h>
#endif
#include <machine/in_cksum.h>
#include <netipsec/ipsec.h>
#include <netipsec/ipsec6.h>
#include <netipsec/xform.h>
#include <netipsec/key.h>
#ifdef IPSEC_DEBUG
#include <netipsec/key_debug.h>
#else
#define KEYDEBUG(lev,arg)
#endif
#include <netinet6/ip6_ipsec.h>
#include <netinet6/ip6_var.h>
extern struct protosw inet6sw[];
static VNET_DEFINE(int, ip6_ipsec6_filtertunnel) = 0;
#define V_ip6_ipsec6_filtertunnel VNET(ip6_ipsec6_filtertunnel)
SYSCTL_DECL(_net_inet6_ipsec6);
SYSCTL_INT(_net_inet6_ipsec6, OID_AUTO, filtertunnel,
CTLFLAG_VNET | CTLFLAG_RW, &VNET_NAME(ip6_ipsec6_filtertunnel), 0,
"If set filter packets from an IPsec tunnel.");
/*
* Check if we have to jump over firewall processing for this packet.
* Called from ip6_input().
* 1 = jump over firewall, 0 = packet goes through firewall.
*/
int
ip6_ipsec_filtertunnel(struct mbuf *m)
{
/*
* Bypass packet filtering for packets previously handled by IPsec.
*/
if (!V_ip6_ipsec6_filtertunnel &&
m_tag_find(m, PACKET_TAG_IPSEC_IN_DONE, NULL) != NULL)
return (1);
return (0);
}
/*
* Check if this packet has an active SA and needs to be dropped instead
* of forwarded.
* Called from ip6_forward().
* 1 = drop packet, 0 = forward packet.
*/
int
ip6_ipsec_fwd(struct mbuf *m)
{
return (ipsec6_in_reject(m, NULL));
}
/*
* Check if protocol type doesn't have a further header and do IPSEC
* decryption or reject right now. Protocols with further headers get
* their IPSEC treatment within the protocol specific processing.
* Called from ip6_input().
* 1 = drop packet, 0 = continue processing packet.
*/
int
ip6_ipsec_input(struct mbuf *m, int nxt)
{
/*
* enforce IPsec policy checking if we are seeing last header.
* note that we do not visit this with protocols with pcb layer
* code - like udp/tcp/raw ip.
*/
if ((inet6sw[ip6_protox[nxt]].pr_flags & PR_LASTHDR) != 0)
return (ipsec6_in_reject(m, NULL));
return (0);
}
/*
* Called from ip6_output().
* 1 = drop packet, 0 = continue processing packet,
* -1 = packet was reinjected and stop processing packet
*/
int
ip6_ipsec_output(struct mbuf **m, struct inpcb *inp, int *error)
{
struct secpolicy *sp;
/*
* Check the security policy (SP) for the packet and, if
* required, do IPsec-related processing. There are two
* cases here; the first time a packet is sent through
* it will be untagged and handled by ipsec4_checkpolicy.
* If the packet is resubmitted to ip6_output (e.g. after
* AH, ESP, etc. processing), there will be a tag to bypass
* the lookup and related policy checking.
*/
if (m_tag_find(*m, PACKET_TAG_IPSEC_OUT_DONE, NULL) != NULL) {
*error = 0;
return (0);
}
sp = ipsec4_checkpolicy(*m, IPSEC_DIR_OUTBOUND, error, inp);
/*
* There are four return cases:
* sp != NULL apply IPsec policy
* sp == NULL, error == 0 no IPsec handling needed
* sp == NULL, error == -EINVAL discard packet w/o error
* sp == NULL, error != 0 discard packet, report error
*/
if (sp != NULL) {
/*
* Do delayed checksums now because we send before
* this is done in the normal processing path.
*/
#ifdef INET
if ((*m)->m_pkthdr.csum_flags & CSUM_DELAY_DATA) {
in_delayed_cksum(*m);
(*m)->m_pkthdr.csum_flags &= ~CSUM_DELAY_DATA;
}
#endif
if ((*m)->m_pkthdr.csum_flags & CSUM_DELAY_DATA_IPV6) {
in6_delayed_cksum(*m, (*m)->m_pkthdr.len - sizeof(struct ip6_hdr),
sizeof(struct ip6_hdr));
(*m)->m_pkthdr.csum_flags &= ~CSUM_DELAY_DATA_IPV6;
}
#ifdef SCTP
if ((*m)->m_pkthdr.csum_flags & CSUM_SCTP_IPV6) {
sctp_delayed_cksum(*m, sizeof(struct ip6_hdr));
(*m)->m_pkthdr.csum_flags &= ~CSUM_SCTP_IPV6;
}
#endif
/* NB: callee frees mbuf */
*error = ipsec6_process_packet(*m, sp->req);
KEY_FREESP(&sp);
if (*error == EJUSTRETURN) {
/*
* We had a SP with a level of 'use' and no SA. We
* will just continue to process the packet without
* IPsec processing.
*/
*error = 0;
goto done;
}
/*
* Preserve KAME behaviour: ENOENT can be returned
* when an SA acquire is in progress. Don't propagate
* this to user-level; it confuses applications.
*
* XXX this will go away when the SADB is redone.
*/
if (*error == ENOENT)
*error = 0;
goto reinjected;
} else { /* sp == NULL */
if (*error != 0) {
/*
* Hack: -EINVAL is used to signal that a packet
* should be silently discarded. This is typically
* because we asked key management for an SA and
* it was delayed (e.g. kicked up to IKE).
*/
if (*error == -EINVAL)
*error = 0;
goto bad;
}
/* No IPsec processing for this packet. */
}
done:
return (0);
reinjected:
return (-1);
bad:
if (sp != NULL)
KEY_FREESP(&sp);
return (1);
}
#if 0
/*
* Compute the MTU for a forwarded packet that gets IPSEC encapsulated.
* Called from ip_forward().
* Returns MTU suggestion for ICMP needfrag reply.
*/
int
ip6_ipsec_mtu(struct mbuf *m)
{
int mtu = 0;
/*
* If the packet is routed over IPsec tunnel, tell the
* originator the tunnel MTU.
* tunnel MTU = if MTU - sizeof(IP) - ESP/AH hdrsiz
* XXX quickhack!!!
*/
#ifdef IPSEC
struct secpolicy *sp = NULL;
int ipsecerror;
int ipsechdr;
struct route *ro;
sp = ipsec_getpolicybyaddr(m,
IPSEC_DIR_OUTBOUND,
IP_FORWARDING,
&ipsecerror);
if (sp != NULL) {
/* count IPsec header size */
ipsechdr = ipsec_hdrsiz(m, IPSEC_DIR_OUTBOUND, NULL);
/*
* find the correct route for outer IPv4
* header, compute tunnel MTU.
*/
if (sp->req != NULL &&
sp->req->sav != NULL &&
sp->req->sav->sah != NULL) {
ro = &sp->req->sav->sah->route_cache.sa_route;
if (ro->ro_rt && ro->ro_rt->rt_ifp) {
mtu = ro->ro_rt->rt_mtu ? ro->ro_rt->rt_mtu :
ro->ro_rt->rt_ifp->if_mtu;
mtu -= ipsechdr;
}
}
KEY_FREESP(&sp);
}
#endif /* IPSEC */
/* XXX else case missing. */
return mtu;
}
#endif

View File

@ -1,42 +0,0 @@
/*-
* Copyright (c) 1982, 1986, 1988, 1993
* The Regents of the University of California. 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.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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$
*/
#ifndef _NETINET_IP6_IPSEC_H_
#define _NETINET_IP6_IPSEC_H_
int ip6_ipsec_filtertunnel(struct mbuf *);
int ip6_ipsec_fwd(struct mbuf *);
int ip6_ipsec_input(struct mbuf *, int);
int ip6_ipsec_output(struct mbuf **, struct inpcb *, int *);
#if 0
int ip6_ipsec_mtu(struct mbuf *);
#endif
#endif

View File

@ -108,12 +108,7 @@ __FBSDID("$FreeBSD$");
#include <netinet6/nd6.h>
#include <netinet6/in6_rss.h>
#ifdef IPSEC
#include <netipsec/ipsec.h>
#include <netipsec/ipsec6.h>
#include <netipsec/key.h>
#include <netinet6/ip6_ipsec.h>
#endif /* IPSEC */
#include <netipsec/ipsec_support.h>
#ifdef SCTP
#include <netinet/sctp.h>
#include <netinet/sctp_crc32.h>
@ -336,6 +331,21 @@ ip6_output(struct mbuf *m0, struct ip6_pktopts *opt,
}
}
#if defined(IPSEC) || defined(IPSEC_SUPPORT)
/*
* IPSec checking which handles several cases.
* FAST IPSEC: We re-injected the packet.
* XXX: need scope argument.
*/
if (IPSEC_ENABLED(ipv6)) {
if ((error = IPSEC_OUTPUT(ipv6, m, inp)) != 0) {
if (error == EINPROGRESS)
error = 0;
goto done;
}
}
#endif /* IPSEC */
bzero(&exthdrs, sizeof(exthdrs));
if (opt) {
/* Hop-by-Hop options header */
@ -360,24 +370,6 @@ ip6_output(struct mbuf *m0, struct ip6_pktopts *opt,
MAKE_EXTHDR(opt->ip6po_dest2, &exthdrs.ip6e_dest2);
}
#ifdef IPSEC
/*
* IPSec checking which handles several cases.
* FAST IPSEC: We re-injected the packet.
* XXX: need scope argument.
*/
switch(ip6_ipsec_output(&m, inp, &error))
{
case 1: /* Bad packet */
goto freehdrs;
case -1: /* IPSec done */
goto done;
case 0: /* No IPSec */
default:
break;
}
#endif /* IPSEC */
/*
* Calculate the total length of the extension header chain.
* Keep the length of the unfragmentable part for fragmentation.
@ -1912,23 +1904,13 @@ do { \
INP_WUNLOCK(in6p);
break;
#ifdef IPSEC
#if defined(IPSEC) || defined(IPSEC_SUPPORT)
case IPV6_IPSEC_POLICY:
{
caddr_t req;
struct mbuf *m;
if ((error = soopt_getm(sopt, &m)) != 0) /* XXX */
if (IPSEC_ENABLED(ipv6)) {
error = IPSEC_PCBCTL(ipv6, in6p, sopt);
break;
if ((error = soopt_mcopyin(sopt, m)) != 0) /* XXX */
break;
req = mtod(m, caddr_t);
error = ipsec_set_policy(in6p, optname, req,
m->m_len, (sopt->sopt_td != NULL) ?
sopt->sopt_td->td_ucred : NULL);
m_freem(m);
break;
}
}
/* FALLTHROUGH */
#endif /* IPSEC */
default:
@ -2153,37 +2135,14 @@ do { \
error = ip6_getmoptions(in6p, sopt);
break;
#ifdef IPSEC
#if defined(IPSEC) || defined(IPSEC_SUPPORT)
case IPV6_IPSEC_POLICY:
{
caddr_t req = NULL;
size_t len = 0;
struct mbuf *m = NULL;
struct mbuf **mp = &m;
size_t ovalsize = sopt->sopt_valsize;
caddr_t oval = (caddr_t)sopt->sopt_val;
error = soopt_getm(sopt, &m); /* XXX */
if (error != 0)
if (IPSEC_ENABLED(ipv6)) {
error = IPSEC_PCBCTL(ipv6, in6p, sopt);
break;
error = soopt_mcopyin(sopt, m); /* XXX */
if (error != 0)
break;
sopt->sopt_valsize = ovalsize;
sopt->sopt_val = oval;
if (m) {
req = mtod(m, caddr_t);
len = m->m_len;
}
error = ipsec_get_policy(in6p, req, len, mp);
if (error == 0)
error = soopt_mcopyout(sopt, m); /* XXX */
if (error == 0 && m)
m_freem(m);
break;
}
/* FALLTHROUGH */
#endif /* IPSEC */
default:
error = ENOPROTOOPT;
break;

View File

@ -104,10 +104,7 @@ __FBSDID("$FreeBSD$");
#include <netinet6/scope6_var.h>
#include <netinet6/send.h>
#ifdef IPSEC
#include <netipsec/ipsec.h>
#include <netipsec/ipsec6.h>
#endif /* IPSEC */
#include <netipsec/ipsec_support.h>
#include <machine/stdarg.h>
@ -258,14 +255,18 @@ rip6_input(struct mbuf **mp, int *offp, int proto)
if (last != NULL) {
struct mbuf *n = m_copym(m, 0, M_COPYALL, M_NOWAIT);
#ifdef IPSEC
#if defined(IPSEC) || defined(IPSEC_SUPPORT)
/*
* Check AH/ESP integrity.
*/
if (n && ipsec6_in_reject(n, last)) {
m_freem(n);
/* Do not inject data into pcb. */
} else
if (IPSEC_ENABLED(ipv6)) {
if (n != NULL &&
IPSEC_CHECK_POLICY(ipv6, n, last) != 0) {
m_freem(n);
/* Do not inject data into pcb. */
n = NULL;
}
}
#endif /* IPSEC */
if (n) {
if (last->inp_flags & INP_CONTROLOPTS ||
@ -289,11 +290,12 @@ rip6_input(struct mbuf **mp, int *offp, int proto)
last = in6p;
}
INP_INFO_RUNLOCK(&V_ripcbinfo);
#ifdef IPSEC
#if defined(IPSEC) || defined(IPSEC_SUPPORT)
/*
* Check AH/ESP integrity.
*/
if ((last != NULL) && ipsec6_in_reject(m, last)) {
if (IPSEC_ENABLED(ipv6) && last != NULL &&
IPSEC_CHECK_POLICY(ipv6, m, last) != 0) {
m_freem(m);
IP6STAT_DEC(ip6s_delivered);
/* Do not inject data into pcb. */

View File

@ -55,11 +55,6 @@ __FBSDID("$FreeBSD$");
#include <netinet/icmp6.h>
#include <netinet/udp.h>
#ifdef IPSEC
#include <netipsec/ipsec.h>
#include <netipsec/ipsec6.h>
#endif /* IPSEC */
extern struct protosw inetsw[];
int

View File

@ -120,10 +120,7 @@ __FBSDID("$FreeBSD$");
#include <netinet6/udp6_var.h>
#include <netinet6/scope6_var.h>
#ifdef IPSEC
#include <netipsec/ipsec.h>
#include <netipsec/ipsec6.h>
#endif /* IPSEC */
#include <netipsec/ipsec_support.h>
#include <security/mac/mac_framework.h>
@ -157,11 +154,13 @@ udp6_append(struct inpcb *inp, struct mbuf *n, int off,
INP_RLOCK(inp);
return (in_pcbrele_rlocked(inp));
}
#ifdef IPSEC
#if defined(IPSEC) || defined(IPSEC_SUPPORT)
/* Check AH/ESP integrity. */
if (ipsec6_in_reject(n, inp)) {
m_freem(n);
return (0);
if (IPSEC_ENABLED(ipv6)) {
if (IPSEC_CHECK_POLICY(ipv6, n, inp) != 0) {
m_freem(n);
return (0);
}
}
#endif /* IPSEC */
#ifdef MAC

File diff suppressed because it is too large Load Diff

View File

@ -53,11 +53,6 @@
#define IPSEC_ASSERT(_c,_m) KASSERT(_c, _m)
#define IPSEC_IS_PRIVILEGED_SO(_so) \
((_so)->so_cred != NULL && \
priv_check_cred((_so)->so_cred, PRIV_NETINET_IPSEC, 0) \
== 0)
/*
* Security Policy Index
* Ensure that both address families in the "src" and "dst" are same.
@ -65,35 +60,41 @@
* specifies ICMPv6 type, and the port field in "dst" specifies ICMPv6 code.
*/
struct secpolicyindex {
u_int8_t dir; /* direction of packet flow, see below */
union sockaddr_union src; /* IP src address for SP */
union sockaddr_union dst; /* IP dst address for SP */
u_int8_t prefs; /* prefix length in bits for src */
u_int8_t prefd; /* prefix length in bits for dst */
u_int16_t ul_proto; /* upper layer Protocol */
#ifdef notyet
uid_t uids;
uid_t uidd;
gid_t gids;
gid_t gidd;
#endif
uint8_t ul_proto; /* upper layer Protocol */
uint8_t dir; /* direction of packet flow */
uint8_t prefs; /* prefix length in bits for src */
uint8_t prefd; /* prefix length in bits for dst */
};
/* Request for IPsec */
struct ipsecrequest {
struct secasindex saidx;/* hint for search proper SA */
/* if __ss_len == 0 then no address specified.*/
u_int level; /* IPsec level defined below. */
};
/* Security Policy Data Base */
struct secpolicy {
TAILQ_ENTRY(secpolicy) chain;
LIST_ENTRY(secpolicy) idhash;
LIST_ENTRY(secpolicy) drainq;
struct secpolicyindex spidx; /* selector */
struct ipsecrequest *req;
/* pointer to the ipsec request tree, */
/* if policy == IPSEC else this value == NULL.*/
u_int refcnt; /* reference count */
#define IPSEC_MAXREQ 4
struct ipsecrequest *req[IPSEC_MAXREQ];
u_int tcount; /* IPsec transforms count */
volatile u_int refcnt; /* reference count */
u_int policy; /* policy_type per pfkeyv2.h */
u_int state;
#define IPSEC_SPSTATE_DEAD 0
#define IPSEC_SPSTATE_ALIVE 1
u_int32_t priority; /* priority of this policy */
u_int32_t id; /* It's unique number on the system. */
#define IPSEC_SPSTATE_LARVAL 1
#define IPSEC_SPSTATE_ALIVE 2
#define IPSEC_SPSTATE_PCB 3
#define IPSEC_SPSTATE_IFNET 4
uint32_t priority; /* priority of this policy */
uint32_t id; /* It's unique number on the system. */
/*
* lifetime handler.
* the policy can be used without limitiation if both lifetime and
@ -107,41 +108,25 @@ struct secpolicy {
long validtime; /* duration this policy is valid without use */
};
/* Request for IPsec */
struct ipsecrequest {
struct ipsecrequest *next;
/* pointer to next structure */
/* If NULL, it means the end of chain. */
struct secasindex saidx;/* hint for search proper SA */
/* if __ss_len == 0 then no address specified.*/
u_int level; /* IPsec level defined below. */
struct secasvar *sav; /* place holder of SA for use */
struct secpolicy *sp; /* back pointer to SP */
struct rwlock lock; /* to interlock updates */
};
/*
* Need recursion for when crypto callbacks happen directly,
* as in the case of software crypto. Need to look at how
* hard it is to remove this...
* PCB security policies.
* Application can setup private security policies for socket.
* Such policies can have IPSEC, BYPASS and ENTRUST type.
* By default, policies are set to NULL. This means that they have ENTRUST type.
* When application sets BYPASS or IPSEC type policy, the flags field
* is also updated. When flags is not set, the system could store
* used security policy into the sp_in/sp_out pointer to speed up further
* lookups.
*/
#define IPSECREQUEST_LOCK_INIT(_isr) \
rw_init_flags(&(_isr)->lock, "ipsec request", RW_RECURSE)
#define IPSECREQUEST_LOCK(_isr) rw_rlock(&(_isr)->lock)
#define IPSECREQUEST_UNLOCK(_isr) rw_runlock(&(_isr)->lock)
#define IPSECREQUEST_WLOCK(_isr) rw_wlock(&(_isr)->lock)
#define IPSECREQUEST_WUNLOCK(_isr) rw_wunlock(&(_isr)->lock)
#define IPSECREQUEST_UPGRADE(_isr) rw_try_upgrade(&(_isr)->lock)
#define IPSECREQUEST_DOWNGRADE(_isr) rw_downgrade(&(_isr)->lock)
#define IPSECREQUEST_LOCK_DESTROY(_isr) rw_destroy(&(_isr)->lock)
#define IPSECREQUEST_LOCK_ASSERT(_isr) rw_assert(&(_isr)->lock, RA_LOCKED)
/* security policy in PCB */
struct inpcbpolicy {
struct secpolicy *sp_in;
struct secpolicy *sp_out;
int priv; /* privileged socket ? */
struct secpolicy *sp_in;
struct secpolicy *sp_out;
uint32_t genid;
uint16_t flags;
#define INP_INBOUND_POLICY 0x0001
#define INP_OUTBOUND_POLICY 0x0002
uint16_t hdrsz;
};
/* SP acquiring list table. */
@ -156,6 +141,9 @@ struct secspacq {
};
#endif /* _KERNEL */
/* buffer size for formatted output of ipsec address */
#define IPSEC_ADDRSTRLEN (INET6_ADDRSTRLEN + 11)
/* according to IANA assignment, port 0x0000 and proto 0xff are reserved. */
#define IPSEC_PORT_ANY 0
#define IPSEC_ULPROTO_ANY 255
@ -288,6 +276,7 @@ VNET_DECLARE(int, ip4_ipsec_dfbit);
VNET_DECLARE(int, ip4_ipsec_ecn);
VNET_DECLARE(int, ip4_esp_randpad);
VNET_DECLARE(int, crypto_support);
VNET_DECLARE(int, natt_cksum_policy);
#define IPSECSTAT_INC(name) \
VNET_PCPUSTAT_ADD(struct ipsecstat, ipsec4stat, name, 1)
@ -300,59 +289,52 @@ VNET_DECLARE(int, crypto_support);
#define V_ip4_ipsec_ecn VNET(ip4_ipsec_ecn)
#define V_ip4_esp_randpad VNET(ip4_esp_randpad)
#define V_crypto_support VNET(crypto_support)
#define V_natt_cksum_policy VNET(natt_cksum_policy)
#define ipseclog(x) do { if (V_ipsec_debug) log x; } while (0)
/* for openbsd compatibility */
#define DPRINTF(x) do { if (V_ipsec_debug) printf x; } while (0)
extern struct ipsecrequest *ipsec_newisr(void);
extern void ipsec_delisr(struct ipsecrequest *);
struct tdb_ident;
extern struct secpolicy *ipsec_getpolicy(struct tdb_ident*, u_int);
struct inpcb;
extern struct secpolicy *ipsec4_checkpolicy(const struct mbuf *, u_int,
int *, struct inpcb *);
extern struct secpolicy * ipsec_getpolicybyaddr(const struct mbuf *, u_int,
struct m_tag;
struct secasvar;
struct sockopt;
struct tcphdr;
union sockaddr_union;
int ipsec_if_input(struct mbuf *, struct secasvar *, uint32_t);
struct ipsecrequest *ipsec_newisr(void);
void ipsec_delisr(struct ipsecrequest *);
struct secpolicy *ipsec4_checkpolicy(const struct mbuf *, struct inpcb *,
int *);
struct inpcb;
extern int ipsec_init_policy(struct socket *so, struct inpcbpolicy **);
extern int ipsec_copy_policy(struct inpcbpolicy *, struct inpcbpolicy *);
extern u_int ipsec_get_reqlevel(struct ipsecrequest *);
u_int ipsec_get_reqlevel(struct secpolicy *, u_int);
extern int ipsec_set_policy(struct inpcb *inp, int optname,
caddr_t request, size_t len, struct ucred *cred);
extern int ipsec_get_policy(struct inpcb *inpcb, caddr_t request,
size_t len, struct mbuf **mp);
extern int ipsec_delete_pcbpolicy(struct inpcb *);
extern int ipsec4_in_reject(const struct mbuf *, struct inpcb *);
void udp_ipsec_adjust_cksum(struct mbuf *, struct secasvar *, int, int);
int udp_ipsec_output(struct mbuf *, struct secasvar *);
int udp_ipsec_input(struct mbuf *, int, int);
int udp_ipsec_pcbctl(struct inpcb *, struct sockopt *);
struct secas;
struct tcpcb;
extern int ipsec_chkreplay(u_int32_t, struct secasvar *);
extern int ipsec_updatereplay(u_int32_t, struct secasvar *);
int ipsec_chkreplay(uint32_t, struct secasvar *);
int ipsec_updatereplay(uint32_t, struct secasvar *);
int ipsec_updateid(struct secasvar *, uint64_t *, uint64_t *);
int ipsec_initialized(void);
extern size_t ipsec_hdrsiz(const struct mbuf *, u_int, struct inpcb *);
extern size_t ipsec_hdrsiz_tcp(struct tcpcb *);
void ipsec_setspidx_inpcb(struct inpcb *, struct secpolicyindex *, u_int);
union sockaddr_union;
extern char *ipsec_address(union sockaddr_union *, char *, socklen_t);
extern char *ipsec_logsastr(struct secasvar *, char *, size_t);
extern void ipsec_dumpmbuf(const struct mbuf *);
struct m_tag;
extern int ah4_input(struct mbuf **mp, int *offp, int proto);
extern void ah4_ctlinput(int cmd, struct sockaddr *sa, void *);
extern int esp4_input(struct mbuf **mp, int *offp, int proto);
extern void esp4_ctlinput(int cmd, struct sockaddr *sa, void *);
extern int ipcomp4_input(struct mbuf **mp, int *offp, int proto);
extern int ipsec_common_input(struct mbuf *m, int, int, int, int);
extern int ipsec4_common_input_cb(struct mbuf *m, struct secasvar *sav,
int skip, int protoff);
extern int ipsec4_process_packet(struct mbuf *, struct ipsecrequest *);
extern int ipsec_process_done(struct mbuf *, struct ipsecrequest *);
void ipsec4_setsockaddrs(const struct mbuf *, union sockaddr_union *,
union sockaddr_union *);
int ipsec4_in_reject(const struct mbuf *, struct inpcb *);
int ipsec4_input(struct mbuf *, int, int);
int ipsec4_forward(struct mbuf *);
int ipsec4_pcbctl(struct inpcb *, struct sockopt *);
int ipsec4_output(struct mbuf *, struct inpcb *);
int ipsec4_capability(struct mbuf *, u_int);
int ipsec4_common_input_cb(struct mbuf *, struct secasvar *, int, int);
int ipsec4_process_packet(struct mbuf *, struct secpolicy *, struct inpcb *);
int ipsec_process_done(struct mbuf *, struct secpolicy *, struct secasvar *,
u_int);
extern void m_checkalignment(const char* where, struct mbuf *m0,
int off, int len);

View File

@ -59,14 +59,22 @@ VNET_DECLARE(int, ip6_ipsec_ecn);
#define V_ip6_ipsec_ecn VNET(ip6_ipsec_ecn)
struct inpcb;
extern int ipsec6_in_reject(const struct mbuf *, struct inpcb *);
struct secpolicy *ipsec6_checkpolicy(const struct mbuf *,
struct inpcb *, int *);
struct m_tag;
extern int ipsec6_common_input(struct mbuf **mp, int *offp, int proto);
extern int ipsec6_common_input_cb(struct mbuf *m, struct secasvar *sav,
int skip, int protoff);
extern void esp6_ctlinput(int, struct sockaddr *, void *);
extern int ipsec6_process_packet(struct mbuf *, struct ipsecrequest *);
void ipsec6_setsockaddrs(const struct mbuf *, union sockaddr_union *,
union sockaddr_union *);
int ipsec6_input(struct mbuf *, int, int);
int ipsec6_in_reject(const struct mbuf *, struct inpcb *);
int ipsec6_forward(struct mbuf *);
int ipsec6_pcbctl(struct inpcb *, struct sockopt *);
int ipsec6_output(struct mbuf *, struct inpcb *);
int ipsec6_capability(struct mbuf *, u_int);
int ipsec6_common_input_cb(struct mbuf *, struct secasvar *, int, int);
int ipsec6_process_packet(struct mbuf *, struct secpolicy *, struct inpcb *);
int ip6_ipsec_filtertunnel(struct mbuf *);
int ip6_ipsec_pcbctl(struct inpcb *, struct sockopt *);
#endif /*_KERNEL*/
#endif /*_NETIPSEC_IPSEC6_H_*/

View File

@ -1,4 +1,3 @@
/* $FreeBSD$ */
/* $OpenBSD: ipsec_input.c,v 1.63 2003/02/20 18:35:43 deraadt Exp $ */
/*-
* The authors of this code are John Ioannidis (ji@tla.org),
@ -19,6 +18,7 @@
* Copyright (C) 1995, 1996, 1997, 1998, 1999 by John Ioannidis,
* Angelos D. Keromytis and Niels Provos.
* Copyright (c) 2001, Angelos D. Keromytis.
* Copyright (c) 2016 Andrey V. Elsukov <ae@FreeBSD.org>
*
* Permission to use, copy, and modify this software with or without fee
* is hereby granted, provided that this entire notice is included in
@ -40,6 +40,9 @@
* IPsec input processing.
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include "opt_inet.h"
#include "opt_inet6.h"
#include "opt_ipsec.h"
@ -87,6 +90,7 @@
#include <netipsec/key.h>
#include <netipsec/keydb.h>
#include <netipsec/key_debug.h>
#include <netipsec/xform.h>
#include <netinet6/ip6protosw.h>
@ -104,29 +108,20 @@
IPCOMPSTAT_INC(ipcomps_##name); \
} while (0)
#ifdef INET
static void ipsec4_common_ctlinput(int, struct sockaddr *, void *, int);
#endif
/*
* ipsec_common_input gets called when an IPsec-protected packet
* is received by IPv4 or IPv6. Its job is to find the right SA
* and call the appropriate transform. The transform callback
* takes care of further processing (like ingress filtering).
*/
int
static int
ipsec_common_input(struct mbuf *m, int skip, int protoff, int af, int sproto)
{
char buf[INET6_ADDRSTRLEN];
char buf[IPSEC_ADDRSTRLEN];
union sockaddr_union dst_address;
struct secasvar *sav;
u_int32_t spi;
uint32_t spi;
int error;
#ifdef INET
#ifdef IPSEC_NAT_T
struct m_tag *tag;
#endif
#endif
IPSEC_ISTAT(sproto, input);
@ -178,12 +173,6 @@ ipsec_common_input(struct mbuf *m, int skip, int protoff, int af, int sproto)
m_copydata(m, offsetof(struct ip, ip_dst),
sizeof(struct in_addr),
(caddr_t) &dst_address.sin.sin_addr);
#ifdef IPSEC_NAT_T
/* Find the source port for NAT-T; see udp*_espdecap. */
tag = m_tag_find(m, PACKET_TAG_IPSEC_NAT_T_PORTS, NULL);
if (tag != NULL)
dst_address.sin.sin_port = ((u_int16_t *)(tag + 1))[1];
#endif /* IPSEC_NAT_T */
break;
#endif /* INET */
#ifdef INET6
@ -209,7 +198,7 @@ ipsec_common_input(struct mbuf *m, int skip, int protoff, int af, int sproto)
}
/* NB: only pass dst since key_allocsa follows RFC2401 */
sav = KEY_ALLOCSA(&dst_address, sproto, spi);
sav = key_allocsa(&dst_address, sproto, spi);
if (sav == NULL) {
DPRINTF(("%s: no key association found for SA %s/%08lx/%u\n",
__func__, ipsec_address(&dst_address, buf, sizeof(buf)),
@ -224,7 +213,7 @@ ipsec_common_input(struct mbuf *m, int skip, int protoff, int af, int sproto)
__func__, ipsec_address(&dst_address, buf, sizeof(buf)),
(u_long) ntohl(spi), sproto));
IPSEC_ISTAT(sproto, noxform);
KEY_FREESAV(&sav);
key_freesav(&sav);
m_freem(m);
return ENXIO;
}
@ -234,69 +223,50 @@ ipsec_common_input(struct mbuf *m, int skip, int protoff, int af, int sproto)
* everything else.
*/
error = (*sav->tdb_xform->xf_input)(m, sav, skip, protoff);
KEY_FREESAV(&sav);
return error;
if (error != 0)
key_freesav(&sav);
return (error);
}
#ifdef INET
extern struct protosw inetsw[];
/*
* IPSEC_INPUT() method implementation for IPv4.
* 0 - Permitted by inbound security policy for further processing.
* EACCES - Forbidden by inbound security policy.
* EINPROGRESS - consumed by IPsec.
*/
int
ah4_input(struct mbuf **mp, int *offp, int proto)
ipsec4_input(struct mbuf *m, int offset, int proto)
{
struct mbuf *m;
int off;
m = *mp;
off = *offp;
*mp = NULL;
ipsec_common_input(m, off, offsetof(struct ip, ip_p),
AF_INET, IPPROTO_AH);
return (IPPROTO_DONE);
}
void
ah4_ctlinput(int cmd, struct sockaddr *sa, void *v)
{
if (sa->sa_family == AF_INET &&
sa->sa_len == sizeof(struct sockaddr_in))
ipsec4_common_ctlinput(cmd, sa, v, IPPROTO_AH);
}
int
esp4_input(struct mbuf **mp, int *offp, int proto)
{
struct mbuf *m;
int off;
m = *mp;
off = *offp;
mp = NULL;
ipsec_common_input(m, off, offsetof(struct ip, ip_p),
AF_INET, IPPROTO_ESP);
return (IPPROTO_DONE);
}
void
esp4_ctlinput(int cmd, struct sockaddr *sa, void *v)
{
if (sa->sa_family == AF_INET &&
sa->sa_len == sizeof(struct sockaddr_in))
ipsec4_common_ctlinput(cmd, sa, v, IPPROTO_ESP);
}
int
ipcomp4_input(struct mbuf **mp, int *offp, int proto)
{
struct mbuf *m;
int off;
m = *mp;
off = *offp;
mp = NULL;
ipsec_common_input(m, off, offsetof(struct ip, ip_p),
AF_INET, IPPROTO_IPCOMP);
return (IPPROTO_DONE);
switch (proto) {
case IPPROTO_AH:
case IPPROTO_ESP:
case IPPROTO_IPCOMP:
/* Do inbound IPsec processing for AH/ESP/IPCOMP */
ipsec_common_input(m, offset,
offsetof(struct ip, ip_p), AF_INET, proto);
return (EINPROGRESS); /* mbuf consumed by IPsec */
default:
/*
* Protocols with further headers get their IPsec treatment
* within the protocol specific processing.
*/
if ((inetsw[ip_protox[proto]].pr_flags & PR_LASTHDR) == 0)
return (0);
/* FALLTHROUGH */
};
/*
* Enforce IPsec policy checking if we are seeing last header.
*/
if (ipsec4_in_reject(m, NULL) != 0) {
/* Forbidden by inbound security policy */
m_freem(m);
return (EACCES);
}
return (0);
}
/*
@ -309,21 +279,14 @@ int
ipsec4_common_input_cb(struct mbuf *m, struct secasvar *sav, int skip,
int protoff)
{
char buf[INET6_ADDRSTRLEN];
char buf[IPSEC_ADDRSTRLEN];
struct ipsec_ctx_data ctx;
int prot, af, sproto, isr_prot;
struct ip *ip;
struct m_tag *mtag;
struct tdb_ident *tdbi;
struct xform_history *xh;
struct secasindex *saidx;
int error;
#ifdef INET6
#ifdef notyet
char ip6buf[INET6_ADDRSTRLEN];
#endif
#endif
struct m_tag *mtag;
struct ip *ip;
int error, prot, af, sproto, isr_prot;
IPSEC_ASSERT(m != NULL, ("null mbuf"));
IPSEC_ASSERT(sav != NULL, ("null SA"));
IPSEC_ASSERT(sav->sah != NULL, ("null SAH"));
saidx = &sav->sah->saidx;
@ -337,7 +300,6 @@ ipsec4_common_input_cb(struct mbuf *m, struct secasvar *sav, int skip,
if (skip != 0) {
/*
* Fix IPv4 header
* XXXGL: do we need this entire block?
*/
if (m->m_len < skip && (m = m_pullup(m, skip)) == NULL) {
DPRINTF(("%s: processing failed for SA %s/%08lx\n",
@ -356,16 +318,23 @@ ipsec4_common_input_cb(struct mbuf *m, struct secasvar *sav, int skip,
ip = mtod(m, struct ip *);
}
prot = ip->ip_p;
/*
* Check that we have NAT-T enabled and apply transport mode
* decapsulation NAT procedure (RFC3948).
* Do this before invoking into the PFIL.
*/
if (sav->natt != NULL &&
(prot == IPPROTO_UDP || prot == IPPROTO_TCP))
udp_ipsec_adjust_cksum(m, sav, prot, skip);
IPSEC_INIT_CTX(&ctx, &m, sav, AF_INET, IPSEC_ENC_BEFORE);
if ((error = ipsec_run_hhooks(&ctx, HHOOK_TYPE_IPSEC_IN)) != 0)
goto bad;
ip = mtod(m, struct ip *);
ip = mtod(m, struct ip *); /* update pointer */
/* IP-in-IP encapsulation */
if (prot == IPPROTO_IPIP &&
saidx->mode != IPSEC_MODE_TRANSPORT) {
if (m->m_pkthdr.len - skip < sizeof(struct ip)) {
IPSEC_ISTAT(sproto, hdrops);
error = EINVAL;
@ -373,40 +342,11 @@ ipsec4_common_input_cb(struct mbuf *m, struct secasvar *sav, int skip,
}
/* enc0: strip outer IPv4 header */
m_striphdr(m, 0, ip->ip_hl << 2);
#ifdef notyet
/* XXX PROXY address isn't recorded in SAH */
/*
* Check that the inner source address is the same as
* the proxy address, if available.
*/
if ((saidx->proxy.sa.sa_family == AF_INET &&
saidx->proxy.sin.sin_addr.s_addr !=
INADDR_ANY &&
ipn.ip_src.s_addr !=
saidx->proxy.sin.sin_addr.s_addr) ||
(saidx->proxy.sa.sa_family != AF_INET &&
saidx->proxy.sa.sa_family != 0)) {
DPRINTF(("%s: inner source address %s doesn't "
"correspond to expected proxy source %s, "
"SA %s/%08lx\n", __func__,
inet_ntoa4(ipn.ip_src),
ipsp_address(saidx->proxy),
ipsp_address(saidx->dst),
(u_long) ntohl(sav->spi)));
IPSEC_ISTAT(sproto, pdrops);
error = EACCES;
goto bad;
}
#endif /* notyet */
}
#ifdef INET6
/* IPv6-in-IP encapsulation. */
else if (prot == IPPROTO_IPV6 &&
saidx->mode != IPSEC_MODE_TRANSPORT) {
if (m->m_pkthdr.len - skip < sizeof(struct ip6_hdr)) {
IPSEC_ISTAT(sproto, hdrops);
error = EINVAL;
@ -414,39 +354,14 @@ ipsec4_common_input_cb(struct mbuf *m, struct secasvar *sav, int skip,
}
/* enc0: strip IPv4 header, keep IPv6 header only */
m_striphdr(m, 0, ip->ip_hl << 2);
#ifdef notyet
/*
* Check that the inner source address is the same as
* the proxy address, if available.
*/
if ((saidx->proxy.sa.sa_family == AF_INET6 &&
!IN6_IS_ADDR_UNSPECIFIED(&saidx->proxy.sin6.sin6_addr) &&
!IN6_ARE_ADDR_EQUAL(&ip6n.ip6_src,
&saidx->proxy.sin6.sin6_addr)) ||
(saidx->proxy.sa.sa_family != AF_INET6 &&
saidx->proxy.sa.sa_family != 0)) {
DPRINTF(("%s: inner source address %s doesn't "
"correspond to expected proxy source %s, "
"SA %s/%08lx\n", __func__,
ip6_sprintf(ip6buf, &ip6n.ip6_src),
ipsec_address(&saidx->proxy),
ipsec_address(&saidx->dst),
(u_long) ntohl(sav->spi)));
IPSEC_ISTAT(sproto, pdrops);
error = EACCES;
goto bad;
}
#endif /* notyet */
}
#endif /* INET6 */
else if (prot != IPPROTO_IPV6 && saidx->mode == IPSEC_MODE_ANY) {
/*
* When mode is wildcard, inner protocol is IPv6 and
* we have no INET6 support - drop this packet a bit later.
* In other cases we assume transport mode and outer
* header was already stripped in xform_xxx_cb.
* In other cases we assume transport mode. Set prot to
* correctly choose netisr.
*/
prot = IPPROTO_IPIP;
}
@ -457,7 +372,7 @@ ipsec4_common_input_cb(struct mbuf *m, struct secasvar *sav, int skip,
*/
if (sproto != IPPROTO_IPCOMP) {
mtag = m_tag_get(PACKET_TAG_IPSEC_IN_DONE,
sizeof(struct tdb_ident), M_NOWAIT);
sizeof(struct xform_history), M_NOWAIT);
if (mtag == NULL) {
DPRINTF(("%s: failed to get tag\n", __func__));
IPSEC_ISTAT(sproto, hdrops);
@ -465,14 +380,11 @@ ipsec4_common_input_cb(struct mbuf *m, struct secasvar *sav, int skip,
goto bad;
}
tdbi = (struct tdb_ident *)(mtag + 1);
bcopy(&saidx->dst, &tdbi->dst, saidx->dst.sa.sa_len);
tdbi->proto = sproto;
tdbi->spi = sav->spi;
/* Cache those two for enc(4) in xform_ipip. */
tdbi->alg_auth = sav->alg_auth;
tdbi->alg_enc = sav->alg_enc;
xh = (struct xform_history *)(mtag + 1);
bcopy(&saidx->dst, &xh->dst, saidx->dst.sa.sa_len);
xh->spi = sav->spi;
xh->proto = sproto;
xh->mode = saidx->mode;
m_tag_prepend(m, mtag);
}
@ -509,69 +421,65 @@ ipsec4_common_input_cb(struct mbuf *m, struct secasvar *sav, int skip,
IPSEC_INIT_CTX(&ctx, &m, sav, af, IPSEC_ENC_AFTER);
if ((error = ipsec_run_hhooks(&ctx, HHOOK_TYPE_IPSEC_IN)) != 0)
goto bad;
error = netisr_queue_src(isr_prot, (uintptr_t)sav->spi, m);
if (error) {
IPSEC_ISTAT(sproto, qfull);
DPRINTF(("%s: queue full; proto %u packet dropped\n",
__func__, sproto));
return error;
}
return 0;
bad:
m_freem(m);
return error;
}
void
ipsec4_common_ctlinput(int cmd, struct sockaddr *sa, void *v, int proto)
{
/* XXX nothing just yet */
/* Handle virtual tunneling interfaces */
if (saidx->mode == IPSEC_MODE_TUNNEL)
error = ipsec_if_input(m, sav, af);
if (error == 0) {
error = netisr_queue_src(isr_prot, (uintptr_t)sav->spi, m);
if (error) {
IPSEC_ISTAT(sproto, qfull);
DPRINTF(("%s: queue full; proto %u packet dropped\n",
__func__, sproto));
}
}
key_freesav(&sav);
return (error);
bad:
key_freesav(&sav);
if (m != NULL)
m_freem(m);
return (error);
}
#endif /* INET */
#ifdef INET6
/* IPv6 AH wrapper. */
/*
* IPSEC_INPUT() method implementation for IPv6.
* 0 - Permitted by inbound security policy for further processing.
* EACCES - Forbidden by inbound security policy.
* EINPROGRESS - consumed by IPsec.
*/
int
ipsec6_common_input(struct mbuf **mp, int *offp, int proto)
ipsec6_input(struct mbuf *m, int offset, int proto)
{
int l = 0;
int protoff;
struct ip6_ext ip6e;
if (*offp < sizeof(struct ip6_hdr)) {
DPRINTF(("%s: bad offset %u\n", __func__, *offp));
return IPPROTO_DONE;
} else if (*offp == sizeof(struct ip6_hdr)) {
protoff = offsetof(struct ip6_hdr, ip6_nxt);
} else {
/* Chase down the header chain... */
protoff = sizeof(struct ip6_hdr);
do {
protoff += l;
m_copydata(*mp, protoff, sizeof(ip6e),
(caddr_t) &ip6e);
if (ip6e.ip6e_nxt == IPPROTO_AH)
l = (ip6e.ip6e_len + 2) << 2;
else
l = (ip6e.ip6e_len + 1) << 3;
IPSEC_ASSERT(l > 0, ("l went zero or negative"));
} while (protoff + l < *offp);
/* Malformed packet check */
if (protoff + l != *offp) {
DPRINTF(("%s: bad packet header chain, protoff %u, "
"l %u, off %u\n", __func__, protoff, l, *offp));
IPSEC_ISTAT(proto, hdrops);
m_freem(*mp);
*mp = NULL;
return IPPROTO_DONE;
}
protoff += offsetof(struct ip6_ext, ip6e_nxt);
switch (proto) {
case IPPROTO_AH:
case IPPROTO_ESP:
case IPPROTO_IPCOMP:
/* Do inbound IPsec processing for AH/ESP/IPCOMP */
ipsec_common_input(m, offset,
offsetof(struct ip6_hdr, ip6_nxt), AF_INET6, proto);
return (EINPROGRESS); /* mbuf consumed by IPsec */
default:
/*
* Protocols with further headers get their IPsec treatment
* within the protocol specific processing.
*/
if ((inet6sw[ip6_protox[proto]].pr_flags & PR_LASTHDR) == 0)
return (0);
/* FALLTHROUGH */
};
/*
* Enforce IPsec policy checking if we are seeing last header.
*/
if (ipsec6_in_reject(m, NULL) != 0) {
/* Forbidden by inbound security policy */
m_freem(m);
return (EACCES);
}
(void) ipsec_common_input(*mp, *offp, protoff, AF_INET6, proto);
return IPPROTO_DONE;
return (0);
}
/*
@ -582,21 +490,17 @@ int
ipsec6_common_input_cb(struct mbuf *m, struct secasvar *sav, int skip,
int protoff)
{
char buf[INET6_ADDRSTRLEN];
char buf[IPSEC_ADDRSTRLEN];
struct ipsec_ctx_data ctx;
int prot, af, sproto;
struct xform_history *xh;
struct secasindex *saidx;
struct ip6_hdr *ip6;
struct m_tag *mtag;
struct tdb_ident *tdbi;
struct secasindex *saidx;
int prot, af, sproto;
int nxt, isr_prot;
u_int8_t nxt8;
int error, nest;
#ifdef notyet
char ip6buf[INET6_ADDRSTRLEN];
#endif
uint8_t nxt8;
IPSEC_ASSERT(m != NULL, ("null mbuf"));
IPSEC_ASSERT(sav != NULL, ("null SA"));
IPSEC_ASSERT(sav->sah != NULL, ("null SAH"));
saidx = &sav->sah->saidx;
@ -620,12 +524,13 @@ ipsec6_common_input_cb(struct mbuf *m, struct secasvar *sav, int skip,
goto bad;
}
ip6 = mtod(m, struct ip6_hdr *);
ip6->ip6_plen = htons(m->m_pkthdr.len - sizeof(struct ip6_hdr));
IPSEC_INIT_CTX(&ctx, &m, sav, af, IPSEC_ENC_BEFORE);
if ((error = ipsec_run_hhooks(&ctx, HHOOK_TYPE_IPSEC_IN)) != 0)
goto bad;
ip6 = mtod(m, struct ip6_hdr *);
ip6->ip6_plen = htons(m->m_pkthdr.len - sizeof(struct ip6_hdr));
/* Save protocol */
m_copydata(m, protoff, 1, &nxt8);
prot = nxt8;
@ -641,31 +546,6 @@ ipsec6_common_input_cb(struct mbuf *m, struct secasvar *sav, int skip,
/* ip6n will now contain the inner IPv6 header. */
m_striphdr(m, 0, skip);
skip = 0;
#ifdef notyet
/*
* Check that the inner source address is the same as
* the proxy address, if available.
*/
if ((saidx->proxy.sa.sa_family == AF_INET6 &&
!IN6_IS_ADDR_UNSPECIFIED(&saidx->proxy.sin6.sin6_addr) &&
!IN6_ARE_ADDR_EQUAL(&ip6n.ip6_src,
&saidx->proxy.sin6.sin6_addr)) ||
(saidx->proxy.sa.sa_family != AF_INET6 &&
saidx->proxy.sa.sa_family != 0)) {
DPRINTF(("%s: inner source address %s doesn't "
"correspond to expected proxy source %s, "
"SA %s/%08lx\n", __func__,
ip6_sprintf(ip6buf, &ip6n.ip6_src),
ipsec_address(&saidx->proxy),
ipsec_address(&saidx->dst),
(u_long) ntohl(sav->spi)));
IPSEC_ISTAT(sproto, pdrops);
error = EACCES;
goto bad;
}
#endif /* notyet */
}
#ifdef INET
/* IP-in-IP encapsulation */
@ -677,32 +557,8 @@ ipsec6_common_input_cb(struct mbuf *m, struct secasvar *sav, int skip,
goto bad;
}
/* ipn will now contain the inner IPv4 header */
m_striphdr(m, 0, skip);
m_striphdr(m, 0, skip);
skip = 0;
#ifdef notyet
/*
* Check that the inner source address is the same as
* the proxy address, if available.
*/
if ((saidx->proxy.sa.sa_family == AF_INET &&
saidx->proxy.sin.sin_addr.s_addr != INADDR_ANY &&
ipn.ip_src.s_addr != saidx->proxy.sin.sin_addr.s_addr) ||
(saidx->proxy.sa.sa_family != AF_INET &&
saidx->proxy.sa.sa_family != 0)) {
DPRINTF(("%s: inner source address %s doesn't "
"correspond to expected proxy source %s, "
"SA %s/%08lx\n", __func__,
inet_ntoa4(ipn.ip_src),
ipsec_address(&saidx->proxy),
ipsec_address(&saidx->dst),
(u_long) ntohl(sav->spi)));
IPSEC_ISTAT(sproto, pdrops);
error = EACCES;
goto bad;
}
#endif /* notyet */
}
#endif /* INET */
else {
@ -715,7 +571,7 @@ ipsec6_common_input_cb(struct mbuf *m, struct secasvar *sav, int skip,
*/
if (sproto != IPPROTO_IPCOMP) {
mtag = m_tag_get(PACKET_TAG_IPSEC_IN_DONE,
sizeof(struct tdb_ident), M_NOWAIT);
sizeof(struct xform_history), M_NOWAIT);
if (mtag == NULL) {
DPRINTF(("%s: failed to get tag\n", __func__));
IPSEC_ISTAT(sproto, hdrops);
@ -723,20 +579,16 @@ ipsec6_common_input_cb(struct mbuf *m, struct secasvar *sav, int skip,
goto bad;
}
tdbi = (struct tdb_ident *)(mtag + 1);
bcopy(&saidx->dst, &tdbi->dst, sizeof(union sockaddr_union));
tdbi->proto = sproto;
tdbi->spi = sav->spi;
/* Cache those two for enc(4) in xform_ipip. */
tdbi->alg_auth = sav->alg_auth;
tdbi->alg_enc = sav->alg_enc;
xh = (struct xform_history *)(mtag + 1);
bcopy(&saidx->dst, &xh->dst, saidx->dst.sa.sa_len);
xh->spi = sav->spi;
xh->proto = sproto;
xh->mode = saidx->mode;
m_tag_prepend(m, mtag);
}
key_sa_recordxfer(sav, m);
#ifdef INET
if (prot == IPPROTO_IPIP)
af = AF_INET;
@ -767,12 +619,19 @@ ipsec6_common_input_cb(struct mbuf *m, struct secasvar *sav, int skip,
error = EPFNOSUPPORT;
goto bad;
}
error = netisr_queue_src(isr_prot, (uintptr_t)sav->spi, m);
if (error) {
IPSEC_ISTAT(sproto, qfull);
DPRINTF(("%s: queue full; proto %u packet dropped\n",
__func__, sproto));
/* Handle virtual tunneling interfaces */
if (saidx->mode == IPSEC_MODE_TUNNEL)
error = ipsec_if_input(m, sav, af);
if (error == 0) {
error = netisr_queue_src(isr_prot,
(uintptr_t)sav->spi, m);
if (error) {
IPSEC_ISTAT(sproto, qfull);
DPRINTF(("%s: queue full; proto %u packet"
" dropped\n", __func__, sproto));
}
}
key_freesav(&sav);
return (error);
}
/*
@ -810,99 +669,12 @@ ipsec6_common_input_cb(struct mbuf *m, struct secasvar *sav, int skip,
}
nxt = (*inet6sw[ip6_protox[nxt]].pr_input)(&m, &skip, nxt);
}
return 0;
key_freesav(&sav);
return (0);
bad:
key_freesav(&sav);
if (m)
m_freem(m);
return error;
}
void
esp6_ctlinput(int cmd, struct sockaddr *sa, void *d)
{
struct ip6ctlparam *ip6cp = NULL;
struct mbuf *m = NULL;
struct ip6_hdr *ip6;
int off;
if (sa->sa_family != AF_INET6 ||
sa->sa_len != sizeof(struct sockaddr_in6))
return;
if ((unsigned)cmd >= PRC_NCMDS)
return;
/* if the parameter is from icmp6, decode it. */
if (d != NULL) {
ip6cp = (struct ip6ctlparam *)d;
m = ip6cp->ip6c_m;
ip6 = ip6cp->ip6c_ip6;
off = ip6cp->ip6c_off;
} else {
m = NULL;
ip6 = NULL;
off = 0; /* calm gcc */
}
if (ip6 != NULL) {
struct ip6ctlparam ip6cp1;
/*
* Notify the error to all possible sockets via pfctlinput2.
* Since the upper layer information (such as protocol type,
* source and destination ports) is embedded in the encrypted
* data and might have been cut, we can't directly call
* an upper layer ctlinput function. However, the pcbnotify
* function will consider source and destination addresses
* as well as the flow info value, and may be able to find
* some PCB that should be notified.
* Although pfctlinput2 will call esp6_ctlinput(), there is
* no possibility of an infinite loop of function calls,
* because we don't pass the inner IPv6 header.
*/
bzero(&ip6cp1, sizeof(ip6cp1));
ip6cp1.ip6c_src = ip6cp->ip6c_src;
pfctlinput2(cmd, sa, (void *)&ip6cp1);
/*
* Then go to special cases that need ESP header information.
* XXX: We assume that when ip6 is non NULL,
* M and OFF are valid.
*/
if (cmd == PRC_MSGSIZE) {
struct secasvar *sav;
u_int32_t spi;
int valid;
/* check header length before using m_copydata */
if (m->m_pkthdr.len < off + sizeof (struct esp))
return;
m_copydata(m, off + offsetof(struct esp, esp_spi),
sizeof(u_int32_t), (caddr_t) &spi);
/*
* Check to see if we have a valid SA corresponding to
* the address in the ICMP message payload.
*/
sav = KEY_ALLOCSA((union sockaddr_union *)sa,
IPPROTO_ESP, spi);
valid = (sav != NULL);
if (sav)
KEY_FREESAV(&sav);
/* XXX Further validation? */
/*
* Depending on whether the SA is "valid" and
* routing table size (mtudisc_{hi,lo}wat), we will:
* - recalcurate the new MTU and create the
* corresponding routing entry, or
* - ignore the MTU change notification.
*/
icmp6_mtudisc_update(ip6cp, valid);
}
} else {
/* we normally notify any pcb here */
}
return (error);
}
#endif /* INET6 */

View File

@ -30,8 +30,6 @@
* IPsec-specific mbuf routines.
*/
#include "opt_param.h"
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/malloc.h>
@ -72,7 +70,21 @@ m_makespace(struct mbuf *m0, int skip, int hlen, int *off)
* the contents of m as needed.
*/
remain = m->m_len - skip; /* data to move */
if (hlen > M_TRAILINGSPACE(m)) {
if (remain > skip &&
hlen + max_linkhdr < M_LEADINGSPACE(m)) {
/*
* mbuf has enough free space at the beginning.
* XXX: which operation is the most heavy - copying of
* possible several hundred of bytes or allocation
* of new mbuf? We can remove max_linkhdr check
* here, but it is possible that this will lead
* to allocation of new mbuf in Layer 2 code.
*/
m->m_data -= hlen;
bcopy(mtodo(m, hlen), mtod(m, caddr_t), skip);
m->m_len += hlen;
*off = skip;
} else if (hlen > M_TRAILINGSPACE(m)) {
struct mbuf *n0, *n, **np;
int todo, len, done, alloc;
@ -140,7 +152,7 @@ m_makespace(struct mbuf *m0, int skip, int hlen, int *off)
* so there's space to write the new header.
*/
bcopy(mtod(m, caddr_t) + skip,
mtod(m, caddr_t) + skip + hlen, remain);
mtod(m, caddr_t) + skip + hlen, remain);
m->m_len += hlen;
*off = skip;
}

148
sys/netipsec/ipsec_mod.c Normal file
View File

@ -0,0 +1,148 @@
/*-
* Copyright (c) 2016 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 "opt_inet.h"
#include "opt_inet6.h"
#include "opt_ipsec.h"
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/lock.h>
#include <sys/malloc.h>
#include <sys/mbuf.h>
#include <sys/module.h>
#include <sys/priv.h>
#include <sys/rmlock.h>
#include <sys/socket.h>
#include <sys/sockopt.h>
#include <sys/syslog.h>
#include <sys/proc.h>
#include <netinet/in.h>
#include <netinet/in_pcb.h>
#include <netipsec/ipsec.h>
#include <netipsec/ipsec6.h>
#include <netipsec/key.h>
#include <netipsec/key_debug.h>
#include <netipsec/ipsec_support.h>
#ifdef INET
static const struct ipsec_methods ipv4_methods = {
.input = ipsec4_input,
.forward = ipsec4_forward,
.output = ipsec4_output,
.pcbctl = ipsec4_pcbctl,
.capability = ipsec4_capability,
.check_policy = ipsec4_in_reject,
.hdrsize = ipsec_hdrsiz_inpcb,
.udp_input = udp_ipsec_input,
.udp_pcbctl = udp_ipsec_pcbctl,
};
#ifndef KLD_MODULE
static const struct ipsec_support ipv4_ipsec = {
.enabled = IPSEC_MODULE_ENABLED,
.methods = &ipv4_methods
};
const struct ipsec_support * const ipv4_ipsec_support = &ipv4_ipsec;
#endif /* !KLD_MODULE */
#endif /* INET */
#ifdef INET6
static const struct ipsec_methods ipv6_methods = {
.input = ipsec6_input,
.forward = ipsec6_forward,
.output = ipsec6_output,
.pcbctl = ipsec6_pcbctl,
.capability = ipsec6_capability,
.check_policy = ipsec6_in_reject,
.hdrsize = ipsec_hdrsiz_inpcb,
};
#ifndef KLD_MODULE
static const struct ipsec_support ipv6_ipsec = {
.enabled = IPSEC_MODULE_ENABLED,
.methods = &ipv6_methods
};
const struct ipsec_support * const ipv6_ipsec_support = &ipv6_ipsec;
#endif /* !KLD_MODULE */
#endif /* INET6 */
/*
* Always register ipsec module.
* Even when IPsec is build in the kernel, we need to have
* module registered. This will prevent to load ipsec.ko.
*/
static int
ipsec_modevent(module_t mod, int type, void *data)
{
switch (type) {
case MOD_LOAD:
/* All xforms are registered via SYSINIT */
if (!ipsec_initialized())
return (ENOMEM);
#ifdef KLD_MODULE
#ifdef INET
ipsec_support_enable(ipv4_ipsec_support, &ipv4_methods);
#endif
#ifdef INET6
ipsec_support_enable(ipv6_ipsec_support, &ipv6_methods);
#endif
#endif /* KLD_MODULE */
break;
case MOD_UNLOAD:
/* All xforms are unregistered via SYSUNINIT */
#ifdef KLD_MODULE
#ifdef INET
ipsec_support_disable(ipv4_ipsec_support);
#endif
#ifdef INET6
ipsec_support_disable(ipv6_ipsec_support);
#endif
#endif /* KLD_MODULE */
break;
default:
return (EOPNOTSUPP);
}
return (0);
}
static moduledata_t ipsec_mod = {
"ipsec",
ipsec_modevent,
0
};
DECLARE_MODULE(ipsec, ipsec_mod, SI_SUB_PROTO_DOMAIN, SI_ORDER_ANY);
MODULE_VERSION(ipsec, 1);
#ifdef KLD_MODULE
MODULE_DEPEND(ipsec, ipsec_support, 1, 1, 1);
#endif

File diff suppressed because it is too large Load Diff

479
sys/netipsec/ipsec_pcb.c Normal file
View File

@ -0,0 +1,479 @@
/*-
* Copyright (c) 2016 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 "opt_ipsec.h"
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/lock.h>
#include <sys/malloc.h>
#include <sys/mbuf.h>
#include <sys/priv.h>
#include <sys/socket.h>
#include <sys/sockopt.h>
#include <sys/syslog.h>
#include <sys/proc.h>
#include <netinet/in.h>
#include <netinet/in_pcb.h>
#include <netipsec/ipsec.h>
#include <netipsec/ipsec6.h>
#include <netipsec/ipsec_support.h>
#include <netipsec/key.h>
#include <netipsec/key_debug.h>
MALLOC_DEFINE(M_IPSEC_INPCB, "inpcbpolicy", "inpcb-resident ipsec policy");
static void
ipsec_setsockaddrs_inpcb(struct inpcb *inp, union sockaddr_union *src,
union sockaddr_union *dst, u_int dir)
{
#ifdef INET6
if (inp->inp_vflag & INP_IPV6) {
struct sockaddr_in6 *sin6;
bzero(&src->sin6, sizeof(src->sin6));
bzero(&dst->sin6, sizeof(dst->sin6));
src->sin6.sin6_family = AF_INET6;
src->sin6.sin6_len = sizeof(struct sockaddr_in6);
dst->sin6.sin6_family = AF_INET6;
dst->sin6.sin6_len = sizeof(struct sockaddr_in6);
if (dir == IPSEC_DIR_OUTBOUND)
sin6 = &src->sin6;
else
sin6 = &dst->sin6;
sin6->sin6_addr = inp->in6p_laddr;
sin6->sin6_port = inp->inp_lport;
if (IN6_IS_SCOPE_LINKLOCAL(&inp->in6p_laddr)) {
/* XXXAE: use in6p_zoneid */
sin6->sin6_addr.s6_addr16[1] = 0;
sin6->sin6_scope_id = ntohs(
inp->in6p_laddr.s6_addr16[1]);
}
if (dir == IPSEC_DIR_OUTBOUND)
sin6 = &dst->sin6;
else
sin6 = &src->sin6;
sin6->sin6_addr = inp->in6p_faddr;
sin6->sin6_port = inp->inp_fport;
if (IN6_IS_SCOPE_LINKLOCAL(&inp->in6p_faddr)) {
/* XXXAE: use in6p_zoneid */
sin6->sin6_addr.s6_addr16[1] = 0;
sin6->sin6_scope_id = ntohs(
inp->in6p_faddr.s6_addr16[1]);
}
}
#endif
#ifdef INET
if (inp->inp_vflag & INP_IPV4) {
struct sockaddr_in *sin;
bzero(&src->sin, sizeof(src->sin));
bzero(&dst->sin, sizeof(dst->sin));
src->sin.sin_family = AF_INET;
src->sin.sin_len = sizeof(struct sockaddr_in);
dst->sin.sin_family = AF_INET;
dst->sin.sin_len = sizeof(struct sockaddr_in);
if (dir == IPSEC_DIR_OUTBOUND)
sin = &src->sin;
else
sin = &dst->sin;
sin->sin_addr = inp->inp_laddr;
sin->sin_port = inp->inp_lport;
if (dir == IPSEC_DIR_OUTBOUND)
sin = &dst->sin;
else
sin = &src->sin;
sin->sin_addr = inp->inp_faddr;
sin->sin_port = inp->inp_fport;
}
#endif
}
void
ipsec_setspidx_inpcb(struct inpcb *inp, struct secpolicyindex *spidx,
u_int dir)
{
ipsec_setsockaddrs_inpcb(inp, &spidx->src, &spidx->dst, dir);
#ifdef INET6
if (inp->inp_vflag & INP_IPV6) {
spidx->prefs = sizeof(struct in6_addr) << 3;
spidx->prefd = sizeof(struct in6_addr) << 3;
}
#endif
#ifdef INET
if (inp->inp_vflag & INP_IPV4) {
spidx->prefs = sizeof(struct in_addr) << 3;
spidx->prefd = sizeof(struct in_addr) << 3;
}
#endif
spidx->ul_proto = IPPROTO_TCP; /* XXX: currently only TCP uses this */
spidx->dir = dir;
KEYDBG(IPSEC_DUMP,
printf("%s: ", __func__); kdebug_secpolicyindex(spidx, NULL));
}
/* Initialize PCB policy. */
int
ipsec_init_pcbpolicy(struct inpcb *inp)
{
IPSEC_ASSERT(inp != NULL, ("null inp"));
IPSEC_ASSERT(inp->inp_sp == NULL, ("inp_sp already initialized"));
inp->inp_sp = malloc(sizeof(struct inpcbpolicy), M_IPSEC_INPCB,
M_NOWAIT | M_ZERO);
if (inp->inp_sp == NULL)
return (ENOBUFS);
return (0);
}
/* Delete PCB policy. */
int
ipsec_delete_pcbpolicy(struct inpcb *inp)
{
if (inp->inp_sp == NULL)
return (0);
if (inp->inp_sp->flags & INP_INBOUND_POLICY)
key_freesp(&inp->inp_sp->sp_in);
if (inp->inp_sp->flags & INP_OUTBOUND_POLICY)
key_freesp(&inp->inp_sp->sp_out);
free(inp->inp_sp, M_IPSEC_INPCB);
inp->inp_sp = NULL;
return (0);
}
/* Deep-copy a policy in PCB. */
static struct secpolicy *
ipsec_deepcopy_pcbpolicy(struct secpolicy *src)
{
struct secpolicy *dst;
int i;
if (src == NULL)
return (NULL);
IPSEC_ASSERT(src->state == IPSEC_SPSTATE_PCB, ("SP isn't PCB"));
dst = key_newsp();
if (dst == NULL)
return (NULL);
/* spidx is not copied here */
dst->policy = src->policy;
dst->state = src->state;
dst->priority = src->priority;
/* Do not touch the refcnt field. */
/* Copy IPsec request chain. */
for (i = 0; i < src->tcount; i++) {
dst->req[i] = ipsec_newisr();
if (dst->req[i] == NULL) {
key_freesp(&dst);
return (NULL);
}
bcopy(src->req[i], dst->req[i], sizeof(struct ipsecrequest));
dst->tcount++;
}
KEYDBG(IPSEC_DUMP,
printf("%s: copied SP(%p) -> SP(%p)\n", __func__, src, dst);
kdebug_secpolicy(dst));
return (dst);
}
/*
* Copy IPsec policy from old INPCB into new.
* It is expected that new INPCB has not configured policies.
*/
int
ipsec_copy_pcbpolicy(struct inpcb *old, struct inpcb *new)
{
struct secpolicy *sp;
/*
* old->inp_sp can be NULL if PCB was created when an IPsec
* support was unavailable. This is not an error, we don't have
* policies in this PCB, so nothing to copy.
*/
if (old->inp_sp == NULL)
return (0);
IPSEC_ASSERT(new->inp_sp != NULL, ("new inp_sp is NULL"));
IPSEC_ASSERT((new->inp_sp->flags & (
INP_INBOUND_POLICY | INP_OUTBOUND_POLICY)) == 0,
("new PCB already has configured policies"));
INP_WLOCK_ASSERT(new);
INP_LOCK_ASSERT(old);
if (old->inp_sp->flags & INP_INBOUND_POLICY) {
sp = ipsec_deepcopy_pcbpolicy(old->inp_sp->sp_in);
if (sp == NULL)
return (ENOBUFS);
ipsec_setspidx_inpcb(new, &sp->spidx, IPSEC_DIR_INBOUND);
new->inp_sp->sp_in = sp;
new->inp_sp->flags |= INP_INBOUND_POLICY;
}
if (old->inp_sp->flags & INP_OUTBOUND_POLICY) {
sp = ipsec_deepcopy_pcbpolicy(old->inp_sp->sp_out);
if (sp == NULL)
return (ENOBUFS);
ipsec_setspidx_inpcb(new, &sp->spidx, IPSEC_DIR_OUTBOUND);
new->inp_sp->sp_out = sp;
new->inp_sp->flags |= INP_OUTBOUND_POLICY;
}
return (0);
}
static int
ipsec_set_pcbpolicy(struct inpcb *inp, struct ucred *cred,
void *request, size_t len)
{
struct sadb_x_policy *xpl;
struct secpolicy **spp, *newsp;
int error, flags;
xpl = (struct sadb_x_policy *)request;
/* Select direction. */
switch (xpl->sadb_x_policy_dir) {
case IPSEC_DIR_INBOUND:
case IPSEC_DIR_OUTBOUND:
break;
default:
ipseclog((LOG_ERR, "%s: invalid direction=%u\n", __func__,
xpl->sadb_x_policy_dir));
return (EINVAL);
}
/*
* Privileged sockets are allowed to set own security policy
* and configure IPsec bypass. Unprivileged sockets only can
* have ENTRUST policy.
*/
switch (xpl->sadb_x_policy_type) {
case IPSEC_POLICY_IPSEC:
case IPSEC_POLICY_BYPASS:
if (cred != NULL &&
priv_check_cred(cred, PRIV_NETINET_IPSEC, 0) != 0)
return (EACCES);
/* Allocate new SP entry. */
newsp = key_msg2sp(xpl, len, &error);
if (newsp == NULL)
return (error);
newsp->state = IPSEC_SPSTATE_PCB;
newsp->spidx.ul_proto = IPSEC_ULPROTO_ANY;
#ifdef INET
if (inp->inp_vflag & INP_IPV4) {
newsp->spidx.src.sin.sin_family =
newsp->spidx.dst.sin.sin_family = AF_INET;
newsp->spidx.src.sin.sin_len =
newsp->spidx.dst.sin.sin_len =
sizeof(struct sockaddr_in);
}
#endif
#ifdef INET6
if (inp->inp_vflag & INP_IPV6) {
newsp->spidx.src.sin6.sin6_family =
newsp->spidx.dst.sin6.sin6_family = AF_INET6;
newsp->spidx.src.sin6.sin6_len =
newsp->spidx.dst.sin6.sin6_len =
sizeof(struct sockaddr_in6);
}
#endif
break;
case IPSEC_POLICY_ENTRUST:
/* We just use NULL pointer for ENTRUST policy */
newsp = NULL;
break;
default:
/* Other security policy types aren't allowed for PCB */
return (EINVAL);
}
INP_WLOCK(inp);
if (xpl->sadb_x_policy_dir == IPSEC_DIR_INBOUND) {
spp = &inp->inp_sp->sp_in;
flags = INP_INBOUND_POLICY;
} else {
spp = &inp->inp_sp->sp_out;
flags = INP_OUTBOUND_POLICY;
}
/* Clear old SP and set new SP. */
if (*spp != NULL)
key_freesp(spp);
*spp = newsp;
KEYDBG(IPSEC_DUMP,
printf("%s: new SP(%p)\n", __func__, newsp));
if (newsp == NULL)
inp->inp_sp->flags &= ~flags;
else {
inp->inp_sp->flags |= flags;
KEYDBG(IPSEC_DUMP, kdebug_secpolicy(newsp));
}
INP_WUNLOCK(inp);
return (0);
}
static int
ipsec_get_pcbpolicy(struct inpcb *inp, void *request, size_t *len)
{
struct sadb_x_policy *xpl;
struct secpolicy *sp;
int error, flags;
xpl = (struct sadb_x_policy *)request;
INP_RLOCK(inp);
flags = inp->inp_sp->flags;
/* Select direction. */
switch (xpl->sadb_x_policy_dir) {
case IPSEC_DIR_INBOUND:
sp = inp->inp_sp->sp_in;
flags &= INP_INBOUND_POLICY;
break;
case IPSEC_DIR_OUTBOUND:
sp = inp->inp_sp->sp_out;
flags &= INP_OUTBOUND_POLICY;
break;
default:
INP_RUNLOCK(inp);
ipseclog((LOG_ERR, "%s: invalid direction=%u\n", __func__,
xpl->sadb_x_policy_dir));
return (EINVAL);
}
if (flags == 0) {
/* Return ENTRUST policy */
INP_RUNLOCK(inp);
xpl->sadb_x_policy_exttype = SADB_X_EXT_POLICY;
xpl->sadb_x_policy_type = IPSEC_POLICY_ENTRUST;
xpl->sadb_x_policy_id = 0;
xpl->sadb_x_policy_priority = 0;
xpl->sadb_x_policy_len = PFKEY_UNIT64(sizeof(*xpl));
*len = sizeof(*xpl);
return (0);
}
IPSEC_ASSERT(sp != NULL,
("sp is NULL, but flags is 0x%04x", inp->inp_sp->flags));
key_addref(sp);
INP_RUNLOCK(inp);
error = key_sp2msg(sp, request, len);
key_freesp(&sp);
if (error == EINVAL)
return (error);
/*
* We return "success", but user should check *len.
* *len will be set to size of valid data and
* sadb_x_policy_len will contain needed size.
*/
return (0);
}
/* Handle socket option control request for PCB */
static int
ipsec_control_pcbpolicy(struct inpcb *inp, struct sockopt *sopt)
{
void *optdata;
size_t optlen;
int error;
if (inp->inp_sp == NULL)
return (ENOPROTOOPT);
/* Limit maximum request size to PAGE_SIZE */
optlen = sopt->sopt_valsize;
if (optlen < sizeof(struct sadb_x_policy) || optlen > PAGE_SIZE)
return (EINVAL);
optdata = malloc(optlen, M_TEMP, sopt->sopt_td ? M_WAITOK: M_NOWAIT);
if (optdata == NULL)
return (ENOBUFS);
/*
* We need a hint from the user, what policy is requested - input
* or output? User should specify it in the buffer, even for
* setsockopt().
*/
error = sooptcopyin(sopt, optdata, optlen, optlen);
if (error == 0) {
if (sopt->sopt_dir == SOPT_SET)
error = ipsec_set_pcbpolicy(inp,
sopt->sopt_td ? sopt->sopt_td->td_ucred: NULL,
optdata, optlen);
else {
error = ipsec_get_pcbpolicy(inp, optdata, &optlen);
if (error == 0)
error = sooptcopyout(sopt, optdata, optlen);
}
}
free(optdata, M_TEMP);
return (error);
}
#ifdef INET
/*
* IPSEC_PCBCTL() method implementation for IPv4.
*/
int
ipsec4_pcbctl(struct inpcb *inp, struct sockopt *sopt)
{
if (sopt->sopt_name != IP_IPSEC_POLICY)
return (ENOPROTOOPT);
return (ipsec_control_pcbpolicy(inp, sopt));
}
#endif
#ifdef INET6
/*
* IPSEC_PCBCTL() method implementation for IPv6.
*/
int
ipsec6_pcbctl(struct inpcb *inp, struct sockopt *sopt)
{
if (sopt->sopt_name != IPV6_IPSEC_POLICY)
return (ENOPROTOOPT);
return (ipsec_control_pcbpolicy(inp, sopt));
}
#endif

View File

@ -0,0 +1,190 @@
/*-
* Copyright (c) 2016 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.
*
* $FreeBSD$
*/
#ifndef _NETIPSEC_IPSEC_SUPPORT_H_
#define _NETIPSEC_IPSEC_SUPPORT_H_
#ifdef _KERNEL
#if defined(IPSEC) || defined(IPSEC_SUPPORT)
struct mbuf;
struct inpcb;
struct tcphdr;
struct sockopt;
struct sockaddr;
struct ipsec_support;
struct tcpmd5_support;
size_t ipsec_hdrsiz_inpcb(struct inpcb *);
int ipsec_init_pcbpolicy(struct inpcb *);
int ipsec_delete_pcbpolicy(struct inpcb *);
int ipsec_copy_pcbpolicy(struct inpcb *, struct inpcb *);
struct ipsec_methods {
int (*input)(struct mbuf *, int, int);
int (*check_policy)(const struct mbuf *, struct inpcb *);
int (*forward)(struct mbuf *);
int (*output)(struct mbuf *, struct inpcb *);
int (*pcbctl)(struct inpcb *, struct sockopt *);
size_t (*hdrsize)(struct inpcb *);
int (*capability)(struct mbuf *, u_int);
int (*ctlinput)(int, struct sockaddr *, void *);
int (*udp_input)(struct mbuf *, int, int);
int (*udp_pcbctl)(struct inpcb *, struct sockopt *);
};
#define IPSEC_CAP_OPERABLE 1
#define IPSEC_CAP_BYPASS_FILTER 2
struct tcpmd5_methods {
int (*input)(struct mbuf *, struct tcphdr *, u_char *);
int (*output)(struct mbuf *, struct tcphdr *, u_char *);
int (*pcbctl)(struct inpcb *, struct sockopt *);
};
#define IPSEC_MODULE_ENABLED 0x0001
#define IPSEC_ENABLED(proto) \
((proto ## _ipsec_support)->enabled & IPSEC_MODULE_ENABLED)
#define TCPMD5_ENABLED() IPSEC_ENABLED(tcp)
#ifdef TCP_SIGNATURE
/* TCP-MD5 build in the kernel */
struct tcpmd5_support {
const u_int enabled;
const struct tcpmd5_methods * const methods;
};
extern const struct tcpmd5_support * const tcp_ipsec_support;
#define TCPMD5_INPUT(m, ...) \
(*tcp_ipsec_support->methods->input)(m, __VA_ARGS__)
#define TCPMD5_OUTPUT(m, ...) \
(*tcp_ipsec_support->methods->output)(m, __VA_ARGS__)
#define TCPMD5_PCBCTL(inp, sopt) \
(*tcp_ipsec_support->methods->pcbctl)(inp, sopt)
#elif defined(IPSEC_SUPPORT)
/* TCP-MD5 build as module */
struct tcpmd5_support {
volatile u_int enabled;
const struct tcpmd5_methods * volatile methods;
};
extern struct tcpmd5_support * const tcp_ipsec_support;
void tcpmd5_support_enable(const struct tcpmd5_methods * const);
void tcpmd5_support_disable(void);
int tcpmd5_kmod_pcbctl(struct tcpmd5_support * const, struct inpcb *,
struct sockopt *);
int tcpmd5_kmod_input(struct tcpmd5_support * const, struct mbuf *,
struct tcphdr *, u_char *);
int tcpmd5_kmod_output(struct tcpmd5_support * const, struct mbuf *,
struct tcphdr *, u_char *);
#define TCPMD5_INPUT(m, ...) \
tcpmd5_kmod_input(tcp_ipsec_support, m, __VA_ARGS__)
#define TCPMD5_OUTPUT(m, ...) \
tcpmd5_kmod_output(tcp_ipsec_support, m, __VA_ARGS__)
#define TCPMD5_PCBCTL(inp, sopt) \
tcpmd5_kmod_pcbctl(tcp_ipsec_support, inp, sopt)
#endif
#endif /* IPSEC || IPSEC_SUPPORT */
#if defined(IPSEC)
struct ipsec_support {
const u_int enabled;
const struct ipsec_methods * const methods;
};
extern const struct ipsec_support * const ipv4_ipsec_support;
extern const struct ipsec_support * const ipv6_ipsec_support;
#define IPSEC_INPUT(proto, m, ...) \
(*(proto ## _ipsec_support)->methods->input)(m, __VA_ARGS__)
#define IPSEC_CHECK_POLICY(proto, m, ...) \
(*(proto ## _ipsec_support)->methods->check_policy)(m, __VA_ARGS__)
#define IPSEC_FORWARD(proto, m) \
(*(proto ## _ipsec_support)->methods->forward)(m)
#define IPSEC_OUTPUT(proto, m, ...) \
(*(proto ## _ipsec_support)->methods->output)(m, __VA_ARGS__)
#define IPSEC_PCBCTL(proto, inp, sopt) \
(*(proto ## _ipsec_support)->methods->pcbctl)(inp, sopt)
#define IPSEC_CAPS(proto, m, ...) \
(*(proto ## _ipsec_support)->methods->capability)(m, __VA_ARGS__)
#define IPSEC_HDRSIZE(proto, inp) \
(*(proto ## _ipsec_support)->methods->hdrsize)(inp)
#define UDPENCAP_INPUT(m, ...) \
(*ipv4_ipsec_support->methods->udp_input)(m, __VA_ARGS__)
#define UDPENCAP_PCBCTL(inp, sopt) \
(*ipv4_ipsec_support->methods->udp_pcbctl)(inp, sopt)
#elif defined(IPSEC_SUPPORT)
struct ipsec_support {
volatile u_int enabled;
const struct ipsec_methods * volatile methods;
};
extern struct ipsec_support * const ipv4_ipsec_support;
extern struct ipsec_support * const ipv6_ipsec_support;
void ipsec_support_enable(struct ipsec_support * const,
const struct ipsec_methods * const);
void ipsec_support_disable(struct ipsec_support * const);
int ipsec_kmod_input(struct ipsec_support * const, struct mbuf *, int, int);
int ipsec_kmod_check_policy(struct ipsec_support * const, struct mbuf *,
struct inpcb *);
int ipsec_kmod_forward(struct ipsec_support * const, struct mbuf *);
int ipsec_kmod_output(struct ipsec_support * const, struct mbuf *,
struct inpcb *);
int ipsec_kmod_pcbctl(struct ipsec_support * const, struct inpcb *,
struct sockopt *);
int ipsec_kmod_capability(struct ipsec_support * const, struct mbuf *, u_int);
size_t ipsec_kmod_hdrsize(struct ipsec_support * const, struct inpcb *);
int ipsec_kmod_udp_input(struct ipsec_support * const, struct mbuf *, int, int);
int ipsec_kmod_udp_pcbctl(struct ipsec_support * const, struct inpcb *,
struct sockopt *);
#define UDPENCAP_INPUT(m, ...) \
ipsec_kmod_udp_input(ipv4_ipsec_support, m, __VA_ARGS__)
#define UDPENCAP_PCBCTL(inp, sopt) \
ipsec_kmod_udp_pcbctl(ipv4_ipsec_support, inp, sopt)
#define IPSEC_INPUT(proto, ...) \
ipsec_kmod_input(proto ## _ipsec_support, __VA_ARGS__)
#define IPSEC_CHECK_POLICY(proto, ...) \
ipsec_kmod_check_policy(proto ## _ipsec_support, __VA_ARGS__)
#define IPSEC_FORWARD(proto, ...) \
ipsec_kmod_forward(proto ## _ipsec_support, __VA_ARGS__)
#define IPSEC_OUTPUT(proto, ...) \
ipsec_kmod_output(proto ## _ipsec_support, __VA_ARGS__)
#define IPSEC_PCBCTL(proto, ...) \
ipsec_kmod_pcbctl(proto ## _ipsec_support, __VA_ARGS__)
#define IPSEC_CAPS(proto, ...) \
ipsec_kmod_capability(proto ## _ipsec_support, __VA_ARGS__)
#define IPSEC_HDRSIZE(proto, ...) \
ipsec_kmod_hdrsize(proto ## _ipsec_support, __VA_ARGS__)
#endif /* IPSEC_SUPPORT */
#endif /* _KERNEL */
#endif /* _NETIPSEC_IPSEC_SUPPORT_H_ */

File diff suppressed because it is too large Load Diff

View File

@ -37,7 +37,6 @@
struct secpolicy;
struct secpolicyindex;
struct ipsecrequest;
struct secasvar;
struct sockaddr;
struct socket;
@ -46,60 +45,33 @@ struct sadb_x_policy;
struct secasindex;
union sockaddr_union;
extern void key_addref(struct secpolicy *sp);
extern int key_havesp(u_int dir);
extern struct secpolicy *key_allocsp(struct secpolicyindex *, u_int,
const char*, int);
extern struct secpolicy *key_allocsp2(u_int32_t spi, union sockaddr_union *dst,
u_int8_t proto, u_int dir, const char*, int);
extern struct secpolicy *key_newsp(const char*, int);
#if 0
extern struct secpolicy *key_gettunnel(const struct sockaddr *,
const struct sockaddr *, const struct sockaddr *,
const struct sockaddr *, const char*, int);
#endif
/* NB: prepend with _ for KAME IPv6 compatbility */
extern void _key_freesp(struct secpolicy **, const char*, int);
struct secpolicy *key_newsp(void);
struct secpolicy *key_allocsp(struct secpolicyindex *, u_int);
struct secpolicy *key_msg2sp(struct sadb_x_policy *, size_t, int *);
int key_sp2msg(struct secpolicy *, void *, size_t *);
void key_addref(struct secpolicy *);
void key_freesp(struct secpolicy **);
int key_spdacquire(struct secpolicy *);
int key_havesp(u_int);
void key_bumpspgen(void);
uint32_t key_getspgen(void);
uint32_t key_newreqid(void);
#define KEY_ALLOCSP(spidx, dir) \
key_allocsp(spidx, dir, __FILE__, __LINE__)
#define KEY_ALLOCSP2(spi, dst, proto, dir) \
key_allocsp2(spi, dst, proto, dir, __FILE__, __LINE__)
#define KEY_NEWSP() \
key_newsp(__FILE__, __LINE__)
#if 0
#define KEY_GETTUNNEL(osrc, odst, isrc, idst) \
key_gettunnel(osrc, odst, isrc, idst, __FILE__, __LINE__)
#endif
#define KEY_FREESP(spp) \
_key_freesp(spp, __FILE__, __LINE__)
struct secasvar *key_allocsa(union sockaddr_union *, uint8_t, uint32_t);
struct secasvar *key_allocsa_tunnel(union sockaddr_union *,
union sockaddr_union *, uint8_t);
struct secasvar *key_allocsa_policy(struct secpolicy *,
const struct secasindex *, int *);
struct secasvar *key_allocsa_tcpmd5(struct secasindex *);
void key_freesav(struct secasvar **);
extern struct secasvar *key_allocsa(union sockaddr_union *, u_int, u_int32_t,
const char*, int);
extern struct secasvar *key_allocsa_tunnel(union sockaddr_union *,
union sockaddr_union *, u_int, const char*, int);
extern void key_addrefsa(struct secasvar *, const char*, int);
extern void key_freesav(struct secasvar **, const char*, int);
int key_sockaddrcmp(const struct sockaddr *, const struct sockaddr *, int);
int key_sockaddrcmp_withmask(const struct sockaddr *, const struct sockaddr *,
size_t);
#define KEY_ALLOCSA(dst, proto, spi) \
key_allocsa(dst, proto, spi, __FILE__, __LINE__)
#define KEY_ALLOCSA_TUNNEL(src, dst, proto) \
key_allocsa_tunnel(src, dst, proto, __FILE__, __LINE__)
#define KEY_ADDREFSA(sav) \
key_addrefsa(sav, __FILE__, __LINE__)
#define KEY_FREESAV(psav) \
key_freesav(psav, __FILE__, __LINE__)
int key_register_ifnet(struct secpolicy **, u_int);
void key_unregister_ifnet(struct secpolicy **, u_int);
extern void key_freeso(struct socket *);
extern int key_checktunnelsanity(struct secasvar *, u_int,
caddr_t, caddr_t);
extern int key_checkrequest(struct ipsecrequest *isr,
const struct secasindex *);
extern struct secpolicy *key_msg2sp(struct sadb_x_policy *,
size_t, int *);
extern struct mbuf *key_sp2msg(struct secpolicy *);
extern int key_ismyaddr(struct sockaddr *);
extern int key_spdacquire(struct secpolicy *);
extern u_long key_random(void);
extern void key_randomfill(void *, size_t);
extern void key_freereg(struct socket *);
@ -109,11 +81,8 @@ extern void key_init(void);
extern void key_destroy(void);
#endif
extern void key_sa_recordxfer(struct secasvar *, struct mbuf *);
#ifdef IPSEC_NAT_T
u_int16_t key_portfromsaddr(struct sockaddr *);
#define KEY_PORTFROMSADDR(saddr) \
key_portfromsaddr((struct sockaddr *)(saddr))
#endif
uint16_t key_portfromsaddr(struct sockaddr *);
void key_porttosaddr(struct sockaddr *, uint16_t port);
#ifdef MALLOC_DECLARE
MALLOC_DECLARE(M_IPSEC_SA);

View File

@ -56,6 +56,7 @@
#include <netipsec/ipsec.h>
#ifdef _KERNEL
#include <netipsec/keydb.h>
#include <netipsec/xform.h>
#endif
#ifndef _KERNEL
@ -458,137 +459,219 @@ kdebug_sadb_x_policy(struct sadb_ext *ext)
#ifdef _KERNEL
/* %%%: about SPD and SAD */
const char*
kdebug_secpolicy_state(u_int state)
{
switch (state) {
case IPSEC_SPSTATE_DEAD:
return ("dead");
case IPSEC_SPSTATE_LARVAL:
return ("larval");
case IPSEC_SPSTATE_ALIVE:
return ("alive");
case IPSEC_SPSTATE_PCB:
return ("pcb");
case IPSEC_SPSTATE_IFNET:
return ("ifnet");
}
return ("unknown");
}
const char*
kdebug_secpolicy_policy(u_int policy)
{
switch (policy) {
case IPSEC_POLICY_DISCARD:
return ("discard");
case IPSEC_POLICY_NONE:
return ("none");
case IPSEC_POLICY_IPSEC:
return ("ipsec");
case IPSEC_POLICY_ENTRUST:
return ("entrust");
case IPSEC_POLICY_BYPASS:
return ("bypass");
}
return ("unknown");
}
const char*
kdebug_secpolicyindex_dir(u_int dir)
{
switch (dir) {
case IPSEC_DIR_ANY:
return ("any");
case IPSEC_DIR_INBOUND:
return ("in");
case IPSEC_DIR_OUTBOUND:
return ("out");
}
return ("unknown");
}
const char*
kdebug_ipsecrequest_level(u_int level)
{
switch (level) {
case IPSEC_LEVEL_DEFAULT:
return ("default");
case IPSEC_LEVEL_USE:
return ("use");
case IPSEC_LEVEL_REQUIRE:
return ("require");
case IPSEC_LEVEL_UNIQUE:
return ("unique");
}
return ("unknown");
}
const char*
kdebug_secasindex_mode(u_int mode)
{
switch (mode) {
case IPSEC_MODE_ANY:
return ("any");
case IPSEC_MODE_TRANSPORT:
return ("transport");
case IPSEC_MODE_TUNNEL:
return ("tunnel");
case IPSEC_MODE_TCPMD5:
return ("tcp-md5");
}
return ("unknown");
}
const char*
kdebug_secasv_state(u_int state)
{
switch (state) {
case SADB_SASTATE_LARVAL:
return ("larval");
case SADB_SASTATE_MATURE:
return ("mature");
case SADB_SASTATE_DYING:
return ("dying");
case SADB_SASTATE_DEAD:
return ("dead");
}
return ("unknown");
}
static char*
kdebug_port2str(const struct sockaddr *sa, char *buf, size_t len)
{
uint16_t port;
IPSEC_ASSERT(sa != NULL, ("null sa"));
switch (sa->sa_family) {
#ifdef INET
case AF_INET:
port = ntohs(((const struct sockaddr_in *)sa)->sin_port);
break;
#endif
#ifdef INET6
case AF_INET6:
port = ntohs(((const struct sockaddr_in6 *)sa)->sin6_port);
break;
#endif
default:
port = 0;
}
if (port == 0)
return ("*");
snprintf(buf, len, "%u", port);
return (buf);
}
void
kdebug_secpolicy(struct secpolicy *sp)
{
/* sanity check */
if (sp == NULL)
panic("%s: NULL pointer was passed.\n", __func__);
u_int idx;
printf("secpolicy{ refcnt=%u policy=%u\n",
sp->refcnt, sp->policy);
kdebug_secpolicyindex(&sp->spidx);
switch (sp->policy) {
case IPSEC_POLICY_DISCARD:
printf(" type=discard }\n");
break;
case IPSEC_POLICY_NONE:
printf(" type=none }\n");
break;
case IPSEC_POLICY_IPSEC:
{
struct ipsecrequest *isr;
for (isr = sp->req; isr != NULL; isr = isr->next) {
printf(" level=%u\n", isr->level);
kdebug_secasindex(&isr->saidx);
if (isr->sav != NULL)
kdebug_secasv(isr->sav);
}
IPSEC_ASSERT(sp != NULL, ("null sp"));
printf("SP { refcnt=%u id=%u priority=%u state=%s policy=%s\n",
sp->refcnt, sp->id, sp->priority,
kdebug_secpolicy_state(sp->state),
kdebug_secpolicy_policy(sp->policy));
kdebug_secpolicyindex(&sp->spidx, " ");
for (idx = 0; idx < sp->tcount; idx++) {
printf(" req[%u]{ level=%s ", idx,
kdebug_ipsecrequest_level(sp->req[idx]->level));
kdebug_secasindex(&sp->req[idx]->saidx, NULL);
printf(" }\n");
}
break;
case IPSEC_POLICY_BYPASS:
printf(" type=bypass }\n");
break;
case IPSEC_POLICY_ENTRUST:
printf(" type=entrust }\n");
break;
default:
printf("%s: Invalid policy found. %d\n", __func__, sp->policy);
break;
}
return;
printf("}\n");
}
void
kdebug_secpolicyindex(struct secpolicyindex *spidx)
kdebug_secpolicyindex(struct secpolicyindex *spidx, const char *indent)
{
char buf[INET6_ADDRSTRLEN];
char buf[IPSEC_ADDRSTRLEN];
/* sanity check */
if (spidx == NULL)
panic("%s: NULL pointer was passed.\n", __func__);
printf("secpolicyindex{ dir=%u prefs=%u prefd=%u ul_proto=%u\n",
spidx->dir, spidx->prefs, spidx->prefd, spidx->ul_proto);
printf("%s -> ", ipsec_address(&spidx->src, buf, sizeof(buf)));
printf("%s }\n", ipsec_address(&spidx->dst, buf, sizeof(buf)));
IPSEC_ASSERT(spidx != NULL, ("null spidx"));
if (indent != NULL)
printf("%s", indent);
printf("spidx { dir=%s ul_proto=",
kdebug_secpolicyindex_dir(spidx->dir));
if (spidx->ul_proto == IPSEC_ULPROTO_ANY)
printf("* ");
else
printf("%u ", spidx->ul_proto);
printf("%s/%u -> ", ipsec_address(&spidx->src, buf, sizeof(buf)),
spidx->prefs);
printf("%s/%u }\n", ipsec_address(&spidx->dst, buf, sizeof(buf)),
spidx->prefd);
}
void
kdebug_secasindex(struct secasindex *saidx)
kdebug_secasindex(const struct secasindex *saidx, const char *indent)
{
char buf[INET6_ADDRSTRLEN];
char buf[IPSEC_ADDRSTRLEN], port[6];
/* sanity check */
if (saidx == NULL)
panic("%s: NULL pointer was passed.\n", __func__);
printf("secasindex{ mode=%u proto=%u\n",
saidx->mode, saidx->proto);
printf("%s -> ", ipsec_address(&saidx->src, buf, sizeof(buf)));
printf("%s }\n", ipsec_address(&saidx->dst, buf, sizeof(buf)));
IPSEC_ASSERT(saidx != NULL, ("null saidx"));
if (indent != NULL)
printf("%s", indent);
printf("saidx { mode=%s proto=%u reqid=%u ",
kdebug_secasindex_mode(saidx->mode), saidx->proto, saidx->reqid);
printf("%s:%s -> ", ipsec_address(&saidx->src, buf, sizeof(buf)),
kdebug_port2str(&saidx->src.sa, port, sizeof(port)));
printf("%s:%s }\n", ipsec_address(&saidx->dst, buf, sizeof(buf)),
kdebug_port2str(&saidx->dst.sa, port, sizeof(port)));
}
static void
kdebug_sec_lifetime(struct seclifetime *lft)
kdebug_sec_lifetime(struct seclifetime *lft, const char *indent)
{
/* sanity check */
if (lft == NULL)
panic("%s: NULL pointer was passed.\n", __func__);
printf("sec_lifetime{ alloc=%u, bytes=%u\n",
lft->allocations, (u_int32_t)lft->bytes);
printf(" addtime=%u, usetime=%u }\n",
(u_int32_t)lft->addtime, (u_int32_t)lft->usetime);
return;
IPSEC_ASSERT(lft != NULL, ("null lft"));
if (indent != NULL)
printf("%s", indent);
printf("lifetime { alloc=%u, bytes=%ju addtime=%ju usetime=%ju }\n",
lft->allocations, (uintmax_t)lft->bytes, (uintmax_t)lft->addtime,
(uintmax_t)lft->usetime);
}
void
kdebug_secasv(struct secasvar *sav)
kdebug_secash(struct secashead *sah, const char *indent)
{
/* sanity check */
if (sav == NULL)
panic("%s: NULL pointer was passed.\n", __func__);
printf("secas{");
kdebug_secasindex(&sav->sah->saidx);
printf(" refcnt=%u state=%u auth=%u enc=%u\n",
sav->refcnt, sav->state, sav->alg_auth, sav->alg_enc);
printf(" spi=%u flags=%u\n",
(u_int32_t)ntohl(sav->spi), sav->flags);
if (sav->key_auth != NULL)
kdebug_sadb_key((struct sadb_ext *)sav->key_auth);
if (sav->key_enc != NULL)
kdebug_sadb_key((struct sadb_ext *)sav->key_enc);
if (sav->replay != NULL) {
SECASVAR_LOCK(sav);
kdebug_secreplay(sav->replay);
SECASVAR_UNLOCK(sav);
}
if (sav->lft_c != NULL)
kdebug_sec_lifetime(sav->lft_c);
if (sav->lft_h != NULL)
kdebug_sec_lifetime(sav->lft_h);
if (sav->lft_s != NULL)
kdebug_sec_lifetime(sav->lft_s);
#ifdef notyet
/* XXX: misc[123] ? */
#endif
return;
IPSEC_ASSERT(sah != NULL, ("null sah"));
if (indent != NULL)
printf("%s", indent);
printf("SAH { refcnt=%u state=%s\n", sah->refcnt,
kdebug_secasv_state(sah->state));
if (indent != NULL)
printf("%s", indent);
kdebug_secasindex(&sah->saidx, indent);
if (indent != NULL)
printf("%s", indent);
printf("}\n");
}
static void
@ -596,27 +679,80 @@ kdebug_secreplay(struct secreplay *rpl)
{
int len, l;
/* sanity check */
if (rpl == NULL)
panic("%s: NULL pointer was passed.\n", __func__);
IPSEC_ASSERT(rpl != NULL, ("null rpl"));
printf(" secreplay{ count=%u bitmap_size=%u wsize=%u seq=%u lastseq=%u",
rpl->count, rpl->bitmap_size, rpl->wsize, rpl->seq, rpl->lastseq);
if (rpl->bitmap == NULL) {
printf(" }\n");
printf(" }\n");
return;
}
printf("\n bitmap { ");
printf("\n bitmap { ");
for (len = 0; len < rpl->bitmap_size*4; len++) {
for (l = 7; l >= 0; l--)
printf("%u", (((rpl->bitmap)[len] >> l) & 1) ? 1 : 0);
}
printf(" }\n");
printf(" }\n");
}
return;
static void
kdebug_secnatt(struct secnatt *natt)
{
char buf[IPSEC_ADDRSTRLEN];
IPSEC_ASSERT(natt != NULL, ("null natt"));
printf(" natt{ sport=%u dport=%u ", ntohs(natt->sport),
ntohs(natt->dport));
if (natt->flags & IPSEC_NATT_F_OAI)
printf("oai=%s ", ipsec_address(&natt->oai, buf, sizeof(buf)));
if (natt->flags & IPSEC_NATT_F_OAR)
printf("oar=%s ", ipsec_address(&natt->oar, buf, sizeof(buf)));
printf("}\n");
}
void
kdebug_secasv(struct secasvar *sav)
{
struct seclifetime lft_c;
IPSEC_ASSERT(sav != NULL, ("null sav"));
printf("SA { refcnt=%u spi=%u seq=%u pid=%u flags=0x%x state=%s\n",
sav->refcnt, ntohl(sav->spi), sav->seq, (uint32_t)sav->pid,
sav->flags, kdebug_secasv_state(sav->state));
kdebug_secash(sav->sah, " ");
lft_c.addtime = sav->created;
lft_c.allocations = (uint32_t)counter_u64_fetch(
sav->lft_c_allocations);
lft_c.bytes = counter_u64_fetch(sav->lft_c_bytes);
lft_c.usetime = sav->firstused;
kdebug_sec_lifetime(&lft_c, " c_");
if (sav->lft_h != NULL)
kdebug_sec_lifetime(sav->lft_h, " h_");
if (sav->lft_s != NULL)
kdebug_sec_lifetime(sav->lft_s, " s_");
if (sav->tdb_authalgxform != NULL)
printf(" alg_auth=%s\n", sav->tdb_authalgxform->name);
if (sav->key_auth != NULL)
KEYDBG(DUMP,
kdebug_sadb_key((struct sadb_ext *)sav->key_auth));
if (sav->tdb_encalgxform != NULL)
printf(" alg_enc=%s\n", sav->tdb_encalgxform->name);
if (sav->key_enc != NULL)
KEYDBG(DUMP,
kdebug_sadb_key((struct sadb_ext *)sav->key_enc));
if (sav->natt != NULL)
kdebug_secnatt(sav->natt);
if (sav->replay != NULL) {
KEYDBG(DUMP,
SECASVAR_LOCK(sav);
kdebug_secreplay(sav->replay);
SECASVAR_UNLOCK(sav));
}
printf("}\n");
}
void
@ -668,6 +804,47 @@ kdebug_mbuf(const struct mbuf *m0)
return;
}
/* Return a printable string for the address. */
char *
ipsec_address(const union sockaddr_union* sa, char *buf, socklen_t size)
{
switch (sa->sa.sa_family) {
#ifdef INET
case AF_INET:
return (inet_ntop(AF_INET, &sa->sin.sin_addr, buf, size));
#endif /* INET */
#ifdef INET6
case AF_INET6:
if (IN6_IS_SCOPE_LINKLOCAL(&sa->sin6.sin6_addr)) {
snprintf(buf, size, "%s%%%u", inet_ntop(AF_INET6,
&sa->sin6.sin6_addr, buf, size),
sa->sin6.sin6_scope_id);
return (buf);
} else
return (inet_ntop(AF_INET6, &sa->sin6.sin6_addr,
buf, size));
#endif /* INET6 */
case 0:
return ("*");
default:
return ("(unknown address family)");
}
}
char *
ipsec_sa2str(struct secasvar *sav, char *buf, size_t size)
{
char sbuf[IPSEC_ADDRSTRLEN], dbuf[IPSEC_ADDRSTRLEN];
snprintf(buf, size, "SA(SPI=%08lx src=%s dst=%s)",
(u_long)ntohl(sav->spi),
ipsec_address(&sav->sah->saidx.src, sbuf, sizeof(sbuf)),
ipsec_address(&sav->sah->saidx.dst, dbuf, sizeof(dbuf)));
return (buf);
}
#endif /* _KERNEL */
void

View File

@ -53,10 +53,12 @@
#define KEYDEBUG_IPSEC_DATA (KEYDEBUG_IPSEC | KEYDEBUG_DATA)
#define KEYDEBUG_IPSEC_DUMP (KEYDEBUG_IPSEC | KEYDEBUG_DUMP)
#define KEYDEBUG(lev,arg) \
do { if ((V_key_debug_level & (lev)) == (lev)) { arg; } } while (0)
#define KEYDBG(lev, arg) \
if ((V_key_debug_level & (KEYDEBUG_ ## lev)) == (KEYDEBUG_ ## lev)) { \
arg; \
}
VNET_DECLARE(u_int32_t, key_debug_level);
VNET_DECLARE(uint32_t, key_debug_level);
#define V_key_debug_level VNET(key_debug_level)
#endif /*_KERNEL*/
@ -69,15 +71,26 @@ extern void kdebug_sadb_x_policy(struct sadb_ext *);
struct secpolicy;
struct secpolicyindex;
struct secasindex;
struct secashead;
struct secasvar;
struct secreplay;
struct mbuf;
extern void kdebug_secpolicy(struct secpolicy *);
extern void kdebug_secpolicyindex(struct secpolicyindex *);
extern void kdebug_secasindex(struct secasindex *);
extern void kdebug_secasv(struct secasvar *);
extern void kdebug_mbufhdr(const struct mbuf *);
extern void kdebug_mbuf(const struct mbuf *);
union sockaddr_union;
const char* kdebug_secpolicy_state(u_int);
const char* kdebug_secpolicy_policy(u_int);
const char* kdebug_secpolicyindex_dir(u_int);
const char* kdebug_ipsecrequest_level(u_int);
const char* kdebug_secasindex_mode(u_int);
const char* kdebug_secasv_state(u_int);
void kdebug_secpolicy(struct secpolicy *);
void kdebug_secpolicyindex(struct secpolicyindex *, const char *);
void kdebug_secasindex(const struct secasindex *, const char *);
void kdebug_secash(struct secashead *, const char *);
void kdebug_secasv(struct secasvar *);
void kdebug_mbufhdr(const struct mbuf *);
void kdebug_mbuf(const struct mbuf *);
char *ipsec_address(const union sockaddr_union *, char *, socklen_t);
char *ipsec_sa2str(struct secasvar *, char *, size_t);
#endif /*_KERNEL*/
struct sockaddr;

View File

@ -34,7 +34,7 @@
#define _NETIPSEC_KEYDB_H_
#ifdef _KERNEL
#include <sys/counter.h>
#include <sys/lock.h>
#include <sys/mutex.h>
@ -57,9 +57,9 @@ union sockaddr_union {
struct secasindex {
union sockaddr_union src; /* source address for SA */
union sockaddr_union dst; /* destination address for SA */
u_int16_t proto; /* IPPROTO_ESP or IPPROTO_AH */
u_int8_t mode; /* mode of protocol, see ipsec.h */
u_int32_t reqid; /* reqid id who owned this SA */
uint8_t proto; /* IPPROTO_ESP or IPPROTO_AH */
uint8_t mode; /* mode of protocol, see ipsec.h */
uint32_t reqid; /* reqid id who owned this SA */
/* see IPSEC_MANUAL_REQID_MAX. */
};
@ -88,9 +88,23 @@ struct seclifetime {
u_int64_t usetime;
};
struct secnatt {
union sockaddr_union oai; /* original addresses of initiator */
union sockaddr_union oar; /* original address of responder */
uint16_t sport; /* source port */
uint16_t dport; /* destination port */
uint16_t cksum; /* checksum delta */
uint16_t flags;
#define IPSEC_NATT_F_OAI 0x0001
#define IPSEC_NATT_F_OAR 0x0002
};
/* Security Association Data Base */
TAILQ_HEAD(secasvar_queue, secasvar);
struct secashead {
LIST_ENTRY(secashead) chain;
TAILQ_ENTRY(secashead) chain;
LIST_ENTRY(secashead) addrhash; /* hash by sproto+src+dst addresses */
LIST_ENTRY(secashead) drainq; /* used ONLY by flush callout */
struct secasindex saidx;
@ -98,10 +112,10 @@ struct secashead {
struct secident *identd; /* destination identity */
/* XXX I don't know how to use them. */
u_int8_t state; /* MATURE or DEAD. */
LIST_HEAD(_satree, secasvar) savtree[SADB_SASTATE_MAX+1];
/* SA chain */
/* The first of this list is newer SA */
volatile u_int refcnt; /* reference count */
uint8_t state; /* MATURE or DEAD. */
struct secasvar_queue savtree_alive; /* MATURE and DYING SA */
struct secasvar_queue savtree_larval; /* LARVAL SA */
};
struct xformsw;
@ -109,63 +123,70 @@ struct enc_xform;
struct auth_hash;
struct comp_algo;
/* Security Association */
/*
* Security Association
*
* For INBOUND packets we do SA lookup using SPI, thus only SPIHASH is used.
* For OUTBOUND packets there may be several SA suitable for packet.
* We use key_preferred_oldsa variable to choose better SA. First of we do
* lookup for suitable SAH using packet's saidx. Then we use SAH's savtree
* to search better candidate. The newer SA (by created time) are placed
* in the beginning of the savtree list. There is no preference between
* DYING and MATURE.
*
* NB: Fields with a tdb_ prefix are part of the "glue" used
* to interface to the OpenBSD crypto support. This was done
* to distinguish this code from the mainline KAME code.
* NB: Fields are sorted on the basis of the frequency of changes, i.e.
* constants and unchangeable fields are going first.
* NB: if you want to change this structure, check that this will not break
* key_updateaddresses().
*/
struct secasvar {
LIST_ENTRY(secasvar) chain;
struct mtx lock; /* update/access lock */
u_int refcnt; /* reference count */
u_int8_t state; /* Status of this Association */
u_int8_t alg_auth; /* Authentication Algorithm Identifier*/
u_int8_t alg_enc; /* Cipher Algorithm Identifier */
u_int8_t alg_comp; /* Compression Algorithm Identifier */
u_int32_t spi; /* SPI Value, network byte order */
u_int32_t flags; /* holder for SADB_KEY_FLAGS */
uint32_t spi; /* SPI Value, network byte order */
uint32_t flags; /* holder for SADB_KEY_FLAGS */
uint32_t seq; /* sequence number */
pid_t pid; /* message's pid */
u_int ivlen; /* length of IV */
struct secashead *sah; /* back pointer to the secashead */
struct seckey *key_auth; /* Key for Authentication */
struct seckey *key_enc; /* Key for Encryption */
u_int ivlen; /* length of IV */
void *sched; /* intermediate encryption key */
size_t schedlen;
uint64_t cntr; /* counter for GCM and CTR */
struct secreplay *replay; /* replay prevention */
time_t created; /* for lifetime */
struct secnatt *natt; /* NAT-T config */
struct mtx *lock; /* update/access lock */
struct seclifetime *lft_c; /* CURRENT lifetime, it's constant. */
const struct xformsw *tdb_xform; /* transform */
const struct enc_xform *tdb_encalgxform;/* encoding algorithm */
const struct auth_hash *tdb_authalgxform;/* authentication algorithm */
const struct comp_algo *tdb_compalgxform;/* compression algorithm */
uint64_t tdb_cryptoid; /* crypto session id */
uint8_t alg_auth; /* Authentication Algorithm Identifier*/
uint8_t alg_enc; /* Cipher Algorithm Identifier */
uint8_t alg_comp; /* Compression Algorithm Identifier */
uint8_t state; /* Status of this SA (pfkeyv2.h) */
counter_u64_t lft_c; /* CURRENT lifetime */
#define lft_c_allocations lft_c
#define lft_c_bytes lft_c + 1
struct seclifetime *lft_h; /* HARD lifetime */
struct seclifetime *lft_s; /* SOFT lifetime */
u_int32_t seq; /* sequence number */
pid_t pid; /* message's pid */
uint64_t created; /* time when SA was created */
uint64_t firstused; /* time when SA was first used */
struct secashead *sah; /* back pointer to the secashead */
TAILQ_ENTRY(secasvar) chain;
LIST_ENTRY(secasvar) spihash;
LIST_ENTRY(secasvar) drainq; /* used ONLY by flush callout */
/*
* NB: Fields with a tdb_ prefix are part of the "glue" used
* to interface to the OpenBSD crypto support. This was done
* to distinguish this code from the mainline KAME code.
*/
struct xformsw *tdb_xform; /* transform */
struct enc_xform *tdb_encalgxform; /* encoding algorithm */
struct auth_hash *tdb_authalgxform; /* authentication algorithm */
struct comp_algo *tdb_compalgxform; /* compression algorithm */
u_int64_t tdb_cryptoid; /* crypto session id */
/*
* NAT-Traversal.
*/
u_int16_t natt_type; /* IKE/ESP-marker in output. */
u_int16_t natt_esp_frag_len; /* MTU for payload fragmentation. */
uint64_t cntr; /* counter for GCM and CTR */
volatile u_int refcnt; /* reference count */
};
#define SECASVAR_LOCK_INIT(_sav) \
mtx_init(&(_sav)->lock, "ipsec association", NULL, MTX_DEF)
#define SECASVAR_LOCK(_sav) mtx_lock(&(_sav)->lock)
#define SECASVAR_UNLOCK(_sav) mtx_unlock(&(_sav)->lock)
#define SECASVAR_LOCK_DESTROY(_sav) mtx_destroy(&(_sav)->lock)
#define SECASVAR_LOCK_ASSERT(_sav) mtx_assert(&(_sav)->lock, MA_OWNED)
#define SECASVAR_LOCK(_sav) mtx_lock((_sav)->lock)
#define SECASVAR_UNLOCK(_sav) mtx_unlock((_sav)->lock)
#define SECASVAR_LOCK_ASSERT(_sav) mtx_assert((_sav)->lock, MA_OWNED)
#define SAV_ISGCM(_sav) \
((_sav)->alg_enc == SADB_X_EALG_AESGCM8 || \
(_sav)->alg_enc == SADB_X_EALG_AESGCM12 || \
@ -197,10 +218,11 @@ struct secreg {
/* acquiring list table. */
struct secacq {
LIST_ENTRY(secacq) chain;
LIST_ENTRY(secacq) addrhash;
LIST_ENTRY(secacq) seqhash;
struct secasindex saidx;
u_int32_t seq; /* sequence number */
uint32_t seq; /* sequence number */
time_t created; /* for lifetime */
int count; /* for lifetime */
};

View File

@ -115,7 +115,7 @@ key_output(struct mbuf *m, struct socket *so, ...)
M_ASSERTPKTHDR(m);
KEYDEBUG(KEYDEBUG_KEY_DUMP, kdebug_mbuf(m));
KEYDBG(KEY_DUMP, kdebug_mbuf(m));
msg = mtod(m, struct sadb_msg *);
PFKEYSTAT_INC(out_msgtype[msg->sadb_msg_type]);
@ -181,9 +181,9 @@ key_sendup(struct socket *so, struct sadb_msg *msg, u_int len, int target)
if (so == NULL || msg == NULL)
panic("%s: NULL pointer was passed.\n", __func__);
KEYDEBUG(KEYDEBUG_KEY_DUMP,
printf("%s: \n", __func__);
kdebug_sadb(msg));
KEYDBG(KEY_DUMP,
printf("%s: \n", __func__);
kdebug_sadb(msg));
/*
* we increment statistics here, just in case we have ENOBUFS

350
sys/netipsec/subr_ipsec.c Normal file
View File

@ -0,0 +1,350 @@
/*-
* Copyright (c) 2016 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 "opt_inet.h"
#include "opt_inet6.h"
#include "opt_ipsec.h"
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/lock.h>
#include <sys/malloc.h>
#include <sys/mbuf.h>
#include <sys/module.h>
#include <sys/priv.h>
#include <sys/socket.h>
#include <sys/sockopt.h>
#include <sys/syslog.h>
#include <sys/proc.h>
#include <netinet/in.h>
#include <netinet/in_pcb.h>
#include <netinet/ip.h>
#include <netinet/ip6.h>
#include <netipsec/ipsec_support.h>
#include <netipsec/ipsec.h>
#include <netipsec/ipsec6.h>
#include <netipsec/key.h>
#include <netipsec/key_debug.h>
#include <machine/atomic.h>
/*
* This file is build in the kernel only when 'options IPSEC' or
* 'options IPSEC_SUPPORT' is enabled.
*/
#ifdef INET
void
ipsec4_setsockaddrs(const struct mbuf *m, union sockaddr_union *src,
union sockaddr_union *dst)
{
static const struct sockaddr_in template = {
sizeof (struct sockaddr_in),
AF_INET,
0, { 0 }, { 0, 0, 0, 0, 0, 0, 0, 0 }
};
src->sin = template;
dst->sin = template;
if (m->m_len < sizeof (struct ip)) {
m_copydata(m, offsetof(struct ip, ip_src),
sizeof (struct in_addr),
(caddr_t) &src->sin.sin_addr);
m_copydata(m, offsetof(struct ip, ip_dst),
sizeof (struct in_addr),
(caddr_t) &dst->sin.sin_addr);
} else {
const struct ip *ip = mtod(m, const struct ip *);
src->sin.sin_addr = ip->ip_src;
dst->sin.sin_addr = ip->ip_dst;
}
}
#endif
#ifdef INET6
void
ipsec6_setsockaddrs(const struct mbuf *m, union sockaddr_union *src,
union sockaddr_union *dst)
{
struct ip6_hdr ip6buf;
const struct ip6_hdr *ip6;
if (m->m_len >= sizeof(*ip6))
ip6 = mtod(m, const struct ip6_hdr *);
else {
m_copydata(m, 0, sizeof(ip6buf), (caddr_t)&ip6buf);
ip6 = &ip6buf;
}
bzero(&src->sin6, sizeof(struct sockaddr_in6));
src->sin6.sin6_family = AF_INET6;
src->sin6.sin6_len = sizeof(struct sockaddr_in6);
bcopy(&ip6->ip6_src, &src->sin6.sin6_addr, sizeof(ip6->ip6_src));
if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_src)) {
src->sin6.sin6_addr.s6_addr16[1] = 0;
src->sin6.sin6_scope_id = ntohs(ip6->ip6_src.s6_addr16[1]);
}
bzero(&dst->sin6, sizeof(struct sockaddr_in6));
dst->sin6.sin6_family = AF_INET6;
dst->sin6.sin6_len = sizeof(struct sockaddr_in6);
bcopy(&ip6->ip6_dst, &dst->sin6.sin6_addr, sizeof(ip6->ip6_dst));
if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_dst)) {
dst->sin6.sin6_addr.s6_addr16[1] = 0;
dst->sin6.sin6_scope_id = ntohs(ip6->ip6_dst.s6_addr16[1]);
}
}
#endif
#ifdef IPSEC_SUPPORT
/*
* Declare IPSEC_SUPPORT as module even if IPSEC is defined.
* tcpmd5.ko module depends from IPSEC_SUPPORT.
*/
#define IPSEC_MODULE_INCR 2
static int
ipsec_kmod_enter(volatile u_int *cntr)
{
u_int old, new;
do {
old = *cntr;
if ((old & IPSEC_MODULE_ENABLED) == 0)
return (ENXIO);
new = old + IPSEC_MODULE_INCR;
} while(atomic_cmpset_acq_int(cntr, old, new) == 0);
return (0);
}
static void
ipsec_kmod_exit(volatile u_int *cntr)
{
u_int old, new;
do {
old = *cntr;
new = old - IPSEC_MODULE_INCR;
} while (atomic_cmpset_rel_int(cntr, old, new) == 0);
}
static void
ipsec_kmod_drain(volatile u_int *cntr)
{
u_int old, new;
do {
old = *cntr;
new = old & ~IPSEC_MODULE_ENABLED;
} while (atomic_cmpset_acq_int(cntr, old, new) == 0);
while (atomic_cmpset_int(cntr, 0, 0) == 0)
pause("ipsecd", hz/2);
}
#define METHOD_DECL(...) __VA_ARGS__
#define METHOD_ARGS(...) __VA_ARGS__
#define IPSEC_KMOD_METHOD(type, name, sc, method, decl, args) \
type name (decl) \
{ \
type ret = (type)ipsec_kmod_enter(&sc->enabled); \
if (ret == 0) { \
ret = (*sc->methods->method)(args); \
ipsec_kmod_exit(&sc->enabled); \
} \
return (ret); \
}
#ifndef TCP_SIGNATURE
/* Declare TCP-MD5 support as kernel module. */
static struct tcpmd5_support tcpmd5_ipsec = {
.enabled = 0,
.methods = NULL
};
struct tcpmd5_support * const tcp_ipsec_support = &tcpmd5_ipsec;
IPSEC_KMOD_METHOD(int, tcpmd5_kmod_input, sc,
input, METHOD_DECL(struct tcpmd5_support * const sc, struct mbuf *m,
struct tcphdr *th, u_char *buf), METHOD_ARGS(m, th, buf)
)
IPSEC_KMOD_METHOD(int, tcpmd5_kmod_output, sc,
output, METHOD_DECL(struct tcpmd5_support * const sc, struct mbuf *m,
struct tcphdr *th, u_char *buf), METHOD_ARGS(m, th, buf)
)
IPSEC_KMOD_METHOD(int, tcpmd5_kmod_pcbctl, sc,
pcbctl, METHOD_DECL(struct tcpmd5_support * const sc, struct inpcb *inp,
struct sockopt *sopt), METHOD_ARGS(inp, sopt)
)
void
tcpmd5_support_enable(const struct tcpmd5_methods * const methods)
{
KASSERT(tcp_ipsec_support->enabled == 0, ("TCP-MD5 already enabled"));
tcp_ipsec_support->methods = methods;
tcp_ipsec_support->enabled |= IPSEC_MODULE_ENABLED;
}
void
tcpmd5_support_disable(void)
{
if (tcp_ipsec_support->enabled & IPSEC_MODULE_ENABLED) {
ipsec_kmod_drain(&tcp_ipsec_support->enabled);
tcp_ipsec_support->methods = NULL;
}
}
#endif
static int
ipsec_support_modevent(module_t mod, int type, void *data)
{
switch (type) {
case MOD_LOAD:
return (0);
case MOD_UNLOAD:
return (EBUSY);
default:
return (EOPNOTSUPP);
}
}
static moduledata_t ipsec_support_mod = {
"ipsec_support",
ipsec_support_modevent,
0
};
DECLARE_MODULE(ipsec_support, ipsec_support_mod, SI_SUB_PROTO_DOMAIN,
SI_ORDER_ANY);
MODULE_VERSION(ipsec_support, 1);
#ifndef IPSEC
/*
* IPsec support is build as kernel module.
*/
#ifdef INET
static struct ipsec_support ipv4_ipsec = {
.enabled = 0,
.methods = NULL
};
struct ipsec_support * const ipv4_ipsec_support = &ipv4_ipsec;
IPSEC_KMOD_METHOD(int, ipsec_kmod_udp_input, sc,
udp_input, METHOD_DECL(struct ipsec_support * const sc, struct mbuf *m,
int off, int af), METHOD_ARGS(m, off, af)
)
IPSEC_KMOD_METHOD(int, ipsec_kmod_udp_pcbctl, sc,
udp_pcbctl, METHOD_DECL(struct ipsec_support * const sc, struct inpcb *inp,
struct sockopt *sopt), METHOD_ARGS(inp, sopt)
)
#endif
#ifdef INET6
static struct ipsec_support ipv6_ipsec = {
.enabled = 0,
.methods = NULL
};
struct ipsec_support * const ipv6_ipsec_support = &ipv6_ipsec;
#endif
IPSEC_KMOD_METHOD(int, ipsec_kmod_input, sc,
input, METHOD_DECL(struct ipsec_support * const sc, struct mbuf *m,
int offset, int proto), METHOD_ARGS(m, offset, proto)
)
IPSEC_KMOD_METHOD(int, ipsec_kmod_check_policy, sc,
check_policy, METHOD_DECL(struct ipsec_support * const sc, struct mbuf *m,
struct inpcb *inp), METHOD_ARGS(m, inp)
)
IPSEC_KMOD_METHOD(int, ipsec_kmod_forward, sc,
forward, METHOD_DECL(struct ipsec_support * const sc, struct mbuf *m),
(m)
)
IPSEC_KMOD_METHOD(int, ipsec_kmod_output, sc,
output, METHOD_DECL(struct ipsec_support * const sc, struct mbuf *m,
struct inpcb *inp), METHOD_ARGS(m, inp)
)
IPSEC_KMOD_METHOD(int, ipsec_kmod_pcbctl, sc,
pcbctl, METHOD_DECL(struct ipsec_support * const sc, struct inpcb *inp,
struct sockopt *sopt), METHOD_ARGS(inp, sopt)
)
IPSEC_KMOD_METHOD(size_t, ipsec_kmod_hdrsize, sc,
hdrsize, METHOD_DECL(struct ipsec_support * const sc, struct inpcb *inp),
(inp)
)
static IPSEC_KMOD_METHOD(int, ipsec_kmod_caps, sc,
capability, METHOD_DECL(struct ipsec_support * const sc, struct mbuf *m,
u_int cap), METHOD_ARGS(m, cap)
)
int
ipsec_kmod_capability(struct ipsec_support * const sc, struct mbuf *m,
u_int cap)
{
/*
* Since PF_KEY is build in the kernel, we can directly
* call key_havesp() without additional synchronizations.
*/
if (cap == IPSEC_CAP_OPERABLE)
return (key_havesp(IPSEC_DIR_INBOUND) != 0 ||
key_havesp(IPSEC_DIR_OUTBOUND) != 0);
return (ipsec_kmod_caps(sc, m, cap));
}
void
ipsec_support_enable(struct ipsec_support * const sc,
const struct ipsec_methods * const methods)
{
KASSERT(sc->enabled == 0, ("IPsec already enabled"));
sc->methods = methods;
sc->enabled |= IPSEC_MODULE_ENABLED;
}
void
ipsec_support_disable(struct ipsec_support * const sc)
{
if (sc->enabled & IPSEC_MODULE_ENABLED) {
ipsec_kmod_drain(&sc->enabled);
sc->methods = NULL;
}
}
#endif /* !IPSEC */
#endif /* IPSEC_SUPPORT */

294
sys/netipsec/udpencap.c Normal file
View File

@ -0,0 +1,294 @@
/*-
* Copyright (c) 2016 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_ipsec.h"
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/lock.h>
#include <sys/mbuf.h>
#include <sys/protosw.h>
#include <sys/socket.h>
#include <sys/sockopt.h>
#include <sys/sysctl.h>
#include <netinet/in.h>
#include <netinet/in_pcb.h>
#include <netinet/in_systm.h>
#include <netinet/ip.h>
#include <netinet/ip_var.h>
#include <netinet/tcp.h>
#include <netinet/udp.h>
#include <netinet/udp_var.h>
#include <net/vnet.h>
#include <netipsec/ipsec.h>
#include <netipsec/esp.h>
#include <netipsec/esp_var.h>
#include <netipsec/xform.h>
#include <netipsec/key.h>
#include <netipsec/key_debug.h>
#include <netipsec/ipsec_support.h>
#include <machine/in_cksum.h>
/*
* Handle UDP_ENCAP socket option. Always return with released INP_WLOCK.
*/
int
udp_ipsec_pcbctl(struct inpcb *inp, struct sockopt *sopt)
{
struct udpcb *up;
int error, optval;
INP_WLOCK_ASSERT(inp);
if (sopt->sopt_name != UDP_ENCAP) {
INP_WUNLOCK(inp);
return (ENOPROTOOPT);
}
up = intoudpcb(inp);
if (sopt->sopt_dir == SOPT_GET) {
if (up->u_flags & UF_ESPINUDP)
optval = UDP_ENCAP_ESPINUDP;
else
optval = 0;
INP_WUNLOCK(inp);
return (sooptcopyout(sopt, &optval, sizeof(optval)));
}
INP_WUNLOCK(inp);
error = sooptcopyin(sopt, &optval, sizeof(optval), sizeof(optval));
if (error != 0)
return (error);
INP_WLOCK(inp);
switch (optval) {
case 0:
up->u_flags &= ~UF_ESPINUDP;
break;
case UDP_ENCAP_ESPINUDP:
up->u_flags |= UF_ESPINUDP;
break;
default:
error = EINVAL;
}
INP_WUNLOCK(inp);
return (error);
}
/*
* Potentially decap ESP in UDP frame. Check for an ESP header.
* If present, strip the UDP header and push the result through IPSec.
*
* Returns error if mbuf consumed and/or processed, otherwise 0.
*/
int
udp_ipsec_input(struct mbuf *m, int off, int af)
{
union sockaddr_union dst;
struct secasvar *sav;
struct udphdr *udp;
struct ip *ip;
uint32_t spi;
int error, hlen;
/*
* Just return if packet doesn't have enough data.
* We need at least [IP header + UDP header + ESP header].
* NAT-Keepalive packet has only one byte of payload, so it
* by default will not be processed.
*/
if (m->m_pkthdr.len < off + sizeof(struct esp))
return (0);
m_copydata(m, off, sizeof(uint32_t), (caddr_t)&spi);
if (spi == 0) /* Non-ESP marker. */
return (0);
/*
* Find SA and check that it is configured for UDP
* encapsulation.
*/
bzero(&dst, sizeof(dst));
dst.sa.sa_family = af;
switch (af) {
#ifdef INET
case AF_INET:
dst.sin.sin_len = sizeof(struct sockaddr_in);
ip = mtod(m, struct ip *);
ip->ip_p = IPPROTO_ESP;
off = offsetof(struct ip, ip_p);
hlen = ip->ip_hl << 2;
dst.sin.sin_addr = ip->ip_dst;
break;
#endif
#ifdef INET6
case AF_INET6:
/* Not yet */
/* FALLTHROUGH */
#endif
default:
ESPSTAT_INC(esps_nopf);
m_freem(m);
return (EPFNOSUPPORT);
}
sav = key_allocsa(&dst, IPPROTO_ESP, spi);
if (sav == NULL) {
ESPSTAT_INC(esps_notdb);
m_freem(m);
return (ENOENT);
}
udp = mtodo(m, hlen);
if (sav->natt == NULL ||
sav->natt->sport != udp->uh_sport ||
sav->natt->dport != udp->uh_dport) {
/* XXXAE: should we check source address? */
ESPSTAT_INC(esps_notdb);
key_freesav(&sav);
m_freem(m);
return (ENOENT);
}
/*
* Remove the UDP header
* Before:
* <--- off --->
* +----+------+-----+
* | IP | UDP | ESP |
* +----+------+-----+
* <-skip->
* After:
* +----+-----+
* | IP | ESP |
* +----+-----+
* <-skip->
*/
m_striphdr(m, hlen, sizeof(*udp));
/*
* We cannot yet update the cksums so clear any h/w cksum flags
* as they are no longer valid.
*/
if (m->m_pkthdr.csum_flags & CSUM_DATA_VALID)
m->m_pkthdr.csum_flags &= ~(CSUM_DATA_VALID | CSUM_PSEUDO_HDR);
/*
* We can update ip_len and ip_sum here, but ipsec4_input_cb()
* will do this anyway, so don't touch them here.
*/
ESPSTAT_INC(esps_input);
error = (*sav->tdb_xform->xf_input)(m, sav, hlen, off);
if (error != 0)
key_freesav(&sav);
return (EINPROGRESS); /* Consumed by IPsec. */
}
int
udp_ipsec_output(struct mbuf *m, struct secasvar *sav)
{
struct udphdr *udp;
struct mbuf *n;
struct ip *ip;
int hlen, off;
IPSEC_ASSERT(sav->natt != NULL, ("UDP encapsulation isn't required."));
if (sav->sah->saidx.dst.sa.sa_family == AF_INET6)
return (EAFNOSUPPORT);
ip = mtod(m, struct ip *);
hlen = ip->ip_hl << 2;
n = m_makespace(m, hlen, sizeof(*udp), &off);
if (n == NULL) {
DPRINTF(("%s: m_makespace for udphdr failed\n", __func__));
return (ENOBUFS);
}
udp = mtodo(n, off);
udp->uh_dport = sav->natt->dport;
udp->uh_sport = sav->natt->sport;
udp->uh_sum = 0;
udp->uh_ulen = htons(m->m_pkthdr.len - hlen);
ip = mtod(m, struct ip *);
ip->ip_len = htons(m->m_pkthdr.len);
ip->ip_p = IPPROTO_UDP;
return (0);
}
void
udp_ipsec_adjust_cksum(struct mbuf *m, struct secasvar *sav, int proto,
int skip)
{
struct ip *ip;
uint16_t cksum, off;
IPSEC_ASSERT(sav->natt != NULL, ("NAT-T isn't required"));
IPSEC_ASSERT(proto == IPPROTO_UDP || proto == IPPROTO_TCP,
("unexpected protocol %u", proto));
if (proto == IPPROTO_UDP)
off = offsetof(struct udphdr, uh_sum);
else
off = offsetof(struct tcphdr, th_sum);
if (V_natt_cksum_policy == 0) { /* auto */
if (sav->natt->cksum != 0) {
/* Incrementally recompute. */
m_copydata(m, skip + off, sizeof(cksum),
(caddr_t)&cksum);
cksum = in_addword(cksum, sav->natt->cksum);
} else {
/* No OA from IKEd. */
if (proto == IPPROTO_TCP) {
/* Ignore for TCP. */
m->m_pkthdr.csum_data = 0xffff;
m->m_pkthdr.csum_flags |= (CSUM_DATA_VALID |
CSUM_PSEUDO_HDR);
return;
}
cksum = 0; /* Reset for UDP. */
}
m_copyback(m, skip + off, sizeof(cksum), (caddr_t)&cksum);
} else { /* Fully recompute */
ip = mtod(m, struct ip *);
cksum = in_pseudo(ip->ip_src.s_addr, ip->ip_dst.s_addr,
htons(m->m_pkthdr.len - skip + proto));
m_copyback(m, skip + off, sizeof(cksum), (caddr_t)&cksum);
m->m_pkthdr.csum_flags =
(proto == IPPROTO_UDP) ? CSUM_UDP: CSUM_TCP;
m->m_pkthdr.csum_data = off;
in_delayed_cksum(m);
m->m_pkthdr.csum_flags &= ~CSUM_DELAY_DATA;
}
}

View File

@ -42,6 +42,7 @@
#define _NETIPSEC_XFORM_H_
#include <sys/types.h>
#include <sys/queue.h>
#include <netinet/in.h>
#include <opencrypto/xform.h>
@ -49,78 +50,68 @@
#define AH_HMAC_MAXHASHLEN (SHA2_512_HASH_LEN/2) /* Keep this updated */
#define AH_HMAC_INITIAL_RPL 1 /* replay counter initial value */
#ifdef _KERNEL
struct secpolicy;
struct secasvar;
/*
* Packet tag assigned on completion of IPsec processing; used
* to speedup processing when/if the packet comes back for more
* processing.
* to speedup security policy checking for INBOUND packets.
*/
struct tdb_ident {
u_int32_t spi;
union sockaddr_union dst;
u_int8_t proto;
/* Cache those two for enc(4) in xform_ipip. */
u_int8_t alg_auth;
u_int8_t alg_enc;
struct xform_history {
union sockaddr_union dst; /* destination address */
uint32_t spi; /* Security Parameters Index */
uint8_t proto; /* IPPROTO_ESP or IPPROTO_AH */
uint8_t mode; /* transport or tunnel */
};
/*
* Opaque data structure hung off a crypto operation descriptor.
*/
struct tdb_crypto {
struct ipsecrequest *tc_isr; /* ipsec request state */
u_int32_t tc_spi; /* associated SPI */
union sockaddr_union tc_dst; /* dst addr of packet */
u_int8_t tc_proto; /* current protocol, e.g. AH */
u_int8_t tc_nxt; /* next protocol, e.g. IPV4 */
int tc_protoff; /* current protocol offset */
int tc_skip; /* data offset */
caddr_t tc_ptr; /* associated crypto data */
struct secasvar *tc_sav; /* related SA */
struct xform_data {
struct secpolicy *sp; /* security policy */
struct secasvar *sav; /* related SA */
uint64_t cryptoid; /* used crypto session id */
u_int idx; /* IPsec request index */
int protoff; /* current protocol offset */
int skip; /* data offset */
uint8_t nxt; /* next protocol, e.g. IPV4 */
};
struct secasvar;
struct ipescrequest;
struct xformsw {
u_short xf_type; /* xform ID */
#define XF_IP4 1 /* unused */
#define XF_AH 2 /* AH */
#define XF_ESP 3 /* ESP */
#define XF_TCPSIGNATURE 5 /* TCP MD5 Signature option, RFC 2358 */
#define XF_IPCOMP 6 /* IPCOMP */
u_short xf_flags;
#define XFT_AUTH 0x0001
#define XFT_CONF 0x0100
#define XFT_COMP 0x1000
char *xf_name; /* human-readable name */
struct xformsw {
u_short xf_type; /* xform ID */
char *xf_name; /* human-readable name */
int (*xf_init)(struct secasvar*, struct xformsw*); /* setup */
int (*xf_zeroize)(struct secasvar*); /* cleanup */
int (*xf_input)(struct mbuf*, struct secasvar*, /* input */
int, int);
int (*xf_output)(struct mbuf*, /* output */
struct ipsecrequest *, struct mbuf **, int, int);
struct xformsw *xf_next; /* list of registered xforms */
int (*xf_output)(struct mbuf*, /* output */
struct secpolicy *, struct secasvar *, u_int, int, int);
LIST_ENTRY(xformsw) chain;
};
#ifdef _KERNEL
extern void xform_register(struct xformsw*);
extern int xform_init(struct secasvar *sav, int xftype);
extern int xform_ah_authsize(struct auth_hash *esph);
const struct enc_xform * enc_algorithm_lookup(int);
const struct auth_hash * auth_algorithm_lookup(int);
const struct comp_algo * comp_algorithm_lookup(int);
void xform_attach(void *);
void xform_detach(void *);
struct cryptoini;
/* XF_AH */
int xform_ah_authsize(const struct auth_hash *);
extern int ah_init0(struct secasvar *, struct xformsw *, struct cryptoini *);
extern int ah_zeroize(struct secasvar *sav);
extern struct auth_hash *ah_algorithm_lookup(int alg);
extern size_t ah_hdrsiz(struct secasvar *);
/* XF_ESP */
extern struct enc_xform *esp_algorithm_lookup(int alg);
extern size_t esp_hdrsiz(struct secasvar *sav);
/* XF_COMP */
extern struct comp_algo *ipcomp_algorithm_lookup(int alg);
#endif /* _KERNEL */
#endif /* _NETIPSEC_XFORM_H_ */

View File

@ -46,7 +46,7 @@
#include <sys/syslog.h>
#include <sys/kernel.h>
#include <sys/lock.h>
#include <sys/rwlock.h>
#include <sys/mutex.h>
#include <sys/sysctl.h>
#include <net/if.h>
@ -113,7 +113,7 @@ static int ah_input_cb(struct cryptop*);
static int ah_output_cb(struct cryptop*);
int
xform_ah_authsize(struct auth_hash *esph)
xform_ah_authsize(const struct auth_hash *esph)
{
int alen;
@ -141,43 +141,6 @@ xform_ah_authsize(struct auth_hash *esph)
return alen;
}
/*
* NB: this is public for use by the PF_KEY support.
*/
struct auth_hash *
ah_algorithm_lookup(int alg)
{
if (alg > SADB_AALG_MAX)
return NULL;
switch (alg) {
case SADB_X_AALG_NULL:
return &auth_hash_null;
case SADB_AALG_MD5HMAC:
return &auth_hash_hmac_md5;
case SADB_AALG_SHA1HMAC:
return &auth_hash_hmac_sha1;
case SADB_X_AALG_RIPEMD160HMAC:
return &auth_hash_hmac_ripemd_160;
case SADB_X_AALG_MD5:
return &auth_hash_key_md5;
case SADB_X_AALG_SHA:
return &auth_hash_key_sha1;
case SADB_X_AALG_SHA2_256:
return &auth_hash_hmac_sha2_256;
case SADB_X_AALG_SHA2_384:
return &auth_hash_hmac_sha2_384;
case SADB_X_AALG_SHA2_512:
return &auth_hash_hmac_sha2_512;
case SADB_X_AALG_AES128GMAC:
return &auth_hash_nist_gmac_aes_128;
case SADB_X_AALG_AES192GMAC:
return &auth_hash_nist_gmac_aes_192;
case SADB_X_AALG_AES256GMAC:
return &auth_hash_nist_gmac_aes_256;
}
return NULL;
}
size_t
ah_hdrsiz(struct secasvar *sav)
{
@ -202,10 +165,10 @@ ah_hdrsiz(struct secasvar *sav)
int
ah_init0(struct secasvar *sav, struct xformsw *xsp, struct cryptoini *cria)
{
struct auth_hash *thash;
const struct auth_hash *thash;
int keylen;
thash = ah_algorithm_lookup(sav->alg_auth);
thash = auth_algorithm_lookup(sav->alg_auth);
if (thash == NULL) {
DPRINTF(("%s: unsupported authentication algorithm %u\n",
__func__, sav->alg_auth));
@ -582,13 +545,13 @@ static int
ah_input(struct mbuf *m, struct secasvar *sav, int skip, int protoff)
{
char buf[128];
struct auth_hash *ahx;
struct tdb_crypto *tc;
struct newah *ah;
int hl, rplen, authsize, error;
const struct auth_hash *ahx;
struct cryptodesc *crda;
struct cryptop *crp;
struct xform_data *xd;
struct newah *ah;
uint64_t cryptoid;
int hl, rplen, authsize, error;
IPSEC_ASSERT(sav != NULL, ("null SA"));
IPSEC_ASSERT(sav->key_auth != NULL, ("null authentication key"));
@ -608,13 +571,18 @@ ah_input(struct mbuf *m, struct secasvar *sav, int skip, int protoff)
}
/* Check replay window, if applicable. */
if (sav->replay && !ipsec_chkreplay(ntohl(ah->ah_seq), sav)) {
SECASVAR_LOCK(sav);
if (sav->replay != NULL && sav->replay->wsize != 0 &&
ipsec_chkreplay(ntohl(ah->ah_seq), sav) == 0) {
SECASVAR_UNLOCK(sav);
AHSTAT_INC(ahs_replay);
DPRINTF(("%s: packet replay failure: %s\n", __func__,
ipsec_logsastr(sav, buf, sizeof(buf))));
ipsec_sa2str(sav, buf, sizeof(buf))));
m_freem(m);
return ENOBUFS;
return (EACCES);
}
cryptoid = sav->tdb_cryptoid;
SECASVAR_UNLOCK(sav);
/* Verify AH header length. */
hl = ah->ah_len * sizeof (u_int32_t);
@ -635,7 +603,8 @@ ah_input(struct mbuf *m, struct secasvar *sav, int skip, int protoff)
/* Get crypto descriptors. */
crp = crypto_getreq(1);
if (crp == NULL) {
DPRINTF(("%s: failed to acquire crypto descriptor\n",__func__));
DPRINTF(("%s: failed to acquire crypto descriptor\n",
__func__));
AHSTAT_INC(ahs_crypto);
m_freem(m);
return ENOBUFS;
@ -654,10 +623,10 @@ ah_input(struct mbuf *m, struct secasvar *sav, int skip, int protoff)
crda->crd_key = sav->key_auth->key_data;
/* Allocate IPsec-specific opaque crypto info. */
tc = (struct tdb_crypto *) malloc(sizeof (struct tdb_crypto) +
skip + rplen + authsize, M_XDATA, M_NOWAIT | M_ZERO);
if (tc == NULL) {
DPRINTF(("%s: failed to allocate tdb_crypto\n", __func__));
xd = malloc(sizeof(*xd) + skip + rplen + authsize, M_XDATA,
M_NOWAIT | M_ZERO);
if (xd == NULL) {
DPRINTF(("%s: failed to allocate xform_data\n", __func__));
AHSTAT_INC(ahs_crypto);
crypto_freereq(crp);
m_freem(m);
@ -668,7 +637,7 @@ ah_input(struct mbuf *m, struct secasvar *sav, int skip, int protoff)
* Save the authenticator, the skipped portion of the packet,
* and the AH header.
*/
m_copydata(m, 0, skip + rplen + authsize, (caddr_t)(tc+1));
m_copydata(m, 0, skip + rplen + authsize, (caddr_t)(xd + 1));
/* Zeroize the authenticator on the packet. */
m_copyback(m, skip + rplen, authsize, ipseczeroes);
@ -679,7 +648,7 @@ ah_input(struct mbuf *m, struct secasvar *sav, int skip, int protoff)
if (error != 0) {
/* NB: mbuf is free'd by ah_massage_headers */
AHSTAT_INC(ahs_hdrops);
free(tc, M_XDATA);
free(xd, M_XDATA);
crypto_freereq(crp);
return (error);
}
@ -689,18 +658,15 @@ ah_input(struct mbuf *m, struct secasvar *sav, int skip, int protoff)
crp->crp_flags = CRYPTO_F_IMBUF | CRYPTO_F_CBIFSYNC;
crp->crp_buf = (caddr_t) m;
crp->crp_callback = ah_input_cb;
crp->crp_sid = sav->tdb_cryptoid;
crp->crp_opaque = (caddr_t) tc;
crp->crp_sid = cryptoid;
crp->crp_opaque = (caddr_t) xd;
/* These are passed as-is to the callback. */
tc->tc_spi = sav->spi;
tc->tc_dst = sav->sah->saidx.dst;
tc->tc_proto = sav->sah->saidx.proto;
tc->tc_nxt = ah->ah_nxt;
tc->tc_protoff = protoff;
tc->tc_skip = skip;
KEY_ADDREFSA(sav);
tc->tc_sav = sav;
xd->sav = sav;
xd->nxt = ah->ah_nxt;
xd->protoff = protoff;
xd->skip = skip;
xd->cryptoid = cryptoid;
return (crypto_dispatch(crp));
}
@ -710,46 +676,43 @@ ah_input(struct mbuf *m, struct secasvar *sav, int skip, int protoff)
static int
ah_input_cb(struct cryptop *crp)
{
char buf[INET6_ADDRSTRLEN];
int rplen, error, skip, protoff;
char buf[IPSEC_ADDRSTRLEN];
unsigned char calc[AH_ALEN_MAX];
const struct auth_hash *ahx;
struct mbuf *m;
struct cryptodesc *crd;
struct auth_hash *ahx;
struct tdb_crypto *tc;
struct xform_data *xd;
struct secasvar *sav;
struct secasindex *saidx;
u_int8_t nxt;
caddr_t ptr;
int authsize;
uint64_t cryptoid;
int authsize, rplen, error, skip, protoff;
uint8_t nxt;
crd = crp->crp_desc;
tc = (struct tdb_crypto *) crp->crp_opaque;
IPSEC_ASSERT(tc != NULL, ("null opaque crypto data area!"));
skip = tc->tc_skip;
nxt = tc->tc_nxt;
protoff = tc->tc_protoff;
m = (struct mbuf *) crp->crp_buf;
sav = tc->tc_sav;
IPSEC_ASSERT(sav != NULL, ("null SA!"));
xd = (struct xform_data *) crp->crp_opaque;
sav = xd->sav;
skip = xd->skip;
nxt = xd->nxt;
protoff = xd->protoff;
cryptoid = xd->cryptoid;
saidx = &sav->sah->saidx;
IPSEC_ASSERT(saidx->dst.sa.sa_family == AF_INET ||
saidx->dst.sa.sa_family == AF_INET6,
("unexpected protocol family %u", saidx->dst.sa.sa_family));
ahx = (struct auth_hash *) sav->tdb_authalgxform;
ahx = sav->tdb_authalgxform;
/* Check for crypto errors. */
if (crp->crp_etype) {
if (sav->tdb_cryptoid != 0)
sav->tdb_cryptoid = crp->crp_sid;
if (crp->crp_etype == EAGAIN)
if (crp->crp_etype == EAGAIN) {
/* Reset the session ID */
if (ipsec_updateid(sav, &crp->crp_sid, &cryptoid) != 0)
crypto_freesession(cryptoid);
xd->cryptoid = crp->crp_sid;
return (crypto_dispatch(crp));
}
AHSTAT_INC(ahs_noxform);
DPRINTF(("%s: crypto error %d\n", __func__, crp->crp_etype));
error = crp->crp_etype;
@ -776,7 +739,7 @@ ah_input_cb(struct cryptop *crp)
m_copydata(m, skip + rplen, authsize, calc);
/* Verify authenticator. */
ptr = (caddr_t) (tc + 1);
ptr = (caddr_t) (xd + 1);
if (timingsafe_bcmp(ptr + skip + rplen, calc, authsize)) {
DPRINTF(("%s: authentication hash mismatch for packet "
"in SA %s/%08lx\n", __func__,
@ -787,11 +750,11 @@ ah_input_cb(struct cryptop *crp)
goto bad;
}
/* Fix the Next Protocol field. */
((u_int8_t *) ptr)[protoff] = nxt;
((uint8_t *) ptr)[protoff] = nxt;
/* Copyback the saved (uncooked) network headers. */
m_copyback(m, 0, skip, ptr);
free(tc, M_XDATA), tc = NULL; /* No longer needed */
free(xd, M_XDATA), xd = NULL; /* No longer needed */
/*
* Header is now authenticated.
@ -806,11 +769,14 @@ ah_input_cb(struct cryptop *crp)
m_copydata(m, skip + offsetof(struct newah, ah_seq),
sizeof (seq), (caddr_t) &seq);
SECASVAR_LOCK(sav);
if (ipsec_updatereplay(ntohl(seq), sav)) {
SECASVAR_UNLOCK(sav);
AHSTAT_INC(ahs_replay);
error = ENOBUFS; /*XXX as above*/
error = EACCES;
goto bad;
}
SECASVAR_UNLOCK(sav);
}
/*
@ -840,41 +806,38 @@ ah_input_cb(struct cryptop *crp)
panic("%s: Unexpected address family: %d saidx=%p", __func__,
saidx->dst.sa.sa_family, saidx);
}
KEY_FREESAV(&sav);
return error;
bad:
if (sav)
KEY_FREESAV(&sav);
key_freesav(&sav);
if (m != NULL)
m_freem(m);
if (tc != NULL)
free(tc, M_XDATA);
if (xd != NULL)
free(xd, M_XDATA);
if (crp != NULL)
crypto_freereq(crp);
return error;
}
/*
* AH output routine, called by ipsec[46]_process_packet().
* AH output routine, called by ipsec[46]_perform_request().
*/
static int
ah_output(struct mbuf *m, struct ipsecrequest *isr, struct mbuf **mp,
int skip, int protoff)
ah_output(struct mbuf *m, struct secpolicy *sp, struct secasvar *sav,
u_int idx, int skip, int protoff)
{
char buf[INET6_ADDRSTRLEN];
struct secasvar *sav;
struct auth_hash *ahx;
char buf[IPSEC_ADDRSTRLEN];
const struct auth_hash *ahx;
struct cryptodesc *crda;
struct tdb_crypto *tc;
struct xform_data *xd;
struct mbuf *mi;
struct cryptop *crp;
u_int16_t iplen;
int error, rplen, authsize, maxpacketsize, roff;
u_int8_t prot;
struct newah *ah;
uint64_t cryptoid;
uint16_t iplen;
int error, rplen, authsize, maxpacketsize, roff;
uint8_t prot;
sav = isr->sav;
IPSEC_ASSERT(sav != NULL, ("null SA"));
ahx = sav->tdb_authalgxform;
IPSEC_ASSERT(ahx != NULL, ("null authentication xform"));
@ -960,9 +923,8 @@ ah_output(struct mbuf *m, struct ipsecrequest *isr, struct mbuf **mp,
m_copyback(m, skip + rplen, authsize, ipseczeroes);
/* Insert packet replay counter, as requested. */
SECASVAR_LOCK(sav);
if (sav->replay) {
SECASVAR_LOCK(sav);
if (sav->replay->count == ~0 &&
(sav->flags & SADB_X_EXT_CYCSEQ) == 0) {
SECASVAR_UNLOCK(sav);
@ -970,7 +932,7 @@ ah_output(struct mbuf *m, struct ipsecrequest *isr, struct mbuf **mp,
__func__, ipsec_address(&sav->sah->saidx.dst, buf,
sizeof(buf)), (u_long) ntohl(sav->spi)));
AHSTAT_INC(ahs_wrap);
error = EINVAL;
error = EACCES;
goto bad;
}
#ifdef REGRESSION
@ -979,9 +941,9 @@ ah_output(struct mbuf *m, struct ipsecrequest *isr, struct mbuf **mp,
#endif
sav->replay->count++;
ah->ah_seq = htonl(sav->replay->count);
SECASVAR_UNLOCK(sav);
}
cryptoid = sav->tdb_cryptoid;
SECASVAR_UNLOCK(sav);
/* Get crypto descriptors. */
crp = crypto_getreq(1);
@ -994,7 +956,6 @@ ah_output(struct mbuf *m, struct ipsecrequest *isr, struct mbuf **mp,
}
crda = crp->crp_desc;
crda->crd_skip = 0;
crda->crd_inject = skip + rplen;
crda->crd_len = m->m_pkthdr.len;
@ -1005,18 +966,18 @@ ah_output(struct mbuf *m, struct ipsecrequest *isr, struct mbuf **mp,
crda->crd_klen = _KEYBITS(sav->key_auth);
/* Allocate IPsec-specific opaque crypto info. */
tc = (struct tdb_crypto *) malloc(
sizeof(struct tdb_crypto) + skip, M_XDATA, M_NOWAIT|M_ZERO);
if (tc == NULL) {
xd = malloc(sizeof(struct xform_data) + skip, M_XDATA,
M_NOWAIT | M_ZERO);
if (xd == NULL) {
crypto_freereq(crp);
DPRINTF(("%s: failed to allocate tdb_crypto\n", __func__));
DPRINTF(("%s: failed to allocate xform_data\n", __func__));
AHSTAT_INC(ahs_crypto);
error = ENOBUFS;
goto bad;
}
/* Save the skipped portion of the packet. */
m_copydata(m, 0, skip, (caddr_t) (tc + 1));
m_copydata(m, 0, skip, (caddr_t) (xd + 1));
/*
* Fix IP header length on the header used for
@ -1026,7 +987,7 @@ ah_output(struct mbuf *m, struct ipsecrequest *isr, struct mbuf **mp,
switch (sav->sah->saidx.dst.sa.sa_family) {
#ifdef INET
case AF_INET:
bcopy(((caddr_t)(tc + 1)) +
bcopy(((caddr_t)(xd + 1)) +
offsetof(struct ip, ip_len),
(caddr_t) &iplen, sizeof(u_int16_t));
iplen = htons(ntohs(iplen) + rplen + authsize);
@ -1037,29 +998,29 @@ ah_output(struct mbuf *m, struct ipsecrequest *isr, struct mbuf **mp,
#ifdef INET6
case AF_INET6:
bcopy(((caddr_t)(tc + 1)) +
bcopy(((caddr_t)(xd + 1)) +
offsetof(struct ip6_hdr, ip6_plen),
(caddr_t) &iplen, sizeof(u_int16_t));
(caddr_t) &iplen, sizeof(uint16_t));
iplen = htons(ntohs(iplen) + rplen + authsize);
m_copyback(m, offsetof(struct ip6_hdr, ip6_plen),
sizeof(u_int16_t), (caddr_t) &iplen);
sizeof(uint16_t), (caddr_t) &iplen);
break;
#endif /* INET6 */
}
/* Fix the Next Header field in saved header. */
((u_int8_t *) (tc + 1))[protoff] = IPPROTO_AH;
((uint8_t *) (xd + 1))[protoff] = IPPROTO_AH;
/* Update the Next Protocol field in the IP header. */
prot = IPPROTO_AH;
m_copyback(m, protoff, sizeof(u_int8_t), (caddr_t) &prot);
m_copyback(m, protoff, sizeof(uint8_t), (caddr_t) &prot);
/* "Massage" the packet headers for crypto processing. */
error = ah_massage_headers(&m, sav->sah->saidx.dst.sa.sa_family,
skip, ahx->type, 1);
if (error != 0) {
m = NULL; /* mbuf was free'd by ah_massage_headers. */
free(tc, M_XDATA);
free(xd, M_XDATA);
crypto_freereq(crp);
goto bad;
}
@ -1069,19 +1030,15 @@ ah_output(struct mbuf *m, struct ipsecrequest *isr, struct mbuf **mp,
crp->crp_flags = CRYPTO_F_IMBUF | CRYPTO_F_CBIFSYNC;
crp->crp_buf = (caddr_t) m;
crp->crp_callback = ah_output_cb;
crp->crp_sid = sav->tdb_cryptoid;
crp->crp_opaque = (caddr_t) tc;
crp->crp_sid = cryptoid;
crp->crp_opaque = (caddr_t) xd;
/* These are passed as-is to the callback. */
key_addref(isr->sp);
tc->tc_isr = isr;
KEY_ADDREFSA(sav);
tc->tc_sav = sav;
tc->tc_spi = sav->spi;
tc->tc_dst = sav->sah->saidx.dst;
tc->tc_proto = sav->sah->saidx.proto;
tc->tc_skip = skip;
tc->tc_protoff = protoff;
xd->sp = sp;
xd->sav = sav;
xd->skip = skip;
xd->idx = idx;
xd->cryptoid = cryptoid;
return crypto_dispatch(crp);
bad:
@ -1096,45 +1053,37 @@ ah_output(struct mbuf *m, struct ipsecrequest *isr, struct mbuf **mp,
static int
ah_output_cb(struct cryptop *crp)
{
int skip, protoff, error;
struct tdb_crypto *tc;
struct ipsecrequest *isr;
struct xform_data *xd;
struct secpolicy *sp;
struct secasvar *sav;
struct mbuf *m;
uint64_t cryptoid;
caddr_t ptr;
u_int idx;
int skip, error;
tc = (struct tdb_crypto *) crp->crp_opaque;
IPSEC_ASSERT(tc != NULL, ("null opaque data area!"));
skip = tc->tc_skip;
protoff = tc->tc_protoff;
ptr = (caddr_t) (tc + 1);
m = (struct mbuf *) crp->crp_buf;
isr = tc->tc_isr;
IPSEC_ASSERT(isr->sp != NULL, ("NULL isr->sp"));
IPSECREQUEST_LOCK(isr);
sav = tc->tc_sav;
/* With the isr lock released SA pointer can be updated. */
if (sav != isr->sav) {
AHSTAT_INC(ahs_notdb);
DPRINTF(("%s: SA expired while in crypto\n", __func__));
error = ENOBUFS; /*XXX*/
goto bad;
}
xd = (struct xform_data *) crp->crp_opaque;
sp = xd->sp;
sav = xd->sav;
skip = xd->skip;
idx = xd->idx;
cryptoid = xd->cryptoid;
ptr = (caddr_t) (xd + 1);
/* Check for crypto errors. */
if (crp->crp_etype) {
if (sav->tdb_cryptoid != 0)
sav->tdb_cryptoid = crp->crp_sid;
if (crp->crp_etype == EAGAIN) {
IPSECREQUEST_UNLOCK(isr);
/* Reset the session ID */
if (ipsec_updateid(sav, &crp->crp_sid, &cryptoid) != 0)
crypto_freesession(cryptoid);
xd->cryptoid = crp->crp_sid;
return (crypto_dispatch(crp));
}
AHSTAT_INC(ahs_noxform);
DPRINTF(("%s: crypto error %d\n", __func__, crp->crp_etype));
error = crp->crp_etype;
m_freem(m);
goto bad;
}
@ -1145,18 +1094,15 @@ ah_output_cb(struct cryptop *crp)
error = EINVAL;
goto bad;
}
AHSTAT_INC(ahs_hist[sav->alg_auth]);
/*
* Copy original headers (with the new protocol number) back
* in place.
*/
m_copyback(m, 0, skip, ptr);
/* No longer needed. */
free(tc, M_XDATA);
free(xd, M_XDATA);
crypto_freereq(crp);
AHSTAT_INC(ahs_hist[sav->alg_auth]);
#ifdef REGRESSION
/* Emulate man-in-the-middle attack when ipsec_integrity is TRUE. */
if (V_ipsec_integrity) {
@ -1172,33 +1118,26 @@ ah_output_cb(struct cryptop *crp)
#endif
/* NB: m is reclaimed by ipsec_process_done. */
error = ipsec_process_done(m, isr);
KEY_FREESAV(&sav);
IPSECREQUEST_UNLOCK(isr);
KEY_FREESP(&isr->sp);
error = ipsec_process_done(m, sp, sav, idx);
return (error);
bad:
if (sav)
KEY_FREESAV(&sav);
IPSECREQUEST_UNLOCK(isr);
KEY_FREESP(&isr->sp);
if (m)
m_freem(m);
free(tc, M_XDATA);
free(xd, M_XDATA);
crypto_freereq(crp);
key_freesav(&sav);
key_freesp(&sp);
return (error);
}
static struct xformsw ah_xformsw = {
XF_AH, XFT_AUTH, "IPsec AH",
ah_init, ah_zeroize, ah_input, ah_output,
.xf_type = XF_AH,
.xf_name = "IPsec AH",
.xf_init = ah_init,
.xf_zeroize = ah_zeroize,
.xf_input = ah_input,
.xf_output = ah_output,
};
static void
ah_attach(void)
{
xform_register(&ah_xformsw);
}
SYSINIT(ah_xform_init, SI_SUB_PROTO_DOMAIN, SI_ORDER_MIDDLE, ah_attach, NULL);
SYSINIT(ah_xform_init, SI_SUB_PROTO_DOMAIN, SI_ORDER_MIDDLE,
xform_attach, &ah_xformsw);
SYSUNINIT(ah_xform_uninit, SI_SUB_PROTO_DOMAIN, SI_ORDER_MIDDLE,
xform_detach, &ah_xformsw);

View File

@ -46,7 +46,7 @@
#include <sys/kernel.h>
#include <sys/lock.h>
#include <sys/random.h>
#include <sys/rwlock.h>
#include <sys/mutex.h>
#include <sys/sysctl.h>
#include <sys/mutex.h>
#include <machine/atomic.h>
@ -97,40 +97,6 @@ SYSCTL_VNET_PCPUSTAT(_net_inet_esp, IPSECCTL_STATS, stats,
static int esp_input_cb(struct cryptop *op);
static int esp_output_cb(struct cryptop *crp);
/*
* NB: this is public for use by the PF_KEY support.
* NB: if you add support here; be sure to add code to esp_attach below!
*/
struct enc_xform *
esp_algorithm_lookup(int alg)
{
if (alg >= ESP_ALG_MAX)
return NULL;
switch (alg) {
case SADB_EALG_DESCBC:
return &enc_xform_des;
case SADB_EALG_3DESCBC:
return &enc_xform_3des;
case SADB_X_EALG_AES:
return &enc_xform_rijndael128;
case SADB_X_EALG_BLOWFISHCBC:
return &enc_xform_blf;
case SADB_X_EALG_CAST128CBC:
return &enc_xform_cast5;
case SADB_EALG_NULL:
return &enc_xform_null;
case SADB_X_EALG_CAMELLIACBC:
return &enc_xform_camellia;
case SADB_X_EALG_AESCTR:
return &enc_xform_aes_icm;
case SADB_X_EALG_AESGCM16:
return &enc_xform_aes_nist_gcm;
case SADB_X_EALG_AESGMAC:
return &enc_xform_aes_nist_gmac;
}
return NULL;
}
size_t
esp_hdrsiz(struct secasvar *sav)
{
@ -168,12 +134,12 @@ esp_hdrsiz(struct secasvar *sav)
static int
esp_init(struct secasvar *sav, struct xformsw *xsp)
{
struct enc_xform *txform;
const struct enc_xform *txform;
struct cryptoini cria, crie;
int keylen;
int error;
txform = esp_algorithm_lookup(sav->alg_enc);
txform = enc_algorithm_lookup(sav->alg_enc);
if (txform == NULL) {
DPRINTF(("%s: unsupported encryption algorithm %d\n",
__func__, sav->alg_enc));
@ -298,14 +264,15 @@ static int
esp_input(struct mbuf *m, struct secasvar *sav, int skip, int protoff)
{
char buf[128];
struct auth_hash *esph;
struct enc_xform *espx;
struct tdb_crypto *tc;
uint8_t *ivp;
int plen, alen, hlen;
struct newesp *esp;
const struct auth_hash *esph;
const struct enc_xform *espx;
struct xform_data *xd;
struct cryptodesc *crde;
struct cryptop *crp;
struct newesp *esp;
uint8_t *ivp;
uint64_t cryptoid;
int plen, alen, hlen;
IPSEC_ASSERT(sav != NULL, ("null SA"));
IPSEC_ASSERT(sav->tdb_encalgxform != NULL, ("null encoding xform"));
@ -354,14 +321,19 @@ esp_input(struct mbuf *m, struct secasvar *sav, int skip, int protoff)
/*
* Check sequence number.
*/
if (esph != NULL && sav->replay != NULL &&
!ipsec_chkreplay(ntohl(esp->esp_seq), sav)) {
DPRINTF(("%s: packet replay check for %s\n", __func__,
ipsec_logsastr(sav, buf, sizeof(buf)))); /*XXX*/
ESPSTAT_INC(esps_replay);
m_freem(m);
return ENOBUFS; /*XXX*/
SECASVAR_LOCK(sav);
if (esph != NULL && sav->replay != NULL && sav->replay->wsize != 0) {
if (ipsec_chkreplay(ntohl(esp->esp_seq), sav) == 0) {
SECASVAR_UNLOCK(sav);
DPRINTF(("%s: packet replay check for %s\n", __func__,
ipsec_sa2str(sav, buf, sizeof(buf))));
ESPSTAT_INC(esps_replay);
m_freem(m);
return (EACCES);
}
}
cryptoid = sav->tdb_cryptoid;
SECASVAR_UNLOCK(sav);
/* Update the counters */
ESPSTAT_ADD(esps_ibytes, m->m_pkthdr.len - (skip + hlen + alen));
@ -377,12 +349,11 @@ esp_input(struct mbuf *m, struct secasvar *sav, int skip, int protoff)
}
/* Get IPsec-specific opaque pointer */
tc = (struct tdb_crypto *) malloc(sizeof(struct tdb_crypto) + alen,
M_XDATA, M_NOWAIT | M_ZERO);
if (tc == NULL) {
crypto_freereq(crp);
DPRINTF(("%s: failed to allocate tdb_crypto\n", __func__));
xd = malloc(sizeof(*xd) + alen, M_XDATA, M_NOWAIT | M_ZERO);
if (xd == NULL) {
DPRINTF(("%s: failed to allocate xform_data\n", __func__));
ESPSTAT_INC(esps_crypto);
crypto_freereq(crp);
m_freem(m);
return ENOBUFS;
}
@ -404,7 +375,7 @@ esp_input(struct mbuf *m, struct secasvar *sav, int skip, int protoff)
/* Copy the authenticator */
m_copydata(m, m->m_pkthdr.len - alen, alen,
(caddr_t) (tc + 1));
(caddr_t) (xd + 1));
/* Chain authentication request */
crde = crda->crd_next;
@ -417,17 +388,14 @@ esp_input(struct mbuf *m, struct secasvar *sav, int skip, int protoff)
crp->crp_flags = CRYPTO_F_IMBUF | CRYPTO_F_CBIFSYNC;
crp->crp_buf = (caddr_t) m;
crp->crp_callback = esp_input_cb;
crp->crp_sid = sav->tdb_cryptoid;
crp->crp_opaque = (caddr_t) tc;
crp->crp_sid = cryptoid;
crp->crp_opaque = (caddr_t) xd;
/* These are passed as-is to the callback */
tc->tc_spi = sav->spi;
tc->tc_dst = sav->sah->saidx.dst;
tc->tc_proto = sav->sah->saidx.proto;
tc->tc_protoff = protoff;
tc->tc_skip = skip;
KEY_ADDREFSA(sav);
tc->tc_sav = sav;
xd->sav = sav;
xd->protoff = protoff;
xd->skip = skip;
xd->cryptoid = cryptoid;
/* Decryption descriptor */
IPSEC_ASSERT(crde != NULL, ("null esp crypto descriptor"));
@ -467,45 +435,39 @@ esp_input_cb(struct cryptop *crp)
{
char buf[128];
u_int8_t lastthree[3], aalg[AH_HMAC_MAXHASHLEN];
int hlen, skip, protoff, error, alen;
const struct auth_hash *esph;
const struct enc_xform *espx;
struct mbuf *m;
struct cryptodesc *crd;
struct auth_hash *esph;
struct enc_xform *espx;
struct tdb_crypto *tc;
struct xform_data *xd;
struct secasvar *sav;
struct secasindex *saidx;
caddr_t ptr;
uint64_t cryptoid;
int hlen, skip, protoff, error, alen;
crd = crp->crp_desc;
IPSEC_ASSERT(crd != NULL, ("null crypto descriptor!"));
tc = (struct tdb_crypto *) crp->crp_opaque;
IPSEC_ASSERT(tc != NULL, ("null opaque crypto data area!"));
skip = tc->tc_skip;
protoff = tc->tc_protoff;
m = (struct mbuf *) crp->crp_buf;
sav = tc->tc_sav;
IPSEC_ASSERT(sav != NULL, ("null SA!"));
xd = (struct xform_data *) crp->crp_opaque;
sav = xd->sav;
skip = xd->skip;
protoff = xd->protoff;
cryptoid = xd->cryptoid;
saidx = &sav->sah->saidx;
IPSEC_ASSERT(saidx->dst.sa.sa_family == AF_INET ||
saidx->dst.sa.sa_family == AF_INET6,
("unexpected protocol family %u", saidx->dst.sa.sa_family));
esph = sav->tdb_authalgxform;
espx = sav->tdb_encalgxform;
/* Check for crypto errors */
if (crp->crp_etype) {
/* Reset the session ID */
if (sav->tdb_cryptoid != 0)
sav->tdb_cryptoid = crp->crp_sid;
if (crp->crp_etype == EAGAIN)
if (crp->crp_etype == EAGAIN) {
/* Reset the session ID */
if (ipsec_updateid(sav, &crp->crp_sid, &cryptoid) != 0)
crypto_freesession(cryptoid);
xd->cryptoid = crp->crp_sid;
return (crypto_dispatch(crp));
}
ESPSTAT_INC(esps_noxform);
DPRINTF(("%s: crypto error %d\n", __func__, crp->crp_etype));
error = crp->crp_etype;
@ -527,7 +489,7 @@ esp_input_cb(struct cryptop *crp)
AHSTAT_INC(ahs_hist[sav->alg_auth]);
/* Copy the authenticator from the packet */
m_copydata(m, m->m_pkthdr.len - alen, alen, aalg);
ptr = (caddr_t) (tc + 1);
ptr = (caddr_t) (xd + 1);
/* Verify authenticator */
if (timingsafe_bcmp(ptr, aalg, alen) != 0) {
@ -539,13 +501,13 @@ esp_input_cb(struct cryptop *crp)
error = EACCES;
goto bad;
}
m->m_flags |= M_AUTHIPDGM;
/* Remove trailing authenticator */
m_adj(m, -alen);
}
/* Release the crypto descriptors */
free(tc, M_XDATA), tc = NULL;
free(xd, M_XDATA), xd = NULL;
crypto_freereq(crp), crp = NULL;
/*
@ -561,13 +523,16 @@ esp_input_cb(struct cryptop *crp)
m_copydata(m, skip + offsetof(struct newesp, esp_seq),
sizeof (seq), (caddr_t) &seq);
SECASVAR_LOCK(sav);
if (ipsec_updatereplay(ntohl(seq), sav)) {
SECASVAR_UNLOCK(sav);
DPRINTF(("%s: packet replay check for %s\n", __func__,
ipsec_logsastr(sav, buf, sizeof(buf))));
ipsec_sa2str(sav, buf, sizeof(buf))));
ESPSTAT_INC(esps_replay);
error = ENOBUFS;
error = EACCES;
goto bad;
}
SECASVAR_UNLOCK(sav);
}
/* Determine the ESP header length */
@ -635,46 +600,40 @@ esp_input_cb(struct cryptop *crp)
panic("%s: Unexpected address family: %d saidx=%p", __func__,
saidx->dst.sa.sa_family, saidx);
}
KEY_FREESAV(&sav);
return error;
bad:
if (sav)
KEY_FREESAV(&sav);
if (sav != NULL)
key_freesav(&sav);
if (m != NULL)
m_freem(m);
if (tc != NULL)
free(tc, M_XDATA);
if (xd != NULL)
free(xd, M_XDATA);
if (crp != NULL)
crypto_freereq(crp);
return error;
}
/*
* ESP output routine, called by ipsec[46]_process_packet().
* ESP output routine, called by ipsec[46]_perform_request().
*/
static int
esp_output(struct mbuf *m, struct ipsecrequest *isr, struct mbuf **mp,
int skip, int protoff)
esp_output(struct mbuf *m, struct secpolicy *sp, struct secasvar *sav,
u_int idx, int skip, int protoff)
{
char buf[INET6_ADDRSTRLEN];
struct enc_xform *espx;
struct auth_hash *esph;
uint8_t *ivp;
uint64_t cntr;
int hlen, rlen, padding, blks, alen, i, roff;
struct mbuf *mo = (struct mbuf *) NULL;
struct tdb_crypto *tc;
struct secasvar *sav;
struct secasindex *saidx;
unsigned char *pad;
u_int8_t prot;
int error, maxpacketsize;
char buf[IPSEC_ADDRSTRLEN];
struct cryptodesc *crde = NULL, *crda = NULL;
struct cryptop *crp;
const struct auth_hash *esph;
const struct enc_xform *espx;
struct mbuf *mo = NULL;
struct xform_data *xd;
struct secasindex *saidx;
unsigned char *pad;
uint8_t *ivp;
uint64_t cntr, cryptoid;
int hlen, rlen, padding, blks, alen, i, roff;
int error, maxpacketsize;
uint8_t prot;
sav = isr->sav;
IPSEC_ASSERT(sav != NULL, ("null SA"));
esph = sav->tdb_authalgxform;
espx = sav->tdb_encalgxform;
@ -720,8 +679,9 @@ esp_output(struct mbuf *m, struct ipsecrequest *isr, struct mbuf **mp,
error = EPFNOSUPPORT;
goto bad;
}
/*
DPRINTF(("%s: skip %d hlen %d rlen %d padding %d alen %d blksd %d\n",
__func__, skip, hlen, rlen, padding, alen, blks));
__func__, skip, hlen, rlen, padding, alen, blks)); */
if (skip + hlen + rlen + padding + alen > maxpacketsize) {
DPRINTF(("%s: packet in SA %s/%08lx got too big "
"(len %u, max len %u)\n", __func__,
@ -752,28 +712,32 @@ esp_output(struct mbuf *m, struct ipsecrequest *isr, struct mbuf **mp,
DPRINTF(("%s: %u byte ESP hdr inject failed for SA %s/%08lx\n",
__func__, hlen, ipsec_address(&saidx->dst, buf,
sizeof(buf)), (u_long) ntohl(sav->spi)));
ESPSTAT_INC(esps_hdrops); /* XXX diffs from openbsd */
ESPSTAT_INC(esps_hdrops); /* XXX diffs from openbsd */
error = ENOBUFS;
goto bad;
}
/* Initialize ESP header. */
bcopy((caddr_t) &sav->spi, mtod(mo, caddr_t) + roff, sizeof(u_int32_t));
bcopy((caddr_t) &sav->spi, mtod(mo, caddr_t) + roff,
sizeof(uint32_t));
SECASVAR_LOCK(sav);
if (sav->replay) {
u_int32_t replay;
uint32_t replay;
SECASVAR_LOCK(sav);
#ifdef REGRESSION
/* Emulate replay attack when ipsec_replay is TRUE. */
if (!V_ipsec_replay)
#endif
sav->replay->count++;
replay = htonl(sav->replay->count);
SECASVAR_UNLOCK(sav);
bcopy((caddr_t) &replay,
mtod(mo, caddr_t) + roff + sizeof(u_int32_t),
sizeof(u_int32_t));
bcopy((caddr_t) &replay, mtod(mo, caddr_t) + roff +
sizeof(uint32_t), sizeof(uint32_t));
}
cryptoid = sav->tdb_cryptoid;
if (SAV_ISCTRORGCM(sav))
cntr = sav->cntr++;
SECASVAR_UNLOCK(sav);
/*
* Add padding -- better to do it ourselves than use the crypto engine,
@ -825,11 +789,10 @@ esp_output(struct mbuf *m, struct ipsecrequest *isr, struct mbuf **mp,
}
/* IPsec-specific opaque crypto info. */
tc = (struct tdb_crypto *) malloc(sizeof(struct tdb_crypto),
M_XDATA, M_NOWAIT|M_ZERO);
if (tc == NULL) {
xd = malloc(sizeof(struct xform_data), M_XDATA, M_NOWAIT | M_ZERO);
if (xd == NULL) {
crypto_freereq(crp);
DPRINTF(("%s: failed to allocate tdb_crypto\n", __func__));
DPRINTF(("%s: failed to allocate xform_data\n", __func__));
ESPSTAT_INC(esps_crypto);
error = ENOBUFS;
goto bad;
@ -855,13 +818,10 @@ esp_output(struct mbuf *m, struct ipsecrequest *isr, struct mbuf **mp,
/* Nonce is last four bytes of key, RFC3686 5.1 */
memcpy(ivp, sav->key_enc->key_data +
_KEYLEN(sav->key_enc) - 4, 4);
SECASVAR_LOCK(sav);
cntr = sav->cntr++;
SECASVAR_UNLOCK(sav);
be64enc(&ivp[4], cntr);
if (SAV_ISCTR(sav)) {
/* Initial block counter is 1, RFC3686 4 */
/* XXXAE: should we use this only for first packet? */
be32enc(&ivp[sav->ivlen + 4], 1);
}
@ -870,21 +830,18 @@ esp_output(struct mbuf *m, struct ipsecrequest *isr, struct mbuf **mp,
}
/* Callback parameters */
key_addref(isr->sp);
tc->tc_isr = isr;
KEY_ADDREFSA(sav);
tc->tc_sav = sav;
tc->tc_spi = sav->spi;
tc->tc_dst = saidx->dst;
tc->tc_proto = saidx->proto;
xd->sp = sp;
xd->sav = sav;
xd->idx = idx;
xd->cryptoid = cryptoid;
/* Crypto operation descriptor. */
crp->crp_ilen = m->m_pkthdr.len; /* Total input length. */
crp->crp_flags = CRYPTO_F_IMBUF | CRYPTO_F_CBIFSYNC;
crp->crp_buf = (caddr_t) m;
crp->crp_callback = esp_output_cb;
crp->crp_opaque = (caddr_t) tc;
crp->crp_sid = sav->tdb_cryptoid;
crp->crp_opaque = (caddr_t) xd;
crp->crp_sid = cryptoid;
if (esph) {
/* Authentication descriptor. */
@ -903,53 +860,40 @@ esp_output(struct mbuf *m, struct ipsecrequest *isr, struct mbuf **mp,
m_freem(m);
return (error);
}
/*
* ESP output callback from the crypto driver.
*/
static int
esp_output_cb(struct cryptop *crp)
{
char buf[INET6_ADDRSTRLEN];
struct tdb_crypto *tc;
struct ipsecrequest *isr;
struct xform_data *xd;
struct secpolicy *sp;
struct secasvar *sav;
struct mbuf *m;
uint64_t cryptoid;
u_int idx;
int error;
tc = (struct tdb_crypto *) crp->crp_opaque;
IPSEC_ASSERT(tc != NULL, ("null opaque data area!"));
xd = (struct xform_data *) crp->crp_opaque;
m = (struct mbuf *) crp->crp_buf;
isr = tc->tc_isr;
IPSEC_ASSERT(isr->sp != NULL, ("NULL isr->sp"));
IPSECREQUEST_LOCK(isr);
sav = tc->tc_sav;
/* With the isr lock released, SA pointer may have changed. */
if (sav != isr->sav) {
ESPSTAT_INC(esps_notdb);
DPRINTF(("%s: SA gone during crypto (SA %s/%08lx proto %u)\n",
__func__, ipsec_address(&tc->tc_dst, buf, sizeof(buf)),
(u_long) ntohl(tc->tc_spi), tc->tc_proto));
error = ENOBUFS; /*XXX*/
goto bad;
}
sp = xd->sp;
sav = xd->sav;
idx = xd->idx;
cryptoid = xd->cryptoid;
/* Check for crypto errors. */
if (crp->crp_etype) {
/* Reset session ID. */
if (sav->tdb_cryptoid != 0)
sav->tdb_cryptoid = crp->crp_sid;
if (crp->crp_etype == EAGAIN) {
IPSECREQUEST_UNLOCK(isr);
/* Reset the session ID */
if (ipsec_updateid(sav, &crp->crp_sid, &cryptoid) != 0)
crypto_freesession(cryptoid);
xd->cryptoid = crp->crp_sid;
return (crypto_dispatch(crp));
}
ESPSTAT_INC(esps_noxform);
DPRINTF(("%s: crypto error %d\n", __func__, crp->crp_etype));
error = crp->crp_etype;
m_freem(m);
goto bad;
}
@ -960,19 +904,17 @@ esp_output_cb(struct cryptop *crp)
error = EINVAL;
goto bad;
}
free(xd, M_XDATA);
crypto_freereq(crp);
ESPSTAT_INC(esps_hist[sav->alg_enc]);
if (sav->tdb_authalgxform != NULL)
AHSTAT_INC(ahs_hist[sav->alg_auth]);
/* Release crypto descriptors. */
free(tc, M_XDATA);
crypto_freereq(crp);
#ifdef REGRESSION
/* Emulate man-in-the-middle attack when ipsec_integrity is TRUE. */
if (V_ipsec_integrity) {
static unsigned char ipseczeroes[AH_HMAC_MAXHASHLEN];
struct auth_hash *esph;
const struct auth_hash *esph;
/*
* Corrupt HMAC if we want to test integrity verification of
@ -990,33 +932,26 @@ esp_output_cb(struct cryptop *crp)
#endif
/* NB: m is reclaimed by ipsec_process_done. */
error = ipsec_process_done(m, isr);
KEY_FREESAV(&sav);
IPSECREQUEST_UNLOCK(isr);
KEY_FREESP(&isr->sp);
error = ipsec_process_done(m, sp, sav, idx);
return (error);
bad:
if (sav)
KEY_FREESAV(&sav);
IPSECREQUEST_UNLOCK(isr);
KEY_FREESP(&isr->sp);
if (m)
m_freem(m);
free(tc, M_XDATA);
free(xd, M_XDATA);
crypto_freereq(crp);
key_freesav(&sav);
key_freesp(&sp);
return (error);
}
static struct xformsw esp_xformsw = {
XF_ESP, XFT_CONF|XFT_AUTH, "IPsec ESP",
esp_init, esp_zeroize, esp_input,
esp_output
.xf_type = XF_ESP,
.xf_name = "IPsec ESP",
.xf_init = esp_init,
.xf_zeroize = esp_zeroize,
.xf_input = esp_input,
.xf_output = esp_output,
};
static void
esp_attach(void)
{
xform_register(&esp_xformsw);
}
SYSINIT(esp_xform_init, SI_SUB_PROTO_DOMAIN, SI_ORDER_MIDDLE, esp_attach, NULL);
SYSINIT(esp_xform_init, SI_SUB_PROTO_DOMAIN, SI_ORDER_MIDDLE,
xform_attach, &esp_xformsw);
SYSUNINIT(esp_xform_uninit, SI_SUB_PROTO_DOMAIN, SI_ORDER_MIDDLE,
xform_detach, &esp_xformsw);

View File

@ -37,7 +37,6 @@
#include <sys/mbuf.h>
#include <sys/lock.h>
#include <sys/mutex.h>
#include <sys/rwlock.h>
#include <sys/socket.h>
#include <sys/kernel.h>
#include <sys/protosw.h>
@ -65,6 +64,7 @@
#include <netipsec/ipcomp_var.h>
#include <netipsec/key.h>
#include <netipsec/key_debug.h>
#include <opencrypto/cryptodev.h>
#include <opencrypto/deflate.h>
@ -88,18 +88,6 @@ SYSCTL_VNET_PCPUSTAT(_net_inet_ipcomp, IPSECCTL_STATS, stats,
static int ipcomp_input_cb(struct cryptop *crp);
static int ipcomp_output_cb(struct cryptop *crp);
struct comp_algo *
ipcomp_algorithm_lookup(int alg)
{
if (alg >= IPCOMP_ALG_MAX)
return NULL;
switch (alg) {
case SADB_X_CALG_DEFLATE:
return &comp_algo_deflate;
}
return NULL;
}
/*
* RFC 3173 p 2.2. Non-Expansion Policy:
* If the total size of a compressed payload and the IPComp header, as
@ -116,10 +104,10 @@ ipcomp_encapcheck(union sockaddr_union *src, union sockaddr_union *dst)
{
struct secasvar *sav;
sav = KEY_ALLOCSA_TUNNEL(src, dst, IPPROTO_IPCOMP);
sav = key_allocsa_tunnel(src, dst, IPPROTO_IPCOMP);
if (sav == NULL)
return (0);
KEY_FREESAV(&sav);
key_freesav(&sav);
if (src->sa.sa_family == AF_INET)
return (sizeof(struct in_addr) << 4);
@ -161,11 +149,11 @@ ipcomp_nonexp_input(struct mbuf **mp, int *offp, int proto)
static int
ipcomp_init(struct secasvar *sav, struct xformsw *xsp)
{
struct comp_algo *tcomp;
const struct comp_algo *tcomp;
struct cryptoini cric;
/* NB: algorithm really comes in alg_enc and not alg_comp! */
tcomp = ipcomp_algorithm_lookup(sav->alg_enc);
tcomp = comp_algorithm_lookup(sav->alg_enc);
if (tcomp == NULL) {
DPRINTF(("%s: unsupported compression algorithm %d\n", __func__,
sav->alg_comp));
@ -201,7 +189,7 @@ ipcomp_zeroize(struct secasvar *sav)
static int
ipcomp_input(struct mbuf *m, struct secasvar *sav, int skip, int protoff)
{
struct tdb_crypto *tc;
struct xform_data *xd;
struct cryptodesc *crdc;
struct cryptop *crp;
struct ipcomp *ipcomp;
@ -236,12 +224,12 @@ ipcomp_input(struct mbuf *m, struct secasvar *sav, int skip, int protoff)
return ENOBUFS;
}
/* Get IPsec-specific opaque pointer */
tc = (struct tdb_crypto *) malloc(sizeof (*tc), M_XDATA, M_NOWAIT|M_ZERO);
if (tc == NULL) {
m_freem(m);
crypto_freereq(crp);
DPRINTF(("%s: cannot allocate tdb_crypto\n", __func__));
xd = malloc(sizeof(*xd), M_XDATA, M_NOWAIT | M_ZERO);
if (xd == NULL) {
DPRINTF(("%s: cannot allocate xform_data\n", __func__));
IPCOMPSTAT_INC(ipcomps_crypto);
crypto_freereq(crp);
m_freem(m);
return ENOBUFS;
}
crdc = crp->crp_desc;
@ -250,27 +238,25 @@ ipcomp_input(struct mbuf *m, struct secasvar *sav, int skip, int protoff)
crdc->crd_len = m->m_pkthdr.len - (skip + hlen);
crdc->crd_inject = skip;
tc->tc_ptr = 0;
/* Decompression operation */
crdc->crd_alg = sav->tdb_compalgxform->type;
/* Crypto operation descriptor */
crp->crp_ilen = m->m_pkthdr.len - (skip + hlen);
crp->crp_flags = CRYPTO_F_IMBUF | CRYPTO_F_CBIFSYNC;
crp->crp_buf = (caddr_t) m;
crp->crp_callback = ipcomp_input_cb;
crp->crp_sid = sav->tdb_cryptoid;
crp->crp_opaque = (caddr_t) tc;
crp->crp_opaque = (caddr_t) xd;
/* These are passed as-is to the callback */
tc->tc_spi = sav->spi;
tc->tc_dst = sav->sah->saidx.dst;
tc->tc_proto = sav->sah->saidx.proto;
tc->tc_protoff = protoff;
tc->tc_skip = skip;
KEY_ADDREFSA(sav);
tc->tc_sav = sav;
xd->sav = sav;
xd->protoff = protoff;
xd->skip = skip;
SECASVAR_LOCK(sav);
crp->crp_sid = xd->cryptoid = sav->tdb_cryptoid;
SECASVAR_UNLOCK(sav);
return crypto_dispatch(crp);
}
@ -281,28 +267,26 @@ ipcomp_input(struct mbuf *m, struct secasvar *sav, int skip, int protoff)
static int
ipcomp_input_cb(struct cryptop *crp)
{
char buf[INET6_ADDRSTRLEN];
char buf[IPSEC_ADDRSTRLEN];
struct cryptodesc *crd;
struct tdb_crypto *tc;
int skip, protoff;
struct xform_data *xd;
struct mbuf *m;
struct secasvar *sav;
struct secasindex *saidx;
int hlen = IPCOMP_HLENGTH, error, clen;
u_int8_t nproto;
caddr_t addr;
uint64_t cryptoid;
int hlen = IPCOMP_HLENGTH, error, clen;
int skip, protoff;
uint8_t nproto;
crd = crp->crp_desc;
tc = (struct tdb_crypto *) crp->crp_opaque;
IPSEC_ASSERT(tc != NULL, ("null opaque crypto data area!"));
skip = tc->tc_skip;
protoff = tc->tc_protoff;
m = (struct mbuf *) crp->crp_buf;
sav = tc->tc_sav;
IPSEC_ASSERT(sav != NULL, ("null SA!"));
xd = (struct xform_data *) crp->crp_opaque;
sav = xd->sav;
skip = xd->skip;
protoff = xd->protoff;
cryptoid = xd->cryptoid;
saidx = &sav->sah->saidx;
IPSEC_ASSERT(saidx->dst.sa.sa_family == AF_INET ||
saidx->dst.sa.sa_family == AF_INET6,
@ -310,12 +294,12 @@ ipcomp_input_cb(struct cryptop *crp)
/* Check for crypto errors */
if (crp->crp_etype) {
/* Reset the session ID */
if (sav->tdb_cryptoid != 0)
sav->tdb_cryptoid = crp->crp_sid;
if (crp->crp_etype == EAGAIN) {
return crypto_dispatch(crp);
/* Reset the session ID */
if (ipsec_updateid(sav, &crp->crp_sid, &cryptoid) != 0)
crypto_freesession(cryptoid);
xd->cryptoid = crp->crp_sid;
return (crypto_dispatch(crp));
}
IPCOMPSTAT_INC(ipcomps_noxform);
DPRINTF(("%s: crypto error %d\n", __func__, crp->crp_etype));
@ -334,7 +318,7 @@ ipcomp_input_cb(struct cryptop *crp)
clen = crp->crp_olen; /* Length of data after processing */
/* Release the crypto descriptors */
free(tc, M_XDATA), tc = NULL;
free(xd, M_XDATA), xd = NULL;
crypto_freereq(crp), crp = NULL;
/* In case it's not done already, adjust the size of the mbuf chain */
@ -379,37 +363,33 @@ ipcomp_input_cb(struct cryptop *crp)
panic("%s: Unexpected address family: %d saidx=%p", __func__,
saidx->dst.sa.sa_family, saidx);
}
KEY_FREESAV(&sav);
return error;
bad:
if (sav)
KEY_FREESAV(&sav);
if (m)
if (sav != NULL)
key_freesav(&sav);
if (m != NULL)
m_freem(m);
if (tc != NULL)
free(tc, M_XDATA);
if (crp)
if (xd != NULL)
free(xd, M_XDATA);
if (crp != NULL)
crypto_freereq(crp);
return error;
}
/*
* IPComp output routine, called by ipsec[46]_process_packet()
* IPComp output routine, called by ipsec[46]_perform_request()
*/
static int
ipcomp_output(struct mbuf *m, struct ipsecrequest *isr, struct mbuf **mp,
int skip, int protoff)
ipcomp_output(struct mbuf *m, struct secpolicy *sp, struct secasvar *sav,
u_int idx, int skip, int protoff)
{
char buf[INET6_ADDRSTRLEN];
struct secasvar *sav;
struct comp_algo *ipcompx;
int error, ralen, maxpacketsize;
char buf[IPSEC_ADDRSTRLEN];
const struct comp_algo *ipcompx;
struct cryptodesc *crdc;
struct cryptop *crp;
struct tdb_crypto *tc;
struct xform_data *xd;
int error, ralen, maxpacketsize;
sav = isr->sav;
IPSEC_ASSERT(sav != NULL, ("null SA"));
ipcompx = sav->tdb_compalgxform;
IPSEC_ASSERT(ipcompx != NULL, ("null compression xform"));
@ -422,7 +402,7 @@ ipcomp_output(struct mbuf *m, struct ipsecrequest *isr, struct mbuf **mp,
*/
if (m->m_pkthdr.len <= ipcompx->minlen) {
IPCOMPSTAT_INC(ipcomps_threshold);
return ipsec_process_done(m, isr);
return ipsec_process_done(m, sp, sav, idx);
}
ralen = m->m_pkthdr.len - skip; /* Raw payload length before comp. */
@ -496,33 +476,31 @@ ipcomp_output(struct mbuf *m, struct ipsecrequest *isr, struct mbuf **mp,
crdc->crd_alg = ipcompx->type;
/* IPsec-specific opaque crypto info */
tc = (struct tdb_crypto *) malloc(sizeof(struct tdb_crypto),
M_XDATA, M_NOWAIT|M_ZERO);
if (tc == NULL) {
xd = malloc(sizeof(struct xform_data), M_XDATA, M_NOWAIT | M_ZERO);
if (xd == NULL) {
IPCOMPSTAT_INC(ipcomps_crypto);
DPRINTF(("%s: failed to allocate tdb_crypto\n", __func__));
DPRINTF(("%s: failed to allocate xform_data\n", __func__));
crypto_freereq(crp);
error = ENOBUFS;
goto bad;
}
key_addref(isr->sp);
tc->tc_isr = isr;
KEY_ADDREFSA(sav);
tc->tc_sav = sav;
tc->tc_spi = sav->spi;
tc->tc_dst = sav->sah->saidx.dst;
tc->tc_proto = sav->sah->saidx.proto;
tc->tc_protoff = protoff;
tc->tc_skip = skip;
xd->sp = sp;
xd->sav = sav;
xd->idx = idx;
xd->skip = skip;
xd->protoff = protoff;
/* Crypto operation descriptor */
crp->crp_ilen = m->m_pkthdr.len; /* Total input length */
crp->crp_flags = CRYPTO_F_IMBUF | CRYPTO_F_CBIFSYNC;
crp->crp_buf = (caddr_t) m;
crp->crp_callback = ipcomp_output_cb;
crp->crp_opaque = (caddr_t) tc;
crp->crp_sid = sav->tdb_cryptoid;
crp->crp_opaque = (caddr_t) xd;
SECASVAR_LOCK(sav);
crp->crp_sid = xd->cryptoid = sav->tdb_cryptoid;
SECASVAR_UNLOCK(sav);
return crypto_dispatch(crp);
bad:
@ -537,39 +515,32 @@ ipcomp_output(struct mbuf *m, struct ipsecrequest *isr, struct mbuf **mp,
static int
ipcomp_output_cb(struct cryptop *crp)
{
char buf[INET6_ADDRSTRLEN];
struct tdb_crypto *tc;
struct ipsecrequest *isr;
char buf[IPSEC_ADDRSTRLEN];
struct xform_data *xd;
struct secpolicy *sp;
struct secasvar *sav;
struct mbuf *m;
int error, skip;
uint64_t cryptoid;
u_int idx;
int error, skip, protoff;
tc = (struct tdb_crypto *) crp->crp_opaque;
IPSEC_ASSERT(tc != NULL, ("null opaque data area!"));
m = (struct mbuf *) crp->crp_buf;
skip = tc->tc_skip;
isr = tc->tc_isr;
IPSEC_ASSERT(isr->sp != NULL, ("NULL isr->sp"));
IPSECREQUEST_LOCK(isr);
sav = tc->tc_sav;
/* With the isr lock released SA pointer can be updated. */
if (sav != isr->sav) {
IPCOMPSTAT_INC(ipcomps_notdb);
DPRINTF(("%s: SA expired while in crypto\n", __func__));
error = ENOBUFS; /*XXX*/
goto bad;
}
xd = (struct xform_data *) crp->crp_opaque;
idx = xd->idx;
sp = xd->sp;
sav = xd->sav;
skip = xd->skip;
protoff = xd->protoff;
cryptoid = xd->cryptoid;
/* Check for crypto errors */
if (crp->crp_etype) {
/* Reset the session ID */
if (sav->tdb_cryptoid != 0)
sav->tdb_cryptoid = crp->crp_sid;
if (crp->crp_etype == EAGAIN) {
IPSECREQUEST_UNLOCK(isr);
return crypto_dispatch(crp);
/* Reset the session ID */
if (ipsec_updateid(sav, &crp->crp_sid, &cryptoid) != 0)
crypto_freesession(cryptoid);
xd->cryptoid = crp->crp_sid;
return (crypto_dispatch(crp));
}
IPCOMPSTAT_INC(ipcomps_noxform);
DPRINTF(("%s: crypto error %d\n", __func__, crp->crp_etype));
@ -595,7 +566,8 @@ ipcomp_output_cb(struct cryptop *crp)
mo = m_makespace(m, skip, IPCOMP_HLENGTH, &roff);
if (mo == NULL) {
IPCOMPSTAT_INC(ipcomps_wrap);
DPRINTF(("%s: IPCOMP header inject failed for IPCA %s/%08lx\n",
DPRINTF(("%s: IPCOMP header inject failed "
"for IPCA %s/%08lx\n",
__func__, ipsec_address(&sav->sah->saidx.dst, buf,
sizeof(buf)), (u_long) ntohl(sav->spi)));
error = ENOBUFS;
@ -622,7 +594,7 @@ ipcomp_output_cb(struct cryptop *crp)
/* Fix Next Protocol in IPv4/IPv6 header */
prot = IPPROTO_IPCOMP;
m_copyback(m, tc->tc_protoff, sizeof(u_int8_t),
m_copyback(m, protoff, sizeof(u_int8_t),
(u_char *)&prot);
/* Adjust the length in the IP header */
@ -658,33 +630,22 @@ ipcomp_output_cb(struct cryptop *crp)
}
/* Release the crypto descriptor */
free(tc, M_XDATA);
free(xd, M_XDATA);
crypto_freereq(crp);
/* NB: m is reclaimed by ipsec_process_done. */
error = ipsec_process_done(m, isr);
KEY_FREESAV(&sav);
IPSECREQUEST_UNLOCK(isr);
KEY_FREESP(&isr->sp);
error = ipsec_process_done(m, sp, sav, idx);
return (error);
bad:
if (sav)
KEY_FREESAV(&sav);
IPSECREQUEST_UNLOCK(isr);
KEY_FREESP(&isr->sp);
if (m)
m_freem(m);
free(tc, M_XDATA);
free(xd, M_XDATA);
crypto_freereq(crp);
key_freesav(&sav);
key_freesp(&sp);
return (error);
}
static struct xformsw ipcomp_xformsw = {
XF_IPCOMP, XFT_COMP, "IPcomp",
ipcomp_init, ipcomp_zeroize, ipcomp_input,
ipcomp_output
};
#ifdef INET
static const struct encaptab *ipe4_cookie = NULL;
extern struct domain inetdomain;
@ -768,6 +729,15 @@ ipcomp6_nonexp_encapcheck(const struct mbuf *m, int off, int proto,
}
#endif
static struct xformsw ipcomp_xformsw = {
.xf_type = XF_IPCOMP,
.xf_name = "IPcomp",
.xf_init = ipcomp_init,
.xf_zeroize = ipcomp_zeroize,
.xf_input = ipcomp_input,
.xf_output = ipcomp_output,
};
static void
ipcomp_attach(void)
{
@ -780,8 +750,23 @@ ipcomp_attach(void)
ipe6_cookie = encap_attach_func(AF_INET6, -1,
ipcomp6_nonexp_encapcheck, &ipcomp6_protosw, NULL);
#endif
xform_register(&ipcomp_xformsw);
xform_attach(&ipcomp_xformsw);
}
static void
ipcomp_detach(void)
{
#ifdef INET
encap_detach(ipe4_cookie);
#endif
#ifdef INET6
encap_detach(ipe6_cookie);
#endif
xform_detach(&ipcomp_xformsw);
}
SYSINIT(ipcomp_xform_init, SI_SUB_PROTO_DOMAIN, SI_ORDER_MIDDLE,
ipcomp_attach, NULL);
SYSUNINIT(ipcomp_xform_uninit, SI_SUB_PROTO_DOMAIN, SI_ORDER_MIDDLE,
ipcomp_detach, NULL);

View File

@ -1,7 +1,6 @@
/* $FreeBSD$ */
/*-
* Copyright (c) 2003 Bruce M. Simpson <bms@spc.org>
* Copyright (c) 2016 Andrey V. Elsukov <ae@FreeBSD.org>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@ -28,19 +27,27 @@
*/
/* TCP MD5 Signature Option (RFC2385) */
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include "opt_inet.h"
#include "opt_inet6.h"
#include "opt_ipsec.h"
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/mbuf.h>
#include <sys/lock.h>
#include <sys/md5.h>
#include <sys/rmlock.h>
#include <sys/socket.h>
#include <sys/sockopt.h>
#include <sys/kernel.h>
#include <sys/module.h>
#include <sys/protosw.h>
#include <sys/sysctl.h>
#include <netinet/in.h>
#include <netinet/in_pcb.h>
#include <netinet/in_systm.h>
#include <netinet/ip.h>
#include <netinet/ip_var.h>
@ -50,6 +57,7 @@
#include <net/vnet.h>
#include <netipsec/ipsec.h>
#include <netipsec/ipsec_support.h>
#include <netipsec/xform.h>
#ifdef INET6
@ -60,13 +68,266 @@
#include <netipsec/key.h>
#include <netipsec/key_debug.h>
#define TCP_SIGLEN 16 /* length of computed digest in bytes */
#define TCP_KEYLEN_MIN 1 /* minimum length of TCP-MD5 key */
#define TCP_KEYLEN_MAX 80 /* maximum length of TCP-MD5 key */
static inline void
tcp_fields_to_net(struct tcphdr *th)
{
th->th_seq = htonl(th->th_seq);
th->th_ack = htonl(th->th_ack);
th->th_win = htons(th->th_win);
th->th_urp = htons(th->th_urp);
}
static int
tcp_ipsec_pcbctl(struct inpcb *inp, struct sockopt *sopt)
{
struct tcpcb *tp;
int error, optval;
INP_WLOCK_ASSERT(inp);
if (sopt->sopt_name != TCP_MD5SIG) {
INP_WUNLOCK(inp);
return (ENOPROTOOPT);
}
tp = intotcpcb(inp);
if (sopt->sopt_dir == SOPT_GET) {
optval = (tp->t_flags & TF_SIGNATURE) ? 1 : 0;
INP_WUNLOCK(inp);
/* On success return with released INP_WLOCK */
return (sooptcopyout(sopt, &optval, sizeof(optval)));
}
INP_WUNLOCK(inp);
error = sooptcopyin(sopt, &optval, sizeof(optval), sizeof(optval));
if (error != 0)
return (error);
/* INP_WLOCK_RECHECK */
INP_WLOCK(inp);
if (inp->inp_flags & (INP_TIMEWAIT | INP_DROPPED)) {
INP_WUNLOCK(inp);
return (ECONNRESET);
}
if (optval > 0)
tp->t_flags |= TF_SIGNATURE;
else
tp->t_flags &= ~TF_SIGNATURE;
/* On success return with acquired INP_WLOCK */
return (error);
}
/*
* Callback function invoked by m_apply() to digest TCP segment data
* contained within an mbuf chain.
*/
static int
tcp_signature_apply(void *fstate, void *data, u_int len)
{
MD5Update(fstate, (u_char *)data, len);
return (0);
}
#ifdef INET
static int
ip_pseudo_compute(struct mbuf *m, MD5_CTX *ctx)
{
struct ippseudo ipp;
struct ip *ip;
ip = mtod(m, struct ip *);
ipp.ippseudo_src.s_addr = ip->ip_src.s_addr;
ipp.ippseudo_dst.s_addr = ip->ip_dst.s_addr;
ipp.ippseudo_p = IPPROTO_TCP;
ipp.ippseudo_pad = 0;
ipp.ippseudo_len = htons(m->m_pkthdr.len - (ip->ip_hl << 2));
MD5Update(ctx, (char *)&ipp, sizeof(ipp));
return (ip->ip_hl << 2);
}
#endif
#ifdef INET6
static int
ip6_pseudo_compute(struct mbuf *m, MD5_CTX *ctx)
{
struct ip6_pseudo {
struct in6_addr src, dst;
uint32_t len;
uint32_t nxt;
} ip6p __aligned(4);
struct ip6_hdr *ip6;
ip6 = mtod(m, struct ip6_hdr *);
ip6p.src = ip6->ip6_src;
ip6p.dst = ip6->ip6_dst;
ip6p.len = htonl(m->m_pkthdr.len - sizeof(*ip6)); /* XXX: ext headers */
ip6p.nxt = htonl(IPPROTO_TCP);
MD5Update(ctx, (char *)&ip6p, sizeof(ip6p));
return (sizeof(*ip6));
}
#endif
static int
tcp_signature_compute(struct mbuf *m, struct tcphdr *th,
struct secasvar *sav, u_char *buf)
{
MD5_CTX ctx;
int len;
u_short csum;
MD5Init(&ctx);
/* Step 1: Update MD5 hash with IP(v6) pseudo-header. */
switch (sav->sah->saidx.dst.sa.sa_family) {
#ifdef INET
case AF_INET:
len = ip_pseudo_compute(m, &ctx);
break;
#endif
#ifdef INET6
case AF_INET6:
len = ip6_pseudo_compute(m, &ctx);
break;
#endif
default:
return (EAFNOSUPPORT);
}
/*
* Step 2: Update MD5 hash with TCP header, excluding options.
* The TCP checksum must be set to zero.
*/
csum = th->th_sum;
th->th_sum = 0;
MD5Update(&ctx, (char *)th, sizeof(struct tcphdr));
th->th_sum = csum;
/*
* Step 3: Update MD5 hash with TCP segment data.
* Use m_apply() to avoid an early m_pullup().
*/
len += (th->th_off << 2);
if (m->m_pkthdr.len - len > 0)
m_apply(m, len, m->m_pkthdr.len - len,
tcp_signature_apply, &ctx);
/*
* Step 4: Update MD5 hash with shared secret.
*/
MD5Update(&ctx, sav->key_auth->key_data, _KEYLEN(sav->key_auth));
MD5Final(buf, &ctx);
key_sa_recordxfer(sav, m);
return (0);
}
static void
setsockaddrs(const struct mbuf *m, union sockaddr_union *src,
union sockaddr_union *dst)
{
struct ip *ip;
IPSEC_ASSERT(m->m_len >= sizeof(*ip), ("unexpected mbuf len"));
ip = mtod(m, struct ip *);
switch (ip->ip_v) {
#ifdef INET
case IPVERSION:
ipsec4_setsockaddrs(m, src, dst);
break;
#endif
#ifdef INET6
case (IPV6_VERSION >> 4):
ipsec6_setsockaddrs(m, src, dst);
break;
#endif
default:
bzero(src, sizeof(*src));
bzero(dst, sizeof(*dst));
}
}
/*
* Compute TCP-MD5 hash of an *INBOUND* TCP segment.
* Parameters:
* m pointer to head of mbuf chain
* th pointer to TCP header
* buf pointer to storage for computed MD5 digest
*
* Return 0 if successful, otherwise return -1.
*/
static int
tcp_ipsec_input(struct mbuf *m, struct tcphdr *th, u_char *buf)
{
char tmpdigest[TCP_SIGLEN];
struct secasindex saidx;
struct secasvar *sav;
setsockaddrs(m, &saidx.src, &saidx.dst);
saidx.proto = IPPROTO_TCP;
saidx.mode = IPSEC_MODE_TCPMD5;
saidx.reqid = 0;
sav = key_allocsa_tcpmd5(&saidx);
if (sav == NULL) {
KMOD_TCPSTAT_INC(tcps_sig_err_buildsig);
return (EACCES);
}
/*
* tcp_input() operates with TCP header fields in host
* byte order. We expect them in network byte order.
*/
tcp_fields_to_net(th);
tcp_signature_compute(m, th, sav, tmpdigest);
tcp_fields_to_host(th);
key_freesav(&sav);
if (bcmp(buf, tmpdigest, TCP_SIGLEN) != 0) {
KMOD_TCPSTAT_INC(tcps_sig_rcvbadsig);
return (EACCES);
}
KMOD_TCPSTAT_INC(tcps_sig_rcvgoodsig);
return (0);
}
/*
* Compute TCP-MD5 hash of an *OUTBOUND* TCP segment.
* Parameters:
* m pointer to head of mbuf chain
* th pointer to TCP header
* buf pointer to storage for computed MD5 digest
*
* Return 0 if successful, otherwise return error code.
*/
static int
tcp_ipsec_output(struct mbuf *m, struct tcphdr *th, u_char *buf)
{
struct secasindex saidx;
struct secasvar *sav;
setsockaddrs(m, &saidx.src, &saidx.dst);
saidx.proto = IPPROTO_TCP;
saidx.mode = IPSEC_MODE_TCPMD5;
saidx.reqid = 0;
sav = key_allocsa_tcpmd5(&saidx);
if (sav == NULL) {
KMOD_TCPSTAT_INC(tcps_sig_err_buildsig);
return (EACCES);
}
tcp_signature_compute(m, th, sav, buf);
key_freesav(&sav);
return (0);
}
/*
* Initialize a TCP-MD5 SA. Called when the SA is being set up.
*
* We don't need to set up the tdb prefixed fields, as we don't use the
* opencrypto code; we just perform a key length check.
*
* XXX: Currently we only allow a single 'magic' SPI to be used.
* XXX: Currently we have used single 'magic' SPI and need to still
* support this.
*
* This allows per-host granularity without affecting the userland
* interface, which is a simple socket option toggle switch,
@ -85,11 +346,6 @@ tcpsignature_init(struct secasvar *sav, struct xformsw *xsp)
{
int keylen;
if (sav->spi != htonl(TCP_SIG_SPI)) {
DPRINTF(("%s: SPI must be TCP_SIG_SPI (0x1000)\n",
__func__));
return (EINVAL);
}
if (sav->alg_auth != SADB_X_AALG_TCP_MD5) {
DPRINTF(("%s: unsupported authentication algorithm %u\n",
__func__, sav->alg_auth));
@ -104,67 +360,76 @@ tcpsignature_init(struct secasvar *sav, struct xformsw *xsp)
DPRINTF(("%s: invalid key length %u\n", __func__, keylen));
return (EINVAL);
}
sav->tdb_xform = xsp;
return (0);
}
/*
* Paranoia.
*
* Called when the SA is deleted.
*/
static int
tcpsignature_zeroize(struct secasvar *sav)
{
if (sav->key_auth)
if (sav->key_auth != NULL)
bzero(sav->key_auth->key_data, _KEYLEN(sav->key_auth));
sav->tdb_cryptoid = 0;
sav->tdb_authalgxform = NULL;
sav->tdb_xform = NULL;
return (0);
}
/*
* Verify that an input packet passes authentication.
* Called from the ipsec layer.
* We do this from within tcp itself, so this routine is just a stub.
*/
static int
tcpsignature_input(struct mbuf *m, struct secasvar *sav, int skip,
int protoff)
{
return (0);
}
/*
* Prepend the authentication header.
* Called from the ipsec layer.
* We do this from within tcp itself, so this routine is just a stub.
*/
static int
tcpsignature_output(struct mbuf *m, struct ipsecrequest *isr,
struct mbuf **mp, int skip, int protoff)
{
return (EINVAL);
}
static struct xformsw tcpsignature_xformsw = {
XF_TCPSIGNATURE, XFT_AUTH, "TCPMD5",
tcpsignature_init, tcpsignature_zeroize,
tcpsignature_input, tcpsignature_output
.xf_type = XF_TCPSIGNATURE,
.xf_name = "TCP-MD5",
.xf_init = tcpsignature_init,
.xf_zeroize = tcpsignature_zeroize,
};
static void
tcpsignature_attach(void)
static const struct tcpmd5_methods tcpmd5_methods = {
.input = tcp_ipsec_input,
.output = tcp_ipsec_output,
.pcbctl = tcp_ipsec_pcbctl,
};
#ifndef KLD_MODULE
/* TCP-MD5 support is build in the kernel */
static const struct tcpmd5_support tcpmd5_ipsec = {
.enabled = IPSEC_MODULE_ENABLED,
.methods = &tcpmd5_methods
};
const struct tcpmd5_support * const tcp_ipsec_support = &tcpmd5_ipsec;
#endif /* !KLD_MODULE */
static int
tcpmd5_modevent(module_t mod, int type, void *data)
{
xform_register(&tcpsignature_xformsw);
switch (type) {
case MOD_LOAD:
xform_attach(&tcpsignature_xformsw);
#ifdef KLD_MODULE
tcpmd5_support_enable(&tcpmd5_methods);
#endif
break;
case MOD_UNLOAD:
#ifdef KLD_MODULE
tcpmd5_support_disable();
#endif
xform_detach(&tcpsignature_xformsw);
break;
default:
return (EOPNOTSUPP);
}
return (0);
}
SYSINIT(tcpsignature_xform_init, SI_SUB_PROTO_DOMAIN, SI_ORDER_MIDDLE,
tcpsignature_attach, NULL);
static moduledata_t tcpmd5_mod = {
"tcpmd5",
tcpmd5_modevent,
0
};
DECLARE_MODULE(tcpmd5, tcpmd5_mod, SI_SUB_PROTO_DOMAIN, SI_ORDER_ANY);
MODULE_VERSION(tcpmd5, 1);
#ifdef KLD_MODULE
MODULE_DEPEND(tcpmd5, ipsec_support, 1, 1, 1);
#endif

View File

@ -858,12 +858,25 @@ tcp_stats(u_long off, const char *name, int af1 __unused, int proto __unused)
"{N:/successful ECN handshake%s}\n");
p(tcps_ecn_rcwnd, "\t{:congestion-reductions/%ju} "
"{N:/time%s ECN reduced the congestion window}\n");
xo_close_container("ecn");
xo_open_container("tcp-signature");
p(tcps_sig_rcvgoodsig, "\t{:received-good-signature/%ju} "
"{N:/packet%s with matching signature received}\n");
p(tcps_sig_rcvbadsig, "\t{:received-bad-signature/%ju} "
"{N:/packet%s with bad signature received}\n");
p(tcps_sig_err_buildsig, "\t{:failed-make-signature/%ju} "
"{N:/time%s failed to make signature due to no SA}\n");
p(tcps_sig_err_sigopt, "\t{:no-signature-expected/%ju} "
"{N:/time%s unexpected signature received}\n");
p(tcps_sig_err_nosigopt, "\t{:no-signature-provided/%ju} "
"{N:/time%s no signature provided by segment}\n");
#undef p
#undef p1a
#undef p2
#undef p2a
#undef p3
xo_close_container("ecn");
xo_close_container("tcp-signature");
xo_open_container("TCP connection count by state");
xo_emit("{T:/TCP connection count by state}:\n");