summaryrefslogtreecommitdiffstats
path: root/sys/netipsec
diff options
context:
space:
mode:
Diffstat (limited to 'sys/netipsec')
-rw-r--r--sys/netipsec/esp.h6
-rw-r--r--sys/netipsec/ipip_var.h71
-rw-r--r--sys/netipsec/ipsec.c195
-rw-r--r--sys/netipsec/ipsec.h68
-rw-r--r--sys/netipsec/ipsec6.h4
-rw-r--r--sys/netipsec/ipsec_input.c118
-rw-r--r--sys/netipsec/ipsec_output.c328
-rw-r--r--sys/netipsec/key.c455
-rw-r--r--sys/netipsec/key.h5
-rw-r--r--sys/netipsec/key_debug.c41
-rw-r--r--sys/netipsec/key_debug.h4
-rw-r--r--sys/netipsec/keydb.h8
-rw-r--r--sys/netipsec/xform.h9
-rw-r--r--sys/netipsec/xform_ah.c214
-rw-r--r--sys/netipsec/xform_esp.c395
-rw-r--r--sys/netipsec/xform_ipcomp.c209
-rw-r--r--sys/netipsec/xform_ipip.c662
17 files changed, 1072 insertions, 1720 deletions
diff --git a/sys/netipsec/esp.h b/sys/netipsec/esp.h
index eb37397..8eb0963 100644
--- a/sys/netipsec/esp.h
+++ b/sys/netipsec/esp.h
@@ -42,8 +42,7 @@ struct esp {
/*variable size, 32bit bound*/ /* Initialization Vector */
/*variable size*/ /* Payload data */
/*variable size*/ /* padding */
- /*8bit*/ /* pad size */
- /*8bit*/ /* next header */
+ /*8bit*/ /* pad length */
/*8bit*/ /* next header */
/*variable size, 32bit bound*/ /* Authentication data (new IPsec) */
};
@@ -53,8 +52,7 @@ struct newesp {
u_int32_t esp_seq; /* Sequence number */
/*variable size*/ /* (IV and) Payload data */
/*variable size*/ /* padding */
- /*8bit*/ /* pad size */
- /*8bit*/ /* next header */
+ /*8bit*/ /* pad length */
/*8bit*/ /* next header */
/*variable size, 32bit bound*/ /* Authentication data */
};
diff --git a/sys/netipsec/ipip_var.h b/sys/netipsec/ipip_var.h
deleted file mode 100644
index 02420c1..0000000
--- a/sys/netipsec/ipip_var.h
+++ /dev/null
@@ -1,71 +0,0 @@
-/* $FreeBSD$ */
-/* $OpenBSD: ip_ipip.h,v 1.5 2002/06/09 16:26:10 itojun Exp $ */
-/*-
- * The authors of this code are John Ioannidis (ji@tla.org),
- * Angelos D. Keromytis (kermit@csd.uch.gr) and
- * Niels Provos (provos@physnet.uni-hamburg.de).
- *
- * The original version of this code was written by John Ioannidis
- * for BSD/OS in Athens, Greece, in November 1995.
- *
- * Ported to OpenBSD and NetBSD, with additional transforms, in December 1996,
- * by Angelos D. Keromytis.
- *
- * Additional transforms and features in 1997 and 1998 by Angelos D. Keromytis
- * and Niels Provos.
- *
- * Additional features in 1999 by Angelos D. Keromytis.
- *
- * Copyright (C) 1995, 1996, 1997, 1998, 1999 by John Ioannidis,
- * Angelos D. Keromytis and Niels Provos.
- * Copyright (c) 2001, Angelos D. Keromytis.
- *
- * Permission to use, copy, and modify this software with or without fee
- * is hereby granted, provided that this entire notice is included in
- * all copies of any software which is or includes a copy or
- * modification of this software.
- * You may use this code under the GNU public license if you so wish. Please
- * contribute changes back to the authors under this freer than GPL license
- * so that we may further the use of strong encryption without limitations to
- * all.
- *
- * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR
- * IMPLIED WARRANTY. IN PARTICULAR, NONE OF THE AUTHORS MAKES ANY
- * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE
- * MERCHANTABILITY OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR
- * PURPOSE.
- */
-
-#ifndef _NETINET_IPIP_H_
-#define _NETINET_IPIP_H_
-
-/*
- * IP-inside-IP processing.
- * Not quite all the functionality of RFC-1853, but the main idea is there.
- */
-
-struct ipipstat {
- uint64_t ipips_ipackets; /* total input packets */
- uint64_t ipips_opackets; /* total output packets */
- uint64_t ipips_hdrops; /* packet shorter than header shows */
- uint64_t ipips_qfull;
- uint64_t ipips_ibytes;
- uint64_t ipips_obytes;
- uint64_t ipips_pdrops; /* packet dropped due to policy */
- uint64_t ipips_spoof; /* IP spoofing attempts */
- uint64_t ipips_family; /* Protocol family mismatch */
- uint64_t ipips_unspec; /* Missing tunnel endpoint address */
-};
-
-#ifdef _KERNEL
-#include <sys/counter.h>
-
-VNET_DECLARE(int, ipip_allow);
-VNET_PCPUSTAT_DECLARE(struct ipipstat, ipipstat);
-
-#define IPIPSTAT_ADD(name, val) \
- VNET_PCPUSTAT_ADD(struct ipipstat, ipipstat, name, (val))
-#define IPIPSTAT_INC(name) IPIPSTAT_ADD(name, 1)
-#define V_ipip_allow VNET(ipip_allow)
-#endif /* _KERNEL */
-#endif /* _NETINET_IPIP_H_ */
diff --git a/sys/netipsec/ipsec.c b/sys/netipsec/ipsec.c
index 93e37e8..0ee3d1a 100644
--- a/sys/netipsec/ipsec.c
+++ b/sys/netipsec/ipsec.c
@@ -117,11 +117,12 @@ VNET_DEFINE(int, ip4_esp_trans_deflev) = IPSEC_LEVEL_USE;
VNET_DEFINE(int, ip4_esp_net_deflev) = IPSEC_LEVEL_USE;
VNET_DEFINE(int, ip4_ah_trans_deflev) = IPSEC_LEVEL_USE;
VNET_DEFINE(int, ip4_ah_net_deflev) = IPSEC_LEVEL_USE;
-VNET_DEFINE(struct secpolicy, ip4_def_policy);
/* ECN ignore(-1)/forbidden(0)/allowed(1) */
VNET_DEFINE(int, ip4_ipsec_ecn) = 0;
VNET_DEFINE(int, ip4_esp_randpad) = -1;
+static VNET_DEFINE(struct secpolicy, def_policy);
+#define V_def_policy VNET(def_policy)
/*
* Crypto support requirements:
*
@@ -140,7 +141,7 @@ SYSCTL_DECL(_net_inet_ipsec);
/* net.inet.ipsec */
SYSCTL_VNET_INT(_net_inet_ipsec, IPSECCTL_DEF_POLICY, def_policy,
- CTLFLAG_RW, &VNET_NAME(ip4_def_policy).policy, 0,
+ CTLFLAG_RW, &VNET_NAME(def_policy).policy, 0,
"IPsec default policy.");
SYSCTL_VNET_INT(_net_inet_ipsec, IPSECCTL_DEF_ESP_TRANSLEV, esp_trans_deflev,
CTLFLAG_RW, &VNET_NAME(ip4_esp_trans_deflev), 0,
@@ -212,7 +213,7 @@ SYSCTL_DECL(_net_inet6_ipsec6);
/* net.inet6.ipsec6 */
SYSCTL_VNET_INT(_net_inet6_ipsec6, IPSECCTL_DEF_POLICY, def_policy, CTLFLAG_RW,
- &VNET_NAME(ip4_def_policy).policy, 0,
+ &VNET_NAME(def_policy).policy, 0,
"IPsec default policy.");
SYSCTL_VNET_INT(_net_inet6_ipsec6, IPSECCTL_DEF_ESP_TRANSLEV,
esp_trans_deflev, CTLFLAG_RW, &VNET_NAME(ip6_esp_trans_deflev), 0,
@@ -236,13 +237,14 @@ SYSCTL_VNET_PCPUSTAT(_net_inet6_ipsec6, IPSECCTL_STATS, ipsecstats,
struct ipsecstat, ipsec6stat, "IPsec IPv6 statistics.");
#endif /* INET6 */
-static int ipsec_setspidx_inpcb(struct mbuf *, struct inpcb *);
-static int ipsec_setspidx(struct mbuf *, struct secpolicyindex *, int);
-static void ipsec4_get_ulp(struct mbuf *m, struct secpolicyindex *, int);
-static int ipsec4_setspidx_ipaddr(struct mbuf *, struct secpolicyindex *);
+static int ipsec_in_reject(struct secpolicy *, const struct mbuf *);
+static int ipsec_setspidx_inpcb(const struct mbuf *, struct inpcb *);
+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 *);
#ifdef INET6
-static void ipsec6_get_ulp(struct mbuf *m, struct secpolicyindex *, int);
-static int ipsec6_setspidx_ipaddr(struct mbuf *, struct secpolicyindex *);
+static void ipsec6_get_ulp(const struct mbuf *m, struct secpolicyindex *, int);
+static int ipsec6_setspidx_ipaddr(const struct mbuf *, struct secpolicyindex *);
#endif
static void ipsec_delpcbpolicy(struct inpcbpolicy *);
static struct secpolicy *ipsec_deepcopy_policy(struct secpolicy *src);
@@ -261,7 +263,7 @@ key_allocsp_default(const char* where, int tag)
KEYDEBUG(KEYDEBUG_IPSEC_STAMP,
printf("DP key_allocsp_default from %s:%u\n", where, tag));
- sp = &V_ip4_def_policy;
+ 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",
@@ -319,7 +321,8 @@ ipsec_getpolicy(struct tdb_ident *tdbi, u_int dir)
* NOTE: IPv6 mapped adddress concern is implemented here.
*/
static struct secpolicy *
-ipsec_getpolicybysock(struct mbuf *m, u_int dir, struct inpcb *inp, int *error)
+ipsec_getpolicybysock(const struct mbuf *m, u_int dir, struct inpcb *inp,
+ int *error)
{
struct inpcbpolicy *pcbsp;
struct secpolicy *currsp = NULL; /* Policy on socket. */
@@ -331,6 +334,12 @@ ipsec_getpolicybysock(struct mbuf *m, u_int dir, struct inpcb *inp, int *error)
IPSEC_ASSERT(dir == IPSEC_DIR_INBOUND || dir == IPSEC_DIR_OUTBOUND,
("invalid direction %u", dir));
+ if (!key_havesp(dir)) {
+ /* No SP found, use system default. */
+ sp = KEY_ALLOCSP_DEFAULT();
+ return (sp);
+ }
+
/* Set spidx in pcb. */
*error = ipsec_setspidx_inpcb(m, inp);
if (*error)
@@ -416,7 +425,7 @@ ipsec_getpolicybysock(struct mbuf *m, u_int dir, struct inpcb *inp, int *error)
* others : error occured.
*/
struct secpolicy *
-ipsec_getpolicybyaddr(struct mbuf *m, u_int dir, int flag, int *error)
+ipsec_getpolicybyaddr(const struct mbuf *m, u_int dir, int *error)
{
struct secpolicyindex spidx;
struct secpolicy *sp;
@@ -427,17 +436,16 @@ ipsec_getpolicybyaddr(struct mbuf *m, u_int dir, int flag, int *error)
("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,
- (flag & IP_FORWARDING) ? 0 : 1);
+ *error = ipsec_setspidx(m, &spidx, 0);
if (*error != 0) {
- DPRINTF(("%s: setpidx failed, dir %u flag %u\n",
- __func__, dir, flag));
+ 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. */
@@ -447,14 +455,14 @@ ipsec_getpolicybyaddr(struct mbuf *m, u_int dir, int flag, int *error)
}
struct secpolicy *
-ipsec4_checkpolicy(struct mbuf *m, u_int dir, u_int flag, int *error,
+ipsec4_checkpolicy(const struct mbuf *m, u_int dir, int *error,
struct inpcb *inp)
{
struct secpolicy *sp;
*error = 0;
if (inp == NULL)
- sp = ipsec_getpolicybyaddr(m, dir, flag, error);
+ sp = ipsec_getpolicybyaddr(m, dir, error);
else
sp = ipsec_getpolicybysock(m, dir, inp, error);
if (sp == NULL) {
@@ -490,7 +498,7 @@ ipsec4_checkpolicy(struct mbuf *m, u_int dir, u_int flag, int *error,
}
static int
-ipsec_setspidx_inpcb(struct mbuf *m, struct inpcb *inp)
+ipsec_setspidx_inpcb(const struct mbuf *m, struct inpcb *inp)
{
int error;
@@ -519,12 +527,13 @@ ipsec_setspidx_inpcb(struct mbuf *m, struct inpcb *inp)
* The caller is responsible for error recovery (like clearing up spidx).
*/
static int
-ipsec_setspidx(struct mbuf *m, struct secpolicyindex *spidx, int needport)
+ipsec_setspidx(const struct mbuf *m, struct secpolicyindex *spidx,
+ int needport)
{
- struct ip *ip = NULL;
struct ip ipbuf;
+ const struct ip *ip = NULL;
+ const struct mbuf *n;
u_int v;
- struct mbuf *n;
int len;
int error;
@@ -553,7 +562,7 @@ ipsec_setspidx(struct mbuf *m, struct secpolicyindex *spidx, int needport)
}
if (m->m_len >= sizeof(*ip))
- ip = mtod(m, struct ip *);
+ ip = mtod(m, const struct ip *);
else {
m_copydata(m, 0, sizeof(ipbuf), (caddr_t)&ipbuf);
ip = &ipbuf;
@@ -589,7 +598,8 @@ ipsec_setspidx(struct mbuf *m, struct secpolicyindex *spidx, int needport)
}
static void
-ipsec4_get_ulp(struct mbuf *m, struct secpolicyindex *spidx, int needport)
+ipsec4_get_ulp(const struct mbuf *m, struct secpolicyindex *spidx,
+ int needport)
{
u_int8_t nxt;
int off;
@@ -599,7 +609,7 @@ ipsec4_get_ulp(struct mbuf *m, struct secpolicyindex *spidx, int needport)
IPSEC_ASSERT(m->m_pkthdr.len >= sizeof(struct ip),("packet too short"));
if (m->m_len >= sizeof (struct ip)) {
- struct ip *ip = mtod(m, struct ip *);
+ const struct ip *ip = mtod(m, const struct ip *);
if (ip->ip_off & htons(IP_MF | IP_OFFMASK))
goto done;
off = ip->ip_hl << 2;
@@ -664,7 +674,7 @@ done_proto:
/* Assumes that m is sane. */
static int
-ipsec4_setspidx_ipaddr(struct mbuf *m, struct secpolicyindex *spidx)
+ipsec4_setspidx_ipaddr(const struct mbuf *m, struct secpolicyindex *spidx)
{
static const struct sockaddr_in template = {
sizeof (struct sockaddr_in),
@@ -683,7 +693,7 @@ ipsec4_setspidx_ipaddr(struct mbuf *m, struct secpolicyindex *spidx)
sizeof (struct in_addr),
(caddr_t) &spidx->dst.sin.sin_addr);
} else {
- struct ip *ip = mtod(m, struct ip *);
+ 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;
}
@@ -696,7 +706,8 @@ ipsec4_setspidx_ipaddr(struct mbuf *m, struct secpolicyindex *spidx)
#ifdef INET6
static void
-ipsec6_get_ulp(struct mbuf *m, struct secpolicyindex *spidx, int needport)
+ipsec6_get_ulp(const struct mbuf *m, struct secpolicyindex *spidx,
+ int needport)
{
int off, nxt;
struct tcphdr th;
@@ -760,14 +771,14 @@ ipsec6_get_ulp(struct mbuf *m, struct secpolicyindex *spidx, int needport)
/* Assumes that m is sane. */
static int
-ipsec6_setspidx_ipaddr(struct mbuf *m, struct secpolicyindex *spidx)
+ipsec6_setspidx_ipaddr(const struct mbuf *m, struct secpolicyindex *spidx)
{
- struct ip6_hdr *ip6 = NULL;
struct ip6_hdr ip6buf;
+ const struct ip6_hdr *ip6 = NULL;
struct sockaddr_in6 *sin6;
if (m->m_len >= sizeof(*ip6))
- ip6 = mtod(m, struct ip6_hdr *);
+ ip6 = mtod(m, const struct ip6_hdr *);
else {
m_copydata(m, 0, sizeof(ip6buf), (caddr_t)&ip6buf);
ip6 = &ip6buf;
@@ -829,17 +840,13 @@ ipsec_init_policy(struct socket *so, struct inpcbpolicy **pcb_sp)
ipsec_delpcbpolicy(new);
return (ENOBUFS);
}
- new->sp_in->state = IPSEC_SPSTATE_ALIVE;
new->sp_in->policy = IPSEC_POLICY_ENTRUST;
-
if ((new->sp_out = KEY_NEWSP()) == NULL) {
KEY_FREESP(&new->sp_in);
ipsec_delpcbpolicy(new);
return (ENOBUFS);
}
- new->sp_out->state = IPSEC_SPSTATE_ALIVE;
new->sp_out->policy = IPSEC_POLICY_ENTRUST;
-
*pcb_sp = new;
return (0);
@@ -928,7 +935,6 @@ ipsec_deepcopy_policy(struct secpolicy *src)
}
dst->req = newchain;
- dst->state = src->state;
dst->policy = src->policy;
/* Do not touch the refcnt fields. */
@@ -940,6 +946,7 @@ fail:
ipsec_delisr(p);
p = NULL;
}
+ KEY_FREESP(&dst);
return (NULL);
}
@@ -980,8 +987,6 @@ ipsec_set_policy_internal(struct secpolicy **pcb_sp, int optname,
if ((newsp = key_msg2sp(xpl, len, &error)) == NULL)
return (error);
- newsp->state = IPSEC_SPSTATE_ALIVE;
-
/* Clear old SP and set new SP. */
KEY_FREESP(pcb_sp);
*pcb_sp = newsp;
@@ -1198,8 +1203,8 @@ ipsec_get_reqlevel(struct ipsecrequest *isr)
* 0: valid
* 1: invalid
*/
-int
-ipsec_in_reject(struct secpolicy *sp, struct mbuf *m)
+static int
+ipsec_in_reject(struct secpolicy *sp, const struct mbuf *m)
{
struct ipsecrequest *isr;
int need_auth;
@@ -1266,8 +1271,11 @@ ipsec_in_reject(struct secpolicy *sp, struct mbuf *m)
return (0); /* Valid. */
}
+/*
+ * Non zero return value means security policy DISCARD or policy violation.
+ */
static int
-ipsec46_in_reject(struct mbuf *m, struct inpcb *inp)
+ipsec46_in_reject(const struct mbuf *m, struct inpcb *inp)
{
struct secpolicy *sp;
int error;
@@ -1278,13 +1286,9 @@ ipsec46_in_reject(struct mbuf *m, struct inpcb *inp)
IPSEC_ASSERT(m != NULL, ("null mbuf"));
- /*
- * Get SP for this packet.
- * When we are called from ip_forward(), we call
- * ipsec_getpolicybyaddr() with IP_FORWARDING flag.
- */
+ /* Get SP for this packet. */
if (inp == NULL)
- sp = ipsec_getpolicybyaddr(m, IPSEC_DIR_INBOUND, IP_FORWARDING, &error);
+ sp = ipsec_getpolicybyaddr(m, IPSEC_DIR_INBOUND, &error);
else
sp = ipsec_getpolicybysock(m, IPSEC_DIR_INBOUND, inp, &error);
@@ -1292,8 +1296,7 @@ ipsec46_in_reject(struct mbuf *m, struct inpcb *inp)
result = ipsec_in_reject(sp, m);
KEY_FREESP(&sp);
} else {
- result = 0; /* XXX Should be panic?
- * -> No, there may be error. */
+ result = 1; /* treat errors as policy violation */
}
return (result);
}
@@ -1304,7 +1307,7 @@ ipsec46_in_reject(struct mbuf *m, struct inpcb *inp)
* and {ah,esp}4_input for tunnel mode.
*/
int
-ipsec4_in_reject(struct mbuf *m, struct inpcb *inp)
+ipsec4_in_reject(const struct mbuf *m, struct inpcb *inp)
{
int result;
@@ -1322,7 +1325,7 @@ ipsec4_in_reject(struct mbuf *m, struct inpcb *inp)
* and {ah,esp}6_input for tunnel mode.
*/
int
-ipsec6_in_reject(struct mbuf *m, struct inpcb *inp)
+ipsec6_in_reject(const struct mbuf *m, struct inpcb *inp)
{
int result;
@@ -1402,7 +1405,7 @@ ipsec_hdrsiz_internal(struct secpolicy *sp)
* disabled ip6_ipsec_mtu() and ip6_forward().
*/
size_t
-ipsec_hdrsiz(struct mbuf *m, u_int dir, struct inpcb *inp)
+ipsec_hdrsiz(const struct mbuf *m, u_int dir, struct inpcb *inp)
{
struct secpolicy *sp;
int error;
@@ -1413,12 +1416,9 @@ ipsec_hdrsiz(struct mbuf *m, u_int dir, struct inpcb *inp)
IPSEC_ASSERT(m != NULL, ("null mbuf"));
- /* Get SP for this packet.
- * When we are called from ip_forward(), we call
- * ipsec_getpolicybyaddr() with IP_FORWARDING flag.
- */
+ /* Get SP for this packet. */
if (inp == NULL)
- sp = ipsec_getpolicybyaddr(m, dir, IP_FORWARDING, &error);
+ sp = ipsec_getpolicybyaddr(m, dir, &error);
else
sp = ipsec_getpolicybysock(m, dir, inp, &error);
@@ -1506,6 +1506,7 @@ ipsec_chkreplay(u_int32_t seq, struct secasvar *sav)
int
ipsec_updatereplay(u_int32_t seq, struct secasvar *sav)
{
+ char buf[128];
struct secreplay *replay;
u_int32_t diff;
int fr;
@@ -1585,7 +1586,8 @@ ok:
return (1);
ipseclog((LOG_WARNING, "%s: replay counter made %d cycle. %s\n",
- __func__, replay->overflow, ipsec_logsastr(sav)));
+ __func__, replay->overflow,
+ ipsec_logsastr(sav, buf, sizeof(buf))));
}
replay->count++;
@@ -1616,81 +1618,51 @@ vshiftl(unsigned char *bitmap, int nbit, int wsize)
}
}
-#ifdef INET
-/* Return a printable string for the IPv4 address. */
-static char *
-inet_ntoa4(struct in_addr ina)
-{
- static char buf[4][4 * sizeof "123" + 4];
- unsigned char *ucp = (unsigned char *) &ina;
- static int i = 3;
-
- /* XXX-BZ Returns static buffer. */
- i = (i + 1) % 4;
- sprintf(buf[i], "%d.%d.%d.%d", ucp[0] & 0xff, ucp[1] & 0xff,
- ucp[2] & 0xff, ucp[3] & 0xff);
- return (buf[i]);
-}
-#endif
-
/* Return a printable string for the address. */
-char *
-ipsec_address(union sockaddr_union* sa)
+char*
+ipsec_address(union sockaddr_union* sa, char *buf, socklen_t size)
{
-#ifdef INET6
- char ip6buf[INET6_ADDRSTRLEN];
-#endif
switch (sa->sa.sa_family) {
#ifdef INET
case AF_INET:
- return (inet_ntoa4(sa->sin.sin_addr));
+ return (inet_ntop(AF_INET, &sa->sin.sin_addr, buf, size));
#endif /* INET */
#ifdef INET6
case AF_INET6:
- return (ip6_sprintf(ip6buf, &sa->sin6.sin6_addr));
+ return (inet_ntop(AF_INET6, &sa->sin6.sin6_addr, buf, size));
#endif /* INET6 */
default:
return ("(unknown address family)");
}
}
-const char *
-ipsec_logsastr(struct secasvar *sav)
+char *
+ipsec_logsastr(struct secasvar *sav, char *buf, size_t size)
{
- static char buf[256];
- char *p;
- struct secasindex *saidx = &sav->sah->saidx;
-
- IPSEC_ASSERT(saidx->src.sa.sa_family == saidx->dst.sa.sa_family,
- ("address family mismatch"));
-
- p = buf;
- snprintf(buf, sizeof(buf), "SA(SPI=%u ", (u_int32_t)ntohl(sav->spi));
- while (p && *p)
- p++;
- /* NB: only use ipsec_address on one address at a time. */
- snprintf(p, sizeof (buf) - (p - buf), "src=%s ",
- ipsec_address(&saidx->src));
- while (p && *p)
- p++;
- snprintf(p, sizeof (buf) - (p - buf), "dst=%s)",
- ipsec_address(&saidx->dst));
+ 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(struct mbuf *m)
+ipsec_dumpmbuf(const struct mbuf *m)
{
+ const u_char *p;
int totlen;
int i;
- u_char *p;
totlen = 0;
printf("---\n");
while (m) {
- p = mtod(m, u_char *);
+ p = mtod(m, const u_char *);
for (i = 0; i < m->m_len; i++) {
printf("%02x ", p[i]);
totlen++;
@@ -1705,14 +1677,15 @@ ipsec_dumpmbuf(struct mbuf *m)
}
static void
-ipsec_init(const void *unused __unused)
+def_policy_init(const void *unused __unused)
{
- SECPOLICY_LOCK_INIT(&V_ip4_def_policy);
- V_ip4_def_policy.refcnt = 1; /* NB: disallow free. */
+ bzero(&V_def_policy, sizeof(struct secpolicy));
+ V_def_policy.policy = IPSEC_POLICY_NONE;
+ V_def_policy.refcnt = 1;
}
-VNET_SYSINIT(ipsec_init, SI_SUB_PROTO_DOMAININIT, SI_ORDER_ANY, ipsec_init,
- NULL);
+VNET_SYSINIT(def_policy_init, SI_SUB_PROTO_DOMAININIT, SI_ORDER_ANY,
+ def_policy_init, NULL);
/* XXX This stuff doesn't belong here... */
diff --git a/sys/netipsec/ipsec.h b/sys/netipsec/ipsec.h
index 6da3fc7..df3457a 100644
--- a/sys/netipsec/ipsec.h
+++ b/sys/netipsec/ipsec.h
@@ -81,21 +81,19 @@ struct secpolicyindex {
/* Security Policy Data Base */
struct secpolicy {
- LIST_ENTRY(secpolicy) chain;
- struct mtx lock;
+ TAILQ_ENTRY(secpolicy) chain;
- u_int refcnt; /* reference count */
struct secpolicyindex spidx; /* selector */
- u_int32_t id; /* It's unique number on the system. */
- u_int state; /* 0: dead, others: alive */
-#define IPSEC_SPSTATE_DEAD 0
-#define IPSEC_SPSTATE_ALIVE 1
- u_int policy; /* policy_type per pfkeyv2.h */
- u_int16_t scangen; /* scan generation # */
struct ipsecrequest *req;
/* pointer to the ipsec request tree, */
/* if policy == IPSEC else this value == NULL.*/
-
+ 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. */
/*
* lifetime handler.
* the policy can be used without limitiation if both lifetime and
@@ -109,13 +107,6 @@ struct secpolicy {
long validtime; /* duration this policy is valid without use */
};
-#define SECPOLICY_LOCK_INIT(_sp) \
- mtx_init(&(_sp)->lock, "ipsec policy", NULL, MTX_DEF)
-#define SECPOLICY_LOCK(_sp) mtx_lock(&(_sp)->lock)
-#define SECPOLICY_UNLOCK(_sp) mtx_unlock(&(_sp)->lock)
-#define SECPOLICY_LOCK_DESTROY(_sp) mtx_destroy(&(_sp)->lock)
-#define SECPOLICY_LOCK_ASSERT(_sp) mtx_assert(&(_sp)->lock, MA_OWNED)
-
/* Request for IPsec */
struct ipsecrequest {
struct ipsecrequest *next;
@@ -271,17 +262,6 @@ struct ipsecstat {
#ifdef _KERNEL
#include <sys/counter.h>
-struct ipsec_output_state {
- struct mbuf *m;
- struct route *ro;
- struct sockaddr *dst;
-};
-
-struct ipsec_history {
- int ih_proto;
- u_int32_t ih_spi;
-};
-
VNET_DECLARE(int, ipsec_debug);
#define V_ipsec_debug VNET(ipsec_debug)
@@ -294,7 +274,6 @@ VNET_DECLARE(int, ipsec_integrity);
#endif
VNET_PCPUSTAT_DECLARE(struct ipsecstat, ipsec4stat);
-VNET_DECLARE(struct secpolicy, ip4_def_policy);
VNET_DECLARE(int, ip4_esp_trans_deflev);
VNET_DECLARE(int, ip4_esp_net_deflev);
VNET_DECLARE(int, ip4_ah_trans_deflev);
@@ -307,7 +286,6 @@ VNET_DECLARE(int, crypto_support);
#define IPSECSTAT_INC(name) \
VNET_PCPUSTAT_ADD(struct ipsecstat, ipsec4stat, name, 1)
-#define V_ip4_def_policy VNET(ip4_def_policy)
#define V_ip4_esp_trans_deflev VNET(ip4_esp_trans_deflev)
#define V_ip4_esp_net_deflev VNET(ip4_esp_net_deflev)
#define V_ip4_ah_trans_deflev VNET(ip4_ah_trans_deflev)
@@ -328,37 +306,36 @@ 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(struct mbuf *, u_int, u_int,
- int *, struct inpcb *);
-extern struct secpolicy * ipsec_getpolicybyaddr(struct mbuf *, u_int,
- int, int *);
+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_in_reject(struct secpolicy *, struct mbuf *);
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);
+ size_t len, struct mbuf **mp);
extern int ipsec_delete_pcbpolicy(struct inpcb *);
-extern int ipsec4_in_reject(struct mbuf *, 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(struct mbuf *, u_int, struct inpcb *);
+extern size_t ipsec_hdrsiz(const struct mbuf *, u_int, struct inpcb *);
extern size_t ipsec_hdrsiz_tcp(struct tcpcb *);
union sockaddr_union;
-extern char * ipsec_address(union sockaddr_union* sa);
-extern const char *ipsec_logsastr(struct secasvar *);
+extern char *ipsec_address(union sockaddr_union *, char *, socklen_t);
+extern char *ipsec_logsastr(struct secasvar *, char *, size_t);
-extern void ipsec_dumpmbuf(struct mbuf *);
+extern void ipsec_dumpmbuf(const struct mbuf *);
struct m_tag;
extern void ah4_input(struct mbuf *m, int off);
@@ -366,15 +343,12 @@ extern void ah4_ctlinput(int cmd, struct sockaddr *sa, void *);
extern void esp4_input(struct mbuf *m, int off);
extern void esp4_ctlinput(int cmd, struct sockaddr *sa, void *);
extern void ipcomp4_input(struct mbuf *m, int off);
-extern int ipsec4_common_input(struct mbuf *m, ...);
+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, struct m_tag *mt);
-extern int ipsec4_process_packet(struct mbuf *, struct ipsecrequest *,
- int, int);
+ int skip, int protoff);
+extern int ipsec4_process_packet(struct mbuf *, struct ipsecrequest *);
extern int ipsec_process_done(struct mbuf *, struct ipsecrequest *);
-extern struct mbuf *ipsec_copypkt(struct mbuf *);
-
extern void m_checkalignment(const char* where, struct mbuf *m0,
int off, int len);
extern struct mbuf *m_makespace(struct mbuf *m0, int skip, int hlen, int *off);
diff --git a/sys/netipsec/ipsec6.h b/sys/netipsec/ipsec6.h
index 5179939..02cf025 100644
--- a/sys/netipsec/ipsec6.h
+++ b/sys/netipsec/ipsec6.h
@@ -59,12 +59,12 @@ VNET_DECLARE(int, ip6_ipsec_ecn);
#define V_ip6_ipsec_ecn VNET(ip6_ipsec_ecn)
struct inpcb;
-extern int ipsec6_in_reject(struct mbuf *, struct inpcb *);
+extern int ipsec6_in_reject(const struct mbuf *, struct inpcb *);
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, struct m_tag *mt);
+ int skip, int protoff);
extern void esp6_ctlinput(int, struct sockaddr *, void *);
extern int ipsec6_process_packet(struct mbuf *, struct ipsecrequest *);
#endif /*_KERNEL*/
diff --git a/sys/netipsec/ipsec_input.c b/sys/netipsec/ipsec_input.c
index 66de530..8fb74ff 100644
--- a/sys/netipsec/ipsec_input.c
+++ b/sys/netipsec/ipsec_input.c
@@ -117,9 +117,10 @@ static void ipsec4_common_ctlinput(int, struct sockaddr *, void *, int);
* and call the appropriate transform. The transform callback
* takes care of further processing (like ingress filtering).
*/
-static int
+int
ipsec_common_input(struct mbuf *m, int skip, int protoff, int af, int sproto)
{
+ char buf[INET6_ADDRSTRLEN];
union sockaddr_union dst_address;
struct secasvar *sav;
u_int32_t spi;
@@ -194,6 +195,13 @@ ipsec_common_input(struct mbuf *m, int skip, int protoff, int af, int sproto)
m_copydata(m, offsetof(struct ip6_hdr, ip6_dst),
sizeof(struct in6_addr),
(caddr_t) &dst_address.sin6.sin6_addr);
+ /* We keep addresses in SADB without embedded scope id */
+ if (IN6_IS_SCOPE_LINKLOCAL(&dst_address.sin6.sin6_addr)) {
+ /* XXX: sa6_recoverscope() */
+ dst_address.sin6.sin6_scope_id =
+ ntohs(dst_address.sin6.sin6_addr.s6_addr16[1]);
+ dst_address.sin6.sin6_addr.s6_addr16[1] = 0;
+ }
break;
#endif /* INET6 */
default:
@@ -207,8 +215,8 @@ ipsec_common_input(struct mbuf *m, int skip, int protoff, int af, int sproto)
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),
- (u_long) ntohl(spi), sproto));
+ __func__, ipsec_address(&dst_address, buf, sizeof(buf)),
+ (u_long) ntohl(spi), sproto));
IPSEC_ISTAT(sproto, notdb);
m_freem(m);
return ENOENT;
@@ -216,8 +224,8 @@ ipsec_common_input(struct mbuf *m, int skip, int protoff, int af, int sproto)
if (sav->tdb_xform == NULL) {
DPRINTF(("%s: attempted to use uninitialized SA %s/%08lx/%u\n",
- __func__, ipsec_address(&dst_address),
- (u_long) ntohl(spi), sproto));
+ __func__, ipsec_address(&dst_address, buf, sizeof(buf)),
+ (u_long) ntohl(spi), sproto));
IPSEC_ISTAT(sproto, noxform);
KEY_FREESAV(&sav);
m_freem(m);
@@ -234,28 +242,11 @@ ipsec_common_input(struct mbuf *m, int skip, int protoff, int af, int sproto)
}
#ifdef INET
-/*
- * Common input handler for IPv4 AH, ESP, and IPCOMP.
- */
-int
-ipsec4_common_input(struct mbuf *m, ...)
-{
- va_list ap;
- int off, nxt;
-
- va_start(ap, m);
- off = va_arg(ap, int);
- nxt = va_arg(ap, int);
- va_end(ap);
-
- return ipsec_common_input(m, off, offsetof(struct ip, ip_p),
- AF_INET, nxt);
-}
-
void
ah4_input(struct mbuf *m, int off)
{
- ipsec4_common_input(m, off, IPPROTO_AH);
+ ipsec_common_input(m, off, offsetof(struct ip, ip_p),
+ AF_INET, IPPROTO_AH);
}
void
ah4_ctlinput(int cmd, struct sockaddr *sa, void *v)
@@ -268,7 +259,8 @@ ah4_ctlinput(int cmd, struct sockaddr *sa, void *v)
void
esp4_input(struct mbuf *m, int off)
{
- ipsec4_common_input(m, off, IPPROTO_ESP);
+ ipsec_common_input(m, off, offsetof(struct ip, ip_p),
+ AF_INET, IPPROTO_ESP);
}
void
esp4_ctlinput(int cmd, struct sockaddr *sa, void *v)
@@ -281,7 +273,8 @@ esp4_ctlinput(int cmd, struct sockaddr *sa, void *v)
void
ipcomp4_input(struct mbuf *m, int off)
{
- ipsec4_common_input(m, off, IPPROTO_IPCOMP);
+ ipsec_common_input(m, off, offsetof(struct ip, ip_p),
+ AF_INET, IPPROTO_IPCOMP);
}
/*
@@ -291,9 +284,10 @@ ipcomp4_input(struct mbuf *m, int off)
* the processed packet.
*/
int
-ipsec4_common_input_cb(struct mbuf *m, struct secasvar *sav,
- int skip, int protoff, struct m_tag *mt)
+ipsec4_common_input_cb(struct mbuf *m, struct secasvar *sav, int skip,
+ int protoff)
{
+ char buf[INET6_ADDRSTRLEN];
int prot, af, sproto, isr_prot;
struct ip *ip;
struct m_tag *mtag;
@@ -332,8 +326,8 @@ ipsec4_common_input_cb(struct mbuf *m, struct secasvar *sav,
*/
if (m->m_len < skip && (m = m_pullup(m, skip)) == NULL) {
DPRINTF(("%s: processing failed for SA %s/%08lx\n",
- __func__, ipsec_address(&sav->sah->saidx.dst),
- (u_long) ntohl(sav->spi)));
+ __func__, ipsec_address(&sav->sah->saidx.dst,
+ buf, sizeof(buf)), (u_long) ntohl(sav->spi)));
IPSEC_ISTAT(sproto, hdrops);
error = ENOBUFS;
goto bad;
@@ -356,6 +350,7 @@ ipsec4_common_input_cb(struct mbuf *m, struct secasvar *sav,
ipsec_bpf(m, sav, AF_INET, ENC_IN|ENC_BEFORE);
if ((error = ipsec_filter(&m, PFIL_IN, ENC_IN|ENC_BEFORE)) != 0)
return (error);
+ ip = mtod(m, struct ip *);
#endif /* DEV_ENC */
/* IP-in-IP encapsulation */
@@ -449,13 +444,9 @@ ipsec4_common_input_cb(struct mbuf *m, struct secasvar *sav,
/*
* Record what we've done to the packet (under what SA it was
- * processed). If we've been passed an mtag, it means the packet
- * was already processed by an ethernet/crypto combo card and
- * thus has a tag attached with all the right information, but
- * with a PACKET_TAG_IPSEC_IN_CRYPTO_DONE as opposed to
- * PACKET_TAG_IPSEC_IN_DONE type; in that case, just change the type.
+ * processed).
*/
- if (mt == NULL && sproto != IPPROTO_IPCOMP) {
+ if (sproto != IPPROTO_IPCOMP) {
mtag = m_tag_get(PACKET_TAG_IPSEC_IN_DONE,
sizeof(struct tdb_ident), M_NOWAIT);
if (mtag == NULL) {
@@ -474,9 +465,6 @@ ipsec4_common_input_cb(struct mbuf *m, struct secasvar *sav,
tdbi->alg_enc = sav->alg_enc;
m_tag_prepend(m, mtag);
- } else if (mt != NULL) {
- mt->m_tag_id = PACKET_TAG_IPSEC_IN_DONE;
- /* XXX do we need to mark m_flags??? */
}
key_sa_recordxfer(sav, m); /* record data transfer */
@@ -593,15 +581,16 @@ ipsec6_common_input(struct mbuf **mp, int *offp, int proto)
* filtering and other sanity checks on the processed packet.
*/
int
-ipsec6_common_input_cb(struct mbuf *m, struct secasvar *sav, int skip, int protoff,
- struct m_tag *mt)
+ipsec6_common_input_cb(struct mbuf *m, struct secasvar *sav, int skip,
+ int protoff)
{
+ char buf[INET6_ADDRSTRLEN];
int prot, af, sproto;
struct ip6_hdr *ip6;
struct m_tag *mtag;
struct tdb_ident *tdbi;
struct secasindex *saidx;
- int nxt;
+ int nxt, isr_prot;
u_int8_t nxt8;
int error, nest;
#ifdef notyet
@@ -632,8 +621,8 @@ ipsec6_common_input_cb(struct mbuf *m, struct secasvar *sav, int skip, int proto
(m = m_pullup(m, sizeof(struct ip6_hdr))) == NULL) {
DPRINTF(("%s: processing failed for SA %s/%08lx\n",
- __func__, ipsec_address(&sav->sah->saidx.dst),
- (u_long) ntohl(sav->spi)));
+ __func__, ipsec_address(&sav->sah->saidx.dst, buf,
+ sizeof(buf)), (u_long) ntohl(sav->spi)));
IPSEC_ISTAT(sproto, hdrops);
error = EACCES;
@@ -738,13 +727,9 @@ ipsec6_common_input_cb(struct mbuf *m, struct secasvar *sav, int skip, int proto
/*
* Record what we've done to the packet (under what SA it was
- * processed). If we've been passed an mtag, it means the packet
- * was already processed by an ethernet/crypto combo card and
- * thus has a tag attached with all the right information, but
- * with a PACKET_TAG_IPSEC_IN_CRYPTO_DONE as opposed to
- * PACKET_TAG_IPSEC_IN_DONE type; in that case, just change the type.
+ * processed).
*/
- if (mt == NULL && sproto != IPPROTO_IPCOMP) {
+ if (sproto != IPPROTO_IPCOMP) {
mtag = m_tag_get(PACKET_TAG_IPSEC_IN_DONE,
sizeof(struct tdb_ident), M_NOWAIT);
if (mtag == NULL) {
@@ -763,10 +748,6 @@ ipsec6_common_input_cb(struct mbuf *m, struct secasvar *sav, int skip, int proto
tdbi->alg_enc = sav->alg_enc;
m_tag_prepend(m, mtag);
- } else {
- if (mt != NULL)
- mt->m_tag_id = PACKET_TAG_IPSEC_IN_DONE;
- /* XXX do we need to mark m_flags??? */
}
key_sa_recordxfer(sav, m);
@@ -785,6 +766,35 @@ ipsec6_common_input_cb(struct mbuf *m, struct secasvar *sav, int skip, int proto
if ((error = ipsec_filter(&m, PFIL_IN, ENC_IN|ENC_AFTER)) != 0)
return (error);
#endif /* DEV_ENC */
+ if (skip == 0) {
+ /*
+ * We stripped outer IPv6 header.
+ * Now we should requeue decrypted packet via netisr.
+ */
+ switch (prot) {
+#ifdef INET
+ case IPPROTO_IPIP:
+ isr_prot = NETISR_IP;
+ break;
+#endif
+ case IPPROTO_IPV6:
+ isr_prot = NETISR_IPV6;
+ break;
+ default:
+ DPRINTF(("%s: cannot handle inner ip proto %d\n",
+ __func__, prot));
+ IPSEC_ISTAT(sproto, nopf);
+ 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));
+ }
+ return (error);
+ }
/*
* See the end of ip6_input for this logic.
* IPPROTO_IPV[46] case will be processed just like other ones
diff --git a/sys/netipsec/ipsec_output.c b/sys/netipsec/ipsec_output.c
index 442fb7a..c6e89de 100644
--- a/sys/netipsec/ipsec_output.c
+++ b/sys/netipsec/ipsec_output.c
@@ -60,6 +60,7 @@
#include <netinet/ip6.h>
#ifdef INET6
#include <netinet6/ip6_var.h>
+#include <netinet6/scope6_var.h>
#endif
#include <netinet/in_pcb.h>
#ifdef INET6
@@ -102,6 +103,7 @@ ipsec_process_done(struct mbuf *m, struct ipsecrequest *isr)
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"));
@@ -155,6 +157,8 @@ ipsec_process_done(struct mbuf *m, struct ipsecrequest *isr)
tdbi->spi = sav->spi;
m_tag_prepend(m, mtag);
+ key_sa_recordxfer(sav, m); /* record data transfer */
+
/*
* If there's another (bundled) SA to apply, do so.
* Note that this puts a burden on the kernel stack size.
@@ -168,7 +172,7 @@ ipsec_process_done(struct mbuf *m, struct ipsecrequest *isr)
#ifdef INET
case AF_INET:
IPSECSTAT_INC(ips_out_bundlesa);
- return ipsec4_process_packet(m, isr->next, 0, 0);
+ return (ipsec4_process_packet(m, isr->next));
/* NOTREACHED */
#endif
#ifdef notyet
@@ -176,7 +180,7 @@ ipsec_process_done(struct mbuf *m, struct ipsecrequest *isr)
case AF_INET6:
/* XXX */
IPSEC6STAT_INC(ips_out_bundlesa);
- return ipsec6_process_packet(m, isr->next);
+ return (ipsec6_process_packet(m, isr->next));
/* NOTREACHED */
#endif /* INET6 */
#endif
@@ -187,12 +191,10 @@ ipsec_process_done(struct mbuf *m, struct ipsecrequest *isr)
goto bad;
}
}
- key_sa_recordxfer(sav, m); /* record data transfer */
/*
* We're done with IPsec processing, transmit the packet using the
- * appropriate network protocol (IP or IPv6). SPD lookup will be
- * performed again there.
+ * appropriate network protocol (IP or IPv6).
*/
switch (saidx->dst.sa.sa_family) {
#ifdef INET
@@ -418,17 +420,117 @@ bad:
#undef IPSEC_OSTAT
}
+static int
+ipsec_encap(struct mbuf **mp, struct secasindex *saidx)
+{
+#ifdef INET6
+ struct ip6_hdr *ip6;
+#endif
+ struct ip *ip;
+ int setdf;
+ uint8_t itos, proto;
+
+ ip = mtod(*mp, struct ip *);
+ switch (ip->ip_v) {
+#ifdef INET
+ case IPVERSION:
+ proto = IPPROTO_IPIP;
+ /*
+ * Collect IP_DF state from the inner header
+ * and honor system-wide control of how to handle it.
+ */
+ switch (V_ip4_ipsec_dfbit) {
+ case 0: /* clear in outer header */
+ case 1: /* set in outer header */
+ setdf = V_ip4_ipsec_dfbit;
+ break;
+ default:/* propagate to outer header */
+ setdf = (ip->ip_off & htons(IP_DF)) != 0;
+ }
+ itos = ip->ip_tos;
+ break;
+#endif
+#ifdef INET6
+ case (IPV6_VERSION >> 4):
+ proto = IPPROTO_IPV6;
+ ip6 = mtod(*mp, struct ip6_hdr *);
+ itos = (ntohl(ip6->ip6_flow) >> 20) & 0xff;
+ setdf = V_ip4_ipsec_dfbit ? 1: 0;
+ /* scoped address handling */
+ in6_clearscope(&ip6->ip6_src);
+ in6_clearscope(&ip6->ip6_dst);
+ break;
+#endif
+ default:
+ return (EAFNOSUPPORT);
+ }
+ switch (saidx->dst.sa.sa_family) {
+#ifdef INET
+ case AF_INET:
+ if (saidx->src.sa.sa_family != AF_INET ||
+ 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);
+ if (*mp == NULL)
+ return (ENOBUFS);
+ ip = mtod(*mp, struct ip *);
+ ip->ip_v = IPVERSION;
+ ip->ip_hl = sizeof(struct ip) >> 2;
+ ip->ip_p = proto;
+ ip->ip_len = htons((*mp)->m_pkthdr.len);
+ ip->ip_ttl = V_ip_defttl;
+ ip->ip_sum = 0;
+ ip->ip_off = setdf ? htons(IP_DF): 0;
+ ip->ip_src = saidx->src.sin.sin_addr;
+ ip->ip_dst = saidx->dst.sin.sin_addr;
+ ip_ecn_ingress(V_ip4_ipsec_ecn, &ip->ip_tos, &itos);
+ ip->ip_id = ip_newid();
+ break;
+#endif /* INET */
+#ifdef INET6
+ case AF_INET6:
+ if (saidx->src.sa.sa_family != AF_INET6 ||
+ 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);
+ if (*mp == NULL)
+ return (ENOBUFS);
+ ip6 = mtod(*mp, struct ip6_hdr *);
+ ip6->ip6_flow = 0;
+ ip6->ip6_vfc = IPV6_VERSION;
+ ip6->ip6_hlim = V_ip6_defhlim;
+ ip6->ip6_nxt = proto;
+ ip6->ip6_dst = saidx->dst.sin6.sin6_addr;
+ /* For link-local address embed scope zone id */
+ if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_dst))
+ ip6->ip6_dst.s6_addr16[1] =
+ htons(saidx->dst.sin6.sin6_scope_id & 0xffff);
+ ip6->ip6_src = saidx->src.sin6.sin6_addr;
+ if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_src))
+ ip6->ip6_src.s6_addr16[1] =
+ htons(saidx->src.sin6.sin6_scope_id & 0xffff);
+ ip6->ip6_plen = htons((*mp)->m_pkthdr.len - sizeof(*ip6));
+ ip_ecn_ingress(V_ip6_ipsec_ecn, &proto, &itos);
+ ip6->ip6_flow |= htonl((uint32_t)proto << 20);
+ break;
+#endif /* INET6 */
+ default:
+ return (EAFNOSUPPORT);
+ }
+ return (0);
+}
+
#ifdef INET
/*
* IPsec output logic for IPv4.
*/
int
-ipsec4_process_packet(
- struct mbuf *m,
- struct ipsecrequest *isr,
- int flags,
- int tunalready)
+ipsec4_process_packet(struct mbuf *m, struct ipsecrequest *isr)
{
+ char sbuf[INET6_ADDRSTRLEN], dbuf[INET6_ADDRSTRLEN];
+ union sockaddr_union *dst;
struct secasindex saidx;
struct secasvar *sav;
struct ip *ip;
@@ -447,7 +549,13 @@ ipsec4_process_packet(
}
sav = isr->sav;
-
+ if (m->m_len < sizeof(struct ip) &&
+ (m = m_pullup(m, sizeof (struct ip))) == NULL) {
+ error = ENOBUFS;
+ goto bad;
+ }
+ ip = mtod(m, struct ip *);
+ dst = &sav->sah->saidx.dst;
#ifdef DEV_ENC
encif->if_opackets++;
encif->if_obytes += m->m_pkthdr.len;
@@ -457,99 +565,29 @@ ipsec4_process_packet(
/* pass the mbuf to enc0 for packet filtering */
if ((error = ipsec_filter(&m, PFIL_OUT, ENC_OUT|ENC_BEFORE)) != 0)
goto bad;
+ ip = mtod(m, struct ip *);
#endif
-
- if (!tunalready) {
- union sockaddr_union *dst = &sav->sah->saidx.dst;
- int setdf;
-
- /*
- * Collect IP_DF state from the outer header.
- */
- if (dst->sa.sa_family == AF_INET) {
- if (m->m_len < sizeof (struct ip) &&
- (m = m_pullup(m, sizeof (struct ip))) == NULL) {
- error = ENOBUFS;
- goto bad;
- }
- ip = mtod(m, struct ip *);
- /* Honor system-wide control of how to handle IP_DF */
- switch (V_ip4_ipsec_dfbit) {
- case 0: /* clear in outer header */
- case 1: /* set in outer header */
- setdf = V_ip4_ipsec_dfbit;
- break;
- default: /* propagate to outer header */
- setdf = ntohs(ip->ip_off & IP_DF);
- break;
- }
- } else {
- ip = NULL; /* keep compiler happy */
- setdf = 0;
- }
- /* Do the appropriate encapsulation, if necessary */
- if (isr->saidx.mode == IPSEC_MODE_TUNNEL || /* Tunnel requ'd */
- dst->sa.sa_family != AF_INET || /* PF mismatch */
-#if 0
- (sav->flags & SADB_X_SAFLAGS_TUNNEL) || /* Tunnel requ'd */
- sav->tdb_xform->xf_type == XF_IP4 || /* ditto */
-#endif
- (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)) {
- struct mbuf *mp;
-
- /* Fix IPv4 header checksum and length */
- if (m->m_len < sizeof (struct ip) &&
- (m = m_pullup(m, sizeof (struct ip))) == NULL) {
- error = ENOBUFS;
- goto bad;
- }
- ip = mtod(m, struct ip *);
- if (ip->ip_v == IPVERSION) {
- ip->ip_len = htons(m->m_pkthdr.len);
- ip->ip_sum = 0;
- ip->ip_sum = in_cksum(m, ip->ip_hl << 2);
- }
-
- /* Encapsulate the packet */
- error = ipip_output(m, isr, &mp, 0, 0);
- if (mp == NULL && !error) {
- /* Should never happen. */
- DPRINTF(("%s: ipip_output returns no mbuf and "
- "no error!", __func__));
- error = EFAULT;
- }
- if (error) {
- if (mp) {
- /* XXX: Should never happen! */
- m_freem(mp);
- }
- m = NULL; /* ipip_output() already freed it */
- goto bad;
- }
- m = mp, mp = NULL;
- /*
- * ipip_output clears IP_DF in the new header. If
- * we need to propagate IP_DF from the outer header,
- * then we have to do it here.
- *
- * XXX shouldn't assume what ipip_output does.
- */
- if (dst->sa.sa_family == AF_INET && setdf) {
- if (m->m_len < sizeof (struct ip) &&
- (m = m_pullup(m, sizeof (struct ip))) == NULL) {
- error = ENOBUFS;
- goto bad;
- }
- ip = mtod(m, struct ip *);
- ip->ip_off = ntohs(ip->ip_off);
- ip->ip_off |= IP_DF;
- ip->ip_off = htons(ip->ip_off);
- }
+ /* 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;
}
}
-
#ifdef DEV_ENC
/* pass the mbuf to enc0 for bpf processing */
ipsec_bpf(m, sav, sav->sah->saidx.dst.sa.sa_family, ENC_OUT|ENC_AFTER);
@@ -561,40 +599,33 @@ ipsec4_process_packet(
/*
* Dispatch to the appropriate IPsec transform logic. The
* packet will be returned for transmission after crypto
- * processing, etc. are completed. For encapsulation we
- * bypass this call because of the explicit call done above
- * (necessary to deal with IP_DF handling for IPv4).
+ * processing, etc. are completed.
*
* NB: m & sav are ``passed to caller'' who's reponsible for
* for reclaiming their resources.
*/
- if (sav->tdb_xform->xf_type != XF_IP4) {
- union sockaddr_union *dst = &sav->sah->saidx.dst;
- 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;
+ 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;
+ case AF_INET6:
+ i = sizeof(struct ip6_hdr);
+ off = offsetof(struct ip6_hdr, ip6_nxt);
+ break;
#endif /* INET6 */
- default:
+ 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);
- } else {
- error = ipsec_process_done(m, isr);
+ __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;
+ return (error);
bad:
if (isr)
IPSECREQUEST_UNLOCK(isr);
@@ -622,11 +653,9 @@ in6_sa_equal_addrwithscope(const struct sockaddr_in6 *sa, const struct in6_addr
* IPsec output logic for IPv6.
*/
int
-ipsec6_process_packet(
- struct mbuf *m,
- struct ipsecrequest *isr
- )
+ipsec6_process_packet(struct mbuf *m, struct ipsecrequest *isr)
{
+ char sbuf[INET6_ADDRSTRLEN], dbuf[INET6_ADDRSTRLEN];
struct secasindex saidx;
struct secasvar *sav;
struct ip6_hdr *ip6;
@@ -644,7 +673,6 @@ ipsec6_process_packet(
goto bad;
return EJUSTRETURN;
}
-
sav = isr->sav;
dst = &sav->sah->saidx.dst;
@@ -659,6 +687,7 @@ ipsec6_process_packet(
/* pass the mbuf to enc0 for packet filtering */
if ((error = ipsec_filter(&m, PFIL_OUT, ENC_OUT|ENC_BEFORE)) != 0)
goto bad;
+ ip6 = mtod(m, struct ip6_hdr *);
#endif /* DEV_ENC */
/* Do the appropriate encapsulation, if necessary */
@@ -668,42 +697,21 @@ ipsec6_process_packet(
(!IN6_IS_ADDR_UNSPECIFIED(&dst->sin6.sin6_addr)) &&
(!in6_sa_equal_addrwithscope(&dst->sin6,
&ip6->ip6_dst)))) {
- struct mbuf *mp;
-
- /* Fix IPv6 header payload length. */
- if (m->m_len < sizeof(struct ip6_hdr))
- if ((m = m_pullup(m,sizeof(struct ip6_hdr))) == NULL) {
- error = ENOBUFS;
- goto bad;
- }
-
if (m->m_pkthdr.len - sizeof(*ip6) > IPV6_MAXPACKET) {
/* No jumbogram support. */
error = ENXIO; /*XXX*/
goto bad;
}
-
- /* Encapsulate the packet */
- error = ipip_output(m, isr, &mp, 0, 0);
- if (mp == NULL && !error) {
- /* Should never happen. */
- DPRINTF(("ipsec6_process_packet: ipip_output "
- "returns no mbuf and no error!"));
- error = EFAULT;
- goto bad;
- }
-
- if (error) {
- if (mp) {
- /* XXX: Should never happen! */
- m_freem(mp);
- }
- m = NULL; /* ipip_output() already freed it */
+ 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;
}
-
- m = mp;
- mp = NULL;
}
#ifdef DEV_ENC
diff --git a/sys/netipsec/key.c b/sys/netipsec/key.c
index a3e3d76..17980d6 100644
--- a/sys/netipsec/key.c
+++ b/sys/netipsec/key.c
@@ -48,6 +48,7 @@
#include <sys/domain.h>
#include <sys/protosw.h>
#include <sys/malloc.h>
+#include <sys/rmlock.h>
#include <sys/socket.h>
#include <sys/socketvar.h>
#include <sys/sysctl.h>
@@ -140,16 +141,19 @@ static VNET_DEFINE(u_int32_t, acq_seq) = 0;
#define V_acq_seq VNET(acq_seq)
/* SPD */
-static VNET_DEFINE(LIST_HEAD(_sptree, secpolicy), sptree[IPSEC_DIR_MAX]);
+static VNET_DEFINE(TAILQ_HEAD(_sptree, secpolicy), sptree[IPSEC_DIR_MAX]);
+static struct rmlock sptree_lock;
#define V_sptree VNET(sptree)
-static struct mtx sptree_lock;
-#define SPTREE_LOCK_INIT() \
- mtx_init(&sptree_lock, "sptree", \
- "fast ipsec security policy database", MTX_DEF)
-#define SPTREE_LOCK_DESTROY() mtx_destroy(&sptree_lock)
-#define SPTREE_LOCK() mtx_lock(&sptree_lock)
-#define SPTREE_UNLOCK() mtx_unlock(&sptree_lock)
-#define SPTREE_LOCK_ASSERT() mtx_assert(&sptree_lock, MA_OWNED)
+#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
+#define SPTREE_RLOCK() rm_rlock(&sptree_lock, &sptree_tracker)
+#define SPTREE_RUNLOCK() rm_runlock(&sptree_lock, &sptree_tracker)
+#define SPTREE_RLOCK_ASSERT() rm_assert(&sptree_lock, RA_RLOCKED)
+#define SPTREE_WLOCK() rm_wlock(&sptree_lock)
+#define SPTREE_WUNLOCK() rm_wunlock(&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 */
#define V_sahtree VNET(sahtree)
@@ -411,9 +415,8 @@ struct sadb_msghdr {
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_delsp(struct secpolicy *);
+static void key_unlink(struct secpolicy *);
static struct secpolicy *key_getsp(struct secpolicyindex *);
-static void _key_delsp(struct secpolicy *sp);
static struct secpolicy *key_getspbyid(u_int32_t);
static u_int32_t key_newreqid(void);
static struct mbuf *key_gather_mbuf(struct mbuf *,
@@ -465,7 +468,7 @@ static void key_porttosaddr(struct sockaddr *, u_int16_t);
key_porttosaddr((struct sockaddr *)(saddr), (port))
static struct mbuf *key_setsadbxsa2(u_int8_t, u_int32_t, u_int32_t);
static struct mbuf *key_setsadbxpolicy(u_int16_t, u_int8_t,
- u_int32_t);
+ u_int32_t, u_int32_t);
static struct seckey *key_dup_keymsg(const struct sadb_key *, u_int,
struct malloc_type *);
static struct seclifetime *key_dup_lifemsg(const struct sadb_lifetime *src,
@@ -570,15 +573,8 @@ sa_delref(struct secasvar *sav)
return (refcount_release(&sav->refcnt));
}
-#define SP_ADDREF(p) do { \
- (p)->refcnt++; \
- IPSEC_ASSERT((p)->refcnt != 0, ("SP refcnt overflow")); \
-} while (0)
-#define SP_DELREF(p) do { \
- IPSEC_ASSERT((p)->refcnt > 0, ("SP refcnt underflow")); \
- (p)->refcnt--; \
-} while (0)
-
+#define SP_ADDREF(p) refcount_acquire(&(p)->refcnt)
+#define SP_DELREF(p) refcount_release(&(p)->refcnt)
/*
* Update the refcnt while holding the SPTREE lock.
@@ -586,9 +582,8 @@ sa_delref(struct secasvar *sav)
void
key_addref(struct secpolicy *sp)
{
- SPTREE_LOCK();
+
SP_ADDREF(sp);
- SPTREE_UNLOCK();
}
/*
@@ -601,7 +596,7 @@ key_havesp(u_int dir)
{
return (dir == IPSEC_DIR_INBOUND || dir == IPSEC_DIR_OUTBOUND ?
- LIST_FIRST(&V_sptree[dir]) != NULL : 1);
+ TAILQ_FIRST(&V_sptree[dir]) != NULL : 1);
}
/* %%% IPsec policy management */
@@ -615,6 +610,7 @@ struct secpolicy *
key_allocsp(struct secpolicyindex *spidx, u_int dir, const char* where,
int tag)
{
+ SPTREE_RLOCK_TRACKER;
struct secpolicy *sp;
IPSEC_ASSERT(spidx != NULL, ("null spidx"));
@@ -629,14 +625,11 @@ key_allocsp(struct secpolicyindex *spidx, u_int dir, const char* where,
printf("*** objects\n");
kdebug_secpolicyindex(spidx));
- SPTREE_LOCK();
- LIST_FOREACH(sp, &V_sptree[dir], chain) {
+ SPTREE_RLOCK();
+ TAILQ_FOREACH(sp, &V_sptree[dir], chain) {
KEYDEBUG(KEYDEBUG_IPSEC_DATA,
printf("*** in SPD\n");
kdebug_secpolicyindex(&sp->spidx));
-
- if (sp->state == IPSEC_SPSTATE_DEAD)
- continue;
if (key_cmpspidx_withmask(&sp->spidx, spidx))
goto found;
}
@@ -650,7 +643,7 @@ found:
sp->lastused = time_second;
SP_ADDREF(sp);
}
- SPTREE_UNLOCK();
+ SPTREE_RUNLOCK();
KEYDEBUG(KEYDEBUG_IPSEC_STAMP,
printf("DP %s return SP:%p (ID=%u) refcnt %u\n", __func__,
@@ -668,6 +661,7 @@ struct secpolicy *
key_allocsp2(u_int32_t spi, union sockaddr_union *dst, u_int8_t proto,
u_int dir, const char* where, int tag)
{
+ SPTREE_RLOCK_TRACKER;
struct secpolicy *sp;
IPSEC_ASSERT(dst != NULL, ("null dst"));
@@ -683,14 +677,11 @@ key_allocsp2(u_int32_t spi, union sockaddr_union *dst, u_int8_t proto,
printf("spi %u proto %u dir %u\n", spi, proto, dir);
kdebug_sockaddr(&dst->sa));
- SPTREE_LOCK();
- LIST_FOREACH(sp, &V_sptree[dir], chain) {
+ SPTREE_RLOCK();
+ TAILQ_FOREACH(sp, &V_sptree[dir], chain) {
KEYDEBUG(KEYDEBUG_IPSEC_DATA,
printf("*** in SPD\n");
kdebug_secpolicyindex(&sp->spidx));
-
- if (sp->state == IPSEC_SPSTATE_DEAD)
- continue;
/* compare simple values, then dst address */
if (sp->spidx.ul_proto != proto)
continue;
@@ -710,7 +701,7 @@ found:
sp->lastused = time_second;
SP_ADDREF(sp);
}
- SPTREE_UNLOCK();
+ SPTREE_RUNLOCK();
KEYDEBUG(KEYDEBUG_IPSEC_STAMP,
printf("DP %s return SP:%p (ID=%u) refcnt %u\n", __func__,
@@ -1162,6 +1153,66 @@ done:
return sav;
}
+struct secasvar *
+key_allocsa_tunnel(union sockaddr_union *src, union sockaddr_union *dst,
+ u_int proto, const char* where, int tag)
+{
+ 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;
+ }
+ }
+ }
+ sav = NULL;
+done:
+ SAHTREE_UNLOCK();
+
+ KEYDEBUG(KEYDEBUG_IPSEC_STAMP,
+ printf("DP %s return SA:%p; refcnt %u\n", __func__,
+ sav, sav ? sav->refcnt : 0));
+ return (sav);
+}
+
/*
* Must be called after calling key_allocsp().
* For both the packet without socket and key_freeso().
@@ -1169,22 +1220,70 @@ done:
void
_key_freesp(struct secpolicy **spp, const char* where, int tag)
{
+ struct ipsecrequest *isr, *nextisr;
struct secpolicy *sp = *spp;
IPSEC_ASSERT(sp != NULL, ("null sp"));
-
- SPTREE_LOCK();
- SP_DELREF(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->refcnt == 0) {
- *spp = NULL;
- key_delsp(sp);
+ if (SP_DELREF(sp) == 0)
+ return;
+ *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);
+ }
+ free(sp, M_IPSEC_SP);
+}
+
+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();
+
+ SPTREE_WLOCK();
+ if (sp->state == IPSEC_SPSTATE_DEAD) {
+ SPTREE_WUNLOCK();
+ return;
}
- SPTREE_UNLOCK();
+ sp->state = IPSEC_SPSTATE_DEAD;
+ TAILQ_REMOVE(&V_sptree[sp->spidx.dir], sp, chain);
+ SPTREE_WUNLOCK();
+ KEY_FREESP(&sp);
+}
+
+/*
+ * insert a secpolicy into the SP database. Lower priorities first
+ */
+static void
+key_insertsp(struct secpolicy *newsp)
+{
+ struct secpolicy *sp;
+
+ SPTREE_WLOCK();
+ 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:
+ newsp->state = IPSEC_SPSTATE_ALIVE;
+ SPTREE_WUNLOCK();
}
/*
@@ -1273,38 +1372,6 @@ key_freesav(struct secasvar **psav, const char* where, int tag)
/* %%% SPD management */
/*
- * free security policy entry.
- */
-static void
-key_delsp(struct secpolicy *sp)
-{
- struct ipsecrequest *isr, *nextisr;
-
- IPSEC_ASSERT(sp != NULL, ("null sp"));
- SPTREE_LOCK_ASSERT();
-
- sp->state = IPSEC_SPSTATE_DEAD;
-
- IPSEC_ASSERT(sp->refcnt == 0,
- ("SP with references deleted (refcnt %u)", sp->refcnt));
-
- /* remove from SP index */
- if (__LIST_CHAINED(sp))
- LIST_REMOVE(sp, chain);
-
- 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);
- }
- _key_delsp(sp);
-}
-
-/*
* search SPD
* OUT: NULL : not found
* others : found, pointer to a SP.
@@ -1312,20 +1379,19 @@ key_delsp(struct secpolicy *sp)
static struct secpolicy *
key_getsp(struct secpolicyindex *spidx)
{
+ SPTREE_RLOCK_TRACKER;
struct secpolicy *sp;
IPSEC_ASSERT(spidx != NULL, ("null spidx"));
- SPTREE_LOCK();
- LIST_FOREACH(sp, &V_sptree[spidx->dir], chain) {
- if (sp->state == IPSEC_SPSTATE_DEAD)
- continue;
+ SPTREE_RLOCK();
+ TAILQ_FOREACH(sp, &V_sptree[spidx->dir], chain) {
if (key_cmpspidx_exactly(spidx, &sp->spidx)) {
SP_ADDREF(sp);
break;
}
}
- SPTREE_UNLOCK();
+ SPTREE_RUNLOCK();
return sp;
}
@@ -1338,28 +1404,25 @@ key_getsp(struct secpolicyindex *spidx)
static struct secpolicy *
key_getspbyid(u_int32_t id)
{
+ SPTREE_RLOCK_TRACKER;
struct secpolicy *sp;
- SPTREE_LOCK();
- LIST_FOREACH(sp, &V_sptree[IPSEC_DIR_INBOUND], chain) {
- if (sp->state == IPSEC_SPSTATE_DEAD)
- continue;
+ SPTREE_RLOCK();
+ TAILQ_FOREACH(sp, &V_sptree[IPSEC_DIR_INBOUND], chain) {
if (sp->id == id) {
SP_ADDREF(sp);
goto done;
}
}
- LIST_FOREACH(sp, &V_sptree[IPSEC_DIR_OUTBOUND], chain) {
- if (sp->state == IPSEC_SPSTATE_DEAD)
- continue;
+ TAILQ_FOREACH(sp, &V_sptree[IPSEC_DIR_OUTBOUND], chain) {
if (sp->id == id) {
SP_ADDREF(sp);
goto done;
}
}
done:
- SPTREE_UNLOCK();
+ SPTREE_RUNLOCK();
return sp;
}
@@ -1371,11 +1434,8 @@ key_newsp(const char* where, int tag)
newsp = (struct secpolicy *)
malloc(sizeof(struct secpolicy), M_IPSEC_SP, M_NOWAIT|M_ZERO);
- if (newsp) {
- SECPOLICY_LOCK_INIT(newsp);
- newsp->refcnt = 1;
- newsp->req = NULL;
- }
+ if (newsp)
+ refcount_init(&newsp->refcnt, 1);
KEYDEBUG(KEYDEBUG_IPSEC_STAMP,
printf("DP %s from %s:%u return SP:%p\n", __func__,
@@ -1383,13 +1443,6 @@ key_newsp(const char* where, int tag)
return newsp;
}
-static void
-_key_delsp(struct secpolicy *sp)
-{
- SECPOLICY_LOCK_DESTROY(sp);
- free(sp, M_IPSEC_SP);
-}
-
/*
* create secpolicy structure from sadb_x_policy structure.
* NOTE: `state', `secpolicyindex' in secpolicy structure are not set,
@@ -1416,6 +1469,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;
/* check policy */
switch (xpl0->sadb_x_policy_type) {
@@ -1652,6 +1706,7 @@ key_sp2msg(struct secpolicy *sp)
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);
/* if is the policy for ipsec ? */
@@ -1869,9 +1924,7 @@ key_spdadd(struct socket *so, struct mbuf *m, const struct sadb_msghdr *mhp)
newsp = key_getsp(&spidx);
if (mhp->msg->sadb_msg_type == SADB_X_SPDUPDATE) {
if (newsp) {
- SPTREE_LOCK();
- newsp->state = IPSEC_SPSTATE_DEAD;
- SPTREE_UNLOCK();
+ key_unlink(newsp);
KEY_FREESP(&newsp);
}
} else {
@@ -1883,13 +1936,15 @@ key_spdadd(struct socket *so, struct mbuf *m, const struct sadb_msghdr *mhp)
}
}
+ /* XXX: there is race between key_getsp and key_msg2sp. */
+
/* allocation new SP entry */
if ((newsp = key_msg2sp(xpl0, PFKEY_EXTLEN(xpl0), &error)) == NULL) {
return key_senderror(so, m, error);
}
if ((newsp->id = key_getnewspid()) == 0) {
- _key_delsp(newsp);
+ KEY_FREESP(&newsp);
return key_senderror(so, m, ENOBUFS);
}
@@ -1905,18 +1960,20 @@ key_spdadd(struct socket *so, struct mbuf *m, const struct sadb_msghdr *mhp)
/* sanity check on addr pair */
if (((struct sockaddr *)(src0 + 1))->sa_family !=
((struct sockaddr *)(dst0+ 1))->sa_family) {
- _key_delsp(newsp);
+ KEY_FREESP(&newsp);
return key_senderror(so, m, EINVAL);
}
if (((struct sockaddr *)(src0 + 1))->sa_len !=
((struct sockaddr *)(dst0+ 1))->sa_len) {
- _key_delsp(newsp);
+ 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_delsp(newsp);
+ 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);
}
}
@@ -1927,9 +1984,7 @@ key_spdadd(struct socket *so, struct mbuf *m, const struct sadb_msghdr *mhp)
newsp->lifetime = lft ? lft->sadb_lifetime_addtime : 0;
newsp->validtime = lft ? lft->sadb_lifetime_usetime : 0;
- newsp->refcnt = 1; /* do not reclaim until I say I do */
- newsp->state = IPSEC_SPSTATE_ALIVE;
- LIST_INSERT_TAIL(&V_sptree[newsp->spidx.dir], newsp, secpolicy, chain);
+ key_insertsp(newsp);
/* delete the entry in spacqtree */
if (mhp->msg->sadb_msg_type == SADB_X_SPDUPDATE) {
@@ -2104,9 +2159,7 @@ key_spddelete(struct socket *so, struct mbuf *m,
/* save policy id to buffer to be returned. */
xpl0->sadb_x_policy_id = sp->id;
- SPTREE_LOCK();
- sp->state = IPSEC_SPSTATE_DEAD;
- SPTREE_UNLOCK();
+ key_unlink(sp);
KEY_FREESP(&sp);
{
@@ -2171,9 +2224,7 @@ key_spddelete2(struct socket *so, struct mbuf *m,
return key_senderror(so, m, EINVAL);
}
- SPTREE_LOCK();
- sp->state = IPSEC_SPSTATE_DEAD;
- SPTREE_UNLOCK();
+ key_unlink(sp);
KEY_FREESP(&sp);
{
@@ -2352,8 +2403,9 @@ 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 sadb_msg *newmsg;
- struct secpolicy *sp;
+ struct secpolicy *sp, *nextsp;
u_int dir;
IPSEC_ASSERT(so != NULL, ("null socket"));
@@ -2364,11 +2416,23 @@ key_spdflush(struct socket *so, struct mbuf *m, const struct sadb_msghdr *mhp)
if (m->m_len != PFKEY_ALIGN8(sizeof(struct sadb_msg)))
return key_senderror(so, m, EINVAL);
+ TAILQ_INIT(&drainq);
+ SPTREE_WLOCK();
for (dir = 0; dir < IPSEC_DIR_MAX; dir++) {
- SPTREE_LOCK();
- LIST_FOREACH(sp, &V_sptree[dir], chain)
- sp->state = IPSEC_SPSTATE_DEAD;
- SPTREE_UNLOCK();
+ TAILQ_CONCAT(&drainq, &V_sptree[dir], chain);
+ }
+ /*
+ * We need to set state to DEAD for each policy to be sure,
+ * that another thread won't try to unlink it.
+ */
+ TAILQ_FOREACH(sp, &drainq, chain)
+ sp->state = IPSEC_SPSTATE_DEAD;
+ SPTREE_WUNLOCK();
+ sp = TAILQ_FIRST(&drainq);
+ while (sp != NULL) {
+ nextsp = TAILQ_NEXT(sp, chain);
+ KEY_FREESP(&sp);
+ sp = nextsp;
}
if (sizeof(struct sadb_msg) > m->m_len + M_TRAILINGSPACE(m)) {
@@ -2401,6 +2465,7 @@ key_spdflush(struct socket *so, struct mbuf *m, const struct sadb_msghdr *mhp)
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;
@@ -2413,20 +2478,20 @@ key_spddump(struct socket *so, struct mbuf *m, const struct sadb_msghdr *mhp)
/* search SPD entry and get buffer size. */
cnt = 0;
- SPTREE_LOCK();
+ SPTREE_RLOCK();
for (dir = 0; dir < IPSEC_DIR_MAX; dir++) {
- LIST_FOREACH(sp, &V_sptree[dir], chain) {
+ TAILQ_FOREACH(sp, &V_sptree[dir], chain) {
cnt++;
}
}
if (cnt == 0) {
- SPTREE_UNLOCK();
+ SPTREE_RUNLOCK();
return key_senderror(so, m, ENOENT);
}
for (dir = 0; dir < IPSEC_DIR_MAX; dir++) {
- LIST_FOREACH(sp, &V_sptree[dir], chain) {
+ TAILQ_FOREACH(sp, &V_sptree[dir], chain) {
--cnt;
n = key_setdumpsp(sp, SADB_X_SPDDUMP, cnt,
mhp->msg->sadb_msg_pid);
@@ -2436,7 +2501,7 @@ key_spddump(struct socket *so, struct mbuf *m, const struct sadb_msghdr *mhp)
}
}
- SPTREE_UNLOCK();
+ SPTREE_RUNLOCK();
m_freem(m);
return 0;
}
@@ -2848,7 +2913,6 @@ key_cleansav(struct secasvar *sav)
sav->tdb_xform->xf_zeroize(sav);
sav->tdb_xform = NULL;
} else {
- KASSERT(sav->iv == NULL, ("iv but no xform"));
if (sav->key_auth != NULL)
bzero(sav->key_auth->key_data, _KEYLEN(sav->key_auth));
if (sav->key_enc != NULL)
@@ -3026,7 +3090,6 @@ key_setsaval(struct secasvar *sav, struct mbuf *m,
sav->key_enc = NULL;
sav->sched = NULL;
sav->schedlen = 0;
- sav->iv = NULL;
sav->lft_c = NULL;
sav->lft_h = NULL;
sav->lft_s = NULL;
@@ -3483,6 +3546,7 @@ key_setdumpsa(struct secasvar *sav, u_int8_t type, u_int8_t satype,
}
m_cat(result, tres);
+ tres = NULL;
if (result->m_len < sizeof(struct sadb_msg)) {
result = m_pullup(result, sizeof(struct sadb_msg));
if (result == NULL)
@@ -3760,7 +3824,7 @@ key_porttosaddr(struct sockaddr *sa, u_int16_t port)
* set data into sadb_x_policy
*/
static struct mbuf *
-key_setsadbxpolicy(u_int16_t type, u_int8_t dir, u_int32_t id)
+key_setsadbxpolicy(u_int16_t type, u_int8_t dir, u_int32_t id, u_int32_t priority)
{
struct mbuf *m;
struct sadb_x_policy *p;
@@ -3780,6 +3844,7 @@ key_setsadbxpolicy(u_int16_t type, u_int8_t dir, u_int32_t id)
p->sadb_x_policy_type = type;
p->sadb_x_policy_dir = dir;
p->sadb_x_policy_id = id;
+ p->sadb_x_policy_priority = priority;
return m;
}
@@ -3871,48 +3936,19 @@ key_ismyaddr(struct sockaddr *sa)
* compare my own address for IPv6.
* 1: ours
* 0: other
- * NOTE: derived ip6_input() in KAME. This is necessary to modify more.
*/
-#include <netinet6/in6_var.h>
-
static int
key_ismyaddr6(struct sockaddr_in6 *sin6)
{
- struct in6_ifaddr *ia;
-#if 0
- struct in6_multi *in6m;
-#endif
-
- IN6_IFADDR_RLOCK();
- TAILQ_FOREACH(ia, &V_in6_ifaddrhead, ia_link) {
- if (key_sockaddrcmp((struct sockaddr *)sin6,
- (struct sockaddr *)&ia->ia_addr, 0) == 0) {
- IN6_IFADDR_RUNLOCK();
- return 1;
- }
-
-#if 0
- /*
- * XXX Multicast
- * XXX why do we care about multlicast here while we don't care
- * about IPv4 multicast??
- * XXX scope
- */
- in6m = NULL;
- IN6_LOOKUP_MULTI(sin6->sin6_addr, ia->ia_ifp, in6m);
- if (in6m) {
- IN6_IFADDR_RUNLOCK();
- return 1;
- }
-#endif
- }
- IN6_IFADDR_RUNLOCK();
+ struct in6_addr in6;
- /* loopback, just for safety */
- if (IN6_IS_ADDR_LOOPBACK(&sin6->sin6_addr))
- return 1;
+ if (!IN6_IS_SCOPE_LINKLOCAL(&sin6->sin6_addr))
+ return (in6_localip(&sin6->sin6_addr));
- return 0;
+ /* 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*/
@@ -4222,47 +4258,30 @@ key_bbcmp(const void *a1, const void *a2, u_int bits)
static void
key_flush_spd(time_t now)
{
- static u_int16_t sptree_scangen = 0;
- u_int16_t gen = sptree_scangen++;
+ SPTREE_RLOCK_TRACKER;
struct secpolicy *sp;
u_int dir;
/* SPD */
for (dir = 0; dir < IPSEC_DIR_MAX; dir++) {
restart:
- SPTREE_LOCK();
- LIST_FOREACH(sp, &V_sptree[dir], chain) {
- if (sp->scangen == gen) /* previously handled */
- continue;
- sp->scangen = gen;
- if (sp->state == IPSEC_SPSTATE_DEAD &&
- sp->refcnt == 1) {
- /*
- * Ensure that we only decrease refcnt once,
- * when we're the last consumer.
- * Directly call SP_DELREF/key_delsp instead
- * of KEY_FREESP to avoid unlocking/relocking
- * SPTREE_LOCK before key_delsp: may refcnt
- * be increased again during that time ?
- * NB: also clean entries created by
- * key_spdflush
- */
- SP_DELREF(sp);
- key_delsp(sp);
- SPTREE_UNLOCK();
- goto restart;
- }
+ SPTREE_RLOCK();
+ TAILQ_FOREACH(sp, &V_sptree[dir], chain) {
if (sp->lifetime == 0 && sp->validtime == 0)
continue;
- if ((sp->lifetime && now - sp->created > sp->lifetime)
- || (sp->validtime && now - sp->lastused > sp->validtime)) {
- sp->state = IPSEC_SPSTATE_DEAD;
- SPTREE_UNLOCK();
+ if ((sp->lifetime &&
+ now - sp->created > sp->lifetime) ||
+ (sp->validtime &&
+ now - sp->lastused > sp->validtime)) {
+ SP_ADDREF(sp);
+ SPTREE_RUNLOCK();
key_spdexpire(sp);
+ key_unlink(sp);
+ KEY_FREESP(&sp);
goto restart;
}
}
- SPTREE_UNLOCK();
+ SPTREE_RUNLOCK();
}
}
@@ -6268,7 +6287,7 @@ key_acquire(const struct secasindex *saidx, struct secpolicy *sp)
/* set sadb_x_policy */
if (sp) {
- m = key_setsadbxpolicy(sp->policy, sp->spidx.dir, sp->id);
+ m = key_setsadbxpolicy(sp->policy, sp->spidx.dir, sp->id, sp->priority);
if (!m) {
error = ENOBUFS;
goto fail;
@@ -6770,7 +6789,7 @@ key_register(struct socket *so, struct mbuf *m, const struct sadb_msghdr *mhp)
continue;
alg = (struct sadb_alg *)(mtod(n, caddr_t) + off);
alg->sadb_alg_id = i;
- alg->sadb_alg_ivlen = ealgo->blocksize;
+ alg->sadb_alg_ivlen = ealgo->ivsize;
alg->sadb_alg_minbits = _BITS(ealgo->minkey);
alg->sadb_alg_maxbits = _BITS(ealgo->maxkey);
off += PFKEY_ALIGN8(sizeof(struct sadb_alg));
@@ -7655,7 +7674,7 @@ key_init(void)
int i;
for (i = 0; i < IPSEC_DIR_MAX; i++)
- LIST_INIT(&V_sptree[i]);
+ TAILQ_INIT(&V_sptree[i]);
LIST_INIT(&V_sahtree);
@@ -7665,10 +7684,6 @@ key_init(void)
LIST_INIT(&V_acqtree);
LIST_INIT(&V_spacqtree);
- /* system default */
- V_ip4_def_policy.policy = IPSEC_POLICY_NONE;
- V_ip4_def_policy.refcnt++; /*never reclaim this*/
-
if (!IS_DEFAULT_VNET(curvnet))
return;
@@ -7685,13 +7700,15 @@ key_init(void)
/* initialize key statistics */
keystat.getspi_count = 1;
- printf("IPsec: Initialized Security Association Processing.\n");
+ if (bootverbose)
+ printf("IPsec: Initialized Security Association Processing.\n");
}
#ifdef VIMAGE
void
key_destroy(void)
{
+ TAILQ_HEAD(, secpolicy) drainq;
struct secpolicy *sp, *nextsp;
struct secacq *acq, *nextacq;
struct secspacq *spacq, *nextspacq;
@@ -7699,18 +7716,18 @@ key_destroy(void)
struct secreg *reg;
int i;
- SPTREE_LOCK();
+ TAILQ_INIT(&drainq);
+ SPTREE_WLOCK();
for (i = 0; i < IPSEC_DIR_MAX; i++) {
- for (sp = LIST_FIRST(&V_sptree[i]);
- sp != NULL; sp = nextsp) {
- nextsp = LIST_NEXT(sp, chain);
- if (__LIST_CHAINED(sp)) {
- LIST_REMOVE(sp, chain);
- free(sp, M_IPSEC_SP);
- }
- }
+ TAILQ_CONCAT(&drainq, &V_sptree[i], chain);
+ }
+ SPTREE_WUNLOCK();
+ sp = TAILQ_FIRST(&drainq);
+ while (sp != NULL) {
+ nextsp = TAILQ_NEXT(sp, chain);
+ KEY_FREESP(&sp);
+ sp = nextsp;
}
- SPTREE_UNLOCK();
SAHTREE_LOCK();
for (sah = LIST_FIRST(&V_sahtree); sah != NULL; sah = nextsah) {
@@ -7831,14 +7848,6 @@ key_sa_chgstate(struct secasvar *sav, u_int8_t state)
}
}
-void
-key_sa_stir_iv(struct secasvar *sav)
-{
-
- IPSEC_ASSERT(sav->iv != NULL, ("null IV"));
- key_randomfill(sav->iv, sav->ivlen);
-}
-
/*
* Take one of the kernel's security keys and convert it into a PF_KEY
* structure within an mbuf, suitable for sending up to a waiting
diff --git a/sys/netipsec/key.h b/sys/netipsec/key.h
index f197977..7564de5 100644
--- a/sys/netipsec/key.h
+++ b/sys/netipsec/key.h
@@ -76,11 +76,15 @@ extern void _key_freesp(struct secpolicy **, const char*, int);
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);
#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) \
@@ -106,7 +110,6 @@ extern void key_init(void);
extern void key_destroy(void);
#endif
extern void key_sa_recordxfer(struct secasvar *, struct mbuf *);
-extern void key_sa_stir_iv(struct secasvar *);
#ifdef IPSEC_NAT_T
u_int16_t key_portfromsaddr(struct sockaddr *);
#define KEY_PORTFROMSADDR(saddr) \
diff --git a/sys/netipsec/key_debug.c b/sys/netipsec/key_debug.c
index 2031af6..03eeeb0 100644
--- a/sys/netipsec/key_debug.c
+++ b/sys/netipsec/key_debug.c
@@ -463,8 +463,8 @@ kdebug_secpolicy(struct secpolicy *sp)
if (sp == NULL)
panic("%s: NULL pointer was passed.\n", __func__);
- printf("secpolicy{ refcnt=%u state=%u policy=%u\n",
- sp->refcnt, sp->state, sp->policy);
+ printf("secpolicy{ refcnt=%u policy=%u\n",
+ sp->refcnt, sp->policy);
kdebug_secpolicyindex(&sp->spidx);
@@ -506,6 +506,8 @@ kdebug_secpolicy(struct secpolicy *sp)
void
kdebug_secpolicyindex(struct secpolicyindex *spidx)
{
+ char buf[INET6_ADDRSTRLEN];
+
/* sanity check */
if (spidx == NULL)
panic("%s: NULL pointer was passed.\n", __func__);
@@ -513,19 +515,15 @@ kdebug_secpolicyindex(struct secpolicyindex *spidx)
printf("secpolicyindex{ dir=%u prefs=%u prefd=%u ul_proto=%u\n",
spidx->dir, spidx->prefs, spidx->prefd, spidx->ul_proto);
- ipsec_hexdump((caddr_t)&spidx->src,
- ((struct sockaddr *)&spidx->src)->sa_len);
- printf("\n");
- ipsec_hexdump((caddr_t)&spidx->dst,
- ((struct sockaddr *)&spidx->dst)->sa_len);
- printf("}\n");
-
- return;
+ printf("%s -> ", ipsec_address(&spidx->src, buf, sizeof(buf)));
+ printf("%s }\n", ipsec_address(&spidx->dst, buf, sizeof(buf)));
}
void
kdebug_secasindex(struct secasindex *saidx)
{
+ char buf[INET6_ADDRSTRLEN];
+
/* sanity check */
if (saidx == NULL)
panic("%s: NULL pointer was passed.\n", __func__);
@@ -533,14 +531,8 @@ kdebug_secasindex(struct secasindex *saidx)
printf("secasindex{ mode=%u proto=%u\n",
saidx->mode, saidx->proto);
- ipsec_hexdump((caddr_t)&saidx->src,
- ((struct sockaddr *)&saidx->src)->sa_len);
- printf("\n");
- ipsec_hexdump((caddr_t)&saidx->dst,
- ((struct sockaddr *)&saidx->dst)->sa_len);
- printf("\n");
-
- return;
+ printf("%s -> ", ipsec_address(&saidx->src, buf, sizeof(buf)));
+ printf("%s }\n", ipsec_address(&saidx->dst, buf, sizeof(buf)));
}
static void
@@ -577,11 +569,6 @@ kdebug_secasv(struct secasvar *sav)
kdebug_sadb_key((struct sadb_ext *)sav->key_auth);
if (sav->key_enc != NULL)
kdebug_sadb_key((struct sadb_ext *)sav->key_enc);
- if (sav->iv != NULL) {
- printf(" iv=");
- ipsec_hexdump(sav->iv, sav->ivlen ? sav->ivlen : 8);
- printf("\n");
- }
if (sav->replay != NULL)
kdebug_secreplay(sav->replay);
@@ -628,7 +615,7 @@ kdebug_secreplay(struct secreplay *rpl)
}
void
-kdebug_mbufhdr(struct mbuf *m)
+kdebug_mbufhdr(const struct mbuf *m)
{
/* sanity check */
if (m == NULL)
@@ -655,9 +642,9 @@ kdebug_mbufhdr(struct mbuf *m)
}
void
-kdebug_mbuf(struct mbuf *m0)
+kdebug_mbuf(const struct mbuf *m0)
{
- struct mbuf *m = m0;
+ const struct mbuf *m = m0;
int i, j;
for (j = 0; m; m = m->m_next) {
@@ -668,7 +655,7 @@ kdebug_mbuf(struct mbuf *m0)
printf("\n");
if (i % 4 == 0)
printf(" ");
- printf("%02x", mtod(m, u_char *)[i]);
+ printf("%02x", mtod(m, const u_char *)[i]);
j++;
}
printf("\n");
diff --git a/sys/netipsec/key_debug.h b/sys/netipsec/key_debug.h
index 16db921..09576bf 100644
--- a/sys/netipsec/key_debug.h
+++ b/sys/netipsec/key_debug.h
@@ -76,8 +76,8 @@ 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(struct mbuf *);
-extern void kdebug_mbuf(struct mbuf *);
+extern void kdebug_mbufhdr(const struct mbuf *);
+extern void kdebug_mbuf(const struct mbuf *);
#endif /*_KERNEL*/
struct sockaddr;
diff --git a/sys/netipsec/keydb.h b/sys/netipsec/keydb.h
index 15dbc9c..3fe28eb 100644
--- a/sys/netipsec/keydb.h
+++ b/sys/netipsec/keydb.h
@@ -122,10 +122,10 @@ struct secasvar {
struct seckey *key_auth; /* Key for Authentication */
struct seckey *key_enc; /* Key for Encryption */
- caddr_t iv; /* Initilization Vector */
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 */
@@ -163,6 +163,12 @@ struct secasvar {
#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 SAV_ISGCM(_sav) \
+ ((_sav)->alg_enc == SADB_X_EALG_AESGCM8 || \
+ (_sav)->alg_enc == SADB_X_EALG_AESGCM12 || \
+ (_sav)->alg_enc == SADB_X_EALG_AESGCM16)
+#define SAV_ISCTR(_sav) ((_sav)->alg_enc == SADB_X_EALG_AESCTR)
+#define SAV_ISCTRORGCM(_sav) (SAV_ISCTR((_sav)) || SAV_ISGCM((_sav)))
/* replay prevention */
struct secreplay {
diff --git a/sys/netipsec/xform.h b/sys/netipsec/xform.h
index e389cab..fee457b 100644
--- a/sys/netipsec/xform.h
+++ b/sys/netipsec/xform.h
@@ -83,7 +83,7 @@ struct ipescrequest;
struct xformsw {
u_short xf_type; /* xform ID */
-#define XF_IP4 1 /* IP inside IP */
+#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 */
@@ -105,15 +105,10 @@ struct xformsw {
#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);
struct cryptoini;
-/* XF_IP4 */
-extern int ip4_input6(struct mbuf **m, int *offp, int proto);
-extern void ip4_input(struct mbuf *m, int);
-extern int ipip_output(struct mbuf *, struct ipsecrequest *,
- struct mbuf **, int, int);
-
/* XF_AH */
extern int ah_init0(struct secasvar *, struct xformsw *, struct cryptoini *);
extern int ah_zeroize(struct secasvar *sav);
diff --git a/sys/netipsec/xform_ah.c b/sys/netipsec/xform_ah.c
index afa452c..0512d21 100644
--- a/sys/netipsec/xform_ah.c
+++ b/sys/netipsec/xform_ah.c
@@ -80,11 +80,11 @@
(((sav)->flags & SADB_X_EXT_OLD) ? \
sizeof (struct ah) : sizeof (struct ah) + sizeof (u_int32_t))
/*
- * Return authenticator size in bytes. The old protocol is known
- * to use a fixed 16-byte authenticator. The new algorithm use 12-byte
- * authenticator.
+ * Return authenticator size in bytes, based on a field in the
+ * algorithm descriptor.
*/
-#define AUTHSIZE(sav) ah_authsize(sav)
+#define AUTHSIZE(sav) ((sav->flags & SADB_X_EXT_OLD) ? 16 : \
+ xform_ah_authsize((sav)->tdb_authalgxform))
VNET_DEFINE(int, ah_enable) = 1; /* control flow of packets with AH */
VNET_DEFINE(int, ah_cleartos) = 1; /* clear ip_tos when doing AH calc */
@@ -110,27 +110,35 @@ static unsigned char ipseczeroes[256]; /* larger than an ip6 extension hdr */
static int ah_input_cb(struct cryptop*);
static int ah_output_cb(struct cryptop*);
-static int
-ah_authsize(struct secasvar *sav)
+int
+xform_ah_authsize(struct auth_hash *esph)
{
+ int alen;
- IPSEC_ASSERT(sav != NULL, ("%s: sav == NULL", __func__));
+ if (esph == NULL)
+ return 0;
- if (sav->flags & SADB_X_EXT_OLD)
- return 16;
+ switch (esph->type) {
+ case CRYPTO_SHA2_256_HMAC:
+ case CRYPTO_SHA2_384_HMAC:
+ case CRYPTO_SHA2_512_HMAC:
+ alen = esph->hashsize / 2; /* RFC4868 2.3 */
+ break;
+
+ case CRYPTO_AES_128_NIST_GMAC:
+ case CRYPTO_AES_192_NIST_GMAC:
+ case CRYPTO_AES_256_NIST_GMAC:
+ alen = esph->hashsize;
+ break;
- switch (sav->alg_auth) {
- case SADB_X_AALG_SHA2_256:
- return 16;
- case SADB_X_AALG_SHA2_384:
- return 24;
- case SADB_X_AALG_SHA2_512:
- return 32;
default:
- return AH_HMAC_HASHLEN;
+ alen = AH_HMAC_HASHLEN;
+ break;
}
- /* NOTREACHED */
+
+ return alen;
}
+
/*
* NB: this is public for use by the PF_KEY support.
*/
@@ -158,6 +166,12 @@ ah_algorithm_lookup(int alg)
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;
}
@@ -565,12 +579,11 @@ ah_massage_headers(struct mbuf **m0, int proto, int skip, int alg, int out)
static int
ah_input(struct mbuf *m, struct secasvar *sav, int skip, int protoff)
{
+ char buf[128];
struct auth_hash *ahx;
- struct tdb_ident *tdbi;
struct tdb_crypto *tc;
- struct m_tag *mtag;
struct newah *ah;
- int hl, rplen, authsize;
+ int hl, rplen, authsize, error;
struct cryptodesc *crda;
struct cryptop *crp;
@@ -596,7 +609,7 @@ ah_input(struct mbuf *m, struct secasvar *sav, int skip, int protoff)
if (sav->replay && !ipsec_chkreplay(ntohl(ah->ah_seq), sav)) {
AHSTAT_INC(ahs_replay);
DPRINTF(("%s: packet replay failure: %s\n", __func__,
- ipsec_logsastr(sav)));
+ ipsec_logsastr(sav, buf, sizeof(buf))));
m_freem(m);
return ENOBUFS;
}
@@ -607,10 +620,10 @@ ah_input(struct mbuf *m, struct secasvar *sav, int skip, int protoff)
authsize = AUTHSIZE(sav);
if (hl != authsize + rplen - sizeof (struct ah)) {
DPRINTF(("%s: bad authenticator length %u (expecting %lu)"
- " for packet in SA %s/%08lx\n", __func__,
- hl, (u_long) (authsize + rplen - sizeof (struct ah)),
- ipsec_address(&sav->sah->saidx.dst),
- (u_long) ntohl(sav->spi)));
+ " for packet in SA %s/%08lx\n", __func__, hl,
+ (u_long) (authsize + rplen - sizeof (struct ah)),
+ ipsec_address(&sav->sah->saidx.dst, buf, sizeof(buf)),
+ (u_long) ntohl(sav->spi)));
AHSTAT_INC(ahs_badauthl);
m_freem(m);
return EACCES;
@@ -638,27 +651,9 @@ ah_input(struct mbuf *m, struct secasvar *sav, int skip, int protoff)
crda->crd_klen = _KEYBITS(sav->key_auth);
crda->crd_key = sav->key_auth->key_data;
- /* Find out if we've already done crypto. */
- for (mtag = m_tag_find(m, PACKET_TAG_IPSEC_IN_CRYPTO_DONE, NULL);
- mtag != NULL;
- mtag = m_tag_find(m, PACKET_TAG_IPSEC_IN_CRYPTO_DONE, mtag)) {
- tdbi = (struct tdb_ident *) (mtag + 1);
- if (tdbi->proto == sav->sah->saidx.proto &&
- tdbi->spi == sav->spi &&
- !bcmp(&tdbi->dst, &sav->sah->saidx.dst,
- sizeof (union sockaddr_union)))
- break;
- }
-
/* Allocate IPsec-specific opaque crypto info. */
- if (mtag == NULL) {
- tc = (struct tdb_crypto *) malloc(sizeof (struct tdb_crypto) +
- skip + rplen + authsize, M_XDATA, M_NOWAIT|M_ZERO);
- } else {
- /* Hash verification has already been done successfully. */
- tc = (struct tdb_crypto *) malloc(sizeof (struct tdb_crypto),
- M_XDATA, M_NOWAIT|M_ZERO);
- }
+ 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__));
AHSTAT_INC(ahs_crypto);
@@ -667,29 +662,24 @@ ah_input(struct mbuf *m, struct secasvar *sav, int skip, int protoff)
return ENOBUFS;
}
- /* Only save information if crypto processing is needed. */
- if (mtag == NULL) {
- int error;
+ /*
+ * Save the authenticator, the skipped portion of the packet,
+ * and the AH header.
+ */
+ m_copydata(m, 0, skip + rplen + authsize, (caddr_t)(tc+1));
+
+ /* Zeroize the authenticator on the packet. */
+ m_copyback(m, skip + rplen, authsize, ipseczeroes);
- /*
- * Save the authenticator, the skipped portion of the packet,
- * and the AH header.
- */
- m_copydata(m, 0, skip + rplen + authsize, (caddr_t)(tc+1));
-
- /* Zeroize the authenticator on the packet. */
- m_copyback(m, skip + rplen, authsize, ipseczeroes);
-
- /* "Massage" the packet headers for crypto processing. */
- error = ah_massage_headers(&m, sav->sah->saidx.dst.sa.sa_family,
- skip, ahx->type, 0);
- if (error != 0) {
- /* NB: mbuf is free'd by ah_massage_headers */
- AHSTAT_INC(ahs_hdrops);
- free(tc, M_XDATA);
- crypto_freereq(crp);
- return error;
- }
+ /* "Massage" the packet headers for crypto processing. */
+ error = ah_massage_headers(&m, sav->sah->saidx.dst.sa.sa_family,
+ skip, ahx->type, 0);
+ if (error != 0) {
+ /* NB: mbuf is free'd by ah_massage_headers */
+ AHSTAT_INC(ahs_hdrops);
+ free(tc, M_XDATA);
+ crypto_freereq(crp);
+ return (error);
}
/* Crypto operation descriptor. */
@@ -707,14 +697,9 @@ ah_input(struct mbuf *m, struct secasvar *sav, int skip, int protoff)
tc->tc_nxt = ah->ah_nxt;
tc->tc_protoff = protoff;
tc->tc_skip = skip;
- tc->tc_ptr = (caddr_t) mtag; /* Save the mtag we've identified. */
KEY_ADDREFSA(sav);
tc->tc_sav = sav;
-
- if (mtag == NULL)
- return crypto_dispatch(crp);
- else
- return ah_input_cb(crp);
+ return (crypto_dispatch(crp));
}
/*
@@ -723,13 +708,13 @@ 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;
unsigned char calc[AH_ALEN_MAX];
struct mbuf *m;
struct cryptodesc *crd;
struct auth_hash *ahx;
struct tdb_crypto *tc;
- struct m_tag *mtag;
struct secasvar *sav;
struct secasindex *saidx;
u_int8_t nxt;
@@ -743,7 +728,6 @@ ah_input_cb(struct cryptop *crp)
skip = tc->tc_skip;
nxt = tc->tc_nxt;
protoff = tc->tc_protoff;
- mtag = (struct m_tag *) tc->tc_ptr;
m = (struct mbuf *) crp->crp_buf;
sav = tc->tc_sav;
@@ -789,34 +773,22 @@ ah_input_cb(struct cryptop *crp)
/* Copy authenticator off the packet. */
m_copydata(m, skip + rplen, authsize, calc);
- /*
- * If we have an mtag, we don't need to verify the authenticator --
- * it has been verified by an IPsec-aware NIC.
- */
- if (mtag == NULL) {
- ptr = (caddr_t) (tc + 1);
-
- /* Verify authenticator. */
- if (bcmp(ptr + skip + rplen, calc, authsize)) {
- DPRINTF(("%s: authentication hash mismatch for packet "
- "in SA %s/%08lx\n", __func__,
- ipsec_address(&saidx->dst),
- (u_long) ntohl(sav->spi)));
- AHSTAT_INC(ahs_badauth);
- error = EACCES;
- goto bad;
- }
-
- /* Fix the Next Protocol field. */
- ((u_int8_t *) ptr)[protoff] = nxt;
-
- /* Copyback the saved (uncooked) network headers. */
- m_copyback(m, 0, skip, ptr);
- } else {
- /* Fix the Next Protocol field. */
- m_copyback(m, protoff, sizeof(u_int8_t), &nxt);
+ /* Verify authenticator. */
+ ptr = (caddr_t) (tc + 1);
+ if (timingsafe_bcmp(ptr + skip + rplen, calc, authsize)) {
+ DPRINTF(("%s: authentication hash mismatch for packet "
+ "in SA %s/%08lx\n", __func__,
+ ipsec_address(&saidx->dst, buf, sizeof(buf)),
+ (u_long) ntohl(sav->spi)));
+ AHSTAT_INC(ahs_badauth);
+ error = EACCES;
+ goto bad;
}
+ /* Fix the Next Protocol field. */
+ ((u_int8_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 */
/*
@@ -845,8 +817,8 @@ ah_input_cb(struct cryptop *crp)
error = m_striphdr(m, skip, rplen + authsize);
if (error) {
DPRINTF(("%s: mangled mbuf chain for SA %s/%08lx\n", __func__,
- ipsec_address(&saidx->dst), (u_long) ntohl(sav->spi)));
-
+ ipsec_address(&saidx->dst, buf, sizeof(buf)),
+ (u_long) ntohl(sav->spi)));
AHSTAT_INC(ahs_hdrops);
goto bad;
}
@@ -854,12 +826,12 @@ ah_input_cb(struct cryptop *crp)
switch (saidx->dst.sa.sa_family) {
#ifdef INET6
case AF_INET6:
- error = ipsec6_common_input_cb(m, sav, skip, protoff, mtag);
+ error = ipsec6_common_input_cb(m, sav, skip, protoff);
break;
#endif
#ifdef INET
case AF_INET:
- error = ipsec4_common_input_cb(m, sav, skip, protoff, mtag);
+ error = ipsec4_common_input_cb(m, sav, skip, protoff);
break;
#endif
default:
@@ -885,13 +857,10 @@ bad:
* AH output routine, called by ipsec[46]_process_packet().
*/
static int
-ah_output(
- struct mbuf *m,
- struct ipsecrequest *isr,
- struct mbuf **mp,
- int skip,
- int protoff)
+ah_output(struct mbuf *m, struct ipsecrequest *isr, struct mbuf **mp,
+ int skip, int protoff)
{
+ char buf[INET6_ADDRSTRLEN];
struct secasvar *sav;
struct auth_hash *ahx;
struct cryptodesc *crda;
@@ -929,7 +898,7 @@ ah_output(
DPRINTF(("%s: unknown/unsupported protocol family %u, "
"SA %s/%08lx\n", __func__,
sav->sah->saidx.dst.sa.sa_family,
- ipsec_address(&sav->sah->saidx.dst),
+ ipsec_address(&sav->sah->saidx.dst, buf, sizeof(buf)),
(u_long) ntohl(sav->spi)));
AHSTAT_INC(ahs_nopf);
error = EPFNOSUPPORT;
@@ -939,7 +908,7 @@ ah_output(
if (rplen + authsize + m->m_pkthdr.len > maxpacketsize) {
DPRINTF(("%s: packet in SA %s/%08lx got too big "
"(len %u, max len %u)\n", __func__,
- ipsec_address(&sav->sah->saidx.dst),
+ ipsec_address(&sav->sah->saidx.dst, buf, sizeof(buf)),
(u_long) ntohl(sav->spi),
rplen + authsize + m->m_pkthdr.len, maxpacketsize));
AHSTAT_INC(ahs_toobig);
@@ -953,7 +922,7 @@ ah_output(
m = m_unshare(m, M_NOWAIT);
if (m == NULL) {
DPRINTF(("%s: cannot clone mbuf chain, SA %s/%08lx\n", __func__,
- ipsec_address(&sav->sah->saidx.dst),
+ ipsec_address(&sav->sah->saidx.dst, buf, sizeof(buf)),
(u_long) ntohl(sav->spi)));
AHSTAT_INC(ahs_hdrops);
error = ENOBUFS;
@@ -966,7 +935,7 @@ ah_output(
DPRINTF(("%s: failed to inject %u byte AH header for SA "
"%s/%08lx\n", __func__,
rplen + authsize,
- ipsec_address(&sav->sah->saidx.dst),
+ ipsec_address(&sav->sah->saidx.dst, buf, sizeof(buf)),
(u_long) ntohl(sav->spi)));
AHSTAT_INC(ahs_hdrops); /*XXX differs from openbsd */
error = ENOBUFS;
@@ -993,9 +962,8 @@ ah_output(
if (sav->replay->count == ~0 &&
(sav->flags & SADB_X_EXT_CYCSEQ) == 0) {
DPRINTF(("%s: replay counter wrapped for SA %s/%08lx\n",
- __func__,
- ipsec_address(&sav->sah->saidx.dst),
- (u_long) ntohl(sav->spi)));
+ __func__, ipsec_address(&sav->sah->saidx.dst, buf,
+ sizeof(buf)), (u_long) ntohl(sav->spi)));
AHSTAT_INC(ahs_wrap);
error = EINVAL;
goto bad;
@@ -1098,6 +1066,7 @@ ah_output(
crp->crp_opaque = (caddr_t) tc;
/* These are passed as-is to the callback. */
+ key_addref(isr->sp);
tc->tc_isr = isr;
KEY_ADDREFSA(sav);
tc->tc_sav = sav;
@@ -1135,6 +1104,7 @@ ah_output_cb(struct cryptop *crp)
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. */
@@ -1198,16 +1168,18 @@ ah_output_cb(struct cryptop *crp)
error = ipsec_process_done(m, isr);
KEY_FREESAV(&sav);
IPSECREQUEST_UNLOCK(isr);
- return error;
+ KEY_FREESP(&isr->sp);
+ return (error);
bad:
if (sav)
KEY_FREESAV(&sav);
IPSECREQUEST_UNLOCK(isr);
+ KEY_FREESP(&isr->sp);
if (m)
m_freem(m);
free(tc, M_XDATA);
crypto_freereq(crp);
- return error;
+ return (error);
}
static struct xformsw ah_xformsw = {
diff --git a/sys/netipsec/xform_esp.c b/sys/netipsec/xform_esp.c
index 90f6d56..982a7a3 100644
--- a/sys/netipsec/xform_esp.c
+++ b/sys/netipsec/xform_esp.c
@@ -46,6 +46,9 @@
#include <sys/kernel.h>
#include <sys/random.h>
#include <sys/sysctl.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <machine/atomic.h>
#include <net/if.h>
#include <net/vnet.h>
@@ -113,12 +116,16 @@ esp_algorithm_lookup(int alg)
return &enc_xform_blf;
case SADB_X_EALG_CAST128CBC:
return &enc_xform_cast5;
- case SADB_X_EALG_SKIPJACK:
- return &enc_xform_skipjack;
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;
}
@@ -176,12 +183,14 @@ esp_init(struct secasvar *sav, struct xformsw *xsp)
__func__, txform->name));
return EINVAL;
}
- if ((sav->flags&(SADB_X_EXT_OLD|SADB_X_EXT_IV4B)) == SADB_X_EXT_IV4B) {
+ if ((sav->flags & (SADB_X_EXT_OLD | SADB_X_EXT_IV4B)) ==
+ SADB_X_EXT_IV4B) {
DPRINTF(("%s: 4-byte IV not supported with protocol\n",
__func__));
return EINVAL;
}
- keylen = _KEYLEN(sav->key_enc);
+ /* subtract off the salt, RFC4106, 8.1 and RFC3686, 5.1 */
+ keylen = _KEYLEN(sav->key_enc) - SAV_ISCTRORGCM(sav) * 4;
if (txform->minkey > keylen || keylen > txform->maxkey) {
DPRINTF(("%s: invalid key length %u, must be in the range "
"[%u..%u] for algorithm %s\n", __func__,
@@ -190,15 +199,10 @@ esp_init(struct secasvar *sav, struct xformsw *xsp)
return EINVAL;
}
- /*
- * NB: The null xform needs a non-zero blocksize to keep the
- * crypto code happy but if we use it to set ivlen then
- * the ESP header will be processed incorrectly. The
- * compromise is to force it to zero here.
- */
- sav->ivlen = (txform == &enc_xform_null ? 0 : txform->blocksize);
- sav->iv = (caddr_t) malloc(sav->ivlen, M_XDATA, M_WAITOK);
- key_randomfill(sav->iv, sav->ivlen); /*XXX*/
+ if (SAV_ISCTRORGCM(sav))
+ sav->ivlen = 8; /* RFC4106 3.1 and RFC3686 3.1 */
+ else
+ sav->ivlen = txform->ivsize;
/*
* Setup AH-related state.
@@ -213,12 +217,42 @@ esp_init(struct secasvar *sav, struct xformsw *xsp)
sav->tdb_xform = xsp;
sav->tdb_encalgxform = txform;
+ /*
+ * Whenever AES-GCM is used for encryption, one
+ * of the AES authentication algorithms is chosen
+ * as well, based on the key size.
+ */
+ if (sav->alg_enc == SADB_X_EALG_AESGCM16) {
+ switch (keylen) {
+ case AES_128_GMAC_KEY_LEN:
+ sav->alg_auth = SADB_X_AALG_AES128GMAC;
+ sav->tdb_authalgxform = &auth_hash_nist_gmac_aes_128;
+ break;
+ case AES_192_GMAC_KEY_LEN:
+ sav->alg_auth = SADB_X_AALG_AES192GMAC;
+ sav->tdb_authalgxform = &auth_hash_nist_gmac_aes_192;
+ break;
+ case AES_256_GMAC_KEY_LEN:
+ sav->alg_auth = SADB_X_AALG_AES256GMAC;
+ sav->tdb_authalgxform = &auth_hash_nist_gmac_aes_256;
+ break;
+ default:
+ DPRINTF(("%s: invalid key length %u"
+ "for algorithm %s\n", __func__,
+ keylen, txform->name));
+ return EINVAL;
+ }
+ bzero(&cria, sizeof(cria));
+ cria.cri_alg = sav->tdb_authalgxform->type;
+ cria.cri_key = sav->key_enc->key_data;
+ cria.cri_klen = _KEYBITS(sav->key_enc) - SAV_ISGCM(sav) * 32;
+ }
+
/* Initialize crypto session. */
- bzero(&crie, sizeof (crie));
+ bzero(&crie, sizeof(crie));
crie.cri_alg = sav->tdb_encalgxform->type;
- crie.cri_klen = _KEYBITS(sav->key_enc);
crie.cri_key = sav->key_enc->key_data;
- /* XXX Rounds ? */
+ crie.cri_klen = _KEYBITS(sav->key_enc) - SAV_ISCTRORGCM(sav) * 32;
if (sav->tdb_authalgxform && sav->tdb_encalgxform) {
/* init both auth & enc */
@@ -251,10 +285,6 @@ esp_zeroize(struct secasvar *sav)
if (sav->key_enc)
bzero(sav->key_enc->key_data, _KEYLEN(sav->key_enc));
- if (sav->iv) {
- free(sav->iv, M_XDATA);
- sav->iv = NULL;
- }
sav->tdb_encalgxform = NULL;
sav->tdb_xform = NULL;
return error;
@@ -266,14 +296,13 @@ esp_zeroize(struct secasvar *sav)
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_ident *tdbi;
struct tdb_crypto *tc;
+ uint8_t *ivp;
int plen, alen, hlen;
- struct m_tag *mtag;
struct newesp *esp;
-
struct cryptodesc *crde;
struct cryptop *crp;
@@ -288,32 +317,19 @@ esp_input(struct mbuf *m, struct secasvar *sav, int skip, int protoff)
m_freem(m);
return EINVAL;
}
-
/* XXX don't pullup, just copy header */
IP6_EXTHDR_GET(esp, struct newesp *, m, skip, sizeof (struct newesp));
esph = sav->tdb_authalgxform;
espx = sav->tdb_encalgxform;
- /* Determine the ESP header length */
+ /* Determine the ESP header and auth length */
if (sav->flags & SADB_X_EXT_OLD)
hlen = sizeof (struct esp) + sav->ivlen;
else
hlen = sizeof (struct newesp) + sav->ivlen;
- /* Authenticator hash size */
- if (esph != NULL) {
- switch (esph->type) {
- case CRYPTO_SHA2_256_HMAC:
- case CRYPTO_SHA2_384_HMAC:
- case CRYPTO_SHA2_512_HMAC:
- alen = esph->hashsize/2;
- break;
- default:
- alen = AH_HMAC_HASHLEN;
- break;
- }
- }else
- alen = 0;
+
+ alen = xform_ah_authsize(esph);
/*
* Verify payload length is multiple of encryption algorithm
@@ -326,10 +342,9 @@ esp_input(struct mbuf *m, struct secasvar *sav, int skip, int protoff)
plen = m->m_pkthdr.len - (skip + hlen + alen);
if ((plen & (espx->blocksize - 1)) || (plen <= 0)) {
DPRINTF(("%s: payload of %d octets not a multiple of %d octets,"
- " SA %s/%08lx\n", __func__,
- plen, espx->blocksize,
- ipsec_address(&sav->sah->saidx.dst),
- (u_long) ntohl(sav->spi)));
+ " SA %s/%08lx\n", __func__, plen, espx->blocksize,
+ ipsec_address(&sav->sah->saidx.dst, buf, sizeof(buf)),
+ (u_long)ntohl(sav->spi)));
ESPSTAT_INC(esps_badilen);
m_freem(m);
return EINVAL;
@@ -338,9 +353,10 @@ esp_input(struct mbuf *m, struct secasvar *sav, int skip, int protoff)
/*
* Check sequence number.
*/
- if (esph && sav->replay && !ipsec_chkreplay(ntohl(esp->esp_seq), sav)) {
+ 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))); /*XXX*/
+ ipsec_logsastr(sav, buf, sizeof(buf)))); /*XXX*/
ESPSTAT_INC(esps_replay);
m_freem(m);
return ENOBUFS; /*XXX*/
@@ -349,18 +365,6 @@ esp_input(struct mbuf *m, struct secasvar *sav, int skip, int protoff)
/* Update the counters */
ESPSTAT_ADD(esps_ibytes, m->m_pkthdr.len - (skip + hlen + alen));
- /* Find out if we've already done crypto */
- for (mtag = m_tag_find(m, PACKET_TAG_IPSEC_IN_CRYPTO_DONE, NULL);
- mtag != NULL;
- mtag = m_tag_find(m, PACKET_TAG_IPSEC_IN_CRYPTO_DONE, mtag)) {
- tdbi = (struct tdb_ident *) (mtag + 1);
- if (tdbi->proto == sav->sah->saidx.proto &&
- tdbi->spi == sav->spi &&
- !bcmp(&tdbi->dst, &sav->sah->saidx.dst,
- sizeof(union sockaddr_union)))
- break;
- }
-
/* Get crypto descriptors */
crp = crypto_getreq(esph && espx ? 2 : 1);
if (crp == NULL) {
@@ -372,12 +376,8 @@ esp_input(struct mbuf *m, struct secasvar *sav, int skip, int protoff)
}
/* Get IPsec-specific opaque pointer */
- if (esph == NULL || mtag != NULL)
- tc = (struct tdb_crypto *) malloc(sizeof(struct tdb_crypto),
- M_XDATA, M_NOWAIT|M_ZERO);
- else
- tc = (struct tdb_crypto *) malloc(sizeof(struct tdb_crypto) + alen,
- M_XDATA, M_NOWAIT|M_ZERO);
+ 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__));
@@ -386,26 +386,24 @@ esp_input(struct mbuf *m, struct secasvar *sav, int skip, int protoff)
return ENOBUFS;
}
- tc->tc_ptr = (caddr_t) mtag;
-
- if (esph) {
+ if (esph != NULL) {
struct cryptodesc *crda = crp->crp_desc;
IPSEC_ASSERT(crda != NULL, ("null ah crypto descriptor"));
/* Authentication descriptor */
crda->crd_skip = skip;
- crda->crd_len = m->m_pkthdr.len - (skip + alen);
+ if (SAV_ISGCM(sav))
+ crda->crd_len = 8; /* RFC4106 5, SPI + SN */
+ else
+ crda->crd_len = m->m_pkthdr.len - (skip + alen);
crda->crd_inject = m->m_pkthdr.len - alen;
crda->crd_alg = esph->type;
- crda->crd_key = sav->key_auth->key_data;
- crda->crd_klen = _KEYBITS(sav->key_auth);
/* Copy the authenticator */
- if (mtag == NULL)
- m_copydata(m, m->m_pkthdr.len - alen, alen,
- (caddr_t) (tc + 1));
+ m_copydata(m, m->m_pkthdr.len - alen, alen,
+ (caddr_t) (tc + 1));
/* Chain authentication request */
crde = crda->crd_next;
@@ -431,22 +429,33 @@ esp_input(struct mbuf *m, struct secasvar *sav, int skip, int protoff)
tc->tc_sav = sav;
/* Decryption descriptor */
- if (espx) {
- IPSEC_ASSERT(crde != NULL, ("null esp crypto descriptor"));
- crde->crd_skip = skip + hlen;
- crde->crd_len = m->m_pkthdr.len - (skip + hlen + alen);
- crde->crd_inject = skip + hlen - sav->ivlen;
-
- crde->crd_alg = espx->type;
- crde->crd_key = sav->key_enc->key_data;
- crde->crd_klen = _KEYBITS(sav->key_enc);
- /* XXX Rounds ? */
+ IPSEC_ASSERT(crde != NULL, ("null esp crypto descriptor"));
+ crde->crd_skip = skip + hlen;
+ crde->crd_len = m->m_pkthdr.len - (skip + hlen + alen);
+ crde->crd_inject = skip + hlen - sav->ivlen;
+
+ if (SAV_ISCTRORGCM(sav)) {
+ ivp = &crde->crd_iv[0];
+
+ /* GCM IV Format: RFC4106 4 */
+ /* CTR IV Format: RFC3686 4 */
+ /* Salt is last four bytes of key, RFC4106 8.1 */
+ /* Nonce is last four bytes of key, RFC3686 5.1 */
+ memcpy(ivp, sav->key_enc->key_data +
+ _KEYLEN(sav->key_enc) - 4, 4);
+
+ if (SAV_ISCTR(sav)) {
+ /* Initial block counter is 1, RFC3686 4 */
+ be32enc(&ivp[sav->ivlen + 4], 1);
+ }
+
+ m_copydata(m, skip + hlen - sav->ivlen, sav->ivlen, &ivp[4]);
+ crde->crd_flags |= CRD_F_IV_EXPLICIT;
}
- if (mtag == NULL)
- return crypto_dispatch(crp);
- else
- return esp_input_cb(crp);
+ crde->crd_alg = espx->type;
+
+ return (crypto_dispatch(crp));
}
/*
@@ -455,6 +464,7 @@ esp_input(struct mbuf *m, struct secasvar *sav, int skip, int protoff)
static int
esp_input_cb(struct cryptop *crp)
{
+ char buf[128];
u_int8_t lastthree[3], aalg[AH_HMAC_MAXHASHLEN];
int hlen, skip, protoff, error, alen;
struct mbuf *m;
@@ -462,7 +472,6 @@ esp_input_cb(struct cryptop *crp)
struct auth_hash *esph;
struct enc_xform *espx;
struct tdb_crypto *tc;
- struct m_tag *mtag;
struct secasvar *sav;
struct secasindex *saidx;
caddr_t ptr;
@@ -474,7 +483,6 @@ esp_input_cb(struct cryptop *crp)
IPSEC_ASSERT(tc != NULL, ("null opaque crypto data area!"));
skip = tc->tc_skip;
protoff = tc->tc_protoff;
- mtag = (struct m_tag *) tc->tc_ptr;
m = (struct mbuf *) crp->crp_buf;
sav = tc->tc_sav;
@@ -514,40 +522,21 @@ esp_input_cb(struct cryptop *crp)
/* If authentication was performed, check now. */
if (esph != NULL) {
- switch (esph->type) {
- case CRYPTO_SHA2_256_HMAC:
- case CRYPTO_SHA2_384_HMAC:
- case CRYPTO_SHA2_512_HMAC:
- alen = esph->hashsize/2;
- break;
- default:
- alen = AH_HMAC_HASHLEN;
- break;
- }
- /*
- * If we have a tag, it means an IPsec-aware NIC did
- * the verification for us. Otherwise we need to
- * check the authentication calculation.
- */
+ alen = xform_ah_authsize(esph);
AHSTAT_INC(ahs_hist[sav->alg_auth]);
- if (mtag == NULL) {
- /* Copy the authenticator from the packet */
- m_copydata(m, m->m_pkthdr.len - alen,
- alen, aalg);
-
- ptr = (caddr_t) (tc + 1);
-
- /* Verify authenticator */
- if (bcmp(ptr, aalg, alen) != 0) {
- DPRINTF(("%s: "
- "authentication hash mismatch for packet in SA %s/%08lx\n",
- __func__,
- ipsec_address(&saidx->dst),
- (u_long) ntohl(sav->spi)));
- ESPSTAT_INC(esps_badauth);
- error = EACCES;
- goto bad;
- }
+ /* Copy the authenticator from the packet */
+ m_copydata(m, m->m_pkthdr.len - alen, alen, aalg);
+ ptr = (caddr_t) (tc + 1);
+
+ /* Verify authenticator */
+ if (timingsafe_bcmp(ptr, aalg, alen) != 0) {
+ DPRINTF(("%s: authentication hash mismatch for "
+ "packet in SA %s/%08lx\n", __func__,
+ ipsec_address(&saidx->dst, buf, sizeof(buf)),
+ (u_long) ntohl(sav->spi)));
+ ESPSTAT_INC(esps_badauth);
+ error = EACCES;
+ goto bad;
}
/* Remove trailing authenticator */
@@ -573,7 +562,7 @@ esp_input_cb(struct cryptop *crp)
sizeof (seq), (caddr_t) &seq);
if (ipsec_updatereplay(ntohl(seq), sav)) {
DPRINTF(("%s: packet replay check for %s\n", __func__,
- ipsec_logsastr(sav)));
+ ipsec_logsastr(sav, buf, sizeof(buf))));
ESPSTAT_INC(esps_replay);
error = ENOBUFS;
goto bad;
@@ -591,7 +580,7 @@ esp_input_cb(struct cryptop *crp)
if (error) {
ESPSTAT_INC(esps_hdrops);
DPRINTF(("%s: bad mbuf chain, SA %s/%08lx\n", __func__,
- ipsec_address(&sav->sah->saidx.dst),
+ ipsec_address(&sav->sah->saidx.dst, buf, sizeof(buf)),
(u_long) ntohl(sav->spi)));
goto bad;
}
@@ -603,10 +592,10 @@ esp_input_cb(struct cryptop *crp)
if (lastthree[1] + 2 > m->m_pkthdr.len - skip) {
ESPSTAT_INC(esps_badilen);
DPRINTF(("%s: invalid padding length %d for %u byte packet "
- "in SA %s/%08lx\n", __func__,
- lastthree[1], m->m_pkthdr.len - skip,
- ipsec_address(&sav->sah->saidx.dst),
- (u_long) ntohl(sav->spi)));
+ "in SA %s/%08lx\n", __func__, lastthree[1],
+ m->m_pkthdr.len - skip,
+ ipsec_address(&sav->sah->saidx.dst, buf, sizeof(buf)),
+ (u_long) ntohl(sav->spi)));
error = EINVAL;
goto bad;
}
@@ -616,9 +605,9 @@ esp_input_cb(struct cryptop *crp)
if (lastthree[1] != lastthree[0] && lastthree[1] != 0) {
ESPSTAT_INC(esps_badenc);
DPRINTF(("%s: decryption failed for packet in "
- "SA %s/%08lx\n", __func__,
- ipsec_address(&sav->sah->saidx.dst),
- (u_long) ntohl(sav->spi)));
+ "SA %s/%08lx\n", __func__, ipsec_address(
+ &sav->sah->saidx.dst, buf, sizeof(buf)),
+ (u_long) ntohl(sav->spi)));
error = EINVAL;
goto bad;
}
@@ -633,12 +622,12 @@ esp_input_cb(struct cryptop *crp)
switch (saidx->dst.sa.sa_family) {
#ifdef INET6
case AF_INET6:
- error = ipsec6_common_input_cb(m, sav, skip, protoff, mtag);
+ error = ipsec6_common_input_cb(m, sav, skip, protoff);
break;
#endif
#ifdef INET
case AF_INET:
- error = ipsec4_common_input_cb(m, sav, skip, protoff, mtag);
+ error = ipsec4_common_input_cb(m, sav, skip, protoff);
break;
#endif
default:
@@ -664,16 +653,14 @@ bad:
* ESP output routine, called by ipsec[46]_process_packet().
*/
static int
-esp_output(
- struct mbuf *m,
- struct ipsecrequest *isr,
- struct mbuf **mp,
- int skip,
- int protoff
-)
+esp_output(struct mbuf *m, struct ipsecrequest *isr, struct mbuf **mp,
+ 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;
@@ -699,27 +686,14 @@ esp_output(
rlen = m->m_pkthdr.len - skip; /* Raw payload length. */
/*
- * NB: The null encoding transform has a blocksize of 4
- * so that headers are properly aligned.
+ * RFC4303 2.4 Requires 4 byte alignment.
*/
- blks = espx->blocksize; /* IV blocksize */
+ blks = MAX(4, espx->blocksize); /* Cipher blocksize */
/* XXX clamp padding length a la KAME??? */
padding = ((blks - ((rlen + 2) % blks)) % blks) + 2;
- if (esph)
- switch (esph->type) {
- case CRYPTO_SHA2_256_HMAC:
- case CRYPTO_SHA2_384_HMAC:
- case CRYPTO_SHA2_512_HMAC:
- alen = esph->hashsize/2;
- break;
- default:
- alen = AH_HMAC_HASHLEN;
- break;
- }
- else
- alen = 0;
+ alen = xform_ah_authsize(esph);
ESPSTAT_INC(esps_output);
@@ -739,16 +713,19 @@ esp_output(
default:
DPRINTF(("%s: unknown/unsupported protocol "
"family %d, SA %s/%08lx\n", __func__,
- saidx->dst.sa.sa_family, ipsec_address(&saidx->dst),
- (u_long) ntohl(sav->spi)));
+ saidx->dst.sa.sa_family, ipsec_address(&saidx->dst,
+ buf, sizeof(buf)), (u_long) ntohl(sav->spi)));
ESPSTAT_INC(esps_nopf);
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));
if (skip + hlen + rlen + padding + alen > maxpacketsize) {
DPRINTF(("%s: packet in SA %s/%08lx got too big "
"(len %u, max len %u)\n", __func__,
- ipsec_address(&saidx->dst), (u_long) ntohl(sav->spi),
+ ipsec_address(&saidx->dst, buf, sizeof(buf)),
+ (u_long) ntohl(sav->spi),
skip + hlen + rlen + padding + alen, maxpacketsize));
ESPSTAT_INC(esps_toobig);
error = EMSGSIZE;
@@ -761,7 +738,8 @@ esp_output(
m = m_unshare(m, M_NOWAIT);
if (m == NULL) {
DPRINTF(("%s: cannot clone mbuf chain, SA %s/%08lx\n", __func__,
- ipsec_address(&saidx->dst), (u_long) ntohl(sav->spi)));
+ ipsec_address(&saidx->dst, buf, sizeof(buf)),
+ (u_long) ntohl(sav->spi)));
ESPSTAT_INC(esps_hdrops);
error = ENOBUFS;
goto bad;
@@ -771,8 +749,8 @@ esp_output(
mo = m_makespace(m, skip, hlen, &roff);
if (mo == NULL) {
DPRINTF(("%s: %u byte ESP hdr inject failed for SA %s/%08lx\n",
- __func__, hlen, ipsec_address(&saidx->dst),
- (u_long) ntohl(sav->spi)));
+ __func__, hlen, ipsec_address(&saidx->dst, buf,
+ sizeof(buf)), (u_long) ntohl(sav->spi)));
ESPSTAT_INC(esps_hdrops); /* XXX diffs from openbsd */
error = ENOBUFS;
goto bad;
@@ -801,7 +779,8 @@ esp_output(
pad = (u_char *) m_pad(m, padding + alen);
if (pad == NULL) {
DPRINTF(("%s: m_pad failed for SA %s/%08lx\n", __func__,
- ipsec_address(&saidx->dst), (u_long) ntohl(sav->spi)));
+ ipsec_address(&saidx->dst, buf, sizeof(buf)),
+ (u_long) ntohl(sav->spi)));
m = NULL; /* NB: free'd by m_pad */
error = ENOBUFS;
goto bad;
@@ -833,7 +812,7 @@ esp_output(
m_copyback(m, protoff, sizeof(u_int8_t), (u_char *) &prot);
/* Get crypto descriptors. */
- crp = crypto_getreq(esph && espx ? 2 : 1);
+ crp = crypto_getreq(esph != NULL ? 2 : 1);
if (crp == NULL) {
DPRINTF(("%s: failed to acquire crypto descriptors\n",
__func__));
@@ -842,27 +821,9 @@ esp_output(
goto bad;
}
- if (espx) {
- crde = crp->crp_desc;
- crda = crde->crd_next;
-
- /* Encryption descriptor. */
- crde->crd_skip = skip + hlen;
- crde->crd_len = m->m_pkthdr.len - (skip + hlen + alen);
- crde->crd_flags = CRD_F_ENCRYPT;
- crde->crd_inject = skip + hlen - sav->ivlen;
-
- /* Encryption operation. */
- crde->crd_alg = espx->type;
- crde->crd_key = sav->key_enc->key_data;
- crde->crd_klen = _KEYBITS(sav->key_enc);
- /* XXX Rounds ? */
- } else
- crda = crp->crp_desc;
-
/* IPsec-specific opaque crypto info. */
tc = (struct tdb_crypto *) malloc(sizeof(struct tdb_crypto),
- M_XDATA, M_NOWAIT|M_ZERO);
+ M_XDATA, M_NOWAIT|M_ZERO);
if (tc == NULL) {
crypto_freereq(crp);
DPRINTF(("%s: failed to allocate tdb_crypto\n", __func__));
@@ -871,7 +832,42 @@ esp_output(
goto bad;
}
+ crde = crp->crp_desc;
+ crda = crde->crd_next;
+
+ /* Encryption descriptor. */
+ crde->crd_skip = skip + hlen;
+ crde->crd_len = m->m_pkthdr.len - (skip + hlen + alen);
+ crde->crd_flags = CRD_F_ENCRYPT;
+ crde->crd_inject = skip + hlen - sav->ivlen;
+
+ /* Encryption operation. */
+ crde->crd_alg = espx->type;
+ if (SAV_ISCTRORGCM(sav)) {
+ ivp = &crde->crd_iv[0];
+
+ /* GCM IV Format: RFC4106 4 */
+ /* CTR IV Format: RFC3686 4 */
+ /* Salt is last four bytes of key, RFC4106 8.1 */
+ /* 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 */
+ be32enc(&ivp[sav->ivlen + 4], 1);
+ }
+
+ m_copyback(m, skip + hlen - sav->ivlen, sav->ivlen, &ivp[4]);
+ crde->crd_flags |= CRD_F_IV_EXPLICIT|CRD_F_IV_PRESENT;
+ }
+
/* Callback parameters */
+ key_addref(isr->sp);
tc->tc_isr = isr;
KEY_ADDREFSA(sav);
tc->tc_sav = sav;
@@ -889,14 +885,13 @@ esp_output(
if (esph) {
/* Authentication descriptor. */
+ crda->crd_alg = esph->type;
crda->crd_skip = skip;
- crda->crd_len = m->m_pkthdr.len - (skip + alen);
+ if (SAV_ISGCM(sav))
+ crda->crd_len = 8; /* RFC4106 5, SPI + SN */
+ else
+ crda->crd_len = m->m_pkthdr.len - (skip + alen);
crda->crd_inject = m->m_pkthdr.len - alen;
-
- /* Authentication operation. */
- crda->crd_alg = esph->type;
- crda->crd_key = sav->key_auth->key_data;
- crda->crd_klen = _KEYBITS(sav->key_auth);
}
return crypto_dispatch(crp);
@@ -912,6 +907,7 @@ bad:
static int
esp_output_cb(struct cryptop *crp)
{
+ char buf[INET6_ADDRSTRLEN];
struct tdb_crypto *tc;
struct ipsecrequest *isr;
struct secasvar *sav;
@@ -923,13 +919,15 @@ esp_output_cb(struct cryptop *crp)
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. */
+
+ /* 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),
+ __func__, ipsec_address(&tc->tc_dst, buf, sizeof(buf)),
(u_long) ntohl(tc->tc_spi), tc->tc_proto));
error = ENOBUFS; /*XXX*/
goto bad;
@@ -981,16 +979,7 @@ esp_output_cb(struct cryptop *crp)
if (esph != NULL) {
int alen;
- switch (esph->type) {
- case CRYPTO_SHA2_256_HMAC:
- case CRYPTO_SHA2_384_HMAC:
- case CRYPTO_SHA2_512_HMAC:
- alen = esph->hashsize/2;
- break;
- default:
- alen = AH_HMAC_HASHLEN;
- break;
- }
+ alen = xform_ah_authsize(esph);
m_copyback(m, m->m_pkthdr.len - alen,
alen, ipseczeroes);
}
@@ -1001,16 +990,18 @@ esp_output_cb(struct cryptop *crp)
error = ipsec_process_done(m, isr);
KEY_FREESAV(&sav);
IPSECREQUEST_UNLOCK(isr);
- return error;
+ KEY_FREESP(&isr->sp);
+ return (error);
bad:
if (sav)
KEY_FREESAV(&sav);
IPSECREQUEST_UNLOCK(isr);
+ KEY_FREESP(&isr->sp);
if (m)
m_freem(m);
free(tc, M_XDATA);
crypto_freereq(crp);
- return error;
+ return (error);
}
static struct xformsw esp_xformsw = {
diff --git a/sys/netipsec/xform_ipcomp.c b/sys/netipsec/xform_ipcomp.c
index 6d95250..5c7b569 100644
--- a/sys/netipsec/xform_ipcomp.c
+++ b/sys/netipsec/xform_ipcomp.c
@@ -47,7 +47,9 @@
#include <netinet/in_systm.h>
#include <netinet/ip.h>
#include <netinet/ip_var.h>
+#include <netinet/ip_encap.h>
+#include <net/netisr.h>
#include <net/vnet.h>
#include <netipsec/ipsec.h>
@@ -55,6 +57,8 @@
#ifdef INET6
#include <netinet/ip6.h>
+#include <netinet6/ip6_var.h>
+#include <netinet6/ip6protosw.h>
#include <netipsec/ipsec6.h>
#endif
@@ -62,7 +66,6 @@
#include <netipsec/ipcomp_var.h>
#include <netipsec/key.h>
-#include <netipsec/key_debug.h>
#include <opencrypto/cryptodev.h>
#include <opencrypto/deflate.h>
@@ -99,6 +102,70 @@ ipcomp_algorithm_lookup(int alg)
}
/*
+ * RFC 3173 p 2.2. Non-Expansion Policy:
+ * If the total size of a compressed payload and the IPComp header, as
+ * defined in section 3, is not smaller than the size of the original
+ * payload, the IP datagram MUST be sent in the original non-compressed
+ * form.
+ *
+ * When we use IPComp in tunnel mode, for small packets we will receive
+ * encapsulated IP-IP datagrams without any compression and without IPComp
+ * header.
+ */
+static int
+ipcomp_encapcheck(union sockaddr_union *src, union sockaddr_union *dst)
+{
+ struct secasvar *sav;
+
+ sav = KEY_ALLOCSA_TUNNEL(src, dst, IPPROTO_IPCOMP);
+ if (sav == NULL)
+ return (0);
+ KEY_FREESAV(&sav);
+
+ if (src->sa.sa_family == AF_INET)
+ return (sizeof(struct in_addr) << 4);
+ else
+ return (sizeof(struct in6_addr) << 4);
+}
+
+static int
+ipcomp6_nonexp_input(struct mbuf **mp, int *offp, int proto)
+{
+ int isr;
+
+ switch (proto) {
+#ifdef INET
+ case IPPROTO_IPV4:
+ isr = NETISR_IP;
+ break;
+#endif
+#ifdef INET6
+ case IPPROTO_IPV6:
+ isr = NETISR_IPV6;
+ break;
+#endif
+ default:
+ IPCOMPSTAT_INC(ipcomps_nopf);
+ m_freem(*mp);
+ return (IPPROTO_DONE);
+ }
+ m_adj(*mp, *offp);
+ IPCOMPSTAT_ADD(ipcomps_ibytes, (*mp)->m_pkthdr.len);
+ IPCOMPSTAT_INC(ipcomps_input);
+ netisr_dispatch(isr, *mp);
+ return (IPPROTO_DONE);
+}
+
+static void
+ipcomp_nonexp_input(struct mbuf *m, int hlen)
+{
+ int off;
+
+ off = hlen;
+ ipcomp6_nonexp_input(&m, &off, AF_INET);
+}
+
+/*
* ipcomp_init() is called when an CPI is being set up.
*/
static int
@@ -224,10 +291,10 @@ ipcomp_input(struct mbuf *m, struct secasvar *sav, int skip, int protoff)
static int
ipcomp_input_cb(struct cryptop *crp)
{
+ char buf[INET6_ADDRSTRLEN];
struct cryptodesc *crd;
struct tdb_crypto *tc;
int skip, protoff;
- struct mtag *mtag;
struct mbuf *m;
struct secasvar *sav;
struct secasindex *saidx;
@@ -241,7 +308,6 @@ ipcomp_input_cb(struct cryptop *crp)
IPSEC_ASSERT(tc != NULL, ("null opaque crypto data area!"));
skip = tc->tc_skip;
protoff = tc->tc_protoff;
- mtag = (struct mtag *) tc->tc_ptr;
m = (struct mbuf *) crp->crp_buf;
sav = tc->tc_sav;
@@ -300,8 +366,8 @@ ipcomp_input_cb(struct cryptop *crp)
if (error) {
IPCOMPSTAT_INC(ipcomps_hdrops);
DPRINTF(("%s: bad mbuf chain, IPCA %s/%08lx\n", __func__,
- ipsec_address(&sav->sah->saidx.dst),
- (u_long) ntohl(sav->spi)));
+ ipsec_address(&sav->sah->saidx.dst, buf, sizeof(buf)),
+ (u_long) ntohl(sav->spi)));
goto bad;
}
@@ -311,12 +377,12 @@ ipcomp_input_cb(struct cryptop *crp)
switch (saidx->dst.sa.sa_family) {
#ifdef INET6
case AF_INET6:
- error = ipsec6_common_input_cb(m, sav, skip, protoff, NULL);
+ error = ipsec6_common_input_cb(m, sav, skip, protoff);
break;
#endif
#ifdef INET
case AF_INET:
- error = ipsec4_common_input_cb(m, sav, skip, protoff, NULL);
+ error = ipsec4_common_input_cb(m, sav, skip, protoff);
break;
#endif
default:
@@ -342,14 +408,10 @@ bad:
* IPComp output routine, called by ipsec[46]_process_packet()
*/
static int
-ipcomp_output(
- struct mbuf *m,
- struct ipsecrequest *isr,
- struct mbuf **mp,
- int skip,
- int protoff
-)
+ipcomp_output(struct mbuf *m, struct ipsecrequest *isr, struct mbuf **mp,
+ int skip, int protoff)
{
+ char buf[INET6_ADDRSTRLEN];
struct secasvar *sav;
struct comp_algo *ipcompx;
int error, ralen, maxpacketsize;
@@ -393,7 +455,7 @@ ipcomp_output(
DPRINTF(("%s: unknown/unsupported protocol family %d, "
"IPCA %s/%08lx\n", __func__,
sav->sah->saidx.dst.sa.sa_family,
- ipsec_address(&sav->sah->saidx.dst),
+ ipsec_address(&sav->sah->saidx.dst, buf, sizeof(buf)),
(u_long) ntohl(sav->spi)));
error = EPFNOSUPPORT;
goto bad;
@@ -402,7 +464,7 @@ ipcomp_output(
IPCOMPSTAT_INC(ipcomps_toobig);
DPRINTF(("%s: packet in IPCA %s/%08lx got too big "
"(len %u, max len %u)\n", __func__,
- ipsec_address(&sav->sah->saidx.dst),
+ ipsec_address(&sav->sah->saidx.dst, buf, sizeof(buf)),
(u_long) ntohl(sav->spi),
ralen + skip + IPCOMP_HLENGTH, maxpacketsize));
error = EMSGSIZE;
@@ -416,8 +478,8 @@ ipcomp_output(
if (m == NULL) {
IPCOMPSTAT_INC(ipcomps_hdrops);
DPRINTF(("%s: cannot clone mbuf chain, IPCA %s/%08lx\n",
- __func__, ipsec_address(&sav->sah->saidx.dst),
- (u_long) ntohl(sav->spi)));
+ __func__, ipsec_address(&sav->sah->saidx.dst, buf,
+ sizeof(buf)), (u_long) ntohl(sav->spi)));
error = ENOBUFS;
goto bad;
}
@@ -454,6 +516,7 @@ ipcomp_output(
goto bad;
}
+ key_addref(isr->sp);
tc->tc_isr = isr;
KEY_ADDREFSA(sav);
tc->tc_sav = sav;
@@ -484,6 +547,7 @@ bad:
static int
ipcomp_output_cb(struct cryptop *crp)
{
+ char buf[INET6_ADDRSTRLEN];
struct tdb_crypto *tc;
struct ipsecrequest *isr;
struct secasvar *sav;
@@ -496,6 +560,7 @@ ipcomp_output_cb(struct cryptop *crp)
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. */
@@ -541,8 +606,8 @@ ipcomp_output_cb(struct cryptop *crp)
if (mo == NULL) {
IPCOMPSTAT_INC(ipcomps_wrap);
DPRINTF(("%s: IPCOMP header inject failed for IPCA %s/%08lx\n",
- __func__, ipsec_address(&sav->sah->saidx.dst),
- (u_long) ntohl(sav->spi)));
+ __func__, ipsec_address(&sav->sah->saidx.dst, buf,
+ sizeof(buf)), (u_long) ntohl(sav->spi)));
error = ENOBUFS;
goto bad;
}
@@ -588,8 +653,8 @@ ipcomp_output_cb(struct cryptop *crp)
DPRINTF(("%s: unknown/unsupported protocol "
"family %d, IPCA %s/%08lx\n", __func__,
sav->sah->saidx.dst.sa.sa_family,
- ipsec_address(&sav->sah->saidx.dst),
- (u_long) ntohl(sav->spi)));
+ ipsec_address(&sav->sah->saidx.dst, buf,
+ sizeof(buf)), (u_long) ntohl(sav->spi)));
error = EPFNOSUPPORT;
goto bad;
}
@@ -610,16 +675,18 @@ ipcomp_output_cb(struct cryptop *crp)
error = ipsec_process_done(m, isr);
KEY_FREESAV(&sav);
IPSECREQUEST_UNLOCK(isr);
- return error;
+ KEY_FREESP(&isr->sp);
+ return (error);
bad:
if (sav)
KEY_FREESAV(&sav);
IPSECREQUEST_UNLOCK(isr);
+ KEY_FREESP(&isr->sp);
if (m)
m_freem(m);
free(tc, M_XDATA);
crypto_freereq(crp);
- return error;
+ return (error);
}
static struct xformsw ipcomp_xformsw = {
@@ -628,11 +695,103 @@ static struct xformsw ipcomp_xformsw = {
ipcomp_output
};
+#ifdef INET
+static const struct encaptab *ipe4_cookie = NULL;
+extern struct domain inetdomain;
+static struct protosw ipcomp4_protosw = {
+ .pr_type = SOCK_RAW,
+ .pr_domain = &inetdomain,
+ .pr_protocol = 0 /* IPPROTO_IPV[46] */,
+ .pr_flags = PR_ATOMIC | PR_ADDR | PR_LASTHDR,
+ .pr_input = ipcomp_nonexp_input,
+ .pr_output = (pr_output_t *)rip_output,
+ .pr_ctloutput = rip_ctloutput,
+ .pr_usrreqs = &rip_usrreqs
+};
+
+static int
+ipcomp4_nonexp_encapcheck(const struct mbuf *m, int off, int proto,
+ void *arg __unused)
+{
+ union sockaddr_union src, dst;
+ const struct ip *ip;
+
+ if (V_ipcomp_enable == 0)
+ return (0);
+ if (proto != IPPROTO_IPV4 && proto != IPPROTO_IPV6)
+ return (0);
+ bzero(&src, sizeof(src));
+ bzero(&dst, sizeof(dst));
+ src.sa.sa_family = dst.sa.sa_family = AF_INET;
+ src.sin.sin_len = dst.sin.sin_len = sizeof(struct sockaddr_in);
+ ip = mtod(m, const struct ip *);
+ src.sin.sin_addr = ip->ip_src;
+ dst.sin.sin_addr = ip->ip_dst;
+ return (ipcomp_encapcheck(&src, &dst));
+}
+#endif
+#ifdef INET6
+static const struct encaptab *ipe6_cookie = NULL;
+extern struct domain inet6domain;
+static struct ip6protosw ipcomp6_protosw = {
+ .pr_type = SOCK_RAW,
+ .pr_domain = &inet6domain,
+ .pr_protocol = 0 /* IPPROTO_IPV[46] */,
+ .pr_flags = PR_ATOMIC | PR_ADDR | PR_LASTHDR,
+ .pr_input = ipcomp6_nonexp_input,
+ .pr_output = rip6_output,
+ .pr_ctloutput = rip6_ctloutput,
+ .pr_usrreqs = &rip6_usrreqs
+};
+
+static int
+ipcomp6_nonexp_encapcheck(const struct mbuf *m, int off, int proto,
+ void *arg __unused)
+{
+ union sockaddr_union src, dst;
+ const struct ip6_hdr *ip6;
+
+ if (V_ipcomp_enable == 0)
+ return (0);
+ if (proto != IPPROTO_IPV4 && proto != IPPROTO_IPV6)
+ return (0);
+ bzero(&src, sizeof(src));
+ bzero(&dst, sizeof(dst));
+ src.sa.sa_family = dst.sa.sa_family = AF_INET;
+ src.sin6.sin6_len = dst.sin6.sin6_len = sizeof(struct sockaddr_in6);
+ ip6 = mtod(m, const struct ip6_hdr *);
+ src.sin6.sin6_addr = ip6->ip6_src;
+ dst.sin6.sin6_addr = ip6->ip6_dst;
+ if (IN6_IS_SCOPE_LINKLOCAL(&src.sin6.sin6_addr)) {
+ /* XXX: sa6_recoverscope() */
+ src.sin6.sin6_scope_id =
+ ntohs(src.sin6.sin6_addr.s6_addr16[1]);
+ src.sin6.sin6_addr.s6_addr16[1] = 0;
+ }
+ if (IN6_IS_SCOPE_LINKLOCAL(&dst.sin6.sin6_addr)) {
+ /* XXX: sa6_recoverscope() */
+ dst.sin6.sin6_scope_id =
+ ntohs(dst.sin6.sin6_addr.s6_addr16[1]);
+ dst.sin6.sin6_addr.s6_addr16[1] = 0;
+ }
+ return (ipcomp_encapcheck(&src, &dst));
+}
+#endif
+
static void
ipcomp_attach(void)
{
+#ifdef INET
+ ipe4_cookie = encap_attach_func(AF_INET, -1,
+ ipcomp4_nonexp_encapcheck, &ipcomp4_protosw, NULL);
+#endif
+#ifdef INET6
+ ipe6_cookie = encap_attach_func(AF_INET6, -1,
+ ipcomp6_nonexp_encapcheck, (void *)&ipcomp6_protosw, NULL);
+#endif
xform_register(&ipcomp_xformsw);
}
-SYSINIT(ipcomp_xform_init, SI_SUB_PROTO_DOMAIN, SI_ORDER_MIDDLE, ipcomp_attach, NULL);
+SYSINIT(ipcomp_xform_init, SI_SUB_PROTO_DOMAIN, SI_ORDER_MIDDLE,
+ ipcomp_attach, NULL);
diff --git a/sys/netipsec/xform_ipip.c b/sys/netipsec/xform_ipip.c
deleted file mode 100644
index 9585eef..0000000
--- a/sys/netipsec/xform_ipip.c
+++ /dev/null
@@ -1,662 +0,0 @@
-/* $FreeBSD$ */
-/* $OpenBSD: ip_ipip.c,v 1.25 2002/06/10 18:04:55 itojun Exp $ */
-/*-
- * The authors of this code are John Ioannidis (ji@tla.org),
- * Angelos D. Keromytis (kermit@csd.uch.gr) and
- * Niels Provos (provos@physnet.uni-hamburg.de).
- *
- * The original version of this code was written by John Ioannidis
- * for BSD/OS in Athens, Greece, in November 1995.
- *
- * Ported to OpenBSD and NetBSD, with additional transforms, in December 1996,
- * by Angelos D. Keromytis.
- *
- * Additional transforms and features in 1997 and 1998 by Angelos D. Keromytis
- * and Niels Provos.
- *
- * Additional features in 1999 by Angelos D. Keromytis.
- *
- * Copyright (C) 1995, 1996, 1997, 1998, 1999 by John Ioannidis,
- * Angelos D. Keromytis and Niels Provos.
- * Copyright (c) 2001, Angelos D. Keromytis.
- *
- * Permission to use, copy, and modify this software with or without fee
- * is hereby granted, provided that this entire notice is included in
- * all copies of any software which is or includes a copy or
- * modification of this software.
- * You may use this code under the GNU public license if you so wish. Please
- * contribute changes back to the authors under this freer than GPL license
- * so that we may further the use of strong encryption without limitations to
- * all.
- *
- * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR
- * IMPLIED WARRANTY. IN PARTICULAR, NONE OF THE AUTHORS MAKES ANY
- * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE
- * MERCHANTABILITY OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR
- * PURPOSE.
- */
-
-/*
- * IP-inside-IP processing
- */
-#include "opt_inet.h"
-#include "opt_inet6.h"
-#include "opt_enc.h"
-
-#include <sys/param.h>
-#include <sys/systm.h>
-#include <sys/mbuf.h>
-#include <sys/socket.h>
-#include <sys/kernel.h>
-#include <sys/protosw.h>
-#include <sys/sysctl.h>
-
-#include <net/if.h>
-#include <net/pfil.h>
-#include <net/netisr.h>
-#include <net/vnet.h>
-
-#include <netinet/in.h>
-#include <netinet/in_systm.h>
-#include <netinet/in_var.h>
-#include <netinet/ip.h>
-#include <netinet/ip_ecn.h>
-#include <netinet/ip_var.h>
-#include <netinet/ip_encap.h>
-
-#include <netipsec/ipsec.h>
-#include <netipsec/xform.h>
-
-#include <netipsec/ipip_var.h>
-
-#ifdef INET6
-#include <netinet/ip6.h>
-#include <netipsec/ipsec6.h>
-#include <netinet6/ip6_ecn.h>
-#include <netinet6/in6_var.h>
-#include <netinet6/ip6protosw.h>
-#endif
-
-#include <netipsec/key.h>
-#include <netipsec/key_debug.h>
-
-#include <machine/stdarg.h>
-
-/*
- * We can control the acceptance of IP4 packets by altering the sysctl
- * net.inet.ipip.allow value. Zero means drop them, all else is acceptance.
- */
-VNET_DEFINE(int, ipip_allow) = 0;
-VNET_PCPUSTAT_DEFINE(struct ipipstat, ipipstat);
-VNET_PCPUSTAT_SYSINIT(ipipstat);
-
-#ifdef VIMAGE
-VNET_PCPUSTAT_SYSUNINIT(ipipstat);
-#endif /* VIMAGE */
-
-SYSCTL_DECL(_net_inet_ipip);
-SYSCTL_VNET_INT(_net_inet_ipip, OID_AUTO,
- ipip_allow, CTLFLAG_RW, &VNET_NAME(ipip_allow), 0, "");
-SYSCTL_VNET_PCPUSTAT(_net_inet_ipip, IPSECCTL_STATS, stats,
- struct ipipstat, ipipstat,
- "IPIP statistics (struct ipipstat, netipsec/ipip_var.h)");
-
-/* XXX IPCOMP */
-#define M_IPSEC (M_AUTHIPHDR|M_AUTHIPDGM|M_DECRYPTED)
-
-static void _ipip_input(struct mbuf *m, int iphlen, struct ifnet *gifp);
-
-#ifdef INET6
-/*
- * Really only a wrapper for ipip_input(), for use with IPv6.
- */
-int
-ip4_input6(struct mbuf **m, int *offp, int proto)
-{
-#if 0
- /* If we do not accept IP-in-IP explicitly, drop. */
- if (!V_ipip_allow && ((*m)->m_flags & M_IPSEC) == 0) {
- DPRINTF(("%s: dropped due to policy\n", __func__));
- IPIPSTAT_INC(ipips_pdrops);
- m_freem(*m);
- return IPPROTO_DONE;
- }
-#endif
- _ipip_input(*m, *offp, NULL);
- return IPPROTO_DONE;
-}
-#endif /* INET6 */
-
-#ifdef INET
-/*
- * Really only a wrapper for ipip_input(), for use with IPv4.
- */
-void
-ip4_input(struct mbuf *m, int off)
-{
-#if 0
- /* If we do not accept IP-in-IP explicitly, drop. */
- if (!V_ipip_allow && (m->m_flags & M_IPSEC) == 0) {
- DPRINTF(("%s: dropped due to policy\n", __func__));
- IPIPSTAT_INC(ipips_pdrops);
- m_freem(m);
- return;
- }
-#endif
- _ipip_input(m, off, NULL);
-}
-#endif /* INET */
-
-/*
- * ipip_input gets called when we receive an IP{46} encapsulated packet,
- * either because we got it at a real interface, or because AH or ESP
- * were being used in tunnel mode (in which case the rcvif element will
- * contain the address of the encX interface associated with the tunnel.
- */
-
-static void
-_ipip_input(struct mbuf *m, int iphlen, struct ifnet *gifp)
-{
- struct ip *ipo;
-#ifdef INET6
- struct ip6_hdr *ip6 = NULL;
- u_int8_t itos;
-#endif
- int isr;
- u_int8_t otos;
- u_int8_t v;
- int hlen;
-
- IPIPSTAT_INC(ipips_ipackets);
-
- m_copydata(m, 0, 1, &v);
-
- switch (v >> 4) {
-#ifdef INET
- case 4:
- hlen = sizeof(struct ip);
- break;
-#endif /* INET */
-#ifdef INET6
- case 6:
- hlen = sizeof(struct ip6_hdr);
- break;
-#endif
- default:
- IPIPSTAT_INC(ipips_family);
- m_freem(m);
- return /* EAFNOSUPPORT */;
- }
-
- /* Bring the IP header in the first mbuf, if not there already */
- if (m->m_len < hlen) {
- if ((m = m_pullup(m, hlen)) == NULL) {
- DPRINTF(("%s: m_pullup (1) failed\n", __func__));
- IPIPSTAT_INC(ipips_hdrops);
- return;
- }
- }
- ipo = mtod(m, struct ip *);
-
- /* Keep outer ecn field. */
- switch (v >> 4) {
-#ifdef INET
- case 4:
- otos = ipo->ip_tos;
- break;
-#endif /* INET */
-#ifdef INET6
- case 6:
- otos = (ntohl(mtod(m, struct ip6_hdr *)->ip6_flow) >> 20) & 0xff;
- break;
-#endif
- default:
- panic("ipip_input: unknown ip version %u (outer)", v>>4);
- }
-
- /* Remove outer IP header */
- m_adj(m, iphlen);
-
- /* Sanity check */
- if (m->m_pkthdr.len < sizeof(struct ip)) {
- IPIPSTAT_INC(ipips_hdrops);
- m_freem(m);
- return;
- }
-
- m_copydata(m, 0, 1, &v);
-
- switch (v >> 4) {
-#ifdef INET
- case 4:
- hlen = sizeof(struct ip);
- break;
-#endif /* INET */
-
-#ifdef INET6
- case 6:
- hlen = sizeof(struct ip6_hdr);
- break;
-#endif
- default:
- IPIPSTAT_INC(ipips_family);
- m_freem(m);
- return; /* EAFNOSUPPORT */
- }
-
- /*
- * Bring the inner IP header in the first mbuf, if not there already.
- */
- if (m->m_len < hlen) {
- if ((m = m_pullup(m, hlen)) == NULL) {
- DPRINTF(("%s: m_pullup (2) failed\n", __func__));
- IPIPSTAT_INC(ipips_hdrops);
- return;
- }
- }
-
- /*
- * RFC 1853 specifies that the inner TTL should not be touched on
- * decapsulation. There's no reason this comment should be here, but
- * this is as good as any a position.
- */
-
- /* Some sanity checks in the inner IP header */
- switch (v >> 4) {
-#ifdef INET
- case 4:
- ipo = mtod(m, struct ip *);
- ip_ecn_egress(V_ip4_ipsec_ecn, &otos, &ipo->ip_tos);
- break;
-#endif /* INET */
-#ifdef INET6
- case 6:
- ip6 = (struct ip6_hdr *) ipo;
- itos = (ntohl(ip6->ip6_flow) >> 20) & 0xff;
- ip_ecn_egress(V_ip6_ipsec_ecn, &otos, &itos);
- ip6->ip6_flow &= ~htonl(0xff << 20);
- ip6->ip6_flow |= htonl((u_int32_t) itos << 20);
- break;
-#endif
- default:
- panic("ipip_input: unknown ip version %u (inner)", v>>4);
- }
-
- /* Check for local address spoofing. */
- if ((m->m_pkthdr.rcvif == NULL ||
- !(m->m_pkthdr.rcvif->if_flags & IFF_LOOPBACK)) &&
- V_ipip_allow != 2) {
-#ifdef INET
- if ((v >> 4) == IPVERSION &&
- in_localip(ipo->ip_src) != 0) {
- IPIPSTAT_INC(ipips_spoof);
- m_freem(m);
- return;
- }
-#endif
-#ifdef INET6
- if ((v & IPV6_VERSION_MASK) == IPV6_VERSION &&
- in6_localip(&ip6->ip6_src) != 0) {
- IPIPSTAT_INC(ipips_spoof);
- m_freem(m);
- return;
- }
-#endif
- }
-
- /* Statistics */
- IPIPSTAT_ADD(ipips_ibytes, m->m_pkthdr.len - iphlen);
-
- /*
- * Interface pointer stays the same; if no IPsec processing has
- * been done (or will be done), this will point to a normal
- * interface. Otherwise, it'll point to an enc interface, which
- * will allow a packet filter to distinguish between secure and
- * untrusted packets.
- */
-
- switch (v >> 4) {
-#ifdef INET
- case 4:
- isr = NETISR_IP;
- break;
-#endif
-#ifdef INET6
- case 6:
- isr = NETISR_IPV6;
- break;
-#endif
- default:
- panic("%s: bogus ip version %u", __func__, v>>4);
- }
-
- if (netisr_queue(isr, m)) { /* (0) on success. */
- IPIPSTAT_INC(ipips_qfull);
- DPRINTF(("%s: packet dropped because of full queue\n",
- __func__));
- }
-}
-
-int
-ipip_output(
- struct mbuf *m,
- struct ipsecrequest *isr,
- struct mbuf **mp,
- int skip,
- int protoff
-)
-{
- struct secasvar *sav;
- u_int8_t tp, otos;
- struct secasindex *saidx;
- int error;
-#if defined(INET) || defined(INET6)
- u_int8_t itos;
-#endif
-#ifdef INET
- struct ip *ipo;
-#endif /* INET */
-#ifdef INET6
- struct ip6_hdr *ip6, *ip6o;
-#endif /* INET6 */
-
- sav = isr->sav;
- IPSEC_ASSERT(sav != NULL, ("null SA"));
- IPSEC_ASSERT(sav->sah != NULL, ("null SAH"));
-
- /* XXX Deal with empty TDB source/destination addresses. */
-
- m_copydata(m, 0, 1, &tp);
- tp = (tp >> 4) & 0xff; /* Get the IP version number. */
-
- saidx = &sav->sah->saidx;
- switch (saidx->dst.sa.sa_family) {
-#ifdef INET
- case AF_INET:
- if (saidx->src.sa.sa_family != AF_INET ||
- saidx->src.sin.sin_addr.s_addr == INADDR_ANY ||
- saidx->dst.sin.sin_addr.s_addr == INADDR_ANY) {
- DPRINTF(("%s: unspecified tunnel endpoint "
- "address in SA %s/%08lx\n", __func__,
- ipsec_address(&saidx->dst),
- (u_long) ntohl(sav->spi)));
- IPIPSTAT_INC(ipips_unspec);
- error = EINVAL;
- goto bad;
- }
-
- M_PREPEND(m, sizeof(struct ip), M_NOWAIT);
- if (m == 0) {
- DPRINTF(("%s: M_PREPEND failed\n", __func__));
- IPIPSTAT_INC(ipips_hdrops);
- error = ENOBUFS;
- goto bad;
- }
-
- ipo = mtod(m, struct ip *);
-
- ipo->ip_v = IPVERSION;
- ipo->ip_hl = 5;
- ipo->ip_len = htons(m->m_pkthdr.len);
- ipo->ip_ttl = V_ip_defttl;
- ipo->ip_sum = 0;
- ipo->ip_src = saidx->src.sin.sin_addr;
- ipo->ip_dst = saidx->dst.sin.sin_addr;
-
- ipo->ip_id = ip_newid();
-
- /* If the inner protocol is IP... */
- switch (tp) {
- case IPVERSION:
- /* Save ECN notification */
- m_copydata(m, sizeof(struct ip) +
- offsetof(struct ip, ip_tos),
- sizeof(u_int8_t), (caddr_t) &itos);
-
- ipo->ip_p = IPPROTO_IPIP;
-
- /*
- * We should be keeping tunnel soft-state and
- * send back ICMPs if needed.
- */
- m_copydata(m, sizeof(struct ip) +
- offsetof(struct ip, ip_off),
- sizeof(u_int16_t), (caddr_t) &ipo->ip_off);
- ipo->ip_off = ntohs(ipo->ip_off);
- ipo->ip_off &= ~(IP_DF | IP_MF | IP_OFFMASK);
- ipo->ip_off = htons(ipo->ip_off);
- break;
-#ifdef INET6
- case (IPV6_VERSION >> 4):
- {
- u_int32_t itos32;
-
- /* Save ECN notification. */
- m_copydata(m, sizeof(struct ip) +
- offsetof(struct ip6_hdr, ip6_flow),
- sizeof(u_int32_t), (caddr_t) &itos32);
- itos = ntohl(itos32) >> 20;
- ipo->ip_p = IPPROTO_IPV6;
- ipo->ip_off = 0;
- break;
- }
-#endif /* INET6 */
- default:
- goto nofamily;
- }
-
- otos = 0;
- ip_ecn_ingress(ECN_ALLOWED, &otos, &itos);
- ipo->ip_tos = otos;
- break;
-#endif /* INET */
-
-#ifdef INET6
- case AF_INET6:
- if (IN6_IS_ADDR_UNSPECIFIED(&saidx->dst.sin6.sin6_addr) ||
- saidx->src.sa.sa_family != AF_INET6 ||
- IN6_IS_ADDR_UNSPECIFIED(&saidx->src.sin6.sin6_addr)) {
- DPRINTF(("%s: unspecified tunnel endpoint "
- "address in SA %s/%08lx\n", __func__,
- ipsec_address(&saidx->dst),
- (u_long) ntohl(sav->spi)));
- IPIPSTAT_INC(ipips_unspec);
- error = ENOBUFS;
- goto bad;
- }
-
- /* scoped address handling */
- ip6 = mtod(m, struct ip6_hdr *);
- if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_src))
- ip6->ip6_src.s6_addr16[1] = 0;
- if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_dst))
- ip6->ip6_dst.s6_addr16[1] = 0;
-
- M_PREPEND(m, sizeof(struct ip6_hdr), M_NOWAIT);
- if (m == 0) {
- DPRINTF(("%s: M_PREPEND failed\n", __func__));
- IPIPSTAT_INC(ipips_hdrops);
- error = ENOBUFS;
- goto bad;
- }
-
- /* Initialize IPv6 header */
- ip6o = mtod(m, struct ip6_hdr *);
- ip6o->ip6_flow = 0;
- ip6o->ip6_vfc &= ~IPV6_VERSION_MASK;
- ip6o->ip6_vfc |= IPV6_VERSION;
- ip6o->ip6_hlim = IPV6_DEFHLIM;
- ip6o->ip6_dst = saidx->dst.sin6.sin6_addr;
- ip6o->ip6_src = saidx->src.sin6.sin6_addr;
- ip6o->ip6_plen = htons(m->m_pkthdr.len - sizeof(*ip6));
-
- switch (tp) {
-#ifdef INET
- case IPVERSION:
- /* Save ECN notification */
- m_copydata(m, sizeof(struct ip6_hdr) +
- offsetof(struct ip, ip_tos), sizeof(u_int8_t),
- (caddr_t) &itos);
-
- /* This is really IPVERSION. */
- ip6o->ip6_nxt = IPPROTO_IPIP;
- break;
-#endif /* INET */
- case (IPV6_VERSION >> 4):
- {
- u_int32_t itos32;
-
- /* Save ECN notification. */
- m_copydata(m, sizeof(struct ip6_hdr) +
- offsetof(struct ip6_hdr, ip6_flow),
- sizeof(u_int32_t), (caddr_t) &itos32);
- itos = ntohl(itos32) >> 20;
-
- ip6o->ip6_nxt = IPPROTO_IPV6;
- break;
- }
- default:
- goto nofamily;
- }
-
- otos = 0;
- ip_ecn_ingress(V_ip6_ipsec_ecn, &otos, &itos);
- ip6o->ip6_flow |= htonl((u_int32_t) otos << 20);
- break;
-#endif /* INET6 */
-
- default:
-nofamily:
- DPRINTF(("%s: unsupported protocol family %u\n", __func__,
- saidx->dst.sa.sa_family));
- IPIPSTAT_INC(ipips_family);
- error = EAFNOSUPPORT; /* XXX diffs from openbsd */
- goto bad;
- }
-
- IPIPSTAT_INC(ipips_opackets);
- *mp = m;
-
-#ifdef INET
- if (saidx->dst.sa.sa_family == AF_INET) {
-#if 0
- if (sav->tdb_xform->xf_type == XF_IP4)
- tdb->tdb_cur_bytes +=
- m->m_pkthdr.len - sizeof(struct ip);
-#endif
- IPIPSTAT_ADD(ipips_obytes,
- m->m_pkthdr.len - sizeof(struct ip));
- }
-#endif /* INET */
-
-#ifdef INET6
- if (saidx->dst.sa.sa_family == AF_INET6) {
-#if 0
- if (sav->tdb_xform->xf_type == XF_IP4)
- tdb->tdb_cur_bytes +=
- m->m_pkthdr.len - sizeof(struct ip6_hdr);
-#endif
- IPIPSTAT_ADD(ipips_obytes,
- m->m_pkthdr.len - sizeof(struct ip6_hdr));
- }
-#endif /* INET6 */
-
- return 0;
-bad:
- if (m)
- m_freem(m);
- *mp = NULL;
- return (error);
-}
-
-#ifdef IPSEC
-#if defined(INET) || defined(INET6)
-static int
-ipe4_init(struct secasvar *sav, struct xformsw *xsp)
-{
- sav->tdb_xform = xsp;
- return 0;
-}
-
-static int
-ipe4_zeroize(struct secasvar *sav)
-{
- sav->tdb_xform = NULL;
- return 0;
-}
-
-static int
-ipe4_input(struct mbuf *m, struct secasvar *sav, int skip, int protoff)
-{
- /* This is a rather serious mistake, so no conditional printing. */
- printf("%s: should never be called\n", __func__);
- if (m)
- m_freem(m);
- return EOPNOTSUPP;
-}
-
-static struct xformsw ipe4_xformsw = {
- XF_IP4, 0, "IPv4 Simple Encapsulation",
- ipe4_init, ipe4_zeroize, ipe4_input, ipip_output,
-};
-
-extern struct domain inetdomain;
-#endif /* INET || INET6 */
-#ifdef INET
-static struct protosw ipe4_protosw = {
- .pr_type = SOCK_RAW,
- .pr_domain = &inetdomain,
- .pr_protocol = IPPROTO_IPV4,
- .pr_flags = PR_ATOMIC|PR_ADDR|PR_LASTHDR,
- .pr_input = ip4_input,
- .pr_ctloutput = rip_ctloutput,
- .pr_usrreqs = &rip_usrreqs
-};
-#endif /* INET */
-#if defined(INET6) && defined(INET)
-static struct ip6protosw ipe6_protosw = {
- .pr_type = SOCK_RAW,
- .pr_domain = &inetdomain,
- .pr_protocol = IPPROTO_IPV6,
- .pr_flags = PR_ATOMIC|PR_ADDR|PR_LASTHDR,
- .pr_input = ip4_input6,
- .pr_ctloutput = rip_ctloutput,
- .pr_usrreqs = &rip_usrreqs
-};
-#endif /* INET6 && INET */
-
-#ifdef INET
-/*
- * Check the encapsulated packet to see if we want it
- */
-static int
-ipe4_encapcheck(const struct mbuf *m, int off, int proto, void *arg)
-{
- /*
- * Only take packets coming from IPSEC tunnels; the rest
- * must be handled by the gif tunnel code. Note that we
- * also return a minimum priority when we want the packet
- * so any explicit gif tunnels take precedence.
- */
- return ((m->m_flags & M_IPSEC) != 0 ? 1 : 0);
-}
-#endif /* INET */
-
-static void
-ipe4_attach(void)
-{
-
- xform_register(&ipe4_xformsw);
- /* attach to encapsulation framework */
- /* XXX save return cookie for detach on module remove */
-#ifdef INET
- (void) encap_attach_func(AF_INET, -1,
- ipe4_encapcheck, &ipe4_protosw, NULL);
-#endif
-#if defined(INET6) && defined(INET)
- (void) encap_attach_func(AF_INET6, -1,
- ipe4_encapcheck, (struct protosw *)&ipe6_protosw, NULL);
-#endif
-}
-SYSINIT(ipe4_xform_init, SI_SUB_PROTO_DOMAIN, SI_ORDER_MIDDLE, ipe4_attach, NULL);
-#endif /* IPSEC */
OpenPOWER on IntegriCloud