NAT64: Fallback to 3-tuple key for non TCP/UDP sessions (VPP-884) 14/7514/3
authorMatus Fabian <matfabia@cisco.com>
Tue, 11 Jul 2017 10:55:02 +0000 (03:55 -0700)
committerOle Trøan <otroan@employees.org>
Wed, 16 Aug 2017 08:36:43 +0000 (08:36 +0000)
Change-Id: I4cafc8291725feb499355092bd429433e649b5b2
Signed-off-by: Matus Fabian <matfabia@cisco.com>
src/plugins/snat/nat64.c
src/plugins/snat/nat64_cli.c
src/plugins/snat/nat64_db.c
src/plugins/snat/nat64_db.h
src/plugins/snat/nat64_in2out.c
src/plugins/snat/nat64_out2in.c
src/plugins/snat/snat.c
src/plugins/snat/snat_api.c
src/vnet/ip/ip4_to_ip6.h
src/vnet/ip/ip6_to_ip4.h
test/test_snat.py

index 47809b0..bd915b5 100644 (file)
@@ -125,7 +125,9 @@ nat64_add_del_pool_addr (ip4_address_t * addr, u32 vrf_id, u8 is_add)
       clib_bitmap_free (a->busy_##n##_port_bitmap);
       foreach_snat_protocol
 #undef _
-       vec_del1 (nm->addr_pool, i);
+       /* Delete sessions using address */
+       nat64_db_free_out_addr (&nm->db, &a->addr);
+      vec_del1 (nm->addr_pool, i);
     }
 
   /* Add/del external address to FIB */
@@ -362,7 +364,7 @@ nat64_add_del_static_bib_entry (ip6_address_t * in_addr,
   addr.as_u64[1] = in_addr->as_u64[1];
   bibe =
     nat64_db_bib_entry_find (&nm->db, &addr, clib_host_to_net_u16 (in_port),
-                            p, fib_index, 1);
+                            proto, fib_index, 1);
 
   if (is_add)
     {
@@ -389,8 +391,11 @@ nat64_add_del_static_bib_entry (ip6_address_t * in_addr,
              foreach_snat_protocol
 #undef _
            default:
-             clib_warning ("unknown protocol");
-             return VNET_API_ERROR_INVALID_VALUE_2;
+             memset (&addr, 0, sizeof (addr));
+             addr.ip4.as_u32 = out_addr->as_u32;
+             if (nat64_db_bib_entry_find
+                 (&nm->db, &addr, 0, proto, fib_index, 0))
+               return VNET_API_ERROR_INVALID_VALUE;
            }
          break;
        }
@@ -398,7 +403,7 @@ nat64_add_del_static_bib_entry (ip6_address_t * in_addr,
        nat64_db_bib_entry_create (&nm->db, in_addr, out_addr,
                                   clib_host_to_net_u16 (in_port),
                                   clib_host_to_net_u16 (out_port), fib_index,
-                                  p, 1);
+                                  proto, 1);
       if (!bibe)
        return VNET_API_ERROR_UNSPECIFIED;
     }
@@ -511,7 +516,7 @@ nat64_session_reset_timeout (nat64_db_st_entry_t * ste, vlib_main_t * vm)
   nat64_main_t *nm = &nat64_main;
   u32 now = (u32) vlib_time_now (vm);
 
-  switch (ste->proto)
+  switch (ip_proto_to_snat_proto (ste->proto))
     {
     case SNAT_PROTOCOL_ICMP:
       ste->expire = now + nm->icmp_timeout;
@@ -539,6 +544,7 @@ nat64_session_reset_timeout (nat64_db_st_entry_t * ste, vlib_main_t * vm)
       ste->expire = now + nm->udp_timeout;
       return;
     default:
+      ste->expire = now + nm->udp_timeout;
       return;
     }
 }
@@ -808,9 +814,6 @@ nat64_extract_ip4 (ip6_address_t * ip6, ip4_address_t * ip4, u32 fib_index)
       clib_warning ("invalid prefix length");
       break;
     }
-
-  clib_warning ("%U %U plen %u", format_ip6_address, ip6, format_ip4_address,
-               ip4, plen);
 }
 
 /**
index 32f671d..d48cb72 100644 (file)
@@ -303,7 +303,7 @@ nat64_add_del_static_bib_command_fn (vlib_main_t *
   ip4_address_t out_addr;
   u16 in_port = 0;
   u16 out_port = 0;
-  u32 vrf_id = 0;
+  u32 vrf_id = 0, protocol;
   snat_protocol_t proto = 0;
   u8 p = 0;
   int rv;
@@ -327,6 +327,11 @@ nat64_add_del_static_bib_command_fn (vlib_main_t *
        ;
       else if (unformat (line_input, "%U", unformat_snat_protocol, &proto))
        ;
+      else
+       if (unformat
+           (line_input, "%U %U %u", unformat_ip6_address, &in_addr,
+            unformat_ip4_address, &out_addr, &protocol))
+       p = (u8) protocol;
       else if (unformat (line_input, "del"))
        is_add = 0;
       else
@@ -337,19 +342,24 @@ nat64_add_del_static_bib_command_fn (vlib_main_t *
        }
     }
 
-  if (!in_port)
+  if (!p)
     {
-      error = clib_error_return (0, "inside port and address  must be set");
-      goto done;
-    }
+      if (!in_port)
+       {
+         error =
+           clib_error_return (0, "inside port and address  must be set");
+         goto done;
+       }
 
-  if (!out_port)
-    {
-      error = clib_error_return (0, "outside port and address  must be set");
-      goto done;
-    }
+      if (!out_port)
+       {
+         error =
+           clib_error_return (0, "outside port and address  must be set");
+         goto done;
+       }
 
-  p = snat_proto_to_ip_proto (proto);
+      p = snat_proto_to_ip_proto (proto);
+    }
 
   rv =
     nat64_add_del_static_bib_entry (&in_addr, &out_addr, in_port, out_port, p,
@@ -391,12 +401,27 @@ nat64_cli_bib_walk (nat64_db_bib_entry_t * bibe, void *ctx)
   if (!fib)
     return -1;
 
-  vlib_cli_output (vm, " %U %u %U %u %U vrf %u %s %u sessions",
-                  format_ip6_address, &bibe->in_addr,
-                  clib_net_to_host_u16 (bibe->in_port), format_ip4_address,
-                  &bibe->out_addr, clib_net_to_host_u16 (bibe->out_port),
-                  format_snat_protocol, bibe->proto, fib->ft_table_id,
-                  bibe->is_static ? "static" : "dynamic", bibe->ses_num);
+  switch (bibe->proto)
+    {
+    case IP_PROTOCOL_ICMP:
+    case IP_PROTOCOL_TCP:
+    case IP_PROTOCOL_UDP:
+      vlib_cli_output (vm, " %U %u %U %u protocol %U vrf %u %s %u sessions",
+                      format_ip6_address, &bibe->in_addr,
+                      clib_net_to_host_u16 (bibe->in_port),
+                      format_ip4_address, &bibe->out_addr,
+                      clib_net_to_host_u16 (bibe->out_port),
+                      format_snat_protocol,
+                      ip_proto_to_snat_proto (bibe->proto), fib->ft_table_id,
+                      bibe->is_static ? "static" : "dynamic", bibe->ses_num);
+      break;
+    default:
+      vlib_cli_output (vm, " %U %U protocol %u vrf %u %s %u sessions",
+                      format_ip6_address, &bibe->in_addr,
+                      format_ip4_address, &bibe->out_addr,
+                      bibe->proto, fib->ft_table_id,
+                      bibe->is_static ? "static" : "dynamic", bibe->ses_num);
+    }
   return 0;
 }
 
@@ -407,7 +432,8 @@ nat64_show_bib_command_fn (vlib_main_t * vm,
   nat64_main_t *nm = &nat64_main;
   unformat_input_t _line_input, *line_input = &_line_input;
   clib_error_t *error = 0;
-  snat_protocol_t proto = 0;
+  u32 proto = ~0;
+  u8 p = 0;
 
   if (nm->is_disabled)
     return clib_error_return (0,
@@ -417,6 +443,8 @@ nat64_show_bib_command_fn (vlib_main_t * vm,
     return 0;
 
   if (unformat (line_input, "%U", unformat_snat_protocol, &proto))
+    p = snat_proto_to_ip_proto (proto);
+  else if (unformat (line_input, "unknown"))
     ;
   else
     {
@@ -426,7 +454,7 @@ nat64_show_bib_command_fn (vlib_main_t * vm,
     }
 
   vlib_cli_output (vm, "NAT64 %U BIB:", format_snat_protocol, proto);
-  nat64_db_bib_walk (&nm->db, proto, nat64_cli_bib_walk, vm);
+  nat64_db_bib_walk (&nm->db, p, nat64_cli_bib_walk, vm);
 
 done:
   unformat_free (line_input);
@@ -563,17 +591,18 @@ nat64_cli_st_walk (nat64_db_st_entry_t * ste, void *ctx)
 
   u32 vrf_id = fib->ft_table_id;
 
-  if (ste->proto == SNAT_PROTOCOL_ICMP)
-    vlib_cli_output (vm, " %U %U %u %U %U %u %U vrf %u",
+  if (ste->proto == IP_PROTOCOL_ICMP)
+    vlib_cli_output (vm, " %U %U %u %U %U %u protocol %U vrf %u",
                     format_ip6_address, &bibe->in_addr,
                     format_ip6_address, &ste->in_r_addr,
                     clib_net_to_host_u16 (bibe->in_port),
                     format_ip4_address, &bibe->out_addr,
                     format_ip4_address, &ste->out_r_addr,
                     clib_net_to_host_u16 (bibe->out_port),
-                    format_snat_protocol, bibe->proto, vrf_id);
-  else
-    vlib_cli_output (vm, " %U %u %U %u %U %u %U %u %U vrf %u",
+                    format_snat_protocol,
+                    ip_proto_to_snat_proto (bibe->proto), vrf_id);
+  else if (ste->proto == IP_PROTOCOL_TCP || ste->proto == IP_PROTOCOL_UDP)
+    vlib_cli_output (vm, " %U %u %U %u %U %u %U %u protcol %U vrf %u",
                     format_ip6_address, &bibe->in_addr,
                     clib_net_to_host_u16 (bibe->in_port),
                     format_ip6_address, &ste->in_r_addr,
@@ -582,7 +611,16 @@ nat64_cli_st_walk (nat64_db_st_entry_t * ste, void *ctx)
                     clib_net_to_host_u16 (bibe->out_port),
                     format_ip4_address, &ste->out_r_addr,
                     clib_net_to_host_u16 (ste->r_port),
-                    format_snat_protocol, bibe->proto, vrf_id);
+                    format_snat_protocol,
+                    ip_proto_to_snat_proto (bibe->proto), vrf_id);
+  else
+    vlib_cli_output (vm, " %U %U %U %U protocol %u vrf %u",
+                    format_ip6_address, &bibe->in_addr,
+                    format_ip6_address, &ste->in_r_addr,
+                    format_ip4_address, &bibe->out_addr,
+                    format_ip4_address, &ste->out_r_addr,
+                    bibe->proto, vrf_id);
+
   return 0;
 }
 
@@ -593,7 +631,8 @@ nat64_show_st_command_fn (vlib_main_t * vm,
   nat64_main_t *nm = &nat64_main;
   unformat_input_t _line_input, *line_input = &_line_input;
   clib_error_t *error = 0;
-  snat_protocol_t proto = 0;
+  u32 proto = ~0;
+  u8 p = 0;
 
   if (nm->is_disabled)
     return clib_error_return (0,
@@ -603,6 +642,8 @@ nat64_show_st_command_fn (vlib_main_t * vm,
     return 0;
 
   if (unformat (line_input, "%U", unformat_snat_protocol, &proto))
+    p = snat_proto_to_ip_proto (proto);
+  else if (unformat (line_input, "unknown"))
     ;
   else
     {
@@ -613,7 +654,7 @@ nat64_show_st_command_fn (vlib_main_t * vm,
 
   vlib_cli_output (vm, "NAT64 %U session table:", format_snat_protocol,
                   proto);
-  nat64_db_st_walk (&nm->db, proto, nat64_cli_st_walk, vm);
+  nat64_db_st_walk (&nm->db, p, nat64_cli_st_walk, vm);
 
 done:
   unformat_free (line_input);
@@ -819,7 +860,7 @@ VLIB_CLI_COMMAND (nat64_add_del_static_bib_command, static) = {
 ?*/
 VLIB_CLI_COMMAND (show_nat64_bib_command, static) = {
   .path = "show nat64 bib",
-  .short_help = "show nat64 bib tcp|udp|icmp",
+  .short_help = "show nat64 bib tcp|udp|icmp|unknown",
   .function = nat64_show_bib_command_fn,
 };
 
