From 0eaf4e6784efb2d058fe2f031578251b6bcc0aa8 Mon Sep 17 00:00:00 2001 From: Filip Varga Date: Wed, 17 Feb 2021 14:34:54 +0100 Subject: nat: Final NAT44 EI/ED split patch This patch achieves complete separation of endpoint-dependent and endpoint-independent IPv4 NAT features. Some common stuff is also moved to NAT library. Type: refactor Change-Id: I52468b7e2b5ac28958a2baf8e2ea01787322e801 Signed-off-by: Filip Varga --- src/plugins/nat/CMakeLists.txt | 59 +- src/plugins/nat/FEATURE.yaml | 15 +- src/plugins/nat/det44/det44.api | 2 +- src/plugins/nat/dslite/dslite_cli.c | 19 - src/plugins/nat/in2out_ed.c | 1531 -------- src/plugins/nat/lib/lib.c | 59 + src/plugins/nat/lib/lib.h | 47 +- src/plugins/nat/lib/log.h | 214 ++ src/plugins/nat/lib/nat_inlines.h | 14 + src/plugins/nat/lib/nat_types.api | 47 + src/plugins/nat/nat.c | 4246 ----------------------- src/plugins/nat/nat.h | 1558 --------- src/plugins/nat/nat44-ed/nat44_ed.api | 1214 +++++++ src/plugins/nat/nat44-ed/nat44_ed.c | 3610 +++++++++++++++++++ src/plugins/nat/nat44-ed/nat44_ed.h | 1183 +++++++ src/plugins/nat/nat44-ed/nat44_ed_affinity.c | 288 ++ src/plugins/nat/nat44-ed/nat44_ed_affinity.h | 152 + src/plugins/nat/nat44-ed/nat44_ed_api.c | 1541 ++++++++ src/plugins/nat/nat44-ed/nat44_ed_classify.c | 362 ++ src/plugins/nat/nat44-ed/nat44_ed_cli.c | 2029 +++++++++++ src/plugins/nat/nat44-ed/nat44_ed_format.c | 277 ++ src/plugins/nat/nat44-ed/nat44_ed_handoff.c | 344 ++ src/plugins/nat/nat44-ed/nat44_ed_in2out.c | 1579 +++++++++ src/plugins/nat/nat44-ed/nat44_ed_inlines.h | 869 +++++ src/plugins/nat/nat44-ed/nat44_ed_out2in.c | 1443 ++++++++ src/plugins/nat/nat44-ei/nat44_ei.api | 862 +++++ src/plugins/nat/nat44-ei/nat44_ei.c | 2933 ++++++++++++---- src/plugins/nat/nat44-ei/nat44_ei.h | 549 ++- src/plugins/nat/nat44-ei/nat44_ei_api.c | 1199 +++++++ src/plugins/nat/nat44-ei/nat44_ei_cli.c | 2025 +++++++++++ src/plugins/nat/nat44-ei/nat44_ei_dpo.c | 79 + src/plugins/nat/nat44-ei/nat44_ei_dpo.h | 36 + src/plugins/nat/nat44-ei/nat44_ei_ha.c | 165 +- src/plugins/nat/nat44-ei/nat44_ei_ha.h | 2 - src/plugins/nat/nat44-ei/nat44_ei_hairpinning.c | 748 ++++ src/plugins/nat/nat44-ei/nat44_ei_hairpinning.h | 92 + src/plugins/nat/nat44-ei/nat44_ei_handoff.c | 326 ++ src/plugins/nat/nat44-ei/nat44_ei_in2out.c | 1151 +++--- src/plugins/nat/nat44-ei/nat44_ei_inlines.h | 178 +- src/plugins/nat/nat44-ei/nat44_ei_out2in.c | 607 ++-- src/plugins/nat/nat44.api | 1214 ------- src/plugins/nat/nat44/ed_inlines.h | 361 -- src/plugins/nat/nat44/inlines.h | 44 - src/plugins/nat/nat44_api.c | 1750 ---------- src/plugins/nat/nat44_classify.c | 506 --- src/plugins/nat/nat44_cli.c | 2597 -------------- src/plugins/nat/nat44_hairpinning.c | 746 ---- src/plugins/nat/nat44_hairpinning.h | 92 - src/plugins/nat/nat44_handoff.c | 343 -- src/plugins/nat/nat64/nat64.api | 2 +- src/plugins/nat/nat66/nat66.api | 2 +- src/plugins/nat/nat_affinity.c | 282 -- src/plugins/nat/nat_affinity.h | 152 - src/plugins/nat/nat_dpo.c | 79 - src/plugins/nat/nat_dpo.h | 36 - src/plugins/nat/nat_format.c | 320 -- src/plugins/nat/nat_inlines.h | 686 ---- src/plugins/nat/nat_types.api | 47 - src/plugins/nat/out2in_ed.c | 1448 -------- src/plugins/nat/test/test_nat44_ed.py | 94 +- src/plugins/nat/test/test_nat44_ei.py | 782 +++-- 61 files changed, 25108 insertions(+), 20129 deletions(-) delete mode 100644 src/plugins/nat/in2out_ed.c create mode 100644 src/plugins/nat/lib/lib.c create mode 100644 src/plugins/nat/lib/log.h create mode 100644 src/plugins/nat/lib/nat_types.api delete mode 100644 src/plugins/nat/nat.c delete mode 100644 src/plugins/nat/nat.h create mode 100644 src/plugins/nat/nat44-ed/nat44_ed.api create mode 100644 src/plugins/nat/nat44-ed/nat44_ed.c create mode 100644 src/plugins/nat/nat44-ed/nat44_ed.h create mode 100644 src/plugins/nat/nat44-ed/nat44_ed_affinity.c create mode 100644 src/plugins/nat/nat44-ed/nat44_ed_affinity.h create mode 100644 src/plugins/nat/nat44-ed/nat44_ed_api.c create mode 100644 src/plugins/nat/nat44-ed/nat44_ed_classify.c create mode 100644 src/plugins/nat/nat44-ed/nat44_ed_cli.c create mode 100644 src/plugins/nat/nat44-ed/nat44_ed_format.c create mode 100644 src/plugins/nat/nat44-ed/nat44_ed_handoff.c create mode 100644 src/plugins/nat/nat44-ed/nat44_ed_in2out.c create mode 100644 src/plugins/nat/nat44-ed/nat44_ed_inlines.h create mode 100644 src/plugins/nat/nat44-ed/nat44_ed_out2in.c create mode 100644 src/plugins/nat/nat44-ei/nat44_ei.api create mode 100644 src/plugins/nat/nat44-ei/nat44_ei_api.c create mode 100644 src/plugins/nat/nat44-ei/nat44_ei_cli.c create mode 100644 src/plugins/nat/nat44-ei/nat44_ei_dpo.c create mode 100644 src/plugins/nat/nat44-ei/nat44_ei_dpo.h create mode 100644 src/plugins/nat/nat44-ei/nat44_ei_hairpinning.c create mode 100644 src/plugins/nat/nat44-ei/nat44_ei_hairpinning.h create mode 100644 src/plugins/nat/nat44-ei/nat44_ei_handoff.c delete mode 100644 src/plugins/nat/nat44.api delete mode 100644 src/plugins/nat/nat44/ed_inlines.h delete mode 100644 src/plugins/nat/nat44/inlines.h delete mode 100644 src/plugins/nat/nat44_api.c delete mode 100644 src/plugins/nat/nat44_classify.c delete mode 100644 src/plugins/nat/nat44_cli.c delete mode 100644 src/plugins/nat/nat44_hairpinning.c delete mode 100644 src/plugins/nat/nat44_hairpinning.h delete mode 100644 src/plugins/nat/nat44_handoff.c delete mode 100644 src/plugins/nat/nat_affinity.c delete mode 100644 src/plugins/nat/nat_affinity.h delete mode 100644 src/plugins/nat/nat_dpo.c delete mode 100644 src/plugins/nat/nat_dpo.h delete mode 100644 src/plugins/nat/nat_format.c delete mode 100644 src/plugins/nat/nat_inlines.h delete mode 100644 src/plugins/nat/nat_types.api delete mode 100644 src/plugins/nat/out2in_ed.c diff --git a/src/plugins/nat/CMakeLists.txt b/src/plugins/nat/CMakeLists.txt index 8fefadf0dd1..623c92ace69 100644 --- a/src/plugins/nat/CMakeLists.txt +++ b/src/plugins/nat/CMakeLists.txt @@ -18,43 +18,60 @@ add_vpp_library(nat lib/ipfix_logging.c lib/nat_syslog.c lib/alloc.c + lib/lib.c INSTALL_HEADERS lib/ipfix_logging.h lib/nat_syslog.h lib/alloc.h + lib/lib.h ) add_vpp_plugin(nat SOURCES - nat.c - nat44_api.c - in2out_ed.c - out2in_ed.c - nat_dpo.c - nat44_cli.c - nat44_handoff.c - nat44_hairpinning.c - nat44_classify.c - nat_affinity.c - nat_format.c + nat44-ed/nat44_ed.c + nat44-ed/nat44_ed_api.c + nat44-ed/nat44_ed_in2out.c + nat44-ed/nat44_ed_out2in.c + nat44-ed/nat44_ed_cli.c + nat44-ed/nat44_ed_format.c + nat44-ed/nat44_ed_affinity.c + nat44-ed/nat44_ed_handoff.c + nat44-ed/nat44_ed_classify.c + + MULTIARCH_SOURCES + nat44-ed/nat44_ed_in2out.c + nat44-ed/nat44_ed_out2in.c + nat44-ed/nat44_ed_handoff.c + nat44-ed/nat44_ed_classify.c + + API_FILES + nat44-ed/nat44_ed.api + lib/nat_types.api + + LINK_LIBRARIES nat +) + +add_vpp_plugin(nat44_ei + SOURCES nat44-ei/nat44_ei.c nat44-ei/nat44_ei_ha.c + nat44-ei/nat44_ei_cli.c + nat44-ei/nat44_ei_api.c + nat44-ei/nat44_ei_dpo.c nat44-ei/nat44_ei_in2out.c nat44-ei/nat44_ei_out2in.c + nat44-ei/nat44_ei_handoff.c + nat44-ei/nat44_ei_hairpinning.c MULTIARCH_SOURCES - in2out_ed.c - out2in_ed.c - nat44_classify.c - nat44_hairpinning.c - nat44_handoff.c nat44-ei/nat44_ei_in2out.c nat44-ei/nat44_ei_out2in.c + nat44-ei/nat44_ei_hairpinning.c API_FILES - nat44.api - nat_types.api + nat44-ei/nat44_ei.api + lib/nat_types.api LINK_LIBRARIES nat ) @@ -96,7 +113,7 @@ add_vpp_plugin(nat66 API_FILES nat66/nat66.api - nat_types.api + lib/nat_types.api LINK_LIBRARIES nat ) @@ -115,7 +132,7 @@ add_vpp_plugin(det44 API_FILES det44/det44.api - nat_types.api + lib/nat_types.api LINK_LIBRARIES nat ) @@ -135,7 +152,7 @@ add_vpp_plugin(nat64 API_FILES nat64/nat64.api - nat_types.api + lib/nat_types.api LINK_LIBRARIES nat ) diff --git a/src/plugins/nat/FEATURE.yaml b/src/plugins/nat/FEATURE.yaml index 175d2c8b1dd..bbb8586390e 100644 --- a/src/plugins/nat/FEATURE.yaml +++ b/src/plugins/nat/FEATURE.yaml @@ -4,7 +4,17 @@ maintainer: - Ole Troan - Filip Varga features: - - NAT44: + - NAT44-EI - IPv4 Endpoint Independent NAT + - 1:1 NAT + - 1:1 NAT with ports + - VRF awareness + - Multiple inside interfaces + - Hairpinning + - IPFIX + - Syslog + - TCP MSS clamping + - Local bypass (DHCP) + - NAT44-ED - IPv4 Endpoint Dependent NAT - 1:1 NAT - 1:1 NAT with ports - VRF awareness @@ -12,13 +22,12 @@ features: - Hairpinning - IPFIX - Syslog - - Endpoint dependent NAT - TCP MSS clamping - Local bypass (DHCP) - DET44 - deterministic NAT (CGN) - NAT64 - NAT66 - - DS-lite + - DSLITE - 464XLAT description: "The Network Address Translation (NAT) plugin offers a multiple address translation functions. These can be used in a raft of different diff --git a/src/plugins/nat/det44/det44.api b/src/plugins/nat/det44/det44.api index 9b71b9c8c6d..7b6aef70883 100644 --- a/src/plugins/nat/det44/det44.api +++ b/src/plugins/nat/det44/det44.api @@ -16,7 +16,7 @@ option version = "1.0.0"; import "vnet/ip/ip_types.api"; import "vnet/interface_types.api"; -import "plugins/nat/nat_types.api"; +import "plugins/nat/lib/nat_types.api"; /** * @file det44.api diff --git a/src/plugins/nat/dslite/dslite_cli.c b/src/plugins/nat/dslite/dslite_cli.c index 25fcd01d523..0537957930c 100644 --- a/src/plugins/nat/dslite/dslite_cli.c +++ b/src/plugins/nat/dslite/dslite_cli.c @@ -203,25 +203,6 @@ dslite_show_b4_ip6_addr_command_fn (vlib_main_t * vm, return 0; } -static u8 * -format_nat_protocol (u8 * s, va_list * args) -{ - u32 i = va_arg (*args, u32); - u8 *t = 0; - - switch (i) - { -#define _(N, j, n, str) case NAT_PROTOCOL_##N: t = (u8 *) str; break; - foreach_nat_protocol -#undef _ - default: - s = format (s, "unknown"); - return s; - } - s = format (s, "%s", t); - return s; -} - static u8 * format_dslite_session (u8 * s, va_list * args) { diff --git a/src/plugins/nat/in2out_ed.c b/src/plugins/nat/in2out_ed.c deleted file mode 100644 index 9dc68576fd4..00000000000 --- a/src/plugins/nat/in2out_ed.c +++ /dev/null @@ -1,1531 +0,0 @@ -/* - * Copyright (c) 2018 Cisco and/or its affiliates. - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/** - * @file - * @brief NAT44 endpoint-dependent inside to outside network translation - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* number of attempts to get a port for ED overloading algorithm, if rolling - * a dice this many times doesn't produce a free port, it's treated - * as if there were no free ports available to conserve resources */ -#define ED_PORT_ALLOC_ATTEMPTS (10) - -static char *nat_in2out_ed_error_strings[] = { -#define _(sym,string) string, - foreach_nat_in2out_ed_error -#undef _ -}; - -typedef struct -{ - u32 sw_if_index; - u32 next_index; - u32 session_index; - nat_translation_error_e translation_error; - nat_6t_flow_t i2of; - nat_6t_flow_t o2if; - clib_bihash_kv_16_8_t search_key; - u8 is_slow_path; - u8 translation_via_i2of; - u8 lookup_skipped; -} nat_in2out_ed_trace_t; - -static u8 * -format_nat_in2out_ed_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 *); - nat_in2out_ed_trace_t *t = va_arg (*args, nat_in2out_ed_trace_t *); - char *tag; - - tag = - t->is_slow_path ? "NAT44_IN2OUT_ED_SLOW_PATH" : - "NAT44_IN2OUT_ED_FAST_PATH"; - - s = format (s, "%s: sw_if_index %d, next index %d, session %d", tag, - t->sw_if_index, t->next_index, t->session_index); - if (~0 != t->session_index) - { - s = format (s, ", translation result '%U' via %s", - format_nat_ed_translation_error, t->translation_error, - t->translation_via_i2of ? "i2of" : "o2if"); - s = format (s, "\n i2of %U", format_nat_6t_flow, &t->i2of); - s = format (s, "\n o2if %U", format_nat_6t_flow, &t->o2if); - } - if (!t->is_slow_path) - { - if (t->lookup_skipped) - { - s = format (s, "\n lookup skipped - cached session index used"); - } - else - { - s = format (s, "\n search key %U", format_ed_session_kvp, - &t->search_key); - } - } - - return s; -} - -static int -nat_ed_alloc_addr_and_port (snat_main_t *sm, u32 rx_fib_index, u32 nat_proto, - u32 thread_index, ip4_address_t r_addr, u16 r_port, - u8 proto, u16 port_per_thread, - u32 snat_thread_index, snat_session_t *s, - ip4_address_t *outside_addr, u16 *outside_port) -{ - int i; - snat_address_t *a, *ga = 0; - - const u16 port_thread_offset = (port_per_thread * snat_thread_index) + 1024; - - for (i = 0; i < vec_len (sm->addresses); i++) - { - a = sm->addresses + i; - switch (nat_proto) - { -#define _(N, j, n, unused) \ - case NAT_PROTOCOL_##N: \ - if (a->fib_index == rx_fib_index) \ - { \ - s->o2i.match.daddr = a->addr; \ - /* first try port suggested by caller */ \ - u16 port = clib_net_to_host_u16 (*outside_port); \ - u16 port_offset = port - port_thread_offset; \ - if (port <= port_thread_offset || \ - port > port_thread_offset + port_per_thread) \ - { \ - /* need to pick a different port, suggested port doesn't fit in \ - * this thread's port range */ \ - port_offset = snat_random_port (0, port_per_thread - 1); \ - port = port_thread_offset + port_offset; \ - } \ - u16 attempts = ED_PORT_ALLOC_ATTEMPTS; \ - do \ - { \ - if (NAT_PROTOCOL_ICMP == nat_proto) \ - { \ - s->o2i.match.sport = clib_host_to_net_u16 (port); \ - } \ - s->o2i.match.dport = clib_host_to_net_u16 (port); \ - if (0 == \ - nat_ed_ses_o2i_flow_hash_add_del (sm, thread_index, s, 2)) \ - { \ - ++a->busy_##n##_port_refcounts[port]; \ - a->busy_##n##_ports_per_thread[thread_index]++; \ - a->busy_##n##_ports++; \ - *outside_addr = a->addr; \ - *outside_port = clib_host_to_net_u16 (port); \ - return 0; \ - } \ - port_offset = snat_random_port (0, port_per_thread - 1); \ - port = port_thread_offset + port_offset; \ - --attempts; \ - } \ - while (attempts > 0); \ - } \ - else if (a->fib_index == ~0) \ - { \ - ga = a; \ - } \ - break; - - foreach_nat_protocol; - default: - nat_elog_info ("unknown protocol"); - return 1; - } - } - - if (ga) - { - /* fake fib_index to reuse macro */ - rx_fib_index = ~0; - a = ga; - switch (nat_proto) - { - foreach_nat_protocol; - default: - nat_elog_info ("unknown protocol"); - return 1; - } - } - -#undef _ - - /* Totally out of translations to use... */ - nat_ipfix_logging_addresses_exhausted (thread_index, 0); - return 1; -} - -static_always_inline u32 -nat_outside_fib_index_lookup (snat_main_t * sm, ip4_address_t addr) -{ - fib_node_index_t fei = FIB_NODE_INDEX_INVALID; - nat_outside_fib_t *outside_fib; - fib_prefix_t pfx = { - .fp_proto = FIB_PROTOCOL_IP4, - .fp_len = 32, - .fp_addr = {.ip4.as_u32 = addr.as_u32,} - , - }; - // TODO: multiple vrfs none can resolve addr - /* *INDENT-OFF* */ - vec_foreach (outside_fib, sm->outside_fibs) - { - fei = fib_table_lookup (outside_fib->fib_index, &pfx); - if (FIB_NODE_INDEX_INVALID != fei) - { - if (fib_entry_get_resolving_interface (fei) != ~0) - { - return outside_fib->fib_index; - } - } - } - /* *INDENT-ON* */ - return ~0; -} - -static_always_inline int -nat44_ed_external_sm_lookup (snat_main_t *sm, ip4_address_t match_addr, - u16 match_port, nat_protocol_t match_protocol, - u32 match_fib_index, ip4_address_t *daddr, - u16 *dport) -{ - clib_bihash_kv_8_8_t kv, value; - init_nat_k (&kv, match_addr, match_port, match_fib_index, match_protocol); - if (clib_bihash_search_8_8 (&sm->static_mapping_by_external, &kv, &value)) - { - /* Try address only mapping */ - init_nat_k (&kv, match_addr, 0, 0, 0); - if (clib_bihash_search_8_8 (&sm->static_mapping_by_external, &kv, - &value)) - return 0; - } - - snat_static_mapping_t *m = - pool_elt_at_index (sm->static_mappings, value.value); - *daddr = m->local_addr; - if (dport) - { - /* Address only mapping doesn't change port */ - *dport = is_addr_only_static_mapping (m) ? match_port : m->local_port; - } - return 1; -} - -static u32 -slow_path_ed (snat_main_t *sm, vlib_buffer_t *b, ip4_address_t l_addr, - ip4_address_t r_addr, u16 l_port, u16 r_port, u8 proto, - u32 rx_fib_index, snat_session_t **sessionp, - vlib_node_runtime_t *node, u32 next, u32 thread_index, f64 now) -{ - snat_main_per_thread_data_t *tsm = &sm->per_thread_data[thread_index]; - ip4_address_t outside_addr; - u16 outside_port; - u32 outside_fib_index; - u8 is_identity_nat; - - u32 nat_proto = ip_proto_to_nat_proto (proto); - snat_session_t *s = NULL; - lb_nat_type_t lb = 0; - ip4_address_t daddr = r_addr; - u16 dport = r_port; - - if (PREDICT_TRUE (nat_proto == NAT_PROTOCOL_TCP)) - { - if (PREDICT_FALSE - (!tcp_flags_is_init - (vnet_buffer (b)->ip.reass.icmp_type_or_tcp_flags))) - { - b->error = node->errors[NAT_IN2OUT_ED_ERROR_NON_SYN]; - return NAT_NEXT_DROP; - } - } - - if (PREDICT_FALSE - (nat44_ed_maximum_sessions_exceeded (sm, rx_fib_index, thread_index))) - { - if (!nat_lru_free_one (sm, thread_index, now)) - { - b->error = node->errors[NAT_IN2OUT_ED_ERROR_MAX_SESSIONS_EXCEEDED]; - nat_ipfix_logging_max_sessions (thread_index, - sm->max_translations_per_thread); - nat_elog_notice ("maximum sessions exceeded"); - return NAT_NEXT_DROP; - } - } - - outside_fib_index = sm->outside_fib_index; - - switch (vec_len (sm->outside_fibs)) - { - case 0: - outside_fib_index = sm->outside_fib_index; - break; - case 1: - outside_fib_index = sm->outside_fibs[0].fib_index; - break; - default: - outside_fib_index = nat_outside_fib_index_lookup (sm, r_addr); - break; - } - - ip4_address_t sm_addr; - u16 sm_port; - u32 sm_fib_index; - /* First try to match static mapping by local address and port */ - int is_sm; - if (snat_static_mapping_match (sm, l_addr, l_port, rx_fib_index, nat_proto, - &sm_addr, &sm_port, &sm_fib_index, 0, 0, 0, - &lb, 0, &is_identity_nat, 0)) - { - is_sm = 0; - } - else - { - is_sm = 1; - } - - if (PREDICT_FALSE (is_sm && is_identity_nat)) - { - *sessionp = NULL; - return next; - } - - s = nat_ed_session_alloc (sm, thread_index, now, proto); - ASSERT (s); - - if (!is_sm) - { - s->in2out.addr = l_addr; - s->in2out.port = l_port; - s->nat_proto = nat_proto; - s->in2out.fib_index = rx_fib_index; - s->out2in.fib_index = outside_fib_index; - - // suggest using local port to allocation function - outside_port = l_port; - - // hairpinning? - int is_hairpinning = nat44_ed_external_sm_lookup ( - sm, r_addr, r_port, nat_proto, outside_fib_index, &daddr, &dport); - s->flags |= is_hairpinning * SNAT_SESSION_FLAG_HAIRPINNING; - - // destination addr/port updated with real values in - // nat_ed_alloc_addr_and_port - nat_6t_o2i_flow_init (sm, thread_index, s, daddr, dport, daddr, 0, - s->out2in.fib_index, proto); - nat_6t_flow_daddr_rewrite_set (&s->o2i, l_addr.as_u32); - if (NAT_PROTOCOL_ICMP == nat_proto) - { - nat_6t_flow_icmp_id_rewrite_set (&s->o2i, l_port); - } - else - { - nat_6t_flow_dport_rewrite_set (&s->o2i, l_port); - } - nat_6t_flow_txfib_rewrite_set (&s->o2i, rx_fib_index); - - if (nat_ed_alloc_addr_and_port ( - sm, rx_fib_index, nat_proto, thread_index, daddr, dport, proto, - sm->port_per_thread, tsm->snat_thread_index, s, &outside_addr, - &outside_port)) - { - nat_elog_notice ("addresses exhausted"); - b->error = node->errors[NAT_IN2OUT_ED_ERROR_OUT_OF_PORTS]; - nat_ed_session_delete (sm, s, thread_index, 1); - return NAT_NEXT_DROP; - } - s->out2in.addr = outside_addr; - s->out2in.port = outside_port; - } - else - { - // static mapping - s->out2in.addr = outside_addr = sm_addr; - s->out2in.port = outside_port = sm_port; - s->in2out.addr = l_addr; - s->in2out.port = l_port; - s->nat_proto = nat_proto; - s->in2out.fib_index = rx_fib_index; - s->out2in.fib_index = outside_fib_index; - s->flags |= SNAT_SESSION_FLAG_STATIC_MAPPING; - - // hairpinning? - int is_hairpinning = nat44_ed_external_sm_lookup ( - sm, r_addr, r_port, nat_proto, outside_fib_index, &daddr, &dport); - s->flags |= is_hairpinning * SNAT_SESSION_FLAG_HAIRPINNING; - - if (NAT_PROTOCOL_ICMP == nat_proto) - { - nat_6t_o2i_flow_init (sm, thread_index, s, daddr, sm_port, sm_addr, - sm_port, s->out2in.fib_index, proto); - nat_6t_flow_icmp_id_rewrite_set (&s->o2i, l_port); - } - else - { - nat_6t_o2i_flow_init (sm, thread_index, s, daddr, dport, sm_addr, - sm_port, s->out2in.fib_index, proto); - nat_6t_flow_dport_rewrite_set (&s->o2i, l_port); - } - nat_6t_flow_daddr_rewrite_set (&s->o2i, l_addr.as_u32); - nat_6t_flow_txfib_rewrite_set (&s->o2i, rx_fib_index); - if (nat_ed_ses_o2i_flow_hash_add_del (sm, thread_index, s, 2)) - { - nat_elog_notice ("out2in key add failed"); - goto error; - } - } - - if (lb) - s->flags |= SNAT_SESSION_FLAG_LOAD_BALANCING; - s->flags |= SNAT_SESSION_FLAG_ENDPOINT_DEPENDENT; - s->ext_host_addr = r_addr; - s->ext_host_port = r_port; - - nat_6t_i2o_flow_init (sm, thread_index, s, l_addr, l_port, r_addr, r_port, - rx_fib_index, proto); - nat_6t_flow_saddr_rewrite_set (&s->i2o, outside_addr.as_u32); - nat_6t_flow_daddr_rewrite_set (&s->i2o, daddr.as_u32); - if (NAT_PROTOCOL_ICMP == nat_proto) - { - nat_6t_flow_icmp_id_rewrite_set (&s->i2o, outside_port); - } - else - { - nat_6t_flow_sport_rewrite_set (&s->i2o, outside_port); - nat_6t_flow_dport_rewrite_set (&s->i2o, dport); - } - nat_6t_flow_txfib_rewrite_set (&s->i2o, outside_fib_index); - - if (nat_ed_ses_i2o_flow_hash_add_del (sm, thread_index, s, 1)) - { - nat_elog_notice ("in2out key add failed"); - goto error; - } - - /* log NAT event */ - nat_ipfix_logging_nat44_ses_create (thread_index, - s->in2out.addr.as_u32, - s->out2in.addr.as_u32, - s->nat_proto, - s->in2out.port, - s->out2in.port, s->in2out.fib_index); - - nat_syslog_nat44_sadd (s->user_index, s->in2out.fib_index, - &s->in2out.addr, s->in2out.port, - &s->ext_host_nat_addr, s->ext_host_nat_port, - &s->out2in.addr, s->out2in.port, - &s->ext_host_addr, s->ext_host_port, s->nat_proto, - 0); - - per_vrf_sessions_register_session (s, thread_index); - - *sessionp = s; - return next; -error: - if (s) - { - if (!is_sm) - { - snat_free_outside_address_and_port (sm->addresses, thread_index, - &outside_addr, outside_port, - nat_proto); - } - nat_ed_session_delete (sm, s, thread_index, 1); - } - *sessionp = s = NULL; - return NAT_NEXT_DROP; -} - -static_always_inline int -nat44_ed_not_translate (snat_main_t * sm, vlib_node_runtime_t * node, - u32 sw_if_index, ip4_header_t * ip, u32 proto, - u32 rx_fib_index, u32 thread_index) -{ - udp_header_t *udp = ip4_next_header (ip); - clib_bihash_kv_16_8_t kv, value; - - init_ed_k (&kv, ip->dst_address, udp->dst_port, ip->src_address, - udp->src_port, sm->outside_fib_index, ip->protocol); - - /* NAT packet aimed at external address if has active sessions */ - if (clib_bihash_search_16_8 (&sm->flow_hash, &kv, &value)) - { - /* or is static mappings */ - ip4_address_t placeholder_addr; - u16 placeholder_port; - u32 placeholder_fib_index; - if (!snat_static_mapping_match - (sm, ip->dst_address, udp->dst_port, sm->outside_fib_index, proto, - &placeholder_addr, &placeholder_port, &placeholder_fib_index, 1, 0, - 0, 0, 0, 0, 0)) - return 0; - } - else - return 0; - - if (sm->forwarding_enabled) - return 1; - - return snat_not_translate_fast (sm, node, sw_if_index, ip, proto, - rx_fib_index); -} - -static_always_inline int -nat_not_translate_output_feature_fwd (snat_main_t * sm, ip4_header_t * ip, - u32 thread_index, f64 now, - vlib_main_t * vm, vlib_buffer_t * b) -{ - clib_bihash_kv_16_8_t kv, value; - snat_session_t *s = 0; - snat_main_per_thread_data_t *tsm = &sm->per_thread_data[thread_index]; - - if (!sm->forwarding_enabled) - return 0; - - if (ip->protocol == IP_PROTOCOL_ICMP) - { - ip4_address_t lookup_saddr, lookup_daddr; - u16 lookup_sport, lookup_dport; - u8 lookup_protocol; - if (nat_get_icmp_session_lookup_values (b, ip, &lookup_saddr, - &lookup_sport, &lookup_daddr, - &lookup_dport, &lookup_protocol)) - return 0; - init_ed_k (&kv, lookup_saddr, lookup_sport, lookup_daddr, lookup_dport, - 0, lookup_protocol); - } - else if (ip->protocol == IP_PROTOCOL_UDP || ip->protocol == IP_PROTOCOL_TCP) - { - init_ed_k (&kv, ip->src_address, vnet_buffer (b)->ip.reass.l4_src_port, - ip->dst_address, vnet_buffer (b)->ip.reass.l4_dst_port, 0, - ip->protocol); - } - else - { - init_ed_k (&kv, ip->src_address, 0, ip->dst_address, 0, 0, - ip->protocol); - } - - if (!clib_bihash_search_16_8 (&sm->flow_hash, &kv, &value)) - { - ASSERT (thread_index == ed_value_get_thread_index (&value)); - s = - pool_elt_at_index (tsm->sessions, - ed_value_get_session_index (&value)); - - if (is_fwd_bypass_session (s)) - { - if (ip->protocol == IP_PROTOCOL_TCP) - { - nat44_set_tcp_session_state_i2o (sm, now, s, b, thread_index); - } - /* Accounting */ - nat44_session_update_counters (s, now, - vlib_buffer_length_in_chain (vm, b), - thread_index); - /* Per-user LRU list maintenance */ - nat44_session_update_lru (sm, s, thread_index); - return 1; - } - else - return 0; - } - - return 0; -} - -static_always_inline int -nat44_ed_not_translate_output_feature (snat_main_t * sm, ip4_header_t * ip, - u16 src_port, u16 dst_port, - u32 thread_index, u32 rx_sw_if_index, - u32 tx_sw_if_index, f64 now) -{ - clib_bihash_kv_16_8_t kv, value; - snat_main_per_thread_data_t *tsm = &sm->per_thread_data[thread_index]; - snat_interface_t *i; - snat_session_t *s; - u32 rx_fib_index = ip4_fib_table_get_index_for_sw_if_index (rx_sw_if_index); - u32 tx_fib_index = ip4_fib_table_get_index_for_sw_if_index (tx_sw_if_index); - - /* src NAT check */ - init_ed_k (&kv, ip->src_address, src_port, ip->dst_address, dst_port, - tx_fib_index, ip->protocol); - if (!clib_bihash_search_16_8 (&sm->flow_hash, &kv, &value)) - { - ASSERT (thread_index == ed_value_get_thread_index (&value)); - s = - pool_elt_at_index (tsm->sessions, - ed_value_get_session_index (&value)); - if (nat44_is_ses_closed (s) - && (!s->tcp_closed_timestamp || now >= s->tcp_closed_timestamp)) - { - nat_free_session_data (sm, s, thread_index, 0); - nat_ed_session_delete (sm, s, thread_index, 1); - } - return 1; - } - - /* dst NAT check */ - init_ed_k (&kv, ip->dst_address, dst_port, ip->src_address, src_port, - rx_fib_index, ip->protocol); - if (!clib_bihash_search_16_8 (&sm->flow_hash, &kv, &value)) - { - ASSERT (thread_index == ed_value_get_thread_index (&value)); - s = - pool_elt_at_index (tsm->sessions, - ed_value_get_session_index (&value)); - - if (is_fwd_bypass_session (s)) - return 0; - - /* hairpinning */ - /* *INDENT-OFF* */ - pool_foreach (i, sm->output_feature_interfaces) - { - if ((nat_interface_is_inside (i)) && (rx_sw_if_index == i->sw_if_index)) - return 0; - } - /* *INDENT-ON* */ - return 1; - } - - return 0; -} - -static inline u32 -icmp_in2out_ed_slow_path (snat_main_t *sm, vlib_buffer_t *b, ip4_header_t *ip, - icmp46_header_t *icmp, u32 sw_if_index, - u32 rx_fib_index, vlib_node_runtime_t *node, - u32 next, f64 now, u32 thread_index, - nat_protocol_t nat_proto, snat_session_t **s_p) -{ - vlib_main_t *vm = vlib_get_main (); - u16 checksum; - int err; - snat_session_t *s = NULL; - u8 lookup_protocol = ip->protocol; - u16 lookup_sport, lookup_dport; - ip4_address_t lookup_saddr, lookup_daddr; - - err = nat_get_icmp_session_lookup_values (b, ip, &lookup_saddr, - &lookup_sport, &lookup_daddr, - &lookup_dport, &lookup_protocol); - if (err != 0) - { - b->error = node->errors[err]; - return NAT_NEXT_DROP; - } - - if (vnet_buffer (b)->sw_if_index[VLIB_TX] != ~0) - { - if (PREDICT_FALSE (nat44_ed_not_translate_output_feature ( - sm, ip, lookup_sport, lookup_dport, thread_index, sw_if_index, - vnet_buffer (b)->sw_if_index[VLIB_TX], now))) - { - return next; - } - } - else - { - if (PREDICT_FALSE (nat44_ed_not_translate (sm, node, sw_if_index, ip, - NAT_PROTOCOL_ICMP, - rx_fib_index, thread_index))) - { - return next; - } - } - - if (PREDICT_FALSE (icmp_type_is_error_message ( - vnet_buffer (b)->ip.reass.icmp_type_or_tcp_flags))) - { - b->error = node->errors[NAT_IN2OUT_ED_ERROR_BAD_ICMP_TYPE]; - return NAT_NEXT_DROP; - } - - next = slow_path_ed (sm, b, ip->src_address, ip->dst_address, lookup_sport, - lookup_dport, ip->protocol, rx_fib_index, &s, node, - next, thread_index, vlib_time_now (vm)); - - if (NAT_NEXT_DROP == next) - goto out; - - if (PREDICT_TRUE (!ip4_is_fragment (ip))) - { - ip_csum_t sum = ip_incremental_checksum_buffer ( - vm, b, (u8 *) icmp - (u8 *) vlib_buffer_get_current (b), - ntohs (ip->length) - ip4_header_bytes (ip), 0); - checksum = ~ip_csum_fold (sum); - if (PREDICT_FALSE (checksum != 0 && checksum != 0xffff)) - { - next = NAT_NEXT_DROP; - goto out; - } - } - -out: - if (PREDICT_TRUE (next != NAT_NEXT_DROP && s)) - { - /* Accounting */ - nat44_session_update_counters ( - s, now, vlib_buffer_length_in_chain (vm, b), thread_index); - /* Per-user LRU list maintenance */ - nat44_session_update_lru (sm, s, thread_index); - } - *s_p = s; - return next; -} - -static snat_session_t * -nat44_ed_in2out_slowpath_unknown_proto (snat_main_t *sm, vlib_buffer_t *b, - ip4_header_t *ip, u32 rx_fib_index, - u32 thread_index, f64 now, - vlib_main_t *vm, - vlib_node_runtime_t *node) -{ - clib_bihash_kv_8_8_t kv, value; - clib_bihash_kv_16_8_t s_kv, s_value; - snat_static_mapping_t *m = NULL; - snat_main_per_thread_data_t *tsm = &sm->per_thread_data[thread_index]; - snat_session_t *s = NULL; - u32 outside_fib_index = sm->outside_fib_index; - int i; - ip4_address_t new_src_addr = { 0 }; - ip4_address_t new_dst_addr = ip->dst_address; - - if (PREDICT_FALSE ( - nat44_ed_maximum_sessions_exceeded (sm, rx_fib_index, thread_index))) - { - b->error = node->errors[NAT_IN2OUT_ED_ERROR_MAX_SESSIONS_EXCEEDED]; - nat_ipfix_logging_max_sessions (thread_index, - sm->max_translations_per_thread); - nat_elog_notice ("maximum sessions exceeded"); - return 0; - } - - switch (vec_len (sm->outside_fibs)) - { - case 0: - outside_fib_index = sm->outside_fib_index; - break; - case 1: - outside_fib_index = sm->outside_fibs[0].fib_index; - break; - default: - outside_fib_index = nat_outside_fib_index_lookup (sm, ip->dst_address); - break; - } - - init_nat_k (&kv, ip->src_address, 0, rx_fib_index, 0); - - /* Try to find static mapping first */ - if (!clib_bihash_search_8_8 (&sm->static_mapping_by_local, &kv, &value)) - { - m = pool_elt_at_index (sm->static_mappings, value.value); - new_src_addr = m->external_addr; - } - else - { - pool_foreach (s, tsm->sessions) - { - if (s->ext_host_addr.as_u32 == ip->dst_address.as_u32) - { - init_ed_k (&s_kv, s->out2in.addr, 0, ip->dst_address, 0, - outside_fib_index, ip->protocol); - if (clib_bihash_search_16_8 (&sm->flow_hash, &s_kv, &s_value)) - { - new_src_addr = s->out2in.addr; - } - break; - } - } - - if (!new_src_addr.as_u32) - { - for (i = 0; i < vec_len (sm->addresses); i++) - { - init_ed_k (&s_kv, sm->addresses[i].addr, 0, ip->dst_address, 0, - outside_fib_index, ip->protocol); - if (clib_bihash_search_16_8 (&sm->flow_hash, &s_kv, &s_value)) - { - new_src_addr = sm->addresses[i].addr; - } - } - } - } - - if (!new_src_addr.as_u32) - { - // could not allocate address for translation ... - return 0; - } - - s = nat_ed_session_alloc (sm, thread_index, now, ip->protocol); - if (!s) - { - b->error = node->errors[NAT_IN2OUT_ED_ERROR_MAX_SESSIONS_EXCEEDED]; - nat_elog_warn ("create NAT session failed"); - return 0; - } - - nat_6t_i2o_flow_init (sm, thread_index, s, ip->src_address, 0, - ip->dst_address, 0, rx_fib_index, ip->protocol); - nat_6t_flow_saddr_rewrite_set (&s->i2o, new_src_addr.as_u32); - nat_6t_flow_txfib_rewrite_set (&s->i2o, outside_fib_index); - - // hairpinning? - int is_hairpinning = - nat44_ed_external_sm_lookup (sm, ip->dst_address, 0, NAT_PROTOCOL_OTHER, - outside_fib_index, &new_dst_addr, NULL); - s->flags |= is_hairpinning * SNAT_SESSION_FLAG_HAIRPINNING; - - nat_6t_flow_daddr_rewrite_set (&s->i2o, new_dst_addr.as_u32); - nat_6t_flow_txfib_rewrite_set (&s->i2o, outside_fib_index); - - nat_6t_o2i_flow_init (sm, thread_index, s, new_dst_addr, 0, new_src_addr, 0, - outside_fib_index, ip->protocol); - nat_6t_flow_saddr_rewrite_set (&s->o2i, ip->dst_address.as_u32); - nat_6t_flow_daddr_rewrite_set (&s->o2i, ip->src_address.as_u32); - nat_6t_flow_txfib_rewrite_set (&s->o2i, rx_fib_index); - - s->ext_host_addr.as_u32 = ip->dst_address.as_u32; - s->flags |= SNAT_SESSION_FLAG_UNKNOWN_PROTO; - s->flags |= SNAT_SESSION_FLAG_ENDPOINT_DEPENDENT; - s->out2in.addr.as_u32 = new_src_addr.as_u32; - s->out2in.fib_index = outside_fib_index; - s->in2out.addr.as_u32 = ip->src_address.as_u32; - s->in2out.fib_index = rx_fib_index; - s->in2out.port = s->out2in.port = ip->protocol; - if (m) - s->flags |= SNAT_SESSION_FLAG_STATIC_MAPPING; - - if (nat_ed_ses_i2o_flow_hash_add_del (sm, thread_index, s, 1)) - { - nat_elog_notice ("in2out flow hash add failed"); - nat_ed_session_delete (sm, s, thread_index, 1); - return NULL; - } - - if (nat_ed_ses_o2i_flow_hash_add_del (sm, thread_index, s, 1)) - { - nat_elog_notice ("out2in flow hash add failed"); - nat_ed_session_delete (sm, s, thread_index, 1); - return NULL; - } - - per_vrf_sessions_register_session (s, thread_index); - - /* Accounting */ - nat44_session_update_counters (s, now, vlib_buffer_length_in_chain (vm, b), - thread_index); - /* Per-user LRU list maintenance */ - nat44_session_update_lru (sm, s, thread_index); - - return s; -} - -static inline uword -nat44_ed_in2out_fast_path_node_fn_inline (vlib_main_t * vm, - vlib_node_runtime_t * node, - vlib_frame_t * frame, - int is_output_feature) -{ - u32 n_left_from, *from; - snat_main_t *sm = &snat_main; - f64 now = vlib_time_now (vm); - u32 thread_index = vm->thread_index; - snat_main_per_thread_data_t *tsm = &sm->per_thread_data[thread_index]; - u32 def_slow = is_output_feature ? NAT_NEXT_IN2OUT_ED_OUTPUT_SLOW_PATH - : NAT_NEXT_IN2OUT_ED_SLOW_PATH; - - from = vlib_frame_vector_args (frame); - n_left_from = frame->n_vectors; - - vlib_buffer_t *bufs[VLIB_FRAME_SIZE], **b = bufs; - u16 nexts[VLIB_FRAME_SIZE], *next = nexts; - vlib_get_buffers (vm, from, b, n_left_from); - - while (n_left_from > 0) - { - vlib_buffer_t *b0; - u32 sw_if_index0, rx_fib_index0, iph_offset0 = 0; - nat_protocol_t proto0; - ip4_header_t *ip0; - snat_session_t *s0 = 0; - clib_bihash_kv_16_8_t kv0, value0; - nat_translation_error_e translation_error = NAT_ED_TRNSL_ERR_SUCCESS; - nat_6t_flow_t *f = 0; - ip4_address_t lookup_saddr, lookup_daddr; - u16 lookup_sport, lookup_dport; - u8 lookup_protocol; - int lookup_skipped = 0; - - b0 = *b; - b++; - - /* Prefetch next iteration. */ - if (PREDICT_TRUE (n_left_from >= 2)) - { - vlib_buffer_t *p2; - - p2 = *b; - - vlib_prefetch_buffer_header (p2, LOAD); - - CLIB_PREFETCH (p2->data, CLIB_CACHE_LINE_BYTES, LOAD); - } - - if (is_output_feature) - { - iph_offset0 = vnet_buffer (b0)->ip.reass.save_rewrite_length; - } - - next[0] = vnet_buffer2 (b0)->nat.arc_next; - - ip0 = - (ip4_header_t *) ((u8 *) vlib_buffer_get_current (b0) + iph_offset0); - - sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX]; - rx_fib_index0 = - fib_table_get_index_for_sw_if_index (FIB_PROTOCOL_IP4, sw_if_index0); - - if (PREDICT_FALSE (ip0->ttl == 1)) - { - vnet_buffer (b0)->sw_if_index[VLIB_TX] = (u32) ~ 0; - icmp4_error_set_vnet_buffer (b0, ICMP4_time_exceeded, - ICMP4_time_exceeded_ttl_exceeded_in_transit, - 0); - next[0] = NAT_NEXT_ICMP_ERROR; - goto trace0; - } - - proto0 = ip_proto_to_nat_proto (ip0->protocol); - - if (is_output_feature) - { - if (PREDICT_FALSE - (nat_not_translate_output_feature_fwd - (sm, ip0, thread_index, now, vm, b0))) - goto trace0; - } - - if (PREDICT_FALSE (proto0 == NAT_PROTOCOL_ICMP)) - { - if (vnet_buffer (b0)->ip.reass.icmp_type_or_tcp_flags != - ICMP4_echo_request && - vnet_buffer (b0)->ip.reass.icmp_type_or_tcp_flags != - ICMP4_echo_reply && - !icmp_type_is_error_message ( - vnet_buffer (b0)->ip.reass.icmp_type_or_tcp_flags)) - { - b0->error = node->errors[NAT_IN2OUT_ED_ERROR_BAD_ICMP_TYPE]; - next[0] = NAT_NEXT_DROP; - goto trace0; - } - int err = nat_get_icmp_session_lookup_values ( - b0, ip0, &lookup_saddr, &lookup_sport, &lookup_daddr, - &lookup_dport, &lookup_protocol); - if (err != 0) - { - b0->error = node->errors[err]; - next[0] = NAT_NEXT_DROP; - goto trace0; - } - } - else - { - lookup_protocol = ip0->protocol; - lookup_saddr = ip0->src_address; - lookup_daddr = ip0->dst_address; - lookup_sport = vnet_buffer (b0)->ip.reass.l4_src_port; - lookup_dport = vnet_buffer (b0)->ip.reass.l4_dst_port; - } - - /* there might be a stashed index in vnet_buffer2 from handoff or - * classify node, see if it can be used */ - if (!pool_is_free_index (tsm->sessions, - vnet_buffer2 (b0)->nat.cached_session_index)) - { - s0 = pool_elt_at_index (tsm->sessions, - vnet_buffer2 (b0)->nat.cached_session_index); - if (PREDICT_TRUE ( - nat_6t_flow_match (&s0->i2o, b0, lookup_saddr, lookup_sport, - lookup_daddr, lookup_dport, lookup_protocol, - rx_fib_index0) - // for some hairpinning cases there are two "i2i" flows instead - // of i2o and o2i as both hosts are on inside - || (s0->flags & SNAT_SESSION_FLAG_HAIRPINNING && - nat_6t_flow_match ( - &s0->o2i, b0, lookup_saddr, lookup_sport, lookup_daddr, - lookup_dport, lookup_protocol, rx_fib_index0)))) - { - /* yes, this is the droid we're looking for */ - lookup_skipped = 1; - goto skip_lookup; - } - s0 = NULL; - } - - init_ed_k (&kv0, ip0->src_address, lookup_sport, ip0->dst_address, - lookup_dport, rx_fib_index0, lookup_protocol); - - // lookup flow - if (clib_bihash_search_16_8 (&sm->flow_hash, &kv0, &value0)) - { - // flow does not exist go slow path - next[0] = def_slow; - goto trace0; - } - - ASSERT (thread_index == ed_value_get_thread_index (&value0)); - s0 = - pool_elt_at_index (tsm->sessions, - ed_value_get_session_index (&value0)); - - skip_lookup: - - if (PREDICT_FALSE (per_vrf_sessions_is_expired (s0, thread_index))) - { - // session is closed, go slow path - nat_free_session_data (sm, s0, thread_index, 0); - nat_ed_session_delete (sm, s0, thread_index, 1); - next[0] = NAT_NEXT_OUT2IN_ED_SLOW_PATH; - goto trace0; - } - - if (s0->tcp_closed_timestamp) - { - if (now >= s0->tcp_closed_timestamp) - { - // session is closed, go slow path, freed in slow path - next[0] = def_slow; - } - else - { - // session in transitory timeout, drop - b0->error = node->errors[NAT_IN2OUT_ED_ERROR_TCP_CLOSED]; - next[0] = NAT_NEXT_DROP; - } - goto trace0; - } - - // drop if session expired - u64 sess_timeout_time; - sess_timeout_time = - s0->last_heard + (f64) nat44_session_get_timeout (sm, s0); - if (now >= sess_timeout_time) - { - nat_free_session_data (sm, s0, thread_index, 0); - nat_ed_session_delete (sm, s0, thread_index, 1); - // session is closed, go slow path - next[0] = def_slow; - goto trace0; - } - - b0->flags |= VNET_BUFFER_F_IS_NATED; - - if (nat_6t_flow_match (&s0->i2o, b0, lookup_saddr, lookup_sport, - lookup_daddr, lookup_dport, lookup_protocol, - rx_fib_index0)) - { - f = &s0->i2o; - } - else if (s0->flags & SNAT_SESSION_FLAG_HAIRPINNING && - nat_6t_flow_match (&s0->o2i, b0, lookup_saddr, lookup_sport, - lookup_daddr, lookup_dport, lookup_protocol, - rx_fib_index0)) - { - f = &s0->o2i; - } - else - { - translation_error = NAT_ED_TRNSL_ERR_FLOW_MISMATCH; - nat_free_session_data (sm, s0, thread_index, 0); - nat_ed_session_delete (sm, s0, thread_index, 1); - next[0] = NAT_NEXT_DROP; - goto trace0; - } - - if (NAT_ED_TRNSL_ERR_SUCCESS != - (translation_error = nat_6t_flow_buf_translate ( - sm, b0, ip0, f, proto0, is_output_feature))) - { - nat_free_session_data (sm, s0, thread_index, 0); - nat_ed_session_delete (sm, s0, thread_index, 1); - next[0] = NAT_NEXT_DROP; - goto trace0; - } - - switch (proto0) - { - case NAT_PROTOCOL_TCP: - vlib_increment_simple_counter (&sm->counters.fastpath.in2out_ed.tcp, - thread_index, sw_if_index0, 1); - nat44_set_tcp_session_state_i2o (sm, now, s0, b0, thread_index); - break; - case NAT_PROTOCOL_UDP: - vlib_increment_simple_counter (&sm->counters.fastpath.in2out_ed.udp, - thread_index, sw_if_index0, 1); - break; - case NAT_PROTOCOL_ICMP: - vlib_increment_simple_counter (&sm->counters.fastpath.in2out_ed.icmp, - thread_index, sw_if_index0, 1); - break; - case NAT_PROTOCOL_OTHER: - vlib_increment_simple_counter ( - &sm->counters.fastpath.in2out_ed.other, thread_index, sw_if_index0, - 1); - break; - } - - /* Accounting */ - nat44_session_update_counters (s0, now, - vlib_buffer_length_in_chain (vm, b0), - thread_index); - /* Per-user LRU list maintenance */ - nat44_session_update_lru (sm, s0, thread_index); - - trace0: - if (PREDICT_FALSE - ((node->flags & VLIB_NODE_FLAG_TRACE) - && (b0->flags & VLIB_BUFFER_IS_TRACED))) - { - nat_in2out_ed_trace_t *t = - vlib_add_trace (vm, node, b0, sizeof (*t)); - t->sw_if_index = sw_if_index0; - t->next_index = next[0]; - t->is_slow_path = 0; - t->translation_error = translation_error; - t->lookup_skipped = lookup_skipped; - clib_memcpy (&t->search_key, &kv0, sizeof (t->search_key)); - - if (s0) - { - t->session_index = s0 - tsm->sessions; - clib_memcpy (&t->i2of, &s0->i2o, sizeof (t->i2of)); - clib_memcpy (&t->o2if, &s0->o2i, sizeof (t->o2if)); - t->translation_via_i2of = (&s0->i2o == f); - } - else - { - t->session_index = ~0; - } - } - - if (next[0] == NAT_NEXT_DROP) - { - vlib_increment_simple_counter (&sm->counters.fastpath. - in2out_ed.drops, thread_index, - sw_if_index0, 1); - } - - n_left_from--; - next++; - } - - vlib_buffer_enqueue_to_next (vm, node, from, (u16 *) nexts, - frame->n_vectors); - return frame->n_vectors; -} - -static inline uword -nat44_ed_in2out_slow_path_node_fn_inline (vlib_main_t * vm, - vlib_node_runtime_t * node, - vlib_frame_t * frame, - int is_output_feature) -{ - u32 n_left_from, *from; - snat_main_t *sm = &snat_main; - f64 now = vlib_time_now (vm); - u32 thread_index = vm->thread_index; - snat_main_per_thread_data_t *tsm = &sm->per_thread_data[thread_index]; - - from = vlib_frame_vector_args (frame); - n_left_from = frame->n_vectors; - - vlib_buffer_t *bufs[VLIB_FRAME_SIZE], **b = bufs; - u16 nexts[VLIB_FRAME_SIZE], *next = nexts; - vlib_get_buffers (vm, from, b, n_left_from); - - while (n_left_from > 0) - { - vlib_buffer_t *b0; - u32 sw_if_index0, rx_fib_index0, iph_offset0 = 0; - nat_protocol_t proto0; - ip4_header_t *ip0; - udp_header_t *udp0; - icmp46_header_t *icmp0; - snat_session_t *s0 = 0; - clib_bihash_kv_16_8_t kv0, value0; - int translation_error = NAT_ED_TRNSL_ERR_SUCCESS; - - b0 = *b; - - if (is_output_feature) - iph_offset0 = vnet_buffer (b0)->ip.reass.save_rewrite_length; - - next[0] = vnet_buffer2 (b0)->nat.arc_next; - - ip0 = (ip4_header_t *) ((u8 *) vlib_buffer_get_current (b0) + - iph_offset0); - - sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX]; - rx_fib_index0 = - fib_table_get_index_for_sw_if_index (FIB_PROTOCOL_IP4, sw_if_index0); - - if (PREDICT_FALSE (ip0->ttl == 1)) - { - vnet_buffer (b0)->sw_if_index[VLIB_TX] = (u32) ~ 0; - icmp4_error_set_vnet_buffer (b0, ICMP4_time_exceeded, - ICMP4_time_exceeded_ttl_exceeded_in_transit, - 0); - next[0] = NAT_NEXT_ICMP_ERROR; - goto trace0; - } - - udp0 = ip4_next_header (ip0); - icmp0 = (icmp46_header_t *) udp0; - proto0 = ip_proto_to_nat_proto (ip0->protocol); - - if (PREDICT_FALSE (proto0 == NAT_PROTOCOL_OTHER)) - { - s0 = nat44_ed_in2out_slowpath_unknown_proto ( - sm, b0, ip0, rx_fib_index0, thread_index, now, vm, node); - if (!s0) - next[0] = NAT_NEXT_DROP; - - if (NAT_ED_TRNSL_ERR_SUCCESS != - (translation_error = nat_6t_flow_buf_translate ( - sm, b0, ip0, &s0->i2o, proto0, is_output_feature))) - { - goto trace0; - } - - vlib_increment_simple_counter (&sm->counters.slowpath. - in2out_ed.other, thread_index, - sw_if_index0, 1); - goto trace0; - } - - if (PREDICT_FALSE (proto0 == NAT_PROTOCOL_ICMP)) - { - next[0] = icmp_in2out_ed_slow_path (sm, b0, ip0, icmp0, sw_if_index0, - rx_fib_index0, node, next[0], - now, thread_index, proto0, &s0); - if (NAT_NEXT_DROP != next[0] && s0 && - NAT_ED_TRNSL_ERR_SUCCESS != - (translation_error = nat_6t_flow_buf_translate ( - sm, b0, ip0, &s0->i2o, proto0, is_output_feature))) - { - goto trace0; - } - - vlib_increment_simple_counter (&sm->counters.slowpath. - in2out_ed.icmp, thread_index, - sw_if_index0, 1); - goto trace0; - } - - init_ed_k (&kv0, ip0->src_address, - vnet_buffer (b0)->ip.reass.l4_src_port, ip0->dst_address, - vnet_buffer (b0)->ip.reass.l4_dst_port, rx_fib_index0, - ip0->protocol); - if (!clib_bihash_search_16_8 (&sm->flow_hash, &kv0, &value0)) - { - ASSERT (thread_index == ed_value_get_thread_index (&value0)); - s0 = - pool_elt_at_index (tsm->sessions, - ed_value_get_session_index (&value0)); - - if (s0->tcp_closed_timestamp && now >= s0->tcp_closed_timestamp) - { - nat_free_session_data (sm, s0, thread_index, 0); - nat_ed_session_delete (sm, s0, thread_index, 1); - s0 = NULL; - } - } - - if (!s0) - { - if (is_output_feature) - { - if (PREDICT_FALSE - (nat44_ed_not_translate_output_feature - (sm, ip0, vnet_buffer (b0)->ip.reass.l4_src_port, - vnet_buffer (b0)->ip.reass.l4_dst_port, thread_index, - sw_if_index0, vnet_buffer (b0)->sw_if_index[VLIB_TX], - now))) - goto trace0; - - /* - * Send DHCP packets to the ipv4 stack, or we won't - * be able to use dhcp client on the outside interface - */ - if (PREDICT_FALSE - (proto0 == NAT_PROTOCOL_UDP - && (vnet_buffer (b0)->ip.reass.l4_dst_port == - clib_host_to_net_u16 (UDP_DST_PORT_dhcp_to_server)) - && ip0->dst_address.as_u32 == 0xffffffff)) - goto trace0; - } - else - { - if (PREDICT_FALSE - (nat44_ed_not_translate - (sm, node, sw_if_index0, ip0, proto0, rx_fib_index0, - thread_index))) - goto trace0; - } - - next[0] = - slow_path_ed (sm, b0, ip0->src_address, ip0->dst_address, - vnet_buffer (b0)->ip.reass.l4_src_port, - vnet_buffer (b0)->ip.reass.l4_dst_port, - ip0->protocol, rx_fib_index0, &s0, node, next[0], - thread_index, now); - - if (PREDICT_FALSE (next[0] == NAT_NEXT_DROP)) - goto trace0; - - if (PREDICT_FALSE (!s0)) - goto trace0; - - } - - b0->flags |= VNET_BUFFER_F_IS_NATED; - - if (NAT_ED_TRNSL_ERR_SUCCESS != - (translation_error = nat_6t_flow_buf_translate ( - sm, b0, ip0, &s0->i2o, proto0, is_output_feature))) - { - nat_free_session_data (sm, s0, thread_index, 0); - nat_ed_session_delete (sm, s0, thread_index, 1); - s0 = NULL; - goto trace0; - } - - if (PREDICT_TRUE (proto0 == NAT_PROTOCOL_TCP)) - { - vlib_increment_simple_counter (&sm->counters.slowpath.in2out_ed.tcp, - thread_index, sw_if_index0, 1); - nat44_set_tcp_session_state_i2o (sm, now, s0, b0, thread_index); - } - else - { - vlib_increment_simple_counter (&sm->counters.slowpath.in2out_ed.udp, - thread_index, sw_if_index0, 1); - } - - /* Accounting */ - nat44_session_update_counters (s0, now, - vlib_buffer_length_in_chain - (vm, b0), thread_index); - /* Per-user LRU list maintenance */ - nat44_session_update_lru (sm, s0, thread_index); - - trace0: - if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE) - && (b0->flags & VLIB_BUFFER_IS_TRACED))) - { - nat_in2out_ed_trace_t *t = - vlib_add_trace (vm, node, b0, sizeof (*t)); - t->sw_if_index = sw_if_index0; - t->next_index = next[0]; - t->is_slow_path = 1; - t->translation_error = translation_error; - clib_memcpy (&t->search_key, &kv0, sizeof (t->search_key)); - - if (s0) - { - t->session_index = s0 - tsm->sessions; - clib_memcpy (&t->i2of, &s0->i2o, sizeof (t->i2of)); - clib_memcpy (&t->o2if, &s0->o2i, sizeof (t->o2if)); - t->translation_via_i2of = 1; - } - - else - { - t->session_index = ~0; - } - } - - if (next[0] == NAT_NEXT_DROP) - { - vlib_increment_simple_counter (&sm->counters.slowpath. - in2out_ed.drops, thread_index, - sw_if_index0, 1); - } - - n_left_from--; - next++; - b++; - } - - vlib_buffer_enqueue_to_next (vm, node, from, (u16 *) nexts, - frame->n_vectors); - - return frame->n_vectors; -} - -VLIB_NODE_FN (nat44_ed_in2out_node) (vlib_main_t * vm, - vlib_node_runtime_t * node, - vlib_frame_t * frame) -{ - return nat44_ed_in2out_fast_path_node_fn_inline (vm, node, frame, 0); -} - -/* *INDENT-OFF* */ -VLIB_REGISTER_NODE (nat44_ed_in2out_node) = { - .name = "nat44-ed-in2out", - .vector_size = sizeof (u32), - .sibling_of = "nat-default", - .format_trace = format_nat_in2out_ed_trace, - .type = VLIB_NODE_TYPE_INTERNAL, - .n_errors = ARRAY_LEN (nat_in2out_ed_error_strings), - .error_strings = nat_in2out_ed_error_strings, - .runtime_data_bytes = sizeof (snat_runtime_t), -}; -/* *INDENT-ON* */ - -VLIB_NODE_FN (nat44_ed_in2out_output_node) (vlib_main_t * vm, - vlib_node_runtime_t * node, - vlib_frame_t * frame) -{ - return nat44_ed_in2out_fast_path_node_fn_inline (vm, node, frame, 1); -} - -/* *INDENT-OFF* */ -VLIB_REGISTER_NODE (nat44_ed_in2out_output_node) = { - .name = "nat44-ed-in2out-output", - .vector_size = sizeof (u32), - .sibling_of = "nat-default", - .format_trace = format_nat_in2out_ed_trace, - .type = VLIB_NODE_TYPE_INTERNAL, - .n_errors = ARRAY_LEN (nat_in2out_ed_error_strings), - .error_strings = nat_in2out_ed_error_strings, - .runtime_data_bytes = sizeof (snat_runtime_t), -}; -/* *INDENT-ON* */ - -VLIB_NODE_FN (nat44_ed_in2out_slowpath_node) (vlib_main_t * vm, - vlib_node_runtime_t * - node, vlib_frame_t * frame) -{ - return nat44_ed_in2out_slow_path_node_fn_inline (vm, node, frame, 0); -} - -/* *INDENT-OFF* */ -VLIB_REGISTER_NODE (nat44_ed_in2out_slowpath_node) = { - .name = "nat44-ed-in2out-slowpath", - .vector_size = sizeof (u32), - .sibling_of = "nat-default", - .format_trace = format_nat_in2out_ed_trace, - .type = VLIB_NODE_TYPE_INTERNAL, - .n_errors = ARRAY_LEN (nat_in2out_ed_error_strings), - .error_strings = nat_in2out_ed_error_strings, - .runtime_data_bytes = sizeof (snat_runtime_t), -}; -/* *INDENT-ON* */ - -VLIB_NODE_FN (nat44_ed_in2out_output_slowpath_node) (vlib_main_t * vm, - vlib_node_runtime_t - * node, - vlib_frame_t * frame) -{ - return nat44_ed_in2out_slow_path_node_fn_inline (vm, node, frame, 1); -} - -/* *INDENT-OFF* */ -VLIB_REGISTER_NODE (nat44_ed_in2out_output_slowpath_node) = { - .name = "nat44-ed-in2out-output-slowpath", - .vector_size = sizeof (u32), - .sibling_of = "nat-default", - .format_trace = format_nat_in2out_ed_trace, - .type = VLIB_NODE_TYPE_INTERNAL, - .n_errors = ARRAY_LEN (nat_in2out_ed_error_strings), - .error_strings = nat_in2out_ed_error_strings, - .runtime_data_bytes = sizeof (snat_runtime_t), -}; -/* *INDENT-ON* */ - -static u8 * -format_nat_pre_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 *); - nat_pre_trace_t *t = va_arg (*args, nat_pre_trace_t *); - return format (s, "in2out next_index %d arc_next_index %d", t->next_index, - t->arc_next_index); -} - -VLIB_NODE_FN (nat_pre_in2out_node) - (vlib_main_t * vm, vlib_node_runtime_t * node, vlib_frame_t * frame) -{ - return nat_pre_node_fn_inline (vm, node, frame, - NAT_NEXT_IN2OUT_ED_FAST_PATH); -} - -VLIB_NODE_FN (nat_pre_in2out_output_node) - (vlib_main_t * vm, vlib_node_runtime_t * node, vlib_frame_t * frame) -{ - return nat_pre_node_fn_inline (vm, node, frame, - NAT_NEXT_IN2OUT_ED_OUTPUT_FAST_PATH); -} - -/* *INDENT-OFF* */ -VLIB_REGISTER_NODE (nat_pre_in2out_node) = { - .name = "nat-pre-in2out", - .vector_size = sizeof (u32), - .sibling_of = "nat-default", - .format_trace = format_nat_pre_trace, - .type = VLIB_NODE_TYPE_INTERNAL, - .n_errors = 0, -}; - -VLIB_REGISTER_NODE (nat_pre_in2out_output_node) = { - .name = "nat-pre-in2out-output", - .vector_size = sizeof (u32), - .sibling_of = "nat-default", - .format_trace = format_nat_pre_trace, - .type = VLIB_NODE_TYPE_INTERNAL, - .n_errors = 0, -}; -/* *INDENT-ON* */ - -/* - * fd.io coding-style-patch-verification: ON - * - * Local Variables: - * eval: (c-set-style "gnu") - * End: - */ diff --git a/src/plugins/nat/lib/lib.c b/src/plugins/nat/lib/lib.c new file mode 100644 index 00000000000..d2def2cc480 --- /dev/null +++ b/src/plugins/nat/lib/lib.c @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2020 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +uword +unformat_nat_protocol (unformat_input_t *input, va_list *args) +{ + u32 *r = va_arg (*args, u32 *); + + if (0) + ; +#define _(N, i, n, s) else if (unformat (input, s)) *r = NAT_PROTOCOL_##N; + foreach_nat_protocol +#undef _ + else return 0; + return 1; +} + +u8 * +format_nat_protocol (u8 *s, va_list *args) +{ + u32 i = va_arg (*args, u32); + u8 *t = 0; + + switch (i) + { +#define _(N, j, n, str) \ + case NAT_PROTOCOL_##N: \ + t = (u8 *) str; \ + break; + foreach_nat_protocol +#undef _ + default : s = format (s, "unknown"); + return s; + } + s = format (s, "%s", t); + return s; +} + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/src/plugins/nat/lib/lib.h b/src/plugins/nat/lib/lib.h index 04bad5c74f7..b0b5229b337 100644 --- a/src/plugins/nat/lib/lib.h +++ b/src/plugins/nat/lib/lib.h @@ -95,7 +95,52 @@ nat_reset_timeouts (nat_timeouts_t * timeouts) timeouts->icmp = NAT_ICMP_TIMEOUT; } -// TODO: move common formating definitions here +static_always_inline u32 +nat_session_get_timeout (nat_timeouts_t *timeouts, nat_protocol_t proto, + u8 state) +{ + switch (proto) + { + case NAT_PROTOCOL_ICMP: + return timeouts->icmp; + case NAT_PROTOCOL_UDP: + return timeouts->udp; + case NAT_PROTOCOL_TCP: + { + if (state) + return timeouts->tcp.transitory; + else + return timeouts->tcp.established; + } + default: + return timeouts->udp; + } + return 0; +} + +static_always_inline u32 +nat_calc_bihash_buckets (u32 n_elts) +{ + n_elts = n_elts / 2.5; + u64 lower_pow2 = 1; + while (lower_pow2 * 2 < n_elts) + { + lower_pow2 = 2 * lower_pow2; + } + u64 upper_pow2 = 2 * lower_pow2; + if ((upper_pow2 - n_elts) < (n_elts - lower_pow2)) + { + if (upper_pow2 <= UINT32_MAX) + { + return upper_pow2; + } + } + return lower_pow2; +} + +u8 *format_nat_protocol (u8 *s, va_list *args); + +uword unformat_nat_protocol (unformat_input_t *input, va_list *args); #endif /* included_nat_lib_h__ */ /* diff --git a/src/plugins/nat/lib/log.h b/src/plugins/nat/lib/log.h new file mode 100644 index 00000000000..26bd93f2589 --- /dev/null +++ b/src/plugins/nat/lib/log.h @@ -0,0 +1,214 @@ +/* + * Copyright (c) 2020 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file + * @brief NAT port/address allocation lib + */ +#ifndef included_nat_log_h__ +#define included_nat_log_h__ + +#include + +#define foreach_nat_log_level \ + _ (0x00, LOG_NONE) \ + _ (0x01, LOG_ERROR) \ + _ (0x02, LOG_WARNING) \ + _ (0x03, LOG_NOTICE) \ + _ (0x04, LOG_INFO) \ + _ (0x05, LOG_DEBUG) + +typedef enum nat_log_level_t_ +{ +#define _(n, f) NAT_##f = n, + foreach_nat_log_level +#undef _ +} nat_log_level_t; + +#define nat_elog(_pm, _level, _str) \ + do \ + { \ + if (PREDICT_FALSE (_pm->log_level >= _level)) \ + { \ + ELOG_TYPE_DECLARE (e) = { \ + .format = "nat-msg " _str, \ + .format_args = "", \ + }; \ + ELOG_DATA (&vlib_global_main.elog_main, e); \ + } \ + } \ + while (0); + +#define nat_elog_addr(_pm, _level, _str, _addr) \ + do \ + { \ + if (PREDICT_FALSE (_pm->log_level >= _level)) \ + { \ + ELOG_TYPE_DECLARE (e) = { \ + .format = "nat-msg " _str " %d.%d.%d.%d", \ + .format_args = "i1i1i1i1", \ + }; \ + CLIB_PACKED (struct { \ + u8 oct1; \ + u8 oct2; \ + u8 oct3; \ + u8 oct4; \ + }) * \ + ed; \ + ed = ELOG_DATA (&vlib_global_main.elog_main, e); \ + ed->oct4 = _addr >> 24; \ + ed->oct3 = _addr >> 16; \ + ed->oct2 = _addr >> 8; \ + ed->oct1 = _addr; \ + } \ + } \ + while (0); + +#define nat_elog_debug_handoff(_pm, _str, _tid, _fib, _src, _dst) \ + do \ + { \ + if (PREDICT_FALSE (_pm->log_level >= NAT_LOG_DEBUG)) \ + { \ + ELOG_TYPE_DECLARE (e) = { \ + .format = "nat-msg " _str " ip src: %d.%d.%d.%d dst: %d.%d.%d.%d" \ + " tid from: %d to: %d fib: %d", \ + .format_args = "i1i1i1i1i1i1i1i1i4i4i4", \ + }; \ + CLIB_PACKED (struct { \ + u8 src_oct1; \ + u8 src_oct2; \ + u8 src_oct3; \ + u8 src_oct4; \ + u8 dst_oct1; \ + u8 dst_oct2; \ + u8 dst_oct3; \ + u8 dst_oct4; \ + u32 ftid; \ + u32 ttid; \ + u32 fib; \ + }) * \ + ed; \ + ed = ELOG_DATA (&vlib_global_main.elog_main, e); \ + ed->src_oct1 = _src >> 24; \ + ed->src_oct2 = _src >> 16; \ + ed->src_oct3 = _src >> 8; \ + ed->src_oct4 = _src; \ + ed->dst_oct1 = _dst >> 24; \ + ed->dst_oct2 = _dst >> 16; \ + ed->dst_oct3 = _dst >> 8; \ + ed->dst_oct4 = _dst; \ + ed->ftid = vlib_get_thread_index (); \ + ed->ttid = _tid; \ + ed->fib = _fib; \ + } \ + } \ + while (0); + +#define nat_elog_debug_handoff_v2(_pm, _str, _prt, _fib, _src, _dst) \ + do \ + { \ + if (PREDICT_FALSE (_pm->log_level >= NAT_LOG_DEBUG)) \ + { \ + ELOG_TYPE_DECLARE (e) = { \ + .format = \ + "nat-msg " _str " ip_src:%d.%d.%d.%d ip_dst:%d.%d.%d.%d" \ + " tid:%d prt:%d fib:%d", \ + .format_args = "i1i1i1i1i1i1i1i1i4i4i4", \ + }; \ + CLIB_PACKED (struct { \ + u8 src_oct1; \ + u8 src_oct2; \ + u8 src_oct3; \ + u8 src_oct4; \ + u8 dst_oct1; \ + u8 dst_oct2; \ + u8 dst_oct3; \ + u8 dst_oct4; \ + u32 tid; \ + u32 prt; \ + u32 fib; \ + }) * \ + ed; \ + ed = ELOG_DATA (&vlib_global_main.elog_main, e); \ + ed->src_oct1 = _src >> 24; \ + ed->src_oct2 = _src >> 16; \ + ed->src_oct3 = _src >> 8; \ + ed->src_oct4 = _src; \ + ed->dst_oct1 = _dst >> 24; \ + ed->dst_oct2 = _dst >> 16; \ + ed->dst_oct3 = _dst >> 8; \ + ed->dst_oct4 = _dst; \ + ed->tid = vlib_get_thread_index (); \ + ed->prt = _prt; \ + ed->fib = _fib; \ + } \ + } \ + while (0); + +#define nat_elog_X1(_pm, _level, _fmt, _arg, _val1) \ + do \ + { \ + if (PREDICT_FALSE (_pm->log_level >= _level)) \ + { \ + ELOG_TYPE_DECLARE (e) = { \ + .format = "nat-msg " _fmt, \ + .format_args = _arg, \ + }; \ + CLIB_PACKED (struct { typeof (_val1) val1; }) * ed; \ + ed = ELOG_DATA (&vlib_global_main.elog_main, e); \ + ed->val1 = _val1; \ + } \ + } \ + while (0); + +#define nat_elog_notice(_pm, nat_elog_str) \ + nat_elog (_pm, NAT_LOG_INFO, "[notice] " nat_elog_str) +#define nat_elog_warn(_pm, nat_elog_str) \ + nat_elog (_pm, NAT_LOG_WARNING, "[warning] " nat_elog_str) +#define nat_elog_err(_pm, nat_elog_str) \ + nat_elog (_pm, NAT_LOG_ERROR, "[error] " nat_elog_str) +#define nat_elog_debug(_pm, nat_elog_str) \ + nat_elog (_pm, NAT_LOG_DEBUG, "[debug] " nat_elog_str) +#define nat_elog_info(_pm, nat_elog_str) \ + nat_elog (_pm, NAT_LOG_INFO, "[info] " nat_elog_str) + +#define nat_elog_notice_X1(_pm, nat_elog_fmt_str, nat_elog_fmt_arg, \ + nat_elog_val1) \ + nat_elog_X1 (_pm, NAT_LOG_NOTICE, "[notice] " nat_elog_fmt_str, \ + nat_elog_fmt_arg, nat_elog_val1) +#define nat_elog_warn_X1(_pm, nat_elog_fmt_str, nat_elog_fmt_arg, \ + nat_elog_val1) \ + nat_elog_X1 (_pm, NAT_LOG_WARNING, "[warning] " nat_elog_fmt_str, \ + nat_elog_fmt_arg, nat_elog_val1) +#define nat_elog_err_X1(_pm, nat_elog_fmt_str, nat_elog_fmt_arg, \ + nat_elog_val1) \ + nat_elog_X1 (_pm, NAT_LOG_ERROR, "[error] " nat_elog_fmt_str, \ + nat_elog_fmt_arg, nat_elog_val1) +#define nat_elog_debug_X1(_pm, nat_elog_fmt_str, nat_elog_fmt_arg, \ + nat_elog_val1) \ + nat_elog_X1 (_pm, NAT_LOG_DEBUG, "[debug] " nat_elog_fmt_str, \ + nat_elog_fmt_arg, nat_elog_val1) +#define nat_elog_info_X1(_pm, nat_elog_fmt_str, nat_elog_fmt_arg, \ + nat_elog_val1) \ + nat_elog_X1 (_pm, NAT_LOG_INFO, "[info] " nat_elog_fmt_str, \ + nat_elog_fmt_arg, nat_elog_val1) + +#endif /* included_nat_lib_h__ */ +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/src/plugins/nat/lib/nat_inlines.h b/src/plugins/nat/lib/nat_inlines.h index c75b8043cc1..0bd7993ac04 100644 --- a/src/plugins/nat/lib/nat_inlines.h +++ b/src/plugins/nat/lib/nat_inlines.h @@ -77,6 +77,20 @@ mss_clamping (u16 mss_clamping, tcp_header_t * tcp, ip_csum_t * sum) } } +static_always_inline u16 +nat_random_port (u32 *random_seed, u16 min, u16 max) +{ + u32 rwide; + u16 r; + + rwide = random_u32 (random_seed); + r = rwide & 0xFFFF; + if (r >= min && r <= max) + return r; + + return min + (rwide % (max - min + 1)); +} + #endif /* __included_lib_nat_inlines_h__ */ /* diff --git a/src/plugins/nat/lib/nat_types.api b/src/plugins/nat/lib/nat_types.api new file mode 100644 index 00000000000..ad4ee9faba0 --- /dev/null +++ b/src/plugins/nat/lib/nat_types.api @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2020 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +option version = "0.0.1"; + +typedef nat_timeouts +{ + u32 udp; + u32 tcp_established; + u32 tcp_transitory; + u32 icmp; +}; + +enum nat_log_level : u8 +{ + NAT_LOG_NONE = 0x00, + NAT_LOG_ERROR = 0x01, + NAT_LOG_WARNING = 0x02, + NAT_LOG_NOTICE = 0x03, + NAT_LOG_INFO = 0x04, + NAT_LOG_DEBUG = 0x05, +}; + +enum nat_config_flags : u8 +{ + NAT_IS_NONE = 0x00, + NAT_IS_TWICE_NAT = 0x01, + NAT_IS_SELF_TWICE_NAT = 0x02, + NAT_IS_OUT2IN_ONLY = 0x04, + NAT_IS_ADDR_ONLY = 0x08, + NAT_IS_OUTSIDE = 0x10, + NAT_IS_INSIDE = 0x20, + NAT_IS_STATIC = 0x40, + NAT_IS_EXT_HOST_VALID = 0x80, +}; diff --git a/src/plugins/nat/nat.c b/src/plugins/nat/nat.c deleted file mode 100644 index 85d4775c8fe..00000000000 --- a/src/plugins/nat/nat.c +++ /dev/null @@ -1,4246 +0,0 @@ -/* - * snat.c - simple nat plugin - * - * Copyright (c) 2016 Cisco and/or its affiliates. - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include -#include - -snat_main_t snat_main; - -#define skip_if_disabled() \ - do \ - { \ - snat_main_t *sm = &snat_main; \ - if (PREDICT_FALSE (!sm->enabled)) \ - return; \ - } \ - while (0) - -#define fail_if_enabled() \ - do \ - { \ - snat_main_t *sm = &snat_main; \ - if (PREDICT_FALSE (sm->enabled)) \ - { \ - nat_log_err ("plugin enabled"); \ - return 1; \ - } \ - } \ - while (0) - -#define fail_if_disabled() \ - do \ - { \ - snat_main_t *sm = &snat_main; \ - if (PREDICT_FALSE (!sm->enabled)) \ - { \ - nat_log_err ("plugin disabled"); \ - return 1; \ - } \ - } \ - while (0) - -/* *INDENT-OFF* */ -/* Hook up input features */ -VNET_FEATURE_INIT (nat_pre_in2out, static) = { - .arc_name = "ip4-unicast", - .node_name = "nat-pre-in2out", - .runs_after = VNET_FEATURES ("acl-plugin-in-ip4-fa", - "ip4-sv-reassembly-feature"), -}; -VNET_FEATURE_INIT (nat_pre_out2in, static) = { - .arc_name = "ip4-unicast", - .node_name = "nat-pre-out2in", - .runs_after = VNET_FEATURES ("acl-plugin-in-ip4-fa", - "ip4-dhcp-client-detect", - "ip4-sv-reassembly-feature"), -}; -VNET_FEATURE_INIT (snat_in2out_worker_handoff, static) = { - .arc_name = "ip4-unicast", - .node_name = "nat44-in2out-worker-handoff", - .runs_after = VNET_FEATURES ("acl-plugin-in-ip4-fa"), -}; -VNET_FEATURE_INIT (snat_out2in_worker_handoff, static) = { - .arc_name = "ip4-unicast", - .node_name = "nat44-out2in-worker-handoff", - .runs_after = VNET_FEATURES ("acl-plugin-in-ip4-fa", - "ip4-dhcp-client-detect"), -}; -VNET_FEATURE_INIT (ip4_snat_in2out, static) = { - .arc_name = "ip4-unicast", - .node_name = "nat44-in2out", - .runs_after = VNET_FEATURES ("acl-plugin-in-ip4-fa","ip4-sv-reassembly-feature"), -}; -VNET_FEATURE_INIT (ip4_snat_out2in, static) = { - .arc_name = "ip4-unicast", - .node_name = "nat44-out2in", - .runs_after = VNET_FEATURES ("acl-plugin-in-ip4-fa","ip4-sv-reassembly-feature", - "ip4-dhcp-client-detect"), -}; -VNET_FEATURE_INIT (ip4_nat_classify, static) = { - .arc_name = "ip4-unicast", - .node_name = "nat44-classify", - .runs_after = VNET_FEATURES ("acl-plugin-in-ip4-fa","ip4-sv-reassembly-feature"), -}; -VNET_FEATURE_INIT (ip4_nat44_ed_in2out, static) = { - .arc_name = "ip4-unicast", - .node_name = "nat44-ed-in2out", - .runs_after = VNET_FEATURES ("acl-plugin-in-ip4-fa","ip4-sv-reassembly-feature"), -}; -VNET_FEATURE_INIT (ip4_nat44_ed_out2in, static) = { - .arc_name = "ip4-unicast", - .node_name = "nat44-ed-out2in", - .runs_after = VNET_FEATURES ("acl-plugin-in-ip4-fa","ip4-sv-reassembly-feature", - "ip4-dhcp-client-detect"), -}; -VNET_FEATURE_INIT (ip4_nat44_ed_classify, static) = { - .arc_name = "ip4-unicast", - .node_name = "nat44-ed-classify", - .runs_after = VNET_FEATURES ("acl-plugin-in-ip4-fa","ip4-sv-reassembly-feature"), -}; -VNET_FEATURE_INIT (ip4_nat_handoff_classify, static) = { - .arc_name = "ip4-unicast", - .node_name = "nat44-handoff-classify", - .runs_after = VNET_FEATURES ("acl-plugin-in-ip4-fa","ip4-sv-reassembly-feature"), -}; -VNET_FEATURE_INIT (ip4_snat_in2out_fast, static) = { - .arc_name = "ip4-unicast", - .node_name = "nat44-in2out-fast", - .runs_after = VNET_FEATURES ("acl-plugin-in-ip4-fa","ip4-sv-reassembly-feature"), -}; -VNET_FEATURE_INIT (ip4_snat_out2in_fast, static) = { - .arc_name = "ip4-unicast", - .node_name = "nat44-out2in-fast", - .runs_after = VNET_FEATURES ("acl-plugin-in-ip4-fa","ip4-sv-reassembly-feature", - "ip4-dhcp-client-detect"), -}; -VNET_FEATURE_INIT (ip4_snat_hairpin_dst, static) = { - .arc_name = "ip4-unicast", - .node_name = "nat44-hairpin-dst", - .runs_after = VNET_FEATURES ("acl-plugin-in-ip4-fa","ip4-sv-reassembly-feature"), -}; - -/* Hook up output features */ -VNET_FEATURE_INIT (ip4_snat_in2out_output, static) = { - .arc_name = "ip4-output", - .node_name = "nat44-in2out-output", - .runs_after = VNET_FEATURES ("acl-plugin-out-ip4-fa","ip4-sv-reassembly-output-feature"), -}; -VNET_FEATURE_INIT (ip4_snat_in2out_output_worker_handoff, static) = { - .arc_name = "ip4-output", - .node_name = "nat44-in2out-output-worker-handoff", - .runs_after = VNET_FEATURES ("acl-plugin-out-ip4-fa","ip4-sv-reassembly-output-feature"), -}; -VNET_FEATURE_INIT (ip4_snat_hairpin_src, static) = { - .arc_name = "ip4-output", - .node_name = "nat44-hairpin-src", - .runs_after = VNET_FEATURES ("acl-plugin-out-ip4-fa","ip4-sv-reassembly-output-feature"), -}; -VNET_FEATURE_INIT (nat_pre_in2out_output, static) = { - .arc_name = "ip4-output", - .node_name = "nat-pre-in2out-output", - .runs_after = VNET_FEATURES ("ip4-sv-reassembly-output-feature"), - .runs_before = VNET_FEATURES ("acl-plugin-out-ip4-fa"), -}; -VNET_FEATURE_INIT (ip4_nat44_ed_in2out_output, static) = { - .arc_name = "ip4-output", - .node_name = "nat44-ed-in2out-output", - .runs_after = VNET_FEATURES ("ip4-sv-reassembly-output-feature"), - .runs_before = VNET_FEATURES ("acl-plugin-out-ip4-fa"), -}; - -/* Hook up ip4-local features */ -VNET_FEATURE_INIT (ip4_nat_hairpinning, static) = -{ - .arc_name = "ip4-local", - .node_name = "nat44-hairpinning", - .runs_before = VNET_FEATURES("ip4-local-end-of-arc"), -}; - -VLIB_PLUGIN_REGISTER () = { - .version = VPP_BUILD_VER, - .description = "Network Address Translation (NAT)", -}; -/* *INDENT-ON* */ - -static void nat44_ed_db_init (u32 translations, u32 translation_buckets, - u32 user_buckets); - -static void nat44_ed_db_free (); - -static u32 -nat44_ed_get_worker_out2in_cb (vlib_buffer_t * b, ip4_header_t * ip, - u32 rx_fib_index, u8 is_output); - -static u32 -nat44_ed_get_worker_in2out_cb (ip4_header_t * ip, u32 rx_fib_index, - u8 is_output); - -u32 nat_calc_bihash_buckets (u32 n_elts); - -u8 * -format_session_kvp (u8 * s, va_list * args) -{ - clib_bihash_kv_8_8_t *v = va_arg (*args, clib_bihash_kv_8_8_t *); - - s = format (s, "%U thread-index %llu session-index %llu", format_snat_key, - v->key, nat_value_get_thread_index (v), - nat_value_get_session_index (v)); - - return s; -} - -u8 * -format_static_mapping_kvp (u8 * s, va_list * args) -{ - clib_bihash_kv_8_8_t *v = va_arg (*args, clib_bihash_kv_8_8_t *); - - s = format (s, "%U static-mapping-index %llu", - format_snat_key, v->key, v->value); - - return s; -} - -u8 * -format_user_kvp (u8 * s, va_list * args) -{ - clib_bihash_kv_8_8_t *v = va_arg (*args, clib_bihash_kv_8_8_t *); - snat_user_key_t k; - - k.as_u64 = v->key; - - s = format (s, "%U fib %d user-index %llu", format_ip4_address, &k.addr, - k.fib_index, v->value); - - return s; -} - -u8 * -format_ed_session_kvp (u8 * s, va_list * args) -{ - clib_bihash_kv_16_8_t *v = va_arg (*args, clib_bihash_kv_16_8_t *); - - u8 proto; - u16 r_port, l_port; - ip4_address_t l_addr, r_addr; - u32 fib_index; - - split_ed_kv (v, &l_addr, &r_addr, &proto, &fib_index, &l_port, &r_port); - s = format (s, - "local %U:%d remote %U:%d proto %U fib %d thread-index %u " - "session-index %u", - format_ip4_address, &l_addr, clib_net_to_host_u16 (l_port), - format_ip4_address, &r_addr, clib_net_to_host_u16 (r_port), - format_ip_protocol, proto, fib_index, - ed_value_get_thread_index (v), ed_value_get_session_index (v)); - - return s; -} - -void -nat_free_session_data (snat_main_t * sm, snat_session_t * s, u32 thread_index, - u8 is_ha) -{ - clib_bihash_kv_8_8_t kv; - - if (is_ed_session (s)) - { - per_vrf_sessions_unregister_session (s, thread_index); - - if (nat_ed_ses_i2o_flow_hash_add_del (sm, thread_index, s, 0)) - nat_elog_warn ("flow hash del failed"); - - if (nat_ed_ses_o2i_flow_hash_add_del (sm, thread_index, s, 0)) - nat_elog_warn ("flow hash del failed"); - } - - if (is_fwd_bypass_session (s)) - { - return; - } - - /* session lookup tables */ - if (is_ed_session (s)) - { - if (is_affinity_sessions (s)) - nat_affinity_unlock (s->ext_host_addr, s->out2in.addr, - s->nat_proto, s->out2in.port); - - if (!is_ha) - nat_syslog_nat44_sdel (s->user_index, s->in2out.fib_index, - &s->in2out.addr, s->in2out.port, - &s->ext_host_nat_addr, s->ext_host_nat_port, - &s->out2in.addr, s->out2in.port, - &s->ext_host_addr, s->ext_host_port, - s->nat_proto, is_twice_nat_session (s)); - } - else - { - init_nat_i2o_k (&kv, s); - if (clib_bihash_add_del_8_8 (&sm->in2out, &kv, 0)) - nat_elog_warn ("in2out key del failed"); - init_nat_o2i_k (&kv, s); - if (clib_bihash_add_del_8_8 (&sm->out2in, &kv, 0)) - nat_elog_warn ("out2in key del failed"); - - if (!is_ha) - nat_syslog_nat44_apmdel (s->user_index, s->in2out.fib_index, - &s->in2out.addr, s->in2out.port, - &s->out2in.addr, s->out2in.port, - s->nat_proto); - } - - if (snat_is_unk_proto_session (s)) - return; - - if (!is_ha) - { - /* log NAT event */ - nat_ipfix_logging_nat44_ses_delete (thread_index, - s->in2out.addr.as_u32, - s->out2in.addr.as_u32, - s->nat_proto, - s->in2out.port, - s->out2in.port, - s->in2out.fib_index); - - nat_ha_sdel (&s->out2in.addr, s->out2in.port, &s->ext_host_addr, - s->ext_host_port, s->nat_proto, s->out2in.fib_index, - thread_index); - } - - /* Twice NAT address and port for external host */ - if (is_twice_nat_session (s)) - { - snat_free_outside_address_and_port (sm->twice_nat_addresses, - thread_index, - &s->ext_host_nat_addr, - s->ext_host_nat_port, s->nat_proto); - } - - if (snat_is_session_static (s)) - return; - - snat_free_outside_address_and_port (sm->addresses, thread_index, - &s->out2in.addr, s->out2in.port, - s->nat_proto); -} - -snat_user_t * -nat_user_get_or_create (snat_main_t * sm, ip4_address_t * addr, u32 fib_index, - u32 thread_index) -{ - snat_user_t *u = 0; - snat_user_key_t user_key; - clib_bihash_kv_8_8_t kv, value; - snat_main_per_thread_data_t *tsm = &sm->per_thread_data[thread_index]; - dlist_elt_t *per_user_list_head_elt; - - user_key.addr.as_u32 = addr->as_u32; - user_key.fib_index = fib_index; - kv.key = user_key.as_u64; - - /* Ever heard of the "user" = src ip4 address before? */ - if (clib_bihash_search_8_8 (&tsm->user_hash, &kv, &value)) - { - if (pool_elts (tsm->users) >= sm->max_users_per_thread) - { - vlib_increment_simple_counter (&sm->user_limit_reached, - thread_index, 0, 1); - nat_elog_warn ("maximum user limit reached"); - return NULL; - } - /* no, make a new one */ - pool_get (tsm->users, u); - clib_memset (u, 0, sizeof (*u)); - - u->addr.as_u32 = addr->as_u32; - u->fib_index = fib_index; - - pool_get (tsm->list_pool, per_user_list_head_elt); - - u->sessions_per_user_list_head_index = per_user_list_head_elt - - tsm->list_pool; - - clib_dlist_init (tsm->list_pool, u->sessions_per_user_list_head_index); - - kv.value = u - tsm->users; - - /* add user */ - if (clib_bihash_add_del_8_8 (&tsm->user_hash, &kv, 1)) - { - nat_elog_warn ("user_hash key add failed"); - nat44_delete_user_with_no_session (sm, u, thread_index); - return NULL; - } - - vlib_set_simple_counter (&sm->total_users, thread_index, 0, - pool_elts (tsm->users)); - } - else - { - u = pool_elt_at_index (tsm->users, value.value); - } - - return u; -} - -// only NAT EI -snat_session_t * -nat_session_alloc_or_recycle (snat_main_t * sm, snat_user_t * u, - u32 thread_index, f64 now) -{ - snat_session_t *s; - snat_main_per_thread_data_t *tsm = &sm->per_thread_data[thread_index]; - u32 oldest_per_user_translation_list_index, session_index; - dlist_elt_t *oldest_per_user_translation_list_elt; - dlist_elt_t *per_user_translation_list_elt; - - /* Over quota? Recycle the least recently used translation */ - if ((u->nsessions + u->nstaticsessions) >= sm->max_translations_per_user) - { - oldest_per_user_translation_list_index = - clib_dlist_remove_head (tsm->list_pool, - u->sessions_per_user_list_head_index); - - ASSERT (oldest_per_user_translation_list_index != ~0); - - /* Add it back to the end of the LRU list */ - clib_dlist_addtail (tsm->list_pool, - u->sessions_per_user_list_head_index, - oldest_per_user_translation_list_index); - /* Get the list element */ - oldest_per_user_translation_list_elt = - pool_elt_at_index (tsm->list_pool, - oldest_per_user_translation_list_index); - - /* Get the session index from the list element */ - session_index = oldest_per_user_translation_list_elt->value; - - /* Get the session */ - s = pool_elt_at_index (tsm->sessions, session_index); - - // TODO: ONLY EI version should be called - nat_free_session_data (sm, s, thread_index, 0); - if (snat_is_session_static (s)) - u->nstaticsessions--; - else - u->nsessions--; - s->flags = 0; - s->total_bytes = 0; - s->total_pkts = 0; - s->state = 0; - s->ext_host_addr.as_u32 = 0; - s->ext_host_port = 0; - s->ext_host_nat_addr.as_u32 = 0; - s->ext_host_nat_port = 0; - } - else - { - pool_get (tsm->sessions, s); - clib_memset (s, 0, sizeof (*s)); - - /* Create list elts */ - pool_get (tsm->list_pool, per_user_translation_list_elt); - clib_dlist_init (tsm->list_pool, - per_user_translation_list_elt - tsm->list_pool); - - per_user_translation_list_elt->value = s - tsm->sessions; - s->per_user_index = per_user_translation_list_elt - tsm->list_pool; - s->per_user_list_head_index = u->sessions_per_user_list_head_index; - - clib_dlist_addtail (tsm->list_pool, - s->per_user_list_head_index, - per_user_translation_list_elt - tsm->list_pool); - - s->user_index = u - tsm->users; - vlib_set_simple_counter (&sm->total_sessions, thread_index, 0, - pool_elts (tsm->sessions)); - } - - s->ha_last_refreshed = now; - - return s; -} - -void -snat_add_del_addr_to_fib (ip4_address_t * addr, u8 p_len, u32 sw_if_index, - int is_add) -{ - snat_main_t *sm = &snat_main; - fib_prefix_t prefix = { - .fp_len = p_len, - .fp_proto = FIB_PROTOCOL_IP4, - .fp_addr = { - .ip4.as_u32 = addr->as_u32, - }, - }; - u32 fib_index = ip4_fib_table_get_index_for_sw_if_index (sw_if_index); - - if (is_add) - fib_table_entry_update_one_path (fib_index, - &prefix, - sm->fib_src_low, - (FIB_ENTRY_FLAG_CONNECTED | - FIB_ENTRY_FLAG_LOCAL | - FIB_ENTRY_FLAG_EXCLUSIVE), - DPO_PROTO_IP4, - NULL, - sw_if_index, - ~0, 1, NULL, FIB_ROUTE_PATH_FLAG_NONE); - else - fib_table_entry_delete (fib_index, &prefix, sm->fib_src_low); -} - -int -snat_add_address (snat_main_t * sm, ip4_address_t * addr, u32 vrf_id, - u8 twice_nat) -{ - snat_address_t *ap; - snat_interface_t *i; - vlib_thread_main_t *tm = vlib_get_thread_main (); - - if (twice_nat && !sm->endpoint_dependent) - { - nat_log_err ("unsupported"); - return VNET_API_ERROR_UNSUPPORTED; - } - - /* Check if address already exists */ - /* *INDENT-OFF* */ - vec_foreach (ap, twice_nat ? sm->twice_nat_addresses : sm->addresses) - { - if (ap->addr.as_u32 == addr->as_u32) - { - nat_log_err ("address exist"); - return VNET_API_ERROR_VALUE_EXIST; - } - } - /* *INDENT-ON* */ - - if (twice_nat) - vec_add2 (sm->twice_nat_addresses, ap, 1); - else - vec_add2 (sm->addresses, ap, 1); - - ap->addr = *addr; - if (vrf_id != ~0) - ap->fib_index = - fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP4, vrf_id, - sm->fib_src_low); - else - ap->fib_index = ~0; - - /* *INDENT-OFF* */ - #define _(N, i, n, s) \ - clib_memset(ap->busy_##n##_port_refcounts, 0, sizeof(ap->busy_##n##_port_refcounts));\ - ap->busy_##n##_ports = 0; \ - ap->busy_##n##_ports_per_thread = 0;\ - vec_validate_init_empty (ap->busy_##n##_ports_per_thread, tm->n_vlib_mains - 1, 0); - foreach_nat_protocol - #undef _ - /* *INDENT-ON* */ - - if (twice_nat) - return 0; - - /* Add external address to FIB */ - /* *INDENT-OFF* */ - pool_foreach (i, sm->interfaces) - { - if (nat_interface_is_inside(i) || sm->out2in_dpo) - continue; - - snat_add_del_addr_to_fib(addr, 32, i->sw_if_index, 1); - break; - } - pool_foreach (i, sm->output_feature_interfaces) - { - if (nat_interface_is_inside(i) || sm->out2in_dpo) - continue; - - snat_add_del_addr_to_fib(addr, 32, i->sw_if_index, 1); - break; - } - /* *INDENT-ON* */ - - return 0; -} - -static int -is_snat_address_used_in_static_mapping (snat_main_t * sm, ip4_address_t addr) -{ - snat_static_mapping_t *m; - /* *INDENT-OFF* */ - pool_foreach (m, sm->static_mappings) - { - if (is_addr_only_static_mapping (m) || - is_out2in_only_static_mapping (m) || - is_identity_static_mapping (m)) - continue; - if (m->external_addr.as_u32 == addr.as_u32) - return 1; - } - /* *INDENT-ON* */ - - return 0; -} - -static void -snat_add_static_mapping_when_resolved (snat_main_t *sm, ip4_address_t l_addr, - u16 l_port, u32 sw_if_index, u16 e_port, - u32 vrf_id, nat_protocol_t proto, - int addr_only, u8 *tag, int twice_nat, - int out2in_only, int identity_nat, - ip4_address_t pool_addr, int exact) -{ - snat_static_map_resolve_t *rp; - - vec_add2 (sm->to_resolve, rp, 1); - rp->l_addr.as_u32 = l_addr.as_u32; - rp->l_port = l_port; - rp->sw_if_index = sw_if_index; - rp->e_port = e_port; - rp->vrf_id = vrf_id; - rp->proto = proto; - rp->addr_only = addr_only; - rp->twice_nat = twice_nat; - rp->out2in_only = out2in_only; - rp->identity_nat = identity_nat; - rp->tag = vec_dup (tag); - rp->pool_addr = pool_addr; - rp->exact = exact; -} - -u32 -get_thread_idx_by_port (u16 e_port) -{ - snat_main_t *sm = &snat_main; - u32 thread_idx = sm->num_workers; - if (sm->num_workers > 1) - { - thread_idx = - sm->first_worker_index + - sm->workers[(e_port - 1024) / sm->port_per_thread]; - } - return thread_idx; -} - -void -nat_ed_static_mapping_del_sessions (snat_main_t * sm, - snat_main_per_thread_data_t * tsm, - ip4_address_t l_addr, - u16 l_port, - u8 protocol, - u32 fib_index, int addr_only, - ip4_address_t e_addr, u16 e_port) -{ - snat_session_t *s; - u32 *indexes_to_free = NULL; - /* *INDENT-OFF* */ - pool_foreach (s, tsm->sessions) { - if (s->in2out.fib_index != fib_index || - s->in2out.addr.as_u32 != l_addr.as_u32) - { - continue; - } - if (!addr_only) - { - if ((s->out2in.addr.as_u32 != e_addr.as_u32) || - s->out2in.port != e_port || - s->in2out.port != l_port || - s->nat_proto != protocol) - continue; - } - - if (is_lb_session (s)) - continue; - if (!snat_is_session_static (s)) - continue; - nat_free_session_data (sm, s, tsm - sm->per_thread_data, 0); - vec_add1 (indexes_to_free, s - tsm->sessions); - if (!addr_only) - break; - } - /* *INDENT-ON* */ - u32 *ses_index; - vec_foreach (ses_index, indexes_to_free) - { - s = pool_elt_at_index (tsm->sessions, *ses_index); - nat_ed_session_delete (sm, s, tsm - sm->per_thread_data, 1); - } - vec_free (indexes_to_free); -} - -static_always_inline int -nat44_ed_add_del_static_mapping (ip4_address_t l_addr, ip4_address_t e_addr, - u16 l_port, u16 e_port, u32 vrf_id, - int addr_only, u32 sw_if_index, - nat_protocol_t proto, int is_add, - twice_nat_type_t twice_nat, u8 out2in_only, - u8 *tag, u8 identity_nat, - ip4_address_t pool_addr, int exact) -{ - snat_main_t *sm = &snat_main; - snat_static_mapping_t *m; - clib_bihash_kv_8_8_t kv, value; - snat_address_t *a = 0; - u32 fib_index = ~0; - snat_interface_t *interface; - int i; - snat_main_per_thread_data_t *tsm; - snat_user_key_t u_key; - snat_user_t *u; - dlist_elt_t *head, *elt; - u32 elt_index, head_index; - u32 ses_index; - u64 user_index; - snat_session_t *s; - snat_static_map_resolve_t *rp, *rp_match = 0; - nat44_lb_addr_port_t *local; - u32 find = ~0; - - if (!sm->endpoint_dependent) - { - if (twice_nat || out2in_only) - return VNET_API_ERROR_UNSUPPORTED; - } - - /* If the external address is a specific interface address */ - if (sw_if_index != ~0) - { - ip4_address_t *first_int_addr; - - for (i = 0; i < vec_len (sm->to_resolve); i++) - { - rp = sm->to_resolve + i; - if (rp->sw_if_index != sw_if_index || - rp->l_addr.as_u32 != l_addr.as_u32 || - rp->vrf_id != vrf_id || rp->addr_only != addr_only) - continue; - - if (!addr_only) - { - if ((rp->l_port != l_port && rp->e_port != e_port) - || rp->proto != proto) - continue; - } - - rp_match = rp; - break; - } - - /* Might be already set... */ - first_int_addr = ip4_interface_first_address - (sm->ip4_main, sw_if_index, 0 /* just want the address */ ); - - if (is_add) - { - if (rp_match) - return VNET_API_ERROR_VALUE_EXIST; - - snat_add_static_mapping_when_resolved ( - sm, l_addr, l_port, sw_if_index, e_port, vrf_id, proto, addr_only, - tag, twice_nat, out2in_only, identity_nat, pool_addr, exact); - - /* DHCP resolution required? */ - if (first_int_addr == 0) - { - return 0; - } - else - { - e_addr.as_u32 = first_int_addr->as_u32; - /* Identity mapping? */ - if (l_addr.as_u32 == 0) - l_addr.as_u32 = e_addr.as_u32; - } - } - else - { - if (!rp_match) - return VNET_API_ERROR_NO_SUCH_ENTRY; - - vec_del1 (sm->to_resolve, i); - - if (first_int_addr) - { - e_addr.as_u32 = first_int_addr->as_u32; - /* Identity mapping? */ - if (l_addr.as_u32 == 0) - l_addr.as_u32 = e_addr.as_u32; - } - else - return 0; - } - } - - init_nat_k (&kv, e_addr, addr_only ? 0 : e_port, 0, addr_only ? 0 : proto); - if (clib_bihash_search_8_8 (&sm->static_mapping_by_external, &kv, &value)) - m = 0; - else - m = pool_elt_at_index (sm->static_mappings, value.value); - - if (is_add) - { - if (m) - { - if (is_identity_static_mapping (m)) - { - /* *INDENT-OFF* */ - pool_foreach (local, m->locals) - { - if (local->vrf_id == vrf_id) - return VNET_API_ERROR_VALUE_EXIST; - } - /* *INDENT-ON* */ - 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, - sm->fib_src_low); - init_nat_kv (&kv, m->local_addr, m->local_port, local->fib_index, - m->proto, 0, 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) - fib_index = - fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP4, vrf_id, - sm->fib_src_low); - /* If not specified use inside VRF id from SNAT plugin startup config */ - else - { - fib_index = sm->inside_fib_index; - vrf_id = sm->inside_vrf_id; - fib_table_lock (fib_index, FIB_PROTOCOL_IP4, sm->fib_src_low); - } - - if (!(out2in_only || identity_nat)) - { - init_nat_k (&kv, l_addr, addr_only ? 0 : l_port, fib_index, - addr_only ? 0 : proto); - if (!clib_bihash_search_8_8 - (&sm->static_mapping_by_local, &kv, &value)) - return VNET_API_ERROR_VALUE_EXIST; - } - - /* Find external address in allocated addresses and reserve port for - address and port pair mapping when dynamic translations enabled */ - if (!(addr_only || sm->static_mapping_only || out2in_only)) - { - for (i = 0; i < vec_len (sm->addresses); i++) - { - if (sm->addresses[i].addr.as_u32 == e_addr.as_u32) - { - a = sm->addresses + i; - /* External port must be unused */ - switch (proto) - { -#define _(N, j, n, s) \ - case NAT_PROTOCOL_##N: \ - if (a->busy_##n##_port_refcounts[e_port]) \ - return VNET_API_ERROR_INVALID_VALUE; \ - ++a->busy_##n##_port_refcounts[e_port]; \ - if (e_port > 1024) \ - { \ - a->busy_##n##_ports++; \ - a->busy_##n##_ports_per_thread[get_thread_idx_by_port(e_port)]++; \ - } \ - break; - foreach_nat_protocol -#undef _ - default: - nat_elog_info ("unknown protocol"); - return VNET_API_ERROR_INVALID_VALUE_2; - } - break; - } - } - /* External address must be allocated */ - if (!a && (l_addr.as_u32 != e_addr.as_u32)) - { - if (sw_if_index != ~0) - { - for (i = 0; i < vec_len (sm->to_resolve); i++) - { - rp = sm->to_resolve + i; - if (rp->addr_only) - continue; - if (rp->sw_if_index != sw_if_index && - rp->l_addr.as_u32 != l_addr.as_u32 && - rp->vrf_id != vrf_id && rp->l_port != l_port && - rp->e_port != e_port && rp->proto != proto) - continue; - - vec_del1 (sm->to_resolve, i); - break; - } - } - return VNET_API_ERROR_NO_SUCH_ENTRY; - } - } - - pool_get (sm->static_mappings, m); - clib_memset (m, 0, sizeof (*m)); - m->tag = vec_dup (tag); - m->local_addr = l_addr; - m->external_addr = e_addr; - m->twice_nat = twice_nat; - - if (twice_nat == TWICE_NAT && exact) - { - m->flags |= NAT_STATIC_MAPPING_FLAG_EXACT_ADDRESS; - m->pool_addr = pool_addr; - } - - 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; - pool_get (m->locals, local); - 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; - m->external_port = e_port; - m->proto = proto; - } - - if (sm->num_workers > 1) - { - ip4_header_t ip = { - .src_address = m->local_addr, - }; - vec_add1 (m->workers, sm->worker_in2out_cb (&ip, m->fib_index, 0)); - tsm = vec_elt_at_index (sm->per_thread_data, m->workers[0]); - } - else - tsm = vec_elt_at_index (sm->per_thread_data, sm->num_workers); - - if (!out2in_only) - { - init_nat_kv (&kv, m->local_addr, m->local_port, fib_index, m->proto, - 0, m - sm->static_mappings); - clib_bihash_add_del_8_8 (&sm->static_mapping_by_local, &kv, 1); - } - - init_nat_kv (&kv, m->external_addr, m->external_port, 0, m->proto, 0, - m - sm->static_mappings); - clib_bihash_add_del_8_8 (&sm->static_mapping_by_external, &kv, 1); - - /* Delete dynamic sessions matching local address (+ local port) */ - // TODO: based on type of NAT EI/ED - if (!(sm->static_mapping_only)) - { - u_key.addr = m->local_addr; - u_key.fib_index = m->fib_index; - kv.key = u_key.as_u64; - if (!clib_bihash_search_8_8 (&tsm->user_hash, &kv, &value)) - { - user_index = value.value; - u = pool_elt_at_index (tsm->users, user_index); - if (u->nsessions) - { - 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 (snat_is_session_static (s)) - continue; - - if (!addr_only && s->in2out.port != m->local_port) - continue; - - nat_free_session_data (sm, s, - tsm - sm->per_thread_data, 0); - nat44_delete_session (sm, s, tsm - sm->per_thread_data); - - if (!addr_only && !sm->endpoint_dependent) - break; - } - } - } - } - } - else - { - if (!m) - { - if (sw_if_index != ~0) - return 0; - else - return VNET_API_ERROR_NO_SUCH_ENTRY; - } - - if (identity_nat) - { - if (vrf_id == ~0) - vrf_id = sm->inside_vrf_id; - - /* *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; - - local = pool_elt_at_index (m->locals, find); - fib_index = local->fib_index; - pool_put (m->locals, local); - } - else - fib_index = m->fib_index; - - /* Free external address port */ - if (!(addr_only || sm->static_mapping_only || out2in_only)) - { - for (i = 0; i < vec_len (sm->addresses); i++) - { - if (sm->addresses[i].addr.as_u32 == e_addr.as_u32) - { - a = sm->addresses + i; - switch (proto) - { -#define _(N, j, n, s) \ - case NAT_PROTOCOL_##N: \ - --a->busy_##n##_port_refcounts[e_port]; \ - if (e_port > 1024) \ - { \ - a->busy_##n##_ports--; \ - a->busy_##n##_ports_per_thread[get_thread_idx_by_port(e_port)]--; \ - } \ - break; - foreach_nat_protocol -#undef _ - default: - nat_elog_info ("unknown protocol"); - return VNET_API_ERROR_INVALID_VALUE_2; - } - break; - } - } - } - - if (sm->num_workers > 1) - tsm = vec_elt_at_index (sm->per_thread_data, m->workers[0]); - else - tsm = vec_elt_at_index (sm->per_thread_data, sm->num_workers); - - init_nat_k (&kv, m->local_addr, m->local_port, fib_index, m->proto); - if (!out2in_only) - clib_bihash_add_del_8_8 (&sm->static_mapping_by_local, &kv, 0); - - /* Delete session(s) for static mapping if exist */ - if (!(sm->static_mapping_only) || - (sm->static_mapping_only && sm->static_mapping_connection_tracking)) - { - if (sm->endpoint_dependent) - { - nat_ed_static_mapping_del_sessions (sm, tsm, m->local_addr, - m->local_port, m->proto, - fib_index, addr_only, - e_addr, e_port); - } - else - { - u_key.addr = m->local_addr; - u_key.fib_index = fib_index; - kv.key = u_key.as_u64; - nat44_ei_static_mapping_del_sessions (sm, tsm, u_key, addr_only, - e_addr, e_port); - } - } - - fib_table_unlock (fib_index, FIB_PROTOCOL_IP4, sm->fib_src_low); - if (pool_elts (m->locals)) - return 0; - - init_nat_k (&kv, m->external_addr, m->external_port, 0, m->proto); - 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 */ - pool_put (sm->static_mappings, m); - } - - if (!addr_only || (l_addr.as_u32 == e_addr.as_u32)) - return 0; - - /* Add/delete external address to FIB */ - /* *INDENT-OFF* */ - pool_foreach (interface, sm->interfaces) - { - if (nat_interface_is_inside(interface) || sm->out2in_dpo) - continue; - - snat_add_del_addr_to_fib(&e_addr, 32, interface->sw_if_index, is_add); - break; - } - pool_foreach (interface, sm->output_feature_interfaces) - { - if (nat_interface_is_inside(interface) || sm->out2in_dpo) - continue; - - snat_add_del_addr_to_fib(&e_addr, 32, interface->sw_if_index, is_add); - break; - } - /* *INDENT-ON* */ - - return 0; -} - -int -snat_add_static_mapping (ip4_address_t l_addr, ip4_address_t e_addr, - u16 l_port, u16 e_port, u32 vrf_id, int addr_only, - u32 sw_if_index, nat_protocol_t proto, int is_add, - twice_nat_type_t twice_nat, u8 out2in_only, u8 *tag, - u8 identity_nat, ip4_address_t pool_addr, int exact) -{ - snat_main_t *sm = &snat_main; - int rv; - - if (sm->endpoint_dependent) - { - rv = nat44_ed_add_del_static_mapping ( - l_addr, e_addr, l_port, e_port, vrf_id, addr_only, sw_if_index, proto, - is_add, twice_nat, out2in_only, tag, identity_nat, pool_addr, exact); - } - else - { - rv = nat44_ei_add_del_static_mapping ( - l_addr, e_addr, l_port, e_port, proto, sw_if_index, vrf_id, addr_only, - identity_nat, tag, is_add); - } - return rv; -} - -int -nat44_add_del_lb_static_mapping (ip4_address_t e_addr, u16 e_port, - nat_protocol_t proto, - nat44_lb_addr_port_t * locals, u8 is_add, - twice_nat_type_t twice_nat, u8 out2in_only, - u8 * tag, u32 affinity) -{ - snat_main_t *sm = &snat_main; - snat_static_mapping_t *m; - clib_bihash_kv_8_8_t kv, value; - snat_address_t *a = 0; - int i; - nat44_lb_addr_port_t *local; - snat_main_per_thread_data_t *tsm; - snat_session_t *s; - uword *bitmap = 0; - - if (!sm->endpoint_dependent) - return VNET_API_ERROR_UNSUPPORTED; - - init_nat_k (&kv, e_addr, e_port, 0, proto); - if (clib_bihash_search_8_8 (&sm->static_mapping_by_external, &kv, &value)) - m = 0; - else - m = pool_elt_at_index (sm->static_mappings, value.value); - - if (is_add) - { - if (m) - return VNET_API_ERROR_VALUE_EXIST; - - if (vec_len (locals) < 2) - return VNET_API_ERROR_INVALID_VALUE; - - /* Find external address in allocated addresses and reserve port for - address and port pair mapping when dynamic translations enabled */ - if (!(sm->static_mapping_only || out2in_only)) - { - for (i = 0; i < vec_len (sm->addresses); i++) - { - if (sm->addresses[i].addr.as_u32 == e_addr.as_u32) - { - a = sm->addresses + i; - /* External port must be unused */ - switch (proto) - { -#define _(N, j, n, s) \ - case NAT_PROTOCOL_##N: \ - if (a->busy_##n##_port_refcounts[e_port]) \ - return VNET_API_ERROR_INVALID_VALUE; \ - ++a->busy_##n##_port_refcounts[e_port]; \ - if (e_port > 1024) \ - { \ - a->busy_##n##_ports++; \ - a->busy_##n##_ports_per_thread[get_thread_idx_by_port(e_port)]++; \ - } \ - break; - foreach_nat_protocol -#undef _ - default: - nat_elog_info ("unknown protocol"); - return VNET_API_ERROR_INVALID_VALUE_2; - } - break; - } - } - /* External address must be allocated */ - if (!a) - return VNET_API_ERROR_NO_SUCH_ENTRY; - } - - pool_get (sm->static_mappings, m); - clib_memset (m, 0, sizeof (*m)); - m->tag = vec_dup (tag); - m->external_addr = e_addr; - 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; - - if (affinity) - m->affinity_per_service_list_head_index = - nat_affinity_get_per_service_list_head_index (); - else - m->affinity_per_service_list_head_index = ~0; - - init_nat_kv (&kv, m->external_addr, m->external_port, 0, m->proto, 0, - m - sm->static_mappings); - if (clib_bihash_add_del_8_8 (&sm->static_mapping_by_external, &kv, 1)) - { - nat_elog_err ("static_mapping_by_external key add failed"); - return VNET_API_ERROR_UNSPECIFIED; - } - - for (i = 0; i < vec_len (locals); i++) - { - locals[i].fib_index = - fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP4, - locals[i].vrf_id, - sm->fib_src_low); - if (!out2in_only) - { - init_nat_kv (&kv, locals[i].addr, locals[i].port, - locals[i].fib_index, m->proto, 0, - m - sm->static_mappings); - clib_bihash_add_del_8_8 (&sm->static_mapping_by_local, &kv, 1); - } - locals[i].prefix = (i == 0) ? locals[i].probability : - (locals[i - 1].prefix + locals[i].probability); - pool_get (m->locals, local); - *local = locals[i]; - if (sm->num_workers > 1) - { - ip4_header_t ip = { - .src_address = locals[i].addr, - }; - bitmap = - clib_bitmap_set (bitmap, - sm->worker_in2out_cb (&ip, m->fib_index, 0), - 1); - } - } - - /* Assign workers */ - if (sm->num_workers > 1) - { - /* *INDENT-OFF* */ - clib_bitmap_foreach (i, bitmap) - { - vec_add1(m->workers, i); - } - /* *INDENT-ON* */ - } - } - else - { - 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)) - { - for (i = 0; i < vec_len (sm->addresses); i++) - { - if (sm->addresses[i].addr.as_u32 == e_addr.as_u32) - { - a = sm->addresses + i; - switch (proto) - { -#define _(N, j, n, s) \ - case NAT_PROTOCOL_##N: \ - --a->busy_##n##_port_refcounts[e_port]; \ - if (e_port > 1024) \ - { \ - a->busy_##n##_ports--; \ - a->busy_##n##_ports_per_thread[get_thread_idx_by_port(e_port)]--; \ - } \ - break; - foreach_nat_protocol -#undef _ - default: - nat_elog_info ("unknown protocol"); - return VNET_API_ERROR_INVALID_VALUE_2; - } - break; - } - } - } - - init_nat_k (&kv, m->external_addr, m->external_port, 0, m->proto); - if (clib_bihash_add_del_8_8 (&sm->static_mapping_by_external, &kv, 0)) - { - nat_elog_err ("static_mapping_by_external key del failed"); - return VNET_API_ERROR_UNSPECIFIED; - } - - /* *INDENT-OFF* */ - pool_foreach (local, m->locals) - { - fib_table_unlock (local->fib_index, FIB_PROTOCOL_IP4, - sm->fib_src_low); - if (!out2in_only) - { -init_nat_k(& kv, local->addr, local->port, local->fib_index, m->proto); - if (clib_bihash_add_del_8_8(&sm->static_mapping_by_local, &kv, 0)) - { - nat_elog_err ("static_mapping_by_local key del failed"); - return VNET_API_ERROR_UNSPECIFIED; - } - } - - 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, 0)); - } - else - tsm = vec_elt_at_index (sm->per_thread_data, sm->num_workers); - - /* Delete sessions */ - pool_foreach (s, tsm->sessions) { - if (!(is_lb_session (s))) - continue; - - if ((s->in2out.addr.as_u32 != local->addr.as_u32) || - s->in2out.port != local->port) - continue; - - nat_free_session_data (sm, s, tsm - sm->per_thread_data, 0); - nat_ed_session_delete (sm, s, tsm - sm->per_thread_data, 1); - } - } - /* *INDENT-ON* */ - if (m->affinity) - nat_affinity_flush_service (m->affinity_per_service_list_head_index); - pool_free (m->locals); - vec_free (m->tag); - vec_free (m->workers); - - pool_put (sm->static_mappings, m); - } - - return 0; -} - -int -nat44_lb_static_mapping_add_del_local (ip4_address_t e_addr, u16 e_port, - ip4_address_t l_addr, u16 l_port, - nat_protocol_t proto, u32 vrf_id, - u8 probability, u8 is_add) -{ - snat_main_t *sm = &snat_main; - snat_static_mapping_t *m = 0; - 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_session_t *s; - u32 *locals = 0; - uword *bitmap = 0; - int i; - - if (!sm->endpoint_dependent) - return VNET_API_ERROR_FEATURE_DISABLED; - - init_nat_k (&kv, e_addr, e_port, 0, proto); - 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, - sm->fib_src_low); - - if (!is_out2in_only_static_mapping (m)) - { - init_nat_kv (&kv, l_addr, l_port, local->fib_index, proto, 0, - m - sm->static_mappings); - if (clib_bihash_add_del_8_8 (&sm->static_mapping_by_local, &kv, 1)) - nat_elog_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, - sm->fib_src_low); - - if (!is_out2in_only_static_mapping (m)) - { - init_nat_k (&kv, l_addr, l_port, match_local->fib_index, proto); - if (clib_bihash_add_del_8_8 (&sm->static_mapping_by_local, &kv, 0)) - nat_elog_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, - 0)); - } - else - tsm = vec_elt_at_index (sm->per_thread_data, sm->num_workers); - - /* Delete sessions */ - /* *INDENT-OFF* */ - pool_foreach (s, tsm->sessions) { - if (!(is_lb_session (s))) - continue; - - if ((s->in2out.addr.as_u32 != match_local->addr.as_u32) || - s->in2out.port != match_local->port) - continue; - - nat_free_session_data (sm, s, tsm - sm->per_thread_data, 0); - nat_ed_session_delete (sm, s, tsm - sm->per_thread_data, 1); - } - /* *INDENT-ON* */ - - 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, 0), - 1); - } - } - /* *INDENT-ON* */ - - ASSERT (vec_len (locals) > 1); - - 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) -{ - snat_address_t *a = 0; - snat_session_t *ses; - u32 *ses_to_be_removed = 0, *ses_index; - snat_main_per_thread_data_t *tsm; - snat_static_mapping_t *m; - snat_interface_t *interface; - int i; - snat_address_t *addresses = - twice_nat ? sm->twice_nat_addresses : sm->addresses; - - /* Find SNAT address */ - for (i = 0; i < vec_len (addresses); i++) - { - if (addresses[i].addr.as_u32 == addr.as_u32) - { - a = addresses + i; - break; - } - } - if (!a) - { - nat_log_err ("no such address"); - return VNET_API_ERROR_NO_SUCH_ENTRY; - } - - if (delete_sm) - { - ip4_address_t pool_addr = { 0 }; - /* *INDENT-OFF* */ - pool_foreach (m, sm->static_mappings) - { - if (m->external_addr.as_u32 == addr.as_u32) - (void) snat_add_static_mapping (m->local_addr, m->external_addr, - m->local_port, m->external_port, - m->vrf_id, - is_addr_only_static_mapping(m), ~0, - m->proto, 0 /* is_add */, - m->twice_nat, - is_out2in_only_static_mapping(m), - m->tag, - is_identity_static_mapping(m), - pool_addr, 0); - } - /* *INDENT-ON* */ - } - else - { - /* Check if address is used in some static mapping */ - if (is_snat_address_used_in_static_mapping (sm, addr)) - { - nat_log_err ("address used in static mapping"); - return VNET_API_ERROR_UNSPECIFIED; - } - } - - if (a->fib_index != ~0) - fib_table_unlock (a->fib_index, FIB_PROTOCOL_IP4, sm->fib_src_low); - - /* Delete sessions using address */ - if (a->busy_tcp_ports || a->busy_udp_ports || a->busy_icmp_ports) - { - vec_foreach (tsm, sm->per_thread_data) - { - /* *INDENT-OFF* */ - pool_foreach (ses, tsm->sessions) { - if (ses->out2in.addr.as_u32 == addr.as_u32) - { - nat_free_session_data (sm, ses, tsm - sm->per_thread_data, 0); - vec_add1 (ses_to_be_removed, ses - tsm->sessions); - } - } - /* *INDENT-ON* */ - - if (sm->endpoint_dependent) - { - vec_foreach (ses_index, ses_to_be_removed) - { - ses = pool_elt_at_index (tsm->sessions, ses_index[0]); - nat_ed_session_delete (sm, ses, tsm - sm->per_thread_data, 1); - } - } - else - { - vec_foreach (ses_index, ses_to_be_removed) - { - ses = pool_elt_at_index (tsm->sessions, ses_index[0]); - nat44_delete_session (sm, ses, tsm - sm->per_thread_data); - } - } - - vec_free (ses_to_be_removed); - } - } - -#define _(N, i, n, s) \ - vec_free (a->busy_##n##_ports_per_thread); - foreach_nat_protocol -#undef _ - - if (twice_nat) - { - vec_del1 (sm->twice_nat_addresses, i); - return 0; - } - else vec_del1 (sm->addresses, i); - - /* Delete external address from FIB */ - pool_foreach (interface, sm->interfaces) - { - if (nat_interface_is_inside (interface) || sm->out2in_dpo) - continue; - - snat_add_del_addr_to_fib (&addr, 32, interface->sw_if_index, 0); - break; - } - - pool_foreach (interface, sm->output_feature_interfaces) - { - if (nat_interface_is_inside(interface) || sm->out2in_dpo) - continue; - - snat_add_del_addr_to_fib(&addr, 32, interface->sw_if_index, 0); - break; - } - - return 0; -} - -static void -nat_validate_counters (snat_main_t * sm, u32 sw_if_index) -{ -#define _(x) \ - vlib_validate_simple_counter (&sm->counters.fastpath.in2out.x, \ - sw_if_index); \ - vlib_zero_simple_counter (&sm->counters.fastpath.in2out.x, sw_if_index); \ - vlib_validate_simple_counter (&sm->counters.fastpath.out2in.x, \ - sw_if_index); \ - vlib_zero_simple_counter (&sm->counters.fastpath.out2in.x, sw_if_index); \ - vlib_validate_simple_counter (&sm->counters.slowpath.in2out.x, \ - sw_if_index); \ - vlib_zero_simple_counter (&sm->counters.slowpath.in2out.x, sw_if_index); \ - vlib_validate_simple_counter (&sm->counters.slowpath.out2in.x, \ - sw_if_index); \ - vlib_zero_simple_counter (&sm->counters.slowpath.out2in.x, sw_if_index); \ - vlib_validate_simple_counter (&sm->counters.fastpath.in2out_ed.x, \ - sw_if_index); \ - vlib_zero_simple_counter (&sm->counters.fastpath.in2out_ed.x, sw_if_index); \ - vlib_validate_simple_counter (&sm->counters.fastpath.out2in_ed.x, \ - sw_if_index); \ - vlib_zero_simple_counter (&sm->counters.fastpath.out2in_ed.x, sw_if_index); \ - vlib_validate_simple_counter (&sm->counters.slowpath.in2out_ed.x, \ - sw_if_index); \ - vlib_zero_simple_counter (&sm->counters.slowpath.in2out_ed.x, sw_if_index); \ - vlib_validate_simple_counter (&sm->counters.slowpath.out2in_ed.x, \ - sw_if_index); \ - vlib_zero_simple_counter (&sm->counters.slowpath.out2in_ed.x, sw_if_index); - foreach_nat_counter; -#undef _ - vlib_validate_simple_counter (&sm->counters.hairpinning, sw_if_index); - vlib_zero_simple_counter (&sm->counters.hairpinning, sw_if_index); -} - -void -expire_per_vrf_sessions (u32 fib_index) -{ - per_vrf_sessions_t *per_vrf_sessions; - snat_main_per_thread_data_t *tsm; - snat_main_t *sm = &snat_main; - - /* *INDENT-OFF* */ - vec_foreach (tsm, sm->per_thread_data) - { - vec_foreach (per_vrf_sessions, tsm->per_vrf_sessions_vec) - { - if ((per_vrf_sessions->rx_fib_index == fib_index) || - (per_vrf_sessions->tx_fib_index == fib_index)) - { - per_vrf_sessions->expired = 1; - } - } - } - /* *INDENT-ON* */ -} - -void -update_per_vrf_sessions_vec (u32 fib_index, int is_del) -{ - snat_main_t *sm = &snat_main; - nat_fib_t *fib; - - // we don't care if it is outside/inside fib - // we just care about their ref_count - // if it reaches 0 sessions should expire - // because the fib isn't valid for NAT anymore - - vec_foreach (fib, sm->fibs) - { - if (fib->fib_index == fib_index) - { - if (is_del) - { - fib->ref_count--; - if (!fib->ref_count) - { - vec_del1 (sm->fibs, fib - sm->fibs); - expire_per_vrf_sessions (fib_index); - } - return; - } - else - fib->ref_count++; - } - } - if (!is_del) - { - vec_add2 (sm->fibs, fib, 1); - fib->ref_count = 1; - fib->fib_index = fib_index; - } -} - -int -snat_interface_add_del (u32 sw_if_index, u8 is_inside, int is_del) -{ - snat_main_t *sm = &snat_main; - snat_interface_t *i; - const char *feature_name, *del_feature_name; - snat_address_t *ap; - snat_static_mapping_t *m; - nat_outside_fib_t *outside_fib; - u32 fib_index = fib_table_get_index_for_sw_if_index (FIB_PROTOCOL_IP4, - sw_if_index); - - if (!sm->enabled) - { - nat_log_err ("nat44 is disabled"); - return VNET_API_ERROR_UNSUPPORTED; - } - - if (sm->out2in_dpo && !is_inside) - { - nat_log_err ("error unsupported"); - return VNET_API_ERROR_UNSUPPORTED; - } - - /* *INDENT-OFF* */ - pool_foreach (i, sm->output_feature_interfaces) - { - if (i->sw_if_index == sw_if_index) - { - nat_log_err ("error interface already configured"); - return VNET_API_ERROR_VALUE_EXIST; - } - } - /* *INDENT-ON* */ - - if (sm->static_mapping_only && !(sm->static_mapping_connection_tracking)) - feature_name = is_inside ? "nat44-in2out-fast" : "nat44-out2in-fast"; - else - { - if (sm->num_workers > 1) - feature_name = - is_inside ? "nat44-in2out-worker-handoff" : - "nat44-out2in-worker-handoff"; - else if (sm->endpoint_dependent) - { - feature_name = is_inside ? "nat-pre-in2out" : "nat-pre-out2in"; - } - else - feature_name = is_inside ? "nat44-in2out" : "nat44-out2in"; - } - - ASSERT (sm->frame_queue_nelts > 0); - - if (sm->fq_in2out_index == ~0 && sm->num_workers > 1) - sm->fq_in2out_index = vlib_frame_queue_main_init (sm->in2out_node_index, - sm->frame_queue_nelts); - - if (sm->fq_out2in_index == ~0 && sm->num_workers > 1) - sm->fq_out2in_index = vlib_frame_queue_main_init (sm->out2in_node_index, - sm->frame_queue_nelts); - - if (sm->endpoint_dependent) - update_per_vrf_sessions_vec (fib_index, is_del); - - if (!is_inside) - { - /* *INDENT-OFF* */ - vec_foreach (outside_fib, sm->outside_fibs) - { - if (outside_fib->fib_index == fib_index) - { - if (is_del) - { - outside_fib->refcount--; - if (!outside_fib->refcount) - vec_del1 (sm->outside_fibs, outside_fib - sm->outside_fibs); - } - else - outside_fib->refcount++; - goto feature_set; - } - } - /* *INDENT-ON* */ - if (!is_del) - { - vec_add2 (sm->outside_fibs, outside_fib, 1); - outside_fib->refcount = 1; - outside_fib->fib_index = fib_index; - } - } - -feature_set: - /* *INDENT-OFF* */ - pool_foreach (i, sm->interfaces) - { - if (i->sw_if_index == sw_if_index) - { - if (is_del) - { - if (nat_interface_is_inside(i) && nat_interface_is_outside(i)) - { - if (is_inside) - i->flags &= ~NAT_INTERFACE_FLAG_IS_INSIDE; - else - i->flags &= ~NAT_INTERFACE_FLAG_IS_OUTSIDE; - - if (sm->num_workers > 1) - { - del_feature_name = "nat44-handoff-classify"; - feature_name = !is_inside ? "nat44-in2out-worker-handoff" : - "nat44-out2in-worker-handoff"; - } - else if (sm->endpoint_dependent) - { - del_feature_name = "nat44-ed-classify"; - feature_name = !is_inside ? "nat-pre-in2out" : - "nat-pre-out2in"; - } - else - { - del_feature_name = "nat44-classify"; - feature_name = !is_inside ? "nat44-in2out" : "nat44-out2in"; - } - - int rv = ip4_sv_reass_enable_disable_with_refcnt (sw_if_index, 0); - if (rv) - return rv; - vnet_feature_enable_disable ("ip4-unicast", del_feature_name, - sw_if_index, 0, 0, 0); - vnet_feature_enable_disable ("ip4-unicast", feature_name, - sw_if_index, 1, 0, 0); - if (!is_inside) - { - if (!sm->endpoint_dependent) - vnet_feature_enable_disable ("ip4-local", - "nat44-hairpinning", - sw_if_index, 1, 0, 0); - } - } - else - { - int rv = ip4_sv_reass_enable_disable_with_refcnt (sw_if_index, 0); - if (rv) - return rv; - vnet_feature_enable_disable ("ip4-unicast", feature_name, - sw_if_index, 0, 0, 0); - pool_put (sm->interfaces, i); - if (is_inside) - { - if (!sm->endpoint_dependent) - vnet_feature_enable_disable ("ip4-local", - "nat44-hairpinning", - sw_if_index, 0, 0, 0); - } - } - } - else - { - if ((nat_interface_is_inside (i) && is_inside) || - (nat_interface_is_outside (i) && !is_inside)) - return 0; - - if (sm->num_workers > 1) - { - del_feature_name = !is_inside ? "nat44-in2out-worker-handoff" : - "nat44-out2in-worker-handoff"; - feature_name = "nat44-handoff-classify"; - } - else if (sm->endpoint_dependent) - { - del_feature_name = - !is_inside ? "nat-pre-in2out" : "nat-pre-out2in"; - - feature_name = "nat44-ed-classify"; - } - else - { - del_feature_name = - !is_inside ? "nat44-in2out" : "nat44-out2in"; - feature_name = "nat44-classify"; - } - - int rv = ip4_sv_reass_enable_disable_with_refcnt (sw_if_index, 1); - if (rv) - return rv; - vnet_feature_enable_disable ("ip4-unicast", del_feature_name, - sw_if_index, 0, 0, 0); - vnet_feature_enable_disable ("ip4-unicast", feature_name, - sw_if_index, 1, 0, 0); - if (!is_inside) - { - if (!sm->endpoint_dependent) - vnet_feature_enable_disable ( - "ip4-local", "nat44-hairpinning", sw_if_index, 0, 0, 0); - } - goto set_flags; - } - - goto fib; - } - } - /* *INDENT-ON* */ - - if (is_del) - { - nat_log_err ("error interface couldn't be found"); - return VNET_API_ERROR_NO_SUCH_ENTRY; - } - - pool_get (sm->interfaces, i); - i->sw_if_index = sw_if_index; - i->flags = 0; - nat_validate_counters (sm, sw_if_index); - - vnet_feature_enable_disable ("ip4-unicast", feature_name, sw_if_index, 1, 0, - 0); - - int rv = ip4_sv_reass_enable_disable_with_refcnt (sw_if_index, 1); - if (rv) - return rv; - - if (is_inside && !sm->out2in_dpo) - { - if (!sm->endpoint_dependent) - vnet_feature_enable_disable ("ip4-local", "nat44-hairpinning", - sw_if_index, 1, 0, 0); - } - -set_flags: - if (is_inside) - { - i->flags |= NAT_INTERFACE_FLAG_IS_INSIDE; - return 0; - } - else - i->flags |= NAT_INTERFACE_FLAG_IS_OUTSIDE; - - /* Add/delete external addresses to FIB */ -fib: - /* *INDENT-OFF* */ - vec_foreach (ap, sm->addresses) - snat_add_del_addr_to_fib(&ap->addr, 32, sw_if_index, !is_del); - - pool_foreach (m, sm->static_mappings) - { - if (!(is_addr_only_static_mapping(m)) || (m->local_addr.as_u32 == m->external_addr.as_u32)) - continue; - - snat_add_del_addr_to_fib(&m->external_addr, 32, sw_if_index, !is_del); - } - /* *INDENT-ON* */ - - return 0; -} - -int -snat_interface_add_del_output_feature (u32 sw_if_index, - u8 is_inside, int is_del) -{ - snat_main_t *sm = &snat_main; - snat_interface_t *i; - snat_address_t *ap; - snat_static_mapping_t *m; - nat_outside_fib_t *outside_fib; - u32 fib_index = fib_table_get_index_for_sw_if_index (FIB_PROTOCOL_IP4, - sw_if_index); - - if (!sm->enabled) - { - nat_log_err ("nat44 is disabled"); - return VNET_API_ERROR_UNSUPPORTED; - } - - if (sm->static_mapping_only && !(sm->static_mapping_connection_tracking)) - { - nat_log_err ("error unsupported"); - return VNET_API_ERROR_UNSUPPORTED; - } - - /* *INDENT-OFF* */ - pool_foreach (i, sm->interfaces) - { - if (i->sw_if_index == sw_if_index) - { - nat_log_err ("error interface already configured"); - return VNET_API_ERROR_VALUE_EXIST; - } - } - /* *INDENT-ON* */ - - if (sm->endpoint_dependent) - update_per_vrf_sessions_vec (fib_index, is_del); - - if (!is_inside) - { - /* *INDENT-OFF* */ - vec_foreach (outside_fib, sm->outside_fibs) - { - if (outside_fib->fib_index == fib_index) - { - if (is_del) - { - outside_fib->refcount--; - if (!outside_fib->refcount) - vec_del1 (sm->outside_fibs, outside_fib - sm->outside_fibs); - } - else - outside_fib->refcount++; - goto feature_set; - } - } - /* *INDENT-ON* */ - if (!is_del) - { - vec_add2 (sm->outside_fibs, outside_fib, 1); - outside_fib->refcount = 1; - outside_fib->fib_index = fib_index; - } - } - -feature_set: - if (is_inside) - { - if (sm->endpoint_dependent) - { - int rv = - ip4_sv_reass_enable_disable_with_refcnt (sw_if_index, !is_del); - if (rv) - return rv; - rv = - ip4_sv_reass_output_enable_disable_with_refcnt (sw_if_index, - !is_del); - if (rv) - return rv; - } - else - { - int rv = - ip4_sv_reass_enable_disable_with_refcnt (sw_if_index, !is_del); - if (rv) - return rv; - rv = - ip4_sv_reass_output_enable_disable_with_refcnt (sw_if_index, - !is_del); - if (rv) - return rv; - vnet_feature_enable_disable ("ip4-unicast", "nat44-hairpin-dst", - sw_if_index, !is_del, 0, 0); - vnet_feature_enable_disable ("ip4-output", "nat44-hairpin-src", - sw_if_index, !is_del, 0, 0); - } - goto fq; - } - - if (sm->num_workers > 1) - { - int rv = ip4_sv_reass_enable_disable_with_refcnt (sw_if_index, !is_del); - if (rv) - return rv; - rv = - ip4_sv_reass_output_enable_disable_with_refcnt (sw_if_index, !is_del); - if (rv) - return rv; - vnet_feature_enable_disable ("ip4-unicast", - "nat44-out2in-worker-handoff", - sw_if_index, !is_del, 0, 0); - vnet_feature_enable_disable ("ip4-output", - "nat44-in2out-output-worker-handoff", - sw_if_index, !is_del, 0, 0); - } - else - { - if (sm->endpoint_dependent) - { - int rv = - ip4_sv_reass_enable_disable_with_refcnt (sw_if_index, !is_del); - if (rv) - return rv; - rv = - ip4_sv_reass_output_enable_disable_with_refcnt (sw_if_index, - !is_del); - if (rv) - return rv; - vnet_feature_enable_disable ("ip4-unicast", "nat-pre-out2in", - sw_if_index, !is_del, 0, 0); - vnet_feature_enable_disable ("ip4-output", "nat-pre-in2out-output", - sw_if_index, !is_del, 0, 0); - } - else - { - int rv = - ip4_sv_reass_enable_disable_with_refcnt (sw_if_index, !is_del); - if (rv) - return rv; - rv = - ip4_sv_reass_output_enable_disable_with_refcnt (sw_if_index, - !is_del); - if (rv) - return rv; - vnet_feature_enable_disable ("ip4-unicast", "nat44-out2in", - sw_if_index, !is_del, 0, 0); - vnet_feature_enable_disable ("ip4-output", "nat44-in2out-output", - sw_if_index, !is_del, 0, 0); - } - } - -fq: - if (sm->fq_in2out_output_index == ~0 && sm->num_workers > 1) - sm->fq_in2out_output_index = - vlib_frame_queue_main_init (sm->in2out_output_node_index, 0); - - if (sm->fq_out2in_index == ~0 && sm->num_workers > 1) - sm->fq_out2in_index = - vlib_frame_queue_main_init (sm->out2in_node_index, 0); - - /* *INDENT-OFF* */ - pool_foreach (i, sm->output_feature_interfaces) - { - if (i->sw_if_index == sw_if_index) - { - if (is_del) - pool_put (sm->output_feature_interfaces, i); - else - return VNET_API_ERROR_VALUE_EXIST; - - goto fib; - } - } - /* *INDENT-ON* */ - - if (is_del) - { - nat_log_err ("error interface couldn't be found"); - return VNET_API_ERROR_NO_SUCH_ENTRY; - } - - pool_get (sm->output_feature_interfaces, i); - i->sw_if_index = sw_if_index; - i->flags = 0; - nat_validate_counters (sm, sw_if_index); - if (is_inside) - i->flags |= NAT_INTERFACE_FLAG_IS_INSIDE; - else - i->flags |= NAT_INTERFACE_FLAG_IS_OUTSIDE; - - /* Add/delete external addresses to FIB */ -fib: - if (is_inside) - return 0; - - /* *INDENT-OFF* */ - vec_foreach (ap, sm->addresses) - snat_add_del_addr_to_fib(&ap->addr, 32, sw_if_index, !is_del); - - pool_foreach (m, sm->static_mappings) - { - if (!((is_addr_only_static_mapping(m))) || (m->local_addr.as_u32 == m->external_addr.as_u32)) - continue; - - snat_add_del_addr_to_fib(&m->external_addr, 32, sw_if_index, !is_del); - } - /* *INDENT-ON* */ - - return 0; -} - -int -snat_set_workers (uword * bitmap) -{ - snat_main_t *sm = &snat_main; - int i, j = 0; - - if (sm->num_workers < 2) - return VNET_API_ERROR_FEATURE_DISABLED; - - if (clib_bitmap_last_set (bitmap) >= sm->num_workers) - return VNET_API_ERROR_INVALID_WORKER; - - vec_free (sm->workers); - /* *INDENT-OFF* */ - clib_bitmap_foreach (i, bitmap) - { - vec_add1(sm->workers, i); - sm->per_thread_data[sm->first_worker_index + i].snat_thread_index = j; - sm->per_thread_data[sm->first_worker_index + i].thread_index = i; - j++; - } - /* *INDENT-ON* */ - - sm->port_per_thread = (0xffff - 1024) / _vec_len (sm->workers); - - return 0; -} - -int -snat_set_frame_queue_nelts (u32 frame_queue_nelts) -{ - fail_if_enabled (); - snat_main_t *sm = &snat_main; - sm->frame_queue_nelts = frame_queue_nelts; - return 0; -} - -static void -snat_update_outside_fib (ip4_main_t * im, uword opaque, - u32 sw_if_index, u32 new_fib_index, - u32 old_fib_index) -{ - snat_main_t *sm = &snat_main; - nat_outside_fib_t *outside_fib; - snat_interface_t *i; - u8 is_add = 1; - u8 match = 0; - - if (!sm->enabled || (new_fib_index == old_fib_index) - || (!vec_len (sm->outside_fibs))) - { - return; - } - - /* *INDENT-OFF* */ - pool_foreach (i, sm->interfaces) - { - if (i->sw_if_index == sw_if_index) - { - if (!(nat_interface_is_outside (i))) - return; - match = 1; - } - } - - pool_foreach (i, sm->output_feature_interfaces) - { - if (i->sw_if_index == sw_if_index) - { - if (!(nat_interface_is_outside (i))) - return; - match = 1; - } - } - /* *INDENT-ON* */ - - if (!match) - return; - - vec_foreach (outside_fib, sm->outside_fibs) - { - if (outside_fib->fib_index == old_fib_index) - { - outside_fib->refcount--; - if (!outside_fib->refcount) - vec_del1 (sm->outside_fibs, outside_fib - sm->outside_fibs); - break; - } - } - - vec_foreach (outside_fib, sm->outside_fibs) - { - if (outside_fib->fib_index == new_fib_index) - { - outside_fib->refcount++; - is_add = 0; - break; - } - } - - if (is_add) - { - vec_add2 (sm->outside_fibs, outside_fib, 1); - outside_fib->refcount = 1; - outside_fib->fib_index = new_fib_index; - } -} - -static void -snat_update_outside_fib (ip4_main_t * im, uword opaque, - u32 sw_if_index, u32 new_fib_index, - u32 old_fib_index); - -static void -snat_ip4_add_del_interface_address_cb (ip4_main_t * im, - uword opaque, - u32 sw_if_index, - ip4_address_t * address, - u32 address_length, - u32 if_address_index, u32 is_delete); - -static void -nat_ip4_add_del_addr_only_sm_cb (ip4_main_t * im, - uword opaque, - u32 sw_if_index, - ip4_address_t * address, - u32 address_length, - u32 if_address_index, u32 is_delete); - -void -test_key_calc_split () -{ - ip4_address_t l_addr; - l_addr.as_u8[0] = 1; - l_addr.as_u8[1] = 1; - l_addr.as_u8[2] = 1; - l_addr.as_u8[3] = 1; - ip4_address_t r_addr; - r_addr.as_u8[0] = 2; - r_addr.as_u8[1] = 2; - r_addr.as_u8[2] = 2; - r_addr.as_u8[3] = 2; - u16 l_port = 40001; - u16 r_port = 40301; - u8 proto = 9; - u32 fib_index = 9000001; - u32 thread_index = 3000000001; - u32 session_index = 3000000221; - clib_bihash_kv_16_8_t kv; - init_ed_kv (&kv, l_addr, l_port, r_addr, r_port, fib_index, proto, - thread_index, session_index); - ip4_address_t l_addr2; - ip4_address_t r_addr2; - clib_memset (&l_addr2, 0, sizeof (l_addr2)); - clib_memset (&r_addr2, 0, sizeof (r_addr2)); - u16 l_port2 = 0; - u16 r_port2 = 0; - u8 proto2 = 0; - u32 fib_index2 = 0; - split_ed_kv (&kv, &l_addr2, &r_addr2, &proto2, &fib_index2, &l_port2, - &r_port2); - ASSERT (l_addr.as_u32 == l_addr2.as_u32); - ASSERT (r_addr.as_u32 == r_addr2.as_u32); - ASSERT (l_port == l_port2); - ASSERT (r_port == r_port2); - ASSERT (proto == proto2); - ASSERT (fib_index == fib_index2); - ASSERT (thread_index == ed_value_get_thread_index (&kv)); - ASSERT (session_index == ed_value_get_session_index (&kv)); - - fib_index = 7001; - proto = 5; - nat_protocol_t proto3 = ~0; - u64 key = calc_nat_key (l_addr, l_port, fib_index, proto); - split_nat_key (key, &l_addr2, &l_port2, &fib_index2, &proto3); - ASSERT (l_addr.as_u32 == l_addr2.as_u32); - ASSERT (l_port == l_port2); - ASSERT (proto == proto3); - ASSERT (fib_index == fib_index2); -} - -static clib_error_t * -nat_ip_table_add_del (vnet_main_t * vnm, u32 table_id, u32 is_add) -{ - snat_main_t *sm = &snat_main; - u32 fib_index; - - if (sm->endpoint_dependent) - { - // TODO: consider removing all NAT interfaces - if (!is_add) - { - fib_index = ip4_fib_index_from_table_id (table_id); - if (fib_index != ~0) - expire_per_vrf_sessions (fib_index); - } - } - return 0; -} - -VNET_IP_TABLE_ADD_DEL_FUNCTION (nat_ip_table_add_del); - -void -nat44_set_node_indexes (snat_main_t * sm, vlib_main_t * vm) -{ - vlib_node_t *node; - - node = vlib_get_node_by_name (vm, (u8 *) "nat44-out2in"); - sm->ei_out2in_node_index = node->index; - node = vlib_get_node_by_name (vm, (u8 *) "nat44-in2out"); - sm->ei_in2out_node_index = node->index; - node = vlib_get_node_by_name (vm, (u8 *) "nat44-in2out-output"); - sm->ei_in2out_output_node_index = node->index; - - node = vlib_get_node_by_name (vm, (u8 *) "nat44-ed-out2in"); - sm->ed_out2in_node_index = node->index; - node = vlib_get_node_by_name (vm, (u8 *) "nat44-ed-in2out"); - sm->ed_in2out_node_index = node->index; - node = vlib_get_node_by_name (vm, (u8 *) "nat44-ed-in2out-output"); - sm->ed_in2out_output_node_index = node->index; - - node = vlib_get_node_by_name (vm, (u8 *) "error-drop"); - sm->error_node_index = node->index; - node = vlib_get_node_by_name (vm, (u8 *) "nat-pre-in2out"); - sm->pre_in2out_node_index = node->index; - node = vlib_get_node_by_name (vm, (u8 *) "nat-pre-out2in"); - sm->pre_out2in_node_index = node->index; - node = vlib_get_node_by_name (vm, (u8 *) "nat-pre-in2out"); - sm->pre_in2out_node_index = node->index; - node = vlib_get_node_by_name (vm, (u8 *) "nat-pre-out2in"); - sm->pre_out2in_node_index = node->index; - node = vlib_get_node_by_name (vm, (u8 *) "nat44-in2out-fast"); - sm->in2out_fast_node_index = node->index; - node = vlib_get_node_by_name (vm, (u8 *) "nat44-in2out-slowpath"); - sm->in2out_slowpath_node_index = node->index; - node = vlib_get_node_by_name (vm, (u8 *) "nat44-in2out-output-slowpath"); - sm->in2out_slowpath_output_node_index = node->index; - node = vlib_get_node_by_name (vm, (u8 *) "nat44-ed-in2out-slowpath"); - sm->ed_in2out_slowpath_node_index = node->index; - node = vlib_get_node_by_name (vm, (u8 *) "nat44-out2in-fast"); - sm->out2in_fast_node_index = node->index; - node = vlib_get_node_by_name (vm, (u8 *) "nat44-ed-out2in-slowpath"); - sm->ed_out2in_slowpath_node_index = node->index; - node = vlib_get_node_by_name (vm, (u8 *) "nat44-hairpinning"); - sm->hairpinning_node_index = node->index; - node = vlib_get_node_by_name (vm, (u8 *) "nat44-hairpin-dst"); - sm->hairpin_dst_node_index = node->index; - node = vlib_get_node_by_name (vm, (u8 *) "nat44-hairpin-src"); - sm->hairpin_src_node_index = node->index; -} - -#define nat_init_simple_counter(c, n, sn) \ -do \ - { \ - c.name = n; \ - c.stat_segment_name = sn; \ - vlib_validate_simple_counter (&c, 0); \ - vlib_zero_simple_counter (&c, 0); \ - } while (0); - -extern vlib_node_registration_t nat44_hairpinning_node; -extern vlib_node_registration_t snat_hairpin_dst_node; -extern vlib_node_registration_t - nat44_in2out_hairpinning_finish_ip4_lookup_node; -extern vlib_node_registration_t - nat44_in2out_hairpinning_finish_interface_output_node; - -static clib_error_t * -nat_init (vlib_main_t * vm) -{ - snat_main_t *sm = &snat_main; - vlib_thread_main_t *tm = vlib_get_thread_main (); - vlib_thread_registration_t *tr; - ip4_add_del_interface_address_callback_t cbi = { 0 }; - ip4_table_bind_callback_t cbt = { 0 }; - u32 i, num_threads = 0; - uword *p, *bitmap = 0; - - clib_memset (sm, 0, sizeof (*sm)); - - // required - sm->vnet_main = vnet_get_main (); - // convenience - sm->ip4_main = &ip4_main; - sm->api_main = vlibapi_get_main (); - sm->ip4_lookup_main = &ip4_main.lookup_main; - - // frame queue indices used for handoff - sm->fq_out2in_index = ~0; - sm->fq_in2out_index = ~0; - sm->fq_in2out_output_index = ~0; - - sm->log_level = SNAT_LOG_ERROR; - - nat44_set_node_indexes (sm, vm); - sm->log_class = vlib_log_register_class ("nat", 0); - nat_ipfix_logging_init (vm); - - nat_init_simple_counter (sm->total_users, "total-users", - "/nat44/total-users"); - nat_init_simple_counter (sm->total_sessions, "total-sessions", - "/nat44/total-sessions"); - nat_init_simple_counter (sm->user_limit_reached, "user-limit-reached", - "/nat44/user-limit-reached"); - -#define _(x) \ - sm->counters.fastpath.in2out.x.name = #x; \ - sm->counters.fastpath.in2out.x.stat_segment_name = \ - "/nat44/in2out/fastpath/" #x; \ - sm->counters.slowpath.in2out.x.name = #x; \ - sm->counters.slowpath.in2out.x.stat_segment_name = \ - "/nat44/in2out/slowpath/" #x; \ - sm->counters.fastpath.out2in.x.name = #x; \ - sm->counters.fastpath.out2in.x.stat_segment_name = \ - "/nat44/out2in/fastpath/" #x; \ - sm->counters.slowpath.out2in.x.name = #x; \ - sm->counters.slowpath.out2in.x.stat_segment_name = \ - "/nat44/out2in/slowpath/" #x; \ - sm->counters.fastpath.in2out_ed.x.name = #x; \ - sm->counters.fastpath.in2out_ed.x.stat_segment_name = \ - "/nat44/ed/in2out/fastpath/" #x; \ - sm->counters.slowpath.in2out_ed.x.name = #x; \ - sm->counters.slowpath.in2out_ed.x.stat_segment_name = \ - "/nat44/ed/in2out/slowpath/" #x; \ - sm->counters.fastpath.out2in_ed.x.name = #x; \ - sm->counters.fastpath.out2in_ed.x.stat_segment_name = \ - "/nat44/ed/out2in/fastpath/" #x; \ - sm->counters.slowpath.out2in_ed.x.name = #x; \ - sm->counters.slowpath.out2in_ed.x.stat_segment_name = \ - "/nat44/ed/out2in/slowpath/" #x; - foreach_nat_counter; -#undef _ - sm->counters.hairpinning.name = "hairpinning"; - sm->counters.hairpinning.stat_segment_name = "/nat44/hairpinning"; - - p = hash_get_mem (tm->thread_registrations_by_name, "workers"); - if (p) - { - tr = (vlib_thread_registration_t *) p[0]; - if (tr) - { - sm->num_workers = tr->count; - sm->first_worker_index = tr->first_index; - } - } - num_threads = tm->n_vlib_mains - 1; - sm->port_per_thread = 0xffff - 1024; - vec_validate (sm->per_thread_data, num_threads); - - /* Use all available workers by default */ - if (sm->num_workers > 1) - { - - for (i = 0; i < sm->num_workers; i++) - bitmap = clib_bitmap_set (bitmap, i, 1); - snat_set_workers (bitmap); - clib_bitmap_free (bitmap); - } - else - sm->per_thread_data[0].snat_thread_index = 0; - - /* callbacks to call when interface address changes. */ - cbi.function = snat_ip4_add_del_interface_address_cb; - vec_add1 (sm->ip4_main->add_del_interface_address_callbacks, cbi); - cbi.function = nat_ip4_add_del_addr_only_sm_cb; - vec_add1 (sm->ip4_main->add_del_interface_address_callbacks, cbi); - - /* callbacks to call when interface to table biding changes */ - cbt.function = snat_update_outside_fib; - vec_add1 (sm->ip4_main->table_bind_callbacks, cbt); - - sm->fib_src_low = - fib_source_allocate ("nat-low", FIB_SOURCE_PRIORITY_LOW, - FIB_SOURCE_BH_SIMPLE); - sm->fib_src_hi = - fib_source_allocate ("nat-hi", FIB_SOURCE_PRIORITY_HI, - FIB_SOURCE_BH_SIMPLE); - - /* used only by out2in-dpo feature */ - nat_dpo_module_init (); - - nat_affinity_init (vm); - nat_ha_init (vm, sm->num_workers, num_threads); - - test_key_calc_split (); - - sm->nat44_hairpinning_fq_index = - vlib_frame_queue_main_init (nat44_hairpinning_node.index, 0); - sm->snat_hairpin_dst_fq_index = - vlib_frame_queue_main_init (snat_hairpin_dst_node.index, 0); - sm->nat44_in2out_hairpinning_finish_ip4_lookup_node_fq_index = - vlib_frame_queue_main_init ( - nat44_in2out_hairpinning_finish_ip4_lookup_node.index, 0); - sm->nat44_in2out_hairpinning_finish_interface_output_node_fq_index = - vlib_frame_queue_main_init ( - nat44_in2out_hairpinning_finish_interface_output_node.index, 0); - return nat44_api_hookup (vm); -} - -VLIB_INIT_FUNCTION (nat_init); - -static int -nat44_ed_plugin_enable (nat44_config_t c) -{ - snat_main_t *sm = &snat_main; - - if (c.static_mapping_only && !c.connection_tracking) - { - nat_log_err ("unsupported combination of configuration"); - return 1; - } - - // nat44 feature configuration - sm->endpoint_dependent = c.endpoint_dependent; - sm->static_mapping_only = c.static_mapping_only; - sm->static_mapping_connection_tracking = c.connection_tracking; - - // EI only feature (could break ED) - sm->out2in_dpo = c.out2in_dpo; - - sm->forwarding_enabled = 0; - sm->mss_clamping = 0; - sm->pat = (!c.static_mapping_only || - (c.static_mapping_only && c.connection_tracking)); - - if (!c.users) - c.users = 1024; - - // EI only feature (could break ED) - sm->max_users_per_thread = c.users; - sm->user_buckets = nat_calc_bihash_buckets (c.users); - - if (!c.sessions) - c.sessions = 63 * 1024; - - sm->max_translations_per_thread = c.sessions; - sm->translation_buckets = nat_calc_bihash_buckets (c.sessions); - - // ED only feature - vec_add1 (sm->max_translations_per_fib, sm->max_translations_per_thread); - - // EI only feature (could break ED) - sm->max_translations_per_user - = c.user_sessions ? c.user_sessions : sm->max_translations_per_thread; - - sm->inside_vrf_id = c.inside_vrf; - sm->inside_fib_index = - fib_table_find_or_create_and_lock - (FIB_PROTOCOL_IP4, c.inside_vrf, sm->fib_src_hi); - - sm->outside_vrf_id = c.outside_vrf; - sm->outside_fib_index = fib_table_find_or_create_and_lock ( - FIB_PROTOCOL_IP4, c.outside_vrf, sm->fib_src_hi); - - sm->worker_in2out_cb = nat44_ed_get_worker_in2out_cb; - sm->worker_out2in_cb = nat44_ed_get_worker_out2in_cb; - - sm->in2out_node_index = sm->ed_in2out_node_index; - sm->out2in_node_index = sm->ed_out2in_node_index; - - sm->in2out_output_node_index = sm->ed_in2out_output_node_index; - - if (sm->pat) - { - sm->icmp_match_in2out_cb = NULL; - sm->icmp_match_out2in_cb = NULL; - } - else - { - sm->icmp_match_in2out_cb = icmp_match_in2out_fast; - sm->icmp_match_out2in_cb = icmp_match_out2in_fast; - } - - nat44_ed_db_init (sm->max_translations_per_thread, sm->translation_buckets, - sm->user_buckets); - - nat_affinity_enable (); - - nat_reset_timeouts (&sm->timeouts); - - // TODO: function for reset counters - vlib_zero_simple_counter (&sm->total_users, 0); - vlib_zero_simple_counter (&sm->total_sessions, 0); - vlib_zero_simple_counter (&sm->user_limit_reached, 0); - - if (!sm->frame_queue_nelts) - sm->frame_queue_nelts = NAT_FQ_NELTS_DEFAULT; - - sm->enabled = 1; - sm->rconfig = c; - - return 0; -} - -int -nat44_plugin_enable (nat44_config_t c) -{ - fail_if_enabled (); - - // c.static_mapping_only + c.connection_tracking - // - supported in NAT EI & NAT ED - // c.out2in_dpo, c.static_mapping_only - // - supported in NAT EI - - if (c.endpoint_dependent) - { - if (c.out2in_dpo || c.users || c.user_sessions) - { - nat_log_err ("unsupported combination of configuration"); - return 1; - } - return nat44_ed_plugin_enable (c); - } - - // separation: - // for now just copy variables - - nat44_ei_config_t ei_c = { - .inside_vrf = c.inside_vrf, - .outside_vrf = c.outside_vrf, - .users = c.users, - .sessions = c.sessions, - .user_sessions = c.user_sessions, - .out2in_dpo = c.out2in_dpo, - .static_mapping_only = c.static_mapping_only, - .connection_tracking = c.connection_tracking, - }; - - return nat44_ei_plugin_enable (ei_c); -} - -void -nat44_addresses_free (snat_address_t ** addresses) -{ - snat_address_t *ap; - /* *INDENT-OFF* */ - vec_foreach (ap, *addresses) - { - #define _(N, i, n, s) \ - vec_free (ap->busy_##n##_ports_per_thread); - foreach_nat_protocol - #undef _ - } - /* *INDENT-ON* */ - vec_free (*addresses); - *addresses = 0; -} - -static int -nat44_ed_plugin_disable () -{ - snat_main_t *sm = &snat_main; - snat_interface_t *i, *vec; - int error = 0; - - // first unregister all nodes from interfaces - vec = vec_dup (sm->interfaces); - /* *INDENT-OFF* */ - vec_foreach (i, vec) - { - if (nat_interface_is_inside(i)) - error = snat_interface_add_del (i->sw_if_index, 1, 1); - if (nat_interface_is_outside(i)) - error = snat_interface_add_del (i->sw_if_index, 0, 1); - - if (error) - { - nat_log_err ("error occurred while removing interface %u", - i->sw_if_index); - } - } - /* *INDENT-ON* */ - vec_free (vec); - sm->interfaces = 0; - - vec = vec_dup (sm->output_feature_interfaces); - /* *INDENT-OFF* */ - vec_foreach (i, vec) - { - if (nat_interface_is_inside(i)) - error = snat_interface_add_del_output_feature (i->sw_if_index, 1, 1); - if (nat_interface_is_outside(i)) - error = snat_interface_add_del_output_feature (i->sw_if_index, 0, 1); - - if (error) - { - nat_log_err ("error occurred while removing interface %u", - i->sw_if_index); - } - } - /* *INDENT-ON* */ - vec_free (vec); - sm->output_feature_interfaces = 0; - - vec_free (sm->max_translations_per_fib); - - nat44_ed_db_free (); - - nat44_addresses_free (&sm->addresses); - nat44_addresses_free (&sm->twice_nat_addresses); - - - vec_free (sm->to_resolve); - vec_free (sm->auto_add_sw_if_indices); - vec_free (sm->auto_add_sw_if_indices_twice_nat); - - sm->to_resolve = 0; - sm->auto_add_sw_if_indices = 0; - sm->auto_add_sw_if_indices_twice_nat = 0; - - sm->forwarding_enabled = 0; - - sm->enabled = 0; - clib_memset (&sm->rconfig, 0, sizeof (sm->rconfig)); - - return 0; -} - -int -nat44_plugin_disable () -{ - snat_main_t *sm = &snat_main; - - fail_if_disabled (); - - if (sm->endpoint_dependent) - return nat44_ed_plugin_disable (); - return nat44_ei_plugin_disable (); -} - -void -snat_free_outside_address_and_port (snat_address_t *addresses, - u32 thread_index, ip4_address_t *addr, - u16 port, nat_protocol_t protocol) -{ - snat_address_t *a; - u32 address_index; - u16 port_host_byte_order = clib_net_to_host_u16 (port); - - for (address_index = 0; address_index < vec_len (addresses); - address_index++) - { - if (addresses[address_index].addr.as_u32 == addr->as_u32) - break; - } - - ASSERT (address_index < vec_len (addresses)); - - a = addresses + address_index; - - switch (protocol) - { -#define _(N, i, n, s) \ - case NAT_PROTOCOL_##N: \ - ASSERT (a->busy_##n##_port_refcounts[port_host_byte_order] >= 1); \ - --a->busy_##n##_port_refcounts[port_host_byte_order]; \ - a->busy_##n##_ports--; \ - a->busy_##n##_ports_per_thread[thread_index]--; \ - break; - foreach_nat_protocol -#undef _ - default: - nat_elog_info ("unknown protocol"); - return; - } -} - -int -nat_set_outside_address_and_port (snat_address_t *addresses, u32 thread_index, - ip4_address_t addr, u16 port, - nat_protocol_t protocol) -{ - snat_address_t *a = 0; - u32 address_index; - u16 port_host_byte_order = clib_net_to_host_u16 (port); - - for (address_index = 0; address_index < vec_len (addresses); - address_index++) - { - if (addresses[address_index].addr.as_u32 != addr.as_u32) - continue; - - a = addresses + address_index; - switch (protocol) - { -#define _(N, j, n, s) \ - case NAT_PROTOCOL_##N: \ - if (a->busy_##n##_port_refcounts[port_host_byte_order]) \ - return VNET_API_ERROR_INSTANCE_IN_USE; \ - ++a->busy_##n##_port_refcounts[port_host_byte_order]; \ - a->busy_##n##_ports_per_thread[thread_index]++; \ - a->busy_##n##_ports++; \ - return 0; - foreach_nat_protocol -#undef _ - default: - nat_elog_info ("unknown protocol"); - return 1; - } - } - - return VNET_API_ERROR_NO_SUCH_ENTRY; -} - -int -snat_static_mapping_match (snat_main_t * sm, - ip4_address_t match_addr, - u16 match_port, - u32 match_fib_index, - nat_protocol_t match_protocol, - ip4_address_t * mapping_addr, - u16 * mapping_port, - u32 * mapping_fib_index, - u8 by_external, - u8 * is_addr_only, - twice_nat_type_t * twice_nat, - lb_nat_type_t * lb, ip4_address_t * ext_host_addr, - u8 * is_identity_nat, snat_static_mapping_t ** out) -{ - clib_bihash_kv_8_8_t kv, value; - clib_bihash_8_8_t *mapping_hash; - snat_static_mapping_t *m; - u32 rand, lo = 0, hi, mid, *tmp = 0, i; - nat44_lb_addr_port_t *local; - u8 backend_index; - - if (!by_external) - { - mapping_hash = &sm->static_mapping_by_local; - init_nat_k (&kv, match_addr, match_port, match_fib_index, - match_protocol); - if (clib_bihash_search_8_8 (mapping_hash, &kv, &value)) - { - /* Try address only mapping */ - init_nat_k (&kv, match_addr, 0, match_fib_index, 0); - if (clib_bihash_search_8_8 (mapping_hash, &kv, &value)) - return 1; - } - } - else - { - mapping_hash = &sm->static_mapping_by_external; - init_nat_k (&kv, match_addr, match_port, 0, match_protocol); - if (clib_bihash_search_8_8 (mapping_hash, &kv, &value)) - { - /* Try address only mapping */ - init_nat_k (&kv, match_addr, 0, 0, 0); - if (clib_bihash_search_8_8 (mapping_hash, &kv, &value)) - return 1; - } - } - - m = pool_elt_at_index (sm->static_mappings, value.value); - - if (by_external) - { - if (is_lb_static_mapping (m)) - { - if (PREDICT_FALSE (lb != 0)) - *lb = m->affinity ? AFFINITY_LB_NAT : LB_NAT; - if (m->affinity && !nat_affinity_find_and_lock (ext_host_addr[0], - match_addr, - match_protocol, - match_port, - &backend_index)) - { - local = pool_elt_at_index (m->locals, backend_index); - *mapping_addr = local->addr; - *mapping_port = local->port; - *mapping_fib_index = local->fib_index; - goto end; - } - // pick locals matching this worker - if (PREDICT_FALSE (sm->num_workers > 1)) - { - u32 thread_index = vlib_get_thread_index (); - /* *INDENT-OFF* */ - pool_foreach_index (i, m->locals) - { - local = pool_elt_at_index (m->locals, i); - - ip4_header_t ip = { - .src_address = local->addr, - }; - - if (sm->worker_in2out_cb (&ip, m->fib_index, 0) == - thread_index) - { - vec_add1 (tmp, i); - } - } - /* *INDENT-ON* */ - ASSERT (vec_len (tmp) != 0); - } - else - { - /* *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; - local = pool_elt_at_index (m->locals, tmp[mid]); - (rand > local->prefix) ? (lo = mid + 1) : (hi = mid); - } - local = pool_elt_at_index (m->locals, tmp[lo]); - if (!(local->prefix >= rand)) - return 1; - *mapping_addr = local->addr; - *mapping_port = 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, - tmp[lo], m->affinity, - m->affinity_per_service_list_head_index)) - nat_elog_info ("create affinity record failed"); - } - vec_free (tmp); - } - else - { - if (PREDICT_FALSE (lb != 0)) - *lb = NO_LB_NAT; - *mapping_fib_index = m->fib_index; - *mapping_addr = m->local_addr; - /* Address only mapping doesn't change port */ - *mapping_port = is_addr_only_static_mapping (m) ? match_port - : m->local_port; - } - } - else - { - *mapping_addr = m->external_addr; - /* Address only mapping doesn't change port */ - *mapping_port = is_addr_only_static_mapping (m) ? match_port - : m->external_port; - *mapping_fib_index = sm->outside_fib_index; - } - -end: - if (PREDICT_FALSE (is_addr_only != 0)) - *is_addr_only = is_addr_only_static_mapping (m); - - if (PREDICT_FALSE (twice_nat != 0)) - *twice_nat = m->twice_nat; - - if (PREDICT_FALSE (is_identity_nat != 0)) - *is_identity_nat = is_identity_static_mapping (m); - - if (out != 0) - *out = m; - - return 0; -} - -void -nat44_add_del_address_dpo (ip4_address_t addr, u8 is_add) -{ - snat_main_t *sm = &snat_main; - dpo_id_t dpo_v4 = DPO_INVALID; - fib_prefix_t pfx = { - .fp_proto = FIB_PROTOCOL_IP4, - .fp_len = 32, - .fp_addr.ip4.as_u32 = addr.as_u32, - }; - - if (is_add) - { - nat_dpo_create (DPO_PROTO_IP4, 0, &dpo_v4); - fib_table_entry_special_dpo_add (0, &pfx, sm->fib_src_hi, - FIB_ENTRY_FLAG_EXCLUSIVE, &dpo_v4); - dpo_reset (&dpo_v4); - } - else - { - fib_table_entry_special_remove (0, &pfx, sm->fib_src_hi); - } -} - -static u32 -nat44_ed_get_worker_in2out_cb (ip4_header_t *ip, u32 rx_fib_index, - u8 is_output) -{ - snat_main_t *sm = &snat_main; - u32 next_worker_index = sm->first_worker_index; - u32 hash; - - clib_bihash_kv_16_8_t kv16, value16; - snat_main_per_thread_data_t *tsm; - udp_header_t *udp; - - if (PREDICT_FALSE (is_output)) - { - u32 fib_index = sm->outside_fib_index; - nat_outside_fib_t *outside_fib; - fib_node_index_t fei = FIB_NODE_INDEX_INVALID; - fib_prefix_t pfx = { - .fp_proto = FIB_PROTOCOL_IP4, - .fp_len = 32, - .fp_addr = { - .ip4.as_u32 = ip->dst_address.as_u32, - } - , - }; - - udp = ip4_next_header (ip); - - switch (vec_len (sm->outside_fibs)) - { - case 0: - fib_index = sm->outside_fib_index; - break; - case 1: - fib_index = sm->outside_fibs[0].fib_index; - break; - default: - /* *INDENT-OFF* */ - vec_foreach (outside_fib, sm->outside_fibs) - { - fei = fib_table_lookup (outside_fib->fib_index, &pfx); - if (FIB_NODE_INDEX_INVALID != fei) - { - if (fib_entry_get_resolving_interface (fei) != ~0) - { - fib_index = outside_fib->fib_index; - break; - } - } - } - /* *INDENT-ON* */ - break; - } - - init_ed_k (&kv16, ip->src_address, udp->src_port, ip->dst_address, - udp->dst_port, fib_index, ip->protocol); - - if (PREDICT_TRUE ( - !clib_bihash_search_16_8 (&sm->flow_hash, &kv16, &value16))) - { - tsm = - vec_elt_at_index (sm->per_thread_data, - ed_value_get_thread_index (&value16)); - next_worker_index += tsm->thread_index; - - nat_elog_debug_handoff ("HANDOFF IN2OUT-OUTPUT-FEATURE (session)", - next_worker_index, fib_index, - clib_net_to_host_u32 (ip-> - src_address.as_u32), - clib_net_to_host_u32 (ip-> - dst_address.as_u32)); - - return next_worker_index; - } - } - - hash = ip->src_address.as_u32 + (ip->src_address.as_u32 >> 8) + - (ip->src_address.as_u32 >> 16) + (ip->src_address.as_u32 >> 24); - - if (PREDICT_TRUE (is_pow2 (_vec_len (sm->workers)))) - next_worker_index += sm->workers[hash & (_vec_len (sm->workers) - 1)]; - else - next_worker_index += sm->workers[hash % _vec_len (sm->workers)]; - - if (PREDICT_TRUE (!is_output)) - { - nat_elog_debug_handoff ("HANDOFF IN2OUT", - next_worker_index, rx_fib_index, - clib_net_to_host_u32 (ip->src_address.as_u32), - clib_net_to_host_u32 (ip->dst_address.as_u32)); - } - else - { - nat_elog_debug_handoff ("HANDOFF IN2OUT-OUTPUT-FEATURE", - next_worker_index, rx_fib_index, - clib_net_to_host_u32 (ip->src_address.as_u32), - clib_net_to_host_u32 (ip->dst_address.as_u32)); - } - - return next_worker_index; -} - -static u32 -nat44_ed_get_worker_out2in_cb (vlib_buffer_t * b, ip4_header_t * ip, - u32 rx_fib_index, u8 is_output) -{ - snat_main_t *sm = &snat_main; - clib_bihash_kv_8_8_t kv, value; - clib_bihash_kv_16_8_t kv16, value16; - snat_main_per_thread_data_t *tsm; - - u32 proto, next_worker_index = 0; - udp_header_t *udp; - u16 port; - snat_static_mapping_t *m; - u32 hash; - - proto = ip_proto_to_nat_proto (ip->protocol); - - if (PREDICT_TRUE (proto == NAT_PROTOCOL_UDP || proto == NAT_PROTOCOL_TCP)) - { - udp = ip4_next_header (ip); - - init_ed_k (&kv16, ip->dst_address, udp->dst_port, ip->src_address, - udp->src_port, rx_fib_index, ip->protocol); - - if (PREDICT_TRUE ( - !clib_bihash_search_16_8 (&sm->flow_hash, &kv16, &value16))) - { - tsm = - vec_elt_at_index (sm->per_thread_data, - ed_value_get_thread_index (&value16)); - vnet_buffer2 (b)->nat.cached_session_index = - ed_value_get_session_index (&value16); - next_worker_index = sm->first_worker_index + tsm->thread_index; - nat_elog_debug_handoff ("HANDOFF OUT2IN (session)", - next_worker_index, rx_fib_index, - clib_net_to_host_u32 (ip-> - src_address.as_u32), - clib_net_to_host_u32 (ip-> - dst_address.as_u32)); - return next_worker_index; - } - } - else if (proto == NAT_PROTOCOL_ICMP) - { - ip4_address_t lookup_saddr, lookup_daddr; - u16 lookup_sport, lookup_dport; - u8 lookup_protocol; - if (!nat_get_icmp_session_lookup_values ( - b, ip, &lookup_saddr, &lookup_sport, &lookup_daddr, &lookup_dport, - &lookup_protocol)) - { - init_ed_k (&kv16, lookup_saddr, lookup_sport, lookup_daddr, - lookup_dport, rx_fib_index, lookup_protocol); - if (PREDICT_TRUE ( - !clib_bihash_search_16_8 (&sm->flow_hash, &kv16, &value16))) - { - tsm = - vec_elt_at_index (sm->per_thread_data, - ed_value_get_thread_index (&value16)); - next_worker_index = sm->first_worker_index + tsm->thread_index; - nat_elog_debug_handoff ("HANDOFF OUT2IN (session)", - next_worker_index, rx_fib_index, - clib_net_to_host_u32 (ip-> - src_address.as_u32), - clib_net_to_host_u32 (ip-> - dst_address.as_u32)); - return next_worker_index; - } - } - } - - /* first try static mappings without port */ - if (PREDICT_FALSE (pool_elts (sm->static_mappings))) - { - init_nat_k (&kv, ip->dst_address, 0, 0, 0); - if (!clib_bihash_search_8_8 - (&sm->static_mapping_by_external, &kv, &value)) - { - m = pool_elt_at_index (sm->static_mappings, value.value); - next_worker_index = m->workers[0]; - goto done; - } - } - - /* unknown protocol */ - if (PREDICT_FALSE (proto == NAT_PROTOCOL_OTHER)) - { - /* use current thread */ - next_worker_index = vlib_get_thread_index (); - goto done; - } - - udp = ip4_next_header (ip); - port = udp->dst_port; - - if (PREDICT_FALSE (ip->protocol == IP_PROTOCOL_ICMP)) - { - icmp46_header_t *icmp = (icmp46_header_t *) udp; - icmp_echo_header_t *echo = (icmp_echo_header_t *) (icmp + 1); - if (!icmp_type_is_error_message - (vnet_buffer (b)->ip.reass.icmp_type_or_tcp_flags)) - port = vnet_buffer (b)->ip.reass.l4_src_port; - else - { - /* if error message, then it's not fragmented and we can access it */ - ip4_header_t *inner_ip = (ip4_header_t *) (echo + 1); - proto = ip_proto_to_nat_proto (inner_ip->protocol); - void *l4_header = ip4_next_header (inner_ip); - switch (proto) - { - case NAT_PROTOCOL_ICMP: - icmp = (icmp46_header_t *) l4_header; - echo = (icmp_echo_header_t *) (icmp + 1); - port = echo->identifier; - break; - case NAT_PROTOCOL_UDP: - case NAT_PROTOCOL_TCP: - port = ((tcp_udp_header_t *) l4_header)->src_port; - break; - default: - next_worker_index = vlib_get_thread_index (); - goto done; - } - } - } - - /* try static mappings with port */ - if (PREDICT_FALSE (pool_elts (sm->static_mappings))) - { - init_nat_k (&kv, ip->dst_address, port, 0, proto); - if (!clib_bihash_search_8_8 - (&sm->static_mapping_by_external, &kv, &value)) - { - m = pool_elt_at_index (sm->static_mappings, value.value); - if (!is_lb_static_mapping (m)) - { - next_worker_index = m->workers[0]; - goto done; - } - - hash = ip->src_address.as_u32 + (ip->src_address.as_u32 >> 8) + - (ip->src_address.as_u32 >> 16) + (ip->src_address.as_u32 >> 24); - - if (PREDICT_TRUE (is_pow2 (_vec_len (m->workers)))) - next_worker_index = - m->workers[hash & (_vec_len (m->workers) - 1)]; - else - next_worker_index = m->workers[hash % _vec_len (m->workers)]; - goto done; - } - } - - /* worker by outside port */ - next_worker_index = sm->first_worker_index; - next_worker_index += - sm->workers[(clib_net_to_host_u16 (port) - 1024) / sm->port_per_thread]; - -done: - nat_elog_debug_handoff ("HANDOFF OUT2IN", next_worker_index, rx_fib_index, - clib_net_to_host_u32 (ip->src_address.as_u32), - clib_net_to_host_u32 (ip->dst_address.as_u32)); - return next_worker_index; -} - -u32 -nat_calc_bihash_buckets (u32 n_elts) -{ - n_elts = n_elts / 2.5; - u64 lower_pow2 = 1; - while (lower_pow2 * 2 < n_elts) - { - lower_pow2 = 2 * lower_pow2; - } - u64 upper_pow2 = 2 * lower_pow2; - if ((upper_pow2 - n_elts) < (n_elts - lower_pow2)) - { - if (upper_pow2 <= UINT32_MAX) - { - return upper_pow2; - } - } - return lower_pow2; -} - -u32 -nat44_get_max_session_limit () -{ - snat_main_t *sm = &snat_main; - u32 max_limit = 0, len = 0; - - for (; len < vec_len (sm->max_translations_per_fib); len++) - { - if (max_limit < sm->max_translations_per_fib[len]) - max_limit = sm->max_translations_per_fib[len]; - } - return max_limit; -} - -int -nat44_set_session_limit (u32 session_limit, u32 vrf_id) -{ - snat_main_t *sm = &snat_main; - u32 fib_index = fib_table_find (FIB_PROTOCOL_IP4, vrf_id); - u32 len = vec_len (sm->max_translations_per_fib); - - if (len <= fib_index) - { - vec_validate (sm->max_translations_per_fib, fib_index + 1); - - for (; len < vec_len (sm->max_translations_per_fib); len++) - sm->max_translations_per_fib[len] = sm->max_translations_per_thread; - } - - sm->max_translations_per_fib[fib_index] = session_limit; - return 0; -} - -int -nat44_update_session_limit (u32 session_limit, u32 vrf_id) -{ - snat_main_t *sm = &snat_main; - - if (nat44_set_session_limit (session_limit, vrf_id)) - return 1; - sm->max_translations_per_thread = nat44_get_max_session_limit (); - - sm->translation_buckets = - nat_calc_bihash_buckets (sm->max_translations_per_thread); - - nat44_sessions_clear (); - return 0; -} - -static void -nat44_ed_worker_db_init (snat_main_per_thread_data_t *tsm, u32 translations, - u32 translation_buckets, u32 user_buckets) -{ - dlist_elt_t *head; - - pool_alloc (tsm->sessions, translations); - pool_alloc (tsm->lru_pool, translations); - - pool_get (tsm->lru_pool, head); - tsm->tcp_trans_lru_head_index = head - tsm->lru_pool; - clib_dlist_init (tsm->lru_pool, tsm->tcp_trans_lru_head_index); - - pool_get (tsm->lru_pool, head); - tsm->tcp_estab_lru_head_index = head - tsm->lru_pool; - clib_dlist_init (tsm->lru_pool, tsm->tcp_estab_lru_head_index); - - pool_get (tsm->lru_pool, head); - tsm->udp_lru_head_index = head - tsm->lru_pool; - clib_dlist_init (tsm->lru_pool, tsm->udp_lru_head_index); - - pool_get (tsm->lru_pool, head); - tsm->icmp_lru_head_index = head - tsm->lru_pool; - clib_dlist_init (tsm->lru_pool, tsm->icmp_lru_head_index); - - pool_get (tsm->lru_pool, head); - tsm->unk_proto_lru_head_index = head - tsm->lru_pool; - clib_dlist_init (tsm->lru_pool, tsm->unk_proto_lru_head_index); - - // TODO: ED nat is not using these - // before removal large refactor required - pool_alloc (tsm->list_pool, translations); - clib_bihash_init_8_8 (&tsm->user_hash, "users", user_buckets, 0); - clib_bihash_set_kvp_format_fn_8_8 (&tsm->user_hash, format_user_kvp); -} - -static void -reinit_ed_flow_hash () -{ - snat_main_t *sm = &snat_main; - // we expect 2 flows per session, so multiply translation_buckets by 2 - clib_bihash_init_16_8 ( - &sm->flow_hash, "ed-flow-hash", - clib_max (1, sm->num_workers) * 2 * sm->translation_buckets, 0); - clib_bihash_set_kvp_format_fn_16_8 (&sm->flow_hash, format_ed_session_kvp); -} - -static void -nat44_ed_db_init (u32 translations, u32 translation_buckets, u32 user_buckets) -{ - snat_main_t *sm = &snat_main; - snat_main_per_thread_data_t *tsm; - u32 static_mapping_buckets = 1024; - u32 static_mapping_memory_size = 64 << 20; - - reinit_ed_flow_hash (); - - clib_bihash_init_8_8 (&sm->static_mapping_by_local, - "static_mapping_by_local", static_mapping_buckets, - static_mapping_memory_size); - clib_bihash_set_kvp_format_fn_8_8 (&sm->static_mapping_by_local, - format_static_mapping_kvp); - - clib_bihash_init_8_8 (&sm->static_mapping_by_external, - "static_mapping_by_external", static_mapping_buckets, - static_mapping_memory_size); - clib_bihash_set_kvp_format_fn_8_8 (&sm->static_mapping_by_external, - format_static_mapping_kvp); - - if (sm->pat) - { - vec_foreach (tsm, sm->per_thread_data) - { - nat44_ed_worker_db_init (tsm, sm->max_translations_per_thread, - sm->translation_buckets, sm->user_buckets); - } - } -} - -static void -nat44_ed_worker_db_free (snat_main_per_thread_data_t *tsm) -{ - pool_free (tsm->sessions); - pool_free (tsm->lru_pool); - - vec_free (tsm->per_vrf_sessions_vec); - - // TODO: resolve static mappings (put only to !ED) - pool_free (tsm->list_pool); - pool_free (tsm->users); - clib_bihash_free_8_8 (&tsm->user_hash); -} - -static void -nat44_ed_db_free () -{ - snat_main_t *sm = &snat_main; - snat_main_per_thread_data_t *tsm; - - pool_free (sm->static_mappings); - clib_bihash_free_16_8 (&sm->flow_hash); - clib_bihash_free_8_8 (&sm->static_mapping_by_local); - clib_bihash_free_8_8 (&sm->static_mapping_by_external); - - if (sm->pat) - { - vec_foreach (tsm, sm->per_thread_data) - { - nat44_ed_worker_db_free (tsm); - } - } -} - -void -nat44_ed_sessions_clear () -{ - snat_main_t *sm = &snat_main; - snat_main_per_thread_data_t *tsm; - - reinit_ed_flow_hash (); - - if (sm->pat) - { - vec_foreach (tsm, sm->per_thread_data) - { - - nat44_ed_worker_db_free (tsm); - nat44_ed_worker_db_init (tsm, sm->max_translations_per_thread, - sm->translation_buckets, sm->user_buckets); - } - } - - // TODO: function for reset counters - vlib_zero_simple_counter (&sm->total_users, 0); - vlib_zero_simple_counter (&sm->total_sessions, 0); - vlib_zero_simple_counter (&sm->user_limit_reached, 0); -} - -void -nat44_sessions_clear () -{ - snat_main_t *sm = &snat_main; - - if (sm->endpoint_dependent) - nat44_ed_sessions_clear (); - else - nat44_ei_sessions_clear (); -} - -static void -nat_ip4_add_del_addr_only_sm_cb (ip4_main_t * im, - uword opaque, - u32 sw_if_index, - ip4_address_t * address, - u32 address_length, - u32 if_address_index, u32 is_delete) -{ - snat_main_t *sm = &snat_main; - snat_static_map_resolve_t *rp; - snat_static_mapping_t *m; - clib_bihash_kv_8_8_t kv, value; - int i, rv; - ip4_address_t l_addr; - - if (!sm->enabled) - return; - - for (i = 0; i < vec_len (sm->to_resolve); i++) - { - rp = sm->to_resolve + i; - if (rp->addr_only == 0) - continue; - if (rp->sw_if_index == sw_if_index) - goto match; - } - - return; - -match: - init_nat_k (&kv, *address, rp->addr_only ? 0 : rp->e_port, - sm->outside_fib_index, rp->addr_only ? 0 : rp->proto); - if (clib_bihash_search_8_8 (&sm->static_mapping_by_external, &kv, &value)) - m = 0; - else - m = pool_elt_at_index (sm->static_mappings, value.value); - - if (!is_delete) - { - /* Don't trip over lease renewal, static config */ - if (m) - return; - } - else - { - if (!m) - return; - } - - /* Indetity mapping? */ - if (rp->l_addr.as_u32 == 0) - l_addr.as_u32 = address[0].as_u32; - else - l_addr.as_u32 = rp->l_addr.as_u32; - /* Add the static mapping */ - rv = snat_add_static_mapping (l_addr, - address[0], - rp->l_port, - rp->e_port, - rp->vrf_id, - rp->addr_only, ~0 /* sw_if_index */ , - rp->proto, !is_delete, rp->twice_nat, - rp->out2in_only, rp->tag, rp->identity_nat, - rp->pool_addr, rp->exact); - if (rv) - nat_elog_notice_X1 ("snat_add_static_mapping returned %d", "i4", rv); -} - -static void -snat_ip4_add_del_interface_address_cb (ip4_main_t * im, - uword opaque, - u32 sw_if_index, - ip4_address_t * address, - u32 address_length, - u32 if_address_index, u32 is_delete) -{ - snat_main_t *sm = &snat_main; - snat_static_map_resolve_t *rp; - ip4_address_t l_addr; - int i, j; - int rv; - u8 twice_nat = 0; - snat_address_t *addresses = sm->addresses; - - if (!sm->enabled) - return; - - for (i = 0; i < vec_len (sm->auto_add_sw_if_indices); i++) - { - if (sw_if_index == sm->auto_add_sw_if_indices[i]) - goto match; - } - - for (i = 0; i < vec_len (sm->auto_add_sw_if_indices_twice_nat); i++) - { - twice_nat = 1; - addresses = sm->twice_nat_addresses; - if (sw_if_index == sm->auto_add_sw_if_indices_twice_nat[i]) - goto match; - } - - return; - -match: - if (!is_delete) - { - /* Don't trip over lease renewal, static config */ - for (j = 0; j < vec_len (addresses); j++) - if (addresses[j].addr.as_u32 == address->as_u32) - return; - - (void) snat_add_address (sm, address, ~0, twice_nat); - /* Scan static map resolution vector */ - for (j = 0; j < vec_len (sm->to_resolve); j++) - { - rp = sm->to_resolve + j; - if (rp->addr_only) - continue; - /* On this interface? */ - if (rp->sw_if_index == sw_if_index) - { - /* Indetity mapping? */ - if (rp->l_addr.as_u32 == 0) - l_addr.as_u32 = address[0].as_u32; - else - l_addr.as_u32 = rp->l_addr.as_u32; - /* Add the static mapping */ - rv = snat_add_static_mapping ( - l_addr, address[0], rp->l_port, rp->e_port, rp->vrf_id, - rp->addr_only, ~0 /* sw_if_index */, rp->proto, 1, - rp->twice_nat, rp->out2in_only, rp->tag, rp->identity_nat, - rp->pool_addr, rp->exact); - if (rv) - nat_elog_notice_X1 ("snat_add_static_mapping returned %d", - "i4", rv); - } - } - return; - } - else - { - (void) snat_del_address (sm, address[0], 1, twice_nat); - return; - } -} - -int -snat_add_interface_address (snat_main_t * sm, u32 sw_if_index, int is_del, - u8 twice_nat) -{ - ip4_main_t *ip4_main = sm->ip4_main; - ip4_address_t *first_int_addr; - snat_static_map_resolve_t *rp; - u32 *indices_to_delete = 0; - int i, j; - u32 *auto_add_sw_if_indices = - twice_nat ? sm-> - auto_add_sw_if_indices_twice_nat : sm->auto_add_sw_if_indices; - - first_int_addr = ip4_interface_first_address (ip4_main, sw_if_index, 0 /* just want the address */ - ); - - for (i = 0; i < vec_len (auto_add_sw_if_indices); i++) - { - if (auto_add_sw_if_indices[i] == sw_if_index) - { - if (is_del) - { - /* if have address remove it */ - if (first_int_addr) - (void) snat_del_address (sm, first_int_addr[0], 1, twice_nat); - else - { - for (j = 0; j < vec_len (sm->to_resolve); j++) - { - rp = sm->to_resolve + j; - if (rp->sw_if_index == sw_if_index) - vec_add1 (indices_to_delete, j); - } - if (vec_len (indices_to_delete)) - { - for (j = vec_len (indices_to_delete) - 1; j >= 0; j--) - vec_del1 (sm->to_resolve, j); - vec_free (indices_to_delete); - } - } - if (twice_nat) - vec_del1 (sm->auto_add_sw_if_indices_twice_nat, i); - else - vec_del1 (sm->auto_add_sw_if_indices, i); - } - else - return VNET_API_ERROR_VALUE_EXIST; - - return 0; - } - } - - if (is_del) - return VNET_API_ERROR_NO_SUCH_ENTRY; - - /* add to the auto-address list */ - if (twice_nat) - vec_add1 (sm->auto_add_sw_if_indices_twice_nat, sw_if_index); - else - vec_add1 (sm->auto_add_sw_if_indices, sw_if_index); - - /* If the address is already bound - or static - add it now */ - if (first_int_addr) - (void) snat_add_address (sm, first_int_addr, ~0, twice_nat); - - return 0; -} - -int -nat44_del_ed_session (snat_main_t * sm, ip4_address_t * addr, u16 port, - ip4_address_t * eh_addr, u16 eh_port, u8 proto, - u32 vrf_id, int is_in) -{ - ip4_header_t ip; - clib_bihash_kv_16_8_t kv, value; - u32 fib_index = fib_table_find (FIB_PROTOCOL_IP4, vrf_id); - snat_session_t *s; - snat_main_per_thread_data_t *tsm; - - if (!sm->endpoint_dependent) - return VNET_API_ERROR_FEATURE_DISABLED; - - ip.dst_address.as_u32 = ip.src_address.as_u32 = addr->as_u32; - if (sm->num_workers > 1) - tsm = - vec_elt_at_index (sm->per_thread_data, - sm->worker_in2out_cb (&ip, fib_index, 0)); - else - tsm = vec_elt_at_index (sm->per_thread_data, sm->num_workers); - - init_ed_k (&kv, *addr, port, *eh_addr, eh_port, fib_index, proto); - if (clib_bihash_search_16_8 (&sm->flow_hash, &kv, &value)) - { - return VNET_API_ERROR_NO_SUCH_ENTRY; - } - - if (pool_is_free_index (tsm->sessions, ed_value_get_session_index (&value))) - return VNET_API_ERROR_UNSPECIFIED; - s = pool_elt_at_index (tsm->sessions, ed_value_get_session_index (&value)); - nat_free_session_data (sm, s, tsm - sm->per_thread_data, 0); - nat_ed_session_delete (sm, s, tsm - sm->per_thread_data, 1); - return 0; -} - -VLIB_NODE_FN (nat_default_node) (vlib_main_t * vm, - vlib_node_runtime_t * node, - vlib_frame_t * frame) -{ - return 0; -} - -/* *INDENT-OFF* */ -VLIB_REGISTER_NODE (nat_default_node) = { - .name = "nat-default", - .vector_size = sizeof (u32), - .format_trace = 0, - .type = VLIB_NODE_TYPE_INTERNAL, - .n_errors = 0, - .n_next_nodes = NAT_N_NEXT, - .next_nodes = { - [NAT_NEXT_DROP] = "error-drop", - [NAT_NEXT_ICMP_ERROR] = "ip4-icmp-error", - [NAT_NEXT_IN2OUT_ED_FAST_PATH] = "nat44-ed-in2out", - [NAT_NEXT_IN2OUT_ED_SLOW_PATH] = "nat44-ed-in2out-slowpath", - [NAT_NEXT_IN2OUT_ED_OUTPUT_FAST_PATH] = "nat44-ed-in2out-output", - [NAT_NEXT_IN2OUT_ED_OUTPUT_SLOW_PATH] = "nat44-ed-in2out-output-slowpath", - [NAT_NEXT_OUT2IN_ED_FAST_PATH] = "nat44-ed-out2in", - [NAT_NEXT_OUT2IN_ED_SLOW_PATH] = "nat44-ed-out2in-slowpath", - [NAT_NEXT_IN2OUT_CLASSIFY] = "nat44-in2out-worker-handoff", - [NAT_NEXT_OUT2IN_CLASSIFY] = "nat44-out2in-worker-handoff", - }, -}; -/* *INDENT-ON* */ - -void -nat_6t_l3_l4_csum_calc (nat_6t_flow_t *f) -{ - f->l3_csum_delta = 0; - f->l4_csum_delta = 0; - if (f->ops & NAT_FLOW_OP_SADDR_REWRITE && - f->rewrite.saddr.as_u32 != f->match.saddr.as_u32) - { - f->l3_csum_delta = - ip_csum_add_even (f->l3_csum_delta, f->rewrite.saddr.as_u32); - f->l3_csum_delta = - ip_csum_sub_even (f->l3_csum_delta, f->match.saddr.as_u32); - } - else - { - f->rewrite.saddr.as_u32 = f->match.saddr.as_u32; - } - if (f->ops & NAT_FLOW_OP_DADDR_REWRITE && - f->rewrite.daddr.as_u32 != f->match.daddr.as_u32) - { - f->l3_csum_delta = - ip_csum_add_even (f->l3_csum_delta, f->rewrite.daddr.as_u32); - f->l3_csum_delta = - ip_csum_sub_even (f->l3_csum_delta, f->match.daddr.as_u32); - } - else - { - f->rewrite.daddr.as_u32 = f->match.daddr.as_u32; - } - if (f->ops & NAT_FLOW_OP_SPORT_REWRITE && f->rewrite.sport != f->match.sport) - { - f->l4_csum_delta = ip_csum_add_even (f->l4_csum_delta, f->rewrite.sport); - f->l4_csum_delta = ip_csum_sub_even (f->l4_csum_delta, f->match.sport); - } - else - { - f->rewrite.sport = f->match.sport; - } - if (f->ops & NAT_FLOW_OP_DPORT_REWRITE && f->rewrite.dport != f->match.dport) - { - f->l4_csum_delta = ip_csum_add_even (f->l4_csum_delta, f->rewrite.dport); - f->l4_csum_delta = ip_csum_sub_even (f->l4_csum_delta, f->match.dport); - } - else - { - f->rewrite.dport = f->match.dport; - } - if (f->ops & NAT_FLOW_OP_ICMP_ID_REWRITE && - f->rewrite.icmp_id != f->match.icmp_id) - { - f->l4_csum_delta = - ip_csum_add_even (f->l4_csum_delta, f->rewrite.icmp_id); - f->l4_csum_delta = ip_csum_sub_even (f->l4_csum_delta, f->match.icmp_id); - } - else - { - f->rewrite.icmp_id = f->match.icmp_id; - } - if (f->ops & NAT_FLOW_OP_TXFIB_REWRITE) - { - } - else - { - f->rewrite.fib_index = f->match.fib_index; - } -} - -static_always_inline int nat_6t_flow_icmp_translate (snat_main_t *sm, - vlib_buffer_t *b, - ip4_header_t *ip, - nat_6t_flow_t *f); - -static_always_inline void -nat_6t_flow_ip4_translate (snat_main_t *sm, vlib_buffer_t *b, ip4_header_t *ip, - nat_6t_flow_t *f, nat_protocol_t proto, - int is_icmp_inner_ip4) -{ - udp_header_t *udp = ip4_next_header (ip); - tcp_header_t *tcp = (tcp_header_t *) udp; - - if ((NAT_PROTOCOL_TCP == proto || NAT_PROTOCOL_UDP == proto) && - !vnet_buffer (b)->ip.reass.is_non_first_fragment) - { - if (!is_icmp_inner_ip4) - { // regular case - ip->src_address = f->rewrite.saddr; - ip->dst_address = f->rewrite.daddr; - udp->src_port = f->rewrite.sport; - udp->dst_port = f->rewrite.dport; - } - else - { // icmp inner ip4 - reversed saddr/daddr - ip->src_address = f->rewrite.daddr; - ip->dst_address = f->rewrite.saddr; - udp->src_port = f->rewrite.dport; - udp->dst_port = f->rewrite.sport; - } - - if (NAT_PROTOCOL_TCP == proto) - { - ip_csum_t tcp_sum = tcp->checksum; - tcp_sum = ip_csum_sub_even (tcp_sum, f->l3_csum_delta); - tcp_sum = ip_csum_sub_even (tcp_sum, f->l4_csum_delta); - mss_clamping (sm->mss_clamping, tcp, &tcp_sum); - tcp->checksum = ip_csum_fold (tcp_sum); - } - else if (proto == NAT_PROTOCOL_UDP && udp->checksum) - { - ip_csum_t udp_sum = udp->checksum; - udp_sum = ip_csum_sub_even (udp_sum, f->l3_csum_delta); - udp_sum = ip_csum_sub_even (udp_sum, f->l4_csum_delta); - udp->checksum = ip_csum_fold (udp_sum); - } - } - else - { - if (!is_icmp_inner_ip4) - { // regular case - ip->src_address = f->rewrite.saddr; - ip->dst_address = f->rewrite.daddr; - } - else - { // icmp inner ip4 - reversed saddr/daddr - ip->src_address = f->rewrite.daddr; - ip->dst_address = f->rewrite.saddr; - } - } - - ip_csum_t ip_sum = ip->checksum; - ip_sum = ip_csum_sub_even (ip_sum, f->l3_csum_delta); - ip->checksum = ip_csum_fold (ip_sum); - ASSERT (ip->checksum == ip4_header_checksum (ip)); -} - -static_always_inline int -nat_6t_flow_icmp_translate (snat_main_t *sm, vlib_buffer_t *b, - ip4_header_t *ip, nat_6t_flow_t *f) -{ - if (IP_PROTOCOL_ICMP != ip->protocol) - return NAT_ED_TRNSL_ERR_TRANSLATION_FAILED; - - icmp46_header_t *icmp = ip4_next_header (ip); - icmp_echo_header_t *echo = (icmp_echo_header_t *) (icmp + 1); - - if ((!vnet_buffer (b)->ip.reass.is_non_first_fragment)) - { - if (icmp->checksum == 0) - icmp->checksum = 0xffff; - - if (!icmp_type_is_error_message (icmp->type)) - { - if ((f->ops & NAT_FLOW_OP_ICMP_ID_REWRITE) && - (f->rewrite.icmp_id != echo->identifier)) - { - ip_csum_t sum = icmp->checksum; - sum = ip_csum_update (sum, echo->identifier, f->rewrite.icmp_id, - icmp_echo_header_t, - identifier /* changed member */); - echo->identifier = f->rewrite.icmp_id; - icmp->checksum = ip_csum_fold (sum); - } - } - else - { - // errors are not fragmented - ip4_header_t *inner_ip = (ip4_header_t *) (echo + 1); - - if (!ip4_header_checksum_is_valid (inner_ip)) - { - return NAT_ED_TRNSL_ERR_TRANSLATION_FAILED; - } - - nat_protocol_t inner_proto = - ip_proto_to_nat_proto (inner_ip->protocol); - - ip_csum_t icmp_sum = icmp->checksum; - - switch (inner_proto) - { - case NAT_PROTOCOL_UDP: - case NAT_PROTOCOL_TCP: - nat_6t_flow_ip4_translate (sm, b, inner_ip, f, inner_proto, - 1 /* is_icmp_inner_ip4 */); - icmp_sum = ip_csum_sub_even (icmp_sum, f->l3_csum_delta); - icmp->checksum = ip_csum_fold (icmp_sum); - break; - case NAT_PROTOCOL_ICMP: - if (f->ops & NAT_FLOW_OP_ICMP_ID_REWRITE) - { - icmp46_header_t *inner_icmp = ip4_next_header (inner_ip); - icmp_echo_header_t *inner_echo = - (icmp_echo_header_t *) (inner_icmp + 1); - if (f->rewrite.icmp_id != inner_echo->identifier) - { - ip_csum_t sum = icmp->checksum; - sum = ip_csum_update ( - sum, inner_echo->identifier, f->rewrite.icmp_id, - icmp_echo_header_t, identifier /* changed member */); - icmp->checksum = ip_csum_fold (sum); - ip_csum_t inner_sum = inner_icmp->checksum; - inner_sum = ip_csum_update ( - sum, inner_echo->identifier, f->rewrite.icmp_id, - icmp_echo_header_t, identifier /* changed member */); - inner_icmp->checksum = ip_csum_fold (inner_sum); - inner_echo->identifier = f->rewrite.icmp_id; - } - } - break; - default: - clib_warning ("unexpected NAT protocol value `%d'", inner_proto); - return NAT_ED_TRNSL_ERR_TRANSLATION_FAILED; - } - } - } - return NAT_ED_TRNSL_ERR_SUCCESS; -} - -nat_translation_error_e -nat_6t_flow_buf_translate (snat_main_t *sm, vlib_buffer_t *b, ip4_header_t *ip, - nat_6t_flow_t *f, nat_protocol_t proto, - int is_output_feature) -{ - if (!is_output_feature && f->ops & NAT_FLOW_OP_TXFIB_REWRITE) - { - vnet_buffer (b)->sw_if_index[VLIB_TX] = f->rewrite.fib_index; - } - - nat_6t_flow_ip4_translate (sm, b, ip, f, proto, 0 /* is_icmp_inner_ip4 */); - - if (NAT_PROTOCOL_ICMP == proto) - { - return nat_6t_flow_icmp_translate (sm, b, ip, f); - } - - return NAT_ED_TRNSL_ERR_SUCCESS; -} - -u8 * -format_nat_6t (u8 *s, va_list *args) -{ - nat_6t_t *t = va_arg (*args, nat_6t_t *); - - s = format (s, "saddr %U sport %u daddr %U dport %u proto %U fib_idx %u", - format_ip4_address, t->saddr.as_u8, - clib_net_to_host_u16 (t->sport), format_ip4_address, - t->daddr.as_u8, clib_net_to_host_u16 (t->dport), - format_ip_protocol, t->proto, t->fib_index); - return s; -} - -u8 * -format_nat_ed_translation_error (u8 *s, va_list *args) -{ - nat_translation_error_e e = va_arg (*args, nat_translation_error_e); - - switch (e) - { - case NAT_ED_TRNSL_ERR_SUCCESS: - s = format (s, "success"); - break; - case NAT_ED_TRNSL_ERR_TRANSLATION_FAILED: - s = format (s, "translation-failed"); - break; - case NAT_ED_TRNSL_ERR_FLOW_MISMATCH: - s = format (s, "flow-mismatch"); - break; - } - return s; -} - -u8 * -format_nat_6t_flow (u8 *s, va_list *args) -{ - nat_6t_flow_t *f = va_arg (*args, nat_6t_flow_t *); - - s = format (s, "match: %U ", format_nat_6t, &f->match); - int r = 0; - if (f->ops & NAT_FLOW_OP_SADDR_REWRITE) - { - s = format (s, "rewrite: saddr %U ", format_ip4_address, - f->rewrite.saddr.as_u8); - r = 1; - } - if (f->ops & NAT_FLOW_OP_SPORT_REWRITE) - { - if (!r) - { - s = format (s, "rewrite: "); - r = 1; - } - s = format (s, "sport %u ", clib_net_to_host_u16 (f->rewrite.sport)); - } - if (f->ops & NAT_FLOW_OP_DADDR_REWRITE) - { - if (!r) - { - s = format (s, "rewrite: "); - r = 1; - } - s = format (s, "daddr %U ", format_ip4_address, f->rewrite.daddr.as_u8); - } - if (f->ops & NAT_FLOW_OP_DPORT_REWRITE) - { - if (!r) - { - s = format (s, "rewrite: "); - r = 1; - } - s = format (s, "dport %u ", clib_net_to_host_u16 (f->rewrite.dport)); - } - if (f->ops & NAT_FLOW_OP_ICMP_ID_REWRITE) - { - if (!r) - { - s = format (s, "rewrite: "); - r = 1; - } - s = format (s, "icmp-id %u ", clib_net_to_host_u16 (f->rewrite.icmp_id)); - } - if (f->ops & NAT_FLOW_OP_TXFIB_REWRITE) - { - if (!r) - { - s = format (s, "rewrite: "); - r = 1; - } - s = format (s, "txfib %u ", f->rewrite.fib_index); - } - return s; -} - -/* - * fd.io coding-style-patch-verification: ON - * - * Local Variables: - * eval: (c-set-style "gnu") - * End: - */ diff --git a/src/plugins/nat/nat.h b/src/plugins/nat/nat.h deleted file mode 100644 index c1dc31e8710..00000000000 --- a/src/plugins/nat/nat.h +++ /dev/null @@ -1,1558 +0,0 @@ -/* - * Copyright (c) 2020 Cisco and/or its affiliates. - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/** - * @file nat.c - * NAT plugin global declarations - */ -#ifndef __included_nat_h__ -#define __included_nat_h__ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* default number of worker handoff frame queue elements */ -#define NAT_FQ_NELTS_DEFAULT 64 - -/* NAT buffer flags */ -#define SNAT_FLAG_HAIRPINNING (1 << 0) - -/* NAT44 API Configuration flags */ -#define foreach_nat44_config_flag \ - _(0x00, IS_ENDPOINT_INDEPENDENT) \ - _(0x01, IS_ENDPOINT_DEPENDENT) \ - _(0x02, IS_STATIC_MAPPING_ONLY) \ - _(0x04, IS_CONNECTION_TRACKING) \ - _(0x08, IS_OUT2IN_DPO) - -typedef enum nat44_config_flags_t_ -{ -#define _(n,f) NAT44_API_##f = n, - foreach_nat44_config_flag -#undef _ -} nat44_config_flags_t; - -typedef struct -{ - /* nat44 plugin features */ - u8 static_mapping_only; - u8 connection_tracking; - u8 endpoint_dependent; - u8 out2in_dpo; - - u32 inside_vrf; - u32 outside_vrf; - - /* maximum number of users */ - u32 users; - - /* maximum number of sessions */ - u32 sessions; - - /* maximum number of ssessions per user */ - u32 user_sessions; -} nat44_config_t; - -typedef enum -{ - NAT_NEXT_DROP, - NAT_NEXT_ICMP_ERROR, - NAT_NEXT_IN2OUT_ED_FAST_PATH, - NAT_NEXT_IN2OUT_ED_SLOW_PATH, - NAT_NEXT_IN2OUT_ED_OUTPUT_FAST_PATH, - NAT_NEXT_IN2OUT_ED_OUTPUT_SLOW_PATH, - NAT_NEXT_OUT2IN_ED_FAST_PATH, - NAT_NEXT_OUT2IN_ED_SLOW_PATH, - NAT_NEXT_IN2OUT_CLASSIFY, - NAT_NEXT_OUT2IN_CLASSIFY, - NAT_N_NEXT, -} nat_next_t; - -typedef struct -{ - u32 next_index; - u32 arc_next_index; -} nat_pre_trace_t; - -/* user (internal host) key */ -typedef struct -{ - union - { - struct - { - ip4_address_t addr; - u32 fib_index; - }; - u64 as_u64; - }; -} snat_user_key_t; - -/* External address and port allocation modes */ -#define foreach_nat_addr_and_port_alloc_alg \ - _(0, DEFAULT, "default") \ - _(1, MAPE, "map-e") \ - _(2, RANGE, "port-range") - -typedef enum -{ -#define _(v, N, s) NAT_ADDR_AND_PORT_ALLOC_ALG_##N = v, - foreach_nat_addr_and_port_alloc_alg -#undef _ -} nat_addr_and_port_alloc_alg_t; - -/* Session state */ -#define foreach_snat_session_state \ - _(0, UNKNOWN, "unknown") \ - _(1, UDP_ACTIVE, "udp-active") \ - _(2, TCP_SYN_SENT, "tcp-syn-sent") \ - _(3, TCP_ESTABLISHED, "tcp-established") \ - _(4, TCP_FIN_WAIT, "tcp-fin-wait") \ - _(5, TCP_CLOSE_WAIT, "tcp-close-wait") \ - _(6, TCP_CLOSING, "tcp-closing") \ - _(7, TCP_LAST_ACK, "tcp-last-ack") \ - _(8, TCP_CLOSED, "tcp-closed") \ - _(9, ICMP_ACTIVE, "icmp-active") - -typedef enum -{ -#define _(v, N, s) SNAT_SESSION_##N = v, - foreach_snat_session_state -#undef _ -} snat_session_state_t; - -#define foreach_nat_in2out_ed_error \ -_(UNSUPPORTED_PROTOCOL, "unsupported protocol") \ -_(OUT_OF_PORTS, "out of ports") \ -_(BAD_ICMP_TYPE, "unsupported ICMP type") \ -_(MAX_SESSIONS_EXCEEDED, "maximum sessions exceeded") \ -_(NON_SYN, "non-SYN packet try to create session") \ -_(TCP_CLOSED, "drops due to TCP in transitory timeout") - -typedef enum -{ -#define _(sym,str) NAT_IN2OUT_ED_ERROR_##sym, - foreach_nat_in2out_ed_error -#undef _ - NAT_IN2OUT_ED_N_ERROR, -} nat_in2out_ed_error_t; - -#define foreach_nat_out2in_ed_error \ - _ (UNSUPPORTED_PROTOCOL, "unsupported protocol") \ - _ (OUT_OF_PORTS, "out of ports") \ - _ (BAD_ICMP_TYPE, "unsupported ICMP type") \ - _ (NO_TRANSLATION, "no translation") \ - _ (MAX_SESSIONS_EXCEEDED, "maximum sessions exceeded") \ - _ (MAX_USER_SESS_EXCEEDED, "max user sessions exceeded") \ - _ (CANNOT_CREATE_USER, "cannot create NAT user") \ - _ (NON_SYN, "non-SYN packet try to create session") \ - _ (TCP_CLOSED, "drops due to TCP in transitory timeout") \ - _ (HASH_ADD_FAILED, "hash table add failed") - -typedef enum -{ -#define _(sym,str) NAT_OUT2IN_ED_ERROR_##sym, - foreach_nat_out2in_ed_error -#undef _ - NAT_OUT2IN_ED_N_ERROR, -} nat_out2in_ed_error_t; - - -/* Endpoint dependent TCP session state */ -#define NAT44_SES_I2O_FIN 1 -#define NAT44_SES_O2I_FIN 2 -#define NAT44_SES_I2O_FIN_ACK 4 -#define NAT44_SES_O2I_FIN_ACK 8 -#define NAT44_SES_I2O_SYN 16 -#define NAT44_SES_O2I_SYN 32 -#define NAT44_SES_RST 64 - -/* Session flags */ -#define SNAT_SESSION_FLAG_STATIC_MAPPING (1 << 0) -#define SNAT_SESSION_FLAG_UNKNOWN_PROTO (1 << 1) -#define SNAT_SESSION_FLAG_LOAD_BALANCING (1 << 2) -#define SNAT_SESSION_FLAG_TWICE_NAT (1 << 3) -#define SNAT_SESSION_FLAG_ENDPOINT_DEPENDENT (1 << 4) -#define SNAT_SESSION_FLAG_FWD_BYPASS (1 << 5) -#define SNAT_SESSION_FLAG_AFFINITY (1 << 6) -#define SNAT_SESSION_FLAG_EXACT_ADDRESS (1 << 7) -#define SNAT_SESSION_FLAG_HAIRPINNING (1 << 8) - -/* NAT interface flags */ -#define NAT_INTERFACE_FLAG_IS_INSIDE 1 -#define NAT_INTERFACE_FLAG_IS_OUTSIDE 2 - -/* Static mapping flags */ -#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 -#define NAT_STATIC_MAPPING_FLAG_EXACT_ADDRESS 16 - -/* *INDENT-OFF* */ -typedef CLIB_PACKED(struct -{ - // number of sessions in this vrf - u32 ses_count; - - u32 rx_fib_index; - u32 tx_fib_index; - - // is this vrf expired - u8 expired; -}) per_vrf_sessions_t; -/* *INDENT-ON* */ - -typedef struct -{ - ip4_address_t saddr, daddr; - u32 fib_index; - u16 sport, dport; - u16 icmp_id; - u8 proto; -} nat_6t_t; - -typedef struct -{ -#define NAT_FLOW_OP_SADDR_REWRITE (1 << 1) -#define NAT_FLOW_OP_SPORT_REWRITE (1 << 2) -#define NAT_FLOW_OP_DADDR_REWRITE (1 << 3) -#define NAT_FLOW_OP_DPORT_REWRITE (1 << 4) -#define NAT_FLOW_OP_ICMP_ID_REWRITE (1 << 5) -#define NAT_FLOW_OP_TXFIB_REWRITE (1 << 6) - int ops; - nat_6t_t match; - nat_6t_t rewrite; - uword l3_csum_delta; - uword l4_csum_delta; -} nat_6t_flow_t; - -always_inline void -nat_6t_flow_saddr_rewrite_set (nat_6t_flow_t *f, u32 saddr) -{ - f->ops |= NAT_FLOW_OP_SADDR_REWRITE; - f->rewrite.saddr.as_u32 = saddr; -} - -always_inline void -nat_6t_flow_daddr_rewrite_set (nat_6t_flow_t *f, u32 daddr) -{ - f->ops |= NAT_FLOW_OP_DADDR_REWRITE; - f->rewrite.daddr.as_u32 = daddr; -} - -always_inline void -nat_6t_flow_sport_rewrite_set (nat_6t_flow_t *f, u32 sport) -{ - f->ops |= NAT_FLOW_OP_SPORT_REWRITE; - f->rewrite.sport = sport; -} - -always_inline void -nat_6t_flow_dport_rewrite_set (nat_6t_flow_t *f, u32 dport) -{ - f->ops |= NAT_FLOW_OP_DPORT_REWRITE; - f->rewrite.dport = dport; -} - -always_inline void -nat_6t_flow_txfib_rewrite_set (nat_6t_flow_t *f, u32 tx_fib_index) -{ - f->ops |= NAT_FLOW_OP_TXFIB_REWRITE; - f->rewrite.fib_index = tx_fib_index; -} - -always_inline void -nat_6t_flow_icmp_id_rewrite_set (nat_6t_flow_t *f, u16 id) -{ - f->ops |= NAT_FLOW_OP_ICMP_ID_REWRITE; - f->rewrite.icmp_id = id; -} - -/* *INDENT-OFF* */ -typedef CLIB_PACKED(struct -{ - /* Outside network tuple */ - struct - { - ip4_address_t addr; - u32 fib_index; - u16 port; - } out2in; - - /* Inside network tuple */ - struct - { - ip4_address_t addr; - u32 fib_index; - u16 port; - } in2out; - - nat_protocol_t nat_proto; - - nat_6t_flow_t i2o; - nat_6t_flow_t o2i; - - /* Flags */ - u32 flags; - - /* Per-user translations */ - u32 per_user_index; - u32 per_user_list_head_index; - - /* head of LRU list in which this session is tracked */ - u32 lru_head_index; - /* index in global LRU list */ - u32 lru_index; - f64 last_lru_update; - - /* Last heard timer */ - f64 last_heard; - - /* Last HA refresh */ - f64 ha_last_refreshed; - - /* Counters */ - u64 total_bytes; - u32 total_pkts; - - /* External host address and port */ - ip4_address_t ext_host_addr; - u16 ext_host_port; - - /* External host address and port after translation */ - ip4_address_t ext_host_nat_addr; - u16 ext_host_nat_port; - - /* TCP session state */ - u8 state; - u32 i2o_fin_seq; - u32 o2i_fin_seq; - u64 tcp_closed_timestamp; - - /* user index */ - u32 user_index; - - /* per vrf sessions index */ - u32 per_vrf_sessions_index; - -}) snat_session_t; -/* *INDENT-ON* */ - -typedef struct -{ - ip4_address_t addr; - u32 fib_index; - u32 sessions_per_user_list_head_index; - u32 nsessions; - u32 nstaticsessions; -} snat_user_t; - -typedef struct -{ - ip4_address_t addr; - u32 fib_index; -/* *INDENT-OFF* */ -#define _(N, i, n, s) \ - u32 busy_##n##_ports; \ - u32 * busy_##n##_ports_per_thread; \ - u32 busy_##n##_port_refcounts[65535]; - foreach_nat_protocol -#undef _ -/* *INDENT-ON* */ -} snat_address_t; - -typedef struct -{ - u32 fib_index; - u32 ref_count; -} nat_fib_t; - -typedef struct -{ - u32 fib_index; - u32 refcount; -} nat_outside_fib_t; - -typedef struct -{ - /* backend IP address */ - ip4_address_t addr; - /* backend port number */ - u16 port; - /* probability of the backend to be randomly matched */ - u8 probability; - u8 prefix; - /* backend FIB table */ - u32 vrf_id; - u32 fib_index; -} nat44_lb_addr_port_t; - -typedef enum -{ - /* twice-nat disabled */ - TWICE_NAT_DISABLED, - /* twice-nat enabled */ - TWICE_NAT, - /* twice-nat only when src IP equals dst IP after translation */ - TWICE_NAT_SELF, -} twice_nat_type_t; - -typedef enum -{ - /* no load-balancing */ - NO_LB_NAT, - /* load-balancing */ - LB_NAT, - /* load-balancing with affinity */ - AFFINITY_LB_NAT, -} lb_nat_type_t; - -typedef struct -{ - /* prefered pool address */ - ip4_address_t pool_addr; - /* local IP address */ - ip4_address_t local_addr; - /* external IP address */ - ip4_address_t external_addr; - /* local port */ - u16 local_port; - /* external port */ - u16 external_port; - /* is twice-nat */ - twice_nat_type_t twice_nat; - /* local FIB table */ - u32 vrf_id; - u32 fib_index; - /* protocol */ - nat_protocol_t proto; - /* 0 = disabled, otherwise client IP affinity sticky time in seconds */ - u32 affinity; - /* worker threads used by backends/local host */ - u32 *workers; - /* opaque string tag */ - u8 *tag; - /* backends for load-balancing mode */ - nat44_lb_addr_port_t *locals; - /* affinity per service lis */ - u32 affinity_per_service_list_head_index; - /* flags */ - u32 flags; -} snat_static_mapping_t; - -typedef struct -{ - u32 sw_if_index; - u8 flags; -} snat_interface_t; - -typedef struct -{ - ip4_address_t l_addr; - ip4_address_t pool_addr; - u16 l_port; - u16 e_port; - u32 sw_if_index; - u32 vrf_id; - nat_protocol_t proto; - u32 flags; - int addr_only; - int twice_nat; - int out2in_only; - int identity_nat; - int exact; - u8 *tag; -} snat_static_map_resolve_t; - -typedef struct -{ - /* Find-a-user => src address lookup */ - clib_bihash_8_8_t user_hash; - - /* User pool */ - snat_user_t *users; - - /* Session pool */ - snat_session_t *sessions; - - /* Pool of doubly-linked list elements */ - dlist_elt_t *list_pool; - - /* LRU session list - head is stale, tail is fresh */ - dlist_elt_t *lru_pool; - u32 tcp_trans_lru_head_index; - u32 tcp_estab_lru_head_index; - u32 udp_lru_head_index; - u32 icmp_lru_head_index; - u32 unk_proto_lru_head_index; - - /* NAT thread index */ - u32 snat_thread_index; - - /* real thread index */ - u32 thread_index; - - per_vrf_sessions_t *per_vrf_sessions_vec; - -} snat_main_per_thread_data_t; - -struct snat_main_s; - -/* ICMP session match function */ -typedef u32 (snat_icmp_match_function_t) ( - struct snat_main_s *sm, vlib_node_runtime_t *node, u32 thread_index, - vlib_buffer_t *b0, ip4_header_t *ip0, ip4_address_t *addr, u16 *port, - u32 *fib_index, nat_protocol_t *proto, snat_session_t **s_out, - u8 *dont_translate); - -/* Return worker thread index for given packet */ -typedef u32 (snat_get_worker_in2out_function_t) (ip4_header_t * ip, - u32 rx_fib_index, - u8 is_output); - -typedef u32 (snat_get_worker_out2in_function_t) (vlib_buffer_t * b, - ip4_header_t * ip, - u32 rx_fib_index, - u8 is_output); - -/* NAT address and port allocation function */ -typedef int (nat_alloc_out_addr_and_port_function_t) (snat_address_t * - addresses, - u32 fib_index, - u32 thread_index, - nat_protocol_t proto, - ip4_address_t * addr, - u16 * port, - u16 port_per_thread, - u32 snat_thread_index); - -#define foreach_nat_counter _ (tcp) _ (udp) _ (icmp) _ (other) _ (drops) - -typedef struct snat_main_s -{ - /* ICMP session match functions */ - snat_icmp_match_function_t *icmp_match_in2out_cb; - snat_icmp_match_function_t *icmp_match_out2in_cb; - - /* Thread settings */ - u32 num_workers; - u32 first_worker_index; - u32 *workers; - snat_get_worker_in2out_function_t *worker_in2out_cb; - snat_get_worker_out2in_function_t *worker_out2in_cb; - u16 port_per_thread; - - /* Per thread data */ - snat_main_per_thread_data_t *per_thread_data; - - /* Find a static mapping by local */ - clib_bihash_8_8_t static_mapping_by_local; - - /* Find a static mapping by external */ - clib_bihash_8_8_t static_mapping_by_external; - - /* Static mapping pool */ - snat_static_mapping_t *static_mappings; - - /* Endpoint independent lookup tables */ - clib_bihash_8_8_t in2out; - clib_bihash_8_8_t out2in; - - /* Endpoint dependent lookup table */ - clib_bihash_16_8_t flow_hash; - - /* Interface pool */ - snat_interface_t *interfaces; - snat_interface_t *output_feature_interfaces; - - /* Vector of outside addresses */ - snat_address_t *addresses; - /* Address and port allocation function */ - nat_alloc_out_addr_and_port_function_t *alloc_addr_and_port; - /* Address and port allocation type */ - nat_addr_and_port_alloc_alg_t addr_and_port_alloc_alg; - /* Port set parameters (MAP-E) */ - u8 psid_offset; - u8 psid_length; - u16 psid; - /* Port range parameters */ - u16 start_port; - u16 end_port; - - /* vector of fibs */ - nat_fib_t *fibs; - - /* vector of outside fibs */ - nat_outside_fib_t *outside_fibs; - - /* Vector of twice NAT addresses for external hosts */ - snat_address_t *twice_nat_addresses; - - /* sw_if_indices whose intfc addresses should be auto-added */ - u32 *auto_add_sw_if_indices; - u32 *auto_add_sw_if_indices_twice_nat; - - /* vector of interface address static mappings to resolve. */ - snat_static_map_resolve_t *to_resolve; - - /* Randomize port allocation order */ - u32 random_seed; - - /* Worker handoff frame-queue index */ - u32 fq_in2out_index; - u32 fq_in2out_output_index; - u32 fq_out2in_index; - - /* node indexes */ - u32 error_node_index; - - /* handoff fq nodes */ - u32 handoff_out2in_index; - u32 handoff_in2out_index; - u32 handoff_in2out_output_index; - - /* respect feature arc nodes */ - u32 pre_out2in_node_index; - u32 pre_in2out_node_index; - - u32 out2in_node_index; - u32 in2out_node_index; - u32 in2out_output_node_index; - - u32 in2out_fast_node_index; - u32 in2out_slowpath_node_index; - u32 in2out_slowpath_output_node_index; - u32 out2in_fast_node_index; - - u32 ei_out2in_node_index; - u32 ei_in2out_node_index; - u32 ei_in2out_output_node_index; - - u32 ed_out2in_node_index; - u32 ed_in2out_node_index; - u32 ed_in2out_output_node_index; - - u32 ed_in2out_slowpath_node_index; - u32 ed_out2in_slowpath_node_index; - - u32 hairpinning_node_index; - u32 hairpin_dst_node_index; - u32 hairpin_src_node_index; - - nat44_config_t rconfig; - //nat44_config_t cconfig; - - /* If forwarding is enabled */ - u8 forwarding_enabled; - - /* Config parameters */ - u8 endpoint_dependent; - - u8 out2in_dpo; - /* static mapping config */ - u8 static_mapping_only; - u8 static_mapping_connection_tracking; - - /* Is translation memory size calculated or user defined */ - u8 translation_memory_size_set; - - u32 translation_buckets; - u32 max_translations_per_thread; - u32 *max_translations_per_fib; - u32 max_users_per_thread; - u32 user_buckets; - u32 max_translations_per_user; - - u32 outside_vrf_id; - u32 outside_fib_index; - u32 inside_vrf_id; - u32 inside_fib_index; - - nat_timeouts_t timeouts; - - /* TCP MSS clamping */ - u16 mss_clamping; - - /* counters */ - vlib_simple_counter_main_t total_users; - vlib_simple_counter_main_t total_sessions; - vlib_simple_counter_main_t user_limit_reached; - -#define _(x) vlib_simple_counter_main_t x; - struct - { - struct - { - struct - { - foreach_nat_counter; - } in2out; - - struct - { - foreach_nat_counter; - } out2in; - - struct - { - foreach_nat_counter; - } in2out_ed; - - struct - { - foreach_nat_counter; - } out2in_ed; - } fastpath; - - struct - { - struct - { - foreach_nat_counter; - } in2out; - - struct - { - foreach_nat_counter; - } out2in; - - struct - { - foreach_nat_counter; - } in2out_ed; - - struct - { - foreach_nat_counter; - } out2in_ed; - } slowpath; - - vlib_simple_counter_main_t hairpinning; - } counters; -#undef _ - - /* API message ID base */ - u16 msg_id_base; - - /* log class */ - vlib_log_class_t log_class; - /* logging level */ - u8 log_level; - - /* convenience */ - api_main_t *api_main; - ip4_main_t *ip4_main; - ip_lookup_main_t *ip4_lookup_main; - - fib_source_t fib_src_hi; - fib_source_t fib_src_low; - - /* pat - dynamic mapping enabled or conneciton tracking */ - u8 pat; - - /* number of worker handoff frame queue elements */ - u32 frame_queue_nelts; - - /* nat44 plugin enabled */ - u8 enabled; - - vnet_main_t *vnet_main; - - u32 nat44_in2out_hairpinning_finish_ip4_lookup_node_fq_index; - u32 nat44_in2out_hairpinning_finish_interface_output_node_fq_index; - u32 nat44_hairpinning_fq_index; - u32 snat_hairpin_dst_fq_index; -} snat_main_t; - -typedef struct -{ - u32 thread_index; - f64 now; -} nat44_is_idle_session_ctx_t; - -typedef struct -{ - u32 cached_sw_if_index; - u32 cached_ip4_address; -} snat_runtime_t; - -extern snat_main_t snat_main; - -// nat pre ed next_node feature classification -extern vlib_node_registration_t nat_default_node; -extern vlib_node_registration_t nat_pre_in2out_node; -extern vlib_node_registration_t nat_pre_out2in_node; - -extern vlib_node_registration_t snat_in2out_node; -extern vlib_node_registration_t snat_in2out_output_node; -extern vlib_node_registration_t snat_out2in_node; -extern vlib_node_registration_t snat_in2out_worker_handoff_node; -extern vlib_node_registration_t snat_in2out_output_worker_handoff_node; -extern vlib_node_registration_t snat_out2in_worker_handoff_node; -extern vlib_node_registration_t nat44_ed_in2out_node; -extern vlib_node_registration_t nat44_ed_in2out_output_node; -extern vlib_node_registration_t nat44_ed_out2in_node; - -extern fib_source_t nat_fib_src_hi; -extern fib_source_t nat_fib_src_low; - -/* format functions */ -format_function_t format_snat_user; -format_function_t format_snat_static_mapping; -format_function_t format_snat_static_map_to_resolve; -format_function_t format_snat_session; -format_function_t format_snat_key; -format_function_t format_static_mapping_key; -format_function_t format_nat_protocol; -format_function_t format_nat_addr_and_port_alloc_alg; -/* unformat functions */ -unformat_function_t unformat_nat_protocol; - -/** \brief Check if SNAT session is created from static mapping. - @param s SNAT session - @return 1 if SNAT session is created from static mapping otherwise 0 -*/ -#define snat_is_session_static(s) (s->flags & SNAT_SESSION_FLAG_STATIC_MAPPING) - -/** \brief Check if SNAT session for unknown protocol. - @param s SNAT session - @return 1 if SNAT session for unknown protocol otherwise 0 -*/ -#define snat_is_unk_proto_session(s) (s->flags & SNAT_SESSION_FLAG_UNKNOWN_PROTO) - -/** \brief Check if NAT session is twice NAT. - @param s NAT session - @return 1 if NAT session is twice NAT -*/ -#define is_twice_nat_session(s) (s->flags & SNAT_SESSION_FLAG_TWICE_NAT) - -/** \brief Check if NAT session is load-balancing. - @param s NAT session - @return 1 if NAT session is load-balancing -*/ -#define is_lb_session(s) (s->flags & SNAT_SESSION_FLAG_LOAD_BALANCING) - -/** \brief Check if NAT session is forwarding bypass. - @param s NAT session - @return 1 if NAT session is load-balancing -*/ -#define is_fwd_bypass_session(s) (s->flags & SNAT_SESSION_FLAG_FWD_BYPASS) - -/** \brief Check if NAT session is endpoint dependent. - @param s NAT session - @return 1 if NAT session is endpoint dependent -*/ -#define is_ed_session(s) (s->flags & SNAT_SESSION_FLAG_ENDPOINT_DEPENDENT) - -/** \brief Check if NAT session has affinity record. - @param s NAT session - @return 1 if NAT session has affinity record -*/ -#define is_affinity_sessions(s) (s->flags & SNAT_SESSION_FLAG_AFFINITY) - -/** \brief Check if exact pool address should be used. - @param s SNAT session - @return 1 if exact pool address or 0 -*/ -#define is_exact_address_session(s) (s->flags & SNAT_SESSION_FLAG_EXACT_ADDRESS) - -/** \brief Check if NAT interface is inside. - @param i NAT interface - @return 1 if inside interface -*/ -#define nat_interface_is_inside(i) i->flags & NAT_INTERFACE_FLAG_IS_INSIDE - -/** \brief Check if NAT interface is outside. - @param i NAT interface - @return 1 if outside interface -*/ -#define nat_interface_is_outside(i) i->flags & NAT_INTERFACE_FLAG_IS_OUTSIDE - -/** \brief Check if NAT44 endpoint-dependent TCP session is closed. - @param s NAT session - @return 1 if session is closed -*/ -#define nat44_is_ses_closed(s) s->state == 0xf - -/** \brief Check if NAT static mapping is address only (1:1NAT). - @param sm NAT static mapping - @return 1 if 1:1NAT, 0 if 1:1NAPT -*/ -#define is_addr_only_static_mapping(sm) (sm->flags & NAT_STATIC_MAPPING_FLAG_ADDR_ONLY) - -/** \brief Check if NAT static mapping match only out2in direction. - @param sm NAT static mapping - @return 1 if rule match only out2in direction -*/ -#define is_out2in_only_static_mapping(sm) (sm->flags & NAT_STATIC_MAPPING_FLAG_OUT2IN_ONLY) - -/** \brief Check if NAT static mapping is identity NAT. - @param sm NAT static mapping - @return 1 if identity NAT -*/ -#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) - -/** \brief Check if exact pool address should be used. - @param s SNAT session - @return 1 if exact pool address or 0 -*/ -#define is_exact_address(s) (s->flags & NAT_STATIC_MAPPING_FLAG_EXACT_ADDRESS) - -/** \brief Check if client initiating TCP connection (received SYN from client) - @param t TCP header - @return 1 if client initiating TCP connection -*/ -always_inline bool -tcp_flags_is_init (u8 f) -{ - return (f & TCP_FLAG_SYN) && !(f & TCP_FLAG_ACK); -} - -/* logging */ -#define nat_log_err(...) \ - vlib_log(VLIB_LOG_LEVEL_ERR, snat_main.log_class, __VA_ARGS__) -#define nat_log_warn(...) \ - vlib_log(VLIB_LOG_LEVEL_WARNING, snat_main.log_class, __VA_ARGS__) -#define nat_log_notice(...) \ - vlib_log(VLIB_LOG_LEVEL_NOTICE, snat_main.log_class, __VA_ARGS__) -#define nat_log_info(...) \ - vlib_log(VLIB_LOG_LEVEL_INFO, snat_main.log_class, __VA_ARGS__) -#define nat_log_debug(...)\ - vlib_log(VLIB_LOG_LEVEL_DEBUG, snat_main.log_class, __VA_ARGS__) - -/* NAT API Logging Levels */ -#define foreach_nat_log_level \ - _(0x00, LOG_NONE) \ - _(0x01, LOG_ERROR) \ - _(0x02, LOG_WARNING) \ - _(0x03, LOG_NOTICE) \ - _(0x04, LOG_INFO) \ - _(0x05, LOG_DEBUG) - -typedef enum nat_log_level_t_ -{ -#define _(n,f) SNAT_##f = n, - foreach_nat_log_level -#undef _ -} nat_log_level_t; - -#define nat_elog(_level, _str) \ -do \ - { \ - snat_main_t *sm = &snat_main; \ - if (PREDICT_FALSE (sm->log_level >= _level)) \ - { \ - ELOG_TYPE_DECLARE (e) = \ - { \ - .format = "nat-msg " _str, \ - .format_args = "", \ - }; \ - ELOG_DATA (&vlib_global_main.elog_main, e); \ - } \ - } while (0); - -#define nat_elog_addr(_level, _str, _addr) \ -do \ - { \ - if (PREDICT_FALSE (sm->log_level >= _level)) \ - { \ - ELOG_TYPE_DECLARE (e) = \ - { \ - .format = "nat-msg " _str " %d.%d.%d.%d", \ - .format_args = "i1i1i1i1", \ - }; \ - CLIB_PACKED(struct \ - { \ - u8 oct1; \ - u8 oct2; \ - u8 oct3; \ - u8 oct4; \ - }) *ed; \ - ed = ELOG_DATA (&vlib_global_main.elog_main, e); \ - ed->oct4 = _addr >> 24; \ - ed->oct3 = _addr >> 16; \ - ed->oct2 = _addr >> 8; \ - ed->oct1 = _addr; \ - } \ - } while (0); - -#define nat_elog_debug_handoff(_str, _tid, _fib, _src, _dst) \ -do \ - { \ - if (PREDICT_FALSE (sm->log_level >= SNAT_LOG_DEBUG)) \ - { \ - ELOG_TYPE_DECLARE (e) = \ - { \ - .format = "nat-msg " _str " ip src: %d.%d.%d.%d dst: %d.%d.%d.%d" \ - " tid from: %d to: %d fib: %d", \ - .format_args = "i1i1i1i1i1i1i1i1i4i4i4", \ - }; \ - CLIB_PACKED(struct \ - { \ - u8 src_oct1; \ - u8 src_oct2; \ - u8 src_oct3; \ - u8 src_oct4; \ - u8 dst_oct1; \ - u8 dst_oct2; \ - u8 dst_oct3; \ - u8 dst_oct4; \ - u32 ftid; \ - u32 ttid; \ - u32 fib; \ - }) *ed; \ - ed = ELOG_DATA (&vlib_global_main.elog_main, e); \ - ed->src_oct1 = _src >> 24; \ - ed->src_oct2 = _src >> 16; \ - ed->src_oct3 = _src >> 8; \ - ed->src_oct4 = _src; \ - ed->dst_oct1 = _dst >> 24; \ - ed->dst_oct2 = _dst >> 16; \ - ed->dst_oct3 = _dst >> 8; \ - ed->dst_oct4 = _dst; \ - ed->ftid = vlib_get_thread_index (); \ - ed->ttid = _tid; \ - ed->fib = _fib; \ - } \ - } while (0); - -#define nat_elog_debug_handoff_v2(_str, _prt, _fib, _src, _dst) \ -do \ - { \ - if (PREDICT_FALSE (sm->log_level >= SNAT_LOG_DEBUG)) \ - { \ - ELOG_TYPE_DECLARE (e) = \ - { \ - .format = "nat-msg " _str " ip_src:%d.%d.%d.%d ip_dst:%d.%d.%d.%d" \ - " tid:%d prt:%d fib:%d", \ - .format_args = "i1i1i1i1i1i1i1i1i4i4i4", \ - }; \ - CLIB_PACKED(struct \ - { \ - u8 src_oct1; \ - u8 src_oct2; \ - u8 src_oct3; \ - u8 src_oct4; \ - u8 dst_oct1; \ - u8 dst_oct2; \ - u8 dst_oct3; \ - u8 dst_oct4; \ - u32 tid; \ - u32 prt; \ - u32 fib; \ - }) *ed; \ - ed = ELOG_DATA (&vlib_global_main.elog_main, e); \ - ed->src_oct1 = _src >> 24; \ - ed->src_oct2 = _src >> 16; \ - ed->src_oct3 = _src >> 8; \ - ed->src_oct4 = _src; \ - ed->dst_oct1 = _dst >> 24; \ - ed->dst_oct2 = _dst >> 16; \ - ed->dst_oct3 = _dst >> 8; \ - ed->dst_oct4 = _dst; \ - ed->tid = vlib_get_thread_index (); \ - ed->prt = _prt; \ - ed->fib = _fib; \ - } \ - } while (0); - -#define nat_elog_X1(_level, _fmt, _arg, _val1) \ -do \ - { \ - snat_main_t *sm = &snat_main; \ - if (PREDICT_FALSE (sm->log_level >= _level)) \ - { \ - ELOG_TYPE_DECLARE (e) = \ - { \ - .format = "nat-msg " _fmt, \ - .format_args = _arg, \ - }; \ - CLIB_PACKED(struct \ - { \ - typeof (_val1) val1; \ - }) *ed; \ - ed = ELOG_DATA (&vlib_global_main.elog_main, e); \ - ed->val1 = _val1; \ - } \ - } while (0); - -#define nat_elog_notice(nat_elog_str) \ - nat_elog(SNAT_LOG_INFO, "[notice] " nat_elog_str) -#define nat_elog_warn(nat_elog_str) \ - nat_elog(SNAT_LOG_WARNING, "[warning] " nat_elog_str) -#define nat_elog_err(nat_elog_str) \ - nat_elog(SNAT_LOG_ERROR, "[error] " nat_elog_str) -#define nat_elog_debug(nat_elog_str) \ - nat_elog(SNAT_LOG_DEBUG, "[debug] " nat_elog_str) -#define nat_elog_info(nat_elog_str) \ - nat_elog(SNAT_LOG_INFO, "[info] " nat_elog_str) - -#define nat_elog_notice_X1(nat_elog_fmt_str, nat_elog_fmt_arg, nat_elog_val1) \ - nat_elog_X1(SNAT_LOG_NOTICE, "[notice] " nat_elog_fmt_str, nat_elog_fmt_arg, nat_elog_val1) -#define nat_elog_warn_X1(nat_elog_fmt_str, nat_elog_fmt_arg, nat_elog_val1) \ - nat_elog_X1(SNAT_LOG_WARNING, "[warning] " nat_elog_fmt_str, nat_elog_fmt_arg, nat_elog_val1) -#define nat_elog_err_X1(nat_elog_fmt_str, nat_elog_fmt_arg, nat_elog_val1) \ - nat_elog_X1(SNAT_LOG_ERROR, "[error] " nat_elog_fmt_str, nat_elog_fmt_arg, nat_elog_val1) -#define nat_elog_debug_X1(nat_elog_fmt_str, nat_elog_fmt_arg, nat_elog_val1) \ - nat_elog_X1(SNAT_LOG_DEBUG, "[debug] " nat_elog_fmt_str, nat_elog_fmt_arg, nat_elog_val1) -#define nat_elog_info_X1(nat_elog_fmt_str, nat_elog_fmt_arg, nat_elog_val1) \ - nat_elog_X1(SNAT_LOG_INFO, "[info] " nat_elog_fmt_str, nat_elog_fmt_arg, nat_elog_val1) - -/* ICMP session match functions */ -u32 icmp_match_in2out_fast (snat_main_t *sm, vlib_node_runtime_t *node, - u32 thread_index, vlib_buffer_t *b0, - ip4_header_t *ip0, ip4_address_t *addr, u16 *port, - u32 *fib_index, nat_protocol_t *proto, - snat_session_t **s0, u8 *dont_translate); -u32 icmp_match_in2out_slow (snat_main_t *sm, vlib_node_runtime_t *node, - u32 thread_index, vlib_buffer_t *b0, - ip4_header_t *ip0, ip4_address_t *addr, u16 *port, - u32 *fib_index, nat_protocol_t *proto, - snat_session_t **s0, u8 *dont_translate); -u32 icmp_match_out2in_fast (snat_main_t *sm, vlib_node_runtime_t *node, - u32 thread_index, vlib_buffer_t *b0, - ip4_header_t *ip0, ip4_address_t *addr, u16 *port, - u32 *fib_index, nat_protocol_t *proto, - snat_session_t **s0, u8 *dont_translate); -u32 icmp_match_out2in_slow (snat_main_t *sm, vlib_node_runtime_t *node, - u32 thread_index, vlib_buffer_t *b0, - ip4_header_t *ip0, ip4_address_t *addr, u16 *port, - u32 *fib_index, nat_protocol_t *proto, - snat_session_t **s0, u8 *dont_translate); - -/* hairpinning functions */ -u32 snat_icmp_hairpinning (snat_main_t *sm, vlib_buffer_t *b0, - u32 thread_index, ip4_header_t *ip0, - icmp46_header_t *icmp0, u32 *required_thread_index); - -void nat_hairpinning_sm_unknown_proto (snat_main_t * sm, vlib_buffer_t * b, - ip4_header_t * ip); - -int snat_hairpinning (vlib_main_t *vm, vlib_node_runtime_t *node, - snat_main_t *sm, u32 thread_index, vlib_buffer_t *b0, - ip4_header_t *ip0, udp_header_t *udp0, - tcp_header_t *tcp0, u32 proto0, int do_trace, - u32 *required_thread_index); - -/* Call back functions for clib_bihash_add_or_overwrite_stale */ -int nat44_i2o_is_idle_session_cb (clib_bihash_kv_8_8_t * kv, void *arg); -int nat44_o2i_is_idle_session_cb (clib_bihash_kv_8_8_t * kv, void *arg); - - -/** - * @brief Enable NAT44 plugin - * - * @param c nat44_config_t - * - * @return 0 on success, non-zero value otherwise - */ -int nat44_plugin_enable (nat44_config_t c); - -/** - * @brief Disable NAT44 plugin - * - * @return 0 on success, non-zero value otherwise - */ -int nat44_plugin_disable (); - -/** - * @brief Add external address to NAT44 pool - * - * @param sm snat global configuration data - * @param addr IPv4 address - * @param vrf_id VRF id of tenant, ~0 means independent of VRF - * @param twice_nat 1 if twice NAT address - * - * @return 0 on success, non-zero value otherwise - */ -int snat_add_address (snat_main_t * sm, ip4_address_t * addr, u32 vrf_id, - u8 twice_nat); - -/** - * @brief Delete external address from NAT44 pool - * - * @param sm snat global configuration data - * @param addr IPv4 address - * @param delete_sm 1 if delete static mapping using address - * @param twice_nat 1 if twice NAT address - * - * @return 0 on success, non-zero value otherwise - */ -int snat_del_address (snat_main_t * sm, ip4_address_t addr, u8 delete_sm, - u8 twice_nat); - -/** - * @brief Add/delete external address to FIB DPO (out2in DPO mode) - * - * @param addr IPv4 address - * @param is_add 1 = add, 0 = delete - * - * @return 0 on success, non-zero value otherwise - */ -void nat44_add_del_address_dpo (ip4_address_t addr, u8 is_add); - -/** - * @brief Add/delete NAT44 static mapping - * - * @param l_addr local IPv4 address - * @param e_addr external IPv4 address - * @param l_port local port number - * @param e_port external port number - * @param vrf_id local VRF ID - * @param addr_only 1 = 1:1NAT, 0 = 1:1NAPT - * @param sw_if_index use interface address as external IPv4 address - * @param proto L4 protocol - * @param is_add 1 = add, 0 = delete - * @param twice_nat twice-nat mode - * @param out2in_only if 1 rule match only out2in direction - * @param tag opaque string tag - * @param identity_nat identity NAT - * @param pool_addr pool IPv4 address - * @param exact 1 = exact pool address - * - * @return 0 on success, non-zero value otherwise - */ -int snat_add_static_mapping (ip4_address_t l_addr, ip4_address_t e_addr, - u16 l_port, u16 e_port, u32 vrf_id, - int addr_only, u32 sw_if_index, - nat_protocol_t proto, int is_add, - twice_nat_type_t twice_nat, u8 out2in_only, - u8 * tag, u8 identity_nat, - ip4_address_t pool_addr, int exact); - -/** - * @brief Add/delete static mapping with load-balancing (multiple backends) - * - * @param e_addr external IPv4 address - * @param e_port external port number - * @param proto L4 protocol - * @param locals list of local backends - * @param is_add 1 = add, 0 = delete - * @param twice_nat twice-nat mode - * @param out2in_only if 1 rule match only out2in direction - * @param tag opaque string tag - * @param affinity 0 = disabled, otherwise client IP affinity sticky time - * - * @return 0 on success, non-zero value otherwise - */ -int nat44_add_del_lb_static_mapping (ip4_address_t e_addr, u16 e_port, - nat_protocol_t proto, - nat44_lb_addr_port_t * locals, u8 is_add, - 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, - nat_protocol_t proto, u32 vrf_id, - u8 probability, u8 is_add); - -clib_error_t *nat44_api_hookup (vlib_main_t * vm); - -/** - * @brief Set NAT plugin workers - * - * @param bitmap NAT workers bitmap - * - * @return 0 on success, non-zero value otherwise - */ -int snat_set_workers (uword * bitmap); - -/** - * @brief Set NAT plugin number of frame queue elements - * - * @param frame_queue_nelts number of worker handoff frame queue elements - * - * @return 0 on success, non-zero value otherwise - */ -int snat_set_frame_queue_nelts (u32 frame_queue_nelts); - -/** - * @brief Enable/disable NAT44 feature on the interface - * - * @param sw_if_index software index of the interface - * @param is_inside 1 = inside, 0 = outside - * @param is_del 1 = delete, 0 = add - * - * @return 0 on success, non-zero value otherwise - */ -int snat_interface_add_del (u32 sw_if_index, u8 is_inside, int is_del); - -/** - * @brief Enable/disable NAT44 output feature on the interface (postrouting NAT) - * - * @param sw_if_index software index of the interface - * @param is_inside 1 = inside, 0 = outside - * @param is_del 1 = delete, 0 = add - * - * @return 0 on success, non-zero value otherwise - */ -int snat_interface_add_del_output_feature (u32 sw_if_index, u8 is_inside, - int is_del); - -/** - * @brief Add/delete NAT44 pool address from specific interface - * - * @param sw_if_index software index of the interface - * @param is_del 1 = delete, 0 = add - * @param twice_nat 1 = twice NAT address for external hosts - * - * @return 0 on success, non-zero value otherwise - */ -int snat_add_interface_address (snat_main_t * sm, u32 sw_if_index, int is_del, - u8 twice_nat); - -/** - * @brief Delete NAT44 endpoint-dependent session - * - * @param sm snat global configuration data - * @param addr IPv4 address - * @param port L4 port number - * @param proto L4 protocol - * @param vrf_id VRF ID - * @param is_in 1 = inside network address and port pair, 0 = outside - * - * @return 0 on success, non-zero value otherwise - */ -int nat44_del_ed_session (snat_main_t * sm, ip4_address_t * addr, u16 port, - ip4_address_t * eh_addr, u16 eh_port, u8 proto, - u32 vrf_id, int is_in); - -/** - * @brief Free NAT44 session data (lookup keys, external address port) - * - * @param sm snat global configuration data - * @param s NAT session - * @param thread_index thread index - * @param is_ha is HA event - */ -void nat_free_session_data (snat_main_t * sm, snat_session_t * s, - u32 thread_index, u8 is_ha); - -/** - * @brief Set NAT44 session limit (session limit, vrf id) - * - * @param session_limit Session limit - * @param vrf_id VRF id - * @return 0 on success, non-zero value otherwise - */ -int nat44_set_session_limit (u32 session_limit, u32 vrf_id); - -/** - * @brief Update NAT44 session limit flushing all data (session limit, vrf id) - * - * @param session_limit Session limit - * @param vrf_id VRF id - * @return 0 on success, non-zero value otherwise - */ -int nat44_update_session_limit (u32 session_limit, u32 vrf_id); - -/** - * @brief Free all NAT44 sessions - */ -void nat44_sessions_clear (); - -/** - * @brief Find or create NAT user - * - * @param sm snat global configuration data - * @param addr IPv4 address - * @param fib_index FIB table index - * @param thread_index thread index - * - * @return NAT user data structure on success otherwise zero value - */ -snat_user_t *nat_user_get_or_create (snat_main_t * sm, - ip4_address_t * addr, u32 fib_index, - u32 thread_index); - -/** - * @brief Allocate new NAT session or recycle last used - * - * @param sm snat global configuration data - * @param u NAT user - * @param thread_index thread index - * @param now time now - * - * @return session data structure on success otherwise zero value - */ -snat_session_t *nat_session_alloc_or_recycle (snat_main_t * sm, - snat_user_t * u, - u32 thread_index, f64 now); - -/** - * @brief Free outside address and port pair - * - * @param addresses vector of outside addresses - * @param thread_index thread index - * @param key address, port and protocol - */ -void -snat_free_outside_address_and_port (snat_address_t * addresses, - u32 thread_index, - ip4_address_t * addr, - u16 port, nat_protocol_t protocol); - -void expire_per_vrf_sessions (u32 fib_index); - -/** - * @brief Match NAT44 static mapping. - * - * @param key address and port to match - * @param addr external/local address of the matched mapping - * @param port port of the matched mapping - * @param fib_index fib index of the matched mapping - * @param by_external if 0 match by local address otherwise match by external - * address - * @param is_addr_only 1 if matched mapping is address only - * @param twice_nat matched mapping is twice NAT type - * @param lb 1 if matched mapping is load-balanced - * @param ext_host_addr external host address - * @param is_identity_nat 1 if indentity mapping - * @param out if !=0 set to pointer of the mapping structure - * - * @returns 0 if match found otherwise 1. - */ -int snat_static_mapping_match (snat_main_t * sm, - ip4_address_t match_addr, - u16 match_port, - u32 match_fib_index, - nat_protocol_t match_protocol, - ip4_address_t * mapping_addr, - u16 * mapping_port, - u32 * mapping_fib_index, - u8 by_external, - u8 * is_addr_only, - twice_nat_type_t * twice_nat, - lb_nat_type_t * lb, - ip4_address_t * ext_host_addr, - u8 * is_identity_nat, - snat_static_mapping_t ** out); - -/** - * @brief Add/del NAT address to FIB. - * - * Add the external NAT address to the FIB as receive entries. This ensures - * that VPP will reply to ARP for this address and we don't need to enable - * proxy ARP on the outside interface. - * - * @param addr IPv4 address - * @param plen address prefix length - * @param sw_if_index software index of the outside interface - * @param is_add 0 = delete, 1 = add. - */ -void snat_add_del_addr_to_fib (ip4_address_t * addr, - u8 p_len, u32 sw_if_index, int is_add); - -#if 0 -void -nat_ha_sadd_ed_cb (ip4_address_t * in_addr, u16 in_port, - ip4_address_t * out_addr, u16 out_port, - ip4_address_t * eh_addr, u16 eh_port, - ip4_address_t * ehn_addr, u16 ehn_port, u8 proto, - u32 fib_index, u16 flags, u32 thread_index); - -void -nat_ha_sdel_ed_cb (ip4_address_t * out_addr, u16 out_port, - ip4_address_t * eh_addr, u16 eh_port, u8 proto, - u32 fib_index, u32 ti); - -void -nat_ha_sref_ed_cb (ip4_address_t * out_addr, u16 out_port, - ip4_address_t * eh_addr, u16 eh_port, u8 proto, - u32 fib_index, u32 total_pkts, u64 total_bytes, - u32 thread_index); -#endif - -int nat_set_outside_address_and_port (snat_address_t *addresses, - u32 thread_index, ip4_address_t addr, - u16 port, nat_protocol_t protocol); - -/* - * Why is this here? Because we don't need to touch this layer to - * simply reply to an icmp. We need to change id to a unique - * value to NAT an echo request/reply. - */ - -typedef struct -{ - u16 identifier; - u16 sequence; -} icmp_echo_header_t; - -typedef struct -{ - u16 src_port, dst_port; -} tcp_udp_header_t; - -u8 *format_user_kvp (u8 * s, va_list * args); - -u32 get_thread_idx_by_port (u16 e_port); - -u8 *format_static_mapping_kvp (u8 *s, va_list *args); - -u8 *format_session_kvp (u8 *s, va_list *args); - -u8 *format_user_kvp (u8 *s, va_list *args); - -u32 nat_calc_bihash_buckets (u32 n_elts); - -void nat44_addresses_free (snat_address_t **addresses); - -typedef enum -{ - NAT_ED_TRNSL_ERR_SUCCESS = 0, - NAT_ED_TRNSL_ERR_TRANSLATION_FAILED = 1, - NAT_ED_TRNSL_ERR_FLOW_MISMATCH = 2, -} nat_translation_error_e; - -nat_translation_error_e -nat_6t_flow_buf_translate (snat_main_t *sm, vlib_buffer_t *b, ip4_header_t *ip, - nat_6t_flow_t *f, nat_protocol_t proto, - int is_output_feature); - -void nat_6t_l3_l4_csum_calc (nat_6t_flow_t *f); - -format_function_t format_nat_ed_translation_error; -format_function_t format_nat_6t_flow; -format_function_t format_ed_session_kvp; - -#endif /* __included_nat_h__ */ -/* - * fd.io coding-style-patch-verification: ON - * - * Local Variables: - * eval: (c-set-style "gnu") - * End: - */ diff --git a/src/plugins/nat/nat44-ed/nat44_ed.api b/src/plugins/nat/nat44-ed/nat44_ed.api new file mode 100644 index 00000000000..6a2d44a6b9c --- /dev/null +++ b/src/plugins/nat/nat44-ed/nat44_ed.api @@ -0,0 +1,1214 @@ +/* + * Copyright (c) 2020 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +option version = "5.2.0"; +import "vnet/ip/ip_types.api"; +import "vnet/interface_types.api"; +import "plugins/nat/lib/nat_types.api"; + +/** + * @file nat44.api + * @brief VPP control-plane API messages. + * + * This file defines VPP control-plane API messages which are generally + * called through a shared memory interface. + */ + +enum nat44_config_flags : u8 +{ + NAT44_IS_ENDPOINT_INDEPENDENT = 0x00, + NAT44_IS_ENDPOINT_DEPENDENT = 0x01, + NAT44_IS_STATIC_MAPPING_ONLY = 0x02, + NAT44_IS_CONNECTION_TRACKING = 0x04, + NAT44_IS_OUT2IN_DPO = 0x08, +}; + +/** \brief Enable/disable NAT44 plugin + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request + @param inside_vrf - inside vrf id + @param outside_vrf - outside vrf id + @param users - maximum number of users per thread + (NAT44_IS_ENDPOINT_INDEPENDENT) + @param user_memory - overwrite hash allocation parameter + (NAT44_IS_ENDPOINT_INDEPENDENT) + @param sessions - maximum number of sessions per thread + @param session_memory - overwrite hash allocation parameter + @param user_sessions - maximum number of sessions per user + (NAT44_IS_ENDPOINT_INDEPENDENT) + @param enable - true if enable, false if disable + @param flags - flag NAT44_IS_ENDPOINT_INDEPENDENT, + NAT44_IS_ENDPOINT_DEPENDENT, + NAT44_IS_STATIC_MAPPING_ONLY, + NAT44_IS_CONNECTION_TRACKING, + NAT44_IS_OUT2IN_DPO +*/ +autoreply define nat44_plugin_enable_disable { + u32 client_index; + u32 context; + u32 inside_vrf; + u32 outside_vrf; + u32 users; + u32 user_memory; + u32 sessions; + u32 session_memory; + u32 user_sessions; + bool enable; + vl_api_nat44_config_flags_t flags; +}; + +/** \brief Control ping from client to api server request + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request +*/ +define nat_control_ping +{ + option deprecated; + u32 client_index; + u32 context; +}; + +/** \brief Control ping from the client to the server response + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request + @param retval - return code for the request + @param vpe_pid - the pid of the vpe, returned by the server +*/ +define nat_control_ping_reply +{ + option deprecated; + u32 context; + i32 retval; + u32 client_index; + u32 vpe_pid; +}; + +/** \brief Show NAT plugin startup config + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request +*/ +define nat_show_config +{ + option deprecated; + u32 client_index; + u32 context; +}; + +/** \brief DEPRECATED: Show NAT plugin startup config reply + @param context - sender context, to match reply w/ request + @param retval - return code for the request + @param static_mapping_only - if true dynamic translations disabled + @param static_mapping_connection_tracking - if true create session data + @param deterministic - if true deterministic mapping + @param endpoint_dependent - if true endpoint-dependent mode + @param out2in_dpo - if true out2in dpo mode + @param dslite_ce - if true DS-Lite is CE/B4 element, if false AFTR elemet + @param translation_buckets - number of translation hash buckets + @param translation_memory_size - translation hash memory size + @param user_buckets - number of user hash buckets + @param user_memory_size - user hash memory size + @param max_translations_per_user - maximum number of translations per user + @param outside_vrf_id - outside VRF id + @param inside_vrf_id - default inside VRF id + @param nat64_bib_buckets - number of NAT64 BIB hash buckets + @param nat64_bib_memory_size - memory size of NAT64 BIB hash + @param nat64_st_buckets - number of NAT64 session table hash buckets + @param nat64_st_memory_size - memory size of NAT64 session table hash +*/ +define nat_show_config_reply +{ + option deprecated; + u32 context; + i32 retval; + bool static_mapping_only; + bool static_mapping_connection_tracking; + bool deterministic; + bool endpoint_dependent; + bool out2in_dpo; + bool dslite_ce; + u32 translation_buckets; + u32 translation_memory_size; + u32 user_buckets; + u64 user_memory_size; + u32 max_translations_per_user; + u32 outside_vrf_id; + u32 inside_vrf_id; + u32 nat64_bib_buckets; + u64 nat64_bib_memory_size; + u32 nat64_st_buckets; + u64 nat64_st_memory_size; +}; + +/** \brief Show NAT plugin startup config + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request +*/ +define nat_show_config_2 +{ + option deprecated; + u32 client_index; + u32 context; +}; + +/** \brief Show NAT plugin startup config reply + @param context - sender context, to match reply w/ request + @param retval - return code for the request + @param static_mapping_only - if true dynamic translations disabled + @param static_mapping_connection_tracking - if true create session data + @param deterministic - if true deterministic mapping + @param endpoint_dependent - if true endpoint-dependent mode + @param out2in_dpo - if true out2in dpo mode + @param dslite_ce - if true DS-Lite is CE/B4 element, if false AFTR elemet + @param translation_buckets - number of translation hash buckets + @param translation_memory_size - translation hash memory size + @param user_buckets - number of user hash buckets + @param user_memory_size - user hash memory size + @param max_translations_per_user - maximum number of translations per user + @param outside_vrf_id - outside VRF id + @param inside_vrf_id - default inside VRF id + @param nat64_bib_buckets - number of NAT64 BIB hash buckets + @param nat64_bib_memory_size - memory size of NAT64 BIB hash + @param nat64_st_buckets - number of NAT64 session table hash buckets + @param nat64_st_memory_size - memory size of NAT64 session table hash + @param max_translations_per_thread - max translations per worker thread + @param max_users_per_thread - max users per worker thread +*/ +define nat_show_config_2_reply +{ + option deprecated; + u32 context; + i32 retval; + bool static_mapping_only; + bool static_mapping_connection_tracking; + bool deterministic; + bool endpoint_dependent; + bool out2in_dpo; + bool dslite_ce; + u32 translation_buckets; + u64 translation_memory_size; + u32 user_buckets; + u64 user_memory_size; + u32 max_translations_per_user; + u32 outside_vrf_id; + u32 inside_vrf_id; + u32 nat64_bib_buckets; + u64 nat64_bib_memory_size; + u32 nat64_st_buckets; + u64 nat64_st_memory_size; + u32 max_translations_per_thread; + u32 max_users_per_thread; +}; + +/** \brief Show NAT44 plugin running config + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request +*/ +define nat44_show_running_config +{ + option in_progress; + u32 client_index; + u32 context; +}; + +/** \brief Show NAT44 plugin running config reply + @param context - sender context, to match reply w/ request + @param retval - return code for the request + @param inside_vrf - default inside VRF id + @param outside_vrf - outside VRF id + @param users - maximum number of users per worker thread + (NAT44_IS_ENDPOINT_INDEPENDENT) + @param sessions - maximum number of sessions per worker thread + @param user_sessions - maximum number of sessions per user + (NAT44_IS_ENDPOINT_INDEPENDENT) + @param user_buckets - number of user hash buckets + (NAT44_IS_ENDPOINT_INDEPENDENT) + @param translation_buckets - number of translation hash buckets + @param flags - flag NAT44_IS_ENDPOINT_INDEPENDENT, + NAT44_IS_ENDPOINT_DEPENDENT, + NAT44_IS_STATIC_MAPPING_ONLY, + NAT44_IS_CONNECTION_TRACKING, + NAT44_IS_OUT2IN_DPO +*/ +define nat44_show_running_config_reply +{ + option in_progress; + u32 context; + i32 retval; + u32 inside_vrf; + u32 outside_vrf; + u32 users; + u32 sessions; + u32 user_sessions; + u32 user_buckets; + u32 translation_buckets; + bool forwarding_enabled; + bool ipfix_logging_enabled; + vl_api_nat_timeouts_t timeouts; + vl_api_nat_log_level_t log_level; + vl_api_nat44_config_flags_t flags; +}; + +/** \brief Run nat44 garbage collection + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request +*/ +autoreply define nat44_session_cleanup { + option deprecated; + u32 client_index; + u32 context; +}; + +/** \brief NAT44 set session limit + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request + @param session_limit - session limit + @param vrf_id - vrf id +*/ +autoreply define nat44_set_session_limit { + u32 client_index; + u32 context; + u32 session_limit; + u32 vrf_id; +}; + +/** \brief Set NAT logging level + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request + @param log_level - logging level +*/ +autoreply define nat_set_log_level { + option deprecated; + u32 client_index; + u32 context; + vl_api_nat_log_level_t log_level; +}; + +/** \brief Set NAT workers + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request + @param worker_mask - NAT workers mask +*/ +autoreply define nat_set_workers { + u32 client_index; + u32 context; + u64 worker_mask; +}; + +/** \brief Dump NAT workers + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request +*/ +define nat_worker_dump { + u32 client_index; + u32 context; +}; + +/** \brief NAT workers details response + @param context - sender context, to match reply w/ request + @param worker_index - worker index + @param lcore_id - lcore ID + @param name - worker name +*/ +define nat_worker_details { + u32 context; + u32 worker_index; + u32 lcore_id; + string name[64]; +}; + +/** \brief Enable/disable NAT IPFIX logging + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request + @param domain_id - observation domain ID + @param src_port - source port number + @param enable - true if enable, false if disable +*/ +autoreply define nat_ipfix_enable_disable { + option deprecated; + u32 client_index; + u32 context; + u32 domain_id; + u16 src_port; + bool enable; +}; + +/** \brief Set values of timeouts for NAT sessions (seconds) + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request + @param udp - UDP timeout (default 300sec) + @param tcp_established - TCP established timeout (default 7440sec) + @param tcp_transitory - TCP transitory timeout (default 240sec) + @param icmp - ICMP timeout (default 60sec) +*/ +autoreply define nat_set_timeouts { + option deprecated; + u32 client_index; + u32 context; + u32 udp; + u32 tcp_established; + u32 tcp_transitory; + u32 icmp; +}; + +/** \brief Get values of timeouts for NAT sessions (seconds) + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request +*/ +define nat_get_timeouts { + option deprecated; + u32 client_index; + u32 context; +}; + +/** \brief Get values of timeouts for NAT sessions reply + @param context - sender context, to match reply w/ request + @param retval - return code + @param udp - UDP timeout + @param tcp_established - TCP established timeout + @param tcp_transitory - TCP transitory timeout + @param icmp - ICMP timeout +*/ +define nat_get_timeouts_reply { + option deprecated; + u32 context; + i32 retval; + u32 udp; + u32 tcp_established; + u32 tcp_transitory; + u32 icmp; +}; + +/** \brief Set address and port assignment algorithm + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request + @param alg - address and port assignment algorithm: + 0 - default, 1 - MAP-E, 2 - port range + (see nat_addr_and_port_alloc_alg_t in nat.h) + @param psid_offset - number of offset bits (valid only for MAP-E alg) + @param psid_length - length of PSID (valid only for MAP-E alg) + @param psid - Port Set Identifier (PSID) value (valid only for MAP-E alg) + @param start_port - beginning of the port range + @param end_port - end of the port range +*/ +autoreply define nat_set_addr_and_port_alloc_alg { + u32 client_index; + u32 context; + u8 alg; + u8 psid_offset; + u8 psid_length; + u16 psid; + u16 start_port; + u16 end_port; +}; + +/** \brief Get address and port assignment algorithm + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request +*/ +define nat_get_addr_and_port_alloc_alg { + u32 client_index; + u32 context; +}; + +/** \brief Get address and port assignment algorithm reply + @param context - sender context, to match reply w/ request + @param retval - return code + @param alg - address and port assignment algorithm: + 0 - default, 1 - MAP-E, 2 - port range + (see nat_addr_and_port_alloc_alg_t in nat.h) + @param psid_offset - number of offset bits (valid only for MAP-E alg) + @param psid_length - length of PSID (valid only for MAP-E alg) + @param psid - Port Set Identifier (PSID) value (valid only for MAP-E alg) + @param start_port - beginning of the port range + @param end_port - end of the port range +*/ +define nat_get_addr_and_port_alloc_alg_reply { + u32 context; + i32 retval; + u8 alg; + u8 psid_offset; + u8 psid_length; + u16 psid; + u16 start_port; + u16 end_port; +}; + +/** \brief Set TCP MSS rewriting configuration + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request + @param mss_value - MSS value to be used for MSS rewriting + @param enable - if true enable MSS rewriting feature else disable +*/ +autoreply define nat_set_mss_clamping { + u32 client_index; + u32 context; + u16 mss_value; + bool enable; +}; + +/** \brief Get TCP MSS rewriting configuration + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request +*/ +define nat_get_mss_clamping { + u32 client_index; + u32 context; +}; + +/** \brief Get TCP MSS rewriting configuration reply + @param context - sender context, to match reply w/ request + @param retval - return code + @param mss_value - MSS value to be used for MSS rewriting + @param enable - if true enable MSS rewriting feature else disable +*/ +define nat_get_mss_clamping_reply { + u32 context; + i32 retval; + u16 mss_value; + bool enable; +}; + +/** \brief Set HA listener (local settings) + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request + @param ip_address - local IP4 address + @param port - local UDP port number + @param path_mtu - path MTU between local and failover +*/ +autoreply define nat_ha_set_listener { + u32 client_index; + u32 context; + vl_api_ip4_address_t ip_address; + u16 port; + u32 path_mtu; +}; + +/** \brief Set HA failover (remote settings) + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request + @param ip_address - failover IP4 address + @param port - failvoer UDP port number + @param session_refresh_interval - number of seconds after which to send + session counters refresh +*/ +autoreply define nat_ha_set_failover { + u32 client_index; + u32 context; + vl_api_ip4_address_t ip_address; + u16 port; + u32 session_refresh_interval; +}; + +/** \brief Get HA listener/local configuration + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request +*/ +define nat_ha_get_listener { + u32 client_index; + u32 context; +}; + +/** \brief Get HA listener/local configuration reply + @param context - sender context, to match reply w/ request + @param retval - return code + @param ip_address - local IP4 address + @param port - local UDP port number + @param path_mtu - Path MTU between local and failover +*/ +define nat_ha_get_listener_reply { + u32 context; + i32 retval; + vl_api_ip4_address_t ip_address; + u16 port; + u32 path_mtu; +}; + +/** \brief Get HA failover/remote settings + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request +*/ +define nat_ha_get_failover { + u32 client_index; + u32 context; +}; + +/** \brief Get HA failover/remote settings reply + @param context - sender context, to match reply w/ request + @param retval - return code + @param ip_address - failover IP4 address + @param port - failvoer UDP port number + @param session_refresh_interval - number of seconds after which to send + session counters refresh +*/ +define nat_ha_get_failover_reply { + u32 context; + i32 retval; + vl_api_ip4_address_t ip_address; + u16 port; + u32 session_refresh_interval; +}; + +/** \brief Flush the current HA data (for testing) + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request +*/ +autoreply define nat_ha_flush { + u32 client_index; + u32 context; +}; + +/** \brief Resync HA (resend existing sessions to new failover) + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request + @param want_resync_event - resync completed event sent to the sender via + nat_ha_resync_completed_event API message if + non-zero + @param pid - sender's pid +*/ +autoreply define nat_ha_resync +{ + u32 client_index; + u32 context; + u8 want_resync_event; + u32 pid; +}; + +/** \brief Tell client about a HA resync completion event + @param client_index - opaque cookie to identify the sender + @param pid - client pid registered to receive notification + @param missed_count - number of missed (not ACKed) messages +*/ +define nat_ha_resync_completed_event +{ + u32 client_index; + u32 pid; + u32 missed_count; +}; + +service { + rpc nat_ha_resync returns nat_ha_resync_reply events nat_ha_resync_completed_event; +}; + +/** \brief Del NAT44 user + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request + @param ip_address - IPv4 address + @param fib_index - FIB index +*/ +autoreply define nat44_del_user { + u32 client_index; + u32 context; + vl_api_ip4_address_t ip_address; + u32 fib_index; +}; + +/** \brief Add/del NAT44 address range + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request + @param first_ip_address - first IPv4 address + @param last_ip_address - last IPv4 address + @param vrf_id - VRF id of tenant, ~0 means independent of VRF + @param is_add - true if add, false if delete + @param flags - flag NAT_IS_TWICE_NAT if NAT address range for external hosts + +*/ +autoreply define nat44_add_del_address_range { + u32 client_index; + u32 context; + vl_api_ip4_address_t first_ip_address; + vl_api_ip4_address_t last_ip_address; + u32 vrf_id; + bool is_add; + vl_api_nat_config_flags_t flags; +}; + +/** \brief Dump NAT44 addresses + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request +*/ +define nat44_address_dump { + u32 client_index; + u32 context; +}; + +/** \brief NAT44 address details response + @param context - sender context, to match reply w/ request + @param ip_address - IPv4 address + @param flags - flag NAT_IS_TWICE_NAT if NAT address range for external hosts + @param vrf_id - VRF id of tenant, ~0 means independent of VRF +*/ +define nat44_address_details { + u32 context; + vl_api_ip4_address_t ip_address; + vl_api_nat_config_flags_t flags; + u32 vrf_id; +}; + +/** \brief Enable/disable NAT44 feature on the interface + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request + @param is_add - true if add, false if delete + @param flags - flag NAT_IS_INSIDE if interface is inside else + interface is outside + @param sw_if_index - software index of the interface +*/ +autoreply define nat44_interface_add_del_feature { + u32 client_index; + u32 context; + bool is_add; + vl_api_nat_config_flags_t flags; + vl_api_interface_index_t sw_if_index; +}; + +/** \brief Dump interfaces with NAT44 feature + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request +*/ +define nat44_interface_dump { + u32 client_index; + u32 context; +}; + +/** \brief NAT44 interface details response + @param context - sender context, to match reply w/ request + @param sw_if_index - software index of the interface + @param flags - flag NAT_IS_INSIDE if interface is inside, + flag NAT_IS_OUTSIDE if interface is outside + and if both flags are set the interface is + both inside and outside +*/ +define nat44_interface_details { + u32 context; + vl_api_nat_config_flags_t flags; + vl_api_interface_index_t sw_if_index; +}; + +/** \brief Enable/disbale NAT44 as an interface output feature (postrouting + in2out translation) + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request + @param is_add - true if add, false if delete + @param flags - flag NAT_IS_INSIDE if interface is inside else + interface is outside + @param sw_if_index - software index of the interface +*/ +autoreply define nat44_interface_add_del_output_feature { + u32 client_index; + u32 context; + bool is_add; + vl_api_nat_config_flags_t flags; + vl_api_interface_index_t sw_if_index; +}; + +/** \brief Dump interfaces with NAT44 output feature + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request +*/ +define nat44_interface_output_feature_dump { + u32 client_index; + u32 context; +}; + +/** \brief NAT44 interface with output feature details response + @param context - sender context, to match reply w/ request + @param flags - flag NAT_IS_INSIDE if interface is inside else + interface is outside + @param sw_if_index - software index of the interface +*/ +define nat44_interface_output_feature_details { + u32 context; + vl_api_nat_config_flags_t flags; + vl_api_interface_index_t sw_if_index; +}; + +/** \brief Add/delete NAT44 static mapping + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request + @param is_add - true if add, false if delete + @param flags - flag NAT_IS_ADDR_ONLY if address only mapping, + flag nat_is_twice_nat if nat address range for external hosts, + flag NAT_IS_SELF_TWICE_NAT if translate external host address + and port whenever external host address equals local + address of internal host, + flag NAT_IS_OUT2IN_ONLY if rule match only out2in direction + @param local_ip_address - local IPv4 address + @param external_ip_address - external IPv4 address + @param protocol - IP protocol, used only if addr_only=0 + @param local_port - local port number, used only if addr_only=0 + @param external_port - external port number, used only if addr_only=0 + @param external_sw_if_index - external interface (if set + external_ip_address is ignored, ~0 means not + used) + @param vfr_id - VRF ID + @param tag - opaque string tag +*/ +autoreply define nat44_add_del_static_mapping { + u32 client_index; + u32 context; + bool is_add; + vl_api_nat_config_flags_t flags; + vl_api_ip4_address_t local_ip_address; + vl_api_ip4_address_t external_ip_address; + u8 protocol; + u16 local_port; + u16 external_port; + vl_api_interface_index_t external_sw_if_index; + u32 vrf_id; + string tag[64]; +}; + +/** \brief Add/delete NAT44 static mapping + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request + @param is_add - true if add, false if delete + @param match_pool - true if use specific pool_ip_address + @param flags - flag NAT_IS_ADDR_ONLY if address only mapping, + flag nat_is_twice_nat if nat address range for external hosts, + flag NAT_IS_SELF_TWICE_NAT if translate external host address + and port whenever external host address equals local + address of internal host, + flag NAT_IS_OUT2IN_ONLY if rule match only out2in direction + @param pool_ip_address - pool IPv4 address to match with pool + @param local_ip_address - local IPv4 address + @param external_ip_address - external IPv4 address + @param protocol - IP protocol, used only if addr_only=0 + @param local_port - local port number, used only if addr_only=0 + @param external_port - external port number, used only if addr_only=0 + @param external_sw_if_index - external interface (if set + external_ip_address is ignored, ~0 means not + used) + @param vfr_id - VRF ID + @param tag - opaque string tag +*/ +autoreply define nat44_add_del_static_mapping_v2 { + option in_progress; + u32 client_index; + u32 context; + bool is_add; + bool match_pool; + vl_api_nat_config_flags_t flags; + vl_api_ip4_address_t pool_ip_address; + vl_api_ip4_address_t local_ip_address; + vl_api_ip4_address_t external_ip_address; + u8 protocol; + u16 local_port; + u16 external_port; + vl_api_interface_index_t external_sw_if_index; + u32 vrf_id; + string tag[64]; +}; + +/** \brief Dump NAT44 static mappings + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request +*/ +define nat44_static_mapping_dump { + u32 client_index; + u32 context; +}; + +/** \brief NAT44 static mapping details response + @param context - sender context, to match reply w/ request + @param flags - flag NAT_ADDR_ONLY if address only mapping, + flag NAT_TWICE_NAT if NAT address range for external hosts, + flag NAT_SELF_TWICE_NAT if translate external host address + and port whenever external host address equals local + address of internal host, + flag NAT_OUT2IN_ONLY if rule match only out2in direction + @param local_ip_address - local IPv4 address + @param external_ip_address - external IPv4 address + @param protocol - IP protocol, valid only if no NAT_ADDR_ONLY flag + @param local_port - local port number, valid only if no NAT_ADDR_ONLY flag + @param external_port - external port number, valid only if no NAT_ADDR_ONLY flag + @param external_sw_if_index - external interface + @param vfr_id - VRF ID + @param tag - opaque string tag +*/ +define nat44_static_mapping_details { + u32 context; + vl_api_nat_config_flags_t flags; + vl_api_ip4_address_t local_ip_address; + vl_api_ip4_address_t external_ip_address; + u8 protocol; + u16 local_port; + u16 external_port; + vl_api_interface_index_t external_sw_if_index; + u32 vrf_id; + string tag[64]; +}; + +/** \brief Add/delete NAT44 identity mapping + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request + @param is_add - true if add, false if delete + @param flags - flag NAT_ADDR_ONLY if address only mapping + @param ip_address - IPv4 address + @param protocol - IP protocol + @param port - port number + @param sw_if_index - interface (if set ip_address is ignored, ~0 means not + used) + @param vfr_id - VRF ID (if ~0 use default VRF) + @param tag - opaque string tag +*/ +autoreply define nat44_add_del_identity_mapping { + u32 client_index; + u32 context; + bool is_add; + vl_api_nat_config_flags_t flags; + vl_api_ip4_address_t ip_address; + u8 protocol; + u16 port; + vl_api_interface_index_t sw_if_index; + u32 vrf_id; + string tag[64]; +}; + +/** \brief Dump NAT44 identity mappings + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request +*/ +define nat44_identity_mapping_dump { + u32 client_index; + u32 context; +}; + +/** \brief NAT44 identity mapping details response + @param context - sender context, to match reply w/ request + @param flags - flag NAT_ADDR_ONLY if address only mapping + @param ip_address - IPv4 address + @param protocol - IP protocol + @param port - port number + @param sw_if_index - interface + @param vfr_id - VRF ID + @param tag - opaque string tag +*/ +define nat44_identity_mapping_details { + u32 context; + vl_api_nat_config_flags_t flags; + vl_api_ip4_address_t ip_address; + u8 protocol; + u16 port; + vl_api_interface_index_t sw_if_index; + u32 vrf_id; + string tag[64]; +}; + +/** \brief Add/delete NAT44 pool address from specific interfce + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request + @param is_add - true if add, false if delete + @param sw_if_index - software index of the interface + @param flags - flag NAT_TWICE_NAT if NAT address range for external hosts +*/ +autoreply define nat44_add_del_interface_addr { + u32 client_index; + u32 context; + bool is_add; + vl_api_interface_index_t sw_if_index; + vl_api_nat_config_flags_t flags; +}; + +/** \brief Dump NAT44 pool addresses interfaces + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request +*/ +define nat44_interface_addr_dump { + u32 client_index; + u32 context; +}; + +/** \brief NAT44 pool addresses interfaces details response + @param context - sender context, to match reply w/ request + @param sw_if_index - software index of the interface + @param flags - flag NAT_TWICE_NAT if NAT address range for external hosts + +*/ +define nat44_interface_addr_details { + u32 context; + vl_api_interface_index_t sw_if_index; + vl_api_nat_config_flags_t flags; +}; + +/** \brief Dump NAT44 users + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request +*/ +define nat44_user_dump { + u32 client_index; + u32 context; +}; + +/** \brief NAT44 users response + @param context - sender context, to match reply w/ request + @vrf_id - VRF ID + @param ip_address - IPv4 address + @param nsessions - number of dynamic sessions + @param nstaticsessions - number of static sessions +*/ +define nat44_user_details { + u32 context; + u32 vrf_id; + vl_api_ip4_address_t ip_address; + u32 nsessions; + u32 nstaticsessions; +}; + +/** \brief NAT44 user's sessions + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request + @param ip_address - IPv4 address of the user to dump + @param vrf_id - VRF_ID +*/ +define nat44_user_session_dump { + u32 client_index; + u32 context; + vl_api_ip4_address_t ip_address; + u32 vrf_id; +}; + +/** \brief NAT44 user's sessions response + @param context - sender context, to match reply w/ request + @param outside_ip_address - outside IPv4 address + @param outside_port - outside port + @param inside_ip_address - inside IPv4 address + @param inside_port - inside port + @param protocol - protocol + @param flags - flag NAT_IS_STATIC if session is static, + flag NAT_IS_TWICE_NAT if session is twice-nat, + flag NAT_IS_EXT_HOST_VALID if external host address + and port are valid + @param last_heard - last heard timer + @param total_bytes - count of bytes sent through session + @param total_pkts - count of pakets sent through session + @param ext_host_address - external host IPv4 address + @param ext_host_port - external host port + @param ext_host_nat_address - post-NAT external host IPv4 address (valid + only if twice-nat session) + @param ext_host_nat_port - post-NAT external host port (valid only if + twice-nat session) +*/ +define nat44_user_session_details { + u32 context; + vl_api_ip4_address_t outside_ip_address; + u16 outside_port; + vl_api_ip4_address_t inside_ip_address; + u16 inside_port; + u16 protocol; + vl_api_nat_config_flags_t flags; + u64 last_heard; + u64 total_bytes; + u32 total_pkts; + vl_api_ip4_address_t ext_host_address; + u16 ext_host_port; + vl_api_ip4_address_t ext_host_nat_address; + u16 ext_host_nat_port; +}; + +/** \brief NAT44 load-balancing address and port pair + @param addr - IPv4 address of the internal node + @param port - L4 port number of the internal node + @param probability - probability of the internal node to be randomly matched + @param vrf_id - VRF id +*/ +typedef nat44_lb_addr_port { + vl_api_ip4_address_t addr; + u16 port; + u8 probability; + u32 vrf_id; +}; + +/** \brief Add/delete NAT44 load-balancing static mapping rule + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request + @param is_add - true if add, false if delete + @param flags - flag NAT_TWICE_NAT if NAT address range for external hosts, + flag NAT_SELF_TWICE_NAT if translate external host address + and port whenever external host address equals local + address of internal host, + flag NAT_OUT2IN_ONLY if rule match only out2in direction + @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 affinity - if 0 disabled, otherwise client IP affinity sticky time + in seconds + @param local_num - number of local network nodes + @param locals - local network nodes + @param tag - opaque string tag +*/ +autoreply define nat44_add_del_lb_static_mapping { + u32 client_index; + u32 context; + bool is_add; + vl_api_nat_config_flags_t flags; + vl_api_ip4_address_t external_addr; + u16 external_port; + u8 protocol; + u32 affinity; + string tag[64]; + 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 - true if add, false 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; + bool is_add; + vl_api_ip4_address_t external_addr; + 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 +*/ +define nat44_lb_static_mapping_dump { + u32 client_index; + u32 context; +}; + +/** \brief NAT44 load-balancing static mapping rule details response + @param context - sender context, to match reply w/ request + @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 flags - flag NAT_TWICE_NAT if NAT address range for external hosts, + flag NAT_SELF_TWICE_NAT if translate external host address + and port whenever external host address equals local + address of internal host, + flag NAT_OUT2IN_ONLY if rule match only out2in direction + @param affinity - if 0 disabled, otherwise client IP affinity sticky time + in seconds + @param local_num - number of local network nodes + @param locals - local network nodes + @param tag - opaque string tag +*/ +define nat44_lb_static_mapping_details { + u32 context; + vl_api_ip4_address_t external_addr; + u16 external_port; + u8 protocol; + vl_api_nat_config_flags_t flags; + u32 affinity; + string tag[64]; + u32 local_num; + vl_api_nat44_lb_addr_port_t locals[local_num]; +}; + +/** \brief Delete NAT44 session + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request + @param ip_address - IPv4 address + @param protocol - IP protocol + @param port - port number + @param vfr_id - VRF ID + @param flags - flag NAT_IS_INSIDE if interface is inside or + interface is outside, + flag NAT_IS_EXT_HOST_VALID if external host address and + port are valid + @param ext_host_address - external host IPv4 address + @param ext_host_port - external host port +*/ +autoreply define nat44_del_session { + u32 client_index; + u32 context; + vl_api_ip4_address_t address; + u8 protocol; + u16 port; + u32 vrf_id; + vl_api_nat_config_flags_t flags; + vl_api_ip4_address_t ext_host_address; + u16 ext_host_port; +}; + +/** \brief Enable/disable forwarding for NAT44 + Forward packets which don't match existing translation + or static mapping instead of dropping them. + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request + @param enable - true for enable, false for disable +*/ +autoreply define nat44_forwarding_enable_disable { + option deprecated; + u32 client_index; + u32 context; + bool enable; +}; + +/** \brief Check if forwarding is enabled or disabled + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request +*/ +define nat44_forwarding_is_enabled { + option deprecated; + u32 client_index; + u32 context; +}; + +/** \brief Response to check if forwarding is enabled or disabled + @param context - sender context, to match reply w/ request + @param enabled - true if enabled, false if disabled +*/ +define nat44_forwarding_is_enabled_reply { + option deprecated; + u32 context; + bool enabled; +}; + +/** \brief Set NAT handoff frame queue options + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request + @param frame_queue_nelts - number of worker handoff frame queue elements +*/ +autoreply define nat44_ed_set_fq_options { + option in_progress; + u32 client_index; + u32 context; + u32 frame_queue_nelts; +}; + +/** \brief Show NAT handoff frame queue options + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request +*/ +define nat44_ed_show_fq_options +{ + option in_progress; + u32 client_index; + u32 context; +}; + +/** \brief Show NAT handoff frame queue options reply + @param context - sender context, to match reply w/ request + @param retval - return code for the request + @param frame_queue_nelts - number of worker handoff frame queue elements +*/ +define nat44_ed_show_fq_options_reply +{ + option in_progress; + u32 context; + i32 retval; + u32 frame_queue_nelts; +}; diff --git a/src/plugins/nat/nat44-ed/nat44_ed.c b/src/plugins/nat/nat44-ed/nat44_ed.c new file mode 100644 index 00000000000..d9d35fc55bc --- /dev/null +++ b/src/plugins/nat/nat44-ed/nat44_ed.c @@ -0,0 +1,3610 @@ +/* + * snat.c - simple nat plugin + * + * Copyright (c) 2016 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +snat_main_t snat_main; + +static_always_inline void nat_validate_interface_counters (snat_main_t *sm, + u32 sw_if_index); + +#define skip_if_disabled() \ + do \ + { \ + snat_main_t *sm = &snat_main; \ + if (PREDICT_FALSE (!sm->enabled)) \ + return; \ + } \ + while (0) + +#define fail_if_enabled() \ + do \ + { \ + snat_main_t *sm = &snat_main; \ + if (PREDICT_FALSE (sm->enabled)) \ + { \ + nat_log_err ("plugin enabled"); \ + return 1; \ + } \ + } \ + while (0) + +#define fail_if_disabled() \ + do \ + { \ + snat_main_t *sm = &snat_main; \ + if (PREDICT_FALSE (!sm->enabled)) \ + { \ + nat_log_err ("plugin disabled"); \ + return 1; \ + } \ + } \ + while (0) + +/* *INDENT-OFF* */ +/* Hook up input features */ +VNET_FEATURE_INIT (nat_pre_in2out, static) = { + .arc_name = "ip4-unicast", + .node_name = "nat-pre-in2out", + .runs_after = VNET_FEATURES ("acl-plugin-in-ip4-fa", + "ip4-sv-reassembly-feature"), +}; +VNET_FEATURE_INIT (nat_pre_out2in, static) = { + .arc_name = "ip4-unicast", + .node_name = "nat-pre-out2in", + .runs_after = VNET_FEATURES ("acl-plugin-in-ip4-fa", + "ip4-dhcp-client-detect", + "ip4-sv-reassembly-feature"), +}; +VNET_FEATURE_INIT (snat_in2out_worker_handoff, static) = { + .arc_name = "ip4-unicast", + .node_name = "nat44-in2out-worker-handoff", + .runs_after = VNET_FEATURES ("acl-plugin-in-ip4-fa"), +}; +VNET_FEATURE_INIT (snat_out2in_worker_handoff, static) = { + .arc_name = "ip4-unicast", + .node_name = "nat44-out2in-worker-handoff", + .runs_after = VNET_FEATURES ("acl-plugin-in-ip4-fa", + "ip4-dhcp-client-detect"), +}; +VNET_FEATURE_INIT (ip4_snat_in2out, static) = { + .arc_name = "ip4-unicast", + .node_name = "nat44-in2out", + .runs_after = VNET_FEATURES ("acl-plugin-in-ip4-fa","ip4-sv-reassembly-feature"), +}; +VNET_FEATURE_INIT (ip4_snat_out2in, static) = { + .arc_name = "ip4-unicast", + .node_name = "nat44-out2in", + .runs_after = VNET_FEATURES ("acl-plugin-in-ip4-fa","ip4-sv-reassembly-feature", + "ip4-dhcp-client-detect"), +}; +VNET_FEATURE_INIT (ip4_nat44_ed_in2out, static) = { + .arc_name = "ip4-unicast", + .node_name = "nat44-ed-in2out", + .runs_after = VNET_FEATURES ("acl-plugin-in-ip4-fa","ip4-sv-reassembly-feature"), +}; +VNET_FEATURE_INIT (ip4_nat44_ed_out2in, static) = { + .arc_name = "ip4-unicast", + .node_name = "nat44-ed-out2in", + .runs_after = VNET_FEATURES ("acl-plugin-in-ip4-fa","ip4-sv-reassembly-feature", + "ip4-dhcp-client-detect"), +}; +VNET_FEATURE_INIT (ip4_nat44_ed_classify, static) = { + .arc_name = "ip4-unicast", + .node_name = "nat44-ed-classify", + .runs_after = VNET_FEATURES ("acl-plugin-in-ip4-fa","ip4-sv-reassembly-feature"), +}; +VNET_FEATURE_INIT (ip4_nat_handoff_classify, static) = { + .arc_name = "ip4-unicast", + .node_name = "nat44-handoff-classify", + .runs_after = VNET_FEATURES ("acl-plugin-in-ip4-fa","ip4-sv-reassembly-feature"), +}; +VNET_FEATURE_INIT (ip4_snat_in2out_fast, static) = { + .arc_name = "ip4-unicast", + .node_name = "nat44-in2out-fast", + .runs_after = VNET_FEATURES ("acl-plugin-in-ip4-fa","ip4-sv-reassembly-feature"), +}; +VNET_FEATURE_INIT (ip4_snat_out2in_fast, static) = { + .arc_name = "ip4-unicast", + .node_name = "nat44-out2in-fast", + .runs_after = VNET_FEATURES ("acl-plugin-in-ip4-fa","ip4-sv-reassembly-feature", + "ip4-dhcp-client-detect"), +}; + +/* Hook up output features */ +VNET_FEATURE_INIT (ip4_snat_in2out_output, static) = { + .arc_name = "ip4-output", + .node_name = "nat44-in2out-output", + .runs_after = VNET_FEATURES ("acl-plugin-out-ip4-fa","ip4-sv-reassembly-output-feature"), +}; +VNET_FEATURE_INIT (ip4_snat_in2out_output_worker_handoff, static) = { + .arc_name = "ip4-output", + .node_name = "nat44-in2out-output-worker-handoff", + .runs_after = VNET_FEATURES ("acl-plugin-out-ip4-fa","ip4-sv-reassembly-output-feature"), +}; +VNET_FEATURE_INIT (nat_pre_in2out_output, static) = { + .arc_name = "ip4-output", + .node_name = "nat-pre-in2out-output", + .runs_after = VNET_FEATURES ("ip4-sv-reassembly-output-feature"), + .runs_before = VNET_FEATURES ("acl-plugin-out-ip4-fa"), +}; +VNET_FEATURE_INIT (ip4_nat44_ed_in2out_output, static) = { + .arc_name = "ip4-output", + .node_name = "nat44-ed-in2out-output", + .runs_after = VNET_FEATURES ("ip4-sv-reassembly-output-feature"), + .runs_before = VNET_FEATURES ("acl-plugin-out-ip4-fa"), +}; + +VLIB_PLUGIN_REGISTER () = { + .version = VPP_BUILD_VER, + .description = "Network Address Translation (NAT)", +}; +/* *INDENT-ON* */ + +static void nat44_ed_db_init (u32 translations, u32 translation_buckets); + +static void nat44_ed_db_free (); + +static u32 +nat44_ed_get_worker_out2in_cb (vlib_buffer_t * b, ip4_header_t * ip, + u32 rx_fib_index, u8 is_output); + +static u32 +nat44_ed_get_worker_in2out_cb (ip4_header_t * ip, u32 rx_fib_index, + u8 is_output); + +u32 nat_calc_bihash_buckets (u32 n_elts); + +u8 * +format_session_kvp (u8 * s, va_list * args) +{ + clib_bihash_kv_8_8_t *v = va_arg (*args, clib_bihash_kv_8_8_t *); + + s = format (s, "%U thread-index %llu session-index %llu", format_snat_key, + v->key, nat_value_get_thread_index (v), + nat_value_get_session_index (v)); + + return s; +} + +u8 * +format_static_mapping_kvp (u8 * s, va_list * args) +{ + clib_bihash_kv_8_8_t *v = va_arg (*args, clib_bihash_kv_8_8_t *); + + s = format (s, "%U static-mapping-index %llu", + format_snat_key, v->key, v->value); + + return s; +} + +u8 * +format_ed_session_kvp (u8 * s, va_list * args) +{ + clib_bihash_kv_16_8_t *v = va_arg (*args, clib_bihash_kv_16_8_t *); + + u8 proto; + u16 r_port, l_port; + ip4_address_t l_addr, r_addr; + u32 fib_index; + + split_ed_kv (v, &l_addr, &r_addr, &proto, &fib_index, &l_port, &r_port); + s = format (s, + "local %U:%d remote %U:%d proto %U fib %d thread-index %u " + "session-index %u", + format_ip4_address, &l_addr, clib_net_to_host_u16 (l_port), + format_ip4_address, &r_addr, clib_net_to_host_u16 (r_port), + format_ip_protocol, proto, fib_index, + ed_value_get_thread_index (v), ed_value_get_session_index (v)); + + return s; +} + +void +nat_free_session_data (snat_main_t * sm, snat_session_t * s, u32 thread_index, + u8 is_ha) +{ + per_vrf_sessions_unregister_session (s, thread_index); + + if (nat_ed_ses_i2o_flow_hash_add_del (sm, thread_index, s, 0)) + nat_elog_warn (sm, "flow hash del failed"); + + if (nat_ed_ses_o2i_flow_hash_add_del (sm, thread_index, s, 0)) + nat_elog_warn (sm, "flow hash del failed"); + + if (is_fwd_bypass_session (s)) + { + return; + } + + if (is_affinity_sessions (s)) + nat_affinity_unlock (s->ext_host_addr, s->out2in.addr, + s->nat_proto, s->out2in.port); + + if (!is_ha) + nat_syslog_nat44_sdel ( + 0, s->in2out.fib_index, &s->in2out.addr, s->in2out.port, + &s->ext_host_nat_addr, s->ext_host_nat_port, &s->out2in.addr, + s->out2in.port, &s->ext_host_addr, s->ext_host_port, s->nat_proto, + is_twice_nat_session (s)); + + if (snat_is_unk_proto_session (s)) + return; + + if (!is_ha) + { + /* log NAT event */ + nat_ipfix_logging_nat44_ses_delete (thread_index, + s->in2out.addr.as_u32, + s->out2in.addr.as_u32, + s->nat_proto, + s->in2out.port, + s->out2in.port, + s->in2out.fib_index); + } + + /* Twice NAT address and port for external host */ + if (is_twice_nat_session (s)) + { + snat_free_outside_address_and_port (sm->twice_nat_addresses, + thread_index, + &s->ext_host_nat_addr, + s->ext_host_nat_port, s->nat_proto); + } + + if (snat_is_session_static (s)) + return; + + snat_free_outside_address_and_port (sm->addresses, thread_index, + &s->out2in.addr, s->out2in.port, + s->nat_proto); +} + +void +snat_add_del_addr_to_fib (ip4_address_t * addr, u8 p_len, u32 sw_if_index, + int is_add) +{ + snat_main_t *sm = &snat_main; + fib_prefix_t prefix = { + .fp_len = p_len, + .fp_proto = FIB_PROTOCOL_IP4, + .fp_addr = { + .ip4.as_u32 = addr->as_u32, + }, + }; + u32 fib_index = ip4_fib_table_get_index_for_sw_if_index (sw_if_index); + + if (is_add) + fib_table_entry_update_one_path (fib_index, + &prefix, + sm->fib_src_low, + (FIB_ENTRY_FLAG_CONNECTED | + FIB_ENTRY_FLAG_LOCAL | + FIB_ENTRY_FLAG_EXCLUSIVE), + DPO_PROTO_IP4, + NULL, + sw_if_index, + ~0, 1, NULL, FIB_ROUTE_PATH_FLAG_NONE); + else + fib_table_entry_delete (fib_index, &prefix, sm->fib_src_low); +} + +int +snat_add_address (snat_main_t * sm, ip4_address_t * addr, u32 vrf_id, + u8 twice_nat) +{ + snat_address_t *ap; + snat_interface_t *i; + vlib_thread_main_t *tm = vlib_get_thread_main (); + + /* Check if address already exists */ + /* *INDENT-OFF* */ + vec_foreach (ap, twice_nat ? sm->twice_nat_addresses : sm->addresses) + { + if (ap->addr.as_u32 == addr->as_u32) + { + nat_log_err ("address exist"); + return VNET_API_ERROR_VALUE_EXIST; + } + } + /* *INDENT-ON* */ + + if (twice_nat) + vec_add2 (sm->twice_nat_addresses, ap, 1); + else + vec_add2 (sm->addresses, ap, 1); + + ap->addr = *addr; + if (vrf_id != ~0) + ap->fib_index = + fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP4, vrf_id, + sm->fib_src_low); + else + ap->fib_index = ~0; + + /* *INDENT-OFF* */ + #define _(N, i, n, s) \ + clib_memset(ap->busy_##n##_port_refcounts, 0, sizeof(ap->busy_##n##_port_refcounts));\ + ap->busy_##n##_ports = 0; \ + ap->busy_##n##_ports_per_thread = 0;\ + vec_validate_init_empty (ap->busy_##n##_ports_per_thread, tm->n_vlib_mains - 1, 0); + foreach_nat_protocol + #undef _ + /* *INDENT-ON* */ + + if (twice_nat) + return 0; + + /* Add external address to FIB */ + /* *INDENT-OFF* */ + pool_foreach (i, sm->interfaces) + { + if (nat_interface_is_inside (i)) + continue; + + snat_add_del_addr_to_fib (addr, 32, i->sw_if_index, 1); + break; + } + pool_foreach (i, sm->output_feature_interfaces) + { + if (nat_interface_is_inside (i)) + continue; + + snat_add_del_addr_to_fib (addr, 32, i->sw_if_index, 1); + break; + } + /* *INDENT-ON* */ + + return 0; +} + +static int +is_snat_address_used_in_static_mapping (snat_main_t * sm, ip4_address_t addr) +{ + snat_static_mapping_t *m; + /* *INDENT-OFF* */ + pool_foreach (m, sm->static_mappings) + { + if (is_addr_only_static_mapping (m) || + is_out2in_only_static_mapping (m) || + is_identity_static_mapping (m)) + continue; + if (m->external_addr.as_u32 == addr.as_u32) + return 1; + } + /* *INDENT-ON* */ + + return 0; +} + +static void +snat_add_static_mapping_when_resolved (snat_main_t *sm, ip4_address_t l_addr, + u16 l_port, u32 sw_if_index, u16 e_port, + u32 vrf_id, nat_protocol_t proto, + int addr_only, u8 *tag, int twice_nat, + int out2in_only, int identity_nat, + ip4_address_t pool_addr, int exact) +{ + snat_static_map_resolve_t *rp; + + vec_add2 (sm->to_resolve, rp, 1); + rp->l_addr.as_u32 = l_addr.as_u32; + rp->l_port = l_port; + rp->sw_if_index = sw_if_index; + rp->e_port = e_port; + rp->vrf_id = vrf_id; + rp->proto = proto; + rp->addr_only = addr_only; + rp->twice_nat = twice_nat; + rp->out2in_only = out2in_only; + rp->identity_nat = identity_nat; + rp->tag = vec_dup (tag); + rp->pool_addr = pool_addr; + rp->exact = exact; +} + +u32 +get_thread_idx_by_port (u16 e_port) +{ + snat_main_t *sm = &snat_main; + u32 thread_idx = sm->num_workers; + if (sm->num_workers > 1) + { + thread_idx = + sm->first_worker_index + + sm->workers[(e_port - 1024) / sm->port_per_thread]; + } + return thread_idx; +} + +void +nat_ed_static_mapping_del_sessions (snat_main_t * sm, + snat_main_per_thread_data_t * tsm, + ip4_address_t l_addr, + u16 l_port, + u8 protocol, + u32 fib_index, int addr_only, + ip4_address_t e_addr, u16 e_port) +{ + snat_session_t *s; + u32 *indexes_to_free = NULL; + /* *INDENT-OFF* */ + pool_foreach (s, tsm->sessions) { + if (s->in2out.fib_index != fib_index || + s->in2out.addr.as_u32 != l_addr.as_u32) + { + continue; + } + if (!addr_only) + { + if ((s->out2in.addr.as_u32 != e_addr.as_u32) || + s->out2in.port != e_port || + s->in2out.port != l_port || + s->nat_proto != protocol) + continue; + } + + if (is_lb_session (s)) + continue; + if (!snat_is_session_static (s)) + continue; + nat_free_session_data (sm, s, tsm - sm->per_thread_data, 0); + vec_add1 (indexes_to_free, s - tsm->sessions); + if (!addr_only) + break; + } + /* *INDENT-ON* */ + u32 *ses_index; + vec_foreach (ses_index, indexes_to_free) + { + s = pool_elt_at_index (tsm->sessions, *ses_index); + nat_ed_session_delete (sm, s, tsm - sm->per_thread_data, 1); + } + vec_free (indexes_to_free); +} + +int +snat_add_static_mapping (ip4_address_t l_addr, ip4_address_t e_addr, + u16 l_port, u16 e_port, u32 vrf_id, int addr_only, + u32 sw_if_index, nat_protocol_t proto, int is_add, + twice_nat_type_t twice_nat, u8 out2in_only, u8 *tag, + u8 identity_nat, ip4_address_t pool_addr, int exact) +{ + snat_main_t *sm = &snat_main; + snat_static_mapping_t *m; + clib_bihash_kv_8_8_t kv, value; + snat_address_t *a = 0; + u32 fib_index = ~0; + snat_interface_t *interface; + snat_main_per_thread_data_t *tsm; + snat_static_map_resolve_t *rp, *rp_match = 0; + nat44_lb_addr_port_t *local; + u32 find = ~0; + int i; + + /* If the external address is a specific interface address */ + if (sw_if_index != ~0) + { + ip4_address_t *first_int_addr; + + for (i = 0; i < vec_len (sm->to_resolve); i++) + { + rp = sm->to_resolve + i; + if (rp->sw_if_index != sw_if_index || + rp->l_addr.as_u32 != l_addr.as_u32 || + rp->vrf_id != vrf_id || rp->addr_only != addr_only) + continue; + + if (!addr_only) + { + if ((rp->l_port != l_port && rp->e_port != e_port) + || rp->proto != proto) + continue; + } + + rp_match = rp; + break; + } + + /* Might be already set... */ + first_int_addr = ip4_interface_first_address + (sm->ip4_main, sw_if_index, 0 /* just want the address */ ); + + if (is_add) + { + if (rp_match) + return VNET_API_ERROR_VALUE_EXIST; + + snat_add_static_mapping_when_resolved ( + sm, l_addr, l_port, sw_if_index, e_port, vrf_id, proto, addr_only, + tag, twice_nat, out2in_only, identity_nat, pool_addr, exact); + + /* DHCP resolution required? */ + if (first_int_addr == 0) + { + return 0; + } + else + { + e_addr.as_u32 = first_int_addr->as_u32; + /* Identity mapping? */ + if (l_addr.as_u32 == 0) + l_addr.as_u32 = e_addr.as_u32; + } + } + else + { + if (!rp_match) + return VNET_API_ERROR_NO_SUCH_ENTRY; + + vec_del1 (sm->to_resolve, i); + + if (first_int_addr) + { + e_addr.as_u32 = first_int_addr->as_u32; + /* Identity mapping? */ + if (l_addr.as_u32 == 0) + l_addr.as_u32 = e_addr.as_u32; + } + else + return 0; + } + } + + init_nat_k (&kv, e_addr, addr_only ? 0 : e_port, 0, addr_only ? 0 : proto); + if (clib_bihash_search_8_8 (&sm->static_mapping_by_external, &kv, &value)) + m = 0; + else + m = pool_elt_at_index (sm->static_mappings, value.value); + + if (is_add) + { + if (m) + { + if (is_identity_static_mapping (m)) + { + /* *INDENT-OFF* */ + pool_foreach (local, m->locals) + { + if (local->vrf_id == vrf_id) + return VNET_API_ERROR_VALUE_EXIST; + } + /* *INDENT-ON* */ + 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, + sm->fib_src_low); + init_nat_kv (&kv, m->local_addr, m->local_port, local->fib_index, + m->proto, 0, 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) + fib_index = + fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP4, vrf_id, + sm->fib_src_low); + /* If not specified use inside VRF id from SNAT plugin startup config */ + else + { + fib_index = sm->inside_fib_index; + vrf_id = sm->inside_vrf_id; + fib_table_lock (fib_index, FIB_PROTOCOL_IP4, sm->fib_src_low); + } + + if (!(out2in_only || identity_nat)) + { + init_nat_k (&kv, l_addr, addr_only ? 0 : l_port, fib_index, + addr_only ? 0 : proto); + if (!clib_bihash_search_8_8 + (&sm->static_mapping_by_local, &kv, &value)) + return VNET_API_ERROR_VALUE_EXIST; + } + + /* Find external address in allocated addresses and reserve port for + address and port pair mapping when dynamic translations enabled */ + if (!(addr_only || sm->static_mapping_only || out2in_only)) + { + for (i = 0; i < vec_len (sm->addresses); i++) + { + if (sm->addresses[i].addr.as_u32 == e_addr.as_u32) + { + a = sm->addresses + i; + /* External port must be unused */ + switch (proto) + { +#define _(N, j, n, s) \ + case NAT_PROTOCOL_##N: \ + if (a->busy_##n##_port_refcounts[e_port]) \ + return VNET_API_ERROR_INVALID_VALUE; \ + ++a->busy_##n##_port_refcounts[e_port]; \ + if (e_port > 1024) \ + { \ + a->busy_##n##_ports++; \ + a->busy_##n##_ports_per_thread[get_thread_idx_by_port(e_port)]++; \ + } \ + break; + foreach_nat_protocol +#undef _ + default : nat_elog_info (sm, "unknown protocol"); + return VNET_API_ERROR_INVALID_VALUE_2; + } + break; + } + } + /* External address must be allocated */ + if (!a && (l_addr.as_u32 != e_addr.as_u32)) + { + if (sw_if_index != ~0) + { + for (i = 0; i < vec_len (sm->to_resolve); i++) + { + rp = sm->to_resolve + i; + if (rp->addr_only) + continue; + if (rp->sw_if_index != sw_if_index && + rp->l_addr.as_u32 != l_addr.as_u32 && + rp->vrf_id != vrf_id && rp->l_port != l_port && + rp->e_port != e_port && rp->proto != proto) + continue; + + vec_del1 (sm->to_resolve, i); + break; + } + } + return VNET_API_ERROR_NO_SUCH_ENTRY; + } + } + + pool_get (sm->static_mappings, m); + clib_memset (m, 0, sizeof (*m)); + m->tag = vec_dup (tag); + m->local_addr = l_addr; + m->external_addr = e_addr; + m->twice_nat = twice_nat; + + if (twice_nat == TWICE_NAT && exact) + { + m->flags |= NAT_STATIC_MAPPING_FLAG_EXACT_ADDRESS; + m->pool_addr = pool_addr; + } + + 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; + pool_get (m->locals, local); + 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; + m->external_port = e_port; + m->proto = proto; + } + + if (sm->num_workers > 1) + { + ip4_header_t ip = { + .src_address = m->local_addr, + }; + vec_add1 (m->workers, sm->worker_in2out_cb (&ip, m->fib_index, 0)); + tsm = vec_elt_at_index (sm->per_thread_data, m->workers[0]); + } + else + tsm = vec_elt_at_index (sm->per_thread_data, sm->num_workers); + + if (!out2in_only) + { + init_nat_kv (&kv, m->local_addr, m->local_port, fib_index, m->proto, + 0, m - sm->static_mappings); + clib_bihash_add_del_8_8 (&sm->static_mapping_by_local, &kv, 1); + } + + init_nat_kv (&kv, m->external_addr, m->external_port, 0, m->proto, 0, + m - sm->static_mappings); + clib_bihash_add_del_8_8 (&sm->static_mapping_by_external, &kv, 1); + } + else + { + if (!m) + { + if (sw_if_index != ~0) + return 0; + else + return VNET_API_ERROR_NO_SUCH_ENTRY; + } + + if (identity_nat) + { + if (vrf_id == ~0) + vrf_id = sm->inside_vrf_id; + + /* *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; + + local = pool_elt_at_index (m->locals, find); + fib_index = local->fib_index; + pool_put (m->locals, local); + } + else + fib_index = m->fib_index; + + /* Free external address port */ + if (!(addr_only || sm->static_mapping_only || out2in_only)) + { + for (i = 0; i < vec_len (sm->addresses); i++) + { + if (sm->addresses[i].addr.as_u32 == e_addr.as_u32) + { + a = sm->addresses + i; + switch (proto) + { +#define _(N, j, n, s) \ + case NAT_PROTOCOL_##N: \ + --a->busy_##n##_port_refcounts[e_port]; \ + if (e_port > 1024) \ + { \ + a->busy_##n##_ports--; \ + a->busy_##n##_ports_per_thread[get_thread_idx_by_port(e_port)]--; \ + } \ + break; + foreach_nat_protocol +#undef _ + default : nat_elog_info (sm, "unknown protocol"); + return VNET_API_ERROR_INVALID_VALUE_2; + } + break; + } + } + } + + if (sm->num_workers > 1) + tsm = vec_elt_at_index (sm->per_thread_data, m->workers[0]); + else + tsm = vec_elt_at_index (sm->per_thread_data, sm->num_workers); + + init_nat_k (&kv, m->local_addr, m->local_port, fib_index, m->proto); + if (!out2in_only) + clib_bihash_add_del_8_8 (&sm->static_mapping_by_local, &kv, 0); + + /* Delete session(s) for static mapping if exist */ + if (!(sm->static_mapping_only) || + (sm->static_mapping_only && sm->static_mapping_connection_tracking)) + { + nat_ed_static_mapping_del_sessions ( + sm, tsm, m->local_addr, m->local_port, m->proto, fib_index, + addr_only, e_addr, e_port); + } + + fib_table_unlock (fib_index, FIB_PROTOCOL_IP4, sm->fib_src_low); + if (pool_elts (m->locals)) + return 0; + + init_nat_k (&kv, m->external_addr, m->external_port, 0, m->proto); + 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 */ + pool_put (sm->static_mappings, m); + } + + if (!addr_only || (l_addr.as_u32 == e_addr.as_u32)) + return 0; + + /* Add/delete external address to FIB */ + /* *INDENT-OFF* */ + pool_foreach (interface, sm->interfaces) + { + if (nat_interface_is_inside (interface)) + continue; + + snat_add_del_addr_to_fib (&e_addr, 32, interface->sw_if_index, is_add); + break; + } + pool_foreach (interface, sm->output_feature_interfaces) + { + if (nat_interface_is_inside (interface)) + continue; + + snat_add_del_addr_to_fib (&e_addr, 32, interface->sw_if_index, is_add); + break; + } + /* *INDENT-ON* */ + + return 0; +} + +int +nat44_add_del_lb_static_mapping (ip4_address_t e_addr, u16 e_port, + nat_protocol_t proto, + nat44_lb_addr_port_t * locals, u8 is_add, + twice_nat_type_t twice_nat, u8 out2in_only, + u8 * tag, u32 affinity) +{ + snat_main_t *sm = &snat_main; + snat_static_mapping_t *m; + clib_bihash_kv_8_8_t kv, value; + snat_address_t *a = 0; + int i; + nat44_lb_addr_port_t *local; + snat_main_per_thread_data_t *tsm; + snat_session_t *s; + uword *bitmap = 0; + + init_nat_k (&kv, e_addr, e_port, 0, proto); + if (clib_bihash_search_8_8 (&sm->static_mapping_by_external, &kv, &value)) + m = 0; + else + m = pool_elt_at_index (sm->static_mappings, value.value); + + if (is_add) + { + if (m) + return VNET_API_ERROR_VALUE_EXIST; + + if (vec_len (locals) < 2) + return VNET_API_ERROR_INVALID_VALUE; + + /* Find external address in allocated addresses and reserve port for + address and port pair mapping when dynamic translations enabled */ + if (!(sm->static_mapping_only || out2in_only)) + { + for (i = 0; i < vec_len (sm->addresses); i++) + { + if (sm->addresses[i].addr.as_u32 == e_addr.as_u32) + { + a = sm->addresses + i; + /* External port must be unused */ + switch (proto) + { +#define _(N, j, n, s) \ + case NAT_PROTOCOL_##N: \ + if (a->busy_##n##_port_refcounts[e_port]) \ + return VNET_API_ERROR_INVALID_VALUE; \ + ++a->busy_##n##_port_refcounts[e_port]; \ + if (e_port > 1024) \ + { \ + a->busy_##n##_ports++; \ + a->busy_##n##_ports_per_thread[get_thread_idx_by_port(e_port)]++; \ + } \ + break; + foreach_nat_protocol +#undef _ + default : nat_elog_info (sm, "unknown protocol"); + return VNET_API_ERROR_INVALID_VALUE_2; + } + break; + } + } + /* External address must be allocated */ + if (!a) + return VNET_API_ERROR_NO_SUCH_ENTRY; + } + + pool_get (sm->static_mappings, m); + clib_memset (m, 0, sizeof (*m)); + m->tag = vec_dup (tag); + m->external_addr = e_addr; + 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; + + if (affinity) + m->affinity_per_service_list_head_index = + nat_affinity_get_per_service_list_head_index (); + else + m->affinity_per_service_list_head_index = ~0; + + init_nat_kv (&kv, m->external_addr, m->external_port, 0, m->proto, 0, + m - sm->static_mappings); + if (clib_bihash_add_del_8_8 (&sm->static_mapping_by_external, &kv, 1)) + { + nat_elog_err (sm, "static_mapping_by_external key add failed"); + return VNET_API_ERROR_UNSPECIFIED; + } + + for (i = 0; i < vec_len (locals); i++) + { + locals[i].fib_index = + fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP4, + locals[i].vrf_id, + sm->fib_src_low); + if (!out2in_only) + { + init_nat_kv (&kv, locals[i].addr, locals[i].port, + locals[i].fib_index, m->proto, 0, + m - sm->static_mappings); + clib_bihash_add_del_8_8 (&sm->static_mapping_by_local, &kv, 1); + } + locals[i].prefix = (i == 0) ? locals[i].probability : + (locals[i - 1].prefix + locals[i].probability); + pool_get (m->locals, local); + *local = locals[i]; + if (sm->num_workers > 1) + { + ip4_header_t ip = { + .src_address = locals[i].addr, + }; + bitmap = + clib_bitmap_set (bitmap, + sm->worker_in2out_cb (&ip, m->fib_index, 0), + 1); + } + } + + /* Assign workers */ + if (sm->num_workers > 1) + { + /* *INDENT-OFF* */ + clib_bitmap_foreach (i, bitmap) + { + vec_add1(m->workers, i); + } + /* *INDENT-ON* */ + } + } + else + { + 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)) + { + for (i = 0; i < vec_len (sm->addresses); i++) + { + if (sm->addresses[i].addr.as_u32 == e_addr.as_u32) + { + a = sm->addresses + i; + switch (proto) + { +#define _(N, j, n, s) \ + case NAT_PROTOCOL_##N: \ + --a->busy_##n##_port_refcounts[e_port]; \ + if (e_port > 1024) \ + { \ + a->busy_##n##_ports--; \ + a->busy_##n##_ports_per_thread[get_thread_idx_by_port(e_port)]--; \ + } \ + break; + foreach_nat_protocol +#undef _ + default : nat_elog_info (sm, "unknown protocol"); + return VNET_API_ERROR_INVALID_VALUE_2; + } + break; + } + } + } + + init_nat_k (&kv, m->external_addr, m->external_port, 0, m->proto); + if (clib_bihash_add_del_8_8 (&sm->static_mapping_by_external, &kv, 0)) + { + nat_elog_err (sm, "static_mapping_by_external key del failed"); + return VNET_API_ERROR_UNSPECIFIED; + } + + /* *INDENT-OFF* */ + pool_foreach (local, m->locals) + { + fib_table_unlock (local->fib_index, FIB_PROTOCOL_IP4, + sm->fib_src_low); + if (!out2in_only) + { +init_nat_k(& kv, local->addr, local->port, local->fib_index, m->proto); + if (clib_bihash_add_del_8_8(&sm->static_mapping_by_local, &kv, 0)) + { + nat_elog_err (sm, "static_mapping_by_local key del failed"); + return VNET_API_ERROR_UNSPECIFIED; + } + } + + 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, 0)); + } + else + tsm = vec_elt_at_index (sm->per_thread_data, sm->num_workers); + + /* Delete sessions */ + pool_foreach (s, tsm->sessions) + { + if (!(is_lb_session (s))) + continue; + + if ((s->in2out.addr.as_u32 != local->addr.as_u32) || + s->in2out.port != local->port) + continue; + + nat_free_session_data (sm, s, tsm - sm->per_thread_data, 0); + nat_ed_session_delete (sm, s, tsm - sm->per_thread_data, 1); + } + } + /* *INDENT-ON* */ + if (m->affinity) + nat_affinity_flush_service (m->affinity_per_service_list_head_index); + pool_free (m->locals); + vec_free (m->tag); + vec_free (m->workers); + + pool_put (sm->static_mappings, m); + } + + return 0; +} + +int +nat44_lb_static_mapping_add_del_local (ip4_address_t e_addr, u16 e_port, + ip4_address_t l_addr, u16 l_port, + nat_protocol_t proto, u32 vrf_id, + u8 probability, u8 is_add) +{ + snat_main_t *sm = &snat_main; + snat_static_mapping_t *m = 0; + 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_session_t *s; + u32 *locals = 0; + uword *bitmap = 0; + int i; + + init_nat_k (&kv, e_addr, e_port, 0, proto); + 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, + sm->fib_src_low); + + if (!is_out2in_only_static_mapping (m)) + { + init_nat_kv (&kv, l_addr, l_port, local->fib_index, proto, 0, + m - sm->static_mappings); + if (clib_bihash_add_del_8_8 (&sm->static_mapping_by_local, &kv, 1)) + nat_elog_err (sm, "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, + sm->fib_src_low); + + if (!is_out2in_only_static_mapping (m)) + { + init_nat_k (&kv, l_addr, l_port, match_local->fib_index, proto); + if (clib_bihash_add_del_8_8 (&sm->static_mapping_by_local, &kv, 0)) + nat_elog_err (sm, "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, + 0)); + } + else + tsm = vec_elt_at_index (sm->per_thread_data, sm->num_workers); + + /* Delete sessions */ + /* *INDENT-OFF* */ + pool_foreach (s, tsm->sessions) { + if (!(is_lb_session (s))) + continue; + + if ((s->in2out.addr.as_u32 != match_local->addr.as_u32) || + s->in2out.port != match_local->port) + continue; + + nat_free_session_data (sm, s, tsm - sm->per_thread_data, 0); + nat_ed_session_delete (sm, s, tsm - sm->per_thread_data, 1); + } + /* *INDENT-ON* */ + + 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, 0), + 1); + } + } + /* *INDENT-ON* */ + + ASSERT (vec_len (locals) > 1); + + 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) +{ + snat_address_t *a = 0; + snat_session_t *ses; + u32 *ses_to_be_removed = 0, *ses_index; + snat_main_per_thread_data_t *tsm; + snat_static_mapping_t *m; + snat_interface_t *interface; + int i; + snat_address_t *addresses = + twice_nat ? sm->twice_nat_addresses : sm->addresses; + + /* Find SNAT address */ + for (i = 0; i < vec_len (addresses); i++) + { + if (addresses[i].addr.as_u32 == addr.as_u32) + { + a = addresses + i; + break; + } + } + if (!a) + { + nat_log_err ("no such address"); + return VNET_API_ERROR_NO_SUCH_ENTRY; + } + + if (delete_sm) + { + ip4_address_t pool_addr = { 0 }; + /* *INDENT-OFF* */ + pool_foreach (m, sm->static_mappings) + { + if (m->external_addr.as_u32 == addr.as_u32) + (void) snat_add_static_mapping (m->local_addr, m->external_addr, + m->local_port, m->external_port, + m->vrf_id, + is_addr_only_static_mapping(m), ~0, + m->proto, 0 /* is_add */, + m->twice_nat, + is_out2in_only_static_mapping(m), + m->tag, + is_identity_static_mapping(m), + pool_addr, 0); + } + /* *INDENT-ON* */ + } + else + { + /* Check if address is used in some static mapping */ + if (is_snat_address_used_in_static_mapping (sm, addr)) + { + nat_log_err ("address used in static mapping"); + return VNET_API_ERROR_UNSPECIFIED; + } + } + + if (a->fib_index != ~0) + fib_table_unlock (a->fib_index, FIB_PROTOCOL_IP4, sm->fib_src_low); + + /* Delete sessions using address */ + if (a->busy_tcp_ports || a->busy_udp_ports || a->busy_icmp_ports) + { + vec_foreach (tsm, sm->per_thread_data) + { + pool_foreach (ses, tsm->sessions) { + if (ses->out2in.addr.as_u32 == addr.as_u32) + { + nat_free_session_data (sm, ses, tsm - sm->per_thread_data, 0); + vec_add1 (ses_to_be_removed, ses - tsm->sessions); + } + } + + vec_foreach (ses_index, ses_to_be_removed) + { + ses = pool_elt_at_index (tsm->sessions, ses_index[0]); + nat_ed_session_delete (sm, ses, tsm - sm->per_thread_data, 1); + } + + vec_free (ses_to_be_removed); + } + } + +#define _(N, i, n, s) \ + vec_free (a->busy_##n##_ports_per_thread); + foreach_nat_protocol +#undef _ + + if (twice_nat) + { + vec_del1 (sm->twice_nat_addresses, i); + return 0; + } + else vec_del1 (sm->addresses, i); + + /* Delete external address from FIB */ + pool_foreach (interface, sm->interfaces) + { + if (nat_interface_is_inside (interface)) + continue; + + snat_add_del_addr_to_fib (&addr, 32, interface->sw_if_index, 0); + break; + } + pool_foreach (interface, sm->output_feature_interfaces) + { + if (nat_interface_is_inside (interface)) + continue; + + snat_add_del_addr_to_fib (&addr, 32, interface->sw_if_index, 0); + break; + } + + return 0; +} + +void +expire_per_vrf_sessions (u32 fib_index) +{ + per_vrf_sessions_t *per_vrf_sessions; + snat_main_per_thread_data_t *tsm; + snat_main_t *sm = &snat_main; + + /* *INDENT-OFF* */ + vec_foreach (tsm, sm->per_thread_data) + { + vec_foreach (per_vrf_sessions, tsm->per_vrf_sessions_vec) + { + if ((per_vrf_sessions->rx_fib_index == fib_index) || + (per_vrf_sessions->tx_fib_index == fib_index)) + { + per_vrf_sessions->expired = 1; + } + } + } + /* *INDENT-ON* */ +} + +void +update_per_vrf_sessions_vec (u32 fib_index, int is_del) +{ + snat_main_t *sm = &snat_main; + nat_fib_t *fib; + + // we don't care if it is outside/inside fib + // we just care about their ref_count + // if it reaches 0 sessions should expire + // because the fib isn't valid for NAT anymore + + vec_foreach (fib, sm->fibs) + { + if (fib->fib_index == fib_index) + { + if (is_del) + { + fib->ref_count--; + if (!fib->ref_count) + { + vec_del1 (sm->fibs, fib - sm->fibs); + expire_per_vrf_sessions (fib_index); + } + return; + } + else + fib->ref_count++; + } + } + if (!is_del) + { + vec_add2 (sm->fibs, fib, 1); + fib->ref_count = 1; + fib->fib_index = fib_index; + } +} + +int +snat_interface_add_del (u32 sw_if_index, u8 is_inside, int is_del) +{ + snat_main_t *sm = &snat_main; + snat_interface_t *i; + const char *feature_name, *del_feature_name; + snat_address_t *ap; + snat_static_mapping_t *m; + nat_outside_fib_t *outside_fib; + u32 fib_index = fib_table_get_index_for_sw_if_index (FIB_PROTOCOL_IP4, + sw_if_index); + + if (!sm->enabled) + { + nat_log_err ("nat44 is disabled"); + return VNET_API_ERROR_UNSUPPORTED; + } + + /* *INDENT-OFF* */ + pool_foreach (i, sm->output_feature_interfaces) + { + if (i->sw_if_index == sw_if_index) + { + nat_log_err ("error interface already configured"); + return VNET_API_ERROR_VALUE_EXIST; + } + } + /* *INDENT-ON* */ + + if (sm->static_mapping_only && !(sm->static_mapping_connection_tracking)) + feature_name = is_inside ? "nat44-in2out-fast" : "nat44-out2in-fast"; + else + { + if (sm->num_workers > 1) + feature_name = + is_inside ? "nat44-in2out-worker-handoff" : + "nat44-out2in-worker-handoff"; + else + feature_name = is_inside ? "nat-pre-in2out" : "nat-pre-out2in"; + } + + ASSERT (sm->frame_queue_nelts > 0); + + if (sm->fq_in2out_index == ~0 && sm->num_workers > 1) + sm->fq_in2out_index = vlib_frame_queue_main_init (sm->in2out_node_index, + sm->frame_queue_nelts); + + if (sm->fq_out2in_index == ~0 && sm->num_workers > 1) + sm->fq_out2in_index = vlib_frame_queue_main_init (sm->out2in_node_index, + sm->frame_queue_nelts); + + update_per_vrf_sessions_vec (fib_index, is_del); + + if (!is_inside) + { + /* *INDENT-OFF* */ + vec_foreach (outside_fib, sm->outside_fibs) + { + if (outside_fib->fib_index == fib_index) + { + if (is_del) + { + outside_fib->refcount--; + if (!outside_fib->refcount) + vec_del1 (sm->outside_fibs, outside_fib - sm->outside_fibs); + } + else + outside_fib->refcount++; + goto feature_set; + } + } + /* *INDENT-ON* */ + if (!is_del) + { + vec_add2 (sm->outside_fibs, outside_fib, 1); + outside_fib->refcount = 1; + outside_fib->fib_index = fib_index; + } + } + +feature_set: + /* *INDENT-OFF* */ + pool_foreach (i, sm->interfaces) + { + if (i->sw_if_index == sw_if_index) + { + if (is_del) + { + if (nat_interface_is_inside(i) && nat_interface_is_outside(i)) + { + if (is_inside) + i->flags &= ~NAT_INTERFACE_FLAG_IS_INSIDE; + else + i->flags &= ~NAT_INTERFACE_FLAG_IS_OUTSIDE; + + if (sm->num_workers > 1) + { + del_feature_name = "nat44-handoff-classify"; + feature_name = !is_inside ? "nat44-in2out-worker-handoff" : + "nat44-out2in-worker-handoff"; + } + else + { + del_feature_name = "nat44-ed-classify"; + feature_name = + !is_inside ? "nat-pre-in2out" : "nat-pre-out2in"; + } + + int rv = ip4_sv_reass_enable_disable_with_refcnt (sw_if_index, 0); + if (rv) + return rv; + vnet_feature_enable_disable ("ip4-unicast", del_feature_name, + sw_if_index, 0, 0, 0); + vnet_feature_enable_disable ("ip4-unicast", feature_name, + sw_if_index, 1, 0, 0); + } + else + { + int rv = ip4_sv_reass_enable_disable_with_refcnt (sw_if_index, 0); + if (rv) + return rv; + vnet_feature_enable_disable ("ip4-unicast", feature_name, + sw_if_index, 0, 0, 0); + pool_put (sm->interfaces, i); + } + } + else + { + if ((nat_interface_is_inside (i) && is_inside) || + (nat_interface_is_outside (i) && !is_inside)) + return 0; + + if (sm->num_workers > 1) + { + del_feature_name = !is_inside ? "nat44-in2out-worker-handoff" : + "nat44-out2in-worker-handoff"; + feature_name = "nat44-handoff-classify"; + } + else + { + del_feature_name = + !is_inside ? "nat-pre-in2out" : "nat-pre-out2in"; + + feature_name = "nat44-ed-classify"; + } + + int rv = ip4_sv_reass_enable_disable_with_refcnt (sw_if_index, 1); + if (rv) + return rv; + vnet_feature_enable_disable ("ip4-unicast", del_feature_name, + sw_if_index, 0, 0, 0); + vnet_feature_enable_disable ("ip4-unicast", feature_name, + sw_if_index, 1, 0, 0); + goto set_flags; + } + + goto fib; + } + } + /* *INDENT-ON* */ + + if (is_del) + { + nat_log_err ("error interface couldn't be found"); + return VNET_API_ERROR_NO_SUCH_ENTRY; + } + + pool_get (sm->interfaces, i); + i->sw_if_index = sw_if_index; + i->flags = 0; + nat_validate_interface_counters (sm, sw_if_index); + + vnet_feature_enable_disable ("ip4-unicast", feature_name, sw_if_index, 1, 0, + 0); + + int rv = ip4_sv_reass_enable_disable_with_refcnt (sw_if_index, 1); + if (rv) + return rv; + +set_flags: + if (is_inside) + { + i->flags |= NAT_INTERFACE_FLAG_IS_INSIDE; + return 0; + } + else + i->flags |= NAT_INTERFACE_FLAG_IS_OUTSIDE; + + /* Add/delete external addresses to FIB */ +fib: + /* *INDENT-OFF* */ + vec_foreach (ap, sm->addresses) + snat_add_del_addr_to_fib(&ap->addr, 32, sw_if_index, !is_del); + + pool_foreach (m, sm->static_mappings) + { + if (!(is_addr_only_static_mapping(m)) || (m->local_addr.as_u32 == m->external_addr.as_u32)) + continue; + + snat_add_del_addr_to_fib(&m->external_addr, 32, sw_if_index, !is_del); + } + /* *INDENT-ON* */ + + return 0; +} + +int +snat_interface_add_del_output_feature (u32 sw_if_index, + u8 is_inside, int is_del) +{ + snat_main_t *sm = &snat_main; + snat_interface_t *i; + snat_address_t *ap; + snat_static_mapping_t *m; + nat_outside_fib_t *outside_fib; + u32 fib_index = fib_table_get_index_for_sw_if_index (FIB_PROTOCOL_IP4, + sw_if_index); + + if (!sm->enabled) + { + nat_log_err ("nat44 is disabled"); + return VNET_API_ERROR_UNSUPPORTED; + } + + if (sm->static_mapping_only && !(sm->static_mapping_connection_tracking)) + { + nat_log_err ("error unsupported"); + return VNET_API_ERROR_UNSUPPORTED; + } + + /* *INDENT-OFF* */ + pool_foreach (i, sm->interfaces) + { + if (i->sw_if_index == sw_if_index) + { + nat_log_err ("error interface already configured"); + return VNET_API_ERROR_VALUE_EXIST; + } + } + /* *INDENT-ON* */ + + update_per_vrf_sessions_vec (fib_index, is_del); + + if (!is_inside) + { + /* *INDENT-OFF* */ + vec_foreach (outside_fib, sm->outside_fibs) + { + if (outside_fib->fib_index == fib_index) + { + if (is_del) + { + outside_fib->refcount--; + if (!outside_fib->refcount) + vec_del1 (sm->outside_fibs, outside_fib - sm->outside_fibs); + } + else + outside_fib->refcount++; + goto feature_set; + } + } + /* *INDENT-ON* */ + if (!is_del) + { + vec_add2 (sm->outside_fibs, outside_fib, 1); + outside_fib->refcount = 1; + outside_fib->fib_index = fib_index; + } + } + +feature_set: + if (is_inside) + { + int rv = + ip4_sv_reass_enable_disable_with_refcnt (sw_if_index, !is_del); + if (rv) + return rv; + rv = + ip4_sv_reass_output_enable_disable_with_refcnt (sw_if_index, + !is_del); + if (rv) + return rv; + goto fq; + } + + if (sm->num_workers > 1) + { + int rv = ip4_sv_reass_enable_disable_with_refcnt (sw_if_index, !is_del); + if (rv) + return rv; + rv = + ip4_sv_reass_output_enable_disable_with_refcnt (sw_if_index, !is_del); + if (rv) + return rv; + vnet_feature_enable_disable ("ip4-unicast", + "nat44-out2in-worker-handoff", + sw_if_index, !is_del, 0, 0); + vnet_feature_enable_disable ("ip4-output", + "nat44-in2out-output-worker-handoff", + sw_if_index, !is_del, 0, 0); + } + else + { + int rv = + ip4_sv_reass_enable_disable_with_refcnt (sw_if_index, !is_del); + if (rv) + return rv; + rv = + ip4_sv_reass_output_enable_disable_with_refcnt (sw_if_index, + !is_del); + if (rv) + return rv; + vnet_feature_enable_disable ("ip4-unicast", "nat-pre-out2in", + sw_if_index, !is_del, 0, 0); + vnet_feature_enable_disable ("ip4-output", "nat-pre-in2out-output", + sw_if_index, !is_del, 0, 0); + } + +fq: + if (sm->fq_in2out_output_index == ~0 && sm->num_workers > 1) + sm->fq_in2out_output_index = + vlib_frame_queue_main_init (sm->in2out_output_node_index, 0); + + if (sm->fq_out2in_index == ~0 && sm->num_workers > 1) + sm->fq_out2in_index = + vlib_frame_queue_main_init (sm->out2in_node_index, 0); + + /* *INDENT-OFF* */ + pool_foreach (i, sm->output_feature_interfaces) + { + if (i->sw_if_index == sw_if_index) + { + if (is_del) + pool_put (sm->output_feature_interfaces, i); + else + return VNET_API_ERROR_VALUE_EXIST; + + goto fib; + } + } + /* *INDENT-ON* */ + + if (is_del) + { + nat_log_err ("error interface couldn't be found"); + return VNET_API_ERROR_NO_SUCH_ENTRY; + } + + pool_get (sm->output_feature_interfaces, i); + i->sw_if_index = sw_if_index; + i->flags = 0; + nat_validate_interface_counters (sm, sw_if_index); + if (is_inside) + i->flags |= NAT_INTERFACE_FLAG_IS_INSIDE; + else + i->flags |= NAT_INTERFACE_FLAG_IS_OUTSIDE; + + /* Add/delete external addresses to FIB */ +fib: + if (is_inside) + return 0; + + /* *INDENT-OFF* */ + vec_foreach (ap, sm->addresses) + snat_add_del_addr_to_fib(&ap->addr, 32, sw_if_index, !is_del); + + pool_foreach (m, sm->static_mappings) + { + if (!((is_addr_only_static_mapping(m))) || (m->local_addr.as_u32 == m->external_addr.as_u32)) + continue; + + snat_add_del_addr_to_fib(&m->external_addr, 32, sw_if_index, !is_del); + } + /* *INDENT-ON* */ + + return 0; +} + +int +snat_set_workers (uword * bitmap) +{ + snat_main_t *sm = &snat_main; + int i, j = 0; + + if (sm->num_workers < 2) + return VNET_API_ERROR_FEATURE_DISABLED; + + if (clib_bitmap_last_set (bitmap) >= sm->num_workers) + return VNET_API_ERROR_INVALID_WORKER; + + vec_free (sm->workers); + /* *INDENT-OFF* */ + clib_bitmap_foreach (i, bitmap) + { + vec_add1(sm->workers, i); + sm->per_thread_data[sm->first_worker_index + i].snat_thread_index = j; + sm->per_thread_data[sm->first_worker_index + i].thread_index = i; + j++; + } + /* *INDENT-ON* */ + + sm->port_per_thread = (0xffff - 1024) / _vec_len (sm->workers); + + return 0; +} + +int +nat44_ed_set_frame_queue_nelts (u32 frame_queue_nelts) +{ + fail_if_enabled (); + snat_main_t *sm = &snat_main; + sm->frame_queue_nelts = frame_queue_nelts; + return 0; +} + +static void +snat_update_outside_fib (ip4_main_t * im, uword opaque, + u32 sw_if_index, u32 new_fib_index, + u32 old_fib_index) +{ + snat_main_t *sm = &snat_main; + nat_outside_fib_t *outside_fib; + snat_interface_t *i; + u8 is_add = 1; + u8 match = 0; + + if (!sm->enabled || (new_fib_index == old_fib_index) + || (!vec_len (sm->outside_fibs))) + { + return; + } + + /* *INDENT-OFF* */ + pool_foreach (i, sm->interfaces) + { + if (i->sw_if_index == sw_if_index) + { + if (!(nat_interface_is_outside (i))) + return; + match = 1; + } + } + + pool_foreach (i, sm->output_feature_interfaces) + { + if (i->sw_if_index == sw_if_index) + { + if (!(nat_interface_is_outside (i))) + return; + match = 1; + } + } + /* *INDENT-ON* */ + + if (!match) + return; + + vec_foreach (outside_fib, sm->outside_fibs) + { + if (outside_fib->fib_index == old_fib_index) + { + outside_fib->refcount--; + if (!outside_fib->refcount) + vec_del1 (sm->outside_fibs, outside_fib - sm->outside_fibs); + break; + } + } + + vec_foreach (outside_fib, sm->outside_fibs) + { + if (outside_fib->fib_index == new_fib_index) + { + outside_fib->refcount++; + is_add = 0; + break; + } + } + + if (is_add) + { + vec_add2 (sm->outside_fibs, outside_fib, 1); + outside_fib->refcount = 1; + outside_fib->fib_index = new_fib_index; + } +} + +static void +snat_update_outside_fib (ip4_main_t * im, uword opaque, + u32 sw_if_index, u32 new_fib_index, + u32 old_fib_index); + +static void +snat_ip4_add_del_interface_address_cb (ip4_main_t * im, + uword opaque, + u32 sw_if_index, + ip4_address_t * address, + u32 address_length, + u32 if_address_index, u32 is_delete); + +static void +nat_ip4_add_del_addr_only_sm_cb (ip4_main_t * im, + uword opaque, + u32 sw_if_index, + ip4_address_t * address, + u32 address_length, + u32 if_address_index, u32 is_delete); + +void +test_key_calc_split () +{ + ip4_address_t l_addr; + l_addr.as_u8[0] = 1; + l_addr.as_u8[1] = 1; + l_addr.as_u8[2] = 1; + l_addr.as_u8[3] = 1; + ip4_address_t r_addr; + r_addr.as_u8[0] = 2; + r_addr.as_u8[1] = 2; + r_addr.as_u8[2] = 2; + r_addr.as_u8[3] = 2; + u16 l_port = 40001; + u16 r_port = 40301; + u8 proto = 9; + u32 fib_index = 9000001; + u32 thread_index = 3000000001; + u32 session_index = 3000000221; + clib_bihash_kv_16_8_t kv; + init_ed_kv (&kv, l_addr, l_port, r_addr, r_port, fib_index, proto, + thread_index, session_index); + ip4_address_t l_addr2; + ip4_address_t r_addr2; + clib_memset (&l_addr2, 0, sizeof (l_addr2)); + clib_memset (&r_addr2, 0, sizeof (r_addr2)); + u16 l_port2 = 0; + u16 r_port2 = 0; + u8 proto2 = 0; + u32 fib_index2 = 0; + split_ed_kv (&kv, &l_addr2, &r_addr2, &proto2, &fib_index2, &l_port2, + &r_port2); + ASSERT (l_addr.as_u32 == l_addr2.as_u32); + ASSERT (r_addr.as_u32 == r_addr2.as_u32); + ASSERT (l_port == l_port2); + ASSERT (r_port == r_port2); + ASSERT (proto == proto2); + ASSERT (fib_index == fib_index2); + ASSERT (thread_index == ed_value_get_thread_index (&kv)); + ASSERT (session_index == ed_value_get_session_index (&kv)); + + fib_index = 7001; + proto = 5; + nat_protocol_t proto3 = ~0; + u64 key = calc_nat_key (l_addr, l_port, fib_index, proto); + split_nat_key (key, &l_addr2, &l_port2, &fib_index2, &proto3); + ASSERT (l_addr.as_u32 == l_addr2.as_u32); + ASSERT (l_port == l_port2); + ASSERT (proto == proto3); + ASSERT (fib_index == fib_index2); +} + +static clib_error_t * +nat_ip_table_add_del (vnet_main_t * vnm, u32 table_id, u32 is_add) +{ + u32 fib_index; + + // TODO: consider removing all NAT interfaces + if (!is_add) + { + fib_index = ip4_fib_index_from_table_id (table_id); + if (fib_index != ~0) + expire_per_vrf_sessions (fib_index); + } + return 0; +} + +VNET_IP_TABLE_ADD_DEL_FUNCTION (nat_ip_table_add_del); + +void +nat44_set_node_indexes (snat_main_t * sm, vlib_main_t * vm) +{ + vlib_node_t *node; + + node = vlib_get_node_by_name (vm, (u8 *) "nat44-ed-out2in"); + sm->in2out_node_index = node->index; + + node = vlib_get_node_by_name (vm, (u8 *) "nat44-ed-in2out"); + sm->out2in_node_index = node->index; + + node = vlib_get_node_by_name (vm, (u8 *) "nat44-ed-in2out-output"); + sm->in2out_output_node_index = node->index; +} + +#define nat_validate_simple_counter(c, i) \ + do \ + { \ + vlib_validate_simple_counter (&c, i); \ + vlib_zero_simple_counter (&c, i); \ + } \ + while (0); + +#define nat_init_simple_counter(c, n, sn) \ + do \ + { \ + c.name = n; \ + c.stat_segment_name = sn; \ + nat_validate_simple_counter (c, 0); \ + } \ + while (0); + +static_always_inline void +nat_validate_interface_counters (snat_main_t *sm, u32 sw_if_index) +{ +#define _(x) \ + nat_validate_simple_counter (sm->counters.fastpath.in2out.x, sw_if_index); \ + nat_validate_simple_counter (sm->counters.fastpath.out2in.x, sw_if_index); \ + nat_validate_simple_counter (sm->counters.slowpath.in2out.x, sw_if_index); \ + nat_validate_simple_counter (sm->counters.slowpath.out2in.x, sw_if_index); + foreach_nat_counter; +#undef _ + nat_validate_simple_counter (sm->counters.hairpinning, sw_if_index); +} + +static clib_error_t * +nat_init (vlib_main_t * vm) +{ + snat_main_t *sm = &snat_main; + vlib_thread_main_t *tm = vlib_get_thread_main (); + vlib_thread_registration_t *tr; + ip4_add_del_interface_address_callback_t cbi = { 0 }; + ip4_table_bind_callback_t cbt = { 0 }; + u32 i, num_threads = 0; + uword *p, *bitmap = 0; + + clib_memset (sm, 0, sizeof (*sm)); + + // required + sm->vnet_main = vnet_get_main (); + // convenience + sm->ip4_main = &ip4_main; + sm->api_main = vlibapi_get_main (); + sm->ip4_lookup_main = &ip4_main.lookup_main; + + // frame queue indices used for handoff + sm->fq_out2in_index = ~0; + sm->fq_in2out_index = ~0; + sm->fq_in2out_output_index = ~0; + + sm->log_level = NAT_LOG_ERROR; + + nat44_set_node_indexes (sm, vm); + sm->log_class = vlib_log_register_class ("nat", 0); + nat_ipfix_logging_init (vm); + + nat_init_simple_counter (sm->total_sessions, "total-sessions", + "/nat44-ed/total-sessions"); + +#define _(x) \ + nat_init_simple_counter (sm->counters.fastpath.in2out.x, #x, \ + "/nat44-ed/in2out/fastpath/" #x); \ + nat_init_simple_counter (sm->counters.fastpath.out2in.x, #x, \ + "/nat44-ed/out2in/fastpath/" #x); \ + nat_init_simple_counter (sm->counters.slowpath.in2out.x, #x, \ + "/nat44-ed/in2out/slowpath/" #x); \ + nat_init_simple_counter (sm->counters.slowpath.out2in.x, #x, \ + "/nat44-ed/out2in/slowpath/" #x); + foreach_nat_counter; +#undef _ + nat_init_simple_counter (sm->counters.hairpinning, "hairpinning", + "/nat44-ed/hairpinning"); + + p = hash_get_mem (tm->thread_registrations_by_name, "workers"); + if (p) + { + tr = (vlib_thread_registration_t *) p[0]; + if (tr) + { + sm->num_workers = tr->count; + sm->first_worker_index = tr->first_index; + } + } + num_threads = tm->n_vlib_mains - 1; + sm->port_per_thread = 0xffff - 1024; + vec_validate (sm->per_thread_data, num_threads); + + /* Use all available workers by default */ + if (sm->num_workers > 1) + { + + for (i = 0; i < sm->num_workers; i++) + bitmap = clib_bitmap_set (bitmap, i, 1); + snat_set_workers (bitmap); + clib_bitmap_free (bitmap); + } + else + sm->per_thread_data[0].snat_thread_index = 0; + + /* callbacks to call when interface address changes. */ + cbi.function = snat_ip4_add_del_interface_address_cb; + vec_add1 (sm->ip4_main->add_del_interface_address_callbacks, cbi); + cbi.function = nat_ip4_add_del_addr_only_sm_cb; + vec_add1 (sm->ip4_main->add_del_interface_address_callbacks, cbi); + + /* callbacks to call when interface to table biding changes */ + cbt.function = snat_update_outside_fib; + vec_add1 (sm->ip4_main->table_bind_callbacks, cbt); + + sm->fib_src_low = + fib_source_allocate ("nat-low", FIB_SOURCE_PRIORITY_LOW, + FIB_SOURCE_BH_SIMPLE); + sm->fib_src_hi = + fib_source_allocate ("nat-hi", FIB_SOURCE_PRIORITY_HI, + FIB_SOURCE_BH_SIMPLE); + + nat_affinity_init (vm); + test_key_calc_split (); + + return nat44_api_hookup (vm); +} + +VLIB_INIT_FUNCTION (nat_init); + +int +nat44_plugin_enable (nat44_config_t c) +{ + snat_main_t *sm = &snat_main; + + fail_if_enabled (); + + // UPDATE based on these appropriate API/CLI + // c.static_mapping_only + c.connection_tracking + // - supported in NAT EI & NAT ED + // c.out2in_dpo, c.static_mapping_only + // - supported in NAT EI + + if (c.static_mapping_only && !c.connection_tracking) + { + nat_log_err ("unsupported combination of configuration"); + return 1; + } + + // nat44 feature configuration + sm->static_mapping_only = c.static_mapping_only; + sm->static_mapping_connection_tracking = c.connection_tracking; + + sm->forwarding_enabled = 0; + sm->mss_clamping = 0; + sm->pat = (!c.static_mapping_only || + (c.static_mapping_only && c.connection_tracking)); + + if (!c.sessions) + c.sessions = 63 * 1024; + + sm->max_translations_per_thread = c.sessions; + sm->translation_buckets = nat_calc_bihash_buckets (c.sessions); + + // ED only feature + vec_add1 (sm->max_translations_per_fib, sm->max_translations_per_thread); + + sm->inside_vrf_id = c.inside_vrf; + sm->inside_fib_index = + fib_table_find_or_create_and_lock + (FIB_PROTOCOL_IP4, c.inside_vrf, sm->fib_src_hi); + + sm->outside_vrf_id = c.outside_vrf; + sm->outside_fib_index = fib_table_find_or_create_and_lock ( + FIB_PROTOCOL_IP4, c.outside_vrf, sm->fib_src_hi); + + sm->worker_in2out_cb = nat44_ed_get_worker_in2out_cb; + sm->worker_out2in_cb = nat44_ed_get_worker_out2in_cb; + + nat44_ed_db_init (sm->max_translations_per_thread, sm->translation_buckets); + + nat_affinity_enable (); + + nat_reset_timeouts (&sm->timeouts); + + vlib_zero_simple_counter (&sm->total_sessions, 0); + + if (!sm->frame_queue_nelts) + sm->frame_queue_nelts = NAT_FQ_NELTS_DEFAULT; + + sm->enabled = 1; + sm->rconfig = c; + + return 0; +} + +void +nat44_addresses_free (snat_address_t ** addresses) +{ + snat_address_t *ap; + vec_foreach (ap, *addresses) + { + #define _(N, i, n, s) \ + vec_free (ap->busy_##n##_ports_per_thread); + foreach_nat_protocol + #undef _ + } + vec_free (*addresses); + *addresses = 0; +} + +int +nat44_plugin_disable () +{ + snat_main_t *sm = &snat_main; + snat_interface_t *i, *vec; + int error = 0; + + fail_if_disabled (); + + // first unregister all nodes from interfaces + vec = vec_dup (sm->interfaces); + vec_foreach (i, vec) + { + if (nat_interface_is_inside(i)) + error = snat_interface_add_del (i->sw_if_index, 1, 1); + if (nat_interface_is_outside(i)) + error = snat_interface_add_del (i->sw_if_index, 0, 1); + + if (error) + { + nat_log_err ("error occurred while removing interface %u", + i->sw_if_index); + } + } + vec_free (vec); + sm->interfaces = 0; + + vec = vec_dup (sm->output_feature_interfaces); + vec_foreach (i, vec) + { + if (nat_interface_is_inside(i)) + error = snat_interface_add_del_output_feature (i->sw_if_index, 1, 1); + if (nat_interface_is_outside(i)) + error = snat_interface_add_del_output_feature (i->sw_if_index, 0, 1); + + if (error) + { + nat_log_err ("error occurred while removing interface %u", + i->sw_if_index); + } + } + vec_free (vec); + sm->output_feature_interfaces = 0; + + vec_free (sm->max_translations_per_fib); + + nat44_ed_db_free (); + + nat44_addresses_free (&sm->addresses); + nat44_addresses_free (&sm->twice_nat_addresses); + + vec_free (sm->to_resolve); + vec_free (sm->auto_add_sw_if_indices); + vec_free (sm->auto_add_sw_if_indices_twice_nat); + + sm->to_resolve = 0; + sm->auto_add_sw_if_indices = 0; + sm->auto_add_sw_if_indices_twice_nat = 0; + + sm->forwarding_enabled = 0; + + sm->enabled = 0; + clib_memset (&sm->rconfig, 0, sizeof (sm->rconfig)); + + return 0; +} + +void +nat44_ed_forwarding_enable_disable (u8 is_enable) +{ + snat_main_per_thread_data_t *tsm; + snat_main_t *sm = &snat_main; + snat_session_t *s; + + u32 *ses_to_be_removed = 0, *ses_index; + + sm->forwarding_enabled = is_enable != 0; + + if (is_enable) + return; + + vec_foreach (tsm, sm->per_thread_data) + { + pool_foreach (s, tsm->sessions) + { + if (is_fwd_bypass_session (s)) + { + vec_add1 (ses_to_be_removed, s - tsm->sessions); + } + } + vec_foreach (ses_index, ses_to_be_removed) + { + s = pool_elt_at_index (tsm->sessions, ses_index[0]); + nat_free_session_data (sm, s, tsm - sm->per_thread_data, 0); + nat_ed_session_delete (sm, s, tsm - sm->per_thread_data, 1); + } + + vec_free (ses_to_be_removed); + } +} + +void +snat_free_outside_address_and_port (snat_address_t *addresses, + u32 thread_index, ip4_address_t *addr, + u16 port, nat_protocol_t protocol) +{ + snat_main_t *sm = &snat_main; + snat_address_t *a; + u32 address_index; + u16 port_host_byte_order = clib_net_to_host_u16 (port); + + for (address_index = 0; address_index < vec_len (addresses); + address_index++) + { + if (addresses[address_index].addr.as_u32 == addr->as_u32) + break; + } + + ASSERT (address_index < vec_len (addresses)); + + a = addresses + address_index; + + switch (protocol) + { +#define _(N, i, n, s) \ + case NAT_PROTOCOL_##N: \ + ASSERT (a->busy_##n##_port_refcounts[port_host_byte_order] >= 1); \ + --a->busy_##n##_port_refcounts[port_host_byte_order]; \ + a->busy_##n##_ports--; \ + a->busy_##n##_ports_per_thread[thread_index]--; \ + break; + foreach_nat_protocol +#undef _ + default : nat_elog_info (sm, "unknown protocol"); + return; + } +} + +int +nat_set_outside_address_and_port (snat_address_t *addresses, u32 thread_index, + ip4_address_t addr, u16 port, + nat_protocol_t protocol) +{ + snat_main_t *sm = &snat_main; + snat_address_t *a = 0; + u32 address_index; + u16 port_host_byte_order = clib_net_to_host_u16 (port); + + for (address_index = 0; address_index < vec_len (addresses); + address_index++) + { + if (addresses[address_index].addr.as_u32 != addr.as_u32) + continue; + + a = addresses + address_index; + switch (protocol) + { +#define _(N, j, n, s) \ + case NAT_PROTOCOL_##N: \ + if (a->busy_##n##_port_refcounts[port_host_byte_order]) \ + return VNET_API_ERROR_INSTANCE_IN_USE; \ + ++a->busy_##n##_port_refcounts[port_host_byte_order]; \ + a->busy_##n##_ports_per_thread[thread_index]++; \ + a->busy_##n##_ports++; \ + return 0; + foreach_nat_protocol +#undef _ + default : nat_elog_info (sm, "unknown protocol"); + return 1; + } + } + + return VNET_API_ERROR_NO_SUCH_ENTRY; +} + +int +snat_static_mapping_match (snat_main_t * sm, + ip4_address_t match_addr, + u16 match_port, + u32 match_fib_index, + nat_protocol_t match_protocol, + ip4_address_t * mapping_addr, + u16 * mapping_port, + u32 * mapping_fib_index, + u8 by_external, + u8 * is_addr_only, + twice_nat_type_t * twice_nat, + lb_nat_type_t * lb, ip4_address_t * ext_host_addr, + u8 * is_identity_nat, snat_static_mapping_t ** out) +{ + clib_bihash_kv_8_8_t kv, value; + clib_bihash_8_8_t *mapping_hash; + snat_static_mapping_t *m; + u32 rand, lo = 0, hi, mid, *tmp = 0, i; + nat44_lb_addr_port_t *local; + u8 backend_index; + + if (!by_external) + { + mapping_hash = &sm->static_mapping_by_local; + init_nat_k (&kv, match_addr, match_port, match_fib_index, + match_protocol); + if (clib_bihash_search_8_8 (mapping_hash, &kv, &value)) + { + /* Try address only mapping */ + init_nat_k (&kv, match_addr, 0, match_fib_index, 0); + if (clib_bihash_search_8_8 (mapping_hash, &kv, &value)) + return 1; + } + } + else + { + mapping_hash = &sm->static_mapping_by_external; + init_nat_k (&kv, match_addr, match_port, 0, match_protocol); + if (clib_bihash_search_8_8 (mapping_hash, &kv, &value)) + { + /* Try address only mapping */ + init_nat_k (&kv, match_addr, 0, 0, 0); + if (clib_bihash_search_8_8 (mapping_hash, &kv, &value)) + return 1; + } + } + + m = pool_elt_at_index (sm->static_mappings, value.value); + + if (by_external) + { + if (is_lb_static_mapping (m)) + { + if (PREDICT_FALSE (lb != 0)) + *lb = m->affinity ? AFFINITY_LB_NAT : LB_NAT; + if (m->affinity && !nat_affinity_find_and_lock (ext_host_addr[0], + match_addr, + match_protocol, + match_port, + &backend_index)) + { + local = pool_elt_at_index (m->locals, backend_index); + *mapping_addr = local->addr; + *mapping_port = local->port; + *mapping_fib_index = local->fib_index; + goto end; + } + // pick locals matching this worker + if (PREDICT_FALSE (sm->num_workers > 1)) + { + u32 thread_index = vlib_get_thread_index (); + pool_foreach_index (i, m->locals) + { + local = pool_elt_at_index (m->locals, i); + + ip4_header_t ip = { + .src_address = local->addr, + }; + + if (sm->worker_in2out_cb (&ip, m->fib_index, 0) == + thread_index) + { + vec_add1 (tmp, i); + } + } + ASSERT (vec_len (tmp) != 0); + } + else + { + pool_foreach_index (i, m->locals) + { + vec_add1 (tmp, i); + } + } + 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; + local = pool_elt_at_index (m->locals, tmp[mid]); + (rand > local->prefix) ? (lo = mid + 1) : (hi = mid); + } + local = pool_elt_at_index (m->locals, tmp[lo]); + if (!(local->prefix >= rand)) + return 1; + *mapping_addr = local->addr; + *mapping_port = 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, + tmp[lo], m->affinity, + m->affinity_per_service_list_head_index)) + nat_elog_info (sm, "create affinity record failed"); + } + vec_free (tmp); + } + else + { + if (PREDICT_FALSE (lb != 0)) + *lb = NO_LB_NAT; + *mapping_fib_index = m->fib_index; + *mapping_addr = m->local_addr; + /* Address only mapping doesn't change port */ + *mapping_port = is_addr_only_static_mapping (m) ? match_port + : m->local_port; + } + } + else + { + *mapping_addr = m->external_addr; + /* Address only mapping doesn't change port */ + *mapping_port = is_addr_only_static_mapping (m) ? match_port + : m->external_port; + *mapping_fib_index = sm->outside_fib_index; + } + +end: + if (PREDICT_FALSE (is_addr_only != 0)) + *is_addr_only = is_addr_only_static_mapping (m); + + if (PREDICT_FALSE (twice_nat != 0)) + *twice_nat = m->twice_nat; + + if (PREDICT_FALSE (is_identity_nat != 0)) + *is_identity_nat = is_identity_static_mapping (m); + + if (out != 0) + *out = m; + + return 0; +} + +static u32 +nat44_ed_get_worker_in2out_cb (ip4_header_t *ip, u32 rx_fib_index, + u8 is_output) +{ + snat_main_t *sm = &snat_main; + u32 next_worker_index = sm->first_worker_index; + u32 hash; + + clib_bihash_kv_16_8_t kv16, value16; + snat_main_per_thread_data_t *tsm; + udp_header_t *udp; + + if (PREDICT_FALSE (is_output)) + { + u32 fib_index = sm->outside_fib_index; + nat_outside_fib_t *outside_fib; + fib_node_index_t fei = FIB_NODE_INDEX_INVALID; + fib_prefix_t pfx = { + .fp_proto = FIB_PROTOCOL_IP4, + .fp_len = 32, + .fp_addr = { + .ip4.as_u32 = ip->dst_address.as_u32, + } + , + }; + + udp = ip4_next_header (ip); + + switch (vec_len (sm->outside_fibs)) + { + case 0: + fib_index = sm->outside_fib_index; + break; + case 1: + fib_index = sm->outside_fibs[0].fib_index; + break; + default: + /* *INDENT-OFF* */ + vec_foreach (outside_fib, sm->outside_fibs) + { + fei = fib_table_lookup (outside_fib->fib_index, &pfx); + if (FIB_NODE_INDEX_INVALID != fei) + { + if (fib_entry_get_resolving_interface (fei) != ~0) + { + fib_index = outside_fib->fib_index; + break; + } + } + } + /* *INDENT-ON* */ + break; + } + + init_ed_k (&kv16, ip->src_address, udp->src_port, ip->dst_address, + udp->dst_port, fib_index, ip->protocol); + + if (PREDICT_TRUE ( + !clib_bihash_search_16_8 (&sm->flow_hash, &kv16, &value16))) + { + tsm = + vec_elt_at_index (sm->per_thread_data, + ed_value_get_thread_index (&value16)); + next_worker_index += tsm->thread_index; + + nat_elog_debug_handoff ( + sm, "HANDOFF IN2OUT-OUTPUT-FEATURE (session)", next_worker_index, + fib_index, clib_net_to_host_u32 (ip->src_address.as_u32), + clib_net_to_host_u32 (ip->dst_address.as_u32)); + + return next_worker_index; + } + } + + hash = ip->src_address.as_u32 + (ip->src_address.as_u32 >> 8) + + (ip->src_address.as_u32 >> 16) + (ip->src_address.as_u32 >> 24); + + if (PREDICT_TRUE (is_pow2 (_vec_len (sm->workers)))) + next_worker_index += sm->workers[hash & (_vec_len (sm->workers) - 1)]; + else + next_worker_index += sm->workers[hash % _vec_len (sm->workers)]; + + if (PREDICT_TRUE (!is_output)) + { + nat_elog_debug_handoff (sm, "HANDOFF IN2OUT", next_worker_index, + rx_fib_index, + clib_net_to_host_u32 (ip->src_address.as_u32), + clib_net_to_host_u32 (ip->dst_address.as_u32)); + } + else + { + nat_elog_debug_handoff (sm, "HANDOFF IN2OUT-OUTPUT-FEATURE", + next_worker_index, rx_fib_index, + clib_net_to_host_u32 (ip->src_address.as_u32), + clib_net_to_host_u32 (ip->dst_address.as_u32)); + } + + return next_worker_index; +} + +static u32 +nat44_ed_get_worker_out2in_cb (vlib_buffer_t * b, ip4_header_t * ip, + u32 rx_fib_index, u8 is_output) +{ + snat_main_t *sm = &snat_main; + clib_bihash_kv_8_8_t kv, value; + clib_bihash_kv_16_8_t kv16, value16; + snat_main_per_thread_data_t *tsm; + + u32 proto, next_worker_index = 0; + udp_header_t *udp; + u16 port; + snat_static_mapping_t *m; + u32 hash; + + proto = ip_proto_to_nat_proto (ip->protocol); + + if (PREDICT_TRUE (proto == NAT_PROTOCOL_UDP || proto == NAT_PROTOCOL_TCP)) + { + udp = ip4_next_header (ip); + + init_ed_k (&kv16, ip->dst_address, udp->dst_port, ip->src_address, + udp->src_port, rx_fib_index, ip->protocol); + + if (PREDICT_TRUE ( + !clib_bihash_search_16_8 (&sm->flow_hash, &kv16, &value16))) + { + tsm = + vec_elt_at_index (sm->per_thread_data, + ed_value_get_thread_index (&value16)); + vnet_buffer2 (b)->nat.cached_session_index = + ed_value_get_session_index (&value16); + next_worker_index = sm->first_worker_index + tsm->thread_index; + nat_elog_debug_handoff ( + sm, "HANDOFF OUT2IN (session)", next_worker_index, rx_fib_index, + clib_net_to_host_u32 (ip->src_address.as_u32), + clib_net_to_host_u32 (ip->dst_address.as_u32)); + return next_worker_index; + } + } + else if (proto == NAT_PROTOCOL_ICMP) + { + ip4_address_t lookup_saddr, lookup_daddr; + u16 lookup_sport, lookup_dport; + u8 lookup_protocol; + if (!nat_get_icmp_session_lookup_values ( + b, ip, &lookup_saddr, &lookup_sport, &lookup_daddr, &lookup_dport, + &lookup_protocol)) + { + init_ed_k (&kv16, lookup_saddr, lookup_sport, lookup_daddr, + lookup_dport, rx_fib_index, lookup_protocol); + if (PREDICT_TRUE ( + !clib_bihash_search_16_8 (&sm->flow_hash, &kv16, &value16))) + { + tsm = + vec_elt_at_index (sm->per_thread_data, + ed_value_get_thread_index (&value16)); + next_worker_index = sm->first_worker_index + tsm->thread_index; + nat_elog_debug_handoff ( + sm, "HANDOFF OUT2IN (session)", next_worker_index, + rx_fib_index, clib_net_to_host_u32 (ip->src_address.as_u32), + clib_net_to_host_u32 (ip->dst_address.as_u32)); + return next_worker_index; + } + } + } + + /* first try static mappings without port */ + if (PREDICT_FALSE (pool_elts (sm->static_mappings))) + { + init_nat_k (&kv, ip->dst_address, 0, 0, 0); + if (!clib_bihash_search_8_8 + (&sm->static_mapping_by_external, &kv, &value)) + { + m = pool_elt_at_index (sm->static_mappings, value.value); + next_worker_index = m->workers[0]; + goto done; + } + } + + /* unknown protocol */ + if (PREDICT_FALSE (proto == NAT_PROTOCOL_OTHER)) + { + /* use current thread */ + next_worker_index = vlib_get_thread_index (); + goto done; + } + + udp = ip4_next_header (ip); + port = udp->dst_port; + + if (PREDICT_FALSE (ip->protocol == IP_PROTOCOL_ICMP)) + { + icmp46_header_t *icmp = (icmp46_header_t *) udp; + icmp_echo_header_t *echo = (icmp_echo_header_t *) (icmp + 1); + if (!icmp_type_is_error_message + (vnet_buffer (b)->ip.reass.icmp_type_or_tcp_flags)) + port = vnet_buffer (b)->ip.reass.l4_src_port; + else + { + /* if error message, then it's not fragmented and we can access it */ + ip4_header_t *inner_ip = (ip4_header_t *) (echo + 1); + proto = ip_proto_to_nat_proto (inner_ip->protocol); + void *l4_header = ip4_next_header (inner_ip); + switch (proto) + { + case NAT_PROTOCOL_ICMP: + icmp = (icmp46_header_t *) l4_header; + echo = (icmp_echo_header_t *) (icmp + 1); + port = echo->identifier; + break; + case NAT_PROTOCOL_UDP: + case NAT_PROTOCOL_TCP: + port = ((tcp_udp_header_t *) l4_header)->src_port; + break; + default: + next_worker_index = vlib_get_thread_index (); + goto done; + } + } + } + + /* try static mappings with port */ + if (PREDICT_FALSE (pool_elts (sm->static_mappings))) + { + init_nat_k (&kv, ip->dst_address, port, 0, proto); + if (!clib_bihash_search_8_8 + (&sm->static_mapping_by_external, &kv, &value)) + { + m = pool_elt_at_index (sm->static_mappings, value.value); + if (!is_lb_static_mapping (m)) + { + next_worker_index = m->workers[0]; + goto done; + } + + hash = ip->src_address.as_u32 + (ip->src_address.as_u32 >> 8) + + (ip->src_address.as_u32 >> 16) + (ip->src_address.as_u32 >> 24); + + if (PREDICT_TRUE (is_pow2 (_vec_len (m->workers)))) + next_worker_index = + m->workers[hash & (_vec_len (m->workers) - 1)]; + else + next_worker_index = m->workers[hash % _vec_len (m->workers)]; + goto done; + } + } + + /* worker by outside port */ + next_worker_index = sm->first_worker_index; + next_worker_index += + sm->workers[(clib_net_to_host_u16 (port) - 1024) / sm->port_per_thread]; + +done: + nat_elog_debug_handoff (sm, "HANDOFF OUT2IN", next_worker_index, + rx_fib_index, + clib_net_to_host_u32 (ip->src_address.as_u32), + clib_net_to_host_u32 (ip->dst_address.as_u32)); + return next_worker_index; +} + +u32 +nat44_get_max_session_limit () +{ + snat_main_t *sm = &snat_main; + u32 max_limit = 0, len = 0; + + for (; len < vec_len (sm->max_translations_per_fib); len++) + { + if (max_limit < sm->max_translations_per_fib[len]) + max_limit = sm->max_translations_per_fib[len]; + } + return max_limit; +} + +int +nat44_set_session_limit (u32 session_limit, u32 vrf_id) +{ + snat_main_t *sm = &snat_main; + u32 fib_index = fib_table_find (FIB_PROTOCOL_IP4, vrf_id); + u32 len = vec_len (sm->max_translations_per_fib); + + if (len <= fib_index) + { + vec_validate (sm->max_translations_per_fib, fib_index + 1); + + for (; len < vec_len (sm->max_translations_per_fib); len++) + sm->max_translations_per_fib[len] = sm->max_translations_per_thread; + } + + sm->max_translations_per_fib[fib_index] = session_limit; + return 0; +} + +int +nat44_update_session_limit (u32 session_limit, u32 vrf_id) +{ + snat_main_t *sm = &snat_main; + + if (nat44_set_session_limit (session_limit, vrf_id)) + return 1; + sm->max_translations_per_thread = nat44_get_max_session_limit (); + + sm->translation_buckets = + nat_calc_bihash_buckets (sm->max_translations_per_thread); + + nat44_ed_sessions_clear (); + return 0; +} + +static void +nat44_ed_worker_db_init (snat_main_per_thread_data_t *tsm, u32 translations, + u32 translation_buckets) +{ + dlist_elt_t *head; + + pool_alloc (tsm->sessions, translations); + pool_alloc (tsm->lru_pool, translations); + + pool_get (tsm->lru_pool, head); + tsm->tcp_trans_lru_head_index = head - tsm->lru_pool; + clib_dlist_init (tsm->lru_pool, tsm->tcp_trans_lru_head_index); + + pool_get (tsm->lru_pool, head); + tsm->tcp_estab_lru_head_index = head - tsm->lru_pool; + clib_dlist_init (tsm->lru_pool, tsm->tcp_estab_lru_head_index); + + pool_get (tsm->lru_pool, head); + tsm->udp_lru_head_index = head - tsm->lru_pool; + clib_dlist_init (tsm->lru_pool, tsm->udp_lru_head_index); + + pool_get (tsm->lru_pool, head); + tsm->icmp_lru_head_index = head - tsm->lru_pool; + clib_dlist_init (tsm->lru_pool, tsm->icmp_lru_head_index); + + pool_get (tsm->lru_pool, head); + tsm->unk_proto_lru_head_index = head - tsm->lru_pool; + clib_dlist_init (tsm->lru_pool, tsm->unk_proto_lru_head_index); +} + +static void +reinit_ed_flow_hash () +{ + snat_main_t *sm = &snat_main; + // we expect 2 flows per session, so multiply translation_buckets by 2 + clib_bihash_init_16_8 ( + &sm->flow_hash, "ed-flow-hash", + clib_max (1, sm->num_workers) * 2 * sm->translation_buckets, 0); + clib_bihash_set_kvp_format_fn_16_8 (&sm->flow_hash, format_ed_session_kvp); +} + +static void +nat44_ed_db_init (u32 translations, u32 translation_buckets) +{ + snat_main_t *sm = &snat_main; + snat_main_per_thread_data_t *tsm; + u32 static_mapping_buckets = 1024; + u32 static_mapping_memory_size = 64 << 20; + + reinit_ed_flow_hash (); + + clib_bihash_init_8_8 (&sm->static_mapping_by_local, + "static_mapping_by_local", static_mapping_buckets, + static_mapping_memory_size); + clib_bihash_set_kvp_format_fn_8_8 (&sm->static_mapping_by_local, + format_static_mapping_kvp); + + clib_bihash_init_8_8 (&sm->static_mapping_by_external, + "static_mapping_by_external", static_mapping_buckets, + static_mapping_memory_size); + clib_bihash_set_kvp_format_fn_8_8 (&sm->static_mapping_by_external, + format_static_mapping_kvp); + + if (sm->pat) + { + vec_foreach (tsm, sm->per_thread_data) + { + nat44_ed_worker_db_init (tsm, sm->max_translations_per_thread, + sm->translation_buckets); + } + } +} + +static void +nat44_ed_worker_db_free (snat_main_per_thread_data_t *tsm) +{ + pool_free (tsm->lru_pool); + pool_free (tsm->sessions); + vec_free (tsm->per_vrf_sessions_vec); +} + +static void +nat44_ed_db_free () +{ + snat_main_t *sm = &snat_main; + snat_main_per_thread_data_t *tsm; + + pool_free (sm->static_mappings); + clib_bihash_free_16_8 (&sm->flow_hash); + clib_bihash_free_8_8 (&sm->static_mapping_by_local); + clib_bihash_free_8_8 (&sm->static_mapping_by_external); + + if (sm->pat) + { + vec_foreach (tsm, sm->per_thread_data) + { + nat44_ed_worker_db_free (tsm); + } + } +} + +void +nat44_ed_sessions_clear () +{ + snat_main_t *sm = &snat_main; + snat_main_per_thread_data_t *tsm; + + reinit_ed_flow_hash (); + + if (sm->pat) + { + vec_foreach (tsm, sm->per_thread_data) + { + + nat44_ed_worker_db_free (tsm); + nat44_ed_worker_db_init (tsm, sm->max_translations_per_thread, + sm->translation_buckets); + } + } + vlib_zero_simple_counter (&sm->total_sessions, 0); +} + +static void +nat_ip4_add_del_addr_only_sm_cb (ip4_main_t * im, + uword opaque, + u32 sw_if_index, + ip4_address_t * address, + u32 address_length, + u32 if_address_index, u32 is_delete) +{ + snat_main_t *sm = &snat_main; + snat_static_map_resolve_t *rp; + snat_static_mapping_t *m; + clib_bihash_kv_8_8_t kv, value; + int i, rv; + ip4_address_t l_addr; + + if (!sm->enabled) + return; + + for (i = 0; i < vec_len (sm->to_resolve); i++) + { + rp = sm->to_resolve + i; + if (rp->addr_only == 0) + continue; + if (rp->sw_if_index == sw_if_index) + goto match; + } + + return; + +match: + init_nat_k (&kv, *address, rp->addr_only ? 0 : rp->e_port, + sm->outside_fib_index, rp->addr_only ? 0 : rp->proto); + if (clib_bihash_search_8_8 (&sm->static_mapping_by_external, &kv, &value)) + m = 0; + else + m = pool_elt_at_index (sm->static_mappings, value.value); + + if (!is_delete) + { + /* Don't trip over lease renewal, static config */ + if (m) + return; + } + else + { + if (!m) + return; + } + + /* Indetity mapping? */ + if (rp->l_addr.as_u32 == 0) + l_addr.as_u32 = address[0].as_u32; + else + l_addr.as_u32 = rp->l_addr.as_u32; + /* Add the static mapping */ + rv = snat_add_static_mapping (l_addr, + address[0], + rp->l_port, + rp->e_port, + rp->vrf_id, + rp->addr_only, ~0 /* sw_if_index */ , + rp->proto, !is_delete, rp->twice_nat, + rp->out2in_only, rp->tag, rp->identity_nat, + rp->pool_addr, rp->exact); + if (rv) + nat_elog_notice_X1 (sm, "snat_add_static_mapping returned %d", "i4", rv); +} + +static void +snat_ip4_add_del_interface_address_cb (ip4_main_t * im, + uword opaque, + u32 sw_if_index, + ip4_address_t * address, + u32 address_length, + u32 if_address_index, u32 is_delete) +{ + snat_main_t *sm = &snat_main; + snat_static_map_resolve_t *rp; + ip4_address_t l_addr; + int i, j; + int rv; + u8 twice_nat = 0; + snat_address_t *addresses = sm->addresses; + + if (!sm->enabled) + return; + + for (i = 0; i < vec_len (sm->auto_add_sw_if_indices); i++) + { + if (sw_if_index == sm->auto_add_sw_if_indices[i]) + goto match; + } + + for (i = 0; i < vec_len (sm->auto_add_sw_if_indices_twice_nat); i++) + { + twice_nat = 1; + addresses = sm->twice_nat_addresses; + if (sw_if_index == sm->auto_add_sw_if_indices_twice_nat[i]) + goto match; + } + + return; + +match: + if (!is_delete) + { + /* Don't trip over lease renewal, static config */ + for (j = 0; j < vec_len (addresses); j++) + if (addresses[j].addr.as_u32 == address->as_u32) + return; + + (void) snat_add_address (sm, address, ~0, twice_nat); + /* Scan static map resolution vector */ + for (j = 0; j < vec_len (sm->to_resolve); j++) + { + rp = sm->to_resolve + j; + if (rp->addr_only) + continue; + /* On this interface? */ + if (rp->sw_if_index == sw_if_index) + { + /* Indetity mapping? */ + if (rp->l_addr.as_u32 == 0) + l_addr.as_u32 = address[0].as_u32; + else + l_addr.as_u32 = rp->l_addr.as_u32; + /* Add the static mapping */ + rv = snat_add_static_mapping ( + l_addr, address[0], rp->l_port, rp->e_port, rp->vrf_id, + rp->addr_only, ~0 /* sw_if_index */, rp->proto, 1, + rp->twice_nat, rp->out2in_only, rp->tag, rp->identity_nat, + rp->pool_addr, rp->exact); + if (rv) + nat_elog_notice_X1 (sm, "snat_add_static_mapping returned %d", + "i4", rv); + } + } + return; + } + else + { + (void) snat_del_address (sm, address[0], 1, twice_nat); + return; + } +} + +int +snat_add_interface_address (snat_main_t * sm, u32 sw_if_index, int is_del, + u8 twice_nat) +{ + ip4_main_t *ip4_main = sm->ip4_main; + ip4_address_t *first_int_addr; + snat_static_map_resolve_t *rp; + u32 *indices_to_delete = 0; + int i, j; + u32 *auto_add_sw_if_indices = + twice_nat ? sm-> + auto_add_sw_if_indices_twice_nat : sm->auto_add_sw_if_indices; + + first_int_addr = ip4_interface_first_address (ip4_main, sw_if_index, 0 /* just want the address */ + ); + + for (i = 0; i < vec_len (auto_add_sw_if_indices); i++) + { + if (auto_add_sw_if_indices[i] == sw_if_index) + { + if (is_del) + { + /* if have address remove it */ + if (first_int_addr) + (void) snat_del_address (sm, first_int_addr[0], 1, twice_nat); + else + { + for (j = 0; j < vec_len (sm->to_resolve); j++) + { + rp = sm->to_resolve + j; + if (rp->sw_if_index == sw_if_index) + vec_add1 (indices_to_delete, j); + } + if (vec_len (indices_to_delete)) + { + for (j = vec_len (indices_to_delete) - 1; j >= 0; j--) + vec_del1 (sm->to_resolve, j); + vec_free (indices_to_delete); + } + } + if (twice_nat) + vec_del1 (sm->auto_add_sw_if_indices_twice_nat, i); + else + vec_del1 (sm->auto_add_sw_if_indices, i); + } + else + return VNET_API_ERROR_VALUE_EXIST; + + return 0; + } + } + + if (is_del) + return VNET_API_ERROR_NO_SUCH_ENTRY; + + /* add to the auto-address list */ + if (twice_nat) + vec_add1 (sm->auto_add_sw_if_indices_twice_nat, sw_if_index); + else + vec_add1 (sm->auto_add_sw_if_indices, sw_if_index); + + /* If the address is already bound - or static - add it now */ + if (first_int_addr) + (void) snat_add_address (sm, first_int_addr, ~0, twice_nat); + + return 0; +} + +int +nat44_del_ed_session (snat_main_t * sm, ip4_address_t * addr, u16 port, + ip4_address_t * eh_addr, u16 eh_port, u8 proto, + u32 vrf_id, int is_in) +{ + ip4_header_t ip; + clib_bihash_kv_16_8_t kv, value; + u32 fib_index = fib_table_find (FIB_PROTOCOL_IP4, vrf_id); + snat_session_t *s; + snat_main_per_thread_data_t *tsm; + + ip.dst_address.as_u32 = ip.src_address.as_u32 = addr->as_u32; + if (sm->num_workers > 1) + tsm = + vec_elt_at_index (sm->per_thread_data, + sm->worker_in2out_cb (&ip, fib_index, 0)); + else + tsm = vec_elt_at_index (sm->per_thread_data, sm->num_workers); + + init_ed_k (&kv, *addr, port, *eh_addr, eh_port, fib_index, proto); + if (clib_bihash_search_16_8 (&sm->flow_hash, &kv, &value)) + { + return VNET_API_ERROR_NO_SUCH_ENTRY; + } + + if (pool_is_free_index (tsm->sessions, ed_value_get_session_index (&value))) + return VNET_API_ERROR_UNSPECIFIED; + s = pool_elt_at_index (tsm->sessions, ed_value_get_session_index (&value)); + nat_free_session_data (sm, s, tsm - sm->per_thread_data, 0); + nat_ed_session_delete (sm, s, tsm - sm->per_thread_data, 1); + return 0; +} + +VLIB_NODE_FN (nat_default_node) (vlib_main_t * vm, + vlib_node_runtime_t * node, + vlib_frame_t * frame) +{ + return 0; +} + +/* *INDENT-OFF* */ +VLIB_REGISTER_NODE (nat_default_node) = { + .name = "nat-default", + .vector_size = sizeof (u32), + .format_trace = 0, + .type = VLIB_NODE_TYPE_INTERNAL, + .n_errors = 0, + .n_next_nodes = NAT_N_NEXT, + .next_nodes = { + [NAT_NEXT_DROP] = "error-drop", + [NAT_NEXT_ICMP_ERROR] = "ip4-icmp-error", + [NAT_NEXT_IN2OUT_ED_FAST_PATH] = "nat44-ed-in2out", + [NAT_NEXT_IN2OUT_ED_SLOW_PATH] = "nat44-ed-in2out-slowpath", + [NAT_NEXT_IN2OUT_ED_OUTPUT_FAST_PATH] = "nat44-ed-in2out-output", + [NAT_NEXT_IN2OUT_ED_OUTPUT_SLOW_PATH] = "nat44-ed-in2out-output-slowpath", + [NAT_NEXT_OUT2IN_ED_FAST_PATH] = "nat44-ed-out2in", + [NAT_NEXT_OUT2IN_ED_SLOW_PATH] = "nat44-ed-out2in-slowpath", + [NAT_NEXT_IN2OUT_CLASSIFY] = "nat44-in2out-worker-handoff", + [NAT_NEXT_OUT2IN_CLASSIFY] = "nat44-out2in-worker-handoff", + }, +}; +/* *INDENT-ON* */ + +void +nat_6t_l3_l4_csum_calc (nat_6t_flow_t *f) +{ + f->l3_csum_delta = 0; + f->l4_csum_delta = 0; + if (f->ops & NAT_FLOW_OP_SADDR_REWRITE && + f->rewrite.saddr.as_u32 != f->match.saddr.as_u32) + { + f->l3_csum_delta = + ip_csum_add_even (f->l3_csum_delta, f->rewrite.saddr.as_u32); + f->l3_csum_delta = + ip_csum_sub_even (f->l3_csum_delta, f->match.saddr.as_u32); + } + else + { + f->rewrite.saddr.as_u32 = f->match.saddr.as_u32; + } + if (f->ops & NAT_FLOW_OP_DADDR_REWRITE && + f->rewrite.daddr.as_u32 != f->match.daddr.as_u32) + { + f->l3_csum_delta = + ip_csum_add_even (f->l3_csum_delta, f->rewrite.daddr.as_u32); + f->l3_csum_delta = + ip_csum_sub_even (f->l3_csum_delta, f->match.daddr.as_u32); + } + else + { + f->rewrite.daddr.as_u32 = f->match.daddr.as_u32; + } + if (f->ops & NAT_FLOW_OP_SPORT_REWRITE && f->rewrite.sport != f->match.sport) + { + f->l4_csum_delta = ip_csum_add_even (f->l4_csum_delta, f->rewrite.sport); + f->l4_csum_delta = ip_csum_sub_even (f->l4_csum_delta, f->match.sport); + } + else + { + f->rewrite.sport = f->match.sport; + } + if (f->ops & NAT_FLOW_OP_DPORT_REWRITE && f->rewrite.dport != f->match.dport) + { + f->l4_csum_delta = ip_csum_add_even (f->l4_csum_delta, f->rewrite.dport); + f->l4_csum_delta = ip_csum_sub_even (f->l4_csum_delta, f->match.dport); + } + else + { + f->rewrite.dport = f->match.dport; + } + if (f->ops & NAT_FLOW_OP_ICMP_ID_REWRITE && + f->rewrite.icmp_id != f->match.icmp_id) + { + f->l4_csum_delta = + ip_csum_add_even (f->l4_csum_delta, f->rewrite.icmp_id); + f->l4_csum_delta = ip_csum_sub_even (f->l4_csum_delta, f->match.icmp_id); + } + else + { + f->rewrite.icmp_id = f->match.icmp_id; + } + if (f->ops & NAT_FLOW_OP_TXFIB_REWRITE) + { + } + else + { + f->rewrite.fib_index = f->match.fib_index; + } +} + +static_always_inline int nat_6t_flow_icmp_translate (snat_main_t *sm, + vlib_buffer_t *b, + ip4_header_t *ip, + nat_6t_flow_t *f); + +static_always_inline void +nat_6t_flow_ip4_translate (snat_main_t *sm, vlib_buffer_t *b, ip4_header_t *ip, + nat_6t_flow_t *f, nat_protocol_t proto, + int is_icmp_inner_ip4) +{ + udp_header_t *udp = ip4_next_header (ip); + tcp_header_t *tcp = (tcp_header_t *) udp; + + if ((NAT_PROTOCOL_TCP == proto || NAT_PROTOCOL_UDP == proto) && + !vnet_buffer (b)->ip.reass.is_non_first_fragment) + { + if (!is_icmp_inner_ip4) + { // regular case + ip->src_address = f->rewrite.saddr; + ip->dst_address = f->rewrite.daddr; + udp->src_port = f->rewrite.sport; + udp->dst_port = f->rewrite.dport; + } + else + { // icmp inner ip4 - reversed saddr/daddr + ip->src_address = f->rewrite.daddr; + ip->dst_address = f->rewrite.saddr; + udp->src_port = f->rewrite.dport; + udp->dst_port = f->rewrite.sport; + } + + if (NAT_PROTOCOL_TCP == proto) + { + ip_csum_t tcp_sum = tcp->checksum; + tcp_sum = ip_csum_sub_even (tcp_sum, f->l3_csum_delta); + tcp_sum = ip_csum_sub_even (tcp_sum, f->l4_csum_delta); + mss_clamping (sm->mss_clamping, tcp, &tcp_sum); + tcp->checksum = ip_csum_fold (tcp_sum); + } + else if (proto == NAT_PROTOCOL_UDP && udp->checksum) + { + ip_csum_t udp_sum = udp->checksum; + udp_sum = ip_csum_sub_even (udp_sum, f->l3_csum_delta); + udp_sum = ip_csum_sub_even (udp_sum, f->l4_csum_delta); + udp->checksum = ip_csum_fold (udp_sum); + } + } + else + { + if (!is_icmp_inner_ip4) + { // regular case + ip->src_address = f->rewrite.saddr; + ip->dst_address = f->rewrite.daddr; + } + else + { // icmp inner ip4 - reversed saddr/daddr + ip->src_address = f->rewrite.daddr; + ip->dst_address = f->rewrite.saddr; + } + } + + ip_csum_t ip_sum = ip->checksum; + ip_sum = ip_csum_sub_even (ip_sum, f->l3_csum_delta); + ip->checksum = ip_csum_fold (ip_sum); + ASSERT (ip->checksum == ip4_header_checksum (ip)); +} + +static_always_inline int +nat_6t_flow_icmp_translate (snat_main_t *sm, vlib_buffer_t *b, + ip4_header_t *ip, nat_6t_flow_t *f) +{ + if (IP_PROTOCOL_ICMP != ip->protocol) + return NAT_ED_TRNSL_ERR_TRANSLATION_FAILED; + + icmp46_header_t *icmp = ip4_next_header (ip); + icmp_echo_header_t *echo = (icmp_echo_header_t *) (icmp + 1); + + if ((!vnet_buffer (b)->ip.reass.is_non_first_fragment)) + { + if (icmp->checksum == 0) + icmp->checksum = 0xffff; + + if (!icmp_type_is_error_message (icmp->type)) + { + if ((f->ops & NAT_FLOW_OP_ICMP_ID_REWRITE) && + (f->rewrite.icmp_id != echo->identifier)) + { + ip_csum_t sum = icmp->checksum; + sum = ip_csum_update (sum, echo->identifier, f->rewrite.icmp_id, + icmp_echo_header_t, + identifier /* changed member */); + echo->identifier = f->rewrite.icmp_id; + icmp->checksum = ip_csum_fold (sum); + } + } + else + { + // errors are not fragmented + ip4_header_t *inner_ip = (ip4_header_t *) (echo + 1); + + if (!ip4_header_checksum_is_valid (inner_ip)) + { + return NAT_ED_TRNSL_ERR_TRANSLATION_FAILED; + } + + nat_protocol_t inner_proto = + ip_proto_to_nat_proto (inner_ip->protocol); + + ip_csum_t icmp_sum = icmp->checksum; + + switch (inner_proto) + { + case NAT_PROTOCOL_UDP: + case NAT_PROTOCOL_TCP: + nat_6t_flow_ip4_translate (sm, b, inner_ip, f, inner_proto, + 1 /* is_icmp_inner_ip4 */); + icmp_sum = ip_csum_sub_even (icmp_sum, f->l3_csum_delta); + icmp->checksum = ip_csum_fold (icmp_sum); + break; + case NAT_PROTOCOL_ICMP: + if (f->ops & NAT_FLOW_OP_ICMP_ID_REWRITE) + { + icmp46_header_t *inner_icmp = ip4_next_header (inner_ip); + icmp_echo_header_t *inner_echo = + (icmp_echo_header_t *) (inner_icmp + 1); + if (f->rewrite.icmp_id != inner_echo->identifier) + { + ip_csum_t sum = icmp->checksum; + sum = ip_csum_update ( + sum, inner_echo->identifier, f->rewrite.icmp_id, + icmp_echo_header_t, identifier /* changed member */); + icmp->checksum = ip_csum_fold (sum); + ip_csum_t inner_sum = inner_icmp->checksum; + inner_sum = ip_csum_update ( + sum, inner_echo->identifier, f->rewrite.icmp_id, + icmp_echo_header_t, identifier /* changed member */); + inner_icmp->checksum = ip_csum_fold (inner_sum); + inner_echo->identifier = f->rewrite.icmp_id; + } + } + break; + default: + clib_warning ("unexpected NAT protocol value `%d'", inner_proto); + return NAT_ED_TRNSL_ERR_TRANSLATION_FAILED; + } + } + } + return NAT_ED_TRNSL_ERR_SUCCESS; +} + +nat_translation_error_e +nat_6t_flow_buf_translate (snat_main_t *sm, vlib_buffer_t *b, ip4_header_t *ip, + nat_6t_flow_t *f, nat_protocol_t proto, + int is_output_feature) +{ + if (!is_output_feature && f->ops & NAT_FLOW_OP_TXFIB_REWRITE) + { + vnet_buffer (b)->sw_if_index[VLIB_TX] = f->rewrite.fib_index; + } + + nat_6t_flow_ip4_translate (sm, b, ip, f, proto, 0 /* is_icmp_inner_ip4 */); + + if (NAT_PROTOCOL_ICMP == proto) + { + return nat_6t_flow_icmp_translate (sm, b, ip, f); + } + + return NAT_ED_TRNSL_ERR_SUCCESS; +} + +u8 * +format_nat_6t (u8 *s, va_list *args) +{ + nat_6t_t *t = va_arg (*args, nat_6t_t *); + + s = format (s, "saddr %U sport %u daddr %U dport %u proto %U fib_idx %u", + format_ip4_address, t->saddr.as_u8, + clib_net_to_host_u16 (t->sport), format_ip4_address, + t->daddr.as_u8, clib_net_to_host_u16 (t->dport), + format_ip_protocol, t->proto, t->fib_index); + return s; +} + +u8 * +format_nat_ed_translation_error (u8 *s, va_list *args) +{ + nat_translation_error_e e = va_arg (*args, nat_translation_error_e); + + switch (e) + { + case NAT_ED_TRNSL_ERR_SUCCESS: + s = format (s, "success"); + break; + case NAT_ED_TRNSL_ERR_TRANSLATION_FAILED: + s = format (s, "translation-failed"); + break; + case NAT_ED_TRNSL_ERR_FLOW_MISMATCH: + s = format (s, "flow-mismatch"); + break; + } + return s; +} + +u8 * +format_nat_6t_flow (u8 *s, va_list *args) +{ + nat_6t_flow_t *f = va_arg (*args, nat_6t_flow_t *); + + s = format (s, "match: %U ", format_nat_6t, &f->match); + int r = 0; + if (f->ops & NAT_FLOW_OP_SADDR_REWRITE) + { + s = format (s, "rewrite: saddr %U ", format_ip4_address, + f->rewrite.saddr.as_u8); + r = 1; + } + if (f->ops & NAT_FLOW_OP_SPORT_REWRITE) + { + if (!r) + { + s = format (s, "rewrite: "); + r = 1; + } + s = format (s, "sport %u ", clib_net_to_host_u16 (f->rewrite.sport)); + } + if (f->ops & NAT_FLOW_OP_DADDR_REWRITE) + { + if (!r) + { + s = format (s, "rewrite: "); + r = 1; + } + s = format (s, "daddr %U ", format_ip4_address, f->rewrite.daddr.as_u8); + } + if (f->ops & NAT_FLOW_OP_DPORT_REWRITE) + { + if (!r) + { + s = format (s, "rewrite: "); + r = 1; + } + s = format (s, "dport %u ", clib_net_to_host_u16 (f->rewrite.dport)); + } + if (f->ops & NAT_FLOW_OP_ICMP_ID_REWRITE) + { + if (!r) + { + s = format (s, "rewrite: "); + r = 1; + } + s = format (s, "icmp-id %u ", clib_net_to_host_u16 (f->rewrite.icmp_id)); + } + if (f->ops & NAT_FLOW_OP_TXFIB_REWRITE) + { + if (!r) + { + s = format (s, "rewrite: "); + r = 1; + } + s = format (s, "txfib %u ", f->rewrite.fib_index); + } + return s; +} + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/src/plugins/nat/nat44-ed/nat44_ed.h b/src/plugins/nat/nat44-ed/nat44_ed.h new file mode 100644 index 00000000000..10d1207fec8 --- /dev/null +++ b/src/plugins/nat/nat44-ed/nat44_ed.h @@ -0,0 +1,1183 @@ +/* + * Copyright (c) 2020 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file nat.c + * NAT plugin global declarations + */ +#ifndef __included_nat44_ed_h__ +#define __included_nat44_ed_h__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +/* default number of worker handoff frame queue elements */ +#define NAT_FQ_NELTS_DEFAULT 64 + +/* NAT buffer flags */ +#define SNAT_FLAG_HAIRPINNING (1 << 0) + +/* NAT44 API Configuration flags */ +#define foreach_nat44_config_flag \ + _(0x00, IS_ENDPOINT_INDEPENDENT) \ + _(0x01, IS_ENDPOINT_DEPENDENT) \ + _(0x02, IS_STATIC_MAPPING_ONLY) \ + _(0x04, IS_CONNECTION_TRACKING) \ + _(0x08, IS_OUT2IN_DPO) + +typedef enum nat44_config_flags_t_ +{ +#define _(n,f) NAT44_API_##f = n, + foreach_nat44_config_flag +#undef _ +} nat44_config_flags_t; + +typedef struct +{ + /* nat44 plugin features */ + u8 static_mapping_only; + u8 connection_tracking; + + u32 inside_vrf; + u32 outside_vrf; + + /* maximum number of sessions */ + u32 sessions; + +} nat44_config_t; + +typedef enum +{ + NAT_NEXT_DROP, + NAT_NEXT_ICMP_ERROR, + NAT_NEXT_IN2OUT_ED_FAST_PATH, + NAT_NEXT_IN2OUT_ED_SLOW_PATH, + NAT_NEXT_IN2OUT_ED_OUTPUT_FAST_PATH, + NAT_NEXT_IN2OUT_ED_OUTPUT_SLOW_PATH, + NAT_NEXT_OUT2IN_ED_FAST_PATH, + NAT_NEXT_OUT2IN_ED_SLOW_PATH, + NAT_NEXT_IN2OUT_CLASSIFY, + NAT_NEXT_OUT2IN_CLASSIFY, + NAT_N_NEXT, +} nat_next_t; + +typedef struct +{ + u32 next_index; + u32 arc_next_index; +} nat_pre_trace_t; + +/* External address and port allocation modes */ +#define foreach_nat_addr_and_port_alloc_alg \ + _(0, DEFAULT, "default") \ + _(1, MAPE, "map-e") \ + _(2, RANGE, "port-range") + +typedef enum +{ +#define _(v, N, s) NAT_ADDR_AND_PORT_ALLOC_ALG_##N = v, + foreach_nat_addr_and_port_alloc_alg +#undef _ +} nat_addr_and_port_alloc_alg_t; + +/* Session state */ +#define foreach_snat_session_state \ + _(0, UNKNOWN, "unknown") \ + _(1, UDP_ACTIVE, "udp-active") \ + _(2, TCP_SYN_SENT, "tcp-syn-sent") \ + _(3, TCP_ESTABLISHED, "tcp-established") \ + _(4, TCP_FIN_WAIT, "tcp-fin-wait") \ + _(5, TCP_CLOSE_WAIT, "tcp-close-wait") \ + _(6, TCP_CLOSING, "tcp-closing") \ + _(7, TCP_LAST_ACK, "tcp-last-ack") \ + _(8, TCP_CLOSED, "tcp-closed") \ + _(9, ICMP_ACTIVE, "icmp-active") + +typedef enum +{ +#define _(v, N, s) SNAT_SESSION_##N = v, + foreach_snat_session_state +#undef _ +} snat_session_state_t; + +#define foreach_nat_in2out_ed_error \ +_(UNSUPPORTED_PROTOCOL, "unsupported protocol") \ +_(OUT_OF_PORTS, "out of ports") \ +_(BAD_ICMP_TYPE, "unsupported ICMP type") \ +_(MAX_SESSIONS_EXCEEDED, "maximum sessions exceeded") \ +_(NON_SYN, "non-SYN packet try to create session") \ +_(TCP_CLOSED, "drops due to TCP in transitory timeout") + +typedef enum +{ +#define _(sym,str) NAT_IN2OUT_ED_ERROR_##sym, + foreach_nat_in2out_ed_error +#undef _ + NAT_IN2OUT_ED_N_ERROR, +} nat_in2out_ed_error_t; + +#define foreach_nat_out2in_ed_error \ + _ (UNSUPPORTED_PROTOCOL, "unsupported protocol") \ + _ (OUT_OF_PORTS, "out of ports") \ + _ (BAD_ICMP_TYPE, "unsupported ICMP type") \ + _ (NO_TRANSLATION, "no translation") \ + _ (MAX_SESSIONS_EXCEEDED, "maximum sessions exceeded") \ + _ (NON_SYN, "non-SYN packet try to create session") \ + _ (TCP_CLOSED, "drops due to TCP in transitory timeout") \ + _ (HASH_ADD_FAILED, "hash table add failed") + +typedef enum +{ +#define _(sym,str) NAT_OUT2IN_ED_ERROR_##sym, + foreach_nat_out2in_ed_error +#undef _ + NAT_OUT2IN_ED_N_ERROR, +} nat_out2in_ed_error_t; + + +/* Endpoint dependent TCP session state */ +#define NAT44_SES_I2O_FIN 1 +#define NAT44_SES_O2I_FIN 2 +#define NAT44_SES_I2O_FIN_ACK 4 +#define NAT44_SES_O2I_FIN_ACK 8 +#define NAT44_SES_I2O_SYN 16 +#define NAT44_SES_O2I_SYN 32 +#define NAT44_SES_RST 64 + +/* Session flags */ +#define SNAT_SESSION_FLAG_STATIC_MAPPING (1 << 0) +#define SNAT_SESSION_FLAG_UNKNOWN_PROTO (1 << 1) +#define SNAT_SESSION_FLAG_LOAD_BALANCING (1 << 2) +#define SNAT_SESSION_FLAG_TWICE_NAT (1 << 3) +#define SNAT_SESSION_FLAG_ENDPOINT_DEPENDENT (1 << 4) +#define SNAT_SESSION_FLAG_FWD_BYPASS (1 << 5) +#define SNAT_SESSION_FLAG_AFFINITY (1 << 6) +#define SNAT_SESSION_FLAG_EXACT_ADDRESS (1 << 7) +#define SNAT_SESSION_FLAG_HAIRPINNING (1 << 8) + +/* NAT interface flags */ +#define NAT_INTERFACE_FLAG_IS_INSIDE 1 +#define NAT_INTERFACE_FLAG_IS_OUTSIDE 2 + +/* Static mapping flags */ +#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 +#define NAT_STATIC_MAPPING_FLAG_EXACT_ADDRESS 16 + +/* *INDENT-OFF* */ +typedef CLIB_PACKED(struct +{ + // number of sessions in this vrf + u32 ses_count; + + u32 rx_fib_index; + u32 tx_fib_index; + + // is this vrf expired + u8 expired; +}) per_vrf_sessions_t; +/* *INDENT-ON* */ + +typedef struct +{ + ip4_address_t saddr, daddr; + u32 fib_index; + u16 sport, dport; + u16 icmp_id; + u8 proto; +} nat_6t_t; + +typedef struct +{ +#define NAT_FLOW_OP_SADDR_REWRITE (1 << 1) +#define NAT_FLOW_OP_SPORT_REWRITE (1 << 2) +#define NAT_FLOW_OP_DADDR_REWRITE (1 << 3) +#define NAT_FLOW_OP_DPORT_REWRITE (1 << 4) +#define NAT_FLOW_OP_ICMP_ID_REWRITE (1 << 5) +#define NAT_FLOW_OP_TXFIB_REWRITE (1 << 6) + int ops; + nat_6t_t match; + nat_6t_t rewrite; + uword l3_csum_delta; + uword l4_csum_delta; +} nat_6t_flow_t; + +void nat44_ed_forwarding_enable_disable (u8 is_enable); + +always_inline void +nat_6t_flow_saddr_rewrite_set (nat_6t_flow_t *f, u32 saddr) +{ + f->ops |= NAT_FLOW_OP_SADDR_REWRITE; + f->rewrite.saddr.as_u32 = saddr; +} + +always_inline void +nat_6t_flow_daddr_rewrite_set (nat_6t_flow_t *f, u32 daddr) +{ + f->ops |= NAT_FLOW_OP_DADDR_REWRITE; + f->rewrite.daddr.as_u32 = daddr; +} + +always_inline void +nat_6t_flow_sport_rewrite_set (nat_6t_flow_t *f, u32 sport) +{ + f->ops |= NAT_FLOW_OP_SPORT_REWRITE; + f->rewrite.sport = sport; +} + +always_inline void +nat_6t_flow_dport_rewrite_set (nat_6t_flow_t *f, u32 dport) +{ + f->ops |= NAT_FLOW_OP_DPORT_REWRITE; + f->rewrite.dport = dport; +} + +always_inline void +nat_6t_flow_txfib_rewrite_set (nat_6t_flow_t *f, u32 tx_fib_index) +{ + f->ops |= NAT_FLOW_OP_TXFIB_REWRITE; + f->rewrite.fib_index = tx_fib_index; +} + +always_inline void +nat_6t_flow_icmp_id_rewrite_set (nat_6t_flow_t *f, u16 id) +{ + f->ops |= NAT_FLOW_OP_ICMP_ID_REWRITE; + f->rewrite.icmp_id = id; +} + +/* *INDENT-OFF* */ +typedef CLIB_PACKED(struct +{ + /* Outside network tuple */ + struct + { + ip4_address_t addr; + u32 fib_index; + u16 port; + } out2in; + + /* Inside network tuple */ + struct + { + ip4_address_t addr; + u32 fib_index; + u16 port; + } in2out; + + nat_protocol_t nat_proto; + + nat_6t_flow_t i2o; + nat_6t_flow_t o2i; + + /* Flags */ + u32 flags; + + /* head of LRU list in which this session is tracked */ + u32 lru_head_index; + /* index in global LRU list */ + u32 lru_index; + f64 last_lru_update; + + /* Last heard timer */ + f64 last_heard; + + /* Last HA refresh */ + f64 ha_last_refreshed; + + /* Counters */ + u64 total_bytes; + u32 total_pkts; + + /* External host address and port */ + ip4_address_t ext_host_addr; + u16 ext_host_port; + + /* External host address and port after translation */ + ip4_address_t ext_host_nat_addr; + u16 ext_host_nat_port; + + /* TCP session state */ + u8 state; + u32 i2o_fin_seq; + u32 o2i_fin_seq; + u64 tcp_closed_timestamp; + + /* per vrf sessions index */ + u32 per_vrf_sessions_index; + +}) snat_session_t; +/* *INDENT-ON* */ + +typedef struct +{ + ip4_address_t addr; + u32 fib_index; +/* *INDENT-OFF* */ +#define _(N, i, n, s) \ + u32 busy_##n##_ports; \ + u32 * busy_##n##_ports_per_thread; \ + u32 busy_##n##_port_refcounts[65535]; + foreach_nat_protocol +#undef _ +/* *INDENT-ON* */ +} snat_address_t; + +typedef struct +{ + u32 fib_index; + u32 ref_count; +} nat_fib_t; + +typedef struct +{ + u32 fib_index; + u32 refcount; +} nat_outside_fib_t; + +typedef struct +{ + /* backend IP address */ + ip4_address_t addr; + /* backend port number */ + u16 port; + /* probability of the backend to be randomly matched */ + u8 probability; + u8 prefix; + /* backend FIB table */ + u32 vrf_id; + u32 fib_index; +} nat44_lb_addr_port_t; + +typedef enum +{ + /* twice-nat disabled */ + TWICE_NAT_DISABLED, + /* twice-nat enabled */ + TWICE_NAT, + /* twice-nat only when src IP equals dst IP after translation */ + TWICE_NAT_SELF, +} twice_nat_type_t; + +typedef enum +{ + /* no load-balancing */ + NO_LB_NAT, + /* load-balancing */ + LB_NAT, + /* load-balancing with affinity */ + AFFINITY_LB_NAT, +} lb_nat_type_t; + +typedef struct +{ + /* prefered pool address */ + ip4_address_t pool_addr; + /* local IP address */ + ip4_address_t local_addr; + /* external IP address */ + ip4_address_t external_addr; + /* local port */ + u16 local_port; + /* external port */ + u16 external_port; + /* is twice-nat */ + twice_nat_type_t twice_nat; + /* local FIB table */ + u32 vrf_id; + u32 fib_index; + /* protocol */ + nat_protocol_t proto; + /* 0 = disabled, otherwise client IP affinity sticky time in seconds */ + u32 affinity; + /* worker threads used by backends/local host */ + u32 *workers; + /* opaque string tag */ + u8 *tag; + /* backends for load-balancing mode */ + nat44_lb_addr_port_t *locals; + /* affinity per service lis */ + u32 affinity_per_service_list_head_index; + /* flags */ + u32 flags; +} snat_static_mapping_t; + +typedef struct +{ + u32 sw_if_index; + u8 flags; +} snat_interface_t; + +typedef struct +{ + ip4_address_t l_addr; + ip4_address_t pool_addr; + u16 l_port; + u16 e_port; + u32 sw_if_index; + u32 vrf_id; + nat_protocol_t proto; + u32 flags; + int addr_only; + int twice_nat; + int out2in_only; + int identity_nat; + int exact; + u8 *tag; +} snat_static_map_resolve_t; + +typedef struct +{ + /* Session pool */ + snat_session_t *sessions; + + /* Pool of doubly-linked list elements */ + dlist_elt_t *list_pool; + + /* LRU session list - head is stale, tail is fresh */ + dlist_elt_t *lru_pool; + u32 tcp_trans_lru_head_index; + u32 tcp_estab_lru_head_index; + u32 udp_lru_head_index; + u32 icmp_lru_head_index; + u32 unk_proto_lru_head_index; + + /* NAT thread index */ + u32 snat_thread_index; + + /* real thread index */ + u32 thread_index; + + per_vrf_sessions_t *per_vrf_sessions_vec; + +} snat_main_per_thread_data_t; + +struct snat_main_s; + +/* Return worker thread index for given packet */ +typedef u32 (snat_get_worker_in2out_function_t) (ip4_header_t * ip, + u32 rx_fib_index, + u8 is_output); + +typedef u32 (snat_get_worker_out2in_function_t) (vlib_buffer_t * b, + ip4_header_t * ip, + u32 rx_fib_index, + u8 is_output); + +/* NAT address and port allocation function */ +typedef int (nat_alloc_out_addr_and_port_function_t) (snat_address_t * + addresses, + u32 fib_index, + u32 thread_index, + nat_protocol_t proto, + ip4_address_t * addr, + u16 * port, + u16 port_per_thread, + u32 snat_thread_index); + +typedef struct snat_main_s +{ + /* Thread settings */ + u32 num_workers; + u32 first_worker_index; + u32 *workers; + snat_get_worker_in2out_function_t *worker_in2out_cb; + snat_get_worker_out2in_function_t *worker_out2in_cb; + u16 port_per_thread; + + /* Per thread data */ + snat_main_per_thread_data_t *per_thread_data; + + /* Find a static mapping by local */ + clib_bihash_8_8_t static_mapping_by_local; + + /* Find a static mapping by external */ + clib_bihash_8_8_t static_mapping_by_external; + + /* Static mapping pool */ + snat_static_mapping_t *static_mappings; + + /* Endpoint independent lookup tables */ + clib_bihash_8_8_t in2out; + clib_bihash_8_8_t out2in; + + /* Endpoint dependent lookup table */ + clib_bihash_16_8_t flow_hash; + + /* Interface pool */ + snat_interface_t *interfaces; + snat_interface_t *output_feature_interfaces; + + /* Vector of outside addresses */ + snat_address_t *addresses; + /* Address and port allocation function */ + nat_alloc_out_addr_and_port_function_t *alloc_addr_and_port; + /* Address and port allocation type */ + nat_addr_and_port_alloc_alg_t addr_and_port_alloc_alg; + /* Port set parameters (MAP-E) */ + u8 psid_offset; + u8 psid_length; + u16 psid; + /* Port range parameters */ + u16 start_port; + u16 end_port; + + /* vector of fibs */ + nat_fib_t *fibs; + + /* vector of outside fibs */ + nat_outside_fib_t *outside_fibs; + + /* Vector of twice NAT addresses for external hosts */ + snat_address_t *twice_nat_addresses; + + /* sw_if_indices whose intfc addresses should be auto-added */ + u32 *auto_add_sw_if_indices; + u32 *auto_add_sw_if_indices_twice_nat; + + /* vector of interface address static mappings to resolve. */ + snat_static_map_resolve_t *to_resolve; + + /* Randomize port allocation order */ + u32 random_seed; + + /* Worker handoff frame-queue index */ + u32 fq_in2out_index; + u32 fq_in2out_output_index; + u32 fq_out2in_index; + + u32 out2in_node_index; + u32 in2out_node_index; + u32 in2out_output_node_index; + + nat44_config_t rconfig; + //nat44_config_t cconfig; + + /* If forwarding is enabled */ + u8 forwarding_enabled; + + /* static mapping config */ + u8 static_mapping_only; + u8 static_mapping_connection_tracking; + + /* Is translation memory size calculated or user defined */ + u8 translation_memory_size_set; + + u32 translation_buckets; + u32 max_translations_per_thread; + u32 *max_translations_per_fib; + + u32 outside_vrf_id; + u32 outside_fib_index; + u32 inside_vrf_id; + u32 inside_fib_index; + + nat_timeouts_t timeouts; + + /* TCP MSS clamping */ + u16 mss_clamping; + + /* counters */ + vlib_simple_counter_main_t total_sessions; + +#define _(x) vlib_simple_counter_main_t x; + struct + { + struct + { + struct + { + foreach_nat_counter; + } in2out; + + struct + { + foreach_nat_counter; + } out2in; + } fastpath; + + struct + { + struct + { + foreach_nat_counter; + } in2out; + + struct + { + foreach_nat_counter; + } out2in; + } slowpath; + + vlib_simple_counter_main_t hairpinning; + } counters; +#undef _ + + /* API message ID base */ + u16 msg_id_base; + + /* log class */ + vlib_log_class_t log_class; + /* logging level */ + u8 log_level; + + /* convenience */ + api_main_t *api_main; + ip4_main_t *ip4_main; + ip_lookup_main_t *ip4_lookup_main; + + fib_source_t fib_src_hi; + fib_source_t fib_src_low; + + /* pat - dynamic mapping enabled or conneciton tracking */ + u8 pat; + + /* number of worker handoff frame queue elements */ + u32 frame_queue_nelts; + + /* nat44 plugin enabled */ + u8 enabled; + + vnet_main_t *vnet_main; + +} snat_main_t; + +typedef struct +{ + u32 thread_index; + f64 now; +} nat44_is_idle_session_ctx_t; + +typedef struct +{ + u32 cached_sw_if_index; + u32 cached_ip4_address; +} snat_runtime_t; + +extern snat_main_t snat_main; + +// nat pre ed next_node feature classification +extern vlib_node_registration_t nat_default_node; +extern vlib_node_registration_t nat_pre_in2out_node; +extern vlib_node_registration_t nat_pre_out2in_node; + +extern vlib_node_registration_t snat_in2out_node; +extern vlib_node_registration_t snat_in2out_output_node; +extern vlib_node_registration_t snat_out2in_node; +extern vlib_node_registration_t snat_in2out_worker_handoff_node; +extern vlib_node_registration_t snat_in2out_output_worker_handoff_node; +extern vlib_node_registration_t snat_out2in_worker_handoff_node; +extern vlib_node_registration_t nat44_ed_in2out_node; +extern vlib_node_registration_t nat44_ed_in2out_output_node; +extern vlib_node_registration_t nat44_ed_out2in_node; + +extern fib_source_t nat_fib_src_hi; +extern fib_source_t nat_fib_src_low; + +/* format functions */ +format_function_t format_snat_static_mapping; +format_function_t format_snat_static_map_to_resolve; +format_function_t format_snat_session; +format_function_t format_snat_key; +format_function_t format_static_mapping_key; +format_function_t format_nat_protocol; +format_function_t format_nat_addr_and_port_alloc_alg; +/* unformat functions */ +unformat_function_t unformat_nat_protocol; + +/** \brief Check if SNAT session is created from static mapping. + @param s SNAT session + @return 1 if SNAT session is created from static mapping otherwise 0 +*/ +#define snat_is_session_static(s) (s->flags & SNAT_SESSION_FLAG_STATIC_MAPPING) + +/** \brief Check if SNAT session for unknown protocol. + @param s SNAT session + @return 1 if SNAT session for unknown protocol otherwise 0 +*/ +#define snat_is_unk_proto_session(s) (s->flags & SNAT_SESSION_FLAG_UNKNOWN_PROTO) + +/** \brief Check if NAT session is twice NAT. + @param s NAT session + @return 1 if NAT session is twice NAT +*/ +#define is_twice_nat_session(s) (s->flags & SNAT_SESSION_FLAG_TWICE_NAT) + +/** \brief Check if NAT session is load-balancing. + @param s NAT session + @return 1 if NAT session is load-balancing +*/ +#define is_lb_session(s) (s->flags & SNAT_SESSION_FLAG_LOAD_BALANCING) + +/** \brief Check if NAT session is forwarding bypass. + @param s NAT session + @return 1 if NAT session is load-balancing +*/ +#define is_fwd_bypass_session(s) (s->flags & SNAT_SESSION_FLAG_FWD_BYPASS) + +/** \brief Check if NAT session is endpoint dependent. + @param s NAT session + @return 1 if NAT session is endpoint dependent +*/ +#define is_ed_session(s) (s->flags & SNAT_SESSION_FLAG_ENDPOINT_DEPENDENT) + +/** \brief Check if NAT session has affinity record. + @param s NAT session + @return 1 if NAT session has affinity record +*/ +#define is_affinity_sessions(s) (s->flags & SNAT_SESSION_FLAG_AFFINITY) + +/** \brief Check if exact pool address should be used. + @param s SNAT session + @return 1 if exact pool address or 0 +*/ +#define is_exact_address_session(s) (s->flags & SNAT_SESSION_FLAG_EXACT_ADDRESS) + +/** \brief Check if NAT interface is inside. + @param i NAT interface + @return 1 if inside interface +*/ +#define nat_interface_is_inside(i) i->flags & NAT_INTERFACE_FLAG_IS_INSIDE + +/** \brief Check if NAT interface is outside. + @param i NAT interface + @return 1 if outside interface +*/ +#define nat_interface_is_outside(i) i->flags & NAT_INTERFACE_FLAG_IS_OUTSIDE + +/** \brief Check if NAT44 endpoint-dependent TCP session is closed. + @param s NAT session + @return 1 if session is closed +*/ +#define nat44_is_ses_closed(s) s->state == 0xf + +/** \brief Check if NAT static mapping is address only (1:1NAT). + @param sm NAT static mapping + @return 1 if 1:1NAT, 0 if 1:1NAPT +*/ +#define is_addr_only_static_mapping(sm) (sm->flags & NAT_STATIC_MAPPING_FLAG_ADDR_ONLY) + +/** \brief Check if NAT static mapping match only out2in direction. + @param sm NAT static mapping + @return 1 if rule match only out2in direction +*/ +#define is_out2in_only_static_mapping(sm) (sm->flags & NAT_STATIC_MAPPING_FLAG_OUT2IN_ONLY) + +/** \brief Check if NAT static mapping is identity NAT. + @param sm NAT static mapping + @return 1 if identity NAT +*/ +#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) + +/** \brief Check if exact pool address should be used. + @param s SNAT session + @return 1 if exact pool address or 0 +*/ +#define is_exact_address(s) (s->flags & NAT_STATIC_MAPPING_FLAG_EXACT_ADDRESS) + +/** \brief Check if client initiating TCP connection (received SYN from client) + @param t TCP header + @return 1 if client initiating TCP connection +*/ +always_inline bool +tcp_flags_is_init (u8 f) +{ + return (f & TCP_FLAG_SYN) && !(f & TCP_FLAG_ACK); +} + +/* logging */ +#define nat_log_err(...) \ + vlib_log(VLIB_LOG_LEVEL_ERR, snat_main.log_class, __VA_ARGS__) +#define nat_log_warn(...) \ + vlib_log(VLIB_LOG_LEVEL_WARNING, snat_main.log_class, __VA_ARGS__) +#define nat_log_notice(...) \ + vlib_log(VLIB_LOG_LEVEL_NOTICE, snat_main.log_class, __VA_ARGS__) +#define nat_log_info(...) \ + vlib_log(VLIB_LOG_LEVEL_INFO, snat_main.log_class, __VA_ARGS__) +#define nat_log_debug(...)\ + vlib_log(VLIB_LOG_LEVEL_DEBUG, snat_main.log_class, __VA_ARGS__) + +/** + * @brief Enable NAT44 plugin + * + * @param c nat44_config_t + * + * @return 0 on success, non-zero value otherwise + */ +int nat44_plugin_enable (nat44_config_t c); + +/** + * @brief Disable NAT44 plugin + * + * @return 0 on success, non-zero value otherwise + */ +int nat44_plugin_disable (); + +/** + * @brief Add external address to NAT44 pool + * + * @param sm snat global configuration data + * @param addr IPv4 address + * @param vrf_id VRF id of tenant, ~0 means independent of VRF + * @param twice_nat 1 if twice NAT address + * + * @return 0 on success, non-zero value otherwise + */ +int snat_add_address (snat_main_t * sm, ip4_address_t * addr, u32 vrf_id, + u8 twice_nat); + +/** + * @brief Delete external address from NAT44 pool + * + * @param sm snat global configuration data + * @param addr IPv4 address + * @param delete_sm 1 if delete static mapping using address + * @param twice_nat 1 if twice NAT address + * + * @return 0 on success, non-zero value otherwise + */ +int snat_del_address (snat_main_t * sm, ip4_address_t addr, u8 delete_sm, + u8 twice_nat); + +/** + * @brief Add/delete external address to FIB DPO (out2in DPO mode) + * + * @param addr IPv4 address + * @param is_add 1 = add, 0 = delete + * + * @return 0 on success, non-zero value otherwise + */ +void nat44_add_del_address_dpo (ip4_address_t addr, u8 is_add); + +/** + * @brief Add/delete NAT44 static mapping + * + * @param l_addr local IPv4 address + * @param e_addr external IPv4 address + * @param l_port local port number + * @param e_port external port number + * @param vrf_id local VRF ID + * @param addr_only 1 = 1:1NAT, 0 = 1:1NAPT + * @param sw_if_index use interface address as external IPv4 address + * @param proto L4 protocol + * @param is_add 1 = add, 0 = delete + * @param twice_nat twice-nat mode + * @param out2in_only if 1 rule match only out2in direction + * @param tag opaque string tag + * @param identity_nat identity NAT + * @param pool_addr pool IPv4 address + * @param exact 1 = exact pool address + * + * @return 0 on success, non-zero value otherwise + */ +int snat_add_static_mapping (ip4_address_t l_addr, ip4_address_t e_addr, + u16 l_port, u16 e_port, u32 vrf_id, + int addr_only, u32 sw_if_index, + nat_protocol_t proto, int is_add, + twice_nat_type_t twice_nat, u8 out2in_only, + u8 * tag, u8 identity_nat, + ip4_address_t pool_addr, int exact); + +/** + * @brief Add/delete static mapping with load-balancing (multiple backends) + * + * @param e_addr external IPv4 address + * @param e_port external port number + * @param proto L4 protocol + * @param locals list of local backends + * @param is_add 1 = add, 0 = delete + * @param twice_nat twice-nat mode + * @param out2in_only if 1 rule match only out2in direction + * @param tag opaque string tag + * @param affinity 0 = disabled, otherwise client IP affinity sticky time + * + * @return 0 on success, non-zero value otherwise + */ +int nat44_add_del_lb_static_mapping (ip4_address_t e_addr, u16 e_port, + nat_protocol_t proto, + nat44_lb_addr_port_t * locals, u8 is_add, + 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, + nat_protocol_t proto, u32 vrf_id, + u8 probability, u8 is_add); + +clib_error_t *nat44_api_hookup (vlib_main_t * vm); + +/** + * @brief Set NAT plugin workers + * + * @param bitmap NAT workers bitmap + * + * @return 0 on success, non-zero value otherwise + */ +int snat_set_workers (uword * bitmap); + +/** + * @brief Set NAT plugin number of frame queue elements + * + * @param frame_queue_nelts number of worker handoff frame queue elements + * + * @return 0 on success, non-zero value otherwise + */ +int snat_set_frame_queue_nelts (u32 frame_queue_nelts); + +/** + * @brief Enable/disable NAT44 feature on the interface + * + * @param sw_if_index software index of the interface + * @param is_inside 1 = inside, 0 = outside + * @param is_del 1 = delete, 0 = add + * + * @return 0 on success, non-zero value otherwise + */ +int snat_interface_add_del (u32 sw_if_index, u8 is_inside, int is_del); + +/** + * @brief Enable/disable NAT44 output feature on the interface (postrouting NAT) + * + * @param sw_if_index software index of the interface + * @param is_inside 1 = inside, 0 = outside + * @param is_del 1 = delete, 0 = add + * + * @return 0 on success, non-zero value otherwise + */ +int snat_interface_add_del_output_feature (u32 sw_if_index, u8 is_inside, + int is_del); + +/** + * @brief Add/delete NAT44 pool address from specific interface + * + * @param sw_if_index software index of the interface + * @param is_del 1 = delete, 0 = add + * @param twice_nat 1 = twice NAT address for external hosts + * + * @return 0 on success, non-zero value otherwise + */ +int snat_add_interface_address (snat_main_t * sm, u32 sw_if_index, int is_del, + u8 twice_nat); + +/** + * @brief Delete NAT44 endpoint-dependent session + * + * @param sm snat global configuration data + * @param addr IPv4 address + * @param port L4 port number + * @param proto L4 protocol + * @param vrf_id VRF ID + * @param is_in 1 = inside network address and port pair, 0 = outside + * + * @return 0 on success, non-zero value otherwise + */ +int nat44_del_ed_session (snat_main_t * sm, ip4_address_t * addr, u16 port, + ip4_address_t * eh_addr, u16 eh_port, u8 proto, + u32 vrf_id, int is_in); + +/** + * @brief Free NAT44 session data (lookup keys, external address port) + * + * @param sm snat global configuration data + * @param s NAT session + * @param thread_index thread index + * @param is_ha is HA event + */ +void nat_free_session_data (snat_main_t * sm, snat_session_t * s, + u32 thread_index, u8 is_ha); + +/** + * @brief Set NAT44 session limit (session limit, vrf id) + * + * @param session_limit Session limit + * @param vrf_id VRF id + * @return 0 on success, non-zero value otherwise + */ +int nat44_set_session_limit (u32 session_limit, u32 vrf_id); + +/** + * @brief Update NAT44 session limit flushing all data (session limit, vrf id) + * + * @param session_limit Session limit + * @param vrf_id VRF id + * @return 0 on success, non-zero value otherwise + */ +int nat44_update_session_limit (u32 session_limit, u32 vrf_id); + +/** + * @brief Free outside address and port pair + * + * @param addresses vector of outside addresses + * @param thread_index thread index + * @param key address, port and protocol + */ +void +snat_free_outside_address_and_port (snat_address_t * addresses, + u32 thread_index, + ip4_address_t * addr, + u16 port, nat_protocol_t protocol); + +void expire_per_vrf_sessions (u32 fib_index); + +/** + * @brief Match NAT44 static mapping. + * + * @param key address and port to match + * @param addr external/local address of the matched mapping + * @param port port of the matched mapping + * @param fib_index fib index of the matched mapping + * @param by_external if 0 match by local address otherwise match by external + * address + * @param is_addr_only 1 if matched mapping is address only + * @param twice_nat matched mapping is twice NAT type + * @param lb 1 if matched mapping is load-balanced + * @param ext_host_addr external host address + * @param is_identity_nat 1 if indentity mapping + * @param out if !=0 set to pointer of the mapping structure + * + * @returns 0 if match found otherwise 1. + */ +int snat_static_mapping_match (snat_main_t * sm, + ip4_address_t match_addr, + u16 match_port, + u32 match_fib_index, + nat_protocol_t match_protocol, + ip4_address_t * mapping_addr, + u16 * mapping_port, + u32 * mapping_fib_index, + u8 by_external, + u8 * is_addr_only, + twice_nat_type_t * twice_nat, + lb_nat_type_t * lb, + ip4_address_t * ext_host_addr, + u8 * is_identity_nat, + snat_static_mapping_t ** out); + +/** + * @brief Add/del NAT address to FIB. + * + * Add the external NAT address to the FIB as receive entries. This ensures + * that VPP will reply to ARP for this address and we don't need to enable + * proxy ARP on the outside interface. + * + * @param addr IPv4 address + * @param plen address prefix length + * @param sw_if_index software index of the outside interface + * @param is_add 0 = delete, 1 = add. + */ +void snat_add_del_addr_to_fib (ip4_address_t * addr, + u8 p_len, u32 sw_if_index, int is_add); + +#if 0 +void +nat_ha_sadd_ed_cb (ip4_address_t * in_addr, u16 in_port, + ip4_address_t * out_addr, u16 out_port, + ip4_address_t * eh_addr, u16 eh_port, + ip4_address_t * ehn_addr, u16 ehn_port, u8 proto, + u32 fib_index, u16 flags, u32 thread_index); + +void +nat_ha_sdel_ed_cb (ip4_address_t * out_addr, u16 out_port, + ip4_address_t * eh_addr, u16 eh_port, u8 proto, + u32 fib_index, u32 ti); + +void +nat_ha_sref_ed_cb (ip4_address_t * out_addr, u16 out_port, + ip4_address_t * eh_addr, u16 eh_port, u8 proto, + u32 fib_index, u32 total_pkts, u64 total_bytes, + u32 thread_index); +#endif + +int nat_set_outside_address_and_port (snat_address_t *addresses, + u32 thread_index, ip4_address_t addr, + u16 port, nat_protocol_t protocol); + +/* + * Why is this here? Because we don't need to touch this layer to + * simply reply to an icmp. We need to change id to a unique + * value to NAT an echo request/reply. + */ + +typedef struct +{ + u16 identifier; + u16 sequence; +} icmp_echo_header_t; + +typedef struct +{ + u16 src_port, dst_port; +} tcp_udp_header_t; + +u32 get_thread_idx_by_port (u16 e_port); + +u8 *format_static_mapping_kvp (u8 *s, va_list *args); + +u8 *format_session_kvp (u8 *s, va_list *args); + +u32 nat_calc_bihash_buckets (u32 n_elts); + +void nat44_addresses_free (snat_address_t **addresses); + +void nat44_ed_sessions_clear (); + +int nat44_ed_set_frame_queue_nelts (u32 frame_queue_nelts); + +typedef enum +{ + NAT_ED_TRNSL_ERR_SUCCESS = 0, + NAT_ED_TRNSL_ERR_TRANSLATION_FAILED = 1, + NAT_ED_TRNSL_ERR_FLOW_MISMATCH = 2, +} nat_translation_error_e; + +nat_translation_error_e +nat_6t_flow_buf_translate (snat_main_t *sm, vlib_buffer_t *b, ip4_header_t *ip, + nat_6t_flow_t *f, nat_protocol_t proto, + int is_output_feature); + +void nat_6t_l3_l4_csum_calc (nat_6t_flow_t *f); + +format_function_t format_nat_ed_translation_error; +format_function_t format_nat_6t_flow; +format_function_t format_ed_session_kvp; + +#endif /* __included_nat44_ed_h__ */ +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/src/plugins/nat/nat44-ed/nat44_ed_affinity.c b/src/plugins/nat/nat44-ed/nat44_ed_affinity.c new file mode 100644 index 00000000000..6debe401292 --- /dev/null +++ b/src/plugins/nat/nat44-ed/nat44_ed_affinity.c @@ -0,0 +1,288 @@ +/* + * Copyright (c) 2018 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file + * @brief NAT plugin client-IP based session affinity for load-balancing + */ + +#include + +#include +#include + +nat_affinity_main_t nat_affinity_main; + +#define AFFINITY_HASH_BUCKETS 65536 +#define AFFINITY_HASH_MEMORY (2 << 25) + +u8 * +format_affinity_kvp (u8 * s, va_list * args) +{ + clib_bihash_kv_16_8_t *v = va_arg (*args, clib_bihash_kv_16_8_t *); + nat_affinity_key_t k; + + k.as_u64[0] = v->key[0]; + k.as_u64[1] = v->key[1]; + + s = format (s, "client %U backend %U:%d proto %U index %llu", + format_ip4_address, &k.client_addr, + format_ip4_address, &k.service_addr, + clib_net_to_host_u16 (k.service_port), + format_nat_protocol, k.proto); + + return s; +} + +void +nat_affinity_enable () +{ + nat_affinity_main_t *nam = &nat_affinity_main; + vlib_thread_main_t *tm = vlib_get_thread_main (); + + if (tm->n_vlib_mains > 1) + clib_spinlock_init (&nam->affinity_lock); + clib_bihash_init_16_8 (&nam->affinity_hash, "nat-affinity", + AFFINITY_HASH_BUCKETS, AFFINITY_HASH_MEMORY); + clib_bihash_set_kvp_format_fn_16_8 (&nam->affinity_hash, + format_affinity_kvp); +} + +void +nat_affinity_disable () +{ + nat_affinity_main_t *nam = &nat_affinity_main; + vlib_thread_main_t *tm = vlib_get_thread_main (); + + if (tm->n_vlib_mains > 1) + clib_spinlock_free (&nam->affinity_lock); + clib_bihash_free_16_8 (&nam->affinity_hash); +} + +clib_error_t * +nat_affinity_init (vlib_main_t * vm) +{ + nat_affinity_main_t *nam = &nat_affinity_main; + nam->vlib_main = vm; + return 0; +} + +static_always_inline void +make_affinity_kv (clib_bihash_kv_16_8_t * kv, ip4_address_t client_addr, + ip4_address_t service_addr, u8 proto, u16 service_port) +{ + nat_affinity_key_t *key = (nat_affinity_key_t *) kv->key; + + key->client_addr = client_addr; + key->service_addr = service_addr; + key->proto = proto; + key->service_port = service_port; + + kv->value = ~0ULL; +} + +u32 +nat_affinity_get_per_service_list_head_index (void) +{ + nat_affinity_main_t *nam = &nat_affinity_main; + dlist_elt_t *head_elt; + + clib_spinlock_lock_if_init (&nam->affinity_lock); + + pool_get (nam->list_pool, head_elt); + clib_dlist_init (nam->list_pool, head_elt - nam->list_pool); + + clib_spinlock_unlock_if_init (&nam->affinity_lock); + + return head_elt - nam->list_pool; +} + +void +nat_affinity_flush_service (u32 affinity_per_service_list_head_index) +{ + snat_main_t *sm = &snat_main; + nat_affinity_main_t *nam = &nat_affinity_main; + u32 elt_index; + dlist_elt_t *elt; + nat_affinity_t *a; + clib_bihash_kv_16_8_t kv; + + clib_spinlock_lock_if_init (&nam->affinity_lock); + + while ((elt_index = + clib_dlist_remove_head (nam->list_pool, + affinity_per_service_list_head_index)) != + ~0) + { + elt = pool_elt_at_index (nam->list_pool, elt_index); + a = pool_elt_at_index (nam->affinity_pool, elt->value); + kv.key[0] = a->key.as_u64[0]; + kv.key[1] = a->key.as_u64[1]; + pool_put_index (nam->affinity_pool, elt->value); + if (clib_bihash_add_del_16_8 (&nam->affinity_hash, &kv, 0)) + nat_elog_warn (sm, "affinity key del failed"); + pool_put_index (nam->list_pool, elt_index); + } + pool_put_index (nam->list_pool, affinity_per_service_list_head_index); + + clib_spinlock_unlock_if_init (&nam->affinity_lock); +} + +int +nat_affinity_find_and_lock (ip4_address_t client_addr, + ip4_address_t service_addr, u8 proto, + u16 service_port, u8 * backend_index) +{ + snat_main_t *sm = &snat_main; + nat_affinity_main_t *nam = &nat_affinity_main; + clib_bihash_kv_16_8_t kv, value; + nat_affinity_t *a; + int rv = 0; + + make_affinity_kv (&kv, client_addr, service_addr, proto, service_port); + clib_spinlock_lock_if_init (&nam->affinity_lock); + if (clib_bihash_search_16_8 (&nam->affinity_hash, &kv, &value)) + { + rv = 1; + goto unlock; + } + + a = pool_elt_at_index (nam->affinity_pool, value.value); + /* if already expired delete */ + if (a->ref_cnt == 0) + { + if (a->expire < vlib_time_now (nam->vlib_main)) + { + clib_dlist_remove (nam->list_pool, a->per_service_index); + pool_put_index (nam->list_pool, a->per_service_index); + pool_put_index (nam->affinity_pool, value.value); + if (clib_bihash_add_del_16_8 (&nam->affinity_hash, &kv, 0)) + nat_elog_warn (sm, "affinity key del failed"); + rv = 1; + goto unlock; + } + } + a->ref_cnt++; + *backend_index = a->backend_index; + +unlock: + clib_spinlock_unlock_if_init (&nam->affinity_lock); + return rv; +} + +static int +affinity_is_expired_cb (clib_bihash_kv_16_8_t * kv, void *arg) +{ + snat_main_t *sm = &snat_main; + nat_affinity_main_t *nam = &nat_affinity_main; + nat_affinity_t *a; + + a = pool_elt_at_index (nam->affinity_pool, kv->value); + if (a->ref_cnt == 0) + { + if (a->expire < vlib_time_now (nam->vlib_main)) + { + clib_dlist_remove (nam->list_pool, a->per_service_index); + pool_put_index (nam->list_pool, a->per_service_index); + pool_put_index (nam->affinity_pool, kv->value); + if (clib_bihash_add_del_16_8 (&nam->affinity_hash, kv, 0)) + nat_elog_warn (sm, "affinity key del failed"); + return 1; + } + } + + return 0; +} + +int +nat_affinity_create_and_lock (ip4_address_t client_addr, + ip4_address_t service_addr, u8 proto, + u16 service_port, u8 backend_index, + u32 sticky_time, + u32 affinity_per_service_list_head_index) +{ + snat_main_t *sm = &snat_main; + nat_affinity_main_t *nam = &nat_affinity_main; + clib_bihash_kv_16_8_t kv, value; + nat_affinity_t *a; + dlist_elt_t *list_elt; + int rv = 0; + + make_affinity_kv (&kv, client_addr, service_addr, proto, service_port); + clib_spinlock_lock_if_init (&nam->affinity_lock); + if (!clib_bihash_search_16_8 (&nam->affinity_hash, &kv, &value)) + { + rv = 1; + nat_elog_notice (sm, "affinity key already exist"); + goto unlock; + } + + pool_get (nam->affinity_pool, a); + kv.value = a - nam->affinity_pool; + rv = + clib_bihash_add_or_overwrite_stale_16_8 (&nam->affinity_hash, &kv, + affinity_is_expired_cb, NULL); + if (rv) + { + nat_elog_notice (sm, "affinity key add failed"); + pool_put (nam->affinity_pool, a); + goto unlock; + } + + pool_get (nam->list_pool, list_elt); + clib_dlist_init (nam->list_pool, list_elt - nam->list_pool); + list_elt->value = a - nam->affinity_pool; + a->per_service_index = list_elt - nam->list_pool; + a->backend_index = backend_index; + a->ref_cnt = 1; + a->sticky_time = sticky_time; + a->key.as_u64[0] = kv.key[0]; + a->key.as_u64[1] = kv.key[1]; + clib_dlist_addtail (nam->list_pool, affinity_per_service_list_head_index, + a->per_service_index); + +unlock: + clib_spinlock_unlock_if_init (&nam->affinity_lock); + return rv; +} + +void +nat_affinity_unlock (ip4_address_t client_addr, ip4_address_t service_addr, + u8 proto, u16 service_port) +{ + nat_affinity_main_t *nam = &nat_affinity_main; + clib_bihash_kv_16_8_t kv, value; + nat_affinity_t *a; + + make_affinity_kv (&kv, client_addr, service_addr, proto, service_port); + clib_spinlock_lock_if_init (&nam->affinity_lock); + if (clib_bihash_search_16_8 (&nam->affinity_hash, &kv, &value)) + goto unlock; + + a = pool_elt_at_index (nam->affinity_pool, value.value); + a->ref_cnt--; + if (a->ref_cnt == 0) + a->expire = (u64) a->sticky_time + vlib_time_now (nam->vlib_main); + +unlock: + clib_spinlock_unlock_if_init (&nam->affinity_lock); +} + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/src/plugins/nat/nat44-ed/nat44_ed_affinity.h b/src/plugins/nat/nat44-ed/nat44_ed_affinity.h new file mode 100644 index 00000000000..2cfa9d29eb0 --- /dev/null +++ b/src/plugins/nat/nat44-ed/nat44_ed_affinity.h @@ -0,0 +1,152 @@ +/* + * Copyright (c) 2018 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file + * @brief NAT plugin client-IP based session affinity for load-balancing + */ + +#ifndef __included_nat44_ed_affinity_h__ +#define __included_nat44_ed_affinity_h__ + +#include +#include +#include + +typedef struct +{ + union + { + struct + { + ip4_address_t service_addr; + ip4_address_t client_addr; + /* align by making this 4 octets even though its a 1 octet field */ + u32 proto; + /* align by making this 4 octets even though its a 2 octets field */ + u32 service_port; + }; + u64 as_u64[2]; + }; +} nat_affinity_key_t; + +/* *INDENT-OFF* */ +typedef CLIB_PACKED(struct +{ + nat_affinity_key_t key; + u32 sticky_time; + u32 ref_cnt; + u32 per_service_index; + u8 backend_index; + f64 expire; +}) nat_affinity_t; +/* *INDENT-ON* */ + +typedef struct +{ + nat_affinity_t *affinity_pool; + clib_bihash_16_8_t affinity_hash; + clib_spinlock_t affinity_lock; + dlist_elt_t *list_pool; + vlib_main_t *vlib_main; +} nat_affinity_main_t; + +extern nat_affinity_main_t nat_affinity_main; + +/** + * @brief Get new affinity per service list head index. + * + * @returns new affinity per service list head index. + */ +u32 nat_affinity_get_per_service_list_head_index (void); + +/** + * @brief Flush all service affinity data. + * + * @param affinity_per_service_list_head_index Per sevice list head index. + */ +void nat_affinity_flush_service (u32 affinity_per_service_list_head_index); + +/** + * @brief NAT affinity enable + */ +void nat_affinity_enable (); + +/** + * @brief NAT affinity disable + */ +void nat_affinity_disable (); + +/** + * @brief Initialize NAT client-IP based affinity. + * + * @param vm vlib main. + * + * @return error code. + */ +clib_error_t *nat_affinity_init (vlib_main_t * vm); + +/** + * @brief Find service backend index for client-IP and take a reference + * counting lock. + * + * @param client_addr Client IP address. + * @param service_addr Service IP address. + * @param proto IP protocol number. + * @param service_port Service L4 port number. + * @param backend_index Service backend index for client-IP if found. + * + * @return 0 on success, non-zero value otherwise. + */ +int nat_affinity_find_and_lock (ip4_address_t client_addr, + ip4_address_t service_addr, u8 proto, + u16 service_port, u8 * backend_index); + +/** + * @brief Create affinity record and take reference counting lock. + * @param client_addr Client IP address. + * @param service_addr Service IP address. + * @param proto IP protocol number. + * @param service_port Service L4 port number. + * @param backend_index Service backend index for client-IP. + * @param sticky_time Affinity sticky time in seconds. + * @param affinity_per_service_list_head_index Per sevice list head index. + * + * @return 0 on success, non-zero value otherwise. + */ +int nat_affinity_create_and_lock (ip4_address_t client_addr, + ip4_address_t service_addr, u8 proto, + u16 service_port, u8 backend_index, + u32 sticky_time, + u32 affinity_per_service_list_head_index); +/** + * @brief Release a reference counting lock for affinity. + * + * @param client_addr Client IP address. + * @param service_addr Service IP address. + * @param proto IP protocol number. + */ +void nat_affinity_unlock (ip4_address_t client_addr, + ip4_address_t service_addr, u8 proto, + u16 service_port); + +#endif /* __included_nat44_ed_affinity_h__ */ + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/src/plugins/nat/nat44-ed/nat44_ed_api.c b/src/plugins/nat/nat44-ed/nat44_ed_api.c new file mode 100644 index 00000000000..ea812da9633 --- /dev/null +++ b/src/plugins/nat/nat44-ed/nat44_ed_api.c @@ -0,0 +1,1541 @@ +/* + * Copyright (c) 2020 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file + * @brief NAT44 plugin API implementation + */ + +#include +#include + +#include + +#include +#include + +#include + +#include +#include + +#define REPLY_MSG_ID_BASE sm->msg_id_base +#include + +static void +vl_api_nat_control_ping_t_handler (vl_api_nat_control_ping_t * mp) +{ + vl_api_nat_control_ping_reply_t *rmp; + snat_main_t *sm = &snat_main; + int rv = 0; + + REPLY_MACRO2 (VL_API_NAT_CONTROL_PING_REPLY, + ({ + rmp->vpe_pid = ntohl (getpid ()); + })); +} + +static void +vl_api_nat_show_config_t_handler (vl_api_nat_show_config_t * mp) +{ + vl_api_nat_show_config_reply_t *rmp; + snat_main_t *sm = &snat_main; + int rv = 0; + + REPLY_MACRO2_ZERO (VL_API_NAT_SHOW_CONFIG_REPLY, + ({ + rmp->translation_buckets = htonl (sm->translation_buckets); + rmp->user_buckets = 0; + rmp->max_translations_per_user = 0; + rmp->outside_vrf_id = htonl (sm->outside_vrf_id); + rmp->inside_vrf_id = htonl (sm->inside_vrf_id); + rmp->static_mapping_only = sm->static_mapping_only; + rmp->static_mapping_connection_tracking = + sm->static_mapping_connection_tracking; + rmp->endpoint_dependent = 1; + rmp->out2in_dpo = 0; + })); +} + +static void +vl_api_nat_show_config_2_t_handler (vl_api_nat_show_config_2_t * mp) +{ + vl_api_nat_show_config_2_reply_t *rmp; + snat_main_t *sm = &snat_main; + int rv = 0; + + REPLY_MACRO2_ZERO (VL_API_NAT_SHOW_CONFIG_2_REPLY, + ({ + rmp->translation_buckets = htonl (sm->translation_buckets); + rmp->user_buckets = 0; + rmp->max_translations_per_user = 0; + rmp->outside_vrf_id = htonl (sm->outside_vrf_id); + rmp->inside_vrf_id = htonl (sm->inside_vrf_id); + rmp->static_mapping_only = sm->static_mapping_only; + rmp->static_mapping_connection_tracking = + sm->static_mapping_connection_tracking; + rmp->endpoint_dependent = 1; + rmp->out2in_dpo = 0; + rmp->max_translations_per_thread = + clib_net_to_host_u32 (sm->max_translations_per_thread); + rmp->max_users_per_thread = 0; + })); +} + +static void +vl_api_nat44_show_running_config_t_handler (vl_api_nat44_show_running_config_t + * mp) +{ + vl_api_nat44_show_running_config_reply_t *rmp; + snat_main_t *sm = &snat_main; + nat44_config_t *rc = &sm->rconfig; + int rv = 0; + + REPLY_MACRO2_ZERO (VL_API_NAT44_SHOW_RUNNING_CONFIG_REPLY, + ({ + rmp->inside_vrf = htonl (rc->inside_vrf); + rmp->outside_vrf = htonl (rc->outside_vrf); + + rmp->sessions = htonl (rc->sessions); + rmp->translation_buckets = htonl (sm->translation_buckets); + + // OBSOLETE + rmp->users = 0; + rmp->user_buckets = 0; + rmp->user_sessions = 0; + + rmp->timeouts.udp = htonl (sm->timeouts.udp); + rmp->timeouts.tcp_established = htonl (sm->timeouts.tcp.established); + rmp->timeouts.tcp_transitory = htonl (sm->timeouts.tcp.transitory); + rmp->timeouts.icmp = htonl (sm->timeouts.icmp); + + rmp->forwarding_enabled = sm->forwarding_enabled == 1; + // consider how to split functionality between subplugins + rmp->ipfix_logging_enabled = nat_ipfix_logging_enabled (); + rmp->flags |= NAT44_IS_ENDPOINT_DEPENDENT; + if (rc->static_mapping_only) + rmp->flags |= NAT44_IS_STATIC_MAPPING_ONLY; + if (rc->connection_tracking) + rmp->flags |= NAT44_IS_CONNECTION_TRACKING; + })); +} + +static void +vl_api_nat_set_workers_t_handler (vl_api_nat_set_workers_t * mp) +{ + snat_main_t *sm = &snat_main; + vl_api_nat_set_workers_reply_t *rmp; + int rv = 0; + uword *bitmap = 0; + u64 mask; + + mask = clib_net_to_host_u64 (mp->worker_mask); + + if (sm->num_workers < 2) + { + rv = VNET_API_ERROR_FEATURE_DISABLED; + goto send_reply; + } + + bitmap = clib_bitmap_set_multiple (bitmap, 0, mask, BITS (mask)); + rv = snat_set_workers (bitmap); + clib_bitmap_free (bitmap); + +send_reply: + REPLY_MACRO (VL_API_NAT_SET_WORKERS_REPLY); +} + +static void +send_nat_worker_details (u32 worker_index, vl_api_registration_t * reg, + u32 context) +{ + vl_api_nat_worker_details_t *rmp; + snat_main_t *sm = &snat_main; + vlib_worker_thread_t *w = + vlib_worker_threads + worker_index + sm->first_worker_index; + + rmp = vl_msg_api_alloc (sizeof (*rmp)); + clib_memset (rmp, 0, sizeof (*rmp)); + rmp->_vl_msg_id = ntohs (VL_API_NAT_WORKER_DETAILS + sm->msg_id_base); + rmp->context = context; + rmp->worker_index = htonl (worker_index); + rmp->lcore_id = htonl (w->cpu_id); + strncpy ((char *) rmp->name, (char *) w->name, ARRAY_LEN (rmp->name) - 1); + + vl_api_send_msg (reg, (u8 *) rmp); +} + +static void +vl_api_nat_worker_dump_t_handler (vl_api_nat_worker_dump_t * mp) +{ + vl_api_registration_t *reg; + snat_main_t *sm = &snat_main; + u32 *worker_index; + + reg = vl_api_client_index_to_registration (mp->client_index); + if (!reg) + return; + + vec_foreach (worker_index, sm->workers) + { + send_nat_worker_details (*worker_index, reg, mp->context); + } +} + +static void +vl_api_nat44_session_cleanup_t_handler (vl_api_nat44_session_cleanup_t * mp) +{ + snat_main_t *sm = &snat_main; + vl_api_nat44_session_cleanup_reply_t *rmp; + int rv = VNET_API_ERROR_UNSUPPORTED; + REPLY_MACRO (VL_API_NAT44_SESSION_CLEANUP_REPLY); +} + +static void +vl_api_nat44_set_session_limit_t_handler (vl_api_nat44_set_session_limit_t * + mp) +{ + snat_main_t *sm = &snat_main; + vl_api_nat44_set_session_limit_reply_t *rmp; + int rv = 0; + + rv = nat44_set_session_limit + (ntohl (mp->session_limit), ntohl (mp->vrf_id)); + + REPLY_MACRO (VL_API_NAT44_SET_SESSION_LIMIT_REPLY); +} + +static void +vl_api_nat_set_log_level_t_handler (vl_api_nat_set_log_level_t * mp) +{ + snat_main_t *sm = &snat_main; + vl_api_nat_set_log_level_reply_t *rmp; + int rv = 0; + + if (sm->log_level > NAT_LOG_DEBUG) + rv = VNET_API_ERROR_UNSUPPORTED; + else + sm->log_level = mp->log_level; + + REPLY_MACRO (VL_API_NAT_SET_WORKERS_REPLY); +} + +static void + vl_api_nat44_plugin_enable_disable_t_handler + (vl_api_nat44_plugin_enable_disable_t * mp) +{ + snat_main_t *sm = &snat_main; + nat44_config_t c = { 0 }; + vl_api_nat44_plugin_enable_disable_reply_t *rmp; + int rv = 0; + + if (mp->enable) + { + if (mp->flags & NAT44_API_IS_OUT2IN_DPO || mp->users || + mp->user_sessions) + { + rv = VNET_API_ERROR_UNSUPPORTED; + } + else + { + c.static_mapping_only = mp->flags & NAT44_API_IS_STATIC_MAPPING_ONLY; + c.connection_tracking = mp->flags & NAT44_API_IS_CONNECTION_TRACKING; + + c.inside_vrf = ntohl (mp->inside_vrf); + c.outside_vrf = ntohl (mp->outside_vrf); + + c.sessions = ntohl (mp->sessions); + + rv = nat44_plugin_enable (c); + } + } + else + { + rv = nat44_plugin_disable (); + } + + REPLY_MACRO (VL_API_NAT44_PLUGIN_ENABLE_DISABLE_REPLY); +} + +static void +vl_api_nat_ipfix_enable_disable_t_handler (vl_api_nat_ipfix_enable_disable_t * + mp) +{ + snat_main_t *sm = &snat_main; + vl_api_nat_ipfix_enable_disable_reply_t *rmp; + int rv = 0; + + rv = nat_ipfix_logging_enable_disable (mp->enable, + clib_host_to_net_u32 + (mp->domain_id), + clib_host_to_net_u16 (mp->src_port)); + + REPLY_MACRO (VL_API_NAT_IPFIX_ENABLE_DISABLE_REPLY); +} + +static void +vl_api_nat_set_timeouts_t_handler (vl_api_nat_set_timeouts_t * mp) +{ + snat_main_t *sm = &snat_main; + vl_api_nat_set_timeouts_reply_t *rmp; + int rv = 0; + + sm->timeouts.udp = ntohl (mp->udp); + sm->timeouts.tcp.established = ntohl (mp->tcp_established); + sm->timeouts.tcp.transitory = ntohl (mp->tcp_transitory); + sm->timeouts.icmp = ntohl (mp->icmp); + + REPLY_MACRO (VL_API_NAT_SET_TIMEOUTS_REPLY); +} + +static void +vl_api_nat_get_timeouts_t_handler (vl_api_nat_get_timeouts_t * mp) +{ + snat_main_t *sm = &snat_main; + vl_api_nat_get_timeouts_reply_t *rmp; + int rv = 0; + + REPLY_MACRO2 (VL_API_NAT_GET_TIMEOUTS_REPLY, + ({ + rmp->udp = htonl (sm->timeouts.udp); + rmp->tcp_established = htonl (sm->timeouts.tcp.established); + rmp->tcp_transitory = htonl (sm->timeouts.tcp.transitory); + rmp->icmp = htonl (sm->timeouts.icmp); + })) +} + +static void + vl_api_nat_set_addr_and_port_alloc_alg_t_handler + (vl_api_nat_set_addr_and_port_alloc_alg_t * mp) +{ + snat_main_t *sm = &snat_main; + vl_api_nat_set_addr_and_port_alloc_alg_reply_t *rmp; + int rv = VNET_API_ERROR_UNSUPPORTED; + REPLY_MACRO (VL_API_NAT_SET_ADDR_AND_PORT_ALLOC_ALG_REPLY); +} + +static void + vl_api_nat_get_addr_and_port_alloc_alg_t_handler + (vl_api_nat_get_addr_and_port_alloc_alg_t * mp) +{ + snat_main_t *sm = &snat_main; + vl_api_nat_get_addr_and_port_alloc_alg_reply_t *rmp; + int rv = VNET_API_ERROR_UNSUPPORTED; + REPLY_MACRO (VL_API_NAT_GET_ADDR_AND_PORT_ALLOC_ALG_REPLY); +} + +static void +vl_api_nat_set_mss_clamping_t_handler (vl_api_nat_set_mss_clamping_t * mp) +{ + snat_main_t *sm = &snat_main; + vl_api_nat_set_mss_clamping_reply_t *rmp; + int rv = 0; + + if (mp->enable) + sm->mss_clamping = ntohs (mp->mss_value); + else + sm->mss_clamping = 0; + + REPLY_MACRO (VL_API_NAT_SET_MSS_CLAMPING_REPLY); +} + +static void +vl_api_nat_get_mss_clamping_t_handler (vl_api_nat_get_mss_clamping_t * mp) +{ + snat_main_t *sm = &snat_main; + vl_api_nat_get_mss_clamping_reply_t *rmp; + int rv = 0; + + REPLY_MACRO2 (VL_API_NAT_GET_MSS_CLAMPING_REPLY, + ({ + rmp->enable = sm->mss_clamping ? 1 : 0; + rmp->mss_value = htons (sm->mss_clamping); + })) +} + +static void +vl_api_nat_ha_set_listener_t_handler (vl_api_nat_ha_set_listener_t * mp) +{ + snat_main_t *sm = &snat_main; + vl_api_nat_ha_set_listener_reply_t *rmp; + int rv = VNET_API_ERROR_UNSUPPORTED; + REPLY_MACRO (VL_API_NAT_HA_SET_LISTENER_REPLY); +} + +static void +vl_api_nat_ha_get_listener_t_handler (vl_api_nat_ha_get_listener_t * mp) +{ + snat_main_t *sm = &snat_main; + vl_api_nat_ha_get_listener_reply_t *rmp; + int rv = VNET_API_ERROR_UNSUPPORTED; + REPLY_MACRO (VL_API_NAT_HA_GET_LISTENER_REPLY); +} + +static void +vl_api_nat_ha_set_failover_t_handler (vl_api_nat_ha_set_failover_t * mp) +{ + snat_main_t *sm = &snat_main; + vl_api_nat_ha_set_failover_reply_t *rmp; + int rv = VNET_API_ERROR_UNSUPPORTED; + REPLY_MACRO (VL_API_NAT_HA_SET_FAILOVER_REPLY); +} + +static void +vl_api_nat_ha_get_failover_t_handler (vl_api_nat_ha_get_failover_t * mp) +{ + snat_main_t *sm = &snat_main; + vl_api_nat_ha_get_failover_reply_t *rmp; + int rv = VNET_API_ERROR_UNSUPPORTED; + REPLY_MACRO (VL_API_NAT_HA_GET_FAILOVER_REPLY); +} + +static void +vl_api_nat_ha_flush_t_handler (vl_api_nat_ha_flush_t * mp) +{ + snat_main_t *sm = &snat_main; + vl_api_nat_ha_flush_reply_t *rmp; + int rv = VNET_API_ERROR_UNSUPPORTED; + REPLY_MACRO (VL_API_NAT_HA_FLUSH_REPLY); +} + +static void +vl_api_nat_ha_resync_t_handler (vl_api_nat_ha_resync_t * mp) +{ + snat_main_t *sm = &snat_main; + vl_api_nat_ha_resync_reply_t *rmp; + int rv = VNET_API_ERROR_UNSUPPORTED; + REPLY_MACRO (VL_API_NAT_HA_RESYNC_REPLY); +} + +static void +vl_api_nat44_del_user_t_handler (vl_api_nat44_del_user_t * mp) +{ + snat_main_t *sm = &snat_main; + vl_api_nat44_del_user_reply_t *rmp; + int rv = VNET_API_ERROR_UNSUPPORTED; + REPLY_MACRO (VL_API_NAT44_DEL_USER_REPLY); +} + +static void + vl_api_nat44_add_del_address_range_t_handler + (vl_api_nat44_add_del_address_range_t * mp) +{ + snat_main_t *sm = &snat_main; + vl_api_nat44_add_del_address_range_reply_t *rmp; + ip4_address_t this_addr; + u8 is_add, twice_nat; + u32 start_host_order, end_host_order; + u32 vrf_id; + int i, count; + int rv = 0; + u32 *tmp; + + if (sm->static_mapping_only) + { + rv = VNET_API_ERROR_FEATURE_DISABLED; + goto send_reply; + } + + is_add = mp->is_add; + twice_nat = mp->flags & NAT_API_IS_TWICE_NAT; + + tmp = (u32 *) mp->first_ip_address; + start_host_order = clib_host_to_net_u32 (tmp[0]); + tmp = (u32 *) mp->last_ip_address; + end_host_order = clib_host_to_net_u32 (tmp[0]); + + count = (end_host_order - start_host_order) + 1; + + vrf_id = clib_host_to_net_u32 (mp->vrf_id); + + if (count > 1024) + nat_log_info ("%U - %U, %d addresses...", + format_ip4_address, mp->first_ip_address, + format_ip4_address, mp->last_ip_address, count); + + memcpy (&this_addr.as_u8, mp->first_ip_address, 4); + + for (i = 0; i < count; i++) + { + if (is_add) + rv = snat_add_address (sm, &this_addr, vrf_id, twice_nat); + else + rv = snat_del_address (sm, this_addr, 0, twice_nat); + + if (rv) + goto send_reply; + + increment_v4_address (&this_addr); + } + +send_reply: + REPLY_MACRO (VL_API_NAT44_ADD_DEL_ADDRESS_RANGE_REPLY); +} + +static void +send_nat44_address_details (snat_address_t * a, + vl_api_registration_t * reg, u32 context, + u8 twice_nat) +{ + vl_api_nat44_address_details_t *rmp; + snat_main_t *sm = &snat_main; + + rmp = vl_msg_api_alloc (sizeof (*rmp)); + clib_memset (rmp, 0, sizeof (*rmp)); + rmp->_vl_msg_id = ntohs (VL_API_NAT44_ADDRESS_DETAILS + sm->msg_id_base); + clib_memcpy (rmp->ip_address, &(a->addr), 4); + if (a->fib_index != ~0) + { + fib_table_t *fib = fib_table_get (a->fib_index, FIB_PROTOCOL_IP4); + rmp->vrf_id = ntohl (fib->ft_table_id); + } + else + rmp->vrf_id = ~0; + if (twice_nat) + rmp->flags |= NAT_API_IS_TWICE_NAT; + rmp->context = context; + + vl_api_send_msg (reg, (u8 *) rmp); +} + +static void +vl_api_nat44_address_dump_t_handler (vl_api_nat44_address_dump_t * mp) +{ + vl_api_registration_t *reg; + snat_main_t *sm = &snat_main; + snat_address_t *a; + + reg = vl_api_client_index_to_registration (mp->client_index); + if (!reg) + return; + + vec_foreach (a, sm->addresses) + send_nat44_address_details (a, reg, mp->context, 0); + vec_foreach (a, sm->twice_nat_addresses) + send_nat44_address_details (a, reg, mp->context, 1); +} + +static void + vl_api_nat44_interface_add_del_feature_t_handler + (vl_api_nat44_interface_add_del_feature_t * mp) +{ + snat_main_t *sm = &snat_main; + vl_api_nat44_interface_add_del_feature_reply_t *rmp; + u32 sw_if_index = ntohl (mp->sw_if_index); + u8 is_del; + int rv = 0; + + is_del = !mp->is_add; + + VALIDATE_SW_IF_INDEX (mp); + + rv = + snat_interface_add_del (sw_if_index, mp->flags & NAT_API_IS_INSIDE, + is_del); + + BAD_SW_IF_INDEX_LABEL; + + REPLY_MACRO (VL_API_NAT44_INTERFACE_ADD_DEL_FEATURE_REPLY); +} + +static void +send_nat44_interface_details (snat_interface_t * i, + vl_api_registration_t * reg, u32 context) +{ + vl_api_nat44_interface_details_t *rmp; + snat_main_t *sm = &snat_main; + + rmp = vl_msg_api_alloc (sizeof (*rmp)); + clib_memset (rmp, 0, sizeof (*rmp)); + rmp->_vl_msg_id = ntohs (VL_API_NAT44_INTERFACE_DETAILS + sm->msg_id_base); + rmp->sw_if_index = ntohl (i->sw_if_index); + + if (nat_interface_is_inside (i)) + rmp->flags |= NAT_API_IS_INSIDE; + if (nat_interface_is_outside (i)) + rmp->flags |= NAT_API_IS_OUTSIDE; + + rmp->context = context; + + vl_api_send_msg (reg, (u8 *) rmp); +} + +static void +vl_api_nat44_interface_dump_t_handler (vl_api_nat44_interface_dump_t * mp) +{ + vl_api_registration_t *reg; + snat_main_t *sm = &snat_main; + snat_interface_t *i; + + reg = vl_api_client_index_to_registration (mp->client_index); + if (!reg) + return; + + pool_foreach (i, sm->interfaces) + { + send_nat44_interface_details(i, reg, mp->context); + } +} + +static void + vl_api_nat44_interface_add_del_output_feature_t_handler + (vl_api_nat44_interface_add_del_output_feature_t * mp) +{ + snat_main_t *sm = &snat_main; + vl_api_nat44_interface_add_del_output_feature_reply_t *rmp; + u32 sw_if_index = ntohl (mp->sw_if_index); + int rv = 0; + + VALIDATE_SW_IF_INDEX (mp); + + rv = snat_interface_add_del_output_feature (sw_if_index, + mp->flags & NAT_API_IS_INSIDE, + !mp->is_add); + + BAD_SW_IF_INDEX_LABEL; + REPLY_MACRO (VL_API_NAT44_INTERFACE_ADD_DEL_OUTPUT_FEATURE_REPLY); +} + +static void +send_nat44_interface_output_feature_details (snat_interface_t * i, + vl_api_registration_t * reg, + u32 context) +{ + vl_api_nat44_interface_output_feature_details_t *rmp; + snat_main_t *sm = &snat_main; + + rmp = vl_msg_api_alloc (sizeof (*rmp)); + clib_memset (rmp, 0, sizeof (*rmp)); + rmp->_vl_msg_id = + ntohs (VL_API_NAT44_INTERFACE_OUTPUT_FEATURE_DETAILS + sm->msg_id_base); + rmp->sw_if_index = ntohl (i->sw_if_index); + rmp->context = context; + + if (nat_interface_is_inside (i)) + rmp->flags |= NAT_API_IS_INSIDE; + + vl_api_send_msg (reg, (u8 *) rmp); +} + +static void + vl_api_nat44_interface_output_feature_dump_t_handler + (vl_api_nat44_interface_output_feature_dump_t * mp) +{ + vl_api_registration_t *reg; + snat_main_t *sm = &snat_main; + snat_interface_t *i; + + reg = vl_api_client_index_to_registration (mp->client_index); + if (!reg) + return; + + pool_foreach (i, sm->output_feature_interfaces) + { + send_nat44_interface_output_feature_details (i, reg, mp->context); + } +} + +static void + vl_api_nat44_add_del_static_mapping_t_handler + (vl_api_nat44_add_del_static_mapping_t * mp) +{ + snat_main_t *sm = &snat_main; + vl_api_nat44_add_del_static_mapping_reply_t *rmp; + ip4_address_t local_addr, external_addr, pool_addr = { 0 }; + u16 local_port = 0, external_port = 0; + u32 vrf_id, external_sw_if_index; + twice_nat_type_t twice_nat = TWICE_NAT_DISABLED; + int rv = 0; + nat_protocol_t proto; + u8 *tag = 0; + + memcpy (&local_addr.as_u8, mp->local_ip_address, 4); + memcpy (&external_addr.as_u8, mp->external_ip_address, 4); + + if (!(mp->flags & NAT_API_IS_ADDR_ONLY)) + { + local_port = mp->local_port; + external_port = mp->external_port; + } + + vrf_id = clib_net_to_host_u32 (mp->vrf_id); + external_sw_if_index = clib_net_to_host_u32 (mp->external_sw_if_index); + proto = ip_proto_to_nat_proto (mp->protocol); + + if (mp->flags & NAT_API_IS_TWICE_NAT) + twice_nat = TWICE_NAT; + else if (mp->flags & NAT_API_IS_SELF_TWICE_NAT) + twice_nat = TWICE_NAT_SELF; + mp->tag[sizeof (mp->tag) - 1] = 0; + tag = format (0, "%s", mp->tag); + vec_terminate_c_string (tag); + + rv = snat_add_static_mapping ( + local_addr, external_addr, local_port, external_port, vrf_id, + mp->flags & NAT_API_IS_ADDR_ONLY, external_sw_if_index, proto, mp->is_add, + twice_nat, mp->flags & NAT_API_IS_OUT2IN_ONLY, tag, 0, pool_addr, 0); + vec_free (tag); + + REPLY_MACRO (VL_API_NAT44_ADD_DEL_STATIC_MAPPING_REPLY); +} + +static void + vl_api_nat44_add_del_static_mapping_v2_t_handler + (vl_api_nat44_add_del_static_mapping_v2_t * mp) +{ + snat_main_t *sm = &snat_main; + vl_api_nat44_add_del_static_mapping_v2_reply_t *rmp; + ip4_address_t local_addr, external_addr, pool_addr; + u16 local_port = 0, external_port = 0; + u32 vrf_id, external_sw_if_index; + twice_nat_type_t twice_nat = TWICE_NAT_DISABLED; + int rv = 0; + nat_protocol_t proto; + u8 *tag = 0; + + memcpy (&pool_addr.as_u8, mp->pool_ip_address, 4); + memcpy (&local_addr.as_u8, mp->local_ip_address, 4); + memcpy (&external_addr.as_u8, mp->external_ip_address, 4); + + if (!(mp->flags & NAT_API_IS_ADDR_ONLY)) + { + local_port = mp->local_port; + external_port = mp->external_port; + } + + vrf_id = clib_net_to_host_u32 (mp->vrf_id); + external_sw_if_index = clib_net_to_host_u32 (mp->external_sw_if_index); + proto = ip_proto_to_nat_proto (mp->protocol); + + if (mp->flags & NAT_API_IS_TWICE_NAT) + twice_nat = TWICE_NAT; + else if (mp->flags & NAT_API_IS_SELF_TWICE_NAT) + twice_nat = TWICE_NAT_SELF; + mp->tag[sizeof (mp->tag) - 1] = 0; + tag = format (0, "%s", mp->tag); + vec_terminate_c_string (tag); + + rv = snat_add_static_mapping (local_addr, external_addr, local_port, + external_port, vrf_id, + mp->flags & NAT_API_IS_ADDR_ONLY, + external_sw_if_index, proto, + mp->is_add, twice_nat, + mp->flags & NAT_API_IS_OUT2IN_ONLY, tag, 0, + pool_addr, mp->match_pool); + vec_free (tag); + + REPLY_MACRO (VL_API_NAT44_ADD_DEL_STATIC_MAPPING_V2_REPLY); +} + +static void +send_nat44_static_mapping_details (snat_static_mapping_t * m, + vl_api_registration_t * reg, u32 context) +{ + vl_api_nat44_static_mapping_details_t *rmp; + snat_main_t *sm = &snat_main; + u32 len = sizeof (*rmp); + + rmp = vl_msg_api_alloc (len); + clib_memset (rmp, 0, len); + rmp->_vl_msg_id = + ntohs (VL_API_NAT44_STATIC_MAPPING_DETAILS + sm->msg_id_base); + + clib_memcpy (rmp->local_ip_address, &(m->local_addr), 4); + clib_memcpy (rmp->external_ip_address, &(m->external_addr), 4); + rmp->external_sw_if_index = ~0; + rmp->vrf_id = htonl (m->vrf_id); + rmp->context = context; + + if (m->twice_nat == TWICE_NAT) + rmp->flags |= NAT_API_IS_TWICE_NAT; + else if (m->twice_nat == TWICE_NAT_SELF) + rmp->flags |= NAT_API_IS_SELF_TWICE_NAT; + + if (is_out2in_only_static_mapping (m)) + rmp->flags |= NAT_API_IS_OUT2IN_ONLY; + + if (is_addr_only_static_mapping (m)) + { + rmp->flags |= NAT_API_IS_ADDR_ONLY; + } + else + { + rmp->protocol = nat_proto_to_ip_proto (m->proto); + rmp->external_port = m->external_port; + rmp->local_port = m->local_port; + } + + if (m->tag) + strncpy ((char *) rmp->tag, (char *) m->tag, vec_len (m->tag)); + + vl_api_send_msg (reg, (u8 *) rmp); +} + +static void +send_nat44_static_map_resolve_details (snat_static_map_resolve_t * m, + vl_api_registration_t * reg, + u32 context) +{ + vl_api_nat44_static_mapping_details_t *rmp; + snat_main_t *sm = &snat_main; + + rmp = vl_msg_api_alloc (sizeof (*rmp)); + clib_memset (rmp, 0, sizeof (*rmp)); + rmp->_vl_msg_id = + ntohs (VL_API_NAT44_STATIC_MAPPING_DETAILS + sm->msg_id_base); + clib_memcpy (rmp->local_ip_address, &(m->l_addr), 4); + rmp->external_sw_if_index = htonl (m->sw_if_index); + rmp->vrf_id = htonl (m->vrf_id); + rmp->context = context; + + if (m->twice_nat) + rmp->flags |= NAT_API_IS_TWICE_NAT; + + if (m->addr_only) + { + rmp->flags |= NAT_API_IS_ADDR_ONLY; + } + else + { + rmp->protocol = nat_proto_to_ip_proto (m->proto); + rmp->external_port = m->e_port; + rmp->local_port = m->l_port; + } + if (m->tag) + strncpy ((char *) rmp->tag, (char *) m->tag, vec_len (m->tag)); + + vl_api_send_msg (reg, (u8 *) rmp); +} + +static void +vl_api_nat44_static_mapping_dump_t_handler (vl_api_nat44_static_mapping_dump_t + * mp) +{ + vl_api_registration_t *reg; + snat_main_t *sm = &snat_main; + snat_static_mapping_t *m; + snat_static_map_resolve_t *rp; + int j; + + reg = vl_api_client_index_to_registration (mp->client_index); + if (!reg) + return; + + pool_foreach (m, sm->static_mappings) + { + if (!is_identity_static_mapping(m) && !is_lb_static_mapping (m)) + send_nat44_static_mapping_details (m, reg, mp->context); + } + + for (j = 0; j < vec_len (sm->to_resolve); j++) + { + rp = sm->to_resolve + j; + if (!rp->identity_nat) + send_nat44_static_map_resolve_details (rp, reg, mp->context); + } +} + +static void + vl_api_nat44_add_del_identity_mapping_t_handler + (vl_api_nat44_add_del_identity_mapping_t * mp) +{ + snat_main_t *sm = &snat_main; + vl_api_nat44_add_del_identity_mapping_reply_t *rmp; + ip4_address_t addr, pool_addr = { 0 }; + u16 port = 0; + u32 vrf_id, sw_if_index; + int rv = 0; + nat_protocol_t proto = NAT_PROTOCOL_OTHER; + u8 *tag = 0; + + if (!(mp->flags & NAT_API_IS_ADDR_ONLY)) + { + port = mp->port; + proto = ip_proto_to_nat_proto (mp->protocol); + } + vrf_id = clib_net_to_host_u32 (mp->vrf_id); + sw_if_index = clib_net_to_host_u32 (mp->sw_if_index); + if (sw_if_index != ~0) + addr.as_u32 = 0; + else + memcpy (&addr.as_u8, mp->ip_address, 4); + mp->tag[sizeof (mp->tag) - 1] = 0; + tag = format (0, "%s", mp->tag); + vec_terminate_c_string (tag); + + rv = + snat_add_static_mapping (addr, addr, port, port, vrf_id, + mp->flags & NAT_API_IS_ADDR_ONLY, sw_if_index, + proto, mp->is_add, 0, 0, tag, 1, pool_addr, 0); + vec_free (tag); + + REPLY_MACRO (VL_API_NAT44_ADD_DEL_IDENTITY_MAPPING_REPLY); +} + +static void +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; + 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)); + rmp->_vl_msg_id = + ntohs (VL_API_NAT44_IDENTITY_MAPPING_DETAILS + sm->msg_id_base); + + if (is_addr_only_static_mapping (m)) + rmp->flags |= NAT_API_IS_ADDR_ONLY; + + clib_memcpy (rmp->ip_address, &(m->local_addr), 4); + rmp->port = m->local_port; + rmp->sw_if_index = ~0; + rmp->vrf_id = htonl (local->vrf_id); + rmp->protocol = nat_proto_to_ip_proto (m->proto); + rmp->context = context; + if (m->tag) + strncpy ((char *) rmp->tag, (char *) m->tag, vec_len (m->tag)); + + vl_api_send_msg (reg, (u8 *) rmp); +} + +static void +send_nat44_identity_map_resolve_details (snat_static_map_resolve_t * m, + vl_api_registration_t * reg, + u32 context) +{ + vl_api_nat44_identity_mapping_details_t *rmp; + snat_main_t *sm = &snat_main; + + rmp = vl_msg_api_alloc (sizeof (*rmp)); + clib_memset (rmp, 0, sizeof (*rmp)); + rmp->_vl_msg_id = + ntohs (VL_API_NAT44_IDENTITY_MAPPING_DETAILS + sm->msg_id_base); + + if (m->addr_only) + rmp->flags = (vl_api_nat_config_flags_t) NAT_API_IS_ADDR_ONLY; + + rmp->port = m->l_port; + rmp->sw_if_index = htonl (m->sw_if_index); + rmp->vrf_id = htonl (m->vrf_id); + rmp->protocol = nat_proto_to_ip_proto (m->proto); + rmp->context = context; + if (m->tag) + strncpy ((char *) rmp->tag, (char *) m->tag, vec_len (m->tag)); + + vl_api_send_msg (reg, (u8 *) rmp); +} + +static void + vl_api_nat44_identity_mapping_dump_t_handler + (vl_api_nat44_identity_mapping_dump_t * mp) +{ + vl_api_registration_t *reg; + snat_main_t *sm = &snat_main; + snat_static_mapping_t *m; + snat_static_map_resolve_t *rp; + int j; + + reg = vl_api_client_index_to_registration (mp->client_index); + if (!reg) + return; + + pool_foreach (m, sm->static_mappings) + { + if (is_identity_static_mapping(m) && !is_lb_static_mapping (m)) + { + pool_foreach_index (j, m->locals) + { + send_nat44_identity_mapping_details (m, j, reg, mp->context); + } + } + } + + for (j = 0; j < vec_len (sm->to_resolve); j++) + { + rp = sm->to_resolve + j; + if (rp->identity_nat) + send_nat44_identity_map_resolve_details (rp, reg, mp->context); + } +} + +static void + vl_api_nat44_add_del_interface_addr_t_handler + (vl_api_nat44_add_del_interface_addr_t * mp) +{ + snat_main_t *sm = &snat_main; + vl_api_nat44_add_del_interface_addr_reply_t *rmp; + u32 sw_if_index = ntohl (mp->sw_if_index); + int rv = 0; + u8 is_del; + + if (sm->static_mapping_only) + { + rv = VNET_API_ERROR_FEATURE_DISABLED; + goto send_reply; + } + + is_del = !mp->is_add; + + VALIDATE_SW_IF_INDEX (mp); + + rv = snat_add_interface_address (sm, sw_if_index, is_del, + mp->flags & NAT_API_IS_TWICE_NAT); + + BAD_SW_IF_INDEX_LABEL; + +send_reply: + REPLY_MACRO (VL_API_NAT44_ADD_DEL_INTERFACE_ADDR_REPLY); +} + +static void +send_nat44_interface_addr_details (u32 sw_if_index, + vl_api_registration_t * reg, u32 context, + u8 twice_nat) +{ + vl_api_nat44_interface_addr_details_t *rmp; + snat_main_t *sm = &snat_main; + + rmp = vl_msg_api_alloc (sizeof (*rmp)); + clib_memset (rmp, 0, sizeof (*rmp)); + rmp->_vl_msg_id = + ntohs (VL_API_NAT44_INTERFACE_ADDR_DETAILS + sm->msg_id_base); + rmp->sw_if_index = ntohl (sw_if_index); + + if (twice_nat) + rmp->flags = (vl_api_nat_config_flags_t) NAT_API_IS_TWICE_NAT; + rmp->context = context; + + vl_api_send_msg (reg, (u8 *) rmp); +} + +static void +vl_api_nat44_interface_addr_dump_t_handler (vl_api_nat44_interface_addr_dump_t + * mp) +{ + vl_api_registration_t *reg; + snat_main_t *sm = &snat_main; + u32 *i; + + reg = vl_api_client_index_to_registration (mp->client_index); + if (!reg) + return; + + vec_foreach (i, sm->auto_add_sw_if_indices) + { + send_nat44_interface_addr_details (*i, reg, mp->context, 0); + } + vec_foreach (i, sm->auto_add_sw_if_indices_twice_nat) + { + send_nat44_interface_addr_details (*i, reg, mp->context, 1); + } +} + +/* user (internal host) key */ +typedef struct +{ + union + { + struct + { + ip4_address_t addr; + u32 fib_index; + }; + u64 as_u64; + }; +} snat_user_key_t; + +typedef struct +{ + ip4_address_t addr; + u32 fib_index; + u32 nsessions; + u32 nstaticsessions; +} snat_user_t; + +typedef struct +{ + u32 user_buckets; + snat_user_t *users; + clib_bihash_8_8_t user_hash; +} user_create_helper_t; + +static void +send_nat44_user_details (snat_user_t * u, vl_api_registration_t * reg, + u32 context) +{ + vl_api_nat44_user_details_t *rmp; + snat_main_t *sm = &snat_main; + ip4_main_t *im = &ip4_main; + + rmp = vl_msg_api_alloc (sizeof (*rmp)); + clib_memset (rmp, 0, sizeof (*rmp)); + rmp->_vl_msg_id = ntohs (VL_API_NAT44_USER_DETAILS + sm->msg_id_base); + + if (!pool_is_free_index (im->fibs, u->fib_index)) + { + fib_table_t *fib = fib_table_get (u->fib_index, FIB_PROTOCOL_IP4); + rmp->vrf_id = ntohl (fib->ft_table_id); + } + + clib_memcpy (rmp->ip_address, &(u->addr), 4); + rmp->nsessions = ntohl (u->nsessions); + rmp->nstaticsessions = ntohl (u->nstaticsessions); + rmp->context = context; + + vl_api_send_msg (reg, (u8 *) rmp); +} + +static void +nat_ed_user_create_helper (user_create_helper_t *uch, snat_session_t *s) +{ + snat_user_key_t k; + k.addr = s->in2out.addr; + k.fib_index = s->in2out.fib_index; + clib_bihash_kv_8_8_t key, value; + key.key = k.as_u64; + snat_user_t *u; + + if (clib_bihash_search_8_8 (&uch->user_hash, &key, &value)) + { + pool_get (uch->users, u); + u->addr = k.addr; + u->fib_index = k.fib_index; + u->nsessions = 0; + u->nstaticsessions = 0; + key.value = u - uch->users; + clib_bihash_add_del_8_8 (&uch->user_hash, &key, 1); + } + else + { + u = pool_elt_at_index (uch->users, value.value); + } + if (snat_is_session_static (s)) + { + ++u->nstaticsessions; + } + else + { + ++u->nsessions; + } +} + +u8 * +format_user_kvp (u8 *s, va_list *args) +{ + clib_bihash_kv_8_8_t *v = va_arg (*args, clib_bihash_kv_8_8_t *); + snat_user_key_t k; + k.as_u64 = v->key; + s = format (s, "%U fib %d user-index %llu", format_ip4_address, &k.addr, + k.fib_index, v->value); + return s; +} + +static void +nat_ed_users_create (snat_main_per_thread_data_t *tsm, + user_create_helper_t *uch) +{ + snat_session_t *s; + clib_bihash_init_8_8 (&uch->user_hash, "users", uch->user_buckets, 0); + clib_bihash_set_kvp_format_fn_8_8 (&uch->user_hash, format_user_kvp); + pool_foreach (s, tsm->sessions) + { + nat_ed_user_create_helper (uch, s); + } +} + +static void +nat_ed_users_destroy (user_create_helper_t *uch) +{ + pool_free (uch->users); + clib_bihash_free_8_8 (&uch->user_hash); +} + +static void +vl_api_nat44_user_dump_t_handler (vl_api_nat44_user_dump_t * mp) +{ + user_create_helper_t uch; + vl_api_registration_t *reg; + snat_main_t *sm = &snat_main; + snat_main_per_thread_data_t *tsm; + snat_user_t *u; + + clib_memset (&uch, 0, sizeof (uch)); + + uch.user_buckets = nat_calc_bihash_buckets (1024); + + reg = vl_api_client_index_to_registration (mp->client_index); + if (!reg) + return; + + vec_foreach (tsm, sm->per_thread_data) + { + nat_ed_users_create (tsm, &uch); + pool_foreach (u, uch.users) + { + send_nat44_user_details (u, reg, mp->context); + } + nat_ed_users_destroy (&uch); + } +} + +static void +send_nat44_user_session_details (snat_session_t * s, + vl_api_registration_t * reg, u32 context) +{ + vl_api_nat44_user_session_details_t *rmp; + snat_main_t *sm = &snat_main; + + rmp = vl_msg_api_alloc (sizeof (*rmp)); + clib_memset (rmp, 0, sizeof (*rmp)); + rmp->_vl_msg_id = + ntohs (VL_API_NAT44_USER_SESSION_DETAILS + sm->msg_id_base); + clib_memcpy (rmp->outside_ip_address, (&s->out2in.addr), 4); + clib_memcpy (rmp->inside_ip_address, (&s->in2out.addr), 4); + + if (snat_is_session_static (s)) + rmp->flags |= NAT_API_IS_STATIC; + + if (is_twice_nat_session (s)) + rmp->flags |= NAT_API_IS_TWICE_NAT; + + if (is_ed_session (s) || is_fwd_bypass_session (s)) + rmp->flags |= NAT_API_IS_EXT_HOST_VALID; + + rmp->last_heard = clib_host_to_net_u64 ((u64) s->last_heard); + rmp->total_bytes = clib_host_to_net_u64 (s->total_bytes); + rmp->total_pkts = ntohl (s->total_pkts); + rmp->context = context; + if (snat_is_unk_proto_session (s)) + { + rmp->outside_port = 0; + rmp->inside_port = 0; + rmp->protocol = ntohs (s->in2out.port); + } + else + { + rmp->outside_port = s->out2in.port; + rmp->inside_port = s->in2out.port; + rmp->protocol = ntohs (nat_proto_to_ip_proto (s->nat_proto)); + } + if (is_ed_session (s) || is_fwd_bypass_session (s)) + { + clib_memcpy (rmp->ext_host_address, &s->ext_host_addr, 4); + rmp->ext_host_port = s->ext_host_port; + if (is_twice_nat_session (s)) + { + clib_memcpy (rmp->ext_host_nat_address, &s->ext_host_nat_addr, 4); + rmp->ext_host_nat_port = s->ext_host_nat_port; + } + } + + vl_api_send_msg (reg, (u8 *) rmp); +} + +static void +vl_api_nat44_user_session_dump_t_handler (vl_api_nat44_user_session_dump_t * + mp) +{ + snat_main_per_thread_data_t *tsm; + snat_main_t *sm = &snat_main; + vl_api_registration_t *reg; + snat_user_key_t ukey; + snat_session_t *s; + ip4_header_t ip; + + reg = vl_api_client_index_to_registration (mp->client_index); + if (!reg) + return; + + clib_memcpy (&ukey.addr, mp->ip_address, 4); + ip.src_address.as_u32 = ukey.addr.as_u32; + ukey.fib_index = fib_table_find (FIB_PROTOCOL_IP4, ntohl (mp->vrf_id)); + if (sm->num_workers > 1) + tsm = + vec_elt_at_index (sm->per_thread_data, + sm->worker_in2out_cb (&ip, ukey.fib_index, 0)); + else + tsm = vec_elt_at_index (sm->per_thread_data, sm->num_workers); + + pool_foreach (s, tsm->sessions) { + if (s->in2out.addr.as_u32 == ukey.addr.as_u32) + { + send_nat44_user_session_details (s, reg, mp->context); + } + } +} + +static nat44_lb_addr_port_t * +unformat_nat44_lb_addr_port (vl_api_nat44_lb_addr_port_t * addr_port_pairs, + u32 addr_port_pair_num) +{ + u8 i; + nat44_lb_addr_port_t *lb_addr_port_pairs = 0, lb_addr_port; + vl_api_nat44_lb_addr_port_t *ap; + + for (i = 0; i < addr_port_pair_num; i++) + { + ap = &addr_port_pairs[i]; + clib_memset (&lb_addr_port, 0, sizeof (lb_addr_port)); + clib_memcpy (&lb_addr_port.addr, ap->addr, 4); + lb_addr_port.port = ap->port; + lb_addr_port.probability = ap->probability; + lb_addr_port.vrf_id = clib_net_to_host_u32 (ap->vrf_id); + vec_add1 (lb_addr_port_pairs, lb_addr_port); + } + + return lb_addr_port_pairs; +} + +static void + vl_api_nat44_add_del_lb_static_mapping_t_handler + (vl_api_nat44_add_del_lb_static_mapping_t * mp) +{ + snat_main_t *sm = &snat_main; + vl_api_nat44_add_del_lb_static_mapping_reply_t *rmp; + twice_nat_type_t twice_nat = TWICE_NAT_DISABLED; + int rv = 0; + nat44_lb_addr_port_t *locals = 0; + ip4_address_t e_addr; + nat_protocol_t proto; + u8 *tag = 0; + + 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_nat_proto (mp->protocol); + + if (mp->flags & NAT_API_IS_TWICE_NAT) + twice_nat = TWICE_NAT; + else if (mp->flags & NAT_API_IS_SELF_TWICE_NAT) + twice_nat = TWICE_NAT_SELF; + mp->tag[sizeof (mp->tag) - 1] = 0; + tag = format (0, "%s", mp->tag); + vec_terminate_c_string (tag); + + rv = + nat44_add_del_lb_static_mapping (e_addr, + mp->external_port, + proto, locals, mp->is_add, + twice_nat, + mp->flags & NAT_API_IS_OUT2IN_ONLY, tag, + clib_net_to_host_u32 (mp->affinity)); + + vec_free (locals); + vec_free (tag); + REPLY_MACRO (VL_API_NAT44_ADD_DEL_LB_STATIC_MAPPING_REPLY); +} + +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; + nat_protocol_t proto; + + clib_memcpy (&e_addr, mp->external_addr, 4); + clib_memcpy (&l_addr, mp->local.addr, 4); + proto = ip_proto_to_nat_proto (mp->protocol); + + rv = + nat44_lb_static_mapping_add_del_local (e_addr, + mp->external_port, + l_addr, + mp->local.port, + proto, + clib_net_to_host_u32 (mp-> + local.vrf_id), + mp->local.probability, mp->is_add); + + REPLY_MACRO (VL_API_NAT44_LB_STATIC_MAPPING_ADD_DEL_LOCAL_REPLY); +} + +static void +send_nat44_lb_static_mapping_details (snat_static_mapping_t * m, + vl_api_registration_t * reg, + u32 context) +{ + vl_api_nat44_lb_static_mapping_details_t *rmp; + 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) + + (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); + + clib_memcpy (rmp->external_addr, &(m->external_addr), 4); + rmp->external_port = m->external_port; + rmp->protocol = nat_proto_to_ip_proto (m->proto); + rmp->context = context; + + if (m->twice_nat == TWICE_NAT) + rmp->flags |= NAT_API_IS_TWICE_NAT; + else if (m->twice_nat == TWICE_NAT_SELF) + rmp->flags |= NAT_API_IS_SELF_TWICE_NAT; + if (is_out2in_only_static_mapping (m)) + rmp->flags |= NAT_API_IS_OUT2IN_ONLY; + if (m->tag) + strncpy ((char *) rmp->tag, (char *) m->tag, vec_len (m->tag)); + + locals = (vl_api_nat44_lb_addr_port_t *) rmp->locals; + pool_foreach (ap, m->locals) + { + clib_memcpy (locals->addr, &(ap->addr), 4); + locals->port = ap->port; + locals->probability = ap->probability; + locals->vrf_id = ntohl (ap->vrf_id); + locals++; + local_num++; + } + rmp->local_num = ntohl (local_num); + + vl_api_send_msg (reg, (u8 *) rmp); +} + +static void + vl_api_nat44_lb_static_mapping_dump_t_handler + (vl_api_nat44_lb_static_mapping_dump_t * mp) +{ + vl_api_registration_t *reg; + snat_main_t *sm = &snat_main; + snat_static_mapping_t *m; + + reg = vl_api_client_index_to_registration (mp->client_index); + if (!reg) + return; + + pool_foreach (m, sm->static_mappings) + { + if (is_lb_static_mapping(m)) + send_nat44_lb_static_mapping_details (m, reg, mp->context); + } +} + +static void +vl_api_nat44_del_session_t_handler (vl_api_nat44_del_session_t * mp) +{ + snat_main_t *sm = &snat_main; + vl_api_nat44_del_session_reply_t *rmp; + ip4_address_t addr, eh_addr; + u16 port, eh_port; + u32 vrf_id; + int rv = 0; + u8 is_in; + + memcpy (&addr.as_u8, mp->address, 4); + port = mp->port; + vrf_id = clib_net_to_host_u32 (mp->vrf_id); + memcpy (&eh_addr.as_u8, mp->ext_host_address, 4); + eh_port = mp->ext_host_port; + + is_in = mp->flags & NAT_API_IS_INSIDE; + + rv = nat44_del_ed_session (sm, &addr, port, &eh_addr, eh_port, mp->protocol, + vrf_id, is_in); + + REPLY_MACRO (VL_API_NAT44_DEL_SESSION_REPLY); +} + +static void + vl_api_nat44_forwarding_enable_disable_t_handler + (vl_api_nat44_forwarding_enable_disable_t * mp) +{ + vl_api_nat44_forwarding_enable_disable_reply_t *rmp; + snat_main_t *sm = &snat_main; + int rv = 0; + nat44_ed_forwarding_enable_disable (mp->enable); + REPLY_MACRO (VL_API_NAT44_FORWARDING_ENABLE_DISABLE_REPLY); +} + +static void + vl_api_nat44_forwarding_is_enabled_t_handler + (vl_api_nat44_forwarding_is_enabled_t * mp) +{ + vl_api_registration_t *reg; + snat_main_t *sm = &snat_main; + vl_api_nat44_forwarding_is_enabled_reply_t *rmp; + + reg = vl_api_client_index_to_registration (mp->client_index); + if (!reg) + return; + + rmp = vl_msg_api_alloc (sizeof (*rmp)); + clib_memset (rmp, 0, sizeof (*rmp)); + rmp->_vl_msg_id = + ntohs (VL_API_NAT44_FORWARDING_IS_ENABLED_REPLY + sm->msg_id_base); + rmp->context = mp->context; + + rmp->enabled = sm->forwarding_enabled; + + vl_api_send_msg (reg, (u8 *) rmp); +} + +static void +vl_api_nat44_ed_set_fq_options_t_handler (vl_api_nat44_ed_set_fq_options_t *mp) +{ + snat_main_t *sm = &snat_main; + vl_api_nat44_ed_set_fq_options_reply_t *rmp; + int rv = 0; + u32 frame_queue_nelts = ntohl (mp->frame_queue_nelts); + rv = nat44_ed_set_frame_queue_nelts (frame_queue_nelts); + REPLY_MACRO (VL_API_NAT44_ED_SET_FQ_OPTIONS_REPLY); +} + +static void +vl_api_nat44_ed_show_fq_options_t_handler ( + vl_api_nat44_ed_show_fq_options_t *mp) +{ + snat_main_t *sm = &snat_main; + vl_api_nat44_ed_show_fq_options_reply_t *rmp; + int rv = 0; + /* clang-format off */ + REPLY_MACRO2_ZERO (VL_API_NAT44_ED_SHOW_FQ_OPTIONS_REPLY, + ({ + rmp->frame_queue_nelts = htonl (sm->frame_queue_nelts); + })); + /* clang-format on */ +} + +/* API definitions */ +#include +#include + +/* Set up the API message handling tables */ +clib_error_t * +nat44_api_hookup (vlib_main_t * vm) +{ + snat_main_t *sm = &snat_main; + sm->msg_id_base = setup_message_id_table (); + return 0; +} + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/src/plugins/nat/nat44-ed/nat44_ed_classify.c b/src/plugins/nat/nat44-ed/nat44_ed_classify.c new file mode 100644 index 00000000000..5a9f4e42657 --- /dev/null +++ b/src/plugins/nat/nat44-ed/nat44_ed_classify.c @@ -0,0 +1,362 @@ +/* + * Copyright (c) 2018 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file + * @brief Classify for one armed NAT44 (in+out interface) + */ + +#include +#include +#include + +#include +#include + +#define foreach_nat44_classify_error \ +_(NEXT_IN2OUT, "next in2out") \ +_(NEXT_OUT2IN, "next out2in") \ +_(FRAG_CACHED, "fragment cached") + +typedef enum +{ +#define _(sym,str) NAT44_CLASSIFY_ERROR_##sym, + foreach_nat44_classify_error +#undef _ + NAT44_CLASSIFY_N_ERROR, +} nat44_classify_error_t; + +typedef enum +{ + NAT44_CLASSIFY_NEXT_IN2OUT, + NAT44_CLASSIFY_NEXT_OUT2IN, + NAT44_CLASSIFY_NEXT_DROP, + NAT44_CLASSIFY_N_NEXT, +} nat44_classify_next_t; + +typedef struct +{ + u8 next_in2out; + u8 cached; +} nat44_classify_trace_t; + +static u8 * +format_nat44_classify_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 *); + nat44_classify_trace_t *t = va_arg (*args, nat44_classify_trace_t *); + char *next; + + if (t->cached) + s = format (s, "nat44-classify: fragment cached"); + else + { + next = t->next_in2out ? "nat44-ed-in2out" : "nat44-ed-out2in"; + s = format (s, "nat44-classify: next %s", next); + } + + return s; +} + +static inline uword +nat44_handoff_classify_node_fn_inline (vlib_main_t * vm, + vlib_node_runtime_t * node, + vlib_frame_t * frame) +{ + u32 n_left_from, *from, *to_next; + nat44_classify_next_t next_index; + snat_main_t *sm = &snat_main; + snat_static_mapping_t *m; + u32 next_in2out = 0, next_out2in = 0; + + from = vlib_frame_vector_args (frame); + n_left_from = frame->n_vectors; + next_index = node->cached_next_index; + + while (n_left_from > 0) + { + u32 n_left_to_next; + + vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next); + + while (n_left_from > 0 && n_left_to_next > 0) + { + u32 bi0; + vlib_buffer_t *b0; + u32 next0 = NAT_NEXT_IN2OUT_CLASSIFY; + ip4_header_t *ip0; + snat_address_t *ap; + clib_bihash_kv_8_8_t kv0, value0; + + /* speculatively enqueue b0 to the current next frame */ + bi0 = from[0]; + to_next[0] = bi0; + from += 1; + to_next += 1; + n_left_from -= 1; + n_left_to_next -= 1; + + b0 = vlib_get_buffer (vm, bi0); + ip0 = vlib_buffer_get_current (b0); + + vec_foreach (ap, sm->addresses) + { + if (ip0->dst_address.as_u32 == ap->addr.as_u32) + { + next0 = NAT_NEXT_OUT2IN_CLASSIFY; + goto enqueue0; + } + } + + if (PREDICT_FALSE (pool_elts (sm->static_mappings))) + { + init_nat_k (&kv0, ip0->dst_address, 0, 0, 0); + /* try to classify the fragment based on IP header alone */ + if (!clib_bihash_search_8_8 (&sm->static_mapping_by_external, + &kv0, &value0)) + { + m = pool_elt_at_index (sm->static_mappings, value0.value); + if (m->local_addr.as_u32 != m->external_addr.as_u32) + next0 = NAT_NEXT_OUT2IN_CLASSIFY; + goto enqueue0; + } + init_nat_k (&kv0, ip0->dst_address, + vnet_buffer (b0)->ip.reass.l4_dst_port, 0, + ip_proto_to_nat_proto (ip0->protocol)); + if (!clib_bihash_search_8_8 + (&sm->static_mapping_by_external, &kv0, &value0)) + { + m = pool_elt_at_index (sm->static_mappings, value0.value); + if (m->local_addr.as_u32 != m->external_addr.as_u32) + next0 = NAT_NEXT_OUT2IN_CLASSIFY; + } + } + + enqueue0: + if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE) + && (b0->flags & VLIB_BUFFER_IS_TRACED))) + { + nat44_classify_trace_t *t = + vlib_add_trace (vm, node, b0, sizeof (*t)); + t->cached = 0; + t->next_in2out = next0 == NAT_NEXT_IN2OUT_CLASSIFY ? 1 : 0; + } + + next_in2out += next0 == NAT_NEXT_IN2OUT_CLASSIFY; + next_out2in += next0 == NAT_NEXT_OUT2IN_CLASSIFY; + + /* verify speculative enqueue, maybe switch current next frame */ + vlib_validate_buffer_enqueue_x1 (vm, node, next_index, + to_next, n_left_to_next, + bi0, next0); + } + + vlib_put_next_frame (vm, node, next_index, n_left_to_next); + } + + vlib_node_increment_counter (vm, node->node_index, + NAT44_CLASSIFY_ERROR_NEXT_IN2OUT, next_in2out); + vlib_node_increment_counter (vm, node->node_index, + NAT44_CLASSIFY_ERROR_NEXT_OUT2IN, next_out2in); + return frame->n_vectors; +} + +static inline uword +nat44_ed_classify_node_fn_inline (vlib_main_t * vm, + vlib_node_runtime_t * node, + vlib_frame_t * frame) +{ + u32 n_left_from, *from, *to_next; + nat44_classify_next_t next_index; + snat_main_t *sm = &snat_main; + snat_static_mapping_t *m; + u32 next_in2out = 0, next_out2in = 0; + + from = vlib_frame_vector_args (frame); + n_left_from = frame->n_vectors; + next_index = node->cached_next_index; + + while (n_left_from > 0) + { + u32 n_left_to_next; + + vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next); + + while (n_left_from > 0 && n_left_to_next > 0) + { + u32 bi0; + vlib_buffer_t *b0; + u32 next0 = NAT_NEXT_IN2OUT_ED_FAST_PATH; + u32 sw_if_index0, rx_fib_index0; + ip4_header_t *ip0; + snat_address_t *ap; + clib_bihash_kv_8_8_t kv0, value0; + clib_bihash_kv_16_8_t ed_kv0, ed_value0; + + /* speculatively enqueue b0 to the current next frame */ + bi0 = from[0]; + to_next[0] = bi0; + from += 1; + to_next += 1; + n_left_from -= 1; + n_left_to_next -= 1; + + b0 = vlib_get_buffer (vm, bi0); + ip0 = vlib_buffer_get_current (b0); + + u32 arc_next; + vnet_feature_next (&arc_next, b0); + vnet_buffer2 (b0)->nat.arc_next = arc_next; + + if (ip0->protocol != IP_PROTOCOL_ICMP) + { + /* process leading fragment/whole packet (with L4 header) */ + sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX]; + rx_fib_index0 = + fib_table_get_index_for_sw_if_index (FIB_PROTOCOL_IP4, + sw_if_index0); + init_ed_k (&ed_kv0, ip0->src_address, + vnet_buffer (b0)->ip.reass.l4_src_port, + ip0->dst_address, + vnet_buffer (b0)->ip.reass.l4_dst_port, + rx_fib_index0, ip0->protocol); + /* process whole packet */ + if (!clib_bihash_search_16_8 (&sm->flow_hash, &ed_kv0, + &ed_value0)) + { + ASSERT (vm->thread_index == + ed_value_get_thread_index (&ed_value0)); + snat_main_per_thread_data_t *tsm = + &sm->per_thread_data[vm->thread_index]; + snat_session_t *s = pool_elt_at_index ( + tsm->sessions, ed_value_get_session_index (&ed_value0)); + clib_bihash_kv_16_8_t i2o_kv; + nat_6t_flow_to_ed_k (&i2o_kv, &s->i2o); + vnet_buffer2 (b0)->nat.cached_session_index = + ed_value_get_session_index (&ed_value0); + if (i2o_kv.key[0] == ed_kv0.key[0] && + i2o_kv.key[1] == ed_kv0.key[1]) + { + next0 = NAT_NEXT_IN2OUT_ED_FAST_PATH; + } + else + { + next0 = NAT_NEXT_OUT2IN_ED_FAST_PATH; + } + + goto enqueue0; + } + /* session doesn't exist so continue in code */ + } + + vec_foreach (ap, sm->addresses) + { + if (ip0->dst_address.as_u32 == ap->addr.as_u32) + { + next0 = NAT_NEXT_OUT2IN_ED_FAST_PATH; + goto enqueue0; + } + } + + if (PREDICT_FALSE (pool_elts (sm->static_mappings))) + { + init_nat_k (&kv0, ip0->dst_address, 0, 0, 0); + /* try to classify the fragment based on IP header alone */ + if (!clib_bihash_search_8_8 (&sm->static_mapping_by_external, + &kv0, &value0)) + { + m = pool_elt_at_index (sm->static_mappings, value0.value); + if (m->local_addr.as_u32 != m->external_addr.as_u32) + next0 = NAT_NEXT_OUT2IN_ED_FAST_PATH; + goto enqueue0; + } + init_nat_k (&kv0, ip0->dst_address, + vnet_buffer (b0)->ip.reass.l4_dst_port, 0, + ip_proto_to_nat_proto (ip0->protocol)); + if (!clib_bihash_search_8_8 + (&sm->static_mapping_by_external, &kv0, &value0)) + { + m = pool_elt_at_index (sm->static_mappings, value0.value); + if (m->local_addr.as_u32 != m->external_addr.as_u32) + next0 = NAT_NEXT_OUT2IN_ED_FAST_PATH; + } + } + + enqueue0: + if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE) + && (b0->flags & VLIB_BUFFER_IS_TRACED))) + { + nat44_classify_trace_t *t = + vlib_add_trace (vm, node, b0, sizeof (*t)); + t->cached = 0; + t->next_in2out = next0 == NAT_NEXT_IN2OUT_ED_FAST_PATH ? 1 : 0; + } + + next_in2out += next0 == NAT_NEXT_IN2OUT_ED_FAST_PATH; + next_out2in += next0 == NAT_NEXT_OUT2IN_ED_FAST_PATH; + + /* verify speculative enqueue, maybe switch current next frame */ + vlib_validate_buffer_enqueue_x1 (vm, node, next_index, + to_next, n_left_to_next, + bi0, next0); + } + + vlib_put_next_frame (vm, node, next_index, n_left_to_next); + } + + vlib_node_increment_counter (vm, node->node_index, + NAT44_CLASSIFY_ERROR_NEXT_IN2OUT, next_in2out); + vlib_node_increment_counter (vm, node->node_index, + NAT44_CLASSIFY_ERROR_NEXT_OUT2IN, next_out2in); + return frame->n_vectors; +} + +VLIB_NODE_FN (nat44_ed_classify_node) (vlib_main_t * vm, + vlib_node_runtime_t * node, + vlib_frame_t * frame) +{ + return nat44_ed_classify_node_fn_inline (vm, node, frame); +} + +VLIB_REGISTER_NODE (nat44_ed_classify_node) = { + .name = "nat44-ed-classify", + .vector_size = sizeof (u32), + .sibling_of = "nat-default", + .format_trace = format_nat44_classify_trace, + .type = VLIB_NODE_TYPE_INTERNAL, +}; + +VLIB_NODE_FN (nat44_handoff_classify_node) (vlib_main_t * vm, + vlib_node_runtime_t * node, + vlib_frame_t * frame) +{ + return nat44_handoff_classify_node_fn_inline (vm, node, frame); +} + +VLIB_REGISTER_NODE (nat44_handoff_classify_node) = { + .name = "nat44-handoff-classify", + .vector_size = sizeof (u32), + .sibling_of = "nat-default", + .format_trace = format_nat44_classify_trace, + .type = VLIB_NODE_TYPE_INTERNAL, +}; + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/src/plugins/nat/nat44-ed/nat44_ed_cli.c b/src/plugins/nat/nat44-ed/nat44_ed_cli.c new file mode 100644 index 00000000000..714b410bbd3 --- /dev/null +++ b/src/plugins/nat/nat44-ed/nat44_ed_cli.c @@ -0,0 +1,2029 @@ +/* + * Copyright (c) 2018 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file + * @brief NAT44 CLI + */ + +#include + +#include +#include +#include + +#include +#include +#include + +static clib_error_t * +nat44_enable_command_fn (vlib_main_t * vm, + unformat_input_t * input, vlib_cli_command_t * cmd) +{ + snat_main_t *sm = &snat_main; + unformat_input_t _line_input, *line_input = &_line_input; + clib_error_t *error = 0; + + nat44_config_t c = { 0 }; + u8 mode_set = 0; + + if (sm->enabled) + return clib_error_return (0, "nat44 already enabled"); + + /* Get a line of input. */ + if (!unformat_user (input, unformat_line_input, line_input)) + { + if (nat44_plugin_enable (c) != 0) + return clib_error_return (0, "nat44 enable failed"); + return 0; + } + + while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT) + { + if (!mode_set && unformat (line_input, "static-mapping-only")) + { + mode_set = 1; + c.static_mapping_only = 1; + if (unformat (line_input, "connection-tracking")) + { + c.connection_tracking = 1; + } + } + else if (unformat (line_input, "inside-vrf %u", &c.inside_vrf)); + else if (unformat (line_input, "outside-vrf %u", &c.outside_vrf)); + else if (unformat (line_input, "sessions %u", &c.sessions)); + else + { + error = clib_error_return (0, "unknown input '%U'", + format_unformat_error, line_input); + goto done; + } + } + + if (!c.sessions) + { + error = clib_error_return (0, "number of sessions is required"); + goto done; + } + + if (nat44_plugin_enable (c) != 0) + error = clib_error_return (0, "nat44 enable failed"); +done: + unformat_free (line_input); + return error; +} + +static clib_error_t * +nat44_disable_command_fn (vlib_main_t * vm, + unformat_input_t * input, vlib_cli_command_t * cmd) +{ + snat_main_t *sm = &snat_main; + clib_error_t *error = 0; + + if (!sm->enabled) + return clib_error_return (0, "nat44 already disabled"); + + if (nat44_plugin_disable () != 0) + error = clib_error_return (0, "nat44 disable failed"); + + return error; +} + +static clib_error_t * +set_workers_command_fn (vlib_main_t * vm, + unformat_input_t * input, vlib_cli_command_t * cmd) +{ + unformat_input_t _line_input, *line_input = &_line_input; + uword *bitmap = 0; + int rv = 0; + clib_error_t *error = 0; + + /* 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, "%U", unformat_bitmap_list, &bitmap)) + ; + else + { + error = clib_error_return (0, "unknown input '%U'", + format_unformat_error, line_input); + goto done; + } + } + + if (bitmap == 0) + { + error = clib_error_return (0, "List of workers must be specified."); + goto done; + } + + rv = snat_set_workers (bitmap); + + clib_bitmap_free (bitmap); + + switch (rv) + { + case VNET_API_ERROR_INVALID_WORKER: + error = clib_error_return (0, "Invalid worker(s)."); + goto done; + case VNET_API_ERROR_FEATURE_DISABLED: + error = clib_error_return (0, + "Supported only if 2 or more workes available."); + goto done; + default: + break; + } + +done: + unformat_free (line_input); + + return error; +} + +static clib_error_t * +nat_show_workers_commnad_fn (vlib_main_t * vm, unformat_input_t * input, + vlib_cli_command_t * cmd) +{ + snat_main_t *sm = &snat_main; + u32 *worker; + + if (sm->num_workers > 1) + { + vlib_cli_output (vm, "%d workers", vec_len (sm->workers)); + vec_foreach (worker, sm->workers) + { + vlib_worker_thread_t *w = + vlib_worker_threads + *worker + sm->first_worker_index; + vlib_cli_output (vm, " %s", w->name); + } + } + + return 0; +} + +static clib_error_t * +snat_set_log_level_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; + u8 log_level = NAT_LOG_NONE; + clib_error_t *error = 0; + + /* Get a line of input. */ + if (!unformat_user (input, unformat_line_input, line_input)) + return 0; + + if (!unformat (line_input, "%d", &log_level)) + { + error = clib_error_return (0, "unknown input '%U'", + format_unformat_error, line_input); + goto done; + } + if (log_level > NAT_LOG_DEBUG) + { + error = clib_error_return (0, "unknown logging level '%d'", log_level); + goto done; + } + sm->log_level = log_level; + +done: + unformat_free (line_input); + + return error; +} + +static clib_error_t * +snat_ipfix_logging_enable_disable_command_fn (vlib_main_t * vm, + unformat_input_t * input, + vlib_cli_command_t * cmd) +{ + unformat_input_t _line_input, *line_input = &_line_input; + u32 domain_id = 0; + u32 src_port = 0; + u8 enable = 1; + int rv = 0; + clib_error_t *error = 0; + + /* Get a line of input. */ + if (!unformat_user (input, unformat_line_input, line_input)) + { + rv = nat_ipfix_logging_enable_disable (enable, domain_id, + (u16) src_port); + if (rv) + return clib_error_return (0, "ipfix logging enable failed"); + return 0; + } + + while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT) + { + if (unformat (line_input, "domain %d", &domain_id)) + ; + else if (unformat (line_input, "src-port %d", &src_port)) + ; + else if (unformat (line_input, "disable")) + enable = 0; + else + { + error = clib_error_return (0, "unknown input '%U'", + format_unformat_error, line_input); + goto done; + } + } + + rv = nat_ipfix_logging_enable_disable (enable, domain_id, (u16) src_port); + + if (rv) + { + error = clib_error_return (0, "ipfix logging enable failed"); + goto done; + } + +done: + unformat_free (line_input); + + return error; +} + +static clib_error_t * +nat44_show_hash_command_fn (vlib_main_t * vm, unformat_input_t * input, + vlib_cli_command_t * cmd) +{ + snat_main_t *sm = &snat_main; + nat_affinity_main_t *nam = &nat_affinity_main; + int i; + int verbose = 0; + + if (unformat (input, "detail")) + verbose = 1; + else if (unformat (input, "verbose")) + verbose = 2; + + vlib_cli_output (vm, "%U", format_bihash_8_8, &sm->static_mapping_by_local, + verbose); + vlib_cli_output (vm, "%U", + format_bihash_8_8, &sm->static_mapping_by_external, + verbose); + vlib_cli_output (vm, "%U", format_bihash_16_8, &sm->flow_hash, verbose); + vec_foreach_index (i, sm->per_thread_data) + { + vlib_cli_output (vm, "-------- thread %d %s --------\n", + i, vlib_worker_threads[i].name); + vlib_cli_output (vm, "%U", format_bihash_16_8, &sm->flow_hash, verbose); + } + + vlib_cli_output (vm, "%U", format_bihash_16_8, &nam->affinity_hash, + verbose); + + vlib_cli_output (vm, "-------- hash table parameters --------\n"); + vlib_cli_output (vm, "translation buckets: %u", sm->translation_buckets); + return 0; +} + +static clib_error_t * +nat_set_mss_clamping_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; + u32 mss; + + /* 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, "disable")) + sm->mss_clamping = 0; + else if (unformat (line_input, "%d", &mss)) + sm->mss_clamping = (u16) mss; + else + { + error = clib_error_return (0, "unknown input '%U'", + format_unformat_error, line_input); + goto done; + } + } + +done: + unformat_free (line_input); + + return error; +} + +static clib_error_t * +nat_show_mss_clamping_command_fn (vlib_main_t * vm, unformat_input_t * input, + vlib_cli_command_t * cmd) +{ + snat_main_t *sm = &snat_main; + + if (sm->mss_clamping) + vlib_cli_output (vm, "mss-clamping %d", sm->mss_clamping); + else + vlib_cli_output (vm, "mss-clamping disabled"); + + return 0; +} + +static clib_error_t * +add_address_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; + ip4_address_t start_addr, end_addr, this_addr; + u32 start_host_order, end_host_order; + u32 vrf_id = ~0; + int i, count; + int is_add = 1; + int rv = 0; + clib_error_t *error = 0; + u8 twice_nat = 0; + + /* 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, "%U - %U", + unformat_ip4_address, &start_addr, + unformat_ip4_address, &end_addr)) + ; + else if (unformat (line_input, "tenant-vrf %u", &vrf_id)) + ; + else if (unformat (line_input, "%U", unformat_ip4_address, &start_addr)) + end_addr = start_addr; + else if (unformat (line_input, "twice-nat")) + twice_nat = 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 (sm->static_mapping_only) + { + error = clib_error_return (0, "static mapping only mode"); + goto done; + } + + start_host_order = clib_host_to_net_u32 (start_addr.as_u32); + end_host_order = clib_host_to_net_u32 (end_addr.as_u32); + + if (end_host_order < start_host_order) + { + error = clib_error_return (0, "end address less than start address"); + goto done; + } + + count = (end_host_order - start_host_order) + 1; + + if (count > 1024) + nat_log_info ("%U - %U, %d addresses...", + format_ip4_address, &start_addr, + format_ip4_address, &end_addr, count); + + this_addr = start_addr; + + for (i = 0; i < count; i++) + { + if (is_add) + rv = snat_add_address (sm, &this_addr, vrf_id, twice_nat); + else + rv = snat_del_address (sm, this_addr, 0, twice_nat); + + switch (rv) + { + case VNET_API_ERROR_VALUE_EXIST: + error = clib_error_return (0, "NAT address already in use."); + goto done; + case VNET_API_ERROR_NO_SUCH_ENTRY: + error = clib_error_return (0, "NAT address not exist."); + goto done; + case VNET_API_ERROR_UNSPECIFIED: + error = + clib_error_return (0, "NAT address used in static mapping."); + goto done; + default: + break; + } + + increment_v4_address (&this_addr); + } + +done: + unformat_free (line_input); + + return error; +} + +static void +nat44_show_lru_summary (vlib_main_t * vm, snat_main_per_thread_data_t * tsm, + u64 now, u64 sess_timeout_time) +{ + snat_main_t *sm = &snat_main; + dlist_elt_t *oldest_elt; + snat_session_t *s; + u32 oldest_index; + +#define _(n, d) \ + oldest_index = \ + clib_dlist_remove_head (tsm->lru_pool, tsm->n##_lru_head_index); \ + if (~0 != oldest_index) \ + { \ + oldest_elt = pool_elt_at_index (tsm->lru_pool, oldest_index); \ + s = pool_elt_at_index (tsm->sessions, oldest_elt->value); \ + sess_timeout_time = \ + s->last_heard + (f64)nat44_session_get_timeout (sm, s); \ + vlib_cli_output (vm, d " LRU min session timeout %llu (now %llu)", \ + sess_timeout_time, now); \ + clib_dlist_addhead (tsm->lru_pool, tsm->n##_lru_head_index, \ + oldest_index); \ + } + _(tcp_estab, "established tcp"); + _(tcp_trans, "transitory tcp"); + _(udp, "udp"); + _(unk_proto, "unknown protocol"); + _(icmp, "icmp"); +#undef _ +} + +static clib_error_t * +nat44_show_summary_command_fn (vlib_main_t * vm, unformat_input_t * input, + vlib_cli_command_t * cmd) +{ + snat_main_per_thread_data_t *tsm; + snat_main_t *sm = &snat_main; + snat_session_t *s; + + u32 count = 0; + + u64 now = vlib_time_now (vm); + u64 sess_timeout_time = 0; + + u32 udp_sessions = 0; + u32 tcp_sessions = 0; + u32 icmp_sessions = 0; + + u32 timed_out = 0; + u32 transitory = 0; + u32 transitory_wait_closed = 0; + u32 transitory_closed = 0; + u32 established = 0; + + u32 fib; + + for (fib = 0; fib < vec_len (sm->max_translations_per_fib); fib++) + vlib_cli_output (vm, "max translations per thread: %u fib %u", + sm->max_translations_per_fib[fib], fib); + + if (sm->num_workers > 1) + { + vec_foreach (tsm, sm->per_thread_data) + { + pool_foreach (s, tsm->sessions) + { + sess_timeout_time = s->last_heard + + (f64) nat44_session_get_timeout (sm, s); + if (now >= sess_timeout_time) + timed_out++; + + switch (s->nat_proto) + { + case NAT_PROTOCOL_ICMP: + icmp_sessions++; + break; + case NAT_PROTOCOL_TCP: + tcp_sessions++; + if (s->state) + { + if (s->tcp_closed_timestamp) + { + if (now >= s->tcp_closed_timestamp) + { + ++transitory_closed; + } + else + { + ++transitory_wait_closed; + } + } + transitory++; + } + else + established++; + break; + case NAT_PROTOCOL_UDP: + default: + udp_sessions++; + break; + } + } + nat44_show_lru_summary (vm, tsm, now, sess_timeout_time); + count += pool_elts (tsm->sessions); + } + } + else + { + tsm = vec_elt_at_index (sm->per_thread_data, sm->num_workers); + pool_foreach (s, tsm->sessions) + { + sess_timeout_time = s->last_heard + + (f64) nat44_session_get_timeout (sm, s); + if (now >= sess_timeout_time) + timed_out++; + + switch (s->nat_proto) + { + case NAT_PROTOCOL_ICMP: + icmp_sessions++; + break; + case NAT_PROTOCOL_TCP: + tcp_sessions++; + if (s->state) + { + if (s->tcp_closed_timestamp) + { + if (now >= s->tcp_closed_timestamp) + { + ++transitory_closed; + } + else + { + ++transitory_wait_closed; + } + } + transitory++; + } + else + established++; + break; + case NAT_PROTOCOL_UDP: + default: + udp_sessions++; + break; + } + } + nat44_show_lru_summary (vm, tsm, now, sess_timeout_time); + count = pool_elts (tsm->sessions); + } + + vlib_cli_output (vm, "total timed out sessions: %u", timed_out); + vlib_cli_output (vm, "total sessions: %u", count); + vlib_cli_output (vm, "total tcp sessions: %u", tcp_sessions); + vlib_cli_output (vm, "total tcp established sessions: %u", established); + vlib_cli_output (vm, "total tcp transitory sessions: %u", transitory); + vlib_cli_output (vm, "total tcp transitory (WAIT-CLOSED) sessions: %u", + transitory_wait_closed); + vlib_cli_output (vm, "total tcp transitory (CLOSED) sessions: %u", + transitory_closed); + vlib_cli_output (vm, "total udp sessions: %u", udp_sessions); + vlib_cli_output (vm, "total icmp sessions: %u", icmp_sessions); + return 0; +} + +static clib_error_t * +nat44_show_addresses_command_fn (vlib_main_t * vm, unformat_input_t * input, + vlib_cli_command_t * cmd) +{ + snat_main_t *sm = &snat_main; + snat_address_t *ap; + + vlib_cli_output (vm, "NAT44 pool addresses:"); + vec_foreach (ap, sm->addresses) + { + vlib_cli_output (vm, "%U", format_ip4_address, &ap->addr); + if (ap->fib_index != ~0) + vlib_cli_output (vm, " tenant VRF: %u", + fib_table_get(ap->fib_index, FIB_PROTOCOL_IP4)->ft_table_id); + else + vlib_cli_output (vm, " tenant VRF independent"); + #define _(N, i, n, s) \ + vlib_cli_output (vm, " %d busy %s ports", ap->busy_##n##_ports, s); + foreach_nat_protocol + #undef _ + } + vlib_cli_output (vm, "NAT44 twice-nat pool addresses:"); + vec_foreach (ap, sm->twice_nat_addresses) + { + vlib_cli_output (vm, "%U", format_ip4_address, &ap->addr); + if (ap->fib_index != ~0) + vlib_cli_output (vm, " tenant VRF: %u", + fib_table_get(ap->fib_index, FIB_PROTOCOL_IP4)->ft_table_id); + else + vlib_cli_output (vm, " tenant VRF independent"); + #define _(N, i, n, s) \ + vlib_cli_output (vm, " %d busy %s ports", ap->busy_##n##_ports, s); + foreach_nat_protocol + #undef _ + } + return 0; +} + +static clib_error_t * +snat_feature_command_fn (vlib_main_t * vm, + unformat_input_t * input, vlib_cli_command_t * cmd) +{ + unformat_input_t _line_input, *line_input = &_line_input; + vnet_main_t *vnm = vnet_get_main (); + clib_error_t *error = 0; + u32 sw_if_index; + u32 *inside_sw_if_indices = 0; + u32 *outside_sw_if_indices = 0; + u8 is_output_feature = 0; + int is_del = 0; + int i; + + sw_if_index = ~0; + + /* 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, "in %U", unformat_vnet_sw_interface, + vnm, &sw_if_index)) + vec_add1 (inside_sw_if_indices, sw_if_index); + else if (unformat (line_input, "out %U", unformat_vnet_sw_interface, + vnm, &sw_if_index)) + vec_add1 (outside_sw_if_indices, sw_if_index); + else if (unformat (line_input, "output-feature")) + is_output_feature = 1; + else if (unformat (line_input, "del")) + is_del = 1; + else + { + error = clib_error_return (0, "unknown input '%U'", + format_unformat_error, line_input); + goto done; + } + } + + if (vec_len (inside_sw_if_indices)) + { + for (i = 0; i < vec_len (inside_sw_if_indices); i++) + { + sw_if_index = inside_sw_if_indices[i]; + if (is_output_feature) + { + if (snat_interface_add_del_output_feature + (sw_if_index, 1, is_del)) + { + error = clib_error_return (0, "%s %U failed", + is_del ? "del" : "add", + format_vnet_sw_if_index_name, + vnm, sw_if_index); + goto done; + } + } + else + { + if (snat_interface_add_del (sw_if_index, 1, is_del)) + { + error = clib_error_return (0, "%s %U failed", + is_del ? "del" : "add", + format_vnet_sw_if_index_name, + vnm, sw_if_index); + goto done; + } + } + } + } + + if (vec_len (outside_sw_if_indices)) + { + for (i = 0; i < vec_len (outside_sw_if_indices); i++) + { + sw_if_index = outside_sw_if_indices[i]; + if (is_output_feature) + { + if (snat_interface_add_del_output_feature + (sw_if_index, 0, is_del)) + { + error = clib_error_return (0, "%s %U failed", + is_del ? "del" : "add", + format_vnet_sw_if_index_name, + vnm, sw_if_index); + goto done; + } + } + else + { + if (snat_interface_add_del (sw_if_index, 0, is_del)) + { + error = clib_error_return (0, "%s %U failed", + is_del ? "del" : "add", + format_vnet_sw_if_index_name, + vnm, sw_if_index); + goto done; + } + } + } + } + +done: + unformat_free (line_input); + vec_free (inside_sw_if_indices); + vec_free (outside_sw_if_indices); + + return error; +} + +static clib_error_t * +nat44_show_interfaces_command_fn (vlib_main_t * vm, unformat_input_t * input, + vlib_cli_command_t * cmd) +{ + snat_main_t *sm = &snat_main; + snat_interface_t *i; + vnet_main_t *vnm = vnet_get_main (); + + vlib_cli_output (vm, "NAT44 interfaces:"); + pool_foreach (i, sm->interfaces) + { + vlib_cli_output (vm, " %U %s", format_vnet_sw_if_index_name, vnm, + i->sw_if_index, + (nat_interface_is_inside(i) && + nat_interface_is_outside(i)) ? "in out" : + (nat_interface_is_inside(i) ? "in" : "out")); + } + + pool_foreach (i, sm->output_feature_interfaces) + { + vlib_cli_output (vm, " %U output-feature %s", + format_vnet_sw_if_index_name, vnm, + i->sw_if_index, + (nat_interface_is_inside(i) && + nat_interface_is_outside(i)) ? "in out" : + (nat_interface_is_inside(i) ? "in" : "out")); + } + + return 0; +} + +static clib_error_t * +add_static_mapping_command_fn (vlib_main_t * vm, + unformat_input_t * input, + vlib_cli_command_t * cmd) +{ + unformat_input_t _line_input, *line_input = &_line_input; + clib_error_t *error = 0; + ip4_address_t l_addr, e_addr, exact_addr; + u32 l_port = 0, e_port = 0, vrf_id = ~0; + int is_add = 1, addr_only = 1, rv, exact = 0; + u32 sw_if_index = ~0; + vnet_main_t *vnm = vnet_get_main (); + nat_protocol_t proto = NAT_PROTOCOL_OTHER; + u8 proto_set = 0; + twice_nat_type_t twice_nat = TWICE_NAT_DISABLED; + u8 out2in_only = 0; + + /* 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", unformat_ip4_address, &l_addr, + &l_port)) + addr_only = 0; + else + if (unformat (line_input, "local %U", unformat_ip4_address, &l_addr)) + ; + else if (unformat (line_input, "external %U %u", unformat_ip4_address, + &e_addr, &e_port)) + addr_only = 0; + else if (unformat (line_input, "external %U", unformat_ip4_address, + &e_addr)) + ; + else if (unformat (line_input, "external %U %u", + unformat_vnet_sw_interface, vnm, &sw_if_index, + &e_port)) + addr_only = 0; + else if (unformat (line_input, "external %U", + unformat_vnet_sw_interface, vnm, &sw_if_index)) + ; + else if (unformat (line_input, "exact %U", unformat_ip4_address, + &exact_addr)) + exact = 1; + else if (unformat (line_input, "vrf %u", &vrf_id)) + ; + else if (unformat (line_input, "%U", unformat_nat_protocol, &proto)) + proto_set = 1; + else if (unformat (line_input, "twice-nat")) + twice_nat = TWICE_NAT; + else if (unformat (line_input, "self-twice-nat")) + twice_nat = TWICE_NAT_SELF; + else if (unformat (line_input, "out2in-only")) + out2in_only = 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 (twice_nat && addr_only) + { + error = clib_error_return (0, "twice NAT only for 1:1 NAPT"); + goto done; + } + + if (addr_only) + { + if (proto_set) + { + error = + clib_error_return (0, + "address only mapping doesn't support protocol"); + goto done; + } + } + else if (!proto_set) + { + error = clib_error_return (0, "protocol is required"); + goto done; + } + + rv = snat_add_static_mapping ( + l_addr, e_addr, clib_host_to_net_u16 (l_port), + clib_host_to_net_u16 (e_port), vrf_id, addr_only, sw_if_index, proto, + is_add, twice_nat, out2in_only, 0, 0, exact_addr, exact); + + switch (rv) + { + case VNET_API_ERROR_INVALID_VALUE: + error = clib_error_return (0, "External port already in use."); + goto done; + case VNET_API_ERROR_NO_SUCH_ENTRY: + if (is_add) + error = clib_error_return (0, "External address must be allocated."); + else + error = clib_error_return (0, "Mapping not exist."); + goto done; + case VNET_API_ERROR_NO_SUCH_FIB: + error = clib_error_return (0, "No such VRF id."); + goto done; + case VNET_API_ERROR_VALUE_EXIST: + error = clib_error_return (0, "Mapping already exist."); + goto done; + default: + break; + } + +done: + unformat_free (line_input); + + return error; +} + +static clib_error_t * +add_identity_mapping_command_fn (vlib_main_t * vm, + unformat_input_t * input, + vlib_cli_command_t * cmd) +{ + unformat_input_t _line_input, *line_input = &_line_input; + clib_error_t *error = 0; + ip4_address_t addr, pool_addr = { 0 }; + u32 port = 0, vrf_id = ~0; + int is_add = 1; + int addr_only = 1; + u32 sw_if_index = ~0; + vnet_main_t *vnm = vnet_get_main (); + int rv; + nat_protocol_t proto; + + addr.as_u32 = 0; + + /* 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, "%U", unformat_ip4_address, &addr)) + ; + else if (unformat (line_input, "external %U", + unformat_vnet_sw_interface, vnm, &sw_if_index)) + ; + else if (unformat (line_input, "vrf %u", &vrf_id)) + ; + else if (unformat (line_input, "%U %u", unformat_nat_protocol, &proto, + &port)) + addr_only = 0; + 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; + } + } + + rv = snat_add_static_mapping ( + addr, addr, clib_host_to_net_u16 (port), clib_host_to_net_u16 (port), + vrf_id, addr_only, sw_if_index, proto, is_add, 0, 0, 0, 1, pool_addr, 0); + + switch (rv) + { + case VNET_API_ERROR_INVALID_VALUE: + error = clib_error_return (0, "External port already in use."); + goto done; + case VNET_API_ERROR_NO_SUCH_ENTRY: + if (is_add) + error = clib_error_return (0, "External address must be allocated."); + else + error = clib_error_return (0, "Mapping not exist."); + goto done; + case VNET_API_ERROR_NO_SUCH_FIB: + error = clib_error_return (0, "No such VRF id."); + goto done; + case VNET_API_ERROR_VALUE_EXIST: + error = clib_error_return (0, "Mapping already exist."); + goto done; + default: + break; + } + +done: + unformat_free (line_input); + + return error; +} + +static clib_error_t * +add_lb_static_mapping_command_fn (vlib_main_t * vm, + unformat_input_t * input, + vlib_cli_command_t * cmd) +{ + unformat_input_t _line_input, *line_input = &_line_input; + clib_error_t *error = 0; + ip4_address_t l_addr, e_addr; + u32 l_port = 0, e_port = 0, vrf_id = 0, probability = 0, affinity = 0; + int is_add = 1; + int rv; + nat_protocol_t proto; + u8 proto_set = 0; + nat44_lb_addr_port_t *locals = 0, local; + twice_nat_type_t twice_nat = TWICE_NAT_DISABLED; + u8 out2in_only = 0; + + /* 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)) + { + clib_memset (&local, 0, sizeof (local)); + local.addr = l_addr; + local.port = (u16) l_port; + local.probability = (u8) probability; + vec_add1 (locals, local); + } + else if (unformat (line_input, "local %U:%u vrf %u probability %u", + unformat_ip4_address, &l_addr, &l_port, &vrf_id, + &probability)) + { + clib_memset (&local, 0, sizeof (local)); + local.addr = l_addr; + local.port = (u16) l_port; + local.probability = (u8) probability; + local.vrf_id = vrf_id; + vec_add1 (locals, local); + } + else if (unformat (line_input, "external %U:%u", unformat_ip4_address, + &e_addr, &e_port)) + ; + else if (unformat (line_input, "protocol %U", unformat_nat_protocol, + &proto)) + proto_set = 1; + else if (unformat (line_input, "twice-nat")) + twice_nat = TWICE_NAT; + else if (unformat (line_input, "self-twice-nat")) + twice_nat = TWICE_NAT_SELF; + else if (unformat (line_input, "out2in-only")) + out2in_only = 1; + else if (unformat (line_input, "del")) + is_add = 0; + else if (unformat (line_input, "affinity %u", &affinity)) + ; + else + { + error = clib_error_return (0, "unknown input: '%U'", + format_unformat_error, line_input); + goto done; + } + } + + if (vec_len (locals) < 2) + { + error = clib_error_return (0, "at least two local must be set"); + goto done; + } + + if (!proto_set) + { + error = clib_error_return (0, "missing protocol"); + goto done; + } + + rv = nat44_add_del_lb_static_mapping (e_addr, (u16) e_port, proto, locals, + is_add, twice_nat, out2in_only, 0, + affinity); + + switch (rv) + { + case VNET_API_ERROR_INVALID_VALUE: + error = clib_error_return (0, "External port already in use."); + goto done; + case VNET_API_ERROR_NO_SUCH_ENTRY: + if (is_add) + error = clib_error_return (0, "External address must be allocated."); + else + error = clib_error_return (0, "Mapping not exist."); + goto done; + case VNET_API_ERROR_VALUE_EXIST: + error = clib_error_return (0, "Mapping already exist."); + goto done; + default: + break; + } + +done: + unformat_free (line_input); + vec_free (locals); + + return error; +} + +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; + 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; + nat_protocol_t proto; + u8 proto_set = 0; + + /* 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_nat_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_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) +{ + snat_main_t *sm = &snat_main; + snat_static_mapping_t *m; + snat_static_map_resolve_t *rp; + + vlib_cli_output (vm, "NAT44 static mappings:"); + pool_foreach (m, sm->static_mappings) + { + vlib_cli_output (vm, " %U", format_snat_static_mapping, m); + } + vec_foreach (rp, sm->to_resolve) + vlib_cli_output (vm, " %U", format_snat_static_map_to_resolve, rp); + + return 0; +} + +static clib_error_t * +snat_add_interface_address_command_fn (vlib_main_t * vm, + unformat_input_t * input, + vlib_cli_command_t * cmd) +{ + snat_main_t *sm = &snat_main; + unformat_input_t _line_input, *line_input = &_line_input; + u32 sw_if_index; + int rv; + int is_del = 0; + clib_error_t *error = 0; + u8 twice_nat = 0; + + /* 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, "%U", unformat_vnet_sw_interface, + sm->vnet_main, &sw_if_index)) + ; + else if (unformat (line_input, "twice-nat")) + twice_nat = 1; + else if (unformat (line_input, "del")) + is_del = 1; + else + { + error = clib_error_return (0, "unknown input '%U'", + format_unformat_error, line_input); + goto done; + } + } + + rv = snat_add_interface_address (sm, sw_if_index, is_del, twice_nat); + + switch (rv) + { + case 0: + break; + + default: + error = clib_error_return (0, "snat_add_interface_address returned %d", + rv); + goto done; + } + +done: + unformat_free (line_input); + + return error; +} + +static clib_error_t * +nat44_show_interface_address_command_fn (vlib_main_t * vm, + unformat_input_t * input, + vlib_cli_command_t * cmd) +{ + snat_main_t *sm = &snat_main; + vnet_main_t *vnm = vnet_get_main (); + u32 *sw_if_index; + + vlib_cli_output (vm, "NAT44 pool address interfaces:"); + vec_foreach (sw_if_index, sm->auto_add_sw_if_indices) + { + vlib_cli_output (vm, " %U", format_vnet_sw_if_index_name, vnm, + *sw_if_index); + } + vlib_cli_output (vm, "NAT44 twice-nat pool address interfaces:"); + vec_foreach (sw_if_index, sm->auto_add_sw_if_indices_twice_nat) + { + vlib_cli_output (vm, " %U", format_vnet_sw_if_index_name, vnm, + *sw_if_index); + } + + return 0; +} + +static clib_error_t * +nat44_show_sessions_command_fn (vlib_main_t * vm, unformat_input_t * input, + vlib_cli_command_t * cmd) +{ + unformat_input_t _line_input, *line_input = &_line_input; + clib_error_t *error = 0; + snat_main_per_thread_data_t *tsm; + snat_main_t *sm = &snat_main; + + int i = 0; + + if (!unformat_user (input, unformat_line_input, line_input)) + goto print; + + while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT) + { + error = clib_error_return (0, "unknown input '%U'", + format_unformat_error, line_input); + break; + } + unformat_free (line_input); + +print: + vlib_cli_output (vm, "NAT44 ED sessions:"); + + vec_foreach_index (i, sm->per_thread_data) + { + tsm = vec_elt_at_index (sm->per_thread_data, i); + + vlib_cli_output (vm, "-------- thread %d %s: %d sessions --------\n", + i, vlib_worker_threads[i].name, + pool_elts (tsm->sessions)); + + snat_session_t *s; + pool_foreach (s, tsm->sessions) + { + vlib_cli_output (vm, " %U\n", format_snat_session, tsm, s); + } + } + return error; +} + +static clib_error_t * +nat44_set_session_limit_command_fn (vlib_main_t * vm, + unformat_input_t * input, + vlib_cli_command_t * cmd) +{ + unformat_input_t _line_input, *line_input = &_line_input; + clib_error_t *error = 0; + + u32 session_limit = 0, vrf_id = 0; + + /* 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, "%u", &session_limit)) + ; + else if (unformat (line_input, "vrf %u", &vrf_id)) + ; + else + { + error = clib_error_return (0, "unknown input '%U'", + format_unformat_error, line_input); + goto done; + } + } + + if (!session_limit) + error = clib_error_return (0, "missing value of session limit"); + else if (nat44_update_session_limit (session_limit, vrf_id)) + error = clib_error_return (0, "nat44_set_session_limit failed"); + +done: + unformat_free (line_input); + + return error; +} + +static clib_error_t * +nat44_del_session_command_fn (vlib_main_t * vm, + unformat_input_t * input, + vlib_cli_command_t * cmd) +{ + snat_main_t *sm = &snat_main; + unformat_input_t _line_input, *line_input = &_line_input; + u32 port = 0, eh_port = 0, vrf_id = sm->outside_vrf_id; + clib_error_t *error = 0; + ip4_address_t addr, eh_addr; + nat_protocol_t proto; + int is_in = 0; + int rv; + + /* 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, "%U:%u %U", unformat_ip4_address, &addr, &port, + unformat_nat_protocol, &proto)) + ; + else if (unformat (line_input, "in")) + { + is_in = 1; + vrf_id = sm->inside_vrf_id; + } + else if (unformat (line_input, "out")) + { + is_in = 0; + vrf_id = sm->outside_vrf_id; + } + else if (unformat (line_input, "vrf %u", &vrf_id)) + ; + else if (unformat (line_input, "external-host %U:%u", + unformat_ip4_address, &eh_addr, &eh_port)) + ; + else + { + error = clib_error_return (0, "unknown input '%U'", + format_unformat_error, line_input); + goto done; + } + } + + rv = + nat44_del_ed_session (sm, &addr, clib_host_to_net_u16 (port), &eh_addr, + clib_host_to_net_u16 (eh_port), + nat_proto_to_ip_proto (proto), vrf_id, is_in); + + switch (rv) + { + case 0: + break; + + default: + error = clib_error_return (0, "nat44_del_session returned %d", rv); + goto done; + } + +done: + unformat_free (line_input); + + return error; +} + +static clib_error_t * +snat_forwarding_set_command_fn (vlib_main_t * vm, + unformat_input_t * input, + vlib_cli_command_t * cmd) +{ + snat_main_t *sm = &snat_main; + unformat_input_t _line_input, *line_input = &_line_input; + u8 forwarding_enable; + u8 forwarding_enable_set = 0; + clib_error_t *error = 0; + + /* Get a line of input. */ + if (!unformat_user (input, unformat_line_input, line_input)) + return clib_error_return (0, "'enable' or 'disable' expected"); + + while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT) + { + if (!forwarding_enable_set && unformat (line_input, "enable")) + { + forwarding_enable = 1; + forwarding_enable_set = 1; + } + else if (!forwarding_enable_set && unformat (line_input, "disable")) + { + forwarding_enable = 0; + forwarding_enable_set = 1; + } + else + { + error = clib_error_return (0, "unknown input '%U'", + format_unformat_error, line_input); + goto done; + } + } + + if (!forwarding_enable_set) + { + error = clib_error_return (0, "'enable' or 'disable' expected"); + goto done; + } + + sm->forwarding_enabled = forwarding_enable; + +done: + unformat_free (line_input); + + return error; +} + +static clib_error_t * +set_timeout_command_fn (vlib_main_t * vm, + unformat_input_t * input, vlib_cli_command_t * cmd) +{ + snat_main_t *sm = &snat_main; + unformat_input_t _line_input, *line_input = &_line_input; + clib_error_t *error = 0; + + /* 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, "udp %u", &sm->timeouts.udp)); + else if (unformat (line_input, "tcp-established %u", + &sm->timeouts.tcp.established)); + else if (unformat (line_input, "tcp-transitory %u", + &sm->timeouts.tcp.transitory)); + else if (unformat (line_input, "icmp %u", &sm->timeouts.icmp)); + else if (unformat (line_input, "reset")) + nat_reset_timeouts (&sm->timeouts); + else + { + error = clib_error_return (0, "unknown input '%U'", + format_unformat_error, line_input); + goto done; + } + } +done: + unformat_free (line_input); + return error; +} + +static clib_error_t * +nat_show_timeouts_command_fn (vlib_main_t * vm, + unformat_input_t * input, + vlib_cli_command_t * cmd) +{ + snat_main_t *sm = &snat_main; + + vlib_cli_output (vm, "udp timeout: %dsec", sm->timeouts.udp); + vlib_cli_output (vm, "tcp-established timeout: %dsec", + sm->timeouts.tcp.established); + vlib_cli_output (vm, "tcp-transitory timeout: %dsec", + sm->timeouts.tcp.transitory); + vlib_cli_output (vm, "icmp timeout: %dsec", sm->timeouts.icmp); + + return 0; +} + +static clib_error_t * +set_frame_queue_nelts_command_fn (vlib_main_t *vm, unformat_input_t *input, + vlib_cli_command_t *cmd) +{ + unformat_input_t _line_input, *line_input = &_line_input; + clib_error_t *error = 0; + u32 frame_queue_nelts = 0; + /* 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, "%u", &frame_queue_nelts)) + ; + else + { + error = clib_error_return (0, "unknown input '%U'", + format_unformat_error, line_input); + goto done; + } + } + if (!frame_queue_nelts) + { + error = clib_error_return (0, "frame_queue_nelts cannot be zero"); + goto done; + } + if (snat_set_frame_queue_nelts (frame_queue_nelts) != 0) + { + error = clib_error_return (0, "snat_set_frame_queue_nelts failed"); + goto done; + } +done: + unformat_free (line_input); + return error; +} + +/*? + * @cliexpar + * @cliexstart{nat44 enable} + * Enable nat44 plugin + * To enable nat44, use: + * vpp# nat44 enable sessions + * To enable nat44 static mapping only, use: + * vpp# nat44 enable sessions static-mapping + * To enable nat44 static mapping with connection tracking, use: + * vpp# nat44 enable sessions static-mapping connection-tracking + * To set inside-vrf outside-vrf, use: + * vpp# nat44 enable sessions inside-vrf outside-vrf + * @cliexend +?*/ +VLIB_CLI_COMMAND (nat44_enable_command, static) = { + .path = "nat44 enable", + .short_help = + "nat44 enable sessions [static-mappig-only " + "[connection-tracking]] [inside-vrf ] [outside-vrf ]", + .function = nat44_enable_command_fn, +}; + +/*? + * @cliexpar + * @cliexstart{nat44 disable} + * Disable nat44 plugin + * To disable nat44, use: + * vpp# nat44 disable + * @cliexend +?*/ +VLIB_CLI_COMMAND (nat44_disable_command, static) = { + .path = "nat44 disable", + .short_help = "nat44 disable", + .function = nat44_disable_command_fn, +}; + +/*? + * @cliexpar + * @cliexstart{set snat workers} + * Set NAT workers if 2 or more workers available, use: + * vpp# set snat workers 0-2,5 + * @cliexend +?*/ +VLIB_CLI_COMMAND (set_workers_command, static) = { + .path = "set nat workers", + .function = set_workers_command_fn, + .short_help = "set nat workers ", +}; + +/*? + * @cliexpar + * @cliexstart{show nat workers} + * Show NAT workers. + * vpp# show nat workers: + * 2 workers + * vpp_wk_0 + * vpp_wk_1 + * @cliexend +?*/ +VLIB_CLI_COMMAND (nat_show_workers_command, static) = { + .path = "show nat workers", + .short_help = "show nat workers", + .function = nat_show_workers_commnad_fn, +}; + +/*? + * @cliexpar + * @cliexstart{set nat timeout} + * Set values of timeouts for NAT sessions (in seconds), use: + * vpp# set nat timeout udp 120 tcp-established 7500 tcp-transitory 250 icmp 90 + * To reset default values use: + * vpp# set nat timeout reset + * @cliexend +?*/ +VLIB_CLI_COMMAND (set_timeout_command, static) = { + .path = "set nat timeout", + .function = set_timeout_command_fn, + .short_help = + "set nat timeout [udp | tcp-established " + "tcp-transitory | icmp | reset]", +}; + +/*? + * @cliexpar + * @cliexstart{show nat timeouts} + * Show values of timeouts for NAT sessions. + * vpp# show nat timeouts + * udp timeout: 300sec + * tcp-established timeout: 7440sec + * tcp-transitory timeout: 240sec + * icmp timeout: 60sec + * @cliexend +?*/ +VLIB_CLI_COMMAND (nat_show_timeouts_command, static) = { + .path = "show nat timeouts", + .short_help = "show nat timeouts", + .function = nat_show_timeouts_command_fn, +}; + +/*? + * @cliexpar + * @cliexstart{set nat frame-queue-nelts} + * Set number of worker handoff frame queue elements. + * @cliexend +?*/ +VLIB_CLI_COMMAND (set_frame_queue_nelts_command, static) = { + .path = "set nat frame-queue-nelts", + .function = set_frame_queue_nelts_command_fn, + .short_help = "set nat frame-queue-nelts ", +}; + +/*? + * @cliexpar + * @cliexstart{nat set logging level} + * To set NAT logging level use: + * Set nat logging level + * @cliexend +?*/ +VLIB_CLI_COMMAND (snat_set_log_level_command, static) = { + .path = "nat set logging level", + .function = snat_set_log_level_command_fn, + .short_help = "nat set logging level ", +}; + +/*? + * @cliexpar + * @cliexstart{snat ipfix logging} + * To enable NAT IPFIX logging use: + * vpp# nat ipfix logging + * To set IPFIX exporter use: + * vpp# set ipfix exporter collector 10.10.10.3 src 10.10.10.1 + * @cliexend +?*/ +VLIB_CLI_COMMAND (snat_ipfix_logging_enable_disable_command, static) = { + .path = "nat ipfix logging", + .function = snat_ipfix_logging_enable_disable_command_fn, + .short_help = "nat ipfix logging [domain ] [src-port ] [disable]", +}; + +/*? + * @cliexpar + * @cliexstart{nat mss-clamping} + * Set TCP MSS rewriting configuration + * To enable TCP MSS rewriting use: + * vpp# nat mss-clamping 1452 + * To disbale TCP MSS rewriting use: + * vpp# nat mss-clamping disable + * @cliexend +?*/ +VLIB_CLI_COMMAND (nat_set_mss_clamping_command, static) = { + .path = "nat mss-clamping", + .short_help = "nat mss-clamping |disable", + .function = nat_set_mss_clamping_command_fn, +}; + +/*? + * @cliexpar + * @cliexstart{show nat mss-clamping} + * Show TCP MSS rewriting configuration + * @cliexend +?*/ +VLIB_CLI_COMMAND (nat_show_mss_clamping_command, static) = { + .path = "show nat mss-clamping", + .short_help = "show nat mss-clamping", + .function = nat_show_mss_clamping_command_fn, +}; + +/*? + * @cliexpar + * @cliexstart{show nat44 hash tables} + * Show NAT44 hash tables + * @cliexend +?*/ +VLIB_CLI_COMMAND (nat44_show_hash, static) = { + .path = "show nat44 hash tables", + .short_help = "show nat44 hash tables [detail|verbose]", + .function = nat44_show_hash_command_fn, +}; + +/*? + * @cliexpar + * @cliexstart{nat44 add address} + * Add/delete NAT44 pool address. + * To add NAT44 pool address use: + * vpp# nat44 add address 172.16.1.3 + * vpp# nat44 add address 172.16.2.2 - 172.16.2.24 + * To add NAT44 pool address for specific tenant (identified by VRF id) use: + * vpp# nat44 add address 172.16.1.3 tenant-vrf 10 + * @cliexend +?*/ +VLIB_CLI_COMMAND (add_address_command, static) = { + .path = "nat44 add address", + .short_help = "nat44 add address [- ] " + "[tenant-vrf ] [twice-nat] [del]", + .function = add_address_command_fn, +}; + +/*? + * @cliexpar + * @cliexstart{show nat44 summary} + * Show NAT44 summary + * vpp# show nat44 summary + * @cliexend +?*/ +VLIB_CLI_COMMAND (nat44_show_summary_command, static) = { + .path = "show nat44 summary", + .short_help = "show nat44 summary", + .function = nat44_show_summary_command_fn, +}; + +/*? + * @cliexpar + * @cliexstart{show nat44 addresses} + * Show NAT44 pool addresses. + * vpp# show nat44 addresses + * NAT44 pool addresses: + * 172.16.2.2 + * tenant VRF independent + * 10 busy udp ports + * 0 busy tcp ports + * 0 busy icmp ports + * 172.16.1.3 + * tenant VRF: 10 + * 0 busy udp ports + * 2 busy tcp ports + * 0 busy icmp ports + * NAT44 twice-nat pool addresses: + * 10.20.30.72 + * tenant VRF independent + * 0 busy udp ports + * 0 busy tcp ports + * 0 busy icmp ports + * @cliexend +?*/ +VLIB_CLI_COMMAND (nat44_show_addresses_command, static) = { + .path = "show nat44 addresses", + .short_help = "show nat44 addresses", + .function = nat44_show_addresses_command_fn, +}; + +/*? + * @cliexpar + * @cliexstart{set interface nat44} + * Enable/disable NAT44 feature on the interface. + * To enable NAT44 feature with local network interface use: + * vpp# set interface nat44 in GigabitEthernet0/8/0 + * To enable NAT44 feature with external network interface use: + * vpp# set interface nat44 out GigabitEthernet0/a/0 + * @cliexend +?*/ +VLIB_CLI_COMMAND (set_interface_snat_command, static) = { + .path = "set interface nat44", + .function = snat_feature_command_fn, + .short_help = "set interface nat44 in out [output-feature] " + "[del]", +}; + +/*? + * @cliexpar + * @cliexstart{show nat44 interfaces} + * Show interfaces with NAT44 feature. + * vpp# show nat44 interfaces + * NAT44 interfaces: + * GigabitEthernet0/8/0 in + * GigabitEthernet0/a/0 out + * @cliexend +?*/ +VLIB_CLI_COMMAND (nat44_show_interfaces_command, static) = { + .path = "show nat44 interfaces", + .short_help = "show nat44 interfaces", + .function = nat44_show_interfaces_command_fn, +}; + +/*? + * @cliexpar + * @cliexstart{nat44 add static mapping} + * Static mapping allows hosts on the external network to initiate connection + * to to the local network host. + * To create static mapping between local host address 10.0.0.3 port 6303 and + * external address 4.4.4.4 port 3606 for TCP protocol use: + * vpp# nat44 add static mapping tcp local 10.0.0.3 6303 external 4.4.4.4 3606 + * If not runnig "static mapping only" NAT plugin mode use before: + * vpp# nat44 add address 4.4.4.4 + * To create address only static mapping between local and external address use: + * vpp# nat44 add static mapping local 10.0.0.3 external 4.4.4.4 + * To create ICMP static mapping between local and external with ICMP echo + * identifier 10 use: + * vpp# nat44 add static mapping icmp local 10.0.0.3 10 external 4.4.4.4 10 + * To force use of specific pool address, vrf independent + * vpp# nat44 add static mapping local 10.0.0.2 1234 external 10.0.2.2 1234 twice-nat exact 10.0.1.2 + * @cliexend +?*/ +VLIB_CLI_COMMAND (add_static_mapping_command, static) = { + .path = "nat44 add static mapping", + .function = add_static_mapping_command_fn, + .short_help = + "nat44 add static mapping tcp|udp|icmp local [] " + "external [] [vrf ] [twice-nat|self-twice-nat] " + "[out2in-only] [exact ] [del]", +}; + +/*? + * @cliexpar + * @cliexstart{nat44 add identity mapping} + * Identity mapping translate an IP address to itself. + * To create identity mapping for address 10.0.0.3 port 6303 for TCP protocol + * use: + * vpp# nat44 add identity mapping 10.0.0.3 tcp 6303 + * To create identity mapping for address 10.0.0.3 use: + * vpp# nat44 add identity mapping 10.0.0.3 + * To create identity mapping for DHCP addressed interface use: + * vpp# nat44 add identity mapping external GigabitEthernet0/a/0 tcp 3606 + * @cliexend +?*/ +VLIB_CLI_COMMAND (add_identity_mapping_command, static) = { + .path = "nat44 add identity mapping", + .function = add_identity_mapping_command_fn, + .short_help = "nat44 add identity mapping |external " + "[ ] [vrf ] [del]", +}; + +/*? + * @cliexpar + * @cliexstart{nat44 add load-balancing static mapping} + * Service load balancing using NAT44 + * To add static mapping with load balancing for service with external IP + * address 1.2.3.4 and TCP port 80 and mapped to 2 local servers + * 10.100.10.10:8080 and 10.100.10.20:8080 with probability 80% resp. 20% use: + * vpp# nat44 add load-balancing static mapping protocol tcp external 1.2.3.4:80 local 10.100.10.10:8080 probability 80 local 10.100.10.20:8080 probability 20 + * @cliexend +?*/ +VLIB_CLI_COMMAND (add_lb_static_mapping_command, static) = { + .path = "nat44 add load-balancing static mapping", + .function = add_lb_static_mapping_command_fn, + .short_help = + "nat44 add load-balancing static mapping protocol tcp|udp " + "external : local : [vrf ] " + "probability [twice-nat|self-twice-nat] [out2in-only] " + "[affinity ] [del]", +}; + +/*? + * @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 : local : [vrf ] " + "probability [del]", +}; + +/*? + * @cliexpar + * @cliexstart{show nat44 static mappings} + * Show NAT44 static mappings. + * vpp# show nat44 static mappings + * NAT44 static mappings: + * local 10.0.0.3 external 4.4.4.4 vrf 0 + * tcp local 192.168.0.4:6303 external 4.4.4.3:3606 vrf 0 + * tcp vrf 0 external 1.2.3.4:80 out2in-only + * local 10.100.10.10:8080 probability 80 + * local 10.100.10.20:8080 probability 20 + * tcp local 10.100.3.8:8080 external 169.10.10.1:80 vrf 0 twice-nat + * tcp local 10.0.0.10:3603 external GigabitEthernet0/a/0:6306 vrf 10 + * @cliexend +?*/ +VLIB_CLI_COMMAND (nat44_show_static_mappings_command, static) = { + .path = "show nat44 static mappings", + .short_help = "show nat44 static mappings", + .function = nat44_show_static_mappings_command_fn, +}; + +/*? + * @cliexpar + * @cliexstart{nat44 add interface address} + * Use NAT44 pool address from specific interfce + * To add NAT44 pool address from specific interface use: + * vpp# nat44 add interface address GigabitEthernet0/8/0 + * @cliexend +?*/ +VLIB_CLI_COMMAND (snat_add_interface_address_command, static) = { + .path = "nat44 add interface address", + .short_help = "nat44 add interface address [twice-nat] [del]", + .function = snat_add_interface_address_command_fn, +}; + +/*? + * @cliexpar + * @cliexstart{show nat44 interface address} + * Show NAT44 pool address interfaces + * vpp# show nat44 interface address + * NAT44 pool address interfaces: + * GigabitEthernet0/a/0 + * NAT44 twice-nat pool address interfaces: + * GigabitEthernet0/8/0 + * @cliexend +?*/ +VLIB_CLI_COMMAND (nat44_show_interface_address_command, static) = { + .path = "show nat44 interface address", + .short_help = "show nat44 interface address", + .function = nat44_show_interface_address_command_fn, +}; + +/*? + * @cliexpar + * @cliexstart{show nat44 sessions} + * Show NAT44 sessions. + * @cliexend +?*/ +VLIB_CLI_COMMAND (nat44_show_sessions_command, static) = { + .path = "show nat44 sessions", + .short_help = "show nat44 sessions [detail|metrics]", + .function = nat44_show_sessions_command_fn, +}; + +/*? + * @cliexpar + * @cliexstart{set nat44 session limit} + * Set NAT44 session limit. + * @cliexend +?*/ +VLIB_CLI_COMMAND (nat44_set_session_limit_command, static) = { + .path = "set nat44 session limit", + .short_help = "set nat44 session limit [vrf ]", + .function = nat44_set_session_limit_command_fn, +}; + +/*? + * @cliexpar + * @cliexstart{nat44 del session} + * To administratively delete NAT44 session by inside address and port use: + * vpp# nat44 del session in 10.0.0.3:6303 tcp + * To administratively delete NAT44 session by outside address and port use: + * vpp# nat44 del session out 1.0.0.3:6033 udp + * @cliexend +?*/ +VLIB_CLI_COMMAND (nat44_del_session_command, static) = { + .path = "nat44 del session", + .short_help = "nat44 del session in|out : tcp|udp|icmp [vrf ] [external-host :]", + .function = nat44_del_session_command_fn, +}; + +/*? + * @cliexpar + * @cliexstart{nat44 forwarding} + * Enable or disable forwarding + * Forward packets which don't match existing translation + * or static mapping instead of dropping them. + * To enable forwarding, use: + * vpp# nat44 forwarding enable + * To disable forwarding, use: + * vpp# nat44 forwarding disable + * @cliexend +?*/ +VLIB_CLI_COMMAND (snat_forwarding_set_command, static) = { + .path = "nat44 forwarding", + .short_help = "nat44 forwarding enable|disable", + .function = snat_forwarding_set_command_fn, +}; + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/src/plugins/nat/nat44-ed/nat44_ed_format.c b/src/plugins/nat/nat44-ed/nat44_ed_format.c new file mode 100644 index 00000000000..29fd1129f32 --- /dev/null +++ b/src/plugins/nat/nat44-ed/nat44_ed_format.c @@ -0,0 +1,277 @@ +/* + * Copyright (c) 2018 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file + * @brief NAT formatting + */ + +#include +#include + +uword +unformat_nat_protocol (unformat_input_t * input, va_list * args) +{ + u32 *r = va_arg (*args, u32 *); + + if (0); +#define _(N, i, n, s) else if (unformat (input, s)) *r = NAT_PROTOCOL_##N; + foreach_nat_protocol +#undef _ + else + return 0; + return 1; +} + +u8 * +format_nat_protocol (u8 * s, va_list * args) +{ + u32 i = va_arg (*args, u32); + u8 *t = 0; + + switch (i) + { +#define _(N, j, n, str) case NAT_PROTOCOL_##N: t = (u8 *) str; break; + foreach_nat_protocol +#undef _ + default: + s = format (s, "unknown"); + return s; + } + s = format (s, "%s", t); + return s; +} + +u8 * +format_nat_addr_and_port_alloc_alg (u8 * s, va_list * args) +{ + u32 i = va_arg (*args, u32); + u8 *t = 0; + + switch (i) + { +#define _(v, N, s) case NAT_ADDR_AND_PORT_ALLOC_ALG_##N: t = (u8 *) s; break; + foreach_nat_addr_and_port_alloc_alg +#undef _ + default: + s = format (s, "unknown"); + return s; + } + s = format (s, "%s", t); + return s; +} + +u8 * +format_snat_key (u8 * s, va_list * args) +{ + u64 key = va_arg (*args, u64); + + ip4_address_t addr; + u16 port; + nat_protocol_t protocol; + u32 fib_index; + + split_nat_key (key, &addr, &port, &fib_index, &protocol); + + s = format (s, "%U proto %U port %d fib %d", + format_ip4_address, &addr, + format_nat_protocol, protocol, + clib_net_to_host_u16 (port), fib_index); + return s; +} + +u8 * +format_snat_session_state (u8 * s, va_list * args) +{ + u32 i = va_arg (*args, u32); + u8 *t = 0; + + switch (i) + { +#define _(v, N, str) case SNAT_SESSION_##N: t = (u8 *) str; break; + foreach_snat_session_state +#undef _ + default: + t = format (t, "unknown"); + } + s = format (s, "%s", t); + return s; +} + +u8 * +format_snat_session (u8 * s, va_list * args) +{ + snat_main_per_thread_data_t *tsm = + va_arg (*args, snat_main_per_thread_data_t *); + snat_session_t *sess = va_arg (*args, snat_session_t *); + + if (snat_is_unk_proto_session (sess)) + { + s = format (s, " i2o %U proto %u fib %u\n", + format_ip4_address, &sess->in2out.addr, + sess->in2out.port, sess->in2out.fib_index); + s = + format (s, " o2i %U proto %u fib %u\n", format_ip4_address, + &sess->out2in.addr, sess->out2in.port, sess->out2in.fib_index); + } + else + { + s = format (s, " i2o %U proto %U port %d fib %d\n", + format_ip4_address, &sess->in2out.addr, + format_nat_protocol, sess->nat_proto, + clib_net_to_host_u16 (sess->in2out.port), + sess->in2out.fib_index); + s = format (s, " o2i %U proto %U port %d fib %d\n", + format_ip4_address, &sess->out2in.addr, format_nat_protocol, + sess->nat_proto, clib_net_to_host_u16 (sess->out2in.port), + sess->out2in.fib_index); + } + if (is_ed_session (sess) || is_fwd_bypass_session (sess)) + { + if (is_twice_nat_session (sess)) + { + s = format (s, " external host o2i %U:%d i2o %U:%d\n", + format_ip4_address, &sess->ext_host_addr, + clib_net_to_host_u16 (sess->ext_host_port), + format_ip4_address, &sess->ext_host_nat_addr, + clib_net_to_host_u16 (sess->ext_host_nat_port)); + } + else + { + if (sess->ext_host_addr.as_u32) + s = format (s, " external host %U:%u\n", + format_ip4_address, &sess->ext_host_addr, + clib_net_to_host_u16 (sess->ext_host_port)); + } + s = format (s, " i2o flow: %U\n", format_nat_6t_flow, &sess->i2o); + s = format (s, " o2i flow: %U\n", format_nat_6t_flow, &sess->o2i); + } + s = format (s, " index %llu\n", sess - tsm->sessions); + s = format (s, " last heard %.2f\n", sess->last_heard); + s = format (s, " total pkts %d, total bytes %lld\n", + sess->total_pkts, sess->total_bytes); + if (snat_is_session_static (sess)) + s = format (s, " static translation\n"); + else + s = format (s, " dynamic translation\n"); + if (is_fwd_bypass_session (sess)) + s = format (s, " forwarding-bypass\n"); + if (is_lb_session (sess)) + s = format (s, " load-balancing\n"); + if (is_twice_nat_session (sess)) + s = format (s, " twice-nat\n"); + return s; +} + +u8 * +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 %U:%d", + format_nat_protocol, m->proto, + format_ip4_address, &m->local_addr, + clib_net_to_host_u16 (m->local_port)); + + /* *INDENT-OFF* */ + pool_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, + format_ip4_address, &m->external_addr, + m->vrf_id, + m->twice_nat == TWICE_NAT ? "twice-nat" : + m->twice_nat == TWICE_NAT_SELF ? "self-twice-nat" : "", + is_out2in_only_static_mapping (m) ? "out2in-only" : ""); + else + { + if (is_lb_static_mapping (m)) + { + s = format (s, "%U external %U:%d %s %s", + format_nat_protocol, m->proto, + format_ip4_address, &m->external_addr, + clib_net_to_host_u16 (m->external_port), + m->twice_nat == TWICE_NAT ? "twice-nat" : + m->twice_nat == TWICE_NAT_SELF ? "self-twice-nat" : "", + is_out2in_only_static_mapping (m) ? "out2in-only" : ""); + + /* *INDENT-OFF* */ + pool_foreach (local, m->locals) + { + s = format (s, "\n local %U:%d vrf %d probability %d\%", + format_ip4_address, &local->addr, + clib_net_to_host_u16 (local->port), + local->vrf_id, local->probability); + } + /* *INDENT-ON* */ + + } + else + s = format (s, "%U local %U:%d external %U:%d vrf %d %s %s", + format_nat_protocol, m->proto, + format_ip4_address, &m->local_addr, + clib_net_to_host_u16 (m->local_port), + format_ip4_address, &m->external_addr, + clib_net_to_host_u16 (m->external_port), + m->vrf_id, + m->twice_nat == TWICE_NAT ? "twice-nat" : + m->twice_nat == TWICE_NAT_SELF ? "self-twice-nat" : "", + is_out2in_only_static_mapping (m) ? "out2in-only" : ""); + } + return s; +} + +u8 * +format_snat_static_map_to_resolve (u8 * s, va_list * args) +{ + snat_static_map_resolve_t *m = va_arg (*args, snat_static_map_resolve_t *); + vnet_main_t *vnm = vnet_get_main (); + + if (m->addr_only) + s = format (s, "local %U external %U vrf %d", + format_ip4_address, &m->l_addr, + format_vnet_sw_if_index_name, vnm, m->sw_if_index, m->vrf_id); + else + s = format (s, "%U local %U:%d external %U:%d vrf %d", + format_nat_protocol, m->proto, + format_ip4_address, &m->l_addr, + clib_net_to_host_u16 (m->l_port), + format_vnet_sw_if_index_name, vnm, m->sw_if_index, + clib_net_to_host_u16 (m->e_port), m->vrf_id); + + return s; +} + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/src/plugins/nat/nat44-ed/nat44_ed_handoff.c b/src/plugins/nat/nat44-ed/nat44_ed_handoff.c new file mode 100644 index 00000000000..6715ce2f2c4 --- /dev/null +++ b/src/plugins/nat/nat44-ed/nat44_ed_handoff.c @@ -0,0 +1,344 @@ +/* + * Copyright (c) 2018 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file + * @brief NAT44 worker handoff + */ + +#include +#include +#include +#include +#include + +#include +#include + +typedef struct +{ + u32 next_worker_index; + u32 trace_index; + u8 in2out; + u8 output; +} nat44_handoff_trace_t; + +#define foreach_nat44_handoff_error \ + _ (CONGESTION_DROP, "congestion drop") \ + _ (SAME_WORKER, "same worker") \ + _ (DO_HANDOFF, "do handoff") + +typedef enum +{ +#define _(sym, str) NAT44_HANDOFF_ERROR_##sym, + foreach_nat44_handoff_error +#undef _ + NAT44_HANDOFF_N_ERROR, +} nat44_handoff_error_t; + +static char *nat44_handoff_error_strings[] = { +#define _(sym,string) string, + foreach_nat44_handoff_error +#undef _ +}; + +static u8 * +format_nat44_handoff_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 *); + nat44_handoff_trace_t *t = va_arg (*args, nat44_handoff_trace_t *); + char *tag, *output; + + tag = t->in2out ? "IN2OUT" : "OUT2IN"; + output = t->output ? "OUTPUT-FEATURE" : ""; + s = + format (s, "NAT44_%s_WORKER_HANDOFF %s: next-worker %d trace index %d", + tag, output, t->next_worker_index, t->trace_index); + + return s; +} + +static inline uword +nat44_worker_handoff_fn_inline (vlib_main_t * vm, + vlib_node_runtime_t * node, + vlib_frame_t * frame, u8 is_output, + u8 is_in2out) +{ + u32 n_enq, n_left_from, *from, do_handoff = 0, same_worker = 0; + + u16 thread_indices[VLIB_FRAME_SIZE], *ti = thread_indices; + vlib_buffer_t *bufs[VLIB_FRAME_SIZE], **b = bufs; + snat_main_t *sm = &snat_main; + + u32 fq_index, thread_index = vm->thread_index; + + from = vlib_frame_vector_args (frame); + n_left_from = frame->n_vectors; + + vlib_get_buffers (vm, from, b, n_left_from); + + if (is_in2out) + { + fq_index = is_output ? sm->fq_in2out_output_index : sm->fq_in2out_index; + } + else + { + fq_index = sm->fq_out2in_index; + } + + while (n_left_from >= 4) + { + u32 arc_next0, arc_next1, arc_next2, arc_next3; + u32 sw_if_index0, sw_if_index1, sw_if_index2, sw_if_index3; + u32 rx_fib_index0, rx_fib_index1, rx_fib_index2, rx_fib_index3; + u32 iph_offset0 = 0, iph_offset1 = 0, iph_offset2 = 0, iph_offset3 = 0; + ip4_header_t *ip0, *ip1, *ip2, *ip3; + + if (PREDICT_TRUE (n_left_from >= 8)) + { + vlib_prefetch_buffer_header (b[4], LOAD); + vlib_prefetch_buffer_header (b[5], LOAD); + vlib_prefetch_buffer_header (b[6], LOAD); + vlib_prefetch_buffer_header (b[7], LOAD); + CLIB_PREFETCH (&b[4]->data, CLIB_CACHE_LINE_BYTES, LOAD); + CLIB_PREFETCH (&b[5]->data, CLIB_CACHE_LINE_BYTES, LOAD); + CLIB_PREFETCH (&b[6]->data, CLIB_CACHE_LINE_BYTES, LOAD); + CLIB_PREFETCH (&b[7]->data, CLIB_CACHE_LINE_BYTES, LOAD); + } + + if (is_output) + { + iph_offset0 = vnet_buffer (b[0])->ip.save_rewrite_length; + iph_offset1 = vnet_buffer (b[1])->ip.save_rewrite_length; + iph_offset2 = vnet_buffer (b[2])->ip.save_rewrite_length; + iph_offset3 = vnet_buffer (b[3])->ip.save_rewrite_length; + } + + ip0 = (ip4_header_t *) ((u8 *) vlib_buffer_get_current (b[0]) + + iph_offset0); + ip1 = (ip4_header_t *) ((u8 *) vlib_buffer_get_current (b[1]) + + iph_offset1); + ip2 = (ip4_header_t *) ((u8 *) vlib_buffer_get_current (b[2]) + + iph_offset2); + ip3 = (ip4_header_t *) ((u8 *) vlib_buffer_get_current (b[3]) + + iph_offset3); + + vnet_feature_next (&arc_next0, b[0]); + vnet_feature_next (&arc_next1, b[1]); + vnet_feature_next (&arc_next2, b[2]); + vnet_feature_next (&arc_next3, b[3]); + + vnet_buffer2 (b[0])->nat.arc_next = arc_next0; + vnet_buffer2 (b[1])->nat.arc_next = arc_next1; + vnet_buffer2 (b[2])->nat.arc_next = arc_next2; + vnet_buffer2 (b[3])->nat.arc_next = arc_next3; + + sw_if_index0 = vnet_buffer (b[0])->sw_if_index[VLIB_RX]; + sw_if_index1 = vnet_buffer (b[1])->sw_if_index[VLIB_RX]; + sw_if_index2 = vnet_buffer (b[2])->sw_if_index[VLIB_RX]; + sw_if_index3 = vnet_buffer (b[3])->sw_if_index[VLIB_RX]; + + rx_fib_index0 = ip4_fib_table_get_index_for_sw_if_index (sw_if_index0); + rx_fib_index1 = ip4_fib_table_get_index_for_sw_if_index (sw_if_index1); + rx_fib_index2 = ip4_fib_table_get_index_for_sw_if_index (sw_if_index2); + rx_fib_index3 = ip4_fib_table_get_index_for_sw_if_index (sw_if_index3); + + if (is_in2out) + { + ti[0] = sm->worker_in2out_cb (ip0, rx_fib_index0, is_output); + ti[1] = sm->worker_in2out_cb (ip1, rx_fib_index1, is_output); + ti[2] = sm->worker_in2out_cb (ip2, rx_fib_index2, is_output); + ti[3] = sm->worker_in2out_cb (ip3, rx_fib_index3, is_output); + } + else + { + ti[0] = sm->worker_out2in_cb (b[0], ip0, rx_fib_index0, is_output); + ti[1] = sm->worker_out2in_cb (b[1], ip1, rx_fib_index1, is_output); + ti[2] = sm->worker_out2in_cb (b[2], ip2, rx_fib_index2, is_output); + ti[3] = sm->worker_out2in_cb (b[3], ip3, rx_fib_index3, is_output); + } + + if (ti[0] == thread_index) + same_worker++; + else + do_handoff++; + + if (ti[1] == thread_index) + same_worker++; + else + do_handoff++; + + if (ti[2] == thread_index) + same_worker++; + else + do_handoff++; + + if (ti[3] == thread_index) + same_worker++; + else + do_handoff++; + + b += 4; + ti += 4; + n_left_from -= 4; + } + + while (n_left_from > 0) + { + u32 arc_next0; + u32 sw_if_index0; + u32 rx_fib_index0; + u32 iph_offset0 = 0; + ip4_header_t *ip0; + + + if (is_output) + iph_offset0 = vnet_buffer (b[0])->ip.save_rewrite_length; + + ip0 = (ip4_header_t *) ((u8 *) vlib_buffer_get_current (b[0]) + + iph_offset0); + + vnet_feature_next (&arc_next0, b[0]); + vnet_buffer2 (b[0])->nat.arc_next = arc_next0; + + sw_if_index0 = vnet_buffer (b[0])->sw_if_index[VLIB_RX]; + rx_fib_index0 = ip4_fib_table_get_index_for_sw_if_index (sw_if_index0); + + if (is_in2out) + { + ti[0] = sm->worker_in2out_cb (ip0, rx_fib_index0, is_output); + } + else + { + ti[0] = sm->worker_out2in_cb (b[0], ip0, rx_fib_index0, is_output); + } + + if (ti[0] == thread_index) + same_worker++; + else + do_handoff++; + + b += 1; + ti += 1; + n_left_from -= 1; + } + + if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE))) + { + u32 i; + b = bufs; + ti = thread_indices; + + for (i = 0; i < frame->n_vectors; i++) + { + if (b[0]->flags & VLIB_BUFFER_IS_TRACED) + { + nat44_handoff_trace_t *t = + vlib_add_trace (vm, node, b[0], sizeof (*t)); + t->next_worker_index = ti[0]; + t->trace_index = vlib_buffer_get_trace_index (b[0]); + t->in2out = is_in2out; + t->output = is_output; + + b += 1; + ti += 1; + } + else + break; + } + } + + n_enq = vlib_buffer_enqueue_to_thread (vm, fq_index, from, thread_indices, + frame->n_vectors, 1); + + if (n_enq < frame->n_vectors) + { + vlib_node_increment_counter (vm, node->node_index, + NAT44_HANDOFF_ERROR_CONGESTION_DROP, + frame->n_vectors - n_enq); + } + + vlib_node_increment_counter (vm, node->node_index, + NAT44_HANDOFF_ERROR_SAME_WORKER, same_worker); + vlib_node_increment_counter (vm, node->node_index, + NAT44_HANDOFF_ERROR_DO_HANDOFF, do_handoff); + return frame->n_vectors; +} + +VLIB_NODE_FN (snat_in2out_worker_handoff_node) (vlib_main_t * vm, + vlib_node_runtime_t * node, + vlib_frame_t * frame) +{ + return nat44_worker_handoff_fn_inline (vm, node, frame, 0, 1); +} + +/* *INDENT-OFF* */ +VLIB_REGISTER_NODE (snat_in2out_worker_handoff_node) = { + .name = "nat44-in2out-worker-handoff", + .vector_size = sizeof (u32), + .sibling_of = "nat-default", + .format_trace = format_nat44_handoff_trace, + .type = VLIB_NODE_TYPE_INTERNAL, + .n_errors = ARRAY_LEN(nat44_handoff_error_strings), + .error_strings = nat44_handoff_error_strings, +}; +/* *INDENT-ON* */ + +VLIB_NODE_FN (snat_in2out_output_worker_handoff_node) (vlib_main_t * vm, + vlib_node_runtime_t * + node, + vlib_frame_t * frame) +{ + return nat44_worker_handoff_fn_inline (vm, node, frame, 1, 1); +} + +/* *INDENT-OFF* */ +VLIB_REGISTER_NODE (snat_in2out_output_worker_handoff_node) = { + .name = "nat44-in2out-output-worker-handoff", + .vector_size = sizeof (u32), + .sibling_of = "nat-default", + .format_trace = format_nat44_handoff_trace, + .type = VLIB_NODE_TYPE_INTERNAL, + .n_errors = ARRAY_LEN(nat44_handoff_error_strings), + .error_strings = nat44_handoff_error_strings, +}; +/* *INDENT-ON* */ + +VLIB_NODE_FN (snat_out2in_worker_handoff_node) (vlib_main_t * vm, + vlib_node_runtime_t * node, + vlib_frame_t * frame) +{ + return nat44_worker_handoff_fn_inline (vm, node, frame, 0, 0); +} + +/* *INDENT-OFF* */ +VLIB_REGISTER_NODE (snat_out2in_worker_handoff_node) = { + .name = "nat44-out2in-worker-handoff", + .vector_size = sizeof (u32), + .sibling_of = "nat-default", + .format_trace = format_nat44_handoff_trace, + .type = VLIB_NODE_TYPE_INTERNAL, + .n_errors = ARRAY_LEN(nat44_handoff_error_strings), + .error_strings = nat44_handoff_error_strings, +}; +/* *INDENT-ON* */ + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/src/plugins/nat/nat44-ed/nat44_ed_in2out.c b/src/plugins/nat/nat44-ed/nat44_ed_in2out.c new file mode 100644 index 00000000000..784dea0faf6 --- /dev/null +++ b/src/plugins/nat/nat44-ed/nat44_ed_in2out.c @@ -0,0 +1,1579 @@ +/* + * Copyright (c) 2018 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file + * @brief NAT44 endpoint-dependent inside to outside network translation + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +/* number of attempts to get a port for ED overloading algorithm, if rolling + * a dice this many times doesn't produce a free port, it's treated + * as if there were no free ports available to conserve resources */ +#define ED_PORT_ALLOC_ATTEMPTS (10) + +static char *nat_in2out_ed_error_strings[] = { +#define _(sym,string) string, + foreach_nat_in2out_ed_error +#undef _ +}; + +typedef struct +{ + u32 sw_if_index; + u32 next_index; + u32 session_index; + nat_translation_error_e translation_error; + nat_6t_flow_t i2of; + nat_6t_flow_t o2if; + clib_bihash_kv_16_8_t search_key; + u8 is_slow_path; + u8 translation_via_i2of; + u8 lookup_skipped; +} nat_in2out_ed_trace_t; + +static u8 * +format_nat_in2out_ed_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 *); + nat_in2out_ed_trace_t *t = va_arg (*args, nat_in2out_ed_trace_t *); + char *tag; + + tag = + t->is_slow_path ? "NAT44_IN2OUT_ED_SLOW_PATH" : + "NAT44_IN2OUT_ED_FAST_PATH"; + + s = format (s, "%s: sw_if_index %d, next index %d, session %d", tag, + t->sw_if_index, t->next_index, t->session_index); + if (~0 != t->session_index) + { + s = format (s, ", translation result '%U' via %s", + format_nat_ed_translation_error, t->translation_error, + t->translation_via_i2of ? "i2of" : "o2if"); + s = format (s, "\n i2of %U", format_nat_6t_flow, &t->i2of); + s = format (s, "\n o2if %U", format_nat_6t_flow, &t->o2if); + } + if (!t->is_slow_path) + { + if (t->lookup_skipped) + { + s = format (s, "\n lookup skipped - cached session index used"); + } + else + { + s = format (s, "\n search key %U", format_ed_session_kvp, + &t->search_key); + } + } + + return s; +} + +/** + * @brief Check if packet should be translated + * + * Packets aimed at outside interface and external address with active session + * should be translated. + * + * @param sm NAT main + * @param rt NAT runtime data + * @param sw_if_index0 index of the inside interface + * @param ip0 IPv4 header + * @param proto0 NAT protocol + * @param rx_fib_index0 RX FIB index + * + * @returns 0 if packet should be translated otherwise 1 + */ +static inline int +snat_not_translate_fast (snat_main_t *sm, vlib_node_runtime_t *node, + u32 sw_if_index0, ip4_header_t *ip0, u32 proto0, + u32 rx_fib_index0) +{ + fib_node_index_t fei = FIB_NODE_INDEX_INVALID; + nat_outside_fib_t *outside_fib; + fib_prefix_t pfx = { + .fp_proto = FIB_PROTOCOL_IP4, + .fp_len = 32, + .fp_addr = { + .ip4.as_u32 = ip0->dst_address.as_u32, + } + , + }; + + /* Don't NAT packet aimed at the intfc address */ + if (PREDICT_FALSE ( + is_interface_addr (sm, node, sw_if_index0, ip0->dst_address.as_u32))) + return 1; + + fei = fib_table_lookup (rx_fib_index0, &pfx); + if (FIB_NODE_INDEX_INVALID != fei) + { + u32 sw_if_index = fib_entry_get_resolving_interface (fei); + if (sw_if_index == ~0) + { + vec_foreach (outside_fib, sm->outside_fibs) + { + fei = fib_table_lookup (outside_fib->fib_index, &pfx); + if (FIB_NODE_INDEX_INVALID != fei) + { + sw_if_index = fib_entry_get_resolving_interface (fei); + if (sw_if_index != ~0) + break; + } + } + } + if (sw_if_index == ~0) + return 1; + + snat_interface_t *i; + pool_foreach (i, sm->interfaces) + { + /* NAT packet aimed at outside interface */ + if ((nat_interface_is_outside (i)) && + (sw_if_index == i->sw_if_index)) + return 0; + } + } + + return 1; +} + +static int +nat_ed_alloc_addr_and_port (snat_main_t *sm, u32 rx_fib_index, u32 nat_proto, + u32 thread_index, ip4_address_t r_addr, u16 r_port, + u8 proto, u16 port_per_thread, + u32 snat_thread_index, snat_session_t *s, + ip4_address_t *outside_addr, u16 *outside_port) +{ + int i; + snat_address_t *a, *ga = 0; + + const u16 port_thread_offset = (port_per_thread * snat_thread_index) + 1024; + + for (i = 0; i < vec_len (sm->addresses); i++) + { + a = sm->addresses + i; + switch (nat_proto) + { +#define _(N, j, n, unused) \ + case NAT_PROTOCOL_##N: \ + if (a->fib_index == rx_fib_index) \ + { \ + s->o2i.match.daddr = a->addr; \ + /* first try port suggested by caller */ \ + u16 port = clib_net_to_host_u16 (*outside_port); \ + u16 port_offset = port - port_thread_offset; \ + if (port <= port_thread_offset || \ + port > port_thread_offset + port_per_thread) \ + { \ + /* need to pick a different port, suggested port doesn't fit in \ + * this thread's port range */ \ + port_offset = snat_random_port (0, port_per_thread - 1); \ + port = port_thread_offset + port_offset; \ + } \ + u16 attempts = ED_PORT_ALLOC_ATTEMPTS; \ + do \ + { \ + if (NAT_PROTOCOL_ICMP == nat_proto) \ + { \ + s->o2i.match.sport = clib_host_to_net_u16 (port); \ + } \ + s->o2i.match.dport = clib_host_to_net_u16 (port); \ + if (0 == \ + nat_ed_ses_o2i_flow_hash_add_del (sm, thread_index, s, 2)) \ + { \ + ++a->busy_##n##_port_refcounts[port]; \ + a->busy_##n##_ports_per_thread[thread_index]++; \ + a->busy_##n##_ports++; \ + *outside_addr = a->addr; \ + *outside_port = clib_host_to_net_u16 (port); \ + return 0; \ + } \ + port_offset = snat_random_port (0, port_per_thread - 1); \ + port = port_thread_offset + port_offset; \ + --attempts; \ + } \ + while (attempts > 0); \ + } \ + else if (a->fib_index == ~0) \ + { \ + ga = a; \ + } \ + break; + + foreach_nat_protocol; + default: + nat_elog_info (sm, "unknown protocol"); + return 1; + } + } + + if (ga) + { + /* fake fib_index to reuse macro */ + rx_fib_index = ~0; + a = ga; + switch (nat_proto) + { + foreach_nat_protocol; + default: + nat_elog_info (sm, "unknown protocol"); + return 1; + } + } + +#undef _ + + /* Totally out of translations to use... */ + nat_ipfix_logging_addresses_exhausted (thread_index, 0); + return 1; +} + +static_always_inline u32 +nat_outside_fib_index_lookup (snat_main_t * sm, ip4_address_t addr) +{ + fib_node_index_t fei = FIB_NODE_INDEX_INVALID; + nat_outside_fib_t *outside_fib; + fib_prefix_t pfx = { + .fp_proto = FIB_PROTOCOL_IP4, + .fp_len = 32, + .fp_addr = {.ip4.as_u32 = addr.as_u32,} + , + }; + // TODO: multiple vrfs none can resolve addr + vec_foreach (outside_fib, sm->outside_fibs) + { + fei = fib_table_lookup (outside_fib->fib_index, &pfx); + if (FIB_NODE_INDEX_INVALID != fei) + { + if (fib_entry_get_resolving_interface (fei) != ~0) + { + return outside_fib->fib_index; + } + } + } + return ~0; +} + +static_always_inline int +nat44_ed_external_sm_lookup (snat_main_t *sm, ip4_address_t match_addr, + u16 match_port, nat_protocol_t match_protocol, + u32 match_fib_index, ip4_address_t *daddr, + u16 *dport) +{ + clib_bihash_kv_8_8_t kv, value; + init_nat_k (&kv, match_addr, match_port, match_fib_index, match_protocol); + if (clib_bihash_search_8_8 (&sm->static_mapping_by_external, &kv, &value)) + { + /* Try address only mapping */ + init_nat_k (&kv, match_addr, 0, 0, 0); + if (clib_bihash_search_8_8 (&sm->static_mapping_by_external, &kv, + &value)) + return 0; + } + + snat_static_mapping_t *m = + pool_elt_at_index (sm->static_mappings, value.value); + *daddr = m->local_addr; + if (dport) + { + /* Address only mapping doesn't change port */ + *dport = is_addr_only_static_mapping (m) ? match_port : m->local_port; + } + return 1; +} + +static u32 +slow_path_ed (snat_main_t *sm, vlib_buffer_t *b, ip4_address_t l_addr, + ip4_address_t r_addr, u16 l_port, u16 r_port, u8 proto, + u32 rx_fib_index, snat_session_t **sessionp, + vlib_node_runtime_t *node, u32 next, u32 thread_index, f64 now) +{ + snat_main_per_thread_data_t *tsm = &sm->per_thread_data[thread_index]; + ip4_address_t outside_addr; + u16 outside_port; + u32 outside_fib_index; + u8 is_identity_nat; + + u32 nat_proto = ip_proto_to_nat_proto (proto); + snat_session_t *s = NULL; + lb_nat_type_t lb = 0; + ip4_address_t daddr = r_addr; + u16 dport = r_port; + + if (PREDICT_TRUE (nat_proto == NAT_PROTOCOL_TCP)) + { + if (PREDICT_FALSE + (!tcp_flags_is_init + (vnet_buffer (b)->ip.reass.icmp_type_or_tcp_flags))) + { + b->error = node->errors[NAT_IN2OUT_ED_ERROR_NON_SYN]; + return NAT_NEXT_DROP; + } + } + + if (PREDICT_FALSE + (nat44_ed_maximum_sessions_exceeded (sm, rx_fib_index, thread_index))) + { + if (!nat_lru_free_one (sm, thread_index, now)) + { + b->error = node->errors[NAT_IN2OUT_ED_ERROR_MAX_SESSIONS_EXCEEDED]; + nat_ipfix_logging_max_sessions (thread_index, + sm->max_translations_per_thread); + nat_elog_notice (sm, "maximum sessions exceeded"); + return NAT_NEXT_DROP; + } + } + + outside_fib_index = sm->outside_fib_index; + + switch (vec_len (sm->outside_fibs)) + { + case 0: + outside_fib_index = sm->outside_fib_index; + break; + case 1: + outside_fib_index = sm->outside_fibs[0].fib_index; + break; + default: + outside_fib_index = nat_outside_fib_index_lookup (sm, r_addr); + break; + } + + ip4_address_t sm_addr; + u16 sm_port; + u32 sm_fib_index; + /* First try to match static mapping by local address and port */ + int is_sm; + if (snat_static_mapping_match (sm, l_addr, l_port, rx_fib_index, nat_proto, + &sm_addr, &sm_port, &sm_fib_index, 0, 0, 0, + &lb, 0, &is_identity_nat, 0)) + { + is_sm = 0; + } + else + { + is_sm = 1; + } + + if (PREDICT_FALSE (is_sm && is_identity_nat)) + { + *sessionp = NULL; + return next; + } + + s = nat_ed_session_alloc (sm, thread_index, now, proto); + ASSERT (s); + + if (!is_sm) + { + s->in2out.addr = l_addr; + s->in2out.port = l_port; + s->nat_proto = nat_proto; + s->in2out.fib_index = rx_fib_index; + s->out2in.fib_index = outside_fib_index; + + // suggest using local port to allocation function + outside_port = l_port; + + // hairpinning? + int is_hairpinning = nat44_ed_external_sm_lookup ( + sm, r_addr, r_port, nat_proto, outside_fib_index, &daddr, &dport); + s->flags |= is_hairpinning * SNAT_SESSION_FLAG_HAIRPINNING; + + // destination addr/port updated with real values in + // nat_ed_alloc_addr_and_port + nat_6t_o2i_flow_init (sm, thread_index, s, daddr, dport, daddr, 0, + s->out2in.fib_index, proto); + nat_6t_flow_daddr_rewrite_set (&s->o2i, l_addr.as_u32); + if (NAT_PROTOCOL_ICMP == nat_proto) + { + nat_6t_flow_icmp_id_rewrite_set (&s->o2i, l_port); + } + else + { + nat_6t_flow_dport_rewrite_set (&s->o2i, l_port); + } + nat_6t_flow_txfib_rewrite_set (&s->o2i, rx_fib_index); + + if (nat_ed_alloc_addr_and_port ( + sm, rx_fib_index, nat_proto, thread_index, daddr, dport, proto, + sm->port_per_thread, tsm->snat_thread_index, s, &outside_addr, + &outside_port)) + { + nat_elog_notice (sm, "addresses exhausted"); + b->error = node->errors[NAT_IN2OUT_ED_ERROR_OUT_OF_PORTS]; + nat_ed_session_delete (sm, s, thread_index, 1); + return NAT_NEXT_DROP; + } + s->out2in.addr = outside_addr; + s->out2in.port = outside_port; + } + else + { + // static mapping + s->out2in.addr = outside_addr = sm_addr; + s->out2in.port = outside_port = sm_port; + s->in2out.addr = l_addr; + s->in2out.port = l_port; + s->nat_proto = nat_proto; + s->in2out.fib_index = rx_fib_index; + s->out2in.fib_index = outside_fib_index; + s->flags |= SNAT_SESSION_FLAG_STATIC_MAPPING; + + // hairpinning? + int is_hairpinning = nat44_ed_external_sm_lookup ( + sm, r_addr, r_port, nat_proto, outside_fib_index, &daddr, &dport); + s->flags |= is_hairpinning * SNAT_SESSION_FLAG_HAIRPINNING; + + if (NAT_PROTOCOL_ICMP == nat_proto) + { + nat_6t_o2i_flow_init (sm, thread_index, s, daddr, sm_port, sm_addr, + sm_port, s->out2in.fib_index, proto); + nat_6t_flow_icmp_id_rewrite_set (&s->o2i, l_port); + } + else + { + nat_6t_o2i_flow_init (sm, thread_index, s, daddr, dport, sm_addr, + sm_port, s->out2in.fib_index, proto); + nat_6t_flow_dport_rewrite_set (&s->o2i, l_port); + } + nat_6t_flow_daddr_rewrite_set (&s->o2i, l_addr.as_u32); + nat_6t_flow_txfib_rewrite_set (&s->o2i, rx_fib_index); + if (nat_ed_ses_o2i_flow_hash_add_del (sm, thread_index, s, 2)) + { + nat_elog_notice (sm, "out2in key add failed"); + goto error; + } + } + + if (lb) + s->flags |= SNAT_SESSION_FLAG_LOAD_BALANCING; + s->flags |= SNAT_SESSION_FLAG_ENDPOINT_DEPENDENT; + s->ext_host_addr = r_addr; + s->ext_host_port = r_port; + + nat_6t_i2o_flow_init (sm, thread_index, s, l_addr, l_port, r_addr, r_port, + rx_fib_index, proto); + nat_6t_flow_saddr_rewrite_set (&s->i2o, outside_addr.as_u32); + nat_6t_flow_daddr_rewrite_set (&s->i2o, daddr.as_u32); + if (NAT_PROTOCOL_ICMP == nat_proto) + { + nat_6t_flow_icmp_id_rewrite_set (&s->i2o, outside_port); + } + else + { + nat_6t_flow_sport_rewrite_set (&s->i2o, outside_port); + nat_6t_flow_dport_rewrite_set (&s->i2o, dport); + } + nat_6t_flow_txfib_rewrite_set (&s->i2o, outside_fib_index); + + if (nat_ed_ses_i2o_flow_hash_add_del (sm, thread_index, s, 1)) + { + nat_elog_notice (sm, "in2out key add failed"); + goto error; + } + + /* log NAT event */ + nat_ipfix_logging_nat44_ses_create (thread_index, + s->in2out.addr.as_u32, + s->out2in.addr.as_u32, + s->nat_proto, + s->in2out.port, + s->out2in.port, s->in2out.fib_index); + + nat_syslog_nat44_sadd (0, s->in2out.fib_index, &s->in2out.addr, + s->in2out.port, &s->ext_host_nat_addr, + s->ext_host_nat_port, &s->out2in.addr, s->out2in.port, + &s->ext_host_addr, s->ext_host_port, s->nat_proto, 0); + + per_vrf_sessions_register_session (s, thread_index); + + *sessionp = s; + return next; +error: + if (s) + { + if (!is_sm) + { + snat_free_outside_address_and_port (sm->addresses, thread_index, + &outside_addr, outside_port, + nat_proto); + } + nat_ed_session_delete (sm, s, thread_index, 1); + } + *sessionp = s = NULL; + return NAT_NEXT_DROP; +} + +static_always_inline int +nat44_ed_not_translate (snat_main_t * sm, vlib_node_runtime_t * node, + u32 sw_if_index, ip4_header_t * ip, u32 proto, + u32 rx_fib_index, u32 thread_index) +{ + udp_header_t *udp = ip4_next_header (ip); + clib_bihash_kv_16_8_t kv, value; + + init_ed_k (&kv, ip->dst_address, udp->dst_port, ip->src_address, + udp->src_port, sm->outside_fib_index, ip->protocol); + + /* NAT packet aimed at external address if has active sessions */ + if (clib_bihash_search_16_8 (&sm->flow_hash, &kv, &value)) + { + /* or is static mappings */ + ip4_address_t placeholder_addr; + u16 placeholder_port; + u32 placeholder_fib_index; + if (!snat_static_mapping_match + (sm, ip->dst_address, udp->dst_port, sm->outside_fib_index, proto, + &placeholder_addr, &placeholder_port, &placeholder_fib_index, 1, 0, + 0, 0, 0, 0, 0)) + return 0; + } + else + return 0; + + if (sm->forwarding_enabled) + return 1; + + return snat_not_translate_fast (sm, node, sw_if_index, ip, proto, + rx_fib_index); +} + +static_always_inline int +nat_not_translate_output_feature_fwd (snat_main_t * sm, ip4_header_t * ip, + u32 thread_index, f64 now, + vlib_main_t * vm, vlib_buffer_t * b) +{ + clib_bihash_kv_16_8_t kv, value; + snat_session_t *s = 0; + snat_main_per_thread_data_t *tsm = &sm->per_thread_data[thread_index]; + + if (!sm->forwarding_enabled) + return 0; + + if (ip->protocol == IP_PROTOCOL_ICMP) + { + ip4_address_t lookup_saddr, lookup_daddr; + u16 lookup_sport, lookup_dport; + u8 lookup_protocol; + if (nat_get_icmp_session_lookup_values (b, ip, &lookup_saddr, + &lookup_sport, &lookup_daddr, + &lookup_dport, &lookup_protocol)) + return 0; + init_ed_k (&kv, lookup_saddr, lookup_sport, lookup_daddr, lookup_dport, + 0, lookup_protocol); + } + else if (ip->protocol == IP_PROTOCOL_UDP || ip->protocol == IP_PROTOCOL_TCP) + { + init_ed_k (&kv, ip->src_address, vnet_buffer (b)->ip.reass.l4_src_port, + ip->dst_address, vnet_buffer (b)->ip.reass.l4_dst_port, 0, + ip->protocol); + } + else + { + init_ed_k (&kv, ip->src_address, 0, ip->dst_address, 0, 0, + ip->protocol); + } + + if (!clib_bihash_search_16_8 (&sm->flow_hash, &kv, &value)) + { + ASSERT (thread_index == ed_value_get_thread_index (&value)); + s = + pool_elt_at_index (tsm->sessions, + ed_value_get_session_index (&value)); + + if (is_fwd_bypass_session (s)) + { + if (ip->protocol == IP_PROTOCOL_TCP) + { + nat44_set_tcp_session_state_i2o (sm, now, s, b, thread_index); + } + /* Accounting */ + nat44_session_update_counters (s, now, + vlib_buffer_length_in_chain (vm, b), + thread_index); + /* Per-user LRU list maintenance */ + nat44_session_update_lru (sm, s, thread_index); + return 1; + } + else + return 0; + } + + return 0; +} + +static_always_inline int +nat44_ed_not_translate_output_feature (snat_main_t * sm, ip4_header_t * ip, + u16 src_port, u16 dst_port, + u32 thread_index, u32 rx_sw_if_index, + u32 tx_sw_if_index, f64 now) +{ + clib_bihash_kv_16_8_t kv, value; + snat_main_per_thread_data_t *tsm = &sm->per_thread_data[thread_index]; + snat_interface_t *i; + snat_session_t *s; + u32 rx_fib_index = ip4_fib_table_get_index_for_sw_if_index (rx_sw_if_index); + u32 tx_fib_index = ip4_fib_table_get_index_for_sw_if_index (tx_sw_if_index); + + /* src NAT check */ + init_ed_k (&kv, ip->src_address, src_port, ip->dst_address, dst_port, + tx_fib_index, ip->protocol); + if (!clib_bihash_search_16_8 (&sm->flow_hash, &kv, &value)) + { + ASSERT (thread_index == ed_value_get_thread_index (&value)); + s = + pool_elt_at_index (tsm->sessions, + ed_value_get_session_index (&value)); + if (nat44_is_ses_closed (s) + && (!s->tcp_closed_timestamp || now >= s->tcp_closed_timestamp)) + { + nat_free_session_data (sm, s, thread_index, 0); + nat_ed_session_delete (sm, s, thread_index, 1); + } + return 1; + } + + /* dst NAT check */ + init_ed_k (&kv, ip->dst_address, dst_port, ip->src_address, src_port, + rx_fib_index, ip->protocol); + if (!clib_bihash_search_16_8 (&sm->flow_hash, &kv, &value)) + { + ASSERT (thread_index == ed_value_get_thread_index (&value)); + s = + pool_elt_at_index (tsm->sessions, + ed_value_get_session_index (&value)); + + if (is_fwd_bypass_session (s)) + return 0; + + /* hairpinning */ + pool_foreach (i, sm->output_feature_interfaces) + { + if ((nat_interface_is_inside (i)) && (rx_sw_if_index == i->sw_if_index)) + return 0; + } + return 1; + } + + return 0; +} + +static inline u32 +icmp_in2out_ed_slow_path (snat_main_t *sm, vlib_buffer_t *b, ip4_header_t *ip, + icmp46_header_t *icmp, u32 sw_if_index, + u32 rx_fib_index, vlib_node_runtime_t *node, + u32 next, f64 now, u32 thread_index, + nat_protocol_t nat_proto, snat_session_t **s_p) +{ + vlib_main_t *vm = vlib_get_main (); + u16 checksum; + int err; + snat_session_t *s = NULL; + u8 lookup_protocol = ip->protocol; + u16 lookup_sport, lookup_dport; + ip4_address_t lookup_saddr, lookup_daddr; + + err = nat_get_icmp_session_lookup_values (b, ip, &lookup_saddr, + &lookup_sport, &lookup_daddr, + &lookup_dport, &lookup_protocol); + if (err != 0) + { + b->error = node->errors[err]; + return NAT_NEXT_DROP; + } + + if (vnet_buffer (b)->sw_if_index[VLIB_TX] != ~0) + { + if (PREDICT_FALSE (nat44_ed_not_translate_output_feature ( + sm, ip, lookup_sport, lookup_dport, thread_index, sw_if_index, + vnet_buffer (b)->sw_if_index[VLIB_TX], now))) + { + return next; + } + } + else + { + if (PREDICT_FALSE (nat44_ed_not_translate (sm, node, sw_if_index, ip, + NAT_PROTOCOL_ICMP, + rx_fib_index, thread_index))) + { + return next; + } + } + + if (PREDICT_FALSE (icmp_type_is_error_message ( + vnet_buffer (b)->ip.reass.icmp_type_or_tcp_flags))) + { + b->error = node->errors[NAT_IN2OUT_ED_ERROR_BAD_ICMP_TYPE]; + return NAT_NEXT_DROP; + } + + next = slow_path_ed (sm, b, ip->src_address, ip->dst_address, lookup_sport, + lookup_dport, ip->protocol, rx_fib_index, &s, node, + next, thread_index, vlib_time_now (vm)); + + if (NAT_NEXT_DROP == next) + goto out; + + if (PREDICT_TRUE (!ip4_is_fragment (ip))) + { + ip_csum_t sum = ip_incremental_checksum_buffer ( + vm, b, (u8 *) icmp - (u8 *) vlib_buffer_get_current (b), + ntohs (ip->length) - ip4_header_bytes (ip), 0); + checksum = ~ip_csum_fold (sum); + if (PREDICT_FALSE (checksum != 0 && checksum != 0xffff)) + { + next = NAT_NEXT_DROP; + goto out; + } + } + +out: + if (PREDICT_TRUE (next != NAT_NEXT_DROP && s)) + { + /* Accounting */ + nat44_session_update_counters ( + s, now, vlib_buffer_length_in_chain (vm, b), thread_index); + /* Per-user LRU list maintenance */ + nat44_session_update_lru (sm, s, thread_index); + } + *s_p = s; + return next; +} + +static snat_session_t * +nat44_ed_in2out_slowpath_unknown_proto (snat_main_t *sm, vlib_buffer_t *b, + ip4_header_t *ip, u32 rx_fib_index, + u32 thread_index, f64 now, + vlib_main_t *vm, + vlib_node_runtime_t *node) +{ + clib_bihash_kv_8_8_t kv, value; + clib_bihash_kv_16_8_t s_kv, s_value; + snat_static_mapping_t *m = NULL; + snat_main_per_thread_data_t *tsm = &sm->per_thread_data[thread_index]; + snat_session_t *s = NULL; + u32 outside_fib_index = sm->outside_fib_index; + int i; + ip4_address_t new_src_addr = { 0 }; + ip4_address_t new_dst_addr = ip->dst_address; + + if (PREDICT_FALSE ( + nat44_ed_maximum_sessions_exceeded (sm, rx_fib_index, thread_index))) + { + b->error = node->errors[NAT_IN2OUT_ED_ERROR_MAX_SESSIONS_EXCEEDED]; + nat_ipfix_logging_max_sessions (thread_index, + sm->max_translations_per_thread); + nat_elog_notice (sm, "maximum sessions exceeded"); + return 0; + } + + switch (vec_len (sm->outside_fibs)) + { + case 0: + outside_fib_index = sm->outside_fib_index; + break; + case 1: + outside_fib_index = sm->outside_fibs[0].fib_index; + break; + default: + outside_fib_index = nat_outside_fib_index_lookup (sm, ip->dst_address); + break; + } + + init_nat_k (&kv, ip->src_address, 0, rx_fib_index, 0); + + /* Try to find static mapping first */ + if (!clib_bihash_search_8_8 (&sm->static_mapping_by_local, &kv, &value)) + { + m = pool_elt_at_index (sm->static_mappings, value.value); + new_src_addr = m->external_addr; + } + else + { + pool_foreach (s, tsm->sessions) + { + if (s->ext_host_addr.as_u32 == ip->dst_address.as_u32) + { + init_ed_k (&s_kv, s->out2in.addr, 0, ip->dst_address, 0, + outside_fib_index, ip->protocol); + if (clib_bihash_search_16_8 (&sm->flow_hash, &s_kv, &s_value)) + { + new_src_addr = s->out2in.addr; + } + break; + } + } + + if (!new_src_addr.as_u32) + { + for (i = 0; i < vec_len (sm->addresses); i++) + { + init_ed_k (&s_kv, sm->addresses[i].addr, 0, ip->dst_address, 0, + outside_fib_index, ip->protocol); + if (clib_bihash_search_16_8 (&sm->flow_hash, &s_kv, &s_value)) + { + new_src_addr = sm->addresses[i].addr; + } + } + } + } + + if (!new_src_addr.as_u32) + { + // could not allocate address for translation ... + return 0; + } + + s = nat_ed_session_alloc (sm, thread_index, now, ip->protocol); + if (!s) + { + b->error = node->errors[NAT_IN2OUT_ED_ERROR_MAX_SESSIONS_EXCEEDED]; + nat_elog_warn (sm, "create NAT session failed"); + return 0; + } + + nat_6t_i2o_flow_init (sm, thread_index, s, ip->src_address, 0, + ip->dst_address, 0, rx_fib_index, ip->protocol); + nat_6t_flow_saddr_rewrite_set (&s->i2o, new_src_addr.as_u32); + nat_6t_flow_txfib_rewrite_set (&s->i2o, outside_fib_index); + + // hairpinning? + int is_hairpinning = + nat44_ed_external_sm_lookup (sm, ip->dst_address, 0, NAT_PROTOCOL_OTHER, + outside_fib_index, &new_dst_addr, NULL); + s->flags |= is_hairpinning * SNAT_SESSION_FLAG_HAIRPINNING; + + nat_6t_flow_daddr_rewrite_set (&s->i2o, new_dst_addr.as_u32); + nat_6t_flow_txfib_rewrite_set (&s->i2o, outside_fib_index); + + nat_6t_o2i_flow_init (sm, thread_index, s, new_dst_addr, 0, new_src_addr, 0, + outside_fib_index, ip->protocol); + nat_6t_flow_saddr_rewrite_set (&s->o2i, ip->dst_address.as_u32); + nat_6t_flow_daddr_rewrite_set (&s->o2i, ip->src_address.as_u32); + nat_6t_flow_txfib_rewrite_set (&s->o2i, rx_fib_index); + + s->ext_host_addr.as_u32 = ip->dst_address.as_u32; + s->flags |= SNAT_SESSION_FLAG_UNKNOWN_PROTO; + s->flags |= SNAT_SESSION_FLAG_ENDPOINT_DEPENDENT; + s->out2in.addr.as_u32 = new_src_addr.as_u32; + s->out2in.fib_index = outside_fib_index; + s->in2out.addr.as_u32 = ip->src_address.as_u32; + s->in2out.fib_index = rx_fib_index; + s->in2out.port = s->out2in.port = ip->protocol; + if (m) + s->flags |= SNAT_SESSION_FLAG_STATIC_MAPPING; + + if (nat_ed_ses_i2o_flow_hash_add_del (sm, thread_index, s, 1)) + { + nat_elog_notice (sm, "in2out flow hash add failed"); + nat_ed_session_delete (sm, s, thread_index, 1); + return NULL; + } + + if (nat_ed_ses_o2i_flow_hash_add_del (sm, thread_index, s, 1)) + { + nat_elog_notice (sm, "out2in flow hash add failed"); + nat_ed_session_delete (sm, s, thread_index, 1); + return NULL; + } + + per_vrf_sessions_register_session (s, thread_index); + + /* Accounting */ + nat44_session_update_counters (s, now, vlib_buffer_length_in_chain (vm, b), + thread_index); + /* Per-user LRU list maintenance */ + nat44_session_update_lru (sm, s, thread_index); + + return s; +} + +static inline uword +nat44_ed_in2out_fast_path_node_fn_inline (vlib_main_t * vm, + vlib_node_runtime_t * node, + vlib_frame_t * frame, + int is_output_feature) +{ + u32 n_left_from, *from; + snat_main_t *sm = &snat_main; + f64 now = vlib_time_now (vm); + u32 thread_index = vm->thread_index; + snat_main_per_thread_data_t *tsm = &sm->per_thread_data[thread_index]; + u32 def_slow = is_output_feature ? NAT_NEXT_IN2OUT_ED_OUTPUT_SLOW_PATH + : NAT_NEXT_IN2OUT_ED_SLOW_PATH; + + from = vlib_frame_vector_args (frame); + n_left_from = frame->n_vectors; + + vlib_buffer_t *bufs[VLIB_FRAME_SIZE], **b = bufs; + u16 nexts[VLIB_FRAME_SIZE], *next = nexts; + vlib_get_buffers (vm, from, b, n_left_from); + + while (n_left_from > 0) + { + vlib_buffer_t *b0; + u32 sw_if_index0, rx_fib_index0, iph_offset0 = 0; + nat_protocol_t proto0; + ip4_header_t *ip0; + snat_session_t *s0 = 0; + clib_bihash_kv_16_8_t kv0, value0; + nat_translation_error_e translation_error = NAT_ED_TRNSL_ERR_SUCCESS; + nat_6t_flow_t *f = 0; + ip4_address_t lookup_saddr, lookup_daddr; + u16 lookup_sport, lookup_dport; + u8 lookup_protocol; + int lookup_skipped = 0; + + b0 = *b; + b++; + + /* Prefetch next iteration. */ + if (PREDICT_TRUE (n_left_from >= 2)) + { + vlib_buffer_t *p2; + + p2 = *b; + + vlib_prefetch_buffer_header (p2, LOAD); + + CLIB_PREFETCH (p2->data, CLIB_CACHE_LINE_BYTES, LOAD); + } + + if (is_output_feature) + { + iph_offset0 = vnet_buffer (b0)->ip.reass.save_rewrite_length; + } + + next[0] = vnet_buffer2 (b0)->nat.arc_next; + + ip0 = + (ip4_header_t *) ((u8 *) vlib_buffer_get_current (b0) + iph_offset0); + + sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX]; + rx_fib_index0 = + fib_table_get_index_for_sw_if_index (FIB_PROTOCOL_IP4, sw_if_index0); + + if (PREDICT_FALSE (ip0->ttl == 1)) + { + vnet_buffer (b0)->sw_if_index[VLIB_TX] = (u32) ~ 0; + icmp4_error_set_vnet_buffer (b0, ICMP4_time_exceeded, + ICMP4_time_exceeded_ttl_exceeded_in_transit, + 0); + next[0] = NAT_NEXT_ICMP_ERROR; + goto trace0; + } + + proto0 = ip_proto_to_nat_proto (ip0->protocol); + + if (is_output_feature) + { + if (PREDICT_FALSE + (nat_not_translate_output_feature_fwd + (sm, ip0, thread_index, now, vm, b0))) + goto trace0; + } + + if (PREDICT_FALSE (proto0 == NAT_PROTOCOL_ICMP)) + { + if (vnet_buffer (b0)->ip.reass.icmp_type_or_tcp_flags != + ICMP4_echo_request && + vnet_buffer (b0)->ip.reass.icmp_type_or_tcp_flags != + ICMP4_echo_reply && + !icmp_type_is_error_message ( + vnet_buffer (b0)->ip.reass.icmp_type_or_tcp_flags)) + { + b0->error = node->errors[NAT_IN2OUT_ED_ERROR_BAD_ICMP_TYPE]; + next[0] = NAT_NEXT_DROP; + goto trace0; + } + int err = nat_get_icmp_session_lookup_values ( + b0, ip0, &lookup_saddr, &lookup_sport, &lookup_daddr, + &lookup_dport, &lookup_protocol); + if (err != 0) + { + b0->error = node->errors[err]; + next[0] = NAT_NEXT_DROP; + goto trace0; + } + } + else + { + lookup_protocol = ip0->protocol; + lookup_saddr = ip0->src_address; + lookup_daddr = ip0->dst_address; + lookup_sport = vnet_buffer (b0)->ip.reass.l4_src_port; + lookup_dport = vnet_buffer (b0)->ip.reass.l4_dst_port; + } + + /* there might be a stashed index in vnet_buffer2 from handoff or + * classify node, see if it can be used */ + if (!pool_is_free_index (tsm->sessions, + vnet_buffer2 (b0)->nat.cached_session_index)) + { + s0 = pool_elt_at_index (tsm->sessions, + vnet_buffer2 (b0)->nat.cached_session_index); + if (PREDICT_TRUE ( + nat_6t_flow_match (&s0->i2o, b0, lookup_saddr, lookup_sport, + lookup_daddr, lookup_dport, lookup_protocol, + rx_fib_index0) + // for some hairpinning cases there are two "i2i" flows instead + // of i2o and o2i as both hosts are on inside + || (s0->flags & SNAT_SESSION_FLAG_HAIRPINNING && + nat_6t_flow_match ( + &s0->o2i, b0, lookup_saddr, lookup_sport, lookup_daddr, + lookup_dport, lookup_protocol, rx_fib_index0)))) + { + /* yes, this is the droid we're looking for */ + lookup_skipped = 1; + goto skip_lookup; + } + s0 = NULL; + } + + init_ed_k (&kv0, ip0->src_address, lookup_sport, ip0->dst_address, + lookup_dport, rx_fib_index0, lookup_protocol); + + // lookup flow + if (clib_bihash_search_16_8 (&sm->flow_hash, &kv0, &value0)) + { + // flow does not exist go slow path + next[0] = def_slow; + goto trace0; + } + + ASSERT (thread_index == ed_value_get_thread_index (&value0)); + s0 = + pool_elt_at_index (tsm->sessions, + ed_value_get_session_index (&value0)); + + skip_lookup: + + if (PREDICT_FALSE (per_vrf_sessions_is_expired (s0, thread_index))) + { + // session is closed, go slow path + nat_free_session_data (sm, s0, thread_index, 0); + nat_ed_session_delete (sm, s0, thread_index, 1); + next[0] = NAT_NEXT_OUT2IN_ED_SLOW_PATH; + goto trace0; + } + + if (s0->tcp_closed_timestamp) + { + if (now >= s0->tcp_closed_timestamp) + { + // session is closed, go slow path, freed in slow path + next[0] = def_slow; + } + else + { + // session in transitory timeout, drop + b0->error = node->errors[NAT_IN2OUT_ED_ERROR_TCP_CLOSED]; + next[0] = NAT_NEXT_DROP; + } + goto trace0; + } + + // drop if session expired + u64 sess_timeout_time; + sess_timeout_time = + s0->last_heard + (f64) nat44_session_get_timeout (sm, s0); + if (now >= sess_timeout_time) + { + nat_free_session_data (sm, s0, thread_index, 0); + nat_ed_session_delete (sm, s0, thread_index, 1); + // session is closed, go slow path + next[0] = def_slow; + goto trace0; + } + + b0->flags |= VNET_BUFFER_F_IS_NATED; + + if (nat_6t_flow_match (&s0->i2o, b0, lookup_saddr, lookup_sport, + lookup_daddr, lookup_dport, lookup_protocol, + rx_fib_index0)) + { + f = &s0->i2o; + } + else if (s0->flags & SNAT_SESSION_FLAG_HAIRPINNING && + nat_6t_flow_match (&s0->o2i, b0, lookup_saddr, lookup_sport, + lookup_daddr, lookup_dport, lookup_protocol, + rx_fib_index0)) + { + f = &s0->o2i; + } + else + { + translation_error = NAT_ED_TRNSL_ERR_FLOW_MISMATCH; + nat_free_session_data (sm, s0, thread_index, 0); + nat_ed_session_delete (sm, s0, thread_index, 1); + next[0] = NAT_NEXT_DROP; + goto trace0; + } + + if (NAT_ED_TRNSL_ERR_SUCCESS != + (translation_error = nat_6t_flow_buf_translate ( + sm, b0, ip0, f, proto0, is_output_feature))) + { + nat_free_session_data (sm, s0, thread_index, 0); + nat_ed_session_delete (sm, s0, thread_index, 1); + next[0] = NAT_NEXT_DROP; + goto trace0; + } + + switch (proto0) + { + case NAT_PROTOCOL_TCP: + vlib_increment_simple_counter (&sm->counters.fastpath.in2out.tcp, + thread_index, sw_if_index0, 1); + nat44_set_tcp_session_state_i2o (sm, now, s0, b0, thread_index); + break; + case NAT_PROTOCOL_UDP: + vlib_increment_simple_counter (&sm->counters.fastpath.in2out.udp, + thread_index, sw_if_index0, 1); + break; + case NAT_PROTOCOL_ICMP: + vlib_increment_simple_counter (&sm->counters.fastpath.in2out.icmp, + thread_index, sw_if_index0, 1); + break; + case NAT_PROTOCOL_OTHER: + vlib_increment_simple_counter (&sm->counters.fastpath.in2out.other, + thread_index, sw_if_index0, 1); + break; + } + + /* Accounting */ + nat44_session_update_counters (s0, now, + vlib_buffer_length_in_chain (vm, b0), + thread_index); + /* Per-user LRU list maintenance */ + nat44_session_update_lru (sm, s0, thread_index); + + trace0: + if (PREDICT_FALSE + ((node->flags & VLIB_NODE_FLAG_TRACE) + && (b0->flags & VLIB_BUFFER_IS_TRACED))) + { + nat_in2out_ed_trace_t *t = + vlib_add_trace (vm, node, b0, sizeof (*t)); + t->sw_if_index = sw_if_index0; + t->next_index = next[0]; + t->is_slow_path = 0; + t->translation_error = translation_error; + t->lookup_skipped = lookup_skipped; + clib_memcpy (&t->search_key, &kv0, sizeof (t->search_key)); + + if (s0) + { + t->session_index = s0 - tsm->sessions; + clib_memcpy (&t->i2of, &s0->i2o, sizeof (t->i2of)); + clib_memcpy (&t->o2if, &s0->o2i, sizeof (t->o2if)); + t->translation_via_i2of = (&s0->i2o == f); + } + else + { + t->session_index = ~0; + } + } + + if (next[0] == NAT_NEXT_DROP) + { + vlib_increment_simple_counter (&sm->counters.fastpath.in2out.drops, + thread_index, sw_if_index0, 1); + } + + n_left_from--; + next++; + } + + vlib_buffer_enqueue_to_next (vm, node, from, (u16 *) nexts, + frame->n_vectors); + return frame->n_vectors; +} + +static inline uword +nat44_ed_in2out_slow_path_node_fn_inline (vlib_main_t * vm, + vlib_node_runtime_t * node, + vlib_frame_t * frame, + int is_output_feature) +{ + u32 n_left_from, *from; + snat_main_t *sm = &snat_main; + f64 now = vlib_time_now (vm); + u32 thread_index = vm->thread_index; + snat_main_per_thread_data_t *tsm = &sm->per_thread_data[thread_index]; + + from = vlib_frame_vector_args (frame); + n_left_from = frame->n_vectors; + + vlib_buffer_t *bufs[VLIB_FRAME_SIZE], **b = bufs; + u16 nexts[VLIB_FRAME_SIZE], *next = nexts; + vlib_get_buffers (vm, from, b, n_left_from); + + while (n_left_from > 0) + { + vlib_buffer_t *b0; + u32 sw_if_index0, rx_fib_index0, iph_offset0 = 0; + nat_protocol_t proto0; + ip4_header_t *ip0; + udp_header_t *udp0; + icmp46_header_t *icmp0; + snat_session_t *s0 = 0; + clib_bihash_kv_16_8_t kv0, value0; + int translation_error = NAT_ED_TRNSL_ERR_SUCCESS; + + b0 = *b; + + if (is_output_feature) + iph_offset0 = vnet_buffer (b0)->ip.reass.save_rewrite_length; + + next[0] = vnet_buffer2 (b0)->nat.arc_next; + + ip0 = (ip4_header_t *) ((u8 *) vlib_buffer_get_current (b0) + + iph_offset0); + + sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX]; + rx_fib_index0 = + fib_table_get_index_for_sw_if_index (FIB_PROTOCOL_IP4, sw_if_index0); + + if (PREDICT_FALSE (ip0->ttl == 1)) + { + vnet_buffer (b0)->sw_if_index[VLIB_TX] = (u32) ~ 0; + icmp4_error_set_vnet_buffer (b0, ICMP4_time_exceeded, + ICMP4_time_exceeded_ttl_exceeded_in_transit, + 0); + next[0] = NAT_NEXT_ICMP_ERROR; + goto trace0; + } + + udp0 = ip4_next_header (ip0); + icmp0 = (icmp46_header_t *) udp0; + proto0 = ip_proto_to_nat_proto (ip0->protocol); + + if (PREDICT_FALSE (proto0 == NAT_PROTOCOL_OTHER)) + { + s0 = nat44_ed_in2out_slowpath_unknown_proto ( + sm, b0, ip0, rx_fib_index0, thread_index, now, vm, node); + if (!s0) + next[0] = NAT_NEXT_DROP; + + if (NAT_ED_TRNSL_ERR_SUCCESS != + (translation_error = nat_6t_flow_buf_translate ( + sm, b0, ip0, &s0->i2o, proto0, is_output_feature))) + { + goto trace0; + } + + vlib_increment_simple_counter (&sm->counters.slowpath.in2out.other, + thread_index, sw_if_index0, 1); + goto trace0; + } + + if (PREDICT_FALSE (proto0 == NAT_PROTOCOL_ICMP)) + { + next[0] = icmp_in2out_ed_slow_path (sm, b0, ip0, icmp0, sw_if_index0, + rx_fib_index0, node, next[0], + now, thread_index, proto0, &s0); + if (NAT_NEXT_DROP != next[0] && s0 && + NAT_ED_TRNSL_ERR_SUCCESS != + (translation_error = nat_6t_flow_buf_translate ( + sm, b0, ip0, &s0->i2o, proto0, is_output_feature))) + { + goto trace0; + } + + vlib_increment_simple_counter (&sm->counters.slowpath.in2out.icmp, + thread_index, sw_if_index0, 1); + goto trace0; + } + + init_ed_k (&kv0, ip0->src_address, + vnet_buffer (b0)->ip.reass.l4_src_port, ip0->dst_address, + vnet_buffer (b0)->ip.reass.l4_dst_port, rx_fib_index0, + ip0->protocol); + if (!clib_bihash_search_16_8 (&sm->flow_hash, &kv0, &value0)) + { + ASSERT (thread_index == ed_value_get_thread_index (&value0)); + s0 = + pool_elt_at_index (tsm->sessions, + ed_value_get_session_index (&value0)); + + if (s0->tcp_closed_timestamp && now >= s0->tcp_closed_timestamp) + { + nat_free_session_data (sm, s0, thread_index, 0); + nat_ed_session_delete (sm, s0, thread_index, 1); + s0 = NULL; + } + } + + if (!s0) + { + if (is_output_feature) + { + if (PREDICT_FALSE + (nat44_ed_not_translate_output_feature + (sm, ip0, vnet_buffer (b0)->ip.reass.l4_src_port, + vnet_buffer (b0)->ip.reass.l4_dst_port, thread_index, + sw_if_index0, vnet_buffer (b0)->sw_if_index[VLIB_TX], + now))) + goto trace0; + + /* + * Send DHCP packets to the ipv4 stack, or we won't + * be able to use dhcp client on the outside interface + */ + if (PREDICT_FALSE + (proto0 == NAT_PROTOCOL_UDP + && (vnet_buffer (b0)->ip.reass.l4_dst_port == + clib_host_to_net_u16 (UDP_DST_PORT_dhcp_to_server)) + && ip0->dst_address.as_u32 == 0xffffffff)) + goto trace0; + } + else + { + if (PREDICT_FALSE + (nat44_ed_not_translate + (sm, node, sw_if_index0, ip0, proto0, rx_fib_index0, + thread_index))) + goto trace0; + } + + next[0] = + slow_path_ed (sm, b0, ip0->src_address, ip0->dst_address, + vnet_buffer (b0)->ip.reass.l4_src_port, + vnet_buffer (b0)->ip.reass.l4_dst_port, + ip0->protocol, rx_fib_index0, &s0, node, next[0], + thread_index, now); + + if (PREDICT_FALSE (next[0] == NAT_NEXT_DROP)) + goto trace0; + + if (PREDICT_FALSE (!s0)) + goto trace0; + + } + + b0->flags |= VNET_BUFFER_F_IS_NATED; + + if (NAT_ED_TRNSL_ERR_SUCCESS != + (translation_error = nat_6t_flow_buf_translate ( + sm, b0, ip0, &s0->i2o, proto0, is_output_feature))) + { + nat_free_session_data (sm, s0, thread_index, 0); + nat_ed_session_delete (sm, s0, thread_index, 1); + s0 = NULL; + goto trace0; + } + + if (PREDICT_TRUE (proto0 == NAT_PROTOCOL_TCP)) + { + vlib_increment_simple_counter (&sm->counters.slowpath.in2out.tcp, + thread_index, sw_if_index0, 1); + nat44_set_tcp_session_state_i2o (sm, now, s0, b0, thread_index); + } + else + { + vlib_increment_simple_counter (&sm->counters.slowpath.in2out.udp, + thread_index, sw_if_index0, 1); + } + + /* Accounting */ + nat44_session_update_counters (s0, now, + vlib_buffer_length_in_chain + (vm, b0), thread_index); + /* Per-user LRU list maintenance */ + nat44_session_update_lru (sm, s0, thread_index); + + trace0: + if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE) + && (b0->flags & VLIB_BUFFER_IS_TRACED))) + { + nat_in2out_ed_trace_t *t = + vlib_add_trace (vm, node, b0, sizeof (*t)); + t->sw_if_index = sw_if_index0; + t->next_index = next[0]; + t->is_slow_path = 1; + t->translation_error = translation_error; + clib_memcpy (&t->search_key, &kv0, sizeof (t->search_key)); + + if (s0) + { + t->session_index = s0 - tsm->sessions; + clib_memcpy (&t->i2of, &s0->i2o, sizeof (t->i2of)); + clib_memcpy (&t->o2if, &s0->o2i, sizeof (t->o2if)); + t->translation_via_i2of = 1; + } + + else + { + t->session_index = ~0; + } + } + + if (next[0] == NAT_NEXT_DROP) + { + vlib_increment_simple_counter (&sm->counters.slowpath.in2out.drops, + thread_index, sw_if_index0, 1); + } + + n_left_from--; + next++; + b++; + } + + vlib_buffer_enqueue_to_next (vm, node, from, (u16 *) nexts, + frame->n_vectors); + + return frame->n_vectors; +} + +VLIB_NODE_FN (nat44_ed_in2out_node) (vlib_main_t * vm, + vlib_node_runtime_t * node, + vlib_frame_t * frame) +{ + return nat44_ed_in2out_fast_path_node_fn_inline (vm, node, frame, 0); +} + +VLIB_REGISTER_NODE (nat44_ed_in2out_node) = { + .name = "nat44-ed-in2out", + .vector_size = sizeof (u32), + .sibling_of = "nat-default", + .format_trace = format_nat_in2out_ed_trace, + .type = VLIB_NODE_TYPE_INTERNAL, + .n_errors = ARRAY_LEN (nat_in2out_ed_error_strings), + .error_strings = nat_in2out_ed_error_strings, + .runtime_data_bytes = sizeof (snat_runtime_t), +}; + +VLIB_NODE_FN (nat44_ed_in2out_output_node) (vlib_main_t * vm, + vlib_node_runtime_t * node, + vlib_frame_t * frame) +{ + return nat44_ed_in2out_fast_path_node_fn_inline (vm, node, frame, 1); +} + +VLIB_REGISTER_NODE (nat44_ed_in2out_output_node) = { + .name = "nat44-ed-in2out-output", + .vector_size = sizeof (u32), + .sibling_of = "nat-default", + .format_trace = format_nat_in2out_ed_trace, + .type = VLIB_NODE_TYPE_INTERNAL, + .n_errors = ARRAY_LEN (nat_in2out_ed_error_strings), + .error_strings = nat_in2out_ed_error_strings, + .runtime_data_bytes = sizeof (snat_runtime_t), +}; + +VLIB_NODE_FN (nat44_ed_in2out_slowpath_node) (vlib_main_t * vm, + vlib_node_runtime_t * + node, vlib_frame_t * frame) +{ + return nat44_ed_in2out_slow_path_node_fn_inline (vm, node, frame, 0); +} + +VLIB_REGISTER_NODE (nat44_ed_in2out_slowpath_node) = { + .name = "nat44-ed-in2out-slowpath", + .vector_size = sizeof (u32), + .sibling_of = "nat-default", + .format_trace = format_nat_in2out_ed_trace, + .type = VLIB_NODE_TYPE_INTERNAL, + .n_errors = ARRAY_LEN (nat_in2out_ed_error_strings), + .error_strings = nat_in2out_ed_error_strings, + .runtime_data_bytes = sizeof (snat_runtime_t), +}; + +VLIB_NODE_FN (nat44_ed_in2out_output_slowpath_node) (vlib_main_t * vm, + vlib_node_runtime_t + * node, + vlib_frame_t * frame) +{ + return nat44_ed_in2out_slow_path_node_fn_inline (vm, node, frame, 1); +} + +VLIB_REGISTER_NODE (nat44_ed_in2out_output_slowpath_node) = { + .name = "nat44-ed-in2out-output-slowpath", + .vector_size = sizeof (u32), + .sibling_of = "nat-default", + .format_trace = format_nat_in2out_ed_trace, + .type = VLIB_NODE_TYPE_INTERNAL, + .n_errors = ARRAY_LEN (nat_in2out_ed_error_strings), + .error_strings = nat_in2out_ed_error_strings, + .runtime_data_bytes = sizeof (snat_runtime_t), +}; + +static u8 * +format_nat_pre_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 *); + nat_pre_trace_t *t = va_arg (*args, nat_pre_trace_t *); + return format (s, "in2out next_index %d arc_next_index %d", t->next_index, + t->arc_next_index); +} + +VLIB_NODE_FN (nat_pre_in2out_node) + (vlib_main_t * vm, vlib_node_runtime_t * node, vlib_frame_t * frame) +{ + return nat_pre_node_fn_inline (vm, node, frame, + NAT_NEXT_IN2OUT_ED_FAST_PATH); +} + +VLIB_NODE_FN (nat_pre_in2out_output_node) + (vlib_main_t * vm, vlib_node_runtime_t * node, vlib_frame_t * frame) +{ + return nat_pre_node_fn_inline (vm, node, frame, + NAT_NEXT_IN2OUT_ED_OUTPUT_FAST_PATH); +} + +VLIB_REGISTER_NODE (nat_pre_in2out_node) = { + .name = "nat-pre-in2out", + .vector_size = sizeof (u32), + .sibling_of = "nat-default", + .format_trace = format_nat_pre_trace, + .type = VLIB_NODE_TYPE_INTERNAL, + .n_errors = 0, +}; + +VLIB_REGISTER_NODE (nat_pre_in2out_output_node) = { + .name = "nat-pre-in2out-output", + .vector_size = sizeof (u32), + .sibling_of = "nat-default", + .format_trace = format_nat_pre_trace, + .type = VLIB_NODE_TYPE_INTERNAL, + .n_errors = 0, +}; + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/src/plugins/nat/nat44-ed/nat44_ed_inlines.h b/src/plugins/nat/nat44-ed/nat44_ed_inlines.h new file mode 100644 index 00000000000..0623940cb6d --- /dev/null +++ b/src/plugins/nat/nat44-ed/nat44_ed_inlines.h @@ -0,0 +1,869 @@ +/* + * Copyright (c) 2018 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @brief The NAT inline functions + */ + +#ifndef __included_nat44_ed_inlines_h__ +#define __included_nat44_ed_inlines_h__ + +#include +#include +#include + +#include +#include + +always_inline u64 +calc_nat_key (ip4_address_t addr, u16 port, u32 fib_index, u8 proto) +{ + ASSERT (fib_index <= (1 << 14) - 1); + ASSERT (proto <= (1 << 3) - 1); + return (u64) addr.as_u32 << 32 | (u64) port << 16 | fib_index << 3 | + (proto & 0x7); +} + +always_inline void +split_nat_key (u64 key, ip4_address_t *addr, u16 *port, u32 *fib_index, + nat_protocol_t *proto) +{ + if (addr) + { + addr->as_u32 = key >> 32; + } + if (port) + { + *port = (key >> 16) & (u16) ~0; + } + if (fib_index) + { + *fib_index = key >> 3 & ((1 << 13) - 1); + } + if (proto) + { + *proto = key & 0x7; + } +} + +always_inline void +init_nat_k (clib_bihash_kv_8_8_t *kv, ip4_address_t addr, u16 port, + u32 fib_index, nat_protocol_t proto) +{ + kv->key = calc_nat_key (addr, port, fib_index, proto); + kv->value = ~0ULL; +} + +always_inline void +init_nat_kv (clib_bihash_kv_8_8_t *kv, ip4_address_t addr, u16 port, + u32 fib_index, nat_protocol_t proto, u32 thread_index, + u32 session_index) +{ + init_nat_k (kv, addr, port, fib_index, proto); + kv->value = (u64) thread_index << 32 | session_index; +} + +always_inline void +init_nat_i2o_k (clib_bihash_kv_8_8_t *kv, snat_session_t *s) +{ + return init_nat_k (kv, s->in2out.addr, s->in2out.port, s->in2out.fib_index, + s->nat_proto); +} + +always_inline void +init_nat_i2o_kv (clib_bihash_kv_8_8_t *kv, snat_session_t *s, u32 thread_index, + u32 session_index) +{ + init_nat_k (kv, s->in2out.addr, s->in2out.port, s->in2out.fib_index, + s->nat_proto); + kv->value = (u64) thread_index << 32 | session_index; +} + +always_inline void +init_nat_o2i_k (clib_bihash_kv_8_8_t *kv, snat_session_t *s) +{ + return init_nat_k (kv, s->out2in.addr, s->out2in.port, s->out2in.fib_index, + s->nat_proto); +} + +always_inline void +init_nat_o2i_kv (clib_bihash_kv_8_8_t *kv, snat_session_t *s, u32 thread_index, + u32 session_index) +{ + init_nat_k (kv, s->out2in.addr, s->out2in.port, s->out2in.fib_index, + s->nat_proto); + kv->value = (u64) thread_index << 32 | session_index; +} + +always_inline u32 +nat_value_get_thread_index (clib_bihash_kv_8_8_t *value) +{ + return value->value >> 32; +} + +always_inline u32 +nat_value_get_session_index (clib_bihash_kv_8_8_t *value) +{ + return value->value & ~(u32) 0; +} + +always_inline void +init_ed_k (clib_bihash_kv_16_8_t *kv, ip4_address_t l_addr, u16 l_port, + ip4_address_t r_addr, u16 r_port, u32 fib_index, u8 proto) +{ + kv->key[0] = (u64) r_addr.as_u32 << 32 | l_addr.as_u32; + kv->key[1] = + (u64) r_port << 48 | (u64) l_port << 32 | fib_index << 8 | proto; +} + +always_inline void +init_ed_kv (clib_bihash_kv_16_8_t *kv, ip4_address_t l_addr, u16 l_port, + ip4_address_t r_addr, u16 r_port, u32 fib_index, u8 proto, + u32 thread_index, u32 session_index) +{ + init_ed_k (kv, l_addr, l_port, r_addr, r_port, fib_index, proto); + kv->value = (u64) thread_index << 32 | session_index; +} + +always_inline u32 +ed_value_get_thread_index (clib_bihash_kv_16_8_t *value) +{ + return value->value >> 32; +} + +always_inline u32 +ed_value_get_session_index (clib_bihash_kv_16_8_t *value) +{ + return value->value & ~(u32) 0; +} + +always_inline void +split_ed_kv (clib_bihash_kv_16_8_t *kv, ip4_address_t *l_addr, + ip4_address_t *r_addr, u8 *proto, u32 *fib_index, u16 *l_port, + u16 *r_port) +{ + if (l_addr) + { + l_addr->as_u32 = kv->key[0] & (u32) ~0; + } + if (r_addr) + { + r_addr->as_u32 = kv->key[0] >> 32; + } + if (r_port) + { + *r_port = kv->key[1] >> 48; + } + if (l_port) + { + *l_port = (kv->key[1] >> 32) & (u16) ~0; + } + if (fib_index) + { + *fib_index = (kv->key[1] >> 8) & ((1 << 24) - 1); + } + if (proto) + { + *proto = kv->key[1] & (u8) ~0; + } +} + +static_always_inline int +nat_get_icmp_session_lookup_values (vlib_buffer_t *b, ip4_header_t *ip0, + ip4_address_t *lookup_saddr, + u16 *lookup_sport, + ip4_address_t *lookup_daddr, + u16 *lookup_dport, u8 *lookup_protocol) +{ + icmp46_header_t *icmp0; + icmp_echo_header_t *echo0, *inner_echo0 = 0; + ip4_header_t *inner_ip0 = 0; + void *l4_header = 0; + icmp46_header_t *inner_icmp0; + + icmp0 = (icmp46_header_t *) ip4_next_header (ip0); + echo0 = (icmp_echo_header_t *) (icmp0 + 1); + + // avoid warning about unused variables in caller by setting to bogus values + *lookup_sport = 0; + *lookup_dport = 0; + + if (!icmp_type_is_error_message ( + vnet_buffer (b)->ip.reass.icmp_type_or_tcp_flags)) + { + *lookup_protocol = IP_PROTOCOL_ICMP; + lookup_saddr->as_u32 = ip0->src_address.as_u32; + *lookup_sport = vnet_buffer (b)->ip.reass.l4_src_port; + lookup_daddr->as_u32 = ip0->dst_address.as_u32; + *lookup_dport = vnet_buffer (b)->ip.reass.l4_dst_port; + } + else + { + inner_ip0 = (ip4_header_t *) (echo0 + 1); + l4_header = ip4_next_header (inner_ip0); + *lookup_protocol = inner_ip0->protocol; + lookup_saddr->as_u32 = inner_ip0->dst_address.as_u32; + lookup_daddr->as_u32 = inner_ip0->src_address.as_u32; + switch (ip_proto_to_nat_proto (inner_ip0->protocol)) + { + case NAT_PROTOCOL_ICMP: + inner_icmp0 = (icmp46_header_t *) l4_header; + inner_echo0 = (icmp_echo_header_t *) (inner_icmp0 + 1); + *lookup_sport = inner_echo0->identifier; + *lookup_dport = inner_echo0->identifier; + break; + case NAT_PROTOCOL_UDP: + case NAT_PROTOCOL_TCP: + *lookup_sport = ((tcp_udp_header_t *) l4_header)->dst_port; + *lookup_dport = ((tcp_udp_header_t *) l4_header)->src_port; + break; + default: + return NAT_IN2OUT_ED_ERROR_UNSUPPORTED_PROTOCOL; + } + } + return 0; +} + +always_inline u32 +nat44_session_get_timeout (snat_main_t *sm, snat_session_t *s) +{ + switch (s->nat_proto) + { + case NAT_PROTOCOL_ICMP: + return sm->timeouts.icmp; + case NAT_PROTOCOL_UDP: + return sm->timeouts.udp; + case NAT_PROTOCOL_TCP: + { + if (s->state) + return sm->timeouts.tcp.transitory; + else + return sm->timeouts.tcp.established; + } + default: + return sm->timeouts.udp; + } + + return 0; +} + +static_always_inline u8 +nat44_ed_maximum_sessions_exceeded (snat_main_t *sm, u32 fib_index, + u32 thread_index) +{ + u32 translations; + translations = pool_elts (sm->per_thread_data[thread_index].sessions); + if (vec_len (sm->max_translations_per_fib) <= fib_index) + fib_index = 0; + return translations >= sm->max_translations_per_fib[fib_index]; +} + +static_always_inline int +nat_ed_lru_insert (snat_main_per_thread_data_t *tsm, snat_session_t *s, + f64 now, u8 proto) +{ + dlist_elt_t *lru_list_elt; + pool_get (tsm->lru_pool, lru_list_elt); + s->lru_index = lru_list_elt - tsm->lru_pool; + switch (proto) + { + case IP_PROTOCOL_UDP: + s->lru_head_index = tsm->udp_lru_head_index; + break; + case IP_PROTOCOL_TCP: + s->lru_head_index = tsm->tcp_trans_lru_head_index; + break; + case IP_PROTOCOL_ICMP: + s->lru_head_index = tsm->icmp_lru_head_index; + break; + default: + s->lru_head_index = tsm->unk_proto_lru_head_index; + break; + } + clib_dlist_addtail (tsm->lru_pool, s->lru_head_index, s->lru_index); + lru_list_elt->value = s - tsm->sessions; + s->last_lru_update = now; + return 1; +} + +static_always_inline void +nat_6t_flow_to_ed_k (clib_bihash_kv_16_8_t *kv, nat_6t_flow_t *f) +{ + init_ed_k (kv, f->match.saddr, f->match.sport, f->match.daddr, + f->match.dport, f->match.fib_index, f->match.proto); +} + +static_always_inline void +nat_6t_flow_to_ed_kv (clib_bihash_kv_16_8_t *kv, nat_6t_flow_t *f, + u32 thread_idx, u32 session_idx) +{ + init_ed_kv (kv, f->match.saddr, f->match.sport, f->match.daddr, + f->match.dport, f->match.fib_index, f->match.proto, thread_idx, + session_idx); +} + +static_always_inline int +nat_ed_ses_i2o_flow_hash_add_del (snat_main_t *sm, u32 thread_idx, + snat_session_t *s, int is_add) +{ + snat_main_per_thread_data_t *tsm = + vec_elt_at_index (sm->per_thread_data, thread_idx); + clib_bihash_kv_16_8_t kv; + if (0 == is_add) + { + nat_6t_flow_to_ed_k (&kv, &s->i2o); + } + else + { + nat_6t_flow_to_ed_kv (&kv, &s->i2o, thread_idx, s - tsm->sessions); + nat_6t_l3_l4_csum_calc (&s->i2o); + } + return clib_bihash_add_del_16_8 (&sm->flow_hash, &kv, is_add); +} + +static_always_inline int +nat_ed_ses_o2i_flow_hash_add_del (snat_main_t *sm, u32 thread_idx, + snat_session_t *s, int is_add) +{ + snat_main_per_thread_data_t *tsm = + vec_elt_at_index (sm->per_thread_data, thread_idx); + clib_bihash_kv_16_8_t kv; + if (0 == is_add) + { + nat_6t_flow_to_ed_k (&kv, &s->o2i); + } + else + { + nat_6t_flow_to_ed_kv (&kv, &s->o2i, thread_idx, s - tsm->sessions); + nat_6t_l3_l4_csum_calc (&s->o2i); + } + return clib_bihash_add_del_16_8 (&sm->flow_hash, &kv, is_add); +} + +always_inline void +nat_ed_session_delete (snat_main_t *sm, snat_session_t *ses, u32 thread_index, + int lru_delete + /* delete from global LRU list */) +{ + snat_main_per_thread_data_t *tsm = + vec_elt_at_index (sm->per_thread_data, thread_index); + + if (lru_delete) + { + clib_dlist_remove (tsm->lru_pool, ses->lru_index); + } + pool_put_index (tsm->lru_pool, ses->lru_index); + if (nat_ed_ses_i2o_flow_hash_add_del (sm, thread_index, ses, 0)) + nat_elog_warn (sm, "flow hash del failed"); + if (nat_ed_ses_o2i_flow_hash_add_del (sm, thread_index, ses, 0)) + nat_elog_warn (sm, "flow hash del failed"); + pool_put (tsm->sessions, ses); + vlib_set_simple_counter (&sm->total_sessions, thread_index, 0, + pool_elts (tsm->sessions)); +} + +static_always_inline int +nat_lru_free_one_with_head (snat_main_t *sm, int thread_index, f64 now, + u32 head_index) +{ + snat_session_t *s = NULL; + dlist_elt_t *oldest_elt; + f64 sess_timeout_time; + u32 oldest_index; + snat_main_per_thread_data_t *tsm = &sm->per_thread_data[thread_index]; + oldest_index = clib_dlist_remove_head (tsm->lru_pool, head_index); + if (~0 != oldest_index) + { + oldest_elt = pool_elt_at_index (tsm->lru_pool, oldest_index); + s = pool_elt_at_index (tsm->sessions, oldest_elt->value); + + sess_timeout_time = + s->last_heard + (f64) nat44_session_get_timeout (sm, s); + if (now >= sess_timeout_time || + (s->tcp_closed_timestamp && now >= s->tcp_closed_timestamp)) + { + nat_free_session_data (sm, s, thread_index, 0); + nat_ed_session_delete (sm, s, thread_index, 0); + return 1; + } + else + { + clib_dlist_addhead (tsm->lru_pool, head_index, oldest_index); + } + } + return 0; +} + +static_always_inline int +nat_lru_free_one (snat_main_t *sm, int thread_index, f64 now) +{ + snat_main_per_thread_data_t *tsm = &sm->per_thread_data[thread_index]; + int rc = 0; +#define _(p) \ + if ((rc = nat_lru_free_one_with_head (sm, thread_index, now, \ + tsm->p##_lru_head_index))) \ + { \ + return rc; \ + } + _ (tcp_trans); + _ (udp); + _ (unk_proto); + _ (icmp); + _ (tcp_estab); +#undef _ + return 0; +} + +static_always_inline snat_session_t * +nat_ed_session_alloc (snat_main_t *sm, u32 thread_index, f64 now, u8 proto) +{ + snat_session_t *s; + snat_main_per_thread_data_t *tsm = &sm->per_thread_data[thread_index]; + + nat_lru_free_one (sm, thread_index, now); + + pool_get (tsm->sessions, s); + clib_memset (s, 0, sizeof (*s)); + + nat_ed_lru_insert (tsm, s, now, proto); + + s->ha_last_refreshed = now; + vlib_set_simple_counter (&sm->total_sessions, thread_index, 0, + pool_elts (tsm->sessions)); + return s; +} + +// slow path +static_always_inline void +per_vrf_sessions_cleanup (u32 thread_index) +{ + snat_main_t *sm = &snat_main; + snat_main_per_thread_data_t *tsm = + vec_elt_at_index (sm->per_thread_data, thread_index); + per_vrf_sessions_t *per_vrf_sessions; + u32 *to_free = 0, *i; + + vec_foreach (per_vrf_sessions, tsm->per_vrf_sessions_vec) + { + if (per_vrf_sessions->expired) + { + if (per_vrf_sessions->ses_count == 0) + { + vec_add1 (to_free, per_vrf_sessions - tsm->per_vrf_sessions_vec); + } + } + } + + if (vec_len (to_free)) + { + vec_foreach (i, to_free) + { + vec_del1 (tsm->per_vrf_sessions_vec, *i); + } + } + + vec_free (to_free); +} + +// slow path +static_always_inline void +per_vrf_sessions_register_session (snat_session_t *s, u32 thread_index) +{ + snat_main_t *sm = &snat_main; + snat_main_per_thread_data_t *tsm = + vec_elt_at_index (sm->per_thread_data, thread_index); + per_vrf_sessions_t *per_vrf_sessions; + + per_vrf_sessions_cleanup (thread_index); + + // s->per_vrf_sessions_index == ~0 ... reuse of old session + + vec_foreach (per_vrf_sessions, tsm->per_vrf_sessions_vec) + { + // ignore already expired registrations + if (per_vrf_sessions->expired) + continue; + + if ((s->in2out.fib_index == per_vrf_sessions->rx_fib_index) && + (s->out2in.fib_index == per_vrf_sessions->tx_fib_index)) + { + goto done; + } + if ((s->in2out.fib_index == per_vrf_sessions->tx_fib_index) && + (s->out2in.fib_index == per_vrf_sessions->rx_fib_index)) + { + goto done; + } + } + + // create a new registration + vec_add2 (tsm->per_vrf_sessions_vec, per_vrf_sessions, 1); + clib_memset (per_vrf_sessions, 0, sizeof (*per_vrf_sessions)); + + per_vrf_sessions->rx_fib_index = s->in2out.fib_index; + per_vrf_sessions->tx_fib_index = s->out2in.fib_index; + +done: + s->per_vrf_sessions_index = per_vrf_sessions - tsm->per_vrf_sessions_vec; + per_vrf_sessions->ses_count++; +} + +// fast path +static_always_inline void +per_vrf_sessions_unregister_session (snat_session_t *s, u32 thread_index) +{ + snat_main_t *sm = &snat_main; + snat_main_per_thread_data_t *tsm; + per_vrf_sessions_t *per_vrf_sessions; + + ASSERT (s->per_vrf_sessions_index != ~0); + + tsm = vec_elt_at_index (sm->per_thread_data, thread_index); + per_vrf_sessions = + vec_elt_at_index (tsm->per_vrf_sessions_vec, s->per_vrf_sessions_index); + + ASSERT (per_vrf_sessions->ses_count != 0); + + per_vrf_sessions->ses_count--; + s->per_vrf_sessions_index = ~0; +} + +// fast path +static_always_inline u8 +per_vrf_sessions_is_expired (snat_session_t *s, u32 thread_index) +{ + snat_main_t *sm = &snat_main; + snat_main_per_thread_data_t *tsm; + per_vrf_sessions_t *per_vrf_sessions; + + ASSERT (s->per_vrf_sessions_index != ~0); + + tsm = vec_elt_at_index (sm->per_thread_data, thread_index); + per_vrf_sessions = + vec_elt_at_index (tsm->per_vrf_sessions_vec, s->per_vrf_sessions_index); + return per_vrf_sessions->expired; +} + +static_always_inline void +nat_6t_flow_init (nat_6t_flow_t *f, u32 thread_idx, ip4_address_t saddr, + u16 sport, ip4_address_t daddr, u16 dport, u32 fib_index, + u8 proto, u32 session_idx) +{ + clib_memset (f, 0, sizeof (*f)); + f->match.saddr = saddr; + f->match.sport = sport; + f->match.daddr = daddr; + f->match.dport = dport; + f->match.proto = proto; + f->match.fib_index = fib_index; +} + +static_always_inline void +nat_6t_i2o_flow_init (snat_main_t *sm, u32 thread_idx, snat_session_t *s, + ip4_address_t saddr, u16 sport, ip4_address_t daddr, + u16 dport, u32 fib_index, u8 proto) +{ + snat_main_per_thread_data_t *tsm = + vec_elt_at_index (sm->per_thread_data, thread_idx); + nat_6t_flow_init (&s->i2o, thread_idx, saddr, sport, daddr, dport, fib_index, + proto, s - tsm->sessions); +} + +static_always_inline void +nat_6t_o2i_flow_init (snat_main_t *sm, u32 thread_idx, snat_session_t *s, + ip4_address_t saddr, u16 sport, ip4_address_t daddr, + u16 dport, u32 fib_index, u8 proto) +{ + snat_main_per_thread_data_t *tsm = + vec_elt_at_index (sm->per_thread_data, thread_idx); + nat_6t_flow_init (&s->o2i, thread_idx, saddr, sport, daddr, dport, fib_index, + proto, s - tsm->sessions); +} + +static_always_inline int +nat_6t_flow_match (nat_6t_flow_t *f, vlib_buffer_t *b, ip4_address_t saddr, + u16 sport, ip4_address_t daddr, u16 dport, u8 protocol, + u32 fib_index) +{ + return f->match.daddr.as_u32 == daddr.as_u32 && + f->match.dport == vnet_buffer (b)->ip.reass.l4_dst_port && + f->match.proto == protocol && f->match.fib_index == fib_index && + f->match.saddr.as_u32 == saddr.as_u32 && + f->match.sport == vnet_buffer (b)->ip.reass.l4_src_port; +} + +static inline uword +nat_pre_node_fn_inline (vlib_main_t *vm, vlib_node_runtime_t *node, + vlib_frame_t *frame, u32 def_next) +{ + u32 n_left_from, *from; + + from = vlib_frame_vector_args (frame); + n_left_from = frame->n_vectors; + + vlib_buffer_t *bufs[VLIB_FRAME_SIZE], **b = bufs; + u16 nexts[VLIB_FRAME_SIZE], *next = nexts; + vlib_get_buffers (vm, from, b, n_left_from); + + while (n_left_from >= 2) + { + u32 next0, next1; + u32 arc_next0, arc_next1; + vlib_buffer_t *b0, *b1; + + b0 = *b; + b++; + b1 = *b; + b++; + + /* Prefetch next iteration. */ + if (PREDICT_TRUE (n_left_from >= 4)) + { + vlib_buffer_t *p2, *p3; + + p2 = *b; + p3 = *(b + 1); + + vlib_prefetch_buffer_header (p2, LOAD); + vlib_prefetch_buffer_header (p3, LOAD); + + CLIB_PREFETCH (p2->data, CLIB_CACHE_LINE_BYTES, LOAD); + CLIB_PREFETCH (p3->data, CLIB_CACHE_LINE_BYTES, LOAD); + } + + next0 = def_next; + next1 = def_next; + + vnet_feature_next (&arc_next0, b0); + vnet_feature_next (&arc_next1, b1); + + vnet_buffer2 (b0)->nat.arc_next = arc_next0; + vnet_buffer2 (b1)->nat.arc_next = arc_next1; + + if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE))) + { + if (b0->flags & VLIB_BUFFER_IS_TRACED) + { + nat_pre_trace_t *t = vlib_add_trace (vm, node, b0, sizeof (*t)); + t->next_index = next0; + t->arc_next_index = arc_next0; + } + if (b1->flags & VLIB_BUFFER_IS_TRACED) + { + nat_pre_trace_t *t = vlib_add_trace (vm, node, b0, sizeof (*t)); + t->next_index = next1; + t->arc_next_index = arc_next1; + } + } + + n_left_from -= 2; + next[0] = next0; + next[1] = next1; + next += 2; + } + + while (n_left_from > 0) + { + u32 next0; + u32 arc_next0; + vlib_buffer_t *b0; + + b0 = *b; + b++; + + next0 = def_next; + vnet_feature_next (&arc_next0, b0); + vnet_buffer2 (b0)->nat.arc_next = arc_next0; + + if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE) && + (b0->flags & VLIB_BUFFER_IS_TRACED))) + { + nat_pre_trace_t *t = vlib_add_trace (vm, node, b0, sizeof (*t)); + t->next_index = next0; + t->arc_next_index = arc_next0; + } + + n_left_from--; + next[0] = next0; + next++; + } + vlib_buffer_enqueue_to_next (vm, node, from, (u16 *) nexts, + frame->n_vectors); + + return frame->n_vectors; +} + +static_always_inline u16 +snat_random_port (u16 min, u16 max) +{ + snat_main_t *sm = &snat_main; + u32 rwide; + u16 r; + + rwide = random_u32 (&sm->random_seed); + r = rwide & 0xFFFF; + if (r >= min && r <= max) + return r; + + return min + (rwide % (max - min + 1)); +} + +always_inline u8 +is_interface_addr (snat_main_t *sm, vlib_node_runtime_t *node, + u32 sw_if_index0, u32 ip4_addr) +{ + snat_runtime_t *rt = (snat_runtime_t *) node->runtime_data; + ip4_address_t *first_int_addr; + + if (PREDICT_FALSE (rt->cached_sw_if_index != sw_if_index0)) + { + first_int_addr = ip4_interface_first_address ( + sm->ip4_main, sw_if_index0, 0 /* just want the address */); + rt->cached_sw_if_index = sw_if_index0; + if (first_int_addr) + rt->cached_ip4_address = first_int_addr->as_u32; + else + rt->cached_ip4_address = 0; + } + + if (PREDICT_FALSE (ip4_addr == rt->cached_ip4_address)) + return 1; + else + return 0; +} + +always_inline void +nat44_set_tcp_session_state_i2o (snat_main_t *sm, f64 now, snat_session_t *ses, + vlib_buffer_t *b, u32 thread_index) +{ + snat_main_per_thread_data_t *tsm = &sm->per_thread_data[thread_index]; + u8 tcp_flags = vnet_buffer (b)->ip.reass.icmp_type_or_tcp_flags; + u32 tcp_ack_number = vnet_buffer (b)->ip.reass.tcp_ack_number; + u32 tcp_seq_number = vnet_buffer (b)->ip.reass.tcp_seq_number; + if ((ses->state == 0) && (tcp_flags & TCP_FLAG_RST)) + ses->state = NAT44_SES_RST; + if ((ses->state == NAT44_SES_RST) && !(tcp_flags & TCP_FLAG_RST)) + ses->state = 0; + if ((tcp_flags & TCP_FLAG_ACK) && (ses->state & NAT44_SES_I2O_SYN) && + (ses->state & NAT44_SES_O2I_SYN)) + ses->state = 0; + if (tcp_flags & TCP_FLAG_SYN) + ses->state |= NAT44_SES_I2O_SYN; + if (tcp_flags & TCP_FLAG_FIN) + { + ses->i2o_fin_seq = clib_net_to_host_u32 (tcp_seq_number); + ses->state |= NAT44_SES_I2O_FIN; + } + if ((tcp_flags & TCP_FLAG_ACK) && (ses->state & NAT44_SES_O2I_FIN)) + { + if (clib_net_to_host_u32 (tcp_ack_number) > ses->o2i_fin_seq) + { + ses->state |= NAT44_SES_O2I_FIN_ACK; + if (nat44_is_ses_closed (ses)) + { // if session is now closed, save the timestamp + ses->tcp_closed_timestamp = now + sm->timeouts.tcp.transitory; + ses->last_lru_update = now; + } + } + } + + // move the session to proper LRU + if (ses->state) + { + ses->lru_head_index = tsm->tcp_trans_lru_head_index; + } + else + { + ses->lru_head_index = tsm->tcp_estab_lru_head_index; + } + clib_dlist_remove (tsm->lru_pool, ses->lru_index); + clib_dlist_addtail (tsm->lru_pool, ses->lru_head_index, ses->lru_index); +} + +always_inline void +nat44_set_tcp_session_state_o2i (snat_main_t *sm, f64 now, snat_session_t *ses, + u8 tcp_flags, u32 tcp_ack_number, + u32 tcp_seq_number, u32 thread_index) +{ + snat_main_per_thread_data_t *tsm = &sm->per_thread_data[thread_index]; + if ((ses->state == 0) && (tcp_flags & TCP_FLAG_RST)) + ses->state = NAT44_SES_RST; + if ((ses->state == NAT44_SES_RST) && !(tcp_flags & TCP_FLAG_RST)) + ses->state = 0; + if ((tcp_flags & TCP_FLAG_ACK) && (ses->state & NAT44_SES_I2O_SYN) && + (ses->state & NAT44_SES_O2I_SYN)) + ses->state = 0; + if (tcp_flags & TCP_FLAG_SYN) + ses->state |= NAT44_SES_O2I_SYN; + if (tcp_flags & TCP_FLAG_FIN) + { + ses->o2i_fin_seq = clib_net_to_host_u32 (tcp_seq_number); + ses->state |= NAT44_SES_O2I_FIN; + } + if ((tcp_flags & TCP_FLAG_ACK) && (ses->state & NAT44_SES_I2O_FIN)) + { + if (clib_net_to_host_u32 (tcp_ack_number) > ses->i2o_fin_seq) + ses->state |= NAT44_SES_I2O_FIN_ACK; + if (nat44_is_ses_closed (ses)) + { // if session is now closed, save the timestamp + ses->tcp_closed_timestamp = now + sm->timeouts.tcp.transitory; + ses->last_lru_update = now; + } + } + // move the session to proper LRU + if (ses->state) + { + ses->lru_head_index = tsm->tcp_trans_lru_head_index; + } + else + { + ses->lru_head_index = tsm->tcp_estab_lru_head_index; + } + clib_dlist_remove (tsm->lru_pool, ses->lru_index); + clib_dlist_addtail (tsm->lru_pool, ses->lru_head_index, ses->lru_index); +} + +always_inline void +nat44_session_update_counters (snat_session_t *s, f64 now, uword bytes, + u32 thread_index) +{ + s->last_heard = now; + s->total_pkts++; + s->total_bytes += bytes; +} + +/** \brief Per-user LRU list maintenance */ +always_inline void +nat44_session_update_lru (snat_main_t *sm, snat_session_t *s, u32 thread_index) +{ + /* don't update too often - timeout is in magnitude of seconds anyway */ + if (s->last_heard > s->last_lru_update + 1) + { + clib_dlist_remove (sm->per_thread_data[thread_index].lru_pool, + s->lru_index); + clib_dlist_addtail (sm->per_thread_data[thread_index].lru_pool, + s->lru_head_index, s->lru_index); + s->last_lru_update = s->last_heard; + } +} + +#endif /* __included_nat44_ed_inlines_h__ */ + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/src/plugins/nat/nat44-ed/nat44_ed_out2in.c b/src/plugins/nat/nat44-ed/nat44_ed_out2in.c new file mode 100644 index 00000000000..4d354d3e8ec --- /dev/null +++ b/src/plugins/nat/nat44-ed/nat44_ed_out2in.c @@ -0,0 +1,1443 @@ +/* + * Copyright (c) 2018 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file + * @brief NAT44 endpoint-dependent outside to inside network translation + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +static char *nat_out2in_ed_error_strings[] = { +#define _(sym,string) string, + foreach_nat_out2in_ed_error +#undef _ +}; + +typedef struct +{ + u32 sw_if_index; + u32 next_index; + u32 session_index; + nat_translation_error_e translation_error; + nat_6t_flow_t i2of; + nat_6t_flow_t o2if; + clib_bihash_kv_16_8_t search_key; + u8 is_slow_path; + u8 translation_via_i2of; + u8 lookup_skipped; +} nat44_ed_out2in_trace_t; + +static u8 * +format_nat44_ed_out2in_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 *); + nat44_ed_out2in_trace_t *t = va_arg (*args, nat44_ed_out2in_trace_t *); + char *tag; + + tag = + t->is_slow_path ? "NAT44_OUT2IN_ED_SLOW_PATH" : + "NAT44_OUT2IN_ED_FAST_PATH"; + + s = format (s, "%s: sw_if_index %d, next index %d, session %d", tag, + t->sw_if_index, t->next_index, t->session_index); + if (~0 != t->session_index) + { + s = format (s, ", translation result '%U' via %s", + format_nat_ed_translation_error, t->translation_error, + t->translation_via_i2of ? "i2of" : "o2if"); + s = format (s, "\n i2of %U", format_nat_6t_flow, &t->i2of); + s = format (s, "\n o2if %U", format_nat_6t_flow, &t->o2if); + } + if (!t->is_slow_path) + { + if (t->lookup_skipped) + { + s = format (s, "\n lookup skipped - cached session index used"); + } + else + { + s = format (s, "\n search key %U", format_ed_session_kvp, + &t->search_key); + } + } + + return s; +} + +static int +next_src_nat (snat_main_t *sm, ip4_header_t *ip, u16 src_port, u16 dst_port, + u32 thread_index, u32 rx_fib_index) +{ + clib_bihash_kv_16_8_t kv, value; + + init_ed_k (&kv, ip->src_address, src_port, ip->dst_address, dst_port, + rx_fib_index, ip->protocol); + if (!clib_bihash_search_16_8 (&sm->flow_hash, &kv, &value)) + return 1; + + return 0; +} + +static void create_bypass_for_fwd (snat_main_t *sm, vlib_buffer_t *b, + snat_session_t *s, ip4_header_t *ip, + u32 rx_fib_index, u32 thread_index); + +static snat_session_t *create_session_for_static_mapping_ed ( + snat_main_t *sm, vlib_buffer_t *b, ip4_address_t i2o_addr, u16 i2o_port, + u32 i2o_fib_index, ip4_address_t o2i_addr, u16 o2i_port, u32 o2i_fib_index, + nat_protocol_t nat_proto, vlib_node_runtime_t *node, u32 rx_fib_index, + u32 thread_index, twice_nat_type_t twice_nat, lb_nat_type_t lb_nat, f64 now, + snat_static_mapping_t *mapping); + +static inline u32 +icmp_out2in_ed_slow_path (snat_main_t *sm, vlib_buffer_t *b, ip4_header_t *ip, + icmp46_header_t *icmp, u32 sw_if_index, + u32 rx_fib_index, vlib_node_runtime_t *node, + u32 next, f64 now, u32 thread_index, + snat_session_t **s_p) +{ + vlib_main_t *vm = vlib_get_main (); + + ip_csum_t sum; + u16 checksum; + + snat_session_t *s = 0; + u8 is_addr_only, identity_nat; + ip4_address_t sm_addr; + u16 sm_port; + u32 sm_fib_index; + snat_static_mapping_t *m; + u8 lookup_protocol; + ip4_address_t lookup_saddr, lookup_daddr; + u16 lookup_sport, lookup_dport; + + sw_if_index = vnet_buffer (b)->sw_if_index[VLIB_RX]; + rx_fib_index = ip4_fib_table_get_index_for_sw_if_index (sw_if_index); + + if (nat_get_icmp_session_lookup_values (b, ip, &lookup_saddr, &lookup_sport, + &lookup_daddr, &lookup_dport, + &lookup_protocol)) + { + b->error = node->errors[NAT_OUT2IN_ED_ERROR_UNSUPPORTED_PROTOCOL]; + next = NAT_NEXT_DROP; + goto out; + } + + if (snat_static_mapping_match ( + sm, ip->dst_address, lookup_sport, rx_fib_index, + ip_proto_to_nat_proto (ip->protocol), &sm_addr, &sm_port, + &sm_fib_index, 1, &is_addr_only, 0, 0, 0, &identity_nat, &m)) + { + // static mapping not matched + if (!sm->forwarding_enabled) + { + /* Don't NAT packet aimed at the intfc address */ + if (!is_interface_addr (sm, node, sw_if_index, + ip->dst_address.as_u32)) + { + b->error = node->errors[NAT_OUT2IN_ED_ERROR_NO_TRANSLATION]; + next = NAT_NEXT_DROP; + } + } + else + { + if (next_src_nat (sm, ip, lookup_sport, lookup_dport, thread_index, + rx_fib_index)) + { + next = NAT_NEXT_IN2OUT_ED_FAST_PATH; + } + else + { + create_bypass_for_fwd (sm, b, s, ip, rx_fib_index, thread_index); + } + } + goto out; + } + + if (PREDICT_FALSE (vnet_buffer (b)->ip.reass.icmp_type_or_tcp_flags != + ICMP4_echo_reply && + (vnet_buffer (b)->ip.reass.icmp_type_or_tcp_flags != + ICMP4_echo_request || + !is_addr_only))) + { + b->error = node->errors[NAT_OUT2IN_ED_ERROR_BAD_ICMP_TYPE]; + next = NAT_NEXT_DROP; + goto out; + } + + if (PREDICT_FALSE (identity_nat)) + { + goto out; + } + + /* Create session initiated by host from external network */ + s = create_session_for_static_mapping_ed ( + sm, b, sm_addr, sm_port, sm_fib_index, ip->dst_address, lookup_sport, + rx_fib_index, ip_proto_to_nat_proto (lookup_protocol), node, rx_fib_index, + thread_index, 0, 0, vlib_time_now (vm), m); + if (!s) + next = NAT_NEXT_DROP; + + if (PREDICT_TRUE (!ip4_is_fragment (ip))) + { + sum = ip_incremental_checksum_buffer ( + vm, b, (u8 *) icmp - (u8 *) vlib_buffer_get_current (b), + ntohs (ip->length) - ip4_header_bytes (ip), 0); + checksum = ~ip_csum_fold (sum); + if (checksum != 0 && checksum != 0xffff) + { + next = NAT_NEXT_DROP; + goto out; + } + } + + if (PREDICT_TRUE (next != NAT_NEXT_DROP && s)) + { + /* Accounting */ + nat44_session_update_counters ( + s, now, vlib_buffer_length_in_chain (vm, b), thread_index); + /* Per-user LRU list maintenance */ + nat44_session_update_lru (sm, s, thread_index); + } +out: + if (NAT_NEXT_DROP == next && s) + { + nat_ed_session_delete (sm, s, thread_index, 1); + s = 0; + } + *s_p = s; + return next; +} + +// allocate exact address based on preference +static_always_inline int +nat_alloc_addr_and_port_exact (snat_address_t * a, + u32 thread_index, + nat_protocol_t proto, + ip4_address_t * addr, + u16 * port, + u16 port_per_thread, u32 snat_thread_index) +{ + snat_main_t *sm = &snat_main; + u32 portnum; + + switch (proto) + { +#define _(N, j, n, s) \ + case NAT_PROTOCOL_##N: \ + if (a->busy_##n##_ports_per_thread[thread_index] < port_per_thread) \ + { \ + while (1) \ + { \ + portnum = (port_per_thread * \ + snat_thread_index) + \ + snat_random_port(0, port_per_thread - 1) + 1024; \ + if (a->busy_##n##_port_refcounts[portnum]) \ + continue; \ + --a->busy_##n##_port_refcounts[portnum]; \ + a->busy_##n##_ports_per_thread[thread_index]++; \ + a->busy_##n##_ports++; \ + *addr = a->addr; \ + *port = clib_host_to_net_u16(portnum); \ + return 0; \ + } \ + } \ + break; + foreach_nat_protocol +#undef _ + default : nat_elog_info (sm, "unknown protocol"); + return 1; + } + + /* Totally out of translations to use... */ + nat_ipfix_logging_addresses_exhausted (thread_index, 0); + return 1; +} + +static_always_inline int +nat44_ed_alloc_outside_addr_and_port (snat_address_t *addresses, u32 fib_index, + u32 thread_index, nat_protocol_t proto, + ip4_address_t *addr, u16 *port, + u16 port_per_thread, + u32 snat_thread_index) +{ + snat_main_t *sm = &snat_main; + snat_address_t *a, *ga = 0; + u32 portnum; + int i; + + for (i = 0; i < vec_len (addresses); i++) + { + a = addresses + i; + switch (proto) + { +#define _(N, j, n, s) \ + case NAT_PROTOCOL_##N: \ + if (a->busy_##n##_ports_per_thread[thread_index] < port_per_thread) \ + { \ + if (a->fib_index == fib_index) \ + { \ + while (1) \ + { \ + portnum = (port_per_thread * snat_thread_index) + \ + snat_random_port (0, port_per_thread - 1) + 1024; \ + if (a->busy_##n##_port_refcounts[portnum]) \ + continue; \ + --a->busy_##n##_port_refcounts[portnum]; \ + a->busy_##n##_ports_per_thread[thread_index]++; \ + a->busy_##n##_ports++; \ + *addr = a->addr; \ + *port = clib_host_to_net_u16 (portnum); \ + return 0; \ + } \ + } \ + else if (a->fib_index == ~0) \ + { \ + ga = a; \ + } \ + } \ + break; + foreach_nat_protocol +#undef _ + default : nat_elog_info (sm, "unknown protocol"); + return 1; + } + } + + if (ga) + { + a = ga; + switch (proto) + { +#define _(N, j, n, s) \ + case NAT_PROTOCOL_##N: \ + while (1) \ + { \ + portnum = (port_per_thread * snat_thread_index) + \ + snat_random_port (0, port_per_thread - 1) + 1024; \ + if (a->busy_##n##_port_refcounts[portnum]) \ + continue; \ + ++a->busy_##n##_port_refcounts[portnum]; \ + a->busy_##n##_ports_per_thread[thread_index]++; \ + a->busy_##n##_ports++; \ + *addr = a->addr; \ + *port = clib_host_to_net_u16 (portnum); \ + return 0; \ + } + break; + foreach_nat_protocol +#undef _ + default : nat_elog_info (sm, "unknown protocol"); + return 1; + } + } + + /* Totally out of translations to use... */ + nat_ipfix_logging_addresses_exhausted (thread_index, 0); + return 1; +} + +static snat_session_t * +create_session_for_static_mapping_ed ( + snat_main_t *sm, vlib_buffer_t *b, ip4_address_t i2o_addr, u16 i2o_port, + u32 i2o_fib_index, ip4_address_t o2i_addr, u16 o2i_port, u32 o2i_fib_index, + nat_protocol_t nat_proto, vlib_node_runtime_t *node, u32 rx_fib_index, + u32 thread_index, twice_nat_type_t twice_nat, lb_nat_type_t lb_nat, f64 now, + snat_static_mapping_t *mapping) +{ + snat_session_t *s; + ip4_header_t *ip; + udp_header_t *udp; + snat_main_per_thread_data_t *tsm = &sm->per_thread_data[thread_index]; + + if (PREDICT_FALSE + (nat44_ed_maximum_sessions_exceeded (sm, rx_fib_index, thread_index))) + { + b->error = node->errors[NAT_OUT2IN_ED_ERROR_MAX_SESSIONS_EXCEEDED]; + nat_elog_notice (sm, "maximum sessions exceeded"); + return 0; + } + + s = nat_ed_session_alloc (sm, thread_index, now, nat_proto); + if (!s) + { + b->error = node->errors[NAT_OUT2IN_ED_ERROR_MAX_SESSIONS_EXCEEDED]; + nat_elog_warn (sm, "create NAT session failed"); + return 0; + } + + ip = vlib_buffer_get_current (b); + udp = ip4_next_header (ip); + + s->ext_host_addr.as_u32 = ip->src_address.as_u32; + s->ext_host_port = nat_proto == NAT_PROTOCOL_ICMP ? 0 : udp->src_port; + s->flags |= SNAT_SESSION_FLAG_STATIC_MAPPING; + if (lb_nat) + s->flags |= SNAT_SESSION_FLAG_LOAD_BALANCING; + if (lb_nat == AFFINITY_LB_NAT) + s->flags |= SNAT_SESSION_FLAG_AFFINITY; + s->flags |= SNAT_SESSION_FLAG_ENDPOINT_DEPENDENT; + s->out2in.addr = o2i_addr; + s->out2in.port = o2i_port; + s->out2in.fib_index = o2i_fib_index; + s->in2out.addr = i2o_addr; + s->in2out.port = i2o_port; + s->in2out.fib_index = i2o_fib_index; + s->nat_proto = nat_proto; + + if (NAT_PROTOCOL_ICMP == nat_proto) + { + nat_6t_o2i_flow_init (sm, thread_index, s, s->ext_host_addr, o2i_port, + o2i_addr, o2i_port, o2i_fib_index, ip->protocol); + nat_6t_flow_icmp_id_rewrite_set (&s->o2i, i2o_port); + } + else + { + nat_6t_o2i_flow_init (sm, thread_index, s, s->ext_host_addr, + s->ext_host_port, o2i_addr, o2i_port, + o2i_fib_index, ip->protocol); + nat_6t_flow_dport_rewrite_set (&s->o2i, i2o_port); + } + nat_6t_flow_daddr_rewrite_set (&s->o2i, i2o_addr.as_u32); + nat_6t_flow_txfib_rewrite_set (&s->o2i, i2o_fib_index); + + if (nat_ed_ses_o2i_flow_hash_add_del (sm, thread_index, s, 1)) + { + b->error = node->errors[NAT_OUT2IN_ED_ERROR_HASH_ADD_FAILED]; + nat_ed_session_delete (sm, s, thread_index, 1); + nat_elog_warn (sm, "out2in flow hash add failed"); + return 0; + } + + if (twice_nat == TWICE_NAT || (twice_nat == TWICE_NAT_SELF && + ip->src_address.as_u32 == i2o_addr.as_u32)) + { + int rc = 0; + snat_address_t *filter = 0; + + // if exact address is specified use this address + if (is_exact_address (mapping)) + { + snat_address_t *ap; + vec_foreach (ap, sm->twice_nat_addresses) + { + if (mapping->pool_addr.as_u32 == ap->addr.as_u32) + { + filter = ap; + break; + } + } + } + + if (filter) + { + rc = nat_alloc_addr_and_port_exact (filter, + thread_index, + nat_proto, + &s->ext_host_nat_addr, + &s->ext_host_nat_port, + sm->port_per_thread, + tsm->snat_thread_index); + s->flags |= SNAT_SESSION_FLAG_EXACT_ADDRESS; + } + else + { + rc = nat44_ed_alloc_outside_addr_and_port ( + sm->twice_nat_addresses, 0, thread_index, nat_proto, + &s->ext_host_nat_addr, &s->ext_host_nat_port, sm->port_per_thread, + tsm->snat_thread_index); + } + + if (rc) + { + b->error = node->errors[NAT_OUT2IN_ED_ERROR_OUT_OF_PORTS]; + if (nat_ed_ses_o2i_flow_hash_add_del (sm, thread_index, s, 0)) + { + nat_elog_warn (sm, "out2in flow hash del failed"); + } + snat_free_outside_address_and_port ( + sm->twice_nat_addresses, thread_index, &s->ext_host_nat_addr, + s->ext_host_nat_port, s->nat_proto); + nat_ed_session_delete (sm, s, thread_index, 1); + return 0; + } + + s->flags |= SNAT_SESSION_FLAG_TWICE_NAT; + + nat_6t_flow_saddr_rewrite_set (&s->o2i, s->ext_host_nat_addr.as_u32); + if (NAT_PROTOCOL_ICMP == nat_proto) + { + nat_6t_flow_icmp_id_rewrite_set (&s->o2i, s->ext_host_nat_port); + } + else + { + nat_6t_flow_sport_rewrite_set (&s->o2i, s->ext_host_nat_port); + } + + nat_6t_l3_l4_csum_calc (&s->o2i); + + nat_6t_i2o_flow_init (sm, thread_index, s, i2o_addr, i2o_port, + s->ext_host_nat_addr, s->ext_host_nat_port, + i2o_fib_index, ip->protocol); + nat_6t_flow_daddr_rewrite_set (&s->i2o, s->ext_host_addr.as_u32); + if (NAT_PROTOCOL_ICMP == nat_proto) + { + nat_6t_flow_icmp_id_rewrite_set (&s->i2o, s->ext_host_port); + } + else + { + nat_6t_flow_dport_rewrite_set (&s->i2o, s->ext_host_port); + } + } + else + { + if (NAT_PROTOCOL_ICMP == nat_proto) + { + nat_6t_i2o_flow_init (sm, thread_index, s, i2o_addr, i2o_port, + s->ext_host_addr, i2o_port, i2o_fib_index, + ip->protocol); + } + else + { + nat_6t_i2o_flow_init (sm, thread_index, s, i2o_addr, i2o_port, + s->ext_host_addr, s->ext_host_port, + i2o_fib_index, ip->protocol); + } + } + + nat_6t_flow_saddr_rewrite_set (&s->i2o, o2i_addr.as_u32); + if (NAT_PROTOCOL_ICMP == nat_proto) + { + nat_6t_flow_icmp_id_rewrite_set (&s->i2o, o2i_port); + } + else + { + nat_6t_flow_sport_rewrite_set (&s->i2o, o2i_port); + } + + if (nat_ed_ses_i2o_flow_hash_add_del (sm, thread_index, s, 1)) + { + nat_elog_notice (sm, "in2out flow hash add failed"); + if (nat_ed_ses_o2i_flow_hash_add_del (sm, thread_index, s, 0)) + { + nat_elog_warn (sm, "out2in flow hash del failed"); + } + nat_ed_session_delete (sm, s, thread_index, 1); + return 0; + } + + nat_ipfix_logging_nat44_ses_create (thread_index, + s->in2out.addr.as_u32, + s->out2in.addr.as_u32, + s->nat_proto, + s->in2out.port, + s->out2in.port, s->in2out.fib_index); + + nat_syslog_nat44_sadd (0, s->in2out.fib_index, &s->in2out.addr, + s->in2out.port, &s->ext_host_nat_addr, + s->ext_host_nat_port, &s->out2in.addr, s->out2in.port, + &s->ext_host_addr, s->ext_host_port, s->nat_proto, + is_twice_nat_session (s)); + + per_vrf_sessions_register_session (s, thread_index); + + return s; +} + +static void +create_bypass_for_fwd (snat_main_t *sm, vlib_buffer_t *b, snat_session_t *s, + ip4_header_t *ip, u32 rx_fib_index, u32 thread_index) +{ + clib_bihash_kv_16_8_t kv, value; + udp_header_t *udp; + snat_main_per_thread_data_t *tsm = &sm->per_thread_data[thread_index]; + vlib_main_t *vm = vlib_get_main (); + f64 now = vlib_time_now (vm); + u16 lookup_sport, lookup_dport; + u8 lookup_protocol; + ip4_address_t lookup_saddr, lookup_daddr; + + if (ip->protocol == IP_PROTOCOL_ICMP) + { + if (nat_get_icmp_session_lookup_values (b, ip, &lookup_saddr, + &lookup_sport, &lookup_daddr, + &lookup_dport, &lookup_protocol)) + return; + } + else + { + if (ip->protocol == IP_PROTOCOL_UDP || ip->protocol == IP_PROTOCOL_TCP) + { + udp = ip4_next_header (ip); + lookup_sport = udp->dst_port; + lookup_dport = udp->src_port; + } + else + { + lookup_sport = 0; + lookup_dport = 0; + } + lookup_saddr.as_u32 = ip->dst_address.as_u32; + lookup_daddr.as_u32 = ip->src_address.as_u32; + lookup_protocol = ip->protocol; + } + + init_ed_k (&kv, lookup_saddr, lookup_sport, lookup_daddr, lookup_dport, + rx_fib_index, lookup_protocol); + + if (!clib_bihash_search_16_8 (&sm->flow_hash, &kv, &value)) + { + ASSERT (thread_index == ed_value_get_thread_index (&value)); + s = + pool_elt_at_index (tsm->sessions, + ed_value_get_session_index (&value)); + } + else if (ip->protocol == IP_PROTOCOL_ICMP && + icmp_type_is_error_message + (vnet_buffer (b)->ip.reass.icmp_type_or_tcp_flags)) + { + return; + } + else + { + u32 proto; + + if (PREDICT_FALSE + (nat44_ed_maximum_sessions_exceeded + (sm, rx_fib_index, thread_index))) + return; + + s = nat_ed_session_alloc (sm, thread_index, now, ip->protocol); + if (!s) + { + nat_elog_warn (sm, "create NAT session failed"); + return; + } + + proto = ip_proto_to_nat_proto (ip->protocol); + + s->ext_host_addr = ip->src_address; + s->ext_host_port = lookup_dport; + s->flags |= SNAT_SESSION_FLAG_FWD_BYPASS; + s->out2in.addr = ip->dst_address; + s->out2in.port = lookup_sport; + s->nat_proto = proto; + if (proto == NAT_PROTOCOL_OTHER) + { + s->flags |= SNAT_SESSION_FLAG_UNKNOWN_PROTO; + s->out2in.port = ip->protocol; + } + s->out2in.fib_index = rx_fib_index; + s->in2out.addr = s->out2in.addr; + s->in2out.port = s->out2in.port; + s->in2out.fib_index = s->out2in.fib_index; + + nat_6t_i2o_flow_init (sm, thread_index, s, ip->dst_address, lookup_sport, + ip->src_address, lookup_dport, rx_fib_index, + ip->protocol); + nat_6t_flow_txfib_rewrite_set (&s->i2o, rx_fib_index); + if (nat_ed_ses_i2o_flow_hash_add_del (sm, thread_index, s, 1)) + { + nat_elog_notice (sm, "in2out flow add failed"); + nat_ed_session_delete (sm, s, thread_index, 1); + return; + } + + per_vrf_sessions_register_session (s, thread_index); + } + + if (ip->protocol == IP_PROTOCOL_TCP) + { + tcp_header_t *tcp = ip4_next_header (ip); + nat44_set_tcp_session_state_o2i (sm, now, s, tcp->flags, + tcp->ack_number, tcp->seq_number, + thread_index); + } + + /* Accounting */ + nat44_session_update_counters (s, now, 0, thread_index); + /* Per-user LRU list maintenance */ + nat44_session_update_lru (sm, s, thread_index); +} + +static snat_session_t * +nat44_ed_out2in_slowpath_unknown_proto (snat_main_t *sm, vlib_buffer_t *b, + ip4_header_t *ip, u32 rx_fib_index, + u32 thread_index, f64 now, + vlib_main_t *vm, + vlib_node_runtime_t *node) +{ + clib_bihash_kv_8_8_t kv, value; + snat_static_mapping_t *m; + snat_session_t *s; + + if (PREDICT_FALSE ( + nat44_ed_maximum_sessions_exceeded (sm, rx_fib_index, thread_index))) + { + b->error = node->errors[NAT_OUT2IN_ED_ERROR_MAX_SESSIONS_EXCEEDED]; + nat_elog_notice (sm, "maximum sessions exceeded"); + return 0; + } + + init_nat_k (&kv, ip->dst_address, 0, 0, 0); + if (clib_bihash_search_8_8 (&sm->static_mapping_by_external, &kv, &value)) + { + b->error = node->errors[NAT_OUT2IN_ED_ERROR_NO_TRANSLATION]; + return 0; + } + + m = pool_elt_at_index (sm->static_mappings, value.value); + + /* Create a new session */ + s = nat_ed_session_alloc (sm, thread_index, now, ip->protocol); + if (!s) + { + b->error = node->errors[NAT_OUT2IN_ED_ERROR_MAX_SESSIONS_EXCEEDED]; + nat_elog_warn (sm, "create NAT session failed"); + return 0; + } + + s->ext_host_addr.as_u32 = ip->src_address.as_u32; + s->flags |= SNAT_SESSION_FLAG_UNKNOWN_PROTO; + s->flags |= SNAT_SESSION_FLAG_STATIC_MAPPING; + s->flags |= SNAT_SESSION_FLAG_ENDPOINT_DEPENDENT; + s->out2in.addr.as_u32 = ip->dst_address.as_u32; + s->out2in.fib_index = rx_fib_index; + s->in2out.addr.as_u32 = m->local_addr.as_u32; + s->in2out.fib_index = m->fib_index; + s->in2out.port = s->out2in.port = ip->protocol; + + nat_6t_o2i_flow_init (sm, thread_index, s, ip->dst_address, 0, + ip->src_address, 0, m->fib_index, ip->protocol); + nat_6t_flow_saddr_rewrite_set (&s->i2o, ip->dst_address.as_u32); + if (nat_ed_ses_i2o_flow_hash_add_del (sm, thread_index, s, 1)) + { + nat_elog_notice (sm, "in2out key add failed"); + nat_ed_session_delete (sm, s, thread_index, 1); + return NULL; + } + + nat_6t_o2i_flow_init (sm, thread_index, s, ip->src_address, 0, + ip->dst_address, 0, rx_fib_index, ip->protocol); + nat_6t_flow_daddr_rewrite_set (&s->o2i, m->local_addr.as_u32); + nat_6t_flow_txfib_rewrite_set (&s->o2i, m->fib_index); + if (nat_ed_ses_o2i_flow_hash_add_del (sm, thread_index, s, 1)) + { + nat_elog_notice (sm, "out2in flow hash add failed"); + nat_ed_session_delete (sm, s, thread_index, 1); + return NULL; + } + + per_vrf_sessions_register_session (s, thread_index); + + /* Accounting */ + nat44_session_update_counters (s, now, vlib_buffer_length_in_chain (vm, b), + thread_index); + /* Per-user LRU list maintenance */ + nat44_session_update_lru (sm, s, thread_index); + + return s; +} + +static inline uword +nat44_ed_out2in_fast_path_node_fn_inline (vlib_main_t * vm, + vlib_node_runtime_t * node, + vlib_frame_t * frame, + int is_multi_worker) +{ + u32 n_left_from, *from; + snat_main_t *sm = &snat_main; + f64 now = vlib_time_now (vm); + u32 thread_index = vm->thread_index; + snat_main_per_thread_data_t *tsm = &sm->per_thread_data[thread_index]; + + from = vlib_frame_vector_args (frame); + n_left_from = frame->n_vectors; + + vlib_buffer_t *bufs[VLIB_FRAME_SIZE], **b = bufs; + u16 nexts[VLIB_FRAME_SIZE], *next = nexts; + vlib_get_buffers (vm, from, b, n_left_from); + + while (n_left_from > 0) + { + vlib_buffer_t *b0; + u32 sw_if_index0, rx_fib_index0; + nat_protocol_t proto0; + ip4_header_t *ip0; + snat_session_t *s0 = 0; + clib_bihash_kv_16_8_t kv0, value0; + nat_translation_error_e translation_error = NAT_ED_TRNSL_ERR_SUCCESS; + nat_6t_flow_t *f = 0; + ip4_address_t lookup_saddr, lookup_daddr; + u16 lookup_sport, lookup_dport; + u8 lookup_protocol; + int lookup_skipped = 0; + + b0 = *b; + b++; + + lookup_sport = vnet_buffer (b0)->ip.reass.l4_src_port; + lookup_dport = vnet_buffer (b0)->ip.reass.l4_dst_port; + + /* Prefetch next iteration. */ + if (PREDICT_TRUE (n_left_from >= 2)) + { + vlib_buffer_t *p2; + + p2 = *b; + + vlib_prefetch_buffer_header (p2, LOAD); + + CLIB_PREFETCH (p2->data, CLIB_CACHE_LINE_BYTES, LOAD); + } + + next[0] = vnet_buffer2 (b0)->nat.arc_next; + + vnet_buffer (b0)->snat.flags = 0; + ip0 = vlib_buffer_get_current (b0); + + sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX]; + rx_fib_index0 = + fib_table_get_index_for_sw_if_index (FIB_PROTOCOL_IP4, sw_if_index0); + + if (PREDICT_FALSE (ip0->ttl == 1)) + { + vnet_buffer (b0)->sw_if_index[VLIB_TX] = (u32) ~ 0; + icmp4_error_set_vnet_buffer (b0, ICMP4_time_exceeded, + ICMP4_time_exceeded_ttl_exceeded_in_transit, + 0); + next[0] = NAT_NEXT_ICMP_ERROR; + goto trace0; + } + + proto0 = ip_proto_to_nat_proto (ip0->protocol); + + if (PREDICT_FALSE (proto0 == NAT_PROTOCOL_ICMP)) + { + if (vnet_buffer (b0)->ip.reass.icmp_type_or_tcp_flags != + ICMP4_echo_request && + vnet_buffer (b0)->ip.reass.icmp_type_or_tcp_flags != + ICMP4_echo_reply && + !icmp_type_is_error_message ( + vnet_buffer (b0)->ip.reass.icmp_type_or_tcp_flags)) + { + b0->error = node->errors[NAT_OUT2IN_ED_ERROR_BAD_ICMP_TYPE]; + next[0] = NAT_NEXT_DROP; + goto trace0; + } + int err = nat_get_icmp_session_lookup_values ( + b0, ip0, &lookup_saddr, &lookup_sport, &lookup_daddr, + &lookup_dport, &lookup_protocol); + if (err != 0) + { + b0->error = node->errors[err]; + next[0] = NAT_NEXT_DROP; + goto trace0; + } + } + else + { + lookup_saddr.as_u32 = ip0->src_address.as_u32; + lookup_daddr.as_u32 = ip0->dst_address.as_u32; + lookup_protocol = ip0->protocol; + } + + /* there might be a stashed index in vnet_buffer2 from handoff or + * classify node, see if it can be used */ + if (!pool_is_free_index (tsm->sessions, + vnet_buffer2 (b0)->nat.cached_session_index)) + { + s0 = pool_elt_at_index (tsm->sessions, + vnet_buffer2 (b0)->nat.cached_session_index); + if (PREDICT_TRUE ( + nat_6t_flow_match (&s0->o2i, b0, lookup_saddr, lookup_sport, + lookup_daddr, lookup_dport, lookup_protocol, + rx_fib_index0) || + (s0->flags & SNAT_SESSION_FLAG_TWICE_NAT && + nat_6t_flow_match (&s0->i2o, b0, lookup_saddr, lookup_sport, + lookup_daddr, lookup_dport, + lookup_protocol, rx_fib_index0)))) + { + /* yes, this is the droid we're looking for */ + lookup_skipped = 1; + goto skip_lookup; + } + s0 = NULL; + } + + init_ed_k (&kv0, lookup_saddr, lookup_sport, lookup_daddr, lookup_dport, + rx_fib_index0, lookup_protocol); + + // lookup flow + if (clib_bihash_search_16_8 (&sm->flow_hash, &kv0, &value0)) + { + // flow does not exist go slow path + next[0] = NAT_NEXT_OUT2IN_ED_SLOW_PATH; + goto trace0; + } + ASSERT (thread_index == ed_value_get_thread_index (&value0)); + s0 = + pool_elt_at_index (tsm->sessions, + ed_value_get_session_index (&value0)); + skip_lookup: + + if (PREDICT_FALSE (per_vrf_sessions_is_expired (s0, thread_index))) + { + // session is closed, go slow path + nat_free_session_data (sm, s0, thread_index, 0); + nat_ed_session_delete (sm, s0, thread_index, 1); + next[0] = NAT_NEXT_OUT2IN_ED_SLOW_PATH; + goto trace0; + } + + if (s0->tcp_closed_timestamp) + { + if (now >= s0->tcp_closed_timestamp) + { + // session is closed, go slow path, freed in slow path + next[0] = NAT_NEXT_OUT2IN_ED_SLOW_PATH; + } + else + { + // session in transitory timeout, drop + b0->error = node->errors[NAT_OUT2IN_ED_ERROR_TCP_CLOSED]; + next[0] = NAT_NEXT_DROP; + } + goto trace0; + } + + // drop if session expired + u64 sess_timeout_time; + sess_timeout_time = + s0->last_heard + (f64) nat44_session_get_timeout (sm, s0); + if (now >= sess_timeout_time) + { + // session is closed, go slow path + nat_free_session_data (sm, s0, thread_index, 0); + nat_ed_session_delete (sm, s0, thread_index, 1); + next[0] = NAT_NEXT_OUT2IN_ED_SLOW_PATH; + goto trace0; + } + + if (nat_6t_flow_match (&s0->o2i, b0, lookup_saddr, lookup_sport, + lookup_daddr, lookup_dport, lookup_protocol, + rx_fib_index0)) + { + f = &s0->o2i; + } + else if (s0->flags & SNAT_SESSION_FLAG_TWICE_NAT && + nat_6t_flow_match (&s0->i2o, b0, lookup_saddr, lookup_sport, + lookup_daddr, lookup_dport, lookup_protocol, + rx_fib_index0)) + { + f = &s0->i2o; + } + else + { + /* + * Send DHCP packets to the ipv4 stack, or we won't + * be able to use dhcp client on the outside interface + */ + if (PREDICT_FALSE ( + proto0 == NAT_PROTOCOL_UDP && + (vnet_buffer (b0)->ip.reass.l4_dst_port == + clib_host_to_net_u16 (UDP_DST_PORT_dhcp_to_client)))) + { + goto trace0; + } + + if (!sm->forwarding_enabled) + { + b0->error = node->errors[NAT_OUT2IN_ED_ERROR_NO_TRANSLATION]; + next[0] = NAT_NEXT_DROP; + goto trace0; + } + else + { + if (nat_6t_flow_match (&s0->i2o, b0, lookup_saddr, lookup_sport, + lookup_daddr, lookup_dport, + lookup_protocol, rx_fib_index0)) + { + f = &s0->i2o; + } + else + { + // FIXME TODO bypass ??? + // create_bypass_for_fwd (sm, b0, s0, ip0, rx_fib_index0, + // thread_index); + translation_error = NAT_ED_TRNSL_ERR_FLOW_MISMATCH; + nat_free_session_data (sm, s0, thread_index, 0); + nat_ed_session_delete (sm, s0, thread_index, 1); + next[0] = NAT_NEXT_DROP; + goto trace0; + } + } + } + + if (NAT_ED_TRNSL_ERR_SUCCESS != + (translation_error = nat_6t_flow_buf_translate ( + sm, b0, ip0, f, proto0, 0 /* is_output_feature */))) + { + next[0] = NAT_NEXT_DROP; + goto trace0; + } + + switch (proto0) + { + case NAT_PROTOCOL_TCP: + vlib_increment_simple_counter (&sm->counters.fastpath.out2in.tcp, + thread_index, sw_if_index0, 1); + nat44_set_tcp_session_state_o2i (sm, now, s0, + vnet_buffer (b0)->ip. + reass.icmp_type_or_tcp_flags, + vnet_buffer (b0)->ip. + reass.tcp_ack_number, + vnet_buffer (b0)->ip. + reass.tcp_seq_number, + thread_index); + break; + case NAT_PROTOCOL_UDP: + vlib_increment_simple_counter (&sm->counters.fastpath.out2in.udp, + thread_index, sw_if_index0, 1); + break; + case NAT_PROTOCOL_ICMP: + vlib_increment_simple_counter (&sm->counters.fastpath.out2in.icmp, + thread_index, sw_if_index0, 1); + break; + case NAT_PROTOCOL_OTHER: + vlib_increment_simple_counter (&sm->counters.fastpath.out2in.other, + thread_index, sw_if_index0, 1); + break; + } + + /* Accounting */ + nat44_session_update_counters (s0, now, + vlib_buffer_length_in_chain (vm, b0), + thread_index); + /* Per-user LRU list maintenance */ + nat44_session_update_lru (sm, s0, thread_index); + + trace0: + if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE) + && (b0->flags & VLIB_BUFFER_IS_TRACED))) + { + nat44_ed_out2in_trace_t *t = + vlib_add_trace (vm, node, b0, sizeof (*t)); + t->sw_if_index = sw_if_index0; + t->next_index = next[0]; + t->is_slow_path = 0; + t->translation_error = translation_error; + clib_memcpy (&t->search_key, &kv0, sizeof (t->search_key)); + t->lookup_skipped = lookup_skipped; + + if (s0) + { + t->session_index = s0 - tsm->sessions; + clib_memcpy (&t->i2of, &s0->i2o, sizeof (t->i2of)); + clib_memcpy (&t->o2if, &s0->o2i, sizeof (t->o2if)); + t->translation_via_i2of = (&s0->i2o == f); + } + else + { + t->session_index = ~0; + } + } + + if (next[0] == NAT_NEXT_DROP) + { + vlib_increment_simple_counter (&sm->counters.fastpath.out2in.drops, + thread_index, sw_if_index0, 1); + } + + n_left_from--; + next++; + } + + vlib_buffer_enqueue_to_next (vm, node, from, (u16 *) nexts, + frame->n_vectors); + return frame->n_vectors; +} + +static inline uword +nat44_ed_out2in_slow_path_node_fn_inline (vlib_main_t * vm, + vlib_node_runtime_t * node, + vlib_frame_t * frame) +{ + u32 n_left_from, *from; + snat_main_t *sm = &snat_main; + f64 now = vlib_time_now (vm); + u32 thread_index = vm->thread_index; + snat_main_per_thread_data_t *tsm = &sm->per_thread_data[thread_index]; + snat_static_mapping_t *m; + + from = vlib_frame_vector_args (frame); + n_left_from = frame->n_vectors; + + vlib_buffer_t *bufs[VLIB_FRAME_SIZE], **b = bufs; + u16 nexts[VLIB_FRAME_SIZE], *next = nexts; + vlib_get_buffers (vm, from, b, n_left_from); + + while (n_left_from > 0) + { + vlib_buffer_t *b0; + u32 sw_if_index0, rx_fib_index0; + nat_protocol_t proto0; + ip4_header_t *ip0; + udp_header_t *udp0; + icmp46_header_t *icmp0; + snat_session_t *s0 = 0; + clib_bihash_kv_16_8_t kv0, value0; + lb_nat_type_t lb_nat0; + twice_nat_type_t twice_nat0; + u8 identity_nat0; + ip4_address_t sm_addr; + u16 sm_port; + u32 sm_fib_index; + nat_translation_error_e translation_error = NAT_ED_TRNSL_ERR_SUCCESS; + + b0 = *b; + next[0] = vnet_buffer2 (b0)->nat.arc_next; + + vnet_buffer (b0)->snat.flags = 0; + ip0 = vlib_buffer_get_current (b0); + + sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX]; + rx_fib_index0 = + fib_table_get_index_for_sw_if_index (FIB_PROTOCOL_IP4, sw_if_index0); + + if (PREDICT_FALSE (ip0->ttl == 1)) + { + vnet_buffer (b0)->sw_if_index[VLIB_TX] = (u32) ~ 0; + icmp4_error_set_vnet_buffer (b0, ICMP4_time_exceeded, + ICMP4_time_exceeded_ttl_exceeded_in_transit, + 0); + next[0] = NAT_NEXT_ICMP_ERROR; + goto trace0; + } + + udp0 = ip4_next_header (ip0); + icmp0 = (icmp46_header_t *) udp0; + proto0 = ip_proto_to_nat_proto (ip0->protocol); + + if (PREDICT_FALSE (proto0 == NAT_PROTOCOL_OTHER)) + { + s0 = nat44_ed_out2in_slowpath_unknown_proto ( + sm, b0, ip0, rx_fib_index0, thread_index, now, vm, node); + if (!sm->forwarding_enabled) + { + if (!s0) + next[0] = NAT_NEXT_DROP; + } + if (NAT_NEXT_DROP != next[0] && + NAT_ED_TRNSL_ERR_SUCCESS != + (translation_error = nat_6t_flow_buf_translate ( + sm, b0, ip0, &s0->o2i, proto0, 0 /* is_output_feature */))) + { + goto trace0; + } + + vlib_increment_simple_counter (&sm->counters.slowpath.out2in.other, + thread_index, sw_if_index0, 1); + goto trace0; + } + + if (PREDICT_FALSE (proto0 == NAT_PROTOCOL_ICMP)) + { + next[0] = icmp_out2in_ed_slow_path + (sm, b0, ip0, icmp0, sw_if_index0, rx_fib_index0, node, + next[0], now, thread_index, &s0); + + if (NAT_NEXT_DROP != next[0] && s0 && + NAT_ED_TRNSL_ERR_SUCCESS != + (translation_error = nat_6t_flow_buf_translate ( + sm, b0, ip0, &s0->o2i, proto0, 0 /* is_output_feature */))) + { + goto trace0; + } + + vlib_increment_simple_counter (&sm->counters.slowpath.out2in.icmp, + thread_index, sw_if_index0, 1); + goto trace0; + } + + init_ed_k (&kv0, ip0->src_address, + vnet_buffer (b0)->ip.reass.l4_src_port, ip0->dst_address, + vnet_buffer (b0)->ip.reass.l4_dst_port, rx_fib_index0, + ip0->protocol); + + s0 = NULL; + if (!clib_bihash_search_16_8 (&sm->flow_hash, &kv0, &value0)) + { + ASSERT (thread_index == ed_value_get_thread_index (&value0)); + s0 = + pool_elt_at_index (tsm->sessions, + ed_value_get_session_index (&value0)); + + if (s0->tcp_closed_timestamp && now >= s0->tcp_closed_timestamp) + { + nat_free_session_data (sm, s0, thread_index, 0); + nat_ed_session_delete (sm, s0, thread_index, 1); + s0 = NULL; + } + } + + if (!s0) + { + /* Try to match static mapping by external address and port, + destination address and port in packet */ + + if (snat_static_mapping_match + (sm, ip0->dst_address, + vnet_buffer (b0)->ip.reass.l4_dst_port, rx_fib_index0, + proto0, &sm_addr, &sm_port, &sm_fib_index, 1, 0, + &twice_nat0, &lb_nat0, &ip0->src_address, &identity_nat0, &m)) + { + /* + * Send DHCP packets to the ipv4 stack, or we won't + * be able to use dhcp client on the outside interface + */ + if (PREDICT_FALSE (proto0 == NAT_PROTOCOL_UDP + && (vnet_buffer (b0)->ip.reass.l4_dst_port == + clib_host_to_net_u16 + (UDP_DST_PORT_dhcp_to_client)))) + { + goto trace0; + } + + if (!sm->forwarding_enabled) + { + b0->error = + node->errors[NAT_OUT2IN_ED_ERROR_NO_TRANSLATION]; + next[0] = NAT_NEXT_DROP; + } + else + { + if (next_src_nat + (sm, ip0, vnet_buffer (b0)->ip.reass.l4_src_port, + vnet_buffer (b0)->ip.reass.l4_dst_port, + thread_index, rx_fib_index0)) + { + next[0] = NAT_NEXT_IN2OUT_ED_FAST_PATH; + } + else + { + create_bypass_for_fwd (sm, b0, s0, ip0, rx_fib_index0, + thread_index); + } + } + goto trace0; + } + + if (PREDICT_FALSE (identity_nat0)) + goto trace0; + + if ((proto0 == NAT_PROTOCOL_TCP) + && !tcp_flags_is_init (vnet_buffer (b0)->ip. + reass.icmp_type_or_tcp_flags)) + { + b0->error = node->errors[NAT_OUT2IN_ED_ERROR_NON_SYN]; + next[0] = NAT_NEXT_DROP; + goto trace0; + } + + /* Create session initiated by host from external network */ + s0 = create_session_for_static_mapping_ed (sm, b0, + sm_addr, sm_port, + sm_fib_index, + ip0->dst_address, + vnet_buffer (b0)-> + ip.reass.l4_dst_port, + rx_fib_index0, proto0, + node, rx_fib_index0, + thread_index, twice_nat0, + lb_nat0, now, m); + if (!s0) + { + next[0] = NAT_NEXT_DROP; + goto trace0; + } + } + + if (NAT_ED_TRNSL_ERR_SUCCESS != + (translation_error = nat_6t_flow_buf_translate ( + sm, b0, ip0, &s0->o2i, proto0, 0 /* is_output_feature */))) + { + next[0] = NAT_NEXT_DROP; + goto trace0; + } + + if (PREDICT_TRUE (proto0 == NAT_PROTOCOL_TCP)) + { + vlib_increment_simple_counter (&sm->counters.slowpath.out2in.tcp, + thread_index, sw_if_index0, 1); + nat44_set_tcp_session_state_o2i (sm, now, s0, + vnet_buffer (b0)->ip. + reass.icmp_type_or_tcp_flags, + vnet_buffer (b0)->ip. + reass.tcp_ack_number, + vnet_buffer (b0)->ip. + reass.tcp_seq_number, + thread_index); + } + else + { + vlib_increment_simple_counter (&sm->counters.slowpath.out2in.udp, + thread_index, sw_if_index0, 1); + } + + /* Accounting */ + nat44_session_update_counters (s0, now, + vlib_buffer_length_in_chain (vm, b0), + thread_index); + /* Per-user LRU list maintenance */ + nat44_session_update_lru (sm, s0, thread_index); + + trace0: + if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE) + && (b0->flags & VLIB_BUFFER_IS_TRACED))) + { + nat44_ed_out2in_trace_t *t = + vlib_add_trace (vm, node, b0, sizeof (*t)); + t->sw_if_index = sw_if_index0; + t->next_index = next[0]; + t->is_slow_path = 1; + t->translation_error = translation_error; + clib_memcpy (&t->search_key, &kv0, sizeof (t->search_key)); + + if (s0) + { + t->session_index = s0 - tsm->sessions; + clib_memcpy (&t->i2of, &s0->i2o, sizeof (t->i2of)); + clib_memcpy (&t->o2if, &s0->o2i, sizeof (t->o2if)); + } + else + { + t->session_index = ~0; + } + } + + if (next[0] == NAT_NEXT_DROP) + { + vlib_increment_simple_counter (&sm->counters.slowpath.out2in.drops, + thread_index, sw_if_index0, 1); + } + + n_left_from--; + next++; + b++; + } + + vlib_buffer_enqueue_to_next (vm, node, from, (u16 *) nexts, + frame->n_vectors); + + return frame->n_vectors; +} + +VLIB_NODE_FN (nat44_ed_out2in_node) (vlib_main_t * vm, + vlib_node_runtime_t * node, + vlib_frame_t * frame) +{ + if (snat_main.num_workers > 1) + { + return nat44_ed_out2in_fast_path_node_fn_inline (vm, node, frame, 1); + } + else + { + return nat44_ed_out2in_fast_path_node_fn_inline (vm, node, frame, 0); + } +} + +/* *INDENT-OFF* */ +VLIB_REGISTER_NODE (nat44_ed_out2in_node) = { + .name = "nat44-ed-out2in", + .vector_size = sizeof (u32), + .sibling_of = "nat-default", + .format_trace = format_nat44_ed_out2in_trace, + .type = VLIB_NODE_TYPE_INTERNAL, + .n_errors = ARRAY_LEN(nat_out2in_ed_error_strings), + .error_strings = nat_out2in_ed_error_strings, + .runtime_data_bytes = sizeof (snat_runtime_t), +}; +/* *INDENT-ON* */ + +VLIB_NODE_FN (nat44_ed_out2in_slowpath_node) (vlib_main_t * vm, + vlib_node_runtime_t * node, + vlib_frame_t * frame) +{ + return nat44_ed_out2in_slow_path_node_fn_inline (vm, node, frame); +} + +/* *INDENT-OFF* */ +VLIB_REGISTER_NODE (nat44_ed_out2in_slowpath_node) = { + .name = "nat44-ed-out2in-slowpath", + .vector_size = sizeof (u32), + .sibling_of = "nat-default", + .format_trace = format_nat44_ed_out2in_trace, + .type = VLIB_NODE_TYPE_INTERNAL, + .n_errors = ARRAY_LEN(nat_out2in_ed_error_strings), + .error_strings = nat_out2in_ed_error_strings, + .runtime_data_bytes = sizeof (snat_runtime_t), +}; +/* *INDENT-ON* */ + +static u8 * +format_nat_pre_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 *); + nat_pre_trace_t *t = va_arg (*args, nat_pre_trace_t *); + return format (s, "out2in next_index %d arc_next_index %d", t->next_index, + t->arc_next_index); +} + +VLIB_NODE_FN (nat_pre_out2in_node) (vlib_main_t * vm, + vlib_node_runtime_t * node, + vlib_frame_t * frame) +{ + return nat_pre_node_fn_inline (vm, node, frame, + NAT_NEXT_OUT2IN_ED_FAST_PATH); +} + +/* *INDENT-OFF* */ +VLIB_REGISTER_NODE (nat_pre_out2in_node) = { + .name = "nat-pre-out2in", + .vector_size = sizeof (u32), + .sibling_of = "nat-default", + .format_trace = format_nat_pre_trace, + .type = VLIB_NODE_TYPE_INTERNAL, + .n_errors = 0, + }; +/* *INDENT-ON* */ + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/src/plugins/nat/nat44-ei/nat44_ei.api b/src/plugins/nat/nat44-ei/nat44_ei.api new file mode 100644 index 00000000000..708c20aaadd --- /dev/null +++ b/src/plugins/nat/nat44-ei/nat44_ei.api @@ -0,0 +1,862 @@ +/* + * Copyright (c) 2020 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +option version = "1.0.0"; +import "vnet/ip/ip_types.api"; +import "vnet/interface_types.api"; +import "plugins/nat/lib/nat_types.api"; + +enum nat44_ei_config_flags : u8 +{ + NAT44_EI_NONE = 0x00, + NAT44_EI_STATIC_MAPPING_ONLY = 0x01, + NAT44_EI_CONNECTION_TRACKING = 0x02, + NAT44_EI_OUT2IN_DPO = 0x04, + NAT44_EI_ADDR_ONLY_MAPPING = 0x08, + NAT44_EI_IF_INSIDE = 0x10, + NAT44_EI_IF_OUTSIDE = 0x20, + NAT44_EI_STATIC_MAPPING = 0x40, +}; + +/** \brief Enable/disable NAT44 plugin + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request + @param inside_vrf - inside vrf id + @param outside_vrf - outside vrf id + @param users - maximum number of users per thread + @param user_memory - overwrite hash allocation parameter + @param sessions - maximum number of sessions per thread + @param session_memory - overwrite hash allocation parameter + @param user_sessions - maximum number of sessions per user + @param enable - true if enable, false if disable + @param flags - flag NAT44_EI_IS_STATIC_MAPPING_ONLY, + NAT44_EI_IS_CONNECTION_TRACKING, + NAT44_EI_IS_OUT2IN_DPO +*/ +autoreply define nat44_ei_plugin_enable_disable { + option in_progress; + u32 client_index; + u32 context; + u32 inside_vrf; + u32 outside_vrf; + u32 users; + u32 user_memory; + u32 sessions; + u32 session_memory; + u32 user_sessions; + bool enable; + vl_api_nat44_ei_config_flags_t flags; +}; + +/** \brief Show NAT44 plugin running config + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request +*/ +define nat44_ei_show_running_config +{ + option in_progress; + u32 client_index; + u32 context; +}; + +/** \brief Show NAT44 plugin running config reply + @param context - sender context, to match reply w/ request + @param retval - return code for the request + @param inside_vrf - default inside VRF id + @param outside_vrf - outside VRF id + @param users - maximum number of users per worker thread + @param sessions - maximum number of sessions per worker thread + @param user_sessions - maximum number of sessions per user + @param user_buckets - number of user hash buckets + @param translation_buckets - number of translation hash buckets + @param flags - flag NAT44_EI_IS_STATIC_MAPPING_ONLY, + NAT44_EI_IS_CONNECTION_TRACKING, + NAT44_EI_IS_OUT2IN_DPO +*/ +define nat44_ei_show_running_config_reply +{ + option in_progress; + u32 context; + i32 retval; + u32 inside_vrf; + u32 outside_vrf; + u32 users; + u32 sessions; + u32 user_sessions; + u32 user_buckets; + u32 translation_buckets; + bool forwarding_enabled; + bool ipfix_logging_enabled; + vl_api_nat_timeouts_t timeouts; + vl_api_nat_log_level_t log_level; + vl_api_nat44_ei_config_flags_t flags; +}; + +/** \brief Set NAT44 logging level + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request + @param log_level - logging level +*/ +autoreply define nat44_ei_set_log_level { + option deprecated; + u32 client_index; + u32 context; + vl_api_nat_log_level_t log_level; +}; + +/** \brief Set NAT workers + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request + @param worker_mask - NAT workers mask +*/ +autoreply define nat44_ei_set_workers { + option in_progress; + u32 client_index; + u32 context; + u64 worker_mask; +}; + +/** \brief Dump NAT workers + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request +*/ +define nat44_ei_worker_dump { + option in_progress; + u32 client_index; + u32 context; +}; + +/** \brief NAT workers details response + @param context - sender context, to match reply w/ request + @param worker_index - worker index + @param lcore_id - lcore ID + @param name - worker name +*/ +define nat44_ei_worker_details { + option in_progress; + u32 context; + u32 worker_index; + u32 lcore_id; + string name[64]; +}; + +/** \brief Enable/disable NAT IPFIX logging + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request + @param domain_id - observation domain ID + @param src_port - source port number + @param enable - true if enable, false if disable +*/ +autoreply define nat44_ei_ipfix_enable_disable { + option in_progress; + u32 client_index; + u32 context; + u32 domain_id; + u16 src_port; + bool enable; +}; + +/** \brief Set values of timeouts for NAT sessions (seconds) + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request + @param udp - UDP timeout (default 300sec) + @param tcp_established - TCP established timeout (default 7440sec) + @param tcp_transitory - TCP transitory timeout (default 240sec) + @param icmp - ICMP timeout (default 60sec) +*/ +autoreply define nat44_ei_set_timeouts { + option in_progress; + u32 client_index; + u32 context; + u32 udp; + u32 tcp_established; + u32 tcp_transitory; + u32 icmp; +}; + +/** \brief Set address and port assignment algorithm + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request + @param alg - address and port assignment algorithm: + 0 - default, 1 - MAP-E, 2 - port range + (see nat44_ei_addr_and_port_alloc_alg_t in nat.h) + @param psid_offset - number of offset bits (valid only for MAP-E alg) + @param psid_length - length of PSID (valid only for MAP-E alg) + @param psid - Port Set Identifier (PSID) value (valid only for MAP-E alg) + @param start_port - beginning of the port range + @param end_port - end of the port range +*/ +autoreply define nat44_ei_set_addr_and_port_alloc_alg { + option in_progress; + u32 client_index; + u32 context; + u8 alg; + u8 psid_offset; + u8 psid_length; + u16 psid; + u16 start_port; + u16 end_port; +}; + +/** \brief Get address and port assignment algorithm + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request +*/ +define nat44_ei_get_addr_and_port_alloc_alg { + option deprecated; + u32 client_index; + u32 context; +}; + +/** \brief Get address and port assignment algorithm reply + @param context - sender context, to match reply w/ request + @param retval - return code + @param alg - address and port assignment algorithm: + 0 - default, 1 - MAP-E, 2 - port range + (see nat44_ei_addr_and_port_alloc_alg_t in nat.h) + @param psid_offset - number of offset bits (valid only for MAP-E alg) + @param psid_length - length of PSID (valid only for MAP-E alg) + @param psid - Port Set Identifier (PSID) value (valid only for MAP-E alg) + @param start_port - beginning of the port range + @param end_port - end of the port range +*/ +define nat44_ei_get_addr_and_port_alloc_alg_reply { + option deprecated; + u32 context; + i32 retval; + u8 alg; + u8 psid_offset; + u8 psid_length; + u16 psid; + u16 start_port; + u16 end_port; +}; + +/** \brief Set TCP MSS rewriting configuration + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request + @param mss_value - MSS value to be used for MSS rewriting + @param enable - if true enable MSS rewriting feature else disable +*/ +autoreply define nat44_ei_set_mss_clamping { + option in_progress; + u32 client_index; + u32 context; + u16 mss_value; + bool enable; +}; + +/** \brief Get TCP MSS rewriting configuration + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request +*/ +define nat44_ei_get_mss_clamping { + option deprecated; + u32 client_index; + u32 context; +}; + +/** \brief Get TCP MSS rewriting configuration reply + @param context - sender context, to match reply w/ request + @param retval - return code + @param mss_value - MSS value to be used for MSS rewriting + @param enable - if true enable MSS rewriting feature else disable +*/ +define nat44_ei_get_mss_clamping_reply { + option deprecated; + u32 context; + i32 retval; + u16 mss_value; + bool enable; +}; + +/** \brief Set HA listener (local settings) + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request + @param ip_address - local IP4 address + @param port - local UDP port number + @param path_mtu - path MTU between local and failover +*/ +autoreply define nat44_ei_ha_set_listener { + option in_progress; + u32 client_index; + u32 context; + vl_api_ip4_address_t ip_address; + u16 port; + u32 path_mtu; +}; + +/** \brief Set HA failover (remote settings) + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request + @param ip_address - failover IP4 address + @param port - failvoer UDP port number + @param session_refresh_interval - number of seconds after which to send + session counters refresh +*/ +autoreply define nat44_ei_ha_set_failover { + option in_progress; + u32 client_index; + u32 context; + vl_api_ip4_address_t ip_address; + u16 port; + u32 session_refresh_interval; +}; + +/** \brief Get HA listener/local configuration + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request +*/ +define nat44_ei_ha_get_listener { + option deprecated; + u32 client_index; + u32 context; +}; + +/** \brief Get HA listener/local configuration reply + @param context - sender context, to match reply w/ request + @param retval - return code + @param ip_address - local IP4 address + @param port - local UDP port number + @param path_mtu - Path MTU between local and failover +*/ +define nat44_ei_ha_get_listener_reply { + option deprecated; + u32 context; + i32 retval; + vl_api_ip4_address_t ip_address; + u16 port; + u32 path_mtu; +}; + +/** \brief Get HA failover/remote settings + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request +*/ +define nat44_ei_ha_get_failover { + option deprecated; + u32 client_index; + u32 context; +}; + +/** \brief Get HA failover/remote settings reply + @param context - sender context, to match reply w/ request + @param retval - return code + @param ip_address - failover IP4 address + @param port - failvoer UDP port number + @param session_refresh_interval - number of seconds after which to send + session counters refresh +*/ +define nat44_ei_ha_get_failover_reply { + option deprecated; + u32 context; + i32 retval; + vl_api_ip4_address_t ip_address; + u16 port; + u32 session_refresh_interval; +}; + +/** \brief Flush the current HA data (for testing) + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request +*/ +autoreply define nat44_ei_ha_flush { + option in_progress; + u32 client_index; + u32 context; +}; + +/** \brief Resync HA (resend existing sessions to new failover) + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request + @param want_resync_event - resync completed event sent to the sender via + nat44_ei_ha_resync_completed_event API message if + non-zero + @param pid - sender's pid +*/ +autoreply define nat44_ei_ha_resync +{ + option in_progress; + u32 client_index; + u32 context; + u8 want_resync_event; + u32 pid; +}; + +/** \brief Tell client about a HA resync completion event + @param client_index - opaque cookie to identify the sender + @param pid - client pid registered to receive notification + @param missed_count - number of missed (not ACKed) messages +*/ +define nat44_ei_ha_resync_completed_event +{ + option in_progress; + u32 client_index; + u32 pid; + u32 missed_count; +}; + +service { + rpc nat44_ei_ha_resync returns nat44_ei_ha_resync_reply events nat44_ei_ha_resync_completed_event; +}; + +/** \brief Del NAT44 user + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request + @param ip_address - IPv4 address + @param fib_index - FIB index +*/ +autoreply define nat44_ei_del_user { + option in_progress; + u32 client_index; + u32 context; + vl_api_ip4_address_t ip_address; + u32 fib_index; +}; + +/** \brief Add/del NAT44 address range + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request + @param first_ip_address - first IPv4 address + @param last_ip_address - last IPv4 address + @param vrf_id - VRF id of tenant, ~0 means independent of VRF + @param is_add - true if add, false if delete + +*/ +autoreply define nat44_ei_add_del_address_range { + option in_progress; + u32 client_index; + u32 context; + vl_api_ip4_address_t first_ip_address; + vl_api_ip4_address_t last_ip_address; + u32 vrf_id; + bool is_add; +}; + +/** \brief Dump NAT44 addresses + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request +*/ +define nat44_ei_address_dump { + option in_progress; + u32 client_index; + u32 context; +}; + +/** \brief NAT44 address details response + @param context - sender context, to match reply w/ request + @param ip_address - IPv4 address + @param vrf_id - VRF id of tenant, ~0 means independent of VRF +*/ +define nat44_ei_address_details { + option in_progress; + u32 context; + vl_api_ip4_address_t ip_address; + u32 vrf_id; +}; + +/** \brief Enable/disable NAT44 feature on the interface + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request + @param is_add - true if add, false if delete + @param flags - flag NAT_IS_INSIDE if interface is inside else + interface is outside + @param sw_if_index - software index of the interface +*/ +autoreply define nat44_ei_interface_add_del_feature { + option in_progress; + u32 client_index; + u32 context; + bool is_add; + vl_api_nat44_ei_config_flags_t flags; + vl_api_interface_index_t sw_if_index; +}; + +/** \brief Dump interfaces with NAT44 feature + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request +*/ +define nat44_ei_interface_dump { + option in_progress; + u32 client_index; + u32 context; +}; + +/** \brief NAT44 interface details response + @param context - sender context, to match reply w/ request + @param sw_if_index - software index of the interface + @param flags - flag NAT_IS_INSIDE if interface is inside, + flag NAT_IS_OUTSIDE if interface is outside + and if both flags are set the interface is + both inside and outside +*/ +define nat44_ei_interface_details { + option in_progress; + u32 context; + vl_api_nat44_ei_config_flags_t flags; + vl_api_interface_index_t sw_if_index; +}; + +/** \brief Enable/disbale NAT44 as an interface output feature (postrouting + in2out translation) + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request + @param is_add - true if add, false if delete + @param flags - flag NAT_IS_INSIDE if interface is inside else + interface is outside + @param sw_if_index - software index of the interface +*/ +autoreply define nat44_ei_interface_add_del_output_feature { + option deprecated; + u32 client_index; + u32 context; + bool is_add; + vl_api_nat44_ei_config_flags_t flags; + vl_api_interface_index_t sw_if_index; +}; + +/** \brief Dump interfaces with NAT44 output feature + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request +*/ +define nat44_ei_interface_output_feature_dump { + option deprecated; + u32 client_index; + u32 context; +}; + +/** \brief NAT44 interface with output feature details response + @param context - sender context, to match reply w/ request + @param flags - flag NAT_IS_INSIDE if interface is inside else + interface is outside + @param sw_if_index - software index of the interface +*/ +define nat44_ei_interface_output_feature_details { + option deprecated; + u32 context; + vl_api_nat44_ei_config_flags_t flags; + vl_api_interface_index_t sw_if_index; +}; + +/** \brief Add/delete NAT44 static mapping + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request + @param is_add - true if add, false if delete + @param flags - flag NAT44_EI_IS_ADDR_ONLY if address only mapping + @param local_ip_address - local IPv4 address + @param external_ip_address - external IPv4 address + @param protocol - IP protocol, used only if addr_only=0 + @param local_port - local port number, used only if addr_only=0 + @param external_port - external port number, used only if addr_only=0 + @param external_sw_if_index - external interface (if set + external_ip_address is ignored, ~0 means not + used) + @param vfr_id - VRF ID + @param tag - opaque string tag +*/ +autoreply define nat44_ei_add_del_static_mapping { + option in_progress; + u32 client_index; + u32 context; + bool is_add; + vl_api_nat44_ei_config_flags_t flags; + vl_api_ip4_address_t local_ip_address; + vl_api_ip4_address_t external_ip_address; + u8 protocol; + u16 local_port; + u16 external_port; + vl_api_interface_index_t external_sw_if_index; + u32 vrf_id; + string tag[64]; +}; + +/** \brief Dump NAT44 static mappings + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request +*/ +define nat44_ei_static_mapping_dump { + option in_progress; + u32 client_index; + u32 context; +}; + +/** \brief NAT44 static mapping details response + @param context - sender context, to match reply w/ request + @param flags - flag NAT44_EI_IS_ADDR_ONLY if address only mapping, + @param local_ip_address - local IPv4 address + @param external_ip_address - external IPv4 address + @param protocol - IP protocol, valid only if no NAT_ADDR_ONLY flag + @param local_port - local port number, valid only if no NAT_ADDR_ONLY flag + @param external_port - external port number, valid only if no NAT_ADDR_ONLY flag + @param external_sw_if_index - external interface + @param vfr_id - VRF ID + @param tag - opaque string tag +*/ +define nat44_ei_static_mapping_details { + option in_progress; + u32 context; + vl_api_nat44_ei_config_flags_t flags; + vl_api_ip4_address_t local_ip_address; + vl_api_ip4_address_t external_ip_address; + u8 protocol; + u16 local_port; + u16 external_port; + vl_api_interface_index_t external_sw_if_index; + u32 vrf_id; + string tag[64]; +}; + +/** \brief Add/delete NAT44 identity mapping + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request + @param is_add - true if add, false if delete + @param flags - flag NAT44_EI_IS_ADDR_ONLY if address only mapping + @param ip_address - IPv4 address + @param protocol - IP protocol + @param port - port number + @param sw_if_index - interface (if set ip_address is ignored, ~0 means not + used) + @param vfr_id - VRF ID (if ~0 use default VRF) + @param tag - opaque string tag +*/ +autoreply define nat44_ei_add_del_identity_mapping { + option deprecated; + u32 client_index; + u32 context; + bool is_add; + vl_api_nat44_ei_config_flags_t flags; + vl_api_ip4_address_t ip_address; + u8 protocol; + u16 port; + vl_api_interface_index_t sw_if_index; + u32 vrf_id; + string tag[64]; +}; + +/** \brief Dump NAT44 identity mappings + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request +*/ +define nat44_ei_identity_mapping_dump { + option deprecated; + u32 client_index; + u32 context; +}; + +/** \brief NAT44 identity mapping details response + @param context - sender context, to match reply w/ request + @param flags - flag NAT44_EI_ADDR_ONLY if address only mapping + @param ip_address - IPv4 address + @param protocol - IP protocol + @param port - port number + @param sw_if_index - interface + @param vfr_id - VRF ID + @param tag - opaque string tag +*/ +define nat44_ei_identity_mapping_details { + option deprecated; + u32 context; + vl_api_nat44_ei_config_flags_t flags; + vl_api_ip4_address_t ip_address; + u8 protocol; + u16 port; + vl_api_interface_index_t sw_if_index; + u32 vrf_id; + string tag[64]; +}; + +/** \brief Add/delete NAT44 pool address from specific interfce + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request + @param is_add - true if add, false if delete + @param sw_if_index - software index of the interface + @param flags - flag NAT_TWICE_NAT if NAT address range for external hosts +*/ +autoreply define nat44_ei_add_del_interface_addr { + option deprecated; + u32 client_index; + u32 context; + bool is_add; + vl_api_interface_index_t sw_if_index; + vl_api_nat44_ei_config_flags_t flags; +}; + +/** \brief Dump NAT44 pool addresses interfaces + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request +*/ +define nat44_ei_interface_addr_dump { + option deprecated; + u32 client_index; + u32 context; +}; + +/** \brief NAT44 pool addresses interfaces details response + @param context - sender context, to match reply w/ request + @param sw_if_index - software index of the interface + +*/ +define nat44_ei_interface_addr_details { + option deprecated; + u32 context; + vl_api_interface_index_t sw_if_index; +}; + +/** \brief Dump NAT44 users + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request +*/ +define nat44_ei_user_dump { + option in_progress; + u32 client_index; + u32 context; +}; + +/** \brief NAT44 users response + @param context - sender context, to match reply w/ request + @vrf_id - VRF ID + @param ip_address - IPv4 address + @param nsessions - number of dynamic sessions + @param nstaticsessions - number of static sessions +*/ +define nat44_ei_user_details { + option in_progress; + u32 context; + u32 vrf_id; + vl_api_ip4_address_t ip_address; + u32 nsessions; + u32 nstaticsessions; +}; + +/** \brief NAT44 user's sessions + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request + @param ip_address - IPv4 address of the user to dump + @param vrf_id - VRF_ID +*/ +define nat44_ei_user_session_dump { + option in_progress; + u32 client_index; + u32 context; + vl_api_ip4_address_t ip_address; + u32 vrf_id; +}; + +/** \brief NAT44 user's sessions response + @param context - sender context, to match reply w/ request + @param outside_ip_address - outside IPv4 address + @param outside_port - outside port + @param inside_ip_address - inside IPv4 address + @param inside_port - inside port + @param protocol - protocol + @param flags - flag NAT_IS_STATIC if session is static + @param last_heard - last heard timer + @param total_bytes - count of bytes sent through session + @param total_pkts - count of pakets sent through session + @param ext_host_address - external host IPv4 address + @param ext_host_port - external host port +*/ +define nat44_ei_user_session_details { + option in_progress; + u32 context; + vl_api_ip4_address_t outside_ip_address; + u16 outside_port; + vl_api_ip4_address_t inside_ip_address; + u16 inside_port; + u16 protocol; + vl_api_nat44_ei_config_flags_t flags; + u64 last_heard; + u64 total_bytes; + u32 total_pkts; + vl_api_ip4_address_t ext_host_address; + u16 ext_host_port; +}; + +/** \brief Delete NAT44 session + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request + @param ip_address - IPv4 address + @param protocol - IP protocol + @param port - port number + @param vfr_id - VRF ID + @param flags - flag NAT_IS_INSIDE if interface is inside or + interface is outside, + flag NAT_IS_EXT_HOST_VALID if external host address and + port are valid + @param ext_host_address - external host IPv4 address + @param ext_host_port - external host port +*/ +autoreply define nat44_ei_del_session { + option in_progress; + u32 client_index; + u32 context; + vl_api_ip4_address_t address; + u8 protocol; + u16 port; + u32 vrf_id; + vl_api_nat44_ei_config_flags_t flags; + vl_api_ip4_address_t ext_host_address; + u16 ext_host_port; +}; + +/** \brief Enable/disable forwarding for NAT44 + Forward packets which don't match existing translation + or static mapping instead of dropping them. + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request + @param enable - true for enable, false for disable +*/ +autoreply define nat44_ei_forwarding_enable_disable { + option in_progress; + u32 client_index; + u32 context; + bool enable; +}; + +/** \brief Set NAT handoff frame queue options + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request + @param frame_queue_nelts - number of worker handoff frame queue elements +*/ +autoreply define nat44_ei_set_fq_options { + option in_progress; + u32 client_index; + u32 context; + u32 frame_queue_nelts; +}; + +/** \brief Show NAT handoff frame queue options + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request +*/ +define nat44_ei_show_fq_options +{ + option in_progress; + u32 client_index; + u32 context; +}; + +/** \brief Show NAT handoff frame queue options reply + @param context - sender context, to match reply w/ request + @param retval - return code for the request + @param frame_queue_nelts - number of worker handoff frame queue elements +*/ +define nat44_ei_show_fq_options_reply +{ + option in_progress; + u32 context; + i32 retval; + u32 frame_queue_nelts; +}; diff --git a/src/plugins/nat/nat44-ei/nat44_ei.c b/src/plugins/nat/nat44-ei/nat44_ei.c index 02403d0cd99..fdf90708a09 100644 --- a/src/plugins/nat/nat44-ei/nat44_ei.c +++ b/src/plugins/nat/nat44-ei/nat44_ei.c @@ -15,393 +15,1402 @@ * under the License. */ +#include +#include + #include #include #include -#include -#include -#include -#include -#include -#include -#include -#include +#include +#include #include #include -#include -#include -#include -#include +#include + +// nat lib +#include +#include +#include +#include +#include #include #include nat44_ei_main_t nat44_ei_main; -static void nat44_ei_db_free (); +extern vlib_node_registration_t nat44_ei_hairpinning_node; +extern vlib_node_registration_t nat44_ei_hairpin_dst_node; +extern vlib_node_registration_t + nat44_ei_in2out_hairpinning_finish_ip4_lookup_node; +extern vlib_node_registration_t + nat44_ei_in2out_hairpinning_finish_interface_output_node; + +#define skip_if_disabled() \ + do \ + { \ + nat44_ei_main_t *nm = &nat44_ei_main; \ + if (PREDICT_FALSE (!nm->enabled)) \ + return; \ + } \ + while (0) + +#define fail_if_enabled() \ + do \ + { \ + nat44_ei_main_t *nm = &nat44_ei_main; \ + if (PREDICT_FALSE (nm->enabled)) \ + { \ + nat44_ei_log_err ("plugin enabled"); \ + return 1; \ + } \ + } \ + while (0) + +#define fail_if_disabled() \ + do \ + { \ + nat44_ei_main_t *nm = &nat44_ei_main; \ + if (PREDICT_FALSE (!nm->enabled)) \ + { \ + nat44_ei_log_err ("plugin disabled"); \ + return 1; \ + } \ + } \ + while (0) + +/* Hook up input features */ +VNET_FEATURE_INIT (ip4_nat_classify, static) = { + .arc_name = "ip4-unicast", + .node_name = "nat44-ei-classify", + .runs_after = VNET_FEATURES ("acl-plugin-in-ip4-fa", + "ip4-sv-reassembly-feature"), +}; +VNET_FEATURE_INIT (ip4_nat44_ei_in2out, static) = { + .arc_name = "ip4-unicast", + .node_name = "nat44-ei-in2out", + .runs_after = VNET_FEATURES ("acl-plugin-in-ip4-fa", + "ip4-sv-reassembly-feature"), +}; +VNET_FEATURE_INIT (ip4_nat44_ei_out2in, static) = { + .arc_name = "ip4-unicast", + .node_name = "nat44-ei-out2in", + .runs_after = VNET_FEATURES ("acl-plugin-in-ip4-fa", + "ip4-sv-reassembly-feature", + "ip4-dhcp-client-detect"), +}; +VNET_FEATURE_INIT (ip4_nat44_ei_in2out_output, static) = { + .arc_name = "ip4-output", + .node_name = "nat44-ei-in2out-output", + .runs_after = VNET_FEATURES ("acl-plugin-out-ip4-fa", + "ip4-sv-reassembly-output-feature"), +}; +VNET_FEATURE_INIT (ip4_nat44_ei_in2out_fast, static) = { + .arc_name = "ip4-unicast", + .node_name = "nat44-ei-in2out-fast", + .runs_after = VNET_FEATURES ("acl-plugin-in-ip4-fa", + "ip4-sv-reassembly-feature"), +}; +VNET_FEATURE_INIT (ip4_nat44_ei_out2in_fast, static) = { + .arc_name = "ip4-unicast", + .node_name = "nat44-ei-out2in-fast", + .runs_after = VNET_FEATURES ("acl-plugin-in-ip4-fa", + "ip4-sv-reassembly-feature", + "ip4-dhcp-client-detect"), +}; +VNET_FEATURE_INIT (ip4_nat44_ei_hairpin_dst, static) = { + .arc_name = "ip4-unicast", + .node_name = "nat44-ei-hairpin-dst", + .runs_after = VNET_FEATURES ("acl-plugin-in-ip4-fa", + "ip4-sv-reassembly-feature"), +}; +VNET_FEATURE_INIT (ip4_nat44_ei_hairpin_src, static) = { + .arc_name = "ip4-output", + .node_name = "nat44-ei-hairpin-src", + .runs_after = VNET_FEATURES ("acl-plugin-out-ip4-fa", + "ip4-sv-reassembly-output-feature"), +}; +VNET_FEATURE_INIT (ip4_nat44_ei_hairpinning, static) = { + .arc_name = "ip4-local", + .node_name = "nat44-ei-hairpinning", + .runs_before = VNET_FEATURES ("ip4-local-end-of-arc"), +}; +VNET_FEATURE_INIT (ip4_nat44_ei_in2out_worker_handoff, static) = { + .arc_name = "ip4-unicast", + .node_name = "nat44-ei-in2out-worker-handoff", + .runs_after = VNET_FEATURES ("acl-plugin-in-ip4-fa"), +}; +VNET_FEATURE_INIT (ip4_nat44_ei_out2in_worker_handoff, static) = { + .arc_name = "ip4-unicast", + .node_name = "nat44-ei-out2in-worker-handoff", + .runs_after = VNET_FEATURES ("acl-plugin-in-ip4-fa", + "ip4-dhcp-client-detect"), +}; +VNET_FEATURE_INIT (ip4_nat44_ei_in2out_output_worker_handoff, static) = { + .arc_name = "ip4-output", + .node_name = "nat44-ei-in2out-output-worker-handoff", + .runs_after = VNET_FEATURES ("acl-plugin-out-ip4-fa", + "ip4-sv-reassembly-output-feature"), +}; + +VLIB_PLUGIN_REGISTER () = { + .version = VPP_BUILD_VER, + .description = "IPv4 Endpoint-Independent NAT (NAT44 EI)", +}; + +#define foreach_nat44_ei_classify_error \ + _ (NEXT_IN2OUT, "next in2out") \ + _ (NEXT_OUT2IN, "next out2in") \ + _ (FRAG_CACHED, "fragment cached") + +typedef enum +{ +#define _(sym, str) NAT44_EI_CLASSIFY_ERROR_##sym, + foreach_nat44_ei_classify_error +#undef _ + NAT44_EI_CLASSIFY_N_ERROR, +} nat44_ei_classify_error_t; -static void nat44_ei_db_init (u32 translations, u32 translation_buckets, - u32 user_buckets); +static char *nat44_ei_classify_error_strings[] = { +#define _(sym, string) string, + foreach_nat44_ei_classify_error +#undef _ +}; -int -nat44_ei_plugin_enable (nat44_ei_config_t c) +typedef enum { - nat44_ei_main_t *nm = &nat44_ei_main; - snat_main_t *sm = &snat_main; + NAT44_EI_CLASSIFY_NEXT_IN2OUT, + NAT44_EI_CLASSIFY_NEXT_OUT2IN, + NAT44_EI_CLASSIFY_NEXT_DROP, + NAT44_EI_CLASSIFY_N_NEXT, +} nat44_ei_classify_next_t; - clib_memset (nm, 0, sizeof (*nm)); +typedef struct +{ + u8 next_in2out; + u8 cached; +} nat44_ei_classify_trace_t; - if (!c.users) - c.users = 1024; +void nat44_ei_add_del_addr_to_fib (ip4_address_t *addr, u8 p_len, + u32 sw_if_index, int is_add); - if (!c.sessions) - c.sessions = 10 * 1024; +static u8 * +format_nat44_ei_classify_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 *); + nat44_ei_classify_trace_t *t = va_arg (*args, nat44_ei_classify_trace_t *); + char *next; - nm->rconfig = c; + if (t->cached) + s = format (s, "nat44-ei-classify: fragment cached"); + else + { + next = t->next_in2out ? "nat44-ei-in2out" : "nat44-ei-out2in"; + s = format (s, "nat44-ei-classify: next %s", next); + } - nm->translations = c.sessions; - nm->translation_buckets = nat_calc_bihash_buckets (c.sessions); - nm->user_buckets = nat_calc_bihash_buckets (c.users); + return s; +} - // OBSOLETE +static void nat44_ei_db_free (); - sm->static_mapping_only = c.static_mapping_only; - sm->static_mapping_connection_tracking = c.connection_tracking; - sm->out2in_dpo = c.out2in_dpo; - sm->forwarding_enabled = 0; - sm->mss_clamping = 0; - sm->pat = (!c.static_mapping_only || - (c.static_mapping_only && c.connection_tracking)); +static void nat44_ei_db_init (u32 translations, u32 translation_buckets, + u32 user_buckets); - sm->max_users_per_thread = c.users; - sm->max_translations_per_thread = c.sessions; - sm->translation_buckets = nat_calc_bihash_buckets (c.sessions); - sm->max_translations_per_user = - c.user_sessions ? c.user_sessions : sm->max_translations_per_thread; +static void nat44_ei_ip4_add_del_interface_address_cb ( + ip4_main_t *im, uword opaque, u32 sw_if_index, ip4_address_t *address, + u32 address_length, u32 if_address_index, u32 is_delete); - sm->inside_vrf_id = c.inside_vrf; - sm->inside_fib_index = fib_table_find_or_create_and_lock ( - FIB_PROTOCOL_IP4, c.inside_vrf, sm->fib_src_hi); +static void nat44_ei_ip4_add_del_addr_only_sm_cb ( + ip4_main_t *im, uword opaque, u32 sw_if_index, ip4_address_t *address, + u32 address_length, u32 if_address_index, u32 is_delete); - sm->outside_vrf_id = c.outside_vrf; - sm->outside_fib_index = fib_table_find_or_create_and_lock ( - FIB_PROTOCOL_IP4, c.outside_vrf, sm->fib_src_hi); +static void nat44_ei_update_outside_fib (ip4_main_t *im, uword opaque, + u32 sw_if_index, u32 new_fib_index, + u32 old_fib_index); - sm->worker_in2out_cb = nat44_ei_get_in2out_worker_index; - sm->worker_out2in_cb = nat44_ei_get_out2in_worker_index; +void +nat44_ei_set_node_indexes (nat44_ei_main_t *nm, vlib_main_t *vm) +{ + vlib_node_t *node; + node = vlib_get_node_by_name (vm, (u8 *) "nat44-ei-out2in"); + nm->out2in_node_index = node->index; + node = vlib_get_node_by_name (vm, (u8 *) "nat44-ei-in2out"); + nm->in2out_node_index = node->index; + node = vlib_get_node_by_name (vm, (u8 *) "nat44-ei-in2out-output"); + nm->in2out_output_node_index = node->index; +} - sm->in2out_node_index = sm->ei_in2out_node_index; - sm->out2in_node_index = sm->ei_out2in_node_index; +int +nat44_ei_set_workers (uword *bitmap) +{ + nat44_ei_main_t *nm = &nat44_ei_main; + int i, j = 0; - sm->in2out_output_node_index = sm->ei_in2out_output_node_index; + if (nm->num_workers < 2) + return VNET_API_ERROR_FEATURE_DISABLED; - if (sm->pat) - { - sm->icmp_match_in2out_cb = icmp_match_in2out_slow; - sm->icmp_match_out2in_cb = icmp_match_out2in_slow; - } - else + if (clib_bitmap_last_set (bitmap) >= nm->num_workers) + return VNET_API_ERROR_INVALID_WORKER; + + vec_free (nm->workers); + clib_bitmap_foreach (i, bitmap) { - sm->icmp_match_in2out_cb = icmp_match_in2out_fast; - sm->icmp_match_out2in_cb = icmp_match_out2in_fast; + vec_add1 (nm->workers, i); + nm->per_thread_data[nm->first_worker_index + i].snat_thread_index = j; + nm->per_thread_data[nm->first_worker_index + i].thread_index = i; + j++; } - nat_reset_timeouts (&sm->timeouts); - nat44_ei_db_init (nm->translations, nm->translation_buckets, - nm->user_buckets); - nat44_ei_set_alloc_default (); - nat_ha_enable (); - - // TODO: function for reset counters - vlib_zero_simple_counter (&sm->total_users, 0); - vlib_zero_simple_counter (&sm->total_sessions, 0); - vlib_zero_simple_counter (&sm->user_limit_reached, 0); + nm->port_per_thread = (0xffff - 1024) / _vec_len (nm->workers); - if (!sm->frame_queue_nelts) - sm->frame_queue_nelts = NAT_FQ_NELTS_DEFAULT; + return 0; +} - sm->enabled = 1; +#define nat_validate_simple_counter(c, i) \ + do \ + { \ + vlib_validate_simple_counter (&c, i); \ + vlib_zero_simple_counter (&c, i); \ + } \ + while (0); + +#define nat_init_simple_counter(c, n, sn) \ + do \ + { \ + c.name = n; \ + c.stat_segment_name = sn; \ + nat_validate_simple_counter (c, 0); \ + } \ + while (0); - return 0; +static_always_inline void +nat_validate_interface_counters (nat44_ei_main_t *nm, u32 sw_if_index) +{ +#define _(x) \ + nat_validate_simple_counter (nm->counters.fastpath.in2out.x, sw_if_index); \ + nat_validate_simple_counter (nm->counters.fastpath.out2in.x, sw_if_index); \ + nat_validate_simple_counter (nm->counters.slowpath.in2out.x, sw_if_index); \ + nat_validate_simple_counter (nm->counters.slowpath.out2in.x, sw_if_index); + foreach_nat_counter; +#undef _ + nat_validate_simple_counter (nm->counters.hairpinning, sw_if_index); } -int -nat44_ei_plugin_disable () +clib_error_t * +nat44_ei_init (vlib_main_t *vm) { nat44_ei_main_t *nm = &nat44_ei_main; - snat_main_t *sm = &snat_main; - snat_interface_t *i, *vec; - int error = 0; + vlib_thread_main_t *tm = vlib_get_thread_main (); + vlib_thread_registration_t *tr; + ip4_add_del_interface_address_callback_t cbi = { 0 }; + ip4_table_bind_callback_t cbt = { 0 }; + u32 i, num_threads = 0; + uword *p, *bitmap = 0; - // first unregister all nodes from interfaces - vec = vec_dup (sm->interfaces); - vec_foreach (i, vec) - { - if (nat_interface_is_inside (i)) - error = snat_interface_add_del (i->sw_if_index, 1, 1); - if (nat_interface_is_outside (i)) - error = snat_interface_add_del (i->sw_if_index, 0, 1); + clib_memset (nm, 0, sizeof (*nm)); - if (error) + // required + nm->vnet_main = vnet_get_main (); + // convenience + nm->ip4_main = &ip4_main; + nm->api_main = vlibapi_get_main (); + nm->ip4_lookup_main = &ip4_main.lookup_main; + + // handoff stuff + nm->fq_out2in_index = ~0; + nm->fq_in2out_index = ~0; + nm->fq_in2out_output_index = ~0; + nm->worker_in2out_cb = nat44_ei_get_in2out_worker_index; + nm->worker_out2in_cb = nat44_ei_get_out2in_worker_index; + + nm->log_level = NAT_LOG_ERROR; + + nat44_ei_set_node_indexes (nm, vm); + nm->log_class = vlib_log_register_class ("nat44-ei", 0); + + nat_init_simple_counter (nm->total_users, "total-users", + "/nat44-ei/total-users"); + nat_init_simple_counter (nm->total_sessions, "total-sessions", + "/nat44-ei/total-sessions"); + nat_init_simple_counter (nm->user_limit_reached, "user-limit-reached", + "/nat44-ei/user-limit-reached"); + +#define _(x) \ + nat_init_simple_counter (nm->counters.fastpath.in2out.x, #x, \ + "/nat44-ei/in2out/fastpath/" #x); \ + nat_init_simple_counter (nm->counters.fastpath.out2in.x, #x, \ + "/nat44-ei/out2in/fastpath/" #x); \ + nat_init_simple_counter (nm->counters.slowpath.in2out.x, #x, \ + "/nat44-ei/in2out/slowpath/" #x); \ + nat_init_simple_counter (nm->counters.slowpath.out2in.x, #x, \ + "/nat44-ei/out2in/slowpath/" #x); + foreach_nat_counter; +#undef _ + nat_init_simple_counter (nm->counters.hairpinning, "hairpinning", + "/nat44-ei/hairpinning"); + + p = hash_get_mem (tm->thread_registrations_by_name, "workers"); + if (p) + { + tr = (vlib_thread_registration_t *) p[0]; + if (tr) { - nat_log_err ("error occurred while removing interface %u", - i->sw_if_index); + nm->num_workers = tr->count; + nm->first_worker_index = tr->first_index; } } - vec_free (vec); - sm->interfaces = 0; + num_threads = tm->n_vlib_mains - 1; + nm->port_per_thread = 0xffff - 1024; + vec_validate (nm->per_thread_data, num_threads); - vec = vec_dup (sm->output_feature_interfaces); - vec_foreach (i, vec) + /* Use all available workers by default */ + if (nm->num_workers > 1) { - if (nat_interface_is_inside (i)) - error = snat_interface_add_del_output_feature (i->sw_if_index, 1, 1); - if (nat_interface_is_outside (i)) - error = snat_interface_add_del_output_feature (i->sw_if_index, 0, 1); - if (error) - { - nat_log_err ("error occurred while removing interface %u", - i->sw_if_index); - } + for (i = 0; i < nm->num_workers; i++) + bitmap = clib_bitmap_set (bitmap, i, 1); + nat44_ei_set_workers (bitmap); + clib_bitmap_free (bitmap); } - vec_free (vec); - sm->output_feature_interfaces = 0; + else + nm->per_thread_data[0].snat_thread_index = 0; + + /* callbacks to call when interface address changes. */ + cbi.function = nat44_ei_ip4_add_del_interface_address_cb; + vec_add1 (nm->ip4_main->add_del_interface_address_callbacks, cbi); + cbi.function = nat44_ei_ip4_add_del_addr_only_sm_cb; + vec_add1 (nm->ip4_main->add_del_interface_address_callbacks, cbi); + + /* callbacks to call when interface to table biding changes */ + cbt.function = nat44_ei_update_outside_fib; + vec_add1 (nm->ip4_main->table_bind_callbacks, cbt); + + nm->fib_src_low = fib_source_allocate ( + "nat44-ei-low", FIB_SOURCE_PRIORITY_LOW, FIB_SOURCE_BH_SIMPLE); + nm->fib_src_hi = fib_source_allocate ("nat44-ei-hi", FIB_SOURCE_PRIORITY_HI, + FIB_SOURCE_BH_SIMPLE); + + // used only by out2in-dpo feature + nat_dpo_module_init (); + nat_ha_init (vm, nm->num_workers, num_threads); + + nm->hairpinning_fq_index = + vlib_frame_queue_main_init (nat44_ei_hairpinning_node.index, 0); + nm->hairpin_dst_fq_index = + vlib_frame_queue_main_init (nat44_ei_hairpin_dst_node.index, 0); + nm->in2out_hairpinning_finish_ip4_lookup_node_fq_index = + vlib_frame_queue_main_init ( + nat44_ei_in2out_hairpinning_finish_ip4_lookup_node.index, 0); + nm->in2out_hairpinning_finish_interface_output_node_fq_index = + vlib_frame_queue_main_init ( + nat44_ei_in2out_hairpinning_finish_interface_output_node.index, 0); + return nat44_ei_api_hookup (vm); +} - nat_ha_disable (); - nat44_ei_db_free (); +VLIB_INIT_FUNCTION (nat44_ei_init); - nat44_addresses_free (&sm->addresses); - nat44_addresses_free (&sm->twice_nat_addresses); +int +nat44_ei_plugin_enable (nat44_ei_config_t c) +{ + nat44_ei_main_t *nm = &nat44_ei_main; - vec_free (sm->to_resolve); - vec_free (sm->auto_add_sw_if_indices); - vec_free (sm->auto_add_sw_if_indices_twice_nat); + fail_if_enabled (); - sm->to_resolve = 0; - sm->auto_add_sw_if_indices = 0; - sm->auto_add_sw_if_indices_twice_nat = 0; + if (!c.users) + c.users = 1024; - sm->forwarding_enabled = 0; + if (!c.sessions) + c.sessions = 10 * 1024; - sm->enabled = 0; - clib_memset (&nm->rconfig, 0, sizeof (nm->rconfig)); - clib_memset (&sm->rconfig, 0, sizeof (sm->rconfig)); + nm->rconfig = c; - return error; -} + if (!nm->frame_queue_nelts) + nm->frame_queue_nelts = NAT_FQ_NELTS_DEFAULT; -void -nat44_ei_free_session_data (snat_main_t *sm, snat_session_t *s, - u32 thread_index, u8 is_ha) -{ - clib_bihash_kv_8_8_t kv; + nm->translations = c.sessions; + nm->translation_buckets = nat_calc_bihash_buckets (c.sessions); + nm->user_buckets = nat_calc_bihash_buckets (c.users); - init_nat_i2o_k (&kv, s); - if (clib_bihash_add_del_8_8 (&sm->in2out, &kv, 0)) - nat_elog_warn ("in2out key del failed"); + nm->pat = (!c.static_mapping_only || + (c.static_mapping_only && c.connection_tracking)); - init_nat_o2i_k (&kv, s); - if (clib_bihash_add_del_8_8 (&sm->out2in, &kv, 0)) - nat_elog_warn ("out2in key del failed"); + nm->static_mapping_only = c.static_mapping_only; + nm->static_mapping_connection_tracking = c.connection_tracking; + nm->out2in_dpo = c.out2in_dpo; + nm->forwarding_enabled = 0; + nm->mss_clamping = 0; - if (!is_ha) - { - nat_syslog_nat44_apmdel (s->user_index, s->in2out.fib_index, - &s->in2out.addr, s->in2out.port, - &s->out2in.addr, s->out2in.port, s->nat_proto); + nm->max_users_per_thread = c.users; + nm->max_translations_per_thread = c.sessions; + nm->max_translations_per_user = + c.user_sessions ? c.user_sessions : nm->max_translations_per_thread; - nat_ipfix_logging_nat44_ses_delete ( - thread_index, s->in2out.addr.as_u32, s->out2in.addr.as_u32, - s->nat_proto, s->in2out.port, s->out2in.port, s->in2out.fib_index); + nm->inside_vrf_id = c.inside_vrf; + nm->inside_fib_index = fib_table_find_or_create_and_lock ( + FIB_PROTOCOL_IP4, c.inside_vrf, nm->fib_src_hi); - nat_ha_sdel (&s->out2in.addr, s->out2in.port, &s->ext_host_addr, - s->ext_host_port, s->nat_proto, s->out2in.fib_index, - thread_index); - } + nm->outside_vrf_id = c.outside_vrf; + nm->outside_fib_index = fib_table_find_or_create_and_lock ( + FIB_PROTOCOL_IP4, c.outside_vrf, nm->fib_src_hi); - if (snat_is_session_static (s)) - return; + nat_reset_timeouts (&nm->timeouts); + nat44_ei_db_init (nm->translations, nm->translation_buckets, + nm->user_buckets); + nat44_ei_set_alloc_default (); - snat_free_outside_address_and_port (sm->addresses, thread_index, - &s->out2in.addr, s->out2in.port, - s->nat_proto); -} + // TODO: zero simple counter for all counters missing -static_always_inline void -nat44_ei_user_del_sessions (snat_user_t *u, u32 thread_index) -{ - dlist_elt_t *elt; - snat_session_t *s; + vlib_zero_simple_counter (&nm->total_users, 0); + vlib_zero_simple_counter (&nm->total_sessions, 0); + vlib_zero_simple_counter (&nm->user_limit_reached, 0); - snat_main_t *sm = &snat_main; - snat_main_per_thread_data_t *tsm = &sm->per_thread_data[thread_index]; + nat_ha_enable (); + nm->enabled = 1; - // get head - elt = - pool_elt_at_index (tsm->list_pool, u->sessions_per_user_list_head_index); - // get first element - elt = pool_elt_at_index (tsm->list_pool, elt->next); + return 0; +} - while (elt->value != ~0) +void +nat44_ei_addresses_free (nat44_ei_address_t **addresses) +{ + nat44_ei_address_t *ap; + vec_foreach (ap, *addresses) { - s = pool_elt_at_index (tsm->sessions, elt->value); - elt = pool_elt_at_index (tsm->list_pool, elt->next); - - nat44_ei_free_session_data (sm, s, thread_index, 0); - nat44_delete_session (sm, s, thread_index); +#define _(N, i, n, s) vec_free (ap->busy_##n##_ports_per_thread); + foreach_nat_protocol +#undef _ } + vec_free (*addresses); + *addresses = 0; } int -nat44_ei_user_del (ip4_address_t *addr, u32 fib_index) +nat44_ei_interface_add_del (u32 sw_if_index, u8 is_inside, int is_del) { - int rv = 1; + const char *feature_name, *del_feature_name; + nat44_ei_main_t *nm = &nat44_ei_main; + nat44_ei_interface_t *i; + nat44_ei_address_t *ap; + nat44_ei_static_mapping_t *m; + nat44_ei_outside_fib_t *outside_fib; + u32 fib_index = + fib_table_get_index_for_sw_if_index (FIB_PROTOCOL_IP4, sw_if_index); - snat_main_t *sm = &snat_main; - snat_main_per_thread_data_t *tsm; + fail_if_disabled (); - snat_user_key_t user_key; - clib_bihash_kv_8_8_t kv, value; + if (nm->out2in_dpo && !is_inside) + { + nat44_ei_log_err ("error unsupported"); + return VNET_API_ERROR_UNSUPPORTED; + } - if (sm->endpoint_dependent) - return rv; + pool_foreach (i, nm->output_feature_interfaces) + { + if (i->sw_if_index == sw_if_index) + { + nat44_ei_log_err ("error interface already configured"); + return VNET_API_ERROR_VALUE_EXIST; + } + } - user_key.addr.as_u32 = addr->as_u32; - user_key.fib_index = fib_index; - kv.key = user_key.as_u64; + if (nm->static_mapping_only && !(nm->static_mapping_connection_tracking)) + feature_name = is_inside ? "nat44-ei-in2out-fast" : "nat44-ei-out2in-fast"; + else + { + if (nm->num_workers > 1) + feature_name = is_inside ? "nat44-ei-in2out-worker-handoff" : + "nat44-ei-out2in-worker-handoff"; + else + feature_name = is_inside ? "nat44-ei-in2out" : "nat44-ei-out2in"; + } - if (sm->num_workers > 1) + if (nm->fq_in2out_index == ~0 && nm->num_workers > 1) + nm->fq_in2out_index = vlib_frame_queue_main_init (nm->in2out_node_index, + nm->frame_queue_nelts); + + if (nm->fq_out2in_index == ~0 && nm->num_workers > 1) + nm->fq_out2in_index = vlib_frame_queue_main_init (nm->out2in_node_index, + nm->frame_queue_nelts); + + if (!is_inside) { - vec_foreach (tsm, sm->per_thread_data) + vec_foreach (outside_fib, nm->outside_fibs) { - if (!clib_bihash_search_8_8 (&tsm->user_hash, &kv, &value)) + if (outside_fib->fib_index == fib_index) { - nat44_ei_user_del_sessions ( - pool_elt_at_index (tsm->users, value.value), - tsm->thread_index); - rv = 0; - break; + if (is_del) + { + outside_fib->refcount--; + if (!outside_fib->refcount) + vec_del1 (nm->outside_fibs, + outside_fib - nm->outside_fibs); + } + else + outside_fib->refcount++; + goto feature_set; } } - } - else - { - tsm = vec_elt_at_index (sm->per_thread_data, sm->num_workers); - if (!clib_bihash_search_8_8 (&tsm->user_hash, &kv, &value)) + if (!is_del) { - nat44_ei_user_del_sessions ( - pool_elt_at_index (tsm->users, value.value), tsm->thread_index); - rv = 0; + vec_add2 (nm->outside_fibs, outside_fib, 1); + outside_fib->refcount = 1; + outside_fib->fib_index = fib_index; } } - return rv; -} - -void -nat44_ei_static_mapping_del_sessions (snat_main_t *sm, - snat_main_per_thread_data_t *tsm, - snat_user_key_t u_key, int addr_only, - ip4_address_t e_addr, u16 e_port) -{ - clib_bihash_kv_8_8_t kv, value; - kv.key = u_key.as_u64; - u64 user_index; - dlist_elt_t *head, *elt; - snat_user_t *u; - snat_session_t *s; - u32 elt_index, head_index, ses_index; - if (!clib_bihash_search_8_8 (&tsm->user_hash, &kv, &value)) +feature_set: + pool_foreach (i, nm->interfaces) { - user_index = value.value; - u = pool_elt_at_index (tsm->users, user_index); - if (u->nstaticsessions) + if (i->sw_if_index == sw_if_index) { - 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) + if (is_del) { - s = pool_elt_at_index (tsm->sessions, ses_index); - elt = pool_elt_at_index (tsm->list_pool, elt->next); - ses_index = elt->value; - - if (!addr_only) + if (nat44_ei_interface_is_inside (i) && + nat44_ei_interface_is_outside (i)) { - if ((s->out2in.addr.as_u32 != e_addr.as_u32) || - (s->out2in.port != e_port)) - continue; - } + if (is_inside) + i->flags &= ~NAT44_EI_INTERFACE_FLAG_IS_INSIDE; + else + i->flags &= ~NAT44_EI_INTERFACE_FLAG_IS_OUTSIDE; - if (is_lb_session (s)) - continue; + if (nm->num_workers > 1) + { + del_feature_name = "nat44-handoff-classify"; + feature_name = !is_inside ? + "nat44-ei-in2out-worker-handoff" : + "nat44-ei-out2in-worker-handoff"; + } + else + { + del_feature_name = "nat44-ei-classify"; + feature_name = + !is_inside ? "nat44-ei-in2out" : "nat44-ei-out2in"; + } - if (!snat_is_session_static (s)) - continue; + int rv = + ip4_sv_reass_enable_disable_with_refcnt (sw_if_index, 0); + if (rv) + return rv; + vnet_feature_enable_disable ("ip4-unicast", del_feature_name, + sw_if_index, 0, 0, 0); + vnet_feature_enable_disable ("ip4-unicast", feature_name, + sw_if_index, 1, 0, 0); + if (!is_inside) + { + vnet_feature_enable_disable ("ip4-local", + "nat44-ei-hairpinning", + sw_if_index, 1, 0, 0); + } + } + else + { + int rv = + ip4_sv_reass_enable_disable_with_refcnt (sw_if_index, 0); + if (rv) + return rv; + vnet_feature_enable_disable ("ip4-unicast", feature_name, + sw_if_index, 0, 0, 0); + pool_put (nm->interfaces, i); + if (is_inside) + { + vnet_feature_enable_disable ("ip4-local", + "nat44-ei-hairpinning", + sw_if_index, 0, 0, 0); + } + } + } + else + { + if ((nat44_ei_interface_is_inside (i) && is_inside) || + (nat44_ei_interface_is_outside (i) && !is_inside)) + return 0; - nat_free_session_data (sm, s, tsm - sm->per_thread_data, 0); - nat44_delete_session (sm, s, tsm - sm->per_thread_data); + if (nm->num_workers > 1) + { + del_feature_name = !is_inside ? + "nat44-ei-in2out-worker-handoff" : + "nat44-ei-out2in-worker-handoff"; + feature_name = "nat44-handoff-classify"; + } + else + { + del_feature_name = + !is_inside ? "nat44-ei-in2out" : "nat44-ei-out2in"; + feature_name = "nat44-ei-classify"; + } - if (!addr_only) - break; + int rv = + ip4_sv_reass_enable_disable_with_refcnt (sw_if_index, 1); + if (rv) + return rv; + vnet_feature_enable_disable ("ip4-unicast", del_feature_name, + sw_if_index, 0, 0, 0); + vnet_feature_enable_disable ("ip4-unicast", feature_name, + sw_if_index, 1, 0, 0); + if (!is_inside) + { + vnet_feature_enable_disable ( + "ip4-local", "nat44-ei-hairpinning", sw_if_index, 0, 0, 0); + } + goto set_flags; } + + goto fib; } } -} -u32 -nat44_ei_get_in2out_worker_index (ip4_header_t *ip0, u32 rx_fib_index0, - u8 is_output) -{ - snat_main_t *sm = &snat_main; - u32 next_worker_index = 0; - u32 hash; + if (is_del) + { + nat44_ei_log_err ("error interface couldn't be found"); + return VNET_API_ERROR_NO_SUCH_ENTRY; + } - next_worker_index = sm->first_worker_index; - hash = ip0->src_address.as_u32 + (ip0->src_address.as_u32 >> 8) + - (ip0->src_address.as_u32 >> 16) + (ip0->src_address.as_u32 >> 24); + pool_get (nm->interfaces, i); + i->sw_if_index = sw_if_index; + i->flags = 0; + nat_validate_interface_counters (nm, sw_if_index); - if (PREDICT_TRUE (is_pow2 (_vec_len (sm->workers)))) - next_worker_index += sm->workers[hash & (_vec_len (sm->workers) - 1)]; - else - next_worker_index += sm->workers[hash % _vec_len (sm->workers)]; + vnet_feature_enable_disable ("ip4-unicast", feature_name, sw_if_index, 1, 0, + 0); - return next_worker_index; -} + int rv = ip4_sv_reass_enable_disable_with_refcnt (sw_if_index, 1); + if (rv) + return rv; -u32 -nat44_ei_get_out2in_worker_index (vlib_buffer_t *b, ip4_header_t *ip0, - u32 rx_fib_index0, u8 is_output) -{ - snat_main_t *sm = &snat_main; - udp_header_t *udp; - u16 port; - clib_bihash_kv_8_8_t kv, value; - snat_static_mapping_t *m; - u32 proto; - u32 next_worker_index = 0; + if (is_inside && !nm->out2in_dpo) + { + vnet_feature_enable_disable ("ip4-local", "nat44-ei-hairpinning", + sw_if_index, 1, 0, 0); + } - /* first try static mappings without port */ - if (PREDICT_FALSE (pool_elts (sm->static_mappings))) +set_flags: + if (is_inside) { - init_nat_k (&kv, ip0->dst_address, 0, rx_fib_index0, 0); - if (!clib_bihash_search_8_8 (&sm->static_mapping_by_external, &kv, - &value)) - { - m = pool_elt_at_index (sm->static_mappings, value.value); - return m->workers[0]; - } + i->flags |= NAT44_EI_INTERFACE_FLAG_IS_INSIDE; + return 0; } + else + i->flags |= NAT44_EI_INTERFACE_FLAG_IS_OUTSIDE; - proto = ip_proto_to_nat_proto (ip0->protocol); - udp = ip4_next_header (ip0); - port = udp->dst_port; + /* Add/delete external addresses to FIB */ +fib: + vec_foreach (ap, nm->addresses) + nat44_ei_add_del_addr_to_fib (&ap->addr, 32, sw_if_index, !is_del); - /* unknown protocol */ - if (PREDICT_FALSE (proto == NAT_PROTOCOL_OTHER)) + pool_foreach (m, nm->static_mappings) + { + if (!(nat44_ei_is_addr_only_static_mapping (m)) || + (m->local_addr.as_u32 == m->external_addr.as_u32)) + continue; + + nat44_ei_add_del_addr_to_fib (&m->external_addr, 32, sw_if_index, + !is_del); + } + + return 0; +} + +int +nat44_ei_interface_add_del_output_feature (u32 sw_if_index, u8 is_inside, + int is_del) +{ + nat44_ei_main_t *nm = &nat44_ei_main; + nat44_ei_interface_t *i; + nat44_ei_address_t *ap; + nat44_ei_static_mapping_t *m; + nat44_ei_outside_fib_t *outside_fib; + u32 fib_index = + fib_table_get_index_for_sw_if_index (FIB_PROTOCOL_IP4, sw_if_index); + + fail_if_disabled (); + + if (nm->static_mapping_only && !(nm->static_mapping_connection_tracking)) + { + nat44_ei_log_err ("error unsupported"); + return VNET_API_ERROR_UNSUPPORTED; + } + + pool_foreach (i, nm->interfaces) + { + if (i->sw_if_index == sw_if_index) + { + nat44_ei_log_err ("error interface already configured"); + return VNET_API_ERROR_VALUE_EXIST; + } + } + + if (!is_inside) + { + vec_foreach (outside_fib, nm->outside_fibs) + { + if (outside_fib->fib_index == fib_index) + { + if (is_del) + { + outside_fib->refcount--; + if (!outside_fib->refcount) + vec_del1 (nm->outside_fibs, + outside_fib - nm->outside_fibs); + } + else + outside_fib->refcount++; + goto feature_set; + } + } + if (!is_del) + { + vec_add2 (nm->outside_fibs, outside_fib, 1); + outside_fib->refcount = 1; + outside_fib->fib_index = fib_index; + } + } + +feature_set: + if (is_inside) + { + int rv = ip4_sv_reass_enable_disable_with_refcnt (sw_if_index, !is_del); + if (rv) + return rv; + rv = + ip4_sv_reass_output_enable_disable_with_refcnt (sw_if_index, !is_del); + if (rv) + return rv; + vnet_feature_enable_disable ("ip4-unicast", "nat44-ei-hairpin-dst", + sw_if_index, !is_del, 0, 0); + vnet_feature_enable_disable ("ip4-output", "nat44-ei-hairpin-src", + sw_if_index, !is_del, 0, 0); + goto fq; + } + + if (nm->num_workers > 1) + { + int rv = ip4_sv_reass_enable_disable_with_refcnt (sw_if_index, !is_del); + if (rv) + return rv; + rv = + ip4_sv_reass_output_enable_disable_with_refcnt (sw_if_index, !is_del); + if (rv) + return rv; + vnet_feature_enable_disable ("ip4-unicast", + "nat44-ei-out2in-worker-handoff", + sw_if_index, !is_del, 0, 0); + vnet_feature_enable_disable ("ip4-output", + "nat44-ei-in2out-output-worker-handoff", + sw_if_index, !is_del, 0, 0); + } + else + { + int rv = ip4_sv_reass_enable_disable_with_refcnt (sw_if_index, !is_del); + if (rv) + return rv; + rv = + ip4_sv_reass_output_enable_disable_with_refcnt (sw_if_index, !is_del); + if (rv) + return rv; + vnet_feature_enable_disable ("ip4-unicast", "nat44-ei-out2in", + sw_if_index, !is_del, 0, 0); + vnet_feature_enable_disable ("ip4-output", "nat44-ei-in2out-output", + sw_if_index, !is_del, 0, 0); + } + +fq: + if (nm->fq_in2out_output_index == ~0 && nm->num_workers > 1) + nm->fq_in2out_output_index = + vlib_frame_queue_main_init (nm->in2out_output_node_index, 0); + + if (nm->fq_out2in_index == ~0 && nm->num_workers > 1) + nm->fq_out2in_index = + vlib_frame_queue_main_init (nm->out2in_node_index, 0); + + pool_foreach (i, nm->output_feature_interfaces) + { + if (i->sw_if_index == sw_if_index) + { + if (is_del) + pool_put (nm->output_feature_interfaces, i); + else + return VNET_API_ERROR_VALUE_EXIST; + + goto fib; + } + } + + if (is_del) + { + nat44_ei_log_err ("error interface couldn't be found"); + return VNET_API_ERROR_NO_SUCH_ENTRY; + } + + pool_get (nm->output_feature_interfaces, i); + i->sw_if_index = sw_if_index; + i->flags = 0; + nat_validate_interface_counters (nm, sw_if_index); + if (is_inside) + i->flags |= NAT44_EI_INTERFACE_FLAG_IS_INSIDE; + else + i->flags |= NAT44_EI_INTERFACE_FLAG_IS_OUTSIDE; + + /* Add/delete external addresses to FIB */ +fib: + if (is_inside) + return 0; + + vec_foreach (ap, nm->addresses) + nat44_ei_add_del_addr_to_fib (&ap->addr, 32, sw_if_index, !is_del); + + pool_foreach (m, nm->static_mappings) + { + if (!((nat44_ei_is_addr_only_static_mapping (m))) || + (m->local_addr.as_u32 == m->external_addr.as_u32)) + continue; + + nat44_ei_add_del_addr_to_fib (&m->external_addr, 32, sw_if_index, + !is_del); + } + + return 0; +} + +int +nat44_ei_plugin_disable () +{ + nat44_ei_main_t *nm = &nat44_ei_main; + nat44_ei_interface_t *i, *vec; + int error = 0; + + // first unregister all nodes from interfaces + vec = vec_dup (nm->interfaces); + vec_foreach (i, vec) + { + if (nat44_ei_interface_is_inside (i)) + error = nat44_ei_interface_add_del (i->sw_if_index, 1, 1); + if (nat44_ei_interface_is_outside (i)) + error = nat44_ei_interface_add_del (i->sw_if_index, 0, 1); + + if (error) + { + nat44_ei_log_err ("error occurred while removing interface %u", + i->sw_if_index); + } + } + vec_free (vec); + nm->interfaces = 0; + + vec = vec_dup (nm->output_feature_interfaces); + vec_foreach (i, vec) + { + if (nat44_ei_interface_is_inside (i)) + error = + nat44_ei_interface_add_del_output_feature (i->sw_if_index, 1, 1); + if (nat44_ei_interface_is_outside (i)) + error = + nat44_ei_interface_add_del_output_feature (i->sw_if_index, 0, 1); + + if (error) + { + nat44_ei_log_err ("error occurred while removing interface %u", + i->sw_if_index); + } + } + vec_free (vec); + nm->output_feature_interfaces = 0; + + nat_ha_disable (); + nat44_ei_db_free (); + + nat44_ei_addresses_free (&nm->addresses); + + vec_free (nm->to_resolve); + vec_free (nm->auto_add_sw_if_indices); + + nm->to_resolve = 0; + nm->auto_add_sw_if_indices = 0; + + nm->forwarding_enabled = 0; + + nm->enabled = 0; + clib_memset (&nm->rconfig, 0, sizeof (nm->rconfig)); + + return error; +} + +int +nat44_ei_set_outside_address_and_port (nat44_ei_address_t *addresses, + u32 thread_index, ip4_address_t addr, + u16 port, nat_protocol_t protocol) +{ + nat44_ei_main_t *nm = &nat44_ei_main; + nat44_ei_address_t *a = 0; + u32 address_index; + u16 port_host_byte_order = clib_net_to_host_u16 (port); + + for (address_index = 0; address_index < vec_len (addresses); address_index++) + { + if (addresses[address_index].addr.as_u32 != addr.as_u32) + continue; + + a = addresses + address_index; + switch (protocol) + { +#define _(N, j, n, s) \ + case NAT_PROTOCOL_##N: \ + if (a->busy_##n##_port_refcounts[port_host_byte_order]) \ + return VNET_API_ERROR_INSTANCE_IN_USE; \ + ++a->busy_##n##_port_refcounts[port_host_byte_order]; \ + a->busy_##n##_ports_per_thread[thread_index]++; \ + a->busy_##n##_ports++; \ + return 0; + foreach_nat_protocol +#undef _ + default : nat_elog_info (nm, "unknown protocol"); + return 1; + } + } + + return VNET_API_ERROR_NO_SUCH_ENTRY; +} + +void +nat44_ei_add_del_address_dpo (ip4_address_t addr, u8 is_add) +{ + nat44_ei_main_t *nm = &nat44_ei_main; + dpo_id_t dpo_v4 = DPO_INVALID; + fib_prefix_t pfx = { + .fp_proto = FIB_PROTOCOL_IP4, + .fp_len = 32, + .fp_addr.ip4.as_u32 = addr.as_u32, + }; + + if (is_add) + { + nat_dpo_create (DPO_PROTO_IP4, 0, &dpo_v4); + fib_table_entry_special_dpo_add (0, &pfx, nm->fib_src_hi, + FIB_ENTRY_FLAG_EXCLUSIVE, &dpo_v4); + dpo_reset (&dpo_v4); + } + else + { + fib_table_entry_special_remove (0, &pfx, nm->fib_src_hi); + } +} + +void +nat44_ei_free_outside_address_and_port (nat44_ei_address_t *addresses, + u32 thread_index, ip4_address_t *addr, + u16 port, nat_protocol_t protocol) +{ + nat44_ei_main_t *nm = &nat44_ei_main; + nat44_ei_address_t *a; + u32 address_index; + u16 port_host_byte_order = clib_net_to_host_u16 (port); + + for (address_index = 0; address_index < vec_len (addresses); address_index++) + { + if (addresses[address_index].addr.as_u32 == addr->as_u32) + break; + } + + ASSERT (address_index < vec_len (addresses)); + + a = addresses + address_index; + + switch (protocol) + { +#define _(N, i, n, s) \ + case NAT_PROTOCOL_##N: \ + ASSERT (a->busy_##n##_port_refcounts[port_host_byte_order] >= 1); \ + --a->busy_##n##_port_refcounts[port_host_byte_order]; \ + a->busy_##n##_ports--; \ + a->busy_##n##_ports_per_thread[thread_index]--; \ + break; + foreach_nat_protocol +#undef _ + default : nat_elog_info (nm, "unknown protocol"); + return; + } +} + +void +nat44_ei_free_session_data_v2 (nat44_ei_main_t *nm, nat44_ei_session_t *s, + u32 thread_index, u8 is_ha) +{ + clib_bihash_kv_8_8_t kv; + + /* session lookup tables */ + init_nat_i2o_k (&kv, s); + if (clib_bihash_add_del_8_8 (&nm->in2out, &kv, 0)) + nat_elog_warn (nm, "in2out key del failed"); + init_nat_o2i_k (&kv, s); + if (clib_bihash_add_del_8_8 (&nm->out2in, &kv, 0)) + nat_elog_warn (nm, "out2in key del failed"); + + if (!is_ha) + nat_syslog_nat44_apmdel (s->user_index, s->in2out.fib_index, + &s->in2out.addr, s->in2out.port, &s->out2in.addr, + s->out2in.port, s->nat_proto); + + if (nat44_ei_is_unk_proto_session (s)) + return; + + if (!is_ha) + { + /* log NAT event */ + nat_ipfix_logging_nat44_ses_delete ( + thread_index, s->in2out.addr.as_u32, s->out2in.addr.as_u32, + s->nat_proto, s->in2out.port, s->out2in.port, s->in2out.fib_index); + + nat_ha_sdel (&s->out2in.addr, s->out2in.port, &s->ext_host_addr, + s->ext_host_port, s->nat_proto, s->out2in.fib_index, + thread_index); + } + + if (nat44_ei_is_session_static (s)) + return; + + nat44_ei_free_outside_address_and_port (nm->addresses, thread_index, + &s->out2in.addr, s->out2in.port, + s->nat_proto); +} + +nat44_ei_user_t * +nat44_ei_user_get_or_create (nat44_ei_main_t *nm, ip4_address_t *addr, + u32 fib_index, u32 thread_index) +{ + nat44_ei_user_t *u = 0; + nat44_ei_user_key_t user_key; + clib_bihash_kv_8_8_t kv, value; + nat44_ei_main_per_thread_data_t *tnm = &nm->per_thread_data[thread_index]; + dlist_elt_t *per_user_list_head_elt; + + user_key.addr.as_u32 = addr->as_u32; + user_key.fib_index = fib_index; + kv.key = user_key.as_u64; + + /* Ever heard of the "user" = src ip4 address before? */ + if (clib_bihash_search_8_8 (&tnm->user_hash, &kv, &value)) + { + if (pool_elts (tnm->users) >= nm->max_users_per_thread) + { + vlib_increment_simple_counter (&nm->user_limit_reached, thread_index, + 0, 1); + nat_elog_warn (nm, "maximum user limit reached"); + return NULL; + } + /* no, make a new one */ + pool_get (tnm->users, u); + clib_memset (u, 0, sizeof (*u)); + + u->addr.as_u32 = addr->as_u32; + u->fib_index = fib_index; + + pool_get (tnm->list_pool, per_user_list_head_elt); + + u->sessions_per_user_list_head_index = + per_user_list_head_elt - tnm->list_pool; + + clib_dlist_init (tnm->list_pool, u->sessions_per_user_list_head_index); + + kv.value = u - tnm->users; + + /* add user */ + if (clib_bihash_add_del_8_8 (&tnm->user_hash, &kv, 1)) + { + nat_elog_warn (nm, "user_hash key add failed"); + nat44_ei_delete_user_with_no_session (nm, u, thread_index); + return NULL; + } + + vlib_set_simple_counter (&nm->total_users, thread_index, 0, + pool_elts (tnm->users)); + } + else + { + u = pool_elt_at_index (tnm->users, value.value); + } + + return u; +} + +nat44_ei_session_t * +nat44_ei_session_alloc_or_recycle (nat44_ei_main_t *nm, nat44_ei_user_t *u, + u32 thread_index, f64 now) +{ + nat44_ei_session_t *s; + nat44_ei_main_per_thread_data_t *tnm = &nm->per_thread_data[thread_index]; + u32 oldest_per_user_translation_list_index, session_index; + dlist_elt_t *oldest_per_user_translation_list_elt; + dlist_elt_t *per_user_translation_list_elt; + + /* Over quota? Recycle the least recently used translation */ + if ((u->nsessions + u->nstaticsessions) >= nm->max_translations_per_user) + { + oldest_per_user_translation_list_index = clib_dlist_remove_head ( + tnm->list_pool, u->sessions_per_user_list_head_index); + + ASSERT (oldest_per_user_translation_list_index != ~0); + + /* Add it back to the end of the LRU list */ + clib_dlist_addtail (tnm->list_pool, u->sessions_per_user_list_head_index, + oldest_per_user_translation_list_index); + /* Get the list element */ + oldest_per_user_translation_list_elt = pool_elt_at_index ( + tnm->list_pool, oldest_per_user_translation_list_index); + + /* Get the session index from the list element */ + session_index = oldest_per_user_translation_list_elt->value; + + /* Get the session */ + s = pool_elt_at_index (tnm->sessions, session_index); + + nat44_ei_free_session_data_v2 (nm, s, thread_index, 0); + if (nat44_ei_is_session_static (s)) + u->nstaticsessions--; + else + u->nsessions--; + s->flags = 0; + s->total_bytes = 0; + s->total_pkts = 0; + s->state = 0; + s->ext_host_addr.as_u32 = 0; + s->ext_host_port = 0; + s->ext_host_nat_addr.as_u32 = 0; + s->ext_host_nat_port = 0; + } + else + { + pool_get (tnm->sessions, s); + clib_memset (s, 0, sizeof (*s)); + + /* Create list elts */ + pool_get (tnm->list_pool, per_user_translation_list_elt); + clib_dlist_init (tnm->list_pool, + per_user_translation_list_elt - tnm->list_pool); + + per_user_translation_list_elt->value = s - tnm->sessions; + s->per_user_index = per_user_translation_list_elt - tnm->list_pool; + s->per_user_list_head_index = u->sessions_per_user_list_head_index; + + clib_dlist_addtail (tnm->list_pool, s->per_user_list_head_index, + per_user_translation_list_elt - tnm->list_pool); + + s->user_index = u - tnm->users; + vlib_set_simple_counter (&nm->total_sessions, thread_index, 0, + pool_elts (tnm->sessions)); + } + + s->ha_last_refreshed = now; + + return s; +} + +void +nat44_ei_free_session_data (nat44_ei_main_t *nm, nat44_ei_session_t *s, + u32 thread_index, u8 is_ha) +{ + clib_bihash_kv_8_8_t kv; + + init_nat_i2o_k (&kv, s); + if (clib_bihash_add_del_8_8 (&nm->in2out, &kv, 0)) + nat_elog_warn (nm, "in2out key del failed"); + + init_nat_o2i_k (&kv, s); + if (clib_bihash_add_del_8_8 (&nm->out2in, &kv, 0)) + nat_elog_warn (nm, "out2in key del failed"); + + if (!is_ha) + { + nat_syslog_nat44_apmdel (s->user_index, s->in2out.fib_index, + &s->in2out.addr, s->in2out.port, + &s->out2in.addr, s->out2in.port, s->nat_proto); + + nat_ipfix_logging_nat44_ses_delete ( + thread_index, s->in2out.addr.as_u32, s->out2in.addr.as_u32, + s->nat_proto, s->in2out.port, s->out2in.port, s->in2out.fib_index); + + nat_ha_sdel (&s->out2in.addr, s->out2in.port, &s->ext_host_addr, + s->ext_host_port, s->nat_proto, s->out2in.fib_index, + thread_index); + } + + if (nat44_ei_is_session_static (s)) + return; + + nat44_ei_free_outside_address_and_port (nm->addresses, thread_index, + &s->out2in.addr, s->out2in.port, + s->nat_proto); +} + +static_always_inline void +nat44_ei_user_del_sessions (nat44_ei_user_t *u, u32 thread_index) +{ + dlist_elt_t *elt; + nat44_ei_session_t *s; + + nat44_ei_main_t *nm = &nat44_ei_main; + nat44_ei_main_per_thread_data_t *tnm = &nm->per_thread_data[thread_index]; + + // get head + elt = + pool_elt_at_index (tnm->list_pool, u->sessions_per_user_list_head_index); + // get first element + elt = pool_elt_at_index (tnm->list_pool, elt->next); + + while (elt->value != ~0) + { + s = pool_elt_at_index (tnm->sessions, elt->value); + elt = pool_elt_at_index (tnm->list_pool, elt->next); + + nat44_ei_free_session_data (nm, s, thread_index, 0); + nat44_ei_delete_session (nm, s, thread_index); + } +} + +int +nat44_ei_user_del (ip4_address_t *addr, u32 fib_index) +{ + int rv = 1; + + nat44_ei_main_t *nm = &nat44_ei_main; + nat44_ei_main_per_thread_data_t *tnm; + + nat44_ei_user_key_t user_key; + clib_bihash_kv_8_8_t kv, value; + + user_key.addr.as_u32 = addr->as_u32; + user_key.fib_index = fib_index; + kv.key = user_key.as_u64; + + if (nm->num_workers > 1) + { + vec_foreach (tnm, nm->per_thread_data) + { + if (!clib_bihash_search_8_8 (&tnm->user_hash, &kv, &value)) + { + nat44_ei_user_del_sessions ( + pool_elt_at_index (tnm->users, value.value), + tnm->thread_index); + rv = 0; + break; + } + } + } + else + { + tnm = vec_elt_at_index (nm->per_thread_data, nm->num_workers); + if (!clib_bihash_search_8_8 (&tnm->user_hash, &kv, &value)) + { + nat44_ei_user_del_sessions ( + pool_elt_at_index (tnm->users, value.value), tnm->thread_index); + rv = 0; + } + } + return rv; +} + +void +nat44_ei_static_mapping_del_sessions (nat44_ei_main_t *nm, + nat44_ei_main_per_thread_data_t *tnm, + nat44_ei_user_key_t u_key, int addr_only, + ip4_address_t e_addr, u16 e_port) +{ + clib_bihash_kv_8_8_t kv, value; + kv.key = u_key.as_u64; + u64 user_index; + dlist_elt_t *head, *elt; + nat44_ei_user_t *u; + nat44_ei_session_t *s; + u32 elt_index, head_index, ses_index; + + if (!clib_bihash_search_8_8 (&tnm->user_hash, &kv, &value)) + { + user_index = value.value; + u = pool_elt_at_index (tnm->users, user_index); + if (u->nstaticsessions) + { + head_index = u->sessions_per_user_list_head_index; + head = pool_elt_at_index (tnm->list_pool, head_index); + elt_index = head->next; + elt = pool_elt_at_index (tnm->list_pool, elt_index); + ses_index = elt->value; + while (ses_index != ~0) + { + s = pool_elt_at_index (tnm->sessions, ses_index); + elt = pool_elt_at_index (tnm->list_pool, elt->next); + ses_index = elt->value; + + if (!addr_only) + { + if ((s->out2in.addr.as_u32 != e_addr.as_u32) || + (s->out2in.port != e_port)) + continue; + } + + if (!nat44_ei_is_session_static (s)) + continue; + + nat44_ei_free_session_data_v2 (nm, s, tnm - nm->per_thread_data, + 0); + nat44_ei_delete_session (nm, s, tnm - nm->per_thread_data); + + if (!addr_only) + break; + } + } + } +} + +u32 +nat44_ei_get_in2out_worker_index (ip4_header_t *ip0, u32 rx_fib_index0, + u8 is_output) +{ + nat44_ei_main_t *nm = &nat44_ei_main; + u32 next_worker_index = 0; + u32 hash; + + next_worker_index = nm->first_worker_index; + hash = ip0->src_address.as_u32 + (ip0->src_address.as_u32 >> 8) + + (ip0->src_address.as_u32 >> 16) + (ip0->src_address.as_u32 >> 24); + + if (PREDICT_TRUE (is_pow2 (_vec_len (nm->workers)))) + next_worker_index += nm->workers[hash & (_vec_len (nm->workers) - 1)]; + else + next_worker_index += nm->workers[hash % _vec_len (nm->workers)]; + + return next_worker_index; +} + +u32 +nat44_ei_get_out2in_worker_index (vlib_buffer_t *b, ip4_header_t *ip0, + u32 rx_fib_index0, u8 is_output) +{ + nat44_ei_main_t *nm = &nat44_ei_main; + udp_header_t *udp; + u16 port; + clib_bihash_kv_8_8_t kv, value; + nat44_ei_static_mapping_t *m; + u32 proto; + u32 next_worker_index = 0; + + /* first try static mappings without port */ + if (PREDICT_FALSE (pool_elts (nm->static_mappings))) + { + init_nat_k (&kv, ip0->dst_address, 0, rx_fib_index0, 0); + if (!clib_bihash_search_8_8 (&nm->static_mapping_by_external, &kv, + &value)) + { + m = pool_elt_at_index (nm->static_mappings, value.value); + return m->workers[0]; + } + } + + proto = ip_proto_to_nat_proto (ip0->protocol); + udp = ip4_next_header (ip0); + port = udp->dst_port; + + /* unknown protocol */ + if (PREDICT_FALSE (proto == NAT_PROTOCOL_OTHER)) { /* use current thread */ return vlib_get_thread_index (); @@ -438,33 +1447,34 @@ nat44_ei_get_out2in_worker_index (vlib_buffer_t *b, ip4_header_t *ip0, } /* try static mappings with port */ - if (PREDICT_FALSE (pool_elts (sm->static_mappings))) + if (PREDICT_FALSE (pool_elts (nm->static_mappings))) { init_nat_k (&kv, ip0->dst_address, port, rx_fib_index0, proto); - if (!clib_bihash_search_8_8 (&sm->static_mapping_by_external, &kv, + if (!clib_bihash_search_8_8 (&nm->static_mapping_by_external, &kv, &value)) { - m = pool_elt_at_index (sm->static_mappings, value.value); + m = pool_elt_at_index (nm->static_mappings, value.value); return m->workers[0]; } } /* worker by outside port */ - next_worker_index = sm->first_worker_index; + next_worker_index = nm->first_worker_index; next_worker_index += - sm->workers[(clib_net_to_host_u16 (port) - 1024) / sm->port_per_thread]; + nm->workers[(clib_net_to_host_u16 (port) - 1024) / nm->port_per_thread]; return next_worker_index; } static int -nat44_ei_alloc_default_cb (snat_address_t *addresses, u32 fib_index, +nat44_ei_alloc_default_cb (nat44_ei_address_t *addresses, u32 fib_index, u32 thread_index, nat_protocol_t proto, ip4_address_t *addr, u16 *port, u16 port_per_thread, u32 snat_thread_index) { - int i; - snat_address_t *a, *ga = 0; + nat44_ei_main_t *nm = &nat44_ei_main; + nat44_ei_address_t *a, *ga = 0; u32 portnum; + int i; for (i = 0; i < vec_len (addresses); i++) { @@ -480,7 +1490,9 @@ nat44_ei_alloc_default_cb (snat_address_t *addresses, u32 fib_index, while (1) \ { \ portnum = (port_per_thread * snat_thread_index) + \ - snat_random_port (0, port_per_thread - 1) + 1024; \ + nat_random_port (&nm->random_seed, 0, \ + port_per_thread - 1) + \ + 1024; \ if (a->busy_##n##_port_refcounts[portnum]) \ continue; \ --a->busy_##n##_port_refcounts[portnum]; \ @@ -499,7 +1511,7 @@ nat44_ei_alloc_default_cb (snat_address_t *addresses, u32 fib_index, break; foreach_nat_protocol #undef _ - default : nat_elog_info ("unknown protocol"); + default : nat_elog_info (nm, "unknown protocol"); return 1; } } @@ -513,8 +1525,9 @@ nat44_ei_alloc_default_cb (snat_address_t *addresses, u32 fib_index, case NAT_PROTOCOL_##N: \ while (1) \ { \ - portnum = (port_per_thread * snat_thread_index) + \ - snat_random_port (0, port_per_thread - 1) + 1024; \ + portnum = \ + (port_per_thread * snat_thread_index) + \ + nat_random_port (&nm->random_seed, 0, port_per_thread - 1) + 1024; \ if (a->busy_##n##_port_refcounts[portnum]) \ continue; \ ++a->busy_##n##_port_refcounts[portnum]; \ @@ -527,7 +1540,7 @@ nat44_ei_alloc_default_cb (snat_address_t *addresses, u32 fib_index, break; foreach_nat_protocol #undef _ - default : nat_elog_info ("unknown protocol"); + default : nat_elog_info (nm, "unknown protocol"); return 1; } } @@ -538,16 +1551,16 @@ nat44_ei_alloc_default_cb (snat_address_t *addresses, u32 fib_index, } static int -nat44_ei_alloc_range_cb (snat_address_t *addresses, u32 fib_index, +nat44_ei_alloc_range_cb (nat44_ei_address_t *addresses, u32 fib_index, u32 thread_index, nat_protocol_t proto, ip4_address_t *addr, u16 *port, u16 port_per_thread, u32 snat_thread_index) { - snat_main_t *sm = &snat_main; - snat_address_t *a = addresses; + nat44_ei_main_t *nm = &nat44_ei_main; + nat44_ei_address_t *a = addresses; u16 portnum, ports; - ports = sm->end_port - sm->start_port + 1; + ports = nm->end_port - nm->start_port + 1; if (!vec_len (addresses)) goto exhausted; @@ -560,7 +1573,8 @@ nat44_ei_alloc_range_cb (snat_address_t *addresses, u32 fib_index, { \ while (1) \ { \ - portnum = snat_random_port (sm->start_port, sm->end_port); \ + portnum = nat_random_port (&nm->random_seed, nm->start_port, \ + nm->end_port); \ if (a->busy_##n##_port_refcounts[portnum]) \ continue; \ ++a->busy_##n##_port_refcounts[portnum]; \ @@ -573,7 +1587,7 @@ nat44_ei_alloc_range_cb (snat_address_t *addresses, u32 fib_index, break; foreach_nat_protocol #undef _ - default : nat_elog_info ("unknown protocol"); + default : nat_elog_info (nm, "unknown protocol"); return 1; } @@ -584,16 +1598,16 @@ exhausted: } static int -nat44_ei_alloc_mape_cb (snat_address_t *addresses, u32 fib_index, +nat44_ei_alloc_mape_cb (nat44_ei_address_t *addresses, u32 fib_index, u32 thread_index, nat_protocol_t proto, ip4_address_t *addr, u16 *port, u16 port_per_thread, u32 snat_thread_index) { - snat_main_t *sm = &snat_main; - snat_address_t *a = addresses; + nat44_ei_main_t *nm = &nat44_ei_main; + nat44_ei_address_t *a = addresses; u16 m, ports, portnum, A, j; - m = 16 - (sm->psid_offset + sm->psid_length); - ports = (1 << (16 - sm->psid_length)) - (1 << m); + m = 16 - (nm->psid_offset + nm->psid_length); + ports = (1 << (16 - nm->psid_length)) - (1 << m); if (!vec_len (addresses)) goto exhausted; @@ -606,9 +1620,10 @@ nat44_ei_alloc_mape_cb (snat_address_t *addresses, u32 fib_index, { \ while (1) \ { \ - A = snat_random_port (1, pow2_mask (sm->psid_offset)); \ - j = snat_random_port (0, pow2_mask (m)); \ - portnum = A | (sm->psid << sm->psid_offset) | (j << (16 - m)); \ + A = nat_random_port (&nm->random_seed, 1, \ + pow2_mask (nm->psid_offset)); \ + j = nat_random_port (&nm->random_seed, 0, pow2_mask (m)); \ + portnum = A | (nm->psid << nm->psid_offset) | (j << (16 - m)); \ if (a->busy_##n##_port_refcounts[portnum]) \ continue; \ ++a->busy_##n##_port_refcounts[portnum]; \ @@ -621,7 +1636,7 @@ nat44_ei_alloc_mape_cb (snat_address_t *addresses, u32 fib_index, break; foreach_nat_protocol #undef _ - default : nat_elog_info ("unknown protocol"); + default : nat_elog_info (nm, "unknown protocol"); return 1; } @@ -634,33 +1649,33 @@ exhausted: void nat44_ei_set_alloc_default () { - snat_main_t *sm = &snat_main; + nat44_ei_main_t *nm = &nat44_ei_main; - sm->addr_and_port_alloc_alg = NAT_ADDR_AND_PORT_ALLOC_ALG_DEFAULT; - sm->alloc_addr_and_port = nat44_ei_alloc_default_cb; + nm->addr_and_port_alloc_alg = NAT44_EI_ADDR_AND_PORT_ALLOC_ALG_DEFAULT; + nm->alloc_addr_and_port = nat44_ei_alloc_default_cb; } void nat44_ei_set_alloc_range (u16 start_port, u16 end_port) { - snat_main_t *sm = &snat_main; + nat44_ei_main_t *nm = &nat44_ei_main; - sm->addr_and_port_alloc_alg = NAT_ADDR_AND_PORT_ALLOC_ALG_RANGE; - sm->alloc_addr_and_port = nat44_ei_alloc_range_cb; - sm->start_port = start_port; - sm->end_port = end_port; + nm->addr_and_port_alloc_alg = NAT44_EI_ADDR_AND_PORT_ALLOC_ALG_RANGE; + nm->alloc_addr_and_port = nat44_ei_alloc_range_cb; + nm->start_port = start_port; + nm->end_port = end_port; } void nat44_ei_set_alloc_mape (u16 psid, u16 psid_offset, u16 psid_length) { - snat_main_t *sm = &snat_main; + nat44_ei_main_t *nm = &nat44_ei_main; - sm->addr_and_port_alloc_alg = NAT_ADDR_AND_PORT_ALLOC_ALG_MAPE; - sm->alloc_addr_and_port = nat44_ei_alloc_mape_cb; - sm->psid = psid; - sm->psid_offset = psid_offset; - sm->psid_length = psid_length; + nm->addr_and_port_alloc_alg = NAT44_EI_ADDR_AND_PORT_ALLOC_ALG_MAPE; + nm->alloc_addr_and_port = nat44_ei_alloc_mape_cb; + nm->psid = psid; + nm->psid_offset = psid_offset; + nm->psid_length = psid_length; } static void @@ -670,10 +1685,10 @@ nat44_ei_add_static_mapping_when_resolved (ip4_address_t l_addr, u16 l_port, int addr_only, int identity_nat, u8 *tag) { - snat_main_t *sm = &snat_main; - snat_static_map_resolve_t *rp; + nat44_ei_main_t *nm = &nat44_ei_main; + nat44_ei_static_map_resolve_t *rp; - vec_add2 (sm->to_resolve, rp, 1); + vec_add2 (nm->to_resolve, rp, 1); clib_memset (rp, 0, sizeof (*rp)); rp->l_addr.as_u32 = l_addr.as_u32; @@ -687,65 +1702,131 @@ nat44_ei_add_static_mapping_when_resolved (ip4_address_t l_addr, u16 l_port, rp->tag = vec_dup (tag); } +void +nat44_ei_delete_session (nat44_ei_main_t *nm, nat44_ei_session_t *ses, + u32 thread_index) +{ + nat44_ei_main_per_thread_data_t *tnm = + vec_elt_at_index (nm->per_thread_data, thread_index); + clib_bihash_kv_8_8_t kv, value; + nat44_ei_user_t *u; + const nat44_ei_user_key_t u_key = { .addr = ses->in2out.addr, + .fib_index = ses->in2out.fib_index }; + const u8 u_static = nat44_ei_is_session_static (ses); + + clib_dlist_remove (tnm->list_pool, ses->per_user_index); + pool_put_index (tnm->list_pool, ses->per_user_index); + + pool_put (tnm->sessions, ses); + vlib_set_simple_counter (&nm->total_sessions, thread_index, 0, + pool_elts (tnm->sessions)); + + kv.key = u_key.as_u64; + if (!clib_bihash_search_8_8 (&tnm->user_hash, &kv, &value)) + { + u = pool_elt_at_index (tnm->users, value.value); + if (u_static) + u->nstaticsessions--; + else + u->nsessions--; + + nat44_ei_delete_user_with_no_session (nm, u, thread_index); + } +} + int -nat44_ei_del_session (snat_main_t *sm, ip4_address_t *addr, u16 port, +nat44_ei_del_session (nat44_ei_main_t *nm, ip4_address_t *addr, u16 port, nat_protocol_t proto, u32 vrf_id, int is_in) { - snat_main_per_thread_data_t *tsm; + nat44_ei_main_per_thread_data_t *tnm; clib_bihash_kv_8_8_t kv, value; ip4_header_t ip; u32 fib_index = fib_table_find (FIB_PROTOCOL_IP4, vrf_id); - snat_session_t *s; + nat44_ei_session_t *s; clib_bihash_8_8_t *t; - if (sm->endpoint_dependent) - return VNET_API_ERROR_UNSUPPORTED; - ip.dst_address.as_u32 = ip.src_address.as_u32 = addr->as_u32; - if (sm->num_workers > 1) - tsm = vec_elt_at_index (sm->per_thread_data, - sm->worker_in2out_cb (&ip, fib_index, 0)); + if (nm->num_workers > 1) + tnm = vec_elt_at_index (nm->per_thread_data, + nm->worker_in2out_cb (&ip, fib_index, 0)); else - tsm = vec_elt_at_index (sm->per_thread_data, sm->num_workers); + tnm = vec_elt_at_index (nm->per_thread_data, nm->num_workers); init_nat_k (&kv, *addr, port, fib_index, proto); - t = is_in ? &sm->in2out : &sm->out2in; + t = is_in ? &nm->in2out : &nm->out2in; if (!clib_bihash_search_8_8 (t, &kv, &value)) { - if (pool_is_free_index (tsm->sessions, value.value)) + if (pool_is_free_index (tnm->sessions, value.value)) return VNET_API_ERROR_UNSPECIFIED; - s = pool_elt_at_index (tsm->sessions, value.value); - nat_free_session_data (sm, s, tsm - sm->per_thread_data, 0); - nat44_delete_session (sm, s, tsm - sm->per_thread_data); + s = pool_elt_at_index (tnm->sessions, value.value); + nat44_ei_free_session_data_v2 (nm, s, tnm - nm->per_thread_data, 0); + nat44_ei_delete_session (nm, s, tnm - nm->per_thread_data); return 0; } return VNET_API_ERROR_NO_SUCH_ENTRY; } +u32 +nat44_ei_get_thread_idx_by_port (u16 e_port) +{ + nat44_ei_main_t *nm = &nat44_ei_main; + u32 thread_idx = nm->num_workers; + if (nm->num_workers > 1) + { + thread_idx = nm->first_worker_index + + nm->workers[(e_port - 1024) / nm->port_per_thread]; + } + return thread_idx; +} + +void +nat44_ei_add_del_addr_to_fib (ip4_address_t *addr, u8 p_len, u32 sw_if_index, + int is_add) +{ + nat44_ei_main_t *nm = &nat44_ei_main; + fib_prefix_t prefix = { + .fp_len = p_len, + .fp_proto = FIB_PROTOCOL_IP4, + .fp_addr = { + .ip4.as_u32 = addr->as_u32, + }, + }; + u32 fib_index = ip4_fib_table_get_index_for_sw_if_index (sw_if_index); + + if (is_add) + fib_table_entry_update_one_path ( + fib_index, &prefix, nm->fib_src_low, + (FIB_ENTRY_FLAG_CONNECTED | FIB_ENTRY_FLAG_LOCAL | + FIB_ENTRY_FLAG_EXCLUSIVE), + DPO_PROTO_IP4, NULL, sw_if_index, ~0, 1, NULL, FIB_ROUTE_PATH_FLAG_NONE); + else + fib_table_entry_delete (fib_index, &prefix, nm->fib_src_low); +} + int nat44_ei_add_del_static_mapping (ip4_address_t l_addr, ip4_address_t e_addr, u16 l_port, u16 e_port, nat_protocol_t proto, u32 sw_if_index, u32 vrf_id, u8 addr_only, u8 identity_nat, u8 *tag, u8 is_add) { - snat_main_t *sm = &snat_main; - snat_static_mapping_t *m = 0; + nat44_ei_main_t *nm = &nat44_ei_main; + nat44_ei_static_mapping_t *m = 0; clib_bihash_kv_8_8_t kv, value; - snat_address_t *a = 0; + nat44_ei_address_t *a = 0; u32 fib_index = ~0; - snat_interface_t *interface; - snat_main_per_thread_data_t *tsm; - snat_user_key_t u_key; - snat_user_t *u; + nat44_ei_interface_t *interface; + nat44_ei_main_per_thread_data_t *tnm; + nat44_ei_user_key_t u_key; + nat44_ei_user_t *u; dlist_elt_t *head, *elt; u32 elt_index, head_index; u32 ses_index; u64 user_index; - snat_session_t *s; - snat_static_map_resolve_t *rp, *rp_match = 0; - nat44_lb_addr_port_t *local; + nat44_ei_session_t *s; + nat44_ei_static_map_resolve_t *rp, *rp_match = 0; + nat44_ei_lb_addr_port_t *local; u32 find = ~0; int i; @@ -753,9 +1834,9 @@ nat44_ei_add_del_static_mapping (ip4_address_t l_addr, ip4_address_t e_addr, { ip4_address_t *first_int_addr; - for (i = 0; i < vec_len (sm->to_resolve); i++) + for (i = 0; i < vec_len (nm->to_resolve); i++) { - rp = sm->to_resolve + i; + rp = nm->to_resolve + i; if (rp->sw_if_index != sw_if_index || rp->l_addr.as_u32 != l_addr.as_u32 || rp->vrf_id != vrf_id || rp->addr_only != addr_only) @@ -774,7 +1855,7 @@ nat44_ei_add_del_static_mapping (ip4_address_t l_addr, ip4_address_t e_addr, /* Might be already set... */ first_int_addr = ip4_interface_first_address ( - sm->ip4_main, sw_if_index, 0 /* just want the address */); + nm->ip4_main, sw_if_index, 0 /* just want the address */); if (is_add) { @@ -799,7 +1880,7 @@ nat44_ei_add_del_static_mapping (ip4_address_t l_addr, ip4_address_t e_addr, if (!rp_match) return VNET_API_ERROR_NO_SUCH_ENTRY; - vec_del1 (sm->to_resolve, i); + vec_del1 (nm->to_resolve, i); if (!first_int_addr) return 0; @@ -812,15 +1893,15 @@ nat44_ei_add_del_static_mapping (ip4_address_t l_addr, ip4_address_t e_addr, } init_nat_k (&kv, e_addr, addr_only ? 0 : e_port, 0, addr_only ? 0 : proto); - if (!clib_bihash_search_8_8 (&sm->static_mapping_by_external, &kv, &value)) - m = pool_elt_at_index (sm->static_mappings, value.value); + if (!clib_bihash_search_8_8 (&nm->static_mapping_by_external, &kv, &value)) + m = pool_elt_at_index (nm->static_mappings, value.value); if (is_add) { if (m) { // identity mapping for second vrf - if (is_identity_static_mapping (m)) + if (nat44_ei_is_identity_static_mapping (m)) { pool_foreach (local, m->locals) { @@ -830,10 +1911,10 @@ nat44_ei_add_del_static_mapping (ip4_address_t l_addr, ip4_address_t e_addr, 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, sm->fib_src_low); + FIB_PROTOCOL_IP4, vrf_id, nm->fib_src_low); init_nat_kv (&kv, m->local_addr, m->local_port, local->fib_index, - m->proto, 0, m - sm->static_mappings); - clib_bihash_add_del_8_8 (&sm->static_mapping_by_local, &kv, 1); + m->proto, 0, m - nm->static_mappings); + clib_bihash_add_del_8_8 (&nm->static_mapping_by_local, &kv, 1); return 0; } return VNET_API_ERROR_VALUE_EXIST; @@ -843,34 +1924,34 @@ nat44_ei_add_del_static_mapping (ip4_address_t l_addr, ip4_address_t e_addr, if (vrf_id != ~0) { fib_index = fib_table_find_or_create_and_lock ( - FIB_PROTOCOL_IP4, vrf_id, sm->fib_src_low); + FIB_PROTOCOL_IP4, vrf_id, nm->fib_src_low); } /* If not specified use inside VRF id from NAT44 plugin config */ else { - fib_index = sm->inside_fib_index; - vrf_id = sm->inside_vrf_id; - fib_table_lock (fib_index, FIB_PROTOCOL_IP4, sm->fib_src_low); + fib_index = nm->inside_fib_index; + vrf_id = nm->inside_vrf_id; + fib_table_lock (fib_index, FIB_PROTOCOL_IP4, nm->fib_src_low); } if (!identity_nat) { init_nat_k (&kv, l_addr, addr_only ? 0 : l_port, fib_index, addr_only ? 0 : proto); - if (!clib_bihash_search_8_8 (&sm->static_mapping_by_local, &kv, + if (!clib_bihash_search_8_8 (&nm->static_mapping_by_local, &kv, &value)) return VNET_API_ERROR_VALUE_EXIST; } /* Find external address in allocated addresses and reserve port for address and port pair mapping when dynamic translations enabled */ - if (!(addr_only || sm->static_mapping_only)) + if (!(addr_only || nm->static_mapping_only)) { - for (i = 0; i < vec_len (sm->addresses); i++) + for (i = 0; i < vec_len (nm->addresses); i++) { - if (sm->addresses[i].addr.as_u32 == e_addr.as_u32) + if (nm->addresses[i].addr.as_u32 == e_addr.as_u32) { - a = sm->addresses + i; + a = nm->addresses + i; /* External port must be unused */ switch (proto) { @@ -882,12 +1963,13 @@ nat44_ei_add_del_static_mapping (ip4_address_t l_addr, ip4_address_t e_addr, if (e_port > 1024) \ { \ a->busy_##n##_ports++; \ - a->busy_##n##_ports_per_thread[get_thread_idx_by_port (e_port)]++; \ + a->busy_##n##_ports_per_thread[nat44_ei_get_thread_idx_by_port ( \ + e_port)]++; \ } \ break; foreach_nat_protocol #undef _ - default : nat_elog_info ("unknown protocol"); + default : nat_elog_info (nm, "unknown protocol"); return VNET_API_ERROR_INVALID_VALUE_2; } break; @@ -898,9 +1980,9 @@ nat44_ei_add_del_static_mapping (ip4_address_t l_addr, ip4_address_t e_addr, { if (sw_if_index != ~0) { - for (i = 0; i < vec_len (sm->to_resolve); i++) + for (i = 0; i < vec_len (nm->to_resolve); i++) { - rp = sm->to_resolve + i; + rp = nm->to_resolve + i; if (rp->addr_only) continue; if (rp->sw_if_index != sw_if_index && @@ -909,7 +1991,7 @@ nat44_ei_add_del_static_mapping (ip4_address_t l_addr, ip4_address_t e_addr, rp->e_port != e_port && rp->proto != proto) continue; - vec_del1 (sm->to_resolve, i); + vec_del1 (nm->to_resolve, i); break; } } @@ -917,14 +1999,14 @@ nat44_ei_add_del_static_mapping (ip4_address_t l_addr, ip4_address_t e_addr, } } - pool_get (sm->static_mappings, m); + pool_get (nm->static_mappings, m); clib_memset (m, 0, sizeof (*m)); m->tag = vec_dup (tag); m->local_addr = l_addr; m->external_addr = e_addr; if (addr_only) - m->flags |= NAT_STATIC_MAPPING_FLAG_ADDR_ONLY; + m->flags |= NAT44_EI_STATIC_MAPPING_FLAG_ADDR_ONLY; else { m->local_port = l_port; @@ -934,7 +2016,7 @@ nat44_ei_add_del_static_mapping (ip4_address_t l_addr, ip4_address_t e_addr, if (identity_nat) { - m->flags |= NAT_STATIC_MAPPING_FLAG_IDENTITY_NAT; + m->flags |= NAT44_EI_STATIC_MAPPING_FLAG_IDENTITY_NAT; pool_get (m->locals, local); local->vrf_id = vrf_id; local->fib_index = fib_index; @@ -945,58 +2027,59 @@ nat44_ei_add_del_static_mapping (ip4_address_t l_addr, ip4_address_t e_addr, m->fib_index = fib_index; } - if (sm->num_workers > 1) + if (nm->num_workers > 1) { ip4_header_t ip = { .src_address = m->local_addr, }; - vec_add1 (m->workers, sm->worker_in2out_cb (&ip, m->fib_index, 0)); - tsm = vec_elt_at_index (sm->per_thread_data, m->workers[0]); + vec_add1 (m->workers, nm->worker_in2out_cb (&ip, m->fib_index, 0)); + tnm = vec_elt_at_index (nm->per_thread_data, m->workers[0]); } else - tsm = vec_elt_at_index (sm->per_thread_data, sm->num_workers); + tnm = vec_elt_at_index (nm->per_thread_data, nm->num_workers); init_nat_kv (&kv, m->local_addr, m->local_port, fib_index, m->proto, 0, - m - sm->static_mappings); - clib_bihash_add_del_8_8 (&sm->static_mapping_by_local, &kv, 1); + m - nm->static_mappings); + clib_bihash_add_del_8_8 (&nm->static_mapping_by_local, &kv, 1); init_nat_kv (&kv, m->external_addr, m->external_port, 0, m->proto, 0, - m - sm->static_mappings); - clib_bihash_add_del_8_8 (&sm->static_mapping_by_external, &kv, 1); + m - nm->static_mappings); + clib_bihash_add_del_8_8 (&nm->static_mapping_by_external, &kv, 1); /* Delete dynamic sessions matching local address (+ local port) */ // TODO: based on type of NAT EI/ED - if (!(sm->static_mapping_only)) + if (!(nm->static_mapping_only)) { u_key.addr = m->local_addr; u_key.fib_index = m->fib_index; kv.key = u_key.as_u64; - if (!clib_bihash_search_8_8 (&tsm->user_hash, &kv, &value)) + if (!clib_bihash_search_8_8 (&tnm->user_hash, &kv, &value)) { user_index = value.value; - u = pool_elt_at_index (tsm->users, user_index); + u = pool_elt_at_index (tnm->users, user_index); if (u->nsessions) { head_index = u->sessions_per_user_list_head_index; - head = pool_elt_at_index (tsm->list_pool, head_index); + head = pool_elt_at_index (tnm->list_pool, head_index); elt_index = head->next; - elt = pool_elt_at_index (tsm->list_pool, elt_index); + elt = pool_elt_at_index (tnm->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); + s = pool_elt_at_index (tnm->sessions, ses_index); + elt = pool_elt_at_index (tnm->list_pool, elt->next); ses_index = elt->value; - if (snat_is_session_static (s)) + if (nat44_ei_is_session_static (s)) continue; if (!addr_only && s->in2out.port != m->local_port) continue; - nat_free_session_data (sm, s, tsm - sm->per_thread_data, - 0); - nat44_delete_session (sm, s, tsm - sm->per_thread_data); + nat44_ei_free_session_data_v2 ( + nm, s, tnm - nm->per_thread_data, 0); + nat44_ei_delete_session (nm, s, + tnm - nm->per_thread_data); if (!addr_only) break; @@ -1005,315 +2088,929 @@ nat44_ei_add_del_static_mapping (ip4_address_t l_addr, ip4_address_t e_addr, } } } - else + else + { + if (!m) + { + if (sw_if_index != ~0) + return 0; + else + return VNET_API_ERROR_NO_SUCH_ENTRY; + } + + if (identity_nat) + { + if (vrf_id == ~0) + vrf_id = nm->inside_vrf_id; + + pool_foreach (local, m->locals) + { + if (local->vrf_id == vrf_id) + find = local - m->locals; + } + if (find == ~0) + return VNET_API_ERROR_NO_SUCH_ENTRY; + + local = pool_elt_at_index (m->locals, find); + fib_index = local->fib_index; + pool_put (m->locals, local); + } + else + fib_index = m->fib_index; + + /* Free external address port */ + if (!(addr_only || nm->static_mapping_only)) + { + for (i = 0; i < vec_len (nm->addresses); i++) + { + if (nm->addresses[i].addr.as_u32 == e_addr.as_u32) + { + a = nm->addresses + i; + switch (proto) + { +#define _(N, j, n, s) \ + case NAT_PROTOCOL_##N: \ + --a->busy_##n##_port_refcounts[e_port]; \ + if (e_port > 1024) \ + { \ + a->busy_##n##_ports--; \ + a->busy_##n##_ports_per_thread[nat44_ei_get_thread_idx_by_port ( \ + e_port)]--; \ + } \ + break; + foreach_nat_protocol +#undef _ + default : return VNET_API_ERROR_INVALID_VALUE_2; + } + break; + } + } + } + + if (nm->num_workers > 1) + tnm = vec_elt_at_index (nm->per_thread_data, m->workers[0]); + else + tnm = vec_elt_at_index (nm->per_thread_data, nm->num_workers); + + init_nat_k (&kv, m->local_addr, m->local_port, fib_index, m->proto); + clib_bihash_add_del_8_8 (&nm->static_mapping_by_local, &kv, 0); + + /* Delete session(s) for static mapping if exist */ + if (!(nm->static_mapping_only) || + (nm->static_mapping_only && nm->static_mapping_connection_tracking)) + { + u_key.addr = m->local_addr; + u_key.fib_index = fib_index; + kv.key = u_key.as_u64; + nat44_ei_static_mapping_del_sessions (nm, tnm, u_key, addr_only, + e_addr, e_port); + } + + fib_table_unlock (fib_index, FIB_PROTOCOL_IP4, nm->fib_src_low); + if (pool_elts (m->locals)) + return 0; + + init_nat_k (&kv, m->external_addr, m->external_port, 0, m->proto); + clib_bihash_add_del_8_8 (&nm->static_mapping_by_external, &kv, 0); + + vec_free (m->tag); + vec_free (m->workers); + /* Delete static mapping from pool */ + pool_put (nm->static_mappings, m); + } + + if (!addr_only || (l_addr.as_u32 == e_addr.as_u32)) + return 0; + + /* Add/delete external address to FIB */ + pool_foreach (interface, nm->interfaces) + { + if (nat44_ei_interface_is_inside (interface) || nm->out2in_dpo) + continue; + + nat44_ei_add_del_addr_to_fib (&e_addr, 32, interface->sw_if_index, + is_add); + break; + } + pool_foreach (interface, nm->output_feature_interfaces) + { + if (nat44_ei_interface_is_inside (interface) || nm->out2in_dpo) + continue; + + nat44_ei_add_del_addr_to_fib (&e_addr, 32, interface->sw_if_index, + is_add); + break; + } + return 0; +} + +int +nat44_ei_static_mapping_match (ip4_address_t match_addr, u16 match_port, + u32 match_fib_index, + nat_protocol_t match_protocol, + ip4_address_t *mapping_addr, u16 *mapping_port, + u32 *mapping_fib_index, u8 by_external, + u8 *is_addr_only, u8 *is_identity_nat) +{ + nat44_ei_main_t *nm = &nat44_ei_main; + clib_bihash_kv_8_8_t kv, value; + nat44_ei_static_mapping_t *m; + u16 port; + + if (by_external) + { + init_nat_k (&kv, match_addr, match_port, 0, match_protocol); + if (clib_bihash_search_8_8 (&nm->static_mapping_by_external, &kv, + &value)) + { + /* Try address only mapping */ + init_nat_k (&kv, match_addr, 0, 0, 0); + if (clib_bihash_search_8_8 (&nm->static_mapping_by_external, &kv, + &value)) + return 1; + } + m = pool_elt_at_index (nm->static_mappings, value.value); + + *mapping_fib_index = m->fib_index; + *mapping_addr = m->local_addr; + port = m->local_port; + } + else + { + init_nat_k (&kv, match_addr, match_port, match_fib_index, + match_protocol); + if (clib_bihash_search_8_8 (&nm->static_mapping_by_local, &kv, &value)) + { + /* Try address only mapping */ + init_nat_k (&kv, match_addr, 0, match_fib_index, 0); + if (clib_bihash_search_8_8 (&nm->static_mapping_by_local, &kv, + &value)) + return 1; + } + m = pool_elt_at_index (nm->static_mappings, value.value); + + *mapping_fib_index = nm->outside_fib_index; + *mapping_addr = m->external_addr; + port = m->external_port; + } + + /* Address only mapping doesn't change port */ + if (nat44_ei_is_addr_only_static_mapping (m)) + *mapping_port = match_port; + else + *mapping_port = port; + + if (PREDICT_FALSE (is_addr_only != 0)) + *is_addr_only = nat44_ei_is_addr_only_static_mapping (m); + + if (PREDICT_FALSE (is_identity_nat != 0)) + *is_identity_nat = nat44_ei_is_identity_static_mapping (m); + + return 0; +} + +static void +nat44_ei_worker_db_free (nat44_ei_main_per_thread_data_t *tnm) +{ + pool_free (tnm->list_pool); + pool_free (tnm->lru_pool); + pool_free (tnm->sessions); + pool_free (tnm->users); + + clib_bihash_free_8_8 (&tnm->user_hash); +} + +u8 * +format_nat44_ei_key (u8 *s, va_list *args) +{ + u64 key = va_arg (*args, u64); + + ip4_address_t addr; + u16 port; + nat_protocol_t protocol; + u32 fib_index; + + split_nat_key (key, &addr, &port, &fib_index, &protocol); + + s = format (s, "%U proto %U port %d fib %d", format_ip4_address, &addr, + format_nat_protocol, protocol, clib_net_to_host_u16 (port), + fib_index); + return s; +} + +u8 * +format_nat44_ei_user_kvp (u8 *s, va_list *args) +{ + clib_bihash_kv_8_8_t *v = va_arg (*args, clib_bihash_kv_8_8_t *); + nat44_ei_user_key_t k; + + k.as_u64 = v->key; + + s = format (s, "%U fib %d user-index %llu", format_ip4_address, &k.addr, + k.fib_index, v->value); + + return s; +} + +u8 * +format_nat44_ei_session_kvp (u8 *s, va_list *args) +{ + clib_bihash_kv_8_8_t *v = va_arg (*args, clib_bihash_kv_8_8_t *); + + s = + format (s, "%U session-index %llu", format_nat44_ei_key, v->key, v->value); + + return s; +} + +u8 * +format_nat44_ei_static_mapping_kvp (u8 *s, va_list *args) +{ + clib_bihash_kv_8_8_t *v = va_arg (*args, clib_bihash_kv_8_8_t *); + + s = format (s, "%U static-mapping-index %llu", format_nat44_ei_key, v->key, + v->value); + + return s; +} + +static void +nat44_ei_worker_db_init (nat44_ei_main_per_thread_data_t *tnm, + u32 translations, u32 translation_buckets, + u32 user_buckets) +{ + dlist_elt_t *head; + + pool_alloc (tnm->list_pool, translations); + pool_alloc (tnm->lru_pool, translations); + pool_alloc (tnm->sessions, translations); + + clib_bihash_init_8_8 (&tnm->user_hash, "users", user_buckets, 0); + + clib_bihash_set_kvp_format_fn_8_8 (&tnm->user_hash, + format_nat44_ei_user_kvp); + + pool_get (tnm->lru_pool, head); + tnm->tcp_trans_lru_head_index = head - tnm->lru_pool; + clib_dlist_init (tnm->lru_pool, tnm->tcp_trans_lru_head_index); + + pool_get (tnm->lru_pool, head); + tnm->tcp_estab_lru_head_index = head - tnm->lru_pool; + clib_dlist_init (tnm->lru_pool, tnm->tcp_estab_lru_head_index); + + pool_get (tnm->lru_pool, head); + tnm->udp_lru_head_index = head - tnm->lru_pool; + clib_dlist_init (tnm->lru_pool, tnm->udp_lru_head_index); + + pool_get (tnm->lru_pool, head); + tnm->icmp_lru_head_index = head - tnm->lru_pool; + clib_dlist_init (tnm->lru_pool, tnm->icmp_lru_head_index); + + pool_get (tnm->lru_pool, head); + tnm->unk_proto_lru_head_index = head - tnm->lru_pool; + clib_dlist_init (tnm->lru_pool, tnm->unk_proto_lru_head_index); +} + +static void +nat44_ei_db_free () +{ + nat44_ei_main_t *nm = &nat44_ei_main; + nat44_ei_main_per_thread_data_t *tnm; + + pool_free (nm->static_mappings); + clib_bihash_free_8_8 (&nm->static_mapping_by_local); + clib_bihash_free_8_8 (&nm->static_mapping_by_external); + + if (nm->pat) + { + clib_bihash_free_8_8 (&nm->in2out); + clib_bihash_free_8_8 (&nm->out2in); + vec_foreach (tnm, nm->per_thread_data) + { + nat44_ei_worker_db_free (tnm); + } + } +} + +static void +nat44_ei_db_init (u32 translations, u32 translation_buckets, u32 user_buckets) +{ + nat44_ei_main_t *nm = &nat44_ei_main; + nat44_ei_main_per_thread_data_t *tnm; + + u32 static_mapping_buckets = 1024; + u32 static_mapping_memory_size = 64 << 20; + + clib_bihash_init_8_8 (&nm->static_mapping_by_local, + "static_mapping_by_local", static_mapping_buckets, + static_mapping_memory_size); + clib_bihash_init_8_8 (&nm->static_mapping_by_external, + "static_mapping_by_external", static_mapping_buckets, + static_mapping_memory_size); + clib_bihash_set_kvp_format_fn_8_8 (&nm->static_mapping_by_local, + format_nat44_ei_static_mapping_kvp); + clib_bihash_set_kvp_format_fn_8_8 (&nm->static_mapping_by_external, + format_nat44_ei_static_mapping_kvp); + + if (nm->pat) + { + clib_bihash_init_8_8 (&nm->in2out, "in2out", translation_buckets, 0); + clib_bihash_init_8_8 (&nm->out2in, "out2in", translation_buckets, 0); + clib_bihash_set_kvp_format_fn_8_8 (&nm->in2out, + format_nat44_ei_session_kvp); + clib_bihash_set_kvp_format_fn_8_8 (&nm->out2in, + format_nat44_ei_session_kvp); + vec_foreach (tnm, nm->per_thread_data) + { + nat44_ei_worker_db_init (tnm, translations, translation_buckets, + user_buckets); + } + } +} + +void +nat44_ei_sessions_clear () +{ + nat44_ei_main_t *nm = &nat44_ei_main; + nat44_ei_main_per_thread_data_t *tnm; + + if (nm->pat) + { + clib_bihash_free_8_8 (&nm->in2out); + clib_bihash_free_8_8 (&nm->out2in); + clib_bihash_init_8_8 (&nm->in2out, "in2out", nm->translation_buckets, 0); + clib_bihash_init_8_8 (&nm->out2in, "out2in", nm->translation_buckets, 0); + clib_bihash_set_kvp_format_fn_8_8 (&nm->in2out, + format_nat44_ei_session_kvp); + clib_bihash_set_kvp_format_fn_8_8 (&nm->out2in, + format_nat44_ei_session_kvp); + vec_foreach (tnm, nm->per_thread_data) + { + nat44_ei_worker_db_free (tnm); + nat44_ei_worker_db_init (tnm, nm->translations, + nm->translation_buckets, nm->user_buckets); + } + } + + vlib_zero_simple_counter (&nm->total_users, 0); + vlib_zero_simple_counter (&nm->total_sessions, 0); + vlib_zero_simple_counter (&nm->user_limit_reached, 0); +} + +static void +nat44_ei_update_outside_fib (ip4_main_t *im, uword opaque, u32 sw_if_index, + u32 new_fib_index, u32 old_fib_index) +{ + nat44_ei_main_t *nm = &nat44_ei_main; + nat44_ei_outside_fib_t *outside_fib; + nat44_ei_interface_t *i; + u8 is_add = 1; + u8 match = 0; + + if (!nm->enabled || (new_fib_index == old_fib_index) || + (!vec_len (nm->outside_fibs))) + { + return; + } + + pool_foreach (i, nm->interfaces) { - if (!m) + if (i->sw_if_index == sw_if_index) { - if (sw_if_index != ~0) - return 0; - else - return VNET_API_ERROR_NO_SUCH_ENTRY; + if (!(nat44_ei_interface_is_outside (i))) + return; + match = 1; } + } - if (identity_nat) + pool_foreach (i, nm->output_feature_interfaces) + { + if (i->sw_if_index == sw_if_index) { - if (vrf_id == ~0) - vrf_id = sm->inside_vrf_id; + if (!(nat44_ei_interface_is_outside (i))) + return; + match = 1; + } + } - pool_foreach (local, m->locals) - { - if (local->vrf_id == vrf_id) - find = local - m->locals; - } - if (find == ~0) - return VNET_API_ERROR_NO_SUCH_ENTRY; + if (!match) + return; - local = pool_elt_at_index (m->locals, find); - fib_index = local->fib_index; - pool_put (m->locals, local); + vec_foreach (outside_fib, nm->outside_fibs) + { + if (outside_fib->fib_index == old_fib_index) + { + outside_fib->refcount--; + if (!outside_fib->refcount) + vec_del1 (nm->outside_fibs, outside_fib - nm->outside_fibs); + break; } - else - fib_index = m->fib_index; + } - /* Free external address port */ - if (!(addr_only || sm->static_mapping_only)) + vec_foreach (outside_fib, nm->outside_fibs) + { + if (outside_fib->fib_index == new_fib_index) { - for (i = 0; i < vec_len (sm->addresses); i++) - { - if (sm->addresses[i].addr.as_u32 == e_addr.as_u32) - { - a = sm->addresses + i; - switch (proto) - { -#define _(N, j, n, s) \ - case NAT_PROTOCOL_##N: \ - --a->busy_##n##_port_refcounts[e_port]; \ - if (e_port > 1024) \ - { \ - a->busy_##n##_ports--; \ - a->busy_##n##_ports_per_thread[get_thread_idx_by_port (e_port)]--; \ - } \ - break; - foreach_nat_protocol -#undef _ - default : return VNET_API_ERROR_INVALID_VALUE_2; - } - break; - } - } + outside_fib->refcount++; + is_add = 0; + break; } + } - if (sm->num_workers > 1) - tsm = vec_elt_at_index (sm->per_thread_data, m->workers[0]); - else - tsm = vec_elt_at_index (sm->per_thread_data, sm->num_workers); + if (is_add) + { + vec_add2 (nm->outside_fibs, outside_fib, 1); + outside_fib->refcount = 1; + outside_fib->fib_index = new_fib_index; + } +} - init_nat_k (&kv, m->local_addr, m->local_port, fib_index, m->proto); - clib_bihash_add_del_8_8 (&sm->static_mapping_by_local, &kv, 0); +int +nat44_ei_add_address (nat44_ei_main_t *nm, ip4_address_t *addr, u32 vrf_id) +{ + nat44_ei_address_t *ap; + nat44_ei_interface_t *i; + vlib_thread_main_t *tm = vlib_get_thread_main (); - /* Delete session(s) for static mapping if exist */ - if (!(sm->static_mapping_only) || - (sm->static_mapping_only && sm->static_mapping_connection_tracking)) + /* Check if address already exists */ + vec_foreach (ap, nm->addresses) + { + if (ap->addr.as_u32 == addr->as_u32) { - u_key.addr = m->local_addr; - u_key.fib_index = fib_index; - kv.key = u_key.as_u64; - nat44_ei_static_mapping_del_sessions (sm, tsm, u_key, addr_only, - e_addr, e_port); + nat44_ei_log_err ("address exist"); + return VNET_API_ERROR_VALUE_EXIST; } + } - fib_table_unlock (fib_index, FIB_PROTOCOL_IP4, sm->fib_src_low); - if (pool_elts (m->locals)) - return 0; + vec_add2 (nm->addresses, ap, 1); - init_nat_k (&kv, m->external_addr, m->external_port, 0, m->proto); - clib_bihash_add_del_8_8 (&sm->static_mapping_by_external, &kv, 0); + ap->addr = *addr; + if (vrf_id != ~0) + ap->fib_index = fib_table_find_or_create_and_lock ( + FIB_PROTOCOL_IP4, vrf_id, nm->fib_src_low); + else + ap->fib_index = ~0; - vec_free (m->tag); - vec_free (m->workers); - /* Delete static mapping from pool */ - pool_put (sm->static_mappings, m); - } +#define _(N, i, n, s) \ + clib_memset (ap->busy_##n##_port_refcounts, 0, \ + sizeof (ap->busy_##n##_port_refcounts)); \ + ap->busy_##n##_ports = 0; \ + ap->busy_##n##_ports_per_thread = 0; \ + vec_validate_init_empty (ap->busy_##n##_ports_per_thread, \ + tm->n_vlib_mains - 1, 0); + foreach_nat_protocol +#undef _ - if (!addr_only || (l_addr.as_u32 == e_addr.as_u32)) - return 0; + /* Add external address to FIB */ + pool_foreach (i, nm->interfaces) + { + if (nat44_ei_interface_is_inside (i) || nm->out2in_dpo) + continue; - /* Add/delete external address to FIB */ - pool_foreach (interface, sm->interfaces) + nat44_ei_add_del_addr_to_fib (addr, 32, i->sw_if_index, 1); + break; + } + pool_foreach (i, nm->output_feature_interfaces) { - if (nat_interface_is_inside (interface) || sm->out2in_dpo) + if (nat44_ei_interface_is_inside (i) || nm->out2in_dpo) continue; - snat_add_del_addr_to_fib (&e_addr, 32, interface->sw_if_index, is_add); + nat44_ei_add_del_addr_to_fib (addr, 32, i->sw_if_index, 1); break; } - pool_foreach (interface, sm->output_feature_interfaces) - { - if (nat_interface_is_inside (interface) || sm->out2in_dpo) - continue; - snat_add_del_addr_to_fib (&e_addr, 32, interface->sw_if_index, is_add); - break; - } return 0; } int -nat44_ei_static_mapping_match (ip4_address_t match_addr, u16 match_port, - u32 match_fib_index, - nat_protocol_t match_protocol, - ip4_address_t *mapping_addr, u16 *mapping_port, - u32 *mapping_fib_index, u8 by_external, - u8 *is_addr_only, u8 *is_identity_nat) +nat44_ei_add_interface_address (nat44_ei_main_t *nm, u32 sw_if_index, + int is_del) { - snat_main_t *sm = &snat_main; - clib_bihash_kv_8_8_t kv, value; - snat_static_mapping_t *m; - u16 port; + ip4_main_t *ip4_main = nm->ip4_main; + ip4_address_t *first_int_addr; + nat44_ei_static_map_resolve_t *rp; + u32 *indices_to_delete = 0; + int i, j; + u32 *auto_add_sw_if_indices = nm->auto_add_sw_if_indices; - if (by_external) - { - init_nat_k (&kv, match_addr, match_port, 0, match_protocol); - if (clib_bihash_search_8_8 (&sm->static_mapping_by_external, &kv, - &value)) - { - /* Try address only mapping */ - init_nat_k (&kv, match_addr, 0, 0, 0); - if (clib_bihash_search_8_8 (&sm->static_mapping_by_external, &kv, - &value)) - return 1; - } - m = pool_elt_at_index (sm->static_mappings, value.value); + first_int_addr = ip4_interface_first_address (ip4_main, sw_if_index, + 0 /* just want the address */); - *mapping_fib_index = m->fib_index; - *mapping_addr = m->local_addr; - port = m->local_port; - } - else + for (i = 0; i < vec_len (auto_add_sw_if_indices); i++) { - init_nat_k (&kv, match_addr, match_port, match_fib_index, - match_protocol); - if (clib_bihash_search_8_8 (&sm->static_mapping_by_local, &kv, &value)) + if (auto_add_sw_if_indices[i] == sw_if_index) { - /* Try address only mapping */ - init_nat_k (&kv, match_addr, 0, match_fib_index, 0); - if (clib_bihash_search_8_8 (&sm->static_mapping_by_local, &kv, - &value)) - return 1; - } - m = pool_elt_at_index (sm->static_mappings, value.value); + if (is_del) + { + /* if have address remove it */ + if (first_int_addr) + (void) nat44_ei_del_address (nm, first_int_addr[0], 1); + else + { + for (j = 0; j < vec_len (nm->to_resolve); j++) + { + rp = nm->to_resolve + j; + if (rp->sw_if_index == sw_if_index) + vec_add1 (indices_to_delete, j); + } + if (vec_len (indices_to_delete)) + { + for (j = vec_len (indices_to_delete) - 1; j >= 0; j--) + vec_del1 (nm->to_resolve, j); + vec_free (indices_to_delete); + } + } + vec_del1 (nm->auto_add_sw_if_indices, i); + } + else + return VNET_API_ERROR_VALUE_EXIST; - *mapping_fib_index = sm->outside_fib_index; - *mapping_addr = m->external_addr; - port = m->external_port; + return 0; + } } - /* Address only mapping doesn't change port */ - if (is_addr_only_static_mapping (m)) - *mapping_port = match_port; - else - *mapping_port = port; + if (is_del) + return VNET_API_ERROR_NO_SUCH_ENTRY; - if (PREDICT_FALSE (is_addr_only != 0)) - *is_addr_only = is_addr_only_static_mapping (m); + /* add to the auto-address list */ + vec_add1 (nm->auto_add_sw_if_indices, sw_if_index); - if (PREDICT_FALSE (is_identity_nat != 0)) - *is_identity_nat = is_identity_static_mapping (m); + /* If the address is already bound - or static - add it now */ + if (first_int_addr) + (void) nat44_ei_add_address (nm, first_int_addr, ~0); return 0; } -static void -nat44_ei_worker_db_free (snat_main_per_thread_data_t *tsm) +static int +nat44_ei_is_address_used_in_static_mapping (ip4_address_t addr) { - pool_free (tsm->list_pool); - pool_free (tsm->lru_pool); - pool_free (tsm->sessions); - pool_free (tsm->users); - - clib_bihash_free_8_8 (&tsm->user_hash); + nat44_ei_main_t *nm = &nat44_ei_main; + nat44_ei_static_mapping_t *m; + pool_foreach (m, nm->static_mappings) + { + if (nat44_ei_is_addr_only_static_mapping (m) || + nat44_ei_is_identity_static_mapping (m)) + continue; + if (m->external_addr.as_u32 == addr.as_u32) + return 1; + } + return 0; } -static void -nat44_ei_worker_db_init (snat_main_per_thread_data_t *tsm, u32 translations, - u32 translation_buckets, u32 user_buckets) +int +nat44_ei_del_address (nat44_ei_main_t *nm, ip4_address_t addr, u8 delete_sm) { - dlist_elt_t *head; + nat44_ei_address_t *a = 0; + nat44_ei_session_t *ses; + u32 *ses_to_be_removed = 0, *ses_index; + nat44_ei_main_per_thread_data_t *tnm; + nat44_ei_interface_t *interface; + nat44_ei_static_mapping_t *m; + int i; - pool_alloc (tsm->list_pool, translations); - pool_alloc (tsm->lru_pool, translations); - pool_alloc (tsm->sessions, translations); + /* Find SNAT address */ + for (i = 0; i < vec_len (nm->addresses); i++) + { + if (nm->addresses[i].addr.as_u32 == addr.as_u32) + { + a = nm->addresses + i; + break; + } + } + if (!a) + { + nat44_ei_log_err ("no such address"); + return VNET_API_ERROR_NO_SUCH_ENTRY; + } - clib_bihash_init_8_8 (&tsm->user_hash, "users", user_buckets, 0); + if (delete_sm) + { + pool_foreach (m, nm->static_mappings) + { + if (m->external_addr.as_u32 == addr.as_u32) + (void) nat44_ei_add_del_static_mapping ( + m->local_addr, m->external_addr, m->local_port, m->external_port, + m->proto, ~0 /* sw_if_index */, m->vrf_id, + nat44_ei_is_addr_only_static_mapping (m), + nat44_ei_is_identity_static_mapping (m), m->tag, 0); + } + } + else + { + /* Check if address is used in some static mapping */ + if (nat44_ei_is_address_used_in_static_mapping (addr)) + { + nat44_ei_log_err ("address used in static mapping"); + return VNET_API_ERROR_UNSPECIFIED; + } + } - clib_bihash_set_kvp_format_fn_8_8 (&tsm->user_hash, format_user_kvp); + if (a->fib_index != ~0) + fib_table_unlock (a->fib_index, FIB_PROTOCOL_IP4, nm->fib_src_low); - pool_get (tsm->lru_pool, head); - tsm->tcp_trans_lru_head_index = head - tsm->lru_pool; - clib_dlist_init (tsm->lru_pool, tsm->tcp_trans_lru_head_index); + /* Delete sessions using address */ + if (a->busy_tcp_ports || a->busy_udp_ports || a->busy_icmp_ports) + { + vec_foreach (tnm, nm->per_thread_data) + { + pool_foreach (ses, tnm->sessions) + { + if (ses->out2in.addr.as_u32 == addr.as_u32) + { + nat44_ei_free_session_data (nm, ses, + tnm - nm->per_thread_data, 0); + vec_add1 (ses_to_be_removed, ses - tnm->sessions); + } + } + vec_foreach (ses_index, ses_to_be_removed) + { + ses = pool_elt_at_index (tnm->sessions, ses_index[0]); + nat44_ei_delete_session (nm, ses, tnm - nm->per_thread_data); + } + vec_free (ses_to_be_removed); + } + } - pool_get (tsm->lru_pool, head); - tsm->tcp_estab_lru_head_index = head - tsm->lru_pool; - clib_dlist_init (tsm->lru_pool, tsm->tcp_estab_lru_head_index); +#define _(N, i, n, s) vec_free (a->busy_##n##_ports_per_thread); + foreach_nat_protocol +#undef _ + vec_del1 (nm->addresses, i); - pool_get (tsm->lru_pool, head); - tsm->udp_lru_head_index = head - tsm->lru_pool; - clib_dlist_init (tsm->lru_pool, tsm->udp_lru_head_index); + /* Delete external address from FIB */ + pool_foreach (interface, nm->interfaces) + { + if (nat44_ei_interface_is_inside (interface) || nm->out2in_dpo) + continue; + nat44_ei_add_del_addr_to_fib (&addr, 32, interface->sw_if_index, 0); + break; + } - pool_get (tsm->lru_pool, head); - tsm->icmp_lru_head_index = head - tsm->lru_pool; - clib_dlist_init (tsm->lru_pool, tsm->icmp_lru_head_index); + pool_foreach (interface, nm->output_feature_interfaces) + { + if (nat44_ei_interface_is_inside (interface) || nm->out2in_dpo) + continue; + nat44_ei_add_del_addr_to_fib (&addr, 32, interface->sw_if_index, 0); + break; + } - pool_get (tsm->lru_pool, head); - tsm->unk_proto_lru_head_index = head - tsm->lru_pool; - clib_dlist_init (tsm->lru_pool, tsm->unk_proto_lru_head_index); + return 0; } static void -nat44_ei_db_free () +nat44_ei_ip4_add_del_interface_address_cb (ip4_main_t *im, uword opaque, + u32 sw_if_index, + ip4_address_t *address, + u32 address_length, + u32 if_address_index, u32 is_delete) { - snat_main_t *sm = &snat_main; - snat_main_per_thread_data_t *tsm; + nat44_ei_main_t *nm = &nat44_ei_main; + nat44_ei_static_map_resolve_t *rp; + ip4_address_t l_addr; + int i, j; + int rv; + nat44_ei_address_t *addresses = nm->addresses; + + if (!nm->enabled) + return; + + for (i = 0; i < vec_len (nm->auto_add_sw_if_indices); i++) + { + if (sw_if_index == nm->auto_add_sw_if_indices[i]) + goto match; + } - pool_free (sm->static_mappings); - clib_bihash_free_8_8 (&sm->static_mapping_by_local); - clib_bihash_free_8_8 (&sm->static_mapping_by_external); + return; - if (sm->pat) +match: + if (!is_delete) { - clib_bihash_free_8_8 (&sm->in2out); - clib_bihash_free_8_8 (&sm->out2in); - vec_foreach (tsm, sm->per_thread_data) + /* Don't trip over lease renewal, static config */ + for (j = 0; j < vec_len (addresses); j++) + if (addresses[j].addr.as_u32 == address->as_u32) + return; + + (void) nat44_ei_add_address (nm, address, ~0); + /* Scan static map resolution vector */ + for (j = 0; j < vec_len (nm->to_resolve); j++) { - nat44_ei_worker_db_free (tsm); + rp = nm->to_resolve + j; + if (rp->addr_only) + continue; + /* On this interface? */ + if (rp->sw_if_index == sw_if_index) + { + /* Indetity mapping? */ + if (rp->l_addr.as_u32 == 0) + l_addr.as_u32 = address[0].as_u32; + else + l_addr.as_u32 = rp->l_addr.as_u32; + /* Add the static mapping */ + rv = nat44_ei_add_del_static_mapping ( + l_addr, address[0], rp->l_port, rp->e_port, rp->proto, + ~0 /* sw_if_index */, rp->vrf_id, rp->addr_only, + rp->identity_nat, rp->tag, 1); + if (rv) + nat_elog_notice_X1 ( + nm, "nat44_ei_add_del_static_mapping returned %d", "i4", rv); + } } + return; } + else + { + (void) nat44_ei_del_address (nm, address[0], 1); + return; + } +} + +int +nat44_ei_set_frame_queue_nelts (u32 frame_queue_nelts) +{ + fail_if_enabled (); + nat44_ei_main_t *nm = &nat44_ei_main; + nm->frame_queue_nelts = frame_queue_nelts; + return 0; } static void -nat44_ei_db_init (u32 translations, u32 translation_buckets, u32 user_buckets) +nat44_ei_ip4_add_del_addr_only_sm_cb (ip4_main_t *im, uword opaque, + u32 sw_if_index, ip4_address_t *address, + u32 address_length, u32 if_address_index, + u32 is_delete) { - snat_main_t *sm = &snat_main; - snat_main_per_thread_data_t *tsm; + nat44_ei_main_t *nm = &nat44_ei_main; + nat44_ei_static_map_resolve_t *rp; + nat44_ei_static_mapping_t *m; + clib_bihash_kv_8_8_t kv, value; + int i, rv; + ip4_address_t l_addr; - u32 static_mapping_buckets = 1024; - u32 static_mapping_memory_size = 64 << 20; + if (!nm->enabled) + return; - clib_bihash_init_8_8 (&sm->static_mapping_by_local, - "static_mapping_by_local", static_mapping_buckets, - static_mapping_memory_size); - clib_bihash_init_8_8 (&sm->static_mapping_by_external, - "static_mapping_by_external", static_mapping_buckets, - static_mapping_memory_size); - clib_bihash_set_kvp_format_fn_8_8 (&sm->static_mapping_by_local, - format_static_mapping_kvp); - clib_bihash_set_kvp_format_fn_8_8 (&sm->static_mapping_by_external, - format_static_mapping_kvp); - - if (sm->pat) - { - clib_bihash_init_8_8 (&sm->in2out, "in2out", translation_buckets, 0); - clib_bihash_init_8_8 (&sm->out2in, "out2in", translation_buckets, 0); - clib_bihash_set_kvp_format_fn_8_8 (&sm->in2out, format_session_kvp); - clib_bihash_set_kvp_format_fn_8_8 (&sm->out2in, format_session_kvp); - vec_foreach (tsm, sm->per_thread_data) - { - nat44_ei_worker_db_init (tsm, translations, translation_buckets, - user_buckets); - } + for (i = 0; i < vec_len (nm->to_resolve); i++) + { + rp = nm->to_resolve + i; + if (rp->addr_only == 0) + continue; + if (rp->sw_if_index == sw_if_index) + goto match; + } + + return; + +match: + init_nat_k (&kv, *address, rp->addr_only ? 0 : rp->e_port, + nm->outside_fib_index, rp->addr_only ? 0 : rp->proto); + if (clib_bihash_search_8_8 (&nm->static_mapping_by_external, &kv, &value)) + m = 0; + else + m = pool_elt_at_index (nm->static_mappings, value.value); + + if (!is_delete) + { + /* Don't trip over lease renewal, static config */ + if (m) + return; + } + else + { + if (!m) + return; } + + /* Indetity mapping? */ + if (rp->l_addr.as_u32 == 0) + l_addr.as_u32 = address[0].as_u32; + else + l_addr.as_u32 = rp->l_addr.as_u32; + /* Add the static mapping */ + + rv = nat44_ei_add_del_static_mapping ( + l_addr, address[0], rp->l_port, rp->e_port, rp->proto, + ~0 /* sw_if_index */, rp->vrf_id, rp->addr_only, rp->identity_nat, rp->tag, + !is_delete); + if (rv) + nat_elog_notice_X1 (nm, "nat44_ei_add_del_static_mapping returned %d", + "i4", rv); } -void -nat44_ei_sessions_clear () +VLIB_NODE_FN (nat44_ei_classify_node) +(vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame) { + u32 n_left_from, *from, *to_next; + nat44_ei_classify_next_t next_index; nat44_ei_main_t *nm = &nat44_ei_main; + nat44_ei_static_mapping_t *m; + u32 next_in2out = 0, next_out2in = 0; - snat_main_per_thread_data_t *tsm; - snat_main_t *sm = &snat_main; + from = vlib_frame_vector_args (frame); + n_left_from = frame->n_vectors; + next_index = node->cached_next_index; - if (sm->pat) + while (n_left_from > 0) { - clib_bihash_free_8_8 (&sm->in2out); - clib_bihash_free_8_8 (&sm->out2in); - clib_bihash_init_8_8 (&sm->in2out, "in2out", nm->translation_buckets, 0); - clib_bihash_init_8_8 (&sm->out2in, "out2in", nm->translation_buckets, 0); - clib_bihash_set_kvp_format_fn_8_8 (&sm->in2out, format_session_kvp); - clib_bihash_set_kvp_format_fn_8_8 (&sm->out2in, format_session_kvp); - vec_foreach (tsm, sm->per_thread_data) + u32 n_left_to_next; + + vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next); + + while (n_left_from > 0 && n_left_to_next > 0) { - nat44_ei_worker_db_free (tsm); - nat44_ei_worker_db_init (tsm, nm->translations, - nm->translation_buckets, nm->user_buckets); + u32 bi0; + vlib_buffer_t *b0; + u32 next0 = NAT44_EI_CLASSIFY_NEXT_IN2OUT; + ip4_header_t *ip0; + nat44_ei_address_t *ap; + clib_bihash_kv_8_8_t kv0, value0; + + /* speculatively enqueue b0 to the current next frame */ + bi0 = from[0]; + to_next[0] = bi0; + from += 1; + to_next += 1; + n_left_from -= 1; + n_left_to_next -= 1; + + b0 = vlib_get_buffer (vm, bi0); + ip0 = vlib_buffer_get_current (b0); + + vec_foreach (ap, nm->addresses) + { + if (ip0->dst_address.as_u32 == ap->addr.as_u32) + { + next0 = NAT44_EI_CLASSIFY_NEXT_OUT2IN; + goto enqueue0; + } + } + + if (PREDICT_FALSE (pool_elts (nm->static_mappings))) + { + init_nat_k (&kv0, ip0->dst_address, 0, 0, 0); + /* try to classify the fragment based on IP header alone */ + if (!clib_bihash_search_8_8 (&nm->static_mapping_by_external, + &kv0, &value0)) + { + m = pool_elt_at_index (nm->static_mappings, value0.value); + if (m->local_addr.as_u32 != m->external_addr.as_u32) + next0 = NAT44_EI_CLASSIFY_NEXT_OUT2IN; + goto enqueue0; + } + init_nat_k (&kv0, ip0->dst_address, + vnet_buffer (b0)->ip.reass.l4_dst_port, 0, + ip_proto_to_nat_proto (ip0->protocol)); + if (!clib_bihash_search_8_8 (&nm->static_mapping_by_external, + &kv0, &value0)) + { + m = pool_elt_at_index (nm->static_mappings, value0.value); + if (m->local_addr.as_u32 != m->external_addr.as_u32) + next0 = NAT44_EI_CLASSIFY_NEXT_OUT2IN; + } + } + + enqueue0: + if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE) && + (b0->flags & VLIB_BUFFER_IS_TRACED))) + { + nat44_ei_classify_trace_t *t = + vlib_add_trace (vm, node, b0, sizeof (*t)); + t->cached = 0; + t->next_in2out = next0 == NAT44_EI_CLASSIFY_NEXT_IN2OUT ? 1 : 0; + } + + next_in2out += next0 == NAT44_EI_CLASSIFY_NEXT_IN2OUT; + next_out2in += next0 == NAT44_EI_CLASSIFY_NEXT_OUT2IN; + + /* verify speculative enqueue, maybe switch current next frame */ + vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next, + n_left_to_next, bi0, next0); } + + vlib_put_next_frame (vm, node, next_index, n_left_to_next); } - // TODO: function for reset counters - vlib_zero_simple_counter (&sm->total_users, 0); - vlib_zero_simple_counter (&sm->total_sessions, 0); - vlib_zero_simple_counter (&sm->user_limit_reached, 0); + vlib_node_increment_counter ( + vm, node->node_index, NAT44_EI_CLASSIFY_ERROR_NEXT_IN2OUT, next_in2out); + vlib_node_increment_counter ( + vm, node->node_index, NAT44_EI_CLASSIFY_ERROR_NEXT_OUT2IN, next_out2in); + return frame->n_vectors; } +VLIB_REGISTER_NODE (nat44_ei_classify_node) = { + .name = "nat44-ei-classify", + .vector_size = sizeof (u32), + .format_trace = format_nat44_ei_classify_trace, + .type = VLIB_NODE_TYPE_INTERNAL, + .n_errors = ARRAY_LEN(nat44_ei_classify_error_strings), + .error_strings = nat44_ei_classify_error_strings, + .n_next_nodes = NAT44_EI_CLASSIFY_N_NEXT, + .next_nodes = { + [NAT44_EI_CLASSIFY_NEXT_IN2OUT] = "nat44-ei-in2out", + [NAT44_EI_CLASSIFY_NEXT_OUT2IN] = "nat44-ei-out2in", + [NAT44_EI_CLASSIFY_NEXT_DROP] = "error-drop", + }, +}; + /* * fd.io coding-style-patch-verification: ON * diff --git a/src/plugins/nat/nat44-ei/nat44_ei.h b/src/plugins/nat/nat44-ei/nat44_ei.h index 510e73de59a..b9212df44bd 100644 --- a/src/plugins/nat/nat44-ei/nat44_ei.h +++ b/src/plugins/nat/nat44-ei/nat44_ei.h @@ -19,6 +19,95 @@ #ifndef __included_nat44_ei_h__ #define __included_nat44_ei_h__ +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +/* default number of worker handoff frame queue elements */ +#define NAT_FQ_NELTS_DEFAULT 64 + +/* External address and port allocation modes */ +#define foreach_nat44_ei_addr_and_port_alloc_alg \ + _ (0, DEFAULT, "default") \ + _ (1, MAPE, "map-e") \ + _ (2, RANGE, "port-range") + +typedef enum +{ +#define _(v, N, s) NAT44_EI_ADDR_AND_PORT_ALLOC_ALG_##N = v, + foreach_nat44_ei_addr_and_port_alloc_alg +#undef _ +} nat44_ei_addr_and_port_alloc_alg_t; + +/* Interface flags */ +#define NAT44_EI_INTERFACE_FLAG_IS_INSIDE (1 << 0) +#define NAT44_EI_INTERFACE_FLAG_IS_OUTSIDE (1 << 1) + +/* Session flags */ +#define NAT44_EI_SESSION_FLAG_STATIC_MAPPING (1 << 0) +#define NAT44_EI_SESSION_FLAG_UNKNOWN_PROTO (1 << 1) + +/* Static mapping flags */ +#define NAT44_EI_STATIC_MAPPING_FLAG_ADDR_ONLY (1 << 0) +#define NAT44_EI_STATIC_MAPPING_FLAG_IDENTITY_NAT (1 << 1) + +typedef struct +{ + ip4_address_t addr; + u32 fib_index; +#define _(N, i, n, s) \ + u32 busy_##n##_ports; \ + u32 *busy_##n##_ports_per_thread; \ + u32 busy_##n##_port_refcounts[65535]; + foreach_nat_protocol +#undef _ +} nat44_ei_address_t; + +clib_error_t *nat44_ei_api_hookup (vlib_main_t *vm); + +/* NAT address and port allocation function */ +typedef int (nat44_ei_alloc_out_addr_and_port_function_t) ( + nat44_ei_address_t *addresses, u32 fib_index, u32 thread_index, + nat_protocol_t proto, ip4_address_t *addr, u16 *port, u16 port_per_thread, + u32 snat_thread_index); + +typedef struct +{ + u16 identifier; + u16 sequence; +} icmp_echo_header_t; + +typedef struct +{ + u16 src_port, dst_port; +} tcp_udp_header_t; + +typedef struct +{ + union + { + struct + { + ip4_address_t addr; + u32 fib_index; + }; + u64 as_u64; + }; +} nat44_ei_user_key_t; + typedef struct { /* maximum number of users */ @@ -39,15 +128,371 @@ typedef struct } nat44_ei_config_t; typedef struct +{ + ip4_address_t l_addr; + ip4_address_t pool_addr; + u16 l_port; + u16 e_port; + u32 sw_if_index; + u32 vrf_id; + u32 flags; + nat_protocol_t proto; + u8 addr_only; + u8 identity_nat; + u8 exact; + u8 *tag; +} nat44_ei_static_map_resolve_t; + +// TODO: cleanup/redo (there is no lb in EI nat) +typedef struct +{ + /* backend IP address */ + ip4_address_t addr; + /* backend port number */ + u16 port; + /* probability of the backend to be randomly matched */ + u8 probability; + u8 prefix; + /* backend FIB table */ + u32 vrf_id; + u32 fib_index; +} nat44_ei_lb_addr_port_t; + +typedef struct +{ + /* prefered pool address */ + ip4_address_t pool_addr; + /* local IP address */ + ip4_address_t local_addr; + /* external IP address */ + ip4_address_t external_addr; + /* local port */ + u16 local_port; + /* external port */ + u16 external_port; + /* local FIB table */ + u32 vrf_id; + u32 fib_index; + /* protocol */ + nat_protocol_t proto; + /* worker threads used by backends/local host */ + u32 *workers; + /* opaque string tag */ + u8 *tag; + /* backends for load-balancing mode */ + nat44_ei_lb_addr_port_t *locals; + /* flags */ + u32 flags; +} nat44_ei_static_mapping_t; + +typedef struct +{ + u32 sw_if_index; + u8 flags; +} nat44_ei_interface_t; + +typedef struct +{ + u32 fib_index; + u32 ref_count; +} nat44_ei_fib_t; + +typedef struct +{ + u32 fib_index; + u32 refcount; +} nat44_ei_outside_fib_t; + +typedef CLIB_PACKED (struct { + /* Outside network tuple */ + struct + { + ip4_address_t addr; + u32 fib_index; + u16 port; + } out2in; + + /* Inside network tuple */ + struct + { + ip4_address_t addr; + u32 fib_index; + u16 port; + } in2out; + + nat_protocol_t nat_proto; + + /* Flags */ + u32 flags; + + /* Per-user translations */ + u32 per_user_index; + u32 per_user_list_head_index; + + /* head of LRU list in which this session is tracked */ + u32 lru_head_index; + /* index in global LRU list */ + u32 lru_index; + f64 last_lru_update; + + /* Last heard timer */ + f64 last_heard; + + /* Last HA refresh */ + f64 ha_last_refreshed; + + /* Counters */ + u64 total_bytes; + u32 total_pkts; + + /* External host address and port */ + ip4_address_t ext_host_addr; + u16 ext_host_port; + + /* External host address and port after translation */ + ip4_address_t ext_host_nat_addr; + u16 ext_host_nat_port; + + /* TCP session state */ + u8 state; + u32 i2o_fin_seq; + u32 o2i_fin_seq; + u64 tcp_closed_timestamp; + + /* user index */ + u32 user_index; +}) nat44_ei_session_t; + +typedef CLIB_PACKED (struct { + ip4_address_t addr; + u32 fib_index; + u32 sessions_per_user_list_head_index; + u32 nsessions; + u32 nstaticsessions; +}) nat44_ei_user_t; + +typedef struct +{ + /* Find-a-user => src address lookup */ + clib_bihash_8_8_t user_hash; + + /* User pool */ + nat44_ei_user_t *users; + + /* Session pool */ + nat44_ei_session_t *sessions; + + /* Pool of doubly-linked list elements */ + dlist_elt_t *list_pool; + + /* LRU session list - head is stale, tail is fresh */ + dlist_elt_t *lru_pool; + u32 tcp_trans_lru_head_index; + u32 tcp_estab_lru_head_index; + u32 udp_lru_head_index; + u32 icmp_lru_head_index; + u32 unk_proto_lru_head_index; + + /* NAT thread index */ + u32 snat_thread_index; + + /* real thread index */ + u32 thread_index; + +} nat44_ei_main_per_thread_data_t; + +/* Return worker thread index for given packet */ +typedef u32 (nat44_ei_get_worker_in2out_function_t) (ip4_header_t *ip, + u32 rx_fib_index, + u8 is_output); + +typedef u32 (nat44_ei_get_worker_out2in_function_t) (vlib_buffer_t *b, + ip4_header_t *ip, + u32 rx_fib_index, + u8 is_output); + +typedef struct +{ + u32 cached_sw_if_index; + u32 cached_ip4_address; +} nat44_ei_runtime_t; + +typedef struct +{ + u32 thread_index; + f64 now; +} nat44_ei_is_idle_session_ctx_t; + +typedef struct nat44_ei_main_s { u32 translations; u32 translation_buckets; u32 user_buckets; + u8 out2in_dpo; + u8 forwarding_enabled; + u8 static_mapping_only; + u8 static_mapping_connection_tracking; + + u16 mss_clamping; + + /* Find a static mapping by local */ + clib_bihash_8_8_t static_mapping_by_local; + + /* Find a static mapping by external */ + clib_bihash_8_8_t static_mapping_by_external; + + /* Static mapping pool */ + nat44_ei_static_mapping_t *static_mappings; + + /* Interface pool */ + nat44_ei_interface_t *interfaces; + nat44_ei_interface_t *output_feature_interfaces; + + /* Is translation memory size calculated or user defined */ + u8 translation_memory_size_set; + + u32 max_users_per_thread; + u32 max_translations_per_thread; + u32 max_translations_per_user; + + u32 inside_vrf_id; + u32 inside_fib_index; + + u32 outside_vrf_id; + u32 outside_fib_index; + + /* Thread settings */ + u32 num_workers; + u32 first_worker_index; + u32 *workers; + nat44_ei_get_worker_in2out_function_t *worker_in2out_cb; + nat44_ei_get_worker_out2in_function_t *worker_out2in_cb; + u16 port_per_thread; + + /* Main lookup tables */ + clib_bihash_8_8_t out2in; + clib_bihash_8_8_t in2out; + + /* Per thread data */ + nat44_ei_main_per_thread_data_t *per_thread_data; + + /* Vector of outside addresses */ + nat44_ei_address_t *addresses; + + nat44_ei_alloc_out_addr_and_port_function_t *alloc_addr_and_port; + /* Address and port allocation type */ + nat44_ei_addr_and_port_alloc_alg_t addr_and_port_alloc_alg; + /* Port set parameters (MAP-E) */ + u8 psid_offset; + u8 psid_length; + u16 psid; + /* Port range parameters */ + u16 start_port; + u16 end_port; + + /* vector of fibs */ + nat44_ei_fib_t *fibs; + + /* vector of outside fibs */ + nat44_ei_outside_fib_t *outside_fibs; + + /* sw_if_indices whose intfc addresses should be auto-added */ + u32 *auto_add_sw_if_indices; + + /* vector of interface address static mappings to resolve. */ + nat44_ei_static_map_resolve_t *to_resolve; + + u32 in2out_node_index; + u32 out2in_node_index; + u32 in2out_output_node_index; + + u32 fq_in2out_index; + u32 fq_in2out_output_index; + u32 fq_out2in_index; + + /* Randomize port allocation order */ + u32 random_seed; + + nat_timeouts_t timeouts; + + /* counters */ + vlib_simple_counter_main_t total_users; + vlib_simple_counter_main_t total_sessions; + vlib_simple_counter_main_t user_limit_reached; + +#define _(x) vlib_simple_counter_main_t x; + struct + { + struct + { + struct + { + foreach_nat_counter; + } in2out; + + struct + { + foreach_nat_counter; + } out2in; + + } fastpath; + + struct + { + struct + { + foreach_nat_counter; + } in2out; + + struct + { + foreach_nat_counter; + } out2in; + } slowpath; + + vlib_simple_counter_main_t hairpinning; + } counters; +#undef _ + + /* API message ID base */ + u16 msg_id_base; + + /* log class */ + vlib_log_class_t log_class; + /* logging level */ + u8 log_level; + + /* convenience */ + api_main_t *api_main; + ip4_main_t *ip4_main; + ip_lookup_main_t *ip4_lookup_main; + + fib_source_t fib_src_hi; + fib_source_t fib_src_low; + + /* pat (port address translation) + * dynamic mapping enabled or conneciton tracking */ + u8 pat; + + /* number of worker handoff frame queue elements */ + u32 frame_queue_nelts; + + /* nat44 plugin enabled */ + u8 enabled; + nat44_ei_config_t rconfig; + u32 in2out_hairpinning_finish_ip4_lookup_node_fq_index; + u32 in2out_hairpinning_finish_interface_output_node_fq_index; + u32 hairpinning_fq_index; + u32 hairpin_dst_fq_index; + + vnet_main_t *vnet_main; } nat44_ei_main_t; +extern nat44_ei_main_t nat44_ei_main; + int nat44_ei_plugin_enable (nat44_ei_config_t c); int nat44_ei_plugin_disable (); @@ -66,11 +511,9 @@ int nat44_ei_user_del (ip4_address_t *addr, u32 fib_index); * @param addr IPv4 address * @param fib_index FIB table index */ -void nat44_ei_static_mapping_del_sessions (snat_main_t *sm, - snat_main_per_thread_data_t *tsm, - snat_user_key_t u_key, - int addr_only, ip4_address_t e_addr, - u16 e_port); +void nat44_ei_static_mapping_del_sessions ( + nat44_ei_main_t *nm, nat44_ei_main_per_thread_data_t *tnm, + nat44_ei_user_key_t u_key, int addr_only, ip4_address_t e_addr, u16 e_port); u32 nat44_ei_get_in2out_worker_index (ip4_header_t *ip0, u32 rx_fib_index0, u8 is_output); @@ -135,7 +578,7 @@ int nat44_ei_add_del_static_mapping (ip4_address_t l_addr, * * @return 0 on success, non-zero value otherwise */ -int nat44_ei_del_session (snat_main_t *sm, ip4_address_t *addr, u16 port, +int nat44_ei_del_session (nat44_ei_main_t *nm, ip4_address_t *addr, u16 port, nat_protocol_t proto, u32 vrf_id, int is_in); /** @@ -165,6 +608,100 @@ int nat44_ei_static_mapping_match (ip4_address_t match_addr, u16 match_port, */ void nat44_ei_sessions_clear (); +nat44_ei_user_t *nat44_ei_user_get_or_create (nat44_ei_main_t *nm, + ip4_address_t *addr, + u32 fib_index, u32 thread_index); + +nat44_ei_session_t *nat44_ei_session_alloc_or_recycle (nat44_ei_main_t *nm, + nat44_ei_user_t *u, + u32 thread_index, + f64 now); + +void nat44_ei_free_session_data_v2 (nat44_ei_main_t *nm, nat44_ei_session_t *s, + u32 thread_index, u8 is_ha); + +void nat44_ei_free_outside_address_and_port (nat44_ei_address_t *addresses, + u32 thread_index, + ip4_address_t *addr, u16 port, + nat_protocol_t protocol); + +int nat44_ei_set_outside_address_and_port (nat44_ei_address_t *addresses, + u32 thread_index, + ip4_address_t addr, u16 port, + nat_protocol_t protocol); + +int nat44_ei_del_address (nat44_ei_main_t *nm, ip4_address_t addr, + u8 delete_sm); + +void nat44_ei_free_session_data (nat44_ei_main_t *nm, nat44_ei_session_t *s, + u32 thread_index, u8 is_ha); + +int nat44_ei_set_workers (uword *bitmap); + +void nat44_ei_add_del_address_dpo (ip4_address_t addr, u8 is_add); + +int nat44_ei_add_address (nat44_ei_main_t *nm, ip4_address_t *addr, + u32 vrf_id); + +void nat44_ei_delete_session (nat44_ei_main_t *nm, nat44_ei_session_t *ses, + u32 thread_index); + +int nat44_ei_interface_add_del (u32 sw_if_index, u8 is_inside, int is_del); + +int nat44_ei_interface_add_del_output_feature (u32 sw_if_index, u8 is_inside, + int is_del); + +int nat44_ei_add_interface_address (nat44_ei_main_t *nm, u32 sw_if_index, + int is_del); + +/* Call back functions for clib_bihash_add_or_overwrite_stale */ +int nat44_i2o_is_idle_session_cb (clib_bihash_kv_8_8_t *kv, void *arg); +int nat44_o2i_is_idle_session_cb (clib_bihash_kv_8_8_t *kv, void *arg); + +int nat44_ei_hairpinning (vlib_main_t *vm, vlib_node_runtime_t *node, + nat44_ei_main_t *nm, u32 thread_index, + vlib_buffer_t *b0, ip4_header_t *ip0, + udp_header_t *udp0, tcp_header_t *tcp0, u32 proto0, + int do_trace, u32 *required_thread_index); + +void nat44_ei_hairpinning_sm_unknown_proto (nat44_ei_main_t *nm, + vlib_buffer_t *b, + ip4_header_t *ip); + +u32 nat44_ei_icmp_hairpinning (nat44_ei_main_t *nm, vlib_buffer_t *b0, + u32 thread_index, ip4_header_t *ip0, + icmp46_header_t *icmp0, + u32 *required_thread_index); + +int nat44_ei_set_frame_queue_nelts (u32 frame_queue_nelts); + +#define nat44_ei_is_session_static(sp) \ + (sp->flags & NAT44_EI_SESSION_FLAG_STATIC_MAPPING) +#define nat44_ei_is_unk_proto_session(sp) \ + (sp->flags & NAT44_EI_SESSION_FLAG_UNKNOWN_PROTO) + +#define nat44_ei_interface_is_inside(ip) \ + (ip->flags & NAT44_EI_INTERFACE_FLAG_IS_INSIDE) +#define nat44_ei_interface_is_outside(ip) \ + (ip->flags & NAT44_EI_INTERFACE_FLAG_IS_OUTSIDE) + +#define nat44_ei_is_addr_only_static_mapping(mp) \ + (mp->flags & NAT44_EI_STATIC_MAPPING_FLAG_ADDR_ONLY) +#define nat44_ei_is_identity_static_mapping(mp) \ + (mp->flags & NAT44_EI_STATIC_MAPPING_FLAG_IDENTITY_NAT) + +/* logging */ +#define nat44_ei_log_err(...) \ + vlib_log (VLIB_LOG_LEVEL_ERR, nat44_ei_main.log_class, __VA_ARGS__) +#define nat44_ei_log_warn(...) \ + vlib_log (VLIB_LOG_LEVEL_WARNING, nat44_ei_main.log_class, __VA_ARGS__) +#define nat44_ei_log_notice(...) \ + vlib_log (VLIB_LOG_LEVEL_NOTICE, nat44_ei_main.log_class, __VA_ARGS__) +#define nat44_ei_log_info(...) \ + vlib_log (VLIB_LOG_LEVEL_INFO, nat44_ei_main.log_class, __VA_ARGS__) +#define nat44_ei_log_debug(...) \ + vlib_log (VLIB_LOG_LEVEL_DEBUG, nat44_ei_main.log_class, __VA_ARGS__) + #endif /* __included_nat44_ei_h__ */ /* * fd.io coding-style-patch-verification: ON diff --git a/src/plugins/nat/nat44-ei/nat44_ei_api.c b/src/plugins/nat/nat44-ei/nat44_ei_api.c new file mode 100644 index 00000000000..74e077485cc --- /dev/null +++ b/src/plugins/nat/nat44-ei/nat44_ei_api.c @@ -0,0 +1,1199 @@ +/* + * Copyright (c) 2020 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +#include + +#include +#include + +#include +#include + +#include +#include + +#define REPLY_MSG_ID_BASE nm->msg_id_base +#include + +static void +vl_api_nat44_ei_show_running_config_t_handler ( + vl_api_nat44_ei_show_running_config_t *mp) +{ + vl_api_nat44_ei_show_running_config_reply_t *rmp; + nat44_ei_main_t *nm = &nat44_ei_main; + nat44_ei_config_t *rc = &nm->rconfig; + int rv = 0; + + REPLY_MACRO2_ZERO ( + VL_API_NAT44_EI_SHOW_RUNNING_CONFIG_REPLY, ({ + rmp->inside_vrf = htonl (rc->inside_vrf); + rmp->outside_vrf = htonl (rc->outside_vrf); + rmp->users = htonl (rc->users); + rmp->sessions = htonl (rc->sessions); + rmp->user_sessions = htonl (rc->user_sessions); + + rmp->user_buckets = htonl (nm->user_buckets); + rmp->translation_buckets = htonl (nm->translation_buckets); + + rmp->timeouts.udp = htonl (nm->timeouts.udp); + rmp->timeouts.tcp_established = htonl (nm->timeouts.tcp.established); + rmp->timeouts.tcp_transitory = htonl (nm->timeouts.tcp.transitory); + rmp->timeouts.icmp = htonl (nm->timeouts.icmp); + + rmp->forwarding_enabled = nm->forwarding_enabled == 1; + // consider how to split functionality between subplugins + rmp->ipfix_logging_enabled = nat_ipfix_logging_enabled (); + + if (rc->static_mapping_only) + rmp->flags |= NAT44_EI_STATIC_MAPPING_ONLY; + if (rc->connection_tracking) + rmp->flags |= NAT44_EI_CONNECTION_TRACKING; + if (rc->out2in_dpo) + rmp->flags |= NAT44_EI_OUT2IN_DPO; + })); +} + +static void +vl_api_nat44_ei_set_workers_t_handler (vl_api_nat44_ei_set_workers_t *mp) +{ + nat44_ei_main_t *nm = &nat44_ei_main; + vl_api_nat44_ei_set_workers_reply_t *rmp; + int rv = 0; + uword *bitmap = 0; + u64 mask; + + mask = clib_net_to_host_u64 (mp->worker_mask); + + if (nm->num_workers < 2) + { + rv = VNET_API_ERROR_FEATURE_DISABLED; + goto send_reply; + } + + bitmap = clib_bitmap_set_multiple (bitmap, 0, mask, BITS (mask)); + rv = nat44_ei_set_workers (bitmap); + clib_bitmap_free (bitmap); + +send_reply: + REPLY_MACRO (VL_API_NAT44_EI_SET_WORKERS_REPLY); +} + +static void +send_nat_worker_details (u32 worker_index, vl_api_registration_t *reg, + u32 context) +{ + vl_api_nat44_ei_worker_details_t *rmp; + nat44_ei_main_t *nm = &nat44_ei_main; + vlib_worker_thread_t *w = + vlib_worker_threads + worker_index + nm->first_worker_index; + + rmp = vl_msg_api_alloc (sizeof (*rmp)); + clib_memset (rmp, 0, sizeof (*rmp)); + rmp->_vl_msg_id = ntohs (VL_API_NAT44_EI_WORKER_DETAILS + nm->msg_id_base); + rmp->context = context; + rmp->worker_index = htonl (worker_index); + rmp->lcore_id = htonl (w->cpu_id); + strncpy ((char *) rmp->name, (char *) w->name, ARRAY_LEN (rmp->name) - 1); + + vl_api_send_msg (reg, (u8 *) rmp); +} + +static void +vl_api_nat44_ei_worker_dump_t_handler (vl_api_nat44_ei_worker_dump_t *mp) +{ + vl_api_registration_t *reg; + nat44_ei_main_t *nm = &nat44_ei_main; + u32 *worker_index; + + reg = vl_api_client_index_to_registration (mp->client_index); + if (!reg) + return; + + vec_foreach (worker_index, nm->workers) + { + send_nat_worker_details (*worker_index, reg, mp->context); + } +} + +static void +vl_api_nat44_ei_set_log_level_t_handler (vl_api_nat44_ei_set_log_level_t *mp) +{ + nat44_ei_main_t *nm = &nat44_ei_main; + vl_api_nat44_ei_set_log_level_reply_t *rmp; + int rv = 0; + + if (nm->log_level > NAT_LOG_DEBUG) + rv = VNET_API_ERROR_UNSUPPORTED; + else + nm->log_level = mp->log_level; + + REPLY_MACRO (VL_API_NAT44_EI_SET_LOG_LEVEL_REPLY); +} + +static void +vl_api_nat44_ei_plugin_enable_disable_t_handler ( + vl_api_nat44_ei_plugin_enable_disable_t *mp) +{ + nat44_ei_main_t *nm = &nat44_ei_main; + nat44_ei_config_t c = { 0 }; + vl_api_nat44_ei_plugin_enable_disable_reply_t *rmp; + int rv = 0; + + if (mp->enable) + { + c.static_mapping_only = mp->flags & NAT44_EI_STATIC_MAPPING_ONLY; + c.connection_tracking = mp->flags & NAT44_EI_CONNECTION_TRACKING; + c.out2in_dpo = mp->flags & NAT44_EI_OUT2IN_DPO; + + c.inside_vrf = ntohl (mp->inside_vrf); + c.outside_vrf = ntohl (mp->outside_vrf); + + c.users = ntohl (mp->users); + + c.sessions = ntohl (mp->sessions); + + c.user_sessions = ntohl (mp->user_sessions); + + rv = nat44_ei_plugin_enable (c); + } + else + rv = nat44_ei_plugin_disable (); + + REPLY_MACRO (VL_API_NAT44_EI_PLUGIN_ENABLE_DISABLE_REPLY); +} + +static void +vl_api_nat44_ei_ipfix_enable_disable_t_handler ( + vl_api_nat44_ei_ipfix_enable_disable_t *mp) +{ + nat44_ei_main_t *nm = &nat44_ei_main; + vl_api_nat44_ei_ipfix_enable_disable_reply_t *rmp; + int rv = 0; + + rv = nat_ipfix_logging_enable_disable (mp->enable, + clib_host_to_net_u32 (mp->domain_id), + clib_host_to_net_u16 (mp->src_port)); + + REPLY_MACRO (VL_API_NAT44_EI_IPFIX_ENABLE_DISABLE_REPLY); +} + +static void +vl_api_nat44_ei_set_timeouts_t_handler (vl_api_nat44_ei_set_timeouts_t *mp) +{ + nat44_ei_main_t *nm = &nat44_ei_main; + vl_api_nat44_ei_set_timeouts_reply_t *rmp; + int rv = 0; + + nm->timeouts.udp = ntohl (mp->udp); + nm->timeouts.tcp.established = ntohl (mp->tcp_established); + nm->timeouts.tcp.transitory = ntohl (mp->tcp_transitory); + nm->timeouts.icmp = ntohl (mp->icmp); + + REPLY_MACRO (VL_API_NAT44_EI_SET_TIMEOUTS_REPLY); +} + +static void +vl_api_nat44_ei_set_addr_and_port_alloc_alg_t_handler ( + vl_api_nat44_ei_set_addr_and_port_alloc_alg_t *mp) +{ + nat44_ei_main_t *nm = &nat44_ei_main; + vl_api_nat44_ei_set_addr_and_port_alloc_alg_reply_t *rmp; + int rv = 0; + u16 port_start, port_end; + + switch (mp->alg) + { + case NAT44_EI_ADDR_AND_PORT_ALLOC_ALG_DEFAULT: + nat44_ei_set_alloc_default (); + break; + case NAT44_EI_ADDR_AND_PORT_ALLOC_ALG_MAPE: + nat44_ei_set_alloc_mape (ntohs (mp->psid), mp->psid_offset, + mp->psid_length); + break; + case NAT44_EI_ADDR_AND_PORT_ALLOC_ALG_RANGE: + port_start = ntohs (mp->start_port); + port_end = ntohs (mp->end_port); + if (port_end <= port_start) + { + rv = VNET_API_ERROR_INVALID_VALUE; + goto send_reply; + } + nat44_ei_set_alloc_range (port_start, port_end); + break; + default: + rv = VNET_API_ERROR_INVALID_VALUE; + break; + } + +send_reply: + REPLY_MACRO (VL_API_NAT44_EI_SET_ADDR_AND_PORT_ALLOC_ALG_REPLY); +} + +static void +vl_api_nat44_ei_get_addr_and_port_alloc_alg_t_handler ( + vl_api_nat44_ei_get_addr_and_port_alloc_alg_t *mp) +{ + nat44_ei_main_t *nm = &nat44_ei_main; + vl_api_nat44_ei_get_addr_and_port_alloc_alg_reply_t *rmp; + int rv = 0; + + REPLY_MACRO2 (VL_API_NAT44_EI_GET_ADDR_AND_PORT_ALLOC_ALG_REPLY, ({ + rmp->alg = nm->addr_and_port_alloc_alg; + rmp->psid_offset = nm->psid_offset; + rmp->psid_length = nm->psid_length; + rmp->psid = htons (nm->psid); + rmp->start_port = htons (nm->start_port); + rmp->end_port = htons (nm->end_port); + })) +} + +static void +vl_api_nat44_ei_set_mss_clamping_t_handler ( + vl_api_nat44_ei_set_mss_clamping_t *mp) +{ + nat44_ei_main_t *nm = &nat44_ei_main; + vl_api_nat44_ei_set_mss_clamping_reply_t *rmp; + int rv = 0; + + if (mp->enable) + nm->mss_clamping = ntohs (mp->mss_value); + else + nm->mss_clamping = 0; + + REPLY_MACRO (VL_API_NAT44_EI_SET_MSS_CLAMPING_REPLY); +} + +static void +vl_api_nat44_ei_get_mss_clamping_t_handler ( + vl_api_nat44_ei_get_mss_clamping_t *mp) +{ + nat44_ei_main_t *nm = &nat44_ei_main; + vl_api_nat44_ei_get_mss_clamping_reply_t *rmp; + int rv = 0; + + REPLY_MACRO2 (VL_API_NAT44_EI_GET_MSS_CLAMPING_REPLY, ({ + rmp->enable = nm->mss_clamping ? 1 : 0; + rmp->mss_value = htons (nm->mss_clamping); + })) +} + +static void +vl_api_nat44_ei_ha_set_listener_t_handler ( + vl_api_nat44_ei_ha_set_listener_t *mp) +{ + nat44_ei_main_t *nm = &nat44_ei_main; + vl_api_nat44_ei_ha_set_listener_reply_t *rmp; + ip4_address_t addr; + int rv; + + memcpy (&addr, &mp->ip_address, sizeof (addr)); + rv = nat_ha_set_listener (&addr, clib_net_to_host_u16 (mp->port), + clib_net_to_host_u32 (mp->path_mtu)); + + REPLY_MACRO (VL_API_NAT44_EI_HA_SET_LISTENER_REPLY); +} + +static void +vl_api_nat44_ei_ha_get_listener_t_handler ( + vl_api_nat44_ei_ha_get_listener_t *mp) +{ + nat44_ei_main_t *nm = &nat44_ei_main; + vl_api_nat44_ei_ha_get_listener_reply_t *rmp; + int rv = 0; + ip4_address_t addr; + u16 port; + u32 path_mtu; + + nat_ha_get_listener (&addr, &port, &path_mtu); + + REPLY_MACRO2 (VL_API_NAT44_EI_HA_GET_LISTENER_REPLY, ({ + clib_memcpy (rmp->ip_address, &addr, sizeof (ip4_address_t)); + rmp->port = clib_host_to_net_u16 (port); + rmp->path_mtu = clib_host_to_net_u32 (path_mtu); + })) +} + +static void +vl_api_nat44_ei_ha_set_failover_t_handler ( + vl_api_nat44_ei_ha_set_failover_t *mp) +{ + nat44_ei_main_t *nm = &nat44_ei_main; + vl_api_nat44_ei_ha_set_failover_reply_t *rmp; + ip4_address_t addr; + int rv; + + memcpy (&addr, &mp->ip_address, sizeof (addr)); + rv = + nat_ha_set_failover (&addr, clib_net_to_host_u16 (mp->port), + clib_net_to_host_u32 (mp->session_refresh_interval)); + + REPLY_MACRO (VL_API_NAT44_EI_HA_SET_FAILOVER_REPLY); +} + +static void +vl_api_nat44_ei_ha_get_failover_t_handler ( + vl_api_nat44_ei_ha_get_failover_t *mp) +{ + nat44_ei_main_t *nm = &nat44_ei_main; + vl_api_nat44_ei_ha_get_failover_reply_t *rmp; + int rv = 0; + ip4_address_t addr; + u16 port; + u32 session_refresh_interval; + + nat_ha_get_failover (&addr, &port, &session_refresh_interval); + + REPLY_MACRO2 (VL_API_NAT44_EI_HA_GET_FAILOVER_REPLY, ({ + clib_memcpy (rmp->ip_address, &addr, sizeof (ip4_address_t)); + rmp->port = clib_host_to_net_u16 (port); + rmp->session_refresh_interval = + clib_host_to_net_u32 (session_refresh_interval); + })) +} + +static void +vl_api_nat44_ei_ha_flush_t_handler (vl_api_nat44_ei_ha_flush_t *mp) +{ + nat44_ei_main_t *nm = &nat44_ei_main; + vl_api_nat44_ei_ha_flush_reply_t *rmp; + int rv = 0; + + nat_ha_flush (0); + + REPLY_MACRO (VL_API_NAT44_EI_HA_FLUSH_REPLY); +} + +static void +nat_ha_resync_completed_event_cb (u32 client_index, u32 pid, u32 missed_count) +{ + nat44_ei_main_t *nm = &nat44_ei_main; + vl_api_registration_t *reg; + vl_api_nat44_ei_ha_resync_completed_event_t *mp; + + reg = vl_api_client_index_to_registration (client_index); + if (!reg) + return; + + mp = vl_msg_api_alloc (sizeof (*mp)); + clib_memset (mp, 0, sizeof (*mp)); + mp->client_index = client_index; + mp->pid = pid; + mp->missed_count = clib_host_to_net_u32 (missed_count); + mp->_vl_msg_id = + ntohs (VL_API_NAT44_EI_HA_RESYNC_COMPLETED_EVENT + nm->msg_id_base); + + vl_api_send_msg (reg, (u8 *) mp); +} + +static void +vl_api_nat44_ei_ha_resync_t_handler (vl_api_nat44_ei_ha_resync_t *mp) +{ + nat44_ei_main_t *nm = &nat44_ei_main; + vl_api_nat44_ei_ha_resync_reply_t *rmp; + int rv; + + rv = nat_ha_resync ( + mp->client_index, mp->pid, + mp->want_resync_event ? nat_ha_resync_completed_event_cb : NULL); + + REPLY_MACRO (VL_API_NAT44_EI_HA_RESYNC_REPLY); +} + +static void +vl_api_nat44_ei_del_user_t_handler (vl_api_nat44_ei_del_user_t *mp) +{ + nat44_ei_main_t *nm = &nat44_ei_main; + vl_api_nat44_ei_del_user_reply_t *rmp; + ip4_address_t addr; + int rv; + memcpy (&addr.as_u8, mp->ip_address, 4); + rv = nat44_ei_user_del (&addr, ntohl (mp->fib_index)); + REPLY_MACRO (VL_API_NAT44_EI_DEL_USER_REPLY); +} + +static void +vl_api_nat44_ei_add_del_address_range_t_handler ( + vl_api_nat44_ei_add_del_address_range_t *mp) +{ + nat44_ei_main_t *nm = &nat44_ei_main; + vl_api_nat44_ei_add_del_address_range_reply_t *rmp; + ip4_address_t this_addr; + u8 is_add; + u32 start_host_order, end_host_order; + u32 vrf_id; + int i, count; + int rv = 0; + u32 *tmp; + + if (nm->static_mapping_only) + { + rv = VNET_API_ERROR_FEATURE_DISABLED; + goto send_reply; + } + + is_add = mp->is_add; + + tmp = (u32 *) mp->first_ip_address; + start_host_order = clib_host_to_net_u32 (tmp[0]); + tmp = (u32 *) mp->last_ip_address; + end_host_order = clib_host_to_net_u32 (tmp[0]); + + count = (end_host_order - start_host_order) + 1; + + vrf_id = clib_host_to_net_u32 (mp->vrf_id); + + if (count > 1024) + nat44_ei_log_info ("%U - %U, %d addresses...", format_ip4_address, + mp->first_ip_address, format_ip4_address, + mp->last_ip_address, count); + + memcpy (&this_addr.as_u8, mp->first_ip_address, 4); + + for (i = 0; i < count; i++) + { + if (is_add) + rv = nat44_ei_add_address (nm, &this_addr, vrf_id); + else + rv = nat44_ei_del_address (nm, this_addr, 0); + + if (rv) + goto send_reply; + + if (nm->out2in_dpo) + nat44_ei_add_del_address_dpo (this_addr, is_add); + + increment_v4_address (&this_addr); + } + +send_reply: + REPLY_MACRO (VL_API_NAT44_EI_ADD_DEL_ADDRESS_RANGE_REPLY); +} + +static void +send_nat44_ei_address_details (nat44_ei_address_t *a, + vl_api_registration_t *reg, u32 context) +{ + vl_api_nat44_ei_address_details_t *rmp; + nat44_ei_main_t *nm = &nat44_ei_main; + + rmp = vl_msg_api_alloc (sizeof (*rmp)); + clib_memset (rmp, 0, sizeof (*rmp)); + rmp->_vl_msg_id = ntohs (VL_API_NAT44_EI_ADDRESS_DETAILS + nm->msg_id_base); + clib_memcpy (rmp->ip_address, &(a->addr), 4); + if (a->fib_index != ~0) + { + fib_table_t *fib = fib_table_get (a->fib_index, FIB_PROTOCOL_IP4); + rmp->vrf_id = ntohl (fib->ft_table_id); + } + else + rmp->vrf_id = ~0; + rmp->context = context; + + vl_api_send_msg (reg, (u8 *) rmp); +} + +static void +vl_api_nat44_ei_address_dump_t_handler (vl_api_nat44_ei_address_dump_t *mp) +{ + vl_api_registration_t *reg; + nat44_ei_main_t *nm = &nat44_ei_main; + nat44_ei_address_t *a; + + reg = vl_api_client_index_to_registration (mp->client_index); + if (!reg) + return; + + vec_foreach (a, nm->addresses) + { + send_nat44_ei_address_details (a, reg, mp->context); + } +} + +static void +vl_api_nat44_ei_interface_add_del_feature_t_handler ( + vl_api_nat44_ei_interface_add_del_feature_t *mp) +{ + nat44_ei_main_t *nm = &nat44_ei_main; + vl_api_nat44_ei_interface_add_del_feature_reply_t *rmp; + u32 sw_if_index = ntohl (mp->sw_if_index); + u8 is_del; + int rv = 0; + + is_del = !mp->is_add; + + VALIDATE_SW_IF_INDEX (mp); + + rv = nat44_ei_interface_add_del (sw_if_index, mp->flags & NAT44_EI_IF_INSIDE, + is_del); + + BAD_SW_IF_INDEX_LABEL; + + REPLY_MACRO (VL_API_NAT44_EI_INTERFACE_ADD_DEL_FEATURE_REPLY); +} + +static void +send_nat44_ei_interface_details (nat44_ei_interface_t *i, + vl_api_registration_t *reg, u32 context) +{ + vl_api_nat44_ei_interface_details_t *rmp; + nat44_ei_main_t *nm = &nat44_ei_main; + + rmp = vl_msg_api_alloc (sizeof (*rmp)); + clib_memset (rmp, 0, sizeof (*rmp)); + rmp->_vl_msg_id = + ntohs (VL_API_NAT44_EI_INTERFACE_DETAILS + nm->msg_id_base); + rmp->sw_if_index = ntohl (i->sw_if_index); + + if (nat44_ei_interface_is_inside (i)) + rmp->flags |= NAT44_EI_IF_INSIDE; + if (nat44_ei_interface_is_outside (i)) + rmp->flags |= NAT44_EI_IF_OUTSIDE; + + rmp->context = context; + + vl_api_send_msg (reg, (u8 *) rmp); +} + +static void +vl_api_nat44_ei_interface_dump_t_handler (vl_api_nat44_ei_interface_dump_t *mp) +{ + vl_api_registration_t *reg; + nat44_ei_main_t *nm = &nat44_ei_main; + nat44_ei_interface_t *i; + + reg = vl_api_client_index_to_registration (mp->client_index); + if (!reg) + return; + + pool_foreach (i, nm->interfaces) + { + send_nat44_ei_interface_details (i, reg, mp->context); + } +} + +static void +vl_api_nat44_ei_interface_add_del_output_feature_t_handler ( + vl_api_nat44_ei_interface_add_del_output_feature_t *mp) +{ + nat44_ei_main_t *nm = &nat44_ei_main; + vl_api_nat44_ei_interface_add_del_output_feature_reply_t *rmp; + u32 sw_if_index = ntohl (mp->sw_if_index); + int rv = 0; + + VALIDATE_SW_IF_INDEX (mp); + + rv = nat44_ei_interface_add_del_output_feature ( + sw_if_index, mp->flags & NAT44_EI_IF_INSIDE, !mp->is_add); + + BAD_SW_IF_INDEX_LABEL; + REPLY_MACRO (VL_API_NAT44_EI_INTERFACE_ADD_DEL_OUTPUT_FEATURE_REPLY); +} + +static void +send_nat44_ei_interface_output_feature_details (nat44_ei_interface_t *i, + vl_api_registration_t *reg, + u32 context) +{ + vl_api_nat44_ei_interface_output_feature_details_t *rmp; + nat44_ei_main_t *nm = &nat44_ei_main; + + rmp = vl_msg_api_alloc (sizeof (*rmp)); + clib_memset (rmp, 0, sizeof (*rmp)); + rmp->_vl_msg_id = + ntohs (VL_API_NAT44_EI_INTERFACE_OUTPUT_FEATURE_DETAILS + nm->msg_id_base); + rmp->sw_if_index = ntohl (i->sw_if_index); + rmp->context = context; + + if (nat44_ei_interface_is_inside (i)) + rmp->flags |= NAT44_EI_IF_INSIDE; + + vl_api_send_msg (reg, (u8 *) rmp); +} + +static void +vl_api_nat44_ei_interface_output_feature_dump_t_handler ( + vl_api_nat44_ei_interface_output_feature_dump_t *mp) +{ + vl_api_registration_t *reg; + nat44_ei_main_t *nm = &nat44_ei_main; + nat44_ei_interface_t *i; + + reg = vl_api_client_index_to_registration (mp->client_index); + if (!reg) + return; + + pool_foreach (i, nm->output_feature_interfaces) + { + send_nat44_ei_interface_output_feature_details (i, reg, mp->context); + } +} + +static void +vl_api_nat44_ei_add_del_static_mapping_t_handler ( + vl_api_nat44_ei_add_del_static_mapping_t *mp) +{ + nat44_ei_main_t *nm = &nat44_ei_main; + vl_api_nat44_ei_add_del_static_mapping_reply_t *rmp; + ip4_address_t local_addr, external_addr; + u16 local_port = 0, external_port = 0; + u32 vrf_id, external_sw_if_index; + int rv = 0; + nat_protocol_t proto; + u8 *tag = 0; + + memcpy (&local_addr.as_u8, mp->local_ip_address, 4); + memcpy (&external_addr.as_u8, mp->external_ip_address, 4); + + if (!(mp->flags & NAT44_EI_ADDR_ONLY_MAPPING)) + { + local_port = mp->local_port; + external_port = mp->external_port; + } + + vrf_id = clib_net_to_host_u32 (mp->vrf_id); + external_sw_if_index = clib_net_to_host_u32 (mp->external_sw_if_index); + proto = ip_proto_to_nat_proto (mp->protocol); + + mp->tag[sizeof (mp->tag) - 1] = 0; + tag = format (0, "%s", mp->tag); + vec_terminate_c_string (tag); + + rv = nat44_ei_add_del_static_mapping ( + local_addr, external_addr, local_port, external_port, proto, + external_sw_if_index, vrf_id, mp->flags & NAT44_EI_ADDR_ONLY_MAPPING, 0, + tag, mp->is_add); + + vec_free (tag); + + REPLY_MACRO (VL_API_NAT44_EI_ADD_DEL_STATIC_MAPPING_REPLY); +} + +static void +send_nat44_ei_static_mapping_details (nat44_ei_static_mapping_t *m, + vl_api_registration_t *reg, u32 context) +{ + vl_api_nat44_ei_static_mapping_details_t *rmp; + nat44_ei_main_t *nm = &nat44_ei_main; + u32 len = sizeof (*rmp); + + rmp = vl_msg_api_alloc (len); + clib_memset (rmp, 0, len); + rmp->_vl_msg_id = + ntohs (VL_API_NAT44_EI_STATIC_MAPPING_DETAILS + nm->msg_id_base); + + clib_memcpy (rmp->local_ip_address, &(m->local_addr), 4); + clib_memcpy (rmp->external_ip_address, &(m->external_addr), 4); + rmp->external_sw_if_index = ~0; + rmp->vrf_id = htonl (m->vrf_id); + rmp->context = context; + + if (nat44_ei_is_addr_only_static_mapping (m)) + { + rmp->flags |= NAT44_EI_ADDR_ONLY_MAPPING; + } + else + { + rmp->protocol = nat_proto_to_ip_proto (m->proto); + rmp->external_port = m->external_port; + rmp->local_port = m->local_port; + } + + if (m->tag) + strncpy ((char *) rmp->tag, (char *) m->tag, vec_len (m->tag)); + + vl_api_send_msg (reg, (u8 *) rmp); +} + +static void +send_nat44_ei_static_map_resolve_details (nat44_ei_static_map_resolve_t *m, + vl_api_registration_t *reg, + u32 context) +{ + vl_api_nat44_ei_static_mapping_details_t *rmp; + nat44_ei_main_t *nm = &nat44_ei_main; + + rmp = vl_msg_api_alloc (sizeof (*rmp)); + clib_memset (rmp, 0, sizeof (*rmp)); + rmp->_vl_msg_id = + ntohs (VL_API_NAT44_EI_STATIC_MAPPING_DETAILS + nm->msg_id_base); + clib_memcpy (rmp->local_ip_address, &(m->l_addr), 4); + rmp->external_sw_if_index = htonl (m->sw_if_index); + rmp->vrf_id = htonl (m->vrf_id); + rmp->context = context; + + if (m->addr_only) + { + rmp->flags |= NAT44_EI_ADDR_ONLY_MAPPING; + } + else + { + rmp->protocol = nat_proto_to_ip_proto (m->proto); + rmp->external_port = m->e_port; + rmp->local_port = m->l_port; + } + if (m->tag) + strncpy ((char *) rmp->tag, (char *) m->tag, vec_len (m->tag)); + + vl_api_send_msg (reg, (u8 *) rmp); +} + +static void +vl_api_nat44_ei_static_mapping_dump_t_handler ( + vl_api_nat44_ei_static_mapping_dump_t *mp) +{ + vl_api_registration_t *reg; + nat44_ei_main_t *nm = &nat44_ei_main; + nat44_ei_static_mapping_t *m; + nat44_ei_static_map_resolve_t *rp; + int j; + + reg = vl_api_client_index_to_registration (mp->client_index); + if (!reg) + return; + + pool_foreach (m, nm->static_mappings) + { + if (!nat44_ei_is_identity_static_mapping (m)) + send_nat44_ei_static_mapping_details (m, reg, mp->context); + } + + for (j = 0; j < vec_len (nm->to_resolve); j++) + { + rp = nm->to_resolve + j; + if (!rp->identity_nat) + send_nat44_ei_static_map_resolve_details (rp, reg, mp->context); + } +} + +static void +vl_api_nat44_ei_add_del_identity_mapping_t_handler ( + vl_api_nat44_ei_add_del_identity_mapping_t *mp) +{ + nat44_ei_main_t *nm = &nat44_ei_main; + vl_api_nat44_ei_add_del_identity_mapping_reply_t *rmp; + ip4_address_t addr; + u16 port = 0; + u32 vrf_id, sw_if_index; + int rv = 0; + nat_protocol_t proto = NAT_PROTOCOL_OTHER; + u8 *tag = 0; + + if (!(mp->flags & NAT44_EI_ADDR_ONLY_MAPPING)) + { + port = mp->port; + proto = ip_proto_to_nat_proto (mp->protocol); + } + vrf_id = clib_net_to_host_u32 (mp->vrf_id); + sw_if_index = clib_net_to_host_u32 (mp->sw_if_index); + if (sw_if_index != ~0) + addr.as_u32 = 0; + else + memcpy (&addr.as_u8, mp->ip_address, 4); + mp->tag[sizeof (mp->tag) - 1] = 0; + tag = format (0, "%s", mp->tag); + vec_terminate_c_string (tag); + + rv = nat44_ei_add_del_static_mapping ( + addr, addr, port, port, proto, sw_if_index, vrf_id, + mp->flags & NAT44_EI_ADDR_ONLY_MAPPING, 1, tag, mp->is_add); + + vec_free (tag); + + REPLY_MACRO (VL_API_NAT44_EI_ADD_DEL_IDENTITY_MAPPING_REPLY); +} + +static void +send_nat44_ei_identity_mapping_details (nat44_ei_static_mapping_t *m, + int index, vl_api_registration_t *reg, + u32 context) +{ + vl_api_nat44_ei_identity_mapping_details_t *rmp; + nat44_ei_main_t *nm = &nat44_ei_main; + nat44_ei_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)); + rmp->_vl_msg_id = + ntohs (VL_API_NAT44_EI_IDENTITY_MAPPING_DETAILS + nm->msg_id_base); + + if (nat44_ei_is_addr_only_static_mapping (m)) + rmp->flags |= NAT44_EI_ADDR_ONLY_MAPPING; + + clib_memcpy (rmp->ip_address, &(m->local_addr), 4); + rmp->port = m->local_port; + rmp->sw_if_index = ~0; + rmp->vrf_id = htonl (local->vrf_id); + rmp->protocol = nat_proto_to_ip_proto (m->proto); + rmp->context = context; + if (m->tag) + strncpy ((char *) rmp->tag, (char *) m->tag, vec_len (m->tag)); + + vl_api_send_msg (reg, (u8 *) rmp); +} + +static void +send_nat44_ei_identity_map_resolve_details (nat44_ei_static_map_resolve_t *m, + vl_api_registration_t *reg, + u32 context) +{ + vl_api_nat44_ei_identity_mapping_details_t *rmp; + nat44_ei_main_t *nm = &nat44_ei_main; + rmp = vl_msg_api_alloc (sizeof (*rmp)); + clib_memset (rmp, 0, sizeof (*rmp)); + rmp->_vl_msg_id = + ntohs (VL_API_NAT44_EI_IDENTITY_MAPPING_DETAILS + nm->msg_id_base); + + if (m->addr_only) + rmp->flags = (vl_api_nat44_ei_config_flags_t) NAT44_EI_ADDR_ONLY_MAPPING; + + rmp->port = m->l_port; + rmp->sw_if_index = htonl (m->sw_if_index); + rmp->vrf_id = htonl (m->vrf_id); + rmp->protocol = nat_proto_to_ip_proto (m->proto); + rmp->context = context; + if (m->tag) + strncpy ((char *) rmp->tag, (char *) m->tag, vec_len (m->tag)); + + vl_api_send_msg (reg, (u8 *) rmp); +} + +static void +vl_api_nat44_ei_identity_mapping_dump_t_handler ( + vl_api_nat44_ei_identity_mapping_dump_t *mp) +{ + vl_api_registration_t *reg; + nat44_ei_main_t *nm = &nat44_ei_main; + nat44_ei_static_mapping_t *m; + nat44_ei_static_map_resolve_t *rp; + int j; + + reg = vl_api_client_index_to_registration (mp->client_index); + if (!reg) + return; + + pool_foreach (m, nm->static_mappings) + { + if (nat44_ei_is_identity_static_mapping (m)) + { + pool_foreach_index (j, m->locals) + { + send_nat44_ei_identity_mapping_details (m, j, reg, mp->context); + } + } + } + + for (j = 0; j < vec_len (nm->to_resolve); j++) + { + rp = nm->to_resolve + j; + if (rp->identity_nat) + send_nat44_ei_identity_map_resolve_details (rp, reg, mp->context); + } +} + +static void +vl_api_nat44_ei_add_del_interface_addr_t_handler ( + vl_api_nat44_ei_add_del_interface_addr_t *mp) +{ + nat44_ei_main_t *nm = &nat44_ei_main; + vl_api_nat44_ei_add_del_interface_addr_reply_t *rmp; + u32 sw_if_index = ntohl (mp->sw_if_index); + int rv = 0; + u8 is_del; + + is_del = !mp->is_add; + + VALIDATE_SW_IF_INDEX (mp); + + rv = nat44_ei_add_interface_address (nm, sw_if_index, is_del); + + BAD_SW_IF_INDEX_LABEL; + REPLY_MACRO (VL_API_NAT44_EI_ADD_DEL_INTERFACE_ADDR_REPLY); +} + +static void +send_nat44_ei_interface_addr_details (u32 sw_if_index, + vl_api_registration_t *reg, u32 context) +{ + vl_api_nat44_ei_interface_addr_details_t *rmp; + nat44_ei_main_t *nm = &nat44_ei_main; + + rmp = vl_msg_api_alloc (sizeof (*rmp)); + clib_memset (rmp, 0, sizeof (*rmp)); + rmp->_vl_msg_id = + ntohs (VL_API_NAT44_EI_INTERFACE_ADDR_DETAILS + nm->msg_id_base); + rmp->sw_if_index = ntohl (sw_if_index); + rmp->context = context; + + vl_api_send_msg (reg, (u8 *) rmp); +} + +static void +vl_api_nat44_ei_interface_addr_dump_t_handler ( + vl_api_nat44_ei_interface_addr_dump_t *mp) +{ + vl_api_registration_t *reg; + nat44_ei_main_t *nm = &nat44_ei_main; + u32 *i; + + reg = vl_api_client_index_to_registration (mp->client_index); + if (!reg) + return; + + vec_foreach (i, nm->auto_add_sw_if_indices) + send_nat44_ei_interface_addr_details (*i, reg, mp->context); +} + +static void +send_nat44_ei_user_details (nat44_ei_user_t *u, vl_api_registration_t *reg, + u32 context) +{ + vl_api_nat44_ei_user_details_t *rmp; + nat44_ei_main_t *nm = &nat44_ei_main; + ip4_main_t *im = &ip4_main; + + rmp = vl_msg_api_alloc (sizeof (*rmp)); + clib_memset (rmp, 0, sizeof (*rmp)); + rmp->_vl_msg_id = ntohs (VL_API_NAT44_EI_USER_DETAILS + nm->msg_id_base); + + if (!pool_is_free_index (im->fibs, u->fib_index)) + { + fib_table_t *fib = fib_table_get (u->fib_index, FIB_PROTOCOL_IP4); + rmp->vrf_id = ntohl (fib->ft_table_id); + } + + clib_memcpy (rmp->ip_address, &(u->addr), 4); + rmp->nsessions = ntohl (u->nsessions); + rmp->nstaticsessions = ntohl (u->nstaticsessions); + rmp->context = context; + + vl_api_send_msg (reg, (u8 *) rmp); +} + +static void +vl_api_nat44_ei_user_dump_t_handler (vl_api_nat44_ei_user_dump_t *mp) +{ + vl_api_registration_t *reg; + nat44_ei_main_t *nm = &nat44_ei_main; + nat44_ei_main_per_thread_data_t *tnm; + nat44_ei_user_t *u; + + reg = vl_api_client_index_to_registration (mp->client_index); + if (!reg) + return; + + vec_foreach (tnm, nm->per_thread_data) + { + pool_foreach (u, tnm->users) + { + send_nat44_ei_user_details (u, reg, mp->context); + } + } +} + +static void +send_nat44_ei_user_session_details (nat44_ei_session_t *s, + vl_api_registration_t *reg, u32 context) +{ + vl_api_nat44_ei_user_session_details_t *rmp; + nat44_ei_main_t *nm = &nat44_ei_main; + + rmp = vl_msg_api_alloc (sizeof (*rmp)); + clib_memset (rmp, 0, sizeof (*rmp)); + rmp->_vl_msg_id = + ntohs (VL_API_NAT44_EI_USER_SESSION_DETAILS + nm->msg_id_base); + clib_memcpy (rmp->outside_ip_address, (&s->out2in.addr), 4); + clib_memcpy (rmp->inside_ip_address, (&s->in2out.addr), 4); + + if (nat44_ei_is_session_static (s)) + rmp->flags |= NAT44_EI_STATIC_MAPPING; + + rmp->last_heard = clib_host_to_net_u64 ((u64) s->last_heard); + rmp->total_bytes = clib_host_to_net_u64 (s->total_bytes); + rmp->total_pkts = ntohl (s->total_pkts); + rmp->context = context; + if (nat44_ei_is_unk_proto_session (s)) + { + rmp->outside_port = 0; + rmp->inside_port = 0; + rmp->protocol = ntohs (s->in2out.port); + } + else + { + rmp->outside_port = s->out2in.port; + rmp->inside_port = s->in2out.port; + rmp->protocol = ntohs (nat_proto_to_ip_proto (s->nat_proto)); + } + vl_api_send_msg (reg, (u8 *) rmp); +} + +static void +vl_api_nat44_ei_user_session_dump_t_handler ( + vl_api_nat44_ei_user_session_dump_t *mp) +{ + vl_api_registration_t *reg; + nat44_ei_main_t *nm = &nat44_ei_main; + nat44_ei_main_per_thread_data_t *tnm; + nat44_ei_session_t *s; + clib_bihash_kv_8_8_t key, value; + nat44_ei_user_key_t ukey; + nat44_ei_user_t *u; + u32 session_index, head_index, elt_index; + dlist_elt_t *head, *elt; + ip4_header_t ip; + + reg = vl_api_client_index_to_registration (mp->client_index); + if (!reg) + return; + + clib_memcpy (&ukey.addr, mp->ip_address, 4); + ip.src_address.as_u32 = ukey.addr.as_u32; + ukey.fib_index = fib_table_find (FIB_PROTOCOL_IP4, ntohl (mp->vrf_id)); + key.key = ukey.as_u64; + if (nm->num_workers > 1) + tnm = vec_elt_at_index (nm->per_thread_data, + nm->worker_in2out_cb (&ip, ukey.fib_index, 0)); + else + tnm = vec_elt_at_index (nm->per_thread_data, nm->num_workers); + + if (clib_bihash_search_8_8 (&tnm->user_hash, &key, &value)) + return; + u = pool_elt_at_index (tnm->users, value.value); + if (!u->nsessions && !u->nstaticsessions) + return; + + head_index = u->sessions_per_user_list_head_index; + head = pool_elt_at_index (tnm->list_pool, head_index); + elt_index = head->next; + elt = pool_elt_at_index (tnm->list_pool, elt_index); + session_index = elt->value; + while (session_index != ~0) + { + s = pool_elt_at_index (tnm->sessions, session_index); + + send_nat44_ei_user_session_details (s, reg, mp->context); + + elt_index = elt->next; + elt = pool_elt_at_index (tnm->list_pool, elt_index); + session_index = elt->value; + } +} + +static void +vl_api_nat44_ei_del_session_t_handler (vl_api_nat44_ei_del_session_t *mp) +{ + nat44_ei_main_t *nm = &nat44_ei_main; + vl_api_nat44_ei_del_session_reply_t *rmp; + ip4_address_t addr, eh_addr; + u16 port; + u32 vrf_id; + int rv = 0; + u8 is_in; + nat_protocol_t proto; + + memcpy (&addr.as_u8, mp->address, 4); + port = mp->port; + vrf_id = clib_net_to_host_u32 (mp->vrf_id); + proto = ip_proto_to_nat_proto (mp->protocol); + memcpy (&eh_addr.as_u8, mp->ext_host_address, 4); + + // is session inside ? + is_in = mp->flags & NAT44_EI_IF_INSIDE; + + rv = nat44_ei_del_session (nm, &addr, port, proto, vrf_id, is_in); + + REPLY_MACRO (VL_API_NAT44_EI_DEL_SESSION_REPLY); +} + +static void +vl_api_nat44_ei_forwarding_enable_disable_t_handler ( + vl_api_nat44_ei_forwarding_enable_disable_t *mp) +{ + nat44_ei_main_t *nm = &nat44_ei_main; + vl_api_nat44_ei_forwarding_enable_disable_reply_t *rmp; + int rv = 0; + u32 *ses_to_be_removed = 0, *ses_index; + nat44_ei_main_per_thread_data_t *tnm; + nat44_ei_session_t *s; + + nm->forwarding_enabled = mp->enable != 0; + + if (mp->enable == 0) + { + vec_foreach (tnm, nm->per_thread_data) + { + vec_foreach (ses_index, ses_to_be_removed) + { + s = pool_elt_at_index (tnm->sessions, ses_index[0]); + nat44_ei_free_session_data (nm, s, tnm - nm->per_thread_data, 0); + nat44_ei_delete_session (nm, s, tnm - nm->per_thread_data); + } + + vec_free (ses_to_be_removed); + } + } + + REPLY_MACRO (VL_API_NAT44_EI_FORWARDING_ENABLE_DISABLE_REPLY); +} + +static void +vl_api_nat44_ei_set_fq_options_t_handler (vl_api_nat44_ei_set_fq_options_t *mp) +{ + nat44_ei_main_t *nm = &nat44_ei_main; + vl_api_nat44_ei_set_fq_options_reply_t *rmp; + int rv = 0; + u32 frame_queue_nelts = ntohl (mp->frame_queue_nelts); + rv = nat44_ei_set_frame_queue_nelts (frame_queue_nelts); + REPLY_MACRO (VL_API_NAT44_EI_SET_FQ_OPTIONS_REPLY); +} + +static void +vl_api_nat44_ei_show_fq_options_t_handler ( + vl_api_nat44_ei_show_fq_options_t *mp) +{ + nat44_ei_main_t *nm = &nat44_ei_main; + vl_api_nat44_ei_show_fq_options_reply_t *rmp; + int rv = 0; + /* clang-format off */ + REPLY_MACRO2_ZERO (VL_API_NAT44_EI_SHOW_FQ_OPTIONS_REPLY, + ({ + rmp->frame_queue_nelts = htonl (nm->frame_queue_nelts); + })); + /* clang-format on */ +} + +/* API definitions */ +#include +#include + +/* Set up the API message handling tables */ +clib_error_t * +nat44_ei_api_hookup (vlib_main_t *vm) +{ + nat44_ei_main_t *nm = &nat44_ei_main; + nm->msg_id_base = setup_message_id_table (); + return 0; +} + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/src/plugins/nat/nat44-ei/nat44_ei_cli.c b/src/plugins/nat/nat44-ei/nat44_ei_cli.c new file mode 100644 index 00000000000..3aa3a2f0525 --- /dev/null +++ b/src/plugins/nat/nat44-ei/nat44_ei_cli.c @@ -0,0 +1,2025 @@ +/* + * Copyright (c) 2020 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include +#include +#include + +#include +#include + +u8 * +format_nat44_ei_session (u8 *s, va_list *args) +{ + nat44_ei_main_per_thread_data_t *tnm = + va_arg (*args, nat44_ei_main_per_thread_data_t *); + nat44_ei_session_t *sess = va_arg (*args, nat44_ei_session_t *); + + if (nat44_ei_is_unk_proto_session (sess)) + { + s = + format (s, " i2o %U proto %u fib %u\n", format_ip4_address, + &sess->in2out.addr, sess->in2out.port, sess->in2out.fib_index); + s = + format (s, " o2i %U proto %u fib %u\n", format_ip4_address, + &sess->out2in.addr, sess->out2in.port, sess->out2in.fib_index); + } + else + { + s = format (s, " i2o %U proto %U port %d fib %d\n", format_ip4_address, + &sess->in2out.addr, format_nat_protocol, sess->nat_proto, + clib_net_to_host_u16 (sess->in2out.port), + sess->in2out.fib_index); + s = format (s, " o2i %U proto %U port %d fib %d\n", format_ip4_address, + &sess->out2in.addr, format_nat_protocol, sess->nat_proto, + clib_net_to_host_u16 (sess->out2in.port), + sess->out2in.fib_index); + } + + s = format (s, " index %llu\n", sess - tnm->sessions); + s = format (s, " last heard %.2f\n", sess->last_heard); + s = format (s, " total pkts %d, total bytes %lld\n", sess->total_pkts, + sess->total_bytes); + if (nat44_ei_is_session_static (sess)) + s = format (s, " static translation\n"); + else + s = format (s, " dynamic translation\n"); + + return s; +} + +u8 * +format_nat44_ei_user (u8 *s, va_list *args) +{ + nat44_ei_main_per_thread_data_t *tnm = + va_arg (*args, nat44_ei_main_per_thread_data_t *); + nat44_ei_user_t *u = va_arg (*args, nat44_ei_user_t *); + int verbose = va_arg (*args, int); + dlist_elt_t *head, *elt; + u32 elt_index, head_index; + u32 session_index; + nat44_ei_session_t *sess; + + s = format (s, "%U: %d dynamic translations, %d static translations\n", + format_ip4_address, &u->addr, u->nsessions, u->nstaticsessions); + + if (verbose == 0) + return s; + + if (u->nsessions || u->nstaticsessions) + { + head_index = u->sessions_per_user_list_head_index; + head = pool_elt_at_index (tnm->list_pool, head_index); + + elt_index = head->next; + elt = pool_elt_at_index (tnm->list_pool, elt_index); + session_index = elt->value; + + while (session_index != ~0) + { + sess = pool_elt_at_index (tnm->sessions, session_index); + + s = format (s, " %U\n", format_nat44_ei_session, tnm, sess); + + elt_index = elt->next; + elt = pool_elt_at_index (tnm->list_pool, elt_index); + session_index = elt->value; + } + } + + return s; +} + +u8 * +format_nat44_ei_static_mapping (u8 *s, va_list *args) +{ + nat44_ei_static_mapping_t *m = va_arg (*args, nat44_ei_static_mapping_t *); + nat44_ei_lb_addr_port_t *local; + + if (nat44_ei_is_identity_static_mapping (m)) + { + if (nat44_ei_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 %U:%d", format_nat_protocol, + m->proto, format_ip4_address, &m->local_addr, + clib_net_to_host_u16 (m->local_port)); + + pool_foreach (local, m->locals) + { + s = format (s, " vrf %d", local->vrf_id); + } + + return s; + } + + if (nat44_ei_is_addr_only_static_mapping (m)) + { + s = format (s, "local %U external %U vrf %d", format_ip4_address, + &m->local_addr, format_ip4_address, &m->external_addr, + m->vrf_id); + } + else + { + s = format (s, "%U local %U:%d external %U:%d vrf %d", + format_nat_protocol, m->proto, format_ip4_address, + &m->local_addr, clib_net_to_host_u16 (m->local_port), + format_ip4_address, &m->external_addr, + clib_net_to_host_u16 (m->external_port), m->vrf_id); + } + return s; +} + +u8 * +format_nat44_ei_static_map_to_resolve (u8 *s, va_list *args) +{ + nat44_ei_static_map_resolve_t *m = + va_arg (*args, nat44_ei_static_map_resolve_t *); + vnet_main_t *vnm = vnet_get_main (); + + if (m->addr_only) + s = + format (s, "local %U external %U vrf %d", format_ip4_address, &m->l_addr, + format_vnet_sw_if_index_name, vnm, m->sw_if_index, m->vrf_id); + else + s = format (s, "%U local %U:%d external %U:%d vrf %d", format_nat_protocol, + m->proto, format_ip4_address, &m->l_addr, + clib_net_to_host_u16 (m->l_port), format_vnet_sw_if_index_name, + vnm, m->sw_if_index, clib_net_to_host_u16 (m->e_port), + m->vrf_id); + + return s; +} + +static clib_error_t * +nat44_ei_enable_command_fn (vlib_main_t *vm, unformat_input_t *input, + vlib_cli_command_t *cmd) +{ + nat44_ei_main_t *nm = &nat44_ei_main; + unformat_input_t _line_input, *line_input = &_line_input; + clib_error_t *error = 0; + + nat44_ei_config_t c = { 0 }; + u8 mode_set = 0; + + if (nm->enabled) + return clib_error_return (0, "nat44 ei already enabled"); + + /* Get a line of input. */ + if (!unformat_user (input, unformat_line_input, line_input)) + { + if (nat44_ei_plugin_enable (c) != 0) + return clib_error_return (0, "nat44 ei enable failed"); + return 0; + } + + while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT) + { + if (!mode_set && unformat (line_input, "static-mapping-only")) + { + mode_set = 1; + c.static_mapping_only = 1; + if (unformat (line_input, "connection-tracking")) + { + c.connection_tracking = 1; + } + } + else if (!mode_set && unformat (line_input, "out2in-dpo")) + { + mode_set = 1; + c.out2in_dpo = 1; + } + else if (unformat (line_input, "inside-vrf %u", &c.inside_vrf)) + ; + else if (unformat (line_input, "outside-vrf %u", &c.outside_vrf)) + ; + else if (unformat (line_input, "users %u", &c.users)) + ; + else if (unformat (line_input, "sessions %u", &c.sessions)) + ; + else if (unformat (line_input, "user-sessions %u", &c.user_sessions)) + ; + else + { + error = clib_error_return (0, "unknown input '%U'", + format_unformat_error, line_input); + goto done; + } + } + + if (!c.sessions) + { + error = clib_error_return (0, "number of sessions is required"); + goto done; + } + + if (nat44_ei_plugin_enable (c) != 0) + error = clib_error_return (0, "nat44 ei enable failed"); +done: + unformat_free (line_input); + return error; +} + +static clib_error_t * +nat44_ei_disable_command_fn (vlib_main_t *vm, unformat_input_t *input, + vlib_cli_command_t *cmd) +{ + nat44_ei_main_t *nm = &nat44_ei_main; + clib_error_t *error = 0; + + if (!nm->enabled) + return clib_error_return (0, "nat44 ei already disabled"); + + if (nat44_ei_plugin_disable () != 0) + error = clib_error_return (0, "nat44 ei disable failed"); + + return error; +} + +static clib_error_t * +set_workers_command_fn (vlib_main_t *vm, unformat_input_t *input, + vlib_cli_command_t *cmd) +{ + unformat_input_t _line_input, *line_input = &_line_input; + uword *bitmap = 0; + int rv = 0; + clib_error_t *error = 0; + + /* 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, "%U", unformat_bitmap_list, &bitmap)) + ; + else + { + error = clib_error_return (0, "unknown input '%U'", + format_unformat_error, line_input); + goto done; + } + } + + if (bitmap == 0) + { + error = clib_error_return (0, "List of workers must be specified."); + goto done; + } + + rv = nat44_ei_set_workers (bitmap); + + clib_bitmap_free (bitmap); + + switch (rv) + { + case VNET_API_ERROR_INVALID_WORKER: + error = clib_error_return (0, "Invalid worker(s)."); + goto done; + case VNET_API_ERROR_FEATURE_DISABLED: + error = + clib_error_return (0, "Supported only if 2 or more workes available."); + goto done; + default: + break; + } + +done: + unformat_free (line_input); + + return error; +} + +static clib_error_t * +nat_show_workers_commnad_fn (vlib_main_t *vm, unformat_input_t *input, + vlib_cli_command_t *cmd) +{ + nat44_ei_main_t *nm = &nat44_ei_main; + u32 *worker; + + if (nm->num_workers > 1) + { + vlib_cli_output (vm, "%d workers", vec_len (nm->workers)); + vec_foreach (worker, nm->workers) + { + vlib_worker_thread_t *w = + vlib_worker_threads + *worker + nm->first_worker_index; + vlib_cli_output (vm, " %s", w->name); + } + } + + return 0; +} + +static clib_error_t * +nat44_ei_set_log_level_command_fn (vlib_main_t *vm, unformat_input_t *input, + vlib_cli_command_t *cmd) +{ + unformat_input_t _line_input, *line_input = &_line_input; + nat44_ei_main_t *nm = &nat44_ei_main; + u8 log_level = NAT_LOG_NONE; + clib_error_t *error = 0; + + /* Get a line of input. */ + if (!unformat_user (input, unformat_line_input, line_input)) + return 0; + + if (!unformat (line_input, "%d", &log_level)) + { + error = clib_error_return (0, "unknown input '%U'", + format_unformat_error, line_input); + goto done; + } + if (log_level > NAT_LOG_DEBUG) + { + error = clib_error_return (0, "unknown logging level '%d'", log_level); + goto done; + } + nm->log_level = log_level; + +done: + unformat_free (line_input); + + return error; +} + +static clib_error_t * +nat44_ei_ipfix_logging_enable_disable_command_fn (vlib_main_t *vm, + unformat_input_t *input, + vlib_cli_command_t *cmd) +{ + unformat_input_t _line_input, *line_input = &_line_input; + u32 domain_id = 0; + u32 src_port = 0; + u8 enable = 1; + int rv = 0; + clib_error_t *error = 0; + + /* Get a line of input. */ + if (!unformat_user (input, unformat_line_input, line_input)) + { + rv = + nat_ipfix_logging_enable_disable (enable, domain_id, (u16) src_port); + if (rv) + return clib_error_return (0, "ipfix logging enable failed"); + return 0; + } + + while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT) + { + if (unformat (line_input, "domain %d", &domain_id)) + ; + else if (unformat (line_input, "src-port %d", &src_port)) + ; + else if (unformat (line_input, "disable")) + enable = 0; + else + { + error = clib_error_return (0, "unknown input '%U'", + format_unformat_error, line_input); + goto done; + } + } + + rv = nat_ipfix_logging_enable_disable (enable, domain_id, (u16) src_port); + + if (rv) + { + error = clib_error_return (0, "ipfix logging enable failed"); + goto done; + } + +done: + unformat_free (line_input); + + return error; +} + +static clib_error_t * +nat44_ei_show_hash_command_fn (vlib_main_t *vm, unformat_input_t *input, + vlib_cli_command_t *cmd) +{ + nat44_ei_main_t *nm = &nat44_ei_main; + nat44_ei_main_per_thread_data_t *tnm; + int i; + int verbose = 0; + + if (unformat (input, "detail")) + verbose = 1; + else if (unformat (input, "verbose")) + verbose = 2; + + vlib_cli_output (vm, "%U", format_bihash_8_8, &nm->static_mapping_by_local, + verbose); + vlib_cli_output (vm, "%U", format_bihash_8_8, + &nm->static_mapping_by_external, verbose); + vec_foreach_index (i, nm->per_thread_data) + { + tnm = vec_elt_at_index (nm->per_thread_data, i); + vlib_cli_output (vm, "-------- thread %d %s --------\n", i, + vlib_worker_threads[i].name); + + vlib_cli_output (vm, "%U", format_bihash_8_8, &nm->in2out, verbose); + vlib_cli_output (vm, "%U", format_bihash_8_8, &nm->out2in, verbose); + vlib_cli_output (vm, "%U", format_bihash_8_8, &tnm->user_hash, verbose); + } + + vlib_cli_output (vm, "-------- hash table parameters --------\n"); + vlib_cli_output (vm, "translation buckets: %u", nm->translation_buckets); + vlib_cli_output (vm, "user buckets: %u", nm->user_buckets); + return 0; +} + +static clib_error_t * +nat44_ei_set_alloc_addr_and_port_alg_command_fn (vlib_main_t *vm, + unformat_input_t *input, + vlib_cli_command_t *cmd) +{ + unformat_input_t _line_input, *line_input = &_line_input; + clib_error_t *error = 0; + u32 psid, psid_offset, psid_length, port_start, port_end; + + /* 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, "default")) + nat44_ei_set_alloc_default (); + else if (unformat (line_input, + "map-e psid %d psid-offset %d psid-len %d", &psid, + &psid_offset, &psid_length)) + nat44_ei_set_alloc_mape ((u16) psid, (u16) psid_offset, + (u16) psid_length); + else if (unformat (line_input, "port-range %d - %d", &port_start, + &port_end)) + { + if (port_end <= port_start) + { + error = clib_error_return ( + 0, "The end-port must be greater than start-port"); + goto done; + } + nat44_ei_set_alloc_range ((u16) port_start, (u16) port_end); + } + else + { + error = clib_error_return (0, "unknown input '%U'", + format_unformat_error, line_input); + goto done; + } + } + +done: + unformat_free (line_input); + + return error; +}; + +u8 * +format_nat44_ei_addr_and_port_alloc_alg (u8 *s, va_list *args) +{ + u32 i = va_arg (*args, u32); + u8 *t = 0; + + switch (i) + { +#define _(v, N, s) \ + case NAT44_EI_ADDR_AND_PORT_ALLOC_ALG_##N: \ + t = (u8 *) s; \ + break; + foreach_nat44_ei_addr_and_port_alloc_alg +#undef _ + default : s = format (s, "unknown"); + return s; + } + s = format (s, "%s", t); + return s; +} + +static clib_error_t * +nat44_ei_show_alloc_addr_and_port_alg_command_fn (vlib_main_t *vm, + unformat_input_t *input, + vlib_cli_command_t *cmd) +{ + nat44_ei_main_t *nm = &nat44_ei_main; + + vlib_cli_output (vm, "NAT address and port: %U", + format_nat44_ei_addr_and_port_alloc_alg, + nm->addr_and_port_alloc_alg); + switch (nm->addr_and_port_alloc_alg) + { + case NAT44_EI_ADDR_AND_PORT_ALLOC_ALG_MAPE: + vlib_cli_output (vm, " psid %d psid-offset %d psid-len %d", nm->psid, + nm->psid_offset, nm->psid_length); + break; + case NAT44_EI_ADDR_AND_PORT_ALLOC_ALG_RANGE: + vlib_cli_output (vm, " start-port %d end-port %d", nm->start_port, + nm->end_port); + break; + default: + break; + } + + return 0; +} + +static clib_error_t * +nat_set_mss_clamping_command_fn (vlib_main_t *vm, unformat_input_t *input, + vlib_cli_command_t *cmd) +{ + unformat_input_t _line_input, *line_input = &_line_input; + nat44_ei_main_t *nm = &nat44_ei_main; + clib_error_t *error = 0; + u32 mss; + + /* 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, "disable")) + nm->mss_clamping = 0; + else if (unformat (line_input, "%d", &mss)) + nm->mss_clamping = (u16) mss; + else + { + error = clib_error_return (0, "unknown input '%U'", + format_unformat_error, line_input); + goto done; + } + } + +done: + unformat_free (line_input); + + return error; +} + +static clib_error_t * +nat_show_mss_clamping_command_fn (vlib_main_t *vm, unformat_input_t *input, + vlib_cli_command_t *cmd) +{ + nat44_ei_main_t *nm = &nat44_ei_main; + + if (nm->mss_clamping) + vlib_cli_output (vm, "mss-clamping %d", nm->mss_clamping); + else + vlib_cli_output (vm, "mss-clamping disabled"); + + return 0; +} + +static clib_error_t * +nat_ha_failover_command_fn (vlib_main_t *vm, unformat_input_t *input, + vlib_cli_command_t *cmd) +{ + unformat_input_t _line_input, *line_input = &_line_input; + ip4_address_t addr; + u32 port, session_refresh_interval = 10; + int rv; + clib_error_t *error = 0; + + /* 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, "%U:%u", unformat_ip4_address, &addr, &port)) + ; + else if (unformat (line_input, "refresh-interval %u", + &session_refresh_interval)) + ; + else + { + error = clib_error_return (0, "unknown input '%U'", + format_unformat_error, line_input); + goto done; + } + } + + rv = nat_ha_set_failover (&addr, (u16) port, session_refresh_interval); + if (rv) + error = clib_error_return (0, "set HA failover failed"); + +done: + unformat_free (line_input); + + return error; +} + +static clib_error_t * +nat_ha_listener_command_fn (vlib_main_t *vm, unformat_input_t *input, + vlib_cli_command_t *cmd) +{ + unformat_input_t _line_input, *line_input = &_line_input; + ip4_address_t addr; + u32 port, path_mtu = 512; + int rv; + clib_error_t *error = 0; + + /* 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, "%U:%u", unformat_ip4_address, &addr, &port)) + ; + else if (unformat (line_input, "path-mtu %u", &path_mtu)) + ; + else + { + error = clib_error_return (0, "unknown input '%U'", + format_unformat_error, line_input); + goto done; + } + } + + rv = nat_ha_set_listener (&addr, (u16) port, path_mtu); + if (rv) + error = clib_error_return (0, "set HA listener failed"); + +done: + unformat_free (line_input); + + return error; +} + +static clib_error_t * +nat_show_ha_command_fn (vlib_main_t *vm, unformat_input_t *input, + vlib_cli_command_t *cmd) +{ + ip4_address_t addr; + u16 port; + u32 path_mtu, session_refresh_interval, resync_ack_missed; + u8 in_resync; + + nat_ha_get_listener (&addr, &port, &path_mtu); + if (!port) + { + vlib_cli_output (vm, "NAT HA disabled\n"); + return 0; + } + + vlib_cli_output (vm, "LISTENER:\n"); + vlib_cli_output (vm, " %U:%u path-mtu %u\n", format_ip4_address, &addr, + port, path_mtu); + + nat_ha_get_failover (&addr, &port, &session_refresh_interval); + vlib_cli_output (vm, "FAILOVER:\n"); + if (port) + vlib_cli_output (vm, " %U:%u refresh-interval %usec\n", + format_ip4_address, &addr, port, + session_refresh_interval); + else + vlib_cli_output (vm, " NA\n"); + + nat_ha_get_resync_status (&in_resync, &resync_ack_missed); + vlib_cli_output (vm, "RESYNC:\n"); + if (in_resync) + vlib_cli_output (vm, " in progress\n"); + else + vlib_cli_output (vm, " completed (%d ACK missed)\n", resync_ack_missed); + + return 0; +} + +static clib_error_t * +nat_ha_flush_command_fn (vlib_main_t *vm, unformat_input_t *input, + vlib_cli_command_t *cmd) +{ + nat_ha_flush (0); + return 0; +} + +static clib_error_t * +nat_ha_resync_command_fn (vlib_main_t *vm, unformat_input_t *input, + vlib_cli_command_t *cmd) +{ + clib_error_t *error = 0; + + if (nat_ha_resync (0, 0, 0)) + error = clib_error_return (0, "NAT HA resync already running"); + + return error; +} + +static clib_error_t * +add_address_command_fn (vlib_main_t *vm, unformat_input_t *input, + vlib_cli_command_t *cmd) +{ + unformat_input_t _line_input, *line_input = &_line_input; + nat44_ei_main_t *nm = &nat44_ei_main; + ip4_address_t start_addr, end_addr, this_addr; + u32 start_host_order, end_host_order; + u32 vrf_id = ~0; + int i, count; + int is_add = 1; + int rv = 0; + clib_error_t *error = 0; + + /* 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, "%U - %U", unformat_ip4_address, &start_addr, + unformat_ip4_address, &end_addr)) + ; + else if (unformat (line_input, "tenant-vrf %u", &vrf_id)) + ; + else if (unformat (line_input, "%U", unformat_ip4_address, &start_addr)) + end_addr = start_addr; + 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 (nm->static_mapping_only) + { + error = clib_error_return (0, "static mapping only mode"); + goto done; + } + + start_host_order = clib_host_to_net_u32 (start_addr.as_u32); + end_host_order = clib_host_to_net_u32 (end_addr.as_u32); + + if (end_host_order < start_host_order) + { + error = clib_error_return (0, "end address less than start address"); + goto done; + } + + count = (end_host_order - start_host_order) + 1; + + if (count > 1024) + nat44_ei_log_info ("%U - %U, %d addresses...", format_ip4_address, + &start_addr, format_ip4_address, &end_addr, count); + + this_addr = start_addr; + + for (i = 0; i < count; i++) + { + if (is_add) + rv = nat44_ei_add_address (nm, &this_addr, vrf_id); + else + rv = nat44_ei_del_address (nm, this_addr, 0); + + switch (rv) + { + case VNET_API_ERROR_VALUE_EXIST: + error = clib_error_return (0, "NAT address already in use."); + goto done; + case VNET_API_ERROR_NO_SUCH_ENTRY: + error = clib_error_return (0, "NAT address not exist."); + goto done; + case VNET_API_ERROR_UNSPECIFIED: + error = clib_error_return (0, "NAT address used in static mapping."); + goto done; + case VNET_API_ERROR_FEATURE_DISABLED: + goto done; + default: + break; + } + + if (nm->out2in_dpo) + nat44_ei_add_del_address_dpo (this_addr, is_add); + + increment_v4_address (&this_addr); + } + +done: + unformat_free (line_input); + + return error; +} + +static clib_error_t * +nat44_ei_show_addresses_command_fn (vlib_main_t *vm, unformat_input_t *input, + vlib_cli_command_t *cmd) +{ + nat44_ei_main_t *nm = &nat44_ei_main; + nat44_ei_address_t *ap; + + vlib_cli_output (vm, "NAT44 pool addresses:"); + vec_foreach (ap, nm->addresses) + { + vlib_cli_output (vm, "%U", format_ip4_address, &ap->addr); + if (ap->fib_index != ~0) + vlib_cli_output ( + vm, " tenant VRF: %u", + fib_table_get (ap->fib_index, FIB_PROTOCOL_IP4)->ft_table_id); + else + vlib_cli_output (vm, " tenant VRF independent"); +#define _(N, i, n, s) \ + vlib_cli_output (vm, " %d busy %s ports", ap->busy_##n##_ports, s); + foreach_nat_protocol +#undef _ + } + return 0; +} + +static clib_error_t * +nat44_ei_feature_command_fn (vlib_main_t *vm, unformat_input_t *input, + vlib_cli_command_t *cmd) +{ + unformat_input_t _line_input, *line_input = &_line_input; + vnet_main_t *vnm = vnet_get_main (); + clib_error_t *error = 0; + u32 sw_if_index; + u32 *inside_sw_if_indices = 0; + u32 *outside_sw_if_indices = 0; + u8 is_output_feature = 0; + int is_del = 0; + int i; + + sw_if_index = ~0; + + /* 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, "in %U", unformat_vnet_sw_interface, vnm, + &sw_if_index)) + vec_add1 (inside_sw_if_indices, sw_if_index); + else if (unformat (line_input, "out %U", unformat_vnet_sw_interface, vnm, + &sw_if_index)) + vec_add1 (outside_sw_if_indices, sw_if_index); + else if (unformat (line_input, "output-feature")) + is_output_feature = 1; + else if (unformat (line_input, "del")) + is_del = 1; + else + { + error = clib_error_return (0, "unknown input '%U'", + format_unformat_error, line_input); + goto done; + } + } + + if (vec_len (inside_sw_if_indices)) + { + for (i = 0; i < vec_len (inside_sw_if_indices); i++) + { + sw_if_index = inside_sw_if_indices[i]; + if (is_output_feature) + { + if (nat44_ei_interface_add_del_output_feature (sw_if_index, 1, + is_del)) + { + error = clib_error_return ( + 0, "%s %U failed", is_del ? "del" : "add", + format_vnet_sw_if_index_name, vnm, sw_if_index); + goto done; + } + } + else + { + if (nat44_ei_interface_add_del (sw_if_index, 1, is_del)) + { + error = clib_error_return ( + 0, "%s %U failed", is_del ? "del" : "add", + format_vnet_sw_if_index_name, vnm, sw_if_index); + goto done; + } + } + } + } + + if (vec_len (outside_sw_if_indices)) + { + for (i = 0; i < vec_len (outside_sw_if_indices); i++) + { + sw_if_index = outside_sw_if_indices[i]; + if (is_output_feature) + { + if (nat44_ei_interface_add_del_output_feature (sw_if_index, 0, + is_del)) + { + error = clib_error_return ( + 0, "%s %U failed", is_del ? "del" : "add", + format_vnet_sw_if_index_name, vnm, sw_if_index); + goto done; + } + } + else + { + if (nat44_ei_interface_add_del (sw_if_index, 0, is_del)) + { + error = clib_error_return ( + 0, "%s %U failed", is_del ? "del" : "add", + format_vnet_sw_if_index_name, vnm, sw_if_index); + goto done; + } + } + } + } + +done: + unformat_free (line_input); + vec_free (inside_sw_if_indices); + vec_free (outside_sw_if_indices); + + return error; +} + +static clib_error_t * +nat44_ei_show_interfaces_command_fn (vlib_main_t *vm, unformat_input_t *input, + vlib_cli_command_t *cmd) +{ + nat44_ei_main_t *nm = &nat44_ei_main; + nat44_ei_interface_t *i; + vnet_main_t *vnm = vnet_get_main (); + + vlib_cli_output (vm, "NAT44 interfaces:"); + pool_foreach (i, nm->interfaces) + { + vlib_cli_output (vm, " %U %s", format_vnet_sw_if_index_name, vnm, + i->sw_if_index, + (nat44_ei_interface_is_inside (i) && + nat44_ei_interface_is_outside (i)) ? + "in out" : + (nat44_ei_interface_is_inside (i) ? "in" : "out")); + } + + pool_foreach (i, nm->output_feature_interfaces) + { + vlib_cli_output (vm, " %U output-feature %s", + format_vnet_sw_if_index_name, vnm, i->sw_if_index, + (nat44_ei_interface_is_inside (i) && + nat44_ei_interface_is_outside (i)) ? + "in out" : + (nat44_ei_interface_is_inside (i) ? "in" : "out")); + } + + return 0; +} + +static clib_error_t * +add_static_mapping_command_fn (vlib_main_t *vm, unformat_input_t *input, + vlib_cli_command_t *cmd) +{ + unformat_input_t _line_input, *line_input = &_line_input; + clib_error_t *error = 0; + ip4_address_t l_addr, e_addr; + u32 l_port = 0, e_port = 0, vrf_id = ~0; + int is_add = 1, addr_only = 1, rv; + u32 sw_if_index = ~0; + vnet_main_t *vnm = vnet_get_main (); + nat_protocol_t proto = NAT_PROTOCOL_OTHER; + u8 proto_set = 0; + + /* 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", unformat_ip4_address, &l_addr, + &l_port)) + addr_only = 0; + else if (unformat (line_input, "local %U", unformat_ip4_address, + &l_addr)) + ; + else if (unformat (line_input, "external %U %u", unformat_ip4_address, + &e_addr, &e_port)) + addr_only = 0; + else if (unformat (line_input, "external %U", unformat_ip4_address, + &e_addr)) + ; + else if (unformat (line_input, "external %U %u", + unformat_vnet_sw_interface, vnm, &sw_if_index, + &e_port)) + addr_only = 0; + else if (unformat (line_input, "external %U", unformat_vnet_sw_interface, + vnm, &sw_if_index)) + ; + else if (unformat (line_input, "vrf %u", &vrf_id)) + ; + else if (unformat (line_input, "%U", unformat_nat_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 (addr_only) + { + if (proto_set) + { + error = clib_error_return ( + 0, "address only mapping doesn't support protocol"); + goto done; + } + } + else if (!proto_set) + { + error = clib_error_return (0, "protocol is required"); + goto done; + } + + rv = nat44_ei_add_del_static_mapping ( + l_addr, e_addr, clib_host_to_net_u16 (l_port), + clib_host_to_net_u16 (e_port), proto, sw_if_index, vrf_id, addr_only, 0, 0, + is_add); + + switch (rv) + { + case VNET_API_ERROR_INVALID_VALUE: + error = clib_error_return (0, "External port already in use."); + goto done; + case VNET_API_ERROR_NO_SUCH_ENTRY: + if (is_add) + error = clib_error_return (0, "External address must be allocated."); + else + error = clib_error_return (0, "Mapping not exist."); + goto done; + case VNET_API_ERROR_NO_SUCH_FIB: + error = clib_error_return (0, "No such VRF id."); + goto done; + case VNET_API_ERROR_VALUE_EXIST: + error = clib_error_return (0, "Mapping already exist."); + goto done; + case VNET_API_ERROR_FEATURE_DISABLED: + goto done; + default: + break; + } + +done: + unformat_free (line_input); + + return error; +} + +static clib_error_t * +add_identity_mapping_command_fn (vlib_main_t *vm, unformat_input_t *input, + vlib_cli_command_t *cmd) +{ + unformat_input_t _line_input, *line_input = &_line_input; + clib_error_t *error = 0; + u32 port = 0, vrf_id = ~0; + ip4_address_t addr; + int is_add = 1; + int addr_only = 1; + u32 sw_if_index = ~0; + vnet_main_t *vnm = vnet_get_main (); + int rv; + nat_protocol_t proto; + + addr.as_u32 = 0; + + /* 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, "%U", unformat_ip4_address, &addr)) + ; + else if (unformat (line_input, "external %U", unformat_vnet_sw_interface, + vnm, &sw_if_index)) + ; + else if (unformat (line_input, "vrf %u", &vrf_id)) + ; + else if (unformat (line_input, "%U %u", unformat_nat_protocol, &proto, + &port)) + addr_only = 0; + 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; + } + } + + rv = nat44_ei_add_del_static_mapping ( + addr, addr, clib_host_to_net_u16 (port), clib_host_to_net_u16 (port), + proto, sw_if_index, vrf_id, addr_only, 1, 0, is_add); + + switch (rv) + { + case VNET_API_ERROR_INVALID_VALUE: + error = clib_error_return (0, "External port already in use."); + goto done; + case VNET_API_ERROR_NO_SUCH_ENTRY: + if (is_add) + error = clib_error_return (0, "External address must be allocated."); + else + error = clib_error_return (0, "Mapping not exist."); + goto done; + case VNET_API_ERROR_NO_SUCH_FIB: + error = clib_error_return (0, "No such VRF id."); + goto done; + case VNET_API_ERROR_VALUE_EXIST: + error = clib_error_return (0, "Mapping already exist."); + goto done; + default: + break; + } + +done: + unformat_free (line_input); + + return error; +} + +static clib_error_t * +nat44_ei_show_static_mappings_command_fn (vlib_main_t *vm, + unformat_input_t *input, + vlib_cli_command_t *cmd) +{ + nat44_ei_main_t *nm = &nat44_ei_main; + nat44_ei_static_mapping_t *m; + nat44_ei_static_map_resolve_t *rp; + + vlib_cli_output (vm, "NAT44 static mappings:"); + pool_foreach (m, nm->static_mappings) + { + vlib_cli_output (vm, " %U", format_nat44_ei_static_mapping, m); + } + vec_foreach (rp, nm->to_resolve) + vlib_cli_output (vm, " %U", format_nat44_ei_static_map_to_resolve, rp); + + return 0; +} + +static clib_error_t * +nat44_ei_add_interface_address_command_fn (vlib_main_t *vm, + unformat_input_t *input, + vlib_cli_command_t *cmd) +{ + nat44_ei_main_t *nm = &nat44_ei_main; + unformat_input_t _line_input, *line_input = &_line_input; + u32 sw_if_index; + int rv; + int is_del = 0; + clib_error_t *error = 0; + + /* 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, "%U", unformat_vnet_sw_interface, + nm->vnet_main, &sw_if_index)) + ; + else if (unformat (line_input, "del")) + is_del = 1; + else + { + error = clib_error_return (0, "unknown input '%U'", + format_unformat_error, line_input); + goto done; + } + } + + rv = nat44_ei_add_interface_address (nm, sw_if_index, is_del); + + switch (rv) + { + case 0: + break; + + default: + error = clib_error_return ( + 0, "nat44_ei_add_interface_address returned %d", rv); + goto done; + } + +done: + unformat_free (line_input); + + return error; +} + +static clib_error_t * +nat44_ei_show_interface_address_command_fn (vlib_main_t *vm, + unformat_input_t *input, + vlib_cli_command_t *cmd) +{ + nat44_ei_main_t *nm = &nat44_ei_main; + vnet_main_t *vnm = vnet_get_main (); + u32 *sw_if_index; + + vlib_cli_output (vm, "NAT44 pool address interfaces:"); + vec_foreach (sw_if_index, nm->auto_add_sw_if_indices) + { + vlib_cli_output (vm, " %U", format_vnet_sw_if_index_name, vnm, + *sw_if_index); + } + return 0; +} + +static clib_error_t * +nat44_ei_show_sessions_command_fn (vlib_main_t *vm, unformat_input_t *input, + vlib_cli_command_t *cmd) +{ + unformat_input_t _line_input, *line_input = &_line_input; + clib_error_t *error = 0; + + nat44_ei_main_per_thread_data_t *tnm; + nat44_ei_main_t *nm = &nat44_ei_main; + + int detail = 0; + int i = 0; + + if (!unformat_user (input, unformat_line_input, line_input)) + goto print; + + while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT) + { + if (unformat (line_input, "detail")) + detail = 1; + else + { + error = clib_error_return (0, "unknown input '%U'", + format_unformat_error, line_input); + break; + } + } + unformat_free (line_input); + +print: + vlib_cli_output (vm, "NAT44 sessions:"); + + vec_foreach_index (i, nm->per_thread_data) + { + tnm = vec_elt_at_index (nm->per_thread_data, i); + + vlib_cli_output (vm, "-------- thread %d %s: %d sessions --------\n", i, + vlib_worker_threads[i].name, pool_elts (tnm->sessions)); + + nat44_ei_user_t *u; + pool_foreach (u, tnm->users) + { + vlib_cli_output (vm, " %U", format_nat44_ei_user, tnm, u, detail); + } + } + return error; +} + +static clib_error_t * +nat44_ei_del_user_command_fn (vlib_main_t *vm, unformat_input_t *input, + vlib_cli_command_t *cmd) +{ + unformat_input_t _line_input, *line_input = &_line_input; + clib_error_t *error = 0; + ip4_address_t addr; + u32 fib_index = 0; + int rv; + + /* 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, "%U", unformat_ip4_address, &addr)) + ; + else if (unformat (line_input, "fib %u", &fib_index)) + ; + else + { + error = clib_error_return (0, "unknown input '%U'", + format_unformat_error, line_input); + goto done; + } + } + + rv = nat44_ei_user_del (&addr, fib_index); + + if (!rv) + { + error = clib_error_return (0, "nat44_ei_user_del returned %d", rv); + } + +done: + unformat_free (line_input); + + return error; +} + +static clib_error_t * +nat44_ei_clear_sessions_command_fn (vlib_main_t *vm, unformat_input_t *input, + vlib_cli_command_t *cmd) +{ + clib_error_t *error = 0; + nat44_ei_sessions_clear (); + return error; +} + +static clib_error_t * +nat44_ei_del_session_command_fn (vlib_main_t *vm, unformat_input_t *input, + vlib_cli_command_t *cmd) +{ + nat44_ei_main_t *nm = &nat44_ei_main; + unformat_input_t _line_input, *line_input = &_line_input; + u32 port = 0, vrf_id = nm->outside_vrf_id; + clib_error_t *error = 0; + nat_protocol_t proto; + ip4_address_t addr; + int rv, is_in = 0; + + /* 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, "%U:%u %U", unformat_ip4_address, &addr, &port, + unformat_nat_protocol, &proto)) + ; + else if (unformat (line_input, "in")) + { + is_in = 1; + vrf_id = nm->inside_vrf_id; + } + else if (unformat (line_input, "out")) + { + is_in = 0; + vrf_id = nm->outside_vrf_id; + } + else if (unformat (line_input, "vrf %u", &vrf_id)) + ; + else + { + error = clib_error_return (0, "unknown input '%U'", + format_unformat_error, line_input); + goto done; + } + } + + rv = nat44_ei_del_session (nm, &addr, clib_host_to_net_u16 (port), proto, + vrf_id, is_in); + + switch (rv) + { + case 0: + break; + + default: + error = clib_error_return (0, "nat44_ei_del_session returned %d", rv); + goto done; + } + +done: + unformat_free (line_input); + + return error; +} + +static clib_error_t * +nat44_ei_forwarding_set_command_fn (vlib_main_t *vm, unformat_input_t *input, + vlib_cli_command_t *cmd) +{ + nat44_ei_main_t *nm = &nat44_ei_main; + unformat_input_t _line_input, *line_input = &_line_input; + u8 forwarding_enable; + u8 forwarding_enable_set = 0; + clib_error_t *error = 0; + + /* Get a line of input. */ + if (!unformat_user (input, unformat_line_input, line_input)) + return clib_error_return (0, "'enable' or 'disable' expected"); + + while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT) + { + if (!forwarding_enable_set && unformat (line_input, "enable")) + { + forwarding_enable = 1; + forwarding_enable_set = 1; + } + else if (!forwarding_enable_set && unformat (line_input, "disable")) + { + forwarding_enable = 0; + forwarding_enable_set = 1; + } + else + { + error = clib_error_return (0, "unknown input '%U'", + format_unformat_error, line_input); + goto done; + } + } + + if (!forwarding_enable_set) + { + error = clib_error_return (0, "'enable' or 'disable' expected"); + goto done; + } + + nm->forwarding_enabled = forwarding_enable; + +done: + unformat_free (line_input); + + return error; +} + +static clib_error_t * +set_timeout_command_fn (vlib_main_t *vm, unformat_input_t *input, + vlib_cli_command_t *cmd) +{ + nat44_ei_main_t *nm = &nat44_ei_main; + unformat_input_t _line_input, *line_input = &_line_input; + clib_error_t *error = 0; + + /* 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, "udp %u", &nm->timeouts.udp)) + ; + else if (unformat (line_input, "tcp-established %u", + &nm->timeouts.tcp.established)) + ; + else if (unformat (line_input, "tcp-transitory %u", + &nm->timeouts.tcp.transitory)) + ; + else if (unformat (line_input, "icmp %u", &nm->timeouts.icmp)) + ; + else if (unformat (line_input, "reset")) + nat_reset_timeouts (&nm->timeouts); + else + { + error = clib_error_return (0, "unknown input '%U'", + format_unformat_error, line_input); + goto done; + } + } +done: + unformat_free (line_input); + return error; +} + +static clib_error_t * +nat_show_timeouts_command_fn (vlib_main_t *vm, unformat_input_t *input, + vlib_cli_command_t *cmd) +{ + nat44_ei_main_t *nm = &nat44_ei_main; + + // TODO: make format timeout function + vlib_cli_output (vm, "udp timeout: %dsec", nm->timeouts.udp); + vlib_cli_output (vm, "tcp-established timeout: %dsec", + nm->timeouts.tcp.established); + vlib_cli_output (vm, "tcp-transitory timeout: %dsec", + nm->timeouts.tcp.transitory); + vlib_cli_output (vm, "icmp timeout: %dsec", nm->timeouts.icmp); + + return 0; +} + +/*? + * @cliexpar + * @cliexstart{nat44 ei enable} + * Enable nat44 ei plugin + * To enable nat44, use: + * vpp# nat44 ei enable sessions + * To enable nat44 ei static mapping only, use: + * vpp# nat44 ei enable sessions static-mapping + * To enable nat44 ei static mapping with connection tracking, use: + * vpp# nat44 ei enable sessions static-mapping connection-tracking + * To enable nat44 ei out2in dpo, use: + * vpp# nat44 ei enable sessions out2in-dpo + * To set inside-vrf outside-vrf, use: + * vpp# nat44 ei enable sessions inside-vrf outside-vrf + * @cliexend +?*/ +VLIB_CLI_COMMAND (nat44_ei_enable_command, static) = { + .path = "nat44 ei enable", + .short_help = + "nat44 ei enable sessions [users ] " + "[static-mappig-only [connection-tracking]|out2in-dpo] [inside-vrf " + "] [outside-vrf ] [user-sessions ]", + .function = nat44_ei_enable_command_fn, +}; + +/*? + * @cliexpar + * @cliexstart{nat44 ei disable} + * Disable nat44 ei plugin + * To disable nat44, use: + * vpp# nat44 ei disable + * @cliexend +?*/ +VLIB_CLI_COMMAND (nat44_ei_disable_command, static) = { + .path = "nat44 ei disable", + .short_help = "nat44 ei disable", + .function = nat44_ei_disable_command_fn, +}; + +/*? + * @cliexpar + * @cliexstart{set snat44 ei workers} + * Set NAT workers if 2 or more workers available, use: + * vpp# set snat44 ei workers 0-2,5 + * @cliexend +?*/ +VLIB_CLI_COMMAND (set_workers_command, static) = { + .path = "set nat44 ei workers", + .function = set_workers_command_fn, + .short_help = "set nat44 ei workers ", +}; + +/*? + * @cliexpar + * @cliexstart{show nat44 ei workers} + * Show NAT workers. + * vpp# show nat44 ei workers: + * 2 workers + * vpp_wk_0 + * vpp_wk_1 + * @cliexend +?*/ +VLIB_CLI_COMMAND (nat_show_workers_command, static) = { + .path = "show nat44 ei workers", + .short_help = "show nat44 ei workers", + .function = nat_show_workers_commnad_fn, +}; + +/*? + * @cliexpar + * @cliexstart{set nat44 ei timeout} + * Set values of timeouts for NAT sessions (in seconds), use: + * vpp# set nat44 ei timeout udp 120 tcp-established 7500 tcp-transitory 250 +icmp 90 + * To reset default values use: + * vpp# set nat44 ei timeout reset + * @cliexend +?*/ +VLIB_CLI_COMMAND (set_timeout_command, static) = { + .path = "set nat44 ei timeout", + .function = set_timeout_command_fn, + .short_help = "set nat44 ei timeout [udp | tcp-established " + "tcp-transitory | icmp | reset]", +}; + +/*? + * @cliexpar + * @cliexstart{show nat44 ei timeouts} + * Show values of timeouts for NAT sessions. + * vpp# show nat44 ei timeouts + * udp timeout: 300sec + * tcp-established timeout: 7440sec + * tcp-transitory timeout: 240sec + * icmp timeout: 60sec + * @cliexend +?*/ +VLIB_CLI_COMMAND (nat_show_timeouts_command, static) = { + .path = "show nat44 ei timeouts", + .short_help = "show nat44 ei timeouts", + .function = nat_show_timeouts_command_fn, +}; + +/*? + * @cliexpar + * @cliexstart{nat44 ei set logging level} + * To set NAT logging level use: + * Set nat44 ei logging level + * @cliexend +?*/ +VLIB_CLI_COMMAND (nat44_ei_set_log_level_command, static) = { + .path = "nat44 ei set logging level", + .function = nat44_ei_set_log_level_command_fn, + .short_help = "nat44 ei set logging level ", +}; + +/*? + * @cliexpar + * @cliexstart{snat44 ei ipfix logging} + * To enable NAT IPFIX logging use: + * vpp# nat44 ei ipfix logging + * To set IPFIX exporter use: + * vpp# set ipfix exporter collector 10.10.10.3 src 10.10.10.1 + * @cliexend +?*/ +VLIB_CLI_COMMAND (nat44_ei_ipfix_logging_enable_disable_command, static) = { + .path = "nat44 ei ipfix logging", + .function = nat44_ei_ipfix_logging_enable_disable_command_fn, + .short_help = + "nat44 ei ipfix logging [domain ] [src-port ] [disable]", +}; + +/*? + * @cliexpar + * @cliexstart{nat44 ei addr-port-assignment-alg} + * Set address and port assignment algorithm + * For the MAP-E CE limit port choice based on PSID use: + * vpp# nat44 ei addr-port-assignment-alg map-e psid 10 psid-offset 6 psid-len +6 + * For port range use: + * vpp# nat44 ei addr-port-assignment-alg port-range - + * To set standard (default) address and port assignment algorithm use: + * vpp# nat44 ei addr-port-assignment-alg default + * @cliexend +?*/ +VLIB_CLI_COMMAND (nat44_ei_set_alloc_addr_and_port_alg_command, static) = { + .path = "nat44 ei addr-port-assignment-alg", + .short_help = "nat44 ei addr-port-assignment-alg []", + .function = nat44_ei_set_alloc_addr_and_port_alg_command_fn, +}; + +/*? + * @cliexpar + * @cliexstart{show nat44 ei addr-port-assignment-alg} + * Show address and port assignment algorithm + * @cliexend +?*/ +VLIB_CLI_COMMAND (nat44_ei_show_alloc_addr_and_port_alg_command, static) = { + .path = "show nat44 ei addr-port-assignment-alg", + .short_help = "show nat44 ei addr-port-assignment-alg", + .function = nat44_ei_show_alloc_addr_and_port_alg_command_fn, +}; + +/*? + * @cliexpar + * @cliexstart{nat44 ei mss-clamping} + * Set TCP MSS rewriting configuration + * To enable TCP MSS rewriting use: + * vpp# nat44 ei mss-clamping 1452 + * To disbale TCP MSS rewriting use: + * vpp# nat44 ei mss-clamping disable + * @cliexend +?*/ +VLIB_CLI_COMMAND (nat_set_mss_clamping_command, static) = { + .path = "nat44 ei mss-clamping", + .short_help = "nat44 ei mss-clamping |disable", + .function = nat_set_mss_clamping_command_fn, +}; + +/*? + * @cliexpar + * @cliexstart{show nat44 ei mss-clamping} + * Show TCP MSS rewriting configuration + * @cliexend +?*/ +VLIB_CLI_COMMAND (nat_show_mss_clamping_command, static) = { + .path = "show nat44 ei mss-clamping", + .short_help = "show nat44 ei mss-clamping", + .function = nat_show_mss_clamping_command_fn, +}; + +/*? + * @cliexpar + * @cliexstart{nat44 ei ha failover} + * Set HA failover (remote settings) + * @cliexend +?*/ +VLIB_CLI_COMMAND (nat_ha_failover_command, static) = { + .path = "nat44 ei ha failover", + .short_help = + "nat44 ei ha failover : [refresh-interval ]", + .function = nat_ha_failover_command_fn, +}; + +/*? + * @cliexpar + * @cliexstart{nat44 ei ha listener} + * Set HA listener (local settings) + * @cliexend +?*/ +VLIB_CLI_COMMAND (nat_ha_listener_command, static) = { + .path = "nat44 ei ha listener", + .short_help = + "nat44 ei ha listener : [path-mtu ]", + .function = nat_ha_listener_command_fn, +}; + +/*? + * @cliexpar + * @cliexstart{show nat44 ei ha} + * Show HA configuration/status + * @cliexend +?*/ +VLIB_CLI_COMMAND (nat_show_ha_command, static) = { + .path = "show nat44 ei ha", + .short_help = "show nat44 ei ha", + .function = nat_show_ha_command_fn, +}; + +/*? + * @cliexpar + * @cliexstart{nat44 ei ha flush} + * Flush the current HA data (for testing) + * @cliexend +?*/ +VLIB_CLI_COMMAND (nat_ha_flush_command, static) = { + .path = "nat44 ei ha flush", + .short_help = "nat44 ei ha flush", + .function = nat_ha_flush_command_fn, +}; + +/*? + * @cliexpar + * @cliexstart{nat44 ei ha resync} + * Resync HA (resend existing sessions to new failover) + * @cliexend +?*/ +VLIB_CLI_COMMAND (nat_ha_resync_command, static) = { + .path = "nat44 ei ha resync", + .short_help = "nat44 ei ha resync", + .function = nat_ha_resync_command_fn, +}; + +/*? + * @cliexpar + * @cliexstart{show nat44 ei hash tables} + * Show NAT44 hash tables + * @cliexend +?*/ +VLIB_CLI_COMMAND (nat44_ei_show_hash, static) = { + .path = "show nat44 ei hash tables", + .short_help = "show nat44 ei hash tables [detail|verbose]", + .function = nat44_ei_show_hash_command_fn, +}; + +/*? + * @cliexpar + * @cliexstart{nat44 ei add address} + * Add/delete NAT44 pool address. + * To add NAT44 pool address use: + * vpp# nat44 ei add address 172.16.1.3 + * vpp# nat44 ei add address 172.16.2.2 - 172.16.2.24 + * To add NAT44 pool address for specific tenant (identified by VRF id) use: + * vpp# nat44 ei add address 172.16.1.3 tenant-vrf 10 + * @cliexend +?*/ +VLIB_CLI_COMMAND (add_address_command, static) = { + .path = "nat44 ei add address", + .short_help = "nat44 ei add address [- ] " + "[tenant-vrf ] [del]", + .function = add_address_command_fn, +}; + +/*? + * @cliexpar + * @cliexstart{show nat44 ei addresses} + * Show NAT44 pool addresses. + * vpp# show nat44 ei addresses + * NAT44 pool addresses: + * 172.16.2.2 + * tenant VRF independent + * 10 busy udp ports + * 0 busy tcp ports + * 0 busy icmp ports + * 172.16.1.3 + * tenant VRF: 10 + * 0 busy udp ports + * 2 busy tcp ports + * 0 busy icmp ports + * @cliexend +?*/ +VLIB_CLI_COMMAND (nat44_ei_show_addresses_command, static) = { + .path = "show nat44 ei addresses", + .short_help = "show nat44 ei addresses", + .function = nat44_ei_show_addresses_command_fn, +}; + +/*? + * @cliexpar + * @cliexstart{set interface nat44} + * Enable/disable NAT44 feature on the interface. + * To enable NAT44 feature with local network interface use: + * vpp# set interface nat44 ei in GigabitEthernet0/8/0 + * To enable NAT44 feature with external network interface use: + * vpp# set interface nat44 ei out GigabitEthernet0/a/0 + * @cliexend +?*/ +VLIB_CLI_COMMAND (set_interface_nat44_ei_command, static) = { + .path = "set interface nat44 ei", + .function = nat44_ei_feature_command_fn, + .short_help = + "set interface nat44 ei in out [output-feature] " + "[del]", +}; + +/*? + * @cliexpar + * @cliexstart{show nat44 ei interfaces} + * Show interfaces with NAT44 feature. + * vpp# show nat44 ei interfaces + * NAT44 interfaces: + * GigabitEthernet0/8/0 in + * GigabitEthernet0/a/0 out + * @cliexend +?*/ +VLIB_CLI_COMMAND (nat44_ei_show_interfaces_command, static) = { + .path = "show nat44 ei interfaces", + .short_help = "show nat44 ei interfaces", + .function = nat44_ei_show_interfaces_command_fn, +}; + +/*? + * @cliexpar + * @cliexstart{nat44 ei add static mapping} + * Static mapping allows hosts on the external network to initiate connection + * to to the local network host. + * To create static mapping between local host address 10.0.0.3 port 6303 and + * external address 4.4.4.4 port 3606 for TCP protocol use: + * vpp# nat44 ei add static mapping tcp local 10.0.0.3 6303 external 4.4.4.4 +3606 + * If not runnig "static mapping only" NAT plugin mode use before: + * vpp# nat44 ei add address 4.4.4.4 + * To create address only static mapping between local and external address +use: + * vpp# nat44 ei add static mapping local 10.0.0.3 external 4.4.4.4 + * To create ICMP static mapping between local and external with ICMP echo + * identifier 10 use: + * vpp# nat44 ei add static mapping icmp local 10.0.0.3 10 external 4.4.4.4 10 + * @cliexend +?*/ +VLIB_CLI_COMMAND (add_static_mapping_command, static) = { + .path = "nat44 ei add static mapping", + .function = add_static_mapping_command_fn, + .short_help = "nat44 ei add static mapping tcp|udp|icmp local " + "[] " + "external [] [vrf ] [del]", +}; + +/*? + * @cliexpar + * @cliexstart{nat44 ei add identity mapping} + * Identity mapping translate an IP address to itself. + * To create identity mapping for address 10.0.0.3 port 6303 for TCP protocol + * use: + * vpp# nat44 ei add identity mapping 10.0.0.3 tcp 6303 + * To create identity mapping for address 10.0.0.3 use: + * vpp# nat44 ei add identity mapping 10.0.0.3 + * To create identity mapping for DHCP addressed interface use: + * vpp# nat44 ei add identity mapping external GigabitEthernet0/a/0 tcp 3606 + * @cliexend +?*/ +VLIB_CLI_COMMAND (add_identity_mapping_command, static) = { + .path = "nat44 ei add identity mapping", + .function = add_identity_mapping_command_fn, + .short_help = + "nat44 ei add identity mapping |external " + "[ ] [vrf ] [del]", +}; + +/*? + * @cliexpar + * @cliexstart{show nat44 ei static mappings} + * Show NAT44 static mappings. + * vpp# show nat44 ei static mappings + * NAT44 static mappings: + * local 10.0.0.3 external 4.4.4.4 vrf 0 + * tcp local 192.168.0.4:6303 external 4.4.4.3:3606 vrf 0 + * tcp vrf 0 external 1.2.3.4:80 + * local 10.100.10.10:8080 probability 80 + * local 10.100.10.20:8080 probability 20 + * tcp local 10.0.0.10:3603 external GigabitEthernet0/a/0:6306 vrf 10 + * @cliexend +?*/ +VLIB_CLI_COMMAND (nat44_ei_show_static_mappings_command, static) = { + .path = "show nat44 ei static mappings", + .short_help = "show nat44 ei static mappings", + .function = nat44_ei_show_static_mappings_command_fn, +}; + +/*? + * @cliexpar + * @cliexstart{nat44 ei add interface address} + * Use NAT44 pool address from specific interfce + * To add NAT44 pool address from specific interface use: + * vpp# nat44 ei add interface address GigabitEthernet0/8/0 + * @cliexend +?*/ +VLIB_CLI_COMMAND (nat44_ei_add_interface_address_command, static) = { + .path = "nat44 ei add interface address", + .short_help = "nat44 ei add interface address [del]", + .function = nat44_ei_add_interface_address_command_fn, +}; + +/*? + * @cliexpar + * @cliexstart{show nat44 ei interface address} + * Show NAT44 pool address interfaces + * vpp# show nat44 ei interface address + * NAT44 pool address interfaces: + * GigabitEthernet0/a/0 + * @cliexend +?*/ +VLIB_CLI_COMMAND (nat44_ei_show_interface_address_command, static) = { + .path = "show nat44 ei interface address", + .short_help = "show nat44 ei interface address", + .function = nat44_ei_show_interface_address_command_fn, +}; + +/*? + * @cliexpar + * @cliexstart{show nat44 ei sessions} + * Show NAT44 sessions. + * @cliexend +?*/ +VLIB_CLI_COMMAND (nat44_ei_show_sessions_command, static) = { + .path = "show nat44 ei sessions", + .short_help = "show nat44 ei sessions [detail|metrics]", + .function = nat44_ei_show_sessions_command_fn, +}; + +/*? + * @cliexpar + * @cliexstart{nat44 ei del user} + * To delete all NAT44 user sessions: + * vpp# nat44 ei del user 10.0.0.3 + * @cliexend +?*/ +VLIB_CLI_COMMAND (nat44_ei_del_user_command, static) = { + .path = "nat44 ei del user", + .short_help = "nat44 ei del user [fib ]", + .function = nat44_ei_del_user_command_fn, +}; + +/*? + * @cliexpar + * @cliexstart{clear nat44 ei sessions} + * To clear all NAT44 sessions + * vpp# clear nat44 ei sessions + * @cliexend +?*/ +VLIB_CLI_COMMAND (nat44_ei_clear_sessions_command, static) = { + .path = "clear nat44 ei sessions", + .short_help = "clear nat44 ei sessions", + .function = nat44_ei_clear_sessions_command_fn, +}; + +/*? + * @cliexpar + * @cliexstart{nat44 ei del session} + * To administratively delete NAT44 session by inside address and port use: + * vpp# nat44 ei del session in 10.0.0.3:6303 tcp + * To administratively delete NAT44 session by outside address and port use: + * vpp# nat44 ei del session out 1.0.0.3:6033 udp + * @cliexend +?*/ +VLIB_CLI_COMMAND (nat44_ei_del_session_command, static) = { + .path = "nat44 ei del session", + .short_help = "nat44 ei del session in|out : tcp|udp|icmp [vrf " + "] [external-host :]", + .function = nat44_ei_del_session_command_fn, +}; + +/*? + * @cliexpar + * @cliexstart{nat44 ei forwarding} + * Enable or disable forwarding + * Forward packets which don't match existing translation + * or static mapping instead of dropping them. + * To enable forwarding, use: + * vpp# nat44 ei forwarding enable + * To disable forwarding, use: + * vpp# nat44 ei forwarding disable + * @cliexend +?*/ +VLIB_CLI_COMMAND (nat44_ei_forwarding_set_command, static) = { + .path = "nat44 ei forwarding", + .short_help = "nat44 ei forwarding enable|disable", + .function = nat44_ei_forwarding_set_command_fn, +}; + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/src/plugins/nat/nat44-ei/nat44_ei_dpo.c b/src/plugins/nat/nat44-ei/nat44_ei_dpo.c new file mode 100644 index 00000000000..51016df549f --- /dev/null +++ b/src/plugins/nat/nat44-ei/nat44_ei_dpo.c @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2020 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +dpo_type_t nat_dpo_type; + +void +nat_dpo_create (dpo_proto_t dproto, u32 aftr_index, dpo_id_t *dpo) +{ + dpo_set (dpo, nat_dpo_type, dproto, aftr_index); +} + +u8 * +format_nat_dpo (u8 *s, va_list *args) +{ + index_t index = va_arg (*args, index_t); + CLIB_UNUSED (u32 indent) = va_arg (*args, u32); + + return (format (s, "NAT44 out2in: AFTR:%d", index)); +} + +static void +nat_dpo_lock (dpo_id_t *dpo) +{ +} + +static void +nat_dpo_unlock (dpo_id_t *dpo) +{ +} + +const static dpo_vft_t nat_dpo_vft = { + .dv_lock = nat_dpo_lock, + .dv_unlock = nat_dpo_unlock, + .dv_format = format_nat_dpo, +}; + +const static char *const nat_ip4_nodes[] = { + "nat44-ei-out2in", + NULL, +}; + +const static char *const nat_ip6_nodes[] = { + NULL, +}; + +const static char *const *const nat_nodes[DPO_PROTO_NUM] = { + [DPO_PROTO_IP4] = nat_ip4_nodes, + [DPO_PROTO_IP6] = nat_ip6_nodes, + [DPO_PROTO_MPLS] = NULL, +}; + +void +nat_dpo_module_init (void) +{ + nat_dpo_type = dpo_register_new_type (&nat_dpo_vft, nat_nodes); +} + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/src/plugins/nat/nat44-ei/nat44_ei_dpo.h b/src/plugins/nat/nat44-ei/nat44_ei_dpo.h new file mode 100644 index 00000000000..9a5ce5b7781 --- /dev/null +++ b/src/plugins/nat/nat44-ei/nat44_ei_dpo.h @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __included_nat_dpo_h__ +#define __included_nat_dpo_h__ + +#include +#include + +void nat_dpo_create (dpo_proto_t dproto, u32 aftr_index, dpo_id_t *dpo); + +u8 *format_nat_dpo (u8 *s, va_list *args); + +void nat_dpo_module_init (void); + +#endif /* __included_nat_dpo_h__ */ + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/src/plugins/nat/nat44-ei/nat44_ei_ha.c b/src/plugins/nat/nat44-ei/nat44_ei_ha.c index aea758af2d4..ca99efcf8ca 100644 --- a/src/plugins/nat/nat44-ei/nat44_ei_ha.c +++ b/src/plugins/nat/nat44-ei/nat44_ei_ha.c @@ -13,13 +13,17 @@ * limitations under the License. */ -#include -#include -#include +//#include +#include #include -#include #include +#include + +#include +#include +#include + /* number of retries */ #define NAT_HA_RETRIES 3 @@ -173,14 +177,14 @@ nat44_ei_ha_sadd (ip4_address_t *in_addr, u16 in_port, ip4_address_t *out_addr, ip4_address_t *ehn_addr, u16 ehn_port, u8 proto, u32 fib_index, u16 flags, u32 thread_index) { - snat_main_t *sm = &snat_main; - snat_main_per_thread_data_t *tsm = &sm->per_thread_data[thread_index]; - snat_user_t *u; - snat_session_t *s; + nat44_ei_main_t *nm = &nat44_ei_main; + nat44_ei_main_per_thread_data_t *tnm = &nm->per_thread_data[thread_index]; + nat44_ei_user_t *u; + nat44_ei_session_t *s; clib_bihash_kv_8_8_t kv; vlib_main_t *vm = vlib_get_main (); f64 now = vlib_time_now (vm); - nat_outside_fib_t *outside_fib; + nat44_ei_outside_fib_t *outside_fib; fib_node_index_t fei = FIB_NODE_INDEX_INVALID; fib_prefix_t pfx = { .fp_proto = FIB_PROTOCOL_IP4, @@ -190,26 +194,21 @@ nat44_ei_ha_sadd (ip4_address_t *in_addr, u16 in_port, ip4_address_t *out_addr, }, }; - if (!(flags & SNAT_SESSION_FLAG_STATIC_MAPPING)) + if (!(flags & NAT44_EI_SESSION_FLAG_STATIC_MAPPING)) { - if (nat_set_outside_address_and_port (sm->addresses, thread_index, - *out_addr, out_port, proto)) + if (nat44_ei_set_outside_address_and_port (nm->addresses, thread_index, + *out_addr, out_port, proto)) return; } - u = nat_user_get_or_create (sm, in_addr, fib_index, thread_index); + u = nat44_ei_user_get_or_create (nm, in_addr, fib_index, thread_index); if (!u) return; - s = nat_session_alloc_or_recycle (sm, u, thread_index, now); + s = nat44_ei_session_alloc_or_recycle (nm, u, thread_index, now); if (!s) return; - if (sm->endpoint_dependent) - { - nat_ed_lru_insert (tsm, s, now, nat_proto_to_ip_proto (proto)); - } - s->out2in.addr.as_u32 = out_addr->as_u32; s->out2in.port = out_port; s->nat_proto = proto; @@ -217,17 +216,17 @@ nat44_ei_ha_sadd (ip4_address_t *in_addr, u16 in_port, ip4_address_t *out_addr, s->flags = flags; s->ext_host_addr.as_u32 = eh_addr->as_u32; s->ext_host_port = eh_port; - user_session_increment (sm, u, snat_is_session_static (s)); - switch (vec_len (sm->outside_fibs)) + nat44_ei_user_session_increment (nm, u, nat44_ei_is_session_static (s)); + switch (vec_len (nm->outside_fibs)) { case 0: - s->out2in.fib_index = sm->outside_fib_index; + s->out2in.fib_index = nm->outside_fib_index; break; case 1: - s->out2in.fib_index = sm->outside_fibs[0].fib_index; + s->out2in.fib_index = nm->outside_fibs[0].fib_index; break; default: - vec_foreach (outside_fib, sm->outside_fibs) + vec_foreach (outside_fib, nm->outside_fibs) { fei = fib_table_lookup (outside_fib->fib_index, &pfx); if (FIB_NODE_INDEX_INVALID != fei) @@ -241,16 +240,16 @@ nat44_ei_ha_sadd (ip4_address_t *in_addr, u16 in_port, ip4_address_t *out_addr, } break; } - init_nat_o2i_kv (&kv, s, thread_index, s - tsm->sessions); - if (clib_bihash_add_del_8_8 (&sm->out2in, &kv, 1)) - nat_elog_warn ("out2in key add failed"); + init_nat_o2i_kv (&kv, s, thread_index, s - tnm->sessions); + if (clib_bihash_add_del_8_8 (&nm->out2in, &kv, 1)) + nat_elog_warn (nm, "out2in key add failed"); s->in2out.addr.as_u32 = in_addr->as_u32; s->in2out.port = in_port; s->in2out.fib_index = fib_index; - init_nat_i2o_kv (&kv, s, thread_index, s - tsm->sessions); - if (clib_bihash_add_del_8_8 (&sm->in2out, &kv, 1)) - nat_elog_warn ("in2out key add failed"); + init_nat_i2o_kv (&kv, s, thread_index, s - tnm->sessions); + if (clib_bihash_add_del_8_8 (&nm->in2out, &kv, 1)) + nat_elog_warn (nm, "in2out key add failed"); } static_always_inline void @@ -258,27 +257,27 @@ nat44_ei_ha_sdel (ip4_address_t *out_addr, u16 out_port, ip4_address_t *eh_addr, u16 eh_port, u8 proto, u32 fib_index, u32 ti) { - snat_main_t *sm = &snat_main; + nat44_ei_main_t *nm = &nat44_ei_main; clib_bihash_kv_8_8_t kv, value; u32 thread_index; - snat_session_t *s; - snat_main_per_thread_data_t *tsm; + nat44_ei_session_t *s; + nat44_ei_main_per_thread_data_t *tnm; - if (sm->num_workers > 1) - thread_index = sm->first_worker_index + - (sm->workers[(clib_net_to_host_u16 (out_port) - 1024) / - sm->port_per_thread]); + if (nm->num_workers > 1) + thread_index = nm->first_worker_index + + (nm->workers[(clib_net_to_host_u16 (out_port) - 1024) / + nm->port_per_thread]); else - thread_index = sm->num_workers; - tsm = vec_elt_at_index (sm->per_thread_data, thread_index); + thread_index = nm->num_workers; + tnm = vec_elt_at_index (nm->per_thread_data, thread_index); init_nat_k (&kv, *out_addr, out_port, fib_index, proto); - if (clib_bihash_search_8_8 (&sm->out2in, &kv, &value)) + if (clib_bihash_search_8_8 (&nm->out2in, &kv, &value)) return; - s = pool_elt_at_index (tsm->sessions, nat_value_get_session_index (&value)); - nat_free_session_data (sm, s, thread_index, 1); - nat44_delete_session (sm, s, thread_index); + s = pool_elt_at_index (tnm->sessions, nat_value_get_session_index (&value)); + nat44_ei_free_session_data_v2 (nm, s, thread_index, 1); + nat44_ei_delete_session (nm, s, thread_index); } static_always_inline void @@ -286,18 +285,18 @@ nat44_ei_ha_sref (ip4_address_t *out_addr, u16 out_port, ip4_address_t *eh_addr, u16 eh_port, u8 proto, u32 fib_index, u32 total_pkts, u64 total_bytes, u32 thread_index) { - snat_main_t *sm = &snat_main; + nat44_ei_main_t *nm = &nat44_ei_main; clib_bihash_kv_8_8_t kv, value; - snat_session_t *s; - snat_main_per_thread_data_t *tsm; + nat44_ei_session_t *s; + nat44_ei_main_per_thread_data_t *tnm; - tsm = vec_elt_at_index (sm->per_thread_data, thread_index); + tnm = vec_elt_at_index (nm->per_thread_data, thread_index); init_nat_k (&kv, *out_addr, out_port, fib_index, proto); - if (clib_bihash_search_8_8 (&sm->out2in, &kv, &value)) + if (clib_bihash_search_8_8 (&nm->out2in, &kv, &value)) return; - s = pool_elt_at_index (tsm->sessions, nat_value_get_session_index (&value)); + s = pool_elt_at_index (tnm->sessions, nat_value_get_session_index (&value)); s->total_pkts = total_pkts; s->total_bytes = total_bytes; } @@ -305,6 +304,7 @@ nat44_ei_ha_sref (ip4_address_t *out_addr, u16 out_port, static void nat_ha_resync_fin (void) { + nat44_ei_main_t *nm = &nat44_ei_main; nat_ha_main_t *ha = &nat_ha_main; /* if no more resync ACK remainig we are done */ @@ -314,11 +314,11 @@ nat_ha_resync_fin (void) ha->in_resync = 0; if (ha->resync_ack_missed) { - nat_elog_info ("resync completed with result FAILED"); + nat_elog_info (nm, "resync completed with result FAILED"); } else { - nat_elog_info ("resync completed with result SUCCESS"); + nat_elog_info (nm, "resync completed with result SUCCESS"); } if (ha->event_callback) ha->event_callback (ha->client_index, ha->pid, ha->resync_ack_missed); @@ -347,6 +347,7 @@ nat_ha_resend_queue_add (u32 seq, u8 * data, u8 data_len, u8 is_resync, static_always_inline void nat_ha_ack_recv (u32 seq, u32 thread_index) { + nat44_ei_main_t *nm = &nat44_ei_main; nat_ha_main_t *ha = &nat_ha_main; nat_ha_per_thread_data_t *td = &ha->per_thread_data[thread_index]; u32 i; @@ -366,7 +367,7 @@ nat_ha_ack_recv (u32 seq, u32 thread_index) } vec_free (td->resend_queue[i].data); vec_del1 (td->resend_queue, i); - nat_elog_debug_X1 ("ACK for seq %d received", "i4", + nat_elog_debug_X1 (nm, "ACK for seq %d received", "i4", clib_net_to_host_u32 (seq)); return; @@ -377,6 +378,7 @@ nat_ha_ack_recv (u32 seq, u32 thread_index) static void nat_ha_resend_scan (f64 now, u32 thread_index) { + nat44_ei_main_t *nm = &nat44_ei_main; nat_ha_main_t *ha = &nat_ha_main; nat_ha_per_thread_data_t *td = &ha->per_thread_data[thread_index]; u32 i, *del, *to_delete = 0; @@ -394,7 +396,7 @@ nat_ha_resend_scan (f64 now, u32 thread_index) /* maximum retry reached delete cached data */ if (td->resend_queue[i].retry_count >= NAT_HA_RETRIES) { - nat_elog_notice_X1 ("seq %d missed", "i4", + nat_elog_notice_X1 (nm, "seq %d missed", "i4", clib_net_to_host_u32 (td->resend_queue[i].seq)); if (td->resend_queue[i].is_resync) { @@ -410,14 +412,14 @@ nat_ha_resend_scan (f64 now, u32 thread_index) } /* retry to send non-ACKed data */ - nat_elog_debug_X1 ("state sync seq %d resend", "i4", + nat_elog_debug_X1 (nm, "state sync seq %d resend", "i4", clib_net_to_host_u32 (td->resend_queue[i].seq)); td->resend_queue[i].retry_count++; vlib_increment_simple_counter (&ha->counters[NAT_HA_COUNTER_RETRY_COUNT], thread_index, 0, 1); if (vlib_buffer_alloc (vm, &bi, 1) != 1) { - nat_elog_warn ("HA NAT state sync can't allocate buffer"); + nat_elog_warn (nm, "HA NAT state sync can't allocate buffer"); return; } b = vlib_get_buffer (vm, bi); @@ -465,13 +467,13 @@ nat_ha_set_node_indexes (nat_ha_main_t *ha, vlib_main_t *vm) { vlib_node_t *node; - node = vlib_get_node_by_name (vm, (u8 *) "nat-ha-handoff"); + node = vlib_get_node_by_name (vm, (u8 *) "nat44-ei-ha-handoff"); ha->ha_handoff_node_index = node->index; - node = vlib_get_node_by_name (vm, (u8 *) "nat-ha-process"); + node = vlib_get_node_by_name (vm, (u8 *) "nat44-ei-ha-process"); ha->ha_process_node_index = node->index; - node = vlib_get_node_by_name (vm, (u8 *) "nat-ha-worker"); + node = vlib_get_node_by_name (vm, (u8 *) "nat44-ei-ha-worker"); ha->ha_worker_node_index = node->index; - node = vlib_get_node_by_name (vm, (u8 *) "nat-ha"); + node = vlib_get_node_by_name (vm, (u8 *) "nat44-ei-ha"); ha->ha_node_index = node->index; } @@ -489,10 +491,11 @@ nat_ha_init (vlib_main_t * vm, u32 num_workers, u32 num_threads) ha->num_workers = num_workers; vec_validate (ha->per_thread_data, num_threads); -#define _(N, s, v) ha->counters[v].name = s; \ - ha->counters[v].stat_segment_name = "/nat44/ha/" s; \ - vlib_validate_simple_counter(&ha->counters[v], 0); \ - vlib_zero_simple_counter(&ha->counters[v], 0); +#define _(N, s, v) \ + ha->counters[v].name = s; \ + ha->counters[v].stat_segment_name = "/nat44-ei/ha/" s; \ + vlib_validate_simple_counter (&ha->counters[v], 0); \ + vlib_zero_simple_counter (&ha->counters[v], 0); foreach_nat_ha_counter #undef _ } @@ -500,6 +503,7 @@ nat_ha_init (vlib_main_t * vm, u32 num_workers, u32 num_threads) int nat_ha_set_listener (ip4_address_t * addr, u16 port, u32 path_mtu) { + nat44_ei_main_t *nm = &nat44_ei_main; nat_ha_main_t *ha = &nat_ha_main; /* unregister previously set UDP port */ @@ -524,7 +528,8 @@ nat_ha_set_listener (ip4_address_t * addr, u16 port, u32 path_mtu) { udp_register_dst_port (ha->vlib_main, port, ha->ha_node_index, 1); } - nat_elog_info_X1 ("HA listening on port %d for state sync", "i4", port); + nat_elog_info_X1 (nm, "HA listening on port %d for state sync", "i4", + port); } return 0; @@ -633,6 +638,7 @@ nat_ha_recv_refresh (nat_ha_event_t * event, f64 now, u32 thread_index) static_always_inline void nat_ha_event_process (nat_ha_event_t * event, f64 now, u32 thread_index) { + nat44_ei_main_t *nm = &nat44_ei_main; switch (event->event_type) { case NAT_HA_ADD: @@ -645,7 +651,7 @@ nat_ha_event_process (nat_ha_event_t * event, f64 now, u32 thread_index) nat_ha_recv_refresh (event, now, thread_index); break; default: - nat_elog_notice_X1 ("Unsupported HA event type %d", "i4", + nat_elog_notice_X1 (nm, "Unsupported HA event type %d", "i4", event->event_type); break; } @@ -728,6 +734,7 @@ static_always_inline void nat_ha_event_add (nat_ha_event_t * event, u8 do_flush, u32 thread_index, u8 is_resync) { + nat44_ei_main_t *nm = &nat44_ei_main; nat_ha_main_t *ha = &nat_ha_main; nat_ha_per_thread_data_t *td = &ha->per_thread_data[thread_index]; vlib_main_t *vm = vlib_mains[thread_index]; @@ -744,7 +751,7 @@ nat_ha_event_add (nat_ha_event_t * event, u8 do_flush, u32 thread_index, if (vlib_buffer_alloc (vm, &bi, 1) != 1) { - nat_elog_warn ("HA NAT state sync can't allocate buffer"); + nat_elog_warn (nm, "HA NAT state sync can't allocate buffer"); return; } @@ -932,10 +939,10 @@ nat_ha_worker_fn (vlib_main_t * vm, vlib_node_runtime_t * rt, /* *INDENT-OFF* */ VLIB_REGISTER_NODE (nat_ha_worker_node) = { - .function = nat_ha_worker_fn, - .type = VLIB_NODE_TYPE_INPUT, - .state = VLIB_NODE_STATE_INTERRUPT, - .name = "nat-ha-worker", + .function = nat_ha_worker_fn, + .type = VLIB_NODE_TYPE_INPUT, + .state = VLIB_NODE_STATE_INTERRUPT, + .name = "nat44-ei-ha-worker", }; /* *INDENT-ON* */ @@ -943,6 +950,7 @@ VLIB_REGISTER_NODE (nat_ha_worker_node) = { static uword nat_ha_process (vlib_main_t * vm, vlib_node_runtime_t * rt, vlib_frame_t * f) { + nat44_ei_main_t *nm = &nat44_ei_main; nat_ha_main_t *ha = &nat_ha_main; uword event_type; uword *event_data = 0; @@ -951,7 +959,7 @@ nat_ha_process (vlib_main_t * vm, vlib_node_runtime_t * rt, vlib_frame_t * f) vlib_process_wait_for_event (vm); event_type = vlib_process_get_events (vm, &event_data); if (event_type) - nat_elog_info ("nat-ha-process: bogus kickoff event received"); + nat_elog_info (nm, "nat44-ei-ha-process: bogus kickoff event received"); vec_reset_length (event_data); while (1) @@ -974,9 +982,9 @@ nat_ha_process (vlib_main_t * vm, vlib_node_runtime_t * rt, vlib_frame_t * f) /* *INDENT-OFF* */ VLIB_REGISTER_NODE (nat_ha_process_node) = { - .function = nat_ha_process, - .type = VLIB_NODE_TYPE_PROCESS, - .name = "nat-ha-process", + .function = nat_ha_process, + .type = VLIB_NODE_TYPE_PROCESS, + .name = "nat44-ei-ha-process", }; /* *INDENT-ON* */ @@ -1002,9 +1010,8 @@ format_nat_ha_trace (u8 * s, va_list * args) CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *); nat_ha_trace_t *t = va_arg (*args, nat_ha_trace_t *); - s = - format (s, "nat-ha: %u events from %U", t->event_count, - format_ip4_address, &t->addr); + s = format (s, "nat44-ei-ha: %u events from %U", t->event_count, + format_ip4_address, &t->addr); return s; } @@ -1173,7 +1180,7 @@ nat_ha_node_fn (vlib_main_t * vm, vlib_node_runtime_t * node, /* *INDENT-OFF* */ VLIB_REGISTER_NODE (nat_ha_node) = { .function = nat_ha_node_fn, - .name = "nat-ha", + .name = "nat44-ei-ha", .vector_size = sizeof (u32), .format_trace = format_nat_ha_trace, .type = VLIB_NODE_TYPE_INTERNAL, @@ -1294,7 +1301,7 @@ nat_ha_resync (u32 client_index, u32 pid, /* *INDENT-OFF* */ VLIB_REGISTER_NODE (nat_ha_handoff_node) = { .function = nat_ha_handoff_node_fn, - .name = "nat-ha-handoff", + .name = "nat44-ei-ha-handoff", .vector_size = sizeof (u32), .format_trace = format_nat_ha_handoff_trace, .type = VLIB_NODE_TYPE_INTERNAL, diff --git a/src/plugins/nat/nat44-ei/nat44_ei_ha.h b/src/plugins/nat/nat44-ei/nat44_ei_ha.h index 5639c8d0239..c466d4c9288 100644 --- a/src/plugins/nat/nat44-ei/nat44_ei_ha.h +++ b/src/plugins/nat/nat44-ei/nat44_ei_ha.h @@ -22,7 +22,6 @@ #include #include -#include /* Call back functions for received HA events on passive/failover */ typedef void (*nat_ha_sadd_cb_t) (ip4_address_t * in_addr, u16 in_port, @@ -31,7 +30,6 @@ typedef void (*nat_ha_sadd_cb_t) (ip4_address_t * in_addr, u16 in_port, ip4_address_t * ehn_addr, u16 ehn_port, u8 proto, u32 fib_index, u16 flags, u32 thread_index); - typedef void (*nat_ha_sdel_cb_t) (ip4_address_t * out_addr, u16 out_port, ip4_address_t * eh_addr, u16 eh_port, u8 proto, u32 fib_index, u32 thread_index); diff --git a/src/plugins/nat/nat44-ei/nat44_ei_hairpinning.c b/src/plugins/nat/nat44-ei/nat44_ei_hairpinning.c new file mode 100644 index 00000000000..0de2de1faf4 --- /dev/null +++ b/src/plugins/nat/nat44-ei/nat44_ei_hairpinning.c @@ -0,0 +1,748 @@ +/* + * nat44_ei.c - nat44 endpoint dependent plugin + * * Copyright (c) 2020 Cisco and/or its affiliates. * Licensed under the + * Apache License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of the License + * at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + +#include +#include +#include + +#include +#include +#include + +/* NAT buffer flags */ +#define NAT44_EI_FLAG_HAIRPINNING (1 << 0) + +typedef enum +{ + NAT44_EI_HAIRPIN_SRC_NEXT_DROP, + NAT44_EI_HAIRPIN_SRC_NEXT_SNAT_IN2OUT, + NAT44_EI_HAIRPIN_SRC_NEXT_SNAT_IN2OUT_WH, + NAT44_EI_HAIRPIN_SRC_NEXT_INTERFACE_OUTPUT, + NAT44_EI_HAIRPIN_SRC_N_NEXT, +} nat44_ei_hairpin_src_next_t; + +typedef enum +{ + NAT44_EI_HAIRPIN_NEXT_LOOKUP, + NAT44_EI_HAIRPIN_NEXT_DROP, + NAT44_EI_HAIRPIN_NEXT_HANDOFF, + NAT44_EI_HAIRPIN_N_NEXT, +} nat44_ei_hairpin_next_t; + +typedef struct +{ + ip4_address_t addr; + u16 port; + u32 fib_index; + u32 session_index; +} nat44_ei_hairpin_trace_t; + +static u8 * +format_nat44_ei_hairpin_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 *); + nat44_ei_hairpin_trace_t *t = va_arg (*args, nat44_ei_hairpin_trace_t *); + + s = format (s, "new dst addr %U port %u fib-index %u", format_ip4_address, + &t->addr, clib_net_to_host_u16 (t->port), t->fib_index); + if (~0 == t->session_index) + { + s = format (s, " is-static-mapping"); + } + else + { + s = format (s, " session-index %u", t->session_index); + } + + return s; +} + +extern vnet_feature_arc_registration_t vnet_feat_arc_ip4_local; + +static_always_inline int +nat44_ei_is_hairpinning (nat44_ei_main_t *nm, ip4_address_t *dst_addr) +{ + nat44_ei_address_t *ap; + clib_bihash_kv_8_8_t kv, value; + + vec_foreach (ap, nm->addresses) + { + if (ap->addr.as_u32 == dst_addr->as_u32) + return 1; + } + + init_nat_k (&kv, *dst_addr, 0, 0, 0); + if (!clib_bihash_search_8_8 (&nm->static_mapping_by_external, &kv, &value)) + return 1; + + return 0; +} + +#ifndef CLIB_MARCH_VARIANT +void +nat44_ei_hairpinning_sm_unknown_proto (nat44_ei_main_t *nm, vlib_buffer_t *b, + ip4_header_t *ip) +{ + clib_bihash_kv_8_8_t kv, value; + nat44_ei_static_mapping_t *m; + u32 old_addr, new_addr; + ip_csum_t sum; + + init_nat_k (&kv, ip->dst_address, 0, 0, 0); + if (clib_bihash_search_8_8 (&nm->static_mapping_by_external, &kv, &value)) + return; + + m = pool_elt_at_index (nm->static_mappings, value.value); + + old_addr = ip->dst_address.as_u32; + new_addr = ip->dst_address.as_u32 = m->local_addr.as_u32; + sum = ip->checksum; + sum = ip_csum_update (sum, old_addr, new_addr, ip4_header_t, dst_address); + ip->checksum = ip_csum_fold (sum); + + if (vnet_buffer (b)->sw_if_index[VLIB_TX] == ~0) + vnet_buffer (b)->sw_if_index[VLIB_TX] = m->fib_index; +} +#endif + +#ifndef CLIB_MARCH_VARIANT +int +nat44_ei_hairpinning (vlib_main_t *vm, vlib_node_runtime_t *node, + nat44_ei_main_t *nm, u32 thread_index, vlib_buffer_t *b0, + ip4_header_t *ip0, udp_header_t *udp0, + tcp_header_t *tcp0, u32 proto0, int do_trace, + u32 *required_thread_index) +{ + nat44_ei_session_t *s0 = NULL; + clib_bihash_kv_8_8_t kv0, value0; + ip_csum_t sum0; + u32 new_dst_addr0 = 0, old_dst_addr0, si = ~0; + u16 new_dst_port0 = ~0, old_dst_port0; + int rv; + ip4_address_t sm0_addr; + u16 sm0_port; + u32 sm0_fib_index; + u32 old_sw_if_index = vnet_buffer (b0)->sw_if_index[VLIB_TX]; + + /* Check if destination is static mappings */ + if (!nat44_ei_static_mapping_match ( + ip0->dst_address, udp0->dst_port, nm->outside_fib_index, proto0, + &sm0_addr, &sm0_port, &sm0_fib_index, 1 /* by external */, 0, 0)) + { + new_dst_addr0 = sm0_addr.as_u32; + new_dst_port0 = sm0_port; + vnet_buffer (b0)->sw_if_index[VLIB_TX] = sm0_fib_index; + } + /* or active session */ + else + { + init_nat_k (&kv0, ip0->dst_address, udp0->dst_port, + nm->outside_fib_index, proto0); + rv = clib_bihash_search_8_8 (&nm->out2in, &kv0, &value0); + if (rv) + { + rv = 0; + goto trace; + } + + if (thread_index != nat_value_get_thread_index (&value0)) + { + *required_thread_index = nat_value_get_thread_index (&value0); + return 0; + } + + si = nat_value_get_session_index (&value0); + s0 = pool_elt_at_index (nm->per_thread_data[thread_index].sessions, si); + new_dst_addr0 = s0->in2out.addr.as_u32; + new_dst_port0 = s0->in2out.port; + vnet_buffer (b0)->sw_if_index[VLIB_TX] = s0->in2out.fib_index; + } + + /* Check if anything has changed and if not, then return 0. This + helps avoid infinite loop, repeating the three nodes + nat44-hairpinning-->ip4-lookup-->ip4-local, in case nothing has + changed. */ + old_dst_addr0 = ip0->dst_address.as_u32; + old_dst_port0 = tcp0->dst; + if (new_dst_addr0 == old_dst_addr0 && new_dst_port0 == old_dst_port0 && + vnet_buffer (b0)->sw_if_index[VLIB_TX] == old_sw_if_index) + return 0; + + /* Destination is behind the same NAT, use internal address and port */ + if (new_dst_addr0) + { + old_dst_addr0 = ip0->dst_address.as_u32; + ip0->dst_address.as_u32 = new_dst_addr0; + sum0 = ip0->checksum; + sum0 = ip_csum_update (sum0, old_dst_addr0, new_dst_addr0, ip4_header_t, + dst_address); + ip0->checksum = ip_csum_fold (sum0); + + old_dst_port0 = tcp0->dst; + if (PREDICT_TRUE (new_dst_port0 != old_dst_port0)) + { + if (PREDICT_TRUE (proto0 == NAT_PROTOCOL_TCP)) + { + tcp0->dst = new_dst_port0; + sum0 = tcp0->checksum; + sum0 = ip_csum_update (sum0, old_dst_addr0, new_dst_addr0, + ip4_header_t, dst_address); + sum0 = ip_csum_update (sum0, old_dst_port0, new_dst_port0, + ip4_header_t /* cheat */, length); + tcp0->checksum = ip_csum_fold (sum0); + } + else + { + udp0->dst_port = new_dst_port0; + udp0->checksum = 0; + } + } + else + { + if (PREDICT_TRUE (proto0 == NAT_PROTOCOL_TCP)) + { + sum0 = tcp0->checksum; + sum0 = ip_csum_update (sum0, old_dst_addr0, new_dst_addr0, + ip4_header_t, dst_address); + tcp0->checksum = ip_csum_fold (sum0); + } + } + rv = 1; + goto trace; + } + rv = 0; +trace: + if (do_trace && PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE) && + (b0->flags & VLIB_BUFFER_IS_TRACED))) + { + nat44_ei_hairpin_trace_t *t = vlib_add_trace (vm, node, b0, sizeof (*t)); + t->addr.as_u32 = new_dst_addr0; + t->port = new_dst_port0; + t->fib_index = vnet_buffer (b0)->sw_if_index[VLIB_TX]; + if (s0) + { + t->session_index = si; + } + else + { + t->session_index = ~0; + } + } + return rv; +} +#endif + +#ifndef CLIB_MARCH_VARIANT +u32 +nat44_ei_icmp_hairpinning (nat44_ei_main_t *nm, vlib_buffer_t *b0, + u32 thread_index, ip4_header_t *ip0, + icmp46_header_t *icmp0, u32 *required_thread_index) +{ + clib_bihash_kv_8_8_t kv0, value0; + u32 old_dst_addr0, new_dst_addr0; + u32 old_addr0, new_addr0; + u16 old_port0, new_port0; + u16 old_checksum0, new_checksum0; + u32 si, ti = 0; + ip_csum_t sum0; + nat44_ei_session_t *s0; + nat44_ei_static_mapping_t *m0; + + if (icmp_type_is_error_message ( + vnet_buffer (b0)->ip.reass.icmp_type_or_tcp_flags)) + { + ip4_header_t *inner_ip0 = 0; + tcp_udp_header_t *l4_header = 0; + + inner_ip0 = (ip4_header_t *) ((icmp_echo_header_t *) (icmp0 + 1) + 1); + l4_header = ip4_next_header (inner_ip0); + u32 protocol = ip_proto_to_nat_proto (inner_ip0->protocol); + + if (protocol != NAT_PROTOCOL_TCP && protocol != NAT_PROTOCOL_UDP) + return 1; + + init_nat_k (&kv0, ip0->dst_address, l4_header->src_port, + nm->outside_fib_index, protocol); + if (clib_bihash_search_8_8 (&nm->out2in, &kv0, &value0)) + return 1; + ti = nat_value_get_thread_index (&value0); + if (ti != thread_index) + { + *required_thread_index = ti; + return 1; + } + si = nat_value_get_session_index (&value0); + s0 = pool_elt_at_index (nm->per_thread_data[ti].sessions, si); + new_dst_addr0 = s0->in2out.addr.as_u32; + vnet_buffer (b0)->sw_if_index[VLIB_TX] = s0->in2out.fib_index; + + /* update inner source IP address */ + old_addr0 = inner_ip0->src_address.as_u32; + inner_ip0->src_address.as_u32 = new_dst_addr0; + new_addr0 = inner_ip0->src_address.as_u32; + sum0 = icmp0->checksum; + sum0 = + ip_csum_update (sum0, old_addr0, new_addr0, ip4_header_t, src_address); + icmp0->checksum = ip_csum_fold (sum0); + + /* update inner IP header checksum */ + old_checksum0 = inner_ip0->checksum; + sum0 = inner_ip0->checksum; + sum0 = + ip_csum_update (sum0, old_addr0, new_addr0, ip4_header_t, src_address); + inner_ip0->checksum = ip_csum_fold (sum0); + new_checksum0 = inner_ip0->checksum; + sum0 = icmp0->checksum; + sum0 = ip_csum_update (sum0, old_checksum0, new_checksum0, ip4_header_t, + checksum); + icmp0->checksum = ip_csum_fold (sum0); + + /* update inner source port */ + old_port0 = l4_header->src_port; + l4_header->src_port = s0->in2out.port; + new_port0 = l4_header->src_port; + sum0 = icmp0->checksum; + sum0 = ip_csum_update (sum0, old_port0, new_port0, tcp_udp_header_t, + src_port); + icmp0->checksum = ip_csum_fold (sum0); + } + else + { + init_nat_k (&kv0, ip0->dst_address, 0, nm->outside_fib_index, 0); + if (clib_bihash_search_8_8 (&nm->static_mapping_by_external, &kv0, + &value0)) + { + icmp_echo_header_t *echo0 = (icmp_echo_header_t *) (icmp0 + 1); + u16 icmp_id0 = echo0->identifier; + init_nat_k (&kv0, ip0->dst_address, icmp_id0, nm->outside_fib_index, + NAT_PROTOCOL_ICMP); + int rv = clib_bihash_search_8_8 (&nm->out2in, &kv0, &value0); + if (!rv) + { + ti = nat_value_get_thread_index (&value0); + if (ti != thread_index) + { + *required_thread_index = ti; + return 1; + } + si = nat_value_get_session_index (&value0); + s0 = pool_elt_at_index (nm->per_thread_data[ti].sessions, si); + new_dst_addr0 = s0->in2out.addr.as_u32; + vnet_buffer (b0)->sw_if_index[VLIB_TX] = s0->in2out.fib_index; + echo0->identifier = s0->in2out.port; + sum0 = icmp0->checksum; + sum0 = ip_csum_update (sum0, icmp_id0, s0->in2out.port, + icmp_echo_header_t, identifier); + icmp0->checksum = ip_csum_fold (sum0); + goto change_addr; + } + + return 1; + } + + m0 = pool_elt_at_index (nm->static_mappings, value0.value); + + new_dst_addr0 = m0->local_addr.as_u32; + if (vnet_buffer (b0)->sw_if_index[VLIB_TX] == ~0) + vnet_buffer (b0)->sw_if_index[VLIB_TX] = m0->fib_index; + } +change_addr: + /* Destination is behind the same NAT, use internal address and port */ + if (new_dst_addr0) + { + old_dst_addr0 = ip0->dst_address.as_u32; + ip0->dst_address.as_u32 = new_dst_addr0; + sum0 = ip0->checksum; + sum0 = ip_csum_update (sum0, old_dst_addr0, new_dst_addr0, ip4_header_t, + dst_address); + ip0->checksum = ip_csum_fold (sum0); + } + return 0; +} +#endif + +void nat44_ei_hairpinning_unknown_proto (nat44_ei_main_t *nm, vlib_buffer_t *b, + ip4_header_t *ip); + +#ifndef CLIB_MARCH_VARIANT +void +nat44_ei_hairpinning_unknown_proto (nat44_ei_main_t *nm, vlib_buffer_t *b, + ip4_header_t *ip) +{ + clib_bihash_kv_8_8_t kv, value; + nat44_ei_static_mapping_t *m; + u32 old_addr, new_addr; + ip_csum_t sum; + + init_nat_k (&kv, ip->dst_address, 0, 0, 0); + if (clib_bihash_search_8_8 (&nm->static_mapping_by_external, &kv, &value)) + return; + + m = pool_elt_at_index (nm->static_mappings, value.value); + + old_addr = ip->dst_address.as_u32; + new_addr = ip->dst_address.as_u32 = m->local_addr.as_u32; + sum = ip->checksum; + sum = ip_csum_update (sum, old_addr, new_addr, ip4_header_t, dst_address); + ip->checksum = ip_csum_fold (sum); + + if (vnet_buffer (b)->sw_if_index[VLIB_TX] == ~0) + vnet_buffer (b)->sw_if_index[VLIB_TX] = m->fib_index; +} +#endif + +VLIB_NODE_FN (nat44_ei_hairpin_src_node) +(vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame) +{ + u32 n_left_from, *from, *to_next; + nat44_ei_hairpin_src_next_t next_index; + nat44_ei_main_t *nm = &nat44_ei_main; + + from = vlib_frame_vector_args (frame); + n_left_from = frame->n_vectors; + next_index = node->cached_next_index; + + while (n_left_from > 0) + { + u32 n_left_to_next; + + vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next); + + while (n_left_from > 0 && n_left_to_next > 0) + { + u32 bi0; + vlib_buffer_t *b0; + u32 next0; + nat44_ei_interface_t *i; + u32 sw_if_index0; + + /* speculatively enqueue b0 to the current next frame */ + bi0 = from[0]; + to_next[0] = bi0; + from += 1; + to_next += 1; + n_left_from -= 1; + n_left_to_next -= 1; + + b0 = vlib_get_buffer (vm, bi0); + sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX]; + vnet_feature_next (&next0, b0); + + pool_foreach (i, nm->output_feature_interfaces) + { + /* Only packets from NAT inside interface */ + if ((nat44_ei_interface_is_inside (i)) && + (sw_if_index0 == i->sw_if_index)) + { + if (PREDICT_FALSE ((vnet_buffer (b0)->snat.flags) & + NAT44_EI_FLAG_HAIRPINNING)) + { + if (PREDICT_TRUE (nm->num_workers > 1)) + next0 = NAT44_EI_HAIRPIN_SRC_NEXT_SNAT_IN2OUT_WH; + else + next0 = NAT44_EI_HAIRPIN_SRC_NEXT_SNAT_IN2OUT; + } + break; + } + } + + if (next0 != NAT44_EI_HAIRPIN_SRC_NEXT_DROP) + { + vlib_increment_simple_counter ( + &nm->counters.hairpinning, vm->thread_index, sw_if_index0, 1); + } + + /* verify speculative enqueue, maybe switch current next frame */ + vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next, + n_left_to_next, bi0, next0); + } + + vlib_put_next_frame (vm, node, next_index, n_left_to_next); + } + + return frame->n_vectors; +} + +VLIB_NODE_FN (nat44_ei_hairpin_dst_node) +(vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame) +{ + u32 n_left_from, *from, *to_next; + u32 thread_index = vm->thread_index; + nat44_ei_hairpin_next_t next_index; + nat44_ei_main_t *nm = &nat44_ei_main; + + from = vlib_frame_vector_args (frame); + n_left_from = frame->n_vectors; + next_index = node->cached_next_index; + + while (n_left_from > 0) + { + u32 n_left_to_next; + + vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next); + + while (n_left_from > 0 && n_left_to_next > 0) + { + u32 bi0; + vlib_buffer_t *b0; + u32 next0; + ip4_header_t *ip0; + u32 proto0; + u32 sw_if_index0; + u32 required_thread_index = thread_index; + + /* speculatively enqueue b0 to the current next frame */ + bi0 = from[0]; + to_next[0] = bi0; + from += 1; + to_next += 1; + n_left_from -= 1; + n_left_to_next -= 1; + + b0 = vlib_get_buffer (vm, bi0); + next0 = NAT44_EI_HAIRPIN_NEXT_LOOKUP; + ip0 = vlib_buffer_get_current (b0); + sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX]; + + proto0 = ip_proto_to_nat_proto (ip0->protocol); + + vnet_buffer (b0)->snat.flags = 0; + if (PREDICT_FALSE (nat44_ei_is_hairpinning (nm, &ip0->dst_address))) + { + if (proto0 == NAT_PROTOCOL_TCP || proto0 == NAT_PROTOCOL_UDP) + { + udp_header_t *udp0 = ip4_next_header (ip0); + tcp_header_t *tcp0 = (tcp_header_t *) udp0; + + nat44_ei_hairpinning (vm, node, nm, thread_index, b0, ip0, + udp0, tcp0, proto0, 1 /* do_trace */, + &required_thread_index); + } + else if (proto0 == NAT_PROTOCOL_ICMP) + { + icmp46_header_t *icmp0 = ip4_next_header (ip0); + + nat44_ei_icmp_hairpinning (nm, b0, thread_index, ip0, icmp0, + &required_thread_index); + } + else + { + nat44_ei_hairpinning_unknown_proto (nm, b0, ip0); + } + + vnet_buffer (b0)->snat.flags = NAT44_EI_FLAG_HAIRPINNING; + } + + if (thread_index != required_thread_index) + { + vnet_buffer (b0)->snat.required_thread_index = + required_thread_index; + next0 = NAT44_EI_HAIRPIN_NEXT_HANDOFF; + } + + if (next0 != NAT44_EI_HAIRPIN_NEXT_DROP) + { + vlib_increment_simple_counter ( + &nm->counters.hairpinning, vm->thread_index, sw_if_index0, 1); + } + + /* verify speculative enqueue, maybe switch current next frame */ + vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next, + n_left_to_next, bi0, next0); + } + + vlib_put_next_frame (vm, node, next_index, n_left_to_next); + } + + return frame->n_vectors; +} + +VLIB_NODE_FN (nat44_ei_hairpinning_node) +(vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame) +{ + u32 n_left_from, *from, *to_next; + u32 thread_index = vm->thread_index; + nat44_ei_hairpin_next_t next_index; + nat44_ei_main_t *nm = &nat44_ei_main; + vnet_feature_main_t *fm = &feature_main; + u8 arc_index = vnet_feat_arc_ip4_local.feature_arc_index; + vnet_feature_config_main_t *cm = &fm->feature_config_mains[arc_index]; + + from = vlib_frame_vector_args (frame); + n_left_from = frame->n_vectors; + next_index = node->cached_next_index; + + while (n_left_from > 0) + { + u32 n_left_to_next; + + vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next); + + while (n_left_from > 0 && n_left_to_next > 0) + { + u32 bi0; + vlib_buffer_t *b0; + u32 next0; + ip4_header_t *ip0; + u32 proto0; + udp_header_t *udp0; + tcp_header_t *tcp0; + u32 sw_if_index0; + u32 required_thread_index = thread_index; + + /* speculatively enqueue b0 to the current next frame */ + bi0 = from[0]; + to_next[0] = bi0; + from += 1; + to_next += 1; + n_left_from -= 1; + n_left_to_next -= 1; + + b0 = vlib_get_buffer (vm, bi0); + ip0 = vlib_buffer_get_current (b0); + udp0 = ip4_next_header (ip0); + tcp0 = (tcp_header_t *) udp0; + sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX]; + + proto0 = ip_proto_to_nat_proto (ip0->protocol); + int next0_resolved = 0; + + if (nat44_ei_hairpinning (vm, node, nm, thread_index, b0, ip0, udp0, + tcp0, proto0, 1 /* do_trace */, + &required_thread_index)) + { + next0 = NAT44_EI_HAIRPIN_NEXT_LOOKUP; + next0_resolved = 1; + } + + if (thread_index != required_thread_index) + { + vnet_buffer (b0)->snat.required_thread_index = + required_thread_index; + next0 = NAT44_EI_HAIRPIN_NEXT_HANDOFF; + next0_resolved = 1; + } + + if (!next0_resolved) + vnet_get_config_data (&cm->config_main, &b0->current_config_index, + &next0, 0); + + if (next0 != NAT44_EI_HAIRPIN_NEXT_DROP) + { + vlib_increment_simple_counter ( + &nm->counters.hairpinning, vm->thread_index, sw_if_index0, 1); + } + + /* verify speculative enqueue, maybe switch current next frame */ + vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next, + n_left_to_next, bi0, next0); + } + + vlib_put_next_frame (vm, node, next_index, n_left_to_next); + } + + return frame->n_vectors; +} + +VLIB_NODE_FN (nat44_ei_hairpinning_dst_handoff_node) +(vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame) +{ + return nat44_ei_hairpinning_handoff_fn_inline ( + vm, node, frame, nat44_ei_main.hairpin_dst_fq_index); +} + +VLIB_NODE_FN (nat44_ei_hairpinning_handoff_node) +(vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame) +{ + return nat44_ei_hairpinning_handoff_fn_inline ( + vm, node, frame, nat44_ei_main.hairpinning_fq_index); +} + +VLIB_REGISTER_NODE (nat44_ei_hairpinning_dst_handoff_node) = { + .name = "nat44-ei-hairpin-dst-handoff", + .vector_size = sizeof (u32), + .n_errors = ARRAY_LEN(nat44_ei_hairpinning_handoff_error_strings), + .error_strings = nat44_ei_hairpinning_handoff_error_strings, + .format_trace = format_nat44_ei_hairpinning_handoff_trace, + + .n_next_nodes = 1, + + .next_nodes = { + [0] = "error-drop", + }, +}; + +VLIB_REGISTER_NODE (nat44_ei_hairpinning_handoff_node) = { + .name = "nat44-ei-hairpinning-handoff", + .vector_size = sizeof (u32), + .n_errors = ARRAY_LEN(nat44_ei_hairpinning_handoff_error_strings), + .error_strings = nat44_ei_hairpinning_handoff_error_strings, + .format_trace = format_nat44_ei_hairpinning_handoff_trace, + + .n_next_nodes = 1, + + .next_nodes = { + [0] = "error-drop", + }, +}; + +VLIB_REGISTER_NODE (nat44_ei_hairpin_src_node) = { + .name = "nat44-ei-hairpin-src", + .vector_size = sizeof (u32), + .type = VLIB_NODE_TYPE_INTERNAL, + .n_next_nodes = NAT44_EI_HAIRPIN_SRC_N_NEXT, + .next_nodes = { + [NAT44_EI_HAIRPIN_SRC_NEXT_DROP] = "error-drop", + [NAT44_EI_HAIRPIN_SRC_NEXT_SNAT_IN2OUT] = "nat44-ei-in2out-output", + [NAT44_EI_HAIRPIN_SRC_NEXT_INTERFACE_OUTPUT] = "interface-output", + [NAT44_EI_HAIRPIN_SRC_NEXT_SNAT_IN2OUT_WH] = "nat44-ei-in2out-output-worker-handoff", + }, +}; + +VLIB_REGISTER_NODE (nat44_ei_hairpin_dst_node) = { + .name = "nat44-ei-hairpin-dst", + .vector_size = sizeof (u32), + .type = VLIB_NODE_TYPE_INTERNAL, + .format_trace = format_nat44_ei_hairpin_trace, + .n_next_nodes = NAT44_EI_HAIRPIN_N_NEXT, + .next_nodes = { + [NAT44_EI_HAIRPIN_NEXT_DROP] = "error-drop", + [NAT44_EI_HAIRPIN_NEXT_LOOKUP] = "ip4-lookup", + [NAT44_EI_HAIRPIN_NEXT_HANDOFF] = "nat44-ei-hairpin-dst-handoff", + }, +}; + +VLIB_REGISTER_NODE (nat44_ei_hairpinning_node) = { + .name = "nat44-ei-hairpinning", + .vector_size = sizeof (u32), + .type = VLIB_NODE_TYPE_INTERNAL, + .format_trace = format_nat44_ei_hairpin_trace, + .n_next_nodes = NAT44_EI_HAIRPIN_N_NEXT, + .next_nodes = { + [NAT44_EI_HAIRPIN_NEXT_DROP] = "error-drop", + [NAT44_EI_HAIRPIN_NEXT_LOOKUP] = "ip4-lookup", + [NAT44_EI_HAIRPIN_NEXT_HANDOFF] = "nat44-ei-hairpinning-handoff", + }, +}; + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/src/plugins/nat/nat44-ei/nat44_ei_hairpinning.h b/src/plugins/nat/nat44-ei/nat44_ei_hairpinning.h new file mode 100644 index 00000000000..bdd2d210200 --- /dev/null +++ b/src/plugins/nat/nat44-ei/nat44_ei_hairpinning.h @@ -0,0 +1,92 @@ +#ifndef __included_nat44_ei_hairpinning_h__ +#define __included_nat44_ei_hairpinning_h__ + +#include + +#define foreach_nat44_ei_hairpinning_handoff_error \ + _ (CONGESTION_DROP, "congestion drop") + +typedef enum +{ +#define _(sym, str) NAT44_EI_HAIRPINNING_HANDOFF_ERROR_##sym, + foreach_nat44_ei_hairpinning_handoff_error +#undef _ + NAT44_EI_HAIRPINNING_HANDOFF_N_ERROR, +} nat44_ei_hairpinning_handoff_error_t; + +static char *nat44_ei_hairpinning_handoff_error_strings[] = { +#define _(sym, string) string, + foreach_nat44_ei_hairpinning_handoff_error +#undef _ +}; + +typedef struct +{ + u32 next_worker_index; +} nat44_ei_hairpinning_handoff_trace_t; + +static u8 * +format_nat44_ei_hairpinning_handoff_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 *); + nat44_ei_hairpinning_handoff_trace_t *t = + va_arg (*args, nat44_ei_hairpinning_handoff_trace_t *); + + s = format (s, "nat44-ei-hairpinning-handoff: next-worker %d", + t->next_worker_index); + + return s; +} + +always_inline uword +nat44_ei_hairpinning_handoff_fn_inline (vlib_main_t *vm, + vlib_node_runtime_t *node, + vlib_frame_t *frame, u32 fq_index) +{ + vlib_buffer_t *bufs[VLIB_FRAME_SIZE], **b; + u32 n_enq, n_left_from, *from; + u16 thread_indices[VLIB_FRAME_SIZE], *ti; + + from = vlib_frame_vector_args (frame); + n_left_from = frame->n_vectors; + vlib_get_buffers (vm, from, bufs, n_left_from); + + b = bufs; + ti = thread_indices; + + while (n_left_from > 0) + { + ti[0] = vnet_buffer (b[0])->snat.required_thread_index; + + if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE) && + (b[0]->flags & VLIB_BUFFER_IS_TRACED))) + { + nat44_ei_hairpinning_handoff_trace_t *t = + vlib_add_trace (vm, node, b[0], sizeof (*t)); + t->next_worker_index = ti[0]; + } + + n_left_from -= 1; + ti += 1; + b += 1; + } + n_enq = vlib_buffer_enqueue_to_thread (vm, fq_index, from, thread_indices, + frame->n_vectors, 1); + + if (n_enq < frame->n_vectors) + vlib_node_increment_counter ( + vm, node->node_index, NAT44_EI_HAIRPINNING_HANDOFF_ERROR_CONGESTION_DROP, + frame->n_vectors - n_enq); + return frame->n_vectors; +} + +#endif // __included_nat44_ei_hairpinning_h__ + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/src/plugins/nat/nat44-ei/nat44_ei_handoff.c b/src/plugins/nat/nat44-ei/nat44_ei_handoff.c new file mode 100644 index 00000000000..6b8db37d414 --- /dev/null +++ b/src/plugins/nat/nat44-ei/nat44_ei_handoff.c @@ -0,0 +1,326 @@ +/* + * Copyright (c) 2020 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include + +#include + +typedef struct +{ + u32 next_worker_index; + u32 trace_index; + u8 in2out; + u8 output; +} nat44_ei_handoff_trace_t; + +#define foreach_nat44_ei_handoff_error \ + _ (CONGESTION_DROP, "congestion drop") \ + _ (SAME_WORKER, "same worker") \ + _ (DO_HANDOFF, "do handoff") + +typedef enum +{ +#define _(sym, str) NAT44_EI_HANDOFF_ERROR_##sym, + foreach_nat44_ei_handoff_error +#undef _ + NAT44_EI_HANDOFF_N_ERROR, +} nat44_ei_handoff_error_t; + +static char *nat44_ei_handoff_error_strings[] = { +#define _(sym, string) string, + foreach_nat44_ei_handoff_error +#undef _ +}; + +static u8 * +format_nat44_ei_handoff_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 *); + nat44_ei_handoff_trace_t *t = va_arg (*args, nat44_ei_handoff_trace_t *); + char *tag, *output; + + tag = t->in2out ? "IN2OUT" : "OUT2IN"; + output = t->output ? "OUTPUT-FEATURE" : ""; + s = + format (s, "NAT44_EI_%s_WORKER_HANDOFF %s: next-worker %d trace index %d", + tag, output, t->next_worker_index, t->trace_index); + + return s; +} + +static inline uword +nat44_ei_worker_handoff_fn_inline (vlib_main_t *vm, vlib_node_runtime_t *node, + vlib_frame_t *frame, u8 is_output, + u8 is_in2out) +{ + u32 n_enq, n_left_from, *from, do_handoff = 0, same_worker = 0; + + u16 thread_indices[VLIB_FRAME_SIZE], *ti = thread_indices; + vlib_buffer_t *bufs[VLIB_FRAME_SIZE], **b = bufs; + nat44_ei_main_t *nm = &nat44_ei_main; + + u32 fq_index, thread_index = vm->thread_index; + + from = vlib_frame_vector_args (frame); + n_left_from = frame->n_vectors; + + vlib_get_buffers (vm, from, b, n_left_from); + + // TODO: move to nm + // TODO: remove callbacks and use inlines that should be moved here + if (is_in2out) + { + fq_index = is_output ? nm->fq_in2out_output_index : nm->fq_in2out_index; + } + else + { + fq_index = nm->fq_out2in_index; + } + + while (n_left_from >= 4) + { + u32 arc_next0, arc_next1, arc_next2, arc_next3; + u32 sw_if_index0, sw_if_index1, sw_if_index2, sw_if_index3; + u32 rx_fib_index0, rx_fib_index1, rx_fib_index2, rx_fib_index3; + u32 iph_offset0 = 0, iph_offset1 = 0, iph_offset2 = 0, iph_offset3 = 0; + ip4_header_t *ip0, *ip1, *ip2, *ip3; + + if (PREDICT_TRUE (n_left_from >= 8)) + { + vlib_prefetch_buffer_header (b[4], LOAD); + vlib_prefetch_buffer_header (b[5], LOAD); + vlib_prefetch_buffer_header (b[6], LOAD); + vlib_prefetch_buffer_header (b[7], LOAD); + CLIB_PREFETCH (&b[4]->data, CLIB_CACHE_LINE_BYTES, LOAD); + CLIB_PREFETCH (&b[5]->data, CLIB_CACHE_LINE_BYTES, LOAD); + CLIB_PREFETCH (&b[6]->data, CLIB_CACHE_LINE_BYTES, LOAD); + CLIB_PREFETCH (&b[7]->data, CLIB_CACHE_LINE_BYTES, LOAD); + } + + if (is_output) + { + iph_offset0 = vnet_buffer (b[0])->ip.save_rewrite_length; + iph_offset1 = vnet_buffer (b[1])->ip.save_rewrite_length; + iph_offset2 = vnet_buffer (b[2])->ip.save_rewrite_length; + iph_offset3 = vnet_buffer (b[3])->ip.save_rewrite_length; + } + + ip0 = + (ip4_header_t *) ((u8 *) vlib_buffer_get_current (b[0]) + iph_offset0); + ip1 = + (ip4_header_t *) ((u8 *) vlib_buffer_get_current (b[1]) + iph_offset1); + ip2 = + (ip4_header_t *) ((u8 *) vlib_buffer_get_current (b[2]) + iph_offset2); + ip3 = + (ip4_header_t *) ((u8 *) vlib_buffer_get_current (b[3]) + iph_offset3); + + vnet_feature_next (&arc_next0, b[0]); + vnet_feature_next (&arc_next1, b[1]); + vnet_feature_next (&arc_next2, b[2]); + vnet_feature_next (&arc_next3, b[3]); + + vnet_buffer2 (b[0])->nat.arc_next = arc_next0; + vnet_buffer2 (b[1])->nat.arc_next = arc_next1; + vnet_buffer2 (b[2])->nat.arc_next = arc_next2; + vnet_buffer2 (b[3])->nat.arc_next = arc_next3; + + sw_if_index0 = vnet_buffer (b[0])->sw_if_index[VLIB_RX]; + sw_if_index1 = vnet_buffer (b[1])->sw_if_index[VLIB_RX]; + sw_if_index2 = vnet_buffer (b[2])->sw_if_index[VLIB_RX]; + sw_if_index3 = vnet_buffer (b[3])->sw_if_index[VLIB_RX]; + + rx_fib_index0 = ip4_fib_table_get_index_for_sw_if_index (sw_if_index0); + rx_fib_index1 = ip4_fib_table_get_index_for_sw_if_index (sw_if_index1); + rx_fib_index2 = ip4_fib_table_get_index_for_sw_if_index (sw_if_index2); + rx_fib_index3 = ip4_fib_table_get_index_for_sw_if_index (sw_if_index3); + + if (is_in2out) + { + ti[0] = nm->worker_in2out_cb (ip0, rx_fib_index0, is_output); + ti[1] = nm->worker_in2out_cb (ip1, rx_fib_index1, is_output); + ti[2] = nm->worker_in2out_cb (ip2, rx_fib_index2, is_output); + ti[3] = nm->worker_in2out_cb (ip3, rx_fib_index3, is_output); + } + else + { + ti[0] = nm->worker_out2in_cb (b[0], ip0, rx_fib_index0, is_output); + ti[1] = nm->worker_out2in_cb (b[1], ip1, rx_fib_index1, is_output); + ti[2] = nm->worker_out2in_cb (b[2], ip2, rx_fib_index2, is_output); + ti[3] = nm->worker_out2in_cb (b[3], ip3, rx_fib_index3, is_output); + } + + if (ti[0] == thread_index) + same_worker++; + else + do_handoff++; + + if (ti[1] == thread_index) + same_worker++; + else + do_handoff++; + + if (ti[2] == thread_index) + same_worker++; + else + do_handoff++; + + if (ti[3] == thread_index) + same_worker++; + else + do_handoff++; + + b += 4; + ti += 4; + n_left_from -= 4; + } + + while (n_left_from > 0) + { + u32 arc_next0; + u32 sw_if_index0; + u32 rx_fib_index0; + u32 iph_offset0 = 0; + ip4_header_t *ip0; + + if (is_output) + iph_offset0 = vnet_buffer (b[0])->ip.save_rewrite_length; + + ip0 = + (ip4_header_t *) ((u8 *) vlib_buffer_get_current (b[0]) + iph_offset0); + + vnet_feature_next (&arc_next0, b[0]); + vnet_buffer2 (b[0])->nat.arc_next = arc_next0; + + sw_if_index0 = vnet_buffer (b[0])->sw_if_index[VLIB_RX]; + rx_fib_index0 = ip4_fib_table_get_index_for_sw_if_index (sw_if_index0); + + if (is_in2out) + { + ti[0] = nm->worker_in2out_cb (ip0, rx_fib_index0, is_output); + } + else + { + ti[0] = nm->worker_out2in_cb (b[0], ip0, rx_fib_index0, is_output); + } + + if (ti[0] == thread_index) + same_worker++; + else + do_handoff++; + + b += 1; + ti += 1; + n_left_from -= 1; + } + + if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE))) + { + u32 i; + b = bufs; + ti = thread_indices; + + for (i = 0; i < frame->n_vectors; i++) + { + if (b[0]->flags & VLIB_BUFFER_IS_TRACED) + { + nat44_ei_handoff_trace_t *t = + vlib_add_trace (vm, node, b[0], sizeof (*t)); + t->next_worker_index = ti[0]; + t->trace_index = vlib_buffer_get_trace_index (b[0]); + t->in2out = is_in2out; + t->output = is_output; + + b += 1; + ti += 1; + } + else + break; + } + } + + n_enq = vlib_buffer_enqueue_to_thread (vm, fq_index, from, thread_indices, + frame->n_vectors, 1); + + if (n_enq < frame->n_vectors) + { + vlib_node_increment_counter (vm, node->node_index, + NAT44_EI_HANDOFF_ERROR_CONGESTION_DROP, + frame->n_vectors - n_enq); + } + + vlib_node_increment_counter ( + vm, node->node_index, NAT44_EI_HANDOFF_ERROR_SAME_WORKER, same_worker); + vlib_node_increment_counter (vm, node->node_index, + NAT44_EI_HANDOFF_ERROR_DO_HANDOFF, do_handoff); + return frame->n_vectors; +} + +VLIB_NODE_FN (nat44_ei_in2out_worker_handoff_node) +(vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame) +{ + return nat44_ei_worker_handoff_fn_inline (vm, node, frame, 0, 1); +} + +VLIB_NODE_FN (nat44_ei_in2out_output_worker_handoff_node) +(vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame) +{ + return nat44_ei_worker_handoff_fn_inline (vm, node, frame, 1, 1); +} + +VLIB_NODE_FN (nat44_ei_out2in_worker_handoff_node) +(vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame) +{ + return nat44_ei_worker_handoff_fn_inline (vm, node, frame, 0, 0); +} + +VLIB_REGISTER_NODE (nat44_ei_in2out_output_worker_handoff_node) = { + .name = "nat44-ei-in2out-output-worker-handoff", + .vector_size = sizeof (u32), + .format_trace = format_nat44_ei_handoff_trace, + .type = VLIB_NODE_TYPE_INTERNAL, + .n_errors = ARRAY_LEN (nat44_ei_handoff_error_strings), + .error_strings = nat44_ei_handoff_error_strings, +}; + +VLIB_REGISTER_NODE (nat44_ei_in2out_worker_handoff_node) = { + .name = "nat44-ei-in2out-worker-handoff", + .vector_size = sizeof (u32), + .format_trace = format_nat44_ei_handoff_trace, + .type = VLIB_NODE_TYPE_INTERNAL, + .n_errors = ARRAY_LEN (nat44_ei_handoff_error_strings), + .error_strings = nat44_ei_handoff_error_strings, +}; + +VLIB_REGISTER_NODE (nat44_ei_out2in_worker_handoff_node) = { + .name = "nat44-ei-out2in-worker-handoff", + .vector_size = sizeof (u32), + .format_trace = format_nat44_ei_handoff_trace, + .type = VLIB_NODE_TYPE_INTERNAL, + .n_errors = ARRAY_LEN (nat44_ei_handoff_error_strings), + .error_strings = nat44_ei_handoff_error_strings, +}; + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/src/plugins/nat/nat44-ei/nat44_ei_in2out.c b/src/plugins/nat/nat44-ei/nat44_ei_in2out.c index 54ed1a92e8b..80beb7a49eb 100644 --- a/src/plugins/nat/nat44-ei/nat44_ei_in2out.c +++ b/src/plugins/nat/nat44-ei/nat44_ei_in2out.c @@ -14,28 +14,27 @@ */ /** * @file - * @brief NAT44 inside to outside network translation + * @brief NAT44 EI inside to outside network translation */ #include -#include +#include #include #include -#include #include -#include -#include -#include -#include -#include -#include +#include #include #include -#include + +#include +#include +#include #include -#include +#include +#include +#include typedef struct { @@ -44,15 +43,15 @@ typedef struct u32 session_index; u32 is_slow_path; u32 is_hairpinning; -} snat_in2out_trace_t; +} nat44_ei_in2out_trace_t; /* packet trace format function */ static u8 * -format_snat_in2out_trace (u8 * s, va_list * args) +format_nat44_ei_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 *); - snat_in2out_trace_t *t = va_arg (*args, snat_in2out_trace_t *); + nat44_ei_in2out_trace_t *t = va_arg (*args, nat44_ei_in2out_trace_t *); char *tag; tag = t->is_slow_path ? "NAT44_IN2OUT_SLOW_PATH" : "NAT44_IN2OUT_FAST_PATH"; @@ -68,11 +67,11 @@ format_snat_in2out_trace (u8 * s, va_list * args) } static u8 * -format_snat_in2out_fast_trace (u8 * s, va_list * args) +format_nat44_ei_in2out_fast_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 *); - snat_in2out_trace_t *t = va_arg (*args, snat_in2out_trace_t *); + nat44_ei_in2out_trace_t *t = va_arg (*args, nat44_ei_in2out_trace_t *); s = format (s, "NAT44_IN2OUT_FAST: sw_if_index %d, next index %d", t->sw_if_index, t->next_index); @@ -80,67 +79,125 @@ format_snat_in2out_fast_trace (u8 * s, va_list * args) return s; } -#define foreach_snat_in2out_error \ -_(UNSUPPORTED_PROTOCOL, "unsupported protocol") \ -_(OUT_OF_PORTS, "out of ports") \ -_(BAD_OUTSIDE_FIB, "outside VRF ID not found") \ -_(BAD_ICMP_TYPE, "unsupported ICMP type") \ -_(NO_TRANSLATION, "no translation") \ -_(MAX_SESSIONS_EXCEEDED, "maximum sessions exceeded") \ -_(CANNOT_CREATE_USER, "cannot create NAT user") +#define foreach_nat44_ei_in2out_error \ + _ (UNSUPPORTED_PROTOCOL, "unsupported protocol") \ + _ (OUT_OF_PORTS, "out of ports") \ + _ (BAD_OUTSIDE_FIB, "outside VRF ID not found") \ + _ (BAD_ICMP_TYPE, "unsupported ICMP type") \ + _ (NO_TRANSLATION, "no translation") \ + _ (MAX_SESSIONS_EXCEEDED, "maximum sessions exceeded") \ + _ (CANNOT_CREATE_USER, "cannot create NAT user") typedef enum { -#define _(sym,str) SNAT_IN2OUT_ERROR_##sym, - foreach_snat_in2out_error +#define _(sym, str) NAT44_EI_IN2OUT_ERROR_##sym, + foreach_nat44_ei_in2out_error #undef _ - SNAT_IN2OUT_N_ERROR, -} snat_in2out_error_t; + NAT44_EI_IN2OUT_N_ERROR, +} nat44_ei_in2out_error_t; -static char *snat_in2out_error_strings[] = { +static char *nat44_ei_in2out_error_strings[] = { #define _(sym,string) string, - foreach_snat_in2out_error + foreach_nat44_ei_in2out_error #undef _ }; typedef enum { - SNAT_IN2OUT_NEXT_LOOKUP, - SNAT_IN2OUT_NEXT_DROP, - SNAT_IN2OUT_NEXT_ICMP_ERROR, - SNAT_IN2OUT_NEXT_SLOW_PATH, - SNAT_IN2OUT_NEXT_HAIRPINNING_HANDOFF, - SNAT_IN2OUT_N_NEXT, -} snat_in2out_next_t; + NAT44_EI_IN2OUT_NEXT_LOOKUP, + NAT44_EI_IN2OUT_NEXT_DROP, + NAT44_EI_IN2OUT_NEXT_ICMP_ERROR, + NAT44_EI_IN2OUT_NEXT_SLOW_PATH, + NAT44_EI_IN2OUT_NEXT_HAIRPINNING_HANDOFF, + NAT44_EI_IN2OUT_N_NEXT, +} nat44_ei_in2out_next_t; typedef enum { - NAT44_IN2OUT_HAIRPINNING_FINISH_NEXT_DROP, - NAT44_IN2OUT_HAIRPINNING_FINISH_NEXT_LOOKUP, - NAT44_IN2OUT_HAIRPINNING_FINISH_N_NEXT, -} nat44_in2out_hairpinnig_finish_next_t; + NAT44_EI_IN2OUT_HAIRPINNING_FINISH_NEXT_DROP, + NAT44_EI_IN2OUT_HAIRPINNING_FINISH_NEXT_LOOKUP, + NAT44_EI_IN2OUT_HAIRPINNING_FINISH_N_NEXT, +} nat44_ei_in2out_hairpinnig_finish_next_t; + +static inline int +nat44_ei_not_translate_fast (vlib_node_runtime_t *node, u32 sw_if_index0, + ip4_header_t *ip0, u32 proto0, u32 rx_fib_index0) +{ + nat44_ei_main_t *nm = &nat44_ei_main; + + if (nm->out2in_dpo) + return 0; + + fib_node_index_t fei = FIB_NODE_INDEX_INVALID; + nat44_ei_outside_fib_t *outside_fib; + fib_prefix_t pfx = { + .fp_proto = FIB_PROTOCOL_IP4, + .fp_len = 32, + .fp_addr = { + .ip4.as_u32 = ip0->dst_address.as_u32, + } + , + }; + + /* Don't NAT packet aimed at the intfc address */ + if (PREDICT_FALSE (nat44_ei_is_interface_addr ( + nm->ip4_main, node, sw_if_index0, ip0->dst_address.as_u32))) + return 1; + + fei = fib_table_lookup (rx_fib_index0, &pfx); + if (FIB_NODE_INDEX_INVALID != fei) + { + u32 sw_if_index = fib_entry_get_resolving_interface (fei); + if (sw_if_index == ~0) + { + vec_foreach (outside_fib, nm->outside_fibs) + { + fei = fib_table_lookup (outside_fib->fib_index, &pfx); + if (FIB_NODE_INDEX_INVALID != fei) + { + sw_if_index = fib_entry_get_resolving_interface (fei); + if (sw_if_index != ~0) + break; + } + } + } + if (sw_if_index == ~0) + return 1; + + nat44_ei_interface_t *i; + pool_foreach (i, nm->interfaces) + { + /* NAT packet aimed at outside interface */ + if ((nat44_ei_interface_is_outside (i)) && + (sw_if_index == i->sw_if_index)) + return 0; + } + } + + return 1; +} static inline int -snat_not_translate (snat_main_t * sm, vlib_node_runtime_t * node, - u32 sw_if_index0, ip4_header_t * ip0, u32 proto0, - u32 rx_fib_index0, u32 thread_index) +nat44_ei_not_translate (nat44_ei_main_t *nm, vlib_node_runtime_t *node, + u32 sw_if_index0, ip4_header_t *ip0, u32 proto0, + u32 rx_fib_index0, u32 thread_index) { udp_header_t *udp0 = ip4_next_header (ip0); clib_bihash_kv_8_8_t kv0, value0; - init_nat_k (&kv0, ip0->dst_address, udp0->dst_port, sm->outside_fib_index, + init_nat_k (&kv0, ip0->dst_address, udp0->dst_port, nm->outside_fib_index, proto0); /* NAT packet aimed at external address if */ /* has active sessions */ - if (clib_bihash_search_8_8 (&sm->out2in, &kv0, &value0)) + if (clib_bihash_search_8_8 (&nm->out2in, &kv0, &value0)) { /* or is static mappings */ ip4_address_t placeholder_addr; u16 placeholder_port; u32 placeholder_fib_index; if (!nat44_ei_static_mapping_match (ip0->dst_address, udp0->dst_port, - sm->outside_fib_index, proto0, + nm->outside_fib_index, proto0, &placeholder_addr, &placeholder_port, &placeholder_fib_index, 1, 0, 0)) return 0; @@ -148,37 +205,38 @@ snat_not_translate (snat_main_t * sm, vlib_node_runtime_t * node, else return 0; - if (sm->forwarding_enabled) + if (nm->forwarding_enabled) return 1; - return snat_not_translate_fast (sm, node, sw_if_index0, ip0, proto0, - rx_fib_index0); + return nat44_ei_not_translate_fast (node, sw_if_index0, ip0, proto0, + rx_fib_index0); } static inline int -nat_not_translate_output_feature (snat_main_t * sm, ip4_header_t * ip0, - u32 proto0, u16 src_port, u16 dst_port, - u32 thread_index, u32 sw_if_index) +nat44_ei_not_translate_output_feature (nat44_ei_main_t *nm, ip4_header_t *ip0, + u32 proto0, u16 src_port, u16 dst_port, + u32 thread_index, u32 sw_if_index) { clib_bihash_kv_8_8_t kv0, value0; - snat_interface_t *i; + nat44_ei_interface_t *i; /* src NAT check */ init_nat_k (&kv0, ip0->src_address, src_port, ip4_fib_table_get_index_for_sw_if_index (sw_if_index), proto0); - if (!clib_bihash_search_8_8 (&sm->out2in, &kv0, &value0)) + if (!clib_bihash_search_8_8 (&nm->out2in, &kv0, &value0)) return 1; /* dst NAT check */ init_nat_k (&kv0, ip0->dst_address, dst_port, ip4_fib_table_get_index_for_sw_if_index (sw_if_index), proto0); - if (!clib_bihash_search_8_8 (&sm->in2out, &kv0, &value0)) + if (!clib_bihash_search_8_8 (&nm->in2out, &kv0, &value0)) { /* hairpinning */ - pool_foreach (i, sm->output_feature_interfaces) + pool_foreach (i, nm->output_feature_interfaces) { - if ((nat_interface_is_inside (i)) && (sw_if_index == i->sw_if_index)) + if ((nat44_ei_interface_is_inside (i)) && + (sw_if_index == i->sw_if_index)) return 0; } return 1; @@ -191,21 +249,22 @@ nat_not_translate_output_feature (snat_main_t * sm, ip4_header_t * ip0, int nat44_i2o_is_idle_session_cb (clib_bihash_kv_8_8_t * kv, void *arg) { - snat_main_t *sm = &snat_main; - nat44_is_idle_session_ctx_t *ctx = arg; - snat_session_t *s; + nat44_ei_main_t *nm = &nat44_ei_main; + nat44_ei_is_idle_session_ctx_t *ctx = arg; + nat44_ei_session_t *s; u64 sess_timeout_time; - snat_main_per_thread_data_t *tsm = vec_elt_at_index (sm->per_thread_data, - ctx->thread_index); + nat44_ei_main_per_thread_data_t *tnm = + vec_elt_at_index (nm->per_thread_data, ctx->thread_index); clib_bihash_kv_8_8_t s_kv; - s = pool_elt_at_index (tsm->sessions, kv->value); - sess_timeout_time = s->last_heard + (f64) nat44_session_get_timeout (sm, s); + s = pool_elt_at_index (tnm->sessions, kv->value); + sess_timeout_time = s->last_heard + (f64) nat_session_get_timeout ( + &nm->timeouts, s->nat_proto, s->state); if (ctx->now >= sess_timeout_time) { init_nat_o2i_k (&s_kv, s); - if (clib_bihash_add_del_8_8 (&sm->out2in, &s_kv, 0)) - nat_elog_warn ("out2in key del failed"); + if (clib_bihash_add_del_8_8 (&nm->out2in, &s_kv, 0)) + nat_elog_warn (nm, "out2in key del failed"); nat_ipfix_logging_nat44_ses_delete (ctx->thread_index, s->in2out.addr.as_u32, @@ -223,12 +282,12 @@ nat44_i2o_is_idle_session_cb (clib_bihash_kv_8_8_t * kv, void *arg) s->ext_host_port, s->nat_proto, s->out2in.fib_index, ctx->thread_index); - if (!snat_is_session_static (s)) - snat_free_outside_address_and_port (sm->addresses, ctx->thread_index, - &s->out2in.addr, - s->out2in.port, s->nat_proto); + if (!nat44_ei_is_session_static (s)) + nat44_ei_free_outside_address_and_port ( + nm->addresses, ctx->thread_index, &s->out2in.addr, s->out2in.port, + s->nat_proto); - nat44_delete_session (sm, s, ctx->thread_index); + nat44_ei_delete_session (nm, s, ctx->thread_index); return 1; } @@ -237,20 +296,16 @@ nat44_i2o_is_idle_session_cb (clib_bihash_kv_8_8_t * kv, void *arg) #endif static u32 -slow_path (snat_main_t * sm, vlib_buffer_t * b0, - ip4_header_t * ip0, - ip4_address_t i2o_addr, - u16 i2o_port, - u32 rx_fib_index0, - nat_protocol_t nat_proto, - snat_session_t ** sessionp, - vlib_node_runtime_t * node, u32 next0, u32 thread_index, f64 now) +slow_path (nat44_ei_main_t *nm, vlib_buffer_t *b0, ip4_header_t *ip0, + ip4_address_t i2o_addr, u16 i2o_port, u32 rx_fib_index0, + nat_protocol_t nat_proto, nat44_ei_session_t **sessionp, + vlib_node_runtime_t *node, u32 next0, u32 thread_index, f64 now) { - snat_user_t *u; - snat_session_t *s = 0; + nat44_ei_user_t *u; + nat44_ei_session_t *s = 0; clib_bihash_kv_8_8_t kv0; u8 is_sm = 0; - nat_outside_fib_t *outside_fib; + nat44_ei_outside_fib_t *outside_fib; fib_node_index_t fei = FIB_NODE_INDEX_INVALID; u8 identity_nat; fib_prefix_t pfx = { @@ -260,18 +315,18 @@ slow_path (snat_main_t * sm, vlib_buffer_t * b0, .ip4.as_u32 = ip0->dst_address.as_u32, }, }; - nat44_is_idle_session_ctx_t ctx0; + nat44_ei_is_idle_session_ctx_t ctx0; ip4_address_t sm_addr; u16 sm_port; u32 sm_fib_index; - if (PREDICT_FALSE (nat44_ei_maximum_sessions_exceeded (sm, thread_index))) + if (PREDICT_FALSE (nat44_ei_maximum_sessions_exceeded (nm, thread_index))) { - b0->error = node->errors[SNAT_IN2OUT_ERROR_MAX_SESSIONS_EXCEEDED]; + b0->error = node->errors[NAT44_EI_IN2OUT_ERROR_MAX_SESSIONS_EXCEEDED]; nat_ipfix_logging_max_sessions (thread_index, - sm->max_translations_per_thread); - nat_elog_notice ("maximum sessions exceeded"); - return SNAT_IN2OUT_NEXT_DROP; + nm->max_translations_per_thread); + nat_elog_notice (nm, "maximum sessions exceeded"); + return NAT44_EI_IN2OUT_NEXT_DROP; } /* First try to match static mapping by local address and port */ @@ -280,13 +335,13 @@ slow_path (snat_main_t * sm, vlib_buffer_t * b0, &sm_fib_index, 0, 0, &identity_nat)) { /* Try to create dynamic translation */ - if (sm->alloc_addr_and_port ( - sm->addresses, rx_fib_index0, thread_index, nat_proto, &sm_addr, - &sm_port, sm->port_per_thread, - sm->per_thread_data[thread_index].snat_thread_index)) + if (nm->alloc_addr_and_port ( + nm->addresses, rx_fib_index0, thread_index, nat_proto, &sm_addr, + &sm_port, nm->port_per_thread, + nm->per_thread_data[thread_index].snat_thread_index)) { - b0->error = node->errors[SNAT_IN2OUT_ERROR_OUT_OF_PORTS]; - return SNAT_IN2OUT_NEXT_DROP; + b0->error = node->errors[NAT44_EI_IN2OUT_ERROR_OUT_OF_PORTS]; + return NAT44_EI_IN2OUT_NEXT_DROP; } } else @@ -300,53 +355,53 @@ slow_path (snat_main_t * sm, vlib_buffer_t * b0, is_sm = 1; } - u = nat_user_get_or_create (sm, &ip0->src_address, rx_fib_index0, - thread_index); + u = nat44_ei_user_get_or_create (nm, &ip0->src_address, rx_fib_index0, + thread_index); if (!u) { - b0->error = node->errors[SNAT_IN2OUT_ERROR_CANNOT_CREATE_USER]; - return SNAT_IN2OUT_NEXT_DROP; + b0->error = node->errors[NAT44_EI_IN2OUT_ERROR_CANNOT_CREATE_USER]; + return NAT44_EI_IN2OUT_NEXT_DROP; } - s = nat_session_alloc_or_recycle (sm, u, thread_index, now); + s = nat44_ei_session_alloc_or_recycle (nm, u, thread_index, now); if (!s) { - nat44_delete_user_with_no_session (sm, u, thread_index); - nat_elog_warn ("create NAT session failed"); - return SNAT_IN2OUT_NEXT_DROP; + nat44_ei_delete_user_with_no_session (nm, u, thread_index); + nat_elog_warn (nm, "create NAT session failed"); + return NAT44_EI_IN2OUT_NEXT_DROP; } if (is_sm) - s->flags |= SNAT_SESSION_FLAG_STATIC_MAPPING; - user_session_increment (sm, u, is_sm); + s->flags |= NAT44_EI_SESSION_FLAG_STATIC_MAPPING; + nat44_ei_user_session_increment (nm, u, is_sm); s->in2out.addr = i2o_addr; s->in2out.port = i2o_port; s->in2out.fib_index = rx_fib_index0; s->nat_proto = nat_proto; s->out2in.addr = sm_addr; s->out2in.port = sm_port; - s->out2in.fib_index = sm->outside_fib_index; - switch (vec_len (sm->outside_fibs)) + s->out2in.fib_index = nm->outside_fib_index; + switch (vec_len (nm->outside_fibs)) { case 0: - s->out2in.fib_index = sm->outside_fib_index; + s->out2in.fib_index = nm->outside_fib_index; break; case 1: - s->out2in.fib_index = sm->outside_fibs[0].fib_index; + s->out2in.fib_index = nm->outside_fibs[0].fib_index; break; default: - vec_foreach (outside_fib, sm->outside_fibs) - { - fei = fib_table_lookup (outside_fib->fib_index, &pfx); - if (FIB_NODE_INDEX_INVALID != fei) - { - if (fib_entry_get_resolving_interface (fei) != ~0) - { - s->out2in.fib_index = outside_fib->fib_index; - break; - } - } - } + vec_foreach (outside_fib, nm->outside_fibs) + { + fei = fib_table_lookup (outside_fib->fib_index, &pfx); + if (FIB_NODE_INDEX_INVALID != fei) + { + if (fib_entry_get_resolving_interface (fei) != ~0) + { + s->out2in.fib_index = outside_fib->fib_index; + break; + } + } + } break; } s->ext_host_addr.as_u32 = ip0->dst_address.as_u32; @@ -357,41 +412,38 @@ slow_path (snat_main_t * sm, vlib_buffer_t * b0, ctx0.now = now; ctx0.thread_index = thread_index; init_nat_i2o_kv (&kv0, s, thread_index, - s - sm->per_thread_data[thread_index].sessions); + s - nm->per_thread_data[thread_index].sessions); if (clib_bihash_add_or_overwrite_stale_8_8 ( - &sm->in2out, &kv0, nat44_i2o_is_idle_session_cb, &ctx0)) - nat_elog_notice ("in2out key add failed"); + &nm->in2out, &kv0, nat44_i2o_is_idle_session_cb, &ctx0)) + nat_elog_notice (nm, "in2out key add failed"); init_nat_o2i_kv (&kv0, s, thread_index, - s - sm->per_thread_data[thread_index].sessions); + s - nm->per_thread_data[thread_index].sessions); if (clib_bihash_add_or_overwrite_stale_8_8 ( - &sm->out2in, &kv0, nat44_o2i_is_idle_session_cb, &ctx0)) - nat_elog_notice ("out2in key add failed"); + &nm->out2in, &kv0, nat44_o2i_is_idle_session_cb, &ctx0)) + nat_elog_notice (nm, "out2in key add failed"); /* log NAT event */ - nat_ipfix_logging_nat44_ses_create (thread_index, - s->in2out.addr.as_u32, - s->out2in.addr.as_u32, - s->nat_proto, - s->in2out.port, - s->out2in.port, s->in2out.fib_index); + nat_ipfix_logging_nat44_ses_create ( + thread_index, s->in2out.addr.as_u32, s->out2in.addr.as_u32, s->nat_proto, + s->in2out.port, s->out2in.port, s->in2out.fib_index); - nat_syslog_nat44_apmadd (s->user_index, s->in2out.fib_index, - &s->in2out.addr, s->in2out.port, &s->out2in.addr, - s->out2in.port, s->nat_proto); + nat_syslog_nat44_apmadd (s->user_index, s->in2out.fib_index, &s->in2out.addr, + s->in2out.port, &s->out2in.addr, s->out2in.port, + s->nat_proto); nat_ha_sadd (&s->in2out.addr, s->in2out.port, &s->out2in.addr, s->out2in.port, &s->ext_host_addr, s->ext_host_port, - &s->ext_host_nat_addr, s->ext_host_nat_port, - s->nat_proto, s->in2out.fib_index, s->flags, thread_index, 0); + &s->ext_host_nat_addr, s->ext_host_nat_port, s->nat_proto, + s->in2out.fib_index, s->flags, thread_index, 0); return next0; } #ifndef CLIB_MARCH_VARIANT -static_always_inline snat_in2out_error_t -icmp_get_key (vlib_buffer_t * b, ip4_header_t * ip0, - ip4_address_t * addr, u16 * port, nat_protocol_t * nat_proto) +static_always_inline nat44_ei_in2out_error_t +icmp_get_key (vlib_buffer_t *b, ip4_header_t *ip0, ip4_address_t *addr, + u16 *port, nat_protocol_t *nat_proto) { icmp46_header_t *icmp0; icmp_echo_header_t *echo0, *inner_echo0 = 0; @@ -427,7 +479,7 @@ icmp_get_key (vlib_buffer_t * b, ip4_header_t * ip0, *port = ((tcp_udp_header_t *) l4_header)->dst_port; break; default: - return SNAT_IN2OUT_ERROR_UNSUPPORTED_PROTOCOL; + return NAT44_EI_IN2OUT_ERROR_UNSUPPORTED_PROTOCOL; } } return -1; /* success */ @@ -437,7 +489,7 @@ icmp_get_key (vlib_buffer_t * b, ip4_header_t * ip0, * Get address and port values to be used for ICMP packet translation * and create session if needed * - * @param[in,out] sm NAT main + * @param[in,out] nm NAT main * @param[in,out] node NAT node runtime * @param[in] thread_index thread index * @param[in,out] b0 buffer containing packet to be translated @@ -449,15 +501,16 @@ icmp_get_key (vlib_buffer_t * b, ip4_header_t * ip0, * @param e optional parameter */ u32 -icmp_match_in2out_slow (snat_main_t *sm, vlib_node_runtime_t *node, - u32 thread_index, vlib_buffer_t *b0, ip4_header_t *ip0, - ip4_address_t *addr, u16 *port, u32 *fib_index, - nat_protocol_t *proto, snat_session_t **p_s0, - u8 *dont_translate) +nat44_ei_icmp_match_in2out_slow (vlib_node_runtime_t *node, u32 thread_index, + vlib_buffer_t *b0, ip4_header_t *ip0, + ip4_address_t *addr, u16 *port, + u32 *fib_index, nat_protocol_t *proto, + nat44_ei_session_t **p_s0, u8 *dont_translate) { - snat_main_per_thread_data_t *tsm = &sm->per_thread_data[thread_index]; + nat44_ei_main_t *nm = &nat44_ei_main; + nat44_ei_main_per_thread_data_t *tnm = &nm->per_thread_data[thread_index]; u32 sw_if_index0; - snat_session_t *s0 = 0; + nat44_ei_session_t *s0 = 0; clib_bihash_kv_8_8_t kv0, value0; u32 next0 = ~0; int err; @@ -471,18 +524,17 @@ icmp_match_in2out_slow (snat_main_t *sm, vlib_node_runtime_t *node, if (err != -1) { b0->error = node->errors[err]; - next0 = SNAT_IN2OUT_NEXT_DROP; + next0 = NAT44_EI_IN2OUT_NEXT_DROP; goto out; } init_nat_k (&kv0, *addr, *port, *fib_index, *proto); - if (clib_bihash_search_8_8 (&sm->in2out, &kv0, &value0)) + if (clib_bihash_search_8_8 (&nm->in2out, &kv0, &value0)) { if (vnet_buffer (b0)->sw_if_index[VLIB_TX] != ~0) { - if (PREDICT_FALSE - (nat_not_translate_output_feature - (sm, ip0, *proto, *port, *port, thread_index, sw_if_index0))) + if (PREDICT_FALSE (nat44_ei_not_translate_output_feature ( + nm, ip0, *proto, *port, *port, thread_index, sw_if_index0))) { *dont_translate = 1; goto out; @@ -490,9 +542,9 @@ icmp_match_in2out_slow (snat_main_t *sm, vlib_node_runtime_t *node, } else { - if (PREDICT_FALSE (snat_not_translate (sm, node, sw_if_index0, - ip0, NAT_PROTOCOL_ICMP, - *fib_index, thread_index))) + if (PREDICT_FALSE (nat44_ei_not_translate ( + nm, node, sw_if_index0, ip0, NAT_PROTOCOL_ICMP, *fib_index, + thread_index))) { *dont_translate = 1; goto out; @@ -503,16 +555,15 @@ icmp_match_in2out_slow (snat_main_t *sm, vlib_node_runtime_t *node, (icmp_type_is_error_message (vnet_buffer (b0)->ip.reass.icmp_type_or_tcp_flags))) { - b0->error = node->errors[SNAT_IN2OUT_ERROR_BAD_ICMP_TYPE]; - next0 = SNAT_IN2OUT_NEXT_DROP; + b0->error = node->errors[NAT44_EI_IN2OUT_ERROR_BAD_ICMP_TYPE]; + next0 = NAT44_EI_IN2OUT_NEXT_DROP; goto out; } - next0 = - slow_path (sm, b0, ip0, *addr, *port, *fib_index, *proto, &s0, node, - next0, thread_index, vlib_time_now (vm)); + next0 = slow_path (nm, b0, ip0, *addr, *port, *fib_index, *proto, &s0, + node, next0, thread_index, vlib_time_now (vm)); - if (PREDICT_FALSE (next0 == SNAT_IN2OUT_NEXT_DROP)) + if (PREDICT_FALSE (next0 == NAT44_EI_IN2OUT_NEXT_DROP)) goto out; if (!s0) @@ -531,12 +582,12 @@ icmp_match_in2out_slow (snat_main_t *sm, vlib_node_runtime_t *node, && !icmp_type_is_error_message (vnet_buffer (b0)->ip. reass.icmp_type_or_tcp_flags))) { - b0->error = node->errors[SNAT_IN2OUT_ERROR_BAD_ICMP_TYPE]; - next0 = SNAT_IN2OUT_NEXT_DROP; + b0->error = node->errors[NAT44_EI_IN2OUT_ERROR_BAD_ICMP_TYPE]; + next0 = NAT44_EI_IN2OUT_NEXT_DROP; goto out; } - s0 = pool_elt_at_index (tsm->sessions, + s0 = pool_elt_at_index (tnm->sessions, nat_value_get_session_index (&value0)); } @@ -554,26 +605,12 @@ out: #endif #ifndef CLIB_MARCH_VARIANT -/** - * Get address and port values to be used for ICMP packet translation - * - * @param[in] sm NAT main - * @param[in,out] node NAT node runtime - * @param[in] thread_index thread index - * @param[in,out] b0 buffer containing packet to be translated - * @param[in,out] ip0 ip header - * @param[out] p_proto protocol used for matching - * @param[out] p_value address and port after NAT translation - * @param[out] p_dont_translate if packet should not be translated - * @param d optional parameter - * @param e optional parameter - */ u32 -icmp_match_in2out_fast (snat_main_t *sm, vlib_node_runtime_t *node, - u32 thread_index, vlib_buffer_t *b0, ip4_header_t *ip0, - ip4_address_t *addr, u16 *port, u32 *fib_index, - nat_protocol_t *proto, snat_session_t **s0, - u8 *dont_translate) +nat44_ei_icmp_match_in2out_fast (vlib_node_runtime_t *node, u32 thread_index, + vlib_buffer_t *b0, ip4_header_t *ip0, + ip4_address_t *addr, u16 *port, + u32 *fib_index, nat_protocol_t *proto, + nat44_ei_session_t **s0, u8 *dont_translate) { u32 sw_if_index0; u8 is_addr_only; @@ -588,7 +625,7 @@ icmp_match_in2out_fast (snat_main_t *sm, vlib_node_runtime_t *node, if (err != -1) { b0->error = node->errors[err]; - next0 = SNAT_IN2OUT_NEXT_DROP; + next0 = NAT44_EI_IN2OUT_NEXT_DROP; goto out; } @@ -600,9 +637,8 @@ icmp_match_in2out_fast (snat_main_t *sm, vlib_node_runtime_t *node, &sm_addr, &sm_port, &sm_fib_index, 0, &is_addr_only, 0)) { - if (PREDICT_FALSE (snat_not_translate_fast (sm, node, sw_if_index0, ip0, - IP_PROTOCOL_ICMP, - *fib_index))) + if (PREDICT_FALSE (nat44_ei_not_translate_fast ( + node, sw_if_index0, ip0, IP_PROTOCOL_ICMP, *fib_index))) { *dont_translate = 1; goto out; @@ -611,12 +647,12 @@ icmp_match_in2out_fast (snat_main_t *sm, vlib_node_runtime_t *node, if (icmp_type_is_error_message (vnet_buffer (b0)->ip.reass.icmp_type_or_tcp_flags)) { - next0 = SNAT_IN2OUT_NEXT_DROP; + next0 = NAT44_EI_IN2OUT_NEXT_DROP; goto out; } - b0->error = node->errors[SNAT_IN2OUT_ERROR_NO_TRANSLATION]; - next0 = SNAT_IN2OUT_NEXT_DROP; + b0->error = node->errors[NAT44_EI_IN2OUT_ERROR_NO_TRANSLATION]; + next0 = NAT44_EI_IN2OUT_NEXT_DROP; goto out; } @@ -627,8 +663,8 @@ icmp_match_in2out_fast (snat_main_t *sm, vlib_node_runtime_t *node, && !icmp_type_is_error_message (vnet_buffer (b0)->ip. reass.icmp_type_or_tcp_flags))) { - b0->error = node->errors[SNAT_IN2OUT_ERROR_BAD_ICMP_TYPE]; - next0 = SNAT_IN2OUT_NEXT_DROP; + b0->error = node->errors[NAT44_EI_IN2OUT_ERROR_BAD_ICMP_TYPE]; + next0 = NAT44_EI_IN2OUT_NEXT_DROP; goto out; } @@ -637,23 +673,25 @@ out: } #endif -u32 icmp_in2out (snat_main_t *sm, vlib_buffer_t *b0, ip4_header_t *ip0, - icmp46_header_t *icmp0, u32 sw_if_index0, u32 rx_fib_index0, - vlib_node_runtime_t *node, u32 next0, u32 thread_index, - snat_session_t **p_s0); +u32 nat44_ei_icmp_in2out (vlib_buffer_t *b0, ip4_header_t *ip0, + icmp46_header_t *icmp0, u32 sw_if_index0, + u32 rx_fib_index0, vlib_node_runtime_t *node, + u32 next0, u32 thread_index, + nat44_ei_session_t **p_s0); #ifndef CLIB_MARCH_VARIANT u32 -icmp_in2out (snat_main_t *sm, vlib_buffer_t *b0, ip4_header_t *ip0, - icmp46_header_t *icmp0, u32 sw_if_index0, u32 rx_fib_index0, - vlib_node_runtime_t *node, u32 next0, u32 thread_index, - snat_session_t **p_s0) +nat44_ei_icmp_in2out (vlib_buffer_t *b0, ip4_header_t *ip0, + icmp46_header_t *icmp0, u32 sw_if_index0, + u32 rx_fib_index0, vlib_node_runtime_t *node, u32 next0, + u32 thread_index, nat44_ei_session_t **p_s0) { + nat44_ei_main_t *nm = &nat44_ei_main; vlib_main_t *vm = vlib_get_main (); ip4_address_t addr; u16 port; u32 fib_index; - nat_protocol_t protocol; + nat_protocol_t proto; icmp_echo_header_t *echo0, *inner_echo0 = 0; ip4_header_t *inner_ip0; void *l4_header = 0; @@ -669,12 +707,22 @@ icmp_in2out (snat_main_t *sm, vlib_buffer_t *b0, ip4_header_t *ip0, echo0 = (icmp_echo_header_t *) (icmp0 + 1); - next0_tmp = - sm->icmp_match_in2out_cb (sm, node, thread_index, b0, ip0, &addr, &port, - &fib_index, &protocol, p_s0, &dont_translate); + if (PREDICT_TRUE (nm->pat)) + { + next0_tmp = nat44_ei_icmp_match_in2out_slow ( + node, thread_index, b0, ip0, &addr, &port, &fib_index, &proto, p_s0, + &dont_translate); + } + else + { + next0_tmp = nat44_ei_icmp_match_in2out_fast ( + node, thread_index, b0, ip0, &addr, &port, &fib_index, &proto, p_s0, + &dont_translate); + } + if (next0_tmp != ~0) next0 = next0_tmp; - if (next0 == SNAT_IN2OUT_NEXT_DROP || dont_translate) + if (next0 == NAT44_EI_IN2OUT_NEXT_DROP || dont_translate) goto out; if (PREDICT_TRUE (!ip4_is_fragment (ip0))) @@ -688,7 +736,7 @@ icmp_in2out (snat_main_t *sm, vlib_buffer_t *b0, ip4_header_t *ip0, checksum0 = ~ip_csum_fold (sum0); if (PREDICT_FALSE (checksum0 != 0 && checksum0 != 0xffff)) { - next0 = SNAT_IN2OUT_NEXT_DROP; + next0 = NAT44_EI_IN2OUT_NEXT_DROP; goto out; } } @@ -729,7 +777,7 @@ icmp_in2out (snat_main_t *sm, vlib_buffer_t *b0, ip4_header_t *ip0, if (!ip4_header_checksum_is_valid (inner_ip0)) { - next0 = SNAT_IN2OUT_NEXT_DROP; + next0 = NAT44_EI_IN2OUT_NEXT_DROP; goto out; } @@ -755,7 +803,7 @@ icmp_in2out (snat_main_t *sm, vlib_buffer_t *b0, ip4_header_t *ip0, checksum); icmp0->checksum = ip_csum_fold (sum0); - switch (protocol) + switch (proto) { case NAT_PROTOCOL_ICMP: inner_icmp0 = (icmp46_header_t *) l4_header; @@ -790,13 +838,13 @@ icmp_in2out (snat_main_t *sm, vlib_buffer_t *b0, ip4_header_t *ip0, if (vnet_buffer (b0)->sw_if_index[VLIB_TX] == ~0) { - if (0 != snat_icmp_hairpinning (sm, b0, thread_index, ip0, icmp0, - &required_thread_index)) + if (0 != nat44_ei_icmp_hairpinning (nm, b0, thread_index, ip0, icmp0, + &required_thread_index)) vnet_buffer (b0)->sw_if_index[VLIB_TX] = fib_index; if (thread_index != required_thread_index) { vnet_buffer (b0)->snat.required_thread_index = required_thread_index; - next0 = SNAT_IN2OUT_NEXT_HAIRPINNING_HANDOFF; + next0 = NAT44_EI_IN2OUT_NEXT_HAIRPINNING_HANDOFF; } } @@ -805,48 +853,43 @@ out: } #endif -static inline u32 -icmp_in2out_slow_path (snat_main_t * sm, - vlib_buffer_t * b0, - ip4_header_t * ip0, - icmp46_header_t * icmp0, - u32 sw_if_index0, - u32 rx_fib_index0, - vlib_node_runtime_t * node, - u32 next0, - f64 now, u32 thread_index, snat_session_t ** p_s0) +static_always_inline u32 +nat44_ei_icmp_in2out_slow_path (nat44_ei_main_t *nm, vlib_buffer_t *b0, + ip4_header_t *ip0, icmp46_header_t *icmp0, + u32 sw_if_index0, u32 rx_fib_index0, + vlib_node_runtime_t *node, u32 next0, f64 now, + u32 thread_index, nat44_ei_session_t **p_s0) { vlib_main_t *vm = vlib_get_main (); - next0 = icmp_in2out (sm, b0, ip0, icmp0, sw_if_index0, rx_fib_index0, node, - next0, thread_index, p_s0); - snat_session_t *s0 = *p_s0; - if (PREDICT_TRUE (next0 != SNAT_IN2OUT_NEXT_DROP && s0)) + next0 = nat44_ei_icmp_in2out (b0, ip0, icmp0, sw_if_index0, rx_fib_index0, + node, next0, thread_index, p_s0); + nat44_ei_session_t *s0 = *p_s0; + if (PREDICT_TRUE (next0 != NAT44_EI_IN2OUT_NEXT_DROP && s0)) { /* Accounting */ nat44_ei_session_update_counters ( s0, now, vlib_buffer_length_in_chain (vm, b0), thread_index); /* Per-user LRU list maintenance */ - nat44_session_update_lru (sm, s0, thread_index); + nat44_ei_session_update_lru (nm, s0, thread_index); } return next0; } static int -nat_in2out_sm_unknown_proto (snat_main_t * sm, - vlib_buffer_t * b, - ip4_header_t * ip, u32 rx_fib_index) +nat_in2out_sm_unknown_proto (nat44_ei_main_t *nm, vlib_buffer_t *b, + ip4_header_t *ip, u32 rx_fib_index) { clib_bihash_kv_8_8_t kv, value; - snat_static_mapping_t *m; + nat44_ei_static_mapping_t *m; u32 old_addr, new_addr; ip_csum_t sum; init_nat_k (&kv, ip->src_address, 0, rx_fib_index, 0); - if (clib_bihash_search_8_8 (&sm->static_mapping_by_local, &kv, &value)) + if (clib_bihash_search_8_8 (&nm->static_mapping_by_local, &kv, &value)) return 1; - m = pool_elt_at_index (sm->static_mappings, value.value); + m = pool_elt_at_index (nm->static_mappings, value.value); old_addr = ip->src_address.as_u32; new_addr = ip->src_address.as_u32 = m->external_addr.as_u32; @@ -859,20 +902,19 @@ nat_in2out_sm_unknown_proto (snat_main_t * sm, if (vnet_buffer (b)->sw_if_index[VLIB_TX] == ~0) { vnet_buffer (b)->sw_if_index[VLIB_TX] = m->fib_index; - nat_hairpinning_sm_unknown_proto (sm, b, ip); + nat44_ei_hairpinning_sm_unknown_proto (nm, b, ip); } return 0; } static inline uword -snat_in2out_node_fn_inline (vlib_main_t * vm, - vlib_node_runtime_t * node, - vlib_frame_t * frame, int is_slow_path, - int is_output_feature) +nat44_ei_in2out_node_fn_inline (vlib_main_t *vm, vlib_node_runtime_t *node, + vlib_frame_t *frame, int is_slow_path, + int is_output_feature) { u32 n_left_from, *from; - snat_main_t *sm = &snat_main; + nat44_ei_main_t *nm = &nat44_ei_main; f64 now = vlib_time_now (vm); u32 thread_index = vm->thread_index; @@ -897,7 +939,7 @@ snat_in2out_node_fn_inline (vlib_main_t * vm, icmp46_header_t *icmp0, *icmp1; u32 rx_fib_index0, rx_fib_index1; u32 proto0, proto1; - snat_session_t *s0 = 0, *s1 = 0; + nat44_ei_session_t *s0 = 0, *s1 = 0; clib_bihash_kv_8_8_t kv0, value0, kv1, value1; u32 iph_offset0 = 0, iph_offset1 = 0; @@ -932,10 +974,10 @@ snat_in2out_node_fn_inline (vlib_main_t * vm, icmp0 = (icmp46_header_t *) udp0; sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX]; - rx_fib_index0 = vec_elt (sm->ip4_main->fib_index_by_sw_if_index, - sw_if_index0); + rx_fib_index0 = + vec_elt (nm->ip4_main->fib_index_by_sw_if_index, sw_if_index0); - next0 = next1 = SNAT_IN2OUT_NEXT_LOOKUP; + next0 = next1 = NAT44_EI_IN2OUT_NEXT_LOOKUP; if (PREDICT_FALSE (ip0->ttl == 1)) { @@ -943,7 +985,7 @@ snat_in2out_node_fn_inline (vlib_main_t * vm, icmp4_error_set_vnet_buffer (b0, ICMP4_time_exceeded, ICMP4_time_exceeded_ttl_exceeded_in_transit, 0); - next0 = SNAT_IN2OUT_NEXT_ICMP_ERROR; + next0 = NAT44_EI_IN2OUT_NEXT_ICMP_ERROR; goto trace00; } @@ -954,30 +996,28 @@ snat_in2out_node_fn_inline (vlib_main_t * vm, { if (PREDICT_FALSE (proto0 == NAT_PROTOCOL_OTHER)) { - if (nat_in2out_sm_unknown_proto (sm, b0, ip0, rx_fib_index0)) + if (nat_in2out_sm_unknown_proto (nm, b0, ip0, rx_fib_index0)) { - next0 = SNAT_IN2OUT_NEXT_DROP; + next0 = NAT44_EI_IN2OUT_NEXT_DROP; b0->error = - node->errors[SNAT_IN2OUT_ERROR_UNSUPPORTED_PROTOCOL]; + node->errors[NAT44_EI_IN2OUT_ERROR_UNSUPPORTED_PROTOCOL]; } - vlib_increment_simple_counter (is_slow_path ? &sm-> - counters.slowpath.in2out. - other : &sm->counters.fastpath. - in2out.other, thread_index, - sw_if_index0, 1); + vlib_increment_simple_counter ( + is_slow_path ? &nm->counters.slowpath.in2out.other : + &nm->counters.fastpath.in2out.other, + thread_index, sw_if_index0, 1); goto trace00; } if (PREDICT_FALSE (proto0 == NAT_PROTOCOL_ICMP)) { - next0 = icmp_in2out_slow_path - (sm, b0, ip0, icmp0, sw_if_index0, rx_fib_index0, - node, next0, now, thread_index, &s0); - vlib_increment_simple_counter (is_slow_path ? &sm-> - counters.slowpath.in2out. - icmp : &sm->counters.fastpath. - in2out.icmp, thread_index, - sw_if_index0, 1); + next0 = nat44_ei_icmp_in2out_slow_path ( + nm, b0, ip0, icmp0, sw_if_index0, rx_fib_index0, node, next0, + now, thread_index, &s0); + vlib_increment_simple_counter ( + is_slow_path ? &nm->counters.slowpath.in2out.icmp : + &nm->counters.fastpath.in2out.icmp, + thread_index, sw_if_index0, 1); goto trace00; } } @@ -985,13 +1025,13 @@ snat_in2out_node_fn_inline (vlib_main_t * vm, { if (PREDICT_FALSE (proto0 == NAT_PROTOCOL_OTHER)) { - next0 = SNAT_IN2OUT_NEXT_SLOW_PATH; + next0 = NAT44_EI_IN2OUT_NEXT_SLOW_PATH; goto trace00; } if (PREDICT_FALSE (proto0 == NAT_PROTOCOL_ICMP)) { - next0 = SNAT_IN2OUT_NEXT_SLOW_PATH; + next0 = NAT44_EI_IN2OUT_NEXT_SLOW_PATH; goto trace00; } } @@ -999,19 +1039,18 @@ snat_in2out_node_fn_inline (vlib_main_t * vm, init_nat_k (&kv0, ip0->src_address, vnet_buffer (b0)->ip.reass.l4_src_port, rx_fib_index0, proto0); - if (PREDICT_FALSE (clib_bihash_search_8_8 (&sm->in2out, &kv0, &value0) != + if (PREDICT_FALSE (clib_bihash_search_8_8 (&nm->in2out, &kv0, &value0) != 0)) { if (is_slow_path) { if (is_output_feature) { - if (PREDICT_FALSE - (nat_not_translate_output_feature - (sm, ip0, proto0, + if (PREDICT_FALSE (nat44_ei_not_translate_output_feature ( + nm, ip0, proto0, vnet_buffer (b0)->ip.reass.l4_src_port, - vnet_buffer (b0)->ip.reass.l4_dst_port, - thread_index, sw_if_index0))) + vnet_buffer (b0)->ip.reass.l4_dst_port, thread_index, + sw_if_index0))) goto trace00; /* @@ -1028,19 +1067,17 @@ snat_in2out_node_fn_inline (vlib_main_t * vm, } else { - if (PREDICT_FALSE - (snat_not_translate - (sm, node, sw_if_index0, ip0, proto0, - rx_fib_index0, thread_index))) + if (PREDICT_FALSE (nat44_ei_not_translate ( + nm, node, sw_if_index0, ip0, proto0, rx_fib_index0, + thread_index))) goto trace00; } - next0 = slow_path (sm, b0, ip0, - ip0->src_address, + next0 = slow_path (nm, b0, ip0, ip0->src_address, vnet_buffer (b0)->ip.reass.l4_src_port, - rx_fib_index0, - proto0, &s0, node, next0, thread_index, now); - if (PREDICT_FALSE (next0 == SNAT_IN2OUT_NEXT_DROP)) + rx_fib_index0, proto0, &s0, node, next0, + thread_index, now); + if (PREDICT_FALSE (next0 == NAT44_EI_IN2OUT_NEXT_DROP)) goto trace00; if (PREDICT_FALSE (!s0)) @@ -1048,12 +1085,12 @@ snat_in2out_node_fn_inline (vlib_main_t * vm, } else { - next0 = SNAT_IN2OUT_NEXT_SLOW_PATH; + next0 = NAT44_EI_IN2OUT_NEXT_SLOW_PATH; goto trace00; } } else - s0 = pool_elt_at_index (sm->per_thread_data[thread_index].sessions, + s0 = pool_elt_at_index (nm->per_thread_data[thread_index].sessions, nat_value_get_session_index (&value0)); b0->flags |= VNET_BUFFER_F_IS_NATED; @@ -1083,12 +1120,12 @@ snat_in2out_node_fn_inline (vlib_main_t * vm, sum0 = ip_csum_update (sum0, old_port0, new_port0, ip4_header_t /* cheat */ , length /* changed member */ ); - mss_clamping (sm->mss_clamping, tcp0, &sum0); + mss_clamping (nm->mss_clamping, tcp0, &sum0); tcp0->checksum = ip_csum_fold (sum0); } - vlib_increment_simple_counter (is_slow_path ? &sm-> - counters.slowpath.in2out.tcp : &sm-> - counters.fastpath.in2out.tcp, + vlib_increment_simple_counter (is_slow_path ? + &nm->counters.slowpath.in2out.tcp : + &nm->counters.fastpath.in2out.tcp, thread_index, sw_if_index0, 1); } else @@ -1110,9 +1147,9 @@ snat_in2out_node_fn_inline (vlib_main_t * vm, udp0->checksum = ip_csum_fold (sum0); } } - vlib_increment_simple_counter (is_slow_path ? &sm-> - counters.slowpath.in2out.udp : &sm-> - counters.fastpath.in2out.udp, + vlib_increment_simple_counter (is_slow_path ? + &nm->counters.slowpath.in2out.udp : + &nm->counters.fastpath.in2out.udp, thread_index, sw_if_index0, 1); } @@ -1120,29 +1157,28 @@ snat_in2out_node_fn_inline (vlib_main_t * vm, nat44_ei_session_update_counters ( s0, now, vlib_buffer_length_in_chain (vm, b0), thread_index); /* Per-user LRU list maintenance */ - nat44_session_update_lru (sm, s0, thread_index); + nat44_ei_session_update_lru (nm, s0, thread_index); trace00: if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE) && (b0->flags & VLIB_BUFFER_IS_TRACED))) { - snat_in2out_trace_t *t = vlib_add_trace (vm, node, b0, sizeof (*t)); + nat44_ei_in2out_trace_t *t = + vlib_add_trace (vm, node, b0, sizeof (*t)); t->is_slow_path = is_slow_path; t->sw_if_index = sw_if_index0; t->next_index = next0; t->session_index = ~0; if (s0) - t->session_index = - s0 - sm->per_thread_data[thread_index].sessions; + t->session_index = s0 - nm->per_thread_data[thread_index].sessions; } - if (next0 == SNAT_IN2OUT_NEXT_DROP) + if (next0 == NAT44_EI_IN2OUT_NEXT_DROP) { - vlib_increment_simple_counter (is_slow_path ? &sm-> - counters.slowpath.in2out. - drops : &sm->counters.fastpath. - in2out.drops, thread_index, - sw_if_index0, 1); + vlib_increment_simple_counter ( + is_slow_path ? &nm->counters.slowpath.in2out.drops : + &nm->counters.fastpath.in2out.drops, + thread_index, sw_if_index0, 1); } if (is_output_feature) @@ -1156,8 +1192,8 @@ snat_in2out_node_fn_inline (vlib_main_t * vm, icmp1 = (icmp46_header_t *) udp1; sw_if_index1 = vnet_buffer (b1)->sw_if_index[VLIB_RX]; - rx_fib_index1 = vec_elt (sm->ip4_main->fib_index_by_sw_if_index, - sw_if_index1); + rx_fib_index1 = + vec_elt (nm->ip4_main->fib_index_by_sw_if_index, sw_if_index1); if (PREDICT_FALSE (ip1->ttl == 1)) { @@ -1165,7 +1201,7 @@ snat_in2out_node_fn_inline (vlib_main_t * vm, icmp4_error_set_vnet_buffer (b1, ICMP4_time_exceeded, ICMP4_time_exceeded_ttl_exceeded_in_transit, 0); - next1 = SNAT_IN2OUT_NEXT_ICMP_ERROR; + next1 = NAT44_EI_IN2OUT_NEXT_ICMP_ERROR; goto trace01; } @@ -1176,30 +1212,28 @@ snat_in2out_node_fn_inline (vlib_main_t * vm, { if (PREDICT_FALSE (proto1 == NAT_PROTOCOL_OTHER)) { - if (nat_in2out_sm_unknown_proto (sm, b1, ip1, rx_fib_index1)) + if (nat_in2out_sm_unknown_proto (nm, b1, ip1, rx_fib_index1)) { - next1 = SNAT_IN2OUT_NEXT_DROP; + next1 = NAT44_EI_IN2OUT_NEXT_DROP; b1->error = - node->errors[SNAT_IN2OUT_ERROR_UNSUPPORTED_PROTOCOL]; + node->errors[NAT44_EI_IN2OUT_ERROR_UNSUPPORTED_PROTOCOL]; } - vlib_increment_simple_counter (is_slow_path ? &sm-> - counters.slowpath.in2out. - other : &sm->counters.fastpath. - in2out.other, thread_index, - sw_if_index1, 1); + vlib_increment_simple_counter ( + is_slow_path ? &nm->counters.slowpath.in2out.other : + &nm->counters.fastpath.in2out.other, + thread_index, sw_if_index1, 1); goto trace01; } if (PREDICT_FALSE (proto1 == NAT_PROTOCOL_ICMP)) { - next1 = icmp_in2out_slow_path - (sm, b1, ip1, icmp1, sw_if_index1, rx_fib_index1, node, - next1, now, thread_index, &s1); - vlib_increment_simple_counter (is_slow_path ? &sm-> - counters.slowpath.in2out. - icmp : &sm->counters.fastpath. - in2out.icmp, thread_index, - sw_if_index1, 1); + next1 = nat44_ei_icmp_in2out_slow_path ( + nm, b1, ip1, icmp1, sw_if_index1, rx_fib_index1, node, next1, + now, thread_index, &s1); + vlib_increment_simple_counter ( + is_slow_path ? &nm->counters.slowpath.in2out.icmp : + &nm->counters.fastpath.in2out.icmp, + thread_index, sw_if_index1, 1); goto trace01; } } @@ -1207,13 +1241,13 @@ snat_in2out_node_fn_inline (vlib_main_t * vm, { if (PREDICT_FALSE (proto1 == NAT_PROTOCOL_OTHER)) { - next1 = SNAT_IN2OUT_NEXT_SLOW_PATH; + next1 = NAT44_EI_IN2OUT_NEXT_SLOW_PATH; goto trace01; } if (PREDICT_FALSE (proto1 == NAT_PROTOCOL_ICMP)) { - next1 = SNAT_IN2OUT_NEXT_SLOW_PATH; + next1 = NAT44_EI_IN2OUT_NEXT_SLOW_PATH; goto trace01; } } @@ -1221,19 +1255,18 @@ snat_in2out_node_fn_inline (vlib_main_t * vm, init_nat_k (&kv1, ip1->src_address, vnet_buffer (b1)->ip.reass.l4_src_port, rx_fib_index1, proto1); - if (PREDICT_FALSE (clib_bihash_search_8_8 (&sm->in2out, &kv1, &value1) != + if (PREDICT_FALSE (clib_bihash_search_8_8 (&nm->in2out, &kv1, &value1) != 0)) { if (is_slow_path) { if (is_output_feature) { - if (PREDICT_FALSE - (nat_not_translate_output_feature - (sm, ip1, proto1, + if (PREDICT_FALSE (nat44_ei_not_translate_output_feature ( + nm, ip1, proto1, vnet_buffer (b1)->ip.reass.l4_src_port, - vnet_buffer (b1)->ip.reass.l4_dst_port, - thread_index, sw_if_index1))) + vnet_buffer (b1)->ip.reass.l4_dst_port, thread_index, + sw_if_index1))) goto trace01; /* @@ -1250,19 +1283,17 @@ snat_in2out_node_fn_inline (vlib_main_t * vm, } else { - if (PREDICT_FALSE - (snat_not_translate - (sm, node, sw_if_index1, ip1, proto1, - rx_fib_index1, thread_index))) + if (PREDICT_FALSE (nat44_ei_not_translate ( + nm, node, sw_if_index1, ip1, proto1, rx_fib_index1, + thread_index))) goto trace01; } - next1 = - slow_path (sm, b1, ip1, ip1->src_address, - vnet_buffer (b1)->ip.reass.l4_src_port, - rx_fib_index1, proto1, &s1, node, next1, - thread_index, now); - if (PREDICT_FALSE (next1 == SNAT_IN2OUT_NEXT_DROP)) + next1 = slow_path (nm, b1, ip1, ip1->src_address, + vnet_buffer (b1)->ip.reass.l4_src_port, + rx_fib_index1, proto1, &s1, node, next1, + thread_index, now); + if (PREDICT_FALSE (next1 == NAT44_EI_IN2OUT_NEXT_DROP)) goto trace01; if (PREDICT_FALSE (!s1)) @@ -1270,12 +1301,12 @@ snat_in2out_node_fn_inline (vlib_main_t * vm, } else { - next1 = SNAT_IN2OUT_NEXT_SLOW_PATH; + next1 = NAT44_EI_IN2OUT_NEXT_SLOW_PATH; goto trace01; } } else - s1 = pool_elt_at_index (sm->per_thread_data[thread_index].sessions, + s1 = pool_elt_at_index (nm->per_thread_data[thread_index].sessions, nat_value_get_session_index (&value1)); b1->flags |= VNET_BUFFER_F_IS_NATED; @@ -1304,12 +1335,12 @@ snat_in2out_node_fn_inline (vlib_main_t * vm, sum1 = ip_csum_update (sum1, old_port1, new_port1, ip4_header_t /* cheat */ , length /* changed member */ ); - mss_clamping (sm->mss_clamping, tcp1, &sum1); + mss_clamping (nm->mss_clamping, tcp1, &sum1); tcp1->checksum = ip_csum_fold (sum1); } - vlib_increment_simple_counter (is_slow_path ? &sm-> - counters.slowpath.in2out.tcp : &sm-> - counters.fastpath.in2out.tcp, + vlib_increment_simple_counter (is_slow_path ? + &nm->counters.slowpath.in2out.tcp : + &nm->counters.fastpath.in2out.tcp, thread_index, sw_if_index1, 1); } else @@ -1331,9 +1362,9 @@ snat_in2out_node_fn_inline (vlib_main_t * vm, udp1->checksum = ip_csum_fold (sum1); } } - vlib_increment_simple_counter (is_slow_path ? &sm-> - counters.slowpath.in2out.udp : &sm-> - counters.fastpath.in2out.udp, + vlib_increment_simple_counter (is_slow_path ? + &nm->counters.slowpath.in2out.udp : + &nm->counters.fastpath.in2out.udp, thread_index, sw_if_index1, 1); } @@ -1341,28 +1372,27 @@ snat_in2out_node_fn_inline (vlib_main_t * vm, nat44_ei_session_update_counters ( s1, now, vlib_buffer_length_in_chain (vm, b1), thread_index); /* Per-user LRU list maintenance */ - nat44_session_update_lru (sm, s1, thread_index); + nat44_ei_session_update_lru (nm, s1, thread_index); trace01: if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE) && (b1->flags & VLIB_BUFFER_IS_TRACED))) { - snat_in2out_trace_t *t = vlib_add_trace (vm, node, b1, sizeof (*t)); + nat44_ei_in2out_trace_t *t = + vlib_add_trace (vm, node, b1, sizeof (*t)); t->sw_if_index = sw_if_index1; t->next_index = next1; t->session_index = ~0; if (s1) - t->session_index = - s1 - sm->per_thread_data[thread_index].sessions; + t->session_index = s1 - nm->per_thread_data[thread_index].sessions; } - if (next1 == SNAT_IN2OUT_NEXT_DROP) + if (next1 == NAT44_EI_IN2OUT_NEXT_DROP) { - vlib_increment_simple_counter (is_slow_path ? &sm-> - counters.slowpath.in2out. - drops : &sm->counters.fastpath. - in2out.drops, thread_index, - sw_if_index1, 1); + vlib_increment_simple_counter ( + is_slow_path ? &nm->counters.slowpath.in2out.drops : + &nm->counters.fastpath.in2out.drops, + thread_index, sw_if_index1, 1); } n_left_from -= 2; @@ -1385,13 +1415,13 @@ snat_in2out_node_fn_inline (vlib_main_t * vm, icmp46_header_t *icmp0; u32 rx_fib_index0; u32 proto0; - snat_session_t *s0 = 0; + nat44_ei_session_t *s0 = 0; clib_bihash_kv_8_8_t kv0, value0; u32 iph_offset0 = 0; b0 = *b; b++; - next0 = SNAT_IN2OUT_NEXT_LOOKUP; + next0 = NAT44_EI_IN2OUT_NEXT_LOOKUP; if (is_output_feature) iph_offset0 = vnet_buffer (b0)->ip.reass.save_rewrite_length; @@ -1404,8 +1434,8 @@ snat_in2out_node_fn_inline (vlib_main_t * vm, icmp0 = (icmp46_header_t *) udp0; sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX]; - rx_fib_index0 = vec_elt (sm->ip4_main->fib_index_by_sw_if_index, - sw_if_index0); + rx_fib_index0 = + vec_elt (nm->ip4_main->fib_index_by_sw_if_index, sw_if_index0); if (PREDICT_FALSE (ip0->ttl == 1)) { @@ -1413,7 +1443,7 @@ snat_in2out_node_fn_inline (vlib_main_t * vm, icmp4_error_set_vnet_buffer (b0, ICMP4_time_exceeded, ICMP4_time_exceeded_ttl_exceeded_in_transit, 0); - next0 = SNAT_IN2OUT_NEXT_ICMP_ERROR; + next0 = NAT44_EI_IN2OUT_NEXT_ICMP_ERROR; goto trace0; } @@ -1424,30 +1454,28 @@ snat_in2out_node_fn_inline (vlib_main_t * vm, { if (PREDICT_FALSE (proto0 == NAT_PROTOCOL_OTHER)) { - if (nat_in2out_sm_unknown_proto (sm, b0, ip0, rx_fib_index0)) + if (nat_in2out_sm_unknown_proto (nm, b0, ip0, rx_fib_index0)) { - next0 = SNAT_IN2OUT_NEXT_DROP; + next0 = NAT44_EI_IN2OUT_NEXT_DROP; b0->error = - node->errors[SNAT_IN2OUT_ERROR_UNSUPPORTED_PROTOCOL]; + node->errors[NAT44_EI_IN2OUT_ERROR_UNSUPPORTED_PROTOCOL]; } - vlib_increment_simple_counter (is_slow_path ? &sm-> - counters.slowpath.in2out. - other : &sm->counters.fastpath. - in2out.other, thread_index, - sw_if_index0, 1); + vlib_increment_simple_counter ( + is_slow_path ? &nm->counters.slowpath.in2out.other : + &nm->counters.fastpath.in2out.other, + thread_index, sw_if_index0, 1); goto trace0; } if (PREDICT_FALSE (proto0 == NAT_PROTOCOL_ICMP)) { - next0 = icmp_in2out_slow_path - (sm, b0, ip0, icmp0, sw_if_index0, rx_fib_index0, node, - next0, now, thread_index, &s0); - vlib_increment_simple_counter (is_slow_path ? &sm-> - counters.slowpath.in2out. - icmp : &sm->counters.fastpath. - in2out.icmp, thread_index, - sw_if_index0, 1); + next0 = nat44_ei_icmp_in2out_slow_path ( + nm, b0, ip0, icmp0, sw_if_index0, rx_fib_index0, node, next0, + now, thread_index, &s0); + vlib_increment_simple_counter ( + is_slow_path ? &nm->counters.slowpath.in2out.icmp : + &nm->counters.fastpath.in2out.icmp, + thread_index, sw_if_index0, 1); goto trace0; } } @@ -1455,13 +1483,13 @@ snat_in2out_node_fn_inline (vlib_main_t * vm, { if (PREDICT_FALSE (proto0 == NAT_PROTOCOL_OTHER)) { - next0 = SNAT_IN2OUT_NEXT_SLOW_PATH; + next0 = NAT44_EI_IN2OUT_NEXT_SLOW_PATH; goto trace0; } if (PREDICT_FALSE (proto0 == NAT_PROTOCOL_ICMP)) { - next0 = SNAT_IN2OUT_NEXT_SLOW_PATH; + next0 = NAT44_EI_IN2OUT_NEXT_SLOW_PATH; goto trace0; } } @@ -1470,18 +1498,17 @@ snat_in2out_node_fn_inline (vlib_main_t * vm, vnet_buffer (b0)->ip.reass.l4_src_port, rx_fib_index0, proto0); - if (clib_bihash_search_8_8 (&sm->in2out, &kv0, &value0)) + if (clib_bihash_search_8_8 (&nm->in2out, &kv0, &value0)) { if (is_slow_path) { if (is_output_feature) { - if (PREDICT_FALSE - (nat_not_translate_output_feature - (sm, ip0, proto0, + if (PREDICT_FALSE (nat44_ei_not_translate_output_feature ( + nm, ip0, proto0, vnet_buffer (b0)->ip.reass.l4_src_port, - vnet_buffer (b0)->ip.reass.l4_dst_port, - thread_index, sw_if_index0))) + vnet_buffer (b0)->ip.reass.l4_dst_port, thread_index, + sw_if_index0))) goto trace0; /* @@ -1498,20 +1525,18 @@ snat_in2out_node_fn_inline (vlib_main_t * vm, } else { - if (PREDICT_FALSE - (snat_not_translate - (sm, node, sw_if_index0, ip0, proto0, rx_fib_index0, + if (PREDICT_FALSE (nat44_ei_not_translate ( + nm, node, sw_if_index0, ip0, proto0, rx_fib_index0, thread_index))) goto trace0; } - next0 = - slow_path (sm, b0, ip0, ip0->src_address, - vnet_buffer (b0)->ip.reass.l4_src_port, - rx_fib_index0, proto0, &s0, node, next0, - thread_index, now); + next0 = slow_path (nm, b0, ip0, ip0->src_address, + vnet_buffer (b0)->ip.reass.l4_src_port, + rx_fib_index0, proto0, &s0, node, next0, + thread_index, now); - if (PREDICT_FALSE (next0 == SNAT_IN2OUT_NEXT_DROP)) + if (PREDICT_FALSE (next0 == NAT44_EI_IN2OUT_NEXT_DROP)) goto trace0; if (PREDICT_FALSE (!s0)) @@ -1519,12 +1544,12 @@ snat_in2out_node_fn_inline (vlib_main_t * vm, } else { - next0 = SNAT_IN2OUT_NEXT_SLOW_PATH; + next0 = NAT44_EI_IN2OUT_NEXT_SLOW_PATH; goto trace0; } } else - s0 = pool_elt_at_index (sm->per_thread_data[thread_index].sessions, + s0 = pool_elt_at_index (nm->per_thread_data[thread_index].sessions, nat_value_get_session_index (&value0)); b0->flags |= VNET_BUFFER_F_IS_NATED; @@ -1554,12 +1579,12 @@ snat_in2out_node_fn_inline (vlib_main_t * vm, ip_csum_update (sum0, old_port0, new_port0, ip4_header_t /* cheat */ , length /* changed member */ ); - mss_clamping (sm->mss_clamping, tcp0, &sum0); + mss_clamping (nm->mss_clamping, tcp0, &sum0); tcp0->checksum = ip_csum_fold (sum0); } - vlib_increment_simple_counter (is_slow_path ? &sm-> - counters.slowpath.in2out.tcp : &sm-> - counters.fastpath.in2out.tcp, + vlib_increment_simple_counter (is_slow_path ? + &nm->counters.slowpath.in2out.tcp : + &nm->counters.fastpath.in2out.tcp, thread_index, sw_if_index0, 1); } else @@ -1582,9 +1607,9 @@ snat_in2out_node_fn_inline (vlib_main_t * vm, udp0->checksum = ip_csum_fold (sum0); } } - vlib_increment_simple_counter (is_slow_path ? &sm-> - counters.slowpath.in2out.udp : &sm-> - counters.fastpath.in2out.udp, + vlib_increment_simple_counter (is_slow_path ? + &nm->counters.slowpath.in2out.udp : + &nm->counters.fastpath.in2out.udp, thread_index, sw_if_index0, 1); } @@ -1592,29 +1617,28 @@ snat_in2out_node_fn_inline (vlib_main_t * vm, nat44_ei_session_update_counters ( s0, now, vlib_buffer_length_in_chain (vm, b0), thread_index); /* Per-user LRU list maintenance */ - nat44_session_update_lru (sm, s0, thread_index); + nat44_ei_session_update_lru (nm, s0, thread_index); trace0: if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE) && (b0->flags & VLIB_BUFFER_IS_TRACED))) { - snat_in2out_trace_t *t = vlib_add_trace (vm, node, b0, sizeof (*t)); + nat44_ei_in2out_trace_t *t = + vlib_add_trace (vm, node, b0, sizeof (*t)); t->is_slow_path = is_slow_path; t->sw_if_index = sw_if_index0; t->next_index = next0; t->session_index = ~0; if (s0) - t->session_index = - s0 - sm->per_thread_data[thread_index].sessions; + t->session_index = s0 - nm->per_thread_data[thread_index].sessions; } - if (next0 == SNAT_IN2OUT_NEXT_DROP) + if (next0 == NAT44_EI_IN2OUT_NEXT_DROP) { - vlib_increment_simple_counter (is_slow_path ? &sm-> - counters.slowpath.in2out. - drops : &sm->counters.fastpath. - in2out.drops, thread_index, - sw_if_index0, 1); + vlib_increment_simple_counter ( + is_slow_path ? &nm->counters.slowpath.in2out.drops : + &nm->counters.fastpath.in2out.drops, + thread_index, sw_if_index0, 1); } n_left_from--; @@ -1627,138 +1651,133 @@ snat_in2out_node_fn_inline (vlib_main_t * vm, return frame->n_vectors; } -VLIB_NODE_FN (snat_in2out_node) (vlib_main_t * vm, - vlib_node_runtime_t * node, - vlib_frame_t * frame) +VLIB_NODE_FN (nat44_ei_in2out_node) +(vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame) { - return snat_in2out_node_fn_inline (vm, node, frame, 0 /* is_slow_path */ , - 0); + return nat44_ei_in2out_node_fn_inline (vm, node, frame, 0 /* is_slow_path */, + 0); } -VLIB_REGISTER_NODE (snat_in2out_node) = { - .name = "nat44-in2out", +VLIB_REGISTER_NODE (nat44_ei_in2out_node) = { + .name = "nat44-ei-in2out", .vector_size = sizeof (u32), - .format_trace = format_snat_in2out_trace, + .format_trace = format_nat44_ei_in2out_trace, .type = VLIB_NODE_TYPE_INTERNAL, - .n_errors = ARRAY_LEN(snat_in2out_error_strings), - .error_strings = snat_in2out_error_strings, + .n_errors = ARRAY_LEN(nat44_ei_in2out_error_strings), + .error_strings = nat44_ei_in2out_error_strings, - .runtime_data_bytes = sizeof (snat_runtime_t), + .runtime_data_bytes = sizeof (nat44_ei_runtime_t), - .n_next_nodes = SNAT_IN2OUT_N_NEXT, + .n_next_nodes = NAT44_EI_IN2OUT_N_NEXT, /* edit / add dispositions here */ .next_nodes = { - [SNAT_IN2OUT_NEXT_DROP] = "error-drop", - [SNAT_IN2OUT_NEXT_LOOKUP] = "ip4-lookup", - [SNAT_IN2OUT_NEXT_SLOW_PATH] = "nat44-in2out-slowpath", - [SNAT_IN2OUT_NEXT_ICMP_ERROR] = "ip4-icmp-error", - [SNAT_IN2OUT_NEXT_HAIRPINNING_HANDOFF] = "nat44-in2out-hairpinning-handoff-ip4-lookup", + [NAT44_EI_IN2OUT_NEXT_DROP] = "error-drop", + [NAT44_EI_IN2OUT_NEXT_LOOKUP] = "ip4-lookup", + [NAT44_EI_IN2OUT_NEXT_SLOW_PATH] = "nat44-ei-in2out-slowpath", + [NAT44_EI_IN2OUT_NEXT_ICMP_ERROR] = "ip4-icmp-error", + [NAT44_EI_IN2OUT_NEXT_HAIRPINNING_HANDOFF] = "nat44-ei-in2out-hairpinning-handoff-ip4-lookup", }, }; -VLIB_NODE_FN (snat_in2out_output_node) (vlib_main_t * vm, - vlib_node_runtime_t * node, - vlib_frame_t * frame) +VLIB_NODE_FN (nat44_ei_in2out_output_node) +(vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame) { - return snat_in2out_node_fn_inline (vm, node, frame, 0 /* is_slow_path */ , - 1); + return nat44_ei_in2out_node_fn_inline (vm, node, frame, 0 /* is_slow_path */, + 1); } -VLIB_REGISTER_NODE (snat_in2out_output_node) = { - .name = "nat44-in2out-output", +VLIB_REGISTER_NODE (nat44_ei_in2out_output_node) = { + .name = "nat44-ei-in2out-output", .vector_size = sizeof (u32), - .format_trace = format_snat_in2out_trace, + .format_trace = format_nat44_ei_in2out_trace, .type = VLIB_NODE_TYPE_INTERNAL, - .n_errors = ARRAY_LEN(snat_in2out_error_strings), - .error_strings = snat_in2out_error_strings, + .n_errors = ARRAY_LEN(nat44_ei_in2out_error_strings), + .error_strings = nat44_ei_in2out_error_strings, - .runtime_data_bytes = sizeof (snat_runtime_t), + .runtime_data_bytes = sizeof (nat44_ei_runtime_t), - .n_next_nodes = SNAT_IN2OUT_N_NEXT, + .n_next_nodes = NAT44_EI_IN2OUT_N_NEXT, /* edit / add dispositions here */ .next_nodes = { - [SNAT_IN2OUT_NEXT_DROP] = "error-drop", - [SNAT_IN2OUT_NEXT_LOOKUP] = "interface-output", - [SNAT_IN2OUT_NEXT_SLOW_PATH] = "nat44-in2out-output-slowpath", - [SNAT_IN2OUT_NEXT_ICMP_ERROR] = "ip4-icmp-error", - [SNAT_IN2OUT_NEXT_HAIRPINNING_HANDOFF] = "nat44-in2out-hairpinning-handoff-interface-output", + [NAT44_EI_IN2OUT_NEXT_DROP] = "error-drop", + [NAT44_EI_IN2OUT_NEXT_LOOKUP] = "interface-output", + [NAT44_EI_IN2OUT_NEXT_SLOW_PATH] = "nat44-ei-in2out-output-slowpath", + [NAT44_EI_IN2OUT_NEXT_ICMP_ERROR] = "ip4-icmp-error", + [NAT44_EI_IN2OUT_NEXT_HAIRPINNING_HANDOFF] = "nat44-ei-in2out-hairpinning-handoff-interface-output", }, }; -VLIB_NODE_FN (snat_in2out_slowpath_node) (vlib_main_t * vm, - vlib_node_runtime_t * node, - vlib_frame_t * frame) +VLIB_NODE_FN (nat44_ei_in2out_slowpath_node) +(vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame) { - return snat_in2out_node_fn_inline (vm, node, frame, 1 /* is_slow_path */ , - 0); + return nat44_ei_in2out_node_fn_inline (vm, node, frame, 1 /* is_slow_path */, + 0); } -VLIB_REGISTER_NODE (snat_in2out_slowpath_node) = { - .name = "nat44-in2out-slowpath", +VLIB_REGISTER_NODE (nat44_ei_in2out_slowpath_node) = { + .name = "nat44-ei-in2out-slowpath", .vector_size = sizeof (u32), - .format_trace = format_snat_in2out_trace, + .format_trace = format_nat44_ei_in2out_trace, .type = VLIB_NODE_TYPE_INTERNAL, - .n_errors = ARRAY_LEN(snat_in2out_error_strings), - .error_strings = snat_in2out_error_strings, + .n_errors = ARRAY_LEN(nat44_ei_in2out_error_strings), + .error_strings = nat44_ei_in2out_error_strings, - .runtime_data_bytes = sizeof (snat_runtime_t), + .runtime_data_bytes = sizeof (nat44_ei_runtime_t), - .n_next_nodes = SNAT_IN2OUT_N_NEXT, + .n_next_nodes = NAT44_EI_IN2OUT_N_NEXT, /* edit / add dispositions here */ .next_nodes = { - [SNAT_IN2OUT_NEXT_DROP] = "error-drop", - [SNAT_IN2OUT_NEXT_LOOKUP] = "ip4-lookup", - [SNAT_IN2OUT_NEXT_SLOW_PATH] = "nat44-in2out-slowpath", - [SNAT_IN2OUT_NEXT_ICMP_ERROR] = "ip4-icmp-error", - [SNAT_IN2OUT_NEXT_HAIRPINNING_HANDOFF] = "nat44-in2out-hairpinning-handoff-ip4-lookup", + [NAT44_EI_IN2OUT_NEXT_DROP] = "error-drop", + [NAT44_EI_IN2OUT_NEXT_LOOKUP] = "ip4-lookup", + [NAT44_EI_IN2OUT_NEXT_SLOW_PATH] = "nat44-ei-in2out-slowpath", + [NAT44_EI_IN2OUT_NEXT_ICMP_ERROR] = "ip4-icmp-error", + [NAT44_EI_IN2OUT_NEXT_HAIRPINNING_HANDOFF] = "nat44-ei-in2out-hairpinning-handoff-ip4-lookup", }, }; -VLIB_NODE_FN (snat_in2out_output_slowpath_node) (vlib_main_t * vm, - vlib_node_runtime_t * node, - vlib_frame_t * frame) +VLIB_NODE_FN (nat44_ei_in2out_output_slowpath_node) +(vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame) { - return snat_in2out_node_fn_inline (vm, node, frame, 1 /* is_slow_path */ , - 1); + return nat44_ei_in2out_node_fn_inline (vm, node, frame, 1 /* is_slow_path */, + 1); } -VLIB_REGISTER_NODE (snat_in2out_output_slowpath_node) = { - .name = "nat44-in2out-output-slowpath", +VLIB_REGISTER_NODE (nat44_ei_in2out_output_slowpath_node) = { + .name = "nat44-ei-in2out-output-slowpath", .vector_size = sizeof (u32), - .format_trace = format_snat_in2out_trace, + .format_trace = format_nat44_ei_in2out_trace, .type = VLIB_NODE_TYPE_INTERNAL, - .n_errors = ARRAY_LEN(snat_in2out_error_strings), - .error_strings = snat_in2out_error_strings, + .n_errors = ARRAY_LEN(nat44_ei_in2out_error_strings), + .error_strings = nat44_ei_in2out_error_strings, - .runtime_data_bytes = sizeof (snat_runtime_t), + .runtime_data_bytes = sizeof (nat44_ei_runtime_t), - .n_next_nodes = SNAT_IN2OUT_N_NEXT, + .n_next_nodes = NAT44_EI_IN2OUT_N_NEXT, /* edit / add dispositions here */ .next_nodes = { - [SNAT_IN2OUT_NEXT_DROP] = "error-drop", - [SNAT_IN2OUT_NEXT_LOOKUP] = "interface-output", - [SNAT_IN2OUT_NEXT_SLOW_PATH] = "nat44-in2out-output-slowpath", - [SNAT_IN2OUT_NEXT_ICMP_ERROR] = "ip4-icmp-error", - [SNAT_IN2OUT_NEXT_HAIRPINNING_HANDOFF] = "nat44-in2out-hairpinning-handoff-interface-output", + [NAT44_EI_IN2OUT_NEXT_DROP] = "error-drop", + [NAT44_EI_IN2OUT_NEXT_LOOKUP] = "interface-output", + [NAT44_EI_IN2OUT_NEXT_SLOW_PATH] = "nat44-ei-in2out-output-slowpath", + [NAT44_EI_IN2OUT_NEXT_ICMP_ERROR] = "ip4-icmp-error", + [NAT44_EI_IN2OUT_NEXT_HAIRPINNING_HANDOFF] = "nat44-ei-in2out-hairpinning-handoff-interface-output", }, }; -VLIB_NODE_FN (snat_in2out_fast_node) (vlib_main_t * vm, - vlib_node_runtime_t * node, - vlib_frame_t * frame) +VLIB_NODE_FN (nat44_ei_in2out_fast_node) +(vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame) { u32 n_left_from, *from, *to_next; u32 thread_index = vm->thread_index; - snat_in2out_next_t next_index; - snat_main_t *sm = &snat_main; + nat44_ei_in2out_next_t next_index; + nat44_ei_main_t *nm = &nat44_ei_main; int is_hairpinning = 0; from = vlib_frame_vector_args (frame); @@ -1800,7 +1819,7 @@ VLIB_NODE_FN (snat_in2out_fast_node) (vlib_main_t * vm, n_left_to_next -= 1; b0 = vlib_get_buffer (vm, bi0); - next0 = SNAT_IN2OUT_NEXT_LOOKUP; + next0 = NAT44_EI_IN2OUT_NEXT_LOOKUP; ip0 = vlib_buffer_get_current (b0); udp0 = ip4_next_header (ip0); @@ -1817,7 +1836,7 @@ VLIB_NODE_FN (snat_in2out_fast_node) (vlib_main_t * vm, icmp4_error_set_vnet_buffer (b0, ICMP4_time_exceeded, ICMP4_time_exceeded_ttl_exceeded_in_transit, 0); - next0 = SNAT_IN2OUT_NEXT_ICMP_ERROR; + next0 = NAT44_EI_IN2OUT_NEXT_ICMP_ERROR; goto trace0; } @@ -1828,8 +1847,8 @@ VLIB_NODE_FN (snat_in2out_fast_node) (vlib_main_t * vm, if (PREDICT_FALSE (proto0 == NAT_PROTOCOL_ICMP)) { - next0 = icmp_in2out (sm, b0, ip0, icmp0, sw_if_index0, - rx_fib_index0, node, next0, ~0, 0); + next0 = nat44_ei_icmp_in2out (b0, ip0, icmp0, sw_if_index0, + rx_fib_index0, node, next0, ~0, 0); goto trace0; } @@ -1837,8 +1856,8 @@ VLIB_NODE_FN (snat_in2out_fast_node) (vlib_main_t * vm, ip0->src_address, udp0->src_port, rx_fib_index0, proto0, &sm0_addr, &sm0_port, &sm0_fib_index, 0, 0, 0)) { - b0->error = node->errors[SNAT_IN2OUT_ERROR_NO_TRANSLATION]; - next0 = SNAT_IN2OUT_NEXT_DROP; + b0->error = node->errors[NAT44_EI_IN2OUT_ERROR_NO_TRANSLATION]; + next0 = NAT44_EI_IN2OUT_NEXT_DROP; goto trace0; } @@ -1868,7 +1887,7 @@ VLIB_NODE_FN (snat_in2out_fast_node) (vlib_main_t * vm, sum0 = ip_csum_update (sum0, old_port0, new_port0, ip4_header_t /* cheat */ , length /* changed member */ ); - mss_clamping (sm->mss_clamping, tcp0, &sum0); + mss_clamping (nm->mss_clamping, tcp0, &sum0); tcp0->checksum = ip_csum_fold (sum0); } else if (udp0->checksum) @@ -1891,7 +1910,7 @@ VLIB_NODE_FN (snat_in2out_fast_node) (vlib_main_t * vm, sum0 = ip_csum_update (sum0, old_addr0, new_addr0, ip4_header_t, dst_address /* changed member */ ); - mss_clamping (sm->mss_clamping, tcp0, &sum0); + mss_clamping (nm->mss_clamping, tcp0, &sum0); tcp0->checksum = ip_csum_fold (sum0); } else if (udp0->checksum) @@ -1905,34 +1924,34 @@ VLIB_NODE_FN (snat_in2out_fast_node) (vlib_main_t * vm, } /* Hairpinning */ - is_hairpinning = snat_hairpinning ( - vm, node, sm, thread_index, b0, ip0, udp0, tcp0, proto0, + is_hairpinning = nat44_ei_hairpinning ( + vm, node, nm, thread_index, b0, ip0, udp0, tcp0, proto0, 0 /* do_trace */, &required_thread_index); if (thread_index != required_thread_index) { vnet_buffer (b0)->snat.required_thread_index = required_thread_index; - next0 = SNAT_IN2OUT_NEXT_HAIRPINNING_HANDOFF; + next0 = NAT44_EI_IN2OUT_NEXT_HAIRPINNING_HANDOFF; } trace0: if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE) && (b0->flags & VLIB_BUFFER_IS_TRACED))) { - snat_in2out_trace_t *t = + nat44_ei_in2out_trace_t *t = vlib_add_trace (vm, node, b0, sizeof (*t)); t->sw_if_index = sw_if_index0; t->next_index = next0; t->is_hairpinning = is_hairpinning; } - if (next0 != SNAT_IN2OUT_NEXT_DROP) + if (next0 != NAT44_EI_IN2OUT_NEXT_DROP) { - vlib_increment_simple_counter (&sm->counters.fastpath. - in2out.other, sw_if_index0, - vm->thread_index, 1); + vlib_increment_simple_counter ( + &nm->counters.fastpath.in2out.other, sw_if_index0, + vm->thread_index, 1); } /* verify speculative enqueue, maybe switch current next frame */ @@ -1947,43 +1966,43 @@ VLIB_NODE_FN (snat_in2out_fast_node) (vlib_main_t * vm, return frame->n_vectors; } -VLIB_REGISTER_NODE (snat_in2out_fast_node) = { - .name = "nat44-in2out-fast", +VLIB_REGISTER_NODE (nat44_ei_in2out_fast_node) = { + .name = "nat44-ei-in2out-fast", .vector_size = sizeof (u32), - .format_trace = format_snat_in2out_fast_trace, + .format_trace = format_nat44_ei_in2out_fast_trace, .type = VLIB_NODE_TYPE_INTERNAL, - .n_errors = ARRAY_LEN(snat_in2out_error_strings), - .error_strings = snat_in2out_error_strings, + .n_errors = ARRAY_LEN(nat44_ei_in2out_error_strings), + .error_strings = nat44_ei_in2out_error_strings, - .runtime_data_bytes = sizeof (snat_runtime_t), + .runtime_data_bytes = sizeof (nat44_ei_runtime_t), - .n_next_nodes = SNAT_IN2OUT_N_NEXT, + .n_next_nodes = NAT44_EI_IN2OUT_N_NEXT, /* edit / add dispositions here */ .next_nodes = { - [SNAT_IN2OUT_NEXT_DROP] = "error-drop", - [SNAT_IN2OUT_NEXT_LOOKUP] = "ip4-lookup", - [SNAT_IN2OUT_NEXT_SLOW_PATH] = "nat44-in2out-slowpath", - [SNAT_IN2OUT_NEXT_ICMP_ERROR] = "ip4-icmp-error", - [SNAT_IN2OUT_NEXT_HAIRPINNING_HANDOFF] = "nat44-in2out-hairpinning-handoff-ip4-lookup", + [NAT44_EI_IN2OUT_NEXT_DROP] = "error-drop", + [NAT44_EI_IN2OUT_NEXT_LOOKUP] = "ip4-lookup", + [NAT44_EI_IN2OUT_NEXT_SLOW_PATH] = "nat44-ei-in2out-slowpath", + [NAT44_EI_IN2OUT_NEXT_ICMP_ERROR] = "ip4-icmp-error", + [NAT44_EI_IN2OUT_NEXT_HAIRPINNING_HANDOFF] = "nat44-ei-in2out-hairpinning-handoff-ip4-lookup", }, }; -VLIB_NODE_FN (nat44_in2out_hairpinning_handoff_ip4_lookup_node) +VLIB_NODE_FN (nat44_ei_in2out_hairpinning_handoff_ip4_lookup_node) (vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame) { - return nat44_hairpinning_handoff_fn_inline ( + return nat44_ei_hairpinning_handoff_fn_inline ( vm, node, frame, - snat_main.nat44_in2out_hairpinning_finish_ip4_lookup_node_fq_index); + nat44_ei_main.in2out_hairpinning_finish_ip4_lookup_node_fq_index); } -VLIB_REGISTER_NODE (nat44_in2out_hairpinning_handoff_ip4_lookup_node) = { - .name = "nat44-in2out-hairpinning-handoff-ip4-lookup", +VLIB_REGISTER_NODE (nat44_ei_in2out_hairpinning_handoff_ip4_lookup_node) = { + .name = "nat44-ei-in2out-hairpinning-handoff-ip4-lookup", .vector_size = sizeof (u32), - .n_errors = ARRAY_LEN(nat44_hairpinning_handoff_error_strings), - .error_strings = nat44_hairpinning_handoff_error_strings, - .format_trace = format_nat44_hairpinning_handoff_trace, + .n_errors = ARRAY_LEN(nat44_ei_hairpinning_handoff_error_strings), + .error_strings = nat44_ei_hairpinning_handoff_error_strings, + .format_trace = format_nat44_ei_hairpinning_handoff_trace, .n_next_nodes = 1, @@ -1992,20 +2011,20 @@ VLIB_REGISTER_NODE (nat44_in2out_hairpinning_handoff_ip4_lookup_node) = { }, }; -VLIB_NODE_FN (nat44_in2out_hairpinning_handoff_interface_output_node) +VLIB_NODE_FN (nat44_ei_in2out_hairpinning_handoff_interface_output_node) (vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame) { - return nat44_hairpinning_handoff_fn_inline ( + return nat44_ei_hairpinning_handoff_fn_inline ( vm, node, frame, - snat_main.nat44_in2out_hairpinning_finish_interface_output_node_fq_index); + nat44_ei_main.in2out_hairpinning_finish_interface_output_node_fq_index); } -VLIB_REGISTER_NODE (nat44_in2out_hairpinning_handoff_interface_output_node) = { - .name = "nat44-in2out-hairpinning-handoff-interface-output", +VLIB_REGISTER_NODE (nat44_ei_in2out_hairpinning_handoff_interface_output_node) = { + .name = "nat44-ei-in2out-hairpinning-handoff-interface-output", .vector_size = sizeof (u32), - .n_errors = ARRAY_LEN(nat44_hairpinning_handoff_error_strings), - .error_strings = nat44_hairpinning_handoff_error_strings, - .format_trace = format_nat44_hairpinning_handoff_trace, + .n_errors = ARRAY_LEN(nat44_ei_hairpinning_handoff_error_strings), + .error_strings = nat44_ei_hairpinning_handoff_error_strings, + .format_trace = format_nat44_ei_hairpinning_handoff_trace, .n_next_nodes = 1, @@ -2015,14 +2034,14 @@ VLIB_REGISTER_NODE (nat44_in2out_hairpinning_handoff_interface_output_node) = { }; static_always_inline int -nat44_in2out_hairpinning_finish_inline (vlib_main_t *vm, - vlib_node_runtime_t *node, - vlib_frame_t *frame) +nat44_ei_in2out_hairpinning_finish_inline (vlib_main_t *vm, + vlib_node_runtime_t *node, + vlib_frame_t *frame) { u32 n_left_from, *from, *to_next; u32 thread_index = vm->thread_index; - snat_in2out_next_t next_index; - snat_main_t *sm = &snat_main; + nat44_ei_in2out_next_t next_index; + nat44_ei_main_t *nm = &nat44_ei_main; int is_hairpinning = 0; from = vlib_frame_vector_args (frame); @@ -2057,7 +2076,7 @@ nat44_in2out_hairpinning_finish_inline (vlib_main_t *vm, n_left_to_next -= 1; b0 = vlib_get_buffer (vm, bi0); - next0 = NAT44_IN2OUT_HAIRPINNING_FINISH_NEXT_LOOKUP; + next0 = NAT44_EI_IN2OUT_HAIRPINNING_FINISH_NEXT_LOOKUP; ip0 = vlib_buffer_get_current (b0); udp0 = ip4_next_header (ip0); @@ -2072,41 +2091,41 @@ nat44_in2out_hairpinning_finish_inline (vlib_main_t *vm, case NAT_PROTOCOL_TCP: // fallthrough case NAT_PROTOCOL_UDP: - is_hairpinning = snat_hairpinning ( - vm, node, sm, thread_index, b0, ip0, udp0, tcp0, proto0, + is_hairpinning = nat44_ei_hairpinning ( + vm, node, nm, thread_index, b0, ip0, udp0, tcp0, proto0, 0 /* do_trace */, &required_thread_index); break; case NAT_PROTOCOL_ICMP: - is_hairpinning = - (0 == snat_icmp_hairpinning (sm, b0, thread_index, ip0, icmp0, - &required_thread_index)); + is_hairpinning = (0 == nat44_ei_icmp_hairpinning ( + nm, b0, thread_index, ip0, icmp0, + &required_thread_index)); break; case NAT_PROTOCOL_OTHER: // this should never happen - next0 = NAT44_IN2OUT_HAIRPINNING_FINISH_NEXT_DROP; + next0 = NAT44_EI_IN2OUT_HAIRPINNING_FINISH_NEXT_DROP; break; } if (thread_index != required_thread_index) { // but we already did a handoff ... - next0 = NAT44_IN2OUT_HAIRPINNING_FINISH_NEXT_DROP; + next0 = NAT44_EI_IN2OUT_HAIRPINNING_FINISH_NEXT_DROP; } if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE) && (b0->flags & VLIB_BUFFER_IS_TRACED))) { - snat_in2out_trace_t *t = + nat44_ei_in2out_trace_t *t = vlib_add_trace (vm, node, b0, sizeof (*t)); t->sw_if_index = sw_if_index0; t->next_index = next0; t->is_hairpinning = is_hairpinning; } - if (next0 != NAT44_IN2OUT_HAIRPINNING_FINISH_NEXT_DROP) + if (next0 != NAT44_EI_IN2OUT_HAIRPINNING_FINISH_NEXT_DROP) { vlib_increment_simple_counter ( - &sm->counters.fastpath.in2out.other, sw_if_index0, + &nm->counters.fastpath.in2out.other, sw_if_index0, vm->thread_index, 1); } @@ -2121,55 +2140,55 @@ nat44_in2out_hairpinning_finish_inline (vlib_main_t *vm, return frame->n_vectors; } -VLIB_NODE_FN (nat44_in2out_hairpinning_finish_ip4_lookup_node) +VLIB_NODE_FN (nat44_ei_in2out_hairpinning_finish_ip4_lookup_node) (vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame) { - return nat44_in2out_hairpinning_finish_inline (vm, node, frame); + return nat44_ei_in2out_hairpinning_finish_inline (vm, node, frame); } -VLIB_REGISTER_NODE (nat44_in2out_hairpinning_finish_ip4_lookup_node) = { - .name = "nat44-in2out-hairpinning-finish-ip4-lookup", +VLIB_REGISTER_NODE (nat44_ei_in2out_hairpinning_finish_ip4_lookup_node) = { + .name = "nat44-ei-in2out-hairpinning-finish-ip4-lookup", .vector_size = sizeof (u32), - .format_trace = format_snat_in2out_fast_trace, + .format_trace = format_nat44_ei_in2out_fast_trace, .type = VLIB_NODE_TYPE_INTERNAL, - .n_errors = ARRAY_LEN(snat_in2out_error_strings), - .error_strings = snat_in2out_error_strings, + .n_errors = ARRAY_LEN(nat44_ei_in2out_error_strings), + .error_strings = nat44_ei_in2out_error_strings, - .runtime_data_bytes = sizeof (snat_runtime_t), + .runtime_data_bytes = sizeof (nat44_ei_runtime_t), - .n_next_nodes = NAT44_IN2OUT_HAIRPINNING_FINISH_N_NEXT, + .n_next_nodes = NAT44_EI_IN2OUT_HAIRPINNING_FINISH_N_NEXT, /* edit / add dispositions here */ .next_nodes = { - [NAT44_IN2OUT_HAIRPINNING_FINISH_NEXT_DROP] = "error-drop", - [NAT44_IN2OUT_HAIRPINNING_FINISH_NEXT_LOOKUP] = "ip4-lookup", + [NAT44_EI_IN2OUT_HAIRPINNING_FINISH_NEXT_DROP] = "error-drop", + [NAT44_EI_IN2OUT_HAIRPINNING_FINISH_NEXT_LOOKUP] = "ip4-lookup", }, }; -VLIB_NODE_FN (nat44_in2out_hairpinning_finish_interface_output_node) +VLIB_NODE_FN (nat44_ei_in2out_hairpinning_finish_interface_output_node) (vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame) { - return nat44_in2out_hairpinning_finish_inline (vm, node, frame); + return nat44_ei_in2out_hairpinning_finish_inline (vm, node, frame); } -VLIB_REGISTER_NODE (nat44_in2out_hairpinning_finish_interface_output_node) = { - .name = "nat44-in2out-hairpinning-finish-interface-output", +VLIB_REGISTER_NODE (nat44_ei_in2out_hairpinning_finish_interface_output_node) = { + .name = "nat44-ei-in2out-hairpinning-finish-interface-output", .vector_size = sizeof (u32), - .format_trace = format_snat_in2out_fast_trace, + .format_trace = format_nat44_ei_in2out_fast_trace, .type = VLIB_NODE_TYPE_INTERNAL, - .n_errors = ARRAY_LEN(snat_in2out_error_strings), - .error_strings = snat_in2out_error_strings, + .n_errors = ARRAY_LEN(nat44_ei_in2out_error_strings), + .error_strings = nat44_ei_in2out_error_strings, - .runtime_data_bytes = sizeof (snat_runtime_t), + .runtime_data_bytes = sizeof (nat44_ei_runtime_t), - .n_next_nodes = NAT44_IN2OUT_HAIRPINNING_FINISH_N_NEXT, + .n_next_nodes = NAT44_EI_IN2OUT_HAIRPINNING_FINISH_N_NEXT, /* edit / add dispositions here */ .next_nodes = { - [NAT44_IN2OUT_HAIRPINNING_FINISH_NEXT_DROP] = "error-drop", - [NAT44_IN2OUT_HAIRPINNING_FINISH_NEXT_LOOKUP] = "interface-output", + [NAT44_EI_IN2OUT_HAIRPINNING_FINISH_NEXT_DROP] = "error-drop", + [NAT44_EI_IN2OUT_HAIRPINNING_FINISH_NEXT_LOOKUP] = "interface-output", }, }; diff --git a/src/plugins/nat/nat44-ei/nat44_ei_inlines.h b/src/plugins/nat/nat44-ei/nat44_ei_inlines.h index f7089ea0c21..30935884ce7 100644 --- a/src/plugins/nat/nat44-ei/nat44_ei_inlines.h +++ b/src/plugins/nat/nat44-ei/nat44_ei_inlines.h @@ -16,19 +16,189 @@ #ifndef __included_nat44_ei_inlines_h__ #define __included_nat44_ei_inlines_h__ +#include + +#include #include +always_inline u64 +calc_nat_key (ip4_address_t addr, u16 port, u32 fib_index, u8 proto) +{ + ASSERT (fib_index <= (1 << 14) - 1); + ASSERT (proto <= (1 << 3) - 1); + return (u64) addr.as_u32 << 32 | (u64) port << 16 | fib_index << 3 | + (proto & 0x7); +} + +always_inline void +split_nat_key (u64 key, ip4_address_t *addr, u16 *port, u32 *fib_index, + nat_protocol_t *proto) +{ + if (addr) + { + addr->as_u32 = key >> 32; + } + if (port) + { + *port = (key >> 16) & (u16) ~0; + } + if (fib_index) + { + *fib_index = key >> 3 & ((1 << 13) - 1); + } + if (proto) + { + *proto = key & 0x7; + } +} + +always_inline void +init_nat_k (clib_bihash_kv_8_8_t *kv, ip4_address_t addr, u16 port, + u32 fib_index, nat_protocol_t proto) +{ + kv->key = calc_nat_key (addr, port, fib_index, proto); + kv->value = ~0ULL; +} + +always_inline void +init_nat_kv (clib_bihash_kv_8_8_t *kv, ip4_address_t addr, u16 port, + u32 fib_index, nat_protocol_t proto, u32 thread_index, + u32 session_index) +{ + init_nat_k (kv, addr, port, fib_index, proto); + kv->value = (u64) thread_index << 32 | session_index; +} + +always_inline void +init_nat_i2o_k (clib_bihash_kv_8_8_t *kv, nat44_ei_session_t *s) +{ + return init_nat_k (kv, s->in2out.addr, s->in2out.port, s->in2out.fib_index, + s->nat_proto); +} + +always_inline void +init_nat_i2o_kv (clib_bihash_kv_8_8_t *kv, nat44_ei_session_t *s, + u32 thread_index, u32 session_index) +{ + init_nat_k (kv, s->in2out.addr, s->in2out.port, s->in2out.fib_index, + s->nat_proto); + kv->value = (u64) thread_index << 32 | session_index; +} + +always_inline void +init_nat_o2i_k (clib_bihash_kv_8_8_t *kv, nat44_ei_session_t *s) +{ + return init_nat_k (kv, s->out2in.addr, s->out2in.port, s->out2in.fib_index, + s->nat_proto); +} + +always_inline void +init_nat_o2i_kv (clib_bihash_kv_8_8_t *kv, nat44_ei_session_t *s, + u32 thread_index, u32 session_index) +{ + init_nat_k (kv, s->out2in.addr, s->out2in.port, s->out2in.fib_index, + s->nat_proto); + kv->value = (u64) thread_index << 32 | session_index; +} + +always_inline u32 +nat_value_get_thread_index (clib_bihash_kv_8_8_t *value) +{ + return value->value >> 32; +} + +always_inline u32 +nat_value_get_session_index (clib_bihash_kv_8_8_t *value) +{ + return value->value & ~(u32) 0; +} + +always_inline u8 +nat44_ei_is_interface_addr (ip4_main_t *im, vlib_node_runtime_t *node, + u32 sw_if_index0, u32 ip4_addr) +{ + nat44_ei_runtime_t *rt = (nat44_ei_runtime_t *) node->runtime_data; + ip4_address_t *first_int_addr; + + if (PREDICT_FALSE (rt->cached_sw_if_index != sw_if_index0)) + { + first_int_addr = ip4_interface_first_address ( + im, sw_if_index0, 0 /* just want the address */); + rt->cached_sw_if_index = sw_if_index0; + if (first_int_addr) + rt->cached_ip4_address = first_int_addr->as_u32; + else + rt->cached_ip4_address = 0; + } + + if (PREDICT_FALSE (ip4_addr == rt->cached_ip4_address)) + return 1; + else + return 0; +} + +/** \brief Per-user LRU list maintenance */ +always_inline void +nat44_ei_session_update_lru (nat44_ei_main_t *nm, nat44_ei_session_t *s, + u32 thread_index) +{ + /* don't update too often - timeout is in magnitude of seconds anyway */ + if (s->last_heard > s->last_lru_update + 1) + { + clib_dlist_remove (nm->per_thread_data[thread_index].list_pool, + s->per_user_index); + clib_dlist_addtail (nm->per_thread_data[thread_index].list_pool, + s->per_user_list_head_index, s->per_user_index); + s->last_lru_update = s->last_heard; + } +} + +always_inline void +nat44_ei_user_session_increment (nat44_ei_main_t *nm, nat44_ei_user_t *u, + u8 is_static) +{ + if (u->nsessions + u->nstaticsessions < nm->max_translations_per_user) + { + if (is_static) + u->nstaticsessions++; + else + u->nsessions++; + } +} + +always_inline void +nat44_ei_delete_user_with_no_session (nat44_ei_main_t *nm, nat44_ei_user_t *u, + u32 thread_index) +{ + clib_bihash_kv_8_8_t kv; + nat44_ei_user_key_t u_key; + nat44_ei_main_per_thread_data_t *tnm = + vec_elt_at_index (nm->per_thread_data, thread_index); + + if (u->nstaticsessions == 0 && u->nsessions == 0) + { + u_key.addr.as_u32 = u->addr.as_u32; + u_key.fib_index = u->fib_index; + kv.key = u_key.as_u64; + pool_put_index (tnm->list_pool, u->sessions_per_user_list_head_index); + pool_put (tnm->users, u); + clib_bihash_add_del_8_8 (&tnm->user_hash, &kv, 0); + vlib_set_simple_counter (&nm->total_users, thread_index, 0, + pool_elts (tnm->users)); + } +} + static_always_inline u8 -nat44_ei_maximum_sessions_exceeded (snat_main_t *sm, u32 thread_index) +nat44_ei_maximum_sessions_exceeded (nat44_ei_main_t *nm, u32 thread_index) { - if (pool_elts (sm->per_thread_data[thread_index].sessions) >= - sm->max_translations_per_thread) + if (pool_elts (nm->per_thread_data[thread_index].sessions) >= + nm->max_translations_per_thread) return 1; return 0; } always_inline void -nat44_ei_session_update_counters (snat_session_t *s, f64 now, uword bytes, +nat44_ei_session_update_counters (nat44_ei_session_t *s, f64 now, uword bytes, u32 thread_index) { s->last_heard = now; diff --git a/src/plugins/nat/nat44-ei/nat44_ei_out2in.c b/src/plugins/nat/nat44-ei/nat44_ei_out2in.c index ca3c272a26c..1d5ebd33cd5 100644 --- a/src/plugins/nat/nat44-ei/nat44_ei_out2in.c +++ b/src/plugins/nat/nat44-ei/nat44_ei_out2in.c @@ -14,41 +14,40 @@ */ /** * @file - * @brief NAT44 endpoint-dependent outside to inside network translation + * @brief NAT44 EI outside to inside network translation */ #include -#include +#include #include -#include #include +#include #include -#include -#include -#include -#include -#include -#include #include #include -#include + +#include +#include +#include +#include +#include typedef struct { u32 sw_if_index; u32 next_index; u32 session_index; -} snat_out2in_trace_t; +} nat44_ei_out2in_trace_t; /* packet trace format function */ static u8 * -format_snat_out2in_trace (u8 * s, va_list * args) +format_nat44_ei_out2in_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 *); - snat_out2in_trace_t *t = va_arg (*args, snat_out2in_trace_t *); + nat44_ei_out2in_trace_t *t = va_arg (*args, nat44_ei_out2in_trace_t *); s = format (s, @@ -58,66 +57,67 @@ format_snat_out2in_trace (u8 * s, va_list * args) } static u8 * -format_snat_out2in_fast_trace (u8 * s, va_list * args) +format_nat44_ei_out2in_fast_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 *); - snat_out2in_trace_t *t = va_arg (*args, snat_out2in_trace_t *); + nat44_ei_out2in_trace_t *t = va_arg (*args, nat44_ei_out2in_trace_t *); s = format (s, "NAT44_OUT2IN_FAST: sw_if_index %d, next index %d", t->sw_if_index, t->next_index); return s; } -#define foreach_snat_out2in_error \ -_(UNSUPPORTED_PROTOCOL, "unsupported protocol") \ -_(OUT_OF_PORTS, "out of ports") \ -_(BAD_ICMP_TYPE, "unsupported ICMP type") \ -_(NO_TRANSLATION, "no translation") \ -_(MAX_SESSIONS_EXCEEDED, "maximum sessions exceeded") \ -_(CANNOT_CREATE_USER, "cannot create NAT user") +#define foreach_nat44_ei_out2in_error \ + _ (UNSUPPORTED_PROTOCOL, "unsupported protocol") \ + _ (OUT_OF_PORTS, "out of ports") \ + _ (BAD_ICMP_TYPE, "unsupported ICMP type") \ + _ (NO_TRANSLATION, "no translation") \ + _ (MAX_SESSIONS_EXCEEDED, "maximum sessions exceeded") \ + _ (CANNOT_CREATE_USER, "cannot create NAT user") typedef enum { -#define _(sym,str) SNAT_OUT2IN_ERROR_##sym, - foreach_snat_out2in_error +#define _(sym, str) NAT44_EI_OUT2IN_ERROR_##sym, + foreach_nat44_ei_out2in_error #undef _ - SNAT_OUT2IN_N_ERROR, -} snat_out2in_error_t; + NAT44_EI_OUT2IN_N_ERROR, +} nat44_ei_out2in_error_t; -static char *snat_out2in_error_strings[] = { +static char *nat44_ei_out2in_error_strings[] = { #define _(sym,string) string, - foreach_snat_out2in_error + foreach_nat44_ei_out2in_error #undef _ }; typedef enum { - SNAT_OUT2IN_NEXT_DROP, - SNAT_OUT2IN_NEXT_LOOKUP, - SNAT_OUT2IN_NEXT_ICMP_ERROR, - SNAT_OUT2IN_N_NEXT, -} snat_out2in_next_t; + NAT44_EI_OUT2IN_NEXT_DROP, + NAT44_EI_OUT2IN_NEXT_LOOKUP, + NAT44_EI_OUT2IN_NEXT_ICMP_ERROR, + NAT44_EI_OUT2IN_N_NEXT, +} nat44_ei_out2in_next_t; #ifndef CLIB_MARCH_VARIANT int nat44_o2i_is_idle_session_cb (clib_bihash_kv_8_8_t * kv, void *arg) { - snat_main_t *sm = &snat_main; - nat44_is_idle_session_ctx_t *ctx = arg; - snat_session_t *s; + nat44_ei_main_t *nm = &nat44_ei_main; + nat44_ei_is_idle_session_ctx_t *ctx = arg; + nat44_ei_session_t *s; u64 sess_timeout_time; - snat_main_per_thread_data_t *tsm = vec_elt_at_index (sm->per_thread_data, - ctx->thread_index); + nat44_ei_main_per_thread_data_t *tnm = + vec_elt_at_index (nm->per_thread_data, ctx->thread_index); clib_bihash_kv_8_8_t s_kv; - s = pool_elt_at_index (tsm->sessions, kv->value); - sess_timeout_time = s->last_heard + (f64) nat44_session_get_timeout (sm, s); + s = pool_elt_at_index (tnm->sessions, kv->value); + sess_timeout_time = s->last_heard + (f64) nat_session_get_timeout ( + &nm->timeouts, s->nat_proto, s->state); if (ctx->now >= sess_timeout_time) { init_nat_i2o_k (&s_kv, s); - if (clib_bihash_add_del_8_8 (&sm->in2out, &s_kv, 0)) - nat_elog_warn ("out2in key del failed"); + if (clib_bihash_add_del_8_8 (&nm->in2out, &s_kv, 0)) + nat_elog_warn (nm, "out2in key del failed"); nat_ipfix_logging_nat44_ses_delete (ctx->thread_index, s->in2out.addr.as_u32, @@ -135,12 +135,12 @@ nat44_o2i_is_idle_session_cb (clib_bihash_kv_8_8_t * kv, void *arg) s->ext_host_port, s->nat_proto, s->out2in.fib_index, ctx->thread_index); - if (!snat_is_session_static (s)) - snat_free_outside_address_and_port (sm->addresses, ctx->thread_index, - &s->out2in.addr, s->out2in.port, - s->nat_proto); + if (!nat44_ei_is_session_static (s)) + nat44_ei_free_outside_address_and_port ( + nm->addresses, ctx->thread_index, &s->out2in.addr, s->out2in.port, + s->nat_proto); - nat44_delete_session (sm, s, ctx->thread_index); + nat44_ei_delete_session (nm, s, ctx->thread_index); return 1; } @@ -154,63 +154,56 @@ nat44_o2i_is_idle_session_cb (clib_bihash_kv_8_8_t * kv, void *arg) * Create NAT session initiated by host from external network with static * mapping. * - * @param sm NAT main. + * @param nm NAT main. * @param b0 Vlib buffer. * @param in2out In2out NAT44 session key. * @param out2in Out2in NAT44 session key. * @param node Vlib node. * - * @returns SNAT session if successfully created otherwise 0. + * @returns NAT44_EI session if successfully created otherwise 0. */ -static inline snat_session_t * -create_session_for_static_mapping (snat_main_t * sm, - vlib_buffer_t * b0, - ip4_address_t i2o_addr, - u16 i2o_port, - u32 i2o_fib_index, - ip4_address_t o2i_addr, - u16 o2i_port, - u32 o2i_fib_index, - nat_protocol_t proto, - vlib_node_runtime_t * node, - u32 thread_index, f64 now) +static inline nat44_ei_session_t * +create_session_for_static_mapping ( + nat44_ei_main_t *nm, vlib_buffer_t *b0, ip4_address_t i2o_addr, u16 i2o_port, + u32 i2o_fib_index, ip4_address_t o2i_addr, u16 o2i_port, u32 o2i_fib_index, + nat_protocol_t proto, vlib_node_runtime_t *node, u32 thread_index, f64 now) { - snat_user_t *u; - snat_session_t *s; + nat44_ei_user_t *u; + nat44_ei_session_t *s; clib_bihash_kv_8_8_t kv0; ip4_header_t *ip0; udp_header_t *udp0; - nat44_is_idle_session_ctx_t ctx0; + nat44_ei_is_idle_session_ctx_t ctx0; - if (PREDICT_FALSE (nat44_ei_maximum_sessions_exceeded (sm, thread_index))) + if (PREDICT_FALSE (nat44_ei_maximum_sessions_exceeded (nm, thread_index))) { - b0->error = node->errors[SNAT_OUT2IN_ERROR_MAX_SESSIONS_EXCEEDED]; - nat_elog_notice ("maximum sessions exceeded"); + b0->error = node->errors[NAT44_EI_OUT2IN_ERROR_MAX_SESSIONS_EXCEEDED]; + nat_elog_notice (nm, "maximum sessions exceeded"); return 0; } ip0 = vlib_buffer_get_current (b0); udp0 = ip4_next_header (ip0); - u = nat_user_get_or_create (sm, &i2o_addr, i2o_fib_index, thread_index); + u = nat44_ei_user_get_or_create (nm, &i2o_addr, i2o_fib_index, thread_index); if (!u) { - b0->error = node->errors[SNAT_OUT2IN_ERROR_CANNOT_CREATE_USER]; + b0->error = node->errors[NAT44_EI_OUT2IN_ERROR_CANNOT_CREATE_USER]; return 0; } - s = nat_session_alloc_or_recycle (sm, u, thread_index, now); + s = nat44_ei_session_alloc_or_recycle (nm, u, thread_index, now); if (!s) { - nat44_delete_user_with_no_session (sm, u, thread_index); - nat_elog_warn ("create NAT session failed"); + nat44_ei_delete_user_with_no_session (nm, u, thread_index); + nat_elog_warn (nm, "create NAT session failed"); return 0; } - s->flags |= SNAT_SESSION_FLAG_STATIC_MAPPING; + s->flags |= NAT44_EI_SESSION_FLAG_STATIC_MAPPING; s->ext_host_addr.as_u32 = ip0->src_address.as_u32; s->ext_host_port = udp0->src_port; - user_session_increment (sm, u, 1 /* static */ ); + nat44_ei_user_session_increment (nm, u, 1 /* static */); s->in2out.addr = i2o_addr; s->in2out.port = i2o_port; s->in2out.fib_index = i2o_fib_index; @@ -223,16 +216,16 @@ create_session_for_static_mapping (snat_main_t * sm, ctx0.now = now; ctx0.thread_index = thread_index; init_nat_i2o_kv (&kv0, s, thread_index, - s - sm->per_thread_data[thread_index].sessions); + s - nm->per_thread_data[thread_index].sessions); if (clib_bihash_add_or_overwrite_stale_8_8 ( - &sm->in2out, &kv0, nat44_i2o_is_idle_session_cb, &ctx0)) - nat_elog_notice ("in2out key add failed"); + &nm->in2out, &kv0, nat44_i2o_is_idle_session_cb, &ctx0)) + nat_elog_notice (nm, "in2out key add failed"); init_nat_o2i_kv (&kv0, s, thread_index, - s - sm->per_thread_data[thread_index].sessions); + s - nm->per_thread_data[thread_index].sessions); if (clib_bihash_add_or_overwrite_stale_8_8 ( - &sm->out2in, &kv0, nat44_o2i_is_idle_session_cb, &ctx0)) - nat_elog_notice ("out2in key add failed"); + &nm->out2in, &kv0, nat44_o2i_is_idle_session_cb, &ctx0)) + nat_elog_notice (nm, "out2in key add failed"); /* log NAT event */ nat_ipfix_logging_nat44_ses_create (thread_index, @@ -255,9 +248,9 @@ create_session_for_static_mapping (snat_main_t * sm, } #ifndef CLIB_MARCH_VARIANT -static_always_inline snat_out2in_error_t -icmp_get_key (vlib_buffer_t * b, ip4_header_t * ip0, - ip4_address_t * addr, u16 * port, nat_protocol_t * nat_proto) +static_always_inline nat44_ei_out2in_error_t +icmp_get_key (vlib_buffer_t *b, ip4_header_t *ip0, ip4_address_t *addr, + u16 *port, nat_protocol_t *nat_proto) { icmp46_header_t *icmp0; icmp_echo_header_t *echo0, *inner_echo0 = 0; @@ -293,7 +286,7 @@ icmp_get_key (vlib_buffer_t * b, ip4_header_t * ip0, *port = ((tcp_udp_header_t *) l4_header)->src_port; break; default: - return SNAT_OUT2IN_ERROR_UNSUPPORTED_PROTOCOL; + return NAT44_EI_OUT2IN_ERROR_UNSUPPORTED_PROTOCOL; } } return -1; /* success */ @@ -303,7 +296,7 @@ icmp_get_key (vlib_buffer_t * b, ip4_header_t * ip0, * Get address and port values to be used for ICMP packet translation * and create session if needed * - * @param[in,out] sm NAT main + * @param[in,out] nm NAT main * @param[in,out] node NAT node runtime * @param[in] thread_index thread index * @param[in,out] b0 buffer containing packet to be translated @@ -315,15 +308,16 @@ icmp_get_key (vlib_buffer_t * b, ip4_header_t * ip0, * @param e optional parameter */ u32 -icmp_match_out2in_slow (snat_main_t *sm, vlib_node_runtime_t *node, - u32 thread_index, vlib_buffer_t *b0, ip4_header_t *ip0, - ip4_address_t *addr, u16 *port, u32 *fib_index, - nat_protocol_t *proto, snat_session_t **p_s0, - u8 *dont_translate) +nat44_ei_icmp_match_out2in_slow (vlib_node_runtime_t *node, u32 thread_index, + vlib_buffer_t *b0, ip4_header_t *ip0, + ip4_address_t *addr, u16 *port, + u32 *fib_index, nat_protocol_t *proto, + nat44_ei_session_t **p_s0, u8 *dont_translate) { - snat_main_per_thread_data_t *tsm = &sm->per_thread_data[thread_index]; + nat44_ei_main_t *nm = &nat44_ei_main; + nat44_ei_main_per_thread_data_t *tnm = &nm->per_thread_data[thread_index]; u32 sw_if_index0; - snat_session_t *s0 = 0; + nat44_ei_session_t *s0 = 0; clib_bihash_kv_8_8_t kv0, value0; u8 is_addr_only; u32 next0 = ~0; @@ -340,8 +334,8 @@ icmp_match_out2in_slow (snat_main_t *sm, vlib_node_runtime_t *node, err = icmp_get_key (b0, ip0, addr, port, proto); if (err != -1) { - b0->error = node->errors[SNAT_OUT2IN_ERROR_UNSUPPORTED_PROTOCOL]; - next0 = SNAT_OUT2IN_NEXT_DROP; + b0->error = node->errors[NAT44_EI_OUT2IN_ERROR_UNSUPPORTED_PROTOCOL]; + next0 = NAT44_EI_OUT2IN_NEXT_DROP; goto out; } @@ -350,7 +344,7 @@ icmp_match_out2in_slow (snat_main_t *sm, vlib_node_runtime_t *node, u32 mapping_fib_index; init_nat_k (&kv0, *addr, *port, *fib_index, *proto); - if (clib_bihash_search_8_8 (&sm->out2in, &kv0, &value0)) + if (clib_bihash_search_8_8 (&nm->out2in, &kv0, &value0)) { /* Try to match static mapping by external address and port, destination address and port in packet */ @@ -358,17 +352,18 @@ icmp_match_out2in_slow (snat_main_t *sm, vlib_node_runtime_t *node, *addr, *port, *fib_index, *proto, &mapping_addr, &mapping_port, &mapping_fib_index, 1, &is_addr_only, &identity_nat)) { - if (!sm->forwarding_enabled) + if (!nm->forwarding_enabled) { /* Don't NAT packet aimed at the intfc address */ - if (PREDICT_FALSE (is_interface_addr (sm, node, sw_if_index0, - ip0->dst_address.as_u32))) + if (PREDICT_FALSE (nat44_ei_is_interface_addr ( + nm->ip4_main, node, sw_if_index0, + ip0->dst_address.as_u32))) { *dont_translate = 1; goto out; } - b0->error = node->errors[SNAT_OUT2IN_ERROR_NO_TRANSLATION]; - next0 = SNAT_OUT2IN_NEXT_DROP; + b0->error = node->errors[NAT44_EI_OUT2IN_ERROR_NO_TRANSLATION]; + next0 = NAT44_EI_OUT2IN_NEXT_DROP; goto out; } else @@ -384,8 +379,8 @@ icmp_match_out2in_slow (snat_main_t *sm, vlib_node_runtime_t *node, && (vnet_buffer (b0)->ip.reass.icmp_type_or_tcp_flags != ICMP4_echo_request || !is_addr_only))) { - b0->error = node->errors[SNAT_OUT2IN_ERROR_BAD_ICMP_TYPE]; - next0 = SNAT_OUT2IN_NEXT_DROP; + b0->error = node->errors[NAT44_EI_OUT2IN_ERROR_BAD_ICMP_TYPE]; + next0 = NAT44_EI_OUT2IN_NEXT_DROP; goto out; } @@ -395,15 +390,13 @@ icmp_match_out2in_slow (snat_main_t *sm, vlib_node_runtime_t *node, goto out; } /* Create session initiated by host from external network */ - s0 = - create_session_for_static_mapping (sm, b0, mapping_addr, mapping_port, - mapping_fib_index, *addr, *port, - *fib_index, *proto, node, - thread_index, vlib_time_now (vm)); + s0 = create_session_for_static_mapping ( + nm, b0, mapping_addr, mapping_port, mapping_fib_index, *addr, *port, + *fib_index, *proto, node, thread_index, vlib_time_now (vm)); if (!s0) { - next0 = SNAT_OUT2IN_NEXT_DROP; + next0 = NAT44_EI_OUT2IN_NEXT_DROP; goto out; } } @@ -417,12 +410,12 @@ icmp_match_out2in_slow (snat_main_t *sm, vlib_node_runtime_t *node, && !icmp_type_is_error_message (vnet_buffer (b0)->ip. reass.icmp_type_or_tcp_flags))) { - b0->error = node->errors[SNAT_OUT2IN_ERROR_BAD_ICMP_TYPE]; - next0 = SNAT_OUT2IN_NEXT_DROP; + b0->error = node->errors[NAT44_EI_OUT2IN_ERROR_BAD_ICMP_TYPE]; + next0 = NAT44_EI_OUT2IN_NEXT_DROP; goto out; } - s0 = pool_elt_at_index (tsm->sessions, + s0 = pool_elt_at_index (tnm->sessions, nat_value_get_session_index (&value0)); } @@ -440,27 +433,15 @@ out: #endif #ifndef CLIB_MARCH_VARIANT -/** - * Get address and port values to be used for ICMP packet translation - * - * @param[in] sm NAT main - * @param[in,out] node NAT node runtime - * @param[in] thread_index thread index - * @param[in,out] b0 buffer containing packet to be translated - * @param[in,out] ip0 ip header - * @param[out] p_proto protocol used for matching - * @param[out] p_value address and port after NAT translation - * @param[out] p_dont_translate if packet should not be translated - * @param d optional parameter - * @param e optional parameter - */ u32 -icmp_match_out2in_fast (snat_main_t *sm, vlib_node_runtime_t *node, - u32 thread_index, vlib_buffer_t *b0, ip4_header_t *ip0, - ip4_address_t *mapping_addr, u16 *mapping_port, - u32 *mapping_fib_index, nat_protocol_t *proto, - snat_session_t **p_s0, u8 *dont_translate) +nat44_ei_icmp_match_out2in_fast (vlib_node_runtime_t *node, u32 thread_index, + vlib_buffer_t *b0, ip4_header_t *ip0, + ip4_address_t *mapping_addr, + u16 *mapping_port, u32 *mapping_fib_index, + nat_protocol_t *proto, + nat44_ei_session_t **p_s0, u8 *dont_translate) { + nat44_ei_main_t *nm = &nat44_ei_main; u32 sw_if_index0; u32 rx_fib_index0; u8 is_addr_only; @@ -477,7 +458,7 @@ icmp_match_out2in_fast (snat_main_t *sm, vlib_node_runtime_t *node, if (err != -1) { b0->error = node->errors[err]; - next0 = SNAT_OUT2IN_NEXT_DROP; + next0 = NAT44_EI_OUT2IN_NEXT_DROP; goto out; } if (nat44_ei_static_mapping_match (addr, port, rx_fib_index0, *proto, @@ -485,13 +466,14 @@ icmp_match_out2in_fast (snat_main_t *sm, vlib_node_runtime_t *node, mapping_fib_index, 1, &is_addr_only, 0)) { /* Don't NAT packet aimed at the intfc address */ - if (is_interface_addr (sm, node, sw_if_index0, ip0->dst_address.as_u32)) + if (nat44_ei_is_interface_addr (nm->ip4_main, node, sw_if_index0, + ip0->dst_address.as_u32)) { *dont_translate = 1; goto out; } - b0->error = node->errors[SNAT_OUT2IN_ERROR_NO_TRANSLATION]; - next0 = SNAT_OUT2IN_NEXT_DROP; + b0->error = node->errors[NAT44_EI_OUT2IN_ERROR_NO_TRANSLATION]; + next0 = NAT44_EI_OUT2IN_NEXT_DROP; goto out; } @@ -502,8 +484,8 @@ icmp_match_out2in_fast (snat_main_t *sm, vlib_node_runtime_t *node, && !icmp_type_is_error_message (vnet_buffer (b0)->ip. reass.icmp_type_or_tcp_flags))) { - b0->error = node->errors[SNAT_OUT2IN_ERROR_BAD_ICMP_TYPE]; - next0 = SNAT_OUT2IN_NEXT_DROP; + b0->error = node->errors[NAT44_EI_OUT2IN_ERROR_BAD_ICMP_TYPE]; + next0 = NAT44_EI_OUT2IN_NEXT_DROP; goto out; } @@ -512,18 +494,20 @@ out: } #endif -u32 icmp_out2in (snat_main_t *sm, vlib_buffer_t *b0, ip4_header_t *ip0, - icmp46_header_t *icmp0, u32 sw_if_index0, u32 rx_fib_index0, - vlib_node_runtime_t *node, u32 next0, u32 thread_index, - snat_session_t **p_s0); +u32 nat44_ei_icmp_out2in (vlib_buffer_t *b0, ip4_header_t *ip0, + icmp46_header_t *icmp0, u32 sw_if_index0, + u32 rx_fib_index0, vlib_node_runtime_t *node, + u32 next0, u32 thread_index, + nat44_ei_session_t **p_s0); #ifndef CLIB_MARCH_VARIANT u32 -icmp_out2in (snat_main_t *sm, vlib_buffer_t *b0, ip4_header_t *ip0, - icmp46_header_t *icmp0, u32 sw_if_index0, u32 rx_fib_index0, - vlib_node_runtime_t *node, u32 next0, u32 thread_index, - snat_session_t **p_s0) +nat44_ei_icmp_out2in (vlib_buffer_t *b0, ip4_header_t *ip0, + icmp46_header_t *icmp0, u32 sw_if_index0, + u32 rx_fib_index0, vlib_node_runtime_t *node, u32 next0, + u32 thread_index, nat44_ei_session_t **p_s0) { + nat44_ei_main_t *nm = &nat44_ei_main; icmp_echo_header_t *echo0, *inner_echo0 = 0; ip4_header_t *inner_ip0 = 0; void *l4_header = 0; @@ -542,12 +526,22 @@ icmp_out2in (snat_main_t *sm, vlib_buffer_t *b0, ip4_header_t *ip0, echo0 = (icmp_echo_header_t *) (icmp0 + 1); - next0_tmp = - sm->icmp_match_out2in_cb (sm, node, thread_index, b0, ip0, &addr, &port, - &fib_index, &proto, p_s0, &dont_translate); + if (PREDICT_TRUE (nm->pat)) + { + next0_tmp = nat44_ei_icmp_match_out2in_slow ( + node, thread_index, b0, ip0, &addr, &port, &fib_index, &proto, p_s0, + &dont_translate); + } + else + { + next0_tmp = nat44_ei_icmp_match_out2in_fast ( + node, thread_index, b0, ip0, &addr, &port, &fib_index, &proto, p_s0, + &dont_translate); + } + if (next0_tmp != ~0) next0 = next0_tmp; - if (next0 == SNAT_OUT2IN_NEXT_DROP || dont_translate) + if (next0 == NAT44_EI_OUT2IN_NEXT_DROP || dont_translate) goto out; if (PREDICT_TRUE (!ip4_is_fragment (ip0))) @@ -561,7 +555,7 @@ icmp_out2in (snat_main_t *sm, vlib_buffer_t *b0, ip4_header_t *ip0, checksum0 = ~ip_csum_fold (sum0); if (checksum0 != 0 && checksum0 != 0xffff) { - next0 = SNAT_OUT2IN_NEXT_DROP; + next0 = NAT44_EI_OUT2IN_NEXT_DROP; goto out; } } @@ -604,7 +598,7 @@ icmp_out2in (snat_main_t *sm, vlib_buffer_t *b0, ip4_header_t *ip0, if (!ip4_header_checksum_is_valid (inner_ip0)) { - next0 = SNAT_OUT2IN_NEXT_DROP; + next0 = NAT44_EI_OUT2IN_NEXT_DROP; goto out; } @@ -656,47 +650,42 @@ out: #endif static inline u32 -icmp_out2in_slow_path (snat_main_t * sm, - vlib_buffer_t * b0, - ip4_header_t * ip0, - icmp46_header_t * icmp0, - u32 sw_if_index0, - u32 rx_fib_index0, - vlib_node_runtime_t * node, - u32 next0, f64 now, - u32 thread_index, snat_session_t ** p_s0) +nat44_ei_icmp_out2in_slow_path (nat44_ei_main_t *nm, vlib_buffer_t *b0, + ip4_header_t *ip0, icmp46_header_t *icmp0, + u32 sw_if_index0, u32 rx_fib_index0, + vlib_node_runtime_t *node, u32 next0, f64 now, + u32 thread_index, nat44_ei_session_t **p_s0) { vlib_main_t *vm = vlib_get_main (); - next0 = icmp_out2in (sm, b0, ip0, icmp0, sw_if_index0, rx_fib_index0, node, - next0, thread_index, p_s0); - snat_session_t *s0 = *p_s0; - if (PREDICT_TRUE (next0 != SNAT_OUT2IN_NEXT_DROP && s0)) + next0 = nat44_ei_icmp_out2in (b0, ip0, icmp0, sw_if_index0, rx_fib_index0, + node, next0, thread_index, p_s0); + nat44_ei_session_t *s0 = *p_s0; + if (PREDICT_TRUE (next0 != NAT44_EI_OUT2IN_NEXT_DROP && s0)) { /* Accounting */ nat44_ei_session_update_counters ( s0, now, vlib_buffer_length_in_chain (vm, b0), thread_index); /* Per-user LRU list maintenance */ - nat44_session_update_lru (sm, s0, thread_index); + nat44_ei_session_update_lru (nm, s0, thread_index); } return next0; } static int -nat_out2in_sm_unknown_proto (snat_main_t * sm, - vlib_buffer_t * b, - ip4_header_t * ip, u32 rx_fib_index) +nat_out2in_sm_unknown_proto (nat44_ei_main_t *nm, vlib_buffer_t *b, + ip4_header_t *ip, u32 rx_fib_index) { clib_bihash_kv_8_8_t kv, value; - snat_static_mapping_t *m; + nat44_ei_static_mapping_t *m; u32 old_addr, new_addr; ip_csum_t sum; init_nat_k (&kv, ip->dst_address, 0, 0, 0); - if (clib_bihash_search_8_8 (&sm->static_mapping_by_external, &kv, &value)) + if (clib_bihash_search_8_8 (&nm->static_mapping_by_external, &kv, &value)) return 1; - m = pool_elt_at_index (sm->static_mappings, value.value); + m = pool_elt_at_index (nm->static_mappings, value.value); old_addr = ip->dst_address.as_u32; new_addr = ip->dst_address.as_u32 = m->local_addr.as_u32; @@ -708,15 +697,14 @@ nat_out2in_sm_unknown_proto (snat_main_t * sm, return 0; } -VLIB_NODE_FN (snat_out2in_node) (vlib_main_t * vm, - vlib_node_runtime_t * node, - vlib_frame_t * frame) +VLIB_NODE_FN (nat44_ei_out2in_node) +(vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame) { u32 n_left_from, *from; - snat_main_t *sm = &snat_main; + nat44_ei_main_t *nm = &nat44_ei_main; f64 now = vlib_time_now (vm); u32 thread_index = vm->thread_index; - snat_main_per_thread_data_t *tsm = &sm->per_thread_data[thread_index]; + nat44_ei_main_per_thread_data_t *tnm = &nm->per_thread_data[thread_index]; from = vlib_frame_vector_args (frame); n_left_from = frame->n_vectors; @@ -728,8 +716,8 @@ VLIB_NODE_FN (snat_out2in_node) (vlib_main_t * vm, while (n_left_from >= 2) { vlib_buffer_t *b0, *b1; - u32 next0 = SNAT_OUT2IN_NEXT_LOOKUP; - u32 next1 = SNAT_OUT2IN_NEXT_LOOKUP; + u32 next0 = NAT44_EI_OUT2IN_NEXT_LOOKUP; + u32 next1 = NAT44_EI_OUT2IN_NEXT_LOOKUP; u32 sw_if_index0, sw_if_index1; ip4_header_t *ip0, *ip1; ip_csum_t sum0, sum1; @@ -742,7 +730,7 @@ VLIB_NODE_FN (snat_out2in_node) (vlib_main_t * vm, icmp46_header_t *icmp0, *icmp1; u32 rx_fib_index0, rx_fib_index1; u32 proto0, proto1; - snat_session_t *s0 = 0, *s1 = 0; + nat44_ei_session_t *s0 = 0, *s1 = 0; clib_bihash_kv_8_8_t kv0, kv1, value0, value1; u8 identity_nat0, identity_nat1; ip4_address_t sm_addr0, sm_addr1; @@ -778,8 +766,8 @@ VLIB_NODE_FN (snat_out2in_node) (vlib_main_t * vm, icmp0 = (icmp46_header_t *) udp0; sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX]; - rx_fib_index0 = vec_elt (sm->ip4_main->fib_index_by_sw_if_index, - sw_if_index0); + rx_fib_index0 = + vec_elt (nm->ip4_main->fib_index_by_sw_if_index, sw_if_index0); if (PREDICT_FALSE (ip0->ttl == 1)) { @@ -787,7 +775,7 @@ VLIB_NODE_FN (snat_out2in_node) (vlib_main_t * vm, icmp4_error_set_vnet_buffer (b0, ICMP4_time_exceeded, ICMP4_time_exceeded_ttl_exceeded_in_transit, 0); - next0 = SNAT_OUT2IN_NEXT_ICMP_ERROR; + next0 = NAT44_EI_OUT2IN_NEXT_ICMP_ERROR; goto trace0; } @@ -795,16 +783,16 @@ VLIB_NODE_FN (snat_out2in_node) (vlib_main_t * vm, if (PREDICT_FALSE (proto0 == NAT_PROTOCOL_OTHER)) { - if (nat_out2in_sm_unknown_proto (sm, b0, ip0, rx_fib_index0)) + if (nat_out2in_sm_unknown_proto (nm, b0, ip0, rx_fib_index0)) { - if (!sm->forwarding_enabled) + if (!nm->forwarding_enabled) { b0->error = - node->errors[SNAT_OUT2IN_ERROR_UNSUPPORTED_PROTOCOL]; - next0 = SNAT_OUT2IN_NEXT_DROP; + node->errors[NAT44_EI_OUT2IN_ERROR_UNSUPPORTED_PROTOCOL]; + next0 = NAT44_EI_OUT2IN_NEXT_DROP; } } - vlib_increment_simple_counter (&sm->counters.slowpath.out2in.other, + vlib_increment_simple_counter (&nm->counters.slowpath.out2in.other, thread_index, sw_if_index0, 1); goto trace0; @@ -812,10 +800,10 @@ VLIB_NODE_FN (snat_out2in_node) (vlib_main_t * vm, if (PREDICT_FALSE (proto0 == NAT_PROTOCOL_ICMP)) { - next0 = icmp_out2in_slow_path - (sm, b0, ip0, icmp0, sw_if_index0, rx_fib_index0, node, - next0, now, thread_index, &s0); - vlib_increment_simple_counter (&sm->counters.slowpath.out2in.icmp, + next0 = nat44_ei_icmp_out2in_slow_path ( + nm, b0, ip0, icmp0, sw_if_index0, rx_fib_index0, node, next0, now, + thread_index, &s0); + vlib_increment_simple_counter (&nm->counters.slowpath.out2in.icmp, thread_index, sw_if_index0, 1); goto trace0; } @@ -823,7 +811,7 @@ VLIB_NODE_FN (snat_out2in_node) (vlib_main_t * vm, init_nat_k (&kv0, ip0->dst_address, vnet_buffer (b0)->ip.reass.l4_dst_port, rx_fib_index0, proto0); - if (clib_bihash_search_8_8 (&sm->out2in, &kv0, &value0)) + if (clib_bihash_search_8_8 (&nm->out2in, &kv0, &value0)) { /* Try to match static mapping by external address and port, destination address and port in packet */ @@ -845,10 +833,11 @@ VLIB_NODE_FN (snat_out2in_node) (vlib_main_t * vm, goto trace0; } - if (!sm->forwarding_enabled) + if (!nm->forwarding_enabled) { - b0->error = node->errors[SNAT_OUT2IN_ERROR_NO_TRANSLATION]; - next0 = SNAT_OUT2IN_NEXT_DROP; + b0->error = + node->errors[NAT44_EI_OUT2IN_ERROR_NO_TRANSLATION]; + next0 = NAT44_EI_OUT2IN_NEXT_DROP; } goto trace0; } @@ -857,22 +846,18 @@ VLIB_NODE_FN (snat_out2in_node) (vlib_main_t * vm, goto trace0; /* Create session initiated by host from external network */ - s0 = create_session_for_static_mapping (sm, b0, - sm_addr0, sm_port0, - sm_fib_index0, - ip0->dst_address, - vnet_buffer (b0)->ip. - reass.l4_dst_port, - rx_fib_index0, proto0, node, - thread_index, now); + s0 = create_session_for_static_mapping ( + nm, b0, sm_addr0, sm_port0, sm_fib_index0, ip0->dst_address, + vnet_buffer (b0)->ip.reass.l4_dst_port, rx_fib_index0, proto0, + node, thread_index, now); if (!s0) { - next0 = SNAT_OUT2IN_NEXT_DROP; + next0 = NAT44_EI_OUT2IN_NEXT_DROP; goto trace0; } } else - s0 = pool_elt_at_index (tsm->sessions, + s0 = pool_elt_at_index (tnm->sessions, nat_value_get_session_index (&value0)); old_addr0 = ip0->dst_address.as_u32; @@ -901,7 +886,7 @@ VLIB_NODE_FN (snat_out2in_node) (vlib_main_t * vm, length /* changed member */ ); tcp0->checksum = ip_csum_fold (sum0); } - vlib_increment_simple_counter (&sm->counters.slowpath.out2in.tcp, + vlib_increment_simple_counter (&nm->counters.slowpath.out2in.tcp, thread_index, sw_if_index0, 1); } else @@ -922,7 +907,7 @@ VLIB_NODE_FN (snat_out2in_node) (vlib_main_t * vm, udp0->checksum = ip_csum_fold (sum0); } } - vlib_increment_simple_counter (&sm->counters.slowpath.out2in.udp, + vlib_increment_simple_counter (&nm->counters.slowpath.out2in.udp, thread_index, sw_if_index0, 1); } @@ -930,24 +915,24 @@ VLIB_NODE_FN (snat_out2in_node) (vlib_main_t * vm, nat44_ei_session_update_counters ( s0, now, vlib_buffer_length_in_chain (vm, b0), thread_index); /* Per-user LRU list maintenance */ - nat44_session_update_lru (sm, s0, thread_index); + nat44_ei_session_update_lru (nm, s0, thread_index); trace0: if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE) && (b0->flags & VLIB_BUFFER_IS_TRACED))) { - snat_out2in_trace_t *t = vlib_add_trace (vm, node, b0, sizeof (*t)); + nat44_ei_out2in_trace_t *t = + vlib_add_trace (vm, node, b0, sizeof (*t)); t->sw_if_index = sw_if_index0; t->next_index = next0; t->session_index = ~0; if (s0) - t->session_index = - s0 - sm->per_thread_data[thread_index].sessions; + t->session_index = s0 - nm->per_thread_data[thread_index].sessions; } - if (next0 == SNAT_OUT2IN_NEXT_DROP) + if (next0 == NAT44_EI_OUT2IN_NEXT_DROP) { - vlib_increment_simple_counter (&sm->counters.slowpath.out2in.drops, + vlib_increment_simple_counter (&nm->counters.slowpath.out2in.drops, thread_index, sw_if_index0, 1); } @@ -958,8 +943,8 @@ VLIB_NODE_FN (snat_out2in_node) (vlib_main_t * vm, icmp1 = (icmp46_header_t *) udp1; sw_if_index1 = vnet_buffer (b1)->sw_if_index[VLIB_RX]; - rx_fib_index1 = vec_elt (sm->ip4_main->fib_index_by_sw_if_index, - sw_if_index1); + rx_fib_index1 = + vec_elt (nm->ip4_main->fib_index_by_sw_if_index, sw_if_index1); if (PREDICT_FALSE (ip1->ttl == 1)) { @@ -967,7 +952,7 @@ VLIB_NODE_FN (snat_out2in_node) (vlib_main_t * vm, icmp4_error_set_vnet_buffer (b1, ICMP4_time_exceeded, ICMP4_time_exceeded_ttl_exceeded_in_transit, 0); - next1 = SNAT_OUT2IN_NEXT_ICMP_ERROR; + next1 = NAT44_EI_OUT2IN_NEXT_ICMP_ERROR; goto trace1; } @@ -975,26 +960,26 @@ VLIB_NODE_FN (snat_out2in_node) (vlib_main_t * vm, if (PREDICT_FALSE (proto1 == NAT_PROTOCOL_OTHER)) { - if (nat_out2in_sm_unknown_proto (sm, b1, ip1, rx_fib_index1)) + if (nat_out2in_sm_unknown_proto (nm, b1, ip1, rx_fib_index1)) { - if (!sm->forwarding_enabled) + if (!nm->forwarding_enabled) { b1->error = - node->errors[SNAT_OUT2IN_ERROR_UNSUPPORTED_PROTOCOL]; - next1 = SNAT_OUT2IN_NEXT_DROP; + node->errors[NAT44_EI_OUT2IN_ERROR_UNSUPPORTED_PROTOCOL]; + next1 = NAT44_EI_OUT2IN_NEXT_DROP; } } - vlib_increment_simple_counter (&sm->counters.slowpath.out2in.other, + vlib_increment_simple_counter (&nm->counters.slowpath.out2in.other, thread_index, sw_if_index1, 1); goto trace1; } if (PREDICT_FALSE (proto1 == NAT_PROTOCOL_ICMP)) { - next1 = icmp_out2in_slow_path - (sm, b1, ip1, icmp1, sw_if_index1, rx_fib_index1, node, - next1, now, thread_index, &s1); - vlib_increment_simple_counter (&sm->counters.slowpath.out2in.icmp, + next1 = nat44_ei_icmp_out2in_slow_path ( + nm, b1, ip1, icmp1, sw_if_index1, rx_fib_index1, node, next1, now, + thread_index, &s1); + vlib_increment_simple_counter (&nm->counters.slowpath.out2in.icmp, thread_index, sw_if_index1, 1); goto trace1; } @@ -1002,7 +987,7 @@ VLIB_NODE_FN (snat_out2in_node) (vlib_main_t * vm, init_nat_k (&kv1, ip1->dst_address, vnet_buffer (b1)->ip.reass.l4_dst_port, rx_fib_index1, proto1); - if (clib_bihash_search_8_8 (&sm->out2in, &kv1, &value1)) + if (clib_bihash_search_8_8 (&nm->out2in, &kv1, &value1)) { /* Try to match static mapping by external address and port, destination address and port in packet */ @@ -1024,10 +1009,11 @@ VLIB_NODE_FN (snat_out2in_node) (vlib_main_t * vm, goto trace1; } - if (!sm->forwarding_enabled) + if (!nm->forwarding_enabled) { - b1->error = node->errors[SNAT_OUT2IN_ERROR_NO_TRANSLATION]; - next1 = SNAT_OUT2IN_NEXT_DROP; + b1->error = + node->errors[NAT44_EI_OUT2IN_ERROR_NO_TRANSLATION]; + next1 = NAT44_EI_OUT2IN_NEXT_DROP; } goto trace1; } @@ -1036,22 +1022,18 @@ VLIB_NODE_FN (snat_out2in_node) (vlib_main_t * vm, goto trace1; /* Create session initiated by host from external network */ - s1 = - create_session_for_static_mapping (sm, b1, sm_addr1, sm_port1, - sm_fib_index1, - ip1->dst_address, - vnet_buffer (b1)->ip. - reass.l4_dst_port, - rx_fib_index1, proto1, node, - thread_index, now); + s1 = create_session_for_static_mapping ( + nm, b1, sm_addr1, sm_port1, sm_fib_index1, ip1->dst_address, + vnet_buffer (b1)->ip.reass.l4_dst_port, rx_fib_index1, proto1, + node, thread_index, now); if (!s1) { - next1 = SNAT_OUT2IN_NEXT_DROP; + next1 = NAT44_EI_OUT2IN_NEXT_DROP; goto trace1; } } else - s1 = pool_elt_at_index (sm->per_thread_data[thread_index].sessions, + s1 = pool_elt_at_index (nm->per_thread_data[thread_index].sessions, nat_value_get_session_index (&value1)); old_addr1 = ip1->dst_address.as_u32; @@ -1081,7 +1063,7 @@ VLIB_NODE_FN (snat_out2in_node) (vlib_main_t * vm, length /* changed member */ ); tcp1->checksum = ip_csum_fold (sum1); } - vlib_increment_simple_counter (&sm->counters.slowpath.out2in.tcp, + vlib_increment_simple_counter (&nm->counters.slowpath.out2in.tcp, thread_index, sw_if_index1, 1); } else @@ -1105,7 +1087,7 @@ VLIB_NODE_FN (snat_out2in_node) (vlib_main_t * vm, udp1->checksum = ip_csum_fold (sum1); } } - vlib_increment_simple_counter (&sm->counters.slowpath.out2in.udp, + vlib_increment_simple_counter (&nm->counters.slowpath.out2in.udp, thread_index, sw_if_index1, 1); } @@ -1113,24 +1095,24 @@ VLIB_NODE_FN (snat_out2in_node) (vlib_main_t * vm, nat44_ei_session_update_counters ( s1, now, vlib_buffer_length_in_chain (vm, b1), thread_index); /* Per-user LRU list maintenance */ - nat44_session_update_lru (sm, s1, thread_index); + nat44_ei_session_update_lru (nm, s1, thread_index); trace1: if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE) && (b1->flags & VLIB_BUFFER_IS_TRACED))) { - snat_out2in_trace_t *t = vlib_add_trace (vm, node, b1, sizeof (*t)); + nat44_ei_out2in_trace_t *t = + vlib_add_trace (vm, node, b1, sizeof (*t)); t->sw_if_index = sw_if_index1; t->next_index = next1; t->session_index = ~0; if (s1) - t->session_index = - s1 - sm->per_thread_data[thread_index].sessions; + t->session_index = s1 - nm->per_thread_data[thread_index].sessions; } - if (next1 == SNAT_OUT2IN_NEXT_DROP) + if (next1 == NAT44_EI_OUT2IN_NEXT_DROP) { - vlib_increment_simple_counter (&sm->counters.slowpath.out2in.drops, + vlib_increment_simple_counter (&nm->counters.slowpath.out2in.drops, thread_index, sw_if_index1, 1); } @@ -1143,7 +1125,7 @@ VLIB_NODE_FN (snat_out2in_node) (vlib_main_t * vm, while (n_left_from > 0) { vlib_buffer_t *b0; - u32 next0 = SNAT_OUT2IN_NEXT_LOOKUP; + u32 next0 = NAT44_EI_OUT2IN_NEXT_LOOKUP; u32 sw_if_index0; ip4_header_t *ip0; ip_csum_t sum0; @@ -1154,7 +1136,7 @@ VLIB_NODE_FN (snat_out2in_node) (vlib_main_t * vm, icmp46_header_t *icmp0; u32 rx_fib_index0; u32 proto0; - snat_session_t *s0 = 0; + nat44_ei_session_t *s0 = 0; clib_bihash_kv_8_8_t kv0, value0; u8 identity_nat0; ip4_address_t sm_addr0; @@ -1172,23 +1154,23 @@ VLIB_NODE_FN (snat_out2in_node) (vlib_main_t * vm, icmp0 = (icmp46_header_t *) udp0; sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX]; - rx_fib_index0 = vec_elt (sm->ip4_main->fib_index_by_sw_if_index, - sw_if_index0); + rx_fib_index0 = + vec_elt (nm->ip4_main->fib_index_by_sw_if_index, sw_if_index0); proto0 = ip_proto_to_nat_proto (ip0->protocol); if (PREDICT_FALSE (proto0 == NAT_PROTOCOL_OTHER)) { - if (nat_out2in_sm_unknown_proto (sm, b0, ip0, rx_fib_index0)) + if (nat_out2in_sm_unknown_proto (nm, b0, ip0, rx_fib_index0)) { - if (!sm->forwarding_enabled) + if (!nm->forwarding_enabled) { b0->error = - node->errors[SNAT_OUT2IN_ERROR_UNSUPPORTED_PROTOCOL]; - next0 = SNAT_OUT2IN_NEXT_DROP; + node->errors[NAT44_EI_OUT2IN_ERROR_UNSUPPORTED_PROTOCOL]; + next0 = NAT44_EI_OUT2IN_NEXT_DROP; } } - vlib_increment_simple_counter (&sm->counters.slowpath.out2in.other, + vlib_increment_simple_counter (&nm->counters.slowpath.out2in.other, thread_index, sw_if_index0, 1); goto trace00; } @@ -1199,16 +1181,16 @@ VLIB_NODE_FN (snat_out2in_node) (vlib_main_t * vm, icmp4_error_set_vnet_buffer (b0, ICMP4_time_exceeded, ICMP4_time_exceeded_ttl_exceeded_in_transit, 0); - next0 = SNAT_OUT2IN_NEXT_ICMP_ERROR; + next0 = NAT44_EI_OUT2IN_NEXT_ICMP_ERROR; goto trace00; } if (PREDICT_FALSE (proto0 == NAT_PROTOCOL_ICMP)) { - next0 = icmp_out2in_slow_path - (sm, b0, ip0, icmp0, sw_if_index0, rx_fib_index0, node, - next0, now, thread_index, &s0); - vlib_increment_simple_counter (&sm->counters.slowpath.out2in.icmp, + next0 = nat44_ei_icmp_out2in_slow_path ( + nm, b0, ip0, icmp0, sw_if_index0, rx_fib_index0, node, next0, now, + thread_index, &s0); + vlib_increment_simple_counter (&nm->counters.slowpath.out2in.icmp, thread_index, sw_if_index0, 1); goto trace00; } @@ -1217,7 +1199,7 @@ VLIB_NODE_FN (snat_out2in_node) (vlib_main_t * vm, vnet_buffer (b0)->ip.reass.l4_dst_port, rx_fib_index0, proto0); - if (clib_bihash_search_8_8 (&sm->out2in, &kv0, &value0)) + if (clib_bihash_search_8_8 (&nm->out2in, &kv0, &value0)) { /* Try to match static mapping by external address and port, destination address and port in packet */ @@ -1239,10 +1221,11 @@ VLIB_NODE_FN (snat_out2in_node) (vlib_main_t * vm, goto trace00; } - if (!sm->forwarding_enabled) + if (!nm->forwarding_enabled) { - b0->error = node->errors[SNAT_OUT2IN_ERROR_NO_TRANSLATION]; - next0 = SNAT_OUT2IN_NEXT_DROP; + b0->error = + node->errors[NAT44_EI_OUT2IN_ERROR_NO_TRANSLATION]; + next0 = NAT44_EI_OUT2IN_NEXT_DROP; } goto trace00; } @@ -1251,22 +1234,18 @@ VLIB_NODE_FN (snat_out2in_node) (vlib_main_t * vm, goto trace00; /* Create session initiated by host from external network */ - s0 = create_session_for_static_mapping (sm, b0, - sm_addr0, sm_port0, - sm_fib_index0, - ip0->dst_address, - vnet_buffer (b0)->ip. - reass.l4_dst_port, - rx_fib_index0, proto0, node, - thread_index, now); + s0 = create_session_for_static_mapping ( + nm, b0, sm_addr0, sm_port0, sm_fib_index0, ip0->dst_address, + vnet_buffer (b0)->ip.reass.l4_dst_port, rx_fib_index0, proto0, + node, thread_index, now); if (!s0) { - next0 = SNAT_OUT2IN_NEXT_DROP; + next0 = NAT44_EI_OUT2IN_NEXT_DROP; goto trace00; } } else - s0 = pool_elt_at_index (sm->per_thread_data[thread_index].sessions, + s0 = pool_elt_at_index (nm->per_thread_data[thread_index].sessions, nat_value_get_session_index (&value0)); old_addr0 = ip0->dst_address.as_u32; @@ -1296,7 +1275,7 @@ VLIB_NODE_FN (snat_out2in_node) (vlib_main_t * vm, length /* changed member */ ); tcp0->checksum = ip_csum_fold (sum0); } - vlib_increment_simple_counter (&sm->counters.slowpath.out2in.tcp, + vlib_increment_simple_counter (&nm->counters.slowpath.out2in.tcp, thread_index, sw_if_index0, 1); } else @@ -1317,7 +1296,7 @@ VLIB_NODE_FN (snat_out2in_node) (vlib_main_t * vm, udp0->checksum = ip_csum_fold (sum0); } } - vlib_increment_simple_counter (&sm->counters.slowpath.out2in.udp, + vlib_increment_simple_counter (&nm->counters.slowpath.out2in.udp, thread_index, sw_if_index0, 1); } @@ -1325,24 +1304,24 @@ VLIB_NODE_FN (snat_out2in_node) (vlib_main_t * vm, nat44_ei_session_update_counters ( s0, now, vlib_buffer_length_in_chain (vm, b0), thread_index); /* Per-user LRU list maintenance */ - nat44_session_update_lru (sm, s0, thread_index); + nat44_ei_session_update_lru (nm, s0, thread_index); trace00: if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE) && (b0->flags & VLIB_BUFFER_IS_TRACED))) { - snat_out2in_trace_t *t = vlib_add_trace (vm, node, b0, sizeof (*t)); + nat44_ei_out2in_trace_t *t = + vlib_add_trace (vm, node, b0, sizeof (*t)); t->sw_if_index = sw_if_index0; t->next_index = next0; t->session_index = ~0; if (s0) - t->session_index = - s0 - sm->per_thread_data[thread_index].sessions; + t->session_index = s0 - nm->per_thread_data[thread_index].sessions; } - if (next0 == SNAT_OUT2IN_NEXT_DROP) + if (next0 == NAT44_EI_OUT2IN_NEXT_DROP) { - vlib_increment_simple_counter (&sm->counters.slowpath.out2in.drops, + vlib_increment_simple_counter (&nm->counters.slowpath.out2in.drops, thread_index, sw_if_index0, 1); } @@ -1358,34 +1337,33 @@ VLIB_NODE_FN (snat_out2in_node) (vlib_main_t * vm, } /* *INDENT-OFF* */ -VLIB_REGISTER_NODE (snat_out2in_node) = { - .name = "nat44-out2in", +VLIB_REGISTER_NODE (nat44_ei_out2in_node) = { + .name = "nat44-ei-out2in", .vector_size = sizeof (u32), - .format_trace = format_snat_out2in_trace, + .format_trace = format_nat44_ei_out2in_trace, .type = VLIB_NODE_TYPE_INTERNAL, - .n_errors = ARRAY_LEN(snat_out2in_error_strings), - .error_strings = snat_out2in_error_strings, + .n_errors = ARRAY_LEN(nat44_ei_out2in_error_strings), + .error_strings = nat44_ei_out2in_error_strings, - .runtime_data_bytes = sizeof (snat_runtime_t), + .runtime_data_bytes = sizeof (nat44_ei_runtime_t), - .n_next_nodes = SNAT_OUT2IN_N_NEXT, + .n_next_nodes = NAT44_EI_OUT2IN_N_NEXT, /* edit / add dispositions here */ .next_nodes = { - [SNAT_OUT2IN_NEXT_DROP] = "error-drop", - [SNAT_OUT2IN_NEXT_LOOKUP] = "ip4-lookup", - [SNAT_OUT2IN_NEXT_ICMP_ERROR] = "ip4-icmp-error", + [NAT44_EI_OUT2IN_NEXT_DROP] = "error-drop", + [NAT44_EI_OUT2IN_NEXT_LOOKUP] = "ip4-lookup", + [NAT44_EI_OUT2IN_NEXT_ICMP_ERROR] = "ip4-icmp-error", }, }; /* *INDENT-ON* */ -VLIB_NODE_FN (snat_out2in_fast_node) (vlib_main_t * vm, - vlib_node_runtime_t * node, - vlib_frame_t * frame) +VLIB_NODE_FN (nat44_ei_out2in_fast_node) +(vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame) { u32 n_left_from, *from; - snat_main_t *sm = &snat_main; + nat44_ei_main_t *nm = &nat44_ei_main; from = vlib_frame_vector_args (frame); n_left_from = frame->n_vectors; @@ -1396,7 +1374,7 @@ VLIB_NODE_FN (snat_out2in_fast_node) (vlib_main_t * vm, while (n_left_from > 0) { vlib_buffer_t *b0; - u32 next0 = SNAT_OUT2IN_NEXT_DROP; + u32 next0 = NAT44_EI_OUT2IN_NEXT_DROP; u32 sw_if_index0; ip4_header_t *ip0; ip_csum_t sum0; @@ -1430,7 +1408,7 @@ VLIB_NODE_FN (snat_out2in_fast_node) (vlib_main_t * vm, icmp4_error_set_vnet_buffer (b0, ICMP4_time_exceeded, ICMP4_time_exceeded_ttl_exceeded_in_transit, 0); - next0 = SNAT_OUT2IN_NEXT_ICMP_ERROR; + next0 = NAT44_EI_OUT2IN_NEXT_ICMP_ERROR; goto trace00; } @@ -1441,8 +1419,8 @@ VLIB_NODE_FN (snat_out2in_fast_node) (vlib_main_t * vm, if (PREDICT_FALSE (proto0 == NAT_PROTOCOL_ICMP)) { - next0 = icmp_out2in (sm, b0, ip0, icmp0, sw_if_index0, rx_fib_index0, - node, next0, ~0, 0); + next0 = nat44_ei_icmp_out2in (b0, ip0, icmp0, sw_if_index0, + rx_fib_index0, node, next0, ~0, 0); goto trace00; } @@ -1450,7 +1428,7 @@ VLIB_NODE_FN (snat_out2in_fast_node) (vlib_main_t * vm, rx_fib_index0, proto0, &sm_addr0, &sm_port0, &sm_fib_index0, 1, 0, 0)) { - b0->error = node->errors[SNAT_OUT2IN_ERROR_NO_TRANSLATION]; + b0->error = node->errors[NAT44_EI_OUT2IN_ERROR_NO_TRANSLATION]; goto trace00; } @@ -1518,14 +1496,15 @@ VLIB_NODE_FN (snat_out2in_fast_node) (vlib_main_t * vm, if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE) && (b0->flags & VLIB_BUFFER_IS_TRACED))) { - snat_out2in_trace_t *t = vlib_add_trace (vm, node, b0, sizeof (*t)); + nat44_ei_out2in_trace_t *t = + vlib_add_trace (vm, node, b0, sizeof (*t)); t->sw_if_index = sw_if_index0; t->next_index = next0; } - if (next0 == SNAT_OUT2IN_NEXT_DROP) + if (next0 == NAT44_EI_OUT2IN_NEXT_DROP) { - vlib_increment_simple_counter (&sm->counters.fastpath.out2in.drops, + vlib_increment_simple_counter (&nm->counters.fastpath.out2in.drops, vm->thread_index, sw_if_index0, 1); } @@ -1541,24 +1520,24 @@ VLIB_NODE_FN (snat_out2in_fast_node) (vlib_main_t * vm, } /* *INDENT-OFF* */ -VLIB_REGISTER_NODE (snat_out2in_fast_node) = { - .name = "nat44-out2in-fast", +VLIB_REGISTER_NODE (nat44_ei_out2in_fast_node) = { + .name = "nat44-ei-out2in-fast", .vector_size = sizeof (u32), - .format_trace = format_snat_out2in_fast_trace, + .format_trace = format_nat44_ei_out2in_fast_trace, .type = VLIB_NODE_TYPE_INTERNAL, - .n_errors = ARRAY_LEN(snat_out2in_error_strings), - .error_strings = snat_out2in_error_strings, + .n_errors = ARRAY_LEN(nat44_ei_out2in_error_strings), + .error_strings = nat44_ei_out2in_error_strings, - .runtime_data_bytes = sizeof (snat_runtime_t), + .runtime_data_bytes = sizeof (nat44_ei_runtime_t), - .n_next_nodes = SNAT_OUT2IN_N_NEXT, + .n_next_nodes = NAT44_EI_OUT2IN_N_NEXT, /* edit / add dispositions here */ .next_nodes = { - [SNAT_OUT2IN_NEXT_LOOKUP] = "ip4-lookup", - [SNAT_OUT2IN_NEXT_DROP] = "error-drop", - [SNAT_OUT2IN_NEXT_ICMP_ERROR] = "ip4-icmp-error", + [NAT44_EI_OUT2IN_NEXT_LOOKUP] = "ip4-lookup", + [NAT44_EI_OUT2IN_NEXT_DROP] = "error-drop", + [NAT44_EI_OUT2IN_NEXT_ICMP_ERROR] = "ip4-icmp-error", }, }; /* *INDENT-ON* */ diff --git a/src/plugins/nat/nat44.api b/src/plugins/nat/nat44.api deleted file mode 100644 index f7fe7fd0fac..00000000000 --- a/src/plugins/nat/nat44.api +++ /dev/null @@ -1,1214 +0,0 @@ -/* - * Copyright (c) 2020 Cisco and/or its affiliates. - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -option version = "5.2.0"; -import "vnet/ip/ip_types.api"; -import "vnet/interface_types.api"; -import "plugins/nat/nat_types.api"; - -/** - * @file nat44.api - * @brief VPP control-plane API messages. - * - * This file defines VPP control-plane API messages which are generally - * called through a shared memory interface. - */ - -enum nat44_config_flags : u8 -{ - NAT44_IS_ENDPOINT_INDEPENDENT = 0x00, - NAT44_IS_ENDPOINT_DEPENDENT = 0x01, - NAT44_IS_STATIC_MAPPING_ONLY = 0x02, - NAT44_IS_CONNECTION_TRACKING = 0x04, - NAT44_IS_OUT2IN_DPO = 0x08, -}; - -/** \brief Enable/disable NAT44 plugin - @param client_index - opaque cookie to identify the sender - @param context - sender context, to match reply w/ request - @param inside_vrf - inside vrf id - @param outside_vrf - outside vrf id - @param users - maximum number of users per thread - (NAT44_IS_ENDPOINT_INDEPENDENT) - @param user_memory - overwrite hash allocation parameter - (NAT44_IS_ENDPOINT_INDEPENDENT) - @param sessions - maximum number of sessions per thread - @param session_memory - overwrite hash allocation parameter - @param user_sessions - maximum number of sessions per user - (NAT44_IS_ENDPOINT_INDEPENDENT) - @param enable - true if enable, false if disable - @param flags - flag NAT44_IS_ENDPOINT_INDEPENDENT, - NAT44_IS_ENDPOINT_DEPENDENT, - NAT44_IS_STATIC_MAPPING_ONLY, - NAT44_IS_CONNECTION_TRACKING, - NAT44_IS_OUT2IN_DPO -*/ -autoreply define nat44_plugin_enable_disable { - u32 client_index; - u32 context; - u32 inside_vrf; - u32 outside_vrf; - u32 users; - u32 user_memory; - u32 sessions; - u32 session_memory; - u32 user_sessions; - bool enable; - vl_api_nat44_config_flags_t flags; -}; - -/** \brief Control ping from client to api server request - @param client_index - opaque cookie to identify the sender - @param context - sender context, to match reply w/ request -*/ -define nat_control_ping -{ - option deprecated; - u32 client_index; - u32 context; -}; - -/** \brief Control ping from the client to the server response - @param client_index - opaque cookie to identify the sender - @param context - sender context, to match reply w/ request - @param retval - return code for the request - @param vpe_pid - the pid of the vpe, returned by the server -*/ -define nat_control_ping_reply -{ - option deprecated; - u32 context; - i32 retval; - u32 client_index; - u32 vpe_pid; -}; - -/** \brief Show NAT plugin startup config - @param client_index - opaque cookie to identify the sender - @param context - sender context, to match reply w/ request -*/ -define nat_show_config -{ - option deprecated; - u32 client_index; - u32 context; -}; - -/** \brief DEPRECATED: Show NAT plugin startup config reply - @param context - sender context, to match reply w/ request - @param retval - return code for the request - @param static_mapping_only - if true dynamic translations disabled - @param static_mapping_connection_tracking - if true create session data - @param deterministic - if true deterministic mapping - @param endpoint_dependent - if true endpoint-dependent mode - @param out2in_dpo - if true out2in dpo mode - @param dslite_ce - if true DS-Lite is CE/B4 element, if false AFTR elemet - @param translation_buckets - number of translation hash buckets - @param translation_memory_size - translation hash memory size - @param user_buckets - number of user hash buckets - @param user_memory_size - user hash memory size - @param max_translations_per_user - maximum number of translations per user - @param outside_vrf_id - outside VRF id - @param inside_vrf_id - default inside VRF id - @param nat64_bib_buckets - number of NAT64 BIB hash buckets - @param nat64_bib_memory_size - memory size of NAT64 BIB hash - @param nat64_st_buckets - number of NAT64 session table hash buckets - @param nat64_st_memory_size - memory size of NAT64 session table hash -*/ -define nat_show_config_reply -{ - option deprecated; - u32 context; - i32 retval; - bool static_mapping_only; - bool static_mapping_connection_tracking; - bool deterministic; - bool endpoint_dependent; - bool out2in_dpo; - bool dslite_ce; - u32 translation_buckets; - u32 translation_memory_size; - u32 user_buckets; - u64 user_memory_size; - u32 max_translations_per_user; - u32 outside_vrf_id; - u32 inside_vrf_id; - u32 nat64_bib_buckets; - u64 nat64_bib_memory_size; - u32 nat64_st_buckets; - u64 nat64_st_memory_size; -}; - -/** \brief Show NAT plugin startup config - @param client_index - opaque cookie to identify the sender - @param context - sender context, to match reply w/ request -*/ -define nat_show_config_2 -{ - option deprecated; - u32 client_index; - u32 context; -}; - -/** \brief Show NAT plugin startup config reply - @param context - sender context, to match reply w/ request - @param retval - return code for the request - @param static_mapping_only - if true dynamic translations disabled - @param static_mapping_connection_tracking - if true create session data - @param deterministic - if true deterministic mapping - @param endpoint_dependent - if true endpoint-dependent mode - @param out2in_dpo - if true out2in dpo mode - @param dslite_ce - if true DS-Lite is CE/B4 element, if false AFTR elemet - @param translation_buckets - number of translation hash buckets - @param translation_memory_size - translation hash memory size - @param user_buckets - number of user hash buckets - @param user_memory_size - user hash memory size - @param max_translations_per_user - maximum number of translations per user - @param outside_vrf_id - outside VRF id - @param inside_vrf_id - default inside VRF id - @param nat64_bib_buckets - number of NAT64 BIB hash buckets - @param nat64_bib_memory_size - memory size of NAT64 BIB hash - @param nat64_st_buckets - number of NAT64 session table hash buckets - @param nat64_st_memory_size - memory size of NAT64 session table hash - @param max_translations_per_thread - max translations per worker thread - @param max_users_per_thread - max users per worker thread -*/ -define nat_show_config_2_reply -{ - option deprecated; - u32 context; - i32 retval; - bool static_mapping_only; - bool static_mapping_connection_tracking; - bool deterministic; - bool endpoint_dependent; - bool out2in_dpo; - bool dslite_ce; - u32 translation_buckets; - u64 translation_memory_size; - u32 user_buckets; - u64 user_memory_size; - u32 max_translations_per_user; - u32 outside_vrf_id; - u32 inside_vrf_id; - u32 nat64_bib_buckets; - u64 nat64_bib_memory_size; - u32 nat64_st_buckets; - u64 nat64_st_memory_size; - u32 max_translations_per_thread; - u32 max_users_per_thread; -}; - -/** \brief Show NAT44 plugin running config - @param client_index - opaque cookie to identify the sender - @param context - sender context, to match reply w/ request -*/ -define nat44_show_running_config -{ - option in_progress; - u32 client_index; - u32 context; -}; - -/** \brief Show NAT44 plugin running config reply - @param context - sender context, to match reply w/ request - @param retval - return code for the request - @param inside_vrf - default inside VRF id - @param outside_vrf - outside VRF id - @param users - maximum number of users per worker thread - (NAT44_IS_ENDPOINT_INDEPENDENT) - @param sessions - maximum number of sessions per worker thread - @param user_sessions - maximum number of sessions per user - (NAT44_IS_ENDPOINT_INDEPENDENT) - @param user_buckets - number of user hash buckets - (NAT44_IS_ENDPOINT_INDEPENDENT) - @param translation_buckets - number of translation hash buckets - @param flags - flag NAT44_IS_ENDPOINT_INDEPENDENT, - NAT44_IS_ENDPOINT_DEPENDENT, - NAT44_IS_STATIC_MAPPING_ONLY, - NAT44_IS_CONNECTION_TRACKING, - NAT44_IS_OUT2IN_DPO -*/ -define nat44_show_running_config_reply -{ - option in_progress; - u32 context; - i32 retval; - u32 inside_vrf; - u32 outside_vrf; - u32 users; - u32 sessions; - u32 user_sessions; - u32 user_buckets; - u32 translation_buckets; - bool forwarding_enabled; - bool ipfix_logging_enabled; - vl_api_nat_timeouts_t timeouts; - vl_api_nat_log_level_t log_level; - vl_api_nat44_config_flags_t flags; -}; - -/** \brief Run nat44 garbage collection - @param client_index - opaque cookie to identify the sender - @param context - sender context, to match reply w/ request -*/ -autoreply define nat44_session_cleanup { - option deprecated; - u32 client_index; - u32 context; -}; - -/** \brief NAT44 set session limit - @param client_index - opaque cookie to identify the sender - @param context - sender context, to match reply w/ request - @param session_limit - session limit - @param vrf_id - vrf id -*/ -autoreply define nat44_set_session_limit { - u32 client_index; - u32 context; - u32 session_limit; - u32 vrf_id; -}; - -/** \brief Set NAT logging level - @param client_index - opaque cookie to identify the sender - @param context - sender context, to match reply w/ request - @param log_level - logging level -*/ -autoreply define nat_set_log_level { - option deprecated; - u32 client_index; - u32 context; - vl_api_nat_log_level_t log_level; -}; - -/** \brief Set NAT workers - @param client_index - opaque cookie to identify the sender - @param context - sender context, to match reply w/ request - @param worker_mask - NAT workers mask -*/ -autoreply define nat_set_workers { - u32 client_index; - u32 context; - u64 worker_mask; -}; - -/** \brief Dump NAT workers - @param client_index - opaque cookie to identify the sender - @param context - sender context, to match reply w/ request -*/ -define nat_worker_dump { - u32 client_index; - u32 context; -}; - -/** \brief NAT workers details response - @param context - sender context, to match reply w/ request - @param worker_index - worker index - @param lcore_id - lcore ID - @param name - worker name -*/ -define nat_worker_details { - u32 context; - u32 worker_index; - u32 lcore_id; - string name[64]; -}; - -/** \brief Enable/disable NAT IPFIX logging - @param client_index - opaque cookie to identify the sender - @param context - sender context, to match reply w/ request - @param domain_id - observation domain ID - @param src_port - source port number - @param enable - true if enable, false if disable -*/ -autoreply define nat_ipfix_enable_disable { - option deprecated; - u32 client_index; - u32 context; - u32 domain_id; - u16 src_port; - bool enable; -}; - -/** \brief Set values of timeouts for NAT sessions (seconds) - @param client_index - opaque cookie to identify the sender - @param context - sender context, to match reply w/ request - @param udp - UDP timeout (default 300sec) - @param tcp_established - TCP established timeout (default 7440sec) - @param tcp_transitory - TCP transitory timeout (default 240sec) - @param icmp - ICMP timeout (default 60sec) -*/ -autoreply define nat_set_timeouts { - option deprecated; - u32 client_index; - u32 context; - u32 udp; - u32 tcp_established; - u32 tcp_transitory; - u32 icmp; -}; - -/** \brief Get values of timeouts for NAT sessions (seconds) - @param client_index - opaque cookie to identify the sender - @param context - sender context, to match reply w/ request -*/ -define nat_get_timeouts { - option deprecated; - u32 client_index; - u32 context; -}; - -/** \brief Get values of timeouts for NAT sessions reply - @param context - sender context, to match reply w/ request - @param retval - return code - @param udp - UDP timeout - @param tcp_established - TCP established timeout - @param tcp_transitory - TCP transitory timeout - @param icmp - ICMP timeout -*/ -define nat_get_timeouts_reply { - option deprecated; - u32 context; - i32 retval; - u32 udp; - u32 tcp_established; - u32 tcp_transitory; - u32 icmp; -}; - -/** \brief Set NAT handoff frame queue options - @param client_index - opaque cookie to identify the sender - @param context - sender context, to match reply w/ request - @param frame_queue_nelts - number of worker handoff frame queue elements -*/ -autoreply define nat_set_fq_options { - option in_progress; - u32 client_index; - u32 context; - u32 frame_queue_nelts; -}; - -/** \brief Show NAT handoff frame queue options - @param client_index - opaque cookie to identify the sender - @param context - sender context, to match reply w/ request -*/ -define nat_show_fq_options -{ - option in_progress; - u32 client_index; - u32 context; -}; - -/** \brief Show NAT handoff frame queue options reply - @param context - sender context, to match reply w/ request - @param retval - return code for the request - @param frame_queue_nelts - number of worker handoff frame queue elements -*/ -define nat_show_fq_options_reply -{ - option in_progress; - u32 context; - i32 retval; - u32 frame_queue_nelts; -}; - -/** \brief Set address and port assignment algorithm - @param client_index - opaque cookie to identify the sender - @param context - sender context, to match reply w/ request - @param alg - address and port assignment algorithm: - 0 - default, 1 - MAP-E, 2 - port range - (see nat_addr_and_port_alloc_alg_t in nat.h) - @param psid_offset - number of offset bits (valid only for MAP-E alg) - @param psid_length - length of PSID (valid only for MAP-E alg) - @param psid - Port Set Identifier (PSID) value (valid only for MAP-E alg) - @param start_port - beginning of the port range - @param end_port - end of the port range -*/ -autoreply define nat_set_addr_and_port_alloc_alg { - u32 client_index; - u32 context; - u8 alg; - u8 psid_offset; - u8 psid_length; - u16 psid; - u16 start_port; - u16 end_port; -}; - -/** \brief Get address and port assignment algorithm - @param client_index - opaque cookie to identify the sender - @param context - sender context, to match reply w/ request -*/ -define nat_get_addr_and_port_alloc_alg { - u32 client_index; - u32 context; -}; - -/** \brief Get address and port assignment algorithm reply - @param context - sender context, to match reply w/ request - @param retval - return code - @param alg - address and port assignment algorithm: - 0 - default, 1 - MAP-E, 2 - port range - (see nat_addr_and_port_alloc_alg_t in nat.h) - @param psid_offset - number of offset bits (valid only for MAP-E alg) - @param psid_length - length of PSID (valid only for MAP-E alg) - @param psid - Port Set Identifier (PSID) value (valid only for MAP-E alg) - @param start_port - beginning of the port range - @param end_port - end of the port range -*/ -define nat_get_addr_and_port_alloc_alg_reply { - u32 context; - i32 retval; - u8 alg; - u8 psid_offset; - u8 psid_length; - u16 psid; - u16 start_port; - u16 end_port; -}; - -/** \brief Set TCP MSS rewriting configuration - @param client_index - opaque cookie to identify the sender - @param context - sender context, to match reply w/ request - @param mss_value - MSS value to be used for MSS rewriting - @param enable - if true enable MSS rewriting feature else disable -*/ -autoreply define nat_set_mss_clamping { - u32 client_index; - u32 context; - u16 mss_value; - bool enable; -}; - -/** \brief Get TCP MSS rewriting configuration - @param client_index - opaque cookie to identify the sender - @param context - sender context, to match reply w/ request -*/ -define nat_get_mss_clamping { - u32 client_index; - u32 context; -}; - -/** \brief Get TCP MSS rewriting configuration reply - @param context - sender context, to match reply w/ request - @param retval - return code - @param mss_value - MSS value to be used for MSS rewriting - @param enable - if true enable MSS rewriting feature else disable -*/ -define nat_get_mss_clamping_reply { - u32 context; - i32 retval; - u16 mss_value; - bool enable; -}; - -/** \brief Set HA listener (local settings) - @param client_index - opaque cookie to identify the sender - @param context - sender context, to match reply w/ request - @param ip_address - local IP4 address - @param port - local UDP port number - @param path_mtu - path MTU between local and failover -*/ -autoreply define nat_ha_set_listener { - u32 client_index; - u32 context; - vl_api_ip4_address_t ip_address; - u16 port; - u32 path_mtu; -}; - -/** \brief Set HA failover (remote settings) - @param client_index - opaque cookie to identify the sender - @param context - sender context, to match reply w/ request - @param ip_address - failover IP4 address - @param port - failvoer UDP port number - @param session_refresh_interval - number of seconds after which to send - session counters refresh -*/ -autoreply define nat_ha_set_failover { - u32 client_index; - u32 context; - vl_api_ip4_address_t ip_address; - u16 port; - u32 session_refresh_interval; -}; - -/** \brief Get HA listener/local configuration - @param client_index - opaque cookie to identify the sender - @param context - sender context, to match reply w/ request -*/ -define nat_ha_get_listener { - u32 client_index; - u32 context; -}; - -/** \brief Get HA listener/local configuration reply - @param context - sender context, to match reply w/ request - @param retval - return code - @param ip_address - local IP4 address - @param port - local UDP port number - @param path_mtu - Path MTU between local and failover -*/ -define nat_ha_get_listener_reply { - u32 context; - i32 retval; - vl_api_ip4_address_t ip_address; - u16 port; - u32 path_mtu; -}; - -/** \brief Get HA failover/remote settings - @param client_index - opaque cookie to identify the sender - @param context - sender context, to match reply w/ request -*/ -define nat_ha_get_failover { - u32 client_index; - u32 context; -}; - -/** \brief Get HA failover/remote settings reply - @param context - sender context, to match reply w/ request - @param retval - return code - @param ip_address - failover IP4 address - @param port - failvoer UDP port number - @param session_refresh_interval - number of seconds after which to send - session counters refresh -*/ -define nat_ha_get_failover_reply { - u32 context; - i32 retval; - vl_api_ip4_address_t ip_address; - u16 port; - u32 session_refresh_interval; -}; - -/** \brief Flush the current HA data (for testing) - @param client_index - opaque cookie to identify the sender - @param context - sender context, to match reply w/ request -*/ -autoreply define nat_ha_flush { - u32 client_index; - u32 context; -}; - -/** \brief Resync HA (resend existing sessions to new failover) - @param client_index - opaque cookie to identify the sender - @param context - sender context, to match reply w/ request - @param want_resync_event - resync completed event sent to the sender via - nat_ha_resync_completed_event API message if - non-zero - @param pid - sender's pid -*/ -autoreply define nat_ha_resync -{ - u32 client_index; - u32 context; - u8 want_resync_event; - u32 pid; -}; - -/** \brief Tell client about a HA resync completion event - @param client_index - opaque cookie to identify the sender - @param pid - client pid registered to receive notification - @param missed_count - number of missed (not ACKed) messages -*/ -define nat_ha_resync_completed_event -{ - u32 client_index; - u32 pid; - u32 missed_count; -}; - -service { - rpc nat_ha_resync returns nat_ha_resync_reply events nat_ha_resync_completed_event; -}; - -/** \brief Del NAT44 user - @param client_index - opaque cookie to identify the sender - @param context - sender context, to match reply w/ request - @param ip_address - IPv4 address - @param fib_index - FIB index -*/ -autoreply define nat44_del_user { - u32 client_index; - u32 context; - vl_api_ip4_address_t ip_address; - u32 fib_index; -}; - -/** \brief Add/del NAT44 address range - @param client_index - opaque cookie to identify the sender - @param context - sender context, to match reply w/ request - @param first_ip_address - first IPv4 address - @param last_ip_address - last IPv4 address - @param vrf_id - VRF id of tenant, ~0 means independent of VRF - @param is_add - true if add, false if delete - @param flags - flag NAT_IS_TWICE_NAT if NAT address range for external hosts - -*/ -autoreply define nat44_add_del_address_range { - u32 client_index; - u32 context; - vl_api_ip4_address_t first_ip_address; - vl_api_ip4_address_t last_ip_address; - u32 vrf_id; - bool is_add; - vl_api_nat_config_flags_t flags; -}; - -/** \brief Dump NAT44 addresses - @param client_index - opaque cookie to identify the sender - @param context - sender context, to match reply w/ request -*/ -define nat44_address_dump { - u32 client_index; - u32 context; -}; - -/** \brief NAT44 address details response - @param context - sender context, to match reply w/ request - @param ip_address - IPv4 address - @param flags - flag NAT_IS_TWICE_NAT if NAT address range for external hosts - @param vrf_id - VRF id of tenant, ~0 means independent of VRF -*/ -define nat44_address_details { - u32 context; - vl_api_ip4_address_t ip_address; - vl_api_nat_config_flags_t flags; - u32 vrf_id; -}; - -/** \brief Enable/disable NAT44 feature on the interface - @param client_index - opaque cookie to identify the sender - @param context - sender context, to match reply w/ request - @param is_add - true if add, false if delete - @param flags - flag NAT_IS_INSIDE if interface is inside else - interface is outside - @param sw_if_index - software index of the interface -*/ -autoreply define nat44_interface_add_del_feature { - u32 client_index; - u32 context; - bool is_add; - vl_api_nat_config_flags_t flags; - vl_api_interface_index_t sw_if_index; -}; - -/** \brief Dump interfaces with NAT44 feature - @param client_index - opaque cookie to identify the sender - @param context - sender context, to match reply w/ request -*/ -define nat44_interface_dump { - u32 client_index; - u32 context; -}; - -/** \brief NAT44 interface details response - @param context - sender context, to match reply w/ request - @param sw_if_index - software index of the interface - @param flags - flag NAT_IS_INSIDE if interface is inside, - flag NAT_IS_OUTSIDE if interface is outside - and if both flags are set the interface is - both inside and outside -*/ -define nat44_interface_details { - u32 context; - vl_api_nat_config_flags_t flags; - vl_api_interface_index_t sw_if_index; -}; - -/** \brief Enable/disbale NAT44 as an interface output feature (postrouting - in2out translation) - @param client_index - opaque cookie to identify the sender - @param context - sender context, to match reply w/ request - @param is_add - true if add, false if delete - @param flags - flag NAT_IS_INSIDE if interface is inside else - interface is outside - @param sw_if_index - software index of the interface -*/ -autoreply define nat44_interface_add_del_output_feature { - u32 client_index; - u32 context; - bool is_add; - vl_api_nat_config_flags_t flags; - vl_api_interface_index_t sw_if_index; -}; - -/** \brief Dump interfaces with NAT44 output feature - @param client_index - opaque cookie to identify the sender - @param context - sender context, to match reply w/ request -*/ -define nat44_interface_output_feature_dump { - u32 client_index; - u32 context; -}; - -/** \brief NAT44 interface with output feature details response - @param context - sender context, to match reply w/ request - @param flags - flag NAT_IS_INSIDE if interface is inside else - interface is outside - @param sw_if_index - software index of the interface -*/ -define nat44_interface_output_feature_details { - u32 context; - vl_api_nat_config_flags_t flags; - vl_api_interface_index_t sw_if_index; -}; - -/** \brief Add/delete NAT44 static mapping - @param client_index - opaque cookie to identify the sender - @param context - sender context, to match reply w/ request - @param is_add - true if add, false if delete - @param flags - flag NAT_IS_ADDR_ONLY if address only mapping, - flag nat_is_twice_nat if nat address range for external hosts, - flag NAT_IS_SELF_TWICE_NAT if translate external host address - and port whenever external host address equals local - address of internal host, - flag NAT_IS_OUT2IN_ONLY if rule match only out2in direction - @param local_ip_address - local IPv4 address - @param external_ip_address - external IPv4 address - @param protocol - IP protocol, used only if addr_only=0 - @param local_port - local port number, used only if addr_only=0 - @param external_port - external port number, used only if addr_only=0 - @param external_sw_if_index - external interface (if set - external_ip_address is ignored, ~0 means not - used) - @param vfr_id - VRF ID - @param tag - opaque string tag -*/ -autoreply define nat44_add_del_static_mapping { - u32 client_index; - u32 context; - bool is_add; - vl_api_nat_config_flags_t flags; - vl_api_ip4_address_t local_ip_address; - vl_api_ip4_address_t external_ip_address; - u8 protocol; - u16 local_port; - u16 external_port; - vl_api_interface_index_t external_sw_if_index; - u32 vrf_id; - string tag[64]; -}; - -/** \brief Add/delete NAT44 static mapping - @param client_index - opaque cookie to identify the sender - @param context - sender context, to match reply w/ request - @param is_add - true if add, false if delete - @param match_pool - true if use specific pool_ip_address - @param flags - flag NAT_IS_ADDR_ONLY if address only mapping, - flag nat_is_twice_nat if nat address range for external hosts, - flag NAT_IS_SELF_TWICE_NAT if translate external host address - and port whenever external host address equals local - address of internal host, - flag NAT_IS_OUT2IN_ONLY if rule match only out2in direction - @param pool_ip_address - pool IPv4 address to match with pool - @param local_ip_address - local IPv4 address - @param external_ip_address - external IPv4 address - @param protocol - IP protocol, used only if addr_only=0 - @param local_port - local port number, used only if addr_only=0 - @param external_port - external port number, used only if addr_only=0 - @param external_sw_if_index - external interface (if set - external_ip_address is ignored, ~0 means not - used) - @param vfr_id - VRF ID - @param tag - opaque string tag -*/ -autoreply define nat44_add_del_static_mapping_v2 { - option in_progress; - u32 client_index; - u32 context; - bool is_add; - bool match_pool; - vl_api_nat_config_flags_t flags; - vl_api_ip4_address_t pool_ip_address; - vl_api_ip4_address_t local_ip_address; - vl_api_ip4_address_t external_ip_address; - u8 protocol; - u16 local_port; - u16 external_port; - vl_api_interface_index_t external_sw_if_index; - u32 vrf_id; - string tag[64]; -}; - -/** \brief Dump NAT44 static mappings - @param client_index - opaque cookie to identify the sender - @param context - sender context, to match reply w/ request -*/ -define nat44_static_mapping_dump { - u32 client_index; - u32 context; -}; - -/** \brief NAT44 static mapping details response - @param context - sender context, to match reply w/ request - @param flags - flag NAT_ADDR_ONLY if address only mapping, - flag NAT_TWICE_NAT if NAT address range for external hosts, - flag NAT_SELF_TWICE_NAT if translate external host address - and port whenever external host address equals local - address of internal host, - flag NAT_OUT2IN_ONLY if rule match only out2in direction - @param local_ip_address - local IPv4 address - @param external_ip_address - external IPv4 address - @param protocol - IP protocol, valid only if no NAT_ADDR_ONLY flag - @param local_port - local port number, valid only if no NAT_ADDR_ONLY flag - @param external_port - external port number, valid only if no NAT_ADDR_ONLY flag - @param external_sw_if_index - external interface - @param vfr_id - VRF ID - @param tag - opaque string tag -*/ -define nat44_static_mapping_details { - u32 context; - vl_api_nat_config_flags_t flags; - vl_api_ip4_address_t local_ip_address; - vl_api_ip4_address_t external_ip_address; - u8 protocol; - u16 local_port; - u16 external_port; - vl_api_interface_index_t external_sw_if_index; - u32 vrf_id; - string tag[64]; -}; - -/** \brief Add/delete NAT44 identity mapping - @param client_index - opaque cookie to identify the sender - @param context - sender context, to match reply w/ request - @param is_add - true if add, false if delete - @param flags - flag NAT_ADDR_ONLY if address only mapping - @param ip_address - IPv4 address - @param protocol - IP protocol - @param port - port number - @param sw_if_index - interface (if set ip_address is ignored, ~0 means not - used) - @param vfr_id - VRF ID (if ~0 use default VRF) - @param tag - opaque string tag -*/ -autoreply define nat44_add_del_identity_mapping { - u32 client_index; - u32 context; - bool is_add; - vl_api_nat_config_flags_t flags; - vl_api_ip4_address_t ip_address; - u8 protocol; - u16 port; - vl_api_interface_index_t sw_if_index; - u32 vrf_id; - string tag[64]; -}; - -/** \brief Dump NAT44 identity mappings - @param client_index - opaque cookie to identify the sender - @param context - sender context, to match reply w/ request -*/ -define nat44_identity_mapping_dump { - u32 client_index; - u32 context; -}; - -/** \brief NAT44 identity mapping details response - @param context - sender context, to match reply w/ request - @param flags - flag NAT_ADDR_ONLY if address only mapping - @param ip_address - IPv4 address - @param protocol - IP protocol - @param port - port number - @param sw_if_index - interface - @param vfr_id - VRF ID - @param tag - opaque string tag -*/ -define nat44_identity_mapping_details { - u32 context; - vl_api_nat_config_flags_t flags; - vl_api_ip4_address_t ip_address; - u8 protocol; - u16 port; - vl_api_interface_index_t sw_if_index; - u32 vrf_id; - string tag[64]; -}; - -/** \brief Add/delete NAT44 pool address from specific interfce - @param client_index - opaque cookie to identify the sender - @param context - sender context, to match reply w/ request - @param is_add - true if add, false if delete - @param sw_if_index - software index of the interface - @param flags - flag NAT_TWICE_NAT if NAT address range for external hosts -*/ -autoreply define nat44_add_del_interface_addr { - u32 client_index; - u32 context; - bool is_add; - vl_api_interface_index_t sw_if_index; - vl_api_nat_config_flags_t flags; -}; - -/** \brief Dump NAT44 pool addresses interfaces - @param client_index - opaque cookie to identify the sender - @param context - sender context, to match reply w/ request -*/ -define nat44_interface_addr_dump { - u32 client_index; - u32 context; -}; - -/** \brief NAT44 pool addresses interfaces details response - @param context - sender context, to match reply w/ request - @param sw_if_index - software index of the interface - @param flags - flag NAT_TWICE_NAT if NAT address range for external hosts - -*/ -define nat44_interface_addr_details { - u32 context; - vl_api_interface_index_t sw_if_index; - vl_api_nat_config_flags_t flags; -}; - -/** \brief Dump NAT44 users - @param client_index - opaque cookie to identify the sender - @param context - sender context, to match reply w/ request -*/ -define nat44_user_dump { - u32 client_index; - u32 context; -}; - -/** \brief NAT44 users response - @param context - sender context, to match reply w/ request - @vrf_id - VRF ID - @param ip_address - IPv4 address - @param nsessions - number of dynamic sessions - @param nstaticsessions - number of static sessions -*/ -define nat44_user_details { - u32 context; - u32 vrf_id; - vl_api_ip4_address_t ip_address; - u32 nsessions; - u32 nstaticsessions; -}; - -/** \brief NAT44 user's sessions - @param client_index - opaque cookie to identify the sender - @param context - sender context, to match reply w/ request - @param ip_address - IPv4 address of the user to dump - @param vrf_id - VRF_ID -*/ -define nat44_user_session_dump { - u32 client_index; - u32 context; - vl_api_ip4_address_t ip_address; - u32 vrf_id; -}; - -/** \brief NAT44 user's sessions response - @param context - sender context, to match reply w/ request - @param outside_ip_address - outside IPv4 address - @param outside_port - outside port - @param inside_ip_address - inside IPv4 address - @param inside_port - inside port - @param protocol - protocol - @param flags - flag NAT_IS_STATIC if session is static, - flag NAT_IS_TWICE_NAT if session is twice-nat, - flag NAT_IS_EXT_HOST_VALID if external host address - and port are valid - @param last_heard - last heard timer - @param total_bytes - count of bytes sent through session - @param total_pkts - count of pakets sent through session - @param ext_host_address - external host IPv4 address - @param ext_host_port - external host port - @param ext_host_nat_address - post-NAT external host IPv4 address (valid - only if twice-nat session) - @param ext_host_nat_port - post-NAT external host port (valid only if - twice-nat session) -*/ -define nat44_user_session_details { - u32 context; - vl_api_ip4_address_t outside_ip_address; - u16 outside_port; - vl_api_ip4_address_t inside_ip_address; - u16 inside_port; - u16 protocol; - vl_api_nat_config_flags_t flags; - u64 last_heard; - u64 total_bytes; - u32 total_pkts; - vl_api_ip4_address_t ext_host_address; - u16 ext_host_port; - vl_api_ip4_address_t ext_host_nat_address; - u16 ext_host_nat_port; -}; - -/** \brief NAT44 load-balancing address and port pair - @param addr - IPv4 address of the internal node - @param port - L4 port number of the internal node - @param probability - probability of the internal node to be randomly matched - @param vrf_id - VRF id -*/ -typedef nat44_lb_addr_port { - vl_api_ip4_address_t addr; - u16 port; - u8 probability; - u32 vrf_id; -}; - -/** \brief Add/delete NAT44 load-balancing static mapping rule - @param client_index - opaque cookie to identify the sender - @param context - sender context, to match reply w/ request - @param is_add - true if add, false if delete - @param flags - flag NAT_TWICE_NAT if NAT address range for external hosts, - flag NAT_SELF_TWICE_NAT if translate external host address - and port whenever external host address equals local - address of internal host, - flag NAT_OUT2IN_ONLY if rule match only out2in direction - @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 affinity - if 0 disabled, otherwise client IP affinity sticky time - in seconds - @param local_num - number of local network nodes - @param locals - local network nodes - @param tag - opaque string tag -*/ -autoreply define nat44_add_del_lb_static_mapping { - u32 client_index; - u32 context; - bool is_add; - vl_api_nat_config_flags_t flags; - vl_api_ip4_address_t external_addr; - u16 external_port; - u8 protocol; - u32 affinity; - string tag[64]; - 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 - true if add, false 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; - bool is_add; - vl_api_ip4_address_t external_addr; - 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 -*/ -define nat44_lb_static_mapping_dump { - u32 client_index; - u32 context; -}; - -/** \brief NAT44 load-balancing static mapping rule details response - @param context - sender context, to match reply w/ request - @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 flags - flag NAT_TWICE_NAT if NAT address range for external hosts, - flag NAT_SELF_TWICE_NAT if translate external host address - and port whenever external host address equals local - address of internal host, - flag NAT_OUT2IN_ONLY if rule match only out2in direction - @param affinity - if 0 disabled, otherwise client IP affinity sticky time - in seconds - @param local_num - number of local network nodes - @param locals - local network nodes - @param tag - opaque string tag -*/ -define nat44_lb_static_mapping_details { - u32 context; - vl_api_ip4_address_t external_addr; - u16 external_port; - u8 protocol; - vl_api_nat_config_flags_t flags; - u32 affinity; - string tag[64]; - u32 local_num; - vl_api_nat44_lb_addr_port_t locals[local_num]; -}; - -/** \brief Delete NAT44 session - @param client_index - opaque cookie to identify the sender - @param context - sender context, to match reply w/ request - @param ip_address - IPv4 address - @param protocol - IP protocol - @param port - port number - @param vfr_id - VRF ID - @param flags - flag NAT_IS_INSIDE if interface is inside or - interface is outside, - flag NAT_IS_EXT_HOST_VALID if external host address and - port are valid - @param ext_host_address - external host IPv4 address - @param ext_host_port - external host port -*/ -autoreply define nat44_del_session { - u32 client_index; - u32 context; - vl_api_ip4_address_t address; - u8 protocol; - u16 port; - u32 vrf_id; - vl_api_nat_config_flags_t flags; - vl_api_ip4_address_t ext_host_address; - u16 ext_host_port; -}; - -/** \brief Enable/disable forwarding for NAT44 - Forward packets which don't match existing translation - or static mapping instead of dropping them. - @param client_index - opaque cookie to identify the sender - @param context - sender context, to match reply w/ request - @param enable - true for enable, false for disable -*/ -autoreply define nat44_forwarding_enable_disable { - option deprecated; - u32 client_index; - u32 context; - bool enable; -}; - -/** \brief Check if forwarding is enabled or disabled - @param client_index - opaque cookie to identify the sender - @param context - sender context, to match reply w/ request -*/ -define nat44_forwarding_is_enabled { - option deprecated; - u32 client_index; - u32 context; -}; - -/** \brief Response to check if forwarding is enabled or disabled - @param context - sender context, to match reply w/ request - @param enabled - true if enabled, false if disabled -*/ -define nat44_forwarding_is_enabled_reply { - option deprecated; - u32 context; - bool enabled; -}; diff --git a/src/plugins/nat/nat44/ed_inlines.h b/src/plugins/nat/nat44/ed_inlines.h deleted file mode 100644 index 87de25e990b..00000000000 --- a/src/plugins/nat/nat44/ed_inlines.h +++ /dev/null @@ -1,361 +0,0 @@ -/* - * simple nat plugin - * - * Copyright (c) 2020 Cisco and/or its affiliates. - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef __included_ed_inlines_h__ -#define __included_ed_inlines_h__ - -#include -#include -#include -#include - -static_always_inline int -nat_ed_lru_insert (snat_main_per_thread_data_t * tsm, - snat_session_t * s, f64 now, u8 proto) -{ - dlist_elt_t *lru_list_elt; - pool_get (tsm->lru_pool, lru_list_elt); - s->lru_index = lru_list_elt - tsm->lru_pool; - switch (proto) - { - case IP_PROTOCOL_UDP: - s->lru_head_index = tsm->udp_lru_head_index; - break; - case IP_PROTOCOL_TCP: - s->lru_head_index = tsm->tcp_trans_lru_head_index; - break; - case IP_PROTOCOL_ICMP: - s->lru_head_index = tsm->icmp_lru_head_index; - break; - default: - s->lru_head_index = tsm->unk_proto_lru_head_index; - break; - } - clib_dlist_addtail (tsm->lru_pool, s->lru_head_index, s->lru_index); - lru_list_elt->value = s - tsm->sessions; - s->last_lru_update = now; - return 1; -} - -static_always_inline void -nat_6t_flow_to_ed_k (clib_bihash_kv_16_8_t *kv, nat_6t_flow_t *f) -{ - init_ed_k (kv, f->match.saddr, f->match.sport, f->match.daddr, - f->match.dport, f->match.fib_index, f->match.proto); -} - -static_always_inline void -nat_6t_flow_to_ed_kv (clib_bihash_kv_16_8_t *kv, nat_6t_flow_t *f, - u32 thread_idx, u32 session_idx) -{ - init_ed_kv (kv, f->match.saddr, f->match.sport, f->match.daddr, - f->match.dport, f->match.fib_index, f->match.proto, thread_idx, - session_idx); -} - -static_always_inline int -nat_ed_ses_i2o_flow_hash_add_del (snat_main_t *sm, u32 thread_idx, - snat_session_t *s, int is_add) -{ - snat_main_per_thread_data_t *tsm = - vec_elt_at_index (sm->per_thread_data, thread_idx); - clib_bihash_kv_16_8_t kv; - if (0 == is_add) - { - nat_6t_flow_to_ed_k (&kv, &s->i2o); - } - else - { - nat_6t_flow_to_ed_kv (&kv, &s->i2o, thread_idx, s - tsm->sessions); - nat_6t_l3_l4_csum_calc (&s->i2o); - } - return clib_bihash_add_del_16_8 (&sm->flow_hash, &kv, is_add); -} - -static_always_inline int -nat_ed_ses_o2i_flow_hash_add_del (snat_main_t *sm, u32 thread_idx, - snat_session_t *s, int is_add) -{ - snat_main_per_thread_data_t *tsm = - vec_elt_at_index (sm->per_thread_data, thread_idx); - clib_bihash_kv_16_8_t kv; - if (0 == is_add) - { - nat_6t_flow_to_ed_k (&kv, &s->o2i); - } - else - { - nat_6t_flow_to_ed_kv (&kv, &s->o2i, thread_idx, s - tsm->sessions); - nat_6t_l3_l4_csum_calc (&s->o2i); - } - return clib_bihash_add_del_16_8 (&sm->flow_hash, &kv, is_add); -} - -always_inline void -nat_ed_session_delete (snat_main_t * sm, snat_session_t * ses, - u32 thread_index, int lru_delete - /* delete from global LRU list */ ) -{ - snat_main_per_thread_data_t *tsm = vec_elt_at_index (sm->per_thread_data, - thread_index); - - if (lru_delete) - { - clib_dlist_remove (tsm->lru_pool, ses->lru_index); - } - pool_put_index (tsm->lru_pool, ses->lru_index); - if (nat_ed_ses_i2o_flow_hash_add_del (sm, thread_index, ses, 0)) - nat_elog_warn ("flow hash del failed"); - if (nat_ed_ses_o2i_flow_hash_add_del (sm, thread_index, ses, 0)) - nat_elog_warn ("flow hash del failed"); - pool_put (tsm->sessions, ses); - vlib_set_simple_counter (&sm->total_sessions, thread_index, 0, - pool_elts (tsm->sessions)); - -} - -static_always_inline int -nat_lru_free_one_with_head (snat_main_t * sm, int thread_index, - f64 now, u32 head_index) -{ - snat_session_t *s = NULL; - dlist_elt_t *oldest_elt; - f64 sess_timeout_time; - u32 oldest_index; - snat_main_per_thread_data_t *tsm = &sm->per_thread_data[thread_index]; - oldest_index = clib_dlist_remove_head (tsm->lru_pool, head_index); - if (~0 != oldest_index) - { - oldest_elt = pool_elt_at_index (tsm->lru_pool, oldest_index); - s = pool_elt_at_index (tsm->sessions, oldest_elt->value); - - sess_timeout_time = - s->last_heard + (f64) nat44_session_get_timeout (sm, s); - if (now >= sess_timeout_time - || (s->tcp_closed_timestamp && now >= s->tcp_closed_timestamp)) - { - nat_free_session_data (sm, s, thread_index, 0); - nat_ed_session_delete (sm, s, thread_index, 0); - return 1; - } - else - { - clib_dlist_addhead (tsm->lru_pool, head_index, oldest_index); - } - } - return 0; -} - -static_always_inline int -nat_lru_free_one (snat_main_t * sm, int thread_index, f64 now) -{ - snat_main_per_thread_data_t *tsm = &sm->per_thread_data[thread_index]; - int rc = 0; -#define _(p) \ - if ((rc = nat_lru_free_one_with_head (sm, thread_index, now, \ - tsm->p##_lru_head_index))) \ - { \ - return rc; \ - } - _(tcp_trans); - _(udp); - _(unk_proto); - _(icmp); - _(tcp_estab); -#undef _ - return 0; -} - -static_always_inline snat_session_t * -nat_ed_session_alloc (snat_main_t * sm, u32 thread_index, f64 now, u8 proto) -{ - snat_session_t *s; - snat_main_per_thread_data_t *tsm = &sm->per_thread_data[thread_index]; - - nat_lru_free_one (sm, thread_index, now); - - pool_get (tsm->sessions, s); - clib_memset (s, 0, sizeof (*s)); - - nat_ed_lru_insert (tsm, s, now, proto); - - s->ha_last_refreshed = now; - vlib_set_simple_counter (&sm->total_sessions, thread_index, 0, - pool_elts (tsm->sessions)); - return s; -} - -// slow path -static_always_inline void -per_vrf_sessions_cleanup (u32 thread_index) -{ - snat_main_t *sm = &snat_main; - snat_main_per_thread_data_t *tsm = - vec_elt_at_index (sm->per_thread_data, thread_index); - per_vrf_sessions_t *per_vrf_sessions; - u32 *to_free = 0, *i; - - vec_foreach (per_vrf_sessions, tsm->per_vrf_sessions_vec) - { - if (per_vrf_sessions->expired) - { - if (per_vrf_sessions->ses_count == 0) - { - vec_add1 (to_free, per_vrf_sessions - tsm->per_vrf_sessions_vec); - } - } - } - - if (vec_len (to_free)) - { - vec_foreach (i, to_free) - { - vec_del1 (tsm->per_vrf_sessions_vec, *i); - } - } - - vec_free (to_free); -} - -// slow path -static_always_inline void -per_vrf_sessions_register_session (snat_session_t * s, u32 thread_index) -{ - snat_main_t *sm = &snat_main; - snat_main_per_thread_data_t *tsm = - vec_elt_at_index (sm->per_thread_data, thread_index); - per_vrf_sessions_t *per_vrf_sessions; - - per_vrf_sessions_cleanup (thread_index); - - // s->per_vrf_sessions_index == ~0 ... reuse of old session - - vec_foreach (per_vrf_sessions, tsm->per_vrf_sessions_vec) - { - // ignore already expired registrations - if (per_vrf_sessions->expired) - continue; - - if ((s->in2out.fib_index == per_vrf_sessions->rx_fib_index) && - (s->out2in.fib_index == per_vrf_sessions->tx_fib_index)) - { - goto done; - } - if ((s->in2out.fib_index == per_vrf_sessions->tx_fib_index) && - (s->out2in.fib_index == per_vrf_sessions->rx_fib_index)) - { - goto done; - } - } - - // create a new registration - vec_add2 (tsm->per_vrf_sessions_vec, per_vrf_sessions, 1); - clib_memset (per_vrf_sessions, 0, sizeof (*per_vrf_sessions)); - - per_vrf_sessions->rx_fib_index = s->in2out.fib_index; - per_vrf_sessions->tx_fib_index = s->out2in.fib_index; - -done: - s->per_vrf_sessions_index = per_vrf_sessions - tsm->per_vrf_sessions_vec; - per_vrf_sessions->ses_count++; -} - -// fast path -static_always_inline void -per_vrf_sessions_unregister_session (snat_session_t * s, u32 thread_index) -{ - snat_main_t *sm = &snat_main; - snat_main_per_thread_data_t *tsm; - per_vrf_sessions_t *per_vrf_sessions; - - ASSERT (s->per_vrf_sessions_index != ~0); - - tsm = vec_elt_at_index (sm->per_thread_data, thread_index); - per_vrf_sessions = - vec_elt_at_index (tsm->per_vrf_sessions_vec, s->per_vrf_sessions_index); - - ASSERT (per_vrf_sessions->ses_count != 0); - - per_vrf_sessions->ses_count--; - s->per_vrf_sessions_index = ~0; -} - -// fast path -static_always_inline u8 -per_vrf_sessions_is_expired (snat_session_t * s, u32 thread_index) -{ - snat_main_t *sm = &snat_main; - snat_main_per_thread_data_t *tsm; - per_vrf_sessions_t *per_vrf_sessions; - - ASSERT (s->per_vrf_sessions_index != ~0); - - tsm = vec_elt_at_index (sm->per_thread_data, thread_index); - per_vrf_sessions = - vec_elt_at_index (tsm->per_vrf_sessions_vec, s->per_vrf_sessions_index); - return per_vrf_sessions->expired; -} - -static_always_inline void -nat_6t_flow_init (nat_6t_flow_t *f, u32 thread_idx, ip4_address_t saddr, - u16 sport, ip4_address_t daddr, u16 dport, u32 fib_index, - u8 proto, u32 session_idx) -{ - clib_memset (f, 0, sizeof (*f)); - f->match.saddr = saddr; - f->match.sport = sport; - f->match.daddr = daddr; - f->match.dport = dport; - f->match.proto = proto; - f->match.fib_index = fib_index; -} - -static_always_inline void -nat_6t_i2o_flow_init (snat_main_t *sm, u32 thread_idx, snat_session_t *s, - ip4_address_t saddr, u16 sport, ip4_address_t daddr, - u16 dport, u32 fib_index, u8 proto) -{ - snat_main_per_thread_data_t *tsm = - vec_elt_at_index (sm->per_thread_data, thread_idx); - nat_6t_flow_init (&s->i2o, thread_idx, saddr, sport, daddr, dport, fib_index, - proto, s - tsm->sessions); -} - -static_always_inline void -nat_6t_o2i_flow_init (snat_main_t *sm, u32 thread_idx, snat_session_t *s, - ip4_address_t saddr, u16 sport, ip4_address_t daddr, - u16 dport, u32 fib_index, u8 proto) -{ - snat_main_per_thread_data_t *tsm = - vec_elt_at_index (sm->per_thread_data, thread_idx); - nat_6t_flow_init (&s->o2i, thread_idx, saddr, sport, daddr, dport, fib_index, - proto, s - tsm->sessions); -} - -static_always_inline int -nat_6t_flow_match (nat_6t_flow_t *f, vlib_buffer_t *b, ip4_address_t saddr, - u16 sport, ip4_address_t daddr, u16 dport, u8 protocol, - u32 fib_index) -{ - return f->match.daddr.as_u32 == daddr.as_u32 && - f->match.dport == vnet_buffer (b)->ip.reass.l4_dst_port && - f->match.proto == protocol && f->match.fib_index == fib_index && - f->match.saddr.as_u32 == saddr.as_u32 && - f->match.sport == vnet_buffer (b)->ip.reass.l4_src_port; -} - -#endif diff --git a/src/plugins/nat/nat44/inlines.h b/src/plugins/nat/nat44/inlines.h deleted file mode 100644 index fde228e8b5e..00000000000 --- a/src/plugins/nat/nat44/inlines.h +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright (c) 2020 Cisco and/or its affiliates. - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/** - * @brief The NAT44 inline functions - */ - -#ifndef included_nat44_inlines_h__ -#define included_nat44_inlines_h__ - -#include -#include - -static_always_inline u8 -nat44_ed_maximum_sessions_exceeded (snat_main_t * sm, - u32 fib_index, u32 thread_index) -{ - u32 translations; - translations = pool_elts (sm->per_thread_data[thread_index].sessions); - if (vec_len (sm->max_translations_per_fib) <= fib_index) - fib_index = 0; - return translations >= sm->max_translations_per_fib[fib_index]; -} - -#endif /* included_nat44_inlines_h__ */ - -/* - * fd.io coding-style-patch-verification: ON - * - * Local Variables: - * eval: (c-set-style "gnu") - * End: - */ diff --git a/src/plugins/nat/nat44_api.c b/src/plugins/nat/nat44_api.c deleted file mode 100644 index a5e29198c64..00000000000 --- a/src/plugins/nat/nat44_api.c +++ /dev/null @@ -1,1750 +0,0 @@ -/* - * Copyright (c) 2020 Cisco and/or its affiliates. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/** - * @file - * @brief NAT44 plugin API implementation - */ - -#include -#include - -#include - -#include -#include - -#include - -#include -#include - -#include -#include -#include - -#include -#include - -#define REPLY_MSG_ID_BASE sm->msg_id_base -#include - -static void -vl_api_nat_control_ping_t_handler (vl_api_nat_control_ping_t * mp) -{ - vl_api_nat_control_ping_reply_t *rmp; - snat_main_t *sm = &snat_main; - int rv = 0; - - /* *INDENT-OFF* */ - REPLY_MACRO2 (VL_API_NAT_CONTROL_PING_REPLY, - ({ - rmp->vpe_pid = ntohl (getpid ()); - })); - /* *INDENT-ON* */ -} - -static void -vl_api_nat_show_config_t_handler (vl_api_nat_show_config_t * mp) -{ - vl_api_nat_show_config_reply_t *rmp; - snat_main_t *sm = &snat_main; - int rv = 0; - - /* *INDENT-OFF* */ - REPLY_MACRO2_ZERO (VL_API_NAT_SHOW_CONFIG_REPLY, - ({ - rmp->translation_buckets = htonl (sm->translation_buckets); - rmp->user_buckets = htonl (sm->user_buckets); - rmp->max_translations_per_user = htonl (sm->max_translations_per_user); - rmp->outside_vrf_id = htonl (sm->outside_vrf_id); - rmp->inside_vrf_id = htonl (sm->inside_vrf_id); - rmp->static_mapping_only = sm->static_mapping_only; - rmp->static_mapping_connection_tracking = - sm->static_mapping_connection_tracking; - rmp->endpoint_dependent = sm->endpoint_dependent; - rmp->out2in_dpo = sm->out2in_dpo; - })); - /* *INDENT-ON* */ -} - -static void -vl_api_nat_show_config_2_t_handler (vl_api_nat_show_config_2_t * mp) -{ - vl_api_nat_show_config_2_reply_t *rmp; - snat_main_t *sm = &snat_main; - int rv = 0; - - /* *INDENT-OFF* */ - REPLY_MACRO2_ZERO (VL_API_NAT_SHOW_CONFIG_2_REPLY, - ({ - rmp->translation_buckets = htonl (sm->translation_buckets); - rmp->user_buckets = htonl (sm->user_buckets); - rmp->max_translations_per_user = htonl (sm->max_translations_per_user); - rmp->outside_vrf_id = htonl (sm->outside_vrf_id); - rmp->inside_vrf_id = htonl (sm->inside_vrf_id); - rmp->static_mapping_only = sm->static_mapping_only; - rmp->static_mapping_connection_tracking = - sm->static_mapping_connection_tracking; - rmp->endpoint_dependent = sm->endpoint_dependent; - rmp->out2in_dpo = sm->out2in_dpo; - rmp->max_translations_per_thread = clib_net_to_host_u32(sm->max_translations_per_thread); - rmp->max_users_per_thread = clib_net_to_host_u32(sm->max_users_per_thread); - })); - /* *INDENT-ON* */ -} - -static void -vl_api_nat44_show_running_config_t_handler (vl_api_nat44_show_running_config_t - * mp) -{ - vl_api_nat44_show_running_config_reply_t *rmp; - snat_main_t *sm = &snat_main; - nat44_config_t *rc = &sm->rconfig; - int rv = 0; - - /* *INDENT-OFF* */ - REPLY_MACRO2_ZERO (VL_API_NAT44_SHOW_RUNNING_CONFIG_REPLY, - ({ - rmp->inside_vrf = htonl (rc->inside_vrf); - rmp->outside_vrf = htonl (rc->outside_vrf); - rmp->users = htonl (rc->users); - rmp->sessions = htonl (rc->sessions); - rmp->user_sessions = htonl (rc->user_sessions); - - rmp->user_buckets = htonl (sm->user_buckets); - rmp->translation_buckets = htonl (sm->translation_buckets); - - rmp->timeouts.udp = htonl (sm->timeouts.udp); - rmp->timeouts.tcp_established = htonl (sm->timeouts.tcp.established); - rmp->timeouts.tcp_transitory = htonl (sm->timeouts.tcp.transitory); - rmp->timeouts.icmp = htonl (sm->timeouts.icmp); - - rmp->forwarding_enabled = sm->forwarding_enabled == 1; - // consider how to split functionality between subplugins - rmp->ipfix_logging_enabled = nat_ipfix_logging_enabled (); - - if (rc->endpoint_dependent) - rmp->flags |= NAT44_IS_ENDPOINT_DEPENDENT; - else - rmp->flags |= NAT44_IS_ENDPOINT_INDEPENDENT; - - if (rc->static_mapping_only) - rmp->flags |= NAT44_IS_STATIC_MAPPING_ONLY; - if (rc->connection_tracking) - rmp->flags |= NAT44_IS_CONNECTION_TRACKING; - if (rc->out2in_dpo) - rmp->flags |= NAT44_IS_OUT2IN_DPO; - })); - /* *INDENT-ON* */ -} - -static void -vl_api_nat_set_workers_t_handler (vl_api_nat_set_workers_t * mp) -{ - snat_main_t *sm = &snat_main; - vl_api_nat_set_workers_reply_t *rmp; - int rv = 0; - uword *bitmap = 0; - u64 mask; - - mask = clib_net_to_host_u64 (mp->worker_mask); - - if (sm->num_workers < 2) - { - rv = VNET_API_ERROR_FEATURE_DISABLED; - goto send_reply; - } - - bitmap = clib_bitmap_set_multiple (bitmap, 0, mask, BITS (mask)); - rv = snat_set_workers (bitmap); - clib_bitmap_free (bitmap); - -send_reply: - REPLY_MACRO (VL_API_NAT_SET_WORKERS_REPLY); -} - -static void -send_nat_worker_details (u32 worker_index, vl_api_registration_t * reg, - u32 context) -{ - vl_api_nat_worker_details_t *rmp; - snat_main_t *sm = &snat_main; - vlib_worker_thread_t *w = - vlib_worker_threads + worker_index + sm->first_worker_index; - - rmp = vl_msg_api_alloc (sizeof (*rmp)); - clib_memset (rmp, 0, sizeof (*rmp)); - rmp->_vl_msg_id = ntohs (VL_API_NAT_WORKER_DETAILS + sm->msg_id_base); - rmp->context = context; - rmp->worker_index = htonl (worker_index); - rmp->lcore_id = htonl (w->cpu_id); - strncpy ((char *) rmp->name, (char *) w->name, ARRAY_LEN (rmp->name) - 1); - - vl_api_send_msg (reg, (u8 *) rmp); -} - -static void -vl_api_nat_worker_dump_t_handler (vl_api_nat_worker_dump_t * mp) -{ - vl_api_registration_t *reg; - snat_main_t *sm = &snat_main; - u32 *worker_index; - - reg = vl_api_client_index_to_registration (mp->client_index); - if (!reg) - return; - - /* *INDENT-OFF* */ - vec_foreach (worker_index, sm->workers) - send_nat_worker_details(*worker_index, reg, mp->context); - /* *INDENT-ON* */ -} - -static void -vl_api_nat44_session_cleanup_t_handler (vl_api_nat44_session_cleanup_t * mp) -{ - snat_main_t *sm = &snat_main; - vl_api_nat44_session_cleanup_reply_t *rmp; - int rv = 0; - REPLY_MACRO (VL_API_NAT44_SESSION_CLEANUP_REPLY); -} - -static void -vl_api_nat44_set_session_limit_t_handler (vl_api_nat44_set_session_limit_t * - mp) -{ - snat_main_t *sm = &snat_main; - vl_api_nat44_set_session_limit_reply_t *rmp; - int rv = 0; - - rv = nat44_set_session_limit - (ntohl (mp->session_limit), ntohl (mp->vrf_id)); - - REPLY_MACRO (VL_API_NAT44_SET_SESSION_LIMIT_REPLY); -} - -static void -vl_api_nat_set_log_level_t_handler (vl_api_nat_set_log_level_t * mp) -{ - snat_main_t *sm = &snat_main; - vl_api_nat_set_log_level_reply_t *rmp; - int rv = 0; - - if (sm->log_level > NAT_LOG_DEBUG) - rv = VNET_API_ERROR_UNSUPPORTED; - else - sm->log_level = mp->log_level; - - REPLY_MACRO (VL_API_NAT_SET_WORKERS_REPLY); -} - -static void - vl_api_nat44_plugin_enable_disable_t_handler - (vl_api_nat44_plugin_enable_disable_t * mp) -{ - snat_main_t *sm = &snat_main; - nat44_config_t c = { 0 }; - vl_api_nat44_plugin_enable_disable_reply_t *rmp; - int rv = 0; - - if (mp->enable) - { - c.endpoint_dependent = mp->flags & NAT44_API_IS_ENDPOINT_DEPENDENT; - c.static_mapping_only = mp->flags & NAT44_API_IS_STATIC_MAPPING_ONLY; - c.connection_tracking = mp->flags & NAT44_API_IS_CONNECTION_TRACKING; - c.out2in_dpo = mp->flags & NAT44_API_IS_OUT2IN_DPO; - - c.inside_vrf = ntohl (mp->inside_vrf); - c.outside_vrf = ntohl (mp->outside_vrf); - - c.users = ntohl (mp->users); - - c.sessions = ntohl (mp->sessions); - - c.user_sessions = ntohl (mp->user_sessions); - - rv = nat44_plugin_enable (c); - } - else - rv = nat44_plugin_disable (); - - REPLY_MACRO (VL_API_NAT44_PLUGIN_ENABLE_DISABLE_REPLY); -} - -static void -vl_api_nat_ipfix_enable_disable_t_handler (vl_api_nat_ipfix_enable_disable_t * - mp) -{ - snat_main_t *sm = &snat_main; - vl_api_nat_ipfix_enable_disable_reply_t *rmp; - int rv = 0; - - rv = nat_ipfix_logging_enable_disable (mp->enable, - clib_host_to_net_u32 - (mp->domain_id), - clib_host_to_net_u16 (mp->src_port)); - - REPLY_MACRO (VL_API_NAT_IPFIX_ENABLE_DISABLE_REPLY); -} - -static void -vl_api_nat_set_timeouts_t_handler (vl_api_nat_set_timeouts_t * mp) -{ - snat_main_t *sm = &snat_main; - vl_api_nat_set_timeouts_reply_t *rmp; - int rv = 0; - - sm->timeouts.udp = ntohl (mp->udp); - sm->timeouts.tcp.established = ntohl (mp->tcp_established); - sm->timeouts.tcp.transitory = ntohl (mp->tcp_transitory); - sm->timeouts.icmp = ntohl (mp->icmp); - - REPLY_MACRO (VL_API_NAT_SET_TIMEOUTS_REPLY); -} - -static void -vl_api_nat_get_timeouts_t_handler (vl_api_nat_get_timeouts_t * mp) -{ - snat_main_t *sm = &snat_main; - vl_api_nat_get_timeouts_reply_t *rmp; - int rv = 0; - - /* *INDENT-OFF* */ - REPLY_MACRO2 (VL_API_NAT_GET_TIMEOUTS_REPLY, - ({ - rmp->udp = htonl (sm->timeouts.udp); - rmp->tcp_established = htonl (sm->timeouts.tcp.established); - rmp->tcp_transitory = htonl (sm->timeouts.tcp.transitory); - rmp->icmp = htonl (sm->timeouts.icmp); - })) - /* *INDENT-ON* */ -} - -static void -vl_api_nat_set_fq_options_t_handler (vl_api_nat_set_fq_options_t *mp) -{ - snat_main_t *sm = &snat_main; - vl_api_nat_set_fq_options_reply_t *rmp; - int rv = 0; - u32 frame_queue_nelts = ntohl (mp->frame_queue_nelts); - rv = snat_set_frame_queue_nelts (frame_queue_nelts); - REPLY_MACRO (VL_API_NAT_SET_FQ_OPTIONS_REPLY); -} - -static void -vl_api_nat_show_fq_options_t_handler (vl_api_nat_show_fq_options_t *mp) -{ - vl_api_nat_show_fq_options_reply_t *rmp; - snat_main_t *sm = &snat_main; - int rv = 0; - /* clang-format off */ - REPLY_MACRO2_ZERO (VL_API_NAT_SHOW_FQ_OPTIONS_REPLY, - ({ - rmp->frame_queue_nelts = htonl (sm->frame_queue_nelts); - })); - /* clang-format on */ -} - -static void - vl_api_nat_set_addr_and_port_alloc_alg_t_handler - (vl_api_nat_set_addr_and_port_alloc_alg_t * mp) -{ - snat_main_t *sm = &snat_main; - vl_api_nat_set_addr_and_port_alloc_alg_reply_t *rmp; - int rv = 0; - u16 port_start, port_end; - - if (sm->endpoint_dependent) - { - rv = VNET_API_ERROR_UNSUPPORTED; - goto send_reply; - } - - switch (mp->alg) - { - case NAT_ADDR_AND_PORT_ALLOC_ALG_DEFAULT: - nat44_ei_set_alloc_default (); - break; - case NAT_ADDR_AND_PORT_ALLOC_ALG_MAPE: - nat44_ei_set_alloc_mape (ntohs (mp->psid), mp->psid_offset, - mp->psid_length); - break; - case NAT_ADDR_AND_PORT_ALLOC_ALG_RANGE: - port_start = ntohs (mp->start_port); - port_end = ntohs (mp->end_port); - if (port_end <= port_start) - { - rv = VNET_API_ERROR_INVALID_VALUE; - goto send_reply; - } - nat44_ei_set_alloc_range (port_start, port_end); - break; - default: - rv = VNET_API_ERROR_INVALID_VALUE; - break; - } - -send_reply: - REPLY_MACRO (VL_API_NAT_SET_ADDR_AND_PORT_ALLOC_ALG_REPLY); -} - -static void - vl_api_nat_get_addr_and_port_alloc_alg_t_handler - (vl_api_nat_get_addr_and_port_alloc_alg_t * mp) -{ - snat_main_t *sm = &snat_main; - vl_api_nat_get_addr_and_port_alloc_alg_reply_t *rmp; - int rv = 0; - - /* *INDENT-OFF* */ - REPLY_MACRO2 (VL_API_NAT_GET_ADDR_AND_PORT_ALLOC_ALG_REPLY, - ({ - rmp->alg = sm->addr_and_port_alloc_alg; - rmp->psid_offset = sm->psid_offset; - rmp->psid_length = sm->psid_length; - rmp->psid = htons (sm->psid); - rmp->start_port = htons (sm->start_port); - rmp->end_port = htons (sm->end_port); - })) - /* *INDENT-ON* */ -} - -static void -vl_api_nat_set_mss_clamping_t_handler (vl_api_nat_set_mss_clamping_t * mp) -{ - snat_main_t *sm = &snat_main; - vl_api_nat_set_mss_clamping_reply_t *rmp; - int rv = 0; - - if (mp->enable) - sm->mss_clamping = ntohs (mp->mss_value); - else - sm->mss_clamping = 0; - - REPLY_MACRO (VL_API_NAT_SET_MSS_CLAMPING_REPLY); -} - -static void -vl_api_nat_get_mss_clamping_t_handler (vl_api_nat_get_mss_clamping_t * mp) -{ - snat_main_t *sm = &snat_main; - vl_api_nat_get_mss_clamping_reply_t *rmp; - int rv = 0; - - /* *INDENT-OFF* */ - REPLY_MACRO2 (VL_API_NAT_GET_MSS_CLAMPING_REPLY, - ({ - rmp->enable = sm->mss_clamping ? 1 : 0; - rmp->mss_value = htons (sm->mss_clamping); - })) - /* *INDENT-ON* */ -} - -static void -vl_api_nat_ha_set_listener_t_handler (vl_api_nat_ha_set_listener_t * mp) -{ - snat_main_t *sm = &snat_main; - vl_api_nat_ha_set_listener_reply_t *rmp; - ip4_address_t addr; - int rv; - - memcpy (&addr, &mp->ip_address, sizeof (addr)); - rv = - nat_ha_set_listener (&addr, clib_net_to_host_u16 (mp->port), - clib_net_to_host_u32 (mp->path_mtu)); - - REPLY_MACRO (VL_API_NAT_HA_SET_LISTENER_REPLY); -} - -static void -vl_api_nat_ha_get_listener_t_handler (vl_api_nat_ha_get_listener_t * mp) -{ - snat_main_t *sm = &snat_main; - vl_api_nat_ha_get_listener_reply_t *rmp; - int rv = 0; - ip4_address_t addr; - u16 port; - u32 path_mtu; - - nat_ha_get_listener (&addr, &port, &path_mtu); - - /* *INDENT-OFF* */ - REPLY_MACRO2 (VL_API_NAT_HA_GET_LISTENER_REPLY, - ({ - clib_memcpy (rmp->ip_address, &addr, sizeof (ip4_address_t)); - rmp->port = clib_host_to_net_u16 (port); - rmp->path_mtu = clib_host_to_net_u32 (path_mtu); - })) - /* *INDENT-ON* */ -} - -static void -vl_api_nat_ha_set_failover_t_handler (vl_api_nat_ha_set_failover_t * mp) -{ - snat_main_t *sm = &snat_main; - vl_api_nat_ha_set_failover_reply_t *rmp; - ip4_address_t addr; - int rv; - - memcpy (&addr, &mp->ip_address, sizeof (addr)); - rv = - nat_ha_set_failover (&addr, clib_net_to_host_u16 (mp->port), - clib_net_to_host_u32 (mp->session_refresh_interval)); - - REPLY_MACRO (VL_API_NAT_HA_SET_FAILOVER_REPLY); -} - -static void -vl_api_nat_ha_get_failover_t_handler (vl_api_nat_ha_get_failover_t * mp) -{ - snat_main_t *sm = &snat_main; - vl_api_nat_ha_get_failover_reply_t *rmp; - int rv = 0; - ip4_address_t addr; - u16 port; - u32 session_refresh_interval; - - nat_ha_get_failover (&addr, &port, &session_refresh_interval); - - /* *INDENT-OFF* */ - REPLY_MACRO2 (VL_API_NAT_HA_GET_FAILOVER_REPLY, - ({ - clib_memcpy (rmp->ip_address, &addr, sizeof (ip4_address_t)); - rmp->port = clib_host_to_net_u16 (port); - rmp->session_refresh_interval = clib_host_to_net_u32 (session_refresh_interval); - })) - /* *INDENT-ON* */ -} - -static void -vl_api_nat_ha_flush_t_handler (vl_api_nat_ha_flush_t * mp) -{ - snat_main_t *sm = &snat_main; - vl_api_nat_ha_flush_reply_t *rmp; - int rv = 0; - - nat_ha_flush (0); - - REPLY_MACRO (VL_API_NAT_HA_FLUSH_REPLY); -} - -static void -nat_ha_resync_completed_event_cb (u32 client_index, u32 pid, u32 missed_count) -{ - snat_main_t *sm = &snat_main; - vl_api_registration_t *reg; - vl_api_nat_ha_resync_completed_event_t *mp; - - reg = vl_api_client_index_to_registration (client_index); - if (!reg) - return; - - mp = vl_msg_api_alloc (sizeof (*mp)); - clib_memset (mp, 0, sizeof (*mp)); - mp->client_index = client_index; - mp->pid = pid; - mp->missed_count = clib_host_to_net_u32 (missed_count); - mp->_vl_msg_id = - ntohs (VL_API_NAT_HA_RESYNC_COMPLETED_EVENT + sm->msg_id_base); - - vl_api_send_msg (reg, (u8 *) mp); -} - -static void -vl_api_nat_ha_resync_t_handler (vl_api_nat_ha_resync_t * mp) -{ - snat_main_t *sm = &snat_main; - vl_api_nat_ha_resync_reply_t *rmp; - int rv; - - rv = - nat_ha_resync (mp->client_index, mp->pid, - mp->want_resync_event ? nat_ha_resync_completed_event_cb : - NULL); - - REPLY_MACRO (VL_API_NAT_HA_RESYNC_REPLY); -} - -static void -vl_api_nat44_del_user_t_handler (vl_api_nat44_del_user_t * mp) -{ - snat_main_t *sm = &snat_main; - vl_api_nat44_del_user_reply_t *rmp; - ip4_address_t addr; - int rv; - memcpy (&addr.as_u8, mp->ip_address, 4); - rv = nat44_ei_user_del (&addr, ntohl (mp->fib_index)); - REPLY_MACRO (VL_API_NAT44_DEL_USER_REPLY); -} - -static void - vl_api_nat44_add_del_address_range_t_handler - (vl_api_nat44_add_del_address_range_t * mp) -{ - snat_main_t *sm = &snat_main; - vl_api_nat44_add_del_address_range_reply_t *rmp; - ip4_address_t this_addr; - u8 is_add, twice_nat; - u32 start_host_order, end_host_order; - u32 vrf_id; - int i, count; - int rv = 0; - u32 *tmp; - - if (sm->static_mapping_only) - { - rv = VNET_API_ERROR_FEATURE_DISABLED; - goto send_reply; - } - - is_add = mp->is_add; - twice_nat = mp->flags & NAT_API_IS_TWICE_NAT; - - tmp = (u32 *) mp->first_ip_address; - start_host_order = clib_host_to_net_u32 (tmp[0]); - tmp = (u32 *) mp->last_ip_address; - end_host_order = clib_host_to_net_u32 (tmp[0]); - - count = (end_host_order - start_host_order) + 1; - - vrf_id = clib_host_to_net_u32 (mp->vrf_id); - - if (count > 1024) - nat_log_info ("%U - %U, %d addresses...", - format_ip4_address, mp->first_ip_address, - format_ip4_address, mp->last_ip_address, count); - - memcpy (&this_addr.as_u8, mp->first_ip_address, 4); - - for (i = 0; i < count; i++) - { - if (is_add) - rv = snat_add_address (sm, &this_addr, vrf_id, twice_nat); - else - rv = snat_del_address (sm, this_addr, 0, twice_nat); - - if (rv) - goto send_reply; - - if (sm->out2in_dpo) - nat44_add_del_address_dpo (this_addr, is_add); - - increment_v4_address (&this_addr); - } - -send_reply: - REPLY_MACRO (VL_API_NAT44_ADD_DEL_ADDRESS_RANGE_REPLY); -} - -static void -send_nat44_address_details (snat_address_t * a, - vl_api_registration_t * reg, u32 context, - u8 twice_nat) -{ - vl_api_nat44_address_details_t *rmp; - snat_main_t *sm = &snat_main; - - rmp = vl_msg_api_alloc (sizeof (*rmp)); - clib_memset (rmp, 0, sizeof (*rmp)); - rmp->_vl_msg_id = ntohs (VL_API_NAT44_ADDRESS_DETAILS + sm->msg_id_base); - clib_memcpy (rmp->ip_address, &(a->addr), 4); - if (a->fib_index != ~0) - { - fib_table_t *fib = fib_table_get (a->fib_index, FIB_PROTOCOL_IP4); - rmp->vrf_id = ntohl (fib->ft_table_id); - } - else - rmp->vrf_id = ~0; - if (twice_nat) - rmp->flags |= NAT_API_IS_TWICE_NAT; - rmp->context = context; - - vl_api_send_msg (reg, (u8 *) rmp); -} - -static void -vl_api_nat44_address_dump_t_handler (vl_api_nat44_address_dump_t * mp) -{ - vl_api_registration_t *reg; - snat_main_t *sm = &snat_main; - snat_address_t *a; - - reg = vl_api_client_index_to_registration (mp->client_index); - if (!reg) - return; - - /* *INDENT-OFF* */ - vec_foreach (a, sm->addresses) - send_nat44_address_details (a, reg, mp->context, 0); - vec_foreach (a, sm->twice_nat_addresses) - send_nat44_address_details (a, reg, mp->context, 1); - /* *INDENT-ON* */ -} - -static void - vl_api_nat44_interface_add_del_feature_t_handler - (vl_api_nat44_interface_add_del_feature_t * mp) -{ - snat_main_t *sm = &snat_main; - vl_api_nat44_interface_add_del_feature_reply_t *rmp; - u32 sw_if_index = ntohl (mp->sw_if_index); - u8 is_del; - int rv = 0; - - is_del = !mp->is_add; - - VALIDATE_SW_IF_INDEX (mp); - - rv = - snat_interface_add_del (sw_if_index, mp->flags & NAT_API_IS_INSIDE, - is_del); - - BAD_SW_IF_INDEX_LABEL; - - REPLY_MACRO (VL_API_NAT44_INTERFACE_ADD_DEL_FEATURE_REPLY); -} - -static void -send_nat44_interface_details (snat_interface_t * i, - vl_api_registration_t * reg, u32 context) -{ - vl_api_nat44_interface_details_t *rmp; - snat_main_t *sm = &snat_main; - - rmp = vl_msg_api_alloc (sizeof (*rmp)); - clib_memset (rmp, 0, sizeof (*rmp)); - rmp->_vl_msg_id = ntohs (VL_API_NAT44_INTERFACE_DETAILS + sm->msg_id_base); - rmp->sw_if_index = ntohl (i->sw_if_index); - - if (nat_interface_is_inside (i)) - rmp->flags |= NAT_API_IS_INSIDE; - if (nat_interface_is_outside (i)) - rmp->flags |= NAT_API_IS_OUTSIDE; - - rmp->context = context; - - vl_api_send_msg (reg, (u8 *) rmp); -} - -static void -vl_api_nat44_interface_dump_t_handler (vl_api_nat44_interface_dump_t * mp) -{ - vl_api_registration_t *reg; - snat_main_t *sm = &snat_main; - snat_interface_t *i; - - reg = vl_api_client_index_to_registration (mp->client_index); - if (!reg) - return; - - /* *INDENT-OFF* */ - pool_foreach (i, sm->interfaces) - { - send_nat44_interface_details(i, reg, mp->context); - } - /* *INDENT-ON* */ -} - -static void - vl_api_nat44_interface_add_del_output_feature_t_handler - (vl_api_nat44_interface_add_del_output_feature_t * mp) -{ - snat_main_t *sm = &snat_main; - vl_api_nat44_interface_add_del_output_feature_reply_t *rmp; - u32 sw_if_index = ntohl (mp->sw_if_index); - int rv = 0; - - VALIDATE_SW_IF_INDEX (mp); - - rv = snat_interface_add_del_output_feature (sw_if_index, - mp->flags & NAT_API_IS_INSIDE, - !mp->is_add); - - BAD_SW_IF_INDEX_LABEL; - REPLY_MACRO (VL_API_NAT44_INTERFACE_ADD_DEL_OUTPUT_FEATURE_REPLY); -} - -static void -send_nat44_interface_output_feature_details (snat_interface_t * i, - vl_api_registration_t * reg, - u32 context) -{ - vl_api_nat44_interface_output_feature_details_t *rmp; - snat_main_t *sm = &snat_main; - - rmp = vl_msg_api_alloc (sizeof (*rmp)); - clib_memset (rmp, 0, sizeof (*rmp)); - rmp->_vl_msg_id = - ntohs (VL_API_NAT44_INTERFACE_OUTPUT_FEATURE_DETAILS + sm->msg_id_base); - rmp->sw_if_index = ntohl (i->sw_if_index); - rmp->context = context; - - if (nat_interface_is_inside (i)) - rmp->flags |= NAT_API_IS_INSIDE; - - vl_api_send_msg (reg, (u8 *) rmp); -} - -static void - vl_api_nat44_interface_output_feature_dump_t_handler - (vl_api_nat44_interface_output_feature_dump_t * mp) -{ - vl_api_registration_t *reg; - snat_main_t *sm = &snat_main; - snat_interface_t *i; - - reg = vl_api_client_index_to_registration (mp->client_index); - if (!reg) - return; - - /* *INDENT-OFF* */ - pool_foreach (i, sm->output_feature_interfaces) - { - send_nat44_interface_output_feature_details(i, reg, mp->context); - } - /* *INDENT-ON* */ -} - -static void - vl_api_nat44_add_del_static_mapping_t_handler - (vl_api_nat44_add_del_static_mapping_t * mp) -{ - snat_main_t *sm = &snat_main; - vl_api_nat44_add_del_static_mapping_reply_t *rmp; - ip4_address_t local_addr, external_addr, pool_addr = { 0 }; - u16 local_port = 0, external_port = 0; - u32 vrf_id, external_sw_if_index; - twice_nat_type_t twice_nat = TWICE_NAT_DISABLED; - int rv = 0; - nat_protocol_t proto; - u8 *tag = 0; - - memcpy (&local_addr.as_u8, mp->local_ip_address, 4); - memcpy (&external_addr.as_u8, mp->external_ip_address, 4); - - if (!(mp->flags & NAT_API_IS_ADDR_ONLY)) - { - local_port = mp->local_port; - external_port = mp->external_port; - } - - vrf_id = clib_net_to_host_u32 (mp->vrf_id); - external_sw_if_index = clib_net_to_host_u32 (mp->external_sw_if_index); - proto = ip_proto_to_nat_proto (mp->protocol); - - if (mp->flags & NAT_API_IS_TWICE_NAT) - twice_nat = TWICE_NAT; - else if (mp->flags & NAT_API_IS_SELF_TWICE_NAT) - twice_nat = TWICE_NAT_SELF; - mp->tag[sizeof (mp->tag) - 1] = 0; - tag = format (0, "%s", mp->tag); - vec_terminate_c_string (tag); - - rv = snat_add_static_mapping ( - local_addr, external_addr, local_port, external_port, vrf_id, - mp->flags & NAT_API_IS_ADDR_ONLY, external_sw_if_index, proto, mp->is_add, - twice_nat, mp->flags & NAT_API_IS_OUT2IN_ONLY, tag, 0, pool_addr, 0); - vec_free (tag); - - REPLY_MACRO (VL_API_NAT44_ADD_DEL_STATIC_MAPPING_REPLY); -} - -static void - vl_api_nat44_add_del_static_mapping_v2_t_handler - (vl_api_nat44_add_del_static_mapping_v2_t * mp) -{ - snat_main_t *sm = &snat_main; - vl_api_nat44_add_del_static_mapping_v2_reply_t *rmp; - ip4_address_t local_addr, external_addr, pool_addr; - u16 local_port = 0, external_port = 0; - u32 vrf_id, external_sw_if_index; - twice_nat_type_t twice_nat = TWICE_NAT_DISABLED; - int rv = 0; - nat_protocol_t proto; - u8 *tag = 0; - - memcpy (&pool_addr.as_u8, mp->pool_ip_address, 4); - memcpy (&local_addr.as_u8, mp->local_ip_address, 4); - memcpy (&external_addr.as_u8, mp->external_ip_address, 4); - - if (!(mp->flags & NAT_API_IS_ADDR_ONLY)) - { - local_port = mp->local_port; - external_port = mp->external_port; - } - - vrf_id = clib_net_to_host_u32 (mp->vrf_id); - external_sw_if_index = clib_net_to_host_u32 (mp->external_sw_if_index); - proto = ip_proto_to_nat_proto (mp->protocol); - - if (mp->flags & NAT_API_IS_TWICE_NAT) - twice_nat = TWICE_NAT; - else if (mp->flags & NAT_API_IS_SELF_TWICE_NAT) - twice_nat = TWICE_NAT_SELF; - mp->tag[sizeof (mp->tag) - 1] = 0; - tag = format (0, "%s", mp->tag); - vec_terminate_c_string (tag); - - rv = snat_add_static_mapping (local_addr, external_addr, local_port, - external_port, vrf_id, - mp->flags & NAT_API_IS_ADDR_ONLY, - external_sw_if_index, proto, - mp->is_add, twice_nat, - mp->flags & NAT_API_IS_OUT2IN_ONLY, tag, 0, - pool_addr, mp->match_pool); - vec_free (tag); - - REPLY_MACRO (VL_API_NAT44_ADD_DEL_STATIC_MAPPING_V2_REPLY); -} - -static void -send_nat44_static_mapping_details (snat_static_mapping_t * m, - vl_api_registration_t * reg, u32 context) -{ - vl_api_nat44_static_mapping_details_t *rmp; - snat_main_t *sm = &snat_main; - u32 len = sizeof (*rmp); - - rmp = vl_msg_api_alloc (len); - clib_memset (rmp, 0, len); - rmp->_vl_msg_id = - ntohs (VL_API_NAT44_STATIC_MAPPING_DETAILS + sm->msg_id_base); - - clib_memcpy (rmp->local_ip_address, &(m->local_addr), 4); - clib_memcpy (rmp->external_ip_address, &(m->external_addr), 4); - rmp->external_sw_if_index = ~0; - rmp->vrf_id = htonl (m->vrf_id); - rmp->context = context; - - if (m->twice_nat == TWICE_NAT) - rmp->flags |= NAT_API_IS_TWICE_NAT; - else if (m->twice_nat == TWICE_NAT_SELF) - rmp->flags |= NAT_API_IS_SELF_TWICE_NAT; - - if (is_out2in_only_static_mapping (m)) - rmp->flags |= NAT_API_IS_OUT2IN_ONLY; - - if (is_addr_only_static_mapping (m)) - { - rmp->flags |= NAT_API_IS_ADDR_ONLY; - } - else - { - rmp->protocol = nat_proto_to_ip_proto (m->proto); - rmp->external_port = m->external_port; - rmp->local_port = m->local_port; - } - - if (m->tag) - strncpy ((char *) rmp->tag, (char *) m->tag, vec_len (m->tag)); - - vl_api_send_msg (reg, (u8 *) rmp); -} - -static void -send_nat44_static_map_resolve_details (snat_static_map_resolve_t * m, - vl_api_registration_t * reg, - u32 context) -{ - vl_api_nat44_static_mapping_details_t *rmp; - snat_main_t *sm = &snat_main; - - rmp = vl_msg_api_alloc (sizeof (*rmp)); - clib_memset (rmp, 0, sizeof (*rmp)); - rmp->_vl_msg_id = - ntohs (VL_API_NAT44_STATIC_MAPPING_DETAILS + sm->msg_id_base); - clib_memcpy (rmp->local_ip_address, &(m->l_addr), 4); - rmp->external_sw_if_index = htonl (m->sw_if_index); - rmp->vrf_id = htonl (m->vrf_id); - rmp->context = context; - - if (m->twice_nat) - rmp->flags |= NAT_API_IS_TWICE_NAT; - - if (m->addr_only) - { - rmp->flags |= NAT_API_IS_ADDR_ONLY; - } - else - { - rmp->protocol = nat_proto_to_ip_proto (m->proto); - rmp->external_port = m->e_port; - rmp->local_port = m->l_port; - } - if (m->tag) - strncpy ((char *) rmp->tag, (char *) m->tag, vec_len (m->tag)); - - vl_api_send_msg (reg, (u8 *) rmp); -} - -static void -vl_api_nat44_static_mapping_dump_t_handler (vl_api_nat44_static_mapping_dump_t - * mp) -{ - vl_api_registration_t *reg; - snat_main_t *sm = &snat_main; - snat_static_mapping_t *m; - snat_static_map_resolve_t *rp; - int j; - - reg = vl_api_client_index_to_registration (mp->client_index); - if (!reg) - return; - - /* *INDENT-OFF* */ - pool_foreach (m, sm->static_mappings) - { - if (!is_identity_static_mapping(m) && !is_lb_static_mapping (m)) - send_nat44_static_mapping_details (m, reg, mp->context); - } - /* *INDENT-ON* */ - - for (j = 0; j < vec_len (sm->to_resolve); j++) - { - rp = sm->to_resolve + j; - if (!rp->identity_nat) - send_nat44_static_map_resolve_details (rp, reg, mp->context); - } -} - -static void - vl_api_nat44_add_del_identity_mapping_t_handler - (vl_api_nat44_add_del_identity_mapping_t * mp) -{ - snat_main_t *sm = &snat_main; - vl_api_nat44_add_del_identity_mapping_reply_t *rmp; - ip4_address_t addr, pool_addr = { 0 }; - u16 port = 0; - u32 vrf_id, sw_if_index; - int rv = 0; - nat_protocol_t proto = NAT_PROTOCOL_OTHER; - u8 *tag = 0; - - if (!(mp->flags & NAT_API_IS_ADDR_ONLY)) - { - port = mp->port; - proto = ip_proto_to_nat_proto (mp->protocol); - } - vrf_id = clib_net_to_host_u32 (mp->vrf_id); - sw_if_index = clib_net_to_host_u32 (mp->sw_if_index); - if (sw_if_index != ~0) - addr.as_u32 = 0; - else - memcpy (&addr.as_u8, mp->ip_address, 4); - mp->tag[sizeof (mp->tag) - 1] = 0; - tag = format (0, "%s", mp->tag); - vec_terminate_c_string (tag); - - rv = - snat_add_static_mapping (addr, addr, port, port, vrf_id, - mp->flags & NAT_API_IS_ADDR_ONLY, sw_if_index, - proto, mp->is_add, 0, 0, tag, 1, pool_addr, 0); - vec_free (tag); - - REPLY_MACRO (VL_API_NAT44_ADD_DEL_IDENTITY_MAPPING_REPLY); -} - -static void -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; - 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)); - rmp->_vl_msg_id = - ntohs (VL_API_NAT44_IDENTITY_MAPPING_DETAILS + sm->msg_id_base); - - if (is_addr_only_static_mapping (m)) - rmp->flags |= NAT_API_IS_ADDR_ONLY; - - clib_memcpy (rmp->ip_address, &(m->local_addr), 4); - rmp->port = m->local_port; - rmp->sw_if_index = ~0; - rmp->vrf_id = htonl (local->vrf_id); - rmp->protocol = nat_proto_to_ip_proto (m->proto); - rmp->context = context; - if (m->tag) - strncpy ((char *) rmp->tag, (char *) m->tag, vec_len (m->tag)); - - vl_api_send_msg (reg, (u8 *) rmp); -} - -static void -send_nat44_identity_map_resolve_details (snat_static_map_resolve_t * m, - vl_api_registration_t * reg, - u32 context) -{ - vl_api_nat44_identity_mapping_details_t *rmp; - snat_main_t *sm = &snat_main; - - rmp = vl_msg_api_alloc (sizeof (*rmp)); - clib_memset (rmp, 0, sizeof (*rmp)); - rmp->_vl_msg_id = - ntohs (VL_API_NAT44_IDENTITY_MAPPING_DETAILS + sm->msg_id_base); - - if (m->addr_only) - rmp->flags = (vl_api_nat_config_flags_t) NAT_API_IS_ADDR_ONLY; - - rmp->port = m->l_port; - rmp->sw_if_index = htonl (m->sw_if_index); - rmp->vrf_id = htonl (m->vrf_id); - rmp->protocol = nat_proto_to_ip_proto (m->proto); - rmp->context = context; - if (m->tag) - strncpy ((char *) rmp->tag, (char *) m->tag, vec_len (m->tag)); - - vl_api_send_msg (reg, (u8 *) rmp); -} - -static void - vl_api_nat44_identity_mapping_dump_t_handler - (vl_api_nat44_identity_mapping_dump_t * mp) -{ - vl_api_registration_t *reg; - snat_main_t *sm = &snat_main; - snat_static_mapping_t *m; - snat_static_map_resolve_t *rp; - int j; - - reg = vl_api_client_index_to_registration (mp->client_index); - if (!reg) - return; - - /* *INDENT-OFF* */ - pool_foreach (m, sm->static_mappings) - { - if (is_identity_static_mapping(m) && !is_lb_static_mapping (m)) - { - pool_foreach_index (j, m->locals) - { - send_nat44_identity_mapping_details (m, j, reg, mp->context); - } - } - } - /* *INDENT-ON* */ - - for (j = 0; j < vec_len (sm->to_resolve); j++) - { - rp = sm->to_resolve + j; - if (rp->identity_nat) - send_nat44_identity_map_resolve_details (rp, reg, mp->context); - } -} - -static void - vl_api_nat44_add_del_interface_addr_t_handler - (vl_api_nat44_add_del_interface_addr_t * mp) -{ - snat_main_t *sm = &snat_main; - vl_api_nat44_add_del_interface_addr_reply_t *rmp; - u32 sw_if_index = ntohl (mp->sw_if_index); - int rv = 0; - u8 is_del; - - if (sm->static_mapping_only) - { - rv = VNET_API_ERROR_FEATURE_DISABLED; - goto send_reply; - } - - is_del = !mp->is_add; - - VALIDATE_SW_IF_INDEX (mp); - - rv = snat_add_interface_address (sm, sw_if_index, is_del, - mp->flags & NAT_API_IS_TWICE_NAT); - - BAD_SW_IF_INDEX_LABEL; - -send_reply: - REPLY_MACRO (VL_API_NAT44_ADD_DEL_INTERFACE_ADDR_REPLY); -} - -static void -send_nat44_interface_addr_details (u32 sw_if_index, - vl_api_registration_t * reg, u32 context, - u8 twice_nat) -{ - vl_api_nat44_interface_addr_details_t *rmp; - snat_main_t *sm = &snat_main; - - rmp = vl_msg_api_alloc (sizeof (*rmp)); - clib_memset (rmp, 0, sizeof (*rmp)); - rmp->_vl_msg_id = - ntohs (VL_API_NAT44_INTERFACE_ADDR_DETAILS + sm->msg_id_base); - rmp->sw_if_index = ntohl (sw_if_index); - - if (twice_nat) - rmp->flags = (vl_api_nat_config_flags_t) NAT_API_IS_TWICE_NAT; - rmp->context = context; - - vl_api_send_msg (reg, (u8 *) rmp); -} - -static void -vl_api_nat44_interface_addr_dump_t_handler (vl_api_nat44_interface_addr_dump_t - * mp) -{ - vl_api_registration_t *reg; - snat_main_t *sm = &snat_main; - u32 *i; - - reg = vl_api_client_index_to_registration (mp->client_index); - if (!reg) - return; - - /* *INDENT-OFF* */ - vec_foreach (i, sm->auto_add_sw_if_indices) - send_nat44_interface_addr_details(*i, reg, mp->context, 0); - vec_foreach (i, sm->auto_add_sw_if_indices_twice_nat) - send_nat44_interface_addr_details(*i, reg, mp->context, 1); - /* *INDENT-ON* */ -} - -static void -send_nat44_user_details (snat_user_t * u, vl_api_registration_t * reg, - u32 context) -{ - vl_api_nat44_user_details_t *rmp; - snat_main_t *sm = &snat_main; - ip4_main_t *im = &ip4_main; - - rmp = vl_msg_api_alloc (sizeof (*rmp)); - clib_memset (rmp, 0, sizeof (*rmp)); - rmp->_vl_msg_id = ntohs (VL_API_NAT44_USER_DETAILS + sm->msg_id_base); - - if (!pool_is_free_index (im->fibs, u->fib_index)) - { - fib_table_t *fib = fib_table_get (u->fib_index, FIB_PROTOCOL_IP4); - rmp->vrf_id = ntohl (fib->ft_table_id); - } - - clib_memcpy (rmp->ip_address, &(u->addr), 4); - rmp->nsessions = ntohl (u->nsessions); - rmp->nstaticsessions = ntohl (u->nstaticsessions); - rmp->context = context; - - vl_api_send_msg (reg, (u8 *) rmp); -} - -static void -nat_ed_user_create_helper (snat_main_per_thread_data_t * tsm, - snat_session_t * s) -{ - snat_user_key_t k; - k.addr = s->in2out.addr; - k.fib_index = s->in2out.fib_index; - clib_bihash_kv_8_8_t key, value; - key.key = k.as_u64; - snat_user_t *u; - if (clib_bihash_search_8_8 (&tsm->user_hash, &key, &value)) - { - pool_get (tsm->users, u); - u->addr = k.addr; - u->fib_index = k.fib_index; - u->nsessions = 0; - u->nstaticsessions = 0; - key.value = u - tsm->users; - clib_bihash_add_del_8_8 (&tsm->user_hash, &key, 1); - } - else - { - u = pool_elt_at_index (tsm->users, value.value); - } - if (snat_is_session_static (s)) - { - ++u->nstaticsessions; - } - else - { - ++u->nsessions; - } -} - -static void -nat_ed_users_create (snat_main_per_thread_data_t * tsm) -{ - snat_session_t *s; - /* *INDENT-OFF* */ - pool_foreach (s, tsm->sessions) { nat_ed_user_create_helper (tsm, s); } - /* *INDENT-ON* */ -} - -static void -nat_ed_users_destroy (snat_main_per_thread_data_t * tsm) -{ - snat_user_t *u; - /* *INDENT-OFF* */ - pool_flush (u, tsm->users, { }); - /* *INDENT-ON* */ - clib_bihash_free_8_8 (&tsm->user_hash); - clib_bihash_init_8_8 (&tsm->user_hash, "users", snat_main.user_buckets, 0); - clib_bihash_set_kvp_format_fn_8_8 (&tsm->user_hash, format_user_kvp); -} - -static void -vl_api_nat44_user_dump_t_handler (vl_api_nat44_user_dump_t * mp) -{ - vl_api_registration_t *reg; - snat_main_t *sm = &snat_main; - snat_main_per_thread_data_t *tsm; - snat_user_t *u; - - reg = vl_api_client_index_to_registration (mp->client_index); - if (!reg) - return; - - /* *INDENT-OFF* */ - vec_foreach (tsm, sm->per_thread_data) - { - if (sm->endpoint_dependent) - { - nat_ed_users_create (tsm); - } - pool_foreach (u, tsm->users) - { - send_nat44_user_details (u, reg, mp->context); - } - if (sm->endpoint_dependent) - { - nat_ed_users_destroy (tsm); - } - } - /* *INDENT-ON* */ -} - -static void -send_nat44_user_session_details (snat_session_t * s, - vl_api_registration_t * reg, u32 context) -{ - vl_api_nat44_user_session_details_t *rmp; - snat_main_t *sm = &snat_main; - - rmp = vl_msg_api_alloc (sizeof (*rmp)); - clib_memset (rmp, 0, sizeof (*rmp)); - rmp->_vl_msg_id = - ntohs (VL_API_NAT44_USER_SESSION_DETAILS + sm->msg_id_base); - clib_memcpy (rmp->outside_ip_address, (&s->out2in.addr), 4); - clib_memcpy (rmp->inside_ip_address, (&s->in2out.addr), 4); - - if (snat_is_session_static (s)) - rmp->flags |= NAT_API_IS_STATIC; - - if (is_twice_nat_session (s)) - rmp->flags |= NAT_API_IS_TWICE_NAT; - - if (is_ed_session (s) || is_fwd_bypass_session (s)) - rmp->flags |= NAT_API_IS_EXT_HOST_VALID; - - rmp->last_heard = clib_host_to_net_u64 ((u64) s->last_heard); - rmp->total_bytes = clib_host_to_net_u64 (s->total_bytes); - rmp->total_pkts = ntohl (s->total_pkts); - rmp->context = context; - if (snat_is_unk_proto_session (s)) - { - rmp->outside_port = 0; - rmp->inside_port = 0; - rmp->protocol = ntohs (s->in2out.port); - } - else - { - rmp->outside_port = s->out2in.port; - rmp->inside_port = s->in2out.port; - rmp->protocol = ntohs (nat_proto_to_ip_proto (s->nat_proto)); - } - if (is_ed_session (s) || is_fwd_bypass_session (s)) - { - clib_memcpy (rmp->ext_host_address, &s->ext_host_addr, 4); - rmp->ext_host_port = s->ext_host_port; - if (is_twice_nat_session (s)) - { - clib_memcpy (rmp->ext_host_nat_address, &s->ext_host_nat_addr, 4); - rmp->ext_host_nat_port = s->ext_host_nat_port; - } - } - - vl_api_send_msg (reg, (u8 *) rmp); -} - -static void -vl_api_nat44_user_session_dump_t_handler (vl_api_nat44_user_session_dump_t * - mp) -{ - vl_api_registration_t *reg; - snat_main_t *sm = &snat_main; - snat_main_per_thread_data_t *tsm; - snat_session_t *s; - clib_bihash_kv_8_8_t key, value; - snat_user_key_t ukey; - snat_user_t *u; - u32 session_index, head_index, elt_index; - dlist_elt_t *head, *elt; - ip4_header_t ip; - - reg = vl_api_client_index_to_registration (mp->client_index); - if (!reg) - return; - - clib_memcpy (&ukey.addr, mp->ip_address, 4); - ip.src_address.as_u32 = ukey.addr.as_u32; - ukey.fib_index = fib_table_find (FIB_PROTOCOL_IP4, ntohl (mp->vrf_id)); - key.key = ukey.as_u64; - if (sm->num_workers > 1) - tsm = - vec_elt_at_index (sm->per_thread_data, - sm->worker_in2out_cb (&ip, ukey.fib_index, 0)); - else - tsm = vec_elt_at_index (sm->per_thread_data, sm->num_workers); - if (!sm->endpoint_dependent) - { - if (clib_bihash_search_8_8 (&tsm->user_hash, &key, &value)) - return; - u = pool_elt_at_index (tsm->users, value.value); - if (!u->nsessions && !u->nstaticsessions) - return; - - 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); - session_index = elt->value; - while (session_index != ~0) - { - s = pool_elt_at_index (tsm->sessions, session_index); - - send_nat44_user_session_details (s, reg, mp->context); - - elt_index = elt->next; - elt = pool_elt_at_index (tsm->list_pool, elt_index); - session_index = elt->value; - } - } - else - { - /* *INDENT-OFF* */ - pool_foreach (s, tsm->sessions) { - if (s->in2out.addr.as_u32 == ukey.addr.as_u32) - { - send_nat44_user_session_details (s, reg, mp->context); - } - } - /* *INDENT-ON* */ - } -} - -static nat44_lb_addr_port_t * -unformat_nat44_lb_addr_port (vl_api_nat44_lb_addr_port_t * addr_port_pairs, - u32 addr_port_pair_num) -{ - u8 i; - nat44_lb_addr_port_t *lb_addr_port_pairs = 0, lb_addr_port; - vl_api_nat44_lb_addr_port_t *ap; - - for (i = 0; i < addr_port_pair_num; i++) - { - ap = &addr_port_pairs[i]; - clib_memset (&lb_addr_port, 0, sizeof (lb_addr_port)); - clib_memcpy (&lb_addr_port.addr, ap->addr, 4); - lb_addr_port.port = ap->port; - lb_addr_port.probability = ap->probability; - lb_addr_port.vrf_id = clib_net_to_host_u32 (ap->vrf_id); - vec_add1 (lb_addr_port_pairs, lb_addr_port); - } - - return lb_addr_port_pairs; -} - -static void - vl_api_nat44_add_del_lb_static_mapping_t_handler - (vl_api_nat44_add_del_lb_static_mapping_t * mp) -{ - snat_main_t *sm = &snat_main; - vl_api_nat44_add_del_lb_static_mapping_reply_t *rmp; - twice_nat_type_t twice_nat = TWICE_NAT_DISABLED; - int rv = 0; - nat44_lb_addr_port_t *locals = 0; - ip4_address_t e_addr; - nat_protocol_t proto; - u8 *tag = 0; - - if (!sm->endpoint_dependent) - { - rv = VNET_API_ERROR_UNSUPPORTED; - goto send_reply; - } - - 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_nat_proto (mp->protocol); - - if (mp->flags & NAT_API_IS_TWICE_NAT) - twice_nat = TWICE_NAT; - else if (mp->flags & NAT_API_IS_SELF_TWICE_NAT) - twice_nat = TWICE_NAT_SELF; - mp->tag[sizeof (mp->tag) - 1] = 0; - tag = format (0, "%s", mp->tag); - vec_terminate_c_string (tag); - - rv = - nat44_add_del_lb_static_mapping (e_addr, - mp->external_port, - proto, locals, mp->is_add, - twice_nat, - mp->flags & NAT_API_IS_OUT2IN_ONLY, tag, - clib_net_to_host_u32 (mp->affinity)); - - vec_free (locals); - vec_free (tag); - -send_reply: - REPLY_MACRO (VL_API_NAT44_ADD_DEL_LB_STATIC_MAPPING_REPLY); -} - -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; - nat_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_nat_proto (mp->protocol); - - rv = - nat44_lb_static_mapping_add_del_local (e_addr, - mp->external_port, - l_addr, - 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 -send_nat44_lb_static_mapping_details (snat_static_mapping_t * m, - vl_api_registration_t * reg, - u32 context) -{ - vl_api_nat44_lb_static_mapping_details_t *rmp; - 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) + - (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); - - clib_memcpy (rmp->external_addr, &(m->external_addr), 4); - rmp->external_port = m->external_port; - rmp->protocol = nat_proto_to_ip_proto (m->proto); - rmp->context = context; - - if (m->twice_nat == TWICE_NAT) - rmp->flags |= NAT_API_IS_TWICE_NAT; - else if (m->twice_nat == TWICE_NAT_SELF) - rmp->flags |= NAT_API_IS_SELF_TWICE_NAT; - if (is_out2in_only_static_mapping (m)) - rmp->flags |= NAT_API_IS_OUT2IN_ONLY; - if (m->tag) - strncpy ((char *) rmp->tag, (char *) m->tag, vec_len (m->tag)); - - locals = (vl_api_nat44_lb_addr_port_t *) rmp->locals; - /* *INDENT-OFF* */ - pool_foreach (ap, m->locals) - { - clib_memcpy (locals->addr, &(ap->addr), 4); - locals->port = ap->port; - locals->probability = ap->probability; - locals->vrf_id = ntohl (ap->vrf_id); - locals++; - local_num++; - } - /* *INDENT-ON* */ - rmp->local_num = ntohl (local_num); - - vl_api_send_msg (reg, (u8 *) rmp); -} - -static void - vl_api_nat44_lb_static_mapping_dump_t_handler - (vl_api_nat44_lb_static_mapping_dump_t * mp) -{ - vl_api_registration_t *reg; - snat_main_t *sm = &snat_main; - snat_static_mapping_t *m; - - if (!sm->endpoint_dependent) - return; - - reg = vl_api_client_index_to_registration (mp->client_index); - if (!reg) - return; - - /* *INDENT-OFF* */ - pool_foreach (m, sm->static_mappings) - { - if (is_lb_static_mapping(m)) - send_nat44_lb_static_mapping_details (m, reg, mp->context); - } - /* *INDENT-ON* */ -} - -static void -vl_api_nat44_del_session_t_handler (vl_api_nat44_del_session_t * mp) -{ - snat_main_t *sm = &snat_main; - vl_api_nat44_del_session_reply_t *rmp; - ip4_address_t addr, eh_addr; - u16 port, eh_port; - u32 vrf_id; - int rv = 0; - u8 is_in; - nat_protocol_t proto; - - memcpy (&addr.as_u8, mp->address, 4); - port = mp->port; - vrf_id = clib_net_to_host_u32 (mp->vrf_id); - proto = ip_proto_to_nat_proto (mp->protocol); - memcpy (&eh_addr.as_u8, mp->ext_host_address, 4); - eh_port = mp->ext_host_port; - - is_in = mp->flags & NAT_API_IS_INSIDE; - - if (mp->flags & NAT_API_IS_EXT_HOST_VALID) - rv = - nat44_del_ed_session (sm, &addr, port, &eh_addr, eh_port, mp->protocol, - vrf_id, is_in); - else - rv = nat44_ei_del_session (sm, &addr, port, proto, vrf_id, is_in); - - REPLY_MACRO (VL_API_NAT44_DEL_SESSION_REPLY); -} - -static void - vl_api_nat44_forwarding_enable_disable_t_handler - (vl_api_nat44_forwarding_enable_disable_t * mp) -{ - snat_main_t *sm = &snat_main; - vl_api_nat44_forwarding_enable_disable_reply_t *rmp; - int rv = 0; - u32 *ses_to_be_removed = 0, *ses_index; - snat_main_per_thread_data_t *tsm; - snat_session_t *s; - - sm->forwarding_enabled = mp->enable != 0; - - if (mp->enable == 0) - { - /* *INDENT-OFF* */ - vec_foreach (tsm, sm->per_thread_data) - { - pool_foreach (s, tsm->sessions) - { - if (is_fwd_bypass_session(s)) - { - vec_add1 (ses_to_be_removed, s - tsm->sessions); - } - } - if(sm->endpoint_dependent){ - vec_foreach (ses_index, ses_to_be_removed) - { - s = pool_elt_at_index(tsm->sessions, ses_index[0]); - nat_free_session_data (sm, s, tsm - sm->per_thread_data, 0); - nat_ed_session_delete (sm, s, tsm - sm->per_thread_data, 1); - } - }else{ - vec_foreach (ses_index, ses_to_be_removed) - { - s = pool_elt_at_index(tsm->sessions, ses_index[0]); - nat_free_session_data (sm, s, tsm - sm->per_thread_data, 0); - nat44_delete_session (sm, s, tsm - sm->per_thread_data); - } - } - vec_free (ses_to_be_removed); - } - /* *INDENT-ON* */ - } - - REPLY_MACRO (VL_API_NAT44_FORWARDING_ENABLE_DISABLE_REPLY); -} - -static void - vl_api_nat44_forwarding_is_enabled_t_handler - (vl_api_nat44_forwarding_is_enabled_t * mp) -{ - vl_api_registration_t *reg; - snat_main_t *sm = &snat_main; - vl_api_nat44_forwarding_is_enabled_reply_t *rmp; - - reg = vl_api_client_index_to_registration (mp->client_index); - if (!reg) - return; - - rmp = vl_msg_api_alloc (sizeof (*rmp)); - clib_memset (rmp, 0, sizeof (*rmp)); - rmp->_vl_msg_id = - ntohs (VL_API_NAT44_FORWARDING_IS_ENABLED_REPLY + sm->msg_id_base); - rmp->context = mp->context; - - rmp->enabled = sm->forwarding_enabled; - - vl_api_send_msg (reg, (u8 *) rmp); -} - -/* API definitions */ -#include -#include - -/* Set up the API message handling tables */ -clib_error_t * -nat44_api_hookup (vlib_main_t * vm) -{ - snat_main_t *sm = &snat_main; - sm->msg_id_base = setup_message_id_table (); - return 0; -} - -/* - * fd.io coding-style-patch-verification: ON - * - * Local Variables: - * eval: (c-set-style "gnu") - * End: - */ diff --git a/src/plugins/nat/nat44_classify.c b/src/plugins/nat/nat44_classify.c deleted file mode 100644 index 85f8c64afd5..00000000000 --- a/src/plugins/nat/nat44_classify.c +++ /dev/null @@ -1,506 +0,0 @@ -/* - * Copyright (c) 2018 Cisco and/or its affiliates. - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/** - * @file - * @brief Classify for one armed NAT44 (in+out interface) - */ - -#include -#include -#include -#include -#include -#include - -#define foreach_nat44_classify_error \ -_(NEXT_IN2OUT, "next in2out") \ -_(NEXT_OUT2IN, "next out2in") \ -_(FRAG_CACHED, "fragment cached") - -typedef enum -{ -#define _(sym,str) NAT44_CLASSIFY_ERROR_##sym, - foreach_nat44_classify_error -#undef _ - NAT44_CLASSIFY_N_ERROR, -} nat44_classify_error_t; - -static char *nat44_classify_error_strings[] = { -#define _(sym,string) string, - foreach_nat44_classify_error -#undef _ -}; - -typedef enum -{ - NAT44_CLASSIFY_NEXT_IN2OUT, - NAT44_CLASSIFY_NEXT_OUT2IN, - NAT44_CLASSIFY_NEXT_DROP, - NAT44_CLASSIFY_N_NEXT, -} nat44_classify_next_t; - -typedef struct -{ - u8 next_in2out; - u8 cached; -} nat44_classify_trace_t; - -static u8 * -format_nat44_classify_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 *); - nat44_classify_trace_t *t = va_arg (*args, nat44_classify_trace_t *); - char *next; - - if (t->cached) - s = format (s, "nat44-classify: fragment cached"); - else - { - next = t->next_in2out ? "nat44-in2out" : "nat44-out2in"; - s = format (s, "nat44-classify: next %s", next); - } - - return s; -} - -static inline uword -nat44_classify_node_fn_inline (vlib_main_t * vm, - vlib_node_runtime_t * node, - vlib_frame_t * frame) -{ - u32 n_left_from, *from, *to_next; - nat44_classify_next_t next_index; - snat_main_t *sm = &snat_main; - snat_static_mapping_t *m; - u32 next_in2out = 0, next_out2in = 0; - - from = vlib_frame_vector_args (frame); - n_left_from = frame->n_vectors; - next_index = node->cached_next_index; - - while (n_left_from > 0) - { - u32 n_left_to_next; - - vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next); - - while (n_left_from > 0 && n_left_to_next > 0) - { - u32 bi0; - vlib_buffer_t *b0; - u32 next0 = NAT44_CLASSIFY_NEXT_IN2OUT; - ip4_header_t *ip0; - snat_address_t *ap; - clib_bihash_kv_8_8_t kv0, value0; - - /* speculatively enqueue b0 to the current next frame */ - bi0 = from[0]; - to_next[0] = bi0; - from += 1; - to_next += 1; - n_left_from -= 1; - n_left_to_next -= 1; - - b0 = vlib_get_buffer (vm, bi0); - ip0 = vlib_buffer_get_current (b0); - - /* *INDENT-OFF* */ - vec_foreach (ap, sm->addresses) - { - if (ip0->dst_address.as_u32 == ap->addr.as_u32) - { - next0 = NAT44_CLASSIFY_NEXT_OUT2IN; - goto enqueue0; - } - } - /* *INDENT-ON* */ - - if (PREDICT_FALSE (pool_elts (sm->static_mappings))) - { - init_nat_k (&kv0, ip0->dst_address, 0, 0, 0); - /* try to classify the fragment based on IP header alone */ - if (!clib_bihash_search_8_8 (&sm->static_mapping_by_external, - &kv0, &value0)) - { - m = pool_elt_at_index (sm->static_mappings, value0.value); - if (m->local_addr.as_u32 != m->external_addr.as_u32) - next0 = NAT44_CLASSIFY_NEXT_OUT2IN; - goto enqueue0; - } - init_nat_k (&kv0, ip0->dst_address, - vnet_buffer (b0)->ip.reass.l4_dst_port, 0, - ip_proto_to_nat_proto (ip0->protocol)); - if (!clib_bihash_search_8_8 - (&sm->static_mapping_by_external, &kv0, &value0)) - { - m = pool_elt_at_index (sm->static_mappings, value0.value); - if (m->local_addr.as_u32 != m->external_addr.as_u32) - next0 = NAT44_CLASSIFY_NEXT_OUT2IN; - } - } - - enqueue0: - if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE) - && (b0->flags & VLIB_BUFFER_IS_TRACED))) - { - nat44_classify_trace_t *t = - vlib_add_trace (vm, node, b0, sizeof (*t)); - t->cached = 0; - t->next_in2out = next0 == NAT44_CLASSIFY_NEXT_IN2OUT ? 1 : 0; - } - - next_in2out += next0 == NAT44_CLASSIFY_NEXT_IN2OUT; - next_out2in += next0 == NAT44_CLASSIFY_NEXT_OUT2IN; - - /* verify speculative enqueue, maybe switch current next frame */ - vlib_validate_buffer_enqueue_x1 (vm, node, next_index, - to_next, n_left_to_next, - bi0, next0); - } - - vlib_put_next_frame (vm, node, next_index, n_left_to_next); - } - - vlib_node_increment_counter (vm, node->node_index, - NAT44_CLASSIFY_ERROR_NEXT_IN2OUT, next_in2out); - vlib_node_increment_counter (vm, node->node_index, - NAT44_CLASSIFY_ERROR_NEXT_OUT2IN, next_out2in); - return frame->n_vectors; -} - -static inline uword -nat44_handoff_classify_node_fn_inline (vlib_main_t * vm, - vlib_node_runtime_t * node, - vlib_frame_t * frame) -{ - u32 n_left_from, *from, *to_next; - nat44_classify_next_t next_index; - snat_main_t *sm = &snat_main; - snat_static_mapping_t *m; - u32 next_in2out = 0, next_out2in = 0; - - from = vlib_frame_vector_args (frame); - n_left_from = frame->n_vectors; - next_index = node->cached_next_index; - - while (n_left_from > 0) - { - u32 n_left_to_next; - - vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next); - - while (n_left_from > 0 && n_left_to_next > 0) - { - u32 bi0; - vlib_buffer_t *b0; - u32 next0 = NAT_NEXT_IN2OUT_CLASSIFY; - ip4_header_t *ip0; - snat_address_t *ap; - clib_bihash_kv_8_8_t kv0, value0; - - /* speculatively enqueue b0 to the current next frame */ - bi0 = from[0]; - to_next[0] = bi0; - from += 1; - to_next += 1; - n_left_from -= 1; - n_left_to_next -= 1; - - b0 = vlib_get_buffer (vm, bi0); - ip0 = vlib_buffer_get_current (b0); - - /* *INDENT-OFF* */ - vec_foreach (ap, sm->addresses) - { - if (ip0->dst_address.as_u32 == ap->addr.as_u32) - { - next0 = NAT_NEXT_OUT2IN_CLASSIFY; - goto enqueue0; - } - } - /* *INDENT-ON* */ - - if (PREDICT_FALSE (pool_elts (sm->static_mappings))) - { - init_nat_k (&kv0, ip0->dst_address, 0, 0, 0); - /* try to classify the fragment based on IP header alone */ - if (!clib_bihash_search_8_8 (&sm->static_mapping_by_external, - &kv0, &value0)) - { - m = pool_elt_at_index (sm->static_mappings, value0.value); - if (m->local_addr.as_u32 != m->external_addr.as_u32) - next0 = NAT_NEXT_OUT2IN_CLASSIFY; - goto enqueue0; - } - init_nat_k (&kv0, ip0->dst_address, - vnet_buffer (b0)->ip.reass.l4_dst_port, 0, - ip_proto_to_nat_proto (ip0->protocol)); - if (!clib_bihash_search_8_8 - (&sm->static_mapping_by_external, &kv0, &value0)) - { - m = pool_elt_at_index (sm->static_mappings, value0.value); - if (m->local_addr.as_u32 != m->external_addr.as_u32) - next0 = NAT_NEXT_OUT2IN_CLASSIFY; - } - } - - enqueue0: - if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE) - && (b0->flags & VLIB_BUFFER_IS_TRACED))) - { - nat44_classify_trace_t *t = - vlib_add_trace (vm, node, b0, sizeof (*t)); - t->cached = 0; - t->next_in2out = next0 == NAT_NEXT_IN2OUT_CLASSIFY ? 1 : 0; - } - - next_in2out += next0 == NAT_NEXT_IN2OUT_CLASSIFY; - next_out2in += next0 == NAT_NEXT_OUT2IN_CLASSIFY; - - /* verify speculative enqueue, maybe switch current next frame */ - vlib_validate_buffer_enqueue_x1 (vm, node, next_index, - to_next, n_left_to_next, - bi0, next0); - } - - vlib_put_next_frame (vm, node, next_index, n_left_to_next); - } - - vlib_node_increment_counter (vm, node->node_index, - NAT44_CLASSIFY_ERROR_NEXT_IN2OUT, next_in2out); - vlib_node_increment_counter (vm, node->node_index, - NAT44_CLASSIFY_ERROR_NEXT_OUT2IN, next_out2in); - return frame->n_vectors; -} - -static inline uword -nat44_ed_classify_node_fn_inline (vlib_main_t * vm, - vlib_node_runtime_t * node, - vlib_frame_t * frame) -{ - u32 n_left_from, *from, *to_next; - nat44_classify_next_t next_index; - snat_main_t *sm = &snat_main; - snat_static_mapping_t *m; - u32 next_in2out = 0, next_out2in = 0; - - from = vlib_frame_vector_args (frame); - n_left_from = frame->n_vectors; - next_index = node->cached_next_index; - - while (n_left_from > 0) - { - u32 n_left_to_next; - - vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next); - - while (n_left_from > 0 && n_left_to_next > 0) - { - u32 bi0; - vlib_buffer_t *b0; - u32 next0 = NAT_NEXT_IN2OUT_ED_FAST_PATH; - u32 sw_if_index0, rx_fib_index0; - ip4_header_t *ip0; - snat_address_t *ap; - clib_bihash_kv_8_8_t kv0, value0; - clib_bihash_kv_16_8_t ed_kv0, ed_value0; - - /* speculatively enqueue b0 to the current next frame */ - bi0 = from[0]; - to_next[0] = bi0; - from += 1; - to_next += 1; - n_left_from -= 1; - n_left_to_next -= 1; - - b0 = vlib_get_buffer (vm, bi0); - ip0 = vlib_buffer_get_current (b0); - - u32 arc_next; - vnet_feature_next (&arc_next, b0); - vnet_buffer2 (b0)->nat.arc_next = arc_next; - - if (ip0->protocol != IP_PROTOCOL_ICMP) - { - /* process leading fragment/whole packet (with L4 header) */ - sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX]; - rx_fib_index0 = - fib_table_get_index_for_sw_if_index (FIB_PROTOCOL_IP4, - sw_if_index0); - init_ed_k (&ed_kv0, ip0->src_address, - vnet_buffer (b0)->ip.reass.l4_src_port, - ip0->dst_address, - vnet_buffer (b0)->ip.reass.l4_dst_port, - rx_fib_index0, ip0->protocol); - /* process whole packet */ - if (!clib_bihash_search_16_8 (&sm->flow_hash, &ed_kv0, - &ed_value0)) - { - ASSERT (vm->thread_index == - ed_value_get_thread_index (&ed_value0)); - snat_main_per_thread_data_t *tsm = - &sm->per_thread_data[vm->thread_index]; - snat_session_t *s = pool_elt_at_index ( - tsm->sessions, ed_value_get_session_index (&ed_value0)); - clib_bihash_kv_16_8_t i2o_kv; - nat_6t_flow_to_ed_k (&i2o_kv, &s->i2o); - vnet_buffer2 (b0)->nat.cached_session_index = - ed_value_get_session_index (&ed_value0); - if (i2o_kv.key[0] == ed_kv0.key[0] && - i2o_kv.key[1] == ed_kv0.key[1]) - { - next0 = NAT_NEXT_IN2OUT_ED_FAST_PATH; - } - else - { - next0 = NAT_NEXT_OUT2IN_ED_FAST_PATH; - } - - goto enqueue0; - } - /* session doesn't exist so continue in code */ - } - - /* *INDENT-OFF* */ - vec_foreach (ap, sm->addresses) - { - if (ip0->dst_address.as_u32 == ap->addr.as_u32) - { - next0 = NAT_NEXT_OUT2IN_ED_FAST_PATH; - goto enqueue0; - } - } - /* *INDENT-ON* */ - - if (PREDICT_FALSE (pool_elts (sm->static_mappings))) - { - init_nat_k (&kv0, ip0->dst_address, 0, 0, 0); - /* try to classify the fragment based on IP header alone */ - if (!clib_bihash_search_8_8 (&sm->static_mapping_by_external, - &kv0, &value0)) - { - m = pool_elt_at_index (sm->static_mappings, value0.value); - if (m->local_addr.as_u32 != m->external_addr.as_u32) - next0 = NAT_NEXT_OUT2IN_ED_FAST_PATH; - goto enqueue0; - } - init_nat_k (&kv0, ip0->dst_address, - vnet_buffer (b0)->ip.reass.l4_dst_port, 0, - ip_proto_to_nat_proto (ip0->protocol)); - if (!clib_bihash_search_8_8 - (&sm->static_mapping_by_external, &kv0, &value0)) - { - m = pool_elt_at_index (sm->static_mappings, value0.value); - if (m->local_addr.as_u32 != m->external_addr.as_u32) - next0 = NAT_NEXT_OUT2IN_ED_FAST_PATH; - } - } - - enqueue0: - if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE) - && (b0->flags & VLIB_BUFFER_IS_TRACED))) - { - nat44_classify_trace_t *t = - vlib_add_trace (vm, node, b0, sizeof (*t)); - t->cached = 0; - t->next_in2out = next0 == NAT_NEXT_IN2OUT_ED_FAST_PATH ? 1 : 0; - } - - next_in2out += next0 == NAT_NEXT_IN2OUT_ED_FAST_PATH; - next_out2in += next0 == NAT_NEXT_OUT2IN_ED_FAST_PATH; - - /* verify speculative enqueue, maybe switch current next frame */ - vlib_validate_buffer_enqueue_x1 (vm, node, next_index, - to_next, n_left_to_next, - bi0, next0); - } - - vlib_put_next_frame (vm, node, next_index, n_left_to_next); - } - - vlib_node_increment_counter (vm, node->node_index, - NAT44_CLASSIFY_ERROR_NEXT_IN2OUT, next_in2out); - vlib_node_increment_counter (vm, node->node_index, - NAT44_CLASSIFY_ERROR_NEXT_OUT2IN, next_out2in); - return frame->n_vectors; -} - -VLIB_NODE_FN (nat44_classify_node) (vlib_main_t * vm, - vlib_node_runtime_t * node, - vlib_frame_t * frame) -{ - return nat44_classify_node_fn_inline (vm, node, frame); -} - -/* *INDENT-OFF* */ -VLIB_REGISTER_NODE (nat44_classify_node) = { - .name = "nat44-classify", - .vector_size = sizeof (u32), - .format_trace = format_nat44_classify_trace, - .type = VLIB_NODE_TYPE_INTERNAL, - .n_errors = ARRAY_LEN(nat44_classify_error_strings), - .error_strings = nat44_classify_error_strings, - .n_next_nodes = NAT44_CLASSIFY_N_NEXT, - .next_nodes = { - [NAT44_CLASSIFY_NEXT_IN2OUT] = "nat44-in2out", - [NAT44_CLASSIFY_NEXT_OUT2IN] = "nat44-out2in", - [NAT44_CLASSIFY_NEXT_DROP] = "error-drop", - }, -}; -/* *INDENT-ON* */ - -VLIB_NODE_FN (nat44_ed_classify_node) (vlib_main_t * vm, - vlib_node_runtime_t * node, - vlib_frame_t * frame) -{ - return nat44_ed_classify_node_fn_inline (vm, node, frame); -} - -/* *INDENT-OFF* */ -VLIB_REGISTER_NODE (nat44_ed_classify_node) = { - .name = "nat44-ed-classify", - .vector_size = sizeof (u32), - .sibling_of = "nat-default", - .format_trace = format_nat44_classify_trace, - .type = VLIB_NODE_TYPE_INTERNAL, -}; -/* *INDENT-ON* */ - -VLIB_NODE_FN (nat44_handoff_classify_node) (vlib_main_t * vm, - vlib_node_runtime_t * node, - vlib_frame_t * frame) -{ - return nat44_handoff_classify_node_fn_inline (vm, node, frame); -} - -/* *INDENT-OFF* */ -VLIB_REGISTER_NODE (nat44_handoff_classify_node) = { - .name = "nat44-handoff-classify", - .vector_size = sizeof (u32), - .sibling_of = "nat-default", - .format_trace = format_nat44_classify_trace, - .type = VLIB_NODE_TYPE_INTERNAL, -}; - -/* *INDENT-ON* */ - -/* - * fd.io coding-style-patch-verification: ON - * - * Local Variables: - * eval: (c-set-style "gnu") - * End: - */ diff --git a/src/plugins/nat/nat44_cli.c b/src/plugins/nat/nat44_cli.c deleted file mode 100644 index 68c53d05389..00000000000 --- a/src/plugins/nat/nat44_cli.c +++ /dev/null @@ -1,2597 +0,0 @@ -/* - * Copyright (c) 2018 Cisco and/or its affiliates. - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/** - * @file - * @brief NAT44 CLI - */ - -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#define UNSUPPORTED_IN_ED_MODE_STR \ - "This command is unsupported in endpoint dependent mode" -#define SUPPORTED_ONLY_IN_ED_MODE_STR \ - "This command is supported only in endpoint dependent mode" - -static clib_error_t * -nat44_enable_command_fn (vlib_main_t * vm, - unformat_input_t * input, vlib_cli_command_t * cmd) -{ - snat_main_t *sm = &snat_main; - unformat_input_t _line_input, *line_input = &_line_input; - clib_error_t *error = 0; - - nat44_config_t c = { 0 }; - u8 mode_set = 0; - - if (sm->enabled) - return clib_error_return (0, "nat44 already enabled"); - - /* Get a line of input. */ - if (!unformat_user (input, unformat_line_input, line_input)) - { - if (nat44_plugin_enable (c) != 0) - return clib_error_return (0, "nat44 enable failed"); - return 0; - } - - while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT) - { - if (!mode_set && unformat (line_input, "static-mapping-only")) - { - mode_set = 1; - c.static_mapping_only = 1; - if (unformat (line_input, "connection-tracking")) - { - c.connection_tracking = 1; - } - } - else if (!mode_set && unformat (line_input, "out2in-dpo")) - { - mode_set = 1; - c.out2in_dpo = 1; - } - else if (!mode_set && unformat (line_input, "endpoint-dependent")) - { - mode_set = 1; - c.endpoint_dependent = 1; - } - else if (unformat (line_input, "inside-vrf %u", &c.inside_vrf)); - else if (unformat (line_input, "outside-vrf %u", &c.outside_vrf)); - else if (unformat (line_input, "users %u", &c.users)); - else if (unformat (line_input, "sessions %u", &c.sessions)); - else if (unformat (line_input, "user-sessions %u", &c.user_sessions)); - else - { - error = clib_error_return (0, "unknown input '%U'", - format_unformat_error, line_input); - goto done; - } - } - - if (!c.sessions) - { - error = clib_error_return (0, "number of sessions is required"); - goto done; - } - - if (nat44_plugin_enable (c) != 0) - error = clib_error_return (0, "nat44 enable failed"); -done: - unformat_free (line_input); - return error; -} - -static clib_error_t * -nat44_disable_command_fn (vlib_main_t * vm, - unformat_input_t * input, vlib_cli_command_t * cmd) -{ - snat_main_t *sm = &snat_main; - clib_error_t *error = 0; - - if (!sm->enabled) - return clib_error_return (0, "nat44 already disabled"); - - if (nat44_plugin_disable () != 0) - error = clib_error_return (0, "nat44 disable failed"); - - return error; -} - -static clib_error_t * -set_workers_command_fn (vlib_main_t * vm, - unformat_input_t * input, vlib_cli_command_t * cmd) -{ - unformat_input_t _line_input, *line_input = &_line_input; - uword *bitmap = 0; - int rv = 0; - clib_error_t *error = 0; - - /* 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, "%U", unformat_bitmap_list, &bitmap)) - ; - else - { - error = clib_error_return (0, "unknown input '%U'", - format_unformat_error, line_input); - goto done; - } - } - - if (bitmap == 0) - { - error = clib_error_return (0, "List of workers must be specified."); - goto done; - } - - rv = snat_set_workers (bitmap); - - clib_bitmap_free (bitmap); - - switch (rv) - { - case VNET_API_ERROR_INVALID_WORKER: - error = clib_error_return (0, "Invalid worker(s)."); - goto done; - case VNET_API_ERROR_FEATURE_DISABLED: - error = clib_error_return (0, - "Supported only if 2 or more workes available."); - goto done; - default: - break; - } - -done: - unformat_free (line_input); - - return error; -} - -static clib_error_t * -nat_show_workers_commnad_fn (vlib_main_t * vm, unformat_input_t * input, - vlib_cli_command_t * cmd) -{ - snat_main_t *sm = &snat_main; - u32 *worker; - - if (sm->num_workers > 1) - { - vlib_cli_output (vm, "%d workers", vec_len (sm->workers)); - /* *INDENT-OFF* */ - vec_foreach (worker, sm->workers) - { - vlib_worker_thread_t *w = - vlib_worker_threads + *worker + sm->first_worker_index; - vlib_cli_output (vm, " %s", w->name); - } - /* *INDENT-ON* */ - } - - return 0; -} - -static clib_error_t * -snat_set_log_level_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; - u8 log_level = SNAT_LOG_NONE; - clib_error_t *error = 0; - - /* Get a line of input. */ - if (!unformat_user (input, unformat_line_input, line_input)) - return 0; - - if (!unformat (line_input, "%d", &log_level)) - { - error = clib_error_return (0, "unknown input '%U'", - format_unformat_error, line_input); - goto done; - } - if (log_level > SNAT_LOG_DEBUG) - { - error = clib_error_return (0, "unknown logging level '%d'", log_level); - goto done; - } - sm->log_level = log_level; - -done: - unformat_free (line_input); - - return error; -} - -static clib_error_t * -snat_ipfix_logging_enable_disable_command_fn (vlib_main_t * vm, - unformat_input_t * input, - vlib_cli_command_t * cmd) -{ - unformat_input_t _line_input, *line_input = &_line_input; - u32 domain_id = 0; - u32 src_port = 0; - u8 enable = 1; - int rv = 0; - clib_error_t *error = 0; - - /* Get a line of input. */ - if (!unformat_user (input, unformat_line_input, line_input)) - { - rv = nat_ipfix_logging_enable_disable (enable, domain_id, - (u16) src_port); - if (rv) - return clib_error_return (0, "ipfix logging enable failed"); - return 0; - } - - while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT) - { - if (unformat (line_input, "domain %d", &domain_id)) - ; - else if (unformat (line_input, "src-port %d", &src_port)) - ; - else if (unformat (line_input, "disable")) - enable = 0; - else - { - error = clib_error_return (0, "unknown input '%U'", - format_unformat_error, line_input); - goto done; - } - } - - rv = nat_ipfix_logging_enable_disable (enable, domain_id, (u16) src_port); - - if (rv) - { - error = clib_error_return (0, "ipfix logging enable failed"); - goto done; - } - -done: - unformat_free (line_input); - - return error; -} - -static clib_error_t * -nat44_show_hash_command_fn (vlib_main_t * vm, unformat_input_t * input, - vlib_cli_command_t * cmd) -{ - snat_main_t *sm = &snat_main; - snat_main_per_thread_data_t *tsm; - nat_affinity_main_t *nam = &nat_affinity_main; - int i; - int verbose = 0; - - if (unformat (input, "detail")) - verbose = 1; - else if (unformat (input, "verbose")) - verbose = 2; - - vlib_cli_output (vm, "%U", format_bihash_8_8, &sm->static_mapping_by_local, - verbose); - vlib_cli_output (vm, "%U", - format_bihash_8_8, &sm->static_mapping_by_external, - verbose); - if (sm->endpoint_dependent) - { - vlib_cli_output (vm, "%U", format_bihash_16_8, &sm->flow_hash, verbose); - } - else - { - vlib_cli_output (vm, "%U", format_bihash_8_8, &sm->in2out, verbose); - vlib_cli_output (vm, "%U", format_bihash_8_8, &sm->out2in, verbose); - } - vec_foreach_index (i, sm->per_thread_data) - { - tsm = vec_elt_at_index (sm->per_thread_data, i); - vlib_cli_output (vm, "-------- thread %d %s --------\n", - i, vlib_worker_threads[i].name); - vlib_cli_output (vm, "%U", format_bihash_8_8, &tsm->user_hash, verbose); - } - - if (sm->endpoint_dependent) - { - vlib_cli_output (vm, "%U", format_bihash_16_8, &nam->affinity_hash, - verbose); - } - - vlib_cli_output (vm, "-------- hash table parameters --------\n"); - vlib_cli_output (vm, "translation buckets: %u", sm->translation_buckets); - if (!sm->endpoint_dependent) - { - vlib_cli_output (vm, "user buckets: %u", sm->user_buckets); - } - return 0; -} - -static clib_error_t * -nat44_set_alloc_addr_and_port_alg_command_fn (vlib_main_t * vm, - unformat_input_t * input, - vlib_cli_command_t * cmd) -{ - unformat_input_t _line_input, *line_input = &_line_input; - clib_error_t *error = 0; - u32 psid, psid_offset, psid_length, port_start, port_end; - snat_main_t *sm = &snat_main; - - if (sm->endpoint_dependent) - return clib_error_return (0, UNSUPPORTED_IN_ED_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, "default")) - nat44_ei_set_alloc_default (); - else - if (unformat - (line_input, "map-e psid %d psid-offset %d psid-len %d", &psid, - &psid_offset, &psid_length)) - nat44_ei_set_alloc_mape ((u16) psid, (u16) psid_offset, - (u16) psid_length); - else - if (unformat - (line_input, "port-range %d - %d", &port_start, &port_end)) - { - if (port_end <= port_start) - { - error = - clib_error_return (0, - "The end-port must be greater than start-port"); - goto done; - } - nat44_ei_set_alloc_range ((u16) port_start, (u16) port_end); - } - else - { - error = clib_error_return (0, "unknown input '%U'", - format_unformat_error, line_input); - goto done; - } - } - -done: - unformat_free (line_input); - - return error; -}; - -static clib_error_t * -nat44_show_alloc_addr_and_port_alg_command_fn (vlib_main_t * vm, - unformat_input_t * input, - vlib_cli_command_t * cmd) -{ - snat_main_t *sm = &snat_main; - - vlib_cli_output (vm, "NAT address and port: %U", - format_nat_addr_and_port_alloc_alg, - sm->addr_and_port_alloc_alg); - switch (sm->addr_and_port_alloc_alg) - { - case NAT_ADDR_AND_PORT_ALLOC_ALG_MAPE: - vlib_cli_output (vm, " psid %d psid-offset %d psid-len %d", sm->psid, - sm->psid_offset, sm->psid_length); - break; - case NAT_ADDR_AND_PORT_ALLOC_ALG_RANGE: - vlib_cli_output (vm, " start-port %d end-port %d", sm->start_port, - sm->end_port); - break; - default: - break; - } - - return 0; -} - -static clib_error_t * -nat_set_mss_clamping_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; - u32 mss; - - /* 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, "disable")) - sm->mss_clamping = 0; - else if (unformat (line_input, "%d", &mss)) - sm->mss_clamping = (u16) mss; - else - { - error = clib_error_return (0, "unknown input '%U'", - format_unformat_error, line_input); - goto done; - } - } - -done: - unformat_free (line_input); - - return error; -} - -static clib_error_t * -nat_show_mss_clamping_command_fn (vlib_main_t * vm, unformat_input_t * input, - vlib_cli_command_t * cmd) -{ - snat_main_t *sm = &snat_main; - - if (sm->mss_clamping) - vlib_cli_output (vm, "mss-clamping %d", sm->mss_clamping); - else - vlib_cli_output (vm, "mss-clamping disabled"); - - return 0; -} - -static clib_error_t * -nat_ha_failover_command_fn (vlib_main_t * vm, unformat_input_t * input, - vlib_cli_command_t * cmd) -{ - unformat_input_t _line_input, *line_input = &_line_input; - ip4_address_t addr; - u32 port, session_refresh_interval = 10; - int rv; - clib_error_t *error = 0; - - /* 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, "%U:%u", unformat_ip4_address, &addr, &port)) - ; - else - if (unformat - (line_input, "refresh-interval %u", &session_refresh_interval)) - ; - else - { - error = clib_error_return (0, "unknown input '%U'", - format_unformat_error, line_input); - goto done; - } - } - - rv = nat_ha_set_failover (&addr, (u16) port, session_refresh_interval); - if (rv) - error = clib_error_return (0, "set HA failover failed"); - -done: - unformat_free (line_input); - - return error; -} - -static clib_error_t * -nat_ha_listener_command_fn (vlib_main_t * vm, unformat_input_t * input, - vlib_cli_command_t * cmd) -{ - unformat_input_t _line_input, *line_input = &_line_input; - ip4_address_t addr; - u32 port, path_mtu = 512; - int rv; - clib_error_t *error = 0; - - /* 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, "%U:%u", unformat_ip4_address, &addr, &port)) - ; - else if (unformat (line_input, "path-mtu %u", &path_mtu)) - ; - else - { - error = clib_error_return (0, "unknown input '%U'", - format_unformat_error, line_input); - goto done; - } - } - - rv = nat_ha_set_listener (&addr, (u16) port, path_mtu); - if (rv) - error = clib_error_return (0, "set HA listener failed"); - -done: - unformat_free (line_input); - - return error; -} - -static clib_error_t * -nat_show_ha_command_fn (vlib_main_t * vm, unformat_input_t * input, - vlib_cli_command_t * cmd) -{ - ip4_address_t addr; - u16 port; - u32 path_mtu, session_refresh_interval, resync_ack_missed; - u8 in_resync; - - nat_ha_get_listener (&addr, &port, &path_mtu); - if (!port) - { - vlib_cli_output (vm, "NAT HA disabled\n"); - return 0; - } - - vlib_cli_output (vm, "LISTENER:\n"); - vlib_cli_output (vm, " %U:%u path-mtu %u\n", - format_ip4_address, &addr, port, path_mtu); - - nat_ha_get_failover (&addr, &port, &session_refresh_interval); - vlib_cli_output (vm, "FAILOVER:\n"); - if (port) - vlib_cli_output (vm, " %U:%u refresh-interval %usec\n", - format_ip4_address, &addr, port, - session_refresh_interval); - else - vlib_cli_output (vm, " NA\n"); - - nat_ha_get_resync_status (&in_resync, &resync_ack_missed); - vlib_cli_output (vm, "RESYNC:\n"); - if (in_resync) - vlib_cli_output (vm, " in progress\n"); - else - vlib_cli_output (vm, " completed (%d ACK missed)\n", resync_ack_missed); - - return 0; -} - -static clib_error_t * -nat_ha_flush_command_fn (vlib_main_t * vm, unformat_input_t * input, - vlib_cli_command_t * cmd) -{ - nat_ha_flush (0); - return 0; -} - -static clib_error_t * -nat_ha_resync_command_fn (vlib_main_t * vm, unformat_input_t * input, - vlib_cli_command_t * cmd) -{ - clib_error_t *error = 0; - - if (nat_ha_resync (0, 0, 0)) - error = clib_error_return (0, "NAT HA resync already running"); - - return error; -} - -static clib_error_t * -add_address_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; - ip4_address_t start_addr, end_addr, this_addr; - u32 start_host_order, end_host_order; - u32 vrf_id = ~0; - int i, count; - int is_add = 1; - int rv = 0; - clib_error_t *error = 0; - u8 twice_nat = 0; - - /* 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, "%U - %U", - unformat_ip4_address, &start_addr, - unformat_ip4_address, &end_addr)) - ; - else if (unformat (line_input, "tenant-vrf %u", &vrf_id)) - ; - else if (unformat (line_input, "%U", unformat_ip4_address, &start_addr)) - end_addr = start_addr; - else if (unformat (line_input, "twice-nat")) - twice_nat = 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 (sm->static_mapping_only) - { - error = clib_error_return (0, "static mapping only mode"); - goto done; - } - - start_host_order = clib_host_to_net_u32 (start_addr.as_u32); - end_host_order = clib_host_to_net_u32 (end_addr.as_u32); - - if (end_host_order < start_host_order) - { - error = clib_error_return (0, "end address less than start address"); - goto done; - } - - count = (end_host_order - start_host_order) + 1; - - if (count > 1024) - nat_log_info ("%U - %U, %d addresses...", - format_ip4_address, &start_addr, - format_ip4_address, &end_addr, count); - - this_addr = start_addr; - - for (i = 0; i < count; i++) - { - if (is_add) - rv = snat_add_address (sm, &this_addr, vrf_id, twice_nat); - else - rv = snat_del_address (sm, this_addr, 0, twice_nat); - - switch (rv) - { - case VNET_API_ERROR_VALUE_EXIST: - error = clib_error_return (0, "NAT address already in use."); - goto done; - case VNET_API_ERROR_NO_SUCH_ENTRY: - error = clib_error_return (0, "NAT address not exist."); - goto done; - case VNET_API_ERROR_UNSPECIFIED: - error = - clib_error_return (0, "NAT address used in static mapping."); - goto done; - case VNET_API_ERROR_FEATURE_DISABLED: - error = - clib_error_return (0, - "twice NAT available only for endpoint-dependent mode."); - goto done; - default: - break; - } - - if (sm->out2in_dpo) - nat44_add_del_address_dpo (this_addr, is_add); - - increment_v4_address (&this_addr); - } - -done: - unformat_free (line_input); - - return error; -} - -static void -nat44_show_lru_summary (vlib_main_t * vm, snat_main_per_thread_data_t * tsm, - u64 now, u64 sess_timeout_time) -{ - snat_main_t *sm = &snat_main; - dlist_elt_t *oldest_elt; - snat_session_t *s; - u32 oldest_index; - -#define _(n, d) \ - oldest_index = \ - clib_dlist_remove_head (tsm->lru_pool, tsm->n##_lru_head_index); \ - if (~0 != oldest_index) \ - { \ - oldest_elt = pool_elt_at_index (tsm->lru_pool, oldest_index); \ - s = pool_elt_at_index (tsm->sessions, oldest_elt->value); \ - sess_timeout_time = \ - s->last_heard + (f64)nat44_session_get_timeout (sm, s); \ - vlib_cli_output (vm, d " LRU min session timeout %llu (now %llu)", \ - sess_timeout_time, now); \ - clib_dlist_addhead (tsm->lru_pool, tsm->n##_lru_head_index, \ - oldest_index); \ - } - _(tcp_estab, "established tcp"); - _(tcp_trans, "transitory tcp"); - _(udp, "udp"); - _(unk_proto, "unknown protocol"); - _(icmp, "icmp"); -#undef _ -} - -static clib_error_t * -nat44_show_summary_command_fn (vlib_main_t * vm, unformat_input_t * input, - vlib_cli_command_t * cmd) -{ - snat_main_per_thread_data_t *tsm; - snat_main_t *sm = &snat_main; - snat_session_t *s; - - if (!sm->endpoint_dependent) - return clib_error_return (0, SUPPORTED_ONLY_IN_ED_MODE_STR); - - u32 count = 0; - - u64 now = vlib_time_now (vm); - u64 sess_timeout_time = 0; - - u32 udp_sessions = 0; - u32 tcp_sessions = 0; - u32 icmp_sessions = 0; - - u32 timed_out = 0; - u32 transitory = 0; - u32 transitory_wait_closed = 0; - u32 transitory_closed = 0; - u32 established = 0; - - u32 fib; - - for (fib = 0; fib < vec_len (sm->max_translations_per_fib); fib++) - vlib_cli_output (vm, "max translations per thread: %u fib %u", - sm->max_translations_per_fib[fib], fib); - - if (sm->num_workers > 1) - { - /* *INDENT-OFF* */ - vec_foreach (tsm, sm->per_thread_data) - { - pool_foreach (s, tsm->sessions) - { - sess_timeout_time = s->last_heard + - (f64) nat44_session_get_timeout (sm, s); - if (now >= sess_timeout_time) - timed_out++; - - switch (s->nat_proto) - { - case NAT_PROTOCOL_ICMP: - icmp_sessions++; - break; - case NAT_PROTOCOL_TCP: - tcp_sessions++; - if (s->state) - { - if (s->tcp_closed_timestamp) - { - if (now >= s->tcp_closed_timestamp) - { - ++transitory_closed; - } - else - { - ++transitory_wait_closed; - } - } - transitory++; - } - else - established++; - break; - case NAT_PROTOCOL_UDP: - default: - udp_sessions++; - break; - } - } - nat44_show_lru_summary (vm, tsm, now, sess_timeout_time); - count += pool_elts (tsm->sessions); - } - /* *INDENT-ON* */ - } - else - { - tsm = vec_elt_at_index (sm->per_thread_data, sm->num_workers); - /* *INDENT-OFF* */ - pool_foreach (s, tsm->sessions) - { - sess_timeout_time = s->last_heard + - (f64) nat44_session_get_timeout (sm, s); - if (now >= sess_timeout_time) - timed_out++; - - switch (s->nat_proto) - { - case NAT_PROTOCOL_ICMP: - icmp_sessions++; - break; - case NAT_PROTOCOL_TCP: - tcp_sessions++; - if (s->state) - { - if (s->tcp_closed_timestamp) - { - if (now >= s->tcp_closed_timestamp) - { - ++transitory_closed; - } - else - { - ++transitory_wait_closed; - } - } - transitory++; - } - else - established++; - break; - case NAT_PROTOCOL_UDP: - default: - udp_sessions++; - break; - } - } - /* *INDENT-ON* */ - nat44_show_lru_summary (vm, tsm, now, sess_timeout_time); - count = pool_elts (tsm->sessions); - } - - vlib_cli_output (vm, "total timed out sessions: %u", timed_out); - vlib_cli_output (vm, "total sessions: %u", count); - vlib_cli_output (vm, "total tcp sessions: %u", tcp_sessions); - vlib_cli_output (vm, "total tcp established sessions: %u", established); - vlib_cli_output (vm, "total tcp transitory sessions: %u", transitory); - vlib_cli_output (vm, "total tcp transitory (WAIT-CLOSED) sessions: %u", - transitory_wait_closed); - vlib_cli_output (vm, "total tcp transitory (CLOSED) sessions: %u", - transitory_closed); - vlib_cli_output (vm, "total udp sessions: %u", udp_sessions); - vlib_cli_output (vm, "total icmp sessions: %u", icmp_sessions); - return 0; -} - -static clib_error_t * -nat44_show_addresses_command_fn (vlib_main_t * vm, unformat_input_t * input, - vlib_cli_command_t * cmd) -{ - snat_main_t *sm = &snat_main; - snat_address_t *ap; - - vlib_cli_output (vm, "NAT44 pool addresses:"); - /* *INDENT-OFF* */ - vec_foreach (ap, sm->addresses) - { - vlib_cli_output (vm, "%U", format_ip4_address, &ap->addr); - if (ap->fib_index != ~0) - vlib_cli_output (vm, " tenant VRF: %u", - fib_table_get(ap->fib_index, FIB_PROTOCOL_IP4)->ft_table_id); - else - vlib_cli_output (vm, " tenant VRF independent"); - #define _(N, i, n, s) \ - vlib_cli_output (vm, " %d busy %s ports", ap->busy_##n##_ports, s); - foreach_nat_protocol - #undef _ - } - vlib_cli_output (vm, "NAT44 twice-nat pool addresses:"); - vec_foreach (ap, sm->twice_nat_addresses) - { - vlib_cli_output (vm, "%U", format_ip4_address, &ap->addr); - if (ap->fib_index != ~0) - vlib_cli_output (vm, " tenant VRF: %u", - fib_table_get(ap->fib_index, FIB_PROTOCOL_IP4)->ft_table_id); - else - vlib_cli_output (vm, " tenant VRF independent"); - #define _(N, i, n, s) \ - vlib_cli_output (vm, " %d busy %s ports", ap->busy_##n##_ports, s); - foreach_nat_protocol - #undef _ - } - /* *INDENT-ON* */ - return 0; -} - -static clib_error_t * -snat_feature_command_fn (vlib_main_t * vm, - unformat_input_t * input, vlib_cli_command_t * cmd) -{ - unformat_input_t _line_input, *line_input = &_line_input; - vnet_main_t *vnm = vnet_get_main (); - clib_error_t *error = 0; - u32 sw_if_index; - u32 *inside_sw_if_indices = 0; - u32 *outside_sw_if_indices = 0; - u8 is_output_feature = 0; - int is_del = 0; - int i; - - sw_if_index = ~0; - - /* 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, "in %U", unformat_vnet_sw_interface, - vnm, &sw_if_index)) - vec_add1 (inside_sw_if_indices, sw_if_index); - else if (unformat (line_input, "out %U", unformat_vnet_sw_interface, - vnm, &sw_if_index)) - vec_add1 (outside_sw_if_indices, sw_if_index); - else if (unformat (line_input, "output-feature")) - is_output_feature = 1; - else if (unformat (line_input, "del")) - is_del = 1; - else - { - error = clib_error_return (0, "unknown input '%U'", - format_unformat_error, line_input); - goto done; - } - } - - if (vec_len (inside_sw_if_indices)) - { - for (i = 0; i < vec_len (inside_sw_if_indices); i++) - { - sw_if_index = inside_sw_if_indices[i]; - if (is_output_feature) - { - if (snat_interface_add_del_output_feature - (sw_if_index, 1, is_del)) - { - error = clib_error_return (0, "%s %U failed", - is_del ? "del" : "add", - format_vnet_sw_if_index_name, - vnm, sw_if_index); - goto done; - } - } - else - { - if (snat_interface_add_del (sw_if_index, 1, is_del)) - { - error = clib_error_return (0, "%s %U failed", - is_del ? "del" : "add", - format_vnet_sw_if_index_name, - vnm, sw_if_index); - goto done; - } - } - } - } - - if (vec_len (outside_sw_if_indices)) - { - for (i = 0; i < vec_len (outside_sw_if_indices); i++) - { - sw_if_index = outside_sw_if_indices[i]; - if (is_output_feature) - { - if (snat_interface_add_del_output_feature - (sw_if_index, 0, is_del)) - { - error = clib_error_return (0, "%s %U failed", - is_del ? "del" : "add", - format_vnet_sw_if_index_name, - vnm, sw_if_index); - goto done; - } - } - else - { - if (snat_interface_add_del (sw_if_index, 0, is_del)) - { - error = clib_error_return (0, "%s %U failed", - is_del ? "del" : "add", - format_vnet_sw_if_index_name, - vnm, sw_if_index); - goto done; - } - } - } - } - -done: - unformat_free (line_input); - vec_free (inside_sw_if_indices); - vec_free (outside_sw_if_indices); - - return error; -} - -static clib_error_t * -nat44_show_interfaces_command_fn (vlib_main_t * vm, unformat_input_t * input, - vlib_cli_command_t * cmd) -{ - snat_main_t *sm = &snat_main; - snat_interface_t *i; - vnet_main_t *vnm = vnet_get_main (); - - vlib_cli_output (vm, "NAT44 interfaces:"); - /* *INDENT-OFF* */ - pool_foreach (i, sm->interfaces) - { - vlib_cli_output (vm, " %U %s", format_vnet_sw_if_index_name, vnm, - i->sw_if_index, - (nat_interface_is_inside(i) && - nat_interface_is_outside(i)) ? "in out" : - (nat_interface_is_inside(i) ? "in" : "out")); - } - - pool_foreach (i, sm->output_feature_interfaces) - { - vlib_cli_output (vm, " %U output-feature %s", - format_vnet_sw_if_index_name, vnm, - i->sw_if_index, - (nat_interface_is_inside(i) && - nat_interface_is_outside(i)) ? "in out" : - (nat_interface_is_inside(i) ? "in" : "out")); - } - /* *INDENT-ON* */ - - return 0; -} - -static clib_error_t * -add_static_mapping_command_fn (vlib_main_t * vm, - unformat_input_t * input, - vlib_cli_command_t * cmd) -{ - unformat_input_t _line_input, *line_input = &_line_input; - clib_error_t *error = 0; - ip4_address_t l_addr, e_addr, exact_addr; - u32 l_port = 0, e_port = 0, vrf_id = ~0; - int is_add = 1, addr_only = 1, rv, exact = 0; - u32 sw_if_index = ~0; - vnet_main_t *vnm = vnet_get_main (); - nat_protocol_t proto = NAT_PROTOCOL_OTHER; - u8 proto_set = 0; - twice_nat_type_t twice_nat = TWICE_NAT_DISABLED; - u8 out2in_only = 0; - - /* 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", unformat_ip4_address, &l_addr, - &l_port)) - addr_only = 0; - else - if (unformat (line_input, "local %U", unformat_ip4_address, &l_addr)) - ; - else if (unformat (line_input, "external %U %u", unformat_ip4_address, - &e_addr, &e_port)) - addr_only = 0; - else if (unformat (line_input, "external %U", unformat_ip4_address, - &e_addr)) - ; - else if (unformat (line_input, "external %U %u", - unformat_vnet_sw_interface, vnm, &sw_if_index, - &e_port)) - addr_only = 0; - else if (unformat (line_input, "external %U", - unformat_vnet_sw_interface, vnm, &sw_if_index)) - ; - else if (unformat (line_input, "exact %U", unformat_ip4_address, - &exact_addr)) - exact = 1; - else if (unformat (line_input, "vrf %u", &vrf_id)) - ; - else if (unformat (line_input, "%U", unformat_nat_protocol, &proto)) - proto_set = 1; - else if (unformat (line_input, "twice-nat")) - twice_nat = TWICE_NAT; - else if (unformat (line_input, "self-twice-nat")) - twice_nat = TWICE_NAT_SELF; - else if (unformat (line_input, "out2in-only")) - out2in_only = 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 (twice_nat && addr_only) - { - error = clib_error_return (0, "twice NAT only for 1:1 NAPT"); - goto done; - } - - if (addr_only) - { - if (proto_set) - { - error = - clib_error_return (0, - "address only mapping doesn't support protocol"); - goto done; - } - } - else if (!proto_set) - { - error = clib_error_return (0, "protocol is required"); - goto done; - } - - rv = snat_add_static_mapping ( - l_addr, e_addr, clib_host_to_net_u16 (l_port), - clib_host_to_net_u16 (e_port), vrf_id, addr_only, sw_if_index, proto, - is_add, twice_nat, out2in_only, 0, 0, exact_addr, exact); - - switch (rv) - { - case VNET_API_ERROR_INVALID_VALUE: - error = clib_error_return (0, "External port already in use."); - goto done; - case VNET_API_ERROR_NO_SUCH_ENTRY: - if (is_add) - error = clib_error_return (0, "External address must be allocated."); - else - error = clib_error_return (0, "Mapping not exist."); - goto done; - case VNET_API_ERROR_NO_SUCH_FIB: - error = clib_error_return (0, "No such VRF id."); - goto done; - case VNET_API_ERROR_VALUE_EXIST: - error = clib_error_return (0, "Mapping already exist."); - goto done; - case VNET_API_ERROR_FEATURE_DISABLED: - error = - clib_error_return (0, - "twice-nat/out2in-only available only for endpoint-dependent mode."); - goto done; - default: - break; - } - -done: - unformat_free (line_input); - - return error; -} - -static clib_error_t * -add_identity_mapping_command_fn (vlib_main_t * vm, - unformat_input_t * input, - vlib_cli_command_t * cmd) -{ - unformat_input_t _line_input, *line_input = &_line_input; - clib_error_t *error = 0; - ip4_address_t addr, pool_addr = { 0 }; - u32 port = 0, vrf_id = ~0; - int is_add = 1; - int addr_only = 1; - u32 sw_if_index = ~0; - vnet_main_t *vnm = vnet_get_main (); - int rv; - nat_protocol_t proto; - - addr.as_u32 = 0; - - /* 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, "%U", unformat_ip4_address, &addr)) - ; - else if (unformat (line_input, "external %U", - unformat_vnet_sw_interface, vnm, &sw_if_index)) - ; - else if (unformat (line_input, "vrf %u", &vrf_id)) - ; - else if (unformat (line_input, "%U %u", unformat_nat_protocol, &proto, - &port)) - addr_only = 0; - 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; - } - } - - rv = snat_add_static_mapping ( - addr, addr, clib_host_to_net_u16 (port), clib_host_to_net_u16 (port), - vrf_id, addr_only, sw_if_index, proto, is_add, 0, 0, 0, 1, pool_addr, 0); - - switch (rv) - { - case VNET_API_ERROR_INVALID_VALUE: - error = clib_error_return (0, "External port already in use."); - goto done; - case VNET_API_ERROR_NO_SUCH_ENTRY: - if (is_add) - error = clib_error_return (0, "External address must be allocated."); - else - error = clib_error_return (0, "Mapping not exist."); - goto done; - case VNET_API_ERROR_NO_SUCH_FIB: - error = clib_error_return (0, "No such VRF id."); - goto done; - case VNET_API_ERROR_VALUE_EXIST: - error = clib_error_return (0, "Mapping already exist."); - goto done; - default: - break; - } - -done: - unformat_free (line_input); - - return error; -} - -static clib_error_t * -add_lb_static_mapping_command_fn (vlib_main_t * vm, - unformat_input_t * input, - vlib_cli_command_t * cmd) -{ - unformat_input_t _line_input, *line_input = &_line_input; - clib_error_t *error = 0; - ip4_address_t l_addr, e_addr; - u32 l_port = 0, e_port = 0, vrf_id = 0, probability = 0, affinity = 0; - int is_add = 1; - int rv; - nat_protocol_t proto; - u8 proto_set = 0; - nat44_lb_addr_port_t *locals = 0, local; - twice_nat_type_t twice_nat = TWICE_NAT_DISABLED; - u8 out2in_only = 0; - - /* 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)) - { - clib_memset (&local, 0, sizeof (local)); - local.addr = l_addr; - local.port = (u16) l_port; - local.probability = (u8) probability; - vec_add1 (locals, local); - } - else if (unformat (line_input, "local %U:%u vrf %u probability %u", - unformat_ip4_address, &l_addr, &l_port, &vrf_id, - &probability)) - { - clib_memset (&local, 0, sizeof (local)); - local.addr = l_addr; - local.port = (u16) l_port; - local.probability = (u8) probability; - local.vrf_id = vrf_id; - vec_add1 (locals, local); - } - else if (unformat (line_input, "external %U:%u", unformat_ip4_address, - &e_addr, &e_port)) - ; - else if (unformat (line_input, "protocol %U", unformat_nat_protocol, - &proto)) - proto_set = 1; - else if (unformat (line_input, "twice-nat")) - twice_nat = TWICE_NAT; - else if (unformat (line_input, "self-twice-nat")) - twice_nat = TWICE_NAT_SELF; - else if (unformat (line_input, "out2in-only")) - out2in_only = 1; - else if (unformat (line_input, "del")) - is_add = 0; - else if (unformat (line_input, "affinity %u", &affinity)) - ; - else - { - error = clib_error_return (0, "unknown input: '%U'", - format_unformat_error, line_input); - goto done; - } - } - - if (vec_len (locals) < 2) - { - error = clib_error_return (0, "at least two local must be set"); - goto done; - } - - if (!proto_set) - { - error = clib_error_return (0, "missing protocol"); - goto done; - } - - rv = nat44_add_del_lb_static_mapping (e_addr, (u16) e_port, proto, locals, - is_add, twice_nat, out2in_only, 0, - affinity); - - switch (rv) - { - case VNET_API_ERROR_INVALID_VALUE: - error = clib_error_return (0, "External port already in use."); - goto done; - case VNET_API_ERROR_NO_SUCH_ENTRY: - if (is_add) - error = clib_error_return (0, "External address must be allocated."); - else - error = clib_error_return (0, "Mapping not exist."); - goto done; - case VNET_API_ERROR_VALUE_EXIST: - error = clib_error_return (0, "Mapping already exist."); - goto done; - case VNET_API_ERROR_FEATURE_DISABLED: - error = - clib_error_return (0, "Available only for endpoint-dependent mode."); - goto done; - default: - break; - } - -done: - unformat_free (line_input); - vec_free (locals); - - return error; -} - -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; - 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; - nat_protocol_t proto; - u8 proto_set = 0; - - /* 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_nat_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) -{ - snat_main_t *sm = &snat_main; - snat_static_mapping_t *m; - snat_static_map_resolve_t *rp; - - vlib_cli_output (vm, "NAT44 static mappings:"); - /* *INDENT-OFF* */ - pool_foreach (m, sm->static_mappings) - { - vlib_cli_output (vm, " %U", format_snat_static_mapping, m); - } - vec_foreach (rp, sm->to_resolve) - vlib_cli_output (vm, " %U", format_snat_static_map_to_resolve, rp); - /* *INDENT-ON* */ - - return 0; -} - -static clib_error_t * -snat_add_interface_address_command_fn (vlib_main_t * vm, - unformat_input_t * input, - vlib_cli_command_t * cmd) -{ - snat_main_t *sm = &snat_main; - unformat_input_t _line_input, *line_input = &_line_input; - u32 sw_if_index; - int rv; - int is_del = 0; - clib_error_t *error = 0; - u8 twice_nat = 0; - - /* 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, "%U", unformat_vnet_sw_interface, - sm->vnet_main, &sw_if_index)) - ; - else if (unformat (line_input, "twice-nat")) - twice_nat = 1; - else if (unformat (line_input, "del")) - is_del = 1; - else - { - error = clib_error_return (0, "unknown input '%U'", - format_unformat_error, line_input); - goto done; - } - } - - rv = snat_add_interface_address (sm, sw_if_index, is_del, twice_nat); - - switch (rv) - { - case 0: - break; - - default: - error = clib_error_return (0, "snat_add_interface_address returned %d", - rv); - goto done; - } - -done: - unformat_free (line_input); - - return error; -} - -static clib_error_t * -nat44_show_interface_address_command_fn (vlib_main_t * vm, - unformat_input_t * input, - vlib_cli_command_t * cmd) -{ - snat_main_t *sm = &snat_main; - vnet_main_t *vnm = vnet_get_main (); - u32 *sw_if_index; - - /* *INDENT-OFF* */ - vlib_cli_output (vm, "NAT44 pool address interfaces:"); - vec_foreach (sw_if_index, sm->auto_add_sw_if_indices) - { - vlib_cli_output (vm, " %U", format_vnet_sw_if_index_name, vnm, - *sw_if_index); - } - vlib_cli_output (vm, "NAT44 twice-nat pool address interfaces:"); - vec_foreach (sw_if_index, sm->auto_add_sw_if_indices_twice_nat) - { - vlib_cli_output (vm, " %U", format_vnet_sw_if_index_name, vnm, - *sw_if_index); - } - /* *INDENT-ON* */ - - return 0; -} - -static clib_error_t * -nat44_show_sessions_command_fn (vlib_main_t * vm, unformat_input_t * input, - vlib_cli_command_t * cmd) -{ - unformat_input_t _line_input, *line_input = &_line_input; - clib_error_t *error = 0; - - snat_main_per_thread_data_t *tsm; - snat_main_t *sm = &snat_main; - - int detail = 0; - int i = 0; - - if (!unformat_user (input, unformat_line_input, line_input)) - goto print; - - while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT) - { - if (unformat (line_input, "detail")) - detail = 1; - else - { - error = clib_error_return (0, "unknown input '%U'", - format_unformat_error, line_input); - break; - } - } - unformat_free (line_input); - -print: - if (!sm->endpoint_dependent) - vlib_cli_output (vm, "NAT44 sessions:"); - else - vlib_cli_output (vm, "NAT44 ED sessions:"); - - /* *INDENT-OFF* */ - vec_foreach_index (i, sm->per_thread_data) - { - tsm = vec_elt_at_index (sm->per_thread_data, i); - - vlib_cli_output (vm, "-------- thread %d %s: %d sessions --------\n", - i, vlib_worker_threads[i].name, - pool_elts (tsm->sessions)); - - if (!sm->endpoint_dependent) - { - snat_user_t *u; - pool_foreach (u, tsm->users) - { - vlib_cli_output (vm, " %U", format_snat_user, tsm, u, detail); - } - } - else - { - snat_session_t *s; - pool_foreach (s, tsm->sessions) - { - vlib_cli_output (vm, " %U\n", format_snat_session, tsm, s); - } - } - } - /* *INDENT-ON* */ - return error; -} - -static clib_error_t * -nat44_set_session_limit_command_fn (vlib_main_t * vm, - unformat_input_t * input, - vlib_cli_command_t * cmd) -{ - unformat_input_t _line_input, *line_input = &_line_input; - clib_error_t *error = 0; - - u32 session_limit = 0, vrf_id = 0; - - /* 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, "%u", &session_limit)) - ; - else if (unformat (line_input, "vrf %u", &vrf_id)) - ; - else - { - error = clib_error_return (0, "unknown input '%U'", - format_unformat_error, line_input); - goto done; - } - } - - if (!session_limit) - error = clib_error_return (0, "missing value of session limit"); - else if (nat44_update_session_limit (session_limit, vrf_id)) - error = clib_error_return (0, "nat44_set_session_limit failed"); - -done: - unformat_free (line_input); - - return error; -} - -static clib_error_t * -nat44_del_user_command_fn (vlib_main_t * vm, - unformat_input_t * input, vlib_cli_command_t * cmd) -{ - snat_main_t *sm = &snat_main; - unformat_input_t _line_input, *line_input = &_line_input; - clib_error_t *error = 0; - ip4_address_t addr; - u32 fib_index = 0; - int rv; - - if (sm->endpoint_dependent) - return clib_error_return (0, UNSUPPORTED_IN_ED_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, "%U", unformat_ip4_address, &addr)) - ; - else if (unformat (line_input, "fib %u", &fib_index)) - ; - else - { - error = clib_error_return (0, "unknown input '%U'", - format_unformat_error, line_input); - goto done; - } - } - - rv = nat44_ei_user_del (&addr, fib_index); - - if (!rv) - { - error = clib_error_return (0, "nat44_ei_user_del returned %d", rv); - } - -done: - unformat_free (line_input); - - return error; -} - -static clib_error_t * -nat44_clear_sessions_command_fn (vlib_main_t * vm, - unformat_input_t * input, - vlib_cli_command_t * cmd) -{ - clib_error_t *error = 0; - nat44_sessions_clear (); - return error; -} - -static clib_error_t * -nat44_del_session_command_fn (vlib_main_t * vm, - unformat_input_t * input, - vlib_cli_command_t * cmd) -{ - snat_main_t *sm = &snat_main; - unformat_input_t _line_input, *line_input = &_line_input; - int is_in = 0, is_ed = 0; - clib_error_t *error = 0; - ip4_address_t addr, eh_addr; - u32 port = 0, eh_port = 0, vrf_id = sm->outside_vrf_id; - nat_protocol_t proto; - int rv; - - /* 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, "%U:%u %U", unformat_ip4_address, &addr, &port, - unformat_nat_protocol, &proto)) - ; - else if (unformat (line_input, "in")) - { - is_in = 1; - vrf_id = sm->inside_vrf_id; - } - else if (unformat (line_input, "out")) - { - is_in = 0; - vrf_id = sm->outside_vrf_id; - } - else if (unformat (line_input, "vrf %u", &vrf_id)) - ; - else - if (unformat - (line_input, "external-host %U:%u", unformat_ip4_address, - &eh_addr, &eh_port)) - is_ed = 1; - else - { - error = clib_error_return (0, "unknown input '%U'", - format_unformat_error, line_input); - goto done; - } - } - - if (is_ed) - rv = - nat44_del_ed_session (sm, &addr, clib_host_to_net_u16 (port), &eh_addr, - clib_host_to_net_u16 (eh_port), - nat_proto_to_ip_proto (proto), vrf_id, is_in); - else - rv = nat44_ei_del_session (sm, &addr, clib_host_to_net_u16 (port), proto, - vrf_id, is_in); - - switch (rv) - { - case 0: - break; - - default: - error = clib_error_return (0, "nat44_del_session returned %d", rv); - goto done; - } - -done: - unformat_free (line_input); - - return error; -} - -static clib_error_t * -snat_forwarding_set_command_fn (vlib_main_t * vm, - unformat_input_t * input, - vlib_cli_command_t * cmd) -{ - snat_main_t *sm = &snat_main; - unformat_input_t _line_input, *line_input = &_line_input; - u8 forwarding_enable; - u8 forwarding_enable_set = 0; - clib_error_t *error = 0; - - /* Get a line of input. */ - if (!unformat_user (input, unformat_line_input, line_input)) - return clib_error_return (0, "'enable' or 'disable' expected"); - - while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT) - { - if (!forwarding_enable_set && unformat (line_input, "enable")) - { - forwarding_enable = 1; - forwarding_enable_set = 1; - } - else if (!forwarding_enable_set && unformat (line_input, "disable")) - { - forwarding_enable = 0; - forwarding_enable_set = 1; - } - else - { - error = clib_error_return (0, "unknown input '%U'", - format_unformat_error, line_input); - goto done; - } - } - - if (!forwarding_enable_set) - { - error = clib_error_return (0, "'enable' or 'disable' expected"); - goto done; - } - - sm->forwarding_enabled = forwarding_enable; - -done: - unformat_free (line_input); - - return error; -} - -static clib_error_t * -set_timeout_command_fn (vlib_main_t * vm, - unformat_input_t * input, vlib_cli_command_t * cmd) -{ - snat_main_t *sm = &snat_main; - unformat_input_t _line_input, *line_input = &_line_input; - clib_error_t *error = 0; - - /* 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, "udp %u", &sm->timeouts.udp)); - else if (unformat (line_input, "tcp-established %u", - &sm->timeouts.tcp.established)); - else if (unformat (line_input, "tcp-transitory %u", - &sm->timeouts.tcp.transitory)); - else if (unformat (line_input, "icmp %u", &sm->timeouts.icmp)); - else if (unformat (line_input, "reset")) - nat_reset_timeouts (&sm->timeouts); - else - { - error = clib_error_return (0, "unknown input '%U'", - format_unformat_error, line_input); - goto done; - } - } -done: - unformat_free (line_input); - return error; -} - -static clib_error_t * -nat_show_timeouts_command_fn (vlib_main_t * vm, - unformat_input_t * input, - vlib_cli_command_t * cmd) -{ - snat_main_t *sm = &snat_main; - - vlib_cli_output (vm, "udp timeout: %dsec", sm->timeouts.udp); - vlib_cli_output (vm, "tcp-established timeout: %dsec", - sm->timeouts.tcp.established); - vlib_cli_output (vm, "tcp-transitory timeout: %dsec", - sm->timeouts.tcp.transitory); - vlib_cli_output (vm, "icmp timeout: %dsec", sm->timeouts.icmp); - - return 0; -} - -static clib_error_t * -set_frame_queue_nelts_command_fn (vlib_main_t *vm, unformat_input_t *input, - vlib_cli_command_t *cmd) -{ - unformat_input_t _line_input, *line_input = &_line_input; - clib_error_t *error = 0; - u32 frame_queue_nelts = 0; - /* 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, "%u", &frame_queue_nelts)) - ; - else - { - error = clib_error_return (0, "unknown input '%U'", - format_unformat_error, line_input); - goto done; - } - } - if (!frame_queue_nelts) - { - error = clib_error_return (0, "frame_queue_nelts cannot be zero"); - goto done; - } - if (snat_set_frame_queue_nelts (frame_queue_nelts) != 0) - { - error = clib_error_return (0, "snat_set_frame_queue_nelts failed"); - goto done; - } -done: - unformat_free (line_input); - return error; -} - -static clib_error_t * -nat44_debug_fib_expire_command_fn (vlib_main_t * vm, - unformat_input_t * input, - vlib_cli_command_t * cmd) -{ - unformat_input_t _line_input, *line_input = &_line_input; - clib_error_t *error = 0; - u32 fib = ~0; - - /* 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, "%u", &fib)) - ; - else - { - error = clib_error_return (0, "unknown input '%U'", - format_unformat_error, line_input); - goto done; - } - } - expire_per_vrf_sessions (fib); -done: - unformat_free (line_input); - return error; -} - -static clib_error_t * -nat44_debug_fib_registration_command_fn (vlib_main_t * vm, - unformat_input_t * input, - vlib_cli_command_t * cmd) -{ - snat_main_t *sm = &snat_main; - snat_main_per_thread_data_t *tsm; - per_vrf_sessions_t *per_vrf_sessions; - - vlib_cli_output (vm, "VRF registration debug:"); - vec_foreach (tsm, sm->per_thread_data) - { - vlib_cli_output (vm, "thread %u:", tsm->thread_index); - vec_foreach (per_vrf_sessions, tsm->per_vrf_sessions_vec) - { - vlib_cli_output (vm, "rx fib %u tx fib %u ses count %u %s", - per_vrf_sessions->rx_fib_index, - per_vrf_sessions->tx_fib_index, - per_vrf_sessions->ses_count, - per_vrf_sessions->expired ? "expired" : ""); - } - } - return 0; -} - -/* *INDENT-OFF* */ - -/*? -?*/ -VLIB_CLI_COMMAND (nat44_debug_fib_expire_command, static) = { - .path = "debug nat44 fib expire", - .short_help = "debug nat44 fib expire ", - .function = nat44_debug_fib_expire_command_fn, -}; - -/*? -?*/ -VLIB_CLI_COMMAND (nat44_debug_fib_registration_command, static) = { - .path = "debug nat44 fib registration", - .short_help = "debug nat44 fib registration", - .function = nat44_debug_fib_registration_command_fn, -}; - -/*? - * @cliexpar - * @cliexstart{nat44 enable} - * Enable nat44 plugin - * To enable nat44, use: - * vpp# nat44 enable sessions - * To enable nat44 static mapping only, use: - * vpp# nat44 enable sessions static-mapping - * To enable nat44 static mapping with connection tracking, use: - * vpp# nat44 enable sessions static-mapping connection-tracking - * To enable nat44 out2in dpo, use: - * vpp# nat44 enable sessions out2in-dpo - * To enable nat44 endpoint-dependent, use: - * vpp# nat44 enable sessions endpoint-dependent - * To set inside-vrf outside-vrf, use: - * vpp# nat44 enable sessions inside-vrf outside-vrf - * @cliexend -?*/ -VLIB_CLI_COMMAND (nat44_enable_command, static) = { - .path = "nat44 enable", - .short_help = "nat44 enable sessions [users ] [static-mappig-only [connection-tracking]|out2in-dpo|endpoint-dependent] [inside-vrf ] [outside-vrf ] [user-sessions ]", - .function = nat44_enable_command_fn, -}; - -/*? - * @cliexpar - * @cliexstart{nat44 disable} - * Disable nat44 plugin - * To disable nat44, use: - * vpp# nat44 disable - * @cliexend -?*/ -VLIB_CLI_COMMAND (nat44_disable_command, static) = { - .path = "nat44 disable", - .short_help = "nat44 disable", - .function = nat44_disable_command_fn, -}; - -/*? - * @cliexpar - * @cliexstart{set snat workers} - * Set NAT workers if 2 or more workers available, use: - * vpp# set snat workers 0-2,5 - * @cliexend -?*/ -VLIB_CLI_COMMAND (set_workers_command, static) = { - .path = "set nat workers", - .function = set_workers_command_fn, - .short_help = "set nat workers ", -}; - -/*? - * @cliexpar - * @cliexstart{show nat workers} - * Show NAT workers. - * vpp# show nat workers: - * 2 workers - * vpp_wk_0 - * vpp_wk_1 - * @cliexend -?*/ -VLIB_CLI_COMMAND (nat_show_workers_command, static) = { - .path = "show nat workers", - .short_help = "show nat workers", - .function = nat_show_workers_commnad_fn, -}; - -/*? - * @cliexpar - * @cliexstart{set nat timeout} - * Set values of timeouts for NAT sessions (in seconds), use: - * vpp# set nat timeout udp 120 tcp-established 7500 tcp-transitory 250 icmp 90 - * To reset default values use: - * vpp# set nat timeout reset - * @cliexend -?*/ -VLIB_CLI_COMMAND (set_timeout_command, static) = { - .path = "set nat timeout", - .function = set_timeout_command_fn, - .short_help = - "set nat timeout [udp | tcp-established " - "tcp-transitory | icmp | reset]", -}; - -/*? - * @cliexpar - * @cliexstart{show nat timeouts} - * Show values of timeouts for NAT sessions. - * vpp# show nat timeouts - * udp timeout: 300sec - * tcp-established timeout: 7440sec - * tcp-transitory timeout: 240sec - * icmp timeout: 60sec - * @cliexend -?*/ -VLIB_CLI_COMMAND (nat_show_timeouts_command, static) = { - .path = "show nat timeouts", - .short_help = "show nat timeouts", - .function = nat_show_timeouts_command_fn, -}; - -/*? - * @cliexpar - * @cliexstart{set nat frame-queue-nelts} - * Set number of worker handoff frame queue elements. - * @cliexend -?*/ -VLIB_CLI_COMMAND (set_frame_queue_nelts_command, static) = { - .path = "set nat frame-queue-nelts", - .function = set_frame_queue_nelts_command_fn, - .short_help = "set nat frame-queue-nelts ", -}; - -/*? - * @cliexpar - * @cliexstart{nat set logging level} - * To set NAT logging level use: - * Set nat logging level - * @cliexend -?*/ -VLIB_CLI_COMMAND (snat_set_log_level_command, static) = { - .path = "nat set logging level", - .function = snat_set_log_level_command_fn, - .short_help = "nat set logging level ", -}; - -/*? - * @cliexpar - * @cliexstart{snat ipfix logging} - * To enable NAT IPFIX logging use: - * vpp# nat ipfix logging - * To set IPFIX exporter use: - * vpp# set ipfix exporter collector 10.10.10.3 src 10.10.10.1 - * @cliexend -?*/ -VLIB_CLI_COMMAND (snat_ipfix_logging_enable_disable_command, static) = { - .path = "nat ipfix logging", - .function = snat_ipfix_logging_enable_disable_command_fn, - .short_help = "nat ipfix logging [domain ] [src-port ] [disable]", -}; - -/*? - * @cliexpar - * @cliexstart{nat addr-port-assignment-alg} - * Set address and port assignment algorithm - * For the MAP-E CE limit port choice based on PSID use: - * vpp# nat addr-port-assignment-alg map-e psid 10 psid-offset 6 psid-len 6 - * For port range use: - * vpp# nat addr-port-assignment-alg port-range - - * To set standard (default) address and port assignment algorithm use: - * vpp# nat addr-port-assignment-alg default - * @cliexend -?*/ -VLIB_CLI_COMMAND (nat44_set_alloc_addr_and_port_alg_command, static) = { - .path = "nat addr-port-assignment-alg", - .short_help = "nat addr-port-assignment-alg []", - .function = nat44_set_alloc_addr_and_port_alg_command_fn, -}; - -/*? - * @cliexpar - * @cliexstart{show nat addr-port-assignment-alg} - * Show address and port assignment algorithm - * @cliexend -?*/ -VLIB_CLI_COMMAND (nat44_show_alloc_addr_and_port_alg_command, static) = { - .path = "show nat addr-port-assignment-alg", - .short_help = "show nat addr-port-assignment-alg", - .function = nat44_show_alloc_addr_and_port_alg_command_fn, -}; - -/*? - * @cliexpar - * @cliexstart{nat mss-clamping} - * Set TCP MSS rewriting configuration - * To enable TCP MSS rewriting use: - * vpp# nat mss-clamping 1452 - * To disbale TCP MSS rewriting use: - * vpp# nat mss-clamping disable - * @cliexend -?*/ -VLIB_CLI_COMMAND (nat_set_mss_clamping_command, static) = { - .path = "nat mss-clamping", - .short_help = "nat mss-clamping |disable", - .function = nat_set_mss_clamping_command_fn, -}; - -/*? - * @cliexpar - * @cliexstart{show nat mss-clamping} - * Show TCP MSS rewriting configuration - * @cliexend -?*/ -VLIB_CLI_COMMAND (nat_show_mss_clamping_command, static) = { - .path = "show nat mss-clamping", - .short_help = "show nat mss-clamping", - .function = nat_show_mss_clamping_command_fn, -}; - -/*? - * @cliexpar - * @cliexstart{nat ha failover} - * Set HA failover (remote settings) - * @cliexend -?*/ -VLIB_CLI_COMMAND (nat_ha_failover_command, static) = { - .path = "nat ha failover", - .short_help = "nat ha failover : [refresh-interval ]", - .function = nat_ha_failover_command_fn, -}; - -/*? - * @cliexpar - * @cliexstart{nat ha listener} - * Set HA listener (local settings) - * @cliexend -?*/ -VLIB_CLI_COMMAND (nat_ha_listener_command, static) = { - .path = "nat ha listener", - .short_help = "nat ha listener : [path-mtu ]", - .function = nat_ha_listener_command_fn, -}; - -/*? - * @cliexpar - * @cliexstart{show nat ha} - * Show HA configuration/status - * @cliexend -?*/ -VLIB_CLI_COMMAND (nat_show_ha_command, static) = { - .path = "show nat ha", - .short_help = "show nat ha", - .function = nat_show_ha_command_fn, -}; - -/*? - * @cliexpar - * @cliexstart{nat ha flush} - * Flush the current HA data (for testing) - * @cliexend -?*/ -VLIB_CLI_COMMAND (nat_ha_flush_command, static) = { - .path = "nat ha flush", - .short_help = "nat ha flush", - .function = nat_ha_flush_command_fn, -}; - -/*? - * @cliexpar - * @cliexstart{nat ha resync} - * Resync HA (resend existing sessions to new failover) - * @cliexend -?*/ -VLIB_CLI_COMMAND (nat_ha_resync_command, static) = { - .path = "nat ha resync", - .short_help = "nat ha resync", - .function = nat_ha_resync_command_fn, -}; - -/*? - * @cliexpar - * @cliexstart{show nat44 hash tables} - * Show NAT44 hash tables - * @cliexend -?*/ -VLIB_CLI_COMMAND (nat44_show_hash, static) = { - .path = "show nat44 hash tables", - .short_help = "show nat44 hash tables [detail|verbose]", - .function = nat44_show_hash_command_fn, -}; - -/*? - * @cliexpar - * @cliexstart{nat44 add address} - * Add/delete NAT44 pool address. - * To add NAT44 pool address use: - * vpp# nat44 add address 172.16.1.3 - * vpp# nat44 add address 172.16.2.2 - 172.16.2.24 - * To add NAT44 pool address for specific tenant (identified by VRF id) use: - * vpp# nat44 add address 172.16.1.3 tenant-vrf 10 - * @cliexend -?*/ -VLIB_CLI_COMMAND (add_address_command, static) = { - .path = "nat44 add address", - .short_help = "nat44 add address [- ] " - "[tenant-vrf ] [twice-nat] [del]", - .function = add_address_command_fn, -}; - -/*? - * @cliexpar - * @cliexstart{show nat44 summary} - * Show NAT44 summary - * vpp# show nat44 summary - * @cliexend -?*/ -VLIB_CLI_COMMAND (nat44_show_summary_command, static) = { - .path = "show nat44 summary", - .short_help = "show nat44 summary", - .function = nat44_show_summary_command_fn, -}; - -/*? - * @cliexpar - * @cliexstart{show nat44 addresses} - * Show NAT44 pool addresses. - * vpp# show nat44 addresses - * NAT44 pool addresses: - * 172.16.2.2 - * tenant VRF independent - * 10 busy udp ports - * 0 busy tcp ports - * 0 busy icmp ports - * 172.16.1.3 - * tenant VRF: 10 - * 0 busy udp ports - * 2 busy tcp ports - * 0 busy icmp ports - * NAT44 twice-nat pool addresses: - * 10.20.30.72 - * tenant VRF independent - * 0 busy udp ports - * 0 busy tcp ports - * 0 busy icmp ports - * @cliexend -?*/ -VLIB_CLI_COMMAND (nat44_show_addresses_command, static) = { - .path = "show nat44 addresses", - .short_help = "show nat44 addresses", - .function = nat44_show_addresses_command_fn, -}; - -/*? - * @cliexpar - * @cliexstart{set interface nat44} - * Enable/disable NAT44 feature on the interface. - * To enable NAT44 feature with local network interface use: - * vpp# set interface nat44 in GigabitEthernet0/8/0 - * To enable NAT44 feature with external network interface use: - * vpp# set interface nat44 out GigabitEthernet0/a/0 - * @cliexend -?*/ -VLIB_CLI_COMMAND (set_interface_snat_command, static) = { - .path = "set interface nat44", - .function = snat_feature_command_fn, - .short_help = "set interface nat44 in out [output-feature] " - "[del]", -}; - -/*? - * @cliexpar - * @cliexstart{show nat44 interfaces} - * Show interfaces with NAT44 feature. - * vpp# show nat44 interfaces - * NAT44 interfaces: - * GigabitEthernet0/8/0 in - * GigabitEthernet0/a/0 out - * @cliexend -?*/ -VLIB_CLI_COMMAND (nat44_show_interfaces_command, static) = { - .path = "show nat44 interfaces", - .short_help = "show nat44 interfaces", - .function = nat44_show_interfaces_command_fn, -}; - -/*? - * @cliexpar - * @cliexstart{nat44 add static mapping} - * Static mapping allows hosts on the external network to initiate connection - * to to the local network host. - * To create static mapping between local host address 10.0.0.3 port 6303 and - * external address 4.4.4.4 port 3606 for TCP protocol use: - * vpp# nat44 add static mapping tcp local 10.0.0.3 6303 external 4.4.4.4 3606 - * If not runnig "static mapping only" NAT plugin mode use before: - * vpp# nat44 add address 4.4.4.4 - * To create address only static mapping between local and external address use: - * vpp# nat44 add static mapping local 10.0.0.3 external 4.4.4.4 - * To create ICMP static mapping between local and external with ICMP echo - * identifier 10 use: - * vpp# nat44 add static mapping icmp local 10.0.0.3 10 external 4.4.4.4 10 - * To force use of specific pool address, vrf independent - * vpp# nat44 add static mapping local 10.0.0.2 1234 external 10.0.2.2 1234 twice-nat exact 10.0.1.2 - * @cliexend -?*/ -VLIB_CLI_COMMAND (add_static_mapping_command, static) = { - .path = "nat44 add static mapping", - .function = add_static_mapping_command_fn, - .short_help = - "nat44 add static mapping tcp|udp|icmp local [] " - "external [] [vrf ] [twice-nat|self-twice-nat] " - "[out2in-only] [exact ] [del]", -}; - -/*? - * @cliexpar - * @cliexstart{nat44 add identity mapping} - * Identity mapping translate an IP address to itself. - * To create identity mapping for address 10.0.0.3 port 6303 for TCP protocol - * use: - * vpp# nat44 add identity mapping 10.0.0.3 tcp 6303 - * To create identity mapping for address 10.0.0.3 use: - * vpp# nat44 add identity mapping 10.0.0.3 - * To create identity mapping for DHCP addressed interface use: - * vpp# nat44 add identity mapping external GigabitEthernet0/a/0 tcp 3606 - * @cliexend -?*/ -VLIB_CLI_COMMAND (add_identity_mapping_command, static) = { - .path = "nat44 add identity mapping", - .function = add_identity_mapping_command_fn, - .short_help = "nat44 add identity mapping |external " - "[ ] [vrf ] [del]", -}; - -/*? - * @cliexpar - * @cliexstart{nat44 add load-balancing static mapping} - * Service load balancing using NAT44 - * To add static mapping with load balancing for service with external IP - * address 1.2.3.4 and TCP port 80 and mapped to 2 local servers - * 10.100.10.10:8080 and 10.100.10.20:8080 with probability 80% resp. 20% use: - * vpp# nat44 add load-balancing static mapping protocol tcp external 1.2.3.4:80 local 10.100.10.10:8080 probability 80 local 10.100.10.20:8080 probability 20 - * @cliexend -?*/ -VLIB_CLI_COMMAND (add_lb_static_mapping_command, static) = { - .path = "nat44 add load-balancing static mapping", - .function = add_lb_static_mapping_command_fn, - .short_help = - "nat44 add load-balancing static mapping protocol tcp|udp " - "external : local : [vrf ] " - "probability [twice-nat|self-twice-nat] [out2in-only] " - "[affinity ] [del]", -}; - -/*? - * @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 : local : [vrf ] " - "probability [del]", -}; - -/*? - * @cliexpar - * @cliexstart{show nat44 static mappings} - * Show NAT44 static mappings. - * vpp# show nat44 static mappings - * NAT44 static mappings: - * local 10.0.0.3 external 4.4.4.4 vrf 0 - * tcp local 192.168.0.4:6303 external 4.4.4.3:3606 vrf 0 - * tcp vrf 0 external 1.2.3.4:80 out2in-only - * local 10.100.10.10:8080 probability 80 - * local 10.100.10.20:8080 probability 20 - * tcp local 10.100.3.8:8080 external 169.10.10.1:80 vrf 0 twice-nat - * tcp local 10.0.0.10:3603 external GigabitEthernet0/a/0:6306 vrf 10 - * @cliexend -?*/ -VLIB_CLI_COMMAND (nat44_show_static_mappings_command, static) = { - .path = "show nat44 static mappings", - .short_help = "show nat44 static mappings", - .function = nat44_show_static_mappings_command_fn, -}; - -/*? - * @cliexpar - * @cliexstart{nat44 add interface address} - * Use NAT44 pool address from specific interfce - * To add NAT44 pool address from specific interface use: - * vpp# nat44 add interface address GigabitEthernet0/8/0 - * @cliexend -?*/ -VLIB_CLI_COMMAND (snat_add_interface_address_command, static) = { - .path = "nat44 add interface address", - .short_help = "nat44 add interface address [twice-nat] [del]", - .function = snat_add_interface_address_command_fn, -}; - -/*? - * @cliexpar - * @cliexstart{show nat44 interface address} - * Show NAT44 pool address interfaces - * vpp# show nat44 interface address - * NAT44 pool address interfaces: - * GigabitEthernet0/a/0 - * NAT44 twice-nat pool address interfaces: - * GigabitEthernet0/8/0 - * @cliexend -?*/ -VLIB_CLI_COMMAND (nat44_show_interface_address_command, static) = { - .path = "show nat44 interface address", - .short_help = "show nat44 interface address", - .function = nat44_show_interface_address_command_fn, -}; - -/*? - * @cliexpar - * @cliexstart{show nat44 sessions} - * Show NAT44 sessions. - * @cliexend -?*/ -VLIB_CLI_COMMAND (nat44_show_sessions_command, static) = { - .path = "show nat44 sessions", - .short_help = "show nat44 sessions [detail|metrics]", - .function = nat44_show_sessions_command_fn, -}; - -/*? - * @cliexpar - * @cliexstart{set nat44 session limit} - * Set NAT44 session limit. - * @cliexend -?*/ -VLIB_CLI_COMMAND (nat44_set_session_limit_command, static) = { - .path = "set nat44 session limit", - .short_help = "set nat44 session limit [vrf ]", - .function = nat44_set_session_limit_command_fn, -}; - -/*? - * @cliexpar - * @cliexstart{nat44 del user} - * To delete all NAT44 user sessions: - * vpp# nat44 del user 10.0.0.3 - * @cliexend -?*/ -VLIB_CLI_COMMAND (nat44_del_user_command, static) = { - .path = "nat44 del user", - .short_help = "nat44 del user [fib ]", - .function = nat44_del_user_command_fn, -}; - -/*? - * @cliexpar - * @cliexstart{clear nat44 sessions} - * To clear all NAT44 sessions - * vpp# clear nat44 sessions - * @cliexend -?*/ -VLIB_CLI_COMMAND (nat44_clear_sessions_command, static) = { - .path = "clear nat44 sessions", - .short_help = "clear nat44 sessions", - .function = nat44_clear_sessions_command_fn, -}; - -/*? - * @cliexpar - * @cliexstart{nat44 del session} - * To administratively delete NAT44 session by inside address and port use: - * vpp# nat44 del session in 10.0.0.3:6303 tcp - * To administratively delete NAT44 session by outside address and port use: - * vpp# nat44 del session out 1.0.0.3:6033 udp - * @cliexend -?*/ -VLIB_CLI_COMMAND (nat44_del_session_command, static) = { - .path = "nat44 del session", - .short_help = "nat44 del session in|out : tcp|udp|icmp [vrf ] [external-host :]", - .function = nat44_del_session_command_fn, -}; - -/*? - * @cliexpar - * @cliexstart{nat44 forwarding} - * Enable or disable forwarding - * Forward packets which don't match existing translation - * or static mapping instead of dropping them. - * To enable forwarding, use: - * vpp# nat44 forwarding enable - * To disable forwarding, use: - * vpp# nat44 forwarding disable - * @cliexend -?*/ -VLIB_CLI_COMMAND (snat_forwarding_set_command, static) = { - .path = "nat44 forwarding", - .short_help = "nat44 forwarding enable|disable", - .function = snat_forwarding_set_command_fn, -}; - -/* *INDENT-ON* */ - -/* - * fd.io coding-style-patch-verification: ON - * - * Local Variables: - * eval: (c-set-style "gnu") - * End: - */ diff --git a/src/plugins/nat/nat44_hairpinning.c b/src/plugins/nat/nat44_hairpinning.c deleted file mode 100644 index a2cb2eb7547..00000000000 --- a/src/plugins/nat/nat44_hairpinning.c +++ /dev/null @@ -1,746 +0,0 @@ -/* - * Copyright (c) 2018 Cisco and/or its affiliates. - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/** - * @file - * @brief NAT44 hairpinning - */ - -#include -#include -#include -#include -#include -#include - -typedef enum -{ - SNAT_HAIRPIN_SRC_NEXT_DROP, - SNAT_HAIRPIN_SRC_NEXT_SNAT_IN2OUT, - SNAT_HAIRPIN_SRC_NEXT_SNAT_IN2OUT_WH, - SNAT_HAIRPIN_SRC_NEXT_INTERFACE_OUTPUT, - SNAT_HAIRPIN_SRC_N_NEXT, -} snat_hairpin_src_next_t; - -typedef enum -{ - NAT_HAIRPIN_NEXT_LOOKUP, - NAT_HAIRPIN_NEXT_DROP, - NAT_HAIRPIN_NEXT_HANDOFF, - NAT_HAIRPIN_N_NEXT, -} nat_hairpin_next_t; - -typedef struct -{ - ip4_address_t addr; - u16 port; - u32 fib_index; - u32 session_index; -} nat_hairpin_trace_t; - -static u8 * -format_nat_hairpin_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 *); - nat_hairpin_trace_t *t = va_arg (*args, nat_hairpin_trace_t *); - - s = - format (s, "new dst addr %U port %u fib-index %u", format_ip4_address, - &t->addr, clib_net_to_host_u16 (t->port), t->fib_index); - if (~0 == t->session_index) - { - s = format (s, " is-static-mapping"); - } - else - { - s = format (s, " session-index %u", t->session_index); - } - - return s; -} - -extern vnet_feature_arc_registration_t vnet_feat_arc_ip4_local; - -static_always_inline int -is_hairpinning (snat_main_t * sm, ip4_address_t * dst_addr) -{ - snat_address_t *ap; - clib_bihash_kv_8_8_t kv, value; - - vec_foreach (ap, sm->addresses) - { - if (ap->addr.as_u32 == dst_addr->as_u32) - return 1; - } - - init_nat_k (&kv, *dst_addr, 0, 0, 0); - if (!clib_bihash_search_8_8 (&sm->static_mapping_by_external, &kv, &value)) - return 1; - - return 0; -} - -#ifndef CLIB_MARCH_VARIANT -int -snat_hairpinning (vlib_main_t *vm, vlib_node_runtime_t *node, snat_main_t *sm, - u32 thread_index, vlib_buffer_t *b0, ip4_header_t *ip0, - udp_header_t *udp0, tcp_header_t *tcp0, u32 proto0, - int do_trace, u32 *required_thread_index) -{ - snat_session_t *s0 = NULL; - clib_bihash_kv_8_8_t kv0, value0; - ip_csum_t sum0; - u32 new_dst_addr0 = 0, old_dst_addr0, si = ~0; - u16 new_dst_port0 = ~0, old_dst_port0; - int rv; - ip4_address_t sm0_addr; - u16 sm0_port; - u32 sm0_fib_index; - u32 old_sw_if_index = vnet_buffer (b0)->sw_if_index[VLIB_TX]; - /* Check if destination is static mappings */ - if (!snat_static_mapping_match - (sm, ip0->dst_address, udp0->dst_port, sm->outside_fib_index, proto0, - &sm0_addr, &sm0_port, &sm0_fib_index, 1, 0, 0, 0, 0, 0, 0)) - { - new_dst_addr0 = sm0_addr.as_u32; - new_dst_port0 = sm0_port; - vnet_buffer (b0)->sw_if_index[VLIB_TX] = sm0_fib_index; - } - /* or active session */ - else - { - init_nat_k (&kv0, ip0->dst_address, udp0->dst_port, - sm->outside_fib_index, proto0); - rv = clib_bihash_search_8_8 (&sm->out2in, &kv0, &value0); - if (rv) - { - rv = 0; - goto trace; - } - - if (thread_index != nat_value_get_thread_index (&value0)) - { - *required_thread_index = nat_value_get_thread_index (&value0); - return 0; - } - - si = nat_value_get_session_index (&value0); - s0 = pool_elt_at_index (sm->per_thread_data[thread_index].sessions, si); - new_dst_addr0 = s0->in2out.addr.as_u32; - new_dst_port0 = s0->in2out.port; - vnet_buffer (b0)->sw_if_index[VLIB_TX] = s0->in2out.fib_index; - } - - /* Check if anything has changed and if not, then return 0. This - helps avoid infinite loop, repeating the three nodes - nat44-hairpinning-->ip4-lookup-->ip4-local, in case nothing has - changed. */ - old_dst_addr0 = ip0->dst_address.as_u32; - old_dst_port0 = tcp0->dst; - if (new_dst_addr0 == old_dst_addr0 - && new_dst_port0 == old_dst_port0 - && vnet_buffer (b0)->sw_if_index[VLIB_TX] == old_sw_if_index) - return 0; - - /* Destination is behind the same NAT, use internal address and port */ - if (new_dst_addr0) - { - old_dst_addr0 = ip0->dst_address.as_u32; - ip0->dst_address.as_u32 = new_dst_addr0; - sum0 = ip0->checksum; - sum0 = ip_csum_update (sum0, old_dst_addr0, new_dst_addr0, - ip4_header_t, dst_address); - ip0->checksum = ip_csum_fold (sum0); - - old_dst_port0 = tcp0->dst; - if (PREDICT_TRUE (new_dst_port0 != old_dst_port0)) - { - if (PREDICT_TRUE (proto0 == NAT_PROTOCOL_TCP)) - { - tcp0->dst = new_dst_port0; - sum0 = tcp0->checksum; - sum0 = ip_csum_update (sum0, old_dst_addr0, new_dst_addr0, - ip4_header_t, dst_address); - sum0 = ip_csum_update (sum0, old_dst_port0, new_dst_port0, - ip4_header_t /* cheat */ , length); - tcp0->checksum = ip_csum_fold (sum0); - } - else - { - udp0->dst_port = new_dst_port0; - udp0->checksum = 0; - } - } - else - { - if (PREDICT_TRUE (proto0 == NAT_PROTOCOL_TCP)) - { - sum0 = tcp0->checksum; - sum0 = ip_csum_update (sum0, old_dst_addr0, new_dst_addr0, - ip4_header_t, dst_address); - tcp0->checksum = ip_csum_fold (sum0); - } - } - rv = 1; - goto trace; - } - rv = 0; -trace: - if (do_trace && PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE) - && (b0->flags & VLIB_BUFFER_IS_TRACED))) - { - nat_hairpin_trace_t *t = vlib_add_trace (vm, node, b0, sizeof (*t)); - t->addr.as_u32 = new_dst_addr0; - t->port = new_dst_port0; - t->fib_index = vnet_buffer (b0)->sw_if_index[VLIB_TX]; - if (s0) - { - t->session_index = si; - } - else - { - t->session_index = ~0; - } - } - return rv; -} -#endif - -#ifndef CLIB_MARCH_VARIANT -u32 -snat_icmp_hairpinning (snat_main_t *sm, vlib_buffer_t *b0, u32 thread_index, - ip4_header_t *ip0, icmp46_header_t *icmp0, - u32 *required_thread_index) -{ - clib_bihash_kv_8_8_t kv0, value0; - u32 old_dst_addr0, new_dst_addr0; - u32 old_addr0, new_addr0; - u16 old_port0, new_port0; - u16 old_checksum0, new_checksum0; - u32 si, ti = 0; - ip_csum_t sum0; - snat_session_t *s0; - snat_static_mapping_t *m0; - - if (icmp_type_is_error_message - (vnet_buffer (b0)->ip.reass.icmp_type_or_tcp_flags)) - { - ip4_header_t *inner_ip0 = 0; - tcp_udp_header_t *l4_header = 0; - - inner_ip0 = (ip4_header_t *) ((icmp_echo_header_t *) (icmp0 + 1) + 1); - l4_header = ip4_next_header (inner_ip0); - u32 protocol = ip_proto_to_nat_proto (inner_ip0->protocol); - - if (protocol != NAT_PROTOCOL_TCP && protocol != NAT_PROTOCOL_UDP) - return 1; - - init_nat_k (&kv0, ip0->dst_address, l4_header->src_port, - sm->outside_fib_index, protocol); - if (clib_bihash_search_8_8 (&sm->out2in, &kv0, &value0)) - return 1; - ti = nat_value_get_thread_index (&value0); - if (ti != thread_index) - { - *required_thread_index = ti; - return 1; - } - si = nat_value_get_session_index (&value0); - s0 = pool_elt_at_index (sm->per_thread_data[ti].sessions, si); - new_dst_addr0 = s0->in2out.addr.as_u32; - vnet_buffer (b0)->sw_if_index[VLIB_TX] = s0->in2out.fib_index; - - /* update inner source IP address */ - old_addr0 = inner_ip0->src_address.as_u32; - inner_ip0->src_address.as_u32 = new_dst_addr0; - new_addr0 = inner_ip0->src_address.as_u32; - sum0 = icmp0->checksum; - sum0 = ip_csum_update (sum0, old_addr0, new_addr0, ip4_header_t, - src_address); - icmp0->checksum = ip_csum_fold (sum0); - - /* update inner IP header checksum */ - old_checksum0 = inner_ip0->checksum; - sum0 = inner_ip0->checksum; - sum0 = ip_csum_update (sum0, old_addr0, new_addr0, ip4_header_t, - src_address); - inner_ip0->checksum = ip_csum_fold (sum0); - new_checksum0 = inner_ip0->checksum; - sum0 = icmp0->checksum; - sum0 = ip_csum_update (sum0, old_checksum0, new_checksum0, ip4_header_t, - checksum); - icmp0->checksum = ip_csum_fold (sum0); - - /* update inner source port */ - old_port0 = l4_header->src_port; - l4_header->src_port = s0->in2out.port; - new_port0 = l4_header->src_port; - sum0 = icmp0->checksum; - sum0 = ip_csum_update (sum0, old_port0, new_port0, tcp_udp_header_t, - src_port); - icmp0->checksum = ip_csum_fold (sum0); - } - else - { - init_nat_k (&kv0, ip0->dst_address, 0, sm->outside_fib_index, 0); - if (clib_bihash_search_8_8 (&sm->static_mapping_by_external, &kv0, - &value0)) - { - icmp_echo_header_t *echo0 = (icmp_echo_header_t *) (icmp0 + 1); - u16 icmp_id0 = echo0->identifier; - init_nat_k (&kv0, ip0->dst_address, icmp_id0, sm->outside_fib_index, - NAT_PROTOCOL_ICMP); - int rv = clib_bihash_search_8_8 (&sm->out2in, &kv0, &value0); - if (!rv) - { - ti = nat_value_get_thread_index (&value0); - if (ti != thread_index) - { - *required_thread_index = ti; - return 1; - } - si = nat_value_get_session_index (&value0); - s0 = pool_elt_at_index (sm->per_thread_data[ti].sessions, si); - new_dst_addr0 = s0->in2out.addr.as_u32; - vnet_buffer (b0)->sw_if_index[VLIB_TX] = s0->in2out.fib_index; - echo0->identifier = s0->in2out.port; - sum0 = icmp0->checksum; - sum0 = ip_csum_update (sum0, icmp_id0, s0->in2out.port, - icmp_echo_header_t, identifier); - icmp0->checksum = ip_csum_fold (sum0); - goto change_addr; - } - - return 1; - } - - m0 = pool_elt_at_index (sm->static_mappings, value0.value); - - new_dst_addr0 = m0->local_addr.as_u32; - if (vnet_buffer (b0)->sw_if_index[VLIB_TX] == ~0) - vnet_buffer (b0)->sw_if_index[VLIB_TX] = m0->fib_index; - } -change_addr: - /* Destination is behind the same NAT, use internal address and port */ - if (new_dst_addr0) - { - old_dst_addr0 = ip0->dst_address.as_u32; - ip0->dst_address.as_u32 = new_dst_addr0; - sum0 = ip0->checksum; - sum0 = ip_csum_update (sum0, old_dst_addr0, new_dst_addr0, - ip4_header_t, dst_address); - ip0->checksum = ip_csum_fold (sum0); - } - return 0; -} -#endif - -#ifndef CLIB_MARCH_VARIANT -void -nat_hairpinning_sm_unknown_proto (snat_main_t * sm, - vlib_buffer_t * b, ip4_header_t * ip) -{ - clib_bihash_kv_8_8_t kv, value; - snat_static_mapping_t *m; - u32 old_addr, new_addr; - ip_csum_t sum; - - init_nat_k (&kv, ip->dst_address, 0, 0, 0); - if (clib_bihash_search_8_8 (&sm->static_mapping_by_external, &kv, &value)) - return; - - m = pool_elt_at_index (sm->static_mappings, value.value); - - old_addr = ip->dst_address.as_u32; - new_addr = ip->dst_address.as_u32 = m->local_addr.as_u32; - sum = ip->checksum; - sum = ip_csum_update (sum, old_addr, new_addr, ip4_header_t, dst_address); - ip->checksum = ip_csum_fold (sum); - - if (vnet_buffer (b)->sw_if_index[VLIB_TX] == ~0) - vnet_buffer (b)->sw_if_index[VLIB_TX] = m->fib_index; -} -#endif - -static inline uword -nat44_hairpinning_fn_inline (vlib_main_t *vm, vlib_node_runtime_t *node, - vlib_frame_t *frame) -{ - u32 n_left_from, *from, *to_next; - u32 thread_index = vm->thread_index; - nat_hairpin_next_t next_index; - snat_main_t *sm = &snat_main; - vnet_feature_main_t *fm = &feature_main; - u8 arc_index = vnet_feat_arc_ip4_local.feature_arc_index; - vnet_feature_config_main_t *cm = &fm->feature_config_mains[arc_index]; - - from = vlib_frame_vector_args (frame); - n_left_from = frame->n_vectors; - next_index = node->cached_next_index; - - while (n_left_from > 0) - { - u32 n_left_to_next; - - vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next); - - while (n_left_from > 0 && n_left_to_next > 0) - { - u32 bi0; - vlib_buffer_t *b0; - u32 next0; - ip4_header_t *ip0; - u32 proto0; - udp_header_t *udp0; - tcp_header_t *tcp0; - u32 sw_if_index0; - u32 required_thread_index = thread_index; - - /* speculatively enqueue b0 to the current next frame */ - bi0 = from[0]; - to_next[0] = bi0; - from += 1; - to_next += 1; - n_left_from -= 1; - n_left_to_next -= 1; - - b0 = vlib_get_buffer (vm, bi0); - ip0 = vlib_buffer_get_current (b0); - udp0 = ip4_next_header (ip0); - tcp0 = (tcp_header_t *) udp0; - sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX]; - - proto0 = ip_proto_to_nat_proto (ip0->protocol); - int next0_resolved = 0; - - if (snat_hairpinning (vm, node, sm, thread_index, b0, ip0, udp0, - tcp0, proto0, 1 /* do_trace */, - &required_thread_index)) - { - next0 = NAT_HAIRPIN_NEXT_LOOKUP; - next0_resolved = 1; - } - - if (thread_index != required_thread_index) - { - vnet_buffer (b0)->snat.required_thread_index = - required_thread_index; - next0 = NAT_HAIRPIN_NEXT_HANDOFF; - next0_resolved = 1; - } - - if (!next0_resolved) - vnet_get_config_data (&cm->config_main, &b0->current_config_index, - &next0, 0); - - if (next0 != NAT_HAIRPIN_NEXT_DROP) - { - vlib_increment_simple_counter (&sm->counters.hairpinning, - vm->thread_index, sw_if_index0, - 1); - } - - /* verify speculative enqueue, maybe switch current next frame */ - vlib_validate_buffer_enqueue_x1 (vm, node, next_index, - to_next, n_left_to_next, - bi0, next0); - } - - vlib_put_next_frame (vm, node, next_index, n_left_to_next); - } - - return frame->n_vectors; -} - -VLIB_NODE_FN (nat44_hairpinning_node) (vlib_main_t * vm, - vlib_node_runtime_t * node, - vlib_frame_t * frame) -{ - return nat44_hairpinning_fn_inline (vm, node, frame); -} - -VLIB_REGISTER_NODE (nat44_hairpinning_node) = { - .name = "nat44-hairpinning", - .vector_size = sizeof (u32), - .type = VLIB_NODE_TYPE_INTERNAL, - .format_trace = format_nat_hairpin_trace, - .n_next_nodes = NAT_HAIRPIN_N_NEXT, - .next_nodes = { - [NAT_HAIRPIN_NEXT_DROP] = "error-drop", - [NAT_HAIRPIN_NEXT_LOOKUP] = "ip4-lookup", - [NAT_HAIRPIN_NEXT_HANDOFF] = "nat44-hairpinning-handoff", - }, -}; - -VLIB_NODE_FN (nat44_hairpinning_handoff_node) -(vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame) -{ - return nat44_hairpinning_handoff_fn_inline ( - vm, node, frame, snat_main.nat44_hairpinning_fq_index); -} - -VLIB_REGISTER_NODE (nat44_hairpinning_handoff_node) = { - .name = "nat44-hairpinning-handoff", - .vector_size = sizeof (u32), - .n_errors = ARRAY_LEN(nat44_hairpinning_handoff_error_strings), - .error_strings = nat44_hairpinning_handoff_error_strings, - .format_trace = format_nat44_hairpinning_handoff_trace, - - .n_next_nodes = 1, - - .next_nodes = { - [0] = "error-drop", - }, -}; - -static inline uword -snat_hairpin_dst_fn_inline (vlib_main_t *vm, vlib_node_runtime_t *node, - vlib_frame_t *frame) -{ - u32 n_left_from, *from, *to_next; - u32 thread_index = vm->thread_index; - nat_hairpin_next_t next_index; - snat_main_t *sm = &snat_main; - - from = vlib_frame_vector_args (frame); - n_left_from = frame->n_vectors; - next_index = node->cached_next_index; - - while (n_left_from > 0) - { - u32 n_left_to_next; - - vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next); - - while (n_left_from > 0 && n_left_to_next > 0) - { - u32 bi0; - vlib_buffer_t *b0; - u32 next0; - ip4_header_t *ip0; - u32 proto0; - u32 sw_if_index0; - u32 required_thread_index = thread_index; - - /* speculatively enqueue b0 to the current next frame */ - bi0 = from[0]; - to_next[0] = bi0; - from += 1; - to_next += 1; - n_left_from -= 1; - n_left_to_next -= 1; - - b0 = vlib_get_buffer (vm, bi0); - next0 = NAT_HAIRPIN_NEXT_LOOKUP; - ip0 = vlib_buffer_get_current (b0); - sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX]; - - proto0 = ip_proto_to_nat_proto (ip0->protocol); - - vnet_buffer (b0)->snat.flags = 0; - if (PREDICT_FALSE (is_hairpinning (sm, &ip0->dst_address))) - { - if (proto0 == NAT_PROTOCOL_TCP || proto0 == NAT_PROTOCOL_UDP) - { - udp_header_t *udp0 = ip4_next_header (ip0); - tcp_header_t *tcp0 = (tcp_header_t *) udp0; - - snat_hairpinning (vm, node, sm, thread_index, b0, ip0, udp0, - tcp0, proto0, 1 /* do_trace */, - &required_thread_index); - } - else if (proto0 == NAT_PROTOCOL_ICMP) - { - icmp46_header_t *icmp0 = ip4_next_header (ip0); - - snat_icmp_hairpinning (sm, b0, thread_index, ip0, icmp0, - &required_thread_index); - } - else - { - nat_hairpinning_sm_unknown_proto (sm, b0, ip0); - } - - vnet_buffer (b0)->snat.flags = SNAT_FLAG_HAIRPINNING; - } - - if (thread_index != required_thread_index) - { - vnet_buffer (b0)->snat.required_thread_index = - required_thread_index; - next0 = NAT_HAIRPIN_NEXT_HANDOFF; - } - - if (next0 != NAT_HAIRPIN_NEXT_DROP) - { - vlib_increment_simple_counter (&sm->counters.hairpinning, - vm->thread_index, sw_if_index0, - 1); - } - - /* verify speculative enqueue, maybe switch current next frame */ - vlib_validate_buffer_enqueue_x1 (vm, node, next_index, - to_next, n_left_to_next, - bi0, next0); - } - - vlib_put_next_frame (vm, node, next_index, n_left_to_next); - } - - return frame->n_vectors; -} - -VLIB_NODE_FN (snat_hairpin_dst_node) (vlib_main_t * vm, - vlib_node_runtime_t * node, - vlib_frame_t * frame) -{ - return snat_hairpin_dst_fn_inline (vm, node, frame); -} - -VLIB_REGISTER_NODE (snat_hairpin_dst_node) = { - .name = "nat44-hairpin-dst", - .vector_size = sizeof (u32), - .type = VLIB_NODE_TYPE_INTERNAL, - .format_trace = format_nat_hairpin_trace, - .n_next_nodes = NAT_HAIRPIN_N_NEXT, - .next_nodes = { - [NAT_HAIRPIN_NEXT_DROP] = "error-drop", - [NAT_HAIRPIN_NEXT_LOOKUP] = "ip4-lookup", - [NAT_HAIRPIN_NEXT_HANDOFF] = "nat44-hairpin-dst-handoff", - }, -}; - -VLIB_NODE_FN (nat44_hairpinning_dst_handoff_node) -(vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame) -{ - return nat44_hairpinning_handoff_fn_inline ( - vm, node, frame, snat_main.snat_hairpin_dst_fq_index); -} - -VLIB_REGISTER_NODE (nat44_hairpinning_dst_handoff_node) = { - .name = "nat44-hairpin-dst-handoff", - .vector_size = sizeof (u32), - .n_errors = ARRAY_LEN(nat44_hairpinning_handoff_error_strings), - .error_strings = nat44_hairpinning_handoff_error_strings, - .format_trace = format_nat44_hairpinning_handoff_trace, - - .n_next_nodes = 1, - - .next_nodes = { - [0] = "error-drop", - }, -}; - -static inline uword -snat_hairpin_src_fn_inline (vlib_main_t *vm, vlib_node_runtime_t *node, - vlib_frame_t *frame) -{ - u32 n_left_from, *from, *to_next; - snat_hairpin_src_next_t next_index; - snat_main_t *sm = &snat_main; - - from = vlib_frame_vector_args (frame); - n_left_from = frame->n_vectors; - next_index = node->cached_next_index; - - while (n_left_from > 0) - { - u32 n_left_to_next; - - vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next); - - while (n_left_from > 0 && n_left_to_next > 0) - { - u32 bi0; - vlib_buffer_t *b0; - u32 next0; - snat_interface_t *i; - u32 sw_if_index0; - - /* speculatively enqueue b0 to the current next frame */ - bi0 = from[0]; - to_next[0] = bi0; - from += 1; - to_next += 1; - n_left_from -= 1; - n_left_to_next -= 1; - - b0 = vlib_get_buffer (vm, bi0); - sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX]; - vnet_feature_next (&next0, b0); - - pool_foreach (i, sm->output_feature_interfaces) - { - /* Only packets from NAT inside interface */ - if ((nat_interface_is_inside(i)) && (sw_if_index0 == i->sw_if_index)) - { - if (PREDICT_FALSE ((vnet_buffer (b0)->snat.flags) & - SNAT_FLAG_HAIRPINNING)) - { - if (PREDICT_TRUE (sm->num_workers > 1)) - next0 = SNAT_HAIRPIN_SRC_NEXT_SNAT_IN2OUT_WH; - else - next0 = SNAT_HAIRPIN_SRC_NEXT_SNAT_IN2OUT; - } - break; - } - } - - if (next0 != SNAT_HAIRPIN_SRC_NEXT_DROP) - { - vlib_increment_simple_counter (&sm->counters.hairpinning, - vm->thread_index, sw_if_index0, - 1); - } - - /* verify speculative enqueue, maybe switch current next frame */ - vlib_validate_buffer_enqueue_x1 (vm, node, next_index, - to_next, n_left_to_next, - bi0, next0); - } - - vlib_put_next_frame (vm, node, next_index, n_left_to_next); - } - - return frame->n_vectors; -} - -VLIB_NODE_FN (snat_hairpin_src_node) (vlib_main_t * vm, - vlib_node_runtime_t * node, - vlib_frame_t * frame) -{ - return snat_hairpin_src_fn_inline (vm, node, frame); -} - -VLIB_REGISTER_NODE (snat_hairpin_src_node) = { - .name = "nat44-hairpin-src", - .vector_size = sizeof (u32), - .type = VLIB_NODE_TYPE_INTERNAL, - .n_next_nodes = SNAT_HAIRPIN_SRC_N_NEXT, - .next_nodes = { - [SNAT_HAIRPIN_SRC_NEXT_DROP] = "error-drop", - [SNAT_HAIRPIN_SRC_NEXT_SNAT_IN2OUT] = "nat44-in2out-output", - [SNAT_HAIRPIN_SRC_NEXT_INTERFACE_OUTPUT] = "interface-output", - [SNAT_HAIRPIN_SRC_NEXT_SNAT_IN2OUT_WH] = "nat44-in2out-output-worker-handoff", - }, -}; - -/* - * fd.io coding-style-patch-verification: ON - * - * Local Variables: - * eval: (c-set-style "gnu") - * End: - */ diff --git a/src/plugins/nat/nat44_hairpinning.h b/src/plugins/nat/nat44_hairpinning.h deleted file mode 100644 index 2a8b73db6ab..00000000000 --- a/src/plugins/nat/nat44_hairpinning.h +++ /dev/null @@ -1,92 +0,0 @@ -#ifndef __included_nat44_hairpinning_h__ -#define __included_nat44_hairpinning_h__ - -#include - -#define foreach_nat44_hairpinning_handoff_error \ - _ (CONGESTION_DROP, "congestion drop") - -typedef enum -{ -#define _(sym, str) NAT44_HAIRPINNING_HANDOFF_ERROR_##sym, - foreach_nat44_hairpinning_handoff_error -#undef _ - NAT44_HAIRPINNING_HANDOFF_N_ERROR, -} nat44_hairpinning_handoff_error_t; - -static char *nat44_hairpinning_handoff_error_strings[] = { -#define _(sym, string) string, - foreach_nat44_hairpinning_handoff_error -#undef _ -}; - -typedef struct -{ - u32 next_worker_index; -} nat44_hairpinning_handoff_trace_t; - -static u8 * -format_nat44_hairpinning_handoff_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 *); - nat44_hairpinning_handoff_trace_t *t = - va_arg (*args, nat44_hairpinning_handoff_trace_t *); - - s = format (s, "nat-hairpinning-handoff: next-worker %d", - t->next_worker_index); - - return s; -} - -always_inline uword -nat44_hairpinning_handoff_fn_inline (vlib_main_t *vm, - vlib_node_runtime_t *node, - vlib_frame_t *frame, u32 fq_index) -{ - vlib_buffer_t *bufs[VLIB_FRAME_SIZE], **b; - u32 n_enq, n_left_from, *from; - u16 thread_indices[VLIB_FRAME_SIZE], *ti; - - from = vlib_frame_vector_args (frame); - n_left_from = frame->n_vectors; - vlib_get_buffers (vm, from, bufs, n_left_from); - - b = bufs; - ti = thread_indices; - - while (n_left_from > 0) - { - ti[0] = vnet_buffer (b[0])->snat.required_thread_index; - - if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE) && - (b[0]->flags & VLIB_BUFFER_IS_TRACED))) - { - nat44_hairpinning_handoff_trace_t *t = - vlib_add_trace (vm, node, b[0], sizeof (*t)); - t->next_worker_index = ti[0]; - } - - n_left_from -= 1; - ti += 1; - b += 1; - } - n_enq = vlib_buffer_enqueue_to_thread (vm, fq_index, from, thread_indices, - frame->n_vectors, 1); - - if (n_enq < frame->n_vectors) - vlib_node_increment_counter ( - vm, node->node_index, NAT44_HAIRPINNING_HANDOFF_ERROR_CONGESTION_DROP, - frame->n_vectors - n_enq); - return frame->n_vectors; -} - -#endif // __included_nat44_hairpinning_h__ - -/* - * fd.io coding-style-patch-verification: ON - * - * Local Variables: - * eval: (c-set-style "gnu") - * End: - */ diff --git a/src/plugins/nat/nat44_handoff.c b/src/plugins/nat/nat44_handoff.c deleted file mode 100644 index 8c1b967c020..00000000000 --- a/src/plugins/nat/nat44_handoff.c +++ /dev/null @@ -1,343 +0,0 @@ -/* - * Copyright (c) 2018 Cisco and/or its affiliates. - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/** - * @file - * @brief NAT44 worker handoff - */ - -#include -#include -#include -#include -#include -#include -#include - -typedef struct -{ - u32 next_worker_index; - u32 trace_index; - u8 in2out; - u8 output; -} nat44_handoff_trace_t; - -#define foreach_nat44_handoff_error \ - _ (CONGESTION_DROP, "congestion drop") \ - _ (SAME_WORKER, "same worker") \ - _ (DO_HANDOFF, "do handoff") - -typedef enum -{ -#define _(sym, str) NAT44_HANDOFF_ERROR_##sym, - foreach_nat44_handoff_error -#undef _ - NAT44_HANDOFF_N_ERROR, -} nat44_handoff_error_t; - -static char *nat44_handoff_error_strings[] = { -#define _(sym,string) string, - foreach_nat44_handoff_error -#undef _ -}; - -static u8 * -format_nat44_handoff_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 *); - nat44_handoff_trace_t *t = va_arg (*args, nat44_handoff_trace_t *); - char *tag, *output; - - tag = t->in2out ? "IN2OUT" : "OUT2IN"; - output = t->output ? "OUTPUT-FEATURE" : ""; - s = - format (s, "NAT44_%s_WORKER_HANDOFF %s: next-worker %d trace index %d", - tag, output, t->next_worker_index, t->trace_index); - - return s; -} - -static inline uword -nat44_worker_handoff_fn_inline (vlib_main_t * vm, - vlib_node_runtime_t * node, - vlib_frame_t * frame, u8 is_output, - u8 is_in2out) -{ - u32 n_enq, n_left_from, *from, do_handoff = 0, same_worker = 0; - - u16 thread_indices[VLIB_FRAME_SIZE], *ti = thread_indices; - vlib_buffer_t *bufs[VLIB_FRAME_SIZE], **b = bufs; - snat_main_t *sm = &snat_main; - - u32 fq_index, thread_index = vm->thread_index; - - from = vlib_frame_vector_args (frame); - n_left_from = frame->n_vectors; - - vlib_get_buffers (vm, from, b, n_left_from); - - if (is_in2out) - { - fq_index = is_output ? sm->fq_in2out_output_index : sm->fq_in2out_index; - } - else - { - fq_index = sm->fq_out2in_index; - } - - while (n_left_from >= 4) - { - u32 arc_next0, arc_next1, arc_next2, arc_next3; - u32 sw_if_index0, sw_if_index1, sw_if_index2, sw_if_index3; - u32 rx_fib_index0, rx_fib_index1, rx_fib_index2, rx_fib_index3; - u32 iph_offset0 = 0, iph_offset1 = 0, iph_offset2 = 0, iph_offset3 = 0; - ip4_header_t *ip0, *ip1, *ip2, *ip3; - - if (PREDICT_TRUE (n_left_from >= 8)) - { - vlib_prefetch_buffer_header (b[4], LOAD); - vlib_prefetch_buffer_header (b[5], LOAD); - vlib_prefetch_buffer_header (b[6], LOAD); - vlib_prefetch_buffer_header (b[7], LOAD); - CLIB_PREFETCH (&b[4]->data, CLIB_CACHE_LINE_BYTES, LOAD); - CLIB_PREFETCH (&b[5]->data, CLIB_CACHE_LINE_BYTES, LOAD); - CLIB_PREFETCH (&b[6]->data, CLIB_CACHE_LINE_BYTES, LOAD); - CLIB_PREFETCH (&b[7]->data, CLIB_CACHE_LINE_BYTES, LOAD); - } - - if (is_output) - { - iph_offset0 = vnet_buffer (b[0])->ip.save_rewrite_length; - iph_offset1 = vnet_buffer (b[1])->ip.save_rewrite_length; - iph_offset2 = vnet_buffer (b[2])->ip.save_rewrite_length; - iph_offset3 = vnet_buffer (b[3])->ip.save_rewrite_length; - } - - ip0 = (ip4_header_t *) ((u8 *) vlib_buffer_get_current (b[0]) + - iph_offset0); - ip1 = (ip4_header_t *) ((u8 *) vlib_buffer_get_current (b[1]) + - iph_offset1); - ip2 = (ip4_header_t *) ((u8 *) vlib_buffer_get_current (b[2]) + - iph_offset2); - ip3 = (ip4_header_t *) ((u8 *) vlib_buffer_get_current (b[3]) + - iph_offset3); - - vnet_feature_next (&arc_next0, b[0]); - vnet_feature_next (&arc_next1, b[1]); - vnet_feature_next (&arc_next2, b[2]); - vnet_feature_next (&arc_next3, b[3]); - - vnet_buffer2 (b[0])->nat.arc_next = arc_next0; - vnet_buffer2 (b[1])->nat.arc_next = arc_next1; - vnet_buffer2 (b[2])->nat.arc_next = arc_next2; - vnet_buffer2 (b[3])->nat.arc_next = arc_next3; - - sw_if_index0 = vnet_buffer (b[0])->sw_if_index[VLIB_RX]; - sw_if_index1 = vnet_buffer (b[1])->sw_if_index[VLIB_RX]; - sw_if_index2 = vnet_buffer (b[2])->sw_if_index[VLIB_RX]; - sw_if_index3 = vnet_buffer (b[3])->sw_if_index[VLIB_RX]; - - rx_fib_index0 = ip4_fib_table_get_index_for_sw_if_index (sw_if_index0); - rx_fib_index1 = ip4_fib_table_get_index_for_sw_if_index (sw_if_index1); - rx_fib_index2 = ip4_fib_table_get_index_for_sw_if_index (sw_if_index2); - rx_fib_index3 = ip4_fib_table_get_index_for_sw_if_index (sw_if_index3); - - if (is_in2out) - { - ti[0] = sm->worker_in2out_cb (ip0, rx_fib_index0, is_output); - ti[1] = sm->worker_in2out_cb (ip1, rx_fib_index1, is_output); - ti[2] = sm->worker_in2out_cb (ip2, rx_fib_index2, is_output); - ti[3] = sm->worker_in2out_cb (ip3, rx_fib_index3, is_output); - } - else - { - ti[0] = sm->worker_out2in_cb (b[0], ip0, rx_fib_index0, is_output); - ti[1] = sm->worker_out2in_cb (b[1], ip1, rx_fib_index1, is_output); - ti[2] = sm->worker_out2in_cb (b[2], ip2, rx_fib_index2, is_output); - ti[3] = sm->worker_out2in_cb (b[3], ip3, rx_fib_index3, is_output); - } - - if (ti[0] == thread_index) - same_worker++; - else - do_handoff++; - - if (ti[1] == thread_index) - same_worker++; - else - do_handoff++; - - if (ti[2] == thread_index) - same_worker++; - else - do_handoff++; - - if (ti[3] == thread_index) - same_worker++; - else - do_handoff++; - - b += 4; - ti += 4; - n_left_from -= 4; - } - - while (n_left_from > 0) - { - u32 arc_next0; - u32 sw_if_index0; - u32 rx_fib_index0; - u32 iph_offset0 = 0; - ip4_header_t *ip0; - - - if (is_output) - iph_offset0 = vnet_buffer (b[0])->ip.save_rewrite_length; - - ip0 = (ip4_header_t *) ((u8 *) vlib_buffer_get_current (b[0]) + - iph_offset0); - - vnet_feature_next (&arc_next0, b[0]); - vnet_buffer2 (b[0])->nat.arc_next = arc_next0; - - sw_if_index0 = vnet_buffer (b[0])->sw_if_index[VLIB_RX]; - rx_fib_index0 = ip4_fib_table_get_index_for_sw_if_index (sw_if_index0); - - if (is_in2out) - { - ti[0] = sm->worker_in2out_cb (ip0, rx_fib_index0, is_output); - } - else - { - ti[0] = sm->worker_out2in_cb (b[0], ip0, rx_fib_index0, is_output); - } - - if (ti[0] == thread_index) - same_worker++; - else - do_handoff++; - - b += 1; - ti += 1; - n_left_from -= 1; - } - - if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE))) - { - u32 i; - b = bufs; - ti = thread_indices; - - for (i = 0; i < frame->n_vectors; i++) - { - if (b[0]->flags & VLIB_BUFFER_IS_TRACED) - { - nat44_handoff_trace_t *t = - vlib_add_trace (vm, node, b[0], sizeof (*t)); - t->next_worker_index = ti[0]; - t->trace_index = vlib_buffer_get_trace_index (b[0]); - t->in2out = is_in2out; - t->output = is_output; - - b += 1; - ti += 1; - } - else - break; - } - } - - n_enq = vlib_buffer_enqueue_to_thread (vm, fq_index, from, thread_indices, - frame->n_vectors, 1); - - if (n_enq < frame->n_vectors) - { - vlib_node_increment_counter (vm, node->node_index, - NAT44_HANDOFF_ERROR_CONGESTION_DROP, - frame->n_vectors - n_enq); - } - - vlib_node_increment_counter (vm, node->node_index, - NAT44_HANDOFF_ERROR_SAME_WORKER, same_worker); - vlib_node_increment_counter (vm, node->node_index, - NAT44_HANDOFF_ERROR_DO_HANDOFF, do_handoff); - return frame->n_vectors; -} - -VLIB_NODE_FN (snat_in2out_worker_handoff_node) (vlib_main_t * vm, - vlib_node_runtime_t * node, - vlib_frame_t * frame) -{ - return nat44_worker_handoff_fn_inline (vm, node, frame, 0, 1); -} - -/* *INDENT-OFF* */ -VLIB_REGISTER_NODE (snat_in2out_worker_handoff_node) = { - .name = "nat44-in2out-worker-handoff", - .vector_size = sizeof (u32), - .sibling_of = "nat-default", - .format_trace = format_nat44_handoff_trace, - .type = VLIB_NODE_TYPE_INTERNAL, - .n_errors = ARRAY_LEN(nat44_handoff_error_strings), - .error_strings = nat44_handoff_error_strings, -}; -/* *INDENT-ON* */ - -VLIB_NODE_FN (snat_in2out_output_worker_handoff_node) (vlib_main_t * vm, - vlib_node_runtime_t * - node, - vlib_frame_t * frame) -{ - return nat44_worker_handoff_fn_inline (vm, node, frame, 1, 1); -} - -/* *INDENT-OFF* */ -VLIB_REGISTER_NODE (snat_in2out_output_worker_handoff_node) = { - .name = "nat44-in2out-output-worker-handoff", - .vector_size = sizeof (u32), - .sibling_of = "nat-default", - .format_trace = format_nat44_handoff_trace, - .type = VLIB_NODE_TYPE_INTERNAL, - .n_errors = ARRAY_LEN(nat44_handoff_error_strings), - .error_strings = nat44_handoff_error_strings, -}; -/* *INDENT-ON* */ - -VLIB_NODE_FN (snat_out2in_worker_handoff_node) (vlib_main_t * vm, - vlib_node_runtime_t * node, - vlib_frame_t * frame) -{ - return nat44_worker_handoff_fn_inline (vm, node, frame, 0, 0); -} - -/* *INDENT-OFF* */ -VLIB_REGISTER_NODE (snat_out2in_worker_handoff_node) = { - .name = "nat44-out2in-worker-handoff", - .vector_size = sizeof (u32), - .sibling_of = "nat-default", - .format_trace = format_nat44_handoff_trace, - .type = VLIB_NODE_TYPE_INTERNAL, - .n_errors = ARRAY_LEN(nat44_handoff_error_strings), - .error_strings = nat44_handoff_error_strings, -}; -/* *INDENT-ON* */ - -/* - * fd.io coding-style-patch-verification: ON - * - * Local Variables: - * eval: (c-set-style "gnu") - * End: - */ diff --git a/src/plugins/nat/nat64/nat64.api b/src/plugins/nat/nat64/nat64.api index 5fc4129d041..21d35923148 100644 --- a/src/plugins/nat/nat64/nat64.api +++ b/src/plugins/nat/nat64/nat64.api @@ -16,7 +16,7 @@ option version = "1.0.0"; import "vnet/ip/ip_types.api"; import "vnet/interface_types.api"; -import "plugins/nat/nat_types.api"; +import "plugins/nat/lib/nat_types.api"; /** * @file nat64.api diff --git a/src/plugins/nat/nat66/nat66.api b/src/plugins/nat/nat66/nat66.api index 6906e41f539..bbadf07184a 100644 --- a/src/plugins/nat/nat66/nat66.api +++ b/src/plugins/nat/nat66/nat66.api @@ -16,7 +16,7 @@ option version = "1.0.0"; import "vnet/ip/ip_types.api"; import "vnet/interface_types.api"; -import "plugins/nat/nat_types.api"; +import "plugins/nat/lib/nat_types.api"; /** \brief Enable/disable NAT66 feature on the interface @param client_index - opaque cookie to identify the sender diff --git a/src/plugins/nat/nat_affinity.c b/src/plugins/nat/nat_affinity.c deleted file mode 100644 index e7a7354c18d..00000000000 --- a/src/plugins/nat/nat_affinity.c +++ /dev/null @@ -1,282 +0,0 @@ -/* - * Copyright (c) 2018 Cisco and/or its affiliates. - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/** - * @file - * @brief NAT plugin client-IP based session affinity for load-balancing - */ - -#include -#include - -nat_affinity_main_t nat_affinity_main; - -#define AFFINITY_HASH_BUCKETS 65536 -#define AFFINITY_HASH_MEMORY (2 << 25) - -u8 * -format_affinity_kvp (u8 * s, va_list * args) -{ - clib_bihash_kv_16_8_t *v = va_arg (*args, clib_bihash_kv_16_8_t *); - nat_affinity_key_t k; - - k.as_u64[0] = v->key[0]; - k.as_u64[1] = v->key[1]; - - s = format (s, "client %U backend %U:%d proto %U index %llu", - format_ip4_address, &k.client_addr, - format_ip4_address, &k.service_addr, - clib_net_to_host_u16 (k.service_port), - format_nat_protocol, k.proto); - - return s; -} - -void -nat_affinity_enable () -{ - nat_affinity_main_t *nam = &nat_affinity_main; - vlib_thread_main_t *tm = vlib_get_thread_main (); - - if (tm->n_vlib_mains > 1) - clib_spinlock_init (&nam->affinity_lock); - clib_bihash_init_16_8 (&nam->affinity_hash, "nat-affinity", - AFFINITY_HASH_BUCKETS, AFFINITY_HASH_MEMORY); - clib_bihash_set_kvp_format_fn_16_8 (&nam->affinity_hash, - format_affinity_kvp); -} - -void -nat_affinity_disable () -{ - nat_affinity_main_t *nam = &nat_affinity_main; - vlib_thread_main_t *tm = vlib_get_thread_main (); - - if (tm->n_vlib_mains > 1) - clib_spinlock_free (&nam->affinity_lock); - clib_bihash_free_16_8 (&nam->affinity_hash); -} - -clib_error_t * -nat_affinity_init (vlib_main_t * vm) -{ - nat_affinity_main_t *nam = &nat_affinity_main; - nam->vlib_main = vm; - return 0; -} - -static_always_inline void -make_affinity_kv (clib_bihash_kv_16_8_t * kv, ip4_address_t client_addr, - ip4_address_t service_addr, u8 proto, u16 service_port) -{ - nat_affinity_key_t *key = (nat_affinity_key_t *) kv->key; - - key->client_addr = client_addr; - key->service_addr = service_addr; - key->proto = proto; - key->service_port = service_port; - - kv->value = ~0ULL; -} - -u32 -nat_affinity_get_per_service_list_head_index (void) -{ - nat_affinity_main_t *nam = &nat_affinity_main; - dlist_elt_t *head_elt; - - clib_spinlock_lock_if_init (&nam->affinity_lock); - - pool_get (nam->list_pool, head_elt); - clib_dlist_init (nam->list_pool, head_elt - nam->list_pool); - - clib_spinlock_unlock_if_init (&nam->affinity_lock); - - return head_elt - nam->list_pool; -} - -void -nat_affinity_flush_service (u32 affinity_per_service_list_head_index) -{ - nat_affinity_main_t *nam = &nat_affinity_main; - u32 elt_index; - dlist_elt_t *elt; - nat_affinity_t *a; - clib_bihash_kv_16_8_t kv; - - clib_spinlock_lock_if_init (&nam->affinity_lock); - - while ((elt_index = - clib_dlist_remove_head (nam->list_pool, - affinity_per_service_list_head_index)) != - ~0) - { - elt = pool_elt_at_index (nam->list_pool, elt_index); - a = pool_elt_at_index (nam->affinity_pool, elt->value); - kv.key[0] = a->key.as_u64[0]; - kv.key[1] = a->key.as_u64[1]; - pool_put_index (nam->affinity_pool, elt->value); - if (clib_bihash_add_del_16_8 (&nam->affinity_hash, &kv, 0)) - nat_elog_warn ("affinity key del failed"); - pool_put_index (nam->list_pool, elt_index); - } - pool_put_index (nam->list_pool, affinity_per_service_list_head_index); - - clib_spinlock_unlock_if_init (&nam->affinity_lock); -} - -int -nat_affinity_find_and_lock (ip4_address_t client_addr, - ip4_address_t service_addr, u8 proto, - u16 service_port, u8 * backend_index) -{ - nat_affinity_main_t *nam = &nat_affinity_main; - clib_bihash_kv_16_8_t kv, value; - nat_affinity_t *a; - int rv = 0; - - make_affinity_kv (&kv, client_addr, service_addr, proto, service_port); - clib_spinlock_lock_if_init (&nam->affinity_lock); - if (clib_bihash_search_16_8 (&nam->affinity_hash, &kv, &value)) - { - rv = 1; - goto unlock; - } - - a = pool_elt_at_index (nam->affinity_pool, value.value); - /* if already expired delete */ - if (a->ref_cnt == 0) - { - if (a->expire < vlib_time_now (nam->vlib_main)) - { - clib_dlist_remove (nam->list_pool, a->per_service_index); - pool_put_index (nam->list_pool, a->per_service_index); - pool_put_index (nam->affinity_pool, value.value); - if (clib_bihash_add_del_16_8 (&nam->affinity_hash, &kv, 0)) - nat_elog_warn ("affinity key del failed"); - rv = 1; - goto unlock; - } - } - a->ref_cnt++; - *backend_index = a->backend_index; - -unlock: - clib_spinlock_unlock_if_init (&nam->affinity_lock); - return rv; -} - -static int -affinity_is_expired_cb (clib_bihash_kv_16_8_t * kv, void *arg) -{ - nat_affinity_main_t *nam = &nat_affinity_main; - nat_affinity_t *a; - - a = pool_elt_at_index (nam->affinity_pool, kv->value); - if (a->ref_cnt == 0) - { - if (a->expire < vlib_time_now (nam->vlib_main)) - { - clib_dlist_remove (nam->list_pool, a->per_service_index); - pool_put_index (nam->list_pool, a->per_service_index); - pool_put_index (nam->affinity_pool, kv->value); - if (clib_bihash_add_del_16_8 (&nam->affinity_hash, kv, 0)) - nat_elog_warn ("affinity key del failed"); - return 1; - } - } - - return 0; -} - -int -nat_affinity_create_and_lock (ip4_address_t client_addr, - ip4_address_t service_addr, u8 proto, - u16 service_port, u8 backend_index, - u32 sticky_time, - u32 affinity_per_service_list_head_index) -{ - nat_affinity_main_t *nam = &nat_affinity_main; - clib_bihash_kv_16_8_t kv, value; - nat_affinity_t *a; - dlist_elt_t *list_elt; - int rv = 0; - - make_affinity_kv (&kv, client_addr, service_addr, proto, service_port); - clib_spinlock_lock_if_init (&nam->affinity_lock); - if (!clib_bihash_search_16_8 (&nam->affinity_hash, &kv, &value)) - { - rv = 1; - nat_elog_notice ("affinity key already exist"); - goto unlock; - } - - pool_get (nam->affinity_pool, a); - kv.value = a - nam->affinity_pool; - rv = - clib_bihash_add_or_overwrite_stale_16_8 (&nam->affinity_hash, &kv, - affinity_is_expired_cb, NULL); - if (rv) - { - nat_elog_notice ("affinity key add failed"); - pool_put (nam->affinity_pool, a); - goto unlock; - } - - pool_get (nam->list_pool, list_elt); - clib_dlist_init (nam->list_pool, list_elt - nam->list_pool); - list_elt->value = a - nam->affinity_pool; - a->per_service_index = list_elt - nam->list_pool; - a->backend_index = backend_index; - a->ref_cnt = 1; - a->sticky_time = sticky_time; - a->key.as_u64[0] = kv.key[0]; - a->key.as_u64[1] = kv.key[1]; - clib_dlist_addtail (nam->list_pool, affinity_per_service_list_head_index, - a->per_service_index); - -unlock: - clib_spinlock_unlock_if_init (&nam->affinity_lock); - return rv; -} - -void -nat_affinity_unlock (ip4_address_t client_addr, ip4_address_t service_addr, - u8 proto, u16 service_port) -{ - nat_affinity_main_t *nam = &nat_affinity_main; - clib_bihash_kv_16_8_t kv, value; - nat_affinity_t *a; - - make_affinity_kv (&kv, client_addr, service_addr, proto, service_port); - clib_spinlock_lock_if_init (&nam->affinity_lock); - if (clib_bihash_search_16_8 (&nam->affinity_hash, &kv, &value)) - goto unlock; - - a = pool_elt_at_index (nam->affinity_pool, value.value); - a->ref_cnt--; - if (a->ref_cnt == 0) - a->expire = (u64) a->sticky_time + vlib_time_now (nam->vlib_main); - -unlock: - clib_spinlock_unlock_if_init (&nam->affinity_lock); -} - -/* - * fd.io coding-style-patch-verification: ON - * - * Local Variables: - * eval: (c-set-style "gnu") - * End: - */ diff --git a/src/plugins/nat/nat_affinity.h b/src/plugins/nat/nat_affinity.h deleted file mode 100644 index d6bda6a8ee1..00000000000 --- a/src/plugins/nat/nat_affinity.h +++ /dev/null @@ -1,152 +0,0 @@ -/* - * Copyright (c) 2018 Cisco and/or its affiliates. - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/** - * @file - * @brief NAT plugin client-IP based session affinity for load-balancing - */ - -#ifndef __included_nat_affinity_h__ -#define __included_nat_affinity_h__ - -#include -#include -#include - -typedef struct -{ - union - { - struct - { - ip4_address_t service_addr; - ip4_address_t client_addr; - /* align by making this 4 octets even though its a 1 octet field */ - u32 proto; - /* align by making this 4 octets even though its a 2 octets field */ - u32 service_port; - }; - u64 as_u64[2]; - }; -} nat_affinity_key_t; - -/* *INDENT-OFF* */ -typedef CLIB_PACKED(struct -{ - nat_affinity_key_t key; - u32 sticky_time; - u32 ref_cnt; - u32 per_service_index; - u8 backend_index; - f64 expire; -}) nat_affinity_t; -/* *INDENT-ON* */ - -typedef struct -{ - nat_affinity_t *affinity_pool; - clib_bihash_16_8_t affinity_hash; - clib_spinlock_t affinity_lock; - dlist_elt_t *list_pool; - vlib_main_t *vlib_main; -} nat_affinity_main_t; - -extern nat_affinity_main_t nat_affinity_main; - -/** - * @brief Get new affinity per service list head index. - * - * @returns new affinity per service list head index. - */ -u32 nat_affinity_get_per_service_list_head_index (void); - -/** - * @brief Flush all service affinity data. - * - * @param affinity_per_service_list_head_index Per sevice list head index. - */ -void nat_affinity_flush_service (u32 affinity_per_service_list_head_index); - -/** - * @brief NAT affinity enable - */ -void nat_affinity_enable (); - -/** - * @brief NAT affinity disable - */ -void nat_affinity_disable (); - -/** - * @brief Initialize NAT client-IP based affinity. - * - * @param vm vlib main. - * - * @return error code. - */ -clib_error_t *nat_affinity_init (vlib_main_t * vm); - -/** - * @brief Find service backend index for client-IP and take a reference - * counting lock. - * - * @param client_addr Client IP address. - * @param service_addr Service IP address. - * @param proto IP protocol number. - * @param service_port Service L4 port number. - * @param backend_index Service backend index for client-IP if found. - * - * @return 0 on success, non-zero value otherwise. - */ -int nat_affinity_find_and_lock (ip4_address_t client_addr, - ip4_address_t service_addr, u8 proto, - u16 service_port, u8 * backend_index); - -/** - * @brief Create affinity record and take reference counting lock. - * @param client_addr Client IP address. - * @param service_addr Service IP address. - * @param proto IP protocol number. - * @param service_port Service L4 port number. - * @param backend_index Service backend index for client-IP. - * @param sticky_time Affinity sticky time in seconds. - * @param affinity_per_service_list_head_index Per sevice list head index. - * - * @return 0 on success, non-zero value otherwise. - */ -int nat_affinity_create_and_lock (ip4_address_t client_addr, - ip4_address_t service_addr, u8 proto, - u16 service_port, u8 backend_index, - u32 sticky_time, - u32 affinity_per_service_list_head_index); -/** - * @brief Release a reference counting lock for affinity. - * - * @param client_addr Client IP address. - * @param service_addr Service IP address. - * @param proto IP protocol number. - */ -void nat_affinity_unlock (ip4_address_t client_addr, - ip4_address_t service_addr, u8 proto, - u16 service_port); - -#endif /* __included_nat_affinity_h__ */ - -/* - * fd.io coding-style-patch-verification: ON - * - * Local Variables: - * eval: (c-set-style "gnu") - * End: - */ diff --git a/src/plugins/nat/nat_dpo.c b/src/plugins/nat/nat_dpo.c deleted file mode 100644 index 46ccda8845e..00000000000 --- a/src/plugins/nat/nat_dpo.c +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright (c) 2017 Cisco and/or its affiliates. - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include -#include - -dpo_type_t nat_dpo_type; - -void -nat_dpo_create (dpo_proto_t dproto, u32 aftr_index, dpo_id_t * dpo) -{ - dpo_set (dpo, nat_dpo_type, dproto, aftr_index); -} - -u8 * -format_nat_dpo (u8 * s, va_list * args) -{ - index_t index = va_arg (*args, index_t); - CLIB_UNUSED (u32 indent) = va_arg (*args, u32); - - return (format (s, "NAT44 out2in: AFTR:%d", index)); -} - -static void -nat_dpo_lock (dpo_id_t * dpo) -{ -} - -static void -nat_dpo_unlock (dpo_id_t * dpo) -{ -} - -const static dpo_vft_t nat_dpo_vft = { - .dv_lock = nat_dpo_lock, - .dv_unlock = nat_dpo_unlock, - .dv_format = format_nat_dpo, -}; - -const static char *const nat_ip4_nodes[] = { - "nat44-out2in", - NULL, -}; - -const static char *const nat_ip6_nodes[] = { - NULL, -}; - -const static char *const *const nat_nodes[DPO_PROTO_NUM] = { - [DPO_PROTO_IP4] = nat_ip4_nodes, - [DPO_PROTO_IP6] = nat_ip6_nodes, - [DPO_PROTO_MPLS] = NULL, -}; - -void -nat_dpo_module_init (void) -{ - nat_dpo_type = dpo_register_new_type (&nat_dpo_vft, nat_nodes); -} - -/* - * fd.io coding-style-patch-verification: ON - * - * Local Variables: - * eval: (c-set-style "gnu") - * End: - */ diff --git a/src/plugins/nat/nat_dpo.h b/src/plugins/nat/nat_dpo.h deleted file mode 100644 index e85b3e824f3..00000000000 --- a/src/plugins/nat/nat_dpo.h +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (c) 2017 Cisco and/or its affiliates. - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef __included_nat_dpo_h__ -#define __included_nat_dpo_h__ - -#include -#include - -void nat_dpo_create (dpo_proto_t dproto, u32 aftr_index, dpo_id_t * dpo); - -u8 *format_nat_dpo (u8 * s, va_list * args); - -void nat_dpo_module_init (void); - -#endif /* __included_nat_dpo_h__ */ - -/* - * fd.io coding-style-patch-verification: ON - * - * Local Variables: - * eval: (c-set-style "gnu") - * End: - */ diff --git a/src/plugins/nat/nat_format.c b/src/plugins/nat/nat_format.c deleted file mode 100644 index 2fbd7498f49..00000000000 --- a/src/plugins/nat/nat_format.c +++ /dev/null @@ -1,320 +0,0 @@ -/* - * Copyright (c) 2018 Cisco and/or its affiliates. - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/** - * @file - * @brief NAT formatting - */ - -#include -#include - -uword -unformat_nat_protocol (unformat_input_t * input, va_list * args) -{ - u32 *r = va_arg (*args, u32 *); - - if (0); -#define _(N, i, n, s) else if (unformat (input, s)) *r = NAT_PROTOCOL_##N; - foreach_nat_protocol -#undef _ - else - return 0; - return 1; -} - -u8 * -format_nat_protocol (u8 * s, va_list * args) -{ - u32 i = va_arg (*args, u32); - u8 *t = 0; - - switch (i) - { -#define _(N, j, n, str) case NAT_PROTOCOL_##N: t = (u8 *) str; break; - foreach_nat_protocol -#undef _ - default: - s = format (s, "unknown"); - return s; - } - s = format (s, "%s", t); - return s; -} - -u8 * -format_nat_addr_and_port_alloc_alg (u8 * s, va_list * args) -{ - u32 i = va_arg (*args, u32); - u8 *t = 0; - - switch (i) - { -#define _(v, N, s) case NAT_ADDR_AND_PORT_ALLOC_ALG_##N: t = (u8 *) s; break; - foreach_nat_addr_and_port_alloc_alg -#undef _ - default: - s = format (s, "unknown"); - return s; - } - s = format (s, "%s", t); - return s; -} - -u8 * -format_snat_key (u8 * s, va_list * args) -{ - u64 key = va_arg (*args, u64); - - ip4_address_t addr; - u16 port; - nat_protocol_t protocol; - u32 fib_index; - - split_nat_key (key, &addr, &port, &fib_index, &protocol); - - s = format (s, "%U proto %U port %d fib %d", - format_ip4_address, &addr, - format_nat_protocol, protocol, - clib_net_to_host_u16 (port), fib_index); - return s; -} - -u8 * -format_snat_session_state (u8 * s, va_list * args) -{ - u32 i = va_arg (*args, u32); - u8 *t = 0; - - switch (i) - { -#define _(v, N, str) case SNAT_SESSION_##N: t = (u8 *) str; break; - foreach_snat_session_state -#undef _ - default: - t = format (t, "unknown"); - } - s = format (s, "%s", t); - return s; -} - -u8 * -format_snat_session (u8 * s, va_list * args) -{ - snat_main_per_thread_data_t *tsm = - va_arg (*args, snat_main_per_thread_data_t *); - snat_session_t *sess = va_arg (*args, snat_session_t *); - - if (snat_is_unk_proto_session (sess)) - { - s = format (s, " i2o %U proto %u fib %u\n", - format_ip4_address, &sess->in2out.addr, - sess->in2out.port, sess->in2out.fib_index); - s = - format (s, " o2i %U proto %u fib %u\n", format_ip4_address, - &sess->out2in.addr, sess->out2in.port, sess->out2in.fib_index); - } - else - { - s = format (s, " i2o %U proto %U port %d fib %d\n", - format_ip4_address, &sess->in2out.addr, - format_nat_protocol, sess->nat_proto, - clib_net_to_host_u16 (sess->in2out.port), - sess->in2out.fib_index); - s = format (s, " o2i %U proto %U port %d fib %d\n", - format_ip4_address, &sess->out2in.addr, format_nat_protocol, - sess->nat_proto, clib_net_to_host_u16 (sess->out2in.port), - sess->out2in.fib_index); - } - if (is_ed_session (sess) || is_fwd_bypass_session (sess)) - { - if (is_twice_nat_session (sess)) - { - s = format (s, " external host o2i %U:%d i2o %U:%d\n", - format_ip4_address, &sess->ext_host_addr, - clib_net_to_host_u16 (sess->ext_host_port), - format_ip4_address, &sess->ext_host_nat_addr, - clib_net_to_host_u16 (sess->ext_host_nat_port)); - } - else - { - if (sess->ext_host_addr.as_u32) - s = format (s, " external host %U:%u\n", - format_ip4_address, &sess->ext_host_addr, - clib_net_to_host_u16 (sess->ext_host_port)); - } - s = format (s, " i2o flow: %U\n", format_nat_6t_flow, &sess->i2o); - s = format (s, " o2i flow: %U\n", format_nat_6t_flow, &sess->o2i); - } - s = format (s, " index %llu\n", sess - tsm->sessions); - s = format (s, " last heard %.2f\n", sess->last_heard); - s = format (s, " total pkts %d, total bytes %lld\n", - sess->total_pkts, sess->total_bytes); - if (snat_is_session_static (sess)) - s = format (s, " static translation\n"); - else - s = format (s, " dynamic translation\n"); - if (is_fwd_bypass_session (sess)) - s = format (s, " forwarding-bypass\n"); - if (is_lb_session (sess)) - s = format (s, " load-balancing\n"); - if (is_twice_nat_session (sess)) - s = format (s, " twice-nat\n"); - - return s; -} - -u8 * -format_snat_user (u8 * s, va_list * args) -{ - snat_main_per_thread_data_t *tsm = - va_arg (*args, snat_main_per_thread_data_t *); - snat_user_t *u = va_arg (*args, snat_user_t *); - int verbose = va_arg (*args, int); - dlist_elt_t *head, *elt; - u32 elt_index, head_index; - u32 session_index; - snat_session_t *sess; - - s = format (s, "%U: %d dynamic translations, %d static translations\n", - format_ip4_address, &u->addr, u->nsessions, u->nstaticsessions); - - if (verbose == 0) - return s; - - if (u->nsessions || 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); - session_index = elt->value; - - while (session_index != ~0) - { - sess = pool_elt_at_index (tsm->sessions, session_index); - - s = format (s, " %U\n", format_snat_session, tsm, sess); - - elt_index = elt->next; - elt = pool_elt_at_index (tsm->list_pool, elt_index); - session_index = elt->value; - } - } - - return s; -} - -u8 * -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 %U:%d", - format_nat_protocol, m->proto, - format_ip4_address, &m->local_addr, - clib_net_to_host_u16 (m->local_port)); - - /* *INDENT-OFF* */ - pool_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, - format_ip4_address, &m->external_addr, - m->vrf_id, - m->twice_nat == TWICE_NAT ? "twice-nat" : - m->twice_nat == TWICE_NAT_SELF ? "self-twice-nat" : "", - is_out2in_only_static_mapping (m) ? "out2in-only" : ""); - else - { - if (is_lb_static_mapping (m)) - { - s = format (s, "%U external %U:%d %s %s", - format_nat_protocol, m->proto, - format_ip4_address, &m->external_addr, - clib_net_to_host_u16 (m->external_port), - m->twice_nat == TWICE_NAT ? "twice-nat" : - m->twice_nat == TWICE_NAT_SELF ? "self-twice-nat" : "", - is_out2in_only_static_mapping (m) ? "out2in-only" : ""); - - /* *INDENT-OFF* */ - pool_foreach (local, m->locals) - { - s = format (s, "\n local %U:%d vrf %d probability %d\%", - format_ip4_address, &local->addr, - clib_net_to_host_u16 (local->port), - local->vrf_id, local->probability); - } - /* *INDENT-ON* */ - - } - else - s = format (s, "%U local %U:%d external %U:%d vrf %d %s %s", - format_nat_protocol, m->proto, - format_ip4_address, &m->local_addr, - clib_net_to_host_u16 (m->local_port), - format_ip4_address, &m->external_addr, - clib_net_to_host_u16 (m->external_port), - m->vrf_id, - m->twice_nat == TWICE_NAT ? "twice-nat" : - m->twice_nat == TWICE_NAT_SELF ? "self-twice-nat" : "", - is_out2in_only_static_mapping (m) ? "out2in-only" : ""); - } - return s; -} - -u8 * -format_snat_static_map_to_resolve (u8 * s, va_list * args) -{ - snat_static_map_resolve_t *m = va_arg (*args, snat_static_map_resolve_t *); - vnet_main_t *vnm = vnet_get_main (); - - if (m->addr_only) - s = format (s, "local %U external %U vrf %d", - format_ip4_address, &m->l_addr, - format_vnet_sw_if_index_name, vnm, m->sw_if_index, m->vrf_id); - else - s = format (s, "%U local %U:%d external %U:%d vrf %d", - format_nat_protocol, m->proto, - format_ip4_address, &m->l_addr, - clib_net_to_host_u16 (m->l_port), - format_vnet_sw_if_index_name, vnm, m->sw_if_index, - clib_net_to_host_u16 (m->e_port), m->vrf_id); - - return s; -} - -/* - * fd.io coding-style-patch-verification: ON - * - * Local Variables: - * eval: (c-set-style "gnu") - * End: - */ diff --git a/src/plugins/nat/nat_inlines.h b/src/plugins/nat/nat_inlines.h deleted file mode 100644 index d53e2453607..00000000000 --- a/src/plugins/nat/nat_inlines.h +++ /dev/null @@ -1,686 +0,0 @@ -/* - * Copyright (c) 2018 Cisco and/or its affiliates. - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/** - * @brief The NAT inline functions - */ - -#ifndef __included_nat_inlines_h__ -#define __included_nat_inlines_h__ - -#include -#include -//#include - -always_inline u64 -calc_nat_key (ip4_address_t addr, u16 port, u32 fib_index, u8 proto) -{ - ASSERT (fib_index <= (1 << 14) - 1); - ASSERT (proto <= (1 << 3) - 1); - return (u64) addr.as_u32 << 32 | (u64) port << 16 | fib_index << 3 | - (proto & 0x7); -} - -always_inline void -split_nat_key (u64 key, ip4_address_t * addr, u16 * port, - u32 * fib_index, nat_protocol_t * proto) -{ - if (addr) - { - addr->as_u32 = key >> 32; - } - if (port) - { - *port = (key >> 16) & (u16) ~ 0; - } - if (fib_index) - { - *fib_index = key >> 3 & ((1 << 13) - 1); - } - if (proto) - { - *proto = key & 0x7; - } -} - -always_inline void -init_nat_k (clib_bihash_kv_8_8_t * kv, ip4_address_t addr, u16 port, - u32 fib_index, nat_protocol_t proto) -{ - kv->key = calc_nat_key (addr, port, fib_index, proto); - kv->value = ~0ULL; -} - -always_inline void -init_nat_kv (clib_bihash_kv_8_8_t *kv, ip4_address_t addr, u16 port, - u32 fib_index, nat_protocol_t proto, u32 thread_index, - u32 session_index) -{ - init_nat_k (kv, addr, port, fib_index, proto); - kv->value = (u64) thread_index << 32 | session_index; -} - -always_inline void -init_nat_i2o_k (clib_bihash_kv_8_8_t * kv, snat_session_t * s) -{ - return init_nat_k (kv, s->in2out.addr, s->in2out.port, s->in2out.fib_index, - s->nat_proto); -} - -always_inline void -init_nat_i2o_kv (clib_bihash_kv_8_8_t *kv, snat_session_t *s, u32 thread_index, - u32 session_index) -{ - init_nat_k (kv, s->in2out.addr, s->in2out.port, s->in2out.fib_index, - s->nat_proto); - kv->value = (u64) thread_index << 32 | session_index; -} - -always_inline void -init_nat_o2i_k (clib_bihash_kv_8_8_t * kv, snat_session_t * s) -{ - return init_nat_k (kv, s->out2in.addr, s->out2in.port, s->out2in.fib_index, - s->nat_proto); -} - -always_inline void -init_nat_o2i_kv (clib_bihash_kv_8_8_t *kv, snat_session_t *s, u32 thread_index, - u32 session_index) -{ - init_nat_k (kv, s->out2in.addr, s->out2in.port, s->out2in.fib_index, - s->nat_proto); - kv->value = (u64) thread_index << 32 | session_index; -} - -always_inline u32 -nat_value_get_thread_index (clib_bihash_kv_8_8_t *value) -{ - return value->value >> 32; -} - -always_inline u32 -nat_value_get_session_index (clib_bihash_kv_8_8_t *value) -{ - return value->value & ~(u32) 0; -} - -static inline uword -nat_pre_node_fn_inline (vlib_main_t * vm, - vlib_node_runtime_t * node, - vlib_frame_t * frame, u32 def_next) -{ - u32 n_left_from, *from; - - from = vlib_frame_vector_args (frame); - n_left_from = frame->n_vectors; - - vlib_buffer_t *bufs[VLIB_FRAME_SIZE], **b = bufs; - u16 nexts[VLIB_FRAME_SIZE], *next = nexts; - vlib_get_buffers (vm, from, b, n_left_from); - - while (n_left_from >= 2) - { - u32 next0, next1; - u32 arc_next0, arc_next1; - vlib_buffer_t *b0, *b1; - - b0 = *b; - b++; - b1 = *b; - b++; - - /* Prefetch next iteration. */ - if (PREDICT_TRUE (n_left_from >= 4)) - { - vlib_buffer_t *p2, *p3; - - p2 = *b; - p3 = *(b + 1); - - vlib_prefetch_buffer_header (p2, LOAD); - vlib_prefetch_buffer_header (p3, LOAD); - - CLIB_PREFETCH (p2->data, CLIB_CACHE_LINE_BYTES, LOAD); - CLIB_PREFETCH (p3->data, CLIB_CACHE_LINE_BYTES, LOAD); - } - - next0 = def_next; - next1 = def_next; - - vnet_feature_next (&arc_next0, b0); - vnet_feature_next (&arc_next1, b1); - - vnet_buffer2 (b0)->nat.arc_next = arc_next0; - vnet_buffer2 (b1)->nat.arc_next = arc_next1; - - if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE))) - { - if (b0->flags & VLIB_BUFFER_IS_TRACED) - { - nat_pre_trace_t *t = vlib_add_trace (vm, node, b0, sizeof (*t)); - t->next_index = next0; - t->arc_next_index = arc_next0; - } - if (b1->flags & VLIB_BUFFER_IS_TRACED) - { - nat_pre_trace_t *t = vlib_add_trace (vm, node, b0, sizeof (*t)); - t->next_index = next1; - t->arc_next_index = arc_next1; - } - } - - n_left_from -= 2; - next[0] = next0; - next[1] = next1; - next += 2; - } - - while (n_left_from > 0) - { - u32 next0; - u32 arc_next0; - vlib_buffer_t *b0; - - b0 = *b; - b++; - - next0 = def_next; - vnet_feature_next (&arc_next0, b0); - vnet_buffer2 (b0)->nat.arc_next = arc_next0; - - if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE) - && (b0->flags & VLIB_BUFFER_IS_TRACED))) - { - nat_pre_trace_t *t = vlib_add_trace (vm, node, b0, sizeof (*t)); - t->next_index = next0; - t->arc_next_index = arc_next0; - } - - n_left_from--; - next[0] = next0; - next++; - } - vlib_buffer_enqueue_to_next (vm, node, from, (u16 *) nexts, - frame->n_vectors); - - return frame->n_vectors; -} - -always_inline u8 -is_interface_addr (snat_main_t * sm, vlib_node_runtime_t * node, - u32 sw_if_index0, u32 ip4_addr) -{ - snat_runtime_t *rt = (snat_runtime_t *) node->runtime_data; - ip4_address_t *first_int_addr; - - if (PREDICT_FALSE (rt->cached_sw_if_index != sw_if_index0)) - { - first_int_addr = - ip4_interface_first_address (sm->ip4_main, sw_if_index0, - 0 /* just want the address */ ); - rt->cached_sw_if_index = sw_if_index0; - if (first_int_addr) - rt->cached_ip4_address = first_int_addr->as_u32; - else - rt->cached_ip4_address = 0; - } - - if (PREDICT_FALSE (ip4_addr == rt->cached_ip4_address)) - return 1; - else - return 0; -} - -always_inline void -user_session_increment (snat_main_t * sm, snat_user_t * u, u8 is_static) -{ - if (u->nsessions + u->nstaticsessions < sm->max_translations_per_user) - { - if (is_static) - u->nstaticsessions++; - else - u->nsessions++; - } -} - -always_inline void -nat44_delete_user_with_no_session (snat_main_t * sm, snat_user_t * u, - u32 thread_index) -{ - clib_bihash_kv_8_8_t kv; - snat_user_key_t u_key; - snat_main_per_thread_data_t *tsm = vec_elt_at_index (sm->per_thread_data, - thread_index); - - if (u->nstaticsessions == 0 && u->nsessions == 0) - { - u_key.addr.as_u32 = u->addr.as_u32; - u_key.fib_index = u->fib_index; - kv.key = u_key.as_u64; - pool_put_index (tsm->list_pool, u->sessions_per_user_list_head_index); - pool_put (tsm->users, u); - clib_bihash_add_del_8_8 (&tsm->user_hash, &kv, 0); - vlib_set_simple_counter (&sm->total_users, thread_index, 0, - pool_elts (tsm->users)); - } -} - -always_inline void -nat44_delete_session (snat_main_t * sm, snat_session_t * ses, - u32 thread_index) -{ - snat_main_per_thread_data_t *tsm = vec_elt_at_index (sm->per_thread_data, - thread_index); - clib_bihash_kv_8_8_t kv, value; - snat_user_t *u; - const snat_user_key_t u_key = { - .addr = ses->in2out.addr, - .fib_index = ses->in2out.fib_index - }; - const u8 u_static = snat_is_session_static (ses); - - clib_dlist_remove (tsm->list_pool, ses->per_user_index); - pool_put_index (tsm->list_pool, ses->per_user_index); - if (sm->endpoint_dependent) - { - clib_dlist_remove (tsm->lru_pool, ses->lru_index); - pool_put_index (tsm->lru_pool, ses->lru_index); - } - pool_put (tsm->sessions, ses); - vlib_set_simple_counter (&sm->total_sessions, thread_index, 0, - pool_elts (tsm->sessions)); - - 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_static) - u->nstaticsessions--; - else - u->nsessions--; - - nat44_delete_user_with_no_session (sm, u, thread_index); - } -} - -always_inline void -nat44_set_tcp_session_state_i2o (snat_main_t * sm, f64 now, - snat_session_t * ses, vlib_buffer_t * b, - u32 thread_index) -{ - snat_main_per_thread_data_t *tsm = &sm->per_thread_data[thread_index]; - u8 tcp_flags = vnet_buffer (b)->ip.reass.icmp_type_or_tcp_flags; - u32 tcp_ack_number = vnet_buffer (b)->ip.reass.tcp_ack_number; - u32 tcp_seq_number = vnet_buffer (b)->ip.reass.tcp_seq_number; - if ((ses->state == 0) && (tcp_flags & TCP_FLAG_RST)) - ses->state = NAT44_SES_RST; - if ((ses->state == NAT44_SES_RST) && !(tcp_flags & TCP_FLAG_RST)) - ses->state = 0; - if ((tcp_flags & TCP_FLAG_ACK) && (ses->state & NAT44_SES_I2O_SYN) && - (ses->state & NAT44_SES_O2I_SYN)) - ses->state = 0; - if (tcp_flags & TCP_FLAG_SYN) - ses->state |= NAT44_SES_I2O_SYN; - if (tcp_flags & TCP_FLAG_FIN) - { - ses->i2o_fin_seq = clib_net_to_host_u32 (tcp_seq_number); - ses->state |= NAT44_SES_I2O_FIN; - } - if ((tcp_flags & TCP_FLAG_ACK) && (ses->state & NAT44_SES_O2I_FIN)) - { - if (clib_net_to_host_u32 (tcp_ack_number) > ses->o2i_fin_seq) - { - ses->state |= NAT44_SES_O2I_FIN_ACK; - if (nat44_is_ses_closed (ses)) - { // if session is now closed, save the timestamp - ses->tcp_closed_timestamp = now + sm->timeouts.tcp.transitory; - ses->last_lru_update = now; - } - } - } - - // move the session to proper LRU - if (ses->state) - { - ses->lru_head_index = tsm->tcp_trans_lru_head_index; - } - else - { - ses->lru_head_index = tsm->tcp_estab_lru_head_index; - } - clib_dlist_remove (tsm->lru_pool, ses->lru_index); - clib_dlist_addtail (tsm->lru_pool, ses->lru_head_index, ses->lru_index); -} - -always_inline void -nat44_set_tcp_session_state_o2i (snat_main_t * sm, f64 now, - snat_session_t * ses, u8 tcp_flags, - u32 tcp_ack_number, u32 tcp_seq_number, - u32 thread_index) -{ - snat_main_per_thread_data_t *tsm = &sm->per_thread_data[thread_index]; - if ((ses->state == 0) && (tcp_flags & TCP_FLAG_RST)) - ses->state = NAT44_SES_RST; - if ((ses->state == NAT44_SES_RST) && !(tcp_flags & TCP_FLAG_RST)) - ses->state = 0; - if ((tcp_flags & TCP_FLAG_ACK) && (ses->state & NAT44_SES_I2O_SYN) && - (ses->state & NAT44_SES_O2I_SYN)) - ses->state = 0; - if (tcp_flags & TCP_FLAG_SYN) - ses->state |= NAT44_SES_O2I_SYN; - if (tcp_flags & TCP_FLAG_FIN) - { - ses->o2i_fin_seq = clib_net_to_host_u32 (tcp_seq_number); - ses->state |= NAT44_SES_O2I_FIN; - } - if ((tcp_flags & TCP_FLAG_ACK) && (ses->state & NAT44_SES_I2O_FIN)) - { - if (clib_net_to_host_u32 (tcp_ack_number) > ses->i2o_fin_seq) - ses->state |= NAT44_SES_I2O_FIN_ACK; - if (nat44_is_ses_closed (ses)) - { // if session is now closed, save the timestamp - ses->tcp_closed_timestamp = now + sm->timeouts.tcp.transitory; - ses->last_lru_update = now; - } - } - // move the session to proper LRU - if (ses->state) - { - ses->lru_head_index = tsm->tcp_trans_lru_head_index; - } - else - { - ses->lru_head_index = tsm->tcp_estab_lru_head_index; - } - clib_dlist_remove (tsm->lru_pool, ses->lru_index); - clib_dlist_addtail (tsm->lru_pool, ses->lru_head_index, ses->lru_index); -} - -always_inline u32 -nat44_session_get_timeout (snat_main_t * sm, snat_session_t * s) -{ - switch (s->nat_proto) - { - case NAT_PROTOCOL_ICMP: - return sm->timeouts.icmp; - case NAT_PROTOCOL_UDP: - return sm->timeouts.udp; - case NAT_PROTOCOL_TCP: - { - if (s->state) - return sm->timeouts.tcp.transitory; - else - return sm->timeouts.tcp.established; - } - default: - return sm->timeouts.udp; - } - - return 0; -} - -always_inline void -nat44_session_update_counters (snat_session_t * s, f64 now, uword bytes, - u32 thread_index) -{ - s->last_heard = now; - s->total_pkts++; - s->total_bytes += bytes; -#if 0 - nat_ha_sref (&s->out2in.addr, s->out2in.port, &s->ext_host_addr, - s->ext_host_port, s->nat_proto, s->out2in.fib_index, - s->total_pkts, s->total_bytes, thread_index, - &s->ha_last_refreshed, now); -#endif -} - -/** \brief Per-user LRU list maintenance */ -always_inline void -nat44_session_update_lru (snat_main_t * sm, snat_session_t * s, - u32 thread_index) -{ - /* don't update too often - timeout is in magnitude of seconds anyway */ - if (s->last_heard > s->last_lru_update + 1) - { - if (!sm->endpoint_dependent) - { - clib_dlist_remove (sm->per_thread_data[thread_index].list_pool, - s->per_user_index); - clib_dlist_addtail (sm->per_thread_data[thread_index].list_pool, - s->per_user_list_head_index, s->per_user_index); - } - else - { - clib_dlist_remove (sm->per_thread_data[thread_index].lru_pool, - s->lru_index); - clib_dlist_addtail (sm->per_thread_data[thread_index].lru_pool, - s->lru_head_index, s->lru_index); - } - s->last_lru_update = s->last_heard; - } -} - -always_inline void -init_ed_k (clib_bihash_kv_16_8_t * kv, ip4_address_t l_addr, u16 l_port, - ip4_address_t r_addr, u16 r_port, u32 fib_index, u8 proto) -{ - kv->key[0] = (u64) r_addr.as_u32 << 32 | l_addr.as_u32; - kv->key[1] = - (u64) r_port << 48 | (u64) l_port << 32 | fib_index << 8 | proto; -} - -always_inline void -init_ed_kv (clib_bihash_kv_16_8_t * kv, ip4_address_t l_addr, u16 l_port, - ip4_address_t r_addr, u16 r_port, u32 fib_index, u8 proto, - u32 thread_index, u32 session_index) -{ - init_ed_k (kv, l_addr, l_port, r_addr, r_port, fib_index, proto); - kv->value = (u64) thread_index << 32 | session_index; -} - -always_inline u32 -ed_value_get_thread_index (clib_bihash_kv_16_8_t * value) -{ - return value->value >> 32; -} - -always_inline u32 -ed_value_get_session_index (clib_bihash_kv_16_8_t * value) -{ - return value->value & ~(u32) 0; -} - -always_inline void -split_ed_kv (clib_bihash_kv_16_8_t * kv, - ip4_address_t * l_addr, ip4_address_t * r_addr, u8 * proto, - u32 * fib_index, u16 * l_port, u16 * r_port) -{ - if (l_addr) - { - l_addr->as_u32 = kv->key[0] & (u32) ~ 0; - } - if (r_addr) - { - r_addr->as_u32 = kv->key[0] >> 32; - } - if (r_port) - { - *r_port = kv->key[1] >> 48; - } - if (l_port) - { - *l_port = (kv->key[1] >> 32) & (u16) ~ 0; - } - if (fib_index) - { - *fib_index = (kv->key[1] >> 8) & ((1 << 24) - 1); - } - if (proto) - { - *proto = kv->key[1] & (u8) ~ 0; - } -} - -static_always_inline int -nat_get_icmp_session_lookup_values (vlib_buffer_t *b, ip4_header_t *ip0, - ip4_address_t *lookup_saddr, - u16 *lookup_sport, - ip4_address_t *lookup_daddr, - u16 *lookup_dport, u8 *lookup_protocol) -{ - icmp46_header_t *icmp0; - icmp_echo_header_t *echo0, *inner_echo0 = 0; - ip4_header_t *inner_ip0 = 0; - void *l4_header = 0; - icmp46_header_t *inner_icmp0; - - icmp0 = (icmp46_header_t *) ip4_next_header (ip0); - echo0 = (icmp_echo_header_t *) (icmp0 + 1); - - // avoid warning about unused variables in caller by setting to bogus values - *lookup_sport = 0; - *lookup_dport = 0; - - if (!icmp_type_is_error_message - (vnet_buffer (b)->ip.reass.icmp_type_or_tcp_flags)) - { - *lookup_protocol = IP_PROTOCOL_ICMP; - lookup_saddr->as_u32 = ip0->src_address.as_u32; - *lookup_sport = vnet_buffer (b)->ip.reass.l4_src_port; - lookup_daddr->as_u32 = ip0->dst_address.as_u32; - *lookup_dport = vnet_buffer (b)->ip.reass.l4_dst_port; - } - else - { - inner_ip0 = (ip4_header_t *) (echo0 + 1); - l4_header = ip4_next_header (inner_ip0); - *lookup_protocol = inner_ip0->protocol; - lookup_saddr->as_u32 = inner_ip0->dst_address.as_u32; - lookup_daddr->as_u32 = inner_ip0->src_address.as_u32; - switch (ip_proto_to_nat_proto (inner_ip0->protocol)) - { - case NAT_PROTOCOL_ICMP: - inner_icmp0 = (icmp46_header_t *) l4_header; - inner_echo0 = (icmp_echo_header_t *) (inner_icmp0 + 1); - *lookup_sport = inner_echo0->identifier; - *lookup_dport = inner_echo0->identifier; - break; - case NAT_PROTOCOL_UDP: - case NAT_PROTOCOL_TCP: - *lookup_sport = ((tcp_udp_header_t *) l4_header)->dst_port; - *lookup_dport = ((tcp_udp_header_t *) l4_header)->src_port; - break; - default: - return NAT_IN2OUT_ED_ERROR_UNSUPPORTED_PROTOCOL; - } - } - return 0; -} - -/** - * @brief Check if packet should be translated - * - * Packets aimed at outside interface and external address with active session - * should be translated. - * - * @param sm NAT main - * @param rt NAT runtime data - * @param sw_if_index0 index of the inside interface - * @param ip0 IPv4 header - * @param proto0 NAT protocol - * @param rx_fib_index0 RX FIB index - * - * @returns 0 if packet should be translated otherwise 1 - */ -static inline int -snat_not_translate_fast (snat_main_t * sm, vlib_node_runtime_t * node, - u32 sw_if_index0, ip4_header_t * ip0, u32 proto0, - u32 rx_fib_index0) -{ - if (sm->out2in_dpo) - return 0; - - fib_node_index_t fei = FIB_NODE_INDEX_INVALID; - nat_outside_fib_t *outside_fib; - fib_prefix_t pfx = { - .fp_proto = FIB_PROTOCOL_IP4, - .fp_len = 32, - .fp_addr = { - .ip4.as_u32 = ip0->dst_address.as_u32, - } - , - }; - - /* Don't NAT packet aimed at the intfc address */ - if (PREDICT_FALSE (is_interface_addr (sm, node, sw_if_index0, - ip0->dst_address.as_u32))) - return 1; - - fei = fib_table_lookup (rx_fib_index0, &pfx); - if (FIB_NODE_INDEX_INVALID != fei) - { - u32 sw_if_index = fib_entry_get_resolving_interface (fei); - if (sw_if_index == ~0) - { - vec_foreach (outside_fib, sm->outside_fibs) - { - fei = fib_table_lookup (outside_fib->fib_index, &pfx); - if (FIB_NODE_INDEX_INVALID != fei) - { - sw_if_index = fib_entry_get_resolving_interface (fei); - if (sw_if_index != ~0) - break; - } - } - } - if (sw_if_index == ~0) - return 1; - - snat_interface_t *i; - /* *INDENT-OFF* */ - pool_foreach (i, sm->interfaces) { - /* NAT packet aimed at outside interface */ - if ((nat_interface_is_outside (i)) && (sw_if_index == i->sw_if_index)) - return 0; - } - /* *INDENT-ON* */ - } - - return 1; -} - -static_always_inline u16 -snat_random_port (u16 min, u16 max) -{ - snat_main_t *sm = &snat_main; - u32 rwide; - u16 r; - - rwide = random_u32 (&sm->random_seed); - r = rwide & 0xFFFF; - if (r >= min && r <= max) - return r; - - return min + (rwide % (max - min + 1)); -} - -#endif /* __included_nat_inlines_h__ */ - -/* - * fd.io coding-style-patch-verification: ON - * - * Local Variables: - * eval: (c-set-style "gnu") - * End: - */ diff --git a/src/plugins/nat/nat_types.api b/src/plugins/nat/nat_types.api deleted file mode 100644 index ad4ee9faba0..00000000000 --- a/src/plugins/nat/nat_types.api +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (c) 2020 Cisco and/or its affiliates. - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -option version = "0.0.1"; - -typedef nat_timeouts -{ - u32 udp; - u32 tcp_established; - u32 tcp_transitory; - u32 icmp; -}; - -enum nat_log_level : u8 -{ - NAT_LOG_NONE = 0x00, - NAT_LOG_ERROR = 0x01, - NAT_LOG_WARNING = 0x02, - NAT_LOG_NOTICE = 0x03, - NAT_LOG_INFO = 0x04, - NAT_LOG_DEBUG = 0x05, -}; - -enum nat_config_flags : u8 -{ - NAT_IS_NONE = 0x00, - NAT_IS_TWICE_NAT = 0x01, - NAT_IS_SELF_TWICE_NAT = 0x02, - NAT_IS_OUT2IN_ONLY = 0x04, - NAT_IS_ADDR_ONLY = 0x08, - NAT_IS_OUTSIDE = 0x10, - NAT_IS_INSIDE = 0x20, - NAT_IS_STATIC = 0x40, - NAT_IS_EXT_HOST_VALID = 0x80, -}; diff --git a/src/plugins/nat/out2in_ed.c b/src/plugins/nat/out2in_ed.c deleted file mode 100644 index d6beadc61bc..00000000000 --- a/src/plugins/nat/out2in_ed.c +++ /dev/null @@ -1,1448 +0,0 @@ -/* - * Copyright (c) 2018 Cisco and/or its affiliates. - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/** - * @file - * @brief NAT44 endpoint-dependent outside to inside network translation - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static char *nat_out2in_ed_error_strings[] = { -#define _(sym,string) string, - foreach_nat_out2in_ed_error -#undef _ -}; - -typedef struct -{ - u32 sw_if_index; - u32 next_index; - u32 session_index; - nat_translation_error_e translation_error; - nat_6t_flow_t i2of; - nat_6t_flow_t o2if; - clib_bihash_kv_16_8_t search_key; - u8 is_slow_path; - u8 translation_via_i2of; - u8 lookup_skipped; -} nat44_ed_out2in_trace_t; - -static u8 * -format_nat44_ed_out2in_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 *); - nat44_ed_out2in_trace_t *t = va_arg (*args, nat44_ed_out2in_trace_t *); - char *tag; - - tag = - t->is_slow_path ? "NAT44_OUT2IN_ED_SLOW_PATH" : - "NAT44_OUT2IN_ED_FAST_PATH"; - - s = format (s, "%s: sw_if_index %d, next index %d, session %d", tag, - t->sw_if_index, t->next_index, t->session_index); - if (~0 != t->session_index) - { - s = format (s, ", translation result '%U' via %s", - format_nat_ed_translation_error, t->translation_error, - t->translation_via_i2of ? "i2of" : "o2if"); - s = format (s, "\n i2of %U", format_nat_6t_flow, &t->i2of); - s = format (s, "\n o2if %U", format_nat_6t_flow, &t->o2if); - } - if (!t->is_slow_path) - { - if (t->lookup_skipped) - { - s = format (s, "\n lookup skipped - cached session index used"); - } - else - { - s = format (s, "\n search key %U", format_ed_session_kvp, - &t->search_key); - } - } - - return s; -} - -static int -next_src_nat (snat_main_t *sm, ip4_header_t *ip, u16 src_port, u16 dst_port, - u32 thread_index, u32 rx_fib_index) -{ - clib_bihash_kv_16_8_t kv, value; - - init_ed_k (&kv, ip->src_address, src_port, ip->dst_address, dst_port, - rx_fib_index, ip->protocol); - if (!clib_bihash_search_16_8 (&sm->flow_hash, &kv, &value)) - return 1; - - return 0; -} - -static void create_bypass_for_fwd (snat_main_t *sm, vlib_buffer_t *b, - snat_session_t *s, ip4_header_t *ip, - u32 rx_fib_index, u32 thread_index); - -static snat_session_t *create_session_for_static_mapping_ed ( - snat_main_t *sm, vlib_buffer_t *b, ip4_address_t i2o_addr, u16 i2o_port, - u32 i2o_fib_index, ip4_address_t o2i_addr, u16 o2i_port, u32 o2i_fib_index, - nat_protocol_t nat_proto, vlib_node_runtime_t *node, u32 rx_fib_index, - u32 thread_index, twice_nat_type_t twice_nat, lb_nat_type_t lb_nat, f64 now, - snat_static_mapping_t *mapping); - -static inline u32 -icmp_out2in_ed_slow_path (snat_main_t *sm, vlib_buffer_t *b, ip4_header_t *ip, - icmp46_header_t *icmp, u32 sw_if_index, - u32 rx_fib_index, vlib_node_runtime_t *node, - u32 next, f64 now, u32 thread_index, - snat_session_t **s_p) -{ - vlib_main_t *vm = vlib_get_main (); - - ip_csum_t sum; - u16 checksum; - - snat_session_t *s = 0; - u8 is_addr_only, identity_nat; - ip4_address_t sm_addr; - u16 sm_port; - u32 sm_fib_index; - snat_static_mapping_t *m; - u8 lookup_protocol; - ip4_address_t lookup_saddr, lookup_daddr; - u16 lookup_sport, lookup_dport; - - sw_if_index = vnet_buffer (b)->sw_if_index[VLIB_RX]; - rx_fib_index = ip4_fib_table_get_index_for_sw_if_index (sw_if_index); - - if (nat_get_icmp_session_lookup_values (b, ip, &lookup_saddr, &lookup_sport, - &lookup_daddr, &lookup_dport, - &lookup_protocol)) - { - b->error = node->errors[NAT_OUT2IN_ED_ERROR_UNSUPPORTED_PROTOCOL]; - next = NAT_NEXT_DROP; - goto out; - } - - if (snat_static_mapping_match ( - sm, ip->dst_address, lookup_sport, rx_fib_index, - ip_proto_to_nat_proto (ip->protocol), &sm_addr, &sm_port, - &sm_fib_index, 1, &is_addr_only, 0, 0, 0, &identity_nat, &m)) - { - // static mapping not matched - if (!sm->forwarding_enabled) - { - /* Don't NAT packet aimed at the intfc address */ - if (!is_interface_addr (sm, node, sw_if_index, - ip->dst_address.as_u32)) - { - b->error = node->errors[NAT_OUT2IN_ED_ERROR_NO_TRANSLATION]; - next = NAT_NEXT_DROP; - } - } - else - { - if (next_src_nat (sm, ip, lookup_sport, lookup_dport, thread_index, - rx_fib_index)) - { - next = NAT_NEXT_IN2OUT_ED_FAST_PATH; - } - else - { - create_bypass_for_fwd (sm, b, s, ip, rx_fib_index, thread_index); - } - } - goto out; - } - - if (PREDICT_FALSE (vnet_buffer (b)->ip.reass.icmp_type_or_tcp_flags != - ICMP4_echo_reply && - (vnet_buffer (b)->ip.reass.icmp_type_or_tcp_flags != - ICMP4_echo_request || - !is_addr_only))) - { - b->error = node->errors[NAT_OUT2IN_ED_ERROR_BAD_ICMP_TYPE]; - next = NAT_NEXT_DROP; - goto out; - } - - if (PREDICT_FALSE (identity_nat)) - { - goto out; - } - - /* Create session initiated by host from external network */ - s = create_session_for_static_mapping_ed ( - sm, b, sm_addr, sm_port, sm_fib_index, ip->dst_address, lookup_sport, - rx_fib_index, ip_proto_to_nat_proto (lookup_protocol), node, rx_fib_index, - thread_index, 0, 0, vlib_time_now (vm), m); - if (!s) - next = NAT_NEXT_DROP; - - if (PREDICT_TRUE (!ip4_is_fragment (ip))) - { - sum = ip_incremental_checksum_buffer ( - vm, b, (u8 *) icmp - (u8 *) vlib_buffer_get_current (b), - ntohs (ip->length) - ip4_header_bytes (ip), 0); - checksum = ~ip_csum_fold (sum); - if (checksum != 0 && checksum != 0xffff) - { - next = NAT_NEXT_DROP; - goto out; - } - } - - if (PREDICT_TRUE (next != NAT_NEXT_DROP && s)) - { - /* Accounting */ - nat44_session_update_counters ( - s, now, vlib_buffer_length_in_chain (vm, b), thread_index); - /* Per-user LRU list maintenance */ - nat44_session_update_lru (sm, s, thread_index); - } -out: - if (NAT_NEXT_DROP == next && s) - { - nat_ed_session_delete (sm, s, thread_index, 1); - s = 0; - } - *s_p = s; - return next; -} - -// allocate exact address based on preference -static_always_inline int -nat_alloc_addr_and_port_exact (snat_address_t * a, - u32 thread_index, - nat_protocol_t proto, - ip4_address_t * addr, - u16 * port, - u16 port_per_thread, u32 snat_thread_index) -{ - u32 portnum; - - switch (proto) - { -#define _(N, j, n, s) \ - case NAT_PROTOCOL_##N: \ - if (a->busy_##n##_ports_per_thread[thread_index] < port_per_thread) \ - { \ - while (1) \ - { \ - portnum = (port_per_thread * \ - snat_thread_index) + \ - snat_random_port(0, port_per_thread - 1) + 1024; \ - if (a->busy_##n##_port_refcounts[portnum]) \ - continue; \ - --a->busy_##n##_port_refcounts[portnum]; \ - a->busy_##n##_ports_per_thread[thread_index]++; \ - a->busy_##n##_ports++; \ - *addr = a->addr; \ - *port = clib_host_to_net_u16(portnum); \ - return 0; \ - } \ - } \ - break; - foreach_nat_protocol -#undef _ - default: - nat_elog_info ("unknown protocol"); - return 1; - } - - /* Totally out of translations to use... */ - nat_ipfix_logging_addresses_exhausted (thread_index, 0); - return 1; -} - -static_always_inline int -nat44_ed_alloc_outside_addr_and_port (snat_address_t *addresses, u32 fib_index, - u32 thread_index, nat_protocol_t proto, - ip4_address_t *addr, u16 *port, - u16 port_per_thread, - u32 snat_thread_index) -{ - int i; - snat_address_t *a, *ga = 0; - u32 portnum; - - for (i = 0; i < vec_len (addresses); i++) - { - a = addresses + i; - switch (proto) - { -#define _(N, j, n, s) \ - case NAT_PROTOCOL_##N: \ - if (a->busy_##n##_ports_per_thread[thread_index] < port_per_thread) \ - { \ - if (a->fib_index == fib_index) \ - { \ - while (1) \ - { \ - portnum = (port_per_thread * snat_thread_index) + \ - snat_random_port (0, port_per_thread - 1) + 1024; \ - if (a->busy_##n##_port_refcounts[portnum]) \ - continue; \ - --a->busy_##n##_port_refcounts[portnum]; \ - a->busy_##n##_ports_per_thread[thread_index]++; \ - a->busy_##n##_ports++; \ - *addr = a->addr; \ - *port = clib_host_to_net_u16 (portnum); \ - return 0; \ - } \ - } \ - else if (a->fib_index == ~0) \ - { \ - ga = a; \ - } \ - } \ - break; - foreach_nat_protocol -#undef _ - default : nat_elog_info ("unknown protocol"); - return 1; - } - } - - if (ga) - { - a = ga; - switch (proto) - { -#define _(N, j, n, s) \ - case NAT_PROTOCOL_##N: \ - while (1) \ - { \ - portnum = (port_per_thread * snat_thread_index) + \ - snat_random_port (0, port_per_thread - 1) + 1024; \ - if (a->busy_##n##_port_refcounts[portnum]) \ - continue; \ - ++a->busy_##n##_port_refcounts[portnum]; \ - a->busy_##n##_ports_per_thread[thread_index]++; \ - a->busy_##n##_ports++; \ - *addr = a->addr; \ - *port = clib_host_to_net_u16 (portnum); \ - return 0; \ - } - break; - foreach_nat_protocol -#undef _ - default : nat_elog_info ("unknown protocol"); - return 1; - } - } - - /* Totally out of translations to use... */ - nat_ipfix_logging_addresses_exhausted (thread_index, 0); - return 1; -} - -static snat_session_t * -create_session_for_static_mapping_ed ( - snat_main_t *sm, vlib_buffer_t *b, ip4_address_t i2o_addr, u16 i2o_port, - u32 i2o_fib_index, ip4_address_t o2i_addr, u16 o2i_port, u32 o2i_fib_index, - nat_protocol_t nat_proto, vlib_node_runtime_t *node, u32 rx_fib_index, - u32 thread_index, twice_nat_type_t twice_nat, lb_nat_type_t lb_nat, f64 now, - snat_static_mapping_t *mapping) -{ - snat_session_t *s; - ip4_header_t *ip; - udp_header_t *udp; - snat_main_per_thread_data_t *tsm = &sm->per_thread_data[thread_index]; - - if (PREDICT_FALSE - (nat44_ed_maximum_sessions_exceeded (sm, rx_fib_index, thread_index))) - { - b->error = node->errors[NAT_OUT2IN_ED_ERROR_MAX_SESSIONS_EXCEEDED]; - nat_elog_notice ("maximum sessions exceeded"); - return 0; - } - - s = nat_ed_session_alloc (sm, thread_index, now, nat_proto); - if (!s) - { - b->error = node->errors[NAT_OUT2IN_ED_ERROR_MAX_USER_SESS_EXCEEDED]; - nat_elog_warn ("create NAT session failed"); - return 0; - } - - ip = vlib_buffer_get_current (b); - udp = ip4_next_header (ip); - - s->ext_host_addr.as_u32 = ip->src_address.as_u32; - s->ext_host_port = nat_proto == NAT_PROTOCOL_ICMP ? 0 : udp->src_port; - s->flags |= SNAT_SESSION_FLAG_STATIC_MAPPING; - if (lb_nat) - s->flags |= SNAT_SESSION_FLAG_LOAD_BALANCING; - if (lb_nat == AFFINITY_LB_NAT) - s->flags |= SNAT_SESSION_FLAG_AFFINITY; - s->flags |= SNAT_SESSION_FLAG_ENDPOINT_DEPENDENT; - s->out2in.addr = o2i_addr; - s->out2in.port = o2i_port; - s->out2in.fib_index = o2i_fib_index; - s->in2out.addr = i2o_addr; - s->in2out.port = i2o_port; - s->in2out.fib_index = i2o_fib_index; - s->nat_proto = nat_proto; - - if (NAT_PROTOCOL_ICMP == nat_proto) - { - nat_6t_o2i_flow_init (sm, thread_index, s, s->ext_host_addr, o2i_port, - o2i_addr, o2i_port, o2i_fib_index, ip->protocol); - nat_6t_flow_icmp_id_rewrite_set (&s->o2i, i2o_port); - } - else - { - nat_6t_o2i_flow_init (sm, thread_index, s, s->ext_host_addr, - s->ext_host_port, o2i_addr, o2i_port, - o2i_fib_index, ip->protocol); - nat_6t_flow_dport_rewrite_set (&s->o2i, i2o_port); - } - nat_6t_flow_daddr_rewrite_set (&s->o2i, i2o_addr.as_u32); - nat_6t_flow_txfib_rewrite_set (&s->o2i, i2o_fib_index); - - if (nat_ed_ses_o2i_flow_hash_add_del (sm, thread_index, s, 1)) - { - b->error = node->errors[NAT_OUT2IN_ED_ERROR_HASH_ADD_FAILED]; - nat_ed_session_delete (sm, s, thread_index, 1); - nat_elog_warn ("out2in flow hash add failed"); - return 0; - } - - if (twice_nat == TWICE_NAT || (twice_nat == TWICE_NAT_SELF && - ip->src_address.as_u32 == i2o_addr.as_u32)) - { - int rc = 0; - snat_address_t *filter = 0; - - // if exact address is specified use this address - if (is_exact_address (mapping)) - { - snat_address_t *ap; - vec_foreach (ap, sm->twice_nat_addresses) - { - if (mapping->pool_addr.as_u32 == ap->addr.as_u32) - { - filter = ap; - break; - } - } - } - - if (filter) - { - rc = nat_alloc_addr_and_port_exact (filter, - thread_index, - nat_proto, - &s->ext_host_nat_addr, - &s->ext_host_nat_port, - sm->port_per_thread, - tsm->snat_thread_index); - s->flags |= SNAT_SESSION_FLAG_EXACT_ADDRESS; - } - else - { - rc = nat44_ed_alloc_outside_addr_and_port ( - sm->twice_nat_addresses, 0, thread_index, nat_proto, - &s->ext_host_nat_addr, &s->ext_host_nat_port, sm->port_per_thread, - tsm->snat_thread_index); - } - - if (rc) - { - b->error = node->errors[NAT_OUT2IN_ED_ERROR_OUT_OF_PORTS]; - if (nat_ed_ses_o2i_flow_hash_add_del (sm, thread_index, s, 0)) - { - nat_elog_warn ("out2in flow hash del failed"); - } - snat_free_outside_address_and_port ( - sm->twice_nat_addresses, thread_index, &s->ext_host_nat_addr, - s->ext_host_nat_port, s->nat_proto); - nat_ed_session_delete (sm, s, thread_index, 1); - return 0; - } - - s->flags |= SNAT_SESSION_FLAG_TWICE_NAT; - - nat_6t_flow_saddr_rewrite_set (&s->o2i, s->ext_host_nat_addr.as_u32); - if (NAT_PROTOCOL_ICMP == nat_proto) - { - nat_6t_flow_icmp_id_rewrite_set (&s->o2i, s->ext_host_nat_port); - } - else - { - nat_6t_flow_sport_rewrite_set (&s->o2i, s->ext_host_nat_port); - } - - nat_6t_l3_l4_csum_calc (&s->o2i); - - nat_6t_i2o_flow_init (sm, thread_index, s, i2o_addr, i2o_port, - s->ext_host_nat_addr, s->ext_host_nat_port, - i2o_fib_index, ip->protocol); - nat_6t_flow_daddr_rewrite_set (&s->i2o, s->ext_host_addr.as_u32); - if (NAT_PROTOCOL_ICMP == nat_proto) - { - nat_6t_flow_icmp_id_rewrite_set (&s->i2o, s->ext_host_port); - } - else - { - nat_6t_flow_dport_rewrite_set (&s->i2o, s->ext_host_port); - } - } - else - { - if (NAT_PROTOCOL_ICMP == nat_proto) - { - nat_6t_i2o_flow_init (sm, thread_index, s, i2o_addr, i2o_port, - s->ext_host_addr, i2o_port, i2o_fib_index, - ip->protocol); - } - else - { - nat_6t_i2o_flow_init (sm, thread_index, s, i2o_addr, i2o_port, - s->ext_host_addr, s->ext_host_port, - i2o_fib_index, ip->protocol); - } - } - - nat_6t_flow_saddr_rewrite_set (&s->i2o, o2i_addr.as_u32); - if (NAT_PROTOCOL_ICMP == nat_proto) - { - nat_6t_flow_icmp_id_rewrite_set (&s->i2o, o2i_port); - } - else - { - nat_6t_flow_sport_rewrite_set (&s->i2o, o2i_port); - } - - if (nat_ed_ses_i2o_flow_hash_add_del (sm, thread_index, s, 1)) - { - nat_elog_notice ("in2out flow hash add failed"); - if (nat_ed_ses_o2i_flow_hash_add_del (sm, thread_index, s, 0)) - { - nat_elog_warn ("out2in flow hash del failed"); - } - nat_ed_session_delete (sm, s, thread_index, 1); - return 0; - } - - nat_ipfix_logging_nat44_ses_create (thread_index, - s->in2out.addr.as_u32, - s->out2in.addr.as_u32, - s->nat_proto, - s->in2out.port, - s->out2in.port, s->in2out.fib_index); - - nat_syslog_nat44_sadd (s->user_index, s->in2out.fib_index, - &s->in2out.addr, s->in2out.port, - &s->ext_host_nat_addr, s->ext_host_nat_port, - &s->out2in.addr, s->out2in.port, - &s->ext_host_addr, s->ext_host_port, - s->nat_proto, is_twice_nat_session (s)); - - per_vrf_sessions_register_session (s, thread_index); - - return s; -} - -static void -create_bypass_for_fwd (snat_main_t *sm, vlib_buffer_t *b, snat_session_t *s, - ip4_header_t *ip, u32 rx_fib_index, u32 thread_index) -{ - clib_bihash_kv_16_8_t kv, value; - udp_header_t *udp; - snat_main_per_thread_data_t *tsm = &sm->per_thread_data[thread_index]; - vlib_main_t *vm = vlib_get_main (); - f64 now = vlib_time_now (vm); - u16 lookup_sport, lookup_dport; - u8 lookup_protocol; - ip4_address_t lookup_saddr, lookup_daddr; - - if (ip->protocol == IP_PROTOCOL_ICMP) - { - if (nat_get_icmp_session_lookup_values (b, ip, &lookup_saddr, - &lookup_sport, &lookup_daddr, - &lookup_dport, &lookup_protocol)) - return; - } - else - { - if (ip->protocol == IP_PROTOCOL_UDP || ip->protocol == IP_PROTOCOL_TCP) - { - udp = ip4_next_header (ip); - lookup_sport = udp->dst_port; - lookup_dport = udp->src_port; - } - else - { - lookup_sport = 0; - lookup_dport = 0; - } - lookup_saddr.as_u32 = ip->dst_address.as_u32; - lookup_daddr.as_u32 = ip->src_address.as_u32; - lookup_protocol = ip->protocol; - } - - init_ed_k (&kv, lookup_saddr, lookup_sport, lookup_daddr, lookup_dport, - rx_fib_index, lookup_protocol); - - if (!clib_bihash_search_16_8 (&sm->flow_hash, &kv, &value)) - { - ASSERT (thread_index == ed_value_get_thread_index (&value)); - s = - pool_elt_at_index (tsm->sessions, - ed_value_get_session_index (&value)); - } - else if (ip->protocol == IP_PROTOCOL_ICMP && - icmp_type_is_error_message - (vnet_buffer (b)->ip.reass.icmp_type_or_tcp_flags)) - { - return; - } - else - { - u32 proto; - - if (PREDICT_FALSE - (nat44_ed_maximum_sessions_exceeded - (sm, rx_fib_index, thread_index))) - return; - - s = nat_ed_session_alloc (sm, thread_index, now, ip->protocol); - if (!s) - { - nat_elog_warn ("create NAT session failed"); - return; - } - - proto = ip_proto_to_nat_proto (ip->protocol); - - s->ext_host_addr = ip->src_address; - s->ext_host_port = lookup_dport; - s->flags |= SNAT_SESSION_FLAG_FWD_BYPASS; - s->out2in.addr = ip->dst_address; - s->out2in.port = lookup_sport; - s->nat_proto = proto; - if (proto == NAT_PROTOCOL_OTHER) - { - s->flags |= SNAT_SESSION_FLAG_UNKNOWN_PROTO; - s->out2in.port = ip->protocol; - } - s->out2in.fib_index = rx_fib_index; - s->in2out.addr = s->out2in.addr; - s->in2out.port = s->out2in.port; - s->in2out.fib_index = s->out2in.fib_index; - - nat_6t_i2o_flow_init (sm, thread_index, s, ip->dst_address, lookup_sport, - ip->src_address, lookup_dport, rx_fib_index, - ip->protocol); - nat_6t_flow_txfib_rewrite_set (&s->i2o, rx_fib_index); - if (nat_ed_ses_i2o_flow_hash_add_del (sm, thread_index, s, 1)) - { - nat_elog_notice ("in2out flow add failed"); - nat_ed_session_delete (sm, s, thread_index, 1); - return; - } - - per_vrf_sessions_register_session (s, thread_index); - } - - if (ip->protocol == IP_PROTOCOL_TCP) - { - tcp_header_t *tcp = ip4_next_header (ip); - nat44_set_tcp_session_state_o2i (sm, now, s, tcp->flags, - tcp->ack_number, tcp->seq_number, - thread_index); - } - - /* Accounting */ - nat44_session_update_counters (s, now, 0, thread_index); - /* Per-user LRU list maintenance */ - nat44_session_update_lru (sm, s, thread_index); -} - -static snat_session_t * -nat44_ed_out2in_slowpath_unknown_proto (snat_main_t *sm, vlib_buffer_t *b, - ip4_header_t *ip, u32 rx_fib_index, - u32 thread_index, f64 now, - vlib_main_t *vm, - vlib_node_runtime_t *node) -{ - clib_bihash_kv_8_8_t kv, value; - snat_static_mapping_t *m; - snat_session_t *s; - - if (PREDICT_FALSE ( - nat44_ed_maximum_sessions_exceeded (sm, rx_fib_index, thread_index))) - { - b->error = node->errors[NAT_OUT2IN_ED_ERROR_MAX_SESSIONS_EXCEEDED]; - nat_elog_notice ("maximum sessions exceeded"); - return 0; - } - - init_nat_k (&kv, ip->dst_address, 0, 0, 0); - if (clib_bihash_search_8_8 (&sm->static_mapping_by_external, &kv, &value)) - { - b->error = node->errors[NAT_OUT2IN_ED_ERROR_NO_TRANSLATION]; - return 0; - } - - m = pool_elt_at_index (sm->static_mappings, value.value); - - /* Create a new session */ - s = nat_ed_session_alloc (sm, thread_index, now, ip->protocol); - if (!s) - { - b->error = node->errors[NAT_OUT2IN_ED_ERROR_MAX_USER_SESS_EXCEEDED]; - nat_elog_warn ("create NAT session failed"); - return 0; - } - - s->ext_host_addr.as_u32 = ip->src_address.as_u32; - s->flags |= SNAT_SESSION_FLAG_UNKNOWN_PROTO; - s->flags |= SNAT_SESSION_FLAG_STATIC_MAPPING; - s->flags |= SNAT_SESSION_FLAG_ENDPOINT_DEPENDENT; - s->out2in.addr.as_u32 = ip->dst_address.as_u32; - s->out2in.fib_index = rx_fib_index; - s->in2out.addr.as_u32 = m->local_addr.as_u32; - s->in2out.fib_index = m->fib_index; - s->in2out.port = s->out2in.port = ip->protocol; - - nat_6t_o2i_flow_init (sm, thread_index, s, ip->dst_address, 0, - ip->src_address, 0, m->fib_index, ip->protocol); - nat_6t_flow_saddr_rewrite_set (&s->i2o, ip->dst_address.as_u32); - if (nat_ed_ses_i2o_flow_hash_add_del (sm, thread_index, s, 1)) - { - nat_elog_notice ("in2out key add failed"); - nat_ed_session_delete (sm, s, thread_index, 1); - return NULL; - } - - nat_6t_o2i_flow_init (sm, thread_index, s, ip->src_address, 0, - ip->dst_address, 0, rx_fib_index, ip->protocol); - nat_6t_flow_daddr_rewrite_set (&s->o2i, m->local_addr.as_u32); - nat_6t_flow_txfib_rewrite_set (&s->o2i, m->fib_index); - if (nat_ed_ses_o2i_flow_hash_add_del (sm, thread_index, s, 1)) - { - nat_elog_notice ("out2in flow hash add failed"); - nat_ed_session_delete (sm, s, thread_index, 1); - return NULL; - } - - per_vrf_sessions_register_session (s, thread_index); - - /* Accounting */ - nat44_session_update_counters (s, now, vlib_buffer_length_in_chain (vm, b), - thread_index); - /* Per-user LRU list maintenance */ - nat44_session_update_lru (sm, s, thread_index); - - return s; -} - -static inline uword -nat44_ed_out2in_fast_path_node_fn_inline (vlib_main_t * vm, - vlib_node_runtime_t * node, - vlib_frame_t * frame, - int is_multi_worker) -{ - u32 n_left_from, *from; - snat_main_t *sm = &snat_main; - f64 now = vlib_time_now (vm); - u32 thread_index = vm->thread_index; - snat_main_per_thread_data_t *tsm = &sm->per_thread_data[thread_index]; - - from = vlib_frame_vector_args (frame); - n_left_from = frame->n_vectors; - - vlib_buffer_t *bufs[VLIB_FRAME_SIZE], **b = bufs; - u16 nexts[VLIB_FRAME_SIZE], *next = nexts; - vlib_get_buffers (vm, from, b, n_left_from); - - while (n_left_from > 0) - { - vlib_buffer_t *b0; - u32 sw_if_index0, rx_fib_index0; - nat_protocol_t proto0; - ip4_header_t *ip0; - snat_session_t *s0 = 0; - clib_bihash_kv_16_8_t kv0, value0; - nat_translation_error_e translation_error = NAT_ED_TRNSL_ERR_SUCCESS; - nat_6t_flow_t *f = 0; - ip4_address_t lookup_saddr, lookup_daddr; - u16 lookup_sport, lookup_dport; - u8 lookup_protocol; - int lookup_skipped = 0; - - b0 = *b; - b++; - - lookup_sport = vnet_buffer (b0)->ip.reass.l4_src_port; - lookup_dport = vnet_buffer (b0)->ip.reass.l4_dst_port; - - /* Prefetch next iteration. */ - if (PREDICT_TRUE (n_left_from >= 2)) - { - vlib_buffer_t *p2; - - p2 = *b; - - vlib_prefetch_buffer_header (p2, LOAD); - - CLIB_PREFETCH (p2->data, CLIB_CACHE_LINE_BYTES, LOAD); - } - - next[0] = vnet_buffer2 (b0)->nat.arc_next; - - vnet_buffer (b0)->snat.flags = 0; - ip0 = vlib_buffer_get_current (b0); - - sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX]; - rx_fib_index0 = - fib_table_get_index_for_sw_if_index (FIB_PROTOCOL_IP4, sw_if_index0); - - if (PREDICT_FALSE (ip0->ttl == 1)) - { - vnet_buffer (b0)->sw_if_index[VLIB_TX] = (u32) ~ 0; - icmp4_error_set_vnet_buffer (b0, ICMP4_time_exceeded, - ICMP4_time_exceeded_ttl_exceeded_in_transit, - 0); - next[0] = NAT_NEXT_ICMP_ERROR; - goto trace0; - } - - proto0 = ip_proto_to_nat_proto (ip0->protocol); - - if (PREDICT_FALSE (proto0 == NAT_PROTOCOL_ICMP)) - { - if (vnet_buffer (b0)->ip.reass.icmp_type_or_tcp_flags != - ICMP4_echo_request && - vnet_buffer (b0)->ip.reass.icmp_type_or_tcp_flags != - ICMP4_echo_reply && - !icmp_type_is_error_message ( - vnet_buffer (b0)->ip.reass.icmp_type_or_tcp_flags)) - { - b0->error = node->errors[NAT_OUT2IN_ED_ERROR_BAD_ICMP_TYPE]; - next[0] = NAT_NEXT_DROP; - goto trace0; - } - int err = nat_get_icmp_session_lookup_values ( - b0, ip0, &lookup_saddr, &lookup_sport, &lookup_daddr, - &lookup_dport, &lookup_protocol); - if (err != 0) - { - b0->error = node->errors[err]; - next[0] = NAT_NEXT_DROP; - goto trace0; - } - } - else - { - lookup_saddr.as_u32 = ip0->src_address.as_u32; - lookup_daddr.as_u32 = ip0->dst_address.as_u32; - lookup_protocol = ip0->protocol; - } - - /* there might be a stashed index in vnet_buffer2 from handoff or - * classify node, see if it can be used */ - if (!pool_is_free_index (tsm->sessions, - vnet_buffer2 (b0)->nat.cached_session_index)) - { - s0 = pool_elt_at_index (tsm->sessions, - vnet_buffer2 (b0)->nat.cached_session_index); - if (PREDICT_TRUE ( - nat_6t_flow_match (&s0->o2i, b0, lookup_saddr, lookup_sport, - lookup_daddr, lookup_dport, lookup_protocol, - rx_fib_index0) || - (s0->flags & SNAT_SESSION_FLAG_TWICE_NAT && - nat_6t_flow_match (&s0->i2o, b0, lookup_saddr, lookup_sport, - lookup_daddr, lookup_dport, - lookup_protocol, rx_fib_index0)))) - { - /* yes, this is the droid we're looking for */ - lookup_skipped = 1; - goto skip_lookup; - } - s0 = NULL; - } - - init_ed_k (&kv0, lookup_saddr, lookup_sport, lookup_daddr, lookup_dport, - rx_fib_index0, lookup_protocol); - - // lookup flow - if (clib_bihash_search_16_8 (&sm->flow_hash, &kv0, &value0)) - { - // flow does not exist go slow path - next[0] = NAT_NEXT_OUT2IN_ED_SLOW_PATH; - goto trace0; - } - ASSERT (thread_index == ed_value_get_thread_index (&value0)); - s0 = - pool_elt_at_index (tsm->sessions, - ed_value_get_session_index (&value0)); - skip_lookup: - - if (PREDICT_FALSE (per_vrf_sessions_is_expired (s0, thread_index))) - { - // session is closed, go slow path - nat_free_session_data (sm, s0, thread_index, 0); - nat_ed_session_delete (sm, s0, thread_index, 1); - next[0] = NAT_NEXT_OUT2IN_ED_SLOW_PATH; - goto trace0; - } - - if (s0->tcp_closed_timestamp) - { - if (now >= s0->tcp_closed_timestamp) - { - // session is closed, go slow path, freed in slow path - next[0] = NAT_NEXT_OUT2IN_ED_SLOW_PATH; - } - else - { - // session in transitory timeout, drop - b0->error = node->errors[NAT_OUT2IN_ED_ERROR_TCP_CLOSED]; - next[0] = NAT_NEXT_DROP; - } - goto trace0; - } - - // drop if session expired - u64 sess_timeout_time; - sess_timeout_time = - s0->last_heard + (f64) nat44_session_get_timeout (sm, s0); - if (now >= sess_timeout_time) - { - // session is closed, go slow path - nat_free_session_data (sm, s0, thread_index, 0); - nat_ed_session_delete (sm, s0, thread_index, 1); - next[0] = NAT_NEXT_OUT2IN_ED_SLOW_PATH; - goto trace0; - } - - if (nat_6t_flow_match (&s0->o2i, b0, lookup_saddr, lookup_sport, - lookup_daddr, lookup_dport, lookup_protocol, - rx_fib_index0)) - { - f = &s0->o2i; - } - else if (s0->flags & SNAT_SESSION_FLAG_TWICE_NAT && - nat_6t_flow_match (&s0->i2o, b0, lookup_saddr, lookup_sport, - lookup_daddr, lookup_dport, lookup_protocol, - rx_fib_index0)) - { - f = &s0->i2o; - } - else - { - /* - * Send DHCP packets to the ipv4 stack, or we won't - * be able to use dhcp client on the outside interface - */ - if (PREDICT_FALSE ( - proto0 == NAT_PROTOCOL_UDP && - (vnet_buffer (b0)->ip.reass.l4_dst_port == - clib_host_to_net_u16 (UDP_DST_PORT_dhcp_to_client)))) - { - goto trace0; - } - - if (!sm->forwarding_enabled) - { - b0->error = node->errors[NAT_OUT2IN_ED_ERROR_NO_TRANSLATION]; - next[0] = NAT_NEXT_DROP; - goto trace0; - } - else - { - if (nat_6t_flow_match (&s0->i2o, b0, lookup_saddr, lookup_sport, - lookup_daddr, lookup_dport, - lookup_protocol, rx_fib_index0)) - { - f = &s0->i2o; - } - else - { - // FIXME TODO bypass ??? - // create_bypass_for_fwd (sm, b0, s0, ip0, rx_fib_index0, - // thread_index); - translation_error = NAT_ED_TRNSL_ERR_FLOW_MISMATCH; - nat_free_session_data (sm, s0, thread_index, 0); - nat_ed_session_delete (sm, s0, thread_index, 1); - next[0] = NAT_NEXT_DROP; - goto trace0; - } - } - } - - if (NAT_ED_TRNSL_ERR_SUCCESS != - (translation_error = nat_6t_flow_buf_translate ( - sm, b0, ip0, f, proto0, 0 /* is_output_feature */))) - { - next[0] = NAT_NEXT_DROP; - goto trace0; - } - - switch (proto0) - { - case NAT_PROTOCOL_TCP: - vlib_increment_simple_counter (&sm->counters.fastpath.out2in_ed.tcp, - thread_index, sw_if_index0, 1); - nat44_set_tcp_session_state_o2i (sm, now, s0, - vnet_buffer (b0)->ip. - reass.icmp_type_or_tcp_flags, - vnet_buffer (b0)->ip. - reass.tcp_ack_number, - vnet_buffer (b0)->ip. - reass.tcp_seq_number, - thread_index); - break; - case NAT_PROTOCOL_UDP: - vlib_increment_simple_counter (&sm->counters.fastpath.out2in_ed.udp, - thread_index, sw_if_index0, 1); - break; - case NAT_PROTOCOL_ICMP: - vlib_increment_simple_counter (&sm->counters.fastpath.out2in_ed.icmp, - thread_index, sw_if_index0, 1); - break; - case NAT_PROTOCOL_OTHER: - vlib_increment_simple_counter ( - &sm->counters.fastpath.out2in_ed.other, thread_index, sw_if_index0, - 1); - break; - } - - /* Accounting */ - nat44_session_update_counters (s0, now, - vlib_buffer_length_in_chain (vm, b0), - thread_index); - /* Per-user LRU list maintenance */ - nat44_session_update_lru (sm, s0, thread_index); - - trace0: - if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE) - && (b0->flags & VLIB_BUFFER_IS_TRACED))) - { - nat44_ed_out2in_trace_t *t = - vlib_add_trace (vm, node, b0, sizeof (*t)); - t->sw_if_index = sw_if_index0; - t->next_index = next[0]; - t->is_slow_path = 0; - t->translation_error = translation_error; - clib_memcpy (&t->search_key, &kv0, sizeof (t->search_key)); - t->lookup_skipped = lookup_skipped; - - if (s0) - { - t->session_index = s0 - tsm->sessions; - clib_memcpy (&t->i2of, &s0->i2o, sizeof (t->i2of)); - clib_memcpy (&t->o2if, &s0->o2i, sizeof (t->o2if)); - t->translation_via_i2of = (&s0->i2o == f); - } - else - { - t->session_index = ~0; - } - } - - if (next[0] == NAT_NEXT_DROP) - { - vlib_increment_simple_counter (&sm->counters.fastpath. - out2in_ed.drops, thread_index, - sw_if_index0, 1); - } - - n_left_from--; - next++; - } - - vlib_buffer_enqueue_to_next (vm, node, from, (u16 *) nexts, - frame->n_vectors); - return frame->n_vectors; -} - -static inline uword -nat44_ed_out2in_slow_path_node_fn_inline (vlib_main_t * vm, - vlib_node_runtime_t * node, - vlib_frame_t * frame) -{ - u32 n_left_from, *from; - snat_main_t *sm = &snat_main; - f64 now = vlib_time_now (vm); - u32 thread_index = vm->thread_index; - snat_main_per_thread_data_t *tsm = &sm->per_thread_data[thread_index]; - snat_static_mapping_t *m; - - from = vlib_frame_vector_args (frame); - n_left_from = frame->n_vectors; - - vlib_buffer_t *bufs[VLIB_FRAME_SIZE], **b = bufs; - u16 nexts[VLIB_FRAME_SIZE], *next = nexts; - vlib_get_buffers (vm, from, b, n_left_from); - - while (n_left_from > 0) - { - vlib_buffer_t *b0; - u32 sw_if_index0, rx_fib_index0; - nat_protocol_t proto0; - ip4_header_t *ip0; - udp_header_t *udp0; - icmp46_header_t *icmp0; - snat_session_t *s0 = 0; - clib_bihash_kv_16_8_t kv0, value0; - lb_nat_type_t lb_nat0; - twice_nat_type_t twice_nat0; - u8 identity_nat0; - ip4_address_t sm_addr; - u16 sm_port; - u32 sm_fib_index; - nat_translation_error_e translation_error = NAT_ED_TRNSL_ERR_SUCCESS; - - b0 = *b; - next[0] = vnet_buffer2 (b0)->nat.arc_next; - - vnet_buffer (b0)->snat.flags = 0; - ip0 = vlib_buffer_get_current (b0); - - sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX]; - rx_fib_index0 = - fib_table_get_index_for_sw_if_index (FIB_PROTOCOL_IP4, sw_if_index0); - - if (PREDICT_FALSE (ip0->ttl == 1)) - { - vnet_buffer (b0)->sw_if_index[VLIB_TX] = (u32) ~ 0; - icmp4_error_set_vnet_buffer (b0, ICMP4_time_exceeded, - ICMP4_time_exceeded_ttl_exceeded_in_transit, - 0); - next[0] = NAT_NEXT_ICMP_ERROR; - goto trace0; - } - - udp0 = ip4_next_header (ip0); - icmp0 = (icmp46_header_t *) udp0; - proto0 = ip_proto_to_nat_proto (ip0->protocol); - - if (PREDICT_FALSE (proto0 == NAT_PROTOCOL_OTHER)) - { - s0 = nat44_ed_out2in_slowpath_unknown_proto ( - sm, b0, ip0, rx_fib_index0, thread_index, now, vm, node); - if (!sm->forwarding_enabled) - { - if (!s0) - next[0] = NAT_NEXT_DROP; - } - if (NAT_NEXT_DROP != next[0] && - NAT_ED_TRNSL_ERR_SUCCESS != - (translation_error = nat_6t_flow_buf_translate ( - sm, b0, ip0, &s0->o2i, proto0, 0 /* is_output_feature */))) - { - goto trace0; - } - - vlib_increment_simple_counter (&sm->counters.slowpath. - out2in_ed.other, thread_index, - sw_if_index0, 1); - goto trace0; - } - - if (PREDICT_FALSE (proto0 == NAT_PROTOCOL_ICMP)) - { - next[0] = icmp_out2in_ed_slow_path - (sm, b0, ip0, icmp0, sw_if_index0, rx_fib_index0, node, - next[0], now, thread_index, &s0); - - if (NAT_NEXT_DROP != next[0] && s0 && - NAT_ED_TRNSL_ERR_SUCCESS != - (translation_error = nat_6t_flow_buf_translate ( - sm, b0, ip0, &s0->o2i, proto0, 0 /* is_output_feature */))) - { - goto trace0; - } - - vlib_increment_simple_counter (&sm->counters.slowpath. - out2in_ed.icmp, thread_index, - sw_if_index0, 1); - goto trace0; - } - - init_ed_k (&kv0, ip0->src_address, - vnet_buffer (b0)->ip.reass.l4_src_port, ip0->dst_address, - vnet_buffer (b0)->ip.reass.l4_dst_port, rx_fib_index0, - ip0->protocol); - - s0 = NULL; - if (!clib_bihash_search_16_8 (&sm->flow_hash, &kv0, &value0)) - { - ASSERT (thread_index == ed_value_get_thread_index (&value0)); - s0 = - pool_elt_at_index (tsm->sessions, - ed_value_get_session_index (&value0)); - - if (s0->tcp_closed_timestamp && now >= s0->tcp_closed_timestamp) - { - nat_free_session_data (sm, s0, thread_index, 0); - nat_ed_session_delete (sm, s0, thread_index, 1); - s0 = NULL; - } - } - - if (!s0) - { - /* Try to match static mapping by external address and port, - destination address and port in packet */ - - if (snat_static_mapping_match - (sm, ip0->dst_address, - vnet_buffer (b0)->ip.reass.l4_dst_port, rx_fib_index0, - proto0, &sm_addr, &sm_port, &sm_fib_index, 1, 0, - &twice_nat0, &lb_nat0, &ip0->src_address, &identity_nat0, &m)) - { - /* - * Send DHCP packets to the ipv4 stack, or we won't - * be able to use dhcp client on the outside interface - */ - if (PREDICT_FALSE (proto0 == NAT_PROTOCOL_UDP - && (vnet_buffer (b0)->ip.reass.l4_dst_port == - clib_host_to_net_u16 - (UDP_DST_PORT_dhcp_to_client)))) - { - goto trace0; - } - - if (!sm->forwarding_enabled) - { - b0->error = - node->errors[NAT_OUT2IN_ED_ERROR_NO_TRANSLATION]; - next[0] = NAT_NEXT_DROP; - } - else - { - if (next_src_nat - (sm, ip0, vnet_buffer (b0)->ip.reass.l4_src_port, - vnet_buffer (b0)->ip.reass.l4_dst_port, - thread_index, rx_fib_index0)) - { - next[0] = NAT_NEXT_IN2OUT_ED_FAST_PATH; - } - else - { - create_bypass_for_fwd (sm, b0, s0, ip0, rx_fib_index0, - thread_index); - } - } - goto trace0; - } - - if (PREDICT_FALSE (identity_nat0)) - goto trace0; - - if ((proto0 == NAT_PROTOCOL_TCP) - && !tcp_flags_is_init (vnet_buffer (b0)->ip. - reass.icmp_type_or_tcp_flags)) - { - b0->error = node->errors[NAT_OUT2IN_ED_ERROR_NON_SYN]; - next[0] = NAT_NEXT_DROP; - goto trace0; - } - - /* Create session initiated by host from external network */ - s0 = create_session_for_static_mapping_ed (sm, b0, - sm_addr, sm_port, - sm_fib_index, - ip0->dst_address, - vnet_buffer (b0)-> - ip.reass.l4_dst_port, - rx_fib_index0, proto0, - node, rx_fib_index0, - thread_index, twice_nat0, - lb_nat0, now, m); - if (!s0) - { - next[0] = NAT_NEXT_DROP; - goto trace0; - } - } - - if (NAT_ED_TRNSL_ERR_SUCCESS != - (translation_error = nat_6t_flow_buf_translate ( - sm, b0, ip0, &s0->o2i, proto0, 0 /* is_output_feature */))) - { - next[0] = NAT_NEXT_DROP; - goto trace0; - } - - if (PREDICT_TRUE (proto0 == NAT_PROTOCOL_TCP)) - { - vlib_increment_simple_counter (&sm->counters.slowpath.out2in_ed.tcp, - thread_index, sw_if_index0, 1); - nat44_set_tcp_session_state_o2i (sm, now, s0, - vnet_buffer (b0)->ip. - reass.icmp_type_or_tcp_flags, - vnet_buffer (b0)->ip. - reass.tcp_ack_number, - vnet_buffer (b0)->ip. - reass.tcp_seq_number, - thread_index); - } - else - { - vlib_increment_simple_counter (&sm->counters.slowpath.out2in_ed.udp, - thread_index, sw_if_index0, 1); - } - - /* Accounting */ - nat44_session_update_counters (s0, now, - vlib_buffer_length_in_chain (vm, b0), - thread_index); - /* Per-user LRU list maintenance */ - nat44_session_update_lru (sm, s0, thread_index); - - trace0: - if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE) - && (b0->flags & VLIB_BUFFER_IS_TRACED))) - { - nat44_ed_out2in_trace_t *t = - vlib_add_trace (vm, node, b0, sizeof (*t)); - t->sw_if_index = sw_if_index0; - t->next_index = next[0]; - t->is_slow_path = 1; - t->translation_error = translation_error; - clib_memcpy (&t->search_key, &kv0, sizeof (t->search_key)); - - if (s0) - { - t->session_index = s0 - tsm->sessions; - clib_memcpy (&t->i2of, &s0->i2o, sizeof (t->i2of)); - clib_memcpy (&t->o2if, &s0->o2i, sizeof (t->o2if)); - } - else - { - t->session_index = ~0; - } - } - - if (next[0] == NAT_NEXT_DROP) - { - vlib_increment_simple_counter (&sm->counters.slowpath. - out2in_ed.drops, thread_index, - sw_if_index0, 1); - } - - n_left_from--; - next++; - b++; - } - - vlib_buffer_enqueue_to_next (vm, node, from, (u16 *) nexts, - frame->n_vectors); - - return frame->n_vectors; -} - -VLIB_NODE_FN (nat44_ed_out2in_node) (vlib_main_t * vm, - vlib_node_runtime_t * node, - vlib_frame_t * frame) -{ - if (snat_main.num_workers > 1) - { - return nat44_ed_out2in_fast_path_node_fn_inline (vm, node, frame, 1); - } - else - { - return nat44_ed_out2in_fast_path_node_fn_inline (vm, node, frame, 0); - } -} - -/* *INDENT-OFF* */ -VLIB_REGISTER_NODE (nat44_ed_out2in_node) = { - .name = "nat44-ed-out2in", - .vector_size = sizeof (u32), - .sibling_of = "nat-default", - .format_trace = format_nat44_ed_out2in_trace, - .type = VLIB_NODE_TYPE_INTERNAL, - .n_errors = ARRAY_LEN(nat_out2in_ed_error_strings), - .error_strings = nat_out2in_ed_error_strings, - .runtime_data_bytes = sizeof (snat_runtime_t), -}; -/* *INDENT-ON* */ - -VLIB_NODE_FN (nat44_ed_out2in_slowpath_node) (vlib_main_t * vm, - vlib_node_runtime_t * node, - vlib_frame_t * frame) -{ - return nat44_ed_out2in_slow_path_node_fn_inline (vm, node, frame); -} - -/* *INDENT-OFF* */ -VLIB_REGISTER_NODE (nat44_ed_out2in_slowpath_node) = { - .name = "nat44-ed-out2in-slowpath", - .vector_size = sizeof (u32), - .sibling_of = "nat-default", - .format_trace = format_nat44_ed_out2in_trace, - .type = VLIB_NODE_TYPE_INTERNAL, - .n_errors = ARRAY_LEN(nat_out2in_ed_error_strings), - .error_strings = nat_out2in_ed_error_strings, - .runtime_data_bytes = sizeof (snat_runtime_t), -}; -/* *INDENT-ON* */ - -static u8 * -format_nat_pre_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 *); - nat_pre_trace_t *t = va_arg (*args, nat_pre_trace_t *); - return format (s, "out2in next_index %d arc_next_index %d", t->next_index, - t->arc_next_index); -} - -VLIB_NODE_FN (nat_pre_out2in_node) (vlib_main_t * vm, - vlib_node_runtime_t * node, - vlib_frame_t * frame) -{ - return nat_pre_node_fn_inline (vm, node, frame, - NAT_NEXT_OUT2IN_ED_FAST_PATH); -} - -/* *INDENT-OFF* */ -VLIB_REGISTER_NODE (nat_pre_out2in_node) = { - .name = "nat-pre-out2in", - .vector_size = sizeof (u32), - .sibling_of = "nat-default", - .format_trace = format_nat_pre_trace, - .type = VLIB_NODE_TYPE_INTERNAL, - .n_errors = 0, - }; -/* *INDENT-ON* */ - -/* - * fd.io coding-style-patch-verification: ON - * - * Local Variables: - * eval: (c-set-style "gnu") - * End: - */ diff --git a/src/plugins/nat/test/test_nat44_ed.py b/src/plugins/nat/test/test_nat44_ed.py index 5133ae3c8ea..3980f69c5bb 100644 --- a/src/plugins/nat/test/test_nat44_ed.py +++ b/src/plugins/nat/test/test_nat44_ed.py @@ -48,7 +48,6 @@ class NAT44EDTestCase(VppTestCase): def plugin_enable(self): self.vapi.nat44_plugin_enable_disable( - flags=self.nat44_config_flags.NAT44_IS_ENDPOINT_DEPENDENT, sessions=self.max_sessions, enable=1) def plugin_disable(self): @@ -150,7 +149,6 @@ class NAT44EDTestCase(VppTestCase): @classmethod def setUpClass(cls): super(NAT44EDTestCase, cls).setUpClass() - cls.vapi.cli("set log class nat level debug") cls.create_pg_interfaces(range(12)) cls.interfaces = list(cls.pg_interfaces[:4]) @@ -874,7 +872,7 @@ class NAT44EDTestCase(VppTestCase): self.config_flags.NAT_IS_EXT_HOST_VALID) self.assertTrue(sessions[0].flags & self.config_flags.NAT_IS_TWICE_NAT) - self.logger.info(self.vapi.cli("show nat44 sessions detail")) + self.logger.info(self.vapi.cli("show nat44 sessions")) self.vapi.nat44_del_session( address=sessions[0].inside_ip_address, port=sessions[0].inside_port, @@ -1978,10 +1976,10 @@ class TestNAT44EDMW(TestNAT44ED): self.nat_add_outside_interface(self.pg1) # in2out - tc1 = self.get_stats_counter('/nat44/ed/in2out/slowpath/tcp') - uc1 = self.get_stats_counter('/nat44/ed/in2out/slowpath/udp') - ic1 = self.get_stats_counter('/nat44/ed/in2out/slowpath/icmp') - dc1 = self.get_stats_counter('/nat44/ed/in2out/slowpath/drops') + tc1 = self.get_stats_counter('/nat44-ed/in2out/slowpath/tcp') + uc1 = self.get_stats_counter('/nat44-ed/in2out/slowpath/udp') + ic1 = self.get_stats_counter('/nat44-ed/in2out/slowpath/icmp') + dc1 = self.get_stats_counter('/nat44-ed/in2out/slowpath/drops') pkts = self.create_stream_in(self.pg0, self.pg1) # TODO: specify worker=idx, also stats have to @@ -1993,10 +1991,10 @@ class TestNAT44EDMW(TestNAT44ED): self.verify_capture_out(capture, ignore_port=True) if_idx = self.pg0.sw_if_index - tc2 = self.get_stats_counter('/nat44/ed/in2out/slowpath/tcp') - uc2 = self.get_stats_counter('/nat44/ed/in2out/slowpath/udp') - ic2 = self.get_stats_counter('/nat44/ed/in2out/slowpath/icmp') - dc2 = self.get_stats_counter('/nat44/ed/in2out/slowpath/drops') + tc2 = self.get_stats_counter('/nat44-ed/in2out/slowpath/tcp') + uc2 = self.get_stats_counter('/nat44-ed/in2out/slowpath/udp') + ic2 = self.get_stats_counter('/nat44-ed/in2out/slowpath/icmp') + dc2 = self.get_stats_counter('/nat44-ed/in2out/slowpath/drops') self.assertEqual(tc2[if_idx] - tc1[if_idx], 2) self.assertEqual(uc2[if_idx] - uc1[if_idx], 1) @@ -2004,10 +2002,10 @@ class TestNAT44EDMW(TestNAT44ED): self.assertEqual(dc2[if_idx] - dc1[if_idx], 0) # out2in - tc1 = self.get_stats_counter('/nat44/ed/out2in/fastpath/tcp') - uc1 = self.get_stats_counter('/nat44/ed/out2in/fastpath/udp') - ic1 = self.get_stats_counter('/nat44/ed/out2in/fastpath/icmp') - dc1 = self.get_stats_counter('/nat44/ed/out2in/fastpath/drops') + tc1 = self.get_stats_counter('/nat44-ed/out2in/fastpath/tcp') + uc1 = self.get_stats_counter('/nat44-ed/out2in/fastpath/udp') + ic1 = self.get_stats_counter('/nat44-ed/out2in/fastpath/icmp') + dc1 = self.get_stats_counter('/nat44-ed/out2in/fastpath/drops') pkts = self.create_stream_out(self.pg1) self.pg1.add_stream(pkts) @@ -2017,17 +2015,17 @@ class TestNAT44EDMW(TestNAT44ED): self.verify_capture_in(capture, self.pg0) if_idx = self.pg1.sw_if_index - tc2 = self.get_stats_counter('/nat44/ed/out2in/fastpath/tcp') - uc2 = self.get_stats_counter('/nat44/ed/out2in/fastpath/udp') - ic2 = self.get_stats_counter('/nat44/ed/out2in/fastpath/icmp') - dc2 = self.get_stats_counter('/nat44/ed/out2in/fastpath/drops') + tc2 = self.get_stats_counter('/nat44-ed/out2in/fastpath/tcp') + uc2 = self.get_stats_counter('/nat44-ed/out2in/fastpath/udp') + ic2 = self.get_stats_counter('/nat44-ed/out2in/fastpath/icmp') + dc2 = self.get_stats_counter('/nat44-ed/out2in/fastpath/drops') self.assertEqual(tc2[if_idx] - tc1[if_idx], 2) self.assertEqual(uc2[if_idx] - uc1[if_idx], 1) self.assertEqual(ic2[if_idx] - ic1[if_idx], 1) self.assertEqual(dc2[if_idx] - dc1[if_idx], 0) - sc = self.get_stats_counter('/nat44/total-sessions') + sc = self.get_stats_counter('/nat44-ed/total-sessions') self.assertEqual(sc[0], 3) def test_frag_in_order(self): @@ -2221,28 +2219,6 @@ class TestNAT44EDMW(TestNAT44ED): capture = outside.get_capture(len(stream)) - def test_clear_sessions(self): - """ NAT44ED session clearing test """ - - self.nat_add_address(self.nat_addr) - self.nat_add_inside_interface(self.pg0) - self.nat_add_outside_interface(self.pg1) - - pkts = self.create_stream_in(self.pg0, self.pg1) - 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, ignore_port=True) - - sessions = self.get_stats_counter('/nat44/total-sessions') - self.assertTrue(sessions[0] > 0) - - self.vapi.cli("clear nat44 sessions") - - sessions = self.get_stats_counter('/nat44/total-sessions') - self.assertEqual(sessions[0], 0) - def test_show_max_translations(self): """ NAT44ED API test - max translations per thread """ nat_config = self.vapi.nat_show_config_2() @@ -3331,13 +3307,13 @@ class TestNAT44EDMW(TestNAT44ED): # in2out tcpn = self.get_stats_counter( - '/nat44/ed/in2out/slowpath/tcp') + '/nat44-ed/in2out/slowpath/tcp') udpn = self.get_stats_counter( - '/nat44/ed/in2out/slowpath/udp') + '/nat44-ed/in2out/slowpath/udp') icmpn = self.get_stats_counter( - '/nat44/ed/in2out/slowpath/icmp') + '/nat44-ed/in2out/slowpath/icmp') drops = self.get_stats_counter( - '/nat44/ed/in2out/slowpath/drops') + '/nat44-ed/in2out/slowpath/drops') pkts = self.create_stream_in(self.pg7, self.pg8) self.pg7.add_stream(pkts) @@ -3348,27 +3324,27 @@ class TestNAT44EDMW(TestNAT44ED): if_idx = self.pg7.sw_if_index cnt = self.get_stats_counter( - '/nat44/ed/in2out/slowpath/tcp') + '/nat44-ed/in2out/slowpath/tcp') self.assertEqual(cnt[if_idx] - tcpn[if_idx], 2) cnt = self.get_stats_counter( - '/nat44/ed/in2out/slowpath/udp') + '/nat44-ed/in2out/slowpath/udp') self.assertEqual(cnt[if_idx] - udpn[if_idx], 1) cnt = self.get_stats_counter( - '/nat44/ed/in2out/slowpath/icmp') + '/nat44-ed/in2out/slowpath/icmp') self.assertEqual(cnt[if_idx] - icmpn[if_idx], 1) cnt = self.get_stats_counter( - '/nat44/ed/in2out/slowpath/drops') + '/nat44-ed/in2out/slowpath/drops') self.assertEqual(cnt[if_idx] - drops[if_idx], 0) # out2in tcpn = self.get_stats_counter( - '/nat44/ed/out2in/fastpath/tcp') + '/nat44-ed/out2in/fastpath/tcp') udpn = self.get_stats_counter( - '/nat44/ed/out2in/fastpath/udp') + '/nat44-ed/out2in/fastpath/udp') icmpn = self.get_stats_counter( - '/nat44/ed/out2in/fastpath/icmp') + '/nat44-ed/out2in/fastpath/icmp') drops = self.get_stats_counter( - '/nat44/ed/out2in/fastpath/drops') + '/nat44-ed/out2in/fastpath/drops') pkts = self.create_stream_out(self.pg8) self.pg8.add_stream(pkts) @@ -3379,19 +3355,19 @@ class TestNAT44EDMW(TestNAT44ED): if_idx = self.pg8.sw_if_index cnt = self.get_stats_counter( - '/nat44/ed/out2in/fastpath/tcp') + '/nat44-ed/out2in/fastpath/tcp') self.assertEqual(cnt[if_idx] - tcpn[if_idx], 2) cnt = self.get_stats_counter( - '/nat44/ed/out2in/fastpath/udp') + '/nat44-ed/out2in/fastpath/udp') self.assertEqual(cnt[if_idx] - udpn[if_idx], 1) cnt = self.get_stats_counter( - '/nat44/ed/out2in/fastpath/icmp') + '/nat44-ed/out2in/fastpath/icmp') self.assertEqual(cnt[if_idx] - icmpn[if_idx], 1) cnt = self.get_stats_counter( - '/nat44/ed/out2in/fastpath/drops') + '/nat44-ed/out2in/fastpath/drops') self.assertEqual(cnt[if_idx] - drops[if_idx], 0) - sessions = self.get_stats_counter('/nat44/total-sessions') + sessions = self.get_stats_counter('/nat44-ed/total-sessions') self.assertEqual(sessions[0], 3) finally: diff --git a/src/plugins/nat/test/test_nat44_ei.py b/src/plugins/nat/test/test_nat44_ei.py index 999f91ecda8..da6ca4293c0 100644 --- a/src/plugins/nat/test/test_nat44_ei.py +++ b/src/plugins/nat/test/test_nat44_ei.py @@ -70,11 +70,7 @@ class MethodHolder(VppTestCase): @property def config_flags(self): - return VppEnum.vl_api_nat_config_flags_t - - @property - def nat44_config_flags(self): - return VppEnum.vl_api_nat44_config_flags_t + return VppEnum.vl_api_nat44_ei_config_flags_t @property def SYSLOG_SEVERITY(self): @@ -100,9 +96,9 @@ class MethodHolder(VppTestCase): """ if not (local_port and external_port): - flags |= self.config_flags.NAT_IS_ADDR_ONLY + flags |= self.config_flags.NAT44_EI_ADDR_ONLY_MAPPING - self.vapi.nat44_add_del_static_mapping( + self.vapi.nat44_ei_add_del_static_mapping( is_add=is_add, local_ip_address=local_ip, external_ip_address=external_ip, @@ -113,20 +109,17 @@ class MethodHolder(VppTestCase): flags=flags, tag=tag) - def nat44_add_address(self, ip, is_add=1, vrf_id=0xFFFFFFFF, twice_nat=0): + def nat44_add_address(self, ip, is_add=1, vrf_id=0xFFFFFFFF): """ Add/delete NAT44EI address :param ip: IP address :param is_add: 1 if add, 0 if delete (Default add) - :param twice_nat: twice NAT address for external hosts """ - flags = self.config_flags.NAT_IS_TWICE_NAT if twice_nat else 0 - self.vapi.nat44_add_del_address_range(first_ip_address=ip, - last_ip_address=ip, - vrf_id=vrf_id, - is_add=is_add, - flags=flags) + self.vapi.nat44_ei_add_del_address_range(first_ip_address=ip, + last_ip_address=ip, + vrf_id=vrf_id, + is_add=is_add) def create_routes_and_neigbors(self): r1 = VppIpRoute(self, self.pg7.remote_ip4, 32, @@ -637,11 +630,11 @@ class MethodHolder(VppTestCase): def verify_no_nat44_user(self): """ Verify that there is no NAT44EI user """ - users = self.vapi.nat44_user_dump() + users = self.vapi.nat44_ei_user_dump() self.assertEqual(len(users), 0) - users = self.statistics.get_counter('/nat44/total-users') + users = self.statistics.get_counter('/nat44-ei/total-users') self.assertEqual(users[0][0], 0) - sessions = self.statistics.get_counter('/nat44/total-sessions') + sessions = self.statistics.get_counter('/nat44-ei/total-sessions') self.assertEqual(sessions[0][0], 0) def verify_syslog_apmap(self, data, is_add=True): @@ -859,33 +852,6 @@ class MethodHolder(VppTestCase): self.assertEqual(data, p[Raw].load) -class TestNAT44EIAPI(MethodHolder): - """ NAT44EI API Test Cases """ - - fq_nelts = 512 - - def setUp(self): - super(TestNAT44EIAPI, self).setUp() - self.vapi.nat_set_fq_options(frame_queue_nelts=self.fq_nelts) - self.vapi.nat44_plugin_enable_disable(enable=1) - - def tearDown(self): - super(TestNAT44EIAPI, self).tearDown() - if not self.vpp_dead: - self.vapi.nat44_plugin_enable_disable(enable=0) - self.vapi.cli("clear logging") - - def test_show_frame_queue_nelts(self): - """ API test - worker handoff frame queue elements """ - nat_config = self.vapi.nat_show_fq_options() - self.assertEqual(self.fq_nelts, nat_config.frame_queue_nelts) - self.vapi.nat44_plugin_enable_disable(enable=0) - self.vapi.cli("set nat frame-queue-nelts 256") - self.vapi.nat44_plugin_enable_disable(enable=1) - nat_config = self.vapi.nat_show_fq_options() - self.assertEqual(256, nat_config.frame_queue_nelts) - - @tag_fixme_vpp_workers class TestNAT44EI(MethodHolder): """ NAT44EI Test Cases """ @@ -896,7 +862,7 @@ class TestNAT44EI(MethodHolder): @classmethod def setUpClass(cls): super(TestNAT44EI, cls).setUpClass() - cls.vapi.cli("set log class nat level debug") + cls.vapi.cli("set log class nat44-ei level debug") cls.tcp_port_in = 6303 cls.tcp_port_out = 6303 @@ -957,39 +923,39 @@ class TestNAT44EI(MethodHolder): cls.pg4._remote_ip4 = cls.pg9._remote_hosts[0]._ip4 = "10.0.0.2" cls.pg9.resolve_arp() - def setUp(self): - super(TestNAT44EI, self).setUp() - self.vapi.nat44_plugin_enable_disable( + def plugin_enable(self): + self.vapi.nat44_ei_plugin_enable_disable( sessions=self.max_translations, users=self.max_users, enable=1) + def setUp(self): + super(TestNAT44EI, self).setUp() + self.plugin_enable() + def tearDown(self): super(TestNAT44EI, self).tearDown() if not self.vpp_dead: - self.vapi.nat_ipfix_enable_disable(domain_id=self.ipfix_domain_id, - src_port=self.ipfix_src_port, - enable=0) + self.vapi.nat44_ei_ipfix_enable_disable( + domain_id=self.ipfix_domain_id, src_port=self.ipfix_src_port, + enable=0) self.ipfix_src_port = 4739 self.ipfix_domain_id = 1 - self.vapi.nat44_plugin_enable_disable(enable=0) + self.vapi.nat44_ei_plugin_enable_disable(enable=0) self.vapi.cli("clear logging") def test_clear_sessions(self): """ NAT44EI session clearing test """ self.nat44_add_address(self.nat_addr) - flags = self.config_flags.NAT_IS_INSIDE - self.vapi.nat44_interface_add_del_feature( + flags = self.config_flags.NAT44_EI_IF_INSIDE + self.vapi.nat44_ei_interface_add_del_feature( sw_if_index=self.pg0.sw_if_index, flags=flags, is_add=1) - self.vapi.nat44_interface_add_del_feature( + self.vapi.nat44_ei_interface_add_del_feature( sw_if_index=self.pg1.sw_if_index, is_add=1) - nat_config = self.vapi.nat_show_config() - self.assertEqual(0, nat_config.endpoint_dependent) - pkts = self.create_stream_in(self.pg0, self.pg1) self.pg0.add_stream(pkts) self.pg_enable_capture(self.pg_interfaces) @@ -997,32 +963,34 @@ class TestNAT44EI(MethodHolder): capture = self.pg1.get_capture(len(pkts)) self.verify_capture_out(capture) - sessions = self.statistics.get_counter('/nat44/total-sessions') + sessions = self.statistics.get_counter('/nat44-ei/total-sessions') self.assertTrue(sessions[0][0] > 0) self.logger.info("sessions before clearing: %s" % sessions[0][0]) - self.vapi.cli("clear nat44 sessions") + self.vapi.cli("clear nat44 ei sessions") - sessions = self.statistics.get_counter('/nat44/total-sessions') + sessions = self.statistics.get_counter('/nat44-ei/total-sessions') self.assertEqual(sessions[0][0], 0) self.logger.info("sessions after clearing: %s" % sessions[0][0]) def test_dynamic(self): """ NAT44EI dynamic translation test """ self.nat44_add_address(self.nat_addr) - flags = self.config_flags.NAT_IS_INSIDE - self.vapi.nat44_interface_add_del_feature( + flags = self.config_flags.NAT44_EI_IF_INSIDE + self.vapi.nat44_ei_interface_add_del_feature( sw_if_index=self.pg0.sw_if_index, flags=flags, is_add=1) - self.vapi.nat44_interface_add_del_feature( + self.vapi.nat44_ei_interface_add_del_feature( sw_if_index=self.pg1.sw_if_index, is_add=1) # in2out - tcpn = self.statistics.get_counter('/nat44/in2out/slowpath/tcp')[0] - udpn = self.statistics.get_counter('/nat44/in2out/slowpath/udp')[0] - icmpn = self.statistics.get_counter('/nat44/in2out/slowpath/icmp')[0] - drops = self.statistics.get_counter('/nat44/in2out/slowpath/drops')[0] + tcpn = self.statistics.get_counter('/nat44-ei/in2out/slowpath/tcp')[0] + udpn = self.statistics.get_counter('/nat44-ei/in2out/slowpath/udp')[0] + icmpn = self.statistics.get_counter( + '/nat44-ei/in2out/slowpath/icmp')[0] + drops = self.statistics.get_counter( + '/nat44-ei/in2out/slowpath/drops')[0] pkts = self.create_stream_in(self.pg0, self.pg1) self.pg0.add_stream(pkts) @@ -1032,20 +1000,22 @@ class TestNAT44EI(MethodHolder): self.verify_capture_out(capture) if_idx = self.pg0.sw_if_index - cnt = self.statistics.get_counter('/nat44/in2out/slowpath/tcp')[0] + cnt = self.statistics.get_counter('/nat44-ei/in2out/slowpath/tcp')[0] self.assertEqual(cnt[if_idx] - tcpn[if_idx], 2) - cnt = self.statistics.get_counter('/nat44/in2out/slowpath/udp')[0] + cnt = self.statistics.get_counter('/nat44-ei/in2out/slowpath/udp')[0] self.assertEqual(cnt[if_idx] - udpn[if_idx], 1) - cnt = self.statistics.get_counter('/nat44/in2out/slowpath/icmp')[0] + cnt = self.statistics.get_counter('/nat44-ei/in2out/slowpath/icmp')[0] self.assertEqual(cnt[if_idx] - icmpn[if_idx], 1) - cnt = self.statistics.get_counter('/nat44/in2out/slowpath/drops')[0] + cnt = self.statistics.get_counter('/nat44-ei/in2out/slowpath/drops')[0] self.assertEqual(cnt[if_idx] - drops[if_idx], 0) # out2in - tcpn = self.statistics.get_counter('/nat44/out2in/slowpath/tcp')[0] - udpn = self.statistics.get_counter('/nat44/out2in/slowpath/udp')[0] - icmpn = self.statistics.get_counter('/nat44/out2in/slowpath/icmp')[0] - drops = self.statistics.get_counter('/nat44/out2in/slowpath/drops')[0] + tcpn = self.statistics.get_counter('/nat44-ei/out2in/slowpath/tcp')[0] + udpn = self.statistics.get_counter('/nat44-ei/out2in/slowpath/udp')[0] + icmpn = self.statistics.get_counter( + '/nat44-ei/out2in/slowpath/icmp')[0] + drops = self.statistics.get_counter( + '/nat44-ei/out2in/slowpath/drops')[0] pkts = self.create_stream_out(self.pg1) self.pg1.add_stream(pkts) @@ -1055,29 +1025,29 @@ class TestNAT44EI(MethodHolder): self.verify_capture_in(capture, self.pg0) if_idx = self.pg1.sw_if_index - cnt = self.statistics.get_counter('/nat44/out2in/slowpath/tcp')[0] + cnt = self.statistics.get_counter('/nat44-ei/out2in/slowpath/tcp')[0] self.assertEqual(cnt[if_idx] - tcpn[if_idx], 2) - cnt = self.statistics.get_counter('/nat44/out2in/slowpath/udp')[0] + cnt = self.statistics.get_counter('/nat44-ei/out2in/slowpath/udp')[0] self.assertEqual(cnt[if_idx] - udpn[if_idx], 1) - cnt = self.statistics.get_counter('/nat44/out2in/slowpath/icmp')[0] + cnt = self.statistics.get_counter('/nat44-ei/out2in/slowpath/icmp')[0] self.assertEqual(cnt[if_idx] - icmpn[if_idx], 1) - cnt = self.statistics.get_counter('/nat44/out2in/slowpath/drops')[0] + cnt = self.statistics.get_counter('/nat44-ei/out2in/slowpath/drops')[0] self.assertEqual(cnt[if_idx] - drops[if_idx], 0) - users = self.statistics.get_counter('/nat44/total-users') + users = self.statistics.get_counter('/nat44-ei/total-users') self.assertEqual(users[0][0], 1) - sessions = self.statistics.get_counter('/nat44/total-sessions') + sessions = self.statistics.get_counter('/nat44-ei/total-sessions') self.assertEqual(sessions[0][0], 3) def test_dynamic_icmp_errors_in2out_ttl_1(self): """ NAT44EI handling of client packets with TTL=1 """ self.nat44_add_address(self.nat_addr) - flags = self.config_flags.NAT_IS_INSIDE - self.vapi.nat44_interface_add_del_feature( + flags = self.config_flags.NAT44_EI_IF_INSIDE + self.vapi.nat44_ei_interface_add_del_feature( sw_if_index=self.pg0.sw_if_index, flags=flags, is_add=1) - self.vapi.nat44_interface_add_del_feature( + self.vapi.nat44_ei_interface_add_del_feature( sw_if_index=self.pg1.sw_if_index, is_add=1) @@ -1095,11 +1065,11 @@ class TestNAT44EI(MethodHolder): """ NAT44EI handling of server packets with TTL=1 """ self.nat44_add_address(self.nat_addr) - flags = self.config_flags.NAT_IS_INSIDE - self.vapi.nat44_interface_add_del_feature( + flags = self.config_flags.NAT44_EI_IF_INSIDE + self.vapi.nat44_ei_interface_add_del_feature( sw_if_index=self.pg0.sw_if_index, flags=flags, is_add=1) - self.vapi.nat44_interface_add_del_feature( + self.vapi.nat44_ei_interface_add_del_feature( sw_if_index=self.pg1.sw_if_index, is_add=1) @@ -1127,11 +1097,11 @@ class TestNAT44EI(MethodHolder): """ self.nat44_add_address(self.nat_addr) - flags = self.config_flags.NAT_IS_INSIDE - self.vapi.nat44_interface_add_del_feature( + flags = self.config_flags.NAT44_EI_IF_INSIDE + self.vapi.nat44_ei_interface_add_del_feature( sw_if_index=self.pg0.sw_if_index, flags=flags, is_add=1) - self.vapi.nat44_interface_add_del_feature( + self.vapi.nat44_ei_interface_add_del_feature( sw_if_index=self.pg1.sw_if_index, is_add=1) @@ -1159,11 +1129,11 @@ class TestNAT44EI(MethodHolder): """ self.nat44_add_address(self.nat_addr) - flags = self.config_flags.NAT_IS_INSIDE - self.vapi.nat44_interface_add_del_feature( + flags = self.config_flags.NAT44_EI_IF_INSIDE + self.vapi.nat44_ei_interface_add_del_feature( sw_if_index=self.pg0.sw_if_index, flags=flags, is_add=1) - self.vapi.nat44_interface_add_del_feature( + self.vapi.nat44_ei_interface_add_del_feature( sw_if_index=self.pg1.sw_if_index, is_add=1) @@ -1198,11 +1168,11 @@ class TestNAT44EI(MethodHolder): """ NAT44EI ping out interface from outside network """ self.nat44_add_address(self.nat_addr) - flags = self.config_flags.NAT_IS_INSIDE - self.vapi.nat44_interface_add_del_feature( + flags = self.config_flags.NAT44_EI_IF_INSIDE + self.vapi.nat44_ei_interface_add_del_feature( sw_if_index=self.pg0.sw_if_index, flags=flags, is_add=1) - self.vapi.nat44_interface_add_del_feature( + self.vapi.nat44_ei_interface_add_del_feature( sw_if_index=self.pg1.sw_if_index, is_add=1) @@ -1229,11 +1199,11 @@ class TestNAT44EI(MethodHolder): """ NAT44EI ping internal host from outside network """ self.nat44_add_static_mapping(self.pg0.remote_ip4, self.nat_addr) - flags = self.config_flags.NAT_IS_INSIDE - self.vapi.nat44_interface_add_del_feature( + flags = self.config_flags.NAT44_EI_IF_INSIDE + self.vapi.nat44_ei_interface_add_del_feature( sw_if_index=self.pg0.sw_if_index, flags=flags, is_add=1) - self.vapi.nat44_interface_add_del_feature( + self.vapi.nat44_ei_interface_add_del_feature( sw_if_index=self.pg1.sw_if_index, is_add=1) @@ -1262,23 +1232,23 @@ class TestNAT44EI(MethodHolder): def test_forwarding(self): """ NAT44EI forwarding test """ - flags = self.config_flags.NAT_IS_INSIDE - self.vapi.nat44_interface_add_del_feature( + flags = self.config_flags.NAT44_EI_IF_INSIDE + self.vapi.nat44_ei_interface_add_del_feature( sw_if_index=self.pg0.sw_if_index, flags=flags, is_add=1) - self.vapi.nat44_interface_add_del_feature( + self.vapi.nat44_ei_interface_add_del_feature( sw_if_index=self.pg1.sw_if_index, is_add=1) - self.vapi.nat44_forwarding_enable_disable(enable=1) + self.vapi.nat44_ei_forwarding_enable_disable(enable=1) real_ip = self.pg0.remote_ip4 alias_ip = self.nat_addr - flags = self.config_flags.NAT_IS_ADDR_ONLY - self.vapi.nat44_add_del_static_mapping(is_add=1, - local_ip_address=real_ip, - external_ip_address=alias_ip, - external_sw_if_index=0xFFFFFFFF, - flags=flags) + flags = self.config_flags.NAT44_EI_ADDR_ONLY_MAPPING + self.vapi.nat44_ei_add_del_static_mapping( + is_add=1, local_ip_address=real_ip, + external_ip_address=alias_ip, + external_sw_if_index=0xFFFFFFFF, + flags=flags) try: # static mapping match @@ -1322,9 +1292,9 @@ class TestNAT44EI(MethodHolder): self.pg0.remote_hosts[0] = host0 finally: - self.vapi.nat44_forwarding_enable_disable(enable=0) - flags = self.config_flags.NAT_IS_ADDR_ONLY - self.vapi.nat44_add_del_static_mapping( + self.vapi.nat44_ei_forwarding_enable_disable(enable=0) + flags = self.config_flags.NAT44_EI_ADDR_ONLY_MAPPING + self.vapi.nat44_ei_add_del_static_mapping( is_add=0, local_ip_address=real_ip, external_ip_address=alias_ip, @@ -1340,14 +1310,14 @@ class TestNAT44EI(MethodHolder): self.icmp_id_out = 6305 self.nat44_add_static_mapping(self.pg0.remote_ip4, nat_ip) - flags = self.config_flags.NAT_IS_INSIDE - self.vapi.nat44_interface_add_del_feature( + flags = self.config_flags.NAT44_EI_IF_INSIDE + self.vapi.nat44_ei_interface_add_del_feature( sw_if_index=self.pg0.sw_if_index, flags=flags, is_add=1) - self.vapi.nat44_interface_add_del_feature( + self.vapi.nat44_ei_interface_add_del_feature( sw_if_index=self.pg1.sw_if_index, is_add=1) - sm = self.vapi.nat44_static_mapping_dump() + sm = self.vapi.nat44_ei_static_mapping_dump() self.assertEqual(len(sm), 1) self.assertEqual(sm[0].tag, '') self.assertEqual(sm[0].protocol, 0) @@ -1380,14 +1350,14 @@ class TestNAT44EI(MethodHolder): tag = "testTAG" self.nat44_add_static_mapping(self.pg0.remote_ip4, nat_ip, tag=tag) - flags = self.config_flags.NAT_IS_INSIDE - self.vapi.nat44_interface_add_del_feature( + flags = self.config_flags.NAT44_EI_IF_INSIDE + self.vapi.nat44_ei_interface_add_del_feature( sw_if_index=self.pg0.sw_if_index, flags=flags, is_add=1) - self.vapi.nat44_interface_add_del_feature( + self.vapi.nat44_ei_interface_add_del_feature( sw_if_index=self.pg1.sw_if_index, is_add=1) - sm = self.vapi.nat44_static_mapping_dump() + sm = self.vapi.nat44_ei_static_mapping_dump() self.assertEqual(len(sm), 1) self.assertEqual(sm[0].tag, tag) @@ -1424,11 +1394,11 @@ class TestNAT44EI(MethodHolder): self.nat44_add_static_mapping(self.pg0.remote_ip4, self.nat_addr, self.icmp_id_in, self.icmp_id_out, proto=IP_PROTOS.icmp) - flags = self.config_flags.NAT_IS_INSIDE - self.vapi.nat44_interface_add_del_feature( + flags = self.config_flags.NAT44_EI_IF_INSIDE + self.vapi.nat44_ei_interface_add_del_feature( sw_if_index=self.pg0.sw_if_index, flags=flags, is_add=1) - self.vapi.nat44_interface_add_del_feature( + self.vapi.nat44_ei_interface_add_del_feature( sw_if_index=self.pg1.sw_if_index, is_add=1) @@ -1465,11 +1435,11 @@ class TestNAT44EI(MethodHolder): self.nat44_add_static_mapping(self.pg0.remote_ip4, self.nat_addr, self.icmp_id_in, self.icmp_id_out, proto=IP_PROTOS.icmp) - flags = self.config_flags.NAT_IS_INSIDE - self.vapi.nat44_interface_add_del_feature( + flags = self.config_flags.NAT44_EI_IF_INSIDE + self.vapi.nat44_ei_interface_add_del_feature( sw_if_index=self.pg0.sw_if_index, flags=flags, is_add=1) - self.vapi.nat44_interface_add_del_feature( + self.vapi.nat44_ei_interface_add_del_feature( sw_if_index=self.pg1.sw_if_index, is_add=1) @@ -1502,14 +1472,14 @@ class TestNAT44EI(MethodHolder): vrf_id=10) self.nat44_add_static_mapping(self.pg0.remote_ip4, nat_ip2, vrf_id=10) - flags = self.config_flags.NAT_IS_INSIDE - self.vapi.nat44_interface_add_del_feature( + flags = self.config_flags.NAT44_EI_IF_INSIDE + self.vapi.nat44_ei_interface_add_del_feature( sw_if_index=self.pg3.sw_if_index, is_add=1) - self.vapi.nat44_interface_add_del_feature( + self.vapi.nat44_ei_interface_add_del_feature( sw_if_index=self.pg0.sw_if_index, flags=flags, is_add=1) - self.vapi.nat44_interface_add_del_feature( + self.vapi.nat44_ei_interface_add_del_feature( sw_if_index=self.pg4.sw_if_index, flags=flags, is_add=1) @@ -1537,11 +1507,11 @@ class TestNAT44EI(MethodHolder): self.icmp_id_out = 6305 self.nat44_add_address(self.nat_addr) - flags = self.config_flags.NAT_IS_INSIDE - self.vapi.nat44_interface_add_del_feature( + flags = self.config_flags.NAT44_EI_IF_INSIDE + self.vapi.nat44_ei_interface_add_del_feature( sw_if_index=self.pg0.sw_if_index, flags=flags, is_add=1) - self.vapi.nat44_interface_add_del_feature( + self.vapi.nat44_ei_interface_add_del_feature( sw_if_index=self.pg1.sw_if_index, is_add=1) @@ -1555,7 +1525,7 @@ class TestNAT44EI(MethodHolder): # 1:1NAT self.nat44_add_static_mapping(self.pg0.remote_ip4, nat_ip) - sessions = self.vapi.nat44_user_session_dump(self.pg0.remote_ip4, 0) + sessions = self.vapi.nat44_ei_user_session_dump(self.pg0.remote_ip4, 0) self.assertEqual(len(sessions), 0) pkts = self.create_stream_in(self.pg0, self.pg1) self.pg0.add_stream(pkts) @@ -1566,15 +1536,15 @@ class TestNAT44EI(MethodHolder): def test_identity_nat(self): """ NAT44EI Identity NAT """ - flags = self.config_flags.NAT_IS_ADDR_ONLY - self.vapi.nat44_add_del_identity_mapping( + flags = self.config_flags.NAT44_EI_ADDR_ONLY_MAPPING + self.vapi.nat44_ei_add_del_identity_mapping( ip_address=self.pg0.remote_ip4, sw_if_index=0xFFFFFFFF, flags=flags, is_add=1) - flags = self.config_flags.NAT_IS_INSIDE - self.vapi.nat44_interface_add_del_feature( + flags = self.config_flags.NAT44_EI_IF_INSIDE + self.vapi.nat44_ei_interface_add_del_feature( sw_if_index=self.pg0.sw_if_index, flags=flags, is_add=1) - self.vapi.nat44_interface_add_del_feature( + self.vapi.nat44_ei_interface_add_del_feature( sw_if_index=self.pg1.sw_if_index, is_add=1) @@ -1598,13 +1568,13 @@ class TestNAT44EI(MethodHolder): self.logger.error(ppp("Unexpected or invalid packet:", p)) raise - sessions = self.vapi.nat44_user_session_dump(self.pg0.remote_ip4, 0) + sessions = self.vapi.nat44_ei_user_session_dump(self.pg0.remote_ip4, 0) self.assertEqual(len(sessions), 0) - flags = self.config_flags.NAT_IS_ADDR_ONLY - self.vapi.nat44_add_del_identity_mapping( + flags = self.config_flags.NAT44_EI_ADDR_ONLY_MAPPING + self.vapi.nat44_ei_add_del_identity_mapping( ip_address=self.pg0.remote_ip4, sw_if_index=0xFFFFFFFF, flags=flags, vrf_id=1, is_add=1) - identity_mappings = self.vapi.nat44_identity_mapping_dump() + identity_mappings = self.vapi.nat44_ei_identity_mapping_dump() self.assertEqual(len(identity_mappings), 2) def test_multiple_inside_interfaces(self): @@ -1612,14 +1582,14 @@ class TestNAT44EI(MethodHolder): """ self.nat44_add_address(self.nat_addr) - flags = self.config_flags.NAT_IS_INSIDE - self.vapi.nat44_interface_add_del_feature( + flags = self.config_flags.NAT44_EI_IF_INSIDE + self.vapi.nat44_ei_interface_add_del_feature( sw_if_index=self.pg0.sw_if_index, flags=flags, is_add=1) - self.vapi.nat44_interface_add_del_feature( + self.vapi.nat44_ei_interface_add_del_feature( sw_if_index=self.pg1.sw_if_index, flags=flags, is_add=1) - self.vapi.nat44_interface_add_del_feature( + self.vapi.nat44_ei_interface_add_del_feature( sw_if_index=self.pg3.sw_if_index, is_add=1) @@ -1677,17 +1647,17 @@ class TestNAT44EI(MethodHolder): static_nat_ip = "10.0.0.10" self.nat44_add_address(self.nat_addr) - flags = self.config_flags.NAT_IS_INSIDE - self.vapi.nat44_interface_add_del_feature( + flags = self.config_flags.NAT44_EI_IF_INSIDE + self.vapi.nat44_ei_interface_add_del_feature( sw_if_index=self.pg3.sw_if_index, is_add=1) - self.vapi.nat44_interface_add_del_feature( + self.vapi.nat44_ei_interface_add_del_feature( sw_if_index=self.pg4.sw_if_index, flags=flags, is_add=1) - self.vapi.nat44_interface_add_del_feature( + self.vapi.nat44_ei_interface_add_del_feature( sw_if_index=self.pg5.sw_if_index, flags=flags, is_add=1) - self.vapi.nat44_interface_add_del_feature( + self.vapi.nat44_ei_interface_add_del_feature( sw_if_index=self.pg6.sw_if_index, flags=flags, is_add=1) self.nat44_add_static_mapping(self.pg6.remote_ip4, static_nat_ip, @@ -1754,12 +1724,14 @@ class TestNAT44EI(MethodHolder): self.verify_capture_in(capture, self.pg5) # pg5 session dump - addresses = self.vapi.nat44_address_dump() + addresses = self.vapi.nat44_ei_address_dump() self.assertEqual(len(addresses), 1) - sessions = self.vapi.nat44_user_session_dump(self.pg5.remote_ip4, 10) + sessions = self.vapi.nat44_ei_user_session_dump( + self.pg5.remote_ip4, 10) self.assertEqual(len(sessions), 3) for session in sessions: - self.assertFalse(session.flags & self.config_flags.NAT_IS_STATIC) + self.assertFalse(session.flags & + self.config_flags.NAT44_EI_STATIC_MAPPING) self.assertEqual(str(session.inside_ip_address), self.pg5.remote_ip4) self.assertEqual(session.outside_ip_address, @@ -1791,37 +1763,39 @@ class TestNAT44EI(MethodHolder): self.verify_capture_in(capture, self.pg6) # general user and session dump verifications - users = self.vapi.nat44_user_dump() + users = self.vapi.nat44_ei_user_dump() self.assertGreaterEqual(len(users), 3) - addresses = self.vapi.nat44_address_dump() + addresses = self.vapi.nat44_ei_address_dump() self.assertEqual(len(addresses), 1) for user in users: - sessions = self.vapi.nat44_user_session_dump(user.ip_address, - user.vrf_id) + sessions = self.vapi.nat44_ei_user_session_dump(user.ip_address, + user.vrf_id) for session in sessions: self.assertEqual(user.ip_address, session.inside_ip_address) self.assertTrue(session.total_bytes > session.total_pkts > 0) self.assertTrue(session.protocol in [IP_PROTOS.tcp, IP_PROTOS.udp, IP_PROTOS.icmp]) - self.assertFalse(session.flags & - self.config_flags.NAT_IS_EXT_HOST_VALID) # pg4 session dump - sessions = self.vapi.nat44_user_session_dump(self.pg4.remote_ip4, 10) + sessions = self.vapi.nat44_ei_user_session_dump( + self.pg4.remote_ip4, 10) self.assertGreaterEqual(len(sessions), 4) for session in sessions: - self.assertFalse(session.flags & self.config_flags.NAT_IS_STATIC) + self.assertFalse( + session.flags & self.config_flags.NAT44_EI_STATIC_MAPPING) self.assertEqual(str(session.inside_ip_address), self.pg4.remote_ip4) self.assertEqual(session.outside_ip_address, addresses[0].ip_address) # pg6 session dump - sessions = self.vapi.nat44_user_session_dump(self.pg6.remote_ip4, 20) + sessions = self.vapi.nat44_ei_user_session_dump( + self.pg6.remote_ip4, 20) self.assertGreaterEqual(len(sessions), 3) for session in sessions: - self.assertTrue(session.flags & self.config_flags.NAT_IS_STATIC) + self.assertTrue( + session.flags & self.config_flags.NAT44_EI_STATIC_MAPPING) self.assertEqual(str(session.inside_ip_address), self.pg6.remote_ip4) self.assertEqual(str(session.outside_ip_address), @@ -1841,11 +1815,11 @@ class TestNAT44EI(MethodHolder): server_out_port = 8765 self.nat44_add_address(self.nat_addr) - flags = self.config_flags.NAT_IS_INSIDE - self.vapi.nat44_interface_add_del_feature( + flags = self.config_flags.NAT44_EI_IF_INSIDE + self.vapi.nat44_ei_interface_add_del_feature( sw_if_index=self.pg0.sw_if_index, flags=flags, is_add=1) - self.vapi.nat44_interface_add_del_feature( + self.vapi.nat44_ei_interface_add_del_feature( sw_if_index=self.pg1.sw_if_index, is_add=1) @@ -1854,7 +1828,7 @@ class TestNAT44EI(MethodHolder): server_in_port, server_out_port, proto=IP_PROTOS.tcp) - cnt = self.statistics.get_counter('/nat44/hairpinning')[0] + cnt = self.statistics.get_counter('/nat44-ei/hairpinning')[0] # send packet from host to server p = (Ether(src=host.mac, dst=self.pg0.local_mac) / IP(src=host.ip4, dst=self.nat_addr) / @@ -1877,7 +1851,7 @@ class TestNAT44EI(MethodHolder): self.logger.error(ppp("Unexpected or invalid packet:", p)) raise - after = self.statistics.get_counter('/nat44/hairpinning')[0] + after = self.statistics.get_counter('/nat44-ei/hairpinning')[0] if_idx = self.pg0.sw_if_index self.assertEqual(after[if_idx] - cnt[if_idx], 1) @@ -1902,7 +1876,7 @@ class TestNAT44EI(MethodHolder): self.logger.error(ppp("Unexpected or invalid packet:", p)) raise - after = self.statistics.get_counter('/nat44/hairpinning')[0] + after = self.statistics.get_counter('/nat44-ei/hairpinning')[0] if_idx = self.pg0.sw_if_index self.assertEqual(after[if_idx] - cnt[if_idx], 2) @@ -1918,11 +1892,11 @@ class TestNAT44EI(MethodHolder): server_udp_port = 20 self.nat44_add_address(self.nat_addr) - flags = self.config_flags.NAT_IS_INSIDE - self.vapi.nat44_interface_add_del_feature( + flags = self.config_flags.NAT44_EI_IF_INSIDE + self.vapi.nat44_ei_interface_add_del_feature( sw_if_index=self.pg0.sw_if_index, flags=flags, is_add=1) - self.vapi.nat44_interface_add_del_feature( + self.vapi.nat44_ei_interface_add_del_feature( sw_if_index=self.pg1.sw_if_index, is_add=1) @@ -2077,7 +2051,7 @@ class TestNAT44EI(MethodHolder): raise def test_hairpinning_avoid_inf_loop(self): - """ NAT44 hairpinning - 1:1 NAPT avoid infinite loop """ + """ NAT44EI hairpinning - 1:1 NAPT avoid infinite loop """ host = self.pg0.remote_hosts[0] server = self.pg0.remote_hosts[1] @@ -2087,11 +2061,11 @@ class TestNAT44EI(MethodHolder): server_out_port = 8765 self.nat44_add_address(self.nat_addr) - flags = self.config_flags.NAT_IS_INSIDE - self.vapi.nat44_interface_add_del_feature( + flags = self.config_flags.NAT44_EI_IF_INSIDE + self.vapi.nat44_ei_interface_add_del_feature( sw_if_index=self.pg0.sw_if_index, flags=flags, is_add=1) - self.vapi.nat44_interface_add_del_feature( + self.vapi.nat44_ei_interface_add_del_feature( sw_if_index=self.pg1.sw_if_index, is_add=1) @@ -2112,7 +2086,7 @@ class TestNAT44EI(MethodHolder): self.pg_start() # Here VPP used to crash due to an infinite loop - cnt = self.statistics.get_counter('/nat44/hairpinning')[0] + cnt = self.statistics.get_counter('/nat44-ei/hairpinning')[0] # send packet from host to server p = (Ether(src=host.mac, dst=self.pg0.local_mac) / IP(src=host.ip4, dst=self.nat_addr) / @@ -2135,7 +2109,7 @@ class TestNAT44EI(MethodHolder): self.logger.error(ppp("Unexpected or invalid packet:", p)) raise - after = self.statistics.get_counter('/nat44/hairpinning')[0] + after = self.statistics.get_counter('/nat44-ei/hairpinning')[0] if_idx = self.pg0.sw_if_index self.assertEqual(after[if_idx] - cnt[if_idx], 1) @@ -2160,36 +2134,36 @@ class TestNAT44EI(MethodHolder): self.logger.error(ppp("Unexpected or invalid packet:", p)) raise - after = self.statistics.get_counter('/nat44/hairpinning')[0] + after = self.statistics.get_counter('/nat44-ei/hairpinning')[0] if_idx = self.pg0.sw_if_index self.assertEqual(after[if_idx] - cnt[if_idx], 2) def test_interface_addr(self): """ NAT44EI acquire addresses from interface """ - self.vapi.nat44_add_del_interface_addr( + self.vapi.nat44_ei_add_del_interface_addr( is_add=1, sw_if_index=self.pg7.sw_if_index) # no address in NAT pool - addresses = self.vapi.nat44_address_dump() + addresses = self.vapi.nat44_ei_address_dump() self.assertEqual(0, len(addresses)) # configure interface address and check NAT address pool self.pg7.config_ip4() - addresses = self.vapi.nat44_address_dump() + addresses = self.vapi.nat44_ei_address_dump() self.assertEqual(1, len(addresses)) self.assertEqual(str(addresses[0].ip_address), self.pg7.local_ip4) # remove interface address and check NAT address pool self.pg7.unconfig_ip4() - addresses = self.vapi.nat44_address_dump() + addresses = self.vapi.nat44_ei_address_dump() self.assertEqual(0, len(addresses)) def test_interface_addr_static_mapping(self): """ NAT44EI Static mapping with addresses from interface """ tag = "testTAG" - self.vapi.nat44_add_del_interface_addr( + self.vapi.nat44_ei_add_del_interface_addr( is_add=1, sw_if_index=self.pg7.sw_if_index) self.nat44_add_static_mapping( @@ -2198,7 +2172,7 @@ class TestNAT44EI(MethodHolder): tag=tag) # static mappings with external interface - static_mappings = self.vapi.nat44_static_mapping_dump() + static_mappings = self.vapi.nat44_ei_static_mapping_dump() self.assertEqual(1, len(static_mappings)) self.assertEqual(self.pg7.sw_if_index, static_mappings[0].external_sw_if_index) @@ -2206,7 +2180,7 @@ class TestNAT44EI(MethodHolder): # configure interface address and check static mappings self.pg7.config_ip4() - static_mappings = self.vapi.nat44_static_mapping_dump() + static_mappings = self.vapi.nat44_ei_static_mapping_dump() self.assertEqual(2, len(static_mappings)) resolved = False for sm in static_mappings: @@ -2219,7 +2193,7 @@ class TestNAT44EI(MethodHolder): # remove interface address and check static mappings self.pg7.unconfig_ip4() - static_mappings = self.vapi.nat44_static_mapping_dump() + static_mappings = self.vapi.nat44_ei_static_mapping_dump() self.assertEqual(1, len(static_mappings)) self.assertEqual(self.pg7.sw_if_index, static_mappings[0].external_sw_if_index) @@ -2227,7 +2201,7 @@ class TestNAT44EI(MethodHolder): # configure interface address again and check static mappings self.pg7.config_ip4() - static_mappings = self.vapi.nat44_static_mapping_dump() + static_mappings = self.vapi.nat44_ei_static_mapping_dump() self.assertEqual(2, len(static_mappings)) resolved = False for sm in static_mappings: @@ -2244,17 +2218,17 @@ class TestNAT44EI(MethodHolder): external_sw_if_index=self.pg7.sw_if_index, tag=tag, is_add=0) - static_mappings = self.vapi.nat44_static_mapping_dump() + static_mappings = self.vapi.nat44_ei_static_mapping_dump() self.assertEqual(0, len(static_mappings)) def test_interface_addr_identity_nat(self): """ NAT44EI Identity NAT with addresses from interface """ port = 53053 - self.vapi.nat44_add_del_interface_addr( + self.vapi.nat44_ei_add_del_interface_addr( is_add=1, sw_if_index=self.pg7.sw_if_index) - self.vapi.nat44_add_del_identity_mapping( + self.vapi.nat44_ei_add_del_identity_mapping( ip_address=b'0', sw_if_index=self.pg7.sw_if_index, port=port, @@ -2262,14 +2236,14 @@ class TestNAT44EI(MethodHolder): is_add=1) # identity mappings with external interface - identity_mappings = self.vapi.nat44_identity_mapping_dump() + identity_mappings = self.vapi.nat44_ei_identity_mapping_dump() self.assertEqual(1, len(identity_mappings)) self.assertEqual(self.pg7.sw_if_index, identity_mappings[0].sw_if_index) # configure interface address and check identity mappings self.pg7.config_ip4() - identity_mappings = self.vapi.nat44_identity_mapping_dump() + identity_mappings = self.vapi.nat44_ei_identity_mapping_dump() resolved = False self.assertEqual(2, len(identity_mappings)) for sm in identity_mappings: @@ -2283,7 +2257,7 @@ class TestNAT44EI(MethodHolder): # remove interface address and check identity mappings self.pg7.unconfig_ip4() - identity_mappings = self.vapi.nat44_identity_mapping_dump() + identity_mappings = self.vapi.nat44_ei_identity_mapping_dump() self.assertEqual(1, len(identity_mappings)) self.assertEqual(self.pg7.sw_if_index, identity_mappings[0].sw_if_index) @@ -2295,11 +2269,11 @@ class TestNAT44EI(MethodHolder): collector_port = 30303 bind_layers(UDP, IPFIX, dport=30303) self.nat44_add_address(self.nat_addr) - flags = self.config_flags.NAT_IS_INSIDE - self.vapi.nat44_interface_add_del_feature( + flags = self.config_flags.NAT44_EI_IF_INSIDE + self.vapi.nat44_ei_interface_add_del_feature( sw_if_index=self.pg0.sw_if_index, flags=flags, is_add=1) - self.vapi.nat44_interface_add_del_feature( + self.vapi.nat44_ei_interface_add_del_feature( sw_if_index=self.pg1.sw_if_index, is_add=1) self.vapi.set_ipfix_exporter(collector_address=self.pg3.remote_ip4, @@ -2307,9 +2281,9 @@ class TestNAT44EI(MethodHolder): path_mtu=512, template_interval=10, collector_port=collector_port) - self.vapi.nat_ipfix_enable_disable(domain_id=self.ipfix_domain_id, - src_port=self.ipfix_src_port, - enable=1) + self.vapi.nat44_ei_ipfix_enable_disable(domain_id=self.ipfix_domain_id, + src_port=self.ipfix_src_port, + enable=1) pkts = self.create_stream_in(self.pg0, self.pg1) self.pg0.add_stream(pkts) @@ -2340,20 +2314,20 @@ class TestNAT44EI(MethodHolder): def test_ipfix_addr_exhausted(self): """ NAT44EI IPFIX logging NAT addresses exhausted """ - flags = self.config_flags.NAT_IS_INSIDE - self.vapi.nat44_interface_add_del_feature( + flags = self.config_flags.NAT44_EI_IF_INSIDE + self.vapi.nat44_ei_interface_add_del_feature( sw_if_index=self.pg0.sw_if_index, flags=flags, is_add=1) - self.vapi.nat44_interface_add_del_feature( + self.vapi.nat44_ei_interface_add_del_feature( sw_if_index=self.pg1.sw_if_index, is_add=1) self.vapi.set_ipfix_exporter(collector_address=self.pg3.remote_ip4, src_address=self.pg3.local_ip4, path_mtu=512, template_interval=10) - self.vapi.nat_ipfix_enable_disable(domain_id=self.ipfix_domain_id, - src_port=self.ipfix_src_port, - enable=1) + self.vapi.nat44_ei_ipfix_enable_disable(domain_id=self.ipfix_domain_id, + src_port=self.ipfix_src_port, + enable=1) p = (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) / IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4) / @@ -2386,11 +2360,11 @@ class TestNAT44EI(MethodHolder): def test_ipfix_max_sessions(self): """ NAT44EI IPFIX logging maximum session entries exceeded """ self.nat44_add_address(self.nat_addr) - flags = self.config_flags.NAT_IS_INSIDE - self.vapi.nat44_interface_add_del_feature( + flags = self.config_flags.NAT44_EI_IF_INSIDE + self.vapi.nat44_ei_interface_add_del_feature( sw_if_index=self.pg0.sw_if_index, flags=flags, is_add=1) - self.vapi.nat44_interface_add_del_feature( + self.vapi.nat44_ei_interface_add_del_feature( sw_if_index=self.pg1.sw_if_index, is_add=1) @@ -2412,9 +2386,9 @@ class TestNAT44EI(MethodHolder): src_address=self.pg3.local_ip4, path_mtu=512, template_interval=10) - self.vapi.nat_ipfix_enable_disable(domain_id=self.ipfix_domain_id, - src_port=self.ipfix_src_port, - enable=1) + self.vapi.nat44_ei_ipfix_enable_disable(domain_id=self.ipfix_domain_id, + src_port=self.ipfix_src_port, + enable=1) p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4) / @@ -2450,11 +2424,11 @@ class TestNAT44EI(MethodHolder): self.SYSLOG_SEVERITY.SYSLOG_API_SEVERITY_INFO) self.vapi.syslog_set_sender(self.pg3.local_ip4, self.pg3.remote_ip4) self.nat44_add_address(self.nat_addr) - flags = self.config_flags.NAT_IS_INSIDE - self.vapi.nat44_interface_add_del_feature( + flags = self.config_flags.NAT44_EI_IF_INSIDE + self.vapi.nat44_ei_interface_add_del_feature( sw_if_index=self.pg0.sw_if_index, flags=flags, is_add=1) - self.vapi.nat44_interface_add_del_feature( + self.vapi.nat44_ei_interface_add_del_feature( sw_if_index=self.pg1.sw_if_index, is_add=1) @@ -2479,11 +2453,11 @@ class TestNAT44EI(MethodHolder): """ NAT44EI add pool addresses to FIB """ static_addr = '10.0.0.10' self.nat44_add_address(self.nat_addr) - flags = self.config_flags.NAT_IS_INSIDE - self.vapi.nat44_interface_add_del_feature( + flags = self.config_flags.NAT44_EI_IF_INSIDE + self.vapi.nat44_ei_interface_add_del_feature( sw_if_index=self.pg0.sw_if_index, flags=flags, is_add=1) - self.vapi.nat44_interface_add_del_feature( + self.vapi.nat44_ei_interface_add_del_feature( sw_if_index=self.pg1.sw_if_index, is_add=1) self.nat44_add_static_mapping(self.pg0.remote_ip4, static_addr) @@ -2561,14 +2535,14 @@ class TestNAT44EI(MethodHolder): self.nat44_add_address(nat_ip1, vrf_id=vrf_id1) self.nat44_add_address(nat_ip2, vrf_id=vrf_id2) - flags = self.config_flags.NAT_IS_INSIDE - self.vapi.nat44_interface_add_del_feature( + flags = self.config_flags.NAT44_EI_IF_INSIDE + self.vapi.nat44_ei_interface_add_del_feature( sw_if_index=self.pg0.sw_if_index, flags=flags, is_add=1) - self.vapi.nat44_interface_add_del_feature( + self.vapi.nat44_ei_interface_add_del_feature( sw_if_index=self.pg1.sw_if_index, flags=flags, is_add=1) - self.vapi.nat44_interface_add_del_feature( + self.vapi.nat44_ei_interface_add_del_feature( sw_if_index=self.pg2.sw_if_index, is_add=1) @@ -2609,14 +2583,14 @@ class TestNAT44EI(MethodHolder): self.nat44_add_address(nat_ip1) self.nat44_add_address(nat_ip2, vrf_id=99) - flags = self.config_flags.NAT_IS_INSIDE - self.vapi.nat44_interface_add_del_feature( + flags = self.config_flags.NAT44_EI_IF_INSIDE + self.vapi.nat44_ei_interface_add_del_feature( sw_if_index=self.pg0.sw_if_index, flags=flags, is_add=1) - self.vapi.nat44_interface_add_del_feature( + self.vapi.nat44_ei_interface_add_del_feature( sw_if_index=self.pg1.sw_if_index, flags=flags, is_add=1) - self.vapi.nat44_interface_add_del_feature( + self.vapi.nat44_ei_interface_add_del_feature( sw_if_index=self.pg2.sw_if_index, is_add=1) @@ -2640,11 +2614,11 @@ class TestNAT44EI(MethodHolder): """ NAT44EI interfaces without configured IP address """ self.create_routes_and_neigbors() self.nat44_add_address(self.nat_addr) - flags = self.config_flags.NAT_IS_INSIDE - self.vapi.nat44_interface_add_del_feature( + flags = self.config_flags.NAT44_EI_IF_INSIDE + self.vapi.nat44_ei_interface_add_del_feature( sw_if_index=self.pg7.sw_if_index, flags=flags, is_add=1) - self.vapi.nat44_interface_add_del_feature( + self.vapi.nat44_ei_interface_add_del_feature( sw_if_index=self.pg8.sw_if_index, is_add=1) @@ -2669,11 +2643,11 @@ class TestNAT44EI(MethodHolder): self.create_routes_and_neigbors() self.nat44_add_static_mapping(self.pg7.remote_ip4, self.nat_addr) - flags = self.config_flags.NAT_IS_INSIDE - self.vapi.nat44_interface_add_del_feature( + flags = self.config_flags.NAT44_EI_IF_INSIDE + self.vapi.nat44_ei_interface_add_del_feature( sw_if_index=self.pg7.sw_if_index, flags=flags, is_add=1) - self.vapi.nat44_interface_add_del_feature( + self.vapi.nat44_ei_interface_add_del_feature( sw_if_index=self.pg8.sw_if_index, is_add=1) @@ -2711,11 +2685,11 @@ class TestNAT44EI(MethodHolder): self.nat44_add_static_mapping(self.pg7.remote_ip4, self.nat_addr, self.icmp_id_in, self.icmp_id_out, proto=IP_PROTOS.icmp) - flags = self.config_flags.NAT_IS_INSIDE - self.vapi.nat44_interface_add_del_feature( + flags = self.config_flags.NAT44_EI_IF_INSIDE + self.vapi.nat44_ei_interface_add_del_feature( sw_if_index=self.pg7.sw_if_index, flags=flags, is_add=1) - self.vapi.nat44_interface_add_del_feature( + self.vapi.nat44_ei_interface_add_del_feature( sw_if_index=self.pg8.sw_if_index, is_add=1) @@ -2739,11 +2713,11 @@ class TestNAT44EI(MethodHolder): """ NAT44EI 1:1 translate packet with unknown protocol """ nat_ip = "10.0.0.10" self.nat44_add_static_mapping(self.pg0.remote_ip4, nat_ip) - flags = self.config_flags.NAT_IS_INSIDE - self.vapi.nat44_interface_add_del_feature( + flags = self.config_flags.NAT44_EI_IF_INSIDE + self.vapi.nat44_ei_interface_add_del_feature( sw_if_index=self.pg0.sw_if_index, flags=flags, is_add=1) - self.vapi.nat44_interface_add_del_feature( + self.vapi.nat44_ei_interface_add_del_feature( sw_if_index=self.pg1.sw_if_index, is_add=1) @@ -2799,11 +2773,11 @@ class TestNAT44EI(MethodHolder): self.nat44_add_static_mapping(host.ip4, host_nat_ip) self.nat44_add_static_mapping(server.ip4, server_nat_ip) - flags = self.config_flags.NAT_IS_INSIDE - self.vapi.nat44_interface_add_del_feature( + flags = self.config_flags.NAT44_EI_IF_INSIDE + self.vapi.nat44_ei_interface_add_del_feature( sw_if_index=self.pg0.sw_if_index, flags=flags, is_add=1) - self.vapi.nat44_interface_add_del_feature( + self.vapi.nat44_ei_interface_add_del_feature( sw_if_index=self.pg1.sw_if_index, is_add=1) @@ -2850,14 +2824,14 @@ class TestNAT44EI(MethodHolder): def test_output_feature(self): """ NAT44EI output feature (in2out postrouting) """ self.nat44_add_address(self.nat_addr) - flags = self.config_flags.NAT_IS_INSIDE - self.vapi.nat44_interface_add_del_output_feature( + flags = self.config_flags.NAT44_EI_IF_INSIDE + self.vapi.nat44_ei_interface_add_del_output_feature( is_add=1, flags=flags, sw_if_index=self.pg0.sw_if_index) - self.vapi.nat44_interface_add_del_output_feature( + self.vapi.nat44_ei_interface_add_del_output_feature( is_add=1, flags=flags, sw_if_index=self.pg1.sw_if_index) - self.vapi.nat44_interface_add_del_output_feature( + self.vapi.nat44_ei_interface_add_del_output_feature( is_add=1, sw_if_index=self.pg3.sw_if_index) @@ -2903,14 +2877,14 @@ class TestNAT44EI(MethodHolder): self.nat44_add_address(nat_ip_vrf10, vrf_id=10) self.nat44_add_address(nat_ip_vrf20, vrf_id=20) - flags = self.config_flags.NAT_IS_INSIDE - self.vapi.nat44_interface_add_del_output_feature( + flags = self.config_flags.NAT44_EI_IF_INSIDE + self.vapi.nat44_ei_interface_add_del_output_feature( is_add=1, flags=flags, sw_if_index=self.pg4.sw_if_index) - self.vapi.nat44_interface_add_del_output_feature( + self.vapi.nat44_ei_interface_add_del_output_feature( is_add=1, flags=flags, sw_if_index=self.pg6.sw_if_index) - self.vapi.nat44_interface_add_del_output_feature( + self.vapi.nat44_ei_interface_add_del_output_feature( is_add=1, sw_if_index=self.pg3.sw_if_index) @@ -2956,11 +2930,11 @@ class TestNAT44EI(MethodHolder): server_out_port = 8765 self.nat44_add_address(self.nat_addr) - flags = self.config_flags.NAT_IS_INSIDE - self.vapi.nat44_interface_add_del_output_feature( + flags = self.config_flags.NAT44_EI_IF_INSIDE + self.vapi.nat44_ei_interface_add_del_output_feature( is_add=1, flags=flags, sw_if_index=self.pg0.sw_if_index) - self.vapi.nat44_interface_add_del_output_feature( + self.vapi.nat44_ei_interface_add_del_output_feature( is_add=1, sw_if_index=self.pg1.sw_if_index) @@ -3019,11 +2993,11 @@ class TestNAT44EI(MethodHolder): external_port = 0 self.nat44_add_address(self.nat_addr) - flags = self.config_flags.NAT_IS_INSIDE - self.vapi.nat44_interface_add_del_feature( + flags = self.config_flags.NAT44_EI_IF_INSIDE + self.vapi.nat44_ei_interface_add_del_feature( sw_if_index=self.pg9.sw_if_index, is_add=1) - self.vapi.nat44_interface_add_del_feature( + self.vapi.nat44_ei_interface_add_del_feature( sw_if_index=self.pg9.sw_if_index, flags=flags, is_add=1) @@ -3071,20 +3045,20 @@ class TestNAT44EI(MethodHolder): raise err = self.statistics.get_err_counter( - '/err/nat44-classify/next in2out') + '/err/nat44-ei-classify/next in2out') self.assertEqual(err, 1) err = self.statistics.get_err_counter( - '/err/nat44-classify/next out2in') + '/err/nat44-ei-classify/next out2in') self.assertEqual(err, 1) def test_del_session(self): """ NAT44EI delete session """ self.nat44_add_address(self.nat_addr) - flags = self.config_flags.NAT_IS_INSIDE - self.vapi.nat44_interface_add_del_feature( + flags = self.config_flags.NAT44_EI_IF_INSIDE + self.vapi.nat44_ei_interface_add_del_feature( sw_if_index=self.pg0.sw_if_index, flags=flags, is_add=1) - self.vapi.nat44_interface_add_del_feature( + self.vapi.nat44_ei_interface_add_del_feature( sw_if_index=self.pg1.sw_if_index, is_add=1) @@ -3094,24 +3068,28 @@ class TestNAT44EI(MethodHolder): self.pg_start() self.pg1.get_capture(len(pkts)) - sessions = self.vapi.nat44_user_session_dump(self.pg0.remote_ip4, 0) + sessions = self.vapi.nat44_ei_user_session_dump(self.pg0.remote_ip4, 0) nsessions = len(sessions) - self.vapi.nat44_del_session(address=sessions[0].inside_ip_address, - port=sessions[0].inside_port, - protocol=sessions[0].protocol, - flags=self.config_flags.NAT_IS_INSIDE) - self.vapi.nat44_del_session(address=sessions[1].outside_ip_address, - port=sessions[1].outside_port, - protocol=sessions[1].protocol) + self.vapi.nat44_ei_del_session( + address=sessions[0].inside_ip_address, + port=sessions[0].inside_port, + protocol=sessions[0].protocol, + flags=self.config_flags.NAT44_EI_IF_INSIDE) - sessions = self.vapi.nat44_user_session_dump(self.pg0.remote_ip4, 0) + self.vapi.nat44_ei_del_session( + address=sessions[1].outside_ip_address, + port=sessions[1].outside_port, + protocol=sessions[1].protocol) + + sessions = self.vapi.nat44_ei_user_session_dump(self.pg0.remote_ip4, 0) self.assertEqual(nsessions - len(sessions), 2) - self.vapi.nat44_del_session(address=sessions[0].inside_ip_address, - port=sessions[0].inside_port, - protocol=sessions[0].protocol, - flags=self.config_flags.NAT_IS_INSIDE) + self.vapi.nat44_ei_del_session( + address=sessions[0].inside_ip_address, + port=sessions[0].inside_port, + protocol=sessions[0].protocol, + flags=self.config_flags.NAT44_EI_IF_INSIDE) self.verify_no_nat44_user() @@ -3119,11 +3097,11 @@ class TestNAT44EI(MethodHolder): """ NAT44EI translate fragments arriving in order """ self.nat44_add_address(self.nat_addr) - flags = self.config_flags.NAT_IS_INSIDE - self.vapi.nat44_interface_add_del_feature( + flags = self.config_flags.NAT44_EI_IF_INSIDE + self.vapi.nat44_ei_interface_add_del_feature( sw_if_index=self.pg0.sw_if_index, flags=flags, is_add=1) - self.vapi.nat44_interface_add_del_feature( + self.vapi.nat44_ei_interface_add_del_feature( sw_if_index=self.pg1.sw_if_index, is_add=1) @@ -3133,17 +3111,17 @@ class TestNAT44EI(MethodHolder): def test_frag_forwarding(self): """ NAT44EI forwarding fragment test """ - self.vapi.nat44_add_del_interface_addr( + self.vapi.nat44_ei_add_del_interface_addr( is_add=1, sw_if_index=self.pg1.sw_if_index) - flags = self.config_flags.NAT_IS_INSIDE - self.vapi.nat44_interface_add_del_feature( + flags = self.config_flags.NAT44_EI_IF_INSIDE + self.vapi.nat44_ei_interface_add_del_feature( sw_if_index=self.pg0.sw_if_index, flags=flags, is_add=1) - self.vapi.nat44_interface_add_del_feature( + self.vapi.nat44_ei_interface_add_del_feature( sw_if_index=self.pg1.sw_if_index, is_add=1) - self.vapi.nat44_forwarding_enable_disable(enable=1) + self.vapi.nat44_ei_forwarding_enable_disable(enable=1) data = b"A" * 16 + b"B" * 16 + b"C" * 3 pkts = self.create_stream_frag(self.pg1, @@ -3172,11 +3150,11 @@ class TestNAT44EI(MethodHolder): server_out_port = random.randint(1025, 65535) self.nat44_add_address(self.nat_addr) - flags = self.config_flags.NAT_IS_INSIDE - self.vapi.nat44_interface_add_del_feature( + flags = self.config_flags.NAT44_EI_IF_INSIDE + self.vapi.nat44_ei_interface_add_del_feature( sw_if_index=self.pg0.sw_if_index, flags=flags, is_add=1) - self.vapi.nat44_interface_add_del_feature( + self.vapi.nat44_ei_interface_add_del_feature( sw_if_index=self.pg1.sw_if_index, is_add=1) # add static mapping for server @@ -3201,11 +3179,11 @@ class TestNAT44EI(MethodHolder): """ NAT44EI translate fragments arriving out of order """ self.nat44_add_address(self.nat_addr) - flags = self.config_flags.NAT_IS_INSIDE - self.vapi.nat44_interface_add_del_feature( + flags = self.config_flags.NAT44_EI_IF_INSIDE + self.vapi.nat44_ei_interface_add_del_feature( sw_if_index=self.pg0.sw_if_index, flags=flags, is_add=1) - self.vapi.nat44_interface_add_del_feature( + self.vapi.nat44_ei_interface_add_del_feature( sw_if_index=self.pg1.sw_if_index, is_add=1) @@ -3216,17 +3194,17 @@ class TestNAT44EI(MethodHolder): def test_port_restricted(self): """ NAT44EI Port restricted NAT44EI (MAP-E CE) """ self.nat44_add_address(self.nat_addr) - flags = self.config_flags.NAT_IS_INSIDE - self.vapi.nat44_interface_add_del_feature( + flags = self.config_flags.NAT44_EI_IF_INSIDE + self.vapi.nat44_ei_interface_add_del_feature( sw_if_index=self.pg0.sw_if_index, flags=flags, is_add=1) - self.vapi.nat44_interface_add_del_feature( + self.vapi.nat44_ei_interface_add_del_feature( sw_if_index=self.pg1.sw_if_index, is_add=1) - self.vapi.nat_set_addr_and_port_alloc_alg(alg=1, - psid_offset=6, - psid_length=6, - psid=10) + self.vapi.nat44_ei_set_addr_and_port_alloc_alg(alg=1, + psid_offset=6, + psid_length=6, + psid=10) p = (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) / IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4) / @@ -3252,16 +3230,16 @@ class TestNAT44EI(MethodHolder): def test_port_range(self): """ NAT44EI External address port range """ self.nat44_add_address(self.nat_addr) - flags = self.config_flags.NAT_IS_INSIDE - self.vapi.nat44_interface_add_del_feature( + flags = self.config_flags.NAT44_EI_IF_INSIDE + self.vapi.nat44_ei_interface_add_del_feature( sw_if_index=self.pg0.sw_if_index, flags=flags, is_add=1) - self.vapi.nat44_interface_add_del_feature( + self.vapi.nat44_ei_interface_add_del_feature( sw_if_index=self.pg1.sw_if_index, is_add=1) - self.vapi.nat_set_addr_and_port_alloc_alg(alg=2, - start_port=1025, - end_port=1027) + self.vapi.nat44_ei_set_addr_and_port_alloc_alg(alg=2, + start_port=1025, + end_port=1027) pkts = [] for port in range(0, 5): @@ -3295,14 +3273,14 @@ class TestNAT44EI(MethodHolder): self.pg2.resolve_arp() self.nat44_add_address(self.nat_addr) - flags = self.config_flags.NAT_IS_INSIDE - self.vapi.nat44_interface_add_del_feature( + flags = self.config_flags.NAT44_EI_IF_INSIDE + self.vapi.nat44_ei_interface_add_del_feature( sw_if_index=self.pg0.sw_if_index, flags=flags, is_add=1) - self.vapi.nat44_interface_add_del_feature( + self.vapi.nat44_ei_interface_add_del_feature( sw_if_index=self.pg1.sw_if_index, is_add=1) - self.vapi.nat44_interface_add_del_feature( + self.vapi.nat44_ei_interface_add_del_feature( sw_if_index=self.pg2.sw_if_index, is_add=1) @@ -3355,11 +3333,11 @@ class TestNAT44EI(MethodHolder): def test_mss_clamping(self): """ NAT44EI TCP MSS clamping """ self.nat44_add_address(self.nat_addr) - flags = self.config_flags.NAT_IS_INSIDE - self.vapi.nat44_interface_add_del_feature( + flags = self.config_flags.NAT44_EI_IF_INSIDE + self.vapi.nat44_ei_interface_add_del_feature( sw_if_index=self.pg0.sw_if_index, flags=flags, is_add=1) - self.vapi.nat44_interface_add_del_feature( + self.vapi.nat44_ei_interface_add_del_feature( sw_if_index=self.pg1.sw_if_index, is_add=1) @@ -3368,7 +3346,7 @@ class TestNAT44EI(MethodHolder): TCP(sport=self.tcp_port_in, dport=self.tcp_external_port, flags="S", options=[('MSS', 1400)])) - self.vapi.nat_set_mss_clamping(enable=1, mss_value=1000) + self.vapi.nat44_ei_set_mss_clamping(enable=1, mss_value=1000) self.pg0.add_stream(p) self.pg_enable_capture(self.pg_interfaces) self.pg_start() @@ -3376,7 +3354,7 @@ class TestNAT44EI(MethodHolder): # Negotiated MSS value greater than configured - changed self.verify_mss_value(capture[0], 1000) - self.vapi.nat_set_mss_clamping(enable=0, mss_value=1500) + self.vapi.nat44_ei_set_mss_clamping(enable=0, mss_value=1500) self.pg0.add_stream(p) self.pg_enable_capture(self.pg_interfaces) self.pg_start() @@ -3384,7 +3362,7 @@ class TestNAT44EI(MethodHolder): # MSS clamping disabled - negotiated MSS unchanged self.verify_mss_value(capture[0], 1400) - self.vapi.nat_set_mss_clamping(enable=1, mss_value=1500) + self.vapi.nat44_ei_set_mss_clamping(enable=1, mss_value=1500) self.pg0.add_stream(p) self.pg_enable_capture(self.pg_interfaces) self.pg_start() @@ -3394,20 +3372,20 @@ class TestNAT44EI(MethodHolder): def test_ha_send(self): """ NAT44EI Send HA session synchronization events (active) """ - flags = self.config_flags.NAT_IS_INSIDE - self.vapi.nat44_interface_add_del_feature( + flags = self.config_flags.NAT44_EI_IF_INSIDE + self.vapi.nat44_ei_interface_add_del_feature( sw_if_index=self.pg0.sw_if_index, flags=flags, is_add=1) - self.vapi.nat44_interface_add_del_feature( + self.vapi.nat44_ei_interface_add_del_feature( sw_if_index=self.pg1.sw_if_index, is_add=1) self.nat44_add_address(self.nat_addr) - self.vapi.nat_ha_set_listener(ip_address=self.pg3.local_ip4, - port=12345, - path_mtu=512) - self.vapi.nat_ha_set_failover(ip_address=self.pg3.remote_ip4, - port=12346, session_refresh_interval=10) + self.vapi.nat44_ei_ha_set_listener( + ip_address=self.pg3.local_ip4, port=12345, path_mtu=512) + self.vapi.nat44_ei_ha_set_failover( + ip_address=self.pg3.remote_ip4, port=12346, + session_refresh_interval=10) bind_layers(UDP, HANATStateSync, sport=12345) # create sessions @@ -3418,8 +3396,8 @@ class TestNAT44EI(MethodHolder): capture = self.pg1.get_capture(len(pkts)) self.verify_capture_out(capture) # active send HA events - self.vapi.nat_ha_flush() - stats = self.statistics.get_counter('/nat44/ha/add-event-send') + self.vapi.nat44_ei_ha_flush() + stats = self.statistics.get_counter('/nat44-ei/ha/add-event-send') self.assertEqual(stats[0][0], 3) capture = self.pg3.get_capture(1) p = capture[0] @@ -3453,17 +3431,16 @@ class TestNAT44EI(MethodHolder): HANATStateSync(sequence_number=seq, flags='ACK')) self.pg3.add_stream(ack) self.pg_start() - stats = self.statistics.get_counter('/nat44/ha/ack-recv') + stats = self.statistics.get_counter('/nat44-ei/ha/ack-recv') self.assertEqual(stats[0][0], 1) # delete one session self.pg_enable_capture(self.pg_interfaces) - self.vapi.nat44_del_session(address=self.pg0.remote_ip4, - port=self.tcp_port_in, - protocol=IP_PROTOS.tcp, - flags=self.config_flags.NAT_IS_INSIDE) - self.vapi.nat_ha_flush() - stats = self.statistics.get_counter('/nat44/ha/del-event-send') + self.vapi.nat44_ei_del_session( + address=self.pg0.remote_ip4, port=self.tcp_port_in, + protocol=IP_PROTOS.tcp, flags=self.config_flags.NAT44_EI_IF_INSIDE) + self.vapi.nat44_ei_ha_flush() + stats = self.statistics.get_counter('/nat44-ei/ha/del-event-send') self.assertEqual(stats[0][0], 1) capture = self.pg3.get_capture(1) p = capture[0] @@ -3478,9 +3455,9 @@ class TestNAT44EI(MethodHolder): # do not send ACK, active retry send HA event again self.pg_enable_capture(self.pg_interfaces) sleep(12) - stats = self.statistics.get_counter('/nat44/ha/retry-count') + stats = self.statistics.get_counter('/nat44-ei/ha/retry-count') self.assertEqual(stats[0][0], 3) - stats = self.statistics.get_counter('/nat44/ha/missed-count') + stats = self.statistics.get_counter('/nat44-ei/ha/missed-count') self.assertEqual(stats[0][0], 1) capture = self.pg3.get_capture(3) for packet in capture: @@ -3492,8 +3469,8 @@ class TestNAT44EI(MethodHolder): self.pg_enable_capture(self.pg_interfaces) self.pg_start() self.pg0.get_capture(2) - self.vapi.nat_ha_flush() - stats = self.statistics.get_counter('/nat44/ha/refresh-event-send') + self.vapi.nat44_ei_ha_flush() + stats = self.statistics.get_counter('/nat44-ei/ha/refresh-event-send') self.assertEqual(stats[0][0], 2) capture = self.pg3.get_capture(1) p = capture[0] @@ -3526,22 +3503,21 @@ class TestNAT44EI(MethodHolder): HANATStateSync(sequence_number=seq, flags='ACK')) self.pg3.add_stream(ack) self.pg_start() - stats = self.statistics.get_counter('/nat44/ha/ack-recv') + stats = self.statistics.get_counter('/nat44-ei/ha/ack-recv') self.assertEqual(stats[0][0], 2) def test_ha_recv(self): """ NAT44EI Receive HA session synchronization events (passive) """ self.nat44_add_address(self.nat_addr) - flags = self.config_flags.NAT_IS_INSIDE - self.vapi.nat44_interface_add_del_feature( + flags = self.config_flags.NAT44_EI_IF_INSIDE + self.vapi.nat44_ei_interface_add_del_feature( sw_if_index=self.pg0.sw_if_index, flags=flags, is_add=1) - self.vapi.nat44_interface_add_del_feature( + self.vapi.nat44_ei_interface_add_del_feature( sw_if_index=self.pg1.sw_if_index, is_add=1) - self.vapi.nat_ha_set_listener(ip_address=self.pg3.local_ip4, - port=12345, - path_mtu=512) + self.vapi.nat44_ei_ha_set_listener(ip_address=self.pg3.local_ip4, + port=12345, path_mtu=512) bind_layers(UDP, HANATStateSync, sport=12345) self.tcp_port_out = random.randint(1025, 65535) @@ -3583,21 +3559,21 @@ class TestNAT44EI(MethodHolder): self.assertEqual(hanat.flags, 'ACK') self.assertEqual(hanat.version, 1) self.assertEqual(hanat.thread_index, 0) - stats = self.statistics.get_counter('/nat44/ha/ack-send') + stats = self.statistics.get_counter('/nat44-ei/ha/ack-send') self.assertEqual(stats[0][0], 1) - stats = self.statistics.get_counter('/nat44/ha/add-event-recv') + stats = self.statistics.get_counter('/nat44-ei/ha/add-event-recv') self.assertEqual(stats[0][0], 2) - users = self.statistics.get_counter('/nat44/total-users') + users = self.statistics.get_counter('/nat44-ei/total-users') self.assertEqual(users[0][0], 1) - sessions = self.statistics.get_counter('/nat44/total-sessions') + sessions = self.statistics.get_counter('/nat44-ei/total-sessions') self.assertEqual(sessions[0][0], 2) - users = self.vapi.nat44_user_dump() + users = self.vapi.nat44_ei_user_dump() self.assertEqual(len(users), 1) self.assertEqual(str(users[0].ip_address), self.pg0.remote_ip4) # there should be 2 sessions created by HA - sessions = self.vapi.nat44_user_session_dump(users[0].ip_address, - users[0].vrf_id) + sessions = self.vapi.nat44_ei_user_session_dump( + users[0].ip_address, users[0].vrf_id) self.assertEqual(len(sessions), 2) for session in sessions: self.assertEqual(str(session.inside_ip_address), @@ -3638,18 +3614,19 @@ class TestNAT44EI(MethodHolder): self.assertEqual(hanat.sequence_number, 2) self.assertEqual(hanat.flags, 'ACK') self.assertEqual(hanat.version, 1) - users = self.vapi.nat44_user_dump() + users = self.vapi.nat44_ei_user_dump() self.assertEqual(len(users), 1) self.assertEqual(str(users[0].ip_address), self.pg0.remote_ip4) # now we should have only 1 session, 1 deleted by HA - sessions = self.vapi.nat44_user_session_dump(users[0].ip_address, - users[0].vrf_id) + sessions = self.vapi.nat44_ei_user_session_dump(users[0].ip_address, + users[0].vrf_id) self.assertEqual(len(sessions), 1) - stats = self.statistics.get_counter('/nat44/ha/del-event-recv') + stats = self.statistics.get_counter('/nat44-ei/ha/del-event-recv') self.assertEqual(stats[0][0], 1) - stats = self.statistics.get_err_counter('/err/nat-ha/pkts-processed') + stats = self.statistics.get_err_counter( + '/err/nat44-ei-ha/pkts-processed') self.assertEqual(stats, 2) # send HA session refresh event to failover/passive @@ -3680,20 +3657,22 @@ class TestNAT44EI(MethodHolder): self.assertEqual(hanat.sequence_number, 3) self.assertEqual(hanat.flags, 'ACK') self.assertEqual(hanat.version, 1) - users = self.vapi.nat44_user_dump() + users = self.vapi.nat44_ei_user_dump() self.assertEqual(len(users), 1) self.assertEqual(str(users[0].ip_address), self.pg0.remote_ip4) - sessions = self.vapi.nat44_user_session_dump(users[0].ip_address, - users[0].vrf_id) + sessions = self.vapi.nat44_ei_user_session_dump( + users[0].ip_address, users[0].vrf_id) self.assertEqual(len(sessions), 1) session = sessions[0] self.assertEqual(session.total_bytes, 1024) self.assertEqual(session.total_pkts, 2) - stats = self.statistics.get_counter('/nat44/ha/refresh-event-recv') + stats = self.statistics.get_counter( + '/nat44-ei/ha/refresh-event-recv') self.assertEqual(stats[0][0], 1) - stats = self.statistics.get_err_counter('/err/nat-ha/pkts-processed') + stats = self.statistics.get_err_counter( + '/err/nat44-ei-ha/pkts-processed') self.assertEqual(stats, 3) # send packet to test session created by HA @@ -3717,17 +3696,28 @@ class TestNAT44EI(MethodHolder): self.assertEqual(tcp.sport, self.tcp_external_port) self.assertEqual(tcp.dport, self.tcp_port_in) + def reconfigure_frame_queue_nelts(self, frame_queue_nelts): + self.vapi.nat44_ei_plugin_enable_disable(enable=0) + self.vapi.nat44_ei_set_fq_options(frame_queue_nelts=frame_queue_nelts) + # keep plugin configuration persistent + self.plugin_enable() + return self.vapi.nat44_ei_show_fq_options().frame_queue_nelts + + def test_set_frame_queue_nelts(self): + """ NAT44 EI API test - worker handoff frame queue elements """ + self.assertEqual(self.reconfigure_frame_queue_nelts(512), 512) + def show_commands_at_teardown(self): - self.logger.info(self.vapi.cli("show nat44 addresses")) - self.logger.info(self.vapi.cli("show nat44 interfaces")) - self.logger.info(self.vapi.cli("show nat44 static mappings")) - self.logger.info(self.vapi.cli("show nat44 interface address")) - self.logger.info(self.vapi.cli("show nat44 sessions detail")) - self.logger.info(self.vapi.cli("show nat44 hash tables detail")) - self.logger.info(self.vapi.cli("show nat timeouts")) + self.logger.info(self.vapi.cli("show nat44 ei timeouts")) + self.logger.info(self.vapi.cli("show nat44 ei addresses")) + self.logger.info(self.vapi.cli("show nat44 ei interfaces")) + self.logger.info(self.vapi.cli("show nat44 ei static mappings")) + self.logger.info(self.vapi.cli("show nat44 ei interface address")) + self.logger.info(self.vapi.cli("show nat44 ei sessions detail")) + self.logger.info(self.vapi.cli("show nat44 ei hash tables detail")) + self.logger.info(self.vapi.cli("show nat44 ei ha")) self.logger.info( - self.vapi.cli("show nat addr-port-assignment-alg")) - self.logger.info(self.vapi.cli("show nat ha")) + self.vapi.cli("show nat44 ei addr-port-assignment-alg")) class TestNAT44Out2InDPO(MethodHolder): @@ -3736,7 +3726,7 @@ class TestNAT44Out2InDPO(MethodHolder): @classmethod def setUpClass(cls): super(TestNAT44Out2InDPO, cls).setUpClass() - cls.vapi.cli("set log class nat level debug") + cls.vapi.cli("set log class nat44-ei level debug") cls.tcp_port_in = 6303 cls.tcp_port_out = 6303 @@ -3765,13 +3755,13 @@ class TestNAT44Out2InDPO(MethodHolder): def setUp(self): super(TestNAT44Out2InDPO, self).setUp() - flags = self.nat44_config_flags.NAT44_API_IS_OUT2IN_DPO - self.vapi.nat44_plugin_enable_disable(enable=1, flags=flags) + flags = self.config_flags.NAT44_EI_OUT2IN_DPO + self.vapi.nat44_ei_plugin_enable_disable(enable=1, flags=flags) def tearDown(self): super(TestNAT44Out2InDPO, self).tearDown() if not self.vpp_dead: - self.vapi.nat44_plugin_enable_disable(enable=0) + self.vapi.nat44_ei_plugin_enable_disable(enable=0) self.vapi.cli("clear logging") def configure_xlat(self): @@ -3791,18 +3781,16 @@ class TestNAT44Out2InDPO(MethodHolder): def test_464xlat_ce(self): """ Test 464XLAT CE with NAT44EI """ - nat_config = self.vapi.nat_show_config() - self.assertEqual(1, nat_config.out2in_dpo) - self.configure_xlat() - flags = self.config_flags.NAT_IS_INSIDE - self.vapi.nat44_interface_add_del_feature( + flags = self.config_flags.NAT44_EI_IF_INSIDE + self.vapi.nat44_ei_interface_add_del_feature( sw_if_index=self.pg0.sw_if_index, flags=flags, is_add=1) - self.vapi.nat44_add_del_address_range(first_ip_address=self.nat_addr_n, - last_ip_address=self.nat_addr_n, - vrf_id=0xFFFFFFFF, is_add=1) + self.vapi.nat44_ei_add_del_address_range( + first_ip_address=self.nat_addr_n, + last_ip_address=self.nat_addr_n, + vrf_id=0xFFFFFFFF, is_add=1) out_src_ip6 = self.compose_ip6(self.dst_ip4, self.dst_ip6_pfx, self.dst_ip6_pfx_len) @@ -3826,10 +3814,10 @@ class TestNAT44Out2InDPO(MethodHolder): capture = self.pg0.get_capture(len(pkts)) self.verify_capture_in(capture, self.pg0) finally: - self.vapi.nat44_interface_add_del_feature( + self.vapi.nat44_ei_interface_add_del_feature( sw_if_index=self.pg0.sw_if_index, flags=flags) - self.vapi.nat44_add_del_address_range( + self.vapi.nat44_ei_add_del_address_range( first_ip_address=self.nat_addr_n, last_ip_address=self.nat_addr_n, vrf_id=0xFFFFFFFF) @@ -3935,20 +3923,21 @@ class TestNAT44EIMW(MethodHolder): def setUp(self): super(TestNAT44EIMW, self).setUp() - self.vapi.nat44_plugin_enable_disable( + self.vapi.nat44_ei_plugin_enable_disable( sessions=self.max_translations, users=self.max_users, enable=1) def tearDown(self): super(TestNAT44EIMW, self).tearDown() if not self.vpp_dead: - self.vapi.nat_ipfix_enable_disable(domain_id=self.ipfix_domain_id, - src_port=self.ipfix_src_port, - enable=0) + self.vapi.nat44_ei_ipfix_enable_disable( + domain_id=self.ipfix_domain_id, + src_port=self.ipfix_src_port, + enable=0) self.ipfix_src_port = 4739 self.ipfix_domain_id = 1 - self.vapi.nat44_plugin_enable_disable(enable=0) + self.vapi.nat44_ei_plugin_enable_disable(enable=0) self.vapi.cli("clear logging") def test_hairpinning(self): @@ -3964,11 +3953,11 @@ class TestNAT44EIMW(MethodHolder): worker_2 = 2 self.nat44_add_address(self.nat_addr) - flags = self.config_flags.NAT_IS_INSIDE - self.vapi.nat44_interface_add_del_feature( + flags = self.config_flags.NAT44_EI_IF_INSIDE + self.vapi.nat44_ei_interface_add_del_feature( sw_if_index=self.pg0.sw_if_index, flags=flags, is_add=1) - self.vapi.nat44_interface_add_del_feature( + self.vapi.nat44_ei_interface_add_del_feature( sw_if_index=self.pg1.sw_if_index, is_add=1) @@ -3977,7 +3966,7 @@ class TestNAT44EIMW(MethodHolder): server_in_port, server_out_port, proto=IP_PROTOS.tcp) - cnt = self.statistics.get_counter('/nat44/hairpinning') + cnt = self.statistics.get_counter('/nat44-ei/hairpinning') # send packet from host to server p = (Ether(src=host.mac, dst=self.pg0.local_mac) / IP(src=host.ip4, dst=self.nat_addr) / @@ -4000,7 +3989,7 @@ class TestNAT44EIMW(MethodHolder): self.logger.error(ppp("Unexpected or invalid packet:", p)) raise - after = self.statistics.get_counter('/nat44/hairpinning') + after = self.statistics.get_counter('/nat44-ei/hairpinning') if_idx = self.pg0.sw_if_index self.assertEqual(after[worker_2][if_idx] - cnt[worker_1][if_idx], 1) @@ -4026,7 +4015,7 @@ class TestNAT44EIMW(MethodHolder): self.logger.error(ppp("Unexpected or invalid packet:", p)) raise - after = self.statistics.get_counter('/nat44/hairpinning') + after = self.statistics.get_counter('/nat44-ei/hairpinning') if_idx = self.pg0.sw_if_index self.assertEqual(after[worker_1][if_idx] - cnt[worker_1][if_idx], 1) self.assertEqual(after[worker_2][if_idx] - cnt[worker_2][if_idx], 2) @@ -4043,11 +4032,11 @@ class TestNAT44EIMW(MethodHolder): server_udp_port = 20 self.nat44_add_address(self.nat_addr) - flags = self.config_flags.NAT_IS_INSIDE - self.vapi.nat44_interface_add_del_feature( + flags = self.config_flags.NAT44_EI_IF_INSIDE + self.vapi.nat44_ei_interface_add_del_feature( sw_if_index=self.pg0.sw_if_index, flags=flags, is_add=1) - self.vapi.nat44_interface_add_del_feature( + self.vapi.nat44_ei_interface_add_del_feature( sw_if_index=self.pg1.sw_if_index, is_add=1) @@ -4201,5 +4190,6 @@ class TestNAT44EIMW(MethodHolder): self.logger.error(ppp("Unexpected or invalid packet:", packet)) raise + if __name__ == '__main__': unittest.main(testRunner=VppTestRunner) -- cgit 1.2.3-korg