Commit 3c2490e2 authored by David S. Miller's avatar David S. Miller

Merge nuts.ninka.net:/home/davem/src/BK/sctp-2.5

into nuts.ninka.net:/home/davem/src/BK/net-2.5
parents b9722f1f d5e3a331
...@@ -155,15 +155,15 @@ typedef struct sctp_paramhdr { ...@@ -155,15 +155,15 @@ typedef struct sctp_paramhdr {
typedef enum { typedef enum {
/* RFC 2960 Section 3.3.5 */ /* RFC 2960 Section 3.3.5 */
SCTP_PARAM_HEATBEAT_INFO = __constant_htons(1), SCTP_PARAM_HEARTBEAT_INFO = __constant_htons(1),
/* RFC 2960 Section 3.3.2.1 */ /* RFC 2960 Section 3.3.2.1 */
SCTP_PARAM_IPV4_ADDRESS = __constant_htons(5), SCTP_PARAM_IPV4_ADDRESS = __constant_htons(5),
SCTP_PARAM_IPV6_ADDRESS = __constant_htons(6), SCTP_PARAM_IPV6_ADDRESS = __constant_htons(6),
SCTP_PARAM_STATE_COOKIE = __constant_htons(7), SCTP_PARAM_STATE_COOKIE = __constant_htons(7),
SCTP_PARAM_UNRECOGNIZED_PARAMETERS = __constant_htons(8), SCTP_PARAM_UNRECOGNIZED_PARAMETERS = __constant_htons(8),
SCTP_PARAM_COOKIE_PRESERVATIVE = __constant_htons(9), SCTP_PARAM_COOKIE_PRESERVATIVE = __constant_htons(9),
SCTP_PARAM_HOST_NAME_ADDRESS = __constant_htons(11), SCTP_PARAM_HOST_NAME_ADDRESS = __constant_htons(11),
SCTP_PARAM_SUPPORTED_ADDRESS_TYPES = __constant_htons(12), SCTP_PARAM_SUPPORTED_ADDRESS_TYPES = __constant_htons(12),
SCTP_PARAM_ECN_CAPABLE = __constant_htons(0x8000), SCTP_PARAM_ECN_CAPABLE = __constant_htons(0x8000),
/* Add-IP Extension. Section 3.2 */ /* Add-IP Extension. Section 3.2 */
...@@ -190,6 +190,7 @@ typedef enum { ...@@ -190,6 +190,7 @@ typedef enum {
SCTP_PARAM_ACTION_SKIP_ERR = __constant_htons(0xc000), SCTP_PARAM_ACTION_SKIP_ERR = __constant_htons(0xc000),
} sctp_param_action_t; } sctp_param_action_t;
enum { SCTP_PARAM_ACTION_MASK = __constant_htons(0xc000), };
/* RFC 2960 Section 3.3.1 Payload Data (DATA) (0) */ /* RFC 2960 Section 3.3.1 Payload Data (DATA) (0) */
......
...@@ -103,6 +103,14 @@ ...@@ -103,6 +103,14 @@
#define SCTP_PROTOSW_FLAG INET_PROTOSW_PERMANENT #define SCTP_PROTOSW_FLAG INET_PROTOSW_PERMANENT
#endif #endif
/* Certain internal static functions need to be exported when
* compiled into the test frame.
*/
#ifndef SCTP_STATIC
#define SCTP_STATIC static
#endif
/* /*
* Function declarations. * Function declarations.
*/ */
......
...@@ -215,7 +215,8 @@ sctp_chunk_t *sctp_make_init(const sctp_association_t *, ...@@ -215,7 +215,8 @@ sctp_chunk_t *sctp_make_init(const sctp_association_t *,
int priority); int priority);
sctp_chunk_t *sctp_make_init_ack(const sctp_association_t *, sctp_chunk_t *sctp_make_init_ack(const sctp_association_t *,
const sctp_chunk_t *, const sctp_chunk_t *,
const int priority); const int priority,
const int unkparam_len);
sctp_chunk_t *sctp_make_cookie_echo(const sctp_association_t *, sctp_chunk_t *sctp_make_cookie_echo(const sctp_association_t *,
const sctp_chunk_t *); const sctp_chunk_t *);
sctp_chunk_t *sctp_make_cookie_ack(const sctp_association_t *, sctp_chunk_t *sctp_make_cookie_ack(const sctp_association_t *,
...@@ -304,6 +305,14 @@ void sctp_generate_t3_rtx_event(unsigned long peer); ...@@ -304,6 +305,14 @@ void sctp_generate_t3_rtx_event(unsigned long peer);
void sctp_generate_heartbeat_event(unsigned long peer); void sctp_generate_heartbeat_event(unsigned long peer);
sctp_sackhdr_t *sctp_sm_pull_sack(sctp_chunk_t *); sctp_sackhdr_t *sctp_sm_pull_sack(sctp_chunk_t *);
sctp_packet_t *sctp_abort_pkt_new(const sctp_endpoint_t *ep,
const sctp_association_t *asoc,
sctp_chunk_t *chunk,
const void *payload,
size_t paylen);
sctp_packet_t *sctp_ootb_pkt_new(const sctp_association_t *asoc,
const sctp_chunk_t *chunk);
void sctp_ootb_pkt_free(sctp_packet_t *packet);
sctp_cookie_param_t * sctp_cookie_param_t *
sctp_pack_cookie(const sctp_endpoint_t *, const sctp_association_t *, sctp_pack_cookie(const sctp_endpoint_t *, const sctp_association_t *,
......
...@@ -1044,6 +1044,20 @@ sctp_association_t *sctp_endpoint_lookup_assoc(const sctp_endpoint_t *ep, ...@@ -1044,6 +1044,20 @@ sctp_association_t *sctp_endpoint_lookup_assoc(const sctp_endpoint_t *ep,
sctp_endpoint_t *sctp_endpoint_is_match(sctp_endpoint_t *, sctp_endpoint_t *sctp_endpoint_is_match(sctp_endpoint_t *,
const sockaddr_storage_t *); const sockaddr_storage_t *);
int sctp_verify_init(const sctp_association_t *asoc,
sctp_cid_t cid,
sctp_init_chunk_t *peer_init,
sctp_chunk_t *chunk,
sctp_chunk_t **err_chunk);
int sctp_verify_param(const sctp_association_t *asoc,
sctpParam_t param,
sctp_cid_t cid,
sctp_chunk_t *chunk,
sctp_chunk_t **err_chunk);
int sctp_process_unk_param(const sctp_association_t *asoc,
sctpParam_t param,
sctp_chunk_t *chunk,
sctp_chunk_t **err_chunk);
void sctp_process_init(sctp_association_t *asoc, sctp_cid_t cid, void sctp_process_init(sctp_association_t *asoc, sctp_cid_t cid,
const sockaddr_storage_t *peer_addr, const sockaddr_storage_t *peer_addr,
sctp_init_chunk_t *peer_init, int priority); sctp_init_chunk_t *peer_init, int priority);
......
...@@ -97,7 +97,7 @@ const char *sctp_cname(const sctp_subtype_t cid) ...@@ -97,7 +97,7 @@ const char *sctp_cname(const sctp_subtype_t cid)
/* These are printable form of variable-length parameters. */ /* These are printable form of variable-length parameters. */
const char *sctp_param_tbl[SCTP_PARAM_ECN_CAPABLE + 1] = { const char *sctp_param_tbl[SCTP_PARAM_ECN_CAPABLE + 1] = {
"", "",
"PARAM_HEATBEAT_INFO", "PARAM_HEARTBEAT_INFO",
"", "",
"", "",
"", "",
......
...@@ -104,7 +104,7 @@ void sctp_outqueue_init(sctp_association_t *asoc, sctp_outqueue_t *q) ...@@ -104,7 +104,7 @@ void sctp_outqueue_init(sctp_association_t *asoc, sctp_outqueue_t *q)
void sctp_outqueue_teardown(sctp_outqueue_t *q) void sctp_outqueue_teardown(sctp_outqueue_t *q)
{ {
sctp_transport_t *transport; sctp_transport_t *transport;
struct list_head *lchunk, *pos; struct list_head *lchunk, *pos, *temp;
sctp_chunk_t *chunk; sctp_chunk_t *chunk;
/* Throw away unacknowledged chunks. */ /* Throw away unacknowledged chunks. */
...@@ -117,6 +117,13 @@ void sctp_outqueue_teardown(sctp_outqueue_t *q) ...@@ -117,6 +117,13 @@ void sctp_outqueue_teardown(sctp_outqueue_t *q)
} }
} }
/* Throw away chunks that have been gap ACKed. */
list_for_each_safe(lchunk, temp, &q->sacked) {
list_del(lchunk);
chunk = list_entry(lchunk, sctp_chunk_t, transmitted_list);
sctp_free_chunk(chunk);
}
/* Throw away any leftover chunks. */ /* Throw away any leftover chunks. */
while ((chunk = (sctp_chunk_t *) skb_dequeue(&q->out))) while ((chunk = (sctp_chunk_t *) skb_dequeue(&q->out)))
sctp_free_chunk(chunk); sctp_free_chunk(chunk);
......
...@@ -223,7 +223,7 @@ sctp_chunk_t *sctp_make_init(const sctp_association_t *asoc, ...@@ -223,7 +223,7 @@ sctp_chunk_t *sctp_make_init(const sctp_association_t *asoc,
sctp_chunk_t *sctp_make_init_ack(const sctp_association_t *asoc, sctp_chunk_t *sctp_make_init_ack(const sctp_association_t *asoc,
const sctp_chunk_t *chunk, const sctp_chunk_t *chunk,
int priority) int priority, int unkparam_len)
{ {
sctp_inithdr_t initack; sctp_inithdr_t initack;
sctp_chunk_t *retval; sctp_chunk_t *retval;
...@@ -278,7 +278,10 @@ sctp_chunk_t *sctp_make_init_ack(const sctp_association_t *asoc, ...@@ -278,7 +278,10 @@ sctp_chunk_t *sctp_make_init_ack(const sctp_association_t *asoc,
if (!cookie) if (!cookie)
goto nomem_cookie; goto nomem_cookie;
chunksize = sizeof(initack) + addrs_len + cookie_len; /* Calculate the total size of allocation, include the reserved
* space for reporting unknown parameters if it is specified.
*/
chunksize = sizeof(initack) + addrs_len + cookie_len + unkparam_len;
/* Tell peer that we'll do ECN only if peer advertised such cap. */ /* Tell peer that we'll do ECN only if peer advertised such cap. */
if (asoc->peer.ecn_capable) if (asoc->peer.ecn_capable)
...@@ -883,25 +886,27 @@ sctp_chunk_t *sctp_make_heartbeat_ack(const sctp_association_t *asoc, ...@@ -883,25 +886,27 @@ sctp_chunk_t *sctp_make_heartbeat_ack(const sctp_association_t *asoc,
return retval; return retval;
} }
/* Create an Operation Error chunk. */ /* Create an Operation Error chunk with the specified space reserved.
sctp_chunk_t *sctp_make_op_error(const sctp_association_t *asoc, * This routine can be used for containing multiple causes in the chunk.
const sctp_chunk_t *chunk, */
__u16 cause_code, const void *payload, sctp_chunk_t *sctp_make_op_error_space(const sctp_association_t *asoc,
size_t paylen) const sctp_chunk_t *chunk,
size_t size)
{ {
sctp_chunk_t *retval = sctp_make_chunk(asoc, SCTP_CID_ERROR, 0, sctp_chunk_t *retval;
sizeof(sctp_errhdr_t) + paylen);
retval = sctp_make_chunk(asoc, SCTP_CID_ERROR, 0,
sizeof(sctp_errhdr_t) + size);
if (!retval) if (!retval)
goto nodata; goto nodata;
sctp_init_cause(retval, cause_code, payload, paylen);
/* RFC 2960 6.4 Multi-homed SCTP Endpoints /* RFC 2960 6.4 Multi-homed SCTP Endpoints
* *
* An endpoint SHOULD transmit reply chunks (e.g., SACK, * An endpoint SHOULD transmit reply chunks (e.g., SACK,
* HEARTBEAT ACK, * etc.) to the same destination transport * HEARTBEAT ACK, etc.) to the same destination transport
* address from which it * received the DATA or control chunk * address from which it received the DATA or control chunk
* to which it is replying. * to which it is replying.
*
*/ */
if (chunk) if (chunk)
retval->transport = chunk->transport; retval->transport = chunk->transport;
...@@ -910,6 +915,23 @@ sctp_chunk_t *sctp_make_op_error(const sctp_association_t *asoc, ...@@ -910,6 +915,23 @@ sctp_chunk_t *sctp_make_op_error(const sctp_association_t *asoc,
return retval; return retval;
} }
/* Create an Operation Error chunk. */
sctp_chunk_t *sctp_make_op_error(const sctp_association_t *asoc,
const sctp_chunk_t *chunk,
__u16 cause_code, const void *payload,
size_t paylen)
{
sctp_chunk_t *retval = sctp_make_op_error_space(asoc, chunk, paylen);
if (!retval)
goto nodata;
sctp_init_cause(retval, cause_code, payload, paylen);
nodata:
return retval;
}
/******************************************************************** /********************************************************************
* 2nd Level Abstractions * 2nd Level Abstractions
********************************************************************/ ********************************************************************/
...@@ -1405,6 +1427,162 @@ sctp_association_t *sctp_unpack_cookie(const sctp_endpoint_t *ep, ...@@ -1405,6 +1427,162 @@ sctp_association_t *sctp_unpack_cookie(const sctp_endpoint_t *ep,
* 3rd Level Abstractions * 3rd Level Abstractions
********************************************************************/ ********************************************************************/
/* Verify the INIT packet before we process it. */
int sctp_verify_init(const sctp_association_t *asoc,
sctp_cid_t cid,
sctp_init_chunk_t *peer_init,
sctp_chunk_t *chunk,
sctp_chunk_t **err_chk_p)
{
sctpParam_t param;
uint8_t *end;
/* FIXME - Verify the fixed fields of the INIT chunk. Also, verify
* the mandatory parameters somewhere here and generate either the
* "Missing mandatory parameter" error or the "Invalid mandatory
* parameter" error. */
/* Find unrecognized parameters. */
end = ((uint8_t *)peer_init + ntohs(peer_init->chunk_hdr.length));
for (param.v = peer_init->init_hdr.params;
param.v < end;
param.v += WORD_ROUND(ntohs(param.p->length))) {
if (!sctp_verify_param(asoc, param, cid, chunk, err_chk_p))
return 0;
} /* for (loop through all parameters) */
return 1;
}
/* Find unrecognized parameters in the chunk.
* Return values:
* 0 - discard the chunk
* 1 - continue with the chunk
*/
int sctp_verify_param(const sctp_association_t *asoc,
sctpParam_t param,
sctp_cid_t cid,
sctp_chunk_t *chunk,
sctp_chunk_t **err_chk_p)
{
int retval = 1;
/* FIXME - This routine is not looking at each parameter per the
* chunk type, i.e., unrecognized parameters should be further
* identified based on the chunk id.
*/
switch (param.p->type) {
case SCTP_PARAM_IPV4_ADDRESS:
case SCTP_PARAM_IPV6_ADDRESS:
case SCTP_PARAM_COOKIE_PRESERVATIVE:
/* FIXME - If we don't support the host name parameter, we should
* generate an error for this - Unresolvable address.
*/
case SCTP_PARAM_HOST_NAME_ADDRESS:
case SCTP_PARAM_SUPPORTED_ADDRESS_TYPES:
case SCTP_PARAM_STATE_COOKIE:
case SCTP_PARAM_HEARTBEAT_INFO:
case SCTP_PARAM_UNRECOGNIZED_PARAMETERS:
case SCTP_PARAM_ECN_CAPABLE:
break;
default:
SCTP_DEBUG_PRINTK("Unrecognized param: %d for chunk %d.\n",
ntohs(param.p->type), cid);
return sctp_process_unk_param(asoc, param, chunk, err_chk_p);
break;
}
return retval;
}
/* RFC 3.2.1 & the Implementers Guide 2.2.
*
* The Parameter Types are encoded such that the
* highest-order two bits specify the action that must be
* taken if the processing endpoint does not recognize the
* Parameter Type.
*
* 00 - Stop processing this SCTP chunk and discard it,
* do not process any further chunks within it.
*
* 01 - Stop processing this SCTP chunk and discard it,
* do not process any further chunks within it, and report
* the unrecognized parameter in an 'Unrecognized
* Parameter Type' (in either an ERROR or in the INIT ACK).
*
* 10 - Skip this parameter and continue processing.
*
* 11 - Skip this parameter and continue processing but
* report the unrecognized parameter in an
* 'Unrecognized Parameter Type' (in either an ERROR or in
* the INIT ACK).
*
* Return value:
* 0 - discard the chunk
* 1 - continue with the chunk
*/
int sctp_process_unk_param(const sctp_association_t *asoc,
sctpParam_t param,
sctp_chunk_t *chunk,
sctp_chunk_t **err_chk_p)
{
int retval = 1;
switch (param.p->type & SCTP_PARAM_ACTION_MASK) {
case SCTP_PARAM_ACTION_DISCARD:
retval = 0;
break;
case SCTP_PARAM_ACTION_DISCARD_ERR:
retval = 0;
/* Make an ERROR chunk, preparing enough room for
* returning multiple unknown parameters.
*/
if (NULL == *err_chk_p)
*err_chk_p = sctp_make_op_error_space(asoc, chunk,
ntohs(chunk->chunk_hdr->length));
if (*err_chk_p)
sctp_init_cause(*err_chk_p, SCTP_ERROR_UNKNOWN_PARAM,
(const void *)param.p,
WORD_ROUND(ntohs(param.p->length)));
break;
case SCTP_PARAM_ACTION_SKIP:
break;
case SCTP_PARAM_ACTION_SKIP_ERR:
/* Make an ERROR chunk, preparing enough room for
* returning multiple unknown parameters.
*/
if (NULL == *err_chk_p)
*err_chk_p = sctp_make_op_error_space(asoc, chunk,
ntohs(chunk->chunk_hdr->length));
if (*err_chk_p) {
sctp_init_cause(*err_chk_p, SCTP_ERROR_UNKNOWN_PARAM,
(const void *)param.p,
WORD_ROUND(ntohs(param.p->length)));
} else {
/* If there is no memory for generating the ERROR
* report as specified, an ABORT will be triggered
* to the peer and the association won't be established.
*/
retval = 0;
}
break;
default:
break;
}
return retval;
}
/* Unpack the parameters in an INIT packet. /* Unpack the parameters in an INIT packet.
* FIXME: There is no return status to allow callers to do * FIXME: There is no return status to allow callers to do
* error handling. * error handling.
...@@ -1609,9 +1787,9 @@ int sctp_process_param(sctp_association_t *asoc, sctpParam_t param, ...@@ -1609,9 +1787,9 @@ int sctp_process_param(sctp_association_t *asoc, sctpParam_t param,
asoc->peer.cookie = param.cookie->body; asoc->peer.cookie = param.cookie->body;
break; break;
case SCTP_PARAM_HEATBEAT_INFO: case SCTP_PARAM_HEARTBEAT_INFO:
SCTP_DEBUG_PRINTK("unimplemented " SCTP_DEBUG_PRINTK("unimplemented "
"SCTP_PARAM_HEATBEAT_INFO\n"); "SCTP_PARAM_HEARTBEAT_INFO\n");
break; break;
case SCTP_PARAM_UNRECOGNIZED_PARAMETERS: case SCTP_PARAM_UNRECOGNIZED_PARAMETERS:
...@@ -1624,14 +1802,13 @@ int sctp_process_param(sctp_association_t *asoc, sctpParam_t param, ...@@ -1624,14 +1802,13 @@ int sctp_process_param(sctp_association_t *asoc, sctpParam_t param,
break; break;
default: default:
/* Any unrecognized parameters should have been caught
* and handled by sctp_verify_param() which should be
* called prior to this routine. Simply log the error
* here.
*/
SCTP_DEBUG_PRINTK("Ignoring param: %d for association %p.\n", SCTP_DEBUG_PRINTK("Ignoring param: %d for association %p.\n",
ntohs(param.p->type), asoc); ntohs(param.p->type), asoc);
/* FIXME: The entire parameter processing really needs
* redesigned. For now, always return success as doing
* otherwise craters the system.
*/
retval = 1;
break; break;
}; };
......
...@@ -327,7 +327,8 @@ int sctp_cmd_interpreter(sctp_event_t event_type, sctp_subtype_t subtype, ...@@ -327,7 +327,8 @@ int sctp_cmd_interpreter(sctp_event_t event_type, sctp_subtype_t subtype,
case SCTP_CMD_GEN_INIT_ACK: case SCTP_CMD_GEN_INIT_ACK:
/* Generate an INIT ACK chunk. */ /* Generate an INIT ACK chunk. */
new_obj = sctp_make_init_ack(asoc, chunk, GFP_ATOMIC); new_obj = sctp_make_init_ack(asoc, chunk, GFP_ATOMIC,
0);
if (!new_obj) if (!new_obj)
goto nomem; goto nomem;
...@@ -344,10 +345,20 @@ int sctp_cmd_interpreter(sctp_event_t event_type, sctp_subtype_t subtype, ...@@ -344,10 +345,20 @@ int sctp_cmd_interpreter(sctp_event_t event_type, sctp_subtype_t subtype,
case SCTP_CMD_GEN_COOKIE_ECHO: case SCTP_CMD_GEN_COOKIE_ECHO:
/* Generate a COOKIE ECHO chunk. */ /* Generate a COOKIE ECHO chunk. */
new_obj = sctp_make_cookie_echo(asoc, chunk); new_obj = sctp_make_cookie_echo(asoc, chunk);
if (!new_obj) if (!new_obj) {
if (command->obj.ptr)
sctp_free_chunk(command->obj.ptr);
goto nomem; goto nomem;
}
sctp_add_cmd_sf(commands, SCTP_CMD_REPLY, sctp_add_cmd_sf(commands, SCTP_CMD_REPLY,
SCTP_CHUNK(new_obj)); SCTP_CHUNK(new_obj));
/* If there is an ERROR chunk to be sent along with
* the COOKIE_ECHO, send it, too.
*/
if (command->obj.ptr)
sctp_add_cmd_sf(commands, SCTP_CMD_REPLY,
SCTP_CHUNK(command->obj.ptr));
break; break;
case SCTP_CMD_GEN_SHUTDOWN: case SCTP_CMD_GEN_SHUTDOWN:
...@@ -397,8 +408,7 @@ int sctp_cmd_interpreter(sctp_event_t event_type, sctp_subtype_t subtype, ...@@ -397,8 +408,7 @@ int sctp_cmd_interpreter(sctp_event_t event_type, sctp_subtype_t subtype,
/* Send a full packet to our peer. */ /* Send a full packet to our peer. */
packet = command->obj.ptr; packet = command->obj.ptr;
sctp_packet_transmit(packet); sctp_packet_transmit(packet);
sctp_transport_free(packet->transport); sctp_ootb_pkt_free(packet);
sctp_packet_free(packet);
break; break;
case SCTP_CMD_RETRAN: case SCTP_CMD_RETRAN:
......
...@@ -194,6 +194,10 @@ sctp_disposition_t sctp_sf_do_5_1B_init(const sctp_endpoint_t *ep, ...@@ -194,6 +194,10 @@ sctp_disposition_t sctp_sf_do_5_1B_init(const sctp_endpoint_t *ep,
sctp_chunk_t *chunk = arg; sctp_chunk_t *chunk = arg;
sctp_chunk_t *repl; sctp_chunk_t *repl;
sctp_association_t *new_asoc; sctp_association_t *new_asoc;
sctp_chunk_t *err_chunk;
sctp_packet_t *packet;
sctp_unrecognized_param_t *unk_param;
int len;
/* If the packet is an OOTB packet which is temporarily on the /* If the packet is an OOTB packet which is temporarily on the
* control endpoint, responding with an ABORT. * control endpoint, responding with an ABORT.
...@@ -208,6 +212,36 @@ sctp_disposition_t sctp_sf_do_5_1B_init(const sctp_endpoint_t *ep, ...@@ -208,6 +212,36 @@ sctp_disposition_t sctp_sf_do_5_1B_init(const sctp_endpoint_t *ep,
if (!chunk->singleton) if (!chunk->singleton)
return SCTP_DISPOSITION_VIOLATION; return SCTP_DISPOSITION_VIOLATION;
/* Verify the INIT chunk before processing it. */
err_chunk = NULL;
if (!sctp_verify_init(asoc, chunk->chunk_hdr->type,
(sctp_init_chunk_t *)chunk->chunk_hdr, chunk,
&err_chunk)) {
/* This chunk contains fatal error. It is to be discarded.
* Send an ABORT, with causes if there is any.
*/
if (err_chunk) {
packet = sctp_abort_pkt_new(ep, asoc, arg,
(__u8 *)(err_chunk->chunk_hdr) +
sizeof(sctp_chunkhdr_t),
ntohs(err_chunk->chunk_hdr->length) -
sizeof(sctp_chunkhdr_t));
sctp_free_chunk(err_chunk);
if (packet) {
sctp_add_cmd_sf(commands, SCTP_CMD_SEND_PKT,
SCTP_PACKET(packet));
return SCTP_DISPOSITION_CONSUME;
} else {
return SCTP_DISPOSITION_NOMEM;
}
} else {
return sctp_sf_tabort_8_4_8(ep, asoc, type, arg,
commands);
}
}
/* Grab the INIT header. */ /* Grab the INIT header. */
chunk->subh.init_hdr = (sctp_inithdr_t *)chunk->skb->data; chunk->subh.init_hdr = (sctp_inithdr_t *)chunk->skb->data;
...@@ -230,10 +264,41 @@ sctp_disposition_t sctp_sf_do_5_1B_init(const sctp_endpoint_t *ep, ...@@ -230,10 +264,41 @@ sctp_disposition_t sctp_sf_do_5_1B_init(const sctp_endpoint_t *ep,
sctp_add_cmd_sf(commands, SCTP_CMD_NEW_ASOC, SCTP_ASOC(new_asoc)); sctp_add_cmd_sf(commands, SCTP_CMD_NEW_ASOC, SCTP_ASOC(new_asoc));
/* B) "Z" shall respond immediately with an INIT ACK chunk. */ /* B) "Z" shall respond immediately with an INIT ACK chunk. */
repl = sctp_make_init_ack(new_asoc, chunk, GFP_ATOMIC);
/* If there are errors need to be reported for unknown parameters,
* make sure to reserve enough room in the INIT ACK for them.
*/
len = 0;
if (err_chunk)
len = ntohs(err_chunk->chunk_hdr->length) -
sizeof(sctp_chunkhdr_t);
repl = sctp_make_init_ack(new_asoc, chunk, GFP_ATOMIC, len);
if (!repl) if (!repl)
goto nomem_ack; goto nomem_ack;
/* If there are errors need to be reported for unknown parameters,
* include them in the outgoing INIT ACK as "Unrecognized parameter"
* parameter.
*/
if (err_chunk) {
/* Get the "Unrecognized parameter" parameter(s) out of the
* ERROR chunk generated by sctp_verify_init(). Since the
* error cause code for "unknown parameter" and the
* "Unrecognized parameter" type is the same, we can
* construct the parameters in INIT ACK by copying the
* ERROR causes over.
*/
unk_param = (sctp_unrecognized_param_t *)
((__u8 *)(err_chunk->chunk_hdr) +
sizeof(sctp_chunkhdr_t));
/* Replace the cause code with the "Unrecognized parameter"
* parameter type.
*/
sctp_addto_chunk(repl, len, unk_param);
sctp_free_chunk(err_chunk);
}
sctp_add_cmd_sf(commands, SCTP_CMD_REPLY, SCTP_CHUNK(repl)); sctp_add_cmd_sf(commands, SCTP_CMD_REPLY, SCTP_CHUNK(repl));
/* /*
...@@ -248,6 +313,9 @@ sctp_disposition_t sctp_sf_do_5_1B_init(const sctp_endpoint_t *ep, ...@@ -248,6 +313,9 @@ sctp_disposition_t sctp_sf_do_5_1B_init(const sctp_endpoint_t *ep,
nomem_ack: nomem_ack:
sctp_association_free(new_asoc); sctp_association_free(new_asoc);
if (err_chunk)
sctp_free_chunk(err_chunk);
nomem: nomem:
return SCTP_DISPOSITION_NOMEM; return SCTP_DISPOSITION_NOMEM;
} }
...@@ -289,6 +357,10 @@ sctp_disposition_t sctp_sf_do_5_1C_ack(const sctp_endpoint_t *ep, ...@@ -289,6 +357,10 @@ sctp_disposition_t sctp_sf_do_5_1C_ack(const sctp_endpoint_t *ep,
sctp_chunk_t *chunk = arg; sctp_chunk_t *chunk = arg;
sctp_init_chunk_t *initchunk; sctp_init_chunk_t *initchunk;
__u32 init_tag; __u32 init_tag;
sctp_chunk_t *err_chunk;
sctp_packet_t *packet;
sctp_disposition_t ret;
/* 6.10 Bundling /* 6.10 Bundling
* An endpoint MUST NOT bundle INIT, INIT ACK or * An endpoint MUST NOT bundle INIT, INIT ACK or
...@@ -319,6 +391,49 @@ sctp_disposition_t sctp_sf_do_5_1C_ack(const sctp_endpoint_t *ep, ...@@ -319,6 +391,49 @@ sctp_disposition_t sctp_sf_do_5_1C_ack(const sctp_endpoint_t *ep,
return SCTP_DISPOSITION_DELETE_TCB; return SCTP_DISPOSITION_DELETE_TCB;
} }
/* Verify the INIT chunk before processing it. */
err_chunk = NULL;
if (!sctp_verify_init(asoc, chunk->chunk_hdr->type,
(sctp_init_chunk_t *)chunk->chunk_hdr, chunk,
&err_chunk)) {
/* This chunk contains fatal error. It is to be discarded.
* Send an ABORT, with causes if there is any.
*/
if (err_chunk) {
packet = sctp_abort_pkt_new(ep, asoc, arg,
(__u8 *)(err_chunk->chunk_hdr) +
sizeof(sctp_chunkhdr_t),
ntohs(err_chunk->chunk_hdr->length) -
sizeof(sctp_chunkhdr_t));
sctp_free_chunk(err_chunk);
if (packet) {
sctp_add_cmd_sf(commands, SCTP_CMD_SEND_PKT,
SCTP_PACKET(packet));
sctp_add_cmd_sf(commands, SCTP_CMD_NEW_STATE,
SCTP_STATE(SCTP_STATE_CLOSED));
sctp_add_cmd_sf(commands, SCTP_CMD_DELETE_TCB,
SCTP_NULL());
return SCTP_DISPOSITION_CONSUME;
} else {
sctp_add_cmd_sf(commands, SCTP_CMD_NEW_STATE,
SCTP_STATE(SCTP_STATE_CLOSED));
sctp_add_cmd_sf(commands, SCTP_CMD_DELETE_TCB,
SCTP_NULL());
return SCTP_DISPOSITION_NOMEM;
}
} else {
ret = sctp_sf_tabort_8_4_8(ep, asoc, type, arg,
commands);
sctp_add_cmd_sf(commands, SCTP_CMD_NEW_STATE,
SCTP_STATE(SCTP_STATE_CLOSED));
sctp_add_cmd_sf(commands, SCTP_CMD_DELETE_TCB,
SCTP_NULL());
return ret;
}
}
/* Tag the variable length paramters. Note that we never /* Tag the variable length paramters. Note that we never
* convert the parameters in an INIT chunk. * convert the parameters in an INIT chunk.
*/ */
...@@ -345,7 +460,12 @@ sctp_disposition_t sctp_sf_do_5_1C_ack(const sctp_endpoint_t *ep, ...@@ -345,7 +460,12 @@ sctp_disposition_t sctp_sf_do_5_1C_ack(const sctp_endpoint_t *ep,
/* 5.1 C) "A" shall then send the State Cookie received in the /* 5.1 C) "A" shall then send the State Cookie received in the
* INIT ACK chunk in a COOKIE ECHO chunk, ... * INIT ACK chunk in a COOKIE ECHO chunk, ...
*/ */
sctp_add_cmd_sf(commands, SCTP_CMD_GEN_COOKIE_ECHO, SCTP_NULL()); /* If there is any errors to report, send the ERROR chunk generated
* for unknown parameters as well.
*/
sctp_add_cmd_sf(commands, SCTP_CMD_GEN_COOKIE_ECHO,
SCTP_CHUNK(err_chunk));
return SCTP_DISPOSITION_CONSUME; return SCTP_DISPOSITION_CONSUME;
nomem: nomem:
...@@ -579,7 +699,7 @@ sctp_disposition_t sctp_sf_sendbeat_8_3(const sctp_endpoint_t *ep, ...@@ -579,7 +699,7 @@ sctp_disposition_t sctp_sf_sendbeat_8_3(const sctp_endpoint_t *ep,
* HEARTBEAT is sent (see Section 8.3). * HEARTBEAT is sent (see Section 8.3).
*/ */
hbinfo.param_hdr.type = SCTP_PARAM_HEATBEAT_INFO; hbinfo.param_hdr.type = SCTP_PARAM_HEARTBEAT_INFO;
hbinfo.param_hdr.length = htons(sizeof(sctp_sender_hb_info_t)); hbinfo.param_hdr.length = htons(sizeof(sctp_sender_hb_info_t));
hbinfo.daddr = transport->ipaddr; hbinfo.daddr = transport->ipaddr;
hbinfo.sent_at = jiffies; hbinfo.sent_at = jiffies;
...@@ -852,6 +972,11 @@ static sctp_disposition_t sctp_sf_do_unexpected_init( ...@@ -852,6 +972,11 @@ static sctp_disposition_t sctp_sf_do_unexpected_init(
sctp_chunk_t *chunk = arg; sctp_chunk_t *chunk = arg;
sctp_chunk_t *repl; sctp_chunk_t *repl;
sctp_association_t *new_asoc; sctp_association_t *new_asoc;
sctp_chunk_t *err_chunk;
sctp_packet_t *packet;
sctp_unrecognized_param_t *unk_param;
int len;
/* 6.10 Bundling /* 6.10 Bundling
* An endpoint MUST NOT bundle INIT, INIT ACK or * An endpoint MUST NOT bundle INIT, INIT ACK or
...@@ -866,6 +991,36 @@ static sctp_disposition_t sctp_sf_do_unexpected_init( ...@@ -866,6 +991,36 @@ static sctp_disposition_t sctp_sf_do_unexpected_init(
/* Tag the variable length parameters. */ /* Tag the variable length parameters. */
chunk->param_hdr.v = skb_pull(chunk->skb, sizeof(sctp_inithdr_t)); chunk->param_hdr.v = skb_pull(chunk->skb, sizeof(sctp_inithdr_t));
/* Verify the INIT chunk before processing it. */
err_chunk = NULL;
if (!sctp_verify_init(asoc, chunk->chunk_hdr->type,
(sctp_init_chunk_t *)chunk->chunk_hdr, chunk,
&err_chunk)) {
/* This chunk contains fatal error. It is to be discarded.
* Send an ABORT, with causes if there is any.
*/
if (err_chunk) {
packet = sctp_abort_pkt_new(ep, asoc, arg,
(__u8 *)(err_chunk->chunk_hdr) +
sizeof(sctp_chunkhdr_t),
ntohs(err_chunk->chunk_hdr->length) -
sizeof(sctp_chunkhdr_t));
sctp_free_chunk(err_chunk);
if (packet) {
sctp_add_cmd_sf(commands, SCTP_CMD_SEND_PKT,
SCTP_PACKET(packet));
return SCTP_DISPOSITION_CONSUME;
} else {
return SCTP_DISPOSITION_NOMEM;
}
} else {
return sctp_sf_tabort_8_4_8(ep, asoc, type, arg,
commands);
}
}
/* /*
* Other parameters for the endpoint SHOULD be copied from the * Other parameters for the endpoint SHOULD be copied from the
* existing parameters of the association (e.g. number of * existing parameters of the association (e.g. number of
...@@ -887,10 +1042,41 @@ static sctp_disposition_t sctp_sf_do_unexpected_init( ...@@ -887,10 +1042,41 @@ static sctp_disposition_t sctp_sf_do_unexpected_init(
sctp_tietags_populate(new_asoc, asoc); sctp_tietags_populate(new_asoc, asoc);
/* B) "Z" shall respond immediately with an INIT ACK chunk. */ /* B) "Z" shall respond immediately with an INIT ACK chunk. */
repl = sctp_make_init_ack(new_asoc, chunk, GFP_ATOMIC);
/* If there are errors need to be reported for unknown parameters,
* make sure to reserve enough room in the INIT ACK for them.
*/
len = 0;
if (err_chunk) {
len = ntohs(err_chunk->chunk_hdr->length) -
sizeof(sctp_chunkhdr_t);
}
repl = sctp_make_init_ack(new_asoc, chunk, GFP_ATOMIC, len);
if (!repl) if (!repl)
goto nomem; goto nomem;
/* If there are errors need to be reported for unknown parameters,
* include them in the outgoing INIT ACK as "Unrecognized parameter"
* parameter.
*/
if (err_chunk) {
/* Get the "Unrecognized parameter" parameter(s) out of the
* ERROR chunk generated by sctp_verify_init(). Since the
* error cause code for "unknown parameter" and the
* "Unrecognized parameter" type is the same, we can
* construct the parameters in INIT ACK by copying the
* ERROR causes over.
*/
unk_param = (sctp_unrecognized_param_t *)
((__u8 *)(err_chunk->chunk_hdr) +
sizeof(sctp_chunkhdr_t));
/* Replace the cause code with the "Unrecognized parameter"
* parameter type.
*/
sctp_addto_chunk(repl, len, unk_param);
sctp_free_chunk(err_chunk);
}
sctp_add_cmd_sf(commands, SCTP_CMD_NEW_ASOC, SCTP_ASOC(new_asoc)); sctp_add_cmd_sf(commands, SCTP_CMD_NEW_ASOC, SCTP_ASOC(new_asoc));
sctp_add_cmd_sf(commands, SCTP_CMD_REPLY, SCTP_CHUNK(repl)); sctp_add_cmd_sf(commands, SCTP_CMD_REPLY, SCTP_CHUNK(repl));
...@@ -903,6 +1089,9 @@ static sctp_disposition_t sctp_sf_do_unexpected_init( ...@@ -903,6 +1089,9 @@ static sctp_disposition_t sctp_sf_do_unexpected_init(
return SCTP_DISPOSITION_CONSUME; return SCTP_DISPOSITION_CONSUME;
nomem: nomem:
if (err_chunk)
sctp_free_chunk(err_chunk);
return SCTP_DISPOSITION_NOMEM; return SCTP_DISPOSITION_NOMEM;
} }
...@@ -1948,17 +2137,16 @@ sctp_disposition_t sctp_sf_eat_data_6_2(const sctp_endpoint_t *ep, ...@@ -1948,17 +2137,16 @@ sctp_disposition_t sctp_sf_eat_data_6_2(const sctp_endpoint_t *ep,
/* This is a new TSN. */ /* This is a new TSN. */
/* If we don't have any room in our receive window, discard. /* Discard if there is no room in the receive window.
* Actually, allow a little bit of overflow (up to a MTU of * Actually, allow a little bit of overflow (up to a MTU).
* of overflow).
*/ */
datalen = ntohs(chunk->chunk_hdr->length); datalen = ntohs(chunk->chunk_hdr->length);
datalen -= sizeof(sctp_data_chunk_t); datalen -= sizeof(sctp_data_chunk_t);
if (!asoc->rwnd || (datalen > asoc->frag_point)) { if (asoc->rwnd_over || (datalen > asoc->rwnd + asoc->frag_point)) {
SCTP_DEBUG_PRINTK("Discarding tsn: %u datalen: %Zd, " SCTP_DEBUG_PRINTK("Discarding tsn: %u datalen: %Zd, "
"rwnd: %d\n", tsn, datalen, asoc->rwnd); "rwnd: %d\n", tsn, datalen, asoc->rwnd);
goto discard_noforce; goto discard_force;
} }
/* /*
...@@ -2330,59 +2518,32 @@ sctp_disposition_t sctp_sf_tabort_8_4_8(const sctp_endpoint_t *ep, ...@@ -2330,59 +2518,32 @@ sctp_disposition_t sctp_sf_tabort_8_4_8(const sctp_endpoint_t *ep,
sctp_cmd_seq_t *commands) sctp_cmd_seq_t *commands)
{ {
sctp_packet_t *packet = NULL; sctp_packet_t *packet = NULL;
sctp_transport_t *transport = NULL;
sctp_chunk_t *chunk = arg; sctp_chunk_t *chunk = arg;
sctp_chunk_t *abort; sctp_chunk_t *abort;
__u16 sport;
__u16 dport;
__u32 vtag;
/* Grub in chunk and endpoint for kewl bitz. */
sport = ntohs(chunk->sctp_hdr->dest);
dport = ntohs(chunk->sctp_hdr->source);
/* -- Make sure the ABORT packet's V-tag is the same as the
* inbound packet if no association exists, otherwise use
* the peer's vtag.
*/
if (asoc)
vtag = asoc->peer.i.init_tag;
else
vtag = ntohl(chunk->sctp_hdr->vtag);
/* Make a transport for the bucket, Eliza... */ packet = sctp_ootb_pkt_new(asoc, chunk);
transport = sctp_transport_new(sctp_source(chunk), GFP_ATOMIC);
if (!transport)
goto nomem;
/* Make a packet for the ABORT to go into. */ if (packet) {
packet = t_new(sctp_packet_t, GFP_ATOMIC); /* Make an ABORT. The T bit will be set if the asoc
if (!packet) * is NULL.
goto nomem_packet; */
abort = sctp_make_abort(asoc, chunk, 0);
packet = sctp_packet_init(packet, transport, sport, dport); if (!abort) {
packet = sctp_packet_config(packet, vtag, 0, NULL); sctp_ootb_pkt_free(packet);
return SCTP_DISPOSITION_NOMEM;
}
/* Make an ABORT. /* Set the skb to the belonging sock for accounting. */
* This will set the T bit since we have no association. abort->skb->sk = ep->base.sk;
*/
abort = sctp_make_abort(NULL, chunk, 0);
if (!abort)
goto nomem_chunk;
/* Set the skb to the belonging sock for accounting. */ sctp_packet_append_chunk(packet, abort);
abort->skb->sk = ep->base.sk;
sctp_packet_append_chunk(packet, abort); sctp_add_cmd_sf(commands, SCTP_CMD_SEND_PKT,
sctp_add_cmd_sf(commands, SCTP_CMD_SEND_PKT, SCTP_PACKET(packet)); SCTP_PACKET(packet));
return SCTP_DISPOSITION_DISCARD;
nomem_chunk: return SCTP_DISPOSITION_CONSUME;
sctp_packet_free(packet); }
nomem_packet:
sctp_transport_free(transport);
nomem:
return SCTP_DISPOSITION_NOMEM; return SCTP_DISPOSITION_NOMEM;
} }
...@@ -2560,60 +2721,35 @@ sctp_disposition_t sctp_sf_shut_8_4_5(const sctp_endpoint_t *ep, ...@@ -2560,60 +2721,35 @@ sctp_disposition_t sctp_sf_shut_8_4_5(const sctp_endpoint_t *ep,
sctp_cmd_seq_t *commands) sctp_cmd_seq_t *commands)
{ {
sctp_packet_t *packet = NULL; sctp_packet_t *packet = NULL;
sctp_transport_t *transport = NULL;
sctp_chunk_t *chunk = arg; sctp_chunk_t *chunk = arg;
sctp_chunk_t *shut; sctp_chunk_t *shut;
__u16 sport;
__u16 dport;
__u32 vtag;
/* Grub in chunk and endpoint for kewl bitz. */ packet = sctp_ootb_pkt_new(asoc, chunk);
sport = ntohs(chunk->sctp_hdr->dest);
dport = ntohs(chunk->sctp_hdr->source);
/* Make sure the ABORT packet's V-tag is the same as the
* inbound packet if no association exists, otherwise use
* the peer's vtag.
*/
vtag = ntohl(chunk->sctp_hdr->vtag);
/* Make a transport for the bucket, Eliza... */
transport = sctp_transport_new(sctp_source(chunk), GFP_ATOMIC);
if (!transport)
goto nomem;
/* Make a packet for the ABORT to go into. */
packet = t_new(sctp_packet_t, GFP_ATOMIC);
if (!packet)
goto nomem_packet;
packet = sctp_packet_init(packet, transport, sport, dport);
packet = sctp_packet_config(packet, vtag, 0, NULL);
/* Make an ABORT.
* This will set the T bit since we have no association.
*/
shut = sctp_make_shutdown_complete(NULL, chunk);
if (!shut)
goto nomem_chunk;
/* Set the skb to the belonging sock for accounting. */ if (packet) {
shut->skb->sk = ep->base.sk; /* Make an SHUTDOWN_COMPLETE.
* The T bit will be set if the asoc is NULL.
*/
shut = sctp_make_shutdown_complete(asoc, chunk);
if (!shut) {
sctp_ootb_pkt_free(packet);
return SCTP_DISPOSITION_NOMEM;
}
sctp_packet_append_chunk(packet, shut); /* Set the skb to the belonging sock for accounting. */
sctp_add_cmd_sf(commands, SCTP_CMD_SEND_PKT, SCTP_PACKET(packet)); shut->skb->sk = ep->base.sk;
return SCTP_DISPOSITION_CONSUME; sctp_packet_append_chunk(packet, shut);
nomem_chunk: sctp_add_cmd_sf(commands, SCTP_CMD_SEND_PKT,
sctp_packet_free(packet); SCTP_PACKET(packet));
nomem_packet: return SCTP_DISPOSITION_CONSUME;
sctp_transport_free(transport); }
nomem:
return SCTP_DISPOSITION_NOMEM; return SCTP_DISPOSITION_NOMEM;
} }
/* /*
* Process an unknown chunk. * Process an unknown chunk.
* *
...@@ -3949,3 +4085,93 @@ sctp_sackhdr_t *sctp_sm_pull_sack(sctp_chunk_t *chunk) ...@@ -3949,3 +4085,93 @@ sctp_sackhdr_t *sctp_sm_pull_sack(sctp_chunk_t *chunk)
skb_pull(chunk->skb, (num_blocks + num_dup_tsns) * sizeof(__u32)); skb_pull(chunk->skb, (num_blocks + num_dup_tsns) * sizeof(__u32));
return sack; return sack;
} }
/* Create an ABORT packet to be sent as a response, with the specified
* error causes.
*/
sctp_packet_t *sctp_abort_pkt_new(const sctp_endpoint_t *ep,
const sctp_association_t *asoc,
sctp_chunk_t *chunk,
const void *payload,
size_t paylen)
{
sctp_packet_t *packet;
sctp_chunk_t *abort;
packet = sctp_ootb_pkt_new(asoc, chunk);
if (packet) {
/* Make an ABORT.
* The T bit will be set if the asoc is NULL.
*/
abort = sctp_make_abort(asoc, chunk, paylen);
if (!abort) {
sctp_ootb_pkt_free(packet);
return NULL;
}
/* Add specified error causes, i.e., payload, to the
* end of the chunk.
*/
sctp_addto_chunk(abort, paylen, payload);
/* Set the skb to the belonging sock for accounting. */
abort->skb->sk = ep->base.sk;
sctp_packet_append_chunk(packet, abort);
}
return packet;
}
/* Allocate a packet for responding in the OOTB conditions. */
sctp_packet_t *sctp_ootb_pkt_new(const sctp_association_t *asoc,
const sctp_chunk_t *chunk)
{
sctp_packet_t *packet;
sctp_transport_t *transport;
__u16 sport;
__u16 dport;
__u32 vtag;
/* Get the source and destination port from the inbound packet. */
sport = ntohs(chunk->sctp_hdr->dest);
dport = ntohs(chunk->sctp_hdr->source);
/* The V-tag is going to be the same as the inbound packet if no
* association exists, otherwise, use the peer's vtag.
*/
if (asoc) {
vtag = asoc->peer.i.init_tag;
} else {
vtag = ntohl(chunk->sctp_hdr->vtag);
}
/* Make a transport for the bucket, Eliza... */
transport = sctp_transport_new(sctp_source(chunk), GFP_ATOMIC);
if (!transport)
goto nomem;
/* Allocate a new packet for sending the response. */
packet = t_new(sctp_packet_t, GFP_ATOMIC);
if (!packet)
goto nomem_packet;
packet = sctp_packet_init(packet, transport, sport, dport);
packet = sctp_packet_config(packet, vtag, 0, NULL);
return packet;
nomem_packet:
sctp_transport_free(transport);
nomem:
return NULL;
}
/* Free the packet allocated earlier for responding in the OOTB condition. */
void sctp_ootb_pkt_free(sctp_packet_t *packet)
{
sctp_transport_free(packet->transport);
sctp_packet_free(packet);
}
...@@ -4,41 +4,41 @@ ...@@ -4,41 +4,41 @@
* Copyright (c) 2001-2002 Intel Corp. * Copyright (c) 2001-2002 Intel Corp.
* Copyright (c) 2001-2002 Nokia, Inc. * Copyright (c) 2001-2002 Nokia, Inc.
* Copyright (c) 2001 La Monte H.P. Yarroll * Copyright (c) 2001 La Monte H.P. Yarroll
* *
* This file is part of the SCTP kernel reference Implementation * This file is part of the SCTP kernel reference Implementation
* *
* These functions interface with the sockets layer to implement the * These functions interface with the sockets layer to implement the
* SCTP Extensions for the Sockets API. * SCTP Extensions for the Sockets API.
* *
* Note that the descriptions from the specification are USER level * Note that the descriptions from the specification are USER level
* functions--this file is the functions which populate the struct proto * functions--this file is the functions which populate the struct proto
* for SCTP which is the BOTTOM of the sockets interface. * for SCTP which is the BOTTOM of the sockets interface.
* *
* The SCTP reference implementation is free software; * The SCTP reference implementation is free software;
* you can redistribute it and/or modify it under the terms of * you can redistribute it and/or modify it under the terms of
* the GNU General Public License as published by * the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option) * the Free Software Foundation; either version 2, or (at your option)
* any later version. * any later version.
* *
* The SCTP reference implementation is distributed in the hope that it * The SCTP reference implementation is distributed in the hope that it
* will be useful, but WITHOUT ANY WARRANTY; without even the implied * will be useful, but WITHOUT ANY WARRANTY; without even the implied
* ************************ * ************************
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details. * See the GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with GNU CC; see the file COPYING. If not, write to * along with GNU CC; see the file COPYING. If not, write to
* the Free Software Foundation, 59 Temple Place - Suite 330, * the Free Software Foundation, 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA. * Boston, MA 02111-1307, USA.
* *
* Please send any bug reports or fixes you make to the * Please send any bug reports or fixes you make to the
* email address(es): * email address(es):
* lksctp developers <lksctp-developers@lists.sourceforge.net> * lksctp developers <lksctp-developers@lists.sourceforge.net>
* *
* Or submit a bug report through the following website: * Or submit a bug report through the following website:
* http://www.sf.net/projects/lksctp * http://www.sf.net/projects/lksctp
* *
* Written or modified by: * Written or modified by:
* La Monte H.P. Yarroll <piggy@acm.org> * La Monte H.P. Yarroll <piggy@acm.org>
* Narasimha Budihal <narsi@refcode.org> * Narasimha Budihal <narsi@refcode.org>
* Karl Knutson <karl@athena.chicago.il.us> * Karl Knutson <karl@athena.chicago.il.us>
...@@ -47,7 +47,7 @@ ...@@ -47,7 +47,7 @@
* Daisy Chang <daisyc@us.ibm.com> * Daisy Chang <daisyc@us.ibm.com>
* Sridhar Samudrala <samudrala@us.ibm.com> * Sridhar Samudrala <samudrala@us.ibm.com>
* Inaky Perez-Gonzalez <inaky.gonzalez@intel.com> * Inaky Perez-Gonzalez <inaky.gonzalez@intel.com>
* *
* Any bugs reported given to us we will try to fix... any fixes shared will * Any bugs reported given to us we will try to fix... any fixes shared will
* be incorporated into the next SCTP release. * be incorporated into the next SCTP release.
*/ */
...@@ -67,14 +67,18 @@ ...@@ -67,14 +67,18 @@
#include <net/icmp.h> #include <net/icmp.h>
#include <net/route.h> #include <net/route.h>
#include <net/ipv6.h> #include <net/ipv6.h>
#include <net/inet_common.h> #include <net/inet_common.h>
#include <linux/socket.h> /* for sa_family_t */ #include <linux/socket.h> /* for sa_family_t */
#include <net/sock.h> #include <net/sock.h>
#include <net/sctp/sctp.h> #include <net/sctp/sctp.h>
/* WARNING: Please do not remove the SCTP_STATIC attribute to
* any of the functions below as they are used to export functions
* used by a project regression testsuite.
*/
/* Forward declarations for internal helper functions. */ /* Forward declarations for internal helper functions. */
static void __sctp_write_space(sctp_association_t *asoc);
static int sctp_writeable(struct sock *sk); static int sctp_writeable(struct sock *sk);
static inline int sctp_wspace(sctp_association_t *asoc); static inline int sctp_wspace(sctp_association_t *asoc);
static inline void sctp_set_owner_w(sctp_chunk_t *chunk); static inline void sctp_set_owner_w(sctp_chunk_t *chunk);
...@@ -92,8 +96,7 @@ static int sctp_bindx_add(struct sock *, struct sockaddr_storage *, int); ...@@ -92,8 +96,7 @@ static int sctp_bindx_add(struct sock *, struct sockaddr_storage *, int);
static int sctp_bindx_rem(struct sock *, struct sockaddr_storage *, int); static int sctp_bindx_rem(struct sock *, struct sockaddr_storage *, int);
static int sctp_do_bind(struct sock *, sockaddr_storage_t *, int); static int sctp_do_bind(struct sock *, sockaddr_storage_t *, int);
static int sctp_autobind(struct sock *sk); static int sctp_autobind(struct sock *sk);
static sctp_bind_bucket_t *sctp_bucket_create(sctp_bind_hashbucket_t *head,
unsigned short snum);
/* API 3.1.2 bind() - UDP Style Syntax /* API 3.1.2 bind() - UDP Style Syntax
* The syntax of bind() is, * The syntax of bind() is,
...@@ -132,7 +135,8 @@ int sctp_bind(struct sock *sk, struct sockaddr *uaddr, int addr_len) ...@@ -132,7 +135,8 @@ int sctp_bind(struct sock *sk, struct sockaddr *uaddr, int addr_len)
static long sctp_get_port_local(struct sock *, unsigned short); static long sctp_get_port_local(struct sock *, unsigned short);
/* Bind a local address either to an endpoint or to an association. */ /* Bind a local address either to an endpoint or to an association. */
static int sctp_do_bind(struct sock *sk, sockaddr_storage_t *newaddr, int addr_len) SCTP_STATIC int sctp_do_bind(struct sock *sk, sockaddr_storage_t *newaddr,
int addr_len)
{ {
sctp_opt_t *sp = sctp_sk(sk); sctp_opt_t *sp = sctp_sk(sk);
sctp_endpoint_t *ep = sp->ep; sctp_endpoint_t *ep = sp->ep;
...@@ -151,7 +155,7 @@ static int sctp_do_bind(struct sock *sk, sockaddr_storage_t *newaddr, int addr_l ...@@ -151,7 +155,7 @@ static int sctp_do_bind(struct sock *sk, sockaddr_storage_t *newaddr, int addr_l
if (PF_INET == sk->family) { if (PF_INET == sk->family) {
if (sa_family != AF_INET) if (sa_family != AF_INET)
return -EINVAL; return -EINVAL;
} }
/* Make a local copy of the new address. */ /* Make a local copy of the new address. */
tmpaddr = *newaddr; tmpaddr = *newaddr;
...@@ -317,7 +321,7 @@ static int sctp_do_bind(struct sock *sk, sockaddr_storage_t *newaddr, int addr_l ...@@ -317,7 +321,7 @@ static int sctp_do_bind(struct sock *sk, sockaddr_storage_t *newaddr, int addr_l
* sctp_bindx() for a lock-protected call. * sctp_bindx() for a lock-protected call.
*/ */
static int __sctp_bindx(struct sock *sk, struct sockaddr_storage *addrs, static int __sctp_bindx(struct sock *sk, struct sockaddr_storage *addrs,
int addrcnt, int flags) int addrcnt, int flags)
{ {
int retval = 0; int retval = 0;
...@@ -603,9 +607,9 @@ int sctp_bindx_rem(struct sock *sk, struct sockaddr_storage *addrs, int addrcnt) ...@@ -603,9 +607,9 @@ int sctp_bindx_rem(struct sock *sk, struct sockaddr_storage *addrs, int addrcnt)
* *
* Returns 0 if ok, <0 errno code on error. * Returns 0 if ok, <0 errno code on error.
*/ */
static int sctp_setsockopt_bindx(struct sock* sk, SCTP_STATIC int sctp_setsockopt_bindx(struct sock* sk,
struct sockaddr_storage *addrs, struct sockaddr_storage *addrs,
int addrssize, int op) int addrssize, int op)
{ {
struct sockaddr_storage *kaddrs; struct sockaddr_storage *kaddrs;
int err; int err;
...@@ -659,7 +663,7 @@ static int sctp_setsockopt_bindx(struct sock* sk, ...@@ -659,7 +663,7 @@ static int sctp_setsockopt_bindx(struct sock* sk,
* If sd in the close() call is a branched-off socket representing only * If sd in the close() call is a branched-off socket representing only
* one association, the shutdown is performed on that association only. * one association, the shutdown is performed on that association only.
*/ */
static void sctp_close(struct sock *sk, long timeout) SCTP_STATIC void sctp_close(struct sock *sk, long timeout)
{ {
sctp_endpoint_t *ep; sctp_endpoint_t *ep;
sctp_association_t *asoc; sctp_association_t *asoc;
...@@ -727,9 +731,9 @@ static void sctp_close(struct sock *sk, long timeout) ...@@ -727,9 +731,9 @@ static void sctp_close(struct sock *sk, long timeout)
/* BUG: We do not implement timeouts. */ /* BUG: We do not implement timeouts. */
/* BUG: We do not implement the equivalent of wait_for_tcp_memory(). */ /* BUG: We do not implement the equivalent of wait_for_tcp_memory(). */
static int sctp_msghdr_parse(const struct msghdr *, sctp_cmsgs_t *); SCTP_STATIC int sctp_msghdr_parse(const struct msghdr *, sctp_cmsgs_t *);
static int sctp_sendmsg(struct sock *sk, struct msghdr *msg, int size) SCTP_STATIC int sctp_sendmsg(struct sock *sk, struct msghdr *msg, int size)
{ {
sctp_opt_t *sp; sctp_opt_t *sp;
sctp_endpoint_t *ep; sctp_endpoint_t *ep;
...@@ -931,7 +935,7 @@ static int sctp_sendmsg(struct sock *sk, struct msghdr *msg, int size) ...@@ -931,7 +935,7 @@ static int sctp_sendmsg(struct sock *sk, struct msghdr *msg, int size)
= sinit->sinit_max_attempts; = sinit->sinit_max_attempts;
} }
if (sinit->sinit_max_init_timeo) { if (sinit->sinit_max_init_timeo) {
asoc->max_init_timeo asoc->max_init_timeo
= sinit->sinit_max_init_timeo * HZ; = sinit->sinit_max_init_timeo * HZ;
} }
} }
...@@ -1094,8 +1098,8 @@ static int sctp_sendmsg(struct sock *sk, struct msghdr *msg, int size) ...@@ -1094,8 +1098,8 @@ static int sctp_sendmsg(struct sock *sk, struct msghdr *msg, int size)
*/ */
static struct sk_buff *sctp_skb_recv_datagram(struct sock *, int, int, int *); static struct sk_buff *sctp_skb_recv_datagram(struct sock *, int, int, int *);
static int sctp_recvmsg(struct sock *sk, struct msghdr *msg, int len, SCTP_STATIC int sctp_recvmsg(struct sock *sk, struct msghdr *msg, int len,
int noblock, int flags, int *addr_len) int noblock, int flags, int *addr_len)
{ {
sctp_ulpevent_t *event = NULL; sctp_ulpevent_t *event = NULL;
sctp_opt_t *sp = sctp_sk(sk); sctp_opt_t *sp = sctp_sk(sk);
...@@ -1170,7 +1174,7 @@ static int sctp_recvmsg(struct sock *sk, struct msghdr *msg, int len, ...@@ -1170,7 +1174,7 @@ static int sctp_recvmsg(struct sock *sk, struct msghdr *msg, int len,
return err; return err;
} }
static inline int sctp_setsockopt_disable_fragments(struct sock *sk, static inline int sctp_setsockopt_disable_fragments(struct sock *sk,
char *optval, int optlen) char *optval, int optlen)
{ {
int val; int val;
...@@ -1186,7 +1190,7 @@ static inline int sctp_setsockopt_disable_fragments(struct sock *sk, ...@@ -1186,7 +1190,7 @@ static inline int sctp_setsockopt_disable_fragments(struct sock *sk,
return 0; return 0;
} }
static inline int sctp_setsockopt_set_events(struct sock *sk, char *optval, static inline int sctp_setsockopt_set_events(struct sock *sk, char *optval,
int optlen) int optlen)
{ {
if (optlen != sizeof(struct sctp_event_subscribe)) if (optlen != sizeof(struct sctp_event_subscribe))
...@@ -1196,7 +1200,7 @@ static inline int sctp_setsockopt_set_events(struct sock *sk, char *optval, ...@@ -1196,7 +1200,7 @@ static inline int sctp_setsockopt_set_events(struct sock *sk, char *optval,
return 0; return 0;
} }
static inline int sctp_setsockopt_autoclose(struct sock *sk, char *optval, static inline int sctp_setsockopt_autoclose(struct sock *sk, char *optval,
int optlen) int optlen)
{ {
sctp_opt_t *sp = sctp_sk(sk); sctp_opt_t *sp = sctp_sk(sk);
...@@ -1229,7 +1233,7 @@ static inline int sctp_setsockopt_autoclose(struct sock *sk, char *optval, ...@@ -1229,7 +1233,7 @@ static inline int sctp_setsockopt_autoclose(struct sock *sk, char *optval,
* optval - the buffer to store the value of the option. * optval - the buffer to store the value of the option.
* optlen - the size of the buffer. * optlen - the size of the buffer.
*/ */
static int sctp_setsockopt(struct sock *sk, int level, int optname, SCTP_STATIC int sctp_setsockopt(struct sock *sk, int level, int optname,
char *optval, int optlen) char *optval, int optlen)
{ {
int retval = 0; int retval = 0;
...@@ -1313,19 +1317,20 @@ static int sctp_setsockopt(struct sock *sk, int level, int optname, ...@@ -1313,19 +1317,20 @@ static int sctp_setsockopt(struct sock *sk, int level, int optname,
} }
/* FIXME: Write comments. */ /* FIXME: Write comments. */
static int sctp_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len) SCTP_STATIC int sctp_connect(struct sock *sk, struct sockaddr *uaddr,
int addr_len)
{ {
return -EOPNOTSUPP; /* STUB */ return -EOPNOTSUPP; /* STUB */
} }
/* FIXME: Write comments. */ /* FIXME: Write comments. */
static int sctp_disconnect(struct sock *sk, int flags) SCTP_STATIC int sctp_disconnect(struct sock *sk, int flags)
{ {
return -EOPNOTSUPP; /* STUB */ return -EOPNOTSUPP; /* STUB */
} }
/* FIXME: Write comments. */ /* FIXME: Write comments. */
static struct sock *sctp_accept(struct sock *sk, int flags, int *err) SCTP_STATIC struct sock *sctp_accept(struct sock *sk, int flags, int *err)
{ {
int error = -EOPNOTSUPP; int error = -EOPNOTSUPP;
...@@ -1334,7 +1339,7 @@ static struct sock *sctp_accept(struct sock *sk, int flags, int *err) ...@@ -1334,7 +1339,7 @@ static struct sock *sctp_accept(struct sock *sk, int flags, int *err)
} }
/* FIXME: Write Comments. */ /* FIXME: Write Comments. */
static int sctp_ioctl(struct sock *sk, int cmd, unsigned long arg) SCTP_STATIC int sctp_ioctl(struct sock *sk, int cmd, unsigned long arg)
{ {
return -EOPNOTSUPP; /* STUB */ return -EOPNOTSUPP; /* STUB */
} }
...@@ -1343,7 +1348,7 @@ static int sctp_ioctl(struct sock *sk, int cmd, unsigned long arg) ...@@ -1343,7 +1348,7 @@ static int sctp_ioctl(struct sock *sk, int cmd, unsigned long arg)
* initialized the SCTP-specific portion of the sock. * initialized the SCTP-specific portion of the sock.
* The sock structure should already be zero-filled memory. * The sock structure should already be zero-filled memory.
*/ */
static int sctp_init_sock(struct sock *sk) SCTP_STATIC int sctp_init_sock(struct sock *sk)
{ {
sctp_endpoint_t *ep; sctp_endpoint_t *ep;
sctp_protocol_t *proto; sctp_protocol_t *proto;
...@@ -1428,7 +1433,7 @@ static int sctp_init_sock(struct sock *sk) ...@@ -1428,7 +1433,7 @@ static int sctp_init_sock(struct sock *sk)
} }
/* Cleanup any SCTP per socket resources. */ /* Cleanup any SCTP per socket resources. */
static int sctp_destroy_sock(struct sock *sk) SCTP_STATIC int sctp_destroy_sock(struct sock *sk)
{ {
sctp_endpoint_t *ep; sctp_endpoint_t *ep;
...@@ -1442,7 +1447,7 @@ static int sctp_destroy_sock(struct sock *sk) ...@@ -1442,7 +1447,7 @@ static int sctp_destroy_sock(struct sock *sk)
} }
/* FIXME: Comments needed. */ /* FIXME: Comments needed. */
static void sctp_shutdown(struct sock *sk, int how) SCTP_STATIC void sctp_shutdown(struct sock *sk, int how)
{ {
/* UDP-style sockets do not support shutdown. */ /* UDP-style sockets do not support shutdown. */
/* STUB */ /* STUB */
...@@ -1563,7 +1568,7 @@ static inline int sctp_getsockopt_autoclose(struct sock *sk, int len, char *optv ...@@ -1563,7 +1568,7 @@ static inline int sctp_getsockopt_autoclose(struct sock *sk, int len, char *optv
} }
/* Helper routine to branch off an association to a new socket. */ /* Helper routine to branch off an association to a new socket. */
static int sctp_do_peeloff(sctp_association_t *assoc, struct socket **newsock) SCTP_STATIC int sctp_do_peeloff(sctp_association_t *assoc, struct socket **newsock)
{ {
struct sock *oldsk = assoc->base.sk; struct sock *oldsk = assoc->base.sk;
struct sock *newsk; struct sock *newsk;
...@@ -1652,8 +1657,8 @@ static inline int sctp_getsockopt_peeloff(struct sock *sk, int len, char *optval ...@@ -1652,8 +1657,8 @@ static inline int sctp_getsockopt_peeloff(struct sock *sk, int len, char *optval
return 0; return 0;
} }
static int sctp_getsockopt(struct sock *sk, int level, int optname, SCTP_STATIC int sctp_getsockopt(struct sock *sk, int level, int optname,
char *optval, int *optlen) char *optval, int *optlen)
{ {
int retval = 0; int retval = 0;
sctp_protocol_t *proto = sctp_get_protocol(); sctp_protocol_t *proto = sctp_get_protocol();
...@@ -1734,6 +1739,8 @@ static void sctp_unhash(struct sock *sk) ...@@ -1734,6 +1739,8 @@ static void sctp_unhash(struct sock *sk)
* link to the socket (struct sock) that uses it, the port number and * link to the socket (struct sock) that uses it, the port number and
* a fastreuse flag (FIXME: NPI ipg). * a fastreuse flag (FIXME: NPI ipg).
*/ */
static sctp_bind_bucket_t *sctp_bucket_create(sctp_bind_hashbucket_t *head,
unsigned short snum);
static long sctp_get_port_local(struct sock *sk, unsigned short snum) static long sctp_get_port_local(struct sock *sk, unsigned short snum)
{ {
sctp_bind_hashbucket_t *head; /* hash list */ sctp_bind_hashbucket_t *head; /* hash list */
...@@ -1927,7 +1934,7 @@ static int sctp_get_port(struct sock *sk, unsigned short snum) ...@@ -1927,7 +1934,7 @@ static int sctp_get_port(struct sock *sk, unsigned short snum)
* An application uses listen() to mark a socket as being able to * An application uses listen() to mark a socket as being able to
* accept new associations. * accept new associations.
*/ */
static int sctp_seqpacket_listen(struct sock *sk, int backlog) SCTP_STATIC int sctp_seqpacket_listen(struct sock *sk, int backlog)
{ {
sctp_opt_t *sp = sctp_sk(sk); sctp_opt_t *sp = sctp_sk(sk);
sctp_endpoint_t *ep = sp->ep; sctp_endpoint_t *ep = sp->ep;
...@@ -2184,7 +2191,8 @@ static int sctp_autobind(struct sock *sk) ...@@ -2184,7 +2191,8 @@ static int sctp_autobind(struct sock *sk)
* msg_control * msg_control
* points here * points here
*/ */
static int sctp_msghdr_parse(const struct msghdr *msg, sctp_cmsgs_t *cmsgs) SCTP_STATIC int sctp_msghdr_parse(const struct msghdr *msg,
sctp_cmsgs_t *cmsgs)
{ {
struct cmsghdr *cmsg; struct cmsghdr *cmsg;
...@@ -2500,6 +2508,31 @@ static inline void sctp_set_owner_w(sctp_chunk_t *chunk) ...@@ -2500,6 +2508,31 @@ static inline void sctp_set_owner_w(sctp_chunk_t *chunk)
sk->wmem_queued += SCTP_DATA_SNDSIZE(chunk); sk->wmem_queued += SCTP_DATA_SNDSIZE(chunk);
} }
/* If sndbuf has changed, wake up per association sndbuf waiters. */
static void __sctp_write_space(sctp_association_t *asoc)
{
struct sock *sk = asoc->base.sk;
struct socket *sock = sk->socket;
if ((sctp_wspace(asoc) > 0) && sock) {
if (waitqueue_active(&asoc->wait))
wake_up_interruptible(&asoc->wait);
if (sctp_writeable(sk)) {
if (sk->sleep && waitqueue_active(sk->sleep))
wake_up_interruptible(sk->sleep);
/* Note that we try to include the Async I/O support
* here by modeling from the current TCP/UDP code.
* We have not tested with it yet.
*/
if (sock->fasync_list &&
!(sk->shutdown & SEND_SHUTDOWN))
sock_wake_async(sock, 2, POLL_OUT);
}
}
}
/* Do accounting for the sndbuf space. /* Do accounting for the sndbuf space.
* Decrement the used sndbuf space of the corresponding association by the * Decrement the used sndbuf space of the corresponding association by the
* data size which was just transmitted(freed). * data size which was just transmitted(freed).
...@@ -2522,7 +2555,8 @@ static void sctp_wfree(struct sk_buff *skb) ...@@ -2522,7 +2555,8 @@ static void sctp_wfree(struct sk_buff *skb)
} }
/* Helper function to wait for space in the sndbuf. */ /* Helper function to wait for space in the sndbuf. */
static int sctp_wait_for_sndbuf(sctp_association_t *asoc, long *timeo_p, int msg_len) static int sctp_wait_for_sndbuf(sctp_association_t *asoc, long *timeo_p,
int msg_len)
{ {
struct sock *sk = asoc->base.sk; struct sock *sk = asoc->base.sk;
int err = 0; int err = 0;
...@@ -2581,31 +2615,6 @@ static int sctp_wait_for_sndbuf(sctp_association_t *asoc, long *timeo_p, int msg ...@@ -2581,31 +2615,6 @@ static int sctp_wait_for_sndbuf(sctp_association_t *asoc, long *timeo_p, int msg
goto out; goto out;
} }
/* If sndbuf has changed, wake up per association sndbuf waiters. */
static void __sctp_write_space(sctp_association_t *asoc)
{
struct sock *sk = asoc->base.sk;
struct socket *sock = sk->socket;
if ((sctp_wspace(asoc) > 0) && sock) {
if (waitqueue_active(&asoc->wait))
wake_up_interruptible(&asoc->wait);
if (sctp_writeable(sk)) {
if (sk->sleep && waitqueue_active(sk->sleep))
wake_up_interruptible(sk->sleep);
/* Note that we try to include the Async I/O support
* here by modeling from the current TCP/UDP code.
* We have not tested with it yet.
*/
if (sock->fasync_list &&
!(sk->shutdown & SEND_SHUTDOWN))
sock_wake_async(sock, 2, POLL_OUT);
}
}
}
/* If socket sndbuf has changed, wake up all per association waiters. */ /* If socket sndbuf has changed, wake up all per association waiters. */
void sctp_write_space(struct sock *sk) void sctp_write_space(struct sock *sk)
{ {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment