From eacf3cfdaf04395c07830b046037f46ae94b06ab Mon Sep 17 00:00:00 2001 From: Marco Varlese Date: Mon, 26 Feb 2018 14:52:25 +0100 Subject: SCTP: handle INIT corner-case handling As per RFC4960 the INIT chunk could be received in unexpected scenarios and - depending on the state of the internal state-machine - the INIT chunk requires different treatment. This patch addresses section 5.2.1 and 5.2.2 of the RFC4960. Change-Id: Ib23ef490c6a5ca3da6c46a9584b75e7577cb7042 Signed-off-by: Marco Varlese --- src/vnet/sctp/sctp.c | 4 + src/vnet/sctp/sctp.h | 13 ++++ src/vnet/sctp/sctp_input.c | 22 ++++-- src/vnet/sctp/sctp_output.c | 181 ++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 215 insertions(+), 5 deletions(-) (limited to 'src/vnet') diff --git a/src/vnet/sctp/sctp.c b/src/vnet/sctp/sctp.c index 9a0f47b599f..046eb18dcc2 100644 --- a/src/vnet/sctp/sctp.c +++ b/src/vnet/sctp/sctp.c @@ -291,6 +291,8 @@ sctp_sub_connection_add_ip4 (u8 thread_index, clib_memcpy (&sctp_conn-> sub_conn[sctp_conn->next_avail_sub_conn].connection.lcl_ip.ip4, &ipv4_addr->address, sizeof (ipv4_addr->address)); + + sctp_conn->forming_association_changed = 1; } void @@ -302,6 +304,8 @@ sctp_sub_connection_add_ip6 (u8 thread_index, clib_memcpy (&sctp_conn-> sub_conn[sctp_conn->next_avail_sub_conn].connection.lcl_ip.ip6, &ipv6_addr->address, sizeof (ipv6_addr->address)); + + sctp_conn->forming_association_changed = 1; } sctp_connection_t * diff --git a/src/vnet/sctp/sctp.h b/src/vnet/sctp/sctp.h index 048d153ac55..de5eb8f6685 100644 --- a/src/vnet/sctp/sctp.h +++ b/src/vnet/sctp/sctp.h @@ -237,6 +237,9 @@ typedef struct _sctp_connection u8 next_avail_sub_conn; /**< Represent the index of the next free slot in sub_conn */ + u8 forming_association_changed; /**< This is a flag indicating whether the original association has been modified during + the life-span of the association itself. For instance, a new sub-connection might have been added. */ + } sctp_connection_t; typedef void (sctp_timer_expiration_handler) (u32 conn_index, u32 timer_id); @@ -280,6 +283,16 @@ void sctp_init_mss (sctp_connection_t * sctp_conn); void sctp_prepare_initack_chunk (sctp_connection_t * sctp_conn, u8 idx, vlib_buffer_t * b, ip4_address_t * ip4_addr, ip6_address_t * ip6_addr); +void +sctp_prepare_initack_chunk_for_collision (sctp_connection_t * sctp_conn, + u8 idx, vlib_buffer_t * b, + ip4_address_t * ip4_addr, + ip6_address_t * ip6_addr); +void sctp_prepare_abort_for_collision (sctp_connection_t * sctp_conn, u8 idx, + vlib_buffer_t * b, + ip4_address_t * ip4_addr, + ip6_address_t * ip6_addr); + void sctp_prepare_cookie_echo_chunk (sctp_connection_t * sctp_conn, u8 idx, vlib_buffer_t * b, sctp_state_cookie_param_t * sc); diff --git a/src/vnet/sctp/sctp_input.c b/src/vnet/sctp/sctp_input.c index 615cdefd134..82adc2aa5b0 100644 --- a/src/vnet/sctp/sctp_input.c +++ b/src/vnet/sctp/sctp_input.c @@ -304,12 +304,24 @@ sctp_handle_init (sctp_header_t * sctp_hdr, { /* UNEXPECTED scenario */ switch (sctp_conn->state) { - case SCTP_STATE_COOKIE_WAIT: /* TODO */ + case SCTP_STATE_COOKIE_WAIT: SCTP_ADV_DBG ("Received INIT chunk while in COOKIE_WAIT state"); - break; - case SCTP_STATE_COOKIE_ECHOED: /* TODO */ + sctp_prepare_initack_chunk_for_collision (sctp_conn, + MAIN_SCTP_SUB_CONN_IDX, + b0, ip4_addr, ip6_addr); + return SCTP_ERROR_NONE; + case SCTP_STATE_COOKIE_ECHOED: + case SCTP_STATE_SHUTDOWN_ACK_SENT: SCTP_ADV_DBG ("Received INIT chunk while in COOKIE_ECHOED state"); - break; + if (sctp_conn->forming_association_changed == 0) + sctp_prepare_initack_chunk_for_collision (sctp_conn, + MAIN_SCTP_SUB_CONN_IDX, + b0, ip4_addr, ip6_addr); + else + sctp_prepare_abort_for_collision (sctp_conn, + MAIN_SCTP_SUB_CONN_IDX, b0, + ip4_addr, ip6_addr); + return SCTP_ERROR_NONE; } } @@ -2353,7 +2365,7 @@ do { \ _(SHUTDOWN_RECEIVED, SHUTDOWN_COMPLETE, SCTP_INPUT_NEXT_DROP, SCTP_ERROR_SHUTDOWN_COMPLETE_VIOLATION); /* UNEXPECTED SHUTDOWN_COMPLETE chunk */ _(SHUTDOWN_ACK_SENT, DATA, SCTP_INPUT_NEXT_DROP, SCTP_ERROR_DATA_CHUNK_VIOLATION); /* UNEXPECTED DATA chunk */ - _(SHUTDOWN_ACK_SENT, INIT, SCTP_INPUT_NEXT_DROP, SCTP_ERROR_INIT_CHUNK_VIOLATION); /* UNEXPECTED INIT chunk */ + _(SHUTDOWN_ACK_SENT, INIT, SCTP_INPUT_NEXT_RCV_PHASE, SCTP_ERROR_NONE); /* UNEXPECTED INIT chunk */ _(SHUTDOWN_ACK_SENT, INIT_ACK, SCTP_INPUT_NEXT_DROP, SCTP_ERROR_ACK_DUP); /* UNEXPECTED INIT_ACK chunk */ _(SHUTDOWN_ACK_SENT, SACK, SCTP_INPUT_NEXT_DROP, SCTP_ERROR_SACK_CHUNK_VIOLATION); /* UNEXPECTED INIT chunk */ _(SHUTDOWN_ACK_SENT, HEARTBEAT, SCTP_INPUT_NEXT_DROP, SCTP_ERROR_HEARTBEAT_CHUNK_VIOLATION); /* UNEXPECTED HEARTBEAT chunk */ diff --git a/src/vnet/sctp/sctp_output.c b/src/vnet/sctp/sctp_output.c index a9b2417e0c9..e44451c034c 100644 --- a/src/vnet/sctp/sctp_output.c +++ b/src/vnet/sctp/sctp_output.c @@ -588,6 +588,187 @@ sctp_prepare_cookie_echo_chunk (sctp_connection_t * sctp_conn, u8 idx, vnet_buffer (b)->sctp.subconn_idx = idx; } +/** + * Convert buffer to ABORT + */ +void +sctp_prepare_abort_for_collision (sctp_connection_t * sctp_conn, u8 idx, + vlib_buffer_t * b, ip4_address_t * ip4_addr, + ip6_address_t * ip6_addr) +{ + vlib_main_t *vm = vlib_get_main (); + + sctp_reuse_buffer (vm, b); + + /* The minimum size of the message is given by the sctp_abort_chunk_t */ + u16 alloc_bytes = sizeof (sctp_abort_chunk_t); + + /* As per RFC 4960 the chunk_length value does NOT contemplate + * the size of the first header (see sctp_header_t) and any padding + */ + u16 chunk_len = alloc_bytes - sizeof (sctp_header_t); + + alloc_bytes += vnet_sctp_calculate_padding (alloc_bytes); + + sctp_abort_chunk_t *abort_chunk = vlib_buffer_push_uninit (b, alloc_bytes); + + /* src_port & dst_port are already in network byte-order */ + abort_chunk->sctp_hdr.checksum = 0; + abort_chunk->sctp_hdr.src_port = + sctp_conn->sub_conn[idx].connection.lcl_port; + abort_chunk->sctp_hdr.dst_port = + sctp_conn->sub_conn[idx].connection.rmt_port; + /* As per RFC4960 Section 5.2.2: copy the INITIATE_TAG into the VERIFICATION_TAG of the ABORT chunk */ + abort_chunk->sctp_hdr.verification_tag = sctp_conn->local_tag; + + vnet_sctp_set_chunk_type (&abort_chunk->chunk_hdr, ABORT); + vnet_sctp_set_chunk_length (&abort_chunk->chunk_hdr, chunk_len); + + vnet_buffer (b)->sctp.connection_index = + sctp_conn->sub_conn[idx].connection.c_index; + vnet_buffer (b)->sctp.subconn_idx = idx; +} + +/** + * Convert buffer to INIT-ACK + */ +void +sctp_prepare_initack_chunk_for_collision (sctp_connection_t * sctp_conn, + u8 idx, vlib_buffer_t * b, + ip4_address_t * ip4_addr, + ip6_address_t * ip6_addr) +{ + vlib_main_t *vm = vlib_get_main (); + sctp_ipv4_addr_param_t *ip4_param = 0; + sctp_ipv6_addr_param_t *ip6_param = 0; + + sctp_reuse_buffer (vm, b); + + /* The minimum size of the message is given by the sctp_init_ack_chunk_t */ + u16 alloc_bytes = + sizeof (sctp_init_ack_chunk_t) + sizeof (sctp_state_cookie_param_t); + + if (PREDICT_TRUE (ip4_addr != NULL)) + { + /* Create room for variable-length fields in the INIT_ACK chunk */ + alloc_bytes += SCTP_IPV4_ADDRESS_TYPE_LENGTH; + } + if (PREDICT_TRUE (ip6_addr != NULL)) + { + /* Create room for variable-length fields in the INIT_ACK chunk */ + alloc_bytes += SCTP_IPV6_ADDRESS_TYPE_LENGTH; + } + + if (sctp_conn->sub_conn[idx].connection.is_ip4) + alloc_bytes += sizeof (sctp_ipv4_addr_param_t); + else + alloc_bytes += sizeof (sctp_ipv6_addr_param_t); + + /* As per RFC 4960 the chunk_length value does NOT contemplate + * the size of the first header (see sctp_header_t) and any padding + */ + u16 chunk_len = alloc_bytes - sizeof (sctp_header_t); + + alloc_bytes += vnet_sctp_calculate_padding (alloc_bytes); + + sctp_init_ack_chunk_t *init_ack_chunk = + vlib_buffer_push_uninit (b, alloc_bytes); + + u16 pointer_offset = sizeof (sctp_init_ack_chunk_t); + + /* Create State Cookie parameter */ + sctp_state_cookie_param_t *state_cookie_param = + (sctp_state_cookie_param_t *) ((char *) init_ack_chunk + pointer_offset); + + state_cookie_param->param_hdr.type = + clib_host_to_net_u16 (SCTP_STATE_COOKIE_TYPE); + state_cookie_param->param_hdr.length = + clib_host_to_net_u16 (sizeof (sctp_state_cookie_param_t)); + state_cookie_param->creation_time = clib_host_to_net_u32 (sctp_time_now ()); + state_cookie_param->cookie_lifespan = + clib_host_to_net_u32 (SCTP_VALID_COOKIE_LIFE); + + sctp_compute_mac (sctp_conn, state_cookie_param); + + pointer_offset += sizeof (sctp_state_cookie_param_t); + + if (PREDICT_TRUE (ip4_addr != NULL)) + { + sctp_ipv4_addr_param_t *ipv4_addr = + (sctp_ipv4_addr_param_t *) init_ack_chunk + pointer_offset; + + ipv4_addr->param_hdr.type = + clib_host_to_net_u16 (SCTP_IPV4_ADDRESS_TYPE); + ipv4_addr->param_hdr.length = + clib_host_to_net_u16 (SCTP_IPV4_ADDRESS_TYPE_LENGTH); + ipv4_addr->address.as_u32 = ip4_addr->as_u32; + + pointer_offset += SCTP_IPV4_ADDRESS_TYPE_LENGTH; + } + if (PREDICT_TRUE (ip6_addr != NULL)) + { + sctp_ipv6_addr_param_t *ipv6_addr = + (sctp_ipv6_addr_param_t *) init_ack_chunk + pointer_offset; + + ipv6_addr->param_hdr.type = + clib_host_to_net_u16 (SCTP_IPV6_ADDRESS_TYPE); + ipv6_addr->param_hdr.length = + clib_host_to_net_u16 (SCTP_IPV6_ADDRESS_TYPE_LENGTH); + ipv6_addr->address.as_u64[0] = ip6_addr->as_u64[0]; + ipv6_addr->address.as_u64[1] = ip6_addr->as_u64[1]; + + pointer_offset += SCTP_IPV6_ADDRESS_TYPE_LENGTH; + } + + if (sctp_conn->sub_conn[idx].connection.is_ip4) + { + ip4_param = (sctp_ipv4_addr_param_t *) init_ack_chunk + pointer_offset; + ip4_param->address.as_u32 = + sctp_conn->sub_conn[idx].connection.lcl_ip.ip4.as_u32; + + pointer_offset += sizeof (sctp_ipv4_addr_param_t); + } + else + { + ip6_param = (sctp_ipv6_addr_param_t *) init_ack_chunk + pointer_offset; + ip6_param->address.as_u64[0] = + sctp_conn->sub_conn[idx].connection.lcl_ip.ip6.as_u64[0]; + ip6_param->address.as_u64[1] = + sctp_conn->sub_conn[idx].connection.lcl_ip.ip6.as_u64[1]; + + pointer_offset += sizeof (sctp_ipv6_addr_param_t); + } + + /* src_port & dst_port are already in network byte-order */ + init_ack_chunk->sctp_hdr.checksum = 0; + init_ack_chunk->sctp_hdr.src_port = + sctp_conn->sub_conn[idx].connection.lcl_port; + init_ack_chunk->sctp_hdr.dst_port = + sctp_conn->sub_conn[idx].connection.rmt_port; + /* the sctp_conn->verification_tag is already in network byte-order (being a copy of the init_tag coming with the INIT chunk) */ + init_ack_chunk->sctp_hdr.verification_tag = sctp_conn->remote_tag; + init_ack_chunk->initial_tsn = + clib_host_to_net_u32 (sctp_conn->local_initial_tsn); + SCTP_CONN_TRACKING_DBG ("init_ack_chunk->initial_tsn = %u", + init_ack_chunk->initial_tsn); + + vnet_sctp_set_chunk_type (&init_ack_chunk->chunk_hdr, INIT_ACK); + vnet_sctp_set_chunk_length (&init_ack_chunk->chunk_hdr, chunk_len); + + init_ack_chunk->initiate_tag = sctp_conn->local_tag; + + init_ack_chunk->a_rwnd = + clib_host_to_net_u32 (sctp_conn->sub_conn[idx].cwnd); + init_ack_chunk->inboud_streams_count = + clib_host_to_net_u16 (INBOUND_STREAMS_COUNT); + init_ack_chunk->outbound_streams_count = + clib_host_to_net_u16 (OUTBOUND_STREAMS_COUNT); + + vnet_buffer (b)->sctp.connection_index = + sctp_conn->sub_conn[idx].connection.c_index; + vnet_buffer (b)->sctp.subconn_idx = idx; +} + /** * Convert buffer to INIT-ACK */ -- cgit 1.2.3-korg