SCTP: shutdown phase
[vpp.git] / src / vnet / sctp / sctp.h
index 3e3750e..60a195f 100644 (file)
@@ -41,6 +41,27 @@ typedef enum _sctp_timers
 
 #define SCTP_TIMER_HANDLE_INVALID ((u32) ~0)
 
+always_inline char *
+sctp_timer_to_string (u8 timer_id)
+{
+  switch (timer_id)
+    {
+    case SCTP_TIMER_T1_INIT:
+      return "SCTP_TIMER_T1_INIT";
+    case SCTP_TIMER_T1_COOKIE:
+      return "SCTP_TIMER_T1_COOKIE";
+    case SCTP_TIMER_T2_SHUTDOWN:
+      return "SCTP_TIMER_T2_SHUTDOWN";
+    case SCTP_TIMER_T3_RXTX:
+      return "SCTP_TIMER_T3_RXTX";
+    case SCTP_TIMER_T4_HEARTBEAT:
+      return "SCTP_TIMER_T4_HEARTBEAT";
+    case SCTP_TIMER_T5_SHUTDOWN_GUARD:
+      return "SCTP_TIMER_T5_SHUTDOWN_GUARD";
+    }
+  return NULL;
+}
+
 typedef enum _sctp_error
 {
 #define sctp_error(n,s) SCTP_ERROR_##n,
@@ -79,9 +100,11 @@ typedef struct _sctp_sub_connection
   u32 cwnd; /**< The current congestion window. */
   u32 ssthresh;        /**< The current ssthresh value. */
 
+  u32 rtt_ts;  /**< USED to hold the timestamp of when the packet has been sent */
+
   u32 RTO; /**< The current retransmission timeout value. */
   u32 SRTT; /**< The current smoothed round-trip time. */
-  u32 RTTVAR; /**< The current RTT variation. */
+  f32 RTTVAR; /**< The current RTT variation. */
 
   u32 partially_acked_bytes; /**< The tracking method for increase of cwnd when in
                                  congestion avoidance mode (see Section 7.2.2).*/
@@ -102,6 +125,9 @@ typedef struct _sctp_sub_connection
   u32 last_time; /**< The time to which this destination was last sent a packet to.
                                  This can be used to determine if a HEARTBEAT is needed. */
 
+  u8 unacknowledged_hb;        /**< Used to track how many unacknowledged heartbeats we had;
+                                 If more than Max.Retransmit then connetion is considered unreachable. */
+
 } sctp_sub_connection_t;
 
 typedef struct
@@ -110,9 +136,25 @@ typedef struct
 
 } sctp_options_t;
 
-#define SetBit(A,k)     ( A[(k/32)] |= (1 << (k%32)) )
-#define ClearBit(A,k)   ( A[(k/32)] &= ~(1 << (k%32)) )
-#define TestBit(A,k)    ( A[(k/32)] & (1 << (k%32)) )
+/* Useful macros to deal with the out_of_order_map (array of bit) */
+#define SET_BIT(A,k)     ( A[(k/32)] |= (1 << (k%32)) )
+#define CLEAR_BIT(A,k)   ( A[(k/32)] &= ~(1 << (k%32)) )
+#define TEST_BIT(A,k)    ( A[(k/32)] & (1 << (k%32)) )
+
+always_inline void
+_bytes_swap (void *pv, size_t n)
+{
+  char *p = pv;
+  size_t lo, hi;
+  for (lo = 0, hi = n - 1; hi > lo; lo++, hi--)
+    {
+      char tmp = p[lo];
+      p[lo] = p[hi];
+      p[hi] = tmp;
+    }
+}
+
+#define ENDIANESS_SWAP(x) _bytes_swap(&x, sizeof(x));
 
 #define MAX_INFLIGHT_PACKETS   128
 #define MAX_ENQUEABLE_SACKS 2
@@ -179,8 +221,9 @@ typedef struct _sctp_connection
   u32 rcv_a_rwnd;              /**< LOCAL max seg size that includes options. To be updated by congestion algos, etc. */
   u32 snd_a_rwnd;              /**< REMOTE max seg size that includes options. To be updated if peer pushes back on window, etc.*/
 
-  u32 rtt_ts;
-  u32 rtt_seq;
+  u8 overall_sending_status; /**< 0 indicates first fragment of a user message
+                                                                 1 indicates normal stream
+                                                                 2 indicates last fragment of a user message */
 
   sctp_options_t rcv_opts;
   sctp_options_t snd_opts;
@@ -197,15 +240,17 @@ void sctp_sub_connection_add_ip4 (u8 thread_index,
                                  sctp_ipv4_addr_param_t * ipv4_addr);
 void sctp_sub_connection_add_ip6 (u8 thread_index,
                                  sctp_ipv6_addr_param_t * ipv6_addr);
-void sctp_connection_close (sctp_connection_t * tc);
-void sctp_connection_cleanup (sctp_connection_t * tc);
-void sctp_connection_del (sctp_connection_t * tc);
+void sctp_connection_close (sctp_connection_t * sctp_conn);
+void sctp_connection_cleanup (sctp_connection_t * sctp_conn);
+void sctp_connection_del (sctp_connection_t * sctp_conn);
 
 u32 sctp_push_header (transport_connection_t * tconn, vlib_buffer_t * b);
