aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/plugins/snat/nat64.c210
-rw-r--r--src/plugins/snat/nat64.h59
-rw-r--r--src/plugins/snat/nat64_cli.c103
-rw-r--r--src/plugins/snat/nat64_in2out.c54
-rw-r--r--src/plugins/snat/nat64_out2in.c40
-rw-r--r--src/plugins/snat/snat_api.c93
-rw-r--r--test/test_snat.py139
-rw-r--r--test/vpp_papi_provider.py32
8 files changed, 658 insertions, 72 deletions
diff --git a/src/plugins/snat/nat64.c b/src/plugins/snat/nat64.c
index d6e052f106d..d7772a74d36 100644
--- a/src/plugins/snat/nat64.c
+++ b/src/plugins/snat/nat64.c
@@ -38,6 +38,13 @@ VNET_FEATURE_INIT (nat64_out2in, static) = {
.runs_before = VNET_FEATURES ("ip4-lookup"),
};
+static u8 well_known_prefix[] = {
+ 0x00, 0x64, 0xff, 0x9b,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00
+};
+
/* *INDENT-ON* */
clib_error_t *
@@ -600,6 +607,209 @@ nat64_tcp_session_set_state (nat64_db_st_entry_t * ste, tcp_header_t * tcp,
}
}
+int
+nat64_add_del_prefix (ip6_address_t * prefix, u8 plen, u32 vrf_id, u8 is_add)
+{
+ nat64_main_t *nm = &nat64_main;
+ nat64_prefix_t *p = 0;
+ int i;
+
+ /* Verify prefix length */
+ if (plen != 32 && plen != 40 && plen != 48 && plen != 56 && plen != 64
+ && plen != 96)
+ return VNET_API_ERROR_INVALID_VALUE;
+
+ /* Check if tenant already have prefix */
+ for (i = 0; i < vec_len (nm->pref64); i++)
+ {
+ if (nm->pref64[i].vrf_id == vrf_id)
+ {
+ p = nm->pref64 + i;
+ break;
+ }
+ }
+
+ if (is_add)
+ {
+ if (!p)
+ {
+ vec_add2 (nm->pref64, p, 1);
+ p->fib_index =
+ fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP6, vrf_id);
+ p->vrf_id = vrf_id;
+ }
+
+ p->prefix.as_u64[0] = prefix->as_u64[0];
+ p->prefix.as_u64[1] = prefix->as_u64[1];
+ p->plen = plen;
+ }
+ else
+ {
+ if (!p)
+ return VNET_API_ERROR_NO_SUCH_ENTRY;
+
+ vec_del1 (nm->pref64, i);
+ }
+
+ return 0;
+}
+
+void
+nat64_prefix_walk (nat64_prefix_walk_fn_t fn, void *ctx)
+{
+ nat64_main_t *nm = &nat64_main;
+ nat64_prefix_t *p = 0;
+
+ /* *INDENT-OFF* */
+ vec_foreach (p, nm->pref64)
+ {
+ if (fn (p, ctx))
+ break;
+ };
+ /* *INDENT-ON* */
+}
+
+void
+nat64_compose_ip6 (ip6_address_t * ip6, ip4_address_t * ip4, u32 fib_index)
+{
+ nat64_main_t *nm = &nat64_main;
+ nat64_prefix_t *p, *gp = 0, *prefix = 0;
+
+ /* *INDENT-OFF* */
+ vec_foreach (p, nm->pref64)
+ {
+ if (p->fib_index == fib_index)
+ {
+ prefix = p;
+ break;
+ }
+
+ if (p->fib_index == 0)
+ gp = p;
+ };
+ /* *INDENT-ON* */
+
+ if (!prefix)
+ prefix = gp;
+
+ if (prefix)
+ {
+ memset (ip6, 0, 16);
+ memcpy (ip6, &p->prefix, p->plen);
+ switch (p->plen)
+ {
+ case 32:
+ ip6->as_u32[1] = ip4->as_u32;
+ break;
+ case 40:
+ ip6->as_u8[5] = ip4->as_u8[0];
+ ip6->as_u8[6] = ip4->as_u8[1];
+ ip6->as_u8[7] = ip4->as_u8[2];
+ ip6->as_u8[9] = ip4->as_u8[3];
+ break;
+ case 48:
+ ip6->as_u8[6] = ip4->as_u8[0];
+ ip6->as_u8[7] = ip4->as_u8[1];
+ ip6->as_u8[9] = ip4->as_u8[2];
+ ip6->as_u8[10] = ip4->as_u8[3];
+ break;
+ case 56:
+ ip6->as_u8[7] = ip4->as_u8[0];
+ ip6->as_u8[9] = ip4->as_u8[1];
+ ip6->as_u8[10] = ip4->as_u8[2];
+ ip6->as_u8[11] = ip4->as_u8[3];
+ break;
+ case 64:
+ ip6->as_u8[9] = ip4->as_u8[0];
+ ip6->as_u8[10] = ip4->as_u8[1];
+ ip6->as_u8[11] = ip4->as_u8[2];
+ ip6->as_u8[12] = ip4->as_u8[3];
+ break;
+ case 96:
+ ip6->as_u32[3] = ip4->as_u32;
+ break;
+ default:
+ clib_warning ("invalid prefix length");
+ break;
+ }
+ }
+ else
+ {
+ memcpy (ip6, well_known_prefix, 16);
+ ip6->as_u32[3] = ip4->as_u32;
+ }
+}
+
+void
+nat64_extract_ip4 (ip6_address_t * ip6, ip4_address_t * ip4, u32 fib_index)
+{
+ nat64_main_t *nm = &nat64_main;
+ nat64_prefix_t *p, *gp = 0;
+ u8 plen = 0;
+
+ /* *INDENT-OFF* */
+ vec_foreach (p, nm->pref64)
+ {
+ if (p->fib_index == fib_index)
+ {
+ plen = p->plen;
+ break;
+ }
+
+ if (p->vrf_id == 0)
+ gp = p;
+ };
+ /* *INDENT-ON* */
+
+ if (!plen)
+ {
+ if (gp)
+ plen = gp->plen;
+ else
+ plen = 96;
+ }
+
+ switch (plen)
+ {
+ case 32:
+ ip4->as_u32 = ip6->as_u32[1];
+ break;
+ case 40:
+ ip4->as_u8[0] = ip6->as_u8[5];
+ ip4->as_u8[1] = ip6->as_u8[6];
+ ip4->as_u8[2] = ip6->as_u8[7];
+ ip4->as_u8[3] = ip6->as_u8[9];
+ break;
+ case 48:
+ ip4->as_u8[0] = ip6->as_u8[6];
+ ip4->as_u8[1] = ip6->as_u8[7];
+ ip4->as_u8[2] = ip6->as_u8[9];
+ ip4->as_u8[3] = ip6->as_u8[10];
+ break;
+ case 56:
+ ip4->as_u8[0] = ip6->as_u8[7];
+ ip4->as_u8[1] = ip6->as_u8[9];
+ ip4->as_u8[2] = ip6->as_u8[10];
+ ip4->as_u8[3] = ip6->as_u8[11];
+ break;
+ case 64:
+ ip4->as_u8[0] = ip6->as_u8[9];
+ ip4->as_u8[1] = ip6->as_u8[10];
+ ip4->as_u8[2] = ip6->as_u8[11];
+ ip4->as_u8[3] = ip6->as_u8[12];
+ break;
+ case 96:
+ ip4->as_u32 = ip6->as_u32[3];
+ break;
+ default:
+ clib_warning ("invalid prefix length");
+ break;
+ }
+
+ clib_warning ("%U %U plen %u", format_ip6_address, ip6, format_ip4_address,
+ ip4, plen);
+}
+
/**
* @brief The 'nat64-expire-walk' process's main loop.
*
diff --git a/src/plugins/snat/nat64.h b/src/plugins/snat/nat64.h
index df0c470ab1f..771b9075405 100644
--- a/src/plugins/snat/nat64.h
+++ b/src/plugins/snat/nat64.h
@@ -41,13 +41,24 @@ typedef enum
typedef struct
{
+ ip6_address_t prefix;
+ u8 plen;
+ u32 vrf_id;
+ u32 fib_index;
+} nat64_prefix_t;
+
+typedef struct
+{
/** Interface pool */
snat_interface_t *interfaces;
/** Address pool vector */
snat_address_t *addr_pool;
- /** BIB and session DB **/
+ /** Pref64 vector */
+ nat64_prefix_t *pref64;
+
+ /** BIB and session DB */
nat64_db_t db;
/* values of various timeouts */
@@ -251,6 +262,52 @@ void nat64_session_reset_timeout (nat64_db_st_entry_t * ste,
void nat64_tcp_session_set_state (nat64_db_st_entry_t * ste,
tcp_header_t * tcp, u8 is_ip6);
+/**
+ * @brief Add/delete NAT64 prefix.
+ *
+ * @param prefix NAT64 prefix.
+ * @param plen Prefix length.
+ * @param vrf_id VRF id of tenant.
+ * @param is_add 1 if add, 0 if delete.
+ *
+ * @returns 0 on success, non-zero value otherwise.
+ */
+int nat64_add_del_prefix (ip6_address_t * prefix, u8 plen, u32 vrf_id,
+ u8 is_add);
+
+/**
+ * @brief Call back function when walking addresses in NAT64 prefixes, non-zero
+ * return value stop walk.
+ */
+typedef int (*nat64_prefix_walk_fn_t) (nat64_prefix_t * pref64, void *ctx);
+
+/**
+ * @brief Walk NAT64 prefixes.
+ *
+ * @param fn The function to invoke on each entry visited.
+ * @param ctx A context passed in the visit function.
+ */
+void nat64_prefix_walk (nat64_prefix_walk_fn_t fn, void *ctx);
+
+/**
+ * Compose IPv4-embedded IPv6 addresses.
+ * @param ip6 IPv4-embedded IPv6 addresses.
+ * @param ip4 IPv4 address.
+ * @param fib_index Tenant FIB index.
+ */
+void nat64_compose_ip6 (ip6_address_t * ip6, ip4_address_t * ip4,
+ u32 fib_index);
+
+/**
+ * Extract IPv4 address from the IPv4-embedded IPv6 addresses.
+ *
+ * @param ip6 IPv4-embedded IPv6 addresses.
+ * @param ip4 IPv4 address.
+ * @param fib_index Tenant FIB index.
+ */
+void nat64_extract_ip4 (ip6_address_t * ip6, ip4_address_t * ip4,
+ u32 fib_index);
+
#define u8_ptr_add(ptr, index) (((u8 *)ptr) + index)
#define u16_net_add(u, val) clib_host_to_net_u16(clib_net_to_host_u16(u) + (val))
diff --git a/src/plugins/snat/nat64_cli.c b/src/plugins/snat/nat64_cli.c
index 293ceaf5efb..25345cd818d 100644
--- a/src/plugins/snat/nat64_cli.c
+++ b/src/plugins/snat/nat64_cli.c
@@ -621,6 +621,96 @@ done:
return error;
}
+static clib_error_t *
+nat64_add_del_prefix_command_fn (vlib_main_t * vm, unformat_input_t * input,
+ vlib_cli_command_t * cmd)
+{
+ nat64_main_t *nm = &nat64_main;
+ clib_error_t *error = 0;
+ unformat_input_t _line_input, *line_input = &_line_input;
+ u8 is_add = 1;
+ u32 vrf_id = 0;
+ ip6_address_t prefix;
+ u32 plen = 0;
+ int rv;
+
+ if (nm->is_disabled)
+ return clib_error_return (0,
+ "NAT64 disabled, multi thread not supported");
+
+ if (!unformat_user (input, unformat_line_input, line_input))
+ return 0;
+
+ while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
+ {
+ if (unformat
+ (line_input, "%U/%u", unformat_ip6_address, &prefix, &plen))
+ ;
+ else if (unformat (line_input, "tenant-vrf %u", &vrf_id))
+ ;
+ else if (unformat (line_input, "del"))
+ is_add = 0;
+ else
+ {
+ error = clib_error_return (0, "unknown input: '%U'",
+ format_unformat_error, line_input);
+ goto done;
+ }
+ }
+
+ if (!plen)
+ {
+ error = clib_error_return (0, "NAT64 prefix must be set.");
+ goto done;
+ }
+
+ rv = nat64_add_del_prefix (&prefix, (u8) plen, vrf_id, is_add);
+
+ switch (rv)
+ {
+ case VNET_API_ERROR_NO_SUCH_ENTRY:
+ error = clib_error_return (0, "NAT64 prefix not exist.");
+ goto done;
+ case VNET_API_ERROR_INVALID_VALUE:
+ error = clib_error_return (0, "Invalid prefix length.");
+ goto done;
+ default:
+ break;
+ }
+
+done:
+ unformat_free (line_input);
+
+ return error;
+}
+
+static int
+nat64_cli_prefix_walk (nat64_prefix_t * p, void *ctx)
+{
+ vlib_main_t *vm = ctx;
+
+ vlib_cli_output (vm, " %U/%u tenant-vrf %u",
+ format_ip6_address, &p->prefix, p->plen, p->vrf_id);
+
+ return 0;
+}
+
+static clib_error_t *
+nat64_show_prefix_command_fn (vlib_main_t * vm, unformat_input_t * input,
+ vlib_cli_command_t * cmd)
+{
+ nat64_main_t *nm = &nat64_main;
+
+ if (nm->is_disabled)
+ return clib_error_return (0,
+ "NAT64 disabled, multi thread not supported");
+
+ vlib_cli_output (vm, "NAT64 prefix:");
+ nat64_prefix_walk (nat64_cli_prefix_walk, vm);
+
+ return 0;
+}
+
/* *INDENT-OFF* */
VLIB_CLI_COMMAND (nat64_add_pool_address_command, static) = {
@@ -680,6 +770,19 @@ VLIB_CLI_COMMAND (show_nat64_st_command, static) = {
.function = nat64_show_st_command_fn,
};
+VLIB_CLI_COMMAND (nat64_add_del_prefix_command, static) = {
+ .path = "nat64 add prefix",
+ .short_help = "nat64 add prefix <ip6-prefix>/<plen> [tenant-vrf <vrf-id>] "
+ "[del]",
+ .function = nat64_add_del_prefix_command_fn,
+};
+
+VLIB_CLI_COMMAND (show_nat64_prefix_command, static) = {
+ .path = "show nat64 prefix",
+ .short_help = "show nat64 prefix",
+ .function = nat64_show_prefix_command_fn,
+};
+
/* *INDENT-ON* */
/*
diff --git a/src/plugins/snat/nat64_in2out.c b/src/plugins/snat/nat64_in2out.c
index fd32bfc5be7..126b076c7ce 100644
--- a/src/plugins/snat/nat64_in2out.c
+++ b/src/plugins/snat/nat64_in2out.c
@@ -21,15 +21,6 @@
#include <vnet/ip/ip6_to_ip4.h>
#include <vnet/fib/fib_table.h>
-/* *INDENT-OFF* */
-static u8 well_known_prefix[] = {
- 0x00, 0x64, 0xff, 0x9b,
- 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00
-};
-/* *INDENT-ON* */
-
typedef struct
{
u32 sw_if_index;
@@ -166,6 +157,7 @@ nat64_in2out_tcp_udp_set_cb (ip6_header_t * ip6, ip4_header_t * ip4,
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, dport);
@@ -178,7 +170,7 @@ nat64_in2out_tcp_udp_set_cb (ip6_header_t * ip6, ip4_header_t * ip4,
ip4->src_address.as_u32 = bibe->out_addr.as_u32;
udp->src_port = bibe->out_port;
- ip4->dst_address.as_u32 = daddr.ip4.as_u32;
+ ip4->dst_address.as_u32 = ste->out_r_addr.as_u32;
if (proto == SNAT_PROTOCOL_TCP)
{
@@ -252,6 +244,8 @@ nat64_in2out_icmp_set_cb (ip6_header_t * ip6, ip4_header_t * ip4, void *arg)
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);
@@ -264,7 +258,7 @@ nat64_in2out_icmp_set_cb (ip6_header_t * ip6, ip4_header_t * ip4, void *arg)
ip4->src_address.as_u32 = bibe->out_addr.as_u32;
((u16 *) (icmp))[2] = bibe->out_port;
- ip4->dst_address.as_u32 = daddr.ip4.as_u32;
+ ip4->dst_address.as_u32 = ste->out_r_addr.as_u32;
}
else
{
@@ -272,7 +266,7 @@ nat64_in2out_icmp_set_cb (ip6_header_t * ip6, ip4_header_t * ip4, void *arg)
return -1;
ip4->src_address.as_u32 = nm->addr_pool[0].addr.as_u32;
- ip4->dst_address.as_u32 = daddr.ip4.as_u32;
+ nat64_extract_ip4 (&ip6->dst_address, &ip4->dst_address, fib_index);
}
return 0;
@@ -321,7 +315,7 @@ nat64_in2out_inner_icmp_set_cb (ip6_header_t * ip6, ip4_header_t * ip4,
ip4->dst_address.as_u32 = bibe->out_addr.as_u32;
((u16 *) (icmp))[2] = bibe->out_port;
- ip4->src_address.as_u32 = saddr.ip4.as_u32;
+ ip4->src_address.as_u32 = ste->out_r_addr.as_u32;
}
else
{
@@ -345,7 +339,7 @@ nat64_in2out_inner_icmp_set_cb (ip6_header_t * ip6, ip4_header_t * ip4,
ip4->dst_address.as_u32 = bibe->out_addr.as_u32;
udp->dst_port = bibe->out_port;
- ip4->src_address.as_u32 = saddr.ip4.as_u32;
+ ip4->src_address.as_u32 = ste->out_r_addr.as_u32;
if (proto == SNAT_PROTOCOL_TCP)
checksum = &tcp->checksum;
@@ -428,6 +422,7 @@ nat64_in2out_tcp_udp_hairpinning (vlib_main_t * vm, vlib_buffer_t * b,
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, dport);
@@ -438,15 +433,12 @@ nat64_in2out_tcp_udp_hairpinning (vlib_main_t * vm, vlib_buffer_t * b,
nat64_session_reset_timeout (ste, vm);
sport = udp->src_port = bibe->out_port;
- memcpy (&ip6->src_address, well_known_prefix, sizeof (ip6_address_t));
- saddr.ip6.as_u32[3] = ip6->src_address.as_u32[3] = bibe->out_addr.as_u32;
+ nat64_compose_ip6 (&ip6->src_address, &bibe->out_addr, fib_index);
- saddr.ip6.as_u32[0] = 0;
- saddr.ip6.as_u32[1] = 0;
- saddr.ip6.as_u32[2] = 0;
- daddr.ip6.as_u32[0] = 0;
- daddr.ip6.as_u32[1] = 0;
- daddr.ip6.as_u32[2] = 0;
+ 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, dport, sport, proto, 0,
@@ -545,22 +537,17 @@ nat64_in2out_icmp_hairpinning (vlib_main_t * vm, vlib_buffer_t * b,
if (!ste)
return -1;
- clib_warning ("");
bibe = nat64_db_bib_entry_by_index (&nm->db, proto, ste->bibe_index);
if (!bibe)
return -1;
dport = udp->dst_port = bibe->out_port;
- memcpy (&inner_ip6->dst_address, well_known_prefix, sizeof (ip6_address_t));
- daddr.ip6.as_u32[3] = inner_ip6->dst_address.as_u32[3] =
- bibe->out_addr.as_u32;
+ nat64_compose_ip6 (&inner_ip6->dst_address, &bibe->out_addr, fib_index);
- saddr.ip6.as_u32[0] = 0;
- saddr.ip6.as_u32[1] = 0;
- saddr.ip6.as_u32[2] = 0;
- daddr.ip6.as_u32[0] = 0;
- daddr.ip6.as_u32[1] = 0;
- daddr.ip6.as_u32[2] = 0;
+ memset (&saddr, 0, sizeof (saddr));
+ memset (&daddr, 0, sizeof (daddr));
+ saddr.ip4.as_u32 = ste->out_r_addr.as_u32;
+ daddr.ip4.as_u32 = bibe->out_addr.as_u32;
ste =
nat64_db_st_entry_find (&nm->db, &saddr, &daddr, sport, dport, proto, 0,
@@ -587,8 +574,7 @@ nat64_in2out_icmp_hairpinning (vlib_main_t * vm, vlib_buffer_t * b,
if (!vec_len (nm->addr_pool))
return -1;
- memcpy (&ip6->src_address, well_known_prefix, sizeof (ip6_address_t));
- ip6->src_address.as_u32[3] = nm->addr_pool[0].addr.as_u32;
+ nat64_compose_ip6 (&ip6->src_address, &nm->addr_pool[0].addr, fib_index);
ip6->dst_address.as_u64[0] = inner_ip6->src_address.as_u64[0];
ip6->dst_address.as_u64[1] = inner_ip6->src_address.as_u64[1];
diff --git a/src/plugins/snat/nat64_out2in.c b/src/plugins/snat/nat64_out2in.c
index 0a5fbe5b357..755aa638aa5 100644
--- a/src/plugins/snat/nat64_out2in.c
+++ b/src/plugins/snat/nat64_out2in.c
@@ -21,15 +21,6 @@
#include <vnet/ip/ip4_to_ip6.h>
#include <vnet/fib/ip4_fib.h>
-/* *INDENT-OFF* */
-static u8 well_known_prefix[] = {
- 0x00, 0x64, 0xff, 0x9b,
- 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00
-};
-/* *INDENT-ON* */
-
typedef struct
{
u32 sw_if_index;
@@ -112,9 +103,6 @@ nat64_out2in_tcp_udp_set_cb (ip4_header_t * ip4, ip6_header_t * ip6,
memset (&daddr, 0, sizeof (daddr));
daddr.ip4.as_u32 = ip4->dst_address.as_u32;
- memcpy (&ip6_saddr, well_known_prefix, sizeof (ip6_saddr));
- ip6_saddr.as_u32[3] = ip4->src_address.as_u32;
-
ste =
nat64_db_st_entry_find (&nm->db, &daddr, &saddr, dport, sport, proto,
fib_index, 0);
@@ -132,6 +120,7 @@ nat64_out2in_tcp_udp_set_cb (ip4_header_t * ip4, ip6_header_t * ip6,
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,
sport);
@@ -139,8 +128,8 @@ nat64_out2in_tcp_udp_set_cb (ip4_header_t * ip4, ip6_header_t * ip6,
nat64_session_reset_timeout (ste, ctx->vm);
- ip6->src_address.as_u64[0] = ip6_saddr.as_u64[0];
- ip6->src_address.as_u64[1] = ip6_saddr.as_u64[1];
+ 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];
@@ -179,9 +168,6 @@ nat64_out2in_icmp_set_cb (ip4_header_t * ip4, ip6_header_t * ip6, void *arg)
memset (&daddr, 0, sizeof (daddr));
daddr.ip4.as_u32 = ip4->dst_address.as_u32;
- memcpy (&ip6_saddr, well_known_prefix, sizeof (ip6_saddr));
- ip6_saddr.as_u32[3] = ip4->src_address.as_u32;
-
if (icmp->type == ICMP6_echo_request || icmp->type == ICMP6_echo_reply)
{
u16 out_id = ((u16 *) (icmp))[2];
@@ -205,6 +191,7 @@ nat64_out2in_icmp_set_cb (ip4_header_t * ip4, ip6_header_t * ip6, void *arg)
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);
@@ -212,8 +199,8 @@ nat64_out2in_icmp_set_cb (ip4_header_t * ip4, ip6_header_t * ip6, void *arg)
nat64_session_reset_timeout (ste, ctx->vm);
- ip6->src_address.as_u64[0] = ip6_saddr.as_u64[0];
- ip6->src_address.as_u64[1] = ip6_saddr.as_u64[1];
+ 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];
@@ -225,8 +212,8 @@ nat64_out2in_icmp_set_cb (ip4_header_t * ip4, ip6_header_t * ip6, void *arg)
{
ip6_header_t *inner_ip6 = (ip6_header_t *) u8_ptr_add (icmp, 8);
- ip6->src_address.as_u64[0] = ip6_saddr.as_u64[0];
- ip6->src_address.as_u64[1] = ip6_saddr.as_u64[1];
+ nat64_compose_ip6 (&ip6->src_address, &ip4->src_address,
+ vnet_buffer (ctx->b)->sw_if_index[VLIB_TX]);
ip6->dst_address.as_u64[0] = inner_ip6->src_address.as_u64[0];
ip6->dst_address.as_u64[1] = inner_ip6->src_address.as_u64[1];
}
@@ -243,7 +230,6 @@ nat64_out2in_inner_icmp_set_cb (ip4_header_t * ip4, ip6_header_t * ip6,
nat64_db_bib_entry_t *bibe;
nat64_db_st_entry_t *ste;
ip46_address_t saddr, daddr;
- ip6_address_t ip6_daddr;
u32 sw_if_index, fib_index;
snat_protocol_t proto = ip_proto_to_snat_proto (ip4->protocol);
@@ -256,9 +242,6 @@ 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;
- memcpy (&ip6_daddr, well_known_prefix, sizeof (ip6_daddr));
- ip6_daddr.as_u32[3] = ip4->dst_address.as_u32;
-
if (proto == SNAT_PROTOCOL_ICMP)
{
icmp46_header_t *icmp = ip4_next_header (ip4);
@@ -279,8 +262,8 @@ nat64_out2in_inner_icmp_set_cb (ip4_header_t * ip4, ip6_header_t * ip6,
if (!bibe)
return -1;
- ip6->dst_address.as_u64[0] = ip6_daddr.as_u64[0];
- ip6->dst_address.as_u64[1] = ip6_daddr.as_u64[1];
+ ip6->dst_address.as_u64[0] = ste->in_r_addr.as_u64[0];
+ ip6->dst_address.as_u64[1] = ste->in_r_addr.as_u64[1];
ip6->src_address.as_u64[0] = bibe->in_addr.as_u64[0];
ip6->src_address.as_u64[1] = bibe->in_addr.as_u64[1];
((u16 *) (icmp))[2] = bibe->in_port;
@@ -306,8 +289,7 @@ nat64_out2in_inner_icmp_set_cb (ip4_header_t * ip4, ip6_header_t * ip6,
if (!bibe)
return -1;
- ip6->dst_address.as_u64[0] = ip6_daddr.as_u64[0];
- ip6->dst_address.as_u64[1] = ip6_daddr.as_u64[1];
+ nat64_compose_ip6 (&ip6->dst_address, &daddr.ip4, bibe->fib_index);
ip6->src_address.as_u64[0] = bibe->in_addr.as_u64[0];
ip6->src_address.as_u64[1] = bibe->in_addr.as_u64[1];
udp->src_port = bibe->in_port;
diff --git a/src/plugins/snat/snat_api.c b/src/plugins/snat/snat_api.c
index aa0b59ac4a0..638f576c354 100644
--- a/src/plugins/snat/snat_api.c
+++ b/src/plugins/snat/snat_api.c
@@ -1677,6 +1677,95 @@ vl_api_nat64_st_dump_t_print (vl_api_nat64_st_dump_t * mp, void *handle)
FINISH;
}
+static void
+vl_api_nat64_add_del_prefix_t_handler (vl_api_nat64_add_del_prefix_t * mp)
+{
+ vl_api_nat64_add_del_prefix_reply_t *rmp;
+ snat_main_t *sm = &snat_main;
+ nat64_main_t *nm = &nat64_main;
+ ip6_address_t prefix;
+ int rv = 0;
+
+ if (nm->is_disabled)
+ {
+ rv = VNET_API_ERROR_FEATURE_DISABLED;
+ goto send_reply;
+ }
+
+ memcpy (&prefix.as_u8, mp->prefix, 16);
+
+ rv =
+ nat64_add_del_prefix (&prefix, mp->prefix_len,
+ clib_net_to_host_u32 (mp->vrf_id), mp->is_add);
+send_reply:
+ REPLY_MACRO (VL_API_NAT64_ADD_DEL_PREFIX_REPLY);
+}
+
+static void *
+vl_api_nat64_add_del_prefix_t_print (vl_api_nat64_add_del_prefix_t * mp,
+ void *handle)
+{
+ u8 *s;
+
+ s = format (0, "SCRIPT: nat64_add_del_prefix %U/%u vrf_id %u %s\n",
+ format_ip6_address, mp->prefix, mp->prefix_len,
+ ntohl (mp->vrf_id), mp->is_add ? "" : "del");
+
+ FINISH;
+}
+
+static int
+nat64_api_prefix_walk (nat64_prefix_t * p, void *arg)
+{
+ vl_api_nat64_prefix_details_t *rmp;
+ snat_main_t *sm = &snat_main;
+ nat64_api_walk_ctx_t *ctx = arg;
+
+ rmp = vl_msg_api_alloc (sizeof (*rmp));
+ memset (rmp, 0, sizeof (*rmp));
+ rmp->_vl_msg_id = ntohs (VL_API_NAT64_PREFIX_DETAILS + sm->msg_id_base);
+ clib_memcpy (rmp->prefix, &(p->prefix), 16);
+ rmp->prefix_len = p->plen;
+ rmp->vrf_id = ntohl (p->vrf_id);
+ rmp->context = ctx->context;
+
+ vl_msg_api_send_shmem (ctx->q, (u8 *) & rmp);
+
+ return 0;
+}
+
+static void
+vl_api_nat64_prefix_dump_t_handler (vl_api_nat64_prefix_dump_t * mp)
+{
+ unix_shared_memory_queue_t *q;
+ nat64_main_t *nm = &nat64_main;
+
+ if (nm->is_disabled)
+ return;
+
+ q = vl_api_client_index_to_input_queue (mp->client_index);
+ if (q == 0)
+ return;
+
+ nat64_api_walk_ctx_t ctx = {
+ .q = q,
+ .context = mp->context,
+ };
+
+ nat64_prefix_walk (nat64_api_prefix_walk, &ctx);
+}
+
+static void *
+vl_api_nat64_prefix_dump_t_print (vl_api_nat64_prefix_dump_t * mp,
+ void *handle)
+{
+ u8 *s;
+
+ s = format (0, "SCRIPT: nat64_prefix_dump\n");
+
+ FINISH;
+}
+
/* List of message types that this plugin understands */
#define foreach_snat_plugin_api_msg \
_(SNAT_ADD_ADDRESS_RANGE, snat_add_address_range) \
@@ -1711,7 +1800,9 @@ _(NAT64_ADD_DEL_STATIC_BIB, nat64_add_del_static_bib) \
_(NAT64_BIB_DUMP, nat64_bib_dump) \
_(NAT64_SET_TIMEOUTS, nat64_set_timeouts) \
_(NAT64_GET_TIMEOUTS, nat64_get_timeouts) \
-_(NAT64_ST_DUMP, nat64_st_dump)
+_(NAT64_ST_DUMP, nat64_st_dump) \
+_(NAT64_ADD_DEL_PREFIX, nat64_add_del_prefix) \
+_(NAT64_PREFIX_DUMP, nat64_prefix_dump)
/* Set up the API message handling tables */
static clib_error_t *
diff --git a/test/test_snat.py b/test/test_snat.py
index 75445820a2f..d8268770f02 100644
--- a/test/test_snat.py
+++ b/test/test_snat.py
@@ -157,16 +157,65 @@ class MethodHolder(VppTestCase):
return pkts
- def create_stream_in_ip6(self, in_if, out_if, hlim=64):
+ def compose_ip6(self, ip4, pref, plen):
+ """
+ Compose IPv4-embedded IPv6 addresses
+
+ :param ip4: IPv4 address
+ :param pref: IPv6 prefix
+ :param plen: IPv6 prefix length
+ :returns: IPv4-embedded IPv6 addresses
+ """
+ pref_n = list(socket.inet_pton(socket.AF_INET6, pref))
+ ip4_n = list(socket.inet_pton(socket.AF_INET, ip4))
+ if plen == 32:
+ pref_n[4] = ip4_n[0]
+ pref_n[5] = ip4_n[1]
+ pref_n[6] = ip4_n[2]
+ pref_n[7] = ip4_n[3]
+ elif plen == 40:
+ pref_n[5] = ip4_n[0]
+ pref_n[6] = ip4_n[1]
+ pref_n[7] = ip4_n[2]
+ pref_n[9] = ip4_n[3]
+ elif plen == 48:
+ pref_n[6] = ip4_n[0]
+ pref_n[7] = ip4_n[1]
+ pref_n[9] = ip4_n[2]
+ pref_n[10] = ip4_n[3]
+ elif plen == 56:
+ pref_n[7] = ip4_n[0]
+ pref_n[9] = ip4_n[1]
+ pref_n[10] = ip4_n[2]
+ pref_n[11] = ip4_n[3]
+ elif plen == 64:
+ pref_n[9] = ip4_n[0]
+ pref_n[10] = ip4_n[1]
+ pref_n[11] = ip4_n[2]
+ pref_n[12] = ip4_n[3]
+ elif plen == 96:
+ pref_n[12] = ip4_n[0]
+ pref_n[13] = ip4_n[1]
+ pref_n[14] = ip4_n[2]
+ pref_n[15] = ip4_n[3]
+ return socket.inet_ntop(socket.AF_INET6, ''.join(pref_n))
+
+ def create_stream_in_ip6(self, in_if, out_if, hlim=64, pref=None, plen=0):
"""
Create IPv6 packet stream for inside network
:param in_if: Inside interface
:param out_if: Outside interface
:param ttl: Hop Limit of generated packets
+ :param pref: NAT64 prefix
+ :param plen: NAT64 prefix length
"""
pkts = []
- dst = ''.join(['64:ff9b::', out_if.remote_ip4])
+ if pref is None:
+ dst = ''.join(['64:ff9b::', out_if.remote_ip4])
+ else:
+ dst = self.compose_ip6(out_if.remote_ip4, pref, plen)
+
# TCP
p = (Ether(dst=in_if.local_mac, src=in_if.remote_mac) /
IPv6(src=in_if.remote_ip6, dst=dst, hlim=hlim) /
@@ -3101,6 +3150,84 @@ class TestNAT64(MethodHolder):
self.logger.error(ppp("Unexpected or invalid packet:", packet))
raise
+ def test_prefix(self):
+ """ NAT64 Network-Specific Prefix """
+
+ self.vapi.nat64_add_del_pool_addr_range(self.nat_addr_n,
+ self.nat_addr_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_pool_addr_range(self.vrf1_nat_addr_n,
+ self.vrf1_nat_addr_n,
+ vrf_id=self.vrf1_id)
+ self.vapi.nat64_add_del_interface(self.pg2.sw_if_index)
+
+ # Add global prefix
+ global_pref64 = "2001:db8::"
+ global_pref64_n = socket.inet_pton(socket.AF_INET6, global_pref64)
+ global_pref64_len = 32
+ self.vapi.nat64_add_del_prefix(global_pref64_n, global_pref64_len)
+
+ prefix = self.vapi.nat64_prefix_dump()
+ self.assertEqual(len(prefix), 1)
+ self.assertEqual(prefix[0].prefix, global_pref64_n)
+ self.assertEqual(prefix[0].prefix_len, global_pref64_len)
+ self.assertEqual(prefix[0].vrf_id, 0)
+
+ # Add tenant specific prefix
+ vrf1_pref64 = "2001:db8:122:300::"
+ vrf1_pref64_n = socket.inet_pton(socket.AF_INET6, vrf1_pref64)
+ vrf1_pref64_len = 56
+ self.vapi.nat64_add_del_prefix(vrf1_pref64_n,
+ vrf1_pref64_len,
+ vrf_id=self.vrf1_id)
+ prefix = self.vapi.nat64_prefix_dump()
+ self.assertEqual(len(prefix), 2)
+
+ # Global prefix
+ pkts = self.create_stream_in_ip6(self.pg0,
+ self.pg1,
+ pref=global_pref64,
+ plen=global_pref64_len)
+ self.pg0.add_stream(pkts)
+ self.pg_enable_capture(self.pg_interfaces)
+ self.pg_start()
+ capture = self.pg1.get_capture(len(pkts))
+ self.verify_capture_out(capture, nat_ip=self.nat_addr,
+ dst_ip=self.pg1.remote_ip4)
+
+ pkts = self.create_stream_out(self.pg1, dst_ip=self.nat_addr)
+ self.pg1.add_stream(pkts)
+ self.pg_enable_capture(self.pg_interfaces)
+ self.pg_start()
+ capture = self.pg0.get_capture(len(pkts))
+ dst_ip = self.compose_ip6(self.pg1.remote_ip4,
+ global_pref64,
+ global_pref64_len)
+ self.verify_capture_in_ip6(capture, dst_ip, self.pg0.remote_ip6)
+
+ # Tenant specific prefix
+ pkts = self.create_stream_in_ip6(self.pg2,
+ self.pg1,
+ pref=vrf1_pref64,
+ plen=vrf1_pref64_len)
+ self.pg2.add_stream(pkts)
+ self.pg_enable_capture(self.pg_interfaces)
+ self.pg_start()
+ capture = self.pg1.get_capture(len(pkts))
+ self.verify_capture_out(capture, nat_ip=self.vrf1_nat_addr,
+ dst_ip=self.pg1.remote_ip4)
+
+ pkts = self.create_stream_out(self.pg1, dst_ip=self.vrf1_nat_addr)
+ self.pg1.add_stream(pkts)
+ self.pg_enable_capture(self.pg_interfaces)
+ self.pg_start()
+ capture = self.pg2.get_capture(len(pkts))
+ dst_ip = self.compose_ip6(self.pg1.remote_ip4,
+ vrf1_pref64,
+ vrf1_pref64_len)
+ self.verify_capture_in_ip6(capture, dst_ip, self.pg2.remote_ip6)
+
def nat64_get_ses_num(self):
"""
Return number of active NAT64 sessions.
@@ -3166,11 +3293,19 @@ class TestNAT64(MethodHolder):
vrf_id=addr.vrf_id,
is_add=0)
+ prefixes = self.vapi.nat64_prefix_dump()
+ for prefix in prefixes:
+ self.vapi.nat64_add_del_prefix(prefix.prefix,
+ prefix.prefix_len,
+ vrf_id=prefix.vrf_id,
+ is_add=0)
+
def tearDown(self):
super(TestNAT64, self).tearDown()
if not self.vpp_dead:
self.logger.info(self.vapi.cli("show nat64 pool"))
self.logger.info(self.vapi.cli("show nat64 interfaces"))
+ self.logger.info(self.vapi.cli("show nat64 prefix"))
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"))
diff --git a/test/vpp_papi_provider.py b/test/vpp_papi_provider.py
index 5f27e8549a8..4f7e9feeec6 100644
--- a/test/vpp_papi_provider.py
+++ b/test/vpp_papi_provider.py
@@ -1424,11 +1424,11 @@ class VppPapiProvider(object):
tcp_incoming_syn=6):
"""Set values of timeouts for NAT64 (in seconds)
- :param udp - UDP timeout (Default value = 300)
- :param icmp - ICMP timeout (Default value = 60)
- :param tcp_trans - TCP transitory timeout (Default value = 240)
- :param tcp_est - TCP established timeout (Default value = 7440)
- :param tcp_incoming_syn - TCP incoming SYN timeout (Default value = 6)
+ :param udpi: UDP timeout (Default value = 300)
+ :param icmp: ICMP timeout (Default value = 60)
+ :param tcp_trans: TCP transitory timeout (Default value = 240)
+ :param tcp_est: TCP established timeout (Default value = 7440)
+ :param tcp_incoming_syn: TCP incoming SYN timeout (Default value = 6)
"""
return self.api(
self.papi.nat64_set_timeouts,
@@ -1453,6 +1453,28 @@ class VppPapiProvider(object):
"""
return self.api(self.papi.nat64_st_dump, {'proto': protocol})
+ def nat64_add_del_prefix(self, prefix, plen, vrf_id=0, is_add=1):
+ """Add/del NAT64 prefix
+
+ :param prefix: NAT64 prefix
+ :param plen: NAT64 prefix length
+ :param vrf_id: VRF id of tenant (Default 0)
+ :param is_add: 1 if add, 0 if delete (Default value = 1)
+ """
+ return self.api(
+ self.papi.nat64_add_del_prefix,
+ {'prefix': prefix,
+ 'prefix_len': plen,
+ 'vrf_id': vrf_id,
+ 'is_add': is_add})
+
+ def nat64_prefix_dump(self):
+ """Dump NAT64 prefix
+
+ :returns: Dictionary of NAT64 prefixes
+ """
+ return self.api(self.papi.nat64_prefix_dump, {})
+
def control_ping(self):
self.api(self.papi.control_ping)