summaryrefslogtreecommitdiff
path: root/sys/netipsec/ipsec.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/netipsec/ipsec.c')
-rw-r--r--sys/netipsec/ipsec.c1593
1 files changed, 626 insertions, 967 deletions
diff --git a/sys/netipsec/ipsec.c b/sys/netipsec/ipsec.c
index dd7be14cb5f01..e120f654aac03 100644
--- a/sys/netipsec/ipsec.c
+++ b/sys/netipsec/ipsec.c
@@ -88,6 +88,7 @@
#include <netipsec/esp_var.h>
#include <netipsec/ipcomp.h> /*XXX*/
#include <netipsec/ipcomp_var.h>
+#include <netipsec/ipsec_support.h>
#include <netipsec/key.h>
#include <netipsec/keydb.h>
@@ -99,12 +100,6 @@
#include <opencrypto/cryptodev.h>
-#ifdef IPSEC_DEBUG
-VNET_DEFINE(int, ipsec_debug) = 1;
-#else
-VNET_DEFINE(int, ipsec_debug) = 0;
-#endif
-
/* NB: name changed so netstat doesn't use it. */
VNET_PCPUSTAT_DEFINE(struct ipsecstat, ipsec4stat);
VNET_PCPUSTAT_SYSINIT(ipsec4stat);
@@ -124,8 +119,28 @@ VNET_DEFINE(int, ip4_ah_net_deflev) = IPSEC_LEVEL_USE;
VNET_DEFINE(int, ip4_ipsec_ecn) = 0;
VNET_DEFINE(int, ip4_esp_randpad) = -1;
-static VNET_DEFINE(struct secpolicy, def_policy);
+static VNET_DEFINE(int, ip4_filtertunnel) = 0;
+#define V_ip4_filtertunnel VNET(ip4_filtertunnel)
+static VNET_DEFINE(int, check_policy_history) = 0;
+#define V_check_policy_history VNET(check_policy_history)
+static VNET_DEFINE(struct secpolicy *, def_policy) = NULL;
#define V_def_policy VNET(def_policy)
+static int
+sysctl_def_policy(SYSCTL_HANDLER_ARGS)
+{
+ int error, value;
+
+ value = V_def_policy->policy;
+ error = sysctl_handle_int(oidp, &value, 0, req);
+ if (error == 0) {
+ if (value != IPSEC_POLICY_DISCARD &&
+ value != IPSEC_POLICY_NONE)
+ return (EINVAL);
+ V_def_policy->policy = value;
+ }
+ return (error);
+}
+
/*
* Crypto support requirements:
*
@@ -134,17 +149,24 @@ static VNET_DEFINE(struct secpolicy, def_policy);
* 0 take anything
*/
VNET_DEFINE(int, crypto_support) = CRYPTOCAP_F_HARDWARE | CRYPTOCAP_F_SOFTWARE;
+/*
+ * TCP/UDP checksum handling policy for transport mode NAT-T (RFC3948)
+ *
+ * 0 - auto: incrementally recompute, when checksum delta is known;
+ * if checksum delta isn't known, reset checksum to zero for UDP,
+ * and mark csum_flags as valid for TCP.
+ * 1 - fully recompute TCP/UDP checksum.
+ */
+VNET_DEFINE(int, natt_cksum_policy) = 0;
FEATURE(ipsec, "Internet Protocol Security (IPsec)");
-#ifdef IPSEC_NAT_T
FEATURE(ipsec_natt, "UDP Encapsulation of IPsec ESP Packets ('NAT-T')");
-#endif
SYSCTL_DECL(_net_inet_ipsec);
/* net.inet.ipsec */
-SYSCTL_INT(_net_inet_ipsec, IPSECCTL_DEF_POLICY, def_policy,
- CTLFLAG_VNET | CTLFLAG_RW, &VNET_NAME(def_policy).policy, 0,
+SYSCTL_PROC(_net_inet_ipsec, IPSECCTL_DEF_POLICY, def_policy,
+ CTLTYPE_INT | CTLFLAG_VNET | CTLFLAG_RW, 0, 0, sysctl_def_policy, "I",
"IPsec default policy.");
SYSCTL_INT(_net_inet_ipsec, IPSECCTL_DEF_ESP_TRANSLEV, esp_trans_deflev,
CTLFLAG_VNET | CTLFLAG_RW, &VNET_NAME(ip4_esp_trans_deflev), 0,
@@ -160,22 +182,28 @@ SYSCTL_INT(_net_inet_ipsec, IPSECCTL_DEF_AH_NETLEV, ah_net_deflev,
"AH tunnel mode default level.");
SYSCTL_INT(_net_inet_ipsec, IPSECCTL_AH_CLEARTOS, ah_cleartos,
CTLFLAG_VNET | CTLFLAG_RW, &VNET_NAME(ah_cleartos), 0,
- "If set clear type-of-service field when doing AH computation.");
+ "If set, clear type-of-service field when doing AH computation.");
SYSCTL_INT(_net_inet_ipsec, IPSECCTL_AH_OFFSETMASK, ah_offsetmask,
CTLFLAG_VNET | CTLFLAG_RW, &VNET_NAME(ip4_ah_offsetmask), 0,
- "If not set clear offset field mask when doing AH computation.");
+ "If not set, clear offset field mask when doing AH computation.");
SYSCTL_INT(_net_inet_ipsec, IPSECCTL_DFBIT, dfbit,
CTLFLAG_VNET | CTLFLAG_RW, &VNET_NAME(ip4_ipsec_dfbit), 0,
"Do not fragment bit on encap.");
SYSCTL_INT(_net_inet_ipsec, IPSECCTL_ECN, ecn,
CTLFLAG_VNET | CTLFLAG_RW, &VNET_NAME(ip4_ipsec_ecn), 0,
"Explicit Congestion Notification handling.");
-SYSCTL_INT(_net_inet_ipsec, IPSECCTL_DEBUG, debug,
- CTLFLAG_VNET | CTLFLAG_RW, &VNET_NAME(ipsec_debug), 0,
- "Enable IPsec debugging output when set.");
SYSCTL_INT(_net_inet_ipsec, OID_AUTO, crypto_support,
CTLFLAG_VNET | CTLFLAG_RW, &VNET_NAME(crypto_support), 0,
"Crypto driver selection.");
+SYSCTL_INT(_net_inet_ipsec, OID_AUTO, check_policy_history,
+ CTLFLAG_VNET | CTLFLAG_RW, &VNET_NAME(check_policy_history), 0,
+ "Use strict check of inbound packets to security policy compliance.");
+SYSCTL_INT(_net_inet_ipsec, OID_AUTO, natt_cksum_policy,
+ CTLFLAG_VNET | CTLFLAG_RW, &VNET_NAME(natt_cksum_policy), 0,
+ "Method to fix TCP/UDP checksum for transport mode IPsec after NAT.");
+SYSCTL_INT(_net_inet_ipsec, OID_AUTO, filtertunnel,
+ CTLFLAG_VNET | CTLFLAG_RW, &VNET_NAME(ip4_filtertunnel), 0,
+ "If set, filter packets from an IPsec tunnel.");
SYSCTL_VNET_PCPUSTAT(_net_inet_ipsec, OID_AUTO, ipsecstats, struct ipsecstat,
ipsec4stat, "IPsec IPv4 statistics.");
@@ -212,11 +240,14 @@ VNET_DEFINE(int, ip6_ah_trans_deflev) = IPSEC_LEVEL_USE;
VNET_DEFINE(int, ip6_ah_net_deflev) = IPSEC_LEVEL_USE;
VNET_DEFINE(int, ip6_ipsec_ecn) = 0; /* ECN ignore(-1)/forbidden(0)/allowed(1) */
+static VNET_DEFINE(int, ip6_filtertunnel) = 0;
+#define V_ip6_filtertunnel VNET(ip6_filtertunnel)
+
SYSCTL_DECL(_net_inet6_ipsec6);
/* net.inet6.ipsec6 */
-SYSCTL_INT(_net_inet6_ipsec6, IPSECCTL_DEF_POLICY, def_policy,
- CTLFLAG_VNET | CTLFLAG_RW, &VNET_NAME(def_policy).policy, 0,
+SYSCTL_PROC(_net_inet6_ipsec6, IPSECCTL_DEF_POLICY, def_policy,
+ CTLTYPE_INT | CTLFLAG_VNET | CTLFLAG_RW, 0, 0, sysctl_def_policy, "I",
"IPsec default policy.");
SYSCTL_INT(_net_inet6_ipsec6, IPSECCTL_DEF_ESP_TRANSLEV, esp_trans_deflev,
CTLFLAG_VNET | CTLFLAG_RW, &VNET_NAME(ip6_esp_trans_deflev), 0,
@@ -233,385 +264,228 @@ SYSCTL_INT(_net_inet6_ipsec6, IPSECCTL_DEF_AH_NETLEV, ah_net_deflev,
SYSCTL_INT(_net_inet6_ipsec6, IPSECCTL_ECN, ecn,
CTLFLAG_VNET | CTLFLAG_RW, &VNET_NAME(ip6_ipsec_ecn), 0,
"Explicit Congestion Notification handling.");
-SYSCTL_INT(_net_inet6_ipsec6, IPSECCTL_DEBUG, debug,
- CTLFLAG_VNET | CTLFLAG_RW, &VNET_NAME(ipsec_debug), 0,
- "Enable IPsec debugging output when set.");
+SYSCTL_INT(_net_inet6_ipsec6, OID_AUTO, filtertunnel,
+ CTLFLAG_VNET | CTLFLAG_RW, &VNET_NAME(ip6_filtertunnel), 0,
+ "If set, filter packets from an IPsec tunnel.");
SYSCTL_VNET_PCPUSTAT(_net_inet6_ipsec6, IPSECCTL_STATS, ipsecstats,
struct ipsecstat, ipsec6stat, "IPsec IPv6 statistics.");
#endif /* INET6 */
-static int ipsec_in_reject(struct secpolicy *, const struct mbuf *);
-static int ipsec_setspidx_inpcb(const struct mbuf *, struct inpcb *, u_int);
-static int ipsec_setspidx(const struct mbuf *, struct secpolicyindex *, int);
-static void ipsec4_get_ulp(const struct mbuf *m, struct secpolicyindex *, int);
-static int ipsec4_setspidx_ipaddr(const struct mbuf *, struct secpolicyindex *);
+static int ipsec_in_reject(struct secpolicy *, struct inpcb *,
+ const struct mbuf *);
+
+#ifdef INET
+static void ipsec4_get_ulp(const struct mbuf *, struct secpolicyindex *, int);
+static void ipsec4_setspidx_ipaddr(const struct mbuf *,
+ struct secpolicyindex *);
+#endif
#ifdef INET6
static void ipsec6_get_ulp(const struct mbuf *m, struct secpolicyindex *, int);
-static int ipsec6_setspidx_ipaddr(const struct mbuf *, struct secpolicyindex *);
+static void ipsec6_setspidx_ipaddr(const struct mbuf *,
+ struct secpolicyindex *);
#endif
-static void ipsec_delpcbpolicy(struct inpcbpolicy *);
-static struct secpolicy *ipsec_deepcopy_policy(struct secpolicy *src);
-
-MALLOC_DEFINE(M_IPSEC_INPCB, "inpcbpolicy", "inpcb-resident ipsec policy");
/*
* Return a held reference to the default SP.
*/
static struct secpolicy *
-key_allocsp_default(const char* where, int tag)
+key_allocsp_default(void)
{
- struct secpolicy *sp;
-
- KEYDEBUG(KEYDEBUG_IPSEC_STAMP,
- printf("DP key_allocsp_default from %s:%u\n", where, tag));
-
- sp = &V_def_policy;
- if (sp->policy != IPSEC_POLICY_DISCARD &&
- sp->policy != IPSEC_POLICY_NONE) {
- ipseclog((LOG_INFO, "fixed system default policy: %d->%d\n",
- sp->policy, IPSEC_POLICY_NONE));
- sp->policy = IPSEC_POLICY_NONE;
- }
- key_addref(sp);
- KEYDEBUG(KEYDEBUG_IPSEC_STAMP,
- printf("DP key_allocsp_default returns SP:%p (%u)\n",
- sp, sp->refcnt));
- return (sp);
+ key_addref(V_def_policy);
+ return (V_def_policy);
}
-#define KEY_ALLOCSP_DEFAULT() \
- key_allocsp_default(__FILE__, __LINE__)
-/*
- * For OUTBOUND packet having a socket. Searching SPD for packet,
- * and return a pointer to SP.
- * OUT: NULL: no apropreate SP found, the following value is set to error.
- * 0 : bypass
- * EACCES : discard packet.
- * ENOENT : ipsec_acquire() in progress, maybe.
- * others : error occurred.
- * others: a pointer to SP
- *
- * NOTE: IPv6 mapped adddress concern is implemented here.
- */
-struct secpolicy *
-ipsec_getpolicy(struct tdb_ident *tdbi, u_int dir)
+static void
+ipsec_invalidate_cache(struct inpcb *inp, u_int dir)
{
struct secpolicy *sp;
- IPSEC_ASSERT(tdbi != NULL, ("null tdbi"));
- IPSEC_ASSERT(dir == IPSEC_DIR_INBOUND || dir == IPSEC_DIR_OUTBOUND,
- ("invalid direction %u", dir));
-
- sp = KEY_ALLOCSP2(tdbi->spi, &tdbi->dst, tdbi->proto, dir);
- if (sp == NULL) /*XXX????*/
- sp = KEY_ALLOCSP_DEFAULT();
- IPSEC_ASSERT(sp != NULL, ("null SP"));
- return (sp);
+ INP_WLOCK_ASSERT(inp);
+ if (dir == IPSEC_DIR_OUTBOUND) {
+ if (inp->inp_sp->flags & INP_INBOUND_POLICY)
+ return;
+ sp = inp->inp_sp->sp_in;
+ inp->inp_sp->sp_in = NULL;
+ } else {
+ if (inp->inp_sp->flags & INP_OUTBOUND_POLICY)
+ return;
+ sp = inp->inp_sp->sp_out;
+ inp->inp_sp->sp_out = NULL;
+ }
+ if (sp != NULL)
+ key_freesp(&sp); /* release extra reference */
}
-/*
- * For OUTBOUND packet having a socket. Searching SPD for packet,
- * and return a pointer to SP.
- * OUT: NULL: no apropreate SP found, the following value is set to error.
- * 0 : bypass
- * EACCES : discard packet.
- * ENOENT : ipsec_acquire() in progress, maybe.
- * others : error occurred.
- * others: a pointer to SP
- *
- * NOTE: IPv6 mapped adddress concern is implemented here.
- */
-static struct secpolicy *
-ipsec_getpolicybysock(const struct mbuf *m, u_int dir, struct inpcb *inp,
- int *error)
+static void
+ipsec_cachepolicy(struct inpcb *inp, struct secpolicy *sp, u_int dir)
{
- struct inpcbpolicy *pcbsp;
- struct secpolicy *currsp = NULL; /* Policy on socket. */
- struct secpolicy *sp;
+ uint32_t genid;
+ int downgrade;
- IPSEC_ASSERT(m != NULL, ("null mbuf"));
- IPSEC_ASSERT(inp != NULL, ("null inpcb"));
- IPSEC_ASSERT(error != NULL, ("null error"));
- IPSEC_ASSERT(dir == IPSEC_DIR_INBOUND || dir == IPSEC_DIR_OUTBOUND,
- ("invalid direction %u", dir));
+ INP_LOCK_ASSERT(inp);
- if (!key_havesp(dir)) {
- /* No SP found, use system default. */
- sp = KEY_ALLOCSP_DEFAULT();
- return (sp);
+ if (dir == IPSEC_DIR_OUTBOUND) {
+ /* Do we have configured PCB policy? */
+ if (inp->inp_sp->flags & INP_OUTBOUND_POLICY)
+ return;
+ /* Another thread has already set cached policy */
+ if (inp->inp_sp->sp_out != NULL)
+ return;
+ /*
+ * Do not cache OUTBOUND policy if PCB isn't connected,
+ * i.e. foreign address is INADDR_ANY/UNSPECIFIED.
+ */
+#ifdef INET
+ if ((inp->inp_vflag & INP_IPV4) != 0 &&
+ inp->inp_faddr.s_addr == INADDR_ANY)
+ return;
+#endif
+#ifdef INET6
+ if ((inp->inp_vflag & INP_IPV6) != 0 &&
+ IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_faddr))
+ return;
+#endif
+ } else {
+ /* Do we have configured PCB policy? */
+ if (inp->inp_sp->flags & INP_INBOUND_POLICY)
+ return;
+ /* Another thread has already set cached policy */
+ if (inp->inp_sp->sp_in != NULL)
+ return;
+ /*
+ * Do not cache INBOUND policy for listen socket,
+ * that is bound to INADDR_ANY/UNSPECIFIED address.
+ */
+#ifdef INET
+ if ((inp->inp_vflag & INP_IPV4) != 0 &&
+ inp->inp_faddr.s_addr == INADDR_ANY)
+ return;
+#endif
+#ifdef INET6
+ if ((inp->inp_vflag & INP_IPV6) != 0 &&
+ IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_faddr))
+ return;
+#endif
}
-
- /* Set spidx in pcb. */
- *error = ipsec_setspidx_inpcb(m, inp, dir);
- if (*error)
- return (NULL);
-
- pcbsp = inp->inp_sp;
- IPSEC_ASSERT(pcbsp != NULL, ("null pcbsp"));
- switch (dir) {
- case IPSEC_DIR_INBOUND:
- currsp = pcbsp->sp_in;
- break;
- case IPSEC_DIR_OUTBOUND:
- currsp = pcbsp->sp_out;
- break;
+ downgrade = 0;
+ if (!INP_WLOCKED(inp)) {
+ if ((downgrade = INP_TRY_UPGRADE(inp)) == 0)
+ return;
}
- IPSEC_ASSERT(currsp != NULL, ("null currsp"));
-
- if (pcbsp->priv) { /* When privilieged socket. */
- switch (currsp->policy) {
- case IPSEC_POLICY_BYPASS:
- case IPSEC_POLICY_IPSEC:
- key_addref(currsp);
- sp = currsp;
- break;
-
- case IPSEC_POLICY_ENTRUST:
- /* Look for a policy in SPD. */
- sp = KEY_ALLOCSP(&currsp->spidx, dir);
- if (sp == NULL) /* No SP found. */
- sp = KEY_ALLOCSP_DEFAULT();
- break;
-
- default:
- ipseclog((LOG_ERR, "%s: Invalid policy for PCB %d\n",
- __func__, currsp->policy));
- *error = EINVAL;
- return (NULL);
- }
- } else { /* Unpriv, SPD has policy. */
- sp = KEY_ALLOCSP(&currsp->spidx, dir);
- if (sp == NULL) { /* No SP found. */
- switch (currsp->policy) {
- case IPSEC_POLICY_BYPASS:
- ipseclog((LOG_ERR, "%s: Illegal policy for "
- "non-priviliged defined %d\n",
- __func__, currsp->policy));
- *error = EINVAL;
- return (NULL);
-
- case IPSEC_POLICY_ENTRUST:
- sp = KEY_ALLOCSP_DEFAULT();
- break;
-
- case IPSEC_POLICY_IPSEC:
- key_addref(currsp);
- sp = currsp;
- break;
-
- default:
- ipseclog((LOG_ERR, "%s: Invalid policy for "
- "PCB %d\n", __func__, currsp->policy));
- *error = EINVAL;
- return (NULL);
- }
- }
+ if (dir == IPSEC_DIR_OUTBOUND)
+ inp->inp_sp->sp_out = sp;
+ else
+ inp->inp_sp->sp_in = sp;
+ /*
+ * SP is already referenced by the lookup code.
+ * We take extra reference here to avoid race in the
+ * ipsec_getpcbpolicy() function - SP will not be freed in the
+ * time between we take SP pointer from the cache and key_addref()
+ * call.
+ */
+ key_addref(sp);
+ genid = key_getspgen();
+ if (genid != inp->inp_sp->genid) {
+ ipsec_invalidate_cache(inp, dir);
+ inp->inp_sp->genid = genid;
}
- IPSEC_ASSERT(sp != NULL,
- ("null SP (priv %u policy %u", pcbsp->priv, currsp->policy));
- KEYDEBUG(KEYDEBUG_IPSEC_STAMP,
- printf("DP %s (priv %u policy %u) allocate SP:%p (refcnt %u)\n",
- __func__, pcbsp->priv, currsp->policy, sp, sp->refcnt));
- return (sp);
+ KEYDBG(IPSEC_STAMP,
+ printf("%s: PCB(%p): cached %s SP(%p)\n",
+ __func__, inp, dir == IPSEC_DIR_OUTBOUND ? "OUTBOUND":
+ "INBOUND", sp));
+ if (downgrade != 0)
+ INP_DOWNGRADE(inp);
}
-/*
- * For FORWADING packet or OUTBOUND without a socket. Searching SPD for packet,
- * and return a pointer to SP.
- * OUT: positive: a pointer to the entry for security policy leaf matched.
- * NULL: no apropreate SP found, the following value is set to error.
- * 0 : bypass
- * EACCES : discard packet.
- * ENOENT : ipsec_acquire() in progress, maybe.
- * others : error occurred.
- */
-struct secpolicy *
-ipsec_getpolicybyaddr(const struct mbuf *m, u_int dir, int *error)
+static struct secpolicy *
+ipsec_checkpolicy(struct secpolicy *sp, struct inpcb *inp, int *error)
{
- struct secpolicyindex spidx;
- struct secpolicy *sp;
- IPSEC_ASSERT(m != NULL, ("null mbuf"));
- IPSEC_ASSERT(error != NULL, ("null error"));
- IPSEC_ASSERT(dir == IPSEC_DIR_INBOUND || dir == IPSEC_DIR_OUTBOUND,
- ("invalid direction %u", dir));
+ /* Save found OUTBOUND policy into PCB SP cache. */
+ if (inp != NULL && inp->inp_sp != NULL && inp->inp_sp->sp_out == NULL)
+ ipsec_cachepolicy(inp, sp, IPSEC_DIR_OUTBOUND);
- sp = NULL;
- *error = 0;
- if (key_havesp(dir)) {
- /* Make an index to look for a policy. */
- *error = ipsec_setspidx(m, &spidx, 0);
- if (*error != 0) {
- DPRINTF(("%s: setpidx failed, dir %u\n",
- __func__, dir));
- return (NULL);
- }
- spidx.dir = dir;
- sp = KEY_ALLOCSP(&spidx, dir);
- }
- if (sp == NULL) /* No SP found, use system default. */
- sp = KEY_ALLOCSP_DEFAULT();
- IPSEC_ASSERT(sp != NULL, ("null SP"));
- return (sp);
-}
-
-struct secpolicy *
-ipsec4_checkpolicy(const struct mbuf *m, u_int dir, int *error,
- struct inpcb *inp)
-{
- struct secpolicy *sp;
-
- *error = 0;
- if (inp == NULL)
- sp = ipsec_getpolicybyaddr(m, dir, error);
- else
- sp = ipsec_getpolicybysock(m, dir, inp, error);
- if (sp == NULL) {
- IPSEC_ASSERT(*error != 0, ("getpolicy failed w/o error"));
- IPSECSTAT_INC(ips_out_inval);
- return (NULL);
- }
- IPSEC_ASSERT(*error == 0, ("sp w/ error set to %u", *error));
switch (sp->policy) {
- case IPSEC_POLICY_ENTRUST:
default:
printf("%s: invalid policy %u\n", __func__, sp->policy);
/* FALLTHROUGH */
case IPSEC_POLICY_DISCARD:
- IPSECSTAT_INC(ips_out_polvio);
*error = -EINVAL; /* Packet is discarded by caller. */
- break;
+ /* FALLTHROUGH */
case IPSEC_POLICY_BYPASS:
case IPSEC_POLICY_NONE:
- KEY_FREESP(&sp);
+ key_freesp(&sp);
sp = NULL; /* NB: force NULL result. */
break;
case IPSEC_POLICY_IPSEC:
- if (sp->req == NULL) /* Acquire a SA. */
- *error = key_spdacquire(sp);
+ /* XXXAE: handle LARVAL SP */
break;
}
- if (*error != 0) {
- KEY_FREESP(&sp);
- sp = NULL;
- }
+ KEYDBG(IPSEC_DUMP,
+ printf("%s: get SP(%p), error %d\n", __func__, sp, *error));
return (sp);
}
-static int
-ipsec_setspidx_inpcb(const struct mbuf *m, struct inpcb *inp, u_int dir)
+static struct secpolicy *
+ipsec_getpcbpolicy(struct inpcb *inp, u_int dir)
{
- struct secpolicyindex *spidx;
- int error;
+ struct secpolicy *sp;
+ int flags, downgrade;
- IPSEC_ASSERT(inp != NULL, ("null inp"));
- IPSEC_ASSERT(inp->inp_sp != NULL, ("null inp_sp"));
- IPSEC_ASSERT(inp->inp_sp->sp_out != NULL && inp->inp_sp->sp_in != NULL,
- ("null sp_in || sp_out"));
+ if (inp == NULL || inp->inp_sp == NULL)
+ return (NULL);
- if (dir == IPSEC_DIR_INBOUND)
- spidx = &inp->inp_sp->sp_in->spidx;
- else
- spidx = &inp->inp_sp->sp_out->spidx;
- error = ipsec_setspidx(m, spidx, 1);
- if (error == 0) {
- spidx->dir = dir;
+ INP_LOCK_ASSERT(inp);
+
+ flags = inp->inp_sp->flags;
+ if (dir == IPSEC_DIR_OUTBOUND) {
+ sp = inp->inp_sp->sp_out;
+ flags &= INP_OUTBOUND_POLICY;
} else {
- bzero(&inp->inp_sp->sp_in->spidx,
- sizeof (inp->inp_sp->sp_in->spidx));
- bzero(&inp->inp_sp->sp_out->spidx,
- sizeof (inp->inp_sp->sp_in->spidx));
+ sp = inp->inp_sp->sp_in;
+ flags &= INP_INBOUND_POLICY;
}
- return (error);
-}
-
-/*
- * Configure security policy index (src/dst/proto/sport/dport)
- * by looking at the content of mbuf.
- * The caller is responsible for error recovery (like clearing up spidx).
- */
-static int
-ipsec_setspidx(const struct mbuf *m, struct secpolicyindex *spidx,
- int needport)
-{
- struct ip ipbuf;
- const struct ip *ip = NULL;
- const struct mbuf *n;
- u_int v;
- int len;
- int error;
-
- IPSEC_ASSERT(m != NULL, ("null mbuf"));
-
/*
- * Validate m->m_pkthdr.len. We see incorrect length if we
- * mistakenly call this function with inconsistent mbuf chain
- * (like 4.4BSD tcp/udp processing). XXX Should we panic here?
+ * Check flags. If we have PCB SP, just return it.
+ * Otherwise we need to check that cached SP entry isn't stale.
*/
- len = 0;
- for (n = m; n; n = n->m_next)
- len += n->m_len;
- if (m->m_pkthdr.len != len) {
- KEYDEBUG(KEYDEBUG_IPSEC_DUMP,
- printf("%s: pkthdr len(%d) mismatch (%d), ignored.\n",
- __func__, len, m->m_pkthdr.len));
- return (EINVAL);
- }
-
- if (m->m_pkthdr.len < sizeof(struct ip)) {
- KEYDEBUG(KEYDEBUG_IPSEC_DUMP,
- printf("%s: pkthdr len(%d) too small (v4), ignored.\n",
- __func__, m->m_pkthdr.len));
- return (EINVAL);
- }
-
- if (m->m_len >= sizeof(*ip))
- ip = mtod(m, const struct ip *);
- else {
- m_copydata(m, 0, sizeof(ipbuf), (caddr_t)&ipbuf);
- ip = &ipbuf;
- }
- v = ip->ip_v;
- switch (v) {
- case 4:
- error = ipsec4_setspidx_ipaddr(m, spidx);
- if (error)
- return (error);
- ipsec4_get_ulp(m, spidx, needport);
- return (0);
-#ifdef INET6
- case 6:
- if (m->m_pkthdr.len < sizeof(struct ip6_hdr)) {
- KEYDEBUG(KEYDEBUG_IPSEC_DUMP,
- printf("%s: pkthdr len(%d) too small (v6), "
- "ignored\n", __func__, m->m_pkthdr.len));
- return (EINVAL);
+ if (flags == 0) {
+ if (sp == NULL)
+ return (NULL);
+ if (inp->inp_sp->genid != key_getspgen()) {
+ /* Invalidate the cache. */
+ downgrade = 0;
+ if (!INP_WLOCKED(inp)) {
+ if ((downgrade = INP_TRY_UPGRADE(inp)) == 0)
+ return (NULL);
+ }
+ ipsec_invalidate_cache(inp, IPSEC_DIR_OUTBOUND);
+ ipsec_invalidate_cache(inp, IPSEC_DIR_INBOUND);
+ if (downgrade != 0)
+ INP_DOWNGRADE(inp);
+ return (NULL);
}
- error = ipsec6_setspidx_ipaddr(m, spidx);
- if (error)
- return (error);
- ipsec6_get_ulp(m, spidx, needport);
- return (0);
-#endif
- default:
- KEYDEBUG(KEYDEBUG_IPSEC_DUMP,
- printf("%s: " "unknown IP version %u, ignored.\n",
- __func__, v));
- return (EINVAL);
+ KEYDBG(IPSEC_STAMP,
+ printf("%s: PCB(%p): cache hit SP(%p)\n",
+ __func__, inp, sp));
+ /* Return referenced cached policy */
}
+ key_addref(sp);
+ return (sp);
}
+#ifdef INET
static void
ipsec4_get_ulp(const struct mbuf *m, struct secpolicyindex *spidx,
int needport)
{
- u_int8_t nxt;
+ uint8_t nxt;
int off;
/* Sanity check. */
- IPSEC_ASSERT(m != NULL, ("null mbuf"));
- IPSEC_ASSERT(m->m_pkthdr.len >= sizeof(struct ip),("packet too short"));
+ IPSEC_ASSERT(m->m_pkthdr.len >= sizeof(struct ip),
+ ("packet too short"));
if (m->m_len >= sizeof (struct ip)) {
const struct ip *ip = mtod(m, const struct ip *);
@@ -675,61 +549,134 @@ done:
done_proto:
spidx->src.sin.sin_port = IPSEC_PORT_ANY;
spidx->dst.sin.sin_port = IPSEC_PORT_ANY;
+ KEYDBG(IPSEC_DUMP,
+ printf("%s: ", __func__); kdebug_secpolicyindex(spidx, NULL));
}
-/* Assumes that m is sane. */
-static int
+static void
ipsec4_setspidx_ipaddr(const struct mbuf *m, struct secpolicyindex *spidx)
{
- static const struct sockaddr_in template = {
- sizeof (struct sockaddr_in),
- AF_INET,
- 0, { 0 }, { 0, 0, 0, 0, 0, 0, 0, 0 }
- };
- spidx->src.sin = template;
- spidx->dst.sin = template;
+ ipsec4_setsockaddrs(m, &spidx->src, &spidx->dst);
+ spidx->prefs = sizeof(struct in_addr) << 3;
+ spidx->prefd = sizeof(struct in_addr) << 3;
+}
- if (m->m_len < sizeof (struct ip)) {
- m_copydata(m, offsetof(struct ip, ip_src),
- sizeof (struct in_addr),
- (caddr_t) &spidx->src.sin.sin_addr);
- m_copydata(m, offsetof(struct ip, ip_dst),
- sizeof (struct in_addr),
- (caddr_t) &spidx->dst.sin.sin_addr);
- } else {
- const struct ip *ip = mtod(m, const struct ip *);
- spidx->src.sin.sin_addr = ip->ip_src;
- spidx->dst.sin.sin_addr = ip->ip_dst;
+static struct secpolicy *
+ipsec4_getpolicy(const struct mbuf *m, struct inpcb *inp, u_int dir)
+{
+ struct secpolicyindex spidx;
+ struct secpolicy *sp;
+
+ sp = ipsec_getpcbpolicy(inp, dir);
+ if (sp == NULL && key_havesp(dir)) {
+ /* Make an index to look for a policy. */
+ ipsec4_setspidx_ipaddr(m, &spidx);
+ /* Fill ports in spidx if we have inpcb. */
+ ipsec4_get_ulp(m, &spidx, inp != NULL);
+ spidx.dir = dir;
+ sp = key_allocsp(&spidx, dir);
}
+ if (sp == NULL) /* No SP found, use system default. */
+ sp = key_allocsp_default();
+ return (sp);
+}
- spidx->prefs = sizeof(struct in_addr) << 3;
- spidx->prefd = sizeof(struct in_addr) << 3;
+/*
+ * Check security policy for *OUTBOUND* IPv4 packet.
+ */
+struct secpolicy *
+ipsec4_checkpolicy(const struct mbuf *m, struct inpcb *inp, int *error)
+{
+ struct secpolicy *sp;
- return (0);
+ *error = 0;
+ sp = ipsec4_getpolicy(m, inp, IPSEC_DIR_OUTBOUND);
+ if (sp != NULL)
+ sp = ipsec_checkpolicy(sp, inp, error);
+ if (sp == NULL) {
+ switch (*error) {
+ case 0: /* No IPsec required: BYPASS or NONE */
+ break;
+ case -EINVAL:
+ IPSECSTAT_INC(ips_out_polvio);
+ break;
+ default:
+ IPSECSTAT_INC(ips_out_inval);
+ }
+ }
+ KEYDBG(IPSEC_STAMP,
+ printf("%s: using SP(%p), error %d\n", __func__, sp, *error));
+ if (sp != NULL)
+ KEYDBG(IPSEC_DATA, kdebug_secpolicy(sp));
+ return (sp);
+}
+
+/*
+ * Check IPv4 packet against *INBOUND* security policy.
+ * This function is called from tcp_input(), udp_input(),
+ * rip_input() and sctp_input().
+ */
+int
+ipsec4_in_reject(const struct mbuf *m, struct inpcb *inp)
+{
+ struct secpolicy *sp;
+ int result;
+
+ sp = ipsec4_getpolicy(m, inp, IPSEC_DIR_INBOUND);
+ result = ipsec_in_reject(sp, inp, m);
+ key_freesp(&sp);
+ if (result != 0)
+ IPSECSTAT_INC(ips_in_polvio);
+ return (result);
+}
+
+/*
+ * IPSEC_CAP() method implementation for IPv4.
+ */
+int
+ipsec4_capability(struct mbuf *m, u_int cap)
+{
+
+ switch (cap) {
+ case IPSEC_CAP_BYPASS_FILTER:
+ /*
+ * Bypass packet filtering for packets previously handled
+ * by IPsec.
+ */
+ if (!V_ip4_filtertunnel &&
+ m_tag_find(m, PACKET_TAG_IPSEC_IN_DONE, NULL) != NULL)
+ return (1);
+ return (0);
+ case IPSEC_CAP_OPERABLE:
+ /* Do we have active security policies? */
+ if (key_havesp(IPSEC_DIR_INBOUND) != 0 ||
+ key_havesp(IPSEC_DIR_OUTBOUND) != 0)
+ return (1);
+ return (0);
+ };
+ return (EOPNOTSUPP);
}
+#endif /* INET */
+
#ifdef INET6
static void
ipsec6_get_ulp(const struct mbuf *m, struct secpolicyindex *spidx,
int needport)
{
- int off, nxt;
struct tcphdr th;
struct udphdr uh;
struct icmp6_hdr ih;
+ int off, nxt;
- /* Sanity check. */
- if (m == NULL)
- panic("%s: NULL pointer was passed.\n", __func__);
-
- KEYDEBUG(KEYDEBUG_IPSEC_DUMP,
- printf("%s:\n", __func__); kdebug_mbuf(m));
+ IPSEC_ASSERT(m->m_pkthdr.len >= sizeof(struct ip6_hdr),
+ ("packet too short"));
/* Set default. */
spidx->ul_proto = IPSEC_ULPROTO_ANY;
- ((struct sockaddr_in6 *)&spidx->src)->sin6_port = IPSEC_PORT_ANY;
- ((struct sockaddr_in6 *)&spidx->dst)->sin6_port = IPSEC_PORT_ANY;
+ spidx->src.sin6.sin6_port = IPSEC_PORT_ANY;
+ spidx->dst.sin6.sin6_port = IPSEC_PORT_ANY;
nxt = -1;
off = ip6_lasthdr(m, 0, IPPROTO_IPV6, &nxt);
@@ -744,8 +691,8 @@ ipsec6_get_ulp(const struct mbuf *m, struct secpolicyindex *spidx,
if (off + sizeof(struct tcphdr) > m->m_pkthdr.len)
break;
m_copydata(m, off, sizeof(th), (caddr_t)&th);
- ((struct sockaddr_in6 *)&spidx->src)->sin6_port = th.th_sport;
- ((struct sockaddr_in6 *)&spidx->dst)->sin6_port = th.th_dport;
+ spidx->src.sin6.sin6_port = th.th_sport;
+ spidx->dst.sin6.sin6_port = th.th_dport;
break;
case IPPROTO_UDP:
spidx->ul_proto = nxt;
@@ -754,377 +701,157 @@ ipsec6_get_ulp(const struct mbuf *m, struct secpolicyindex *spidx,
if (off + sizeof(struct udphdr) > m->m_pkthdr.len)
break;
m_copydata(m, off, sizeof(uh), (caddr_t)&uh);
- ((struct sockaddr_in6 *)&spidx->src)->sin6_port = uh.uh_sport;
- ((struct sockaddr_in6 *)&spidx->dst)->sin6_port = uh.uh_dport;
+ spidx->src.sin6.sin6_port = uh.uh_sport;
+ spidx->dst.sin6.sin6_port = uh.uh_dport;
break;
case IPPROTO_ICMPV6:
spidx->ul_proto = nxt;
if (off + sizeof(struct icmp6_hdr) > m->m_pkthdr.len)
break;
m_copydata(m, off, sizeof(ih), (caddr_t)&ih);
- ((struct sockaddr_in6 *)&spidx->src)->sin6_port =
- htons((uint16_t)ih.icmp6_type);
- ((struct sockaddr_in6 *)&spidx->dst)->sin6_port =
- htons((uint16_t)ih.icmp6_code);
+ spidx->src.sin6.sin6_port = htons((uint16_t)ih.icmp6_type);
+ spidx->dst.sin6.sin6_port = htons((uint16_t)ih.icmp6_code);
break;
default:
/* XXX Intermediate headers??? */
spidx->ul_proto = nxt;
break;
}
+ KEYDBG(IPSEC_DUMP,
+ printf("%s: ", __func__); kdebug_secpolicyindex(spidx, NULL));
}
-/* Assumes that m is sane. */
-static int
+static void
ipsec6_setspidx_ipaddr(const struct mbuf *m, struct secpolicyindex *spidx)
{
- struct ip6_hdr ip6buf;
- const struct ip6_hdr *ip6 = NULL;
- struct sockaddr_in6 *sin6;
-
- if (m->m_len >= sizeof(*ip6))
- ip6 = mtod(m, const struct ip6_hdr *);
- else {
- m_copydata(m, 0, sizeof(ip6buf), (caddr_t)&ip6buf);
- ip6 = &ip6buf;
- }
- sin6 = (struct sockaddr_in6 *)&spidx->src;
- bzero(sin6, sizeof(*sin6));
- sin6->sin6_family = AF_INET6;
- sin6->sin6_len = sizeof(struct sockaddr_in6);
- bcopy(&ip6->ip6_src, &sin6->sin6_addr, sizeof(ip6->ip6_src));
- if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_src)) {
- sin6->sin6_addr.s6_addr16[1] = 0;
- sin6->sin6_scope_id = ntohs(ip6->ip6_src.s6_addr16[1]);
- }
+ ipsec6_setsockaddrs(m, &spidx->src, &spidx->dst);
spidx->prefs = sizeof(struct in6_addr) << 3;
-
- sin6 = (struct sockaddr_in6 *)&spidx->dst;
- bzero(sin6, sizeof(*sin6));
- sin6->sin6_family = AF_INET6;
- sin6->sin6_len = sizeof(struct sockaddr_in6);
- bcopy(&ip6->ip6_dst, &sin6->sin6_addr, sizeof(ip6->ip6_dst));
- if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_dst)) {
- sin6->sin6_addr.s6_addr16[1] = 0;
- sin6->sin6_scope_id = ntohs(ip6->ip6_dst.s6_addr16[1]);
- }
spidx->prefd = sizeof(struct in6_addr) << 3;
-
- return (0);
}
-#endif
-int
-ipsec_run_hhooks(struct ipsec_ctx_data *ctx, int type)
+static struct secpolicy *
+ipsec6_getpolicy(const struct mbuf *m, struct inpcb *inp, u_int dir)
{
- int idx;
+ struct secpolicyindex spidx;
+ struct secpolicy *sp;
- switch (ctx->af) {
-#ifdef INET
- case AF_INET:
- idx = HHOOK_IPSEC_INET;
- break;
-#endif
-#ifdef INET6
- case AF_INET6:
- idx = HHOOK_IPSEC_INET6;
- break;
-#endif
- default:
- return (EPFNOSUPPORT);
+ sp = ipsec_getpcbpolicy(inp, dir);
+ if (sp == NULL && key_havesp(dir)) {
+ /* Make an index to look for a policy. */
+ ipsec6_setspidx_ipaddr(m, &spidx);
+ /* Fill ports in spidx if we have inpcb. */
+ ipsec6_get_ulp(m, &spidx, inp != NULL);
+ spidx.dir = dir;
+ sp = key_allocsp(&spidx, dir);
}
- if (type == HHOOK_TYPE_IPSEC_IN)
- HHOOKS_RUN_IF(V_ipsec_hhh_in[idx], ctx, NULL);
- else
- HHOOKS_RUN_IF(V_ipsec_hhh_out[idx], ctx, NULL);
- if (*ctx->mp == NULL)
- return (EACCES);
- return (0);
-}
-
-static void
-ipsec_delpcbpolicy(struct inpcbpolicy *p)
-{
-
- free(p, M_IPSEC_INPCB);
+ if (sp == NULL) /* No SP found, use system default. */
+ sp = key_allocsp_default();
+ return (sp);
}
-/* Initialize policy in PCB. */
-int
-ipsec_init_policy(struct socket *so, struct inpcbpolicy **pcb_sp)
+/*
+ * Check security policy for *OUTBOUND* IPv6 packet.
+ */
+struct secpolicy *
+ipsec6_checkpolicy(const struct mbuf *m, struct inpcb *inp, int *error)
{
- struct inpcbpolicy *new;
-
- /* Sanity check. */
- if (so == NULL || pcb_sp == NULL)
- panic("%s: NULL pointer was passed.\n", __func__);
-
- new = (struct inpcbpolicy *) malloc(sizeof(struct inpcbpolicy),
- M_IPSEC_INPCB, M_NOWAIT|M_ZERO);
- if (new == NULL) {
- ipseclog((LOG_DEBUG, "%s: No more memory.\n", __func__));
- return (ENOBUFS);
- }
-
- new->priv = IPSEC_IS_PRIVILEGED_SO(so);
+ struct secpolicy *sp;
- if ((new->sp_in = KEY_NEWSP()) == NULL) {
- ipsec_delpcbpolicy(new);
- return (ENOBUFS);
- }
- new->sp_in->policy = IPSEC_POLICY_ENTRUST;
- if ((new->sp_out = KEY_NEWSP()) == NULL) {
- KEY_FREESP(&new->sp_in);
- ipsec_delpcbpolicy(new);
- return (ENOBUFS);
+ *error = 0;
+ sp = ipsec6_getpolicy(m, inp, IPSEC_DIR_OUTBOUND);
+ if (sp != NULL)
+ sp = ipsec_checkpolicy(sp, inp, error);
+ if (sp == NULL) {
+ switch (*error) {
+ case 0: /* No IPsec required: BYPASS or NONE */
+ break;
+ case -EINVAL:
+ IPSEC6STAT_INC(ips_out_polvio);
+ break;
+ default:
+ IPSEC6STAT_INC(ips_out_inval);
+ }
}
- new->sp_out->policy = IPSEC_POLICY_ENTRUST;
- *pcb_sp = new;
-
- return (0);
+ KEYDBG(IPSEC_STAMP,
+ printf("%s: using SP(%p), error %d\n", __func__, sp, *error));
+ if (sp != NULL)
+ KEYDBG(IPSEC_DATA, kdebug_secpolicy(sp));
+ return (sp);
}
-/* Copy old IPsec policy into new. */
+/*
+ * Check IPv6 packet against inbound security policy.
+ * This function is called from tcp6_input(), udp6_input(),
+ * rip6_input() and sctp_input().
+ */
int
-ipsec_copy_policy(struct inpcbpolicy *old, struct inpcbpolicy *new)
+ipsec6_in_reject(const struct mbuf *m, struct inpcb *inp)
{
struct secpolicy *sp;
+ int result;
- sp = ipsec_deepcopy_policy(old->sp_in);
- if (sp) {
- KEY_FREESP(&new->sp_in);
- new->sp_in = sp;
- } else
- return (ENOBUFS);
-
- sp = ipsec_deepcopy_policy(old->sp_out);
- if (sp) {
- KEY_FREESP(&new->sp_out);
- new->sp_out = sp;
- } else
- return (ENOBUFS);
-
- new->priv = old->priv;
-
- return (0);
-}
-
-struct ipsecrequest *
-ipsec_newisr(void)
-{
- struct ipsecrequest *p;
-
- p = malloc(sizeof(struct ipsecrequest), M_IPSEC_SR, M_NOWAIT|M_ZERO);
- if (p != NULL)
- IPSECREQUEST_LOCK_INIT(p);
- return (p);
-}
-
-void
-ipsec_delisr(struct ipsecrequest *p)
-{
-
- IPSECREQUEST_LOCK_DESTROY(p);
- free(p, M_IPSEC_SR);
-}
-
-/* Deep-copy a policy in PCB. */
-static struct secpolicy *
-ipsec_deepcopy_policy(struct secpolicy *src)
-{
- struct ipsecrequest *newchain = NULL;
- struct ipsecrequest *p;
- struct ipsecrequest **q;
- struct ipsecrequest *r;
- struct secpolicy *dst;
-
- if (src == NULL)
- return (NULL);
- dst = KEY_NEWSP();
- if (dst == NULL)
- return (NULL);
-
- /*
- * Deep-copy IPsec request chain. This is required since struct
- * ipsecrequest is not reference counted.
- */
- q = &newchain;
- for (p = src->req; p; p = p->next) {
- *q = ipsec_newisr();
- if (*q == NULL)
- goto fail;
- (*q)->saidx.proto = p->saidx.proto;
- (*q)->saidx.mode = p->saidx.mode;
- (*q)->level = p->level;
- (*q)->saidx.reqid = p->saidx.reqid;
-
- bcopy(&p->saidx.src, &(*q)->saidx.src, sizeof((*q)->saidx.src));
- bcopy(&p->saidx.dst, &(*q)->saidx.dst, sizeof((*q)->saidx.dst));
-
- (*q)->sp = dst;
-
- q = &((*q)->next);
- }
-
- dst->req = newchain;
- dst->policy = src->policy;
- /* Do not touch the refcnt fields. */
-
- return (dst);
-
-fail:
- for (p = newchain; p; p = r) {
- r = p->next;
- ipsec_delisr(p);
- p = NULL;
- }
- KEY_FREESP(&dst);
- return (NULL);
-}
-
-/* Set policy and IPsec request if present. */
-static int
-ipsec_set_policy_internal(struct secpolicy **pcb_sp, int optname,
- caddr_t request, size_t len, struct ucred *cred)
-{
- struct sadb_x_policy *xpl;
- struct secpolicy *newsp = NULL;
- int error;
-
- /* Sanity check. */
- if (pcb_sp == NULL || *pcb_sp == NULL || request == NULL)
- return (EINVAL);
- if (len < sizeof(*xpl))
- return (EINVAL);
- xpl = (struct sadb_x_policy *)request;
-
- KEYDEBUG(KEYDEBUG_IPSEC_DUMP,
- printf("%s: passed policy\n", __func__);
- kdebug_sadb_x_policy((struct sadb_ext *)xpl));
-
- /* Check policy type. */
- /* ipsec_set_policy_internal() accepts IPSEC, ENTRUST and BYPASS. */
- if (xpl->sadb_x_policy_type == IPSEC_POLICY_DISCARD
- || xpl->sadb_x_policy_type == IPSEC_POLICY_NONE)
- return (EINVAL);
-
- /* Check privileged socket. */
- if (cred != NULL && xpl->sadb_x_policy_type == IPSEC_POLICY_BYPASS) {
- error = priv_check_cred(cred, PRIV_NETINET_IPSEC, 0);
- if (error)
- return (EACCES);
- }
-
- /* Allocating new SP entry. */
- if ((newsp = key_msg2sp(xpl, len, &error)) == NULL)
- return (error);
-
- /* Clear old SP and set new SP. */
- KEY_FREESP(pcb_sp);
- *pcb_sp = newsp;
- KEYDEBUG(KEYDEBUG_IPSEC_DUMP,
- printf("%s: new policy\n", __func__);
- kdebug_secpolicy(newsp));
-
- return (0);
+ sp = ipsec6_getpolicy(m, inp, IPSEC_DIR_INBOUND);
+ result = ipsec_in_reject(sp, inp, m);
+ key_freesp(&sp);
+ if (result)
+ IPSEC6STAT_INC(ips_in_polvio);
+ return (result);
}
+/*
+ * IPSEC_CAP() method implementation for IPv6.
+ */
int
-ipsec_set_policy(struct inpcb *inp, int optname, caddr_t request,
- size_t len, struct ucred *cred)
+ipsec6_capability(struct mbuf *m, u_int cap)
{
- struct sadb_x_policy *xpl;
- struct secpolicy **pcb_sp;
-
- /* Sanity check. */
- if (inp == NULL || request == NULL)
- return (EINVAL);
- if (len < sizeof(*xpl))
- return (EINVAL);
- xpl = (struct sadb_x_policy *)request;
-
- /* Select direction. */
- switch (xpl->sadb_x_policy_dir) {
- case IPSEC_DIR_INBOUND:
- pcb_sp = &inp->inp_sp->sp_in;
- break;
- case IPSEC_DIR_OUTBOUND:
- pcb_sp = &inp->inp_sp->sp_out;
- break;
- default:
- ipseclog((LOG_ERR, "%s: invalid direction=%u\n", __func__,
- xpl->sadb_x_policy_dir));
- return (EINVAL);
- }
- return (ipsec_set_policy_internal(pcb_sp, optname, request, len, cred));
+ switch (cap) {
+ case IPSEC_CAP_BYPASS_FILTER:
+ /*
+ * Bypass packet filtering for packets previously handled
+ * by IPsec.
+ */
+ if (!V_ip6_filtertunnel &&
+ m_tag_find(m, PACKET_TAG_IPSEC_IN_DONE, NULL) != NULL)
+ return (1);
+ return (0);
+ case IPSEC_CAP_OPERABLE:
+ /* Do we have active security policies? */
+ if (key_havesp(IPSEC_DIR_INBOUND) != 0 ||
+ key_havesp(IPSEC_DIR_OUTBOUND) != 0)
+ return (1);
+ return (0);
+ };
+ return (EOPNOTSUPP);
}
+#endif /* INET6 */
int
-ipsec_get_policy(struct inpcb *inp, caddr_t request, size_t len,
- struct mbuf **mp)
+ipsec_run_hhooks(struct ipsec_ctx_data *ctx, int type)
{
- struct sadb_x_policy *xpl;
- struct secpolicy *pcb_sp;
+ int idx;
- /* Sanity check. */
- if (inp == NULL || request == NULL || mp == NULL)
- return (EINVAL);
- IPSEC_ASSERT(inp->inp_sp != NULL, ("null inp_sp"));
- if (len < sizeof(*xpl))
- return (EINVAL);
- xpl = (struct sadb_x_policy *)request;
-
- /* Select direction. */
- switch (xpl->sadb_x_policy_dir) {
- case IPSEC_DIR_INBOUND:
- pcb_sp = inp->inp_sp->sp_in;
+ switch (ctx->af) {
+#ifdef INET
+ case AF_INET:
+ idx = HHOOK_IPSEC_INET;
break;
- case IPSEC_DIR_OUTBOUND:
- pcb_sp = inp->inp_sp->sp_out;
+#endif
+#ifdef INET6
+ case AF_INET6:
+ idx = HHOOK_IPSEC_INET6;
break;
+#endif
default:
- ipseclog((LOG_ERR, "%s: invalid direction=%u\n", __func__,
- xpl->sadb_x_policy_dir));
- return (EINVAL);
- }
-
- /* Sanity check. Should be an IPSEC_ASSERT. */
- if (pcb_sp == NULL)
- return (EINVAL);
-
- *mp = key_sp2msg(pcb_sp);
- if (!*mp) {
- ipseclog((LOG_DEBUG, "%s: No more memory.\n", __func__));
- return (ENOBUFS);
+ return (EPFNOSUPPORT);
}
-
- (*mp)->m_type = MT_DATA;
- KEYDEBUG(KEYDEBUG_IPSEC_DUMP,
- printf("%s:\n", __func__); kdebug_mbuf(*mp));
-
- return (0);
-}
-
-/* Delete policy in PCB. */
-int
-ipsec_delete_pcbpolicy(struct inpcb *inp)
-{
- IPSEC_ASSERT(inp != NULL, ("null inp"));
-
- if (inp->inp_sp == NULL)
- return (0);
-
- if (inp->inp_sp->sp_in != NULL)
- KEY_FREESP(&inp->inp_sp->sp_in);
-
- if (inp->inp_sp->sp_out != NULL)
- KEY_FREESP(&inp->inp_sp->sp_out);
-
- ipsec_delpcbpolicy(inp->inp_sp);
- inp->inp_sp = NULL;
-
+ if (type == HHOOK_TYPE_IPSEC_IN)
+ HHOOKS_RUN_IF(V_ipsec_hhh_in[idx], ctx, NULL);
+ else
+ HHOOKS_RUN_IF(V_ipsec_hhh_out[idx], ctx, NULL);
+ if (*ctx->mp == NULL)
+ return (EACCES);
return (0);
}
@@ -1133,32 +860,36 @@ ipsec_delete_pcbpolicy(struct inpcb *inp)
* Either IPSEC_LEVEL_USE or IPSEC_LEVEL_REQUIRE are always returned.
*/
u_int
-ipsec_get_reqlevel(struct ipsecrequest *isr)
+ipsec_get_reqlevel(struct secpolicy *sp, u_int idx)
{
- u_int level = 0;
+ struct ipsecrequest *isr;
u_int esp_trans_deflev, esp_net_deflev;
u_int ah_trans_deflev, ah_net_deflev;
+ u_int level = 0;
- IPSEC_ASSERT(isr != NULL && isr->sp != NULL, ("null argument"));
- IPSEC_ASSERT(isr->sp->spidx.src.sa.sa_family == isr->sp->spidx.dst.sa.sa_family,
- ("af family mismatch, src %u, dst %u",
- isr->sp->spidx.src.sa.sa_family,
- isr->sp->spidx.dst.sa.sa_family));
-
+ IPSEC_ASSERT(idx < sp->tcount, ("Wrong IPsec request index %d", idx));
/* XXX Note that we have ipseclog() expanded here - code sync issue. */
#define IPSEC_CHECK_DEFAULT(lev) \
- (((lev) != IPSEC_LEVEL_USE && (lev) != IPSEC_LEVEL_REQUIRE \
- && (lev) != IPSEC_LEVEL_UNIQUE) \
- ? (V_ipsec_debug \
- ? log(LOG_INFO, "fixed system default level " #lev ":%d->%d\n",\
- (lev), IPSEC_LEVEL_REQUIRE) \
- : 0), \
- (lev) = IPSEC_LEVEL_REQUIRE, \
- (lev) \
- : (lev))
+ (((lev) != IPSEC_LEVEL_USE && (lev) != IPSEC_LEVEL_REQUIRE && \
+ (lev) != IPSEC_LEVEL_UNIQUE) \
+ ? (V_ipsec_debug ? \
+ log(LOG_INFO, "fixed system default level " #lev ":%d->%d\n",\
+ (lev), IPSEC_LEVEL_REQUIRE) : 0), \
+ (lev) = IPSEC_LEVEL_REQUIRE, (lev) : (lev))
+
+ /*
+ * IPsec VTI uses unique security policy with fake spidx filled
+ * with zeroes. Just return IPSEC_LEVEL_REQUIRE instead of doing
+ * full level lookup for such policies.
+ */
+ if (sp->state == IPSEC_SPSTATE_IFNET) {
+ IPSEC_ASSERT(sp->req[idx]->level == IPSEC_LEVEL_UNIQUE,
+ ("Wrong IPsec request level %d", sp->req[idx]->level));
+ return (IPSEC_LEVEL_REQUIRE);
+ }
/* Set default level. */
- switch (((struct sockaddr *)&isr->sp->spidx.src)->sa_family) {
+ switch (sp->spidx.src.sa.sa_family) {
#ifdef INET
case AF_INET:
esp_trans_deflev = IPSEC_CHECK_DEFAULT(V_ip4_esp_trans_deflev);
@@ -1177,11 +908,12 @@ ipsec_get_reqlevel(struct ipsecrequest *isr)
#endif /* INET6 */
default:
panic("%s: unknown af %u",
- __func__, isr->sp->spidx.src.sa.sa_family);
+ __func__, sp->spidx.src.sa.sa_family);
}
#undef IPSEC_CHECK_DEFAULT
+ isr = sp->req[idx];
/* Set level. */
switch (isr->level) {
case IPSEC_LEVEL_DEFAULT:
@@ -1226,6 +958,45 @@ ipsec_get_reqlevel(struct ipsecrequest *isr)
return (level);
}
+static int
+ipsec_check_history(const struct mbuf *m, struct secpolicy *sp, u_int idx)
+{
+ struct xform_history *xh;
+ struct m_tag *mtag;
+
+ mtag = NULL;
+ while ((mtag = m_tag_find(__DECONST(struct mbuf *, m),
+ PACKET_TAG_IPSEC_IN_DONE, mtag)) != NULL) {
+ xh = (struct xform_history *)(mtag + 1);
+ KEYDBG(IPSEC_DATA,
+ char buf[IPSEC_ADDRSTRLEN];
+ printf("%s: mode %s proto %u dst %s\n", __func__,
+ kdebug_secasindex_mode(xh->mode), xh->proto,
+ ipsec_address(&xh->dst, buf, sizeof(buf))));
+ if (xh->proto != sp->req[idx]->saidx.proto)
+ continue;
+ /* If SA had IPSEC_MODE_ANY, consider this as match. */
+ if (xh->mode != sp->req[idx]->saidx.mode &&
+ xh->mode != IPSEC_MODE_ANY)
+ continue;
+ /*
+ * For transport mode IPsec request doesn't contain
+ * addresses. We need to use address from spidx.
+ */
+ if (sp->req[idx]->saidx.mode == IPSEC_MODE_TRANSPORT) {
+ if (key_sockaddrcmp_withmask(&xh->dst.sa,
+ &sp->spidx.dst.sa, sp->spidx.prefd) != 0)
+ continue;
+ } else {
+ if (key_sockaddrcmp(&xh->dst.sa,
+ &sp->req[idx]->saidx.dst.sa, 0) != 0)
+ continue;
+ }
+ return (0); /* matched */
+ }
+ return (1);
+}
+
/*
* Check security policy requirements against the actual
* packet contents. Return one if the packet should be
@@ -1237,13 +1008,16 @@ ipsec_get_reqlevel(struct ipsecrequest *isr)
* 1: invalid
*/
static int
-ipsec_in_reject(struct secpolicy *sp, const struct mbuf *m)
+ipsec_in_reject(struct secpolicy *sp, struct inpcb *inp, const struct mbuf *m)
{
- struct ipsecrequest *isr;
- int need_auth;
+ int i;
+
+ KEYDBG(IPSEC_STAMP,
+ printf("%s: PCB(%p): using SP(%p)\n", __func__, inp, sp));
+ KEYDBG(IPSEC_DATA, kdebug_secpolicy(sp));
- KEYDEBUG(KEYDEBUG_IPSEC_DATA,
- printf("%s: using SP\n", __func__); kdebug_secpolicy(sp));
+ if (inp != NULL && inp->inp_sp != NULL && inp->inp_sp->sp_in == NULL)
+ ipsec_cachepolicy(inp, sp, IPSEC_DIR_INBOUND);
/* Check policy. */
switch (sp->policy) {
@@ -1257,132 +1031,59 @@ ipsec_in_reject(struct secpolicy *sp, const struct mbuf *m)
IPSEC_ASSERT(sp->policy == IPSEC_POLICY_IPSEC,
("invalid policy %u", sp->policy));
- /* XXX Should compare policy against IPsec header history. */
-
- need_auth = 0;
- for (isr = sp->req; isr != NULL; isr = isr->next) {
- if (ipsec_get_reqlevel(isr) != IPSEC_LEVEL_REQUIRE)
+ /*
+ * ipsec[46]_common_input_cb after each transform adds
+ * PACKET_TAG_IPSEC_IN_DONE mbuf tag. It contains SPI, proto, mode
+ * and destination address from saidx. We can compare info from
+ * these tags with requirements in SP.
+ */
+ for (i = 0; i < sp->tcount; i++) {
+ /*
+ * Do not check IPcomp, since IPcomp document
+ * says that we shouldn't compress small packets.
+ * IPComp policy should always be treated as being
+ * in "use" level.
+ */
+ if (sp->req[i]->saidx.proto == IPPROTO_IPCOMP ||
+ ipsec_get_reqlevel(sp, i) != IPSEC_LEVEL_REQUIRE)
continue;
- switch (isr->saidx.proto) {
+ if (V_check_policy_history != 0 &&
+ ipsec_check_history(m, sp, i) != 0)
+ return (1);
+ else switch (sp->req[i]->saidx.proto) {
case IPPROTO_ESP:
if ((m->m_flags & M_DECRYPTED) == 0) {
- KEYDEBUG(KEYDEBUG_IPSEC_DUMP,
+ KEYDBG(IPSEC_DUMP,
printf("%s: ESP m_flags:%x\n", __func__,
m->m_flags));
return (1);
}
-
- if (!need_auth &&
- isr->sav != NULL &&
- isr->sav->tdb_authalgxform != NULL &&
- (m->m_flags & M_AUTHIPDGM) == 0) {
- KEYDEBUG(KEYDEBUG_IPSEC_DUMP,
- printf("%s: ESP/AH m_flags:%x\n", __func__,
- m->m_flags));
- return (1);
- }
break;
case IPPROTO_AH:
- need_auth = 1;
if ((m->m_flags & M_AUTHIPHDR) == 0) {
- KEYDEBUG(KEYDEBUG_IPSEC_DUMP,
+ KEYDBG(IPSEC_DUMP,
printf("%s: AH m_flags:%x\n", __func__,
m->m_flags));
return (1);
}
break;
- case IPPROTO_IPCOMP:
- /*
- * We don't really care, as IPcomp document
- * says that we shouldn't compress small
- * packets. IPComp policy should always be
- * treated as being in "use" level.
- */
- break;
}
}
return (0); /* Valid. */
}
/*
- * Non zero return value means security policy DISCARD or policy violation.
- */
-static int
-ipsec46_in_reject(const struct mbuf *m, struct inpcb *inp)
-{
- struct secpolicy *sp;
- int error;
- int result;
-
- if (!key_havesp(IPSEC_DIR_INBOUND))
- return 0;
-
- IPSEC_ASSERT(m != NULL, ("null mbuf"));
-
- /* Get SP for this packet. */
- if (inp == NULL)
- sp = ipsec_getpolicybyaddr(m, IPSEC_DIR_INBOUND, &error);
- else
- sp = ipsec_getpolicybysock(m, IPSEC_DIR_INBOUND, inp, &error);
-
- if (sp != NULL) {
- result = ipsec_in_reject(sp, m);
- KEY_FREESP(&sp);
- } else {
- result = 1; /* treat errors as policy violation */
- }
- return (result);
-}
-
-/*
- * Check AH/ESP integrity.
- * This function is called from tcp_input(), udp_input(),
- * and {ah,esp}4_input for tunnel mode.
- */
-int
-ipsec4_in_reject(const struct mbuf *m, struct inpcb *inp)
-{
- int result;
-
- result = ipsec46_in_reject(m, inp);
- if (result)
- IPSECSTAT_INC(ips_in_polvio);
-
- return (result);
-}
-
-#ifdef INET6
-/*
- * Check AH/ESP integrity.
- * This function is called from tcp6_input(), udp6_input(),
- * and {ah,esp}6_input for tunnel mode.
- */
-int
-ipsec6_in_reject(const struct mbuf *m, struct inpcb *inp)
-{
- int result;
-
- result = ipsec46_in_reject(m, inp);
- if (result)
- IPSEC6STAT_INC(ips_in_polvio);
-
- return (result);
-}
-#endif
-
-/*
* Compute the byte size to be occupied by IPsec header.
* In case it is tunnelled, it includes the size of outer IP header.
- * NOTE: SP passed is freed in this function.
*/
static size_t
ipsec_hdrsiz_internal(struct secpolicy *sp)
{
- struct ipsecrequest *isr;
size_t size;
+ int i;
- KEYDEBUG(KEYDEBUG_IPSEC_DATA,
- printf("%s: using SP\n", __func__); kdebug_secpolicy(sp));
+ KEYDBG(IPSEC_STAMP, printf("%s: using SP(%p)\n", __func__, sp));
+ KEYDBG(IPSEC_DATA, kdebug_secpolicy(sp));
switch (sp->policy) {
case IPSEC_POLICY_DISCARD:
@@ -1394,80 +1095,69 @@ ipsec_hdrsiz_internal(struct secpolicy *sp)
IPSEC_ASSERT(sp->policy == IPSEC_POLICY_IPSEC,
("invalid policy %u", sp->policy));
+ /*
+ * XXX: for each transform we need to lookup suitable SA
+ * and use info from SA to calculate headers size.
+ * XXX: for NAT-T we need to cosider UDP header size.
+ */
size = 0;
- for (isr = sp->req; isr != NULL; isr = isr->next) {
- size_t clen = 0;
-
- switch (isr->saidx.proto) {
+ for (i = 0; i < sp->tcount; i++) {
+ switch (sp->req[i]->saidx.proto) {
case IPPROTO_ESP:
- clen = esp_hdrsiz(isr->sav);
+ size += esp_hdrsiz(NULL);
break;
case IPPROTO_AH:
- clen = ah_hdrsiz(isr->sav);
+ size += ah_hdrsiz(NULL);
break;
case IPPROTO_IPCOMP:
- clen = sizeof(struct ipcomp);
+ size += sizeof(struct ipcomp);
break;
}
- if (isr->saidx.mode == IPSEC_MODE_TUNNEL) {
- switch (isr->saidx.dst.sa.sa_family) {
+ if (sp->req[i]->saidx.mode == IPSEC_MODE_TUNNEL) {
+ switch (sp->req[i]->saidx.dst.sa.sa_family) {
+#ifdef INET
case AF_INET:
- clen += sizeof(struct ip);
+ size += sizeof(struct ip);
break;
+#endif
#ifdef INET6
case AF_INET6:
- clen += sizeof(struct ip6_hdr);
+ size += sizeof(struct ip6_hdr);
break;
#endif
default:
ipseclog((LOG_ERR, "%s: unknown AF %d in "
"IPsec tunnel SA\n", __func__,
- ((struct sockaddr *)&isr->saidx.dst)->sa_family));
+ sp->req[i]->saidx.dst.sa.sa_family));
break;
}
}
- size += clen;
}
-
return (size);
}
-/*
- * This function is called from ipsec_hdrsiz_tcp(), ip_ipsec_mtu(),
- * disabled ip6_ipsec_mtu() and ip6_forward().
+/*
+ * Compute ESP/AH header size for protocols with PCB, including
+ * outer IP header. Currently only tcp_output() uses it.
*/
size_t
-ipsec_hdrsiz(const struct mbuf *m, u_int dir, struct inpcb *inp)
+ipsec_hdrsiz_inpcb(struct inpcb *inp)
{
+ struct secpolicyindex spidx;
struct secpolicy *sp;
- int error;
- size_t size;
-
- if (!key_havesp(dir))
- return 0;
-
- IPSEC_ASSERT(m != NULL, ("null mbuf"));
-
- /* Get SP for this packet. */
- if (inp == NULL)
- sp = ipsec_getpolicybyaddr(m, dir, &error);
- else
- sp = ipsec_getpolicybysock(m, dir, inp, &error);
-
- if (sp != NULL) {
- size = ipsec_hdrsiz_internal(sp);
- KEYDEBUG(KEYDEBUG_IPSEC_DATA,
- printf("%s: size:%lu.\n", __func__,
- (unsigned long)size));
+ size_t sz;
- KEY_FREESP(&sp);
- } else {
- size = 0; /* XXX Should be panic?
- * -> No, we are called w/o knowing if
- * IPsec processing is needed. */
+ sp = ipsec_getpcbpolicy(inp, IPSEC_DIR_OUTBOUND);
+ if (sp == NULL && key_havesp(IPSEC_DIR_OUTBOUND)) {
+ ipsec_setspidx_inpcb(inp, &spidx, IPSEC_DIR_OUTBOUND);
+ sp = key_allocsp(&spidx, IPSEC_DIR_OUTBOUND);
}
- return (size);
+ if (sp == NULL)
+ sp = key_allocsp_default();
+ sz = ipsec_hdrsiz_internal(sp);
+ key_freesp(&sp);
+ return (sz);
}
/*
@@ -1487,42 +1177,39 @@ ipsec_hdrsiz(const struct mbuf *m, u_int dir, struct inpcb *inp)
#define IPSEC_BITMAP_LOC_MASK (IPSEC_REDUNDANT_BITS - 1)
int
-ipsec_chkreplay(u_int32_t seq, struct secasvar *sav)
+ipsec_chkreplay(uint32_t seq, struct secasvar *sav)
{
const struct secreplay *replay;
- u_int32_t wsizeb; /* Constant: window size. */
- int ret, index, bit_location;
+ uint32_t wsizeb; /* Constant: window size. */
+ int index, bit_location;
IPSEC_ASSERT(sav != NULL, ("Null SA"));
IPSEC_ASSERT(sav->replay != NULL, ("Null replay state"));
- SECASVAR_LOCK(sav);
-
- ret = 0;
replay = sav->replay;
/* No need to check replay if disabled. */
if (replay->wsize == 0)
- goto allowed;
+ return (1);
/* Constant. */
wsizeb = replay->wsize << 3;
/* Sequence number of 0 is invalid. */
if (seq == 0)
- goto end;
+ return (0);
/* First time is always okay. */
if (replay->count == 0)
- goto allowed;
+ return (1);
/* Larger sequences are okay. */
if (seq > replay->lastseq)
- goto allowed;
+ return (1);
/* Over range to check, i.e. too old or wrapped. */
if (replay->lastseq - seq >= wsizeb)
- goto end;
+ return (0);
/* The sequence is inside the sliding window
* now check the bit in the bitmap
@@ -1534,14 +1221,8 @@ ipsec_chkreplay(u_int32_t seq, struct secasvar *sav)
/* This packet already seen? */
if ((replay->bitmap)[index] & (1 << bit_location))
- goto end;
-
-allowed:
- ret = 1;
-end:
- SECASVAR_UNLOCK(sav);
-
- return (ret);
+ return (0);
+ return (1);
}
/*
@@ -1550,19 +1231,16 @@ end:
* 1: NG
*/
int
-ipsec_updatereplay(u_int32_t seq, struct secasvar *sav)
+ipsec_updatereplay(uint32_t seq, struct secasvar *sav)
{
char buf[128];
struct secreplay *replay;
- u_int32_t wsizeb; /* Constant: window size. */
- int ret, diff, index, bit_location;
+ uint32_t wsizeb; /* Constant: window size. */
+ int diff, index, bit_location;
IPSEC_ASSERT(sav != NULL, ("Null SA"));
IPSEC_ASSERT(sav->replay != NULL, ("Null replay state"));
- SECASVAR_LOCK(sav);
-
- ret = 1;
replay = sav->replay;
if (replay->wsize == 0)
@@ -1573,7 +1251,7 @@ ipsec_updatereplay(u_int32_t seq, struct secasvar *sav)
/* Sequence number of 0 is invalid. */
if (seq == 0)
- goto end;
+ return (1);
/* The packet is too old, no need to update */
if (wsizeb + seq < replay->lastseq)
@@ -1606,7 +1284,7 @@ ipsec_updatereplay(u_int32_t seq, struct secasvar *sav)
/* this packet has already been received */
if (replay->bitmap[index] & (1 << bit_location))
- goto end;
+ return (1);
replay->bitmap[index] |= (1 << bit_location);
@@ -1617,118 +1295,99 @@ ok:
replay->overflow++;
/* Don't increment, no more packets accepted. */
- if ((sav->flags & SADB_X_EXT_CYCSEQ) == 0)
- goto end;
+ if ((sav->flags & SADB_X_EXT_CYCSEQ) == 0) {
+ if (sav->sah->saidx.proto == IPPROTO_AH)
+ AHSTAT_INC(ahs_wrap);
+ else if (sav->sah->saidx.proto == IPPROTO_ESP)
+ ESPSTAT_INC(esps_wrap);
+ return (1);
+ }
ipseclog((LOG_WARNING, "%s: replay counter made %d cycle. %s\n",
__func__, replay->overflow,
- ipsec_logsastr(sav, buf, sizeof(buf))));
+ ipsec_sa2str(sav, buf, sizeof(buf))));
}
-
- ret = 0;
-
-end:
- SECASVAR_UNLOCK(sav);
- return (ret);
+ return (0);
}
-/* Return a printable string for the address. */
-char*
-ipsec_address(union sockaddr_union* sa, char *buf, socklen_t size)
+int
+ipsec_updateid(struct secasvar *sav, uint64_t *new, uint64_t *old)
{
+ uint64_t tmp;
- switch (sa->sa.sa_family) {
-#ifdef INET
- case AF_INET:
- return (inet_ntop(AF_INET, &sa->sin.sin_addr, buf, size));
-#endif /* INET */
-#ifdef INET6
- case AF_INET6:
- return (inet_ntop(AF_INET6, &sa->sin6.sin6_addr, buf, size));
-#endif /* INET6 */
- default:
- return ("(unknown address family)");
+ /*
+ * tdb_cryptoid is initialized by xform_init().
+ * Then it can be changed only when some crypto error occurred or
+ * when SA is deleted. We stored used cryptoid in the xform_data
+ * structure. In case when crypto error occurred and crypto
+ * subsystem has reinited the session, it returns new cryptoid
+ * and EAGAIN error code.
+ *
+ * This function will be called when we got EAGAIN from crypto
+ * subsystem.
+ * *new is cryptoid that was returned by crypto subsystem in
+ * the crp_sid.
+ * *old is the original cryptoid that we stored in xform_data.
+ *
+ * For first failed request *old == sav->tdb_cryptoid, then
+ * we update sav->tdb_cryptoid and redo crypto_dispatch().
+ * For next failed request *old != sav->tdb_cryptoid, then
+ * we store cryptoid from first request into the *new variable
+ * and crp_sid from this second session will be returned via
+ * *old pointer, so caller can release second session.
+ *
+ * XXXAE: check this more carefully.
+ */
+ KEYDBG(IPSEC_STAMP,
+ printf("%s: SA(%p) moves cryptoid %jd -> %jd\n",
+ __func__, sav, (uintmax_t)(*old), (uintmax_t)(*new)));
+ KEYDBG(IPSEC_DATA, kdebug_secasv(sav));
+ SECASVAR_LOCK(sav);
+ if (sav->tdb_cryptoid != *old) {
+ /* cryptoid was already updated */
+ tmp = *new;
+ *new = sav->tdb_cryptoid;
+ *old = tmp;
+ SECASVAR_UNLOCK(sav);
+ return (1);
}
+ sav->tdb_cryptoid = *new;
+ SECASVAR_UNLOCK(sav);
+ return (0);
}
-char *
-ipsec_logsastr(struct secasvar *sav, char *buf, size_t size)
-{
- char sbuf[INET6_ADDRSTRLEN], dbuf[INET6_ADDRSTRLEN];
-
- IPSEC_ASSERT(sav->sah->saidx.src.sa.sa_family ==
- sav->sah->saidx.dst.sa.sa_family, ("address family mismatch"));
-
- snprintf(buf, size, "SA(SPI=%08lx src=%s dst=%s)",
- (u_long)ntohl(sav->spi),
- ipsec_address(&sav->sah->saidx.src, sbuf, sizeof(sbuf)),
- ipsec_address(&sav->sah->saidx.dst, dbuf, sizeof(dbuf)));
- return (buf);
-}
-
-void
-ipsec_dumpmbuf(const struct mbuf *m)
+int
+ipsec_initialized(void)
{
- const u_char *p;
- int totlen;
- int i;
- totlen = 0;
- printf("---\n");
- while (m) {
- p = mtod(m, const u_char *);
- for (i = 0; i < m->m_len; i++) {
- printf("%02x ", p[i]);
- totlen++;
- if (totlen % 16 == 0)
- printf("\n");
- }
- m = m->m_next;
- }
- if (totlen % 16 != 0)
- printf("\n");
- printf("---\n");
+ return (V_def_policy != NULL);
}
static void
def_policy_init(const void *unused __unused)
{
- bzero(&V_def_policy, sizeof(struct secpolicy));
- V_def_policy.policy = IPSEC_POLICY_NONE;
- V_def_policy.refcnt = 1;
+ V_def_policy = key_newsp();
+ if (V_def_policy != NULL) {
+ V_def_policy->policy = IPSEC_POLICY_NONE;
+ /* Force INPCB SP cache invalidation */
+ key_bumpspgen();
+ } else
+ printf("%s: failed to initialize default policy\n", __func__);
}
-VNET_SYSINIT(def_policy_init, SI_SUB_PROTO_DOMAIN, SI_ORDER_FIRST,
- def_policy_init, NULL);
-
-/* XXX This stuff doesn't belong here... */
-static struct xformsw* xforms = NULL;
-
-/*
- * Register a transform; typically at system startup.
- */
-void
-xform_register(struct xformsw* xsp)
+static void
+def_policy_uninit(const void *unused __unused)
{
- xsp->xf_next = xforms;
- xforms = xsp;
+ if (V_def_policy != NULL) {
+ key_freesp(&V_def_policy);
+ key_bumpspgen();
+ }
}
-/*
- * Initialize transform support in an sav.
- */
-int
-xform_init(struct secasvar *sav, int xftype)
-{
- struct xformsw *xsp;
-
- if (sav->tdb_xform != NULL) /* Previously initialized. */
- return (0);
- for (xsp = xforms; xsp; xsp = xsp->xf_next)
- if (xsp->xf_type == xftype)
- return ((*xsp->xf_init)(sav, xsp));
- return (EINVAL);
-}
+VNET_SYSINIT(def_policy_init, SI_SUB_PROTO_DOMAIN, SI_ORDER_FIRST,
+ def_policy_init, NULL);
+VNET_SYSUNINIT(def_policy_uninit, SI_SUB_PROTO_DOMAIN, SI_ORDER_FIRST,
+ def_policy_uninit, NULL);