X-Git-Url: https://gerrit.fd.io/r/gitweb?a=blobdiff_plain;f=src%2Fvnet%2Fsctp%2Fsctp_input.c;h=00d0a61feff1fa23ee79597d52a31a88821d621a;hb=3c6a976;hp=8df95642ce4be71f42deeeed0c0aca7517aaf4c8;hpb=be2251b0c5b10a3a556e75c9bfbea96df4799297;p=vpp.git diff --git a/src/vnet/sctp/sctp_input.c b/src/vnet/sctp/sctp_input.c index 8df95642ce4..00d0a61feff 100644 --- a/src/vnet/sctp/sctp_input.c +++ b/src/vnet/sctp/sctp_input.c @@ -27,7 +27,8 @@ static char *sctp_error_strings[] = { /* All SCTP nodes have the same outgoing arcs */ #define foreach_sctp_state_next \ - _ (DROP, "error-drop") \ + _ (DROP4, "ip4-drop") \ + _ (DROP6, "ip6-drop") \ _ (SCTP4_OUTPUT, "sctp4-output") \ _ (SCTP6_OUTPUT, "sctp6-output") @@ -233,6 +234,8 @@ typedef struct #define sctp_next_output(is_ip4) (is_ip4 ? SCTP_NEXT_SCTP4_OUTPUT \ : SCTP_NEXT_SCTP6_OUTPUT) +#define sctp_next_drop(is_ip4) (is_ip4 ? SCTP_NEXT_DROP4 \ + : SCTP_NEXT_DROP6) void sctp_set_rx_trace_data (sctp_rx_trace_t * rx_trace, @@ -279,6 +282,36 @@ sctp_is_bundling (u16 sctp_implied_length, return 0; } +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 (clib_net_to_host_u16 (op_err->err_causes[0].param_hdr.type) == + 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, @@ -301,12 +334,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; } } @@ -354,7 +399,10 @@ sctp_handle_init (sctp_header_t * sctp_hdr, clib_memcpy (ip4_addr, &ipv4->address, sizeof (ip4_address_t)); - sctp_sub_connection_add_ip4 (vlib_get_thread_index (), ipv4); + sctp_sub_connection_add_ip4 (vlib_get_main (), + &sctp_conn->sub_conn + [MAIN_SCTP_SUB_CONN_IDX].connection. + lcl_ip.ip4, &ipv4->address); break; } @@ -365,7 +413,10 @@ sctp_handle_init (sctp_header_t * sctp_hdr, clib_memcpy (ip6_addr, &ipv6->address, sizeof (ip6_address_t)); - sctp_sub_connection_add_ip6 (vlib_get_thread_index (), ipv6); + sctp_sub_connection_add_ip6 (vlib_get_main (), + &sctp_conn->sub_conn + [MAIN_SCTP_SUB_CONN_IDX].connection. + lcl_ip.ip6, &ipv6->address); break; } @@ -396,7 +447,8 @@ sctp_handle_init (sctp_header_t * sctp_hdr, } /* Reuse buffer to make init-ack and send */ - sctp_prepare_initack_chunk (sctp_conn, b0, ip4_addr, ip6_addr); + sctp_prepare_initack_chunk (sctp_conn, MAIN_SCTP_SUB_CONN_IDX, b0, ip4_addr, + ip6_addr); return SCTP_ERROR_NONE; } @@ -482,7 +534,10 @@ sctp_handle_init_ack (sctp_header_t * sctp_hdr, sctp_ipv4_addr_param_t *ipv4 = (sctp_ipv4_addr_param_t *) opt_params_hdr; - sctp_sub_connection_add_ip4 (vlib_get_thread_index (), ipv4); + sctp_sub_connection_add_ip4 (vlib_get_main (), + &sctp_conn->sub_conn + [MAIN_SCTP_SUB_CONN_IDX].connection. + lcl_ip.ip4, &ipv4->address); break; } @@ -491,7 +546,10 @@ sctp_handle_init_ack (sctp_header_t * sctp_hdr, sctp_ipv6_addr_param_t *ipv6 = (sctp_ipv6_addr_param_t *) opt_params_hdr; - sctp_sub_connection_add_ip6 (vlib_get_thread_index (), ipv6); + sctp_sub_connection_add_ip6 (vlib_get_main (), + &sctp_conn->sub_conn + [MAIN_SCTP_SUB_CONN_IDX].connection. + lcl_ip.ip6, &ipv6->address); break; } @@ -527,10 +585,10 @@ sctp_handle_init_ack (sctp_header_t * sctp_hdr, } } - sctp_prepare_cookie_echo_chunk (sctp_conn, b0, &state_cookie); + sctp_prepare_cookie_echo_chunk (sctp_conn, idx, b0, &state_cookie); /* Start the T1_COOKIE timer */ - sctp_timer_set (sctp_conn, sctp_pick_conn_idx_on_chunk (COOKIE_ECHO), + sctp_timer_set (sctp_conn, idx, SCTP_TIMER_T1_COOKIE, sctp_conn->sub_conn[idx].RTO); return SCTP_ERROR_NONE; @@ -645,14 +703,14 @@ sctp_session_enqueue_data (sctp_connection_t * sctp_conn, vlib_buffer_t * b, } always_inline u8 -sctp_is_sack_delayable (sctp_connection_t * sctp_conn, u8 gapping) +sctp_is_sack_delayable (sctp_connection_t * sctp_conn, u8 idx, u8 is_gapping) { - if (gapping != 0) + if (is_gapping != 0) { SCTP_CONN_TRACKING_DBG ("gapping != 0: CONN_INDEX = %u, sctp_conn->ack_state = %u", sctp_conn->sub_conn[idx].connection.c_index, sctp_conn->ack_state); - return 1; + return 0; } if (sctp_conn->ack_state >= MAX_ENQUEABLE_SACKS) @@ -660,12 +718,12 @@ sctp_is_sack_delayable (sctp_connection_t * sctp_conn, u8 gapping) SCTP_CONN_TRACKING_DBG ("sctp_conn->ack_state >= MAX_ENQUEABLE_SACKS: CONN_INDEX = %u, sctp_conn->ack_state = %u", sctp_conn->sub_conn[idx].connection.c_index, sctp_conn->ack_state); - return 1; + return 0; } sctp_conn->ack_state += 1; - return 0; + return 1; } always_inline void @@ -676,7 +734,7 @@ sctp_is_connection_gapping (sctp_connection_t * sctp_conn, u32 tsn, { SCTP_CONN_TRACKING_DBG ("GAPPING: CONN_INDEX = %u, sctp_conn->next_tsn_expected = %u, tsn = %u, diff = %u", - sctp_conn->sub_conn[idx].connection.c_index, + sctp_conn->sub_conn[MAIN_SCTP_SUB_CONN_IDX].connection.c_index, sctp_conn->next_tsn_expected, tsn, sctp_conn->next_tsn_expected - tsn); @@ -720,7 +778,11 @@ sctp_handle_data (sctp_payload_data_chunk_t * sctp_data_chunk, { /* In order data, enqueue. Fifo figures out by itself if any out-of-order * segments can be enqueued after fifo tail offset changes. */ - error = sctp_session_enqueue_data (sctp_conn, b, n_data_bytes, idx); + if (PREDICT_FALSE (is_gapping == 1)) + error = + sctp_session_enqueue_data_ooo (sctp_conn, b, n_data_bytes, idx); + else + error = sctp_session_enqueue_data (sctp_conn, b, n_data_bytes, idx); } else if (bbit == 1 && ebit == 0) /* First piece of a fragmented user message */ { @@ -744,12 +806,14 @@ sctp_handle_data (sctp_payload_data_chunk_t * sctp_data_chunk, } sctp_conn->last_rcvd_tsn = tsn; - *next0 = sctp_next_output (sctp_conn->sub_conn[idx].c_is_ip4); + *next0 = sctp_next_drop (sctp_conn->sub_conn[idx].c_is_ip4); SCTP_ADV_DBG ("POINTER_WITH_DATA = %p", b->data); - if (sctp_is_sack_delayable (sctp_conn, is_gapping) != 0) - sctp_prepare_sack_chunk (sctp_conn, b); + if (!sctp_is_sack_delayable (sctp_conn, idx, is_gapping)) + sctp_prepare_sack_chunk (sctp_conn, idx, b); + + sctp_conn->sub_conn[idx].enqueue_state = error; return error; } @@ -784,10 +848,11 @@ sctp_handle_cookie_echo (sctp_header_t * sctp_hdr, return SCTP_ERROR_COOKIE_ECHO_VIOLATION; } - sctp_prepare_cookie_ack_chunk (sctp_conn, b0); + sctp_prepare_cookie_ack_chunk (sctp_conn, idx, b0); /* Change state */ sctp_conn->state = SCTP_STATE_ESTABLISHED; + sctp_conn->sub_conn[idx].state = SCTP_SUBCONN_STATE_UP; *next0 = sctp_next_output (sctp_conn->sub_conn[idx].c_is_ip4); sctp_timer_set (sctp_conn, idx, SCTP_TIMER_T4_HEARTBEAT, @@ -816,7 +881,9 @@ sctp_handle_cookie_ack (sctp_header_t * sctp_hdr, sctp_timer_reset (sctp_conn, idx, SCTP_TIMER_T1_COOKIE); /* Change state */ sctp_conn->state = SCTP_STATE_ESTABLISHED; - *next0 = sctp_next_output (sctp_conn->sub_conn[idx].c_is_ip4); + sctp_conn->sub_conn[idx].state = SCTP_SUBCONN_STATE_UP; + + *next0 = sctp_next_drop (sctp_conn->sub_conn[idx].c_is_ip4); sctp_timer_set (sctp_conn, idx, SCTP_TIMER_T4_HEARTBEAT, sctp_conn->sub_conn[idx].RTO); @@ -857,7 +924,7 @@ sctp46_rcv_phase_inline (vlib_main_t * vm, vlib_node_runtime_t * node, ip6_header_t *ip6_hdr = 0; sctp_connection_t *sctp_conn, *new_sctp_conn; u16 sctp_implied_length = 0; - u16 error0 = SCTP_ERROR_NONE, next0 = SCTP_RCV_PHASE_N_NEXT; + u16 error0 = SCTP_ERROR_NONE, next0 = sctp_next_drop (is_ip4); u8 idx; bi0 = from[0]; @@ -887,36 +954,18 @@ sctp46_rcv_phase_inline (vlib_main_t * vm, vlib_node_runtime_t * node, { ip4_hdr = vlib_buffer_get_current (b0); sctp_hdr = ip4_next_header (ip4_hdr); + idx = sctp_sub_conn_id_via_ip4h (sctp_conn, ip4_hdr); } else { ip6_hdr = vlib_buffer_get_current (b0); sctp_hdr = ip6_next_header (ip6_hdr); + idx = sctp_sub_conn_id_via_ip6h (sctp_conn, ip6_hdr); } - idx = sctp_pick_conn_idx_on_state (sctp_conn->state); + sctp_conn->sub_conn[idx].subconn_idx = idx; sctp_full_hdr_t *full_hdr = (sctp_full_hdr_t *) sctp_hdr; - transport_connection_t *trans_conn = - &sctp_conn->sub_conn[idx].connection; - - trans_conn->lcl_port = sctp_hdr->dst_port; - trans_conn->rmt_port = sctp_hdr->src_port; - trans_conn->is_ip4 = is_ip4; - - if (is_ip4) - { - trans_conn->lcl_ip.ip4.as_u32 = ip4_hdr->dst_address.as_u32; - trans_conn->rmt_ip.ip4.as_u32 = ip4_hdr->src_address.as_u32; - } - else - { - clib_memcpy (&trans_conn->lcl_ip.ip6, &ip6_hdr->dst_address, - sizeof (ip6_address_t)); - clib_memcpy (&trans_conn->rmt_ip.ip6, &ip6_hdr->src_address, - sizeof (ip6_address_t)); - } - sctp_chunk_hdr = (sctp_chunks_common_hdr_t *) (&full_hdr->common_hdr); @@ -941,7 +990,9 @@ sctp46_rcv_phase_inline (vlib_main_t * vm, vlib_node_runtime_t * node, new_sctp_conn - tm->connections[my_thread_index]; new_sctp_conn->sub_conn[idx].c_thread_index = my_thread_index; - new_sctp_conn->sub_conn[idx].parent = new_sctp_conn; + new_sctp_conn->sub_conn[idx].PMTU = + sctp_conn->sub_conn[idx].PMTU; + new_sctp_conn->sub_conn[idx].subconn_idx = idx; if (sctp_half_open_connection_cleanup (sctp_conn)) { @@ -956,7 +1007,7 @@ sctp46_rcv_phase_inline (vlib_main_t * vm, vlib_node_runtime_t * node, new_sctp_conn, idx, b0, sctp_implied_length); - sctp_init_mss (new_sctp_conn); + sctp_init_cwnd (new_sctp_conn); if (session_stream_connect_notify (&new_sctp_conn->sub_conn[idx].connection, 0)) @@ -967,8 +1018,14 @@ sctp46_rcv_phase_inline (vlib_main_t * vm, vlib_node_runtime_t * node, sctp_connection_cleanup (new_sctp_conn); goto drop; } + next0 = sctp_next_output (is_ip4); } - next0 = sctp_next_output (is_ip4); + 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) @@ -977,7 +1034,7 @@ sctp46_rcv_phase_inline (vlib_main_t * vm, vlib_node_runtime_t * node, */ default: error0 = SCTP_ERROR_UNKOWN_CHUNK; - next0 = SCTP_NEXT_DROP; + next0 = sctp_next_drop (is_ip4); goto drop; } @@ -985,7 +1042,7 @@ sctp46_rcv_phase_inline (vlib_main_t * vm, vlib_node_runtime_t * node, { clib_warning ("error while parsing chunk"); sctp_connection_cleanup (sctp_conn); - next0 = SCTP_NEXT_DROP; + next0 = sctp_next_drop (is_ip4); goto drop; } @@ -1110,11 +1167,11 @@ sctp_handle_shutdown (sctp_header_t * sctp_hdr, case SCTP_STATE_ESTABLISHED: if (sctp_check_outstanding_data_chunks (sctp_conn) == 0) sctp_conn->state = SCTP_STATE_SHUTDOWN_RECEIVED; - sctp_send_shutdown_ack (sctp_conn, b0); + sctp_send_shutdown_ack (sctp_conn, idx, b0); break; case SCTP_STATE_SHUTDOWN_SENT: - sctp_send_shutdown_ack (sctp_conn, b0); + sctp_send_shutdown_ack (sctp_conn, idx, b0); break; } @@ -1152,7 +1209,8 @@ sctp_handle_shutdown_ack (sctp_header_t * sctp_hdr, */ sctp_timer_reset (sctp_conn, MAIN_SCTP_SUB_CONN_IDX, SCTP_TIMER_T2_SHUTDOWN); - sctp_send_shutdown_complete (sctp_conn); + + sctp_send_shutdown_complete (sctp_conn, idx, b0); *next0 = sctp_next_output (sctp_conn->sub_conn[idx].c_is_ip4); @@ -1181,15 +1239,13 @@ sctp_handle_shutdown_complete (sctp_header_t * sctp_hdr, if (sctp_is_bundling (sctp_implied_length, &shutdown_complete->chunk_hdr)) return SCTP_ERROR_BUNDLING_VIOLATION; - sctp_timer_reset (sctp_conn, MAIN_SCTP_SUB_CONN_IDX, - SCTP_TIMER_T2_SHUTDOWN); + sctp_timer_reset (sctp_conn, idx, SCTP_TIMER_T2_SHUTDOWN); - sctp_conn->state = SCTP_STATE_CLOSED; + stream_session_disconnect_notify (&sctp_conn->sub_conn[idx].connection); - stream_session_disconnect_notify (&sctp_conn->sub_conn - [MAIN_SCTP_SUB_CONN_IDX].connection); + sctp_conn->state = SCTP_STATE_CLOSED; - *next0 = sctp_next_output (sctp_conn->sub_conn[idx].c_is_ip4); + *next0 = sctp_next_drop (sctp_conn->sub_conn[idx].c_is_ip4); return SCTP_ERROR_NONE; } @@ -1225,6 +1281,7 @@ sctp46_shutdown_phase_inline (vlib_main_t * vm, sctp_connection_t *sctp_conn; u16 sctp_implied_length = 0; u16 error0 = SCTP_ERROR_NONE, next0 = SCTP_RCV_PHASE_N_NEXT; + u8 idx = 0; bi0 = from[0]; to_next[0] = bi0; @@ -1250,11 +1307,13 @@ sctp46_shutdown_phase_inline (vlib_main_t * vm, { ip4_hdr = vlib_buffer_get_current (b0); sctp_hdr = ip4_next_header (ip4_hdr); + idx = sctp_sub_conn_id_via_ip4h (sctp_conn, ip4_hdr); } else { ip6_hdr = vlib_buffer_get_current (b0); sctp_hdr = ip6_next_header (ip6_hdr); + idx = sctp_sub_conn_id_via_ip6h (sctp_conn, ip6_hdr); } sctp_full_hdr_t *full_hdr = (sctp_full_hdr_t *) sctp_hdr; @@ -1263,8 +1322,6 @@ sctp46_shutdown_phase_inline (vlib_main_t * vm, sctp_implied_length = sctp_calculate_implied_length (ip4_hdr, ip6_hdr, is_ip4); - u8 idx = sctp_pick_conn_idx_on_state (sctp_conn->state); - u8 chunk_type = vnet_sctp_get_chunk_type (sctp_chunk_hdr); switch (chunk_type) { @@ -1300,13 +1357,25 @@ 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; + + case COOKIE_ECHO: /* Cookie Received While Shutting Down */ + sctp_prepare_operation_error (sctp_conn, idx, b0, + COOKIE_RECEIVED_WHILE_SHUTTING_DOWN); + error0 = SCTP_ERROR_NONE; + next0 = sctp_next_output (is_ip4); + 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. */ default: error0 = SCTP_ERROR_UNKOWN_CHUNK; - next0 = SCTP_NEXT_DROP; + next0 = sctp_next_drop (is_ip4); goto drop; } @@ -1314,7 +1383,7 @@ sctp46_shutdown_phase_inline (vlib_main_t * vm, { clib_warning ("error while parsing chunk"); sctp_connection_cleanup (sctp_conn); - next0 = SCTP_NEXT_DROP; + next0 = sctp_next_drop (is_ip4); goto drop; } @@ -1423,6 +1492,19 @@ sctp_handle_sack (sctp_selective_ack_chunk_t * sack_chunk, return SCTP_ERROR_INVALID_TAG; } + sctp_conn->sub_conn[idx].last_seen = sctp_time_now (); + + /* Section 7.2.2; point (2) */ + if (sctp_conn->sub_conn[idx].cwnd > sctp_conn->sub_conn[idx].ssthresh) + sctp_conn->sub_conn[idx].partially_acked_bytes = + sctp_conn->next_tsn - sack_chunk->cumulative_tsn_ack; + + /* Section 7.2.2; point (5) */ + if (sctp_conn->next_tsn - sack_chunk->cumulative_tsn_ack == 0) + sctp_conn->sub_conn[idx].partially_acked_bytes = 0; + + sctp_conn->last_unacked_tsn = sack_chunk->cumulative_tsn_ack; + sctp_calculate_rto (sctp_conn, idx); sctp_timer_update (sctp_conn, idx, SCTP_TIMER_T3_RXTX, @@ -1430,7 +1512,7 @@ sctp_handle_sack (sctp_selective_ack_chunk_t * sack_chunk, sctp_conn->sub_conn[idx].RTO_pending = 0; - *next0 = sctp_next_output (sctp_conn->sub_conn[idx].connection.is_ip4); + *next0 = sctp_next_drop (sctp_conn->sub_conn[idx].c_is_ip4); return SCTP_ERROR_NONE; } @@ -1446,7 +1528,7 @@ sctp_handle_heartbeat (sctp_hb_req_chunk_t * sctp_hb_chunk, return SCTP_ERROR_INVALID_TAG; } - sctp_prepare_heartbeat_ack_chunk (sctp_conn, b0); + sctp_prepare_heartbeat_ack_chunk (sctp_conn, idx, b0); *next0 = sctp_next_output (sctp_conn->sub_conn[idx].connection.is_ip4); @@ -1458,27 +1540,29 @@ sctp_handle_heartbeat_ack (sctp_hb_ack_chunk_t * sctp_hb_ack_chunk, sctp_connection_t * sctp_conn, u8 idx, vlib_buffer_t * b0, u16 * next0) { + sctp_conn->sub_conn[idx].last_seen = sctp_time_now (); + sctp_conn->sub_conn[idx].unacknowledged_hb -= 1; sctp_timer_update (sctp_conn, idx, SCTP_TIMER_T4_HEARTBEAT, sctp_conn->sub_conn[idx].RTO); - *next0 = sctp_next_output (sctp_conn->sub_conn[idx].connection.is_ip4); + *next0 = sctp_next_drop (sctp_conn->sub_conn[idx].c_is_ip4); return SCTP_ERROR_NONE; } always_inline void -sctp_node_inc_counter (vlib_main_t * vm, u32 tcp4_node, u32 tcp6_node, +sctp_node_inc_counter (vlib_main_t * vm, u32 sctp4_node, u32 sctp6_node, u8 is_ip4, u8 evt, u8 val) { if (PREDICT_TRUE (!val)) return; if (is_ip4) - vlib_node_increment_counter (vm, tcp4_node, evt, val); + vlib_node_increment_counter (vm, sctp4_node, evt, val); else - vlib_node_increment_counter (vm, tcp6_node, evt, val); + vlib_node_increment_counter (vm, sctp6_node, evt, val); } always_inline uword @@ -1509,7 +1593,7 @@ sctp46_listen_process_inline (vlib_main_t * vm, ip6_header_t *ip6_hdr; sctp_connection_t *child_conn; sctp_connection_t *sctp_listener; - u16 next0 = SCTP_LISTEN_PHASE_N_NEXT, error0 = SCTP_ERROR_ENQUEUED; + u16 next0 = sctp_next_drop (is_ip4), error0 = SCTP_ERROR_ENQUEUED; bi0 = from[0]; to_next[0] = bi0; @@ -1551,7 +1635,8 @@ sctp46_listen_process_inline (vlib_main_t * vm, /* Create child session and send SYN-ACK */ child_conn = sctp_connection_new (my_thread_index); - child_conn->sub_conn[MAIN_SCTP_SUB_CONN_IDX].parent = child_conn; + child_conn->sub_conn[MAIN_SCTP_SUB_CONN_IDX].subconn_idx = + MAIN_SCTP_SUB_CONN_IDX; child_conn->sub_conn[MAIN_SCTP_SUB_CONN_IDX].c_lcl_port = sctp_hdr->dst_port; child_conn->sub_conn[MAIN_SCTP_SUB_CONN_IDX].c_rmt_port = @@ -1559,6 +1644,8 @@ sctp46_listen_process_inline (vlib_main_t * vm, child_conn->sub_conn[MAIN_SCTP_SUB_CONN_IDX].c_is_ip4 = is_ip4; child_conn->sub_conn[MAIN_SCTP_SUB_CONN_IDX].connection.proto = sctp_listener->sub_conn[MAIN_SCTP_SUB_CONN_IDX].connection.proto; + child_conn->sub_conn[MAIN_SCTP_SUB_CONN_IDX].PMTU = + sctp_listener->sub_conn[MAIN_SCTP_SUB_CONN_IDX].PMTU; child_conn->state = SCTP_STATE_CLOSED; if (is_ip4) @@ -1590,7 +1677,7 @@ sctp46_listen_process_inline (vlib_main_t * vm, connection.c_index, sctp_chunk_to_string (chunk_type)); error0 = SCTP_ERROR_UNKOWN_CHUNK; - next0 = SCTP_NEXT_DROP; + next0 = sctp_next_drop (is_ip4); goto drop; } @@ -1608,7 +1695,7 @@ sctp46_listen_process_inline (vlib_main_t * vm, sctp_handle_init (sctp_hdr, sctp_chunk_hdr, child_conn, b0, sctp_implied_length); - sctp_init_mss (child_conn); + sctp_init_cwnd (child_conn); if (error0 == SCTP_ERROR_NONE) { @@ -1623,8 +1710,8 @@ sctp46_listen_process_inline (vlib_main_t * vm, error0 = SCTP_ERROR_CREATE_SESSION_FAIL; goto drop; } + next0 = sctp_next_output (is_ip4); } - next0 = sctp_next_output (is_ip4); break; /* Reception of a DATA chunk whilst in the CLOSED state is called @@ -1633,6 +1720,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: @@ -1698,7 +1792,8 @@ sctp46_established_phase_inline (vlib_main_t * vm, vlib_node_runtime_t * node, ip4_header_t *ip4_hdr = 0; ip6_header_t *ip6_hdr = 0; sctp_connection_t *sctp_conn; - u16 error0 = SCTP_ERROR_NONE, next0 = SCTP_ESTABLISHED_PHASE_N_NEXT; + u16 error0 = SCTP_ERROR_ENQUEUED, next0 = + SCTP_ESTABLISHED_PHASE_N_NEXT; u8 idx; bi0 = from[0]; @@ -1724,39 +1819,18 @@ sctp46_established_phase_inline (vlib_main_t * vm, vlib_node_runtime_t * node, { ip4_hdr = vlib_buffer_get_current (b0); sctp_hdr = ip4_next_header (ip4_hdr); + idx = sctp_sub_conn_id_via_ip4h (sctp_conn, ip4_hdr); } else { ip6_hdr = vlib_buffer_get_current (b0); sctp_hdr = ip6_next_header (ip6_hdr); + idx = sctp_sub_conn_id_via_ip6h (sctp_conn, ip6_hdr); } - idx = sctp_pick_conn_idx_on_state (sctp_conn->state); + sctp_conn->sub_conn[idx].subconn_idx = idx; sctp_full_hdr_t *full_hdr = (sctp_full_hdr_t *) sctp_hdr; - - transport_connection_t *trans_conn = - &sctp_conn->sub_conn[idx].connection; - - trans_conn->lcl_port = sctp_hdr->dst_port; - trans_conn->rmt_port = sctp_hdr->src_port; - trans_conn->is_ip4 = is_ip4; - - sctp_conn->sub_conn[idx].parent = sctp_conn; - - if (is_ip4) - { - trans_conn->lcl_ip.ip4.as_u32 = ip4_hdr->dst_address.as_u32; - trans_conn->rmt_ip.ip4.as_u32 = ip4_hdr->src_address.as_u32; - } - else - { - clib_memcpy (&trans_conn->lcl_ip.ip6, &ip6_hdr->dst_address, - sizeof (ip6_address_t)); - clib_memcpy (&trans_conn->rmt_ip.ip6, &ip6_hdr->src_address, - sizeof (ip6_address_t)); - } - sctp_chunk_hdr = (sctp_chunks_common_hdr_t *) (&full_hdr->common_hdr); @@ -1800,13 +1874,19 @@ 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. */ default: error0 = SCTP_ERROR_UNKOWN_CHUNK; - next0 = SCTP_NEXT_DROP; + next0 = sctp_next_drop (is_ip4); goto done; } @@ -2005,7 +2085,7 @@ sctp46_input_dispatcher (vlib_main_t * vm, vlib_node_runtime_t * node, n_left_to_next -= 1; b0 = vlib_get_buffer (vm, bi0); - vnet_buffer (b0)->tcp.flags = 0; + vnet_buffer (b0)->sctp.flags = 0; fib_index0 = vnet_buffer (b0)->ip.fib_index; /* Checksum computed by ipx_local no need to compute again */ @@ -2067,10 +2147,20 @@ sctp46_input_dispatcher (vlib_main_t * vm, vlib_node_runtime_t * node, sctp_conn = sctp_get_connection_from_transport (trans_conn); vnet_sctp_common_hdr_params_net_to_host (sctp_chunk_hdr); - u8 type = vnet_sctp_get_chunk_type (sctp_chunk_hdr); -#if SCTP_DEBUG_STATE_MACHINE - u8 idx = sctp_pick_conn_idx_on_state (sctp_conn->state); -#endif + u8 chunk_type = vnet_sctp_get_chunk_type (sctp_chunk_hdr); + if (chunk_type >= UNKNOWN) + { + clib_warning + ("Received an unrecognized chunk; sending back OPERATION_ERROR chunk"); + + sctp_prepare_operation_error (sctp_conn, MAIN_SCTP_SUB_CONN_IDX, + b0, UNRECOGNIZED_CHUNK_TYPE); + + error0 = SCTP_ERROR_UNKOWN_CHUNK; + next0 = sctp_next_output (is_ip4); + goto done; + } + vnet_buffer (b0)->sctp.hdr_offset = (u8 *) sctp_hdr - (u8 *) vlib_buffer_get_current (b0); @@ -2082,20 +2172,20 @@ sctp46_input_dispatcher (vlib_main_t * vm, vlib_node_runtime_t * node, vnet_buffer (b0)->sctp.data_offset = n_advance_bytes0; vnet_buffer (b0)->sctp.data_len = n_data_bytes0; - next0 = tm->dispatch_table[sctp_conn->state][type].next; - error0 = tm->dispatch_table[sctp_conn->state][type].error; - - SCTP_DBG_STATE_MACHINE ("CONNECTION_INDEX = %u: " - "CURRENT_CONNECTION_STATE = %s," - "CHUNK_TYPE_RECEIVED = %s " - "NEXT_PHASE = %s", - sctp_conn->sub_conn - [idx].connection.c_index, - sctp_state_to_string (sctp_conn->state), - sctp_chunk_to_string (type), - phase_to_string (next0)); + next0 = tm->dispatch_table[sctp_conn->state][chunk_type].next; + error0 = tm->dispatch_table[sctp_conn->state][chunk_type].error; + + SCTP_DBG_STATE_MACHINE + ("S_INDEX = %u, C_INDEX = %u, TRANS_CONN = %p, SCTP_CONN = %p, CURRENT_CONNECTION_STATE = %s," + "CHUNK_TYPE_RECEIVED = %s " "NEXT_PHASE = %s", + sctp_conn->sub_conn[MAIN_SCTP_SUB_CONN_IDX]. + connection.s_index, + sctp_conn->sub_conn[MAIN_SCTP_SUB_CONN_IDX]. + connection.c_index, trans_conn, sctp_conn, + sctp_state_to_string (sctp_conn->state), + sctp_chunk_to_string (chunk_type), phase_to_string (next0)); - if (type == DATA) + if (chunk_type == DATA) SCTP_ADV_DBG ("n_advance_bytes0 = %u, n_data_bytes0 = %u", n_advance_bytes0, n_data_bytes0); @@ -2233,7 +2323,7 @@ do { \ * _(SHUTDOWN_RECEIVED, "SHUTDOWN_RECEIVED") \ * _(SHUTDOWN_ACK_SENT, "SHUTDOWN_ACK_SENT") */ - _(CLOSED, DATA, SCTP_INPUT_NEXT_LISTEN_PHASE, SCTP_ERROR_NONE); /* UNEXPECTED DATA chunk which requires special handling */ + //_(CLOSED, DATA, SCTP_INPUT_NEXT_LISTEN_PHASE, SCTP_ERROR_NONE); /* UNEXPECTED DATA chunk which requires special handling */ _(CLOSED, INIT, SCTP_INPUT_NEXT_LISTEN_PHASE, SCTP_ERROR_NONE); _(CLOSED, INIT_ACK, SCTP_INPUT_NEXT_DROP, SCTP_ERROR_ACK_DUP); /* UNEXPECTED INIT_ACK chunk */ _(CLOSED, SACK, SCTP_INPUT_NEXT_DROP, SCTP_ERROR_SACK_CHUNK_VIOLATION); /* UNEXPECTED SACK chunk */ @@ -2248,8 +2338,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 */ @@ -2264,6 +2355,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 */ @@ -2281,6 +2374,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 */ @@ -2299,6 +2394,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 */ @@ -2313,11 +2410,14 @@ do { \ SCTP_ERROR_NONE); _(SHUTDOWN_PENDING, SHUTDOWN_ACK, SCTP_INPUT_NEXT_DROP, SCTP_ERROR_SHUTDOWN_ACK_CHUNK_VIOLATION); /* UNEXPECTED SHUTDOWN_ACK chunk */ _(SHUTDOWN_PENDING, OPERATION_ERROR, SCTP_INPUT_NEXT_DROP, SCTP_ERROR_OPERATION_ERROR_VIOLATION); /* UNEXPECTED OPERATION_ERROR chunk */ - _(SHUTDOWN_PENDING, COOKIE_ECHO, SCTP_INPUT_NEXT_DROP, SCTP_ERROR_COOKIE_ECHO_VIOLATION); /* UNEXPECTED COOKIE_ECHO chunk */ + _(SHUTDOWN_PENDING, COOKIE_ECHO, SCTP_INPUT_NEXT_SHUTDOWN_PHASE, + SCTP_ERROR_NONE); _(SHUTDOWN_PENDING, COOKIE_ACK, SCTP_INPUT_NEXT_DROP, SCTP_ERROR_ACK_DUP); /* UNEXPECTED COOKIE_ACK chunk */ _(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 */ @@ -2329,11 +2429,14 @@ do { \ _(SHUTDOWN_SENT, SHUTDOWN, SCTP_INPUT_NEXT_SHUTDOWN_PHASE, SCTP_ERROR_NONE); _(SHUTDOWN_SENT, SHUTDOWN_ACK, SCTP_INPUT_NEXT_SHUTDOWN_PHASE, SCTP_ERROR_NONE); - _(SHUTDOWN_SENT, COOKIE_ECHO, SCTP_INPUT_NEXT_DROP, SCTP_ERROR_COOKIE_ECHO_VIOLATION); /* UNEXPECTED COOKIE_ECHO chunk */ + _(SHUTDOWN_SENT, COOKIE_ECHO, SCTP_INPUT_NEXT_SHUTDOWN_PHASE, + SCTP_ERROR_NONE); _(SHUTDOWN_SENT, COOKIE_ACK, SCTP_INPUT_NEXT_DROP, SCTP_ERROR_ACK_DUP); /* UNEXPECTED COOKIE_ACK chunk */ _(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 */ @@ -2345,14 +2448,17 @@ do { \ _(SHUTDOWN_RECEIVED, SHUTDOWN, SCTP_INPUT_NEXT_DROP, SCTP_ERROR_SHUTDOWN_CHUNK_VIOLATION); /* UNEXPECTED SHUTDOWN chunk */ _(SHUTDOWN_RECEIVED, SHUTDOWN_ACK, SCTP_INPUT_NEXT_SHUTDOWN_PHASE, SCTP_ERROR_NONE); - _(SHUTDOWN_RECEIVED, COOKIE_ECHO, SCTP_INPUT_NEXT_DROP, SCTP_ERROR_COOKIE_ECHO_VIOLATION); /* UNEXPECTED COOKIE_ECHO chunk */ + _(SHUTDOWN_RECEIVED, COOKIE_ECHO, SCTP_INPUT_NEXT_SHUTDOWN_PHASE, + SCTP_ERROR_NONE); _(SHUTDOWN_RECEIVED, COOKIE_ACK, SCTP_INPUT_NEXT_DROP, SCTP_ERROR_ACK_DUP); /* UNEXPECTED COOKIE_ACK chunk */ _(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_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 */ @@ -2360,12 +2466,15 @@ do { \ _(SHUTDOWN_ACK_SENT, ABORT, SCTP_INPUT_NEXT_DROP, SCTP_ERROR_ABORT_CHUNK_VIOLATION); /* UNEXPECTED ABORT chunk */ _(SHUTDOWN_ACK_SENT, SHUTDOWN, SCTP_INPUT_NEXT_DROP, SCTP_ERROR_SHUTDOWN_CHUNK_VIOLATION); /* UNEXPECTED SHUTDOWN chunk */ _(SHUTDOWN_ACK_SENT, SHUTDOWN_ACK, SCTP_INPUT_NEXT_DROP, SCTP_ERROR_SHUTDOWN_ACK_CHUNK_VIOLATION); /* UNEXPECTED SHUTDOWN_ACK chunk */ - _(SHUTDOWN_ACK_SENT, COOKIE_ECHO, SCTP_INPUT_NEXT_DROP, SCTP_ERROR_COOKIE_ECHO_VIOLATION); /* UNEXPECTED COOKIE_ECHO chunk */ + _(SHUTDOWN_ACK_SENT, COOKIE_ECHO, SCTP_INPUT_NEXT_SHUTDOWN_PHASE, + SCTP_ERROR_NONE); _(SHUTDOWN_ACK_SENT, COOKIE_ACK, SCTP_INPUT_NEXT_DROP, SCTP_ERROR_ACK_DUP); /* UNEXPECTED COOKIE_ACK chunk */ _(SHUTDOWN_ACK_SENT, ECNE, SCTP_INPUT_NEXT_DROP, SCTP_ERROR_ECNE_VIOLATION); /* UNEXPECTED ECNE chunk */ _(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 */