ipsecesp.c revision 0c0328cd0042940f46d6fbd784333d637c0e2bf8
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (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 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#include <sys/sysmacros.h>
#include <inet/ipsec_info.h>
#include <inet/ipsec_impl.h>
#include <inet/ipsecesp.h>
#include <inet/udp_impl.h>
/*
* Table of ND variables supported by ipsecesp. These are loaded into
* ipsecesp_g_nd in ipsecesp_init_nd.
*/
static ipsecespparam_t lcl_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"},
{ 0, 600, 20, "ipsecesp_nat_keepalive_interval"},
};
#define ipsecesp_acquire_timeout \
#define ipsecesp_larval_timeout \
#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 \
/* For ipsecesp_nat_keepalive_interval, see ipsecesp.h. */
/* NOTE: != 0 instead of > 0 so lint doesn't complain. */
static int ipsecesp_close(queue_t *);
ipsecesp_stack_t *);
kstat_named_t **, ipsecesp_stack_t *);
uint_t);
static struct module_info info = {
};
};
};
struct streamtab ipsecespinfo = {
};
/*
*
* 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
*/
/*
* Stats. This may eventually become a full-blown SNMP MIB once that spec
* stabilizes.
*/
typedef struct esp_kstats_s {
} esp_kstats_t;
/*
* espstack->esp_kstats is equal to espstack->esp_ksp->ks_data if
* kstat_create_netstack for espstack->esp_ksp succeeds, but when it
* fails, it will be NULL. Note this is done for all stack instances,
* so it *could* fail. hence a non-NULL checking is done for
* ESP_BUMP_STAT and ESP_DEBUMP_STAT
*/
#define ESP_BUMP_STAT(espstack, x) \
do { \
} while (0)
#define ESP_DEBUMP_STAT(espstack, x) \
do { \
} while (0)
static int esp_kstat_update(kstat_t *, int);
static boolean_t
{
"net", KSTAT_TYPE_NAMED,
sizeof (esp_kstats_t) / sizeof (kstat_named_t),
return (B_FALSE);
#define K64 KSTAT_DATA_UINT64
KI(keysock_in);
return (B_TRUE);
}
static int
{
netstack_t *ns;
return (EIO);
if (rw == KSTAT_WRITE)
return (EACCES);
return (-1);
return (-1);
}
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().
*/
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)
{
/*
* We want to be informed each time a stack is created or
* destroyed in the kernel, so we can maintain the
* set of ipsecesp_stack_t's.
*/
return (B_TRUE);
}
/*
* Walk through the param array specified registering each element with the
* named dispatch handler.
*/
static boolean_t
{
espp->ipsecesp_param_name[0]) {
return (B_FALSE);
}
}
}
return (B_TRUE);
}
/*
* Initialize things for ESP for each stack instance
*/
static void *
{
KM_SLEEP);
return (espstack);
}
/*
* Destroy things for ESP at module unload time.
*/
void
ipsecesp_ddi_destroy(void)
{
}
/*
* Destroy things for ESP for one stack instance
*/
static void
{
}
}
/*
* ESP module open routine.
*/
/* ARGSUSED */
static int
{
netstack_t *ns;
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...
*/
struct T_unbind_req *tur;
/* Allocate an unbind... */
BPRI_HI);
/*
* Send down T_BIND_REQ to bind IPPROTO_ESP.
* Handle the ACK here in ESP.
*/
qprocson(q);
}
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 == espstack->esp_pfkey_q) {
("ipsecesp_close: Ummm... keysock is closing ESP.\n"));
/* Detach qtimeouts. */
}
/*
* 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.
*/
}
} else {
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, check padding, 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.
*/
"Corrupt ESP packet (padlen too big).\n");
padlen));
"hdr - ivlen(%d) = %d.\n",
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) {
"Corrupt ESP packet (v6 padlen too big).\n");
padlen));
("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!
*/
}
/*
* Weak padding check: compare last-byte to length, they
* should be equal.
*/
"Corrupt ESP packet (lastpad != padlen).\n");
("lastpad (%d) not equal to padlen (%d):\n",
return (B_FALSE);
}
/*
* Strong padding check: Check all pad bytes to see that
* they're ascending. Go backwards using a descending counter
* to verify. padlen == 1 is checked by previous block, so
* only bother if we've more than 1 byte of padding.
* Consequently, start the check one byte before the location
* of "lastpad".
*/
/*
* This assert may have to become an if and a pullup
* if we start accepting multi-dblk mblks. For now,
* though, any packet here will have been pulled up in
* esp_inbound.
*/
/*
* Use "--lastpad" because we already checked the very
* last pad byte previously.
*/
while (--lastpad != 0) {
"packet (bad padding).\n");
("padding not in correct"
" format:\n"));
return (B_FALSE);
}
lastbyte--;
}
}
}
/* 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--;
}
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.
*/
{
/*
* 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.
*/
&espstack->esp_dropper);
return (IPSEC_STATUS_FAILED);
}
/*
* Has this packet already been processed by a hardware
* IPsec accelerator?
*/
if (ii->ipsec_in_accelerated) {
("esp_inbound: pkt processed by ill=%d isv6=%d\n",
return (rv);
}
/*
* 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
{
netstack_t *ns;
/*
* 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.
*/
continue;
if (!(prot->ipp_use_esp))
continue;
if (prot->ipp_esp_auth_alg != 0) {
[prot->ipp_esp_auth_alg];
continue;
}
[prot->ipp_encr_alg];
continue;
comb->sadb_comb_flags = 0;
comb->sadb_comb_reserved = 0;
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
{
return;
}
/* Set up ACQUIRE. */
ns->netstack_ipsec);
esp0dbg(("sadb_setup_acquire failed.\n"));
return;
}
/* Insert proposal here. */
/*
* 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.
*/
}
}
/*
* 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) {
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. */
("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);
}
/*
* Section 7 of RFC 3947 says:
*
* 7. Recovering from the Expiring NAT Mappings
*
* There are cases where NAT box decides to remove mappings that are still
* alive (for example, when the keepalive interval is too long, or when the
* NAT box is rebooted). To recover from this, ends that are NOT behind
* NAT SHOULD use the last valid UDP encapsulated IKE or IPsec packet from
* the other end to determine which IP and port addresses should be used.
* The host behind dynamic NAT MUST NOT do this, as otherwise it opens a
* DoS attack possibility because the IP address or port of the other host
* will not change (it is not behind NAT).
*
* Keepalives cannot be used for these purposes, as they are not
* authenticated, but any IKE authenticated IKE packet or ESP packet can be
* used to detect whether the IP address or the port has changed.
*
* The following function will check an SA and its explicitly-set pair to see
* if the NAT-T remote port matches the received packet (which must have
* passed ESP authentication, see esp_in_done() for the caller context). If
* there is a mismatch, the SAs are updated. It is not important if we race
* with a transmitting thread, as if there is a transmitting thread, it will
* merely emit a packet that will most-likely be dropped.
*
* "ports" are ordered src,dst, and assoc is an inbound SA, where src should
* match ipsa_remote_nat_port and dst should match ipsa_local_nat_port.
*/
#ifdef _LITTLE_ENDIAN
#define FIRST_16(x) ((x) & 0xFFFF)
#else
#define NEXT_16(x) ((x) & 0xFFFF)
#endif
static void
{
/* We found a conn_t, therefore local != 0. */
/* Assume an IPv4 SA. */
/*
* On-the-wire rport == 0 means something's very wrong.
* An unpaired SA is also useless to us.
* If we are behind the NAT, don't bother.
* A zero local NAT port defaults to 4500, so check that too.
* And, of course, if the ports already match, we don't need to
* bother.
*/
(assoc->ipsa_remote_nat_port == 0 &&
return;
/* Try and snag the peer. NOTE: Assume IPv4 for now. */
assoc->ipsa_srcaddr[0]);
/* We probably lost a race to a deleting or expiring thread. */
if (outbound_peer == NULL)
return;
/*
* Hold the mutexes for both SAs so we don't race another inbound
* thread. A lock-entry order shouldn't matter, since all other
* per-ipsa locks are individually held-then-released.
*
* Luckily, this has nothing to do with the remote-NAT address,
* so we don't have to re-scribble the cached-checksum differential.
*/
}
/*
* 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 */
/* authentication-only ESP */
} else {
/* encryption present */
/* encryption-only ESP */
} else {
/* encryption with authentication */
}
}
/* 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;
}
if (is_natt)
}
/* 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.
*/
espstack)) {
if (is_natt)
return (IPSEC_STATUS_SUCCESS);
}
/*
* TODO: Extract inbound interface from the IPSEC_IN message's
* ii->ipsec_in_rill_index.
*/
&espstack->esp_dropper);
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.
*/
&espstack->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 (is_inbound) {
} else {
}
/*
* Verify that the netstack is still around; could have vanished
* while kEf was doing its work.
*/
/* Disappeared on us */
return;
}
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.
*/
/* NAT-T 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 {
("esp_kcf_callback: crypto failed with 0x%x\n",
status));
if (is_inbound)
else
&espstack->esp_dropper);
}
}
/*
* Invoked on crypto framework failure during inbound and outbound processing.
*/
static void
{
&espstack->esp_dropper);
if (is_inbound)
else
}
#define ESP_INIT_CALLREQ(_cr) { \
}
}
} else { \
} \
}
}
static ipsec_status_t
{
int kef_rc = CRYPTO_FAILED;
/*
* In case kEF queues and calls back, keep netstackid_t for
* verification that the IP instance is still around in
* esp_kcf_callback().
*/
/*
* An inbound packet is of the form:
* IPSEC_IN -> [IP,options,ESP,IV,data,ICV,pad]
*/
if (do_auth) {
/* force asynchronous processing? */
/* authentication context template */
/* ICV to be verified */
/* authentication starts at the ESP header */
if (!do_encr) {
/* authentication only */
/* initialize input data argument */
/* call the crypto framework */
}
}
if (do_encr) {
/* force asynchronous processing? */
/* 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 */
}
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);
}
/*
* Compute the IP and UDP checksums -- common code for both keepalives and
* actual ESP-in-UDP packets. Be flexible with multiple mblks because ESP
* uses mblk-insertion to insert the UDP header.
* TODO - If there is an easy way to prep a packet for HW checksums, make
* it happen here.
*/
static void
{
int offset;
ipha->ipha_hdr_checksum = 0;
/* arr points to the IP header. */
/* arr[6-9] are the IP addresses. */
}
/* arr points to the UDP header's checksum field. */
}
}
/*
* Send a one-byte UDP NAT-T keepalive. Construct an IPSEC_OUT too that'll
* get fed into esp_send_udp/ip_wput_ipsec_out.
*/
void
{
return;
ipha->ipha_type_of_service = 0;
/* Use the low-16 of the SPI so we have some clue where it came from. */
ipha->ipha_hdr_checksum = 0;
udpha->uha_checksum = 0;
return;
}
}
static ipsec_status_t
{
int kef_rc = CRYPTO_FAILED;
/*
* In case kEF queues and calls back, keep netstackid_t for
* verification that the IP instance is still around in
* esp_kcf_callback().
*/
/*
* 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]
* Get a pointer to the mblk containing the ESP header.
*/
if (do_auth) {
/* force asynchronous processing? */
/* authentication context template */
/* where to store the computed mac */
/* authentication starts at the ESP header */
if (!do_encr) {
/* authentication only */
/* initialize input data argument */
/* call the crypto framework */
}
}
if (do_encr) {
/* force asynchronous processing? */
/* 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 */
}
switch (kef_rc) {
case CRYPTO_SUCCESS:
if (is_natt)
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
{
netstack_t *ns;
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().
*/
&espstack->esp_dropper);
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. */
}
}
/* wedge in fake UDP */
}
/*
* Set up ESP header and encryption padding for ENCR PI request.
*/
/* Determine the padding length. Pad to 4-bytes for no-encryption. */
/*
* 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.
*/
} else {
iv_len = 0;
(sizeof (uint32_t) - 1);
}
/* Allocate ESP header and IV. */
/*
* Update association byte-count lifetimes. Don't forget to take
* into account the padding length and next-header (hence the + 2).
*
* 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).
*
*/
/*
* TODO: Find the outbound IRE for this packet and
* pass it to ip_drop_packet().
*/
&espstack->esp_dropper);
return (IPSEC_STATUS_FAILED);
}
/*
* TODO: Find the outbound IRE for this packet and
* pass it to ip_drop_packet().
*/
&espstack->esp_dropper);
return (IPSEC_STATUS_FAILED);
}
if (is_natt) {
/*
* Set the checksum to 0, so that the esp_prepare_udp() call
* 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().
*/
&espstack->esp_dropper);
return (IPSEC_STATUS_FAILED);
}
/*
* 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.
*/
/* 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. */
/* 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().
*/
&espstack->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().
*/
&espstack->esp_dropper);
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++;
}
/*
* 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!
*/
if (mac_len > 0) {
} else {
}
}
/*
* IP calls this to validate the ICMP errors that
* we got from the network.
*/
{
netstack_t *ns;
if (is_inbound) {
} else {
}
/*
* 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.
*/
&espstack->esp_dropper);
return (IPSEC_STATUS_FAILED);
}
/*
* ESP module read put routine.
*/
/* ARGSUSED */
static void
{
case M_PROTO:
case M_PCPROTO:
/* TPI message of some sort. */
case T_BIND_ACK:
("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. */
}
}
/*
* Construct an SADB_REGISTER message with the current algorithms.
*/
static boolean_t
{
uint_t i, numalgs_snap;
int current_aalgs;
int current_ealgs;
/* 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);
}
for (num_ealgs = 0, i = 0; i < IPSEC_MAX_ALGS; i++)
num_ealgs++;
if (num_ealgs != 0) {
allocsize += sizeof (*sasupp_encr);
}
return (B_FALSE);
}
if (num_aalgs != 0) {
sasupp_auth = (sadb_supported_t *)
numalgs_snap = 0;
for (i = 0;
i++) {
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 {
}
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 */
}
/* 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) {
}
if (sasupp_encr != 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
{
/*
* 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, espstack);
}
/*
* taskq_dispatch handler.
*/
static void
inbound_task(void *arg)
{
int ipsec_rc;
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;
/*
* Locate the appropriate table(s).
*/
if (is_ipv4) {
} else {
}
/*
* Use the direction flags provided by the KMD to determine
* if the inbound or outbound table should be the primary
* for this SA. If these flags were absent then make this
* decision based on the addresses.
*/
is_inbound = B_TRUE;
} else {
}
}
/*
* The KMD did not set a direction flag, determine which
* table to insert the SA into based on addresses.
*/
switch (ksi->ks_in_dsttype) {
case KS_IN_ADDR_MBCAST:
/* FALLTHRU */
/*
* 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).
*/
case KS_IN_ADDR_ME:
is_inbound = B_TRUE;
break;
/*
* 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.
*/
case KS_IN_ADDR_NOTME:
}
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) {
&espstack->esp_dropper);
}
/*
* 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;
}
}
&espstack->esp_dropper);
}
return (rc);
}
/*
* routine eventually.
*/
static int
{
/* I need certain extensions present for an ADD message. */
return (EINVAL);
}
return (EINVAL);
}
return (EINVAL);
}
return (EINVAL);
}
return (EINVAL);
}
return (EINVAL);
}
/* Sundry ADD-specific reality checks. */
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.
*/
[assoc->sadb_sa_auth];
assoc->sadb_sa_auth));
return (EINVAL);
}
/*
* Sanity check key sizes.
* Note: It's not possible to use SADB_AALG_NONE because
* this auth_alg is not defined with ALG_FLAG_VALID. If this
* ever changes, the same check for SADB_AALG_NONE and
* a auth_key != NULL should be made here ( see below).
*/
return (EINVAL);
}
/* check key and fix parity if needed */
diagnostic) != 0) {
return (EINVAL);
}
}
/*
* Then locate the encryption algorithm.
*/
[assoc->sadb_sa_encrypt];
assoc->sadb_sa_encrypt));
return (EINVAL);
}
/*
* Sanity check key sizes. If the encryption algorithm is
* SADB_EALG_NULL but the encryption key is NOT
* NULL then complain.
*/
return (EINVAL);
}
/* check key */
diagnostic) != 0) {
return (EINVAL);
}
}
diagnostic, espstack));
}
/*
* 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
{
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:
}
/*
* First-cut reality check for an inbound PF_KEY message.
*/
static boolean_t
{
int diagnostic;
goto badmsg;
}
goto badmsg;
}
return (B_FALSE); /* False ==> no failures */
ksi->ks_in_serial);
return (B_TRUE); /* True ==> failures */
}
/*
* 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. And do other address reality checks.
*/
return;
}
switch (samsg->sadb_msg_type) {
case SADB_ADD:
if (error != 0) {
}
/* else esp_add_sa() took care of things. */
break;
case SADB_DELETE:
case SADB_X_DELPAIR:
if (error != 0) {
}
/* Else esp_del_sa() took care of things. */
break;
case SADB_GET:
if (error != 0) {
}
/* 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.
*/
} 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().
*/
}
break;
case SADB_UPDATE:
case SADB_X_UPDATEPAIR:
/*
* Find a larval, if not there, find a full one and get
* strict.
*/
if (error != 0) {
}
/* 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. */
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.
*/
}
}
/*
* ESP module write put routine.
*/
static void
{
/* 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:
break;
case KEYSOCK_IN:
/* Parse the message. */
break;
case KEYSOCK_HELLO:
break;
default:
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:
("Got default message, type %d, passing to IP.\n",
}
}
/*
* 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
{
netstack_t *ns;
/* 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 */
&espstack->esp_dropper);
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
{
/*
* 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;
}
"succeeded, checking replay\n"));
/*
* Remove ESP header and padding from packet.
*/
"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",
&espstack->esp_dropper);
return (IPSEC_STATUS_FAILED);
}
/* done processing the packet */
return (IPSEC_STATUS_SUCCESS);
&espstack->esp_dropper);
return (IPSEC_STATUS_FAILED);
}
/*
* Wrapper to allow IP to trigger an ESP association failure message
* during inbound SA selection.
*/
void
{
if (espstack->ipsecesp_log_unknown_spi) {
}
&espstack->esp_dropper);
}
/*
* Initialize the ESP input and output processing functions.
*/
void
{
}