cnat: flag to disable rsession
[vpp.git] / src / plugins / cnat / cnat_node.h
index 80d803c..d81f674 100644 (file)
@@ -19,6 +19,7 @@
 #include <vlibmemory/api.h>
 #include <vnet/dpo/load_balance.h>
 #include <vnet/dpo/load_balance_map.h>
+#include <vnet/ip/ip_psh_cksum.h>
 
 #include <cnat/cnat_session.h>
 #include <cnat/cnat_client.h>
@@ -169,86 +170,92 @@ cmp_ip6_address (const ip6_address_t * a1, const ip6_address_t * a2)
  * Inline translation functions
  */
 
-static_always_inline u8
-has_ip6_address (ip6_address_t * a)
+static_always_inline u16
+ip4_pseudo_header_cksum2 (ip4_header_t *ip4, ip4_address_t address[VLIB_N_DIR])
 {
-  return ((0 != a->as_u64[0]) || (0 != a->as_u64[1]));
+  ip4_psh_t psh = { 0 };
+  psh.src = address[VLIB_RX];
+  psh.dst = address[VLIB_TX];
+  psh.proto = ip4->protocol;
+  psh.l4len = clib_host_to_net_u16 (clib_net_to_host_u16 (ip4->length) -
+                                   sizeof (ip4_header_t));
+  return ~(clib_ip_csum ((u8 *) &psh, sizeof (ip4_psh_t)));
 }
 
 static_always_inline void
-cnat_ip4_translate_l4 (ip4_header_t * ip4, udp_header_t * udp,
-                      ip_csum_t * sum,
+cnat_ip4_translate_l4 (ip4_header_t *ip4, udp_header_t *udp, ip_csum_t *sum,
                       ip4_address_t new_addr[VLIB_N_DIR],
-                      u16 new_port[VLIB_N_DIR])
+                      u16 new_port[VLIB_N_DIR], u32 oflags)
 {
   u16 old_port[VLIB_N_DIR];
-  ip4_address_t old_addr[VLIB_N_DIR];
+  old_port[VLIB_TX] = udp->dst_port;
+  old_port[VLIB_RX] = udp->src_port;
 
-  /* Fastpath no checksum */
-  if (PREDICT_TRUE (0 == *sum))
+  udp->dst_port = new_port[VLIB_TX];
+  udp->src_port = new_port[VLIB_RX];
+
+  if (oflags &
+      (VNET_BUFFER_OFFLOAD_F_TCP_CKSUM | VNET_BUFFER_OFFLOAD_F_UDP_CKSUM))
     {
-      udp->dst_port = new_port[VLIB_TX];
-      udp->src_port = new_port[VLIB_RX];
+      *sum = ip4_pseudo_header_cksum2 (ip4, new_addr);
       return;
     }
 
-  old_port[VLIB_TX] = udp->dst_port;
-  old_port[VLIB_RX] = udp->src_port;
-  old_addr[VLIB_TX] = ip4->dst_address;
-  old_addr[VLIB_RX] = ip4->src_address;
+  *sum = ip_csum_update (*sum, ip4->dst_address.as_u32,
+                        new_addr[VLIB_TX].as_u32, ip4_header_t, dst_address);
+  *sum = ip_csum_update (*sum, ip4->src_address.as_u32,
+                        new_addr[VLIB_RX].as_u32, ip4_header_t, src_address);
 
-  if (new_addr[VLIB_TX].as_u32)
+  *sum = ip_csum_update (*sum, old_port[VLIB_TX], new_port[VLIB_TX],
+                        udp_header_t, dst_port);
+  *sum = ip_csum_update (*sum, old_port[VLIB_RX], new_port[VLIB_RX],
+                        udp_header_t, src_port);
+}
+
+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))
     {
-      *sum =
-       ip_csum_update (*sum, old_addr[VLIB_TX].as_u32,
-                       new_addr[VLIB_TX].as_u32, ip4_header_t, dst_address);
+      sctp->dst_port = new_port[VLIB_TX];
+      sctp->src_port = new_port[VLIB_RX];
+      return;
     }
+
   if (new_port[VLIB_TX])
