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