+
+/*
+ * 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_u32 (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;
+}
+