summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--ObsoleteFiles.inc3
-rw-r--r--contrib/netcat/netcat.c31
-rw-r--r--lib/libipsec/pfkey.c13
-rw-r--r--lib/libipsec/pfkey_dump.c65
-rw-r--r--sbin/ifconfig/Makefile1
-rw-r--r--sbin/ifconfig/ifipsec.c101
-rw-r--r--sbin/ipfw/ipfw.83
-rw-r--r--sbin/setkey/setkey.841
-rw-r--r--sbin/setkey/setkey.c24
-rw-r--r--share/man/man4/Makefile1
-rw-r--r--share/man/man4/if_ipsec.4141
-rw-r--r--share/man/man4/ipsec.433
-rw-r--r--share/man/man4/tcp.424
-rw-r--r--share/man/man4/udp.416
-rw-r--r--sys/conf/NOTES24
-rw-r--r--sys/conf/files72
-rw-r--r--sys/conf/files.amd645
-rw-r--r--sys/conf/files.arm4
-rw-r--r--sys/conf/files.arm644
-rw-r--r--sys/conf/files.i3864
-rw-r--r--sys/conf/files.mips6
-rw-r--r--sys/conf/files.pc984
-rw-r--r--sys/conf/files.powerpc4
-rw-r--r--sys/conf/files.riscv4
-rw-r--r--sys/conf/files.sparc644
-rw-r--r--sys/conf/kern.opts.mk1
-rw-r--r--sys/conf/options5
-rw-r--r--sys/libkern/arc4random.c117
-rw-r--r--sys/modules/Makefile6
-rw-r--r--sys/modules/ipsec/Makefile14
-rw-r--r--sys/modules/tcp/tcpmd5/Makefile11
-rw-r--r--sys/net/if_ipsec.c1002
-rw-r--r--sys/net/if_ipsec.h39
-rw-r--r--sys/net/pfkeyv2.h18
-rw-r--r--sys/netinet/in_pcb.c14
-rw-r--r--sys/netinet/in_proto.c38
-rw-r--r--sys/netinet/ip_input.c101
-rw-r--r--sys/netinet/ip_ipsec.c249
-rw-r--r--sys/netinet/ip_ipsec.h40
-rw-r--r--sys/netinet/ip_output.c66
-rw-r--r--sys/netinet/raw_ip.c11
-rw-r--r--sys/netinet/sctp_input.c28
-rw-r--r--sys/netinet/sctp_os_bsd.h10
-rw-r--r--sys/netinet/sctp_pcb.c18
-rw-r--r--sys/netinet/tcp_input.c116
-rw-r--r--sys/netinet/tcp_output.c67
-rw-r--r--sys/netinet/tcp_stacks/fastpath.c6
-rw-r--r--sys/netinet/tcp_subr.c369
-rw-r--r--sys/netinet/tcp_syncache.c149
-rw-r--r--sys/netinet/tcp_usrreq.c30
-rw-r--r--sys/netinet/tcp_var.h30
-rw-r--r--sys/netinet/udp.h2
-rw-r--r--sys/netinet/udp_usrreq.c224
-rw-r--r--sys/netinet6/in6.h3
-rw-r--r--sys/netinet6/in6_proto.c38
-rw-r--r--sys/netinet6/ip6_forward.c229
-rw-r--r--sys/netinet6/ip6_input.c48
-rw-r--r--sys/netinet6/ip6_ipsec.c297
-rw-r--r--sys/netinet6/ip6_ipsec.h42
-rw-r--r--sys/netinet6/ip6_output.c91
-rw-r--r--sys/netinet6/raw_ip6.c24
-rw-r--r--sys/netinet6/sctp6_usrreq.c9
-rw-r--r--sys/netinet6/udp6_usrreq.c15
-rw-r--r--sys/netipsec/ipsec.c1691
-rw-r--r--sys/netipsec/ipsec.h184
-rw-r--r--sys/netipsec/ipsec6.h22
-rw-r--r--sys/netipsec/ipsec_input.c546
-rw-r--r--sys/netipsec/ipsec_mbuf.c20
-rw-r--r--sys/netipsec/ipsec_mod.c148
-rw-r--r--sys/netipsec/ipsec_output.c1132
-rw-r--r--sys/netipsec/ipsec_pcb.c479
-rw-r--r--sys/netipsec/ipsec_support.h190
-rw-r--r--sys/netipsec/key.c6102
-rw-r--r--sys/netipsec/key.h81
-rw-r--r--sys/netipsec/key_debug.c392
-rw-r--r--sys/netipsec/key_debug.h31
-rw-r--r--sys/netipsec/keydb.h153
-rw-r--r--sys/netipsec/keysock.c8
-rw-r--r--sys/netipsec/subr_ipsec.c354
-rw-r--r--sys/netipsec/udpencap.c297
-rw-r--r--sys/netipsec/xform.h75
-rw-r--r--sys/netipsec/xform_ah.c312
-rw-r--r--sys/netipsec/xform_esp.c323
-rw-r--r--sys/netipsec/xform_ipcomp.c245
-rw-r--r--sys/netipsec/xform_tcp.c353
-rw-r--r--usr.bin/netstat/inet.c15
86 files changed, 9581 insertions, 7781 deletions
diff --git a/ObsoleteFiles.inc b/ObsoleteFiles.inc
index 70b4a34..ea5ffcd 100644
--- a/ObsoleteFiles.inc
+++ b/ObsoleteFiles.inc
@@ -45,6 +45,9 @@ OLD_FILES+=usr/tests/sys/geom/class/gate/3_test
OLD_FILES+=usr/tests/sys/geom/class/gate/conf.sh
# 20170211: libarchive ACL pax test renamed to test_acl_pax_posix1e.tar.uu
OLD_FILES+=usr/tests/lib/libarchive/test_acl_pax.tar.uu
+# 20170206: merged projects/ipsec
+OLD_FILES+=usr/include/netinet/ip_ipsec.h
+OLD_FILES+=usr/include/netinet6/ip6_ipsec.h
# 20170103: libbsnmptools.so made into an INTERNALLIB
OLD_FILES+=usr/lib/libbsnmptools.a
OLD_FILES+=usr/lib/libbsnmptools_p.a
diff --git a/contrib/netcat/netcat.c b/contrib/netcat/netcat.c
index 04e31e8a..efbe9bb 100644
--- a/contrib/netcat/netcat.c
+++ b/contrib/netcat/netcat.c
@@ -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);
diff --git a/lib/libipsec/pfkey.c b/lib/libipsec/pfkey.c
index 16e018b..34a3ac3 100644
--- a/lib/libipsec/pfkey.c
+++ b/lib/libipsec/pfkey.c
@@ -1776,20 +1776,17 @@ pfkey_align(msg, mhp)
case SADB_EXT_SPIRANGE:
case SADB_X_EXT_POLICY:
case SADB_X_EXT_SA2:
- 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;
diff --git a/lib/libipsec/pfkey_dump.c b/lib/libipsec/pfkey_dump.c
index 872aa43..5619ac7 100644
--- a/lib/libipsec/pfkey_dump.c
+++ b/lib/libipsec/pfkey_dump.c
@@ -35,8 +35,9 @@ __FBSDID("$FreeBSD$");
#include <sys/types.h>
#include <sys/param.h>
#include <sys/socket.h>
-#include <netipsec/ipsec.h>
+#include <net/if.h>
#include <net/pfkeyv2.h>
+#include <netipsec/ipsec.h>
#include <netipsec/key_var.h>
#include <netipsec/key_debug.h>
@@ -204,6 +205,13 @@ static struct val2str str_alg_comp[] = {
{ -1, NULL, },
};
+static struct val2str str_sp_scope[] = {
+ { IPSEC_POLICYSCOPE_GLOBAL, "global" },
+ { IPSEC_POLICYSCOPE_IFNET, "ifnet" },
+ { IPSEC_POLICYSCOPE_PCB, "pcb"},
+ { -1, NULL },
+};
+
/*
* dump SADB_MSG formated. For debugging, you should use kdebug_sadb().
*/
@@ -219,6 +227,10 @@ pfkey_sadump(m)
struct sadb_key *m_auth, *m_enc;
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)) {
@@ -243,33 +255,47 @@ pfkey_sadump(m)
m_sid = (struct sadb_ident *)mhp[SADB_EXT_IDENTITY_SRC];
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);
@@ -280,6 +306,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: ");
@@ -306,7 +344,8 @@ pfkey_sadump(m)
/* replay windoe size & flags */
printf("\tseq=0x%08x replay=%u flags=0x%08x ",
m_sa2->sadb_x_sa2_sequence,
- m_sa->sadb_sa_replay,
+ m_sa_replay ? (m_sa_replay->sadb_x_sa_replay_replay >> 3) :
+ m_sa->sadb_sa_replay,
m_sa->sadb_sa_flags);
/* state */
@@ -367,8 +406,7 @@ pfkey_sadump(m)
}
void
-pfkey_spdump(m)
- struct sadb_msg *m;
+pfkey_spdump(struct sadb_msg *m)
{
char pbuf[NI_MAXSERV];
caddr_t mhp[SADB_EXT_MAX + 1];
@@ -476,10 +514,15 @@ pfkey_spdump(m)
}
- printf("\tspid=%ld seq=%ld pid=%ld\n",
+ printf("\tspid=%ld seq=%ld pid=%ld scope=",
(u_long)m_xpl->sadb_x_policy_id,
(u_long)m->sadb_msg_seq,
(u_long)m->sadb_msg_pid);
+ GETMSGV2S(str_sp_scope, m_xpl->sadb_x_policy_scope);
+ if (m_xpl->sadb_x_policy_scope == IPSEC_POLICYSCOPE_IFNET &&
+ if_indextoname(m_xpl->sadb_x_policy_ifindex, pbuf) != NULL)
+ printf("ifname=%s", pbuf);
+ printf("\n");
/* XXX TEST */
printf("\trefcnt=%u\n", m->sadb_msg_reserved);
diff --git a/sbin/ifconfig/Makefile b/sbin/ifconfig/Makefile
index a3623d5..5433926 100644
--- a/sbin/ifconfig/Makefile
+++ b/sbin/ifconfig/Makefile
@@ -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
diff --git a/sbin/ifconfig/ifipsec.c b/sbin/ifconfig/ifipsec.c
new file mode 100644
index 0000000..fed5a84
--- /dev/null
+++ b/sbin/ifconfig/ifipsec.c
@@ -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
+}
diff --git a/sbin/ipfw/ipfw.8 b/sbin/ipfw/ipfw.8
index ce8823f..0d26dca 100644
--- a/sbin/ipfw/ipfw.8
+++ b/sbin/ipfw/ipfw.8
@@ -1518,8 +1518,7 @@ Matches IPv4 packets whose precedence field is equal to
.It Cm ipsec
Matches packets that have IPSEC history associated with them
(i.e., the packet comes encapsulated in IPSEC, the kernel
-has IPSEC support and IPSEC_FILTERTUNNEL option, and can correctly
-decapsulate it).
+has IPSEC support, and can correctly decapsulate it).
.Pp
Note that specifying
.Cm ipsec
diff --git a/sbin/setkey/setkey.8 b/sbin/setkey/setkey.8
index 1eab814..afaa753 100644
--- a/sbin/setkey/setkey.8
+++ b/sbin/setkey/setkey.8
@@ -29,7 +29,7 @@
.\"
.\" $FreeBSD$
.\"
-.Dd October 3, 2016
+.Dd March 7, 2017
.Dt SETKEY 8
.Os
.\"
@@ -45,7 +45,7 @@
.Op Fl v
.Fl f Ar filename
.Nm
-.Op Fl aPlv
+.Op Fl Pgltv
.Fl D
.Nm
.Op Fl Pv
@@ -81,18 +81,21 @@ Flush the SAD entries.
If with
.Fl P ,
the SPD entries are flushed.
-.It Fl a
-The
-.Nm
-utility
-usually does not display dead SAD entries with
-.Fl D .
-If with
-.Fl a ,
-the dead SAD entries will be displayed as well.
-A dead SAD entry means that
-it has been expired but remains in the system
-because it is referenced by some SPD entries.
+.It Fl g
+Only SPD entries with global scope are dumped with
+.Fl D
+and
+.Fl P
+flags.
+.It Fl t
+Only SPD entries with ifnet scope are dumped with
+.Fl D
+and
+.Fl P
+flags.
+Such SPD entries are linked to the corresponding
+.Xr if_ipsec 4
+virtual tunneling interface.
.It Fl h
Add hexadecimal dump on
.Fl x
@@ -270,8 +273,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
@@ -595,12 +596,11 @@ keyed-md5 128 ah: 96bit ICV (no document)
keyed-sha1 160 ah: 96bit ICV (no document)
160 ah-old: 128bit ICV (no document)
null 0 to 2048 for debugging
-hmac-sha2-256 256 ah: 96bit ICV
- (draft-ietf-ipsec-ciph-sha-256-00)
+hmac-sha2-256 256 ah: 128bit ICV (RFC4868)
256 ah-old: 128bit ICV (no document)
-hmac-sha2-384 384 ah: 96bit ICV (no document)
+hmac-sha2-384 384 ah: 192bit ICV (RFC4868)
384 ah-old: 128bit ICV (no document)
-hmac-sha2-512 512 ah: 96bit ICV (no document)
+hmac-sha2-512 512 ah: 256bit ICV (RFC4868)
512 ah-old: 128bit ICV (no document)
hmac-ripemd160 160 ah: 96bit ICV (RFC2857)
ah-old: 128bit ICV (no document)
@@ -700,6 +700,7 @@ add 10.1.10.34 10.1.10.36 tcp 0x1000 -A tcp-md5 "TCP-MD5 BGP secret" ;
.\"
.Sh SEE ALSO
.Xr ipsec_set_policy 3 ,
+.Xr if_ipsec 4 ,
.Xr racoon 8 ,
.Xr sysctl 8
.Rs
diff --git a/sbin/setkey/setkey.c b/sbin/setkey/setkey.c
index 543f428..e4525ee 100644
--- a/sbin/setkey/setkey.c
+++ b/sbin/setkey/setkey.c
@@ -56,7 +56,7 @@
void usage(void);
int main(int, char **);
int get_supported(void);
-void sendkeyshort(u_int);
+void sendkeyshort(u_int, uint8_t);
void promisc(void);
int sendkeymsg(char *, size_t);
int postproc(struct sadb_msg *, int);
@@ -81,6 +81,7 @@ int f_cmddump = 0;
int f_policy = 0;
int f_hexdump = 0;
int f_tflag = 0;
+int f_scope = 0;
static time_t thiszone;
extern int lineno;
@@ -93,7 +94,7 @@ usage()
printf("usage: setkey [-v] -c\n");
printf(" setkey [-v] -f filename\n");
- printf(" setkey [-Palv] -D\n");
+ printf(" setkey [-Pagltv] -D\n");
printf(" setkey [-Pv] -F\n");
printf(" setkey [-h] -x\n");
exit(1);
@@ -114,7 +115,7 @@ main(ac, av)
thiszone = gmt2local(0);
- while ((c = getopt(ac, av, "acdf:hlvxDFP")) != -1) {
+ while ((c = getopt(ac, av, "acdf:ghltvxDFP")) != -1) {
switch (c) {
case 'c':
f_mode = MODE_SCRIPT;
@@ -149,6 +150,12 @@ main(ac, av)
case 'P':
f_policy = 1;
break;
+ case 'g': /* global */
+ f_scope |= IPSEC_POLICYSCOPE_GLOBAL;
+ break;
+ case 't': /* tunnel */
+ f_scope |= IPSEC_POLICYSCOPE_IFNET;
+ break;
case 'v':
f_verbose = 1;
break;
@@ -166,10 +173,12 @@ main(ac, av)
switch (f_mode) {
case MODE_CMDDUMP:
- sendkeyshort(f_policy ? SADB_X_SPDDUMP: SADB_DUMP);
+ sendkeyshort(f_policy ? SADB_X_SPDDUMP: SADB_DUMP,
+ f_policy ? f_scope: SADB_SATYPE_UNSPEC);
break;
case MODE_CMDFLUSH:
- sendkeyshort(f_policy ? SADB_X_SPDFLUSH: SADB_FLUSH);
+ sendkeyshort(f_policy ? SADB_X_SPDFLUSH: SADB_FLUSH,
+ SADB_SATYPE_UNSPEC);
break;
case MODE_SCRIPT:
if (get_supported() < 0) {
@@ -204,15 +213,14 @@ get_supported()
}
void
-sendkeyshort(type)
- u_int type;
+sendkeyshort(u_int type, uint8_t satype)
{
struct sadb_msg msg;
msg.sadb_msg_version = PF_KEY_V2;
msg.sadb_msg_type = type;
msg.sadb_msg_errno = 0;
- msg.sadb_msg_satype = SADB_SATYPE_UNSPEC;
+ msg.sadb_msg_satype = satype;
msg.sadb_msg_len = PFKEY_UNIT64(sizeof(msg));
msg.sadb_msg_reserved = 0;
msg.sadb_msg_seq = 0;
diff --git a/share/man/man4/Makefile b/share/man/man4/Makefile
index 1ac5387..8543ec0 100644
--- a/share/man/man4/Makefile
+++ b/share/man/man4/Makefile
@@ -202,6 +202,7 @@ MAN= aac.4 \
icmp.4 \
icmp6.4 \
ida.4 \
+ if_ipsec.4 \
ifmib.4 \
ig4.4 \
igb.4 \
diff --git a/share/man/man4/if_ipsec.4 b/share/man/man4/if_ipsec.4
new file mode 100644
index 0000000..2e978c0
--- /dev/null
+++ b/share/man/man4/if_ipsec.4
@@ -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
diff --git a/share/man/man4/ipsec.4 b/share/man/man4/ipsec.4
index c6a3f24..9bee931 100644
--- a/share/man/man4/ipsec.4
+++ b/share/man/man4/ipsec.4
@@ -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 ,
diff --git a/share/man/man4/tcp.4 b/share/man/man4/tcp.4
index 9911a36..8c71716 100644
--- a/share/man/man4/tcp.4
+++ b/share/man/man4/tcp.4
@@ -34,7 +34,7 @@
.\" From: @(#)tcp.4 8.1 (Berkeley) 6/5/93
.\" $FreeBSD$
.\"
-.Dd October 21, 2016
+.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
diff --git a/share/man/man4/udp.4 b/share/man/man4/udp.4
index 2f828bf..3d869c3 100644
--- a/share/man/man4/udp.4
+++ b/share/man/man4/udp.4
@@ -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
diff --git a/sys/conf/NOTES b/sys/conf/NOTES
index 6d15ae4..6c13640 100644
--- a/sys/conf/NOTES
+++ b/sys/conf/NOTES
@@ -613,23 +613,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
-#
-# #DEPRECATED#
-# Set IPSEC_FILTERTUNNEL to change the default of the sysctl to force packets
-# coming through a tunnel to be processed by any configured packet filtering
-# twice. The default is that packets coming out of a tunnel are _not_ processed;
-# they are assumed trusted.
-#
-# IPSEC history is preserved for such packets, and can be filtered
-# using ipfw(8)'s 'ipsec' keyword, when this option is enabled.
-#
-#options IPSEC_FILTERTUNNEL #filter ipsec packets from a tunnel
-#
-# 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
@@ -1015,7 +1004,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
diff --git a/sys/conf/files b/sys/conf/files
index 52cb263..afd3ca5 100644
--- a/sys/conf/files
+++ b/sys/conf/files
@@ -574,22 +574,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 | geom_bde | \
- ipsec | random !random_loadable | wlan_ccmp
+ ipsec | ipsec_support | random !random_loadable | wlan_ccmp
crypto/rijndael/rijndael-api-fst.c optional 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 | 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 | 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
@@ -3592,8 +3594,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
@@ -3629,6 +3630,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
@@ -3814,7 +3816,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
@@ -3883,7 +3884,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
@@ -3896,15 +3896,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
@@ -4278,18 +4288,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
diff --git a/sys/conf/files.amd64 b/sys/conf/files.amd64
index da1b432..4ad9ba5 100644
--- a/sys/conf/files.amd64
+++ b/sys/conf/files.amd64
@@ -179,8 +179,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
diff --git a/sys/conf/files.arm b/sys/conf/files.arm
index a5657bc..04bedd4 100644
--- a/sys/conf/files.arm
+++ b/sys/conf/files.arm
@@ -105,8 +105,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/dwc/if_dwc.c optional dwc
dev/dwc/if_dwc_if.m optional dwc
dev/fb/fb.c optional sc
diff --git a/sys/conf/files.arm64 b/sys/conf/files.arm64
index 053eb5d..10eb61f 100644
--- a/sys/conf/files.arm64
+++ b/sys/conf/files.arm64
@@ -65,8 +65,8 @@ arm64/cavium/thunder_pcie_pem.c optional soc_cavm_thunderx pci
arm64/cavium/thunder_pcie_pem_fdt.c optional soc_cavm_thunderx pci fdt
arm64/cavium/thunder_pcie_common.c optional soc_cavm_thunderx pci
arm64/cloudabi64/cloudabi64_sysvec.c optional compat_cloudabi64
-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 fdt
dev/hwpmc/hwpmc_arm64.c optional hwpmc
diff --git a/sys/conf/files.i386 b/sys/conf/files.i386
index c9a98a5..2373780 100644
--- a/sys/conf/files.i386
+++ b/sys/conf/files.i386
@@ -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
diff --git a/sys/conf/files.mips b/sys/conf/files.mips
index 0fe0795..f3a0dc8 100644
--- a/sys/conf/files.mips
+++ b/sys/conf/files.mips
@@ -82,8 +82,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.c optional nvram2env
diff --git a/sys/conf/files.pc98 b/sys/conf/files.pc98
index ae45b80..a3c5149 100644
--- a/sys/conf/files.pc98
+++ b/sys/conf/files.pc98
@@ -90,11 +90,11 @@ 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
-crypto/des/arch/i386/des_enc.S optional crypto | ipsec | netsmb
+crypto/des/arch/i386/des_enc.S optional crypto | ipsec | ipsec_support | netsmb
dev/agp/agp_ali.c optional agp
dev/agp/agp_amd.c optional agp
dev/agp/agp_i810.c optional agp
diff --git a/sys/conf/files.powerpc b/sys/conf/files.powerpc
index d1d5312..1d6aa11 100644
--- a/sys/conf/files.powerpc
+++ b/sys/conf/files.powerpc
@@ -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
diff --git a/sys/conf/files.riscv b/sys/conf/files.riscv
index fe30078..addef0e 100644
--- a/sys/conf/files.riscv
+++ b/sys/conf/files.riscv
@@ -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
diff --git a/sys/conf/files.sparc64 b/sys/conf/files.sparc64
index a9643bd..ab543f1 100644
--- a/sys/conf/files.sparc64
+++ b/sys/conf/files.sparc64
@@ -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
diff --git a/sys/conf/kern.opts.mk b/sys/conf/kern.opts.mk
index bb4270e..81d16df 100644
--- a/sys/conf/kern.opts.mk
+++ b/sys/conf/kern.opts.mk
@@ -34,6 +34,7 @@ __DEFAULT_YES_OPTIONS = \
INET \
INET6 \
IPFILTER \
+ IPSEC_SUPPORT \
ISCSI \
KERNEL_SYMBOLS \
NETGRAPH \
diff --git a/sys/conf/options b/sys/conf/options
index a58f97a..5dd66e0 100644
--- a/sys/conf/options
+++ b/sys/conf/options
@@ -423,8 +423,7 @@ IPFIREWALL_VERBOSE opt_ipfw.h
IPFIREWALL_VERBOSE_LIMIT opt_ipfw.h
IPSEC opt_ipsec.h
IPSEC_DEBUG opt_ipsec.h
-IPSEC_FILTERTUNNEL opt_ipsec.h
-IPSEC_NAT_T opt_ipsec.h
+IPSEC_SUPPORT opt_ipsec.h
IPSTEALTH
KRPC
LIBALIAS
@@ -446,7 +445,7 @@ SIFTR
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
diff --git a/sys/libkern/arc4random.c b/sys/libkern/arc4random.c
index d230aa2..ccda6ad 100644
--- a/sys/libkern/arc4random.c
+++ b/sys/libkern/arc4random.c
@@ -19,6 +19,8 @@ __FBSDID("$FreeBSD$");
#include <sys/lock.h>
#include <sys/mutex.h>
#include <sys/time.h>
+#include <sys/smp.h>
+#include <sys/malloc.h>
#define ARC4_RESEED_BYTES 65536
#define ARC4_RESEED_SECONDS 300
@@ -26,13 +28,23 @@ __FBSDID("$FreeBSD$");
int arc4rand_iniseed_state = ARC4_ENTR_NONE;
-static u_int8_t arc4_i, arc4_j;
-static int arc4_numruns = 0;
-static u_int8_t arc4_sbox[256];
-static time_t arc4_t_reseed;
-static struct mtx arc4_mtx;
+MALLOC_DEFINE(M_ARC4RANDOM, "arc4random", "arc4random structures");
-static u_int8_t arc4_randbyte(void);
+struct arc4_s {
+ struct mtx mtx;
+ u_int8_t i, j;
+ int numruns;
+ u_int8_t sbox[256];
+ time_t t_reseed;
+
+} __aligned(CACHE_LINE_SIZE);
+
+static struct arc4_s *arc4inst = NULL;
+
+#define ARC4_FOREACH(_arc4) \
+ for (_arc4 = &arc4inst[0]; _arc4 <= &arc4inst[mp_maxid]; _arc4++)
+
+static u_int8_t arc4_randbyte(struct arc4_s *arc4);
static __inline void
arc4_swap(u_int8_t *a, u_int8_t *b)
@@ -48,7 +60,7 @@ arc4_swap(u_int8_t *a, u_int8_t *b)
* Stir our S-box.
*/
static void
-arc4_randomstir(void)
+arc4_randomstir(struct arc4_s* arc4)
{
u_int8_t key[ARC4_KEYBYTES];
int n;
@@ -60,15 +72,15 @@ arc4_randomstir(void)
*/
(void)read_random(key, ARC4_KEYBYTES);
getmicrouptime(&tv_now);
- mtx_lock(&arc4_mtx);
+ mtx_lock(&arc4->mtx);
for (n = 0; n < 256; n++) {
- arc4_j = (arc4_j + arc4_sbox[n] + key[n]) % 256;
- arc4_swap(&arc4_sbox[n], &arc4_sbox[arc4_j]);
+ arc4->j = (arc4->j + arc4->sbox[n] + key[n]) % 256;
+ arc4_swap(&arc4->sbox[n], &arc4->sbox[arc4->j]);
}
- arc4_i = arc4_j = 0;
+ arc4->i = arc4->j = 0;
/* Reset for next reseed cycle. */
- arc4_t_reseed = tv_now.tv_sec + ARC4_RESEED_SECONDS;
- arc4_numruns = 0;
+ arc4->t_reseed = tv_now.tv_sec + ARC4_RESEED_SECONDS;
+ arc4->numruns = 0;
/*
* Throw away the first N words of output, as suggested in the
* paper "Weaknesses in the Key Scheduling Algorithm of RC4"
@@ -77,8 +89,9 @@ arc4_randomstir(void)
* http://dl.acm.org/citation.cfm?id=646557.694759
*/
for (n = 0; n < 768*4; n++)
- arc4_randbyte();
- mtx_unlock(&arc4_mtx);
+ arc4_randbyte(arc4);
+
+ mtx_unlock(&arc4->mtx);
}
/*
@@ -87,33 +100,57 @@ arc4_randomstir(void)
static void
arc4_init(void)
{
+ struct arc4_s *arc4;
int n;
- mtx_init(&arc4_mtx, "arc4_mtx", NULL, MTX_DEF);
- arc4_i = arc4_j = 0;
- for (n = 0; n < 256; n++)
- arc4_sbox[n] = (u_int8_t) n;
+ arc4inst = malloc((mp_maxid + 1) * sizeof(struct arc4_s),
+ M_ARC4RANDOM, M_NOWAIT | M_ZERO);
+ KASSERT(arc4inst != NULL, ("arc4_init: memory allocation error"));
+
+ ARC4_FOREACH(arc4) {
+ mtx_init(&arc4->mtx, "arc4_mtx", NULL, MTX_DEF);
+
+ arc4->i = arc4->j = 0;
+ for (n = 0; n < 256; n++)
+ arc4->sbox[n] = (u_int8_t) n;
+
+ arc4->t_reseed = -1;
+ arc4->numruns = 0;
+ }
+}
+SYSINIT(arc4, SI_SUB_LOCK, SI_ORDER_ANY, arc4_init, NULL);
+
+
+static void
+arc4_uninit(void)
+{
+ struct arc4_s *arc4;
+
+ ARC4_FOREACH(arc4) {
+ mtx_destroy(&arc4->mtx);
+ }
- arc4_t_reseed = 0;
+ free(arc4inst, M_ARC4RANDOM);
}
-SYSINIT(arc4_init, SI_SUB_LOCK, SI_ORDER_ANY, arc4_init, NULL);
+SYSUNINIT(arc4, SI_SUB_LOCK, SI_ORDER_ANY, arc4_uninit, NULL);
+
/*
* Generate a random byte.
*/
static u_int8_t
-arc4_randbyte(void)
+arc4_randbyte(struct arc4_s *arc4)
{
u_int8_t arc4_t;
- arc4_i = (arc4_i + 1) % 256;
- arc4_j = (arc4_j + arc4_sbox[arc4_i]) % 256;
+ arc4->i = (arc4->i + 1) % 256;
+ arc4->j = (arc4->j + arc4->sbox[arc4->i]) % 256;
- arc4_swap(&arc4_sbox[arc4_i], &arc4_sbox[arc4_j]);
+ arc4_swap(&arc4->sbox[arc4->i], &arc4->sbox[arc4->j]);
- arc4_t = (arc4_sbox[arc4_i] + arc4_sbox[arc4_j]) % 256;
- return arc4_sbox[arc4_t];
+ arc4_t = (arc4->sbox[arc4->i] + arc4->sbox[arc4->j]) % 256;
+ return arc4->sbox[arc4_t];
}
/*
@@ -124,20 +161,26 @@ arc4rand(void *ptr, u_int len, int reseed)
{
u_char *p;
struct timeval tv;
+ struct arc4_s *arc4;
+ if (reseed || atomic_cmpset_int(&arc4rand_iniseed_state,
+ ARC4_ENTR_HAVE, ARC4_ENTR_SEED)) {
+ ARC4_FOREACH(arc4)
+ arc4_randomstir(arc4);
+ }
+
+ arc4 = &arc4inst[curcpu];
getmicrouptime(&tv);
- if (atomic_cmpset_int(&arc4rand_iniseed_state, ARC4_ENTR_HAVE,
- ARC4_ENTR_SEED) || reseed ||
- (arc4_numruns > ARC4_RESEED_BYTES) ||
- (tv.tv_sec > arc4_t_reseed))
- arc4_randomstir();
-
- mtx_lock(&arc4_mtx);
- arc4_numruns += len;
+ if ((arc4->numruns > ARC4_RESEED_BYTES) ||
+ (tv.tv_sec > arc4->t_reseed))
+ arc4_randomstir(arc4);
+
+ mtx_lock(&arc4->mtx);
+ arc4->numruns += len;
p = ptr;
while (len--)
- *p++ = arc4_randbyte();
- mtx_unlock(&arc4_mtx);
+ *p++ = arc4_randbyte(arc4);
+ mtx_unlock(&arc4->mtx);
}
uint32_t
diff --git a/sys/modules/Makefile b/sys/modules/Makefile
index 14b7036..a760663 100644
--- a/sys/modules/Makefile
+++ b/sys/modules/Makefile
@@ -177,6 +177,7 @@ SUBDIR= \
ip6_mroute_mod \
ip_mroute_mod \
${_ips} \
+ ${_ipsec} \
${_ipw} \
${_ipwfw} \
${_isci} \
@@ -361,6 +362,7 @@ SUBDIR= \
sysvipc \
${_ti} \
${_tcp_fastpath} \
+ ${_tcpmd5} \
tests/framework \
tests/callout_test \
tl \
@@ -455,6 +457,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") || \
diff --git a/sys/modules/ipsec/Makefile b/sys/modules/ipsec/Makefile
new file mode 100644
index 0000000..ed061f3
--- /dev/null
+++ b/sys/modules/ipsec/Makefile
@@ -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>
diff --git a/sys/modules/tcp/tcpmd5/Makefile b/sys/modules/tcp/tcpmd5/Makefile
new file mode 100644
index 0000000..1337f1d
--- /dev/null
+++ b/sys/modules/tcp/tcpmd5/Makefile
@@ -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>
diff --git a/sys/net/if_ipsec.c b/sys/net/if_ipsec.c
new file mode 100644
index 0000000..e860648
--- /dev/null
+++ b/sys/net/if_ipsec.c
@@ -0,0 +1,1002 @@
+/*-
+ * 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 "opt_inet.h"
+#include "opt_inet6.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/fnv_hash.h>
+#include <sys/jail.h>
+#include <sys/lock.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
+#include <sys/module.h>
+#include <sys/rmlock.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <sys/sx.h>
+#include <sys/errno.h>
+#include <sys/sysctl.h>
+#include <sys/priv.h>
+#include <sys/proc.h>
+#include <sys/conf.h>
+
+#include <net/if.h>
+#include <net/if_var.h>
+#include <net/if_clone.h>
+#include <net/if_types.h>
+#include <net/bpf.h>
+#include <net/route.h>
+#include <net/vnet.h>
+
+#include <netinet/in.h>
+#include <netinet/in_var.h>
+#include <netinet/ip.h>
+
+#include <netinet/ip6.h>
+#include <netinet6/in6_var.h>
+#include <netinet6/scope6_var.h>
+
+#include <netipsec/ipsec.h>
+#ifdef INET6
+#include <netipsec/ipsec6.h>
+#endif
+
+#include <net/if_ipsec.h>
+#include <netipsec/key.h>
+
+#include <security/mac/mac_framework.h>
+
+static MALLOC_DEFINE(M_IPSEC, "ipsec", "IPsec Virtual Tunnel Interface");
+static const char ipsecname[] = "ipsec";
+
+#if defined(INET) && defined(INET6)
+#define IPSEC_SPCOUNT 4
+#else
+#define IPSEC_SPCOUNT 2
+#endif
+
+struct ipsec_softc {
+ struct ifnet *ifp;
+
+ struct rmlock lock;
+ struct secpolicy *sp[IPSEC_SPCOUNT];
+
+ uint32_t reqid;
+ u_int family;
+ u_int fibnum;
+ LIST_ENTRY(ipsec_softc) chain;
+ LIST_ENTRY(ipsec_softc) hash;
+};
+
+#define IPSEC_LOCK_INIT(sc) rm_init(&(sc)->lock, "if_ipsec softc")
+#define IPSEC_LOCK_DESTROY(sc) rm_destroy(&(sc)->lock)
+#define IPSEC_RLOCK_TRACKER struct rm_priotracker ipsec_tracker
+#define IPSEC_RLOCK(sc) rm_rlock(&(sc)->lock, &ipsec_tracker)
+#define IPSEC_RUNLOCK(sc) rm_runlock(&(sc)->lock, &ipsec_tracker)
+#define IPSEC_RLOCK_ASSERT(sc) rm_assert(&(sc)->lock, RA_RLOCKED)
+#define IPSEC_WLOCK(sc) rm_wlock(&(sc)->lock)
+#define IPSEC_WUNLOCK(sc) rm_wunlock(&(sc)->lock)
+#define IPSEC_WLOCK_ASSERT(sc) rm_assert(&(sc)->lock, RA_WLOCKED)
+
+static struct rmlock ipsec_sc_lock;
+RM_SYSINIT(ipsec_sc_lock, &ipsec_sc_lock, "if_ipsec softc list");
+
+#define IPSEC_SC_RLOCK_TRACKER struct rm_priotracker ipsec_sc_tracker
+#define IPSEC_SC_RLOCK() rm_rlock(&ipsec_sc_lock, &ipsec_sc_tracker)
+#define IPSEC_SC_RUNLOCK() rm_runlock(&ipsec_sc_lock, &ipsec_sc_tracker)
+#define IPSEC_SC_RLOCK_ASSERT() rm_assert(&ipsec_sc_lock, RA_RLOCKED)
+#define IPSEC_SC_WLOCK() rm_wlock(&ipsec_sc_lock)
+#define IPSEC_SC_WUNLOCK() rm_wunlock(&ipsec_sc_lock)
+#define IPSEC_SC_WLOCK_ASSERT() rm_assert(&ipsec_sc_lock, RA_WLOCKED)
+
+LIST_HEAD(ipsec_iflist, ipsec_softc);
+static VNET_DEFINE(struct ipsec_iflist, ipsec_sc_list);
+static VNET_DEFINE(struct ipsec_iflist *, ipsec_sc_htbl);
+static VNET_DEFINE(u_long, ipsec_sc_hmask);
+#define V_ipsec_sc_list VNET(ipsec_sc_list)
+#define V_ipsec_sc_htbl VNET(ipsec_sc_htbl)
+#define V_ipsec_sc_hmask VNET(ipsec_sc_hmask)
+
+static uint32_t
+ipsec_hash(uint32_t id)
+{
+
+ return (fnv_32_buf(&id, sizeof(id), FNV1_32_INIT));
+}
+
+#define SCHASH_NHASH_LOG2 5
+#define SCHASH_NHASH (1 << SCHASH_NHASH_LOG2)
+#define SCHASH_HASHVAL(id) (ipsec_hash((id)) & V_ipsec_sc_hmask)
+#define SCHASH_HASH(id) &V_ipsec_sc_htbl[SCHASH_HASHVAL(id)]
+
+/*
+ * ipsec_ioctl_sx protects from concurrent ioctls.
+ */
+static struct sx ipsec_ioctl_sx;
+SX_SYSINIT(ipsec_ioctl_sx, &ipsec_ioctl_sx, "ipsec_ioctl");
+
+static int ipsec_init_reqid(struct ipsec_softc *);
+static int ipsec_set_tunnel(struct ipsec_softc *, struct sockaddr *,
+ struct sockaddr *, uint32_t);
+static void ipsec_delete_tunnel(struct ifnet *, int);
+
+static int ipsec_set_addresses(struct ifnet *, struct sockaddr *,
+ struct sockaddr *);
+static int ipsec_set_reqid(struct ifnet *, uint32_t);
+
+static int ipsec_ioctl(struct ifnet *, u_long, caddr_t);
+static int ipsec_transmit(struct ifnet *, struct mbuf *);
+static int ipsec_output(struct ifnet *, struct mbuf *,
+ const struct sockaddr *, struct route *);
+static void ipsec_qflush(struct ifnet *);
+static int ipsec_clone_create(struct if_clone *, int, caddr_t);
+static void ipsec_clone_destroy(struct ifnet *);
+
+static VNET_DEFINE(struct if_clone *, ipsec_cloner);
+#define V_ipsec_cloner VNET(ipsec_cloner)
+
+static int
+ipsec_clone_create(struct if_clone *ifc, int unit, caddr_t params)
+{
+ struct ipsec_softc *sc;
+ struct ifnet *ifp;
+
+ sc = malloc(sizeof(*sc), M_IPSEC, M_WAITOK | M_ZERO);
+ sc->fibnum = curthread->td_proc->p_fibnum;
+ sc->ifp = ifp = if_alloc(IFT_TUNNEL);
+ IPSEC_LOCK_INIT(sc);
+ ifp->if_softc = sc;
+ if_initname(ifp, ipsecname, unit);
+
+ ifp->if_addrlen = 0;
+ ifp->if_mtu = IPSEC_MTU;
+ ifp->if_flags = IFF_POINTOPOINT | IFF_MULTICAST;
+ ifp->if_ioctl = ipsec_ioctl;
+ ifp->if_transmit = ipsec_transmit;
+ ifp->if_qflush = ipsec_qflush;
+ ifp->if_output = ipsec_output;
+ if_attach(ifp);
+ bpfattach(ifp, DLT_NULL, sizeof(uint32_t));
+
+ IPSEC_SC_WLOCK();
+ LIST_INSERT_HEAD(&V_ipsec_sc_list, sc, chain);
+ IPSEC_SC_WUNLOCK();
+ return (0);
+}
+
+static void
+ipsec_clone_destroy(struct ifnet *ifp)
+{
+ struct ipsec_softc *sc;
+
+ sx_xlock(&ipsec_ioctl_sx);
+ sc = ifp->if_softc;
+
+ IPSEC_SC_WLOCK();
+ ipsec_delete_tunnel(ifp, 1);
+ LIST_REMOVE(sc, chain);
+ IPSEC_SC_WUNLOCK();
+
+ bpfdetach(ifp);
+ if_detach(ifp);
+ ifp->if_softc = NULL;
+ sx_xunlock(&ipsec_ioctl_sx);
+
+ if_free(ifp);
+ IPSEC_LOCK_DESTROY(sc);
+ free(sc, M_IPSEC);
+}
+
+static void
+vnet_ipsec_init(const void *unused __unused)
+{
+
+ LIST_INIT(&V_ipsec_sc_list);
+ V_ipsec_sc_htbl = hashinit(SCHASH_NHASH, M_IPSEC, &V_ipsec_sc_hmask);
+ V_ipsec_cloner = if_clone_simple(ipsecname, ipsec_clone_create,
+ ipsec_clone_destroy, 0);
+}
+VNET_SYSINIT(vnet_ipsec_init, SI_SUB_PROTO_IFATTACHDOMAIN, SI_ORDER_ANY,
+ vnet_ipsec_init, NULL);
+
+static void
+vnet_ipsec_uninit(const void *unused __unused)
+{
+
+ if_clone_detach(V_ipsec_cloner);
+ hashdestroy(V_ipsec_sc_htbl, M_IPSEC, V_ipsec_sc_hmask);
+}
+VNET_SYSUNINIT(vnet_ipsec_uninit, SI_SUB_PROTO_IFATTACHDOMAIN, SI_ORDER_ANY,
+ vnet_ipsec_uninit, NULL);
+
+static struct secpolicy *
+ipsec_getpolicy(struct ipsec_softc *sc, int dir, sa_family_t af)
+{
+
+ switch (af) {
+#ifdef INET
+ case AF_INET:
+ return (sc->sp[(dir == IPSEC_DIR_INBOUND ? 0: 1)]);
+#endif
+#ifdef INET6
+ case AF_INET6:
+ return (sc->sp[(dir == IPSEC_DIR_INBOUND ? 0: 1)
+#ifdef INET
+ + 2
+#endif
+ ]);
+#endif
+ }
+ return (NULL);
+}
+
+static struct secasindex *
+ipsec_getsaidx(struct ipsec_softc *sc, int dir, sa_family_t af)
+{
+ struct secpolicy *sp;
+
+ sp = ipsec_getpolicy(sc, dir, af);
+ if (sp == NULL)
+ return (NULL);
+ return (&sp->req[0]->saidx);
+}
+
+static int
+ipsec_transmit(struct ifnet *ifp, struct mbuf *m)
+{
+ IPSEC_RLOCK_TRACKER;
+ struct ipsec_softc *sc;
+ struct secpolicy *sp;
+ struct ip *ip;
+ uint32_t af;
+ int error;
+
+#ifdef MAC
+ error = mac_ifnet_check_transmit(ifp, m);
+ if (error) {
+ m_freem(m);
+ goto err;
+ }
+#endif
+ error = ENETDOWN;
+ sc = ifp->if_softc;
+ if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0 ||
+ (ifp->if_flags & IFF_MONITOR) != 0 ||
+ (ifp->if_flags & IFF_UP) == 0) {
+ m_freem(m);
+ goto err;
+ }
+
+ /* Determine address family to correctly handle packet in BPF */
+ ip = mtod(m, struct ip *);
+ switch (ip->ip_v) {
+#ifdef INET
+ case IPVERSION:
+ af = AF_INET;
+ break;
+#endif
+#ifdef INET6
+ case (IPV6_VERSION >> 4):
+ af = AF_INET6;
+ break;
+#endif
+ default:
+ error = EAFNOSUPPORT;
+ m_freem(m);
+ goto err;
+ }
+
+ /*
+ * Loop prevention.
+ * XXX: for now just check presence of IPSEC_OUT_DONE mbuf tag.
+ * We can read full chain and compare destination address,
+ * proto and mode from xform_history with values from softc.
+ */
+ if (m_tag_find(m, PACKET_TAG_IPSEC_OUT_DONE, NULL) != NULL) {
+ m_freem(m);
+ goto err;
+ }
+
+ IPSEC_RLOCK(sc);
+ if (sc->family == 0) {
+ IPSEC_RUNLOCK(sc);
+ m_freem(m);
+ goto err;
+ }
+ sp = ipsec_getpolicy(sc, IPSEC_DIR_OUTBOUND, af);
+ key_addref(sp);
+ M_SETFIB(m, sc->fibnum);
+ IPSEC_RUNLOCK(sc);
+
+ BPF_MTAP2(ifp, &af, sizeof(af), m);
+ if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1);
+ if_inc_counter(ifp, IFCOUNTER_OBYTES, m->m_pkthdr.len);
+
+ switch (af) {
+#ifdef INET
+ case AF_INET:
+ error = ipsec4_process_packet(m, sp, NULL);
+ break;
+#endif
+#ifdef INET6
+ case AF_INET6:
+ error = ipsec6_process_packet(m, sp, NULL);
+ break;
+#endif
+ default:
+ panic("%s: unknown address family\n", __func__);
+ }
+err:
+ if (error != 0)
+ if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
+ return (error);
+}
+
+static void
+ipsec_qflush(struct ifnet *ifp __unused)
+{
+
+}
+
+static int
+ipsec_output(struct ifnet *ifp, struct mbuf *m, const struct sockaddr *dst,
+ struct route *ro)
+{
+
+ return (ifp->if_transmit(ifp, m));
+}
+
+int
+ipsec_if_input(struct mbuf *m, struct secasvar *sav, uint32_t af)
+{
+ IPSEC_SC_RLOCK_TRACKER;
+ struct secasindex *saidx;
+ struct ipsec_softc *sc;
+ struct ifnet *ifp;
+
+ if (sav->state != SADB_SASTATE_MATURE &&
+ sav->state != SADB_SASTATE_DYING) {
+ m_freem(m);
+ return (ENETDOWN);
+ }
+
+ if (sav->sah->saidx.mode != IPSEC_MODE_TUNNEL ||
+ sav->sah->saidx.proto != IPPROTO_ESP)
+ return (0);
+
+ IPSEC_SC_RLOCK();
+ /*
+ * We only acquire SC_RLOCK() while we are doing search in
+ * ipsec_sc_htbl. It is safe, because removing softc or changing
+ * of reqid/addresses requires removing from hash table.
+ */
+ LIST_FOREACH(sc, SCHASH_HASH(sav->sah->saidx.reqid), hash) {
+ saidx = ipsec_getsaidx(sc, IPSEC_DIR_INBOUND,
+ sav->sah->saidx.src.sa.sa_family);
+ /* SA's reqid should match reqid in SP */
+ if (saidx == NULL ||
+ sav->sah->saidx.reqid != saidx->reqid)
+ continue;
+ /* SAH's addresses should match tunnel endpoints. */
+ if (key_sockaddrcmp(&sav->sah->saidx.dst.sa,
+ &saidx->dst.sa, 0) != 0)
+ continue;
+ if (key_sockaddrcmp(&sav->sah->saidx.src.sa,
+ &saidx->src.sa, 0) == 0)
+ break;
+ }
+ if (sc == NULL) {
+ IPSEC_SC_RUNLOCK();
+ /* Tunnel was not found. Nothing to do. */
+ return (0);
+ }
+ ifp = sc->ifp;
+ if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0 ||
+ (ifp->if_flags & IFF_UP) == 0) {
+ IPSEC_SC_RUNLOCK();
+ m_freem(m);
+ return (ENETDOWN);
+ }
+ /*
+ * We found matching and working tunnel.
+ * Set its ifnet as receiving interface.
+ */
+ m->m_pkthdr.rcvif = ifp;
+ IPSEC_SC_RUNLOCK();
+
+ /* m_clrprotoflags(m); */
+ M_SETFIB(m, ifp->if_fib);
+ BPF_MTAP2(ifp, &af, sizeof(af), m);
+ if_inc_counter(ifp, IFCOUNTER_IPACKETS, 1);
+ if_inc_counter(ifp, IFCOUNTER_IBYTES, m->m_pkthdr.len);
+ if ((ifp->if_flags & IFF_MONITOR) != 0) {
+ m_freem(m);
+ return (ENETDOWN);
+ }
+ return (0);
+}
+
+/* XXX how should we handle IPv6 scope on SIOC[GS]IFPHYADDR? */
+int
+ipsec_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
+{
+ IPSEC_RLOCK_TRACKER;
+ struct ifreq *ifr = (struct ifreq*)data;
+ struct sockaddr *dst, *src;
+ struct ipsec_softc *sc;
+ struct secasindex *saidx;
+#ifdef INET
+ struct sockaddr_in *sin = NULL;
+#endif
+#ifdef INET6
+ struct sockaddr_in6 *sin6 = NULL;
+#endif
+ uint32_t reqid;
+ int error;
+
+ switch (cmd) {
+ case SIOCSIFADDR:
+ ifp->if_flags |= IFF_UP;
+ case SIOCADDMULTI:
+ case SIOCDELMULTI:
+ case SIOCGIFMTU:
+ case SIOCSIFFLAGS:
+ return (0);
+ case SIOCSIFMTU:
+ if (ifr->ifr_mtu < IPSEC_MTU_MIN ||
+ ifr->ifr_mtu > IPSEC_MTU_MAX)
+ return (EINVAL);
+ else
+ ifp->if_mtu = ifr->ifr_mtu;
+ return (0);
+ }
+ sx_xlock(&ipsec_ioctl_sx);
+ sc = ifp->if_softc;
+ /* Check that softc is still here */
+ if (sc == NULL) {
+ error = ENXIO;
+ goto bad;
+ }
+ error = 0;
+ switch (cmd) {
+ case SIOCSIFPHYADDR:
+#ifdef INET6
+ case SIOCSIFPHYADDR_IN6:
+#endif
+ error = EINVAL;
+ switch (cmd) {
+#ifdef INET
+ case SIOCSIFPHYADDR:
+ src = (struct sockaddr *)
+ &(((struct in_aliasreq *)data)->ifra_addr);
+ dst = (struct sockaddr *)
+ &(((struct in_aliasreq *)data)->ifra_dstaddr);
+ break;
+#endif
+#ifdef INET6
+ case SIOCSIFPHYADDR_IN6:
+ src = (struct sockaddr *)
+ &(((struct in6_aliasreq *)data)->ifra_addr);
+ dst = (struct sockaddr *)
+ &(((struct in6_aliasreq *)data)->ifra_dstaddr);
+ break;
+#endif
+ default:
+ goto bad;
+ }
+ /* sa_family must be equal */
+ if (src->sa_family != dst->sa_family ||
+ src->sa_len != dst->sa_len)
+ goto bad;
+
+ /* validate sa_len */
+ switch (src->sa_family) {
+#ifdef INET
+ case AF_INET:
+ if (src->sa_len != sizeof(struct sockaddr_in))
+ goto bad;
+ break;
+#endif
+#ifdef INET6
+ case AF_INET6:
+ if (src->sa_len != sizeof(struct sockaddr_in6))
+ goto bad;
+ break;
+#endif
+ default:
+ error = EAFNOSUPPORT;
+ goto bad;
+ }
+ /* check sa_family looks sane for the cmd */
+ error = EAFNOSUPPORT;
+ switch (cmd) {
+#ifdef INET
+ case SIOCSIFPHYADDR:
+ if (src->sa_family == AF_INET)
+ break;
+ goto bad;
+#endif
+#ifdef INET6
+ case SIOCSIFPHYADDR_IN6:
+ if (src->sa_family == AF_INET6)
+ break;
+ goto bad;
+#endif
+ }
+ error = EADDRNOTAVAIL;
+ switch (src->sa_family) {
+#ifdef INET
+ case AF_INET:
+ if (satosin(src)->sin_addr.s_addr == INADDR_ANY ||
+ satosin(dst)->sin_addr.s_addr == INADDR_ANY)
+ goto bad;
+ break;
+#endif
+#ifdef INET6
+ case AF_INET6:
+ if (IN6_IS_ADDR_UNSPECIFIED(&satosin6(src)->sin6_addr)
+ ||
+ IN6_IS_ADDR_UNSPECIFIED(&satosin6(dst)->sin6_addr))
+ goto bad;
+ /*
+ * Check validity of the scope zone ID of the
+ * addresses, and convert it into the kernel
+ * internal form if necessary.
+ */
+ error = sa6_embedscope(satosin6(src), 0);
+ if (error != 0)
+ goto bad;
+ error = sa6_embedscope(satosin6(dst), 0);
+ if (error != 0)
+ goto bad;
+#endif
+ };
+ error = ipsec_set_addresses(ifp, src, dst);
+ break;
+ case SIOCDIFPHYADDR:
+ ipsec_delete_tunnel(ifp, 0);
+ break;
+ case SIOCGIFPSRCADDR:
+ case SIOCGIFPDSTADDR:
+#ifdef INET6
+ case SIOCGIFPSRCADDR_IN6:
+ case SIOCGIFPDSTADDR_IN6:
+#endif
+ IPSEC_RLOCK(sc);
+ if (sc->family == 0) {
+ IPSEC_RUNLOCK(sc);
+ error = EADDRNOTAVAIL;
+ break;
+ }
+ saidx = ipsec_getsaidx(sc, IPSEC_DIR_OUTBOUND, sc->family);
+ switch (cmd) {
+#ifdef INET
+ case SIOCGIFPSRCADDR:
+ case SIOCGIFPDSTADDR:
+ if (saidx->src.sa.sa_family != AF_INET) {
+ error = EADDRNOTAVAIL;
+ break;
+ }
+ sin = (struct sockaddr_in *)&ifr->ifr_addr;
+ memset(sin, 0, sizeof(*sin));
+ sin->sin_family = AF_INET;
+ sin->sin_len = sizeof(*sin);
+ break;
+#endif
+#ifdef INET6
+ case SIOCGIFPSRCADDR_IN6:
+ case SIOCGIFPDSTADDR_IN6:
+ if (saidx->src.sa.sa_family != AF_INET6) {
+ error = EADDRNOTAVAIL;
+ break;
+ }
+ sin6 = (struct sockaddr_in6 *)
+ &(((struct in6_ifreq *)data)->ifr_addr);
+ memset(sin6, 0, sizeof(*sin6));
+ sin6->sin6_family = AF_INET6;
+ sin6->sin6_len = sizeof(*sin6);
+ break;
+#endif
+ default:
+ error = EAFNOSUPPORT;
+ }
+ if (error == 0) {
+ switch (cmd) {
+#ifdef INET
+ case SIOCGIFPSRCADDR:
+ sin->sin_addr = saidx->src.sin.sin_addr;
+ break;
+ case SIOCGIFPDSTADDR:
+ sin->sin_addr = saidx->dst.sin.sin_addr;
+ break;
+#endif
+#ifdef INET6
+ case SIOCGIFPSRCADDR_IN6:
+ sin6->sin6_addr = saidx->src.sin6.sin6_addr;
+ break;
+ case SIOCGIFPDSTADDR_IN6:
+ sin6->sin6_addr = saidx->dst.sin6.sin6_addr;
+ break;
+#endif
+ }
+ }
+ IPSEC_RUNLOCK(sc);
+ if (error != 0)
+ break;
+ switch (cmd) {
+#ifdef INET
+ case SIOCGIFPSRCADDR:
+ case SIOCGIFPDSTADDR:
+ error = prison_if(curthread->td_ucred,
+ (struct sockaddr *)sin);
+ if (error != 0)
+ memset(sin, 0, sizeof(*sin));
+ break;
+#endif
+#ifdef INET6
+ case SIOCGIFPSRCADDR_IN6:
+ case SIOCGIFPDSTADDR_IN6:
+ error = prison_if(curthread->td_ucred,
+ (struct sockaddr *)sin6);
+ if (error == 0)
+ error = sa6_recoverscope(sin6);
+ if (error != 0)
+ memset(sin6, 0, sizeof(*sin6));
+#endif
+ }
+ break;
+ case SIOCGTUNFIB:
+ ifr->ifr_fib = sc->fibnum;
+ break;
+ case SIOCSTUNFIB:
+ if ((error = priv_check(curthread, PRIV_NET_SETIFFIB)) != 0)
+ break;
+ if (ifr->ifr_fib >= rt_numfibs)
+ error = EINVAL;
+ else
+ sc->fibnum = ifr->ifr_fib;
+ break;
+ case IPSECGREQID:
+ reqid = sc->reqid;
+ error = copyout(&reqid, ifr->ifr_data, sizeof(reqid));
+ break;
+ case IPSECSREQID:
+ if ((error = priv_check(curthread, PRIV_NET_SETIFCAP)) != 0)
+ break;
+ error = copyin(ifr->ifr_data, &reqid, sizeof(reqid));
+ if (error != 0)
+ break;
+ error = ipsec_set_reqid(ifp, reqid);
+ break;
+ default:
+ error = EINVAL;
+ break;
+ }
+bad:
+ sx_xunlock(&ipsec_ioctl_sx);
+ return (error);
+}
+
+/*
+ * Allocate new private security policies for tunneling interface.
+ * Each tunneling interface has following security policies for
+ * both AF:
+ * 0.0.0.0/0[any] 0.0.0.0/0[any] -P in \
+ * ipsec esp/tunnel/RemoteIP-LocalIP/unique:reqid
+ * 0.0.0.0/0[any] 0.0.0.0/0[any] -P out \
+ * ipsec esp/tunnel/LocalIP-RemoteIP/unique:reqid
+ */
+static int
+ipsec_newpolicies(struct ipsec_softc *sc, struct secpolicy *sp[IPSEC_SPCOUNT],
+ const struct sockaddr *src, const struct sockaddr *dst, uint32_t reqid)
+{
+ struct ipsecrequest *isr;
+ int i;
+
+ memset(sp, 0, sizeof(struct secpolicy *) * IPSEC_SPCOUNT);
+ for (i = 0; i < IPSEC_SPCOUNT; i++) {
+ if ((sp[i] = key_newsp()) == NULL)
+ goto fail;
+ if ((isr = ipsec_newisr()) == NULL)
+ goto fail;
+
+ sp[i]->policy = IPSEC_POLICY_IPSEC;
+ sp[i]->state = IPSEC_SPSTATE_DEAD;
+ sp[i]->req[sp[i]->tcount++] = isr;
+ sp[i]->created = time_second;
+ /* Use priority field to store if_index */
+ sp[i]->priority = sc->ifp->if_index;
+ isr->level = IPSEC_LEVEL_UNIQUE;
+ isr->saidx.proto = IPPROTO_ESP;
+ isr->saidx.mode = IPSEC_MODE_TUNNEL;
+ isr->saidx.reqid = reqid;
+ if (i % 2 == 0) {
+ sp[i]->spidx.dir = IPSEC_DIR_INBOUND;
+ bcopy(src, &isr->saidx.dst, src->sa_len);
+ bcopy(dst, &isr->saidx.src, dst->sa_len);
+ } else {
+ sp[i]->spidx.dir = IPSEC_DIR_OUTBOUND;
+ bcopy(src, &isr->saidx.src, src->sa_len);
+ bcopy(dst, &isr->saidx.dst, dst->sa_len);
+ }
+ sp[i]->spidx.ul_proto = IPSEC_ULPROTO_ANY;
+#ifdef INET
+ if (i < 2) {
+ sp[i]->spidx.src.sa.sa_family =
+ sp[i]->spidx.dst.sa.sa_family = AF_INET;
+ sp[i]->spidx.src.sa.sa_len =
+ sp[i]->spidx.dst.sa.sa_len =
+ sizeof(struct sockaddr_in);
+ continue;
+ }
+#endif
+#ifdef INET6
+ sp[i]->spidx.src.sa.sa_family =
+ sp[i]->spidx.dst.sa.sa_family = AF_INET6;
+ sp[i]->spidx.src.sa.sa_len =
+ sp[i]->spidx.dst.sa.sa_len = sizeof(struct sockaddr_in6);
+#endif
+ }
+ return (0);
+fail:
+ for (i = 0; i < IPSEC_SPCOUNT; i++)
+ key_freesp(&sp[i]);
+ return (ENOMEM);
+}
+
+static int
+ipsec_check_reqid(uint32_t reqid)
+{
+ struct ipsec_softc *sc;
+
+ IPSEC_SC_RLOCK_ASSERT();
+ LIST_FOREACH(sc, &V_ipsec_sc_list, chain) {
+ if (sc->reqid == reqid)
+ return (EEXIST);
+ }
+ return (0);
+}
+
+/*
+ * We use key_newreqid() to automatically obtain unique reqid.
+ * Then we check that given id is unique, i.e. it is not used by
+ * another if_ipsec(4) interface. This macro limits the number of
+ * tries to get unique id.
+ */
+#define IPSEC_REQID_TRYCNT 64
+static int
+ipsec_init_reqid(struct ipsec_softc *sc)
+{
+ uint32_t reqid;
+ int trycount;
+
+ IPSEC_SC_RLOCK_ASSERT();
+
+ if (sc->reqid != 0) /* already initialized */
+ return (0);
+
+ trycount = IPSEC_REQID_TRYCNT;
+ while (--trycount > 0) {
+ reqid = key_newreqid();
+ if (ipsec_check_reqid(reqid) == 0)
+ break;
+ }
+ if (trycount == 0)
+ return (EEXIST);
+ sc->reqid = reqid;
+ return (0);
+}
+
+/*
+ * Set or update reqid for given tunneling interface.
+ * When specified reqid is zero, generate new one.
+ * We are protected by ioctl_sx lock from concurrent id generation.
+ * Also softc would not disappear while we hold ioctl_sx lock.
+ */
+static int
+ipsec_set_reqid(struct ifnet *ifp, uint32_t reqid)
+{
+ IPSEC_SC_RLOCK_TRACKER;
+ struct ipsec_softc *sc;
+ struct secasindex *saidx;
+
+ sx_assert(&ipsec_ioctl_sx, SA_XLOCKED);
+
+ sc = ifp->if_softc;
+ if (sc->reqid == reqid && reqid != 0)
+ return (0);
+
+ IPSEC_SC_RLOCK();
+ if (reqid != 0) {
+ /* Check that specified reqid doesn't exist */
+ if (ipsec_check_reqid(reqid) != 0) {
+ IPSEC_SC_RUNLOCK();
+ return (EEXIST);
+ }
+ sc->reqid = reqid;
+ } else {
+ /* Generate new reqid */
+ if (ipsec_init_reqid(sc) != 0) {
+ IPSEC_SC_RUNLOCK();
+ return (EEXIST);
+ }
+ }
+ IPSEC_SC_RUNLOCK();
+
+ /* Tunnel isn't fully configured, just return. */
+ if (sc->family == 0)
+ return (0);
+
+ saidx = ipsec_getsaidx(sc, IPSEC_DIR_OUTBOUND, sc->family);
+ KASSERT(saidx != NULL,
+ ("saidx is NULL, but family is %d", sc->family));
+ return (ipsec_set_tunnel(sc, &saidx->src.sa, &saidx->dst.sa,
+ sc->reqid));
+}
+
+/*
+ * Set tunnel endpoints addresses.
+ */
+static int
+ipsec_set_addresses(struct ifnet *ifp, struct sockaddr *src,
+ struct sockaddr *dst)
+{
+ IPSEC_SC_RLOCK_TRACKER;
+ struct ipsec_softc *sc, *tsc;
+ struct secasindex *saidx;
+
+ sx_assert(&ipsec_ioctl_sx, SA_XLOCKED);
+
+ sc = ifp->if_softc;
+ if (sc->family != 0) {
+ saidx = ipsec_getsaidx(sc, IPSEC_DIR_OUTBOUND,
+ src->sa_family);
+ if (saidx != NULL && saidx->reqid == sc->reqid &&
+ key_sockaddrcmp(&saidx->src.sa, src, 0) == 0 &&
+ key_sockaddrcmp(&saidx->dst.sa, dst, 0) == 0)
+ return (0); /* Nothing has been changed. */
+
+ }
+ /*
+ * We cannot service IPsec tunnel when source address is
+ * not our own.
+ */
+#ifdef INET
+ if (src->sa_family == AF_INET &&
+ in_localip(satosin(src)->sin_addr) == 0)
+ return (EADDRNOTAVAIL);
+#endif
+#ifdef INET6
+ /*
+ * NOTE: IPv6 addresses are in kernel internal form with
+ * embedded scope zone id.
+ */
+ if (src->sa_family == AF_INET6 &&
+ in6_localip(&satosin6(src)->sin6_addr) == 0)
+ return (EADDRNOTAVAIL);
+#endif
+ /* Check that given addresses aren't already configured */
+ IPSEC_SC_RLOCK();
+ LIST_FOREACH(tsc, &V_ipsec_sc_list, chain) {
+ if (tsc == sc || tsc->family != src->sa_family)
+ continue;
+ saidx = ipsec_getsaidx(tsc, IPSEC_DIR_OUTBOUND, tsc->family);
+ if (key_sockaddrcmp(&saidx->src.sa, src, 0) == 0 &&
+ key_sockaddrcmp(&saidx->dst.sa, dst, 0) == 0) {
+ /* We already have tunnel with such addresses */
+ IPSEC_SC_RUNLOCK();
+ return (EADDRNOTAVAIL);
+ }
+ }
+ /* If reqid is not set, generate new one. */
+ if (ipsec_init_reqid(sc) != 0) {
+ IPSEC_SC_RUNLOCK();
+ return (EEXIST);
+ }
+ IPSEC_SC_RUNLOCK();
+ return (ipsec_set_tunnel(sc, src, dst, sc->reqid));
+}
+
+static int
+ipsec_set_tunnel(struct ipsec_softc *sc, struct sockaddr *src,
+ struct sockaddr *dst, uint32_t reqid)
+{
+ struct secpolicy *sp[IPSEC_SPCOUNT];
+ struct secpolicy *oldsp[IPSEC_SPCOUNT];
+ int i, f;
+
+ sx_assert(&ipsec_ioctl_sx, SA_XLOCKED);
+
+ /* Allocate SP with new addresses. */
+ if (ipsec_newpolicies(sc, sp, src, dst, reqid) == 0) {
+ /* Add new policies to SPDB */
+ if (key_register_ifnet(sp, IPSEC_SPCOUNT) != 0) {
+ for (i = 0; i < IPSEC_SPCOUNT; i++)
+ key_freesp(&sp[i]);
+ return (EAGAIN);
+ }
+ IPSEC_SC_WLOCK();
+ if ((f = sc->family) != 0)
+ LIST_REMOVE(sc, hash);
+ IPSEC_WLOCK(sc);
+ for (i = 0; i < IPSEC_SPCOUNT; i++) {
+ oldsp[i] = sc->sp[i];
+ sc->sp[i] = sp[i];
+ }
+ sc->family = src->sa_family;
+ IPSEC_WUNLOCK(sc);
+ LIST_INSERT_HEAD(SCHASH_HASH(sc->reqid), sc, hash);
+ IPSEC_SC_WUNLOCK();
+ } else {
+ sc->ifp->if_drv_flags &= ~IFF_DRV_RUNNING;
+ return (ENOMEM);
+ }
+
+ sc->ifp->if_drv_flags |= IFF_DRV_RUNNING;
+ if (f != 0) {
+ key_unregister_ifnet(oldsp, IPSEC_SPCOUNT);
+ for (i = 0; i < IPSEC_SPCOUNT; i++)
+ key_freesp(&oldsp[i]);
+ }
+ return (0);
+}
+
+static void
+ipsec_delete_tunnel(struct ifnet *ifp, int locked)
+{
+ struct ipsec_softc *sc = ifp->if_softc;
+ struct secpolicy *oldsp[IPSEC_SPCOUNT];
+ int i;
+
+ sx_assert(&ipsec_ioctl_sx, SA_XLOCKED);
+
+ ifp->if_drv_flags &= ~IFF_DRV_RUNNING;
+ if (sc->family != 0) {
+ if (!locked)
+ IPSEC_SC_WLOCK();
+ /* Remove from hash table */
+ LIST_REMOVE(sc, hash);
+ IPSEC_WLOCK(sc);
+ for (i = 0; i < IPSEC_SPCOUNT; i++) {
+ oldsp[i] = sc->sp[i];
+ sc->sp[i] = NULL;
+ }
+ sc->family = 0;
+ IPSEC_WUNLOCK(sc);
+ if (!locked)
+ IPSEC_SC_WUNLOCK();
+ key_unregister_ifnet(oldsp, IPSEC_SPCOUNT);
+ for (i = 0; i < IPSEC_SPCOUNT; i++)
+ key_freesp(&oldsp[i]);
+ }
+}
diff --git a/sys/net/if_ipsec.h b/sys/net/if_ipsec.h
new file mode 100644
index 0000000..e7c8e7b
--- /dev/null
+++ b/sys/net/if_ipsec.h
@@ -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_ */
diff --git a/sys/net/pfkeyv2.h b/sys/net/pfkeyv2.h
index c9b2769..c2cf568 100644
--- a/sys/net/pfkeyv2.h
+++ b/sys/net/pfkeyv2.h
@@ -223,9 +223,12 @@ struct sadb_x_policy {
u_int16_t sadb_x_policy_exttype;
u_int16_t sadb_x_policy_type; /* See policy type of ipsec.h */
u_int8_t sadb_x_policy_dir; /* direction, see ipsec.h */
- u_int8_t sadb_x_policy_reserved;
+ u_int8_t sadb_x_policy_scope; /* scope, see ipsec.h */
u_int32_t sadb_x_policy_id;
u_int32_t sadb_x_policy_priority;
+#define sadb_x_policy_reserved sadb_x_policy_scope
+/* Policy with ifnet scope uses priority field to store ifindex */
+#define sadb_x_policy_ifindex sadb_x_policy_priority
};
_Static_assert(sizeof(struct sadb_x_policy) == 16, "struct size mismatch");
@@ -283,6 +286,14 @@ struct sadb_x_nat_t_frag {
};
_Static_assert(sizeof(struct sadb_x_nat_t_frag) == 8, "struct size mismatch");
+/* Additional large replay window support
+ */
+struct sadb_x_sa_replay {
+ u_int16_t sadb_x_sa_replay_len;
+ u_int16_t sadb_x_sa_replay_exttype;
+ u_int32_t sadb_x_sa_replay_replay; /* in packets */
+};
+_Static_assert(sizeof(struct sadb_x_sa_replay) == 8, "struct size mismatch");
#define SADB_EXT_RESERVED 0
#define SADB_EXT_SA 1
@@ -311,7 +322,10 @@ _Static_assert(sizeof(struct sadb_x_nat_t_frag) == 8, "struct size mismatch");
#define SADB_X_EXT_NAT_T_OAI 23 /* Peer's NAT_OA for src of SA. */
#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_EXT_MAX 25
+#define SADB_X_EXT_SA_REPLAY 26 /* Replay window override. */
+#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
diff --git a/sys/netinet/in_pcb.c b/sys/netinet/in_pcb.c
index 7d5da0e..7eb48af 100644
--- a/sys/netinet/in_pcb.c
+++ b/sys/netinet/in_pcb.c
@@ -96,11 +96,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>
@@ -303,8 +299,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);
@@ -331,7 +327,7 @@ in_pcballoc(struct socket *so, struct inpcbinfo *pcbinfo)
inp->inp_gencnt = ++pcbinfo->ipi_gencnt;
refcount_init(&inp->inp_refcount, 1); /* Reference from inpcbinfo */
INP_LIST_WUNLOCK(pcbinfo);
-#if defined(IPSEC) || defined(MAC)
+#if defined(IPSEC) || defined(IPSEC_SUPPORT) || defined(MAC)
out:
if (error != 0) {
crfree(inp->inp_cred);
@@ -1278,7 +1274,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
diff --git a/sys/netinet/in_proto.c b/sys/netinet/in_proto.c
index 7403ec0..363716f 100644
--- a/sys/netinet/in_proto.c
+++ b/sys/netinet/in_proto.c
@@ -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>
@@ -152,7 +148,7 @@ struct protosw inetsw[] = {
.pr_type = SOCK_SEQPACKET,
.pr_domain = &inetdomain,
.pr_protocol = IPPROTO_SCTP,
- .pr_flags = PR_WANTRCVD,
+ .pr_flags = PR_WANTRCVD|PR_LASTHDR,
.pr_input = sctp_input,
.pr_ctlinput = sctp_ctlinput,
.pr_ctloutput = sctp_ctloutput,
@@ -164,7 +160,7 @@ struct protosw inetsw[] = {
.pr_type = SOCK_STREAM,
.pr_domain = &inetdomain,
.pr_protocol = IPPROTO_SCTP,
- .pr_flags = PR_CONNREQUIRED|PR_WANTRCVD,
+ .pr_flags = PR_CONNREQUIRED|PR_WANTRCVD|PR_LASTHDR,
.pr_input = sctp_input,
.pr_ctlinput = sctp_ctlinput,
.pr_ctloutput = sctp_ctloutput,
@@ -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");
diff --git a/sys/netinet/ip_input.c b/sys/netinet/ip_input.c
index 73a155d..9f2b7df 100644
--- a/sys/netinet/ip_input.c
+++ b/sys/netinet/ip_input.c
@@ -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 @@ tooshort:
* 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 @@ tooshort:
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 @@ ours:
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);
- return;
- }
-#endif /* IPSEC */
+ if (
#ifdef IPSTEALTH
- if (!V_ipstealth) {
+ V_ipstealth == 0 &&
#endif
- if (ip->ip_ttl <= IPTTLDEC) {
- icmp_error(m, ICMP_TIMXCEED, ICMP_TIMXCEED_INTRANS,
- 0, 0);
- return;
- }
-#ifdef IPSTEALTH
+ ip->ip_ttl <= IPTTLDEC) {
+ icmp_error(m, ICMP_TIMXCEED, ICMP_TIMXCEED_INTRANS, 0, 0);
+ return;
}
-#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.
diff --git a/sys/netinet/ip_ipsec.c b/sys/netinet/ip_ipsec.c
deleted file mode 100644
index ac1e3d5..0000000
--- a/sys/netinet/ip_ipsec.c
+++ /dev/null
@@ -1,249 +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[];
-
-#ifdef IPSEC_FILTERTUNNEL
-static VNET_DEFINE(int, ip4_ipsec_filtertunnel) = 1;
-#else
-static VNET_DEFINE(int, ip4_ipsec_filtertunnel) = 0;
-#endif
-#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;
-}
diff --git a/sys/netinet/ip_ipsec.h b/sys/netinet/ip_ipsec.h
deleted file mode 100644
index f499b74..0000000
--- a/sys/netinet/ip_ipsec.h
+++ /dev/null
@@ -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
diff --git a/sys/netinet/ip_output.c b/sys/netinet/ip_output.c
index b1564a2..7b59b72 100644
--- a/sys/netinet/ip_output.c
+++ b/sys/netinet/ip_output.c
@@ -83,10 +83,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>
@@ -227,7 +224,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);
@@ -382,7 +379,7 @@ again:
(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.
@@ -552,15 +549,13 @@ again:
}
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.
@@ -1190,23 +1185,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:
@@ -1349,24 +1334,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:
diff --git a/sys/netinet/raw_ip.c b/sys/netinet/raw_ip.c
index 6d40954..661bbf4 100644
--- a/sys/netinet/raw_ip.c
+++ b/sys/netinet/raw_ip.c
@@ -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
diff --git a/sys/netinet/sctp_input.c b/sys/netinet/sctp_input.c
index 5aa56ba..956b159 100644
--- a/sys/netinet/sctp_input.c
+++ b/sys/netinet/sctp_input.c
@@ -5790,34 +5790,6 @@ sctp_common_input_processing(struct mbuf **mm, int iphlen, int offset, int lengt
} else if (stcb == NULL) {
inp_decr = inp;
}
-#ifdef IPSEC
- /*-
- * I very much doubt any of the IPSEC stuff will work but I have no
- * idea, so I will leave it in place.
- */
- if (inp != NULL) {
- 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;
- }
- break;
-#endif
-#ifdef INET6
- case AF_INET6:
- if (ipsec6_in_reject(m, &inp->ip_inp.inp)) {
- SCTP_STAT_INCR(sctps_hdrops);
- goto out;
- }
- break;
-#endif
- default:
- break;
- }
- }
-#endif
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) {
diff --git a/sys/netinet/sctp_os_bsd.h b/sys/netinet/sctp_os_bsd.h
index b0c92da..b439b59 100644
--- a/sys/netinet/sctp_os_bsd.h
+++ b/sys/netinet/sctp_os_bsd.h
@@ -38,7 +38,6 @@ __FBSDID("$FreeBSD$");
/*
* includes
*/
-#include "opt_ipsec.h"
#include "opt_compat.h"
#include "opt_inet6.h"
#include "opt_inet.h"
@@ -82,16 +81,8 @@ __FBSDID("$FreeBSD$");
#include <netinet/ip_icmp.h>
#include <netinet/icmp_var.h>
-#ifdef IPSEC
-#include <netipsec/ipsec.h>
-#include <netipsec/key.h>
-#endif /* IPSEC */
-
#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>
@@ -100,7 +91,6 @@ __FBSDID("$FreeBSD$");
#include <netinet6/scope6_var.h>
#endif /* INET6 */
-
#include <netinet/ip_options.h>
#include <crypto/sha1.h>
diff --git a/sys/netinet/sctp_pcb.c b/sys/netinet/sctp_pcb.c
index ffc3a7e..db3f39c 100644
--- a/sys/netinet/sctp_pcb.c
+++ b/sys/netinet/sctp_pcb.c
@@ -2469,15 +2469,6 @@ 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 (error != 0) {
- crfree(inp->ip_inp.inp.inp_cred);
- SCTP_ZONE_FREE(SCTP_BASE_INFO(ipi_zone_ep), inp);
- SCTP_INP_INFO_WUNLOCK();
- return error;
- }
-#endif /* IPSEC */
SCTP_INCR_EP_COUNT();
inp->ip_inp.inp.inp_ip_ttl = MODULE_GLOBAL(ip_defttl);
SCTP_INP_INFO_WUNLOCK();
@@ -2504,9 +2495,6 @@ 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
- ipsec_delete_pcbpolicy(&inp->ip_inp.inp);
-#endif
SCTP_ZONE_FREE(SCTP_BASE_INFO(ipi_zone_ep), inp);
return (EOPNOTSUPP);
}
@@ -2527,9 +2515,6 @@ 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
- ipsec_delete_pcbpolicy(&inp->ip_inp.inp);
-#endif
SCTP_ZONE_FREE(SCTP_BASE_INFO(ipi_zone_ep), inp);
return (ENOBUFS);
}
@@ -3641,9 +3626,6 @@ 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
- ipsec_delete_pcbpolicy(ip_pcb);
-#endif
if (ip_pcb->inp_options) {
(void)sctp_m_free(ip_pcb->inp_options);
ip_pcb->inp_options = 0;
diff --git a/sys/netinet/tcp_input.c b/sys/netinet/tcp_input.c
index de71c31..3c6e604 100644
--- a/sys/netinet/tcp_input.c
+++ b/sys/netinet/tcp_input.c
@@ -118,10 +118,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>
@@ -474,20 +471,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:
@@ -598,9 +581,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
@@ -931,15 +911,22 @@ findpcb:
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)) {
+ if (isipv6 && IPSEC_ENABLED(ipv6) &&
+ IPSEC_CHECK_POLICY(ipv6, m, inp) != 0) {
goto dropunlock;
- } else
+ }
+#ifdef INET
+ else
+#endif
#endif /* INET6 */
- if (ipsec4_in_reject(m, inp) != 0) {
+#ifdef INET
+ if (IPSEC_ENABLED(ipv4) &&
+ IPSEC_CHECK_POLICY(ipv4, m, inp) != 0) {
goto dropunlock;
}
+#endif /* INET */
#endif /* IPSEC */
/*
@@ -1122,7 +1109,16 @@ relocked:
* 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.
@@ -1174,26 +1170,6 @@ new_tfo_socket:
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
@@ -1423,26 +1399,18 @@ new_tfo_socket:
*/
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, mtod(m, const char *), tp, th);
/*
@@ -1617,6 +1585,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
@@ -3378,20 +3353,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;
diff --git a/sys/netinet/tcp_output.c b/sys/netinet/tcp_output.c
index 6519f99..adb46d3 100644
--- a/sys/netinet/tcp_output.c
+++ b/sys/netinet/tcp_output.c
@@ -88,9 +88,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>
@@ -193,7 +191,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;
@@ -545,14 +543,23 @@ after_sack_rexmit:
* 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);
@@ -563,7 +570,7 @@ after_sack_rexmit:
offsetof(struct ipoption, ipopt_list);
else
ipoptlen = 0;
-#ifdef IPSEC
+#if defined(IPSEC) || defined(IPSEC_SUPPORT)
ipoptlen += ipsec_optlen;
#endif
@@ -831,8 +838,12 @@ send:
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 */
@@ -1245,20 +1256,31 @@ send:
*/
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) {
/*
@@ -1296,7 +1318,7 @@ send:
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: %ld + %u + %u - %u != %u",
__func__, len, hdrlen, ipoptlen, ipsec_optlen, m_length(m, NULL)));
@@ -1553,6 +1575,9 @@ timer:
}
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);
@@ -1720,7 +1745,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;
@@ -1729,8 +1753,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;
@@ -1739,7 +1765,6 @@ tcp_addoptions(struct tcpopt *to, u_char *optp)
*optp++ = 0;
break;
}
-#endif
case TOF_SACK:
{
int sackblks = 0;
diff --git a/sys/netinet/tcp_stacks/fastpath.c b/sys/netinet/tcp_stacks/fastpath.c
index 4b1bfdc..b1c1507 100644
--- a/sys/netinet/tcp_stacks/fastpath.c
+++ b/sys/netinet/tcp_stacks/fastpath.c
@@ -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>
@@ -113,11 +112,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>
diff --git a/sys/netinet/tcp_subr.c b/sys/netinet/tcp_subr.c
index 662c435..4fd388b 100644
--- a/sys/netinet/tcp_subr.c
+++ b/sys/netinet/tcp_subr.c
@@ -114,15 +114,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>
@@ -229,12 +221,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)
@@ -1049,12 +1035,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);
@@ -1110,10 +1095,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
@@ -2483,7 +2471,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
@@ -2499,7 +2487,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
@@ -2511,343 +2499,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)
{
diff --git a/sys/netinet/tcp_syncache.c b/sys/netinet/tcp_syncache.c
index ee9bf95..2c24f0e 100644
--- a/sys/netinet/tcp_syncache.c
+++ b/sys/netinet/tcp_syncache.c
@@ -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>
@@ -736,11 +730,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);
@@ -822,6 +811,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);
@@ -872,7 +866,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
@@ -996,7 +990,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.
*
@@ -1262,6 +1306,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.
@@ -1437,15 +1497,15 @@ skip_alloc:
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)
@@ -1527,10 +1587,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) :
@@ -1639,32 +1695,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;
@@ -1680,18 +1714,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;
diff --git a/sys/netinet/tcp_usrreq.c b/sys/netinet/tcp_usrreq.c
index e7187fc..9a53b93 100644
--- a/sys/netinet/tcp_usrreq.c
+++ b/sys/netinet/tcp_usrreq.c
@@ -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.
@@ -1533,21 +1535,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:
@@ -1773,11 +1771,13 @@ unlock_and_done:
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
diff --git a/sys/netinet/tcp_var.h b/sys/netinet/tcp_var.h
index 7adb717..f65c2fb 100644
--- a/sys/netinet/tcp_var.h
+++ b/sys/netinet/tcp_var.h
@@ -344,21 +344,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
*/
@@ -595,7 +580,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 */
@@ -812,17 +797,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 *);
@@ -866,7 +840,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)
{
@@ -876,7 +849,6 @@ tcp_fields_to_net(struct tcphdr *th)
th->th_win = htons(th->th_win);
th->th_urp = htons(th->th_urp);
}
-#endif
#endif /* _KERNEL */
#endif /* _NETINET_TCP_VAR_H_ */
diff --git a/sys/netinet/udp.h b/sys/netinet/udp.h
index c2d638d..d8475720 100644
--- a/sys/netinet/udp.h
+++ b/sys/netinet/udp.h
@@ -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
diff --git a/sys/netinet/udp_usrreq.c b/sys/netinet/udp_usrreq.c
index 3bf5c6f..c771201 100644
--- a/sys/netinet/udp_usrreq.c
+++ b/sys/netinet/udp_usrreq.c
@@ -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>
@@ -166,15 +163,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)
{
@@ -333,21 +321,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) {
@@ -1013,42 +998,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) {
@@ -1085,15 +1045,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) {
@@ -1576,142 +1538,6 @@ release:
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)
{
diff --git a/sys/netinet6/in6.h b/sys/netinet6/in6.h
index 62c5e0b..e913c0a 100644
--- a/sys/netinet6/in6.h
+++ b/sys/netinet6/in6.h
@@ -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 */
diff --git a/sys/netinet6/in6_proto.c b/sys/netinet6/in6_proto.c
index 0678b4c..44ea1af 100644
--- a/sys/netinet6/in6_proto.c
+++ b/sys/netinet6/in6_proto.c
@@ -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>
/*
@@ -190,7 +185,7 @@ struct protosw inet6sw[] = {
.pr_type = SOCK_SEQPACKET,
.pr_domain = &inet6domain,
.pr_protocol = IPPROTO_SCTP,
- .pr_flags = PR_WANTRCVD,
+ .pr_flags = PR_WANTRCVD|PR_LASTHDR,
.pr_input = sctp6_input,
.pr_ctlinput = sctp6_ctlinput,
.pr_ctloutput = sctp_ctloutput,
@@ -204,7 +199,7 @@ struct protosw inet6sw[] = {
.pr_type = SOCK_STREAM,
.pr_domain = &inet6domain,
.pr_protocol = IPPROTO_SCTP,
- .pr_flags = PR_CONNREQUIRED|PR_WANTRCVD,
+ .pr_flags = PR_CONNREQUIRED|PR_WANTRCVD|PR_LASTHDR,
.pr_input = sctp6_input,
.pr_ctlinput = sctp6_ctlinput,
.pr_ctloutput = sctp_ctloutput,
@@ -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 */
diff --git a/sys/netinet6/ip6_forward.c b/sys/netinet6/ip6_forward.c
index 002c761..bf1f66e 100644
--- a/sys/netinet6/ip6_forward.c
+++ b/sys/netinet6/ip6_forward.c
@@ -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
#ifdef SCTP
int sw_csum;
#endif
@@ -133,32 +125,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 -
@@ -170,167 +147,22 @@ ip6_forward(struct mbuf *m, int srcrt)
* processing may modify the mbuf.
*/
mcopy = m_copy(m, 0, imin(m->m_pkthdr.len, ICMPV6_PLD_MAXLEN));
-
-#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
- m_freem(mcopy);
+#ifdef IPSTEALTH
+ if (V_ip6stealth == 0)
#endif
- }
- m_freem(m);
- return;
- }
-
- error = 0;
+ ip6->ip6_hlim -= IPV6_HLIMDEC;
- /* 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
+#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;
-
- 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));
@@ -542,34 +374,9 @@ pass:
/* 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;
}
diff --git a/sys/netinet6/ip6_input.c b/sys/netinet6/ip6_input.c
index 5ccb989..436efc7 100644
--- a/sys/netinet6/ip6_input.c
+++ b/sys/netinet6/ip6_input.c
@@ -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 @@ passin:
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);
diff --git a/sys/netinet6/ip6_ipsec.c b/sys/netinet6/ip6_ipsec.c
deleted file mode 100644
index a99f1db..0000000
--- a/sys/netinet6/ip6_ipsec.c
+++ /dev/null
@@ -1,297 +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[];
-
-#ifdef IPSEC_FILTERTUNNEL
-static VNET_DEFINE(int, ip6_ipsec6_filtertunnel) = 1;
-#else
-static VNET_DEFINE(int, ip6_ipsec6_filtertunnel) = 0;
-#endif
-#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
diff --git a/sys/netinet6/ip6_ipsec.h b/sys/netinet6/ip6_ipsec.h
deleted file mode 100644
index e335d85..0000000
--- a/sys/netinet6/ip6_ipsec.h
+++ /dev/null
@@ -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
diff --git a/sys/netinet6/ip6_output.c b/sys/netinet6/ip6_output.c
index 2674f8a..0072fd5 100644
--- a/sys/netinet6/ip6_output.c
+++ b/sys/netinet6/ip6_output.c
@@ -107,12 +107,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>
@@ -335,6 +330,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 */
@@ -359,24 +369,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.
@@ -1876,23 +1868,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:
@@ -2117,37 +2099,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)
- break;
- error = soopt_mcopyin(sopt, m); /* XXX */
- if (error != 0)
+ if (IPSEC_ENABLED(ipv6)) {
+ error = IPSEC_PCBCTL(ipv6, in6p, sopt);
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;
diff --git a/sys/netinet6/raw_ip6.c b/sys/netinet6/raw_ip6.c
index 2b95e48..f91eb51 100644
--- a/sys/netinet6/raw_ip6.c
+++ b/sys/netinet6/raw_ip6.c
@@ -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_copy(m, 0, (int)M_COPYALL);
-#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. */
diff --git a/sys/netinet6/sctp6_usrreq.c b/sys/netinet6/sctp6_usrreq.c
index 35ec1f3..c2a45a6 100644
--- a/sys/netinet6/sctp6_usrreq.c
+++ b/sys/netinet6/sctp6_usrreq.c
@@ -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
@@ -556,10 +551,6 @@ sctp6_attach(struct socket *so, int proto SCTP_UNUSED, struct thread *p SCTP_UNU
*/
inp6->inp_ip_ttl = MODULE_GLOBAL(ip_defttl);
#endif
- /*
- * Hmm what about the IPSEC stuff that is missing here but in
- * sctp_attach()?
- */
SCTP_INP_WUNLOCK(inp);
return (0);
}
diff --git a/sys/netinet6/udp6_usrreq.c b/sys/netinet6/udp6_usrreq.c
index b44fb9e..cc10527 100644
--- a/sys/netinet6/udp6_usrreq.c
+++ b/sys/netinet6/udp6_usrreq.c
@@ -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
diff --git a/sys/netipsec/ipsec.c b/sys/netipsec/ipsec.c
index 413455f..e120f65 100644
--- a/sys/netipsec/ipsec.c
+++ b/sys/netipsec/ipsec.c
@@ -88,6 +88,7 @@
#include <netipsec/esp_var.h>
#include <netipsec/ipcomp.h> /*XXX*/
#include <netipsec/ipcomp_var.h>
+#include <netipsec/ipsec_support.h>
#include <netipsec/key.h>
#include <netipsec/keydb.h>
@@ -99,12 +100,6 @@
#include <opencrypto/cryptodev.h>
-#ifdef IPSEC_DEBUG
-VNET_DEFINE(int, ipsec_debug) = 1;
-#else
-VNET_DEFINE(int, ipsec_debug) = 0;
-#endif
-
/* NB: name changed so netstat doesn't use it. */
VNET_PCPUSTAT_DEFINE(struct ipsecstat, ipsec4stat);
VNET_PCPUSTAT_SYSINIT(ipsec4stat);
@@ -124,8 +119,28 @@ VNET_DEFINE(int, ip4_ah_net_deflev) = IPSEC_LEVEL_USE;
VNET_DEFINE(int, ip4_ipsec_ecn) = 0;
VNET_DEFINE(int, ip4_esp_randpad) = -1;
-static VNET_DEFINE(struct secpolicy, def_policy);
+static VNET_DEFINE(int, ip4_filtertunnel) = 0;
+#define V_ip4_filtertunnel VNET(ip4_filtertunnel)
+static VNET_DEFINE(int, check_policy_history) = 0;
+#define V_check_policy_history VNET(check_policy_history)
+static VNET_DEFINE(struct secpolicy *, def_policy) = NULL;
#define V_def_policy VNET(def_policy)
+static int
+sysctl_def_policy(SYSCTL_HANDLER_ARGS)
+{
+ int error, value;
+
+ value = V_def_policy->policy;
+ error = sysctl_handle_int(oidp, &value, 0, req);
+ if (error == 0) {
+ if (value != IPSEC_POLICY_DISCARD &&
+ value != IPSEC_POLICY_NONE)
+ return (EINVAL);
+ V_def_policy->policy = value;
+ }
+ return (error);
+}
+
/*
* Crypto support requirements:
*
@@ -134,17 +149,24 @@ static VNET_DEFINE(struct secpolicy, def_policy);
* 0 take anything
*/
VNET_DEFINE(int, crypto_support) = CRYPTOCAP_F_HARDWARE | CRYPTOCAP_F_SOFTWARE;
+/*
+ * TCP/UDP checksum handling policy for transport mode NAT-T (RFC3948)
+ *
+ * 0 - auto: incrementally recompute, when checksum delta is known;
+ * if checksum delta isn't known, reset checksum to zero for UDP,
+ * and mark csum_flags as valid for TCP.
+ * 1 - fully recompute TCP/UDP checksum.
+ */
+VNET_DEFINE(int, natt_cksum_policy) = 0;
FEATURE(ipsec, "Internet Protocol Security (IPsec)");
-#ifdef IPSEC_NAT_T
FEATURE(ipsec_natt, "UDP Encapsulation of IPsec ESP Packets ('NAT-T')");
-#endif
SYSCTL_DECL(_net_inet_ipsec);
/* net.inet.ipsec */
-SYSCTL_INT(_net_inet_ipsec, IPSECCTL_DEF_POLICY, def_policy,
- CTLFLAG_VNET | CTLFLAG_RW, &VNET_NAME(def_policy).policy, 0,
+SYSCTL_PROC(_net_inet_ipsec, IPSECCTL_DEF_POLICY, def_policy,
+ CTLTYPE_INT | CTLFLAG_VNET | CTLFLAG_RW, 0, 0, sysctl_def_policy, "I",
"IPsec default policy.");
SYSCTL_INT(_net_inet_ipsec, IPSECCTL_DEF_ESP_TRANSLEV, esp_trans_deflev,
CTLFLAG_VNET | CTLFLAG_RW, &VNET_NAME(ip4_esp_trans_deflev), 0,
@@ -160,22 +182,28 @@ SYSCTL_INT(_net_inet_ipsec, IPSECCTL_DEF_AH_NETLEV, ah_net_deflev,
"AH tunnel mode default level.");
SYSCTL_INT(_net_inet_ipsec, IPSECCTL_AH_CLEARTOS, ah_cleartos,
CTLFLAG_VNET | CTLFLAG_RW, &VNET_NAME(ah_cleartos), 0,
- "If set clear type-of-service field when doing AH computation.");
+ "If set, clear type-of-service field when doing AH computation.");
SYSCTL_INT(_net_inet_ipsec, IPSECCTL_AH_OFFSETMASK, ah_offsetmask,
CTLFLAG_VNET | CTLFLAG_RW, &VNET_NAME(ip4_ah_offsetmask), 0,
- "If not set clear offset field mask when doing AH computation.");
+ "If not set, clear offset field mask when doing AH computation.");
SYSCTL_INT(_net_inet_ipsec, IPSECCTL_DFBIT, dfbit,
CTLFLAG_VNET | CTLFLAG_RW, &VNET_NAME(ip4_ipsec_dfbit), 0,
"Do not fragment bit on encap.");
SYSCTL_INT(_net_inet_ipsec, IPSECCTL_ECN, ecn,
CTLFLAG_VNET | CTLFLAG_RW, &VNET_NAME(ip4_ipsec_ecn), 0,
"Explicit Congestion Notification handling.");
-SYSCTL_INT(_net_inet_ipsec, IPSECCTL_DEBUG, debug,
- CTLFLAG_VNET | CTLFLAG_RW, &VNET_NAME(ipsec_debug), 0,
- "Enable IPsec debugging output when set.");
SYSCTL_INT(_net_inet_ipsec, OID_AUTO, crypto_support,
CTLFLAG_VNET | CTLFLAG_RW, &VNET_NAME(crypto_support), 0,
"Crypto driver selection.");
+SYSCTL_INT(_net_inet_ipsec, OID_AUTO, check_policy_history,
+ CTLFLAG_VNET | CTLFLAG_RW, &VNET_NAME(check_policy_history), 0,
+ "Use strict check of inbound packets to security policy compliance.");
+SYSCTL_INT(_net_inet_ipsec, OID_AUTO, natt_cksum_policy,
+ CTLFLAG_VNET | CTLFLAG_RW, &VNET_NAME(natt_cksum_policy), 0,
+ "Method to fix TCP/UDP checksum for transport mode IPsec after NAT.");
+SYSCTL_INT(_net_inet_ipsec, OID_AUTO, filtertunnel,
+ CTLFLAG_VNET | CTLFLAG_RW, &VNET_NAME(ip4_filtertunnel), 0,
+ "If set, filter packets from an IPsec tunnel.");
SYSCTL_VNET_PCPUSTAT(_net_inet_ipsec, OID_AUTO, ipsecstats, struct ipsecstat,
ipsec4stat, "IPsec IPv4 statistics.");
@@ -212,11 +240,14 @@ VNET_DEFINE(int, ip6_ah_trans_deflev) = IPSEC_LEVEL_USE;
VNET_DEFINE(int, ip6_ah_net_deflev) = IPSEC_LEVEL_USE;
VNET_DEFINE(int, ip6_ipsec_ecn) = 0; /* ECN ignore(-1)/forbidden(0)/allowed(1) */
+static VNET_DEFINE(int, ip6_filtertunnel) = 0;
+#define V_ip6_filtertunnel VNET(ip6_filtertunnel)
+
SYSCTL_DECL(_net_inet6_ipsec6);
/* net.inet6.ipsec6 */
-SYSCTL_INT(_net_inet6_ipsec6, IPSECCTL_DEF_POLICY, def_policy,
- CTLFLAG_VNET | CTLFLAG_RW, &VNET_NAME(def_policy).policy, 0,
+SYSCTL_PROC(_net_inet6_ipsec6, IPSECCTL_DEF_POLICY, def_policy,
+ CTLTYPE_INT | CTLFLAG_VNET | CTLFLAG_RW, 0, 0, sysctl_def_policy, "I",
"IPsec default policy.");
SYSCTL_INT(_net_inet6_ipsec6, IPSECCTL_DEF_ESP_TRANSLEV, esp_trans_deflev,
CTLFLAG_VNET | CTLFLAG_RW, &VNET_NAME(ip6_esp_trans_deflev), 0,
@@ -233,386 +264,228 @@ SYSCTL_INT(_net_inet6_ipsec6, IPSECCTL_DEF_AH_NETLEV, ah_net_deflev,
SYSCTL_INT(_net_inet6_ipsec6, IPSECCTL_ECN, ecn,
CTLFLAG_VNET | CTLFLAG_RW, &VNET_NAME(ip6_ipsec_ecn), 0,
"Explicit Congestion Notification handling.");
-SYSCTL_INT(_net_inet6_ipsec6, IPSECCTL_DEBUG, debug,
- CTLFLAG_VNET | CTLFLAG_RW, &VNET_NAME(ipsec_debug), 0,
- "Enable IPsec debugging output when set.");
+SYSCTL_INT(_net_inet6_ipsec6, OID_AUTO, filtertunnel,
+ CTLFLAG_VNET | CTLFLAG_RW, &VNET_NAME(ip6_filtertunnel), 0,
+ "If set, filter packets from an IPsec tunnel.");
SYSCTL_VNET_PCPUSTAT(_net_inet6_ipsec6, IPSECCTL_STATS, ipsecstats,
struct ipsecstat, ipsec6stat, "IPsec IPv6 statistics.");
#endif /* INET6 */
-static int ipsec_in_reject(struct secpolicy *, const struct mbuf *);
-static int ipsec_setspidx_inpcb(const struct mbuf *, struct inpcb *, u_int);
-static int ipsec_setspidx(const struct mbuf *, struct secpolicyindex *, int);
-static void ipsec4_get_ulp(const struct mbuf *m, struct secpolicyindex *, int);
-static int ipsec4_setspidx_ipaddr(const struct mbuf *, struct secpolicyindex *);
+static int ipsec_in_reject(struct secpolicy *, struct inpcb *,
+ const struct mbuf *);
+
+#ifdef INET
+static void ipsec4_get_ulp(const struct mbuf *, struct secpolicyindex *, int);
+static void ipsec4_setspidx_ipaddr(const struct mbuf *,
+ struct secpolicyindex *);
+#endif
#ifdef INET6
static void ipsec6_get_ulp(const struct mbuf *m, struct secpolicyindex *, int);
-static int ipsec6_setspidx_ipaddr(const struct mbuf *, struct secpolicyindex *);
+static void ipsec6_setspidx_ipaddr(const struct mbuf *,
+ struct secpolicyindex *);
#endif
-static void ipsec_delpcbpolicy(struct inpcbpolicy *);
-static struct secpolicy *ipsec_deepcopy_policy(struct secpolicy *src);
-static void vshiftl(unsigned char *, int, int);
-
-MALLOC_DEFINE(M_IPSEC_INPCB, "inpcbpolicy", "inpcb-resident ipsec policy");
/*
* Return a held reference to the default SP.
*/
static struct secpolicy *
-key_allocsp_default(const char* where, int tag)
+key_allocsp_default(void)
{
- struct secpolicy *sp;
-
- KEYDEBUG(KEYDEBUG_IPSEC_STAMP,
- printf("DP key_allocsp_default from %s:%u\n", where, tag));
- sp = &V_def_policy;
- if (sp->policy != IPSEC_POLICY_DISCARD &&
- sp->policy != IPSEC_POLICY_NONE) {
- ipseclog((LOG_INFO, "fixed system default policy: %d->%d\n",
- sp->policy, IPSEC_POLICY_NONE));
- sp->policy = IPSEC_POLICY_NONE;
- }
- key_addref(sp);
-
- KEYDEBUG(KEYDEBUG_IPSEC_STAMP,
- printf("DP key_allocsp_default returns SP:%p (%u)\n",
- sp, sp->refcnt));
- return (sp);
+ key_addref(V_def_policy);
+ return (V_def_policy);
}
-#define KEY_ALLOCSP_DEFAULT() \
- key_allocsp_default(__FILE__, __LINE__)
-/*
- * For OUTBOUND packet having a socket. Searching SPD for packet,
- * and return a pointer to SP.
- * OUT: NULL: no apropreate SP found, the following value is set to error.
- * 0 : bypass
- * EACCES : discard packet.
- * ENOENT : ipsec_acquire() in progress, maybe.
- * others : error occurred.
- * others: a pointer to SP
- *
- * NOTE: IPv6 mapped adddress concern is implemented here.
- */
-struct secpolicy *
-ipsec_getpolicy(struct tdb_ident *tdbi, u_int dir)
+static void
+ipsec_invalidate_cache(struct inpcb *inp, u_int dir)
{
struct secpolicy *sp;
- IPSEC_ASSERT(tdbi != NULL, ("null tdbi"));
- IPSEC_ASSERT(dir == IPSEC_DIR_INBOUND || dir == IPSEC_DIR_OUTBOUND,
- ("invalid direction %u", dir));
-
- sp = KEY_ALLOCSP2(tdbi->spi, &tdbi->dst, tdbi->proto, dir);
- if (sp == NULL) /*XXX????*/
- sp = KEY_ALLOCSP_DEFAULT();
- IPSEC_ASSERT(sp != NULL, ("null SP"));
- return (sp);
+ INP_WLOCK_ASSERT(inp);
+ if (dir == IPSEC_DIR_OUTBOUND) {
+ if (inp->inp_sp->flags & INP_INBOUND_POLICY)
+ return;
+ sp = inp->inp_sp->sp_in;
+ inp->inp_sp->sp_in = NULL;
+ } else {
+ if (inp->inp_sp->flags & INP_OUTBOUND_POLICY)
+ return;
+ sp = inp->inp_sp->sp_out;
+ inp->inp_sp->sp_out = NULL;
+ }
+ if (sp != NULL)
+ key_freesp(&sp); /* release extra reference */
}
-/*
- * For OUTBOUND packet having a socket. Searching SPD for packet,
- * and return a pointer to SP.
- * OUT: NULL: no apropreate SP found, the following value is set to error.
- * 0 : bypass
- * EACCES : discard packet.
- * ENOENT : ipsec_acquire() in progress, maybe.
- * others : error occurred.
- * others: a pointer to SP
- *
- * NOTE: IPv6 mapped adddress concern is implemented here.
- */
-static struct secpolicy *
-ipsec_getpolicybysock(const struct mbuf *m, u_int dir, struct inpcb *inp,
- int *error)
+static void
+ipsec_cachepolicy(struct inpcb *inp, struct secpolicy *sp, u_int dir)
{
- struct inpcbpolicy *pcbsp;
- struct secpolicy *currsp = NULL; /* Policy on socket. */
- struct secpolicy *sp;
+ uint32_t genid;
+ int downgrade;
- IPSEC_ASSERT(m != NULL, ("null mbuf"));
- IPSEC_ASSERT(inp != NULL, ("null inpcb"));
- IPSEC_ASSERT(error != NULL, ("null error"));
- IPSEC_ASSERT(dir == IPSEC_DIR_INBOUND || dir == IPSEC_DIR_OUTBOUND,
- ("invalid direction %u", dir));
+ INP_LOCK_ASSERT(inp);
- if (!key_havesp(dir)) {
- /* No SP found, use system default. */
- sp = KEY_ALLOCSP_DEFAULT();
- return (sp);
+ if (dir == IPSEC_DIR_OUTBOUND) {
+ /* Do we have configured PCB policy? */
+ if (inp->inp_sp->flags & INP_OUTBOUND_POLICY)
+ return;
+ /* Another thread has already set cached policy */
+ if (inp->inp_sp->sp_out != NULL)
+ return;
+ /*
+ * Do not cache OUTBOUND policy if PCB isn't connected,
+ * i.e. foreign address is INADDR_ANY/UNSPECIFIED.
+ */
+#ifdef INET
+ if ((inp->inp_vflag & INP_IPV4) != 0 &&
+ inp->inp_faddr.s_addr == INADDR_ANY)
+ return;
+#endif
+#ifdef INET6
+ if ((inp->inp_vflag & INP_IPV6) != 0 &&
+ IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_faddr))
+ return;
+#endif
+ } else {
+ /* Do we have configured PCB policy? */
+ if (inp->inp_sp->flags & INP_INBOUND_POLICY)
+ return;
+ /* Another thread has already set cached policy */
+ if (inp->inp_sp->sp_in != NULL)
+ return;
+ /*
+ * Do not cache INBOUND policy for listen socket,
+ * that is bound to INADDR_ANY/UNSPECIFIED address.
+ */
+#ifdef INET
+ if ((inp->inp_vflag & INP_IPV4) != 0 &&
+ inp->inp_faddr.s_addr == INADDR_ANY)
+ return;
+#endif
+#ifdef INET6
+ if ((inp->inp_vflag & INP_IPV6) != 0 &&
+ IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_faddr))
+ return;
+#endif
}
-
- /* Set spidx in pcb. */
- *error = ipsec_setspidx_inpcb(m, inp, dir);
- if (*error)
- return (NULL);
-
- pcbsp = inp->inp_sp;
- IPSEC_ASSERT(pcbsp != NULL, ("null pcbsp"));
- switch (dir) {
- case IPSEC_DIR_INBOUND:
- currsp = pcbsp->sp_in;
- break;
- case IPSEC_DIR_OUTBOUND:
- currsp = pcbsp->sp_out;
- break;
+ downgrade = 0;
+ if (!INP_WLOCKED(inp)) {
+ if ((downgrade = INP_TRY_UPGRADE(inp)) == 0)
+ return;
}
- IPSEC_ASSERT(currsp != NULL, ("null currsp"));
-
- if (pcbsp->priv) { /* When privilieged socket. */
- switch (currsp->policy) {
- case IPSEC_POLICY_BYPASS:
- case IPSEC_POLICY_IPSEC:
- key_addref(currsp);
- sp = currsp;
- break;
-
- case IPSEC_POLICY_ENTRUST:
- /* Look for a policy in SPD. */
- sp = KEY_ALLOCSP(&currsp->spidx, dir);
- if (sp == NULL) /* No SP found. */
- sp = KEY_ALLOCSP_DEFAULT();
- break;
-
- default:
- ipseclog((LOG_ERR, "%s: Invalid policy for PCB %d\n",
- __func__, currsp->policy));
- *error = EINVAL;
- return (NULL);
- }
- } else { /* Unpriv, SPD has policy. */
- sp = KEY_ALLOCSP(&currsp->spidx, dir);
- if (sp == NULL) { /* No SP found. */
- switch (currsp->policy) {
- case IPSEC_POLICY_BYPASS:
- ipseclog((LOG_ERR, "%s: Illegal policy for "
- "non-priviliged defined %d\n",
- __func__, currsp->policy));
- *error = EINVAL;
- return (NULL);
-
- case IPSEC_POLICY_ENTRUST:
- sp = KEY_ALLOCSP_DEFAULT();
- break;
-
- case IPSEC_POLICY_IPSEC:
- key_addref(currsp);
- sp = currsp;
- break;
-
- default:
- ipseclog((LOG_ERR, "%s: Invalid policy for "
- "PCB %d\n", __func__, currsp->policy));
- *error = EINVAL;
- return (NULL);
- }
- }
+ if (dir == IPSEC_DIR_OUTBOUND)
+ inp->inp_sp->sp_out = sp;
+ else
+ inp->inp_sp->sp_in = sp;
+ /*
+ * SP is already referenced by the lookup code.
+ * We take extra reference here to avoid race in the
+ * ipsec_getpcbpolicy() function - SP will not be freed in the
+ * time between we take SP pointer from the cache and key_addref()
+ * call.
+ */
+ key_addref(sp);
+ genid = key_getspgen();
+ if (genid != inp->inp_sp->genid) {
+ ipsec_invalidate_cache(inp, dir);
+ inp->inp_sp->genid = genid;
}
- IPSEC_ASSERT(sp != NULL,
- ("null SP (priv %u policy %u", pcbsp->priv, currsp->policy));
- KEYDEBUG(KEYDEBUG_IPSEC_STAMP,
- printf("DP %s (priv %u policy %u) allocate SP:%p (refcnt %u)\n",
- __func__, pcbsp->priv, currsp->policy, sp, sp->refcnt));
- return (sp);
+ KEYDBG(IPSEC_STAMP,
+ printf("%s: PCB(%p): cached %s SP(%p)\n",
+ __func__, inp, dir == IPSEC_DIR_OUTBOUND ? "OUTBOUND":
+ "INBOUND", sp));
+ if (downgrade != 0)
+ INP_DOWNGRADE(inp);
}
-/*
- * For FORWADING packet or OUTBOUND without a socket. Searching SPD for packet,
- * and return a pointer to SP.
- * OUT: positive: a pointer to the entry for security policy leaf matched.
- * NULL: no apropreate SP found, the following value is set to error.
- * 0 : bypass
- * EACCES : discard packet.
- * ENOENT : ipsec_acquire() in progress, maybe.
- * others : error occurred.
- */
-struct secpolicy *
-ipsec_getpolicybyaddr(const struct mbuf *m, u_int dir, int *error)
+static struct secpolicy *
+ipsec_checkpolicy(struct secpolicy *sp, struct inpcb *inp, int *error)
{
- struct secpolicyindex spidx;
- struct secpolicy *sp;
-
- IPSEC_ASSERT(m != NULL, ("null mbuf"));
- IPSEC_ASSERT(error != NULL, ("null error"));
- IPSEC_ASSERT(dir == IPSEC_DIR_INBOUND || dir == IPSEC_DIR_OUTBOUND,
- ("invalid direction %u", dir));
- sp = NULL;
- *error = 0;
- if (key_havesp(dir)) {
- /* Make an index to look for a policy. */
- *error = ipsec_setspidx(m, &spidx, 0);
- if (*error != 0) {
- DPRINTF(("%s: setpidx failed, dir %u\n",
- __func__, dir));
- return (NULL);
- }
- spidx.dir = dir;
- sp = KEY_ALLOCSP(&spidx, dir);
- }
- if (sp == NULL) /* No SP found, use system default. */
- sp = KEY_ALLOCSP_DEFAULT();
- IPSEC_ASSERT(sp != NULL, ("null SP"));
- return (sp);
-}
-
-struct secpolicy *
-ipsec4_checkpolicy(const struct mbuf *m, u_int dir, int *error,
- struct inpcb *inp)
-{
- struct secpolicy *sp;
+ /* Save found OUTBOUND policy into PCB SP cache. */
+ if (inp != NULL && inp->inp_sp != NULL && inp->inp_sp->sp_out == NULL)
+ ipsec_cachepolicy(inp, sp, IPSEC_DIR_OUTBOUND);
- *error = 0;
- if (inp == NULL)
- sp = ipsec_getpolicybyaddr(m, dir, error);
- else
- sp = ipsec_getpolicybysock(m, dir, inp, error);
- if (sp == NULL) {
- IPSEC_ASSERT(*error != 0, ("getpolicy failed w/o error"));
- IPSECSTAT_INC(ips_out_inval);
- return (NULL);
- }
- IPSEC_ASSERT(*error == 0, ("sp w/ error set to %u", *error));
switch (sp->policy) {
- case IPSEC_POLICY_ENTRUST:
default:
printf("%s: invalid policy %u\n", __func__, sp->policy);
/* FALLTHROUGH */
case IPSEC_POLICY_DISCARD:
- IPSECSTAT_INC(ips_out_polvio);
*error = -EINVAL; /* Packet is discarded by caller. */
- break;
+ /* FALLTHROUGH */
case IPSEC_POLICY_BYPASS:
case IPSEC_POLICY_NONE:
- KEY_FREESP(&sp);
+ key_freesp(&sp);
sp = NULL; /* NB: force NULL result. */
break;
case IPSEC_POLICY_IPSEC:
- if (sp->req == NULL) /* Acquire a SA. */
- *error = key_spdacquire(sp);
+ /* XXXAE: handle LARVAL SP */
break;
}
- if (*error != 0) {
- KEY_FREESP(&sp);
- sp = NULL;
- }
+ KEYDBG(IPSEC_DUMP,
+ printf("%s: get SP(%p), error %d\n", __func__, sp, *error));
return (sp);
}
-static int
-ipsec_setspidx_inpcb(const struct mbuf *m, struct inpcb *inp, u_int dir)
+static struct secpolicy *
+ipsec_getpcbpolicy(struct inpcb *inp, u_int dir)
{
- struct secpolicyindex *spidx;
- int error;
+ struct secpolicy *sp;
+ int flags, downgrade;
- IPSEC_ASSERT(inp != NULL, ("null inp"));
- IPSEC_ASSERT(inp->inp_sp != NULL, ("null inp_sp"));
- IPSEC_ASSERT(inp->inp_sp->sp_out != NULL && inp->inp_sp->sp_in != NULL,
- ("null sp_in || sp_out"));
+ if (inp == NULL || inp->inp_sp == NULL)
+ return (NULL);
- if (dir == IPSEC_DIR_INBOUND)
- spidx = &inp->inp_sp->sp_in->spidx;
- else
- spidx = &inp->inp_sp->sp_out->spidx;
- error = ipsec_setspidx(m, spidx, 1);
- if (error == 0) {
- spidx->dir = dir;
+ INP_LOCK_ASSERT(inp);
+
+ flags = inp->inp_sp->flags;
+ if (dir == IPSEC_DIR_OUTBOUND) {
+ sp = inp->inp_sp->sp_out;
+ flags &= INP_OUTBOUND_POLICY;
} else {
- bzero(&inp->inp_sp->sp_in->spidx,
- sizeof (inp->inp_sp->sp_in->spidx));
- bzero(&inp->inp_sp->sp_out->spidx,
- sizeof (inp->inp_sp->sp_in->spidx));
+ sp = inp->inp_sp->sp_in;
+ flags &= INP_INBOUND_POLICY;
}
- return (error);
-}
-
-/*
- * Configure security policy index (src/dst/proto/sport/dport)
- * by looking at the content of mbuf.
- * The caller is responsible for error recovery (like clearing up spidx).
- */
-static int
-ipsec_setspidx(const struct mbuf *m, struct secpolicyindex *spidx,
- int needport)
-{
- struct ip ipbuf;
- const struct ip *ip = NULL;
- const struct mbuf *n;
- u_int v;
- int len;
- int error;
-
- IPSEC_ASSERT(m != NULL, ("null mbuf"));
-
/*
- * Validate m->m_pkthdr.len. We see incorrect length if we
- * mistakenly call this function with inconsistent mbuf chain
- * (like 4.4BSD tcp/udp processing). XXX Should we panic here?
+ * Check flags. If we have PCB SP, just return it.
+ * Otherwise we need to check that cached SP entry isn't stale.
*/
- len = 0;
- for (n = m; n; n = n->m_next)
- len += n->m_len;
- if (m->m_pkthdr.len != len) {
- KEYDEBUG(KEYDEBUG_IPSEC_DUMP,
- printf("%s: pkthdr len(%d) mismatch (%d), ignored.\n",
- __func__, len, m->m_pkthdr.len));
- return (EINVAL);
- }
-
- if (m->m_pkthdr.len < sizeof(struct ip)) {
- KEYDEBUG(KEYDEBUG_IPSEC_DUMP,
- printf("%s: pkthdr len(%d) too small (v4), ignored.\n",
- __func__, m->m_pkthdr.len));
- return (EINVAL);
- }
-
- if (m->m_len >= sizeof(*ip))
- ip = mtod(m, const struct ip *);
- else {
- m_copydata(m, 0, sizeof(ipbuf), (caddr_t)&ipbuf);
- ip = &ipbuf;
- }
- v = ip->ip_v;
- switch (v) {
- case 4:
- error = ipsec4_setspidx_ipaddr(m, spidx);
- if (error)
- return (error);
- ipsec4_get_ulp(m, spidx, needport);
- return (0);
-#ifdef INET6
- case 6:
- if (m->m_pkthdr.len < sizeof(struct ip6_hdr)) {
- KEYDEBUG(KEYDEBUG_IPSEC_DUMP,
- printf("%s: pkthdr len(%d) too small (v6), "
- "ignored\n", __func__, m->m_pkthdr.len));
- return (EINVAL);
+ if (flags == 0) {
+ if (sp == NULL)
+ return (NULL);
+ if (inp->inp_sp->genid != key_getspgen()) {
+ /* Invalidate the cache. */
+ downgrade = 0;
+ if (!INP_WLOCKED(inp)) {
+ if ((downgrade = INP_TRY_UPGRADE(inp)) == 0)
+ return (NULL);
+ }
+ ipsec_invalidate_cache(inp, IPSEC_DIR_OUTBOUND);
+ ipsec_invalidate_cache(inp, IPSEC_DIR_INBOUND);
+ if (downgrade != 0)
+ INP_DOWNGRADE(inp);
+ return (NULL);
}
- error = ipsec6_setspidx_ipaddr(m, spidx);
- if (error)
- return (error);
- ipsec6_get_ulp(m, spidx, needport);
- return (0);
-#endif
- default:
- KEYDEBUG(KEYDEBUG_IPSEC_DUMP,
- printf("%s: " "unknown IP version %u, ignored.\n",
- __func__, v));
- return (EINVAL);
+ KEYDBG(IPSEC_STAMP,
+ printf("%s: PCB(%p): cache hit SP(%p)\n",
+ __func__, inp, sp));
+ /* Return referenced cached policy */
}
+ key_addref(sp);
+ return (sp);
}
+#ifdef INET
static void
ipsec4_get_ulp(const struct mbuf *m, struct secpolicyindex *spidx,
int needport)
{
- u_int8_t nxt;
+ uint8_t nxt;
int off;
/* Sanity check. */
- IPSEC_ASSERT(m != NULL, ("null mbuf"));
- IPSEC_ASSERT(m->m_pkthdr.len >= sizeof(struct ip),("packet too short"));
+ IPSEC_ASSERT(m->m_pkthdr.len >= sizeof(struct ip),
+ ("packet too short"));
if (m->m_len >= sizeof (struct ip)) {
const struct ip *ip = mtod(m, const struct ip *);
@@ -676,61 +549,134 @@ done:
done_proto:
spidx->src.sin.sin_port = IPSEC_PORT_ANY;
spidx->dst.sin.sin_port = IPSEC_PORT_ANY;
+ KEYDBG(IPSEC_DUMP,
+ printf("%s: ", __func__); kdebug_secpolicyindex(spidx, NULL));
}
-/* Assumes that m is sane. */
-static int
+static void
ipsec4_setspidx_ipaddr(const struct mbuf *m, struct secpolicyindex *spidx)
{
- static const struct sockaddr_in template = {
- sizeof (struct sockaddr_in),
- AF_INET,
- 0, { 0 }, { 0, 0, 0, 0, 0, 0, 0, 0 }
- };
- spidx->src.sin = template;
- spidx->dst.sin = template;
+ ipsec4_setsockaddrs(m, &spidx->src, &spidx->dst);
+ spidx->prefs = sizeof(struct in_addr) << 3;
+ spidx->prefd = sizeof(struct in_addr) << 3;
+}
- if (m->m_len < sizeof (struct ip)) {
- m_copydata(m, offsetof(struct ip, ip_src),
- sizeof (struct in_addr),
- (caddr_t) &spidx->src.sin.sin_addr);
- m_copydata(m, offsetof(struct ip, ip_dst),
- sizeof (struct in_addr),
- (caddr_t) &spidx->dst.sin.sin_addr);
- } else {
- const struct ip *ip = mtod(m, const struct ip *);
- spidx->src.sin.sin_addr = ip->ip_src;
- spidx->dst.sin.sin_addr = ip->ip_dst;
+static struct secpolicy *
+ipsec4_getpolicy(const struct mbuf *m, struct inpcb *inp, u_int dir)
+{
+ struct secpolicyindex spidx;
+ struct secpolicy *sp;
+
+ sp = ipsec_getpcbpolicy(inp, dir);
+ if (sp == NULL && key_havesp(dir)) {
+ /* Make an index to look for a policy. */
+ ipsec4_setspidx_ipaddr(m, &spidx);
+ /* Fill ports in spidx if we have inpcb. */
+ ipsec4_get_ulp(m, &spidx, inp != NULL);
+ spidx.dir = dir;
+ sp = key_allocsp(&spidx, dir);
}
+ if (sp == NULL) /* No SP found, use system default. */
+ sp = key_allocsp_default();
+ return (sp);
+}
- spidx->prefs = sizeof(struct in_addr) << 3;
- spidx->prefd = sizeof(struct in_addr) << 3;
+/*
+ * Check security policy for *OUTBOUND* IPv4 packet.
+ */
+struct secpolicy *
+ipsec4_checkpolicy(const struct mbuf *m, struct inpcb *inp, int *error)
+{
+ struct secpolicy *sp;
- return (0);
+ *error = 0;
+ sp = ipsec4_getpolicy(m, inp, IPSEC_DIR_OUTBOUND);
+ if (sp != NULL)
+ sp = ipsec_checkpolicy(sp, inp, error);
+ if (sp == NULL) {
+ switch (*error) {
+ case 0: /* No IPsec required: BYPASS or NONE */
+ break;
+ case -EINVAL:
+ IPSECSTAT_INC(ips_out_polvio);
+ break;
+ default:
+ IPSECSTAT_INC(ips_out_inval);
+ }
+ }
+ KEYDBG(IPSEC_STAMP,
+ printf("%s: using SP(%p), error %d\n", __func__, sp, *error));
+ if (sp != NULL)
+ KEYDBG(IPSEC_DATA, kdebug_secpolicy(sp));
+ return (sp);
+}
+
+/*
+ * Check IPv4 packet against *INBOUND* security policy.
+ * This function is called from tcp_input(), udp_input(),
+ * rip_input() and sctp_input().
+ */
+int
+ipsec4_in_reject(const struct mbuf *m, struct inpcb *inp)
+{
+ struct secpolicy *sp;
+ int result;
+
+ sp = ipsec4_getpolicy(m, inp, IPSEC_DIR_INBOUND);
+ result = ipsec_in_reject(sp, inp, m);
+ key_freesp(&sp);
+ if (result != 0)
+ IPSECSTAT_INC(ips_in_polvio);
+ return (result);
+}
+
+/*
+ * IPSEC_CAP() method implementation for IPv4.
+ */
+int
+ipsec4_capability(struct mbuf *m, u_int cap)
+{
+
+ switch (cap) {
+ case IPSEC_CAP_BYPASS_FILTER:
+ /*
+ * Bypass packet filtering for packets previously handled
+ * by IPsec.
+ */
+ if (!V_ip4_filtertunnel &&
+ m_tag_find(m, PACKET_TAG_IPSEC_IN_DONE, NULL) != NULL)
+ return (1);
+ return (0);
+ case IPSEC_CAP_OPERABLE:
+ /* Do we have active security policies? */
+ if (key_havesp(IPSEC_DIR_INBOUND) != 0 ||
+ key_havesp(IPSEC_DIR_OUTBOUND) != 0)
+ return (1);
+ return (0);
+ };
+ return (EOPNOTSUPP);
}
+#endif /* INET */
+
#ifdef INET6
static void
ipsec6_get_ulp(const struct mbuf *m, struct secpolicyindex *spidx,
int needport)
{
- int off, nxt;
struct tcphdr th;
struct udphdr uh;
struct icmp6_hdr ih;
+ int off, nxt;
- /* Sanity check. */
- if (m == NULL)
- panic("%s: NULL pointer was passed.\n", __func__);
-
- KEYDEBUG(KEYDEBUG_IPSEC_DUMP,
- printf("%s:\n", __func__); kdebug_mbuf(m));
+ IPSEC_ASSERT(m->m_pkthdr.len >= sizeof(struct ip6_hdr),
+ ("packet too short"));
/* Set default. */
spidx->ul_proto = IPSEC_ULPROTO_ANY;
- ((struct sockaddr_in6 *)&spidx->src)->sin6_port = IPSEC_PORT_ANY;
- ((struct sockaddr_in6 *)&spidx->dst)->sin6_port = IPSEC_PORT_ANY;
+ spidx->src.sin6.sin6_port = IPSEC_PORT_ANY;
+ spidx->dst.sin6.sin6_port = IPSEC_PORT_ANY;
nxt = -1;
off = ip6_lasthdr(m, 0, IPPROTO_IPV6, &nxt);
@@ -745,8 +691,8 @@ ipsec6_get_ulp(const struct mbuf *m, struct secpolicyindex *spidx,
if (off + sizeof(struct tcphdr) > m->m_pkthdr.len)
break;
m_copydata(m, off, sizeof(th), (caddr_t)&th);
- ((struct sockaddr_in6 *)&spidx->src)->sin6_port = th.th_sport;
- ((struct sockaddr_in6 *)&spidx->dst)->sin6_port = th.th_dport;
+ spidx->src.sin6.sin6_port = th.th_sport;
+ spidx->dst.sin6.sin6_port = th.th_dport;
break;
case IPPROTO_UDP:
spidx->ul_proto = nxt;
@@ -755,377 +701,157 @@ ipsec6_get_ulp(const struct mbuf *m, struct secpolicyindex *spidx,
if (off + sizeof(struct udphdr) > m->m_pkthdr.len)
break;
m_copydata(m, off, sizeof(uh), (caddr_t)&uh);
- ((struct sockaddr_in6 *)&spidx->src)->sin6_port = uh.uh_sport;
- ((struct sockaddr_in6 *)&spidx->dst)->sin6_port = uh.uh_dport;
+ spidx->src.sin6.sin6_port = uh.uh_sport;
+ spidx->dst.sin6.sin6_port = uh.uh_dport;
break;
case IPPROTO_ICMPV6:
spidx->ul_proto = nxt;
if (off + sizeof(struct icmp6_hdr) > m->m_pkthdr.len)
break;
m_copydata(m, off, sizeof(ih), (caddr_t)&ih);
- ((struct sockaddr_in6 *)&spidx->src)->sin6_port =
- htons((uint16_t)ih.icmp6_type);
- ((struct sockaddr_in6 *)&spidx->dst)->sin6_port =
- htons((uint16_t)ih.icmp6_code);
+ spidx->src.sin6.sin6_port = htons((uint16_t)ih.icmp6_type);
+ spidx->dst.sin6.sin6_port = htons((uint16_t)ih.icmp6_code);
break;
default:
/* XXX Intermediate headers??? */
spidx->ul_proto = nxt;
break;
}
+ KEYDBG(IPSEC_DUMP,
+ printf("%s: ", __func__); kdebug_secpolicyindex(spidx, NULL));
}
-/* Assumes that m is sane. */
-static int
+static void
ipsec6_setspidx_ipaddr(const struct mbuf *m, struct secpolicyindex *spidx)
{
- struct ip6_hdr ip6buf;
- const struct ip6_hdr *ip6 = NULL;
- struct sockaddr_in6 *sin6;
-
- 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;
- }
- sin6 = (struct sockaddr_in6 *)&spidx->src;
- bzero(sin6, sizeof(*sin6));
- sin6->sin6_family = AF_INET6;
- sin6->sin6_len = sizeof(struct sockaddr_in6);
- bcopy(&ip6->ip6_src, &sin6->sin6_addr, sizeof(ip6->ip6_src));
- if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_src)) {
- sin6->sin6_addr.s6_addr16[1] = 0;
- sin6->sin6_scope_id = ntohs(ip6->ip6_src.s6_addr16[1]);
- }
+ ipsec6_setsockaddrs(m, &spidx->src, &spidx->dst);
spidx->prefs = sizeof(struct in6_addr) << 3;
-
- sin6 = (struct sockaddr_in6 *)&spidx->dst;
- bzero(sin6, sizeof(*sin6));
- sin6->sin6_family = AF_INET6;
- sin6->sin6_len = sizeof(struct sockaddr_in6);
- bcopy(&ip6->ip6_dst, &sin6->sin6_addr, sizeof(ip6->ip6_dst));
- if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_dst)) {
- sin6->sin6_addr.s6_addr16[1] = 0;
- sin6->sin6_scope_id = ntohs(ip6->ip6_dst.s6_addr16[1]);
- }
spidx->prefd = sizeof(struct in6_addr) << 3;
-
- return (0);
}
-#endif
-int
-ipsec_run_hhooks(struct ipsec_ctx_data *ctx, int type)
+static struct secpolicy *
+ipsec6_getpolicy(const struct mbuf *m, struct inpcb *inp, u_int dir)
{
- int idx;
+ struct secpolicyindex spidx;
+ struct secpolicy *sp;
- switch (ctx->af) {
-#ifdef INET
- case AF_INET:
- idx = HHOOK_IPSEC_INET;
- break;
-#endif
-#ifdef INET6
- case AF_INET6:
- idx = HHOOK_IPSEC_INET6;
- break;
-#endif
- default:
- return (EPFNOSUPPORT);
+ sp = ipsec_getpcbpolicy(inp, dir);
+ if (sp == NULL && key_havesp(dir)) {
+ /* Make an index to look for a policy. */
+ ipsec6_setspidx_ipaddr(m, &spidx);
+ /* Fill ports in spidx if we have inpcb. */
+ ipsec6_get_ulp(m, &spidx, inp != NULL);
+ spidx.dir = dir;
+ sp = key_allocsp(&spidx, dir);
}
- if (type == HHOOK_TYPE_IPSEC_IN)
- HHOOKS_RUN_IF(V_ipsec_hhh_in[idx], ctx, NULL);
- else
- HHOOKS_RUN_IF(V_ipsec_hhh_out[idx], ctx, NULL);
- if (*ctx->mp == NULL)
- return (EACCES);
- return (0);
-}
-
-static void
-ipsec_delpcbpolicy(struct inpcbpolicy *p)
-{
-
- free(p, M_IPSEC_INPCB);
+ if (sp == NULL) /* No SP found, use system default. */
+ sp = key_allocsp_default();
+ return (sp);
}
-/* Initialize policy in PCB. */
-int
-ipsec_init_policy(struct socket *so, struct inpcbpolicy **pcb_sp)
+/*
+ * Check security policy for *OUTBOUND* IPv6 packet.
+ */
+struct secpolicy *
+ipsec6_checkpolicy(const struct mbuf *m, struct inpcb *inp, int *error)
{
- struct inpcbpolicy *new;
-
- /* Sanity check. */
- if (so == NULL || pcb_sp == NULL)
- panic("%s: NULL pointer was passed.\n", __func__);
-
- new = (struct inpcbpolicy *) malloc(sizeof(struct inpcbpolicy),
- M_IPSEC_INPCB, M_NOWAIT|M_ZERO);
- if (new == NULL) {
- ipseclog((LOG_DEBUG, "%s: No more memory.\n", __func__));
- return (ENOBUFS);
- }
-
- new->priv = IPSEC_IS_PRIVILEGED_SO(so);
+ struct secpolicy *sp;
- if ((new->sp_in = KEY_NEWSP()) == NULL) {
- ipsec_delpcbpolicy(new);
- return (ENOBUFS);
- }
- new->sp_in->policy = IPSEC_POLICY_ENTRUST;
- if ((new->sp_out = KEY_NEWSP()) == NULL) {
- KEY_FREESP(&new->sp_in);
- ipsec_delpcbpolicy(new);
- return (ENOBUFS);
+ *error = 0;
+ sp = ipsec6_getpolicy(m, inp, IPSEC_DIR_OUTBOUND);
+ if (sp != NULL)
+ sp = ipsec_checkpolicy(sp, inp, error);
+ if (sp == NULL) {
+ switch (*error) {
+ case 0: /* No IPsec required: BYPASS or NONE */
+ break;
+ case -EINVAL:
+ IPSEC6STAT_INC(ips_out_polvio);
+ break;
+ default:
+ IPSEC6STAT_INC(ips_out_inval);
+ }
}
- new->sp_out->policy = IPSEC_POLICY_ENTRUST;
- *pcb_sp = new;
-
- return (0);
+ KEYDBG(IPSEC_STAMP,
+ printf("%s: using SP(%p), error %d\n", __func__, sp, *error));
+ if (sp != NULL)
+ KEYDBG(IPSEC_DATA, kdebug_secpolicy(sp));
+ return (sp);
}
-/* Copy old IPsec policy into new. */
+/*
+ * Check IPv6 packet against inbound security policy.
+ * This function is called from tcp6_input(), udp6_input(),
+ * rip6_input() and sctp_input().
+ */
int
-ipsec_copy_policy(struct inpcbpolicy *old, struct inpcbpolicy *new)
+ipsec6_in_reject(const struct mbuf *m, struct inpcb *inp)
{
struct secpolicy *sp;
+ int result;
- sp = ipsec_deepcopy_policy(old->sp_in);
- if (sp) {
- KEY_FREESP(&new->sp_in);
- new->sp_in = sp;
- } else
- return (ENOBUFS);
-
- sp = ipsec_deepcopy_policy(old->sp_out);
- if (sp) {
- KEY_FREESP(&new->sp_out);
- new->sp_out = sp;
- } else
- return (ENOBUFS);
-
- new->priv = old->priv;
-
- return (0);
-}
-
-struct ipsecrequest *
-ipsec_newisr(void)
-{
- struct ipsecrequest *p;
-
- p = malloc(sizeof(struct ipsecrequest), M_IPSEC_SR, M_NOWAIT|M_ZERO);
- if (p != NULL)
- IPSECREQUEST_LOCK_INIT(p);
- return (p);
-}
-
-void
-ipsec_delisr(struct ipsecrequest *p)
-{
-
- IPSECREQUEST_LOCK_DESTROY(p);
- free(p, M_IPSEC_SR);
-}
-
-/* Deep-copy a policy in PCB. */
-static struct secpolicy *
-ipsec_deepcopy_policy(struct secpolicy *src)
-{
- struct ipsecrequest *newchain = NULL;
- struct ipsecrequest *p;
- struct ipsecrequest **q;
- struct ipsecrequest *r;
- struct secpolicy *dst;
-
- if (src == NULL)
- return (NULL);
- dst = KEY_NEWSP();
- if (dst == NULL)
- return (NULL);
-
- /*
- * Deep-copy IPsec request chain. This is required since struct
- * ipsecrequest is not reference counted.
- */
- q = &newchain;
- for (p = src->req; p; p = p->next) {
- *q = ipsec_newisr();
- if (*q == NULL)
- goto fail;
- (*q)->saidx.proto = p->saidx.proto;
- (*q)->saidx.mode = p->saidx.mode;
- (*q)->level = p->level;
- (*q)->saidx.reqid = p->saidx.reqid;
-
- bcopy(&p->saidx.src, &(*q)->saidx.src, sizeof((*q)->saidx.src));
- bcopy(&p->saidx.dst, &(*q)->saidx.dst, sizeof((*q)->saidx.dst));
-
- (*q)->sp = dst;
-
- q = &((*q)->next);
- }
-
- dst->req = newchain;
- dst->policy = src->policy;
- /* Do not touch the refcnt fields. */
-
- return (dst);
-
-fail:
- for (p = newchain; p; p = r) {
- r = p->next;
- ipsec_delisr(p);
- p = NULL;
- }
- KEY_FREESP(&dst);
- return (NULL);
-}
-
-/* Set policy and IPsec request if present. */
-static int
-ipsec_set_policy_internal(struct secpolicy **pcb_sp, int optname,
- caddr_t request, size_t len, struct ucred *cred)
-{
- struct sadb_x_policy *xpl;
- struct secpolicy *newsp = NULL;
- int error;
-
- /* Sanity check. */
- if (pcb_sp == NULL || *pcb_sp == NULL || request == NULL)
- return (EINVAL);
- if (len < sizeof(*xpl))
- return (EINVAL);
- xpl = (struct sadb_x_policy *)request;
-
- KEYDEBUG(KEYDEBUG_IPSEC_DUMP,
- printf("%s: passed policy\n", __func__);
- kdebug_sadb_x_policy((struct sadb_ext *)xpl));
-
- /* Check policy type. */
- /* ipsec_set_policy_internal() accepts IPSEC, ENTRUST and BYPASS. */
- if (xpl->sadb_x_policy_type == IPSEC_POLICY_DISCARD
- || xpl->sadb_x_policy_type == IPSEC_POLICY_NONE)
- return (EINVAL);
-
- /* Check privileged socket. */
- if (cred != NULL && xpl->sadb_x_policy_type == IPSEC_POLICY_BYPASS) {
- error = priv_check_cred(cred, PRIV_NETINET_IPSEC, 0);
- if (error)
- return (EACCES);
- }
-
- /* Allocating new SP entry. */
- if ((newsp = key_msg2sp(xpl, len, &error)) == NULL)
- return (error);
-
- /* Clear old SP and set new SP. */
- KEY_FREESP(pcb_sp);
- *pcb_sp = newsp;
- KEYDEBUG(KEYDEBUG_IPSEC_DUMP,
- printf("%s: new policy\n", __func__);
- kdebug_secpolicy(newsp));
-
- return (0);
+ sp = ipsec6_getpolicy(m, inp, IPSEC_DIR_INBOUND);
+ result = ipsec_in_reject(sp, inp, m);
+ key_freesp(&sp);
+ if (result)
+ IPSEC6STAT_INC(ips_in_polvio);
+ return (result);
}
+/*
+ * IPSEC_CAP() method implementation for IPv6.
+ */
int
-ipsec_set_policy(struct inpcb *inp, int optname, caddr_t request,
- size_t len, struct ucred *cred)
+ipsec6_capability(struct mbuf *m, u_int cap)
{
- struct sadb_x_policy *xpl;
- struct secpolicy **pcb_sp;
-
- /* Sanity check. */
- if (inp == NULL || request == NULL)
- return (EINVAL);
- if (len < sizeof(*xpl))
- return (EINVAL);
- xpl = (struct sadb_x_policy *)request;
-
- /* Select direction. */
- switch (xpl->sadb_x_policy_dir) {
- case IPSEC_DIR_INBOUND:
- pcb_sp = &inp->inp_sp->sp_in;
- break;
- case IPSEC_DIR_OUTBOUND:
- pcb_sp = &inp->inp_sp->sp_out;
- break;
- default:
- ipseclog((LOG_ERR, "%s: invalid direction=%u\n", __func__,
- xpl->sadb_x_policy_dir));
- return (EINVAL);
- }
- return (ipsec_set_policy_internal(pcb_sp, optname, request, len, cred));
+ switch (cap) {
+ case IPSEC_CAP_BYPASS_FILTER:
+ /*
+ * Bypass packet filtering for packets previously handled
+ * by IPsec.
+ */
+ if (!V_ip6_filtertunnel &&
+ m_tag_find(m, PACKET_TAG_IPSEC_IN_DONE, NULL) != NULL)
+ return (1);
+ return (0);
+ case IPSEC_CAP_OPERABLE:
+ /* Do we have active security policies? */
+ if (key_havesp(IPSEC_DIR_INBOUND) != 0 ||
+ key_havesp(IPSEC_DIR_OUTBOUND) != 0)
+ return (1);
+ return (0);
+ };
+ return (EOPNOTSUPP);
}
+#endif /* INET6 */
int
-ipsec_get_policy(struct inpcb *inp, caddr_t request, size_t len,
- struct mbuf **mp)
+ipsec_run_hhooks(struct ipsec_ctx_data *ctx, int type)
{
- struct sadb_x_policy *xpl;
- struct secpolicy *pcb_sp;
+ int idx;
- /* Sanity check. */
- if (inp == NULL || request == NULL || mp == NULL)
- return (EINVAL);
- IPSEC_ASSERT(inp->inp_sp != NULL, ("null inp_sp"));
- if (len < sizeof(*xpl))
- return (EINVAL);
- xpl = (struct sadb_x_policy *)request;
-
- /* Select direction. */
- switch (xpl->sadb_x_policy_dir) {
- case IPSEC_DIR_INBOUND:
- pcb_sp = inp->inp_sp->sp_in;
+ switch (ctx->af) {
+#ifdef INET
+ case AF_INET:
+ idx = HHOOK_IPSEC_INET;
break;
- case IPSEC_DIR_OUTBOUND:
- pcb_sp = inp->inp_sp->sp_out;
+#endif
+#ifdef INET6
+ case AF_INET6:
+ idx = HHOOK_IPSEC_INET6;
break;
+#endif
default:
- ipseclog((LOG_ERR, "%s: invalid direction=%u\n", __func__,
- xpl->sadb_x_policy_dir));
- return (EINVAL);
- }
-
- /* Sanity check. Should be an IPSEC_ASSERT. */
- if (pcb_sp == NULL)
- return (EINVAL);
-
- *mp = key_sp2msg(pcb_sp);
- if (!*mp) {
- ipseclog((LOG_DEBUG, "%s: No more memory.\n", __func__));
- return (ENOBUFS);
+ return (EPFNOSUPPORT);
}
-
- (*mp)->m_type = MT_DATA;
- KEYDEBUG(KEYDEBUG_IPSEC_DUMP,
- printf("%s:\n", __func__); kdebug_mbuf(*mp));
-
- return (0);
-}
-
-/* Delete policy in PCB. */
-int
-ipsec_delete_pcbpolicy(struct inpcb *inp)
-{
- IPSEC_ASSERT(inp != NULL, ("null inp"));
-
- if (inp->inp_sp == NULL)
- return (0);
-
- if (inp->inp_sp->sp_in != NULL)
- KEY_FREESP(&inp->inp_sp->sp_in);
-
- if (inp->inp_sp->sp_out != NULL)
- KEY_FREESP(&inp->inp_sp->sp_out);
-
- ipsec_delpcbpolicy(inp->inp_sp);
- inp->inp_sp = NULL;
-
+ if (type == HHOOK_TYPE_IPSEC_IN)
+ HHOOKS_RUN_IF(V_ipsec_hhh_in[idx], ctx, NULL);
+ else
+ HHOOKS_RUN_IF(V_ipsec_hhh_out[idx], ctx, NULL);
+ if (*ctx->mp == NULL)
+ return (EACCES);
return (0);
}
@@ -1134,32 +860,36 @@ ipsec_delete_pcbpolicy(struct inpcb *inp)
* Either IPSEC_LEVEL_USE or IPSEC_LEVEL_REQUIRE are always returned.
*/
u_int
-ipsec_get_reqlevel(struct ipsecrequest *isr)
+ipsec_get_reqlevel(struct secpolicy *sp, u_int idx)
{
- u_int level = 0;
+ struct ipsecrequest *isr;
u_int esp_trans_deflev, esp_net_deflev;
u_int ah_trans_deflev, ah_net_deflev;
+ u_int level = 0;
- IPSEC_ASSERT(isr != NULL && isr->sp != NULL, ("null argument"));
- IPSEC_ASSERT(isr->sp->spidx.src.sa.sa_family == isr->sp->spidx.dst.sa.sa_family,
- ("af family mismatch, src %u, dst %u",
- isr->sp->spidx.src.sa.sa_family,
- isr->sp->spidx.dst.sa.sa_family));
-
+ IPSEC_ASSERT(idx < sp->tcount, ("Wrong IPsec request index %d", idx));
/* XXX Note that we have ipseclog() expanded here - code sync issue. */
#define IPSEC_CHECK_DEFAULT(lev) \
- (((lev) != IPSEC_LEVEL_USE && (lev) != IPSEC_LEVEL_REQUIRE \
- && (lev) != IPSEC_LEVEL_UNIQUE) \
- ? (V_ipsec_debug \
- ? log(LOG_INFO, "fixed system default level " #lev ":%d->%d\n",\
- (lev), IPSEC_LEVEL_REQUIRE) \
- : 0), \
- (lev) = IPSEC_LEVEL_REQUIRE, \
- (lev) \
- : (lev))
+ (((lev) != IPSEC_LEVEL_USE && (lev) != IPSEC_LEVEL_REQUIRE && \
+ (lev) != IPSEC_LEVEL_UNIQUE) \
+ ? (V_ipsec_debug ? \
+ log(LOG_INFO, "fixed system default level " #lev ":%d->%d\n",\
+ (lev), IPSEC_LEVEL_REQUIRE) : 0), \
+ (lev) = IPSEC_LEVEL_REQUIRE, (lev) : (lev))
+
+ /*
+ * IPsec VTI uses unique security policy with fake spidx filled
+ * with zeroes. Just return IPSEC_LEVEL_REQUIRE instead of doing
+ * full level lookup for such policies.
+ */
+ if (sp->state == IPSEC_SPSTATE_IFNET) {
+ IPSEC_ASSERT(sp->req[idx]->level == IPSEC_LEVEL_UNIQUE,
+ ("Wrong IPsec request level %d", sp->req[idx]->level));
+ return (IPSEC_LEVEL_REQUIRE);
+ }
/* Set default level. */
- switch (((struct sockaddr *)&isr->sp->spidx.src)->sa_family) {
+ switch (sp->spidx.src.sa.sa_family) {
#ifdef INET
case AF_INET:
esp_trans_deflev = IPSEC_CHECK_DEFAULT(V_ip4_esp_trans_deflev);
@@ -1178,11 +908,12 @@ ipsec_get_reqlevel(struct ipsecrequest *isr)
#endif /* INET6 */
default:
panic("%s: unknown af %u",
- __func__, isr->sp->spidx.src.sa.sa_family);
+ __func__, sp->spidx.src.sa.sa_family);
}
#undef IPSEC_CHECK_DEFAULT
+ isr = sp->req[idx];
/* Set level. */
switch (isr->level) {
case IPSEC_LEVEL_DEFAULT:
@@ -1227,6 +958,45 @@ ipsec_get_reqlevel(struct ipsecrequest *isr)
return (level);
}
+static int
+ipsec_check_history(const struct mbuf *m, struct secpolicy *sp, u_int idx)
+{
+ struct xform_history *xh;
+ struct m_tag *mtag;
+
+ mtag = NULL;
+ while ((mtag = m_tag_find(__DECONST(struct mbuf *, m),
+ PACKET_TAG_IPSEC_IN_DONE, mtag)) != NULL) {
+ xh = (struct xform_history *)(mtag + 1);
+ KEYDBG(IPSEC_DATA,
+ char buf[IPSEC_ADDRSTRLEN];
+ printf("%s: mode %s proto %u dst %s\n", __func__,
+ kdebug_secasindex_mode(xh->mode), xh->proto,
+ ipsec_address(&xh->dst, buf, sizeof(buf))));
+ if (xh->proto != sp->req[idx]->saidx.proto)
+ continue;
+ /* If SA had IPSEC_MODE_ANY, consider this as match. */
+ if (xh->mode != sp->req[idx]->saidx.mode &&
+ xh->mode != IPSEC_MODE_ANY)
+ continue;
+ /*
+ * For transport mode IPsec request doesn't contain
+ * addresses. We need to use address from spidx.
+ */
+ if (sp->req[idx]->saidx.mode == IPSEC_MODE_TRANSPORT) {
+ if (key_sockaddrcmp_withmask(&xh->dst.sa,
+ &sp->spidx.dst.sa, sp->spidx.prefd) != 0)
+ continue;
+ } else {
+ if (key_sockaddrcmp(&xh->dst.sa,
+ &sp->req[idx]->saidx.dst.sa, 0) != 0)
+ continue;
+ }
+ return (0); /* matched */
+ }
+ return (1);
+}
+
/*
* Check security policy requirements against the actual
* packet contents. Return one if the packet should be
@@ -1238,13 +1008,16 @@ ipsec_get_reqlevel(struct ipsecrequest *isr)
* 1: invalid
*/
static int
-ipsec_in_reject(struct secpolicy *sp, const struct mbuf *m)
+ipsec_in_reject(struct secpolicy *sp, struct inpcb *inp, const struct mbuf *m)
{
- struct ipsecrequest *isr;
- int need_auth;
+ int i;
+
+ KEYDBG(IPSEC_STAMP,
+ printf("%s: PCB(%p): using SP(%p)\n", __func__, inp, sp));
+ KEYDBG(IPSEC_DATA, kdebug_secpolicy(sp));
- KEYDEBUG(KEYDEBUG_IPSEC_DATA,
- printf("%s: using SP\n", __func__); kdebug_secpolicy(sp));
+ if (inp != NULL && inp->inp_sp != NULL && inp->inp_sp->sp_in == NULL)
+ ipsec_cachepolicy(inp, sp, IPSEC_DIR_INBOUND);
/* Check policy. */
switch (sp->policy) {
@@ -1258,132 +1031,59 @@ ipsec_in_reject(struct secpolicy *sp, const struct mbuf *m)
IPSEC_ASSERT(sp->policy == IPSEC_POLICY_IPSEC,
("invalid policy %u", sp->policy));
- /* XXX Should compare policy against IPsec header history. */
-
- need_auth = 0;
- for (isr = sp->req; isr != NULL; isr = isr->next) {
- if (ipsec_get_reqlevel(isr) != IPSEC_LEVEL_REQUIRE)
+ /*
+ * ipsec[46]_common_input_cb after each transform adds
+ * PACKET_TAG_IPSEC_IN_DONE mbuf tag. It contains SPI, proto, mode
+ * and destination address from saidx. We can compare info from
+ * these tags with requirements in SP.
+ */
+ for (i = 0; i < sp->tcount; i++) {
+ /*
+ * Do not check IPcomp, since IPcomp document
+ * says that we shouldn't compress small packets.
+ * IPComp policy should always be treated as being
+ * in "use" level.
+ */
+ if (sp->req[i]->saidx.proto == IPPROTO_IPCOMP ||
+ ipsec_get_reqlevel(sp, i) != IPSEC_LEVEL_REQUIRE)
continue;
- switch (isr->saidx.proto) {
+ if (V_check_policy_history != 0 &&
+ ipsec_check_history(m, sp, i) != 0)
+ return (1);
+ else switch (sp->req[i]->saidx.proto) {
case IPPROTO_ESP:
if ((m->m_flags & M_DECRYPTED) == 0) {
- KEYDEBUG(KEYDEBUG_IPSEC_DUMP,
+ KEYDBG(IPSEC_DUMP,
printf("%s: ESP m_flags:%x\n", __func__,
m->m_flags));
return (1);
}
-
- if (!need_auth &&
- isr->sav != NULL &&
- isr->sav->tdb_authalgxform != NULL &&
- (m->m_flags & M_AUTHIPDGM) == 0) {
- KEYDEBUG(KEYDEBUG_IPSEC_DUMP,
- printf("%s: ESP/AH m_flags:%x\n", __func__,
- m->m_flags));
- return (1);
- }
break;
case IPPROTO_AH:
- need_auth = 1;
if ((m->m_flags & M_AUTHIPHDR) == 0) {
- KEYDEBUG(KEYDEBUG_IPSEC_DUMP,
+ KEYDBG(IPSEC_DUMP,
printf("%s: AH m_flags:%x\n", __func__,
m->m_flags));
return (1);
}
break;
- case IPPROTO_IPCOMP:
- /*
- * We don't really care, as IPcomp document
- * says that we shouldn't compress small
- * packets. IPComp policy should always be
- * treated as being in "use" level.
- */
- break;
}
}
return (0); /* Valid. */
}
/*
- * Non zero return value means security policy DISCARD or policy violation.
- */
-static int
-ipsec46_in_reject(const struct mbuf *m, struct inpcb *inp)
-{
- struct secpolicy *sp;
- int error;
- int result;
-
- if (!key_havesp(IPSEC_DIR_INBOUND))
- return 0;
-
- IPSEC_ASSERT(m != NULL, ("null mbuf"));
-
- /* Get SP for this packet. */
- if (inp == NULL)
- sp = ipsec_getpolicybyaddr(m, IPSEC_DIR_INBOUND, &error);
- else
- sp = ipsec_getpolicybysock(m, IPSEC_DIR_INBOUND, inp, &error);
-
- if (sp != NULL) {
- result = ipsec_in_reject(sp, m);
- KEY_FREESP(&sp);
- } else {
- result = 1; /* treat errors as policy violation */
- }
- return (result);
-}
-
-/*
- * Check AH/ESP integrity.
- * This function is called from tcp_input(), udp_input(),
- * and {ah,esp}4_input for tunnel mode.
- */
-int
-ipsec4_in_reject(const struct mbuf *m, struct inpcb *inp)
-{
- int result;
-
- result = ipsec46_in_reject(m, inp);
- if (result)
- IPSECSTAT_INC(ips_in_polvio);
-
- return (result);
-}
-
-#ifdef INET6
-/*
- * Check AH/ESP integrity.
- * This function is called from tcp6_input(), udp6_input(),
- * and {ah,esp}6_input for tunnel mode.
- */
-int
-ipsec6_in_reject(const struct mbuf *m, struct inpcb *inp)
-{
- int result;
-
- result = ipsec46_in_reject(m, inp);
- if (result)
- IPSEC6STAT_INC(ips_in_polvio);
-
- return (result);
-}
-#endif
-
-/*
* Compute the byte size to be occupied by IPsec header.
* In case it is tunnelled, it includes the size of outer IP header.
- * NOTE: SP passed is freed in this function.
*/
static size_t
ipsec_hdrsiz_internal(struct secpolicy *sp)
{
- struct ipsecrequest *isr;
size_t size;
+ int i;
- KEYDEBUG(KEYDEBUG_IPSEC_DATA,
- printf("%s: using SP\n", __func__); kdebug_secpolicy(sp));
+ KEYDBG(IPSEC_STAMP, printf("%s: using SP(%p)\n", __func__, sp));
+ KEYDBG(IPSEC_DATA, kdebug_secpolicy(sp));
switch (sp->policy) {
case IPSEC_POLICY_DISCARD:
@@ -1395,80 +1095,69 @@ ipsec_hdrsiz_internal(struct secpolicy *sp)
IPSEC_ASSERT(sp->policy == IPSEC_POLICY_IPSEC,
("invalid policy %u", sp->policy));
+ /*
+ * XXX: for each transform we need to lookup suitable SA
+ * and use info from SA to calculate headers size.
+ * XXX: for NAT-T we need to cosider UDP header size.
+ */
size = 0;
- for (isr = sp->req; isr != NULL; isr = isr->next) {
- size_t clen = 0;
-
- switch (isr->saidx.proto) {
+ for (i = 0; i < sp->tcount; i++) {
+ switch (sp->req[i]->saidx.proto) {
case IPPROTO_ESP:
- clen = esp_hdrsiz(isr->sav);
+ size += esp_hdrsiz(NULL);
break;
case IPPROTO_AH:
- clen = ah_hdrsiz(isr->sav);
+ size += ah_hdrsiz(NULL);
break;
case IPPROTO_IPCOMP:
- clen = sizeof(struct ipcomp);
+ size += sizeof(struct ipcomp);
break;
}
- if (isr->saidx.mode == IPSEC_MODE_TUNNEL) {
- switch (isr->saidx.dst.sa.sa_family) {
+ if (sp->req[i]->saidx.mode == IPSEC_MODE_TUNNEL) {
+ switch (sp->req[i]->saidx.dst.sa.sa_family) {
+#ifdef INET
case AF_INET:
- clen += sizeof(struct ip);
+ size += sizeof(struct ip);
break;
+#endif
#ifdef INET6
case AF_INET6:
- clen += sizeof(struct ip6_hdr);
+ size += sizeof(struct ip6_hdr);
break;
#endif
default:
ipseclog((LOG_ERR, "%s: unknown AF %d in "
"IPsec tunnel SA\n", __func__,
- ((struct sockaddr *)&isr->saidx.dst)->sa_family));
+ sp->req[i]->saidx.dst.sa.sa_family));
break;
}
}
- size += clen;
}
-
return (size);
}
-/*
- * This function is called from ipsec_hdrsiz_tcp(), ip_ipsec_mtu(),
- * disabled ip6_ipsec_mtu() and ip6_forward().
+/*
+ * Compute ESP/AH header size for protocols with PCB, including
+ * outer IP header. Currently only tcp_output() uses it.
*/
size_t
-ipsec_hdrsiz(const struct mbuf *m, u_int dir, struct inpcb *inp)
+ipsec_hdrsiz_inpcb(struct inpcb *inp)
{
+ struct secpolicyindex spidx;
struct secpolicy *sp;
- int error;
- size_t size;
-
- if (!key_havesp(dir))
- return 0;
-
- IPSEC_ASSERT(m != NULL, ("null mbuf"));
-
- /* Get SP for this packet. */
- if (inp == NULL)
- sp = ipsec_getpolicybyaddr(m, dir, &error);
- else
- sp = ipsec_getpolicybysock(m, dir, inp, &error);
+ size_t sz;
- if (sp != NULL) {
- size = ipsec_hdrsiz_internal(sp);
- KEYDEBUG(KEYDEBUG_IPSEC_DATA,
- printf("%s: size:%lu.\n", __func__,
- (unsigned long)size));
-
- KEY_FREESP(&sp);
- } else {
- size = 0; /* XXX Should be panic?
- * -> No, we are called w/o knowing if
- * IPsec processing is needed. */
+ sp = ipsec_getpcbpolicy(inp, IPSEC_DIR_OUTBOUND);
+ if (sp == NULL && key_havesp(IPSEC_DIR_OUTBOUND)) {
+ ipsec_setspidx_inpcb(inp, &spidx, IPSEC_DIR_OUTBOUND);
+ sp = key_allocsp(&spidx, IPSEC_DIR_OUTBOUND);
}
- return (size);
+ if (sp == NULL)
+ sp = key_allocsp_default();
+ sz = ipsec_hdrsiz_internal(sp);
+ key_freesp(&sp);
+ return (sz);
}
/*
@@ -1479,27 +1168,31 @@ ipsec_hdrsiz(const struct mbuf *m, u_int dir, struct inpcb *inp)
* beforehand).
* 0 (zero) is returned if packet disallowed, 1 if packet permitted.
*
- * Based on RFC 2401.
+ * Based on RFC 6479. Blocks are 32 bits unsigned integers
*/
+
+#define IPSEC_BITMAP_INDEX_MASK(w) (w - 1)
+#define IPSEC_REDUNDANT_BIT_SHIFTS 5
+#define IPSEC_REDUNDANT_BITS (1 << IPSEC_REDUNDANT_BIT_SHIFTS)
+#define IPSEC_BITMAP_LOC_MASK (IPSEC_REDUNDANT_BITS - 1)
+
int
-ipsec_chkreplay(u_int32_t seq, struct secasvar *sav)
+ipsec_chkreplay(uint32_t seq, struct secasvar *sav)
{
const struct secreplay *replay;
- u_int32_t diff;
- int fr;
- u_int32_t wsizeb; /* Constant: bits of window size. */
- int frlast; /* Constant: last frame. */
+ uint32_t wsizeb; /* Constant: window size. */
+ int index, bit_location;
IPSEC_ASSERT(sav != NULL, ("Null SA"));
IPSEC_ASSERT(sav->replay != NULL, ("Null replay state"));
replay = sav->replay;
+ /* No need to check replay if disabled. */
if (replay->wsize == 0)
- return (1); /* No need to check replay. */
+ return (1);
/* Constant. */
- frlast = replay->wsize - 1;
wsizeb = replay->wsize << 3;
/* Sequence number of 0 is invalid. */
@@ -1510,26 +1203,26 @@ ipsec_chkreplay(u_int32_t seq, struct secasvar *sav)
if (replay->count == 0)
return (1);
- if (seq > replay->lastseq) {
- /* Larger sequences are okay. */
+ /* Larger sequences are okay. */
+ if (seq > replay->lastseq)
return (1);
- } else {
- /* seq is equal or less than lastseq. */
- diff = replay->lastseq - seq;
- /* Over range to check, i.e. too old or wrapped. */
- if (diff >= wsizeb)
- return (0);
-
- fr = frlast - diff / 8;
+ /* Over range to check, i.e. too old or wrapped. */
+ if (replay->lastseq - seq >= wsizeb)
+ return (0);
- /* This packet already seen? */
- if ((replay->bitmap)[fr] & (1 << (diff % 8)))
- return (0);
+ /* The sequence is inside the sliding window
+ * now check the bit in the bitmap
+ * bit location only depends on the sequence number
+ */
+ bit_location = seq & IPSEC_BITMAP_LOC_MASK;
+ index = (seq >> IPSEC_REDUNDANT_BIT_SHIFTS)
+ & IPSEC_BITMAP_INDEX_MASK(replay->bitmap_size);
- /* Out of order but good. */
- return (1);
- }
+ /* This packet already seen? */
+ if ((replay->bitmap)[index] & (1 << bit_location))
+ return (0);
+ return (1);
}
/*
@@ -1538,14 +1231,12 @@ ipsec_chkreplay(u_int32_t seq, struct secasvar *sav)
* 1: NG
*/
int
-ipsec_updatereplay(u_int32_t seq, struct secasvar *sav)
+ipsec_updatereplay(uint32_t seq, struct secasvar *sav)
{
char buf[128];
struct secreplay *replay;
- u_int32_t diff;
- int fr;
- u_int32_t wsizeb; /* Constant: bits of window size. */
- int frlast; /* Constant: last frame. */
+ uint32_t wsizeb; /* Constant: window size. */
+ int diff, index, bit_location;
IPSEC_ASSERT(sav != NULL, ("Null SA"));
IPSEC_ASSERT(sav->replay != NULL, ("Null replay state"));
@@ -1556,58 +1247,46 @@ ipsec_updatereplay(u_int32_t seq, struct secasvar *sav)
goto ok; /* No need to check replay. */
/* Constant. */
- frlast = replay->wsize - 1;
wsizeb = replay->wsize << 3;
/* Sequence number of 0 is invalid. */
if (seq == 0)
return (1);
- /* First time. */
- if (replay->count == 0) {
- replay->lastseq = seq;
- bzero(replay->bitmap, replay->wsize);
- (replay->bitmap)[frlast] = 1;
+ /* The packet is too old, no need to update */
+ if (wsizeb + seq < replay->lastseq)
goto ok;
- }
+ /* Now update the bit */
+ index = (seq >> IPSEC_REDUNDANT_BIT_SHIFTS);
+
+ /* First check if the sequence number is in the range */
if (seq > replay->lastseq) {
- /* seq is larger than lastseq. */
- diff = seq - replay->lastseq;
-
- /* New larger sequence number. */
- if (diff < wsizeb) {
- /* In window. */
- /* Set bit for this packet. */
- vshiftl(replay->bitmap, diff, replay->wsize);
- (replay->bitmap)[frlast] |= 1;
- } else {
- /* This packet has a "way larger". */
- bzero(replay->bitmap, replay->wsize);
- (replay->bitmap)[frlast] = 1;
- }
- replay->lastseq = seq;
+ int id;
+ int index_cur = replay->lastseq >> IPSEC_REDUNDANT_BIT_SHIFTS;
- /* Larger is good. */
- } else {
- /* seq is equal or less than lastseq. */
- diff = replay->lastseq - seq;
+ diff = index - index_cur;
+ if (diff > replay->bitmap_size) {
+ /* something unusual in this case */
+ diff = replay->bitmap_size;
+ }
- /* Over range to check, i.e. too old or wrapped. */
- if (diff >= wsizeb)
- return (1);
+ for (id = 0; id < diff; ++id) {
+ replay->bitmap[(id + index_cur + 1)
+ & IPSEC_BITMAP_INDEX_MASK(replay->bitmap_size)] = 0;
+ }
- fr = frlast - diff / 8;
+ replay->lastseq = seq;
+ }
- /* This packet already seen? */
- if ((replay->bitmap)[fr] & (1 << (diff % 8)))
- return (1);
+ index &= IPSEC_BITMAP_INDEX_MASK(replay->bitmap_size);
+ bit_location = seq & IPSEC_BITMAP_LOC_MASK;
- /* Mark as seen. */
- (replay->bitmap)[fr] |= (1 << (diff % 8));
+ /* this packet has already been received */
+ if (replay->bitmap[index] & (1 << bit_location))
+ return (1);
- /* Out of order but good. */
- }
+ replay->bitmap[index] |= (1 << bit_location);
ok:
if (replay->count == ~0) {
@@ -1616,139 +1295,99 @@ ok:
replay->overflow++;
/* Don't increment, no more packets accepted. */
- if ((sav->flags & SADB_X_EXT_CYCSEQ) == 0)
+ if ((sav->flags & SADB_X_EXT_CYCSEQ) == 0) {
+ if (sav->sah->saidx.proto == IPPROTO_AH)
+ AHSTAT_INC(ahs_wrap);
+ else if (sav->sah->saidx.proto == IPPROTO_ESP)
+ ESPSTAT_INC(esps_wrap);
return (1);
+ }
ipseclog((LOG_WARNING, "%s: replay counter made %d cycle. %s\n",
__func__, replay->overflow,
- ipsec_logsastr(sav, buf, sizeof(buf))));
+ ipsec_sa2str(sav, buf, sizeof(buf))));
}
-
- replay->count++;
-
return (0);
}
-/*
- * Shift variable length buffer to left.
- * IN: bitmap: pointer to the buffer
- * nbit: the number of to shift.
- * wsize: buffer size (bytes).
- */
-static void
-vshiftl(unsigned char *bitmap, int nbit, int wsize)
-{
- int s, j, i;
- unsigned char over;
-
- for (j = 0; j < nbit; j += 8) {
- s = (nbit - j < 8) ? (nbit - j): 8;
- bitmap[0] <<= s;
- for (i = 1; i < wsize; i++) {
- over = (bitmap[i] >> (8 - s));
- bitmap[i] <<= s;
- bitmap[i-1] |= over;
- }
- }
-}
-
-/* Return a printable string for the address. */
-char*
-ipsec_address(union sockaddr_union* sa, char *buf, socklen_t size)
+int
+ipsec_updateid(struct secasvar *sav, uint64_t *new, uint64_t *old)
{
+ uint64_t tmp;
- 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:
- return (inet_ntop(AF_INET6, &sa->sin6.sin6_addr, buf, size));
-#endif /* INET6 */
- default:
- return ("(unknown address family)");
+ /*
+ * tdb_cryptoid is initialized by xform_init().
+ * Then it can be changed only when some crypto error occurred or
+ * when SA is deleted. We stored used cryptoid in the xform_data
+ * structure. In case when crypto error occurred and crypto
+ * subsystem has reinited the session, it returns new cryptoid
+ * and EAGAIN error code.
+ *
+ * This function will be called when we got EAGAIN from crypto
+ * subsystem.
+ * *new is cryptoid that was returned by crypto subsystem in
+ * the crp_sid.
+ * *old is the original cryptoid that we stored in xform_data.
+ *
+ * For first failed request *old == sav->tdb_cryptoid, then
+ * we update sav->tdb_cryptoid and redo crypto_dispatch().
+ * For next failed request *old != sav->tdb_cryptoid, then
+ * we store cryptoid from first request into the *new variable
+ * and crp_sid from this second session will be returned via
+ * *old pointer, so caller can release second session.
+ *
+ * XXXAE: check this more carefully.
+ */
+ KEYDBG(IPSEC_STAMP,
+ printf("%s: SA(%p) moves cryptoid %jd -> %jd\n",
+ __func__, sav, (uintmax_t)(*old), (uintmax_t)(*new)));
+ KEYDBG(IPSEC_DATA, kdebug_secasv(sav));
+ SECASVAR_LOCK(sav);
+ if (sav->tdb_cryptoid != *old) {
+ /* cryptoid was already updated */
+ tmp = *new;
+ *new = sav->tdb_cryptoid;
+ *old = tmp;
+ SECASVAR_UNLOCK(sav);
+ return (1);
}
+ sav->tdb_cryptoid = *new;
+ SECASVAR_UNLOCK(sav);
+ return (0);
}
-char *
-ipsec_logsastr(struct secasvar *sav, char *buf, size_t size)
-{
- char sbuf[INET6_ADDRSTRLEN], dbuf[INET6_ADDRSTRLEN];
-
- IPSEC_ASSERT(sav->sah->saidx.src.sa.sa_family ==
- sav->sah->saidx.dst.sa.sa_family, ("address family mismatch"));
-
- 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);
-}
-
-void
-ipsec_dumpmbuf(const struct mbuf *m)
+int
+ipsec_initialized(void)
{
- const u_char *p;
- int totlen;
- int i;
- totlen = 0;
- printf("---\n");
- while (m) {
- p = mtod(m, const u_char *);
- for (i = 0; i < m->m_len; i++) {
- printf("%02x ", p[i]);
- totlen++;
- if (totlen % 16 == 0)
- printf("\n");
- }
- m = m->m_next;
- }
- if (totlen % 16 != 0)
- printf("\n");
- printf("---\n");
+ return (V_def_policy != NULL);
}
static void
def_policy_init(const void *unused __unused)
{
- bzero(&V_def_policy, sizeof(struct secpolicy));
- V_def_policy.policy = IPSEC_POLICY_NONE;
- V_def_policy.refcnt = 1;
+ V_def_policy = key_newsp();
+ if (V_def_policy != NULL) {
+ V_def_policy->policy = IPSEC_POLICY_NONE;
+ /* Force INPCB SP cache invalidation */
+ key_bumpspgen();
+ } else
+ printf("%s: failed to initialize default policy\n", __func__);
}
-VNET_SYSINIT(def_policy_init, SI_SUB_PROTO_DOMAIN, SI_ORDER_FIRST,
- def_policy_init, NULL);
-
-/* XXX This stuff doesn't belong here... */
-static struct xformsw* xforms = NULL;
-
-/*
- * Register a transform; typically at system startup.
- */
-void
-xform_register(struct xformsw* xsp)
+static void
+def_policy_uninit(const void *unused __unused)
{
- xsp->xf_next = xforms;
- xforms = xsp;
+ if (V_def_policy != NULL) {
+ key_freesp(&V_def_policy);
+ key_bumpspgen();
+ }
}
-/*
- * Initialize transform support in an sav.
- */
-int
-xform_init(struct secasvar *sav, int xftype)
-{
- struct xformsw *xsp;
-
- if (sav->tdb_xform != NULL) /* Previously initialized. */
- return (0);
- for (xsp = xforms; xsp; xsp = xsp->xf_next)
- if (xsp->xf_type == xftype)
- return ((*xsp->xf_init)(sav, xsp));
- return (EINVAL);
-}
+VNET_SYSINIT(def_policy_init, SI_SUB_PROTO_DOMAIN, SI_ORDER_FIRST,
+ def_policy_init, NULL);
+VNET_SYSUNINIT(def_policy_uninit, SI_SUB_PROTO_DOMAIN, SI_ORDER_FIRST,
+ def_policy_uninit, NULL);
diff --git a/sys/netipsec/ipsec.h b/sys/netipsec/ipsec.h
index 0e427e3..49413e2 100644
--- a/sys/netipsec/ipsec.h
+++ b/sys/netipsec/ipsec.h
@@ -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
@@ -191,6 +179,12 @@ struct secspacq {
#define IPSEC_POLICY_ENTRUST 3 /* consulting SPD if present. */
#define IPSEC_POLICY_BYPASS 4 /* only for privileged socket. */
+/* Policy scope */
+#define IPSEC_POLICYSCOPE_ANY 0x00 /* unspecified */
+#define IPSEC_POLICYSCOPE_GLOBAL 0x01 /* global scope */
+#define IPSEC_POLICYSCOPE_IFNET 0x02 /* if_ipsec(4) scope */
+#define IPSEC_POLICYSCOPE_PCB 0x04 /* PCB scope */
+
/* Security protocol level */
#define IPSEC_LEVEL_DEFAULT 0 /* reference to system default */
#define IPSEC_LEVEL_USE 1 /* use SA if present. */
@@ -288,6 +282,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 +295,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,
- 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 *);
-
-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 *);
-
-struct secas;
-struct tcpcb;
-extern int ipsec_chkreplay(u_int32_t, struct secasvar *);
-extern int ipsec_updatereplay(u_int32_t, struct secasvar *);
-
-extern size_t ipsec_hdrsiz(const struct mbuf *, u_int, struct inpcb *);
-extern size_t ipsec_hdrsiz_tcp(struct tcpcb *);
-
+struct m_tag;
+struct secasvar;
+struct sockopt;
+struct tcphdr;
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 *);
+int ipsec_if_input(struct mbuf *, struct secasvar *, uint32_t);
-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 *);
+struct ipsecrequest *ipsec_newisr(void);
+void ipsec_delisr(struct ipsecrequest *);
+struct secpolicy *ipsec4_checkpolicy(const struct mbuf *, struct inpcb *,
+ int *);
+
+u_int ipsec_get_reqlevel(struct secpolicy *, u_int);
+
+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 *);
+
+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);
+
+void ipsec_setspidx_inpcb(struct inpcb *, struct secpolicyindex *, u_int);
+
+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);
diff --git a/sys/netipsec/ipsec6.h b/sys/netipsec/ipsec6.h
index 02cf025..a5fae4d 100644
--- a/sys/netipsec/ipsec6.h
+++ b/sys/netipsec/ipsec6.h
@@ -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_*/
diff --git a/sys/netipsec/ipsec_input.c b/sys/netipsec/ipsec_input.c
index 063096f..c4acafe 100644
--- a/sys/netipsec/ipsec_input.c
+++ b/sys/netipsec/ipsec_input.c
@@ -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
-int
-ah4_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_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);
-}
+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
-ipcomp4_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_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;
@@ -334,18 +297,9 @@ ipsec4_common_input_cb(struct mbuf *m, struct secasvar *sav, int skip,
sproto == IPPROTO_IPCOMP,
("unexpected security protocol %u", sproto));
- /* Sanity check */
- if (m == NULL) {
- DPRINTF(("%s: null mbuf", __func__));
- IPSEC_ISTAT(sproto, badkcr);
- KEY_FREESAV(&sav);
- return EINVAL;
- }
-
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",
@@ -364,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;
@@ -381,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;
@@ -422,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;
}
@@ -465,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);
@@ -473,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);
}
@@ -517,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;
+
+ /* 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));
+ }
}
- return 0;
+ key_freesav(&sav);
+ return (error);
bad:
- m_freem(m);
- return error;
-}
-
-void
-ipsec4_common_ctlinput(int cmd, struct sockaddr *sa, void *v, int proto)
-{
- /* XXX nothing just yet */
+ 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);
}
/*
@@ -590,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;
@@ -615,14 +511,6 @@ ipsec6_common_input_cb(struct mbuf *m, struct secasvar *sav, int skip,
sproto == IPPROTO_IPCOMP,
("unexpected security protocol %u", sproto));
- /* Sanity check */
- if (m == NULL) {
- DPRINTF(("%s: null mbuf", __func__));
- IPSEC_ISTAT(sproto, badkcr);
- error = EINVAL;
- goto bad;
- }
-
/* Fix IPv6 header */
if (m->m_len < sizeof(struct ip6_hdr) &&
(m = m_pullup(m, sizeof(struct ip6_hdr))) == NULL) {
@@ -636,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;
@@ -657,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 */
@@ -693,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 {
@@ -731,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);
@@ -739,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;
@@ -783,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);
}
/*
@@ -826,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 */
diff --git a/sys/netipsec/ipsec_mbuf.c b/sys/netipsec/ipsec_mbuf.c
index 7111210..6617d48 100644
--- a/sys/netipsec/ipsec_mbuf.c
+++ b/sys/netipsec/ipsec_mbuf.c
@@ -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;
}
diff --git a/sys/netipsec/ipsec_mod.c b/sys/netipsec/ipsec_mod.c
new file mode 100644
index 0000000..0652333
--- /dev/null
+++ b/sys/netipsec/ipsec_mod.c
@@ -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
diff --git a/sys/netipsec/ipsec_output.c b/sys/netipsec/ipsec_output.c
index 24d8f4e..392008e 100644
--- a/sys/netipsec/ipsec_output.c
+++ b/sys/netipsec/ipsec_output.c
@@ -1,5 +1,6 @@
/*-
* Copyright (c) 2002, 2003 Sam Leffler, Errno Consulting
+ * Copyright (c) 2016 Andrey V. Elsukov <ae@FreeBSD.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -32,6 +33,7 @@
#include "opt_inet.h"
#include "opt_inet6.h"
#include "opt_ipsec.h"
+#include "opt_sctp.h"
#include <sys/param.h>
#include <sys/systm.h>
@@ -67,7 +69,13 @@
#ifdef INET6
#include <netinet/icmp6.h>
#endif
+#ifdef SCTP
+#include <netinet/sctp_crc32.h>
+#endif
+#include <netinet/udp.h>
+#include <netipsec/ah.h>
+#include <netipsec/esp.h>
#include <netipsec/ipsec.h>
#ifdef INET6
#include <netipsec/ipsec6.h>
@@ -84,25 +92,616 @@
#include <machine/in_cksum.h>
-#ifdef IPSEC_NAT_T
-#include <netinet/udp.h>
+#define IPSEC_OSTAT_INC(proto, name) do { \
+ if ((proto) == IPPROTO_ESP) \
+ ESPSTAT_INC(esps_##name); \
+ else if ((proto) == IPPROTO_AH)\
+ AHSTAT_INC(ahs_##name); \
+ else \
+ IPCOMPSTAT_INC(ipcomps_##name); \
+} while (0)
+
+static int ipsec_encap(struct mbuf **mp, struct secasindex *saidx);
+
+#ifdef INET
+static struct secasvar *
+ipsec4_allocsa(struct mbuf *m, struct secpolicy *sp, u_int *pidx, int *error)
+{
+ struct secasindex *saidx, tmpsaidx;
+ struct ipsecrequest *isr;
+ struct sockaddr_in *sin;
+ struct secasvar *sav;
+ struct ip *ip;
+
+ /*
+ * Check system global policy controls.
+ */
+next:
+ isr = sp->req[*pidx];
+ if ((isr->saidx.proto == IPPROTO_ESP && !V_esp_enable) ||
+ (isr->saidx.proto == IPPROTO_AH && !V_ah_enable) ||
+ (isr->saidx.proto == IPPROTO_IPCOMP && !V_ipcomp_enable)) {
+ DPRINTF(("%s: IPsec outbound packet dropped due"
+ " to policy (check your sysctls)\n", __func__));
+ IPSEC_OSTAT_INC(isr->saidx.proto, pdrops);
+ *error = EHOSTUNREACH;
+ return (NULL);
+ }
+ /*
+ * Craft SA index to search for proper SA. Note that
+ * we only initialize unspecified SA peers for transport
+ * mode; for tunnel mode they must already be filled in.
+ */
+ if (isr->saidx.mode == IPSEC_MODE_TRANSPORT) {
+ saidx = &tmpsaidx;
+ *saidx = isr->saidx;
+ ip = mtod(m, struct ip *);
+ if (saidx->src.sa.sa_len == 0) {
+ sin = &saidx->src.sin;
+ sin->sin_len = sizeof(*sin);
+ sin->sin_family = AF_INET;
+ sin->sin_port = IPSEC_PORT_ANY;
+ sin->sin_addr = ip->ip_src;
+ }
+ if (saidx->dst.sa.sa_len == 0) {
+ sin = &saidx->dst.sin;
+ sin->sin_len = sizeof(*sin);
+ sin->sin_family = AF_INET;
+ sin->sin_port = IPSEC_PORT_ANY;
+ sin->sin_addr = ip->ip_dst;
+ }
+ } else
+ saidx = &sp->req[*pidx]->saidx;
+ /*
+ * Lookup SA and validate it.
+ */
+ sav = key_allocsa_policy(sp, saidx, error);
+ if (sav == NULL) {
+ IPSECSTAT_INC(ips_out_nosa);
+ if (*error != 0)
+ return (NULL);
+ if (ipsec_get_reqlevel(sp, *pidx) != IPSEC_LEVEL_REQUIRE) {
+ /*
+ * We have no SA and policy that doesn't require
+ * this IPsec transform, thus we can continue w/o
+ * IPsec processing, i.e. return EJUSTRETURN.
+ * But first check if there is some bundled transform.
+ */
+ if (sp->tcount > ++(*pidx))
+ goto next;
+ *error = EJUSTRETURN;
+ }
+ return (NULL);
+ }
+ IPSEC_ASSERT(sav->tdb_xform != NULL, ("SA with NULL tdb_xform"));
+ return (sav);
+}
+
+/*
+ * IPsec output logic for IPv4.
+ */
+static int
+ipsec4_perform_request(struct mbuf *m, struct secpolicy *sp, u_int idx)
+{
+ char sbuf[IPSEC_ADDRSTRLEN], dbuf[IPSEC_ADDRSTRLEN];
+ struct ipsec_ctx_data ctx;
+ union sockaddr_union *dst;
+ struct secasvar *sav;
+ struct ip *ip;
+ int error, i, off;
+
+ IPSEC_ASSERT(idx < sp->tcount, ("Wrong IPsec request index %d", idx));
+
+ /*
+ * We hold the reference to SP. Content of SP couldn't be changed.
+ * Craft secasindex and do lookup for suitable SA.
+ * Then do encapsulation if needed and call xform's output.
+ * We need to store SP in the xform callback parameters.
+ * In xform callback we will extract SP and it can be used to
+ * determine next transform. At the end of transform we can
+ * release reference to SP.
+ */
+ sav = ipsec4_allocsa(m, sp, &idx, &error);
+ if (sav == NULL) {
+ if (error == EJUSTRETURN) { /* No IPsec required */
+ key_freesp(&sp);
+ return (error);
+ }
+ goto bad;
+ }
+ /*
+ * XXXAE: most likely ip_sum at this point is wrong.
+ */
+ IPSEC_INIT_CTX(&ctx, &m, sav, AF_INET, IPSEC_ENC_BEFORE);
+ if ((error = ipsec_run_hhooks(&ctx, HHOOK_TYPE_IPSEC_OUT)) != 0)
+ goto bad;
+
+ ip = mtod(m, struct ip *);
+ dst = &sav->sah->saidx.dst;
+ /* Do the appropriate encapsulation, if necessary */
+ if (sp->req[idx]->saidx.mode == IPSEC_MODE_TUNNEL || /* Tunnel requ'd */
+ dst->sa.sa_family != AF_INET || /* PF mismatch */
+ (dst->sa.sa_family == AF_INET && /* Proxy */
+ dst->sin.sin_addr.s_addr != INADDR_ANY &&
+ dst->sin.sin_addr.s_addr != ip->ip_dst.s_addr)) {
+ /* Fix IPv4 header checksum and length */
+ ip->ip_len = htons(m->m_pkthdr.len);
+ ip->ip_sum = 0;
+ ip->ip_sum = in_cksum(m, ip->ip_hl << 2);
+ error = ipsec_encap(&m, &sav->sah->saidx);
+ if (error != 0) {
+ DPRINTF(("%s: encapsulation for SA %s->%s "
+ "SPI 0x%08x failed with error %d\n", __func__,
+ ipsec_address(&sav->sah->saidx.src, sbuf,
+ sizeof(sbuf)),
+ ipsec_address(&sav->sah->saidx.dst, dbuf,
+ sizeof(dbuf)), ntohl(sav->spi), error));
+ /* XXXAE: IPSEC_OSTAT_INC(tunnel); */
+ goto bad;
+ }
+ }
+
+ IPSEC_INIT_CTX(&ctx, &m, sav, dst->sa.sa_family, IPSEC_ENC_AFTER);
+ if ((error = ipsec_run_hhooks(&ctx, HHOOK_TYPE_IPSEC_OUT)) != 0)
+ goto bad;
+
+ /*
+ * Dispatch to the appropriate IPsec transform logic. The
+ * packet will be returned for transmission after crypto
+ * processing, etc. are completed.
+ *
+ * NB: m & sav are ``passed to caller'' who's responsible for
+ * reclaiming their resources.
+ */
+ switch(dst->sa.sa_family) {
+ case AF_INET:
+ ip = mtod(m, struct ip *);
+ i = ip->ip_hl << 2;
+ off = offsetof(struct ip, ip_p);
+ break;
+#ifdef INET6
+ case AF_INET6:
+ i = sizeof(struct ip6_hdr);
+ off = offsetof(struct ip6_hdr, ip6_nxt);
+ break;
+#endif /* INET6 */
+ default:
+ DPRINTF(("%s: unsupported protocol family %u\n",
+ __func__, dst->sa.sa_family));
+ error = EPFNOSUPPORT;
+ IPSEC_OSTAT_INC(sav->sah->saidx.proto, nopf);
+ goto bad;
+ }
+ error = (*sav->tdb_xform->xf_output)(m, sp, sav, idx, i, off);
+ if (error != 0) {
+ key_freesav(&sav);
+ key_freesp(&sp);
+ }
+ return (error);
+bad:
+ IPSECSTAT_INC(ips_out_inval);
+ if (m != NULL)
+ m_freem(m);
+ if (sav != NULL)
+ key_freesav(&sav);
+ key_freesp(&sp);
+ return (error);
+}
+
+int
+ipsec4_process_packet(struct mbuf *m, struct secpolicy *sp,
+ struct inpcb *inp)
+{
+
+ return (ipsec4_perform_request(m, sp, 0));
+}
+
+static int
+ipsec4_common_output(struct mbuf *m, struct inpcb *inp, int forwarding)
+{
+ struct secpolicy *sp;
+ int error;
+
+ /* Lookup for the corresponding outbound security policy */
+ sp = ipsec4_checkpolicy(m, inp, &error);
+ if (sp == NULL) {
+ if (error == -EINVAL) {
+ /* Discarded by policy. */
+ m_freem(m);
+ return (EACCES);
+ }
+ return (0); /* No IPsec required. */
+ }
+
+ /*
+ * Usually we have to have tunnel mode IPsec security policy
+ * when we are forwarding a packet. Otherwise we could not handle
+ * encrypted replies, because they are not destined for us. But
+ * some users are doing source address translation for forwarded
+ * packets, and thus, even if they are forwarded, the replies will
+ * return back to us.
+ */
+ if (!forwarding) {
+ /*
+ * 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 and releases reference to SP */
+ error = ipsec4_process_packet(m, sp, inp);
+ 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.
+ */
+ return (0);
+ }
+ if (error == 0)
+ return (EINPROGRESS); /* consumed by IPsec */
+ return (error);
+}
+/*
+ * IPSEC_OUTPUT() method implementation for IPv4.
+ * 0 - no IPsec handling needed
+ * other values - mbuf consumed by IPsec.
+ */
int
-ipsec_process_done(struct mbuf *m, struct ipsecrequest *isr)
+ipsec4_output(struct mbuf *m, struct inpcb *inp)
{
- struct tdb_ident *tdbi;
- struct m_tag *mtag;
+
+ /*
+ * 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)
+ return (0);
+
+ return (ipsec4_common_output(m, inp, 0));
+}
+
+/*
+ * IPSEC_FORWARD() method implementation for IPv4.
+ * 0 - no IPsec handling needed
+ * other values - mbuf consumed by IPsec.
+ */
+int
+ipsec4_forward(struct mbuf *m)
+{
+
+ /*
+ * Check if this packet has an active inbound SP and needs to be
+ * dropped instead of forwarded.
+ */
+ if (ipsec4_in_reject(m, NULL) != 0) {
+ m_freem(m);
+ return (EACCES);
+ }
+ return (ipsec4_common_output(m, NULL, 1));
+}
+#endif
+
+#ifdef INET6
+static int
+in6_sa_equal_addrwithscope(const struct sockaddr_in6 *sa,
+ const struct in6_addr *ia)
+{
+ struct in6_addr ia2;
+
+ if (IN6_IS_SCOPE_LINKLOCAL(&sa->sin6_addr)) {
+ memcpy(&ia2, &sa->sin6_addr, sizeof(ia2));
+ ia2.s6_addr16[1] = htons(sa->sin6_scope_id);
+ return (IN6_ARE_ADDR_EQUAL(ia, &ia2));
+ }
+ return (IN6_ARE_ADDR_EQUAL(&sa->sin6_addr, ia));
+}
+
+static struct secasvar *
+ipsec6_allocsa(struct mbuf *m, struct secpolicy *sp, u_int *pidx, int *error)
+{
+ struct secasindex *saidx, tmpsaidx;
+ struct ipsecrequest *isr;
+ struct sockaddr_in6 *sin6;
struct secasvar *sav;
- struct secasindex *saidx;
+ struct ip6_hdr *ip6;
+
+ /*
+ * Check system global policy controls.
+ */
+next:
+ isr = sp->req[*pidx];
+ if ((isr->saidx.proto == IPPROTO_ESP && !V_esp_enable) ||
+ (isr->saidx.proto == IPPROTO_AH && !V_ah_enable) ||
+ (isr->saidx.proto == IPPROTO_IPCOMP && !V_ipcomp_enable)) {
+ DPRINTF(("%s: IPsec outbound packet dropped due"
+ " to policy (check your sysctls)\n", __func__));
+ IPSEC_OSTAT_INC(isr->saidx.proto, pdrops);
+ *error = EHOSTUNREACH;
+ return (NULL);
+ }
+ /*
+ * Craft SA index to search for proper SA. Note that
+ * we only fillin unspecified SA peers for transport
+ * mode; for tunnel mode they must already be filled in.
+ */
+ if (isr->saidx.mode == IPSEC_MODE_TRANSPORT) {
+ saidx = &tmpsaidx;
+ *saidx = isr->saidx;
+ ip6 = mtod(m, struct ip6_hdr *);
+ if (saidx->src.sin6.sin6_len == 0) {
+ sin6 = (struct sockaddr_in6 *)&saidx->src;
+ sin6->sin6_len = sizeof(*sin6);
+ sin6->sin6_family = AF_INET6;
+ sin6->sin6_port = IPSEC_PORT_ANY;
+ sin6->sin6_addr = ip6->ip6_src;
+ if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_src)) {
+ /* fix scope id for comparing SPD */
+ sin6->sin6_addr.s6_addr16[1] = 0;
+ sin6->sin6_scope_id =
+ ntohs(ip6->ip6_src.s6_addr16[1]);
+ }
+ }
+ if (saidx->dst.sin6.sin6_len == 0) {
+ sin6 = (struct sockaddr_in6 *)&saidx->dst;
+ sin6->sin6_len = sizeof(*sin6);
+ sin6->sin6_family = AF_INET6;
+ sin6->sin6_port = IPSEC_PORT_ANY;
+ sin6->sin6_addr = ip6->ip6_dst;
+ if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_dst)) {
+ /* fix scope id for comparing SPD */
+ sin6->sin6_addr.s6_addr16[1] = 0;
+ sin6->sin6_scope_id =
+ ntohs(ip6->ip6_dst.s6_addr16[1]);
+ }
+ }
+ } else
+ saidx = &sp->req[*pidx]->saidx;
+ /*
+ * Lookup SA and validate it.
+ */
+ sav = key_allocsa_policy(sp, saidx, error);
+ if (sav == NULL) {
+ IPSEC6STAT_INC(ips_out_nosa);
+ if (*error != 0)
+ return (NULL);
+ if (ipsec_get_reqlevel(sp, *pidx) != IPSEC_LEVEL_REQUIRE) {
+ /*
+ * We have no SA and policy that doesn't require
+ * this IPsec transform, thus we can continue w/o
+ * IPsec processing, i.e. return EJUSTRETURN.
+ * But first check if there is some bundled transform.
+ */
+ if (sp->tcount > ++(*pidx))
+ goto next;
+ *error = EJUSTRETURN;
+ }
+ return (NULL);
+ }
+ IPSEC_ASSERT(sav->tdb_xform != NULL, ("SA with NULL tdb_xform"));
+ return (sav);
+}
+
+/*
+ * IPsec output logic for IPv6.
+ */
+static int
+ipsec6_perform_request(struct mbuf *m, struct secpolicy *sp, u_int idx)
+{
+ char sbuf[IPSEC_ADDRSTRLEN], dbuf[IPSEC_ADDRSTRLEN];
+ struct ipsec_ctx_data ctx;
+ union sockaddr_union *dst;
+ struct secasvar *sav;
+ struct ip6_hdr *ip6;
+ int error, i, off;
+
+ IPSEC_ASSERT(idx < sp->tcount, ("Wrong IPsec request index %d", idx));
+
+ sav = ipsec6_allocsa(m, sp, &idx, &error);
+ if (sav == NULL) {
+ if (error == EJUSTRETURN) { /* No IPsec required */
+ key_freesp(&sp);
+ return (error);
+ }
+ goto bad;
+ }
+
+ /* Fix IP length in case if it is not set yet. */
+ ip6 = mtod(m, struct ip6_hdr *);
+ ip6->ip6_plen = htons(m->m_pkthdr.len - sizeof(*ip6));
+
+ IPSEC_INIT_CTX(&ctx, &m, sav, AF_INET6, IPSEC_ENC_BEFORE);
+ if ((error = ipsec_run_hhooks(&ctx, HHOOK_TYPE_IPSEC_OUT)) != 0)
+ goto bad;
+
+ ip6 = mtod(m, struct ip6_hdr *); /* pfil can change mbuf */
+ dst = &sav->sah->saidx.dst;
+
+ /* Do the appropriate encapsulation, if necessary */
+ if (sp->req[idx]->saidx.mode == IPSEC_MODE_TUNNEL || /* Tunnel requ'd */
+ dst->sa.sa_family != AF_INET6 || /* PF mismatch */
+ ((dst->sa.sa_family == AF_INET6) &&
+ (!IN6_IS_ADDR_UNSPECIFIED(&dst->sin6.sin6_addr)) &&
+ (!in6_sa_equal_addrwithscope(&dst->sin6, &ip6->ip6_dst)))) {
+ if (m->m_pkthdr.len - sizeof(*ip6) > IPV6_MAXPACKET) {
+ /* No jumbogram support. */
+ error = ENXIO; /*XXX*/
+ goto bad;
+ }
+ error = ipsec_encap(&m, &sav->sah->saidx);
+ if (error != 0) {
+ DPRINTF(("%s: encapsulation for SA %s->%s "
+ "SPI 0x%08x failed with error %d\n", __func__,
+ ipsec_address(&sav->sah->saidx.src, sbuf,
+ sizeof(sbuf)),
+ ipsec_address(&sav->sah->saidx.dst, dbuf,
+ sizeof(dbuf)), ntohl(sav->spi), error));
+ /* XXXAE: IPSEC_OSTAT_INC(tunnel); */
+ goto bad;
+ }
+ }
+
+ IPSEC_INIT_CTX(&ctx, &m, sav, dst->sa.sa_family, IPSEC_ENC_AFTER);
+ if ((error = ipsec_run_hhooks(&ctx, HHOOK_TYPE_IPSEC_OUT)) != 0)
+ goto bad;
+
+ switch(dst->sa.sa_family) {
+#ifdef INET
+ case AF_INET:
+ {
+ struct ip *ip;
+ ip = mtod(m, struct ip *);
+ i = ip->ip_hl << 2;
+ off = offsetof(struct ip, ip_p);
+ }
+ break;
+#endif /* AF_INET */
+ case AF_INET6:
+ i = sizeof(struct ip6_hdr);
+ off = offsetof(struct ip6_hdr, ip6_nxt);
+ break;
+ default:
+ DPRINTF(("%s: unsupported protocol family %u\n",
+ __func__, dst->sa.sa_family));
+ error = EPFNOSUPPORT;
+ IPSEC_OSTAT_INC(sav->sah->saidx.proto, nopf);
+ goto bad;
+ }
+ error = (*sav->tdb_xform->xf_output)(m, sp, sav, idx, i, off);
+ if (error != 0) {
+ key_freesav(&sav);
+ key_freesp(&sp);
+ }
+ return (error);
+bad:
+ IPSEC6STAT_INC(ips_out_inval);
+ if (m != NULL)
+ m_freem(m);
+ if (sav != NULL)
+ key_freesav(&sav);
+ key_freesp(&sp);
+ return (error);
+}
+
+int
+ipsec6_process_packet(struct mbuf *m, struct secpolicy *sp,
+ struct inpcb *inp)
+{
+
+ return (ipsec6_perform_request(m, sp, 0));
+}
+
+static int
+ipsec6_common_output(struct mbuf *m, struct inpcb *inp, int forwarding)
+{
+ struct secpolicy *sp;
int error;
- IPSEC_ASSERT(m != NULL, ("null mbuf"));
- IPSEC_ASSERT(isr != NULL, ("null ISR"));
- IPSEC_ASSERT(isr->sp != NULL, ("NULL isr->sp"));
- sav = isr->sav;
- IPSEC_ASSERT(sav != NULL, ("null SA"));
- IPSEC_ASSERT(sav->sah != NULL, ("null SAH"));
+ /* Lookup for the corresponding outbound security policy */
+ sp = ipsec6_checkpolicy(m, inp, &error);
+ if (sp == NULL) {
+ if (error == -EINVAL) {
+ /* Discarded by policy. */
+ m_freem(m);
+ return (EACCES);
+ }
+ return (0); /* No IPsec required. */
+ }
+
+ if (!forwarding) {
+ /*
+ * 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_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 and releases reference to SP */
+ error = ipsec6_process_packet(m, sp, inp);
+ 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.
+ */
+ return (0);
+ }
+ if (error == 0)
+ return (EINPROGRESS); /* consumed by IPsec */
+ return (error);
+}
+
+/*
+ * IPSEC_OUTPUT() method implementation for IPv6.
+ * 0 - no IPsec handling needed
+ * other values - mbuf consumed by IPsec.
+ */
+int
+ipsec6_output(struct mbuf *m, struct inpcb *inp)
+{
+
+ /*
+ * 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)
+ return (0);
+
+ return (ipsec6_common_output(m, inp, 0));
+}
+
+/*
+ * IPSEC_FORWARD() method implementation for IPv6.
+ * 0 - no IPsec handling needed
+ * other values - mbuf consumed by IPsec.
+ */
+int
+ipsec6_forward(struct mbuf *m)
+{
+
+ /*
+ * Check if this packet has an active inbound SP and needs to be
+ * dropped instead of forwarded.
+ */
+ if (ipsec6_in_reject(m, NULL) != 0) {
+ m_freem(m);
+ return (EACCES);
+ }
+ return (ipsec6_common_output(m, NULL, 1));
+}
+#endif /* INET6 */
+
+int
+ipsec_process_done(struct mbuf *m, struct secpolicy *sp, struct secasvar *sav,
+ u_int idx)
+{
+ struct xform_history *xh;
+ struct secasindex *saidx;
+ struct m_tag *mtag;
+ int error;
saidx = &sav->sah->saidx;
switch (saidx->dst.sa.sa_family) {
@@ -136,21 +735,20 @@ ipsec_process_done(struct mbuf *m, struct ipsecrequest *isr)
}
/*
- * Add a record of what we've done or what needs to be done to the
- * packet.
+ * Add a record of what we've done to the packet.
*/
- mtag = m_tag_get(PACKET_TAG_IPSEC_OUT_DONE,
- sizeof(struct tdb_ident), M_NOWAIT);
+ mtag = m_tag_get(PACKET_TAG_IPSEC_OUT_DONE, sizeof(*xh), M_NOWAIT);
if (mtag == NULL) {
DPRINTF(("%s: could not get packet tag\n", __func__));
error = ENOMEM;
goto bad;
}
- tdbi = (struct tdb_ident *)(mtag + 1);
- tdbi->dst = saidx->dst;
- tdbi->proto = saidx->proto;
- tdbi->spi = sav->spi;
+ xh = (struct xform_history *)(mtag + 1);
+ xh->dst = saidx->dst;
+ xh->proto = saidx->proto;
+ xh->mode = saidx->mode;
+ xh->spi = sav->spi;
m_tag_prepend(m, mtag);
key_sa_recordxfer(sav, m); /* record data transfer */
@@ -162,32 +760,41 @@ ipsec_process_done(struct mbuf *m, struct ipsecrequest *isr)
* to set the packet on so we can unwind the stack before
* doing further processing.
*/
- if (isr->next) {
- /* XXX-BZ currently only support same AF bundles. */
+ if (++idx < sp->tcount) {
switch (saidx->dst.sa.sa_family) {
#ifdef INET
case AF_INET:
+ key_freesav(&sav);
IPSECSTAT_INC(ips_out_bundlesa);
- return (ipsec4_process_packet(m, isr->next));
+ return (ipsec4_perform_request(m, sp, idx));
/* NOTREACHED */
#endif
-#ifdef notyet
#ifdef INET6
case AF_INET6:
- /* XXX */
+ key_freesav(&sav);
IPSEC6STAT_INC(ips_out_bundlesa);
- return (ipsec6_process_packet(m, isr->next));
+ return (ipsec6_perform_request(m, sp, idx));
/* NOTREACHED */
#endif /* INET6 */
-#endif
default:
DPRINTF(("%s: unknown protocol family %u\n", __func__,
saidx->dst.sa.sa_family));
- error = ENXIO;
+ error = EPFNOSUPPORT;
goto bad;
}
}
+ key_freesp(&sp), sp = NULL; /* Release reference to SP */
+#ifdef INET
+ /*
+ * Do UDP encapsulation if SA requires it.
+ */
+ if (sav->natt != NULL) {
+ error = udp_ipsec_output(m, sav);
+ if (error != 0)
+ goto bad;
+ }
+#endif /* INET */
/*
* We're done with IPsec processing, transmit the packet using the
* appropriate network protocol (IP or IPv6).
@@ -195,225 +802,68 @@ ipsec_process_done(struct mbuf *m, struct ipsecrequest *isr)
switch (saidx->dst.sa.sa_family) {
#ifdef INET
case AF_INET:
-#ifdef IPSEC_NAT_T
- /*
- * If NAT-T is enabled, now that all IPsec processing is done
- * insert UDP encapsulation header after IP header.
- */
- if (sav->natt_type) {
- struct ip *ip = mtod(m, struct ip *);
- const int hlen = (ip->ip_hl << 2);
- int size, off;
- struct mbuf *mi;
- struct udphdr *udp;
-
- size = sizeof(struct udphdr);
- if (sav->natt_type == UDP_ENCAP_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.
- */
- size += sizeof(u_int64_t);
- }
- mi = m_makespace(m, hlen, size, &off);
- if (mi == NULL) {
- DPRINTF(("%s: m_makespace for udphdr failed\n",
- __func__));
- error = ENOBUFS;
- goto bad;
- }
-
- udp = (struct udphdr *)(mtod(mi, caddr_t) + off);
- if (sav->natt_type == UDP_ENCAP_ESPINUDP_NON_IKE)
- udp->uh_sport = htons(UDP_ENCAP_ESPINUDP_PORT);
- else
- udp->uh_sport =
- KEY_PORTFROMSADDR(&sav->sah->saidx.src);
- udp->uh_dport = KEY_PORTFROMSADDR(&sav->sah->saidx.dst);
- udp->uh_sum = 0;
- udp->uh_ulen = htons(m->m_pkthdr.len - hlen);
- ip->ip_len = htons(m->m_pkthdr.len);
- ip->ip_p = IPPROTO_UDP;
-
- if (sav->natt_type == UDP_ENCAP_ESPINUDP_NON_IKE)
- *(u_int64_t *)(udp + 1) = 0;
- }
-#endif /* IPSEC_NAT_T */
-
+ key_freesav(&sav);
return ip_output(m, NULL, NULL, IP_RAWOUTPUT, NULL, NULL);
#endif /* INET */
#ifdef INET6
case AF_INET6:
- /*
- * We don't need massage, IPv6 header fields are always in
- * net endian.
- */
+ key_freesav(&sav);
return ip6_output(m, NULL, NULL, 0, NULL, NULL, NULL);
#endif /* INET6 */
}
panic("ipsec_process_done");
bad:
m_freem(m);
+ key_freesav(&sav);
+ if (sp != NULL)
+ key_freesp(&sp);
return (error);
}
-static struct ipsecrequest *
-ipsec_nextisr(
- struct mbuf *m,
- struct ipsecrequest *isr,
- int af,
- struct secasindex *saidx,
- int *error
-)
+/*
+ * ipsec_prepend() is optimized version of M_PREPEND().
+ * ipsec_encap() is called by IPsec output routine for tunnel mode SA.
+ * It is expected that after IP encapsulation some IPsec transform will
+ * be performed. Each IPsec transform inserts its variable length header
+ * just after outer IP header using m_makespace(). If given mbuf has not
+ * enough free space at the beginning, we allocate new mbuf and reserve
+ * some space at the beginning and at the end.
+ * This helps avoid allocating of new mbuf and data copying in m_makespace(),
+ * we place outer header in the middle of mbuf's data with reserved leading
+ * and trailing space:
+ * [ LEADINGSPACE ][ Outer IP header ][ TRAILINGSPACE ]
+ * LEADINGSPACE will be used to add ethernet header, TRAILINGSPACE will
+ * be used to inject AH/ESP/IPCOMP header.
+ */
+#define IPSEC_TRAILINGSPACE (sizeof(struct udphdr) +/* NAT-T */ \
+ max(sizeof(struct newesp) + EALG_MAX_BLOCK_LEN, /* ESP + IV */ \
+ sizeof(struct newah) + HASH_MAX_LEN /* AH + ICV */))
+static struct mbuf *
+ipsec_prepend(struct mbuf *m, int len, int how)
{
-#define IPSEC_OSTAT(name) do { \
- if (isr->saidx.proto == IPPROTO_ESP) \
- ESPSTAT_INC(esps_##name); \
- else if (isr->saidx.proto == IPPROTO_AH)\
- AHSTAT_INC(ahs_##name); \
- else \
- IPCOMPSTAT_INC(ipcomps_##name); \
-} while (0)
- struct secasvar *sav;
-
- IPSECREQUEST_LOCK_ASSERT(isr);
-
- IPSEC_ASSERT(af == AF_INET || af == AF_INET6,
- ("invalid address family %u", af));
-again:
- /*
- * Craft SA index to search for proper SA. Note that
- * we only fillin unspecified SA peers for transport
- * mode; for tunnel mode they must already be filled in.
- */
- *saidx = isr->saidx;
- if (isr->saidx.mode == IPSEC_MODE_TRANSPORT) {
- /* Fillin unspecified SA peers only for transport mode */
- if (af == AF_INET) {
- struct sockaddr_in *sin;
- struct ip *ip = mtod(m, struct ip *);
-
- if (saidx->src.sa.sa_len == 0) {
- sin = &saidx->src.sin;
- sin->sin_len = sizeof(*sin);
- sin->sin_family = AF_INET;
- sin->sin_port = IPSEC_PORT_ANY;
- sin->sin_addr = ip->ip_src;
- }
- if (saidx->dst.sa.sa_len == 0) {
- sin = &saidx->dst.sin;
- sin->sin_len = sizeof(*sin);
- sin->sin_family = AF_INET;
- sin->sin_port = IPSEC_PORT_ANY;
- sin->sin_addr = ip->ip_dst;
- }
- } else {
- struct sockaddr_in6 *sin6;
- struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *);
-
- if (saidx->src.sin6.sin6_len == 0) {
- sin6 = (struct sockaddr_in6 *)&saidx->src;
- sin6->sin6_len = sizeof(*sin6);
- sin6->sin6_family = AF_INET6;
- sin6->sin6_port = IPSEC_PORT_ANY;
- sin6->sin6_addr = ip6->ip6_src;
- if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_src)) {
- /* fix scope id for comparing SPD */
- sin6->sin6_addr.s6_addr16[1] = 0;
- sin6->sin6_scope_id =
- ntohs(ip6->ip6_src.s6_addr16[1]);
- }
- }
- if (saidx->dst.sin6.sin6_len == 0) {
- sin6 = (struct sockaddr_in6 *)&saidx->dst;
- sin6->sin6_len = sizeof(*sin6);
- sin6->sin6_family = AF_INET6;
- sin6->sin6_port = IPSEC_PORT_ANY;
- sin6->sin6_addr = ip6->ip6_dst;
- if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_dst)) {
- /* fix scope id for comparing SPD */
- sin6->sin6_addr.s6_addr16[1] = 0;
- sin6->sin6_scope_id =
- ntohs(ip6->ip6_dst.s6_addr16[1]);
- }
- }
- }
+ struct mbuf *n;
+
+ M_ASSERTPKTHDR(m);
+ IPSEC_ASSERT(len < MHLEN, ("wrong length"));
+ if (M_LEADINGSPACE(m) >= len) {
+ /* No need to allocate new mbuf. */
+ m->m_data -= len;
+ m->m_len += len;
+ m->m_pkthdr.len += len;
+ return (m);
}
-
- /*
- * Lookup SA and validate it.
- */
- *error = key_checkrequest(isr, saidx);
- if (*error != 0) {
- /*
- * IPsec processing is required, but no SA found.
- * I assume that key_acquire() had been called
- * to get/establish the SA. Here I discard
- * this packet because it is responsibility for
- * upper layer to retransmit the packet.
- */
- switch(af) {
- case AF_INET:
- IPSECSTAT_INC(ips_out_nosa);
- break;
-#ifdef INET6
- case AF_INET6:
- IPSEC6STAT_INC(ips_out_nosa);
- break;
-#endif
- }
- goto bad;
- }
- sav = isr->sav;
- if (sav == NULL) {
- IPSEC_ASSERT(ipsec_get_reqlevel(isr) == IPSEC_LEVEL_USE,
- ("no SA found, but required; level %u",
- ipsec_get_reqlevel(isr)));
- IPSECREQUEST_UNLOCK(isr);
- isr = isr->next;
- /*
- * If isr is NULL, we found a 'use' policy w/o SA.
- * Return w/o error and w/o isr so we can drop out
- * and continue w/o IPsec processing.
- */
- if (isr == NULL)
- return isr;
- IPSECREQUEST_LOCK(isr);
- goto again;
- }
-
- /*
- * Check system global policy controls.
- */
- if ((isr->saidx.proto == IPPROTO_ESP && !V_esp_enable) ||
- (isr->saidx.proto == IPPROTO_AH && !V_ah_enable) ||
- (isr->saidx.proto == IPPROTO_IPCOMP && !V_ipcomp_enable)) {
- DPRINTF(("%s: IPsec outbound packet dropped due"
- " to policy (check your sysctls)\n", __func__));
- IPSEC_OSTAT(pdrops);
- *error = EHOSTUNREACH;
- goto bad;
- }
-
- /*
- * Sanity check the SA contents for the caller
- * before they invoke the xform output method.
- */
- if (sav->tdb_xform == NULL) {
- DPRINTF(("%s: no transform for SA\n", __func__));
- IPSEC_OSTAT(noxform);
- *error = EHOSTUNREACH;
- goto bad;
+ n = m_gethdr(how, m->m_type);
+ if (n == NULL) {
+ m_freem(m);
+ return (NULL);
}
- return isr;
-bad:
- IPSEC_ASSERT(*error != 0, ("error return w/ no error code"));
- IPSECREQUEST_UNLOCK(isr);
- return NULL;
-#undef IPSEC_OSTAT
+ m_move_pkthdr(n, m);
+ n->m_next = m;
+ if (len + IPSEC_TRAILINGSPACE < M_SIZE(n))
+ m_align(n, len + IPSEC_TRAILINGSPACE);
+ n->m_len = len;
+ n->m_pkthdr.len += len;
+ return (n);
}
static int
@@ -467,7 +917,7 @@ ipsec_encap(struct mbuf **mp, struct secasindex *saidx)
saidx->src.sin.sin_addr.s_addr == INADDR_ANY ||
saidx->dst.sin.sin_addr.s_addr == INADDR_ANY)
return (EINVAL);
- M_PREPEND(*mp, sizeof(struct ip), M_NOWAIT);
+ *mp = ipsec_prepend(*mp, sizeof(struct ip), M_NOWAIT);
if (*mp == NULL)
return (ENOBUFS);
ip = mtod(*mp, struct ip *);
@@ -490,7 +940,7 @@ ipsec_encap(struct mbuf **mp, struct secasindex *saidx)
IN6_IS_ADDR_UNSPECIFIED(&saidx->src.sin6.sin6_addr) ||
IN6_IS_ADDR_UNSPECIFIED(&saidx->dst.sin6.sin6_addr))
return (EINVAL);
- M_PREPEND(*mp, sizeof(struct ip6_hdr), M_NOWAIT);
+ *mp = ipsec_prepend(*mp, sizeof(struct ip6_hdr), M_NOWAIT);
if (*mp == NULL)
return (ENOBUFS);
ip6 = mtod(*mp, struct ip6_hdr *);
@@ -515,221 +965,7 @@ ipsec_encap(struct mbuf **mp, struct secasindex *saidx)
default:
return (EAFNOSUPPORT);
}
+ (*mp)->m_flags &= ~(M_BCAST | M_MCAST);
return (0);
}
-#ifdef INET
-/*
- * IPsec output logic for IPv4.
- */
-int
-ipsec4_process_packet(struct mbuf *m, struct ipsecrequest *isr)
-{
- char sbuf[INET6_ADDRSTRLEN], dbuf[INET6_ADDRSTRLEN];
- struct ipsec_ctx_data ctx;
- union sockaddr_union *dst;
- struct secasindex saidx;
- struct secasvar *sav;
- struct ip *ip;
- int error, i, off;
-
- IPSEC_ASSERT(m != NULL, ("null mbuf"));
- IPSEC_ASSERT(isr != NULL, ("null isr"));
-
- IPSECREQUEST_LOCK(isr); /* insure SA contents don't change */
-
- isr = ipsec_nextisr(m, isr, AF_INET, &saidx, &error);
- if (isr == NULL) {
- if (error != 0)
- goto bad;
- return EJUSTRETURN;
- }
-
- sav = isr->sav;
- if (m->m_len < sizeof(struct ip) &&
- (m = m_pullup(m, sizeof (struct ip))) == NULL) {
- error = ENOBUFS;
- goto bad;
- }
-
- IPSEC_INIT_CTX(&ctx, &m, sav, AF_INET, IPSEC_ENC_BEFORE);
- if ((error = ipsec_run_hhooks(&ctx, HHOOK_TYPE_IPSEC_OUT)) != 0)
- goto bad;
-
- ip = mtod(m, struct ip *);
- dst = &sav->sah->saidx.dst;
- /* Do the appropriate encapsulation, if necessary */
- if (isr->saidx.mode == IPSEC_MODE_TUNNEL || /* Tunnel requ'd */
- dst->sa.sa_family != AF_INET || /* PF mismatch */
- (dst->sa.sa_family == AF_INET && /* Proxy */
- dst->sin.sin_addr.s_addr != INADDR_ANY &&
- dst->sin.sin_addr.s_addr != ip->ip_dst.s_addr)) {
- /* Fix IPv4 header checksum and length */
- ip->ip_len = htons(m->m_pkthdr.len);
- ip->ip_sum = 0;
- ip->ip_sum = in_cksum(m, ip->ip_hl << 2);
- error = ipsec_encap(&m, &sav->sah->saidx);
- if (error != 0) {
- DPRINTF(("%s: encapsulation for SA %s->%s "
- "SPI 0x%08x failed with error %d\n", __func__,
- ipsec_address(&sav->sah->saidx.src, sbuf,
- sizeof(sbuf)),
- ipsec_address(&sav->sah->saidx.dst, dbuf,
- sizeof(dbuf)), ntohl(sav->spi), error));
- goto bad;
- }
- }
-
- IPSEC_INIT_CTX(&ctx, &m, sav, dst->sa.sa_family, IPSEC_ENC_AFTER);
- if ((error = ipsec_run_hhooks(&ctx, HHOOK_TYPE_IPSEC_OUT)) != 0)
- goto bad;
-
- /*
- * Dispatch to the appropriate IPsec transform logic. The
- * packet will be returned for transmission after crypto
- * processing, etc. are completed.
- *
- * NB: m & sav are ``passed to caller'' who's responsible for
- * for reclaiming their resources.
- */
- switch(dst->sa.sa_family) {
- case AF_INET:
- ip = mtod(m, struct ip *);
- i = ip->ip_hl << 2;
- off = offsetof(struct ip, ip_p);
- break;
-#ifdef INET6
- case AF_INET6:
- i = sizeof(struct ip6_hdr);
- off = offsetof(struct ip6_hdr, ip6_nxt);
- break;
-#endif /* INET6 */
- default:
- DPRINTF(("%s: unsupported protocol family %u\n",
- __func__, dst->sa.sa_family));
- error = EPFNOSUPPORT;
- IPSECSTAT_INC(ips_out_inval);
- goto bad;
- }
- error = (*sav->tdb_xform->xf_output)(m, isr, NULL, i, off);
- IPSECREQUEST_UNLOCK(isr);
- return (error);
-bad:
- if (isr)
- IPSECREQUEST_UNLOCK(isr);
- if (m)
- m_freem(m);
- return error;
-}
-#endif
-
-
-#ifdef INET6
-static int
-in6_sa_equal_addrwithscope(const struct sockaddr_in6 *sa, const struct in6_addr *ia)
-{
- struct in6_addr ia2;
-
- memcpy(&ia2, &sa->sin6_addr, sizeof(ia2));
- if (IN6_IS_SCOPE_LINKLOCAL(&sa->sin6_addr))
- ia2.s6_addr16[1] = htons(sa->sin6_scope_id);
-
- return IN6_ARE_ADDR_EQUAL(ia, &ia2);
-}
-
-/*
- * IPsec output logic for IPv6.
- */
-int
-ipsec6_process_packet(struct mbuf *m, struct ipsecrequest *isr)
-{
- char sbuf[INET6_ADDRSTRLEN], dbuf[INET6_ADDRSTRLEN];
- struct ipsec_ctx_data ctx;
- struct secasindex saidx;
- struct secasvar *sav;
- struct ip6_hdr *ip6;
- int error, i, off;
- union sockaddr_union *dst;
-
- IPSEC_ASSERT(m != NULL, ("ipsec6_process_packet: null mbuf"));
- IPSEC_ASSERT(isr != NULL, ("ipsec6_process_packet: null isr"));
-
- IPSECREQUEST_LOCK(isr); /* insure SA contents don't change */
-
- isr = ipsec_nextisr(m, isr, AF_INET6, &saidx, &error);
- if (isr == NULL) {
- if (error != 0)
- goto bad;
- return EJUSTRETURN;
- }
- sav = isr->sav;
- dst = &sav->sah->saidx.dst;
-
- IPSEC_INIT_CTX(&ctx, &m, sav, AF_INET6, IPSEC_ENC_BEFORE);
- if ((error = ipsec_run_hhooks(&ctx, HHOOK_TYPE_IPSEC_OUT)) != 0)
- goto bad;
-
- ip6 = mtod(m, struct ip6_hdr *);
- ip6->ip6_plen = htons(m->m_pkthdr.len - sizeof(*ip6));
-
- /* Do the appropriate encapsulation, if necessary */
- if (isr->saidx.mode == IPSEC_MODE_TUNNEL || /* Tunnel requ'd */
- dst->sa.sa_family != AF_INET6 || /* PF mismatch */
- ((dst->sa.sa_family == AF_INET6) &&
- (!IN6_IS_ADDR_UNSPECIFIED(&dst->sin6.sin6_addr)) &&
- (!in6_sa_equal_addrwithscope(&dst->sin6,
- &ip6->ip6_dst)))) {
- if (m->m_pkthdr.len - sizeof(*ip6) > IPV6_MAXPACKET) {
- /* No jumbogram support. */
- error = ENXIO; /*XXX*/
- goto bad;
- }
- error = ipsec_encap(&m, &sav->sah->saidx);
- if (error != 0) {
- DPRINTF(("%s: encapsulation for SA %s->%s "
- "SPI 0x%08x failed with error %d\n", __func__,
- ipsec_address(&sav->sah->saidx.src, sbuf,
- sizeof(sbuf)),
- ipsec_address(&sav->sah->saidx.dst, dbuf,
- sizeof(dbuf)), ntohl(sav->spi), error));
- goto bad;
- }
- }
-
- IPSEC_INIT_CTX(&ctx, &m, sav, dst->sa.sa_family, IPSEC_ENC_AFTER);
- if ((error = ipsec_run_hhooks(&ctx, HHOOK_TYPE_IPSEC_OUT)) != 0)
- goto bad;
-
- switch(dst->sa.sa_family) {
-#ifdef INET
- case AF_INET:
- {
- struct ip *ip;
- ip = mtod(m, struct ip *);
- i = ip->ip_hl << 2;
- off = offsetof(struct ip, ip_p);
- }
- break;
-#endif /* AF_INET */
- case AF_INET6:
- i = sizeof(struct ip6_hdr);
- off = offsetof(struct ip6_hdr, ip6_nxt);
- break;
- default:
- DPRINTF(("%s: unsupported protocol family %u\n",
- __func__, dst->sa.sa_family));
- error = EPFNOSUPPORT;
- goto bad;
- }
- error = (*sav->tdb_xform->xf_output)(m, isr, NULL, i, off);
- IPSECREQUEST_UNLOCK(isr);
- return error;
-bad:
- IPSEC6STAT_INC(ips_out_inval);
- if (isr)
- IPSECREQUEST_UNLOCK(isr);
- if (m)
- m_freem(m);
- return error;
-}
-#endif /*INET6*/
diff --git a/sys/netipsec/ipsec_pcb.c b/sys/netipsec/ipsec_pcb.c
new file mode 100644
index 0000000..b6ed5f3
--- /dev/null
+++ b/sys/netipsec/ipsec_pcb.c
@@ -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
+
diff --git a/sys/netipsec/ipsec_support.h b/sys/netipsec/ipsec_support.h
new file mode 100644
index 0000000..b72aee2
--- /dev/null
+++ b/sys/netipsec/ipsec_support.h
@@ -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_ */
diff --git a/sys/netipsec/key.c b/sys/netipsec/key.c
index bacab2b7..a7ae8be 100644
--- a/sys/netipsec/key.c
+++ b/sys/netipsec/key.c
@@ -42,6 +42,7 @@
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
+#include <sys/fnv_hash.h>
#include <sys/lock.h>
#include <sys/mutex.h>
#include <sys/mbuf.h>
@@ -58,6 +59,8 @@
#include <sys/refcount.h>
#include <sys/syslog.h>
+#include <vm/uma.h>
+
#include <net/if.h>
#include <net/if_var.h>
#include <net/vnet.h>
@@ -67,6 +70,7 @@
#include <netinet/in_systm.h>
#include <netinet/ip.h>
#include <netinet/in_var.h>
+#include <netinet/udp.h>
#ifdef INET6
#include <netinet/ip6.h>
@@ -74,13 +78,6 @@
#include <netinet6/ip6_var.h>
#endif /* INET6 */
-#if defined(INET) || defined(INET6)
-#include <netinet/in_pcb.h>
-#endif
-#ifdef INET6
-#include <netinet6/in6_pcb.h>
-#endif /* INET6 */
-
#include <net/pfkeyv2.h>
#include <netipsec/keydb.h>
#include <netipsec/key.h>
@@ -93,7 +90,7 @@
#endif
#include <netipsec/xform.h>
-
+#include <machine/in_cksum.h>
#include <machine/stdarg.h>
/* randomness */
@@ -141,10 +138,17 @@ static VNET_DEFINE(int, key_preferred_oldsa) = 1;
static VNET_DEFINE(u_int32_t, acq_seq) = 0;
#define V_acq_seq VNET(acq_seq)
- /* SPD */
-static VNET_DEFINE(TAILQ_HEAD(_sptree, secpolicy), sptree[IPSEC_DIR_MAX]);
+static VNET_DEFINE(uint32_t, sp_genid) = 0;
+#define V_sp_genid VNET(sp_genid)
+
+/* SPD */
+TAILQ_HEAD(secpolicy_queue, secpolicy);
+LIST_HEAD(secpolicy_list, secpolicy);
+static VNET_DEFINE(struct secpolicy_queue, sptree[IPSEC_DIR_MAX]);
+static VNET_DEFINE(struct secpolicy_queue, sptree_ifnet[IPSEC_DIR_MAX]);
static struct rmlock sptree_lock;
#define V_sptree VNET(sptree)
+#define V_sptree_ifnet VNET(sptree_ifnet)
#define SPTREE_LOCK_INIT() rm_init(&sptree_lock, "sptree")
#define SPTREE_LOCK_DESTROY() rm_destroy(&sptree_lock)
#define SPTREE_RLOCK_TRACKER struct rm_priotracker sptree_tracker
@@ -156,16 +160,96 @@ static struct rmlock sptree_lock;
#define SPTREE_WLOCK_ASSERT() rm_assert(&sptree_lock, RA_WLOCKED)
#define SPTREE_UNLOCK_ASSERT() rm_assert(&sptree_lock, RA_UNLOCKED)
-static VNET_DEFINE(LIST_HEAD(_sahtree, secashead), sahtree); /* SAD */
+/* Hash table for lookup SP using unique id */
+static VNET_DEFINE(struct secpolicy_list *, sphashtbl);
+static VNET_DEFINE(u_long, sphash_mask);
+#define V_sphashtbl VNET(sphashtbl)
+#define V_sphash_mask VNET(sphash_mask)
+
+#define SPHASH_NHASH_LOG2 7
+#define SPHASH_NHASH (1 << SPHASH_NHASH_LOG2)
+#define SPHASH_HASHVAL(id) (key_u32hash(id) & V_sphash_mask)
+#define SPHASH_HASH(id) &V_sphashtbl[SPHASH_HASHVAL(id)]
+
+/* SAD */
+TAILQ_HEAD(secashead_queue, secashead);
+LIST_HEAD(secashead_list, secashead);
+static VNET_DEFINE(struct secashead_queue, sahtree);
+static struct rmlock sahtree_lock;
#define V_sahtree VNET(sahtree)
-static struct mtx sahtree_lock;
-#define SAHTREE_LOCK_INIT() \
- mtx_init(&sahtree_lock, "sahtree", \
- "fast ipsec security association database", MTX_DEF)
-#define SAHTREE_LOCK_DESTROY() mtx_destroy(&sahtree_lock)
-#define SAHTREE_LOCK() mtx_lock(&sahtree_lock)
-#define SAHTREE_UNLOCK() mtx_unlock(&sahtree_lock)
-#define SAHTREE_LOCK_ASSERT() mtx_assert(&sahtree_lock, MA_OWNED)
+#define SAHTREE_LOCK_INIT() rm_init(&sahtree_lock, "sahtree")
+#define SAHTREE_LOCK_DESTROY() rm_destroy(&sahtree_lock)
+#define SAHTREE_RLOCK_TRACKER struct rm_priotracker sahtree_tracker
+#define SAHTREE_RLOCK() rm_rlock(&sahtree_lock, &sahtree_tracker)
+#define SAHTREE_RUNLOCK() rm_runlock(&sahtree_lock, &sahtree_tracker)
+#define SAHTREE_RLOCK_ASSERT() rm_assert(&sahtree_lock, RA_RLOCKED)
+#define SAHTREE_WLOCK() rm_wlock(&sahtree_lock)
+#define SAHTREE_WUNLOCK() rm_wunlock(&sahtree_lock)
+#define SAHTREE_WLOCK_ASSERT() rm_assert(&sahtree_lock, RA_WLOCKED)
+#define SAHTREE_UNLOCK_ASSERT() rm_assert(&sahtree_lock, RA_UNLOCKED)
+
+/* Hash table for lookup in SAD using SA addresses */
+static VNET_DEFINE(struct secashead_list *, sahaddrhashtbl);
+static VNET_DEFINE(u_long, sahaddrhash_mask);
+#define V_sahaddrhashtbl VNET(sahaddrhashtbl)
+#define V_sahaddrhash_mask VNET(sahaddrhash_mask)
+
+#define SAHHASH_NHASH_LOG2 7
+#define SAHHASH_NHASH (1 << SAHHASH_NHASH_LOG2)
+#define SAHADDRHASH_HASHVAL(saidx) \
+ (key_saidxhash(saidx) & V_sahaddrhash_mask)
+#define SAHADDRHASH_HASH(saidx) \
+ &V_sahaddrhashtbl[SAHADDRHASH_HASHVAL(saidx)]
+
+/* Hash table for lookup in SAD using SPI */
+LIST_HEAD(secasvar_list, secasvar);
+static VNET_DEFINE(struct secasvar_list *, savhashtbl);
+static VNET_DEFINE(u_long, savhash_mask);
+#define V_savhashtbl VNET(savhashtbl)
+#define V_savhash_mask VNET(savhash_mask)
+#define SAVHASH_NHASH_LOG2 7
+#define SAVHASH_NHASH (1 << SAVHASH_NHASH_LOG2)
+#define SAVHASH_HASHVAL(spi) (key_u32hash(spi) & V_savhash_mask)
+#define SAVHASH_HASH(spi) &V_savhashtbl[SAVHASH_HASHVAL(spi)]
+
+static uint32_t
+key_saidxhash(const struct secasindex *saidx)
+{
+ uint32_t hval;
+
+ hval = fnv_32_buf(&saidx->proto, sizeof(saidx->proto),
+ FNV1_32_INIT);
+ switch (saidx->dst.sa.sa_family) {
+#ifdef INET
+ case AF_INET:
+ hval = fnv_32_buf(&saidx->src.sin.sin_addr,
+ sizeof(in_addr_t), hval);
+ hval = fnv_32_buf(&saidx->dst.sin.sin_addr,
+ sizeof(in_addr_t), hval);
+ break;
+#endif
+#ifdef INET6
+ case AF_INET6:
+ hval = fnv_32_buf(&saidx->src.sin6.sin6_addr,
+ sizeof(struct in6_addr), hval);
+ hval = fnv_32_buf(&saidx->dst.sin6.sin6_addr,
+ sizeof(struct in6_addr), hval);
+ break;
+#endif
+ default:
+ hval = 0;
+ ipseclog((LOG_DEBUG, "%s: unknown address family %d",
+ __func__, saidx->dst.sa.sa_family));
+ }
+ return (hval);
+}
+
+static uint32_t
+key_u32hash(uint32_t val)
+{
+
+ return (fnv_32_buf(&val, sizeof(val), FNV1_32_INIT));
+}
/* registed list */
static VNET_DEFINE(LIST_HEAD(_regtree, secreg), regtree[SADB_SATYPE_MAX + 1]);
@@ -178,16 +262,40 @@ static struct mtx regtree_lock;
#define REGTREE_UNLOCK() mtx_unlock(&regtree_lock)
#define REGTREE_LOCK_ASSERT() mtx_assert(&regtree_lock, MA_OWNED)
-static VNET_DEFINE(LIST_HEAD(_acqtree, secacq), acqtree); /* acquiring list */
+/* Acquiring list */
+LIST_HEAD(secacq_list, secacq);
+static VNET_DEFINE(struct secacq_list, acqtree);
#define V_acqtree VNET(acqtree)
static struct mtx acq_lock;
#define ACQ_LOCK_INIT() \
- mtx_init(&acq_lock, "acqtree", "fast ipsec acquire list", MTX_DEF)
+ mtx_init(&acq_lock, "acqtree", "ipsec SA acquiring list", MTX_DEF)
#define ACQ_LOCK_DESTROY() mtx_destroy(&acq_lock)
#define ACQ_LOCK() mtx_lock(&acq_lock)
#define ACQ_UNLOCK() mtx_unlock(&acq_lock)
#define ACQ_LOCK_ASSERT() mtx_assert(&acq_lock, MA_OWNED)
+/* Hash table for lookup in ACQ list using SA addresses */
+static VNET_DEFINE(struct secacq_list *, acqaddrhashtbl);
+static VNET_DEFINE(u_long, acqaddrhash_mask);
+#define V_acqaddrhashtbl VNET(acqaddrhashtbl)
+#define V_acqaddrhash_mask VNET(acqaddrhash_mask)
+
+/* Hash table for lookup in ACQ list using SEQ number */
+static VNET_DEFINE(struct secacq_list *, acqseqhashtbl);
+static VNET_DEFINE(u_long, acqseqhash_mask);
+#define V_acqseqhashtbl VNET(acqseqhashtbl)
+#define V_acqseqhash_mask VNET(acqseqhash_mask)
+
+#define ACQHASH_NHASH_LOG2 7
+#define ACQHASH_NHASH (1 << ACQHASH_NHASH_LOG2)
+#define ACQADDRHASH_HASHVAL(saidx) \
+ (key_saidxhash(saidx) & V_acqaddrhash_mask)
+#define ACQSEQHASH_HASHVAL(seq) \
+ (key_u32hash(seq) & V_acqseqhash_mask)
+#define ACQADDRHASH_HASH(saidx) \
+ &V_acqaddrhashtbl[ACQADDRHASH_HASHVAL(saidx)]
+#define ACQSEQHASH_HASH(seq) \
+ &V_acqseqhashtbl[ACQSEQHASH_HASHVAL(seq)]
/* SP acquiring list */
static VNET_DEFINE(LIST_HEAD(_spacqtree, secspacq), spacqtree);
#define V_spacqtree VNET(spacqtree)
@@ -200,22 +308,6 @@ static struct mtx spacq_lock;
#define SPACQ_UNLOCK() mtx_unlock(&spacq_lock)
#define SPACQ_LOCK_ASSERT() mtx_assert(&spacq_lock, MA_OWNED)
-/* search order for SAs */
-static const u_int saorder_state_valid_prefer_old[] = {
- SADB_SASTATE_DYING, SADB_SASTATE_MATURE,
-};
-static const u_int saorder_state_valid_prefer_new[] = {
- SADB_SASTATE_MATURE, SADB_SASTATE_DYING,
-};
-static const u_int saorder_state_alive[] = {
- /* except DEAD */
- SADB_SASTATE_MATURE, SADB_SASTATE_DYING, SADB_SASTATE_LARVAL
-};
-static const u_int saorder_state_any[] = {
- SADB_SASTATE_MATURE, SADB_SASTATE_DYING,
- SADB_SASTATE_LARVAL, SADB_SASTATE_DEAD
-};
-
static const int minsize[] = {
sizeof(struct sadb_msg), /* SADB_EXT_RESERVED */
sizeof(struct sadb_sa), /* SADB_EXT_SA */
@@ -243,7 +335,12 @@ static const int minsize[] = {
sizeof(struct sadb_address), /* SADB_X_EXT_NAT_T_OAI */
sizeof(struct sadb_address), /* SADB_X_EXT_NAT_T_OAR */
sizeof(struct sadb_x_nat_t_frag),/* SADB_X_EXT_NAT_T_FRAG */
+ sizeof(struct sadb_x_sa_replay), /* SADB_X_EXT_SA_REPLAY */
+ sizeof(struct sadb_address), /* SADB_X_EXT_NEW_ADDRESS_SRC */
+ sizeof(struct sadb_address), /* SADB_X_EXT_NEW_ADDRESS_DST */
};
+_Static_assert(sizeof(minsize)/sizeof(int) == SADB_EXT_MAX + 1, "minsize size mismatch");
+
static const int maxsize[] = {
sizeof(struct sadb_msg), /* SADB_EXT_RESERVED */
sizeof(struct sadb_sa), /* SADB_EXT_SA */
@@ -271,7 +368,23 @@ static const int maxsize[] = {
0, /* SADB_X_EXT_NAT_T_OAI */
0, /* SADB_X_EXT_NAT_T_OAR */
sizeof(struct sadb_x_nat_t_frag),/* SADB_X_EXT_NAT_T_FRAG */
+ sizeof(struct sadb_x_sa_replay), /* SADB_X_EXT_SA_REPLAY */
+ 0, /* SADB_X_EXT_NEW_ADDRESS_SRC */
+ 0, /* SADB_X_EXT_NEW_ADDRESS_DST */
};
+_Static_assert(sizeof(maxsize)/sizeof(int) == SADB_EXT_MAX + 1, "minsize size mismatch");
+
+/*
+ * Internal values for SA flags:
+ * SADB_X_EXT_F_CLONED means that SA was cloned by key_updateaddresses,
+ * thus we will not free the most of SA content in key_delsav().
+ */
+#define SADB_X_EXT_F_CLONED 0x80000000
+
+#define SADB_CHECKLEN(_mhp, _ext) \
+ ((_mhp)->extlen[(_ext)] < minsize[(_ext)] || (maxsize[(_ext)] != 0 && \
+ ((_mhp)->extlen[(_ext)] > maxsize[(_ext)])))
+#define SADB_CHECKHDR(_mhp, _ext) ((_mhp)->ext[(_ext)] == NULL)
static VNET_DEFINE(int, ipsec_esp_keymin) = 256;
static VNET_DEFINE(int, ipsec_esp_auth) = 0;
@@ -281,10 +394,26 @@ static VNET_DEFINE(int, ipsec_ah_keymin) = 128;
#define V_ipsec_esp_auth VNET(ipsec_esp_auth)
#define V_ipsec_ah_keymin VNET(ipsec_ah_keymin)
-#ifdef SYSCTL_DECL
-SYSCTL_DECL(_net_key);
+#ifdef IPSEC_DEBUG
+VNET_DEFINE(int, ipsec_debug) = 1;
+#else
+VNET_DEFINE(int, ipsec_debug) = 0;
#endif
+#ifdef INET
+SYSCTL_DECL(_net_inet_ipsec);
+SYSCTL_INT(_net_inet_ipsec, IPSECCTL_DEBUG, debug,
+ CTLFLAG_VNET | CTLFLAG_RW, &VNET_NAME(ipsec_debug), 0,
+ "Enable IPsec debugging output when set.");
+#endif
+#ifdef INET6
+SYSCTL_DECL(_net_inet6_ipsec6);
+SYSCTL_INT(_net_inet6_ipsec6, IPSECCTL_DEBUG, debug,
+ CTLFLAG_VNET | CTLFLAG_RW, &VNET_NAME(ipsec_debug), 0,
+ "Enable IPsec debugging output when set.");
+#endif
+
+SYSCTL_DECL(_net_key);
SYSCTL_INT(_net_key, KEYCTL_DEBUG_LEVEL, debug,
CTLFLAG_VNET | CTLFLAG_RW, &VNET_NAME(key_debug_level), 0, "");
@@ -334,35 +463,6 @@ SYSCTL_INT(_net_key, KEYCTL_PREFERED_OLDSA, preferred_oldsa,
#define __LIST_CHAINED(elm) \
(!((elm)->chain.le_next == NULL && (elm)->chain.le_prev == NULL))
-#define LIST_INSERT_TAIL(head, elm, type, field) \
-do {\
- struct type *curelm = LIST_FIRST(head); \
- if (curelm == NULL) {\
- LIST_INSERT_HEAD(head, elm, field); \
- } else { \
- while (LIST_NEXT(curelm, field)) \
- curelm = LIST_NEXT(curelm, field);\
- LIST_INSERT_AFTER(curelm, elm, field);\
- }\
-} while (0)
-
-#define KEY_CHKSASTATE(head, sav, name) \
-do { \
- if ((head) != (sav)) { \
- ipseclog((LOG_DEBUG, "%s: state mismatched (TREE=%d SA=%d)\n", \
- (name), (head), (sav))); \
- break; \
- } \
-} while (0)
-
-#define KEY_CHKSPDIR(head, sp, name) \
-do { \
- if ((head) != (sp)) { \
- ipseclog((LOG_DEBUG, "%s: direction mismatched (TREE=%d SP=%d), " \
- "anyway continue.\n", \
- (name), (head), (sp))); \
- } \
-} while (0)
MALLOC_DEFINE(M_IPSEC_SA, "secasvar", "ipsec security association");
MALLOC_DEFINE(M_IPSEC_SAH, "sahead", "ipsec sa head");
@@ -372,6 +472,17 @@ MALLOC_DEFINE(M_IPSEC_MISC, "ipsec-misc", "ipsec miscellaneous");
MALLOC_DEFINE(M_IPSEC_SAQ, "ipsec-saq", "ipsec sa acquire");
MALLOC_DEFINE(M_IPSEC_SAR, "ipsec-reg", "ipsec sa acquire");
+static VNET_DEFINE(uma_zone_t, key_lft_zone);
+#define V_key_lft_zone VNET(key_lft_zone)
+
+static LIST_HEAD(xforms_list, xformsw) xforms = LIST_HEAD_INITIALIZER();
+static struct mtx xforms_lock;
+#define XFORMS_LOCK_INIT() \
+ mtx_init(&xforms_lock, "xforms_list", "IPsec transforms list", MTX_DEF)
+#define XFORMS_LOCK_DESTROY() mtx_destroy(&xforms_lock)
+#define XFORMS_LOCK() mtx_lock(&xforms_lock)
+#define XFORMS_UNLOCK() mtx_unlock(&xforms_lock)
+
/*
* set parameters into secpolicyindex buffer.
* Must allocate secpolicyindex buffer passed to this function.
@@ -399,6 +510,8 @@ do { \
(idx)->reqid = (r); \
bcopy((s), &(idx)->src, ((const struct sockaddr *)(s))->sa_len); \
bcopy((d), &(idx)->dst, ((const struct sockaddr *)(d))->sa_len); \
+ key_porttosaddr(&(idx)->src.sa, 0); \
+ key_porttosaddr(&(idx)->dst.sa, 0); \
} while (0)
/* key statistics */
@@ -413,22 +526,59 @@ struct sadb_msghdr {
int extlen[SADB_EXT_MAX + 1];
};
+static struct supported_ealgs {
+ int sadb_alg;
+ const struct enc_xform *xform;
+} supported_ealgs[] = {
+ { SADB_EALG_DESCBC, &enc_xform_des },
+ { SADB_EALG_3DESCBC, &enc_xform_3des },
+ { SADB_X_EALG_AES, &enc_xform_rijndael128 },
+ { SADB_X_EALG_BLOWFISHCBC, &enc_xform_blf },
+ { SADB_X_EALG_CAST128CBC, &enc_xform_cast5 },
+ { SADB_EALG_NULL, &enc_xform_null },
+ { SADB_X_EALG_CAMELLIACBC, &enc_xform_camellia },
+ { SADB_X_EALG_AESCTR, &enc_xform_aes_icm },
+ { SADB_X_EALG_AESGCM16, &enc_xform_aes_nist_gcm },
+ { SADB_X_EALG_AESGMAC, &enc_xform_aes_nist_gmac },
+};
+
+static struct supported_aalgs {
+ int sadb_alg;
+ const struct auth_hash *xform;
+} supported_aalgs[] = {
+ { SADB_X_AALG_NULL, &auth_hash_null },
+ { SADB_AALG_MD5HMAC, &auth_hash_hmac_md5 },
+ { SADB_AALG_SHA1HMAC, &auth_hash_hmac_sha1 },
+ { SADB_X_AALG_RIPEMD160HMAC, &auth_hash_hmac_ripemd_160 },
+ { SADB_X_AALG_MD5, &auth_hash_key_md5 },
+ { SADB_X_AALG_SHA, &auth_hash_key_sha1 },
+ { SADB_X_AALG_SHA2_256, &auth_hash_hmac_sha2_256 },
+ { SADB_X_AALG_SHA2_384, &auth_hash_hmac_sha2_384 },
+ { SADB_X_AALG_SHA2_512, &auth_hash_hmac_sha2_512 },
+ { SADB_X_AALG_AES128GMAC, &auth_hash_nist_gmac_aes_128 },
+ { SADB_X_AALG_AES192GMAC, &auth_hash_nist_gmac_aes_192 },
+ { SADB_X_AALG_AES256GMAC, &auth_hash_nist_gmac_aes_256 },
+};
+
+static struct supported_calgs {
+ int sadb_alg;
+ const struct comp_algo *xform;
+} supported_calgs[] = {
+ { SADB_X_CALG_DEFLATE, &comp_algo_deflate },
+};
+
#ifndef IPSEC_DEBUG2
static struct callout key_timer;
#endif
-static struct secasvar *key_allocsa_policy(const struct secasindex *);
-static void key_freesp_so(struct secpolicy **);
-static struct secasvar *key_do_allocsa_policy(struct secashead *, u_int);
static void key_unlink(struct secpolicy *);
static struct secpolicy *key_getsp(struct secpolicyindex *);
static struct secpolicy *key_getspbyid(u_int32_t);
-static u_int32_t key_newreqid(void);
static struct mbuf *key_gather_mbuf(struct mbuf *,
const struct sadb_msghdr *, int, int, ...);
static int key_spdadd(struct socket *, struct mbuf *,
const struct sadb_msghdr *);
-static u_int32_t key_getnewspid(void);
+static uint32_t key_getnewspid(void);
static int key_spddelete(struct socket *, struct mbuf *,
const struct sadb_msghdr *);
static int key_spddelete2(struct socket *, struct mbuf *,
@@ -441,22 +591,25 @@ static int key_spddump(struct socket *, struct mbuf *,
const struct sadb_msghdr *);
static struct mbuf *key_setdumpsp(struct secpolicy *,
u_int8_t, u_int32_t, u_int32_t);
-static u_int key_getspreqmsglen(struct secpolicy *);
+static struct mbuf *key_sp2mbuf(struct secpolicy *);
+static size_t key_getspreqmsglen(struct secpolicy *);
static int key_spdexpire(struct secpolicy *);
static struct secashead *key_newsah(struct secasindex *);
+static void key_freesah(struct secashead **);
static void key_delsah(struct secashead *);
-static struct secasvar *key_newsav(struct mbuf *,
- const struct sadb_msghdr *, struct secashead *, int *,
- const char*, int);
-#define KEY_NEWSAV(m, sadb, sah, e) \
- key_newsav(m, sadb, sah, e, __FILE__, __LINE__)
+static struct secasvar *key_newsav(const struct sadb_msghdr *,
+ struct secasindex *, uint32_t, int *);
static void key_delsav(struct secasvar *);
+static void key_unlinksav(struct secasvar *);
static struct secashead *key_getsah(struct secasindex *);
-static struct secasvar *key_checkspidup(struct secasindex *, u_int32_t);
-static struct secasvar *key_getsavbyspi(struct secashead *, u_int32_t);
-static int key_setsaval(struct secasvar *, struct mbuf *,
- const struct sadb_msghdr *);
-static int key_mature(struct secasvar *);
+static int key_checkspidup(uint32_t);
+static struct secasvar *key_getsavbyspi(uint32_t);
+static int key_setnatt(struct secasvar *, const struct sadb_msghdr *);
+static int key_setsaval(struct secasvar *, const struct sadb_msghdr *);
+static int key_updatelifetimes(struct secasvar *, const struct sadb_msghdr *);
+static int key_updateaddresses(struct socket *, struct mbuf *,
+ const struct sadb_msghdr *, struct secasvar *, struct secasindex *);
+
static struct mbuf *key_setdumpsa(struct secasvar *, u_int8_t,
u_int8_t, u_int32_t, u_int32_t);
static struct mbuf *key_setsadbmsg(u_int8_t, u_int16_t, u_int8_t,
@@ -464,23 +617,16 @@ static struct mbuf *key_setsadbmsg(u_int8_t, u_int16_t, u_int8_t,
static struct mbuf *key_setsadbsa(struct secasvar *);
static struct mbuf *key_setsadbaddr(u_int16_t,
const struct sockaddr *, u_int8_t, u_int16_t);
-#ifdef IPSEC_NAT_T
static struct mbuf *key_setsadbxport(u_int16_t, u_int16_t);
static struct mbuf *key_setsadbxtype(u_int16_t);
-#endif
-static void key_porttosaddr(struct sockaddr *, u_int16_t);
-#define KEY_PORTTOSADDR(saddr, port) \
- key_porttosaddr((struct sockaddr *)(saddr), (port))
static struct mbuf *key_setsadbxsa2(u_int8_t, u_int32_t, u_int32_t);
+static struct mbuf *key_setsadbxsareplay(u_int32_t);
static struct mbuf *key_setsadbxpolicy(u_int16_t, u_int8_t,
u_int32_t, u_int32_t);
-static struct seckey *key_dup_keymsg(const struct sadb_key *, u_int,
- struct malloc_type *);
+static struct seckey *key_dup_keymsg(const struct sadb_key *, size_t,
+ struct malloc_type *);
static struct seclifetime *key_dup_lifemsg(const struct sadb_lifetime *src,
- struct malloc_type *type);
-#ifdef INET6
-static int key_ismyaddr6(struct sockaddr_in6 *);
-#endif
+ struct malloc_type *);
/* flags for key_cmpsaidx() */
#define CMP_HEAD 1 /* protocol, addresses. */
@@ -493,44 +639,39 @@ static int key_cmpspidx_exactly(struct secpolicyindex *,
struct secpolicyindex *);
static int key_cmpspidx_withmask(struct secpolicyindex *,
struct secpolicyindex *);
-static int key_sockaddrcmp(const struct sockaddr *,
- const struct sockaddr *, int);
static int key_bbcmp(const void *, const void *, u_int);
-static u_int16_t key_satype2proto(u_int8_t);
-static u_int8_t key_proto2satype(u_int16_t);
+static uint8_t key_satype2proto(uint8_t);
+static uint8_t key_proto2satype(uint8_t);
static int key_getspi(struct socket *, struct mbuf *,
const struct sadb_msghdr *);
-static u_int32_t key_do_getnewspi(struct sadb_spirange *,
- struct secasindex *);
+static uint32_t key_do_getnewspi(struct sadb_spirange *, struct secasindex *);
static int key_update(struct socket *, struct mbuf *,
const struct sadb_msghdr *);
-#ifdef IPSEC_DOSEQCHECK
-static struct secasvar *key_getsavbyseq(struct secashead *, u_int32_t);
-#endif
static int key_add(struct socket *, struct mbuf *,
const struct sadb_msghdr *);
-static int key_setident(struct secashead *, struct mbuf *,
- const struct sadb_msghdr *);
+static int key_setident(struct secashead *, const struct sadb_msghdr *);
static struct mbuf *key_getmsgbuf_x1(struct mbuf *,
const struct sadb_msghdr *);
static int key_delete(struct socket *, struct mbuf *,
const struct sadb_msghdr *);
static int key_delete_all(struct socket *, struct mbuf *,
- const struct sadb_msghdr *, u_int16_t);
+ const struct sadb_msghdr *, struct secasindex *);
+static void key_delete_xform(const struct xformsw *);
static int key_get(struct socket *, struct mbuf *,
const struct sadb_msghdr *);
static void key_getcomb_setlifetime(struct sadb_comb *);
-static struct mbuf *key_getcomb_esp(void);
+static struct mbuf *key_getcomb_ealg(void);
static struct mbuf *key_getcomb_ah(void);
static struct mbuf *key_getcomb_ipcomp(void);
static struct mbuf *key_getprop(const struct secasindex *);
static int key_acquire(const struct secasindex *, struct secpolicy *);
-static struct secacq *key_newacq(const struct secasindex *);
-static struct secacq *key_getacq(const struct secasindex *);
-static struct secacq *key_getacqbyseq(u_int32_t);
+static uint32_t key_newacq(const struct secasindex *, int *);
+static uint32_t key_getacq(const struct secasindex *, int *);
+static int key_acqdone(const struct secasindex *, uint32_t);
+static int key_acqreset(uint32_t);
static struct secspacq *key_newspacq(struct secpolicyindex *);
static struct secspacq *key_getspacq(struct secpolicyindex *);
static int key_acquire2(struct socket *, struct mbuf *,
@@ -547,39 +688,44 @@ static int key_promisc(struct socket *, struct mbuf *,
static int key_senderror(struct socket *, struct mbuf *, int);
static int key_validate_ext(const struct sadb_ext *, int);
static int key_align(struct mbuf *, struct sadb_msghdr *);
-static struct mbuf *key_setlifetime(struct seclifetime *src,
- u_int16_t exttype);
-static struct mbuf *key_setkey(struct seckey *src, u_int16_t exttype);
+static struct mbuf *key_setlifetime(struct seclifetime *, uint16_t);
+static struct mbuf *key_setkey(struct seckey *, uint16_t);
+static int xform_init(struct secasvar *, u_short);
+
+#define DBG_IPSEC_INITREF(t, p) do { \
+ refcount_init(&(p)->refcnt, 1); \
+ KEYDBG(KEY_STAMP, \
+ printf("%s: Initialize refcnt %s(%p) = %u\n", \
+ __func__, #t, (p), (p)->refcnt)); \
+} while (0)
+#define DBG_IPSEC_ADDREF(t, p) do { \
+ refcount_acquire(&(p)->refcnt); \
+ KEYDBG(KEY_STAMP, \
+ printf("%s: Acquire refcnt %s(%p) -> %u\n", \
+ __func__, #t, (p), (p)->refcnt)); \
+} while (0)
+#define DBG_IPSEC_DELREF(t, p) do { \
+ KEYDBG(KEY_STAMP, \
+ printf("%s: Release refcnt %s(%p) -> %u\n", \
+ __func__, #t, (p), (p)->refcnt - 1)); \
+ refcount_release(&(p)->refcnt); \
+} while (0)
-#if 0
-static const char *key_getfqdn(void);
-static const char *key_getuserfqdn(void);
-#endif
-static void key_sa_chgstate(struct secasvar *, u_int8_t);
+#define IPSEC_INITREF(t, p) refcount_init(&(p)->refcnt, 1)
+#define IPSEC_ADDREF(t, p) refcount_acquire(&(p)->refcnt)
+#define IPSEC_DELREF(t, p) refcount_release(&(p)->refcnt)
-static __inline void
-sa_initref(struct secasvar *sav)
-{
+#define SP_INITREF(p) IPSEC_INITREF(SP, p)
+#define SP_ADDREF(p) IPSEC_ADDREF(SP, p)
+#define SP_DELREF(p) IPSEC_DELREF(SP, p)
- refcount_init(&sav->refcnt, 1);
-}
-static __inline void
-sa_addref(struct secasvar *sav)
-{
+#define SAH_INITREF(p) IPSEC_INITREF(SAH, p)
+#define SAH_ADDREF(p) IPSEC_ADDREF(SAH, p)
+#define SAH_DELREF(p) IPSEC_DELREF(SAH, p)
- refcount_acquire(&sav->refcnt);
- IPSEC_ASSERT(sav->refcnt != 0, ("SA refcnt overflow"));
-}
-static __inline int
-sa_delref(struct secasvar *sav)
-{
-
- IPSEC_ASSERT(sav->refcnt > 0, ("SA refcnt underflow"));
- return (refcount_release(&sav->refcnt));
-}
-
-#define SP_ADDREF(p) refcount_acquire(&(p)->refcnt)
-#define SP_DELREF(p) refcount_release(&(p)->refcnt)
+#define SAV_INITREF(p) IPSEC_INITREF(SAV, p)
+#define SAV_ADDREF(p) IPSEC_ADDREF(SAV, p)
+#define SAV_DELREF(p) IPSEC_DELREF(SAV, p)
/*
* Update the refcnt while holding the SPTREE lock.
@@ -606,54 +752,49 @@ key_havesp(u_int dir)
/* %%% IPsec policy management */
/*
- * allocating a SP for OUTBOUND or INBOUND packet.
- * Must call key_freesp() later.
- * OUT: NULL: not found
- * others: found and return the pointer.
+ * Return current SPDB generation.
*/
-struct secpolicy *
-key_allocsp(struct secpolicyindex *spidx, u_int dir, const char* where,
- int tag)
+uint32_t
+key_getspgen(void)
{
- SPTREE_RLOCK_TRACKER;
- struct secpolicy *sp;
- IPSEC_ASSERT(spidx != NULL, ("null spidx"));
- IPSEC_ASSERT(dir == IPSEC_DIR_INBOUND || dir == IPSEC_DIR_OUTBOUND,
- ("invalid direction %u", dir));
+ return (V_sp_genid);
+}
- KEYDEBUG(KEYDEBUG_IPSEC_STAMP,
- printf("DP %s from %s:%u\n", __func__, where, tag));
+void
+key_bumpspgen(void)
+{
- /* get a SP entry */
- KEYDEBUG(KEYDEBUG_IPSEC_DATA,
- printf("*** objects\n");
- kdebug_secpolicyindex(spidx));
+ V_sp_genid++;
+}
- SPTREE_RLOCK();
- TAILQ_FOREACH(sp, &V_sptree[dir], chain) {
- KEYDEBUG(KEYDEBUG_IPSEC_DATA,
- printf("*** in SPD\n");
- kdebug_secpolicyindex(&sp->spidx));
- if (key_cmpspidx_withmask(&sp->spidx, spidx))
- goto found;
- }
- sp = NULL;
-found:
- if (sp) {
- /* sanity check */
- KEY_CHKSPDIR(sp->spidx.dir, dir, __func__);
-
- /* found a SPD entry */
- sp->lastused = time_second;
- SP_ADDREF(sp);
- }
- SPTREE_RUNLOCK();
+static int
+key_checksockaddrs(struct sockaddr *src, struct sockaddr *dst)
+{
- KEYDEBUG(KEYDEBUG_IPSEC_STAMP,
- printf("DP %s return SP:%p (ID=%u) refcnt %u\n", __func__,
- sp, sp ? sp->id : 0, sp ? sp->refcnt : 0));
- return sp;
+ /* family match */
+ if (src->sa_family != dst->sa_family)
+ return (EINVAL);
+ /* sa_len match */
+ if (src->sa_len != dst->sa_len)
+ return (EINVAL);
+ switch (src->sa_family) {
+#ifdef INET
+ case AF_INET:
+ if (src->sa_len != sizeof(struct sockaddr_in))
+ return (EINVAL);
+ break;
+#endif
+#ifdef INET6
+ case AF_INET6:
+ if (src->sa_len != sizeof(struct sockaddr_in6))
+ return (EINVAL);
+ break;
+#endif
+ default:
+ return (EAFNOSUPPORT);
+ }
+ return (0);
}
/*
@@ -663,395 +804,161 @@ found:
* others: found and return the pointer.
*/
struct secpolicy *
-key_allocsp2(u_int32_t spi, union sockaddr_union *dst, u_int8_t proto,
- u_int dir, const char* where, int tag)
+key_allocsp(struct secpolicyindex *spidx, u_int dir)
{
SPTREE_RLOCK_TRACKER;
struct secpolicy *sp;
- IPSEC_ASSERT(dst != NULL, ("null dst"));
+ IPSEC_ASSERT(spidx != NULL, ("null spidx"));
IPSEC_ASSERT(dir == IPSEC_DIR_INBOUND || dir == IPSEC_DIR_OUTBOUND,
("invalid direction %u", dir));
- KEYDEBUG(KEYDEBUG_IPSEC_STAMP,
- printf("DP %s from %s:%u\n", __func__, where, tag));
-
- /* get a SP entry */
- KEYDEBUG(KEYDEBUG_IPSEC_DATA,
- printf("*** objects\n");
- printf("spi %u proto %u dir %u\n", spi, proto, dir);
- kdebug_sockaddr(&dst->sa));
-
SPTREE_RLOCK();
TAILQ_FOREACH(sp, &V_sptree[dir], chain) {
- KEYDEBUG(KEYDEBUG_IPSEC_DATA,
- printf("*** in SPD\n");
- kdebug_secpolicyindex(&sp->spidx));
- /* compare simple values, then dst address */
- if (sp->spidx.ul_proto != proto)
- continue;
- /* NB: spi's must exist and match */
- if (!sp->req || !sp->req->sav || sp->req->sav->spi != spi)
- continue;
- if (key_sockaddrcmp(&sp->spidx.dst.sa, &dst->sa, 1) == 0)
- goto found;
+ if (key_cmpspidx_withmask(&sp->spidx, spidx)) {
+ SP_ADDREF(sp);
+ break;
+ }
}
- sp = NULL;
-found:
- if (sp) {
- /* sanity check */
- KEY_CHKSPDIR(sp->spidx.dir, dir, __func__);
+ SPTREE_RUNLOCK();
- /* found a SPD entry */
+ if (sp != NULL) { /* found a SPD entry */
sp->lastused = time_second;
- SP_ADDREF(sp);
+ KEYDBG(IPSEC_STAMP,
+ printf("%s: return SP(%p)\n", __func__, sp));
+ KEYDBG(IPSEC_DATA, kdebug_secpolicy(sp));
+ } else {
+ KEYDBG(IPSEC_DATA,
+ printf("%s: lookup failed for ", __func__);
+ kdebug_secpolicyindex(spidx, NULL));
}
- SPTREE_RUNLOCK();
-
- KEYDEBUG(KEYDEBUG_IPSEC_STAMP,
- printf("DP %s return SP:%p (ID=%u) refcnt %u\n", __func__,
- sp, sp ? sp->id : 0, sp ? sp->refcnt : 0));
- return sp;
+ return (sp);
}
-#if 0
/*
- * return a policy that matches this particular inbound packet.
- * XXX slow
+ * Allocating an SA entry for an *INBOUND* or *OUTBOUND* TCP packet, signed
+ * or should be signed by MD5 signature.
+ * We don't use key_allocsa() for such lookups, because we don't know SPI.
+ * Unlike ESP and AH protocols, SPI isn't transmitted in the TCP header with
+ * signed packet. We use SADB only as storage for password.
+ * OUT: positive: corresponding SA for given saidx found.
+ * NULL: SA not found
*/
-struct secpolicy *
-key_gettunnel(const struct sockaddr *osrc,
- const struct sockaddr *odst,
- const struct sockaddr *isrc,
- const struct sockaddr *idst,
- const char* where, int tag)
+struct secasvar *
+key_allocsa_tcpmd5(struct secasindex *saidx)
{
- struct secpolicy *sp;
- const int dir = IPSEC_DIR_INBOUND;
- struct ipsecrequest *r1, *r2, *p;
- struct secpolicyindex spidx;
-
- KEYDEBUG(KEYDEBUG_IPSEC_STAMP,
- printf("DP %s from %s:%u\n", __func__, where, tag));
-
- if (isrc->sa_family != idst->sa_family) {
- ipseclog((LOG_ERR, "%s: protocol family mismatched %d != %d\n.",
- __func__, isrc->sa_family, idst->sa_family));
- sp = NULL;
- goto done;
- }
+ SAHTREE_RLOCK_TRACKER;
+ struct secashead *sah;
+ struct secasvar *sav;
- SPTREE_LOCK();
- LIST_FOREACH(sp, &V_sptree[dir], chain) {
- if (sp->state == IPSEC_SPSTATE_DEAD)
+ IPSEC_ASSERT(saidx->proto == IPPROTO_TCP,
+ ("unexpected security protocol %u", saidx->proto));
+ IPSEC_ASSERT(saidx->mode == IPSEC_MODE_TCPMD5,
+ ("unexpected mode %u", saidx->mode));
+
+ SAHTREE_RLOCK();
+ LIST_FOREACH(sah, SAHADDRHASH_HASH(saidx), addrhash) {
+ KEYDBG(IPSEC_DUMP,
+ printf("%s: checking SAH\n", __func__);
+ kdebug_secash(sah, " "));
+ if (sah->saidx.proto != IPPROTO_TCP)
continue;
-
- r1 = r2 = NULL;
- for (p = sp->req; p; p = p->next) {
- if (p->saidx.mode != IPSEC_MODE_TUNNEL)
- continue;
-
- r1 = r2;
- r2 = p;
-
- if (!r1) {
- /* here we look at address matches only */
- spidx = sp->spidx;
- if (isrc->sa_len > sizeof(spidx.src) ||
- idst->sa_len > sizeof(spidx.dst))
- continue;
- bcopy(isrc, &spidx.src, isrc->sa_len);
- bcopy(idst, &spidx.dst, idst->sa_len);
- if (!key_cmpspidx_withmask(&sp->spidx, &spidx))
- continue;
- } else {
- if (key_sockaddrcmp(&r1->saidx.src.sa, isrc, 0) ||
- key_sockaddrcmp(&r1->saidx.dst.sa, idst, 0))
- continue;
- }
-
- if (key_sockaddrcmp(&r2->saidx.src.sa, osrc, 0) ||
- key_sockaddrcmp(&r2->saidx.dst.sa, odst, 0))
- continue;
-
- goto found;
- }
+ if (!key_sockaddrcmp(&saidx->dst.sa, &sah->saidx.dst.sa, 0))
+ break;
}
- sp = NULL;
-found:
- if (sp) {
- sp->lastused = time_second;
- SP_ADDREF(sp);
+ if (sah != NULL) {
+ if (V_key_preferred_oldsa)
+ sav = TAILQ_LAST(&sah->savtree_alive, secasvar_queue);
+ else
+ sav = TAILQ_FIRST(&sah->savtree_alive);
+ if (sav != NULL)
+ SAV_ADDREF(sav);
+ } else
+ sav = NULL;
+ SAHTREE_RUNLOCK();
+
+ if (sav != NULL) {
+ KEYDBG(IPSEC_STAMP,
+ printf("%s: return SA(%p)\n", __func__, sav));
+ KEYDBG(IPSEC_DATA, kdebug_secasv(sav));
+ } else {
+ KEYDBG(IPSEC_STAMP,
+ printf("%s: SA not found\n", __func__));
+ KEYDBG(IPSEC_DATA, kdebug_secasindex(saidx, NULL));
}
- SPTREE_UNLOCK();
-done:
- KEYDEBUG(KEYDEBUG_IPSEC_STAMP,
- printf("DP %s return SP:%p (ID=%u) refcnt %u\n", __func__,
- sp, sp ? sp->id : 0, sp ? sp->refcnt : 0));
- return sp;
+ return (sav);
}
-#endif
/*
- * allocating an SA entry for an *OUTBOUND* packet.
- * checking each request entries in SP, and acquire an SA if need.
- * OUT: 0: there are valid requests.
- * ENOENT: policy may be valid, but SA with REQUIRE is on acquiring.
+ * Allocating an SA entry for an *OUTBOUND* packet.
+ * OUT: positive: corresponding SA for given saidx found.
+ * NULL: SA not found, but will be acquired, check *error
+ * for acquiring status.
*/
-int
-key_checkrequest(struct ipsecrequest *isr, const struct secasindex *saidx)
+struct secasvar *
+key_allocsa_policy(struct secpolicy *sp, const struct secasindex *saidx,
+ int *error)
{
- u_int level;
- int error;
+ SAHTREE_RLOCK_TRACKER;
+ struct secashead *sah;
struct secasvar *sav;
- IPSEC_ASSERT(isr != NULL, ("null isr"));
IPSEC_ASSERT(saidx != NULL, ("null saidx"));
IPSEC_ASSERT(saidx->mode == IPSEC_MODE_TRANSPORT ||
saidx->mode == IPSEC_MODE_TUNNEL,
("unexpected policy %u", saidx->mode));
/*
- * XXX guard against protocol callbacks from the crypto
- * thread as they reference ipsecrequest.sav which we
- * temporarily null out below. Need to rethink how we
- * handle bundled SA's in the callback thread.
- */
- IPSECREQUEST_LOCK_ASSERT(isr);
-
- /* get current level */
- level = ipsec_get_reqlevel(isr);
-
- /*
* We check new SA in the IPsec request because a different
* SA may be involved each time this request is checked, either
* because new SAs are being configured, or this request is
* associated with an unconnected datagram socket, or this request
* is associated with a system default policy.
- *
- * key_allocsa_policy should allocate the oldest SA available.
- * See key_do_allocsa_policy(), and draft-jenkins-ipsec-rekeying-03.txt.
*/
- sav = key_allocsa_policy(saidx);
- if (sav != isr->sav) {
- /* SA need to be updated. */
- if (!IPSECREQUEST_UPGRADE(isr)) {
- /* Kick everyone off. */
- IPSECREQUEST_UNLOCK(isr);
- IPSECREQUEST_WLOCK(isr);
- }
- if (isr->sav != NULL)
- KEY_FREESAV(&isr->sav);
- isr->sav = sav;
- IPSECREQUEST_DOWNGRADE(isr);
- } else if (sav != NULL)
- KEY_FREESAV(&sav);
-
- /* When there is SA. */
- if (isr->sav != NULL) {
- if (isr->sav->state != SADB_SASTATE_MATURE &&
- isr->sav->state != SADB_SASTATE_DYING)
- return EINVAL;
- return 0;
- }
-
- /* there is no SA */
- error = key_acquire(saidx, isr->sp);
- if (error != 0) {
- /* XXX What should I do ? */
- ipseclog((LOG_DEBUG, "%s: error %d returned from key_acquire\n",
- __func__, error));
- return error;
- }
-
- if (level != IPSEC_LEVEL_REQUIRE) {
- /* XXX sigh, the interface to this routine is botched */
- IPSEC_ASSERT(isr->sav == NULL, ("unexpected SA"));
- return 0;
- } else {
- return ENOENT;
- }
-}
-
-/*
- * allocating a SA for policy entry from SAD.
- * NOTE: searching SAD of aliving state.
- * OUT: NULL: not found.
- * others: found and return the pointer.
- */
-static struct secasvar *
-key_allocsa_policy(const struct secasindex *saidx)
-{
-#define N(a) _ARRAYLEN(a)
- struct secashead *sah;
- struct secasvar *sav;
- u_int stateidx, arraysize;
- const u_int *state_valid;
-
- state_valid = NULL; /* silence gcc */
- arraysize = 0; /* silence gcc */
-
- SAHTREE_LOCK();
- LIST_FOREACH(sah, &V_sahtree, chain) {
- if (sah->state == SADB_SASTATE_DEAD)
- continue;
- if (key_cmpsaidx(&sah->saidx, saidx, CMP_MODE_REQID)) {
- if (V_key_preferred_oldsa) {
- state_valid = saorder_state_valid_prefer_old;
- arraysize = N(saorder_state_valid_prefer_old);
- } else {
- state_valid = saorder_state_valid_prefer_new;
- arraysize = N(saorder_state_valid_prefer_new);
- }
+ SAHTREE_RLOCK();
+ LIST_FOREACH(sah, SAHADDRHASH_HASH(saidx), addrhash) {
+ KEYDBG(IPSEC_DUMP,
+ printf("%s: checking SAH\n", __func__);
+ kdebug_secash(sah, " "));
+ if (key_cmpsaidx(&sah->saidx, saidx, CMP_MODE_REQID))
break;
- }
- }
- SAHTREE_UNLOCK();
- if (sah == NULL)
- return NULL;
- /* search valid state */
- for (stateidx = 0; stateidx < arraysize; stateidx++) {
- sav = key_do_allocsa_policy(sah, state_valid[stateidx]);
- if (sav != NULL)
- return sav;
}
-
- return NULL;
-#undef N
-}
-
-/*
- * searching SAD with direction, protocol, mode and state.
- * called by key_allocsa_policy().
- * OUT:
- * NULL : not found
- * others : found, pointer to a SA.
- */
-static struct secasvar *
-key_do_allocsa_policy(struct secashead *sah, u_int state)
-{
- struct secasvar *sav, *nextsav, *candidate, *d;
-
- /* initialize */
- candidate = NULL;
-
- SAHTREE_LOCK();
- for (sav = LIST_FIRST(&sah->savtree[state]);
- sav != NULL;
- sav = nextsav) {
-
- nextsav = LIST_NEXT(sav, chain);
-
- /* sanity check */
- KEY_CHKSASTATE(sav->state, state, __func__);
-
- /* initialize */
- if (candidate == NULL) {
- candidate = sav;
- continue;
- }
-
- /* Which SA is the better ? */
-
- IPSEC_ASSERT(candidate->lft_c != NULL,
- ("null candidate lifetime"));
- IPSEC_ASSERT(sav->lft_c != NULL, ("null sav lifetime"));
-
- /* What the best method is to compare ? */
- if (V_key_preferred_oldsa) {
- if (candidate->lft_c->addtime >
- sav->lft_c->addtime) {
- candidate = sav;
- }
- continue;
- /*NOTREACHED*/
- }
-
- /* preferred new sa rather than old sa */
- if (candidate->lft_c->addtime <
- sav->lft_c->addtime) {
- d = candidate;
- candidate = sav;
- } else
- d = sav;
-
+ if (sah != NULL) {
/*
- * prepared to delete the SA when there is more
- * suitable candidate and the lifetime of the SA is not
- * permanent.
+ * Allocate the oldest SA available according to
+ * draft-jenkins-ipsec-rekeying-03.
*/
- if (d->lft_h->addtime != 0) {
- struct mbuf *m, *result;
- u_int8_t satype;
-
- key_sa_chgstate(d, SADB_SASTATE_DEAD);
-
- IPSEC_ASSERT(d->refcnt > 0, ("bogus ref count"));
-
- satype = key_proto2satype(d->sah->saidx.proto);
- if (satype == 0)
- goto msgfail;
-
- m = key_setsadbmsg(SADB_DELETE, 0,
- satype, 0, 0, d->refcnt - 1);
- if (!m)
- goto msgfail;
- result = m;
-
- /* set sadb_address for saidx's. */
- m = key_setsadbaddr(SADB_EXT_ADDRESS_SRC,
- &d->sah->saidx.src.sa,
- d->sah->saidx.src.sa.sa_len << 3,
- IPSEC_ULPROTO_ANY);
- if (!m)
- goto msgfail;
- m_cat(result, m);
-
- /* set sadb_address for saidx's. */
- m = key_setsadbaddr(SADB_EXT_ADDRESS_DST,
- &d->sah->saidx.dst.sa,
- d->sah->saidx.dst.sa.sa_len << 3,
- IPSEC_ULPROTO_ANY);
- if (!m)
- goto msgfail;
- m_cat(result, m);
-
- /* create SA extension */
- m = key_setsadbsa(d);
- if (!m)
- goto msgfail;
- m_cat(result, m);
-
- if (result->m_len < sizeof(struct sadb_msg)) {
- result = m_pullup(result,
- sizeof(struct sadb_msg));
- if (result == NULL)
- goto msgfail;
- }
-
- result->m_pkthdr.len = 0;
- for (m = result; m; m = m->m_next)
- result->m_pkthdr.len += m->m_len;
- mtod(result, struct sadb_msg *)->sadb_msg_len =
- PFKEY_UNIT64(result->m_pkthdr.len);
+ if (V_key_preferred_oldsa)
+ sav = TAILQ_LAST(&sah->savtree_alive, secasvar_queue);
+ else
+ sav = TAILQ_FIRST(&sah->savtree_alive);
+ if (sav != NULL)
+ SAV_ADDREF(sav);
+ } else
+ sav = NULL;
+ SAHTREE_RUNLOCK();
- if (key_sendup_mbuf(NULL, result,
- KEY_SENDUP_REGISTERED))
- goto msgfail;
- msgfail:
- KEY_FREESAV(&d);
- }
- }
- if (candidate) {
- sa_addref(candidate);
- KEYDEBUG(KEYDEBUG_IPSEC_STAMP,
- printf("DP %s cause refcnt++:%d SA:%p\n",
- __func__, candidate->refcnt, candidate));
+ if (sav != NULL) {
+ *error = 0;
+ KEYDBG(IPSEC_STAMP,
+ printf("%s: chosen SA(%p) for SP(%p)\n", __func__,
+ sav, sp));
+ KEYDBG(IPSEC_DATA, kdebug_secasv(sav));
+ return (sav); /* return referenced SA */
}
- SAHTREE_UNLOCK();
- return candidate;
+ /* there is no SA */
+ *error = key_acquire(saidx, sp);
+ if ((*error) != 0)
+ ipseclog((LOG_DEBUG,
+ "%s: error %d returned from key_acquire()\n",
+ __func__, *error));
+ KEYDBG(IPSEC_STAMP,
+ printf("%s: acquire SA for SP(%p), error %d\n",
+ __func__, sp, *error));
+ KEYDBG(IPSEC_DATA, kdebug_secasindex(saidx, NULL));
+ return (NULL);
}
/*
@@ -1060,190 +967,121 @@ key_do_allocsa_policy(struct secashead *sah, u_int state)
* OUT: positive: pointer to a usable sav (i.e. MATURE or DYING state).
* NULL: not found, or error occurred.
*
- * In the comparison, no source address is used--for RFC2401 conformance.
- * To quote, from section 4.1:
- * A security association is uniquely identified by a triple consisting
- * of a Security Parameter Index (SPI), an IP Destination Address, and a
- * security protocol (AH or ESP) identifier.
+ * According to RFC 2401 SA is uniquely identified by a triple SPI,
+ * destination address, and security protocol. But according to RFC 4301,
+ * SPI by itself suffices to specify an SA.
+ *
* Note that, however, we do need to keep source address in IPsec SA.
* IKE specification and PF_KEY specification do assume that we
* keep source address in IPsec SA. We see a tricky situation here.
*/
struct secasvar *
-key_allocsa(union sockaddr_union *dst, u_int proto, u_int32_t spi,
- const char* where, int tag)
+key_allocsa(union sockaddr_union *dst, uint8_t proto, uint32_t spi)
{
- struct secashead *sah;
+ SAHTREE_RLOCK_TRACKER;
struct secasvar *sav;
- u_int stateidx, arraysize, state;
- const u_int *saorder_state_valid;
-#ifdef IPSEC_NAT_T
- int natt_chkport;
-#endif
-
- IPSEC_ASSERT(dst != NULL, ("null dst address"));
-
- KEYDEBUG(KEYDEBUG_IPSEC_STAMP,
- printf("DP %s from %s:%u\n", __func__, where, tag));
-#ifdef IPSEC_NAT_T
- natt_chkport = (dst->sa.sa_family == AF_INET &&
- dst->sa.sa_len == sizeof(struct sockaddr_in) &&
- dst->sin.sin_port != 0);
-#endif
+ IPSEC_ASSERT(proto == IPPROTO_ESP || proto == IPPROTO_AH ||
+ proto == IPPROTO_IPCOMP, ("unexpected security protocol %u",
+ proto));
+ SAHTREE_RLOCK();
+ LIST_FOREACH(sav, SAVHASH_HASH(spi), spihash) {
+ if (sav->spi == spi)
+ break;
+ }
/*
- * searching SAD.
- * XXX: to be checked internal IP header somewhere. Also when
- * IPsec tunnel packet is received. But ESP tunnel mode is
- * encrypted so we can't check internal IP header.
+ * We use single SPI namespace for all protocols, so it is
+ * impossible to have SPI duplicates in the SAVHASH.
*/
- SAHTREE_LOCK();
- if (V_key_preferred_oldsa) {
- saorder_state_valid = saorder_state_valid_prefer_old;
- arraysize = _ARRAYLEN(saorder_state_valid_prefer_old);
- } else {
- saorder_state_valid = saorder_state_valid_prefer_new;
- arraysize = _ARRAYLEN(saorder_state_valid_prefer_new);
- }
- LIST_FOREACH(sah, &V_sahtree, chain) {
- int checkport;
-
- /* search valid state */
- for (stateidx = 0; stateidx < arraysize; stateidx++) {
- state = saorder_state_valid[stateidx];
- LIST_FOREACH(sav, &sah->savtree[state], chain) {
- /* sanity check */
- KEY_CHKSASTATE(sav->state, state, __func__);
- /* do not return entries w/ unusable state */
- if (sav->state != SADB_SASTATE_MATURE &&
- sav->state != SADB_SASTATE_DYING)
- continue;
- if (proto != sav->sah->saidx.proto)
- continue;
- if (spi != sav->spi)
- continue;
- checkport = 0;
-#ifdef IPSEC_NAT_T
- /*
- * Really only check ports when this is a NAT-T
- * SA. Otherwise other lookups providing ports
- * might suffer.
- */
- if (sav->natt_type && natt_chkport)
- checkport = 1;
-#endif
-#if 0 /* don't check src */
- /* check src address */
- if (key_sockaddrcmp(&src->sa,
- &sav->sah->saidx.src.sa, checkport) != 0)
- continue;
-#endif
- /* check dst address */
- if (key_sockaddrcmp(&dst->sa,
- &sav->sah->saidx.dst.sa, checkport) != 0)
- continue;
- sa_addref(sav);
- goto done;
- }
- }
+ if (sav != NULL) {
+ if (sav->state != SADB_SASTATE_LARVAL &&
+ sav->sah->saidx.proto == proto &&
+ key_sockaddrcmp(&dst->sa,
+ &sav->sah->saidx.dst.sa, 0) == 0)
+ SAV_ADDREF(sav);
+ else
+ sav = NULL;
}
- sav = NULL;
-done:
- SAHTREE_UNLOCK();
+ SAHTREE_RUNLOCK();
- KEYDEBUG(KEYDEBUG_IPSEC_STAMP,
- printf("DP %s return SA:%p; refcnt %u\n", __func__,
- sav, sav ? sav->refcnt : 0));
- return sav;
+ if (sav == NULL) {
+ KEYDBG(IPSEC_STAMP,
+ char buf[IPSEC_ADDRSTRLEN];
+ printf("%s: SA not found for spi %u proto %u dst %s\n",
+ __func__, ntohl(spi), proto, ipsec_address(dst, buf,
+ sizeof(buf))));
+ } else {
+ KEYDBG(IPSEC_STAMP,
+ printf("%s: return SA(%p)\n", __func__, sav));
+ KEYDBG(IPSEC_DATA, kdebug_secasv(sav));
+ }
+ return (sav);
}
struct secasvar *
key_allocsa_tunnel(union sockaddr_union *src, union sockaddr_union *dst,
- u_int proto, const char* where, int tag)
+ uint8_t proto)
{
+ SAHTREE_RLOCK_TRACKER;
+ struct secasindex saidx;
struct secashead *sah;
struct secasvar *sav;
- u_int stateidx, arraysize, state;
- const u_int *saorder_state_valid;
IPSEC_ASSERT(src != NULL, ("null src address"));
IPSEC_ASSERT(dst != NULL, ("null dst address"));
- KEYDEBUG(KEYDEBUG_IPSEC_STAMP,
- printf("DP %s from %s:%u\n", __func__, where, tag));
- SAHTREE_LOCK();
- if (V_key_preferred_oldsa) {
- saorder_state_valid = saorder_state_valid_prefer_old;
- arraysize = _ARRAYLEN(saorder_state_valid_prefer_old);
- } else {
- saorder_state_valid = saorder_state_valid_prefer_new;
- arraysize = _ARRAYLEN(saorder_state_valid_prefer_new);
- }
- LIST_FOREACH(sah, &V_sahtree, chain) {
- /* search valid state */
- for (stateidx = 0; stateidx < arraysize; stateidx++) {
- state = saorder_state_valid[stateidx];
- LIST_FOREACH(sav, &sah->savtree[state], chain) {
- /* sanity check */
- KEY_CHKSASTATE(sav->state, state, __func__);
- /* do not return entries w/ unusable state */
- if (sav->state != SADB_SASTATE_MATURE &&
- sav->state != SADB_SASTATE_DYING)
- continue;
- if (IPSEC_MODE_TUNNEL != sav->sah->saidx.mode)
- continue;
- if (proto != sav->sah->saidx.proto)
- continue;
- /* check src address */
- if (key_sockaddrcmp(&src->sa,
- &sav->sah->saidx.src.sa, 0) != 0)
- continue;
- /* check dst address */
- if (key_sockaddrcmp(&dst->sa,
- &sav->sah->saidx.dst.sa, 0) != 0)
- continue;
- sa_addref(sav);
- goto done;
- }
+ KEY_SETSECASIDX(proto, IPSEC_MODE_TUNNEL, 0, &src->sa,
+ &dst->sa, &saidx);
+
+ sav = NULL;
+ SAHTREE_RLOCK();
+ LIST_FOREACH(sah, SAHADDRHASH_HASH(&saidx), addrhash) {
+ if (IPSEC_MODE_TUNNEL != sah->saidx.mode)
+ continue;
+ if (proto != sah->saidx.proto)
+ continue;
+ if (key_sockaddrcmp(&src->sa, &sav->sah->saidx.src.sa, 0) != 0)
+ continue;
+ if (key_sockaddrcmp(&dst->sa, &sav->sah->saidx.dst.sa, 0) != 0)
+ continue;
+ /* XXXAE: is key_preferred_oldsa reasonably?*/
+ if (V_key_preferred_oldsa)
+ sav = TAILQ_LAST(&sah->savtree_alive, secasvar_queue);
+ else
+ sav = TAILQ_FIRST(&sah->savtree_alive);
+ if (sav != NULL) {
+ SAV_ADDREF(sav);
+ break;
}
}
- sav = NULL;
-done:
- SAHTREE_UNLOCK();
-
- KEYDEBUG(KEYDEBUG_IPSEC_STAMP,
- printf("DP %s return SA:%p; refcnt %u\n", __func__,
- sav, sav ? sav->refcnt : 0));
+ SAHTREE_RUNLOCK();
+ KEYDBG(IPSEC_STAMP,
+ printf("%s: return SA(%p)\n", __func__, sav));
+ if (sav != NULL)
+ KEYDBG(IPSEC_DATA, kdebug_secasv(sav));
return (sav);
}
/*
* Must be called after calling key_allocsp().
- * For both the packet without socket and key_freeso().
*/
void
-_key_freesp(struct secpolicy **spp, const char* where, int tag)
+key_freesp(struct secpolicy **spp)
{
- struct ipsecrequest *isr, *nextisr;
struct secpolicy *sp = *spp;
IPSEC_ASSERT(sp != NULL, ("null sp"));
- KEYDEBUG(KEYDEBUG_IPSEC_STAMP,
- printf("DP %s SP:%p (ID=%u) from %s:%u; refcnt now %u\n",
- __func__, sp, sp->id, where, tag, sp->refcnt));
-
if (SP_DELREF(sp) == 0)
return;
+
+ KEYDBG(IPSEC_STAMP,
+ printf("%s: last reference to SP(%p)\n", __func__, sp));
+ KEYDBG(IPSEC_DATA, kdebug_secpolicy(sp));
+
*spp = NULL;
- for (isr = sp->req; isr != NULL; isr = nextisr) {
- if (isr->sav != NULL) {
- KEY_FREESAV(&isr->sav);
- isr->sav = NULL;
- }
- nextisr = isr->next;
- ipsec_delisr(isr);
- }
+ while (sp->tcount > 0)
+ ipsec_delisr(sp->req[--sp->tcount]);
free(sp, M_IPSEC_SP);
}
@@ -1251,21 +1089,25 @@ static void
key_unlink(struct secpolicy *sp)
{
- IPSEC_ASSERT(sp != NULL, ("null sp"));
IPSEC_ASSERT(sp->spidx.dir == IPSEC_DIR_INBOUND ||
sp->spidx.dir == IPSEC_DIR_OUTBOUND,
("invalid direction %u", sp->spidx.dir));
SPTREE_UNLOCK_ASSERT();
+ KEYDBG(KEY_STAMP,
+ printf("%s: SP(%p)\n", __func__, sp));
SPTREE_WLOCK();
- if (sp->state == IPSEC_SPSTATE_DEAD) {
+ if (sp->state != IPSEC_SPSTATE_ALIVE) {
+ /* SP is already unlinked */
SPTREE_WUNLOCK();
return;
}
sp->state = IPSEC_SPSTATE_DEAD;
TAILQ_REMOVE(&V_sptree[sp->spidx.dir], sp, chain);
+ LIST_REMOVE(sp, idhash);
+ V_sp_genid++;
SPTREE_WUNLOCK();
- KEY_FREESP(&sp);
+ key_freesp(&sp);
}
/*
@@ -1276,78 +1118,101 @@ key_insertsp(struct secpolicy *newsp)
{
struct secpolicy *sp;
- SPTREE_WLOCK();
+ SPTREE_WLOCK_ASSERT();
TAILQ_FOREACH(sp, &V_sptree[newsp->spidx.dir], chain) {
if (newsp->priority < sp->priority) {
TAILQ_INSERT_BEFORE(sp, newsp, chain);
goto done;
}
}
-
TAILQ_INSERT_TAIL(&V_sptree[newsp->spidx.dir], newsp, chain);
-
done:
+ LIST_INSERT_HEAD(SPHASH_HASH(newsp->id), newsp, idhash);
newsp->state = IPSEC_SPSTATE_ALIVE;
- SPTREE_WUNLOCK();
+ V_sp_genid++;
}
/*
- * Must be called after calling key_allocsp().
- * For the packet with socket.
+ * Insert a bunch of VTI secpolicies into the SPDB.
+ * We keep VTI policies in the separate list due to following reasons:
+ * 1) they should be immutable to user's or some deamon's attempts to
+ * delete. The only way delete such policies - destroy or unconfigure
+ * corresponding virtual inteface.
+ * 2) such policies have traffic selector that matches all traffic per
+ * address family.
+ * Since all VTI policies have the same priority, we don't care about
+ * policies order.
*/
-void
-key_freeso(struct socket *so)
+int
+key_register_ifnet(struct secpolicy **spp, u_int count)
{
- IPSEC_ASSERT(so != NULL, ("null so"));
+ struct mbuf *m;
+ u_int i;
- switch (so->so_proto->pr_domain->dom_family) {
-#if defined(INET) || defined(INET6)
-#ifdef INET
- case PF_INET:
-#endif
-#ifdef INET6
- case PF_INET6:
-#endif
- {
- struct inpcb *pcb = sotoinpcb(so);
+ SPTREE_WLOCK();
+ /*
+ * First of try to acquire id for each SP.
+ */
+ for (i = 0; i < count; i++) {
+ IPSEC_ASSERT(spp[i]->spidx.dir == IPSEC_DIR_INBOUND ||
+ spp[i]->spidx.dir == IPSEC_DIR_OUTBOUND,
+ ("invalid direction %u", spp[i]->spidx.dir));
- /* Does it have a PCB ? */
- if (pcb == NULL)
- return;
- key_freesp_so(&pcb->inp_sp->sp_in);
- key_freesp_so(&pcb->inp_sp->sp_out);
- }
- break;
-#endif /* INET || INET6 */
- default:
- ipseclog((LOG_DEBUG, "%s: unknown address family=%d.\n",
- __func__, so->so_proto->pr_domain->dom_family));
- return;
+ if ((spp[i]->id = key_getnewspid()) == 0) {
+ SPTREE_WUNLOCK();
+ return (EAGAIN);
+ }
}
-}
-
-static void
-key_freesp_so(struct secpolicy **sp)
-{
- IPSEC_ASSERT(sp != NULL && *sp != NULL, ("null sp"));
-
- if ((*sp)->policy == IPSEC_POLICY_ENTRUST ||
- (*sp)->policy == IPSEC_POLICY_BYPASS)
- return;
-
- IPSEC_ASSERT((*sp)->policy == IPSEC_POLICY_IPSEC,
- ("invalid policy %u", (*sp)->policy));
- KEY_FREESP(sp);
+ for (i = 0; i < count; i++) {
+ TAILQ_INSERT_TAIL(&V_sptree_ifnet[spp[i]->spidx.dir],
+ spp[i], chain);
+ /*
+ * NOTE: despite the fact that we keep VTI SP in the
+ * separate list, SPHASH contains policies from both
+ * sources. Thus SADB_X_SPDGET will correctly return
+ * SP by id, because it uses SPHASH for lookups.
+ */
+ LIST_INSERT_HEAD(SPHASH_HASH(spp[i]->id), spp[i], idhash);
+ spp[i]->state = IPSEC_SPSTATE_IFNET;
+ }
+ SPTREE_WUNLOCK();
+ /*
+ * Notify user processes about new SP.
+ */
+ for (i = 0; i < count; i++) {
+ m = key_setdumpsp(spp[i], SADB_X_SPDADD, 0, 0);
+ if (m != NULL)
+ key_sendup_mbuf(NULL, m, KEY_SENDUP_ALL);
+ }
+ return (0);
}
void
-key_addrefsa(struct secasvar *sav, const char* where, int tag)
+key_unregister_ifnet(struct secpolicy **spp, u_int count)
{
+ struct mbuf *m;
+ u_int i;
- IPSEC_ASSERT(sav != NULL, ("null sav"));
- IPSEC_ASSERT(sav->refcnt > 0, ("refcount must exist"));
+ SPTREE_WLOCK();
+ for (i = 0; i < count; i++) {
+ IPSEC_ASSERT(spp[i]->spidx.dir == IPSEC_DIR_INBOUND ||
+ spp[i]->spidx.dir == IPSEC_DIR_OUTBOUND,
+ ("invalid direction %u", spp[i]->spidx.dir));
- sa_addref(sav);
+ if (spp[i]->state != IPSEC_SPSTATE_IFNET)
+ continue;
+ spp[i]->state = IPSEC_SPSTATE_DEAD;
+ TAILQ_REMOVE(&V_sptree_ifnet[spp[i]->spidx.dir],
+ spp[i], chain);
+ LIST_REMOVE(spp[i], idhash);
+ }
+ SPTREE_WUNLOCK();
+
+ for (i = 0; i < count; i++) {
+ m = key_setdumpsp(spp[i], SADB_X_SPDDELETE, 0, 0);
+ if (m != NULL)
+ key_sendup_mbuf(NULL, m, KEY_SENDUP_ALL);
+ }
}
/*
@@ -1356,23 +1221,54 @@ key_addrefsa(struct secasvar *sav, const char* where, int tag)
* for a policy.
*/
void
-key_freesav(struct secasvar **psav, const char* where, int tag)
+key_freesav(struct secasvar **psav)
{
struct secasvar *sav = *psav;
IPSEC_ASSERT(sav != NULL, ("null sav"));
+ if (SAV_DELREF(sav) == 0)
+ return;
- if (sa_delref(sav)) {
- KEYDEBUG(KEYDEBUG_IPSEC_STAMP,
- printf("DP %s SA:%p (SPI %u) from %s:%u; refcnt now %u\n",
- __func__, sav, ntohl(sav->spi), where, tag, sav->refcnt));
- *psav = NULL;
- key_delsav(sav);
- } else {
- KEYDEBUG(KEYDEBUG_IPSEC_STAMP,
- printf("DP %s SA:%p (SPI %u) from %s:%u; refcnt now %u\n",
- __func__, sav, ntohl(sav->spi), where, tag, sav->refcnt));
+ KEYDBG(IPSEC_STAMP,
+ printf("%s: last reference to SA(%p)\n", __func__, sav));
+
+ *psav = NULL;
+ key_delsav(sav);
+}
+
+/*
+ * Unlink SA from SAH and SPI hash under SAHTREE_WLOCK.
+ * Expect that SA has extra reference due to lookup.
+ * Release this references, also release SAH reference after unlink.
+ */
+static void
+key_unlinksav(struct secasvar *sav)
+{
+ struct secashead *sah;
+
+ KEYDBG(KEY_STAMP,
+ printf("%s: SA(%p)\n", __func__, sav));
+
+ SAHTREE_UNLOCK_ASSERT();
+ SAHTREE_WLOCK();
+ if (sav->state == SADB_SASTATE_DEAD) {
+ /* SA is already unlinked */
+ SAHTREE_WUNLOCK();
+ return;
}
+ /* Unlink from SAH */
+ if (sav->state == SADB_SASTATE_LARVAL)
+ TAILQ_REMOVE(&sav->sah->savtree_larval, sav, chain);
+ else
+ TAILQ_REMOVE(&sav->sah->savtree_alive, sav, chain);
+ /* Unlink from SPI hash */
+ LIST_REMOVE(sav, spihash);
+ sav->state = SADB_SASTATE_DEAD;
+ sah = sav->sah;
+ SAHTREE_WUNLOCK();
+ key_freesav(&sav);
+ /* Since we are unlinked, release reference to SAH */
+ key_freesah(&sah);
}
/* %%% SPD management */
@@ -1404,54 +1300,55 @@ key_getsp(struct secpolicyindex *spidx)
/*
* get SP by index.
* OUT: NULL : not found
- * others : found, pointer to a SP.
+ * others : found, pointer to referenced SP.
*/
static struct secpolicy *
-key_getspbyid(u_int32_t id)
+key_getspbyid(uint32_t id)
{
SPTREE_RLOCK_TRACKER;
struct secpolicy *sp;
SPTREE_RLOCK();
- TAILQ_FOREACH(sp, &V_sptree[IPSEC_DIR_INBOUND], chain) {
- if (sp->id == id) {
- SP_ADDREF(sp);
- goto done;
- }
- }
-
- TAILQ_FOREACH(sp, &V_sptree[IPSEC_DIR_OUTBOUND], chain) {
+ LIST_FOREACH(sp, SPHASH_HASH(id), idhash) {
if (sp->id == id) {
SP_ADDREF(sp);
- goto done;
+ break;
}
}
-done:
SPTREE_RUNLOCK();
-
- return sp;
+ return (sp);
}
struct secpolicy *
-key_newsp(const char* where, int tag)
+key_newsp(void)
{
- struct secpolicy *newsp = NULL;
+ struct secpolicy *sp;
- newsp = (struct secpolicy *)
- malloc(sizeof(struct secpolicy), M_IPSEC_SP, M_NOWAIT|M_ZERO);
- if (newsp)
- refcount_init(&newsp->refcnt, 1);
+ sp = malloc(sizeof(*sp), M_IPSEC_SP, M_NOWAIT | M_ZERO);
+ if (sp != NULL)
+ SP_INITREF(sp);
+ return (sp);
+}
- KEYDEBUG(KEYDEBUG_IPSEC_STAMP,
- printf("DP %s from %s:%u return SP:%p\n", __func__,
- where, tag, newsp));
- return newsp;
+struct ipsecrequest *
+ipsec_newisr(void)
+{
+
+ return (malloc(sizeof(struct ipsecrequest), M_IPSEC_SR,
+ M_NOWAIT | M_ZERO));
+}
+
+void
+ipsec_delisr(struct ipsecrequest *p)
+{
+
+ free(p, M_IPSEC_SR);
}
/*
* create secpolicy structure from sadb_x_policy structure.
- * NOTE: `state', `secpolicyindex' in secpolicy structure are not set,
- * so must be set properly later.
+ * NOTE: `state', `secpolicyindex' and 'id' in secpolicy structure
+ * are not set, so must be set properly later.
*/
struct secpolicy *
key_msg2sp(struct sadb_x_policy *xpl0, size_t len, int *error)
@@ -1467,7 +1364,7 @@ key_msg2sp(struct sadb_x_policy *xpl0, size_t len, int *error)
return NULL;
}
- if ((newsp = KEY_NEWSP()) == NULL) {
+ if ((newsp = key_newsp()) == NULL) {
*error = ENOBUFS;
return NULL;
}
@@ -1475,6 +1372,7 @@ key_msg2sp(struct sadb_x_policy *xpl0, size_t len, int *error)
newsp->spidx.dir = xpl0->sadb_x_policy_dir;
newsp->policy = xpl0->sadb_x_policy_type;
newsp->priority = xpl0->sadb_x_policy_priority;
+ newsp->tcount = 0;
/* check policy */
switch (xpl0->sadb_x_policy_type) {
@@ -1482,20 +1380,19 @@ key_msg2sp(struct sadb_x_policy *xpl0, size_t len, int *error)
case IPSEC_POLICY_NONE:
case IPSEC_POLICY_ENTRUST:
case IPSEC_POLICY_BYPASS:
- newsp->req = NULL;
break;
case IPSEC_POLICY_IPSEC:
{
- int tlen;
struct sadb_x_ipsecrequest *xisr;
- struct ipsecrequest **p_isr = &newsp->req;
+ struct ipsecrequest *isr;
+ int tlen;
/* validity check */
if (PFKEY_EXTLEN(xpl0) < sizeof(*xpl0)) {
ipseclog((LOG_DEBUG, "%s: Invalid msg length.\n",
__func__));
- KEY_FREESP(&newsp);
+ key_freesp(&newsp);
*error = EINVAL;
return NULL;
}
@@ -1508,22 +1405,33 @@ key_msg2sp(struct sadb_x_policy *xpl0, size_t len, int *error)
if (xisr->sadb_x_ipsecrequest_len < sizeof(*xisr)) {
ipseclog((LOG_DEBUG, "%s: invalid ipsecrequest "
"length.\n", __func__));
- KEY_FREESP(&newsp);
+ key_freesp(&newsp);
*error = EINVAL;
return NULL;
}
+ if (newsp->tcount >= IPSEC_MAXREQ) {
+ ipseclog((LOG_DEBUG,
+ "%s: too many ipsecrequests.\n",
+ __func__));
+ key_freesp(&newsp);
+ *error = EINVAL;
+ return (NULL);
+ }
+
/* allocate request buffer */
/* NB: data structure is zero'd */
- *p_isr = ipsec_newisr();
- if ((*p_isr) == NULL) {
+ isr = ipsec_newisr();
+ if (isr == NULL) {
ipseclog((LOG_DEBUG,
"%s: No more memory.\n", __func__));
- KEY_FREESP(&newsp);
+ key_freesp(&newsp);
*error = ENOBUFS;
return NULL;
}
+ newsp->req[newsp->tcount++] = isr;
+
/* set values */
switch (xisr->sadb_x_ipsecrequest_proto) {
case IPPROTO_ESP:
@@ -1534,11 +1442,12 @@ key_msg2sp(struct sadb_x_policy *xpl0, size_t len, int *error)
ipseclog((LOG_DEBUG,
"%s: invalid proto type=%u\n", __func__,
xisr->sadb_x_ipsecrequest_proto));
- KEY_FREESP(&newsp);
+ key_freesp(&newsp);
*error = EPROTONOSUPPORT;
return NULL;
}
- (*p_isr)->saidx.proto = xisr->sadb_x_ipsecrequest_proto;
+ isr->saidx.proto =
+ (uint8_t)xisr->sadb_x_ipsecrequest_proto;
switch (xisr->sadb_x_ipsecrequest_mode) {
case IPSEC_MODE_TRANSPORT:
@@ -1549,11 +1458,11 @@ key_msg2sp(struct sadb_x_policy *xpl0, size_t len, int *error)
ipseclog((LOG_DEBUG,
"%s: invalid mode=%u\n", __func__,
xisr->sadb_x_ipsecrequest_mode));
- KEY_FREESP(&newsp);
+ key_freesp(&newsp);
*error = EINVAL;
return NULL;
}
- (*p_isr)->saidx.mode = xisr->sadb_x_ipsecrequest_mode;
+ isr->saidx.mode = xisr->sadb_x_ipsecrequest_mode;
switch (xisr->sadb_x_ipsecrequest_level) {
case IPSEC_LEVEL_DEFAULT:
@@ -1580,16 +1489,16 @@ key_msg2sp(struct sadb_x_policy *xpl0, size_t len, int *error)
if (xisr->sadb_x_ipsecrequest_reqid == 0) {
u_int32_t reqid;
if ((reqid = key_newreqid()) == 0) {
- KEY_FREESP(&newsp);
+ key_freesp(&newsp);
*error = ENOBUFS;
return NULL;
}
- (*p_isr)->saidx.reqid = reqid;
+ isr->saidx.reqid = reqid;
xisr->sadb_x_ipsecrequest_reqid = reqid;
} else {
/* set it for manual keying. */
- (*p_isr)->saidx.reqid =
- xisr->sadb_x_ipsecrequest_reqid;
+ isr->saidx.reqid =
+ xisr->sadb_x_ipsecrequest_reqid;
}
break;
@@ -1597,59 +1506,72 @@ key_msg2sp(struct sadb_x_policy *xpl0, size_t len, int *error)
ipseclog((LOG_DEBUG, "%s: invalid level=%u\n",
__func__,
xisr->sadb_x_ipsecrequest_level));
- KEY_FREESP(&newsp);
+ key_freesp(&newsp);
*error = EINVAL;
return NULL;
}
- (*p_isr)->level = xisr->sadb_x_ipsecrequest_level;
+ isr->level = xisr->sadb_x_ipsecrequest_level;
/* set IP addresses if there */
if (xisr->sadb_x_ipsecrequest_len > sizeof(*xisr)) {
struct sockaddr *paddr;
paddr = (struct sockaddr *)(xisr + 1);
-
/* validity check */
if (paddr->sa_len
- > sizeof((*p_isr)->saidx.src)) {
+ > sizeof(isr->saidx.src)) {
ipseclog((LOG_DEBUG, "%s: invalid "
"request address length.\n",
__func__));
- KEY_FREESP(&newsp);
+ key_freesp(&newsp);
*error = EINVAL;
return NULL;
}
- bcopy(paddr, &(*p_isr)->saidx.src,
- paddr->sa_len);
-
- paddr = (struct sockaddr *)((caddr_t)paddr
- + paddr->sa_len);
+ bcopy(paddr, &isr->saidx.src, paddr->sa_len);
+ paddr = (struct sockaddr *)((caddr_t)paddr +
+ paddr->sa_len);
/* validity check */
if (paddr->sa_len
- > sizeof((*p_isr)->saidx.dst)) {
+ > sizeof(isr->saidx.dst)) {
ipseclog((LOG_DEBUG, "%s: invalid "
"request address length.\n",
__func__));
- KEY_FREESP(&newsp);
+ key_freesp(&newsp);
*error = EINVAL;
return NULL;
}
- bcopy(paddr, &(*p_isr)->saidx.dst,
- paddr->sa_len);
+ /* AF family should match */
+ if (paddr->sa_family !=
+ isr->saidx.src.sa.sa_family) {
+ ipseclog((LOG_DEBUG, "%s: address "
+ "family doesn't match.\n",
+ __func__));
+ key_freesp(&newsp);
+ *error = EINVAL;
+ return (NULL);
+ }
+ bcopy(paddr, &isr->saidx.dst, paddr->sa_len);
+ } else {
+ /*
+ * Addresses for TUNNEL mode requests are
+ * mandatory.
+ */
+ if (isr->saidx.mode == IPSEC_MODE_TUNNEL) {
+ ipseclog((LOG_DEBUG, "%s: missing "
+ "request addresses.\n", __func__));
+ key_freesp(&newsp);
+ *error = EINVAL;
+ return (NULL);
+ }
}
-
- (*p_isr)->sp = newsp;
-
- /* initialization for the next. */
- p_isr = &(*p_isr)->next;
tlen -= xisr->sadb_x_ipsecrequest_len;
/* validity check */
if (tlen < 0) {
ipseclog((LOG_DEBUG, "%s: becoming tlen < 0.\n",
__func__));
- KEY_FREESP(&newsp);
+ key_freesp(&newsp);
*error = EINVAL;
return NULL;
}
@@ -1657,72 +1579,114 @@ key_msg2sp(struct sadb_x_policy *xpl0, size_t len, int *error)
xisr = (struct sadb_x_ipsecrequest *)((caddr_t)xisr
+ xisr->sadb_x_ipsecrequest_len);
}
+ /* XXXAE: LARVAL SP */
+ if (newsp->tcount < 1) {
+ ipseclog((LOG_DEBUG, "%s: valid IPSEC transforms "
+ "not found.\n", __func__));
+ key_freesp(&newsp);
+ *error = EINVAL;
+ return (NULL);
+ }
}
break;
default:
ipseclog((LOG_DEBUG, "%s: invalid policy type.\n", __func__));
- KEY_FREESP(&newsp);
+ key_freesp(&newsp);
*error = EINVAL;
return NULL;
}
*error = 0;
- return newsp;
+ return (newsp);
}
-static u_int32_t
-key_newreqid()
+uint32_t
+key_newreqid(void)
{
- static u_int32_t auto_reqid = IPSEC_MANUAL_REQID_MAX + 1;
+ static uint32_t auto_reqid = IPSEC_MANUAL_REQID_MAX + 1;
- auto_reqid = (auto_reqid == ~0
- ? IPSEC_MANUAL_REQID_MAX + 1 : auto_reqid + 1);
+ if (auto_reqid == ~0)
+ auto_reqid = IPSEC_MANUAL_REQID_MAX + 1;
+ else
+ auto_reqid++;
/* XXX should be unique check */
-
- return auto_reqid;
+ return (auto_reqid);
}
/*
* copy secpolicy struct to sadb_x_policy structure indicated.
*/
-struct mbuf *
-key_sp2msg(struct secpolicy *sp)
+static struct mbuf *
+key_sp2mbuf(struct secpolicy *sp)
{
- struct sadb_x_policy *xpl;
- int tlen;
- caddr_t p;
struct mbuf *m;
-
- IPSEC_ASSERT(sp != NULL, ("null policy"));
+ size_t tlen;
tlen = key_getspreqmsglen(sp);
-
m = m_get2(tlen, M_NOWAIT, MT_DATA, 0);
if (m == NULL)
return (NULL);
m_align(m, tlen);
m->m_len = tlen;
- xpl = mtod(m, struct sadb_x_policy *);
- bzero(xpl, tlen);
+ if (key_sp2msg(sp, m->m_data, &tlen) != 0) {
+ m_freem(m);
+ return (NULL);
+ }
+ return (m);
+}
+
+int
+key_sp2msg(struct secpolicy *sp, void *request, size_t *len)
+{
+ struct sadb_x_ipsecrequest *xisr;
+ struct sadb_x_policy *xpl;
+ struct ipsecrequest *isr;
+ size_t xlen, ilen;
+ caddr_t p;
+ int error, i;
- xpl->sadb_x_policy_len = PFKEY_UNIT64(tlen);
+ IPSEC_ASSERT(sp != NULL, ("null policy"));
+
+ xlen = sizeof(*xpl);
+ if (*len < xlen)
+ return (EINVAL);
+
+ error = 0;
+ bzero(request, *len);
+ xpl = (struct sadb_x_policy *)request;
xpl->sadb_x_policy_exttype = SADB_X_EXT_POLICY;
xpl->sadb_x_policy_type = sp->policy;
xpl->sadb_x_policy_dir = sp->spidx.dir;
xpl->sadb_x_policy_id = sp->id;
xpl->sadb_x_policy_priority = sp->priority;
- p = (caddr_t)xpl + sizeof(*xpl);
+ switch (sp->state) {
+ case IPSEC_SPSTATE_IFNET:
+ xpl->sadb_x_policy_scope = IPSEC_POLICYSCOPE_IFNET;
+ break;
+ case IPSEC_SPSTATE_PCB:
+ xpl->sadb_x_policy_scope = IPSEC_POLICYSCOPE_PCB;
+ break;
+ default:
+ xpl->sadb_x_policy_scope = IPSEC_POLICYSCOPE_GLOBAL;
+ }
/* if is the policy for ipsec ? */
if (sp->policy == IPSEC_POLICY_IPSEC) {
- struct sadb_x_ipsecrequest *xisr;
- struct ipsecrequest *isr;
-
- for (isr = sp->req; isr != NULL; isr = isr->next) {
-
+ p = (caddr_t)xpl + sizeof(*xpl);
+ for (i = 0; i < sp->tcount; i++) {
+ isr = sp->req[i];
+ ilen = PFKEY_ALIGN8(sizeof(*xisr) +
+ isr->saidx.src.sa.sa_len +
+ isr->saidx.dst.sa.sa_len);
+ xlen += ilen;
+ if (xlen > *len) {
+ error = ENOBUFS;
+ /* Calculate needed size */
+ continue;
+ }
xisr = (struct sadb_x_ipsecrequest *)p;
-
+ xisr->sadb_x_ipsecrequest_len = ilen;
xisr->sadb_x_ipsecrequest_proto = isr->saidx.proto;
xisr->sadb_x_ipsecrequest_mode = isr->saidx.mode;
xisr->sadb_x_ipsecrequest_level = isr->level;
@@ -1732,16 +1696,15 @@ key_sp2msg(struct secpolicy *sp)
bcopy(&isr->saidx.src, p, isr->saidx.src.sa.sa_len);
p += isr->saidx.src.sa.sa_len;
bcopy(&isr->saidx.dst, p, isr->saidx.dst.sa.sa_len);
- p += isr->saidx.src.sa.sa_len;
-
- xisr->sadb_x_ipsecrequest_len =
- PFKEY_ALIGN8(sizeof(*xisr)
- + isr->saidx.src.sa.sa_len
- + isr->saidx.dst.sa.sa_len);
+ p += isr->saidx.dst.sa.sa_len;
}
}
-
- return m;
+ xpl->sadb_x_policy_len = PFKEY_UNIT64(xlen);
+ if (error == 0)
+ *len = xlen;
+ else
+ *len = sizeof(*xpl);
+ return (error);
}
/* m will not be freed nor modified */
@@ -1833,15 +1796,16 @@ fail:
* SPDSETIDX like SPDADD without a part of policy requests.
* SPDUPDATE replace a unique policy entry.
*
+ * XXXAE: serialize this in PF_KEY to avoid races.
* m will always be freed.
*/
static int
key_spdadd(struct socket *so, struct mbuf *m, const struct sadb_msghdr *mhp)
{
+ struct secpolicyindex spidx;
struct sadb_address *src0, *dst0;
struct sadb_x_policy *xpl0, *xpl;
struct sadb_lifetime *lft = NULL;
- struct secpolicyindex spidx;
struct secpolicy *newsp;
int error;
@@ -1850,24 +1814,26 @@ key_spdadd(struct socket *so, struct mbuf *m, const struct sadb_msghdr *mhp)
IPSEC_ASSERT(mhp != NULL, ("null msghdr"));
IPSEC_ASSERT(mhp->msg != NULL, ("null msg"));
- if (mhp->ext[SADB_EXT_ADDRESS_SRC] == NULL ||
- mhp->ext[SADB_EXT_ADDRESS_DST] == NULL ||
- mhp->ext[SADB_X_EXT_POLICY] == NULL) {
- ipseclog((LOG_DEBUG, "key_spdadd: invalid message is passed.\n"));
+ if (SADB_CHECKHDR(mhp, SADB_EXT_ADDRESS_SRC) ||
+ SADB_CHECKHDR(mhp, SADB_EXT_ADDRESS_DST) ||
+ SADB_CHECKHDR(mhp, SADB_X_EXT_POLICY)) {
+ ipseclog((LOG_DEBUG,
+ "%s: invalid message: missing required header.\n",
+ __func__));
return key_senderror(so, m, EINVAL);
}
- if (mhp->extlen[SADB_EXT_ADDRESS_SRC] < sizeof(struct sadb_address) ||
- mhp->extlen[SADB_EXT_ADDRESS_DST] < sizeof(struct sadb_address) ||
- mhp->extlen[SADB_X_EXT_POLICY] < sizeof(struct sadb_x_policy)) {
- ipseclog((LOG_DEBUG, "%s: invalid message is passed.\n",
- __func__));
+ if (SADB_CHECKLEN(mhp, SADB_EXT_ADDRESS_SRC) ||
+ SADB_CHECKLEN(mhp, SADB_EXT_ADDRESS_DST) ||
+ SADB_CHECKLEN(mhp, SADB_X_EXT_POLICY)) {
+ ipseclog((LOG_DEBUG,
+ "%s: invalid message: wrong header size.\n", __func__));
return key_senderror(so, m, EINVAL);
}
- if (mhp->ext[SADB_EXT_LIFETIME_HARD] != NULL) {
- if (mhp->extlen[SADB_EXT_LIFETIME_HARD]
- < sizeof(struct sadb_lifetime)) {
- ipseclog((LOG_DEBUG, "%s: invalid message is passed.\n",
- __func__));
+ if (!SADB_CHECKHDR(mhp, SADB_EXT_LIFETIME_HARD)) {
+ if (SADB_CHECKLEN(mhp, SADB_EXT_LIFETIME_HARD)) {
+ ipseclog((LOG_DEBUG,
+ "%s: invalid message: wrong header size.\n",
+ __func__));
return key_senderror(so, m, EINVAL);
}
lft = (struct sadb_lifetime *)mhp->ext[SADB_EXT_LIFETIME_HARD];
@@ -1877,141 +1843,93 @@ key_spdadd(struct socket *so, struct mbuf *m, const struct sadb_msghdr *mhp)
dst0 = (struct sadb_address *)mhp->ext[SADB_EXT_ADDRESS_DST];
xpl0 = (struct sadb_x_policy *)mhp->ext[SADB_X_EXT_POLICY];
- /*
- * Note: do not parse SADB_X_EXT_NAT_T_* here:
- * we are processing traffic endpoints.
- */
-
- /* make secindex */
- /* XXX boundary check against sa_len */
- KEY_SETSECSPIDX(xpl0->sadb_x_policy_dir,
- src0 + 1,
- dst0 + 1,
- src0->sadb_address_prefixlen,
- dst0->sadb_address_prefixlen,
- src0->sadb_address_proto,
- &spidx);
-
- /* checking the direciton. */
+ /* check the direciton */
switch (xpl0->sadb_x_policy_dir) {
case IPSEC_DIR_INBOUND:
case IPSEC_DIR_OUTBOUND:
break;
default:
- ipseclog((LOG_DEBUG, "%s: Invalid SP direction.\n", __func__));
- mhp->msg->sadb_msg_errno = EINVAL;
- return 0;
+ ipseclog((LOG_DEBUG, "%s: invalid SP direction.\n", __func__));
+ return key_senderror(so, m, EINVAL);
}
-
- /* check policy */
/* key_spdadd() accepts DISCARD, NONE and IPSEC. */
- if (xpl0->sadb_x_policy_type == IPSEC_POLICY_ENTRUST
- || xpl0->sadb_x_policy_type == IPSEC_POLICY_BYPASS) {
- ipseclog((LOG_DEBUG, "%s: Invalid policy type.\n", __func__));
+ if (xpl0->sadb_x_policy_type != IPSEC_POLICY_DISCARD &&
+ xpl0->sadb_x_policy_type != IPSEC_POLICY_NONE &&
+ xpl0->sadb_x_policy_type != IPSEC_POLICY_IPSEC) {
+ ipseclog((LOG_DEBUG, "%s: invalid policy type.\n", __func__));
return key_senderror(so, m, EINVAL);
}
/* policy requests are mandatory when action is ipsec. */
- if (mhp->msg->sadb_msg_type != SADB_X_SPDSETIDX
- && xpl0->sadb_x_policy_type == IPSEC_POLICY_IPSEC
- && mhp->extlen[SADB_X_EXT_POLICY] <= sizeof(*xpl0)) {
- ipseclog((LOG_DEBUG, "%s: some policy requests part required\n",
- __func__));
+ if (xpl0->sadb_x_policy_type == IPSEC_POLICY_IPSEC &&
+ mhp->extlen[SADB_X_EXT_POLICY] <= sizeof(*xpl0)) {
+ ipseclog((LOG_DEBUG,
+ "%s: policy requests required.\n", __func__));
return key_senderror(so, m, EINVAL);
}
- /*
- * checking there is SP already or not.
- * SPDUPDATE doesn't depend on whether there is a SP or not.
- * If the type is either SPDADD or SPDSETIDX AND a SP is found,
- * then error.
- */
- newsp = key_getsp(&spidx);
- if (mhp->msg->sadb_msg_type == SADB_X_SPDUPDATE) {
- if (newsp) {
- key_unlink(newsp);
- KEY_FREESP(&newsp);
- }
- } else {
- if (newsp != NULL) {
- KEY_FREESP(&newsp);
- ipseclog((LOG_DEBUG, "%s: a SP entry exists already.\n",
- __func__));
- return key_senderror(so, m, EEXIST);
- }
- }
-
- /* XXX: there is race between key_getsp and key_msg2sp. */
-
- /* allocation new SP entry */
- if ((newsp = key_msg2sp(xpl0, PFKEY_EXTLEN(xpl0), &error)) == NULL) {
+ error = key_checksockaddrs((struct sockaddr *)(src0 + 1),
+ (struct sockaddr *)(dst0 + 1));
+ if (error != 0 ||
+ src0->sadb_address_proto != dst0->sadb_address_proto) {
+ ipseclog((LOG_DEBUG, "%s: invalid sockaddr.\n", __func__));
return key_senderror(so, m, error);
}
-
- if ((newsp->id = key_getnewspid()) == 0) {
- KEY_FREESP(&newsp);
- return key_senderror(so, m, ENOBUFS);
- }
-
- /* XXX boundary check against sa_len */
+ /* make secindex */
KEY_SETSECSPIDX(xpl0->sadb_x_policy_dir,
src0 + 1,
dst0 + 1,
src0->sadb_address_prefixlen,
dst0->sadb_address_prefixlen,
src0->sadb_address_proto,
- &newsp->spidx);
-
- /* sanity check on addr pair */
- if (((struct sockaddr *)(src0 + 1))->sa_family !=
- ((struct sockaddr *)(dst0+ 1))->sa_family) {
- KEY_FREESP(&newsp);
- return key_senderror(so, m, EINVAL);
- }
- if (((struct sockaddr *)(src0 + 1))->sa_len !=
- ((struct sockaddr *)(dst0+ 1))->sa_len) {
- KEY_FREESP(&newsp);
- return key_senderror(so, m, EINVAL);
- }
-#if 1
- if (newsp->req && newsp->req->saidx.src.sa.sa_family &&
- newsp->req->saidx.dst.sa.sa_family) {
- if (newsp->req->saidx.src.sa.sa_family !=
- newsp->req->saidx.dst.sa.sa_family) {
- KEY_FREESP(&newsp);
- return key_senderror(so, m, EINVAL);
+ &spidx);
+ /* Checking there is SP already or not. */
+ newsp = key_getsp(&spidx);
+ if (newsp != NULL) {
+ if (mhp->msg->sadb_msg_type == SADB_X_SPDUPDATE) {
+ KEYDBG(KEY_STAMP,
+ printf("%s: unlink SP(%p) for SPDUPDATE\n",
+ __func__, newsp));
+ KEYDBG(KEY_DATA, kdebug_secpolicy(newsp));
+ key_unlink(newsp);
+ key_freesp(&newsp);
+ } else {
+ key_freesp(&newsp);
+ ipseclog((LOG_DEBUG, "%s: a SP entry exists already.",
+ __func__));
+ return (key_senderror(so, m, EEXIST));
}
}
-#endif
- newsp->created = time_second;
- newsp->lastused = newsp->created;
+ /* allocate new SP entry */
+ if ((newsp = key_msg2sp(xpl0, PFKEY_EXTLEN(xpl0), &error)) == NULL) {
+ return key_senderror(so, m, error);
+ }
+
+ newsp->lastused = newsp->created = time_second;
newsp->lifetime = lft ? lft->sadb_lifetime_addtime : 0;
newsp->validtime = lft ? lft->sadb_lifetime_usetime : 0;
+ bcopy(&spidx, &newsp->spidx, sizeof(spidx));
+ /* XXXAE: there is race between key_getsp() and key_insertsp() */
+ SPTREE_WLOCK();
+ if ((newsp->id = key_getnewspid()) == 0) {
+ SPTREE_WUNLOCK();
+ key_freesp(&newsp);
+ return key_senderror(so, m, ENOBUFS);
+ }
key_insertsp(newsp);
+ SPTREE_WUNLOCK();
- /* delete the entry in spacqtree */
- if (mhp->msg->sadb_msg_type == SADB_X_SPDUPDATE) {
- struct secspacq *spacq = key_getspacq(&spidx);
- if (spacq != NULL) {
- /* reset counter in order to deletion by timehandler. */
- spacq->created = time_second;
- spacq->count = 0;
- SPACQ_UNLOCK();
- }
- }
+ KEYDBG(KEY_STAMP,
+ printf("%s: SP(%p)\n", __func__, newsp));
+ KEYDBG(KEY_DATA, kdebug_secpolicy(newsp));
{
struct mbuf *n, *mpolicy;
struct sadb_msg *newmsg;
int off;
- /*
- * Note: do not send SADB_X_EXT_NAT_T_* here:
- * we are sending traffic endpoints.
- */
-
/* create new sadb_msg to reply. */
if (lft) {
n = key_gather_mbuf(m, mhp, 2, 5, SADB_EXT_RESERVED,
@@ -2059,30 +1977,32 @@ key_spdadd(struct socket *so, struct mbuf *m, const struct sadb_msghdr *mhp)
* 0: failure.
* others: success.
*/
-static u_int32_t
-key_getnewspid()
+static uint32_t
+key_getnewspid(void)
{
- u_int32_t newid = 0;
- int count = V_key_spi_trycnt; /* XXX */
struct secpolicy *sp;
+ uint32_t newid = 0;
+ int count = V_key_spi_trycnt; /* XXX */
- /* when requesting to allocate spi ranged */
+ SPTREE_WLOCK_ASSERT();
while (count--) {
- newid = (V_policy_id = (V_policy_id == ~0 ? 1 : V_policy_id + 1));
-
- if ((sp = key_getspbyid(newid)) == NULL)
+ if (V_policy_id == ~0) /* overflowed */
+ newid = V_policy_id = 1;
+ else
+ newid = ++V_policy_id;
+ LIST_FOREACH(sp, SPHASH_HASH(newid), idhash) {
+ if (sp->id == newid)
+ break;
+ }
+ if (sp == NULL)
break;
-
- KEY_FREESP(&sp);
}
-
if (count == 0 || newid == 0) {
- ipseclog((LOG_DEBUG, "%s: to allocate policy id is failed.\n",
- __func__));
- return 0;
+ ipseclog((LOG_DEBUG, "%s: failed to allocate policy id.\n",
+ __func__));
+ return (0);
}
-
- return newid;
+ return (newid);
}
/*
@@ -2101,9 +2021,9 @@ static int
key_spddelete(struct socket *so, struct mbuf *m,
const struct sadb_msghdr *mhp)
{
+ struct secpolicyindex spidx;
struct sadb_address *src0, *dst0;
struct sadb_x_policy *xpl0;
- struct secpolicyindex spidx;
struct secpolicy *sp;
IPSEC_ASSERT(so != NULL, ("null so"));
@@ -2111,18 +2031,19 @@ key_spddelete(struct socket *so, struct mbuf *m,
IPSEC_ASSERT(mhp != NULL, ("null msghdr"));
IPSEC_ASSERT(mhp->msg != NULL, ("null msg"));
- if (mhp->ext[SADB_EXT_ADDRESS_SRC] == NULL ||
- mhp->ext[SADB_EXT_ADDRESS_DST] == NULL ||
- mhp->ext[SADB_X_EXT_POLICY] == NULL) {
- ipseclog((LOG_DEBUG, "%s: invalid message is passed.\n",
- __func__));
+ if (SADB_CHECKHDR(mhp, SADB_EXT_ADDRESS_SRC) ||
+ SADB_CHECKHDR(mhp, SADB_EXT_ADDRESS_DST) ||
+ SADB_CHECKHDR(mhp, SADB_X_EXT_POLICY)) {
+ ipseclog((LOG_DEBUG,
+ "%s: invalid message: missing required header.\n",
+ __func__));
return key_senderror(so, m, EINVAL);
}
- if (mhp->extlen[SADB_EXT_ADDRESS_SRC] < sizeof(struct sadb_address) ||
- mhp->extlen[SADB_EXT_ADDRESS_DST] < sizeof(struct sadb_address) ||
- mhp->extlen[SADB_X_EXT_POLICY] < sizeof(struct sadb_x_policy)) {
- ipseclog((LOG_DEBUG, "%s: invalid message is passed.\n",
- __func__));
+ if (SADB_CHECKLEN(mhp, SADB_EXT_ADDRESS_SRC) ||
+ SADB_CHECKLEN(mhp, SADB_EXT_ADDRESS_DST) ||
+ SADB_CHECKLEN(mhp, SADB_X_EXT_POLICY)) {
+ ipseclog((LOG_DEBUG,
+ "%s: invalid message: wrong header size.\n", __func__));
return key_senderror(so, m, EINVAL);
}
@@ -2130,13 +2051,29 @@ key_spddelete(struct socket *so, struct mbuf *m,
dst0 = (struct sadb_address *)mhp->ext[SADB_EXT_ADDRESS_DST];
xpl0 = (struct sadb_x_policy *)mhp->ext[SADB_X_EXT_POLICY];
- /*
- * Note: do not parse SADB_X_EXT_NAT_T_* here:
- * we are processing traffic endpoints.
- */
-
+ /* check the direciton */
+ switch (xpl0->sadb_x_policy_dir) {
+ case IPSEC_DIR_INBOUND:
+ case IPSEC_DIR_OUTBOUND:
+ break;
+ default:
+ ipseclog((LOG_DEBUG, "%s: invalid SP direction.\n", __func__));
+ return key_senderror(so, m, EINVAL);
+ }
+ /* Only DISCARD, NONE and IPSEC are allowed */
+ if (xpl0->sadb_x_policy_type != IPSEC_POLICY_DISCARD &&
+ xpl0->sadb_x_policy_type != IPSEC_POLICY_NONE &&
+ xpl0->sadb_x_policy_type != IPSEC_POLICY_IPSEC) {
+ ipseclog((LOG_DEBUG, "%s: invalid policy type.\n", __func__));
+ return key_senderror(so, m, EINVAL);
+ }
+ if (key_checksockaddrs((struct sockaddr *)(src0 + 1),
+ (struct sockaddr *)(dst0 + 1)) != 0 ||
+ src0->sadb_address_proto != dst0->sadb_address_proto) {
+ ipseclog((LOG_DEBUG, "%s: invalid sockaddr.\n", __func__));
+ return key_senderror(so, m, EINVAL);
+ }
/* make secindex */
- /* XXX boundary check against sa_len */
KEY_SETSECSPIDX(xpl0->sadb_x_policy_dir,
src0 + 1,
dst0 + 1,
@@ -2145,16 +2082,6 @@ key_spddelete(struct socket *so, struct mbuf *m,
src0->sadb_address_proto,
&spidx);
- /* checking the direciton. */
- switch (xpl0->sadb_x_policy_dir) {
- case IPSEC_DIR_INBOUND:
- case IPSEC_DIR_OUTBOUND:
- break;
- default:
- ipseclog((LOG_DEBUG, "%s: Invalid SP direction.\n", __func__));
- return key_senderror(so, m, EINVAL);
- }
-
/* Is there SP in SPD ? */
if ((sp = key_getsp(&spidx)) == NULL) {
ipseclog((LOG_DEBUG, "%s: no SP found.\n", __func__));
@@ -2164,18 +2091,16 @@ key_spddelete(struct socket *so, struct mbuf *m,
/* save policy id to buffer to be returned. */
xpl0->sadb_x_policy_id = sp->id;
+ KEYDBG(KEY_STAMP,
+ printf("%s: SP(%p)\n", __func__, sp));
+ KEYDBG(KEY_DATA, kdebug_secpolicy(sp));
key_unlink(sp);
- KEY_FREESP(&sp);
+ key_freesp(&sp);
{
struct mbuf *n;
struct sadb_msg *newmsg;
- /*
- * Note: do not send SADB_X_EXT_NAT_T_* here:
- * we are sending traffic endpoints.
- */
-
/* create new sadb_msg to reply. */
n = key_gather_mbuf(m, mhp, 1, 4, SADB_EXT_RESERVED,
SADB_X_EXT_POLICY, SADB_EXT_ADDRESS_SRC, SADB_EXT_ADDRESS_DST);
@@ -2207,30 +2132,42 @@ static int
key_spddelete2(struct socket *so, struct mbuf *m,
const struct sadb_msghdr *mhp)
{
- u_int32_t id;
struct secpolicy *sp;
+ uint32_t id;
IPSEC_ASSERT(so != NULL, ("null socket"));
IPSEC_ASSERT(m != NULL, ("null mbuf"));
IPSEC_ASSERT(mhp != NULL, ("null msghdr"));
IPSEC_ASSERT(mhp->msg != NULL, ("null msg"));
- if (mhp->ext[SADB_X_EXT_POLICY] == NULL ||
- mhp->extlen[SADB_X_EXT_POLICY] < sizeof(struct sadb_x_policy)) {
- ipseclog((LOG_DEBUG, "%s: invalid message is passed.\n", __func__));
+ if (SADB_CHECKHDR(mhp, SADB_X_EXT_POLICY) ||
+ SADB_CHECKLEN(mhp, SADB_X_EXT_POLICY)) {
+ ipseclog((LOG_DEBUG, "%s: invalid message is passed.\n",
+ __func__));
return key_senderror(so, m, EINVAL);
}
- id = ((struct sadb_x_policy *)mhp->ext[SADB_X_EXT_POLICY])->sadb_x_policy_id;
+ id = ((struct sadb_x_policy *)
+ mhp->ext[SADB_X_EXT_POLICY])->sadb_x_policy_id;
/* Is there SP in SPD ? */
if ((sp = key_getspbyid(id)) == NULL) {
- ipseclog((LOG_DEBUG, "%s: no SP found id:%u.\n", __func__, id));
+ ipseclog((LOG_DEBUG, "%s: no SP found for id %u.\n",
+ __func__, id));
return key_senderror(so, m, EINVAL);
}
+ KEYDBG(KEY_STAMP,
+ printf("%s: SP(%p)\n", __func__, sp));
+ KEYDBG(KEY_DATA, kdebug_secpolicy(sp));
key_unlink(sp);
- KEY_FREESP(&sp);
+ if (sp->state != IPSEC_SPSTATE_DEAD) {
+ ipseclog((LOG_DEBUG, "%s: failed to delete SP with id %u.\n",
+ __func__, id));
+ key_freesp(&sp);
+ return (key_senderror(so, m, EACCES));
+ }
+ key_freesp(&sp);
{
struct mbuf *n, *nn;
@@ -2295,33 +2232,35 @@ key_spddelete2(struct socket *so, struct mbuf *m,
static int
key_spdget(struct socket *so, struct mbuf *m, const struct sadb_msghdr *mhp)
{
- u_int32_t id;
struct secpolicy *sp;
struct mbuf *n;
+ uint32_t id;
IPSEC_ASSERT(so != NULL, ("null socket"));
IPSEC_ASSERT(m != NULL, ("null mbuf"));
IPSEC_ASSERT(mhp != NULL, ("null msghdr"));
IPSEC_ASSERT(mhp->msg != NULL, ("null msg"));
- if (mhp->ext[SADB_X_EXT_POLICY] == NULL ||
- mhp->extlen[SADB_X_EXT_POLICY] < sizeof(struct sadb_x_policy)) {
+ if (SADB_CHECKHDR(mhp, SADB_X_EXT_POLICY) ||
+ SADB_CHECKLEN(mhp, SADB_X_EXT_POLICY)) {
ipseclog((LOG_DEBUG, "%s: invalid message is passed.\n",
- __func__));
+ __func__));
return key_senderror(so, m, EINVAL);
}
- id = ((struct sadb_x_policy *)mhp->ext[SADB_X_EXT_POLICY])->sadb_x_policy_id;
+ id = ((struct sadb_x_policy *)
+ mhp->ext[SADB_X_EXT_POLICY])->sadb_x_policy_id;
/* Is there SP in SPD ? */
if ((sp = key_getspbyid(id)) == NULL) {
- ipseclog((LOG_DEBUG, "%s: no SP found id:%u.\n", __func__, id));
+ ipseclog((LOG_DEBUG, "%s: no SP found for id %u.\n",
+ __func__, id));
return key_senderror(so, m, ENOENT);
}
n = key_setdumpsp(sp, SADB_X_SPDGET, mhp->msg->sadb_msg_seq,
mhp->msg->sadb_msg_pid);
- KEY_FREESP(&sp);
+ key_freesp(&sp);
if (n != NULL) {
m_freem(m);
return key_sendup_mbuf(so, n, KEY_SENDUP_ONE);
@@ -2407,7 +2346,7 @@ key_spdacquire(struct secpolicy *sp)
static int
key_spdflush(struct socket *so, struct mbuf *m, const struct sadb_msghdr *mhp)
{
- TAILQ_HEAD(, secpolicy) drainq;
+ struct secpolicy_queue drainq;
struct sadb_msg *newmsg;
struct secpolicy *sp, *nextsp;
u_int dir;
@@ -2428,14 +2367,18 @@ key_spdflush(struct socket *so, struct mbuf *m, const struct sadb_msghdr *mhp)
/*
* We need to set state to DEAD for each policy to be sure,
* that another thread won't try to unlink it.
+ * Also remove SP from sphash.
*/
- TAILQ_FOREACH(sp, &drainq, chain)
+ TAILQ_FOREACH(sp, &drainq, chain) {
sp->state = IPSEC_SPSTATE_DEAD;
+ LIST_REMOVE(sp, idhash);
+ }
+ V_sp_genid++;
SPTREE_WUNLOCK();
sp = TAILQ_FIRST(&drainq);
while (sp != NULL) {
nextsp = TAILQ_NEXT(sp, chain);
- KEY_FREESP(&sp);
+ key_freesp(&sp);
sp = nextsp;
}
@@ -2455,25 +2398,34 @@ key_spdflush(struct socket *so, struct mbuf *m, const struct sadb_msghdr *mhp)
return key_sendup_mbuf(so, m, KEY_SENDUP_ALL);
}
+static uint8_t
+key_satype2scopemask(uint8_t satype)
+{
+
+ if (satype == IPSEC_POLICYSCOPE_ANY)
+ return (0xff);
+ return (satype);
+}
/*
* SADB_SPDDUMP processing
* receive
* <base>
- * from the user, and dump all SP leaves
- * and send,
+ * from the user, and dump all SP leaves and send,
* <base> .....
* to the ikmpd.
*
- * m will always be freed.
+ * NOTE:
+ * sadb_msg_satype is considered as mask of policy scopes.
+ * m will always be freed.
*/
static int
key_spddump(struct socket *so, struct mbuf *m, const struct sadb_msghdr *mhp)
{
SPTREE_RLOCK_TRACKER;
struct secpolicy *sp;
- int cnt;
- u_int dir;
struct mbuf *n;
+ int cnt;
+ u_int dir, scope;
IPSEC_ASSERT(so != NULL, ("null socket"));
IPSEC_ASSERT(m != NULL, ("null mbuf"));
@@ -2482,10 +2434,16 @@ key_spddump(struct socket *so, struct mbuf *m, const struct sadb_msghdr *mhp)
/* search SPD entry and get buffer size. */
cnt = 0;
+ scope = key_satype2scopemask(mhp->msg->sadb_msg_satype);
SPTREE_RLOCK();
for (dir = 0; dir < IPSEC_DIR_MAX; dir++) {
- TAILQ_FOREACH(sp, &V_sptree[dir], chain) {
- cnt++;
+ if (scope & IPSEC_POLICYSCOPE_GLOBAL) {
+ TAILQ_FOREACH(sp, &V_sptree[dir], chain)
+ cnt++;
+ }
+ if (scope & IPSEC_POLICYSCOPE_IFNET) {
+ TAILQ_FOREACH(sp, &V_sptree_ifnet[dir], chain)
+ cnt++;
}
}
@@ -2495,19 +2453,31 @@ key_spddump(struct socket *so, struct mbuf *m, const struct sadb_msghdr *mhp)
}
for (dir = 0; dir < IPSEC_DIR_MAX; dir++) {
- TAILQ_FOREACH(sp, &V_sptree[dir], chain) {
- --cnt;
- n = key_setdumpsp(sp, SADB_X_SPDDUMP, cnt,
- mhp->msg->sadb_msg_pid);
+ if (scope & IPSEC_POLICYSCOPE_GLOBAL) {
+ TAILQ_FOREACH(sp, &V_sptree[dir], chain) {
+ --cnt;
+ n = key_setdumpsp(sp, SADB_X_SPDDUMP, cnt,
+ mhp->msg->sadb_msg_pid);
+
+ if (n != NULL)
+ key_sendup_mbuf(so, n, KEY_SENDUP_ONE);
+ }
+ }
+ if (scope & IPSEC_POLICYSCOPE_IFNET) {
+ TAILQ_FOREACH(sp, &V_sptree_ifnet[dir], chain) {
+ --cnt;
+ n = key_setdumpsp(sp, SADB_X_SPDDUMP, cnt,
+ mhp->msg->sadb_msg_pid);
- if (n)
- key_sendup_mbuf(so, n, KEY_SENDUP_ONE);
+ if (n != NULL)
+ key_sendup_mbuf(so, n, KEY_SENDUP_ONE);
+ }
}
}
SPTREE_RUNLOCK();
m_freem(m);
- return 0;
+ return (0);
}
static struct mbuf *
@@ -2522,10 +2492,6 @@ key_setdumpsp(struct secpolicy *sp, u_int8_t type, u_int32_t seq,
goto fail;
result = m;
- /*
- * Note: do not send SADB_X_EXT_NAT_T_* here:
- * we are sending traffic endpoints.
- */
m = key_setsadbaddr(SADB_EXT_ADDRESS_SRC,
&sp->spidx.src.sa, sp->spidx.prefs,
sp->spidx.ul_proto);
@@ -2540,7 +2506,7 @@ key_setdumpsp(struct secpolicy *sp, u_int8_t type, u_int32_t seq,
goto fail;
m_cat(result, m);
- m = key_sp2msg(sp);
+ m = key_sp2mbuf(sp);
if (!m)
goto fail;
m_cat(result, m);
@@ -2583,36 +2549,29 @@ fail:
m_freem(result);
return NULL;
}
-
/*
* get PFKEY message length for security policy and request.
*/
-static u_int
+static size_t
key_getspreqmsglen(struct secpolicy *sp)
{
- u_int tlen;
+ size_t tlen, len;
+ int i;
tlen = sizeof(struct sadb_x_policy);
-
/* if is the policy for ipsec ? */
if (sp->policy != IPSEC_POLICY_IPSEC)
- return tlen;
+ return (tlen);
/* get length of ipsec requests */
- {
- struct ipsecrequest *isr;
- int len;
-
- for (isr = sp->req; isr != NULL; isr = isr->next) {
+ for (i = 0; i < sp->tcount; i++) {
len = sizeof(struct sadb_x_ipsecrequest)
- + isr->saidx.src.sa.sa_len
- + isr->saidx.dst.sa.sa_len;
+ + sp->req[i]->saidx.src.sa.sa_len
+ + sp->req[i]->saidx.dst.sa.sa_len;
tlen += PFKEY_ALIGN8(len);
}
- }
-
- return tlen;
+ return (tlen);
}
/*
@@ -2627,15 +2586,16 @@ key_getspreqmsglen(struct secpolicy *sp)
static int
key_spdexpire(struct secpolicy *sp)
{
- struct mbuf *result = NULL, *m;
- int len;
- int error = -1;
struct sadb_lifetime *lt;
-
- /* XXX: Why do we lock ? */
+ struct mbuf *result = NULL, *m;
+ int len, error = -1;
IPSEC_ASSERT(sp != NULL, ("null secpolicy"));
+ KEYDBG(KEY_STAMP,
+ printf("%s: SP(%p)\n", __func__, sp));
+ KEYDBG(KEY_DATA, kdebug_secpolicy(sp));
+
/* set msg header */
m = key_setsadbmsg(SADB_X_SPDEXPIRE, 0, 0, 0, 0, 0);
if (!m) {
@@ -2670,11 +2630,6 @@ key_spdexpire(struct secpolicy *sp)
lt->sadb_lifetime_usetime = sp->validtime;
m_cat(result, m);
- /*
- * Note: do not send SADB_X_EXT_NAT_T_* here:
- * we are sending traffic endpoints.
- */
-
/* set sadb_address for source */
m = key_setsadbaddr(SADB_EXT_ADDRESS_SRC,
&sp->spidx.src.sa,
@@ -2696,7 +2651,7 @@ key_spdexpire(struct secpolicy *sp)
m_cat(result, m);
/* set secpolicy */
- m = key_sp2msg(sp);
+ m = key_sp2mbuf(sp);
if (!m) {
error = ENOBUFS;
goto fail;
@@ -2733,174 +2688,220 @@ key_spdexpire(struct secpolicy *sp)
/* %%% SAD management */
/*
- * allocating a memory for new SA head, and copy from the values of mhp.
+ * allocating and initialize new SA head.
* OUT: NULL : failure due to the lack of memory.
* others : pointer to new SA head.
*/
static struct secashead *
key_newsah(struct secasindex *saidx)
{
- struct secashead *newsah;
+ struct secashead *sah;
- IPSEC_ASSERT(saidx != NULL, ("null saidx"));
+ sah = malloc(sizeof(struct secashead), M_IPSEC_SAH,
+ M_NOWAIT | M_ZERO);
+ if (sah == NULL) {
+ PFKEYSTAT_INC(in_nomem);
+ return (NULL);
+ }
+ TAILQ_INIT(&sah->savtree_larval);
+ TAILQ_INIT(&sah->savtree_alive);
+ sah->saidx = *saidx;
+ sah->state = SADB_SASTATE_DEAD;
+ SAH_INITREF(sah);
- newsah = malloc(sizeof(struct secashead), M_IPSEC_SAH, M_NOWAIT|M_ZERO);
- if (newsah != NULL) {
- int i;
- for (i = 0; i < sizeof(newsah->savtree)/sizeof(newsah->savtree[0]); i++)
- LIST_INIT(&newsah->savtree[i]);
- newsah->saidx = *saidx;
+ KEYDBG(KEY_STAMP,
+ printf("%s: SAH(%p)\n", __func__, sah));
+ KEYDBG(KEY_DATA, kdebug_secash(sah, NULL));
+ return (sah);
+}
- /* add to saidxtree */
- newsah->state = SADB_SASTATE_MATURE;
+static void
+key_freesah(struct secashead **psah)
+{
+ struct secashead *sah = *psah;
- SAHTREE_LOCK();
- LIST_INSERT_HEAD(&V_sahtree, newsah, chain);
- SAHTREE_UNLOCK();
- }
- return(newsah);
+ if (SAH_DELREF(sah) == 0)
+ return;
+
+ KEYDBG(KEY_STAMP,
+ printf("%s: last reference to SAH(%p)\n", __func__, sah));
+ KEYDBG(KEY_DATA, kdebug_secash(sah, NULL));
+
+ *psah = NULL;
+ key_delsah(sah);
}
-/*
- * delete SA index and all SA registerd.
- */
static void
key_delsah(struct secashead *sah)
{
- struct secasvar *sav, *nextsav;
- u_int stateidx;
- int zombie = 0;
-
IPSEC_ASSERT(sah != NULL, ("NULL sah"));
- SAHTREE_LOCK_ASSERT();
-
- /* searching all SA registerd in the secindex. */
- for (stateidx = 0;
- stateidx < _ARRAYLEN(saorder_state_any);
- stateidx++) {
- u_int state = saorder_state_any[stateidx];
- LIST_FOREACH_SAFE(sav, &sah->savtree[state], chain, nextsav) {
- if (sav->refcnt == 0) {
- /* sanity check */
- KEY_CHKSASTATE(state, sav->state, __func__);
- /*
- * do NOT call KEY_FREESAV here:
- * it will only delete the sav if refcnt == 1,
- * where we already know that refcnt == 0
- */
- key_delsav(sav);
- } else {
- /* give up to delete this sa */
- zombie++;
- }
- }
- }
- if (!zombie) { /* delete only if there are savs */
- /* remove from tree of SA index */
- if (__LIST_CHAINED(sah))
- LIST_REMOVE(sah, chain);
- free(sah, M_IPSEC_SAH);
- }
+ IPSEC_ASSERT(sah->state == SADB_SASTATE_DEAD,
+ ("Attempt to free non DEAD SAH %p", sah));
+ IPSEC_ASSERT(TAILQ_EMPTY(&sah->savtree_larval),
+ ("Attempt to free SAH %p with LARVAL SA", sah));
+ IPSEC_ASSERT(TAILQ_EMPTY(&sah->savtree_alive),
+ ("Attempt to free SAH %p with ALIVE SA", sah));
+
+ free(sah, M_IPSEC_SAH);
}
/*
- * allocating a new SA with LARVAL state. key_add() and key_getspi() call,
+ * allocating a new SA for key_add() and key_getspi() call,
* and copy the values of mhp into new buffer.
- * When SAD message type is GETSPI:
- * to set sequence number from acq_seq++,
- * to set zero to SPI.
- * not to call key_setsava().
+ * When SAD message type is SADB_GETSPI set SA state to LARVAL.
+ * For SADB_ADD create and initialize SA with MATURE state.
* OUT: NULL : fail
* others : pointer to new secasvar.
- *
- * does not modify mbuf. does not free mbuf on error.
*/
static struct secasvar *
-key_newsav(struct mbuf *m, const struct sadb_msghdr *mhp,
- struct secashead *sah, int *errp, const char *where, int tag)
+key_newsav(const struct sadb_msghdr *mhp, struct secasindex *saidx,
+ uint32_t spi, int *errp)
{
- struct secasvar *newsav;
- const struct sadb_sa *xsa;
+ struct secashead *sah;
+ struct secasvar *sav;
+ int isnew;
- IPSEC_ASSERT(m != NULL, ("null mbuf"));
IPSEC_ASSERT(mhp != NULL, ("null msghdr"));
IPSEC_ASSERT(mhp->msg != NULL, ("null msg"));
- IPSEC_ASSERT(sah != NULL, ("null secashead"));
-
- newsav = malloc(sizeof(struct secasvar), M_IPSEC_SA, M_NOWAIT|M_ZERO);
- if (newsav == NULL) {
- ipseclog((LOG_DEBUG, "%s: No more memory.\n", __func__));
- *errp = ENOBUFS;
- goto done;
- }
+ IPSEC_ASSERT(mhp->msg->sadb_msg_type == SADB_GETSPI ||
+ mhp->msg->sadb_msg_type == SADB_ADD, ("wrong message type"));
- switch (mhp->msg->sadb_msg_type) {
- case SADB_GETSPI:
- newsav->spi = 0;
-
-#ifdef IPSEC_DOSEQCHECK
- /* sync sequence number */
- if (mhp->msg->sadb_msg_seq == 0)
- newsav->seq =
- (V_acq_seq = (V_acq_seq == ~0 ? 1 : ++V_acq_seq));
- else
-#endif
- newsav->seq = mhp->msg->sadb_msg_seq;
- break;
-
- case SADB_ADD:
- /* sanity check */
- if (mhp->ext[SADB_EXT_SA] == NULL) {
- free(newsav, M_IPSEC_SA);
- newsav = NULL;
- ipseclog((LOG_DEBUG, "%s: invalid message is passed.\n",
- __func__));
+ sav = NULL;
+ sah = NULL;
+ /* check SPI value */
+ switch (saidx->proto) {
+ case IPPROTO_ESP:
+ case IPPROTO_AH:
+ /*
+ * RFC 4302, 2.4. Security Parameters Index (SPI), SPI values
+ * 1-255 reserved by IANA for future use,
+ * 0 for implementation specific, local use.
+ */
+ if (ntohl(spi) <= 255) {
+ ipseclog((LOG_DEBUG, "%s: illegal range of SPI %u.\n",
+ __func__, ntohl(spi)));
*errp = EINVAL;
goto done;
}
- xsa = (const struct sadb_sa *)mhp->ext[SADB_EXT_SA];
- newsav->spi = xsa->sadb_sa_spi;
- newsav->seq = mhp->msg->sadb_msg_seq;
break;
- default:
- free(newsav, M_IPSEC_SA);
- newsav = NULL;
- *errp = EINVAL;
- goto done;
}
+ sav = malloc(sizeof(struct secasvar), M_IPSEC_SA, M_NOWAIT | M_ZERO);
+ if (sav == NULL) {
+ *errp = ENOBUFS;
+ goto done;
+ }
+ sav->lock = malloc(sizeof(struct mtx), M_IPSEC_MISC,
+ M_NOWAIT | M_ZERO);
+ if (sav->lock == NULL) {
+ *errp = ENOBUFS;
+ goto done;
+ }
+ mtx_init(sav->lock, "ipsec association", NULL, MTX_DEF);
+ sav->lft_c = uma_zalloc(V_key_lft_zone, M_NOWAIT);
+ if (sav->lft_c == NULL) {
+ *errp = ENOBUFS;
+ goto done;
+ }
+ counter_u64_zero(sav->lft_c_allocations);
+ counter_u64_zero(sav->lft_c_bytes);
- /* copy sav values */
- if (mhp->msg->sadb_msg_type != SADB_GETSPI) {
- *errp = key_setsaval(newsav, m, mhp);
- if (*errp) {
- free(newsav, M_IPSEC_SA);
- newsav = NULL;
+ sav->spi = spi;
+ sav->seq = mhp->msg->sadb_msg_seq;
+ sav->state = SADB_SASTATE_LARVAL;
+ sav->pid = (pid_t)mhp->msg->sadb_msg_pid;
+ SAV_INITREF(sav);
+again:
+ sah = key_getsah(saidx);
+ if (sah == NULL) {
+ /* create a new SA index */
+ sah = key_newsah(saidx);
+ if (sah == NULL) {
+ ipseclog((LOG_DEBUG,
+ "%s: No more memory.\n", __func__));
+ *errp = ENOBUFS;
goto done;
}
- }
-
- SECASVAR_LOCK_INIT(newsav);
-
- /* reset created */
- newsav->created = time_second;
- newsav->pid = mhp->msg->sadb_msg_pid;
+ isnew = 1;
+ } else
+ isnew = 0;
- /* add to satree */
- newsav->sah = sah;
- sa_initref(newsav);
- newsav->state = SADB_SASTATE_LARVAL;
+ sav->sah = sah;
+ if (mhp->msg->sadb_msg_type == SADB_GETSPI) {
+ sav->created = time_second;
+ } else if (sav->state == SADB_SASTATE_LARVAL) {
+ /*
+ * Do not call key_setsaval() second time in case
+ * of `goto again`. We will have MATURE state.
+ */
+ *errp = key_setsaval(sav, mhp);
+ if (*errp != 0)
+ goto done;
+ sav->state = SADB_SASTATE_MATURE;
+ }
- SAHTREE_LOCK();
- LIST_INSERT_TAIL(&sah->savtree[SADB_SASTATE_LARVAL], newsav,
- secasvar, chain);
- SAHTREE_UNLOCK();
+ SAHTREE_WLOCK();
+ /*
+ * Check that existing SAH wasn't unlinked.
+ * Since we didn't hold the SAHTREE lock, it is possible,
+ * that callout handler or key_flush() or key_delete() could
+ * unlink this SAH.
+ */
+ if (isnew == 0 && sah->state == SADB_SASTATE_DEAD) {
+ SAHTREE_WUNLOCK();
+ key_freesah(&sah); /* reference from key_getsah() */
+ goto again;
+ }
+ if (isnew != 0) {
+ /*
+ * Add new SAH into SADB.
+ *
+ * XXXAE: we can serialize key_add and key_getspi calls, so
+ * several threads will not fight in the race.
+ * Otherwise we should check under SAHTREE lock, that this
+ * SAH would not added twice.
+ */
+ TAILQ_INSERT_HEAD(&V_sahtree, sah, chain);
+ /* Add new SAH into hash by addresses */
+ LIST_INSERT_HEAD(SAHADDRHASH_HASH(saidx), sah, addrhash);
+ /* Now we are linked in the chain */
+ sah->state = SADB_SASTATE_MATURE;
+ /*
+ * SAV references this new SAH.
+ * In case of existing SAH we reuse reference
+ * from key_getsah().
+ */
+ SAH_ADDREF(sah);
+ }
+ /* Link SAV with SAH */
+ if (sav->state == SADB_SASTATE_MATURE)
+ TAILQ_INSERT_HEAD(&sah->savtree_alive, sav, chain);
+ else
+ TAILQ_INSERT_HEAD(&sah->savtree_larval, sav, chain);
+ /* Add SAV into SPI hash */
+ LIST_INSERT_HEAD(SAVHASH_HASH(sav->spi), sav, spihash);
+ SAHTREE_WUNLOCK();
+ *errp = 0; /* success */
done:
- KEYDEBUG(KEYDEBUG_IPSEC_STAMP,
- printf("DP %s from %s:%u return SP:%p\n", __func__,
- where, tag, newsav));
-
- return newsav;
+ if (*errp != 0) {
+ if (sav != NULL) {
+ if (sav->lock != NULL) {
+ mtx_destroy(sav->lock);
+ free(sav->lock, M_IPSEC_MISC);
+ }
+ if (sav->lft_c != NULL)
+ uma_zfree(V_key_lft_zone, sav->lft_c);
+ free(sav, M_IPSEC_SA), sav = NULL;
+ }
+ if (sah != NULL)
+ key_freesah(&sah);
+ if (*errp == ENOBUFS) {
+ ipseclog((LOG_DEBUG, "%s: No more memory.\n",
+ __func__));
+ PFKEYSTAT_INC(in_nomem);
+ }
+ }
+ return (sav);
}
/*
@@ -2909,6 +2910,13 @@ done:
static void
key_cleansav(struct secasvar *sav)
{
+
+ if (sav->natt != NULL) {
+ free(sav->natt, M_IPSEC_MISC);
+ sav->natt = NULL;
+ }
+ if (sav->flags & SADB_X_EXT_F_CLONED)
+ return;
/*
* Cleanup xform state. Note that zeroize'ing causes the
* keys to be cleared; otherwise we must do it ourself.
@@ -2934,19 +2942,12 @@ key_cleansav(struct secasvar *sav)
free(sav->key_enc, M_IPSEC_MISC);
sav->key_enc = NULL;
}
- if (sav->sched) {
- bzero(sav->sched, sav->schedlen);
- free(sav->sched, M_IPSEC_MISC);
- sav->sched = NULL;
- }
if (sav->replay != NULL) {
+ if (sav->replay->bitmap != NULL)
+ free(sav->replay->bitmap, M_IPSEC_MISC);
free(sav->replay, M_IPSEC_MISC);
sav->replay = NULL;
}
- if (sav->lft_c != NULL) {
- free(sav->lft_c, M_IPSEC_MISC);
- sav->lft_c = NULL;
- }
if (sav->lft_h != NULL) {
free(sav->lft_h, M_IPSEC_MISC);
sav->lft_h = NULL;
@@ -2964,190 +2965,285 @@ static void
key_delsav(struct secasvar *sav)
{
IPSEC_ASSERT(sav != NULL, ("null sav"));
- IPSEC_ASSERT(sav->refcnt == 0, ("reference count %u > 0", sav->refcnt));
+ IPSEC_ASSERT(sav->state == SADB_SASTATE_DEAD,
+ ("attempt to free non DEAD SA %p", sav));
+ IPSEC_ASSERT(sav->refcnt == 0, ("reference count %u > 0",
+ sav->refcnt));
- /* remove from SA header */
- if (__LIST_CHAINED(sav))
- LIST_REMOVE(sav, chain);
+ /*
+ * SA must be unlinked from the chain and hashtbl.
+ * If SA was cloned, we leave all fields untouched,
+ * except NAT-T config.
+ */
key_cleansav(sav);
- SECASVAR_LOCK_DESTROY(sav);
+ if ((sav->flags & SADB_X_EXT_F_CLONED) == 0) {
+ mtx_destroy(sav->lock);
+ free(sav->lock, M_IPSEC_MISC);
+ uma_zfree(V_key_lft_zone, sav->lft_c);
+ }
free(sav, M_IPSEC_SA);
}
/*
- * search SAD.
+ * search SAH.
* OUT:
* NULL : not found
- * others : found, pointer to a SA.
+ * others : found, referenced pointer to a SAH.
*/
static struct secashead *
key_getsah(struct secasindex *saidx)
{
+ SAHTREE_RLOCK_TRACKER;
struct secashead *sah;
- SAHTREE_LOCK();
- LIST_FOREACH(sah, &V_sahtree, chain) {
- if (sah->state == SADB_SASTATE_DEAD)
- continue;
- if (key_cmpsaidx(&sah->saidx, saidx, CMP_REQID))
- break;
+ SAHTREE_RLOCK();
+ LIST_FOREACH(sah, SAHADDRHASH_HASH(saidx), addrhash) {
+ if (key_cmpsaidx(&sah->saidx, saidx, CMP_MODE_REQID) != 0) {
+ SAH_ADDREF(sah);
+ break;
+ }
}
- SAHTREE_UNLOCK();
-
- return sah;
+ SAHTREE_RUNLOCK();
+ return (sah);
}
/*
- * check not to be duplicated SPI.
- * NOTE: this function is too slow due to searching all SAD.
+ * Check not to be duplicated SPI.
* OUT:
- * NULL : not found
- * others : found, pointer to a SA.
+ * 0 : not found
+ * 1 : found SA with given SPI.
*/
-static struct secasvar *
-key_checkspidup(struct secasindex *saidx, u_int32_t spi)
+static int
+key_checkspidup(uint32_t spi)
{
- struct secashead *sah;
+ SAHTREE_RLOCK_TRACKER;
struct secasvar *sav;
- /* check address family */
- if (saidx->src.sa.sa_family != saidx->dst.sa.sa_family) {
- ipseclog((LOG_DEBUG, "%s: address family mismatched.\n",
- __func__));
- return NULL;
- }
-
- sav = NULL;
- /* check all SAD */
- SAHTREE_LOCK();
- LIST_FOREACH(sah, &V_sahtree, chain) {
- if (!key_ismyaddr((struct sockaddr *)&sah->saidx.dst))
- continue;
- sav = key_getsavbyspi(sah, spi);
- if (sav != NULL)
+ /* Assume SPI is in network byte order */
+ SAHTREE_RLOCK();
+ LIST_FOREACH(sav, SAVHASH_HASH(spi), spihash) {
+ if (sav->spi == spi)
break;
}
- SAHTREE_UNLOCK();
-
- return sav;
+ SAHTREE_RUNLOCK();
+ return (sav != NULL);
}
/*
- * search SAD litmited alive SA, protocol, SPI.
+ * Search SA by SPI.
* OUT:
* NULL : not found
- * others : found, pointer to a SA.
+ * others : found, referenced pointer to a SA.
*/
static struct secasvar *
-key_getsavbyspi(struct secashead *sah, u_int32_t spi)
+key_getsavbyspi(uint32_t spi)
{
+ SAHTREE_RLOCK_TRACKER;
struct secasvar *sav;
- u_int stateidx, state;
-
- sav = NULL;
- SAHTREE_LOCK_ASSERT();
- /* search all status */
- for (stateidx = 0;
- stateidx < _ARRAYLEN(saorder_state_alive);
- stateidx++) {
-
- state = saorder_state_alive[stateidx];
- LIST_FOREACH(sav, &sah->savtree[state], chain) {
-
- /* sanity check */
- if (sav->state != state) {
- ipseclog((LOG_DEBUG, "%s: "
- "invalid sav->state (queue: %d SA: %d)\n",
- __func__, state, sav->state));
- continue;
- }
- if (sav->spi == spi)
- return sav;
- }
+ /* Assume SPI is in network byte order */
+ SAHTREE_RLOCK();
+ LIST_FOREACH(sav, SAVHASH_HASH(spi), spihash) {
+ if (sav->spi != spi)
+ continue;
+ SAV_ADDREF(sav);
+ break;
}
+ SAHTREE_RUNLOCK();
+ return (sav);
+}
- return NULL;
+static int
+key_updatelifetimes(struct secasvar *sav, const struct sadb_msghdr *mhp)
+{
+ struct seclifetime *lft_h, *lft_s, *tmp;
+
+ /* Lifetime extension is optional, check that it is present. */
+ if (SADB_CHECKHDR(mhp, SADB_EXT_LIFETIME_HARD) &&
+ SADB_CHECKHDR(mhp, SADB_EXT_LIFETIME_SOFT)) {
+ /*
+ * In case of SADB_UPDATE we may need to change
+ * existing lifetimes.
+ */
+ if (sav->state == SADB_SASTATE_MATURE) {
+ lft_h = lft_s = NULL;
+ goto reset;
+ }
+ return (0);
+ }
+ /* Both HARD and SOFT extensions must present */
+ if ((SADB_CHECKHDR(mhp, SADB_EXT_LIFETIME_HARD) &&
+ !SADB_CHECKHDR(mhp, SADB_EXT_LIFETIME_SOFT)) ||
+ (SADB_CHECKHDR(mhp, SADB_EXT_LIFETIME_SOFT) &&
+ !SADB_CHECKHDR(mhp, SADB_EXT_LIFETIME_HARD))) {
+ ipseclog((LOG_DEBUG,
+ "%s: invalid message: missing required header.\n",
+ __func__));
+ return (EINVAL);
+ }
+ if (SADB_CHECKLEN(mhp, SADB_EXT_LIFETIME_HARD) ||
+ SADB_CHECKLEN(mhp, SADB_EXT_LIFETIME_SOFT)) {
+ ipseclog((LOG_DEBUG,
+ "%s: invalid message: wrong header size.\n", __func__));
+ return (EINVAL);
+ }
+ lft_h = key_dup_lifemsg((const struct sadb_lifetime *)
+ mhp->ext[SADB_EXT_LIFETIME_HARD], M_IPSEC_MISC);
+ if (lft_h == NULL) {
+ PFKEYSTAT_INC(in_nomem);
+ ipseclog((LOG_DEBUG, "%s: No more memory.\n", __func__));
+ return (ENOBUFS);
+ }
+ lft_s = key_dup_lifemsg((const struct sadb_lifetime *)
+ mhp->ext[SADB_EXT_LIFETIME_SOFT], M_IPSEC_MISC);
+ if (lft_s == NULL) {
+ PFKEYSTAT_INC(in_nomem);
+ free(lft_h, M_IPSEC_MISC);
+ ipseclog((LOG_DEBUG, "%s: No more memory.\n", __func__));
+ return (ENOBUFS);
+ }
+reset:
+ if (sav->state != SADB_SASTATE_LARVAL) {
+ /*
+ * key_update() holds reference to this SA,
+ * so it won't be deleted in meanwhile.
+ */
+ SECASVAR_LOCK(sav);
+ tmp = sav->lft_h;
+ sav->lft_h = lft_h;
+ lft_h = tmp;
+
+ tmp = sav->lft_s;
+ sav->lft_s = lft_s;
+ lft_s = tmp;
+ SECASVAR_UNLOCK(sav);
+ if (lft_h != NULL)
+ free(lft_h, M_IPSEC_MISC);
+ if (lft_s != NULL)
+ free(lft_s, M_IPSEC_MISC);
+ return (0);
+ }
+ /* We can update lifetime without holding a lock */
+ IPSEC_ASSERT(sav->lft_h == NULL, ("lft_h is already initialized\n"));
+ IPSEC_ASSERT(sav->lft_s == NULL, ("lft_s is already initialized\n"));
+ sav->lft_h = lft_h;
+ sav->lft_s = lft_s;
+ return (0);
}
/*
- * copy SA values from PF_KEY message except *SPI, SEQ, PID, STATE and TYPE*.
- * You must update these if need.
+ * copy SA values from PF_KEY message except *SPI, SEQ, PID and TYPE*.
+ * You must update these if need. Expects only LARVAL SAs.
* OUT: 0: success.
* !0: failure.
- *
- * does not modify mbuf. does not free mbuf on error.
*/
static int
-key_setsaval(struct secasvar *sav, struct mbuf *m,
- const struct sadb_msghdr *mhp)
+key_setsaval(struct secasvar *sav, const struct sadb_msghdr *mhp)
{
- int error = 0;
+ const struct sadb_sa *sa0;
+ const struct sadb_key *key0;
+ uint32_t replay;
+ size_t len;
+ int error;
- IPSEC_ASSERT(m != NULL, ("null mbuf"));
IPSEC_ASSERT(mhp != NULL, ("null msghdr"));
IPSEC_ASSERT(mhp->msg != NULL, ("null msg"));
+ IPSEC_ASSERT(sav->state == SADB_SASTATE_LARVAL,
+ ("Attempt to update non LARVAL SA"));
- /* initialization */
- sav->replay = NULL;
- sav->key_auth = NULL;
- sav->key_enc = NULL;
- sav->sched = NULL;
- sav->schedlen = 0;
- sav->lft_c = NULL;
- sav->lft_h = NULL;
- sav->lft_s = NULL;
- sav->tdb_xform = NULL; /* transform */
- sav->tdb_encalgxform = NULL; /* encoding algorithm */
- sav->tdb_authalgxform = NULL; /* authentication algorithm */
- sav->tdb_compalgxform = NULL; /* compression algorithm */
- /* Initialize even if NAT-T not compiled in: */
- sav->natt_type = 0;
- sav->natt_esp_frag_len = 0;
+ /* XXX rewrite */
+ error = key_setident(sav->sah, mhp);
+ if (error != 0)
+ goto fail;
/* SA */
- if (mhp->ext[SADB_EXT_SA] != NULL) {
- const struct sadb_sa *sa0;
-
- sa0 = (const struct sadb_sa *)mhp->ext[SADB_EXT_SA];
- if (mhp->extlen[SADB_EXT_SA] < sizeof(*sa0)) {
+ if (!SADB_CHECKHDR(mhp, SADB_EXT_SA)) {
+ if (SADB_CHECKLEN(mhp, SADB_EXT_SA)) {
error = EINVAL;
goto fail;
}
-
+ sa0 = (const struct sadb_sa *)mhp->ext[SADB_EXT_SA];
sav->alg_auth = sa0->sadb_sa_auth;
sav->alg_enc = sa0->sadb_sa_encrypt;
sav->flags = sa0->sadb_sa_flags;
+ if ((sav->flags & SADB_KEY_FLAGS_MAX) != sav->flags) {
+ ipseclog((LOG_DEBUG,
+ "%s: invalid sa_flags 0x%08x.\n", __func__,
+ sav->flags));
+ error = EINVAL;
+ goto fail;
+ }
- /* replay window */
- if ((sa0->sadb_sa_flags & SADB_X_EXT_OLD) == 0) {
- sav->replay = (struct secreplay *)
- malloc(sizeof(struct secreplay)+sa0->sadb_sa_replay, M_IPSEC_MISC, M_NOWAIT|M_ZERO);
- if (sav->replay == NULL) {
+ /* Optional replay window */
+ replay = 0;
+ if ((sa0->sadb_sa_flags & SADB_X_EXT_OLD) == 0)
+ replay = sa0->sadb_sa_replay;
+ if (!SADB_CHECKHDR(mhp, SADB_X_EXT_SA_REPLAY)) {
+ if (SADB_CHECKLEN(mhp, SADB_X_EXT_SA_REPLAY)) {
+ error = EINVAL;
+ goto fail;
+ }
+ replay = ((const struct sadb_x_sa_replay *)
+ mhp->ext[SADB_X_EXT_SA_REPLAY])->sadb_x_sa_replay_replay;
+
+ if (replay > UINT32_MAX - 32) {
+ ipseclog((LOG_DEBUG,
+ "%s: replay window too big.\n", __func__));
+ error = EINVAL;
+ goto fail;
+ }
+
+ replay = (replay + 7) >> 3;
+ }
+
+ sav->replay = malloc(sizeof(struct secreplay), M_IPSEC_MISC,
+ M_NOWAIT | M_ZERO);
+ if (sav->replay == NULL) {
+ PFKEYSTAT_INC(in_nomem);
+ ipseclog((LOG_DEBUG, "%s: No more memory.\n",
+ __func__));
+ error = ENOBUFS;
+ goto fail;
+ }
+
+ if (replay != 0) {
+ /* number of 32b blocks to be allocated */
+ uint32_t bitmap_size;
+
+ /* RFC 6479:
+ * - the allocated replay window size must be
+ * a power of two.
+ * - use an extra 32b block as a redundant window.
+ */
+ bitmap_size = 1;
+ while (replay + 4 > bitmap_size)
+ bitmap_size <<= 1;
+ bitmap_size = bitmap_size / 4;
+
+ sav->replay->bitmap = malloc(
+ bitmap_size * sizeof(uint32_t), M_IPSEC_MISC,
+ M_NOWAIT | M_ZERO);
+ if (sav->replay->bitmap == NULL) {
+ PFKEYSTAT_INC(in_nomem);
ipseclog((LOG_DEBUG, "%s: No more memory.\n",
__func__));
error = ENOBUFS;
goto fail;
}
- if (sa0->sadb_sa_replay != 0)
- sav->replay->bitmap = (caddr_t)(sav->replay+1);
- sav->replay->wsize = sa0->sadb_sa_replay;
+ sav->replay->bitmap_size = bitmap_size;
+ sav->replay->wsize = replay;
}
}
/* Authentication keys */
- if (mhp->ext[SADB_EXT_KEY_AUTH] != NULL) {
- const struct sadb_key *key0;
- int len;
-
- key0 = (const struct sadb_key *)mhp->ext[SADB_EXT_KEY_AUTH];
- len = mhp->extlen[SADB_EXT_KEY_AUTH];
-
- error = 0;
- if (len < sizeof(*key0)) {
+ if (!SADB_CHECKHDR(mhp, SADB_EXT_KEY_AUTH)) {
+ if (SADB_CHECKLEN(mhp, SADB_EXT_KEY_AUTH)) {
error = EINVAL;
goto fail;
}
+ error = 0;
+ key0 = (const struct sadb_key *)mhp->ext[SADB_EXT_KEY_AUTH];
+ len = mhp->extlen[SADB_EXT_KEY_AUTH];
switch (mhp->msg->sadb_msg_satype) {
case SADB_SATYPE_AH:
case SADB_SATYPE_ESP:
@@ -3167,29 +3263,25 @@ key_setsaval(struct secasvar *sav, struct mbuf *m,
goto fail;
}
- sav->key_auth = (struct seckey *)key_dup_keymsg(key0, len,
- M_IPSEC_MISC);
+ sav->key_auth = key_dup_keymsg(key0, len, M_IPSEC_MISC);
if (sav->key_auth == NULL ) {
ipseclog((LOG_DEBUG, "%s: No more memory.\n",
__func__));
+ PFKEYSTAT_INC(in_nomem);
error = ENOBUFS;
goto fail;
}
}
/* Encryption key */
- if (mhp->ext[SADB_EXT_KEY_ENCRYPT] != NULL) {
- const struct sadb_key *key0;
- int len;
-
- key0 = (const struct sadb_key *)mhp->ext[SADB_EXT_KEY_ENCRYPT];
- len = mhp->extlen[SADB_EXT_KEY_ENCRYPT];
-
- error = 0;
- if (len < sizeof(*key0)) {
+ if (!SADB_CHECKHDR(mhp, SADB_EXT_KEY_ENCRYPT)) {
+ if (SADB_CHECKLEN(mhp, SADB_EXT_KEY_ENCRYPT)) {
error = EINVAL;
goto fail;
}
+ error = 0;
+ key0 = (const struct sadb_key *)mhp->ext[SADB_EXT_KEY_ENCRYPT];
+ len = mhp->extlen[SADB_EXT_KEY_ENCRYPT];
switch (mhp->msg->sadb_msg_satype) {
case SADB_SATYPE_ESP:
if (len == PFKEY_ALIGN8(sizeof(struct sadb_key)) &&
@@ -3197,12 +3289,11 @@ key_setsaval(struct secasvar *sav, struct mbuf *m,
error = EINVAL;
break;
}
- sav->key_enc = (struct seckey *)key_dup_keymsg(key0,
- len,
- M_IPSEC_MISC);
+ sav->key_enc = key_dup_keymsg(key0, len, M_IPSEC_MISC);
if (sav->key_enc == NULL) {
ipseclog((LOG_DEBUG, "%s: No more memory.\n",
__func__));
+ PFKEYSTAT_INC(in_nomem);
error = ENOBUFS;
goto fail;
}
@@ -3227,172 +3318,83 @@ key_setsaval(struct secasvar *sav, struct mbuf *m,
/* set iv */
sav->ivlen = 0;
-
switch (mhp->msg->sadb_msg_satype) {
case SADB_SATYPE_AH:
- error = xform_init(sav, XF_AH);
- break;
- case SADB_SATYPE_ESP:
- error = xform_init(sav, XF_ESP);
- break;
- case SADB_X_SATYPE_IPCOMP:
- error = xform_init(sav, XF_IPCOMP);
- break;
- case SADB_X_SATYPE_TCPSIGNATURE:
- error = xform_init(sav, XF_TCPSIGNATURE);
- break;
- }
- if (error) {
- ipseclog((LOG_DEBUG, "%s: unable to initialize SA type %u.\n",
- __func__, mhp->msg->sadb_msg_satype));
- goto fail;
- }
-
- /* reset created */
- sav->created = time_second;
-
- /* make lifetime for CURRENT */
- sav->lft_c = malloc(sizeof(struct seclifetime), M_IPSEC_MISC, M_NOWAIT);
- if (sav->lft_c == NULL) {
- ipseclog((LOG_DEBUG, "%s: No more memory.\n", __func__));
- error = ENOBUFS;
- goto fail;
- }
-
- sav->lft_c->allocations = 0;
- sav->lft_c->bytes = 0;
- sav->lft_c->addtime = time_second;
- sav->lft_c->usetime = 0;
-
- /* lifetimes for HARD and SOFT */
- {
- const struct sadb_lifetime *lft0;
-
- lft0 = (struct sadb_lifetime *)mhp->ext[SADB_EXT_LIFETIME_HARD];
- if (lft0 != NULL) {
- if (mhp->extlen[SADB_EXT_LIFETIME_HARD] < sizeof(*lft0)) {
+ if (sav->flags & SADB_X_EXT_DERIV) {
+ ipseclog((LOG_DEBUG, "%s: invalid flag (derived) "
+ "given to AH SA.\n", __func__));
error = EINVAL;
goto fail;
}
- sav->lft_h = key_dup_lifemsg(lft0, M_IPSEC_MISC);
- if (sav->lft_h == NULL) {
- ipseclog((LOG_DEBUG, "%s: No more memory.\n",__func__));
- error = ENOBUFS;
- goto fail;
- }
- /* to be initialize ? */
- }
-
- lft0 = (struct sadb_lifetime *)mhp->ext[SADB_EXT_LIFETIME_SOFT];
- if (lft0 != NULL) {
- if (mhp->extlen[SADB_EXT_LIFETIME_SOFT] < sizeof(*lft0)) {
+ if (sav->alg_enc != SADB_EALG_NONE) {
+ ipseclog((LOG_DEBUG, "%s: protocol and algorithm "
+ "mismated.\n", __func__));
error = EINVAL;
goto fail;
}
- sav->lft_s = key_dup_lifemsg(lft0, M_IPSEC_MISC);
- if (sav->lft_s == NULL) {
- ipseclog((LOG_DEBUG, "%s: No more memory.\n",__func__));
- error = ENOBUFS;
- goto fail;
- }
- /* to be initialize ? */
- }
- }
-
- return 0;
-
- fail:
- /* initialization */
- key_cleansav(sav);
-
- return error;
-}
-
-/*
- * validation with a secasvar entry, and set SADB_SATYPE_MATURE.
- * OUT: 0: valid
- * other: errno
- */
-static int
-key_mature(struct secasvar *sav)
-{
- int error;
-
- /* check SPI value */
- switch (sav->sah->saidx.proto) {
- case IPPROTO_ESP:
- case IPPROTO_AH:
- /*
- * RFC 4302, 2.4. Security Parameters Index (SPI), SPI values
- * 1-255 reserved by IANA for future use,
- * 0 for implementation specific, local use.
- */
- if (ntohl(sav->spi) <= 255) {
- ipseclog((LOG_DEBUG, "%s: illegal range of SPI %u.\n",
- __func__, (u_int32_t)ntohl(sav->spi)));
- return EINVAL;
- }
+ error = xform_init(sav, XF_AH);
break;
- }
-
- /* check satype */
- switch (sav->sah->saidx.proto) {
- case IPPROTO_ESP:
- /* check flags */
- if ((sav->flags & (SADB_X_EXT_OLD|SADB_X_EXT_DERIV)) ==
- (SADB_X_EXT_OLD|SADB_X_EXT_DERIV)) {
+ case SADB_SATYPE_ESP:
+ if ((sav->flags & (SADB_X_EXT_OLD | SADB_X_EXT_DERIV)) ==
+ (SADB_X_EXT_OLD | SADB_X_EXT_DERIV)) {
ipseclog((LOG_DEBUG, "%s: invalid flag (derived) "
- "given to old-esp.\n", __func__));
- return EINVAL;
+ "given to old-esp.\n", __func__));
+ error = EINVAL;
+ goto fail;
}
error = xform_init(sav, XF_ESP);
break;
- case IPPROTO_AH:
- /* check flags */
- if (sav->flags & SADB_X_EXT_DERIV) {
- ipseclog((LOG_DEBUG, "%s: invalid flag (derived) "
- "given to AH SA.\n", __func__));
- return EINVAL;
- }
- if (sav->alg_enc != SADB_EALG_NONE) {
- ipseclog((LOG_DEBUG, "%s: protocol and algorithm "
- "mismated.\n", __func__));
- return(EINVAL);
- }
- error = xform_init(sav, XF_AH);
- break;
- case IPPROTO_IPCOMP:
+ case SADB_X_SATYPE_IPCOMP:
if (sav->alg_auth != SADB_AALG_NONE) {
ipseclog((LOG_DEBUG, "%s: protocol and algorithm "
- "mismated.\n", __func__));
- return(EINVAL);
+ "mismated.\n", __func__));
+ error = EINVAL;
+ goto fail;
}
- if ((sav->flags & SADB_X_EXT_RAWCPI) == 0
- && ntohl(sav->spi) >= 0x10000) {
+ if ((sav->flags & SADB_X_EXT_RAWCPI) == 0 &&
+ ntohl(sav->spi) >= 0x10000) {
ipseclog((LOG_DEBUG, "%s: invalid cpi for IPComp.\n",
- __func__));
- return(EINVAL);
+ __func__));
+ error = EINVAL;
+ goto fail;
}
error = xform_init(sav, XF_IPCOMP);
break;
- case IPPROTO_TCP:
+ case SADB_X_SATYPE_TCPSIGNATURE:
if (sav->alg_enc != SADB_EALG_NONE) {
ipseclog((LOG_DEBUG, "%s: protocol and algorithm "
- "mismated.\n", __func__));
- return(EINVAL);
+ "mismated.\n", __func__));
+ error = EINVAL;
+ goto fail;
}
error = xform_init(sav, XF_TCPSIGNATURE);
break;
default:
ipseclog((LOG_DEBUG, "%s: Invalid satype.\n", __func__));
error = EPROTONOSUPPORT;
- break;
+ goto fail;
}
- if (error == 0) {
- SAHTREE_LOCK();
- key_sa_chgstate(sav, SADB_SASTATE_MATURE);
- SAHTREE_UNLOCK();
+ if (error) {
+ ipseclog((LOG_DEBUG, "%s: unable to initialize SA type %u.\n",
+ __func__, mhp->msg->sadb_msg_satype));
+ goto fail;
}
+
+ /* Handle NAT-T headers */
+ error = key_setnatt(sav, mhp);
+ if (error != 0)
+ goto fail;
+
+ /* Initialize lifetime for CURRENT */
+ sav->firstused = 0;
+ sav->created = time_second;
+
+ /* lifetimes for HARD and SOFT */
+ error = key_updatelifetimes(sav, mhp);
+ if (error == 0)
+ return (0);
+fail:
+ key_cleansav(sav);
return (error);
}
@@ -3400,25 +3402,25 @@ key_mature(struct secasvar *sav)
* subroutine for SADB_GET and SADB_DUMP.
*/
static struct mbuf *
-key_setdumpsa(struct secasvar *sav, u_int8_t type, u_int8_t satype,
- u_int32_t seq, u_int32_t pid)
+key_setdumpsa(struct secasvar *sav, uint8_t type, uint8_t satype,
+ uint32_t seq, uint32_t pid)
{
+ struct seclifetime lft_c;
struct mbuf *result = NULL, *tres = NULL, *m;
- int i;
- int dumporder[] = {
- SADB_EXT_SA, SADB_X_EXT_SA2,
+ int i, dumporder[] = {
+ SADB_EXT_SA, SADB_X_EXT_SA2, SADB_X_EXT_SA_REPLAY,
SADB_EXT_LIFETIME_HARD, SADB_EXT_LIFETIME_SOFT,
SADB_EXT_LIFETIME_CURRENT, SADB_EXT_ADDRESS_SRC,
- SADB_EXT_ADDRESS_DST, SADB_EXT_ADDRESS_PROXY, SADB_EXT_KEY_AUTH,
- SADB_EXT_KEY_ENCRYPT, SADB_EXT_IDENTITY_SRC,
- SADB_EXT_IDENTITY_DST, SADB_EXT_SENSITIVITY,
-#ifdef IPSEC_NAT_T
+ SADB_EXT_ADDRESS_DST, SADB_EXT_ADDRESS_PROXY,
+ SADB_EXT_KEY_AUTH, SADB_EXT_KEY_ENCRYPT,
+ SADB_EXT_IDENTITY_SRC, SADB_EXT_IDENTITY_DST,
+ SADB_EXT_SENSITIVITY,
SADB_X_EXT_NAT_T_TYPE,
SADB_X_EXT_NAT_T_SPORT, SADB_X_EXT_NAT_T_DPORT,
SADB_X_EXT_NAT_T_OAI, SADB_X_EXT_NAT_T_OAR,
SADB_X_EXT_NAT_T_FRAG,
-#endif
};
+ uint32_t replay_count;
m = key_setsadbmsg(type, 0, satype, seq, pid, sav->refcnt);
if (m == NULL)
@@ -3435,13 +3437,25 @@ key_setdumpsa(struct secasvar *sav, u_int8_t type, u_int8_t satype,
break;
case SADB_X_EXT_SA2:
- m = key_setsadbxsa2(sav->sah->saidx.mode,
- sav->replay ? sav->replay->count : 0,
+ SECASVAR_LOCK(sav);
+ replay_count = sav->replay ? sav->replay->count : 0;
+ SECASVAR_UNLOCK(sav);
+ m = key_setsadbxsa2(sav->sah->saidx.mode, replay_count,
sav->sah->saidx.reqid);
if (!m)
goto fail;
break;
+ case SADB_X_EXT_SA_REPLAY:
+ if (sav->replay == NULL ||
+ sav->replay->wsize <= UINT8_MAX)
+ continue;
+
+ m = key_setsadbxsareplay(sav->replay->wsize);
+ if (!m)
+ goto fail;
+ break;
+
case SADB_EXT_ADDRESS_SRC:
m = key_setsadbaddr(SADB_EXT_ADDRESS_SRC,
&sav->sah->saidx.src.sa,
@@ -3475,10 +3489,12 @@ key_setdumpsa(struct secasvar *sav, u_int8_t type, u_int8_t satype,
break;
case SADB_EXT_LIFETIME_CURRENT:
- if (!sav->lft_c)
- continue;
- m = key_setlifetime(sav->lft_c,
- SADB_EXT_LIFETIME_CURRENT);
+ 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;
+ m = key_setlifetime(&lft_c, SADB_EXT_LIFETIME_CURRENT);
if (!m)
goto fail;
break;
@@ -3502,35 +3518,53 @@ key_setdumpsa(struct secasvar *sav, u_int8_t type, u_int8_t satype,
goto fail;
break;
-#ifdef IPSEC_NAT_T
case SADB_X_EXT_NAT_T_TYPE:
- m = key_setsadbxtype(sav->natt_type);
+ if (sav->natt == NULL)
+ continue;
+ m = key_setsadbxtype(UDP_ENCAP_ESPINUDP);
if (!m)
goto fail;
break;
-
+
case SADB_X_EXT_NAT_T_DPORT:
- m = key_setsadbxport(
- KEY_PORTFROMSADDR(&sav->sah->saidx.dst),
+ if (sav->natt == NULL)
+ continue;
+ m = key_setsadbxport(sav->natt->dport,
SADB_X_EXT_NAT_T_DPORT);
if (!m)
goto fail;
break;
case SADB_X_EXT_NAT_T_SPORT:
- m = key_setsadbxport(
- KEY_PORTFROMSADDR(&sav->sah->saidx.src),
+ if (sav->natt == NULL)
+ continue;
+ m = key_setsadbxport(sav->natt->sport,
SADB_X_EXT_NAT_T_SPORT);
if (!m)
goto fail;
break;
case SADB_X_EXT_NAT_T_OAI:
+ if (sav->natt == NULL ||
+ (sav->natt->flags & IPSEC_NATT_F_OAI) == 0)
+ continue;
+ m = key_setsadbaddr(SADB_X_EXT_NAT_T_OAI,
+ &sav->natt->oai.sa, FULLMASK, IPSEC_ULPROTO_ANY);
+ if (!m)
+ goto fail;
+ break;
case SADB_X_EXT_NAT_T_OAR:
+ if (sav->natt == NULL ||
+ (sav->natt->flags & IPSEC_NATT_F_OAR) == 0)
+ continue;
+ m = key_setsadbaddr(SADB_X_EXT_NAT_T_OAR,
+ &sav->natt->oar.sa, FULLMASK, IPSEC_ULPROTO_ANY);
+ if (!m)
+ goto fail;
+ break;
case SADB_X_EXT_NAT_T_FRAG:
/* We do not (yet) support those. */
continue;
-#endif
case SADB_EXT_ADDRESS_PROXY:
case SADB_EXT_IDENTITY_SRC:
@@ -3546,7 +3580,6 @@ key_setdumpsa(struct secasvar *sav, u_int8_t type, u_int8_t satype,
if (tres)
m_cat(m, tres);
tres = m;
-
}
m_cat(result, tres);
@@ -3634,13 +3667,14 @@ key_setsadbsa(struct secasvar *sav)
p->sadb_sa_len = PFKEY_UNIT64(len);
p->sadb_sa_exttype = SADB_EXT_SA;
p->sadb_sa_spi = sav->spi;
- p->sadb_sa_replay = (sav->replay != NULL ? sav->replay->wsize : 0);
+ p->sadb_sa_replay = sav->replay ?
+ (sav->replay->wsize > UINT8_MAX ? UINT8_MAX :
+ sav->replay->wsize): 0;
p->sadb_sa_state = sav->state;
p->sadb_sa_auth = sav->alg_auth;
p->sadb_sa_encrypt = sav->alg_enc;
- p->sadb_sa_flags = sav->flags;
-
- return m;
+ p->sadb_sa_flags = sav->flags & SADB_KEY_FLAGS_MAX;
+ return (m);
}
/*
@@ -3719,7 +3753,32 @@ key_setsadbxsa2(u_int8_t mode, u_int32_t seq, u_int32_t reqid)
return m;
}
-#ifdef IPSEC_NAT_T
+/*
+ * Set data into sadb_x_sa_replay.
+ */
+static struct mbuf *
+key_setsadbxsareplay(u_int32_t replay)
+{
+ struct mbuf *m;
+ struct sadb_x_sa_replay *p;
+ size_t len;
+
+ len = PFKEY_ALIGN8(sizeof(struct sadb_x_sa_replay));
+ m = m_get2(len, M_NOWAIT, MT_DATA, 0);
+ if (m == NULL)
+ return (NULL);
+ m_align(m, len);
+ m->m_len = len;
+ p = mtod(m, struct sadb_x_sa_replay *);
+
+ bzero(p, len);
+ p->sadb_x_sa_replay_len = PFKEY_UNIT64(len);
+ p->sadb_x_sa_replay_exttype = SADB_X_EXT_SA_REPLAY;
+ p->sadb_x_sa_replay_replay = (replay << 3);
+
+ return m;
+}
+
/*
* Set a type in sadb_x_nat_t_type.
*/
@@ -3774,10 +3833,10 @@ key_setsadbxport(u_int16_t port, u_int16_t type)
return (m);
}
-/*
+/*
* Get port from sockaddr. Port is in network byte order.
*/
-u_int16_t
+uint16_t
key_portfromsaddr(struct sockaddr *sa)
{
@@ -3791,18 +3850,14 @@ key_portfromsaddr(struct sockaddr *sa)
return ((struct sockaddr_in6 *)sa)->sin6_port;
#endif
}
- KEYDEBUG(KEYDEBUG_IPSEC_STAMP,
- printf("DP %s unexpected address family %d\n",
- __func__, sa->sa_family));
return (0);
}
-#endif /* IPSEC_NAT_T */
/*
* Set port in struct sockaddr. Port is in network byte order.
*/
-static void
-key_porttosaddr(struct sockaddr *sa, u_int16_t port)
+void
+key_porttosaddr(struct sockaddr *sa, uint16_t port)
{
switch (sa->sa_family) {
@@ -3860,29 +3915,29 @@ key_setsadbxpolicy(u_int16_t type, u_int8_t dir, u_int32_t id, u_int32_t priorit
* OUT: NULL no more memory
*/
struct seckey *
-key_dup_keymsg(const struct sadb_key *src, u_int len,
+key_dup_keymsg(const struct sadb_key *src, size_t len,
struct malloc_type *type)
{
struct seckey *dst;
- dst = (struct seckey *)malloc(sizeof(struct seckey), type, M_NOWAIT);
+
+ dst = malloc(sizeof(*dst), type, M_NOWAIT);
if (dst != NULL) {
dst->bits = src->sadb_key_bits;
- dst->key_data = (char *)malloc(len, type, M_NOWAIT);
+ dst->key_data = malloc(len, type, M_NOWAIT);
if (dst->key_data != NULL) {
- bcopy((const char *)src + sizeof(struct sadb_key),
- dst->key_data, len);
+ bcopy((const char *)(src + 1), dst->key_data, len);
} else {
- ipseclog((LOG_DEBUG, "%s: No more memory.\n",
- __func__));
+ ipseclog((LOG_DEBUG, "%s: No more memory.\n",
+ __func__));
free(dst, type);
dst = NULL;
}
} else {
- ipseclog((LOG_DEBUG, "%s: No more memory.\n",
- __func__));
+ ipseclog((LOG_DEBUG, "%s: No more memory.\n",
+ __func__));
}
- return dst;
+ return (dst);
}
/* Take a lifetime message (sadb_lifetime) passed in on a socket and
@@ -3895,66 +3950,20 @@ key_dup_keymsg(const struct sadb_key *src, u_int len,
static struct seclifetime *
key_dup_lifemsg(const struct sadb_lifetime *src, struct malloc_type *type)
{
- struct seclifetime *dst = NULL;
+ struct seclifetime *dst;
- dst = (struct seclifetime *)malloc(sizeof(struct seclifetime),
- type, M_NOWAIT);
+ dst = malloc(sizeof(*dst), type, M_NOWAIT);
if (dst == NULL) {
- /* XXX counter */
ipseclog((LOG_DEBUG, "%s: No more memory.\n", __func__));
- } else {
- dst->allocations = src->sadb_lifetime_allocations;
- dst->bytes = src->sadb_lifetime_bytes;
- dst->addtime = src->sadb_lifetime_addtime;
- dst->usetime = src->sadb_lifetime_usetime;
- }
- return dst;
-}
-
-/* compare my own address
- * OUT: 1: true, i.e. my address.
- * 0: false
- */
-int
-key_ismyaddr(struct sockaddr *sa)
-{
-
- IPSEC_ASSERT(sa != NULL, ("null sockaddr"));
- switch (sa->sa_family) {
-#ifdef INET
- case AF_INET:
- return (in_localip(satosin(sa)->sin_addr));
-#endif
-#ifdef INET6
- case AF_INET6:
- return key_ismyaddr6((struct sockaddr_in6 *)sa);
-#endif
+ return (NULL);
}
-
- return 0;
+ dst->allocations = src->sadb_lifetime_allocations;
+ dst->bytes = src->sadb_lifetime_bytes;
+ dst->addtime = src->sadb_lifetime_addtime;
+ dst->usetime = src->sadb_lifetime_usetime;
+ return (dst);
}
-#ifdef INET6
-/*
- * compare my own address for IPv6.
- * 1: ours
- * 0: other
- */
-static int
-key_ismyaddr6(struct sockaddr_in6 *sin6)
-{
- struct in6_addr in6;
-
- if (!IN6_IS_SCOPE_LINKLOCAL(&sin6->sin6_addr))
- return (in6_localip(&sin6->sin6_addr));
-
- /* Convert address into kernel-internal form */
- in6 = sin6->sin6_addr;
- in6.s6_addr16[1] = htons(sin6->sin6_scope_id & 0xffff);
- return (in6_localip(&in6));
-}
-#endif /*INET6*/
-
/*
* compare two secasindex structure.
* flag can specify to compare 2 saidxes.
@@ -3971,7 +3980,6 @@ static int
key_cmpsaidx(const struct secasindex *saidx0, const struct secasindex *saidx1,
int flag)
{
- int chkport = 0;
/* sanity */
if (saidx0 == NULL && saidx1 == NULL)
@@ -3988,19 +3996,21 @@ key_cmpsaidx(const struct secasindex *saidx0, const struct secasindex *saidx1,
return 0;
if (saidx0->reqid != saidx1->reqid)
return 0;
- if (bcmp(&saidx0->src, &saidx1->src, saidx0->src.sa.sa_len) != 0 ||
- bcmp(&saidx0->dst, &saidx1->dst, saidx0->dst.sa.sa_len) != 0)
+ if (bcmp(&saidx0->src, &saidx1->src,
+ saidx0->src.sa.sa_len) != 0 ||
+ bcmp(&saidx0->dst, &saidx1->dst,
+ saidx0->dst.sa.sa_len) != 0)
return 0;
} else {
/* CMP_MODE_REQID, CMP_REQID, CMP_HEAD */
- if (flag == CMP_MODE_REQID
- ||flag == CMP_REQID) {
+ if (flag == CMP_MODE_REQID || flag == CMP_REQID) {
/*
* If reqid of SPD is non-zero, unique SA is required.
* The result must be of same reqid in this case.
*/
- if (saidx1->reqid != 0 && saidx0->reqid != saidx1->reqid)
+ if (saidx1->reqid != 0 &&
+ saidx0->reqid != saidx1->reqid)
return 0;
}
@@ -4010,29 +4020,10 @@ key_cmpsaidx(const struct secasindex *saidx0, const struct secasindex *saidx1,
return 0;
}
-#ifdef IPSEC_NAT_T
- /*
- * If NAT-T is enabled, check ports for tunnel mode.
- * Do not check ports if they are set to zero in the SPD.
- * Also do not do it for native transport mode, as there
- * is no port information available in the SP.
- */
- if ((saidx1->mode == IPSEC_MODE_TUNNEL ||
- (saidx1->mode == IPSEC_MODE_TRANSPORT &&
- saidx1->proto == IPPROTO_ESP)) &&
- saidx1->src.sa.sa_family == AF_INET &&
- saidx1->dst.sa.sa_family == AF_INET &&
- ((const struct sockaddr_in *)(&saidx1->src))->sin_port &&
- ((const struct sockaddr_in *)(&saidx1->dst))->sin_port)
- chkport = 1;
-#endif /* IPSEC_NAT_T */
-
- if (key_sockaddrcmp(&saidx0->src.sa, &saidx1->src.sa, chkport) != 0) {
+ if (key_sockaddrcmp(&saidx0->src.sa, &saidx1->src.sa, 0) != 0)
return 0;
- }
- if (key_sockaddrcmp(&saidx0->dst.sa, &saidx1->dst.sa, chkport) != 0) {
+ if (key_sockaddrcmp(&saidx0->dst.sa, &saidx1->dst.sa, 0) != 0)
return 0;
- }
}
return 1;
@@ -4167,11 +4158,6 @@ key_cmpspidx_withmask(struct secpolicyindex *spidx0,
return 1;
}
-/* returns 0 on match */
-static int
-key_sockaddrcmp(const struct sockaddr *sa1, const struct sockaddr *sa2,
- int port)
-{
#ifdef satosin
#undef satosin
#endif
@@ -4180,10 +4166,16 @@ key_sockaddrcmp(const struct sockaddr *sa1, const struct sockaddr *sa2,
#undef satosin6
#endif
#define satosin6(s) ((const struct sockaddr_in6 *)s)
+/* returns 0 on match */
+int
+key_sockaddrcmp(const struct sockaddr *sa1, const struct sockaddr *sa2,
+ int port)
+{
if (sa1->sa_family != sa2->sa_family || sa1->sa_len != sa2->sa_len)
return 1;
switch (sa1->sa_family) {
+#ifdef INET
case AF_INET:
if (sa1->sa_len != sizeof(struct sockaddr_in))
return 1;
@@ -4194,6 +4186,8 @@ key_sockaddrcmp(const struct sockaddr *sa1, const struct sockaddr *sa2,
if (port && satosin(sa1)->sin_port != satosin(sa2)->sin_port)
return 1;
break;
+#endif
+#ifdef INET6
case AF_INET6:
if (sa1->sa_len != sizeof(struct sockaddr_in6))
return 1; /*EINVAL*/
@@ -4210,6 +4204,7 @@ key_sockaddrcmp(const struct sockaddr *sa1, const struct sockaddr *sa2,
return 1;
}
break;
+#endif
default:
if (bcmp(sa1, sa2, sa1->sa_len) != 0)
return 1;
@@ -4217,9 +4212,35 @@ key_sockaddrcmp(const struct sockaddr *sa1, const struct sockaddr *sa2,
}
return 0;
+}
+
+/* returns 0 on match */
+int
+key_sockaddrcmp_withmask(const struct sockaddr *sa1,
+ const struct sockaddr *sa2, size_t mask)
+{
+ if (sa1->sa_family != sa2->sa_family || sa1->sa_len != sa2->sa_len)
+ return (1);
+
+ switch (sa1->sa_family) {
+#ifdef INET
+ case AF_INET:
+ return (!key_bbcmp(&satosin(sa1)->sin_addr,
+ &satosin(sa2)->sin_addr, mask));
+#endif
+#ifdef INET6
+ case AF_INET6:
+ if (satosin6(sa1)->sin6_scope_id !=
+ satosin6(sa2)->sin6_scope_id)
+ return (1);
+ return (!key_bbcmp(&satosin6(sa1)->sin6_addr,
+ &satosin6(sa2)->sin6_addr, mask));
+#endif
+ }
+ return (1);
+}
#undef satosin
#undef satosin6
-}
/*
* compare two buffers with mask.
@@ -4262,13 +4283,13 @@ static void
key_flush_spd(time_t now)
{
SPTREE_RLOCK_TRACKER;
- struct secpolicy *sp;
+ struct secpolicy_list drainq;
+ struct secpolicy *sp, *nextsp;
u_int dir;
- /* SPD */
+ LIST_INIT(&drainq);
+ SPTREE_RLOCK();
for (dir = 0; dir < IPSEC_DIR_MAX; dir++) {
-restart:
- SPTREE_RLOCK();
TAILQ_FOREACH(sp, &V_sptree[dir], chain) {
if (sp->lifetime == 0 && sp->validtime == 0)
continue;
@@ -4276,53 +4297,86 @@ restart:
now - sp->created > sp->lifetime) ||
(sp->validtime &&
now - sp->lastused > sp->validtime)) {
+ /* Hold extra reference to send SPDEXPIRE */
SP_ADDREF(sp);
- SPTREE_RUNLOCK();
- key_spdexpire(sp);
- key_unlink(sp);
- KEY_FREESP(&sp);
- goto restart;
+ LIST_INSERT_HEAD(&drainq, sp, drainq);
}
}
- SPTREE_RUNLOCK();
+ }
+ SPTREE_RUNLOCK();
+ if (LIST_EMPTY(&drainq))
+ return;
+
+ SPTREE_WLOCK();
+ sp = LIST_FIRST(&drainq);
+ while (sp != NULL) {
+ nextsp = LIST_NEXT(sp, drainq);
+ /* Check that SP is still linked */
+ if (sp->state != IPSEC_SPSTATE_ALIVE) {
+ LIST_REMOVE(sp, drainq);
+ key_freesp(&sp); /* release extra reference */
+ sp = nextsp;
+ continue;
+ }
+ TAILQ_REMOVE(&V_sptree[sp->spidx.dir], sp, chain);
+ LIST_REMOVE(sp, idhash);
+ sp->state = IPSEC_SPSTATE_DEAD;
+ sp = nextsp;
+ }
+ V_sp_genid++;
+ SPTREE_WUNLOCK();
+
+ sp = LIST_FIRST(&drainq);
+ while (sp != NULL) {
+ nextsp = LIST_NEXT(sp, drainq);
+ key_spdexpire(sp);
+ key_freesp(&sp); /* release extra reference */
+ key_freesp(&sp); /* release last reference */
+ sp = nextsp;
}
}
static void
key_flush_sad(time_t now)
{
+ SAHTREE_RLOCK_TRACKER;
+ struct secashead_list emptyq;
+ struct secasvar_list drainq, hexpireq, sexpireq, freeq;
struct secashead *sah, *nextsah;
struct secasvar *sav, *nextsav;
- /* SAD */
- SAHTREE_LOCK();
- LIST_FOREACH_SAFE(sah, &V_sahtree, chain, nextsah) {
- /* if sah has been dead, then delete it and process next sah. */
- if (sah->state == SADB_SASTATE_DEAD) {
- key_delsah(sah);
+ LIST_INIT(&drainq);
+ LIST_INIT(&hexpireq);
+ LIST_INIT(&sexpireq);
+ LIST_INIT(&emptyq);
+
+ SAHTREE_RLOCK();
+ TAILQ_FOREACH(sah, &V_sahtree, chain) {
+ /* Check for empty SAH */
+ if (TAILQ_EMPTY(&sah->savtree_larval) &&
+ TAILQ_EMPTY(&sah->savtree_alive)) {
+ SAH_ADDREF(sah);
+ LIST_INSERT_HEAD(&emptyq, sah, drainq);
continue;
}
-
- /* if LARVAL entry doesn't become MATURE, delete it. */
- LIST_FOREACH_SAFE(sav, &sah->savtree[SADB_SASTATE_LARVAL], chain, nextsav) {
- /* Need to also check refcnt for a larval SA ??? */
- if (now - sav->created > V_key_larval_lifetime)
- KEY_FREESAV(&sav);
+ /* Add all stale LARVAL SAs into drainq */
+ TAILQ_FOREACH(sav, &sah->savtree_larval, chain) {
+ if (now - sav->created < V_key_larval_lifetime)
+ continue;
+ SAV_ADDREF(sav);
+ LIST_INSERT_HEAD(&drainq, sav, drainq);
}
-
- /*
- * check MATURE entry to start to send expire message
- * whether or not.
- */
- LIST_FOREACH_SAFE(sav, &sah->savtree[SADB_SASTATE_MATURE], chain, nextsav) {
- /* we don't need to check. */
- if (sav->lft_s == NULL)
+ TAILQ_FOREACH(sav, &sah->savtree_alive, chain) {
+ /* lifetimes aren't specified */
+ if (sav->lft_h == NULL)
continue;
-
- /* sanity check */
- if (sav->lft_c == NULL) {
- ipseclog((LOG_DEBUG,"%s: there is no CURRENT "
- "time, why?\n", __func__));
+ SECASVAR_LOCK(sav);
+ /*
+ * Check again with lock held, because it may
+ * be updated by SADB_UPDATE.
+ */
+ if (sav->lft_h == NULL) {
+ SECASVAR_UNLOCK(sav);
continue;
}
/*
@@ -4335,84 +4389,149 @@ key_flush_sad(time_t now)
/* check HARD lifetime */
if ((sav->lft_h->addtime != 0 &&
now - sav->created > sav->lft_h->addtime) ||
- (sav->lft_h->bytes != 0 &&
- sav->lft_h->bytes < sav->lft_c->bytes)) {
- key_sa_chgstate(sav, SADB_SASTATE_DEAD);
- key_expire(sav, 1);
- KEY_FREESAV(&sav);
+ (sav->lft_h->usetime != 0 && sav->firstused &&
+ now - sav->firstused > sav->lft_h->usetime) ||
+ (sav->lft_h->bytes != 0 && counter_u64_fetch(
+ sav->lft_c_bytes) > sav->lft_h->bytes)) {
+ SECASVAR_UNLOCK(sav);
+ SAV_ADDREF(sav);
+ LIST_INSERT_HEAD(&hexpireq, sav, drainq);
+ continue;
}
- /* check SOFT lifetime */
- else if ((sav->lft_s->addtime != 0 &&
+ /* check SOFT lifetime (only for MATURE SAs) */
+ if (sav->state == SADB_SASTATE_MATURE && (
+ (sav->lft_s->addtime != 0 &&
now - sav->created > sav->lft_s->addtime) ||
- (sav->lft_s->bytes != 0 &&
- sav->lft_s->bytes < sav->lft_c->bytes)) {
- key_sa_chgstate(sav, SADB_SASTATE_DYING);
- key_expire(sav, 0);
- }
- }
-
- /* check DYING entry to change status to DEAD. */
- LIST_FOREACH_SAFE(sav, &sah->savtree[SADB_SASTATE_DYING], chain, nextsav) {
- /* we don't need to check. */
- if (sav->lft_h == NULL)
- continue;
-
- /* sanity check */
- if (sav->lft_c == NULL) {
- ipseclog((LOG_DEBUG, "%s: there is no CURRENT "
- "time, why?\n", __func__));
+ (sav->lft_s->usetime != 0 && sav->firstused &&
+ now - sav->firstused > sav->lft_s->usetime) ||
+ (sav->lft_s->bytes != 0 && counter_u64_fetch(
+ sav->lft_c_bytes) > sav->lft_s->bytes))) {
+ SECASVAR_UNLOCK(sav);
+ SAV_ADDREF(sav);
+ LIST_INSERT_HEAD(&sexpireq, sav, drainq);
continue;
}
+ SECASVAR_UNLOCK(sav);
+ }
+ }
+ SAHTREE_RUNLOCK();
- if (sav->lft_h->addtime != 0 &&
- now - sav->created > sav->lft_h->addtime) {
- key_sa_chgstate(sav, SADB_SASTATE_DEAD);
- key_expire(sav, 1);
- KEY_FREESAV(&sav);
- }
-#if 0 /* XXX Should we keep to send expire message until HARD lifetime ? */
- else if (sav->lft_s != NULL
- && sav->lft_s->addtime != 0
- && now - sav->created > sav->lft_s->addtime) {
- /*
- * XXX: should be checked to be
- * installed the valid SA.
- */
+ if (LIST_EMPTY(&emptyq) && LIST_EMPTY(&drainq) &&
+ LIST_EMPTY(&hexpireq) && LIST_EMPTY(&sexpireq))
+ return;
- /*
- * If there is no SA then sending
- * expire message.
- */
- key_expire(sav, 0);
- }
-#endif
- /* check HARD lifetime by bytes */
- else if (sav->lft_h->bytes != 0 &&
- sav->lft_h->bytes < sav->lft_c->bytes) {
- key_sa_chgstate(sav, SADB_SASTATE_DEAD);
- key_expire(sav, 1);
- KEY_FREESAV(&sav);
- }
+ LIST_INIT(&freeq);
+ SAHTREE_WLOCK();
+ /* Unlink stale LARVAL SAs */
+ sav = LIST_FIRST(&drainq);
+ while (sav != NULL) {
+ nextsav = LIST_NEXT(sav, drainq);
+ /* Check that SA is still LARVAL */
+ if (sav->state != SADB_SASTATE_LARVAL) {
+ LIST_REMOVE(sav, drainq);
+ LIST_INSERT_HEAD(&freeq, sav, drainq);
+ sav = nextsav;
+ continue;
}
-
- /* delete entry in DEAD */
- LIST_FOREACH_SAFE(sav, &sah->savtree[SADB_SASTATE_DEAD], chain, nextsav) {
- /* sanity check */
- if (sav->state != SADB_SASTATE_DEAD) {
- ipseclog((LOG_DEBUG, "%s: invalid sav->state "
- "(queue: %d SA: %d): kill it anyway\n",
- __func__,
- SADB_SASTATE_DEAD, sav->state));
- }
- /*
- * do not call key_freesav() here.
- * sav should already be freed, and sav->refcnt
- * shows other references to sav
- * (such as from SPD).
- */
+ TAILQ_REMOVE(&sav->sah->savtree_larval, sav, chain);
+ LIST_REMOVE(sav, spihash);
+ sav->state = SADB_SASTATE_DEAD;
+ sav = nextsav;
+ }
+ /* Unlink all SAs with expired HARD lifetime */
+ sav = LIST_FIRST(&hexpireq);
+ while (sav != NULL) {
+ nextsav = LIST_NEXT(sav, drainq);
+ /* Check that SA is not unlinked */
+ if (sav->state == SADB_SASTATE_DEAD) {
+ LIST_REMOVE(sav, drainq);
+ LIST_INSERT_HEAD(&freeq, sav, drainq);
+ sav = nextsav;
+ continue;
}
+ TAILQ_REMOVE(&sav->sah->savtree_alive, sav, chain);
+ LIST_REMOVE(sav, spihash);
+ sav->state = SADB_SASTATE_DEAD;
+ sav = nextsav;
+ }
+ /* Mark all SAs with expired SOFT lifetime as DYING */
+ sav = LIST_FIRST(&sexpireq);
+ while (sav != NULL) {
+ nextsav = LIST_NEXT(sav, drainq);
+ /* Check that SA is not unlinked */
+ if (sav->state == SADB_SASTATE_DEAD) {
+ LIST_REMOVE(sav, drainq);
+ LIST_INSERT_HEAD(&freeq, sav, drainq);
+ sav = nextsav;
+ continue;
+ }
+ /*
+ * NOTE: this doesn't change SA order in the chain.
+ */
+ sav->state = SADB_SASTATE_DYING;
+ sav = nextsav;
+ }
+ /* Unlink empty SAHs */
+ sah = LIST_FIRST(&emptyq);
+ while (sah != NULL) {
+ nextsah = LIST_NEXT(sah, drainq);
+ /* Check that SAH is still empty and not unlinked */
+ if (sah->state == SADB_SASTATE_DEAD ||
+ !TAILQ_EMPTY(&sah->savtree_larval) ||
+ !TAILQ_EMPTY(&sah->savtree_alive)) {
+ LIST_REMOVE(sah, drainq);
+ key_freesah(&sah); /* release extra reference */
+ sah = nextsah;
+ continue;
+ }
+ TAILQ_REMOVE(&V_sahtree, sah, chain);
+ LIST_REMOVE(sah, addrhash);
+ sah->state = SADB_SASTATE_DEAD;
+ sah = nextsah;
+ }
+ SAHTREE_WUNLOCK();
+
+ /* Send SPDEXPIRE messages */
+ sav = LIST_FIRST(&hexpireq);
+ while (sav != NULL) {
+ nextsav = LIST_NEXT(sav, drainq);
+ key_expire(sav, 1);
+ key_freesah(&sav->sah); /* release reference from SAV */
+ key_freesav(&sav); /* release extra reference */
+ key_freesav(&sav); /* release last reference */
+ sav = nextsav;
+ }
+ sav = LIST_FIRST(&sexpireq);
+ while (sav != NULL) {
+ nextsav = LIST_NEXT(sav, drainq);
+ key_expire(sav, 0);
+ key_freesav(&sav); /* release extra reference */
+ sav = nextsav;
+ }
+ /* Free stale LARVAL SAs */
+ sav = LIST_FIRST(&drainq);
+ while (sav != NULL) {
+ nextsav = LIST_NEXT(sav, drainq);
+ key_freesah(&sav->sah); /* release reference from SAV */
+ key_freesav(&sav); /* release extra reference */
+ key_freesav(&sav); /* release last reference */
+ sav = nextsav;
+ }
+ /* Free SAs that were unlinked/changed by someone else */
+ sav = LIST_FIRST(&freeq);
+ while (sav != NULL) {
+ nextsav = LIST_NEXT(sav, drainq);
+ key_freesav(&sav); /* release extra reference */
+ sav = nextsav;
+ }
+ /* Free empty SAH */
+ sah = LIST_FIRST(&emptyq);
+ while (sah != NULL) {
+ nextsah = LIST_NEXT(sah, drainq);
+ key_freesah(&sah); /* release extra reference */
+ key_freesah(&sah); /* release last reference */
+ sah = nextsah;
}
- SAHTREE_UNLOCK();
}
static void
@@ -4422,13 +4541,16 @@ key_flush_acq(time_t now)
/* ACQ tree */
ACQ_LOCK();
- for (acq = LIST_FIRST(&V_acqtree); acq != NULL; acq = nextacq) {
+ acq = LIST_FIRST(&V_acqtree);
+ while (acq != NULL) {
nextacq = LIST_NEXT(acq, chain);
- if (now - acq->created > V_key_blockacq_lifetime
- && __LIST_CHAINED(acq)) {
+ if (now - acq->created > V_key_blockacq_lifetime) {
LIST_REMOVE(acq, chain);
+ LIST_REMOVE(acq, addrhash);
+ LIST_REMOVE(acq, seqhash);
free(acq, M_IPSEC_SAQ);
}
+ acq = nextacq;
}
ACQ_UNLOCK();
}
@@ -4519,8 +4641,8 @@ key_randomfill(void *p, size_t l)
* OUT:
* 0: invalid satype.
*/
-static u_int16_t
-key_satype2proto(u_int8_t satype)
+static uint8_t
+key_satype2proto(uint8_t satype)
{
switch (satype) {
case SADB_SATYPE_UNSPEC:
@@ -4544,8 +4666,8 @@ key_satype2proto(u_int8_t satype)
* OUT:
* 0: invalid protocol type.
*/
-static u_int8_t
-key_proto2satype(u_int16_t proto)
+static uint8_t
+key_proto2satype(uint8_t proto)
{
switch (proto) {
case IPPROTO_AH:
@@ -4578,39 +4700,56 @@ key_proto2satype(u_int16_t proto)
static int
key_getspi(struct socket *so, struct mbuf *m, const struct sadb_msghdr *mhp)
{
- struct sadb_address *src0, *dst0;
struct secasindex saidx;
- struct secashead *newsah;
- struct secasvar *newsav;
- u_int8_t proto;
- u_int32_t spi;
- u_int8_t mode;
- u_int32_t reqid;
+ struct sadb_address *src0, *dst0;
+ struct secasvar *sav;
+ uint32_t reqid, spi;
int error;
+ uint8_t mode, proto;
IPSEC_ASSERT(so != NULL, ("null socket"));
IPSEC_ASSERT(m != NULL, ("null mbuf"));
IPSEC_ASSERT(mhp != NULL, ("null msghdr"));
IPSEC_ASSERT(mhp->msg != NULL, ("null msg"));
- if (mhp->ext[SADB_EXT_ADDRESS_SRC] == NULL ||
- mhp->ext[SADB_EXT_ADDRESS_DST] == NULL) {
- ipseclog((LOG_DEBUG, "%s: invalid message is passed.\n",
- __func__));
- return key_senderror(so, m, EINVAL);
+ if (SADB_CHECKHDR(mhp, SADB_EXT_ADDRESS_SRC) ||
+ SADB_CHECKHDR(mhp, SADB_EXT_ADDRESS_DST)
+#ifdef PFKEY_STRICT_CHECKS
+ || SADB_CHECKHDR(mhp, SADB_EXT_SPIRANGE)
+#endif
+ ) {
+ ipseclog((LOG_DEBUG,
+ "%s: invalid message: missing required header.\n",
+ __func__));
+ error = EINVAL;
+ goto fail;
}
- if (mhp->extlen[SADB_EXT_ADDRESS_SRC] < sizeof(struct sadb_address) ||
- mhp->extlen[SADB_EXT_ADDRESS_DST] < sizeof(struct sadb_address)) {
- ipseclog((LOG_DEBUG, "%s: invalid message is passed.\n",
- __func__));
- return key_senderror(so, m, EINVAL);
+ if (SADB_CHECKLEN(mhp, SADB_EXT_ADDRESS_SRC) ||
+ SADB_CHECKLEN(mhp, SADB_EXT_ADDRESS_DST)
+#ifdef PFKEY_STRICT_CHECKS
+ || SADB_CHECKLEN(mhp, SADB_EXT_SPIRANGE)
+#endif
+ ) {
+ ipseclog((LOG_DEBUG,
+ "%s: invalid message: wrong header size.\n", __func__));
+ error = EINVAL;
+ goto fail;
}
- if (mhp->ext[SADB_X_EXT_SA2] != NULL) {
- mode = ((struct sadb_x_sa2 *)mhp->ext[SADB_X_EXT_SA2])->sadb_x_sa2_mode;
- reqid = ((struct sadb_x_sa2 *)mhp->ext[SADB_X_EXT_SA2])->sadb_x_sa2_reqid;
- } else {
+ if (SADB_CHECKHDR(mhp, SADB_X_EXT_SA2)) {
mode = IPSEC_MODE_ANY;
reqid = 0;
+ } else {
+ if (SADB_CHECKLEN(mhp, SADB_X_EXT_SA2)) {
+ ipseclog((LOG_DEBUG,
+ "%s: invalid message: wrong header size.\n",
+ __func__));
+ error = EINVAL;
+ goto fail;
+ }
+ mode = ((struct sadb_x_sa2 *)
+ mhp->ext[SADB_X_EXT_SA2])->sadb_x_sa2_mode;
+ reqid = ((struct sadb_x_sa2 *)
+ mhp->ext[SADB_X_EXT_SA2])->sadb_x_sa2_reqid;
}
src0 = (struct sadb_address *)(mhp->ext[SADB_EXT_ADDRESS_SRC]);
@@ -4620,121 +4759,55 @@ key_getspi(struct socket *so, struct mbuf *m, const struct sadb_msghdr *mhp)
if ((proto = key_satype2proto(mhp->msg->sadb_msg_satype)) == 0) {
ipseclog((LOG_DEBUG, "%s: invalid satype is passed.\n",
__func__));
- return key_senderror(so, m, EINVAL);
- }
-
- /*
- * Make sure the port numbers are zero.
- * In case of NAT-T we will update them later if needed.
- */
- switch (((struct sockaddr *)(src0 + 1))->sa_family) {
- case AF_INET:
- if (((struct sockaddr *)(src0 + 1))->sa_len !=
- sizeof(struct sockaddr_in))
- return key_senderror(so, m, EINVAL);
- ((struct sockaddr_in *)(src0 + 1))->sin_port = 0;
- break;
- case AF_INET6:
- if (((struct sockaddr *)(src0 + 1))->sa_len !=
- sizeof(struct sockaddr_in6))
- return key_senderror(so, m, EINVAL);
- ((struct sockaddr_in6 *)(src0 + 1))->sin6_port = 0;
- break;
- default:
- ; /*???*/
+ error = EINVAL;
+ goto fail;
}
- switch (((struct sockaddr *)(dst0 + 1))->sa_family) {
- case AF_INET:
- if (((struct sockaddr *)(dst0 + 1))->sa_len !=
- sizeof(struct sockaddr_in))
- return key_senderror(so, m, EINVAL);
- ((struct sockaddr_in *)(dst0 + 1))->sin_port = 0;
- break;
- case AF_INET6:
- if (((struct sockaddr *)(dst0 + 1))->sa_len !=
- sizeof(struct sockaddr_in6))
- return key_senderror(so, m, EINVAL);
- ((struct sockaddr_in6 *)(dst0 + 1))->sin6_port = 0;
- break;
- default:
- ; /*???*/
+ error = key_checksockaddrs((struct sockaddr *)(src0 + 1),
+ (struct sockaddr *)(dst0 + 1));
+ if (error != 0) {
+ ipseclog((LOG_DEBUG, "%s: invalid sockaddr.\n", __func__));
+ error = EINVAL;
+ goto fail;
}
-
- /* XXX boundary check against sa_len */
KEY_SETSECASIDX(proto, mode, reqid, src0 + 1, dst0 + 1, &saidx);
-#ifdef IPSEC_NAT_T
- /*
- * Handle NAT-T info if present.
- * We made sure the port numbers are zero above, so we do
- * not have to worry in case we do not update them.
- */
- if (mhp->ext[SADB_X_EXT_NAT_T_OAI] != NULL)
- ipseclog((LOG_DEBUG, "%s: NAT-T OAi present\n", __func__));
- if (mhp->ext[SADB_X_EXT_NAT_T_OAR] != NULL)
- ipseclog((LOG_DEBUG, "%s: NAT-T OAr present\n", __func__));
-
- if (mhp->ext[SADB_X_EXT_NAT_T_TYPE] != NULL &&
- mhp->ext[SADB_X_EXT_NAT_T_SPORT] != NULL &&
- mhp->ext[SADB_X_EXT_NAT_T_DPORT] != NULL) {
- struct sadb_x_nat_t_type *type;
- struct sadb_x_nat_t_port *sport, *dport;
-
- if (mhp->extlen[SADB_X_EXT_NAT_T_TYPE] < sizeof(*type) ||
- mhp->extlen[SADB_X_EXT_NAT_T_SPORT] < sizeof(*sport) ||
- mhp->extlen[SADB_X_EXT_NAT_T_DPORT] < sizeof(*dport)) {
- ipseclog((LOG_DEBUG, "%s: invalid nat-t message "
- "passed.\n", __func__));
- return key_senderror(so, m, EINVAL);
- }
-
- sport = (struct sadb_x_nat_t_port *)
- mhp->ext[SADB_X_EXT_NAT_T_SPORT];
- dport = (struct sadb_x_nat_t_port *)
- mhp->ext[SADB_X_EXT_NAT_T_DPORT];
-
- if (sport)
- KEY_PORTTOSADDR(&saidx.src, sport->sadb_x_nat_t_port_port);
- if (dport)
- KEY_PORTTOSADDR(&saidx.dst, dport->sadb_x_nat_t_port_port);
- }
-#endif
-
/* SPI allocation */
- spi = key_do_getnewspi((struct sadb_spirange *)mhp->ext[SADB_EXT_SPIRANGE],
- &saidx);
- if (spi == 0)
- return key_senderror(so, m, EINVAL);
-
- /* get a SA index */
- if ((newsah = key_getsah(&saidx)) == NULL) {
- /* create a new SA index */
- if ((newsah = key_newsah(&saidx)) == NULL) {
- ipseclog((LOG_DEBUG, "%s: No more memory.\n",__func__));
- return key_senderror(so, m, ENOBUFS);
- }
+ spi = key_do_getnewspi(
+ (struct sadb_spirange *)mhp->ext[SADB_EXT_SPIRANGE], &saidx);
+ if (spi == 0) {
+ /*
+ * Requested SPI or SPI range is not available or
+ * already used.
+ */
+ error = EEXIST;
+ goto fail;
}
+ sav = key_newsav(mhp, &saidx, spi, &error);
+ if (sav == NULL)
+ goto fail;
- /* get a new SA */
- /* XXX rewrite */
- newsav = KEY_NEWSAV(m, mhp, newsah, &error);
- if (newsav == NULL) {
- /* XXX don't free new SA index allocated in above. */
- return key_senderror(so, m, error);
+ if (sav->seq != 0) {
+ /*
+ * RFC2367:
+ * If the SADB_GETSPI message is in response to a
+ * kernel-generated SADB_ACQUIRE, the sadb_msg_seq
+ * MUST be the same as the SADB_ACQUIRE message.
+ *
+ * XXXAE: However it doesn't definethe behaviour how to
+ * check this and what to do if it doesn't match.
+ * Also what we should do if it matches?
+ *
+ * We can compare saidx used in SADB_ACQUIRE with saidx
+ * used in SADB_GETSPI, but this probably can break
+ * existing software. For now just warn if it doesn't match.
+ *
+ * XXXAE: anyway it looks useless.
+ */
+ key_acqdone(&saidx, sav->seq);
}
-
- /* set spi */
- newsav->spi = htonl(spi);
-
- /* delete the entry in acqtree */
- if (mhp->msg->sadb_msg_seq != 0) {
- struct secacq *acq;
- if ((acq = key_getacqbyseq(mhp->msg->sadb_msg_seq)) != NULL) {
- /* reset counter in order to deletion by timehandler. */
- acq->created = time_second;
- acq->count = 0;
- }
- }
+ KEYDBG(KEY_STAMP,
+ printf("%s: SA(%p)\n", __func__, sav));
+ KEYDBG(KEY_DATA, kdebug_secasv(sav));
{
struct mbuf *n, *nn;
@@ -4753,8 +4826,10 @@ key_getspi(struct socket *so, struct mbuf *m, const struct sadb_msghdr *mhp)
n = NULL;
}
}
- if (!n)
- return key_senderror(so, m, ENOBUFS);
+ if (!n) {
+ error = ENOBUFS;
+ goto fail;
+ }
n->m_len = len;
n->m_next = NULL;
@@ -4766,7 +4841,7 @@ key_getspi(struct socket *so, struct mbuf *m, const struct sadb_msghdr *mhp)
m_sa = (struct sadb_sa *)(mtod(n, caddr_t) + off);
m_sa->sadb_sa_len = PFKEY_UNIT64(sizeof(struct sadb_sa));
m_sa->sadb_sa_exttype = SADB_EXT_SA;
- m_sa->sadb_sa_spi = htonl(spi);
+ m_sa->sadb_sa_spi = spi; /* SPI is already in network byte order */
off += PFKEY_ALIGN8(sizeof(struct sadb_sa));
IPSEC_ASSERT(off == len,
@@ -4776,7 +4851,8 @@ key_getspi(struct socket *so, struct mbuf *m, const struct sadb_msghdr *mhp)
SADB_EXT_ADDRESS_DST);
if (!n->m_next) {
m_freem(n);
- return key_senderror(so, m, ENOBUFS);
+ error = ENOBUFS;
+ goto fail;
}
if (n->m_len < sizeof(struct sadb_msg)) {
@@ -4790,13 +4866,16 @@ key_getspi(struct socket *so, struct mbuf *m, const struct sadb_msghdr *mhp)
n->m_pkthdr.len += nn->m_len;
newmsg = mtod(n, struct sadb_msg *);
- newmsg->sadb_msg_seq = newsav->seq;
+ newmsg->sadb_msg_seq = sav->seq;
newmsg->sadb_msg_errno = 0;
newmsg->sadb_msg_len = PFKEY_UNIT64(n->m_pkthdr.len);
m_freem(m);
return key_sendup_mbuf(so, n, KEY_SENDUP_ONE);
}
+
+fail:
+ return (key_senderror(so, m, error));
}
/*
@@ -4804,13 +4883,12 @@ key_getspi(struct socket *so, struct mbuf *m, const struct sadb_msghdr *mhp)
* called by key_getspi().
* OUT:
* 0: failure.
- * others: success.
+ * others: success, SPI in network byte order.
*/
-static u_int32_t
+static uint32_t
key_do_getnewspi(struct sadb_spirange *spirange, struct secasindex *saidx)
{
- u_int32_t newspi;
- u_int32_t min, max;
+ uint32_t min, max, newspi, t;
int count = V_key_spi_trycnt;
/* set spi range to allocate */
@@ -4823,7 +4901,6 @@ key_do_getnewspi(struct sadb_spirange *spirange, struct secasindex *saidx)
}
/* IPCOMP needs 2-byte SPI */
if (saidx->proto == IPPROTO_IPCOMP) {
- u_int32_t t;
if (min >= 0x10000)
min = 0xffff;
if (max >= 0x10000)
@@ -4834,15 +4911,14 @@ key_do_getnewspi(struct sadb_spirange *spirange, struct secasindex *saidx)
}
if (min == max) {
- if (key_checkspidup(saidx, min) != NULL) {
+ if (!key_checkspidup(htonl(min))) {
ipseclog((LOG_DEBUG, "%s: SPI %u exists already.\n",
- __func__, min));
+ __func__, min));
return 0;
}
count--; /* taking one cost. */
newspi = min;
-
} else {
/* init SPI */
@@ -4852,23 +4928,244 @@ key_do_getnewspi(struct sadb_spirange *spirange, struct secasindex *saidx)
while (count--) {
/* generate pseudo-random SPI value ranged. */
newspi = min + (key_random() % (max - min + 1));
-
- if (key_checkspidup(saidx, newspi) == NULL)
+ if (!key_checkspidup(htonl(newspi)))
break;
}
if (count == 0 || newspi == 0) {
- ipseclog((LOG_DEBUG, "%s: to allocate spi is failed.\n",
- __func__));
+ ipseclog((LOG_DEBUG,
+ "%s: failed to allocate SPI.\n", __func__));
return 0;
}
}
/* statistics */
keystat.getspi_count =
- (keystat.getspi_count + V_key_spi_trycnt - count) / 2;
+ (keystat.getspi_count + V_key_spi_trycnt - count) / 2;
- return newspi;
+ return (htonl(newspi));
+}
+
+/*
+ * Find TCP-MD5 SA with corresponding secasindex.
+ * If not found, return NULL and fill SPI with usable value if needed.
+ */
+static struct secasvar *
+key_getsav_tcpmd5(struct secasindex *saidx, uint32_t *spi)
+{
+ SAHTREE_RLOCK_TRACKER;
+ struct secashead *sah;
+ struct secasvar *sav;
+
+ IPSEC_ASSERT(saidx->proto == IPPROTO_TCP, ("wrong proto"));
+ SAHTREE_RLOCK();
+ LIST_FOREACH(sah, SAHADDRHASH_HASH(saidx), addrhash) {
+ if (sah->saidx.proto != IPPROTO_TCP)
+ continue;
+ if (!key_sockaddrcmp(&saidx->dst.sa, &sah->saidx.dst.sa, 0))
+ break;
+ }
+ if (sah != NULL) {
+ if (V_key_preferred_oldsa)
+ sav = TAILQ_LAST(&sah->savtree_alive, secasvar_queue);
+ else
+ sav = TAILQ_FIRST(&sah->savtree_alive);
+ if (sav != NULL) {
+ SAV_ADDREF(sav);
+ SAHTREE_RUNLOCK();
+ return (sav);
+ }
+ }
+ if (spi == NULL) {
+ /* No SPI required */
+ SAHTREE_RUNLOCK();
+ return (NULL);
+ }
+ /* Check that SPI is unique */
+ LIST_FOREACH(sav, SAVHASH_HASH(*spi), spihash) {
+ if (sav->spi == *spi)
+ break;
+ }
+ if (sav == NULL) {
+ SAHTREE_RUNLOCK();
+ /* SPI is already unique */
+ return (NULL);
+ }
+ SAHTREE_RUNLOCK();
+ /* XXX: not optimal */
+ *spi = key_do_getnewspi(NULL, saidx);
+ return (NULL);
+}
+
+static int
+key_updateaddresses(struct socket *so, struct mbuf *m,
+ const struct sadb_msghdr *mhp, struct secasvar *sav,
+ struct secasindex *saidx)
+{
+ struct sockaddr *newaddr;
+ struct secashead *sah;
+ struct secasvar *newsav, *tmp;
+ struct mbuf *n;
+ int error, isnew;
+
+ /* Check that we need to change SAH */
+ if (!SADB_CHECKHDR(mhp, SADB_X_EXT_NEW_ADDRESS_SRC)) {
+ newaddr = (struct sockaddr *)(
+ ((struct sadb_address *)
+ mhp->ext[SADB_X_EXT_NEW_ADDRESS_SRC]) + 1);
+ bcopy(newaddr, &saidx->src, newaddr->sa_len);
+ key_porttosaddr(&saidx->src.sa, 0);
+ }
+ if (!SADB_CHECKHDR(mhp, SADB_X_EXT_NEW_ADDRESS_DST)) {
+ newaddr = (struct sockaddr *)(
+ ((struct sadb_address *)
+ mhp->ext[SADB_X_EXT_NEW_ADDRESS_DST]) + 1);
+ bcopy(newaddr, &saidx->dst, newaddr->sa_len);
+ key_porttosaddr(&saidx->dst.sa, 0);
+ }
+ if (!SADB_CHECKHDR(mhp, SADB_X_EXT_NEW_ADDRESS_SRC) ||
+ !SADB_CHECKHDR(mhp, SADB_X_EXT_NEW_ADDRESS_DST)) {
+ error = key_checksockaddrs(&saidx->src.sa, &saidx->dst.sa);
+ if (error != 0) {
+ ipseclog((LOG_DEBUG, "%s: invalid new sockaddr.\n",
+ __func__));
+ return (error);
+ }
+
+ sah = key_getsah(saidx);
+ if (sah == NULL) {
+ /* create a new SA index */
+ sah = key_newsah(saidx);
+ if (sah == NULL) {
+ ipseclog((LOG_DEBUG,
+ "%s: No more memory.\n", __func__));
+ return (ENOBUFS);
+ }
+ isnew = 2; /* SAH is new */
+ } else
+ isnew = 1; /* existing SAH is referenced */
+ } else {
+ /*
+ * src and dst addresses are still the same.
+ * Do we want to change NAT-T config?
+ */
+ if (sav->sah->saidx.proto != IPPROTO_ESP ||
+ SADB_CHECKHDR(mhp, SADB_X_EXT_NAT_T_TYPE) ||
+ SADB_CHECKHDR(mhp, SADB_X_EXT_NAT_T_SPORT) ||
+ SADB_CHECKHDR(mhp, SADB_X_EXT_NAT_T_DPORT)) {
+ ipseclog((LOG_DEBUG,
+ "%s: invalid message: missing required header.\n",
+ __func__));
+ return (EINVAL);
+ }
+ /* We hold reference to SA, thus SAH will be referenced too. */
+ sah = sav->sah;
+ isnew = 0;
+ }
+
+ newsav = malloc(sizeof(struct secasvar), M_IPSEC_SA,
+ M_NOWAIT | M_ZERO);
+ if (newsav == NULL) {
+ ipseclog((LOG_DEBUG, "%s: No more memory.\n", __func__));
+ error = ENOBUFS;
+ goto fail;
+ }
+
+ /* Clone SA's content into newsav */
+ SAV_INITREF(newsav);
+ bcopy(sav, newsav, offsetof(struct secasvar, chain));
+ /*
+ * We create new NAT-T config if it is needed.
+ * Old NAT-T config will be freed by key_cleansav() when
+ * last reference to SA will be released.
+ */
+ newsav->natt = NULL;
+ newsav->sah = sah;
+ newsav->state = SADB_SASTATE_MATURE;
+ error = key_setnatt(sav, mhp);
+ if (error != 0)
+ goto fail;
+
+ SAHTREE_WLOCK();
+ /* Check that SA is still alive */
+ if (sav->state == SADB_SASTATE_DEAD) {
+ /* SA was unlinked */
+ SAHTREE_WUNLOCK();
+ error = ESRCH;
+ goto fail;
+ }
+
+ /* Unlink SA from SAH and SPI hash */
+ IPSEC_ASSERT((sav->flags & SADB_X_EXT_F_CLONED) == 0,
+ ("SA is already cloned"));
+ IPSEC_ASSERT(sav->state == SADB_SASTATE_MATURE ||
+ sav->state == SADB_SASTATE_DYING,
+ ("Wrong SA state %u\n", sav->state));
+ TAILQ_REMOVE(&sav->sah->savtree_alive, sav, chain);
+ LIST_REMOVE(sav, spihash);
+ sav->state = SADB_SASTATE_DEAD;
+
+ /*
+ * Link new SA with SAH. Keep SAs ordered by
+ * create time (newer are first).
+ */
+ TAILQ_FOREACH(tmp, &sah->savtree_alive, chain) {
+ if (newsav->created > tmp->created) {
+ TAILQ_INSERT_BEFORE(tmp, newsav, chain);
+ break;
+ }
+ }
+ if (tmp == NULL)
+ TAILQ_INSERT_TAIL(&sah->savtree_alive, newsav, chain);
+
+ /* Add new SA into SPI hash. */
+ LIST_INSERT_HEAD(SAVHASH_HASH(newsav->spi), newsav, spihash);
+
+ /* Add new SAH into SADB. */
+ if (isnew == 2) {
+ TAILQ_INSERT_HEAD(&V_sahtree, sah, chain);
+ LIST_INSERT_HEAD(SAHADDRHASH_HASH(saidx), sah, addrhash);
+ sah->state = SADB_SASTATE_MATURE;
+ SAH_ADDREF(sah); /* newsav references new SAH */
+ }
+ /*
+ * isnew == 1 -> @sah was referenced by key_getsah().
+ * isnew == 0 -> we use the same @sah, that was used by @sav,
+ * and we use its reference for @newsav.
+ */
+ SECASVAR_LOCK(sav);
+ /* XXX: replace cntr with pointer? */
+ newsav->cntr = sav->cntr;
+ sav->flags |= SADB_X_EXT_F_CLONED;
+ SECASVAR_UNLOCK(sav);
+
+ SAHTREE_WUNLOCK();
+
+ KEYDBG(KEY_STAMP,
+ printf("%s: SA(%p) cloned into SA(%p)\n",
+ __func__, sav, newsav));
+ KEYDBG(KEY_DATA, kdebug_secasv(newsav));
+
+ key_freesav(&sav); /* release last reference */
+
+ /* set msg buf from mhp */
+ n = key_getmsgbuf_x1(m, mhp);
+ if (n == NULL) {
+ ipseclog((LOG_DEBUG, "%s: No more memory.\n", __func__));
+ return (ENOBUFS);
+ }
+ m_freem(m);
+ key_sendup_mbuf(so, n, KEY_SENDUP_ALL);
+ return (0);
+fail:
+ if (isnew != 0)
+ key_freesah(&sah);
+ if (newsav != NULL) {
+ if (newsav->natt != NULL)
+ free(newsav->natt, M_IPSEC_MISC);
+ free(newsav, M_IPSEC_SA);
+ }
+ return (error);
}
/*
@@ -4887,21 +5184,13 @@ key_do_getnewspi(struct sadb_spirange *spirange, struct secasindex *saidx)
static int
key_update(struct socket *so, struct mbuf *m, const struct sadb_msghdr *mhp)
{
- struct sadb_sa *sa0;
- struct sadb_address *src0, *dst0;
-#ifdef IPSEC_NAT_T
- struct sadb_x_nat_t_type *type;
- struct sadb_x_nat_t_port *sport, *dport;
- struct sadb_address *iaddr, *raddr;
- struct sadb_x_nat_t_frag *frag;
-#endif
struct secasindex saidx;
- struct secashead *sah;
+ struct sadb_address *src0, *dst0;
+ struct sadb_sa *sa0;
struct secasvar *sav;
- u_int16_t proto;
- u_int8_t mode;
- u_int32_t reqid;
+ uint32_t reqid;
int error;
+ uint8_t mode, proto;
IPSEC_ASSERT(so != NULL, ("null socket"));
IPSEC_ASSERT(m != NULL, ("null mbuf"));
@@ -4911,199 +5200,182 @@ key_update(struct socket *so, struct mbuf *m, const struct sadb_msghdr *mhp)
/* map satype to proto */
if ((proto = key_satype2proto(mhp->msg->sadb_msg_satype)) == 0) {
ipseclog((LOG_DEBUG, "%s: invalid satype is passed.\n",
- __func__));
+ __func__));
return key_senderror(so, m, EINVAL);
}
- if (mhp->ext[SADB_EXT_SA] == NULL ||
- mhp->ext[SADB_EXT_ADDRESS_SRC] == NULL ||
- mhp->ext[SADB_EXT_ADDRESS_DST] == NULL ||
- (mhp->msg->sadb_msg_satype == SADB_SATYPE_ESP &&
- mhp->ext[SADB_EXT_KEY_ENCRYPT] == NULL) ||
- (mhp->msg->sadb_msg_satype == SADB_SATYPE_AH &&
- mhp->ext[SADB_EXT_KEY_AUTH] == NULL) ||
- (mhp->ext[SADB_EXT_LIFETIME_HARD] != NULL &&
- mhp->ext[SADB_EXT_LIFETIME_SOFT] == NULL) ||
- (mhp->ext[SADB_EXT_LIFETIME_HARD] == NULL &&
- mhp->ext[SADB_EXT_LIFETIME_SOFT] != NULL)) {
- ipseclog((LOG_DEBUG, "%s: invalid message is passed.\n",
- __func__));
+ if (SADB_CHECKHDR(mhp, SADB_EXT_SA) ||
+ SADB_CHECKHDR(mhp, SADB_EXT_ADDRESS_SRC) ||
+ SADB_CHECKHDR(mhp, SADB_EXT_ADDRESS_DST) ||
+ (SADB_CHECKHDR(mhp, SADB_EXT_LIFETIME_HARD) &&
+ !SADB_CHECKHDR(mhp, SADB_EXT_LIFETIME_SOFT)) ||
+ (SADB_CHECKHDR(mhp, SADB_EXT_LIFETIME_SOFT) &&
+ !SADB_CHECKHDR(mhp, SADB_EXT_LIFETIME_HARD))) {
+ ipseclog((LOG_DEBUG,
+ "%s: invalid message: missing required header.\n",
+ __func__));
return key_senderror(so, m, EINVAL);
}
- if (mhp->extlen[SADB_EXT_SA] < sizeof(struct sadb_sa) ||
- mhp->extlen[SADB_EXT_ADDRESS_SRC] < sizeof(struct sadb_address) ||
- mhp->extlen[SADB_EXT_ADDRESS_DST] < sizeof(struct sadb_address)) {
- ipseclog((LOG_DEBUG, "%s: invalid message is passed.\n",
- __func__));
+ if (SADB_CHECKLEN(mhp, SADB_EXT_SA) ||
+ SADB_CHECKLEN(mhp, SADB_EXT_ADDRESS_SRC) ||
+ SADB_CHECKLEN(mhp, SADB_EXT_ADDRESS_DST)) {
+ ipseclog((LOG_DEBUG,
+ "%s: invalid message: wrong header size.\n", __func__));
return key_senderror(so, m, EINVAL);
}
- if (mhp->ext[SADB_X_EXT_SA2] != NULL) {
- mode = ((struct sadb_x_sa2 *)mhp->ext[SADB_X_EXT_SA2])->sadb_x_sa2_mode;
- reqid = ((struct sadb_x_sa2 *)mhp->ext[SADB_X_EXT_SA2])->sadb_x_sa2_reqid;
- } else {
+ if (SADB_CHECKHDR(mhp, SADB_X_EXT_SA2)) {
mode = IPSEC_MODE_ANY;
reqid = 0;
+ } else {
+ if (SADB_CHECKLEN(mhp, SADB_X_EXT_SA2)) {
+ ipseclog((LOG_DEBUG,
+ "%s: invalid message: wrong header size.\n",
+ __func__));
+ return key_senderror(so, m, EINVAL);
+ }
+ mode = ((struct sadb_x_sa2 *)
+ mhp->ext[SADB_X_EXT_SA2])->sadb_x_sa2_mode;
+ reqid = ((struct sadb_x_sa2 *)
+ mhp->ext[SADB_X_EXT_SA2])->sadb_x_sa2_reqid;
}
- /* XXX boundary checking for other extensions */
sa0 = (struct sadb_sa *)mhp->ext[SADB_EXT_SA];
src0 = (struct sadb_address *)(mhp->ext[SADB_EXT_ADDRESS_SRC]);
dst0 = (struct sadb_address *)(mhp->ext[SADB_EXT_ADDRESS_DST]);
- /* XXX boundary check against sa_len */
- KEY_SETSECASIDX(proto, mode, reqid, src0 + 1, dst0 + 1, &saidx);
-
- /*
- * Make sure the port numbers are zero.
- * In case of NAT-T we will update them later if needed.
- */
- KEY_PORTTOSADDR(&saidx.src, 0);
- KEY_PORTTOSADDR(&saidx.dst, 0);
-
-#ifdef IPSEC_NAT_T
/*
- * Handle NAT-T info if present.
+ * Only SADB_SASTATE_MATURE SAs may be submitted in an
+ * SADB_UPDATE message.
*/
- if (mhp->ext[SADB_X_EXT_NAT_T_TYPE] != NULL &&
- mhp->ext[SADB_X_EXT_NAT_T_SPORT] != NULL &&
- mhp->ext[SADB_X_EXT_NAT_T_DPORT] != NULL) {
-
- if (mhp->extlen[SADB_X_EXT_NAT_T_TYPE] < sizeof(*type) ||
- mhp->extlen[SADB_X_EXT_NAT_T_SPORT] < sizeof(*sport) ||
- mhp->extlen[SADB_X_EXT_NAT_T_DPORT] < sizeof(*dport)) {
- ipseclog((LOG_DEBUG, "%s: invalid message.\n",
- __func__));
- return key_senderror(so, m, EINVAL);
- }
-
- type = (struct sadb_x_nat_t_type *)
- mhp->ext[SADB_X_EXT_NAT_T_TYPE];
- sport = (struct sadb_x_nat_t_port *)
- mhp->ext[SADB_X_EXT_NAT_T_SPORT];
- dport = (struct sadb_x_nat_t_port *)
- mhp->ext[SADB_X_EXT_NAT_T_DPORT];
- } else {
- type = NULL;
- sport = dport = NULL;
- }
- if (mhp->ext[SADB_X_EXT_NAT_T_OAI] != NULL &&
- mhp->ext[SADB_X_EXT_NAT_T_OAR] != NULL) {
- if (mhp->extlen[SADB_X_EXT_NAT_T_OAI] < sizeof(*iaddr) ||
- mhp->extlen[SADB_X_EXT_NAT_T_OAR] < sizeof(*raddr)) {
- ipseclog((LOG_DEBUG, "%s: invalid message\n",
- __func__));
- return key_senderror(so, m, EINVAL);
- }
- iaddr = (struct sadb_address *)mhp->ext[SADB_X_EXT_NAT_T_OAI];
- raddr = (struct sadb_address *)mhp->ext[SADB_X_EXT_NAT_T_OAR];
- ipseclog((LOG_DEBUG, "%s: NAT-T OAi/r present\n", __func__));
- } else {
- iaddr = raddr = NULL;
- }
- if (mhp->ext[SADB_X_EXT_NAT_T_FRAG] != NULL) {
- if (mhp->extlen[SADB_X_EXT_NAT_T_FRAG] < sizeof(*frag)) {
- ipseclog((LOG_DEBUG, "%s: invalid message\n",
- __func__));
- return key_senderror(so, m, EINVAL);
- }
- frag = (struct sadb_x_nat_t_frag *)
- mhp->ext[SADB_X_EXT_NAT_T_FRAG];
- } else {
- frag = NULL;
- }
+ if (sa0->sadb_sa_state != SADB_SASTATE_MATURE) {
+ ipseclog((LOG_DEBUG, "%s: invalid state.\n", __func__));
+#ifdef PFKEY_STRICT_CHECKS
+ return key_senderror(so, m, EINVAL);
#endif
-
- /* get a SA header */
- if ((sah = key_getsah(&saidx)) == NULL) {
- ipseclog((LOG_DEBUG, "%s: no SA index found.\n", __func__));
- return key_senderror(so, m, ENOENT);
}
-
- /* set spidx if there */
- /* XXX rewrite */
- error = key_setident(sah, m, mhp);
- if (error)
+ error = key_checksockaddrs((struct sockaddr *)(src0 + 1),
+ (struct sockaddr *)(dst0 + 1));
+ if (error != 0) {
+ ipseclog((LOG_DEBUG, "%s: invalid sockaddr.\n", __func__));
return key_senderror(so, m, error);
-
- /* find a SA with sequence number. */
-#ifdef IPSEC_DOSEQCHECK
- if (mhp->msg->sadb_msg_seq != 0
- && (sav = key_getsavbyseq(sah, mhp->msg->sadb_msg_seq)) == NULL) {
- ipseclog((LOG_DEBUG, "%s: no larval SA with sequence %u "
- "exists.\n", __func__, mhp->msg->sadb_msg_seq));
- return key_senderror(so, m, ENOENT);
}
-#else
- SAHTREE_LOCK();
- sav = key_getsavbyspi(sah, sa0->sadb_sa_spi);
- SAHTREE_UNLOCK();
+ KEY_SETSECASIDX(proto, mode, reqid, src0 + 1, dst0 + 1, &saidx);
+ sav = key_getsavbyspi(sa0->sadb_sa_spi);
if (sav == NULL) {
- ipseclog((LOG_DEBUG, "%s: no such a SA found (spi:%u)\n",
- __func__, (u_int32_t)ntohl(sa0->sadb_sa_spi)));
- return key_senderror(so, m, EINVAL);
- }
-#endif
-
- /* validity check */
- if (sav->sah->saidx.proto != proto) {
- ipseclog((LOG_DEBUG, "%s: protocol mismatched "
- "(DB=%u param=%u)\n", __func__,
- sav->sah->saidx.proto, proto));
+ ipseclog((LOG_DEBUG, "%s: no SA found for SPI %u\n",
+ __func__, ntohl(sa0->sadb_sa_spi)));
return key_senderror(so, m, EINVAL);
}
-#ifdef IPSEC_DOSEQCHECK
- if (sav->spi != sa0->sadb_sa_spi) {
- ipseclog((LOG_DEBUG, "%s: SPI mismatched (DB:%u param:%u)\n",
- __func__,
- (u_int32_t)ntohl(sav->spi),
- (u_int32_t)ntohl(sa0->sadb_sa_spi)));
- return key_senderror(so, m, EINVAL);
- }
-#endif
+ /*
+ * Check that SADB_UPDATE issued by the same process that did
+ * SADB_GETSPI or SADB_ADD.
+ */
if (sav->pid != mhp->msg->sadb_msg_pid) {
- ipseclog((LOG_DEBUG, "%s: pid mismatched (DB:%u param:%u)\n",
- __func__, sav->pid, mhp->msg->sadb_msg_pid));
+ ipseclog((LOG_DEBUG,
+ "%s: pid mismatched (SPI %u, pid %u vs. %u)\n", __func__,
+ ntohl(sav->spi), sav->pid, mhp->msg->sadb_msg_pid));
+ key_freesav(&sav);
return key_senderror(so, m, EINVAL);
}
-
- /* copy sav values */
- error = key_setsaval(sav, m, mhp);
- if (error) {
- KEY_FREESAV(&sav);
- return key_senderror(so, m, error);
- }
-
-#ifdef IPSEC_NAT_T
- /*
- * Handle more NAT-T info if present,
- * now that we have a sav to fill.
- */
- if (type)
- sav->natt_type = type->sadb_x_nat_t_type_type;
-
- if (sport)
- KEY_PORTTOSADDR(&sav->sah->saidx.src,
- sport->sadb_x_nat_t_port_port);
- if (dport)
- KEY_PORTTOSADDR(&sav->sah->saidx.dst,
- dport->sadb_x_nat_t_port_port);
-
-#if 0
- /*
- * In case SADB_X_EXT_NAT_T_FRAG was not given, leave it at 0.
- * We should actually check for a minimum MTU here, if we
- * want to support it in ip_output.
- */
- if (frag)
- sav->natt_esp_frag_len = frag->sadb_x_nat_t_frag_fraglen;
-#endif
-#endif
-
- /* check SA values to be mature. */
- if ((mhp->msg->sadb_msg_errno = key_mature(sav)) != 0) {
- KEY_FREESAV(&sav);
- return key_senderror(so, m, 0);
+ /* saidx should match with SA. */
+ if (key_cmpsaidx(&sav->sah->saidx, &saidx, CMP_MODE_REQID) == 0) {
+ ipseclog((LOG_DEBUG, "%s: saidx mismatched for SPI %u",
+ __func__, ntohl(sav->spi)));
+ key_freesav(&sav);
+ return key_senderror(so, m, ESRCH);
+ }
+
+ if (sav->state == SADB_SASTATE_LARVAL) {
+ if ((mhp->msg->sadb_msg_satype == SADB_SATYPE_ESP &&
+ SADB_CHECKHDR(mhp, SADB_EXT_KEY_ENCRYPT)) ||
+ (mhp->msg->sadb_msg_satype == SADB_SATYPE_AH &&
+ SADB_CHECKHDR(mhp, SADB_EXT_KEY_AUTH))) {
+ ipseclog((LOG_DEBUG,
+ "%s: invalid message: missing required header.\n",
+ __func__));
+ key_freesav(&sav);
+ return key_senderror(so, m, EINVAL);
+ }
+ /*
+ * We can set any values except src, dst and SPI.
+ */
+ error = key_setsaval(sav, mhp);
+ if (error != 0) {
+ key_freesav(&sav);
+ return (key_senderror(so, m, error));
+ }
+ /* Change SA state to MATURE */
+ SAHTREE_WLOCK();
+ if (sav->state != SADB_SASTATE_LARVAL) {
+ /* SA was deleted or another thread made it MATURE. */
+ SAHTREE_WUNLOCK();
+ key_freesav(&sav);
+ return (key_senderror(so, m, ESRCH));
+ }
+ /*
+ * NOTE: we keep SAs in savtree_alive ordered by created
+ * time. When SA's state changed from LARVAL to MATURE,
+ * we update its created time in key_setsaval() and move
+ * it into head of savtree_alive.
+ */
+ TAILQ_REMOVE(&sav->sah->savtree_larval, sav, chain);
+ TAILQ_INSERT_HEAD(&sav->sah->savtree_alive, sav, chain);
+ sav->state = SADB_SASTATE_MATURE;
+ SAHTREE_WUNLOCK();
+ } else {
+ /*
+ * For DYING and MATURE SA we can change only state
+ * and lifetimes. Report EINVAL if something else attempted
+ * to change.
+ */
+ if (!SADB_CHECKHDR(mhp, SADB_EXT_KEY_ENCRYPT) ||
+ !SADB_CHECKHDR(mhp, SADB_EXT_KEY_AUTH)) {
+ key_freesav(&sav);
+ return (key_senderror(so, m, EINVAL));
+ }
+ error = key_updatelifetimes(sav, mhp);
+ if (error != 0) {
+ key_freesav(&sav);
+ return (key_senderror(so, m, error));
+ }
+ /*
+ * This is FreeBSD extension to RFC2367.
+ * IKEd can specify SADB_X_EXT_NEW_ADDRESS_SRC and/or
+ * SADB_X_EXT_NEW_ADDRESS_DST when it wants to change
+ * SA addresses (for example to implement MOBIKE protocol
+ * as described in RFC4555). Also we allow to change
+ * NAT-T config.
+ */
+ if (!SADB_CHECKHDR(mhp, SADB_X_EXT_NEW_ADDRESS_SRC) ||
+ !SADB_CHECKHDR(mhp, SADB_X_EXT_NEW_ADDRESS_DST) ||
+ !SADB_CHECKHDR(mhp, SADB_X_EXT_NAT_T_TYPE) ||
+ sav->natt != NULL) {
+ error = key_updateaddresses(so, m, mhp, sav, &saidx);
+ key_freesav(&sav);
+ if (error != 0)
+ return (key_senderror(so, m, error));
+ return (0);
+ }
+ /* Check that SA is still alive */
+ SAHTREE_WLOCK();
+ if (sav->state == SADB_SASTATE_DEAD) {
+ /* SA was unlinked */
+ SAHTREE_WUNLOCK();
+ key_freesav(&sav);
+ return (key_senderror(so, m, ESRCH));
+ }
+ /*
+ * NOTE: there is possible state moving from DYING to MATURE,
+ * but this doesn't change created time, so we won't reorder
+ * this SA.
+ */
+ sav->state = SADB_SASTATE_MATURE;
+ SAHTREE_WUNLOCK();
}
+ KEYDBG(KEY_STAMP,
+ printf("%s: SA(%p)\n", __func__, sav));
+ KEYDBG(KEY_DATA, kdebug_secasv(sav));
+ key_freesav(&sav);
{
struct mbuf *n;
@@ -5121,40 +5393,6 @@ key_update(struct socket *so, struct mbuf *m, const struct sadb_msghdr *mhp)
}
/*
- * search SAD with sequence for a SA which state is SADB_SASTATE_LARVAL.
- * only called by key_update().
- * OUT:
- * NULL : not found
- * others : found, pointer to a SA.
- */
-#ifdef IPSEC_DOSEQCHECK
-static struct secasvar *
-key_getsavbyseq(struct secashead *sah, u_int32_t seq)
-{
- struct secasvar *sav;
- u_int state;
-
- state = SADB_SASTATE_LARVAL;
-
- /* search SAD with sequence number ? */
- LIST_FOREACH(sav, &sah->savtree[state], chain) {
-
- KEY_CHKSASTATE(state, sav->state, __func__);
-
- if (sav->seq == seq) {
- sa_addref(sav);
- KEYDEBUG(KEYDEBUG_IPSEC_STAMP,
- printf("DP %s cause refcnt++:%d SA:%p\n",
- __func__, sav->refcnt, sav));
- return sav;
- }
- }
-
- return NULL;
-}
-#endif
-
-/*
* SADB_ADD processing
* add an entry to SA database, when received
* <base, SA, (SA2), (lifetime(HSC),) address(SD), (address(P),)
@@ -5172,19 +5410,12 @@ key_getsavbyseq(struct secashead *sah, u_int32_t seq)
static int
key_add(struct socket *so, struct mbuf *m, const struct sadb_msghdr *mhp)
{
- struct sadb_sa *sa0;
- struct sadb_address *src0, *dst0;
-#ifdef IPSEC_NAT_T
- struct sadb_x_nat_t_type *type;
- struct sadb_address *iaddr, *raddr;
- struct sadb_x_nat_t_frag *frag;
-#endif
struct secasindex saidx;
- struct secashead *newsah;
- struct secasvar *newsav;
- u_int16_t proto;
- u_int8_t mode;
- u_int32_t reqid;
+ struct sadb_address *src0, *dst0;
+ struct sadb_sa *sa0;
+ struct secasvar *sav;
+ uint32_t reqid, spi;
+ uint8_t mode, proto;
int error;
IPSEC_ASSERT(so != NULL, ("null socket"));
@@ -5195,176 +5426,115 @@ key_add(struct socket *so, struct mbuf *m, const struct sadb_msghdr *mhp)
/* map satype to proto */
if ((proto = key_satype2proto(mhp->msg->sadb_msg_satype)) == 0) {
ipseclog((LOG_DEBUG, "%s: invalid satype is passed.\n",
- __func__));
+ __func__));
return key_senderror(so, m, EINVAL);
}
- if (mhp->ext[SADB_EXT_SA] == NULL ||
- mhp->ext[SADB_EXT_ADDRESS_SRC] == NULL ||
- mhp->ext[SADB_EXT_ADDRESS_DST] == NULL ||
- (mhp->msg->sadb_msg_satype == SADB_SATYPE_ESP &&
- mhp->ext[SADB_EXT_KEY_ENCRYPT] == NULL) ||
- (mhp->msg->sadb_msg_satype == SADB_SATYPE_AH &&
- mhp->ext[SADB_EXT_KEY_AUTH] == NULL) ||
- (mhp->ext[SADB_EXT_LIFETIME_HARD] != NULL &&
- mhp->ext[SADB_EXT_LIFETIME_SOFT] == NULL) ||
- (mhp->ext[SADB_EXT_LIFETIME_HARD] == NULL &&
- mhp->ext[SADB_EXT_LIFETIME_SOFT] != NULL)) {
- ipseclog((LOG_DEBUG, "%s: invalid message is passed.\n",
- __func__));
+ if (SADB_CHECKHDR(mhp, SADB_EXT_SA) ||
+ SADB_CHECKHDR(mhp, SADB_EXT_ADDRESS_SRC) ||
+ SADB_CHECKHDR(mhp, SADB_EXT_ADDRESS_DST) ||
+ (mhp->msg->sadb_msg_satype == SADB_SATYPE_ESP && (
+ SADB_CHECKHDR(mhp, SADB_EXT_KEY_ENCRYPT) ||
+ SADB_CHECKLEN(mhp, SADB_EXT_KEY_ENCRYPT))) ||
+ (mhp->msg->sadb_msg_satype == SADB_SATYPE_AH && (
+ SADB_CHECKHDR(mhp, SADB_EXT_KEY_AUTH) ||
+ SADB_CHECKLEN(mhp, SADB_EXT_KEY_AUTH))) ||
+ (SADB_CHECKHDR(mhp, SADB_EXT_LIFETIME_HARD) &&
+ !SADB_CHECKHDR(mhp, SADB_EXT_LIFETIME_SOFT)) ||
+ (SADB_CHECKHDR(mhp, SADB_EXT_LIFETIME_SOFT) &&
+ !SADB_CHECKHDR(mhp, SADB_EXT_LIFETIME_HARD))) {
+ ipseclog((LOG_DEBUG,
+ "%s: invalid message: missing required header.\n",
+ __func__));
return key_senderror(so, m, EINVAL);
}
- if (mhp->extlen[SADB_EXT_SA] < sizeof(struct sadb_sa) ||
- mhp->extlen[SADB_EXT_ADDRESS_SRC] < sizeof(struct sadb_address) ||
- mhp->extlen[SADB_EXT_ADDRESS_DST] < sizeof(struct sadb_address)) {
- /* XXX need more */
- ipseclog((LOG_DEBUG, "%s: invalid message is passed.\n",
- __func__));
+ if (SADB_CHECKLEN(mhp, SADB_EXT_SA) ||
+ SADB_CHECKLEN(mhp, SADB_EXT_ADDRESS_SRC) ||
+ SADB_CHECKLEN(mhp, SADB_EXT_ADDRESS_DST)) {
+ ipseclog((LOG_DEBUG,
+ "%s: invalid message: wrong header size.\n", __func__));
return key_senderror(so, m, EINVAL);
}
- if (mhp->ext[SADB_X_EXT_SA2] != NULL) {
- mode = ((struct sadb_x_sa2 *)mhp->ext[SADB_X_EXT_SA2])->sadb_x_sa2_mode;
- reqid = ((struct sadb_x_sa2 *)mhp->ext[SADB_X_EXT_SA2])->sadb_x_sa2_reqid;
- } else {
+ if (SADB_CHECKHDR(mhp, SADB_X_EXT_SA2)) {
mode = IPSEC_MODE_ANY;
reqid = 0;
+ } else {
+ if (SADB_CHECKLEN(mhp, SADB_X_EXT_SA2)) {
+ ipseclog((LOG_DEBUG,
+ "%s: invalid message: wrong header size.\n",
+ __func__));
+ return key_senderror(so, m, EINVAL);
+ }
+ mode = ((struct sadb_x_sa2 *)
+ mhp->ext[SADB_X_EXT_SA2])->sadb_x_sa2_mode;
+ reqid = ((struct sadb_x_sa2 *)
+ mhp->ext[SADB_X_EXT_SA2])->sadb_x_sa2_reqid;
}
sa0 = (struct sadb_sa *)mhp->ext[SADB_EXT_SA];
src0 = (struct sadb_address *)mhp->ext[SADB_EXT_ADDRESS_SRC];
dst0 = (struct sadb_address *)mhp->ext[SADB_EXT_ADDRESS_DST];
- /* XXX boundary check against sa_len */
- KEY_SETSECASIDX(proto, mode, reqid, src0 + 1, dst0 + 1, &saidx);
-
- /*
- * Make sure the port numbers are zero.
- * In case of NAT-T we will update them later if needed.
- */
- KEY_PORTTOSADDR(&saidx.src, 0);
- KEY_PORTTOSADDR(&saidx.dst, 0);
-
-#ifdef IPSEC_NAT_T
/*
- * Handle NAT-T info if present.
+ * Only SADB_SASTATE_MATURE SAs may be submitted in an
+ * SADB_ADD message.
*/
- if (mhp->ext[SADB_X_EXT_NAT_T_TYPE] != NULL &&
- mhp->ext[SADB_X_EXT_NAT_T_SPORT] != NULL &&
- mhp->ext[SADB_X_EXT_NAT_T_DPORT] != NULL) {
- struct sadb_x_nat_t_port *sport, *dport;
-
- if (mhp->extlen[SADB_X_EXT_NAT_T_TYPE] < sizeof(*type) ||
- mhp->extlen[SADB_X_EXT_NAT_T_SPORT] < sizeof(*sport) ||
- mhp->extlen[SADB_X_EXT_NAT_T_DPORT] < sizeof(*dport)) {
- ipseclog((LOG_DEBUG, "%s: invalid message.\n",
- __func__));
- return key_senderror(so, m, EINVAL);
- }
-
- type = (struct sadb_x_nat_t_type *)
- mhp->ext[SADB_X_EXT_NAT_T_TYPE];
- sport = (struct sadb_x_nat_t_port *)
- mhp->ext[SADB_X_EXT_NAT_T_SPORT];
- dport = (struct sadb_x_nat_t_port *)
- mhp->ext[SADB_X_EXT_NAT_T_DPORT];
-
- if (sport)
- KEY_PORTTOSADDR(&saidx.src,
- sport->sadb_x_nat_t_port_port);
- if (dport)
- KEY_PORTTOSADDR(&saidx.dst,
- dport->sadb_x_nat_t_port_port);
- } else {
- type = NULL;
+ if (sa0->sadb_sa_state != SADB_SASTATE_MATURE) {
+ ipseclog((LOG_DEBUG, "%s: invalid state.\n", __func__));
+#ifdef PFKEY_STRICT_CHECKS
+ return key_senderror(so, m, EINVAL);
+#endif
}
- if (mhp->ext[SADB_X_EXT_NAT_T_OAI] != NULL &&
- mhp->ext[SADB_X_EXT_NAT_T_OAR] != NULL) {
- if (mhp->extlen[SADB_X_EXT_NAT_T_OAI] < sizeof(*iaddr) ||
- mhp->extlen[SADB_X_EXT_NAT_T_OAR] < sizeof(*raddr)) {
- ipseclog((LOG_DEBUG, "%s: invalid message\n",
- __func__));
- return key_senderror(so, m, EINVAL);
- }
- iaddr = (struct sadb_address *)mhp->ext[SADB_X_EXT_NAT_T_OAI];
- raddr = (struct sadb_address *)mhp->ext[SADB_X_EXT_NAT_T_OAR];
- ipseclog((LOG_DEBUG, "%s: NAT-T OAi/r present\n", __func__));
- } else {
- iaddr = raddr = NULL;
+ error = key_checksockaddrs((struct sockaddr *)(src0 + 1),
+ (struct sockaddr *)(dst0 + 1));
+ if (error != 0) {
+ ipseclog((LOG_DEBUG, "%s: invalid sockaddr.\n", __func__));
+ return key_senderror(so, m, error);
}
- if (mhp->ext[SADB_X_EXT_NAT_T_FRAG] != NULL) {
- if (mhp->extlen[SADB_X_EXT_NAT_T_FRAG] < sizeof(*frag)) {
- ipseclog((LOG_DEBUG, "%s: invalid message\n",
+ KEY_SETSECASIDX(proto, mode, reqid, src0 + 1, dst0 + 1, &saidx);
+ spi = sa0->sadb_sa_spi;
+ /*
+ * For TCP-MD5 SAs we don't use SPI. Check the uniqueness using
+ * secasindex.
+ * XXXAE: IPComp seems also doesn't use SPI.
+ */
+ if (proto == IPPROTO_TCP) {
+ sav = key_getsav_tcpmd5(&saidx, &spi);
+ if (sav == NULL && spi == 0) {
+ /* Failed to allocate SPI */
+ ipseclog((LOG_DEBUG, "%s: SA already exists.\n",
__func__));
- return key_senderror(so, m, EINVAL);
+ return key_senderror(so, m, EEXIST);
}
- frag = (struct sadb_x_nat_t_frag *)
- mhp->ext[SADB_X_EXT_NAT_T_FRAG];
+ /* XXX: SPI that we report back can have another value */
} else {
- frag = NULL;
- }
-#endif
-
- /* get a SA header */
- if ((newsah = key_getsah(&saidx)) == NULL) {
- /* create a new SA header */
- if ((newsah = key_newsah(&saidx)) == NULL) {
- ipseclog((LOG_DEBUG, "%s: No more memory.\n",__func__));
- return key_senderror(so, m, ENOBUFS);
- }
- }
-
- /* set spidx if there */
- /* XXX rewrite */
- error = key_setident(newsah, m, mhp);
- if (error) {
- return key_senderror(so, m, error);
+ /* We can create new SA only if SPI is different. */
+ sav = key_getsavbyspi(spi);
}
-
- /* create new SA entry. */
- /* We can create new SA only if SPI is differenct. */
- SAHTREE_LOCK();
- newsav = key_getsavbyspi(newsah, sa0->sadb_sa_spi);
- SAHTREE_UNLOCK();
- if (newsav != NULL) {
+ if (sav != NULL) {
+ key_freesav(&sav);
ipseclog((LOG_DEBUG, "%s: SA already exists.\n", __func__));
return key_senderror(so, m, EEXIST);
}
- newsav = KEY_NEWSAV(m, mhp, newsah, &error);
- if (newsav == NULL) {
- return key_senderror(so, m, error);
- }
-
-#ifdef IPSEC_NAT_T
- /*
- * Handle more NAT-T info if present,
- * now that we have a sav to fill.
- */
- if (type)
- newsav->natt_type = type->sadb_x_nat_t_type_type;
-#if 0
- /*
- * In case SADB_X_EXT_NAT_T_FRAG was not given, leave it at 0.
- * We should actually check for a minimum MTU here, if we
- * want to support it in ip_output.
- */
- if (frag)
- newsav->natt_esp_frag_len = frag->sadb_x_nat_t_frag_fraglen;
-#endif
-#endif
-
- /* check SA values to be mature. */
- if ((error = key_mature(newsav)) != 0) {
- KEY_FREESAV(&newsav);
+ sav = key_newsav(mhp, &saidx, spi, &error);
+ if (sav == NULL)
return key_senderror(so, m, error);
- }
-
+ KEYDBG(KEY_STAMP,
+ printf("%s: return SA(%p)\n", __func__, sav));
+ KEYDBG(KEY_DATA, kdebug_secasv(sav));
/*
- * don't call key_freesav() here, as we would like to keep the SA
- * in the database on success.
+ * If SADB_ADD was in response to SADB_ACQUIRE, we need to schedule
+ * ACQ for deletion.
*/
+ if (sav->seq != 0)
+ key_acqdone(&saidx, sav->seq);
{
+ /*
+ * Don't call key_freesav() on error here, as we would like to
+ * keep the SA in the database.
+ */
struct mbuf *n;
/* set msg buf from mhp */
@@ -5379,31 +5549,199 @@ key_add(struct socket *so, struct mbuf *m, const struct sadb_msghdr *mhp)
}
}
-/* m is retained */
+/*
+ * NAT-T support.
+ * IKEd may request the use ESP in UDP encapsulation when it detects the
+ * presence of NAT. It uses NAT-T extension headers for such SAs to specify
+ * parameters needed for encapsulation and decapsulation. These PF_KEY
+ * extension headers are not standardized, so this comment addresses our
+ * implementation.
+ * SADB_X_EXT_NAT_T_TYPE specifies type of encapsulation, we support only
+ * UDP_ENCAP_ESPINUDP as described in RFC3948.
+ * SADB_X_EXT_NAT_T_SPORT/DPORT specifies source and destination ports for
+ * UDP header. We use these ports in UDP encapsulation procedure, also we
+ * can check them in UDP decapsulation procedure.
+ * SADB_X_EXT_NAT_T_OA[IR] specifies original address of initiator or
+ * responder. These addresses can be used for transport mode to adjust
+ * checksum after decapsulation and decryption. Since original IP addresses
+ * used by peer usually different (we detected presence of NAT), TCP/UDP
+ * pseudo header checksum and IP header checksum was calculated using original
+ * addresses. After decapsulation and decryption we need to adjust checksum
+ * to have correct datagram.
+ *
+ * We expect presence of NAT-T extension headers only in SADB_ADD and
+ * SADB_UPDATE messages. We report NAT-T extension headers in replies
+ * to SADB_ADD, SADB_UPDATE, SADB_GET, and SADB_DUMP messages.
+ */
static int
-key_setident(struct secashead *sah, struct mbuf *m,
- const struct sadb_msghdr *mhp)
+key_setnatt(struct secasvar *sav, const struct sadb_msghdr *mhp)
+{
+ struct sadb_x_nat_t_port *port;
+ struct sadb_x_nat_t_type *type;
+ struct sadb_address *oai, *oar;
+ struct sockaddr *sa;
+ uint32_t addr;
+ uint16_t cksum;
+
+ IPSEC_ASSERT(sav->natt == NULL, ("natt is already initialized"));
+ /*
+ * Ignore NAT-T headers if sproto isn't ESP.
+ */
+ if (sav->sah->saidx.proto != IPPROTO_ESP)
+ return (0);
+
+ if (!SADB_CHECKHDR(mhp, SADB_X_EXT_NAT_T_TYPE) &&
+ !SADB_CHECKHDR(mhp, SADB_X_EXT_NAT_T_SPORT) &&
+ !SADB_CHECKHDR(mhp, SADB_X_EXT_NAT_T_DPORT)) {
+ if (SADB_CHECKLEN(mhp, SADB_X_EXT_NAT_T_TYPE) ||
+ SADB_CHECKLEN(mhp, SADB_X_EXT_NAT_T_SPORT) ||
+ SADB_CHECKLEN(mhp, SADB_X_EXT_NAT_T_DPORT)) {
+ ipseclog((LOG_DEBUG,
+ "%s: invalid message: wrong header size.\n",
+ __func__));
+ return (EINVAL);
+ }
+ } else
+ return (0);
+
+ type = (struct sadb_x_nat_t_type *)mhp->ext[SADB_X_EXT_NAT_T_TYPE];
+ if (type->sadb_x_nat_t_type_type != UDP_ENCAP_ESPINUDP) {
+ ipseclog((LOG_DEBUG, "%s: unsupported NAT-T type %u.\n",
+ __func__, type->sadb_x_nat_t_type_type));
+ return (EINVAL);
+ }
+ /*
+ * Allocate storage for NAT-T config.
+ * On error it will be released by key_cleansav().
+ */
+ sav->natt = malloc(sizeof(struct secnatt), M_IPSEC_MISC,
+ M_NOWAIT | M_ZERO);
+ if (sav->natt == NULL) {
+ PFKEYSTAT_INC(in_nomem);
+ ipseclog((LOG_DEBUG, "%s: No more memory.\n", __func__));
+ return (ENOBUFS);
+ }
+ port = (struct sadb_x_nat_t_port *)mhp->ext[SADB_X_EXT_NAT_T_SPORT];
+ if (port->sadb_x_nat_t_port_port == 0) {
+ ipseclog((LOG_DEBUG, "%s: invalid NAT-T sport specified.\n",
+ __func__));
+ return (EINVAL);
+ }
+ sav->natt->sport = port->sadb_x_nat_t_port_port;
+ port = (struct sadb_x_nat_t_port *)mhp->ext[SADB_X_EXT_NAT_T_DPORT];
+ if (port->sadb_x_nat_t_port_port == 0) {
+ ipseclog((LOG_DEBUG, "%s: invalid NAT-T dport specified.\n",
+ __func__));
+ return (EINVAL);
+ }
+ sav->natt->dport = port->sadb_x_nat_t_port_port;
+
+ /*
+ * SADB_X_EXT_NAT_T_OAI and SADB_X_EXT_NAT_T_OAR are optional
+ * and needed only for transport mode IPsec.
+ * Usually NAT translates only one address, but it is possible,
+ * that both addresses could be translated.
+ * NOTE: Value of SADB_X_EXT_NAT_T_OAI is equal to SADB_X_EXT_NAT_T_OA.
+ */
+ if (!SADB_CHECKHDR(mhp, SADB_X_EXT_NAT_T_OAI)) {
+ if (SADB_CHECKLEN(mhp, SADB_X_EXT_NAT_T_OAI)) {
+ ipseclog((LOG_DEBUG,
+ "%s: invalid message: wrong header size.\n",
+ __func__));
+ return (EINVAL);
+ }
+ oai = (struct sadb_address *)mhp->ext[SADB_X_EXT_NAT_T_OAI];
+ } else
+ oai = NULL;
+ if (!SADB_CHECKHDR(mhp, SADB_X_EXT_NAT_T_OAR)) {
+ if (SADB_CHECKLEN(mhp, SADB_X_EXT_NAT_T_OAR)) {
+ ipseclog((LOG_DEBUG,
+ "%s: invalid message: wrong header size.\n",
+ __func__));
+ return (EINVAL);
+ }
+ oar = (struct sadb_address *)mhp->ext[SADB_X_EXT_NAT_T_OAR];
+ } else
+ oar = NULL;
+
+ /* Initialize addresses only for transport mode */
+ if (sav->sah->saidx.mode != IPSEC_MODE_TUNNEL) {
+ cksum = 0;
+ if (oai != NULL) {
+ /* Currently we support only AF_INET */
+ sa = (struct sockaddr *)(oai + 1);
+ if (sa->sa_family != AF_INET ||
+ sa->sa_len != sizeof(struct sockaddr_in)) {
+ ipseclog((LOG_DEBUG,
+ "%s: wrong NAT-OAi header.\n",
+ __func__));
+ return (EINVAL);
+ }
+ /* Ignore address if it the same */
+ if (((struct sockaddr_in *)sa)->sin_addr.s_addr !=
+ sav->sah->saidx.src.sin.sin_addr.s_addr) {
+ bcopy(sa, &sav->natt->oai.sa, sa->sa_len);
+ sav->natt->flags |= IPSEC_NATT_F_OAI;
+ /* Calculate checksum delta */
+ addr = sav->sah->saidx.src.sin.sin_addr.s_addr;
+ cksum = in_addword(cksum, ~addr >> 16);
+ cksum = in_addword(cksum, ~addr & 0xffff);
+ addr = sav->natt->oai.sin.sin_addr.s_addr;
+ cksum = in_addword(cksum, addr >> 16);
+ cksum = in_addword(cksum, addr & 0xffff);
+ }
+ }
+ if (oar != NULL) {
+ /* Currently we support only AF_INET */
+ sa = (struct sockaddr *)(oar + 1);
+ if (sa->sa_family != AF_INET ||
+ sa->sa_len != sizeof(struct sockaddr_in)) {
+ ipseclog((LOG_DEBUG,
+ "%s: wrong NAT-OAr header.\n",
+ __func__));
+ return (EINVAL);
+ }
+ /* Ignore address if it the same */
+ if (((struct sockaddr_in *)sa)->sin_addr.s_addr !=
+ sav->sah->saidx.dst.sin.sin_addr.s_addr) {
+ bcopy(sa, &sav->natt->oar.sa, sa->sa_len);
+ sav->natt->flags |= IPSEC_NATT_F_OAR;
+ /* Calculate checksum delta */
+ addr = sav->sah->saidx.dst.sin.sin_addr.s_addr;
+ cksum = in_addword(cksum, ~addr >> 16);
+ cksum = in_addword(cksum, ~addr & 0xffff);
+ addr = sav->natt->oar.sin.sin_addr.s_addr;
+ cksum = in_addword(cksum, addr >> 16);
+ cksum = in_addword(cksum, addr & 0xffff);
+ }
+ }
+ sav->natt->cksum = cksum;
+ }
+ return (0);
+}
+
+static int
+key_setident(struct secashead *sah, const struct sadb_msghdr *mhp)
{
const struct sadb_ident *idsrc, *iddst;
int idsrclen, iddstlen;
IPSEC_ASSERT(sah != NULL, ("null secashead"));
- IPSEC_ASSERT(m != NULL, ("null mbuf"));
IPSEC_ASSERT(mhp != NULL, ("null msghdr"));
IPSEC_ASSERT(mhp->msg != NULL, ("null msg"));
/* don't make buffer if not there */
- if (mhp->ext[SADB_EXT_IDENTITY_SRC] == NULL &&
- mhp->ext[SADB_EXT_IDENTITY_DST] == NULL) {
+ if (SADB_CHECKHDR(mhp, SADB_EXT_IDENTITY_SRC) &&
+ SADB_CHECKHDR(mhp, SADB_EXT_IDENTITY_DST)) {
sah->idents = NULL;
sah->identd = NULL;
- return 0;
+ return (0);
}
-
- if (mhp->ext[SADB_EXT_IDENTITY_SRC] == NULL ||
- mhp->ext[SADB_EXT_IDENTITY_DST] == NULL) {
+
+ if (SADB_CHECKHDR(mhp, SADB_EXT_IDENTITY_SRC) ||
+ SADB_CHECKHDR(mhp, SADB_EXT_IDENTITY_DST)) {
ipseclog((LOG_DEBUG, "%s: invalid identity.\n", __func__));
- return EINVAL;
+ return (EINVAL);
}
idsrc = (const struct sadb_ident *)mhp->ext[SADB_EXT_IDENTITY_SRC];
@@ -5452,7 +5790,10 @@ key_setident(struct secashead *sah, struct mbuf *m,
/*
* m will not be freed on return.
- * it is caller's responsibility to free the result.
+ * it is caller's responsibility to free the result.
+ *
+ * Called from SADB_ADD and SADB_UPDATE. Reply will contain headers
+ * from the request in defined order.
*/
static struct mbuf *
key_getmsgbuf_x1(struct mbuf *m, const struct sadb_msghdr *mhp)
@@ -5464,11 +5805,15 @@ key_getmsgbuf_x1(struct mbuf *m, const struct sadb_msghdr *mhp)
IPSEC_ASSERT(mhp->msg != NULL, ("null msg"));
/* create new sadb_msg to reply. */
- n = key_gather_mbuf(m, mhp, 1, 9, SADB_EXT_RESERVED,
+ n = key_gather_mbuf(m, mhp, 1, 16, SADB_EXT_RESERVED,
SADB_EXT_SA, SADB_X_EXT_SA2,
SADB_EXT_ADDRESS_SRC, SADB_EXT_ADDRESS_DST,
SADB_EXT_LIFETIME_HARD, SADB_EXT_LIFETIME_SOFT,
- SADB_EXT_IDENTITY_SRC, SADB_EXT_IDENTITY_DST);
+ SADB_EXT_IDENTITY_SRC, SADB_EXT_IDENTITY_DST,
+ SADB_X_EXT_NAT_T_TYPE, SADB_X_EXT_NAT_T_SPORT,
+ SADB_X_EXT_NAT_T_DPORT, SADB_X_EXT_NAT_T_OAI,
+ SADB_X_EXT_NAT_T_OAR, SADB_X_EXT_NEW_ADDRESS_SRC,
+ SADB_X_EXT_NEW_ADDRESS_DST);
if (!n)
return NULL;
@@ -5498,12 +5843,11 @@ key_getmsgbuf_x1(struct mbuf *m, const struct sadb_msghdr *mhp)
static int
key_delete(struct socket *so, struct mbuf *m, const struct sadb_msghdr *mhp)
{
- struct sadb_sa *sa0;
- struct sadb_address *src0, *dst0;
struct secasindex saidx;
- struct secashead *sah;
- struct secasvar *sav = NULL;
- u_int16_t proto;
+ struct sadb_address *src0, *dst0;
+ struct secasvar *sav;
+ struct sadb_sa *sa0;
+ uint8_t proto;
IPSEC_ASSERT(so != NULL, ("null socket"));
IPSEC_ASSERT(m != NULL, ("null mbuf"));
@@ -5513,110 +5857,70 @@ key_delete(struct socket *so, struct mbuf *m, const struct sadb_msghdr *mhp)
/* map satype to proto */
if ((proto = key_satype2proto(mhp->msg->sadb_msg_satype)) == 0) {
ipseclog((LOG_DEBUG, "%s: invalid satype is passed.\n",
- __func__));
+ __func__));
return key_senderror(so, m, EINVAL);
}
- if (mhp->ext[SADB_EXT_ADDRESS_SRC] == NULL ||
- mhp->ext[SADB_EXT_ADDRESS_DST] == NULL) {
+ if (SADB_CHECKHDR(mhp, SADB_EXT_ADDRESS_SRC) ||
+ SADB_CHECKHDR(mhp, SADB_EXT_ADDRESS_DST) ||
+ SADB_CHECKLEN(mhp, SADB_EXT_ADDRESS_SRC) ||
+ SADB_CHECKLEN(mhp, SADB_EXT_ADDRESS_DST)) {
ipseclog((LOG_DEBUG, "%s: invalid message is passed.\n",
- __func__));
+ __func__));
return key_senderror(so, m, EINVAL);
}
- if (mhp->extlen[SADB_EXT_ADDRESS_SRC] < sizeof(struct sadb_address) ||
- mhp->extlen[SADB_EXT_ADDRESS_DST] < sizeof(struct sadb_address)) {
- ipseclog((LOG_DEBUG, "%s: invalid message is passed.\n",
- __func__));
- return key_senderror(so, m, EINVAL);
- }
+ src0 = (struct sadb_address *)(mhp->ext[SADB_EXT_ADDRESS_SRC]);
+ dst0 = (struct sadb_address *)(mhp->ext[SADB_EXT_ADDRESS_DST]);
- if (mhp->ext[SADB_EXT_SA] == NULL) {
+ if (key_checksockaddrs((struct sockaddr *)(src0 + 1),
+ (struct sockaddr *)(dst0 + 1)) != 0) {
+ ipseclog((LOG_DEBUG, "%s: invalid sockaddr.\n", __func__));
+ return (key_senderror(so, m, EINVAL));
+ }
+ KEY_SETSECASIDX(proto, IPSEC_MODE_ANY, 0, src0 + 1, dst0 + 1, &saidx);
+ if (SADB_CHECKHDR(mhp, SADB_EXT_SA)) {
/*
* Caller wants us to delete all non-LARVAL SAs
* that match the src/dst. This is used during
* IKE INITIAL-CONTACT.
+ * XXXAE: this looks like some extension to RFC2367.
*/
ipseclog((LOG_DEBUG, "%s: doing delete all.\n", __func__));
- return key_delete_all(so, m, mhp, proto);
- } else if (mhp->extlen[SADB_EXT_SA] < sizeof(struct sadb_sa)) {
- ipseclog((LOG_DEBUG, "%s: invalid message is passed.\n",
- __func__));
- return key_senderror(so, m, EINVAL);
- }
-
- sa0 = (struct sadb_sa *)mhp->ext[SADB_EXT_SA];
- src0 = (struct sadb_address *)(mhp->ext[SADB_EXT_ADDRESS_SRC]);
- dst0 = (struct sadb_address *)(mhp->ext[SADB_EXT_ADDRESS_DST]);
-
- /* XXX boundary check against sa_len */
- KEY_SETSECASIDX(proto, IPSEC_MODE_ANY, 0, src0 + 1, dst0 + 1, &saidx);
-
- /*
- * Make sure the port numbers are zero.
- * In case of NAT-T we will update them later if needed.
- */
- KEY_PORTTOSADDR(&saidx.src, 0);
- KEY_PORTTOSADDR(&saidx.dst, 0);
-
-#ifdef IPSEC_NAT_T
- /*
- * Handle NAT-T info if present.
- */
- if (mhp->ext[SADB_X_EXT_NAT_T_SPORT] != NULL &&
- mhp->ext[SADB_X_EXT_NAT_T_DPORT] != NULL) {
- struct sadb_x_nat_t_port *sport, *dport;
-
- if (mhp->extlen[SADB_X_EXT_NAT_T_SPORT] < sizeof(*sport) ||
- mhp->extlen[SADB_X_EXT_NAT_T_DPORT] < sizeof(*dport)) {
- ipseclog((LOG_DEBUG, "%s: invalid message.\n",
- __func__));
- return key_senderror(so, m, EINVAL);
- }
-
- sport = (struct sadb_x_nat_t_port *)
- mhp->ext[SADB_X_EXT_NAT_T_SPORT];
- dport = (struct sadb_x_nat_t_port *)
- mhp->ext[SADB_X_EXT_NAT_T_DPORT];
-
- if (sport)
- KEY_PORTTOSADDR(&saidx.src,
- sport->sadb_x_nat_t_port_port);
- if (dport)
- KEY_PORTTOSADDR(&saidx.dst,
- dport->sadb_x_nat_t_port_port);
- }
-#endif
-
- /* get a SA header */
- SAHTREE_LOCK();
- LIST_FOREACH(sah, &V_sahtree, chain) {
- if (sah->state == SADB_SASTATE_DEAD)
- continue;
- if (key_cmpsaidx(&sah->saidx, &saidx, CMP_HEAD) == 0)
- continue;
-
- /* get a SA with SPI. */
- sav = key_getsavbyspi(sah, sa0->sadb_sa_spi);
- if (sav)
- break;
+ return (key_delete_all(so, m, mhp, &saidx));
}
- if (sah == NULL) {
- SAHTREE_UNLOCK();
- ipseclog((LOG_DEBUG, "%s: no SA found.\n", __func__));
- return key_senderror(so, m, ENOENT);
+ if (SADB_CHECKLEN(mhp, SADB_EXT_SA)) {
+ ipseclog((LOG_DEBUG,
+ "%s: invalid message: wrong header size.\n", __func__));
+ return (key_senderror(so, m, EINVAL));
}
-
- key_sa_chgstate(sav, SADB_SASTATE_DEAD);
- KEY_FREESAV(&sav);
- SAHTREE_UNLOCK();
+ sa0 = (struct sadb_sa *)mhp->ext[SADB_EXT_SA];
+ if (proto == IPPROTO_TCP)
+ sav = key_getsav_tcpmd5(&saidx, NULL);
+ else
+ sav = key_getsavbyspi(sa0->sadb_sa_spi);
+ if (sav == NULL) {
+ ipseclog((LOG_DEBUG, "%s: no SA found for SPI %u.\n",
+ __func__, ntohl(sa0->sadb_sa_spi)));
+ return (key_senderror(so, m, ESRCH));
+ }
+ if (key_cmpsaidx(&sav->sah->saidx, &saidx, CMP_HEAD) == 0) {
+ ipseclog((LOG_DEBUG, "%s: saidx mismatched for SPI %u.\n",
+ __func__, ntohl(sav->spi)));
+ key_freesav(&sav);
+ return (key_senderror(so, m, ESRCH));
+ }
+ KEYDBG(KEY_STAMP,
+ printf("%s: SA(%p)\n", __func__, sav));
+ KEYDBG(KEY_DATA, kdebug_secasv(sav));
+ key_unlinksav(sav);
+ key_freesav(&sav);
{
struct mbuf *n;
struct sadb_msg *newmsg;
/* create new sadb_msg to reply. */
- /* XXX-BZ NAT-T extensions? */
n = key_gather_mbuf(m, mhp, 1, 4, SADB_EXT_RESERVED,
SADB_EXT_SA, SADB_EXT_ADDRESS_SRC, SADB_EXT_ADDRESS_DST);
if (!n)
@@ -5641,94 +5945,43 @@ key_delete(struct socket *so, struct mbuf *m, const struct sadb_msghdr *mhp)
*/
static int
key_delete_all(struct socket *so, struct mbuf *m,
- const struct sadb_msghdr *mhp, u_int16_t proto)
+ const struct sadb_msghdr *mhp, struct secasindex *saidx)
{
- struct sadb_address *src0, *dst0;
- struct secasindex saidx;
+ struct secasvar_queue drainq;
struct secashead *sah;
struct secasvar *sav, *nextsav;
- u_int stateidx, state;
-
- src0 = (struct sadb_address *)(mhp->ext[SADB_EXT_ADDRESS_SRC]);
- dst0 = (struct sadb_address *)(mhp->ext[SADB_EXT_ADDRESS_DST]);
-
- /* XXX boundary check against sa_len */
- KEY_SETSECASIDX(proto, IPSEC_MODE_ANY, 0, src0 + 1, dst0 + 1, &saidx);
-
- /*
- * Make sure the port numbers are zero.
- * In case of NAT-T we will update them later if needed.
- */
- KEY_PORTTOSADDR(&saidx.src, 0);
- KEY_PORTTOSADDR(&saidx.dst, 0);
-
-#ifdef IPSEC_NAT_T
- /*
- * Handle NAT-T info if present.
- */
-
- if (mhp->ext[SADB_X_EXT_NAT_T_SPORT] != NULL &&
- mhp->ext[SADB_X_EXT_NAT_T_DPORT] != NULL) {
- struct sadb_x_nat_t_port *sport, *dport;
- if (mhp->extlen[SADB_X_EXT_NAT_T_SPORT] < sizeof(*sport) ||
- mhp->extlen[SADB_X_EXT_NAT_T_DPORT] < sizeof(*dport)) {
- ipseclog((LOG_DEBUG, "%s: invalid message.\n",
- __func__));
- return key_senderror(so, m, EINVAL);
- }
-
- sport = (struct sadb_x_nat_t_port *)
- mhp->ext[SADB_X_EXT_NAT_T_SPORT];
- dport = (struct sadb_x_nat_t_port *)
- mhp->ext[SADB_X_EXT_NAT_T_DPORT];
-
- if (sport)
- KEY_PORTTOSADDR(&saidx.src,
- sport->sadb_x_nat_t_port_port);
- if (dport)
- KEY_PORTTOSADDR(&saidx.dst,
- dport->sadb_x_nat_t_port_port);
- }
-#endif
-
- SAHTREE_LOCK();
- LIST_FOREACH(sah, &V_sahtree, chain) {
- if (sah->state == SADB_SASTATE_DEAD)
- continue;
- if (key_cmpsaidx(&sah->saidx, &saidx, CMP_HEAD) == 0)
+ TAILQ_INIT(&drainq);
+ SAHTREE_WLOCK();
+ LIST_FOREACH(sah, SAHADDRHASH_HASH(saidx), addrhash) {
+ if (key_cmpsaidx(&sah->saidx, saidx, CMP_HEAD) == 0)
continue;
-
- /* Delete all non-LARVAL SAs. */
- for (stateidx = 0;
- stateidx < _ARRAYLEN(saorder_state_alive);
- stateidx++) {
- state = saorder_state_alive[stateidx];
- if (state == SADB_SASTATE_LARVAL)
- continue;
- for (sav = LIST_FIRST(&sah->savtree[state]);
- sav != NULL; sav = nextsav) {
- nextsav = LIST_NEXT(sav, chain);
- /* sanity check */
- if (sav->state != state) {
- ipseclog((LOG_DEBUG, "%s: invalid "
- "sav->state (queue %d SA %d)\n",
- __func__, state, sav->state));
- continue;
- }
-
- key_sa_chgstate(sav, SADB_SASTATE_DEAD);
- KEY_FREESAV(&sav);
- }
- }
+ /* Move all ALIVE SAs into drainq */
+ TAILQ_CONCAT(&drainq, &sah->savtree_alive, chain);
+ }
+ /* Unlink all queued SAs from SPI hash */
+ TAILQ_FOREACH(sav, &drainq, chain) {
+ sav->state = SADB_SASTATE_DEAD;
+ LIST_REMOVE(sav, spihash);
+ }
+ SAHTREE_WUNLOCK();
+ /* Now we can release reference for all SAs in drainq */
+ sav = TAILQ_FIRST(&drainq);
+ while (sav != NULL) {
+ KEYDBG(KEY_STAMP,
+ printf("%s: SA(%p)\n", __func__, sav));
+ KEYDBG(KEY_DATA, kdebug_secasv(sav));
+ nextsav = TAILQ_NEXT(sav, chain);
+ key_freesah(&sav->sah); /* release reference from SAV */
+ key_freesav(&sav); /* release last reference */
+ sav = nextsav;
}
- SAHTREE_UNLOCK();
+
{
struct mbuf *n;
struct sadb_msg *newmsg;
/* create new sadb_msg to reply. */
- /* XXX-BZ NAT-T extensions? */
n = key_gather_mbuf(m, mhp, 1, 3, SADB_EXT_RESERVED,
SADB_EXT_ADDRESS_SRC, SADB_EXT_ADDRESS_DST);
if (!n)
@@ -5749,6 +6002,52 @@ key_delete_all(struct socket *so, struct mbuf *m,
}
/*
+ * Delete all alive SAs for corresponding xform.
+ * Larval SAs have not initialized tdb_xform, so it is safe to leave them
+ * here when xform disappears.
+ */
+static void
+key_delete_xform(const struct xformsw *xsp)
+{
+ struct secasvar_queue drainq;
+ struct secashead *sah;
+ struct secasvar *sav, *nextsav;
+
+ TAILQ_INIT(&drainq);
+ SAHTREE_WLOCK();
+ TAILQ_FOREACH(sah, &V_sahtree, chain) {
+ sav = TAILQ_FIRST(&sah->savtree_alive);
+ if (sav == NULL)
+ continue;
+ if (sav->tdb_xform != xsp)
+ continue;
+ /*
+ * It is supposed that all SAs in the chain are related to
+ * one xform.
+ */
+ TAILQ_CONCAT(&drainq, &sah->savtree_alive, chain);
+ }
+ /* Unlink all queued SAs from SPI hash */
+ TAILQ_FOREACH(sav, &drainq, chain) {
+ sav->state = SADB_SASTATE_DEAD;
+ LIST_REMOVE(sav, spihash);
+ }
+ SAHTREE_WUNLOCK();
+
+ /* Now we can release reference for all SAs in drainq */
+ sav = TAILQ_FIRST(&drainq);
+ while (sav != NULL) {
+ KEYDBG(KEY_STAMP,
+ printf("%s: SA(%p)\n", __func__, sav));
+ KEYDBG(KEY_DATA, kdebug_secasv(sav));
+ nextsav = TAILQ_NEXT(sav, chain);
+ key_freesah(&sav->sah); /* release reference from SAV */
+ key_freesav(&sav); /* release last reference */
+ sav = nextsav;
+ }
+}
+
+/*
* SADB_GET processing
* receive
* <base, SA(*), address(SD)>
@@ -5763,12 +6062,11 @@ key_delete_all(struct socket *so, struct mbuf *m,
static int
key_get(struct socket *so, struct mbuf *m, const struct sadb_msghdr *mhp)
{
- struct sadb_sa *sa0;
- struct sadb_address *src0, *dst0;
struct secasindex saidx;
- struct secashead *sah;
- struct secasvar *sav = NULL;
- u_int16_t proto;
+ struct sadb_address *src0, *dst0;
+ struct sadb_sa *sa0;
+ struct secasvar *sav;
+ uint8_t proto;
IPSEC_ASSERT(so != NULL, ("null socket"));
IPSEC_ASSERT(m != NULL, ("null mbuf"));
@@ -5782,18 +6080,19 @@ key_get(struct socket *so, struct mbuf *m, const struct sadb_msghdr *mhp)
return key_senderror(so, m, EINVAL);
}
- if (mhp->ext[SADB_EXT_SA] == NULL ||
- mhp->ext[SADB_EXT_ADDRESS_SRC] == NULL ||
- mhp->ext[SADB_EXT_ADDRESS_DST] == NULL) {
- ipseclog((LOG_DEBUG, "%s: invalid message is passed.\n",
- __func__));
+ if (SADB_CHECKHDR(mhp, SADB_EXT_SA) ||
+ SADB_CHECKHDR(mhp, SADB_EXT_ADDRESS_SRC) ||
+ SADB_CHECKHDR(mhp, SADB_EXT_ADDRESS_DST)) {
+ ipseclog((LOG_DEBUG,
+ "%s: invalid message: missing required header.\n",
+ __func__));
return key_senderror(so, m, EINVAL);
}
- if (mhp->extlen[SADB_EXT_SA] < sizeof(struct sadb_sa) ||
- mhp->extlen[SADB_EXT_ADDRESS_SRC] < sizeof(struct sadb_address) ||
- mhp->extlen[SADB_EXT_ADDRESS_DST] < sizeof(struct sadb_address)) {
- ipseclog((LOG_DEBUG, "%s: invalid message is passed.\n",
- __func__));
+ if (SADB_CHECKLEN(mhp, SADB_EXT_SA) ||
+ SADB_CHECKLEN(mhp, SADB_EXT_ADDRESS_SRC) ||
+ SADB_CHECKLEN(mhp, SADB_EXT_ADDRESS_DST)) {
+ ipseclog((LOG_DEBUG,
+ "%s: invalid message: wrong header size.\n", __func__));
return key_senderror(so, m, EINVAL);
}
@@ -5801,79 +6100,45 @@ key_get(struct socket *so, struct mbuf *m, const struct sadb_msghdr *mhp)
src0 = (struct sadb_address *)mhp->ext[SADB_EXT_ADDRESS_SRC];
dst0 = (struct sadb_address *)mhp->ext[SADB_EXT_ADDRESS_DST];
- /* XXX boundary check against sa_len */
- KEY_SETSECASIDX(proto, IPSEC_MODE_ANY, 0, src0 + 1, dst0 + 1, &saidx);
-
- /*
- * Make sure the port numbers are zero.
- * In case of NAT-T we will update them later if needed.
- */
- KEY_PORTTOSADDR(&saidx.src, 0);
- KEY_PORTTOSADDR(&saidx.dst, 0);
-
-#ifdef IPSEC_NAT_T
- /*
- * Handle NAT-T info if present.
- */
-
- if (mhp->ext[SADB_X_EXT_NAT_T_SPORT] != NULL &&
- mhp->ext[SADB_X_EXT_NAT_T_DPORT] != NULL) {
- struct sadb_x_nat_t_port *sport, *dport;
-
- if (mhp->extlen[SADB_X_EXT_NAT_T_SPORT] < sizeof(*sport) ||
- mhp->extlen[SADB_X_EXT_NAT_T_DPORT] < sizeof(*dport)) {
- ipseclog((LOG_DEBUG, "%s: invalid message.\n",
- __func__));
- return key_senderror(so, m, EINVAL);
- }
-
- sport = (struct sadb_x_nat_t_port *)
- mhp->ext[SADB_X_EXT_NAT_T_SPORT];
- dport = (struct sadb_x_nat_t_port *)
- mhp->ext[SADB_X_EXT_NAT_T_DPORT];
-
- if (sport)
- KEY_PORTTOSADDR(&saidx.src,
- sport->sadb_x_nat_t_port_port);
- if (dport)
- KEY_PORTTOSADDR(&saidx.dst,
- dport->sadb_x_nat_t_port_port);
+ if (key_checksockaddrs((struct sockaddr *)(src0 + 1),
+ (struct sockaddr *)(dst0 + 1)) != 0) {
+ ipseclog((LOG_DEBUG, "%s: invalid sockaddr.\n", __func__));
+ return key_senderror(so, m, EINVAL);
}
-#endif
-
- /* get a SA header */
- SAHTREE_LOCK();
- LIST_FOREACH(sah, &V_sahtree, chain) {
- if (sah->state == SADB_SASTATE_DEAD)
- continue;
- if (key_cmpsaidx(&sah->saidx, &saidx, CMP_HEAD) == 0)
- continue;
+ KEY_SETSECASIDX(proto, IPSEC_MODE_ANY, 0, src0 + 1, dst0 + 1, &saidx);
- /* get a SA with SPI. */
- sav = key_getsavbyspi(sah, sa0->sadb_sa_spi);
- if (sav)
- break;
- }
- SAHTREE_UNLOCK();
- if (sah == NULL) {
+ if (proto == IPPROTO_TCP)
+ sav = key_getsav_tcpmd5(&saidx, NULL);
+ else
+ sav = key_getsavbyspi(sa0->sadb_sa_spi);
+ if (sav == NULL) {
ipseclog((LOG_DEBUG, "%s: no SA found.\n", __func__));
- return key_senderror(so, m, ENOENT);
+ return key_senderror(so, m, ESRCH);
+ }
+ if (key_cmpsaidx(&sav->sah->saidx, &saidx, CMP_HEAD) == 0) {
+ ipseclog((LOG_DEBUG, "%s: saidx mismatched for SPI %u.\n",
+ __func__, ntohl(sa0->sadb_sa_spi)));
+ key_freesav(&sav);
+ return (key_senderror(so, m, ESRCH));
}
{
struct mbuf *n;
- u_int8_t satype;
+ uint8_t satype;
/* map proto to satype */
- if ((satype = key_proto2satype(sah->saidx.proto)) == 0) {
+ if ((satype = key_proto2satype(sav->sah->saidx.proto)) == 0) {
ipseclog((LOG_DEBUG, "%s: there was invalid proto in SAD.\n",
- __func__));
+ __func__));
+ key_freesav(&sav);
return key_senderror(so, m, EINVAL);
}
/* create new sadb_msg to reply. */
n = key_setdumpsa(sav, SADB_GET, satype, mhp->msg->sadb_msg_seq,
mhp->msg->sadb_msg_pid);
+
+ key_freesav(&sav);
if (!n)
return key_senderror(so, m, ENOBUFS);
@@ -5902,10 +6167,10 @@ key_getcomb_setlifetime(struct sadb_comb *comb)
* XXX no idea if the user wants ESP authentication or not
*/
static struct mbuf *
-key_getcomb_esp()
+key_getcomb_ealg(void)
{
struct sadb_comb *comb;
- struct enc_xform *algo;
+ const struct enc_xform *algo;
struct mbuf *result = NULL, *m, *n;
int encmin;
int i, off, o;
@@ -5914,7 +6179,7 @@ key_getcomb_esp()
m = NULL;
for (i = 1; i <= SADB_EALG_MAX; i++) {
- algo = esp_algorithm_lookup(i);
+ algo = enc_algorithm_lookup(i);
if (algo == NULL)
continue;
@@ -6007,8 +6272,8 @@ key_getsizes_ah(const struct auth_hash *ah, int alg, u_int16_t* min,
static struct mbuf *
key_getcomb_ah()
{
+ const struct auth_hash *algo;
struct sadb_comb *comb;
- struct auth_hash *algo;
struct mbuf *m;
u_int16_t minkeysize, maxkeysize;
int i;
@@ -6025,7 +6290,7 @@ key_getcomb_ah()
i != SADB_X_AALG_SHA2_512)
continue;
#endif
- algo = ah_algorithm_lookup(i);
+ algo = auth_algorithm_lookup(i);
if (!algo)
continue;
key_getsizes_ah(algo, i, &minkeysize, &maxkeysize);
@@ -6065,15 +6330,15 @@ key_getcomb_ah()
static struct mbuf *
key_getcomb_ipcomp()
{
+ const struct comp_algo *algo;
struct sadb_comb *comb;
- struct comp_algo *algo;
struct mbuf *m;
int i;
const int l = PFKEY_ALIGN8(sizeof(struct sadb_comb));
m = NULL;
for (i = 1; i <= SADB_X_CALG_MAX; i++) {
- algo = ipcomp_algorithm_lookup(i);
+ algo = comp_algorithm_lookup(i);
if (!algo)
continue;
@@ -6116,7 +6381,7 @@ key_getprop(const struct secasindex *saidx)
switch (saidx->proto) {
case IPPROTO_ESP:
- m = key_getcomb_esp();
+ m = key_getcomb_ealg();
break;
case IPPROTO_AH:
m = key_getcomb_ah();
@@ -6172,11 +6437,10 @@ key_acquire(const struct secasindex *saidx, struct secpolicy *sp)
{
union sockaddr_union addr;
struct mbuf *result, *m;
- struct secacq *newacq;
- u_int32_t seq;
+ uint32_t seq;
int error;
- u_int16_t ul_proto;
- u_int8_t mask, satype;
+ uint16_t ul_proto;
+ uint8_t mask, satype;
IPSEC_ASSERT(saidx != NULL, ("null saidx"));
satype = key_proto2satype(saidx->proto);
@@ -6185,30 +6449,12 @@ key_acquire(const struct secasindex *saidx, struct secpolicy *sp)
error = -1;
result = NULL;
ul_proto = IPSEC_ULPROTO_ANY;
- /*
- * We never do anything about acquirng SA. There is anather
- * solution that kernel blocks to send SADB_ACQUIRE message until
- * getting something message from IKEd. In later case, to be
- * managed with ACQUIRING list.
- */
- /* Get an entry to check whether sending message or not. */
- if ((newacq = key_getacq(saidx)) != NULL) {
- if (V_key_blockacq_count < newacq->count) {
- /* reset counter and do send message. */
- newacq->count = 0;
- } else {
- /* increment counter and do nothing. */
- newacq->count++;
- return 0;
- }
- } else {
- /* make new entry for blocking to send SADB_ACQUIRE. */
- if ((newacq = key_newacq(saidx)) == NULL)
- return ENOBUFS;
- }
+ /* Get seq number to check whether sending message or not. */
+ seq = key_getacq(saidx, &error);
+ if (seq == 0)
+ return (error);
- seq = newacq->seq;
m = key_setsadbmsg(SADB_ACQUIRE, 0, satype, seq, 0, 0);
if (!m) {
error = ENOBUFS;
@@ -6217,15 +6463,14 @@ key_acquire(const struct secasindex *saidx, struct secpolicy *sp)
result = m;
/*
- * No SADB_X_EXT_NAT_T_* here: we do not know
- * anything related to NAT-T at this time.
- */
-
- /*
* set sadb_address for saidx's.
*
* Note that if sp is supplied, then we're being called from
- * key_checkrequest and should supply port and protocol information.
+ * key_allocsa_policy() and should supply port and protocol
+ * information.
+ * XXXAE: why only TCP and UDP? ICMP and SCTP looks applicable too.
+ * XXXAE: probably we can handle this in the ipsec[46]_allocsa().
+ * XXXAE: it looks like we should save this info in the ACQ entry.
*/
if (sp != NULL && (sp->spidx.ul_proto == IPPROTO_TCP ||
sp->spidx.ul_proto == IPPROTO_UDP))
@@ -6243,7 +6488,8 @@ key_acquire(const struct secasindex *saidx, struct secpolicy *sp)
break;
case AF_INET6:
if (sp->spidx.src.sin6.sin6_port != IPSEC_PORT_ANY) {
- addr.sin6.sin6_port = sp->spidx.src.sin6.sin6_port;
+ addr.sin6.sin6_port =
+ sp->spidx.src.sin6.sin6_port;
mask = sp->spidx.prefs;
}
break;
@@ -6270,7 +6516,8 @@ key_acquire(const struct secasindex *saidx, struct secpolicy *sp)
break;
case AF_INET6:
if (sp->spidx.dst.sin6.sin6_port != IPSEC_PORT_ANY) {
- addr.sin6.sin6_port = sp->spidx.dst.sin6.sin6_port;
+ addr.sin6.sin6_port =
+ sp->spidx.dst.sin6.sin6_port;
mask = sp->spidx.prefd;
}
break;
@@ -6288,8 +6535,9 @@ key_acquire(const struct secasindex *saidx, struct secpolicy *sp)
/* XXX proxy address (optional) */
/* set sadb_x_policy */
- if (sp) {
- m = key_setsadbxpolicy(sp->policy, sp->spidx.dir, sp->id, sp->priority);
+ if (sp != NULL) {
+ m = key_setsadbxpolicy(sp->policy, sp->spidx.dir, sp->id,
+ sp->priority);
if (!m) {
error = ENOBUFS;
goto fail;
@@ -6381,6 +6629,10 @@ key_acquire(const struct secasindex *saidx, struct secpolicy *sp)
mtod(result, struct sadb_msg *)->sadb_msg_len =
PFKEY_UNIT64(result->m_pkthdr.len);
+ KEYDBG(KEY_STAMP,
+ printf("%s: SP(%p)\n", __func__, sp));
+ KEYDBG(KEY_DATA, kdebug_secasindex(saidx, NULL));
+
return key_sendup_mbuf(NULL, result, KEY_SENDUP_REGISTERED);
fail:
@@ -6389,60 +6641,122 @@ key_acquire(const struct secasindex *saidx, struct secpolicy *sp)
return error;
}
-static struct secacq *
-key_newacq(const struct secasindex *saidx)
+static uint32_t
+key_newacq(const struct secasindex *saidx, int *perror)
{
- struct secacq *newacq;
+ struct secacq *acq;
+ uint32_t seq;
- /* get new entry */
- newacq = malloc(sizeof(struct secacq), M_IPSEC_SAQ, M_NOWAIT|M_ZERO);
- if (newacq == NULL) {
+ acq = malloc(sizeof(*acq), M_IPSEC_SAQ, M_NOWAIT | M_ZERO);
+ if (acq == NULL) {
ipseclog((LOG_DEBUG, "%s: No more memory.\n", __func__));
- return NULL;
+ *perror = ENOBUFS;
+ return (0);
}
/* copy secindex */
- bcopy(saidx, &newacq->saidx, sizeof(newacq->saidx));
- newacq->seq = (V_acq_seq == ~0 ? 1 : ++V_acq_seq);
- newacq->created = time_second;
- newacq->count = 0;
+ bcopy(saidx, &acq->saidx, sizeof(acq->saidx));
+ acq->created = time_second;
+ acq->count = 0;
/* add to acqtree */
ACQ_LOCK();
- LIST_INSERT_HEAD(&V_acqtree, newacq, chain);
+ seq = acq->seq = (V_acq_seq == ~0 ? 1 : ++V_acq_seq);
+ LIST_INSERT_HEAD(&V_acqtree, acq, chain);
+ LIST_INSERT_HEAD(ACQADDRHASH_HASH(saidx), acq, addrhash);
+ LIST_INSERT_HEAD(ACQSEQHASH_HASH(seq), acq, seqhash);
ACQ_UNLOCK();
-
- return newacq;
+ *perror = 0;
+ return (seq);
}
-static struct secacq *
-key_getacq(const struct secasindex *saidx)
+static uint32_t
+key_getacq(const struct secasindex *saidx, int *perror)
{
struct secacq *acq;
+ uint32_t seq;
ACQ_LOCK();
- LIST_FOREACH(acq, &V_acqtree, chain) {
- if (key_cmpsaidx(saidx, &acq->saidx, CMP_EXACTLY))
+ LIST_FOREACH(acq, ACQADDRHASH_HASH(saidx), addrhash) {
+ if (key_cmpsaidx(&acq->saidx, saidx, CMP_EXACTLY)) {
+ if (acq->count > V_key_blockacq_count) {
+ /*
+ * Reset counter and send message.
+ * Also reset created time to keep ACQ for
+ * this saidx.
+ */
+ acq->created = time_second;
+ acq->count = 0;
+ seq = acq->seq;
+ } else {
+ /*
+ * Increment counter and do nothing.
+ * We send SADB_ACQUIRE message only
+ * for each V_key_blockacq_count packet.
+ */
+ acq->count++;
+ seq = 0;
+ }
break;
+ }
}
ACQ_UNLOCK();
-
- return acq;
+ if (acq != NULL) {
+ *perror = 0;
+ return (seq);
+ }
+ /* allocate new entry */
+ return (key_newacq(saidx, perror));
}
-static struct secacq *
-key_getacqbyseq(u_int32_t seq)
+static int
+key_acqreset(uint32_t seq)
{
struct secacq *acq;
ACQ_LOCK();
- LIST_FOREACH(acq, &V_acqtree, chain) {
- if (acq->seq == seq)
+ LIST_FOREACH(acq, ACQSEQHASH_HASH(seq), seqhash) {
+ if (acq->seq == seq) {
+ acq->count = 0;
+ acq->created = time_second;
break;
+ }
}
ACQ_UNLOCK();
+ if (acq == NULL)
+ return (ESRCH);
+ return (0);
+}
+/*
+ * Mark ACQ entry as stale to remove it in key_flush_acq().
+ * Called after successful SADB_GETSPI message.
+ */
+static int
+key_acqdone(const struct secasindex *saidx, uint32_t seq)
+{
+ struct secacq *acq;
- return acq;
+ ACQ_LOCK();
+ LIST_FOREACH(acq, ACQSEQHASH_HASH(seq), seqhash) {
+ if (acq->seq == seq)
+ break;
+ }
+ if (acq != NULL) {
+ if (key_cmpsaidx(&acq->saidx, saidx, CMP_EXACTLY) == 0) {
+ ipseclog((LOG_DEBUG,
+ "%s: Mismatched saidx for ACQ %u", __func__, seq));
+ acq = NULL;
+ } else {
+ acq->created = 0;
+ }
+ } else {
+ ipseclog((LOG_DEBUG,
+ "%s: ACQ %u is not found.", __func__, seq));
+ }
+ ACQ_UNLOCK();
+ if (acq == NULL)
+ return (ESRCH);
+ return (0);
}
static struct secspacq *
@@ -6504,11 +6818,13 @@ key_getspacq(struct secpolicyindex *spidx)
static int
key_acquire2(struct socket *so, struct mbuf *m, const struct sadb_msghdr *mhp)
{
- const struct sadb_address *src0, *dst0;
+ SAHTREE_RLOCK_TRACKER;
+ struct sadb_address *src0, *dst0;
struct secasindex saidx;
struct secashead *sah;
- u_int16_t proto;
+ uint32_t reqid;
int error;
+ uint8_t mode, proto;
IPSEC_ASSERT(so != NULL, ("null socket"));
IPSEC_ASSERT(m != NULL, ("null mbuf"));
@@ -6522,30 +6838,23 @@ key_acquire2(struct socket *so, struct mbuf *m, const struct sadb_msghdr *mhp)
* We do not raise error even if error occurred in this function.
*/
if (mhp->msg->sadb_msg_len == PFKEY_UNIT64(sizeof(struct sadb_msg))) {
- struct secacq *acq;
-
/* check sequence number */
- if (mhp->msg->sadb_msg_seq == 0) {
+ if (mhp->msg->sadb_msg_seq == 0 ||
+ mhp->msg->sadb_msg_errno == 0) {
ipseclog((LOG_DEBUG, "%s: must specify sequence "
- "number.\n", __func__));
- m_freem(m);
- return 0;
- }
-
- if ((acq = key_getacqbyseq(mhp->msg->sadb_msg_seq)) == NULL) {
+ "number and errno.\n", __func__));
+ } else {
/*
- * the specified larval SA is already gone, or we got
- * a bogus sequence number. we can silently ignore it.
+ * IKEd reported that error occurred.
+ * XXXAE: what it expects from the kernel?
+ * Probably we should send SADB_ACQUIRE again?
+ * If so, reset ACQ's state.
+ * XXXAE: it looks useless.
*/
- m_freem(m);
- return 0;
+ key_acqreset(mhp->msg->sadb_msg_seq);
}
-
- /* reset acq counter in order to deletion by timehander. */
- acq->created = time_second;
- acq->count = 0;
m_freem(m);
- return 0;
+ return (0);
}
/*
@@ -6555,79 +6864,60 @@ key_acquire2(struct socket *so, struct mbuf *m, const struct sadb_msghdr *mhp)
/* map satype to proto */
if ((proto = key_satype2proto(mhp->msg->sadb_msg_satype)) == 0) {
ipseclog((LOG_DEBUG, "%s: invalid satype is passed.\n",
- __func__));
+ __func__));
return key_senderror(so, m, EINVAL);
}
- if (mhp->ext[SADB_EXT_ADDRESS_SRC] == NULL ||
- mhp->ext[SADB_EXT_ADDRESS_DST] == NULL ||
- mhp->ext[SADB_EXT_PROPOSAL] == NULL) {
- /* error */
- ipseclog((LOG_DEBUG, "%s: invalid message is passed.\n",
- __func__));
+ if (SADB_CHECKHDR(mhp, SADB_EXT_ADDRESS_SRC) ||
+ SADB_CHECKHDR(mhp, SADB_EXT_ADDRESS_DST) ||
+ SADB_CHECKHDR(mhp, SADB_EXT_PROPOSAL)) {
+ ipseclog((LOG_DEBUG,
+ "%s: invalid message: missing required header.\n",
+ __func__));
return key_senderror(so, m, EINVAL);
}
- if (mhp->extlen[SADB_EXT_ADDRESS_SRC] < sizeof(struct sadb_address) ||
- mhp->extlen[SADB_EXT_ADDRESS_DST] < sizeof(struct sadb_address) ||
- mhp->extlen[SADB_EXT_PROPOSAL] < sizeof(struct sadb_prop)) {
- /* error */
- ipseclog((LOG_DEBUG, "%s: invalid message is passed.\n",
- __func__));
+ if (SADB_CHECKLEN(mhp, SADB_EXT_ADDRESS_SRC) ||
+ SADB_CHECKLEN(mhp, SADB_EXT_ADDRESS_DST) ||
+ SADB_CHECKLEN(mhp, SADB_EXT_PROPOSAL)) {
+ ipseclog((LOG_DEBUG,
+ "%s: invalid message: wrong header size.\n", __func__));
return key_senderror(so, m, EINVAL);
}
- src0 = (struct sadb_address *)mhp->ext[SADB_EXT_ADDRESS_SRC];
- dst0 = (struct sadb_address *)mhp->ext[SADB_EXT_ADDRESS_DST];
-
- /* XXX boundary check against sa_len */
- KEY_SETSECASIDX(proto, IPSEC_MODE_ANY, 0, src0 + 1, dst0 + 1, &saidx);
-
- /*
- * Make sure the port numbers are zero.
- * In case of NAT-T we will update them later if needed.
- */
- KEY_PORTTOSADDR(&saidx.src, 0);
- KEY_PORTTOSADDR(&saidx.dst, 0);
-
-#ifndef IPSEC_NAT_T
- /*
- * Handle NAT-T info if present.
- */
-
- if (mhp->ext[SADB_X_EXT_NAT_T_SPORT] != NULL &&
- mhp->ext[SADB_X_EXT_NAT_T_DPORT] != NULL) {
- struct sadb_x_nat_t_port *sport, *dport;
-
- if (mhp->extlen[SADB_X_EXT_NAT_T_SPORT] < sizeof(*sport) ||
- mhp->extlen[SADB_X_EXT_NAT_T_DPORT] < sizeof(*dport)) {
- ipseclog((LOG_DEBUG, "%s: invalid message.\n",
+ if (SADB_CHECKHDR(mhp, SADB_X_EXT_SA2)) {
+ mode = IPSEC_MODE_ANY;
+ reqid = 0;
+ } else {
+ if (SADB_CHECKLEN(mhp, SADB_X_EXT_SA2)) {
+ ipseclog((LOG_DEBUG,
+ "%s: invalid message: wrong header size.\n",
__func__));
return key_senderror(so, m, EINVAL);
}
+ mode = ((struct sadb_x_sa2 *)
+ mhp->ext[SADB_X_EXT_SA2])->sadb_x_sa2_mode;
+ reqid = ((struct sadb_x_sa2 *)
+ mhp->ext[SADB_X_EXT_SA2])->sadb_x_sa2_reqid;
+ }
- sport = (struct sadb_x_nat_t_port *)
- mhp->ext[SADB_X_EXT_NAT_T_SPORT];
- dport = (struct sadb_x_nat_t_port *)
- mhp->ext[SADB_X_EXT_NAT_T_DPORT];
+ src0 = (struct sadb_address *)mhp->ext[SADB_EXT_ADDRESS_SRC];
+ dst0 = (struct sadb_address *)mhp->ext[SADB_EXT_ADDRESS_DST];
- if (sport)
- KEY_PORTTOSADDR(&saidx.src,
- sport->sadb_x_nat_t_port_port);
- if (dport)
- KEY_PORTTOSADDR(&saidx.dst,
- dport->sadb_x_nat_t_port_port);
+ error = key_checksockaddrs((struct sockaddr *)(src0 + 1),
+ (struct sockaddr *)(dst0 + 1));
+ if (error != 0) {
+ ipseclog((LOG_DEBUG, "%s: invalid sockaddr.\n", __func__));
+ return key_senderror(so, m, EINVAL);
}
-#endif
+ KEY_SETSECASIDX(proto, mode, reqid, src0 + 1, dst0 + 1, &saidx);
/* get a SA index */
- SAHTREE_LOCK();
- LIST_FOREACH(sah, &V_sahtree, chain) {
- if (sah->state == SADB_SASTATE_DEAD)
- continue;
+ SAHTREE_RLOCK();
+ LIST_FOREACH(sah, SAHADDRHASH_HASH(&saidx), addrhash) {
if (key_cmpsaidx(&sah->saidx, &saidx, CMP_MODE_REQID))
break;
}
- SAHTREE_UNLOCK();
+ SAHTREE_RUNLOCK();
if (sah != NULL) {
ipseclog((LOG_DEBUG, "%s: a SA exists already.\n", __func__));
return key_senderror(so, m, EEXIST);
@@ -6635,12 +6925,13 @@ key_acquire2(struct socket *so, struct mbuf *m, const struct sadb_msghdr *mhp)
error = key_acquire(&saidx, NULL);
if (error != 0) {
- ipseclog((LOG_DEBUG, "%s: error %d returned from key_acquire\n",
- __func__, mhp->msg->sadb_msg_errno));
+ ipseclog((LOG_DEBUG,
+ "%s: error %d returned from key_acquire()\n",
+ __func__, error));
return key_senderror(so, m, error);
}
-
- return key_sendup_mbuf(so, m, KEY_SENDUP_REGISTERED);
+ m_freem(m);
+ return (0);
}
/*
@@ -6713,14 +7004,14 @@ key_register(struct socket *so, struct mbuf *m, const struct sadb_msghdr *mhp)
/* create new sadb_msg to reply. */
alen = 0;
for (i = 1; i <= SADB_AALG_MAX; i++) {
- if (ah_algorithm_lookup(i))
+ if (auth_algorithm_lookup(i))
alen += sizeof(struct sadb_alg);
}
if (alen)
alen += sizeof(struct sadb_supported);
elen = 0;
for (i = 1; i <= SADB_EALG_MAX; i++) {
- if (esp_algorithm_lookup(i))
+ if (enc_algorithm_lookup(i))
elen += sizeof(struct sadb_alg);
}
if (elen)
@@ -6759,10 +7050,10 @@ key_register(struct socket *so, struct mbuf *m, const struct sadb_msghdr *mhp)
off += PFKEY_ALIGN8(sizeof(*sup));
for (i = 1; i <= SADB_AALG_MAX; i++) {
- struct auth_hash *aalgo;
+ const struct auth_hash *aalgo;
u_int16_t minkeysize, maxkeysize;
- aalgo = ah_algorithm_lookup(i);
+ aalgo = auth_algorithm_lookup(i);
if (!aalgo)
continue;
alg = (struct sadb_alg *)(mtod(n, caddr_t) + off);
@@ -6783,9 +7074,9 @@ key_register(struct socket *so, struct mbuf *m, const struct sadb_msghdr *mhp)
off += PFKEY_ALIGN8(sizeof(*sup));
for (i = 1; i <= SADB_EALG_MAX; i++) {
- struct enc_xform *ealgo;
+ const struct enc_xform *ealgo;
- ealgo = esp_algorithm_lookup(i);
+ ealgo = enc_algorithm_lookup(i);
if (!ealgo)
continue;
alg = (struct sadb_alg *)(mtod(n, caddr_t) + off);
@@ -6848,15 +7139,19 @@ key_freereg(struct socket *so)
static int
key_expire(struct secasvar *sav, int hard)
{
- int satype;
struct mbuf *result = NULL, *m;
- int len;
- int error = -1;
struct sadb_lifetime *lt;
+ uint32_t replay_count;
+ int error, len;
+ uint8_t satype;
IPSEC_ASSERT (sav != NULL, ("null sav"));
IPSEC_ASSERT (sav->sah != NULL, ("null sa header"));
+ KEYDBG(KEY_STAMP,
+ printf("%s: SA(%p) expired %s lifetime\n", __func__,
+ sav, hard ? "hard": "soft"));
+ KEYDBG(KEY_DATA, kdebug_secasv(sav));
/* set msg header */
satype = key_proto2satype(sav->sah->saidx.proto);
IPSEC_ASSERT(satype != 0, ("invalid proto, satype %u", satype));
@@ -6876,8 +7171,11 @@ key_expire(struct secasvar *sav, int hard)
m_cat(result, m);
/* create SA extension */
- m = key_setsadbxsa2(sav->sah->saidx.mode,
- sav->replay ? sav->replay->count : 0,
+ SECASVAR_LOCK(sav);
+ replay_count = sav->replay ? sav->replay->count : 0;
+ SECASVAR_UNLOCK(sav);
+
+ m = key_setsadbxsa2(sav->sah->saidx.mode, replay_count,
sav->sah->saidx.reqid);
if (!m) {
error = ENOBUFS;
@@ -6885,6 +7183,15 @@ key_expire(struct secasvar *sav, int hard)
}
m_cat(result, m);
+ if (sav->replay && sav->replay->wsize > UINT8_MAX) {
+ m = key_setsadbxsareplay(sav->replay->wsize);
+ if (!m) {
+ error = ENOBUFS;
+ goto fail;
+ }
+ m_cat(result, m);
+ }
+
/* create lifetime extension (current and soft) */
len = PFKEY_ALIGN8(sizeof(*lt)) * 2;
m = m_get2(len, M_NOWAIT, MT_DATA, 0);
@@ -6898,10 +7205,12 @@ key_expire(struct secasvar *sav, int hard)
lt = mtod(m, struct sadb_lifetime *);
lt->sadb_lifetime_len = PFKEY_UNIT64(sizeof(struct sadb_lifetime));
lt->sadb_lifetime_exttype = SADB_EXT_LIFETIME_CURRENT;
- lt->sadb_lifetime_allocations = sav->lft_c->allocations;
- lt->sadb_lifetime_bytes = sav->lft_c->bytes;
- lt->sadb_lifetime_addtime = sav->lft_c->addtime;
- lt->sadb_lifetime_usetime = sav->lft_c->usetime;
+ lt->sadb_lifetime_allocations =
+ (uint32_t)counter_u64_fetch(sav->lft_c_allocations);
+ lt->sadb_lifetime_bytes =
+ counter_u64_fetch(sav->lft_c_bytes);
+ lt->sadb_lifetime_addtime = sav->created;
+ lt->sadb_lifetime_usetime = sav->firstused;
lt = (struct sadb_lifetime *)(mtod(m, caddr_t) + len / 2);
lt->sadb_lifetime_len = PFKEY_UNIT64(sizeof(struct sadb_lifetime));
if (hard) {
@@ -6941,6 +7250,8 @@ key_expire(struct secasvar *sav, int hard)
/*
* XXX-BZ Handle NAT-T extensions here.
+ * XXXAE: it doesn't seem quite useful. IKEs should not depend on
+ * this information, we report only significant SA fields.
*/
if ((result->m_flags & M_PKTHDR) == 0) {
@@ -6971,6 +7282,36 @@ key_expire(struct secasvar *sav, int hard)
return error;
}
+static void
+key_freesah_flushed(struct secashead_queue *flushq)
+{
+ struct secashead *sah, *nextsah;
+ struct secasvar *sav, *nextsav;
+
+ sah = TAILQ_FIRST(flushq);
+ while (sah != NULL) {
+ sav = TAILQ_FIRST(&sah->savtree_larval);
+ while (sav != NULL) {
+ nextsav = TAILQ_NEXT(sav, chain);
+ TAILQ_REMOVE(&sah->savtree_larval, sav, chain);
+ key_freesav(&sav); /* release last reference */
+ key_freesah(&sah); /* release reference from SAV */
+ sav = nextsav;
+ }
+ sav = TAILQ_FIRST(&sah->savtree_alive);
+ while (sav != NULL) {
+ nextsav = TAILQ_NEXT(sav, chain);
+ TAILQ_REMOVE(&sah->savtree_alive, sav, chain);
+ key_freesav(&sav); /* release last reference */
+ key_freesah(&sah); /* release reference from SAV */
+ sav = nextsav;
+ }
+ nextsah = TAILQ_NEXT(sah, chain);
+ key_freesah(&sah); /* release last reference */
+ sah = nextsah;
+ }
+}
+
/*
* SADB_FLUSH processing
* receive
@@ -6986,12 +7327,12 @@ key_expire(struct secasvar *sav, int hard)
static int
key_flush(struct socket *so, struct mbuf *m, const struct sadb_msghdr *mhp)
{
+ struct secashead_queue flushq;
struct sadb_msg *newmsg;
struct secashead *sah, *nextsah;
- struct secasvar *sav, *nextsav;
- u_int16_t proto;
- u_int8_t state;
- u_int stateidx;
+ struct secasvar *sav;
+ uint8_t proto;
+ int i;
IPSEC_ASSERT(so != NULL, ("null socket"));
IPSEC_ASSERT(mhp != NULL, ("null msghdr"));
@@ -7003,37 +7344,71 @@ key_flush(struct socket *so, struct mbuf *m, const struct sadb_msghdr *mhp)
__func__));
return key_senderror(so, m, EINVAL);
}
-
- /* no SATYPE specified, i.e. flushing all SA. */
- SAHTREE_LOCK();
- for (sah = LIST_FIRST(&V_sahtree);
- sah != NULL;
- sah = nextsah) {
- nextsah = LIST_NEXT(sah, chain);
-
- if (mhp->msg->sadb_msg_satype != SADB_SATYPE_UNSPEC
- && proto != sah->saidx.proto)
- continue;
-
- for (stateidx = 0;
- stateidx < _ARRAYLEN(saorder_state_alive);
- stateidx++) {
- state = saorder_state_any[stateidx];
- for (sav = LIST_FIRST(&sah->savtree[state]);
- sav != NULL;
- sav = nextsav) {
-
- nextsav = LIST_NEXT(sav, chain);
-
- key_sa_chgstate(sav, SADB_SASTATE_DEAD);
- KEY_FREESAV(&sav);
+ KEYDBG(KEY_STAMP,
+ printf("%s: proto %u\n", __func__, proto));
+
+ TAILQ_INIT(&flushq);
+ if (proto == IPSEC_PROTO_ANY) {
+ /* no SATYPE specified, i.e. flushing all SA. */
+ SAHTREE_WLOCK();
+ /* Move all SAHs into flushq */
+ TAILQ_CONCAT(&flushq, &V_sahtree, chain);
+ /* Flush all buckets in SPI hash */
+ for (i = 0; i < V_savhash_mask + 1; i++)
+ LIST_INIT(&V_savhashtbl[i]);
+ /* Flush all buckets in SAHADDRHASH */
+ for (i = 0; i < V_sahaddrhash_mask + 1; i++)
+ LIST_INIT(&V_sahaddrhashtbl[i]);
+ /* Mark all SAHs as unlinked */
+ TAILQ_FOREACH(sah, &flushq, chain) {
+ sah->state = SADB_SASTATE_DEAD;
+ /*
+ * Callout handler makes its job using
+ * RLOCK and drain queues. In case, when this
+ * function will be called just before it
+ * acquires WLOCK, we need to mark SAs as
+ * unlinked to prevent second unlink.
+ */
+ TAILQ_FOREACH(sav, &sah->savtree_larval, chain) {
+ sav->state = SADB_SASTATE_DEAD;
+ }
+ TAILQ_FOREACH(sav, &sah->savtree_alive, chain) {
+ sav->state = SADB_SASTATE_DEAD;
}
}
-
- sah->state = SADB_SASTATE_DEAD;
+ SAHTREE_WUNLOCK();
+ } else {
+ SAHTREE_WLOCK();
+ sah = TAILQ_FIRST(&V_sahtree);
+ while (sah != NULL) {
+ IPSEC_ASSERT(sah->state != SADB_SASTATE_DEAD,
+ ("DEAD SAH %p in SADB_FLUSH", sah));
+ nextsah = TAILQ_NEXT(sah, chain);
+ if (sah->saidx.proto != proto) {
+ sah = nextsah;
+ continue;
+ }
+ sah->state = SADB_SASTATE_DEAD;
+ TAILQ_REMOVE(&V_sahtree, sah, chain);
+ LIST_REMOVE(sah, addrhash);
+ /* Unlink all SAs from SPI hash */
+ TAILQ_FOREACH(sav, &sah->savtree_larval, chain) {
+ LIST_REMOVE(sav, spihash);
+ sav->state = SADB_SASTATE_DEAD;
+ }
+ TAILQ_FOREACH(sav, &sah->savtree_alive, chain) {
+ LIST_REMOVE(sav, spihash);
+ sav->state = SADB_SASTATE_DEAD;
+ }
+ /* Add SAH into flushq */
+ TAILQ_INSERT_HEAD(&flushq, sah, chain);
+ sah = nextsah;
+ }
+ SAHTREE_WUNLOCK();
}
- SAHTREE_UNLOCK();
+ key_freesah_flushed(&flushq);
+ /* Free all queued SAs and SAHs */
if (m->m_len < sizeof(struct sadb_msg) ||
sizeof(struct sadb_msg) > m->m_len + M_TRAILINGSPACE(m)) {
ipseclog((LOG_DEBUG, "%s: No more memory.\n", __func__));
@@ -7066,15 +7441,13 @@ key_flush(struct socket *so, struct mbuf *m, const struct sadb_msghdr *mhp)
static int
key_dump(struct socket *so, struct mbuf *m, const struct sadb_msghdr *mhp)
{
+ SAHTREE_RLOCK_TRACKER;
struct secashead *sah;
struct secasvar *sav;
- u_int16_t proto;
- u_int stateidx;
- u_int8_t satype;
- u_int8_t state;
- int cnt;
struct sadb_msg *newmsg;
struct mbuf *n;
+ uint32_t cnt;
+ uint8_t proto, satype;
IPSEC_ASSERT(so != NULL, ("null socket"));
IPSEC_ASSERT(m != NULL, ("null mbuf"));
@@ -7084,69 +7457,66 @@ key_dump(struct socket *so, struct mbuf *m, const struct sadb_msghdr *mhp)
/* map satype to proto */
if ((proto = key_satype2proto(mhp->msg->sadb_msg_satype)) == 0) {
ipseclog((LOG_DEBUG, "%s: invalid satype is passed.\n",
- __func__));
+ __func__));
return key_senderror(so, m, EINVAL);
}
/* count sav entries to be sent to the userland. */
cnt = 0;
- SAHTREE_LOCK();
- LIST_FOREACH(sah, &V_sahtree, chain) {
- if (mhp->msg->sadb_msg_satype != SADB_SATYPE_UNSPEC
- && proto != sah->saidx.proto)
+ SAHTREE_RLOCK();
+ TAILQ_FOREACH(sah, &V_sahtree, chain) {
+ if (mhp->msg->sadb_msg_satype != SADB_SATYPE_UNSPEC &&
+ proto != sah->saidx.proto)
continue;
- for (stateidx = 0;
- stateidx < _ARRAYLEN(saorder_state_any);
- stateidx++) {
- state = saorder_state_any[stateidx];
- LIST_FOREACH(sav, &sah->savtree[state], chain) {
- cnt++;
- }
- }
+ TAILQ_FOREACH(sav, &sah->savtree_larval, chain)
+ cnt++;
+ TAILQ_FOREACH(sav, &sah->savtree_alive, chain)
+ cnt++;
}
if (cnt == 0) {
- SAHTREE_UNLOCK();
+ SAHTREE_RUNLOCK();
return key_senderror(so, m, ENOENT);
}
/* send this to the userland, one at a time. */
newmsg = NULL;
- LIST_FOREACH(sah, &V_sahtree, chain) {
- if (mhp->msg->sadb_msg_satype != SADB_SATYPE_UNSPEC
- && proto != sah->saidx.proto)
+ TAILQ_FOREACH(sah, &V_sahtree, chain) {
+ if (mhp->msg->sadb_msg_satype != SADB_SATYPE_UNSPEC &&
+ proto != sah->saidx.proto)
continue;
/* map proto to satype */
if ((satype = key_proto2satype(sah->saidx.proto)) == 0) {
- SAHTREE_UNLOCK();
+ SAHTREE_RUNLOCK();
ipseclog((LOG_DEBUG, "%s: there was invalid proto in "
- "SAD.\n", __func__));
+ "SAD.\n", __func__));
return key_senderror(so, m, EINVAL);
}
-
- for (stateidx = 0;
- stateidx < _ARRAYLEN(saorder_state_any);
- stateidx++) {
- state = saorder_state_any[stateidx];
- LIST_FOREACH(sav, &sah->savtree[state], chain) {
- n = key_setdumpsa(sav, SADB_DUMP, satype,
- --cnt, mhp->msg->sadb_msg_pid);
- if (!n) {
- SAHTREE_UNLOCK();
- return key_senderror(so, m, ENOBUFS);
- }
- key_sendup_mbuf(so, n, KEY_SENDUP_ONE);
+ TAILQ_FOREACH(sav, &sah->savtree_larval, chain) {
+ n = key_setdumpsa(sav, SADB_DUMP, satype,
+ --cnt, mhp->msg->sadb_msg_pid);
+ if (n == NULL) {
+ SAHTREE_RUNLOCK();
+ return key_senderror(so, m, ENOBUFS);
+ }
+ key_sendup_mbuf(so, n, KEY_SENDUP_ONE);
+ }
+ TAILQ_FOREACH(sav, &sah->savtree_alive, chain) {
+ n = key_setdumpsa(sav, SADB_DUMP, satype,
+ --cnt, mhp->msg->sadb_msg_pid);
+ if (n == NULL) {
+ SAHTREE_RUNLOCK();
+ return key_senderror(so, m, ENOBUFS);
}
+ key_sendup_mbuf(so, n, KEY_SENDUP_ONE);
}
}
- SAHTREE_UNLOCK();
-
+ SAHTREE_RUNLOCK();
m_freem(m);
- return 0;
+ return (0);
}
-
/*
* SADB_X_PROMISC processing
*
@@ -7250,12 +7620,6 @@ key_parse(struct mbuf *m, struct socket *so)
IPSEC_ASSERT(so != NULL, ("null socket"));
IPSEC_ASSERT(m != NULL, ("null mbuf"));
-#if 0 /*kdebug_sadb assumes msg in linear buffer*/
- KEYDEBUG(KEYDEBUG_KEY_DUMP,
- ipseclog((LOG_DEBUG, "%s: passed sadb_msg\n", __func__));
- kdebug_sadb(msg));
-#endif
-
if (m->m_len < sizeof(struct sadb_msg)) {
m = m_pullup(m, sizeof(struct sadb_msg));
if (!m)
@@ -7321,64 +7685,79 @@ key_parse(struct mbuf *m, struct socket *so)
msg = mh.msg;
- /* check SA type */
- switch (msg->sadb_msg_satype) {
- case SADB_SATYPE_UNSPEC:
- switch (msg->sadb_msg_type) {
- case SADB_GETSPI:
- case SADB_UPDATE:
- case SADB_ADD:
- case SADB_DELETE:
- case SADB_GET:
- case SADB_ACQUIRE:
- case SADB_EXPIRE:
- ipseclog((LOG_DEBUG, "%s: must specify satype "
- "when msg type=%u.\n", __func__,
- msg->sadb_msg_type));
+ /* We use satype as scope mask for spddump */
+ if (msg->sadb_msg_type == SADB_X_SPDDUMP) {
+ switch (msg->sadb_msg_satype) {
+ case IPSEC_POLICYSCOPE_ANY:
+ case IPSEC_POLICYSCOPE_GLOBAL:
+ case IPSEC_POLICYSCOPE_IFNET:
+ case IPSEC_POLICYSCOPE_PCB:
+ break;
+ default:
+ ipseclog((LOG_DEBUG, "%s: illegal satype=%u\n",
+ __func__, msg->sadb_msg_type));
PFKEYSTAT_INC(out_invsatype);
error = EINVAL;
goto senderror;
}
- break;
- case SADB_SATYPE_AH:
- case SADB_SATYPE_ESP:
- case SADB_X_SATYPE_IPCOMP:
- case SADB_X_SATYPE_TCPSIGNATURE:
- switch (msg->sadb_msg_type) {
- case SADB_X_SPDADD:
- case SADB_X_SPDDELETE:
- case SADB_X_SPDGET:
- case SADB_X_SPDDUMP:
- case SADB_X_SPDFLUSH:
- case SADB_X_SPDSETIDX:
- case SADB_X_SPDUPDATE:
- case SADB_X_SPDDELETE2:
- ipseclog((LOG_DEBUG, "%s: illegal satype=%u\n",
- __func__, msg->sadb_msg_type));
+ } else {
+ switch (msg->sadb_msg_satype) { /* check SA type */
+ case SADB_SATYPE_UNSPEC:
+ switch (msg->sadb_msg_type) {
+ case SADB_GETSPI:
+ case SADB_UPDATE:
+ case SADB_ADD:
+ case SADB_DELETE:
+ case SADB_GET:
+ case SADB_ACQUIRE:
+ case SADB_EXPIRE:
+ ipseclog((LOG_DEBUG, "%s: must specify satype "
+ "when msg type=%u.\n", __func__,
+ msg->sadb_msg_type));
+ PFKEYSTAT_INC(out_invsatype);
+ error = EINVAL;
+ goto senderror;
+ }
+ break;
+ case SADB_SATYPE_AH:
+ case SADB_SATYPE_ESP:
+ case SADB_X_SATYPE_IPCOMP:
+ case SADB_X_SATYPE_TCPSIGNATURE:
+ switch (msg->sadb_msg_type) {
+ case SADB_X_SPDADD:
+ case SADB_X_SPDDELETE:
+ case SADB_X_SPDGET:
+ case SADB_X_SPDFLUSH:
+ case SADB_X_SPDSETIDX:
+ case SADB_X_SPDUPDATE:
+ case SADB_X_SPDDELETE2:
+ ipseclog((LOG_DEBUG, "%s: illegal satype=%u\n",
+ __func__, msg->sadb_msg_type));
+ PFKEYSTAT_INC(out_invsatype);
+ error = EINVAL;
+ goto senderror;
+ }
+ break;
+ case SADB_SATYPE_RSVP:
+ case SADB_SATYPE_OSPFV2:
+ case SADB_SATYPE_RIPV2:
+ case SADB_SATYPE_MIP:
+ ipseclog((LOG_DEBUG, "%s: type %u isn't supported.\n",
+ __func__, msg->sadb_msg_satype));
+ PFKEYSTAT_INC(out_invsatype);
+ error = EOPNOTSUPP;
+ goto senderror;
+ case 1: /* XXX: What does it do? */
+ if (msg->sadb_msg_type == SADB_X_PROMISC)
+ break;
+ /*FALLTHROUGH*/
+ default:
+ ipseclog((LOG_DEBUG, "%s: invalid type %u is passed.\n",
+ __func__, msg->sadb_msg_satype));
PFKEYSTAT_INC(out_invsatype);
error = EINVAL;
goto senderror;
}
- break;
- case SADB_SATYPE_RSVP:
- case SADB_SATYPE_OSPFV2:
- case SADB_SATYPE_RIPV2:
- case SADB_SATYPE_MIP:
- ipseclog((LOG_DEBUG, "%s: type %u isn't supported.\n",
- __func__, msg->sadb_msg_satype));
- PFKEYSTAT_INC(out_invsatype);
- error = EOPNOTSUPP;
- goto senderror;
- case 1: /* XXX: What does it do? */
- if (msg->sadb_msg_type == SADB_X_PROMISC)
- break;
- /*FALLTHROUGH*/
- default:
- ipseclog((LOG_DEBUG, "%s: invalid type %u is passed.\n",
- __func__, msg->sadb_msg_satype));
- PFKEYSTAT_INC(out_invsatype);
- error = EINVAL;
- goto senderror;
}
/* check field of upper layer protocol and address family */
@@ -7552,14 +7931,15 @@ key_align(struct mbuf *m, struct sadb_msghdr *mhp)
case SADB_EXT_SPIRANGE:
case SADB_X_EXT_POLICY:
case SADB_X_EXT_SA2:
-#ifdef IPSEC_NAT_T
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_OAI:
case SADB_X_EXT_NAT_T_OAR:
case SADB_X_EXT_NAT_T_FRAG:
-#endif
+ case SADB_X_EXT_SA_REPLAY:
+ case SADB_X_EXT_NEW_ADDRESS_SRC:
+ case SADB_X_EXT_NEW_ADDRESS_DST:
/* duplicate check */
/*
* XXX Are there duplication payloads of either
@@ -7635,6 +8015,10 @@ key_validate_ext(const struct sadb_ext *ext, int len)
case SADB_EXT_ADDRESS_SRC:
case SADB_EXT_ADDRESS_DST:
case SADB_EXT_ADDRESS_PROXY:
+ case SADB_X_EXT_NAT_T_OAI:
+ case SADB_X_EXT_NAT_T_OAR:
+ case SADB_X_EXT_NEW_ADDRESS_SRC:
+ case SADB_X_EXT_NEW_ADDRESS_DST:
baselen = PFKEY_ALIGN8(sizeof(struct sadb_address));
checktype = ADDR;
break;
@@ -7672,10 +8056,24 @@ key_init(void)
{
int i;
- for (i = 0; i < IPSEC_DIR_MAX; i++)
+ for (i = 0; i < IPSEC_DIR_MAX; i++) {
TAILQ_INIT(&V_sptree[i]);
+ TAILQ_INIT(&V_sptree_ifnet[i]);
+ }
+
+ V_key_lft_zone = uma_zcreate("IPsec SA lft_c",
+ sizeof(uint64_t) * 2, NULL, NULL, NULL, NULL,
+ UMA_ALIGN_PTR, UMA_ZONE_PCPU);
- LIST_INIT(&V_sahtree);
+ TAILQ_INIT(&V_sahtree);
+ V_sphashtbl = hashinit(SPHASH_NHASH, M_IPSEC_SP, &V_sphash_mask);
+ V_savhashtbl = hashinit(SAVHASH_NHASH, M_IPSEC_SA, &V_savhash_mask);
+ V_sahaddrhashtbl = hashinit(SAHHASH_NHASH, M_IPSEC_SAH,
+ &V_sahaddrhash_mask);
+ V_acqaddrhashtbl = hashinit(ACQHASH_NHASH, M_IPSEC_SAQ,
+ &V_acqaddrhash_mask);
+ V_acqseqhashtbl = hashinit(ACQHASH_NHASH, M_IPSEC_SAQ,
+ &V_acqseqhash_mask);
for (i = 0; i <= SADB_SATYPE_MAX; i++)
LIST_INIT(&V_regtree[i]);
@@ -7686,6 +8084,7 @@ key_init(void)
if (!IS_DEFAULT_VNET(curvnet))
return;
+ XFORMS_LOCK_INIT();
SPTREE_LOCK_INIT();
REGTREE_LOCK_INIT();
SAHTREE_LOCK_INIT();
@@ -7708,36 +8107,56 @@ key_init(void)
void
key_destroy(void)
{
- TAILQ_HEAD(, secpolicy) drainq;
+ struct secashead_queue sahdrainq;
+ struct secpolicy_queue drainq;
struct secpolicy *sp, *nextsp;
struct secacq *acq, *nextacq;
struct secspacq *spacq, *nextspacq;
- struct secashead *sah, *nextsah;
+ struct secashead *sah;
+ struct secasvar *sav;
struct secreg *reg;
int i;
+ /*
+ * XXX: can we just call free() for each object without
+ * walking through safe way with releasing references?
+ */
TAILQ_INIT(&drainq);
SPTREE_WLOCK();
for (i = 0; i < IPSEC_DIR_MAX; i++) {
TAILQ_CONCAT(&drainq, &V_sptree[i], chain);
+ TAILQ_CONCAT(&drainq, &V_sptree_ifnet[i], chain);
}
SPTREE_WUNLOCK();
sp = TAILQ_FIRST(&drainq);
while (sp != NULL) {
nextsp = TAILQ_NEXT(sp, chain);
- KEY_FREESP(&sp);
+ key_freesp(&sp);
sp = nextsp;
}
- SAHTREE_LOCK();
- for (sah = LIST_FIRST(&V_sahtree); sah != NULL; sah = nextsah) {
- nextsah = LIST_NEXT(sah, chain);
- if (__LIST_CHAINED(sah)) {
- LIST_REMOVE(sah, chain);
- free(sah, M_IPSEC_SAH);
+ TAILQ_INIT(&sahdrainq);
+ SAHTREE_WLOCK();
+ TAILQ_CONCAT(&sahdrainq, &V_sahtree, chain);
+ for (i = 0; i < V_savhash_mask + 1; i++)
+ LIST_INIT(&V_savhashtbl[i]);
+ for (i = 0; i < V_sahaddrhash_mask + 1; i++)
+ LIST_INIT(&V_sahaddrhashtbl[i]);
+ TAILQ_FOREACH(sah, &sahdrainq, chain) {
+ sah->state = SADB_SASTATE_DEAD;
+ TAILQ_FOREACH(sav, &sah->savtree_larval, chain) {
+ sav->state = SADB_SASTATE_DEAD;
+ }
+ TAILQ_FOREACH(sav, &sah->savtree_alive, chain) {
+ sav->state = SADB_SASTATE_DEAD;
}
}
- SAHTREE_UNLOCK();
+ SAHTREE_WUNLOCK();
+
+ key_freesah_flushed(&sahdrainq);
+ hashdestroy(V_sphashtbl, M_IPSEC_SP, V_sphash_mask);
+ hashdestroy(V_savhashtbl, M_IPSEC_SA, V_savhash_mask);
+ hashdestroy(V_sahaddrhashtbl, M_IPSEC_SAH, V_sahaddrhash_mask);
REGTREE_LOCK();
for (i = 0; i <= SADB_SATYPE_MAX; i++) {
@@ -7752,12 +8171,12 @@ key_destroy(void)
REGTREE_UNLOCK();
ACQ_LOCK();
- for (acq = LIST_FIRST(&V_acqtree); acq != NULL; acq = nextacq) {
+ acq = LIST_FIRST(&V_acqtree);
+ while (acq != NULL) {
nextacq = LIST_NEXT(acq, chain);
- if (__LIST_CHAINED(acq)) {
- LIST_REMOVE(acq, chain);
- free(acq, M_IPSEC_SAQ);
- }
+ LIST_REMOVE(acq, chain);
+ free(acq, M_IPSEC_SAQ);
+ acq = nextacq;
}
ACQ_UNLOCK();
@@ -7771,51 +8190,31 @@ key_destroy(void)
}
}
SPACQ_UNLOCK();
+ hashdestroy(V_acqaddrhashtbl, M_IPSEC_SAQ, V_acqaddrhash_mask);
+ hashdestroy(V_acqseqhashtbl, M_IPSEC_SAQ, V_acqseqhash_mask);
+ uma_zdestroy(V_key_lft_zone);
}
#endif
-/*
- * XXX: maybe This function is called after INBOUND IPsec processing.
- *
- * Special check for tunnel-mode packets.
- * We must make some checks for consistency between inner and outer IP header.
- *
- * xxx more checks to be provided
- */
-int
-key_checktunnelsanity(struct secasvar *sav, u_int family, caddr_t src,
- caddr_t dst)
-{
- IPSEC_ASSERT(sav->sah != NULL, ("null SA header"));
-
- /* XXX: check inner IP header */
-
- return 1;
-}
-
/* record data transfer on SA, and update timestamps */
void
key_sa_recordxfer(struct secasvar *sav, struct mbuf *m)
{
IPSEC_ASSERT(sav != NULL, ("Null secasvar"));
IPSEC_ASSERT(m != NULL, ("Null mbuf"));
- if (!sav->lft_c)
- return;
/*
* XXX Currently, there is a difference of bytes size
* between inbound and outbound processing.
*/
- sav->lft_c->bytes += m->m_pkthdr.len;
- /* to check bytes lifetime is done in key_timehandler(). */
+ counter_u64_add(sav->lft_c_bytes, m->m_pkthdr.len);
/*
* We use the number of packets as the unit of
* allocations. We increment the variable
* whenever {esp,ah}_{in,out}put is called.
*/
- sav->lft_c->allocations++;
- /* XXX check for expires? */
+ counter_u64_add(sav->lft_c_allocations, 1);
/*
* NOTE: We record CURRENT usetime by using wall clock,
@@ -7828,24 +8227,8 @@ key_sa_recordxfer(struct secasvar *sav, struct mbuf *m)
* <--------------> HARD
* <-----> SOFT
*/
- sav->lft_c->usetime = time_second;
- /* XXX check for expires? */
-
- return;
-}
-
-static void
-key_sa_chgstate(struct secasvar *sav, u_int8_t state)
-{
- IPSEC_ASSERT(sav != NULL, ("NULL sav"));
- SAHTREE_LOCK_ASSERT();
-
- if (sav->state != state) {
- if (__LIST_CHAINED(sav))
- LIST_REMOVE(sav, chain);
- sav->state = state;
- LIST_INSERT_HEAD(&sav->sah->savtree[state], sav, chain);
- }
+ if (sav->firstused == 0)
+ sav->firstused = time_second;
}
/*
@@ -7862,7 +8245,7 @@ key_sa_chgstate(struct secasvar *sav, u_int8_t state)
*/
static struct mbuf *
-key_setkey(struct seckey *src, u_int16_t exttype)
+key_setkey(struct seckey *src, uint16_t exttype)
{
struct mbuf *m;
struct sadb_key *p;
@@ -7902,7 +8285,7 @@ key_setkey(struct seckey *src, u_int16_t exttype)
*/
static struct mbuf *
-key_setlifetime(struct seclifetime *src, u_int16_t exttype)
+key_setlifetime(struct seclifetime *src, uint16_t exttype)
{
struct mbuf *m = NULL;
struct sadb_lifetime *p;
@@ -7929,3 +8312,104 @@ key_setlifetime(struct seclifetime *src, u_int16_t exttype)
return m;
}
+
+const struct enc_xform *
+enc_algorithm_lookup(int alg)
+{
+ int i;
+
+ for (i = 0; i < nitems(supported_ealgs); i++)
+ if (alg == supported_ealgs[i].sadb_alg)
+ return (supported_ealgs[i].xform);
+ return (NULL);
+}
+
+const struct auth_hash *
+auth_algorithm_lookup(int alg)
+{
+ int i;
+
+ for (i = 0; i < nitems(supported_aalgs); i++)
+ if (alg == supported_aalgs[i].sadb_alg)
+ return (supported_aalgs[i].xform);
+ return (NULL);
+}
+
+const struct comp_algo *
+comp_algorithm_lookup(int alg)
+{
+ int i;
+
+ for (i = 0; i < nitems(supported_calgs); i++)
+ if (alg == supported_calgs[i].sadb_alg)
+ return (supported_calgs[i].xform);
+ return (NULL);
+}
+
+/*
+ * Register a transform.
+ */
+static int
+xform_register(struct xformsw* xsp)
+{
+ struct xformsw *entry;
+
+ XFORMS_LOCK();
+ LIST_FOREACH(entry, &xforms, chain) {
+ if (entry->xf_type == xsp->xf_type) {
+ XFORMS_UNLOCK();
+ return (EEXIST);
+ }
+ }
+ LIST_INSERT_HEAD(&xforms, xsp, chain);
+ XFORMS_UNLOCK();
+ return (0);
+}
+
+void
+xform_attach(void *data)
+{
+ struct xformsw *xsp = (struct xformsw *)data;
+
+ if (xform_register(xsp) != 0)
+ printf("%s: failed to register %s xform\n", __func__,
+ xsp->xf_name);
+}
+
+void
+xform_detach(void *data)
+{
+ struct xformsw *xsp = (struct xformsw *)data;
+
+ XFORMS_LOCK();
+ LIST_REMOVE(xsp, chain);
+ XFORMS_UNLOCK();
+
+ /* Delete all SAs related to this xform. */
+ key_delete_xform(xsp);
+}
+
+/*
+ * Initialize transform support in an sav.
+ */
+static int
+xform_init(struct secasvar *sav, u_short xftype)
+{
+ struct xformsw *entry;
+ int ret;
+
+ IPSEC_ASSERT(sav->tdb_xform == NULL,
+ ("tdb_xform is already initialized"));
+
+ ret = EINVAL;
+ XFORMS_LOCK();
+ LIST_FOREACH(entry, &xforms, chain) {
+ if (entry->xf_type == xftype) {
+ ret = (*entry->xf_init)(sav, entry);
+ break;
+ }
+ }
+ XFORMS_UNLOCK();
+ return (ret);
+}
+
diff --git a/sys/netipsec/key.h b/sys/netipsec/key.h
index ad2b53d..a646832 100644
--- a/sys/netipsec/key.h
+++ b/sys/netipsec/key.h
@@ -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);
diff --git a/sys/netipsec/key_debug.c b/sys/netipsec/key_debug.c
index a3ff8be..d3c57a8 100644
--- a/sys/netipsec/key_debug.c
+++ b/sys/netipsec/key_debug.c
@@ -39,8 +39,10 @@
#include <sys/param.h>
#ifdef _KERNEL
#include <sys/systm.h>
+#include <sys/lock.h>
#include <sys/malloc.h>
#include <sys/mbuf.h>
+#include <sys/mutex.h>
#include <sys/queue.h>
#endif
#include <sys/socket.h>
@@ -54,6 +56,7 @@
#include <netipsec/ipsec.h>
#ifdef _KERNEL
#include <netipsec/keydb.h>
+#include <netipsec/xform.h>
#endif
#ifndef _KERNEL
@@ -456,134 +459,219 @@ kdebug_sadb_x_policy(struct sadb_ext *ext)
#ifdef _KERNEL
/* %%%: about SPD and SAD */
-void
-kdebug_secpolicy(struct secpolicy *sp)
+const char*
+kdebug_secpolicy_state(u_int state)
{
- /* sanity check */
- if (sp == NULL)
- panic("%s: NULL pointer was passed.\n", __func__);
- printf("secpolicy{ refcnt=%u policy=%u\n",
- sp->refcnt, sp->policy);
+ 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");
+}
- kdebug_secpolicyindex(&sp->spidx);
+const char*
+kdebug_secpolicy_policy(u_int policy)
+{
- switch (sp->policy) {
+ switch (policy) {
case IPSEC_POLICY_DISCARD:
- printf(" type=discard }\n");
- break;
+ return ("discard");
case IPSEC_POLICY_NONE:
- printf(" type=none }\n");
- break;
+ return ("none");
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);
- }
- printf(" }\n");
- }
- break;
- case IPSEC_POLICY_BYPASS:
- printf(" type=bypass }\n");
- break;
+ return ("ipsec");
case IPSEC_POLICY_ENTRUST:
- printf(" type=entrust }\n");
- break;
- default:
- printf("%s: Invalid policy found. %d\n", __func__, sp->policy);
- break;
+ return ("entrust");
+ case IPSEC_POLICY_BYPASS:
+ return ("bypass");
}
-
- return;
+ return ("unknown");
}
-void
-kdebug_secpolicyindex(struct secpolicyindex *spidx)
+const char*
+kdebug_secpolicyindex_dir(u_int dir)
{
- char buf[INET6_ADDRSTRLEN];
- /* sanity check */
- if (spidx == NULL)
- panic("%s: NULL pointer was passed.\n", __func__);
+ switch (dir) {
+ case IPSEC_DIR_ANY:
+ return ("any");
+ case IPSEC_DIR_INBOUND:
+ return ("in");
+ case IPSEC_DIR_OUTBOUND:
+ return ("out");
+ }
+ return ("unknown");
+}
- printf("secpolicyindex{ dir=%u prefs=%u prefd=%u ul_proto=%u\n",
- spidx->dir, spidx->prefs, spidx->prefd, spidx->ul_proto);
+const char*
+kdebug_ipsecrequest_level(u_int level)
+{
- printf("%s -> ", ipsec_address(&spidx->src, buf, sizeof(buf)));
- printf("%s }\n", ipsec_address(&spidx->dst, buf, sizeof(buf)));
+ 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");
}
-void
-kdebug_secasindex(struct secasindex *saidx)
+const char*
+kdebug_secasindex_mode(u_int mode)
{
- char buf[INET6_ADDRSTRLEN];
- /* sanity check */
- if (saidx == NULL)
- panic("%s: NULL pointer was passed.\n", __func__);
+ 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");
+}
- printf("secasindex{ mode=%u proto=%u\n",
- saidx->mode, saidx->proto);
+const char*
+kdebug_secasv_state(u_int state)
+{
- printf("%s -> ", ipsec_address(&saidx->src, buf, sizeof(buf)));
- printf("%s }\n", ipsec_address(&saidx->dst, buf, sizeof(buf)));
+ 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 void
-kdebug_sec_lifetime(struct seclifetime *lft)
+static char*
+kdebug_port2str(const struct sockaddr *sa, char *buf, size_t len)
{
- /* 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);
+ uint16_t port;
- return;
+ 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_secasv(struct secasvar *sav)
+kdebug_secpolicy(struct secpolicy *sp)
{
- /* sanity check */
- if (sav == NULL)
- panic("%s: NULL pointer was passed.\n", __func__);
+ u_int idx;
+
+ 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");
+ }
+ printf("}\n");
+}
- printf("secas{");
- kdebug_secasindex(&sav->sah->saidx);
+void
+kdebug_secpolicyindex(struct secpolicyindex *spidx, const char *indent)
+{
+ char buf[IPSEC_ADDRSTRLEN];
+
+ 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);
+}
- 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);
+void
+kdebug_secasindex(const struct secasindex *saidx, const char *indent)
+{
+ char buf[IPSEC_ADDRSTRLEN], port[6];
+
+ 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)));
+}
- 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);
+static void
+kdebug_sec_lifetime(struct seclifetime *lft, const char *indent)
+{
- if (sav->replay != NULL)
- kdebug_secreplay(sav->replay);
- 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);
+ 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);
+}
-#ifdef notyet
- /* XXX: misc[123] ? */
-#endif
+void
+kdebug_secash(struct secashead *sah, const char *indent)
+{
- 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
@@ -591,27 +679,80 @@ kdebug_secreplay(struct secreplay *rpl)
{
int len, l;
- /* sanity check */
- if (rpl == NULL)
- panic("%s: NULL pointer was passed.\n", __func__);
-
- printf(" secreplay{ count=%u wsize=%u seq=%u lastseq=%u",
- rpl->count, rpl->wsize, rpl->seq, rpl->lastseq);
+ 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 { ");
-
- for (len = 0; len < rpl->wsize; len++) {
+ 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
@@ -663,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
diff --git a/sys/netipsec/key_debug.h b/sys/netipsec/key_debug.h
index 09576bf..18150b5 100644
--- a/sys/netipsec/key_debug.h
+++ b/sys/netipsec/key_debug.h
@@ -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;
diff --git a/sys/netipsec/keydb.h b/sys/netipsec/keydb.h
index 3fe28eb..ffbbe33 100644
--- a/sys/netipsec/keydb.h
+++ b/sys/netipsec/keydb.h
@@ -34,6 +34,9 @@
#define _NETIPSEC_KEYDB_H_
#ifdef _KERNEL
+#include <sys/counter.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
#include <netipsec/key_var.h>
@@ -54,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. */
};
@@ -85,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;
@@ -95,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;
@@ -106,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 seclifetime *lft_c; /* CURRENT lifetime, it's constant. */
+ struct secnatt *natt; /* NAT-T config */
+ struct mtx *lock; /* update/access lock */
+
+ 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 || \
@@ -170,14 +194,18 @@ struct secasvar {
#define SAV_ISCTR(_sav) ((_sav)->alg_enc == SADB_X_EALG_AESCTR)
#define SAV_ISCTRORGCM(_sav) (SAV_ISCTR((_sav)) || SAV_ISGCM((_sav)))
-/* replay prevention */
+/* Replay prevention, protected by SECASVAR_LOCK:
+ * (m) locked by mtx
+ * (c) read only except during creation / free
+ */
struct secreplay {
- u_int32_t count;
- u_int wsize; /* window size, i.g. 4 bytes */
- u_int32_t seq; /* used by sender */
- u_int32_t lastseq; /* used by receiver */
- caddr_t bitmap; /* used by receiver */
- int overflow; /* overflow flag */
+ u_int32_t count; /* (m) */
+ u_int wsize; /* (c) window size, i.g. 4 bytes */
+ u_int32_t seq; /* (m) used by sender */
+ u_int32_t lastseq; /* (m) used by receiver */
+ u_int32_t *bitmap; /* (m) used by receiver */
+ u_int bitmap_size; /* (c) size of the bitmap array */
+ int overflow; /* (m) overflow flag */
};
/* socket table due to send PF_KEY messages. */
@@ -190,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 */
};
diff --git a/sys/netipsec/keysock.c b/sys/netipsec/keysock.c
index c3092e4..7bab8a7 100644
--- a/sys/netipsec/keysock.c
+++ b/sys/netipsec/keysock.c
@@ -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
diff --git a/sys/netipsec/subr_ipsec.c b/sys/netipsec/subr_ipsec.c
new file mode 100644
index 0000000..acfe66a
--- /dev/null
+++ b/sys/netipsec/subr_ipsec.c
@@ -0,0 +1,354 @@
+/*-
+ * 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
+/*
+ * IPSEC_SUPPORT - loading of ipsec.ko and tcpmd5.ko is supported.
+ * IPSEC + IPSEC_SUPPORT - loading tcpmd5.ko is supported.
+ * IPSEC + TCP_SIGNATURE - all is build in the kernel, do not build
+ * IPSEC_SUPPORT.
+ */
+#if !defined(IPSEC) || !defined(TCP_SIGNATURE)
+#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); \
+}
+
+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);
+#endif /* !IPSEC || !TCP_SIGNATURE */
+
+#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 /* !TCP_SIGNATURE */
+
+#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 */
diff --git a/sys/netipsec/udpencap.c b/sys/netipsec/udpencap.c
new file mode 100644
index 0000000..08795a3
--- /dev/null
+++ b/sys/netipsec/udpencap.c
@@ -0,0 +1,297 @@
+/*-
+ * 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);
+ /* Do not adjust UDP checksum if it is zero. */
+ if (proto == IPPROTO_UDP && cksum == 0)
+ return;
+ 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;
+ }
+}
+
diff --git a/sys/netipsec/xform.h b/sys/netipsec/xform.h
index fee457b..8e6f8bd 100644
--- a/sys/netipsec/xform.h
+++ b/sys/netipsec/xform.h
@@ -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);
-struct cryptoini;
+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_ */
diff --git a/sys/netipsec/xform_ah.c b/sys/netipsec/xform_ah.c
index 0d39eeb..e5f8031 100644
--- a/sys/netipsec/xform_ah.c
+++ b/sys/netipsec/xform_ah.c
@@ -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,14 +923,16 @@ 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) {
if (sav->replay->count == ~0 &&
(sav->flags & SADB_X_EXT_CYCSEQ) == 0) {
+ SECASVAR_UNLOCK(sav);
DPRINTF(("%s: replay counter wrapped for SA %s/%08lx\n",
__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
@@ -977,6 +942,8 @@ ah_output(struct mbuf *m, struct ipsecrequest *isr, struct mbuf **mp,
sav->replay->count++;
ah->ah_seq = htonl(sav->replay->count);
}
+ cryptoid = sav->tdb_cryptoid;
+ SECASVAR_UNLOCK(sav);
/* Get crypto descriptors. */
crp = crypto_getreq(1);
@@ -989,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;
@@ -1000,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
@@ -1021,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);
@@ -1032,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;
}
@@ -1064,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:
@@ -1091,45 +1053,37 @@ bad:
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;
}
@@ -1140,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) {
@@ -1167,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);
diff --git a/sys/netipsec/xform_esp.c b/sys/netipsec/xform_esp.c
index c8b5f16..3c5ca6b 100644
--- a/sys/netipsec/xform_esp.c
+++ b/sys/netipsec/xform_esp.c
@@ -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;
+ 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;
- u_int8_t prot;
+ uint8_t *ivp;
+ uint64_t cntr, cryptoid;
+ int hlen, rlen, padding, blks, alen, i, roff;
int error, maxpacketsize;
+ uint8_t prot;
- struct cryptodesc *crde = NULL, *crda = NULL;
- struct cryptop *crp;
-
- 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,15 +712,17 @@ 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;
#ifdef REGRESSION
/* Emulate replay attack when ipsec_replay is TRUE. */
@@ -768,10 +730,14 @@ esp_output(struct mbuf *m, struct ipsecrequest *isr, struct mbuf **mp,
#endif
sav->replay->count++;
replay = htonl(sav->replay->count);
- 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,
@@ -823,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;
@@ -853,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);
}
@@ -868,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. */
@@ -901,53 +860,40 @@ bad:
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;
}
@@ -958,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
@@ -988,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);
diff --git a/sys/netipsec/xform_ipcomp.c b/sys/netipsec/xform_ipcomp.c
index 066f048..eb68f2a 100644
--- a/sys/netipsec/xform_ipcomp.c
+++ b/sys/netipsec/xform_ipcomp.c
@@ -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 @@ bad:
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);
diff --git a/sys/netipsec/xform_tcp.c b/sys/netipsec/xform_tcp.c
index 6a55ed2..a05ae9d 100644
--- a/sys/netipsec/xform_tcp.c
+++ b/sys/netipsec/xform_tcp.c
@@ -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,256 @@
#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 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 +336,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 +350,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)
-{
+static struct xformsw tcpsignature_xformsw = {
+ .xf_type = XF_TCPSIGNATURE,
+ .xf_name = "TCP-MD5",
+ .xf_init = tcpsignature_init,
+ .xf_zeroize = tcpsignature_zeroize,
+};
- return (0);
-}
+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 */
-/*
- * 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)
+tcpmd5_modevent(module_t mod, int type, void *data)
{
- return (EINVAL);
+ 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);
}
-static struct xformsw tcpsignature_xformsw = {
- XF_TCPSIGNATURE, XFT_AUTH, "TCPMD5",
- tcpsignature_init, tcpsignature_zeroize,
- tcpsignature_input, tcpsignature_output
+static moduledata_t tcpmd5_mod = {
+ "tcpmd5",
+ tcpmd5_modevent,
+ 0
};
-static void
-tcpsignature_attach(void)
-{
-
- xform_register(&tcpsignature_xformsw);
-}
-
-SYSINIT(tcpsignature_xform_init, SI_SUB_PROTO_DOMAIN, SI_ORDER_MIDDLE,
- tcpsignature_attach, NULL);
+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
diff --git a/usr.bin/netstat/inet.c b/usr.bin/netstat/inet.c
index 71dc9f6..bf995d2 100644
--- a/usr.bin/netstat/inet.c
+++ b/usr.bin/netstat/inet.c
@@ -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");
OpenPOWER on IntegriCloud