ipsecesp.c revision 7c478bd95313f5f23a4c958a745db2134aa03244
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2004 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <sys/sysmacros.h>
#include <inet/ipsec_info.h>
#include <inet/ipsec_impl.h>
#include <inet/ipsecesp.h>
#include <inet/udp_impl.h>
/* EXPORT DELETE START */
/* EXPORT DELETE END */
/* Packet dropper for ESP drops. */
static ipdropper_t esp_dropper;
/*
* Table of ND variables supported by ipsecesp. These are loaded into
* ipsecesp_g_nd in ipsecesp_init_nd.
*/
static ipsecespparam_t ipsecesp_param_arr[] = {
/* min max value name */
{ 0, 3, 0, "ipsecesp_debug"},
{ 1, 10, 1, "ipsecesp_reap_delay"},
{ 1, 300, 15, "ipsecesp_acquire_timeout"},
{ 1, 1800, 90, "ipsecesp_larval_timeout"},
/* Default lifetime values for ACQUIRE messages. */
{ 0, 0xffffffffU, 0, "ipsecesp_default_soft_bytes"},
{ 0, 0xffffffffU, 0, "ipsecesp_default_hard_bytes"},
{ 0, 0xffffffffU, 24000, "ipsecesp_default_soft_addtime"},
{ 0, 0xffffffffU, 28800, "ipsecesp_default_hard_addtime"},
{ 0, 0xffffffffU, 0, "ipsecesp_default_soft_usetime"},
{ 0, 0xffffffffU, 0, "ipsecesp_default_hard_usetime"},
{ 0, 1, 0, "ipsecesp_log_unknown_spi"},
{ 0, 2, 1, "ipsecesp_padding_check"},
};
#define ipsecesp_default_soft_bytes \
#define ipsecesp_default_hard_bytes \
#define ipsecesp_default_soft_addtime \
#define ipsecesp_default_hard_addtime \
#define ipsecesp_default_soft_usetime \
#define ipsecesp_default_hard_usetime \
#define ipsecesp_log_unknown_spi \
#define ipsecesp_padding_check \
/* NOTE: != 0 instead of > 0 so lint doesn't complain. */
static IDP ipsecesp_g_nd;
static int ipsecesp_close(queue_t *);
/* EXPORT DELETE START */
/* EXPORT DELETE END */
kstat_named_t **);
uint_t);
static struct module_info info = {
};
};
};
struct streamtab ipsecespinfo = {
};
/*
* Keysock instance of ESP. "There can be only one." :)
* Use casptr() on this because I don't set it until KEYSOCK_HELLO comes down.
* Paired up with the esp_pfkey_q is the esp_event, which will age SAs.
*/
static queue_t *esp_pfkey_q;
static timeout_id_t esp_event;
/*
*
* Question: Do I need this, given that all instance's esps->esps_wq point
* to IP?
*
* Answer: Yes, because I need to know which queue is BOUND to
* IPPROTO_ESP
*/
static mblk_t *esp_ip_unbind;
/*
* Stats. This may eventually become a full-blown SNMP MIB once that spec
* stabilizes.
*/
typedef struct {
/* EXPORT DELETE START */
/* EXPORT DELETE END */
} esp_kstats_t;
static esp_kstats_t *esp_kstats;
static int esp_kstat_update(kstat_t *, int);
static boolean_t
esp_kstat_init(void)
{
return (B_FALSE);
#define K64 KSTAT_DATA_UINT64
/* EXPORT DELETE START */
/* EXPORT DELETE END */
KI(keysock_in);
/* EXPORT DELETE START */
/* EXPORT DELETE END */
return (B_TRUE);
}
static int
{
return (EIO);
if (rw == KSTAT_WRITE)
return (EACCES);
/* EXPORT DELETE START */
/* EXPORT DELETE END */
return (0);
}
#ifdef DEBUG
/*
* Debug routine, useful to see pre-encryption data.
*/
static char *
{
unsigned char *ptr;
printf("mblk address 0x%p, length %ld, db_ref %d "
"type %d, base 0x%p, lim 0x%p\n",
tmp_line[0] = '\0';
if (!(diff & 0x1f)) {
tmp_line[0] = '\0';
}
}
if (!(diff & 0x3))
ptr++;
}
}
return ("\n");
}
#else /* DEBUG */
static char *
{
return ("\n");
}
#endif /* DEBUG */
/*
* Don't have to lock age_interval, as only one thread will access it at
* a time, because I control the one function that does with timeout().
*/
/* ARGSUSED */
static void
{
}
/*
* Get an ESP NDD parameter.
*/
/* ARGSUSED */
static int
queue_t *q;
{
return (0);
}
/*
* This routine sets an NDD variable in a ipsecespparam_t structure.
*/
/* ARGSUSED */
static int
queue_t *q;
char *value;
{
/*
* Fail the request if the new value does not lie within the
* required bounds.
*/
return (EINVAL);
}
/* Set the new value */
return (0);
}
/*
* Using lifetime NDD variables, fill in an extended combination's
* lifetime information.
*/
void
{
}
/*
* Initialize things for ESP at module load time.
*/
ipsecesp_ddi_init(void)
{
int count;
espp->ipsecesp_param_name[0]) {
return (B_FALSE);
}
}
}
if (!esp_kstat_init()) {
return (B_FALSE);
}
return (B_TRUE);
}
/*
* Destroy things for ESP at module unload time.
*/
void
ipsecesp_ddi_destroy(void)
{
esp1dbg(("In ipsecesp_ddi_destroy.\n"));
}
/*
* ESP module open routine.
*/
/* ARGSUSED */
static int
{
esp1dbg(("Non-privileged user trying to open ipsecesp.\n"));
return (EPERM);
}
return (0); /* Re-open of an already open instance. */
return (EINVAL);
/*
* ASSUMPTIONS (because I'm MT_OCEXCL):
*
* * I'm being pushed on top of IP for all my opens (incl. #1).
* * Only ipsecesp_open() can write into esp_sadb.s_ip_q.
* * Because of this, I can check lazily for esp_sadb.s_ip_q.
*
* If these assumptions are wrong, I'm in BIG trouble...
*/
q->q_ptr = q; /* just so I know I'm open */
struct T_unbind_req *tur;
/* Allocate an unbind... */
/*
* Send down T_BIND_REQ to bind IPPROTO_ESP.
* Handle the ACK here in ESP.
*/
qprocson(q);
if (esp_ip_unbind == NULL ||
if (esp_ip_unbind != NULL) {
}
return (ENOMEM);
}
} else {
qprocson(q);
}
/*
* For now, there's not much I can do. I'll be getting a message
* passed down to me from keysock (in my wput), and a T_BIND_ACK
* up from IP (in my rput).
*/
return (0);
}
/*
* ESP module close routine.
*/
static int
{
/*
* If esp_sadb.s_ip_q is attached to this instance, send a
* T_UNBIND_REQ to IP for the instance before doing
* a qprocsoff().
*/
}
/*
* Clean up q_ptr, if needed.
*/
qprocsoff(q);
/* Keysock queue check is safe, because of OCEXCL perimeter. */
if (q == esp_pfkey_q) {
esp0dbg(("ipsecesp_close: Ummm... keysock is closing ESP.\n"));
esp_pfkey_q = NULL;
/* Detach qtimeouts. */
(void) quntimeout(q, esp_event);
}
/*
* If the esp_sadb.s_ip_q is attached to this instance, find
* another. The OCEXCL outer perimeter helps us here.
*/
/*
* Find a replacement queue for esp_sadb.s_ip_q.
*/
/*
* See if we can use the pfkey_q.
*/
}
esp1dbg(("ipsecesp: Can't reassign ip_q.\n"));
} else {
BPRI_HI);
if (esp_ip_unbind != NULL) {
struct T_unbind_req *tur;
tur = (struct T_unbind_req *)
}
/* If it's NULL, I can't do much here. */
}
}
return (0);
}
/*
* Add a number of bytes to what the SA has protected so far. Return
* B_TRUE if the SA can still protect that many bytes.
*
* Caller must REFRELE the passed-in assoc. This function must REFRELE
* any obtained peer SA.
*/
static boolean_t
{
int outhash;
/* No peer? No problem! */
if (!assoc->ipsa_haspeer) {
B_TRUE));
}
/*
* Otherwise, we want to grab both the original assoc and its peer.
* There might be a race for this, but if it's a real race, two
* expire messages may occur. We limit this by only sending the
* expire message on one of the peers, we'll pick the inbound
* arbitrarily.
*
* If we need tight synchronization on the peer SA, then we need to
* reconsider.
*/
if (inbound) {
if (isv6) {
&inassoc->ipsa_dstaddr));
} else {
&inassoc->ipsa_dstaddr));
}
/* Q: Do we wish to set haspeer == B_FALSE? */
esp0dbg(("esp_age_bytes: "
"can't find peer for inbound.\n"));
}
} else {
/* Q: Do we wish to set haspeer == B_FALSE? */
esp0dbg(("esp_age_bytes: "
"can't find peer for outbound.\n"));
}
}
/*
* REFRELE any peer SA.
*
* Because of the multi-line macro nature of IPSA_REFRELE, keep
* them in { }.
*/
if (inbound) {
} else {
}
}
/*
* Do incoming NAT-T manipulations for packet.
*/
static ipsec_status_t
{
/* Initialize to our inbound cksum adjustment... */
switch (ipha->ipha_protocol) {
case IPPROTO_TCP:
break;
case IPPROTO_UDP:
if (udpha->uha_checksum != 0) {
/* Adujst if the inbound one was not zero. */
if (udpha->uha_checksum == 0)
}
break;
case IPPROTO_IP:
/*
* This case is only an issue for self-encapsulated
* packets. So for now, fall through.
*/
break;
}
return (IPSEC_STATUS_SUCCESS);
}
/*
* Strip ESP header and fix IP header
* Returns B_TRUE on success, B_FALSE if an error occured.
*/
static boolean_t
{
/*
* Strip ESP data and fix IP header.
*
* XXX In case the beginning of esp_inbound() changes to not do a
* pullup, this part of the code can remain unchanged.
*/
if (isv4) {
} else {
}
/*
* "Next header" and padding length are the last two bytes in the
* ESP-protected datagram, thus the explicit - 1 and - 2.
* lastpad is the last byte of the padding, which can be used for
* a quick check to see if the padding is correct.
*/
if (isv4) {
/* Fix part of the IP header. */
/*
* Reality check the padlen. The explicit - 2 is for the
* padding length and the next-header bytes.
*/
/* EXPORT DELETE START */
/* EXPORT DELETE END */
"Possibly corrupt ESP packet.");
esp1dbg(("pkt len(%d) - ip hdr - esp hdr - ivlen(%d) "
return (B_FALSE);
}
/*
* Fix the rest of the header. The explicit - 2 is for the
* padding length and the next-header bytes.
*/
ipha->ipha_hdr_checksum = 0;
} else {
} else {
} else {
/* Panic a DEBUG kernel. */
/* Otherwise, pretend it's IP + ESP. */
}
}
ivlen) {
/* EXPORT DELETE START */
/* EXPORT DELETE END */
"Possibly corrupt ESP packet.");
esp1dbg(("pkt len(%u) - ip hdr - esp hdr - ivlen(%d)"
return (B_FALSE);
}
/*
* Fix the rest of the header. The explicit - 2 is for the
* padding length and the next-header bytes. IPv6 is nice,
* because there's no hdr checksum!
*/
}
if (ipsecesp_padding_check > 0 &&
"Possibly corrupt ESP packet.");
esp1dbg(("lastpad (%d) not equal to padlen (%d):\n",
return (B_FALSE);
}
if (ipsecesp_padding_check > 1) {
/*
* this assert may have to become an if
* and a pullup if we start accepting
* multi-dblk mblks. Any packet here will
* have been pulled up in esp_inbound.
*/
while (lastval != 0) {
"Possibly corrupt ESP packet.");
esp1dbg(("padding not in correct"
" format:\n"));
return (B_FALSE);
}
}
}
/* Trim off the padding. */
/*
* Remove the ESP header.
*
* The above assertions about data_mp's size will make this work.
*
* XXX Question: If I send up and get back a contiguous mblk,
* would it be quicker to bcopy over, or keep doing the dupb stuff?
* I go with copying for now.
*/
do {
src--;
dst--;
} else {
do {
src--;
dst--;
}
esp2dbg(("data_mp after inbound ESP adjustment:\n"));
return (B_TRUE);
}
/*
* Updating use times can be tricky business if the ipsa_haspeer flag is
* set. This function is called once in an SA's lifetime.
*
* Caller has to REFRELE "assoc" which is passed in. This function has
* to REFRELE any peer SA that is obtained.
*/
static void
{
int outhash;
/* No peer? No problem! */
if (!assoc->ipsa_haspeer) {
return;
}
/*
* Otherwise, we want to grab both the original assoc and its peer.
* There might be a race for this, but if it's a real race, the times
* will be out-of-synch by at most a second, and since our time
* granularity is a second, this won't be a problem.
*
* If we need tight synchronization on the peer SA, then we need to
* reconsider.
*/
if (inbound) {
if (isv6) {
&inassoc->ipsa_dstaddr));
} else {
&inassoc->ipsa_dstaddr));
}
/* Q: Do we wish to set haspeer == B_FALSE? */
esp0dbg(("esp_set_usetime: "
"can't find peer for inbound.\n"));
return;
}
} else {
/* Q: Do we wish to set haspeer == B_FALSE? */
esp0dbg(("esp_set_usetime: "
"can't find peer for outbound.\n"));
return;
}
}
/* Update usetime on both. */
/*
* REFRELE any peer SA.
*
* Because of the multi-line macro nature of IPSA_REFRELE, keep
* them in { }.
*/
if (inbound) {
} else {
}
}
/*
* Handle ESP inbound data for IPv4 and IPv6.
* On success returns B_TRUE, on failure returns B_FALSE and frees the
* mblk chain ipsec_in_mp.
*/
{
if (ipsa->ipsa_usetime == 0)
/*
* We may wish to check replay in-range-only here as an optimization.
* Include the reality check of ipsa->ipsa_replay >
* ipsa->ipsa_replay_wsize for times when it's the first N packets,
* where N == ipsa->ipsa_replay_wsize.
*
* Another check that may come here later is the "collision" check.
* If legitimate packets flow quickly enough, this won't be a problem,
* but collisions may cause authentication algorithm crunching to
* take place when it doesn't need to.
*/
/*
* TODO: Extract inbound interface from the IPSEC_IN
* message's ii->ipsec_in_rill_index.
*/
return (IPSEC_STATUS_FAILED);
}
/* EXPORT DELETE START */
/*
* Has this packet already been processed by a hardware
* IPsec accelerator?
*/
if (ii->ipsec_in_accelerated) {
esp3dbg(("esp_inbound: pkt processed by ill=%d isv6=%d\n",
return (rv);
}
/* EXPORT DELETE END */
/*
* Adjust the IP header's payload length to reflect the removal
* of the ICV.
*/
if (!ii->ipsec_in_v4) {
ipsa->ipsa_mac_len);
} else {
ipsa->ipsa_mac_len);
}
/* submit the request to the crypto framework */
}
/*
* Perform the really difficult work of inserting the proposed situation.
* Called while holding the algorithm lock.
*/
static void
{
/*
* Based upon algorithm properties, and what-not, prioritize
* a proposal. If the IPSEC_OUT message has an algorithm specified,
* use it first and foremost.
*
* For each action in policy list
* Add combination. If I've hit limit, return.
*/
/* EXPORT DELETE START */
/* EXPORT DELETE END */
continue;
if (!(prot->ipp_use_esp))
continue;
if (prot->ipp_esp_auth_alg != 0) {
[prot->ipp_esp_auth_alg];
continue;
}
/* EXPORT DELETE START */
continue;
/* EXPORT DELETE END */
comb->sadb_comb_flags = 0;
comb->sadb_comb_reserved = 0;
/* EXPORT DELETE START */
/* EXPORT DELETE END */
comb->sadb_comb_auth = 0;
comb->sadb_comb_auth_minbits = 0;
comb->sadb_comb_auth_maxbits = 0;
} else {
}
/*
* The following may be based on algorithm
* properties, but in the meantime, we just pick
* some good, sensible numbers. Key mgmt. can
* (and perhaps should) be the place to finalize
* such decisions.
*/
/*
* No limits on allocations, since we really don't
* support that concept currently.
*/
/*
* These may want to come from policy rule..
*/
if (--combs == 0)
break; /* out of space.. */
comb++;
}
}
/*
* Prepare and actually send the SADB_ACQUIRE message to PF_KEY.
*/
static void
{
pfkeymp = sadb_keysock_out(0);
esp0dbg(("esp_send_acquire: 1st allocb() failed.\n"));
/* Just bail. */
goto done;
}
/*
* First, allocate a basic ACQUIRE message. Beyond that,
* you need to extract certificate info from
*/
sizeof (sadb_address_t) + sizeof (sadb_prop_t);
switch (acqrec->ipsacq_addrfam) {
case AF_INET:
break;
case AF_INET6:
break;
}
/* EXPORT DELETE START */
#if 0
/* EXPORT DELETE END */
/* EXPORT DELETE START */
#else
#endif
/* EXPORT DELETE END */
/*
* XXX If there are:
* certificate IDs
* proxy address
* <Others>
* add additional allocation size.
*/
esp0dbg(("esp_send_acquire: 2nd allocb() failed.\n"));
/* Just bail. */
goto done;
}
/* Set up ACQUIRE. */
esp0dbg(("sadb_setup_acquire failed.\n"));
/* Just bail. */
goto done;
}
/* XXX Insert proxy address information here. */
/* XXX Insert identity information here. */
/* XXXMLS Insert sensitivity information here. */
/* Insert proposal here. */
done:
/*
* Must mutex_exit() before sending PF_KEY message up, in
* order to avoid recursive mutex_enter() if there are no registered
* listeners.
*
* Once I've sent the message, I'm cool anyway.
*/
}
return;
}
/* XXX freemsg() works for extended == NULL. */
}
/*
* Handle the SADB_GETSPI message. Create a larval SA.
*/
static void
{
int rc, diagnostic;
/*
* Randomly generate a proposed SPI value
*/
ksi->ks_in_serial);
return;
ksi->ks_in_serial);
return;
}
/*
* XXX - We may randomly collide. We really should recover from this.
* Unfortunately, that could require spending way-too-much-time
* in here. For now, let the user retry.
*/
} else {
}
/*
* Check for collisions (i.e. did sadb_getspi() return with something
* that already exists?).
*
* Try outbound first. Even though SADB_GETSPI is traditionally
* for inbound SAs, you never know what a user might do.
*/
}
/*
* I don't have collisions elsewhere!
*/
} else {
/*
* sadb_insertassoc() also checks for collisions, so
* if there's a colliding entry, rc will be set
* to EEXIST.
*/
}
/*
* Can exit outbound mutex. Hold inbound until we're done
* with newbie.
*/
if (rc != 0) {
ksi->ks_in_serial);
return;
}
/* Can write here because I'm still holding the bucket lock. */
/*
* Construct successful return message. We have one thing going
* for us in PF_KEY v2. That's the fact that
* sizeof (sadb_spirange_t) == sizeof (sadb_sa_t)
*/
/* Convert KEYSOCK_IN to KEYSOCK_OUT. */
/*
* Can safely putnext() to esp_pfkey_q, because this is a turnaround
* from the esp_pfkey_q.
*/
}
/*
* Insert the ESP header into a packet. Duplicate an mblk, and insert a newly
* allocated mblk with the ESP header in between the two.
*/
static boolean_t
{
}
/* "scratch" is the 2nd half, split_mp is the first. */
esp1dbg(("esp_insert_esp: can't allocate scratch.\n"));
return (B_FALSE);
}
/* NOTE: dupb() doesn't set b_cont appropriately. */
}
/*
* At this point, split_mp is exactly "wheretodiv" bytes long, and
* holds the end of the pre-ESP part of the datagram.
*/
return (B_TRUE);
}
/*
* Finish processing of an inbound ESP packet after processing by the
* crypto framework.
* - Remove the ESP header.
* - Send packet back to IP.
* If authentication was performed on the packet, this function is called
* only if the authentication succeeded.
* On success returns B_TRUE, on failure returns B_FALSE and frees the
* mblk chain ipsec_in_mp.
*/
static ipsec_status_t
{
/* get the pointer to the ESP header */
/* EXPORT DELETE START */
/* authentication-only ESP */
/* EXPORT DELETE END */
/* EXPORT DELETE START */
} else {
/* encryption present */
/* encryption-only ESP */
} else {
/* encryption with authentication */
}
}
/* EXPORT DELETE END */
/* authentication passed if we reach this point */
/*
* Check replay window here!
* For right now, assume keysock will set the replay window
* size to zero for SAs that have an unspecified sender.
* This may change...
*/
/*
* Log the event. As of now we print out an event.
* Do not print the replay failure number, or else
* syslog cannot collate the error messages. Printing
* the replay number that failed opens a denial-of-
* service attack.
*/
"Replay failed for ESP spi 0x%x, dst %s.\n",
goto drop_and_bail;
}
}
/* The ipsa has hit hard expiration, LOG and AUDIT. */
"ESP association 0x%x, dst %s had bytes expire.\n",
goto drop_and_bail;
}
/*
* Remove ESP header and padding from packet. I hope the compiler
* spews "branch, predict taken" code for this.
*/
if (is_natt)
return (IPSEC_STATUS_SUCCESS);
}
esp1dbg(("esp_in_done: esp_strip_header() failed\n"));
/*
* TODO: Extract inbound interface from the IPSEC_IN message's
* ii->ipsec_in_rill_index.
*/
return (IPSEC_STATUS_FAILED);
}
/*
* Called upon failing the inbound ICV check. The message passed as
* argument is freed.
*/
static void
{
/*
* Log the event. Don't print to the console, block
* potential denial-of-service attack.
*/
"ESP Authentication failed for spi 0x%x, dst %s.\n",
/*
* TODO: Extract inbound interface from the IPSEC_IN
* message's ii->ipsec_in_rill_index.
*/
&esp_dropper);
}
/*
* Invoked for outbound packets after ESP processing. If the packet
* also requires AH, performs the AH SA selection and AH processing.
* Returns B_TRUE if the AH processing was not needed or if it was
* performed successfully. Returns B_FALSE and consumes the passed mblk
* if AH processing was required but could not be performed.
*/
static boolean_t
{
}
if (!ap->ipa_want_ah)
return (B_TRUE);
return (B_FALSE);
}
}
return (ipsec_rc == IPSEC_STATUS_SUCCESS);
}
/*
* Kernel crypto framework callback invoked after completion of async
* crypto requests.
*/
static void
{
if (status == CRYPTO_SUCCESS) {
if (is_inbound) {
return;
/* finish IPsec processing */
} else {
/*
* If a ICV was computed, it was stored by the
* crypto framework at the end of the packet.
*/
/* do AH processing if needed */
if (!esp_do_outbound_ah(ipsec_mp))
return;
/* finish IPsec processing */
NULL);
} else {
}
}
} else if (status == CRYPTO_INVALID_MAC) {
} else {
esp1dbg(("esp_kcf_callback: crypto failed with 0x%x\n",
status));
if (is_inbound)
else
}
}
/*
* Invoked on crypto framework failure during inbound and outbound processing.
*/
static void
{
esp1dbg(("crypto failed for %s ESP with 0x%x\n",
&esp_dropper);
if (is_inbound)
else
}
#define ESP_INIT_CALLREQ(_cr) { \
}
}
} else { \
} \
}
/* EXPORT DELETE START */
}
/* EXPORT DELETE END */
static ipsec_status_t
{
int kef_rc = CRYPTO_FAILED;
/* EXPORT DELETE START */
/* EXPORT DELETE END */
/* EXPORT DELETE START */
/*
* An inbound packet is of the form:
* IPSEC_IN -> [IP,options,ESP,IV,data,ICV,pad]
*/
/* EXPORT DELETE END */
if (do_auth) {
/* force asynchronous processing? */
if (ipsec_algs_exec_mode[IPSEC_ALG_AUTH] ==
/* authentication context template */
/* ICV to be verified */
/* authentication starts at the ESP header */
/* EXPORT DELETE START */
if (!do_encr) {
/* authentication only */
/* EXPORT DELETE END */
/* initialize input data argument */
/* call the crypto framework */
/* EXPORT DELETE START */
}
/* EXPORT DELETE END */
}
/* EXPORT DELETE START */
if (do_encr) {
/* force asynchronous processing? */
if (ipsec_algs_exec_mode[IPSEC_ALG_ENCR] ==
/* encryption template */
/* skip IV, since it is passed separately */
if (!do_auth) {
/* decryption only */
/* initialize input data argument */
/* specify IV */
/* call the crypto framework */
}
}
/* dual operation */
/* initialize input data argument */
/* specify IV */
/* call the framework */
}
/* EXPORT DELETE END */
switch (kef_rc) {
case CRYPTO_SUCCESS:
return (esp_in_done(ipsec_mp));
case CRYPTO_QUEUED:
/* esp_kcf_callback() will be invoked on completion */
return (IPSEC_STATUS_PENDING);
case CRYPTO_INVALID_MAC:
return (IPSEC_STATUS_FAILED);
}
return (IPSEC_STATUS_FAILED);
}
static ipsec_status_t
{
int kef_rc = CRYPTO_FAILED;
/* EXPORT DELETE START */
/* EXPORT DELETE END */
/* EXPORT DELETE START */
/* EXPORT DELETE END */
/*
* EXPORT DELETE START
* Outbound IPsec packets are of the form:
* IPSEC_OUT -> [IP,options] -> [ESP,IV] -> [data] -> [pad,ICV]
* unless it's NATT, then it's
* IPSEC_OUT -> [IP,options] -> [udp][ESP,IV] -> [data] -> [pad,ICV]
* EXPORT DELETE END
* Get a pointer to the mblk containing the ESP header.
*/
if (do_auth) {
/* force asynchronous processing? */
if (ipsec_algs_exec_mode[IPSEC_ALG_AUTH] ==
/* authentication context template */
/* where to store the computed mac */
/* authentication starts at the ESP header */
auth_len = payload_len +
/* EXPORT DELETE START */
iv_len +
/* EXPORT DELETE END */
sizeof (esph_t);
/* EXPORT DELETE START */
if (!do_encr) {
/* authentication only */
/* EXPORT DELETE END */
/* initialize input data argument */
/* call the crypto framework */
/* EXPORT DELETE START */
}
/* EXPORT DELETE END */
}
/* EXPORT DELETE START */
if (do_encr) {
/* force asynchronous processing? */
if (ipsec_algs_exec_mode[IPSEC_ALG_ENCR] ==
/* encryption context template */
if (!do_auth) {
/* encryption only, skip mblk that contains ESP hdr */
/* initialize input data argument */
/* specify IV */
/* call the crypto framework */
}
}
/*
* Encryption and authentication:
* Pass the pointer to the mblk chain starting at the ESP
* header to the framework. Skip the ESP header mblk
* for encryption, which is reflected by an encryption
* offset equal to the length of that mblk. Start
* the authentication at the ESP header, i.e. use an
* authentication offset of zero.
*/
/* specify IV */
/* call the framework */
}
/* EXPORT DELETE END */
switch (kef_rc) {
case CRYPTO_SUCCESS:
return (IPSEC_STATUS_SUCCESS);
case CRYPTO_QUEUED:
/* esp_kcf_callback() will be invoked on completion */
return (IPSEC_STATUS_PENDING);
}
return (IPSEC_STATUS_FAILED);
}
/*
* Handle outbound IPsec processing for IPv4 and IPv6
* On success returns B_TRUE, on failure returns B_FALSE and frees the
* mblk chain ipsec_in_mp.
*/
static ipsec_status_t
{
ipsec_out_mp = mp;
/*
* <sigh> We have to copy the message here, because TCP (for example)
* keeps a dupb() of the message lying around for retransmission.
* Since ESP changes the whole of the datagram, we have to create our
* own copy lest we clobber TCP's data. Since we have to copy anyway,
* we might as well make use of msgpullup() and get the mblk into one
* contiguous piece!
*/
esp0dbg(("esp_outbound: msgpullup() failed, "
"dropping packet.\n"));
/*
* TODO: Find the outbound IRE for this packet and
* pass it to ip_drop_packet().
*/
return (IPSEC_STATUS_FAILED);
} else {
}
/*
* Reality check....
*/
if (io->ipsec_out_v4) {
} else {
/*
* Destination options are tricky. If we get in here,
* then we have a terminal header following the
* destination options. We need to adjust backwards
* so we insert ESP BEFORE the destination options
* bag. (So that the dstopts get encrypted!)
*
* Since this is for outbound packets only, we know
* that non-terminal destination options only precede
* routing headers.
*/
}
} else {
/* It's probably IP + ESP. */
}
}
if (assoc->ipsa_usetime == 0)
/* wedge in fake UDP */
}
/* EXPORT DELETE START */
/* EXPORT DELETE END */
/* EXPORT DELETE START */
/*
* Set up ESP header and encryption padding for ENCR PI request.
*/
/* EXPORT DELETE END */
/*
* Determine the padding length. Pad to 4-bytes.
*
* Include the two additional bytes (hence the - 2) for the padding
* length and the next header. Take this into account when
* calculating the actual length of the padding.
*/
/* EXPORT DELETE START */
} else {
/* EXPORT DELETE END */
sizeof (uint32_t);
/* EXPORT DELETE START */
}
/* Allocate ESP header and IV. */
/* EXPORT DELETE END */
/*
* Update association byte-count lifetimes. Don't forget to take
* into account the padding length and next-header (hence the + 2).
* EXPORT DELETE START
* Use the amount of data fed into the "encryption algorithm". This
* is the IV, the data length, the padding length, and the final two
* bytes (padlen, and next-header).
*
* EXPORT DELETE END
*/
/* EXPORT DELETE START */
iv_len +
/* EXPORT DELETE END */
2, B_FALSE)) {
/*
* TODO: Find the outbound IRE for this packet and
* pass it to ip_drop_packet().
*/
return (IPSEC_STATUS_FAILED);
}
esp1dbg(("esp_outbound: can't allocate espmp.\n"));
/*
* TODO: Find the outbound IRE for this packet and
* pass it to ip_drop_packet().
*/
&esp_dropper);
return (IPSEC_STATUS_FAILED);
}
if (is_natt) {
esp3dbg(("esp_outbound: NATT"));
if (assoc->ipsa_remote_port != 0)
else
/*
* Set the checksum to 0, so that the ip_wput_ipsec_out()
* can do the right thing.
*/
udpha->uha_checksum = 0;
}
/*
* XXX We have replay counter wrapping.
* We probably want to nuke this SA (and its peer).
*/
"Outbound ESP SA (0x%x, %s) has wrapped sequence.\n",
/*
* TODO: Find the outbound IRE for this packet and
* pass it to ip_drop_packet().
*/
&esp_dropper);
return (IPSEC_STATUS_FAILED);
}
/* EXPORT DELETE START */
/*
* Set the IV to a random quantity. We do not require the
* highest quality random bits, but for best security with CBC
* mode ciphers, the value must be unlikely to repeat and also
* must not be known in advance to an adversary capable of
* influencing the plaintext.
*/
/* EXPORT DELETE END */
/* Fix the IP header. */
if (io->ipsec_out_v4) {
if (is_natt) {
*nhp = IPPROTO_UDP;
} else {
*nhp = IPPROTO_ESP;
}
ipha->ipha_hdr_checksum = 0;
} else {
*nhp = IPPROTO_ESP;
}
/* I've got the two ESP mblks, now insert them. */
esp2dbg(("data_mp before outbound ESP adjustment:\n"));
/* NOTE: esp_insert_esp() only fails if there's no memory. */
/*
* TODO: Find the outbound IRE for this packet and
* pass it to ip_drop_packet().
*/
&esp_dropper);
return (IPSEC_STATUS_FAILED);
}
/* Append padding (and leave room for ICV). */
;
esp0dbg(("esp_outbound: Can't allocate tailmp.\n"));
/*
* TODO: Find the outbound IRE for this packet and
* pass it to ip_drop_packet().
*/
return (IPSEC_STATUS_FAILED);
}
}
/*
* If there's padding, N bytes of padding must be of the form 0x1,
* 0x2, 0x3... 0xN.
*/
for (i = 0; i < padlen; ) {
i++;
}
/* EXPORT DELETE START */
esp2dbg(("data_Mp before encryption:\n"));
/*
* The packet is eligible for hardware acceleration if the
* following conditions are satisfied:
*
* 1. the packet will not be fragmented
* 2. the provider supports the algorithms specified by SA
* 3. there is no pending control message being exchanged
* 4. snoop is not attached
* 5. the destination address is not a multicast address
*
* All five of these conditions are checked by IP prior to
* sending the packet to ESP.
*
* But We, and We Alone, can, nay MUST check if the packet
* is over NATT, and then disqualify it from hardware
* acceleration.
*/
}
/*
* Okay. I've set up the pre-encryption ESP. Let's do it!
*/
/* EXPORT DELETE END */
if (mac_len > 0) {
} else {
}
}
/*
* IP calls this to validate the ICMP errors that
* we got from the network.
*/
{
/*
* Unless we get an entire packet back, this function is useless.
* Why?
*
* 1.) Partial packets are useless, because the "next header"
* is at the end of the decrypted ESP packet. Without the
* whole packet, this is useless.
*
* 2.) If we every use a stateful cipher, such as a stream or a
* one-time pad, we can't do anything.
*
* Since the chances of us getting an entire packet back are very
* very small, we discard here.
*/
&esp_dropper);
return (IPSEC_STATUS_FAILED);
}
/*
* ESP module read put routine.
*/
/* ARGSUSED */
static void
{
int *addrtype;
case M_CTL:
/*
* IPsec request of some variety from IP. IPSEC_{IN,OUT}
* are the common cases, but even ICMP error messages from IP
* may rise up here.
*
* Ummmm, actually, this can also be the reflected KEYSOCK_IN
* message, with an IRE_DB_TYPE hung off at the end.
*/
case KEYSOCK_IN:
break; /* Out of switch. */
}
if (esp_pfkey_q != NULL) {
/*
* Decrement counter to make up for
* auto-increment in ipsecesp_wput().
* I'm running all MT-hot through here, so
* don't worry about perimeters and lateral
* puts.
*/
} else {
}
break;
default:
break;
}
break;
case M_PROTO:
case M_PCPROTO:
/* TPI message of some sort. */
case T_BIND_ACK:
esp3dbg(("Thank you IP from ESP for T_BIND_ACK\n"));
break;
case T_ERROR_ACK:
"ipsecesp: ESP received T_ERROR_ACK from IP.");
/*
* Make esp_sadb.s_ip_q NULL, and in the
* future, perhaps try again.
*/
break;
case T_OK_ACK:
/* Probably from a (rarely sent) T_UNBIND_REQ. */
break;
default:
esp0dbg(("Unknown M_{,PC}PROTO message.\n"));
}
break;
default:
/* For now, passthru message. */
esp2dbg(("ESP got unknown mblk type %d.\n",
}
}
/*
* Construct an SADB_REGISTER message with the current algorithms.
*/
static boolean_t
{
/* EXPORT DELETE START */
/* EXPORT DELETE END */
uint_t i, numalgs_snap;
int current_aalgs;
/* EXPORT DELETE START */
int current_ealgs;
/* EXPORT DELETE END */
/* Allocate the KEYSOCK_OUT. */
if (keysock_out_mp == NULL) {
esp0dbg(("esp_register_out: couldn't allocate mblk.\n"));
return (B_FALSE);
}
/*
* Allocate the PF_KEY message that follows KEYSOCK_OUT.
*/
/*
* Fill SADB_REGISTER message's algorithm descriptors. Hold
* down the lock while filling it.
*
* Return only valid algorithms, so the number of algorithms
* to send up may be less than the number of algorithm entries
* in the table.
*/
for (num_aalgs = 0, i = 0; i < IPSEC_MAX_ALGS; i++)
num_aalgs++;
if (num_aalgs != 0) {
allocsize += sizeof (*sasupp_auth);
}
/* EXPORT DELETE START */
for (num_ealgs = 0, i = 0; i < IPSEC_MAX_ALGS; i++)
num_ealgs++;
if (num_ealgs != 0) {
allocsize += sizeof (*sasupp_encr);
}
/* EXPORT DELETE END */
return (B_FALSE);
}
if (num_aalgs != 0) {
sasupp_auth = (sadb_supported_t *)
numalgs_snap = 0;
for (i = 0;
continue;
saalg->sadb_alg_ivlen = 0;
authalgs[i]->alg_increment;
numalgs_snap++;
saalg++;
}
#ifdef DEBUG
/*
* Reality check to make sure I snagged all of the
* algorithms.
*/
for (; i < IPSEC_MAX_ALGS; i++) {
"Missed aalg #%d.\n", i);
}
}
#endif /* DEBUG */
} else {
}
/* EXPORT DELETE START */
if (num_ealgs != 0) {
numalgs_snap = 0;
for (i = 0;
continue;
encralgs[i]->alg_increment;
numalgs_snap++;
saalg++;
}
#ifdef DEBUG
/*
* Reality check to make sure I snagged all of the
* algorithms.
*/
for (; i < IPSEC_MAX_ALGS; i++) {
"Missed ealg #%d.\n", i);
}
}
#endif /* DEBUG */
}
/* EXPORT DELETE END */
/* EXPORT DELETE START */
/* EXPORT DELETE END */
/* Now fill the rest of the SADB_REGISTER message. */
samsg->sadb_msg_errno = 0;
samsg->sadb_msg_reserved = 0;
/*
* from me over a new alg., I could give two hoots about sequence.
*/
if (sasupp_auth != NULL) {
SADB_8TO64(sizeof (*sasupp_auth) +
sizeof (*saalg) * current_aalgs);
}
/* EXPORT DELETE START */
if (sasupp_encr != NULL) {
SADB_8TO64(sizeof (*sasupp_encr) +
sizeof (*saalg) * current_ealgs);
}
/* EXPORT DELETE END */
if (esp_pfkey_q != NULL)
else {
return (B_FALSE);
}
return (B_TRUE);
}
/*
* Invoked when the algorithm table changes. Causes SADB_REGISTER
* messages continaining the current list of algorithms to be
* sent up to the ESP listeners.
*/
void
ipsecesp_algs_changed(void)
{
/*
* Time to send a PF_KEY SADB_REGISTER message to ESP listeners
* everywhere. (The function itself checks for NULL esp_pfkey_q.)
*/
(void) esp_register_out(0, 0, 0);
}
/*
* taskq_dispatch handler.
*/
static void
inbound_task(void *arg)
{
int ipsec_rc;
esp2dbg(("in ESP inbound_task"));
return;
if (ipsec_rc != IPSEC_STATUS_SUCCESS)
return;
}
/*
* Now that weak-key passed, actually ADD the security association, and
* send back a reply ADD message.
*/
static int
{
struct sockaddr_in *dst;
struct sockaddr_in6 *dst6;
int rc;
int outhash;
/* EXPORT DELETE START */
#if 0
/*
* Gross hack for export control. Since esp_encr_keycheck
* is gone, I have to somehow enforce that exportable ESP source
* can't have encryption.
*/
/* EXPORT DELETE END */
return (EINVAL);
}
/* EXPORT DELETE START */
#endif
/* EXPORT DELETE END */
/*
* Locate the appropriate table(s).
*/
if (is_ipv4) {
} else {
}
switch (ksi->ks_in_dsttype) {
case KS_IN_ADDR_MBCAST:
/* FALLTHRU */
case KS_IN_ADDR_ME:
/*
* If the source address is either one of mine, or unspecified
* (which is best summed up by saying "not 'not mine'"),
* then the association is potentially bi-directional,
* in that it can be used for inbound traffic and outbound
* traffic. The best example of such an SA is a multicast
* SA (which allows me to receive the outbound traffic).
*/
is_inbound = B_TRUE;
break;
case KS_IN_ADDR_NOTME:
/*
* If the source address literally not mine (either
* unspecified or not mine), then this SA may have an
* address that WILL be mine after some configuration.
* We pay the price for this by making it a bi-directional
* SA.
*/
break;
default:
return (EINVAL);
}
/*
* Find a ACQUIRE list entry if possible. If we've added an SA that
* suits the needs of an ACQUIRE list entry, we can eliminate the
* ACQUIRE list entry and transmit the enqueued packets. Use the
* high-bit of the sequence number to queue it. Key off destination
* addr, and change acqrec's state.
*/
/*
* Q: I only check sequence. Should I check dst?
* A: Yes, check dest because those are the packets
* that are queued up.
*/
break;
}
/*
* AHA! I found an ACQUIRE record for this SA.
* Grab the msg list, and free the acquire record.
* I already am holding the lock for this record,
* so all I have to do is free it.
*/
}
}
/*
* Find PF_KEY message, and see if I'm an update. If so, find entry
* in larval list (if there).
*/
esp0dbg(("Larval update, but larval disappeared.\n"));
return (ESRCH);
} /* Else sadb_common_add unlinks it for me! */
}
(void *) lpkt, TQ_NOSLEEP);
}
if (rc != 0) {
}
/*
* How much more stack will I create with all of these
* esp_outbound() calls?
*/
if (rc == 0) {
/* do AH processing if needed */
if (!esp_do_outbound_ah(mp))
continue;
/* finish IPsec processing */
if (is_ipv4) {
} else {
}
}
continue;
}
}
}
return (rc);
}
/*
* routine eventually.
*/
static int
{
/* I need certain extensions present for an ADD message. */
return (EINVAL);
}
return (EINVAL);
}
return (EINVAL);
}
return (EINVAL);
}
/* Sundry ADD-specific reality checks. */
return (EINVAL);
}
return (EINVAL);
}
return (EINVAL);
}
return (EINVAL);
}
return (EINVAL);
}
return (EINVAL);
}
if (nttext_loc == NULL) {
return (EINVAL);
}
return (EINVAL);
}
}
if (nttext_rem == NULL) {
return (EINVAL);
}
return (EINVAL);
}
}
/* Stuff I don't support, for now. XXX Diagnostic? */
return (EOPNOTSUPP);
/*
* XXX Policy : I'm not checking identities or sensitivity
* labels at this time, but if I did, I'd do them here, before I sent
* the weak key check up to the algorithm.
*/
/*
* First locate the authentication algorithm.
*/
esp1dbg(("Couldn't find auth alg #%d.\n",
assoc->sadb_sa_auth));
return (EINVAL);
}
/* sanity check key sizes */
return (EINVAL);
}
/* check key and fix parity if needed */
diagnostic) != 0) {
return (EINVAL);
}
}
/* EXPORT DELETE START */
/*
* Then locate the encryption algorithm.
*/
esp1dbg(("Couldn't find encr alg #%d.\n",
assoc->sadb_sa_encrypt));
return (EINVAL);
}
/* sanity check key sizes */
return (EINVAL);
}
/* check key */
diagnostic) != 0) {
return (EINVAL);
}
}
/* EXPORT DELETE END */
}
/*
* Update a security association. Updates come in two varieties. The first
* is an update of lifetimes on a non-larval SA. The second is an update of
* a larval SA, which ends up looking a lot more like an add.
*/
static int
{
struct sockaddr_in *sin;
return (EINVAL);
}
}
/*
* Delete a security association. This is REALLY likely to be code common to
* both AH and ESP. Find the association, then unlink it.
*/
static int
{
struct sockaddr_in *sin;
} else {
return (EINVAL);
}
}
}
/*
* Convert the entire contents of all of ESP's SA tables into PF_KEY SADB_DUMP
* messages.
*/
static void
{
int error;
/*
* Dump each fanout, bailing if error is non-zero.
*/
if (error != 0)
goto bail;
bail:
NULL);
}
/*
* ESP parsing of PF_KEY messages. Keysock did most of the really silly
* error cases. What I receive is a fully-formed, syntactically legal
* PF_KEY message. I then need to check semantics...
*
* This code may become common to AH and ESP. Stay tuned.
*
* I also make the assumption that db_ref's are cool. If this assumption
* is wrong, this means that someone other than keysock or me has been
* mucking with PF_KEY messages.
*/
static void
{
int error;
int diagnostic = SADB_X_DIAGNOSTIC_NONE;
/*
* If applicable, convert unspecified AF_INET6 to unspecified
* AF_INET.
*/
switch (samsg->sadb_msg_type) {
case SADB_ADD:
if (error != 0) {
ksi->ks_in_serial);
}
/* else esp_add_sa() took care of things. */
break;
case SADB_DELETE:
if (error != 0) {
ksi->ks_in_serial);
}
/* Else esp_del_sa() took care of things. */
break;
case SADB_GET:
if (error != 0) {
ksi->ks_in_serial);
}
/* Else sadb_get_sa() took care of things. */
break;
case SADB_FLUSH:
break;
case SADB_REGISTER:
/*
* Hmmm, let's do it! Check for extensions (there should
* be none), extract the fields, call esp_register_out(),
* then either free or report an error.
*
* Keysock takes care of the PF_KEY bookkeeping for this.
*/
ksi->ks_in_serial)) {
} else {
/*
* Only way this path hits is if there is a memory
* failure. It will not return B_FALSE because of
* lack of esp_pfkey_q if I am in wput().
*/
ksi->ks_in_serial);
}
break;
case SADB_UPDATE:
/*
* Find a larval, if not there, find a full one and get
* strict.
*/
if (error != 0) {
ksi->ks_in_serial);
}
/* else esp_update_sa() took care of things. */
break;
case SADB_GETSPI:
/*
* Reserve a new larval entry.
*/
break;
case SADB_ACQUIRE:
/*
* most likely an error. Inbound ACQUIRE messages should only
* have the base header.
*/
break;
case SADB_DUMP:
/*
* Dump all entries.
*/
/* esp_dump will take care of the return message, etc. */
break;
case SADB_EXPIRE:
/* Should never reach me. */
ksi->ks_in_serial);
break;
default:
break;
}
}
/*
* Handle case where PF_KEY says it can't find a keysock for one of my
* ACQUIRE messages.
*/
static void
{
return;
}
/*
* If keysock can't find any registered, delete the acquire record
* immediately, and handle errors.
*/
/*
* Use the write-side of the esp_pfkey_q, in case there is
* no esp_sadb.s_ip_q.
*/
}
}
/*
* First-cut reality check for an inbound PF_KEY message.
*/
static boolean_t
{
int diagnostic;
goto badmsg;
}
goto badmsg;
}
goto badmsg;
}
goto badmsg;
}
return (B_FALSE); /* False ==> no failures */
ksi->ks_in_serial);
return (B_TRUE); /* True ==> failures */
}
/*
* ESP module write put routine.
*/
static void
{
int rc;
esp3dbg(("In esp_wput().\n"));
/* NOTE: Each case must take care of freeing or passing mp. */
case M_CTL:
/* Not big enough message. */
break;
}
switch (ii->ipsec_info_type) {
case KEYSOCK_OUT_ERR:
esp1dbg(("Got KEYSOCK_OUT_ERR message.\n"));
break;
case KEYSOCK_IN:
esp3dbg(("Got KEYSOCK_IN message.\n"));
/*
* Some common reality checks.
*/
return;
/*
* Use 'q' instead of esp_sadb.s_ip_q, since
* it's the write side already, and it'll go
* down to IP. Use esp_pfkey_q because we
* wouldn't get here if that weren't set, and
* the RD(q) has been done already.
*/
ksi->ks_in_serial);
if (rc == KS_IN_ADDR_UNKNOWN)
return;
else
}
ksi->ks_in_serial);
if (rc == KS_IN_ADDR_UNKNOWN)
return;
else
}
/*
* XXX Proxy may be a different address family.
*/
ksi->ks_in_serial);
if (rc == KS_IN_ADDR_UNKNOWN)
return;
else
}
break;
case KEYSOCK_HELLO:
break;
default:
esp2dbg(("Got M_CTL from above of 0x%x.\n",
ii->ipsec_info_type));
break;
}
break;
case M_IOCTL:
case ND_SET:
case ND_GET:
return;
} else {
}
/* FALLTHRU */
default:
/* We really don't support any other ioctls, do we? */
/* Return EINVAL */
return;
}
default:
esp3dbg(("Got default message, type %d, passing to IP.\n",
}
}
/* EXPORT DELETE START */
/*
* Process an outbound ESP packet that can be accelerated by a IPsec
* hardware acceleration capable Provider.
* The caller already inserted and initialized the ESP header.
* This function allocates a tagging M_CTL, and adds room at the end
* of the packet to hold the ICV if authentication is needed.
*
* On success returns B_TRUE, on failure returns B_FALSE and frees the
* mblk chain ipsec_out.
*/
static ipsec_status_t
{
/* mark packet as being accelerated in IPSEC_OUT */
/*
* add room at the end of the packet for the ICV if needed
*/
if (icv_len > 0) {
/* go to last mblk */
do {
/* if not enough available room, allocate new mblk */
return (IPSEC_STATUS_FAILED);
}
}
}
return (IPSEC_STATUS_SUCCESS);
}
/*
* Process an inbound accelerated ESP packet.
* On success returns B_TRUE, on failure returns B_FALSE and frees the
* mblk chain ipsec_in.
*/
static ipsec_status_t
{
ipsec_in_t *ii;
/*
* We only support one level of decapsulation in hardware, so
* nuke the pointer.
*/
/*
* ESP with authentication. We expect the Provider to have
* computed the ICV and placed it in the hardware acceleration
* data attributes.
*
* Extract ICV length from attributes M_CTL and sanity check
* its value. We allow the mblk to be smaller than da_ipsec_t
* for a small ICV, as long as the entire ICV fits within the
* mblk.
*
* Also ensures that the ICV length computed by Provider
* corresponds to the ICV length of the agorithm specified by
* the SA.
*/
esp0dbg(("esp_inbound_accelerated: "
"ICV len (%u) incorrect or mblk too small (%u)\n",
goto esp_in_discard;
}
}
/* get pointers to IP header */
if (isv4) {
} else {
}
/*
* Compare ICV in ESP packet vs ICV computed by adapter.
* We also remove the ICV from the end of the packet since
* it will no longer be needed.
*
* Assume that esp_inbound() already ensured that the pkt
* was in one mblk.
*/
/* adjust IP header */
if (isv4)
else
int af;
void *addr;
if (isv4) {
} else {
}
/*
* Log the event. Don't print to the console, block
* potential denial-of-service attack.
*/
"ESP Authentication failed spi %x, dst_addr %s",
goto esp_in_discard;
}
esp3dbg(("esp_inbound_accelerated: ESP authentication succeeded, "
"checking replay\n"));
/*
* Remove ESP header and padding from packet.
*/
&counter)) {
esp1dbg(("esp_inbound_accelerated: "
"esp_strip_header() failed\n"));
goto esp_in_discard;
}
/*
* Account for usage..
*/
/* The ipsa has hit hard expiration, LOG and AUDIT. */
"ESP association 0x%x, dst %s had bytes expire.\n",
return (IPSEC_STATUS_FAILED);
}
/* done processing the packet */
return (IPSEC_STATUS_SUCCESS);
return (IPSEC_STATUS_FAILED);
}
/* EXPORT DELETE END */
/*
* Wrapper to allow IP to trigger an ESP association failure message
* during inbound SA selection.
*/
void
{
if (ipsecesp_log_unknown_spi) {
}
&esp_dropper);
}
/*
* Initialize the ESP input and output processing functions.
*/
void
{
}