-void sctp_send_init (sctp_connection_t * tc);
-void sctp_send_shutdown (sctp_connection_t * tc);
-void sctp_send_shutdown_ack (sctp_connection_t * tc);
-void sctp_send_shutdown_complete (sctp_connection_t * tc);
+void sctp_send_init (sctp_connection_t * sctp_conn);
+void sctp_send_shutdown (sctp_connection_t * sctp_conn);
+void sctp_send_shutdown_ack (sctp_connection_t * sctp_conn,
+                            vlib_buffer_t * b);
+void sctp_send_shutdown_complete (sctp_connection_t * sctp_conn);
+void sctp_send_heartbeat (sctp_connection_t * sctp_conn);
 void sctp_flush_frame_to_output (vlib_main_t * vm, u8 thread_index,
                                 u8 is_ip4);
 void sctp_flush_frames_to_output (u8 thread_index);
@@ -234,6 +279,8 @@ void sctp_prepare_cookie_echo_chunk (sctp_connection_t * tc,
 void sctp_prepare_cookie_ack_chunk (sctp_connection_t * tc,
                                    vlib_buffer_t * b);
 void sctp_prepare_sack_chunk (sctp_connection_t * tc, vlib_buffer_t * b);
+void sctp_prepare_heartbeat_ack_chunk (sctp_connection_t * sctp_conn,
+                                      vlib_buffer_t * b);
 
 u16 sctp_check_outstanding_data_chunks (sctp_connection_t * tc);
 
@@ -503,7 +550,7 @@ sctp_timer_set (sctp_connection_t * tc, u8 conn_idx, u8 timer_id,
          SCTP_TIMER_HANDLE_INVALID);
 
   sctp_sub_connection_t *sub = &tc->sub_conn[conn_idx];
-  tc->sub_conn[conn_idx].timers[timer_id] =
+  sub->timers[timer_id] =
     tw_timer_start_16t_2w_512sl (&sctp_main.timer_wheels[sub->c_thread_index],
                                 sub->c_c_index, timer_id, interval);
 }
@@ -522,15 +569,6 @@ sctp_timer_reset (sctp_connection_t * tc, u8 conn_idx, u8 timer_id)
   sub->timers[timer_id] = SCTP_TIMER_HANDLE_INVALID;
 }
 
-always_inline void
-sctp_update_time (f64 now, u32 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);
-}
-
 /**
  * Try to cleanup half-open connection
  *
@@ -579,6 +617,61 @@ sctp_time_now (void)
   return sctp_main.time_now[vlib_get_thread_index ()];
 }
 
+#define ABS(x) ((x) > 0) ? (x) : -(x);
+
+always_inline void
+sctp_calculate_rto (sctp_connection_t * sctp_conn, u8 conn_idx)
+{
+  /* See RFC4960, 6.3.1.  RTO Calculation */
+  u32 RTO = 0;
+  f32 RTTVAR = 0;
+  u32 now = sctp_time_now ();
+  u32 prev_ts = sctp_conn->sub_conn[conn_idx].rtt_ts;
+  u32 R = prev_ts - now;
+
+  if (sctp_conn->sub_conn[conn_idx].RTO == 0)  // C1: Let's initialize our RTO
+    {
+      sctp_conn->sub_conn[conn_idx].RTO = SCTP_RTO_MIN;
+      return;
+    }
+
+  if (sctp_conn->sub_conn[conn_idx].RTO == SCTP_RTO_MIN && sctp_conn->sub_conn[conn_idx].SRTT == 0)    // C2: First RTT calculation
+    {
+      sctp_conn->sub_conn[conn_idx].SRTT = R;
+      RTTVAR = R / 2;
+
+      if (RTTVAR == 0)
+       RTTVAR = 100e-3;        /* 100 ms */
+
+      sctp_conn->sub_conn[conn_idx].RTTVAR = RTTVAR;
+    }
+  else                         // C3: RTT already exists; let's recalculate
+    {
+      RTTVAR = (1 - SCTP_RTO_BETA) * sctp_conn->sub_conn[conn_idx].RTTVAR +
+       SCTP_RTO_BETA * ABS (sctp_conn->sub_conn[conn_idx].SRTT - R);
+
+      if (RTTVAR == 0)
+       RTTVAR = 100e-3;        /* 100 ms */
+
+      sctp_conn->sub_conn[conn_idx].RTTVAR = RTTVAR;
+
+      sctp_conn->sub_conn[conn_idx].SRTT =
+       (1 - SCTP_RTO_ALPHA) * sctp_conn->sub_conn[conn_idx].SRTT +
+       SCTP_RTO_ALPHA * R;
+    }
+
+  RTO =
+    sctp_conn->sub_conn[conn_idx].SRTT +
+    4 * sctp_conn->sub_conn[conn_idx].RTTVAR;
+  if (RTO < SCTP_RTO_MIN)      // C6
+    RTO = SCTP_RTO_MIN;
+
+  if (RTO > SCTP_RTO_MAX)      // C7
+    RTO = SCTP_RTO_MAX;
+
+  sctp_conn->sub_conn[conn_idx].RTO = RTO;
+}
+
 always_inline void
 sctp_timer_update (sctp_connection_t * tc, u8 conn_idx, u8 timer_id,
                   u32 interval)