-    {
-      udp->dst_port = new_port[VLIB_TX];
-      *sum = ip_csum_update (*sum, old_port[VLIB_TX], new_port[VLIB_TX],
-                            ip4_header_t /* cheat */ ,
-                            length /* changed member */ );
-    }
-  if (new_addr[VLIB_RX].as_u32)
-    {
-      *sum =
-       ip_csum_update (*sum, old_addr[VLIB_RX].as_u32,
-                       new_addr[VLIB_RX].as_u32, ip4_header_t, src_address);
-    }
+    sctp->dst_port = new_port[VLIB_TX];
   if (new_port[VLIB_RX])
-    {
-      udp->src_port = new_port[VLIB_RX];
-      *sum = ip_csum_update (*sum, old_port[VLIB_RX], new_port[VLIB_RX],
-                            ip4_header_t /* cheat */ ,
-                            length /* changed member */ );
-    }
+    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])
+cnat_ip4_translate_l3 (ip4_header_t *ip4, ip4_address_t new_addr[VLIB_N_DIR],
+                      u32 oflags)
 {
   ip4_address_t old_addr[VLIB_N_DIR];
   ip_csum_t sum;
-
   old_addr[VLIB_TX] = ip4->dst_address;
   old_addr[VLIB_RX] = ip4->src_address;
 
+  ip4->dst_address = new_addr[VLIB_TX];
+  ip4->src_address = new_addr[VLIB_RX];
+
+  // We always compute the IP checksum even if oflags &
+  // VNET_BUFFER_OFFLOAD_F_IP_CKSUM is set as this is relatively inexpensive
+  // and will allow avoiding issues in driver that do not behave properly
+  // downstream.
   sum = ip4->checksum;
-  if (new_addr[VLIB_TX].as_u32)
-    {
-      ip4->dst_address = new_addr[VLIB_TX];
-      sum =
-       ip_csum_update (sum, old_addr[VLIB_TX].as_u32,
+  sum = ip_csum_update (sum, old_addr[VLIB_TX].as_u32,
                        new_addr[VLIB_TX].as_u32, ip4_header_t, dst_address);
-    }
-  if (new_addr[VLIB_RX].as_u32)
-    {
-      ip4->src_address = new_addr[VLIB_RX];
-      sum =
-       ip_csum_update (sum, old_addr[VLIB_RX].as_u32,
+  sum = ip_csum_update (sum, old_addr[VLIB_RX].as_u32,
                        new_addr[VLIB_RX].as_u32, ip4_header_t, src_address);
-    }
   ip4->checksum = ip_csum_fold (sum);
 }
 
@@ -257,48 +264,40 @@ cnat_tcp_update_session_lifetime (tcp_header_t * tcp, u32 index)
 {
   cnat_main_t *cm = &cnat_main;
   if (PREDICT_FALSE (tcp_fin (tcp)))
-    {
-      cnat_timestamp_set_lifetime (index, CNAT_DEFAULT_TCP_RST_TIMEOUT);
-    }
+    cnat_timestamp_set_lifetime (index, CNAT_DEFAULT_TCP_RST_TIMEOUT);
 
   if (PREDICT_FALSE (tcp_rst (tcp)))
-    {
-      cnat_timestamp_set_lifetime (index, CNAT_DEFAULT_TCP_RST_TIMEOUT);
-    }
+    cnat_timestamp_set_lifetime (index, CNAT_DEFAULT_TCP_RST_TIMEOUT);
 
   if (PREDICT_FALSE (tcp_syn (tcp) && tcp_ack (tcp)))
-    {
-      cnat_timestamp_set_lifetime (index, cm->tcp_max_age);
-    }
+    cnat_timestamp_set_lifetime (index, cm->tcp_max_age);
 }
 
 static_always_inline void
-cnat_translation_icmp4_echo (ip4_header_t * ip4, icmp46_header_t * icmp,
+cnat_translation_icmp4_echo (ip4_header_t *ip4, icmp46_header_t *icmp,
                             ip4_address_t new_addr[VLIB_N_DIR],
-                            u16 new_port[VLIB_N_DIR])
+                            u16 new_port[VLIB_N_DIR], u32 oflags)
 {
   ip_csum_t sum;
   u16 old_port;
   cnat_echo_header_t *echo = (cnat_echo_header_t *) (icmp + 1);
 
-  cnat_ip4_translate_l3 (ip4, new_addr);
+  cnat_ip4_translate_l3 (ip4, new_addr, oflags);
   old_port = echo->identifier;
   echo->identifier = new_port[VLIB_RX];
 
   sum = icmp->checksum;
-  sum = ip_csum_update (sum, old_port, new_port[VLIB_RX],
-                       ip4_header_t /* cheat */ ,
-                       length /* changed member */ );
+  sum =
+    ip_csum_update (sum, old_port, new_port[VLIB_RX], udp_header_t, src_port);
 
   icmp->checksum = ip_csum_fold (sum);
 }
 
 static_always_inline void
-cnat_translation_icmp4_error (ip4_header_t * outer_ip4,
-                             icmp46_header_t * icmp,
+cnat_translation_icmp4_error (ip4_header_t *outer_ip4, icmp46_header_t *icmp,
                              ip4_address_t outer_new_addr[VLIB_N_DIR],
-                             u16 outer_new_port[VLIB_N_DIR],
-                             u8 snat_outer_ip)
+                             u16 outer_new_port[VLIB_N_DIR], u8 snat_outer_ip,
+                             u32 oflags)
 {
   ip4_address_t new_addr[VLIB_N_DIR];
   ip4_address_t old_addr[VLIB_N_DIR];
@@ -327,18 +326,20 @@ cnat_translation_icmp4_error (ip4_header_t * outer_ip4,
   /* translate outer ip. */
   if (!snat_outer_ip)
     outer_new_addr[VLIB_RX] = outer_ip4->src_address;
-  cnat_ip4_translate_l3 (outer_ip4, outer_new_addr);
+  cnat_ip4_translate_l3 (outer_ip4, outer_new_addr, oflags);
 
   if (ip4->protocol == IP_PROTOCOL_TCP)
     {
       inner_l4_old_sum = inner_l4_sum = tcp->checksum;
-      cnat_ip4_translate_l4 (ip4, udp, &inner_l4_sum, new_addr, new_port);
+      cnat_ip4_translate_l4 (ip4, udp, &inner_l4_sum, new_addr, new_port,
+                            0 /* flags */);
       tcp->checksum = ip_csum_fold (inner_l4_sum);
     }
   else if (ip4->protocol == IP_PROTOCOL_UDP)
     {
       inner_l4_old_sum = inner_l4_sum = udp->checksum;
-      cnat_ip4_translate_l4 (ip4, udp, &inner_l4_sum, new_addr, new_port);
+      cnat_ip4_translate_l4 (ip4, udp, &inner_l4_sum, new_addr, new_port,
+                            0 /* flags */);
       udp->checksum = ip_csum_fold (inner_l4_sum);
     }
   else
@@ -351,37 +352,30 @@ cnat_translation_icmp4_error (ip4_header_t * outer_ip4,
   /* UDP/TCP Ports changed */
   if (old_port[VLIB_TX] && new_port[VLIB_TX])
     sum = ip_csum_update (sum, old_port[VLIB_TX], new_port[VLIB_TX],
-                         ip4_header_t /* cheat */ ,
-                         length /* changed member */ );
+                         udp_header_t, dst_port);
 
   if (old_port[VLIB_RX] && new_port[VLIB_RX])
     sum = ip_csum_update (sum, old_port[VLIB_RX], new_port[VLIB_RX],
-                         ip4_header_t /* cheat */ ,
-                         length /* changed member */ );
+                         udp_header_t, src_port);
 
-
-  cnat_ip4_translate_l3 (ip4, new_addr);
+  cnat_ip4_translate_l3 (ip4, new_addr, 0 /* oflags */);
   ip_csum_t new_ip_sum = ip4->checksum;
   /* IP checksum changed */
   sum = ip_csum_update (sum, old_ip_sum, new_ip_sum, ip4_header_t, checksum);
 
   /* IP src/dst addr changed */
-  if (new_addr[VLIB_TX].as_u32)
-    sum =
-      ip_csum_update (sum, old_addr[VLIB_TX].as_u32, new_addr[VLIB_TX].as_u32,
-                     ip4_header_t, dst_address);
+  sum = ip_csum_update (sum, old_addr[VLIB_TX].as_u32,
+                       new_addr[VLIB_TX].as_u32, ip4_header_t, dst_address);
 
-  if (new_addr[VLIB_RX].as_u32)
-    sum =
-      ip_csum_update (sum, old_addr[VLIB_RX].as_u32, new_addr[VLIB_RX].as_u32,
-                     ip4_header_t, src_address);
+  sum = ip_csum_update (sum, old_addr[VLIB_RX].as_u32,
+                       new_addr[VLIB_RX].as_u32, ip4_header_t, src_address);
 
   icmp->checksum = ip_csum_fold (sum);
 }
 
 static_always_inline void
-cnat_translation_ip4 (const cnat_session_t * session,
-                     ip4_header_t * ip4, udp_header_t * udp)
+cnat_translation_ip4 (const cnat_session_t *session, ip4_header_t *ip4,
+                     udp_header_t *udp, u32 oflags)
 {
   tcp_header_t *tcp = (tcp_header_t *) udp;
   ip4_address_t new_addr[VLIB_N_DIR];
@@ -395,17 +389,23 @@ cnat_translation_ip4 (const cnat_session_t * session,
   if (ip4->protocol == IP_PROTOCOL_TCP)
     {
       ip_csum_t sum = tcp->checksum;
-      cnat_ip4_translate_l4 (ip4, udp, &sum, new_addr, new_port);
+      cnat_ip4_translate_l4 (ip4, udp, &sum, new_addr, new_port, oflags);
       tcp->checksum = ip_csum_fold (sum);
-      cnat_ip4_translate_l3 (ip4, new_addr);
+      cnat_ip4_translate_l3 (ip4, new_addr, oflags);
       cnat_tcp_update_session_lifetime (tcp, session->value.cs_ts_index);
     }
   else if (ip4->protocol == IP_PROTOCOL_UDP)
     {
       ip_csum_t sum = udp->checksum;
-      cnat_ip4_translate_l4 (ip4, udp, &sum, new_addr, new_port);
+      cnat_ip4_translate_l4 (ip4, udp, &sum, new_addr, new_port, oflags);
       udp->checksum = ip_csum_fold (sum);
-      cnat_ip4_translate_l3 (ip4, new_addr);
+      cnat_ip4_translate_l3 (ip4, new_addr, oflags);
+    }
+  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, oflags);
     }
   else if (ip4->protocol == IP_PROTOCOL_ICMP)
     {
@@ -417,74 +417,65 @@ cnat_translation_ip4 (const cnat_session_t * session,
            (ip4->src_address.as_u32 ==
             session->key.cs_ip[VLIB_RX].ip4.as_u32);
          cnat_translation_icmp4_error (ip4, icmp, new_addr, new_port,
-                                       snat_outer_ip);
+                                       snat_outer_ip, oflags);
        }
       else if (icmp_type_is_echo (icmp->type))
-       cnat_translation_icmp4_echo (ip4, icmp, new_addr, new_port);
+       cnat_translation_icmp4_echo (ip4, icmp, new_addr, new_port, oflags);
     }
 }
 
 static_always_inline void
 cnat_ip6_translate_l3 (ip6_header_t * ip6, ip6_address_t new_addr[VLIB_N_DIR])
 {
-  if (has_ip6_address (&new_addr[VLIB_TX]))
-    ip6_address_copy (&ip6->dst_address, &new_addr[VLIB_TX]);
-  if (has_ip6_address (&new_addr[VLIB_RX]))
-    ip6_address_copy (&ip6->src_address, &new_addr[VLIB_RX]);
+  ip6_address_copy (&ip6->dst_address, &new_addr[VLIB_TX]);
+  ip6_address_copy (&ip6->src_address, &new_addr[VLIB_RX]);
+}
+
+static_always_inline u16
+ip6_pseudo_header_cksum2 (ip6_header_t *ip6, ip6_address_t address[VLIB_N_DIR])
+{
+  ip6_psh_t psh = { 0 };
+  psh.src = address[VLIB_RX];
+  psh.dst = address[VLIB_TX];
+  psh.l4len = ip6->payload_length;
+  psh.proto = clib_host_to_net_u32 ((u32) ip6->protocol);
+  return ~(clib_ip_csum ((u8 *) &psh, sizeof (ip6_psh_t)));
 }
 
 static_always_inline void
