spdsock.c revision 134a1f4e3289b54e0f980e9cf05352e419a60bee
/*
* 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
*/
/*
*/
#include <sys/sysmacros.h>
#define _SUN_TPI_VERSION 2
#include <net/pfpolicy.h>
#include <inet/proto_set.h>
#include <inet/ipsec_impl.h>
#include <sys/isa_defs.h>
/*
* This is a transport provider for the PF_POLICY IPsec policy
* management socket, which provides a management interface into the
* SPD, allowing policy rules to be added, deleted, and queried.
*
* This effectively replaces the old private SIOC*IPSECONFIG ioctls
* with an extensible interface which will hopefully be public some
* day.
*
* See <net/pfpolicy.h> for more details on the protocol.
*
* SPD; see ipsec_impl.h for the policy data structures and spd.c for
* the code which maintains them.
*
* The MT model of this is QPAIR with the addition of some explicit
* locking to protect system-wide policy data structures.
*/
/* Default structure copied into T_INFO_ACK messages (from rts.c...) */
static struct T_info_ack spdsock_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. spdsock allows maximum size messages. */
T_COTS, /* SERV_type. spdsock supports connection oriented. */
TS_UNBND, /* CURRENT_state. This is set from spdsock_state. */
(XPG4_1) /* Provider flags */
};
/* Named Dispatch Parameter Management Structure */
typedef struct spdsockparam_s {
char *spdsock_param_name;
/*
* Table of NDD variables supported by spdsock. These are loaded into
* spdsock_g_nd in spdsock_init_nd.
*/
static spdsockparam_t lcl_param_arr[] = {
/* min max value name */
{ 4096, 65536, 8192, "spdsock_xmit_hiwat"},
{ 0, 65536, 1024, "spdsock_xmit_lowat"},
{ 4096, 65536, 8192, "spdsock_recv_hiwat"},
{ 65536, 1024*1024*1024, 256*1024, "spdsock_max_buf"},
{ 0, 3, 0, "spdsock_debug"},
};
/* NOTE: != 0 instead of > 0 so lint doesn't complain. */
(ss)->spdsock_dump_cur_type = 0; \
(ss)->spdsock_dump_count = 0; \
(ss)->spdsock_dump_cur_chain = 0; \
}
static int spdsock_close(queue_t *);
static void spdsock_wsrv(queue_t *);
static void spdsock_rsrv(queue_t *);
static void spdsock_loadcheck(void *);
static void spdsock_merge_algs(spd_stack_t *);
static void update_iptun_policy(ipsec_tun_pol_t *);
static struct module_info info = {
};
};
};
struct streamtab spdsockinfo = {
};
/* mapping from alg type to protocol number, as per RFC 2407 */
};
/* mapping from kernel exec mode to spdsock exec mode */
};
/* ARGSUSED */
static int
queue_t *q;
{
return (0);
}
/* This routine sets an NDD variable in a spdsockparam_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 at module load time
*/
spdsock_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 spd_stack_t's.
*/
return (B_TRUE);
}
/*
* Walk through the param array specified registering each element with the
* named dispatch handler.
*/
static boolean_t
{
ssp->spdsock_param_name[0]) {
return (B_FALSE);
}
}
}
return (B_TRUE);
}
/*
* Initialize for each stack instance
*/
/* ARGSUSED */
static void *
{
return (spds);
}
void
spdsock_ddi_destroy(void)
{
}
/*
* Do pre-removal cleanup.
*/
/* ARGSUSED */
static void
{
}
}
/* ARGSUSED */
static void
{
}
/*
* NOTE: large quantities of this should be shared with keysock.
* Would be nice to combine some of this into a common module, but
* not possible given time pressures.
*/
/*
* High-level reality checking of extensions.
*/
/* ARGSUSED */ /* XXX */
static boolean_t
{
int i;
char *idstr;
/* (NOTE: Modified from SADB_EXT_IDENTITY..) */
/*
* Make sure the strings in these identities are
* null-terminated. Let's "proactively" null-terminate the
* string at the last byte if it's not terminated sooner.
*/
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';
}
}
return (B_TRUE); /* For now... */
}
/* Return values for spdsock_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]->spd_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]->spd_ext_len == 0 ||
return (KGE_LEN);
/* Check for redundant headers. */
return (KGE_DUP);
/*
* Reality check the extension if possible at the spdsock
* level.
*/
return (KGE_CHK);
/* If I make it here, assign the appropriate bin. */
/* Advance pointer (See above for uint64_t ptr reasoning.) */
}
/* 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);
}
static const int bad_ext_diag[] = {
};
static const int dup_ext_diag[] = {
};
/*
* Transmit a PF_POLICY 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.
*/
}
static void
{
}
static void
{
}
/*
* Do NOT consume a reference to itp.
*/
/*ARGSUSED*/
static void
{
if (active)
else
/* SPD_FLUSH is worth a tunnel MTU check. */
}
/*
* Clear out one polhead.
*/
static void
{
}
static void
{
if (auditing) {
}
} else {
/* First flush the global policy. */
if (auditing) {
}
/* Then flush every tunnel's appropriate one. */
if (auditing) {
}
}
}
static boolean_t
{
}
struct spd_portrange *pr =
}
struct spd_portrange *pr =
}
struct spd_typecode *tc=
else
else
}
}
struct spd_address *ap = \
IPV6_ADDR_LEN : IP_ADDR_LEN; \
return (B_FALSE); \
} \
}
return (B_FALSE);
}
return (B_TRUE);
}
static boolean_t
{
switch (type) {
case SPD_ACTTYPE_DROP:
return (B_TRUE);
case SPD_ACTTYPE_PASS:
return (B_TRUE);
case SPD_ACTTYPE_IPSEC:
return (B_TRUE);
}
return (B_FALSE);
}
static boolean_t
{
/*
* Note use of !! for boolean canonicalization.
*/
return (B_TRUE);
}
static void
{
}
/*
* Sanity check action against reality, and shrink-wrap key sizes..
*/
static boolean_t
{
return (B_FALSE);
}
return (B_FALSE);
}
return (B_FALSE);
}
}
/*
* We may be short a few error checks here..
*/
static boolean_t
{
struct spd_ext_actions *sactp =
int nact;
*nactp = 0;
return (B_FALSE);
}
/*
* Parse the "action" extension and convert into an action chain.
*/
return (B_FALSE);
}
switch (attrp->spd_attr_tag) {
case SPD_ATTR_NOP:
break;
case SPD_ATTR_EMPTY:
break;
case SPD_ATTR_END:
/* FALLTHRU */
case SPD_ATTR_NEXT:
goto fail;
}
goto fail;
break;
case SPD_ATTR_TYPE:
goto fail;
}
break;
case SPD_ATTR_FLAGS:
/*
* Set "sa unique" for transport-mode
* tunnels whether we want to or not.
*/
}
goto fail;
}
break;
case SPD_ATTR_AH_AUTH:
if (attrp->spd_attr_value == 0) {
goto fail;
}
break;
case SPD_ATTR_ESP_ENCR:
if (attrp->spd_attr_value == 0) {
goto fail;
}
break;
case SPD_ATTR_ESP_AUTH:
if (attrp->spd_attr_value == 0) {
goto fail;
}
break;
case SPD_ATTR_ENCR_MINBITS:
break;
case SPD_ATTR_ENCR_MAXBITS:
break;
case SPD_ATTR_AH_MINBITS:
break;
case SPD_ATTR_AH_MAXBITS:
break;
case SPD_ATTR_ESPA_MINBITS:
break;
case SPD_ATTR_ESPA_MAXBITS:
break;
case SPD_ATTR_LIFE_SOFT_TIME:
case SPD_ATTR_LIFE_HARD_TIME:
case SPD_ATTR_LIFE_SOFT_BYTES:
case SPD_ATTR_LIFE_HARD_BYTES:
break;
case SPD_ATTR_KM_PROTO:
break;
case SPD_ATTR_KM_COOKIE:
break;
case SPD_ATTR_REPLAY_DEPTH:
break;
}
}
goto fail;
}
return (B_TRUE);
fail:
return (B_FALSE);
}
typedef struct
{
int dir;
} tmprule_t;
static int
{
return (ENOMEM);
(*rp)++;
return (EEXIST);
return (0);
}
static int
{
int error;
if (error != 0)
return (error);
}
if (error != 0)
return (error);
}
return (0);
}
static void
{
if (auditing) {
}
return;
}
return;
}
} else {
if (tunnel_mode)
}
} else {
}
if (rule->spd_rule_index != 0) {
goto fail2;
}
goto fail2;
}
if (tunnel_mode) {
if (sel.ipsl_valid &
(IPSL_REMOTE_PORT | IPSL_LOCAL_PORT)) {
}
} else {
/*
* For now, we don't allow transport-mode on a tunnel
* with ANY specific selectors. Bail if we have such
* a request.
*/
goto fail2;
}
}
}
goto fail2;
}
/*
* If no addresses were specified, add both.
*/
if (afs == 0)
if (error != 0)
goto fail;
}
if (error != 0)
goto fail;
}
}
if (auditing) {
}
return;
fail:
if (empty_itp)
}
if (auditing) {
}
}
void
{
if (auditing) {
cpid);
}
return;
}
/*
* Must enter itp_lock first to avoid deadlock. See tun.c's
* set_sec_simple() for the other case of itp_lock and iph_lock.
*/
if (rule->spd_rule_index != 0) {
0) {
goto fail;
}
} else {
goto fail;
}
goto fail;
}
goto fail;
}
}
else
}
/* Can exit locks in any order. */
}
if (auditing) {
}
return;
fail:
if (auditing) {
}
}
/* Do NOT consume a reference to itp. */
/* ARGSUSED */
static void
{
/* SPD_FLIP is worth a tunnel MTU check. */
}
void
{
char *tname;
if (*tname == '\0') {
/* can't fail */
if (auditing) {
}
if (auditing) {
}
} else {
/* Better idea for "tunnel not found"? */
if (auditing) {
}
return;
}
if (auditing) {
}
}
} else {
if (auditing) {
}
}
}
/*
* Unimplemented feature
*/
/* ARGSUSED */
static void
{
}
static mblk_t *
{
if (m == NULL) {
return (NULL);
}
return (m);
}
static mblk_t *
{
mblk_t *m;
}
return (m);
}
/*
* Rule encoding functions.
* We do a two-pass encode.
* If base != NULL, fill in encoded rule part starting at base+offset.
* Always return "offset" plus length of to-be-encoded data.
*/
static uint_t
{
struct spd_typecode *tcp;
}
return (offset);
}
static uint_t
{
spp->spd_proto_reserved1 = 0;
spp->spd_proto_reserved2 = 0;
}
return (offset);
}
static uint_t
{
struct spd_portrange *spp;
}
return (offset);
}
static uint_t
{
struct spd_address *sae;
} else {
}
sae->spd_address_reserved2 = 0;
}
return (offset);
}
static uint_t
{
selkey->ipsl_lport);
selkey->ipsl_rport);
}
return (offset);
}
static uint_t
{
struct spd_attribute *attr;
}
offset += sizeof (struct spd_attribute);
return (offset);
}
static uint_t
{
EMIT(SPD_ATTR_EMPTY, 0);
case IPSEC_ACT_DISCARD:
case IPSEC_ACT_REJECT:
break;
case IPSEC_ACT_BYPASS:
case IPSEC_ACT_CLEAR:
break;
case IPSEC_ACT_APPLY:
flags = 0;
flags |= SPD_APPLY_AH;
flags |= SPD_APPLY_ESP;
flags |= SPD_APPLY_ESPA;
flags |= SPD_APPLY_SE;
if (flags & SPD_APPLY_AH) {
}
if (flags & SPD_APPLY_ESP) {
if (flags & SPD_APPLY_ESPA) {
}
}
/* Add more here */
break;
}
return (offset);
}
static uint_t
const ipsec_action_t *ap)
{
struct spd_ext_actions *act;
act->spd_actions_len = 0;
act->spd_actions_count = 0;
act->spd_actions_reserved = 0;
}
nact++;
EMIT(SPD_ATTR_NEXT, 0);
}
}
EMIT(SPD_ATTR_END, 0);
}
return (offset);
}
/* ARGSUSED */
static uint_t
{
if (dir == IPSEC_TYPE_INBOUND)
if (dir == IPSEC_TYPE_OUTBOUND)
return (flags);
}
static uint_t
{
}
if (tunnel)
spr->spd_rule_unused = 0;
}
/*
* If we have an interface name (i.e. if this policy head came from
* a tunnel), add the SPD_EXT_TUN_NAME extension.
*/
}
}
}
return (offset);
}
/* ARGSUSED */
static mblk_t *
{
mblk_t *m;
/*
* Figure out how much space we'll need.
*/
tunnel);
/*
* Allocate mblk.
*/
if (m == NULL)
return (NULL);
/*
* Fill it in..
*/
return (m);
}
static ipsec_policy_t *
{
ss->spdsock_dump_count++;
return (cur);
}
static ipsec_policy_t *
{
next:
chain++;
}
}
af++;
}
}
type++;
if (type >= IPSEC_NTYPES)
return (NULL);
ss->spdsock_dump_cur_chain = 0;
goto next;
}
/*
* If we're done with one policy head, but have more to go, we iterate through
* another IPsec tunnel policy head (itp). Return NULL if it is an error
* worthy of returning EAGAIN via PF_POLICY.
*/
static ipsec_tun_pol_t *
{
/* Oops, state of the tunnel polheads changed. */
/* Just finished global, find first node. */
} else {
/* We just finished current polhead, find the next one. */
}
}
}
return (itp);
}
static mblk_t *
{
mblk_t *m;
}
if (--(ss->spdsock_dump_remaining_polheads) == 0)
return (spdsock_dump_finish(ss, 0));
/*
* If we reach here, we have more policy heads (tunnel
* entries) to dump. Let's reset to a new policy head
* and get some more rules.
*
* An empty policy head will have spdsock_dump_next_rule()
* return NULL, and we loop (while dropping the number of
* remaining polheads). If we loop to 0, we finish. We
* keep looping until we hit 0 or until we have a rule to
* encode.
*
* NOTE: No need for ITP_REF*() macros here as we're only
* going after and refholding the policy head itself.
*/
}
/* Reset other spdsock_dump thingies. */
if (ss->spdsock_dump_active) {
} else {
}
}
if (m == NULL)
return (m);
}
/*
* Dump records until we run into flow-control back-pressure.
*/
static void
{
m = spdsock_dump_next_record(ss);
if (m == NULL)
return;
freemsg(m);
return;
}
}
}
/*
* Start dumping.
* Format a start-of-dump record, and set up the stream and kick the rsrv
* procedure to continue the job..
*/
/* ARGSUSED */
static void
{
/* spdsock_open() already set spdsock_itp to NULL. */
if (iph == ALL_ACTIVE_POLHEADS) {
} else {
}
} else {
}
if (!mr) {
return;
}
}
/* Do NOT consume a reference to ITP. */
void
{
if (*errptr != 0)
return; /* We've failed already for some reason. */
}
void
{
int error;
char *tname;
if (*tname == '\0') {
if (auditing) {
}
if (error == 0) {
if (auditing) {
cpid);
}
}
} else {
if (auditing) {
}
return;
}
if (auditing) {
}
}
} else {
if (auditing) {
}
}
if (error != 0)
else
}
/*
* Process a SPD_ALGLIST request. The caller expects separate alg entries
* for AH authentication, ESP authentication, and ESP encryption.
* The same distinction is then used when setting the min and max key
* sizes when defining policies.
*/
#define SPDSOCK_AH_AUTH 0
#define SPDSOCK_ESP_AUTH 1
#define SPDSOCK_ESP_ENCR 2
#define SPDSOCK_NTYPES 3
};
};
};
};
};
void
{
mblk_t *m;
struct spd_ext_actions *act;
struct spd_attribute *attr;
/*
* The SPD client expects to receive separate entries for
* AH authentication and ESP authentication supported algorithms.
*
* Don't return the "any" algorithms, if defined, as no
* kernel policies can be set for these algorithms.
*/
algcount--;
algcount--;
/*
* For each algorithm, we encode:
* ALG / MINBITS / MAXBITS / DEFBITS / INCRBITS / {END, NEXT}
*/
if (m == NULL) {
return;
}
msg->spd_msg_errno = 0;
msg->spd_msg_diagnostic = 0;
act->spd_actions_reserved = 0;
attr++; \
}
/*
* If you change the number of EMIT's here, change
* ATTRPERALG above to match
*/
#define EMITALGATTRS(_type) { \
}
algidx++) {
if (algtype == IPSEC_ALG_AUTH) {
if (algid == SADB_AALG_NONE)
continue;
} else {
if (algid == SADB_EALG_NONE)
continue;
}
}
}
attr--;
qreply(q, m);
}
/*
* Process a SPD_DUMPALGS request.
*/
void
{
mblk_t *m;
struct spd_ext_actions *act;
struct spd_attribute *attr;
uint_t i;
/*
* For each algorithm, we encode:
* ALG / MINBITS / MAXBITS / DEFBITS / INCRBITS / {END, NEXT}
*
* ALG_ID / ALG_PROTO / ALG_INCRBITS / ALG_NKEYSIZES / ALG_KEYSIZE*
* ALG_NBLOCKSIZES / ALG_BLOCKSIZE* / ALG_NPARAMS / ALG_PARAMS* /
* ALG_MECHNAME / ALG_FLAGS / {END, NEXT}
*/
/*
* Compute the size of the SPD message.
*/
algidx++) {
alg_size = sizeof (struct spd_attribute) *
}
}
if (m == NULL) {
return;
}
msg->spd_msg_errno = 0;
msg->spd_msg_diagnostic = 0;
act->spd_actions_reserved = 0;
/*
* If there aren't any algorithms registered, return an empty message.
* spdsock_get_ext() knows how to deal with this.
*/
if (act->spd_actions_count == 0) {
act->spd_actions_len = 0;
goto error;
}
attr++; \
}
algidx++) {
/*
* If you change the number of EMIT's here, change
* ATTRPERALG above to match
*/
for (i = 0; i < alg->alg_nkey_sizes; i++)
alg->alg_key_sizes[i]);
for (i = 0; i < alg->alg_nblock_sizes; i++)
alg->alg_block_sizes[i]);
for (i = 0; i < alg->alg_nparams; i++)
alg->alg_params[i]);
EMIT(SPD_ATTR_NEXT, 0);
}
}
attr--;
qreply(q, m);
}
/*
* Do the actual work of processing an SPD_UPDATEALGS request. Can
* be invoked either once IPsec is loaded on a cached request, or
* when a request is received while IPsec is loaded.
*/
static int
{
struct spd_ext_actions *actp;
ipsec_algtype_t alg_type = 0;
int diag = -1;
/* parse the message, building the list of algorithms */
return (SPD_DIAGNOSTIC_NO_ACTION_EXT);
sizeof (ipsec_alginfo_t *));
switch (attr->spd_attr_tag) {
case SPD_ATTR_NOP:
case SPD_ATTR_EMPTY:
break;
case SPD_ATTR_END:
/* FALLTHRU */
case SPD_ATTR_NEXT:
if (doing_proto) {
break;
}
if (skip_alg) {
} else {
alg;
}
break;
case SPD_ATTR_ALG_ID:
"invalid alg id %d\n",
attr->spd_attr_value));
goto bail;
}
break;
case SPD_ATTR_ALG_PROTO:
/* find the alg type */
for (i = 0; i < NALGPROTOS; i++)
break;
skip_alg = (i == NALGPROTOS);
if (!skip_alg)
alg_type = i;
break;
case SPD_ATTR_ALG_INCRBITS:
break;
case SPD_ATTR_ALG_NKEYSIZES:
ALG_KEY_SIZES(alg));
}
/*
* Allocate room for the trailing zero key size
* value as well.
*/
KM_SLEEP);
cur_key = 0;
break;
case SPD_ATTR_ALG_KEYSIZE:
"too many key sizes\n"));
goto bail;
}
break;
case SPD_ATTR_ALG_FLAGS:
/*
* Flags (bit mask). The alg_flags element of
* ipsecalg_flags_t is only 8 bits wide. The
* user can set the VALID bit, but we will ignore it
* and make the decision is the algorithm is valid.
*/
break;
case SPD_ATTR_ALG_NBLOCKSIZES:
}
/*
* Allocate room for the trailing zero block size
* value as well.
*/
KM_SLEEP);
cur_block = 0;
break;
case SPD_ATTR_ALG_BLOCKSIZE:
"too many block sizes\n"));
goto bail;
}
break;
case SPD_ATTR_ALG_NPARAMS:
}
/*
* Allocate room for the trailing zero block size
* value as well.
*/
KM_SLEEP);
cur_block = 0;
break;
case SPD_ATTR_ALG_PARAMS:
"too many params\n"));
goto bail;
}
/*
* Array contains: iv_len, icv_len, salt_len
* Any additional parameters are currently ignored.
*/
break;
case SPD_ATTR_ALG_MECHNAME: {
char *mech_name;
"mech name too long\n"));
goto bail;
}
break;
}
case SPD_ATTR_PROTO_ID:
for (i = 0; i < NALGPROTOS; i++) {
alg_type = i;
break;
}
}
break;
case SPD_ATTR_PROTO_EXEC_MODE:
if (!doing_proto)
break;
for (i = 0; i < NEXECMODES; i++) {
break;
}
}
break;
}
attr++;
}
/* update the algorithm tables */
bail:
/* cleanup */
return (diag);
}
/*
* Process an SPD_UPDATEALGS request. If IPsec is not loaded, queue
* the request until IPsec loads. If IPsec is loaded, act on it
* immediately.
*/
static void
{
if (!ipsec_loaded(ipss)) {
/*
* IPsec is not loaded, save request and return nicely,
* the message will be processed once IPsec loads.
*/
/* last update message wins */
return;
}
if (auditing) {
cpid);
}
} else {
/*
* IPsec is loaded, act on the message immediately.
*/
int diag;
if (diag == -1) {
/* Keep the lock held while we walk the SA tables. */
sadb_alg_update(IPSEC_ALG_ALL, 0, 0,
if (auditing) {
cpid);
}
} else {
if (auditing) {
cpid);
}
}
}
}
/*
* Find a tunnel instance (using the name to link ID mapping), and
* update it after an IPsec change. We need to do this always in case
* we add policy AFTER plumbing a tunnel. We also need to do this
* because, as a side-effect, the tunnel's MTU is updated to reflect
* any IPsec overhead in the itp's policy.
*/
static void
{
}
/*
* Sort through the mess of polhead options to retrieve an appropriate one.
* Returns NULL if we send an spdsock error. Returns a valid pointer if we
* found a valid polhead. Returns ALL_ACTIVE_POLHEADS (aka. -1) or
* ALL_INACTIVE_POLHEADS (aka. -2) if the operation calls for the operation to
* act on ALL policy heads.
*/
static ipsec_policy_head_t *
{
int errno;
char *tname;
return (NULL);
}
/* Acting on a tunnel's SPD. */
if (*tname == '\0') {
/* Handle all-polhead cases here. */
spdsock_diag(q, mp,
return (NULL);
}
return (active ? ALL_ACTIVE_POLHEADS :
}
if (msgtype != SPD_ADDRULE) {
/* "Tunnel not found" */
return (NULL);
}
errno = 0;
/*
* Something very bad happened, most likely
* ENOMEM. Return an indicator.
*/
return (NULL);
}
}
/* Match up the itp to an iptun instance. */
/* For spdsock dump state, set the polhead's name. */
}
} else {
/* For spdsock dump state, indicate it's global policy. */
}
if (active)
else
}
return (iph);
}
static void
{
/* Make sure nothing's below me. */
/*
* 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 spd_msg_type is hosed,
* I need to set it to SPD_RESERVED to get delivery to
* do the right thing. Then again, maybe just letting
* the error delivery do the right thing.
*/
("mblk (%lu) and base (%d) message sizes don't jibe.\n",
return;
}
/* Get all message into one mblk. */
/*
* Something screwy happened.
*/
return;
} else {
}
}
case KGE_DUP:
/* Handle duplicate extension. */
extv[0]->spd_ext_type));
return;
case KGE_UNK:
/* Handle unknown extension. */
extv[0]->spd_ext_type));
return;
case KGE_LEN:
/* Length error. */
return;
case KGE_CHK:
/* Reality check failed. */
extv[0]->spd_ext_type));
return;
default:
/* Default case is no errors. */
break;
}
/*
* Special-case SPD_UPDATEALGS so as not to load IPsec.
*/
q, LOADCHECK_INTERVAL);
return;
}
/* First check for messages that need no polheads at all. */
switch (spmsg->spd_msg_type) {
case SPD_UPDATEALGS:
return;
case SPD_ALGLIST:
spdsock_alglist(q, mp);
return;
case SPD_DUMPALGS:
spdsock_dumpalgs(q, mp);
return;
}
/*
* finding the appropriate tunnel policy if need be.
*/
switch (spmsg->spd_msg_type) {
case SPD_FLIP:
return;
case SPD_CLONE:
return;
}
/*
* Finally, find ones that operate on exactly one polhead, or
*/
return;
/* All-polheads-ready operations. */
switch (spmsg->spd_msg_type) {
case SPD_FLUSH:
else
}
/* SPD_FLUSH is worth a tunnel MTU check. */
}
return;
case SPD_DUMP:
return;
}
return;
}
/* Single-polhead-only operations. */
switch (spmsg->spd_msg_type) {
case SPD_ADDRULE:
break;
case SPD_DELETERULE:
break;
case SPD_LOOKUP:
break;
default:
break;
}
/* SPD_{ADD,DELETE}RULE are worth a tunnel MTU check. */
}
}
/*
* If an algorithm mapping was received before IPsec was loaded, process it.
* Called from the IPsec loader.
*/
void
{
}
}
static void
spdsock_loadcheck(void *arg)
{
ss->spdsock_timeout = 0;
if (ipsec_failed(ipss))
else
spdsock_parse(q, mp);
}
/*
* Copy relevant state bits.
*/
static void
{
}
/*
* This routine responds to T_CAPABILITY_REQ messages. It is called by
* spdsock_wput. Much of the T_CAPABILITY_ACK information is copied from
* spdsock_g_t_info_ack. The current state of the stream is copied from
* spdsock_state.
*/
static void
{
struct T_capability_ack *tcap;
return;
}
}
/*
* This routine responds to T_INFO_REQ messages. It is called by
* spdsock_wput_other.
* Most of the T_INFO_ACK information is copied from spdsock_g_t_info_ack.
* The current state of the stream is copied from spdsock_state.
*/
static void
spdsock_info_req(q, mp)
queue_t *q;
{
return;
}
/*
* spdsock_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;
/*
* 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_SNDBUF:
return (ENOBUFS);
break;
case SO_RCVBUF:
return (ENOBUFS);
break;
}
break;
}
return (0);
}
/*
* Handle STREAMS messages.
*/
static void
{
int error;
case M_PROTO:
case M_PCPROTO:
"spdsock_wput_other: Not big enough M_PROTO\n"));
return;
}
case T_CAPABILITY_REQ:
spdsock_capability_req(q, mp);
break;
case T_INFO_REQ:
spdsock_info_req(q, mp);
break;
case T_SVR4_OPTMGMT_REQ:
case T_OPTMGMT_REQ:
/*
* All Solaris components should pass a db_credp
* for this TPI message, hence we ASSERT.
* But in case there is some other M_PROTO that looks
* like a TPI message sent by some other kernel
* component, we check and return an error.
*/
return;
}
} else {
}
break;
case T_DATA_REQ:
case T_EXDATA_REQ:
case T_ORDREL_REQ:
/* Illegal for spdsock. */
break;
default:
/* Not supported by spdsock. */
break;
}
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. */
}
static void
{
/*
* If we're dumping, defer processing other messages until the
* dump completes.
*/
return;
}
case M_DATA:
/*
* Silently discard.
*/
return;
case M_PROTO:
case M_PCPROTO:
/* No data after T_DATA_REQ. */
("No data after DATA_REQ.\n"));
return;
}
break; /* Out of switch. */
}
}
/* FALLTHRU */
default:
spdsock_wput_other(q, mp);
return;
}
/* I now have a PF_POLICY message in an M_DATA block. */
spdsock_parse(q, mp);
}
/*
* Device open procedure, called when new queue pair created.
* We are passed the read-side queue.
*/
/* ARGSUSED */
static int
{
netstack_t *ns;
return (EPERM);
return (0); /* Re-open of an already open instance. */
return (EINVAL);
if (ssminor == 0) {
return (ENOMEM);
}
return (ENOMEM);
}
qprocson(q);
return (0);
}
/*
* Read-side service procedure, invoked when we get back-enabled
* when buffer space becomes available.
*
* Dump another chunk if we were dumping before; when we finish, kick
* the write-side queue in case it's waiting for read queue space.
*/
void
spdsock_rsrv(queue_t *q)
{
spdsock_dump_some(q, ss);
}
/*
* Write-side service procedure, invoked when we defer processing
* if another message is received while a dump is in progress.
*/
void
spdsock_wsrv(queue_t *q)
{
return;
}
if (ipsec_loaded(ipss)) {
spdsock_wput(q, mp);
return;
} else if (!ipsec_failed(ipss)) {
} else {
}
}
}
static int
spdsock_close(queue_t *q)
{
qprocsoff(q);
/* Safe assumption. */
if (ss->spdsock_timeout != 0)
return (0);
}
/*
* Merge the IPsec algorithms tables with the received algorithm information.
*/
void
{
/*
* Get the list of supported mechanisms from the crypto framework.
* If a mechanism is supported by KCF, resolve its mechanism
* id and mark it as being valid. This operation must be done
* without holding alg_lock, since it can cause a provider
* module to be loaded and the provider notification callback to
* be invoked.
*/
int algflags = 0;
continue;
/*
* The NULL encryption algorithm is a special
* case because there are no mechanisms, yet
* the algorithm is still valid.
*/
continue;
}
CRYPTO_MAX_MECH_NAME) == 0) {
break;
}
}
}
}
/*
* For each algorithm currently defined, check if it is
* present in the new tables created from the SPD_UPDATEALGS
* message received from user-space.
* Delete the algorithm entries that are currently defined
* but not part of the new tables.
*/
}
}
/*
* For each algorithm we just received, check if it is
* present in the currently defined tables. If it is, swap
* the entry with the one we just allocated.
* If the new algorithm is not in the current tables,
* add it.
*/
continue;
NULL) {
/*
* New algorithm, add it to the algorithm
* table.
*/
} else {
/*
* Algorithm is already in the table. Swap
* the existing entry with the new one.
*/
}
}
}
}
}