diff options
Diffstat (limited to 'src/plugins/nat/nat44-ed')
-rw-r--r-- | src/plugins/nat/nat44-ed/nat44_ed.api | 798 | ||||
-rw-r--r-- | src/plugins/nat/nat44-ed/nat44_ed.c | 2986 | ||||
-rw-r--r-- | src/plugins/nat/nat44-ed/nat44_ed.h | 617 | ||||
-rw-r--r-- | src/plugins/nat/nat44-ed/nat44_ed_affinity.c | 7 | ||||
-rw-r--r-- | src/plugins/nat/nat44-ed/nat44_ed_api.c | 772 | ||||
-rw-r--r-- | src/plugins/nat/nat44-ed/nat44_ed_classify.c | 42 | ||||
-rw-r--r-- | src/plugins/nat/nat44-ed/nat44_ed_cli.c | 758 | ||||
-rw-r--r-- | src/plugins/nat/nat44-ed/nat44_ed_doc.rst | 729 | ||||
-rw-r--r-- | src/plugins/nat/nat44-ed/nat44_ed_format.c | 293 | ||||
-rw-r--r-- | src/plugins/nat/nat44-ed/nat44_ed_handoff.c | 1 | ||||
-rw-r--r-- | src/plugins/nat/nat44-ed/nat44_ed_in2out.c | 867 | ||||
-rw-r--r-- | src/plugins/nat/nat44-ed/nat44_ed_inlines.h | 379 | ||||
-rw-r--r-- | src/plugins/nat/nat44-ed/nat44_ed_out2in.c | 444 | ||||
-rw-r--r-- | src/plugins/nat/nat44-ed/tcp_conn_track.rst | 65 |
14 files changed, 4807 insertions, 3951 deletions
diff --git a/src/plugins/nat/nat44-ed/nat44_ed.api b/src/plugins/nat/nat44-ed/nat44_ed.api index c65b7a81166..322260f7f96 100644 --- a/src/plugins/nat/nat44-ed/nat44_ed.api +++ b/src/plugins/nat/nat44-ed/nat44_ed.api @@ -13,13 +13,13 @@ * limitations under the License. */ -option version = "5.3.0"; +option version = "5.5.0"; import "vnet/ip/ip_types.api"; import "vnet/interface_types.api"; import "plugins/nat/lib/nat_types.api"; /** - * @file nat44.api + * @file nat44_ed.api * @brief VPP control-plane API messages. * * This file defines VPP control-plane API messages which are generally @@ -35,41 +35,6 @@ enum nat44_config_flags : u8 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 { - option deprecated; - 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 Enable/disable NAT44ED plugin @param client_index - opaque cookie to identify the sender @param context - sender context, to match reply w/ request @@ -82,7 +47,6 @@ autoreply define nat44_plugin_enable_disable { NAT44_IS_CONNECTION_TRACKING */ autoreply define nat44_ed_plugin_enable_disable { - option in_progress; u32 client_index; u32 context; u32 inside_vrf; @@ -93,146 +57,65 @@ autoreply define nat44_ed_plugin_enable_disable { vl_api_nat44_config_flags_t flags; }; -/** \brief Control ping from client to api server request +/** \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 */ -define nat_control_ping -{ +autoreply define nat44_forwarding_enable_disable { option deprecated; u32 client_index; u32 context; + bool enable; }; -/** \brief Control ping from the client to the server response +/** \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 retval - return code for the request - @param vpe_pid - the pid of the vpe, returned by the server + @param domain_id - observation domain ID + @param src_port - source port number + @param enable - true if enable, false if disable */ -define nat_control_ping_reply -{ +autoreply define nat_ipfix_enable_disable { option deprecated; - u32 context; - i32 retval; u32 client_index; - u32 vpe_pid; + u32 context; + u32 domain_id; + u16 src_port; + bool enable; }; -/** \brief Show NAT plugin startup config +/** \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) */ -define nat_show_config -{ +autoreply define nat_set_timeouts { option deprecated; u32 client_index; u32 context; + u32 udp; + u32 tcp_established; + u32 tcp_transitory; + u32 icmp; }; -/** \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 +/** \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 */ -define nat_show_config_2 -{ - option deprecated; +autoreply define nat44_set_session_limit { 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; + u32 session_limit; + u32 vrf_id; }; /** \brief Show NAT44 plugin running config @@ -241,7 +124,6 @@ define nat_show_config_2_reply */ define nat44_show_running_config { - option in_progress; u32 client_index; u32 context; }; @@ -267,7 +149,6 @@ define nat44_show_running_config */ define nat44_show_running_config_reply { - option in_progress; u32 context; i32 retval; u32 inside_vrf; @@ -284,41 +165,6 @@ define nat44_show_running_config_reply 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 @@ -352,121 +198,82 @@ define nat_worker_details { string name[64]; }; -/** \brief Enable/disable NAT IPFIX logging +/** \brief Add/delete inter VRF NAT44-ED routing table @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 + @param table_vrf_id - id of (rx) VRF used for resolving + destination (tx) VRF during dynamic + session creation + @param is_add - if true add else del */ -autoreply define nat_ipfix_enable_disable { - option deprecated; +autoreply define nat44_ed_add_del_vrf_table { u32 client_index; u32 context; - u32 domain_id; - u16 src_port; - bool enable; + u32 table_vrf_id; + bool is_add; }; -/** \brief Set values of timeouts for NAT sessions (seconds) +/** \brief Add/del inter VRF NAT44-ED route record @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) + @param table_vrf_id - id of the VRF NAT routing table + @param vrf_id - id of resolving destination (tx) VRF table + @param is_add - if true add else del */ -autoreply define nat_set_timeouts { - option deprecated; +autoreply define nat44_ed_add_del_vrf_route { u32 client_index; u32 context; - u32 udp; - u32 tcp_established; - u32 tcp_transitory; - u32 icmp; + u32 table_vrf_id; + u32 vrf_id; + bool is_add; }; -/** \brief Get values of timeouts for NAT sessions (seconds) +/** \brief Dump NAT44-ED inter VRF NAT routing tables @param client_index - opaque cookie to identify the sender @param context - sender context, to match reply w/ request */ -define nat_get_timeouts { - option deprecated; +define nat44_ed_vrf_tables_dump { 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 +/** \brief NAT44-ED inter VRF NAT routing table details response @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 + @param table_vrf_id - id of the VRF NAT routing table + @param n_vrf_ids - number of vrf_ids + @param vrf_ids - ids of resolving destination (tx) VRFs */ -autoreply define nat_set_addr_and_port_alloc_alg { - u32 client_index; +define nat44_ed_vrf_tables_details { u32 context; - u8 alg; - u8 psid_offset; - u8 psid_length; - u16 psid; - u16 start_port; - u16 end_port; + u32 table_vrf_id; + u32 n_vrf_ids; + u32 vrf_ids[n_vrf_ids]; + option deprecated; }; -/** \brief Get address and port assignment algorithm +/** \brief Dump NAT44-ED inter VRF NAT routing tables @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 { +define nat44_ed_vrf_tables_v2_dump { u32 client_index; u32 context; + option status="in_progress"; }; -/** \brief Get address and port assignment algorithm reply +/** \brief NAT44-ED inter VRF NAT routing table details response @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 + @param table_vrf_id - id of the VRF NAT routing table + @param n_vrf_ids - number of vrf_ids + @param vrf_ids - ids of resolving destination (tx) VRFs */ -define nat_get_addr_and_port_alloc_alg_reply { +define nat44_ed_vrf_tables_v2_details { u32 context; - i32 retval; - u8 alg; - u8 psid_offset; - u8 psid_length; - u16 psid; - u16 start_port; - u16 end_port; + u32 table_vrf_id; + u32 n_vrf_ids; + u32 vrf_ids[n_vrf_ids]; + option status="in_progress"; }; /** \brief Set TCP MSS rewriting configuration @@ -504,138 +311,73 @@ define nat_get_mss_clamping_reply { 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) +/** \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 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 + @param frame_queue_nelts - number of worker handoff frame queue elements */ -autoreply define nat_ha_set_failover { +autoreply define nat44_ed_set_fq_options { u32 client_index; u32 context; - vl_api_ip4_address_t ip_address; - u16 port; - u32 session_refresh_interval; + u32 frame_queue_nelts; }; -/** \brief Get HA listener/local configuration +/** \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_ha_get_listener { +define nat44_ed_show_fq_options +{ u32 client_index; u32 context; }; -/** \brief Get HA listener/local configuration reply +/** \brief Show NAT handoff frame queue options 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 + @param retval - return code for the request + @param frame_queue_nelts - number of worker handoff frame queue elements */ -define nat_ha_get_listener_reply { +define nat44_ed_show_fq_options_reply +{ u32 context; i32 retval; - vl_api_ip4_address_t ip_address; - u16 port; - u32 path_mtu; + u32 frame_queue_nelts; }; -/** \brief Get HA failover/remote settings +/** \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 */ -define nat_ha_get_failover { +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 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) +/** \brief Dump NAT44 pool addresses interfaces @param client_index - opaque cookie to identify the sender @param context - sender context, to match reply w/ request */ -autoreply define nat_ha_flush { +define nat44_interface_addr_dump { u32 client_index; u32 context; }; -/** \brief Resync HA (resend existing sessions to new failover) - @param client_index - opaque cookie to identify the sender +/** \brief NAT44 pool addresses interfaces details response @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; -}; + @param sw_if_index - software index of the interface + @param flags - flag NAT_TWICE_NAT if NAT address range for external hosts -/** \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; +define nat44_interface_addr_details { u32 context; - vl_api_ip4_address_t ip_address; - u32 fib_index; + vl_api_interface_index_t sw_if_index; + vl_api_nat_config_flags_t flags; }; /** \brief Add/del NAT44 address range @@ -719,41 +461,42 @@ define nat44_interface_details { vl_api_interface_index_t sw_if_index; }; -/** \brief Enable/disbale NAT44 as an interface output feature (postrouting +/** \brief add/del NAT output interface (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 { +autoendian autoreply define nat44_ed_add_del_output_interface { 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 { +service { + rpc nat44_ed_output_interface_get returns nat44_ed_output_interface_get_reply + stream nat44_ed_output_interface_details; +}; + +define nat44_ed_output_interface_get +{ u32 client_index; u32 context; + u32 cursor; }; -/** \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 { +define nat44_ed_output_interface_get_reply +{ + u32 context; + i32 retval; + u32 cursor; +}; + +define nat44_ed_output_interface_details +{ u32 context; - vl_api_nat_config_flags_t flags; vl_api_interface_index_t sw_if_index; }; @@ -779,6 +522,8 @@ define nat44_interface_output_feature_details { @param tag - opaque string tag */ autoreply define nat44_add_del_static_mapping { + option deprecated; + u32 client_index; u32 context; bool is_add; @@ -817,7 +562,6 @@ autoreply define nat44_add_del_static_mapping { @param tag - opaque string tag */ autoreply define nat44_add_del_static_mapping_v2 { - option in_progress; u32 client_index; u32 context; bool is_add; @@ -929,117 +673,6 @@ define nat44_identity_mapping_details { 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 @@ -1167,72 +800,195 @@ autoreply define nat44_del_session { 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. +/** \brief Dump NAT44 users @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; +define nat44_user_dump { u32 client_index; u32 context; - bool enable; }; -/** \brief Check if forwarding is enabled or disabled +/** \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_forwarding_is_enabled { +define nat44_user_session_dump { option deprecated; + u32 client_index; u32 context; + vl_api_ip4_address_t ip_address; + u32 vrf_id; }; -/** \brief Response to check if forwarding is enabled or disabled +/** \brief NAT44 user's sessions response @param context - sender context, to match reply w/ request - @param enabled - true if enabled, false if disabled + @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_forwarding_is_enabled_reply { +define nat44_user_session_details { option deprecated; + u32 context; - bool enabled; + 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 Set NAT handoff frame queue options +/** \brief NAT44 user's sessions @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 + @param ip_address - IPv4 address of the user to dump + @param vrf_id - VRF_ID */ -autoreply define nat44_ed_set_fq_options { - option in_progress; +define nat44_user_session_v2_dump { + option deprecated; + u32 client_index; u32 context; - u32 frame_queue_nelts; + vl_api_ip4_address_t ip_address; + u32 vrf_id; }; -/** \brief Show NAT handoff frame queue options - @param client_index - opaque cookie to identify the sender +/** \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) + @param is_timed_out - true, if session is timed out, and false, if session + is active */ -define nat44_ed_show_fq_options -{ - option in_progress; - u32 client_index; +define nat44_user_session_v2_details { + option deprecated; + 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; + bool is_timed_out; }; -/** \brief Show NAT handoff frame queue options reply +/** \brief NAT44 user's sessions response @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 + @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 since VPP start + @param time_since_last_heard - difference between current vpp time and last_heard value + @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) + @param is_timed_out - true, if session is timed out, and false, if session + is active */ -define nat44_ed_show_fq_options_reply -{ - option in_progress; +define nat44_user_session_v3_details { u32 context; - i32 retval; - u32 frame_queue_nelts; + 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 time_since_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; + bool is_timed_out; +}; + +/** \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_v3_dump { + u32 client_index; + u32 context; + vl_api_ip4_address_t ip_address; + u32 vrf_id; }; diff --git a/src/plugins/nat/nat44-ed/nat44_ed.c b/src/plugins/nat/nat44-ed/nat44_ed.c index 4e13907a9d8..08e577747c3 100644 --- a/src/plugins/nat/nat44-ed/nat44_ed.c +++ b/src/plugins/nat/nat44-ed/nat44_ed.c @@ -1,6 +1,4 @@ /* - * 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. @@ -28,15 +26,17 @@ #include <vppinfra/bihash_16_8.h> #include <nat/lib/log.h> -#include <nat/lib/nat_syslog.h> #include <nat/lib/nat_inlines.h> #include <nat/lib/ipfix_logging.h> +#include <vnet/syslog/syslog.h> +#include <nat/lib/nat_syslog_constants.h> +#include <nat/lib/nat_syslog.h> #include <nat/nat44-ed/nat44_ed.h> #include <nat/nat44-ed/nat44_ed_affinity.h> #include <nat/nat44-ed/nat44_ed_inlines.h> -#include <vpp/stats/stat_segment.h> +#include <vlib/stats/stats.h> snat_main_t snat_main; @@ -59,7 +59,7 @@ static_always_inline void nat_validate_interface_counters (snat_main_t *sm, if (PREDICT_FALSE (sm->enabled)) \ { \ nat_log_err ("plugin enabled"); \ - return 1; \ + return VNET_API_ERROR_FEATURE_ALREADY_ENABLED; \ } \ } \ while (0) @@ -71,12 +71,11 @@ static_always_inline void nat_validate_interface_counters (snat_main_t *sm, if (PREDICT_FALSE (!sm->enabled)) \ { \ nat_log_err ("plugin disabled"); \ - return 1; \ + return VNET_API_ERROR_FEATURE_ALREADY_DISABLED; \ } \ } \ while (0) -/* Hook up input features */ VNET_FEATURE_INIT (nat_pre_in2out, static) = { .arc_name = "ip4-unicast", .node_name = "nat-pre-in2out", @@ -90,6 +89,18 @@ VNET_FEATURE_INIT (nat_pre_out2in, static) = { "ip4-dhcp-client-detect", "ip4-sv-reassembly-feature"), }; +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 (snat_in2out_worker_handoff, static) = { .arc_name = "ip4-unicast", .node_name = "nat44-in2out-worker-handoff", @@ -101,17 +112,6 @@ VNET_FEATURE_INIT (snat_out2in_worker_handoff, static) = { .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", @@ -123,32 +123,9 @@ VNET_FEATURE_INIT (ip4_nat44_ed_out2in, static) = { .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) = { +VNET_FEATURE_INIT (nat_pre_in2out_output, static) = { .arc_name = "ip4-output", - .node_name = "nat44-in2out-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"), }; @@ -158,12 +135,6 @@ VNET_FEATURE_INIT (ip4_snat_in2out_output_worker_handoff, static) = { .runs_after = VNET_FEATURES ("ip4-sv-reassembly-output-feature"), .runs_before = VNET_FEATURES ("acl-plugin-out-ip4-fa"), }; -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", @@ -176,156 +147,375 @@ VLIB_PLUGIN_REGISTER () = { .description = "Network Address Translation (NAT)", }; -static void nat44_ed_db_init (u32 translations, u32 translation_buckets); - +static void nat44_ed_db_init (); static void nat44_ed_db_free (); +static void nat44_ed_worker_db_free (snat_main_per_thread_data_t *tsm); + +static int nat44_ed_add_static_mapping_internal ( + ip4_address_t l_addr, ip4_address_t e_addr, u16 l_port, u16 e_port, + ip_protocol_t proto, u32 vrf_id, u32 sw_if_index, u32 flags, + ip4_address_t pool_addr, u8 *tag); +static int nat44_ed_del_static_mapping_internal (ip4_address_t l_addr, + ip4_address_t e_addr, + u16 l_port, u16 e_port, + ip_protocol_t proto, + u32 vrf_id, u32 flags); u32 nat_calc_bihash_buckets (u32 n_elts); -u8 * -format_session_kvp (u8 * s, va_list * args) +static_always_inline int +nat44_ed_sm_i2o_add (snat_main_t *sm, snat_static_mapping_t *m, + ip4_address_t addr, u16 port, u32 fib_index, u8 proto) { - clib_bihash_kv_8_8_t *v = va_arg (*args, clib_bihash_kv_8_8_t *); + ASSERT (!pool_is_free (sm->static_mappings, m)); + clib_bihash_kv_16_8_t kv; + nat44_ed_sm_init_i2o_kv (&kv, addr.as_u32, port, fib_index, proto, + m - sm->static_mappings); + return clib_bihash_add_del_16_8 (&sm->flow_hash, &kv, 1 /*is_add*/); +} - 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)); +static_always_inline int +nat44_ed_sm_i2o_del (snat_main_t *sm, ip4_address_t addr, u16 port, + u32 fib_index, u8 proto) +{ + clib_bihash_kv_16_8_t kv; + nat44_ed_sm_init_i2o_k (&kv, addr.as_u32, port, fib_index, proto); + return clib_bihash_add_del_16_8 (&sm->flow_hash, &kv, 0 /*is_add*/); +} - return s; +static_always_inline int +nat44_ed_sm_o2i_add (snat_main_t *sm, snat_static_mapping_t *m, + ip4_address_t addr, u16 port, u32 fib_index, u8 proto) +{ + ASSERT (!pool_is_free (sm->static_mappings, m)); + clib_bihash_kv_16_8_t kv; + nat44_ed_sm_init_o2i_kv (&kv, addr.as_u32, port, fib_index, proto, + m - sm->static_mappings); + return clib_bihash_add_del_16_8 (&sm->flow_hash, &kv, 1 /*is_add*/); } -u8 * -format_static_mapping_kvp (u8 * s, va_list * args) +static_always_inline int +nat44_ed_sm_o2i_del (snat_main_t *sm, ip4_address_t addr, u16 port, + u32 fib_index, u8 proto) { - clib_bihash_kv_8_8_t *v = va_arg (*args, clib_bihash_kv_8_8_t *); + clib_bihash_kv_16_8_t kv; + nat44_ed_sm_init_o2i_k (&kv, addr.as_u32, port, fib_index, proto); + return clib_bihash_add_del_16_8 (&sm->flow_hash, &kv, 0 /*is_add*/); +} + +void +nat44_ed_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"); - s = format (s, "%U static-mapping-index %llu", - format_snat_key, v->key, v->value); + if (nat_ed_ses_o2i_flow_hash_add_del (sm, thread_index, s, 0)) + nat_elog_warn (sm, "flow hash del failed"); - return s; + if (na44_ed_is_fwd_bypass_session (s)) + { + return; + } + + if (nat44_ed_is_affinity_session (s)) + nat_affinity_unlock (s->ext_host_addr, s->out2in.addr, s->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->proto, nat44_ed_is_twice_nat_session (s)); + + 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->proto, + s->in2out.port, s->out2in.port, s->in2out.fib_index); + } } -u8 * -format_ed_session_kvp (u8 * s, va_list * args) +static ip_interface_address_t * +nat44_ed_get_ip_interface_address (u32 sw_if_index, ip4_address_t addr) { - clib_bihash_kv_16_8_t *v = va_arg (*args, clib_bihash_kv_16_8_t *); + snat_main_t *sm = &snat_main; + + ip_lookup_main_t *lm = &sm->ip4_main->lookup_main; + ip_interface_address_t *ia; + ip4_address_t *ip4a; + + foreach_ip_interface_address ( + lm, ia, sw_if_index, 1, ({ + ip4a = ip_interface_address_get_address (lm, ia); + nat_log_debug ("sw_if_idx: %u addr: %U ? %U", sw_if_index, + format_ip4_address, ip4a, format_ip4_address, &addr); + if (ip4a->as_u32 == addr.as_u32) + { + return ia; + } + })); + return NULL; +} - u8 proto; - u16 r_port, l_port; - ip4_address_t l_addr, r_addr; +static int +nat44_ed_resolve_nat_addr_len (snat_address_t *ap, + snat_interface_t *interfaces) +{ + ip_interface_address_t *ia; + snat_interface_t *i; 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)); + pool_foreach (i, interfaces) + { + if (!nat44_ed_is_interface_outside (i)) + { + continue; + } - return s; + fib_index = ip4_fib_table_get_index_for_sw_if_index (i->sw_if_index); + if (fib_index != ap->fib_index) + { + continue; + } + + if ((ia = nat44_ed_get_ip_interface_address (i->sw_if_index, ap->addr))) + { + ap->addr_len = ia->address_length; + ap->sw_if_index = i->sw_if_index; + ap->net.as_u32 = ap->addr.as_u32 & ip4_main.fib_masks[ap->addr_len]; + + nat_log_debug ("pool addr %U binds to -> sw_if_idx: %u net: %U/%u", + format_ip4_address, &ap->addr, ap->sw_if_index, + format_ip4_address, &ap->net, ap->addr_len); + return 0; + } + } + return 1; } -void -nat_free_session_data (snat_main_t * sm, snat_session_t * s, u32 thread_index, - u8 is_ha) +static void +nat44_ed_update_outside_if_addresses (snat_address_t *ap) { - 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"); + snat_main_t *sm = &snat_main; - if (nat_ed_ses_o2i_flow_hash_add_del (sm, thread_index, s, 0)) - nat_elog_warn (sm, "flow hash del failed"); + if (!nat44_ed_resolve_nat_addr_len (ap, sm->interfaces)) + { + return; + } - if (is_fwd_bypass_session (s)) + if (!nat44_ed_resolve_nat_addr_len (ap, sm->output_feature_interfaces)) { return; } +} + +static void +nat44_ed_bind_if_addr_to_nat_addr (u32 sw_if_index) +{ + snat_main_t *sm = &snat_main; + ip_interface_address_t *ia; + snat_address_t *ap; + + u32 fib_index = ip4_fib_table_get_index_for_sw_if_index (sw_if_index); + + vec_foreach (ap, sm->addresses) + { + if (fib_index != ap->fib_index) + { + continue; + } + + if ((ia = nat44_ed_get_ip_interface_address (sw_if_index, ap->addr))) + { + ap->addr_len = ia->address_length; + ap->sw_if_index = sw_if_index; + ap->net.as_u32 = ap->addr.as_u32 & ip4_main.fib_masks[ap->addr_len]; + + nat_log_debug ("pool addr %U binds to -> sw_if_idx: %u net: %U/%u", + format_ip4_address, &ap->addr, ap->sw_if_index, + format_ip4_address, &ap->net, ap->addr_len); + return; + } + } +} - if (is_affinity_sessions (s)) - nat_affinity_unlock (s->ext_host_addr, s->out2in.addr, - s->nat_proto, s->out2in.port); +static_always_inline snat_fib_entry_reg_t * +nat44_ed_get_fib_entry_reg (ip4_address_t addr, u32 sw_if_index, int *out_idx) +{ + snat_main_t *sm = &snat_main; + snat_fib_entry_reg_t *fe; + int i; - 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)); + for (i = 0; i < vec_len (sm->fib_entry_reg); i++) + { + fe = sm->fib_entry_reg + i; + if ((addr.as_u32 == fe->addr.as_u32) && (sw_if_index == fe->sw_if_index)) + { + if (out_idx) + { + *out_idx = i; + } + return fe; + } + } + return NULL; +} - if (snat_is_unk_proto_session (s)) - return; +static void +nat44_ed_add_fib_entry_reg (ip4_address_t addr, u32 sw_if_index) +{ + // 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. + snat_main_t *sm = &snat_main; + snat_fib_entry_reg_t *fe; - if (!is_ha) + if (!(fe = nat44_ed_get_fib_entry_reg (addr, sw_if_index, 0))) { - /* 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); + fib_prefix_t prefix = { + .fp_len = 32, + .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); + 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); + + vec_add2 (sm->fib_entry_reg, fe, 1); + clib_memset (fe, 0, sizeof (*fe)); + fe->addr.as_u32 = addr.as_u32; + fe->sw_if_index = sw_if_index; + } + fe->count++; +} + +static void +nat44_ed_del_fib_entry_reg (ip4_address_t addr, u32 sw_if_index) +{ + snat_main_t *sm = &snat_main; + snat_fib_entry_reg_t *fe; + int i; + + if ((fe = nat44_ed_get_fib_entry_reg (addr, sw_if_index, &i))) + { + fe->count--; + if (0 == fe->count) + { + fib_prefix_t prefix = { + .fp_len = 32, + .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); + fib_table_entry_delete (fib_index, &prefix, sm->fib_src_low); + vec_del1 (sm->fib_entry_reg, i); + } } +} - /* Twice NAT address and port for external host */ - if (is_twice_nat_session (s)) +static void +nat44_ed_add_del_interface_fib_reg_entries (ip4_address_t addr, u8 is_add) +{ + snat_main_t *sm = &snat_main; + snat_interface_t *i; + + pool_foreach (i, sm->interfaces) + { + if (nat44_ed_is_interface_outside (i)) + { + if (is_add) + { + nat44_ed_add_fib_entry_reg (addr, i->sw_if_index); + } + else + { + nat44_ed_del_fib_entry_reg (addr, i->sw_if_index); + } + } + } + pool_foreach (i, sm->output_feature_interfaces) { - 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 (nat44_ed_is_interface_outside (i)) + { + if (is_add) + { + nat44_ed_add_fib_entry_reg (addr, i->sw_if_index); + } + else + { + nat44_ed_del_fib_entry_reg (addr, i->sw_if_index); + } + } } +} - if (snat_is_session_static (s)) - return; +static_always_inline void +nat44_ed_add_del_nat_addr_fib_reg_entries (u32 sw_if_index, u8 is_add) +{ + snat_main_t *sm = &snat_main; + snat_address_t *ap; - snat_free_outside_address_and_port (sm->addresses, thread_index, - &s->out2in.addr, s->out2in.port, - s->nat_proto); + vec_foreach (ap, sm->addresses) + { + if (is_add) + { + nat44_ed_add_fib_entry_reg (ap->addr, sw_if_index); + } + else + { + nat44_ed_del_fib_entry_reg (ap->addr, sw_if_index); + } + } } -void -snat_add_del_addr_to_fib (ip4_address_t * addr, u8 p_len, u32 sw_if_index, - int is_add) +static_always_inline void +nat44_ed_add_del_sm_fib_reg_entries (u32 sw_if_index, u8 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); + snat_static_mapping_t *m; - 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); + pool_foreach (m, sm->static_mappings) + { + if (is_add) + { + nat44_ed_add_fib_entry_reg (m->external_addr, sw_if_index); + } + else + { + nat44_ed_del_fib_entry_reg (m->external_addr, sw_if_index); + } + } } int -snat_add_address (snat_main_t * sm, ip4_address_t * addr, u32 vrf_id, - u8 twice_nat) +nat44_ed_add_address (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 (); + snat_main_t *sm = &snat_main; + snat_address_t *ap, *addresses; + + addresses = twice_nat ? sm->twice_nat_addresses : sm->addresses; - /* Check if address already exists */ - vec_foreach (ap, twice_nat ? sm->twice_nat_addresses : sm->addresses) + if (!sm->enabled) + { + return VNET_API_ERROR_UNSUPPORTED; + } + + // check if address already exists + vec_foreach (ap, addresses) { if (ap->addr.as_u32 == addr->as_u32) { @@ -335,62 +525,231 @@ snat_add_address (snat_main_t * sm, ip4_address_t * addr, u32 vrf_id, } if (twice_nat) - vec_add2 (sm->twice_nat_addresses, ap, 1); + { + vec_add2 (sm->twice_nat_addresses, ap, 1); + } else - vec_add2 (sm->addresses, ap, 1); + { + vec_add2 (sm->addresses, ap, 1); + } + ap->addr_len = ~0; + ap->fib_index = ~0; 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); + { + ap->fib_index = fib_table_find_or_create_and_lock ( + FIB_PROTOCOL_IP4, vrf_id, sm->fib_src_low); + } + + if (!twice_nat) + { + // if we don't have enabled interface we don't add address + // to fib + nat44_ed_add_del_interface_fib_reg_entries (*addr, 1); + nat44_ed_update_outside_if_addresses (ap); + } + return 0; +} + +int +nat44_ed_del_address (ip4_address_t addr, u8 twice_nat) +{ + snat_main_t *sm = &snat_main; + snat_address_t *a = 0, *addresses; + snat_session_t *ses; + u32 *ses_to_be_removed = 0, *ses_index; + snat_main_per_thread_data_t *tsm; + int j; + + addresses = twice_nat ? sm->twice_nat_addresses : sm->addresses; + + for (j = 0; j < vec_len (addresses); j++) + { + if (addresses[j].addr.as_u32 == addr.as_u32) + { + a = addresses + j; + break; + } + } + if (!a) + { + nat_log_err ("no such address"); + return VNET_API_ERROR_NO_SUCH_ENTRY; + } + + // delete dynamic sessions only + vec_foreach (tsm, sm->per_thread_data) + { + pool_foreach (ses, tsm->sessions) + { + if (ses->flags & SNAT_SESSION_FLAG_STATIC_MAPPING) + { + continue; + } + if (ses->out2in.addr.as_u32 == addr.as_u32) + { + nat44_ed_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); + } + + if (!twice_nat) + { + nat44_ed_add_del_interface_fib_reg_entries (addr, 0); + } + + if (a->fib_index != ~0) + { + fib_table_unlock (a->fib_index, FIB_PROTOCOL_IP4, sm->fib_src_low); + } + + if (!twice_nat) + { + vec_del1 (sm->addresses, j); + } else - ap->fib_index = ~0; + { + vec_del1 (sm->twice_nat_addresses, j); + } - #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 _ + return 0; +} - if (twice_nat) - return 0; +vrf_table_t * +nat44_ed_get_vrf_table (u32 table_vrf_id) +{ + snat_main_t *sm = &snat_main; + vrf_table_t *t; - /* Add external address to FIB */ - pool_foreach (i, sm->interfaces) - { - if (nat_interface_is_inside (i)) - continue; + pool_foreach (t, sm->vrf_tables) + { + if (table_vrf_id == t->table_vrf_id) + { + return t; + } + } + return NULL; +} - 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; +vrf_route_t * +nat44_ed_get_vrf_route (vrf_table_t *t, u32 vrf_id) +{ + vrf_route_t *r; - snat_add_del_addr_to_fib (addr, 32, i->sw_if_index, 1); - break; - } + pool_foreach (r, t->routes) + { + if (vrf_id == r->vrf_id) + { + return r; + } + } + return NULL; +} + +int +nat44_ed_add_del_vrf_table (u32 table_vrf_id, bool is_add) +{ + snat_main_t *sm = &snat_main; + vrf_table_t *t; + vrf_route_t *r; + + t = nat44_ed_get_vrf_table (table_vrf_id); + if (t) + { + if (is_add) + { + return VNET_API_ERROR_VALUE_EXIST; + } + pool_foreach (r, t->routes) + { + fib_table_unlock (r->fib_index, FIB_PROTOCOL_IP4, sm->fib_src_low); + } + fib_table_unlock (t->table_fib_index, FIB_PROTOCOL_IP4, sm->fib_src_low); + + pool_free (t->routes); + pool_put (sm->vrf_tables, t); + } + else + { + if (!is_add) + { + return VNET_API_ERROR_NO_SUCH_ENTRY; + } + pool_get (sm->vrf_tables, t); + clib_memset (t, 0, sizeof (*t)); + t->table_vrf_id = table_vrf_id; + t->table_fib_index = fib_table_find_or_create_and_lock ( + FIB_PROTOCOL_IP4, table_vrf_id, sm->fib_src_low); + } return 0; } -static int -is_snat_address_used_in_static_mapping (snat_main_t * sm, ip4_address_t addr) +void +nat44_ed_del_vrf_tables () { - snat_static_mapping_t *m; - pool_foreach (m, sm->static_mappings) - { - if (is_sm_addr_only (m->flags) || is_sm_out2in_only (m->flags) || - is_sm_identity_nat (m->flags)) - continue; - if (m->external_addr.as_u32 == addr.as_u32) - return 1; - } + snat_main_t *sm = &snat_main; + vrf_table_t *t; + vrf_route_t *r; + + pool_foreach (t, sm->vrf_tables) + { + pool_foreach (r, t->routes) + { + fib_table_unlock (r->fib_index, FIB_PROTOCOL_IP4, sm->fib_src_low); + } + fib_table_unlock (t->table_fib_index, FIB_PROTOCOL_IP4, sm->fib_src_low); + pool_free (t->routes); + } + pool_free (sm->vrf_tables); +} + +int +nat44_ed_add_del_vrf_route (u32 table_vrf_id, u32 vrf_id, bool is_add) +{ + snat_main_t *sm = &snat_main; + vrf_table_t *t; + vrf_route_t *r; + + t = nat44_ed_get_vrf_table (table_vrf_id); + if (!t) + { + return VNET_API_ERROR_NO_SUCH_ENTRY; + } + + r = nat44_ed_get_vrf_route (t, vrf_id); + if (r) + { + if (is_add) + { + return VNET_API_ERROR_VALUE_EXIST; + } + fib_table_unlock (r->fib_index, FIB_PROTOCOL_IP4, sm->fib_src_low); + pool_put (t->routes, r); + } + else + { + if (!is_add) + { + return VNET_API_ERROR_NO_SUCH_ENTRY; + } + pool_get (t->routes, r); + clib_memset (r, 0, sizeof (*r)); + r->vrf_id = vrf_id; + r->fib_index = fib_table_find_or_create_and_lock ( + FIB_PROTOCOL_IP4, vrf_id, sm->fib_src_low); + } + return 0; } @@ -401,9 +760,9 @@ get_thread_idx_by_port (u16 e_port) 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]; + thread_idx = sm->first_worker_index + + sm->workers[(e_port - ED_USER_PORT_OFFSET) / + sm->port_per_thread % _vec_len (sm->workers)]; } return thread_idx; } @@ -427,18 +786,17 @@ nat_ed_static_mapping_del_sessions (snat_main_t * sm, } 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 ((s->out2in.addr.as_u32 != e_addr.as_u32) || + s->out2in.port != e_port || s->in2out.port != l_port || + s->proto != protocol) + continue; } - if (is_lb_session (s)) + if (nat44_ed_is_lb_session (s)) continue; - if (!snat_is_session_static (s)) + if (!nat44_ed_is_session_static (s)) continue; - nat_free_session_data (sm, s, tsm - sm->per_thread_data, 0); + nat44_ed_free_session_data (sm, s, tsm - sm->per_thread_data, 0); vec_add1 (indexes_to_free, s - tsm->sessions); if (!addr_only) break; @@ -452,118 +810,50 @@ nat_ed_static_mapping_del_sessions (snat_main_t * sm, vec_free (indexes_to_free); } -int -nat44_ed_reserve_port (ip4_address_t addr, u16 port, nat_protocol_t proto) +static_always_inline snat_static_mapping_t * +nat44_ed_sm_lookup (snat_main_t *sm, clib_bihash_kv_16_8_t *kv) { - u32 ti = get_thread_idx_by_port (port); - snat_main_t *sm = &snat_main; - snat_address_t *a = 0; - int i; - - for (i = 0; i < vec_len (sm->addresses); i++) + clib_bihash_kv_16_8_t v; + int rc = clib_bihash_search_16_8 (&sm->flow_hash, kv, &v); + if (!rc) { - a = sm->addresses + i; - - if (a->addr.as_u32 != addr.as_u32) - continue; - - switch (proto) - { -#define _(N, j, n, s) \ - case NAT_PROTOCOL_##N: \ - if (a->busy_##n##_port_refcounts[port]) \ - goto done; \ - ++a->busy_##n##_port_refcounts[port]; \ - if (port > 1024) \ - { \ - a->busy_##n##_ports++; \ - a->busy_##n##_ports_per_thread[ti]++; \ - } \ - break; - foreach_nat_protocol -#undef _ - default : nat_elog_info (sm, "unknown protocol"); - goto done; - } - - return 0; + ASSERT (0 == ed_value_get_thread_index (&v)); + return pool_elt_at_index (sm->static_mappings, + ed_value_get_session_index (&v)); } - -done: - return 1; + return NULL; } -int -nat44_ed_free_port (ip4_address_t addr, u16 port, nat_protocol_t proto) +snat_static_mapping_t * +nat44_ed_sm_o2i_lookup (snat_main_t *sm, ip4_address_t addr, u16 port, + u32 fib_index, u8 proto) { - u32 ti = get_thread_idx_by_port (port); - snat_main_t *sm = &snat_main; - snat_address_t *a = 0; - int i; - - for (i = 0; i < vec_len (sm->addresses); i++) - { - a = sm->addresses + i; - - if (a->addr.as_u32 != addr.as_u32) - continue; - - switch (proto) - { -#define _(N, j, n, s) \ - case NAT_PROTOCOL_##N: \ - --a->busy_##n##_port_refcounts[port]; \ - if (port > 1024) \ - { \ - a->busy_##n##_ports--; \ - a->busy_##n##_ports_per_thread[ti]--; \ - } \ - break; - foreach_nat_protocol -#undef _ - default : nat_elog_info (sm, "unknown protocol"); - goto done; - } - - return 0; - } - -done: - return 1; + clib_bihash_kv_16_8_t kv; + nat44_ed_sm_init_o2i_k (&kv, addr.as_u32, port, fib_index, proto); + return nat44_ed_sm_lookup (sm, &kv); } -void -nat44_ed_add_resolve_record (ip4_address_t l_addr, u16 l_port, u16 e_port, - nat_protocol_t proto, u32 vrf_id, u32 sw_if_index, - u32 flags, ip4_address_t pool_addr, u8 *tag) +snat_static_mapping_t * +nat44_ed_sm_i2o_lookup (snat_main_t *sm, ip4_address_t addr, u16 port, + u32 fib_index, u8 proto) { - snat_static_map_resolve_t *rp; - snat_main_t *sm = &snat_main; - - vec_add2 (sm->to_resolve, rp, 1); - rp->l_addr.as_u32 = l_addr.as_u32; - rp->l_port = l_port; - rp->e_port = e_port; - rp->sw_if_index = sw_if_index; - rp->vrf_id = vrf_id; - rp->proto = proto; - rp->flags = flags; - rp->pool_addr = pool_addr; - rp->tag = vec_dup (tag); + clib_bihash_kv_16_8_t kv; + nat44_ed_sm_init_i2o_k (&kv, addr.as_u32, port, fib_index, proto); + return nat44_ed_sm_lookup (sm, &kv); } -int +static snat_static_mapping_resolve_t * nat44_ed_get_resolve_record (ip4_address_t l_addr, u16 l_port, u16 e_port, - nat_protocol_t proto, u32 vrf_id, u32 sw_if_index, - u32 flags, int *out) + ip_protocol_t proto, u32 vrf_id, u32 sw_if_index, + u32 flags, int *out_idx) { - snat_static_map_resolve_t *rp; + snat_static_mapping_resolve_t *rp; snat_main_t *sm = &snat_main; int i; - for (i = 0; i < vec_len (sm->to_resolve); i++) + for (i = 0; i < vec_len (sm->sm_to_resolve); i++) { - rp = sm->to_resolve + i; + rp = sm->sm_to_resolve + i; if (rp->sw_if_index == sw_if_index && rp->vrf_id == vrf_id) { @@ -592,27 +882,27 @@ nat44_ed_get_resolve_record (ip4_address_t l_addr, u16 l_port, u16 e_port, { continue; } - if (out) + if (out_idx) { - *out = i; + *out_idx = i; } - return 0; + return rp; } } - return 1; + return NULL; } -int +static int nat44_ed_del_resolve_record (ip4_address_t l_addr, u16 l_port, u16 e_port, - nat_protocol_t proto, u32 vrf_id, u32 sw_if_index, + ip_protocol_t proto, u32 vrf_id, u32 sw_if_index, u32 flags) { snat_main_t *sm = &snat_main; int i; - if (!nat44_ed_get_resolve_record (l_addr, l_port, e_port, proto, vrf_id, - sw_if_index, flags, &i)) + if (nat44_ed_get_resolve_record (l_addr, l_port, e_port, proto, vrf_id, + sw_if_index, flags, &i)) { - vec_del1 (sm->to_resolve, i); + vec_del1 (sm->sm_to_resolve, i); return 0; } return 1; @@ -639,45 +929,98 @@ nat44_ed_validate_sm_input (u32 flags) int nat44_ed_add_static_mapping (ip4_address_t l_addr, ip4_address_t e_addr, - u16 l_port, u16 e_port, nat_protocol_t proto, + u16 l_port, u16 e_port, ip_protocol_t proto, u32 vrf_id, u32 sw_if_index, u32 flags, ip4_address_t pool_addr, u8 *tag) { + snat_static_mapping_resolve_t *rp; snat_main_t *sm = &snat_main; - clib_bihash_kv_8_8_t kv, value; - snat_interface_t *interface; - nat44_lb_addr_port_t *local; - snat_static_mapping_t *m; - u32 fib_index = ~0; int rv; + if (!sm->enabled) + { + return VNET_API_ERROR_UNSUPPORTED; + } + rv = nat44_ed_validate_sm_input (flags); if (rv != 0) { return rv; } - if (is_sm_addr_only (flags)) + // interface bound mapping + if (is_sm_switch_address (flags)) { - e_port = l_port = proto = 0; + if (nat44_ed_get_resolve_record (l_addr, l_port, e_port, proto, vrf_id, + sw_if_index, flags, 0)) + { + return VNET_API_ERROR_VALUE_EXIST; + } + + vec_add2 (sm->sm_to_resolve, rp, 1); + rp->l_addr.as_u32 = l_addr.as_u32; + rp->l_port = l_port; + rp->e_port = e_port; + rp->sw_if_index = sw_if_index; + rp->vrf_id = vrf_id; + rp->proto = proto; + rp->flags = flags; + rp->pool_addr = pool_addr; + rp->tag = vec_dup (tag); + rp->is_resolved = 0; + + ip4_address_t *first_int_addr = + ip4_interface_first_address (sm->ip4_main, sw_if_index, 0); + if (!first_int_addr) + { + return 0; + } + + e_addr.as_u32 = first_int_addr->as_u32; + rp->is_resolved = 1; } - if (is_sm_switch_address (flags)) + rv = nat44_ed_add_static_mapping_internal (l_addr, e_addr, l_port, e_port, + proto, vrf_id, sw_if_index, flags, + pool_addr, tag); + if ((0 != rv) && is_sm_switch_address (flags)) { - // this mapping is interface bound - ip4_address_t *first_int_addr; + nat44_ed_del_resolve_record (l_addr, l_port, e_port, proto, vrf_id, + sw_if_index, flags); + } + + return rv; +} + +int +nat44_ed_del_static_mapping (ip4_address_t l_addr, ip4_address_t e_addr, + u16 l_port, u16 e_port, ip_protocol_t proto, + u32 vrf_id, u32 sw_if_index, u32 flags) +{ + snat_main_t *sm = &snat_main; + int rv; + + if (!sm->enabled) + { + return VNET_API_ERROR_UNSUPPORTED; + } + + rv = nat44_ed_validate_sm_input (flags); + if (rv != 0) + { + return rv; + } - // check if this record isn't registered for resolve - if (!nat44_ed_get_resolve_record (l_addr, l_port, e_port, proto, vrf_id, - sw_if_index, flags, 0)) + // interface bound mapping + if (is_sm_switch_address (flags)) + { + if (nat44_ed_del_resolve_record (l_addr, l_port, e_port, proto, vrf_id, + sw_if_index, flags)) { - return VNET_API_ERROR_VALUE_EXIST; + return VNET_API_ERROR_NO_SUCH_ENTRY; } - // register record for resolve - nat44_ed_add_resolve_record (l_addr, l_port, e_port, proto, vrf_id, - sw_if_index, flags, pool_addr, tag); - first_int_addr = + ip4_address_t *first_int_addr = ip4_interface_first_address (sm->ip4_main, sw_if_index, 0); if (!first_int_addr) { @@ -688,25 +1031,44 @@ nat44_ed_add_static_mapping (ip4_address_t l_addr, ip4_address_t e_addr, e_addr.as_u32 = first_int_addr->as_u32; } + return nat44_ed_del_static_mapping_internal (l_addr, e_addr, l_port, e_port, + proto, vrf_id, flags); +} + +static int +nat44_ed_add_static_mapping_internal (ip4_address_t l_addr, + ip4_address_t e_addr, u16 l_port, + u16 e_port, ip_protocol_t proto, + u32 vrf_id, u32 sw_if_index, u32 flags, + ip4_address_t pool_addr, u8 *tag) +{ + snat_main_t *sm = &snat_main; + nat44_lb_addr_port_t *local; + snat_static_mapping_t *m; + u32 fib_index = ~0; + + if (is_sm_addr_only (flags)) + { + e_port = l_port = proto = 0; + } + if (is_sm_identity_nat (flags)) { l_port = e_port; l_addr.as_u32 = e_addr.as_u32; } - // fib index 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 = nat44_ed_sm_o2i_lookup (sm, e_addr, e_port, 0, proto); + if (m) { - m = pool_elt_at_index (sm->static_mappings, value.value); + // case: + // adding local identity nat record for different vrf table + if (!is_sm_identity_nat (m->flags)) { return VNET_API_ERROR_VALUE_EXIST; } - // case: - // adding local identity nat record for different vrf table pool_foreach (local, m->locals) { if (local->vrf_id == vrf_id) @@ -721,9 +1083,8 @@ nat44_ed_add_static_mapping (ip4_address_t l_addr, ip4_address_t e_addr, 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); + nat44_ed_sm_i2o_add (sm, m, m->local_addr, m->local_port, + local->fib_index, m->proto); return 0; } @@ -745,28 +1106,12 @@ nat44_ed_add_static_mapping (ip4_address_t l_addr, ip4_address_t e_addr, // identity nat supports multiple records in local mapping if (!(is_sm_out2in_only (flags) || is_sm_identity_nat (flags))) { - init_nat_k (&kv, l_addr, l_port, fib_index, proto); - if (!clib_bihash_search_8_8 (&sm->static_mapping_by_local, &kv, &value)) + if (nat44_ed_sm_i2o_lookup (sm, l_addr, l_port, fib_index, proto)) { return VNET_API_ERROR_VALUE_EXIST; } } - if (!(is_sm_out2in_only (flags) || is_sm_addr_only (flags) || - sm->static_mapping_only)) - { - if (nat44_ed_reserve_port (e_addr, e_port, proto)) - { - // remove resolve record - if (is_sm_switch_address (flags) && !is_sm_identity_nat (flags)) - { - nat44_ed_del_resolve_record (l_addr, l_port, e_port, proto, - vrf_id, sw_if_index, flags); - } - return VNET_API_ERROR_NO_SUCH_ENTRY; - } - } - pool_get (sm->static_mappings, m); clib_memset (m, 0, sizeof (*m)); @@ -774,13 +1119,9 @@ nat44_ed_add_static_mapping (ip4_address_t l_addr, ip4_address_t e_addr, m->local_addr = l_addr; m->external_addr = e_addr; + m->pool_addr = pool_addr; m->tag = vec_dup (tag); - if (is_sm_exact_address (flags) && is_sm_twice_nat (flags)) - { - m->pool_addr = pool_addr; - } - if (!is_sm_addr_only (flags)) { m->local_port = l_port; @@ -803,14 +1144,11 @@ nat44_ed_add_static_mapping (ip4_address_t l_addr, ip4_address_t e_addr, if (!is_sm_out2in_only (flags)) { - 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); + nat44_ed_sm_i2o_add (sm, m, m->local_addr, m->local_port, fib_index, + m->proto); } - 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); + nat44_ed_sm_o2i_add (sm, m, m->external_addr, m->external_port, 0, m->proto); if (sm->num_workers > 1) { @@ -824,79 +1162,29 @@ nat44_ed_add_static_mapping (ip4_address_t l_addr, ip4_address_t e_addr, vec_add1 (m->workers, worker_index); } - if (is_sm_identity_nat (flags) || !is_sm_addr_only (flags)) - return 0; - - 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, 1); - 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, 1); - break; - } + nat44_ed_add_del_interface_fib_reg_entries (e_addr, 1); return 0; } -int -nat44_ed_del_static_mapping (ip4_address_t l_addr, ip4_address_t e_addr, - u16 l_port, u16 e_port, nat_protocol_t proto, - u32 vrf_id, u32 sw_if_index, u32 flags) +static int +nat44_ed_del_static_mapping_internal (ip4_address_t l_addr, + ip4_address_t e_addr, u16 l_port, + u16 e_port, ip_protocol_t proto, + u32 vrf_id, u32 flags) { snat_main_per_thread_data_t *tsm; snat_main_t *sm = &snat_main; - clib_bihash_kv_8_8_t kv, value; - snat_interface_t *interface; nat44_lb_addr_port_t *local; snat_static_mapping_t *m; u32 fib_index = ~0; - int rv; - - rv = nat44_ed_validate_sm_input (flags); - if (rv != 0) - { - return rv; - } if (is_sm_addr_only (flags)) { e_port = l_port = proto = 0; } - if (is_sm_switch_address (flags)) - { - // this mapping is interface bound - ip4_address_t *first_int_addr; - - // delete record registered for resolve - if (nat44_ed_del_resolve_record (l_addr, l_port, e_port, proto, vrf_id, - sw_if_index, flags)) - { - return VNET_API_ERROR_NO_SUCH_ENTRY; - } - - first_int_addr = - ip4_interface_first_address (sm->ip4_main, sw_if_index, 0); - if (!first_int_addr) - { - // dhcp resolution required - return 0; - } - - e_addr.as_u32 = first_int_addr->as_u32; - } - if (is_sm_identity_nat (flags)) { l_port = e_port; @@ -904,24 +1192,17 @@ nat44_ed_del_static_mapping (ip4_address_t l_addr, ip4_address_t e_addr, } // fib index 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 = nat44_ed_sm_o2i_lookup (sm, e_addr, e_port, 0, proto); + if (!m) { - if (is_sm_switch_address (flags)) - { - return 0; - } return VNET_API_ERROR_NO_SUCH_ENTRY; } - m = pool_elt_at_index (sm->static_mappings, value.value); - if (is_sm_identity_nat (flags)) { - u8 failure = 1; + u8 found = 0; - if (!is_sm_switch_address (flags)) + if (vrf_id == ~0) { vrf_id = sm->inside_vrf_id; } @@ -933,11 +1214,11 @@ nat44_ed_del_static_mapping (ip4_address_t l_addr, ip4_address_t e_addr, local = pool_elt_at_index (m->locals, local - m->locals); fib_index = local->fib_index; pool_put (m->locals, local); - failure = 0; + found = 1; } } - if (failure) + if (!found) { return VNET_API_ERROR_NO_SUCH_ENTRY; } @@ -947,66 +1228,38 @@ nat44_ed_del_static_mapping (ip4_address_t l_addr, ip4_address_t e_addr, fib_index = m->fib_index; } - if (!(is_sm_out2in_only (flags) || is_sm_addr_only (flags) || - sm->static_mapping_only)) + if (!is_sm_out2in_only (flags)) { - if (nat44_ed_free_port (e_addr, e_port, proto)) - { - return VNET_API_ERROR_INVALID_VALUE; - } + nat44_ed_sm_i2o_del (sm, l_addr, l_port, fib_index, proto); } - if (!is_sm_out2in_only (flags)) + // delete sessions for static mapping + if (sm->num_workers > 1) { - init_nat_k (&kv, l_addr, l_port, fib_index, proto); - clib_bihash_add_del_8_8 (&sm->static_mapping_by_local, &kv, 0); + tsm = vec_elt_at_index (sm->per_thread_data, m->workers[0]); } - - if (!sm->static_mapping_only || sm->static_mapping_connection_tracking) + else { - // delete sessions for static mapping - 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); - - nat_ed_static_mapping_del_sessions ( - sm, tsm, m->local_addr, m->local_port, m->proto, fib_index, - is_sm_addr_only (flags), e_addr, e_port); + tsm = vec_elt_at_index (sm->per_thread_data, sm->num_workers); } - fib_table_unlock (fib_index, FIB_PROTOCOL_IP4, sm->fib_src_low); - - if (pool_elts (m->locals)) - return 0; - - // fib_index 0 - init_nat_k (&kv, e_addr, e_port, 0, proto); - clib_bihash_add_del_8_8 (&sm->static_mapping_by_external, &kv, 0); - - vec_free (m->tag); - vec_free (m->workers); - pool_put (sm->static_mappings, m); + nat_ed_static_mapping_del_sessions (sm, tsm, m->local_addr, m->local_port, + m->proto, fib_index, + is_sm_addr_only (flags), e_addr, e_port); - if (is_sm_identity_nat (flags) || !is_sm_addr_only (flags)) - return 0; + fib_table_unlock (fib_index, FIB_PROTOCOL_IP4, sm->fib_src_low); - pool_foreach (interface, sm->interfaces) + if (!pool_elts (m->locals)) { - if (nat_interface_is_inside (interface)) - continue; + // this is last record remove all required stuff + // fib_index 0 + nat44_ed_sm_o2i_del (sm, e_addr, e_port, 0, proto); - snat_add_del_addr_to_fib (&e_addr, 32, interface->sw_if_index, 0); - break; - } - - pool_foreach (interface, sm->output_feature_interfaces) - { - if (nat_interface_is_inside (interface)) - continue; + vec_free (m->tag); + vec_free (m->workers); + pool_put (sm->static_mappings, m); - snat_add_del_addr_to_fib (&e_addr, 32, interface->sw_if_index, 0); - break; + nat44_ed_add_del_interface_fib_reg_entries (e_addr, 0); } return 0; @@ -1014,66 +1267,59 @@ nat44_ed_del_static_mapping (ip4_address_t l_addr, ip4_address_t e_addr, int nat44_ed_add_lb_static_mapping (ip4_address_t e_addr, u16 e_port, - nat_protocol_t proto, + ip_protocol_t proto, nat44_lb_addr_port_t *locals, u32 flags, 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; nat44_lb_addr_port_t *local; uword *bitmap = 0; + int rc = 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 = 0; - else - m = pool_elt_at_index (sm->static_mappings, value.value); + if (!sm->enabled) + { + return VNET_API_ERROR_UNSUPPORTED; + } + + m = nat44_ed_sm_o2i_lookup (sm, e_addr, e_port, 0, proto); if (m) - return VNET_API_ERROR_VALUE_EXIST; + { + return VNET_API_ERROR_VALUE_EXIST; + } if (vec_len (locals) < 2) - return VNET_API_ERROR_INVALID_VALUE; + { + 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 || is_sm_out2in_only (flags))) + if (!is_sm_out2in_only (flags)) { + /* Find external address in allocated addresses and reserve port for + address and port pair mapping when dynamic translations enabled */ 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) + a = sm->addresses + i; + if (nat44_ed_sm_o2i_lookup (sm, a->addr, e_port, 0, 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; + return VNET_API_ERROR_VALUE_EXIST; } break; } } - /* External address must be allocated */ + // external address must be allocated if (!a) - return VNET_API_ERROR_NO_SUCH_ENTRY; + { + return VNET_API_ERROR_NO_SUCH_ENTRY; + } } pool_get (sm->static_mappings, m); @@ -1093,11 +1339,10 @@ nat44_ed_add_lb_static_mapping (ip4_address_t e_addr, u16 e_port, 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)) + if (nat44_ed_sm_o2i_add (sm, m, m->external_addr, m->external_port, 0, + m->proto)) { - nat_elog_err (sm, "static_mapping_by_external key add failed"); + nat_log_err ("sm o2i key add failed"); return VNET_API_ERROR_UNSPECIFIED; } @@ -1107,10 +1352,17 @@ nat44_ed_add_lb_static_mapping (ip4_address_t e_addr, u16 e_port, FIB_PROTOCOL_IP4, locals[i].vrf_id, sm->fib_src_low); if (!is_sm_out2in_only (flags)) { - 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); + if (nat44_ed_sm_i2o_add (sm, m, locals[i].addr, locals[i].port, 0, + proto)) + { + nat_log_err ("sm i2o key add failed"); + rc = VNET_API_ERROR_UNSPECIFIED; + // here we continue with add operation so that it can be safely + // reversed in delete path - otherwise we'd have to track what + // we've done and deal with partial cleanups and since bihash + // adds are (extremely improbable) the only points of failure, + // it's easier to just do it this way + } } locals[i].prefix = (i == 0) ? locals[i].probability : @@ -1137,68 +1389,36 @@ nat44_ed_add_lb_static_mapping (ip4_address_t e_addr, u16 e_port, } } - return 0; + return rc; } int nat44_ed_del_lb_static_mapping (ip4_address_t e_addr, u16 e_port, - nat_protocol_t proto, u32 flags) + ip_protocol_t proto, u32 flags) { snat_main_t *sm = &snat_main; snat_static_mapping_t *m; - clib_bihash_kv_8_8_t kv, value; - snat_address_t *a = 0; nat44_lb_addr_port_t *local; snat_main_per_thread_data_t *tsm; snat_session_t *s; - 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 = 0; - else - m = pool_elt_at_index (sm->static_mappings, value.value); + if (!sm->enabled) + { + return VNET_API_ERROR_UNSUPPORTED; + } + m = nat44_ed_sm_o2i_lookup (sm, e_addr, e_port, 0, proto); if (!m) return VNET_API_ERROR_NO_SUCH_ENTRY; if (!is_sm_lb (m->flags)) return VNET_API_ERROR_INVALID_VALUE; - /* Free external address port */ - if (!(sm->static_mapping_only || is_sm_out2in_only (flags))) - { - 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)) + if (nat44_ed_sm_o2i_del (sm, m->external_addr, m->external_port, 0, + m->proto)) { - nat_elog_err (sm, "static_mapping_by_external key del failed"); + nat_log_err ("sm o2i key del failed"); return VNET_API_ERROR_UNSPECIFIED; } @@ -1207,12 +1427,11 @@ nat44_ed_del_lb_static_mapping (ip4_address_t e_addr, u16 e_port, fib_table_unlock (local->fib_index, FIB_PROTOCOL_IP4, sm->fib_src_low); if (!is_sm_out2in_only (flags)) { - 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)) + if (nat44_ed_sm_i2o_del (sm, local->addr, local->port, + local->fib_index, m->proto)) { - nat_elog_err (sm, "static_mapping_by_local key del failed"); - return VNET_API_ERROR_UNSPECIFIED; + nat_log_err ("sm i2o key del failed"); + // For the same reasons as above } } @@ -1231,14 +1450,14 @@ nat44_ed_del_lb_static_mapping (ip4_address_t e_addr, u16 e_port, /* Delete sessions */ pool_foreach (s, tsm->sessions) { - if (!(is_lb_session (s))) + if (!(nat44_ed_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); + nat44_ed_free_session_data (sm, s, tsm - sm->per_thread_data, 0); nat_ed_session_delete (sm, s, tsm - sm->per_thread_data, 1); } } @@ -1259,12 +1478,11 @@ nat44_ed_del_lb_static_mapping (ip4_address_t e_addr, u16 e_port, int nat44_ed_add_del_lb_static_mapping_local (ip4_address_t e_addr, u16 e_port, ip4_address_t l_addr, u16 l_port, - nat_protocol_t proto, u32 vrf_id, + ip_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; @@ -1272,15 +1490,22 @@ nat44_ed_add_del_lb_static_mapping_local (ip4_address_t e_addr, u16 e_port, 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 (!sm->enabled) + { + return VNET_API_ERROR_UNSUPPORTED; + } + + m = nat44_ed_sm_o2i_lookup (sm, e_addr, e_port, 0, proto); if (!m) - return VNET_API_ERROR_NO_SUCH_ENTRY; + { + return VNET_API_ERROR_NO_SUCH_ENTRY; + } if (!is_sm_lb (m->flags)) - return VNET_API_ERROR_INVALID_VALUE; + { + return VNET_API_ERROR_INVALID_VALUE; + } pool_foreach (local, m->locals) { @@ -1295,7 +1520,9 @@ nat44_ed_add_del_lb_static_mapping_local (ip4_address_t e_addr, u16 e_port, if (is_add) { if (match_local) - return VNET_API_ERROR_VALUE_EXIST; + { + return VNET_API_ERROR_VALUE_EXIST; + } pool_get (m->locals, local); clib_memset (local, 0, sizeof (*local)); @@ -1309,10 +1536,13 @@ nat44_ed_add_del_lb_static_mapping_local (ip4_address_t e_addr, u16 e_port, if (!is_sm_out2in_only (m->flags)) { - 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"); + if (nat44_ed_sm_i2o_add (sm, m, l_addr, l_port, local->fib_index, + proto)) + { + nat_log_err ("sm i2o key add failed"); + pool_put (m->locals, local); + return VNET_API_ERROR_UNSPECIFIED; + } } } else @@ -1328,9 +1558,9 @@ nat44_ed_add_del_lb_static_mapping_local (ip4_address_t e_addr, u16 e_port, if (!is_sm_out2in_only (m->flags)) { - 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 (nat44_ed_sm_i2o_del (sm, l_addr, l_port, match_local->fib_index, + proto)) + nat_log_err ("sm i2o key del failed"); } if (sm->num_workers > 1) @@ -1347,15 +1577,15 @@ nat44_ed_add_del_lb_static_mapping_local (ip4_address_t e_addr, u16 e_port, /* Delete sessions */ pool_foreach (s, tsm->sessions) { - if (!(is_lb_session (s))) - continue; + if (!(nat44_ed_is_lb_session (s))) + continue; - if ((s->in2out.addr.as_u32 != match_local->addr.as_u32) || - s->in2out.port != match_local->port) - 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); + nat44_ed_free_session_data (sm, s, tsm - sm->per_thread_data, 0); + nat_ed_session_delete (sm, s, tsm - sm->per_thread_data, 1); } pool_put (m->locals, match_local); @@ -1396,116 +1626,6 @@ nat44_ed_add_del_lb_static_mapping_local (ip4_address_t e_addr, u16 e_port, 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) - { - pool_foreach (m, sm->static_mappings) - { - if (m->external_addr.as_u32 == addr.as_u32) - { - nat44_ed_del_static_mapping (m->local_addr, m->external_addr, - m->local_port, m->external_port, - m->proto, m->vrf_id, ~0, m->flags); - } - } - } - 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) { @@ -1515,19 +1635,19 @@ expire_per_vrf_sessions (u32 fib_index) 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; - } - } + pool_foreach (per_vrf_sessions, tsm->per_vrf_sessions_pool) + { + if ((per_vrf_sessions->rx_fib_index == fib_index) || + (per_vrf_sessions->tx_fib_index == fib_index)) + { + per_vrf_sessions->expired = 1; + } + } } } void -update_per_vrf_sessions_vec (u32 fib_index, int is_del) +update_per_vrf_sessions_pool (u32 fib_index, int is_del) { snat_main_t *sm = &snat_main; nat_fib_t *fib; @@ -1563,10 +1683,10 @@ update_per_vrf_sessions_vec (u32 fib_index, int is_del) } } -static_always_inline nat_outside_fib_t * -nat44_ed_get_outside_fib (nat_outside_fib_t *outside_fibs, u32 fib_index) +static_always_inline nat_fib_t * +nat44_ed_get_outside_fib (nat_fib_t *outside_fibs, u32 fib_index) { - nat_outside_fib_t *f; + nat_fib_t *f; vec_foreach (f, outside_fibs) { if (f->fib_index == fib_index) @@ -1597,10 +1717,8 @@ nat44_ed_add_interface (u32 sw_if_index, u8 is_inside) const char *del_feature_name, *feature_name; snat_main_t *sm = &snat_main; - nat_outside_fib_t *outside_fib; - snat_static_mapping_t *m; + nat_fib_t *outside_fib; snat_interface_t *i; - snat_address_t *ap; u32 fib_index; int rv; @@ -1619,8 +1737,8 @@ nat44_ed_add_interface (u32 sw_if_index, u8 is_inside) i = nat44_ed_get_interface (sm->interfaces, sw_if_index); if (i) { - if ((nat_interface_is_inside (i) && is_inside) || - (nat_interface_is_outside (i) && !is_inside)) + if ((nat44_ed_is_interface_inside (i) && is_inside) || + (nat44_ed_is_interface_outside (i) && !is_inside)) { return 0; } @@ -1672,7 +1790,7 @@ nat44_ed_add_interface (u32 sw_if_index, u8 is_inside) fib_index = fib_table_get_index_for_sw_if_index (FIB_PROTOCOL_IP4, sw_if_index); - update_per_vrf_sessions_vec (fib_index, 0 /*is_del*/); + update_per_vrf_sessions_pool (fib_index, 0 /*is_del*/); if (!is_inside) { @@ -1681,28 +1799,19 @@ nat44_ed_add_interface (u32 sw_if_index, u8 is_inside) outside_fib = nat44_ed_get_outside_fib (sm->outside_fibs, fib_index); if (outside_fib) { - outside_fib->refcount++; + outside_fib->ref_count++; } else { vec_add2 (sm->outside_fibs, outside_fib, 1); outside_fib->fib_index = fib_index; - outside_fib->refcount = 1; + outside_fib->ref_count = 1; } - vec_foreach (ap, sm->addresses) - { - snat_add_del_addr_to_fib (&ap->addr, 32, sw_if_index, 1); - } - pool_foreach (m, sm->static_mappings) - { - if (!(is_sm_addr_only (m->flags)) || - (m->local_addr.as_u32 == m->external_addr.as_u32)) - { - continue; - } - snat_add_del_addr_to_fib (&m->external_addr, 32, sw_if_index, 1); - } + nat44_ed_add_del_nat_addr_fib_reg_entries (sw_if_index, 1); + nat44_ed_add_del_sm_fib_reg_entries (sw_if_index, 1); + + nat44_ed_bind_if_addr_to_nat_addr (sw_if_index); } else { @@ -1718,10 +1827,8 @@ nat44_ed_del_interface (u32 sw_if_index, u8 is_inside) const char *del_feature_name, *feature_name; snat_main_t *sm = &snat_main; - nat_outside_fib_t *outside_fib; - snat_static_mapping_t *m; + nat_fib_t *outside_fib; snat_interface_t *i; - snat_address_t *ap; u32 fib_index; int rv; @@ -1738,7 +1845,7 @@ nat44_ed_del_interface (u32 sw_if_index, u8 is_inside) return VNET_API_ERROR_NO_SUCH_ENTRY; } - if (nat_interface_is_inside (i) && nat_interface_is_outside (i)) + if (nat44_ed_is_interface_inside (i) && nat44_ed_is_interface_outside (i)) { if (sm->num_workers > 1) { @@ -1798,34 +1905,22 @@ nat44_ed_del_interface (u32 sw_if_index, u8 is_inside) fib_index = fib_table_get_index_for_sw_if_index (FIB_PROTOCOL_IP4, sw_if_index); - update_per_vrf_sessions_vec (fib_index, 1 /*is_del*/); + update_per_vrf_sessions_pool (fib_index, 1 /*is_del*/); if (!is_inside) { outside_fib = nat44_ed_get_outside_fib (sm->outside_fibs, fib_index); if (outside_fib) { - outside_fib->refcount--; - if (!outside_fib->refcount) + outside_fib->ref_count--; + if (!outside_fib->ref_count) { vec_del1 (sm->outside_fibs, outside_fib - sm->outside_fibs); } } - vec_foreach (ap, sm->addresses) - { - snat_add_del_addr_to_fib (&ap->addr, 32, sw_if_index, 0); - } - - pool_foreach (m, sm->static_mappings) - { - if (!(is_sm_addr_only (m->flags)) || - (m->local_addr.as_u32 == m->external_addr.as_u32)) - { - continue; - } - snat_add_del_addr_to_fib (&m->external_addr, 32, sw_if_index, 0); - } + nat44_ed_add_del_nat_addr_fib_reg_entries (sw_if_index, 0); + nat44_ed_add_del_sm_fib_reg_entries (sw_if_index, 0); } return 0; @@ -1836,10 +1931,8 @@ nat44_ed_add_output_interface (u32 sw_if_index) { snat_main_t *sm = &snat_main; - nat_outside_fib_t *outside_fib; - snat_static_mapping_t *m; + nat_fib_t *outside_fib; snat_interface_t *i; - snat_address_t *ap; u32 fib_index; int rv; @@ -1911,34 +2004,24 @@ nat44_ed_add_output_interface (u32 sw_if_index) fib_index = fib_table_get_index_for_sw_if_index (FIB_PROTOCOL_IP4, sw_if_index); - update_per_vrf_sessions_vec (fib_index, 0 /*is_del*/); + update_per_vrf_sessions_pool (fib_index, 0 /*is_del*/); outside_fib = nat44_ed_get_outside_fib (sm->outside_fibs, fib_index); if (outside_fib) { - outside_fib->refcount++; + outside_fib->ref_count++; } else { vec_add2 (sm->outside_fibs, outside_fib, 1); outside_fib->fib_index = fib_index; - outside_fib->refcount = 1; + outside_fib->ref_count = 1; } - vec_foreach (ap, sm->addresses) - { - snat_add_del_addr_to_fib (&ap->addr, 32, sw_if_index, 1); - } + nat44_ed_add_del_nat_addr_fib_reg_entries (sw_if_index, 1); + nat44_ed_add_del_sm_fib_reg_entries (sw_if_index, 1); - pool_foreach (m, sm->static_mappings) - { - if (!((is_sm_addr_only (m->flags))) || - (m->local_addr.as_u32 == m->external_addr.as_u32)) - { - continue; - } - snat_add_del_addr_to_fib (&m->external_addr, 32, sw_if_index, 1); - } + nat44_ed_bind_if_addr_to_nat_addr (sw_if_index); return 0; } @@ -1948,10 +2031,8 @@ nat44_ed_del_output_interface (u32 sw_if_index) { snat_main_t *sm = &snat_main; - nat_outside_fib_t *outside_fib; - snat_static_mapping_t *m; + nat_fib_t *outside_fib; snat_interface_t *i; - snat_address_t *ap; u32 fib_index; int rv; @@ -2013,32 +2094,20 @@ nat44_ed_del_output_interface (u32 sw_if_index) fib_index = fib_table_get_index_for_sw_if_index (FIB_PROTOCOL_IP4, sw_if_index); - update_per_vrf_sessions_vec (fib_index, 1 /*is_del*/); + update_per_vrf_sessions_pool (fib_index, 1 /*is_del*/); outside_fib = nat44_ed_get_outside_fib (sm->outside_fibs, fib_index); if (outside_fib) { - outside_fib->refcount--; - if (!outside_fib->refcount) + outside_fib->ref_count--; + if (!outside_fib->ref_count) { vec_del1 (sm->outside_fibs, outside_fib - sm->outside_fibs); } } - vec_foreach (ap, sm->addresses) - { - snat_add_del_addr_to_fib (&ap->addr, 32, sw_if_index, 0); - } - - pool_foreach (m, sm->static_mappings) - { - if (!((is_sm_addr_only (m->flags))) || - (m->local_addr.as_u32 == m->external_addr.as_u32)) - { - continue; - } - snat_add_del_addr_to_fib (&m->external_addr, 32, sw_if_index, 0); - } + nat44_ed_add_del_nat_addr_fib_reg_entries (sw_if_index, 0); + nat44_ed_add_del_sm_fib_reg_entries (sw_if_index, 0); return 0; } @@ -2064,7 +2133,7 @@ snat_set_workers (uword * bitmap) j++; } - sm->port_per_thread = (0xffff - 1024) / _vec_len (sm->workers); + sm->port_per_thread = (65536 - ED_USER_PORT_OFFSET) / _vec_len (sm->workers); return 0; } @@ -2074,17 +2143,28 @@ nat44_ed_set_frame_queue_nelts (u32 frame_queue_nelts) { fail_if_enabled (); snat_main_t *sm = &snat_main; + + if ((sm->fq_in2out_index != ~0) || (sm->fq_out2in_index != ~0) || + (sm->fq_in2out_output_index != ~0)) + { + // frame queu nelts can be set only before first + // call to nat44_plugin_enable after that it + // doesn't make sense + nat_log_err ("Frame queue was already initialized. " + "Change is not possible"); + return 1; + } + 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) +nat44_ed_update_outside_fib_cb (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; + nat_fib_t *outside_fib; snat_interface_t *i; u8 is_add = 1; u8 match = 0; @@ -2096,20 +2176,20 @@ snat_update_outside_fib (ip4_main_t * im, uword opaque, } pool_foreach (i, sm->interfaces) - { + { if (i->sw_if_index == sw_if_index) { - if (!(nat_interface_is_outside (i))) + if (!(nat44_ed_is_interface_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))) + if (!(nat44_ed_is_interface_outside (i))) return; match = 1; } @@ -2119,54 +2199,45 @@ snat_update_outside_fib (ip4_main_t * im, uword opaque, 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; - } - } + { + if (outside_fib->fib_index == old_fib_index) + { + outside_fib->ref_count--; + if (!outside_fib->ref_count) + 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 (outside_fib->fib_index == new_fib_index) + { + outside_fib->ref_count++; + is_add = 0; + break; + } + } if (is_add) { vec_add2 (sm->outside_fibs, outside_fib, 1); - outside_fib->refcount = 1; + outside_fib->ref_count = 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 nat44_ed_update_outside_fib_cb (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 nat44_ed_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); +static void nat44_ed_add_del_static_mapping_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 () @@ -2188,8 +2259,8 @@ test_key_calc_split () 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); + init_ed_kv (&kv, l_addr.as_u32, l_port, r_addr.as_u32, 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)); @@ -2208,16 +2279,6 @@ test_key_calc_split () 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 * @@ -2237,21 +2298,6 @@ nat_ip_table_add_del (vnet_main_t * vnm, u32 table_id, u32 is_add) 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->out2in_node_index = node->index; - - node = vlib_get_node_by_name (vm, (u8 *) "nat44-ed-in2out"); - sm->in2out_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 \ { \ @@ -2295,12 +2341,8 @@ nat_init (vlib_main_t * vm) 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; @@ -2309,15 +2351,13 @@ nat_init (vlib_main_t * vm) 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"); - sm->max_cfg_sessions_gauge = stat_segment_new_entry ( - (u8 *) "/nat44-ed/max-cfg-sessions", STAT_DIR_TYPE_SCALAR_INDEX); + sm->max_cfg_sessions_gauge = + vlib_stats_add_gauge ("/nat44-ed/max-cfg-sessions"); #define _(x) \ nat_init_simple_counter (sm->counters.fastpath.in2out.x, #x, \ @@ -2344,7 +2384,7 @@ nat_init (vlib_main_t * vm) } } num_threads = tm->n_vlib_mains - 1; - sm->port_per_thread = 0xffff - 1024; + sm->port_per_thread = 65536 - ED_USER_PORT_OFFSET; vec_validate (sm->per_thread_data, num_threads); /* Use all available workers by default */ @@ -2361,13 +2401,13 @@ nat_init (vlib_main_t * vm) } /* callbacks to call when interface address changes. */ - cbi.function = snat_ip4_add_del_interface_address_cb; + cbi.function = nat44_ed_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; + cbi.function = nat44_ed_add_del_static_mapping_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; + cbt.function = nat44_ed_update_outside_fib_cb; vec_add1 (sm->ip4_main->table_bind_callbacks, cbt); sm->fib_src_low = @@ -2392,26 +2432,15 @@ nat44_plugin_enable (nat44_config_t c) fail_if_enabled (); - if (c.static_mapping_only && !c.connection_tracking) - { - nat_log_err ("unsupported combination of configuration"); - return 1; - } - - 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; - stat_segment_set_state_counter (sm->max_cfg_sessions_gauge, - sm->max_translations_per_thread); + vlib_stats_set_gauge (sm->max_cfg_sessions_gauge, + sm->max_translations_per_thread); sm->translation_buckets = nat_calc_bihash_buckets (c.sessions); vec_add1 (sm->max_translations_per_fib, sm->max_translations_per_thread); @@ -2425,7 +2454,7 @@ nat44_plugin_enable (nat44_config_t c) sm->outside_fib_index = fib_table_find_or_create_and_lock ( FIB_PROTOCOL_IP4, c.outside_vrf, sm->fib_src_hi); - nat44_ed_db_init (sm->max_translations_per_thread, sm->translation_buckets); + nat44_ed_db_init (); nat_affinity_enable (); @@ -2440,20 +2469,26 @@ nat44_plugin_enable (nat44_config_t c) if (sm->num_workers > 1) { + vlib_main_t *vm = vlib_get_main (); + vlib_node_t *node; + if (sm->fq_in2out_index == ~0) { - sm->fq_in2out_index = vlib_frame_queue_main_init ( - sm->in2out_node_index, sm->frame_queue_nelts); + node = vlib_get_node_by_name (vm, (u8 *) "nat44-ed-in2out"); + sm->fq_in2out_index = + vlib_frame_queue_main_init (node->index, sm->frame_queue_nelts); } if (sm->fq_out2in_index == ~0) { - sm->fq_out2in_index = vlib_frame_queue_main_init ( - sm->out2in_node_index, sm->frame_queue_nelts); + node = vlib_get_node_by_name (vm, (u8 *) "nat44-ed-out2in"); + sm->fq_out2in_index = + vlib_frame_queue_main_init (node->index, sm->frame_queue_nelts); } if (sm->fq_in2out_output_index == ~0) { - sm->fq_in2out_output_index = vlib_frame_queue_main_init ( - sm->in2out_output_node_index, sm->frame_queue_nelts); + node = vlib_get_node_by_name (vm, (u8 *) "nat44-ed-in2out-output"); + sm->fq_in2out_output_index = + vlib_frame_queue_main_init (node->index, sm->frame_queue_nelts); } } @@ -2463,86 +2498,213 @@ nat44_plugin_enable (nat44_config_t c) return 0; } -void -nat44_addresses_free (snat_address_t ** addresses) +int +nat44_ed_del_addresses () { - snat_address_t *ap; - vec_foreach (ap, *addresses) + snat_main_t *sm = &snat_main; + snat_address_t *a, *vec; + int error = 0; + + vec = vec_dup (sm->addresses); + vec_foreach (a, vec) { - #define _(N, i, n, s) \ - vec_free (ap->busy_##n##_ports_per_thread); - foreach_nat_protocol - #undef _ + error = nat44_ed_del_address (a->addr, 0); + if (error) + { + nat_log_err ("error occurred while removing adderess"); + } } - vec_free (*addresses); - *addresses = 0; + vec_free (vec); + vec_free (sm->addresses); + sm->addresses = 0; + + vec = vec_dup (sm->twice_nat_addresses); + vec_foreach (a, vec) + { + error = nat44_ed_del_address (a->addr, 1); + if (error) + { + nat_log_err ("error occurred while removing adderess"); + } + } + vec_free (vec); + vec_free (sm->twice_nat_addresses); + sm->twice_nat_addresses = 0; + + vec_free (sm->addr_to_resolve); + sm->addr_to_resolve = 0; + + return error; } int -nat44_plugin_disable () +nat44_ed_del_interfaces () { snat_main_t *sm = &snat_main; snat_interface_t *i, *pool; int error = 0; - fail_if_disabled (); - pool = pool_dup (sm->interfaces); pool_foreach (i, pool) { - if (nat_interface_is_inside (i)) + if (nat44_ed_is_interface_inside (i)) { error = nat44_ed_del_interface (i->sw_if_index, 1); } - if (nat_interface_is_outside (i)) + if (nat44_ed_is_interface_outside (i)) { error = nat44_ed_del_interface (i->sw_if_index, 0); } + if (error) - { - nat_log_err ("error occurred while removing interface %u", - i->sw_if_index); - } + { + nat_log_err ("error occurred while removing interface"); + } } - pool_free (sm->interfaces); pool_free (pool); + pool_free (sm->interfaces); sm->interfaces = 0; + return error; +} + +int +nat44_ed_del_output_interfaces () +{ + snat_main_t *sm = &snat_main; + snat_interface_t *i, *pool; + int error = 0; pool = pool_dup (sm->output_feature_interfaces); pool_foreach (i, pool) { error = nat44_ed_del_output_interface (i->sw_if_index); if (error) - { - nat_log_err ("error occurred while removing interface %u", - i->sw_if_index); - } + { + nat_log_err ("error occurred while removing output interface"); + } } - pool_free (sm->output_feature_interfaces); pool_free (pool); + pool_free (sm->output_feature_interfaces); sm->output_feature_interfaces = 0; + return error; +} + +static clib_error_t * +nat44_ed_sw_interface_add_del (vnet_main_t *vnm, u32 sw_if_index, u32 is_add) +{ + snat_main_t *sm = &snat_main; + snat_interface_t *i; + int error = 0; + + if (is_add) + return 0; + + if (!sm->enabled) + return 0; + + i = nat44_ed_get_interface (sm->interfaces, sw_if_index); + if (i) + { + bool is_inside = nat44_ed_is_interface_inside (i); + bool is_outside = nat44_ed_is_interface_outside (i); + + if (is_inside) + { + error |= nat44_ed_del_interface (sw_if_index, 1); + } + if (is_outside) + { + error |= nat44_ed_del_interface (sw_if_index, 0); + } + + if (error) + { + nat_log_err ("error occurred while removing interface"); + } + } + + i = nat44_ed_get_interface (sm->output_feature_interfaces, sw_if_index); + if (i) + { + error = nat44_ed_del_output_interface (sw_if_index); + if (error) + { + nat_log_err ("error occurred while removing output interface"); + } + } + + return 0; +} + +VNET_SW_INTERFACE_ADD_DEL_FUNCTION (nat44_ed_sw_interface_add_del); + +int +nat44_ed_del_static_mappings () +{ + snat_main_t *sm = &snat_main; + snat_static_mapping_t *m, *pool; + int error = 0; + + pool = pool_dup (sm->static_mappings); + pool_foreach (m, pool) + { + error = nat44_ed_del_static_mapping_internal ( + m->local_addr, m->external_addr, m->local_port, m->external_port, + m->proto, m->vrf_id, m->flags); + if (error) + { + nat_log_err ("error occurred while removing mapping"); + } + } + pool_free (pool); + pool_free (sm->static_mappings); + sm->static_mappings = 0; + + vec_free (sm->sm_to_resolve); + sm->sm_to_resolve = 0; + + return error; +} + +int +nat44_plugin_disable () +{ + snat_main_t *sm = &snat_main; + int rc, error = 0; + + fail_if_disabled (); + + rc = nat44_ed_del_static_mappings (); + if (rc) + error = VNET_API_ERROR_BUG; + + rc = nat44_ed_del_addresses (); + if (rc) + error = VNET_API_ERROR_BUG; + + rc = nat44_ed_del_interfaces (); + if (rc) + error = VNET_API_ERROR_BUG; + + rc = nat44_ed_del_output_interfaces (); + if (rc) + error = VNET_API_ERROR_BUG; + + nat44_ed_del_vrf_tables (); vec_free (sm->max_translations_per_fib); + sm->max_translations_per_fib = 0; 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); + clib_memset (&sm->rconfig, 0, sizeof (sm->rconfig)); - sm->to_resolve = 0; - sm->auto_add_sw_if_indices = 0; - sm->auto_add_sw_if_indices_twice_nat = 0; + nat_affinity_disable (); sm->forwarding_enabled = 0; - sm->enabled = 0; - clib_memset (&sm->rconfig, 0, sizeof (sm->rconfig)); - return 0; + return error; } void @@ -2556,14 +2718,16 @@ nat44_ed_forwarding_enable_disable (u8 is_enable) sm->forwarding_enabled = is_enable != 0; - if (is_enable) - return; + if (!sm->enabled || is_enable) + { + return; + } vec_foreach (tsm, sm->per_thread_data) { pool_foreach (s, tsm->sessions) { - if (is_fwd_bypass_session (s)) + if (na44_ed_is_fwd_bypass_session (s)) { vec_add1 (ses_to_be_removed, s - tsm->sessions); } @@ -2571,7 +2735,7 @@ nat44_ed_forwarding_enable_disable (u8 is_enable) 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_ed_free_session_data (sm, s, tsm - sm->per_thread_data, 0); nat_ed_session_delete (sm, s, tsm - sm->per_thread_data, 1); } @@ -2579,125 +2743,91 @@ nat44_ed_forwarding_enable_disable (u8 is_enable) } } -void -snat_free_outside_address_and_port (snat_address_t *addresses, - u32 thread_index, ip4_address_t *addr, - u16 port, nat_protocol_t protocol) +static_always_inline snat_static_mapping_t * +nat44_ed_sm_match (snat_main_t *sm, ip4_address_t match_addr, u16 match_port, + u32 match_fib_index, ip_protocol_t match_protocol, + int by_external) { - 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++) + snat_static_mapping_t *m; + if (!by_external) { - if (addresses[address_index].addr.as_u32 == addr->as_u32) - break; - } - - ASSERT (address_index < vec_len (addresses)); - - a = addresses + address_index; + m = nat44_ed_sm_i2o_lookup (sm, match_addr, match_port, match_fib_index, + match_protocol); + if (m) + return m; - 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; - } -} + // try address only mapping + m = nat44_ed_sm_i2o_lookup (sm, match_addr, 0, match_fib_index, 0); + if (m) + return m; -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; + // default static mapping fib index (based on configuration) + if (sm->inside_fib_index != match_fib_index) + { + m = nat44_ed_sm_i2o_lookup (sm, match_addr, match_port, + sm->inside_fib_index, match_protocol); + if (m) + return m; + + // try address only mapping + m = nat44_ed_sm_i2o_lookup (sm, match_addr, 0, sm->inside_fib_index, + 0); + if (m) + return m; + } + // TODO: this specific use case may be deprecated (needs testing) + if (sm->outside_fib_index != match_fib_index) + { + m = nat44_ed_sm_i2o_lookup (sm, match_addr, match_port, + sm->outside_fib_index, match_protocol); + if (m) + return m; + + // try address only mapping + m = nat44_ed_sm_i2o_lookup (sm, match_addr, 0, sm->outside_fib_index, + 0); + if (m) + return m; } } + else + { + m = + nat44_ed_sm_o2i_lookup (sm, match_addr, match_port, 0, match_protocol); + if (m) + return m; - return VNET_API_ERROR_NO_SUCH_ENTRY; + // try address only mapping + m = nat44_ed_sm_o2i_lookup (sm, match_addr, 0, 0, 0); + if (m) + return m; + } + return 0; } int -snat_static_mapping_match (vlib_main_t *vm, snat_main_t *sm, - ip4_address_t match_addr, u16 match_port, - u32 match_fib_index, nat_protocol_t match_protocol, +snat_static_mapping_match (vlib_main_t *vm, ip4_address_t match_addr, + u16 match_port, u32 match_fib_index, + ip_protocol_t match_protocol, ip4_address_t *mapping_addr, u16 *mapping_port, - u32 *mapping_fib_index, u8 by_external, + u32 *mapping_fib_index, int 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_main_t *sm = &snat_main; 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 + m = nat44_ed_sm_match (sm, match_addr, match_port, match_fib_index, + match_protocol, by_external); + if (!m) { - 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; - } + return 1; } - m = pool_elt_at_index (sm->static_mappings, value.value); - if (by_external) { if (is_sm_lb (m->flags)) @@ -2829,7 +2959,7 @@ nat44_ed_get_in2out_worker_index (vlib_buffer_t *b, ip4_header_t *ip, if (PREDICT_FALSE (is_output)) { fib_index = sm->outside_fib_index; - nat_outside_fib_t *outside_fib; + nat_fib_t *outside_fib; fib_node_index_t fei = FIB_NODE_INDEX_INVALID; fib_prefix_t pfx = { .fp_proto = FIB_PROTOCOL_IP4, @@ -2864,9 +2994,33 @@ nat44_ed_get_in2out_worker_index (vlib_buffer_t *b, ip4_header_t *ip, } } - init_ed_k (&kv16, ip->src_address, vnet_buffer (b)->ip.reass.l4_src_port, - ip->dst_address, vnet_buffer (b)->ip.reass.l4_dst_port, - fib_index, ip->protocol); + if (PREDICT_FALSE (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)) + { + init_ed_k (&kv16, lookup_saddr.as_u32, lookup_sport, + lookup_daddr.as_u32, lookup_dport, rx_fib_index, + lookup_protocol); + if (!clib_bihash_search_16_8 (&sm->flow_hash, &kv16, &value16)) + { + next_worker_index = ed_value_get_thread_index (&value16); + vnet_buffer2 (b)->nat.cached_session_index = + ed_value_get_session_index (&value16); + goto out; + } + } + } + + init_ed_k (&kv16, ip->src_address.as_u32, + vnet_buffer (b)->ip.reass.l4_src_port, ip->dst_address.as_u32, + vnet_buffer (b)->ip.reass.l4_dst_port, fib_index, + ip->protocol); if (!clib_bihash_search_16_8 (&sm->flow_hash, &kv16, &value16)) { @@ -2877,9 +3031,10 @@ nat44_ed_get_in2out_worker_index (vlib_buffer_t *b, ip4_header_t *ip, } // dst NAT - init_ed_k (&kv16, ip->dst_address, vnet_buffer (b)->ip.reass.l4_dst_port, - ip->src_address, vnet_buffer (b)->ip.reass.l4_src_port, - rx_fib_index, ip->protocol); + init_ed_k (&kv16, ip->dst_address.as_u32, + vnet_buffer (b)->ip.reass.l4_dst_port, ip->src_address.as_u32, + vnet_buffer (b)->ip.reass.l4_src_port, rx_fib_index, + ip->protocol); if (!clib_bihash_search_16_8 (&sm->flow_hash, &kv16, &value16)) { next_worker_index = ed_value_get_thread_index (&value16); @@ -2890,7 +3045,9 @@ nat44_ed_get_in2out_worker_index (vlib_buffer_t *b, ip4_header_t *ip, } hash = ip->src_address.as_u32 + (ip->src_address.as_u32 >> 8) + - (ip->src_address.as_u32 >> 16) + (ip->src_address.as_u32 >> 24); + (ip->src_address.as_u32 >> 16) + (ip->src_address.as_u32 >> 24) + + rx_fib_index + (rx_fib_index >> 8) + (rx_fib_index >> 16) + + (rx_fib_index >> 24); if (PREDICT_TRUE (is_pow2 (_vec_len (sm->workers)))) next_worker_index += sm->workers[hash & (_vec_len (sm->workers) - 1)]; @@ -2921,17 +3078,16 @@ nat44_ed_get_out2in_worker_index (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; - u32 proto, next_worker_index = 0; + u8 proto, next_worker_index = 0; u16 port; snat_static_mapping_t *m; u32 hash; - proto = ip_proto_to_nat_proto (ip->protocol); + proto = ip->protocol; - if (PREDICT_FALSE (proto == NAT_PROTOCOL_ICMP)) + if (PREDICT_FALSE (IP_PROTOCOL_ICMP == proto)) { ip4_address_t lookup_saddr, lookup_daddr; u16 lookup_sport, lookup_dport; @@ -2940,8 +3096,9 @@ nat44_ed_get_out2in_worker_index (vlib_buffer_t *b, ip4_header_t *ip, 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); + init_ed_k (&kv16, lookup_saddr.as_u32, lookup_sport, + lookup_daddr.as_u32, lookup_dport, rx_fib_index, + lookup_protocol); if (PREDICT_TRUE ( !clib_bihash_search_16_8 (&sm->flow_hash, &kv16, &value16))) { @@ -2955,9 +3112,10 @@ nat44_ed_get_out2in_worker_index (vlib_buffer_t *b, ip4_header_t *ip, } } - init_ed_k (&kv16, ip->src_address, vnet_buffer (b)->ip.reass.l4_src_port, - ip->dst_address, vnet_buffer (b)->ip.reass.l4_dst_port, - rx_fib_index, ip->protocol); + init_ed_k (&kv16, ip->src_address.as_u32, + vnet_buffer (b)->ip.reass.l4_src_port, ip->dst_address.as_u32, + vnet_buffer (b)->ip.reass.l4_dst_port, rx_fib_index, + ip->protocol); if (PREDICT_TRUE ( !clib_bihash_search_16_8 (&sm->flow_hash, &kv16, &value16))) @@ -2975,18 +3133,18 @@ nat44_ed_get_out2in_worker_index (vlib_buffer_t *b, ip4_header_t *ip, /* 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 = nat44_ed_sm_o2i_lookup (sm, ip->dst_address, 0, 0, proto); + if (m) { - m = pool_elt_at_index (sm->static_mappings, value.value); - next_worker_index = m->workers[0]; - goto done; + { + next_worker_index = m->workers[0]; + goto done; + } } } /* unknown protocol */ - if (PREDICT_FALSE (proto == NAT_PROTOCOL_OTHER)) + if (PREDICT_FALSE (nat44_ed_is_unk_proto (proto))) { /* use current thread */ next_worker_index = vlib_get_thread_index (); @@ -2999,7 +3157,7 @@ nat44_ed_get_out2in_worker_index (vlib_buffer_t *b, ip4_header_t *ip, { udp_header_t *udp = ip4_next_header (ip); icmp46_header_t *icmp = (icmp46_header_t *) udp; - icmp_echo_header_t *echo = (icmp_echo_header_t *) (icmp + 1); + nat_icmp_echo_header_t *echo = (nat_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; @@ -3007,18 +3165,19 @@ nat44_ed_get_out2in_worker_index (vlib_buffer_t *b, ip4_header_t *ip, { /* 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); + proto = inner_ip->protocol; void *l4_header = ip4_next_header (inner_ip); switch (proto) { - case NAT_PROTOCOL_ICMP: + case IP_PROTOCOL_ICMP: icmp = (icmp46_header_t *) l4_header; - echo = (icmp_echo_header_t *) (icmp + 1); + echo = (nat_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; + case IP_PROTOCOL_UDP: + /* breakthrough */ + case IP_PROTOCOL_TCP: + port = ((nat_tcp_udp_header_t *) l4_header)->src_port; break; default: next_worker_index = vlib_get_thread_index (); @@ -3030,11 +3189,9 @@ nat44_ed_get_out2in_worker_index (vlib_buffer_t *b, ip4_header_t *ip, /* 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 = nat44_ed_sm_o2i_lookup (sm, ip->dst_address, port, 0, proto); + if (m) { - m = pool_elt_at_index (sm->static_mappings, value.value); if (!is_sm_lb (m->flags)) { next_worker_index = m->workers[0]; @@ -3054,9 +3211,7 @@ nat44_ed_get_out2in_worker_index (vlib_buffer_t *b, ip4_header_t *ip, } /* 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]; + next_worker_index = get_thread_idx_by_port (clib_net_to_host_u16 (port)); done: nat_elog_debug_handoff (sm, "HANDOFF OUT2IN", next_worker_index, @@ -3085,16 +3240,12 @@ 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; - } + if (~0 == fib_index) + return -1; + vec_validate_init_empty (sm->max_translations_per_fib, fib_index, + sm->max_translations_per_thread); sm->max_translations_per_fib[fib_index] = session_limit; return 0; } @@ -3108,8 +3259,8 @@ nat44_update_session_limit (u32 session_limit, u32 vrf_id) return 1; sm->max_translations_per_thread = nat44_get_max_session_limit (); - stat_segment_set_state_counter (sm->max_cfg_sessions_gauge, - sm->max_translations_per_thread); + vlib_stats_set_gauge (sm->max_cfg_sessions_gauge, + sm->max_translations_per_thread); sm->translation_buckets = nat_calc_bihash_buckets (sm->max_translations_per_thread); @@ -3119,11 +3270,11 @@ nat44_update_session_limit (u32 session_limit, u32 vrf_id) } static void -nat44_ed_worker_db_init (snat_main_per_thread_data_t *tsm, u32 translations, - u32 translation_buckets) +nat44_ed_worker_db_init (snat_main_per_thread_data_t *tsm, u32 translations) { dlist_elt_t *head; + pool_alloc (tsm->per_vrf_sessions_pool, translations); pool_alloc (tsm->sessions, translations); pool_alloc (tsm->lru_pool, translations); @@ -3149,7 +3300,7 @@ nat44_ed_worker_db_init (snat_main_per_thread_data_t *tsm, u32 translations, } static void -reinit_ed_flow_hash () +nat44_ed_flow_hash_init () { snat_main_t *sm = &snat_main; // we expect 2 flows per session, so multiply translation_buckets by 2 @@ -3160,34 +3311,16 @@ reinit_ed_flow_hash () } static void -nat44_ed_db_init (u32 translations, u32 translation_buckets) +nat44_ed_db_init () { 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); + nat44_ed_flow_hash_init (); - if (sm->pat) + vec_foreach (tsm, sm->per_thread_data) { - vec_foreach (tsm, sm->per_thread_data) - { - nat44_ed_worker_db_init (tsm, sm->max_translations_per_thread, - sm->translation_buckets); - } + nat44_ed_worker_db_init (tsm, sm->max_translations_per_thread); } } @@ -3196,283 +3329,299 @@ 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); + pool_free (tsm->per_vrf_sessions_pool); } static void -nat44_ed_db_free () +nat44_ed_flow_hash_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); +} + +static void +nat44_ed_db_free () +{ + snat_main_t *sm = &snat_main; + snat_main_per_thread_data_t *tsm; - if (sm->pat) + vec_foreach (tsm, sm->per_thread_data) { - vec_foreach (tsm, sm->per_thread_data) - { - nat44_ed_worker_db_free (tsm); - } + nat44_ed_worker_db_free (tsm); } + + nat44_ed_flow_hash_free (); } 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); - } - } + nat44_ed_db_free (); + nat44_ed_db_init (); 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) +nat44_ed_add_del_static_mapping_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_static_mapping_resolve_t *rp; 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; - ip4_address_t l_addr; - int i, rv; + int rv = 0; 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; } - 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) + vec_foreach (rp, sm->sm_to_resolve) { - /* Don't trip over lease renewal, static config */ - if (m) - return; - } - else - { - if (!m) - return; + if (sw_if_index == rp->sw_if_index) + { + if (is_delete) + { + if (rp->is_resolved) + { + rv = nat44_ed_del_static_mapping_internal ( + rp->l_addr, address[0], rp->l_port, rp->e_port, rp->proto, + rp->vrf_id, rp->flags); + if (rv) + { + nat_log_err ("ed del static mapping failed"); + } + else + { + rp->is_resolved = 0; + } + } + } + else + { + if (!rp->is_resolved) + { + rv = nat44_ed_add_static_mapping_internal ( + rp->l_addr, address[0], rp->l_port, rp->e_port, rp->proto, + rp->vrf_id, ~0, rp->flags, rp->pool_addr, rp->tag); + if (rv) + { + nat_log_err ("ed add static mapping failed"); + } + else + { + rp->is_resolved = 1; + } + } + } + } } +} - /* 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; +static int +nat44_ed_get_addr_resolve_record (u32 sw_if_index, u8 twice_nat, int *out) +{ + snat_main_t *sm = &snat_main; + snat_address_resolve_t *rp; + int i; - if (is_delete) + for (i = 0; i < vec_len (sm->addr_to_resolve); i++) { - rv = nat44_ed_del_static_mapping (l_addr, address[0], rp->l_port, - rp->e_port, rp->proto, rp->vrf_id, ~0, - rp->flags); - } - else - { - rv = nat44_ed_add_static_mapping (l_addr, address[0], rp->l_port, - rp->e_port, rp->proto, rp->vrf_id, ~0, - rp->flags, rp->pool_addr, rp->tag); + rp = sm->addr_to_resolve + i; + + if ((rp->sw_if_index == sw_if_index) && (rp->is_twice_nat == twice_nat)) + { + if (out) + { + *out = i; + } + return 0; + } } - if (rv) + return 1; +} +static int +nat44_ed_del_addr_resolve_record (u32 sw_if_index, u8 twice_nat) +{ + snat_main_t *sm = &snat_main; + int i; + if (!nat44_ed_get_addr_resolve_record (sw_if_index, twice_nat, &i)) { - nat_elog_notice_X1 (sm, "add_static_mapping returned %d", "i4", rv); + vec_del1 (sm->addr_to_resolve, i); + return 0; } + return 1; } static void -snat_ip4_add_del_interface_address_cb (ip4_main_t * im, - uword opaque, - u32 sw_if_index, - ip4_address_t * address, +nat44_ed_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; + snat_address_resolve_t *arp; + snat_address_t *ap; u8 twice_nat = 0; - snat_address_t *addresses = sm->addresses; + int i, rv; 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; + return; } - for (i = 0; i < vec_len (sm->auto_add_sw_if_indices_twice_nat); i++) + if (nat44_ed_get_addr_resolve_record (sw_if_index, 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; + if (nat44_ed_get_addr_resolve_record (sw_if_index, twice_nat, &i)) + { + u32 fib_index = + ip4_fib_table_get_index_for_sw_if_index (sw_if_index); + vec_foreach (ap, sm->addresses) + { + if ((fib_index == ap->fib_index) && + (address->as_u32 == ap->addr.as_u32)) + { + if (!is_delete) + { + ap->addr_len = address_length; + ap->sw_if_index = sw_if_index; + ap->net.as_u32 = + ap->addr.as_u32 & ip4_main.fib_masks[ap->addr_len]; + + nat_log_debug ( + "pool addr %U binds to -> sw_if_idx: %u net: %U/%u", + format_ip4_address, &ap->addr, ap->sw_if_index, + format_ip4_address, &ap->net, ap->addr_len); + } + else + { + ap->addr_len = ~0; + } + break; + } + } + return; + } } - return; + arp = sm->addr_to_resolve + i; -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) + if (arp->is_resolved) + { 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++) + rv = nat44_ed_add_address (address, ~0, arp->is_twice_nat); + if (0 == rv) { - rp = sm->to_resolve + j; - if (rp->addr_only) - continue; - /* On this interface? */ - if (rp->sw_if_index == sw_if_index) - { - - // TODO: remove if not needed (handled by function) - /* 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_ed_add_static_mapping ( - l_addr, address[0], rp->l_port, rp->e_port, rp->proto, - rp->vrf_id, ~0, rp->flags, rp->pool_addr, rp->tag); - if (rv) - { - nat_elog_notice_X1 (sm, "add_static_mapping returned %d", - "i4", rv); - } - } + arp->is_resolved = 1; } - return; } else { - (void) snat_del_address (sm, address[0], 1, twice_nat); - return; + if (!arp->is_resolved) + { + return; + } + + rv = nat44_ed_del_address (address[0], arp->is_twice_nat); + if (0 == rv) + { + arp->is_resolved = 0; + } } } int -snat_add_interface_address (snat_main_t * sm, u32 sw_if_index, int is_del, - u8 twice_nat) +nat44_ed_add_interface_address (u32 sw_if_index, u8 twice_nat) { + snat_main_t *sm = &snat_main; 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; + snat_address_resolve_t *ap; + int rv; - first_int_addr = ip4_interface_first_address (ip4_main, sw_if_index, 0 /* just want the address */ - ); + if (!sm->enabled) + { + nat_log_err ("nat44 is disabled"); + return VNET_API_ERROR_UNSUPPORTED; + } - for (i = 0; i < vec_len (auto_add_sw_if_indices); i++) + if (!nat44_ed_get_addr_resolve_record (sw_if_index, twice_nat, 0)) { - 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 VNET_API_ERROR_VALUE_EXIST; + } - return 0; + vec_add2 (sm->addr_to_resolve, ap, 1); + ap->sw_if_index = sw_if_index; + ap->is_twice_nat = twice_nat; + ap->is_resolved = 0; + + first_int_addr = ip4_interface_first_address (ip4_main, sw_if_index, 0); + if (first_int_addr) + { + rv = nat44_ed_add_address (first_int_addr, ~0, twice_nat); + if (0 != rv) + { + nat44_ed_del_addr_resolve_record (sw_if_index, twice_nat); + return rv; } + ap->is_resolved = 1; } - if (is_del) - return VNET_API_ERROR_NO_SUCH_ENTRY; + return 0; +} - /* 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); +int +nat44_ed_del_interface_address (u32 sw_if_index, u8 twice_nat) +{ + snat_main_t *sm = &snat_main; + ip4_main_t *ip4_main = sm->ip4_main; + ip4_address_t *first_int_addr; - /* If the address is already bound - or static - add it now */ + if (!sm->enabled) + { + nat_log_err ("nat44 is disabled"); + return VNET_API_ERROR_UNSUPPORTED; + } + + if (nat44_ed_del_addr_resolve_record (sw_if_index, twice_nat)) + { + return VNET_API_ERROR_NO_SUCH_ENTRY; + } + + first_int_addr = ip4_interface_first_address (ip4_main, sw_if_index, 0); if (first_int_addr) - (void) snat_add_address (sm, first_int_addr, ~0, twice_nat); + { + return nat44_ed_del_address (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, +nat44_ed_del_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); + u32 fib_index; snat_session_t *s; snat_main_per_thread_data_t *tsm; + if (!sm->enabled) + { + return VNET_API_ERROR_UNSUPPORTED; + } + + fib_index = fib_table_find (FIB_PROTOCOL_IP4, vrf_id); ip.dst_address.as_u32 = ip.src_address.as_u32 = addr->as_u32; if (sm->num_workers > 1) tsm = vec_elt_at_index ( @@ -3481,7 +3630,8 @@ nat44_del_ed_session (snat_main_t * sm, ip4_address_t * addr, u16 port, 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); + init_ed_k (&kv, addr->as_u32, port, eh_addr->as_u32, eh_port, fib_index, + proto); if (clib_bihash_search_16_8 (&sm->flow_hash, &kv, &value)) { return VNET_API_ERROR_NO_SUCH_ENTRY; @@ -3490,7 +3640,7 @@ nat44_del_ed_session (snat_main_t * sm, ip4_address_t * addr, u16 port, 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); + nat44_ed_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; } @@ -3596,13 +3746,13 @@ nat_6t_flow_icmp_translate (vlib_main_t *vm, snat_main_t *sm, vlib_buffer_t *b, 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, + nat_6t_flow_t *f, ip_protocol_t proto, int is_icmp_inner_ip4, int skip_saddr_rewrite) { 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) && + if ((IP_PROTOCOL_TCP == proto || IP_PROTOCOL_UDP == proto) && !vnet_buffer (b)->ip.reass.is_non_first_fragment) { if (!is_icmp_inner_ip4) @@ -3620,7 +3770,7 @@ nat_6t_flow_ip4_translate (snat_main_t *sm, vlib_buffer_t *b, ip4_header_t *ip, udp->dst_port = f->rewrite.sport; } - if (NAT_PROTOCOL_TCP == proto) + if (IP_PROTOCOL_TCP == proto) { ip_csum_t tcp_sum = tcp->checksum; tcp_sum = ip_csum_sub_even (tcp_sum, f->l3_csum_delta); @@ -3628,7 +3778,7 @@ nat_6t_flow_ip4_translate (snat_main_t *sm, vlib_buffer_t *b, ip4_header_t *ip, mss_clamping (sm->mss_clamping, tcp, &tcp_sum); tcp->checksum = ip_csum_fold (tcp_sum); } - else if (proto == NAT_PROTOCOL_UDP && udp->checksum) + else if (IP_PROTOCOL_UDP == proto && udp->checksum) { ip_csum_t udp_sum = udp->checksum; udp_sum = ip_csum_sub_even (udp_sum, f->l3_csum_delta); @@ -3685,7 +3835,7 @@ nat_6t_flow_icmp_translate (vlib_main_t *vm, snat_main_t *sm, vlib_buffer_t *b, 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); + nat_icmp_echo_header_t *echo = (nat_icmp_echo_header_t *) (icmp + 1); if ((!vnet_buffer (b)->ip.reass.is_non_first_fragment)) { @@ -3694,15 +3844,6 @@ nat_6t_flow_icmp_translate (vlib_main_t *vm, snat_main_t *sm, vlib_buffer_t *b, return NAT_ED_TRNSL_ERR_PACKET_TRUNCATED; } - ssize_t icmp_offset = (u8 *) icmp - (u8 *) vlib_buffer_get_current (b); - ip_csum_t sum = - ip_incremental_checksum (0, icmp, b->current_length - icmp_offset); - sum = (u16) ~ip_csum_fold (sum); - if (sum != 0) - { - return NAT_ED_TRNSL_ERR_INVALID_CSUM; - } - if (!icmp_type_is_error_message (icmp->type)) { if ((f->ops & NAT_FLOW_OP_ICMP_ID_REWRITE) && @@ -3710,7 +3851,7 @@ nat_6t_flow_icmp_translate (vlib_main_t *vm, snat_main_t *sm, vlib_buffer_t *b, { ip_csum_t sum = icmp->checksum; sum = ip_csum_update (sum, echo->identifier, f->rewrite.icmp_id, - icmp_echo_header_t, + nat_icmp_echo_header_t, identifier /* changed member */); echo->identifier = f->rewrite.icmp_id; icmp->checksum = ip_csum_fold (sum); @@ -3718,6 +3859,15 @@ nat_6t_flow_icmp_translate (vlib_main_t *vm, snat_main_t *sm, vlib_buffer_t *b, } else { + ip_csum_t sum = ip_incremental_checksum ( + 0, icmp, + clib_net_to_host_u16 (ip->length) - ip4_header_bytes (ip)); + sum = (u16) ~ip_csum_fold (sum); + if (sum != 0) + { + return NAT_ED_TRNSL_ERR_INVALID_CSUM; + } + // errors are not fragmented ip4_header_t *inner_ip = (ip4_header_t *) (echo + 1); @@ -3726,8 +3876,7 @@ nat_6t_flow_icmp_translate (vlib_main_t *vm, snat_main_t *sm, vlib_buffer_t *b, return NAT_ED_TRNSL_ERR_INNER_IP_CORRUPT; } - nat_protocol_t inner_proto = - ip_proto_to_nat_proto (inner_ip->protocol); + ip_protocol_t inner_proto = inner_ip->protocol; ip_csum_t old_icmp_sum = icmp->checksum; ip_csum_t old_inner_ip_sum = inner_ip->checksum; @@ -3739,7 +3888,7 @@ nat_6t_flow_icmp_translate (vlib_main_t *vm, snat_main_t *sm, vlib_buffer_t *b, switch (inner_proto) { - case NAT_PROTOCOL_UDP: + case IP_PROTOCOL_UDP: udp = (udp_header_t *) (inner_ip + 1); if (!it_fits (vm, b, udp, sizeof (*udp))) { @@ -3760,7 +3909,7 @@ nat_6t_flow_icmp_translate (vlib_main_t *vm, snat_main_t *sm, vlib_buffer_t *b, new_icmp_sum = ip_csum_fold (new_icmp_sum); icmp->checksum = new_icmp_sum; break; - case NAT_PROTOCOL_TCP: + case IP_PROTOCOL_TCP: tcp = (tcp_header_t *) (inner_ip + 1); if (!it_fits (vm, b, tcp, sizeof (*tcp))) { @@ -3781,7 +3930,10 @@ nat_6t_flow_icmp_translate (vlib_main_t *vm, snat_main_t *sm, vlib_buffer_t *b, new_icmp_sum = ip_csum_fold (new_icmp_sum); icmp->checksum = new_icmp_sum; break; - case NAT_PROTOCOL_ICMP: + case IP_PROTOCOL_ICMP: + nat_6t_flow_ip4_translate (sm, b, inner_ip, f, inner_proto, + 1 /* is_icmp_inner_ip4 */, + 0 /* skip_saddr_rewrite */); if (f->ops & NAT_FLOW_OP_ICMP_ID_REWRITE) { icmp46_header_t *inner_icmp = ip4_next_header (inner_ip); @@ -3789,19 +3941,21 @@ nat_6t_flow_icmp_translate (vlib_main_t *vm, snat_main_t *sm, vlib_buffer_t *b, { return NAT_ED_TRNSL_ERR_PACKET_TRUNCATED; } - icmp_echo_header_t *inner_echo = - (icmp_echo_header_t *) (inner_icmp + 1); + nat_icmp_echo_header_t *inner_echo = + (nat_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 */); + sum = ip_csum_update (sum, inner_echo->identifier, + f->rewrite.icmp_id, + nat_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 */); + nat_icmp_echo_header_t, + identifier /* changed member */); inner_icmp->checksum = ip_csum_fold (inner_sum); inner_echo->identifier = f->rewrite.icmp_id; } @@ -3820,7 +3974,7 @@ nat_6t_flow_icmp_translate (vlib_main_t *vm, snat_main_t *sm, vlib_buffer_t *b, static_always_inline nat_translation_error_e nat_6t_flow_buf_translate (vlib_main_t *vm, 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, + ip_protocol_t proto, int is_output_feature, int is_i2o) { if (!is_output_feature && f->ops & NAT_FLOW_OP_TXFIB_REWRITE) @@ -3828,7 +3982,7 @@ nat_6t_flow_buf_translate (vlib_main_t *vm, snat_main_t *sm, vlib_buffer_t *b, vnet_buffer (b)->sw_if_index[VLIB_TX] = f->rewrite.fib_index; } - if (NAT_PROTOCOL_ICMP == proto) + if (IP_PROTOCOL_ICMP == proto) { if (ip->src_address.as_u32 != f->rewrite.saddr.as_u32) { @@ -3856,7 +4010,7 @@ nat_6t_flow_buf_translate (vlib_main_t *vm, snat_main_t *sm, vlib_buffer_t *b, nat_translation_error_e nat_6t_flow_buf_translate_i2o (vlib_main_t *vm, snat_main_t *sm, vlib_buffer_t *b, ip4_header_t *ip, - nat_6t_flow_t *f, nat_protocol_t proto, + nat_6t_flow_t *f, ip_protocol_t proto, int is_output_feature) { return nat_6t_flow_buf_translate (vm, sm, b, ip, f, proto, is_output_feature, @@ -3866,116 +4020,130 @@ nat_6t_flow_buf_translate_i2o (vlib_main_t *vm, snat_main_t *sm, nat_translation_error_e nat_6t_flow_buf_translate_o2i (vlib_main_t *vm, snat_main_t *sm, vlib_buffer_t *b, ip4_header_t *ip, - nat_6t_flow_t *f, nat_protocol_t proto, + nat_6t_flow_t *f, ip_protocol_t proto, int is_output_feature) { return nat_6t_flow_buf_translate (vm, sm, b, ip, f, proto, is_output_feature, 0 /* is_i2o */); } -u8 * -format_nat_6t (u8 *s, va_list *args) +static_always_inline void +nat_syslog_nat44_sess (u32 ssubix, u32 sfibix, ip4_address_t *isaddr, + u16 isport, ip4_address_t *xsaddr, u16 xsport, + ip4_address_t *idaddr, u16 idport, + ip4_address_t *xdaddr, u16 xdport, u8 proto, u8 is_add, + u8 is_twicenat) { - nat_6t_t *t = va_arg (*args, nat_6t_t *); + syslog_msg_t syslog_msg; + fib_table_t *fib; - 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; + if (!syslog_is_enabled ()) + return; + + if (syslog_severity_filter_block (SADD_SDEL_SEVERITY)) + return; + + fib = fib_table_get (sfibix, FIB_PROTOCOL_IP4); + + syslog_msg_init (&syslog_msg, NAT_FACILITY, SADD_SDEL_SEVERITY, NAT_APPNAME, + is_add ? SADD_MSGID : SDEL_MSGID); + + syslog_msg_sd_init (&syslog_msg, NSESS_SDID); + syslog_msg_add_sd_param (&syslog_msg, SSUBIX_SDPARAM_NAME, "%d", ssubix); + syslog_msg_add_sd_param (&syslog_msg, SVLAN_SDPARAM_NAME, "%d", + fib->ft_table_id); + syslog_msg_add_sd_param (&syslog_msg, IATYP_SDPARAM_NAME, IATYP_IPV4); + syslog_msg_add_sd_param (&syslog_msg, ISADDR_SDPARAM_NAME, "%U", + format_ip4_address, isaddr); + syslog_msg_add_sd_param (&syslog_msg, ISPORT_SDPARAM_NAME, "%d", + clib_net_to_host_u16 (isport)); + syslog_msg_add_sd_param (&syslog_msg, XATYP_SDPARAM_NAME, IATYP_IPV4); + syslog_msg_add_sd_param (&syslog_msg, XSADDR_SDPARAM_NAME, "%U", + format_ip4_address, xsaddr); + syslog_msg_add_sd_param (&syslog_msg, XSPORT_SDPARAM_NAME, "%d", + clib_net_to_host_u16 (xsport)); + syslog_msg_add_sd_param (&syslog_msg, PROTO_SDPARAM_NAME, "%d", proto); + syslog_msg_add_sd_param (&syslog_msg, XDADDR_SDPARAM_NAME, "%U", + format_ip4_address, xdaddr); + syslog_msg_add_sd_param (&syslog_msg, XDPORT_SDPARAM_NAME, "%d", + clib_net_to_host_u16 (xdport)); + if (is_twicenat) + { + syslog_msg_add_sd_param (&syslog_msg, IDADDR_SDPARAM_NAME, "%U", + format_ip4_address, idaddr); + syslog_msg_add_sd_param (&syslog_msg, IDPORT_SDPARAM_NAME, "%d", + clib_net_to_host_u16 (idport)); + } + + syslog_msg_send (&syslog_msg); } -u8 * -format_nat_ed_translation_error (u8 *s, va_list *args) +void +nat_syslog_nat44_sadd (u32 ssubix, u32 sfibix, ip4_address_t *isaddr, + u16 isport, ip4_address_t *idaddr, u16 idport, + ip4_address_t *xsaddr, u16 xsport, + ip4_address_t *xdaddr, u16 xdport, u8 proto, + u8 is_twicenat) { - 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; - case NAT_ED_TRNSL_ERR_PACKET_TRUNCATED: - s = format (s, "packet-truncated"); - break; - case NAT_ED_TRNSL_ERR_INNER_IP_CORRUPT: - s = format (s, "inner-ip-corrupted"); - break; - case NAT_ED_TRNSL_ERR_INVALID_CSUM: - s = format (s, "invalid-checksum"); - break; - } - return s; + nat_syslog_nat44_sess (ssubix, sfibix, isaddr, isport, xsaddr, xsport, + idaddr, idport, xdaddr, xdport, proto, 1, + is_twicenat); } -u8 * -format_nat_6t_flow (u8 *s, va_list *args) +void +nat_syslog_nat44_sdel (u32 ssubix, u32 sfibix, ip4_address_t *isaddr, + u16 isport, ip4_address_t *idaddr, u16 idport, + ip4_address_t *xsaddr, u16 xsport, + ip4_address_t *xdaddr, u16 xdport, u8 proto, + u8 is_twicenat) +{ + nat_syslog_nat44_sess (ssubix, sfibix, isaddr, isport, xsaddr, xsport, + idaddr, idport, xdaddr, xdport, proto, 0, + is_twicenat); +} +__clib_export void +nat44_original_dst_lookup (ip4_address_t *i2o_src, u16 i2o_src_port, + ip4_address_t *i2o_dst, u16 i2o_dst_port, + ip_protocol_t proto, u32 *original_dst, + u16 *original_dst_port) { - nat_6t_flow_t *f = va_arg (*args, nat_6t_flow_t *); + snat_main_per_thread_data_t *tsm; + snat_main_t *sm = &snat_main; + u32 fib_index = 0; + snat_session_t *s; + ip4_header_t ip; - 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) + ip.src_address.as_u32 = i2o_src->as_u32; + fib_index = fib_table_find (FIB_PROTOCOL_IP4, 0); + + if (sm->num_workers > 1) { - if (!r) - { - s = format (s, "rewrite: "); - r = 1; - } - s = format (s, "daddr %U ", format_ip4_address, f->rewrite.daddr.as_u8); + tsm = vec_elt_at_index ( + sm->per_thread_data, + nat44_ed_get_in2out_worker_index (0, &ip, fib_index, 0)); } - if (f->ops & NAT_FLOW_OP_DPORT_REWRITE) + else { - if (!r) - { - s = format (s, "rewrite: "); - r = 1; - } - s = format (s, "dport %u ", clib_net_to_host_u16 (f->rewrite.dport)); + tsm = vec_elt_at_index (sm->per_thread_data, sm->num_workers); } - if (f->ops & NAT_FLOW_OP_ICMP_ID_REWRITE) + + /* query */ + clib_bihash_kv_16_8_t kv = { 0 }, value; + init_ed_k (&kv, i2o_src->as_u32, i2o_src_port, i2o_dst->as_u32, i2o_dst_port, + fib_index, proto); + if (tsm->sessions == NULL || + clib_bihash_search_16_8 (&sm->flow_hash, &kv, &value)) { - if (!r) - { - s = format (s, "rewrite: "); - r = 1; - } - s = format (s, "icmp-id %u ", clib_net_to_host_u16 (f->rewrite.icmp_id)); + return; } - if (f->ops & NAT_FLOW_OP_TXFIB_REWRITE) + s = pool_elt_at_index (tsm->sessions, ed_value_get_session_index (&value)); + if (s) { - if (!r) - { - s = format (s, "rewrite: "); - r = 1; - } - s = format (s, "txfib %u ", f->rewrite.fib_index); + *original_dst = s->i2o.rewrite.saddr.as_u32; + *original_dst_port = s->i2o.rewrite.sport; } - return s; + return; } - /* * fd.io coding-style-patch-verification: ON * diff --git a/src/plugins/nat/nat44-ed/nat44_ed.h b/src/plugins/nat/nat44-ed/nat44_ed.h index b74b46f81b7..706511475cf 100644 --- a/src/plugins/nat/nat44-ed/nat44_ed.h +++ b/src/plugins/nat/nat44-ed/nat44_ed.h @@ -12,10 +12,7 @@ * 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__ @@ -39,6 +36,16 @@ /* default number of worker handoff frame queue elements */ #define NAT_FQ_NELTS_DEFAULT 64 +/* 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) + +/* system ports range is 0-1023, first user port is 1024 per + * https://www.rfc-editor.org/rfc/rfc6335#section-6 + */ +#define ED_USER_PORT_OFFSET 1024 + /* NAT buffer flags */ #define SNAT_FLAG_HAIRPINNING (1 << 0) @@ -58,16 +65,9 @@ typedef enum 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 @@ -91,46 +91,12 @@ typedef struct 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") \ _ (TRNSL_FAILED, "couldn't translate packet") typedef enum @@ -160,19 +126,42 @@ typedef enum NAT_OUT2IN_ED_N_ERROR, } nat_out2in_ed_error_t; +typedef enum +{ + NAT44_ED_TCP_FLAG_FIN = 0, + NAT44_ED_TCP_FLAG_SYN, + NAT44_ED_TCP_FLAG_RST, + NAT44_ED_TCP_FLAG_ACK, + NAT44_ED_TCP_N_FLAG, +} nat44_ed_tcp_flag_e; + +typedef enum +{ + NAT44_ED_DIR_I2O = 0, + NAT44_ED_DIR_O2I, + NAT44_ED_N_DIR, +} nat44_ed_dir_e; /* 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 +typedef enum +{ + NAT44_ED_TCP_STATE_CLOSED = 0, + NAT44_ED_TCP_STATE_ESTABLISHED, + NAT44_ED_TCP_STATE_CLOSING, + NAT44_ED_TCP_N_STATE, +} nat44_ed_tcp_state_e; + +format_function_t format_ed_session_kvp; +format_function_t format_snat_session; +format_function_t format_snat_static_mapping; +format_function_t format_snat_static_map_to_resolve; +format_function_t format_nat_ed_translation_error; +format_function_t format_nat_6t_flow; +format_function_t format_nat_6t; +format_function_t format_nat44_ed_tcp_state; /* 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) @@ -308,7 +297,7 @@ typedef CLIB_PACKED(struct u16 port; } in2out; - nat_protocol_t nat_proto; + ip_protocol_t proto; nat_6t_flow_t i2o; nat_6t_flow_t o2i; @@ -341,10 +330,8 @@ typedef CLIB_PACKED(struct u16 ext_host_nat_port; /* TCP session state */ - u8 state; - u32 i2o_fin_seq; - u32 o2i_fin_seq; - u64 tcp_closed_timestamp; + u8 tcp_flags[NAT44_ED_N_DIR]; + nat44_ed_tcp_state_e tcp_state; /* per vrf sessions index */ u32 per_vrf_sessions_index; @@ -355,29 +342,14 @@ typedef CLIB_PACKED(struct typedef struct { ip4_address_t addr; + ip4_address_t net; + u32 sw_if_index; 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 _ + u32 addr_len; } 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 */ @@ -412,7 +384,7 @@ typedef enum typedef struct { - /* prefered pool address */ + /* preferred pool address */ ip4_address_t pool_addr; /* local IP address */ ip4_address_t local_addr; @@ -426,7 +398,7 @@ typedef struct u32 vrf_id; u32 fib_index; /* protocol */ - nat_protocol_t proto; + ip_protocol_t proto; /* 0 = disabled, otherwise client IP affinity sticky time in seconds */ u32 affinity; /* worker threads used by backends/local host */ @@ -449,21 +421,31 @@ typedef struct typedef struct { + u8 is_resolved; 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; + ip_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; +} snat_static_mapping_resolve_t; + +typedef struct +{ + u8 is_resolved; + u8 is_twice_nat; + u32 sw_if_index; +} snat_address_resolve_t; + +typedef struct +{ + u32 count; + u32 sw_if_index; + ip4_address_t addr; +} snat_fib_entry_reg_t; typedef struct { @@ -487,7 +469,7 @@ typedef struct /* real thread index */ u32 thread_index; - per_vrf_sessions_t *per_vrf_sessions_vec; + per_vrf_sessions_t *per_vrf_sessions_pool; } snat_main_per_thread_data_t; @@ -498,17 +480,24 @@ u32 nat44_ed_get_in2out_worker_index (vlib_buffer_t *b, ip4_header_t *ip, u32 nat44_ed_get_out2in_worker_index (vlib_buffer_t *b, ip4_header_t *ip, u32 rx_fib_index, u8 is_output); -/* Return worker thread index for given packet */ -/* 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 nat_fib_s +{ + u32 fib_index; + u32 ref_count; +} nat_fib_t; + +typedef struct vrf_route_s +{ + u32 vrf_id; + u32 fib_index; +} vrf_route_t; + +typedef struct vrf_table_s +{ + u32 table_vrf_id; + u32 table_fib_index; + vrf_route_t *routes; +} vrf_table_t; typedef struct snat_main_s { @@ -521,12 +510,6 @@ typedef struct snat_main_s /* 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; @@ -537,39 +520,40 @@ typedef struct snat_main_s /* Endpoint dependent lookup table */ clib_bihash_16_8_t flow_hash; + // vector of fibs + nat_fib_t *fibs; + + u32 inside_vrf_id; + u32 inside_fib_index; + + u32 outside_vrf_id; + u32 outside_fib_index; + + // vector of outside fibs + nat_fib_t *outside_fibs; + + // VRF routing table for dynamic sessions + vrf_table_t *vrf_tables; + /* Interface pool */ snat_interface_t *interfaces; snat_interface_t *output_feature_interfaces; + // broken api backward compatibility + snat_interface_t *output_feature_dummy_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; + /* first interface address should be auto-added */ + snat_address_resolve_t *addr_to_resolve; + + /* vector of fib entries */ + snat_fib_entry_reg_t *fib_entry_reg; /* vector of interface address static mappings to resolve. */ - snat_static_map_resolve_t *to_resolve; + snat_static_mapping_resolve_t *sm_to_resolve; /* Randomize port allocation order */ u32 random_seed; @@ -579,20 +563,11 @@ typedef struct snat_main_s 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; @@ -600,11 +575,6 @@ typedef struct snat_main_s 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 */ @@ -657,24 +627,27 @@ typedef struct snat_main_s 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; - + /* TCP session state machine table: + * first dimension is possible states + * second dimension is direction (in2out/out2in) + * third dimension is TCP flag (SYN, RST, FIN) + * + * value is next state to change to + */ + nat44_ed_tcp_state_e tcp_state_change_table[NAT44_ED_TCP_N_STATE] + [NAT44_ED_N_DIR] + [NAT44_ED_TCP_N_FLAG]; } snat_main_t; typedef struct @@ -689,106 +662,109 @@ typedef struct uword *cached_presence_by_ip4_address; } snat_runtime_t; +/* + * 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. + */ + 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; +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; /** \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 + @return true if SNAT session is created from static mapping otherwise 0 */ -#define snat_is_unk_proto_session(s) (s->flags & SNAT_SESSION_FLAG_UNKNOWN_PROTO) +always_inline bool +nat44_ed_is_session_static (snat_session_t *s) +{ + return s->flags & SNAT_SESSION_FLAG_STATIC_MAPPING; +} /** \brief Check if NAT session is twice NAT. @param s NAT session - @return 1 if NAT session is twice NAT + @return true if NAT session is twice NAT */ -#define is_twice_nat_session(s) (s->flags & SNAT_SESSION_FLAG_TWICE_NAT) +always_inline bool +nat44_ed_is_twice_nat_session (snat_session_t *s) +{ + return 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 + @return true if NAT session is load-balancing */ -#define is_lb_session(s) (s->flags & SNAT_SESSION_FLAG_LOAD_BALANCING) +always_inline bool +nat44_ed_is_lb_session (snat_session_t *s) +{ + return 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 + @return true if NAT session is load-balancing */ -#define is_ed_session(s) (s->flags & SNAT_SESSION_FLAG_ENDPOINT_DEPENDENT) +always_inline bool +na44_ed_is_fwd_bypass_session (snat_session_t *s) +{ + return s->flags & SNAT_SESSION_FLAG_FWD_BYPASS; +} /** \brief Check if NAT session has affinity record. @param s NAT session - @return 1 if NAT session has affinity record + @return true if NAT session has affinity record */ -#define is_affinity_sessions(s) (s->flags & SNAT_SESSION_FLAG_AFFINITY) +always_inline bool +nat44_ed_is_affinity_session (snat_session_t *s) +{ + return 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 + @return true if exact pool address */ -#define is_exact_address_session(s) (s->flags & SNAT_SESSION_FLAG_EXACT_ADDRESS) +always_inline bool +nat44_ed_is_exact_address_session (snat_session_t *s) +{ + return s->flags & SNAT_SESSION_FLAG_EXACT_ADDRESS; +} /** \brief Check if NAT interface is inside. @param i NAT interface - @return 1 if inside interface + @return true if inside interface */ -#define nat_interface_is_inside(i) i->flags & NAT_INTERFACE_FLAG_IS_INSIDE +always_inline bool +nat44_ed_is_interface_inside (snat_interface_t *i) +{ + return 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 + @return true if outside interface */ -#define nat44_is_ses_closed(s) s->state == 0xf +always_inline bool +nat44_ed_is_interface_outside (snat_interface_t *i) +{ + return i->flags & NAT_INTERFACE_FLAG_IS_OUTSIDE; +} /** \brief Check if client initiating TCP connection (received SYN from client) @param t TCP header - @return 1 if client initiating TCP connection + @return true if client initiating TCP connection */ always_inline bool tcp_flags_is_init (u8 f) @@ -844,111 +820,59 @@ is_sm_switch_address (u32 f) return (f & NAT_SM_FLAG_SWITCH_ADDRESS); } -/* 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__) +clib_error_t *nat44_api_hookup (vlib_main_t *vm); + +int snat_set_workers (uword *bitmap); + +int nat44_plugin_enable (nat44_config_t c); +int nat44_plugin_disable (); + +int nat44_ed_add_interface (u32 sw_if_index, u8 is_inside); +int nat44_ed_del_interface (u32 sw_if_index, u8 is_inside); +int nat44_ed_add_output_interface (u32 sw_if_index); +int nat44_ed_del_output_interface (u32 sw_if_index); + +int nat44_ed_add_address (ip4_address_t *addr, u32 vrf_id, u8 twice_nat); +int nat44_ed_del_address (ip4_address_t addr, u8 twice_nat); +int nat44_ed_add_interface_address (u32 sw_if_index, u8 twice_nat); +int nat44_ed_del_interface_address (u32 sw_if_index, u8 twice_nat); + +int nat44_ed_add_del_vrf_table (u32 table_vrf_id, bool is_add); +int nat44_ed_add_del_vrf_route (u32 table_vrf_id, u32 vrf_id, bool is_add); +void nat44_ed_del_vrf_tables (); + int nat44_ed_add_static_mapping (ip4_address_t l_addr, ip4_address_t e_addr, - u16 l_port, u16 e_port, nat_protocol_t proto, + u16 l_port, u16 e_port, ip_protocol_t proto, u32 vrf_id, u32 sw_if_index, u32 flags, ip4_address_t pool_addr, u8 *tag); int nat44_ed_del_static_mapping (ip4_address_t l_addr, ip4_address_t e_addr, - u16 l_port, u16 e_port, nat_protocol_t proto, + u16 l_port, u16 e_port, ip_protocol_t proto, u32 vrf_id, u32 sw_if_index, u32 flags); int nat44_ed_add_lb_static_mapping (ip4_address_t e_addr, u16 e_port, - nat_protocol_t proto, + ip_protocol_t proto, nat44_lb_addr_port_t *locals, u32 flags, u8 *tag, u32 affinity); int nat44_ed_del_lb_static_mapping (ip4_address_t e_addr, u16 e_port, - nat_protocol_t proto, u32 flags); + ip_protocol_t proto, u32 flags); int nat44_ed_add_del_lb_static_mapping_local (ip4_address_t e_addr, u16 e_port, ip4_address_t l_addr, u16 l_port, - nat_protocol_t proto, u32 vrf_id, + ip_protocol_t proto, u32 vrf_id, u8 probability, u8 is_add); /** - * @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); - -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); - -int nat44_ed_add_interface (u32 sw_if_index, u8 is_inside); -int nat44_ed_del_interface (u32 sw_if_index, u8 is_inside); -int nat44_ed_add_output_interface (u32 sw_if_index); -int nat44_ed_del_output_interface (u32 sw_if_index); - -/** - * @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 @@ -960,20 +884,12 @@ int snat_add_interface_address (snat_main_t * sm, u32 sw_if_index, int is_del, * * @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, +int nat44_ed_del_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); +void nat44_ed_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) @@ -993,89 +909,20 @@ int nat44_set_session_limit (u32 session_limit, u32 vrf_id); */ 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 ( - vlib_main_t *vm, 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); - -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; +int snat_static_mapping_match (vlib_main_t *vm, ip4_address_t match_addr, + u16 match_port, u32 match_fib_index, + ip_protocol_t match_protocol, + ip4_address_t *mapping_addr, u16 *mapping_port, + u32 *mapping_fib_index, int 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); 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); @@ -1084,6 +931,28 @@ void nat44_ed_sessions_clear (); int nat44_ed_set_frame_queue_nelts (u32 frame_queue_nelts); +void nat_6t_l3_l4_csum_calc (nat_6t_flow_t *f); + +snat_static_mapping_t *nat44_ed_sm_i2o_lookup (snat_main_t *sm, + ip4_address_t addr, u16 port, + u32 fib_index, u8 proto); + +snat_static_mapping_t *nat44_ed_sm_o2i_lookup (snat_main_t *sm, + ip4_address_t addr, u16 port, + u32 fib_index, u8 proto); + +void nat_syslog_nat44_sadd (u32 ssubix, u32 sfibix, ip4_address_t *isaddr, + u16 isport, ip4_address_t *idaddr, u16 idport, + ip4_address_t *xsaddr, u16 xsport, + ip4_address_t *xdaddr, u16 xdport, u8 proto, + u8 is_twicenat); + +void nat_syslog_nat44_sdel (u32 ssubix, u32 sfibix, ip4_address_t *isaddr, + u16 isport, ip4_address_t *idaddr, u16 idport, + ip4_address_t *xsaddr, u16 xsport, + ip4_address_t *xdaddr, u16 xdport, u8 proto, + u8 is_twicenat); + typedef enum { NAT_ED_TRNSL_ERR_SUCCESS = 0, @@ -1096,17 +965,11 @@ typedef enum nat_translation_error_e nat_6t_flow_buf_translate_i2o ( vlib_main_t *vm, 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); + nat_6t_flow_t *f, ip_protocol_t proto, int is_output_feature); nat_translation_error_e nat_6t_flow_buf_translate_o2i ( vlib_main_t *vm, 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; + nat_6t_flow_t *f, ip_protocol_t proto, int is_output_feature); #endif /* __included_nat44_ed_h__ */ /* diff --git a/src/plugins/nat/nat44-ed/nat44_ed_affinity.c b/src/plugins/nat/nat44-ed/nat44_ed_affinity.c index 89f11c64ef3..178671c6b7e 100644 --- a/src/plugins/nat/nat44-ed/nat44_ed_affinity.c +++ b/src/plugins/nat/nat44-ed/nat44_ed_affinity.c @@ -37,10 +37,9 @@ format_affinity_kvp (u8 * s, va_list * args) 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); + format_ip4_address, &k.client_addr, format_ip4_address, + &k.service_addr, clib_net_to_host_u16 (k.service_port), + format_ip_protocol, k.proto); return s; } diff --git a/src/plugins/nat/nat44-ed/nat44_ed_api.c b/src/plugins/nat/nat44-ed/nat44_ed_api.c index 74d48b2d821..b6c9d51d777 100644 --- a/src/plugins/nat/nat44-ed/nat44_ed_api.c +++ b/src/plugins/nat/nat44-ed/nat44_ed_api.c @@ -31,6 +31,8 @@ #include <nat/nat44-ed/nat44_ed.api_enum.h> #include <nat/nat44-ed/nat44_ed.api_types.h> +#include <nat/nat44-ed/nat44_ed_inlines.h> + #define REPLY_MSG_ID_BASE sm->msg_id_base #include <vlibapi/api_helper_macros.h> @@ -47,15 +49,19 @@ vl_api_nat44_ed_plugin_enable_disable_t_handler ( if (mp->enable) { - 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); + if ((mp->flags & NAT44_API_IS_STATIC_MAPPING_ONLY) || + (mp->flags & NAT44_API_IS_CONNECTION_TRACKING)) + { + rv = VNET_API_ERROR_UNSUPPORTED; + } + else + { + c.sessions = ntohl (mp->sessions); + c.inside_vrf = ntohl (mp->inside_vrf); + c.outside_vrf = ntohl (mp->outside_vrf); - rv = nat44_plugin_enable (c); + rv = nat44_plugin_enable (c); + } } else { @@ -171,21 +177,6 @@ vl_api_nat44_set_session_limit_t_handler (vl_api_nat44_set_session_limit_t * } 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_nat_ipfix_enable_disable_t_handler (vl_api_nat_ipfix_enable_disable_t * mp) { @@ -217,22 +208,6 @@ vl_api_nat_set_timeouts_t_handler (vl_api_nat_set_timeouts_t * mp) } 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_mss_clamping_t_handler (vl_api_nat_set_mss_clamping_t * mp) { snat_main_t *sm = &snat_main; @@ -275,12 +250,6 @@ static void 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; @@ -303,9 +272,13 @@ static void for (i = 0; i < count; i++) { if (is_add) - rv = snat_add_address (sm, &this_addr, vrf_id, twice_nat); + { + rv = nat44_ed_add_address (&this_addr, vrf_id, twice_nat); + } else - rv = snat_del_address (sm, this_addr, 0, twice_nat); + { + rv = nat44_ed_del_address (this_addr, twice_nat); + } if (rv) goto send_reply; @@ -400,9 +373,9 @@ send_nat44_interface_details (snat_interface_t * i, 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)) + if (nat44_ed_is_interface_inside (i)) rmp->flags |= NAT_API_IS_INSIDE; - if (nat_interface_is_outside (i)) + if (nat44_ed_is_interface_outside (i)) rmp->flags |= NAT_API_IS_OUTSIDE; rmp->context = context; @@ -422,74 +395,77 @@ vl_api_nat44_interface_dump_t_handler (vl_api_nat44_interface_dump_t * mp) return; pool_foreach (i, sm->interfaces) - { - send_nat44_interface_details(i, reg, mp->context); - } + { + 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) +vl_api_nat44_ed_add_del_output_interface_t_handler ( + vl_api_nat44_ed_add_del_output_interface_t *mp) { - vl_api_nat44_interface_add_del_output_feature_reply_t *rmp; + vl_api_nat44_ed_add_del_output_interface_reply_t *rmp; snat_main_t *sm = &snat_main; - u32 sw_if_index; int rv = 0; - VALIDATE_SW_IF_INDEX (mp); - - sw_if_index = ntohl (mp->sw_if_index); + VALIDATE_SW_IF_INDEX_END (mp); if (mp->is_add) { - rv = nat44_ed_add_output_interface (sw_if_index); + rv = nat44_ed_add_output_interface (mp->sw_if_index); } else { - rv = nat44_ed_del_output_interface (sw_if_index); + rv = nat44_ed_del_output_interface (mp->sw_if_index); } - BAD_SW_IF_INDEX_LABEL; - REPLY_MACRO (VL_API_NAT44_INTERFACE_ADD_DEL_OUTPUT_FEATURE_REPLY); +bad_sw_if_index: + REPLY_MACRO_END (VL_API_NAT44_ED_ADD_DEL_OUTPUT_INTERFACE_REPLY); } +#define vl_endianfun +#include <nat/nat44-ed/nat44_ed.api.h> +#undef vl_endianfun static void -send_nat44_interface_output_feature_details (snat_interface_t * i, - vl_api_registration_t * reg, - u32 context) +send_nat44_ed_output_interface_details (u32 index, vl_api_registration_t *rp, + 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); + vl_api_nat44_ed_output_interface_details_t *rmp; + snat_interface_t *i = + pool_elt_at_index (sm->output_feature_interfaces, index); + + /* Make sure every field is initiated (or don't skip the clib_memset()) */ + REPLY_MACRO_DETAILS4 ( + VL_API_NAT44_ED_OUTPUT_INTERFACE_DETAILS, rp, context, ({ + rmp->sw_if_index = i->sw_if_index; + + /* Endian hack until apigen registers _details + * endian functions */ + vl_api_nat44_ed_output_interface_details_t_endian (rmp, + 1 /* to network */); + rmp->_vl_msg_id = htons (rmp->_vl_msg_id); + rmp->context = htonl (rmp->context); + })); } static void - vl_api_nat44_interface_output_feature_dump_t_handler - (vl_api_nat44_interface_output_feature_dump_t * mp) +vl_api_nat44_ed_output_interface_get_t_handler ( + vl_api_nat44_ed_output_interface_get_t *mp) { - vl_api_registration_t *reg; + vl_api_nat44_ed_output_interface_get_reply_t *rmp; snat_main_t *sm = &snat_main; - snat_interface_t *i; + i32 rv = 0; - reg = vl_api_client_index_to_registration (mp->client_index); - if (!reg) - return; + if (pool_elts (sm->output_feature_interfaces) == 0) + { + REPLY_MACRO (VL_API_NAT44_ED_OUTPUT_INTERFACE_GET_REPLY); + return; + } - pool_foreach (i, sm->output_feature_interfaces) - { - send_nat44_interface_output_feature_details (i, reg, mp->context); - } + REPLY_AND_DETAILS_MACRO ( + VL_API_NAT44_ED_OUTPUT_INTERFACE_GET_REPLY, sm->output_feature_interfaces, + ({ send_nat44_ed_output_interface_details (cursor, rp, mp->context); })); } static void @@ -504,7 +480,7 @@ static void ip4_address_t l_addr, e_addr, pool_addr = { 0 }; u32 sw_if_index, flags = 0, vrf_id; u16 l_port = 0, e_port = 0; - nat_protocol_t proto = 0; + ip_protocol_t proto = 0; u8 *tag = 0; memcpy (&l_addr.as_u8, mp->local_ip_address, 4); @@ -517,7 +493,7 @@ static void { l_port = mp->local_port; e_port = mp->external_port; - proto = ip_proto_to_nat_proto (mp->protocol); + proto = mp->protocol; } if (mp->flags & NAT_API_IS_TWICE_NAT) @@ -578,7 +554,7 @@ static void ip4_address_t l_addr, e_addr, pool_addr; u32 sw_if_index, flags = 0, vrf_id; u16 l_port = 0, e_port = 0; - nat_protocol_t proto; + ip_protocol_t proto; u8 *tag = 0; memcpy (&l_addr.as_u8, mp->local_ip_address, 4); @@ -615,7 +591,7 @@ static void } sw_if_index = clib_net_to_host_u32 (mp->external_sw_if_index); - if (sw_if_index) + if (sw_if_index != ~0) { flags |= NAT_SM_FLAG_SWITCH_ADDRESS; } @@ -624,7 +600,7 @@ static void memcpy (&e_addr.as_u8, mp->external_ip_address, 4); } - proto = ip_proto_to_nat_proto (mp->protocol); + proto = mp->protocol; vrf_id = clib_net_to_host_u32 (mp->vrf_id); if (mp->is_add) @@ -688,7 +664,7 @@ send_nat44_static_mapping_details (snat_static_mapping_t * m, } else { - rmp->protocol = nat_proto_to_ip_proto (m->proto); + rmp->protocol = m->proto; rmp->external_port = m->external_port; rmp->local_port = m->local_port; } @@ -700,9 +676,8 @@ send_nat44_static_mapping_details (snat_static_mapping_t * m, } static void -send_nat44_static_map_resolve_details (snat_static_map_resolve_t * m, - vl_api_registration_t * reg, - u32 context) +send_nat44_static_map_resolve_details (snat_static_mapping_resolve_t *m, + vl_api_registration_t *reg, u32 context) { vl_api_nat44_static_mapping_details_t *rmp; snat_main_t *sm = &snat_main; @@ -716,19 +691,22 @@ send_nat44_static_map_resolve_details (snat_static_map_resolve_t * m, rmp->vrf_id = htonl (m->vrf_id); rmp->context = context; - if (m->twice_nat) - rmp->flags |= NAT_API_IS_TWICE_NAT; + if (is_sm_twice_nat (m->flags)) + { + rmp->flags |= NAT_API_IS_TWICE_NAT; + } - if (m->addr_only) + if (is_sm_addr_only (m->flags)) { rmp->flags |= NAT_API_IS_ADDR_ONLY; } else { - rmp->protocol = nat_proto_to_ip_proto (m->proto); + rmp->protocol = 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)); @@ -742,7 +720,7 @@ vl_api_nat44_static_mapping_dump_t_handler (vl_api_nat44_static_mapping_dump_t vl_api_registration_t *reg; snat_main_t *sm = &snat_main; snat_static_mapping_t *m; - snat_static_map_resolve_t *rp; + snat_static_mapping_resolve_t *rp; int j; reg = vl_api_client_index_to_registration (mp->client_index); @@ -755,10 +733,10 @@ vl_api_nat44_static_mapping_dump_t_handler (vl_api_nat44_static_mapping_dump_t send_nat44_static_mapping_details (m, reg, mp->context); } - for (j = 0; j < vec_len (sm->to_resolve); j++) + for (j = 0; j < vec_len (sm->sm_to_resolve); j++) { - rp = sm->to_resolve + j; - if (!rp->identity_nat) + rp = sm->sm_to_resolve + j; + if (!is_sm_identity_nat (rp->flags)) send_nat44_static_map_resolve_details (rp, reg, mp->context); } } @@ -774,7 +752,7 @@ static void ip4_address_t addr, pool_addr = { 0 }; u32 sw_if_index, flags, vrf_id; - nat_protocol_t proto = 0; + ip_protocol_t proto = 0; u16 port = 0; u8 *tag = 0; @@ -787,7 +765,7 @@ static void else { port = mp->port; - proto = ip_proto_to_nat_proto (mp->protocol); + proto = mp->protocol; } sw_if_index = clib_net_to_host_u32 (mp->sw_if_index); @@ -840,7 +818,7 @@ send_nat44_identity_mapping_details (snat_static_mapping_t * m, int index, 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->protocol = m->proto; rmp->context = context; if (m->tag) strncpy ((char *) rmp->tag, (char *) m->tag, vec_len (m->tag)); @@ -849,8 +827,8 @@ send_nat44_identity_mapping_details (snat_static_mapping_t * m, int index, } static void -send_nat44_identity_map_resolve_details (snat_static_map_resolve_t * m, - vl_api_registration_t * reg, +send_nat44_identity_map_resolve_details (snat_static_mapping_resolve_t *m, + vl_api_registration_t *reg, u32 context) { vl_api_nat44_identity_mapping_details_t *rmp; @@ -861,13 +839,13 @@ send_nat44_identity_map_resolve_details (snat_static_map_resolve_t * m, rmp->_vl_msg_id = ntohs (VL_API_NAT44_IDENTITY_MAPPING_DETAILS + sm->msg_id_base); - if (m->addr_only) + if (is_sm_addr_only (m->flags)) 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->protocol = m->proto; rmp->context = context; if (m->tag) strncpy ((char *) rmp->tag, (char *) m->tag, vec_len (m->tag)); @@ -882,7 +860,7 @@ static void vl_api_registration_t *reg; snat_main_t *sm = &snat_main; snat_static_mapping_t *m; - snat_static_map_resolve_t *rp; + snat_static_mapping_resolve_t *rp; int j; reg = vl_api_client_index_to_registration (mp->client_index); @@ -890,20 +868,20 @@ static void return; pool_foreach (m, sm->static_mappings) - { - if (is_sm_identity_nat (m->flags) && !is_sm_lb (m->flags)) - { - pool_foreach_index (j, m->locals) - { - send_nat44_identity_mapping_details (m, j, reg, mp->context); - } - } - } + { + if (is_sm_identity_nat (m->flags) && !is_sm_lb (m->flags)) + { + 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++) + for (j = 0; j < vec_len (sm->sm_to_resolve); j++) { - rp = sm->to_resolve + j; - if (rp->identity_nat) + rp = sm->sm_to_resolve + j; + if (is_sm_identity_nat (rp->flags)) send_nat44_identity_map_resolve_details (rp, reg, mp->context); } } @@ -915,25 +893,24 @@ static void 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); + u8 twice_nat; 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); + twice_nat = mp->flags & NAT_API_IS_TWICE_NAT; + + if (mp->is_add) + { + rv = nat44_ed_add_interface_address (sw_if_index, twice_nat); + } + else + { + rv = nat44_ed_del_interface_address (sw_if_index, twice_nat); + } BAD_SW_IF_INDEX_LABEL; -send_reply: REPLY_MACRO (VL_API_NAT44_ADD_DEL_INTERFACE_ADDR_REPLY); } @@ -962,21 +939,18 @@ 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; + vl_api_registration_t *reg; + snat_address_resolve_t *ap; 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) + vec_foreach (ap, sm->addr_to_resolve) { - send_nat44_interface_addr_details (*i, reg, mp->context, 1); + send_nat44_interface_addr_details (ap->sw_if_index, reg, mp->context, + ap->is_twice_nat); } } @@ -1010,7 +984,7 @@ vl_api_nat44_add_del_lb_static_mapping_t_handler ( vl_api_nat44_add_del_lb_static_mapping_reply_t *rmp; nat44_lb_addr_port_t *locals = 0; ip4_address_t e_addr; - nat_protocol_t proto; + ip_protocol_t proto; u32 flags = 0; u8 *tag = 0; int rv = 0; @@ -1018,7 +992,7 @@ vl_api_nat44_add_del_lb_static_mapping_t_handler ( 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); + proto = mp->protocol; if (mp->flags & NAT_API_IS_TWICE_NAT) { @@ -1063,11 +1037,11 @@ vl_api_nat44_lb_static_mapping_add_del_local_t_handler ( 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; + ip_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); + proto = mp->protocol; rv = nat44_ed_add_del_lb_static_mapping_local ( e_addr, mp->external_port, l_addr, mp->local.port, proto, @@ -1089,13 +1063,14 @@ send_nat44_lb_static_mapping_details (snat_static_mapping_t *m, 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->protocol = m->proto; rmp->context = context; if (is_sm_self_twice_nat (m->flags)) @@ -1169,7 +1144,7 @@ vl_api_nat44_del_session_t_handler (vl_api_nat44_del_session_t *mp) is_in = mp->flags & NAT_API_IS_INSIDE; - rv = nat44_del_ed_session (sm, &addr, port, &eh_addr, eh_port, mp->protocol, + rv = nat44_ed_del_session (sm, &addr, port, &eh_addr, eh_port, mp->protocol, vrf_id, is_in); REPLY_MACRO (VL_API_NAT44_DEL_SESSION_REPLY); @@ -1187,253 +1162,160 @@ vl_api_nat44_forwarding_enable_disable_t_handler ( } static void -vl_api_nat44_forwarding_is_enabled_t_handler ( - vl_api_nat44_forwarding_is_enabled_t *mp) +vl_api_nat44_show_running_config_t_handler ( + vl_api_nat44_show_running_config_t *mp) { - vl_api_registration_t *reg; + vl_api_nat44_show_running_config_reply_t *rmp; 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; + nat44_config_t *rc = &sm->rconfig; + int rv = 0; - 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; + 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->enabled = sm->forwarding_enabled; + rmp->sessions = htonl (rc->sessions); + rmp->translation_buckets = htonl (sm->translation_buckets); - vl_api_send_msg (reg, (u8 *) rmp); -} + // OBSOLETE + rmp->users = 0; + rmp->user_buckets = 0; + rmp->user_sessions = 0; -/* Obsolete calls hold back because of deprecation - * should not be used */ + 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); -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); + 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; + })); } static void -vl_api_nat_get_addr_and_port_alloc_alg_t_handler ( - vl_api_nat_get_addr_and_port_alloc_alg_t *mp) +vl_api_nat44_ed_add_del_vrf_table_t_handler ( + vl_api_nat44_ed_add_del_vrf_table_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); + vl_api_nat44_ed_add_del_vrf_table_reply_t *rmp; + int rv = nat44_ed_add_del_vrf_table (clib_net_to_host_u32 (mp->table_vrf_id), + mp->is_add); + REPLY_MACRO (VL_API_NAT44_ED_ADD_DEL_VRF_TABLE_REPLY); } static void -vl_api_nat_ha_set_listener_t_handler (vl_api_nat_ha_set_listener_t *mp) +vl_api_nat44_ed_add_del_vrf_route_t_handler ( + vl_api_nat44_ed_add_del_vrf_route_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); + vl_api_nat44_ed_add_del_vrf_route_reply_t *rmp; + int rv = + nat44_ed_add_del_vrf_route (clib_net_to_host_u32 (mp->table_vrf_id), + clib_net_to_host_u32 (mp->vrf_id), mp->is_add); + REPLY_MACRO (VL_API_NAT44_ED_ADD_DEL_VRF_ROUTE_REPLY); } static void -vl_api_nat_ha_get_listener_t_handler (vl_api_nat_ha_get_listener_t *mp) +nat44_ed_vrf_tables_send_details (vl_api_registration_t *rp, u32 context, + vrf_table_t *t) { 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); -} + vl_api_nat44_ed_vrf_tables_details_t *mp; -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); -} + u32 *vrf_ids = 0; + vrf_route_t *r; -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); -} + mp = vl_msg_api_alloc_zero (sizeof (*mp) + + sizeof (mp->vrf_ids[0]) * vec_len (t->routes)); + mp->_vl_msg_id = + ntohs (VL_API_NAT44_ED_VRF_TABLES_DETAILS + sm->msg_id_base); + mp->context = context; + mp->n_vrf_ids = clib_host_to_net_u32 (vec_len (t->routes)); -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); -} + pool_foreach (r, t->routes) + { + vec_add1 (vrf_ids, r->vrf_id); + } -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); -} + // copy the records + clib_memcpy (mp->vrf_ids, vrf_ids, + sizeof (mp->vrf_ids[0]) * vec_len (t->routes)); -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); -} + vec_free (vrf_ids); -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); + // send the message + vl_api_send_msg (rp, (u8 *) mp); } static void -vl_api_nat44_plugin_enable_disable_t_handler ( - vl_api_nat44_plugin_enable_disable_t *mp) +nat44_ed_vrf_tables_send_details_v2 (vl_api_registration_t *rp, u32 context, + vrf_table_t *t) { snat_main_t *sm = &snat_main; - nat44_config_t c = { 0 }; - vl_api_nat44_plugin_enable_disable_reply_t *rmp; - int rv = 0; + vl_api_nat44_ed_vrf_tables_v2_details_t *mp; - if (mp->enable) - { - if (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; + u32 *vrf_ids = 0; + vrf_route_t *r; - 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 + mp = vl_msg_api_alloc_zero (sizeof (*mp) + + sizeof (mp->vrf_ids[0]) * vec_len (t->routes)); + mp->_vl_msg_id = clib_net_to_host_u16 (VL_API_NAT44_ED_VRF_TABLES_DETAILS + + sm->msg_id_base); + mp->context = context; + mp->n_vrf_ids = clib_net_to_host_u32 (vec_len (t->routes)); + mp->table_vrf_id = clib_net_to_host_u32 (t->table_vrf_id); + pool_foreach (r, t->routes) { - rv = nat44_plugin_disable (); + vec_add1 (vrf_ids, clib_net_to_host_u32 (r->vrf_id)); } - REPLY_MACRO (VL_API_NAT44_PLUGIN_ENABLE_DISABLE_REPLY); -} + // copy the records + clib_memcpy (mp->vrf_ids, vrf_ids, + sizeof (mp->vrf_ids[0]) * vec_len (t->routes)); -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; + vec_free (vrf_ids); - REPLY_MACRO2 (VL_API_NAT_CONTROL_PING_REPLY, - ({ rmp->vpe_pid = ntohl (getpid ()); })); + // send the message + vl_api_send_msg (rp, (u8 *) mp); } static void -vl_api_nat_show_config_t_handler (vl_api_nat_show_config_t *mp) +vl_api_nat44_ed_vrf_tables_dump_t_handler ( + vl_api_nat44_ed_vrf_tables_dump_t *mp) { - vl_api_nat_show_config_reply_t *rmp; snat_main_t *sm = &snat_main; - int rv = 0; + vl_api_registration_t *rp; + vrf_table_t *t; - 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; + rp = vl_api_client_index_to_registration (mp->client_index); + if (rp == 0) + return; - 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; - })); + pool_foreach (t, sm->vrf_tables) + { + nat44_ed_vrf_tables_send_details (rp, mp->context, t); + } } static void -vl_api_nat44_show_running_config_t_handler ( - vl_api_nat44_show_running_config_t *mp) +vl_api_nat44_ed_vrf_tables_v2_dump_t_handler ( + vl_api_nat44_ed_vrf_tables_v2_dump_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); + vl_api_registration_t *rp; + vrf_table_t *t; - 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); + rp = vl_api_client_index_to_registration (mp->client_index); + if (rp == 0) + return; - 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; - })); + pool_foreach (t, sm->vrf_tables) + { + nat44_ed_vrf_tables_send_details_v2 (rp, mp->context, t); + } } /* user (internal host) key */ @@ -1515,7 +1397,7 @@ nat_ed_user_create_helper (user_create_helper_t *uch, snat_session_t *s) { u = pool_elt_at_index (uch->users, value.value); } - if (snat_is_session_static (s)) + if (nat44_ed_is_session_static (s)) { ++u->nstaticsessions; } @@ -1598,40 +1480,27 @@ send_nat44_user_session_details (snat_session_t * s, 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)) + if (nat44_ed_is_session_static (s)) rmp->flags |= NAT_API_IS_STATIC; - if (is_twice_nat_session (s)) + if (nat44_ed_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->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 = clib_host_to_net_u16 (s->proto); + clib_memcpy (rmp->ext_host_address, &s->ext_host_addr, 4); + rmp->ext_host_port = s->ext_host_port; + if (nat44_ed_is_twice_nat_session (s)) { - 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; - } + 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); @@ -1670,6 +1539,167 @@ vl_api_nat44_user_session_dump_t_handler (vl_api_nat44_user_session_dump_t * } } +static void +send_nat44_user_session_v2_details (snat_session_t *s, + vl_api_registration_t *reg, u32 context) +{ + vl_api_nat44_user_session_v2_details_t *rmp; + snat_main_t *sm = &snat_main; + vnet_main_t *vnm = vnet_get_main (); + u64 now = vlib_time_now (vnm->vlib_main); + u64 sess_timeout_time = 0; + + rmp = vl_msg_api_alloc (sizeof (*rmp)); + clib_memset (rmp, 0, sizeof (*rmp)); + rmp->_vl_msg_id = + ntohs (VL_API_NAT44_USER_SESSION_V2_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 (nat44_ed_is_session_static (s)) + rmp->flags |= NAT_API_IS_STATIC; + + if (nat44_ed_is_twice_nat_session (s)) + rmp->flags |= NAT_API_IS_TWICE_NAT; + + 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; + rmp->outside_port = s->out2in.port; + rmp->inside_port = s->in2out.port; + rmp->protocol = clib_host_to_net_u16 (s->proto); + clib_memcpy (rmp->ext_host_address, &s->ext_host_addr, 4); + rmp->ext_host_port = s->ext_host_port; + if (nat44_ed_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; + } + + sess_timeout_time = s->last_heard + nat44_session_get_timeout (sm, s); + rmp->is_timed_out = (now >= sess_timeout_time); + + vl_api_send_msg (reg, (u8 *) rmp); +} + +static void +send_nat44_user_session_v3_details (snat_session_t *s, + vl_api_registration_t *reg, u32 context) +{ + vl_api_nat44_user_session_v3_details_t *rmp; + snat_main_t *sm = &snat_main; + u64 now = vlib_time_now (vlib_get_main ()); + u64 sess_timeout_time = 0; + + rmp = vl_msg_api_alloc (sizeof (*rmp)); + clib_memset (rmp, 0, sizeof (*rmp)); + rmp->_vl_msg_id = + ntohs (VL_API_NAT44_USER_SESSION_V3_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 (nat44_ed_is_session_static (s)) + rmp->flags |= NAT_API_IS_STATIC; + + if (nat44_ed_is_twice_nat_session (s)) + rmp->flags |= NAT_API_IS_TWICE_NAT; + + rmp->flags |= NAT_API_IS_EXT_HOST_VALID; + + rmp->last_heard = clib_host_to_net_u64 ((u64) s->last_heard); + rmp->time_since_last_heard = + clib_host_to_net_u64 ((u64) (now - s->last_heard)); + rmp->total_bytes = clib_host_to_net_u64 (s->total_bytes); + rmp->total_pkts = ntohl (s->total_pkts); + rmp->context = context; + rmp->outside_port = s->out2in.port; + rmp->inside_port = s->in2out.port; + rmp->protocol = clib_host_to_net_u16 (s->proto); + clib_memcpy (rmp->ext_host_address, &s->ext_host_addr, 4); + rmp->ext_host_port = s->ext_host_port; + if (nat44_ed_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; + } + + sess_timeout_time = s->last_heard + nat44_session_get_timeout (sm, s); + rmp->is_timed_out = (now >= sess_timeout_time); + + vl_api_send_msg (reg, (u8 *) rmp); +} + +static void +vl_api_nat44_user_session_v2_dump_t_handler ( + vl_api_nat44_user_session_v2_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, + nat44_ed_get_in2out_worker_index (0, &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_v2_details (s, reg, mp->context); + } + } +} + +static void +vl_api_nat44_user_session_v3_dump_t_handler ( + vl_api_nat44_user_session_v3_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, + nat44_ed_get_in2out_worker_index (0, &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_v3_details (s, reg, mp->context); + } + } +} + /* API definitions */ #include <vnet/format_fns.h> #include <nat/nat44-ed/nat44_ed.api.c> diff --git a/src/plugins/nat/nat44-ed/nat44_ed_classify.c b/src/plugins/nat/nat44-ed/nat44_ed_classify.c index 5a9f4e42657..229cf3669e6 100644 --- a/src/plugins/nat/nat44-ed/nat44_ed_classify.c +++ b/src/plugins/nat/nat44-ed/nat44_ed_classify.c @@ -98,7 +98,6 @@ nat44_handoff_classify_node_fn_inline (vlib_main_t * vm, 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]; @@ -122,23 +121,19 @@ nat44_handoff_classify_node_fn_inline (vlib_main_t * vm, 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 = nat44_ed_sm_o2i_lookup (sm, ip0->dst_address, 0, 0, 0); + if (m) { - 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 = nat44_ed_sm_o2i_lookup ( + sm, ip0->dst_address, vnet_buffer (b0)->ip.reass.l4_dst_port, + 0, ip0->protocol); + if (m) { - 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; } @@ -202,7 +197,6 @@ nat44_ed_classify_node_fn_inline (vlib_main_t * vm, 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 */ @@ -227,11 +221,11 @@ nat44_ed_classify_node_fn_inline (vlib_main_t * vm, 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, + init_ed_k (&ed_kv0, ip0->src_address.as_u32, vnet_buffer (b0)->ip.reass.l4_src_port, - ip0->dst_address, - vnet_buffer (b0)->ip.reass.l4_dst_port, - rx_fib_index0, ip0->protocol); + ip0->dst_address.as_u32, + 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)) @@ -272,23 +266,19 @@ nat44_ed_classify_node_fn_inline (vlib_main_t * vm, 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 = nat44_ed_sm_o2i_lookup (sm, ip0->dst_address, 0, 0, 0); + if (m) { - 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 = nat44_ed_sm_o2i_lookup ( + sm, ip0->dst_address, vnet_buffer (b0)->ip.reass.l4_dst_port, + 0, ip0->protocol); + if (m) { - 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; } diff --git a/src/plugins/nat/nat44-ed/nat44_ed_cli.c b/src/plugins/nat/nat44-ed/nat44_ed_cli.c index acf9069af2b..14313d05a35 100644 --- a/src/plugins/nat/nat44-ed/nat44_ed_cli.c +++ b/src/plugins/nat/nat44-ed/nat44_ed_cli.c @@ -38,23 +38,15 @@ nat44_ed_enable_disable_command_fn (vlib_main_t *vm, unformat_input_t *input, clib_error_t *error = 0; nat44_config_t c = { 0 }; - u8 enable_set = 0, enable = 0, mode_set = 0; + u8 enable_set = 0, enable = 0; if (!unformat_user (input, unformat_line_input, line_input)) return clib_error_return (0, NAT44_ED_EXPECTED_ARGUMENT); 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)); + 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 if (!enable_set) @@ -116,7 +108,6 @@ set_workers_command_fn (vlib_main_t * vm, int rv = 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, NAT44_ED_EXPECTED_ARGUMENT); @@ -162,8 +153,8 @@ done: } static clib_error_t * -nat_show_workers_commnad_fn (vlib_main_t * vm, unformat_input_t * input, - vlib_cli_command_t * cmd) +nat_show_workers_command_fn (vlib_main_t *vm, unformat_input_t *input, + vlib_cli_command_t *cmd) { snat_main_t *sm = &snat_main; u32 *worker; @@ -189,10 +180,9 @@ snat_set_log_level_command_fn (vlib_main_t * vm, { unformat_input_t _line_input, *line_input = &_line_input; snat_main_t *sm = &snat_main; - u8 log_level = NAT_LOG_NONE; + u32 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 clib_error_return (0, NAT44_ED_EXPECTED_ARGUMENT); @@ -283,12 +273,7 @@ nat44_show_hash_command_fn (vlib_main_t * vm, unformat_input_t * input, 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); + 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", @@ -296,8 +281,7 @@ nat44_show_hash_command_fn (vlib_main_t * vm, unformat_input_t * input, 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, "%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); @@ -313,7 +297,6 @@ nat_set_mss_clamping_command_fn (vlib_main_t * vm, unformat_input_t * input, clib_error_t *error = 0; u32 mss; - /* Get a line of input. */ if (!unformat_user (input, unformat_line_input, line_input)) return clib_error_return (0, NAT44_ED_EXPECTED_ARGUMENT); @@ -356,7 +339,6 @@ 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; @@ -366,7 +348,6 @@ add_address_command_fn (vlib_main_t * vm, clib_error_t *error = 0; u8 twice_nat = 0; - /* Get a line of input. */ if (!unformat_user (input, unformat_line_input, line_input)) return clib_error_return (0, NAT44_ED_EXPECTED_ARGUMENT); @@ -392,12 +373,6 @@ add_address_command_fn (vlib_main_t * vm, } } - 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); @@ -419,9 +394,13 @@ add_address_command_fn (vlib_main_t * vm, for (i = 0; i < count; i++) { if (is_add) - rv = snat_add_address (sm, &this_addr, vrf_id, twice_nat); + { + rv = nat44_ed_add_address (&this_addr, vrf_id, twice_nat); + } else - rv = snat_del_address (sm, this_addr, 0, twice_nat); + { + rv = nat44_ed_del_address (this_addr, twice_nat); + } switch (rv) { @@ -495,15 +474,12 @@ nat44_show_summary_command_fn (vlib_main_t * vm, unformat_input_t * input, 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; + struct + { + u32 total; + u32 timed_out; + } udp = { 0 }, tcp = { 0 }, tcp_established = { 0 }, tcp_transitory = { 0 }, + icmp = { 0 }, other = { 0 }; u32 fib; @@ -517,45 +493,48 @@ nat44_show_summary_command_fn (vlib_main_t * vm, unformat_input_t * input, { 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); - } + sess_timeout_time = + s->last_heard + (f64) nat44_session_get_timeout (sm, s); + + switch (s->proto) + { + case IP_PROTOCOL_ICMP: + ++icmp.total; + if (now >= sess_timeout_time) + ++icmp.timed_out; + break; + case IP_PROTOCOL_TCP: + ++tcp.total; + if (now >= sess_timeout_time) + ++tcp.timed_out; + if (nat44_ed_tcp_is_established (s->tcp_state)) + { + ++tcp_established.total; + if (now >= sess_timeout_time) + ++tcp_established.timed_out; + } + else + { + ++tcp_transitory.total; + if (now >= sess_timeout_time) + ++tcp_transitory.timed_out; + } + break; + case IP_PROTOCOL_UDP: + ++udp.total; + if (now >= sess_timeout_time) + ++udp.timed_out; + break; + default: + ++other.total; + if (now >= sess_timeout_time) + ++other.timed_out; + break; + } + } + nat44_show_lru_summary (vm, tsm, now, sess_timeout_time); + count += pool_elts (tsm->sessions); + } } else { @@ -564,55 +543,66 @@ nat44_show_summary_command_fn (vlib_main_t * vm, unformat_input_t * input, { 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; - } + + switch (s->proto) + { + case IP_PROTOCOL_ICMP: + ++icmp.total; + if (now >= sess_timeout_time) + ++icmp.timed_out; + break; + case IP_PROTOCOL_TCP: + ++tcp.total; + if (now >= sess_timeout_time) + ++tcp.timed_out; + if (nat44_ed_tcp_is_established (s->tcp_state)) + { + ++tcp_established.total; + if (now >= sess_timeout_time) + ++tcp_established.timed_out; + } + else + { + ++tcp_transitory.total; + if (now >= sess_timeout_time) + ++tcp_transitory.timed_out; + } + break; + case IP_PROTOCOL_UDP: + ++udp.total; + if (now >= sess_timeout_time) + ++udp.timed_out; + break; + default: + ++other.total; + if (now >= sess_timeout_time) + ++other.timed_out; + 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); + u32 timed_out = + tcp.timed_out + icmp.timed_out + udp.timed_out + other.timed_out; + vlib_cli_output (vm, "total sessions: %u (timed out: %u)", count, timed_out); + vlib_cli_output (vm, "tcp sessions:"); + vlib_cli_output (vm, " total: %u (timed out: %u)", tcp.total, + tcp.timed_out); + vlib_cli_output (vm, " established: %u (timed out: %u)", + tcp_established.total, tcp_established.timed_out); + vlib_cli_output (vm, " transitory: %u (timed out: %u)", + tcp_transitory.total, tcp_transitory.timed_out); + vlib_cli_output (vm, "udp sessions:"); + vlib_cli_output (vm, " total: %u (timed out: %u)", udp.total, + udp.timed_out); + vlib_cli_output (vm, "icmp sessions:"); + vlib_cli_output (vm, " total: %u (timed out: %u)", icmp.total, + icmp.timed_out); + vlib_cli_output (vm, "other sessions:"); + vlib_cli_output (vm, " total: %u (timed out: %u)", other.total, + other.timed_out); return 0; } @@ -628,14 +618,14 @@ nat44_show_addresses_command_fn (vlib_main_t * vm, unformat_input_t * input, { 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); + 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 _ + + if (ap->addr_len != ~0) + vlib_cli_output (vm, " synced with interface address"); } vlib_cli_output (vm, "NAT44 twice-nat pool addresses:"); vec_foreach (ap, sm->twice_nat_addresses) @@ -646,10 +636,9 @@ nat44_show_addresses_command_fn (vlib_main_t * vm, unformat_input_t * input, 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 _ + + if (ap->addr_len != ~0) + vlib_cli_output (vm, " synced with interface address"); } return 0; } @@ -669,7 +658,6 @@ snat_feature_command_fn (vlib_main_t * vm, sw_if_index = ~0; - /* Get a line of input. */ if (!unformat_user (input, unformat_line_input, line_input)) return clib_error_return (0, NAT44_ED_EXPECTED_ARGUMENT); @@ -804,21 +792,22 @@ nat44_show_interfaces_command_fn (vlib_main_t * vm, unformat_input_t * input, 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")); + vlib_cli_output (vm, " %U %s", format_vnet_sw_if_index_name, vnm, + i->sw_if_index, + (nat44_ed_is_interface_inside (i) && + nat44_ed_is_interface_outside (i)) ? + "in out" : + (nat44_ed_is_interface_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")); + vlib_cli_output (vm, " %U output-feature %s", + format_vnet_sw_if_index_name, vnm, i->sw_if_index, + (nat44_ed_is_interface_inside (i) && + nat44_ed_is_interface_outside (i)) ? + "in out" : + (nat44_ed_is_interface_inside (i) ? "in" : "out")); } return 0; @@ -832,14 +821,13 @@ add_static_mapping_command_fn (vlib_main_t * vm, unformat_input_t _line_input, *line_input = &_line_input; vnet_main_t *vnm = vnet_get_main (); clib_error_t *error = 0; - int rv; - - nat_protocol_t proto = NAT_PROTOCOL_OTHER; ip4_address_t l_addr, e_addr, pool_addr; u32 l_port = 0, e_port = 0, vrf_id = ~0; u8 l_port_set = 0, e_port_set = 0; - u32 sw_if_index, flags = 0; - int is_add = 1; + int is_add = 1, rv; + u32 flags = 0; + u32 sw_if_index = ~0; + ip_protocol_t proto = 0; if (!unformat_user (input, unformat_line_input, line_input)) return clib_error_return (0, NAT44_ED_EXPECTED_ARGUMENT); @@ -881,7 +869,7 @@ add_static_mapping_command_fn (vlib_main_t * vm, } else if (unformat (line_input, "vrf %u", &vrf_id)) ; - else if (unformat (line_input, "%U", unformat_nat_protocol, &proto)) + else if (unformat (line_input, "%U", unformat_ip_protocol, &proto)) ; else if (unformat (line_input, "self-twice-nat")) { @@ -923,8 +911,6 @@ add_static_mapping_command_fn (vlib_main_t * vm, e_port = clib_host_to_net_u16 (e_port); } - // TODO: specific pool_addr for both pool & twice nat pool ? - if (is_add) { rv = @@ -937,25 +923,17 @@ add_static_mapping_command_fn (vlib_main_t * vm, vrf_id, sw_if_index, flags); } - // TODO: fix returns - switch (rv) { - case VNET_API_ERROR_INVALID_VALUE: - error = clib_error_return (0, "External port already in use."); - goto done; + case VNET_API_ERROR_UNSUPPORTED: + error = clib_error_return (0, "Plugin disabled."); + break; 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; + error = clib_error_return (0, "Mapping not exist."); + break; case VNET_API_ERROR_VALUE_EXIST: error = clib_error_return (0, "Mapping already exist."); - goto done; + break; default: break; } @@ -966,7 +944,6 @@ done: return error; } -// TODO: either delete this bullshit or update it static clib_error_t * add_identity_mapping_command_fn (vlib_main_t * vm, unformat_input_t * input, @@ -978,12 +955,11 @@ add_identity_mapping_command_fn (vlib_main_t * vm, int rv, is_add = 1, port_set = 0; u32 sw_if_index, port, flags, vrf_id = ~0; - nat_protocol_t proto; + ip_protocol_t proto = 0; ip4_address_t addr; flags = NAT_SM_FLAG_IDENTITY_NAT; - /* Get a line of input. */ if (!unformat_user (input, unformat_line_input, line_input)) return clib_error_return (0, NAT44_ED_EXPECTED_ARGUMENT); @@ -998,7 +974,7 @@ add_identity_mapping_command_fn (vlib_main_t * vm, } else if (unformat (line_input, "vrf %u", &vrf_id)) ; - else if (unformat (line_input, "%U %u", unformat_nat_protocol, &proto, + else if (unformat (line_input, "%U %u", unformat_ip_protocol, &proto, &port)) { port_set = 1; @@ -1036,25 +1012,17 @@ add_identity_mapping_command_fn (vlib_main_t * vm, sw_if_index, flags); } - // TODO: fix returns - switch (rv) { - case VNET_API_ERROR_INVALID_VALUE: - error = clib_error_return (0, "External port already in use."); - goto done; + case VNET_API_ERROR_UNSUPPORTED: + error = clib_error_return (0, "Plugin disabled."); + break; 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; + error = clib_error_return (0, "Mapping not exist."); + break; case VNET_API_ERROR_VALUE_EXIST: error = clib_error_return (0, "Mapping already exist."); - goto done; + break; default: break; } @@ -1075,12 +1043,11 @@ add_lb_static_mapping_command_fn (vlib_main_t * vm, ip4_address_t l_addr, e_addr; u32 l_port = 0, e_port = 0, vrf_id = 0, probability = 0, affinity = 0; u8 proto_set = 0; - nat_protocol_t proto; + ip_protocol_t proto; nat44_lb_addr_port_t *locals = 0, local; int rv, is_add = 1; u32 flags = 0; - /* Get a line of input. */ if (!unformat_user (input, unformat_line_input, line_input)) return clib_error_return (0, NAT44_ED_EXPECTED_ARGUMENT); @@ -1091,6 +1058,7 @@ add_lb_static_mapping_command_fn (vlib_main_t * vm, { clib_memset (&local, 0, sizeof (local)); local.addr = l_addr; + l_port = clib_host_to_net_u16 (l_port); local.port = (u16) l_port; local.probability = (u8) probability; vec_add1 (locals, local); @@ -1101,6 +1069,7 @@ add_lb_static_mapping_command_fn (vlib_main_t * vm, { clib_memset (&local, 0, sizeof (local)); local.addr = l_addr; + l_port = clib_host_to_net_u16 (l_port); local.port = (u16) l_port; local.probability = (u8) probability; local.vrf_id = vrf_id; @@ -1108,8 +1077,10 @@ add_lb_static_mapping_command_fn (vlib_main_t * vm, } 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, + { + e_port = clib_host_to_net_u16 (e_port); + } + else if (unformat (line_input, "protocol %U", unformat_ip_protocol, &proto)) { proto_set = 1; @@ -1197,10 +1168,9 @@ add_lb_backend_command_fn (vlib_main_t * vm, u32 l_port = 0, e_port = 0, vrf_id = 0, probability = 0; int is_add = 1; int rv; - nat_protocol_t proto; + ip_protocol_t proto; u8 proto_set = 0; - /* Get a line of input. */ if (!unformat_user (input, unformat_line_input, line_input)) return clib_error_return (0, NAT44_ED_EXPECTED_ARGUMENT); @@ -1216,7 +1186,7 @@ add_lb_backend_command_fn (vlib_main_t * vm, 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, + else if (unformat (line_input, "protocol %U", unformat_ip_protocol, &proto)) proto_set = 1; else if (unformat (line_input, "del")) @@ -1276,14 +1246,14 @@ nat44_show_static_mappings_command_fn (vlib_main_t * vm, { snat_main_t *sm = &snat_main; snat_static_mapping_t *m; - snat_static_map_resolve_t *rp; + snat_static_mapping_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) + vec_foreach (rp, sm->sm_to_resolve) vlib_cli_output (vm, " %U", format_snat_static_map_to_resolve, rp); return 0; @@ -1294,27 +1264,31 @@ 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; + vnet_main_t *vnm = vnet_get_main (); clib_error_t *error = 0; + int rv, is_del = 0; u8 twice_nat = 0; + u32 sw_if_index; + + sw_if_index = ~0; - /* Get a line of input. */ if (!unformat_user (input, unformat_line_input, line_input)) return clib_error_return (0, NAT44_ED_EXPECTED_ARGUMENT); 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)) + if (unformat (line_input, "%U", unformat_vnet_sw_interface, vnm, + &sw_if_index)) ; else if (unformat (line_input, "twice-nat")) - twice_nat = 1; + { + twice_nat = 1; + } else if (unformat (line_input, "del")) - is_del = 1; + { + is_del = 1; + } else { error = clib_error_return (0, "unknown input '%U'", @@ -1323,19 +1297,86 @@ snat_add_interface_address_command_fn (vlib_main_t * vm, } } - rv = snat_add_interface_address (sm, sw_if_index, is_del, twice_nat); + if (is_del) + { + rv = nat44_ed_del_interface_address (sw_if_index, twice_nat); + } + else + { + rv = nat44_ed_add_interface_address (sw_if_index, twice_nat); + } - switch (rv) + if (0 != rv) { - case 0: - break; + error = + clib_error_return (0, "%s %U address failed", is_del ? "del" : "add", + format_vnet_sw_if_index_name, vnm, sw_if_index); + goto done; + } - default: - error = clib_error_return (0, "snat_add_interface_address returned %d", - rv); +done: + unformat_free (line_input); + + return error; +} + +static clib_error_t * +nat44_ed_add_del_vrf_table_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; + bool is_add = true, not_set = true; + u32 vrf_id = ~0; + int rv; + + if (!unformat_user (input, unformat_line_input, line_input)) + return clib_error_return (0, NAT44_ED_EXPECTED_ARGUMENT); + + while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT) + { + if (unformat (line_input, "%u", &vrf_id)) + ; + else if (not_set) + { + if (unformat (line_input, "add")) + { + is_add = true; + } + else if (unformat (line_input, "del")) + { + is_add = false; + } + not_set = false; + } + else + { + error = clib_error_return (0, "unknown input '%U'", + format_unformat_error, line_input); + goto done; + } + } + + if (not_set) + { + error = clib_error_return (0, "missing required parameter"); + goto done; + } + + if (~0 == vrf_id) + { + error = clib_error_return (0, "missing vrf id"); goto done; } + rv = nat44_ed_add_del_vrf_table (vrf_id, is_add); + if (rv) + { + error = clib_error_return (0, "%s vrf table returned %d", + is_add ? "add" : "del", rv); + } + done: unformat_free (line_input); @@ -1343,27 +1384,107 @@ done: } static clib_error_t * +nat44_ed_add_del_vrf_route_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; + bool is_add = true, not_set = true; + u32 vrf_id = ~0, table_vrf_id = ~0; + int rv; + + if (!unformat_user (input, unformat_line_input, line_input)) + return clib_error_return (0, NAT44_ED_EXPECTED_ARGUMENT); + + while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT) + { + if (unformat (line_input, "table %u", &table_vrf_id)) + ; + else if (unformat (line_input, "%u", &vrf_id)) + ; + else if (not_set) + { + if (unformat (line_input, "add")) + { + is_add = true; + } + else if (unformat (line_input, "del")) + { + is_add = false; + } + not_set = false; + } + else + { + error = clib_error_return (0, "unknown input '%U'", + format_unformat_error, line_input); + goto done; + } + } + + if (not_set) + { + error = clib_error_return (0, "missing required parameter"); + goto done; + } + + if ((~0 == vrf_id) || (~0 == table_vrf_id)) + { + error = clib_error_return (0, "missing vrf id"); + goto done; + } + + rv = nat44_ed_add_del_vrf_route (table_vrf_id, vrf_id, is_add); + if (rv) + { + error = clib_error_return (0, "%s vrf table returned %d", + is_add ? "add" : "del", rv); + } + +done: + unformat_free (line_input); + + return error; +} + +static clib_error_t * +nat44_ed_show_vrf_tables_command_fn (vlib_main_t *vm, unformat_input_t *input, + vlib_cli_command_t *cmd) +{ + snat_main_t *sm = &snat_main; + vrf_table_t *t; + vrf_route_t *r; + int i = 0; + + pool_foreach (t, sm->vrf_tables) + { + vlib_cli_output (vm, "table %u:", t->table_vrf_id); + pool_foreach (r, t->routes) + { + vlib_cli_output (vm, "[%u] vrf-id %u", i, r->vrf_id); + i++; + } + } + + return 0; +} + +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; + snat_address_resolve_t *ap; 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) + vec_foreach (ap, sm->addr_to_resolve) { - vlib_cli_output (vm, " %U", format_vnet_sw_if_index_name, vnm, - *sw_if_index); + vlib_cli_output (vm, " %U%s", format_vnet_sw_if_index_name, vnm, + ap->sw_if_index, ap->is_twice_nat ? " twice-nat" : ""); } - return 0; } @@ -1375,22 +1496,61 @@ nat44_show_sessions_command_fn (vlib_main_t * vm, unformat_input_t * input, clib_error_t *error = 0; snat_main_per_thread_data_t *tsm; snat_main_t *sm = &snat_main; - - int i = 0; + ip4_address_t i2o_sa, i2o_da, o2i_sa, o2i_da; + u8 filter_i2o_sa = 0, filter_i2o_da = 0; + u8 filter_o2i_sa = 0, filter_o2i_da = 0; + u16 i2o_sp, i2o_dp, o2i_sp, o2i_dp; + u8 filter_i2o_sp = 0, filter_i2o_dp = 0; + u8 filter_o2i_sp = 0, filter_o2i_dp = 0; + ip_protocol_t proto; + u8 filter_proto = 0; + u8 had_input = 1, filtering = 0; + int i = 0, showed_sessions; if (!unformat_user (input, unformat_line_input, line_input)) - goto print; + { + had_input = 0; + 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; + if (unformat (line_input, "filter i2o saddr %U", unformat_ip4_address, + &i2o_sa)) + filter_i2o_sa = filtering = 1; + else if (unformat (line_input, "filter i2o daddr %U", + unformat_ip4_address, &i2o_da)) + filter_i2o_da = filtering = 1; + else if (unformat (line_input, "filter o2i saddr %U", + unformat_ip4_address, &o2i_sa)) + filter_o2i_sa = filtering = 1; + else if (unformat (line_input, "filter o2i daddr %U", + unformat_ip4_address, &o2i_da)) + filter_o2i_da = filtering = 1; + else if (unformat (line_input, "filter i2o sport %u", &i2o_sp)) + filter_i2o_sp = filtering = 1; + else if (unformat (line_input, "filter i2o dport %u", &i2o_dp)) + filter_i2o_dp = filtering = 1; + else if (unformat (line_input, "filter o2i sport %u", &o2i_sp)) + filter_o2i_sp = filtering = 1; + else if (unformat (line_input, "filter o2i dport %u", &o2i_dp)) + filter_o2i_dp = filtering = 1; + else if (unformat (line_input, "filter i2o proto %U", + unformat_ip_protocol, &proto)) + filter_proto = filtering = 1; + else if (unformat (line_input, "filter o2i proto %U", + unformat_ip_protocol, &proto)) + filter_proto = filtering = 1; + else + { + error = clib_error_return (0, "unknown input '%U'", + format_unformat_error, line_input); + goto done; + } } - unformat_free (line_input); print: - vlib_cli_output (vm, "NAT44 ED sessions:"); + vlib_cli_output (vm, "NAT44 ED sessions:"); vec_foreach_index (i, sm->per_thread_data) { @@ -1400,12 +1560,53 @@ print: 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); - } + showed_sessions = 0; + snat_session_t *s; + pool_foreach (s, tsm->sessions) + { + if (filtering) + { + if (filter_i2o_sa && i2o_sa.as_u32 != s->i2o.match.saddr.as_u32) + continue; + if (filter_i2o_da && i2o_da.as_u32 != s->i2o.match.daddr.as_u32) + continue; + if (filter_o2i_sa && o2i_sa.as_u32 != s->o2i.match.saddr.as_u32) + continue; + if (filter_o2i_da && o2i_da.as_u32 != s->o2i.match.daddr.as_u32) + continue; + if (filter_i2o_sp && + i2o_sp != clib_net_to_host_u16 (s->i2o.match.sport)) + continue; + if (filter_i2o_dp && + i2o_dp != clib_net_to_host_u16 (s->i2o.match.dport)) + continue; + if (filter_o2i_sp && + o2i_sp != clib_net_to_host_u16 (s->o2i.match.sport)) + continue; + if (filter_o2i_dp && + o2i_dp != clib_net_to_host_u16 (s->o2i.match.dport)) + continue; + if (filter_proto && proto != s->proto) + continue; + showed_sessions++; + } + vlib_cli_output (vm, " %U\n", format_snat_session, sm, tsm, s, + vlib_time_now (vm)); + } + if (filtering) + { + vlib_cli_output (vm, + "Showed: %d, Filtered: %d of total %d " + "sessions of thread %d\n\n", + showed_sessions, + pool_elts (tsm->sessions) - showed_sessions, + pool_elts (tsm->sessions), i); + } } + +done: + if (had_input) + unformat_free (line_input); return error; } @@ -1457,7 +1658,7 @@ nat44_del_session_command_fn (vlib_main_t * vm, 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; + ip_protocol_t proto; int is_in = 0; int rv; @@ -1466,9 +1667,8 @@ nat44_del_session_command_fn (vlib_main_t * vm, 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)) + if (unformat (line_input, "%U:%u %U", unformat_ip4_address, &addr, &port, + unformat_ip_protocol, &proto)) ; else if (unformat (line_input, "in")) { @@ -1493,10 +1693,9 @@ nat44_del_session_command_fn (vlib_main_t * vm, } } - 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); + rv = nat44_ed_del_session (sm, &addr, clib_host_to_net_u16 (port), &eh_addr, + clib_host_to_net_u16 (eh_port), proto, vrf_id, + is_in); switch (rv) { @@ -1648,21 +1847,19 @@ done: * @cliexstart{nat44} * Enable nat44 plugin * To enable nat44-ed, use: - * vpp# nat44 enable + * vpp# nat44 plugin enable * To disable nat44-ed, use: - * vpp# nat44 disable - * To enable nat44-ed static mapping with connection tracking, use: - * vpp# nat44-ed enable static-mapping connection-tracking + * vpp# nat44 plugin disable * To set inside-vrf outside-vrf, use: - * vpp# nat44 enable inside-vrf <id> outside-vrf <id> + * vpp# nat44 plugin enable inside-vrf <id> outside-vrf <id> * @cliexend ?*/ VLIB_CLI_COMMAND (nat44_ed_enable_disable_command, static) = { - .path = "nat44", - .short_help = "nat44 <enable [sessions <max-number>] [static-mapping-only " - "connection-tracking] [inside-vrf <vrf-id>] " - "[outside-vrf <vrf-id>]>|disable", + .path = "nat44 plugin", .function = nat44_ed_enable_disable_command_fn, + .short_help = + "nat44 plugin <enable [sessions <max-number>] [inside-vrf <vrf-id>] " + "[outside-vrf <vrf-id>]>|disable", }; /*? @@ -1691,7 +1888,7 @@ VLIB_CLI_COMMAND (set_workers_command, static) = { VLIB_CLI_COMMAND (nat_show_workers_command, static) = { .path = "show nat workers", .short_help = "show nat workers", - .function = nat_show_workers_commnad_fn, + .function = nat_show_workers_command_fn, }; /*? @@ -2019,9 +2216,48 @@ VLIB_CLI_COMMAND (nat44_show_static_mappings_command, static) = { * @cliexend ?*/ VLIB_CLI_COMMAND (snat_add_interface_address_command, static) = { - .path = "nat44 add interface address", - .short_help = "nat44 add interface address <interface> [twice-nat] [del]", - .function = snat_add_interface_address_command_fn, + .path = "nat44 add interface address", + .function = snat_add_interface_address_command_fn, + .short_help = "nat44 add interface address <interface> [twice-nat] [del]", +}; + +/*? + * @cliexpar + * @cliexstart{nat44 vrf table} + * Add empty inter VRF routing table + * vpp# nat44 vrf table add 10 + * @cliexend +?*/ +VLIB_CLI_COMMAND (nat44_ed_add_del_vrf_table_command, static) = { + .path = "nat44 vrf table", + .short_help = "nat44 vrf table [add|del] <vrf-id>", + .function = nat44_ed_add_del_vrf_table_command_fn, +}; + +/*? + * @cliexpar + * @cliexstart{nat44 vrf route} + * Add inter VRF route record to VRF routing table + * vpp# nat44 vrf route add table 10 20 + * @cliexend +?*/ +VLIB_CLI_COMMAND (nat44_ed_add_del_vrf_route_command, static) = { + .path = "nat44 vrf route", + .short_help = "nat44 vrf route [add|del] table <vrf-id> <vrf-id>", + .function = nat44_ed_add_del_vrf_route_command_fn, +}; + +/*? + * @cliexpar + * @cliexstart{show nat44 vrf tables} + * Show inter VRF route tables + * vpp# show nat44 vrf tables + * @cliexend +?*/ +VLIB_CLI_COMMAND (nat44_ed_show_vrf_tables_command, static) = { + .path = "show nat44 vrf tables", + .short_help = "show nat44 vrf tables", + .function = nat44_ed_show_vrf_tables_command_fn, }; /*? @@ -2049,7 +2285,9 @@ VLIB_CLI_COMMAND (nat44_show_interface_address_command, static) = { ?*/ VLIB_CLI_COMMAND (nat44_show_sessions_command, static) = { .path = "show nat44 sessions", - .short_help = "show nat44 sessions", + .short_help = "show nat44 sessions [filter {i2o | o2i} {saddr <ip4-addr> " + "| sport <n> | daddr <ip4-addr> | dport <n> | proto <proto>} " + "[filter .. [..]]]", .function = nat44_show_sessions_command_fn, }; diff --git a/src/plugins/nat/nat44-ed/nat44_ed_doc.rst b/src/plugins/nat/nat44-ed/nat44_ed_doc.rst new file mode 100644 index 00000000000..a6c461d4260 --- /dev/null +++ b/src/plugins/nat/nat44-ed/nat44_ed_doc.rst @@ -0,0 +1,729 @@ +.. _NAT44_Endpoint_Dependent: + +.. toctree:: + +NAT44-ED: NAT44 Endpoint Dependent +================================== + +Introduction +------------ + +NAT44-ED is the IPv4 endpoint dependent network address translation +plugin. The component implements an address and port-dependent mapping +and address and port-dependent filtering NAT as described in +`RFC4787 <https://tools.ietf.org/html/rfc4787>`__. + +The outside address and port (X1’:x1’) is reused for internal hosts +(X:x) for different values of Y:y. A flow is matched by {source address, +destination address, protocol, transport source port, transport +destination port, fib index}. As long as all these are unique the +mapping is valid. While a single outside address in theory allows for +2^16 source ports \* 2^32 destination IP addresses \* 2^16 destination +ports = 2^64 sessions, this number is much smaller in practice. Few +destination ports are generally used (80, 443) and a fraction of the IP +address space is available. The limitation is 2^16 bindings per outside +IP address to a single destination address and port (Y:y). + +The implementation is split, a control-plane / slow-path and a +data-plane / fast-path. Essentially acting as a flow router. The +data-plane does a 6-tuple flow lookup (SA, DA, P, SP, DP, FIB) and on a +match runs the per-flow packet handling instructions on the packet. On a +flow lookup miss, the packet is punted to the slow-path, where depending +on policy new sessions are created. + +The support set of packet handling instructions is ever-increasing. +Currently, the implementation supports rewrite of SA, DA, SP, DP and TCP +MSS. The fast-path also does connection tracking and expiry of older +sessions. + +NAT44-ED uses 6 +tuple\ ``(src address, src port, dst address, dst port, protocol and fib)``\ for +matching communication. + +Structure +~~~~~~~~~ + +1) Dynamic NAT + +- also called PAT (Port Address Translation) +- supports port overloading + +2) Static NAT + +- types of Static NAT: + + a) identity mapping + + - exceptions to translations + + b) static mapping + + - supported features: + + 1. address only mapping + + - one to one translation without ports + + 2. twice-nat + + - double-nat, translation of source and destination + + 3. self-twice-nat + + - double nat, translation of source and destination, where + external host address is the same as local host address + + 4. out2in-only mapping + + - session is created only from outside interface (out2in feature) + + c) load balanced static mapping + + - translates one frontend (``addr``:``port``) to multiple backends + (``addr``:``port``) + +3) Interfaces + +a) inside interface (in2out feature) - local to external network + translation - feature is before ip4-lookup +b) outside interface (out2in feature) - external to local network + translation - feature is before ip4-lookup +c) inside & outside interface (classify feature) - local or external + network translation - correct type of translation is determined per + communication - feature is before ip4-lookup +d) output interface (output feature) - used for post routing translation + - feature is after ip4-lookup + +4) Addresses + +a) interface address - automatically managed external address - first + address of VPP interface +b) pool address - range of external addresses + +5) Logging and Accounting + +a) ipfix logging +b) syslog + +6) Miscellaneous Features + +a) inter-vrf translation control 1. basic + + - nat44 plugin enable inside-vrf / outside-vrf + - inside/outside interface vrf’s + + 2. advanced + + - vrf table routing feature + +b) udp/tcp/icmp timeouts - configurable timeouts for these protocols +c) session limiting 1. basic (plugin enable [sessions ] 2. advanced + (per vrf table / global limiting) +d) mss-clamping - MSS (maximum segment size) is by default determined by + egress interface MTU (maximum transmission unit) size - used to lower + MSS value in VPN tunnel scenarios where additional headers can + enlarge the packet beyond MTU causing drops +e) hairpinning - hosts on the same lan segment communicating via + external address +f) forwarding - if enabled translation only occurs if active session or + static configuration exist, rest of the traffic is passed without + being translated + +Session Table +------------- + +Session table exists per thread and contains pool of sessions that can +be either expired or not expired. NAT44-ED plugin doesn’t use scavenging +for clearing expired sessions. Rather then using scavenging plugin uses +LRU doubly-linked list. LRU contains ordered list of sessions indices. +Head of the list contains last updated session. Each session holds +record of the LRU head (tcp transitory, tcp established, udp, icmp or +unknown lru head). Because of this plugin can reach maximum number of +sessions without requirement to clear old sessions. During session +creation if a maximum number of sessions was reached LRU head is +checked. Expired head record gets deleted and a new session gets +created. For better performance LRU head records exist. Each time a new +packet is received session index gets moved to the tail of LRU list. + +Terminology +----------- + +IN2OUT (inside to outside translation) OUT2IN (outside to inside +translation) + +NAT (network address translation) PAT (port address translation) MSS +(maximum segment size) MTU (maximum transmission unit) VRF (virtual +routing and forwarding) + +HAIRPINNING + +Dynamic NAT (Minimal Required Configuration) +-------------------------------------------- + +:: + + +-------------+ + | 10.0.0.0/24 | + +-------------+ + | + +----------------------+ + | GigabitEthernet0/8/0 | + +----------------------+ + +----------------------+ + | GigabitEthernet0/a/0 | + +----------------------+ + | + +-------------+ + | 10.0.1.0/24 | + +-------------+ + +1) enable nat plugin + +.. + + nat44 plugin enable sessions 10000 + +2) configure NAT interfaces, two options: + +a) add inside NAT interface on local VPP interface, add outside NAT + interface on external VPP interface + +.. + + set interface nat44 in GigabitEthernet0/8/0 out GigabitEthernet0/a/0 + +b) add output NAT interface on external VPP interface + +.. + + set interface nat44 in GigabitEthernet0/a/0 output-feature + +3) configure NAT address + +a) add external address range + +.. + + nat44 add address 10.0.1.1 + +b) add external VPP interface address + +.. + + nat44 add interface address GigabitEthernet0/a/0 + +Static NAT +---------- + +Identity Mapping +~~~~~~~~~~~~~~~~ + + nat44 add identity mapping ``ip4-addr``\ \|external ``interface`` + [``protocol`` ``port``] [vrf ``table-id``] [del] + +Static Mapping +~~~~~~~~~~~~~~ + + nat44 add static mapping tcp|udp|icmp local ``addr`` + [``port|icmp-echo-id``] external ``addr`` [``port|icmp-echo-id``] + [vrf ``table-id``] [twice-nat|self-twice-nat] [out2in-only] [exact + ``pool-addr``] [del] + +Load Balanced Static Mapping +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + nat44 add load-balancing back-end protocol tcp|udp external + ``addr``:``port`` local ``addr``:``port`` [vrf ``table-id``] + probability ``n`` [del] + +.. + + nat44 add load-balancing static mapping protocol tcp|udp external + ``addr``:``port`` local ``addr``:``port`` [vrf ``table-id``] + probability ``n`` [twice-nat|self-twice-nat] [out2in-only] [affinity + ``timeout-seconds``] [del] + +Interfaces +---------- + +Inside Interface +~~~~~~~~~~~~~~~~ + +:: + + NAT INSIDE IF + +----------------------+ + | GigabitEthernet0/8/0 | + +----------------------+ + +.. + + set interface nat44 in GigabitEthernet0/8/0 [del] + +NAT inside interface is used for translating local to external +communication. Translates Dynamic and Static NAT traffic. If no matching +session is found a new session is created for both Dynamic NAT and +Static NAT. Dynamic NAT sessions can get created only on inside +interface. + +Outside Interface +~~~~~~~~~~~~~~~~~ + +:: + + NAT OUTSIDE IF + +----------------------+ + | GigabitEthernet0/a/0 | + +----------------------+ + +.. + + set interface nat44 out GigabitEthernet0/a/0 [del] + +NAT outside interface is used for translating external to local +communication. Translates Dynamic and Static NAT traffic. New session +gets created only if no matching session is found and matching Static +NAT configuration exists. + +Inside & Outside Interface +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +:: + + NAT IN AND OUT IF + +----------------------+ + | GigabitEthernet0/8/0 | + +----------------------+ + NAT IN AND OUT IF + +----------------------+ + | GigabitEthernet0/a/0 | + +----------------------+ + +.. + + set interface nat44 in GigabitEthernet0/8/0 out GigabitEthernet0/8/0 + [del] + + set interface nat44 in GigabitEthernet0/a/0 out GigabitEthernet0/a/0 + [del] + +If one VPP interface is configured both as inside and outside NAT +interface then classification feature is used. By default NAT inside +interface uses in2out feature and NAT outside uses out2in feature. +Classification feature determines if the communication should be passed +to in2out feature or to out2in feature. Traffic will get passed to +out2in feature if destination address is one of NAT addresses or a +static mapping in out2in direction flow matches this communication. By +default all traffic is passed to in2out feature. + +Output Interface +~~~~~~~~~~~~~~~~ + +:: + + +-------------+ +-------------+ + | 10.0.2.0/24 | | 10.0.3.0/24 | + +-------------+ +-------------+ + | | + +----------------------+ +----------------------+ + | GigabitEthernet0/7/0 | | GigabitEthernet0/8/0 | + +----------------------+ +----------------------+ + NAT OUTPUT IF + +----------------------+ + | GigabitEthernet0/a/0 | + +----------------------+ + +----------+ + | 10.0.1.1 | + +----------+ + | + +-------------+ + | 10.0.1.0/24 | + +-------------+ + +.. + + set interface nat44 in GigabitEthernet0/a/0 output-feature [del] + +NAT output interface acts as both inside and outside interfaces. Inside +rules apply for all egress communication on VPP interface and outside +rules apply for all ingress communication. Compared to inside/outside +NAT configuration method non of the local interfaces require to be +configured as inside NAT interfaces. Translation only occurs after +routing decision has been made and just before leaving NAT output +interface. In above example all traffic destined for 10.0.1.0/24 from +10.0.2.0/24 or 10.0.3.0/24 will get translated. NAT output interface +acts as post-routing feature. + +Addresses +--------- + +Interface Address +~~~~~~~~~~~~~~~~~ + + nat44 add interface address ``interface`` `twice-nat <#twice-nat>`__ + [del] + +NAT interface address is a standard external pool address that gets auto +added upon resolving first VPP interface address. Supports both standard +address and twice-nat address. Twice-nat address is used in conjunction +with static mapping twice-nat and self-twice-nat feature. + +Pool Address +~~~~~~~~~~~~ + + nat44 add address ``ip4-range-start`` [- ``ip4-range-end``] + [tenant-vrf ``vrf-id``] `twice-nat <#twice-nat>`__ [del] + +Statically configured address or range of addresses that supports both +standard and twice-nat address. Specifying vrf-id lets user assign +address/addresses to specific NAT inside interfaces that belong to the +same vrf table. + +Logging +------- + + nat set logging level ``level`` + +Configuration of logging level is used only for internal VPP logging. + + nat ipfix logging [domain ``domain-id``] [src-port ``port``] + [disable] + +Both syslog and ipfix support connection tracking capabilities. Session +creation, session deletion, maximum sessions exceeded among other things +are logged by syslog and ipfix. + +Miscellaneous +------------- + +VRFs +~~~~ + +:: + + VRF 0 VRF 1 + +-------------+ +-------------+ + | 10.0.2.0/24 | | 10.0.3.0/24 | + +-------------+ +-------------+ + | | + NAT INSIDE IF NAT INSIDE IF + +----------------------+ +----------------------+ + | GigabitEthernet0/7/0 | | GigabitEthernet0/8/0 | + +----------------------+ +----------------------+ + NAT OUTSIDE IF NAT OUTSIDE IF + +----------------------+ +----------------------+ + | GigabitEthernet0/a/0 | | GigabitEthernet0/b/0 | + +----------------------+ +----------------------+ + VRF 2 VRF 3 + | | + +--------------------------+ + | + +------------+------------+------------+ + | | | | + +----------+ +----------+ +----------+ +----------+ + | 10.0.0.1 | | 10.0.0.2 | | 10.0.1.1 | | 10.0.1.2 | + +----------+ +----------+ +----------+ +----------+ + VRF 0 POOL VRF 1 POOL VRF 0 POOL VRF 1 POOL + +.. + + nat44 add address ``ip4-addr`` [tenant-vrf ``vrf-id``] [del] + + nat44 plugin enable inside-vrf ``vrf-id`` outside-vrf ``vrf-id`` + [disable]", + +Default behavior +^^^^^^^^^^^^^^^^ + +By design NAT supports passing communication between VRFs. Passing +communication between multiple different VRFs is also supported (GE0/7/0 +-> GE0/b/0, GE0/8/0 -> GE0/a/0). + +NAT pool address tenant-vrf configuration parameter is used to constrain +pool address to specific inside VRF. Example communication (in the above +diagram): 1) from GE0/7/0 -> GE0/b/0 would choose 10.0.1.1 pool address +2) from GE0/8/0 -> GE0/b/0 would choose 10.0.1.2 pool address + +Plugin enable parameters inside-vrf and outside-vrf are used as follows: + +Both ``inside-vrf`` and ``outside-vrf`` configuration parameters are +used in conjunction with Static NAT, inside-vrf is only used for Static +NAT. + +inside VRF: - used only in conjunction with static mappings - default +inside VRF parameter is used in in2out feature to lookup static mapping +if mapping can’t be found by inside interface VRF - used as default when +adding static mappings as in2out vrf + +outside VRF: - used in conjunction with static mappings - secondary +option for looking up static mappings in in2out feature based on outside +VRF - used as default destination vrf in in2out feature during session +creation if non of outside interfaces can resolve destination IP address + +Session creation default behavior (in2out only): - ingress interface fib +is used as inside fib - Outside fib is chosen based on ability to +resolve destination address in one of the outside interface networks. if +there is no such network that is able to resolve destination a default +outside fib (outside vrf index) is used. + +Default behavior enables use of multiple outside and inside fibs with +some limitations. The limitation in the default behavior is that if each +interface belonging to different fib contains default gateway every time +first interface network fib gets used as outside fib index during +session creation. + +VRF tables +^^^^^^^^^^ + + nat44 vrf table [add|del] ``vrf-id`` + +.. + + nat44 vrf route [add|del] table ``vrf-id`` ``vrf-id`` + +VRF tables change the default behavior of working with inter-vrf +communication. Adding empty VRF table disables passing communication +between VRFs. Adding additional routes to the table makes destination +VRF decision making algorithm do lookups into these tables. During +session creation destination VRF in in2out feature is resolved by +traversing VRF routes in the matching VRF table. If VRF route resolves +destination IPv4 address then this VRF gets used. If non VRF route can +resolve destination IPv4 address If VRF route can’t be found source VRF +will be used. Priority of VRF routes is based on order of configuration. + +Timeouts +~~~~~~~~ + + set nat timeout [udp ``sec`` \| tcp-established ``sec`` + tcp-transitory ``sec`` \| icmp ``sec`` \| reset] + +Session Limiting +~~~~~~~~~~~~~~~~ + + nat44 plugin enable sessions ``max-number`` + +Maximum number of sessions value is used on per-thread (per-worker) +basis. + + set nat44 session limit ``limit`` [vrf ``table-id``] + +Per-vrf session limiting makes it possible to split maximum number of +sessions between different VRFs. + +MSS Clamping +~~~~~~~~~~~~ + + nat mss-clamping ``mss-value``\ \|disable + +Forwarding +~~~~~~~~~~ + + nat44 forwarding enable|disable + +Additional Configuration Commands +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + | set nat frame-queue-nelts ``number`` + | set nat workers ``workers-list`` + | nat44 del session in|out ``addr``:``port`` tcp|udp|icmp [vrf + ``id``] [external-host ``addr``:``port``] + +Show commands +^^^^^^^^^^^^^ + +:: + + show nat workers + show nat timeouts + show nat44 summary + show nat44 sessions + show nat44 addresses + show nat mss-clamping + show nat44 interfaces + show nat44 vrf tables + show nat44 hash tables + nat44 show static mappings + show nat44 interface address + +Configuration Examples +---------------------- + +TWICE-NAT +~~~~~~~~~ + +Twice NAT lets you translate both the source and destination address in +a single rule. Currently, twice NAT44 is supported only for local +network service session initiated from outside network. Twice NAT static +mappings can only get initiated (create sessions) from outside network. + +Topology +^^^^^^^^ + +:: + + +--------------------------+ + | 10.0.0.2/24 (local host) | + +--------------------------+ + | + +---------------------------------+ + | 10.0.0.1/24 (eth0) (nat inside) | + | 20.0.0.1/24 (eth1) (nat outside)| + +---------------------------------+ + | + +---------------------------+ + | 20.0.0.2/24 (remote host) | + +---------------------------+ + +In this example traffic will be initiated from remote host. Remote host +will be accessing local host via twice-nat mapping. + +Translation will occur as follows: +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +outside to inside translation: + + + | src address: 20.0.0.2 -> 192.168.160.101 + | dst address: 20.0.0.1 -> 10.0.0.2 + +inside to outside translation: + + + | src address: 10.0.0.2 -> 20.0.0.1 + | dst address: 192.168.160.101 -> 20.0.0.2 + +Configuration +^^^^^^^^^^^^^ + +Enable nat44-ed plugin: + + +:: + + nat44 plugin enable sessions 1000 + +Configure inside interface: + + +:: + + set int state eth0 up + set int ip address eth0 10.0.0.1/24 + set int nat44 in eth0 + +Configure outside interface: + + +:: + + set int state eth1 up + set int ip address eth1 20.0.0.1/24 + set int nat44 out eth1 + +Configure nat address pools: + + +:: + + nat44 add address 20.0.0.1 + nat44 add address 192.168.160.101 twice-nat + +- alternatively we could use ``nat44 add interface address eth1`` +- both pools are required +- pool ``20.0.0.1`` is used for out2in incoming traffic +- special twice-nat pool ``192.168.160.101`` is used for secondary + translation + +Finally, add twice-nat mapping: + + + nat44 add static mapping tcp local 10.0.0.2 5201 external 20.0.0.1 + 5201 twice-nat + +SELF TWICE-NAT +~~~~~~~~~~~~~~ + +Self twice NAT works similar to twice NAT with few exceptions. Self +twice NAT is a feature that lets client and service running on the same +host to communicate via NAT device. This means that external address is +the same address as local address. Self twice NAT static mappings can +only get initiated (create sessions) from outside network. + +.. _topology-self-twice-nat: + +Topology +^^^^^^^^ + +:: + + +--------------------------+ + | 10.0.0.2/24 (local host) | + +--------------------------+ + | + +-------------------------------------------+ + | 10.0.0.1/24 (eth0) (nat inside & outside) | + +-------------------------------------------+ + +In this example traffic will be initiated from local host. Local host +will be accessing itself via self-twice-nat mapping. + +.. _translation-will-occur-as-follows-1: + +Translation will occur as follows: +'''''''''''''''''''''''''''''''''' + +.. _outside-to-inside-translation-1: + +outside to inside translation: + + + | src address: 10.0.0.2 -> 192.168.160.101 + | dst address: 10.0.0.1 -> 10.0.0.2 + +.. _inside-to-outside-translation-1: + +inside to outside translation: + + + | src address: 10.0.0.2 -> 10.0.0.1 + | dst address: 192.168.160.101 -> 10.0.0.2 + +.. _configuration-1: + +Configuration +^^^^^^^^^^^^^ + +.. _enable-nat44-ed-plugin-1: + +Enable nat44-ed plugin: + + +:: + + nat44 plugin enable sessions 1000 + +Configure NAT interface: + + +:: + + set int state eth0 up + set int ip address eth0 10.0.0.1/24 + set int nat44 in eth0 + set int nat44 out eth0 + +.. _configure-nat-address-pools-1: + +Configure nat address pools: + + +:: + + nat44 add address 10.0.0.1 + nat44 add address 192.168.160.101 twice-nat + +Finally, add self-twice-nat mapping: + + + nat44 add static mapping tcp local 10.0.0.2 5201 external 10.0.0.1 + 5201 self-twice-nat diff --git a/src/plugins/nat/nat44-ed/nat44_ed_format.c b/src/plugins/nat/nat44-ed/nat44_ed_format.c index 597bc2b4d0b..ee3e925e529 100644 --- a/src/plugins/nat/nat44-ed/nat44_ed_format.c +++ b/src/plugins/nat/nat44-ed/nat44_ed_format.c @@ -12,111 +12,42 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -/** - * @file - * @brief NAT formatting - */ #include <nat/nat44-ed/nat44_ed.h> #include <nat/nat44-ed/nat44_ed_inlines.h> -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) +format_ed_session_kvp (u8 *s, va_list *args) { - u64 key = va_arg (*args, u64); + clib_bihash_kv_16_8_t *v = va_arg (*args, clib_bihash_kv_16_8_t *); - ip4_address_t addr; - u16 port; - nat_protocol_t protocol; + u8 proto; + u16 r_port, l_port; + ip4_address_t l_addr, r_addr; u32 fib_index; - split_nat_key (key, &addr, &port, &fib_index, &protocol); + 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)); - 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_t *sm = va_arg (*args, snat_main_t *); 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 *); + f64 now = va_arg (*args, f64); - if (snat_is_unk_proto_session (sess)) + if (nat44_ed_is_unk_proto (sess->proto)) { s = format (s, " i2o %U proto %u fib %u\n", format_ip4_address, &sess->in2out.addr, @@ -127,26 +58,23 @@ format_snat_session (u8 * s, va_list * args) } 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, + s = format (s, " i2o %U proto %U port %d fib %d\n", format_ip4_address, + &sess->in2out.addr, format_ip_protocol, sess->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), + format_ip4_address, &sess->out2in.addr, format_ip_protocol, + sess->proto, clib_net_to_host_u16 (sess->out2in.port), sess->out2in.fib_index); } - if (is_ed_session (sess) || is_fwd_bypass_session (sess)) + if (nat44_ed_is_twice_nat_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)); - } + 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) @@ -156,20 +84,21 @@ format_snat_session (u8 * s, va_list * args) } 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, " timeout in %.2f\n", + nat44_session_get_timeout (sm, sess) - (now - sess->last_heard)); + s = format (s, " total pkts %d, total bytes %lld\n", sess->total_pkts, + sess->total_bytes); + if (nat44_ed_is_session_static (sess)) s = format (s, " static translation\n"); else s = format (s, " dynamic translation\n"); - if (is_fwd_bypass_session (sess)) + if (na44_ed_is_fwd_bypass_session (sess)) s = format (s, " forwarding-bypass\n"); - if (is_lb_session (sess)) + if (nat44_ed_is_lb_session (sess)) s = format (s, " load-balancing\n"); - if (is_twice_nat_session (sess)) + if (nat44_ed_is_twice_nat_session (sess)) s = format (s, " twice-nat\n"); return s; } @@ -186,9 +115,8 @@ format_snat_static_mapping (u8 * s, va_list * args) 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, + s = format (s, "identity mapping %U %U:%d", format_ip_protocol, + m->proto, format_ip4_address, &m->local_addr, clib_net_to_host_u16 (m->local_port)); pool_foreach (local, m->locals) @@ -212,8 +140,8 @@ format_snat_static_mapping (u8 * s, va_list * args) if (is_sm_lb (m->flags)) { s = - format (s, "%U external %U:%d %s %s", format_nat_protocol, - m->proto, format_ip4_address, &m->external_addr, + format (s, "%U external %U:%d %s %s", format_ip_protocol, m->proto, + format_ip4_address, &m->external_addr, clib_net_to_host_u16 (m->external_port), is_sm_twice_nat (m->flags) ? "twice-nat" : @@ -230,7 +158,7 @@ format_snat_static_mapping (u8 * s, va_list * args) } else s = format (s, "%U local %U:%d external %U:%d vrf %d %s %s", - format_nat_protocol, m->proto, format_ip4_address, + format_ip_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, @@ -245,21 +173,146 @@ format_snat_static_mapping (u8 * s, va_list * args) 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 *); + snat_static_mapping_resolve_t *m = + va_arg (*args, snat_static_mapping_resolve_t *); vnet_main_t *vnm = vnet_get_main (); - if (m->addr_only) + if (is_sm_addr_only (m->flags)) 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); + s = format (s, "%U local %U:%d external %U:%d vrf %d", format_ip_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; +} + +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; + case NAT_ED_TRNSL_ERR_PACKET_TRUNCATED: + s = format (s, "packet-truncated"); + break; + case NAT_ED_TRNSL_ERR_INNER_IP_CORRUPT: + s = format (s, "inner-ip-corrupted"); + break; + case NAT_ED_TRNSL_ERR_INVALID_CSUM: + s = format (s, "invalid-checksum"); + 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; +} + +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_nat44_ed_tcp_state (u8 *s, va_list *args) +{ + nat44_ed_tcp_state_e e = va_arg (*args, nat44_ed_tcp_state_e); + switch (e) + { + case NAT44_ED_TCP_STATE_CLOSED: + s = format (s, "closed"); + break; + case NAT44_ED_TCP_STATE_ESTABLISHED: + s = format (s, "established"); + break; + case NAT44_ED_TCP_STATE_CLOSING: + s = format (s, "closing"); + break; + case NAT44_ED_TCP_N_STATE: + s = format (s, "BUG! unexpected N_STATE! BUG!"); + break; + } return s; } diff --git a/src/plugins/nat/nat44-ed/nat44_ed_handoff.c b/src/plugins/nat/nat44-ed/nat44_ed_handoff.c index c5ceff4e454..5cb4effb6c4 100644 --- a/src/plugins/nat/nat44-ed/nat44_ed_handoff.c +++ b/src/plugins/nat/nat44-ed/nat44_ed_handoff.c @@ -19,7 +19,6 @@ #include <vlib/vlib.h> #include <vnet/vnet.h> -#include <vnet/handoff.h> #include <vnet/fib/ip4_fib.h> #include <vppinfra/error.h> diff --git a/src/plugins/nat/nat44-ed/nat44_ed_in2out.c b/src/plugins/nat/nat44-ed/nat44_ed_in2out.c index 0065d7703b1..9b4dac3b356 100644 --- a/src/plugins/nat/nat44-ed/nat44_ed_in2out.c +++ b/src/plugins/nat/nat44-ed/nat44_ed_in2out.c @@ -25,18 +25,12 @@ #include <vnet/udp/udp_local.h> #include <vppinfra/error.h> -#include <nat/lib/nat_syslog.h> #include <nat/lib/nat_inlines.h> #include <nat/lib/ipfix_logging.h> #include <nat/nat44-ed/nat44_ed.h> #include <nat/nat44-ed/nat44_ed_inlines.h> -/* 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 @@ -55,6 +49,7 @@ typedef struct u8 is_slow_path; u8 translation_via_i2of; u8 lookup_skipped; + u8 tcp_state; } nat_in2out_ed_trace_t; static u8 * @@ -84,7 +79,7 @@ format_nat_in2out_ed_trace (u8 * s, va_list * args) { if (t->lookup_skipped) { - s = format (s, "\n lookup skipped - cached session index used"); + s = format (s, "\n lookup skipped - cached session index used"); } else { @@ -92,93 +87,33 @@ format_nat_in2out_ed_trace (u8 * s, va_list * args) &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) + if (IP_PROTOCOL_TCP == t->i2of.match.proto) { - 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; - } + s = format (s, "\n TCP state: %U", format_nat44_ed_tcp_state, + t->tcp_state); } - return 1; + return s; } static int nat_ed_alloc_addr_and_port_with_snat_address ( - snat_main_t *sm, u32 nat_proto, u32 thread_index, snat_address_t *a, + snat_main_t *sm, u8 proto, u32 thread_index, snat_address_t *a, u16 port_per_thread, u32 snat_thread_index, snat_session_t *s, ip4_address_t *outside_addr, u16 *outside_port) { - const u16 port_thread_offset = (port_per_thread * snat_thread_index) + 1024; + const u16 port_thread_offset = + (port_per_thread * snat_thread_index) + ED_USER_PORT_OFFSET; + + /* Backup original match in case of failure */ + const nat_6t_t match = s->o2i.match; 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) + 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 */ @@ -188,27 +123,13 @@ nat_ed_alloc_addr_and_port_with_snat_address ( u16 attempts = ED_PORT_ALLOC_ATTEMPTS; do { - if (NAT_PROTOCOL_ICMP == nat_proto) + if (IP_PROTOCOL_ICMP == 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)) { -#define _(N, i, n, s) \ - case NAT_PROTOCOL_##N: \ - ++a->busy_##n##_port_refcounts[port]; \ - a->busy_##n##_ports_per_thread[thread_index]++; \ - a->busy_##n##_ports++; \ - break; - switch (nat_proto) - { - foreach_nat_protocol; - default: - nat_elog_info (sm, "unknown protocol"); - return 1; - } -#undef _ *outside_addr = a->addr; *outside_port = clib_host_to_net_u16 (port); return 0; @@ -218,58 +139,141 @@ nat_ed_alloc_addr_and_port_with_snat_address ( --attempts; } while (attempts > 0); + + /* Revert match */ + s->o2i.match = match; return 1; } static int -nat_ed_alloc_addr_and_port (snat_main_t *sm, u32 rx_fib_index, u32 nat_proto, +nat_ed_alloc_addr_and_port (snat_main_t *sm, u32 rx_fib_index, + u32 tx_sw_if_index, u32 nat_proto, u32 thread_index, ip4_address_t s_addr, - u16 port_per_thread, u32 snat_thread_index, + ip4_address_t d_addr, u32 snat_thread_index, snat_session_t *s, ip4_address_t *outside_addr, u16 *outside_port) { - int i; - snat_address_t *a, *ga = 0; - if (vec_len (sm->addresses) > 0) { - int s_addr_offset = s_addr.as_u32 % vec_len (sm->addresses); - - for (i = s_addr_offset; i < vec_len (sm->addresses); ++i) + u32 s_addr_offset = (s_addr.as_u32 + (s_addr.as_u32 >> 8) + + (s_addr.as_u32 >> 16) + (s_addr.as_u32 >> 24)) % + vec_len (sm->addresses); + snat_address_t *a, *ja = 0, *ra = 0, *ba = 0; + int i; + + // output feature + if (tx_sw_if_index != ~0) { - a = sm->addresses + i; - if (a->fib_index == rx_fib_index) + for (i = s_addr_offset; i < vec_len (sm->addresses); ++i) { - return nat_ed_alloc_addr_and_port_with_snat_address ( - sm, nat_proto, thread_index, a, port_per_thread, - snat_thread_index, s, outside_addr, outside_port); + a = sm->addresses + i; + if (a->fib_index == rx_fib_index) + { + if (a->sw_if_index == tx_sw_if_index) + { + if ((a->addr_len != ~0) && + (a->net.as_u32 == + (d_addr.as_u32 & ip4_main.fib_masks[a->addr_len]))) + + { + return nat_ed_alloc_addr_and_port_with_snat_address ( + sm, nat_proto, thread_index, a, + sm->port_per_thread, snat_thread_index, s, + outside_addr, outside_port); + } + ra = a; + } + ja = a; + } + else if (a->fib_index == ~0) + { + ba = a; + } } - else if (a->fib_index == ~0) + for (i = 0; i < s_addr_offset; ++i) { - ga = a; + a = sm->addresses + i; + if (a->fib_index == rx_fib_index) + { + if (a->sw_if_index == tx_sw_if_index) + { + if ((a->addr_len != ~0) && + (a->net.as_u32 == + (d_addr.as_u32 & ip4_main.fib_masks[a->addr_len]))) + + { + return nat_ed_alloc_addr_and_port_with_snat_address ( + sm, nat_proto, thread_index, a, + sm->port_per_thread, snat_thread_index, s, + outside_addr, outside_port); + } + ra = a; + } + ja = a; + } + else if (a->fib_index == ~0) + { + ba = a; + } } - } - - for (i = 0; i < s_addr_offset; ++i) - { - a = sm->addresses + i; - if (a->fib_index == rx_fib_index) + if (ra) { return nat_ed_alloc_addr_and_port_with_snat_address ( - sm, nat_proto, thread_index, a, port_per_thread, + sm, nat_proto, thread_index, ra, sm->port_per_thread, snat_thread_index, s, outside_addr, outside_port); } - else if (a->fib_index == ~0) + } + else + { + // first try nat pool addresses to sw interface addreses mappings + for (i = s_addr_offset; i < vec_len (sm->addresses); ++i) { - ga = a; + a = sm->addresses + i; + if (a->fib_index == rx_fib_index) + { + if ((a->addr_len != ~0) && + (a->net.as_u32 == + (d_addr.as_u32 & ip4_main.fib_masks[a->addr_len]))) + { + return nat_ed_alloc_addr_and_port_with_snat_address ( + sm, nat_proto, thread_index, a, sm->port_per_thread, + snat_thread_index, s, outside_addr, outside_port); + } + ja = a; + } + else if (a->fib_index == ~0) + { + ba = a; + } + } + for (i = 0; i < s_addr_offset; ++i) + { + a = sm->addresses + i; + if (a->fib_index == rx_fib_index) + { + if ((a->addr_len != ~0) && + (a->net.as_u32 == + (d_addr.as_u32 & ip4_main.fib_masks[a->addr_len]))) + { + return nat_ed_alloc_addr_and_port_with_snat_address ( + sm, nat_proto, thread_index, a, sm->port_per_thread, + snat_thread_index, s, outside_addr, outside_port); + } + ja = a; + } + else if (a->fib_index == ~0) + { + ba = a; + } } } - if (ga) + if (ja || ba) { + a = ja ? ja : ba; return nat_ed_alloc_addr_and_port_with_snat_address ( - sm, nat_proto, thread_index, a, port_per_thread, snat_thread_index, - s, outside_addr, outside_port); + sm, nat_proto, thread_index, a, sm->port_per_thread, + snat_thread_index, s, outside_addr, outside_port); } } /* Totally out of translations to use... */ @@ -277,74 +281,167 @@ nat_ed_alloc_addr_and_port (snat_main_t *sm, u32 rx_fib_index, u32 nat_proto, return 1; } +static_always_inline int +nat44_ed_external_sm_lookup (snat_main_t *sm, ip4_address_t match_addr, + u16 match_port, ip_protocol_t match_protocol, + ip4_address_t *daddr, u16 *dport) +{ + snat_static_mapping_t *m = + nat44_ed_sm_o2i_lookup (sm, match_addr, match_port, 0, match_protocol); + if (!m) + { + /* Try address only mapping */ + m = nat44_ed_sm_o2i_lookup (sm, match_addr, 0, 0, 0); + if (!m) + return 0; + } + *daddr = m->local_addr; + if (dport) + { + /* Address only mapping doesn't change port */ + *dport = is_sm_addr_only (m->flags) ? match_port : m->local_port; + } + return 1; +} + +static_always_inline vrf_table_t * +get_vrf_table_by_fib (u32 fib_index) +{ + snat_main_t *sm = &snat_main; + vrf_table_t *t; + + pool_foreach (t, sm->vrf_tables) + { + if (fib_index == t->table_fib_index) + { + return t; + } + } + + return 0; +} + static_always_inline u32 -nat_outside_fib_index_lookup (snat_main_t * sm, ip4_address_t addr) +get_tx_fib_index (u32 rx_fib_index, 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) + + snat_main_t *sm = &snat_main; + vrf_table_t *t = get_vrf_table_by_fib (rx_fib_index); + // default to rx fib + u32 tx_fib_index = rx_fib_index; + + if (0 != t) { - 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; - } - } + // managed routes to other fibs + vrf_route_t *r; + pool_foreach (r, t->routes) + { + fei = fib_table_lookup (r->fib_index, &pfx); + if ((FIB_NODE_INDEX_INVALID != fei) && + (~0 != fib_entry_get_resolving_interface (fei))) + { + tx_fib_index = r->fib_index; + break; + } + } } - return ~0; + else + { + // default to configured fib + tx_fib_index = sm->outside_fib_index; + + // default routes to other fibs + nat_fib_t *f; + vec_foreach (f, sm->outside_fibs) + { + fei = fib_table_lookup (f->fib_index, &pfx); + if ((FIB_NODE_INDEX_INVALID != fei) && + (~0 != fib_entry_get_resolving_interface (fei))) + { + tx_fib_index = f->fib_index; + break; + } + } + } + + return tx_fib_index; } 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) +is_destination_resolvable (u32 rx_fib_index, ip4_address_t addr) { - 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)) + 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 = addr.as_u32,} + , + }; + + snat_main_t *sm = &snat_main; + vrf_table_t *t = get_vrf_table_by_fib (rx_fib_index); + u32 ii; + + if (0 != t) { - /* 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; + // managed routes to other fibs + vrf_route_t *r; + pool_foreach (r, t->routes) + { + fei = fib_table_lookup (r->fib_index, &pfx); + if ((FIB_NODE_INDEX_INVALID != fei) && + (~0 != (ii = fib_entry_get_resolving_interface (fei)))) + { + return 1; + } + } } - - snat_static_mapping_t *m = - pool_elt_at_index (sm->static_mappings, value.value); - *daddr = m->local_addr; - if (dport) + else { - /* Address only mapping doesn't change port */ - *dport = is_sm_addr_only (m->flags) ? match_port : m->local_port; + // default routes to other fibs + nat_fib_t *f; + vec_foreach (f, sm->outside_fibs) + { + fei = fib_table_lookup (f->fib_index, &pfx); + if ((FIB_NODE_INDEX_INVALID != fei) && + (~0 != (ii = fib_entry_get_resolving_interface (fei)))) + { + snat_interface_t *i; + pool_foreach (i, sm->interfaces) + { + if ((nat44_ed_is_interface_outside (i)) && + (ii == i->sw_if_index)) + { + return 1; + } + } + } + } } - return 1; + + return 0; } static u32 slow_path_ed (vlib_main_t *vm, 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, + u16 r_port, u8 proto, u32 rx_fib_index, u32 tx_sw_if_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; + u32 tx_fib_index; u8 is_identity_nat = 0; - 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; @@ -363,33 +460,14 @@ slow_path_ed (vlib_main_t *vm, snat_main_t *sm, vlib_buffer_t *b, } } - 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 (vm, 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 + int is_sm = 0; + // First try to match static mapping by local address and port + if (!snat_static_mapping_match (vm, l_addr, l_port, rx_fib_index, proto, + &sm_addr, &sm_port, &sm_fib_index, 0, 0, 0, + &lb, 0, &is_identity_nat, 0)) { if (PREDICT_FALSE (is_identity_nat)) { @@ -399,7 +477,7 @@ slow_path_ed (vlib_main_t *vm, snat_main_t *sm, vlib_buffer_t *b, is_sm = 1; } - if (PREDICT_TRUE (nat_proto == NAT_PROTOCOL_TCP)) + if (PREDICT_TRUE (proto == IP_PROTOCOL_TCP)) { if (PREDICT_FALSE (!tcp_flags_is_init ( vnet_buffer (b)->ip.reass.icmp_type_or_tcp_flags))) @@ -412,28 +490,31 @@ slow_path_ed (vlib_main_t *vm, snat_main_t *sm, vlib_buffer_t *b, s = nat_ed_session_alloc (sm, thread_index, now, proto); ASSERT (s); + tx_fib_index = get_tx_fib_index (rx_fib_index, r_addr); + if (!is_sm) { s->in2out.addr = l_addr; s->in2out.port = l_port; - s->nat_proto = nat_proto; + s->proto = proto; s->in2out.fib_index = rx_fib_index; - s->out2in.fib_index = outside_fib_index; + s->out2in.fib_index = tx_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; + if (PREDICT_FALSE (nat44_ed_external_sm_lookup (sm, r_addr, r_port, + proto, &daddr, &dport))) + { + s->flags |= 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) + if (IP_PROTOCOL_ICMP == proto) { nat_6t_flow_icmp_id_rewrite_set (&s->o2i, l_port); } @@ -442,11 +523,11 @@ slow_path_ed (vlib_main_t *vm, snat_main_t *sm, vlib_buffer_t *b, nat_6t_flow_dport_rewrite_set (&s->o2i, l_port); } nat_6t_flow_txfib_rewrite_set (&s->o2i, rx_fib_index); + nat_6t_flow_saddr_rewrite_set (&s->o2i, r_addr.as_u32); if (nat_ed_alloc_addr_and_port ( - sm, rx_fib_index, nat_proto, thread_index, l_addr, - sm->port_per_thread, tsm->snat_thread_index, s, &outside_addr, - &outside_port)) + sm, rx_fib_index, tx_sw_if_index, proto, thread_index, l_addr, + r_addr, 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]; @@ -463,17 +544,17 @@ slow_path_ed (vlib_main_t *vm, snat_main_t *sm, vlib_buffer_t *b, s->out2in.port = outside_port = sm_port; s->in2out.addr = l_addr; s->in2out.port = l_port; - s->nat_proto = nat_proto; + s->proto = proto; s->in2out.fib_index = rx_fib_index; - s->out2in.fib_index = outside_fib_index; + s->out2in.fib_index = tx_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); + int is_hairpinning = nat44_ed_external_sm_lookup (sm, r_addr, r_port, + proto, &daddr, &dport); s->flags |= is_hairpinning * SNAT_SESSION_FLAG_HAIRPINNING; - if (NAT_PROTOCOL_ICMP == nat_proto) + if (IP_PROTOCOL_ICMP == proto) { nat_6t_o2i_flow_init (sm, thread_index, s, daddr, sm_port, sm_addr, sm_port, s->out2in.fib_index, proto); @@ -487,6 +568,7 @@ slow_path_ed (vlib_main_t *vm, snat_main_t *sm, vlib_buffer_t *b, } nat_6t_flow_daddr_rewrite_set (&s->o2i, l_addr.as_u32); nat_6t_flow_txfib_rewrite_set (&s->o2i, rx_fib_index); + nat_6t_flow_saddr_rewrite_set (&s->o2i, r_addr.as_u32); if (nat_ed_ses_o2i_flow_hash_add_del (sm, thread_index, s, 2)) { nat_elog_notice (sm, "out2in key add failed"); @@ -496,7 +578,6 @@ slow_path_ed (vlib_main_t *vm, snat_main_t *sm, vlib_buffer_t *b, 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; @@ -505,7 +586,7 @@ slow_path_ed (vlib_main_t *vm, snat_main_t *sm, vlib_buffer_t *b, 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) + if (IP_PROTOCOL_ICMP == proto) { nat_6t_flow_icmp_id_rewrite_set (&s->i2o, outside_port); } @@ -514,7 +595,7 @@ slow_path_ed (vlib_main_t *vm, snat_main_t *sm, vlib_buffer_t *b, 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); + nat_6t_flow_txfib_rewrite_set (&s->i2o, tx_fib_index); if (nat_ed_ses_i2o_flow_hash_add_del (sm, thread_index, s, 1)) { @@ -523,17 +604,14 @@ slow_path_ed (vlib_main_t *vm, snat_main_t *sm, vlib_buffer_t *b, } /* 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->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); + &s->ext_host_addr, s->ext_host_port, s->proto, 0); per_vrf_sessions_register_session (s, thread_index); @@ -542,12 +620,6 @@ slow_path_ed (vlib_main_t *vm, snat_main_t *sm, vlib_buffer_t *b, 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; @@ -555,38 +627,55 @@ error: } static_always_inline int -nat44_ed_not_translate (vlib_main_t *vm, snat_main_t *sm, - vlib_node_runtime_t *node, u32 sw_if_index, - vlib_buffer_t *b, ip4_header_t *ip, u32 proto, - u32 rx_fib_index, u32 thread_index) +nat44_ed_not_translate (vlib_main_t *vm, vlib_node_runtime_t *node, + u32 sw_if_index, vlib_buffer_t *b, ip4_header_t *ip, + u32 proto, u32 rx_fib_index) { + snat_main_t *sm = &snat_main; + clib_bihash_kv_16_8_t kv, value; + ip4_address_t placeholder_addr; + u32 placeholder_fib_index; + u16 placeholder_port; + + init_ed_k (&kv, ip->dst_address.as_u32, + vnet_buffer (b)->ip.reass.l4_dst_port, ip->src_address.as_u32, + vnet_buffer (b)->ip.reass.l4_src_port, sm->outside_fib_index, + ip->protocol); + + // do nat if active session or is static mapping + if (!clib_bihash_search_16_8 (&sm->flow_hash, &kv, &value) || + !snat_static_mapping_match ( + vm, ip->dst_address, vnet_buffer (b)->ip.reass.l4_dst_port, + sm->outside_fib_index, proto, &placeholder_addr, &placeholder_port, + &placeholder_fib_index, 1, 0, 0, 0, 0, 0, 0)) + { + return 0; + } - init_ed_k (&kv, ip->dst_address, vnet_buffer (b)->ip.reass.l4_dst_port, - ip->src_address, vnet_buffer (b)->ip.reass.l4_src_port, - sm->outside_fib_index, ip->protocol); + // do not nat if forwarding enabled + if (sm->forwarding_enabled) + { + return 1; + } - /* NAT packet aimed at external address if has active sessions */ - if (clib_bihash_search_16_8 (&sm->flow_hash, &kv, &value)) + // do not nat packet aimed at the interface address + if (PREDICT_FALSE ( + is_interface_addr (sm, node, sw_if_index, ip->dst_address.as_u32))) { - /* or is static mappings */ - ip4_address_t placeholder_addr; - u16 placeholder_port; - u32 placeholder_fib_index; - if (!snat_static_mapping_match ( - vm, sm, ip->dst_address, vnet_buffer (b)->ip.reass.l4_dst_port, - sm->outside_fib_index, proto, &placeholder_addr, &placeholder_port, - &placeholder_fib_index, 1, 0, 0, 0, 0, 0, 0)) - return 0; + return 1; } - else - return 0; - if (sm->forwarding_enabled) - return 1; + // do nat packets with resolvable destination + // destination can be resolved either by: + // a) vrf routing table entry + // b) (non output feature) outside interface fib + if (is_destination_resolvable (rx_fib_index, ip->dst_address)) + { + return 0; + } - return snat_not_translate_fast (sm, node, sw_if_index, ip, proto, - rx_fib_index); + return 1; } static_always_inline int @@ -610,18 +699,18 @@ nat_not_translate_output_feature_fwd (snat_main_t * sm, ip4_header_t * ip, &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); + init_ed_k (&kv, lookup_saddr.as_u32, lookup_sport, lookup_daddr.as_u32, + 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); + init_ed_k (&kv, ip->src_address.as_u32, + vnet_buffer (b)->ip.reass.l4_src_port, ip->dst_address.as_u32, + 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, + init_ed_k (&kv, ip->src_address.as_u32, 0, ip->dst_address.as_u32, 0, 0, ip->protocol); } @@ -632,11 +721,13 @@ nat_not_translate_output_feature_fwd (snat_main_t * sm, ip4_header_t * ip, pool_elt_at_index (tsm->sessions, ed_value_get_session_index (&value)); - if (is_fwd_bypass_session (s)) + if (na44_ed_is_fwd_bypass_session (s)) { if (ip->protocol == IP_PROTOCOL_TCP) { - nat44_set_tcp_session_state_i2o (sm, now, s, b, thread_index); + nat44_set_tcp_session_state_i2o ( + sm, now, s, vnet_buffer (b)->ip.reass.icmp_type_or_tcp_flags, + thread_index); } /* Accounting */ nat44_session_update_counters (s, now, @@ -658,7 +749,7 @@ nat44_ed_not_translate_output_feature (snat_main_t *sm, vlib_buffer_t *b, 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, int is_multi_worker) + int is_multi_worker) { clib_bihash_kv_16_8_t kv, value; snat_main_per_thread_data_t *tsm = &sm->per_thread_data[thread_index]; @@ -668,20 +759,14 @@ nat44_ed_not_translate_output_feature (snat_main_t *sm, vlib_buffer_t *b, 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); + init_ed_k (&kv, ip->src_address.as_u32, src_port, ip->dst_address.as_u32, + 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; } @@ -706,8 +791,8 @@ nat44_ed_not_translate_output_feature (snat_main_t *sm, vlib_buffer_t *b, s = NULL; } - init_ed_k (&kv, ip->dst_address, dst_port, ip->src_address, src_port, - rx_fib_index, ip->protocol); + init_ed_k (&kv, ip->dst_address.as_u32, dst_port, ip->src_address.as_u32, + 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)); @@ -716,15 +801,16 @@ nat44_ed_not_translate_output_feature (snat_main_t *sm, vlib_buffer_t *b, ed_value_get_session_index (&value)); skip_dst_nat_lookup: - if (is_fwd_bypass_session (s)) + if (na44_ed_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; - } + { + if ((nat44_ed_is_interface_inside (i)) && + (rx_sw_if_index == i->sw_if_index)) + return 0; + } return 1; } @@ -734,9 +820,9 @@ nat44_ed_not_translate_output_feature (snat_main_t *sm, vlib_buffer_t *b, 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, + u32 tx_sw_if_index, u32 rx_fib_index, + vlib_node_runtime_t *node, u32 next, f64 now, + u32 thread_index, snat_session_t **s_p, int is_multi_worker) { vlib_main_t *vm = vlib_get_main (); @@ -756,20 +842,19 @@ icmp_in2out_ed_slow_path (snat_main_t *sm, vlib_buffer_t *b, ip4_header_t *ip, return NAT_NEXT_DROP; } - if (vnet_buffer (b)->sw_if_index[VLIB_TX] != ~0) + if (tx_sw_if_index != ~0) { if (PREDICT_FALSE (nat44_ed_not_translate_output_feature ( sm, b, ip, lookup_sport, lookup_dport, thread_index, sw_if_index, - vnet_buffer (b)->sw_if_index[VLIB_TX], now, is_multi_worker))) + tx_sw_if_index, is_multi_worker))) { return next; } } else { - if (PREDICT_FALSE (nat44_ed_not_translate (vm, sm, node, sw_if_index, b, - ip, NAT_PROTOCOL_ICMP, - rx_fib_index, thread_index))) + if (PREDICT_FALSE (nat44_ed_not_translate ( + vm, node, sw_if_index, b, ip, IP_PROTOCOL_ICMP, rx_fib_index))) { return next; } @@ -782,9 +867,10 @@ icmp_in2out_ed_slow_path (snat_main_t *sm, vlib_buffer_t *b, ip4_header_t *ip, return NAT_NEXT_DROP; } - next = slow_path_ed (vm, 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)); + next = + slow_path_ed (vm, sm, b, ip->src_address, ip->dst_address, lookup_sport, + lookup_dport, ip->protocol, rx_fib_index, tx_sw_if_index, &s, + node, next, thread_index, vlib_time_now (vm)); if (NAT_NEXT_DROP == next) goto out; @@ -822,12 +908,11 @@ nat44_ed_in2out_slowpath_unknown_proto (snat_main_t *sm, vlib_buffer_t *b, 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; + u32 tx_fib_index; int i; ip4_address_t new_src_addr = { 0 }; ip4_address_t new_dst_addr = ip->dst_address; @@ -842,25 +927,13 @@ nat44_ed_in2out_slowpath_unknown_proto (snat_main_t *sm, vlib_buffer_t *b, 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; - } + tx_fib_index = get_tx_fib_index (rx_fib_index, ip->dst_address); - 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)) + // Try to find static mapping first + m = nat44_ed_sm_i2o_lookup (sm, ip->src_address, 0, rx_fib_index, + ip->protocol); + if (m) { - m = pool_elt_at_index (sm->static_mappings, value.value); new_src_addr = m->external_addr; } else @@ -869,8 +942,9 @@ nat44_ed_in2out_slowpath_unknown_proto (snat_main_t *sm, vlib_buffer_t *b, { 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); + init_ed_k (&s_kv, s->out2in.addr.as_u32, 0, + ip->dst_address.as_u32, 0, tx_fib_index, + ip->protocol); if (clib_bihash_search_16_8 (&sm->flow_hash, &s_kv, &s_value)) { new_src_addr = s->out2in.addr; @@ -883,8 +957,9 @@ nat44_ed_in2out_slowpath_unknown_proto (snat_main_t *sm, vlib_buffer_t *b, { 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); + init_ed_k (&s_kv, sm->addresses[i].addr.as_u32, 0, + ip->dst_address.as_u32, 0, tx_fib_index, + ip->protocol); if (clib_bihash_search_16_8 (&sm->flow_hash, &s_kv, &s_value)) { new_src_addr = sm->addresses[i].addr; @@ -910,28 +985,25 @@ nat44_ed_in2out_slowpath_unknown_proto (snat_main_t *sm, vlib_buffer_t *b, 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); + nat_6t_flow_txfib_rewrite_set (&s->i2o, tx_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); + int is_hairpinning = nat44_ed_external_sm_lookup ( + sm, ip->dst_address, 0, ip->protocol, &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_flow_txfib_rewrite_set (&s->i2o, tx_fib_index); nat_6t_o2i_flow_init (sm, thread_index, s, new_dst_addr, 0, new_src_addr, 0, - outside_fib_index, ip->protocol); + tx_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->out2in.fib_index = tx_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; @@ -988,11 +1060,13 @@ nat44_ed_in2out_fast_path_node_fn_inline (vlib_main_t *vm, while (n_left_from > 0) { vlib_buffer_t *b0; - u32 sw_if_index0, rx_fib_index0, iph_offset0 = 0; - nat_protocol_t proto0; + u32 rx_sw_if_index0, rx_fib_index0, iph_offset0 = 0; + u32 tx_sw_if_index0; + u32 cntr_sw_if_index0; + ip_protocol_t proto0; ip4_header_t *ip0; snat_session_t *s0 = 0; - clib_bihash_kv_16_8_t kv0, value0; + clib_bihash_kv_16_8_t kv0 = { 0 }, value0; nat_translation_error_e translation_error = NAT_ED_TRNSL_ERR_SUCCESS; nat_6t_flow_t *f = 0; nat_6t_t lookup; @@ -1023,9 +1097,12 @@ nat44_ed_in2out_fast_path_node_fn_inline (vlib_main_t *vm, 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); + rx_sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX]; + tx_sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_TX]; + cntr_sw_if_index0 = + is_output_feature ? tx_sw_if_index0 : rx_sw_if_index0; + rx_fib_index0 = fib_table_get_index_for_sw_if_index (FIB_PROTOCOL_IP4, + rx_sw_if_index0); lookup.fib_index = rx_fib_index0; if (PREDICT_FALSE (!is_output_feature && ip0->ttl == 1)) @@ -1038,7 +1115,7 @@ nat44_ed_in2out_fast_path_node_fn_inline (vlib_main_t *vm, goto trace0; } - proto0 = ip_proto_to_nat_proto (ip0->protocol); + proto0 = ip0->protocol; if (is_output_feature) { @@ -1048,7 +1125,7 @@ nat44_ed_in2out_fast_path_node_fn_inline (vlib_main_t *vm, goto trace0; } - if (PREDICT_FALSE (proto0 == NAT_PROTOCOL_ICMP)) + if (PREDICT_FALSE (proto0 == IP_PROTOCOL_ICMP)) { if (vnet_buffer (b0)->ip.reass.icmp_type_or_tcp_flags != ICMP4_echo_request && @@ -1102,8 +1179,8 @@ nat44_ed_in2out_fast_path_node_fn_inline (vlib_main_t *vm, s0 = NULL; } - init_ed_k (&kv0, lookup.saddr, lookup.sport, lookup.daddr, lookup.dport, - lookup.fib_index, lookup.proto); + init_ed_k (&kv0, lookup.saddr.as_u32, lookup.sport, lookup.daddr.as_u32, + lookup.dport, lookup.fib_index, lookup.proto); // lookup flow if (clib_bihash_search_16_8 (&sm->flow_hash, &kv0, &value0)) @@ -1125,25 +1202,10 @@ nat44_ed_in2out_fast_path_node_fn_inline (vlib_main_t *vm, 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); + nat44_ed_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; - } + s0 = 0; + next[0] = def_slow; goto trace0; } @@ -1153,8 +1215,9 @@ nat44_ed_in2out_fast_path_node_fn_inline (vlib_main_t *vm, s0->last_heard + (f64) nat44_session_get_timeout (sm, s0); if (now >= sess_timeout_time) { - nat_free_session_data (sm, s0, thread_index, 0); + nat44_ed_free_session_data (sm, s0, thread_index, 0); nat_ed_session_delete (sm, s0, thread_index, 1); + s0 = 0; // session is closed, go slow path next[0] = def_slow; goto trace0; @@ -1174,8 +1237,9 @@ nat44_ed_in2out_fast_path_node_fn_inline (vlib_main_t *vm, else { translation_error = NAT_ED_TRNSL_ERR_FLOW_MISMATCH; - nat_free_session_data (sm, s0, thread_index, 0); + nat44_ed_free_session_data (sm, s0, thread_index, 0); nat_ed_session_delete (sm, s0, thread_index, 1); + s0 = 0; next[0] = NAT_NEXT_DROP; b0->error = node->errors[NAT_IN2OUT_ED_ERROR_TRNSL_FAILED]; goto trace0; @@ -1185,8 +1249,9 @@ nat44_ed_in2out_fast_path_node_fn_inline (vlib_main_t *vm, (translation_error = nat_6t_flow_buf_translate_i2o ( vm, sm, b0, ip0, f, proto0, is_output_feature))) { - nat_free_session_data (sm, s0, thread_index, 0); + nat44_ed_free_session_data (sm, s0, thread_index, 0); nat_ed_session_delete (sm, s0, thread_index, 1); + s0 = 0; next[0] = NAT_NEXT_DROP; b0->error = node->errors[NAT_IN2OUT_ED_ERROR_TRNSL_FAILED]; goto trace0; @@ -1194,22 +1259,24 @@ nat44_ed_in2out_fast_path_node_fn_inline (vlib_main_t *vm, switch (proto0) { - case NAT_PROTOCOL_TCP: + case IP_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); + thread_index, cntr_sw_if_index0, 1); + nat44_set_tcp_session_state_i2o ( + sm, now, s0, vnet_buffer (b0)->ip.reass.icmp_type_or_tcp_flags, + thread_index); break; - case NAT_PROTOCOL_UDP: + case IP_PROTOCOL_UDP: vlib_increment_simple_counter (&sm->counters.fastpath.in2out.udp, - thread_index, sw_if_index0, 1); + thread_index, cntr_sw_if_index0, 1); break; - case NAT_PROTOCOL_ICMP: + case IP_PROTOCOL_ICMP: vlib_increment_simple_counter (&sm->counters.fastpath.in2out.icmp, - thread_index, sw_if_index0, 1); + thread_index, cntr_sw_if_index0, 1); break; - case NAT_PROTOCOL_OTHER: + default: vlib_increment_simple_counter (&sm->counters.fastpath.in2out.other, - thread_index, sw_if_index0, 1); + thread_index, cntr_sw_if_index0, 1); break; } @@ -1227,7 +1294,7 @@ nat44_ed_in2out_fast_path_node_fn_inline (vlib_main_t *vm, { nat_in2out_ed_trace_t *t = vlib_add_trace (vm, node, b0, sizeof (*t)); - t->sw_if_index = sw_if_index0; + t->sw_if_index = rx_sw_if_index0; t->next_index = next[0]; t->is_slow_path = 0; t->translation_error = translation_error; @@ -1240,6 +1307,7 @@ nat44_ed_in2out_fast_path_node_fn_inline (vlib_main_t *vm, 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); + t->tcp_state = s0->tcp_state; } else { @@ -1250,7 +1318,7 @@ nat44_ed_in2out_fast_path_node_fn_inline (vlib_main_t *vm, if (next[0] == NAT_NEXT_DROP) { vlib_increment_simple_counter (&sm->counters.fastpath.in2out.drops, - thread_index, sw_if_index0, 1); + thread_index, cntr_sw_if_index0, 1); } n_left_from--; @@ -1285,13 +1353,15 @@ nat44_ed_in2out_slow_path_node_fn_inline (vlib_main_t *vm, while (n_left_from > 0) { vlib_buffer_t *b0; - u32 sw_if_index0, rx_fib_index0, iph_offset0 = 0; - nat_protocol_t proto0; + u32 rx_sw_if_index0, rx_fib_index0, iph_offset0 = 0; + u32 tx_sw_if_index0; + u32 cntr_sw_if_index0; + ip_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; + clib_bihash_kv_16_8_t kv0 = { 0 }, value0; int translation_error = NAT_ED_TRNSL_ERR_SUCCESS; b0 = *b; @@ -1304,9 +1374,12 @@ nat44_ed_in2out_slow_path_node_fn_inline (vlib_main_t *vm, 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); + rx_sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX]; + tx_sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_TX]; + cntr_sw_if_index0 = + is_output_feature ? tx_sw_if_index0 : rx_sw_if_index0; + rx_fib_index0 = fib_table_get_index_for_sw_if_index (FIB_PROTOCOL_IP4, + rx_sw_if_index0); if (PREDICT_FALSE (!is_output_feature && ip0->ttl == 1)) { @@ -1320,9 +1393,9 @@ nat44_ed_in2out_slow_path_node_fn_inline (vlib_main_t *vm, udp0 = ip4_next_header (ip0); icmp0 = (icmp46_header_t *) udp0; - proto0 = ip_proto_to_nat_proto (ip0->protocol); + proto0 = ip0->protocol; - if (PREDICT_FALSE (proto0 == NAT_PROTOCOL_OTHER)) + if (PREDICT_FALSE (nat44_ed_is_unk_proto (proto0))) { s0 = nat44_ed_in2out_slowpath_unknown_proto ( sm, b0, ip0, rx_fib_index0, thread_index, now, vm, node); @@ -1334,57 +1407,57 @@ nat44_ed_in2out_slow_path_node_fn_inline (vlib_main_t *vm, (translation_error = nat_6t_flow_buf_translate_i2o ( vm, sm, b0, ip0, &s0->i2o, proto0, is_output_feature))) { - nat_free_session_data (sm, s0, thread_index, 0); + nat44_ed_free_session_data (sm, s0, thread_index, 0); nat_ed_session_delete (sm, s0, thread_index, 1); + s0 = 0; next[0] = NAT_NEXT_DROP; b0->error = node->errors[NAT_IN2OUT_ED_ERROR_TRNSL_FAILED]; goto trace0; } vlib_increment_simple_counter (&sm->counters.slowpath.in2out.other, - thread_index, sw_if_index0, 1); + thread_index, cntr_sw_if_index0, 1); goto trace0; } - if (PREDICT_FALSE (proto0 == NAT_PROTOCOL_ICMP)) + if (PREDICT_FALSE (proto0 == IP_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, is_multi_worker); + sm, b0, ip0, icmp0, rx_sw_if_index0, tx_sw_if_index0, + rx_fib_index0, node, next[0], now, thread_index, &s0, + is_multi_worker); if (NAT_NEXT_DROP != next[0] && s0 && NAT_ED_TRNSL_ERR_SUCCESS != (translation_error = nat_6t_flow_buf_translate_i2o ( vm, sm, b0, ip0, &s0->i2o, proto0, is_output_feature))) { - nat_free_session_data (sm, s0, thread_index, 0); + nat44_ed_free_session_data (sm, s0, thread_index, 0); nat_ed_session_delete (sm, s0, thread_index, 1); + s0 = 0; next[0] = NAT_NEXT_DROP; b0->error = node->errors[NAT_IN2OUT_ED_ERROR_TRNSL_FAILED]; goto trace0; } - vlib_increment_simple_counter (&sm->counters.slowpath.in2out.icmp, - thread_index, sw_if_index0, 1); + if (NAT_NEXT_DROP != next[0]) + { + vlib_increment_simple_counter ( + &sm->counters.slowpath.in2out.icmp, thread_index, + cntr_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); + init_ed_k ( + &kv0, ip0->src_address.as_u32, vnet_buffer (b0)->ip.reass.l4_src_port, + ip0->dst_address.as_u32, 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) @@ -1394,34 +1467,34 @@ nat44_ed_in2out_slow_path_node_fn_inline (vlib_main_t *vm, if (PREDICT_FALSE (nat44_ed_not_translate_output_feature ( sm, b0, 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, - is_multi_worker))) + rx_sw_if_index0, tx_sw_if_index0, is_multi_worker))) 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)) + if (PREDICT_FALSE ( + proto0 == IP_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 ( - vm, sm, node, sw_if_index0, b0, ip0, proto0, rx_fib_index0, - thread_index))) + if (PREDICT_FALSE ( + nat44_ed_not_translate (vm, node, rx_sw_if_index0, b0, ip0, + proto0, rx_fib_index0))) goto trace0; } - next[0] = slow_path_ed ( - vm, 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); + next[0] = + slow_path_ed (vm, 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, tx_sw_if_index0, &s0, + node, next[0], thread_index, now); if (PREDICT_FALSE (next[0] == NAT_NEXT_DROP)) goto trace0; @@ -1437,23 +1510,26 @@ nat44_ed_in2out_slow_path_node_fn_inline (vlib_main_t *vm, (translation_error = nat_6t_flow_buf_translate_i2o ( vm, sm, b0, ip0, &s0->i2o, proto0, is_output_feature))) { - nat_free_session_data (sm, s0, thread_index, 0); + nat44_ed_free_session_data (sm, s0, thread_index, 0); nat_ed_session_delete (sm, s0, thread_index, 1); + s0 = 0; next[0] = NAT_NEXT_DROP; b0->error = node->errors[NAT_IN2OUT_ED_ERROR_TRNSL_FAILED]; goto trace0; } - if (PREDICT_TRUE (proto0 == NAT_PROTOCOL_TCP)) + if (PREDICT_TRUE (proto0 == IP_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); + thread_index, cntr_sw_if_index0, 1); + nat44_set_tcp_session_state_i2o ( + sm, now, s0, vnet_buffer (b0)->ip.reass.icmp_type_or_tcp_flags, + thread_index); } else { vlib_increment_simple_counter (&sm->counters.slowpath.in2out.udp, - thread_index, sw_if_index0, 1); + thread_index, cntr_sw_if_index0, 1); } /* Accounting */ @@ -1469,7 +1545,7 @@ nat44_ed_in2out_slow_path_node_fn_inline (vlib_main_t *vm, { nat_in2out_ed_trace_t *t = vlib_add_trace (vm, node, b0, sizeof (*t)); - t->sw_if_index = sw_if_index0; + t->sw_if_index = rx_sw_if_index0; t->next_index = next[0]; t->is_slow_path = 1; t->translation_error = translation_error; @@ -1481,6 +1557,7 @@ nat44_ed_in2out_slow_path_node_fn_inline (vlib_main_t *vm, clib_memcpy (&t->i2of, &s0->i2o, sizeof (t->i2of)); clib_memcpy (&t->o2if, &s0->o2i, sizeof (t->o2if)); t->translation_via_i2of = 1; + t->tcp_state = s0->tcp_state; } else @@ -1492,7 +1569,7 @@ nat44_ed_in2out_slow_path_node_fn_inline (vlib_main_t *vm, if (next[0] == NAT_NEXT_DROP) { vlib_increment_simple_counter (&sm->counters.slowpath.in2out.drops, - thread_index, sw_if_index0, 1); + thread_index, cntr_sw_if_index0, 1); } n_left_from--; diff --git a/src/plugins/nat/nat44-ed/nat44_ed_inlines.h b/src/plugins/nat/nat44-ed/nat44_ed_inlines.h index 0d75e736849..04e5236b7f9 100644 --- a/src/plugins/nat/nat44-ed/nat44_ed_inlines.h +++ b/src/plugins/nat/nat44-ed/nat44_ed_inlines.h @@ -12,6 +12,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + /** * @brief The NAT inline functions */ @@ -24,116 +25,53 @@ #include <vnet/fib/ip4_fib.h> #include <nat/lib/log.h> +#include <nat/lib/ipfix_logging.h> #include <nat/nat44-ed/nat44_ed.h> -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) +init_ed_k (clib_bihash_kv_16_8_t *kv, u32 l_addr, u16 l_port, u32 r_addr, + u16 r_port, u32 fib_index, ip_protocol_t proto) { - kv->key = calc_nat_key (addr, port, fib_index, proto); - kv->value = ~0ULL; + kv->key[0] = (u64) r_addr << 32 | l_addr; + kv->key[1] = + (u64) r_port << 48 | (u64) l_port << 32 | fib_index << 8 | proto; } 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_ed_kv (clib_bihash_kv_16_8_t *kv, u32 l_addr, u16 l_port, u32 r_addr, + u16 r_port, u32 fib_index, u8 proto, u32 thread_index, + u32 session_index) { - init_nat_k (kv, addr, port, fib_index, proto); + 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 void -init_nat_i2o_k (clib_bihash_kv_8_8_t *kv, snat_session_t *s) +nat44_ed_sm_init_i2o_kv (clib_bihash_kv_16_8_t *kv, u32 addr, u16 port, + u32 fib_index, u8 proto, u32 sm_index) { - return init_nat_k (kv, s->in2out.addr, s->in2out.port, s->in2out.fib_index, - s->nat_proto); + return init_ed_kv (kv, addr, port, 0, 0, fib_index, proto, 0, sm_index); } always_inline void -init_nat_i2o_kv (clib_bihash_kv_8_8_t *kv, snat_session_t *s, u32 thread_index, - u32 session_index) +nat44_ed_sm_init_o2i_kv (clib_bihash_kv_16_8_t *kv, u32 e_addr, u16 e_port, + u32 fib_index, u8 proto, u32 sm_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; + return init_ed_kv (kv, 0, 0, e_addr, e_port, fib_index, proto, 0, sm_index); } always_inline void -init_nat_o2i_k (clib_bihash_kv_8_8_t *kv, snat_session_t *s) +nat44_ed_sm_init_i2o_k (clib_bihash_kv_16_8_t *kv, u32 addr, u16 port, + u32 fib_index, u8 proto) { - return init_nat_k (kv, s->out2in.addr, s->out2in.port, s->out2in.fib_index, - s->nat_proto); + return nat44_ed_sm_init_i2o_kv (kv, addr, port, fib_index, proto, 0); } always_inline void -init_nat_o2i_kv (clib_bihash_kv_8_8_t *kv, snat_session_t *s, u32 thread_index, - u32 session_index) +nat44_ed_sm_init_o2i_k (clib_bihash_kv_16_8_t *kv, u32 e_addr, u16 e_port, + u32 fib_index, u8 proto) { - 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; + return nat44_ed_sm_init_o2i_kv (kv, e_addr, e_port, fib_index, proto, 0); } always_inline u32 @@ -187,13 +125,13 @@ nat_get_icmp_session_lookup_values (vlib_buffer_t *b, ip4_header_t *ip0, u16 *lookup_dport, u8 *lookup_protocol) { icmp46_header_t *icmp0; - icmp_echo_header_t *echo0, *inner_echo0 = 0; + nat_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); + echo0 = (nat_icmp_echo_header_t *) (icmp0 + 1); // avoid warning about unused variables in caller by setting to bogus values *lookup_sport = 0; @@ -215,18 +153,18 @@ nat_get_icmp_session_lookup_values (vlib_buffer_t *b, ip4_header_t *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)) + switch (inner_ip0->protocol) { - case NAT_PROTOCOL_ICMP: + case IP_PROTOCOL_ICMP: inner_icmp0 = (icmp46_header_t *) l4_header; - inner_echo0 = (icmp_echo_header_t *) (inner_icmp0 + 1); + inner_echo0 = (nat_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; + case IP_PROTOCOL_UDP: + case IP_PROTOCOL_TCP: + *lookup_sport = ((nat_tcp_udp_header_t *) l4_header)->dst_port; + *lookup_dport = ((nat_tcp_udp_header_t *) l4_header)->src_port; break; default: return NAT_IN2OUT_ED_ERROR_UNSUPPORTED_PROTOCOL; @@ -235,21 +173,29 @@ nat_get_icmp_session_lookup_values (vlib_buffer_t *b, ip4_header_t *ip0, return 0; } +always_inline int +nat44_ed_tcp_is_established (nat44_ed_tcp_state_e state) +{ + return state == NAT44_ED_TCP_STATE_ESTABLISHED ? 1 : 0; +} + always_inline u32 nat44_session_get_timeout (snat_main_t *sm, snat_session_t *s) { - switch (s->nat_proto) + switch (s->proto) { - case NAT_PROTOCOL_ICMP: + case IP_PROTOCOL_ICMP: + /* fallthrough */ + case IP_PROTOCOL_ICMP6: return sm->timeouts.icmp; - case NAT_PROTOCOL_UDP: + case IP_PROTOCOL_UDP: return sm->timeouts.udp; - case NAT_PROTOCOL_TCP: + case IP_PROTOCOL_TCP: { - if (s->state) - return sm->timeouts.tcp.transitory; - else + if (nat44_ed_tcp_is_established (s->tcp_state)) return sm->timeouts.tcp.established; + else + return sm->timeouts.tcp.transitory; } default: return sm->timeouts.udp; @@ -300,7 +246,7 @@ nat_ed_lru_insert (snat_main_per_thread_data_t *tsm, snat_session_t *s, 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, + init_ed_k (kv, f->match.saddr.as_u32, f->match.sport, f->match.daddr.as_u32, f->match.dport, f->match.fib_index, f->match.proto); } @@ -308,7 +254,7 @@ 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, + init_ed_kv (kv, f->match.saddr.as_u32, f->match.sport, f->match.daddr.as_u32, f->match.dport, f->match.fib_index, f->match.proto, thread_idx, session_idx); } @@ -348,6 +294,15 @@ nat_ed_ses_o2i_flow_hash_add_del (snat_main_t *sm, u32 thread_idx, else { nat_6t_flow_to_ed_kv (&kv, &s->o2i, thread_idx, s - tsm->sessions); + if (!(s->flags & SNAT_SESSION_FLAG_STATIC_MAPPING)) + { + if (nat44_ed_sm_o2i_lookup (sm, s->o2i.match.daddr, + s->o2i.match.dport, 0, + s->o2i.match.proto)) + { + return -1; + } + } nat_6t_l3_l4_csum_calc (&s->o2i); } ASSERT (thread_idx == s->thread_index); @@ -393,10 +348,9 @@ nat_lru_free_one_with_head (snat_main_t *sm, int thread_index, f64 now, 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)) + if (now >= sess_timeout_time) { - nat_free_session_data (sm, s, thread_index, 0); + nat44_ed_free_session_data (sm, s, thread_index, 0); nat_ed_session_delete (sm, s, thread_index, 0); return 1; } @@ -460,23 +414,16 @@ per_vrf_sessions_cleanup (u32 thread_index) per_vrf_sessions_t *per_vrf_sessions; u32 *to_free = 0, *i; - vec_foreach (per_vrf_sessions, tsm->per_vrf_sessions_vec) + pool_foreach (per_vrf_sessions, tsm->per_vrf_sessions_pool) { - 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 (per_vrf_sessions->expired && per_vrf_sessions->ses_count == 0) + vec_add1 (to_free, per_vrf_sessions - tsm->per_vrf_sessions_pool); } - if (vec_len (to_free)) + vec_foreach (i, to_free) { - vec_foreach (i, to_free) - { - vec_del1 (tsm->per_vrf_sessions_vec, *i); - } + per_vrf_sessions = pool_elt_at_index (tsm->per_vrf_sessions_pool, *i); + pool_put (tsm->per_vrf_sessions_pool, per_vrf_sessions); } vec_free (to_free); @@ -495,7 +442,7 @@ per_vrf_sessions_register_session (snat_session_t *s, u32 thread_index) // s->per_vrf_sessions_index == ~0 ... reuse of old session - vec_foreach (per_vrf_sessions, tsm->per_vrf_sessions_vec) + pool_foreach (per_vrf_sessions, tsm->per_vrf_sessions_pool) { // ignore already expired registrations if (per_vrf_sessions->expired) @@ -514,14 +461,13 @@ per_vrf_sessions_register_session (snat_session_t *s, u32 thread_index) } // create a new registration - vec_add2 (tsm->per_vrf_sessions_vec, per_vrf_sessions, 1); + pool_get (tsm->per_vrf_sessions_pool, per_vrf_sessions); 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; + s->per_vrf_sessions_index = per_vrf_sessions - tsm->per_vrf_sessions_pool; per_vrf_sessions->ses_count++; } @@ -537,7 +483,7 @@ per_vrf_sessions_unregister_session (snat_session_t *s, u32 thread_index) 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); + pool_elt_at_index (tsm->per_vrf_sessions_pool, s->per_vrf_sessions_index); ASSERT (per_vrf_sessions->ses_count != 0); @@ -557,7 +503,7 @@ per_vrf_sessions_is_expired (snat_session_t *s, u32 thread_index) 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); + pool_elt_at_index (tsm->per_vrf_sessions_pool, s->per_vrf_sessions_index); return per_vrf_sessions->expired; } @@ -754,100 +700,120 @@ is_interface_addr (snat_main_t *sm, vlib_node_runtime_t *node, } 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) +nat44_ed_session_reopen (u32 thread_index, snat_session_t *s) +{ + 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->proto, + nat44_ed_is_twice_nat_session (s)); + + nat_ipfix_logging_nat44_ses_delete ( + thread_index, s->in2out.addr.as_u32, s->out2in.addr.as_u32, s->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->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->proto, 0); + s->total_pkts = 0; + s->total_bytes = 0; +} + +/* + * "Some rise by SYN, and some by virtue FIN" - William Shakespeare + * TCP state tracking patterned after RFC 7857 (and RFC 6146, which is + * referenced by RFC 7857). In contrast to the state machine in RFC7857 we only + * transition to ESTABLISHED state after seeing a full 3-way handshake (SYNs + * and ACKs in both directions). RFC7857 as a means of protecting against + * spurious RSTs closing a session, goes back to ESTABLISHED if a data packet + * is received after the RST. This state machine will leave the state in + * transitory if RST is seen. Our implementation also goes beyond by supporting + * creation of a new session while old session is in transitory timeout after + * seeing FIN packets from both sides. + */ +always_inline void +nat44_set_tcp_session_state (snat_main_t *sm, f64 now, snat_session_t *ses, + u8 tcp_flags, u32 thread_index, + nat44_ed_dir_e dir) { 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)) + u8 old_flags = ses->tcp_flags[dir]; + ses->tcp_flags[dir] |= + tcp_flags & (TCP_FLAG_FIN | TCP_FLAG_SYN | TCP_FLAG_RST | TCP_FLAG_ACK); + if (old_flags == ses->tcp_flags[dir]) + return; + + u8 old_state = ses->tcp_state; + + switch (old_state) { - if (clib_net_to_host_u32 (tcp_ack_number) > ses->o2i_fin_seq) + case NAT44_ED_TCP_STATE_CLOSED: + // ESTABLISHED when a SYN and ACK is seen from both sides + if ((ses->tcp_flags[NAT44_ED_DIR_I2O] & + ses->tcp_flags[NAT44_ED_DIR_O2I]) == (TCP_FLAG_SYN | TCP_FLAG_ACK)) { - 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; - } + ses->tcp_state = NAT44_ED_TCP_STATE_ESTABLISHED; + ses->lru_head_index = tsm->tcp_estab_lru_head_index; } + break; + case NAT44_ED_TCP_STATE_ESTABLISHED: + // CLOSING when a FIN is seen from either side or session has been RST + if ((ses->tcp_flags[dir] & TCP_FLAG_FIN) || + (ses->tcp_flags[dir] & TCP_FLAG_RST)) + { + ses->tcp_state = NAT44_ED_TCP_STATE_CLOSING; + ses->tcp_flags[NAT44_ED_DIR_I2O] = 0; + ses->tcp_flags[NAT44_ED_DIR_O2I] = 0; + // need to update last heard otherwise session might get + // immediately timed out if it has been idle longer than + // transitory timeout + ses->last_heard = now; + ses->lru_head_index = tsm->tcp_trans_lru_head_index; + } + break; + case NAT44_ED_TCP_STATE_CLOSING: + // Allow a transitory session to reopen + if ((ses->tcp_flags[NAT44_ED_DIR_I2O] & + ses->tcp_flags[NAT44_ED_DIR_O2I]) == (TCP_FLAG_SYN | TCP_FLAG_ACK)) + { + nat44_ed_session_reopen (thread_index, ses); + ses->tcp_state = NAT44_ED_TCP_STATE_ESTABLISHED; + ses->lru_head_index = tsm->tcp_estab_lru_head_index; + } + break; } - - // 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; - } + if (old_state == ses->tcp_state) + return; + ses->last_lru_update = now; 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_i2o (snat_main_t *sm, f64 now, snat_session_t *ses, + u8 tcp_flags, u32 thread_index) +{ + return nat44_set_tcp_session_state (sm, now, ses, tcp_flags, thread_index, + NAT44_ED_DIR_I2O); +} + +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) + u8 tcp_flags, 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); + return nat44_set_tcp_session_state (sm, now, ses, tcp_flags, thread_index, + NAT44_ED_DIR_O2I); } always_inline void nat44_session_update_counters (snat_session_t *s, f64 now, uword bytes, u32 thread_index) { + // regardless of TCP state, reset the timer if data packet is seen. s->last_heard = now; s->total_pkts++; s->total_bytes += bytes; @@ -868,6 +834,19 @@ nat44_session_update_lru (snat_main_t *sm, snat_session_t *s, u32 thread_index) } } +static_always_inline int +nat44_ed_is_unk_proto (u8 proto) +{ + static const int lookup_table[256] = { + [IP_PROTOCOL_TCP] = 1, + [IP_PROTOCOL_UDP] = 1, + [IP_PROTOCOL_ICMP] = 1, + [IP_PROTOCOL_ICMP6] = 1, + }; + + return 1 - lookup_table[proto]; +} + #endif /* __included_nat44_ed_inlines_h__ */ /* diff --git a/src/plugins/nat/nat44-ed/nat44_ed_out2in.c b/src/plugins/nat/nat44-ed/nat44_ed_out2in.c index 186d1d6c004..fe4a41c5e08 100644 --- a/src/plugins/nat/nat44-ed/nat44_ed_out2in.c +++ b/src/plugins/nat/nat44-ed/nat44_ed_out2in.c @@ -25,7 +25,6 @@ #include <vnet/udp/udp_local.h> #include <vppinfra/error.h> -#include <nat/lib/nat_syslog.h> #include <nat/lib/ipfix_logging.h> #include <nat/nat44-ed/nat44_ed.h> @@ -42,7 +41,6 @@ typedef enum NAT_ED_SP_REASON_NO_REASON, NAT_ED_SP_REASON_LOOKUP_FAILED, NAT_ED_SP_REASON_VRF_EXPIRED, - NAT_ED_SP_TCP_CLOSED, NAT_ED_SP_SESS_EXPIRED, } nat_slow_path_reason_e; @@ -58,6 +56,7 @@ typedef struct u8 is_slow_path; u8 translation_via_i2of; u8 lookup_skipped; + u8 tcp_state; nat_slow_path_reason_e slow_path_reason; } nat44_ed_out2in_trace_t; @@ -73,8 +72,6 @@ format_slow_path_reason (u8 *s, va_list *args) return format (s, "slow path because lookup failed"); case NAT_ED_SP_REASON_VRF_EXPIRED: return format (s, "slow path because vrf expired"); - case NAT_ED_SP_TCP_CLOSED: - return format (s, "slow path because tcp closed"); case NAT_ED_SP_SESS_EXPIRED: return format (s, "slow path because session expired"); } @@ -108,14 +105,19 @@ format_nat44_ed_out2in_trace (u8 * s, va_list * args) { if (t->lookup_skipped) { - s = format (s, "\n lookup skipped - cached session index used"); + 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); } - s = format (s, "\n %U", format_slow_path_reason, t->slow_path_reason); + s = format (s, "\n %U", format_slow_path_reason, t->slow_path_reason); + } + if (IP_PROTOCOL_TCP == t->i2of.match.proto) + { + s = format (s, "\n TCP state: %U", format_nat44_ed_tcp_state, + t->tcp_state); } return s; @@ -123,12 +125,12 @@ format_nat44_ed_out2in_trace (u8 * s, va_list * args) 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) + 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); + init_ed_k (&kv, ip->src_address.as_u32, src_port, ip->dst_address.as_u32, + dst_port, rx_fib_index, ip->protocol); if (!clib_bihash_search_16_8 (&sm->flow_hash, &kv, &value)) return 1; @@ -142,8 +144,8 @@ static void create_bypass_for_fwd (snat_main_t *sm, vlib_buffer_t *b, 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, + ip_protocol_t proto, vlib_node_runtime_t *node, 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 @@ -180,10 +182,10 @@ icmp_out2in_ed_slow_path (snat_main_t *sm, vlib_buffer_t *b, ip4_header_t *ip, goto out; } - if (snat_static_mapping_match ( - vm, 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)) + if (snat_static_mapping_match (vm, ip->dst_address, lookup_sport, + rx_fib_index, 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) @@ -198,8 +200,7 @@ icmp_out2in_ed_slow_path (snat_main_t *sm, vlib_buffer_t *b, ip4_header_t *ip, } else { - if (next_src_nat (sm, ip, lookup_sport, lookup_dport, thread_index, - rx_fib_index)) + if (next_src_nat (sm, ip, lookup_sport, lookup_dport, rx_fib_index)) { next = NAT_NEXT_IN2OUT_ED_FAST_PATH; } @@ -230,8 +231,8 @@ icmp_out2in_ed_slow_path (snat_main_t *sm, vlib_buffer_t *b, ip4_header_t *ip, /* 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); + rx_fib_index, lookup_protocol, node, thread_index, 0, 0, + vlib_time_now (vm), m); if (!s) next = NAT_NEXT_DROP; @@ -266,44 +267,30 @@ out: 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) +nat44_ed_alloc_i2o_port (snat_main_t *sm, snat_address_t *a, snat_session_t *s, + ip4_address_t i2o_addr, u16 i2o_port, + u32 i2o_fib_index, ip_protocol_t proto, + u32 thread_index, u32 snat_thread_index, + ip4_address_t *outside_addr, u16 *outside_port) { - snat_main_t *sm = &snat_main; u32 portnum; - switch (proto) + for (int i = 0; i < ED_PORT_ALLOC_ATTEMPTS; ++i) { -#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; + portnum = (sm->port_per_thread * snat_thread_index) + + snat_random_port (0, sm->port_per_thread - 1) + + ED_USER_PORT_OFFSET; + portnum = clib_host_to_net_u16 (portnum); + nat_6t_i2o_flow_init (sm, thread_index, s, i2o_addr, i2o_port, a->addr, + portnum, i2o_fib_index, proto); + if (!nat_ed_ses_i2o_flow_hash_add_del (sm, thread_index, s, + 1 /* is_add */)) + { + *outside_addr = a->addr; + *outside_port = portnum; + return 0; + } } /* Totally out of translations to use... */ @@ -312,80 +299,56 @@ nat_alloc_addr_and_port_exact (snat_address_t * a, } 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) +nat44_ed_alloc_i2o_addr_and_port (snat_main_t *sm, snat_address_t *addresses, + snat_session_t *s, ip4_address_t i2o_addr, + u16 i2o_port, u32 i2o_fib_index, + ip_protocol_t proto, u32 thread_index, + u32 snat_thread_index, + ip4_address_t *outside_addr, + u16 *outside_port) { - snat_main_t *sm = &snat_main; snat_address_t *a, *ga = 0; - u32 portnum; int i; - for (i = 0; i < vec_len (addresses); i++) + if (vec_len (addresses) > 0) { - a = addresses + i; - switch (proto) + int s_addr_offset = i2o_addr.as_u32 % vec_len (addresses); + + for (i = s_addr_offset; i < vec_len (addresses); ++i) { -#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; + a = addresses + i; + if (a->fib_index == i2o_fib_index) + { + return nat44_ed_alloc_i2o_port ( + sm, a, s, i2o_addr, i2o_port, i2o_fib_index, proto, + thread_index, snat_thread_index, outside_addr, outside_port); + } + else if (a->fib_index == ~0) + { + ga = a; + } } - } - if (ga) - { - a = ga; - switch (proto) + for (i = 0; i < s_addr_offset; ++i) { -#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; + a = addresses + i; + if (a->fib_index == i2o_fib_index) + { + return nat44_ed_alloc_i2o_port ( + sm, a, s, i2o_addr, i2o_port, i2o_fib_index, proto, + thread_index, snat_thread_index, outside_addr, outside_port); + } + else if (a->fib_index == ~0) + { + ga = a; + } + } + + if (ga) + { + return nat44_ed_alloc_i2o_port ( + sm, a, s, i2o_addr, i2o_port, i2o_fib_index, proto, thread_index, + snat_thread_index, outside_addr, outside_port); } } @@ -398,23 +361,23 @@ 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, + ip_protocol_t proto, vlib_node_runtime_t *node, 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; 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))) + if (PREDICT_FALSE ( + nat44_ed_maximum_sessions_exceeded (sm, o2i_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); + s = nat_ed_session_alloc (sm, thread_index, now, proto); if (!s) { b->error = node->errors[NAT_OUT2IN_ED_ERROR_MAX_SESSIONS_EXCEEDED]; @@ -426,22 +389,21 @@ create_session_for_static_mapping_ed ( s->ext_host_addr.as_u32 = ip->src_address.as_u32; s->ext_host_port = - nat_proto == NAT_PROTOCOL_ICMP ? 0 : vnet_buffer (b)->ip.reass.l4_src_port; + proto == IP_PROTOCOL_ICMP ? 0 : vnet_buffer (b)->ip.reass.l4_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; + s->proto = proto; - if (NAT_PROTOCOL_ICMP == nat_proto) + if (IP_PROTOCOL_ICMP == 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); @@ -487,33 +449,23 @@ create_session_for_static_mapping_ed ( 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); + rc = nat44_ed_alloc_i2o_port ( + sm, filter, s, i2o_addr, i2o_port, i2o_fib_index, proto, + thread_index, tsm->snat_thread_index, &s->ext_host_nat_addr, + &s->ext_host_nat_port); 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); + rc = nat44_ed_alloc_i2o_addr_and_port ( + sm, sm->twice_nat_addresses, s, i2o_addr, i2o_port, i2o_fib_index, + proto, thread_index, tsm->snat_thread_index, &s->ext_host_nat_addr, + &s->ext_host_nat_port); } 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; } @@ -521,7 +473,7 @@ create_session_for_static_mapping_ed ( 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) + if (IP_PROTOCOL_ICMP == proto) { nat_6t_flow_icmp_id_rewrite_set (&s->o2i, s->ext_host_nat_port); } @@ -532,11 +484,8 @@ create_session_for_static_mapping_ed ( 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) + if (IP_PROTOCOL_ICMP == proto) { nat_6t_flow_icmp_id_rewrite_set (&s->i2o, s->ext_host_port); } @@ -544,10 +493,21 @@ create_session_for_static_mapping_ed ( { nat_6t_flow_dport_rewrite_set (&s->i2o, s->ext_host_port); } + + nat_6t_flow_saddr_rewrite_set (&s->i2o, o2i_addr.as_u32); + if (IP_PROTOCOL_ICMP == proto) + { + nat_6t_flow_icmp_id_rewrite_set (&s->i2o, o2i_port); + } + else + { + nat_6t_flow_sport_rewrite_set (&s->i2o, o2i_port); + } + nat_6t_l3_l4_csum_calc (&s->i2o); } else { - if (NAT_PROTOCOL_ICMP == nat_proto) + if (IP_PROTOCOL_ICMP == proto) { nat_6t_i2o_flow_init (sm, thread_index, s, i2o_addr, i2o_port, s->ext_host_addr, i2o_port, i2o_fib_index, @@ -559,10 +519,9 @@ create_session_for_static_mapping_ed ( 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) + if (IP_PROTOCOL_ICMP == proto) { nat_6t_flow_icmp_id_rewrite_set (&s->i2o, o2i_port); } @@ -581,19 +540,16 @@ create_session_for_static_mapping_ed ( 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_ipfix_logging_nat44_ses_create ( + thread_index, s->in2out.addr.as_u32, s->out2in.addr.as_u32, s->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)); + &s->ext_host_addr, s->ext_host_port, s->proto, + nat44_ed_is_twice_nat_session (s)); per_vrf_sessions_register_session (s, thread_index); @@ -636,8 +592,8 @@ create_bypass_for_fwd (snat_main_t *sm, vlib_buffer_t *b, snat_session_t *s, lookup_protocol = ip->protocol; } - init_ed_k (&kv, lookup_saddr, lookup_sport, lookup_daddr, lookup_dport, - rx_fib_index, lookup_protocol); + init_ed_k (&kv, lookup_saddr.as_u32, lookup_sport, lookup_daddr.as_u32, + lookup_dport, rx_fib_index, lookup_protocol); if (!clib_bihash_search_16_8 (&sm->flow_hash, &kv, &value)) { @@ -654,8 +610,6 @@ create_bypass_for_fwd (snat_main_t *sm, vlib_buffer_t *b, snat_session_t *s, } else { - u32 proto; - if (PREDICT_FALSE (nat44_ed_maximum_sessions_exceeded (sm, rx_fib_index, thread_index))) @@ -668,19 +622,12 @@ create_bypass_for_fwd (snat_main_t *sm, vlib_buffer_t *b, snat_session_t *s, 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->proto = ip->protocol; s->out2in.fib_index = rx_fib_index; s->in2out.addr = s->out2in.addr; s->in2out.port = s->out2in.port; @@ -702,10 +649,9 @@ create_bypass_for_fwd (snat_main_t *sm, vlib_buffer_t *b, snat_session_t *s, 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); + nat44_set_tcp_session_state_o2i ( + sm, now, s, vnet_buffer (b)->ip.reass.icmp_type_or_tcp_flags, + thread_index); } /* Accounting */ @@ -721,7 +667,6 @@ nat44_ed_out2in_slowpath_unknown_proto (snat_main_t *sm, vlib_buffer_t *b, 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; @@ -733,15 +678,13 @@ nat44_ed_out2in_slowpath_unknown_proto (snat_main_t *sm, vlib_buffer_t *b, 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)) + m = nat44_ed_sm_o2i_lookup (sm, ip->dst_address, 0, 0, ip->protocol); + if (!m) { 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) @@ -752,9 +695,7 @@ nat44_ed_out2in_slowpath_unknown_proto (snat_main_t *sm, vlib_buffer_t *b, } 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; @@ -816,10 +757,10 @@ nat44_ed_out2in_fast_path_node_fn_inline (vlib_main_t * vm, { vlib_buffer_t *b0; u32 sw_if_index0, rx_fib_index0; - nat_protocol_t proto0; + ip_protocol_t proto0; ip4_header_t *ip0; snat_session_t *s0 = 0; - clib_bihash_kv_16_8_t kv0, value0; + clib_bihash_kv_16_8_t kv0 = {}, value0; nat_translation_error_e translation_error = NAT_ED_TRNSL_ERR_SUCCESS; nat_slow_path_reason_e slow_path_reason = NAT_ED_SP_REASON_NO_REASON; nat_6t_flow_t *f = 0; @@ -865,9 +806,9 @@ nat44_ed_out2in_fast_path_node_fn_inline (vlib_main_t * vm, goto trace0; } - proto0 = ip_proto_to_nat_proto (ip0->protocol); + proto0 = ip0->protocol; - if (PREDICT_FALSE (proto0 == NAT_PROTOCOL_ICMP)) + if (PREDICT_FALSE (proto0 == IP_PROTOCOL_ICMP)) { if (vnet_buffer (b0)->ip.reass.icmp_type_or_tcp_flags != ICMP4_echo_request && @@ -916,8 +857,8 @@ nat44_ed_out2in_fast_path_node_fn_inline (vlib_main_t * vm, s0 = NULL; } - init_ed_k (&kv0, lookup.saddr, lookup.sport, lookup.daddr, lookup.dport, - lookup.fib_index, lookup.proto); + init_ed_k (&kv0, lookup.saddr.as_u32, lookup.sport, lookup.daddr.as_u32, + lookup.dport, lookup.fib_index, lookup.proto); // lookup flow if (clib_bihash_search_16_8 (&sm->flow_hash, &kv0, &value0)) @@ -938,30 +879,14 @@ nat44_ed_out2in_fast_path_node_fn_inline (vlib_main_t * vm, 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); + nat44_ed_free_session_data (sm, s0, thread_index, 0); nat_ed_session_delete (sm, s0, thread_index, 1); + s0 = 0; slow_path_reason = NAT_ED_SP_REASON_VRF_EXPIRED; 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 - slow_path_reason = NAT_ED_SP_TCP_CLOSED; - 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 = @@ -969,8 +894,9 @@ nat44_ed_out2in_fast_path_node_fn_inline (vlib_main_t * vm, if (now >= sess_timeout_time) { // session is closed, go slow path - nat_free_session_data (sm, s0, thread_index, 0); + nat44_ed_free_session_data (sm, s0, thread_index, 0); nat_ed_session_delete (sm, s0, thread_index, 1); + s0 = 0; slow_path_reason = NAT_ED_SP_SESS_EXPIRED; next[0] = NAT_NEXT_OUT2IN_ED_SLOW_PATH; goto trace0; @@ -992,7 +918,7 @@ nat44_ed_out2in_fast_path_node_fn_inline (vlib_main_t * vm, * be able to use dhcp client on the outside interface */ if (PREDICT_FALSE ( - proto0 == NAT_PROTOCOL_UDP && + proto0 == IP_PROTOCOL_UDP && (vnet_buffer (b0)->ip.reass.l4_dst_port == clib_host_to_net_u16 (UDP_DST_PORT_dhcp_to_client)))) { @@ -1017,8 +943,9 @@ nat44_ed_out2in_fast_path_node_fn_inline (vlib_main_t * vm, // 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); + nat44_ed_free_session_data (sm, s0, thread_index, 0); nat_ed_session_delete (sm, s0, thread_index, 1); + s0 = 0; next[0] = NAT_NEXT_DROP; b0->error = node->errors[NAT_OUT2IN_ED_ERROR_TRNSL_FAILED]; goto trace0; @@ -1037,27 +964,23 @@ nat44_ed_out2in_fast_path_node_fn_inline (vlib_main_t * vm, switch (proto0) { - case NAT_PROTOCOL_TCP: + case IP_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: + case IP_PROTOCOL_UDP: vlib_increment_simple_counter (&sm->counters.fastpath.out2in.udp, thread_index, sw_if_index0, 1); break; - case NAT_PROTOCOL_ICMP: + case IP_PROTOCOL_ICMP: vlib_increment_simple_counter (&sm->counters.fastpath.out2in.icmp, thread_index, sw_if_index0, 1); break; - case NAT_PROTOCOL_OTHER: + default: vlib_increment_simple_counter (&sm->counters.fastpath.out2in.other, thread_index, sw_if_index0, 1); break; @@ -1090,6 +1013,7 @@ nat44_ed_out2in_fast_path_node_fn_inline (vlib_main_t * vm, 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); + t->tcp_state = s0->tcp_state; } else { @@ -1135,12 +1059,12 @@ nat44_ed_out2in_slow_path_node_fn_inline (vlib_main_t * vm, { vlib_buffer_t *b0; u32 sw_if_index0, rx_fib_index0; - nat_protocol_t proto0; + ip_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; + clib_bihash_kv_16_8_t kv0 = {}, value0; lb_nat_type_t lb_nat0; twice_nat_type_t twice_nat0; u8 identity_nat0; @@ -1171,9 +1095,9 @@ nat44_ed_out2in_slow_path_node_fn_inline (vlib_main_t * vm, udp0 = ip4_next_header (ip0); icmp0 = (icmp46_header_t *) udp0; - proto0 = ip_proto_to_nat_proto (ip0->protocol); + proto0 = ip0->protocol; - if (PREDICT_FALSE (proto0 == NAT_PROTOCOL_OTHER)) + if (PREDICT_FALSE (nat44_ed_is_unk_proto (proto0))) { s0 = nat44_ed_out2in_slowpath_unknown_proto ( sm, b0, ip0, rx_fib_index0, thread_index, now, vm, node); @@ -1198,7 +1122,7 @@ nat44_ed_out2in_slow_path_node_fn_inline (vlib_main_t * vm, goto trace0; } - if (PREDICT_FALSE (proto0 == NAT_PROTOCOL_ICMP)) + if (PREDICT_FALSE (proto0 == IP_PROTOCOL_ICMP)) { next[0] = icmp_out2in_ed_slow_path (sm, b0, ip0, icmp0, sw_if_index0, rx_fib_index0, node, @@ -1215,15 +1139,19 @@ nat44_ed_out2in_slow_path_node_fn_inline (vlib_main_t * vm, goto trace0; } - vlib_increment_simple_counter (&sm->counters.slowpath.out2in.icmp, - thread_index, sw_if_index0, 1); + if (NAT_NEXT_DROP != next[0]) + { + 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); + init_ed_k ( + &kv0, ip0->src_address.as_u32, vnet_buffer (b0)->ip.reass.l4_src_port, + ip0->dst_address.as_u32, 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)) @@ -1232,13 +1160,6 @@ nat44_ed_out2in_slow_path_node_fn_inline (vlib_main_t * vm, 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) @@ -1247,19 +1168,18 @@ nat44_ed_out2in_slow_path_node_fn_inline (vlib_main_t * vm, destination address and port in packet */ if (snat_static_mapping_match ( - vm, 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)) + vm, 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)))) + if (PREDICT_FALSE ( + proto0 == IP_PROTOCOL_UDP && + (vnet_buffer (b0)->ip.reass.l4_dst_port == + clib_host_to_net_u16 (UDP_DST_PORT_dhcp_to_client)))) { goto trace0; } @@ -1272,10 +1192,9 @@ nat44_ed_out2in_slow_path_node_fn_inline (vlib_main_t * vm, } 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)) + if (next_src_nat ( + sm, ip0, vnet_buffer (b0)->ip.reass.l4_src_port, + vnet_buffer (b0)->ip.reass.l4_dst_port, rx_fib_index0)) { next[0] = NAT_NEXT_IN2OUT_ED_FAST_PATH; } @@ -1291,9 +1210,9 @@ nat44_ed_out2in_slow_path_node_fn_inline (vlib_main_t * vm, 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)) + if ((proto0 == IP_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; @@ -1301,16 +1220,10 @@ nat44_ed_out2in_slow_path_node_fn_inline (vlib_main_t * vm, } /* 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); + 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, thread_index, twice_nat0, lb_nat0, now, m); if (!s0) { next[0] = NAT_NEXT_DROP; @@ -1326,17 +1239,13 @@ nat44_ed_out2in_slow_path_node_fn_inline (vlib_main_t * vm, goto trace0; } - if (PREDICT_TRUE (proto0 == NAT_PROTOCOL_TCP)) + if (PREDICT_TRUE (proto0 == IP_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 @@ -1369,6 +1278,7 @@ nat44_ed_out2in_slow_path_node_fn_inline (vlib_main_t * vm, 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->tcp_state = s0->tcp_state; } else { diff --git a/src/plugins/nat/nat44-ed/tcp_conn_track.rst b/src/plugins/nat/nat44-ed/tcp_conn_track.rst new file mode 100644 index 00000000000..faf0dec8b06 --- /dev/null +++ b/src/plugins/nat/nat44-ed/tcp_conn_track.rst @@ -0,0 +1,65 @@ +NAT44ED TCP connection tracking +=============================== + +TCP connection tracking in endpoint-dependent NAT is based on RFC 7857 +and RFC 6146, which RFC 7857 references. + +See RFC 7857 for the original graph - our graph is slightly different, +allowing creation of new session, while an old session is in transitory +timeout after seeing FIN packets from both sides: + +After discussion on vpp-dev and with Andrew Yourtschenko we agreed that +it's friendly behaviour to allow creating a new session while the old +one is closed and in transitory timeout. The alternative means VPP is +insisting that a 5-tuple connection cannot be created while an old one +is finished and timing out. There is no apparent reason why our change +would break anything and we agreed that it could only help users. + +:: + + + +------------transitory timeout----------------+ + | | + | +-------------+ | + | session created---->+ CLOSED | | + | +-------------+ | + | | | | ++-----+ | SYN SYN | +| v v IN2OUT OUT2IN | +| +->session removed | | | +| | ^ ^ ^ ^ ^ v v | +| | | | | | | +-------+ +-------+ | +| | | | | | +----transitory timeout---+SYN_I2O| |SYN_O2I+--+ +| | | | | | +---------+ |-------| |-------| +| | | | | +-transitory---+RST_TRANS| | | +| | | | | timeout +---------+ SYN SYN +| | | | | | ^ OUT2IN IN2OUT +| | | | | | | | | +| | | | | | | v v +| | | | | | | +-----------+ +| | | | | | +--RST----+ESTABLISHED+<-SYN IN2OUT-+ +| | | | | | +-----------+ | +| | | | | +---data pkt-----^ | | | ^ | +| | | | | | | | | | +| | | | +----established timeout---------------+ | | | | +| | | | | | | | +| | | | +-----FIN IN2OUT---------+ | | | +| | | | v | | | +| | | | +-------+ +--FIN OUT2IN----+ | | +| | | +--established---+FIN_I2O| | | | +| | | timeout +-------+ v +-SYN OUT2IN-+ | +| | | | +-------+ | | +| | +----established-------------+FIN_O2I| +--------------+ | +| | timeout | +-------+ |REOPEN_SYN_I2O| +--------------+ +| | | | +--------------+ |REOPEN_SYN_O2I| +| | FIN FIN ^ | +--------------+ +| | OUT2IN IN2OUT | | ^ | +| | | | | | | | +| | v v | | | | +| | +-------------+ | | | | +| +--transitory timeout---+ FIN_TRANS +-SYN IN2OUT-+ | | | +| +-------------+ | | | +| | | | | +| +--------SYN OUT2IN----|-----------+ | +| v | ++------------------transitory timeout-------------------+<-------------+ |