keysock.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>
#define _SUN_TPI_VERSION 2
#include <inet/ipsec_info.h>
#include <inet/ipsec_impl.h>
#include <sys/isa_defs.h>
/*
* This is a transport provider for the PF_KEY key mangement socket.
* (See RFC 2367 for details.)
* Downstream messages are wrapped in a keysock consumer interface KEYSOCK_IN
* messages (see ipsec_info.h), and passed to the appropriate consumer.
* Upstream messages are generated for all open PF_KEY sockets, when
* appropriate, as well as the sender (as long as SO_USELOOPBACK is enabled)
* in reply to downstream messages.
*
* Upstream messages must be created asynchronously for the following
* situations:
*
* 1.) A keysock consumer requires an SA, and there is currently none.
* 2.) An SA expires, either hard or soft lifetime.
* 3.) Other events a consumer deems fit.
*
* The MT model of this is PERMOD, with shared put procedures. Two types of
* messages, SADB_FLUSH and SADB_DUMP, need to lock down the perimeter to send
* down the *multiple* messages they create.
*/
/* List of open PF_KEY sockets, protected by keysock_list_lock. */
static kmutex_t keysock_list_lock;
static keysock_t *keysock_list;
/* Consumers table. If an entry is NULL, keysock maintains the table. */
static kmutex_t keysock_consumers_lock;
#define KEYSOCK_MAX_CONSUMERS 256
/* Default structure copied into T_INFO_ACK messages (from rts.c...) */
static struct T_info_ack keysock_g_t_info_ack = {
T_INFINITE, /* TSDU_size. Maximum size messages. */
T_INVALID, /* ETSDU_size. No expedited data. */
T_INVALID, /* CDATA_size. No connect data. */
T_INVALID, /* DDATA_size. No disconnect data. */
0, /* ADDR_size. */
0, /* OPT_size. No user-settable options */
64 * 1024, /* TIDU_size. keysock allows maximum size messages. */
T_COTS, /* SERV_type. keysock supports connection oriented. */
TS_UNBND, /* CURRENT_state. This is set from keysock_state. */
(XPG4_1) /* Provider flags */
};
/* Named Dispatch Parameter Management Structure */
typedef struct keysockpparam_s {
char *keysock_param_name;
/*
* Table of NDD variables supported by keysock. These are loaded into
* keysock_g_nd in keysock_init_nd.
*/
static keysockparam_t keysock_param_arr[] = {
/* min max value name */
{ 4096, 65536, 8192, "keysock_xmit_hiwat"},
{ 0, 65536, 1024, "keysock_xmit_lowat"},
{ 4096, 65536, 8192, "keysock_recv_hiwat"},
{ 65536, 1024*1024*1024, 256*1024, "keysock_max_buf"},
{ 0, 3, 0, "keysock_debug"},
};
/* NOTE: != 0 instead of > 0 so lint doesn't complain. */
static IDP keysock_g_nd;
/*
* cas32() works best for a known 32-bit quantity.
*/
static uint32_t keysock_flushdump;
static int keysock_flushdump_errno;
static int keysock_close(queue_t *);
static void keysock_rsrv(queue_t *);
static struct module_info info = {
};
};
};
struct streamtab keysockinfo = {
};
extern struct modlinkage *keysock_modlp;
/*
* Plumb IPsec.
*
* NOTE: New "default" modules will need to be loaded here if needed before
* boot time.
*/
/* Keep these in global space to keep the lint from complaining. */
static char *IPSECESP = "ipsecesp";
static char *IPSECESPDEV = "/devices/pseudo/ipsecesp@0:ipsecesp";
static char *IPSECAH = "ipsecah";
static char *IPSECAHDEV = "/devices/pseudo/ipsecah@0:ipsecah";
static char *KEYSOCK = "keysock";
static char *STRMOD = "strmod";
/*
* keysock_plumbed: zero if plumb not attempted, positive if it succeeded,
* negative if it failed.
*/
static int keysock_plumbed = 0;
/*
* This integer counts the number of extended REGISTERed sockets. This
* determines if we should send extended REGISTERs.
*/
static uint32_t keysock_num_extended = 0;
/*
* Global sequence space for SADB_ACQUIRE messages of any sort.
*/
/*
* Load the other ipsec modules and plumb them together.
*/
int
keysock_plumb_ipsec(void)
{
int err = 0;
keysock_plumbed = 0; /* we're trying again.. */
/*
*
* I do this separately from the actual plumbing in case this function
* ever gets called from a diskless boot before the root filesystem is
* up. I don't have to worry about "keysock" because, well, if I'm
* here, keysock must've loaded successfully.
*/
ks0dbg(("IPsec: AH failed to attach.\n"));
goto bail;
}
ks0dbg(("IPsec: ESP failed to attach.\n"));
}
/*
* Set up the IP streams for AH and ESP, as well as tacking keysock
* on top of them. Assume keysock has set the autopushes up already.
*/
/* Open IP. */
if (err) {
ks0dbg(("IPsec: lid_ident_from_mod failed (err %d).\n",
err));
goto bail;
}
if (err) {
goto bail;
}
if (err) {
goto bail;
}
if (err) {
ks0dbg(("IPsec: Push of KEYSOCK onto AH failed (err %d).\n",
err));
goto bail;
}
if (err) {
goto bail;
}
if (esp_present) {
if (err) {
goto bail;
}
if (err) {
ks0dbg(("IPsec: "
"Push of KEYSOCK onto ESP failed (err %d).\n",
err));
goto bail;
}
if (err) {
ks0dbg(("IPsec: "
goto bail;
}
}
bail:
}
return (err);
}
/* ARGSUSED */
static int
queue_t *q;
{
return (0);
}
/* This routine sets an NDD variable in a keysockparam_t structure. */
/* ARGSUSED */
static int
queue_t *q;
char *value;
{
/* Convert the value from a string into a long integer. */
return (EINVAL);
/*
* Fail the request if the new value does not lie within the
* required bounds.
*/
return (EINVAL);
}
/* Set the new value */
return (0);
}
/*
* Initialize NDD variables, and other things, for keysock.
*/
keysock_ddi_init(void)
{
if (!keysock_g_nd) {
ksp->keysock_param_name[0]) {
if (!nd_load(&keysock_g_nd,
return (B_FALSE);
}
}
}
}
return (B_TRUE);
}
/*
* Free NDD variable space, and other destructors, for keysock.
*/
void
keysock_ddi_destroy(void)
{
/* XXX Free instances? */
ks0dbg(("keysock_ddi_destroy being called.\n"));
}
/*
* Close routine for keysock.
*/
static int
keysock_close(queue_t *q)
{
int size;
qprocsoff(q);
/* Safe assumption. */
ks0dbg(("Module close, removing a consumer (%d).\n",
kc->kc_sa_type));
/*
* can inspect KC_FLUSHING w/o locking down kc->kc_lock.
*/
/*
* If this decrement was the last one, send
* down the next pending one, if any.
*
* With a PERMOD perimeter, the mutexes ops aren't
* really necessary, but if we ever loosen up, we will
* have this bit covered already.
*/
if (keysock_flushdump == 0) {
/*
* consumer go away. I need to send up to the
* appropriate keysock all of the relevant
* information. Unfortunately, I don't
* have that handy.
*/
ks0dbg(("Consumer went away while flushing or"
" dumping.\n"));
}
}
size = sizeof (keysock_consumer_t);
} else {
ks3dbg(("Driver close, PF_KEY socket is going away.\n"));
}
/* Now I'm free. */
return (0);
}
/*
* Open routine for keysock.
*/
/* ARGSUSED */
static int
{
ks3dbg(("Entering keysock open.\n"));
/* Privilege debugging will log the error */
return (EPERM);
}
return (0); /* Re-open of an already open instance. */
if (keysock_plumbed < 1) {
keysock_plumbed = 0;
/*
* Don't worry about ipsec_failure being true here.
* (See ip.c). An open of keysock should try and force
* the issue. Maybe it was a transient failure.
*/
}
/* Initialize keysock_consumer state here. */
return (ENOMEM);
qprocson(q);
/*
* Send down initial message to whatever I was pushed on top
* of asking for its consumer type. The reply will set it.
*/
/* Allocate it. */
ks1dbg((
"keysock_open: Cannot allocate KEYSOCK_HELLO.\n"));
/* Do I need to set these to null? */
return (ENOMEM);
}
/* If I allocated okay, putnext to what I was pushed atop. */
ks2dbg(("Ready to putnext KEYSOCK_HELLO.\n"));
} else {
/* Initialize keysock state. */
ks2dbg(("Made it into PF_KEY socket open.\n"));
if (ksminor == 0)
return (ENOMEM);
return (ENOMEM);
}
ks->keysock_rq = q;
/*
* The receive hiwat is only looked at on the stream head
* queue. Store in q_hiwat in order to return on SO_RCVBUF
* getsockopts.
*/
q->q_hiwat = keysock_recv_hiwat;
/*
* SO_SNDBUF/SO_SNDLOWAT getsockopts.
*/
/*
* Thread keysock into the global keysock list.
*/
if (keysock_list != NULL)
keysock_list = ks;
qprocson(q);
(void) mi_set_sth_hiwat(q, keysock_recv_hiwat);
/*
* Wait outside the keysock module perimeter for IPsec
* plumbing to be completed. If it fails, keysock_close()
* undoes everything we just did.
*/
if (!ipsec_loader_wait(q)) {
(void) keysock_close(q);
return (EPFNOSUPPORT);
}
}
return (0);
}
/* BELOW THIS LINE ARE ROUTINES INCLUDING AND RELATED TO keysock_wput(). */
/*
* Copy relevant state bits.
*/
static void
{
}
/*
* This routine responds to T_CAPABILITY_REQ messages. It is called by
* keysock_wput. Much of the T_CAPABILITY_ACK information is copied from
* keysock_g_t_info_ack. The current state of the stream is copied from
* keysock_state.
*/
static void
{
struct T_capability_ack *tcap;
return;
}
}
/*
* This routine responds to T_INFO_REQ messages. It is called by
* keysock_wput_other.
* Most of the T_INFO_ACK information is copied from keysock_g_t_info_ack.
* The current state of the stream is copied from keysock_state.
*/
static void
keysock_info_req(q, mp)
queue_t *q;
{
return;
}
/*
* keysock_err_ack. This routine creates a
* T_ERROR_ACK message and passes it
* upstream.
*/
static void
queue_t *q;
int t_error;
int sys_error;
{
}
/*
* This routine retrieves the current status of socket options.
* It returns the size of the option retrieved.
*/
/* ARGSUSED */
int
{
switch (level) {
case SOL_SOCKET:
switch (name) {
case SO_TYPE:
break;
case SO_USELOOPBACK:
break;
/*
* The following two items can be manipulated,
* but changing them should do nothing.
*/
case SO_SNDBUF:
break;
case SO_RCVBUF:
break;
}
break;
default:
return (0);
}
return (sizeof (int));
}
/*
* This routine sets socket options.
*/
/* ARGSUSED */
int
{
switch (level) {
case SOL_SOCKET:
switch (name) {
case SO_USELOOPBACK:
if (!(*i1))
break;
case SO_SNDBUF:
if (*i1 > keysock_max_buf)
return (ENOBUFS);
break;
case SO_RCVBUF:
if (*i1 > keysock_max_buf)
return (ENOBUFS);
break;
}
break;
}
return (0);
}
/*
* Handle STREAMS messages.
*/
static void
{
int error;
case M_PROTO:
case M_PCPROTO:
ks3dbg((
"keysock_wput_other: Not big enough M_PROTO\n"));
return;
}
case T_CAPABILITY_REQ:
keysock_capability_req(q, mp);
return;
case T_INFO_REQ:
keysock_info_req(q, mp);
return;
case T_SVR4_OPTMGMT_REQ:
return;
case T_OPTMGMT_REQ:
return;
case T_DATA_REQ:
case T_EXDATA_REQ:
case T_ORDREL_REQ:
/* Illegal for keysock. */
return;
default:
/* Not supported by keysock. */
return;
}
case M_IOCTL:
case ND_SET:
case ND_GET:
return;
} else
/* FALLTHRU */
default:
return;
}
case M_FLUSH:
}
return;
}
/* Else FALLTHRU */
}
/* If fell through, just black-hole the message. */
}
/*
* Transmit a PF_KEY error message to the instance either pointed to
* by ks, the instance with serial number serial, or more, depending.
*
* The faulty message (or a reasonable facsimile thereof) is in mp.
* This function will free mp or recycle it for delivery, thereby causing
* the stream head to free it.
*/
static void
{
/*
* Strip out extension headers.
*/
}
/*
* Pass down a message to a consumer. Wrap it in KEYSOCK_IN, and copy
* in the extv if passed in.
*/
static void
{
int i;
ks3dbg(("keysock_passdown: allocb failed.\n"));
if (flushmsg) {
ks0dbg((
/* If this is true, I hold the perimeter. */
}
return;
}
for (i = 0; i <= SADB_EXT_MAX; i++)
/*
* Find the appropriate consumer where the message is passed down.
*/
if (flushmsg) {
ks0dbg((
/* If this is true, I hold the perimeter. */
}
return;
}
/*
* NOTE: There used to be code in here to spin while a flush or
* dump finished. Keysock now assumes that consumers have enough
* MT-savviness to deal with that.
*/
/*
* Current consumers (AH and ESP) are guaranteed to return a
* FLUSH or DUMP message back, so when we reach here, we don't
* have to worry about keysock_flushdumps.
*/
}
/*
* High-level reality checking of extensions.
*/
static boolean_t
{
int i;
char *idstr;
switch (ext->sadb_ext_type) {
case SADB_EXT_ADDRESS_SRC:
case SADB_EXT_ADDRESS_DST:
case SADB_EXT_ADDRESS_PROXY:
/* Check for at least enough addtl length for a sockaddr. */
return (B_FALSE);
break;
case SADB_EXT_LIFETIME_HARD:
case SADB_EXT_LIFETIME_SOFT:
return (B_FALSE);
break;
case SADB_EXT_SPIRANGE:
/* See if the SPI range is legit. */
return (B_FALSE);
break;
case SADB_EXT_KEY_AUTH:
case SADB_EXT_KEY_ENCRYPT:
/* Key length check. */
return (B_FALSE);
/*
* Check to see if the key length (in bits) is less than the
* extension length (in 8-bits words).
*/
ks1dbg((
ks1dbg(("%d bits, len is %d bytes.\n",
return (B_FALSE);
}
/* All-zeroes key check. */
for (i = 0;
i++)
if (lp[i] != 0)
break; /* Out of for loop. */
/* If finished the loop naturally, it's an all zero key. */
if (lp[i] == 0)
return (B_FALSE);
break;
case SADB_EXT_IDENTITY_SRC:
case SADB_EXT_IDENTITY_DST:
/*
* Make sure the strings in these identities are
* null-terminated. RFC 2367 underspecified how to handle
* such a case. I "proactively" null-terminate the string
* at the last byte if it's not terminated sooner.
*/
i -= sizeof (sadb_ident_t);
while (*idstr != '\0' && i > 0) {
i--;
idstr++;
}
if (i == 0) {
/*
* I.e., if the bozo user didn't NULL-terminate the
* string...
*/
idstr--;
*idstr = '\0';
}
break;
}
return (B_TRUE); /* For now... */
}
/* Return values for keysock_get_ext(). */
#define KGE_OK 0
#define KGE_DUP 1
#define KGE_UNK 2
#define KGE_LEN 3
#define KGE_CHK 4
/*
* Parse basic extension headers and return in the passed-in pointer vector.
* Return values include:
*
* KGE_OK Everything's nice and parsed out.
* If there are no extensions, place NULL in extv[0].
* KGE_DUP There is a duplicate extension.
* First instance in appropriate bin. First duplicate in
* extv[0].
* KGE_UNK Unknown extension type encountered. extv[0] contains
* unknown header.
* KGE_LEN Extension length error.
* KGE_CHK High-level reality check failed on specific extension.
*
* My apologies for some of the pointer arithmetic in here. I'm thinking
* like an assembly programmer, yet trying to make the compiler happy.
*/
static int
{
/* Use extv[0] as the "current working pointer". */
/* Check for unknown headers. */
if (extv[0]->sadb_ext_type == 0 ||
return (KGE_UNK);
/*
* Check length. Use uint64_t because extlen is in units
* of 64-bit words. If length goes beyond the msgsize,
* return an error. (Zero length also qualifies here.)
*/
if (extv[0]->sadb_ext_len == 0 ||
return (KGE_LEN);
/* Check for redundant headers. */
return (KGE_DUP);
/*
* Reality check the extension if possible at the keysock
* level.
*/
return (KGE_CHK);
/* If I make it here, assign the appropriate bin. */
/* Advance pointer (See above for uint64_t ptr reasoning.) */
extv[0] = (sadb_ext_t *)
}
/* Everything's cool. */
/*
* If extv[0] == NULL, then there are no extension headers in this
* message. Ensure that this is the case.
*/
return (KGE_OK);
}
/*
* qwriter() callback to handle flushes and dumps. This routine will hold
* the inner perimeter.
*/
void
{
/*
* I am guaranteed this will work. I did the work in keysock_parse()
* already.
*/
/*
* I hold the perimeter, therefore I don't need to use atomic ops.
*/
if (keysock_flushdump != 0) {
/* XXX Should I instead use EBUSY? */
/* XXX Or is there a way to queue these up? */
return;
}
start = 0;
} else {
}
/*
* Fill up keysock_flushdump with the number of outstanding dumps
*/
/*
* Okay, I hold the perimeter. Eventually keysock_flushdump will
* contain the number of consumers with outstanding flush operations.
*
* SO, here's the plan:
* * For each relevant consumer (Might be one, might be all)
* * Twiddle on the FLUSHING flag.
*
* keysock_flushdump. When I decrement it to 0, I will pass the
* pass down the right SA type to the consumer (either its own, or
* that of UNSPEC), the right one will be reflected from each consumer,
* and accordingly back to the socket.
*/
if (keysock_consumers[i] != NULL) {
ks0dbg(("SADB_FLUSH copymsg() failed.\n"));
/*
* Error? And what about outstanding
* flushes? Oh, yeah, they get sucked up and
* the counter is decremented. Consumers
* (see keysock_passdown()) are guaranteed
* to deliver back a flush request, even if
* it's an error.
*/
return;
}
/*
* Because my entry conditions are met above, the
* following assertion should hold true.
*/
== 0);
/* Always increment the number of flushes... */
/* Guaranteed to return a message. */
/*
* In case where start == finish, and there's no
* consumer, should we force an error? Yes.
*/
return;
}
}
if (keysock_flushdump == 0) {
/*
* There were no consumers at all for this message.
* XXX For now return ESRCH.
*/
} else {
/* Otherwise, free the original message. */
}
}
/*
* Get the right diagnostic for a duplicate. Should probably use a static
* table lookup.
*/
int
{
int rc = 0;
switch (ext_type) {
case SADB_EXT_ADDRESS_SRC:
break;
case SADB_EXT_ADDRESS_DST:
break;
case SADB_EXT_SA:
break;
case SADB_EXT_SPIRANGE:
break;
case SADB_EXT_KEY_AUTH:
break;
case SADB_EXT_KEY_ENCRYPT:
break;
}
return (rc);
}
/*
* Get the right diagnostic for a reality check failure. Should probably use
* a static table lookup.
*/
int
{
int rc = 0;
switch (ext_type) {
case SADB_EXT_ADDRESS_SRC:
break;
case SADB_EXT_ADDRESS_DST:
break;
case SADB_EXT_SA:
break;
case SADB_EXT_SPIRANGE:
break;
case SADB_EXT_KEY_AUTH:
break;
case SADB_EXT_KEY_ENCRYPT:
break;
}
return (rc);
}
/*
* Keysock massaging of an inverse ACQUIRE. Consult policy,
* and construct an appropriate response.
*/
static void
{
/*
* Reality check things...
*/
return;
}
}
} else {
}
}
/*
* Spew an extended REGISTER down to the relevant consumers.
*/
static void
{
}
} else {
return;
}
/*
* Since we've made it here, keysock_get_ext will work!
*/
(void) keysock_get_ext(downextv,
B_FALSE);
++satypes;
}
}
/*
* Set global to indicate we prefer an extended ACQUIRE.
*/
}
/*
* Handle PF_KEY messages.
*/
static void
{
/* Make sure I'm a PF_KEY socket. (i.e. nothing's below me) */
ks2dbg(("Received possible PF_KEY message, type %d.\n",
samsg->sadb_msg_type));
/*
* Message len incorrect w.r.t. actual size. Send an error
* (EMSGSIZE). It may be necessary to massage things a
* bit. For example, if the sadb_msg_type is hosed,
* I need to set it to SADB_RESERVED to get delivery to
* do the right thing. Then again, maybe just letting
* the error delivery do the right thing.
*/
ks2dbg(("mblk (%lu) and base (%d) message sizes don't jibe.\n",
return;
}
/* Get all message into one mblk. */
/*
* Something screwy happened.
*/
ks3dbg(("keysock_parse: pullupmsg() failed.\n"));
return;
} else {
}
}
case KGE_DUP:
/* Handle duplicate extension. */
ks1dbg(("Got duplicate extension of type %d.\n",
extv[0]->sadb_ext_type));
return;
case KGE_UNK:
/* Handle unknown extension. */
ks1dbg(("Got unknown extension of type %d.\n",
extv[0]->sadb_ext_type));
return;
case KGE_LEN:
/* Length error. */
ks1dbg(("Length %d on extension type %d overrun or 0.\n",
return;
case KGE_CHK:
/* Reality check failed. */
ks1dbg(("Reality check failed on extension type %d.\n",
extv[0]->sadb_ext_type));
return;
default:
/* Default case is no errors. */
break;
}
switch (samsg->sadb_msg_type) {
case SADB_REGISTER:
/*
* There's a semantic weirdness in that a message OTHER than
* the return REGISTER message may be passed up if I set the
* registered bit BEFORE I pass it down.
*
* SOOOO, I'll not twiddle any registered bits until I see
* the upbound REGISTER (with a serial number in it).
*/
/* Handle extended register here. */
return;
return;
}
/* FALLTHRU */
case SADB_GETSPI:
case SADB_ADD:
case SADB_UPDATE:
case SADB_DELETE:
case SADB_GET:
/*
* Pass down to appropriate consumer.
*/
B_FALSE);
return;
case SADB_ACQUIRE:
/*
* If I _receive_ an acquire, this means I should spread it
* out to registered sockets. Unless there's an errno...
*
* Need ADDRESS, may have ID, SENS, and PROP, unless errno,
* in which case there should be NO extensions.
*
* Return to registered.
*/
if (samsg->sadb_msg_errno != 0) {
if (satype == SADB_SATYPE_UNSPEC) {
return;
}
/*
* Reassign satype based on the first
* flags that KEYSOCK_SETREG says.
*/
while (satype <= SADB_SATYPE_MAX) {
break;
satype++;
}
if (satype > SADB_SATYPE_MAX) {
return;
}
}
} else {
else
}
return;
case SADB_EXPIRE:
/*
* If someone sends this in, then send out to all senders.
* (Save maybe ESP or AH, I have to be careful here.)
*
* Need ADDRESS, may have ID and SENS.
*
* XXX for now this is unsupported.
*/
break;
case SADB_FLUSH:
case SADB_DUMP: /* not used by normal applications */
/*
* Nuke all SAs, or dump out the whole SA table to sender only.
*
* No extensions at all. Return to all listeners.
*
* Question: Should I hold a lock here to prevent
* Answer: No. (See keysock_passdown() for details.)
*/
/*
* FLUSH or DUMP messages shouldn't have extensions.
* Return EINVAL.
*/
ks2dbg(("FLUSH message with extension.\n"));
return;
}
return;
case SADB_X_PROMISC:
/*
* Promiscuous processing message.
*/
if (samsg->sadb_msg_satype == 0)
else
return;
case SADB_X_INVERSE_ACQUIRE:
return;
default:
ks2dbg(("Got unknown message type %d.\n",
samsg->sadb_msg_type));
return;
}
/* As a placeholder... */
ks0dbg(("keysock_parse(): Hit EOPNOTSUPP\n"));
}
/*
* I don't convert to ioctl()'s for IP. I am the end-all driver as far
* as PF_KEY sockets are concerned. I do some conversion, but not as much
*/
static void
{
ks3dbg(("In keysock_wput\n"));
/*
* We shouldn't get writes on a consumer instance.
* But for now, just passthru.
*/
ks1dbg(("Huh? wput for an consumer instance (%d)?\n",
kc->kc_sa_type));
return;
}
case M_DATA:
/*
* Silently discard.
*/
ks2dbg(("raw M_DATA in keysock.\n"));
return;
case M_PROTO:
case M_PCPROTO:
/* No data after T_DATA_REQ. */
ks2dbg(("No data after DATA_REQ.\n"));
return;
}
ks2dbg(("T_DATA_REQ\n"));
break; /* Out of switch. */
}
}
/* FALLTHRU */
default:
ks3dbg(("In default wput case (%d %d).\n",
keysock_wput_other(q, mp);
return;
}
/* I now have a PF_KEY message in an M_DATA block, pointed to by mp. */
keysock_parse(q, mp);
}
/* BELOW THIS LINE ARE ROUTINES INCLUDING AND RELATED TO keysock_rput(). */
/*
* Called upon receipt of a KEYSOCK_HELLO_ACK to set up the appropriate
* state vectors.
*/
static void
{
ks0dbg((
"Hmmmm, someone closed %d before the HELLO_ACK happened.\n",
satype));
/*
* Perhaps updating the new below-me consumer with what I have
* so far would work too?
*/
} else {
/* Add new below-me consumer. */
/* Scan the keysock list. */
/*
* XXX Perhaps send an SADB_REGISTER down on
* the socket's behalf.
*/
ks1dbg(("Socket %u registered already for "
}
}
}
}
/*
* Generate a KEYSOCK_OUT_ERR message for my consumer.
*/
static void
{
ks1dbg(("keysock_out_err: Can't alloc message.\n"));
return;
}
/* Is serial necessary? */
kse->ks_err_serial = 0;
/*
* XXX What else do I need to do here w.r.t. information
* to tell the consumer what caused this error?
*
* I believe the answer is the PF_KEY ACQUIRE (or other) message
* attached in mp, which is appended at the end. I believe the
* db_ref won't matter here, because the PF_KEY message is only read
* for KEYSOCK_OUT_ERR.
*/
}
/* XXX this is a hack errno. */
#define EIPSECNOSA 255
/*
* Route message (pointed by mp, header in samsg) toward appropriate
* sockets. Assume the message's creator did its job correctly.
*
* This should be a function that is followed by a return in its caller.
* The compiler _should_ be able to use tail-call optimizations to make the
* large ## of parameters not a huge deal.
*/
static void
{
int err = EIPSECNOSA;
/* Convert mp, which is M_DATA, into an M_PROTO of type T_DATA_IND */
goto error;
}
switch (samsg->sadb_msg_type) {
case SADB_FLUSH:
case SADB_GETSPI:
case SADB_UPDATE:
case SADB_ADD:
case SADB_DELETE:
case SADB_EXPIRE:
/*
* These are most likely replies. Don't worry about
* KEYSOCK_OUT_ERR handling. Deliver to all sockets.
*/
ks3dbg(("Delivering normal message (%d) to all sockets.\n",
samsg->sadb_msg_type));
break;
case SADB_REGISTER:
/*
* REGISTERs come up for one of three reasons:
*
* 1.) In response to a normal SADB_REGISTER
* (samsg->sadb_msg_satype != SADB_SATYPE_UNSPEC &&
* serial != 0)
* Deliver to normal SADB_REGISTERed sockets.
* 2.) In response to an extended REGISTER
* (samsg->sadb_msg_satype == SADB_SATYPE_UNSPEC)
* Deliver to extended REGISTERed socket.
* 3.) Spontaneous algorithm changes
* (samsg->sadb_msg_satype != SADB_SATYPE_UNSPEC &&
* serial == 0)
* Deliver to REGISTERed sockets of all sorts.
*/
/* Here because of keysock_error() call. */
break; /* Out of switch. */
}
ks3dbg(("Delivering REGISTER.\n"));
if (satype == SADB_SATYPE_UNSPEC) {
/* REGISTER Reason #2 */
/*
* Rewhack SA type so PF_KEY socket holder knows what
* consumer generated this algorithm list.
*/
} else if (serial == 0) {
/* REGISTER Reason #3 */
} else {
/* REGISTER Reason #1 */
}
break;
case SADB_ACQUIRE:
/*
* ACQUIREs are either extended (sadb_msg_satype == 0) or
* regular (sadb_msg_satype != 0). And we're guaranteed
* that serial == 0 for an ACQUIRE.
*/
ks3dbg(("Delivering ACQUIRE.\n"));
/*
* Corner case - if we send a regular ACQUIRE and there's
* extended ones registered, don't send an error down to
* consumers if nobody's listening and prematurely destroy
* their ACQUIRE record. This might be too hackish of a
* solution.
*/
if (allreg && keysock_num_extended > 0)
err = 0;
break;
case SADB_X_PROMISC:
case SADB_X_INVERSE_ACQUIRE:
case SADB_DUMP:
case SADB_GET:
default:
/*
* Deliver to the sender and promiscuous only.
*/
samsg->sadb_msg_type));
break;
}
/* Delivery loop. */
/*
* Check special keysock-setting cases (REGISTER replies)
* here.
*/
}
/*
* NOLOOP takes precedence over PROMISC. So if you've set
* !SO_USELOOPBACK, don't expect to see any data...
*/
continue;
/*
* Messages to all, or promiscuous sockets just GET the
* message. Perform rules-type checking iff it's not for all
* listeners or the socket is in promiscuous mode.
*
* NOTE:Because of the (kc != NULL && ISREG()), make sure
* extended ACQUIREs arrive off a consumer that is
* part of the extended REGISTER set of consumers.
*/
!toall &&
continue;
ks2dbg((
"keysock_passup(): dupmsg() failed.\n"));
}
/*
* At this point, we can deliver or attempt to deliver
* this message. We're free of obligation to report
* no listening PF_KEY sockets. So set err to 0.
*/
err = 0;
/*
* See if we canputnext(), as well as see if the message
* needs to be queued if we can't.
*/
if (persistent) {
ks1dbg((
"keysock_passup: putq failed.\n"));
} else {
continue;
}
}
continue;
}
/*
* Unlike the specific keysock instance case, this
* will only hit for listeners, so we will only
* putnext() if we can.
*/
break; /* out of for loop. */
}
/*
* Generate KEYSOCK_OUT_ERR for consumer.
* Basically, I send this back if I have not been able to
* transmit (for whatever reason)
*/
ks1dbg(("keysock_passup(): No registered of type %d.\n",
satype));
}
/*
* Do a copymsg() because people who get
* KEYSOCK_OUT_ERR may alter the message contents.
*/
ks2dbg(("keysock_passup: copymsg() failed.\n"));
}
}
}
/*
* XXX Blank the message somehow. This is difficult because we don't
* know at this point if the message has db_ref > 1, etc.
*
* Optimally, keysock messages containing actual keying material would
* be allocated with esballoc(), with a zeroing free function.
*/
}
/*
* Keysock's read service procedure is there only for PF_KEY reply
* messages that really need to reach the top.
*/
static void
keysock_rsrv(queue_t *q)
{
if (canputnext(q)) {
} else {
return;
}
}
}
/*
* The read procedure should only be invoked by a keysock consumer, like
* ESP, AH, etc. I should only see KEYSOCK_OUT and KEYSOCK_HELLO_ACK
* messages on my read queues.
*/
static void
{
/* Make sure I'm a consumer instance. (i.e. something's below me) */
/*
* Keysock should only see keysock consumer interface
* messages (see ipsec_info.h) on its read procedure.
* To be robust, however, putnext() up so the STREAM head can
* deal with it appropriately.
*/
ks1dbg(("Hmmm, a non M_CTL (%d, 0x%x) on keysock_rput.\n",
return;
}
switch (ii->ipsec_info_type) {
case KEYSOCK_OUT:
/*
* A consumer needs to pass a response message or an ACQUIRE
* UP. I assume that the consumer has done the right
* thing w.r.t. message creation, etc.
*/
/*
* If I'm an end-of-FLUSH or an end-of-DUMP marker...
*/
if (samsg->sadb_msg_errno != 0)
/*
* Lower the atomic "flushing" count. If it's
* the last one, send up the end-of-{FLUSH,DUMP} to
* the appropriate PF_KEY socket.
*/
return;
}
samsg->sadb_msg_seq = 0;
}
}
return;
case KEYSOCK_HELLO_ACK:
/* Aha, now we can link in the consumer! */
return;
default:
ks1dbg(("Hmmm, an IPsec info I'm not used to, 0x%x\n",
ii->ipsec_info_type));
}
}
/*
* So we can avoid external linking problems....
*/
keysock_extended_reg(void)
{
return (keysock_num_extended != 0);
}
keysock_next_seq(void)
{
}