ipsecah.c revision 7ba9381bba98b75a603ea95f9978a4cb870e8e0e
/*
* 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.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <sys/sysmacros.h>
#include <inet/ipsec_info.h>
#include <inet/ipsec_impl.h>
#include <inet/ipsec_impl.h>
/*
* Table of ND variables supported by ipsecah. These are loaded into
* ipsecah_g_nd in ipsecah_init_nd.
*/
static ipsecahparam_t lcl_param_arr[] = {
/* min max value name */
{ 0, 3, 0, "ipsecah_debug"},
{ 1, 10, 1, "ipsecah_reap_delay"},
{ 1, 300, 15, "ipsecah_acquire_timeout"},
{ 1, 1800, 90, "ipsecah_larval_timeout"},
/* Default lifetime values for ACQUIRE messages. */
{ 0, 0xffffffffU, 0, "ipsecah_default_soft_bytes"},
{ 0, 0xffffffffU, 0, "ipsecah_default_hard_bytes"},
{ 0, 0xffffffffU, 24000, "ipsecah_default_soft_addtime"},
{ 0, 0xffffffffU, 28800, "ipsecah_default_hard_addtime"},
{ 0, 0xffffffffU, 0, "ipsecah_default_soft_usetime"},
{ 0, 0xffffffffU, 0, "ipsecah_default_hard_usetime"},
{ 0, 1, 0, "ipsecah_log_unknown_spi"},
};
/* NOTE: != 0 instead of > 0 so lint doesn't complain. */
/*
* XXX This is broken. Padding should be determined dynamically
* depending on the ICV size and IP version number so that the
* total AH header size is a multiple of 32 bits or 64 bits
* for V4 and V6 respectively. For 96bit ICVs we have no problems.
* Anything different from that, we need to fix our code.
*/
/*
* Helper macro. Avoids a call to msgdsize if there is only one
* mblk in the chain.
*/
boolean_t, ipsecah_stack_t *);
boolean_t, ipsecah_stack_t *);
uint32_t);
static int ipsecah_close(queue_t *);
static struct module_info info = {
};
};
};
struct streamtab ipsecahinfo = {
};
static int ah_kstat_update(kstat_t *, int);
static boolean_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);
}
/*
* Don't have to lock ipsec_age_interval, as only one thread will access it at
* a time, because I control the one function that does a qtimeout() on
* ah_pfkey_q.
*/
static void
{
}
/*
* Get an AH NDD parameter.
*/
/* ARGSUSED */
static int
queue_t *q;
{
return (0);
}
/*
* This routine sets an NDD variable in a ipsecahparam_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 AH at module load time.
*/
ipsecah_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 ipsecah_stack_t's.
*/
return (B_TRUE);
}
/*
* Walk through the param array specified registering each element with the
* named dispatch handler.
*/
static boolean_t
{
ahp->ipsecah_param_name[0]) {
return (B_FALSE);
}
}
}
return (B_TRUE);
}
/*
* Initialize things for AH for each stack instance
*/
static void *
{
return (ahstack);
}
/*
* Destroy things for AH at module unload time.
*/
void
ipsecah_ddi_destroy(void)
{
}
/*
* Destroy things for AH for one stack... Never called?
*/
static void
{
}
}
/*
* AH module open routine. The module should be opened by keysock.
*/
/* ARGSUSED */
static int
{
netstack_t *ns;
ah0dbg(("Non-privileged user trying to open ipsecah.\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 ipsecah_open() can write into ah_sadb.s_ip_q.
* * Because of this, I can check lazily for ah_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_AH.
* Handle the ACK here in AH.
*/
qprocson(q);
}
qprocsoff(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);
}
/*
* AH module close routine.
*/
static int
ipsecah_close(queue_t *q)
{
/*
* If ah_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 == ahstack->ah_pfkey_q) {
("ipsecah_close: Ummm... keysock is closing AH.\n"));
/* Detach qtimeouts. */
}
/*
* If the ah_sadb.s_ip_q is attached to this instance, find
* another. The OCEXCL outer perimeter helps us here.
*/
/*
* Find a replacement queue for ah_sadb.s_ip_q.
*/
/*
* See if we can use the pfkey_q.
*/
}
("ipsecah: Can't reassign ah_sadb.s_ip_q.\n"));
} else {
struct T_unbind_req *tur;
tur = (struct T_unbind_req *)
}
/* If it's NULL, I can't do much here. */
}
}
return (0);
}
/*
* AH module read put routine.
*/
/* ARGSUSED */
static void
{
case M_PROTO:
case M_PCPROTO:
/* TPI message of some sort. */
case T_BIND_ACK:
/* We expect this. */
("Thank you IP from AH for T_BIND_ACK\n"));
break;
case T_ERROR_ACK:
"ipsecah: AH received T_ERROR_ACK from IP.");
break;
case T_OK_ACK:
/* Probably from a (rarely sent) T_UNBIND_REQ. */
break;
default:
}
break;
default:
/* For now, passthru message. */
}
}
/*
* Construct an SADB_REGISTER message with the current algorithms.
*/
static boolean_t
{
uint_t i, numalgs_snap;
/* Allocate the KEYSOCK_OUT. */
ah0dbg(("ah_register_out: couldn't allocate mblk.\n"));
return (B_FALSE);
}
/*
* Allocate the PF_KEY message that follows KEYSOCK_OUT.
* The alg reader lock needs to be held while allocating
* the variable part (i.e. the algorithms) of the message.
*/
/*
* 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++;
/*
* Fill SADB_REGISTER message's algorithm descriptors. Hold
* down the lock while filling it.
*/
if (num_aalgs != 0) {
}
return (B_FALSE);
}
if (num_aalgs != 0) {
sizeof (*sasupp));
numalgs_snap = 0;
for (i = 0;
i++) {
continue;
saalg->sadb_alg_ivlen = 0;
authalgs[i]->alg_increment;
authalgs[i]->alg_ef_default;
numalgs_snap++;
saalg++;
}
#ifdef DEBUG
/*
* Reality check to make sure I snagged all of the
* algorithms.
*/
for (; i < IPSEC_MAX_ALGS; i++)
"ah_register_out()! Missed #%d.\n", i);
#endif /* DEBUG */
}
/* Now fill the restof 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.
*/
}
else {
}
return (rc);
}
/*
* Invoked when the algorithm table changes. Causes SADB_REGISTER
* messages continaining the current list of algorithms to be
* sent up to the AH listeners.
*/
void
{
/*
* Time to send a PF_KEY SADB_REGISTER message to AH listeners
* everywhere. (The function itself checks for NULL ah_pfkey_q.)
*/
(void) ah_register_out(0, 0, 0, ahstack);
}
/*
* Stub function that taskq_dispatch() invokes to take the mblk (in arg)
* and put() it into AH and STREAMS again.
*/
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 and 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).
*/
ah0dbg(("Larval update, but larval disappeared.\n"));
return (ESRCH);
} /* Else sadb_common_add unlinks it for me! */
}
/*
* How much more stack will I create with all of these
* ah_inbound_* and ah_outbound_*() calls?
*/
(void *) lpkt, TQ_NOSLEEP);
if (rc != 0) {
&ahstack->ah_dropper);
}
if (rc == 0) {
if (is_ipv4) {
} else {
}
}
continue;
}
}
&ahstack->ah_dropper);
}
return (rc);
}
/*
* routine eventually.
*/
static int
{
/* We don't need sockaddr_in6 for now. */
/* 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);
}
if (assoc->sadb_sa_flags &
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.
*/
/* verify that there is a mapping for the specified algorithm */
assoc->sadb_sa_auth));
return (EINVAL);
}
/* sanity check key sizes */
return (EINVAL);
}
/* check key and fix parity if needed */
diagnostic) != 0) {
return (EINVAL);
}
diagnostic, ahstack));
}
/*
* 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 AH'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;
return (B_TRUE);
}
goto badmsg;
}
goto badmsg;
}
goto badmsg;
}
return (B_FALSE); /* False ==> no failures */
return (B_TRUE); /* True ==> failures */
}
/*
* AH 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.
*/
ahstack->ipsecah_netstack) ||
return;
}
switch (samsg->sadb_msg_type) {
case SADB_ADD:
if (error != 0) {
}
/* else ah_add_sa() took care of things. */
break;
case SADB_DELETE:
case SADB_X_DELPAIR:
if (error != 0) {
}
/* Else ah_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 ah_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 ah_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 ah_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.
*/
/* ah_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 ah_pfkey_q, in case there is
* no ahstack->ah_sadb.s_ip_q.
*/
}
}
/*
* AH 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",
}
}
/*
* 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 (isv6) {
} else {
}
if (inbound) {
if (isv6)
else
/* Q: Do we wish to set haspeer == B_FALSE? */
ah0dbg(("ah_set_usetime: "
"can't find peer for inbound.\n"));
return;
}
} else {
/* Q: Do we wish to set haspeer == B_FALSE? */
ah0dbg(("ah_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 {
}
}
/*
* 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 (isv6) {
} else {
}
if (inbound) {
if (isv6)
else
/* Q: Do we wish to set haspeer == B_FALSE? */
ah0dbg(("ah_age_bytes: "
"can't find peer for inbound.\n"));
}
} else {
/* Q: Do we wish to set haspeer == B_FALSE? */
ah0dbg(("ah_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 {
}
}
/*
* 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, based on the ordering of the ah algorithms in the
* alternatives presented in the policy rule passed down
* through the ipsec_out_t and attached to the acquire record.
*/
continue;
[prot->ipp_auth_alg];
continue;
/* XXX check aalg for duplicates??.. */
comb->sadb_comb_flags = 0;
comb->sadb_comb_reserved = 0;
comb->sadb_comb_encrypt = 0;
/*
* 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)
return; /* out of space.. */
comb++;
}
}
/*
* Prepare and actually send the SADB_ACQUIRE message to PF_KEY.
*/
static void
{
return;
}
/* Set up ACQUIRE. */
ns->netstack_ipsec);
ah0dbg(("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 larval 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 ah_pfkey_q, because this is a turnaround
* from the ah_pfkey_q.
*/
}
/*
* IPv6 sends up the ICMP errors for validation and the removal of the AH
* header.
*/
static ipsec_status_t
{
/*
* Change the type to M_DATA till we finish pullups.
*/
/*
* Eat the cost of a pullupmsg() for now. It makes the rest of this
* code far less convoluted.
*/
&nexthdrp) ||
&ahstack->ah_dropper);
return (IPSEC_STATUS_FAILED);
}
&ahstack->ah_dropper);
return (IPSEC_STATUS_FAILED);
}
if (ahstack->ipsecah_log_unknown_spi) {
"Bad ICMP message - No association for the "
"attached AH header whose spi is 0x%x, "
"sender is 0x%x\n",
}
&ahstack->ah_dropper);
return (IPSEC_STATUS_FAILED);
}
/*
* There seems to be a valid association. If there is enough of AH
* header remove it, otherwise bail. One could check whether it has
* complete AH header plus 8 bytes but it does not make sense if an
* icmp error is returned for ICMP messages e.g ICMP time exceeded,
* that are being sent up. Let the caller figure out.
*
* NOTE: ah_length is the number of 32 bit words minus 2.
*/
&ahstack->ah_dropper);
return (IPSEC_STATUS_FAILED);
}
/* Rewhack to be an ICMP error. */
return (IPSEC_STATUS_SUCCESS);
}
/*
* IP sends up the ICMP errors for validation and the removal of
* the AH header.
*/
static ipsec_status_t
{
int iph_hdr_length;
int hdr_length;
int ah_length;
int alloc_size;
/*
* Change the type to M_DATA till we finish pullups.
*/
/*
* See if we have enough to locate the SPI
*/
"ICMP error: Small AH header\n");
&ahstack->ah_dropper);
return (IPSEC_STATUS_FAILED);
}
}
if (ahstack->ipsecah_log_unknown_spi) {
"Bad ICMP message - No association for the "
"attached AH header whose spi is 0x%x, "
"sender is 0x%x\n",
}
&ahstack->ah_dropper);
return (IPSEC_STATUS_FAILED);
}
/*
* There seems to be a valid association. If there
* is enough of AH header remove it, otherwise remove
* as much as possible and send it back. One could check
* whether it has complete AH header plus 8 bytes but it
* does not make sense if an icmp error is returned for
* ICMP messages e.g ICMP time exceeded, that are being
* sent up. Let the caller figure out.
*
* NOTE: ah_length is the number of 32 bit words minus 2.
*/
/*
* There is nothing to pullup. Just remove as
* much as possible. This is a common case for
* IPV4.
*/
hdr_length));
goto done;
}
/* Pullup the full ah header */
/*
* pullupmsg could have failed if there was not
* enough to pullup or memory allocation failed.
* We tried hard, give up now.
*/
&ahstack->ah_dropper);
return (IPSEC_STATUS_FAILED);
}
}
done:
/*
* Remove the AH header and change the protocol.
* Don't update the spi fields in the ipsec_in
* message as we are called just to validate the
* message attached to the ICMP message.
*
* If we never pulled up since all of the message
* is in one single mblk, we can't remove the AH header
* by just setting the b_wptr to the beginning of the
* AH header. We need to allocate a mblk that can hold
* up until the inner IP header and copy them.
*/
&ahstack->ah_dropper);
return (IPSEC_STATUS_FAILED);
}
/* ICMP errors are M_CTL messages */
/*
* Skip whatever we have copied and as much of AH header
* possible. If we still have something left in the original
* message, tag on.
*/
} else {
}
ipha->ipha_hdr_checksum = 0;
return (IPSEC_STATUS_SUCCESS);
}
/*
* IP calls this to validate the ICMP errors that
* we got from the network.
*/
{
if (ii->ipsec_in_v4)
else
}
static int
{
/*
* Copy the next header and hdr ext. len of the HOP-by-HOP
* and Destination option.
*/
ehdrlen -= 2;
/*
* Now handle all the TLV encoded options.
*/
while (ehdrlen != 0) {
if (opt_type == IP6OPT_PAD1) {
optlen = 1;
} else {
if (ehdrlen < 2)
goto bad_opt;
goto bad_opt;
}
} else {
if (optlen == 1) {
*pi_opt = 0;
} else {
/*
* Copy the type and data length fields.
* Zero the option data by skipping
* option type and option data len
* fields.
*/
}
}
}
return (0);
return (-1);
}
/*
* Construct a pseudo header for AH, processing all the options.
*
* oip6h is the IPv6 header of the incoming or outgoing packet.
* ip6h is the pointer to the pseudo headers IPV6 header. All
* the space needed for the options have been allocated including
* the AH header.
*
* If copy_always is set, all the options that appear before AH are copied
* blindly without checking for IP6OPT_MUTABLE. This is used by
* ah_auth_out_done(). Please refer to that function for details.
*
* NOTE :
*
* * AH header is never copied in this function even if copy_always
* is set. It just returns the ah_offset - offset of the AH header
* and the caller needs to do the copying. This is done so that we
* don't have pass extra arguments e.g. SA etc. and also,
* it is not needed when ah_auth_out_done is calling this function.
*/
static uint_t
{
int ehdrlen;
int ret;
/*
* In the outbound case for source route, ULP has already moved
* the first hop, which is now in ip6_dst. We need to re-arrange
* the header to make it look like how it would appear in the
* receiver i.e
*
* Because of ip_massage_options_v6 the header looks like
* this :
*
* ip6_src = S, ip6_dst = I1. followed by I2,I3,D.
*
* When it reaches the receiver, it would look like
*
* ip6_src = S, ip6_dst = D. followed by I1,I2,I3.
*
* NOTE : We assume that there are no problems with the options
* as IP should have already checked this.
*/
/*
* We set the prev_nexthdr properly in the pseudo header.
* After we finish authentication and come back from the
* algorithm module, pseudo header will become the real
* IP header.
*/
/* Assume IP has already stripped it */
for (;;) {
switch (nexthdr) {
case IPPROTO_HOPOPTS:
/*
* Return a zero offset indicating error if there
* was error.
*/
if (ret == -1)
return (0);
break;
case IPPROTO_ROUTING:
if (!copy_always && outbound) {
int i, left;
/*
* First eight bytes except seg_left
* does not change en route.
*/
prthdr->ip6r0_segleft = 0;
/*
* First address has been moved to
* the destination address of the
* ip header by ip_massage_options_v6.
* And the real destination address is
* in the last address part of the
* option.
*/
} else {
}
break;
case IPPROTO_DSTOPTS:
/*
* Destination options are tricky. If there is
* a terminal (e.g. non-IPv6-extension) header
* following the destination options, don't
* reset prev_nexthdr or advance the AH insertion
* point and just treat this as a terminal header.
*
* If this is an inbound packet, just deal with
* it as is.
*/
/*
* XXX I hope common-subexpression elimination
* saves us the double-evaluate.
*/
goto terminal_hdr;
/*
* Return a zero offset indicating error if there
* was error.
*/
if (ret == -1)
return (0);
break;
case IPPROTO_AH:
/*
* Be conservative in what you send. We shouldn't
* see two same-scoped AH's in one packet.
* (Inner-IP-scoped AH will be hit by terminal
* header of IP or IPv6.)
*/
default:
}
}
/* NOTREACHED */
}
static boolean_t
{
int i;
/*
* Padding :
*
* 1) Authentication data may have to be padded
* before ICV calculation if ICV is not a multiple
* of 64 bits. This padding is arbitrary and transmitted
* with the packet at the end of the authentication data.
* Payload length should include the padding bytes.
*
* 2) Explicit padding of the whole datagram may be
* required by the algorithm which need not be
* transmitted. It is assumed that this will be taken
* care by the algorithm module.
*/
if (inbound_ah == NULL) {
/* Outbound AH datagram. */
phdr_ah->ah_reserved = 0;
/*
* XXX We have replay counter wrapping. We probably
* want to nuke this SA (and its peer).
*/
"Outbound AH SA (0x%x), dst %s has wrapped "
/* Caller will free phdr_mp and return NULL. */
return (B_FALSE);
}
if (ah_data_sz != ah_align_sz) {
for (i = 0; i < (ah_align_sz - ah_data_sz); i++) {
}
}
} else {
/* Inbound AH datagram. */
phdr_ah->ah_reserved = 0;
if (ah_data_sz != ah_align_sz) {
sizeof (ah_t) + ah_data_sz);
for (i = 0; i < (ah_align_sz - ah_data_sz); i++) {
}
}
}
return (B_TRUE);
}
/*
* Called upon failing the inbound ICV check. The message passed as
* argument is freed.
*/
static void
{
int af;
void *addr;
if (isv4) {
} else {
}
/*
* Log the event. Don't print to the console, block
* potential denial-of-service attack.
*/
"AH Authentication failed spi %x, dst_addr %s",
&ahstack->ah_dropper);
}
/*
* 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 (ah_auth_out_done(ipsec_mp) !=
return;
}
/* finish IPsec processing */
NULL);
} else {
}
}
} else if (status == CRYPTO_INVALID_MAC) {
} else {
status));
if (is_inbound)
else
&ahstack->ah_dropper);
}
}
/*
* Invoked on kernel crypto failure during inbound and outbound processing.
*/
static void
{
&ahstack->ah_dropper);
if (is_inbound)
else
}
/*
* Helper macros for the ah_submit_req_{inbound,outbound}() functions.
*/
}
}
}
/*
* Submit an inbound packet for processing by the crypto framework.
*/
static ipsec_status_t
{
int kef_rc;
/*
* In case kEF queues and calls back, keep netstackid_t for
* verification that the IP instance is still around in
* ah_kcf_callback().
*/
/* init arguments for the crypto framework */
phdr_mp);
sizeof (ah_t));
/* call KEF to do the MAC operation */
switch (kef_rc) {
case CRYPTO_SUCCESS:
return (ah_auth_in_done(ipsec_mp));
case CRYPTO_QUEUED:
/* ah_kcf_callback() will be invoked on completion */
return (IPSEC_STATUS_PENDING);
case CRYPTO_INVALID_MAC:
return (IPSEC_STATUS_FAILED);
}
return (IPSEC_STATUS_FAILED);
}
/*
* Submit an outbound packet for processing by the crypto framework.
*/
static ipsec_status_t
{
int kef_rc;
/*
* In case kEF queues and calls back, keep netstackid_t for
* verification that the IP instance is still around in
* ah_kcf_callback().
*/
/* init arguments for the crypto framework */
phdr_mp);
/* call KEF to do the MAC operation */
switch (kef_rc) {
case CRYPTO_SUCCESS:
return (ah_auth_out_done(ipsec_mp));
case CRYPTO_QUEUED:
/* ah_kcf_callback() will be invoked on completion */
return (IPSEC_STATUS_PENDING);
}
return (IPSEC_STATUS_FAILED);
}
/*
* This function constructs a pseudo header by looking at the IP header
* and options if any. This is called for both outbound and inbound,
* before computing the ICV.
*/
static mblk_t *
{
int option_length;
int hdr_size;
/*
* Allocate space for the authentication data also. It is
* useful both during the ICV calculation where we need to
* feed in zeroes and while sending the datagram back to IP
* where we will be using the same space.
*
* We need to allocate space for padding bytes if it is not
* a multiple of IPV6_PADDING_ALIGN.
*
* In addition, we allocate space for the ICV computed by
* the kernel crypto framework, saving us a separate kmem
* allocation down the road.
*/
/* This was not included in ipsec_ah_get_hdr_size_v6() */
/*
* We have post-AH header options in a separate mblk,
* a pullup is required.
*/
return (NULL);
}
return (NULL);
}
/*
* Form the basic IP header first. Zero out the header
* so that the mutable fields are zeroed out.
*/
if (outbound) {
/*
* Include the size of AH and authentication data.
* This is how our recipient would compute the
* authentication data. Look at what we do in the
* inbound case below.
*/
sizeof (ah_t) + ah_align_sz);
} else {
}
if (option_length == 0) {
/* Form the AH header */
} else {
/* option_length does not include the AH header's size */
if (ah_offset == 0) {
&ahstack->ah_dropper);
return (NULL);
}
}
/*
* Returning NULL will tell the caller to
* IPSA_REFELE(), free the memory, etc.
*/
return (NULL);
}
if (!outbound)
return (phdr_mp);
}
/*
* This function constructs a pseudo header by looking at the IP header
* and options if any. This is called for both outbound and inbound,
* before computing the ICV.
*/
static mblk_t *
{
int size;
int ip_hdr_length;
#ifdef _BIG_ENDIAN
#else
#endif
/*
* Allocate space for the authentication data also. It is
* useful both during the ICV calculation where we need to
* feed in zeroes and while sending the datagram back to IP
* where we will be using the same space.
*
* We need to allocate space for padding bytes if it is not
* a multiple of IPV4_PADDING_ALIGN.
*
* In addition, we allocate space for the ICV computed by
* the kernel crypto framework, saving us a separate kmem
* allocation down the road.
*/
if (V_HLEN != IP_SIMPLE_HDR_VERSION) {
option_length <<= 2;
size += option_length;
}
return (NULL);
}
/*
* Form the basic IP header first.
*/
ipha->ipha_type_of_service = 0;
if (outbound) {
/*
* Include the size of AH and authentication data.
* This is how our recipient would compute the
* authentication data. Look at what we do in the
* inbound case below.
*/
sizeof (ah_t) + ah_align_sz);
} else {
}
ipha->ipha_hdr_checksum = 0;
/*
* If there is no option to process return now.
*/
if (V_HLEN == IP_SIMPLE_HDR_VERSION) {
/* Form the AH header */
goto ah_hdr;
}
/*
* We have options. In the outbound case for source route,
* ULP has already moved the first hop, which is now in
* ipha_dst. We need the final destination for the calculation
* of authentication data. And also make sure that mutable
* and experimental fields are zeroed out in the IP options.
*/
switch (optval) {
case IPOPT_EXTSEC:
case IPOPT_COMSEC:
case IPOPT_RA:
case IPOPT_SDMDD:
case IPOPT_SECURITY:
/*
* These options are Immutable, leave them as-is.
* Note that IPOPT_NOP is also Immutable, but it
* was skipped by ipoptp_next() and thus remains
* intact in the header.
*/
break;
case IPOPT_SSRR:
case IPOPT_LSRR:
goto bad_ipv4opt;
/*
* These two are mutable and will be zeroed, but
* first get the final destination.
*/
/*
* If one of the conditions is true, it means
* end of options and dst already has the right
* value. So, just fall through.
*/
}
/* FALLTHRU */
case IPOPT_RR:
case IPOPT_TS:
case IPOPT_SATID:
default:
/*
* optlen should include from the beginning of an
* option.
* NOTE : Stream Identifier Option (SID): RFC 791
* shows the bit pattern of optlen as 2 and documents
* the length as 4. We assume it to be 2 here.
*/
break;
}
}
return (NULL);
}
/*
* Don't change ipha_dst for an inbound datagram as it points
* because of ip_massage_options called by the ULP, ipha_dst
* points to the first hop and we need to use the final
* destination for computing the ICV.
*/
if (outbound)
/*
* Returning NULL will tell the caller to IPSA_REFELE(), free
* the memory, etc.
*/
return (NULL);
}
sizeof (ah_t) + ah_align_sz);
if (outbound)
else
return (phdr_mp);
}
/*
* Authenticate an outbound datagram. This function is called
* whenever IP sends an outbound datagram that needs authentication.
*/
static ipsec_status_t
{
int length_to_skip;
netstack_t *ns;
/*
* Construct the chain of mblks
*
* IPSEC_OUT->PSEUDO_HDR->DATA
*
* one by one.
*/
/*
* Age SA according to number of bytes that will be sent after
* adding the AH header, ICV, and padding to the packet.
*/
if (oi->ipsec_out_v4) {
} else {
sizeof (ah_t) + ah_align_sz;
}
/* rig things as if ipsec_getassocbyconn() failed */
"AH association 0x%x, dst %s had bytes expire.\n",
return (IPSEC_STATUS_FAILED);
}
if (oi->ipsec_out_is_capab_ill) {
if (oi->ipsec_out_v4)
else
}
/*
* Insert pseudo header:
* IPSEC_INFO -> [IP, ULP] => IPSEC_INFO -> [IP, AH, ICV] -> ULP
*/
if (oi->ipsec_out_v4) {
} else {
}
&ahstack->ah_dropper);
return (IPSEC_STATUS_FAILED);
}
/*
* At this point ipsec_out points to the IPSEC_OUT, new_mp
* points to an mblk containing the pseudo header (IP header,
* AH header, and ICV with mutable fields zero'ed out).
* mp points to the mblk containing the ULP data. The original
* IP header is kept before the ULP data in mp.
*/
/* submit MAC request to KCF */
}
static ipsec_status_t
{
int length_to_skip;
int ah_length;
/*
* 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.
*/
&ahstack->ah_dropper);
return (IPSEC_STATUS_FAILED);
}
/*
* The offset of the AH header can be computed from its pointer
* within the data mblk, which was pulled up until the AH header
* by ipsec_inbound_ah_sa() during SA selection.
*/
/*
* Has this packet already been processed by a hardware
* IPsec accelerator?
*/
if (ii->ipsec_in_accelerated) {
("ah_inbound_v6: pkt processed by ill=%d isv6=%d\n",
}
/*
* We need to pullup until the ICV before we call
* ah_process_ip_options_v6.
*/
/*
* to re-assign following the pullup.
*/
"ah_inbound: Small AH header\n");
&ahstack->ah_dropper);
return (IPSEC_STATUS_FAILED);
}
}
/*
* Insert pseudo header:
* IPSEC_INFO -> [IP, ULP] => IPSEC_INFO -> [IP, AH, ICV] -> ULP
*/
if (ii->ipsec_in_v4) {
} else {
}
(ii->ipsec_in_v4 ?
&ahstack->ah_dropper);
return (IPSEC_STATUS_FAILED);
}
/* submit request to KCF */
assoc));
}
/*
* ah_inbound_accelerated:
* Called from ah_inbound() to process IPsec packets that have been
* accelerated by hardware.
*
* Basically does what ah_auth_in_done() with some changes since
* no pseudo-headers are involved, i.e. the passed message is a
* IPSEC_INFO->DATA.
*
* It is assumed that only packets that have been successfully
* processed by the adapter come here.
*
* 1. get algorithm structure corresponding to association
* 2. calculate pointers to authentication header and ICV
* 3. compare ICV in AH header with ICV in data attributes
* 3.1 if different:
* 3.1.1 generate error
* 3.1.2 discard message
* 3.2 if ICV matches:
* 3.2.1 check replay
* 3.2.2 remove AH header
* 3.2.3 age SA byte
* 3.2.4 send to IP
*/
{
ipsec_in_t *ii;
netstack_t *ns;
/*
* We only support one level of decapsulation in hardware, so
* nuke the pointer.
*/
/*
* 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 algorithm specified by the SA.
*/
ah0dbg(("ah_inbound_accelerated: "
"ICV len (%u) incorrect or mblk too small (%u)\n",
goto ah_in_discard;
}
/* compute the padded AH ICV len */
if (isv4) {
} else {
}
/* compare ICV in AH header vs ICV computed by adapter */
int af;
void *addr;
if (isv4) {
} else {
}
/*
* Log the event. Don't print to the console, block
* potential denial-of-service attack.
*/
"AH Authentication failed spi %x, dst_addr %s",
goto ah_in_discard;
}
int af;
void *addr;
if (isv4) {
} else {
}
/*
* 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 (or printing to the
* console) opens a denial-of-service attack.
*/
"Replay failed for AH spi %x, dst_addr %s",
goto ah_in_discard;
}
/*
* Remove AH header. We do this by copying everything before
* the AH header onto the AH header+ICV.
*/
/* overwrite AH with what was preceeding it (IP header) */
if (isv4) {
/* adjust IP header next protocol */
/* adjust length in IP header */
/* recalculate checksum */
ipha->ipha_hdr_checksum = 0;
} else {
/* adjust IP header next protocol */
sizeof (ah_t);
/* adjust length in IP header */
}
/* age SA */
/* The ipsa has hit hard expiration, LOG and AUDIT. */
"AH Association 0x%x, dst %s had bytes expire.\n",
goto ah_in_discard;
}
return (IPSEC_STATUS_SUCCESS);
&ahstack->ah_dropper);
return (IPSEC_STATUS_FAILED);
}
/*
* ah_outbound_accelerated_v4:
* Called from ah_outbound_v4() and once it is determined that the
* packet is elligible for hardware acceleration.
*
* We proceed as follows:
* 1. allocate and initialize attributes mblk
* 2. mark IPSEC_OUT to indicate that pkt is accelerated
* 3. insert AH header
*/
static ipsec_status_t
{
uint_t option_length = 0;
netstack_t *ns;
/* mark packet as being accelerated in IPSEC_OUT */
/* calculate authentication data length, i.e. ICV + padding */
/*
* Insert pseudo header:
* IPSEC_INFO -> [IP, ULP] => IPSEC_INFO -> [IP, AH, ICV] -> ULP
*/
/* IP + AH + authentication + padding data length */
if (V_HLEN != IP_SIMPLE_HDR_VERSION) {
option_length <<= 2;
}
/* allocate pseudo-header mblk */
/* IPsec kstats: bump bean counter here */
&ahstack->ah_dropper);
return (IPSEC_STATUS_FAILED);
}
/* copy original IP header to new header */
/* update IP header */
nipha->ipha_hdr_checksum = 0;
/* skip original IP header in mp */
/* initialize AH header */
ahstack)) {
/* Only way this fails is if outbound replay counter wraps. */
&ahstack->ah_dropper);
return (IPSEC_STATUS_FAILED);
}
return (IPSEC_STATUS_SUCCESS);
}
/*
* ah_outbound_accelerated_v6:
*
* Called from ah_outbound_v6() once it is determined that the packet
* is eligible for hardware acceleration.
*
* We proceed as follows:
* 1. allocate and initialize attributes mblk
* 2. mark IPSEC_OUT to indicate that pkt is accelerated
* 3. insert AH header
*/
static ipsec_status_t
{
uint_t option_length = 0;
netstack_t *ns;
/* mark packet as being accelerated in IPSEC_OUT */
/* calculate authentication data length, i.e. ICV + padding */
/* This was not included in ipsec_ah_get_hdr_size_v6() */
&ahstack->ah_dropper);
return (IPSEC_STATUS_FAILED);
}
/*
* Form the basic IP header first. We always assign every bit
* of the v6 basic header, so a separate bzero is unneeded.
*/
/*
* Include the size of AH and authentication data.
* This is how our recipient would compute the
* authentication data. Look at what we do in the
* inbound case below.
*/
/*
* Insert pseudo header:
* IPSEC_INFO -> [IP6, LLH, ULP] =>
* IPSEC_INFO -> [IP, LLH, AH, ICV] -> ULP
*/
if (option_length == 0) {
/* Form the AH header */
} else {
/* option_length does not include the AH header's size */
if (ah_offset == 0) {
&ahstack->ah_dropper);
return (IPSEC_STATUS_FAILED);
}
}
/* skip original IP header in mp */
/* initialize AH header */
/* Only way this fails is if outbound replay counter wraps. */
&ahstack->ah_dropper);
return (IPSEC_STATUS_FAILED);
}
return (IPSEC_STATUS_SUCCESS);
}
/*
* Invoked after processing of an inbound packet by the
* kernel crypto framework. Called by ah_submit_req() for a sync request,
* or by the kcf callback for an async request.
* Returns IPSEC_STATUS_SUCCESS on success, IPSEC_STATUS_FAILED on failure.
* On failure, the mblk chain ipsec_in is freed by this function.
*/
static ipsec_status_t
{
ipsec_in_t *ii;
netstack_t *ns;
&ahstack->ah_dropper);
return (IPSEC_STATUS_FAILED);
}
&ahstack->ah_dropper);
return (IPSEC_STATUS_FAILED);
}
if (isv4) {
ah_offset <<= 2;
} else {
}
/*
* We get here only when authentication passed.
*/
int af;
void *addr;
if (isv4) {
} else {
}
/*
* 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 (or printing to the
* console) opens a denial-of-service attack.
*/
"Replay failed for AH spi %x, dst_addr %s",
goto ah_in_discard;
}
/*
* We need to remove the AH header from the original
* datagram. Best way to do this is to move the pre-AH headers
* forward in the (relatively simple) IPv4 case. In IPv6, it's
* a bit more complicated because of IPv6's next-header chaining,
* but it's doable.
*/
if (isv4) {
/*
* Assign the right protocol, adjust the length as we
* are removing the AH header and adjust the checksum to
* account for the protocol and length.
*/
/* The ipsa has hit hard expiration, LOG and AUDIT. */
"AH Association 0x%x, dst %s had bytes expire.\n",
goto ah_in_discard;
}
ipha->ipha_hdr_checksum = 0;
} else {
int hdrlen;
/*
* Make phdr_mp hold until the AH header and make
* mp hold everything past AH header.
*/
/* The ipsa has hit hard expiration, LOG and AUDIT. */
"AH Association 0x%x, dst %s had bytes "
goto ah_in_discard;
}
/*
* Update the next header field of the header preceding
* AH with the next header field of AH. Start with the
* IPv6 header and proceed with the extension headers
* until we find what we're looking for.
*/
while (*nexthdr != IPPROTO_AH) {
/* Assume IP has already stripped it */
*nexthdr != IPPROTO_RAW);
switch (*nexthdr) {
case IPPROTO_HOPOPTS:
break;
case IPPROTO_DSTOPTS:
break;
case IPPROTO_ROUTING:
break;
}
}
}
/* Now that we've fixed the IP header, move it forward. */
} else {
}
return (IPSEC_STATUS_SUCCESS);
&ahstack->ah_dropper);
return (IPSEC_STATUS_FAILED);
}
/*
* Invoked after processing of an outbound packet by the
* kernel crypto framework, either by ah_submit_req() for a request
* executed syncrhonously, or by the KEF callback for a request
* executed asynchronously.
*/
static ipsec_status_t
{
int align_len;
netstack_t *ns;
&ahstack->ah_dropper);
return (IPSEC_STATUS_FAILED);
}
&ahstack->ah_dropper);
return (IPSEC_STATUS_FAILED);
}
if (isv4) {
hdrs_length <<= 2;
/*
* phdr_mp must have the right amount of space for the
* combined IP and AH header. Copy the IP header and
* the ack_data onto AH. Note that the AH header was
* already formed before the ICV calculation and hence
* you don't have to copy it here.
*/
/*
* Compute the new header checksum as we are assigning
* IPPROTO_AH and adjusting the length here.
*/
nipha->ipha_hdr_checksum = 0;
} else {
/*
* phdr_mp must have the right amount of space for the
* combined IP and AH header. Copy the IP header with
* options into the pseudo header. When we constructed
* a pseudo header, we did not copy some of the mutable
* fields. We do it now by calling ah_fix_phdr_v6()
* with the last argument B_TRUE. It returns the
* ah_offset into the pseudo header.
*/
/*
* phdr_mp can hold exactly the whole IP header with options
* plus the AH header also. Thus subtracting the AH header's
* size should give exactly how much of the original header
* should be skipped.
*/
}
/* Skip the original IP header */
}
return (IPSEC_STATUS_SUCCESS);
}
/*
* Wrapper to allow IP to trigger an AH association failure message
* during SA inbound selection.
*/
void
{
if (ahstack->ipsecah_log_unknown_spi) {
}
&ahstack->ah_dropper);
}
/*
* Initialize the AH input and output processing functions.
*/
void
{
}