/* * 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 #include 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[SCTP_PRIMARY_PATH_IDX].subconn_idx = SCTP_PRIMARY_PATH_IDX; listener->sub_conn[SCTP_PRIMARY_PATH_IDX].c_c_index = listener - tm->listener_pool; listener->sub_conn[SCTP_PRIMARY_PATH_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[SCTP_PRIMARY_PATH_IDX].connection.lcl_ip, &tep->ip, tep->is_ip4); listener->sub_conn[SCTP_PRIMARY_PATH_IDX].PMTU = vnet_sw_interface_get_mtu (vnet_get_main (), tep->sw_if_index, VLIB_TX); listener->sub_conn[SCTP_PRIMARY_PATH_IDX].connection.is_ip4 = tep->is_ip4; listener->sub_conn[SCTP_PRIMARY_PATH_IDX].connection.proto = TRANSPORT_PROTO_SCTP; listener->sub_conn[SCTP_PRIMARY_PATH_IDX].c_s_index = session_index; listener->sub_conn[SCTP_PRIMARY_PATH_IDX].connection.fib_index = tep->fib_index; listener->state = SCTP_STATE_CLOSED; sctp_connection_timers_init (listener); return listener->sub_conn[SCTP_PRIMARY_PATH_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 *sctp_conn; sctp_conn = pool_elt_at_index (tm->listener_pool, listener_index); /* Poison the entry */ if (CLIB_DEBUG > 0) memset (sctp_conn, 0xFA, sizeof (*sctp_conn)); 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 * sctp_conn) { int i, j; /* Set all to invalid */ for (i = 0; i < MAX_SCTP_CONNECTIONS; i++) { sctp_conn->sub_conn[i].RTO = SCTP_RTO_INIT; for (j = 0; j < SCTP_N_TIMERS; j++) { sctp_conn->sub_conn[i].timers[j] = SCTP_TIMER_HANDLE_INVALID; } } } /** * Stop all connection timers */ void sctp_connection_timers_reset (sctp_connection_t * sctp_conn) { int i, j; for (i = 0; i < MAX_SCTP_CONNECTIONS; i++) { for (j = 0; j < SCTP_N_TIMERS; j++) sctp_timer_reset (sctp_conn, 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 *sctp_conn = va_arg (*args, sctp_connection_t *); if (!sctp_conn) return s; u8 i; for (i = 0; i < MAX_SCTP_CONNECTIONS; i++) { if (sctp_conn->sub_conn[i].connection.is_ip4) { s = format (s, "%U[#%d][%s] %U:%d->%U:%d", s, sctp_conn->sub_conn[i].connection.thread_index, "T", format_ip4_address, &sctp_conn->sub_conn[i].connection.lcl_ip.ip4, clib_net_to_host_u16 (sctp_conn->sub_conn[i]. connection.lcl_port), format_ip4_address, &sctp_conn->sub_conn[i].connection.rmt_ip.ip4, clib_net_to_host_u16 (sctp_conn->sub_conn[i]. connection.rmt_port)); } else { s = format (s, "%U[#%d][%s] %U:%d->%U:%d", s, sctp_conn->sub_conn[i].connection.thread_index, "T", format_ip6_address, &sctp_conn->sub_conn[i].connection.lcl_ip.ip6, clib_net_to_host_u16 (sctp_conn->sub_conn[i]. connection.lcl_port), format_ip6_address, &sctp_conn->sub_conn[i].connection.rmt_ip.ip6, clib_net_to_host_u16 (sctp_conn->sub_conn[i]. connection.rmt_port)); } } return s; } u8 * format_sctp_connection (u8 * s, va_list * args) { sctp_connection_t *sctp_conn = va_arg (*args, sctp_connection_t *); u32 verbose = va_arg (*args, u32); if (!sctp_conn) return s; s = format (s, "%-50U", format_sctp_connection_id, sctp_conn); if (verbose) { s = format (s, "%-15U", format_sctp_state, sctp_conn->state); } return s; } /** * Initialize connection send variables. */ void sctp_init_snd_vars (sctp_connection_t * sctp_conn) { 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 (); sctp_conn->local_initial_tsn = random_u32 (&time_now); sctp_conn->last_unacked_tsn = sctp_conn->local_initial_tsn; sctp_conn->next_tsn = sctp_conn->local_initial_tsn + 1; sctp_conn->remote_initial_tsn = 0x0; sctp_conn->last_rcvd_tsn = sctp_conn->remote_initial_tsn; } always_inline sctp_connection_t * sctp_sub_connection_add (u8 thread_index) { sctp_main_t *tm = vnet_get_sctp_main (); sctp_connection_t *sctp_conn = tm->connections[thread_index]; u8 subconn_idx = sctp_next_avail_subconn (sctp_conn); ASSERT (subconn_idx < MAX_SCTP_CONNECTIONS); sctp_conn->sub_conn[subconn_idx].connection.c_index = sctp_conn->sub_conn[SCTP_PRIMARY_PATH_IDX].connection.c_index; sctp_conn->sub_conn[subconn_idx].connection.thread_index = thread_index; sctp_conn->sub_conn[subconn_idx].subconn_idx = subconn_idx; return sctp_conn; } u8 sctp_sub_connection_add_ip4 (vlib_main_t * vm, ip4_address_t * lcl_addr, ip4_address_t * rmt_addr) { sctp_connection_t *sctp_conn = sctp_sub_connection_add (vm->thread_index); u8 subconn_idx = sctp_next_avail_subconn (sctp_conn); if (subconn_idx == MAX_SCTP_CONNECTIONS) return SCTP_ERROR_MAX_CONNECTIONS; clib_memcpy (&sctp_conn->sub_conn[subconn_idx].connection.lcl_ip, &lcl_addr, sizeof (lcl_addr)); clib_memcpy (&sctp_conn->sub_conn[subconn_idx].connection.rmt_ip, &rmt_addr, sizeof (rmt_addr)); sctp_conn->forming_association_changed = 1; return SCTP_ERROR_NONE; } u8 sctp_sub_connection_del_ip4 (ip4_address_t * lcl_addr, ip4_address_t * rmt_addr) { sctp_main_t *sctp_main = vnet_get_sctp_main (); u32 thread_idx = vlib_get_thread_index (); u8 i; ASSERT (thread_idx == 0); for (i = 0; i < MAX_SCTP_CONNECTIONS; i++) { sctp_connection_t *sctp_conn = sctp_main->connections[thread_idx]; sctp_sub_connection_t *sub_conn = &sctp_main->connections[thread_idx]->sub_conn[i]; ip46_address_t *lcl_ip = &sctp_main->connections[thread_idx]->sub_conn[i].connection.lcl_ip; ip46_address_t *rmt_ip = &sctp_main->connections[thread_idx]->sub_conn[i].connection.rmt_ip; if (!sub_conn->connection.is_ip4) continue; if (lcl_ip->ip4.as_u32 == lcl_addr->as_u32 && rmt_ip->ip4.as_u32 == rmt_addr->as_u32) { sub_conn->state = SCTP_SUBCONN_STATE_DOWN; sctp_conn->forming_association_changed = 1; break; } } return SCTP_ERROR_NONE; } u8 sctp_sub_connection_add_ip6 (vlib_main_t * vm, ip6_address_t * lcl_addr, ip6_address_t * rmt_addr) { sctp_connection_t *sctp_conn = sctp_sub_connection_add (vm->thread_index); u8 subconn_idx = sctp_next_avail_subconn (sctp_conn); if (subconn_idx == MAX_SCTP_CONNECTIONS) return SCTP_ERROR_MAX_CONNECTIONS
/*
 * Copyright (c) 2015-2016 Cisco and/or its affiliates.
 * 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.
 */

option version = "1.0.0";

/** \brief Add/del policer
    @param client_index - opaque cookie to identify the sender
    @param context - sender context, to match reply w/ request
    @param is_add - add policer if non-zero, else delete
    @param name - policer name
    @param cir - CIR
    @param eir - EIR
    @param cb - Committed Burst
    @param eb - Excess or Peak Burst
    @param rate_type - rate type
    @param round_type - rounding type
    @param type - policer algorithm
    @param color_aware - 0=color-blind, 1=color-aware
    @param conform_action_type - conform action type
    @param conform_dscp - DSCP for conform mar-and-transmit action
    @param exceed_action_type - exceed action type
    @param exceed_dscp - DSCP for exceed mar-and-transmit action
    @param violate_action_type - violate action type
    @param violate_dscp - DSCP for violate mar-and-transmit action
*/
define policer_add_del
{
  u32 client_index;
  u32 context;

  u8 is_add;
  u8 name[64];
  u32 cir;
  u32 eir;
  u64 cb;
  u64 eb;
  u8 rate_type;
  u8 round_type;
  u8 type;
  u8 color_aware;
  u8 conform_action_type;
  u8 conform_dscp;
  u8 exceed_action_type;
  u8 exceed_dscp;
  u8 violate_action_type;
  u8 violate_dscp;
};

/** \brief Add/del policer response
    @param context - sender context, to match reply w/ request
    @param retval - return value for request
    @param policer_index - for add, returned index of the new policer
*/
define policer_add_del_reply
{
  u32 context;
  i32 retval;
  u32 policer_index;
};

/** \brief Get list of policers
    @param client_index - opaque cookie to identify the sender
    @param context - sender context, to match reply w/ request
    @param match_name_valid - if 0 request all policers otherwise use match_name
    @param match_name - policer name
*/
define policer_dump
{
  u32 client_index;
  u32 context;

  u8 match_name_valid;
  u8 match_name[64];
};

/** \brief Policer operational state response.
    @param context - sender context, to match reply w/ request
    @param name - policer name
    @param cir - CIR
    @param eir - EIR
    @param cb - Committed Burst
    @param eb - Excess or Peak Burst
    @param rate_type - rate type
    @param round_type - rounding type
    @param type - policer algorithm
    @param conform_action_type - conform action type
    @param conform_dscp - DSCP for conform mar-and-transmit action
    @param exceed_action_type - exceed action type
    @param exceed_dscp - DSCP for exceed mar-and-transmit action
    @param violate_action_type - violate action type
    @param violate_dscp - DSCP for violate mar-and-transmit action
    @param single_rate - 1 = single rate policer, 0 = two rate policer
    @param color_aware - for hierarchical policing
    @param scale - power-of-2 shift amount for lower rates
    @param cir_tokens_per_period - number of tokens for each period
    @param pir_tokens_per_period - number of tokens for each period for 2-rate policer
    @param current_limit - current limit
    @param current_bucket - current bucket
    @param extended_limit - extended limit
    @param extended_bucket - extended bucket
    @param last_update_time - last update time
*/
define policer_details
{
  u32 context;

  u8 name[64];
  u32 cir;
  u32 eir;
  u64 cb;
  u64 eb;
  u8 rate_type;
  u8 round_type;
  u8 type;
  u8 conform_action_type;
  u8 conform_dscp;
  u8 exceed_action_type;
  u8 exceed_dscp;
  u8 violate_action_type;
  u8 violate_dscp;
  u8 single_rate;
  u8 color_aware;
  u32 scale;
  u32 cir_tokens_per_period;
  u32 pir_tokens_per_period;
  u32 current_limit;
  u32 current_bucket;
  u32 extended_limit;
  u32 extended_bucket;
  u64 last_update_time;
};

/*
 * Local Variables:
 * eval: (c-set-style "gnu")
 * End:
 */
MAX_SCTP_CONNECTIONS) { /* Start cleanup. App wasn't notified yet so use delete notify as * opposed to delete to cleanup session layer state. */ stream_session_delete_notify (&sctp_conn->sub_conn [SCTP_PRIMARY_PATH_IDX].connection); sctp_connection_timers_reset (sctp_conn); sctp_connection_cleanup (sctp_conn); } return; } switch (timer_id) { case SCTP_TIMER_T1_INIT: sctp_send_init (sctp_conn); break; case SCTP_TIMER_T1_COOKIE: sctp_send_cookie_echo (sctp_conn); break; case SCTP_TIMER_T2_SHUTDOWN: sctp_send_shutdown (sctp_conn); break; case SCTP_TIMER_T3_RXTX: sctp_timer_reset (sctp_conn, conn_index, timer_id); sctp_conn->flags |= SCTP_CONN_RECOVERY; sctp_data_retransmit (sctp_conn); break; case SCTP_TIMER_T4_HEARTBEAT: sctp_timer_reset (sctp_conn, conn_index, timer_id); goto heartbeat; } return; heartbeat: sctp_send_heartbeat (sctp_conn); } 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; SCTP_DBG ("Expired timer ID: %u", timer_id); /* Handle expiration */ sctp_expired_timers_cb (connection_index, timer_id); } } 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 *sctp_conn __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) { clib_spinlock_init (&tm->half_open_lock); } 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 *sctp_conn = sctp_half_open_connection_get (conn_index); return &sctp_conn->sub_conn[SCTP_PRIMARY_PATH_IDX].connection; } u8 * format_sctp_half_open (u8 * s, va_list * args) { u32 tci = va_arg (*args, u32); sctp_connection_t *sctp_conn = sctp_half_open_connection_get (tci); return format (s, "%U", format_sctp_connection_id, sctp_conn); } void sctp_update_time (f64 now, u8 thread_index) { sctp_set_time_now (thread_index); tw_timer_expire_timers_16t_2w_512sl (&sctp_main.timer_wheels[thread_index], now); sctp_flush_frames_to_output (thread_index); } /* *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, .update_time = sctp_update_time, .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, .tx_type = TRANSPORT_TX_DEQUEUE, .service_type = TRANSPORT_SERVICE_VC, }; /* *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); sctp_api_reference (); return 0; } VLIB_INIT_FUNCTION (sctp_init); /* * fd.io coding-style-patch-verification: ON * * Local Variables: * eval: (c-set-style "gnu") * End: */