sctp_asconf.c revision bd670b35a010421b6e1a5536c34453a827007c81
/*
* 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 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#include <inet/ipclassifier.h>
#include "sctp_impl.h"
#include "sctp_asconf.h"
#include "sctp_addr.h"
typedef struct sctp_asconf_s {
/*
* This is only used on a clustered node to maintain pre-allocated buffer info.
* before sending an ASCONF chunk. The reason for pre-allocation is we don't
* want to fail allocating memory when we get then ASCONF-ACK in order to
* update the clustering subsystem's state for this assoc.
*/
typedef struct sctp_cl_ainfo_s {
/*
* The ASCONF chunk per-parameter request interface. ph is the
* parameter header for the parameter in the request, and cid
* is the parameters correlation ID. cont should be set to 1
* if the ASCONF framework should continue processing request
* parameters following this one, or 0 if it should stop. If
* cont is -1, this indicates complete memory depletion, which
* will cause the ASCONF framework to abort building a reply. If
* act is 1, the callback should take whatever action it needs
* to fulfil this request. If act is 0, this request has already
* been processed, so the callback should only verify and pass
* back error parameters, and not take any action.
*
* The callback should return an mblk with any reply enclosed,
* with the correlation ID in the first four bytes of the
* message. A NULL return implies implicit success to the
* requestor.
*/
/*
* The ASCONF chunk per-parameter ACK interface. ph is the parameter
* header for the parameter returned in the ACK, and oph is the
* original parameter sent out in the ASCONF request.
* If the peer implicitly responded OK (by not including an
* explicit OK for the request), ph will be NULL.
* ph can also point to an Unrecognized Parameter parameter,
* in which case the peer did not understand the request
* parameter.
*
* ph and oph parameter headers are in host byte order. Encapsulated
* parameters will still be in network byte order.
*/
typedef struct {
} dispatch_t;
static const dispatch_t sctp_asconf_dispatch_tbl[] = {
/* ID ASCONF ASCONF_ACK */
};
static const dispatch_t sctp_asconf_default_dispatch = {
};
/*
* ASCONF framework
*/
static const dispatch_t *
{
int i;
for (i = 0; i < A_CNT(sctp_asconf_dispatch_tbl); i++) {
return (sctp_asconf_dispatch_tbl + i);
}
}
return (&sctp_asconf_default_dispatch);
}
/*
* Frees mp on failure
*/
static mblk_t *
{
/* Prepend a wrapper err cause ind param */
return (NULL);
}
return (wmp);
}
/*ARGSUSED*/
static mblk_t *
{
/* Unrecognized param; check the high order bits */
/* report unrecognized param, and keep processing */
*cont = -1;
return (NULL);
}
/* Prepend a the CID and a wrapper err cause ind param */
*cont = -1;
return (NULL);
}
*cont = 1;
return (mp);
}
/* Stop processing and drop; report unrecognized param */
*cont = -1;
return (NULL);
}
/* Prepend a the CID and a wrapper err cause ind param */
*cont = -1;
return (NULL);
}
*cont = 0;
return (mp);
}
/* skip and continue processing */
*cont = 1;
return (NULL);
}
/* 2 high bits are clear; stop processing and drop packet */
*cont = 0;
return (NULL);
}
/*ARGSUSED*/
static void
{
}
static void
{
}
static int
{
/* XXX can't exceed MTU */
else
return (0);
}
static void
{
}
}
static int
{
/* SCTP chunk header + Serial Number + Address Param TLV */
return (ENOMEM);
return (ENOMEM);
}
/*
* Stash the address list and the count so that when the operation
* completes, i.e. when as get an ACK, we can update the clustering's
* state for this association.
*/
}
/* Clean up the temporary mblk chain */
/* Queue it ... */
} else {
}
/* And try to send it. */
return (0);
}
/*
* If the peer does not understand an ASCONF chunk, we simply
* clear out the cxmit_list, since we can send nothing further
* that the peer will understand.
*
* Assumes chunk length has already been checked.
*/
/*ARGSUSED*/
void
{
/* Nothing pending */
return;
}
}
}
}
void
{
const dispatch_t *dp;
int cont;
int act;
int acount = 0;
int dcount = 0;
/* nothing there; bail out */
return;
}
/* Check for duplicates */
act = 1;
act = 0;
} else {
/* stale or malicious packet; drop */
return;
}
/* Create the ASCONF_ACK header */
/* Let the peer retransmit */
return;
}
/* Set the length later */
/* Move to the Address Parameter */
return;
}
/*
* We already have the association here, so this address parameter
* doesn't seem to be very useful, should we make sure this is part
* of the association and send an error, if not?
* Ignore it for now.
*/
/*
* We need to pre-allocate buffer before processing the ASCONF
* chunk. We don't want to fail allocating buffers after processing
* the ASCONF chunk. So, we walk the list and get the number of
*/
if (cl_sctp_assoc_change != NULL) {
/*
* This not very efficient, but there is no better way of
* doing it. It should be fine since normally the param list
* will not be very long.
*/
while (orlen > 0) {
/* Sanity checks */
break;
break;
acount++;
dcount++;
break;
}
if (acount > 0) {
return;
}
}
if (dcount > 0) {
if (acount > 0)
return;
}
}
/*
* We will get the actual count when we process
* the chunk.
*/
acount = 0;
dcount = 0;
}
}
cont = 1;
/* Sanity checks */
break;
break;
}
if (cont == -1) {
/*
* Not even enough memory to create
* an out-of-resources error. Free
* everything and return; the peer
* should retransmit.
*/
return;
}
} else if (act != 0) {
if (cl_sctp_assoc_change != NULL) {
htons(PARM_ADD_IP)) {
sizeof (addr));
acount++;
htons(PARM_DEL_IP)) {
sizeof (addr));
dcount++;
}
}
}
}
break;
}
/*
* processed successfully). Regardless, if the ?size is > 0,
* it is the clustering module's responsibility to free the lists.
*/
if (cl_sctp_assoc_change != NULL) {
/* alist and dlist will be freed by the clustering module */
}
/* Now that the params have been processed, increment the fcsn */
if (act) {
}
else
}
static sctp_parm_hdr_t *
{
while (rlen > 0) {
return (ph);
}
break;
}
return (NULL);
}
void
{
const dispatch_t *dp;
int redosrcs = 0;
if (rlen < 0) {
return;
}
/* Accept only an ACK for the current serial number */
/* Need to send an abort */
return;
}
sctp->sctp_cchunk_pend = 0;
/*
* We fill in the addresses here to update the clustering's state for
* this assoc.
*/
}
/*
* Pass explicit replies to callbacks:
* For each reply in the ACK, look up the corresponding
* original parameter in the request using the correlation
* ID, and pass it to the right callback.
*/
/* Get to the 1st ASCONF param, need to skip Address TLV parm */
while (rlen > 0) {
/* Sanity checks */
break;
}
break;
}
if (dp->asconf_ack) {
/* hack. see below */
redosrcs = 1;
/*
* If the address was sucessfully
* list to send to the clustering
* module.
*/
if (cl_sctp_assoc_change != NULL &&
addr)) {
htons(PARM_ADD_IP)) {
sizeof (addr));
acount++;
} else {
sizeof (addr));
dcount++;
}
}
}
}
}
break;
}
/*
* Pass implicit replies to callbacks:
* For each original request, look up its parameter
* in the ACK. If there is no corresponding reply,
* call the callback with a NULL parameter, indicating
* success.
*/
sizeof (uint32_t));
while (rlen > 0) {
if (dp->asconf_ack) {
/* hack. see below */
redosrcs = 1;
/*
* If the address was sucessfully
* list to send to the clustering
* module.
*/
if (cl_sctp_assoc_change != NULL &&
addr)) {
htons(PARM_ADD_IP)) {
sizeof (addr));
acount++;
} else {
sizeof (addr));
dcount++;
}
}
}
}
}
break;
}
}
/* We can now free up the first chunk in the cxmit list */
/*
* succeed). Regardless, if the ?size is > 0, it is the clustering
* module's responsibility to free the lists.
*/
if (cl_sctp_assoc_change != NULL) {
/* alist and dlist will be freed by the clustering module */
ainfo->sctp_cl_asize = 0;
ainfo->sctp_cl_dsize = 0;
}
/* can now send the next control chunk */
/*
* If an add-ip or del-ip has completed (successfully or
* unsuccessfully), the pool of available source addresses
* may have changed, so we need to redo faddr source
* address selections. This is a bit of a hack since
* that code consists of callbacks called for *each*
* expensive enough that we really don't want to be
* doing it for each one. So we do it once here.
*/
if (redosrcs)
}
static void
{
fp->rc_timer_running = 0;
return;
}
/*
* Not a retransmission, this was deferred due to some error
* condition
*/
return;
}
/*
* The sent flag indicates if the msg has been sent on this fp.
*/
/* Retransmission */
/* time to give up */
return;
}
return;
}
sctp->sctp_strikes++;
sctp->sctp_cchunk_pend = 0;
/*
* Enter slow start for this destination.
* XXX anything in the data path that needs to be considered?
*/
/*
* The rexmit flags is used to determine if a serial number needs to
* be assigned or not, so once set we leave it there.
*/
}
void
{
/* Queue it for later transmission if not yet established */
ip2dbg(("sctp_wput_asconf: cchunk pending? (%d) or null "\
"sctp_cxmit_list? (%s) or incorrect state? (%x)\n",
return;
}
/* OK to send */
return;
}
/* Fill in the mandatory Address Parameter TLV */
sizeof (uint32_t));
if (isv4) {
} else {
/*
* All the addresses are down.
* Maybe we might have better luck next time.
*/
if (!saddr_set) {
return;
}
}
} else {
} else {
/*
* All the addresses are down.
* Maybe we might have better luck next time.
*/
if (!saddr_set) {
return;
}
}
}
/* Don't exceed CWND */
return;
}
/* Set the serial number now, if sending for the first time */
if (!SCTP_CHUNK_WANT_REXMIT(mp)) {
}
/* Attach the header and send the chunk */
}
/*
* Generate ASCONF error param, include errph, if present.
*/
static mblk_t *
{
}
return (NULL);
}
/* error cause wrapper */
/* error cause */
sizeof (cid));
/* details */
if (elen > 0) {
}
return (mp);
}
static mblk_t *
{
*cont = -1;
}
return (mp);
}
*cont = -1;
}
return (mp);
}
/* Address parameter is present; extract and screen it */
if (atype == PARM_ADDR4) {
/* screen XXX loopback to scoping */
SCTP_PRINTADDR(addr)));
cid);
*cont = -1;
}
return (mp);
}
/*
* XXX also need to check for subnet
* broadcasts. This should probably
* wait until we have full access
* to the ILL tables.
*/
} else {
/* screen XXX loopback to scoping */
if (IN6_IS_ADDR_LINKLOCAL(&addr) ||
IN6_IS_ADDR_LOOPBACK(&addr)) {
SCTP_PRINTADDR(addr)));
cid);
*cont = -1;
}
return (mp);
}
}
/* OK */
return (NULL);
}
/*
* Handles both add and delete address requests.
*/
static mblk_t *
{
int err;
*cont = 1;
/* Send back an authorization error if addip is disabled */
if (!sctps->sctps_addip_enabled) {
goto error_handler;
}
/* Check input */
goto error_handler;
}
sizeof (cid));
return (mp);
if (type == PARM_ADD_IP) {
/* Address is already part of association */
SCTP_PRINTADDR(addr)));
goto error_handler;
}
if (!act) {
return (NULL);
}
/* Add the new address */
/* no memory */
*cont = -1;
return (NULL);
}
if (err != 0) {
goto error_handler;
}
} else if (type == PARM_DEL_IP) {
/*
* Peer is trying to delete an address that is not
* part of the association.
*/
SCTP_PRINTADDR(addr)));
goto error_handler;
}
/* Peer is trying to delete last address */
SCTP_PRINTADDR(addr)));
goto error_handler;
}
/* Peer is trying to delete source address */
SCTP_PRINTADDR(addr)));
goto error_handler;
}
if (!act) {
return (NULL);
}
/* Update all references to the deleted faddr */
}
}
}
}
;
}
} else {
ASSERT(0);
}
/* Successful, don't need to return anything. */
return (NULL);
*cont = -1;
return (mp);
}
/*
* Handles both add and delete IP ACKs.
*/
/*ARGSUSED*/
static void
{
/* could be an ASSERT */
/* If the peer doesn't understand Add-IP, remember it */
}
/*
* If OK, continue with the add / delete action, otherwise
* back out the action.
*/
}
} else {
}
/* Signifies that the address was sucessfully processed */
if (type == PARM_ADD_IP) {
if (backout) {
} else {
sp->saddr_ipif_dontsrc = 0;
}
} else if (type == PARM_DEL_IP) {
if (backout) {
sp->saddr_ipif_delete_pending = 0;
sp->saddr_ipif_dontsrc = 0;
} else {
}
} else {
/* Must be either PARM_ADD_IP or PARM_DEL_IP */
ASSERT(0);
}
}
/*ARGSUSED*/
static mblk_t *
{
*cont = 1;
/* Check input */
*cont = -1;
}
return (mp);
}
sizeof (cid));
return (mp);
}
/*
* Peer is trying to set an address that is not
* part of the association.
*/
SCTP_PRINTADDR(addr)));
*cont = -1;
}
return (mp);
}
return (NULL);
}
return (NULL);
}
/*ARGSUSED*/
static void
{
/* If the peer doesn't understand Add-IP, remember it */
}
}
/* On success we do nothing */
}
int
{
struct sockaddr_in *sin4;
struct sockaddr_in6 *sin6;
int error = 0;
int i;
/* Does the peer understand ASCONF and Add-IP? */
return (EOPNOTSUPP);
/*
* On a clustered node, we need to pass this list when
* we get an ASCONF-ACK. We only pre-allocate memory for the
* list, but fill in the addresses when it is processed
* successfully after we get an ASCONF-ACK.
*/
if (cl_sctp_assoc_change != NULL) {
/*
* Reserve space for the list of new addresses
*/
KM_SLEEP);
}
/*
* Screen addresses:
* If adding:
* o Must not already be a part of the association
* o Must be AF_INET or AF_INET6
* o XXX Must be valid source address for this node
* o Must be unicast
* o XXX Must fit scoping rules
* If deleting:
* o Must be part of the association
*/
for (i = 0; i < cnt; i++) {
switch (connp->conn_family) {
case AF_INET:
break;
case AF_INET6:
break;
}
if (v4mapped) {
goto fail;
}
htons(sizeof (sctp_parm_hdr_t) +
} else {
goto fail;
}
htons(sizeof (sctp_parm_hdr_t) +
}
if (error != 0)
goto fail;
}
if (error != 0)
goto fail;
return (0);
fail:
ainfo->sctp_cl_asize = 0;
}
return (error);
}
int
{
struct sockaddr_in *sin4;
struct sockaddr_in6 *sin6;
int error = 0;
int i;
int addrcnt = 0;
/* Does the peer understand ASCONF and Add-IP? */
}
if (asconf) {
/*
* On a clustered node, we need to pass this list when
* we get an ASCONF-ACK. We only pre-allocate memory for the
* list, but fill in the addresses when it is processed
* successfully after we get an ASCONF-ACK.
*/
if (cl_sctp_assoc_change != NULL) {
KM_SLEEP);
}
}
/*
* Screen addresses:
* If adding:
* o Must not already be a part of the association
* o Must be AF_INET or AF_INET6
* o XXX Must be valid source address for this node
* o Must be unicast
* o XXX Must fit scoping rules
* If deleting:
* o Must be part of the association
*/
for (i = 0; i < cnt; i++) {
ifindex = 0;
switch (connp->conn_family) {
case AF_INET:
if (check_lport &&
goto fail;
}
break;
case AF_INET6:
if (check_lport &&
goto fail;
}
break;
}
goto fail;
}
/* Collect the list of addresses, if required */
p += sizeof (addr);
}
if (!asconf)
continue;
addrcnt++;
if (v4mapped) {
goto fail;
}
htons(sizeof (sctp_parm_hdr_t) +
} else {
goto fail;
}
sizeof (ad6->asconf_req_cid));
}
if (error != 0)
goto fail;
}
if (!asconf) {
return (0);
}
if (error != 0)
goto fail;
return (0);
fail:
ainfo->sctp_cl_dsize = 0;
}
if (!asconf)
return (error);
for (i = 0; i < addrcnt; i++) {
ifindex = 0;
switch (connp->conn_family) {
case AF_INET:
break;
case AF_INET6:
break;
}
nsp->saddr_ipif_delete_pending = 0;
nsp->saddr_ipif_dontsrc = 0;
}
return (error);
}
int
{
const struct sockaddr_storage *ss;
struct sockaddr_in *sin;
struct sockaddr_in6 *sin6;
int error = 0;
/* Does the peer understand ASCONF and Add-IP? */
return (EOPNOTSUPP);
}
/* Don't do anything if we are not connected */
return (EINVAL);
} else {
return (EAFNOSUPPORT);
}
return (EADDRNOTAVAIL);
if (sin) {
goto fail;
}
} else {
goto fail;
}
}
if (error != 0) {
goto fail;
}
if (error == 0) {
return (0);
}
fail:
return (error);
}