diff options
Diffstat (limited to 'src/vnet/sctp/sctp_output.c')
-rw-r--r-- | src/vnet/sctp/sctp_output.c | 1568 |
1 files changed, 0 insertions, 1568 deletions
diff --git a/src/vnet/sctp/sctp_output.c b/src/vnet/sctp/sctp_output.c deleted file mode 100644 index 3c2099a08fd..00000000000 --- a/src/vnet/sctp/sctp_output.c +++ /dev/null @@ -1,1568 +0,0 @@ -/* - * Copyright (c) 2017 SUSE LLC. - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include <vnet/sctp/sctp.h> -#include <vnet/sctp/sctp_debug.h> -#include <vppinfra/random.h> -#include <openssl/hmac.h> - -/** - * Flush tx frame populated by retransmits and timer pops - */ -void -sctp_flush_frame_to_output (vlib_main_t * vm, u8 thread_index, u8 is_ip4) -{ - if (sctp_main.tx_frames[!is_ip4][thread_index]) - { - u32 next_index; - next_index = is_ip4 ? sctp4_output_node.index : sctp6_output_node.index; - vlib_put_frame_to_node (vm, next_index, - sctp_main.tx_frames[!is_ip4][thread_index]); - sctp_main.tx_frames[!is_ip4][thread_index] = 0; - } -} - -/** - * Flush ip lookup tx frames populated by timer pops - */ -always_inline void -sctp_flush_frame_to_ip_lookup (vlib_main_t * vm, u8 thread_index, u8 is_ip4) -{ - if (sctp_main.ip_lookup_tx_frames[!is_ip4][thread_index]) - { - u32 next_index; - next_index = is_ip4 ? ip4_lookup_node.index : ip6_lookup_node.index; - vlib_put_frame_to_node (vm, next_index, - sctp_main.ip_lookup_tx_frames[!is_ip4] - [thread_index]); - sctp_main.ip_lookup_tx_frames[!is_ip4][thread_index] = 0; - } -} - -/** - * Flush v4 and v6 sctp and ip-lookup tx frames for thread index - */ -void -sctp_flush_frames_to_output (u8 thread_index) -{ - vlib_main_t *vm = vlib_get_main (); - sctp_flush_frame_to_output (vm, thread_index, 1); - sctp_flush_frame_to_output (vm, thread_index, 0); - sctp_flush_frame_to_ip_lookup (vm, thread_index, 1); - sctp_flush_frame_to_ip_lookup (vm, thread_index, 0); -} - -u32 -ip4_sctp_compute_checksum (vlib_main_t * vm, vlib_buffer_t * p0, - ip4_header_t * ip0) -{ - ip_csum_t checksum; - u32 ip_header_length, payload_length_host_byte_order; - u32 n_this_buffer, n_bytes_left, n_ip_bytes_this_buffer; - void *data_this_buffer; - - /* Initialize checksum with ip header. */ - ip_header_length = ip4_header_bytes (ip0); - payload_length_host_byte_order = - clib_net_to_host_u16 (ip0->length) - ip_header_length; - checksum = - clib_host_to_net_u32 (payload_length_host_byte_order + - (ip0->protocol << 16)); - - if (BITS (uword) == 32) - { - checksum = - ip_csum_with_carry (checksum, - clib_mem_unaligned (&ip0->src_address, u32)); - checksum = - ip_csum_with_carry (checksum, - clib_mem_unaligned (&ip0->dst_address, u32)); - } - else - checksum = - ip_csum_with_carry (checksum, - clib_mem_unaligned (&ip0->src_address, u64)); - - n_bytes_left = n_this_buffer = payload_length_host_byte_order; - data_this_buffer = (void *) ip0 + ip_header_length; - n_ip_bytes_this_buffer = - p0->current_length - (((u8 *) ip0 - p0->data) - p0->current_data); - if (n_this_buffer + ip_header_length > n_ip_bytes_this_buffer) - { - n_this_buffer = n_ip_bytes_this_buffer > ip_header_length ? - n_ip_bytes_this_buffer - ip_header_length : 0; - } - while (1) - { - checksum = - ip_incremental_checksum (checksum, data_this_buffer, n_this_buffer); - n_bytes_left -= n_this_buffer; - if (n_bytes_left == 0) - break; - - ASSERT (p0->flags & VLIB_BUFFER_NEXT_PRESENT); - p0 = vlib_get_buffer (vm, p0->next_buffer); - data_this_buffer = vlib_buffer_get_current (p0); - n_this_buffer = p0->current_length; - } - - return checksum; -} - -u32 -ip6_sctp_compute_checksum (vlib_main_t * vm, vlib_buffer_t * p0, - ip6_header_t * ip0, int *bogus_lengthp) -{ - ip_csum_t checksum; - u16 payload_length_host_byte_order; - u32 i, n_this_buffer, n_bytes_left; - u32 headers_size = sizeof (ip0[0]); - void *data_this_buffer; - - ASSERT (bogus_lengthp); - *bogus_lengthp = 0; - - /* Initialize checksum with ip header. */ - checksum = ip0->payload_length + clib_host_to_net_u16 (ip0->protocol); - payload_length_host_byte_order = clib_net_to_host_u16 (ip0->payload_length); - data_this_buffer = (void *) (ip0 + 1); - - for (i = 0; i < ARRAY_LEN (ip0->src_address.as_uword); i++) - { - checksum = ip_csum_with_carry (checksum, - clib_mem_unaligned (&ip0-> - src_address.as_uword - [i], uword)); - checksum = - ip_csum_with_carry (checksum, - clib_mem_unaligned (&ip0->dst_address.as_uword[i], - uword)); - } - - /* some icmp packets may come with a "router alert" hop-by-hop extension header (e.g., mldv2 packets) - * or UDP-Ping packets */ - if (PREDICT_FALSE (ip0->protocol == IP_PROTOCOL_IP6_HOP_BY_HOP_OPTIONS)) - { - u32 skip_bytes; - ip6_hop_by_hop_ext_t *ext_hdr = - (ip6_hop_by_hop_ext_t *) data_this_buffer; - - /* validate really icmp6 next */ - ASSERT ((ext_hdr->next_hdr == IP_PROTOCOL_SCTP)); - - skip_bytes = 8 * (1 + ext_hdr->n_data_u64s); - data_this_buffer = (void *) ((u8 *) data_this_buffer + skip_bytes); - - payload_length_host_byte_order -= skip_bytes; - headers_size += skip_bytes; - } - - n_bytes_left = n_this_buffer = payload_length_host_byte_order; - if (p0 && n_this_buffer + headers_size > p0->current_length) - n_this_buffer = - p0->current_length > - headers_size ? p0->current_length - headers_size : 0; - while (1) - { - checksum = - ip_incremental_checksum (checksum, data_this_buffer, n_this_buffer); - n_bytes_left -= n_this_buffer; - if (n_bytes_left == 0) - break; - - if (!(p0->flags & VLIB_BUFFER_NEXT_PRESENT)) - { - *bogus_lengthp = 1; - return 0xfefe; - } - p0 = vlib_get_buffer (vm, p0->next_buffer); - data_this_buffer = vlib_buffer_get_current (p0); - n_this_buffer = p0->current_length; - } - - return checksum; -} - -void -sctp_push_ip_hdr (sctp_main_t * tm, sctp_sub_connection_t * sctp_sub_conn, - vlib_buffer_t * b) -{ - sctp_header_t *th = vlib_buffer_get_current (b); - vlib_main_t *vm = vlib_get_main (); - if (sctp_sub_conn->c_is_ip4) - { - ip4_header_t *ih; - ih = vlib_buffer_push_ip4 (vm, b, &sctp_sub_conn->c_lcl_ip4, - &sctp_sub_conn->c_rmt_ip4, IP_PROTOCOL_SCTP, - 1); - th->checksum = ip4_sctp_compute_checksum (vm, b, ih); - } - else - { - ip6_header_t *ih; - int bogus = ~0; - - ih = vlib_buffer_push_ip6 (vm, b, &sctp_sub_conn->c_lcl_ip6, - &sctp_sub_conn->c_rmt_ip6, IP_PROTOCOL_SCTP); - th->checksum = ip6_sctp_compute_checksum (vm, b, ih, &bogus); - ASSERT (!bogus); - } -} - -always_inline void * -sctp_reuse_buffer (vlib_main_t * vm, vlib_buffer_t * b) -{ - if (b->flags & VLIB_BUFFER_NEXT_PRESENT) - vlib_buffer_free_one (vm, b->next_buffer); - /* Zero all flags but free list index and trace flag */ - b->flags &= VLIB_BUFFER_NEXT_PRESENT - 1; - b->current_data = 0; - b->current_length = 0; - b->total_length_not_including_first_buffer = 0; - vnet_buffer (b)->sctp.flags = 0; - vnet_buffer (b)->sctp.subconn_idx = MAX_SCTP_CONNECTIONS; - - /* Leave enough space for headers */ - return vlib_buffer_make_headroom (b, TRANSPORT_MAX_HDRS_LEN); -} - -always_inline void * -sctp_init_buffer (vlib_main_t * vm, vlib_buffer_t * b) -{ - ASSERT ((b->flags & VLIB_BUFFER_NEXT_PRESENT) == 0); - b->flags |= VNET_BUFFER_F_LOCALLY_ORIGINATED; - b->total_length_not_including_first_buffer = 0; - vnet_buffer (b)->sctp.flags = 0; - vnet_buffer (b)->sctp.subconn_idx = MAX_SCTP_CONNECTIONS; - VLIB_BUFFER_TRACE_TRAJECTORY_INIT (b); - /* Leave enough space for headers */ - return vlib_buffer_make_headroom (b, TRANSPORT_MAX_HDRS_LEN); -} - -always_inline int -sctp_alloc_tx_buffers (sctp_main_t * tm, u8 thread_index, u32 n_free_buffers) -{ - vlib_main_t *vm = vlib_get_main (); - u32 current_length = vec_len (tm->tx_buffers[thread_index]); - u32 n_allocated; - - vec_validate (tm->tx_buffers[thread_index], - current_length + n_free_buffers - 1); - n_allocated = - vlib_buffer_alloc (vm, &tm->tx_buffers[thread_index][current_length], - n_free_buffers); - _vec_len (tm->tx_buffers[thread_index]) = current_length + n_allocated; - /* buffer shortage, report failure */ - if (vec_len (tm->tx_buffers[thread_index]) == 0) - { - clib_warning ("out of buffers"); - return -1; - } - return 0; -} - -always_inline int -sctp_get_free_buffer_index (sctp_main_t * tm, u32 * bidx) -{ - u32 *my_tx_buffers; - u32 thread_index = vlib_get_thread_index (); - if (PREDICT_FALSE (vec_len (tm->tx_buffers[thread_index]) == 0)) - { - if (sctp_alloc_tx_buffers (tm, thread_index, VLIB_FRAME_SIZE)) - return -1; - } - my_tx_buffers = tm->tx_buffers[thread_index]; - *bidx = my_tx_buffers[vec_len (my_tx_buffers) - 1]; - _vec_len (my_tx_buffers) -= 1; - return 0; -} - -always_inline void -sctp_enqueue_to_output_i (vlib_main_t * vm, vlib_buffer_t * b, u32 bi, - u8 is_ip4, u8 flush) -{ - sctp_main_t *tm = vnet_get_sctp_main (); - u32 thread_index = vlib_get_thread_index (); - u32 *to_next, next_index; - vlib_frame_t *f; - - b->flags |= VNET_BUFFER_F_LOCALLY_ORIGINATED; - b->error = 0; - - /* Decide where to send the packet */ - next_index = is_ip4 ? sctp4_output_node.index : sctp6_output_node.index; - sctp_trajectory_add_start (b, 2); - - /* Get frame to v4/6 output node */ - f = tm->tx_frames[!is_ip4][thread_index]; - if (!f) - { - f = vlib_get_frame_to_node (vm, next_index); - ASSERT (f); - tm->tx_frames[!is_ip4][thread_index] = f; - } - to_next = vlib_frame_vector_args (f); - to_next[f->n_vectors] = bi; - f->n_vectors += 1; - if (flush || f->n_vectors == VLIB_FRAME_SIZE) - { - vlib_put_frame_to_node (vm, next_index, f); - tm->tx_frames[!is_ip4][thread_index] = 0; - } -} - -always_inline void -sctp_enqueue_to_output_now (vlib_main_t * vm, vlib_buffer_t * b, u32 bi, - u8 is_ip4) -{ - sctp_enqueue_to_output_i (vm, b, bi, is_ip4, 1); -} - -always_inline void -sctp_enqueue_to_ip_lookup_i (vlib_main_t * vm, vlib_buffer_t * b, u32 bi, - u8 is_ip4, u32 fib_index, u8 flush) -{ - sctp_main_t *tm = vnet_get_sctp_main (); - u32 thread_index = vlib_get_thread_index (); - u32 *to_next, next_index; - vlib_frame_t *f; - - b->flags |= VNET_BUFFER_F_LOCALLY_ORIGINATED; - b->error = 0; - - vnet_buffer (b)->sw_if_index[VLIB_TX] = fib_index; - vnet_buffer (b)->sw_if_index[VLIB_RX] = 0; - - /* Send to IP lookup */ - next_index = is_ip4 ? ip4_lookup_node.index : ip6_lookup_node.index; - if (VLIB_BUFFER_TRACE_TRAJECTORY > 0) - { - b->pre_data[0] = 2; - b->pre_data[1] = next_index; - } - - f = tm->ip_lookup_tx_frames[!is_ip4][thread_index]; - if (!f) - { - f = vlib_get_frame_to_node (vm, next_index); - ASSERT (f); - tm->ip_lookup_tx_frames[!is_ip4][thread_index] = f; - } - - to_next = vlib_frame_vector_args (f); - to_next[f->n_vectors] = bi; - f->n_vectors += 1; - if (flush || f->n_vectors == VLIB_FRAME_SIZE) - { - vlib_put_frame_to_node (vm, next_index, f); - tm->ip_lookup_tx_frames[!is_ip4][thread_index] = 0; - } -} - -always_inline void -sctp_enqueue_to_ip_lookup (vlib_main_t * vm, vlib_buffer_t * b, u32 bi, - u8 is_ip4, u32 fib_index) -{ - sctp_enqueue_to_ip_lookup_i (vm, b, bi, is_ip4, fib_index, 0); - if (vm->thread_index == 0 && vlib_num_workers ()) - session_flush_frames_main_thread (vm); -} - -/** - * Convert buffer to INIT - */ -void -sctp_prepare_init_chunk (sctp_connection_t * sctp_conn, u8 idx, - vlib_buffer_t * b) -{ - u32 random_seed = random_default_seed (); - u16 alloc_bytes = sizeof (sctp_init_chunk_t); - sctp_sub_connection_t *sub_conn = &sctp_conn->sub_conn[idx]; - - sctp_ipv4_addr_param_t *ip4_param = 0; - sctp_ipv6_addr_param_t *ip6_param = 0; - - if (sub_conn->c_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_chunk_t *init_chunk = vlib_buffer_push_uninit (b, alloc_bytes); - - u16 pointer_offset = sizeof (init_chunk); - if (sub_conn->c_is_ip4) - { - ip4_param = (sctp_ipv4_addr_param_t *) init_chunk + pointer_offset; - ip4_param->address.as_u32 = sub_conn->c_lcl_ip.ip4.as_u32; - - pointer_offset += sizeof (sctp_ipv4_addr_param_t); - } - else - { - ip6_param = (sctp_ipv6_addr_param_t *) init_chunk + pointer_offset; - ip6_param->address.as_u64[0] = sub_conn->c_lcl_ip.ip6.as_u64[0]; - ip6_param->address.as_u64[1] = sub_conn->c_lcl_ip.ip6.as_u64[1]; - - pointer_offset += sizeof (sctp_ipv6_addr_param_t); - } - - init_chunk->sctp_hdr.src_port = sub_conn->c_lcl_port; /* No need of host_to_net conversion, already in net-byte order */ - init_chunk->sctp_hdr.dst_port = sub_conn->c_rmt_port; /* No need of host_to_net conversion, already in net-byte order */ - init_chunk->sctp_hdr.checksum = 0; - /* The sender of an INIT must set the VERIFICATION_TAG to 0 as per RFC 4960 Section 8.5.1 */ - init_chunk->sctp_hdr.verification_tag = 0x0; - - vnet_sctp_set_chunk_type (&init_chunk->chunk_hdr, INIT); - vnet_sctp_set_chunk_length (&init_chunk->chunk_hdr, chunk_len); - vnet_sctp_common_hdr_params_host_to_net (&init_chunk->chunk_hdr); - - sctp_init_cwnd (sctp_conn); - - init_chunk->a_rwnd = clib_host_to_net_u32 (sctp_conn->sub_conn[idx].cwnd); - init_chunk->initiate_tag = clib_host_to_net_u32 (random_u32 (&random_seed)); - init_chunk->inboud_streams_count = - clib_host_to_net_u16 (INBOUND_STREAMS_COUNT); - init_chunk->outbound_streams_count = - clib_host_to_net_u16 (OUTBOUND_STREAMS_COUNT); - - init_chunk->initial_tsn = - clib_host_to_net_u32 (sctp_conn->local_initial_tsn); - SCTP_CONN_TRACKING_DBG ("sctp_conn->local_initial_tsn = %u", - sctp_conn->local_initial_tsn); - - sctp_conn->local_tag = init_chunk->initiate_tag; - - vnet_buffer (b)->sctp.connection_index = sub_conn->c_c_index; - vnet_buffer (b)->sctp.subconn_idx = idx; - - SCTP_DBG_STATE_MACHINE ("CONN_INDEX = %u, CURR_CONN_STATE = %u (%s), " - "CHUNK_TYPE = %s, " - "SRC_PORT = %u, DST_PORT = %u", - sub_conn->connection.c_index, - sctp_conn->state, - sctp_state_to_string (sctp_conn->state), - sctp_chunk_to_string (INIT), - init_chunk->sctp_hdr.src_port, - init_chunk->sctp_hdr.dst_port); -} - -void -sctp_compute_mac (sctp_connection_t * sctp_conn, - sctp_state_cookie_param_t * state_cookie) -{ -#if OPENSSL_VERSION_NUMBER >= 0x10100000L - HMAC_CTX *ctx; -#else - HMAC_CTX ctx; -#endif - unsigned int len = 0; - const EVP_MD *md = EVP_sha1 (); -#if OPENSSL_VERSION_NUMBER >= 0x10100000L - ctx = HMAC_CTX_new (); - HMAC_Init_ex (ctx, &state_cookie->creation_time, - sizeof (state_cookie->creation_time), md, NULL); - HMAC_Update (ctx, (const unsigned char *) &sctp_conn, sizeof (sctp_conn)); - HMAC_Final (ctx, state_cookie->mac, &len); -#else - HMAC_CTX_init (&ctx); - HMAC_Init_ex (&ctx, &state_cookie->creation_time, - sizeof (state_cookie->creation_time), md, NULL); - HMAC_Update (&ctx, (const unsigned char *) &sctp_conn, sizeof (sctp_conn)); - HMAC_Final (&ctx, state_cookie->mac, &len); - HMAC_CTX_cleanup (&ctx); -#endif - - ENDIANESS_SWAP (state_cookie->mac); -} - -void -sctp_prepare_cookie_ack_chunk (sctp_connection_t * sctp_conn, u8 idx, - vlib_buffer_t * b) -{ - vlib_main_t *vm = vlib_get_main (); - - sctp_reuse_buffer (vm, b); - - u16 alloc_bytes = sizeof (sctp_cookie_ack_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_cookie_ack_chunk_t *cookie_ack_chunk = - vlib_buffer_push_uninit (b, alloc_bytes); - - cookie_ack_chunk->sctp_hdr.checksum = 0; - cookie_ack_chunk->sctp_hdr.src_port = - sctp_conn->sub_conn[idx].connection.lcl_port; - cookie_ack_chunk->sctp_hdr.dst_port = - sctp_conn->sub_conn[idx].connection.rmt_port; - cookie_ack_chunk->sctp_hdr.verification_tag = sctp_conn->remote_tag; - vnet_sctp_set_chunk_type (&cookie_ack_chunk->chunk_hdr, COOKIE_ACK); - vnet_sctp_set_chunk_length (&cookie_ack_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; -} - -void -sctp_prepare_cookie_echo_chunk (sctp_connection_t * sctp_conn, u8 idx, - vlib_buffer_t * b, u8 reuse_buffer) -{ - vlib_main_t *vm = vlib_get_main (); - - if (reuse_buffer) - 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_cookie_echo_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_cookie_echo_chunk_t *cookie_echo_chunk = - vlib_buffer_push_uninit (b, alloc_bytes); - cookie_echo_chunk->sctp_hdr.checksum = 0; - cookie_echo_chunk->sctp_hdr.src_port = - sctp_conn->sub_conn[idx].connection.lcl_port; - cookie_echo_chunk->sctp_hdr.dst_port = - sctp_conn->sub_conn[idx].connection.rmt_port; - cookie_echo_chunk->sctp_hdr.verification_tag = sctp_conn->remote_tag; - vnet_sctp_set_chunk_type (&cookie_echo_chunk->chunk_hdr, COOKIE_ECHO); - vnet_sctp_set_chunk_length (&cookie_echo_chunk->chunk_hdr, chunk_len); - clib_memcpy_fast (&(cookie_echo_chunk->cookie), &sctp_conn->cookie_param, - sizeof (sctp_state_cookie_param_t)); - - vnet_buffer (b)->sctp.connection_index = - sctp_conn->sub_conn[idx].connection.c_index; - vnet_buffer (b)->sctp.subconn_idx = idx; -} - - -/* - * Send COOKIE_ECHO - */ -void -sctp_send_cookie_echo (sctp_connection_t * sctp_conn) -{ - vlib_buffer_t *b; - u32 bi; - sctp_main_t *tm = vnet_get_sctp_main (); - vlib_main_t *vm = vlib_get_main (); - - if (PREDICT_FALSE (sctp_conn->init_retransmit_err > SCTP_MAX_INIT_RETRANS)) - { - clib_warning ("Reached MAX_INIT_RETRANS times. Aborting connection."); - - session_stream_connect_notify (&sctp_conn->sub_conn - [SCTP_PRIMARY_PATH_IDX].connection, 1); - - sctp_connection_timers_reset (sctp_conn); - - sctp_connection_cleanup (sctp_conn); - } - - if (PREDICT_FALSE (sctp_get_free_buffer_index (tm, &bi))) - return; - - b = vlib_get_buffer (vm, bi); - u8 idx = SCTP_PRIMARY_PATH_IDX; - - sctp_init_buffer (vm, b); - sctp_prepare_cookie_echo_chunk (sctp_conn, idx, b, 0); - sctp_enqueue_to_output_now (vm, b, bi, sctp_conn->sub_conn[idx].c_is_ip4); - - /* Start the T1_INIT timer */ - sctp_timer_set (sctp_conn, idx, SCTP_TIMER_T1_INIT, - sctp_conn->sub_conn[idx].RTO); - - /* Change state to COOKIE_WAIT */ - sctp_conn->state = SCTP_STATE_COOKIE_WAIT; - - /* Measure RTT with this */ - sctp_conn->sub_conn[idx].rtt_ts = sctp_time_now (); -} - - -/** - * Convert buffer to ERROR - */ -void -sctp_prepare_operation_error (sctp_connection_t * sctp_conn, u8 idx, - vlib_buffer_t * b, u8 err_cause) -{ - 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) + sizeof (sctp_err_cause_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_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; - - err_chunk->err_causes[0].param_hdr.length = - clib_host_to_net_u16 (sizeof (err_chunk->err_causes[0].param_hdr.type) + - sizeof (err_chunk->err_causes[0].param_hdr.length)); - err_chunk->err_causes[0].param_hdr.type = clib_host_to_net_u16 (err_cause); - - 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, - 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_u64 (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 - */ -void -sctp_prepare_initack_chunk (sctp_connection_t * sctp_conn, u8 idx, - vlib_buffer_t * b, ip4_address_t * ip4_addr, - u8 add_ip4, ip6_address_t * ip6_addr, u8 add_ip6) -{ - vlib_main_t *vm = vlib_get_main (); - sctp_ipv4_addr_param_t *ip4_param = 0; - sctp_ipv6_addr_param_t *ip6_param = 0; - u32 random_seed = random_default_seed (); - - 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_FALSE (add_ip4 == 1)) - { - /* Create room for variable-length fields in the INIT_ACK chunk */ - alloc_bytes += SCTP_IPV4_ADDRESS_TYPE_LENGTH; - } - if (PREDICT_FALSE (add_ip6 == 1)) - { - /* 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_u64 (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 = - clib_host_to_net_u32 (random_u32 (&random_seed)); - - 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); - - sctp_conn->local_tag = init_ack_chunk->initiate_tag; - - vnet_buffer (b)->sctp.connection_index = - sctp_conn->sub_conn[idx].connection.c_index; - vnet_buffer (b)->sctp.subconn_idx = idx; -} - -/** - * Convert buffer to SHUTDOWN - */ -void -sctp_prepare_shutdown_chunk (sctp_connection_t * sctp_conn, u8 idx, - vlib_buffer_t * b) -{ - u16 alloc_bytes = sizeof (sctp_shutdown_association_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_shutdown_association_chunk_t *shutdown_chunk = - vlib_buffer_push_uninit (b, alloc_bytes); - - shutdown_chunk->sctp_hdr.checksum = 0; - /* No need of host_to_net conversion, already in net-byte order */ - shutdown_chunk->sctp_hdr.src_port = - sctp_conn->sub_conn[idx].connection.lcl_port; - shutdown_chunk->sctp_hdr.dst_port = - sctp_conn->sub_conn[idx].connection.rmt_port; - shutdown_chunk->sctp_hdr.verification_tag = sctp_conn->remote_tag; - vnet_sctp_set_chunk_type (&shutdown_chunk->chunk_hdr, SHUTDOWN); - vnet_sctp_set_chunk_length (&shutdown_chunk->chunk_hdr, chunk_len); - - shutdown_chunk->cumulative_tsn_ack = sctp_conn->last_rcvd_tsn; - - vnet_buffer (b)->sctp.connection_index = - sctp_conn->sub_conn[idx].connection.c_index; - vnet_buffer (b)->sctp.subconn_idx = idx; -} - -/* - * Send SHUTDOWN - */ -void -sctp_send_shutdown (sctp_connection_t * sctp_conn) -{ - vlib_buffer_t *b; - u32 bi; - sctp_main_t *tm = vnet_get_sctp_main (); - vlib_main_t *vm = vlib_get_main (); - - if (sctp_check_outstanding_data_chunks (sctp_conn) > 0) - return; - - if (PREDICT_FALSE (sctp_get_free_buffer_index (tm, &bi))) - return; - - u8 idx = SCTP_PRIMARY_PATH_IDX; - - b = vlib_get_buffer (vm, bi); - sctp_init_buffer (vm, b); - sctp_prepare_shutdown_chunk (sctp_conn, idx, b); - - sctp_enqueue_to_output_now (vm, b, bi, - sctp_conn->sub_conn[idx].connection.is_ip4); -} - -/** - * Convert buffer to SHUTDOWN_ACK - */ -void -sctp_prepare_shutdown_ack_chunk (sctp_connection_t * sctp_conn, u8 idx, - vlib_buffer_t * b) -{ - u16 alloc_bytes = sizeof (sctp_shutdown_association_chunk_t); - alloc_bytes += vnet_sctp_calculate_padding (alloc_bytes); - - u16 chunk_len = alloc_bytes - sizeof (sctp_header_t); - - sctp_shutdown_ack_chunk_t *shutdown_ack_chunk = - vlib_buffer_push_uninit (b, alloc_bytes); - - shutdown_ack_chunk->sctp_hdr.checksum = 0; - /* No need of host_to_net conversion, already in net-byte order */ - shutdown_ack_chunk->sctp_hdr.src_port = - sctp_conn->sub_conn[idx].connection.lcl_port; - shutdown_ack_chunk->sctp_hdr.dst_port = - sctp_conn->sub_conn[idx].connection.rmt_port; - shutdown_ack_chunk->sctp_hdr.verification_tag = sctp_conn->remote_tag; - - vnet_sctp_set_chunk_type (&shutdown_ack_chunk->chunk_hdr, SHUTDOWN_ACK); - vnet_sctp_set_chunk_length (&shutdown_ack_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; -} - -/* - * Send SHUTDOWN_ACK - */ -void -sctp_send_shutdown_ack (sctp_connection_t * sctp_conn, u8 idx, - vlib_buffer_t * b) -{ - vlib_main_t *vm = vlib_get_main (); - - if (sctp_check_outstanding_data_chunks (sctp_conn) > 0) - return; - - sctp_reuse_buffer (vm, b); - - sctp_prepare_shutdown_ack_chunk (sctp_conn, idx, b); -} - -/** - * Convert buffer to SACK - */ -void -sctp_prepare_sack_chunk (sctp_connection_t * sctp_conn, u8 idx, - vlib_buffer_t * b) -{ - vlib_main_t *vm = vlib_get_main (); - - sctp_reuse_buffer (vm, b); - - u16 alloc_bytes = sizeof (sctp_selective_ack_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_selective_ack_chunk_t *sack = vlib_buffer_push_uninit (b, alloc_bytes); - - sack->sctp_hdr.checksum = 0; - sack->sctp_hdr.src_port = sctp_conn->sub_conn[idx].connection.lcl_port; - sack->sctp_hdr.dst_port = sctp_conn->sub_conn[idx].connection.rmt_port; - sack->sctp_hdr.verification_tag = sctp_conn->remote_tag; - vnet_sctp_set_chunk_type (&sack->chunk_hdr, SACK); - vnet_sctp_set_chunk_length (&sack->chunk_hdr, chunk_len); - - sack->cumulative_tsn_ack = sctp_conn->next_tsn_expected; - - sctp_conn->ack_state = 0; - - vnet_buffer (b)->sctp.connection_index = - sctp_conn->sub_conn[idx].connection.c_index; - vnet_buffer (b)->sctp.subconn_idx = idx; -} - -/** - * Convert buffer to HEARTBEAT_ACK - */ -void -sctp_prepare_heartbeat_ack_chunk (sctp_connection_t * sctp_conn, u8 idx, - vlib_buffer_t * b) -{ - vlib_main_t *vm = vlib_get_main (); - - u16 alloc_bytes = sizeof (sctp_hb_ack_chunk_t); - - sctp_reuse_buffer (vm, b); - - /* 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_hb_ack_chunk_t *hb_ack = vlib_buffer_push_uninit (b, alloc_bytes); - - hb_ack->sctp_hdr.checksum = 0; - /* No need of host_to_net conversion, already in net-byte order */ - hb_ack->sctp_hdr.src_port = sctp_conn->sub_conn[idx].connection.lcl_port; - hb_ack->sctp_hdr.dst_port = sctp_conn->sub_conn[idx].connection.rmt_port; - hb_ack->sctp_hdr.verification_tag = sctp_conn->remote_tag; - hb_ack->hb_info.param_hdr.type = clib_host_to_net_u16 (1); - hb_ack->hb_info.param_hdr.length = - clib_host_to_net_u16 (sizeof (hb_ack->hb_info.hb_info)); - - vnet_sctp_set_chunk_type (&hb_ack->chunk_hdr, HEARTBEAT_ACK); - vnet_sctp_set_chunk_length (&hb_ack->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 HEARTBEAT - */ -void -sctp_prepare_heartbeat_chunk (sctp_connection_t * sctp_conn, u8 idx, - vlib_buffer_t * b) -{ - u16 alloc_bytes = sizeof (sctp_hb_req_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_hb_req_chunk_t *hb_req = vlib_buffer_push_uninit (b, alloc_bytes); - - hb_req->sctp_hdr.checksum = 0; - /* No need of host_to_net conversion, already in net-byte order */ - hb_req->sctp_hdr.src_port = sctp_conn->sub_conn[idx].connection.lcl_port; - hb_req->sctp_hdr.dst_port = sctp_conn->sub_conn[idx].connection.rmt_port; - hb_req->sctp_hdr.verification_tag = sctp_conn->remote_tag; - hb_req->hb_info.param_hdr.type = clib_host_to_net_u16 (1); - hb_req->hb_info.param_hdr.length = - clib_host_to_net_u16 (sizeof (hb_req->hb_info.hb_info)); - - vnet_sctp_set_chunk_type (&hb_req->chunk_hdr, HEARTBEAT); - vnet_sctp_set_chunk_length (&hb_req->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; -} - -void -sctp_send_heartbeat (sctp_connection_t * sctp_conn) -{ - vlib_buffer_t *b; - u32 bi; - sctp_main_t *tm = vnet_get_sctp_main (); - vlib_main_t *vm = vlib_get_main (); - - u8 i; - u64 now = sctp_time_now (); - - for (i = 0; i < MAX_SCTP_CONNECTIONS; i++) - { - if (sctp_conn->sub_conn[i].state == SCTP_SUBCONN_STATE_DOWN) - continue; - - if (now > (sctp_conn->sub_conn[i].last_seen + SCTP_HB_INTERVAL)) - { - if (PREDICT_FALSE (sctp_get_free_buffer_index (tm, &bi))) - return; - - b = vlib_get_buffer (vm, bi); - sctp_init_buffer (vm, b); - sctp_prepare_heartbeat_chunk (sctp_conn, i, b); - - sctp_enqueue_to_output_now (vm, b, bi, - sctp_conn->sub_conn[i]. - connection.is_ip4); - - sctp_conn->sub_conn[i].unacknowledged_hb += 1; - } - } -} - -/** - * Convert buffer to SHUTDOWN_COMPLETE - */ -void -sctp_prepare_shutdown_complete_chunk (sctp_connection_t * sctp_conn, u8 idx, - vlib_buffer_t * b) -{ - u16 alloc_bytes = sizeof (sctp_shutdown_association_chunk_t); - alloc_bytes += vnet_sctp_calculate_padding (alloc_bytes); - - u16 chunk_len = alloc_bytes - sizeof (sctp_header_t); - - sctp_shutdown_complete_chunk_t *shutdown_complete = - vlib_buffer_push_uninit (b, alloc_bytes); - - shutdown_complete->sctp_hdr.checksum = 0; - /* No need of host_to_net conversion, already in net-byte order */ - shutdown_complete->sctp_hdr.src_port = - sctp_conn->sub_conn[idx].connection.lcl_port; - shutdown_complete->sctp_hdr.dst_port = - sctp_conn->sub_conn[idx].connection.rmt_port; - shutdown_complete->sctp_hdr.verification_tag = sctp_conn->remote_tag; - - vnet_sctp_set_chunk_type (&shutdown_complete->chunk_hdr, SHUTDOWN_COMPLETE); - vnet_sctp_set_chunk_length (&shutdown_complete->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; -} - -void -sctp_send_shutdown_complete (sctp_connection_t * sctp_conn, u8 idx, - vlib_buffer_t * b0) -{ - vlib_main_t *vm = vlib_get_main (); - - if (sctp_check_outstanding_data_chunks (sctp_conn) > 0) - return; - - sctp_reuse_buffer (vm, b0); - - sctp_prepare_shutdown_complete_chunk (sctp_conn, idx, b0); -} - -/* - * Send INIT - */ -void -sctp_send_init (sctp_connection_t * sctp_conn) -{ - vlib_buffer_t *b; - u32 bi; - sctp_main_t *tm = vnet_get_sctp_main (); - vlib_main_t *vm = vlib_get_main (); - - if (PREDICT_FALSE (sctp_conn->init_retransmit_err > SCTP_MAX_INIT_RETRANS)) - { - clib_warning ("Reached MAX_INIT_RETRANS times. Aborting connection."); - - session_stream_connect_notify (&sctp_conn->sub_conn - [SCTP_PRIMARY_PATH_IDX].connection, 1); - - sctp_connection_timers_reset (sctp_conn); - - sctp_connection_cleanup (sctp_conn); - - return; - } - - if (PREDICT_FALSE (sctp_get_free_buffer_index (tm, &bi))) - return; - - b = vlib_get_buffer (vm, bi); - u8 idx = SCTP_PRIMARY_PATH_IDX; - - sctp_init_buffer (vm, b); - sctp_prepare_init_chunk (sctp_conn, idx, b); - - sctp_push_ip_hdr (tm, &sctp_conn->sub_conn[idx], b); - sctp_enqueue_to_ip_lookup (vm, b, bi, sctp_conn->sub_conn[idx].c_is_ip4, - sctp_conn->sub_conn[idx].c_fib_index); - - /* Start the T1_INIT timer */ - sctp_timer_set (sctp_conn, idx, SCTP_TIMER_T1_INIT, - sctp_conn->sub_conn[idx].RTO); - - /* Change state to COOKIE_WAIT */ - sctp_conn->state = SCTP_STATE_COOKIE_WAIT; - - /* Measure RTT with this */ - sctp_conn->sub_conn[idx].rtt_ts = sctp_time_now (); -} - -/** - * Push SCTP header and update connection variables - */ -static void -sctp_push_hdr_i (sctp_connection_t * sctp_conn, vlib_buffer_t * b, - sctp_state_t next_state) -{ - u16 data_len = b->current_length; - - if (b->flags & VLIB_BUFFER_TOTAL_LENGTH_VALID) - data_len += b->total_length_not_including_first_buffer; - - ASSERT (!b->total_length_not_including_first_buffer - || (b->flags & VLIB_BUFFER_NEXT_PRESENT) - || !(b->flags & VLIB_BUFFER_TOTAL_LENGTH_VALID)); - - SCTP_ADV_DBG_OUTPUT ("b->current_length = %u, " - "b->current_data = %p " - "data_len = %u", - b->current_length, b->current_data, data_len); - - u16 data_padding = vnet_sctp_calculate_padding (b->current_length); - if (data_padding > 0) - { - u8 *p_tail = vlib_buffer_put_uninit (b, data_padding); - clib_memset_u8 (p_tail, 0, data_padding); - } - - u16 bytes_to_add = sizeof (sctp_payload_data_chunk_t); - u16 chunk_length = data_len + bytes_to_add - sizeof (sctp_header_t); - - sctp_payload_data_chunk_t *data_chunk = - vlib_buffer_push_uninit (b, bytes_to_add); - - u8 idx = sctp_data_subconn_select (sctp_conn); - SCTP_DBG_OUTPUT - ("SCTP_CONN = %p, IDX = %u, S_INDEX = %u, C_INDEX = %u, sctp_conn->[...].LCL_PORT = %u, sctp_conn->[...].RMT_PORT = %u", - sctp_conn, idx, sctp_conn->sub_conn[idx].connection.s_index, - sctp_conn->sub_conn[idx].connection.c_index, - sctp_conn->sub_conn[idx].connection.lcl_port, - sctp_conn->sub_conn[idx].connection.rmt_port); - data_chunk->sctp_hdr.checksum = 0; - data_chunk->sctp_hdr.src_port = - sctp_conn->sub_conn[idx].connection.lcl_port; - data_chunk->sctp_hdr.dst_port = - sctp_conn->sub_conn[idx].connection.rmt_port; - data_chunk->sctp_hdr.verification_tag = sctp_conn->remote_tag; - - data_chunk->tsn = clib_host_to_net_u32 (sctp_conn->next_tsn); - data_chunk->stream_id = clib_host_to_net_u16 (0); - data_chunk->stream_seq = clib_host_to_net_u16 (0); - - vnet_sctp_set_chunk_type (&data_chunk->chunk_hdr, DATA); - vnet_sctp_set_chunk_length (&data_chunk->chunk_hdr, chunk_length); - - vnet_sctp_set_bbit (&data_chunk->chunk_hdr); - vnet_sctp_set_ebit (&data_chunk->chunk_hdr); - - SCTP_ADV_DBG_OUTPUT ("POINTER_WITH_DATA = %p, DATA_OFFSET = %u", - b->data, b->current_data); - - if (sctp_conn->sub_conn[idx].state != SCTP_SUBCONN_AWAITING_SACK) - { - sctp_conn->sub_conn[idx].state = SCTP_SUBCONN_AWAITING_SACK; - sctp_conn->last_unacked_tsn = sctp_conn->next_tsn; - } - - sctp_conn->next_tsn += data_len; - - u32 inflight = sctp_conn->next_tsn - sctp_conn->last_unacked_tsn; - /* Section 7.2.2; point (3) */ - if (sctp_conn->sub_conn[idx].partially_acked_bytes >= - sctp_conn->sub_conn[idx].cwnd - && inflight >= sctp_conn->sub_conn[idx].cwnd) - { - sctp_conn->sub_conn[idx].cwnd += sctp_conn->sub_conn[idx].PMTU; - sctp_conn->sub_conn[idx].partially_acked_bytes -= - sctp_conn->sub_conn[idx].cwnd; - } - - sctp_conn->sub_conn[idx].last_data_ts = sctp_time_now (); - - vnet_buffer (b)->sctp.connection_index = - sctp_conn->sub_conn[idx].connection.c_index; - - vnet_buffer (b)->sctp.subconn_idx = idx; -} - -u32 -sctp_push_header (transport_connection_t * trans_conn, vlib_buffer_t * b) -{ - sctp_connection_t *sctp_conn = - sctp_get_connection_from_transport (trans_conn); - - SCTP_DBG_OUTPUT ("TRANS_CONN = %p, SCTP_CONN = %p, " - "S_INDEX = %u, C_INDEX = %u," - "trans_conn->LCL_PORT = %u, trans_conn->RMT_PORT = %u", - trans_conn, - sctp_conn, - trans_conn->s_index, - trans_conn->c_index, - trans_conn->lcl_port, trans_conn->rmt_port); - - sctp_push_hdr_i (sctp_conn, b, SCTP_STATE_ESTABLISHED); - - sctp_trajectory_add_start (b, 3); - - return 0; -} - -u32 -sctp_prepare_data_retransmit (sctp_connection_t * sctp_conn, - u8 idx, - u32 offset, - u32 max_deq_bytes, vlib_buffer_t ** b) -{ - sctp_main_t *tm = vnet_get_sctp_main (); - vlib_main_t *vm = vlib_get_main (); - int n_bytes = 0; - u32 bi, available_bytes, seg_size; - u8 *data; - - ASSERT (sctp_conn->state >= SCTP_STATE_ESTABLISHED); - ASSERT (max_deq_bytes != 0); - - /* - * Make sure we can retransmit something - */ - available_bytes = - transport_max_tx_dequeue (&sctp_conn->sub_conn[idx].connection); - ASSERT (available_bytes >= offset); - available_bytes -= offset; - if (!available_bytes) - return 0; - max_deq_bytes = clib_min (sctp_conn->sub_conn[idx].cwnd, max_deq_bytes); - max_deq_bytes = clib_min (available_bytes, max_deq_bytes); - - seg_size = max_deq_bytes; - - /* - * Allocate and fill in buffer(s) - */ - - if (PREDICT_FALSE (sctp_get_free_buffer_index (tm, &bi))) - return 0; - *b = vlib_get_buffer (vm, bi); - data = sctp_init_buffer (vm, *b); - - /* Easy case, buffer size greater than mss */ - if (PREDICT_TRUE (seg_size <= tm->bytes_per_buffer)) - { - n_bytes = - session_tx_fifo_peek_bytes (&sctp_conn->sub_conn[idx].connection, - data, offset, max_deq_bytes); - ASSERT (n_bytes == max_deq_bytes); - b[0]->current_length = n_bytes; - sctp_push_hdr_i (sctp_conn, *b, sctp_conn->state); - } - - return n_bytes; -} - -void -sctp_data_retransmit (sctp_connection_t * sctp_conn) -{ - vlib_main_t *vm = vlib_get_main (); - vlib_buffer_t *b = 0; - u32 bi, n_bytes = 0; - - u8 idx = sctp_data_subconn_select (sctp_conn); - - SCTP_DBG_OUTPUT - ("SCTP_CONN = %p, IDX = %u, S_INDEX = %u, C_INDEX = %u, sctp_conn->[...].LCL_PORT = %u, sctp_conn->[...].RMT_PORT = %u", - sctp_conn, idx, sctp_conn->sub_conn[idx].connection.s_index, - sctp_conn->sub_conn[idx].connection.c_index, - sctp_conn->sub_conn[idx].connection.lcl_port, - sctp_conn->sub_conn[idx].connection.rmt_port); - - if (sctp_conn->state >= SCTP_STATE_ESTABLISHED) - { - return; - } - - n_bytes = - sctp_prepare_data_retransmit (sctp_conn, idx, 0, - sctp_conn->sub_conn[idx].cwnd, &b); - if (n_bytes > 0) - SCTP_DBG_OUTPUT ("We have data (%u bytes) to retransmit", n_bytes); - - bi = vlib_get_buffer_index (vm, b); - - sctp_enqueue_to_output_now (vm, b, bi, - sctp_conn->sub_conn[idx].connection.is_ip4); - - return; -} - -#if SCTP_DEBUG_STATE_MACHINE -always_inline u8 -sctp_validate_output_state_machine (sctp_connection_t * sctp_conn, - u8 chunk_type) -{ - u8 result = 0; - switch (sctp_conn->state) - { - case SCTP_STATE_CLOSED: - if (chunk_type != INIT && chunk_type != INIT_ACK) - result = 1; - break; - case SCTP_STATE_ESTABLISHED: - if (chunk_type != DATA && chunk_type != HEARTBEAT && - chunk_type != HEARTBEAT_ACK && chunk_type != SACK && - chunk_type != COOKIE_ACK && chunk_type != SHUTDOWN) - result = 1; - break; - case SCTP_STATE_COOKIE_WAIT: - if (chunk_type != COOKIE_ECHO) - result = 1; - break; - case SCTP_STATE_SHUTDOWN_SENT: - if (chunk_type != SHUTDOWN_COMPLETE) - result = 1; - break; - case SCTP_STATE_SHUTDOWN_RECEIVED: - if (chunk_type != SHUTDOWN_ACK) - result = 1; - break; - } - return result; -} -#endif - -/* - * fd.io coding-style-patch-verification: ON - * - * Local Variables: - * eval: (c-set-style "gnu") - * End: - */ |