diff options
-rw-r--r-- | src/plugins/snat/nat64.c | 210 | ||||
-rw-r--r-- | src/plugins/snat/nat64.h | 59 | ||||
-rw-r--r-- | src/plugins/snat/nat64_cli.c | 103 | ||||
-rw-r--r-- | src/plugins/snat/nat64_in2out.c | 54 | ||||
-rw-r--r-- | src/plugins/snat/nat64_out2in.c | 40 | ||||
-rw-r--r-- | src/plugins/snat/snat_api.c | 93 | ||||
-rw-r--r-- | test/test_snat.py | 139 | ||||
-rw-r--r-- | test/vpp_papi_provider.py | 32 |
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) |