cnat: Add sctp support

This patch adds SCTP support in the CNat translation primitives.
It also exposes a clib_crc32c_with_init function allowing to set
the init value to start the crc32 with instead of 0.

Type: feature

Change-Id: I86add4cfcac08f2a5a34d1e1841122fafd349fe7
Signed-off-by: Nathan Skrzypczak <nathan.skrzypczak@gmail.com>
diff --git a/src/plugins/cnat/cnat_node.h b/src/plugins/cnat/cnat_node.h
index 80d803c..c304c5c 100644
--- a/src/plugins/cnat/cnat_node.h
+++ b/src/plugins/cnat/cnat_node.h
@@ -226,6 +226,29 @@
 }
 
 static_always_inline void
+cnat_ip4_translate_sctp (ip4_header_t *ip4, sctp_header_t *sctp,
+			 u16 new_port[VLIB_N_DIR])
+{
+  /* Fastpath no checksum */
+  if (PREDICT_TRUE (0 == sctp->checksum))
+    {
+      sctp->dst_port = new_port[VLIB_TX];
+      sctp->src_port = new_port[VLIB_RX];
+      return;
+    }
+
+  if (new_port[VLIB_TX])
+    sctp->dst_port = new_port[VLIB_TX];
+  if (new_port[VLIB_RX])
+    sctp->src_port = new_port[VLIB_RX];
+
+  sctp->checksum = 0;
+  sctp->checksum = clib_host_to_little_u32 (~clib_crc32c_with_init (
+    (u8 *) sctp, ntohs (ip4->length) - sizeof (ip4_header_t),
+    ~0 /* init value */));
+}
+
+static_always_inline void
 cnat_ip4_translate_l3 (ip4_header_t * ip4, ip4_address_t new_addr[VLIB_N_DIR])
 {
   ip4_address_t old_addr[VLIB_N_DIR];
@@ -407,6 +430,12 @@
       udp->checksum = ip_csum_fold (sum);
       cnat_ip4_translate_l3 (ip4, new_addr);
     }
+  else if (ip4->protocol == IP_PROTOCOL_SCTP)
+    {
+      sctp_header_t *sctp = (sctp_header_t *) udp;
+      cnat_ip4_translate_sctp (ip4, sctp, new_port);
+      cnat_ip4_translate_l3 (ip4, new_addr);
+    }
   else if (ip4->protocol == IP_PROTOCOL_ICMP)
     {
       icmp46_header_t *icmp = (icmp46_header_t *) udp;
@@ -743,6 +772,18 @@
 	  session->key.cs_port[VLIB_RX] = udp->src_port;
 	  session->key.cs_port[VLIB_TX] = udp->dst_port;
 	}
+      else if (ip4->protocol == IP_PROTOCOL_SCTP)
+	{
+	  sctp_header_t *sctp;
+	  sctp = (sctp_header_t *) (ip4 + 1);
+	  ip46_address_set_ip4 (&session->key.cs_ip[VLIB_TX],
+				&ip4->dst_address);
+	  ip46_address_set_ip4 (&session->key.cs_ip[VLIB_RX],
+				&ip4->src_address);
+	  session->key.cs_proto = ip4->protocol;
+	  session->key.cs_port[VLIB_RX] = sctp->src_port;
+	  session->key.cs_port[VLIB_TX] = sctp->dst_port;
+	}
       else
 	goto error;
     }
diff --git a/src/plugins/cnat/cnat_types.h b/src/plugins/cnat/cnat_types.h
index 7b779a6..bf2726f 100644
--- a/src/plugins/cnat/cnat_types.h
+++ b/src/plugins/cnat/cnat_types.h
@@ -55,6 +55,17 @@
 
 #define MIN_SRC_PORT ((u16) 0xC000)
 
+typedef struct
+{
+  /* Source and destination port. */
+  u16 src_port, dst_port;
+
+  /* Random value to distinguish connections. */
+  u32 verification_tag;
+
+  u32 checksum;
+} sctp_header_t;
+
 typedef enum cnat_trk_flag_t_
 {
   /* Endpoint is active (static or dhcp resolved) */
diff --git a/src/vppinfra/crc32.h b/src/vppinfra/crc32.h
index 3b81daf..2b20fef 100644
--- a/src/vppinfra/crc32.h
+++ b/src/vppinfra/crc32.h
@@ -76,23 +76,27 @@
 
 #ifdef clib_crc32c_uses_intrinsics
 static_always_inline u32
-clib_crc32c (u8 * s, int len)
+clib_crc32c_with_init (u8 *s, int len, u32 last)
 {
-  u32 v = 0;
-
   for (; len >= 8; len -= 8, s += 8)
-    v = clib_crc32c_u64 (v, *((u64u *) s));
+    last = clib_crc32c_u64 (last, *((u64u *) s));
 
   for (; len >= 4; len -= 4, s += 4)
-    v = clib_crc32c_u32 (v, *((u32u *) s));
+    last = clib_crc32c_u32 (last, *((u32u *) s));
 
   for (; len >= 2; len -= 2, s += 2)
-    v = clib_crc32c_u16 (v, *((u16u *) s));
+    last = clib_crc32c_u16 (last, *((u16u *) s));
 
   for (; len >= 1; len -= 1, s += 1)
-    v = clib_crc32c_u8 (v, *((u8 *) s));
+    last = clib_crc32c_u8 (last, *((u8 *) s));
 
-  return v;
+  return last;
+}
+
+static_always_inline u32
+clib_crc32c (u8 *s, int len)
+{
+  return clib_crc32c_with_init (s, len, 0);
 }
 #endif