summaryrefslogtreecommitdiff
path: root/sys/netinet
diff options
context:
space:
mode:
Diffstat (limited to 'sys/netinet')
-rw-r--r--sys/netinet/ip_ah.c1356
-rw-r--r--sys/netinet/ip_ah.h23
-rw-r--r--sys/netinet/ip_esp.c975
-rw-r--r--sys/netinet/ip_esp.h15
-rw-r--r--sys/netinet/ip_ipsp.c432
-rw-r--r--sys/netinet/ip_ipsp.h117
-rw-r--r--sys/netinet/ip_output.c130
-rw-r--r--sys/netinet/ip_xform.c341
-rw-r--r--sys/netinet/ipsec_input.c319
9 files changed, 1635 insertions, 2073 deletions
diff --git a/sys/netinet/ip_ah.c b/sys/netinet/ip_ah.c
index b4a412b7a07..ed7b57df6f7 100644
--- a/sys/netinet/ip_ah.c
+++ b/sys/netinet/ip_ah.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: ip_ah.c,v 1.34 2000/02/07 06:09:09 itojun Exp $ */
+/* $OpenBSD: ip_ah.c,v 1.35 2000/03/17 10:25:22 angelos Exp $ */
/*
* The authors of this code are John Ioannidis (ji@tla.org),
@@ -68,6 +68,12 @@
#include <net/pfkeyv2.h>
#include <net/if_enc.h>
+#include <sys/md5k.h>
+#include <crypto/sha1.h>
+#include <crypto/rmd160.h>
+#include <crypto/crypto.h>
+#include <crypto/xform.h>
+
#include "bpfilter.h"
#ifdef ENCDEBUG
@@ -80,20 +86,6 @@
#define offsetof(s, e) ((int)&((s *)0)->e)
#endif
-extern struct auth_hash auth_hash_hmac_md5_96;
-extern struct auth_hash auth_hash_hmac_sha1_96;
-extern struct auth_hash auth_hash_hmac_ripemd_160_96;
-extern struct auth_hash auth_hash_key_md5;
-extern struct auth_hash auth_hash_key_sha1;
-
-struct auth_hash *ah_hash[] = {
- &auth_hash_hmac_md5_96,
- &auth_hash_hmac_sha1_96,
- &auth_hash_hmac_ripemd_160_96,
- &auth_hash_key_md5,
- &auth_hash_key_sha1,
-};
-
/*
* ah_attach() is called from the transformation initialization code
*/
@@ -110,19 +102,35 @@ int
ah_init(struct tdb *tdbp, struct xformsw *xsp, struct ipsecinit *ii)
{
struct auth_hash *thash = NULL;
- int i;
+ struct cryptoini cria;
- for (i = sizeof(ah_hash) / sizeof(ah_hash[0]) - 1; i >= 0; i--)
- if (ii->ii_authalg == ah_hash[i]->type)
- break;
-
- if (i < 0)
+ /* Authentication operation */
+ switch (ii->ii_authalg)
{
- DPRINTF(("ah_init(): unsupported authentication algorithm %d specified\n", ii->ii_authalg));
- return EINVAL;
- }
+ case SADB_AALG_MD5HMAC96:
+ thash = &auth_hash_hmac_md5_96;
+ break;
+
+ case SADB_AALG_SHA1HMAC96:
+ thash = &auth_hash_hmac_sha1_96;
+ break;
- thash = ah_hash[i];
+ case SADB_X_AALG_RIPEMD160HMAC96:
+ thash = &auth_hash_hmac_ripemd_160_96;
+ break;
+
+ case SADB_X_AALG_MD5:
+ thash = &auth_hash_key_md5;
+ break;
+
+ case SADB_X_AALG_SHA1:
+ thash = &auth_hash_key_sha1;
+ break;
+
+ default:
+ DPRINTF(("ah_init(): unsupported authentication algorithm %d specified\n", ii->ii_authalg));
+ return EINVAL;
+ }
if ((ii->ii_authkeylen != thash->keysize) && (thash->keysize != 0))
{
@@ -144,48 +152,23 @@ ah_init(struct tdb *tdbp, struct xformsw *xsp, struct ipsecinit *ii)
bcopy(ii->ii_authkey, tdbp->tdb_amxkey, tdbp->tdb_amxkeylen);
- /* "Old" AH */
- if ((thash->type == SADB_X_AALG_MD5) || (thash->type == SADB_X_AALG_SHA1))
- {
- MALLOC(tdbp->tdb_ictx, u_int8_t *, thash->ctxsize, M_XDATA, M_WAITOK);
- bzero(tdbp->tdb_ictx, thash->ctxsize);
- thash->Init(tdbp->tdb_ictx);
- thash->Update(tdbp->tdb_ictx, tdbp->tdb_amxkey, tdbp->tdb_amxkeylen);
- thash->Final(NULL, tdbp->tdb_ictx);
- }
- else /* HMAC */
- {
- /* Precompute the I and O pads of the HMAC */
- for (i = 0; i < ii->ii_authkeylen; i++)
- ii->ii_authkey[i] ^= HMAC_IPAD_VAL;
-
- MALLOC(tdbp->tdb_ictx, u_int8_t *, thash->ctxsize, M_XDATA, M_WAITOK);
- bzero(tdbp->tdb_ictx, thash->ctxsize);
- thash->Init(tdbp->tdb_ictx);
- thash->Update(tdbp->tdb_ictx, ii->ii_authkey, ii->ii_authkeylen);
- thash->Update(tdbp->tdb_ictx, hmac_ipad_buffer,
- HMAC_BLOCK_LEN - ii->ii_authkeylen);
-
- for (i = 0; i < ii->ii_authkeylen; i++)
- ii->ii_authkey[i] ^= (HMAC_IPAD_VAL ^ HMAC_OPAD_VAL);
-
- MALLOC(tdbp->tdb_octx, u_int8_t *, thash->ctxsize, M_XDATA, M_WAITOK);
- bzero(tdbp->tdb_octx, thash->ctxsize);
- thash->Init(tdbp->tdb_octx);
- thash->Update(tdbp->tdb_octx, ii->ii_authkey, ii->ii_authkeylen);
- thash->Update(tdbp->tdb_octx, hmac_opad_buffer,
- HMAC_BLOCK_LEN - ii->ii_authkeylen);
- }
-
- bzero(ipseczeroes, IPSEC_ZEROES_SIZE); /* paranoid */
+ /* Initialize crypto session */
+ bzero(&cria, sizeof(cria));
+ cria.cri_alg = tdbp->tdb_authalgxform->type;
+ cria.cri_klen = ii->ii_authkeylen * 8;
+ cria.cri_key = ii->ii_authkey;
- return 0;
+ return crypto_newsession(&tdbp->tdb_cryptoid, &cria);
}
-/* Free memory */
+/*
+ * Paranoia.
+ */
int
ah_zeroize(struct tdb *tdbp)
{
+ int err;
+
if (tdbp->tdb_amxkey)
{
bzero(tdbp->tdb_amxkey, tdbp->tdb_amxkeylen);
@@ -193,130 +176,32 @@ ah_zeroize(struct tdb *tdbp)
tdbp->tdb_amxkey = NULL;
}
- if (tdbp->tdb_ictx)
- {
- if (tdbp->tdb_authalgxform)
- bzero(tdbp->tdb_ictx, tdbp->tdb_authalgxform->ctxsize);
-
- FREE(tdbp->tdb_ictx, M_XDATA);
- tdbp->tdb_ictx = NULL;
- }
-
- if (tdbp->tdb_octx)
- {
- if (tdbp->tdb_authalgxform)
- bzero(tdbp->tdb_octx, tdbp->tdb_authalgxform->ctxsize);
-
- FREE(tdbp->tdb_octx, M_XDATA);
- tdbp->tdb_octx = NULL;
- }
-
- return 0;
+ err = crypto_freesession(tdbp->tdb_cryptoid);
+ tdbp->tdb_cryptoid = 0;
+ return err;
}
/*
- * ah_input() gets called to verify that an input packet
- * passes authentication
+ * Massage IPv4/IPv6 headers for AH processing.
*/
-
-struct mbuf *
-ah_input(struct mbuf *m, struct tdb *tdb, int skip, int protoff)
+int
+ah_massage_headers(struct mbuf **m0, int proto, int skip, int alg, int out)
{
- struct auth_hash *ahx = (struct auth_hash *) tdb->tdb_authalgxform;
- unsigned char calcauth[AH_MAX_HASHLEN], savauth[AH_MAX_HASHLEN];
- int len, count, off, roff, rplen;
- struct mbuf *m0, *m1;
+ struct mbuf *m = *m0;
unsigned char *ptr;
- union authctx ctx;
- struct ah ah;
-
+ int off, count;
+
#ifdef INET
- struct ip ipo;
+ struct ip *ip;
#endif /* INET */
#ifdef INET6
struct ip6_ext *ip6e;
struct ip6_hdr ip6;
- int last;
+ int alloc, len, ad;
#endif /* INET6 */
- if (!(tdb->tdb_flags & TDBF_NOREPLAY))
- rplen = sizeof(u_int32_t);
- else
- rplen = 0;
-
- /* Save the AH header, we use it throughout */
- m_copydata(m, skip, AH_FLENGTH + rplen, (unsigned char *) &ah);
-
- /* Save the Authenticator too */
- m_copydata(m, skip + AH_FLENGTH + rplen, ahx->authsize, savauth);
-
- /* Replay window checking, if applicable */
- if ((tdb->tdb_wnd > 0) && (!(tdb->tdb_flags & TDBF_NOREPLAY)))
- {
- switch (checkreplaywindow32(ntohl(ah.ah_rpl), 0, &(tdb->tdb_rpl),
- tdb->tdb_wnd, &(tdb->tdb_bitmap)))
- {
- case 0: /* All's well */
- break;
-
- case 1:
- DPRINTF(("ah_input(): replay counter wrapped for SA %s/%08x\n", ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi)));
- ahstat.ahs_wrap++;
- m_freem(m);
- return NULL;
-
- case 2:
- case 3:
- DPRINTF(("ah_input(): duplicate packet received in SA %s/%08x\n", ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi)));
- ahstat.ahs_replay++;
- m_freem(m);
- return NULL;
-
- default:
- DPRINTF(("ah_input(): bogus value from checkreplaywindow32() in SA %s/%08x\n", ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi)));
- ahstat.ahs_replay++;
- m_freem(m);
- return NULL;
- }
- }
-
- /* Verify AH header length */
- if (ah.ah_hl * sizeof(u_int32_t) != ahx->authsize + rplen)
- {
- DPRINTF(("ah_input(): bad authenticator length %d for packet in SA %s/%08x\n", ah.ah_hl * sizeof(u_int32_t), ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi)));
- ahstat.ahs_badauthl++;
- m_freem(m);
- return NULL;
- }
-
- /* Update the counters */
- tdb->tdb_cur_bytes += (m->m_pkthdr.len - skip -
- ah.ah_hl * sizeof(u_int32_t));
- ahstat.ahs_ibytes += (m->m_pkthdr.len - skip -
- ah.ah_hl * sizeof(u_int32_t));
-
- /* Hard expiration */
- if ((tdb->tdb_flags & TDBF_BYTES) &&
- (tdb->tdb_cur_bytes >= tdb->tdb_exp_bytes))
- {
- pfkeyv2_expire(tdb, SADB_EXT_LIFETIME_HARD);
- tdb_delete(tdb, 0, TDBEXP_TIMEOUT);
- m_freem(m);
- return NULL;
- }
-
- /* Notify on expiration */
- if ((tdb->tdb_flags & TDBF_SOFT_BYTES) &&
- (tdb->tdb_cur_bytes >= tdb->tdb_soft_bytes))
- {
- pfkeyv2_expire(tdb, SADB_EXT_LIFETIME_SOFT);
- tdb->tdb_flags &= ~TDBF_SOFT_BYTES; /* Turn off checking */
- }
-
- bcopy(tdb->tdb_ictx, &ctx, ahx->ctxsize);
-
- switch (tdb->tdb_dst.sa.sa_family)
+ switch (proto)
{
#ifdef INET
case AF_INET:
@@ -325,35 +210,37 @@ ah_input(struct mbuf *m, struct tdb *tdb, int skip, int protoff)
* and option processing -- just make sure they're in
* contiguous memory.
*/
- m = m_pullup(m, skip);
+ *m0 = m = m_pullup(m, skip);
if (m == NULL)
{
- DPRINTF(("ah_input(): m_pullup() failed, SA %s/%08x\n",
- ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi)));
+ DPRINTF(("ah_massage_headers(): m_pullup() failed\n"));
ahstat.ahs_hdrops++;
- return NULL;
+ return ENOBUFS;
}
- ptr = mtod(m, unsigned char *) + sizeof(struct ip);
+ /* Fix the IP header */
+ ip = mtod(m, struct ip *);
+ ip->ip_tos = 0;
+ ip->ip_ttl = 0;
+ ip->ip_sum = 0;
- bcopy(mtod(m, unsigned char *), (unsigned char *) &ipo,
- sizeof(struct ip));
-
- ipo.ip_tos = 0;
- ipo.ip_len += skip; /* adjusted in ip_intr() */
- HTONS(ipo.ip_len);
- HTONS(ipo.ip_id);
+ /*
+ * On input, fix ip_len and ip_id, which have been byte-swapped
+ * at ip_intr()
+ */
+ if (!out)
+ {
+ ip->ip_len += skip;
+ HTONS(ip->ip_len);
+ HTONS(ip->ip_id);
+ }
- if ((ahx->type == SADB_X_AALG_MD5) ||
- (ahx->type == SADB_X_AALG_SHA1))
- ipo.ip_off = htons(ipo.ip_off & IP_DF);
+ if ((alg == CRYPTO_MD5_KPDK) || (alg == CRYPTO_SHA1_KPDK))
+ ip->ip_off = htons(ip->ip_off & IP_DF);
else
- ipo.ip_off = 0;
- ipo.ip_ttl = 0;
- ipo.ip_sum = 0;
+ ip->ip_off = 0;
- /* Include IP header in authenticator computation */
- ahx->Update(&ctx, (unsigned char *) &ipo, sizeof(struct ip));
+ ptr = mtod(m, unsigned char *) + sizeof(struct ip);
/* IPv4 option processing */
for (off = sizeof(struct ip); off < skip;)
@@ -361,12 +248,10 @@ ah_input(struct mbuf *m, struct tdb *tdb, int skip, int protoff)
switch (ptr[off])
{
case IPOPT_EOL:
- ahx->Update(&ctx, ptr + off, 1);
off = skip; /* End the loop */
break;
case IPOPT_NOP:
- ahx->Update(&ctx, ptr + off, 1);
off++;
break;
@@ -375,32 +260,57 @@ ah_input(struct mbuf *m, struct tdb *tdb, int skip, int protoff)
case 0x86: /* Commercial security */
case 0x94: /* Router alert */
case 0x95: /* RFC1770 */
- ahx->Update(&ctx, ptr + off, ptr[off + 1]);
/* Sanity check for zero-length options */
if (ptr[off + 1] == 0)
{
- DPRINTF(("ah_input(): illegal zero-length IPv4 option %d in SA %s/%08x\n", ptr[off], ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi)));
+ DPRINTF(("ah_massage_headers(): illegal zero-length IPv4 option %d\n", ptr[off]));
ahstat.ahs_hdrops++;
m_freem(m);
- return NULL;
+ return EINVAL;
}
off += ptr[off + 1];
break;
+ case IPOPT_LSRR:
+ case IPOPT_SSRR:
+ /*
+ * On output, if we have either of the source routing
+ * options, we should swap the destination address of
+ * the IP header with the last address specified in
+ * the option, as that is what the destination's
+ * IP header will look like.
+ */
+ if (out)
+ bcopy(ptr + off + ptr[off + 1] -
+ sizeof(struct in_addr),
+ &(ip->ip_dst), sizeof(struct in_addr));
+
+ /* Fall through */
default:
- ahx->Update(&ctx, ipseczeroes, ptr[off + 1]);
- off += ptr[off + 1];
+ /* Sanity check for zero-length options */
+ if (ptr[off + 1] == 0)
+ {
+ DPRINTF(("ah_massage_headers(): illegal zero-length IPv4 option %d\n", ptr[off]));
+ ahstat.ahs_hdrops++;
+ m_freem(m);
+ return EINVAL;
+ }
+
+ /* Zeroize all other options */
+ count = ptr[off + 1];
+ bcopy(ipseczeroes, ptr, count);
+ off += count;
break;
}
/* Sanity check */
if (off > skip)
{
- DPRINTF(("ah_input(): malformed IPv4 options header in SA %s/%08x\n", ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi)));
+ DPRINTF(("ah_massage_headers(): malformed IPv4 options header\n"));
ahstat.ahs_hdrops++;
m_freem(m);
- return NULL;
+ return EINVAL;
}
}
@@ -409,16 +319,16 @@ ah_input(struct mbuf *m, struct tdb *tdb, int skip, int protoff)
#ifdef INET6
case AF_INET6: /* Ugly... */
- /* Copy and "cook" (later on) the IPv6 header */
- m_copydata(m, 0, sizeof(ip6), (unsigned char *) &ip6);
+ /* Copy and "cook" the IPv6 header */
+ m_copydata(m, 0, sizeof(ip6), (caddr_t) &ip6);
/* We don't do IPv6 Jumbograms */
if (ip6.ip6_plen == 0)
{
- DPRINTF(("ah_input(): unsupported IPv6 jumbogram in SA %s/%08x\n", ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi)));
+ DPRINTF(("ah_massage_headers(): unsupported IPv6 jumbogram"));
ahstat.ahs_hdrops++;
m_freem(m);
- return NULL;
+ return EMSGSIZE;
}
ip6.ip6_flow = 0;
@@ -426,23 +336,35 @@ ah_input(struct mbuf *m, struct tdb *tdb, int skip, int protoff)
ip6.ip6_vfc &= ~IPV6_VERSION_MASK;
ip6.ip6_vfc |= IPV6_VERSION;
- /* Include IPv6 header in authenticator computation */
- ahx->Update(&ctx, (unsigned char *) &ip6, sizeof(ip6));
-
+ /* Done with IPv6 header */
+ m_copyback(m, 0, sizeof(struct ip6_hdr), (caddr_t) &ip6);
+
/* Let's deal with the remaining headers (if any) */
if (skip - sizeof(struct ip6_hdr) > 0)
{
if (m->m_len <= skip)
{
MALLOC(ptr, unsigned char *, skip - sizeof(struct ip6_hdr),
- M_XDATA, M_WAITOK);
+ M_XDATA, M_DONTWAIT);
+ if (ptr == NULL)
+ {
+ DPRINTF(("ah_massage_headers(): failed to allocate memory for IPv6 headers\n"));
+ ahstat.ahs_hdrops++;
+ m_freem(m);
+ return ENOBUFS;
+ }
/* Copy all the protocol headers after the IPv6 header */
m_copydata(m, sizeof(struct ip6_hdr),
skip - sizeof(struct ip6_hdr), ptr);
+ alloc = 1;
}
else
- ptr = mtod(m, unsigned char *) + sizeof(struct ip6_hdr);
+ {
+ /* No need to allocate memory */
+ ptr = mtod(m, unsigned char *) + sizeof(struct ip6_hdr);
+ alloc = 0;
+ }
}
else
break;
@@ -460,224 +382,374 @@ ah_input(struct mbuf *m, struct tdb *tdb, int skip, int protoff)
* Process the mutable/immutable options -- borrows
* heavily from the KAME code.
*/
- for (last = len, count = len + sizeof(struct ip6_ext);
+ for (count = len + sizeof(struct ip6_ext);
count < len + ((ip6e->ip6e_len + 1) << 3);)
{
if (ptr[count] == IP6OPT_PAD1)
{
count++;
- continue;
+ continue; /* Skip padding */
}
/* Sanity check */
- if (count + sizeof(struct ip6_ext) > len +
- ((ip6e->ip6e_len + 1) << 3))
+ if (count > len + ((ip6e->ip6e_len + 1) << 3))
{
- DPRINTF(("ah_input(): malformed IPv6 options header in SA %s/%08x\n", ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi)));
+ DPRINTF(("ah_massage_headers(): malformed IPv6 options header\n"));
ahstat.ahs_hdrops++;
m_freem(m);
/* Free, if we allocated */
- if (m->m_len < skip)
- {
- FREE(ptr, M_XDATA);
- ptr = NULL;
- }
- return NULL;
+ if (alloc)
+ FREE(ptr, M_XDATA);
+
+ return EINVAL;
}
- /*
- * If mutable option, calculate authenticator
- * for all immutable fields so far, then include
- * a zeroed-out version of this option.
- */
+ ad = ptr[count + 1];
+
+ /* If mutable option, zeroize */
if (ptr[count] & IP6OPT_MUTABLE)
- {
- /* Calculate immutables */
- ahx->Update(&ctx, ptr + last,
- count + sizeof(struct ip6_ext) -
- last);
- last = count + ptr[count + 1] +
- sizeof(struct ip6_ext);
-
- /* Calculate "zeroed-out" immutables */
- ahx->Update(&ctx, ipseczeroes, ptr[count + 1] -
- sizeof(struct ip6_ext));
- }
-
- count += ptr[count + 1] + sizeof(struct ip6_ext);
+ bcopy(ipseczeroes, ptr + count, ptr[count + 1]);
+
+ count += ad;
/* Sanity check */
if (count > skip - sizeof(struct ip6_hdr))
{
- DPRINTF(("ah_input(): malformed IPv6 options header in SA %s/%08x\n", ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi)));
+ DPRINTF(("ah_massage_headers(): malformed IPv6 options header\n"));
ahstat.ahs_hdrops++;
m_freem(m);
/* Free, if we allocated */
- if (m->m_len < skip)
- {
- FREE(ptr, M_XDATA);
- ptr = NULL;
- }
- return NULL;
+ if (alloc)
+ FREE(ptr, M_XDATA);
+
+ return EINVAL;
}
}
- /* Include any trailing immutable options */
- ahx->Update(&ctx, ptr + last,
- len + ((ip6e->ip6e_len + 1) << 3) - last);
-
len += ((ip6e->ip6e_len + 1) << 3); /* Advance */
off = ip6e->ip6e_nxt;
break;
case IPPROTO_ROUTING:
+ /* Always include routing headers in computation */
ip6e = (struct ip6_ext *) (ptr + len);
- ahx->Update(&ctx, ptr + len, (ip6e->ip6e_len + 1) << 3);
len += ((ip6e->ip6e_len + 1) << 3); /* Advance */
off = ip6e->ip6e_nxt;
break;
default:
- DPRINTF(("ah_input(): unexpected IPv6 header type %d in SA %s/%08x\n", off, ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi)));
- len = skip - sizeof(struct ip6_hdr);
- break;
+ DPRINTF(("ah_massage_headers(): unexpected IPv6 header type %d\n", off));
+ if (alloc)
+ FREE(ptr, M_XDATA);
+ ahstat.ahs_hdrops++;
+ m_freem(m);
+ return EINVAL;
}
- /* Free, if we allocated */
- if (m->m_len < skip)
+ /* Copyback and free, if we allocated */
+ if (alloc)
{
+ m_copyback(m, sizeof(struct ip6_hdr),
+ skip - sizeof(struct ip6_hdr), ptr);
FREE(ptr, M_XDATA);
- ptr = NULL;
}
break;
#endif /* INET6 */
+ }
- default:
- DPRINTF(("ah_input(): unsupported protocol family %d in SA %s/%08x\n", ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi)));
- ahstat.ahs_hdrops++;
- m_freem(m);
- return NULL;
+ return 0;
+}
+
+/*
+ * ah_input() gets called to verify that an input packet
+ * passes authentication.
+ */
+int
+ah_input(struct mbuf *m, struct tdb *tdb, int skip, int protoff)
+{
+ struct auth_hash *ahx = (struct auth_hash *) tdb->tdb_authalgxform;
+ u_int32_t btsx;
+ u_int8_t hl;
+ int rplen;
+
+ struct cryptodesc *crda = NULL;
+ struct cryptop *crp;
+
+ if (!(tdb->tdb_flags & TDBF_NOREPLAY))
+ rplen = AH_FLENGTH + sizeof(u_int32_t);
+ else
+ rplen = AH_FLENGTH;
+
+ /* Save the AH header, we use it throughout */
+ m_copydata(m, skip + offsetof(struct ah, ah_hl), sizeof(u_int8_t),
+ (caddr_t) &hl);
+
+ /* Replay window checking, if applicable */
+ if ((tdb->tdb_wnd > 0) && (!(tdb->tdb_flags & TDBF_NOREPLAY)))
+ {
+ m_copydata(m, skip + offsetof(struct ah, ah_rpl), sizeof(u_int32_t),
+ (caddr_t) &btsx);
+ btsx = ntohl(btsx);
+
+ switch (checkreplaywindow32(btsx, 0, &(tdb->tdb_rpl),
+ tdb->tdb_wnd, &(tdb->tdb_bitmap)))
+ {
+ case 0: /* All's well */
+ break;
+
+ case 1:
+ DPRINTF(("ah_input(): replay counter wrapped for SA %s/%08x\n", ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi)));
+ ahstat.ahs_wrap++;
+ m_freem(m);
+ return ENOBUFS;
+
+ case 2:
+ case 3:
+ DPRINTF(("ah_input(): duplicate packet received in SA %s/%08x\n", ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi)));
+ ahstat.ahs_replay++;
+ m_freem(m);
+ return ENOBUFS;
+
+ default:
+ DPRINTF(("ah_input(): bogus value from checkreplaywindow32() in SA %s/%08x\n", ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi)));
+ ahstat.ahs_replay++;
+ m_freem(m);
+ return ENOBUFS;
+ }
}
- /* Record the beginning of the AH header */
- for (len = 0, m1 = m; m1 && (len + m1->m_len <= skip); m1 = m1->m_next)
- len += m1->m_len;
+ /* Verify AH header length */
+ if (hl * sizeof(u_int32_t) != ahx->authsize + rplen - AH_FLENGTH)
+ {
+ DPRINTF(("ah_input(): bad authenticator length %d for packet in SA %s/%08x\n", hl * sizeof(u_int32_t), ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi)));
+ ahstat.ahs_badauthl++;
+ m_freem(m);
+ return EACCES;
+ }
- if (m1 == NULL)
+ /* Update the counters */
+ tdb->tdb_cur_bytes += (m->m_pkthdr.len - skip - hl * sizeof(u_int32_t));
+ ahstat.ahs_ibytes += (m->m_pkthdr.len - skip - hl * sizeof(u_int32_t));
+
+ /* Hard expiration */
+ if ((tdb->tdb_flags & TDBF_BYTES) &&
+ (tdb->tdb_cur_bytes >= tdb->tdb_exp_bytes))
+ {
+ pfkeyv2_expire(tdb, SADB_EXT_LIFETIME_HARD);
+ tdb_delete(tdb, 0, TDBEXP_TIMEOUT);
+ m_freem(m);
+ return ENXIO;
+ }
+
+ /* Notify on expiration */
+ if ((tdb->tdb_flags & TDBF_SOFT_BYTES) &&
+ (tdb->tdb_cur_bytes >= tdb->tdb_soft_bytes))
+ {
+ pfkeyv2_expire(tdb, SADB_EXT_LIFETIME_SOFT);
+ tdb->tdb_flags &= ~TDBF_SOFT_BYTES; /* Turn off checking */
+ }
+
+ /* Get crypto descriptors */
+ crp = crypto_getreq(1);
+ if (crp == NULL)
{
- DPRINTF(("ah_input(): bad mbuf chain for packet in SA %s/%08x\n",
- ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi)));
- ahstat.ahs_hdrops++;
m_freem(m);
- return NULL;
+ DPRINTF(("ah_input(): failed to acquire crypto descriptors\n"));
+ ahstat.ahs_crypto++;
+ return ENOBUFS;
}
- else
- roff = skip - len;
- /* Skip the AH header */
- for (len = 0, m0 = m1;
- m0 && (len + m0->m_len <= AH_FLENGTH + rplen + ahx->authsize + roff);
- m0 = m0->m_next)
- len += m0->m_len;
+ crda = crp->crp_desc;
+
+ crda->crd_skip = 0;
+ crda->crd_len = m->m_pkthdr.len;
+ crda->crd_inject = skip + rplen;
+
+ /* Authentication operation */
+ crda->crd_alg = ahx->type;
+ crda->crd_key = tdb->tdb_amxkey;
+ crda->crd_klen = tdb->tdb_amxkeylen * 8;
- if (m0 == NULL)
+ /*
+ * Save the authenticator, the skipped portion of the packet, and the
+ * AH header.
+ */
+ MALLOC(crp->crp_opaque4, caddr_t, skip + rplen + ahx->authsize,
+ M_XDATA, M_DONTWAIT);
+ if (crp->crp_opaque4 == 0)
{
- DPRINTF(("ah_input(): bad mbuf chain for packet in SA %s/%08x\n",
- ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi)));
- ahstat.ahs_hdrops++;
m_freem(m);
- return NULL;
+ crypto_freereq(crp);
+ DPRINTF(("ah_input(): failed to allocate auth array\n"));
+ ahstat.ahs_crypto++;
+ return ENOBUFS;
}
- else
- off = (AH_FLENGTH + rplen + ahx->authsize + roff) - len;
- /* Include the AH header (minus the authenticator) in the computation */
- ahx->Update(&ctx, (unsigned char *) &ah, AH_FLENGTH + rplen);
+ /* Save data */
+ m_copydata(m, 0, skip + rplen + ahx->authsize, crp->crp_opaque4);
- /* All-zeroes for the authenticator */
- ahx->Update(&ctx, ipseczeroes, ahx->authsize);
+ /* Zeroize the authenticator on the packet */
+ m_copyback(m, skip + rplen, ahx->authsize, ipseczeroes);
- /* Amount of data to be verified */
- len = m->m_pkthdr.len - skip - AH_FLENGTH - rplen - ahx->authsize;
+ /* "Massage" the packet headers for crypto processing */
+ if ((btsx = ah_massage_headers(&m, tdb->tdb_dst.sa.sa_family,
+ skip, ahx->type, 0)) != 0)
+ {
+ /* mbuf will be free'd by callee */
+ FREE(crp->crp_opaque4, M_XDATA);
- /* Loop through the mbuf chain computing the HMAC */
- while (len > 0)
+ crypto_freereq(crp);
+ return btsx;
+ }
+
+ tdb->tdb_ref++;
+
+ /* Crypto operation descriptor */
+ crp->crp_ilen = m->m_pkthdr.len; /* Total input length */
+ crp->crp_flags = CRYPTO_F_IMBUF;
+ crp->crp_buf = (caddr_t) m;
+ crp->crp_callback = (int (*) (struct cryptop *)) ah_input_cb;
+ crp->crp_sid = tdb->tdb_cryptoid;
+
+ /* These are passed as-is to the callback */
+ crp->crp_opaque1 = (caddr_t) tdb;
+ crp->crp_opaque2 = (caddr_t) skip;
+ crp->crp_opaque3 = (caddr_t) protoff;
+
+ return crypto_dispatch(crp);
+}
+
+/*
+ * AH input callback, called directly by the crypto driver.
+ */
+int
+ah_input_cb(void *op)
+{
+ int roff, rplen, error, skip, protoff;
+ unsigned char calc[AH_ALEN_MAX];
+ struct mbuf *m1, *m0, *m;
+ struct cryptodesc *crd;
+ struct auth_hash *ahx;
+ struct cryptop *crp;
+ struct tdb *tdb;
+
+ crp = (struct cryptop *) op;
+ crd = crp->crp_desc;
+ tdb = (struct tdb *) crp->crp_opaque1;
+ ahx = (struct auth_hash *) tdb->tdb_authalgxform;
+ skip = (int) crp->crp_opaque2;
+ protoff = (int) crp->crp_opaque3;
+ m = (struct mbuf *) crp->crp_buf;
+
+ tdb->tdb_ref--;
+
+ /* Check for crypto errors */
+ if (crp->crp_etype)
{
- if (m0 == NULL)
+ if (tdb->tdb_cryptoid != 0)
+ tdb->tdb_cryptoid = crp->crp_sid;
+
+ if (crp->crp_etype == EAGAIN)
{
- DPRINTF(("ah_input(): bad mbuf chain for packet in SA %s/%08x\n", ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi)));
- ahstat.ahs_hdrops++;
- m_freem(m);
- return NULL;
+ tdb->tdb_ref++;
+ return crypto_dispatch(crp);
}
- count = min(m0->m_len - off, len);
-
- ahx->Update(&ctx, mtod(m0, unsigned char *) + off, count);
-
- len -= count;
- off = 0;
- m0 = m0->m_next;
+ ahstat.ahs_noxform++;
+ DPRINTF(("ah_input_cb(): crypto error %d\n", crp->crp_etype));
+ error = crp->crp_etype;
+ goto baddone;
}
- /* Finish computation */
- if ((ahx->type == SADB_X_AALG_MD5) || (ahx->type == SADB_X_AALG_SHA1))
+ /* Shouldn't happen... */
+ if (!m)
{
- ahx->Update(&ctx, (unsigned char *) tdb->tdb_amxkey,
- tdb->tdb_amxkeylen);
- ahx->Final(calcauth, &ctx);
+ ahstat.ahs_crypto++;
+ DPRINTF(("ah_input_cb(): bogus returned buffer from crypto\n"));
+ error = EINVAL;
+ goto baddone;
}
- else
+
+ /*
+ * Check that the TDB is still valid -- not really an error, but
+ * we need to handle it as such. It may happen if the TDB expired
+ * or was deleted while there was a pending request in the crypto
+ * queue.
+ */
+ if (tdb->tdb_flags & TDBF_INVALID)
{
- /* Finish HMAC computation */
- ahx->Final(calcauth, &ctx);
- bcopy(tdb->tdb_octx, &ctx, ahx->ctxsize);
- ahx->Update(&ctx, calcauth, ahx->hashsize);
- ahx->Final(calcauth, &ctx);
+ ahstat.ahs_invalid++;
+ tdb_delete(tdb, 0, 0);
+ error = ENXIO;
+ DPRINTF(("ah_input_cb(): TDB expired while processing crypto\n"));
+ goto baddone;
}
- /* Verify */
- if (bcmp(savauth, calcauth, ahx->authsize))
+ if (!(tdb->tdb_flags & TDBF_NOREPLAY))
+ rplen = AH_FLENGTH + sizeof(u_int32_t);
+ else
+ rplen = AH_FLENGTH;
+
+ /* Copy computed authenticator */
+ m_copydata(m, skip + rplen, ahx->authsize, calc);
+
+ /* Verify authenticator */
+ if (bcmp(crp->crp_opaque4 + skip + rplen, calc, ahx->authsize))
{
DPRINTF(("ah_input(): authentication failed for packet in SA %s/%08x\n", ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi)));
ahstat.ahs_badauth++;
- m_freem(m);
- return NULL;
+ error = EACCES;
+ goto baddone;
}
/* Fix the Next Protocol field */
- m_copyback(m, protoff, sizeof(u_int8_t), (u_char *) &(ah.ah_nh));
+ ((u_int8_t *) crp->crp_opaque4)[protoff] =
+ ((u_int8_t *) crp->crp_opaque4)[skip];
- /*
- * Remove the AH header from the mbuf.
- */
+ /* Copyback the saved (uncooked) network headers */
+ m_copyback(m, 0, skip, crp->crp_opaque4);
+
+ /* No longer needed */
+ FREE(crp->crp_opaque4, M_XDATA);
+ crypto_freereq(crp);
+
+ /* Record the beginning of the AH header */
+ m1 = m_getptr(m, skip, &roff);
+ if (m1 == NULL)
+ {
+ DPRINTF(("ah_input(): bad mbuf chain for packet in SA %s/%08x\n",
+ ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi)));
+ ahstat.ahs_hdrops++;
+ m_freem(m);
+ return EINVAL;
+ }
+
+ /* Remove the AH header from the mbuf */
if (roff == 0)
{
/* The AH header was conveniently at the beginning of the mbuf */
- m_adj(m1, AH_FLENGTH + rplen + ahx->authsize);
+ m_adj(m1, rplen + ahx->authsize);
if (!(m1->m_flags & M_PKTHDR))
- m->m_pkthdr.len -= AH_FLENGTH + rplen + ahx->authsize;
+ m->m_pkthdr.len -= rplen + ahx->authsize;
}
else
- if (roff + AH_FLENGTH + rplen + ahx->authsize >= m1->m_len)
+ if (roff + rplen + ahx->authsize >= m1->m_len)
{
/*
* Part or all of the AH header is at the end of this mbuf, so first
* let's remove the remainder of the AH header from the
* beginning of the remainder of the mbuf chain, if any.
*/
- if (roff + AH_FLENGTH + rplen + ahx->authsize > m1->m_len)
+ if (roff + rplen + ahx->authsize > m1->m_len)
{
/* Adjust the next mbuf by the remainder */
- m_adj(m1->m_next, roff + AH_FLENGTH + rplen +
- ahx->authsize - m1->m_len);
+ m_adj(m1->m_next, roff + rplen + ahx->authsize - m1->m_len);
/* The second mbuf is guaranteed not to have a pkthdr... */
- m->m_pkthdr.len -= (roff + AH_FLENGTH + rplen +
- ahx->authsize - m1->m_len);
+ m->m_pkthdr.len -= (roff + rplen + ahx->authsize - m1->m_len);
}
/* Now, let's unlink the mbuf chain for a second...*/
@@ -699,38 +771,43 @@ ah_input(struct mbuf *m, struct tdb *tdb, int skip, int protoff)
* overlapping copy of the remainder of the mbuf over the ESP
* header.
*/
- bcopy(mtod(m1, u_char *) + roff + AH_FLENGTH + rplen + ahx->authsize,
+ bcopy(mtod(m1, u_char *) + roff + rplen + ahx->authsize,
mtod(m1, u_char *) + roff,
- m1->m_len - (roff + AH_FLENGTH + rplen + ahx->authsize));
- m1->m_len -= AH_FLENGTH + rplen + ahx->authsize;
- m->m_pkthdr.len -= AH_FLENGTH + rplen + ahx->authsize;
+ m1->m_len - (roff + rplen + ahx->authsize));
+ m1->m_len -= rplen + ahx->authsize;
+ m->m_pkthdr.len -= rplen + ahx->authsize;
}
- return m;
+ return ipsec_common_input_cb(m, tdb, skip, protoff);
+
+ baddone:
+ if (m)
+ m_freem(m);
+
+ /* We have to free this manually */
+ if (crp && crp->crp_opaque4)
+ FREE(crp->crp_opaque4, M_XDATA);
+
+ crypto_freereq(crp);
+
+ return error;
}
+/*
+ * AH output routine, called by ipsp_process_packet().
+ */
int
ah_output(struct mbuf *m, struct tdb *tdb, struct mbuf **mp, int skip,
- int protoff)
+ int protoff)
{
struct auth_hash *ahx = (struct auth_hash *) tdb->tdb_authalgxform;
- unsigned char calcauth[AH_MAX_HASHLEN];
- int len, off, count, rplen;
- unsigned char *ptr;
- union authctx ctx;
- struct mbuf *mo;
+ struct cryptodesc *crda;
+ struct mbuf *mo, *mi;
+ struct cryptop *crp;
+ u_int16_t iplen;
+ int len, rplen;
struct ah *ah;
-#ifdef INET
- struct ip ipo;
-#endif /* INET */
-
-#ifdef INET6
- struct ip6_ext *ip6e;
- struct ip6_hdr ip6;
- int last;
-#endif /* INET6 */
-
#if NBPFILTER > 0
{
struct ifnet *ifn;
@@ -771,35 +848,47 @@ ah_output(struct mbuf *m, struct tdb *tdb, struct mbuf **mp, int skip,
}
if (!(tdb->tdb_flags & TDBF_NOREPLAY))
- rplen = sizeof(u_int32_t);
+ rplen = AH_FLENGTH + sizeof(u_int32_t);
else
- rplen = 0;
+ rplen = AH_FLENGTH;
-#ifdef INET
- if ((tdb->tdb_dst.sa.sa_family == AF_INET) &&
- (AH_FLENGTH + rplen + ahx->authsize + m->m_pkthdr.len > IP_MAXPACKET))
+ switch (tdb->tdb_dst.sa.sa_family)
{
- DPRINTF(("ah_output(): packet in SA %s/%08x got too big\n",
- ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi)));
- m_freem(m);
- ahstat.ahs_toobig++;
- return EMSGSIZE;
- }
+#ifdef INET
+ case AF_INET:
+ /* Check for IP maximum packet size violations */
+ if (rplen + ahx->authsize + m->m_pkthdr.len > IP_MAXPACKET)
+ {
+ DPRINTF(("ah_output(): packet in SA %s/%08x got too big\n",
+ ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi)));
+ m_freem(m);
+ ahstat.ahs_toobig++;
+ return EMSGSIZE;
+ }
+ break;
#endif /* INET */
#ifdef INET6
- if ((tdb->tdb_dst.sa.sa_family == AF_INET6) &&
- (AH_FLENGTH + rplen + ahx->authsize + m->m_pkthdr.len >
- IPV6_MAXPACKET))
- {
- DPRINTF(("ah_output(): packet in SA %s/%08x got too big\n",
- ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi)));
- m_freem(m);
- ahstat.ahs_toobig++;
- return EMSGSIZE;
- }
+ case AF_INET6:
+ /* Check for IPv6 maximum packet size violations */
+ if (rplen + ahx->authsize + m->m_pkthdr.len > IPV6_MAXPACKET)
+ {
+ DPRINTF(("ah_output(): packet in SA %s/%08x got too big\n",
+ ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi)));
+ m_freem(m);
+ ahstat.ahs_toobig++;
+ return EMSGSIZE;
+ }
+ break;
#endif /* INET6 */
+ default:
+ DPRINTF(("ah_output(): unknown/unsupported protocol family %d, SA %s/%08x\n", tdb->tdb_dst.sa.sa_family, ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi)));
+ m_freem(m);
+ ahstat.ahs_nopf++;
+ return EPFNOSUPPORT;
+ }
+
/* Update the counters */
tdb->tdb_cur_bytes += m->m_pkthdr.len - skip;
ahstat.ahs_obytes += m->m_pkthdr.len - skip;
@@ -825,24 +914,21 @@ ah_output(struct mbuf *m, struct tdb *tdb, struct mbuf **mp, int skip,
/*
* Loop through mbuf chain; if we find an M_EXT mbuf with
* more than one reference, replace the rest of the chain.
- * This may not be strictly necessary for AH packets, if we were
- * careful with the rest of our processing (and made a lot of
- * assumptions about the layout of the packets/mbufs).
*/
- (*mp) = m;
- while ((*mp) != NULL &&
- (!((*mp)->m_flags & M_EXT) ||
- ((*mp)->m_ext.ext_ref == NULL &&
- mclrefcnt[mtocl((*mp)->m_ext.ext_buf)] <= 1)))
+ mi = m;
+ while (mi != NULL &&
+ (!(mi->m_flags & M_EXT) ||
+ (mi->m_ext.ext_ref == NULL &&
+ mclrefcnt[mtocl(mi->m_ext.ext_buf)] <= 1)))
{
- mo = (*mp);
- (*mp) = (*mp)->m_next;
+ mo = mi;
+ mi = mi->m_next;
}
- if ((*mp) != NULL)
+ if (mi != NULL)
{
/* Replace the rest of the mbuf chain. */
- struct mbuf *n = m_copym2((*mp), 0, M_COPYALL, M_DONTWAIT);
+ struct mbuf *n = m_copym2(mi, 0, M_COPYALL, M_DONTWAIT);
if (n == NULL)
{
@@ -856,385 +942,211 @@ ah_output(struct mbuf *m, struct tdb *tdb, struct mbuf **mp, int skip,
else
m = n;
- m_freem((*mp));
- (*mp) = NULL;
+ m_freem(mi);
}
- bcopy(tdb->tdb_ictx, (caddr_t) &ctx, ahx->ctxsize);
-
- switch (tdb->tdb_dst.sa.sa_family)
+ /* Inject AH header */
+ mi = m_inject(m, skip, rplen + ahx->authsize, M_DONTWAIT);
+ if (mi == NULL)
{
-#ifdef INET
- case AF_INET:
- /*
- * This is the most painless way of dealing with IPv4 header
- * and option processing -- just make sure they're in
- * contiguous memory.
- */
- m = m_pullup(m, skip);
- if (m == NULL)
- {
- DPRINTF(("ah_output(): m_pullup() failed, SA %s/%08x\n",
- ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi)));
- ahstat.ahs_hdrops++;
- return ENOBUFS;
- }
-
- ptr = mtod(m, unsigned char *) + sizeof(struct ip);
-
- bcopy(mtod(m, unsigned char *), (unsigned char *) &ipo,
- sizeof(struct ip));
+ DPRINTF(("ah_output(): failed to inject AH header for SA %s/%08x\n",
+ ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi)));
+ m_freem(m);
+ ahstat.ahs_wrap++;
+ return ENOBUFS;
+ }
- ipo.ip_tos = 0;
- if ((ahx->type == SADB_X_AALG_MD5) ||
- (ahx->type == SADB_X_AALG_SHA1))
- ipo.ip_off = htons(ntohs(ipo.ip_off) & IP_DF);
- else
- ipo.ip_off = 0;
- ipo.ip_ttl = 0;
- ipo.ip_sum = 0;
- ipo.ip_p = IPPROTO_AH;
- ipo.ip_len = htons(ntohs(ipo.ip_len) + AH_FLENGTH + rplen +
- ahx->authsize);
-
- /*
- * If we have a loose or strict routing option, we are
- * supposed to use the last address in it as the
- * destination address in the authenticated IPv4 header.
- *
- * Note that this is an issue only with the output routine;
- * we will correctly process (in the AH input routine) incoming
- * packets with these options without special consideration.
- *
- * We assume that the IP header contains the next hop's address,
- * and that the last entry in the option is the final
- * destination's address.
- */
- if (skip > sizeof(struct ip))
- {
- for (off = sizeof(struct ip); off < skip;)
- {
- /* First sanity check for zero-length options */
- if ((ptr[off] != IPOPT_EOL) && (ptr[off] != IPOPT_NOP) &&
- (ptr[off + 1] == 0))
- {
- DPRINTF(("ah_output(): illegal zero-length IPv4 option %d in SA %s/%08x\n", ptr[off], ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi)));
- ahstat.ahs_hdrops++;
- m_freem(m);
- return EMSGSIZE;
- }
+ /*
+ * The AH header is guaranteed by m_inject() to be in contiguous memory,
+ * at the beginning of the returned mbuf.
+ */
+ ah = mtod(mi, struct ah *);
+
+ /* Initialize the AH header */
+ m_copydata(m, protoff, sizeof(u_int8_t), (caddr_t) &ah->ah_nh);
+ ah->ah_hl = (rplen + ahx->authsize - AH_FLENGTH) / sizeof(u_int32_t);
+ ah->ah_rv = 0;
+ ah->ah_spi = tdb->tdb_spi;
- switch (ptr[off])
- {
- case IPOPT_LSRR:
- case IPOPT_SSRR:
- /* Sanity check for length */
- if (ptr[off + 1] < 2 + sizeof(struct in_addr))
- {
- DPRINTF(("ah_output(): malformed LSRR or SSRR IPv4 option header in SA %s/%08x\n", ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi)));
- ahstat.ahs_hdrops++;
- m_freem(m);
- return EMSGSIZE;
- }
-
- bcopy(ptr + off + ptr[off + 1] -
- sizeof(struct in_addr),
- &(ipo.ip_dst), sizeof(struct in_addr));
- off = skip;
- break;
-
- case IPOPT_EOL:
- off = skip;
- break;
-
- case IPOPT_NOP:
- off++;
- break;
-
- default: /* Some other option, just skip it */
- off += ptr[off + 1];
- break;
- }
+ /* Zeroize authenticator */
+ m_copyback(m, skip + rplen, ahx->authsize, ipseczeroes);
- /* Sanity check */
- if (off > skip)
- {
- DPRINTF(("ah_output(): malformed IPv4 options header in SA %s/%08x\n", ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi)));
- ahstat.ahs_hdrops++;
- m_freem(m);
- return EMSGSIZE;
- }
- }
- }
+ if (!(tdb->tdb_flags & TDBF_NOREPLAY))
+ ah->ah_rpl = htonl(tdb->tdb_rpl++);
- /* Include IP header in authenticator computation */
- ahx->Update(&ctx, (unsigned char *) &ipo, sizeof(struct ip));
+ /* Get crypto descriptors */
+ crp = crypto_getreq(1);
+ if (crp == NULL)
+ {
+ m_freem(m);
+ DPRINTF(("ah_output(): failed to acquire crypto descriptors\n"));
+ ahstat.ahs_crypto++;
+ return ENOBUFS;
+ }
- /* IPv4 option processing */
- for (off = sizeof(struct ip); off < skip;)
- {
- switch (ptr[off])
- {
- case IPOPT_EOL:
- ahx->Update(&ctx, ptr + off, 1);
- off = skip; /* End the loop */
- break;
+ crda = crp->crp_desc;
- case IPOPT_NOP:
- ahx->Update(&ctx, ptr + off, 1);
- off++;
- break;
+ crda->crd_skip = 0;
+ crda->crd_inject = skip + rplen;
+ crda->crd_len = m->m_pkthdr.len;
- case IPOPT_SECURITY: /* 0x82 */
- case 0x85: /* Extended security */
- case 0x86: /* Commercial security */
- case 0x94: /* Router alert */
- case 0x95: /* RFC1770 */
- ahx->Update(&ctx, ptr + off, ptr[off + 1]);
- off += ptr[off + 1];
- break;
+ /* Authentication operation */
+ crda->crd_alg = ahx->type;
+ crda->crd_key = tdb->tdb_amxkey;
+ crda->crd_klen = tdb->tdb_amxkeylen * 8;
- default:
- ahx->Update(&ctx, ipseczeroes, ptr[off + 1]);
- off += ptr[off + 1];
- break;
- }
+ /* Save the skipped portion of the packet */
+ MALLOC(crp->crp_opaque4, caddr_t, skip, M_XDATA, M_DONTWAIT);
+ if (crp->crp_opaque4 == 0)
+ {
+ m_freem(m);
+ crypto_freereq(crp);
+ DPRINTF(("ah_output(): failed to allocate auth array\n"));
+ ahstat.ahs_crypto++;
+ return ENOBUFS;
+ }
+ else
+ m_copydata(m, 0, skip, crp->crp_opaque4);
- /* Sanity check */
- if (off > skip)
- {
- DPRINTF(("ah_output(): malformed IPv4 options header in SA %s/%08x\n", ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi)));
- ahstat.ahs_hdrops++;
- m_freem(m);
- return EMSGSIZE;
- }
- }
+ /*
+ * Fix IP header length on the header used for authentication. We don't
+ * need to fix the original header length as it will be fixed by our
+ * caller.
+ */
+ switch (tdb->tdb_dst.sa.sa_family)
+ {
+#ifdef INET
+ case AF_INET:
+ bcopy(crp->crp_opaque4 + offsetof(struct ip, ip_len),
+ (caddr_t) &iplen, sizeof(u_int16_t));
+ iplen = htons(ntohs(iplen) + rplen + ahx->authsize);
+ m_copyback(m, offsetof(struct ip, ip_len), sizeof(u_int16_t),
+ (caddr_t) &iplen);
break;
#endif /* INET */
#ifdef INET6
case AF_INET6:
- /* Copy and "cook" the IPv6 header */
- m_copydata(m, 0, sizeof(ip6), (unsigned char *) &ip6);
-
- /* We don't do IPv6 Jumbograms */
- if (ip6.ip6_plen == 0)
- {
- DPRINTF(("ah_output(): unsupported IPv6 jumbogram in SA %s/%08x\n", ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi)));
- ahstat.ahs_hdrops++;
- m_freem(m);
- return EMSGSIZE;
- }
-
- ip6.ip6_flow = 0;
- ip6.ip6_hlim = 0;
- ip6.ip6_vfc &= ~IPV6_VERSION_MASK;
- ip6.ip6_vfc |= IPV6_VERSION;
-
- /*
- * Note that here we assume that on output, the IPv6 header
- * and any Type0 Routing Header present have been made to look
- * like the will at the destination. Note that this is a
- * different assumption than we made for IPv4 (because of
- * different option processing in IPv4 and IPv6, and different
- * code paths from IPv4/IPv6 to here).
- */
-
- /* Include IPv6 header in authenticator computation */
- ahx->Update(&ctx, (unsigned char *) &ip6, sizeof(ip6));
-
- /* Let's deal with the remaining headers (if any) */
- if (skip - sizeof(struct ip6_hdr) > 0)
- {
- if (m->m_len <= skip)
- {
- MALLOC(ptr, unsigned char *,
- skip - sizeof(struct ip6_hdr), M_XDATA, M_WAITOK);
-
- /* Copy all the protocol headers after the IPv6 header */
- m_copydata(m, sizeof(struct ip6_hdr),
- skip - sizeof(struct ip6_hdr), ptr);
- }
- else
- ptr = mtod(m, unsigned char *) + sizeof(struct ip6_hdr);
- }
- else
- break; /* Done */
+ bcopy(crp->crp_opaque4 + offsetof(struct ip6_hdr, ip6_plen),
+ (caddr_t) &iplen, sizeof(u_int16_t));
+ iplen = htons(ntohs(iplen) + rplen + ahx->authsize);
+ m_copyback(m, offsetof(struct ip6_hdr, ip6_plen),
+ sizeof(u_int16_t), (caddr_t) &iplen);
+ break;
+#endif /* INET6 */
+ }
- off = ip6.ip6_nxt & 0xff; /* Next header type */
- for (len = 0; len < skip - sizeof(struct ip6_hdr);)
- switch (off)
- {
- case IPPROTO_HOPOPTS:
- case IPPROTO_DSTOPTS:
- ip6e = (struct ip6_ext *) (ptr + len);
+ tdb->tdb_ref++;
- /*
- * Process the mutable/immutable options -- borrows
- * heavily from the KAME code.
- */
- for (last = len, count = len + sizeof(struct ip6_ext);
- count < len + ((ip6e->ip6e_len + 1) << 3);)
- {
- if (ptr[count] == IP6OPT_PAD1)
- {
- count++;
- continue;
- }
+ /* Update the Next Protocol field in the IP header and the saved data */
+ len = IPPROTO_AH;
+ m_copyback(m, protoff, sizeof(u_int8_t), (caddr_t) &len);
+ ((u_int8_t *) crp->crp_opaque4)[protoff] = IPPROTO_AH;
- /* Sanity check */
- if (count + sizeof(struct ip6_ext) > len +
- ((ip6e->ip6e_len + 1) << 3))
- {
- DPRINTF(("ah_output(): malformed IPv6 options header in SA %s/%08x\n", ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi)));
- ahstat.ahs_hdrops++;
- m_freem(m);
+ /* "Massage" the packet headers for crypto processing */
+ if ((len = ah_massage_headers(&m, tdb->tdb_dst.sa.sa_family,
+ skip, ahx->type, 1)) != 0)
+ {
+ /* mbuf will be free'd by callee */
+ FREE(crp->crp_opaque4, M_XDATA);
+ crypto_freereq(crp);
+ return len;
+ }
- /* Free, if we allocated */
- if (m->m_len < skip)
- FREE(ptr, M_XDATA);
- return EMSGSIZE;
- }
+ /* Crypto operation descriptor */
+ crp->crp_ilen = m->m_pkthdr.len; /* Total input length */
+ crp->crp_flags = CRYPTO_F_IMBUF;
+ crp->crp_buf = (caddr_t) m;
+ crp->crp_callback = (int (*) (struct cryptop *)) ah_output_cb;
+ crp->crp_sid = tdb->tdb_cryptoid;
- /*
- * If mutable option, calculate authenticator
- * for all immutable fields so far, then include
- * a zeroed-out version of this option.
- */
- if (ptr[count] & IP6OPT_MUTABLE)
- {
- /* Calculate immutables */
- ahx->Update(&ctx, ptr + last, count + 2 - last);
- last = count + ptr[count + 1];
+ /* These are passed as-is to the callback */
+ crp->crp_opaque1 = (caddr_t) tdb;
+ crp->crp_opaque2 = (caddr_t) skip;
+ crp->crp_opaque3 = (caddr_t) protoff;
- /* Calculate "zeroed-out" immutables */
- ahx->Update(&ctx, ipseczeroes,
- ptr[count + 1] - 2);
- }
-
- count += ptr[count + 1];
-
- /* Sanity check */
- if (count > skip - sizeof(struct ip6_hdr))
- {
- DPRINTF(("ah_output(): malformed IPv6 options header in SA %s/%08x\n", ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi)));
- ahstat.ahs_hdrops++;
- m_freem(m);
+ return crypto_dispatch(crp);
+}
- /* Free, if we allocated */
- if (m->m_len < skip)
- FREE(ptr, M_XDATA);
- return EMSGSIZE;
- }
- }
+/*
+ * AH output callback, called directly from the crypto handler.
+ */
+int
+ah_output_cb(void *op)
+{
+ int skip, protoff, error;
+ struct cryptop *crp;
+ struct tdb *tdb;
+ struct mbuf *m;
- /* Include any trailing immutable options */
- ahx->Update(&ctx, ptr + last,
- len + ((ip6e->ip6e_len + 1) << 3) - last);
+ crp = (struct cryptop *) op;
+ tdb = (struct tdb *) crp->crp_opaque1;
+ skip = (int) crp->crp_opaque2;
+ protoff = (int) crp->crp_opaque3;
+ m = (struct mbuf *) crp->crp_buf;
- len += ((ip6e->ip6e_len + 1) << 3); /* Advance */
- off = ip6e->ip6e_nxt;
- break;
+ tdb->tdb_ref--;
- case IPPROTO_ROUTING:
- ip6e = (struct ip6_ext *) (ptr + len);
- ahx->Update(&ctx, ptr + len, (ip6e->ip6e_len + 1) << 3);
- len += ((ip6e->ip6e_len + 1) << 3); /* Advance */
- off = ip6e->ip6e_nxt;
- break;
- }
-
- /* Free, if we allocated */
- if (m->m_len < skip)
- {
- FREE(ptr, M_XDATA);
- ptr = NULL;
- }
+ /* Check for crypto errors */
+ if (crp->crp_etype)
+ {
+ if (tdb->tdb_cryptoid != 0)
+ tdb->tdb_cryptoid = crp->crp_sid;
- break;
-#endif /* INET6 */
+ if (crp->crp_etype == EAGAIN)
+ {
+ tdb->tdb_ref++;
+ return crypto_dispatch(crp);
+ }
- default:
- DPRINTF(("ah_output(): unsupported protocol family %d in SA %s/%08x\n", ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi)));
- ahstat.ahs_nopf++;
- m_freem(m);
- return EPFNOSUPPORT;
+ ahstat.ahs_noxform++;
+ DPRINTF(("ah_output_cb(): crypto error %d\n", crp->crp_etype));
+ error = crp->crp_etype;
+ goto baddone;
}
- /* Inject AH header */
- (*mp) = m_inject(m, skip, AH_FLENGTH + rplen + ahx->authsize, M_WAITOK);
- if ((*mp) == NULL)
+ /* Shouldn't happen... */
+ if (!m)
{
- DPRINTF(("ah_output(): failed to inject AH header for SA %s/%08x\n", ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi)));
- m_freem(m);
- ahstat.ahs_wrap++;
- return ENOBUFS;
+ ahstat.ahs_crypto++;
+ DPRINTF(("ah_output_cb(): bogus returned buffer from crypto\n"));
+ error = EINVAL;
+ goto baddone;
}
/*
- * The AH header is guaranteed by m_inject() to be in contiguous memory,
- * at the beginning of the returned mbuf.
+ * Check that the TDB is still valid -- not really an error, but
+ * we need to handle it as such. It may happen if the TDB expired
+ * or was deleted while there was a pending request in the crypto
+ * queue.
*/
- ah = mtod((*mp), struct ah *);
-
- /* Initialize the AH header */
- m_copydata(m, protoff, sizeof(u_int8_t), (caddr_t) &ah->ah_nh);
- ah->ah_hl = (rplen + ahx->authsize) / sizeof(u_int32_t);
- ah->ah_rv = 0;
- ah->ah_spi = tdb->tdb_spi;
-
- if (!(tdb->tdb_flags & TDBF_NOREPLAY))
- ah->ah_rpl = htonl(tdb->tdb_rpl++);
-
- /* Update the Next Protocol field in the IP header */
- len = IPPROTO_AH;
- m_copyback(m, protoff, sizeof(u_int8_t), (caddr_t) &len);
-
- /* Include the header AH in the authenticator computation */
- ahx->Update(&ctx, (unsigned char *) ah, AH_FLENGTH + rplen);
- ahx->Update(&ctx, ipseczeroes, ahx->authsize);
-
- /* Calculate the authenticator over the rest of the packet */
- len = m->m_pkthdr.len - (skip + AH_FLENGTH + rplen + ahx->authsize);
- off = AH_FLENGTH + rplen + ahx->authsize;
-
- while (len > 0)
+ if (tdb->tdb_flags & TDBF_INVALID)
{
- if ((*mp) == 0)
- {
- DPRINTF(("ah_output(): bad mbuf chain for packet in SA %s/%08x\n", ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi)));
- ahstat.ahs_hdrops++;
- m_freem(m);
- (*mp) = NULL;
- return EMSGSIZE;
- }
+ ahstat.ahs_invalid++;
+ tdb_delete(tdb, 0, 0);
+ error = ENXIO;
+ DPRINTF(("ah_output_cb(): TDB expired while processing crypto\n"));
+ goto baddone;
+ }
- count = min((*mp)->m_len - off, len);
+ /* Copy original headers (with the new protocol number) back in place */
+ m_copyback(m, 0, skip, crp->crp_opaque4);
- ahx->Update(&ctx, mtod((*mp), unsigned char *) + off, count);
+ /* No longer needed */
+ FREE(crp->crp_opaque4, M_XDATA);
+ crypto_freereq(crp);
- len -= count;
- off = 0;
- (*mp) = (*mp)->m_next;
- }
+ return ipsp_process_done(m, tdb);
- if ((ahx->type == SADB_X_AALG_MD5) || (ahx->type == SADB_X_AALG_SHA1))
- ahx->Update(&ctx, (unsigned char *) tdb->tdb_amxkey,
- tdb->tdb_amxkeylen);
- else
- {
- /* HMAC */
- ahx->Final(calcauth, &ctx);
- bcopy(tdb->tdb_octx, &ctx, ahx->ctxsize);
- ahx->Update(&ctx, calcauth, ahx->hashsize);
- }
+ baddone:
+ if (m)
+ m_freem(m);
- ahx->Final(calcauth, &ctx);
+ /* We have to free this manually */
+ if (crp && crp->crp_opaque4)
+ FREE(crp->crp_opaque4, M_XDATA);
- /* Copy the authenticator */
- bcopy(calcauth, ((caddr_t) ah) + AH_FLENGTH + rplen, ahx->authsize);
+ crypto_freereq(crp);
- *mp = m;
-
- return 0;
+ return error;
}
diff --git a/sys/netinet/ip_ah.h b/sys/netinet/ip_ah.h
index 1938abad340..45901eea86f 100644
--- a/sys/netinet/ip_ah.h
+++ b/sys/netinet/ip_ah.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: ip_ah.h,v 1.24 2000/01/27 08:09:08 angelos Exp $ */
+/* $OpenBSD: ip_ah.h,v 1.25 2000/03/17 10:25:22 angelos Exp $ */
/*
* The authors of this code are John Ioannidis (ji@tla.org),
@@ -40,23 +40,24 @@
struct ahstat
{
- u_int32_t ahs_hdrops; /* packet shorter than header shows */
- u_int32_t ahs_nopf; /* Protocol family not supported */
+ u_int32_t ahs_hdrops; /* Packet shorter than header shows */
+ u_int32_t ahs_nopf; /* Protocol family not supported */
u_int32_t ahs_notdb;
u_int32_t ahs_badkcr;
u_int32_t ahs_badauth;
u_int32_t ahs_noxform;
u_int32_t ahs_qfull;
- u_int32_t ahs_wrap;
- u_int32_t ahs_replay;
- u_int32_t ahs_badauthl; /* bad authenticator length */
+ u_int32_t ahs_wrap;
+ u_int32_t ahs_replay;
+ u_int32_t ahs_badauthl; /* Bad authenticator length */
u_int32_t ahs_input; /* Input AH packets */
u_int32_t ahs_output; /* Output AH packets */
- u_int32_t ahs_invalid; /* Trying to use an invalid TDB */
- u_int64_t ahs_ibytes; /* input bytes */
- u_int64_t ahs_obytes; /* output bytes */
- u_int32_t ahs_toobig; /* packet got larger than IP_MAXPACKET */
- u_int32_t ahs_pdrops; /* packet blocked due to policy */
+ u_int32_t ahs_invalid; /* Trying to use an invalid TDB */
+ u_int64_t ahs_ibytes; /* Input bytes */
+ u_int64_t ahs_obytes; /* Output bytes */
+ u_int32_t ahs_toobig; /* Packet got larger than IP_MAXPACKET */
+ u_int32_t ahs_pdrops; /* Packet blocked due to policy */
+ u_int32_t ahs_crypto; /* Crypto processing failure */
};
struct ah
diff --git a/sys/netinet/ip_esp.c b/sys/netinet/ip_esp.c
index b6bf578d455..57ee4adb935 100644
--- a/sys/netinet/ip_esp.c
+++ b/sys/netinet/ip_esp.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: ip_esp.c,v 1.32 2000/02/07 06:09:09 itojun Exp $ */
+/* $OpenBSD: ip_esp.c,v 1.33 2000/03/17 10:25:22 angelos Exp $ */
/*
* The authors of this code are John Ioannidis (ji@tla.org),
@@ -73,6 +73,12 @@
#include <net/pfkeyv2.h>
#include <net/if_enc.h>
+#include <sys/md5k.h>
+#include <crypto/sha1.h>
+#include <crypto/rmd160.h>
+#include <crypto/crypto.h>
+#include <crypto/xform.h>
+
#include "bpfilter.h"
#ifdef ENCDEBUG
@@ -85,34 +91,9 @@
#define offsetof(s, e) ((int)&((s *)0)->e)
#endif
-extern struct auth_hash auth_hash_hmac_md5_96;
-extern struct auth_hash auth_hash_hmac_sha1_96;
-extern struct auth_hash auth_hash_hmac_ripemd_160_96;
-
-struct auth_hash *esp_hash[] = {
- &auth_hash_hmac_md5_96,
- &auth_hash_hmac_sha1_96,
- &auth_hash_hmac_ripemd_160_96
-};
-
-extern struct enc_xform enc_xform_des;
-extern struct enc_xform enc_xform_3des;
-extern struct enc_xform enc_xform_blf;
-extern struct enc_xform enc_xform_cast5;
-extern struct enc_xform enc_xform_skipjack;
-
-struct enc_xform *esp_xform[] = {
- &enc_xform_des,
- &enc_xform_3des,
- &enc_xform_blf,
- &enc_xform_cast5,
- &enc_xform_skipjack,
-};
-
/*
* esp_attach() is called from the transformation initialization code.
*/
-
int
esp_attach()
{
@@ -122,27 +103,39 @@ esp_attach()
/*
* esp_init() is called when an SPI is being set up.
*/
-
int
esp_init(struct tdb *tdbp, struct xformsw *xsp, struct ipsecinit *ii)
{
struct enc_xform *txform = NULL;
struct auth_hash *thash = NULL;
- int i;
+ struct cryptoini cria, crie;
- /* Check whether the encryption algorithm is supported */
- for (i = sizeof(esp_xform) / sizeof(esp_xform[0]) - 1;
- i >= 0; i--)
- if (ii->ii_encalg == esp_xform[i]->type)
- break;
-
- if (i < 0)
+ switch (ii->ii_encalg)
{
- DPRINTF(("esp_init(): unsupported encryption algorithm %d specified\n", ii->ii_encalg));
- return EINVAL;
- }
+ case SADB_EALG_DESCBC:
+ txform = &enc_xform_des;
+ break;
+
+ case SADB_EALG_3DESCBC:
+ txform = &enc_xform_3des;
+ break;
+
+ case SADB_X_EALG_BLF:
+ txform = &enc_xform_blf;
+ break;
+
+ case SADB_X_EALG_CAST:
+ txform = &enc_xform_cast5;
+ break;
+
+ case SADB_X_EALG_SKIPJACK:
+ txform = &enc_xform_skipjack;
+ break;
- txform = esp_xform[i];
+ default:
+ DPRINTF(("esp_init(): unsupported encryption algorithm %d specified\n", ii->ii_encalg));
+ return EINVAL;
+ }
if (ii->ii_enckeylen < txform->minkey)
{
@@ -158,18 +151,24 @@ esp_init(struct tdb *tdbp, struct xformsw *xsp, struct ipsecinit *ii)
if (ii->ii_authalg)
{
- for (i = sizeof(esp_hash) / sizeof(esp_hash[0]) - 1;
- i >= 0; i--)
- if (ii->ii_authalg == esp_hash[i]->type)
- break;
-
- if (i < 0)
+ switch (ii->ii_authalg)
{
- DPRINTF(("esp_init(): unsupported authentication algorithm %d specified\n", ii->ii_authalg));
- return EINVAL;
- }
+ case SADB_AALG_MD5HMAC96:
+ thash = &auth_hash_hmac_md5_96;
+ break;
+
+ case SADB_AALG_SHA1HMAC96:
+ thash = &auth_hash_hmac_sha1_96;
+ break;
- thash = esp_hash[i];
+ case SADB_X_AALG_RIPEMD160HMAC96:
+ thash = &auth_hash_hmac_ripemd_160_96;
+ break;
+
+ default:
+ DPRINTF(("esp_init(): unsupported authentication algorithm %d specified\n", ii->ii_authalg));
+ return EINVAL;
+ }
if (ii->ii_authkeylen != thash->keysize)
{
@@ -195,53 +194,62 @@ esp_init(struct tdb *tdbp, struct xformsw *xsp, struct ipsecinit *ii)
if (tdbp->tdb_flags & TDBF_HALFIV)
tdbp->tdb_ivlen /= 2;
- /* Initialize the IV */
- get_random_bytes(tdbp->tdb_iv, tdbp->tdb_ivlen);
-
/* Save the raw keys */
- tdbp->tdb_amxkeylen = ii->ii_authkeylen;
- MALLOC(tdbp->tdb_amxkey, u_int8_t *, tdbp->tdb_amxkeylen, M_XDATA,
- M_WAITOK);
- bcopy(ii->ii_authkey, tdbp->tdb_amxkey, tdbp->tdb_amxkeylen);
+ if (tdbp->tdb_authalgxform)
+ {
+ tdbp->tdb_amxkeylen = ii->ii_authkeylen;
+ MALLOC(tdbp->tdb_amxkey, u_int8_t *, tdbp->tdb_amxkeylen, M_XDATA,
+ M_WAITOK);
+ bcopy(ii->ii_authkey, tdbp->tdb_amxkey, tdbp->tdb_amxkeylen);
+ }
- tdbp->tdb_emxkeylen = ii->ii_enckeylen;
- MALLOC(tdbp->tdb_emxkey, u_int8_t *, tdbp->tdb_emxkeylen, M_XDATA,
- M_WAITOK);
- bcopy(ii->ii_enckey, tdbp->tdb_emxkey, tdbp->tdb_emxkeylen);
+ if (tdbp->tdb_encalgxform)
+ {
+ tdbp->tdb_emxkeylen = ii->ii_enckeylen;
+ MALLOC(tdbp->tdb_emxkey, u_int8_t *, tdbp->tdb_emxkeylen, M_XDATA,
+ M_WAITOK);
+ bcopy(ii->ii_enckey, tdbp->tdb_emxkey, tdbp->tdb_emxkeylen);
+ }
+
+ /* Initialize crypto session */
+ if (tdbp->tdb_encalgxform)
+ {
+ bzero(&crie, sizeof(crie));
+
+ crie.cri_alg = tdbp->tdb_encalgxform->type;
- if (txform->setkey)
- txform->setkey(&tdbp->tdb_key, ii->ii_enckey, ii->ii_enckeylen);
+ if (tdbp->tdb_authalgxform)
+ crie.cri_next = &cria;
+ else
+ crie.cri_next = NULL;
+
+ crie.cri_klen = ii->ii_enckeylen * 8;
+ crie.cri_key = ii->ii_enckey;
+ /* XXX Rounds ? */
+ }
- if (thash)
+ if (tdbp->tdb_authalgxform)
{
- /* Precompute the I and O pads of the HMAC */
- for (i = 0; i < ii->ii_authkeylen; i++)
- ii->ii_authkey[i] ^= HMAC_IPAD_VAL;
-
- MALLOC(tdbp->tdb_ictx, u_int8_t *, thash->ctxsize, M_XDATA, M_WAITOK);
- bzero(tdbp->tdb_ictx, thash->ctxsize);
- thash->Init(tdbp->tdb_ictx);
- thash->Update(tdbp->tdb_ictx, ii->ii_authkey, ii->ii_authkeylen);
- thash->Update(tdbp->tdb_ictx, hmac_ipad_buffer,
- HMAC_BLOCK_LEN - ii->ii_authkeylen);
-
- for (i = 0; i < ii->ii_authkeylen; i++)
- ii->ii_authkey[i] ^= (HMAC_IPAD_VAL ^ HMAC_OPAD_VAL);
-
- MALLOC(tdbp->tdb_octx, u_int8_t *, thash->ctxsize, M_XDATA, M_WAITOK);
- bzero(tdbp->tdb_octx, thash->ctxsize);
- thash->Init(tdbp->tdb_octx);
- thash->Update(tdbp->tdb_octx, ii->ii_authkey, ii->ii_authkeylen);
- thash->Update(tdbp->tdb_octx, hmac_opad_buffer,
- HMAC_BLOCK_LEN - ii->ii_authkeylen);
+ bzero(&cria, sizeof(cria));
+
+ cria.cri_alg = tdbp->tdb_authalgxform->type;
+ cria.cri_next = NULL;
+ cria.cri_klen = ii->ii_authkeylen * 8;
+ cria.cri_key = ii->ii_authkey;
}
- return 0;
+ return crypto_newsession(&tdbp->tdb_cryptoid,
+ (tdbp->tdb_encalgxform ? &crie : &cria));
}
+/*
+ * Paranoia.
+ */
int
esp_zeroize(struct tdb *tdbp)
{
+ int err;
+
if (tdbp->tdb_amxkey)
{
bzero(tdbp->tdb_amxkey, tdbp->tdb_amxkeylen);
@@ -256,69 +264,54 @@ esp_zeroize(struct tdb *tdbp)
tdbp->tdb_emxkey = NULL;
}
- if (tdbp->tdb_key && tdbp->tdb_encalgxform &&
- tdbp->tdb_encalgxform->zerokey)
- tdbp->tdb_encalgxform->zerokey(&tdbp->tdb_key);
-
- if (tdbp->tdb_ictx)
- {
- if (tdbp->tdb_authalgxform)
- bzero(tdbp->tdb_ictx, tdbp->tdb_authalgxform->ctxsize);
- FREE(tdbp->tdb_ictx, M_XDATA);
- tdbp->tdb_ictx = NULL;
- }
-
- if (tdbp->tdb_octx)
- {
- if (tdbp->tdb_authalgxform)
- bzero(tdbp->tdb_octx, tdbp->tdb_authalgxform->ctxsize);
- FREE(tdbp->tdb_octx, M_XDATA);
- tdbp->tdb_octx = NULL;
- }
-
- return 0;
+ err = crypto_freesession(tdbp->tdb_cryptoid);
+ tdbp->tdb_cryptoid = 0;
+ return err;
}
#define MAXBUFSIZ (AH_ALEN_MAX > ESP_MAX_IVS ? AH_ALEN_MAX : ESP_MAX_IVS)
-struct mbuf *
+/*
+ * ESP input processing, called (eventually) through the protocol switch.
+ */
+int
esp_input(struct mbuf *m, struct tdb *tdb, int skip, int protoff)
{
struct auth_hash *esph = (struct auth_hash *) tdb->tdb_authalgxform;
struct enc_xform *espx = (struct enc_xform *) tdb->tdb_encalgxform;
- int oplen, plen, alen, ilen, i, blks, rest, count, off, roff, hlen;
- u_char iv[MAXBUFSIZ], niv[MAXBUFSIZ], blk[ESP_MAX_BLKS], *lblk;
- u_char *idat, *odat, *ivp, *ivn;
- struct mbuf *mi, *mo, *m1;
- union authctx ctx;
+ int plen, alen, hlen;
u_int32_t btsx;
+ struct cryptodesc *crde = NULL, *crda = NULL;
+ struct cryptop *crp;
+
/* Determine the ESP header length */
if (tdb->tdb_flags & TDBF_NOREPLAY)
hlen = sizeof(u_int32_t) + tdb->tdb_ivlen; /* "old" ESP */
else
hlen = 2 * sizeof(u_int32_t) + tdb->tdb_ivlen; /* "new" ESP */
- blks = espx->blocksize;
-
if (esph)
alen = AH_HMAC_HASHLEN;
else
alen = 0;
- /* Skip the IP header, IP options, SPI, IV, and any Replay and Auth Data */
- plen = m->m_pkthdr.len - (skip + hlen + alen);
- if ((plen & (blks - 1)) || (plen <= 0))
+ if (espx)
{
- DPRINTF(("esp_input(): payload not a multiple of %d octets, SA %s/%08x\n", blks, ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi)));
- espstat.esps_badilen++;
- m_freem(m);
- return NULL;
+ /*
+ * Verify payload length is multiple of encryption algorithm
+ * block size.
+ */
+ plen = m->m_pkthdr.len - (skip + hlen + alen);
+ if ((plen & (espx->blocksize - 1)) || (plen <= 0))
+ {
+ DPRINTF(("esp_input(): payload not a multiple of %d octets, SA %s/%08x\n", espx->blocksize, ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi)));
+ espstat.esps_badilen++;
+ m_freem(m);
+ return EINVAL;
+ }
}
- /* Auth covers SPI + SN + IV */
- oplen = plen + hlen;
-
/* Replay window checking, if appropriate */
if ((tdb->tdb_wnd > 0) && (!(tdb->tdb_flags & TDBF_NOREPLAY)))
{
@@ -336,20 +329,20 @@ esp_input(struct mbuf *m, struct tdb *tdb, int skip, int protoff)
DPRINTF(("esp_input(): replay counter wrapped for SA %s/%08x\n", ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi)));
espstat.esps_wrap++;
m_freem(m);
- return NULL;
+ return EACCES;
case 2:
case 3:
DPRINTF(("esp_input(): duplicate packet received in SA %s/%08x\n", ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi)));
espstat.esps_replay++;
m_freem(m);
- return NULL;
+ return EACCES;
default:
DPRINTF(("esp_input(): bogus value from checkreplaywindow32() in SA %s/%08x\n", ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi)));
espstat.esps_replay++;
m_freem(m);
- return NULL;
+ return EACCES;
}
}
@@ -364,10 +357,10 @@ esp_input(struct mbuf *m, struct tdb *tdb, int skip, int protoff)
pfkeyv2_expire(tdb, SADB_EXT_LIFETIME_HARD);
tdb_delete(tdb, 0, TDBEXP_TIMEOUT);
m_freem(m);
- return NULL;
+ return ENXIO;
}
- /* Notify on expiration */
+ /* Notify on soft expiration */
if ((tdb->tdb_flags & TDBF_SOFT_BYTES) &&
(tdb->tdb_cur_bytes >= tdb->tdb_soft_bytes))
{
@@ -375,114 +368,191 @@ esp_input(struct mbuf *m, struct tdb *tdb, int skip, int protoff)
tdb->tdb_flags &= ~TDBF_SOFT_BYTES; /* Turn off checking */
}
- /*
- * Skip forward to the beginning of the ESP header. If we run out
- * of mbufs in the process, the check inside the following while()
- * loop will catch it.
- */
- for (mo = m, i = 0; mo && i + mo->m_len <= skip; mo = mo->m_next)
- i += mo->m_len;
-
- off = skip - i;
-
- /* Preserve these for later processing */
- roff = off;
- m1 = mo;
+ /* Get crypto descriptors */
+ crp = crypto_getreq(esph && espx ? 2 : 1);
+ if (crp == NULL)
+ {
+ m_freem(m);
+ DPRINTF(("esp_input(): failed to acquire crypto descriptors\n"));
+ espstat.esps_crypto++;
+ return ENOBUFS;
+ }
- /* Verify the authenticator, if applicable */
if (esph)
{
- bcopy(tdb->tdb_ictx, &ctx, esph->ctxsize);
+ crda = crp->crp_desc;
+ crde = crda->crd_next;
- /* Copy the authentication data */
- m_copydata(m, m->m_pkthdr.len - alen, alen, iv);
+ /* Authentication descriptor */
+ crda->crd_skip = skip;
+ crda->crd_len = m->m_pkthdr.len - (skip + alen);
+ crda->crd_inject = m->m_pkthdr.len - alen;
- /* Compute authenticator over the mbuf chain */
- while (oplen > 0)
- {
- if (mo == NULL)
- {
- DPRINTF(("esp_input(): bad mbuf chain, SA %s/%08x\n",
- ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi)));
- espstat.esps_hdrops++;
- m_freem(m);
- return NULL;
- }
+ crda->crd_alg = esph->type;
+ crda->crd_key = tdb->tdb_amxkey;
+ crda->crd_klen = tdb->tdb_amxkeylen * 8;
+ }
+ else
+ crde = crp->crp_desc;
- count = min(mo->m_len - off, oplen);
- esph->Update(&ctx, mtod(mo, unsigned char *) + off, count);
- oplen -= count;
- off = 0;
- mo = mo->m_next;
- }
+ tdb->tdb_ref++;
- esph->Final(niv, &ctx);
- bcopy(tdb->tdb_octx, &ctx, esph->ctxsize);
- esph->Update(&ctx, niv, esph->hashsize);
- esph->Final(niv, &ctx);
+ /* Crypto operation descriptor */
+ crp->crp_ilen = m->m_pkthdr.len; /* Total input length */
+ crp->crp_flags = CRYPTO_F_IMBUF;
+ crp->crp_buf = (caddr_t) m;
+ crp->crp_callback = (int (*) (struct cryptop *)) esp_input_cb;
+ crp->crp_sid = tdb->tdb_cryptoid;
- /* Verify */
- if (bcmp(niv, iv, AH_HMAC_HASHLEN))
- {
- DPRINTF(("esp_input(): authentication failed for packet in SA %s/%08x\n", ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi)));
- espstat.esps_badauth++;
- m_freem(m);
- return NULL;
- }
+ /* These are passed as-is to the callback */
+ crp->crp_opaque1 = (caddr_t) tdb;
+ crp->crp_opaque2 = (caddr_t) skip;
+ crp->crp_opaque3 = (caddr_t) protoff;
+
+ /* Decryption descriptor */
+ if (espx)
+ {
+ crde->crd_skip = skip + hlen;
+ crde->crd_len = m->m_pkthdr.len - (skip + hlen + alen);
+ crde->crd_inject = skip + hlen - tdb->tdb_ivlen;
+ if (tdb->tdb_flags & TDBF_HALFIV)
+ crde->crd_flags |= CRD_F_HALFIV;
+
+ crde->crd_alg = espx->type;
+ crde->crd_key = tdb->tdb_emxkey;
+ crde->crd_klen = tdb->tdb_emxkeylen * 8;
+ /* XXX Rounds ? */
}
- oplen = plen;
+ MALLOC(crp->crp_opaque4, caddr_t, alen, M_XDATA, M_DONTWAIT);
+ if (crp->crp_opaque4 == 0)
+ {
+ m_freem(m);
+ crypto_freereq(crp);
+ DPRINTF(("esp_input(): failed to allocate auth array\n"));
+ espstat.esps_crypto++;
+ return ENOBUFS;
+ }
- /* Find beginning of encrypted data (actually, the IV) */
- mi = m1;
- ilen = mi->m_len - roff - sizeof(u_int32_t);
- if (!(tdb->tdb_flags & TDBF_NOREPLAY))
- ilen -= sizeof(u_int32_t);
- while (ilen <= 0)
+ /* Copy the authenticator */
+ m_copydata(m, m->m_pkthdr.len - alen, alen, crp->crp_opaque4);
+
+ return crypto_dispatch(crp);
+}
+
+/*
+ * ESP input callback, called directly by the crypto driver.
+ */
+int
+esp_input_cb(void *op)
+{
+ u_int8_t lastthree[3], aalg[AH_HMAC_HASHLEN];
+ int hlen, roff, skip, protoff, error;
+ struct mbuf *m1, *mo, *m;
+ struct cryptodesc *crd;
+ struct auth_hash *esph;
+ struct enc_xform *espx;
+ struct cryptop *crp;
+ struct tdb *tdb;
+
+ crp = (struct cryptop *) op;
+ crd = crp->crp_desc;
+ tdb = (struct tdb *) crp->crp_opaque1;
+ esph = (struct auth_hash *) tdb->tdb_authalgxform;
+ espx = (struct enc_xform *) tdb->tdb_encalgxform;
+ skip = (int) crp->crp_opaque2;
+ protoff = (int) crp->crp_opaque3;
+ m = (struct mbuf *) crp->crp_buf;
+
+ tdb->tdb_ref--;
+
+ /* Check for crypto errors */
+ if (crp->crp_etype)
{
- mi = mi->m_next;
- if (mi == NULL)
+ /* Reset the session ID */
+ if (tdb->tdb_cryptoid != 0)
+ tdb->tdb_cryptoid = crp->crp_sid;
+
+ if (crp->crp_etype == EAGAIN)
{
- DPRINTF(("esp_input(): bad mbuf chain, SA %s/%08x\n",
- ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi)));
- espstat.esps_hdrops++;
- m_freem(m);
- return NULL;
+ tdb->tdb_ref++;
+ return crypto_dispatch(crp);
}
- ilen += mi->m_len;
+ espstat.esps_noxform++;
+ DPRINTF(("esp_input_cb(): crypto error %d\n", crp->crp_etype));
+ error = crp->crp_etype;
+ goto baddone;
}
- idat = mtod(mi, unsigned char *) + (mi->m_len - ilen);
- m_copydata(mi, mi->m_len - ilen, tdb->tdb_ivlen, iv);
+ /* Shouldn't happen... */
+ if (!m)
+ {
+ espstat.esps_crypto++;
+ DPRINTF(("esp_input_cb(): bogus returned buffer from crypto\n"));
+ error = EINVAL;
+ goto baddone;
+ }
- /* If we're doing half-IV, generate full IV */
- if (tdb->tdb_flags & TDBF_HALFIV)
+ /*
+ * Check that the TDB is still valid -- not really an error, but
+ * we need to handle it as such. It may happen if the TDB expired
+ * or was deleted while there was a pending request in the crypto
+ * queue.
+ */
+ if (tdb->tdb_flags & TDBF_INVALID)
{
- for (i = 0; i < tdb->tdb_ivlen; i++)
- iv[tdb->tdb_ivlen + i] = ~iv[i];
+ espstat.esps_invalid++;
+ tdb_delete(tdb, 0, 0);
+ error = ENXIO;
+ DPRINTF(("esp_input_cb(): TDB expired while processing crypto\n"));
+ goto baddone;
}
- /* Now skip over the IV */
- ilen -= tdb->tdb_ivlen;
- while (ilen <= 0)
+ /* If authentication was performed, check now */
+ if (esph)
{
- mi = mi->m_next;
- if (mi == NULL)
+ /* Copy the authenticator from the packet */
+ m_copydata(m, m->m_pkthdr.len - esph->authsize,
+ esph->authsize, aalg);
+
+ /* Verify authenticator */
+ if (bcmp(crp->crp_opaque4, aalg, esph->authsize))
{
- DPRINTF(("esp_input(): bad mbuf chain, SA %s/%08x\n",
- ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi)));
- espstat.esps_hdrops++;
- m_freem(m);
- return NULL;
+ DPRINTF(("esp_input_cb(): authentication failed for packet in SA %s/%08x\n", ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi)));
+ espstat.esps_badauth++;
+ error = EACCES;
+ goto baddone;
}
- ilen += mi->m_len;
+ /* Remove trailing authenticator */
+ m_adj(m, -(esph->authsize));
+
+ /* We have to manually free this */
+ FREE(crp->crp_opaque4, M_XDATA);
}
- /*
- * Remove the ESP header and IV from the mbuf.
- */
+ /* Release the crypto descriptors */
+ crypto_freereq(crp);
+
+ /* Determine the ESP header length */
+ if (tdb->tdb_flags & TDBF_NOREPLAY)
+ hlen = sizeof(u_int32_t) + tdb->tdb_ivlen; /* "old" ESP */
+ else
+ hlen = 2 * sizeof(u_int32_t) + tdb->tdb_ivlen; /* "new" ESP */
+
+ /* Find beginning of ESP header */
+ m1 = m_getptr(m, skip, &roff);
+ if (m1 == NULL)
+ {
+ DPRINTF(("esp_input_cb(): bad mbuf chain, SA %s/%08x\n",
+ ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi)));
+ espstat.esps_hdrops++;
+ m_freem(m);
+ return EINVAL;
+ }
+
+ /* Remove the ESP header and IV from the mbuf. */
if (roff == 0)
{
/* The ESP header was conveniently at the beginning of the mbuf */
@@ -494,8 +564,8 @@ esp_input(struct mbuf *m, struct tdb *tdb, int skip, int protoff)
if (roff + hlen >= m1->m_len)
{
/*
- * Part or all of the ESP header is at the end of this mbuf, so first
- * let's remove the remainder of the ESP header from the
+ * Part or all of the ESP header is at the end of this mbuf, so
+ * first let's remove the remainder of the ESP header from the
* beginning of the remainder of the mbuf chain, if any.
*/
if (roff + hlen > m1->m_len)
@@ -532,166 +602,67 @@ esp_input(struct mbuf *m, struct tdb *tdb, int skip, int protoff)
m->m_pkthdr.len -= hlen;
}
- /* Point to the encrypted data */
- idat = mtod(mi, unsigned char *) + (mi->m_len - ilen);
-
- /*
- * At this point:
- * plen is # of encapsulated payload octets
- * ilen is # of octets left in this mbuf
- * idat is first encapsulated payload octed in this mbuf
- * same for olen and odat
- * ivp points to the IV, ivn buffers the next IV.
- * mi points to the first mbuf
- *
- * From now on until the end of the mbuf chain:
- * . move the next eight octets of the chain into ivn
- * . decrypt idat and xor with ivp
- * . swap ivp and ivn.
- * . repeat
- */
+ /* Save the last three bytes of decrypted data */
+ m_copydata(m, m->m_pkthdr.len - 3, 3, lastthree);
- ivp = iv;
- ivn = niv;
- rest = ilen % blks;
- while (plen > 0) /* while not done */
+ /* Verify pad length */
+ if (lastthree[1] + 2 > m->m_pkthdr.len - skip - hlen)
{
- if (ilen < blks)
- {
- if (rest)
- {
- bcopy(idat, blk, rest);
- odat = idat;
- }
-
- do {
- mi = (mo = mi)->m_next;
- if (mi == NULL)
- {
- DPRINTF(("esp_input(): bad mbuf chain, SA %s/%08x\n",
- ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi)));
- espstat.esps_hdrops++;
- m_freem(m);
- return NULL;
- }
- } while (mi->m_len == 0);
-
- if (mi->m_len < blks - rest)
- {
- if ((mi = m_pullup(mi, blks - rest)) == NULL)
- {
- DPRINTF(("esp_input(): m_pullup() failed, SA %s/%08x\n", ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi)));
- m_freem(m);
- espstat.esps_hdrops++;
- return NULL;
- }
- /*
- * m_pullup was not called at the beginning of the chain
- * but might return a new mbuf, link it into the chain.
- */
- mo->m_next = mi;
- }
-
- ilen = mi->m_len;
- idat = mtod(mi, u_char *);
-
- if (rest)
- {
- bcopy(idat, blk + rest, blks - rest);
- bcopy(blk, ivn, blks);
-
- espx->decrypt(tdb, blk);
-
- for (i = 0; i < blks; i++)
- blk[i] ^= ivp[i];
-
- ivp = ivn;
- ivn = (ivp == iv) ? niv : iv;
-
- bcopy(blk, odat, rest);
- bcopy(blk + rest, idat, blks - rest);
-
- lblk = blk; /* last block touched */
-
- idat += blks - rest;
- ilen -= blks - rest;
- plen -= blks;
- }
-
- rest = ilen % blks;
- }
-
- while (ilen >= blks && plen > 0)
- {
- bcopy(idat, ivn, blks);
-
- espx->decrypt(tdb, idat);
-
- for (i = 0; i < blks; i++)
- idat[i] ^= ivp[i];
-
- ivp = ivn;
- ivn = (ivp == iv) ? niv : iv;
-
- lblk = idat; /* last block touched */
- idat += blks;
-
- ilen -= blks;
- plen -= blks;
- }
- }
-
- /* Save last block (end of padding), if it was in-place decrypted */
- if (lblk != blk)
- bcopy(lblk, blk, blks);
-
- /*
- * Now, the entire chain has been decrypted. As a side effect,
- * blk[blks - 1] contains the next protocol, and blk[blks - 2] contains
- * the amount of padding the original chain had. Chop off the
- * appropriate parts of the chain, and return.
- */
-
- if (blk[blks - 2] + 2 + alen > m->m_pkthdr.len - skip - hlen)
- {
- DPRINTF(("esp_input(): invalid padding length %d for packet in SA %s/%08x\n", blk[blks - 2], ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi)));
+ DPRINTF(("esp_input_cb(): invalid padding length %d for packet in SA %s/%08x\n", lastthree[2], ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi)));
espstat.esps_badilen++;
m_freem(m);
- return NULL;
+ return EINVAL;
}
- /* Verify correct decryption by checking the last padding bytes. */
+ /* Verify correct decryption by checking the last padding bytes */
if (!(tdb->tdb_flags & TDBF_RANDOMPADDING))
{
- if ((blk[blks - 2] != blk[blks - 3]) && (blk[blks - 2] != 0))
+ if ((lastthree[1] != lastthree[0]) && (lastthree[1] != 0))
{
DPRINTF(("esp_input(): decryption failed for packet in SA %s/%08x\n", ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi)));
espstat.esps_badenc++;
m_freem(m);
- return NULL;
+ return EINVAL;
}
}
- /* Trim the mbuf chain to remove the trailing authenticator */
- m_adj(m, - blk[blks - 2] - 2 - alen);
+ /* Trim the mbuf chain to remove the trailing authenticator and padding */
+ m_adj(m, -(lastthree[1] + 2));
/* Restore the Next Protocol field */
- m_copyback(m, protoff, 1, &blk[blks - 1]);
+ m_copyback(m, protoff, sizeof(u_int8_t), lastthree + 2);
+
+ /* Back to generic IPsec input processing */
+ return ipsec_common_input_cb(m, tdb, skip, protoff);
+
+ baddone:
+ if (m)
+ m_freem(m);
- return m;
+ /* We have to manually free this */
+ if (crp && crp->crp_opaque4)
+ FREE(crp->crp_opaque4, M_XDATA);
+
+ crypto_freereq(crp);
+
+ return error;
}
+/*
+ * ESP output routine, called by ipsp_process_packet().
+ */
int
esp_output(struct mbuf *m, struct tdb *tdb, struct mbuf **mp, int skip,
- int protoff)
+ int protoff)
{
struct enc_xform *espx = (struct enc_xform *) tdb->tdb_encalgxform;
struct auth_hash *esph = (struct auth_hash *) tdb->tdb_authalgxform;
- u_char iv[ESP_MAX_IVS], blk[ESP_MAX_BLKS], auth[AH_ALEN_MAX];
- int i, ilen, hlen, rlen, plen, padding, rest, blks, alen;
+ int ilen, hlen, rlen, plen, padding, blks, alen;
struct mbuf *mi, *mo = (struct mbuf *) NULL;
- u_char *pad, *idat, *odat, *ivp;
- union authctx ctx;
+ unsigned char *pad;
+
+ struct cryptodesc *crde = NULL, *crda = NULL;
+ struct cryptop *crp;
#if NBPFILTER > 0
{
@@ -727,8 +698,12 @@ esp_output(struct mbuf *m, struct tdb *tdb, struct mbuf **mp, int skip,
else
hlen = 2 * sizeof(u_int32_t) + tdb->tdb_ivlen;
- blks = espx->blocksize;
rlen = m->m_pkthdr.len - skip; /* Raw payload length */
+ if (espx)
+ blks = espx->blocksize;
+ else
+ blks = 4; /* If no encryption is used, we have to be 4-byte aligned */
+
padding = ((blks - ((rlen + 2) % blks)) % blks) + 2;
plen = rlen + padding; /* Padded payload length */
@@ -743,39 +718,50 @@ esp_output(struct mbuf *m, struct tdb *tdb, struct mbuf **mp, int skip,
if ((!(tdb->tdb_flags & TDBF_NOREPLAY)) &&
(tdb->tdb_rpl == 0) && (tdb->tdb_wnd > 0))
{
- DPRINTF(("esp_output(): SA %s/%0x8 should have expired\n",
+ DPRINTF(("esp_output(): SA %s/%08x should have expired\n",
ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi)));
m_freem(m);
espstat.esps_wrap++;
- return ENOBUFS;
+ return EACCES;
}
+ switch (tdb->tdb_dst.sa.sa_family)
+ {
#ifdef INET
- /* Check for IPv6 maximum packet size violations */
- if (tdb->tdb_dst.sa.sa_family == AF_INET)
- if (skip + hlen + rlen + padding + alen > IP_MAXPACKET)
- {
- DPRINTF(("esp_output(): packet in SA %s/%0x8 got too big\n",
- ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi)));
- m_freem(m);
- espstat.esps_toobig++;
- return EMSGSIZE;
- }
+ case AF_INET:
+ /* Check for IP maximum packet size violations */
+ if (skip + hlen + rlen + padding + alen > IP_MAXPACKET)
+ {
+ DPRINTF(("esp_output(): packet in SA %s/%08x got too big\n",
+ ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi)));
+ m_freem(m);
+ espstat.esps_toobig++;
+ return EMSGSIZE;
+ }
+ break;
#endif /* INET */
#ifdef INET6
- /* Check for IPv6 maximum packet size violations */
- if (tdb->tdb_dst.sa.sa_family == AF_INET6)
- if (skip + hlen + rlen + padding + alen > IPV6_MAXPACKET)
- {
- DPRINTF(("esp_output(): packet in SA %s/%0x8 got too big\n",
- ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi)));
- m_freem(m);
- espstat.esps_toobig++;
- return EMSGSIZE;
- }
+ case AF_INET6:
+ /* Check for IPv6 maximum packet size violations */
+ if (skip + hlen + rlen + padding + alen > IPV6_MAXPACKET)
+ {
+ DPRINTF(("esp_output(): packet in SA %s/%08x got too big\n",
+ ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi)));
+ m_freem(m);
+ espstat.esps_toobig++;
+ return EMSGSIZE;
+ }
+ break;
#endif /* INET6 */
+ default:
+ DPRINTF(("esp_output(): unknown/unsupported protocol family %d, SA %s/%08x\n", tdb->tdb_dst.sa.sa_family, ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi)));
+ m_freem(m);
+ espstat.esps_nopf++;
+ return EPFNOSUPPORT;
+ }
+
/* Update the counters */
tdb->tdb_cur_bytes += m->m_pkthdr.len - skip;
espstat.esps_obytes += m->m_pkthdr.len - skip;
@@ -800,7 +786,7 @@ esp_output(struct mbuf *m, struct tdb *tdb, struct mbuf **mp, int skip,
/*
* Loop through mbuf chain; if we find an M_EXT mbuf with
- * more than one reference, replace the rest of the chain.
+ * more than one reference, replace the rest of the chain.
*/
mi = m;
while (mi != NULL &&
@@ -833,37 +819,29 @@ esp_output(struct mbuf *m, struct tdb *tdb, struct mbuf **mp, int skip,
}
/* Inject ESP header */
- mo = m_inject(m, skip, hlen, M_WAITOK);
+ mo = m_inject(m, skip, hlen, M_DONTWAIT);
if (mo == NULL)
{
- DPRINTF(("esp_output(): failed to inject ESP header for SA %s/%08x\n", ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi)));
+ DPRINTF(("esp_output(): failed to inject ESP header for SA %s/%08x\n",
+ ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi)));
m_freem(m);
espstat.esps_wrap++;
return ENOBUFS;
}
/* Initialize ESP header */
- m_copyback(mo, 0, sizeof(u_int32_t), (caddr_t) &tdb->tdb_spi);
+ bcopy((caddr_t) &tdb->tdb_spi, mtod(mo, caddr_t), sizeof(u_int32_t));
if (!(tdb->tdb_flags & TDBF_NOREPLAY))
{
u_int32_t replay = htonl(tdb->tdb_rpl++);
- m_copyback(mo, sizeof(u_int32_t), sizeof(u_int32_t),
- (caddr_t) &replay);
+ bcopy((caddr_t) &replay, mtod(mo, caddr_t) + sizeof(u_int32_t),
+ sizeof(u_int32_t));
}
- /* Initialize IV (cook the half-IV if applicable) */
- bcopy(tdb->tdb_iv, iv, tdb->tdb_ivlen);
- if (tdb->tdb_flags & TDBF_HALFIV)
- {
- for (i = 0; i < tdb->tdb_ivlen; i++)
- iv[i + tdb->tdb_ivlen] = ~iv[i];
- }
-
- /* Copy IV in ESP header */
- m_copyback(mo, hlen - tdb->tdb_ivlen, tdb->tdb_ivlen,
- (caddr_t) &tdb->tdb_iv);
-
- /* Add padding */
+ /*
+ * Add padding -- better to do it ourselves than use the crypto engine,
+ * although if/when we support compression, we'd have to do that.
+ */
pad = (u_char *) m_pad(m, padding + alen,
tdb->tdb_flags & TDBF_RANDOMPADDING);
if (pad == NULL)
@@ -882,145 +860,142 @@ esp_output(struct mbuf *m, struct tdb *tdb, struct mbuf **mp, int skip,
/* Fix padding length and Next Protocol in padding itself */
pad[padding - 2] = padding - 2;
- m_copydata(m, protoff, 1, &pad[padding - 1]);
+ m_copydata(m, protoff, sizeof(u_int8_t), pad + padding - 1);
/* Fix Next Protocol in IPv4/IPv6 header */
ilen = IPPROTO_ESP;
m_copyback(m, protoff, sizeof(u_int8_t), (u_char *) &ilen);
- mi = mo;
-
- /* If it's just the ESP header, just skip to the next mbuf */
- if (mi->m_len == hlen)
+ /* Get crypto descriptors */
+ crp = crypto_getreq(esph && espx ? 2 : 0);
+ if (crp == NULL)
{
- mi = mi->m_next;
- ilen = mi->m_len;
- idat = mtod(mi, u_char *);
- }
- else
- { /* There's data at the end of this mbuf, skip over ESP header */
- ilen = mi->m_len - hlen;
- idat = mtod(mi, u_char *) + hlen;
+ m_freem(m);
+ DPRINTF(("esp_output(): failed to acquire crypto descriptors\n"));
+ espstat.esps_crypto++;
+ return ENOBUFS;
}
- /* Authenticate the ESP header if applicable */
- if (esph)
+ if (espx)
{
- bcopy(tdb->tdb_ictx, &ctx, esph->ctxsize);
- esph->Update(&ctx, mtod(mo, unsigned char *), hlen);
+ 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;
+ if (tdb->tdb_flags & TDBF_HALFIV)
+ crde->crd_flags |= CRD_F_HALFIV;
+ crde->crd_inject = skip + hlen - tdb->tdb_ivlen;
+
+ /* Encryption operation */
+ crde->crd_alg = espx->type;
+ crde->crd_key = tdb->tdb_emxkey;
+ crde->crd_klen = tdb->tdb_emxkeylen * 8;
+ /* XXX Rounds ? */
}
+ else
+ crda = crp->crp_desc;
- /* Encrypt the payload */
- ivp = iv;
- rest = ilen % blks;
- while (plen > 0) /* while not done */
- {
- if (ilen < blks)
- {
- if (rest)
- {
- if (ivp == blk)
- {
- bcopy(blk, iv, blks);
- ivp = iv;
- }
-
- bcopy(idat, blk, rest);
- odat = idat;
- }
+ tdb->tdb_ref++;
- do {
- mi = (mo = mi)->m_next;
- if (mi == NULL)
- {
- DPRINTF(("esp_output(): bad mbuf chain, SA %s/%08x\n",
- ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi)));
- espstat.esps_hdrops++;
- m_freem(m);
- return EINVAL;
- }
- } while (mi->m_len == 0);
-
- if (mi->m_len < blks - rest)
- {
- if ((mi = m_pullup(mi, blks - rest)) == NULL)
- {
- DPRINTF(("esp_output(): m_pullup() failed, SA %s/%08x\n", ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi)));
- m_freem(m);
- espstat.esps_hdrops++;
- return ENOBUFS;
- }
- /*
- * m_pullup was not called at the beginning of the chain
- * but might return a new mbuf, link it into the chain.
- */
- mo->m_next = mi;
- }
-
- ilen = mi->m_len;
- idat = mtod(mi, u_char *);
+ /* Crypto operation descriptor */
+ crp->crp_ilen = m->m_pkthdr.len; /* Total input length */
+ crp->crp_flags = CRYPTO_F_IMBUF;
+ crp->crp_buf = (caddr_t) m;
+ crp->crp_callback = (int (*) (struct cryptop *)) esp_output_cb;
+ crp->crp_opaque1 = (caddr_t) tdb;
+ crp->crp_sid = tdb->tdb_cryptoid;
- if (rest)
- {
- bcopy(idat, blk + rest, blks - rest);
-
- for (i = 0; i < blks; i++)
- blk[i] ^= ivp[i];
+ if (esph)
+ {
+ /* Authentication descriptor */
+ crda->crd_skip = skip;
+ 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 = tdb->tdb_amxkey;
+ crda->crd_klen = tdb->tdb_amxkeylen * 8;
+ }
- espx->encrypt(tdb, blk);
+ return crypto_dispatch(crp);
+}
- if (esph)
- esph->Update(&ctx, blk, blks);
+/*
+ * ESP output callback, called directly by the crypto driver.
+ */
+int
+esp_output_cb(void *op)
+{
+ struct cryptop *crp = (struct cryptop *) op;
+ struct tdb *tdb;
+ struct mbuf *m;
+ int error;
- ivp = blk;
+ tdb = (struct tdb *) crp->crp_opaque1;
+ m = (struct mbuf *) crp->crp_buf;
- bcopy(blk, odat, rest);
- bcopy(blk + rest, idat, blks - rest);
-
- idat += blks - rest;
- ilen -= blks - rest;
- plen -= blks;
- }
+ tdb->tdb_ref--;
- rest = ilen % blks;
- }
+ /* Check for crypto errors */
+ if (crp->crp_etype)
+ {
+ /* Reset session ID */
+ if (tdb->tdb_cryptoid != 0)
+ tdb->tdb_cryptoid = crp->crp_sid;
- while (ilen >= blks && plen > 0)
+ if (crp->crp_etype == EAGAIN)
{
- for (i = 0; i < blks; i++)
- idat[i] ^= ivp[i];
-
- espx->encrypt(tdb, idat);
-
- if (esph)
- esph->Update(&ctx, idat, blks);
-
- ivp = idat;
- idat += blks;
-
- ilen -= blks;
- plen -= blks;
+ tdb->tdb_ref++;
+ return crypto_dispatch(crp);
}
+
+ espstat.esps_noxform++;
+ DPRINTF(("esp_output_cb(): crypto error %d\n", crp->crp_etype));
+ error = crp->crp_etype;
+ goto baddone;
}
- /* Put in authentication data */
- if (esph)
+ /* Shouldn't happen... */
+ if (!m)
{
- esph->Final(auth, &ctx);
- bcopy(tdb->tdb_octx, &ctx, esph->ctxsize);
- esph->Update(&ctx, auth, esph->hashsize);
- esph->Final(auth, &ctx);
+ espstat.esps_crypto++;
+ DPRINTF(("esp_output_cb(): bogus returned buffer from crypto\n"));
+ error = EINVAL;
+ goto baddone;
+ }
- /* Copy the final authenticator -- cheat and use bcopy() again */
- bcopy(auth, pad + padding, alen);
+ /*
+ * Check that the TDB is still valid -- not really an error, but
+ * we need to handle it as such. It may happen if the TDB expired
+ * or was deleted while there was a pending request in the crypto
+ * queue.
+ */
+ if (tdb->tdb_flags & TDBF_INVALID)
+ {
+ espstat.esps_invalid++;
+ tdb_delete(tdb, 0, 0);
+ error = ENXIO;
+ DPRINTF(("esp_output_cb(): TDB expired while processing crypto\n"));
+ goto baddone;
}
-
- /* Save the last encrypted block, to be used as the next IV */
- bcopy(ivp, tdb->tdb_iv, tdb->tdb_ivlen);
- *mp = m;
+ /* Release crypto descriptors */
+ crypto_freereq(crp);
- return 0;
+ /* Call the IPsec input callback */
+ return ipsp_process_done(m, tdb);
+
+ baddone:
+ if (m)
+ m_freem(m);
+
+ crypto_freereq(crp);
+
+ return error;
}
/*
@@ -1057,6 +1032,7 @@ checkreplaywindow32(u_int32_t seq, u_int32_t initial, u_int32_t *lastseq,
espstat.esps_wrap++;
return 2;
}
+
if ((*bitmap) & (((u_int32_t) 1) << diff))
{
espstat.esps_replay++;
@@ -1068,13 +1044,10 @@ checkreplaywindow32(u_int32_t seq, u_int32_t initial, u_int32_t *lastseq,
}
/*
- *
- *
* m_pad(m, n) pads <m> with <n> bytes at the end. The packet header
* length is updated, and a pointer to the first byte of the padding
* (which is guaranteed to be all in one mbuf) is returned. The third
* argument specifies whether we need randompadding or not.
- *
*/
caddr_t
diff --git a/sys/netinet/ip_esp.h b/sys/netinet/ip_esp.h
index 2438f1a8c8d..f7cae5bbe6c 100644
--- a/sys/netinet/ip_esp.h
+++ b/sys/netinet/ip_esp.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: ip_esp.h,v 1.31 2000/01/27 08:09:10 angelos Exp $ */
+/* $OpenBSD: ip_esp.h,v 1.32 2000/03/17 10:25:22 angelos Exp $ */
/*
* The authors of this code are John Ioannidis (ji@tla.org),
@@ -42,7 +42,7 @@
struct espstat
{
- u_int32_t esps_hdrops; /* packet shorter than header shows */
+ u_int32_t esps_hdrops; /* Packet shorter than header shows */
u_int32_t esps_nopf; /* Protocol family not supported */
u_int32_t esps_notdb;
u_int32_t esps_badkcr;
@@ -55,11 +55,12 @@ struct espstat
u_int32_t esps_replay; /* Possible packet replay detected */
u_int32_t esps_input; /* Input ESP packets */
u_int32_t esps_output; /* Output ESP packets */
- u_int32_t esps_invalid; /* Trying to use an invalid TDB */
- u_int64_t esps_ibytes; /* input bytes */
- u_int64_t esps_obytes; /* output bytes */
- u_int32_t esps_toobig; /* packet got larger than IP_MAXPACKET */
- u_int32_t esps_pdrops; /* packet blocked due to policy */
+ u_int32_t esps_invalid; /* Trying to use an invalid TDB */
+ u_int64_t esps_ibytes; /* Input bytes */
+ u_int64_t esps_obytes; /* Output bytes */
+ u_int32_t esps_toobig; /* Packet got larger than IP_MAXPACKET */
+ u_int32_t esps_pdrops; /* Packet blocked due to policy */
+ u_int32_t esps_crypto; /* Crypto processing failure */
};
/*
diff --git a/sys/netinet/ip_ipsp.c b/sys/netinet/ip_ipsp.c
index 97087d12935..db5ea0581f7 100644
--- a/sys/netinet/ip_ipsp.c
+++ b/sys/netinet/ip_ipsp.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: ip_ipsp.c,v 1.79 2000/02/09 04:19:19 itojun Exp $ */
+/* $OpenBSD: ip_ipsp.c,v 1.80 2000/03/17 10:25:22 angelos Exp $ */
/*
* The authors of this code are John Ioannidis (ji@tla.org),
@@ -60,6 +60,7 @@
#include <netinet/in_systm.h>
#include <netinet/ip.h>
#include <netinet/in_pcb.h>
+#include <netinet/ip_var.h>
#endif /* INET */
#ifdef INET6
@@ -74,6 +75,9 @@
#include <netinet/ip_ah.h>
#include <netinet/ip_esp.h>
+#include <crypto/crypto.h>
+#include <crypto/xform.h>
+
#include <dev/rndvar.h>
#ifdef DDB
@@ -112,26 +116,6 @@ struct expclusterlist_head expclusterlist =
TAILQ_HEAD_INITIALIZER(expclusterlist);
struct explist_head explist = TAILQ_HEAD_INITIALIZER(explist);
-u_int8_t hmac_ipad_buffer[64] = {
- 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
- 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
- 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
- 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
- 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
- 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
- 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
- 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36 };
-
-u_int8_t hmac_opad_buffer[64] = {
- 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C,
- 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C,
- 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C,
- 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C,
- 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C,
- 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C,
- 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C,
- 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C };
-
/*
* This is the proper place to define the various encapsulation transforms.
*/
@@ -139,7 +123,7 @@ u_int8_t hmac_opad_buffer[64] = {
struct xformsw xformsw[] = {
{ XF_IP4, 0, "IPv4 Simple Encapsulation",
ipe4_attach, ipe4_init, ipe4_zeroize,
- (struct mbuf * (*)(struct mbuf *, struct tdb *, int, int))ipe4_input,
+ (int (*)(struct mbuf *, struct tdb *, int, int))ipe4_input,
ipip_output, },
{ XF_AH, XFT_AUTH, "IPsec AH",
ah_attach, ah_init, ah_zeroize,
@@ -1121,6 +1105,7 @@ puttdb(struct tdb *tdbp)
}
tdbp->tdb_hnext = tdbh[hashval];
tdbh[hashval] = tdbp;
+ tdbp->tdb_ref++;
tdb_count++;
splx(s);
}
@@ -1181,7 +1166,7 @@ delete_flow(struct flow *flow, struct tdb *tdb, int ingress)
void
tdb_delete(struct tdb *tdbp, int delchain, int expflags)
{
- struct tdb *tdbpp;
+ struct tdb *tdbpp, *tdbpn;
struct inpcb *inp;
u_int32_t hashval = tdbp->tdb_sproto + tdbp->tdb_spi;
int s;
@@ -1211,12 +1196,15 @@ tdb_delete(struct tdb *tdbp, int delchain, int expflags)
{
tdbpp->tdb_hnext = tdbp->tdb_hnext;
tdbpp = tdbp;
+ break;
}
+ tdbp->tdb_hnext = NULL;
+
skip_hash:
/*
* If there was something before us in the chain pointing to us,
- * make it point nowhere
+ * make it point nowhere.
*/
if ((tdbp->tdb_inext) &&
(tdbp->tdb_inext->tdb_onext == tdbp))
@@ -1224,16 +1212,20 @@ tdb_delete(struct tdb *tdbp, int delchain, int expflags)
/*
* If there was something after us in the chain pointing to us,
- * make it point nowhere
+ * make it point nowhere.
*/
if ((tdbp->tdb_onext) &&
(tdbp->tdb_onext->tdb_inext == tdbp))
tdbp->tdb_onext->tdb_inext = NULL;
- tdbpp = tdbp->tdb_onext;
-
+ tdbpn = tdbp->tdb_onext;
+ tdbp->tdb_inext = tdbp->tdb_onext = NULL;
+
if (tdbp->tdb_xform)
- (*(tdbp->tdb_xform->xf_zeroize))(tdbp);
+ {
+ (*(tdbp->tdb_xform->xf_zeroize))(tdbp);
+ tdbp->tdb_xform = NULL;
+ }
while (tdbp->tdb_access)
delete_flow(tdbp->tdb_access, tdbp, FLOW_INGRESS);
@@ -1326,6 +1318,7 @@ tdb_delete(struct tdb *tdbp, int delchain, int expflags)
TAILQ_REMOVE(&tdbpp->tdb_bind_in, tdbpp, tdb_bind_in_next);
tdbpp->tdb_bind_out = NULL;
}
+
/* Cleanup inp references */
for (inp = TAILQ_FIRST(&tdbp->tdb_inp); inp;
inp = TAILQ_FIRST(&tdbp->tdb_inp))
@@ -1346,20 +1339,32 @@ tdb_delete(struct tdb *tdbp, int delchain, int expflags)
}
if (tdbp->tdb_srcid)
- FREE(tdbp->tdb_srcid, M_XDATA);
+ {
+ FREE(tdbp->tdb_srcid, M_XDATA);
+ tdbp->tdb_srcid = NULL;
+ }
if (tdbp->tdb_dstid)
- FREE(tdbp->tdb_dstid, M_XDATA);
+ {
+ FREE(tdbp->tdb_dstid, M_XDATA);
+ tdbp->tdb_dstid = NULL;
+ }
/* If we're deleting the bypass tdb, reset the variable. */
if (tdbp == tdb_bypass)
tdb_bypass = NULL;
- FREE(tdbp, M_TDB);
- tdb_count--;
+ /* Don't always delete TDBs as they may be referenced by something else */
+ if (--tdbp->tdb_ref <= 0)
+ {
+ FREE(tdbp, M_TDB);
+ tdb_count--;
+ }
+ else
+ tdbp->tdb_flags |= TDBF_INVALID;
- if (delchain && tdbpp)
- tdb_delete(tdbpp, delchain, expflags);
+ if (delchain && tdbpn)
+ tdb_delete(tdbpn, delchain, expflags);
splx(s);
}
@@ -1520,6 +1525,11 @@ ipsp_kern(int off, char **bufp, int len)
l += sprintf(buffer + l, ">\n");
}
+ l += sprintf(buffer + l, "\tCrypto ID: %qu\n", tdb->tdb_cryptoid);
+
+ l += sprintf(buffer + l, "\tCurrently referenced %d time%s\n",
+ tdb->tdb_ref, tdb->tdb_ref == 1 ? "" : "s");
+
if (tdb->tdb_xform)
l += sprintf(buffer + l, "\txform = <%s>\n",
tdb->tdb_xform->xf_name);
@@ -1714,221 +1724,285 @@ ipsp_address(union sockaddr_union sa)
* place.
*/
int
-ipsp_process_packet(struct mbuf *m, struct mbuf **mp, struct tdb *tdb, int *af,
- int tunalready)
+ipsp_process_packet(struct mbuf *m, struct tdb *tdb, int af, int tunalready)
{
- int i, error, off;
- struct tdb *t;
+ int i, off, error;
+ struct mbuf *mp;
#ifdef INET
struct ip *ip;
#endif /* INET */
-
#ifdef INET6
struct ip6_hdr *ip6;
#endif /* INET6 */
- for (t = tdb; t != NULL; t = t->tdb_onext)
- if ((t->tdb_sproto == IPPROTO_ESP && !esp_enable) ||
- (t->tdb_sproto == IPPROTO_AH && !ah_enable))
- {
- DPRINTF(("ipsp_process_packet(): IPSec outbound packet dropped due to policy\n"));
- m_freem(m);
- return EHOSTUNREACH;
- }
+ /* Check that the transform is allowed by the administrator */
+ if ((tdb->tdb_sproto == IPPROTO_ESP && !esp_enable) ||
+ (tdb->tdb_sproto == IPPROTO_AH && !ah_enable))
+ {
+ DPRINTF(("ipsp_process_packet(): IPSec outbound packet dropped due to policy\n"));
+ m_freem(m);
+ return EHOSTUNREACH;
+ }
- while (tdb && tdb->tdb_xform)
+ /* Sanity check */
+ if (!tdb->tdb_xform)
{
- /* Check if the SPI is invalid */
- if (tdb->tdb_flags & TDBF_INVALID)
- {
- DPRINTF(("ipsp_process_packet(): attempt to use invalid SA %s/%08x/%u\n", ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi), tdb->tdb_sproto));
- m_freem(m);
- return ENXIO;
- }
+ DPRINTF(("ipsp_process_packet(): uninitialized TDB\n"));
+ m_freem(m);
+ return EHOSTUNREACH;
+ }
-#ifndef INET6
- /* Sanity check */
- if (tdb->tdb_dst.sa.sa_family != AF_INET)
- {
- DPRINTF(("ipsp_process_packet(): attempt to use SA %s/%08x/%u for protocol family %d\n", ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi), tdb->tdb_sproto, tdb->tdb_dst.sa.sa_family));
- m_freem(m);
- return ENXIO;
- }
+ /* Check if the SPI is invalid */
+ if (tdb->tdb_flags & TDBF_INVALID)
+ {
+ DPRINTF(("ipsp_process_packet(): attempt to use invalid SA %s/%08x/%u\n", ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi), tdb->tdb_sproto));
+ m_freem(m);
+ return ENXIO;
+ }
+
+ /* Check that the network protocol is supported */
+ switch (tdb->tdb_dst.sa.sa_family)
+ {
+#ifdef INET
+ case AF_INET:
+ break;
+#endif /* INET */
+
+#ifdef INET6
+ case AF_INET6:
+ break;
#endif /* INET6 */
-#ifndef INET
- /* Sanity check */
- if (tdb->tdb_dst.sa.sa_family != AF_INET6)
- {
+ default:
DPRINTF(("ipsp_process_packet(): attempt to use SA %s/%08x/%u for protocol family %d\n", ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi), tdb->tdb_sproto, tdb->tdb_dst.sa.sa_family));
m_freem(m);
return ENXIO;
- }
-#endif /* INET */
+ }
- /* Register first use, setup expiration timer */
- if (tdb->tdb_first_use == 0)
- {
- tdb->tdb_first_use = time.tv_sec;
- tdb_expiration(tdb, TDBEXP_TIMEOUT);
- }
+ /* Register first use if applicable, setup relevant expiration timer */
+ if (tdb->tdb_first_use == 0)
+ {
+ tdb->tdb_first_use = time.tv_sec;
+ tdb_expiration(tdb, TDBEXP_TIMEOUT);
+ }
- /* Check for tunneling if we don't have the first header in place */
- if (tunalready == 0)
+ /*
+ * Check for tunneling if we don't have the first header in place.
+ * When doing Ethernet-over-IP, we are handed an already-encapsulated
+ * frame, so we don't need to re-encapsulate.
+ */
+ if (tunalready == 0)
+ {
+ /*
+ * If the target protocol family is different, we know we'll be
+ * doing tunneling.
+ */
+ if (af == tdb->tdb_dst.sa.sa_family)
{
- if ((*af) == tdb->tdb_dst.sa.sa_family)
- {
#ifdef INET
- if ((*af) == AF_INET)
- i = sizeof(struct ip);
+ if (af == AF_INET)
+ i = sizeof(struct ip);
#endif /* INET */
#ifdef INET6
- if ((*af) == AF_INET6)
- i = sizeof(struct ip6_hdr);
+ if (af == AF_INET6)
+ i = sizeof(struct ip6_hdr);
#endif /* INET6 */
- if (m->m_len < i)
- {
- if ((m = m_pullup(m, i)) == 0)
- return ENOBUFS;
- }
+ /* Bring the network header in the first mbuf */
+ if (m->m_len < i)
+ {
+ if ((m = m_pullup(m, i)) == 0)
+ return ENOBUFS;
+ }
#ifdef INET
- ip = mtod(m, struct ip *);
+ ip = mtod(m, struct ip *);
#endif /* INET */
#ifdef INET6
- ip6 = mtod(m, struct ip6_hdr *);
+ ip6 = mtod(m, struct ip6_hdr *);
#endif /* INET6 */
- }
+ }
- if ((tdb->tdb_dst.sa.sa_family != (*af)) ||
- ((tdb->tdb_flags & TDBF_TUNNELING) &&
- (tdb->tdb_xform->xf_type != XF_IP4)) ||
+ /* Do the appropriate encapsulation, if necessary */
+ if ((tdb->tdb_dst.sa.sa_family != af) || /* PF mismatch */
+ (tdb->tdb_flags & TDBF_TUNNELING) || /* Tunneling requested */
+ (tdb->tdb_xform->xf_type == XF_IP4) || /* ditto */
#ifdef INET
- ((tdb->tdb_dst.sa.sa_family == AF_INET) &&
- (tdb->tdb_dst.sin.sin_addr.s_addr != INADDR_ANY) &&
- (tdb->tdb_dst.sin.sin_addr.s_addr != ip->ip_dst.s_addr)) ||
+ ((tdb->tdb_dst.sa.sa_family == AF_INET) &&
+ (tdb->tdb_dst.sin.sin_addr.s_addr != INADDR_ANY) &&
+ (tdb->tdb_dst.sin.sin_addr.s_addr != ip->ip_dst.s_addr)) ||
#endif /* INET */
#ifdef INET6
- ((tdb->tdb_dst.sa.sa_family == AF_INET6) &&
- (!IN6_IS_ADDR_UNSPECIFIED(&tdb->tdb_dst.sin6.sin6_addr)) &&
- (!IN6_ARE_ADDR_EQUAL(&tdb->tdb_dst.sin6.sin6_addr,
- &ip6->ip6_dst))) ||
+ ((tdb->tdb_dst.sa.sa_family == AF_INET6) &&
+ (!IN6_IS_ADDR_UNSPECIFIED(&tdb->tdb_dst.sin6.sin6_addr)) &&
+ (!IN6_ARE_ADDR_EQUAL(&tdb->tdb_dst.sin6.sin6_addr,
+ &ip6->ip6_dst))) ||
#endif /* INET6 */
- 0)
- {
+ 0)
+ {
#ifdef INET
- /* Fix IPv4 header checksum and length */
- if ((*af) == AF_INET)
- {
- ip->ip_len = htons(m->m_pkthdr.len);
- ip->ip_sum = in_cksum(m, ip->ip_hl << 2);
- }
+ /* Fix IPv4 header checksum and length */
+ if (af == AF_INET)
+ {
+ if ((m = m_pullup(m, sizeof(struct ip))) == 0)
+ return ENOBUFS;
+
+ ip = mtod(m, struct ip *);
+ ip->ip_len = htons(m->m_pkthdr.len);
+ ip->ip_sum = in_cksum(m, ip->ip_hl << 2);
+ }
#endif /* INET */
#ifdef INET6
- /* Fix IPv6 header payload length */
- if ((*af) == AF_INET6)
- ip6->ip6_plen = htons(m->m_pkthdr.len);
+ /* Fix IPv6 header payload length */
+ if (af == AF_INET6)
+ {
+ if ((m = m_pullup(m, sizeof(struct ip6_hdr))) == 0)
+ return ENOBUFS;
+
+ ip6 = mtod(m, struct ip6_hdr *);
+ ip6->ip6_plen = htons(m->m_pkthdr.len);
+ }
#endif /* INET6 */
- /* Encapsulate -- the last two arguments are unused */
- error = ipip_output(m, tdb, mp, 0, 0);
- if (((*mp) == NULL) && (!error))
- error = EFAULT;
- if (error)
+ /* Encapsulate -- the last two arguments are unused */
+ error = ipip_output(m, tdb, &mp, 0, 0);
+ if ((mp == NULL) && (!error))
+ error = EFAULT;
+ if (error)
+ {
+ if (mp)
{
- if (*mp)
- {
- m_freem(*mp);
- *mp = NULL;
- }
- return error;
+ m_freem(mp);
+ mp = NULL;
}
- *af = tdb->tdb_dst.sa.sa_family;
- m = *mp;
- *mp = NULL;
+ return error;
}
- }
- else
- {
- tunalready = 0;
- if (tdb->tdb_xform->xf_type == XF_IP4)
- continue;
+ m = mp;
+ mp = NULL;
}
-
+
+ /* We may be done with this TDB */
+ if (tdb->tdb_xform->xf_type == XF_IP4)
+ return ipsp_process_done(m, tdb);
+ }
+ else
+ {
+ /*
+ * If this is just an IP-IP TDB and we're told there's already an
+ * encapsulation header, move on.
+ */
+ if (tdb->tdb_xform->xf_type == XF_IP4)
+ return ipsp_process_done(m, tdb);
+ }
+
+ /* Extract some information off the headers */
+ switch (tdb->tdb_dst.sa.sa_family)
+ {
#ifdef INET
- if (tdb->tdb_dst.sa.sa_family == AF_INET)
- {
+ case AF_INET:
ip = mtod(m, struct ip *);
i = ip->ip_hl << 2;
off = offsetof(struct ip, ip_p);
-
- if (tdb->tdb_xform->xf_type == XF_IP4)
- {
- ip->ip_len = htons(m->m_pkthdr.len);
- ip->ip_sum = in_cksum(m, i);
- }
- }
+ break;
#endif /* INET */
#ifdef INET6
- if (tdb->tdb_dst.sa.sa_family == AF_INET6)
- {
+ case AF_INET6:
ip6 = mtod(m, struct ip6_hdr *);
i = sizeof(struct ip6_hdr);
off = offsetof(struct ip6_hdr, ip6_nxt);
- ip6->ip6_plen = htons(m->m_pkthdr.len);
- }
+ break;
#endif /* INET6 */
-
- error = (*(tdb->tdb_xform->xf_output))(m, tdb, mp, i, off);
- if ((*mp) == NULL)
- error = EFAULT;
- if (error)
- {
- if (*mp)
- m_freem(*mp);
- return error;
- }
+ }
- m = *mp;
- *mp = NULL;
- tdb = tdb->tdb_onext;
+ /* Invoke the IPsec transform */
+ return (*(tdb->tdb_xform->xf_output))(m, tdb, NULL, i, off);
+}
+/*
+ * Called by the IPsec output transform callbacks, to transmit the packet
+ * or do further processing, as necessary.
+ */
+int
+ipsp_process_done(struct mbuf *m, struct tdb *tdb)
+{
#ifdef INET
- /* Fix the header length, for AH processing */
- if ((*af) == AF_INET)
- {
- ip = mtod(m, struct ip *);
- ip->ip_len = htons(m->m_pkthdr.len);
- }
+ struct ip *ip;
#endif /* INET */
#ifdef INET6
- /* Fix the header length, for AH processing */
- if ((*af) == AF_INET6)
- {
- ip6 = mtod(m, struct ip6_hdr *);
- ip6->ip6_plen = htons(m->m_pkthdr.len);
- }
+ struct ip6_hdr *ip6;
#endif /* INET6 */
- }
-#ifdef INET
- /* Fix checksum */
- if ((*af) == AF_INET)
+ switch (tdb->tdb_dst.sa.sa_family)
{
- ip = mtod(m, struct ip *);
- ip->ip_sum = in_cksum(m, ip->ip_hl << 2);
+#ifdef INET
+ case AF_INET:
+ /* Fix the header length, for AH processing */
+ if (tdb->tdb_dst.sa.sa_family == AF_INET)
+ {
+ ip = mtod(m, struct ip *);
+ ip->ip_len = htons(m->m_pkthdr.len);
+ }
+ break;
+#endif /* INET */
+
+#ifdef INET6
+ case AF_INET6:
+ /* Fix the header length, for AH processing */
+ if (tdb->tdb_dst.sa.sa_family == AF_INET6)
+ {
+ ip6 = mtod(m, struct ip6_hdr *);
+ ip6->ip6_plen = htons(m->m_pkthdr.len);
+ }
+ break;
+#endif /* INET6 */
+
+ default:
+ m_freem(m);
+ DPRINTF(("ipsp_process_done(): unknown protocol family (%d)\n",
+ tdb->tdb_dst.sa.sa_family));
+ return ENXIO;
}
+
+ /* If there's another TDB to apply, do so. */
+ if (tdb->tdb_onext)
+ return ipsp_process_packet(m, tdb->tdb_onext,
+ tdb->tdb_onext->tdb_dst.sa.sa_family, 0);
+
+ /*
+ * If we're done with IPsec processing, transmit the packet using the
+ * appropriate network protocol (IP or IPv6).
+ */
+ switch (tdb->tdb_dst.sa.sa_family)
+ {
+#ifdef INET
+ case AF_INET:
+ NTOHS(ip->ip_len);
+ NTOHS(ip->ip_off);
+ ip->ip_sum = in_cksum(m, ip->ip_hl << 2); /* Fix checksum */
+
+ return ip_output(m, NULL, NULL, IP_ENCAPSULATED | IP_RAWOUTPUT,
+ NULL, NULL);
#endif /* INET */
- *mp = m;
- return 0;
+#ifdef INET6
+ case AF_INET6:
+ ip6 = mtod(m, struct ip6_hdr *);
+ NTOHS(ip6->ip6_plen);
+
+ /* XXX ip6_output() has to honor those two flags... */
+ return ip6_output(m, NULL, NULL, IP_ENCAPSULATED | IP_RAWOUTPUT,
+ NULL, NULL);
+#endif /* INET6 */
+ }
+
+ /* Not reached */
+ return EINVAL;
}
diff --git a/sys/netinet/ip_ipsp.h b/sys/netinet/ip_ipsp.h
index 7c3feb7d986..5695fc19692 100644
--- a/sys/netinet/ip_ipsp.h
+++ b/sys/netinet/ip_ipsp.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: ip_ipsp.h,v 1.61 2000/02/28 23:13:07 deraadt Exp $ */
+/* $OpenBSD: ip_ipsp.h,v 1.62 2000/03/17 10:25:22 angelos Exp $ */
/*
* The authors of this code are John Ioannidis (ji@tla.org),
@@ -47,12 +47,6 @@
#include <sys/types.h>
#include <sys/queue.h>
#include <netinet/in.h>
-#include <sys/md5k.h>
-#include <crypto/sha1.h>
-#include <crypto/rmd160.h>
-#include <crypto/blf.h>
-#include <crypto/cast.h>
-#include <crypto/skipjack.h>
union sockaddr_union
{
@@ -69,33 +63,10 @@ union sockaddr_union
#define SHA1HMAC96_KEYSIZE 20
#define RIPEMD160HMAC96_KEYSIZE 20
-/* IV lengths */
-#define ESP_DES_IVS 8
-#define ESP_3DES_IVS 8
-#define ESP_BLF_IVS 8
-#define ESP_CAST_IVS 8
-#define ESP_SKIPJACK_IVS 8
-#define ESP_MAX_IVS 8 /* Keep updated */
-
-/* Block sizes -- it is assumed that they're powers of 2 */
-#define ESP_DES_BLKS 8
-#define ESP_3DES_BLKS 8
-#define ESP_BLF_BLKS 8
-#define ESP_CAST_BLKS 8
-#define ESP_SKIPJACK_BLKS 8
-#define ESP_MAX_BLKS 8 /* Keep updated */
-
-#define HMAC_BLOCK_LEN 64
-
#define AH_HMAC_HASHLEN 12 /* 96 bits of authenticator */
#define AH_HMAC_RPLENGTH 4 /* 32 bits of replay counter */
#define AH_HMAC_INITIAL_RPL 1 /* Replay counter initial value */
-/* HMAC definitions */
-#define HMAC_IPAD_VAL 0x36
-#define HMAC_OPAD_VAL 0x5C
-#define HMAC_BLOCK_LEN 64
-
/* Authenticator lengths */
#define AH_MD5_ALEN 16
#define AH_SHA1_ALEN 20
@@ -298,7 +269,9 @@ struct tdb /* tunnel descriptor block */
u_int64_t tdb_soft_first_use; /* Soft warning */
u_int64_t tdb_exp_first_use; /* Expire if tdb_first_use +
* tdb_exp_first_use <= curtime */
+ u_int64_t tdb_cryptoid; /* Crypto session ID */
+ u_int32_t tdb_ref; /* References */
u_int32_t tdb_spi; /* SPI */
u_int16_t tdb_amxkeylen; /* Raw authentication key length */
u_int16_t tdb_emxkeylen; /* Raw encryption key length */
@@ -311,24 +284,11 @@ struct tdb /* tunnel descriptor block */
union sockaddr_union tdb_src; /* Source address for this SA */
union sockaddr_union tdb_proxy;
- u_int8_t *tdb_key; /* Key material (schedules) */
- u_int8_t *tdb_ictx; /* Authentication contexts */
- u_int8_t *tdb_octx;
u_int8_t *tdb_srcid; /* Source ID for this SA */
u_int8_t *tdb_dstid; /* Destination ID for this SA */
u_int8_t *tdb_amxkey; /* Raw authentication key */
u_int8_t *tdb_emxkey; /* Raw encryption key */
- union
- {
- u_int8_t Iv[ESP_3DES_IVS]; /* That's enough space */
- u_int32_t Ivl; /* Make sure this is 4 bytes */
- u_int64_t Ivq; /* Make sure this is 8 bytes! */
- }IV;
-#define tdb_iv IV.Iv
-#define tdb_ivl IV.Ivl
-#define tdb_ivq IV.Ivq
-
u_int32_t tdb_rpl; /* Replay counter */
u_int32_t tdb_bitmap; /* Used for replay sliding window */
u_int32_t tdb_initial; /* Initial replay value */
@@ -349,47 +309,12 @@ struct tdb /* tunnel descriptor block */
TAILQ_HEAD(tdb_inp_head, inpcb) tdb_inp;
};
-union authctx_old {
- MD5_CTX md5ctx;
- SHA1_CTX sha1ctx;
-};
-
-union authctx {
- MD5_CTX md5ctx;
- SHA1_CTX sha1ctx;
- RMD160_CTX rmd160ctx;
-};
-
struct tdb_ident {
u_int32_t spi;
union sockaddr_union dst;
u_int8_t proto;
};
-struct auth_hash {
- int type;
- char *name;
- u_int16_t keysize;
- u_int16_t hashsize;
- u_int16_t authsize;
- u_int16_t ctxsize;
- void (*Init)(void *);
- void (*Update)(void *, u_int8_t *, u_int16_t);
- void (*Final)(u_int8_t *, void *);
-};
-
-struct enc_xform {
- int type;
- char *name;
- u_int16_t blocksize, ivsize;
- u_int16_t minkey, maxkey;
- u_int32_t ivmask; /* Or all possible modes, zero iv = 1 */
- void (*encrypt)(struct tdb *, u_int8_t *);
- void (*decrypt)(struct tdb *, u_int8_t *);
- void (*setkey)(u_int8_t **, u_int8_t *, int len);
- void (*zerokey)(u_int8_t **);
-};
-
struct ipsecinit
{
u_int8_t *ii_enckey;
@@ -408,7 +333,7 @@ struct xformsw
int (*xf_attach)(void); /* called at config time */
int (*xf_init)(struct tdb *, struct xformsw *, struct ipsecinit *);
int (*xf_zeroize)(struct tdb *); /* termination */
- struct mbuf *(*xf_input)(struct mbuf *, struct tdb *, int, int); /* input */
+ int (*xf_input)(struct mbuf *, struct tdb *, int, int); /* input */
int (*xf_output)(struct mbuf *, struct tdb *, struct mbuf **, int, int); /* output */
};
@@ -463,8 +388,6 @@ extern int ipsec_keep_invalid;
extern int ipsec_in_use;
extern int ipsec_require_pfs;
-extern u_int8_t hmac_ipad_buffer[64];
-extern u_int8_t hmac_opad_buffer[64];
extern int ipsec_soft_allocations;
extern int ipsec_exp_allocations;
extern int ipsec_soft_bytes;
@@ -488,6 +411,7 @@ extern struct auth_hash auth_hash_hmac_ripemd_160_96;
extern TAILQ_HEAD(expclusterlist_head, tdb) expclusterlist;
extern TAILQ_HEAD(explist_head, tdb) explist;
+
extern struct xformsw xformsw[], *xformswNXFORMSW;
/* Check if a given tdb has encryption, authentication and/or tunneling */
@@ -571,34 +495,41 @@ extern void etherip_input __P((struct mbuf *, ...));
extern int ah_attach(void);
extern int ah_init(struct tdb *, struct xformsw *, struct ipsecinit *);
extern int ah_zeroize(struct tdb *);
-extern int ah_output(struct mbuf *, struct tdb *, struct mbuf **,
- int, int);
-extern struct mbuf *ah_input(struct mbuf *, struct tdb *, int, int);
+extern int ah_output(struct mbuf *, struct tdb *, struct mbuf **, int, int);
+extern int ah_output_cb(void *);
+extern int ah_input(struct mbuf *, struct tdb *, int, int);
+extern int ah_input_cb(void *);
extern int ah_sysctl(int *, u_int, void *, size_t *, void *, size_t);
+extern int ah_massage_headers(struct mbuf **, int, int, int, int);
#ifdef INET
extern void ah4_input __P((struct mbuf *, ...));
+extern int ah4_input_cb __P((struct mbuf *, ...));
#endif /* INET */
#ifdef INET6
-int ah6_input __P((struct mbuf **, int *, int));
+extern int ah6_input __P((struct mbuf **, int *, int));
+extern int ah6_input_cb __P((struct mbuf *, int));
#endif /* INET6 */
/* XF_ESP */
extern int esp_attach(void);
extern int esp_init(struct tdb *, struct xformsw *, struct ipsecinit *);
extern int esp_zeroize(struct tdb *);
-extern int esp_output(struct mbuf *, struct tdb *, struct mbuf **,
- int, int);
-extern struct mbuf *esp_input(struct mbuf *, struct tdb *, int, int);
+extern int esp_output(struct mbuf *, struct tdb *, struct mbuf **, int, int);
+extern int esp_output_cb(void *);
+extern int esp_input(struct mbuf *, struct tdb *, int, int);
+extern int esp_input_cb(void *);
extern int esp_sysctl(int *, u_int, void *, size_t *, void *, size_t);
#ifdef INET
extern void esp4_input __P((struct mbuf *, ...));
+extern int esp4_input_cb __P((struct mbuf *, ...));
#endif /* INET */
#ifdef INET6
-int esp6_input __P((struct mbuf **, int *, int));
+extern int esp6_input __P((struct mbuf **, int *, int));
+extern int esp6_input_cb __P((struct mbuf *, int));
#endif /* INET6 */
/* XF_TCPSIGNATURE */
@@ -606,7 +537,8 @@ extern int tcp_signature_tdb_attach __P((void));
extern int tcp_signature_tdb_init __P((struct tdb *, struct xformsw *,
struct ipsecinit *));
extern int tcp_signature_tdb_zeroize __P((struct tdb *));
-extern struct mbuf *tcp_signature_tdb_input __P((struct mbuf *, struct tdb *, int, int));
+extern int tcp_signature_tdb_input __P((struct mbuf *, struct tdb *, int,
+ int));
extern int tcp_signature_tdb_output __P((struct mbuf *, struct tdb *,
struct mbuf **, int, int));
@@ -620,7 +552,8 @@ extern int checkreplaywindow32(u_int32_t, u_int32_t, u_int32_t *, u_int32_t,
extern unsigned char ipseczeroes[];
/* Packet processing */
-int ipsp_process_packet(struct mbuf *, struct mbuf **, struct tdb *,
- int *, int);
+extern int ipsp_process_packet(struct mbuf *, struct tdb *, int, int);
+extern int ipsp_process_done(struct mbuf *, struct tdb *);
+extern int ipsec_common_input_cb(struct mbuf *, struct tdb *, int, int);
#endif /* _KERNEL */
#endif /* _NETINET_IPSP_H_ */
diff --git a/sys/netinet/ip_output.c b/sys/netinet/ip_output.c
index e4fb895561a..605f1e50885 100644
--- a/sys/netinet/ip_output.c
+++ b/sys/netinet/ip_output.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: ip_output.c,v 1.64 2000/01/11 03:10:04 angelos Exp $ */
+/* $OpenBSD: ip_output.c,v 1.65 2000/03/17 10:25:22 angelos Exp $ */
/* $NetBSD: ip_output.c,v 1.28 1996/02/13 23:43:07 christos Exp $ */
/*
@@ -130,20 +130,13 @@ ip_output(m0, va_alist)
va_list ap;
#ifdef IPSEC
union sockaddr_union sunion;
- struct mbuf *mp;
- struct udphdr *udp;
- struct tcphdr *tcp;
struct inpcb *inp;
struct route_enc re0, *re = &re0;
struct sockaddr_encap *ddst, *gw;
u_int8_t sa_require, sa_have = 0;
- int s, protoflag = AF_INET;
+ int s;
struct tdb *tdb, tdb2;
-
-#ifdef INET6
- struct ip6_hdr *ip6;
-#endif /* INET6 */
#endif /* IPSEC */
va_start(ap, m0);
@@ -385,12 +378,19 @@ sendit:
goto have_tdb;
}
+ /*
+ * If there are no flows in place, there's no point
+ * continuing with the SPD lookup.
+ */
if (!ipsec_in_use) {
splx(s);
goto no_encap;
}
- /* Do an SPD lookup */
+ /*
+ * Do an SPD lookup -- this code should probably be moved
+ * to a separate function.
+ */
ddst = (struct sockaddr_encap *) &re->re_dst;
ddst->sen_family = PF_KEY;
ddst->sen_len = SENT_IP4_LEN;
@@ -399,36 +399,27 @@ sendit:
ddst->sen_ip_dst = ip->ip_dst;
ddst->sen_proto = ip->ip_p;
+ /* If TCP/UDP, extract the port numbers to use in the lookup */
switch (ip->ip_p) {
case IPPROTO_UDP:
- if (m->m_len < hlen + 2 * sizeof(u_int16_t)) {
- if ((m = m_pullup(m, hlen + 2 *
- sizeof(u_int16_t))) == 0)
- return ENOBUFS;
- ip = mtod(m, struct ip *);
- }
- udp = (struct udphdr *) (mtod(m, u_char *) + hlen);
- ddst->sen_sport = udp->uh_sport;
- ddst->sen_dport = udp->uh_dport;
- break;
-
case IPPROTO_TCP:
- if (m->m_len < hlen + 2 * sizeof(u_int16_t)) {
- if ((m = m_pullup(m, hlen + 2 *
- sizeof(u_int16_t))) == 0)
- return ENOBUFS;
- ip = mtod(m, struct ip *);
- }
- tcp = (struct tcphdr *) (mtod(m, u_char *) + hlen);
- ddst->sen_sport = tcp->th_sport;
- ddst->sen_dport = tcp->th_dport;
- break;
+ /*
+ * Luckily, the offset of the src/dst ports in both the UDP
+ * and TCP headers is the same (first two 16-bit values
+ * in the respective headers), so we can just copy them.
+ */
+ m_copydata(m, hlen, sizeof(u_int16_t),
+ (caddr_t) &ddst->sen_sport);
+ m_copydata(m, hlen + sizeof(u_int16_t), sizeof(u_int16_t),
+ (caddr_t) &ddst->sen_dport);
+ break;
default:
ddst->sen_sport = 0;
ddst->sen_dport = 0;
}
+ /* Actual SPD lookup */
rtalloc((struct route *) re);
if (re->re_rt == NULL) {
splx(s);
@@ -496,6 +487,7 @@ sendit:
}
#endif /* INET6 */
+ /* Lookup in the TDB table */
tdb = (struct tdb *) gettdb(gw->sen_ipsp_spi, &sunion,
gw->sen_ipsp_sproto);
@@ -524,7 +516,7 @@ sendit:
sa_require |= NOTIFY_SATYPE_TUNNEL;
}
- /* Check for PFS */
+ /* Check whether Perfect Forward Secrect is required */
if (ipsec_require_pfs)
tdb->tdb_flags |= TDBF_PFS;
else
@@ -637,21 +629,6 @@ sendit:
have_tdb:
- ip->ip_len = htons((u_short) ip->ip_len);
- ip->ip_off = htons((u_short) ip->ip_off);
- ip->ip_sum = 0;
-
- /*
- * Now we check if this tdb has all the transforms which
- * are requried by the socket or our default policy.
- */
- SPI_CHAIN_ATTRIB(sa_have, tdb_onext, tdb);
-
- if (sa_require & ~sa_have) {
- splx(s);
- goto no_encap;
- }
-
if (tdb == NULL) {
splx(s);
if (gw->sen_type == SENT_IPSP)
@@ -662,63 +639,40 @@ sendit:
DPRINTF(("ip_output(): non-existant TDB for SA %s/%08x/%u\n", inet6_ntoa4(gw->sen_ipsp6_dst), ntohl(gw->sen_ipsp6_spi), gw->sen_ipsp6_sproto));
#endif /* INET6 */
- if (re->re_rt)
- RTFREE(re->re_rt);
error = EHOSTUNREACH;
m_freem(m);
goto done;
}
- error = ipsp_process_packet(m, &mp, tdb, &protoflag, 0);
- if ((mp == NULL) && (!error))
- error = ENOBUFS;
- if (error) {
- if (re->re_rt)
- RTFREE(re->re_rt);
- if (mp)
- m_freem(mp);
- goto done;
- }
-
- m = mp;
- mp = NULL;
-
- splx(s);
-
- /*
- * At this point, m is pointing to an mbuf chain with the
- * processed packet. Call ourselves recursively, but
- * bypass the encap code.
- */
+ /* We don't need this anymore */
if (re->re_rt)
RTFREE(re->re_rt);
- if (protoflag == AF_INET) {
- ip = mtod(m, struct ip *);
- NTOHS(ip->ip_len);
- NTOHS(ip->ip_off);
+ /* Massage the IP header for use by the IPsec code */
+ ip->ip_len = htons((u_short) ip->ip_len);
+ ip->ip_off = htons((u_short) ip->ip_off);
+ ip->ip_sum = 0;
- return ip_output(m, NULL, NULL,
- IP_ENCAPSULATED | IP_RAWOUTPUT,
- NULL, NULL);
+ /*
+ * Now we check if this tdb has all the transforms which
+ * are required by the socket or our default policy.
+ */
+ SPI_CHAIN_ATTRIB(sa_have, tdb_onext, tdb);
+ if (sa_require & ~sa_have) {
+ splx(s);
+ goto no_encap;
}
-#ifdef INET6
- if (protoflag == AF_INET6) {
- ip6 = mtod(m, struct ip6_hdr *);
- NTOHS(ip6->ip6_plen);
-
- /* Naturally, ip6_output() has to honor those two flags */
- return ip6_output(m, NULL, NULL,
- IP_ENCAPSULATED | IP_RAWOUTPUT,
- NULL, NULL);
- }
-#endif /* INET6 */
+ /* Callee frees mbuf */
+ error = ipsp_process_packet(m, tdb, AF_INET, 0);
+ splx(s);
+ return error; /* Nothing more to be done */
no_encap:
/* This is for possible future use, don't move or delete */
if (re->re_rt)
RTFREE(re->re_rt);
+
/* No IPSec processing though it was required, drop packet */
if (sa_require) {
error = EHOSTUNREACH;
diff --git a/sys/netinet/ip_xform.c b/sys/netinet/ip_xform.c
deleted file mode 100644
index 144dc098356..00000000000
--- a/sys/netinet/ip_xform.c
+++ /dev/null
@@ -1,341 +0,0 @@
-/* $OpenBSD: ip_xform.c,v 1.6 2000/03/10 03:51:59 deraadt 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).
- *
- * 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.
- *
- * Permission to use, copy, and modify this software 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.
- */
-
-/*
- * Encapsulation Security Payload Processing
- * Per RFC1827 (Atkinson, 1995)
- */
-
-#include <sys/param.h>
-#include <sys/systm.h>
-#include <sys/malloc.h>
-#include <sys/mbuf.h>
-#include <sys/domain.h>
-#include <sys/protosw.h>
-#include <sys/socket.h>
-#include <sys/sysctl.h>
-#include <sys/errno.h>
-#include <sys/time.h>
-#include <sys/kernel.h>
-#include <machine/cpu.h>
-
-#include <net/if.h>
-#include <net/route.h>
-#include <net/netisr.h>
-#include <net/bpf.h>
-
-#include <netinet/in.h>
-#include <netinet/in_systm.h>
-#include <netinet/ip.h>
-#include <netinet/in_pcb.h>
-#include <netinet/in_var.h>
-#include <netinet/ip_var.h>
-
-#include <sys/socketvar.h>
-#include <net/raw_cb.h>
-
-#include <netinet/ip_icmp.h>
-#include <netinet/ip_ipsp.h>
-#include <netinet/ip_esp.h>
-#include <net/pfkeyv2.h>
-#include <net/if_enc.h>
-
-extern void des_ecb3_encrypt(caddr_t, caddr_t, caddr_t, caddr_t, caddr_t, int);
-extern void des_ecb_encrypt(caddr_t, caddr_t, caddr_t, int);
-extern void des_set_key(caddr_t, caddr_t);
-
-static void des1_encrypt(struct tdb *, u_int8_t *);
-static void des3_encrypt(struct tdb *, u_int8_t *);
-static void blf_encrypt(struct tdb *, u_int8_t *);
-static void cast5_encrypt(struct tdb *, u_int8_t *);
-static void skipjack_encrypt(struct tdb *, u_int8_t *);
-static void des1_decrypt(struct tdb *, u_int8_t *);
-static void des3_decrypt(struct tdb *, u_int8_t *);
-static void blf_decrypt(struct tdb *, u_int8_t *);
-static void cast5_decrypt(struct tdb *, u_int8_t *);
-static void skipjack_decrypt(struct tdb *, u_int8_t *);
-
-static void
-des1_encrypt(struct tdb *tdb, u_int8_t *blk)
-{
- des_ecb_encrypt(blk, blk, tdb->tdb_key, 1);
-}
-
-static void
-des1_decrypt(struct tdb *tdb, u_int8_t *blk)
-{
- des_ecb_encrypt(blk, blk, tdb->tdb_key, 0);
-}
-
-static void
-des1_setkey(u_int8_t **sched, u_int8_t *key, int len)
-{
- MALLOC(*sched, u_int8_t *, 128, M_XDATA, M_WAITOK);
- bzero(*sched, 128);
- des_set_key(key, *sched);
-}
-
-static void
-des1_zerokey(u_int8_t **sched)
-{
- bzero(*sched, 128);
- FREE(*sched, M_XDATA);
- *sched = NULL;
-}
-
-struct enc_xform enc_xform_des = {
- SADB_EALG_DESCBC, "Data Encryption Standard (DES)",
- ESP_DES_BLKS, ESP_DES_IVS,
- 8, 8, 8,
- des1_encrypt,
- des1_decrypt,
- des1_setkey,
- des1_zerokey,
-};
-
-static void
-des3_encrypt(struct tdb *tdb, u_int8_t *blk)
-{
- des_ecb3_encrypt(blk, blk, tdb->tdb_key, tdb->tdb_key + 128,
- tdb->tdb_key + 256, 1);
-}
-
-static void
-des3_decrypt(struct tdb *tdb, u_int8_t *blk)
-{
- des_ecb3_encrypt(blk, blk, tdb->tdb_key + 256, tdb->tdb_key + 128,
- tdb->tdb_key, 0);
-}
-
-static void
-des3_setkey(u_int8_t **sched, u_int8_t *key, int len)
-{
- MALLOC(*sched, u_int8_t *, 384, M_XDATA, M_WAITOK);
- bzero(*sched, 384);
- des_set_key(key, *sched);
- des_set_key(key + 8, *sched + 128);
- des_set_key(key + 16, *sched + 256);
-}
-
-static void
-des3_zerokey(u_int8_t **sched)
-{
- bzero(*sched, 384);
- FREE(*sched, M_XDATA);
- *sched = NULL;
-}
-
-struct enc_xform enc_xform_3des = {
- SADB_EALG_3DESCBC, "Triple DES (3DES)",
- ESP_3DES_BLKS, ESP_3DES_IVS,
- 24, 24, 8,
- des3_encrypt,
- des3_decrypt,
- des3_setkey,
- des3_zerokey
-};
-
-static void
-blf_encrypt(struct tdb *tdb, u_int8_t *blk)
-{
- blf_ecb_encrypt((blf_ctx *) tdb->tdb_key, blk, 8);
-}
-
-static void
-blf_decrypt(struct tdb *tdb, u_int8_t *blk)
-{
- blf_ecb_decrypt((blf_ctx *) tdb->tdb_key, blk, 8);
-}
-
-static void
-blf_setkey(u_int8_t **sched, u_int8_t *key, int len)
-{
- MALLOC(*sched, u_int8_t *, sizeof(blf_ctx), M_XDATA, M_WAITOK);
- bzero(*sched, sizeof(blf_ctx));
- blf_key((blf_ctx *)*sched, key, len);
-}
-
-static void
-blf_zerokey(u_int8_t **sched)
-{
- bzero(*sched, sizeof(blf_ctx));
- FREE(*sched, M_XDATA);
- *sched = NULL;
-}
-
-struct enc_xform enc_xform_blf = {
- SADB_X_EALG_BLF, "Blowfish",
- ESP_BLF_BLKS, ESP_BLF_IVS,
- 5, BLF_MAXKEYLEN, 8,
- blf_encrypt,
- blf_decrypt,
- blf_setkey,
- blf_zerokey
-};
-
-static void
-cast5_encrypt(struct tdb *tdb, u_int8_t *blk)
-{
- cast_encrypt((cast_key *) tdb->tdb_key, blk, blk);
-}
-
-static void
-cast5_decrypt(struct tdb *tdb, u_int8_t *blk)
-{
- cast_decrypt((cast_key *) tdb->tdb_key, blk, blk);
-}
-
-static void
-cast5_setkey(u_int8_t **sched, u_int8_t *key, int len)
-{
- MALLOC(*sched, u_int8_t *, sizeof(blf_ctx), M_XDATA, M_WAITOK);
- bzero(*sched, sizeof(blf_ctx));
- cast_setkey((cast_key *)*sched, key, len);
-}
-
-static void
-cast5_zerokey(u_int8_t **sched)
-{
- bzero(*sched, sizeof(cast_key));
- FREE(*sched, M_XDATA);
- *sched = NULL;
-}
-
-struct enc_xform enc_xform_cast5 = {
- SADB_X_EALG_CAST, "CAST",
- ESP_CAST_BLKS, ESP_CAST_IVS,
- 5, 16, 8,
- cast5_encrypt,
- cast5_decrypt,
- cast5_setkey,
- cast5_zerokey
-};
-
-static void
-skipjack_encrypt(struct tdb *tdb, u_int8_t *blk)
-{
- skipjack_forwards(blk, blk, (u_int8_t **) tdb->tdb_key);
-}
-
-static void
-skipjack_decrypt(struct tdb *tdb, u_int8_t *blk)
-{
- skipjack_backwards(blk, blk, (u_int8_t **) tdb->tdb_key);
-}
-
-static void
-skipjack_setkey(u_int8_t **sched, u_int8_t *key, int len)
-{
- MALLOC(*sched, u_int8_t *, 10 * sizeof(u_int8_t *), M_XDATA, M_WAITOK);
- bzero(*sched, 10 * sizeof(u_int8_t *));
- subkey_table_gen(key, (u_int8_t **) *sched);
-}
-
-static void
-skipjack_zerokey(u_int8_t **sched)
-{
- int k;
-
- for (k = 0; k < 10; k++)
- if (((u_int8_t **)(*sched))[k])
- {
- bzero(((u_int8_t **)(*sched))[k], 0x100);
- FREE(((u_int8_t **)(*sched))[k], M_XDATA);
- }
- bzero(*sched, 10 * sizeof(u_int8_t *));
- FREE(*sched, M_XDATA);
- *sched = NULL;
-}
-
-struct enc_xform enc_xform_skipjack = {
- SADB_X_EALG_SKIPJACK, "Skipjack",
- ESP_SKIPJACK_BLKS, ESP_SKIPJACK_IVS,
- 10, 10, 8,
- skipjack_encrypt,
- skipjack_decrypt,
- skipjack_setkey,
- skipjack_zerokey
-};
-
-/*
- * And now for auth
- */
-
-struct auth_hash auth_hash_hmac_md5_96 = {
- SADB_AALG_MD5HMAC96, "HMAC-MD5-96",
- MD5HMAC96_KEYSIZE, AH_MD5_ALEN, AH_HMAC_HASHLEN,
- sizeof(MD5_CTX),
- (void (*) (void *)) MD5Init,
- (void (*) (void *, u_int8_t *, u_int16_t)) MD5Update,
- (void (*) (u_int8_t *, void *)) MD5Final
-};
-
-struct auth_hash auth_hash_hmac_sha1_96 = {
- SADB_AALG_SHA1HMAC96, "HMAC-SHA1-96",
- SHA1HMAC96_KEYSIZE, AH_SHA1_ALEN, AH_HMAC_HASHLEN,
- sizeof(SHA1_CTX),
- (void (*) (void *)) SHA1Init,
- (void (*) (void *, u_int8_t *, u_int16_t)) SHA1Update,
- (void (*) (u_int8_t *, void *)) SHA1Final
-};
-
-struct auth_hash auth_hash_hmac_ripemd_160_96 = {
- SADB_X_AALG_RIPEMD160HMAC96, "HMAC-RIPEMD-160-96",
- RIPEMD160HMAC96_KEYSIZE, AH_RMD160_ALEN, AH_HMAC_HASHLEN,
- sizeof(RMD160_CTX),
- (void (*)(void *)) RMD160Init,
- (void (*)(void *, u_int8_t *, u_int16_t)) RMD160Update,
- (void (*)(u_int8_t *, void *)) RMD160Final
-};
-
-struct auth_hash auth_hash_key_md5 = {
- SADB_X_AALG_MD5, "Keyed MD5",
- 0, AH_MD5_ALEN, AH_MD5_ALEN,
- sizeof(MD5_CTX),
- (void (*)(void *))MD5Init,
- (void (*)(void *, u_int8_t *, u_int16_t))MD5Update,
- (void (*)(u_int8_t *, void *))MD5Final
-};
-
-struct auth_hash auth_hash_key_sha1 = {
- SADB_X_AALG_SHA1, "Keyed SHA1",
- 0, AH_SHA1_ALEN, AH_SHA1_ALEN,
- sizeof(SHA1_CTX),
- (void (*)(void *))SHA1Init,
- (void (*)(void *, u_int8_t *, u_int16_t))SHA1Update,
- (void (*)(u_int8_t *, void *))SHA1Final
-};
diff --git a/sys/netinet/ipsec_input.c b/sys/netinet/ipsec_input.c
index a46d2921356..7aca77e11cb 100644
--- a/sys/netinet/ipsec_input.c
+++ b/sys/netinet/ipsec_input.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: ipsec_input.c,v 1.19 2000/02/07 06:09:09 itojun Exp $ */
+/* $OpenBSD: ipsec_input.c,v 1.20 2000/03/17 10:25:23 angelos Exp $ */
/*
* The authors of this code are John Ioannidis (ji@tla.org),
@@ -81,7 +81,7 @@
#include "bpfilter.h"
-int ipsec_common_input(struct mbuf **, int, int, int, int);
+int ipsec_common_input(struct mbuf *, int, int, int, int);
#ifdef ENCDEBUG
#define DPRINTF(x) if (encdebug) printf x
@@ -99,50 +99,34 @@ int ah_enable = 0;
/*
* ipsec_common_input() gets called when we receive an IPsec-protected packet
- * in IPv4 or IPv6.
+ * in IPv4 or IPv6. All it does is find the right TDB and call the appropriate
+ * transform. The callback takes care of further processing (like ingress
+ * filtering).
*/
-
int
-ipsec_common_input(struct mbuf **m0, int skip, int protoff, int af, int sproto)
+ipsec_common_input(struct mbuf *m, int skip, int protoff, int af, int sproto)
{
#define IPSEC_ISTAT(y,z) (sproto == IPPROTO_ESP ? (y)++ : (z)++)
-#define IPSEC_NAME (sproto == IPPROTO_ESP ? (af == AF_INET ? "esp4_input()" :\
- "esp6_input()") :\
- (af == AF_INET ? "ah4_input()" :\
- "ah6_input()"))
- union sockaddr_union src_address, dst_address, src2, dst2;
+
+ union sockaddr_union src_address, dst_address;
caddr_t sport = 0, dport = 0;
- struct flow *flow;
struct tdb *tdbp;
- struct mbuf *m;
u_int32_t spi;
- u_int8_t prot;
int s;
-#ifdef INET
- struct ip *ip, ipn;
-#endif /* INET */
-
-#ifdef INET6
- struct ip6_hdr *ip6, ip6n;
-#endif /* INET6 */
-
IPSEC_ISTAT(espstat.esps_input, ahstat.ahs_input);
- if (m0 == 0)
+ if (m == 0)
{
DPRINTF(("%s: NULL packet received\n"));
IPSEC_ISTAT(espstat.esps_hdrops, ahstat.ahs_hdrops);
return EINVAL;
}
- else
- m = *m0;
if ((sproto == IPPROTO_ESP && !esp_enable) ||
(sproto == IPPROTO_AH && !ah_enable))
{
m_freem(m);
- *m0 = NULL;
IPSEC_ISTAT(espstat.esps_pdrops, ahstat.ahs_pdrops);
return EOPNOTSUPP;
}
@@ -187,9 +171,9 @@ ipsec_common_input(struct mbuf **m0, int skip, int protoff, int af, int sproto)
#endif /* INET6 */
default:
- DPRINTF(("%s: unsupported protocol family %d\n", IPSEC_NAME, af));
+ DPRINTF(("ipsec_common_input(): unsupported protocol family %d\n",
+ af));
m_freem(m);
- *m0 = NULL;
IPSEC_ISTAT(espstat.esps_nopf, ahstat.ahs_nopf);
return EPFNOSUPPORT;
}
@@ -199,10 +183,8 @@ ipsec_common_input(struct mbuf **m0, int skip, int protoff, int af, int sproto)
if (tdbp == NULL)
{
splx(s);
- DPRINTF(("%s: could not find SA for packet to %s, spi %08x\n",
- IPSEC_NAME, ipsp_address(dst_address), ntohl(spi)));
+ DPRINTF(("ipsec_common_input(): could not find SA for packet to %s, spi %08x\n", ipsp_address(dst_address), ntohl(spi)));
m_freem(m);
- *m0 = NULL;
IPSEC_ISTAT(espstat.esps_notdb, ahstat.ahs_notdb);
return ENOENT;
}
@@ -210,10 +192,9 @@ ipsec_common_input(struct mbuf **m0, int skip, int protoff, int af, int sproto)
if (tdbp->tdb_flags & TDBF_INVALID)
{
splx(s);
- DPRINTF(("%s: attempted to use invalid SA %s/%08x\n",
- IPSEC_NAME, ipsp_address(dst_address), ntohl(spi)));
+ DPRINTF(("ipsec_common_input(): attempted to use invalid SA %s/%08x\n",
+ ipsp_address(dst_address), ntohl(spi)));
m_freem(m);
- *m0 = NULL;
IPSEC_ISTAT(espstat.esps_invalid, ahstat.ahs_invalid);
return EINVAL;
}
@@ -221,10 +202,8 @@ ipsec_common_input(struct mbuf **m0, int skip, int protoff, int af, int sproto)
if (tdbp->tdb_xform == NULL)
{
splx(s);
- DPRINTF(("%s: attempted to use uninitialized SA %s/%08x\n",
- IPSEC_NAME, ipsp_address(dst_address), ntohl(spi)));
+ DPRINTF(("ipsec_common_input(): attempted to use uninitialized SA %s/%08x\n", ipsp_address(dst_address), ntohl(spi)));
m_freem(m);
- *m0 = NULL;
IPSEC_ISTAT(espstat.esps_noxform, ahstat.ahs_noxform);
return ENXIO;
}
@@ -244,39 +223,74 @@ ipsec_common_input(struct mbuf **m0, int skip, int protoff, int af, int sproto)
/* If we do ingress filtering and the list is empty, quick drop */
if (ipsec_acl && (tdbp->tdb_access == NULL))
{
- DPRINTF(("%s: packet from %s dropped due to empty policy list, SA %s/%08x\n", IPSEC_NAME, ipsp_address(src_address), ipsp_address(tdbp->tdb_dst), ntohl(spi)));
+ DPRINTF(("ipsec_common_input(): packet from %s dropped due to empty policy list, SA %s/%08x\n", ipsp_address(src_address), ipsp_address(tdbp->tdb_dst), ntohl(spi)));
splx(s);
m_freem(m);
- *m0 = NULL;
IPSEC_ISTAT(espstat.esps_pdrops, ahstat.ahs_pdrops);
return EACCES;
}
- m = (*(tdbp->tdb_xform->xf_input))(m, tdbp, skip, protoff);
+ /*
+ * Call appropriate transform and return -- callback takes care of
+ * everything else.
+ */
+ if ((*(tdbp->tdb_xform->xf_input))(m, tdbp, skip, protoff) == NULL)
+ {
+ splx(s);
+ return EINVAL;
+ }
+ else
+ {
+ splx(s);
+ return 0;
+ }
+}
+
+/*
+ * IPsec input callback, called by the transform callback. Takes care of
+ * filtering and other sanity checks on the processed packet.
+ */
+int
+ipsec_common_input_cb(struct mbuf *m, struct tdb *tdbp, int skip, int protoff)
+{
+ union sockaddr_union src_address, dst_address, src2, dst2;
+ caddr_t sport = 0, dport = 0;
+ int prot, af, sproto;
+ struct flow *flow;
+
+#ifdef INET
+ struct ip *ip, ipn;
+#endif /* INET */
+
+#ifdef INET6
+ struct ip6_hdr *ip6, ip6n;
+#endif /* INET6 */
+
+ af = tdbp->tdb_dst.sa.sa_family;
+ sproto = tdbp->tdb_sproto;
+
+ /* Sanity check */
if (m == NULL)
{
/* The called routine will print a message if necessary */
- splx(s);
IPSEC_ISTAT(espstat.esps_badkcr, ahstat.ahs_badkcr);
return EINVAL;
}
#ifdef INET
/* Fix IPv4 header */
- if (af == AF_INET)
+ if (tdbp->tdb_dst.sa.sa_family == AF_INET)
{
if ((m = m_pullup(m, skip)) == 0)
{
- DPRINTF(("%s: processing failed for SA %s/%08x\n",
- IPSEC_NAME, ipsp_address(tdbp->tdb_dst), ntohl(spi)));
- splx(s);
+ DPRINTF(("ipsec_common_input_cb(): processing failed for SA %s/%08x\n", ipsp_address(tdbp->tdb_dst), ntohl(tdbp->tdb_spi)));
IPSEC_ISTAT(espstat.esps_hdrops, ahstat.ahs_hdrops);
- *m0 = NULL;
- return ENOMEM;
+ return ENOBUFS;
}
ip = mtod(m, struct ip *);
ip->ip_len = htons(m->m_pkthdr.len);
+ HTONS(ip->ip_id);
ip->ip_sum = 0;
ip->ip_sum = in_cksum(m, ip->ip_hl << 2);
prot = ip->ip_p;
@@ -297,10 +311,8 @@ ipsec_common_input(struct mbuf **m0, int skip, int protoff, int af, int sproto)
((tdbp->tdb_proxy.sa.sa_family != AF_INET) &&
(tdbp->tdb_proxy.sa.sa_family != 0)))
{
- DPRINTF(("%s: inner source address %s doesn't correspond to expected proxy source %s, SA %s/%08x\n", IPSEC_NAME, inet_ntoa4(ipn.ip_src), ipsp_address(tdbp->tdb_proxy), ipsp_address(tdbp->tdb_dst), ntohl(spi)));
- splx(s);
+ DPRINTF(("ipsec_common_input_cb(): inner source address %s doesn't correspond to expected proxy source %s, SA %s/%08x\n", inet_ntoa4(ipn.ip_src), ipsp_address(tdbp->tdb_proxy), ipsp_address(tdbp->tdb_dst), ntohl(tdbp->tdb_spi)));
m_freem(m);
- *m0 = NULL;
IPSEC_ISTAT(espstat.esps_pdrops, ahstat.ahs_pdrops);
return EACCES;
}
@@ -325,10 +337,8 @@ ipsec_common_input(struct mbuf **m0, int skip, int protoff, int af, int sproto)
((tdbp->tdb_proxy.sa.sa_family != AF_INET6) &&
(tdbp->tdb_proxy.sa.sa_family != 0)))
{
- DPRINTF(("%s: inner source address %s doesn't correspond to expected proxy source %s, SA %s/%08x\n", IPSEC_NAME, inet6_ntoa4(ip6n.ip6_src), ipsp_address(tdbp->tdb_proxy), ipsp_address(tdbp->tdb_dst), ntohl(spi)));
- splx(s);
+ DPRINTF(("ipsec_common_input_cb(): inner source address %s doesn't correspond to expected proxy source %s, SA %s/%08x\n", inet6_ntoa4(ip6n.ip6_src), ipsp_address(tdbp->tdb_proxy), ipsp_address(tdbp->tdb_dst), ntohl(tdbp->tdb_spi)));
m_freem(m);
- *m0 = NULL;
IPSEC_ISTAT(espstat.esps_pdrops, ahstat.ahs_pdrops);
return EACCES;
}
@@ -345,11 +355,9 @@ ipsec_common_input(struct mbuf **m0, int skip, int protoff, int af, int sproto)
((tdbp->tdb_src.sa.sa_family != AF_INET) &&
(tdbp->tdb_src.sa.sa_family != 0)))
{
- DPRINTF(("%s: source address %s doesn't correspond to expected source %s, SA %s/%08x\n", IPSEC_NAME, inet_ntoa4(ip->ip_src), ipsp_address(tdbp->tdb_src), ipsp_address(tdbp->tdb_dst), ntohl(spi)));
- splx(s);
+ DPRINTF(("ipsec_common_input_cb(): source address %s doesn't correspond to expected source %s, SA %s/%08x\n", inet_ntoa4(ip->ip_src), ipsp_address(tdbp->tdb_src), ipsp_address(tdbp->tdb_dst), ntohl(tdbp->tdb_spi)));
m_freem(m);
IPSEC_ISTAT(espstat.esps_pdrops, ahstat.ahs_pdrops);
- *m0 = NULL;
return EACCES;
}
}
@@ -361,12 +369,9 @@ ipsec_common_input(struct mbuf **m0, int skip, int protoff, int af, int sproto)
{
if ((m = m_pullup(m, sizeof(struct ip6_hdr))) == 0)
{
- DPRINTF(("%s: processing failed for SA %s/%08x\n",
- IPSEC_NAME, ipsp_address(tdbp->tdb_dst), ntohl(spi)));
- splx(s);
+ DPRINTF(("ipsec_common_input_cb(): processing failed for SA %s/%08x\n", ipsp_address(tdbp->tdb_dst), ntohl(tdbp->tdb_spi)));
IPSEC_ISTAT(espstat.esps_hdrops, ahstat.ahs_hdrops);
- *m0 = NULL;
- return ENOMEM;
+ return EACCES;
}
ip6 = mtod(m, struct ip6_hdr *);
@@ -392,11 +397,9 @@ ipsec_common_input(struct mbuf **m0, int skip, int protoff, int af, int sproto)
((tdbp->tdb_proxy.sa.sa_family != AF_INET) &&
(tdbp->tdb_proxy.sa.sa_family != 0)))
{
- DPRINTF(("%s: inner source address %s doesn't correspond to expected proxy source %s, SA %s/%08x\n", IPSEC_NAME, inet_ntoa4(ipn.ip_src), ipsp_address(tdbp->tdb_proxy), ipsp_address(tdbp->tdb_dst), ntohl(spi)));
- splx(s);
+ DPRINTF(("ipsec_common_input_cb(): inner source address %s doesn't correspond to expected proxy source %s, SA %s/%08x\n", inet_ntoa4(ipn.ip_src), ipsp_address(tdbp->tdb_proxy), ipsp_address(tdbp->tdb_dst), ntohl(tdbp->tdb_spi)));
m_freem(m);
IPSEC_ISTAT(espstat.esps_pdrops, ahstat.ahs_pdrops);
- *m0 = NULL;
return EACCES;
}
}
@@ -419,10 +422,8 @@ ipsec_common_input(struct mbuf **m0, int skip, int protoff, int af, int sproto)
((tdbp->tdb_proxy.sa.sa_family != AF_INET6) &&
(tdbp->tdb_proxy.sa.sa_family != 0)))
{
- DPRINTF(("%s: inner source address %s doesn't correspond to expected proxy source %s, SA %s/%08x\n", IPSEC_NAME, inet6_ntoa4(ip6n.ip6_src), ipsp_address(tdbp->tdb_proxy), ipsp_address(tdbp->tdb_dst), ntohl(spi)));
- splx(s);
+ DPRINTF(("ipsec_common_input_cb(): inner source address %s doesn't correspond to expected proxy source %s, SA %s/%08x\n", inet6_ntoa4(ip6n.ip6_src), ipsp_address(tdbp->tdb_proxy), ipsp_address(tdbp->tdb_dst), ntohl(tdbp->tdb_spi)));
m_freem(m);
- *m0 = NULL;
IPSEC_ISTAT(espstat.esps_pdrops, ahstat.ahs_pdrops);
return EACCES;
}
@@ -439,10 +440,8 @@ ipsec_common_input(struct mbuf **m0, int skip, int protoff, int af, int sproto)
((tdbp->tdb_src.sa.sa_family != AF_INET6) &&
(tdbp->tdb_src.sa.sa_family != 0)))
{
- DPRINTF(("%s: packet %s to %s does not match any ACL entries, SA %s/%08x\n", IPSEC_NAME, ipsp_address(src_address), ipsp_address(dst_address), ipsp_address(tdbp->tdb_src), ipsp_address(tdbp->tdb_dst), ntohl(spi)));
- splx(s);
+ DPRINTF(("ipsec_common_input_cb(): packet %s to %s does not match any ACL entries, SA %s/%08x\n", ipsp_address(src_address), ipsp_address(dst_address), ipsp_address(tdbp->tdb_src), ipsp_address(tdbp->tdb_dst), ntohl(tdbp->tdb_spi)));
m_freem(m);
- *m0 = NULL;
IPSEC_ISTAT(espstat.esps_pdrops, ahstat.ahs_pdrops);
return EACCES;
}
@@ -515,10 +514,8 @@ ipsec_common_input(struct mbuf **m0, int skip, int protoff, int af, int sproto)
if (flow == NULL)
{
/* Failed to match any entry in the ACL */
- DPRINTF(("%s: packet from %s to %s dropped due to policy, SA %s/%08x\n", IPSEC_NAME, ipsp_address(src_address), ipsp_address(dst_address), ipsp_address(tdbp->tdb_dst), ntohl(spi)));
- splx(s);
+ DPRINTF(("ipsec_common_input_cb(): packet from %s to %s dropped due to policy, SA %s/%08x\n", ipsp_address(src_address), ipsp_address(dst_address), ipsp_address(tdbp->tdb_dst), ntohl(tdbp->tdb_spi)));
m_freem(m);
- *m0 = NULL;
IPSEC_ISTAT(espstat.esps_pdrops, ahstat.ahs_pdrops);
return EACCES;
}
@@ -529,7 +526,7 @@ ipsec_common_input(struct mbuf **m0, int skip, int protoff, int af, int sproto)
if (tdbp->tdb_bind_out)
{
if (!(m->m_flags & M_PKTHDR))
- DPRINTF(("%s: mbuf is not a packet header!\n", IPSEC_NAME));
+ DPRINTF(("ipsec_common_input_cb(): mbuf is not a packet header!\n"));
MALLOC(m->m_pkthdr.tdbi, struct tdb_ident *,
sizeof(struct tdb_ident), M_TEMP, M_NOWAIT);
@@ -586,11 +583,51 @@ ipsec_common_input(struct mbuf **m0, int skip, int protoff, int af, int sproto)
bpf_mtap(m->m_pkthdr.rcvif->if_bpf, &m1);
}
#endif
- splx(s);
- *m0 = m;
- return 0;
-#undef IPSEC_NAME
+ /* Call the appropriate IPsec transform callback */
+ switch (af)
+ {
+#ifdef INET
+ case AF_INET:
+ switch (sproto)
+ {
+ case IPPROTO_ESP:
+ return esp4_input_cb(m);
+
+ case IPPROTO_AH:
+ return ah4_input_cb(m);
+
+ default:
+ DPRINTF(("ipsec_common_input_cb(): unknown/unsupported security protocol %d\n", sproto));
+ m_freem(m);
+ return EPFNOSUPPORT;
+ }
+ break;
+#endif /* INET */
+
+#ifdef INET6
+ case AF_INET6:
+ switch (sproto)
+ {
+ case IPPROTO_ESP:
+ return esp6_input_cb(m, protoff);
+
+ case IPPROTO_AH:
+ return ah6_input_cb(m, protoff);
+
+ default:
+ DPRINTF(("ipsec_common_input_cb(): unknown/unsupported security protocol %d\n", sproto));
+ m_freem(m);
+ return EPFNOSUPPORT;
+ }
+ break;
+#endif /* INET6 */
+
+ default:
+ DPRINTF(("ipsec_common_input_cb(): unknown/unsupported protocol family %d\n", af));
+ m_freem(m);
+ return EPFNOSUPPORT;
+ }
#undef IPSEC_ISTAT
}
@@ -635,80 +672,86 @@ ah_sysctl(int *name, u_int namelen, void *oldp, size_t *oldlen, void *newp,
void
ah4_input(struct mbuf *m, ...)
{
- struct ifqueue *ifq = &ipintrq;
- struct mbuf *mp = m;
- int skip, s;
+ int skip;
va_list ap;
va_start(ap, m);
skip = va_arg(ap, int);
va_end(ap);
- if (ipsec_common_input(&mp, skip, offsetof(struct ip, ip_p), AF_INET,
- IPPROTO_AH) != 0)
- return;
+ ipsec_common_input(m, skip, offsetof(struct ip, ip_p), AF_INET,
+ IPPROTO_AH);
+ return;
+}
+
+/* IPv4 AH callback */
+int
+ah4_input_cb(struct mbuf *m, ...)
+{
+ struct ifqueue *ifq = &ipintrq;
/*
* Interface pointer is already in first mbuf; chop off the
* `outer' header and reschedule.
*/
- s = splimp(); /* isn't it already? */
if (IF_QFULL(ifq))
{
IF_DROP(ifq);
- if (mp->m_pkthdr.tdbi)
- free(mp->m_pkthdr.tdbi, M_TEMP);
- m_freem(mp);
+ if (m->m_pkthdr.tdbi)
+ free(m->m_pkthdr.tdbi, M_TEMP);
+ m_freem(m);
ahstat.ahs_qfull++;
- splx(s);
- DPRINTF(("ah4_input(): dropped packet because of full IP queue\n"));
- return;
+
+ DPRINTF(("ah4_input_cb(): dropped packet because of full IP queue\n"));
+ return ENOBUFS;
}
- IF_ENQUEUE(ifq, mp);
+ IF_ENQUEUE(ifq, m);
schednetisr(NETISR_IP);
- splx(s);
+ return 0;
}
/* IPv4 ESP wrapper */
void
esp4_input(struct mbuf *m, ...)
{
- struct ifqueue *ifq = &ipintrq;
- struct mbuf *mp = m;
- int skip, s;
+ int skip;
va_list ap;
va_start(ap, m);
skip = va_arg(ap, int);
va_end(ap);
- if (ipsec_common_input(&mp, skip, offsetof(struct ip, ip_p), AF_INET,
- IPPROTO_ESP) != 0)
- return;
+ ipsec_common_input(m, skip, offsetof(struct ip, ip_p), AF_INET,
+ IPPROTO_ESP);
+}
+
+/* IPv4 ESP callback */
+int
+esp4_input_cb(struct mbuf *m, ...)
+{
+ struct ifqueue *ifq = &ipintrq;
/*
* Interface pointer is already in first mbuf; chop off the
* `outer' header and reschedule.
*/
-
- s = splimp(); /* isn't it already? */
if (IF_QFULL(ifq))
{
IF_DROP(ifq);
- if (mp->m_pkthdr.tdbi)
- free(mp->m_pkthdr.tdbi, M_TEMP);
- m_freem(mp);
+ if (m->m_pkthdr.tdbi)
+ free(m->m_pkthdr.tdbi, M_TEMP);
+ m_freem(m);
espstat.esps_qfull++;
- splx(s);
- DPRINTF(("esp4_input(): dropped packet because of full IP queue\n"));
- return;
+
+ DPRINTF(("esp4_input_cb(): dropped packet because of full IP queue\n"));
+ return ENOBUFS;
}
- IF_ENQUEUE(ifq, mp);
+ IF_ENQUEUE(ifq, m);
schednetisr(NETISR_IP);
- splx(s);
+ return 0;
}
#endif /* INET */
@@ -717,7 +760,6 @@ esp4_input(struct mbuf *m, ...)
int
ah6_input(struct mbuf **mp, int *offp, int proto)
{
- struct mbuf *m = *mp;
u_int8_t nxt = 0;
int protoff;
@@ -725,14 +767,13 @@ ah6_input(struct mbuf **mp, int *offp, int proto)
protoff = offsetof(struct ip6_hdr, ip6_nxt);
else
{
- /* Chase the header chain... */
-
+ /* Chase down the header chain... */
protoff = sizeof(struct ip6_hdr);
do
{
protoff += nxt;
- m_copydata(m, protoff + offsetof(struct ip6_ext, ip6e_len),
+ m_copydata(*mp, protoff + offsetof(struct ip6_ext, ip6e_len),
sizeof(u_int8_t), (caddr_t) &nxt);
nxt = (nxt + 1) * 8;
} while (protoff + nxt < *offp);
@@ -742,7 +783,7 @@ ah6_input(struct mbuf **mp, int *offp, int proto)
{
DPRINTF(("ah6_input(): bad packet header chain\n"));
ahstat.ahs_hdrops++;
- m_freem(m);
+ m_freem(*mp);
*mp = NULL;
return IPPROTO_DONE;
}
@@ -750,22 +791,29 @@ ah6_input(struct mbuf **mp, int *offp, int proto)
protoff += offsetof(struct ip6_ext, ip6e_nxt);
}
- if (ipsec_common_input(&m, *offp, protoff, AF_INET6, proto) != 0)
- {
- *mp = NULL;
- return IPPROTO_DONE;
- }
+ ipsec_common_input(*mp, *offp, protoff, AF_INET6, proto);
+ return IPPROTO_DONE;
+}
+
+/* IPv6 AH callback */
+int
+ah6_input_cb(struct mbuf *mp, int protoff)
+{
+ u_int8_t nxt = 0;
/* Retrieve new protocol */
- m_copydata(m, protoff, sizeof(u_int8_t), (caddr_t) &nxt);
- return nxt;
+ m_copydata(mp, protoff, sizeof(u_int8_t), (caddr_t) &nxt);
+
+ /* XXX Requeue -- for now, drop packet */
+ m_freem(mp);
+
+ return 0;
}
/* IPv6 ESP wrapper */
int
esp6_input(struct mbuf **mp, int *offp, int proto)
{
- struct mbuf *m = *mp;
u_int8_t nxt = 0;
int protoff;
@@ -773,14 +821,13 @@ esp6_input(struct mbuf **mp, int *offp, int proto)
protoff = offsetof(struct ip6_hdr, ip6_nxt);
else
{
- /* Chase the header chain... */
-
+ /* Chase down the header chain... */
protoff = sizeof(struct ip6_hdr);
do
{
protoff += nxt;
- m_copydata(m, protoff + offsetof(struct ip6_ext, ip6e_len),
+ m_copydata(*mp, protoff + offsetof(struct ip6_ext, ip6e_len),
sizeof(u_int8_t), (caddr_t) &nxt);
nxt = (nxt + 1) * 8;
} while (protoff + nxt < *offp);
@@ -790,7 +837,7 @@ esp6_input(struct mbuf **mp, int *offp, int proto)
{
DPRINTF(("esp6_input(): bad packet header chain\n"));
espstat.esps_hdrops++;
- m_freem(m);
+ m_freem(*mp);
*mp = NULL;
return IPPROTO_DONE;
}
@@ -799,14 +846,22 @@ esp6_input(struct mbuf **mp, int *offp, int proto)
}
protoff = offsetof(struct ip6_hdr, ip6_nxt);
- if (ipsec_common_input(&m, *offp, protoff, AF_INET6, proto) != 0)
- {
- *mp = NULL;
- return IPPROTO_DONE;
- }
+ ipsec_common_input(*mp, *offp, protoff, AF_INET6, proto);
+ return IPPROTO_DONE;
+}
+
+/* IPv6 ESP callback */
+int
+esp6_input_cb(struct mbuf *mp, int protoff)
+{
+ u_int8_t nxt = 0;
/* Retrieve new protocol */
- m_copydata(m, protoff, sizeof(u_int8_t), (caddr_t) &nxt);
- return nxt;
+ m_copydata(mp, protoff, sizeof(u_int8_t), (caddr_t) &nxt);
+
+ /* XXX Requeue -- for now, drop packet */
+ m_freem(mp);
+
+ return 0;
}
#endif /* INET6 */