diff options
author | Marco Varlese <marco.varlese@suse.com> | 2017-10-30 18:17:21 +0100 |
---|---|---|
committer | Florin Coras <florin.coras@gmail.com> | 2018-01-24 15:45:35 +0000 |
commit | 191a59401c0552e5ea79041f34456eb9fcc1f311 (patch) | |
tree | 286d871aee72cb37e6d751715e89c6b178c7b527 /src/vnet/sctp/sctp.c | |
parent | ce111d2ee3bacec2a09d8f5b664dcfafa0dd50c7 (diff) |
SCTP stack (RFC4960)
== CONTENT ==
* SCTP chunks definition as per RFC4960;
* Helper functions to set/get values to/from the corresponding chunks;
* Hooks to the session/application layers;
* Complete state-machine handling;
* Implementation for unexpected chunk received in a certain
state (state-machine error handling)
* Support for 1-single connection;
* Sample application to test receive/transmit data-path;
* Test to validate SCTP stack;
Change-Id: I1b55c455ab400be9513f4e094dadfc3181d2ebc9
Signed-off-by: Marco Varlese <marco.varlese@suse.com>
Diffstat (limited to 'src/vnet/sctp/sctp.c')
-rw-r--r-- | src/vnet/sctp/sctp.c | 848 |
1 files changed, 848 insertions, 0 deletions
diff --git a/src/vnet/sctp/sctp.c b/src/vnet/sctp/sctp.c new file mode 100644 index 00000000000..2e37a91dbac --- /dev/null +++ b/src/vnet/sctp/sctp.c @@ -0,0 +1,848 @@ +/* + * 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> + +sctp_main_t sctp_main; + +static u32 +sctp_connection_bind (u32 session_index, transport_endpoint_t * tep) +{ + sctp_main_t *tm = &sctp_main; + sctp_connection_t *listener; + void *iface_ip; + + pool_get (tm->listener_pool, listener); + memset (listener, 0, sizeof (*listener)); + + listener->sub_conn[MAIN_SCTP_SUB_CONN_IDX].parent = listener; + listener->sub_conn[MAIN_SCTP_SUB_CONN_IDX].c_c_index = + listener - tm->listener_pool; + listener->sub_conn[MAIN_SCTP_SUB_CONN_IDX].connection.lcl_port = tep->port; + + /* If we are provided a sw_if_index, bind using one of its IPs */ + if (ip_is_zero (&tep->ip, 1) && tep->sw_if_index != ENDPOINT_INVALID_INDEX) + { + if ((iface_ip = ip_interface_get_first_ip (tep->sw_if_index, + tep->is_ip4))) + ip_set (&tep->ip, iface_ip, tep->is_ip4); + } + ip_copy (&listener->sub_conn[MAIN_SCTP_SUB_CONN_IDX].connection.lcl_ip, + &tep->ip, tep->is_ip4); + + listener->sub_conn[MAIN_SCTP_SUB_CONN_IDX].connection.is_ip4 = tep->is_ip4; + listener->sub_conn[MAIN_SCTP_SUB_CONN_IDX].connection.proto = + TRANSPORT_PROTO_SCTP; + listener->sub_conn[MAIN_SCTP_SUB_CONN_IDX].c_s_index = session_index; + listener->sub_conn[MAIN_SCTP_SUB_CONN_IDX].connection.fib_index = + tep->fib_index; + listener->state = SCTP_STATE_CLOSED; + + sctp_connection_timers_init (listener); + + return listener->sub_conn[MAIN_SCTP_SUB_CONN_IDX].c_c_index; +} + +u32 +sctp_session_bind (u32 session_index, transport_endpoint_t * tep) +{ + return sctp_connection_bind (session_index, tep); +} + +static void +sctp_connection_unbind (u32 listener_index) +{ + sctp_main_t *tm = vnet_get_sctp_main (); + sctp_connection_t *tc; + + tc = pool_elt_at_index (tm->listener_pool, listener_index); + + /* Poison the entry */ + if (CLIB_DEBUG > 0) + memset (tc, 0xFA, sizeof (*tc)); + + pool_put_index (tm->listener_pool, listener_index); +} + +u32 +sctp_session_unbind (u32 listener_index) +{ + sctp_connection_unbind (listener_index); + return 0; +} + +void +sctp_punt_unknown (vlib_main_t * vm, u8 is_ip4, u8 is_add) +{ + sctp_main_t *tm = &sctp_main; + if (is_ip4) + tm->punt_unknown4 = is_add; + else + tm->punt_unknown6 = is_add; +} + +static int +sctp_alloc_custom_local_endpoint (sctp_main_t * tm, ip46_address_t * lcl_addr, + u16 * lcl_port, u8 is_ip4) +{ + int index, port; + if (is_ip4) + { + index = tm->last_v4_address_rotor++; + if (tm->last_v4_address_rotor >= vec_len (tm->ip4_src_addresses)) + tm->last_v4_address_rotor = 0; + lcl_addr->ip4.as_u32 = tm->ip4_src_addresses[index].as_u32; + } + else + { + index = tm->last_v6_address_rotor++; + if (tm->last_v6_address_rotor >= vec_len (tm->ip6_src_addresses)) + tm->last_v6_address_rotor = 0; + clib_memcpy (&lcl_addr->ip6, &tm->ip6_src_addresses[index], + sizeof (ip6_address_t)); + } + port = transport_alloc_local_port (TRANSPORT_PROTO_SCTP, lcl_addr); + if (port < 1) + { + clib_warning ("Failed to allocate src port"); + return -1; + } + *lcl_port = port; + return 0; +} + +/** + * Initialize all connection timers as invalid + */ +void +sctp_connection_timers_init (sctp_connection_t * tc) +{ + int i, j; + + /* Set all to invalid */ + for (i = 0; i < MAX_SCTP_CONNECTIONS; i++) + for (j = 0; j < SCTP_N_TIMERS; j++) + { + tc->sub_conn[i].timers[j] = SCTP_TIMER_HANDLE_INVALID; + } + + tc->rto = SCTP_RTO_INIT; +} + +/** + * Stop all connection timers + */ +void +sctp_connection_timers_reset (sctp_connection_t * tc) +{ + int i, j; + for (i = 0; i < MAX_SCTP_CONNECTIONS; i++) + { + for (j = 0; j < SCTP_N_TIMERS; j++) + sctp_timer_reset (tc, i, j); + } +} + +const char *sctp_fsm_states[] = { +#define _(sym, str) str, + foreach_sctp_fsm_state +#undef _ +}; + +u8 * +format_sctp_state (u8 * s, va_list * args) +{ + u32 state = va_arg (*args, u32); + + if (state < SCTP_N_STATES) + s = format (s, "%s", sctp_fsm_states[state]); + else + s = format (s, "UNKNOWN (%d (0x%x))", state, state); + return s; +} + +u8 * +format_sctp_connection_id (u8 * s, va_list * args) +{ + /* + sctp_connection_t *tc = va_arg (*args, sctp_connection_t *); + if (!tc) + return s; + if (tc->c_is_ip4) + { + s = format (s, "[#%d][%s] %U:%d->%U:%d", tc->c_thread_index, "T", + format_ip4_address, &tc->c_lcl_ip4, + clib_net_to_host_u16 (tc->c_lcl_port), format_ip4_address, + &tc->c_rmt_ip4, clib_net_to_host_u16 (tc->c_rmt_port)); + } + else + { + s = format (s, "[#%d][%s] %U:%d->%U:%d", tc->c_thread_index, "T", + format_ip6_address, &tc->c_lcl_ip6, + clib_net_to_host_u16 (tc->c_lcl_port), format_ip6_address, + &tc->c_rmt_ip6, clib_net_to_host_u16 (tc->c_rmt_port)); + } + */ + return s; +} + +u8 * +format_sctp_connection (u8 * s, va_list * args) +{ + sctp_connection_t *tc = va_arg (*args, sctp_connection_t *); + u32 verbose = va_arg (*args, u32); + + if (!tc) + return s; + s = format (s, "%-50U", format_sctp_connection_id, tc); + if (verbose) + { + s = format (s, "%-15U", format_sctp_state, tc->state); + } + + return s; +} + +/** + * Initialize connection send variables. + */ +void +sctp_init_snd_vars (sctp_connection_t * tc) +{ + u32 time_now; + + /* + * We use the time to randomize iss and for setting up the initial + * timestamp. Make sure it's updated otherwise syn and ack in the + * handshake may make it look as if time has flown in the opposite + * direction for us. + */ + sctp_set_time_now (vlib_get_thread_index ()); + time_now = sctp_time_now (); + + tc->iss = random_u32 (&time_now); + tc->snd_una = tc->iss; + tc->snd_nxt = tc->iss + 1; + tc->snd_una_max = tc->snd_nxt; +} + +/** + * Update max segment size we're able to process. + * + * The value is constrained by our interface's MTU and IP options. It is + * also what we advertise to our peer. + */ +void +sctp_update_rcv_mss (sctp_connection_t * tc) +{ + /* TODO find our iface MTU */ + tc->a_rwnd = DEFAULT_A_RWND - sizeof (sctp_full_hdr_t); + tc->rcv_opts.a_rwnd = tc->a_rwnd; + tc->rcv_a_rwnd = tc->a_rwnd; /* This will be updated by our congestion algos */ +} + +void +sctp_init_mss (sctp_connection_t * tc) +{ + SCTP_DBG ("CONN_INDEX = %u", + tc->sub_conn[MAIN_SCTP_SUB_CONN_IDX].connection.c_index); + + u16 default_a_rwnd = 536; + sctp_update_rcv_mss (tc); + + /* TODO cache mss and consider PMTU discovery */ + tc->snd_a_rwnd = clib_min (tc->rcv_opts.a_rwnd, tc->a_rwnd); + + if (tc->snd_a_rwnd < sizeof (sctp_full_hdr_t)) + { + SCTP_ADV_DBG ("tc->snd_a_rwnd < sizeof(sctp_full_hdr_t)"); + /* Assume that at least the min default mss works */ + tc->snd_a_rwnd = default_a_rwnd; + tc->rcv_opts.a_rwnd = default_a_rwnd; + } + + ASSERT (tc->snd_a_rwnd > sizeof (sctp_full_hdr_t)); +} + +/** Initialize sctp connection variables + * + * Should be called after having received a msg from the peer, i.e., a SYN or + * a SYNACK, such that connection options have already been exchanged. */ +void +sctp_connection_init_vars (sctp_connection_t * tc) +{ + sctp_init_mss (tc); + sctp_init_snd_vars (tc); +} + +always_inline sctp_connection_t * +sctp_sub_connection_add (u8 thread_index) +{ + sctp_main_t *tm = vnet_get_sctp_main (); + sctp_connection_t *tc = tm->connections[thread_index]; + + tc->sub_conn[tc->next_avail_sub_conn].connection.c_index = + tc->sub_conn[MAIN_SCTP_SUB_CONN_IDX].connection.c_index; + tc->sub_conn[tc->next_avail_sub_conn].connection.thread_index = + thread_index; + tc->sub_conn[tc->next_avail_sub_conn].parent = tc; + + tc->next_avail_sub_conn += 1; + + return tc; +} + +void +sctp_sub_connection_add_ip4 (u8 thread_index, + sctp_ipv4_addr_param_t * ipv4_addr) +{ + sctp_connection_t *tc = sctp_sub_connection_add (thread_index); + + clib_memcpy (&tc->sub_conn[tc->next_avail_sub_conn].connection.lcl_ip.ip4, + &ipv4_addr->address, sizeof (ipv4_addr->address)); +} + +void +sctp_sub_connection_add_ip6 (u8 thread_index, + sctp_ipv6_addr_param_t * ipv6_addr) +{ + sctp_connection_t *tc = sctp_sub_connection_add (thread_index); + + clib_memcpy (&tc->sub_conn[tc->next_avail_sub_conn].connection.lcl_ip.ip6, + &ipv6_addr->address, sizeof (ipv6_addr->address)); +} + +sctp_connection_t * +sctp_connection_new (u8 thread_index) +{ + sctp_main_t *tm = vnet_get_sctp_main (); + sctp_connection_t *tc; + + pool_get (tm->connections[thread_index], tc); + memset (tc, 0, sizeof (*tc)); + tc->sub_conn[MAIN_SCTP_SUB_CONN_IDX].parent = tc; + tc->sub_conn[MAIN_SCTP_SUB_CONN_IDX].c_c_index = + tc - tm->connections[thread_index]; + tc->sub_conn[MAIN_SCTP_SUB_CONN_IDX].c_thread_index = thread_index; + tc->local_tag = 0; + tc->next_avail_sub_conn = 1; + + return tc; +} + +sctp_connection_t * +sctp_half_open_connection_new (u8 thread_index) +{ + sctp_main_t *tm = vnet_get_sctp_main (); + sctp_connection_t *tc = 0; + ASSERT (vlib_get_thread_index () == 0); + pool_get (tm->half_open_connections, tc); + memset (tc, 0, sizeof (*tc)); + tc->sub_conn[MAIN_SCTP_SUB_CONN_IDX].c_c_index = + tc - tm->half_open_connections; + tc->sub_conn[MAIN_SCTP_SUB_CONN_IDX].parent = tc; + return tc; +} + +static inline int +sctp_connection_open (transport_endpoint_t * rmt) +{ + sctp_main_t *tm = vnet_get_sctp_main (); + sctp_connection_t *tc; + ip46_address_t lcl_addr; + u16 lcl_port; + uword thread_id; + int rv; + + u8 idx = sctp_pick_conn_idx_on_state (SCTP_STATE_CLOSED); + + /* + * Allocate local endpoint + */ + if ((rmt->is_ip4 && vec_len (tm->ip4_src_addresses)) + || (!rmt->is_ip4 && vec_len (tm->ip6_src_addresses))) + rv = sctp_alloc_custom_local_endpoint (tm, &lcl_addr, &lcl_port, + rmt->is_ip4); + else + rv = transport_alloc_local_endpoint (TRANSPORT_PROTO_SCTP, + rmt, &lcl_addr, &lcl_port); + + if (rv) + return -1; + + /* + * Create connection and send INIT CHUNK + */ + thread_id = vlib_get_thread_index (); + ASSERT (thread_id == 0); + + clib_spinlock_lock_if_init (&tm->half_open_lock); + tc = sctp_half_open_connection_new (thread_id); + + transport_connection_t *t_conn = &tc->sub_conn[idx].connection; + ip_copy (&t_conn->rmt_ip, &rmt->ip, rmt->is_ip4); + ip_copy (&t_conn->lcl_ip, &lcl_addr, rmt->is_ip4); + tc->sub_conn[idx].parent = tc; + t_conn->rmt_port = rmt->port; + t_conn->lcl_port = clib_host_to_net_u16 (lcl_port); + t_conn->is_ip4 = rmt->is_ip4; + t_conn->proto = TRANSPORT_PROTO_SCTP; + t_conn->fib_index = rmt->fib_index; + + sctp_connection_timers_init (tc); + /* The other connection vars will be initialized after INIT_ACK chunk received */ + sctp_init_snd_vars (tc); + + sctp_send_init (tc); + + clib_spinlock_unlock_if_init (&tm->half_open_lock); + + return tc->sub_conn[idx].connection.c_index; +} + +/** + * Cleans up connection state. + * + * No notifications. + */ +void +sctp_connection_cleanup (sctp_connection_t * tc) +{ + sctp_main_t *tm = &sctp_main; + u8 i; + + /* Cleanup local endpoint if this was an active connect */ + for (i = 0; i < MAX_SCTP_CONNECTIONS; i++) + transport_endpoint_cleanup (TRANSPORT_PROTO_SCTP, + &tc->sub_conn[i].connection.lcl_ip, + tc->sub_conn[i].connection.lcl_port); + + /* Check if connection is not yet fully established */ + if (tc->state == SCTP_STATE_COOKIE_WAIT) + { + + } + else + { + int thread_index = + tc->sub_conn[MAIN_SCTP_SUB_CONN_IDX].connection.thread_index; + + /* Make sure all timers are cleared */ + sctp_connection_timers_reset (tc); + + /* Poison the entry */ + if (CLIB_DEBUG > 0) + memset (tc, 0xFA, sizeof (*tc)); + pool_put (tm->connections[thread_index], tc); + } +} + +int +sctp_session_open (transport_endpoint_t * tep) +{ + return sctp_connection_open (tep); +} + +u16 +sctp_check_outstanding_data_chunks (sctp_connection_t * tc) +{ + return 0; /* Indicates no more data to be read/sent */ +} + +void +sctp_connection_close (sctp_connection_t * tc) +{ + SCTP_DBG ("Closing connection %u...", + tc->sub_conn[MAIN_SCTP_SUB_CONN_IDX].connection.c_index); + + tc->state = SCTP_STATE_SHUTDOWN_PENDING; + + sctp_send_shutdown (tc); +} + +void +sctp_session_close (u32 conn_index, u32 thread_index) +{ + ASSERT (thread_index == 0); + + sctp_connection_t *tc; + tc = sctp_connection_get (conn_index, thread_index); + sctp_connection_close (tc); +} + +void +sctp_session_cleanup (u32 conn_index, u32 thread_index) +{ + sctp_connection_t *tc; + tc = sctp_connection_get (conn_index, thread_index); + sctp_connection_timers_reset (tc); + + /* Wait for the session tx events to clear */ + tc->state = SCTP_STATE_CLOSED; +} + +/** + * Update snd_mss to reflect the effective segment size that we can send + */ +void +sctp_update_snd_mss (sctp_connection_t * tc) +{ + /* The overhead for the sctp_header_t and sctp_chunks_common_hdr_t + * (the sum equals to sctp_full_hdr_t) is already taken into account + * for the tc->a_rwnd computation. + * So let's not account it again here. + */ + tc->snd_hdr_length = + sizeof (sctp_payload_data_chunk_t) - sizeof (sctp_full_hdr_t); + tc->snd_a_rwnd = + clib_min (tc->a_rwnd, tc->rcv_opts.a_rwnd) - tc->snd_hdr_length; + + SCTP_DBG ("tc->snd_a_rwnd = %u, tc->snd_hdr_length = %u ", + tc->snd_a_rwnd, tc->snd_hdr_length); + + ASSERT (tc->snd_a_rwnd > 0); +} + +u16 +sctp_session_send_mss (transport_connection_t * trans_conn) +{ + SCTP_DBG ("CONN_INDEX: %u", trans_conn->c_index); + + sctp_connection_t *tc = sctp_get_connection_from_transport (trans_conn); + + if (trans_conn == NULL) + { + SCTP_DBG ("trans_conn == NULL"); + return 0; + } + + if (tc == NULL) + { + SCTP_DBG ("tc == NULL"); + return 0; + } + /* Ensure snd_mss does accurately reflect the amount of data we can push + * in a segment. This also makes sure that options are updated according to + * the current state of the connection. */ + sctp_update_snd_mss (tc); + + return tc->snd_a_rwnd; +} + +u16 +sctp_snd_space (sctp_connection_t * sctp_conn) +{ + /* TODO: This requires a real implementation */ + if (sctp_conn == NULL) + { + SCTP_DBG ("sctp_conn == NULL"); + return 0; + } + + if (sctp_conn->state != SCTP_STATE_ESTABLISHED) + { + SCTP_DBG_STATE_MACHINE + ("Trying to send DATA while not in SCTP_STATE_ESTABLISHED"); + return 0; + } + + return sctp_conn->snd_a_rwnd; +} + +u32 +sctp_session_send_space (transport_connection_t * trans_conn) +{ + SCTP_DBG ("CONN_INDEX: %u", trans_conn->c_index); + + sctp_connection_t *tc = sctp_get_connection_from_transport (trans_conn); + + return sctp_snd_space (tc); +} + +u32 +sctp_session_tx_fifo_offset (transport_connection_t * trans_conn) +{ + SCTP_DBG ("CONN_INDEX: %u", trans_conn->c_index); + + sctp_connection_t *tc = sctp_get_connection_from_transport (trans_conn); + + if (tc == NULL) + { + SCTP_DBG ("tc == NULL"); + return 0; + } + + /* This still works if fast retransmit is on */ + return (tc->snd_nxt - tc->snd_una); +} + +transport_connection_t * +sctp_session_get_transport (u32 conn_index, u32 thread_index) +{ + sctp_connection_t *tc = sctp_connection_get (conn_index, thread_index); + return &tc->sub_conn[MAIN_SCTP_SUB_CONN_IDX].connection; +} + +transport_connection_t * +sctp_session_get_listener (u32 listener_index) +{ + sctp_main_t *tm = vnet_get_sctp_main (); + sctp_connection_t *tc; + tc = pool_elt_at_index (tm->listener_pool, listener_index); + return &tc->sub_conn[MAIN_SCTP_SUB_CONN_IDX].connection; +} + +u8 * +format_sctp_session (u8 * s, va_list * args) +{ + return NULL; +} + +u8 * +format_sctp_listener_session (u8 * s, va_list * args) +{ + return NULL; +} + +void +sctp_timer_init_handler (u32 conn_index) +{ + sctp_connection_t *tc; + + tc = sctp_connection_get (conn_index, vlib_get_thread_index ()); + /* note: the connection may have already disappeared */ + if (PREDICT_FALSE (tc == 0)) + return; + ASSERT (tc->state == SCTP_STATE_COOKIE_ECHOED); + /* Start cleanup. App wasn't notified yet so use delete notify as + * opposed to delete to cleanup session layer state. */ + stream_session_delete_notify (&tc-> + sub_conn[MAIN_SCTP_SUB_CONN_IDX].connection); + tc->sub_conn[MAIN_SCTP_SUB_CONN_IDX].timers[SCTP_TIMER_T1_INIT] = + SCTP_TIMER_HANDLE_INVALID; + + sctp_connection_cleanup (tc); +} + +/* *INDENT OFF* */ +static timer_expiration_handler *sctp_timer_expiration_handlers[SCTP_N_TIMERS] + = { + sctp_timer_init_handler +}; + +/* *INDENT ON* */ + +static void +sctp_expired_timers_dispatch (u32 * expired_timers) +{ + int i; + u32 connection_index, timer_id; + + for (i = 0; i < vec_len (expired_timers); i++) + { + /* Get session index and timer id */ + connection_index = expired_timers[i] & 0x0FFFFFFF; + timer_id = expired_timers[i] >> 28; + + /* Handle expiration */ + (*sctp_timer_expiration_handlers[timer_id]) (connection_index); + } +} + +void +sctp_initialize_timer_wheels (sctp_main_t * tm) +{ + tw_timer_wheel_16t_2w_512sl_t *tw; + /* *INDENT-OFF* */ + foreach_vlib_main (({ + tw = &tm->timer_wheels[ii]; + tw_timer_wheel_init_16t_2w_512sl (tw, sctp_expired_timers_dispatch, + 100e-3 /* timer period 100ms */ , ~0); + tw->last_run_time = vlib_time_now (this_vlib_main); + })); + /* *INDENT-ON* */ +} + +clib_error_t * +sctp_main_enable (vlib_main_t * vm) +{ + sctp_main_t *tm = vnet_get_sctp_main (); + vlib_thread_main_t *vtm = vlib_get_thread_main (); + clib_error_t *error = 0; + u32 num_threads; + int thread; + sctp_connection_t *tc __attribute__ ((unused)); + u32 preallocated_connections_per_thread; + + if ((error = vlib_call_init_function (vm, ip_main_init))) + return error; + if ((error = vlib_call_init_function (vm, ip4_lookup_init))) + return error; + if ((error = vlib_call_init_function (vm, ip6_lookup_init))) + return error; + + /* + * Registrations + */ + + ip4_register_protocol (IP_PROTOCOL_SCTP, sctp4_input_node.index); + ip6_register_protocol (IP_PROTOCOL_SCTP, sctp6_input_node.index); + + /* + * Initialize data structures + */ + + num_threads = 1 /* main thread */ + vtm->n_threads; + vec_validate (tm->connections, num_threads - 1); + + /* + * Preallocate connections. Assume that thread 0 won't + * use preallocated threads when running multi-core + */ + if (num_threads == 1) + { + thread = 0; + preallocated_connections_per_thread = tm->preallocated_connections; + } + else + { + thread = 1; + preallocated_connections_per_thread = + tm->preallocated_connections / (num_threads - 1); + } + for (; thread < num_threads; thread++) + { + if (preallocated_connections_per_thread) + pool_init_fixed (tm->connections[thread], + preallocated_connections_per_thread); + } + + /* Initialize per worker thread tx buffers (used for control messages) */ + vec_validate (tm->tx_buffers, num_threads - 1); + + /* Initialize timer wheels */ + vec_validate (tm->timer_wheels, num_threads - 1); + sctp_initialize_timer_wheels (tm); + + /* Initialize clocks per tick for SCTP timestamp. Used to compute + * monotonically increasing timestamps. */ + tm->tstamp_ticks_per_clock = vm->clib_time.seconds_per_clock + / SCTP_TSTAMP_RESOLUTION; + + if (num_threads > 1) + { + } + + vec_validate (tm->tx_frames[0], num_threads - 1); + vec_validate (tm->tx_frames[1], num_threads - 1); + vec_validate (tm->ip_lookup_tx_frames[0], num_threads - 1); + vec_validate (tm->ip_lookup_tx_frames[1], num_threads - 1); + + tm->bytes_per_buffer = vlib_buffer_free_list_buffer_size + (vm, VLIB_BUFFER_DEFAULT_FREE_LIST_INDEX); + + vec_validate (tm->time_now, num_threads - 1); + return error; +} + +clib_error_t * +sctp_enable_disable (vlib_main_t * vm, u8 is_en) +{ + if (is_en) + { + if (sctp_main.is_enabled) + return 0; + + return sctp_main_enable (vm); + } + else + { + sctp_main.is_enabled = 0; + } + + return 0; +} + +transport_connection_t * +sctp_half_open_session_get_transport (u32 conn_index) +{ + sctp_connection_t *tc = sctp_half_open_connection_get (conn_index); + return &tc->sub_conn[MAIN_SCTP_SUB_CONN_IDX].connection; +} + +u8 * +format_sctp_half_open (u8 * s, va_list * args) +{ + u32 tci = va_arg (*args, u32); + sctp_connection_t *tc = sctp_half_open_connection_get (tci); + return format (s, "%U", format_sctp_connection_id, tc); +} + +/* *INDENT OFF* */ +const static transport_proto_vft_t sctp_proto = { + .enable = sctp_enable_disable, + .bind = sctp_session_bind, + .unbind = sctp_session_unbind, + .open = sctp_session_open, + .close = sctp_session_close, + .cleanup = sctp_session_cleanup, + .push_header = sctp_push_header, + .send_mss = sctp_session_send_mss, + .send_space = sctp_session_send_space, + .tx_fifo_offset = NULL, //sctp_session_tx_fifo_offset, + .get_connection = sctp_session_get_transport, + .get_listener = sctp_session_get_listener, + .get_half_open = sctp_half_open_session_get_transport, + .format_connection = format_sctp_session, + .format_listener = format_sctp_listener_session, + .format_half_open = format_sctp_half_open, +}; + +/* *INDENT ON* */ + +clib_error_t * +sctp_init (vlib_main_t * vm) +{ + sctp_main_t *tm = vnet_get_sctp_main (); + ip_main_t *im = &ip_main; + ip_protocol_info_t *pi; + /* Session layer, and by implication SCTP, are disabled by default */ + tm->is_enabled = 0; + + /* Register with IP for header parsing */ + pi = ip_get_protocol_info (im, IP_PROTOCOL_SCTP); + if (pi == 0) + return clib_error_return (0, "SCTP protocol info AWOL"); + pi->format_header = format_sctp_header; + pi->unformat_pg_edit = unformat_pg_sctp_header; + + /* Register as transport with session layer */ + transport_register_protocol (TRANSPORT_PROTO_SCTP, &sctp_proto, + FIB_PROTOCOL_IP4, sctp4_output_node.index); + transport_register_protocol (TRANSPORT_PROTO_SCTP, &sctp_proto, + FIB_PROTOCOL_IP6, sctp6_output_node.index); + + return 0; +} + +VLIB_INIT_FUNCTION (sctp_init); + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ |