tcp: move features to separate files

Type: refactor

Signed-off-by: Florin Coras <fcoras@cisco.com>
Change-Id: Ia477b8dba9266f47907967e363c11048e5cd95ab
diff --git a/src/vnet/tcp/tcp_packet.h b/src/vnet/tcp/tcp_packet.h
index 1e637a8..fcc55ff 100644
--- a/src/vnet/tcp/tcp_packet.h
+++ b/src/vnet/tcp/tcp_packet.h
@@ -172,6 +172,219 @@
 #define TCP_MAX_WND_SCALE               14	/* See RFC 1323 */
 #define TCP_OPTS_ALIGN                  4
 #define TCP_OPTS_MAX_SACK_BLOCKS        3
+
+/* Modulo arithmetic for TCP sequence numbers */
+#define seq_lt(_s1, _s2) ((i32)((_s1)-(_s2)) < 0)
+#define seq_leq(_s1, _s2) ((i32)((_s1)-(_s2)) <= 0)
+#define seq_gt(_s1, _s2) ((i32)((_s1)-(_s2)) > 0)
+#define seq_geq(_s1, _s2) ((i32)((_s1)-(_s2)) >= 0)
+#define seq_max(_s1, _s2) (seq_gt((_s1), (_s2)) ? (_s1) : (_s2))
+
+/* Modulo arithmetic for timestamps */
+#define timestamp_lt(_t1, _t2) ((i32)((_t1)-(_t2)) < 0)
+#define timestamp_leq(_t1, _t2) ((i32)((_t1)-(_t2)) <= 0)
+
+/**
+ * Parse TCP header options.
+ *
+ * @param th TCP header
+ * @param to TCP options data structure to be populated
+ * @param is_syn set if packet is syn
+ * @return -1 if parsing failed
+ */
+always_inline int
+tcp_options_parse (tcp_header_t * th, tcp_options_t * to, u8 is_syn)
+{
+  const u8 *data;
+  u8 opt_len, opts_len, kind;
+  int j;
+  sack_block_t b;
+
+  opts_len = (tcp_doff (th) << 2) - sizeof (tcp_header_t);
+  data = (const u8 *) (th + 1);
+
+  /* Zero out all flags but those set in SYN */
+  to->flags &= (TCP_OPTS_FLAG_SACK_PERMITTED | TCP_OPTS_FLAG_WSCALE
+		| TCP_OPTS_FLAG_TSTAMP | TCP_OPTS_FLAG_MSS);
+
+  for (; opts_len > 0; opts_len -= opt_len, data += opt_len)
+    {
+      kind = data[0];
+
+      /* Get options length */
+      if (kind == TCP_OPTION_EOL)
+	break;
+      else if (kind == TCP_OPTION_NOOP)
+	{
+	  opt_len = 1;
+	  continue;
+	}
+      else
+	{
+	  /* broken options */
+	  if (opts_len < 2)
+	    return -1;
+	  opt_len = data[1];
+
+	  /* weird option length */
+	  if (opt_len < 2 || opt_len > opts_len)
+	    return -1;
+	}
+
+      /* Parse options */
+      switch (kind)
+	{
+	case TCP_OPTION_MSS:
+	  if (!is_syn)
+	    break;
+	  if ((opt_len == TCP_OPTION_LEN_MSS) && tcp_syn (th))
+	    {
+	      to->flags |= TCP_OPTS_FLAG_MSS;
+	      to->mss = clib_net_to_host_u16 (*(u16 *) (data + 2));
+	    }
+	  break;
+	case TCP_OPTION_WINDOW_SCALE:
+	  if (!is_syn)
+	    break;
+	  if ((opt_len == TCP_OPTION_LEN_WINDOW_SCALE) && tcp_syn (th))
+	    {
+	      to->flags |= TCP_OPTS_FLAG_WSCALE;
+	      to->wscale = data[2];
+	      if (to->wscale > TCP_MAX_WND_SCALE)
+		to->wscale = TCP_MAX_WND_SCALE;
+	    }
+	  break;
+	case TCP_OPTION_TIMESTAMP:
+	  if (is_syn)
+	    to->flags |= TCP_OPTS_FLAG_TSTAMP;
+	  if ((to->flags & TCP_OPTS_FLAG_TSTAMP)
+	      && opt_len == TCP_OPTION_LEN_TIMESTAMP)
+	    {
+	      to->tsval = clib_net_to_host_u32 (*(u32 *) (data + 2));
+	      to->tsecr = clib_net_to_host_u32 (*(u32 *) (data + 6));
+	    }
+	  break;
+	case TCP_OPTION_SACK_PERMITTED:
+	  if (!is_syn)
+	    break;
+	  if (opt_len == TCP_OPTION_LEN_SACK_PERMITTED && tcp_syn (th))
+	    to->flags |= TCP_OPTS_FLAG_SACK_PERMITTED;
+	  break;
+	case TCP_OPTION_SACK_BLOCK:
+	  /* If SACK permitted was not advertised or a SYN, break */
+	  if ((to->flags & TCP_OPTS_FLAG_SACK_PERMITTED) == 0 || tcp_syn (th))
+	    break;
+
+	  /* If too short or not correctly formatted, break */
+	  if (opt_len < 10 || ((opt_len - 2) % TCP_OPTION_LEN_SACK_BLOCK))
+	    break;
+
+	  to->flags |= TCP_OPTS_FLAG_SACK;
+	  to->n_sack_blocks = (opt_len - 2) / TCP_OPTION_LEN_SACK_BLOCK;
+	  vec_reset_length (to->sacks);
+	  for (j = 0; j < to->n_sack_blocks; j++)
+	    {
+	      b.start = clib_net_to_host_u32 (*(u32 *) (data + 2 + 8 * j));
+	      b.end = clib_net_to_host_u32 (*(u32 *) (data + 6 + 8 * j));
+	      vec_add1 (to->sacks, b);
+	    }
+	  break;
+	default:
+	  /* Nothing to see here */
+	  continue;
+	}
+    }
+  return 0;
+}
+
+/**
+ * Write TCP options to segment.
+ *
+ * @param data	buffer where to write the options
+ * @param opts	options to write
+ * @return	length of options written
+ */
+always_inline u32
+tcp_options_write (u8 * data, tcp_options_t * opts)
+{
+  u32 opts_len = 0;
+  u32 buf, seq_len = 4;
+
+  if (tcp_opts_mss (opts))
+    {
+      *data++ = TCP_OPTION_MSS;
+      *data++ = TCP_OPTION_LEN_MSS;
+      buf = clib_host_to_net_u16 (opts->mss);
+      clib_memcpy_fast (data, &buf, sizeof (opts->mss));
+      data += sizeof (opts->mss);
+      opts_len += TCP_OPTION_LEN_MSS;
+    }
+
+  if (tcp_opts_wscale (opts))
+    {
+      *data++ = TCP_OPTION_WINDOW_SCALE;
+      *data++ = TCP_OPTION_LEN_WINDOW_SCALE;
+      *data++ = opts->wscale;
+      opts_len += TCP_OPTION_LEN_WINDOW_SCALE;
+    }
+
+  if (tcp_opts_sack_permitted (opts))
+    {
+      *data++ = TCP_OPTION_SACK_PERMITTED;
+      *data++ = TCP_OPTION_LEN_SACK_PERMITTED;
+      opts_len += TCP_OPTION_LEN_SACK_PERMITTED;
+    }
+
+  if (tcp_opts_tstamp (opts))
+    {
+      *data++ = TCP_OPTION_TIMESTAMP;
+      *data++ = TCP_OPTION_LEN_TIMESTAMP;
+      buf = clib_host_to_net_u32 (opts->tsval);
+      clib_memcpy_fast (data, &buf, sizeof (opts->tsval));
+      data += sizeof (opts->tsval);
+      buf = clib_host_to_net_u32 (opts->tsecr);
+      clib_memcpy_fast (data, &buf, sizeof (opts->tsecr));
+      data += sizeof (opts->tsecr);
+      opts_len += TCP_OPTION_LEN_TIMESTAMP;
+    }
+
+  if (tcp_opts_sack (opts))
+    {
+      int i;
+
+      if (opts->n_sack_blocks != 0)
+	{
+	  *data++ = TCP_OPTION_SACK_BLOCK;
+	  *data++ = 2 + opts->n_sack_blocks * TCP_OPTION_LEN_SACK_BLOCK;
+	  for (i = 0; i < opts->n_sack_blocks; i++)
+	    {
+	      buf = clib_host_to_net_u32 (opts->sacks[i].start);
+	      clib_memcpy_fast (data, &buf, seq_len);
+	      data += seq_len;
+	      buf = clib_host_to_net_u32 (opts->sacks[i].end);
+	      clib_memcpy_fast (data, &buf, seq_len);
+	      data += seq_len;
+	    }
+	  opts_len += 2 + opts->n_sack_blocks * TCP_OPTION_LEN_SACK_BLOCK;
+	}
+    }
+
+  /* Terminate TCP options */
+  if (opts_len % 4)
+    {
+      *data++ = TCP_OPTION_EOL;
+      opts_len += TCP_OPTION_LEN_EOL;
+    }
+
+  /* Pad with zeroes to a u32 boundary */
+  while (opts_len % 4)
+    {
+      *data++ = TCP_OPTION_NOOP;
+      opts_len += TCP_OPTION_LEN_NOOP;
+    }
+  return opts_len;
+}
+
 #endif /* included_tcp_packet_h */
 
 /*