-cnat_ip6_translate_l4 (ip6_header_t * ip6, udp_header_t * udp,
-                      ip_csum_t * sum,
+cnat_ip6_translate_l4 (ip6_header_t *ip6, udp_header_t *udp, ip_csum_t *sum,
                       ip6_address_t new_addr[VLIB_N_DIR],
-                      u16 new_port[VLIB_N_DIR])
+                      u16 new_port[VLIB_N_DIR], u32 oflags)
 {
   u16 old_port[VLIB_N_DIR];
-  ip6_address_t old_addr[VLIB_N_DIR];
+  old_port[VLIB_TX] = udp->dst_port;
+  old_port[VLIB_RX] = udp->src_port;
 
-  /* Fastpath no checksum */
-  if (PREDICT_TRUE (0 == *sum))
+  udp->dst_port = new_port[VLIB_TX];
+  udp->src_port = new_port[VLIB_RX];
+
+  if (oflags &
+      (VNET_BUFFER_OFFLOAD_F_TCP_CKSUM | VNET_BUFFER_OFFLOAD_F_UDP_CKSUM))
     {
-      udp->dst_port = new_port[VLIB_TX];
-      udp->src_port = new_port[VLIB_RX];
+      *sum = ip6_pseudo_header_cksum2 (ip6, new_addr);
       return;
     }
 
-  old_port[VLIB_TX] = udp->dst_port;
-  old_port[VLIB_RX] = udp->src_port;
-  ip6_address_copy (&old_addr[VLIB_TX], &ip6->dst_address);
-  ip6_address_copy (&old_addr[VLIB_RX], &ip6->src_address);
+  *sum = ip_csum_add_even (*sum, new_addr[VLIB_TX].as_u64[0]);
+  *sum = ip_csum_add_even (*sum, new_addr[VLIB_TX].as_u64[1]);
+  *sum = ip_csum_sub_even (*sum, ip6->dst_address.as_u64[0]);
+  *sum = ip_csum_sub_even (*sum, ip6->dst_address.as_u64[1]);
 
-  if (has_ip6_address (&new_addr[VLIB_TX]))
-    {
-      *sum = ip_csum_add_even (*sum, new_addr[VLIB_TX].as_u64[0]);
-      *sum = ip_csum_add_even (*sum, new_addr[VLIB_TX].as_u64[1]);
-      *sum = ip_csum_sub_even (*sum, old_addr[VLIB_TX].as_u64[0]);
-      *sum = ip_csum_sub_even (*sum, old_addr[VLIB_TX].as_u64[1]);
-    }
+  *sum = ip_csum_add_even (*sum, new_addr[VLIB_RX].as_u64[0]);
+  *sum = ip_csum_add_even (*sum, new_addr[VLIB_RX].as_u64[1]);
+  *sum = ip_csum_sub_even (*sum, ip6->src_address.as_u64[0]);
+  *sum = ip_csum_sub_even (*sum, ip6->src_address.as_u64[1]);
 
-  if (new_port[VLIB_TX])
-    {
-      udp->dst_port = new_port[VLIB_TX];
-      *sum = ip_csum_update (*sum, old_port[VLIB_TX], new_port[VLIB_TX],
-                            ip4_header_t /* cheat */ ,
-                            length /* changed member */ );
-    }
-  if (has_ip6_address (&new_addr[VLIB_RX]))
-    {
-      *sum = ip_csum_add_even (*sum, new_addr[VLIB_RX].as_u64[0]);
-      *sum = ip_csum_add_even (*sum, new_addr[VLIB_RX].as_u64[1]);
-      *sum = ip_csum_sub_even (*sum, old_addr[VLIB_RX].as_u64[0]);
-      *sum = ip_csum_sub_even (*sum, old_addr[VLIB_RX].as_u64[1]);
-    }
+  *sum = ip_csum_update (*sum, old_port[VLIB_TX], new_port[VLIB_TX],
+                        udp_header_t, dst_port);
 
-  if (new_port[VLIB_RX])
-    {
-      udp->src_port = new_port[VLIB_RX];
-      *sum = ip_csum_update (*sum, old_port[VLIB_RX], new_port[VLIB_RX],
-                            ip4_header_t /* cheat */ ,
-                            length /* changed member */ );
-    }
+  *sum = ip_csum_update (*sum, old_port[VLIB_RX], new_port[VLIB_RX],
+                        udp_header_t, src_port);
 }
 
 static_always_inline void
