From 82b4ceb8e66020f41fd6faf9765614f9dc5a3163 Mon Sep 17 00:00:00 2001 From: Matus Fabian Date: Thu, 11 Oct 2018 04:28:48 -0700 Subject: [PATCH] NAT44: identity NAT fix (VPP-1441) Change-Id: Ic4affc54d15d08b9b730f6ec6146ee053b28b4b6 Signed-off-by: Matus Fabian --- src/plugins/nat/nat.c | 105 +++++++++++++++++++++++++++++++++---------- src/plugins/nat/nat.h | 7 +++ src/plugins/nat/nat_api.c | 19 ++++---- src/plugins/nat/nat_format.c | 19 +++++++- test/test_nat.py | 4 ++ 5 files changed, 122 insertions(+), 32 deletions(-) diff --git a/src/plugins/nat/nat.c b/src/plugins/nat/nat.c index ae2e64e1f82..2ebd6834d92 100755 --- a/src/plugins/nat/nat.c +++ b/src/plugins/nat/nat.c @@ -631,7 +631,6 @@ snat_add_static_mapping (ip4_address_t l_addr, ip4_address_t e_addr, clib_bihash_kv_8_8_t kv, value; snat_address_t *a = 0; u32 fib_index = ~0; - uword *p; snat_interface_t *interface; int i; snat_main_per_thread_data_t *tsm; @@ -643,6 +642,8 @@ snat_add_static_mapping (ip4_address_t l_addr, ip4_address_t e_addr, u64 user_index; snat_session_t *s; snat_static_map_resolve_t *rp, *rp_match = 0; + nat44_lb_addr_port_t *local; + u8 find = 0; if (!sm->endpoint_dependent) { @@ -732,19 +733,42 @@ snat_add_static_mapping (ip4_address_t l_addr, ip4_address_t e_addr, if (is_add) { if (m) - return VNET_API_ERROR_VALUE_EXIST; + { + 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; + } + /* *INDENT-ON* */ + vec_add2 (m->locals, local, 1); + local->vrf_id = vrf_id; + local->fib_index = + fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP4, vrf_id, + FIB_SOURCE_PLUGIN_LOW); + m_key.addr = m->local_addr; + m_key.port = m->local_port; + m_key.protocol = m->proto; + m_key.fib_index = local->fib_index; + kv.key = m_key.as_u64; + kv.value = m - sm->static_mappings; + clib_bihash_add_del_8_8 (&sm->static_mapping_by_local, &kv, 1); + return 0; + } + else + return VNET_API_ERROR_VALUE_EXIST; + } if (twice_nat && addr_only) return VNET_API_ERROR_UNSUPPORTED; /* Convert VRF id to FIB index */ if (vrf_id != ~0) - { - p = hash_get (sm->ip4_main->fib_index_by_table_id, vrf_id); - if (!p) - return VNET_API_ERROR_NO_SUCH_FIB; - fib_index = p[0]; - } + fib_index = + fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP4, vrf_id, + FIB_SOURCE_PLUGIN_LOW); /* If not specified use inside VRF id from SNAT plugin startup config */ else { @@ -752,7 +776,7 @@ snat_add_static_mapping (ip4_address_t l_addr, ip4_address_t e_addr, vrf_id = sm->inside_vrf_id; } - if (!out2in_only) + if (!(out2in_only || identity_nat)) { m_key.addr = l_addr; m_key.port = addr_only ? 0 : l_port; @@ -825,15 +849,23 @@ snat_add_static_mapping (ip4_address_t l_addr, ip4_address_t e_addr, m->tag = vec_dup (tag); m->local_addr = l_addr; m->external_addr = e_addr; - m->vrf_id = vrf_id; - m->fib_index = fib_index; m->twice_nat = twice_nat; if (out2in_only) m->flags |= NAT_STATIC_MAPPING_FLAG_OUT2IN_ONLY; if (addr_only) m->flags |= NAT_STATIC_MAPPING_FLAG_ADDR_ONLY; if (identity_nat) - m->flags |= NAT_STATIC_MAPPING_FLAG_IDENTITY_NAT; + { + m->flags |= NAT_STATIC_MAPPING_FLAG_IDENTITY_NAT; + vec_add2 (m->locals, local, 1); + local->vrf_id = vrf_id; + local->fib_index = fib_index; + } + else + { + m->vrf_id = vrf_id; + m->fib_index = fib_index; + } if (!addr_only) { m->local_port = l_port; @@ -855,7 +887,7 @@ snat_add_static_mapping (ip4_address_t l_addr, ip4_address_t e_addr, m_key.addr = m->local_addr; m_key.port = m->local_port; m_key.protocol = m->proto; - m_key.fib_index = m->fib_index; + m_key.fib_index = fib_index; kv.key = m_key.as_u64; kv.value = m - sm->static_mappings; if (!out2in_only) @@ -920,6 +952,25 @@ snat_add_static_mapping (ip4_address_t l_addr, ip4_address_t e_addr, return VNET_API_ERROR_NO_SUCH_ENTRY; } + if (identity_nat) + { + for (i = 0; i < vec_len (m->locals); i++) + { + if (m->locals[i].vrf_id == vrf_id) + { + find = 1; + break; + } + } + if (!find) + return VNET_API_ERROR_NO_SUCH_ENTRY; + + fib_index = m->locals[i].fib_index; + vec_del1 (m->locals, i); + } + else + fib_index = m->fib_index; + /* Free external address port */ if (!(addr_only || sm->static_mapping_only || out2in_only)) { @@ -958,23 +1009,17 @@ snat_add_static_mapping (ip4_address_t l_addr, ip4_address_t e_addr, m_key.addr = m->local_addr; m_key.port = m->local_port; m_key.protocol = m->proto; - m_key.fib_index = m->fib_index; + m_key.fib_index = fib_index; kv.key = m_key.as_u64; if (!out2in_only) clib_bihash_add_del_8_8 (&sm->static_mapping_by_local, &kv, 0); - m_key.addr = m->external_addr; - m_key.port = m->external_port; - m_key.fib_index = 0; - kv.key = m_key.as_u64; - clib_bihash_add_del_8_8 (&sm->static_mapping_by_external, &kv, 0); - /* Delete session(s) for static mapping if exist */ if (!(sm->static_mapping_only) || (sm->static_mapping_only && sm->static_mapping_connection_tracking)) { u_key.addr = m->local_addr; - u_key.fib_index = m->fib_index; + u_key.fib_index = fib_index; kv.key = u_key.as_u64; if (!clib_bihash_search_8_8 (&tsm->user_hash, &kv, &value)) { @@ -1018,6 +1063,16 @@ 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)) + return 0; + + m_key.addr = m->external_addr; + m_key.port = m->external_port; + m_key.fib_index = 0; + kv.key = m_key.as_u64; + clib_bihash_add_del_8_8 (&sm->static_mapping_by_external, &kv, 0); + vec_free (m->tag); vec_free (m->workers); /* Delete static mapping from pool */ @@ -1137,6 +1192,7 @@ nat44_add_del_lb_static_mapping (ip4_address_t e_addr, u16 e_port, m->external_port = e_port; m->proto = proto; m->twice_nat = twice_nat; + m->flags |= NAT_STATIC_MAPPING_FLAG_LB; if (out2in_only) m->flags |= NAT_STATIC_MAPPING_FLAG_OUT2IN_ONLY; m->affinity = affinity; @@ -1205,6 +1261,9 @@ nat44_add_del_lb_static_mapping (ip4_address_t e_addr, u16 e_port, if (!m) return VNET_API_ERROR_NO_SUCH_ENTRY; + if (!is_lb_static_mapping (m)) + return VNET_API_ERROR_INVALID_VALUE; + /* Free external address port */ if (!(sm->static_mapping_only || out2in_only)) { @@ -2041,7 +2100,7 @@ snat_static_mapping_match (snat_main_t * sm, if (by_external) { - if (vec_len (m->locals)) + if (is_lb_static_mapping (m)) { if (PREDICT_FALSE (lb != 0)) *lb = m->affinity ? AFFINITY_LB_NAT : LB_NAT; @@ -2612,7 +2671,7 @@ nat44_ed_get_worker_out2in_cb (ip4_header_t * ip, u32 rx_fib_index) (&sm->static_mapping_by_external, &kv, &value)) { m = pool_elt_at_index (sm->static_mappings, value.value); - if (!vec_len (m->locals)) + if (!is_lb_static_mapping (m)) return m->workers[0]; hash = ip->src_address.as_u32 + (ip->src_address.as_u32 >> 8) + diff --git a/src/plugins/nat/nat.h b/src/plugins/nat/nat.h index 0549acdba3a..13467203917 100644 --- a/src/plugins/nat/nat.h +++ b/src/plugins/nat/nat.h @@ -183,6 +183,7 @@ typedef enum #define NAT_STATIC_MAPPING_FLAG_ADDR_ONLY 1 #define NAT_STATIC_MAPPING_FLAG_OUT2IN_ONLY 2 #define NAT_STATIC_MAPPING_FLAG_IDENTITY_NAT 4 +#define NAT_STATIC_MAPPING_FLAG_LB 8 /* *INDENT-OFF* */ typedef CLIB_PACKED(struct @@ -666,6 +667,12 @@ unformat_function_t unformat_snat_protocol; */ #define is_identity_static_mapping(sm) (sm->flags & NAT_STATIC_MAPPING_FLAG_IDENTITY_NAT) +/** \brief Check if NAT static mapping is load-balancing. + @param sm NAT static mapping + @return 1 if load-balancing +*/ +#define is_lb_static_mapping(sm) (sm->flags & NAT_STATIC_MAPPING_FLAG_LB) + /* logging */ #define nat_log_err(...) \ vlib_log(VLIB_LOG_LEVEL_ERR, snat_main.log_class, __VA_ARGS__) diff --git a/src/plugins/nat/nat_api.c b/src/plugins/nat/nat_api.c index 8ad5c6652ca..4727826049b 100644 --- a/src/plugins/nat/nat_api.c +++ b/src/plugins/nat/nat_api.c @@ -1100,7 +1100,7 @@ vl_api_nat44_static_mapping_dump_t_handler (vl_api_nat44_static_mapping_dump_t /* *INDENT-OFF* */ pool_foreach (m, sm->static_mappings, ({ - if (!is_identity_static_mapping(m) && !vec_len (m->locals)) + if (!is_identity_static_mapping(m) && !is_lb_static_mapping (m)) send_nat44_static_mapping_details (m, reg, mp->context); })); /* *INDENT-ON* */ @@ -1181,17 +1181,17 @@ static void *vl_api_nat44_add_del_identity_mapping_t_print if (mp->addr_only == 0) s = - format (s, "protocol %d port %d", mp->protocol, + format (s, " protocol %d port %d", mp->protocol, clib_net_to_host_u16 (mp->port)); if (mp->vrf_id != ~0) - s = format (s, "vrf %d", clib_net_to_host_u32 (mp->vrf_id)); + s = format (s, " vrf %d", clib_net_to_host_u32 (mp->vrf_id)); FINISH; } static void -send_nat44_identity_mapping_details (snat_static_mapping_t * m, +send_nat44_identity_mapping_details (snat_static_mapping_t * m, int index, vl_api_registration_t * reg, u32 context) { vl_api_nat44_identity_mapping_details_t *rmp; @@ -1205,7 +1205,7 @@ send_nat44_identity_mapping_details (snat_static_mapping_t * m, 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->vrf_id); + rmp->vrf_id = htonl (m->locals[index].vrf_id); rmp->protocol = snat_proto_to_ip_proto (m->proto); rmp->context = context; if (m->tag) @@ -1258,8 +1258,11 @@ static void /* *INDENT-OFF* */ pool_foreach (m, sm->static_mappings, ({ - if (is_identity_static_mapping(m) && !vec_len (m->locals)) - send_nat44_identity_mapping_details (m, reg, mp->context); + if (is_identity_static_mapping(m) && !is_lb_static_mapping (m)) + { + for (j = 0; j < vec_len (m->locals); j++) + send_nat44_identity_mapping_details (m, j, reg, mp->context); + } })); /* *INDENT-ON* */ @@ -1689,7 +1692,7 @@ static void /* *INDENT-OFF* */ pool_foreach (m, sm->static_mappings, ({ - if (vec_len(m->locals)) + if (is_lb_static_mapping(m)) send_nat44_lb_static_mapping_details (m, reg, mp->context); })); /* *INDENT-ON* */ diff --git a/src/plugins/nat/nat_format.c b/src/plugins/nat/nat_format.c index a4b62b6e977..5ce00dcb1d0 100644 --- a/src/plugins/nat/nat_format.c +++ b/src/plugins/nat/nat_format.c @@ -220,6 +220,23 @@ format_snat_static_mapping (u8 * s, va_list * args) snat_static_mapping_t *m = va_arg (*args, snat_static_mapping_t *); nat44_lb_addr_port_t *local; + if (is_identity_static_mapping (m)) + { + if (is_addr_only_static_mapping (m)) + s = format (s, "identity mapping %U", + format_ip4_address, &m->local_addr); + else + s = format (s, "identity mapping %U:%d", + format_ip4_address, &m->local_addr, m->local_port); + + /* *INDENT-OFF* */ + vec_foreach (local, m->locals) + s = format (s, " vrf %d", local->vrf_id); + /* *INDENT-ON* */ + + return s; + } + if (is_addr_only_static_mapping (m)) s = format (s, "local %U external %U vrf %d %s %s", format_ip4_address, &m->local_addr, @@ -230,7 +247,7 @@ format_snat_static_mapping (u8 * s, va_list * args) is_out2in_only_static_mapping (m) ? "out2in-only" : ""); else { - if (vec_len (m->locals)) + if (is_lb_static_mapping (m)) { s = format (s, "%U external %U:%d %s %s", format_snat_protocol, m->proto, diff --git a/test/test_nat.py b/test/test_nat.py index 3b9007f6e93..e9e7dfa3068 100644 --- a/test/test_nat.py +++ b/test/test_nat.py @@ -1937,6 +1937,10 @@ class TestNAT44(MethodHolder): sessions = self.vapi.nat44_user_session_dump(self.pg0.remote_ip4n, 0) self.assertEqual(len(sessions), 0) + self.vapi.nat44_add_del_identity_mapping(ip=self.pg0.remote_ip4n, + vrf_id=1) + identity_mappings = self.vapi.nat44_identity_mapping_dump() + self.assertEqual(len(identity_mappings), 2) def test_multiple_inside_interfaces(self): """ NAT44 multiple non-overlapping address space inside interfaces """ -- 2.16.6