@@ -883,7 +924,7 @@ VLIB_CLI_COMMAND (show_nat64_timeouts_command, static) = {
 ?*/
 VLIB_CLI_COMMAND (show_nat64_st_command, static) = {
   .path = "show nat64 session table",
-  .short_help = "show nat64 session table tcp|udp|icmp",
+  .short_help = "show nat64 session table tcp|udp|icmp|unknown",
   .function = nat64_show_st_command_fn,
 };
 
index d15761d..b6e199c 100644 (file)
@@ -44,7 +44,7 @@ nat64_db_init (nat64_db_t * db)
 nat64_db_bib_entry_t *
 nat64_db_bib_entry_create (nat64_db_t * db, ip6_address_t * in_addr,
                           ip4_address_t * out_addr, u16 in_port,
-                          u16 out_port, u32 fib_index, snat_protocol_t proto,
+                          u16 out_port, u32 fib_index, u8 proto,
                           u8 is_static)
 {
   nat64_db_bib_entry_t *bibe;
@@ -52,7 +52,7 @@ nat64_db_bib_entry_create (nat64_db_t * db, ip6_address_t * in_addr,
   clib_bihash_kv_24_8_t kv;
 
   /* create pool entry */
-  switch (proto)
+  switch (ip_proto_to_snat_proto (proto))
     {
 /* *INDENT-OFF* */
 #define _(N, i, n, s) \
@@ -64,8 +64,9 @@ nat64_db_bib_entry_create (nat64_db_t * db, ip6_address_t * in_addr,
 #undef _
 /* *INDENT-ON* */
     default:
-      clib_warning ("unknown protocol %u", proto);
-      return 0;
+      pool_get (db->bib._unk_proto_bib, bibe);
+      kv.value = bibe - db->bib._unk_proto_bib;
+      break;
     }
   memset (bibe, 0, sizeof (*bibe));
   bibe->in_addr.as_u64[0] = in_addr->as_u64[0];
@@ -110,7 +111,7 @@ nat64_db_bib_entry_free (nat64_db_t * db, nat64_db_bib_entry_t * bibe)
   u32 *ste_to_be_free = 0, *ste_index, bibe_index;
   nat64_db_st_entry_t *st, *ste;
 
-  switch (bibe->proto)
+  switch (ip_proto_to_snat_proto (bibe->proto))
     {
 /* *INDENT-OFF* */
 #define _(N, i, n, s) \
@@ -122,8 +123,9 @@ nat64_db_bib_entry_free (nat64_db_t * db, nat64_db_bib_entry_t * bibe)
 #undef _
 /* *INDENT-ON* */
     default:
-      clib_warning ("unknown protocol %u", bibe->proto);
-      return;
+      bib = db->bib._unk_proto_bib;
+      st = db->st._unk_proto_st;
+      break;
     }
 
   bibe_index = bibe - bib;
@@ -169,14 +171,14 @@ nat64_db_bib_entry_free (nat64_db_t * db, nat64_db_bib_entry_t * bibe)
 
 nat64_db_bib_entry_t *
 nat64_db_bib_entry_find (nat64_db_t * db, ip46_address_t * addr, u16 port,
-                        snat_protocol_t proto, u32 fib_index, u8 is_ip6)
+                        u8 proto, u32 fib_index, u8 is_ip6)
 {
   nat64_db_bib_entry_t *bibe = 0;
   nat64_db_bib_entry_key_t bibe_key;
   clib_bihash_kv_24_8_t kv, value;
   nat64_db_bib_entry_t *bib;
 
-  switch (proto)
+  switch (ip_proto_to_snat_proto (proto))
     {
 /* *INDENT-OFF* */
 #define _(N, i, n, s) \
@@ -187,8 +189,8 @@ nat64_db_bib_entry_find (nat64_db_t * db, ip46_address_t * addr, u16 port,
 #undef _
 /* *INDENT-ON* */
     default:
-      clib_warning ("unknown protocol %u", proto);
-      return 0;
+      bib = db->bib._unk_proto_bib;
+      break;
     }
 
   bibe_key.addr.as_u64[0] = addr->as_u64[0];
@@ -210,12 +212,12 @@ nat64_db_bib_entry_find (nat64_db_t * db, ip46_address_t * addr, u16 port,
 }
 
 void
-nat64_db_bib_walk (nat64_db_t * db, snat_protocol_t proto,
+nat64_db_bib_walk (nat64_db_t * db, u8 proto,
                   nat64_db_bib_walk_fn_t fn, void *ctx)
 {
   nat64_db_bib_entry_t *bib, *bibe;
 
-  switch (proto)
+  switch (ip_proto_to_snat_proto (proto))
     {
 /* *INDENT-OFF* */
 #define _(N, i, n, s) \
@@ -226,8 +228,8 @@ nat64_db_bib_walk (nat64_db_t * db, snat_protocol_t proto,
 #undef _
 /* *INDENT-ON* */
     default:
-      clib_warning ("unknown protocol");
-      return;
+      bib = db->bib._unk_proto_bib;
+      break;
     }
 
   /* *INDENT-OFF* */
@@ -240,12 +242,11 @@ nat64_db_bib_walk (nat64_db_t * db, snat_protocol_t proto,
 }
 
 nat64_db_bib_entry_t *
-nat64_db_bib_entry_by_index (nat64_db_t * db, snat_protocol_t proto,
-                            u32 bibe_index)
+nat64_db_bib_entry_by_index (nat64_db_t * db, u8 proto, u32 bibe_index)
 {
   nat64_db_bib_entry_t *bib;
 
-  switch (proto)
+  switch (ip_proto_to_snat_proto (proto))
     {
 /* *INDENT-OFF* */
 #define _(N, i, n, s) \
@@ -256,20 +257,20 @@ nat64_db_bib_entry_by_index (nat64_db_t * db, snat_protocol_t proto,
 #undef _
 /* *INDENT-ON* */
     default:
-      clib_warning ("unknown protocol %u", proto);
-      return 0;
+      bib = db->bib._unk_proto_bib;
+      break;
     }
 
   return pool_elt_at_index (bib, bibe_index);
 }
 
 void
-nat64_db_st_walk (nat64_db_t * db, snat_protocol_t proto,
+nat64_db_st_walk (nat64_db_t * db, u8 proto,
                  nat64_db_st_walk_fn_t fn, void *ctx)
 {
   nat64_db_st_entry_t *st, *ste;
 
-  switch (proto)
+  switch (ip_proto_to_snat_proto (proto))
     {
 /* *INDENT-OFF* */
 #define _(N, i, n, s) \
@@ -280,8 +281,8 @@ nat64_db_st_walk (nat64_db_t * db, snat_protocol_t proto,
 #undef _
 /* *INDENT-ON* */
     default:
-      clib_warning ("unknown protocol");
-      return;
+      st = db->st._unk_proto_st;
+      break;
     }
 
   /* *INDENT-OFF* */
@@ -304,7 +305,7 @@ nat64_db_st_entry_create (nat64_db_t * db, nat64_db_bib_entry_t * bibe,
   clib_bihash_kv_48_8_t kv;
 
   /* create pool entry */
-  switch (bibe->proto)
+  switch (ip_proto_to_snat_proto (bibe->proto))
     {
 /* *INDENT-OFF* */
 #define _(N, i, n, s) \
@@ -317,8 +318,10 @@ nat64_db_st_entry_create (nat64_db_t * db, nat64_db_bib_entry_t * bibe,
 #undef _
 /* *INDENT-ON* */
     default:
-      clib_warning ("unknown protocol %u", bibe->proto);
-      return 0;
+      pool_get (db->st._unk_proto_st, ste);
+      kv.value = ste - db->st._unk_proto_st;
+      bib = db->bib._unk_proto_bib;
+      break;
     }
   memset (ste, 0, sizeof (*ste));
   ste->in_r_addr.as_u64[0] = in_r_addr->as_u64[0];
@@ -374,7 +377,7 @@ nat64_db_st_entry_free (nat64_db_t * db, nat64_db_st_entry_t * ste)
   nat64_db_st_entry_key_t ste_key;
   clib_bihash_kv_48_8_t kv;
 
-  switch (ste->proto)
+  switch (ip_proto_to_snat_proto (ste->proto))
     {
 /* *INDENT-OFF* */
 #define _(N, i, n, s) \
@@ -386,8 +389,9 @@ nat64_db_st_entry_free (nat64_db_t * db, nat64_db_st_entry_t * ste)
 #undef _
 /* *INDENT-ON* */
     default:
-      clib_warning ("unknown protocol %u", ste->proto);
-      return;
+      st = db->st._unk_proto_st;
+      bib = db->bib._unk_proto_bib;
+      break;
     }
 
   bibe = pool_elt_at_index (bib, ste->bibe_index);
@@ -438,14 +442,14 @@ nat64_db_st_entry_free (nat64_db_t * db, nat64_db_st_entry_t * ste)
 nat64_db_st_entry_t *
 nat64_db_st_entry_find (nat64_db_t * db, ip46_address_t * l_addr,
                        ip46_address_t * r_addr, u16 l_port, u16 r_port,
-                       snat_protocol_t proto, u32 fib_index, u8 is_ip6)
+                       u8 proto, u32 fib_index, u8 is_ip6)
 {
   nat64_db_st_entry_t *ste = 0;
   nat64_db_st_entry_t *st;
   nat64_db_st_entry_key_t ste_key;
   clib_bihash_kv_48_8_t kv, value;
 
-  switch (proto)
+  switch (ip_proto_to_snat_proto (proto))
     {
 /* *INDENT-OFF* */
 #define _(N, i, n, s) \
@@ -456,8 +460,8 @@ nat64_db_st_entry_find (nat64_db_t * db, ip46_address_t * l_addr,
 #undef _
 /* *INDENT-ON* */
     default:
-      clib_warning ("unknown protocol %u", proto);
-      return ste;
+      st = db->st._unk_proto_st;
+      break;
     }
 
   memset (&ste_key, 0, sizeof (ste_key));
@@ -504,6 +508,47 @@ nad64_db_st_free_expired (nat64_db_t * db, u32 now)
   ste_to_be_free = 0;
   foreach_snat_protocol
 #undef _
+  st = db->st._unk_proto_st;
+  pool_foreach (ste, st, ({
+    if (ste->expire < now)
+      vec_add1 (ste_to_be_free, ste - st);
+  }));
+  vec_foreach (ste_index, ste_to_be_free)
+    nat64_db_st_entry_free (db, pool_elt_at_index(st, ste_index[0]));
+  vec_free (ste_to_be_free);
+/* *INDENT-ON* */
+}
+
+void
+nat64_db_free_out_addr (nat64_db_t * db, ip4_address_t * out_addr)
+{
+  u32 *ste_to_be_free = 0, *ste_index;
+  nat64_db_st_entry_t *st, *ste;
+  nat64_db_bib_entry_t *bibe;
+
+/* *INDENT-OFF* */
+#define _(N, i, n, s) \
+  st = db->st._##n##_st; \
+  pool_foreach (ste, st, ({ \
+    bibe = pool_elt_at_index (db->bib._##n##_bib, ste->bibe_index); \
+    if (bibe->out_addr.as_u32 == out_addr->as_u32) \
+      vec_add1 (ste_to_be_free, ste - st); \
+  })); \
+  vec_foreach (ste_index, ste_to_be_free) \
+    nat64_db_st_entry_free (db, pool_elt_at_index(st, ste_index[0])); \
+  vec_free (ste_to_be_free); \
+  ste_to_be_free = 0;
+  foreach_snat_protocol
+#undef _
+  st = db->st._unk_proto_st;
+  pool_foreach (ste, st, ({
+    bibe = pool_elt_at_index (db->bib._unk_proto_bib, ste->bibe_index);
+    if (bibe->out_addr.as_u32 == out_addr->as_u32)
+      vec_add1 (ste_to_be_free, ste - st);
+  }));
+  vec_foreach (ste_index, ste_to_be_free)
+    nat64_db_st_entry_free (db, pool_elt_at_index(st, ste_index[0]));
+  vec_free (ste_to_be_free);
 /* *INDENT-ON* */
 }
 
index caea3bf..4511fb2 100644 (file)
@@ -63,6 +63,7 @@ typedef struct
   foreach_snat_protocol
 #undef _
 /* *INDENT-ON* */
+  nat64_db_bib_entry_t *_unk_proto_bib;
 
   /* BIB lookup */
   clib_bihash_24_8_t in2out;
@@ -109,6 +110,7 @@ typedef struct
   foreach_snat_protocol
 #undef _
 /* *INDENT-ON* */
+  nat64_db_st_entry_t *_unk_proto_st;
 
   /* session lookup */
   clib_bihash_48_8_t in2out;
@@ -149,8 +151,7 @@ nat64_db_bib_entry_t *nat64_db_bib_entry_create (nat64_db_t * db,
                                                 ip4_address_t * out_addr,
                                                 u16 in_port, u16 out_port,
                                                 u32 fib_index,
-                                                snat_protocol_t proto,
-                                                u8 is_static);
+                                                u8 proto, u8 is_static);
 
 /**
  * @brief Free NAT64 BIB entry.
@@ -170,11 +171,11 @@ typedef int (*nat64_db_bib_walk_fn_t) (nat64_db_bib_entry_t * bibe,
  * @brief Walk NAT64 BIB.
  *
  * @param db NAT64 DB.
- * @param proto BIB protocol (TCP/UDP/ICMP).
+ * @param proto L4 protocol.
  * @param fn The function to invoke on each entry visited.
  * @param ctx A context passed in the visit function.
  */
-void nat64_db_bib_walk (nat64_db_t * db, snat_protocol_t proto,
+void nat64_db_bib_walk (nat64_db_t * db, u8 proto,
                        nat64_db_bib_walk_fn_t fn, void *ctx);
 
 /**
@@ -192,7 +193,7 @@ void nat64_db_bib_walk (nat64_db_t * db, snat_protocol_t proto,
 nat64_db_bib_entry_t *nat64_db_bib_entry_find (nat64_db_t * db,
                                               ip46_address_t * addr,
                                               u16 port,
-                                              snat_protocol_t proto,
+                                              u8 proto,
                                               u32 fib_index, u8 is_ip6);
 
 /**
@@ -205,8 +206,7 @@ nat64_db_bib_entry_t *nat64_db_bib_entry_find (nat64_db_t * db,
  * @return BIB entry if found.
  */
 nat64_db_bib_entry_t *nat64_db_bib_entry_by_index (nat64_db_t * db,
-                                                  snat_protocol_t proto,
-                                                  u32 bibe_index);
+                                                  u8 proto, u32 bibe_index);
 /**
  * @brief Create new NAT64 session table entry.
  *
@@ -250,7 +250,7 @@ nat64_db_st_entry_t *nat64_db_st_entry_find (nat64_db_t * db,
                                             ip46_address_t * l_addr,
                                             ip46_address_t * r_addr,
                                             u16 l_port, u16 r_port,
-                                            snat_protocol_t proto,
+                                            u8 proto,
                                             u32 fib_index, u8 is_ip6);
 
 /**
@@ -263,11 +263,11 @@ typedef int (*nat64_db_st_walk_fn_t) (nat64_db_st_entry_t * ste, void *ctx);
  * @brief Walk NAT64 session table.
  *
  * @param db NAT64 DB.
- * @param proto Session table protocol (TCP/UDP/ICMP).
+ * @param proto L4 protocol.
  * @param fn The function to invoke on each entry visited.
  * @param ctx A context passed in the visit function.
  */
-void nat64_db_st_walk (nat64_db_t * db, snat_protocol_t proto,
+void nat64_db_st_walk (nat64_db_t * db, u8 proto,
                       nat64_db_st_walk_fn_t fn, void *ctx);
 
 /**
@@ -278,6 +278,14 @@ void nat64_db_st_walk (nat64_db_t * db, snat_protocol_t proto,
  */
 void nad64_db_st_free_expired (nat64_db_t * db, u32 now);
 
+/**
+ * @brief Free sessions using specific outside address.
+ *
+ * @param db NAT64 DB.
+ * @param out_addr Outside address to match.
+ */
+void nat64_db_free_out_addr (nat64_db_t * db, ip4_address_t * out_addr);
+
 #endif /* __included_nat64_db_h__ */
 
 /*
index 126b076..8c67fec 100644 (file)
@@ -25,6 +25,7 @@ typedef struct
 {
   u32 sw_if_index;
   u32 next_index;
+  u8 is_slow_path;
 } nat64_in2out_trace_t;
 
 static u8 *
@@ -33,15 +34,19 @@ format_nat64_in2out_trace (u8 * s, va_list * args)
   CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
   CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
   nat64_in2out_trace_t *t = va_arg (*args, nat64_in2out_trace_t *);
+  char *tag;
+
+  tag = t->is_slow_path ? "NAT64-in2out-slowpath" : "NAT64-in2out";
 
   s =
-    format (s, "NAT64-in2out: sw_if_index %d, next index %d", t->sw_if_index,
+    format (s, "%s: sw_if_index %d, next index %d", tag, t->sw_if_index,
            t->next_index);
 
   return s;
 }
 
 vlib_node_registration_t nat64_in2out_node;
+vlib_node_registration_t nat64_in2out_slowpath_node;
 
 #define foreach_nat64_in2out_error                 \
 _(UNSUPPORTED_PROTOCOL, "unsupported protocol")    \
@@ -68,6 +73,7 @@ typedef enum
   NAT64_IN2OUT_NEXT_IP4_LOOKUP,
   NAT64_IN2OUT_NEXT_IP6_LOOKUP,
   NAT64_IN2OUT_NEXT_DROP,
+  NAT64_IN2OUT_NEXT_SLOWPATH,
   NAT64_IN2OUT_N_NEXT,
 } nat64_in2out_next_t;
 
@@ -113,7 +119,7 @@ nat64_in2out_tcp_udp_set_cb (ip6_header_t * ip6, ip4_header_t * ip4,
   ip46_address_t saddr, daddr;
   u32 sw_if_index, fib_index;
   udp_header_t *udp = ip6_next_header (ip6);
-  snat_protocol_t proto = ip_proto_to_snat_proto (ip6->protocol);
+  u8 proto = ip6->protocol;
   u16 sport = udp->src_port;
   u16 dport = udp->dst_port;
 
@@ -146,7 +152,8 @@ nat64_in2out_tcp_udp_set_cb (ip6_header_t * ip6, ip4_header_t * ip4,
          u16 out_port;
          ip4_address_t out_addr;
          if (nat64_alloc_out_addr_and_port
-             (fib_index, proto, &out_addr, &out_port))
+             (fib_index, ip_proto_to_snat_proto (proto), &out_addr,
+              &out_port))
            return -1;
 
          bibe =
@@ -172,7 +179,7 @@ nat64_in2out_tcp_udp_set_cb (ip6_header_t * ip6, ip4_header_t * ip4,
 
   ip4->dst_address.as_u32 = ste->out_r_addr.as_u32;
 
-  if (proto == SNAT_PROTOCOL_TCP)
+  if (proto == IP_PROTOCOL_TCP)
     {
       u16 *checksum;
       ip_csum_t csum;
@@ -212,12 +219,12 @@ nat64_in2out_icmp_set_cb (ip6_header_t * ip6, ip4_header_t * ip4, void *arg)
       u16 in_id = ((u16 *) (icmp))[2];
       ste =
        nat64_db_st_entry_find (&nm->db, &saddr, &daddr, in_id, 0,
-                               SNAT_PROTOCOL_ICMP, fib_index, 1);
+                               IP_PROTOCOL_ICMP, fib_index, 1);
 
       if (ste)
        {
          bibe =
-           nat64_db_bib_entry_by_index (&nm->db, SNAT_PROTOCOL_ICMP,
+           nat64_db_bib_entry_by_index (&nm->db, IP_PROTOCOL_ICMP,
                                         ste->bibe_index);
          if (!bibe)
            return -1;
@@ -226,7 +233,7 @@ nat64_in2out_icmp_set_cb (ip6_header_t * ip6, ip4_header_t * ip4, void *arg)
        {
          bibe =
            nat64_db_bib_entry_find (&nm->db, &saddr, in_id,
-                                    SNAT_PROTOCOL_ICMP, fib_index, 1);
+                                    IP_PROTOCOL_ICMP, fib_index, 1);
 
          if (!bibe)
            {
@@ -240,7 +247,7 @@ nat64_in2out_icmp_set_cb (ip6_header_t * ip6, ip4_header_t * ip4, void *arg)
                nat64_db_bib_entry_create (&nm->db, &ip6->src_address,
                                           &out_addr, in_id,
                                           clib_host_to_net_u16 (out_id),
-                                          fib_index, SNAT_PROTOCOL_ICMP, 0);
+                                          fib_index, IP_PROTOCOL_ICMP, 0);
              if (!bibe)
                return -1;
            }
@@ -282,7 +289,7 @@ nat64_in2out_inner_icmp_set_cb (ip6_header_t * ip6, ip4_header_t * ip4,
   nat64_db_bib_entry_t *bibe;
   ip46_address_t saddr, daddr;
   u32 sw_if_index, fib_index;
-  snat_protocol_t proto = ip_proto_to_snat_proto (ip6->protocol);
+  u8 proto = ip6->protocol;
 
   sw_if_index = vnet_buffer (ctx->b)->sw_if_index[VLIB_RX];
   fib_index =
@@ -293,10 +300,11 @@ nat64_in2out_inner_icmp_set_cb (ip6_header_t * ip6, ip4_header_t * ip4,
   daddr.as_u64[0] = ip6->dst_address.as_u64[0];
   daddr.as_u64[1] = ip6->dst_address.as_u64[1];
 
-  if (proto == SNAT_PROTOCOL_ICMP)
+  if (proto == IP_PROTOCOL_ICMP6)
     {
       icmp46_header_t *icmp = ip6_next_header (ip6);
       u16 in_id = ((u16 *) (icmp))[2];
+      proto = IP_PROTOCOL_ICMP;
 
       if (!
          (icmp->type == ICMP4_echo_request
@@ -341,7 +349,7 @@ nat64_in2out_inner_icmp_set_cb (ip6_header_t * ip6, ip4_header_t * ip4,
       udp->dst_port = bibe->out_port;
       ip4->src_address.as_u32 = ste->out_r_addr.as_u32;
 
-      if (proto == SNAT_PROTOCOL_TCP)
+      if (proto == IP_PROTOCOL_TCP)
        checksum = &tcp->checksum;
       else
        checksum = &udp->checksum;
@@ -353,6 +361,153 @@ nat64_in2out_inner_icmp_set_cb (ip6_header_t * ip6, ip4_header_t * ip4,
   return 0;
 }
 
+typedef struct unk_proto_st_walk_ctx_t_
+{
+  ip6_address_t src_addr;
+  ip6_address_t dst_addr;
+  ip4_address_t out_addr;
+  u32 fib_index;
+  u8 proto;
+} unk_proto_st_walk_ctx_t;
+
+static int
+unk_proto_st_walk (nat64_db_st_entry_t * ste, void *arg)
+{
+  nat64_main_t *nm = &nat64_main;
+  unk_proto_st_walk_ctx_t *ctx = arg;
+  nat64_db_bib_entry_t *bibe;
+  ip46_address_t saddr, daddr;
+
+  if (ip46_address_is_equal (&ste->in_r_addr, &ctx->dst_addr))
+    {
+      bibe =
+       nat64_db_bib_entry_by_index (&nm->db, ste->proto, ste->bibe_index);
+      if (!bibe)
+       return -1;
+
+      if (ip46_address_is_equal (&bibe->in_addr, &ctx->src_addr)
+         && bibe->fib_index == ctx->fib_index)
+       {
+         memset (&saddr, 0, sizeof (saddr));
+         saddr.ip4.as_u32 = bibe->out_addr.as_u32;
+         memset (&daddr, 0, sizeof (daddr));
+         nat64_extract_ip4 (&ctx->dst_addr, &daddr.ip4, ctx->fib_index);
+
+         if (nat64_db_st_entry_find
+             (&nm->db, &daddr, &saddr, 0, 0, ctx->proto, ctx->fib_index, 0))
+           return -1;
+
+         ctx->out_addr.as_u32 = bibe->out_addr.as_u32;
+         return 1;
+       }
+    }
+
+  return 0;
+}
+
+static int
+nat64_in2out_unk_proto_set_cb (ip6_header_t * ip6, ip4_header_t * ip4,
+                              void *arg)
+{
+  nat64_main_t *nm = &nat64_main;
+  nat64_in2out_set_ctx_t *ctx = arg;
+  nat64_db_bib_entry_t *bibe;
+  nat64_db_st_entry_t *ste;
+  ip46_address_t saddr, daddr, addr;
+  u32 sw_if_index, fib_index;
+  u8 proto = ip6->protocol;
+  int i;
+
+  sw_if_index = vnet_buffer (ctx->b)->sw_if_index[VLIB_RX];
+  fib_index =
+    fib_table_get_index_for_sw_if_index (FIB_PROTOCOL_IP6, sw_if_index);
+
+  saddr.as_u64[0] = ip6->src_address.as_u64[0];
+  saddr.as_u64[1] = ip6->src_address.as_u64[1];
+  daddr.as_u64[0] = ip6->dst_address.as_u64[0];
+  daddr.as_u64[1] = ip6->dst_address.as_u64[1];
+
+  ste =
+    nat64_db_st_entry_find (&nm->db, &saddr, &daddr, 0, 0, proto, fib_index,
+                           1);
+
+  if (ste)
+    {
+      bibe = nat64_db_bib_entry_by_index (&nm->db, proto, ste->bibe_index);
+      if (!bibe)
+       return -1;
+    }
+  else
+    {
+      bibe =
+       nat64_db_bib_entry_find (&nm->db, &saddr, 0, proto, fib_index, 1);
+
+      if (!bibe)
+       {
+         /* Choose same out address as for TCP/UDP session to same dst */
+         unk_proto_st_walk_ctx_t ctx = {
+           .src_addr.as_u64[0] = ip6->src_address.as_u64[0],
+           .src_addr.as_u64[1] = ip6->src_address.as_u64[1],
+           .dst_addr.as_u64[0] = ip6->dst_address.as_u64[0],
+           .dst_addr.as_u64[1] = ip6->dst_address.as_u64[1],
+           .out_addr.as_u32 = 0,
+           .fib_index = fib_index,
+           .proto = proto,
+         };
+
+         nat64_db_st_walk (&nm->db, IP_PROTOCOL_TCP, unk_proto_st_walk,
+                           &ctx);
+
+         if (!ctx.out_addr.as_u32)
+           nat64_db_st_walk (&nm->db, IP_PROTOCOL_UDP, unk_proto_st_walk,
+                             &ctx);
+
+         /* Verify if out address is not already in use for protocol */
+         memset (&addr, 0, sizeof (addr));
+         addr.ip4.as_u32 = ctx.out_addr.as_u32;
+         if (nat64_db_bib_entry_find (&nm->db, &addr, 0, proto, 0, 0))
+           ctx.out_addr.as_u32 = 0;
+
+         if (!ctx.out_addr.as_u32)
+           {
+             for (i = 0; i < vec_len (nm->addr_pool); i++)
+               {
+                 addr.ip4.as_u32 = nm->addr_pool[i].addr.as_u32;
+                 if (!nat64_db_bib_entry_find
+                     (&nm->db, &addr, 0, proto, 0, 0))
+                   break;
+               }
+           }
+
+         if (!ctx.out_addr.as_u32)
+           return -1;
+
+         bibe =
+           nat64_db_bib_entry_create (&nm->db, &ip6->src_address,
+                                      &ctx.out_addr, 0, 0, fib_index, proto,
+                                      0);
+         if (!bibe)
+           return -1;
+       }
+
+      nat64_extract_ip4 (&ip6->dst_address, &daddr.ip4, fib_index);
+      ste =
+       nat64_db_st_entry_create (&nm->db, bibe, &ip6->dst_address,
+                                 &daddr.ip4, 0);
+      if (!ste)
+       return -1;
+    }
+
+  nat64_session_reset_timeout (ste, ctx->vm);
+
+  ip4->src_address.as_u32 = bibe->out_addr.as_u32;
+  ip4->dst_address.as_u32 = ste->out_r_addr.as_u32;
+
+  return 0;
+}
+
+
+
 static int
 nat64_in2out_tcp_udp_hairpinning (vlib_main_t * vm, vlib_buffer_t * b,
                                  ip6_header_t * ip6)
@@ -364,7 +519,7 @@ nat64_in2out_tcp_udp_hairpinning (vlib_main_t * vm, vlib_buffer_t * b,
   u32 sw_if_index, fib_index;
   udp_header_t *udp = ip6_next_header (ip6);
   tcp_header_t *tcp = ip6_next_header (ip6);
-  snat_protocol_t proto = ip_proto_to_snat_proto (ip6->protocol);
+  u8 proto = ip6->protocol;
   u16 sport = udp->src_port;
   u16 dport = udp->dst_port;
   u16 *checksum;
@@ -379,7 +534,7 @@ nat64_in2out_tcp_udp_hairpinning (vlib_main_t * vm, vlib_buffer_t * b,
   daddr.as_u64[0] = ip6->dst_address.as_u64[0];
   daddr.as_u64[1] = ip6->dst_address.as_u64[1];
 
-  if (proto == SNAT_PROTOCOL_UDP)
+  if (proto == IP_PROTOCOL_UDP)
     checksum = &udp->checksum;
   else
     checksum = &tcp->checksum;
@@ -411,7 +566,8 @@ nat64_in2out_tcp_udp_hairpinning (vlib_main_t * vm, vlib_buffer_t * b,
          u16 out_port;
          ip4_address_t out_addr;
          if (nat64_alloc_out_addr_and_port
-             (fib_index, proto, &out_addr, &out_port))
+             (fib_index, ip_proto_to_snat_proto (proto), &out_addr,
+              &out_port))
            return -1;
 
          bibe =
@@ -488,7 +644,7 @@ nat64_in2out_icmp_hairpinning (vlib_main_t * vm, vlib_buffer_t * b,
   ip6_header_t *inner_ip6;
   ip46_address_t saddr, daddr;
   u32 sw_if_index, fib_index;
-  snat_protocol_t proto;
+  u8 proto;
   udp_header_t *udp;
   tcp_header_t *tcp;
   u16 *checksum, sport, dport;
@@ -499,9 +655,9 @@ nat64_in2out_icmp_hairpinning (vlib_main_t * vm, vlib_buffer_t * b,
 
   inner_ip6 = (ip6_header_t *) u8_ptr_add (icmp, 8);
 
-  proto = ip_proto_to_snat_proto (inner_ip6->protocol);
+  proto = inner_ip6->protocol;
 
-  if (proto == SNAT_PROTOCOL_ICMP)
+  if (proto == IP_PROTOCOL_ICMP6)
     return -1;
 
   sw_if_index = vnet_buffer (b)->sw_if_index[VLIB_RX];
@@ -519,7 +675,7 @@ nat64_in2out_icmp_hairpinning (vlib_main_t * vm, vlib_buffer_t * b,
   sport = udp->src_port;
   dport = udp->dst_port;
 
-  if (proto == SNAT_PROTOCOL_UDP)
+  if (proto == IP_PROTOCOL_UDP)
     checksum = &udp->checksum;
   else
     checksum = &tcp->checksum;
@@ -593,13 +749,144 @@ nat64_in2out_icmp_hairpinning (vlib_main_t * vm, vlib_buffer_t * b,
   return 0;
 }
 
-static uword
-nat64_in2out_node_fn (vlib_main_t * vm, vlib_node_runtime_t * node,
-                     vlib_frame_t * frame)
+static int
+nat64_in2out_unk_proto_hairpinning (vlib_main_t * vm, vlib_buffer_t * b,
+                                   ip6_header_t * ip6)
+{
+  nat64_main_t *nm = &nat64_main;
+  nat64_db_bib_entry_t *bibe;
+  nat64_db_st_entry_t *ste;
+  ip46_address_t saddr, daddr, addr;
+  u32 sw_if_index, fib_index;
+  u8 proto = ip6->protocol;
+  int i;
+
+  sw_if_index = vnet_buffer (b)->sw_if_index[VLIB_RX];
+  fib_index =
+    fib_table_get_index_for_sw_if_index (FIB_PROTOCOL_IP6, sw_if_index);
+
+  saddr.as_u64[0] = ip6->src_address.as_u64[0];
+  saddr.as_u64[1] = ip6->src_address.as_u64[1];
+  daddr.as_u64[0] = ip6->dst_address.as_u64[0];
+  daddr.as_u64[1] = ip6->dst_address.as_u64[1];
+
+  ste =
+    nat64_db_st_entry_find (&nm->db, &saddr, &daddr, 0, 0, proto, fib_index,
+                           1);
+
+  if (ste)
+    {
+      bibe = nat64_db_bib_entry_by_index (&nm->db, proto, ste->bibe_index);
+      if (!bibe)
+       return -1;
+    }
+  else
+    {
+      bibe =
+       nat64_db_bib_entry_find (&nm->db, &saddr, 0, proto, fib_index, 1);
+
+      if (!bibe)
+       {
+         /* Choose same out address as for TCP/UDP session to same dst */
+         unk_proto_st_walk_ctx_t ctx = {
+           .src_addr.as_u64[0] = ip6->src_address.as_u64[0],
+           .src_addr.as_u64[1] = ip6->src_address.as_u64[1],
+           .dst_addr.as_u64[0] = ip6->dst_address.as_u64[0],
+           .dst_addr.as_u64[1] = ip6->dst_address.as_u64[1],
+           .out_addr.as_u32 = 0,
+           .fib_index = fib_index,
+           .proto = proto,
+         };
+
+         nat64_db_st_walk (&nm->db, IP_PROTOCOL_TCP, unk_proto_st_walk,
+                           &ctx);
+
+         if (!ctx.out_addr.as_u32)
+           nat64_db_st_walk (&nm->db, IP_PROTOCOL_UDP, unk_proto_st_walk,
+                             &ctx);
+
+         /* Verify if out address is not already in use for protocol */
+         memset (&addr, 0, sizeof (addr));
+         addr.ip4.as_u32 = ctx.out_addr.as_u32;
+         if (nat64_db_bib_entry_find (&nm->db, &addr, 0, proto, 0, 0))
+           ctx.out_addr.as_u32 = 0;
+
+         if (!ctx.out_addr.as_u32)
+           {
+             for (i = 0; i < vec_len (nm->addr_pool); i++)
+               {
+                 addr.ip4.as_u32 = nm->addr_pool[i].addr.as_u32;
+                 if (!nat64_db_bib_entry_find
+                     (&nm->db, &addr, 0, proto, 0, 0))
+                   break;
+               }
+           }
+
+         if (!ctx.out_addr.as_u32)
+           return -1;
+
+         bibe =
+           nat64_db_bib_entry_create (&nm->db, &ip6->src_address,
+                                      &ctx.out_addr, 0, 0, fib_index, proto,
+                                      0);
+         if (!bibe)
+           return -1;
+       }
+
+      nat64_extract_ip4 (&ip6->dst_address, &daddr.ip4, fib_index);
+      ste =
+       nat64_db_st_entry_create (&nm->db, bibe, &ip6->dst_address,
+                                 &daddr.ip4, 0);
+      if (!ste)
+       return -1;
+    }
+
+  nat64_session_reset_timeout (ste, vm);
+
+  nat64_compose_ip6 (&ip6->src_address, &bibe->out_addr, fib_index);
+
+  memset (&saddr, 0, sizeof (saddr));
+  memset (&daddr, 0, sizeof (daddr));
+  saddr.ip4.as_u32 = bibe->out_addr.as_u32;
+  daddr.ip4.as_u32 = ste->out_r_addr.as_u32;
+
+  ste = nat64_db_st_entry_find (&nm->db, &daddr, &saddr, 0, 0, proto, 0, 0);
+
+  if (ste)
+    {
+      bibe = nat64_db_bib_entry_by_index (&nm->db, proto, ste->bibe_index);
+      if (!bibe)
+       return -1;
+    }
+  else
+    {
+      bibe = nat64_db_bib_entry_find (&nm->db, &daddr, 0, proto, 0, 0);
+
+      if (!bibe)
+       return -1;
+
+      ste =
+       nat64_db_st_entry_create (&nm->db, bibe, &ip6->src_address,
+                                 &saddr.ip4, 0);
+    }
+
+  ip6->dst_address.as_u64[0] = bibe->in_addr.as_u64[0];
+  ip6->dst_address.as_u64[1] = bibe->in_addr.as_u64[1];
+
+  return 0;
+}
+
+static inline uword
+nat64_in2out_node_fn_inline (vlib_main_t * vm, vlib_node_runtime_t * node,
+                            vlib_frame_t * frame, u8 is_slow_path)
 {
   u32 n_left_from, *from, *to_next;
   nat64_in2out_next_t next_index;
   u32 pkts_processed = 0;
+  u32 stats_node_index;
+
+  stats_node_index =
+    is_slow_path ? nat64_in2out_slowpath_node.index : nat64_in2out_node.index;
 
   from = vlib_frame_vector_args (frame);
   n_left_from = frame->n_vectors;
@@ -649,7 +936,7 @@ nat64_in2out_node_fn (vlib_main_t * vm, vlib_node_runtime_t * node,
            }
 
          proto0 = ip_proto_to_snat_proto (l4_protocol0);
-         if (PREDICT_FALSE ((proto0 == ~0) || (frag_offset0 != 0)))
+         if (frag_offset0 != 0)
            {
              next0 = NAT64_IN2OUT_NEXT_DROP;
              b0->error =
@@ -657,6 +944,41 @@ nat64_in2out_node_fn (vlib_main_t * vm, vlib_node_runtime_t * node,
              goto trace0;
            }
 
+         if (is_slow_path)
+           {
+             if (PREDICT_TRUE (proto0 == ~0))
+               {
+                 if (is_hairpinning (&ip60->dst_address))
+                   {
+                     next0 = NAT64_IN2OUT_NEXT_IP6_LOOKUP;
+                     if (nat64_in2out_unk_proto_hairpinning (vm, b0, ip60))
+                       {
+                         next0 = NAT64_IN2OUT_NEXT_DROP;
+                         b0->error =
+                           node->errors[NAT64_IN2OUT_ERROR_NO_TRANSLATION];
+                       }
+                     goto trace0;
+                   }
+
+                 if (ip6_to_ip4 (b0, nat64_in2out_unk_proto_set_cb, &ctx0))
+                   {
+                     next0 = NAT64_IN2OUT_NEXT_DROP;
+                     b0->error =
+                       node->errors[NAT64_IN2OUT_ERROR_NO_TRANSLATION];
+                     goto trace0;
+                   }
+               }
+             goto trace0;
+           }
+         else
+           {
+             if (PREDICT_FALSE (proto0 == ~0))
+               {
+                 next0 = NAT64_IN2OUT_NEXT_SLOWPATH;
+                 goto trace0;
+               }
+           }
+
          if (proto0 == SNAT_PROTOCOL_ICMP)
            {
              if (is_hairpinning (&ip60->dst_address))
@@ -680,7 +1002,7 @@ nat64_in2out_node_fn (vlib_main_t * vm, vlib_node_runtime_t * node,
                  goto trace0;
                }
            }
-         else
+         else if (proto0 == SNAT_PROTOCOL_TCP || proto0 == SNAT_PROTOCOL_UDP)
            {
              if (is_hairpinning (&ip60->dst_address))
                {
@@ -711,6 +1033,7 @@ nat64_in2out_node_fn (vlib_main_t * vm, vlib_node_runtime_t * node,
                vlib_add_trace (vm, node, b0, sizeof (*t));
              t->sw_if_index = vnet_buffer (b0)->sw_if_index[VLIB_RX];
              t->next_index = next0;
+             t->is_slow_path = is_slow_path;
            }
 
          pkts_processed += next0 != NAT64_IN2OUT_NEXT_DROP;
@@ -721,32 +1044,71 @@ nat64_in2out_node_fn (vlib_main_t * vm, vlib_node_runtime_t * node,
        }
       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
     }
-  vlib_node_increment_counter (vm, nat64_in2out_node.index,
+  vlib_node_increment_counter (vm, stats_node_index,
                               NAT64_IN2OUT_ERROR_IN2OUT_PACKETS,
                               pkts_processed);
   return frame->n_vectors;
 }
 
+static uword
+nat64_in2out_node_fn (vlib_main_t * vm, vlib_node_runtime_t * node,
+                     vlib_frame_t * frame)
+{
+  return nat64_in2out_node_fn_inline (vm, node, frame, 0);
+}
+
 /* *INDENT-OFF* */
 VLIB_REGISTER_NODE (nat64_in2out_node) = {
-  .function = nat64_in2out_node_fn,.name = "nat64-in2out",
+  .function = nat64_in2out_node_fn,
+  .name = "nat64-in2out",
   .vector_size = sizeof (u32),
   .format_trace = format_nat64_in2out_trace,
   .type = VLIB_NODE_TYPE_INTERNAL,
   .n_errors = ARRAY_LEN (nat64_in2out_error_strings),
   .error_strings = nat64_in2out_error_strings,
-  .n_next_nodes = 2,
+  .n_next_nodes = NAT64_IN2OUT_N_NEXT,
   /* edit / add dispositions here */
   .next_nodes = {
     [NAT64_IN2OUT_NEXT_DROP] = "error-drop",
     [NAT64_IN2OUT_NEXT_IP4_LOOKUP] = "ip4-lookup",
     [NAT64_IN2OUT_NEXT_IP6_LOOKUP] = "ip6-lookup",
+    [NAT64_IN2OUT_NEXT_SLOWPATH] = "nat64-in2out-slowpath",
   },
 };
 /* *INDENT-ON* */
 
 VLIB_NODE_FUNCTION_MULTIARCH (nat64_in2out_node, nat64_in2out_node_fn);
 
+static uword
+nat64_in2out_slowpath_node_fn (vlib_main_t * vm, vlib_node_runtime_t * node,
+                              vlib_frame_t * frame)
+{
+  return nat64_in2out_node_fn_inline (vm, node, frame, 1);
+}
+
+/* *INDENT-OFF* */
+VLIB_REGISTER_NODE (nat64_in2out_slowpath_node) = {
+  .function = nat64_in2out_slowpath_node_fn,
+  .name = "nat64-in2out-slowpath",
+  .vector_size = sizeof (u32),
+  .format_trace = format_nat64_in2out_trace,
+  .type = VLIB_NODE_TYPE_INTERNAL,
+  .n_errors = ARRAY_LEN (nat64_in2out_error_strings),
+  .error_strings = nat64_in2out_error_strings,
+  .n_next_nodes = NAT64_IN2OUT_N_NEXT,
+  /* edit / add dispositions here */
+  .next_nodes = {
+    [NAT64_IN2OUT_NEXT_DROP] = "error-drop",
+    [NAT64_IN2OUT_NEXT_IP4_LOOKUP] = "ip4-lookup",
+    [NAT64_IN2OUT_NEXT_IP6_LOOKUP] = "ip6-lookup",
+    [NAT64_IN2OUT_NEXT_SLOWPATH] = "nat64-in2out-slowpath",
+  },
+};
+/* *INDENT-ON* */
+
+VLIB_NODE_FUNCTION_MULTIARCH (nat64_in2out_slowpath_node,
+                             nat64_in2out_slowpath_node_fn);
+
 /*
  * fd.io coding-style-patch-verification: ON
  *
index 755aa63..cd5b253 100644 (file)
@@ -88,7 +88,7 @@ nat64_out2in_tcp_udp_set_cb (ip4_header_t * ip4, ip6_header_t * ip6,
   ip6_address_t ip6_saddr;
   udp_header_t *udp = ip4_next_header (ip4);
   tcp_header_t *tcp = ip4_next_header (ip4);
-  snat_protocol_t proto = ip_proto_to_snat_proto (ip4->protocol);
+  u8 proto = ip4->protocol;
   u16 dport = udp->dst_port;
   u16 sport = udp->src_port;
   u32 sw_if_index, fib_index;
@@ -135,7 +135,7 @@ nat64_out2in_tcp_udp_set_cb (ip4_header_t * ip4, ip6_header_t * ip6,
   ip6->dst_address.as_u64[1] = bibe->in_addr.as_u64[1];
   udp->dst_port = bibe->in_port;
 
-  if (proto == SNAT_PROTOCOL_UDP)
+  if (proto == IP_PROTOCOL_UDP)
     checksum = &udp->checksum;
   else
     checksum = &tcp->checksum;
@@ -173,12 +173,12 @@ nat64_out2in_icmp_set_cb (ip4_header_t * ip4, ip6_header_t * ip6, void *arg)
       u16 out_id = ((u16 *) (icmp))[2];
       ste =
        nat64_db_st_entry_find (&nm->db, &daddr, &saddr, out_id, 0,
-                               SNAT_PROTOCOL_ICMP, fib_index, 0);
+                               IP_PROTOCOL_ICMP, fib_index, 0);
 
       if (ste)
        {
          bibe =
-           nat64_db_bib_entry_by_index (&nm->db, SNAT_PROTOCOL_ICMP,
+           nat64_db_bib_entry_by_index (&nm->db, IP_PROTOCOL_ICMP,
                                         ste->bibe_index);
          if (!bibe)
            return -1;
@@ -187,7 +187,7 @@ nat64_out2in_icmp_set_cb (ip4_header_t * ip4, ip6_header_t * ip6, void *arg)
        {
          bibe =
            nat64_db_bib_entry_find (&nm->db, &daddr, out_id,
-                                    SNAT_PROTOCOL_ICMP, fib_index, 0);
+                                    IP_PROTOCOL_ICMP, fib_index, 0);
          if (!bibe)
            return -1;
 
@@ -231,7 +231,7 @@ nat64_out2in_inner_icmp_set_cb (ip4_header_t * ip4, ip6_header_t * ip6,
   nat64_db_st_entry_t *ste;
   ip46_address_t saddr, daddr;
   u32 sw_if_index, fib_index;
-  snat_protocol_t proto = ip_proto_to_snat_proto (ip4->protocol);
+  u8 proto = ip4->protocol;
 
   sw_if_index = vnet_buffer (ctx->b)->sw_if_index[VLIB_RX];
   fib_index =
@@ -242,10 +242,11 @@ nat64_out2in_inner_icmp_set_cb (ip4_header_t * ip4, ip6_header_t * ip6,
   memset (&daddr, 0, sizeof (daddr));
   daddr.ip4.as_u32 = ip4->dst_address.as_u32;
 
-  if (proto == SNAT_PROTOCOL_ICMP)
+  if (proto == IP_PROTOCOL_ICMP6)
     {
       icmp46_header_t *icmp = ip4_next_header (ip4);
       u16 out_id = ((u16 *) (icmp))[2];
+      proto = IP_PROTOCOL_ICMP;
 
       if (!
          (icmp->type == ICMP6_echo_request
@@ -294,7 +295,7 @@ nat64_out2in_inner_icmp_set_cb (ip4_header_t * ip4, ip6_header_t * ip6,
       ip6->src_address.as_u64[1] = bibe->in_addr.as_u64[1];
       udp->src_port = bibe->in_port;
 
-      if (proto == SNAT_PROTOCOL_UDP)
+      if (proto == IP_PROTOCOL_UDP)
        checksum = &udp->checksum;
       else
        checksum = &tcp->checksum;
@@ -311,6 +312,62 @@ nat64_out2in_inner_icmp_set_cb (ip4_header_t * ip4, ip6_header_t * ip6,
   return 0;
 }
 
+static int
+nat64_out2in_unk_proto_set_cb (ip4_header_t * ip4, ip6_header_t * ip6,
+                              void *arg)
+{
+  nat64_main_t *nm = &nat64_main;
+  nat64_out2in_set_ctx_t *ctx = arg;
+  nat64_db_bib_entry_t *bibe;
+  nat64_db_st_entry_t *ste;
+  ip46_address_t saddr, daddr;
+  ip6_address_t ip6_saddr;
+  u32 sw_if_index, fib_index;
+  u8 proto = ip4->protocol;
+
+  sw_if_index = vnet_buffer (ctx->b)->sw_if_index[VLIB_RX];
+  fib_index = ip4_fib_table_get_index_for_sw_if_index (sw_if_index);
+
+  memset (&saddr, 0, sizeof (saddr));
+  saddr.ip4.as_u32 = ip4->src_address.as_u32;
+  memset (&daddr, 0, sizeof (daddr));
+  daddr.ip4.as_u32 = ip4->dst_address.as_u32;
+
+  ste =
+    nat64_db_st_entry_find (&nm->db, &daddr, &saddr, 0, 0, proto, fib_index,
+                           0);
+  if (ste)
+    {
+      bibe = nat64_db_bib_entry_by_index (&nm->db, proto, ste->bibe_index);
+      if (!bibe)
+       return -1;
+    }
+  else
+    {
+      bibe =
+       nat64_db_bib_entry_find (&nm->db, &daddr, 0, proto, fib_index, 0);
+
+      if (!bibe)
+       return -1;
+
+      nat64_compose_ip6 (&ip6_saddr, &ip4->src_address, bibe->fib_index);
+      ste =
+       nat64_db_st_entry_create (&nm->db, bibe, &ip6_saddr, &saddr.ip4, 0);
+    }
+
+  nat64_session_reset_timeout (ste, ctx->vm);
+
+  ip6->src_address.as_u64[0] = ste->in_r_addr.as_u64[0];
+  ip6->src_address.as_u64[1] = ste->in_r_addr.as_u64[1];
+
+  ip6->dst_address.as_u64[0] = bibe->in_addr.as_u64[0];
+  ip6->dst_address.as_u64[1] = bibe->in_addr.as_u64[1];
+
+  vnet_buffer (ctx->b)->sw_if_index[VLIB_TX] = bibe->fib_index;
+
+  return 0;
+}
+
 static uword
 nat64_out2in_node_fn (vlib_main_t * vm, vlib_node_runtime_t * node,
                      vlib_frame_t * frame)
@@ -354,13 +411,6 @@ nat64_out2in_node_fn (vlib_main_t * vm, vlib_node_runtime_t * node,
          next0 = NAT64_OUT2IN_NEXT_LOOKUP;
 
          proto0 = ip_proto_to_snat_proto (ip40->protocol);
-         if (PREDICT_FALSE (proto0 == ~0))
-           {
-             next0 = NAT64_OUT2IN_NEXT_DROP;
-             b0->error =
-               node->errors[NAT64_OUT2IN_ERROR_UNSUPPORTED_PROTOCOL];
-             goto trace0;
-           }
 
          if (proto0 == SNAT_PROTOCOL_ICMP)
            {
@@ -373,7 +423,7 @@ nat64_out2in_node_fn (vlib_main_t * vm, vlib_node_runtime_t * node,
                  goto trace0;
                }
            }
-         else
+         else if (proto0 == SNAT_PROTOCOL_TCP || proto0 == SNAT_PROTOCOL_UDP)
            {
              if (ip4_to_ip6_tcp_udp (b0, nat64_out2in_tcp_udp_set_cb, &ctx0))
                {
@@ -382,6 +432,15 @@ nat64_out2in_node_fn (vlib_main_t * vm, vlib_node_runtime_t * node,
                  goto trace0;
                }
            }
+         else
+           {
+             if (ip4_to_ip6 (b0, nat64_out2in_unk_proto_set_cb, &ctx0))
+               {
+                 next0 = NAT64_OUT2IN_NEXT_DROP;
+                 b0->error = node->errors[NAT64_OUT2IN_ERROR_NO_TRANSLATION];
+                 goto trace0;
+               }
+           }
 
        trace0:
          if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE)
index f196b5c..315cec8 100644 (file)
@@ -1398,6 +1398,7 @@ format_snat_protocol (u8 * s, va_list * args)
 #undef _
     default:
       s = format (s, "unknown");
+      return s;
     }
   s = format (s, "%s", t);
   return s;
index ee623d2..227074f 100644 (file)
@@ -1580,7 +1580,7 @@ nat64_api_bib_walk (nat64_db_bib_entry_t * bibe, void *arg)
   rmp->i_port = bibe->in_port;
   rmp->o_port = bibe->out_port;
   rmp->vrf_id = ntohl (fib->ft_table_id);
-  rmp->proto = snat_proto_to_ip_proto (bibe->proto);
+  rmp->proto = bibe->proto;
   rmp->is_static = bibe->is_static;
   rmp->ses_num = ntohl (bibe->ses_num);
 
@@ -1594,7 +1594,6 @@ vl_api_nat64_bib_dump_t_handler (vl_api_nat64_bib_dump_t * mp)
 {
   unix_shared_memory_queue_t *q;
   nat64_main_t *nm = &nat64_main;
-  snat_protocol_t proto;
 
   if (nm->is_disabled)
     return;
@@ -1608,9 +1607,7 @@ vl_api_nat64_bib_dump_t_handler (vl_api_nat64_bib_dump_t * mp)
     .context = mp->context,
   };
 
-  proto = ip_proto_to_snat_proto (mp->proto);
-
-  nat64_db_bib_walk (&nm->db, proto, nat64_api_bib_walk, &ctx);
+  nat64_db_bib_walk (&nm->db, mp->proto, nat64_api_bib_walk, &ctx);
 }
 
 static void *
@@ -1729,7 +1726,7 @@ nat64_api_st_walk (nat64_db_st_entry_t * ste, void *arg)
   clib_memcpy (rmp->or_addr, &(ste->out_r_addr), 4);
   rmp->il_port = ste->r_port;
   rmp->vrf_id = ntohl (fib->ft_table_id);
-  rmp->proto = snat_proto_to_ip_proto (ste->proto);
+  rmp->proto = ste->proto;
 
   vl_msg_api_send_shmem (ctx->q, (u8 *) & rmp);
 
@@ -1741,7 +1738,6 @@ vl_api_nat64_st_dump_t_handler (vl_api_nat64_st_dump_t * mp)
 {
   unix_shared_memory_queue_t *q;
   nat64_main_t *nm = &nat64_main;
-  snat_protocol_t proto;
 
   if (nm->is_disabled)
     return;
@@ -1755,9 +1751,7 @@ vl_api_nat64_st_dump_t_handler (vl_api_nat64_st_dump_t * mp)
     .context = mp->context,
   };
 
-  proto = ip_proto_to_snat_proto (mp->proto);
-
-  nat64_db_st_walk (&nm->db, proto, nat64_api_st_walk, &ctx);
+  nat64_db_st_walk (&nm->db, mp->proto, nat64_api_st_walk, &ctx);
 }
 
 static void *
index cdd3707..6ffc562 100644 (file)
@@ -586,6 +586,68 @@ ip4_to_ip6_tcp_udp (vlib_buffer_t * p, ip4_to_ip6_set_fn_t fn, void *ctx)
   return 0;
 }
 
+/**
+ * @brief Translate IPv4 packet to IPv6 (IP header only).
+ *
+ * @param p   Buffer to translate.
+ * @param fn  The function to translate header.
+ * @param ctx A context passed in the header translate function.
+ *
+ * @returns 0 on success, non-zero value otherwise.
+ */
+always_inline int
+ip4_to_ip6 (vlib_buffer_t * p, ip4_to_ip6_set_fn_t fn, void *ctx)
+{
+  ip4_header_t *ip4;
+  ip6_header_t *ip6;
+  ip6_frag_hdr_t *frag;
+  u32 frag_id;
+  int rv;
+
+  ip4 = vlib_buffer_get_current (p);
+
+  // Deal with fragmented packets
+  if (PREDICT_FALSE (ip4->flags_and_fragment_offset &
+                    clib_host_to_net_u16 (IP4_HEADER_FLAG_MORE_FRAGMENTS)))
+    {
+      ip6 =
+       (ip6_header_t *) u8_ptr_add (ip4,
+                                    sizeof (*ip4) - sizeof (*ip6) -
+                                    sizeof (*frag));
+      frag =
+       (ip6_frag_hdr_t *) u8_ptr_add (ip4, sizeof (*ip4) - sizeof (*frag));
+      frag_id = frag_id_4to6 (ip4->fragment_id);
+      vlib_buffer_advance (p, sizeof (*ip4) - sizeof (*ip6) - sizeof (*frag));
+    }
+  else
+    {
+      ip6 = (ip6_header_t *) (((u8 *) ip4) + sizeof (*ip4) - sizeof (*ip6));
+      vlib_buffer_advance (p, sizeof (*ip4) - sizeof (*ip6));
+      frag = NULL;
+    }
+
+  ip6->ip_version_traffic_class_and_flow_label =
+    clib_host_to_net_u32 ((6 << 28) + (ip4->tos << 20));
+  ip6->payload_length = u16_net_add (ip4->length, -sizeof (*ip4));
+  ip6->hop_limit = ip4->ttl;
+  ip6->protocol = ip4->protocol;
+
+  if (PREDICT_FALSE (frag != NULL))
+    {
+      frag->next_hdr = ip6->protocol;
+      frag->identification = frag_id;
+      frag->rsv = 0;
+      frag->fragment_offset_and_more = ip6_frag_hdr_offset_and_more (0, 1);
+      ip6->protocol = IP_PROTOCOL_IPV6_FRAGMENTATION;
+      ip6->payload_length = u16_net_add (ip6->payload_length, sizeof (*frag));
+    }
+
+  if ((rv = fn (ip4, ip6, ctx)) != 0)
+    return rv;
+
+  return 0;
+}
+
 #endif /* __included_ip4_to_ip6_h__ */
 
 /*
index 7a0d534..c14b46c 100644 (file)
@@ -562,6 +562,67 @@ no_csum:
   return 0;
 }
 
+/**
+ * @brief Translate IPv6 packet to IPv4 (IP header only).
+ *
+ * @param p   Buffer to translate.
+ * @param fn  The function to translate header.
+ * @param ctx A context passed in the header translate function.
+ *
+ * @returns 0 on success, non-zero value otherwise.
+ */
+always_inline int
+ip6_to_ip4 (vlib_buffer_t * p, ip6_to_ip4_set_fn_t fn, void *ctx)
+{
+  ip6_header_t *ip6;
+  ip4_header_t *ip4;
+  u16 fragment_id;
+  u16 flags;
+  u16 frag_offset;
+  u8 l4_protocol;
+  u16 l4_offset;
+  int rv;
+
+  ip6 = vlib_buffer_get_current (p);
+
+  if (ip6_parse
+      (ip6, p->current_length, &l4_protocol, &l4_offset, &frag_offset))
+    return -1;
+
+  ip4 = (ip4_header_t *) u8_ptr_add (ip6, l4_offset - sizeof (*ip4));
+
+  vlib_buffer_advance (p, l4_offset - sizeof (*ip4));
+
+  if (PREDICT_FALSE (frag_offset))
+    {
+      //Only the first fragment
+      ip6_frag_hdr_t *hdr = (ip6_frag_hdr_t *) u8_ptr_add (ip6, frag_offset);
+      fragment_id = frag_id_6to4 (hdr->identification);
+      flags = clib_host_to_net_u16 (IP4_HEADER_FLAG_MORE_FRAGMENTS);
+    }
+  else
+    {
+      fragment_id = 0;
+      flags = 0;
+    }
+
+  if ((rv = fn (ip6, ip4, ctx)) != 0)
+    return rv;
+
+  ip4->ip_version_and_header_length =
+    IP4_VERSION_AND_HEADER_LENGTH_NO_OPTIONS;
+  ip4->tos = ip6_translate_tos (ip6);
+  ip4->length = u16_net_add (ip6->payload_length,
+                            sizeof (*ip4) + sizeof (*ip6) - l4_offset);
+  ip4->fragment_id = fragment_id;
+  ip4->flags_and_fragment_offset = flags;
+  ip4->ttl = ip6->hop_limit;
+  ip4->protocol = l4_protocol;
+  ip4->checksum = ip4_header_checksum (ip4);
+
+  return 0;
+}
+
 #endif /* __included_ip6_to_ip4_h__ */
 
 /*
index 9f5377e..8fd05fa 100644 (file)
@@ -3497,7 +3497,7 @@ class TestNAT64(MethodHolder):
                                   vrf1_pref64_len)
         self.verify_capture_in_ip6(capture, dst_ip, self.pg2.remote_ip6)
 
-    def _test_unknown_proto(self):
+    def test_unknown_proto(self):
         """ NAT64 translate packet with unknown protocol """
 
         self.vapi.nat64_add_del_pool_addr_range(self.nat_addr_n,
@@ -3516,7 +3516,7 @@ class TestNAT64(MethodHolder):
         p = self.pg1.get_capture(1)
 
         p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
-             IPv6(src=self.pg0.remote_ip6, dst=remote_ip6) /
+             IPv6(src=self.pg0.remote_ip6, dst=remote_ip6, nh=47) /
              GRE() /
              IP(src=self.pg2.local_ip4, dst=self.pg2.remote_ip4) /
              TCP(sport=1234, dport=1234))
@@ -3547,13 +3547,13 @@ class TestNAT64(MethodHolder):
         packet = p[0]
         try:
             self.assertEqual(packet[IPv6].src, remote_ip6)
-            self.assertEqual(packet[IPv6].dst, self.pgi0.remote_ip6)
-            self.assertTrue(packet.haslayer(GRE))
+            self.assertEqual(packet[IPv6].dst, self.pg0.remote_ip6)
+            self.assertEqual(packet[IPv6].nh, 47)
         except:
             self.logger.error(ppp("Unexpected or invalid packet:", packet))
             raise
 
-    def _test_hairpinning_unknown_proto(self):
+    def test_hairpinning_unknown_proto(self):
         """ NAT64 translate packet with unknown protocol - hairpinning """
 
         client = self.pg0.remote_hosts[0]
@@ -3561,23 +3561,40 @@ class TestNAT64(MethodHolder):
         server_tcp_in_port = 22
         server_tcp_out_port = 4022
         client_tcp_in_port = 1234
-        client_udp_in_port = 1235
-        nat_addr_ip6 = self.compose_ip6(self.nat_addr, '64:ff9b::', 96)
-
-        self.vapi.nat64_add_del_pool_addr_range(self.nat_addr_n,
-                                                self.nat_addr_n)
+        client_tcp_out_port = 1235
+        server_nat_ip = "10.0.0.100"
+        client_nat_ip = "10.0.0.110"
+        server_nat_ip_n = socket.inet_pton(socket.AF_INET, server_nat_ip)
+        client_nat_ip_n = socket.inet_pton(socket.AF_INET, client_nat_ip)
+        server_nat_ip6 = self.compose_ip6(server_nat_ip, '64:ff9b::', 96)
+        client_nat_ip6 = self.compose_ip6(client_nat_ip, '64:ff9b::', 96)
+
+        self.vapi.nat64_add_del_pool_addr_range(server_nat_ip_n,
+                                                client_nat_ip_n)
         self.vapi.nat64_add_del_interface(self.pg0.sw_if_index)
         self.vapi.nat64_add_del_interface(self.pg1.sw_if_index, is_inside=0)
 
         self.vapi.nat64_add_del_static_bib(server.ip6n,
-                                           self.nat_addr_n,
+                                           server_nat_ip_n,
                                            server_tcp_in_port,
                                            server_tcp_out_port,
                                            IP_PROTOS.tcp)
 
+        self.vapi.nat64_add_del_static_bib(server.ip6n,
+                                           server_nat_ip_n,
+                                           0,
+                                           0,
+                                           IP_PROTOS.gre)
+
+        self.vapi.nat64_add_del_static_bib(client.ip6n,
+                                           client_nat_ip_n,
+                                           client_tcp_in_port,
+                                           client_tcp_out_port,
+                                           IP_PROTOS.tcp)
+
         # client to server
         p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
-             IPv6(src=client.ip6, dst=nat_addr_ip6) /
+             IPv6(src=client.ip6, dst=server_nat_ip6) /
              TCP(sport=client_tcp_in_port, dport=server_tcp_out_port))
         self.pg0.add_stream(p)
         self.pg_enable_capture(self.pg_interfaces)
@@ -3585,7 +3602,7 @@ class TestNAT64(MethodHolder):
         p = self.pg0.get_capture(1)
 
         p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
-             IPv6(src=client.ip6, dst=nat_addr_ip6) /
+             IPv6(src=client.ip6, dst=server_nat_ip6, nh=IP_PROTOS.gre) /
              GRE() /
              IP(src=self.pg2.local_ip4, dst=self.pg2.remote_ip4) /
              TCP(sport=1234, dport=1234))
@@ -3595,16 +3612,16 @@ class TestNAT64(MethodHolder):
         p = self.pg0.get_capture(1)
         packet = p[0]
         try:
-            self.assertEqual(packet[IPv6].src, nat_addr_ip6)
+            self.assertEqual(packet[IPv6].src, client_nat_ip6)
             self.assertEqual(packet[IPv6].dst, server.ip6)
-            self.assertTrue(packet.haslayer(GRE))
+            self.assertEqual(packet[IPv6].nh, IP_PROTOS.gre)
         except:
             self.logger.error(ppp("Unexpected or invalid packet:", packet))
             raise
 
         # server to client
         p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
-             IPv6(src=server.ip6, dst=nat_addr_ip6) /
+             IPv6(src=server.ip6, dst=client_nat_ip6, nh=IP_PROTOS.gre) /
              GRE() /
              IP(src=self.pg2.remote_ip4, dst=self.pg2.local_ip4) /
              TCP(sport=1234, dport=1234))
@@ -3614,9 +3631,9 @@ class TestNAT64(MethodHolder):
         p = self.pg0.get_capture(1)
         packet = p[0]
         try:
-            self.assertEqual(packet[IPv6].src, nat_addr_ip6)
+            self.assertEqual(packet[IPv6].src, server_nat_ip6)
             self.assertEqual(packet[IPv6].dst, client.ip6)
-            self.assertTrue(packet.haslayer(GRE))
+            self.assertEqual(packet[IPv6].nh, IP_PROTOS.gre)
         except:
             self.logger.error(ppp("Unexpected or invalid packet:", packet))
             raise
@@ -3702,9 +3719,11 @@ class TestNAT64(MethodHolder):
             self.logger.info(self.vapi.cli("show nat64 bib tcp"))
             self.logger.info(self.vapi.cli("show nat64 bib udp"))
             self.logger.info(self.vapi.cli("show nat64 bib icmp"))
+            self.logger.info(self.vapi.cli("show nat64 bib unknown"))
             self.logger.info(self.vapi.cli("show nat64 session table tcp"))
             self.logger.info(self.vapi.cli("show nat64 session table udp"))
             self.logger.info(self.vapi.cli("show nat64 session table icmp"))
+            self.logger.info(self.vapi.cli("show nat64 session table unknown"))
             self.clear_nat64()
 
 if __name__ == '__main__':