@@ -503,26 +494,20 @@ cnat_translation_icmp6_echo (ip6_header_t * ip6, icmp46_header_t * icmp,
   sum = icmp->checksum;
 
   cnat_ip6_translate_l3 (ip6, new_addr);
-  if (has_ip6_address (&new_addr[VLIB_TX]))
-    {
-      sum = ip_csum_add_even (sum, new_addr[VLIB_TX].as_u64[0]);
-      sum = ip_csum_add_even (sum, new_addr[VLIB_TX].as_u64[1]);
-      sum = ip_csum_sub_even (sum, old_addr[VLIB_TX].as_u64[0]);
-      sum = ip_csum_sub_even (sum, old_addr[VLIB_TX].as_u64[1]);
-    }
 
-  if (has_ip6_address (&new_addr[VLIB_RX]))
-    {
-      sum = ip_csum_add_even (sum, new_addr[VLIB_RX].as_u64[0]);
-      sum = ip_csum_add_even (sum, new_addr[VLIB_RX].as_u64[1]);
-      sum = ip_csum_sub_even (sum, old_addr[VLIB_RX].as_u64[0]);
-      sum = ip_csum_sub_even (sum, old_addr[VLIB_RX].as_u64[1]);
-    }
+  sum = ip_csum_add_even (sum, new_addr[VLIB_TX].as_u64[0]);
+  sum = ip_csum_add_even (sum, new_addr[VLIB_TX].as_u64[1]);
+  sum = ip_csum_sub_even (sum, old_addr[VLIB_TX].as_u64[0]);
+  sum = ip_csum_sub_even (sum, old_addr[VLIB_TX].as_u64[1]);
+
+  sum = ip_csum_add_even (sum, new_addr[VLIB_RX].as_u64[0]);
+  sum = ip_csum_add_even (sum, new_addr[VLIB_RX].as_u64[1]);
+  sum = ip_csum_sub_even (sum, old_addr[VLIB_RX].as_u64[0]);
+  sum = ip_csum_sub_even (sum, old_addr[VLIB_RX].as_u64[1]);
 
   echo->identifier = new_port[VLIB_RX];
-  sum = ip_csum_update (sum, old_port, new_port[VLIB_RX],
-                       ip4_header_t /* cheat */ ,
-                       length /* changed member */ );
+  sum =
+    ip_csum_update (sum, old_port, new_port[VLIB_RX], udp_header_t, src_port);
 
   icmp->checksum = ip_csum_fold (sum);
 }
