NAT64: Fallback to 3-tuple key for non TCP/UDP sessions (VPP-884)
Change-Id: I4cafc8291725feb499355092bd429433e649b5b2
Signed-off-by: Matus Fabian <matfabia@cisco.com>
diff --git a/src/plugins/snat/nat64.c b/src/plugins/snat/nat64.c
index 47809b0..bd915b5 100644
--- a/src/plugins/snat/nat64.c
+++ b/src/plugins/snat/nat64.c
@@ -125,7 +125,9 @@
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 @@
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 @@
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_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_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 @@
ste->expire = now + nm->udp_timeout;
return;
default:
+ ste->expire = now + nm->udp_timeout;
return;
}
}
@@ -808,9 +814,6 @@
clib_warning ("invalid prefix length");
break;
}
-
- clib_warning ("%U %U plen %u", format_ip6_address, ip6, format_ip4_address,
- ip4, plen);
}
/**
diff --git a/src/plugins/snat/nat64_cli.c b/src/plugins/snat/nat64_cli.c
index 32f671d..d48cb72 100644
--- a/src/plugins/snat/nat64_cli.c
+++ b/src/plugins/snat/nat64_cli.c
@@ -303,7 +303,7 @@
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 @@
;
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 @@
}
}
- 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 @@
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_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 @@
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 @@
}
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,26 +591,36 @@
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);
+ 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,
+ clib_net_to_host_u16 (ste->r_port),
+ format_ip4_address, &bibe->out_addr,
+ 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,
+ ip_proto_to_snat_proto (bibe->proto), vrf_id);
else
- vlib_cli_output (vm, " %U %u %U %u %U %u %U %u %U vrf %u",
+ vlib_cli_output (vm, " %U %U %U %U protocol %u vrf %u",
format_ip6_address, &bibe->in_addr,
- clib_net_to_host_u16 (bibe->in_port),
format_ip6_address, &ste->in_r_addr,
- clib_net_to_host_u16 (ste->r_port),
format_ip4_address, &bibe->out_addr,
- 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);
+ bibe->proto, vrf_id);
+
return 0;
}
@@ -593,7 +631,8 @@
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 @@
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 @@
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 (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_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,
};
diff --git a/src/plugins/snat/nat64_db.c b/src/plugins/snat/nat64_db.c
index d15761d..b6e199c 100644
--- a/src/plugins/snat/nat64_db.c
+++ b/src/plugins/snat/nat64_db.c
@@ -44,7 +44,7 @@
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 @@
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 @@
#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 @@
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 @@
#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_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 @@
#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 @@
}
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 @@
#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_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 @@
#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 @@
#undef _
/* *INDENT-ON* */
default:
- clib_warning ("unknown protocol");
- return;
+ st = db->st._unk_proto_st;
+ break;
}
/* *INDENT-OFF* */
@@ -304,7 +305,7 @@
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 @@
#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_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 @@
#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_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 @@
#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 @@
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* */
}
diff --git a/src/plugins/snat/nat64_db.h b/src/plugins/snat/nat64_db.h
index caea3bf..4511fb2 100644
--- a/src/plugins/snat/nat64_db.h
+++ b/src/plugins/snat/nat64_db.h
@@ -63,6 +63,7 @@
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 @@
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 @@
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 @@
* @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 @@
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 @@
* @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 @@
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 @@
* @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 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__ */
/*
diff --git a/src/plugins/snat/nat64_in2out.c b/src/plugins/snat/nat64_in2out.c
index 126b076..8c67fec 100644
--- a/src/plugins/snat/nat64_in2out.c
+++ b/src/plugins/snat/nat64_in2out.c
@@ -25,6 +25,7 @@
{
u32 sw_if_index;
u32 next_index;
+ u8 is_slow_path;
} nat64_in2out_trace_t;
static u8 *
@@ -33,15 +34,19 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
{
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_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_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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
}
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 @@
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 @@
goto trace0;
}
}
- else
+ else if (proto0 == SNAT_PROTOCOL_TCP || proto0 == SNAT_PROTOCOL_UDP)
{
if (is_hairpinning (&ip60->dst_address))
{
@@ -711,6 +1033,7 @@
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 @@
}
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
*
diff --git a/src/plugins/snat/nat64_out2in.c b/src/plugins/snat/nat64_out2in.c
index 755aa63..cd5b253 100644
--- a/src/plugins/snat/nat64_out2in.c
+++ b/src/plugins/snat/nat64_out2in.c
@@ -88,7 +88,7 @@
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 @@
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 @@
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 @@
{
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_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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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)
diff --git a/src/plugins/snat/snat.c b/src/plugins/snat/snat.c
index f196b5c..315cec8 100644
--- a/src/plugins/snat/snat.c
+++ b/src/plugins/snat/snat.c
@@ -1398,6 +1398,7 @@
#undef _
default:
s = format (s, "unknown");
+ return s;
}
s = format (s, "%s", t);
return s;
diff --git a/src/plugins/snat/snat_api.c b/src/plugins/snat/snat_api.c
index ee623d2..227074f 100644
--- a/src/plugins/snat/snat_api.c
+++ b/src/plugins/snat/snat_api.c
@@ -1580,7 +1580,7 @@
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 @@
{
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 @@
.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 @@
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 @@
{
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 @@
.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 *
diff --git a/src/vnet/ip/ip4_to_ip6.h b/src/vnet/ip/ip4_to_ip6.h
index cdd3707..6ffc562 100644
--- a/src/vnet/ip/ip4_to_ip6.h
+++ b/src/vnet/ip/ip4_to_ip6.h
@@ -586,6 +586,68 @@
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__ */
/*
diff --git a/src/vnet/ip/ip6_to_ip4.h b/src/vnet/ip/ip6_to_ip4.h
index 7a0d534..c14b46c 100644
--- a/src/vnet/ip/ip6_to_ip4.h
+++ b/src/vnet/ip/ip6_to_ip4.h
@@ -562,6 +562,67 @@
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__ */
/*
diff --git a/test/test_snat.py b/test/test_snat.py
index 9f5377e..8fd05fa 100644
--- a/test/test_snat.py
+++ b/test/test_snat.py
@@ -3497,7 +3497,7 @@
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 @@
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 @@
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 @@
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)
+ 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(self.nat_addr_n,
- self.nat_addr_n)
+ 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 @@
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 @@
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 @@
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 @@
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__':