nat: respect udp checksum
[vpp.git] / src / plugins / nat / out2in_ed.c
index 06e72f3..2067c51 100644 (file)
 #include <nat/nat_syslog.h>
 #include <nat/nat_ha.h>
 
-#define foreach_nat_out2in_ed_error                     \
-_(UNSUPPORTED_PROTOCOL, "unsupported protocol")         \
-_(OUT2IN_PACKETS, "good out2in packets processed")      \
-_(OUT_OF_PORTS, "out of ports")                         \
-_(BAD_ICMP_TYPE, "unsupported ICMP type")               \
-_(NO_TRANSLATION, "no translation")                     \
-_(MAX_SESSIONS_EXCEEDED, "maximum sessions exceeded")   \
-_(DROP_FRAGMENT, "drop fragment")                       \
-_(MAX_REASS, "maximum reassemblies exceeded")           \
-_(MAX_FRAG, "maximum fragments per reassembly exceeded")\
-_(NON_SYN, "non-SYN packet try to create session")      \
-_(TCP_PACKETS, "TCP packets")                           \
-_(UDP_PACKETS, "UDP packets")                           \
-_(ICMP_PACKETS, "ICMP packets")                         \
-_(OTHER_PACKETS, "other protocol packets")              \
-_(FRAGMENTS, "fragments")                               \
-_(CACHED_FRAGMENTS, "cached fragments")                 \
-_(PROCESSED_FRAGMENTS, "processed fragments")
-
-typedef enum
-{
-#define _(sym,str) NAT_OUT2IN_ED_ERROR_##sym,
-  foreach_nat_out2in_ed_error
-#undef _
-    NAT_OUT2IN_ED_N_ERROR,
-} nat_out2in_ed_error_t;
-
 static char *nat_out2in_ed_error_strings[] = {
 #define _(sym,string) string,
   foreach_nat_out2in_ed_error
@@ -167,7 +140,7 @@ nat44_o2i_ed_is_idle_session_cb (clib_bihash_kv_16_8_t * kv, void *arg)
       ed_kv.key[0] = ed_key.as_u64[0];
       ed_kv.key[1] = ed_key.as_u64[1];
       if (clib_bihash_add_del_16_8 (&tsm->in2out_ed, &ed_kv, 0))
-       nat_log_warn ("in2out_ed key del failed");
+       nat_elog_warn ("in2out_ed key del failed");
 
       if (snat_is_unk_proto_session (s))
        goto delete;
@@ -244,14 +217,14 @@ create_session_for_static_mapping_ed (snat_main_t * sm,
   if (PREDICT_FALSE (maximum_sessions_exceeded (sm, thread_index)))
     {
       b->error = node->errors[NAT_OUT2IN_ED_ERROR_MAX_SESSIONS_EXCEEDED];
-      nat_log_notice ("maximum sessions exceeded");
+      nat_elog_notice ("maximum sessions exceeded");
       return 0;
     }
 
   u = nat_user_get_or_create (sm, &l_key.addr, l_key.fib_index, thread_index);
   if (!u)
     {
-      nat_log_warn ("create NAT user failed");
+      nat_elog_warn ("create NAT user failed");
       return 0;
     }
 
@@ -259,7 +232,7 @@ create_session_for_static_mapping_ed (snat_main_t * sm,
   if (!s)
     {
       nat44_delete_user_with_no_session (sm, u, thread_index);
-      nat_log_warn ("create NAT session failed");
+      nat_elog_warn ("create NAT session failed");
       return 0;
     }
 
@@ -288,7 +261,7 @@ create_session_for_static_mapping_ed (snat_main_t * sm,
   if (clib_bihash_add_or_overwrite_stale_16_8 (&tsm->out2in_ed, &kv,
                                               nat44_o2i_ed_is_idle_session_cb,
                                               &ctx))
-    nat_log_notice ("out2in-ed key add failed");
+    nat_elog_notice ("out2in-ed key add failed");
 
   if (twice_nat == TWICE_NAT || (twice_nat == TWICE_NAT_SELF &&
                                 ip->src_address.as_u32 == l_key.addr.as_u32))
@@ -302,7 +275,7 @@ create_session_for_static_mapping_ed (snat_main_t * sm,
          b->error = node->errors[NAT_OUT2IN_ED_ERROR_OUT_OF_PORTS];
          nat44_delete_session (sm, s, thread_index);
          if (clib_bihash_add_del_16_8 (&tsm->out2in_ed, &kv, 0))
-           nat_log_notice ("out2in-ed key del failed");
+           nat_elog_notice ("out2in-ed key del failed");
          return 0;
        }
       s->ext_host_nat_addr.as_u32 = eh_key.addr.as_u32;
@@ -320,7 +293,7 @@ create_session_for_static_mapping_ed (snat_main_t * sm,
   if (clib_bihash_add_or_overwrite_stale_16_8 (&tsm->in2out_ed, &kv,
                                               nat44_i2o_ed_is_idle_session_cb,
                                               &ctx))
-    nat_log_notice ("in2out-ed key add failed");
+    nat_elog_notice ("in2out-ed key add failed");
 
   snat_ipfix_logging_nat44_ses_create (thread_index,
                                       s->in2out.addr.as_u32,
@@ -345,55 +318,6 @@ create_session_for_static_mapping_ed (snat_main_t * sm,
   return s;
 }
 
-static_always_inline int
-icmp_get_ed_key (ip4_header_t * ip0, nat_ed_ses_key_t * p_key0)
-{
-  icmp46_header_t *icmp0;
-  nat_ed_ses_key_t key0;
-  icmp_echo_header_t *echo0, *inner_echo0 = 0;
-  ip4_header_t *inner_ip0;
-  void *l4_header = 0;
-  icmp46_header_t *inner_icmp0;
-
-  icmp0 = (icmp46_header_t *) ip4_next_header (ip0);
-  echo0 = (icmp_echo_header_t *) (icmp0 + 1);
-
-  if (!icmp_is_error_message (icmp0))
-    {
-      key0.proto = IP_PROTOCOL_ICMP;
-      key0.l_addr = ip0->dst_address;
-      key0.r_addr = ip0->src_address;
-      key0.l_port = echo0->identifier;
-      key0.r_port = 0;
-    }
-  else
-    {
-      inner_ip0 = (ip4_header_t *) (echo0 + 1);
-      l4_header = ip4_next_header (inner_ip0);
-      key0.proto = inner_ip0->protocol;
-      key0.l_addr = inner_ip0->src_address;
-      key0.r_addr = inner_ip0->dst_address;
-      switch (ip_proto_to_snat_proto (inner_ip0->protocol))
-       {
-       case SNAT_PROTOCOL_ICMP:
-         inner_icmp0 = (icmp46_header_t *) l4_header;
-         inner_echo0 = (icmp_echo_header_t *) (inner_icmp0 + 1);
-         key0.l_port = inner_echo0->identifier;
-         key0.r_port = 0;
-         break;
-       case SNAT_PROTOCOL_UDP:
-       case SNAT_PROTOCOL_TCP:
-         key0.l_port = ((tcp_udp_header_t *) l4_header)->src_port;
-         key0.r_port = ((tcp_udp_header_t *) l4_header)->dst_port;
-         break;
-       default:
-         return -1;
-       }
-    }
-  *p_key0 = key0;
-  return 0;
-}
-
 static int
 next_src_nat (snat_main_t * sm, ip4_header_t * ip, u8 proto, u16 src_port,
              u16 dst_port, u32 thread_index, u32 rx_fib_index)
@@ -423,7 +347,7 @@ create_bypass_for_fwd (snat_main_t * sm, ip4_header_t * ip, u32 rx_fib_index,
 
   if (ip->protocol == IP_PROTOCOL_ICMP)
     {
-      if (icmp_get_ed_key (ip, &key))
+      if (get_icmp_o2i_ed_key (ip, &key))
        return;
     }
   else if (ip->protocol == IP_PROTOCOL_UDP || ip->protocol == IP_PROTOCOL_TCP)
@@ -461,7 +385,7 @@ create_bypass_for_fwd (snat_main_t * sm, ip4_header_t * ip, u32 rx_fib_index,
                                  thread_index);
       if (!u)
        {
-         nat_log_warn ("create NAT user failed");
+         nat_elog_warn ("create NAT user failed");
          return;
        }
 
@@ -469,7 +393,7 @@ create_bypass_for_fwd (snat_main_t * sm, ip4_header_t * ip, u32 rx_fib_index,
       if (!s)
        {
          nat44_delete_user_with_no_session (sm, u, thread_index);
-         nat_log_warn ("create NAT session failed");
+         nat_elog_warn ("create NAT session failed");
          return;
        }
 
@@ -492,7 +416,7 @@ create_bypass_for_fwd (snat_main_t * sm, ip4_header_t * ip, u32 rx_fib_index,
 
       kv.value = s - tsm->sessions;
       if (clib_bihash_add_del_16_8 (&tsm->in2out_ed, &kv, 1))
-       nat_log_notice ("in2out_ed key add failed");
+       nat_elog_notice ("in2out_ed key add failed");
     }
 
   if (ip->protocol == IP_PROTOCOL_TCP)
@@ -508,6 +432,18 @@ create_bypass_for_fwd (snat_main_t * sm, ip4_header_t * ip, u32 rx_fib_index,
   nat44_session_update_lru (sm, s, thread_index);
 }
 
+static inline void
+create_bypass_for_fwd_worker (snat_main_t * sm, ip4_header_t * ip,
+                             u32 rx_fib_index)
+{
+  ip4_header_t ip_wkr = {
+    .src_address = ip->dst_address,
+  };
+  u32 thread_index = sm->worker_in2out_cb (&ip_wkr, rx_fib_index, 0);
+
+  create_bypass_for_fwd (sm, ip, rx_fib_index, thread_index);
+}
+
 #ifndef CLIB_MARCH_VARIANT
 u32
 icmp_match_out2in_ed (snat_main_t * sm, vlib_node_runtime_t * node,
@@ -528,7 +464,7 @@ icmp_match_out2in_ed (snat_main_t * sm, vlib_node_runtime_t * node,
   sw_if_index = vnet_buffer (b)->sw_if_index[VLIB_RX];
   rx_fib_index = ip4_fib_table_get_index_for_sw_if_index (sw_if_index);
 
-  if (icmp_get_ed_key (ip, &key))
+  if (get_icmp_o2i_ed_key (ip, &key))
     {
       b->error = node->errors[NAT_OUT2IN_ED_ERROR_UNSUPPORTED_PROTOCOL];
       next = NAT44_ED_OUT2IN_NEXT_DROP;
@@ -570,7 +506,10 @@ icmp_match_out2in_ed (snat_main_t * sm, vlib_node_runtime_t * node,
                  next = NAT44_ED_OUT2IN_NEXT_IN2OUT;
                  goto out;
                }
-             create_bypass_for_fwd (sm, ip, rx_fib_index, thread_index);
+             if (sm->num_workers > 1)
+               create_bypass_for_fwd_worker (sm, ip, rx_fib_index);
+             else
+               create_bypass_for_fwd (sm, ip, rx_fib_index, thread_index);
              goto out;
            }
        }
@@ -659,7 +598,7 @@ nat44_ed_out2in_unknown_proto (snat_main_t * sm,
       if (PREDICT_FALSE (maximum_sessions_exceeded (sm, thread_index)))
        {
          b->error = node->errors[NAT_OUT2IN_ED_ERROR_MAX_SESSIONS_EXCEEDED];
-         nat_log_notice ("maximum sessions exceeded");
+         nat_elog_notice ("maximum sessions exceeded");
          return 0;
        }
 
@@ -679,7 +618,7 @@ nat44_ed_out2in_unknown_proto (snat_main_t * sm,
                                  thread_index);
       if (!u)
        {
-         nat_log_warn ("create NAT user failed");
+         nat_elog_warn ("create NAT user failed");
          return 0;
        }
 
@@ -688,7 +627,7 @@ nat44_ed_out2in_unknown_proto (snat_main_t * sm,
       if (!s)
        {
          nat44_delete_user_with_no_session (sm, u, thread_index);
-         nat_log_warn ("create NAT session failed");
+         nat_elog_warn ("create NAT session failed");
          return 0;
        }
 
@@ -706,13 +645,13 @@ nat44_ed_out2in_unknown_proto (snat_main_t * sm,
       /* Add to lookup tables */
       s_kv.value = s - tsm->sessions;
       if (clib_bihash_add_del_16_8 (&tsm->out2in_ed, &s_kv, 1))
-       nat_log_notice ("out2in key add failed");
+       nat_elog_notice ("out2in key add failed");
 
       make_ed_kv (&s_kv, &ip->dst_address, &ip->src_address, ip->protocol,
                  m->fib_index, 0, 0);
       s_kv.value = s - tsm->sessions;
       if (clib_bihash_add_del_16_8 (&tsm->in2out_ed, &s_kv, 1))
-       nat_log_notice ("in2out key add failed");
+       nat_elog_notice ("in2out key add failed");
     }
 
   /* Update IP checksum */
@@ -923,8 +862,12 @@ nat44_ed_out2in_node_fn_inline (vlib_main_t * vm,
                              next0 = NAT44_ED_OUT2IN_NEXT_IN2OUT;
                              goto trace00;
                            }
-                         create_bypass_for_fwd (sm, ip0, rx_fib_index0,
-                                                thread_index);
+                         if (sm->num_workers > 1)
+                           create_bypass_for_fwd_worker (sm, ip0,
+                                                         rx_fib_index0);
+                         else
+                           create_bypass_for_fwd (sm, ip0, rx_fib_index0,
+                                                  thread_index);
                        }
                      goto trace00;
                    }
@@ -976,11 +919,11 @@ nat44_ed_out2in_node_fn_inline (vlib_main_t * vm,
                                   src_address);
          ip0->checksum = ip_csum_fold (sum0);
 
+         old_port0 = udp0->dst_port;
+         new_port0 = udp0->dst_port = s0->in2out.port;
+
          if (PREDICT_TRUE (proto0 == SNAT_PROTOCOL_TCP))
            {
-             old_port0 = tcp0->dst_port;
-             new_port0 = tcp0->dst_port = s0->in2out.port;
-
              sum0 = tcp0->checksum;
              sum0 = ip_csum_update (sum0, old_addr0, new_addr0, ip4_header_t,
                                     dst_address);
@@ -1003,15 +946,34 @@ nat44_ed_out2in_node_fn_inline (vlib_main_t * vm,
                  (sm, s0, tcp0, thread_index))
                goto trace00;
            }
+         else if (udp0->checksum)
+           {
+             sum0 = udp0->checksum;
+             sum0 = ip_csum_update (sum0, old_addr0, new_addr0, ip4_header_t,
+                                    dst_address);
+             sum0 = ip_csum_update (sum0, old_port0, new_port0, ip4_header_t,
+                                    length);
+             if (PREDICT_FALSE (is_twice_nat_session (s0)))
+               {
+                 sum0 = ip_csum_update (sum0, ip0->src_address.as_u32,
+                                        s0->ext_host_nat_addr.as_u32,
+                                        ip4_header_t, dst_address);
+                 sum0 = ip_csum_update (sum0, udp0->src_port,
+                                        s0->ext_host_nat_port, ip4_header_t,
+                                        length);
+                 udp0->src_port = s0->ext_host_nat_port;
+                 ip0->src_address.as_u32 = s0->ext_host_nat_addr.as_u32;
+               }
+             udp0->checksum = ip_csum_fold (sum0);
+             udp_packets++;
+           }
          else
            {
-             udp0->dst_port = s0->in2out.port;
-             if (is_twice_nat_session (s0))
+             if (PREDICT_FALSE (is_twice_nat_session (s0)))
                {
                  udp0->src_port = s0->ext_host_nat_port;
                  ip0->src_address.as_u32 = s0->ext_host_nat_addr.as_u32;
                }
-             udp0->checksum = 0;
              udp_packets++;
            }
 
@@ -1157,8 +1119,12 @@ nat44_ed_out2in_node_fn_inline (vlib_main_t * vm,
                              next1 = NAT44_ED_OUT2IN_NEXT_IN2OUT;
                              goto trace01;
                            }
-                         create_bypass_for_fwd (sm, ip1, rx_fib_index1,
-                                                thread_index);
+                         if (sm->num_workers > 1)
+                           create_bypass_for_fwd_worker (sm, ip1,
+                                                         rx_fib_index1);
+                         else
+                           create_bypass_for_fwd (sm, ip1, rx_fib_index1,
+                                                  thread_index);
                        }
                      goto trace01;
                    }
@@ -1210,11 +1176,11 @@ nat44_ed_out2in_node_fn_inline (vlib_main_t * vm,
                                   src_address);
          ip1->checksum = ip_csum_fold (sum1);
 
+         old_port1 = udp1->dst_port;
+         new_port1 = udp1->dst_port = s1->in2out.port;
+
          if (PREDICT_TRUE (proto1 == SNAT_PROTOCOL_TCP))
            {
-             old_port1 = tcp1->dst_port;
-             new_port1 = tcp1->dst_port = s1->in2out.port;
-
              sum1 = tcp1->checksum;
              sum1 = ip_csum_update (sum1, old_addr1, new_addr1, ip4_header_t,
                                     dst_address);
@@ -1237,15 +1203,34 @@ nat44_ed_out2in_node_fn_inline (vlib_main_t * vm,
                  (sm, s1, tcp1, thread_index))
                goto trace01;
            }
+         else if (udp1->checksum)
+           {
+             sum1 = udp1->checksum;
+             sum1 = ip_csum_update (sum1, old_addr1, new_addr1, ip4_header_t,
+                                    dst_address);
+             sum1 = ip_csum_update (sum1, old_port1, new_port1, ip4_header_t,
+                                    length);
+             if (PREDICT_FALSE (is_twice_nat_session (s1)))
+               {
+                 sum1 = ip_csum_update (sum1, ip1->src_address.as_u32,
+                                        s1->ext_host_nat_addr.as_u32,
+                                        ip4_header_t, dst_address);
+                 sum1 = ip_csum_update (sum1, udp1->src_port,
+                                        s1->ext_host_nat_port, ip4_header_t,
+                                        length);
+                 udp1->src_port = s1->ext_host_nat_port;
+                 ip1->src_address.as_u32 = s1->ext_host_nat_addr.as_u32;
+               }
+             udp1->checksum = ip_csum_fold (sum1);
+             udp_packets++;
+           }
          else
            {
-             udp1->dst_port = s1->in2out.port;
-             if (is_twice_nat_session (s1))
+             if (PREDICT_FALSE (is_twice_nat_session (s1)))
                {
                  udp1->src_port = s1->ext_host_nat_port;
                  ip1->src_address.as_u32 = s1->ext_host_nat_addr.as_u32;
                }
-             udp1->checksum = 0;
              udp_packets++;
            }
 
@@ -1425,8 +1410,12 @@ nat44_ed_out2in_node_fn_inline (vlib_main_t * vm,
                              next0 = NAT44_ED_OUT2IN_NEXT_IN2OUT;
                              goto trace0;
                            }
-                         create_bypass_for_fwd (sm, ip0, rx_fib_index0,
-                                                thread_index);
+                         if (sm->num_workers > 1)
+                           create_bypass_for_fwd_worker (sm, ip0,
+                                                         rx_fib_index0);
+                         else
+                           create_bypass_for_fwd (sm, ip0, rx_fib_index0,
+                                                  thread_index);
                        }
                      goto trace0;
                    }
@@ -1478,11 +1467,11 @@ nat44_ed_out2in_node_fn_inline (vlib_main_t * vm,
                                   src_address);
          ip0->checksum = ip_csum_fold (sum0);
 
+         old_port0 = udp0->dst_port;
+         new_port0 = udp0->dst_port = s0->in2out.port;
+
          if (PREDICT_TRUE (proto0 == SNAT_PROTOCOL_TCP))
            {
-             old_port0 = tcp0->dst_port;
-             new_port0 = tcp0->dst_port = s0->in2out.port;
-
              sum0 = tcp0->checksum;
              sum0 = ip_csum_update (sum0, old_addr0, new_addr0, ip4_header_t,
                                     dst_address);
@@ -1505,15 +1494,34 @@ nat44_ed_out2in_node_fn_inline (vlib_main_t * vm,
                  (sm, s0, tcp0, thread_index))
                goto trace0;
            }
+         else if (udp0->checksum)
+           {
+             sum0 = udp0->checksum;
+             sum0 = ip_csum_update (sum0, old_addr0, new_addr0, ip4_header_t,
+                                    dst_address);
+             sum0 = ip_csum_update (sum0, old_port0, new_port0, ip4_header_t,
+                                    length);
+             if (PREDICT_FALSE (is_twice_nat_session (s0)))
+               {
+                 sum0 = ip_csum_update (sum0, ip0->src_address.as_u32,
+                                        s0->ext_host_nat_addr.as_u32,
+                                        ip4_header_t, dst_address);
+                 sum0 = ip_csum_update (sum0, udp0->src_port,
+                                        s0->ext_host_nat_port, ip4_header_t,
+                                        length);
+                 udp0->src_port = s0->ext_host_nat_port;
+                 ip0->src_address.as_u32 = s0->ext_host_nat_addr.as_u32;
+               }
+             udp0->checksum = ip_csum_fold (sum0);
+             udp_packets++;
+           }
          else
            {
-             udp0->dst_port = s0->in2out.port;
-             if (is_twice_nat_session (s0))
+             if (PREDICT_FALSE (is_twice_nat_session (s0)))
                {
                  udp0->src_port = s0->ext_host_nat_port;
                  ip0->src_address.as_u32 = s0->ext_host_nat_addr.as_u32;
                }
-             udp0->checksum = 0;
              udp_packets++;
            }
 
@@ -1706,7 +1714,7 @@ VLIB_NODE_FN (nat44_ed_out2in_reass_node) (vlib_main_t * vm,
            {
              next0 = NAT44_ED_OUT2IN_NEXT_DROP;
              b0->error = node->errors[NAT_OUT2IN_ED_ERROR_MAX_REASS];
-             nat_log_notice ("maximum reassemblies exceeded");
+             nat_elog_notice ("maximum reassemblies exceeded");
              goto trace0;
            }
 
@@ -1778,8 +1786,12 @@ VLIB_NODE_FN (nat44_ed_out2in_reass_node) (vlib_main_t * vm,
                              next0 = NAT44_ED_OUT2IN_NEXT_IN2OUT;
                              goto trace0;
                            }
-                         create_bypass_for_fwd (sm, ip0, rx_fib_index0,
-                                                thread_index);
+                         if (sm->num_workers > 1)
+                           create_bypass_for_fwd_worker (sm, ip0,
+                                                         rx_fib_index0);
+                         else
+                           create_bypass_for_fwd (sm, ip0, rx_fib_index0,
+                                                  thread_index);
                          reass0->flags |= NAT_REASS_FLAG_ED_DONT_TRANSLATE;
                          nat_ip4_reass_get_frags (reass0,
                                                   &fragments_to_loopback);
@@ -1834,7 +1846,7 @@ VLIB_NODE_FN (nat44_ed_out2in_reass_node) (vlib_main_t * vm,
                      (thread_index, reass0, bi0, &fragments_to_drop))
                    {
                      b0->error = node->errors[NAT_OUT2IN_ED_ERROR_MAX_FRAG];
-                     nat_log_notice
+                     nat_elog_notice
                        ("maximum fragments per reassembly exceeded");
                      next0 = NAT44_ED_OUT2IN_NEXT_DROP;
                      goto trace0;
@@ -1863,12 +1875,11 @@ VLIB_NODE_FN (nat44_ed_out2in_reass_node) (vlib_main_t * vm,
 
          if (PREDICT_FALSE (ip4_is_first_fragment (ip0)))
            {
+             old_port0 = udp0->dst_port;
+             new_port0 = udp0->dst_port = s0->in2out.port;
+
              if (PREDICT_TRUE (proto0 == SNAT_PROTOCOL_TCP))
                {
-                 old_port0 = tcp0->dst_port;
-                 tcp0->dst_port = s0->in2out.port;
-                 new_port0 = tcp0->dst_port;
-
                  sum0 = tcp0->checksum;
                  sum0 = ip_csum_update (sum0, old_addr0, new_addr0,
                                         ip4_header_t,
@@ -1890,16 +1901,35 @@ VLIB_NODE_FN (nat44_ed_out2in_reass_node) (vlib_main_t * vm,
                    }
                  tcp0->checksum = ip_csum_fold (sum0);
                }
+             else if (udp0->checksum)
+               {
+                 sum0 = udp0->checksum;
+                 sum0 =
+                   ip_csum_update (sum0, old_addr0, new_addr0, ip4_header_t,
+                                   dst_address);
+                 sum0 =
+                   ip_csum_update (sum0, old_port0, new_port0, ip4_header_t,
+                                   length);
+                 if (PREDICT_FALSE (is_twice_nat_session (s0)))
+                   {
+                     sum0 = ip_csum_update (sum0, ip0->src_address.as_u32,
+                                            s0->ext_host_nat_addr.as_u32,
+                                            ip4_header_t, dst_address);
+                     sum0 = ip_csum_update (sum0, udp0->src_port,
+                                            s0->ext_host_nat_port,
+                                            ip4_header_t, length);
+                     udp0->src_port = s0->ext_host_nat_port;
+                     ip0->src_address.as_u32 = s0->ext_host_nat_addr.as_u32;
+                   }
+                 udp0->checksum = ip_csum_fold (sum0);
+               }
              else
                {
-                 old_port0 = udp0->dst_port;
-                 udp0->dst_port = s0->in2out.port;
-                 if (is_twice_nat_session (s0))
+                 if (PREDICT_FALSE (is_twice_nat_session (s0)))
                    {
                      udp0->src_port = s0->ext_host_nat_port;
                      ip0->src_address.as_u32 = s0->ext_host_nat_addr.as_u32;
                    }
-                 udp0->checksum = 0;
                }
            }