@@ -566,79 +551,64 @@ cnat_translation_icmp6_error (ip6_header_t * outer_ip6,
   if (!snat_outer_ip)
     ip6_address_copy (&outer_new_addr[VLIB_RX], &outer_ip6->src_address);
   cnat_ip6_translate_l3 (outer_ip6, outer_new_addr);
-  if (has_ip6_address (&outer_new_addr[VLIB_TX]))
-    {
-      sum = ip_csum_add_even (sum, outer_new_addr[VLIB_TX].as_u64[0]);
-      sum = ip_csum_add_even (sum, outer_new_addr[VLIB_TX].as_u64[1]);
-      sum = ip_csum_sub_even (sum, outer_old_addr[VLIB_TX].as_u64[0]);
-      sum = ip_csum_sub_even (sum, outer_old_addr[VLIB_TX].as_u64[1]);
-    }
 
-  if (has_ip6_address (&outer_new_addr[VLIB_RX]))
-    {
-      sum = ip_csum_add_even (sum, outer_new_addr[VLIB_RX].as_u64[0]);
-      sum = ip_csum_add_even (sum, outer_new_addr[VLIB_RX].as_u64[1]);
-      sum = ip_csum_sub_even (sum, outer_old_addr[VLIB_RX].as_u64[0]);
-      sum = ip_csum_sub_even (sum, outer_old_addr[VLIB_RX].as_u64[1]);
-    }
+  sum = ip_csum_add_even (sum, outer_new_addr[VLIB_TX].as_u64[0]);
+  sum = ip_csum_add_even (sum, outer_new_addr[VLIB_TX].as_u64[1]);
+  sum = ip_csum_sub_even (sum, outer_old_addr[VLIB_TX].as_u64[0]);
+  sum = ip_csum_sub_even (sum, outer_old_addr[VLIB_TX].as_u64[1]);
+
+  sum = ip_csum_add_even (sum, outer_new_addr[VLIB_RX].as_u64[0]);
+  sum = ip_csum_add_even (sum, outer_new_addr[VLIB_RX].as_u64[1]);
+  sum = ip_csum_sub_even (sum, outer_old_addr[VLIB_RX].as_u64[0]);
+  sum = ip_csum_sub_even (sum, outer_old_addr[VLIB_RX].as_u64[1]);
 
   /* Translate inner TCP / UDP */
   if (ip6->protocol == IP_PROTOCOL_TCP)
     {
       inner_l4_old_sum = inner_l4_sum = tcp->checksum;
-      cnat_ip6_translate_l4 (ip6, udp, &inner_l4_sum, new_addr, new_port);
+      cnat_ip6_translate_l4 (ip6, udp, &inner_l4_sum, new_addr, new_port,
+                            0 /* oflags */);
       tcp->checksum = ip_csum_fold (inner_l4_sum);
     }
   else if (ip6->protocol == IP_PROTOCOL_UDP)
     {
       inner_l4_old_sum = inner_l4_sum = udp->checksum;
-      cnat_ip6_translate_l4 (ip6, udp, &inner_l4_sum, new_addr, new_port);
+      cnat_ip6_translate_l4 (ip6, udp, &inner_l4_sum, new_addr, new_port,
+                            0 /* oflags */);
       udp->checksum = ip_csum_fold (inner_l4_sum);
     }
   else
     return;
 
   /* UDP/TCP checksum changed */
-  sum = ip_csum_update (sum, inner_l4_old_sum, inner_l4_sum,
-                       ip4_header_t /* cheat */ ,
+  sum = ip_csum_update (sum, inner_l4_old_sum, inner_l4_sum, ip4_header_t,
                        checksum);
 
   /* UDP/TCP Ports changed */
-  if (old_port[VLIB_TX] && new_port[VLIB_TX])
-    sum = ip_csum_update (sum, old_port[VLIB_TX], new_port[VLIB_TX],
-                         ip4_header_t /* cheat */ ,
-                         length /* changed member */ );
-
-  if (old_port[VLIB_RX] && new_port[VLIB_RX])
-    sum = ip_csum_update (sum, old_port[VLIB_RX], new_port[VLIB_RX],
-                         ip4_header_t /* cheat */ ,
-                         length /* changed member */ );
+  sum = ip_csum_update (sum, old_port[VLIB_TX], new_port[VLIB_TX],
+                       udp_header_t, dst_port);
 
+  sum = ip_csum_update (sum, old_port[VLIB_RX], new_port[VLIB_RX],
+                       udp_header_t, src_port);
 
   cnat_ip6_translate_l3 (ip6, new_addr);
   /* IP src/dst addr changed */
-  if (has_ip6_address (&new_addr[VLIB_TX]))
-    {
-      sum = ip_csum_add_even (sum, new_addr[VLIB_TX].as_u64[0]);
-      sum = ip_csum_add_even (sum, new_addr[VLIB_TX].as_u64[1]);
-      sum = ip_csum_sub_even (sum, old_addr[VLIB_TX].as_u64[0]);
-      sum = ip_csum_sub_even (sum, old_addr[VLIB_TX].as_u64[1]);
-    }
+  sum = ip_csum_add_even (sum, new_addr[VLIB_TX].as_u64[0]);
+  sum = ip_csum_add_even (sum, new_addr[VLIB_TX].as_u64[1]);
+  sum = ip_csum_sub_even (sum, old_addr[VLIB_TX].as_u64[0]);
+  sum = ip_csum_sub_even (sum, old_addr[VLIB_TX].as_u64[1]);
 
-  if (has_ip6_address (&new_addr[VLIB_RX]))
-    {
-      sum = ip_csum_add_even (sum, new_addr[VLIB_RX].as_u64[0]);
-      sum = ip_csum_add_even (sum, new_addr[VLIB_RX].as_u64[1]);
-      sum = ip_csum_sub_even (sum, old_addr[VLIB_RX].as_u64[0]);
-      sum = ip_csum_sub_even (sum, old_addr[VLIB_RX].as_u64[1]);
-    }
+  sum = ip_csum_add_even (sum, new_addr[VLIB_RX].as_u64[0]);
+  sum = ip_csum_add_even (sum, new_addr[VLIB_RX].as_u64[1]);
+  sum = ip_csum_sub_even (sum, old_addr[VLIB_RX].as_u64[0]);
+  sum = ip_csum_sub_even (sum, old_addr[VLIB_RX].as_u64[1]);
 
   icmp->checksum = ip_csum_fold (sum);
 }
 
 static_always_inline void
-cnat_translation_ip6 (const cnat_session_t * session,
-                     ip6_header_t * ip6, udp_header_t * udp)
+cnat_translation_ip6 (const cnat_session_t *session, ip6_header_t *ip6,
+                     udp_header_t *udp, u32 oflags)
 {
   tcp_header_t *tcp = (tcp_header_t *) udp;
   ip6_address_t new_addr[VLIB_N_DIR];
@@ -652,7 +622,7 @@ cnat_translation_ip6 (const cnat_session_t * session,
   if (ip6->protocol == IP_PROTOCOL_TCP)
     {
       ip_csum_t sum = tcp->checksum;
-      cnat_ip6_translate_l4 (ip6, udp, &sum, new_addr, new_port);
+      cnat_ip6_translate_l4 (ip6, udp, &sum, new_addr, new_port, oflags);
       tcp->checksum = ip_csum_fold (sum);
       cnat_ip6_translate_l3 (ip6, new_addr);
       cnat_tcp_update_session_lifetime (tcp, session->value.cs_ts_index);
@@ -660,7 +630,7 @@ cnat_translation_ip6 (const cnat_session_t * session,
   else if (ip6->protocol == IP_PROTOCOL_UDP)
     {
       ip_csum_t sum = udp->checksum;
-      cnat_ip6_translate_l4 (ip6, udp, &sum, new_addr, new_port);
+      cnat_ip6_translate_l4 (ip6, udp, &sum, new_addr, new_port, oflags);
       udp->checksum = ip_csum_fold (sum);
       cnat_ip6_translate_l3 (ip6, new_addr);
     }
@@ -743,6 +713,18 @@ cnat_session_make_key (vlib_buffer_t *b, ip_address_family_t af,
          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;
     }
@@ -837,10 +819,20 @@ cnat_load_balance (const cnat_translation_t *ct, ip_address_family_t af,
  * rsession_location is the location the (return) session will be
  * matched at
  */
+
 static_always_inline void
-cnat_session_create (cnat_session_t *session, cnat_node_ctx_t *ctx,
-                    cnat_session_location_t rsession_location,
-                    u8 rsession_flags)
+cnat_session_create (cnat_session_t *session, cnat_node_ctx_t *ctx)
+{
+  cnat_bihash_kv_t *bkey = (cnat_bihash_kv_t *) session;
+
+  session->value.cs_ts_index = cnat_timestamp_new (ctx->now);
+  cnat_bihash_add_del (&cnat_session_db, bkey, 1);
+}
+
+static_always_inline void
+cnat_rsession_create (cnat_session_t *session, cnat_node_ctx_t *ctx,
+                     cnat_session_location_t rsession_location,
+                     cnat_session_flag_t rsession_flags)
 {
   cnat_client_t *cc;
   cnat_bihash_kv_t rkey;
@@ -849,7 +841,7 @@ cnat_session_create (cnat_session_t *session, cnat_node_ctx_t *ctx,
   int rv, n_retries = 0;
   static u32 sport_seed = 0;
 
-  session->value.cs_ts_index = cnat_timestamp_new (ctx->now);
+  cnat_timestamp_inc_refcnt (session->value.cs_ts_index);
 
   /* First create the return session */
   ip46_address_copy (&rsession->key.cs_ip[VLIB_RX],