diff options
-rw-r--r-- | src/vnet/sctp/sctp.c | 24 | ||||
-rw-r--r-- | src/vnet/sctp/sctp_input.c | 71 | ||||
-rw-r--r-- | src/vnet/sctp/sctp_output.c | 42 | ||||
-rw-r--r-- | src/vnet/sctp/sctp_packet.h | 26 |
4 files changed, 146 insertions, 17 deletions
diff --git a/src/vnet/sctp/sctp.c b/src/vnet/sctp/sctp.c index 046eb18dcc2..20f23f22942 100644 --- a/src/vnet/sctp/sctp.c +++ b/src/vnet/sctp/sctp.c @@ -417,24 +417,16 @@ sctp_connection_cleanup (sctp_connection_t * sctp_conn) &sctp_conn->sub_conn[i].connection.lcl_ip, sctp_conn->sub_conn[i].connection.lcl_port); - /* Check if connection is not yet fully established */ - if (sctp_conn->state == SCTP_STATE_COOKIE_WAIT) - { - - } - else - { - int thread_index = - sctp_conn->sub_conn[MAIN_SCTP_SUB_CONN_IDX].connection.thread_index; + int thread_index = + sctp_conn->sub_conn[MAIN_SCTP_SUB_CONN_IDX].connection.thread_index; - /* Make sure all timers are cleared */ - sctp_connection_timers_reset (sctp_conn); + /* Make sure all timers are cleared */ + sctp_connection_timers_reset (sctp_conn); - /* Poison the entry */ - if (CLIB_DEBUG > 0) - memset (sctp_conn, 0xFA, sizeof (*sctp_conn)); - pool_put (tm->connections[thread_index], sctp_conn); - } + /* Poison the entry */ + if (CLIB_DEBUG > 0) + memset (sctp_conn, 0xFA, sizeof (*sctp_conn)); + pool_put (tm->connections[thread_index], sctp_conn); } int diff --git a/src/vnet/sctp/sctp_input.c b/src/vnet/sctp/sctp_input.c index 82adc2aa5b0..1863c89ef45 100644 --- a/src/vnet/sctp/sctp_input.c +++ b/src/vnet/sctp/sctp_input.c @@ -283,6 +283,35 @@ sctp_is_bundling (u16 sctp_implied_length, } always_inline u16 +sctp_handle_operation_err (sctp_header_t * sctp_hdr, + sctp_connection_t * sctp_conn, u8 idx, + vlib_buffer_t * b, u16 * next0) +{ + sctp_operation_error_t *op_err = (sctp_operation_error_t *) sctp_hdr; + + /* Check that the LOCALLY generated tag is being used by the REMOTE peer as the verification tag */ + if (sctp_conn->local_tag != sctp_hdr->verification_tag) + { + return SCTP_ERROR_INVALID_TAG; + } + + if (op_err->err_causes[0].cause_info == STALE_COOKIE_ERROR) + { + if (sctp_conn->state != SCTP_STATE_COOKIE_ECHOED) + *next0 = sctp_next_drop (sctp_conn->sub_conn[idx].c_is_ip4); + else + { + sctp_connection_cleanup (sctp_conn); + + stream_session_disconnect_notify (&sctp_conn-> + sub_conn[idx].connection); + } + } + + return SCTP_ERROR_NONE; +} + +always_inline u16 sctp_handle_init (sctp_header_t * sctp_hdr, sctp_chunks_common_hdr_t * sctp_chunk_hdr, sctp_connection_t * sctp_conn, vlib_buffer_t * b0, @@ -980,6 +1009,12 @@ sctp46_rcv_phase_inline (vlib_main_t * vm, vlib_node_runtime_t * node, } break; + case OPERATION_ERROR: + error0 = + sctp_handle_operation_err (sctp_hdr, sctp_conn, idx, b0, + &next0); + break; + /* All UNEXPECTED scenarios (wrong chunk received per state-machine) * are handled by the input-dispatcher function using the table-lookup * hence we should never get to the "default" case below. @@ -1309,6 +1344,12 @@ sctp46_shutdown_phase_inline (vlib_main_t * vm, sctp_conn, idx, b0, &next0); break; + case OPERATION_ERROR: + error0 = + sctp_handle_operation_err (sctp_hdr, sctp_conn, idx, b0, + &next0); + break; + /* All UNEXPECTED scenarios (wrong chunk received per state-machine) * are handled by the input-dispatcher function using the table-lookup * hence we should never get to the "default" case below. @@ -1660,6 +1701,13 @@ sctp46_listen_process_inline (vlib_main_t * vm, */ case DATA: break; + + case OPERATION_ERROR: + error0 = + sctp_handle_operation_err (sctp_hdr, child_conn, + MAIN_SCTP_SUB_CONN_IDX, b0, + &next0); + break; } drop: @@ -1807,6 +1855,12 @@ sctp46_established_phase_inline (vlib_main_t * vm, vlib_node_runtime_t * node, sctp_conn, idx, b0, &next0); break; + case OPERATION_ERROR: + error0 = + sctp_handle_operation_err (sctp_hdr, sctp_conn, idx, b0, + &next0); + break; + /* All UNEXPECTED scenarios (wrong chunk received per state-machine) * are handled by the input-dispatcher function using the table-lookup * hence we should never get to the "default" case below. @@ -2261,8 +2315,9 @@ do { \ _(CLOSED, ECNE, SCTP_INPUT_NEXT_DROP, SCTP_ERROR_ECNE_VIOLATION); /* UNEXPECTED ECNE chunk */ _(CLOSED, CWR, SCTP_INPUT_NEXT_DROP, SCTP_ERROR_CWR_VIOLATION); /* UNEXPECTED CWR chunk */ _(CLOSED, SHUTDOWN_COMPLETE, SCTP_INPUT_NEXT_DROP, SCTP_ERROR_SHUTDOWN_COMPLETE_VIOLATION); /* UNEXPECTED SHUTDOWN_COMPLETE chunk */ + _(CLOSED, OPERATION_ERROR, SCTP_INPUT_NEXT_LISTEN_PHASE, SCTP_ERROR_NONE); - _(COOKIE_WAIT, DATA, SCTP_INPUT_NEXT_DROP, SCTP_ERROR_NONE); + _(COOKIE_WAIT, DATA, SCTP_INPUT_NEXT_DROP, SCTP_ERROR_NONE); /* UNEXPECTED DATA chunk which requires special handling */ _(COOKIE_WAIT, INIT, SCTP_INPUT_NEXT_RCV_PHASE, SCTP_ERROR_NONE); /* UNEXPECTED INIT chunk which requires special handling */ _(COOKIE_WAIT, INIT_ACK, SCTP_INPUT_NEXT_RCV_PHASE, SCTP_ERROR_NONE); _(COOKIE_WAIT, SACK, SCTP_INPUT_NEXT_DROP, SCTP_ERROR_SACK_CHUNK_VIOLATION); /* UNEXPECTED SACK chunk */ @@ -2277,6 +2332,8 @@ do { \ _(COOKIE_WAIT, ECNE, SCTP_INPUT_NEXT_DROP, SCTP_ERROR_ECNE_VIOLATION); /* UNEXPECTED ECNE chunk */ _(COOKIE_WAIT, CWR, SCTP_INPUT_NEXT_DROP, SCTP_ERROR_CWR_VIOLATION); /* UNEXPECTED CWR chunk */ _(COOKIE_WAIT, SHUTDOWN_COMPLETE, SCTP_INPUT_NEXT_DROP, SCTP_ERROR_SHUTDOWN_COMPLETE_VIOLATION); /* UNEXPECTED SHUTDOWN_COMPLETE chunk */ + _(COOKIE_WAIT, OPERATION_ERROR, SCTP_INPUT_NEXT_LISTEN_PHASE, + SCTP_ERROR_NONE); _(COOKIE_ECHOED, DATA, SCTP_INPUT_NEXT_DROP, SCTP_ERROR_NONE); _(COOKIE_ECHOED, INIT, SCTP_INPUT_NEXT_RCV_PHASE, SCTP_ERROR_NONE); /* UNEXPECTED INIT chunk which requires special handling */ @@ -2294,6 +2351,8 @@ do { \ _(COOKIE_ECHOED, ECNE, SCTP_INPUT_NEXT_DROP, SCTP_ERROR_ECNE_VIOLATION); /* UNEXPECTED ECNE chunk */ _(COOKIE_ECHOED, CWR, SCTP_INPUT_NEXT_DROP, SCTP_ERROR_CWR_VIOLATION); /* UNEXPECTED CWR chunk */ _(COOKIE_ECHOED, SHUTDOWN_COMPLETE, SCTP_INPUT_NEXT_DROP, SCTP_ERROR_SHUTDOWN_COMPLETE_VIOLATION); /* UNEXPECTED SHUTDOWN_COMPLETE chunk */ + _(COOKIE_ECHOED, OPERATION_ERROR, SCTP_INPUT_NEXT_LISTEN_PHASE, + SCTP_ERROR_NONE); _(ESTABLISHED, DATA, SCTP_INPUT_NEXT_ESTABLISHED_PHASE, SCTP_ERROR_NONE); _(ESTABLISHED, INIT, SCTP_INPUT_NEXT_DROP, SCTP_ERROR_INIT_CHUNK_VIOLATION); /* UNEXPECTED INIT chunk */ @@ -2312,6 +2371,8 @@ do { \ _(ESTABLISHED, ECNE, SCTP_INPUT_NEXT_DROP, SCTP_ERROR_ECNE_VIOLATION); /* UNEXPECTED ECNE chunk */ _(ESTABLISHED, CWR, SCTP_INPUT_NEXT_DROP, SCTP_ERROR_CWR_VIOLATION); /* UNEXPECTED CWR chunk */ _(ESTABLISHED, SHUTDOWN_COMPLETE, SCTP_INPUT_NEXT_DROP, SCTP_ERROR_SHUTDOWN_COMPLETE_VIOLATION); /* UNEXPECTED SHUTDOWN_COMPLETE chunk */ + _(ESTABLISHED, OPERATION_ERROR, SCTP_INPUT_NEXT_LISTEN_PHASE, + SCTP_ERROR_NONE); _(SHUTDOWN_PENDING, DATA, SCTP_INPUT_NEXT_SHUTDOWN_PHASE, SCTP_ERROR_NONE); _(SHUTDOWN_PENDING, INIT, SCTP_INPUT_NEXT_DROP, SCTP_ERROR_INIT_CHUNK_VIOLATION); /* UNEXPECTED INIT chunk */ @@ -2331,6 +2392,8 @@ do { \ _(SHUTDOWN_PENDING, ECNE, SCTP_INPUT_NEXT_DROP, SCTP_ERROR_ECNE_VIOLATION); /* UNEXPECTED ECNE chunk */ _(SHUTDOWN_PENDING, CWR, SCTP_INPUT_NEXT_DROP, SCTP_ERROR_CWR_VIOLATION); /* UNEXPECTED CWR chunk */ _(SHUTDOWN_PENDING, SHUTDOWN_COMPLETE, SCTP_INPUT_NEXT_DROP, SCTP_ERROR_SHUTDOWN_COMPLETE_VIOLATION); /* UNEXPECTED SHUTDOWN_COMPLETE chunk */ + _(SHUTDOWN_PENDING, OPERATION_ERROR, SCTP_INPUT_NEXT_LISTEN_PHASE, + SCTP_ERROR_NONE); _(SHUTDOWN_SENT, DATA, SCTP_INPUT_NEXT_SHUTDOWN_PHASE, SCTP_ERROR_NONE); _(SHUTDOWN_SENT, INIT, SCTP_INPUT_NEXT_DROP, SCTP_ERROR_INIT_CHUNK_VIOLATION); /* UNEXPECTED INIT chunk */ @@ -2347,6 +2410,8 @@ do { \ _(SHUTDOWN_SENT, ECNE, SCTP_INPUT_NEXT_DROP, SCTP_ERROR_ECNE_VIOLATION); /* UNEXPECTED ECNE chunk */ _(SHUTDOWN_SENT, CWR, SCTP_INPUT_NEXT_DROP, SCTP_ERROR_CWR_VIOLATION); /* UNEXPECTED CWR chunk */ _(SHUTDOWN_SENT, SHUTDOWN_COMPLETE, SCTP_INPUT_NEXT_DROP, SCTP_ERROR_SHUTDOWN_COMPLETE_VIOLATION); /* UNEXPECTED SHUTDOWN_COMPLETE chunk */ + _(SHUTDOWN_SENT, OPERATION_ERROR, SCTP_INPUT_NEXT_LISTEN_PHASE, + SCTP_ERROR_NONE); _(SHUTDOWN_RECEIVED, DATA, SCTP_INPUT_NEXT_DROP, SCTP_ERROR_DATA_CHUNK_VIOLATION); /* UNEXPECTED DATA chunk */ _(SHUTDOWN_RECEIVED, INIT, SCTP_INPUT_NEXT_DROP, SCTP_ERROR_INIT_CHUNK_VIOLATION); /* UNEXPECTED INIT chunk */ @@ -2363,6 +2428,8 @@ do { \ _(SHUTDOWN_RECEIVED, ECNE, SCTP_INPUT_NEXT_DROP, SCTP_ERROR_ECNE_VIOLATION); /* UNEXPECTED ECNE chunk */ _(SHUTDOWN_RECEIVED, CWR, SCTP_INPUT_NEXT_DROP, SCTP_ERROR_CWR_VIOLATION); /* UNEXPECTED CWR chunk */ _(SHUTDOWN_RECEIVED, SHUTDOWN_COMPLETE, SCTP_INPUT_NEXT_DROP, SCTP_ERROR_SHUTDOWN_COMPLETE_VIOLATION); /* UNEXPECTED SHUTDOWN_COMPLETE chunk */ + _(SHUTDOWN_RECEIVED, OPERATION_ERROR, SCTP_INPUT_NEXT_LISTEN_PHASE, + SCTP_ERROR_NONE); _(SHUTDOWN_ACK_SENT, DATA, SCTP_INPUT_NEXT_DROP, SCTP_ERROR_DATA_CHUNK_VIOLATION); /* UNEXPECTED DATA chunk */ _(SHUTDOWN_ACK_SENT, INIT, SCTP_INPUT_NEXT_RCV_PHASE, SCTP_ERROR_NONE); /* UNEXPECTED INIT chunk */ @@ -2379,6 +2446,8 @@ do { \ _(SHUTDOWN_ACK_SENT, CWR, SCTP_INPUT_NEXT_DROP, SCTP_ERROR_CWR_VIOLATION); /* UNEXPECTED CWR chunk */ _(SHUTDOWN_ACK_SENT, SHUTDOWN_COMPLETE, SCTP_INPUT_NEXT_SHUTDOWN_PHASE, SCTP_ERROR_NONE); + _(SHUTDOWN_ACK_SENT, OPERATION_ERROR, SCTP_INPUT_NEXT_LISTEN_PHASE, + SCTP_ERROR_NONE); /* TODO: Handle COOKIE ECHO when a TCB Exists */ diff --git a/src/vnet/sctp/sctp_output.c b/src/vnet/sctp/sctp_output.c index e44451c034c..5e64ca792af 100644 --- a/src/vnet/sctp/sctp_output.c +++ b/src/vnet/sctp/sctp_output.c @@ -591,6 +591,48 @@ sctp_prepare_cookie_echo_chunk (sctp_connection_t * sctp_conn, u8 idx, /** * Convert buffer to ABORT */ +/* +void +sctp_prepare_operation_error (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_operation_error_t + u16 alloc_bytes = sizeof (sctp_operation_error_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_operation_error_t *err_chunk = + vlib_buffer_push_uninit (b, alloc_bytes); + + // src_port & dst_port are already in network byte-order + err_chunk->sctp_hdr.checksum = 0; + err_chunk->sctp_hdr.src_port = sctp_conn->sub_conn[idx].connection.lcl_port; + err_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 + err_chunk->sctp_hdr.verification_tag = sctp_conn->local_tag; + + vnet_sctp_set_chunk_type (&err_chunk->chunk_hdr, OPERATION_ERROR); + vnet_sctp_set_chunk_length (&err_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 ABORT + */ void sctp_prepare_abort_for_collision (sctp_connection_t * sctp_conn, u8 idx, vlib_buffer_t * b, ip4_address_t * ip4_addr, diff --git a/src/vnet/sctp/sctp_packet.h b/src/vnet/sctp/sctp_packet.h index 8109efcafc1..0cee3f26174 100644 --- a/src/vnet/sctp/sctp_packet.h +++ b/src/vnet/sctp/sctp_packet.h @@ -1315,6 +1315,32 @@ typedef struct } sctp_err_cause_param_t; + +/* + * An end-point sends this chunk to its peer end-point to notify it of + * certain error conditions. It contains one or more error causes. + * An Operation Error is not considered fatal in and of itself, but may be + * used with an ABORT chunk to report a fatal condition. It has the + * following parameters: + * + * 0 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Type = 9 | Chunk Flags | Length | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * \ \ + * / one or more Error Causes / + * \ \ + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ +typedef struct +{ + sctp_header_t sctp_hdr; + sctp_chunks_common_hdr_t chunk_hdr; + sctp_err_cause_param_t err_causes[]; + +} sctp_operation_error_t; + /* * Abort Association (ABORT) * |