diff options
author | Matus Fabian <matfabia@cisco.com> | 2018-12-06 03:11:09 -0800 |
---|---|---|
committer | Matus Fabian <matfabia@cisco.com> | 2018-12-18 13:36:39 +0000 |
commit | b686508c4edff42dac2ab140318de8aacb2ac18b (patch) | |
tree | 07cd05a8720548497bcecf1f8959fbbe9b9d6276 | |
parent | 003d5da298b2cd1e20400fb5fdcefce752ca616e (diff) |
NAT44: nat44_add_del_lb_static_mapping enhancements (VPP-1514)
Change-Id: I5419e06592b0402e911e132796368800321f355a
Signed-off-by: Matus Fabian <matfabia@cisco.com>
-rw-r--r-- | src/plugins/nat/nat.api | 25 | ||||
-rwxr-xr-x | src/plugins/nat/nat.c | 273 | ||||
-rw-r--r-- | src/plugins/nat/nat.h | 5 | ||||
-rw-r--r-- | src/plugins/nat/nat44_cli.c | 110 | ||||
-rw-r--r-- | src/plugins/nat/nat_api.c | 76 | ||||
-rw-r--r-- | src/plugins/nat/nat_format.c | 12 | ||||
-rw-r--r-- | test/test_nat.py | 60 | ||||
-rw-r--r-- | test/vpp_papi_provider.py | 33 |
8 files changed, 540 insertions, 54 deletions
diff --git a/src/plugins/nat/nat.api b/src/plugins/nat/nat.api index 6e2d8e57853..f41428b70bb 100644 --- a/src/plugins/nat/nat.api +++ b/src/plugins/nat/nat.api @@ -13,7 +13,7 @@ * limitations under the License. */ -option version = "4.0.0"; +option version = "4.1.0"; /** * @file nat.api @@ -775,10 +775,29 @@ autoreply manual_endian define nat44_add_del_lb_static_mapping { u8 out2in_only; u8 tag[64]; u32 affinity; - u8 local_num; + u32 local_num; vl_api_nat44_lb_addr_port_t locals[local_num]; }; +/** \brief Add/delete NAT44 load-balancing static mapping rule backend + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request + @param is_add - 1 if add, 0 if delete + @param external_addr - external IPv4 address of the service + @param external_port - external L4 port number of the service + @param protocol - IP protocol number of the service + @param local - local network node +*/ +autoreply define nat44_lb_static_mapping_add_del_local { + u32 client_index; + u32 context; + u8 is_add; + u8 external_addr[4]; + u16 external_port; + u8 protocol; + vl_api_nat44_lb_addr_port_t local; +}; + /** \brief Dump NAT44 load-balancing static mapping rules @param client_index - opaque cookie to identify the sender @param context - sender context, to match reply w/ request @@ -815,7 +834,7 @@ manual_endian define nat44_lb_static_mapping_details { u8 out2in_only; u8 tag[64]; u32 affinity; - u8 local_num; + u32 local_num; vl_api_nat44_lb_addr_port_t locals[local_num]; }; diff --git a/src/plugins/nat/nat.c b/src/plugins/nat/nat.c index 6bfea3c4162..2e1aa770581 100755 --- a/src/plugins/nat/nat.c +++ b/src/plugins/nat/nat.c @@ -665,7 +665,7 @@ snat_add_static_mapping (ip4_address_t l_addr, ip4_address_t e_addr, snat_session_t *s; snat_static_map_resolve_t *rp, *rp_match = 0; nat44_lb_addr_port_t *local; - u8 find = 0; + u32 find = ~0; if (!sm->endpoint_dependent) { @@ -759,13 +759,13 @@ snat_add_static_mapping (ip4_address_t l_addr, ip4_address_t e_addr, if (is_identity_static_mapping (m)) { /* *INDENT-OFF* */ - vec_foreach (local, m->locals) - { - if (local->vrf_id == vrf_id) - return VNET_API_ERROR_VALUE_EXIST; - } + pool_foreach (local, m->locals, + ({ + if (local->vrf_id == vrf_id) + return VNET_API_ERROR_VALUE_EXIST; + })); /* *INDENT-ON* */ - vec_add2 (m->locals, local, 1); + pool_get (m->locals, local); local->vrf_id = vrf_id; local->fib_index = fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP4, vrf_id, @@ -879,7 +879,7 @@ snat_add_static_mapping (ip4_address_t l_addr, ip4_address_t e_addr, if (identity_nat) { m->flags |= NAT_STATIC_MAPPING_FLAG_IDENTITY_NAT; - vec_add2 (m->locals, local, 1); + pool_get (m->locals, local); local->vrf_id = vrf_id; local->fib_index = fib_index; } @@ -979,19 +979,19 @@ snat_add_static_mapping (ip4_address_t l_addr, ip4_address_t e_addr, if (vrf_id == ~0) vrf_id = sm->inside_vrf_id; - for (i = 0; i < vec_len (m->locals); i++) - { - if (m->locals[i].vrf_id == vrf_id) - { - find = 1; - break; - } - } - if (!find) + /* *INDENT-OFF* */ + pool_foreach (local, m->locals, + ({ + if (local->vrf_id == vrf_id) + find = local - m->locals; + })); + /* *INDENT-ON* */ + if (find == ~0) return VNET_API_ERROR_NO_SUCH_ENTRY; - fib_index = m->locals[i].fib_index; - vec_del1 (m->locals, i); + local = pool_elt_at_index (m->locals, find); + fib_index = local->fib_index; + pool_put (m->locals, local); } else fib_index = m->fib_index; @@ -1089,7 +1089,7 @@ snat_add_static_mapping (ip4_address_t l_addr, ip4_address_t e_addr, } fib_table_unlock (fib_index, FIB_PROTOCOL_IP4, FIB_SOURCE_PLUGIN_LOW); - if (vec_len (m->locals)) + if (pool_elts (m->locals)) return 0; m_key.addr = m->external_addr; @@ -1258,7 +1258,8 @@ nat44_add_del_lb_static_mapping (ip4_address_t e_addr, u16 e_port, } locals[i].prefix = (i == 0) ? locals[i].probability : (locals[i - 1].prefix + locals[i].probability); - vec_add1 (m->locals, locals[i]); + pool_get (m->locals, local); + *local = locals[i]; if (sm->num_workers > 1) { ip4_header_t ip = { @@ -1331,8 +1332,8 @@ nat44_add_del_lb_static_mapping (ip4_address_t e_addr, u16 e_port, } /* *INDENT-OFF* */ - vec_foreach (local, m->locals) - { + pool_foreach (local, m->locals, + ({ fib_table_unlock (local->fib_index, FIB_PROTOCOL_IP4, FIB_SOURCE_PLUGIN_LOW); m_key.addr = local->addr; @@ -1361,7 +1362,7 @@ nat44_add_del_lb_static_mapping (ip4_address_t e_addr, u16 e_port, /* Delete sessions */ u_key.addr = local->addr; - u_key.fib_index = m->fib_index; + u_key.fib_index = local->fib_index; kv.key = u_key.as_u64; if (!clib_bihash_search_8_8 (&tsm->user_hash, &kv, &value)) { @@ -1391,11 +1392,11 @@ nat44_add_del_lb_static_mapping (ip4_address_t e_addr, u16 e_port, } } } - } + })); /* *INDENT-ON* */ if (m->affinity) nat_affinity_flush_service (m->affinity_per_service_list_head_index); - vec_free (m->locals); + pool_free (m->locals); vec_free (m->tag); vec_free (m->workers); @@ -1406,6 +1407,187 @@ nat44_add_del_lb_static_mapping (ip4_address_t e_addr, u16 e_port, } int +nat44_lb_static_mapping_add_del_local (ip4_address_t e_addr, u16 e_port, + ip4_address_t l_addr, u16 l_port, + snat_protocol_t proto, u32 vrf_id, + u8 probability, u8 is_add) +{ + snat_main_t *sm = &snat_main; + snat_static_mapping_t *m = 0; + snat_session_key_t m_key; + clib_bihash_kv_8_8_t kv, value; + nat44_lb_addr_port_t *local, *prev_local, *match_local = 0; + snat_main_per_thread_data_t *tsm; + snat_user_key_t u_key; + snat_user_t *u; + snat_session_t *s; + dlist_elt_t *head, *elt; + u32 elt_index, head_index, ses_index, *locals = 0; + uword *bitmap = 0; + int i; + + if (!sm->endpoint_dependent) + return VNET_API_ERROR_FEATURE_DISABLED; + + m_key.addr = e_addr; + m_key.port = e_port; + m_key.protocol = proto; + m_key.fib_index = 0; + kv.key = m_key.as_u64; + if (!clib_bihash_search_8_8 (&sm->static_mapping_by_external, &kv, &value)) + m = pool_elt_at_index (sm->static_mappings, value.value); + + if (!m) + return VNET_API_ERROR_NO_SUCH_ENTRY; + + if (!is_lb_static_mapping (m)) + return VNET_API_ERROR_INVALID_VALUE; + + /* *INDENT-OFF* */ + pool_foreach (local, m->locals, + ({ + if ((local->addr.as_u32 == l_addr.as_u32) && (local->port == l_port) && + (local->vrf_id == vrf_id)) + { + match_local = local; + break; + } + })); + /* *INDENT-ON* */ + + if (is_add) + { + if (match_local) + return VNET_API_ERROR_VALUE_EXIST; + + pool_get (m->locals, local); + clib_memset (local, 0, sizeof (*local)); + local->addr.as_u32 = l_addr.as_u32; + local->port = l_port; + local->probability = probability; + local->vrf_id = vrf_id; + local->fib_index = + fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP4, vrf_id, + FIB_SOURCE_PLUGIN_LOW); + + if (!is_out2in_only_static_mapping (m)) + { + m_key.addr = l_addr; + m_key.port = l_port; + m_key.fib_index = local->fib_index; + kv.key = m_key.as_u64; + kv.value = m - sm->static_mappings; + if (clib_bihash_add_del_8_8 (&sm->static_mapping_by_local, &kv, 1)) + nat_log_err ("static_mapping_by_local key add failed"); + } + } + else + { + if (!match_local) + return VNET_API_ERROR_NO_SUCH_ENTRY; + + if (pool_elts (m->locals) < 3) + return VNET_API_ERROR_UNSPECIFIED; + + fib_table_unlock (match_local->fib_index, FIB_PROTOCOL_IP4, + FIB_SOURCE_PLUGIN_LOW); + + if (!is_out2in_only_static_mapping (m)) + { + m_key.addr = l_addr; + m_key.port = l_port; + m_key.fib_index = match_local->fib_index; + kv.key = m_key.as_u64; + if (clib_bihash_add_del_8_8 (&sm->static_mapping_by_local, &kv, 0)) + nat_log_err ("static_mapping_by_local key del failed"); + } + + if (sm->num_workers > 1) + { + ip4_header_t ip = { + .src_address = local->addr, + }; + tsm = vec_elt_at_index (sm->per_thread_data, + sm->worker_in2out_cb (&ip, m->fib_index)); + } + else + tsm = vec_elt_at_index (sm->per_thread_data, sm->num_workers); + + /* Delete sessions */ + u_key.addr = match_local->addr; + u_key.fib_index = match_local->fib_index; + kv.key = u_key.as_u64; + if (!clib_bihash_search_8_8 (&tsm->user_hash, &kv, &value)) + { + u = pool_elt_at_index (tsm->users, value.value); + if (u->nstaticsessions) + { + head_index = u->sessions_per_user_list_head_index; + head = pool_elt_at_index (tsm->list_pool, head_index); + elt_index = head->next; + elt = pool_elt_at_index (tsm->list_pool, elt_index); + ses_index = elt->value; + while (ses_index != ~0) + { + s = pool_elt_at_index (tsm->sessions, ses_index); + elt = pool_elt_at_index (tsm->list_pool, elt->next); + ses_index = elt->value; + + if (!(is_lb_session (s))) + continue; + + if ((s->in2out.addr.as_u32 != match_local->addr.as_u32) || + (clib_net_to_host_u16 (s->in2out.port) != + match_local->port)) + continue; + + nat_free_session_data (sm, s, tsm - sm->per_thread_data); + nat44_delete_session (sm, s, tsm - sm->per_thread_data); + } + } + } + + pool_put (m->locals, match_local); + } + + vec_free (m->workers); + + /* *INDENT-OFF* */ + pool_foreach (local, m->locals, + ({ + vec_add1 (locals, local - m->locals); + if (sm->num_workers > 1) + { + ip4_header_t ip; + ip.src_address.as_u32 = local->addr.as_u32, + bitmap = clib_bitmap_set (bitmap, + sm->worker_in2out_cb (&ip, local->fib_index), + 1); + } + })); + /* *INDENT-ON* */ + + local = pool_elt_at_index (m->locals, locals[0]); + local->prefix = local->probability; + for (i = 1; i < vec_len (locals); i++) + { + local = pool_elt_at_index (m->locals, locals[i]); + prev_local = pool_elt_at_index (m->locals, locals[i - 1]); + local->prefix = local->probability + prev_local->prefix; + } + + /* Assign workers */ + if (sm->num_workers > 1) + { + /* *INDENT-OFF* */ + clib_bitmap_foreach (i, bitmap, ({ vec_add1(m->workers, i); })); + /* *INDENT-ON* */ + } + + return 0; +} + +int snat_del_address (snat_main_t * sm, ip4_address_t addr, u8 delete_sm, u8 twice_nat) { @@ -2095,8 +2277,9 @@ snat_static_mapping_match (snat_main_t * sm, snat_static_mapping_t *m; snat_session_key_t m_key; clib_bihash_8_8_t *mapping_hash = &sm->static_mapping_by_local; - u32 rand, lo = 0, hi, mid; + u32 rand, lo = 0, hi, mid, *tmp = 0, i; u8 backend_index; + nat44_lb_addr_port_t *local; m_key.fib_index = match.fib_index; if (by_external) @@ -2136,42 +2319,52 @@ snat_static_mapping_match (snat_main_t * sm, &backend_index)) goto get_local; - mapping->addr = m->locals[backend_index].addr; - mapping->port = - clib_host_to_net_u16 (m->locals[backend_index].port); - mapping->fib_index = m->locals[backend_index].fib_index; + local = pool_elt_at_index (m->locals, backend_index); + mapping->addr = local->addr; + mapping->port = clib_host_to_net_u16 (local->port); + mapping->fib_index = local->fib_index; goto end; } get_local: - hi = vec_len (m->locals) - 1; - rand = 1 + (random_u32 (&sm->random_seed) % m->locals[hi].prefix); + /* *INDENT-OFF* */ + pool_foreach_index (i, m->locals, + ({ + vec_add1 (tmp, i); + })); + /* *INDENT-ON* */ + hi = vec_len (tmp) - 1; + local = pool_elt_at_index (m->locals, tmp[hi]); + rand = 1 + (random_u32 (&sm->random_seed) % local->prefix); while (lo < hi) { mid = ((hi - lo) >> 1) + lo; - (rand > m->locals[mid].prefix) ? (lo = mid + 1) : (hi = mid); + local = pool_elt_at_index (m->locals, tmp[mid]); + (rand > local->prefix) ? (lo = mid + 1) : (hi = mid); } - if (!(m->locals[lo].prefix >= rand)) + local = pool_elt_at_index (m->locals, tmp[lo]); + if (!(local->prefix >= rand)) return 1; if (PREDICT_FALSE (sm->num_workers > 1)) { ip4_header_t ip = { - .src_address = m->locals[lo].addr, + .src_address = local->addr, }; if (sm->worker_in2out_cb (&ip, m->fib_index) != vlib_get_thread_index ()) goto get_local; } - mapping->addr = m->locals[lo].addr; - mapping->port = clib_host_to_net_u16 (m->locals[lo].port); - mapping->fib_index = m->locals[lo].fib_index; + mapping->addr = local->addr; + mapping->port = clib_host_to_net_u16 (local->port); + mapping->fib_index = local->fib_index; if (m->affinity) { if (nat_affinity_create_and_lock (ext_host_addr[0], match.addr, match.protocol, match.port, - lo, m->affinity, + tmp[lo], m->affinity, m->affinity_per_service_list_head_index)) nat_log_info ("create affinity record failed"); } + vec_free (tmp); } else { diff --git a/src/plugins/nat/nat.h b/src/plugins/nat/nat.h index 3ce83ea2602..e851e2f98f7 100644 --- a/src/plugins/nat/nat.h +++ b/src/plugins/nat/nat.h @@ -859,6 +859,11 @@ int nat44_add_del_lb_static_mapping (ip4_address_t e_addr, u16 e_port, twice_nat_type_t twice_nat, u8 out2in_only, u8 * tag, u32 affinity); +int nat44_lb_static_mapping_add_del_local (ip4_address_t e_addr, u16 e_port, + ip4_address_t l_addr, u16 l_port, + snat_protocol_t proto, u32 vrf_id, + u8 probability, u8 is_add); + clib_error_t *snat_api_init (vlib_main_t * vm, snat_main_t * sm); /** diff --git a/src/plugins/nat/nat44_cli.c b/src/plugins/nat/nat44_cli.c index 0c5b80c394e..9aaa8498e22 100644 --- a/src/plugins/nat/nat44_cli.c +++ b/src/plugins/nat/nat44_cli.c @@ -948,6 +948,98 @@ done: } static clib_error_t * +add_lb_backend_command_fn (vlib_main_t * vm, + unformat_input_t * input, vlib_cli_command_t * cmd) +{ + unformat_input_t _line_input, *line_input = &_line_input; + snat_main_t *sm = &snat_main; + clib_error_t *error = 0; + ip4_address_t l_addr, e_addr; + u32 l_port = 0, e_port = 0, vrf_id = 0, probability = 0; + int is_add = 1; + int rv; + snat_protocol_t proto; + u8 proto_set = 0; + + if (sm->deterministic) + return clib_error_return (0, UNSUPPORTED_IN_DET_MODE_STR); + + /* Get a line of input. */ + 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, "local %U:%u probability %u", + unformat_ip4_address, &l_addr, &l_port, &probability)) + ; + else if (unformat (line_input, "local %U:%u vrf %u probability %u", + unformat_ip4_address, &l_addr, &l_port, &vrf_id, + &probability)) + ; + else if (unformat (line_input, "external %U:%u", unformat_ip4_address, + &e_addr, &e_port)) + ; + else if (unformat (line_input, "protocol %U", unformat_snat_protocol, + &proto)) + proto_set = 1; + 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 (!l_port || !e_port) + { + error = clib_error_return (0, "local or external must be set"); + goto done; + } + + if (!proto_set) + { + error = clib_error_return (0, "missing protocol"); + goto done; + } + + rv = + nat44_lb_static_mapping_add_del_local (e_addr, (u16) e_port, l_addr, + l_port, proto, vrf_id, probability, + is_add); + + switch (rv) + { + case VNET_API_ERROR_INVALID_VALUE: + error = clib_error_return (0, "External is not load-balancing static " + "mapping."); + goto done; + case VNET_API_ERROR_NO_SUCH_ENTRY: + error = clib_error_return (0, "Mapping or back-end not exist."); + goto done; + case VNET_API_ERROR_VALUE_EXIST: + error = clib_error_return (0, "Back-end already exist."); + goto done; + case VNET_API_ERROR_FEATURE_DISABLED: + error = + clib_error_return (0, "Available only for endpoint-dependent mode."); + goto done; + case VNET_API_ERROR_UNSPECIFIED: + error = clib_error_return (0, "At least two back-ends must remain"); + goto done; + default: + break; + } + +done: + unformat_free (line_input); + + return error; +} + +static clib_error_t * nat44_show_static_mappings_command_fn (vlib_main_t * vm, unformat_input_t * input, vlib_cli_command_t * cmd) @@ -1936,6 +2028,24 @@ VLIB_CLI_COMMAND (add_lb_static_mapping_command, static) = { /*? * @cliexpar + * @cliexstart{nat44 add load-balancing static mapping} + * Modify service load balancing using NAT44 + * To add new back-end server 10.100.10.30:8080 for service load balancing + * static mapping with external IP address 1.2.3.4 and TCP port 80 use: + * vpp# nat44 add load-balancing back-end protocol tcp external 1.2.3.4:80 local 10.100.10.30:8080 probability 25 + * @cliexend +?*/ +VLIB_CLI_COMMAND (add_lb_backend_command, static) = { + .path = "nat44 add load-balancing back-end", + .function = add_lb_backend_command_fn, + .short_help = + "nat44 add load-balancing back-end protocol tcp|udp " + "external <addr>:<port> local <addr>:<port> [vrf <table-id>] " + "probability <n> [del]", +}; + +/*? + * @cliexpar * @cliexstart{show nat44 static mappings} * Show NAT44 static mappings. * vpp# show nat44 static mappings diff --git a/src/plugins/nat/nat_api.c b/src/plugins/nat/nat_api.c index 378e7abe871..b1aa3243ab9 100644 --- a/src/plugins/nat/nat_api.c +++ b/src/plugins/nat/nat_api.c @@ -1196,6 +1196,7 @@ send_nat44_identity_mapping_details (snat_static_mapping_t * m, int index, { vl_api_nat44_identity_mapping_details_t *rmp; snat_main_t *sm = &snat_main; + nat44_lb_addr_port_t *local = pool_elt_at_index (m->locals, index); rmp = vl_msg_api_alloc (sizeof (*rmp)); clib_memset (rmp, 0, sizeof (*rmp)); @@ -1205,7 +1206,7 @@ send_nat44_identity_mapping_details (snat_static_mapping_t * m, int index, clib_memcpy (rmp->ip_address, &(m->local_addr), 4); rmp->port = htons (m->local_port); rmp->sw_if_index = ~0; - rmp->vrf_id = htonl (m->locals[index].vrf_id); + rmp->vrf_id = htonl (local->vrf_id); rmp->protocol = snat_proto_to_ip_proto (m->proto); rmp->context = context; if (m->tag) @@ -1260,8 +1261,10 @@ static void ({ if (is_identity_static_mapping(m) && !is_lb_static_mapping (m)) { - for (j = 0; j < vec_len (m->locals); j++) + pool_foreach_index (j, m->locals, + ({ send_nat44_identity_mapping_details (m, j, reg, mp->context); + })); } })); /* *INDENT-ON* */ @@ -1555,7 +1558,7 @@ vl_api_nat44_user_session_dump_t_print (vl_api_nat44_user_session_dump_t * mp, static nat44_lb_addr_port_t * unformat_nat44_lb_addr_port (vl_api_nat44_lb_addr_port_t * addr_port_pairs, - u8 addr_port_pair_num) + u32 addr_port_pair_num) { u8 i; nat44_lb_addr_port_t *lb_addr_port_pairs = 0, lb_addr_port; @@ -1594,7 +1597,9 @@ static void goto send_reply; } - locals = unformat_nat44_lb_addr_port (mp->locals, mp->local_num); + locals = + unformat_nat44_lb_addr_port (mp->locals, + clib_net_to_host_u32 (mp->local_num)); clib_memcpy (&e_addr, mp->external_addr, 4); proto = ip_proto_to_snat_proto (mp->protocol); if (mp->twice_nat) @@ -1632,6 +1637,52 @@ static void *vl_api_nat44_add_del_lb_static_mapping_t_print } static void + vl_api_nat44_lb_static_mapping_add_del_local_t_handler + (vl_api_nat44_lb_static_mapping_add_del_local_t * mp) +{ + snat_main_t *sm = &snat_main; + vl_api_nat44_lb_static_mapping_add_del_local_reply_t *rmp; + int rv = 0; + ip4_address_t e_addr, l_addr; + snat_protocol_t proto; + + if (!sm->endpoint_dependent) + { + rv = VNET_API_ERROR_UNSUPPORTED; + goto send_reply; + } + + clib_memcpy (&e_addr, mp->external_addr, 4); + clib_memcpy (&l_addr, mp->local.addr, 4); + proto = ip_proto_to_snat_proto (mp->protocol); + + rv = + nat44_lb_static_mapping_add_del_local (e_addr, + clib_net_to_host_u16 + (mp->external_port), l_addr, + clib_net_to_host_u16 (mp-> + local.port), + proto, + clib_net_to_host_u32 (mp-> + local.vrf_id), + mp->local.probability, mp->is_add); + +send_reply: + REPLY_MACRO (VL_API_NAT44_LB_STATIC_MAPPING_ADD_DEL_LOCAL_REPLY); +} + +static void *vl_api_nat44_lb_static_mapping_add_del_local_t_print + (vl_api_nat44_lb_static_mapping_add_del_local_t * mp, void *handle) +{ + u8 *s; + + s = format (0, "SCRIPT: nat44_lb_static_mapping_add_del_local "); + s = format (s, "is_add %d", mp->is_add); + + FINISH; +} + +static void send_nat44_lb_static_mapping_details (snat_static_mapping_t * m, vl_api_registration_t * reg, u32 context) @@ -1640,10 +1691,12 @@ send_nat44_lb_static_mapping_details (snat_static_mapping_t * m, snat_main_t *sm = &snat_main; nat44_lb_addr_port_t *ap; vl_api_nat44_lb_addr_port_t *locals; + u32 local_num = 0; rmp = vl_msg_api_alloc (sizeof (*rmp) + - (vec_len (m->locals) * sizeof (nat44_lb_addr_port_t))); + (pool_elts (m->locals) * + sizeof (nat44_lb_addr_port_t))); clib_memset (rmp, 0, sizeof (*rmp)); rmp->_vl_msg_id = ntohs (VL_API_NAT44_LB_STATIC_MAPPING_DETAILS + sm->msg_id_base); @@ -1661,15 +1714,18 @@ send_nat44_lb_static_mapping_details (snat_static_mapping_t * m, strncpy ((char *) rmp->tag, (char *) m->tag, vec_len (m->tag)); locals = (vl_api_nat44_lb_addr_port_t *) rmp->locals; - vec_foreach (ap, m->locals) - { + /* *INDENT-OFF* */ + pool_foreach (ap, m->locals, + ({ clib_memcpy (locals->addr, &(ap->addr), 4); locals->port = htons (ap->port); locals->probability = ap->probability; locals->vrf_id = ntohl (ap->vrf_id); locals++; - rmp->local_num++; - } + local_num++; + })); + /* *INDENT-ON* */ + rmp->local_num = ntohl (local_num); vl_api_send_msg (reg, (u8 *) rmp); } @@ -3159,6 +3215,8 @@ _(NAT44_INTERFACE_ADD_DEL_OUTPUT_FEATURE, \ _(NAT44_INTERFACE_OUTPUT_FEATURE_DUMP, \ nat44_interface_output_feature_dump) \ _(NAT44_ADD_DEL_LB_STATIC_MAPPING, nat44_add_del_lb_static_mapping) \ +_(NAT44_LB_STATIC_MAPPING_ADD_DEL_LOCAL, \ + nat44_lb_static_mapping_add_del_local) \ _(NAT44_LB_STATIC_MAPPING_DUMP, nat44_lb_static_mapping_dump) \ _(NAT44_DEL_SESSION, nat44_del_session) \ _(NAT44_FORWARDING_ENABLE_DISABLE, nat44_forwarding_enable_disable) \ diff --git a/src/plugins/nat/nat_format.c b/src/plugins/nat/nat_format.c index 8e5ac4cade4..8452940898d 100644 --- a/src/plugins/nat/nat_format.c +++ b/src/plugins/nat/nat_format.c @@ -231,8 +231,10 @@ format_snat_static_mapping (u8 * s, va_list * args) format_ip4_address, &m->local_addr, m->local_port); /* *INDENT-OFF* */ - vec_foreach (local, m->locals) + pool_foreach (local, m->locals, + ({ s = format (s, " vrf %d", local->vrf_id); + })); /* *INDENT-ON* */ return s; @@ -256,10 +258,16 @@ format_snat_static_mapping (u8 * s, va_list * args) m->twice_nat == TWICE_NAT ? "twice-nat" : m->twice_nat == TWICE_NAT_SELF ? "self-twice-nat" : "", is_out2in_only_static_mapping (m) ? "out2in-only" : ""); - vec_foreach (local, m->locals) + + /* *INDENT-OFF* */ + pool_foreach (local, m->locals, + ({ s = format (s, "\n local %U:%d vrf %d probability %d\%", format_ip4_address, &local->addr, local->port, local->vrf_id, local->probability); + })); + /* *INDENT-ON* */ + } else s = format (s, "%U local %U:%d external %U:%d vrf %d %s %s", diff --git a/test/test_nat.py b/test/test_nat.py index 9879b762cc9..516961739b8 100644 --- a/test/test_nat.py +++ b/test/test_nat.py @@ -4269,6 +4269,7 @@ class TestNAT44EndpointDependent(MethodHolder): local_port = 8080 server1 = self.pg0.remote_hosts[0] server2 = self.pg0.remote_hosts[1] + server3 = self.pg0.remote_hosts[2] locals = [{'addr': server1.ip4n, 'port': local_port, @@ -4309,6 +4310,65 @@ class TestNAT44EndpointDependent(MethodHolder): server2_n += 1 self.assertGreater(server1_n, server2_n) + # add new back-end + self.vapi.nat44_lb_static_mapping_add_del_local(external_addr_n, + external_port, + server3.ip4n, + local_port, + IP_PROTOS.tcp, + 20) + server1_n = 0 + server2_n = 0 + server3_n = 0 + clients = ip4_range(self.pg1.remote_ip4, 60, 110) + pkts = [] + for client in clients: + p = (Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac) / + IP(src=client, dst=self.nat_addr) / + TCP(sport=12346, dport=external_port)) + pkts.append(p) + self.assertGreater(len(pkts), 0) + self.pg1.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = self.pg0.get_capture(len(pkts)) + for p in capture: + if p[IP].dst == server1.ip4: + server1_n += 1 + elif p[IP].dst == server2.ip4: + server2_n += 1 + else: + server3_n += 1 + self.assertGreater(server1_n, 0) + self.assertGreater(server2_n, 0) + self.assertGreater(server3_n, 0) + + # remove one back-end + self.vapi.nat44_lb_static_mapping_add_del_local(external_addr_n, + external_port, + server2.ip4n, + local_port, + IP_PROTOS.tcp, + 10, + is_add=0) + server1_n = 0 + server2_n = 0 + server3_n = 0 + self.pg1.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = self.pg0.get_capture(len(pkts)) + for p in capture: + if p[IP].dst == server1.ip4: + server1_n += 1 + elif p[IP].dst == server2.ip4: + server2_n += 1 + else: + server3_n += 1 + self.assertGreater(server1_n, 0) + self.assertEqual(server2_n, 0) + self.assertGreater(server3_n, 0) + def test_static_lb_2(self): """ NAT44 local service load balancing (asymmetrical rule) """ external_addr_n = socket.inet_pton(socket.AF_INET, self.nat_addr) diff --git a/test/vpp_papi_provider.py b/test/vpp_papi_provider.py index 0a33c1eb2ac..4ab4380692e 100644 --- a/test/vpp_papi_provider.py +++ b/test/vpp_papi_provider.py @@ -1668,6 +1668,39 @@ class VppPapiProvider(object): 'local_num': local_num, 'locals': locals}) + def nat44_lb_static_mapping_add_del_local( + self, + external_addr, + external_port, + local_addr, + local_port, + protocol, + probability, + vrf_id=0, + is_add=1): + """Add/delete NAT44 load-balancing static mapping rule backend + + :param external_addr: external IPv4 address of the servic + :param external_port: external L4 port number of the service + :param local_addr: IPv4 address of the internal node + :param local_port: L4 port number of the internal node + :param protocol: IP protocol number + :param probability: probability of the internal node + :param vrf_id: VRF id of the internal node + :param is_add: 1 if add, 0 if delete + """ + return self.api( + self.papi.nat44_lb_static_mapping_add_del_local, + {'is_add': is_add, + 'external_addr': external_addr, + 'external_port': external_port, + 'local': { + 'addr': local_addr, + 'port': local_port, + 'probability': probability, + 'vrf_id': vrf_id}, + 'protocol': protocol}) + def nat44_lb_static_mapping_dump(self): """Dump NAT44 load balancing static mappings |