From 2ba92e32e0197f676dd905e5edcb4ff3e1bec241 Mon Sep 17 00:00:00 2001 From: Matus Fabian Date: Mon, 21 Aug 2017 07:05:03 -0700 Subject: NAT: Rename snat plugin to nat (VPP-955) Change-Id: I30a7e3da7a4efc6038a91e27b48045d4b07e2764 Signed-off-by: Matus Fabian --- src/plugins/nat/nat.h | 541 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 541 insertions(+) create mode 100644 src/plugins/nat/nat.h (limited to 'src/plugins/nat/nat.h') diff --git a/src/plugins/nat/nat.h b/src/plugins/nat/nat.h new file mode 100644 index 00000000..04c466dc --- /dev/null +++ b/src/plugins/nat/nat.h @@ -0,0 +1,541 @@ + +/* + * nat.h - NAT plugin definitions + * + * Copyright (c) 2016 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef __included_nat_h__ +#define __included_nat_h__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#define SNAT_UDP_TIMEOUT 300 +#define SNAT_UDP_TIMEOUT_MIN 120 +#define SNAT_TCP_TRANSITORY_TIMEOUT 240 +#define SNAT_TCP_ESTABLISHED_TIMEOUT 7440 +#define SNAT_TCP_INCOMING_SYN 6 +#define SNAT_ICMP_TIMEOUT 60 + +#define SNAT_FLAG_HAIRPINNING (1 << 0) + +/* Key */ +typedef struct { + union + { + struct + { + ip4_address_t addr; + u16 port; + u16 protocol:3, + fib_index:13; + }; + u64 as_u64; + }; +} snat_session_key_t; + +typedef struct { + union + { + struct + { + ip4_address_t l_addr; + ip4_address_t r_addr; + u32 fib_index; + u8 proto; + u8 rsvd[3]; + }; + u64 as_u64[2]; + }; +} snat_unk_proto_ses_key_t; + +typedef struct { + union + { + struct + { + ip4_address_t ext_host_addr; + u16 ext_host_port; + u16 out_port; + }; + u64 as_u64; + }; +} snat_det_out_key_t; + +typedef struct { + union + { + struct + { + ip4_address_t addr; + u32 fib_index; + }; + u64 as_u64; + }; +} snat_user_key_t; + +typedef struct { + union + { + struct + { + ip4_address_t addr; + u16 port; + u16 fib_index; + }; + u64 as_u64; + }; +} snat_worker_key_t; + + +#define foreach_snat_protocol \ + _(UDP, 0, udp, "udp") \ + _(TCP, 1, tcp, "tcp") \ + _(ICMP, 2, icmp, "icmp") + +typedef enum { +#define _(N, i, n, s) SNAT_PROTOCOL_##N = i, + foreach_snat_protocol +#undef _ +} snat_protocol_t; + + +#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_LAST_ACK, "tcp-last-ack") \ + _(7, ICMP_ACTIVE, "icmp-active") + +typedef enum { +#define _(v, N, s) SNAT_SESSION_##N = v, + foreach_snat_session_state +#undef _ +} snat_session_state_t; + + +#define SNAT_SESSION_FLAG_STATIC_MAPPING 1 +#define SNAT_SESSION_FLAG_UNKNOWN_PROTO 2 + +typedef CLIB_PACKED(struct { + snat_session_key_t out2in; /* 0-15 */ + + snat_session_key_t in2out; /* 16-31 */ + + u32 flags; /* 32-35 */ + + /* per-user translations */ + u32 per_user_index; /* 36-39 */ + + u32 per_user_list_head_index; /* 40-43 */ + + /* Last heard timer */ + f64 last_heard; /* 44-51 */ + + u64 total_bytes; /* 52-59 */ + + u32 total_pkts; /* 60-63 */ + + /* Outside address */ + u32 outside_address_index; /* 64-67 */ + + /* External host address */ + ip4_address_t ext_host_addr; /* 68-71 */ + +}) snat_session_t; + + +typedef struct { + ip4_address_t addr; + u32 fib_index; + u32 sessions_per_user_list_head_index; + u32 nsessions; + u32 nstaticsessions; +} snat_user_t; + +typedef struct { + ip4_address_t addr; + u32 fib_index; +#define _(N, i, n, s) \ + u32 busy_##n##_ports; \ + uword * busy_##n##_port_bitmap; + foreach_snat_protocol +#undef _ +} snat_address_t; + +typedef struct { + u16 in_port; + snat_det_out_key_t out; + u8 state; + u32 expire; +} snat_det_session_t; + +typedef struct { + ip4_address_t in_addr; + u8 in_plen; + ip4_address_t out_addr; + u8 out_plen; + u32 sharing_ratio; + u16 ports_per_host; + u32 ses_num; + /* vector of sessions */ + snat_det_session_t * sessions; +} snat_det_map_t; + +typedef struct { + ip4_address_t local_addr; + ip4_address_t external_addr; + u16 local_port; + u16 external_port; + u8 addr_only; + u32 vrf_id; + u32 fib_index; + snat_protocol_t proto; +} snat_static_mapping_t; + +typedef struct { + u32 sw_if_index; + u8 is_inside; +} snat_interface_t; + +typedef struct { + ip4_address_t l_addr; + u16 l_port; + u16 e_port; + u32 sw_if_index; + u32 vrf_id; + snat_protocol_t proto; + int addr_only; + int is_add; +} snat_static_map_resolve_t; + +typedef struct { + /* User pool */ + snat_user_t * users; + + /* Session pool */ + snat_session_t * sessions; + + /* Pool of doubly-linked list elements */ + dlist_elt_t * list_pool; + + u32 snat_thread_index; +} snat_main_per_thread_data_t; + +struct snat_main_s; + +typedef u32 snat_icmp_match_function_t (struct snat_main_s *sm, + vlib_node_runtime_t *node, + u32 thread_index, + vlib_buffer_t *b0, + u8 *p_proto, + snat_session_key_t *p_value, + u8 *p_dont_translate, + void *d, + void *e); + +typedef u32 (snat_get_worker_function_t) (ip4_header_t * ip, u32 rx_fib_index); + +typedef struct snat_main_s { + /* Main lookup tables */ + clib_bihash_8_8_t out2in; + clib_bihash_8_8_t in2out; + + /* Unknown protocol sessions lookup tables */ + clib_bihash_16_8_t out2in_unk_proto; + clib_bihash_16_8_t in2out_unk_proto; + + /* Find-a-user => src address lookup */ + clib_bihash_8_8_t user_hash; + + /* Non-translated packets worker lookup => src address + VRF */ + clib_bihash_8_8_t worker_by_in; + + /* Translated packets worker lookup => IP address + port number */ + clib_bihash_8_8_t worker_by_out; + + snat_icmp_match_function_t * icmp_match_in2out_cb; + snat_icmp_match_function_t * icmp_match_out2in_cb; + + u32 num_workers; + u32 first_worker_index; + u32 next_worker; + u32 * workers; + snat_get_worker_function_t * worker_in2out_cb; + snat_get_worker_function_t * worker_out2in_cb; + u16 port_per_thread; + u32 num_snat_thread; + + /* Per thread data */ + snat_main_per_thread_data_t * per_thread_data; + + /* Find a static mapping by local */ + clib_bihash_8_8_t static_mapping_by_local; + + /* Find a static mapping by external */ + clib_bihash_8_8_t static_mapping_by_external; + + /* Static mapping pool */ + snat_static_mapping_t * static_mappings; + + /* Interface pool */ + snat_interface_t * interfaces; + snat_interface_t * output_feature_interfaces; + + /* Vector of outside addresses */ + snat_address_t * addresses; + + /* sw_if_indices whose intfc addresses should be auto-added */ + u32 * auto_add_sw_if_indices; + + /* vector of interface address static mappings to resolve. */ + snat_static_map_resolve_t *to_resolve; + + /* Randomize port allocation order */ + u32 random_seed; + + /* Worker handoff index */ + u32 fq_in2out_index; + u32 fq_in2out_output_index; + u32 fq_out2in_index; + + /* in2out and out2in node index */ + u32 in2out_node_index; + u32 in2out_output_node_index; + u32 out2in_node_index; + + /* Deterministic NAT */ + snat_det_map_t * det_maps; + + /* Config parameters */ + u8 static_mapping_only; + u8 static_mapping_connection_tracking; + u8 deterministic; + u32 translation_buckets; + u32 translation_memory_size; + u32 user_buckets; + u32 user_memory_size; + u32 max_translations_per_user; + u32 outside_vrf_id; + u32 outside_fib_index; + u32 inside_vrf_id; + u32 inside_fib_index; + + /* tenant VRF aware address pool activation flag */ + u8 vrf_mode; + + /* values of various timeouts */ + u32 udp_timeout; + u32 tcp_established_timeout; + u32 tcp_transitory_timeout; + u32 icmp_timeout; + + /* API message ID base */ + u16 msg_id_base; + + /* convenience */ + vlib_main_t * vlib_main; + vnet_main_t * vnet_main; + ip4_main_t * ip4_main; + ip_lookup_main_t * ip4_lookup_main; + api_main_t * api_main; +} snat_main_t; + +extern snat_main_t snat_main; +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_fast_node; +extern vlib_node_registration_t snat_out2in_fast_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 snat_det_in2out_node; +extern vlib_node_registration_t snat_det_out2in_node; +extern vlib_node_registration_t snat_hairpin_dst_node; +extern vlib_node_registration_t snat_hairpin_src_node; + +void snat_free_outside_address_and_port (snat_main_t * sm, + snat_session_key_t * k, + u32 address_index); + +int snat_alloc_outside_address_and_port (snat_main_t * sm, + u32 fib_index, + u32 thread_index, + snat_session_key_t * k, + u32 * address_indexp); + +int snat_static_mapping_match (snat_main_t * sm, + snat_session_key_t match, + snat_session_key_t * mapping, + u8 by_external, + u8 *is_addr_only); + +void snat_add_del_addr_to_fib (ip4_address_t * addr, + u8 p_len, + u32 sw_if_index, + int is_add); + +format_function_t format_snat_user; + +typedef struct { + u32 cached_sw_if_index; + u32 cached_ip4_address; +} snat_runtime_t; + +/** \brief Check if SNAT session is created from static mapping. + @param s SNAT session + @return 1 if SNAT session is created from static mapping otherwise 0 +*/ +#define snat_is_session_static(s) s->flags & SNAT_SESSION_FLAG_STATIC_MAPPING + +/** \brief Check if SNAT session for unknown protocol. + @param s SNAT session + @return 1 if SNAT session for unknown protocol otherwise 0 +*/ +#define snat_is_unk_proto_session(s) s->flags & SNAT_SESSION_FLAG_UNKNOWN_PROTO + +/* + * 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; + +always_inline u32 +ip_proto_to_snat_proto (u8 ip_proto) +{ + u32 snat_proto = ~0; + + snat_proto = (ip_proto == IP_PROTOCOL_UDP) ? SNAT_PROTOCOL_UDP : snat_proto; + snat_proto = (ip_proto == IP_PROTOCOL_TCP) ? SNAT_PROTOCOL_TCP : snat_proto; + snat_proto = (ip_proto == IP_PROTOCOL_ICMP) ? SNAT_PROTOCOL_ICMP : snat_proto; + snat_proto = (ip_proto == IP_PROTOCOL_ICMP6) ? SNAT_PROTOCOL_ICMP : snat_proto; + + return snat_proto; +} + +always_inline u8 +snat_proto_to_ip_proto (snat_protocol_t snat_proto) +{ + u8 ip_proto = ~0; + + ip_proto = (snat_proto == SNAT_PROTOCOL_UDP) ? IP_PROTOCOL_UDP : ip_proto; + ip_proto = (snat_proto == SNAT_PROTOCOL_TCP) ? IP_PROTOCOL_TCP : ip_proto; + ip_proto = (snat_proto == SNAT_PROTOCOL_ICMP) ? IP_PROTOCOL_ICMP : ip_proto; + + return ip_proto; +} + +typedef struct { + u16 src_port, dst_port; +} tcp_udp_header_t; + +u32 icmp_match_in2out_fast(snat_main_t *sm, vlib_node_runtime_t *node, + u32 thread_index, vlib_buffer_t *b0, u8 *p_proto, + snat_session_key_t *p_value, + u8 *p_dont_translate, void *d, void *e); +u32 icmp_match_in2out_slow(snat_main_t *sm, vlib_node_runtime_t *node, + u32 thread_index, vlib_buffer_t *b0, u8 *p_proto, + snat_session_key_t *p_value, + u8 *p_dont_translate, void *d, void *e); +u32 icmp_match_in2out_det(snat_main_t *sm, vlib_node_runtime_t *node, + u32 thread_index, vlib_buffer_t *b0, u8 *p_proto, + snat_session_key_t *p_value, + u8 *p_dont_translate, void *d, void *e); +u32 icmp_match_out2in_fast(snat_main_t *sm, vlib_node_runtime_t *node, + u32 thread_index, vlib_buffer_t *b0, u8 *p_proto, + snat_session_key_t *p_value, + u8 *p_dont_translate, void *d, void *e); +u32 icmp_match_out2in_slow(snat_main_t *sm, vlib_node_runtime_t *node, + u32 thread_index, vlib_buffer_t *b0, u8 *p_proto, + snat_session_key_t *p_value, + u8 *p_dont_translate, void *d, void *e); +u32 icmp_match_out2in_det(snat_main_t *sm, vlib_node_runtime_t *node, + u32 thread_index, vlib_buffer_t *b0, u8 *p_proto, + snat_session_key_t *p_value, + u8 *p_dont_translate, void *d, void *e); +void increment_v4_address(ip4_address_t * a); +void snat_add_address(snat_main_t *sm, ip4_address_t *addr, u32 vrf_id); +int snat_del_address(snat_main_t *sm, ip4_address_t addr, u8 delete_sm); +int snat_add_static_mapping(ip4_address_t l_addr, ip4_address_t e_addr, + u16 l_port, u16 e_port, u32 vrf_id, int addr_only, + u32 sw_if_index, snat_protocol_t proto, int is_add); +clib_error_t * snat_api_init(vlib_main_t * vm, snat_main_t * sm); +int snat_set_workers (uword * bitmap); +int snat_interface_add_del(u32 sw_if_index, u8 is_inside, int is_del); +int snat_interface_add_del_output_feature(u32 sw_if_index, u8 is_inside, + int is_del); +int snat_add_interface_address(snat_main_t *sm, u32 sw_if_index, int is_del); +uword unformat_snat_protocol(unformat_input_t * input, va_list * args); +u8 * format_snat_protocol(u8 * s, va_list * args); + +static_always_inline u8 +icmp_is_error_message (icmp46_header_t * icmp) +{ + switch(icmp->type) + { + case ICMP4_destination_unreachable: + case ICMP4_time_exceeded: + case ICMP4_parameter_problem: + case ICMP4_source_quench: + case ICMP4_redirect: + case ICMP4_alternate_host_address: + return 1; + } + return 0; +} + +static_always_inline u8 +is_interface_addr(snat_main_t *sm, vlib_node_runtime_t *node, u32 sw_if_index0, + u32 ip4_addr) +{ + snat_runtime_t *rt = (snat_runtime_t *) node->runtime_data; + ip4_address_t * first_int_addr; + + if (PREDICT_FALSE(rt->cached_sw_if_index != sw_if_index0)) + { + first_int_addr = + ip4_interface_first_address (sm->ip4_main, sw_if_index0, + 0 /* just want the address */); + rt->cached_sw_if_index = sw_if_index0; + if (first_int_addr) + rt->cached_ip4_address = first_int_addr->as_u32; + else + rt->cached_ip4_address = 0; + } + + if (PREDICT_FALSE(ip4_addr == rt->cached_ip4_address)) + return 1; + else + return 0; +} + +#endif /* __included_nat_h__ */ -- cgit 1.2.3-korg From 704018cf117b6667f08b09d6db5fbec105bf6d57 Mon Sep 17 00:00:00 2001 From: Matus Fabian Date: Mon, 4 Sep 2017 02:17:18 -0700 Subject: NAT: Destination NAT44 with load-balancing (VPP-954) added load-balancing static mappings with unequal load support Change-Id: Ie505e41f24d46f812b94dd28bdafe3dc170a6060 Signed-off-by: Matus Fabian --- src/plugins/nat/in2out.c | 253 ++++++++++++++++++++++++--- src/plugins/nat/nat.api | 33 ++++ src/plugins/nat/nat.c | 430 ++++++++++++++++++++++++++++++++++++++++++---- src/plugins/nat/nat.h | 23 ++- src/plugins/nat/nat_api.c | 137 ++++++++++++++- src/plugins/nat/out2in.c | 205 ++++++++++++++++++++-- test/test_nat.py | 101 +++++++++++ test/vpp_papi_provider.py | 30 ++++ 8 files changed, 1142 insertions(+), 70 deletions(-) (limited to 'src/plugins/nat/nat.h') diff --git a/src/plugins/nat/in2out.c b/src/plugins/nat/in2out.c index bb186393..c51d4fb4 100644 --- a/src/plugins/nat/in2out.c +++ b/src/plugins/nat/in2out.c @@ -314,23 +314,25 @@ static u32 slow_path (snat_main_t *sm, vlib_buffer_t *b0, if (snat_is_unk_proto_session (s)) { clib_bihash_kv_16_8_t up_kv; - snat_unk_proto_ses_key_t key; + nat_ed_ses_key_t key; /* Remove from lookup tables */ key.l_addr = s->in2out.addr; key.r_addr = s->ext_host_addr; key.fib_index = s->in2out.fib_index; key.proto = s->in2out.port; + key.rsvd = 0; + key.l_port = 0; up_kv.key[0] = key.as_u64[0]; up_kv.key[1] = key.as_u64[1]; - if (clib_bihash_add_del_16_8 (&sm->in2out_unk_proto, &up_kv, 0)) + if (clib_bihash_add_del_16_8 (&sm->in2out_ed, &up_kv, 0)) clib_warning ("in2out key del failed"); key.l_addr = s->out2in.addr; key.fib_index = s->out2in.fib_index; up_kv.key[0] = key.as_u64[0]; up_kv.key[1] = key.as_u64[1]; - if (clib_bihash_add_del_16_8 (&sm->out2in_unk_proto, &up_kv, 0)) + if (clib_bihash_add_del_16_8 (&sm->out2in_ed, &up_kv, 0)) clib_warning ("out2in key del failed"); } else @@ -1033,7 +1035,7 @@ snat_hairpinning_unknown_proto (snat_main_t *sm, u32 old_addr, new_addr = 0, ti = 0; clib_bihash_kv_8_8_t kv, value; clib_bihash_kv_16_8_t s_kv, s_value; - snat_unk_proto_ses_key_t key; + nat_ed_ses_key_t key; snat_session_key_t m_key; snat_worker_key_t w_key; snat_static_mapping_t *m; @@ -1045,10 +1047,11 @@ snat_hairpinning_unknown_proto (snat_main_t *sm, key.r_addr.as_u32 = ip->src_address.as_u32; key.fib_index = sm->outside_fib_index; key.proto = ip->protocol; - key.rsvd[0] = key.rsvd[1] = key.rsvd[2] = 0; + key.rsvd = 0; + key.l_port = 0; s_kv.key[0] = key.as_u64[0]; s_kv.key[1] = key.as_u64[1]; - if (clib_bihash_search_16_8 (&sm->out2in_unk_proto, &s_kv, &s_value)) + if (clib_bihash_search_16_8 (&sm->out2in_ed, &s_kv, &s_value)) { m_key.addr = ip->dst_address; m_key.fib_index = sm->outside_fib_index; @@ -1110,7 +1113,7 @@ snat_in2out_unknown_proto (snat_main_t *sm, snat_main_per_thread_data_t *tsm = &sm->per_thread_data[thread_index]; u32 elt_index, head_index, ses_index, oldest_index; snat_session_t * s; - snat_unk_proto_ses_key_t key; + nat_ed_ses_key_t key; u32 address_index = ~0; int i; u8 is_sm = 0; @@ -1121,11 +1124,12 @@ snat_in2out_unknown_proto (snat_main_t *sm, key.r_addr = ip->dst_address; key.fib_index = rx_fib_index; key.proto = ip->protocol; - key.rsvd[0] = key.rsvd[1] = key.rsvd[2] = 0; + key.rsvd = 0; + key.l_port = 0; s_kv.key[0] = key.as_u64[0]; s_kv.key[1] = key.as_u64[1]; - if (!clib_bihash_search_16_8 (&sm->in2out_unk_proto, &s_kv, &s_value)) + if (!clib_bihash_search_16_8 (&sm->in2out_ed, &s_kv, &s_value)) { s = pool_elt_at_index (tsm->sessions, s_value.value); new_addr = ip->src_address.as_u32 = s->out2in.addr.as_u32; @@ -1202,7 +1206,7 @@ snat_in2out_unknown_proto (snat_main_t *sm, key.l_addr.as_u32 = new_addr; s_kv.key[0] = key.as_u64[0]; s_kv.key[1] = key.as_u64[1]; - if (clib_bihash_search_16_8 (&sm->out2in_unk_proto, &s_kv, &s_value)) + if (clib_bihash_search_16_8 (&sm->out2in_ed, &s_kv, &s_value)) break; goto create_ses; @@ -1215,7 +1219,7 @@ snat_in2out_unknown_proto (snat_main_t *sm, key.l_addr.as_u32 = sm->addresses[i].addr.as_u32; s_kv.key[0] = key.as_u64[0]; s_kv.key[1] = key.as_u64[1]; - if (clib_bihash_search_16_8 (&sm->out2in_unk_proto, &s_kv, &s_value)) + if (clib_bihash_search_16_8 (&sm->out2in_ed, &s_kv, &s_value)) { new_addr = ip->src_address.as_u32 = key.l_addr.as_u32; address_index = i; @@ -1259,14 +1263,14 @@ create_ses: key.proto = s->in2out.port; s_kv.key[0] = key.as_u64[0]; s_kv.key[1] = key.as_u64[1]; - if (clib_bihash_add_del_16_8 (&sm->in2out_unk_proto, &s_kv, 0)) + if (clib_bihash_add_del_16_8 (&sm->in2out_ed, &s_kv, 0)) clib_warning ("in2out key del failed"); key.l_addr = s->out2in.addr; key.fib_index = s->out2in.fib_index; s_kv.key[0] = key.as_u64[0]; s_kv.key[1] = key.as_u64[1]; - if (clib_bihash_add_del_16_8 (&sm->out2in_unk_proto, &s_kv, 0)) + if (clib_bihash_add_del_16_8 (&sm->out2in_ed, &s_kv, 0)) clib_warning ("out2in key del failed"); } else @@ -1333,14 +1337,14 @@ create_ses: s_kv.key[0] = key.as_u64[0]; s_kv.key[1] = key.as_u64[1]; s_kv.value = s - tsm->sessions; - if (clib_bihash_add_del_16_8 (&sm->in2out_unk_proto, &s_kv, 1)) + if (clib_bihash_add_del_16_8 (&sm->in2out_ed, &s_kv, 1)) clib_warning ("in2out key add failed"); key.l_addr.as_u32 = new_addr; key.fib_index = sm->outside_fib_index; s_kv.key[0] = key.as_u64[0]; s_kv.key[1] = key.as_u64[1]; - if (clib_bihash_add_del_16_8 (&sm->out2in_unk_proto, &s_kv, 1)) + if (clib_bihash_add_del_16_8 (&sm->out2in_ed, &s_kv, 1)) clib_warning ("out2in key add failed"); } @@ -1366,6 +1370,153 @@ create_ses: vnet_buffer(b)->sw_if_index[VLIB_TX] = sm->outside_fib_index; } +static void +snat_in2out_lb (snat_main_t *sm, + vlib_buffer_t * b, + ip4_header_t * ip, + u32 rx_fib_index, + u32 thread_index, + f64 now, + vlib_main_t * vm) +{ + nat_ed_ses_key_t key; + clib_bihash_kv_16_8_t s_kv, s_value; + udp_header_t *udp = ip4_next_header (ip); + tcp_header_t *tcp = (tcp_header_t *) udp; + snat_session_t *s = 0; + snat_main_per_thread_data_t *tsm = &sm->per_thread_data[thread_index]; + u32 old_addr, new_addr; + u16 new_port, old_port; + ip_csum_t sum; + u32 proto = ip_proto_to_snat_proto (ip->protocol); + snat_session_key_t e_key, l_key; + clib_bihash_kv_8_8_t kv, value; + snat_user_key_t u_key; + snat_user_t *u; + dlist_elt_t *head, *elt; + + old_addr = ip->src_address.as_u32; + + key.l_addr = ip->src_address; + key.r_addr = ip->dst_address; + key.fib_index = rx_fib_index; + key.proto = ip->protocol; + key.rsvd = 0; + key.l_port = udp->src_port; + s_kv.key[0] = key.as_u64[0]; + s_kv.key[1] = key.as_u64[1]; + + if (!clib_bihash_search_16_8 (&sm->in2out_ed, &s_kv, &s_value)) + { + s = pool_elt_at_index (tsm->sessions, s_value.value); + } + else + { + l_key.addr = ip->src_address; + l_key.port = udp->src_port; + l_key.protocol = proto; + l_key.fib_index = rx_fib_index; + if (snat_static_mapping_match(sm, l_key, &e_key, 0, 0)) + return; + + u_key.addr = ip->src_address; + u_key.fib_index = rx_fib_index; + kv.key = u_key.as_u64; + + /* Ever heard of the "user" = src ip4 address before? */ + if (clib_bihash_search_8_8 (&sm->user_hash, &kv, &value)) + { + /* no, make a new one */ + pool_get (tsm->users, u); + memset (u, 0, sizeof (*u)); + u->addr = ip->src_address; + u->fib_index = rx_fib_index; + + pool_get (tsm->list_pool, head); + u->sessions_per_user_list_head_index = head - tsm->list_pool; + + clib_dlist_init (tsm->list_pool, + u->sessions_per_user_list_head_index); + + kv.value = u - tsm->users; + + /* add user */ + if (clib_bihash_add_del_8_8 (&sm->user_hash, &kv, 1)) + clib_warning ("user key add failed"); + } + else + { + u = pool_elt_at_index (tsm->users, value.value); + } + + /* Create a new session */ + pool_get (tsm->sessions, s); + memset (s, 0, sizeof (*s)); + + s->ext_host_addr.as_u32 = ip->dst_address.as_u32; + s->flags |= SNAT_SESSION_FLAG_STATIC_MAPPING; + s->flags |= SNAT_SESSION_FLAG_LOAD_BALANCING; + s->outside_address_index = ~0; + s->in2out = l_key; + s->out2in = e_key; + u->nstaticsessions++; + + /* Create list elts */ + pool_get (tsm->list_pool, elt); + clib_dlist_init (tsm->list_pool, elt - tsm->list_pool); + elt->value = s - tsm->sessions; + s->per_user_index = elt - tsm->list_pool; + s->per_user_list_head_index = u->sessions_per_user_list_head_index; + clib_dlist_addtail (tsm->list_pool, s->per_user_list_head_index, + s->per_user_index); + + /* Add to lookup tables */ + s_kv.value = s - tsm->sessions; + if (clib_bihash_add_del_16_8 (&sm->in2out_ed, &s_kv, 1)) + clib_warning ("in2out-ed key add failed"); + + key.l_addr = e_key.addr; + key.fib_index = e_key.fib_index; + key.l_port = e_key.port; + s_kv.key[0] = key.as_u64[0]; + s_kv.key[1] = key.as_u64[1]; + if (clib_bihash_add_del_16_8 (&sm->out2in_ed, &s_kv, 1)) + clib_warning ("out2in-ed key add failed"); + } + + new_addr = ip->src_address.as_u32 = s->out2in.addr.as_u32; + + /* Update IP checksum */ + sum = ip->checksum; + sum = ip_csum_update (sum, old_addr, new_addr, ip4_header_t, src_address); + ip->checksum = ip_csum_fold (sum); + + if (PREDICT_TRUE(proto == SNAT_PROTOCOL_TCP)) + { + old_port = tcp->src_port; + tcp->src_port = s->out2in.port; + new_port = tcp->src_port; + + sum = tcp->checksum; + sum = ip_csum_update (sum, old_addr, new_addr, ip4_header_t, src_address); + sum = ip_csum_update (sum, old_port, new_port, ip4_header_t, length); + tcp->checksum = ip_csum_fold(sum); + } + else + { + udp->src_port = s->out2in.port; + udp->checksum = 0; + } + + if (vnet_buffer(b)->sw_if_index[VLIB_TX] == ~0) + vnet_buffer(b)->sw_if_index[VLIB_TX] = sm->outside_fib_index; + + /* Accounting */ + s->last_heard = now; + s->total_pkts++; + s->total_bytes += vlib_buffer_length_in_chain (vm, b); +} + static inline uword snat_in2out_node_fn_inline (vlib_main_t * vm, vlib_node_runtime_t * node, @@ -1521,8 +1672,28 @@ snat_in2out_node_fn_inline (vlib_main_t * vm, } } else - s0 = pool_elt_at_index (sm->per_thread_data[thread_index].sessions, - value0.value); + { + if (PREDICT_FALSE (value0.value == ~0ULL)) + { + if (is_slow_path) + { + snat_in2out_lb(sm, b0, ip0, rx_fib_index0, thread_index, + now, vm); + goto trace00; + } + else + { + next0 = SNAT_IN2OUT_NEXT_SLOW_PATH; + goto trace00; + } + } + else + { + s0 = pool_elt_at_index ( + sm->per_thread_data[thread_index].sessions, + value0.value); + } + } old_addr0 = ip0->src_address.as_u32; ip0->src_address = s0->out2in.addr; @@ -1672,8 +1843,28 @@ snat_in2out_node_fn_inline (vlib_main_t * vm, } } else - s1 = pool_elt_at_index (sm->per_thread_data[thread_index].sessions, - value1.value); + { + if (PREDICT_FALSE (value1.value == ~0ULL)) + { + if (is_slow_path) + { + snat_in2out_lb(sm, b1, ip1, rx_fib_index1, thread_index, + now, vm); + goto trace01; + } + else + { + next1 = SNAT_IN2OUT_NEXT_SLOW_PATH; + goto trace01; + } + } + else + { + s1 = pool_elt_at_index ( + sm->per_thread_data[thread_index].sessions, + value1.value); + } + } old_addr1 = ip1->src_address.as_u32; ip1->src_address = s1->out2in.addr; @@ -1860,8 +2051,28 @@ snat_in2out_node_fn_inline (vlib_main_t * vm, } } else - s0 = pool_elt_at_index (sm->per_thread_data[thread_index].sessions, - value0.value); + { + if (PREDICT_FALSE (value0.value == ~0ULL)) + { + if (is_slow_path) + { + snat_in2out_lb(sm, b0, ip0, rx_fib_index0, thread_index, + now, vm); + goto trace0; + } + else + { + next0 = SNAT_IN2OUT_NEXT_SLOW_PATH; + goto trace0; + } + } + else + { + s0 = pool_elt_at_index ( + sm->per_thread_data[thread_index].sessions, + value0.value); + } + } old_addr0 = ip0->src_address.as_u32; ip0->src_address = s0->out2in.addr; diff --git a/src/plugins/nat/nat.api b/src/plugins/nat/nat.api index 7245cb07..d7a4a9ef 100644 --- a/src/plugins/nat/nat.api +++ b/src/plugins/nat/nat.api @@ -1025,6 +1025,39 @@ define nat44_user_session_details { u32 total_pkts; }; +typeonly manual_endian define nat44_lb_addr_port { + u8 addr[4]; + u16 port; + u8 probability; +}; + +autoreply manual_endian define nat44_add_del_lb_static_mapping { + u32 client_index; + u32 context; + u8 is_add; + u8 external_addr[4]; + u16 external_port; + u8 protocol; + u32 vrf_id; + u8 local_num; + vl_api_nat44_lb_addr_port_t locals[local_num]; +}; + +define nat44_lb_static_mapping_dump { + u32 client_index; + u32 context; +}; + +manual_endian define nat44_lb_static_mapping_details { + u32 context; + u8 external_addr[4]; + u16 external_port; + u8 protocol; + u32 vrf_id; + u8 local_num; + vl_api_nat44_lb_addr_port_t locals[local_num]; +}; + /* * Deterministic NAT (CGN) APIs */ diff --git a/src/plugins/nat/nat.c b/src/plugins/nat/nat.c index f9ecb943..fabd0bc2 100644 --- a/src/plugins/nat/nat.c +++ b/src/plugins/nat/nat.c @@ -506,15 +506,16 @@ int snat_add_static_mapping(ip4_address_t l_addr, ip4_address_t e_addr, if (snat_is_unk_proto_session (s)) { clib_bihash_kv_16_8_t up_kv; - snat_unk_proto_ses_key_t up_key; + nat_ed_ses_key_t up_key; up_key.l_addr = s->in2out.addr; up_key.r_addr = s->ext_host_addr; up_key.fib_index = s->in2out.fib_index; up_key.proto = s->in2out.port; - up_key.rsvd[0] = up_key.rsvd[1] = up_key.rsvd[2] = 0; + up_key.rsvd = 0; + up_key.l_port = 0; up_kv.key[0] = up_key.as_u64[0]; up_kv.key[1] = up_key.as_u64[1]; - if (clib_bihash_add_del_16_8 (&sm->in2out_unk_proto, + if (clib_bihash_add_del_16_8 (&sm->in2out_ed, &up_kv, 0)) clib_warning ("in2out key del failed"); @@ -522,7 +523,7 @@ int snat_add_static_mapping(ip4_address_t l_addr, ip4_address_t e_addr, up_key.fib_index = s->out2in.fib_index; up_kv.key[0] = up_key.as_u64[0]; up_kv.key[1] = up_key.as_u64[1]; - if (clib_bihash_add_del_16_8 (&sm->out2in_unk_proto, + if (clib_bihash_add_del_16_8 (&sm->out2in_ed, &up_kv, 0)) clib_warning ("out2in key del failed"); @@ -589,6 +590,243 @@ delete: return 0; } +int nat44_add_del_lb_static_mapping (ip4_address_t e_addr, u16 e_port, + snat_protocol_t proto, u32 vrf_id, + nat44_lb_addr_port_t *locals, u8 is_add) +{ + snat_main_t * sm = &snat_main; + snat_static_mapping_t *m; + snat_session_key_t m_key; + clib_bihash_kv_8_8_t kv, value; + u32 fib_index; + snat_address_t *a = 0; + int i; + nat44_lb_addr_port_t *local; + snat_user_key_t w_key0; + snat_worker_key_t w_key1; + u32 worker_index = 0; + + m_key.addr = e_addr; + m_key.port = e_port; + m_key.protocol = proto; + m_key.fib_index = sm->outside_fib_index; + kv.key = m_key.as_u64; + if (clib_bihash_search_8_8 (&sm->static_mapping_by_external, &kv, &value)) + m = 0; + else + m = pool_elt_at_index (sm->static_mappings, value.value); + + if (is_add) + { + if (m) + return VNET_API_ERROR_VALUE_EXIST; + + if (vec_len (locals) < 2) + return VNET_API_ERROR_INVALID_VALUE; + + fib_index = fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP4, + vrf_id); + + /* Find external address in allocated addresses and reserve port for + address and port pair mapping when dynamic translations enabled */ + if (!sm->static_mapping_only) + { + for (i = 0; i < vec_len (sm->addresses); i++) + { + if (sm->addresses[i].addr.as_u32 == e_addr.as_u32) + { + a = sm->addresses + i; + /* External port must be unused */ + switch (proto) + { +#define _(N, j, n, s) \ + case SNAT_PROTOCOL_##N: \ + if (clib_bitmap_get_no_check (a->busy_##n##_port_bitmap, e_port)) \ + return VNET_API_ERROR_INVALID_VALUE; \ + clib_bitmap_set_no_check (a->busy_##n##_port_bitmap, e_port, 1); \ + if (e_port > 1024) \ + a->busy_##n##_ports++; \ + break; + foreach_snat_protocol +#undef _ + default: + clib_warning("unknown_protocol"); + return VNET_API_ERROR_INVALID_VALUE_2; + } + break; + } + } + /* External address must be allocated */ + if (!a) + return VNET_API_ERROR_NO_SUCH_ENTRY; + } + + pool_get (sm->static_mappings, m); + memset (m, 0, sizeof (*m)); + m->external_addr = e_addr; + m->addr_only = 0; + m->vrf_id = vrf_id; + m->fib_index = fib_index; + m->external_port = e_port; + m->proto = proto; + + m_key.addr = m->external_addr; + m_key.port = m->external_port; + m_key.protocol = m->proto; + m_key.fib_index = sm->outside_fib_index; + kv.key = m_key.as_u64; + kv.value = m - sm->static_mappings; + if (clib_bihash_add_del_8_8(&sm->static_mapping_by_external, &kv, 1)) + { + clib_warning ("static_mapping_by_external key add failed"); + return VNET_API_ERROR_UNSPECIFIED; + } + m_key.port = clib_host_to_net_u16 (m->external_port); + kv.key = m_key.as_u64; + kv.value = ~0ULL; + if (clib_bihash_add_del_8_8(&sm->out2in, &kv, 1)) + { + clib_warning ("static_mapping_by_local key add failed"); + return VNET_API_ERROR_UNSPECIFIED; + } + + m_key.fib_index = m->fib_index; + + /* Assign worker */ + if (sm->workers) + { + w_key0.addr = locals[0].addr; + w_key0.fib_index = fib_index; + kv.key = w_key0.as_u64; + + if (clib_bihash_search_8_8 (&sm->worker_by_in, &kv, &value)) + worker_index = sm->first_worker_index + + sm->workers[sm->next_worker++ % vec_len (sm->workers)]; + else + worker_index = value.value; + + w_key1.addr = m->external_addr; + w_key1.port = clib_host_to_net_u16 (m->external_port); + w_key1.fib_index = sm->outside_fib_index; + kv.key = w_key1.as_u64; + kv.value = worker_index; + if (clib_bihash_add_del_8_8 (&sm->worker_by_out, &kv, 1)) + { + clib_warning ("worker-by-out add key failed"); + return VNET_API_ERROR_UNSPECIFIED; + } + } + + for (i = 0; i < vec_len (locals); i++) + { + m_key.addr = locals[i].addr; + m_key.port = locals[i].port; + kv.key = m_key.as_u64; + kv.value = m - sm->static_mappings; + clib_bihash_add_del_8_8(&sm->static_mapping_by_local, &kv, 1); + locals[i].prefix = locals[i - 1].prefix + locals[i].probability; + vec_add1 (m->locals, locals[i]); + m_key.port = clib_host_to_net_u16 (locals[i].port); + kv.key = m_key.as_u64; + kv.value = ~0ULL; + if (clib_bihash_add_del_8_8(&sm->in2out, &kv, 1)) + { + clib_warning ("in2out key add failed"); + return VNET_API_ERROR_UNSPECIFIED; + } + /* Assign worker */ + if (sm->workers) + { + w_key0.addr = locals[i].addr; + w_key0.fib_index = fib_index; + kv.key = w_key0.as_u64; + kv.value = worker_index; + if (clib_bihash_add_del_8_8 (&sm->worker_by_in, &kv, 1)) + { + clib_warning ("worker-by-in key add failed"); + return VNET_API_ERROR_UNSPECIFIED; + } + } + } + } + else + { + if (!m) + return VNET_API_ERROR_NO_SUCH_ENTRY; + + fib_table_unlock (m->fib_index, FIB_PROTOCOL_IP4); + + /* Free external address port */ + if (!sm->static_mapping_only) + { + for (i = 0; i < vec_len (sm->addresses); i++) + { + if (sm->addresses[i].addr.as_u32 == e_addr.as_u32) + { + a = sm->addresses + i; + switch (proto) + { +#define _(N, j, n, s) \ + case SNAT_PROTOCOL_##N: \ + clib_bitmap_set_no_check (a->busy_##n##_port_bitmap, e_port, 0); \ + if (e_port > 1024) \ + a->busy_##n##_ports--; \ + break; + foreach_snat_protocol +#undef _ + default: + clib_warning("unknown_protocol"); + return VNET_API_ERROR_INVALID_VALUE_2; + } + break; + } + } + } + + m_key.addr = m->external_addr; + m_key.port = m->external_port; + m_key.protocol = m->proto; + m_key.fib_index = sm->outside_fib_index; + kv.key = m_key.as_u64; + if (clib_bihash_add_del_8_8(&sm->static_mapping_by_external, &kv, 0)) + { + clib_warning ("static_mapping_by_external key del failed"); + return VNET_API_ERROR_UNSPECIFIED; + } + m_key.port = clib_host_to_net_u16 (m->external_port); + kv.key = m_key.as_u64; + if (clib_bihash_add_del_8_8(&sm->out2in, &kv, 0)) + { + clib_warning ("outi2in key del failed"); + return VNET_API_ERROR_UNSPECIFIED; + } + + vec_foreach (local, m->locals) + { + m_key.addr = local->addr; + m_key.port = local->port; + m_key.fib_index = m->fib_index; + kv.key = m_key.as_u64; + if (clib_bihash_add_del_8_8(&sm->static_mapping_by_local, &kv, 0)) + { + clib_warning ("static_mapping_by_local key del failed"); + return VNET_API_ERROR_UNSPECIFIED; + } + m_key.port = clib_host_to_net_u16 (local->port); + kv.key = m_key.as_u64; + if (clib_bihash_add_del_8_8(&sm->in2out, &kv, 0)) + { + clib_warning ("in2out key del failed"); + return VNET_API_ERROR_UNSPECIFIED; + } + } + + pool_put (sm->static_mappings, m); + } + + return 0; +} + int snat_del_address (snat_main_t *sm, ip4_address_t addr, u8 delete_sm) { snat_address_t *a = 0; @@ -649,15 +887,16 @@ int snat_del_address (snat_main_t *sm, ip4_address_t addr, u8 delete_sm) if (snat_is_unk_proto_session (ses)) { clib_bihash_kv_16_8_t up_kv; - snat_unk_proto_ses_key_t up_key; + nat_ed_ses_key_t up_key; up_key.l_addr = ses->in2out.addr; up_key.r_addr = ses->ext_host_addr; up_key.fib_index = ses->in2out.fib_index; up_key.proto = ses->in2out.port; - up_key.rsvd[0] = up_key.rsvd[1] = up_key.rsvd[2] = 0; + up_key.rsvd = 0; + up_key.l_port = 0; up_kv.key[0] = up_key.as_u64[0]; up_kv.key[1] = up_key.as_u64[1]; - if (clib_bihash_add_del_16_8 (&sm->in2out_unk_proto, + if (clib_bihash_add_del_16_8 (&sm->in2out_ed, &up_kv, 0)) clib_warning ("in2out key del failed"); @@ -665,7 +904,7 @@ int snat_del_address (snat_main_t *sm, ip4_address_t addr, u8 delete_sm) up_key.fib_index = ses->out2in.fib_index; up_kv.key[0] = up_key.as_u64[0]; up_kv.key[1] = up_key.as_u64[1]; - if (clib_bihash_add_del_16_8 (&sm->out2in_unk_proto, + if (clib_bihash_add_del_16_8 (&sm->out2in_ed, &up_kv, 0)) clib_warning ("out2in key del failed"); } @@ -1048,6 +1287,7 @@ int snat_static_mapping_match (snat_main_t * sm, snat_static_mapping_t *m; snat_session_key_t m_key; clib_bihash_8_8_t *mapping_hash = &sm->static_mapping_by_local; + u32 rand, lo = 0, hi, mid; if (by_external) mapping_hash = &sm->static_mapping_by_external; @@ -1073,11 +1313,29 @@ int snat_static_mapping_match (snat_main_t * sm, if (by_external) { - mapping->addr = m->local_addr; - /* Address only mapping doesn't change port */ - mapping->port = m->addr_only ? match.port - : clib_host_to_net_u16 (m->local_port); + if (vec_len (m->locals)) + { + hi = vec_len (m->locals) - 1; + rand = 1 + (random_u32 (&sm->random_seed) % m->locals[hi].prefix); + while (lo < hi) + { + mid = ((hi - 1) >> 1) + lo; + (rand > m->locals[mid].prefix) ? (lo = mid + 1) : (hi = mid); + } + if (!(m->locals[lo].prefix >= rand)) + return 1; + mapping->addr = m->locals[lo].addr; + mapping->port = clib_host_to_net_u16 (m->locals[lo].port); + } + else + { + mapping->addr = m->local_addr; + /* Address only mapping doesn't change port */ + mapping->port = m->addr_only ? match.port + : clib_host_to_net_u16 (m->local_port); + } mapping->fib_index = m->fib_index; + mapping->protocol = m->proto; } else { @@ -1517,6 +1775,101 @@ VLIB_CLI_COMMAND (add_static_mapping_command, static) = { "nat44 add static mapping tcp|udp|icmp local [] external [] [vrf ] [del]", }; +static clib_error_t * +add_lb_static_mapping_command_fn (vlib_main_t * vm, + unformat_input_t * input, + vlib_cli_command_t * cmd) +{ + unformat_input_t _line_input, *line_input = &_line_input; + clib_error_t * error = 0; + ip4_address_t l_addr, e_addr; + u32 l_port = 0, e_port = 0, vrf_id = 0, probability = 0; + int is_add = 1; + int rv; + snat_protocol_t proto; + u8 proto_set = 0; + nat44_lb_addr_port_t *locals = 0, local; + + /* Get a line of input. */ + if (!unformat_user (input, unformat_line_input, line_input)) + return 0; + + while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT) + { + if (unformat (line_input, "local %U:%u probability %u", + unformat_ip4_address, &l_addr, &l_port, &probability)) + { + memset (&local, 0, sizeof (local)); + local.addr = l_addr; + local.port = (u16) l_port; + local.probability = (u8) probability; + vec_add1 (locals, local); + } + else if (unformat (line_input, "external %U:%u", unformat_ip4_address, + &e_addr, &e_port)) + ; + else if (unformat (line_input, "vrf %u", &vrf_id)) + ; + else if (unformat (line_input, "protocol %U", unformat_snat_protocol, + &proto)) + proto_set = 1; + else if (unformat (line_input, "del")) + is_add = 0; + else + { + error = clib_error_return (0, "unknown input: '%U'", + format_unformat_error, line_input); + goto done; + } + } + + if (vec_len (locals) < 2) + { + error = clib_error_return (0, "at least two local must be set"); + goto done; + } + + if (!proto_set) + { + error = clib_error_return (0, "missing protocol"); + goto done; + } + + rv = nat44_add_del_lb_static_mapping (e_addr, (u16) e_port, proto, vrf_id, + locals, is_add); + + switch (rv) + { + case VNET_API_ERROR_INVALID_VALUE: + error = clib_error_return (0, "External port already in use."); + goto done; + case VNET_API_ERROR_NO_SUCH_ENTRY: + if (is_add) + error = clib_error_return (0, "External addres must be allocated."); + else + error = clib_error_return (0, "Mapping not exist."); + goto done; + case VNET_API_ERROR_VALUE_EXIST: + error = clib_error_return (0, "Mapping already exist."); + goto done; + default: + break; + } + +done: + unformat_free (line_input); + vec_free (locals); + + return error; +} + +VLIB_CLI_COMMAND (add_lb_static_mapping_command, static) = { + .path = "nat44 add load-balancing static mapping", + .function = add_lb_static_mapping_command_fn, + .short_help = + "nat44 add load-balancing static mapping protocol tcp|udp external : local : probability [vrf ] [del]", +}; + static clib_error_t * set_workers_command_fn (vlib_main_t * vm, unformat_input_t * input, @@ -1839,10 +2192,10 @@ snat_config (vlib_main_t * vm, unformat_input_t * input) clib_bihash_init_8_8 (&sm->user_hash, "users", user_buckets, user_memory_size); - clib_bihash_init_16_8 (&sm->in2out_unk_proto, "in2out-unk-proto", + clib_bihash_init_16_8 (&sm->in2out_ed, "in2out-ed", translation_buckets, translation_memory_size); - clib_bihash_init_16_8 (&sm->out2in_unk_proto, "out2in-unk-proto", + clib_bihash_init_16_8 (&sm->out2in_ed, "out2in-ed", translation_buckets, translation_memory_size); } else @@ -1884,18 +2237,10 @@ u8 * format_snat_session_state (u8 * s, va_list * args) u8 * format_snat_key (u8 * s, va_list * args) { snat_session_key_t * key = va_arg (*args, snat_session_key_t *); - char * protocol_string = "unknown"; - static char *protocol_strings[] = { - "UDP", - "TCP", - "ICMP", - }; - - if (key->protocol < ARRAY_LEN(protocol_strings)) - protocol_string = protocol_strings[key->protocol]; - s = format (s, "%U proto %s port %d fib %d", - format_ip4_address, &key->addr, protocol_string, + s = format (s, "%U proto %U port %d fib %d", + format_ip4_address, &key->addr, + format_snat_protocol, key->protocol, clib_net_to_host_u16 (key->port), key->fib_index); return s; } @@ -1919,6 +2264,9 @@ u8 * format_snat_session (u8 * s, va_list * args) s = format (s, " i2o %U\n", format_snat_key, &sess->in2out); s = format (s, " o2i %U\n", format_snat_key, &sess->out2in); } + if (sess->ext_host_addr.as_u32) + s = format (s, " external host %U\n", + format_ip4_address, &sess->ext_host_addr); 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); @@ -1926,6 +2274,8 @@ u8 * format_snat_session (u8 * s, va_list * args) s = format (s, " static translation\n"); else s = format (s, " dynamic translation\n"); + if (sess->flags & SNAT_SESSION_FLAG_LOAD_BALANCING) + s = format (s, " load-balancing\n"); return s; } @@ -1973,6 +2323,7 @@ u8 * format_snat_user (u8 * s, va_list * args) u8 * format_snat_static_mapping (u8 * s, va_list * args) { snat_static_mapping_t *m = va_arg (*args, snat_static_mapping_t *); + nat44_lb_addr_port_t *local; if (m->addr_only) s = format (s, "local %U external %U vrf %d", @@ -1980,12 +2331,25 @@ u8 * format_snat_static_mapping (u8 * s, va_list * args) format_ip4_address, &m->external_addr, m->vrf_id); else - s = format (s, "%U local %U:%d external %U:%d vrf %d", - format_snat_protocol, m->proto, - format_ip4_address, &m->local_addr, m->local_port, - format_ip4_address, &m->external_addr, m->external_port, - m->vrf_id); - + { + if (vec_len (m->locals)) + { + s = format (s, "%U vrf %d external %U:%d", + format_snat_protocol, m->proto, + m->vrf_id, + format_ip4_address, &m->external_addr, m->external_port); + vec_foreach (local, m->locals) + s = format (s, "\n local %U:%d probability %d\%", + format_ip4_address, &local->addr, local->port, + local->probability); + } + else + s = format (s, "%U local %U:%d external %U:%d vrf %d", + format_snat_protocol, m->proto, + format_ip4_address, &m->local_addr, m->local_port, + format_ip4_address, &m->external_addr, m->external_port, + m->vrf_id); + } return s; } @@ -2208,6 +2572,10 @@ show_snat_command_fn (vlib_main_t * vm, verbose - 1); vlib_cli_output (vm, "%U", format_bihash_8_8, &sm->out2in, verbose - 1); + vlib_cli_output (vm, "%U", format_bihash_16_8, &sm->in2out_ed, + verbose - 1); + vlib_cli_output (vm, "%U", format_bihash_16_8, &sm->out2in_ed, + verbose - 1); vlib_cli_output (vm, "%U", format_bihash_8_8, &sm->worker_by_in, verbose - 1); vlib_cli_output (vm, "%U", format_bihash_8_8, &sm->worker_by_out, diff --git a/src/plugins/nat/nat.h b/src/plugins/nat/nat.h index 04c466dc..8935144d 100644 --- a/src/plugins/nat/nat.h +++ b/src/plugins/nat/nat.h @@ -62,12 +62,13 @@ typedef struct { ip4_address_t l_addr; ip4_address_t r_addr; u32 fib_index; + u16 l_port; u8 proto; - u8 rsvd[3]; + u8 rsvd; }; u64 as_u64[2]; }; -} snat_unk_proto_ses_key_t; +} nat_ed_ses_key_t; typedef struct { union @@ -139,6 +140,7 @@ typedef enum { #define SNAT_SESSION_FLAG_STATIC_MAPPING 1 #define SNAT_SESSION_FLAG_UNKNOWN_PROTO 2 +#define SNAT_SESSION_FLAG_LOAD_BALANCING 4 typedef CLIB_PACKED(struct { snat_session_key_t out2in; /* 0-15 */ @@ -205,6 +207,13 @@ typedef struct { snat_det_session_t * sessions; } snat_det_map_t; +typedef struct { + ip4_address_t addr; + u16 port; + u8 probability; + u8 prefix; +} nat44_lb_addr_port_t; + typedef struct { ip4_address_t local_addr; ip4_address_t external_addr; @@ -214,6 +223,7 @@ typedef struct { u32 vrf_id; u32 fib_index; snat_protocol_t proto; + nat44_lb_addr_port_t *locals; } snat_static_mapping_t; typedef struct { @@ -264,9 +274,9 @@ typedef struct snat_main_s { clib_bihash_8_8_t out2in; clib_bihash_8_8_t in2out; - /* Unknown protocol sessions lookup tables */ - clib_bihash_16_8_t out2in_unk_proto; - clib_bihash_16_8_t in2out_unk_proto; + /* Endpoint address dependent sessions lookup tables */ + clib_bihash_16_8_t out2in_ed; + clib_bihash_16_8_t in2out_ed; /* Find-a-user => src address lookup */ clib_bihash_8_8_t user_hash; @@ -496,6 +506,9 @@ int snat_interface_add_del_output_feature(u32 sw_if_index, u8 is_inside, int snat_add_interface_address(snat_main_t *sm, u32 sw_if_index, int is_del); uword unformat_snat_protocol(unformat_input_t * input, va_list * args); u8 * format_snat_protocol(u8 * s, va_list * args); +int nat44_add_del_lb_static_mapping (ip4_address_t e_addr, u16 e_port, + snat_protocol_t proto, u32 vrf_id, + nat44_lb_addr_port_t *locals, u8 is_add); static_always_inline u8 icmp_is_error_message (icmp46_header_t * icmp) diff --git a/src/plugins/nat/nat_api.c b/src/plugins/nat/nat_api.c index 0a2141f2..fa20f2cc 100644 --- a/src/plugins/nat/nat_api.c +++ b/src/plugins/nat/nat_api.c @@ -27,6 +27,10 @@ #include #include +#define vl_api_nat44_lb_addr_port_t_endian vl_noop_handler +#define vl_api_nat44_add_del_lb_static_mapping_t_endian vl_noop_handler +#define vl_api_nat44_nat44_lb_static_mapping_details_t_endian vl_noop_handler + /* define message structures */ #define vl_typedefs #include @@ -465,7 +469,8 @@ static void /* *INDENT-OFF* */ pool_foreach (m, sm->static_mappings, ({ - send_snat_static_mapping_details (m, q, mp->context); + if (!vec_len(m->locals)) + send_snat_static_mapping_details (m, q, mp->context); })); /* *INDENT-ON* */ @@ -1888,7 +1893,8 @@ vl_api_nat44_static_mapping_dump_t_handler (vl_api_nat44_static_mapping_dump_t /* *INDENT-OFF* */ pool_foreach (m, sm->static_mappings, ({ - send_nat44_static_mapping_details (m, q, mp->context); + if (!vec_len(m->locals)) + send_nat44_static_mapping_details (m, q, mp->context); })); /* *INDENT-ON* */ @@ -2136,6 +2142,131 @@ vl_api_nat44_user_session_dump_t_print (vl_api_nat44_user_session_dump_t * mp, FINISH; } +static nat44_lb_addr_port_t * +unformat_nat44_lb_addr_port (vl_api_nat44_lb_addr_port_t * addr_port_pairs, + u8 addr_port_pair_num) +{ + u8 i; + nat44_lb_addr_port_t *lb_addr_port_pairs = 0, lb_addr_port; + vl_api_nat44_lb_addr_port_t *ap; + + for (i = 0; i < addr_port_pair_num; i++) + { + ap = &addr_port_pairs[i]; + memset (&lb_addr_port, 0, sizeof (lb_addr_port)); + clib_memcpy (&lb_addr_port.addr, ap->addr, 4); + lb_addr_port.port = clib_net_to_host_u16 (ap->port); + lb_addr_port.probability = ap->probability; + vec_add1 (lb_addr_port_pairs, lb_addr_port); + } + + return lb_addr_port_pairs; +} + +static void + vl_api_nat44_add_del_lb_static_mapping_t_handler + (vl_api_nat44_add_del_lb_static_mapping_t * mp) +{ + snat_main_t *sm = &snat_main; + vl_api_nat44_add_del_lb_static_mapping_reply_t *rmp; + int rv = 0; + nat44_lb_addr_port_t *locals = 0; + ip4_address_t e_addr; + snat_protocol_t proto; + + locals = unformat_nat44_lb_addr_port (mp->locals, mp->local_num); + clib_memcpy (&e_addr, mp->external_addr, 4); + proto = ip_proto_to_snat_proto (mp->protocol); + + rv = + nat44_add_del_lb_static_mapping (e_addr, + clib_net_to_host_u16 (mp->external_port), + proto, clib_net_to_host_u32 (mp->vrf_id), + locals, mp->is_add); + + vec_free (locals); + + REPLY_MACRO (VL_API_NAT44_ADD_DEL_LB_STATIC_MAPPING_REPLY); +} + +static void *vl_api_nat44_add_del_lb_static_mapping_t_print + (vl_api_nat44_add_del_lb_static_mapping_t * mp, void *handle) +{ + u8 *s; + + s = format (0, "SCRIPT: nat44_add_del_lb_static_mapping "); + s = format (s, "is_add %d\n", mp->is_add); + + FINISH; +} + +static void +send_nat44_lb_static_mapping_details (snat_static_mapping_t * m, + unix_shared_memory_queue_t * q, + u32 context) +{ + vl_api_nat44_lb_static_mapping_details_t *rmp; + snat_main_t *sm = &snat_main; + nat44_lb_addr_port_t *ap; + vl_api_nat44_lb_addr_port_t *locals; + + rmp = + vl_msg_api_alloc (sizeof (*rmp) + + (vec_len (m->locals) * sizeof (nat44_lb_addr_port_t))); + 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 = ntohs (m->external_port); + rmp->protocol = snat_proto_to_ip_proto (m->proto); + rmp->vrf_id = ntohl (m->vrf_id); + rmp->context = context; + + locals = (vl_api_nat44_lb_addr_port_t *) rmp->locals; + vec_foreach (ap, m->locals) + { + clib_memcpy (locals->addr, &(ap->addr), 4); + locals->port = htons (ap->port); + locals->probability = ap->probability; + locals++; + rmp->local_num++; + } + + vl_msg_api_send_shmem (q, (u8 *) & rmp); +} + +static void + vl_api_nat44_lb_static_mapping_dump_t_handler + (vl_api_nat44_lb_static_mapping_dump_t * mp) +{ + unix_shared_memory_queue_t *q; + snat_main_t *sm = &snat_main; + snat_static_mapping_t *m; + + q = vl_api_client_index_to_input_queue (mp->client_index); + if (q == 0) + return; + + /* *INDENT-OFF* */ + pool_foreach (m, sm->static_mappings, + ({ + if (vec_len(m->locals)) + send_nat44_lb_static_mapping_details (m, q, mp->context); + })); + /* *INDENT-ON* */ +} + +static void *vl_api_nat44_lb_static_mapping_dump_t_print + (vl_api_nat44_lb_static_mapping_dump_t * mp, void *handle) +{ + u8 *s; + + s = format (0, "SCRIPT: nat44_lb_static_mapping_dump "); + + FINISH; +} + /*******************************/ /*** Deterministic NAT (CGN) ***/ /*******************************/ @@ -3159,6 +3290,8 @@ _(NAT44_INTERFACE_ADD_DEL_OUTPUT_FEATURE, \ nat44_interface_add_del_output_feature) \ _(NAT44_INTERFACE_OUTPUT_FEATURE_DUMP, \ nat44_interface_output_feature_dump) \ +_(NAT44_ADD_DEL_LB_STATIC_MAPPING, nat44_add_del_lb_static_mapping) \ +_(NAT44_LB_STATIC_MAPPING_DUMP, nat44_lb_static_mapping_dump) \ _(NAT_DET_ADD_DEL_MAP, nat_det_add_del_map) \ _(NAT_DET_FORWARD, nat_det_forward) \ _(NAT_DET_REVERSE, nat_det_reverse) \ diff --git a/src/plugins/nat/out2in.c b/src/plugins/nat/out2in.c index 67950066..55a750e4 100644 --- a/src/plugins/nat/out2in.c +++ b/src/plugins/nat/out2in.c @@ -630,7 +630,7 @@ snat_out2in_unknown_proto (snat_main_t *sm, snat_session_key_t m_key; u32 old_addr, new_addr; ip_csum_t sum; - snat_unk_proto_ses_key_t key; + nat_ed_ses_key_t key; snat_session_t * s; snat_main_per_thread_data_t *tsm = &sm->per_thread_data[thread_index]; snat_user_key_t u_key; @@ -643,11 +643,12 @@ snat_out2in_unknown_proto (snat_main_t *sm, key.r_addr = ip->src_address; key.fib_index = rx_fib_index; key.proto = ip->protocol; - key.rsvd[0] = key.rsvd[1] = key.rsvd[2] = 0; + key.rsvd = 0; + key.l_port = 0; s_kv.key[0] = key.as_u64[0]; s_kv.key[1] = key.as_u64[1]; - if (!clib_bihash_search_16_8 (&sm->out2in_unk_proto, &s_kv, &s_value)) + if (!clib_bihash_search_16_8 (&sm->out2in_ed, &s_kv, &s_value)) { s = pool_elt_at_index (tsm->sessions, s_value.value); new_addr = ip->dst_address.as_u32 = s->in2out.addr.as_u32; @@ -721,14 +722,14 @@ snat_out2in_unknown_proto (snat_main_t *sm, /* Add to lookup tables */ s_kv.value = s - tsm->sessions; - if (clib_bihash_add_del_16_8 (&sm->out2in_unk_proto, &s_kv, 1)) + if (clib_bihash_add_del_16_8 (&sm->out2in_ed, &s_kv, 1)) clib_warning ("out2in key add failed"); key.l_addr = ip->dst_address; key.fib_index = m->fib_index; s_kv.key[0] = key.as_u64[0]; s_kv.key[1] = key.as_u64[1]; - if (clib_bihash_add_del_16_8 (&sm->in2out_unk_proto, &s_kv, 1)) + if (clib_bihash_add_del_16_8 (&sm->in2out_ed, &s_kv, 1)) clib_warning ("in2out key add failed"); } @@ -749,6 +750,152 @@ snat_out2in_unknown_proto (snat_main_t *sm, s->per_user_index); } +static void +snat_out2in_lb (snat_main_t *sm, + vlib_buffer_t * b, + ip4_header_t * ip, + u32 rx_fib_index, + u32 thread_index, + f64 now, + vlib_main_t * vm) +{ + nat_ed_ses_key_t key; + clib_bihash_kv_16_8_t s_kv, s_value; + udp_header_t *udp = ip4_next_header (ip); + tcp_header_t *tcp = (tcp_header_t *) udp; + snat_session_t *s = 0; + snat_main_per_thread_data_t *tsm = &sm->per_thread_data[thread_index]; + snat_session_key_t e_key, l_key; + clib_bihash_kv_8_8_t kv, value; + u32 old_addr, new_addr; + u32 proto = ip_proto_to_snat_proto (ip->protocol); + u16 new_port, old_port; + ip_csum_t sum; + snat_user_key_t u_key; + snat_user_t *u; + dlist_elt_t *head, *elt; + + old_addr = ip->dst_address.as_u32; + + key.l_addr = ip->dst_address; + key.r_addr = ip->src_address; + key.fib_index = rx_fib_index; + key.proto = ip->protocol; + key.rsvd = 0; + key.l_port = udp->dst_port; + s_kv.key[0] = key.as_u64[0]; + s_kv.key[1] = key.as_u64[1]; + + if (!clib_bihash_search_16_8 (&sm->out2in_ed, &s_kv, &s_value)) + { + s = pool_elt_at_index (tsm->sessions, s_value.value); + } + else + { + e_key.addr = ip->dst_address; + e_key.port = udp->dst_port; + e_key.protocol = proto; + e_key.fib_index = rx_fib_index; + if (snat_static_mapping_match(sm, e_key, &l_key, 1, 0)) + return; + + u_key.addr = l_key.addr; + u_key.fib_index = l_key.fib_index; + kv.key = u_key.as_u64; + + /* Ever heard of the "user" = src ip4 address before? */ + if (clib_bihash_search_8_8 (&sm->user_hash, &kv, &value)) + { + /* no, make a new one */ + pool_get (tsm->users, u); + memset (u, 0, sizeof (*u)); + u->addr = l_key.addr; + u->fib_index = l_key.fib_index; + + pool_get (tsm->list_pool, head); + u->sessions_per_user_list_head_index = head - tsm->list_pool; + + clib_dlist_init (tsm->list_pool, + u->sessions_per_user_list_head_index); + + kv.value = u - tsm->users; + + /* add user */ + if (clib_bihash_add_del_8_8 (&sm->user_hash, &kv, 1)) + clib_warning ("user key add failed"); + } + else + { + u = pool_elt_at_index (tsm->users, value.value); + } + + /* Create a new session */ + pool_get (tsm->sessions, s); + memset (s, 0, sizeof (*s)); + + s->ext_host_addr.as_u32 = ip->src_address.as_u32; + s->flags |= SNAT_SESSION_FLAG_STATIC_MAPPING; + s->flags |= SNAT_SESSION_FLAG_LOAD_BALANCING; + s->outside_address_index = ~0; + s->out2in = e_key; + s->in2out = l_key; + u->nstaticsessions++; + + /* Create list elts */ + pool_get (tsm->list_pool, elt); + clib_dlist_init (tsm->list_pool, elt - tsm->list_pool); + elt->value = s - tsm->sessions; + s->per_user_index = elt - tsm->list_pool; + s->per_user_list_head_index = u->sessions_per_user_list_head_index; + clib_dlist_addtail (tsm->list_pool, s->per_user_list_head_index, + s->per_user_index); + + /* Add to lookup tables */ + s_kv.value = s - tsm->sessions; + if (clib_bihash_add_del_16_8 (&sm->out2in_ed, &s_kv, 1)) + clib_warning ("out2in-ed key add failed"); + + key.l_addr = l_key.addr; + key.fib_index = l_key.fib_index; + key.l_port = l_key.port; + s_kv.key[0] = key.as_u64[0]; + s_kv.key[1] = key.as_u64[1]; + if (clib_bihash_add_del_16_8 (&sm->in2out_ed, &s_kv, 1)) + clib_warning ("in2out-ed key add failed"); + } + + new_addr = ip->dst_address.as_u32 = s->in2out.addr.as_u32; + + /* Update IP checksum */ + sum = ip->checksum; + sum = ip_csum_update (sum, old_addr, new_addr, ip4_header_t, dst_address); + ip->checksum = ip_csum_fold (sum); + + if (PREDICT_TRUE(proto == SNAT_PROTOCOL_TCP)) + { + old_port = tcp->dst_port; + tcp->dst_port = s->in2out.port; + new_port = tcp->dst_port; + + sum = tcp->checksum; + sum = ip_csum_update (sum, old_addr, new_addr, ip4_header_t, dst_address); + sum = ip_csum_update (sum, old_port, new_port, ip4_header_t, length); + tcp->checksum = ip_csum_fold(sum); + } + else + { + udp->dst_port = s->in2out.port; + udp->checksum = 0; + } + + vnet_buffer(b)->sw_if_index[VLIB_TX] = s->in2out.fib_index; + + /* Accounting */ + s->last_heard = now; + s->total_pkts++; + s->total_bytes += vlib_buffer_length_in_chain (vm, b); +} + static uword snat_out2in_node_fn (vlib_main_t * vm, vlib_node_runtime_t * node, @@ -894,8 +1041,20 @@ snat_out2in_node_fn (vlib_main_t * vm, } } else - s0 = pool_elt_at_index (sm->per_thread_data[thread_index].sessions, - value0.value); + { + if (PREDICT_FALSE (value0.value == ~0ULL)) + { + snat_out2in_lb(sm, b0, ip0, rx_fib_index0, thread_index, now, + vm); + goto trace0; + } + else + { + s0 = pool_elt_at_index ( + sm->per_thread_data[thread_index].sessions, + value0.value); + } + } old_addr0 = ip0->dst_address.as_u32; ip0->dst_address = s0->in2out.addr; @@ -1033,8 +1192,20 @@ snat_out2in_node_fn (vlib_main_t * vm, } } else - s1 = pool_elt_at_index (sm->per_thread_data[thread_index].sessions, - value1.value); + { + if (PREDICT_FALSE (value1.value == ~0ULL)) + { + snat_out2in_lb(sm, b1, ip1, rx_fib_index1, thread_index, now, + vm); + goto trace1; + } + else + { + s1 = pool_elt_at_index ( + sm->per_thread_data[thread_index].sessions, + value1.value); + } + } old_addr1 = ip1->dst_address.as_u32; ip1->dst_address = s1->in2out.addr; @@ -1209,8 +1380,20 @@ snat_out2in_node_fn (vlib_main_t * vm, } } else - s0 = pool_elt_at_index (sm->per_thread_data[thread_index].sessions, - value0.value); + { + if (PREDICT_FALSE (value0.value == ~0ULL)) + { + snat_out2in_lb(sm, b0, ip0, rx_fib_index0, thread_index, now, + vm); + goto trace00; + } + else + { + s0 = pool_elt_at_index ( + sm->per_thread_data[thread_index].sessions, + value0.value); + } + } old_addr0 = ip0->dst_address.as_u32; ip0->dst_address = s0->in2out.addr; diff --git a/test/test_nat.py b/test/test_nat.py index 0d622b08..de07019f 100644 --- a/test/test_nat.py +++ b/test/test_nat.py @@ -15,6 +15,7 @@ from scapy.packet import bind_layers from util import ppp from ipfix import IPFIX, Set, Template, Data, IPFIXDecoder from time import sleep +from util import ip4_range class MethodHolder(VppTestCase): @@ -633,6 +634,15 @@ class TestNAT44(MethodHolder): protocol=sm.protocol, is_add=0) + lb_static_mappings = self.vapi.nat44_lb_static_mapping_dump() + for lb_sm in lb_static_mappings: + self.vapi.nat44_add_del_lb_static_mapping( + lb_sm.external_addr, + lb_sm.external_port, + lb_sm.protocol, + lb_sm.vrf_id, + is_add=0) + adresses = self.vapi.nat44_address_dump() for addr in adresses: self.vapi.nat44_add_del_address_range(addr.ip_address, @@ -1037,6 +1047,97 @@ class TestNAT44(MethodHolder): self.pg_start() self.pg3.assert_nothing_captured() + def test_static_lb(self): + """ NAT44 local service load balancing """ + external_addr_n = socket.inet_pton(socket.AF_INET, self.nat_addr) + external_port = 80 + local_port = 8080 + server1 = self.pg0.remote_hosts[0] + server2 = self.pg0.remote_hosts[1] + + locals = [{'addr': server1.ip4n, + 'port': local_port, + 'probability': 70}, + {'addr': server2.ip4n, + 'port': local_port, + 'probability': 30}] + + self.nat44_add_address(self.nat_addr) + self.vapi.nat44_add_del_lb_static_mapping(external_addr_n, + external_port, + IP_PROTOS.tcp, + local_num=len(locals), + locals=locals) + self.vapi.nat44_interface_add_del_feature(self.pg0.sw_if_index) + self.vapi.nat44_interface_add_del_feature(self.pg1.sw_if_index, + is_inside=0) + + # from client to service + p = (Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac) / + IP(src=self.pg1.remote_ip4, dst=self.nat_addr) / + TCP(sport=12345, dport=external_port)) + self.pg1.add_stream(p) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = self.pg0.get_capture(1) + p = capture[0] + server = None + try: + ip = p[IP] + tcp = p[TCP] + self.assertIn(ip.dst, [server1.ip4, server2.ip4]) + if ip.dst == server1.ip4: + server = server1 + else: + server = server2 + self.assertEqual(tcp.dport, local_port) + self.check_tcp_checksum(p) + self.check_ip_checksum(p) + except: + self.logger.error(ppp("Unexpected or invalid packet:", p)) + raise + + # from service back to client + p = (Ether(src=server.mac, dst=self.pg0.local_mac) / + IP(src=server.ip4, dst=self.pg1.remote_ip4) / + TCP(sport=local_port, dport=12345)) + self.pg0.add_stream(p) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = self.pg1.get_capture(1) + p = capture[0] + try: + ip = p[IP] + tcp = p[TCP] + self.assertEqual(ip.src, self.nat_addr) + self.assertEqual(tcp.sport, external_port) + self.check_tcp_checksum(p) + self.check_ip_checksum(p) + except: + self.logger.error(ppp("Unexpected or invalid packet:", p)) + raise + + # multiple clients + server1_n = 0 + server2_n = 0 + clients = ip4_range(self.pg1.remote_ip4, 10, 20) + pkts = [] + for client in clients: + p = (Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac) / + IP(src=client, dst=self.nat_addr) / + TCP(sport=12345, dport=external_port)) + pkts.append(p) + self.pg1.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = self.pg0.get_capture(len(pkts)) + for p in capture: + if p[IP].dst == server1.ip4: + server1_n += 1 + else: + server2_n += 1 + self.assertTrue(server1_n > server2_n) + def test_multiple_inside_interfaces(self): """ NAT44 multiple non-overlapping address space inside interfaces """ diff --git a/test/vpp_papi_provider.py b/test/vpp_papi_provider.py index 61db4d6b..03238b9d 100644 --- a/test/vpp_papi_provider.py +++ b/test/vpp_papi_provider.py @@ -1238,6 +1238,36 @@ class VppPapiProvider(object): """ return self.api(self.papi.nat44_user_dump, {}) + def nat44_add_del_lb_static_mapping( + self, + external_addr, + external_port, + protocol, + vrf_id=0, + local_num=0, + locals=None, + is_add=1): + """Add/delete NAT44 load balancing static mapping + + :param is_add - 1 if add, 0 if delete + """ + return self.api( + self.papi.nat44_add_del_lb_static_mapping, + {'is_add': is_add, + 'external_addr': external_addr, + 'external_port': external_port, + 'protocol': protocol, + 'vrf_id': vrf_id, + 'local_num': local_num, + 'locals': locals}) + + def nat44_lb_static_mapping_dump(self): + """Dump NAT44 load balancing static mappings + + :return: Dictionary of NAT44 load balancing static mapping + """ + return self.api(self.papi.nat44_lb_static_mapping_dump, {}) + def nat_det_add_del_map( self, in_addr, -- cgit 1.2.3-korg From 624b8d9807ac449c4077df4d2d4f40313597a224 Mon Sep 17 00:00:00 2001 From: Matus Fabian Date: Tue, 12 Sep 2017 04:15:30 -0700 Subject: NAT: fixed bug in snat_alloc_outside_address_and_port (VPP-981) Change-Id: I6c5eccd4193c44604da3fd27c108defe71b38a4b Signed-off-by: Matus Fabian --- src/plugins/nat/in2out.c | 4 ++-- src/plugins/nat/nat.c | 30 ++++++++++++++++++++++++------ src/plugins/nat/nat.h | 4 +++- 3 files changed, 29 insertions(+), 9 deletions(-) (limited to 'src/plugins/nat/nat.h') diff --git a/src/plugins/nat/in2out.c b/src/plugins/nat/in2out.c index efb3856b..d0a13237 100644 --- a/src/plugins/nat/in2out.c +++ b/src/plugins/nat/in2out.c @@ -354,7 +354,7 @@ static u32 slow_path (snat_main_t *sm, vlib_buffer_t *b0, s->in2out.fib_index); snat_free_outside_address_and_port - (sm, &s->out2in, s->outside_address_index); + (sm, thread_index, &s->out2in, s->outside_address_index); } s->outside_address_index = ~0; @@ -1283,7 +1283,7 @@ create_ses: s->out2in.port, s->in2out.fib_index); - snat_free_outside_address_and_port (sm, &s->out2in, + snat_free_outside_address_and_port (sm, thread_index, &s->out2in, s->outside_address_index); /* Remove in2out, out2in keys */ diff --git a/src/plugins/nat/nat.c b/src/plugins/nat/nat.c index faf75fcb..b98b8347 100644 --- a/src/plugins/nat/nat.c +++ b/src/plugins/nat/nat.c @@ -152,6 +152,7 @@ void snat_add_address (snat_main_t *sm, ip4_address_t *addr, u32 vrf_id) { snat_address_t * ap; snat_interface_t *i; + vlib_thread_main_t *tm = vlib_get_thread_main (); if (vrf_id != ~0) sm->vrf_mode = 1; @@ -172,7 +173,9 @@ void snat_add_address (snat_main_t *sm, ip4_address_t *addr, u32 vrf_id) else ap->fib_index = ~0; #define _(N, i, n, s) \ - clib_bitmap_alloc (ap->busy_##n##_port_bitmap, 65535); + clib_bitmap_alloc (ap->busy_##n##_port_bitmap, 65535); \ + ap->busy_##n##_ports = 0; \ + vec_validate_init_empty (ap->busy_##n##_ports_per_thread, tm->n_vlib_mains - 1, 0); foreach_snat_protocol #undef _ @@ -339,7 +342,10 @@ int snat_add_static_mapping(ip4_address_t l_addr, ip4_address_t e_addr, return VNET_API_ERROR_INVALID_VALUE; \ clib_bitmap_set_no_check (a->busy_##n##_port_bitmap, e_port, 1); \ if (e_port > 1024) \ - a->busy_##n##_ports++; \ + { \ + a->busy_##n##_ports++; \ + a->busy_##n##_ports_per_thread[e_port / sm->port_per_thread]++; \ + } \ break; foreach_snat_protocol #undef _ @@ -432,7 +438,10 @@ int snat_add_static_mapping(ip4_address_t l_addr, ip4_address_t e_addr, case SNAT_PROTOCOL_##N: \ clib_bitmap_set_no_check (a->busy_##n##_port_bitmap, e_port, 0); \ if (e_port > 1024) \ - a->busy_##n##_ports--; \ + { \ + a->busy_##n##_ports--; \ + a->busy_##n##_ports_per_thread[e_port / sm->port_per_thread]--; \ + } \ break; foreach_snat_protocol #undef _ @@ -647,7 +656,10 @@ int nat44_add_del_lb_static_mapping (ip4_address_t e_addr, u16 e_port, return VNET_API_ERROR_INVALID_VALUE; \ clib_bitmap_set_no_check (a->busy_##n##_port_bitmap, e_port, 1); \ if (e_port > 1024) \ - a->busy_##n##_ports++; \ + { \ + a->busy_##n##_ports++; \ + a->busy_##n##_ports_per_thread[e_port / sm->port_per_thread]++; \ + } \ break; foreach_snat_protocol #undef _ @@ -773,7 +785,10 @@ int nat44_add_del_lb_static_mapping (ip4_address_t e_addr, u16 e_port, case SNAT_PROTOCOL_##N: \ clib_bitmap_set_no_check (a->busy_##n##_port_bitmap, e_port, 0); \ if (e_port > 1024) \ - a->busy_##n##_ports--; \ + { \ + a->busy_##n##_ports--; \ + a->busy_##n##_ports_per_thread[e_port / sm->port_per_thread]--; \ + } \ break; foreach_snat_protocol #undef _ @@ -1241,6 +1256,7 @@ static clib_error_t * snat_init (vlib_main_t * vm) VLIB_INIT_FUNCTION (snat_init); void snat_free_outside_address_and_port (snat_main_t * sm, + u32 thread_index, snat_session_key_t * k, u32 address_index) { @@ -1260,6 +1276,7 @@ void snat_free_outside_address_and_port (snat_main_t * sm, clib_bitmap_set_no_check (a->busy_##n##_port_bitmap, \ port_host_byte_order, 0); \ a->busy_##n##_ports--; \ + a->busy_##n##_ports_per_thread[thread_index]--; \ break; foreach_snat_protocol #undef _ @@ -1382,7 +1399,7 @@ int snat_alloc_outside_address_and_port (snat_main_t * sm, { #define _(N, j, n, s) \ case SNAT_PROTOCOL_##N: \ - if (a->busy_##n##_ports < (sm->port_per_thread * sm->num_snat_thread)) \ + if (a->busy_##n##_ports_per_thread[thread_index] < sm->port_per_thread) \ { \ while (1) \ { \ @@ -1392,6 +1409,7 @@ int snat_alloc_outside_address_and_port (snat_main_t * sm, if (clib_bitmap_get_no_check (a->busy_##n##_port_bitmap, portnum)) \ continue; \ clib_bitmap_set_no_check (a->busy_##n##_port_bitmap, portnum, 1); \ + a->busy_##n##_ports_per_thread[thread_index]++; \ a->busy_##n##_ports++; \ k->addr = a->addr; \ k->port = clib_host_to_net_u16(portnum); \ diff --git a/src/plugins/nat/nat.h b/src/plugins/nat/nat.h index 8935144d..d1ba5d55 100644 --- a/src/plugins/nat/nat.h +++ b/src/plugins/nat/nat.h @@ -182,7 +182,8 @@ typedef struct { ip4_address_t addr; u32 fib_index; #define _(N, i, n, s) \ - u32 busy_##n##_ports; \ + u16 busy_##n##_ports; \ + u16 * busy_##n##_ports_per_thread; \ uword * busy_##n##_port_bitmap; foreach_snat_protocol #undef _ @@ -389,6 +390,7 @@ extern vlib_node_registration_t snat_hairpin_dst_node; extern vlib_node_registration_t snat_hairpin_src_node; void snat_free_outside_address_and_port (snat_main_t * sm, + u32 thread_index, snat_session_key_t * k, u32 address_index); -- cgit 1.2.3-korg From 092b3cd59f17d5c3ebe167d8729273838afbe2cb Mon Sep 17 00:00:00 2001 From: Matus Fabian Date: Tue, 19 Sep 2017 05:42:38 -0700 Subject: NAT: move session and user lookup tables to per thread data (VPP-986) Change-Id: I41a51bb36e31e05c76fef0b34fe006afbee27729 Signed-off-by: Matus Fabian --- src/plugins/nat/in2out.c | 118 ++++++++++++++++++++++++++-------------------- src/plugins/nat/nat.c | 93 +++++++++++++++++++++--------------- src/plugins/nat/nat.h | 14 +++--- src/plugins/nat/nat_api.c | 4 +- src/plugins/nat/out2in.c | 32 ++++++++----- 5 files changed, 151 insertions(+), 110 deletions(-) (limited to 'src/plugins/nat/nat.h') diff --git a/src/plugins/nat/in2out.c b/src/plugins/nat/in2out.c index 9196ccbb..8b658302 100755 --- a/src/plugins/nat/in2out.c +++ b/src/plugins/nat/in2out.c @@ -191,7 +191,7 @@ snat_not_translate_fast (snat_main_t * sm, vlib_node_runtime_t *node, static inline int snat_not_translate (snat_main_t * sm, vlib_node_runtime_t *node, u32 sw_if_index0, ip4_header_t * ip0, u32 proto0, - u32 rx_fib_index0) + u32 rx_fib_index0, u32 thread_index) { udp_header_t * udp0 = ip4_next_header (ip0); snat_session_key_t key0, sm0; @@ -205,7 +205,8 @@ snat_not_translate (snat_main_t * sm, vlib_node_runtime_t *node, /* NAT packet aimed at external address if */ /* has active sessions */ - if (clib_bihash_search_8_8 (&sm->out2in, &kv0, &value0)) + if (clib_bihash_search_8_8 (&sm->per_thread_data[thread_index].out2in, &kv0, + &value0)) { /* or is static mappings */ if (!snat_static_mapping_match(sm, key0, &sm0, 1, 0)) @@ -256,7 +257,8 @@ static u32 slow_path (snat_main_t *sm, vlib_buffer_t *b0, kv0.key = user_key.as_u64; /* Ever heard of the "user" = src ip4 address before? */ - if (clib_bihash_search_8_8 (&sm->user_hash, &kv0, &value0)) + if (clib_bihash_search_8_8 (&sm->per_thread_data[thread_index].user_hash, + &kv0, &value0)) { /* no, make a new one */ pool_get (sm->per_thread_data[thread_index].users, u); @@ -275,7 +277,8 @@ static u32 slow_path (snat_main_t *sm, vlib_buffer_t *b0, kv0.value = u - sm->per_thread_data[thread_index].users; /* add user */ - clib_bihash_add_del_8_8 (&sm->user_hash, &kv0, 1 /* is_add */); + clib_bihash_add_del_8_8 (&sm->per_thread_data[thread_index].user_hash, + &kv0, 1 /* is_add */); } else { @@ -339,10 +342,12 @@ static u32 slow_path (snat_main_t *sm, vlib_buffer_t *b0, { /* Remove in2out, out2in keys */ kv0.key = s->in2out.as_u64; - if (clib_bihash_add_del_8_8 (&sm->in2out, &kv0, 0 /* is_add */)) + if (clib_bihash_add_del_8_8 (&sm->per_thread_data[thread_index].in2out, + &kv0, 0 /* is_add */)) clib_warning ("in2out key delete failed"); kv0.key = s->out2in.as_u64; - if (clib_bihash_add_del_8_8 (&sm->out2in, &kv0, 0 /* is_add */)) + if (clib_bihash_add_del_8_8 (&sm->per_thread_data[thread_index].out2in, + &kv0, 0 /* is_add */)) clib_warning ("out2in key delete failed"); /* log NAT event */ @@ -431,13 +436,15 @@ static u32 slow_path (snat_main_t *sm, vlib_buffer_t *b0, /* Add to translation hashes */ kv0.key = s->in2out.as_u64; kv0.value = s - sm->per_thread_data[thread_index].sessions; - if (clib_bihash_add_del_8_8 (&sm->in2out, &kv0, 1 /* is_add */)) + if (clib_bihash_add_del_8_8 (&sm->per_thread_data[thread_index].in2out, &kv0, + 1 /* is_add */)) clib_warning ("in2out key add failed"); kv0.key = s->out2in.as_u64; kv0.value = s - sm->per_thread_data[thread_index].sessions; - if (clib_bihash_add_del_8_8 (&sm->out2in, &kv0, 1 /* is_add */)) + if (clib_bihash_add_del_8_8 (&sm->per_thread_data[thread_index].out2in, &kv0, + 1 /* is_add */)) clib_warning ("out2in key add failed"); /* Add to translated packets worker lookup */ @@ -554,10 +561,11 @@ u32 icmp_match_in2out_slow(snat_main_t *sm, vlib_node_runtime_t *node, kv0.key = key0.as_u64; - if (clib_bihash_search_8_8 (&sm->in2out, &kv0, &value0)) + if (clib_bihash_search_8_8 (&sm->per_thread_data[thread_index].in2out, &kv0, + &value0)) { if (PREDICT_FALSE(snat_not_translate(sm, node, sw_if_index0, ip0, - IP_PROTOCOL_ICMP, rx_fib_index0) && + IP_PROTOCOL_ICMP, rx_fib_index0, thread_index) && vnet_buffer(b0)->sw_if_index[VLIB_TX] == ~0)) { dont_translate = 1; @@ -841,8 +849,22 @@ snat_hairpinning (snat_main_t *sm, key0.fib_index = sm->outside_fib_index; kv0.key = key0.as_u64; + if (sm->num_workers > 1) + { + k0.addr = ip0->dst_address; + k0.port = udp0->dst_port; + k0.fib_index = sm->outside_fib_index; + kv0.key = k0.as_u64; + if (clib_bihash_search_8_8 (&sm->worker_by_out, &kv0, &value0)) + return; + else + ti = value0.value; + } + else + ti = sm->num_workers; + /* Check if destination is in active sessions */ - if (clib_bihash_search_8_8 (&sm->out2in, &kv0, &value0)) + if (clib_bihash_search_8_8 (&sm->per_thread_data[ti].out2in, &kv0, &value0)) { /* or static mappings */ if (!snat_static_mapping_match(sm, key0, &sm0, 1, 0)) @@ -855,19 +877,6 @@ snat_hairpinning (snat_main_t *sm, else { si = value0.value; - if (sm->num_workers > 1) - { - k0.addr = ip0->dst_address; - k0.port = udp0->dst_port; - k0.fib_index = sm->outside_fib_index; - kv0.key = k0.as_u64; - if (clib_bihash_search_8_8 (&sm->worker_by_out, &kv0, &value0)) - ASSERT(0); - else - ti = value0.value; - } - else - ti = sm->num_workers; s0 = pool_elt_at_index (sm->per_thread_data[ti].sessions, si); new_dst_addr0 = s0->in2out.addr.as_u32; @@ -940,8 +949,23 @@ snat_icmp_hairpinning (snat_main_t *sm, key0.fib_index = sm->outside_fib_index; kv0.key = key0.as_u64; + if (sm->num_workers > 1) + { + k0.addr = ip0->dst_address; + k0.port = icmp_id0; + k0.fib_index = sm->outside_fib_index; + kv0.key = k0.as_u64; + if (clib_bihash_search_8_8 (&sm->worker_by_out, &kv0, &value0)) + return; + else + ti = value0.value; + } + else + ti = sm->num_workers; + /* Check if destination is in active sessions */ - if (clib_bihash_search_8_8 (&sm->out2in, &kv0, &value0)) + if (clib_bihash_search_8_8 (&sm->per_thread_data[ti].out2in, &kv0, + &value0)) { /* or static mappings */ if (!snat_static_mapping_match(sm, key0, &sm0, 1, 0)) @@ -953,19 +977,6 @@ snat_icmp_hairpinning (snat_main_t *sm, else { si = value0.value; - if (sm->num_workers > 1) - { - k0.addr = ip0->dst_address; - k0.port = icmp_id0; - k0.fib_index = sm->outside_fib_index; - kv0.key = k0.as_u64; - if (clib_bihash_search_8_8 (&sm->worker_by_out, &kv0, &value0)) - ASSERT(0); - else - ti = value0.value; - } - else - ti = sm->num_workers; s0 = pool_elt_at_index (sm->per_thread_data[ti].sessions, si); new_dst_addr0 = s0->in2out.addr.as_u32; @@ -1141,7 +1152,7 @@ snat_in2out_unknown_proto (snat_main_t *sm, kv.key = u_key.as_u64; /* Ever heard of the "user" = src ip4 address before? */ - if (clib_bihash_search_8_8 (&sm->user_hash, &kv, &value)) + if (clib_bihash_search_8_8 (&tsm->user_hash, &kv, &value)) { /* no, make a new one */ pool_get (tsm->users, u); @@ -1158,7 +1169,7 @@ snat_in2out_unknown_proto (snat_main_t *sm, kv.value = u - tsm->users; /* add user */ - clib_bihash_add_del_8_8 (&sm->user_hash, &kv, 1); + clib_bihash_add_del_8_8 (&tsm->user_hash, &kv, 1); } else { @@ -1183,7 +1194,7 @@ snat_in2out_unknown_proto (snat_main_t *sm, else { /* Choose same out address as for TCP/UDP session to same destination */ - if (!clib_bihash_search_8_8 (&sm->user_hash, &kv, &value)) + if (!clib_bihash_search_8_8 (&tsm->user_hash, &kv, &value)) { head_index = u->sessions_per_user_list_head_index; head = pool_elt_at_index (tsm->list_pool, head_index); @@ -1288,10 +1299,12 @@ create_ses: /* Remove in2out, out2in keys */ kv.key = s->in2out.as_u64; - if (clib_bihash_add_del_8_8 (&sm->in2out, &kv, 0)) + if (clib_bihash_add_del_8_8 ( + &sm->per_thread_data[thread_index].in2out, &kv, 0)) clib_warning ("in2out key del failed"); kv.key = s->out2in.as_u64; - if (clib_bihash_add_del_8_8 (&sm->out2in, &kv, 0)) + if (clib_bihash_add_del_8_8 ( + &sm->per_thread_data[thread_index].out2in, &kv, 0)) clib_warning ("out2in key del failed"); } } @@ -1424,7 +1437,7 @@ snat_in2out_lb (snat_main_t *sm, kv.key = u_key.as_u64; /* Ever heard of the "user" = src ip4 address before? */ - if (clib_bihash_search_8_8 (&sm->user_hash, &kv, &value)) + if (clib_bihash_search_8_8 (&tsm->user_hash, &kv, &value)) { /* no, make a new one */ pool_get (tsm->users, u); @@ -1441,7 +1454,7 @@ snat_in2out_lb (snat_main_t *sm, kv.value = u - tsm->users; /* add user */ - if (clib_bihash_add_del_8_8 (&sm->user_hash, &kv, 1)) + if (clib_bihash_add_del_8_8 (&tsm->user_hash, &kv, 1)) clib_warning ("user key add failed"); } else @@ -1653,12 +1666,13 @@ snat_in2out_node_fn_inline (vlib_main_t * vm, kv0.key = key0.as_u64; - if (PREDICT_FALSE (clib_bihash_search_8_8 (&sm->in2out, &kv0, &value0) != 0)) + if (PREDICT_FALSE (clib_bihash_search_8_8 ( + &sm->per_thread_data[thread_index].in2out, &kv0, &value0) != 0)) { if (is_slow_path) { if (PREDICT_FALSE(snat_not_translate(sm, node, sw_if_index0, - ip0, proto0, rx_fib_index0)) && !is_output_feature) + ip0, proto0, rx_fib_index0, thread_index)) && !is_output_feature) goto trace00; next0 = slow_path (sm, b0, ip0, rx_fib_index0, &key0, @@ -1824,12 +1838,13 @@ snat_in2out_node_fn_inline (vlib_main_t * vm, kv1.key = key1.as_u64; - if (PREDICT_FALSE(clib_bihash_search_8_8 (&sm->in2out, &kv1, &value1) != 0)) + if (PREDICT_FALSE(clib_bihash_search_8_8 ( + &sm->per_thread_data[thread_index].in2out, &kv1, &value1) != 0)) { if (is_slow_path) { if (PREDICT_FALSE(snat_not_translate(sm, node, sw_if_index1, - ip1, proto1, rx_fib_index1)) && !is_output_feature) + ip1, proto1, rx_fib_index1, thread_index)) && !is_output_feature) goto trace01; next1 = slow_path (sm, b1, ip1, rx_fib_index1, &key1, @@ -2031,12 +2046,13 @@ snat_in2out_node_fn_inline (vlib_main_t * vm, kv0.key = key0.as_u64; - if (clib_bihash_search_8_8 (&sm->in2out, &kv0, &value0)) + if (clib_bihash_search_8_8 (&sm->per_thread_data[thread_index].in2out, + &kv0, &value0)) { if (is_slow_path) { if (PREDICT_FALSE(snat_not_translate(sm, node, sw_if_index0, - ip0, proto0, rx_fib_index0)) && !is_output_feature) + ip0, proto0, rx_fib_index0, thread_index)) && !is_output_feature) goto trace0; next0 = slow_path (sm, b0, ip0, rx_fib_index0, &key0, diff --git a/src/plugins/nat/nat.c b/src/plugins/nat/nat.c index 36b72664..876b6aad 100644 --- a/src/plugins/nat/nat.c +++ b/src/plugins/nat/nat.c @@ -483,13 +483,13 @@ int snat_add_static_mapping(ip4_address_t l_addr, ip4_address_t e_addr, u_key.addr = m->local_addr; u_key.fib_index = m->fib_index; kv.key = u_key.as_u64; - if (!clib_bihash_search_8_8 (&sm->user_hash, &kv, &value)) + if (!clib_bihash_search_8_8 (&sm->worker_by_in, &kv, &value)) + tsm = vec_elt_at_index (sm->per_thread_data, value.value); + else + tsm = vec_elt_at_index (sm->per_thread_data, sm->num_workers); + if (!clib_bihash_search_8_8 (&tsm->user_hash, &kv, &value)) { user_index = value.value; - if (!clib_bihash_search_8_8 (&sm->worker_by_in, &kv, &value)) - tsm = vec_elt_at_index (sm->per_thread_data, value.value); - else - tsm = vec_elt_at_index (sm->per_thread_data, sm->num_workers); u = pool_elt_at_index (tsm->users, user_index); if (u->nstaticsessions) { @@ -548,10 +548,10 @@ int snat_add_static_mapping(ip4_address_t l_addr, ip4_address_t e_addr, s->in2out.fib_index); value.key = s->in2out.as_u64; - if (clib_bihash_add_del_8_8 (&sm->in2out, &value, 0)) + if (clib_bihash_add_del_8_8 (&tsm->in2out, &value, 0)) clib_warning ("in2out key del failed"); value.key = s->out2in.as_u64; - if (clib_bihash_add_del_8_8 (&sm->out2in, &value, 0)) + if (clib_bihash_add_del_8_8 (&tsm->out2in, &value, 0)) clib_warning ("out2in key del failed"); delete: pool_put (tsm->sessions, s); @@ -566,7 +566,7 @@ delete: if (addr_only) { pool_put (tsm->users, u); - clib_bihash_add_del_8_8 (&sm->user_hash, &kv, 0); + clib_bihash_add_del_8_8 (&tsm->user_hash, &kv, 0); } } } @@ -615,6 +615,7 @@ int nat44_add_del_lb_static_mapping (ip4_address_t e_addr, u16 e_port, snat_user_key_t w_key0; snat_worker_key_t w_key1; u32 worker_index = 0; + snat_main_per_thread_data_t *tsm; m_key.addr = e_addr; m_key.port = e_port; @@ -695,16 +696,6 @@ int nat44_add_del_lb_static_mapping (ip4_address_t e_addr, u16 e_port, clib_warning ("static_mapping_by_external key add failed"); return VNET_API_ERROR_UNSPECIFIED; } - m_key.port = clib_host_to_net_u16 (m->external_port); - kv.key = m_key.as_u64; - kv.value = ~0ULL; - if (clib_bihash_add_del_8_8(&sm->out2in, &kv, 1)) - { - clib_warning ("static_mapping_by_local key add failed"); - return VNET_API_ERROR_UNSPECIFIED; - } - - m_key.fib_index = m->fib_index; /* Assign worker */ if (sm->workers) @@ -729,8 +720,21 @@ int nat44_add_del_lb_static_mapping (ip4_address_t e_addr, u16 e_port, clib_warning ("worker-by-out add key failed"); return VNET_API_ERROR_UNSPECIFIED; } + tsm = vec_elt_at_index (sm->per_thread_data, worker_index); + } + else + tsm = vec_elt_at_index (sm->per_thread_data, sm->num_workers); + + m_key.port = clib_host_to_net_u16 (m->external_port); + kv.key = m_key.as_u64; + kv.value = ~0ULL; + if (clib_bihash_add_del_8_8(&tsm->out2in, &kv, 1)) + { + clib_warning ("static_mapping_by_local key add failed"); + return VNET_API_ERROR_UNSPECIFIED; } + m_key.fib_index = m->fib_index; for (i = 0; i < vec_len (locals); i++) { m_key.addr = locals[i].addr; @@ -744,7 +748,7 @@ int nat44_add_del_lb_static_mapping (ip4_address_t e_addr, u16 e_port, m_key.port = clib_host_to_net_u16 (locals[i].port); kv.key = m_key.as_u64; kv.value = ~0ULL; - if (clib_bihash_add_del_8_8(&sm->in2out, &kv, 1)) + if (clib_bihash_add_del_8_8(&tsm->in2out, &kv, 1)) { clib_warning ("in2out key add failed"); return VNET_API_ERROR_UNSPECIFIED; @@ -801,6 +805,15 @@ int nat44_add_del_lb_static_mapping (ip4_address_t e_addr, u16 e_port, } } + w_key1.addr = m->external_addr; + w_key1.port = clib_host_to_net_u16 (m->external_port); + w_key1.fib_index = sm->outside_fib_index; + kv.key = w_key1.as_u64; + if (!clib_bihash_search_8_8 (&sm->worker_by_out, &kv, &value)) + tsm = vec_elt_at_index (sm->per_thread_data, value.value); + else + tsm = vec_elt_at_index (sm->per_thread_data, sm->num_workers); + m_key.addr = m->external_addr; m_key.port = m->external_port; m_key.protocol = m->proto; @@ -813,7 +826,7 @@ int nat44_add_del_lb_static_mapping (ip4_address_t e_addr, u16 e_port, } m_key.port = clib_host_to_net_u16 (m->external_port); kv.key = m_key.as_u64; - if (clib_bihash_add_del_8_8(&sm->out2in, &kv, 0)) + if (clib_bihash_add_del_8_8(&tsm->out2in, &kv, 0)) { clib_warning ("outi2in key del failed"); return VNET_API_ERROR_UNSPECIFIED; @@ -832,7 +845,7 @@ int nat44_add_del_lb_static_mapping (ip4_address_t e_addr, u16 e_port, } m_key.port = clib_host_to_net_u16 (local->port); kv.key = m_key.as_u64; - if (clib_bihash_add_del_8_8(&sm->in2out, &kv, 0)) + if (clib_bihash_add_del_8_8(&tsm->in2out, &kv, 0)) { clib_warning ("in2out key del failed"); return VNET_API_ERROR_UNSPECIFIED; @@ -938,16 +951,16 @@ int snat_del_address (snat_main_t *sm, ip4_address_t addr, u8 delete_sm) ses->out2in.port, ses->in2out.fib_index); kv.key = ses->in2out.as_u64; - clib_bihash_add_del_8_8 (&sm->in2out, &kv, 0); + clib_bihash_add_del_8_8 (&tsm->in2out, &kv, 0); kv.key = ses->out2in.as_u64; - clib_bihash_add_del_8_8 (&sm->out2in, &kv, 0); + clib_bihash_add_del_8_8 (&tsm->out2in, &kv, 0); } vec_add1 (ses_to_be_removed, ses - tsm->sessions); clib_dlist_remove (tsm->list_pool, ses->per_user_index); user_key.addr = ses->in2out.addr; user_key.fib_index = ses->in2out.fib_index; kv.key = user_key.as_u64; - if (!clib_bihash_search_8_8 (&sm->user_hash, &kv, &value)) + if (!clib_bihash_search_8_8 (&tsm->user_hash, &kv, &value)) { u = pool_elt_at_index (tsm->users, value.value); u->nsessions--; @@ -2130,6 +2143,7 @@ snat_config (vlib_main_t * vm, unformat_input_t * input) u32 static_mapping_memory_size = 64<<20; u8 static_mapping_only = 0; u8 static_mapping_connection_tracking = 0; + snat_main_per_thread_data_t *tsm; sm->deterministic = 0; @@ -2204,21 +2218,24 @@ snat_config (vlib_main_t * vm, unformat_input_t * input) sm->icmp_match_in2out_cb = icmp_match_in2out_slow; sm->icmp_match_out2in_cb = icmp_match_out2in_slow; - clib_bihash_init_8_8 (&sm->worker_by_in, "worker-by-in", user_buckets, - user_memory_size); - - clib_bihash_init_8_8 (&sm->worker_by_out, "worker-by-out", user_buckets, - user_memory_size); + vec_foreach (tsm, sm->per_thread_data) + { + clib_bihash_init_8_8 (&tsm->in2out, "in2out", translation_buckets, + translation_memory_size); - clib_bihash_init_8_8 (&sm->in2out, "in2out", translation_buckets, - translation_memory_size); + clib_bihash_init_8_8 (&tsm->out2in, "out2in", translation_buckets, + translation_memory_size); - clib_bihash_init_8_8 (&sm->out2in, "out2in", translation_buckets, - translation_memory_size); + clib_bihash_init_8_8 (&tsm->user_hash, "users", user_buckets, + user_memory_size); + } - clib_bihash_init_8_8 (&sm->user_hash, "users", user_buckets, + clib_bihash_init_8_8 (&sm->worker_by_in, "worker-by-in", user_buckets, user_memory_size); + clib_bihash_init_8_8 (&sm->worker_by_out, "worker-by-out", + translation_buckets, translation_memory_size); + clib_bihash_init_16_8 (&sm->in2out_ed, "in2out-ed", translation_buckets, translation_memory_size); @@ -2595,10 +2612,6 @@ show_snat_command_fn (vlib_main_t * vm, if (verbose > 0) { - vlib_cli_output (vm, "%U", format_bihash_8_8, &sm->in2out, - verbose - 1); - vlib_cli_output (vm, "%U", format_bihash_8_8, &sm->out2in, - verbose - 1); vlib_cli_output (vm, "%U", format_bihash_16_8, &sm->in2out_ed, verbose - 1); vlib_cli_output (vm, "%U", format_bihash_16_8, &sm->out2in_ed, @@ -2617,6 +2630,10 @@ show_snat_command_fn (vlib_main_t * vm, vlib_worker_thread_t *w = vlib_worker_threads + j; vlib_cli_output (vm, "Thread %d (%s at lcore %u):", j, w->name, w->lcore_id); + vlib_cli_output (vm, " %U", format_bihash_8_8, &tsm->in2out, + verbose - 1); + vlib_cli_output (vm, " %U", format_bihash_8_8, &tsm->out2in, + verbose - 1); vlib_cli_output (vm, " %d list pool elements", pool_elts (tsm->list_pool)); diff --git a/src/plugins/nat/nat.h b/src/plugins/nat/nat.h index d1ba5d55..f970821b 100644 --- a/src/plugins/nat/nat.h +++ b/src/plugins/nat/nat.h @@ -244,6 +244,13 @@ typedef struct { } snat_static_map_resolve_t; typedef struct { + /* Main lookup tables */ + clib_bihash_8_8_t out2in; + clib_bihash_8_8_t in2out; + + /* Find-a-user => src address lookup */ + clib_bihash_8_8_t user_hash; + /* User pool */ snat_user_t * users; @@ -271,17 +278,10 @@ typedef u32 snat_icmp_match_function_t (struct snat_main_s *sm, typedef u32 (snat_get_worker_function_t) (ip4_header_t * ip, u32 rx_fib_index); typedef struct snat_main_s { - /* Main lookup tables */ - clib_bihash_8_8_t out2in; - clib_bihash_8_8_t in2out; - /* Endpoint address dependent sessions lookup tables */ clib_bihash_16_8_t out2in_ed; clib_bihash_16_8_t in2out_ed; - /* Find-a-user => src address lookup */ - clib_bihash_8_8_t user_hash; - /* Non-translated packets worker lookup => src address + VRF */ clib_bihash_8_8_t worker_by_in; diff --git a/src/plugins/nat/nat_api.c b/src/plugins/nat/nat_api.c index fa20f2cc..50b4a9ae 100644 --- a/src/plugins/nat/nat_api.c +++ b/src/plugins/nat/nat_api.c @@ -870,7 +870,7 @@ static void tsm = vec_elt_at_index (sm->per_thread_data, value.value); else tsm = vec_elt_at_index (sm->per_thread_data, sm->num_workers); - if (clib_bihash_search_8_8 (&sm->user_hash, &key, &value)) + if (clib_bihash_search_8_8 (&tsm->user_hash, &key, &value)) return; u = pool_elt_at_index (tsm->users, value.value); if (!u->nsessions && !u->nstaticsessions) @@ -2105,7 +2105,7 @@ vl_api_nat44_user_session_dump_t_handler (vl_api_nat44_user_session_dump_t * tsm = vec_elt_at_index (sm->per_thread_data, value.value); else tsm = vec_elt_at_index (sm->per_thread_data, sm->num_workers); - if (clib_bihash_search_8_8 (&sm->user_hash, &key, &value)) + if (clib_bihash_search_8_8 (&tsm->user_hash, &key, &value)) return; u = pool_elt_at_index (tsm->users, value.value); if (!u->nsessions && !u->nstaticsessions) diff --git a/src/plugins/nat/out2in.c b/src/plugins/nat/out2in.c index 52adc8a8..6472e7ff 100755 --- a/src/plugins/nat/out2in.c +++ b/src/plugins/nat/out2in.c @@ -146,7 +146,8 @@ create_session_for_static_mapping (snat_main_t *sm, kv0.key = user_key.as_u64; /* Ever heard of the "user" = inside ip4 address before? */ - if (clib_bihash_search_8_8 (&sm->user_hash, &kv0, &value0)) + if (clib_bihash_search_8_8 (&sm->per_thread_data[thread_index].user_hash, + &kv0, &value0)) { /* no, make a new one */ pool_get (sm->per_thread_data[thread_index].users, u); @@ -166,7 +167,8 @@ create_session_for_static_mapping (snat_main_t *sm, kv0.value = u - sm->per_thread_data[thread_index].users; /* add user */ - clib_bihash_add_del_8_8 (&sm->user_hash, &kv0, 1 /* is_add */); + clib_bihash_add_del_8_8 (&sm->per_thread_data[thread_index].user_hash, + &kv0, 1 /* is_add */); /* add non-traslated packets worker lookup */ kv0.value = thread_index; @@ -211,13 +213,15 @@ create_session_for_static_mapping (snat_main_t *sm, /* Add to translation hashes */ kv0.key = s->in2out.as_u64; kv0.value = s - sm->per_thread_data[thread_index].sessions; - if (clib_bihash_add_del_8_8 (&sm->in2out, &kv0, 1 /* is_add */)) + if (clib_bihash_add_del_8_8 (&sm->per_thread_data[thread_index].in2out, &kv0, + 1 /* is_add */)) clib_warning ("in2out key add failed"); kv0.key = s->out2in.as_u64; kv0.value = s - sm->per_thread_data[thread_index].sessions; - if (clib_bihash_add_del_8_8 (&sm->out2in, &kv0, 1 /* is_add */)) + if (clib_bihash_add_del_8_8 (&sm->per_thread_data[thread_index].out2in, &kv0, + 1 /* is_add */)) clib_warning ("out2in key add failed"); /* log NAT event */ @@ -325,7 +329,8 @@ u32 icmp_match_out2in_slow(snat_main_t *sm, vlib_node_runtime_t *node, kv0.key = key0.as_u64; - if (clib_bihash_search_8_8 (&sm->out2in, &kv0, &value0)) + if (clib_bihash_search_8_8 (&sm->per_thread_data[thread_index].out2in, &kv0, + &value0)) { /* Try to match static mapping by external address and port, destination address and port in packet */ @@ -672,7 +677,7 @@ snat_out2in_unknown_proto (snat_main_t *sm, kv.key = u_key.as_u64; /* Ever heard of the "user" = src ip4 address before? */ - if (clib_bihash_search_8_8 (&sm->user_hash, &kv, &value)) + if (clib_bihash_search_8_8 (&tsm->user_hash, &kv, &value)) { /* no, make a new one */ pool_get (tsm->users, u); @@ -689,7 +694,7 @@ snat_out2in_unknown_proto (snat_main_t *sm, kv.value = u - tsm->users; /* add user */ - clib_bihash_add_del_8_8 (&sm->user_hash, &kv, 1); + clib_bihash_add_del_8_8 (&tsm->user_hash, &kv, 1); } else { @@ -804,7 +809,7 @@ snat_out2in_lb (snat_main_t *sm, kv.key = u_key.as_u64; /* Ever heard of the "user" = src ip4 address before? */ - if (clib_bihash_search_8_8 (&sm->user_hash, &kv, &value)) + if (clib_bihash_search_8_8 (&tsm->user_hash, &kv, &value)) { /* no, make a new one */ pool_get (tsm->users, u); @@ -821,7 +826,7 @@ snat_out2in_lb (snat_main_t *sm, kv.value = u - tsm->users; /* add user */ - if (clib_bihash_add_del_8_8 (&sm->user_hash, &kv, 1)) + if (clib_bihash_add_del_8_8 (&tsm->user_hash, &kv, 1)) clib_warning ("user key add failed"); } else @@ -1013,7 +1018,8 @@ snat_out2in_node_fn (vlib_main_t * vm, kv0.key = key0.as_u64; - if (clib_bihash_search_8_8 (&sm->out2in, &kv0, &value0)) + if (clib_bihash_search_8_8 (&sm->per_thread_data[thread_index].out2in, + &kv0, &value0)) { /* Try to match static mapping by external address and port, destination address and port in packet */ @@ -1164,7 +1170,8 @@ snat_out2in_node_fn (vlib_main_t * vm, kv1.key = key1.as_u64; - if (clib_bihash_search_8_8 (&sm->out2in, &kv1, &value1)) + if (clib_bihash_search_8_8 (&sm->per_thread_data[thread_index].out2in, + &kv1, &value1)) { /* Try to match static mapping by external address and port, destination address and port in packet */ @@ -1351,7 +1358,8 @@ snat_out2in_node_fn (vlib_main_t * vm, kv0.key = key0.as_u64; - if (clib_bihash_search_8_8 (&sm->out2in, &kv0, &value0)) + if (clib_bihash_search_8_8 (&sm->per_thread_data[thread_index].out2in, + &kv0, &value0)) { /* Try to match static mapping by external address and port, destination address and port in packet */ -- cgit 1.2.3-korg From ed3c160983d302909dee5223675a2b356d306c81 Mon Sep 17 00:00:00 2001 From: Matus Fabian Date: Thu, 21 Sep 2017 05:07:12 -0700 Subject: NAT: remove worker_by_out lookup hash table (VPP-989) Change-Id: Ibcd2cf22348ae5a72770a8f8ad25cbe8df7fd390 Signed-off-by: Matus Fabian --- src/plugins/nat/in2out.c | 79 ++++++---------------- src/plugins/nat/nat.c | 170 +++++++++++++++++++++++++++-------------------- src/plugins/nat/nat.h | 4 +- 3 files changed, 120 insertions(+), 133 deletions(-) (limited to 'src/plugins/nat/nat.h') diff --git a/src/plugins/nat/in2out.c b/src/plugins/nat/in2out.c index 8b658302..dfe10303 100755 --- a/src/plugins/nat/in2out.c +++ b/src/plugins/nat/in2out.c @@ -241,7 +241,6 @@ static u32 slow_path (snat_main_t *sm, vlib_buffer_t *b0, u32 address_index = ~0; u32 outside_fib_index; uword * p; - snat_worker_key_t worker_by_out_key; p = hash_get (sm->ip4_main->fib_index_by_table_id, sm->outside_vrf_id); if (! p) @@ -447,14 +446,6 @@ static u32 slow_path (snat_main_t *sm, vlib_buffer_t *b0, 1 /* is_add */)) clib_warning ("out2in key add failed"); - /* Add to translated packets worker lookup */ - worker_by_out_key.addr = s->out2in.addr; - worker_by_out_key.port = s->out2in.port; - worker_by_out_key.fib_index = s->out2in.fib_index; - kv0.key = worker_by_out_key.as_u64; - kv0.value = thread_index; - clib_bihash_add_del_8_8 (&sm->worker_by_out, &kv0, 1); - /* log NAT event */ snat_ipfix_logging_nat44_ses_create(s->in2out.addr.as_u32, s->out2in.addr.as_u32, @@ -836,7 +827,6 @@ snat_hairpinning (snat_main_t *sm, u32 proto0) { snat_session_key_t key0, sm0; - snat_worker_key_t k0; snat_session_t * s0; clib_bihash_kv_8_8_t kv0, value0; ip_csum_t sum0; @@ -849,39 +839,30 @@ snat_hairpinning (snat_main_t *sm, key0.fib_index = sm->outside_fib_index; kv0.key = key0.as_u64; - if (sm->num_workers > 1) + /* Check if destination is static mappings */ + if (!snat_static_mapping_match(sm, key0, &sm0, 1, 0)) { - k0.addr = ip0->dst_address; - k0.port = udp0->dst_port; - k0.fib_index = sm->outside_fib_index; - kv0.key = k0.as_u64; - if (clib_bihash_search_8_8 (&sm->worker_by_out, &kv0, &value0)) - return; - else - ti = value0.value; + new_dst_addr0 = sm0.addr.as_u32; + new_dst_port0 = sm0.port; + vnet_buffer(b0)->sw_if_index[VLIB_TX] = sm0.fib_index; } + /* or active session */ else - ti = sm->num_workers; - - /* Check if destination is in active sessions */ - if (clib_bihash_search_8_8 (&sm->per_thread_data[ti].out2in, &kv0, &value0)) { - /* or static mappings */ - if (!snat_static_mapping_match(sm, key0, &sm0, 1, 0)) + if (sm->num_workers > 1) + ti = (clib_net_to_host_u16 (udp0->dst_port) - 1024) / sm->port_per_thread; + else + ti = sm->num_workers; + + if (!clib_bihash_search_8_8 (&sm->per_thread_data[ti].out2in, &kv0, &value0)) { - new_dst_addr0 = sm0.addr.as_u32; - new_dst_port0 = sm0.port; - vnet_buffer(b0)->sw_if_index[VLIB_TX] = sm0.fib_index; - } - } - else - { - si = value0.value; + si = value0.value; - s0 = pool_elt_at_index (sm->per_thread_data[ti].sessions, si); - new_dst_addr0 = s0->in2out.addr.as_u32; - new_dst_port0 = s0->in2out.port; - vnet_buffer(b0)->sw_if_index[VLIB_TX] = s0->in2out.fib_index; + s0 = pool_elt_at_index (sm->per_thread_data[ti].sessions, si); + new_dst_addr0 = s0->in2out.addr.as_u32; + new_dst_port0 = s0->in2out.port; + vnet_buffer(b0)->sw_if_index[VLIB_TX] = s0->in2out.fib_index; + } } /* Destination is behind the same NAT, use internal address and port */ @@ -934,7 +915,6 @@ snat_icmp_hairpinning (snat_main_t *sm, { snat_session_key_t key0, sm0; clib_bihash_kv_8_8_t kv0, value0; - snat_worker_key_t k0; u32 new_dst_addr0 = 0, old_dst_addr0, si, ti = 0; ip_csum_t sum0; snat_session_t *s0; @@ -950,16 +930,7 @@ snat_icmp_hairpinning (snat_main_t *sm, kv0.key = key0.as_u64; if (sm->num_workers > 1) - { - k0.addr = ip0->dst_address; - k0.port = icmp_id0; - k0.fib_index = sm->outside_fib_index; - kv0.key = k0.as_u64; - if (clib_bihash_search_8_8 (&sm->worker_by_out, &kv0, &value0)) - return; - else - ti = value0.value; - } + ti = (clib_net_to_host_u16 (icmp_id0) - 1024) / sm->port_per_thread; else ti = sm->num_workers; @@ -1048,7 +1019,6 @@ snat_hairpinning_unknown_proto (snat_main_t *sm, clib_bihash_kv_16_8_t s_kv, s_value; nat_ed_ses_key_t key; snat_session_key_t m_key; - snat_worker_key_t w_key; snat_static_mapping_t *m; ip_csum_t sum; snat_session_t *s; @@ -1080,16 +1050,7 @@ snat_hairpinning_unknown_proto (snat_main_t *sm, else { if (sm->num_workers > 1) - { - w_key.addr = ip->dst_address; - w_key.port = 0; - w_key.fib_index = sm->outside_fib_index; - kv.key = w_key.as_u64; - if (clib_bihash_search_8_8 (&sm->worker_by_out, &kv, &value)) - return; - else - ti = value.value; - } + ti = sm->worker_out2in_cb (ip, sm->outside_fib_index); else ti = sm->num_workers; diff --git a/src/plugins/nat/nat.c b/src/plugins/nat/nat.c index 876b6aad..5f3b006e 100644 --- a/src/plugins/nat/nat.c +++ b/src/plugins/nat/nat.c @@ -344,7 +344,7 @@ int snat_add_static_mapping(ip4_address_t l_addr, ip4_address_t e_addr, if (e_port > 1024) \ { \ a->busy_##n##_ports++; \ - a->busy_##n##_ports_per_thread[e_port / sm->port_per_thread]++; \ + a->busy_##n##_ports_per_thread[(e_port - 1024) / sm->port_per_thread]++; \ } \ break; foreach_snat_protocol @@ -394,7 +394,6 @@ int snat_add_static_mapping(ip4_address_t l_addr, ip4_address_t e_addr, if (sm->workers) { snat_user_key_t w_key0; - snat_worker_key_t w_key1; w_key0.addr = m->local_addr; w_key0.fib_index = m->fib_index; @@ -412,11 +411,7 @@ int snat_add_static_mapping(ip4_address_t l_addr, ip4_address_t e_addr, kv.value = value.value; } - w_key1.addr = m->external_addr; - w_key1.port = clib_host_to_net_u16 (m->external_port); - w_key1.fib_index = sm->outside_fib_index; - kv.key = w_key1.as_u64; - clib_bihash_add_del_8_8 (&sm->worker_by_out, &kv, 1); + m->worker_index = kv.value; } } else @@ -440,7 +435,7 @@ int snat_add_static_mapping(ip4_address_t l_addr, ip4_address_t e_addr, if (e_port > 1024) \ { \ a->busy_##n##_ports--; \ - a->busy_##n##_ports_per_thread[e_port / sm->port_per_thread]--; \ + a->busy_##n##_ports_per_thread[(e_port - 1024) / sm->port_per_thread]--; \ } \ break; foreach_snat_protocol @@ -613,7 +608,6 @@ int nat44_add_del_lb_static_mapping (ip4_address_t e_addr, u16 e_port, int i; nat44_lb_addr_port_t *local; snat_user_key_t w_key0; - snat_worker_key_t w_key1; u32 worker_index = 0; snat_main_per_thread_data_t *tsm; @@ -659,7 +653,7 @@ int nat44_add_del_lb_static_mapping (ip4_address_t e_addr, u16 e_port, if (e_port > 1024) \ { \ a->busy_##n##_ports++; \ - a->busy_##n##_ports_per_thread[e_port / sm->port_per_thread]++; \ + a->busy_##n##_ports_per_thread[(e_port - 1024) / sm->port_per_thread]++; \ } \ break; foreach_snat_protocol @@ -710,17 +704,8 @@ int nat44_add_del_lb_static_mapping (ip4_address_t e_addr, u16 e_port, else worker_index = value.value; - w_key1.addr = m->external_addr; - w_key1.port = clib_host_to_net_u16 (m->external_port); - w_key1.fib_index = sm->outside_fib_index; - kv.key = w_key1.as_u64; - kv.value = worker_index; - if (clib_bihash_add_del_8_8 (&sm->worker_by_out, &kv, 1)) - { - clib_warning ("worker-by-out add key failed"); - return VNET_API_ERROR_UNSPECIFIED; - } tsm = vec_elt_at_index (sm->per_thread_data, worker_index); + m->worker_index = worker_index; } else tsm = vec_elt_at_index (sm->per_thread_data, sm->num_workers); @@ -791,7 +776,7 @@ int nat44_add_del_lb_static_mapping (ip4_address_t e_addr, u16 e_port, if (e_port > 1024) \ { \ a->busy_##n##_ports--; \ - a->busy_##n##_ports_per_thread[e_port / sm->port_per_thread]--; \ + a->busy_##n##_ports_per_thread[(e_port - 1024) / sm->port_per_thread]--; \ } \ break; foreach_snat_protocol @@ -805,15 +790,7 @@ int nat44_add_del_lb_static_mapping (ip4_address_t e_addr, u16 e_port, } } - w_key1.addr = m->external_addr; - w_key1.port = clib_host_to_net_u16 (m->external_port); - w_key1.fib_index = sm->outside_fib_index; - kv.key = w_key1.as_u64; - if (!clib_bihash_search_8_8 (&sm->worker_by_out, &kv, &value)) - tsm = vec_elt_at_index (sm->per_thread_data, value.value); - else - tsm = vec_elt_at_index (sm->per_thread_data, sm->num_workers); - + tsm = vec_elt_at_index (sm->per_thread_data, m->worker_index); m_key.addr = m->external_addr; m_key.port = m->external_port; m_key.protocol = m->proto; @@ -2074,58 +2051,114 @@ static u32 snat_get_worker_out2in_cb (ip4_header_t * ip0, u32 rx_fib_index0) { snat_main_t *sm = &snat_main; - snat_worker_key_t key0; - clib_bihash_kv_8_8_t kv0, value0; - udp_header_t * udp0; - u32 next_worker_index = 0; - - udp0 = ip4_next_header (ip0); - - key0.addr = ip0->dst_address; - key0.port = udp0->dst_port; - key0.fib_index = rx_fib_index0; + udp_header_t *udp; + u16 port; + snat_session_key_t m_key; + clib_bihash_kv_8_8_t kv, value; + snat_static_mapping_t *m; + nat_ed_ses_key_t key; + clib_bihash_kv_16_8_t s_kv, s_value; + snat_main_per_thread_data_t *tsm; + snat_session_t *s; + int i; + u32 proto; - if (PREDICT_FALSE(ip0->protocol == IP_PROTOCOL_ICMP)) + /* first try static mappings without port */ + if (PREDICT_FALSE (pool_elts (sm->static_mappings))) { - icmp46_header_t * icmp0 = (icmp46_header_t *) udp0; - icmp_echo_header_t *echo0 = (icmp_echo_header_t *)(icmp0+1); - key0.port = echo0->identifier; + m_key.addr = ip0->dst_address; + m_key.port = 0; + m_key.protocol = 0; + m_key.fib_index = rx_fib_index0; + kv.key = m_key.as_u64; + if (!clib_bihash_search_8_8 (&sm->static_mapping_by_external, &kv, &value)) + { + m = pool_elt_at_index (sm->static_mappings, value.value); + return m->worker_index; + } } - kv0.key = key0.as_u64; + proto = ip_proto_to_snat_proto (ip0->protocol); + udp = ip4_next_header (ip0); + port = udp->dst_port; - /* Ever heard of of the "user" before? */ - if (clib_bihash_search_8_8 (&sm->worker_by_out, &kv0, &value0)) + /* unknown protocol */ + if (PREDICT_FALSE (proto == ~0)) { - key0.port = 0; - kv0.key = key0.as_u64; + key.l_addr = ip0->dst_address; + key.r_addr = ip0->src_address; + key.fib_index = rx_fib_index0; + key.proto = ip0->protocol; + key.rsvd = 0; + key.l_port = 0; + s_kv.key[0] = key.as_u64[0]; + s_kv.key[1] = key.as_u64[1]; - if (clib_bihash_search_8_8 (&sm->worker_by_out, &kv0, &value0)) + if (!clib_bihash_search_16_8 (&sm->out2in_ed, &s_kv, &s_value)) { - /* No, assign next available worker (RR) */ - next_worker_index = sm->first_worker_index; - if (vec_len (sm->workers)) + for (i = 0; i < _vec_len (sm->per_thread_data); i++) { - next_worker_index += - sm->workers[sm->next_worker++ % _vec_len (sm->workers)]; + tsm = vec_elt_at_index (sm->per_thread_data, i); + if (!pool_is_free_index(tsm->sessions, s_value.value)) + { + s = pool_elt_at_index (tsm->sessions, s_value.value); + if (s->out2in.addr.as_u32 == ip0->dst_address.as_u32 && + s->out2in.port == ip0->protocol && + snat_is_unk_proto_session (s)) + return i; + } } - } + } + + /* if no session use current thread */ + return vlib_get_thread_index (); + } + + if (PREDICT_FALSE (ip0->protocol == IP_PROTOCOL_ICMP)) + { + icmp46_header_t * icmp = (icmp46_header_t *) udp; + icmp_echo_header_t *echo = (icmp_echo_header_t *)(icmp + 1); + if (!icmp_is_error_message (icmp)) + port = echo->identifier; else { - /* Static mapping without port */ - next_worker_index = value0.value; + ip4_header_t *inner_ip = (ip4_header_t *)(echo + 1); + proto = ip_proto_to_snat_proto (inner_ip->protocol); + void *l4_header = ip4_next_header (inner_ip); + switch (proto) + { + case SNAT_PROTOCOL_ICMP: + icmp = (icmp46_header_t*)l4_header; + echo = (icmp_echo_header_t *)(icmp + 1); + port = echo->identifier; + break; + case SNAT_PROTOCOL_UDP: + case SNAT_PROTOCOL_TCP: + port = ((tcp_udp_header_t*)l4_header)->src_port; + break; + default: + return vlib_get_thread_index (); + } } + } - /* Add to translated packets worker lookup */ - key0.port = udp0->dst_port; - kv0.key = key0.as_u64; - kv0.value = next_worker_index; - clib_bihash_add_del_8_8 (&sm->worker_by_out, &kv0, 1); + /* try static mappings with port */ + if (PREDICT_FALSE (pool_elts (sm->static_mappings))) + { + m_key.addr = ip0->dst_address; + m_key.port = clib_net_to_host_u16 (port); + m_key.protocol = proto; + m_key.fib_index = rx_fib_index0; + kv.key = m_key.as_u64; + if (!clib_bihash_search_8_8 (&sm->static_mapping_by_external, &kv, &value)) + { + m = pool_elt_at_index (sm->static_mappings, value.value); + return m->worker_index; + } } - else - next_worker_index = value0.value; - return next_worker_index; + /* worker by outside port */ + return (u32) ((clib_net_to_host_u16 (port) - 1024) / sm->port_per_thread); } static clib_error_t * @@ -2233,9 +2266,6 @@ snat_config (vlib_main_t * vm, unformat_input_t * input) clib_bihash_init_8_8 (&sm->worker_by_in, "worker-by-in", user_buckets, user_memory_size); - clib_bihash_init_8_8 (&sm->worker_by_out, "worker-by-out", - translation_buckets, translation_memory_size); - clib_bihash_init_16_8 (&sm->in2out_ed, "in2out-ed", translation_buckets, translation_memory_size); @@ -2618,8 +2648,6 @@ show_snat_command_fn (vlib_main_t * vm, verbose - 1); vlib_cli_output (vm, "%U", format_bihash_8_8, &sm->worker_by_in, verbose - 1); - vlib_cli_output (vm, "%U", format_bihash_8_8, &sm->worker_by_out, - verbose - 1); vec_foreach_index (j, sm->per_thread_data) { tsm = vec_elt_at_index (sm->per_thread_data, j); diff --git a/src/plugins/nat/nat.h b/src/plugins/nat/nat.h index f970821b..20e45952 100644 --- a/src/plugins/nat/nat.h +++ b/src/plugins/nat/nat.h @@ -224,6 +224,7 @@ typedef struct { u32 vrf_id; u32 fib_index; snat_protocol_t proto; + u32 worker_index; nat44_lb_addr_port_t *locals; } snat_static_mapping_t; @@ -285,9 +286,6 @@ typedef struct snat_main_s { /* Non-translated packets worker lookup => src address + VRF */ clib_bihash_8_8_t worker_by_in; - /* Translated packets worker lookup => IP address + port number */ - clib_bihash_8_8_t worker_by_out; - snat_icmp_match_function_t * icmp_match_in2out_cb; snat_icmp_match_function_t * icmp_match_out2in_cb; -- cgit 1.2.3-korg From 41fef50d5db5e7deb3cfd901c3108abbc4406813 Mon Sep 17 00:00:00 2001 From: Matus Fabian Date: Fri, 22 Sep 2017 02:43:05 -0700 Subject: NAT: session number limitation to avoid running out of memory crash (VPP-984) Change-Id: I7f18f8c4ba609d96950dc1f833feb967d4a099b7 Signed-off-by: Matus Fabian --- src/plugins/nat/in2out.c | 69 ++++++++++++++++++++++++++++++++----------- src/plugins/nat/nat.c | 2 ++ src/plugins/nat/nat.h | 10 +++++++ src/plugins/nat/out2in.c | 77 +++++++++++++++++++++++++++++++++++------------- 4 files changed, 120 insertions(+), 38 deletions(-) (limited to 'src/plugins/nat/nat.h') diff --git a/src/plugins/nat/in2out.c b/src/plugins/nat/in2out.c index dfe10303..dbbc67f9 100755 --- a/src/plugins/nat/in2out.c +++ b/src/plugins/nat/in2out.c @@ -101,7 +101,8 @@ _(IN2OUT_PACKETS, "Good in2out packets processed") \ _(OUT_OF_PORTS, "Out of ports") \ _(BAD_OUTSIDE_FIB, "Outside VRF ID not found") \ _(BAD_ICMP_TYPE, "unsupported ICMP type") \ -_(NO_TRANSLATION, "No translation") +_(NO_TRANSLATION, "No translation") \ +_(MAX_SESSIONS_EXCEEDED, "Maximum sessions exceeded") typedef enum { #define _(sym,str) SNAT_IN2OUT_ERROR_##sym, @@ -242,6 +243,12 @@ static u32 slow_path (snat_main_t *sm, vlib_buffer_t *b0, u32 outside_fib_index; uword * p; + if (PREDICT_FALSE (maximum_sessions_exceeded(sm, thread_index))) + { + b0->error = node->errors[SNAT_IN2OUT_ERROR_MAX_SESSIONS_EXCEEDED]; + return SNAT_IN2OUT_NEXT_DROP; + } + p = hash_get (sm->ip4_main->fib_index_by_table_id, sm->outside_vrf_id); if (! p) { @@ -1064,14 +1071,15 @@ snat_hairpinning_unknown_proto (snat_main_t *sm, ip->checksum = ip_csum_fold (sum); } -static void +static snat_session_t * snat_in2out_unknown_proto (snat_main_t *sm, vlib_buffer_t * b, ip4_header_t * ip, u32 rx_fib_index, u32 thread_index, f64 now, - vlib_main_t * vm) + vlib_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; @@ -1108,6 +1116,12 @@ snat_in2out_unknown_proto (snat_main_t *sm, } else { + if (PREDICT_FALSE (maximum_sessions_exceeded(sm, thread_index))) + { + b->error = node->errors[SNAT_IN2OUT_ERROR_MAX_SESSIONS_EXCEEDED]; + return 0; + } + u_key.addr = ip->src_address; u_key.fib_index = rx_fib_index; kv.key = u_key.as_u64; @@ -1198,7 +1212,7 @@ snat_in2out_unknown_proto (snat_main_t *sm, goto create_ses; } } - return; + return 0; } create_ses: @@ -1342,6 +1356,8 @@ create_ses: if (vnet_buffer(b)->sw_if_index[VLIB_TX] == ~0) vnet_buffer(b)->sw_if_index[VLIB_TX] = sm->outside_fib_index; + + return s; } static snat_session_t * @@ -1351,7 +1367,8 @@ snat_in2out_lb (snat_main_t *sm, u32 rx_fib_index, u32 thread_index, f64 now, - vlib_main_t * vm) + vlib_main_t * vm, + vlib_node_runtime_t * node) { nat_ed_ses_key_t key; clib_bihash_kv_16_8_t s_kv, s_value; @@ -1386,6 +1403,12 @@ snat_in2out_lb (snat_main_t *sm, } else { + if (PREDICT_FALSE (maximum_sessions_exceeded (sm, thread_index))) + { + b->error = node->errors[SNAT_IN2OUT_ERROR_MAX_SESSIONS_EXCEEDED]; + return 0; + } + l_key.addr = ip->src_address; l_key.port = udp->src_port; l_key.protocol = proto; @@ -1598,8 +1621,10 @@ snat_in2out_node_fn_inline (vlib_main_t * vm, { if (PREDICT_FALSE (proto0 == ~0)) { - snat_in2out_unknown_proto (sm, b0, ip0, rx_fib_index0, - thread_index, now, vm); + s0 = snat_in2out_unknown_proto (sm, b0, ip0, rx_fib_index0, + thread_index, now, vm, node); + if (!s0) + next0 = SNAT_IN2OUT_NEXT_DROP; goto trace00; } @@ -1653,8 +1678,10 @@ snat_in2out_node_fn_inline (vlib_main_t * vm, { if (is_slow_path) { - s0 = snat_in2out_lb(sm, b0, ip0, rx_fib_index0, thread_index, - now, vm); + s0 = snat_in2out_lb(sm, b0, ip0, rx_fib_index0, + thread_index, now, vm, node); + if (!s0) + next0 = SNAT_IN2OUT_NEXT_DROP; goto trace00; } else @@ -1770,8 +1797,10 @@ snat_in2out_node_fn_inline (vlib_main_t * vm, { if (PREDICT_FALSE (proto1 == ~0)) { - snat_in2out_unknown_proto (sm, b1, ip1, rx_fib_index1, - thread_index, now, vm); + s1 = snat_in2out_unknown_proto (sm, b1, ip1, rx_fib_index1, + thread_index, now, vm, node); + if (!s1) + next1 = SNAT_IN2OUT_NEXT_DROP; goto trace01; } @@ -1825,8 +1854,10 @@ snat_in2out_node_fn_inline (vlib_main_t * vm, { if (is_slow_path) { - s1 = snat_in2out_lb(sm, b1, ip1, rx_fib_index1, thread_index, - now, vm); + s1 = snat_in2out_lb(sm, b1, ip1, rx_fib_index1, + thread_index, now, vm, node); + if (!s1) + next1 = SNAT_IN2OUT_NEXT_DROP; goto trace01; } else @@ -1978,8 +2009,10 @@ snat_in2out_node_fn_inline (vlib_main_t * vm, { if (PREDICT_FALSE (proto0 == ~0)) { - snat_in2out_unknown_proto (sm, b0, ip0, rx_fib_index0, - thread_index, now, vm); + s0 = snat_in2out_unknown_proto (sm, b0, ip0, rx_fib_index0, + thread_index, now, vm, node); + if (!s0) + next0 = SNAT_IN2OUT_NEXT_DROP; goto trace0; } @@ -2034,8 +2067,10 @@ snat_in2out_node_fn_inline (vlib_main_t * vm, { if (is_slow_path) { - s0 = snat_in2out_lb(sm, b0, ip0, rx_fib_index0, thread_index, - now, vm); + s0 = snat_in2out_lb(sm, b0, ip0, rx_fib_index0, + thread_index, now, vm, node); + if (!s0) + next0 = SNAT_IN2OUT_NEXT_DROP; goto trace0; } else diff --git a/src/plugins/nat/nat.c b/src/plugins/nat/nat.c index 5f3b006e..612085fc 100644 --- a/src/plugins/nat/nat.c +++ b/src/plugins/nat/nat.c @@ -2216,6 +2216,8 @@ snat_config (vlib_main_t * vm, unformat_input_t * input) /* for show commands, etc. */ sm->translation_buckets = translation_buckets; sm->translation_memory_size = translation_memory_size; + /* do not exceed load factor 10 */ + sm->max_translations = 10 * translation_buckets; sm->user_buckets = user_buckets; sm->user_memory_size = user_memory_size; sm->max_translations_per_user = max_translations_per_user; diff --git a/src/plugins/nat/nat.h b/src/plugins/nat/nat.h index 20e45952..d34ff07b 100644 --- a/src/plugins/nat/nat.h +++ b/src/plugins/nat/nat.h @@ -345,6 +345,7 @@ typedef struct snat_main_s { u8 deterministic; u32 translation_buckets; u32 translation_memory_size; + u32 max_translations; u32 user_buckets; u32 user_memory_size; u32 max_translations_per_user; @@ -551,4 +552,13 @@ is_interface_addr(snat_main_t *sm, vlib_node_runtime_t *node, u32 sw_if_index0, return 0; } +always_inline u8 +maximum_sessions_exceeded (snat_main_t *sm, u32 thread_index) +{ + if (pool_elts (sm->per_thread_data[thread_index].sessions) >= sm->max_translations) + return 1; + + return 0; +} + #endif /* __included_nat_h__ */ diff --git a/src/plugins/nat/out2in.c b/src/plugins/nat/out2in.c index 6472e7ff..e5426c1a 100755 --- a/src/plugins/nat/out2in.c +++ b/src/plugins/nat/out2in.c @@ -87,7 +87,8 @@ vlib_node_registration_t snat_det_out2in_node; _(UNSUPPORTED_PROTOCOL, "Unsupported protocol") \ _(OUT2IN_PACKETS, "Good out2in packets processed") \ _(BAD_ICMP_TYPE, "unsupported ICMP type") \ -_(NO_TRANSLATION, "No translation") +_(NO_TRANSLATION, "No translation") \ +_(MAX_SESSIONS_EXCEEDED, "Maximum sessions exceeded") typedef enum { #define _(sym,str) SNAT_OUT2IN_ERROR_##sym, @@ -139,6 +140,12 @@ create_session_for_static_mapping (snat_main_t *sm, dlist_elt_t * per_user_list_head_elt; ip4_header_t *ip0; + if (PREDICT_FALSE (maximum_sessions_exceeded(sm, thread_index))) + { + b0->error = node->errors[SNAT_OUT2IN_ERROR_MAX_SESSIONS_EXCEEDED]; + return 0; + } + ip0 = vlib_buffer_get_current (b0); user_key.addr = in2out.addr; @@ -620,14 +627,15 @@ static inline u32 icmp_out2in_slow_path (snat_main_t *sm, return next0; } -static void +static snat_session_t * snat_out2in_unknown_proto (snat_main_t *sm, vlib_buffer_t * b, ip4_header_t * ip, u32 rx_fib_index, u32 thread_index, f64 now, - vlib_main_t * vm) + vlib_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; @@ -660,13 +668,22 @@ snat_out2in_unknown_proto (snat_main_t *sm, } else { + if (PREDICT_FALSE (maximum_sessions_exceeded(sm, thread_index))) + { + b->error = node->errors[SNAT_OUT2IN_ERROR_MAX_SESSIONS_EXCEEDED]; + return 0; + } + m_key.addr = ip->dst_address; m_key.port = 0; m_key.protocol = 0; m_key.fib_index = rx_fib_index; kv.key = m_key.as_u64; if (clib_bihash_search_8_8 (&sm->static_mapping_by_external, &kv, &value)) - return; + { + b->error = node->errors[SNAT_OUT2IN_ERROR_NO_TRANSLATION]; + return 0; + } m = pool_elt_at_index (sm->static_mappings, value.value); @@ -753,6 +770,8 @@ snat_out2in_unknown_proto (snat_main_t *sm, clib_dlist_remove (tsm->list_pool, s->per_user_index); clib_dlist_addtail (tsm->list_pool, s->per_user_list_head_index, s->per_user_index); + + return s; } static snat_session_t * @@ -762,7 +781,8 @@ snat_out2in_lb (snat_main_t *sm, u32 rx_fib_index, u32 thread_index, f64 now, - vlib_main_t * vm) + vlib_main_t * vm, + vlib_node_runtime_t * node) { nat_ed_ses_key_t key; clib_bihash_kv_16_8_t s_kv, s_value; @@ -797,6 +817,12 @@ snat_out2in_lb (snat_main_t *sm, } else { + if (PREDICT_FALSE (maximum_sessions_exceeded(sm, thread_index))) + { + b->error = node->errors[SNAT_OUT2IN_ERROR_MAX_SESSIONS_EXCEEDED]; + return 0; + } + e_key.addr = ip->dst_address; e_key.port = udp->dst_port; e_key.protocol = proto; @@ -998,8 +1024,10 @@ snat_out2in_node_fn (vlib_main_t * vm, if (PREDICT_FALSE (proto0 == ~0)) { - snat_out2in_unknown_proto(sm, b0, ip0, rx_fib_index0, - thread_index, now, vm); + s0 = snat_out2in_unknown_proto(sm, b0, ip0, rx_fib_index0, + thread_index, now, vm, node); + if (!s0) + next0 = SNAT_OUT2IN_NEXT_DROP; goto trace0; } @@ -1042,7 +1070,6 @@ snat_out2in_node_fn (vlib_main_t * vm, thread_index); if (!s0) { - b0->error = node->errors[SNAT_OUT2IN_ERROR_NO_TRANSLATION]; next0 = SNAT_OUT2IN_NEXT_DROP; goto trace0; } @@ -1051,8 +1078,10 @@ snat_out2in_node_fn (vlib_main_t * vm, { if (PREDICT_FALSE (value0.value == ~0ULL)) { - s0 = snat_out2in_lb(sm, b0, ip0, rx_fib_index0, thread_index, now, - vm); + s0 = snat_out2in_lb(sm, b0, ip0, rx_fib_index0, thread_index, + now, vm, node); + if (!s0) + next0 = SNAT_OUT2IN_NEXT_DROP; goto trace0; } else @@ -1150,8 +1179,10 @@ snat_out2in_node_fn (vlib_main_t * vm, if (PREDICT_FALSE (proto1 == ~0)) { - snat_out2in_unknown_proto(sm, b1, ip1, rx_fib_index1, - thread_index, now, vm); + s1 = snat_out2in_unknown_proto(sm, b1, ip1, rx_fib_index1, + thread_index, now, vm, node); + if (!s1) + next1 = SNAT_OUT2IN_NEXT_DROP; goto trace1; } @@ -1194,7 +1225,6 @@ snat_out2in_node_fn (vlib_main_t * vm, thread_index); if (!s1) { - b1->error = node->errors[SNAT_OUT2IN_ERROR_NO_TRANSLATION]; next1 = SNAT_OUT2IN_NEXT_DROP; goto trace1; } @@ -1203,8 +1233,10 @@ snat_out2in_node_fn (vlib_main_t * vm, { if (PREDICT_FALSE (value1.value == ~0ULL)) { - s1 = snat_out2in_lb(sm, b1, ip1, rx_fib_index1, thread_index, now, - vm); + s1 = snat_out2in_lb(sm, b1, ip1, rx_fib_index1, thread_index, + now, vm, node); + if (!s1) + next1 = SNAT_OUT2IN_NEXT_DROP; goto trace1; } else @@ -1328,8 +1360,10 @@ snat_out2in_node_fn (vlib_main_t * vm, if (PREDICT_FALSE (proto0 == ~0)) { - snat_out2in_unknown_proto(sm, b0, ip0, rx_fib_index0, - thread_index, now, vm); + s0 = snat_out2in_unknown_proto(sm, b0, ip0, rx_fib_index0, + thread_index, now, vm, node); + if (!s0) + next0 = SNAT_OUT2IN_NEXT_DROP; goto trace00; } @@ -1383,8 +1417,7 @@ snat_out2in_node_fn (vlib_main_t * vm, thread_index); if (!s0) { - b0->error = node->errors[SNAT_OUT2IN_ERROR_NO_TRANSLATION]; - next0 = SNAT_OUT2IN_NEXT_DROP; + next0 = SNAT_OUT2IN_NEXT_DROP; goto trace00; } } @@ -1392,8 +1425,10 @@ snat_out2in_node_fn (vlib_main_t * vm, { if (PREDICT_FALSE (value0.value == ~0ULL)) { - s0 = snat_out2in_lb(sm, b0, ip0, rx_fib_index0, thread_index, now, - vm); + s0 = snat_out2in_lb(sm, b0, ip0, rx_fib_index0, thread_index, + now, vm, node); + if (!s0) + next0 = SNAT_OUT2IN_NEXT_DROP; goto trace00; } else -- cgit 1.2.3-korg From 7865b5c8a4f11731d8c6d4627e505afe990989b4 Mon Sep 17 00:00:00 2001 From: Matus Fabian Date: Tue, 26 Sep 2017 01:23:01 -0700 Subject: NAT: remove worker_by_in lookup hash table (VPP-992) Change-Id: I3873d3e411bf93cac82e73a0b8e3b22563aaf217 Signed-off-by: Matus Fabian --- src/plugins/nat/nat.c | 91 ++++++++++------------------------------------- src/plugins/nat/nat.h | 16 --------- src/plugins/nat/nat_api.c | 16 ++++++--- src/plugins/nat/out2in.c | 4 --- 4 files changed, 30 insertions(+), 97 deletions(-) (limited to 'src/plugins/nat/nat.h') diff --git a/src/plugins/nat/nat.c b/src/plugins/nat/nat.c index 94416255..c2f9586c 100644 --- a/src/plugins/nat/nat.c +++ b/src/plugins/nat/nat.c @@ -390,28 +390,12 @@ int snat_add_static_mapping(ip4_address_t l_addr, ip4_address_t e_addr, kv.value = m - sm->static_mappings; clib_bihash_add_del_8_8(&sm->static_mapping_by_external, &kv, 1); - /* Assign worker */ if (sm->workers) { - snat_user_key_t w_key0; - - w_key0.addr = m->local_addr; - w_key0.fib_index = m->fib_index; - kv.key = w_key0.as_u64; - - if (clib_bihash_search_8_8 (&sm->worker_by_in, &kv, &value)) - { - kv.value = sm->first_worker_index + - sm->workers[sm->next_worker++ % vec_len (sm->workers)]; - - clib_bihash_add_del_8_8 (&sm->worker_by_in, &kv, 1); - } - else - { - kv.value = value.value; - } - - m->worker_index = kv.value; + ip4_header_t ip = { + .src_address = m->local_addr, + }; + m->worker_index = sm->worker_in2out_cb (&ip, m->fib_index); } } else @@ -478,8 +462,8 @@ int snat_add_static_mapping(ip4_address_t l_addr, ip4_address_t e_addr, u_key.addr = m->local_addr; u_key.fib_index = m->fib_index; kv.key = u_key.as_u64; - if (!clib_bihash_search_8_8 (&sm->worker_by_in, &kv, &value)) - tsm = vec_elt_at_index (sm->per_thread_data, value.value); + if (sm->num_workers) + tsm = vec_elt_at_index (sm->per_thread_data, m->worker_index); else tsm = vec_elt_at_index (sm->per_thread_data, sm->num_workers); if (!clib_bihash_search_8_8 (&tsm->user_hash, &kv, &value)) @@ -607,7 +591,6 @@ int nat44_add_del_lb_static_mapping (ip4_address_t e_addr, u16 e_port, snat_address_t *a = 0; int i; nat44_lb_addr_port_t *local; - snat_user_key_t w_key0; u32 worker_index = 0; snat_main_per_thread_data_t *tsm; @@ -694,16 +677,8 @@ int nat44_add_del_lb_static_mapping (ip4_address_t e_addr, u16 e_port, /* Assign worker */ if (sm->workers) { - w_key0.addr = locals[0].addr; - w_key0.fib_index = fib_index; - kv.key = w_key0.as_u64; - - if (clib_bihash_search_8_8 (&sm->worker_by_in, &kv, &value)) - worker_index = sm->first_worker_index + - sm->workers[sm->next_worker++ % vec_len (sm->workers)]; - else - worker_index = value.value; - + worker_index = sm->first_worker_index + + sm->workers[sm->next_worker++ % vec_len (sm->workers)]; tsm = vec_elt_at_index (sm->per_thread_data, worker_index); m->worker_index = worker_index; } @@ -730,6 +705,7 @@ int nat44_add_del_lb_static_mapping (ip4_address_t e_addr, u16 e_port, locals[i].prefix = (i == 0) ? locals[i].probability :\ (locals[i - 1].prefix + locals[i].probability); vec_add1 (m->locals, locals[i]); + m_key.port = clib_host_to_net_u16 (locals[i].port); kv.key = m_key.as_u64; kv.value = ~0ULL; @@ -738,19 +714,6 @@ int nat44_add_del_lb_static_mapping (ip4_address_t e_addr, u16 e_port, clib_warning ("in2out key add failed"); return VNET_API_ERROR_UNSPECIFIED; } - /* Assign worker */ - if (sm->workers) - { - w_key0.addr = locals[i].addr; - w_key0.fib_index = fib_index; - kv.key = w_key0.as_u64; - kv.value = worker_index; - if (clib_bihash_add_del_8_8 (&sm->worker_by_in, &kv, 1)) - { - clib_warning ("worker-by-in key add failed"); - return VNET_API_ERROR_UNSPECIFIED; - } - } } } else @@ -801,6 +764,7 @@ int nat44_add_del_lb_static_mapping (ip4_address_t e_addr, u16 e_port, clib_warning ("static_mapping_by_external key del failed"); return VNET_API_ERROR_UNSPECIFIED; } + m_key.port = clib_host_to_net_u16 (m->external_port); kv.key = m_key.as_u64; if (clib_bihash_add_del_8_8(&tsm->out2in, &kv, 0)) @@ -820,6 +784,7 @@ int nat44_add_del_lb_static_mapping (ip4_address_t e_addr, u16 e_port, clib_warning ("static_mapping_by_local key del failed"); return VNET_API_ERROR_UNSPECIFIED; } + m_key.port = clib_host_to_net_u16 (local->port); kv.key = m_key.as_u64; if (clib_bihash_add_del_8_8(&tsm->in2out, &kv, 0)) @@ -2017,32 +1982,17 @@ static u32 snat_get_worker_in2out_cb (ip4_header_t * ip0, u32 rx_fib_index0) { snat_main_t *sm = &snat_main; - snat_user_key_t key0; - clib_bihash_kv_8_8_t kv0, value0; u32 next_worker_index = 0; + u32 hash; - key0.addr = ip0->src_address; - key0.fib_index = rx_fib_index0; - - kv0.key = key0.as_u64; + next_worker_index = sm->first_worker_index; + hash = ip0->src_address.as_u32 + (ip0->src_address.as_u32 >> 8) + + (ip0->src_address.as_u32 >> 16) + (ip0->src_address.as_u32 >>24); - /* Ever heard of of the "user" before? */ - if (clib_bihash_search_8_8 (&sm->worker_by_in, &kv0, &value0)) - { - /* No, assign next available worker (RR) */ - next_worker_index = sm->first_worker_index; - if (vec_len (sm->workers)) - { - next_worker_index += - sm->workers[sm->next_worker++ % _vec_len (sm->workers)]; - } - - /* add non-traslated packets worker lookup */ - kv0.value = next_worker_index; - clib_bihash_add_del_8_8 (&sm->worker_by_in, &kv0, 1); - } + if (PREDICT_TRUE (is_pow2 (_vec_len (sm->workers)))) + next_worker_index += sm->workers[hash & (_vec_len (sm->workers) - 1)]; else - next_worker_index = value0.value; + next_worker_index += sm->workers[hash % _vec_len (sm->workers)]; return next_worker_index; } @@ -2265,9 +2215,6 @@ snat_config (vlib_main_t * vm, unformat_input_t * input) user_memory_size); } - clib_bihash_init_8_8 (&sm->worker_by_in, "worker-by-in", user_buckets, - user_memory_size); - clib_bihash_init_16_8 (&sm->in2out_ed, "in2out-ed", translation_buckets, translation_memory_size); @@ -2648,8 +2595,6 @@ show_snat_command_fn (vlib_main_t * vm, verbose - 1); vlib_cli_output (vm, "%U", format_bihash_16_8, &sm->out2in_ed, verbose - 1); - vlib_cli_output (vm, "%U", format_bihash_8_8, &sm->worker_by_in, - verbose - 1); vec_foreach_index (j, sm->per_thread_data) { tsm = vec_elt_at_index (sm->per_thread_data, j); diff --git a/src/plugins/nat/nat.h b/src/plugins/nat/nat.h index d34ff07b..e467fde7 100644 --- a/src/plugins/nat/nat.h +++ b/src/plugins/nat/nat.h @@ -95,19 +95,6 @@ typedef struct { }; } snat_user_key_t; -typedef struct { - union - { - struct - { - ip4_address_t addr; - u16 port; - u16 fib_index; - }; - u64 as_u64; - }; -} snat_worker_key_t; - #define foreach_snat_protocol \ _(UDP, 0, udp, "udp") \ @@ -283,9 +270,6 @@ typedef struct snat_main_s { clib_bihash_16_8_t out2in_ed; clib_bihash_16_8_t in2out_ed; - /* Non-translated packets worker lookup => src address + VRF */ - clib_bihash_8_8_t worker_by_in; - snat_icmp_match_function_t * icmp_match_in2out_cb; snat_icmp_match_function_t * icmp_match_out2in_cb; diff --git a/src/plugins/nat/nat_api.c b/src/plugins/nat/nat_api.c index 50b4a9ae..b56b4436 100644 --- a/src/plugins/nat/nat_api.c +++ b/src/plugins/nat/nat_api.c @@ -856,6 +856,7 @@ static void snat_user_t *u; u32 session_index, head_index, elt_index; dlist_elt_t *head, *elt; + ip4_header_t ip; q = vl_api_client_index_to_input_queue (mp->client_index); if (q == 0) @@ -864,10 +865,13 @@ static void return; clib_memcpy (&ukey.addr, mp->ip_address, 4); + ip.src_address.as_u32 = ukey.addr.as_u32; ukey.fib_index = fib_table_find (FIB_PROTOCOL_IP4, ntohl (mp->vrf_id)); key.key = ukey.as_u64; - if (!clib_bihash_search_8_8 (&sm->worker_by_in, &key, &value)) - tsm = vec_elt_at_index (sm->per_thread_data, value.value); + if (sm->num_workers) + tsm = + vec_elt_at_index (sm->per_thread_data, + sm->worker_in2out_cb (&ip, ukey.fib_index)); else tsm = vec_elt_at_index (sm->per_thread_data, sm->num_workers); if (clib_bihash_search_8_8 (&tsm->user_hash, &key, &value)) @@ -2093,16 +2097,20 @@ vl_api_nat44_user_session_dump_t_handler (vl_api_nat44_user_session_dump_t * snat_user_t *u; u32 session_index, head_index, elt_index; dlist_elt_t *head, *elt; + ip4_header_t ip; q = vl_api_client_index_to_input_queue (mp->client_index); if (q == 0) return; clib_memcpy (&ukey.addr, mp->ip_address, 4); + ip.src_address.as_u32 = ukey.addr.as_u32; ukey.fib_index = fib_table_find (FIB_PROTOCOL_IP4, ntohl (mp->vrf_id)); key.key = ukey.as_u64; - if (!clib_bihash_search_8_8 (&sm->worker_by_in, &key, &value)) - tsm = vec_elt_at_index (sm->per_thread_data, value.value); + if (sm->num_workers) + tsm = + vec_elt_at_index (sm->per_thread_data, + sm->worker_in2out_cb (&ip, ukey.fib_index)); else tsm = vec_elt_at_index (sm->per_thread_data, sm->num_workers); if (clib_bihash_search_8_8 (&tsm->user_hash, &key, &value)) diff --git a/src/plugins/nat/out2in.c b/src/plugins/nat/out2in.c index e5426c1a..802c3312 100755 --- a/src/plugins/nat/out2in.c +++ b/src/plugins/nat/out2in.c @@ -176,10 +176,6 @@ create_session_for_static_mapping (snat_main_t *sm, /* add user */ clib_bihash_add_del_8_8 (&sm->per_thread_data[thread_index].user_hash, &kv0, 1 /* is_add */); - - /* add non-traslated packets worker lookup */ - kv0.value = thread_index; - clib_bihash_add_del_8_8 (&sm->worker_by_in, &kv0, 1); } else { -- cgit 1.2.3-korg From dea5881815426442f8b3137c5971ed1764bdd278 Mon Sep 17 00:00:00 2001 From: Matus Fabian Date: Wed, 4 Oct 2017 23:57:58 -0700 Subject: NAT: fixed ICMP broken translation for GRE tunnel interface (VPP-1008) Change-Id: Ie3245b96c511cc30915e70e8c881f445291a38c2 Signed-off-by: Matus Fabian --- src/plugins/nat/in2out.c | 22 +++++++--------------- src/plugins/nat/nat.h | 19 +++++++++++++------ src/plugins/nat/out2in.c | 17 +++++++---------- 3 files changed, 27 insertions(+), 31 deletions(-) (limited to 'src/plugins/nat/nat.h') diff --git a/src/plugins/nat/in2out.c b/src/plugins/nat/in2out.c index dbbc67f9..8e583313 100755 --- a/src/plugins/nat/in2out.c +++ b/src/plugins/nat/in2out.c @@ -523,11 +523,11 @@ snat_in2out_error_t icmp_get_key(ip4_header_t *ip0, * @param e optional parameter */ u32 icmp_match_in2out_slow(snat_main_t *sm, vlib_node_runtime_t *node, - u32 thread_index, vlib_buffer_t *b0, u8 *p_proto, + u32 thread_index, vlib_buffer_t *b0, + ip4_header_t *ip0, u8 *p_proto, snat_session_key_t *p_value, u8 *p_dont_translate, void *d, void *e) { - ip4_header_t *ip0; icmp46_header_t *icmp0; u32 sw_if_index0; u32 rx_fib_index0; @@ -537,13 +537,7 @@ u32 icmp_match_in2out_slow(snat_main_t *sm, vlib_node_runtime_t *node, clib_bihash_kv_8_8_t kv0, value0; u32 next0 = ~0; int err; - u32 iph_offset0 = 0; - if (PREDICT_FALSE(vnet_buffer(b0)->sw_if_index[VLIB_TX] != ~0)) - { - iph_offset0 = vnet_buffer (b0)->ip.save_rewrite_length; - } - ip0 = (ip4_header_t *) ((u8 *) vlib_buffer_get_current (b0) + iph_offset0); icmp0 = (icmp46_header_t *) ip4_next_header (ip0); sw_if_index0 = vnet_buffer(b0)->sw_if_index[VLIB_RX]; rx_fib_index0 = ip4_fib_table_get_index_for_sw_if_index (sw_if_index0); @@ -622,11 +616,11 @@ out: * @param e optional parameter */ u32 icmp_match_in2out_fast(snat_main_t *sm, vlib_node_runtime_t *node, - u32 thread_index, vlib_buffer_t *b0, u8 *p_proto, + u32 thread_index, vlib_buffer_t *b0, + ip4_header_t *ip0, u8 *p_proto, snat_session_key_t *p_value, u8 *p_dont_translate, void *d, void *e) { - ip4_header_t *ip0; icmp46_header_t *icmp0; u32 sw_if_index0; u32 rx_fib_index0; @@ -637,7 +631,6 @@ u32 icmp_match_in2out_fast(snat_main_t *sm, vlib_node_runtime_t *node, u32 next0 = ~0; int err; - ip0 = vlib_buffer_get_current (b0); icmp0 = (icmp46_header_t *) ip4_next_header (ip0); sw_if_index0 = vnet_buffer(b0)->sw_if_index[VLIB_RX]; rx_fib_index0 = ip4_fib_table_get_index_for_sw_if_index (sw_if_index0); @@ -715,7 +708,7 @@ static inline u32 icmp_in2out (snat_main_t *sm, echo0 = (icmp_echo_header_t *)(icmp0+1); - next0_tmp = sm->icmp_match_in2out_cb(sm, node, thread_index, b0, + next0_tmp = sm->icmp_match_in2out_cb(sm, node, thread_index, b0, ip0, &protocol, &sm0, &dont_translate, d, e); if (next0_tmp != ~0) next0 = next0_tmp; @@ -2919,11 +2912,11 @@ VLIB_NODE_FUNCTION_MULTIARCH (snat_det_in2out_node, snat_det_in2out_node_fn); * @param e optional parameter */ u32 icmp_match_in2out_det(snat_main_t *sm, vlib_node_runtime_t *node, - u32 thread_index, vlib_buffer_t *b0, u8 *p_proto, + u32 thread_index, vlib_buffer_t *b0, + ip4_header_t *ip0, u8 *p_proto, snat_session_key_t *p_value, u8 *p_dont_translate, void *d, void *e) { - ip4_header_t *ip0; icmp46_header_t *icmp0; u32 sw_if_index0; u32 rx_fib_index0; @@ -2942,7 +2935,6 @@ u32 icmp_match_in2out_det(snat_main_t *sm, vlib_node_runtime_t *node, ip4_address_t in_addr; u16 in_port; - ip0 = vlib_buffer_get_current (b0); icmp0 = (icmp46_header_t *) ip4_next_header (ip0); echo0 = (icmp_echo_header_t *)(icmp0+1); sw_if_index0 = vnet_buffer(b0)->sw_if_index[VLIB_RX]; diff --git a/src/plugins/nat/nat.h b/src/plugins/nat/nat.h index e467fde7..e53e924f 100644 --- a/src/plugins/nat/nat.h +++ b/src/plugins/nat/nat.h @@ -257,6 +257,7 @@ typedef u32 snat_icmp_match_function_t (struct snat_main_s *sm, vlib_node_runtime_t *node, u32 thread_index, vlib_buffer_t *b0, + ip4_header_t *ip0, u8 *p_proto, snat_session_key_t *p_value, u8 *p_dont_translate, @@ -454,27 +455,33 @@ typedef struct { } tcp_udp_header_t; u32 icmp_match_in2out_fast(snat_main_t *sm, vlib_node_runtime_t *node, - u32 thread_index, vlib_buffer_t *b0, u8 *p_proto, + u32 thread_index, vlib_buffer_t *b0, + ip4_header_t *ip0, u8 *p_proto, snat_session_key_t *p_value, u8 *p_dont_translate, void *d, void *e); u32 icmp_match_in2out_slow(snat_main_t *sm, vlib_node_runtime_t *node, - u32 thread_index, vlib_buffer_t *b0, u8 *p_proto, + u32 thread_index, vlib_buffer_t *b0, + ip4_header_t *ip0, u8 *p_proto, snat_session_key_t *p_value, u8 *p_dont_translate, void *d, void *e); u32 icmp_match_in2out_det(snat_main_t *sm, vlib_node_runtime_t *node, - u32 thread_index, vlib_buffer_t *b0, u8 *p_proto, + u32 thread_index, vlib_buffer_t *b0, + ip4_header_t *ip0, u8 *p_proto, snat_session_key_t *p_value, u8 *p_dont_translate, void *d, void *e); u32 icmp_match_out2in_fast(snat_main_t *sm, vlib_node_runtime_t *node, - u32 thread_index, vlib_buffer_t *b0, u8 *p_proto, + u32 thread_index, vlib_buffer_t *b0, + ip4_header_t *ip0, u8 *p_proto, snat_session_key_t *p_value, u8 *p_dont_translate, void *d, void *e); u32 icmp_match_out2in_slow(snat_main_t *sm, vlib_node_runtime_t *node, - u32 thread_index, vlib_buffer_t *b0, u8 *p_proto, + u32 thread_index, vlib_buffer_t *b0, + ip4_header_t *ip0, u8 *p_proto, snat_session_key_t *p_value, u8 *p_dont_translate, void *d, void *e); u32 icmp_match_out2in_det(snat_main_t *sm, vlib_node_runtime_t *node, - u32 thread_index, vlib_buffer_t *b0, u8 *p_proto, + u32 thread_index, vlib_buffer_t *b0, + ip4_header_t *ip0, u8 *p_proto, snat_session_key_t *p_value, u8 *p_dont_translate, void *d, void *e); void increment_v4_address(ip4_address_t * a); diff --git a/src/plugins/nat/out2in.c b/src/plugins/nat/out2in.c index 802c3312..f250136b 100755 --- a/src/plugins/nat/out2in.c +++ b/src/plugins/nat/out2in.c @@ -297,11 +297,11 @@ snat_out2in_error_t icmp_get_key(ip4_header_t *ip0, * @param e optional parameter */ u32 icmp_match_out2in_slow(snat_main_t *sm, vlib_node_runtime_t *node, - u32 thread_index, vlib_buffer_t *b0, u8 *p_proto, + u32 thread_index, vlib_buffer_t *b0, + ip4_header_t *ip0, u8 *p_proto, snat_session_key_t *p_value, u8 *p_dont_translate, void *d, void *e) { - ip4_header_t *ip0; icmp46_header_t *icmp0; u32 sw_if_index0; u32 rx_fib_index0; @@ -314,7 +314,6 @@ u32 icmp_match_out2in_slow(snat_main_t *sm, vlib_node_runtime_t *node, u32 next0 = ~0; int err; - ip0 = vlib_buffer_get_current (b0); icmp0 = (icmp46_header_t *) ip4_next_header (ip0); sw_if_index0 = vnet_buffer(b0)->sw_if_index[VLIB_RX]; rx_fib_index0 = ip4_fib_table_get_index_for_sw_if_index (sw_if_index0); @@ -408,11 +407,11 @@ out: * @param e optional parameter */ u32 icmp_match_out2in_fast(snat_main_t *sm, vlib_node_runtime_t *node, - u32 thread_index, vlib_buffer_t *b0, u8 *p_proto, + u32 thread_index, vlib_buffer_t *b0, + ip4_header_t *ip0, u8 *p_proto, snat_session_key_t *p_value, u8 *p_dont_translate, void *d, void *e) { - ip4_header_t *ip0; icmp46_header_t *icmp0; u32 sw_if_index0; u32 rx_fib_index0; @@ -423,7 +422,6 @@ u32 icmp_match_out2in_fast(snat_main_t *sm, vlib_node_runtime_t *node, u32 next0 = ~0; int err; - ip0 = vlib_buffer_get_current (b0); icmp0 = (icmp46_header_t *) ip4_next_header (ip0); sw_if_index0 = vnet_buffer(b0)->sw_if_index[VLIB_RX]; rx_fib_index0 = ip4_fib_table_get_index_for_sw_if_index (sw_if_index0); @@ -494,7 +492,7 @@ static inline u32 icmp_out2in (snat_main_t *sm, echo0 = (icmp_echo_header_t *)(icmp0+1); - next0_tmp = sm->icmp_match_out2in_cb(sm, node, thread_index, b0, + next0_tmp = sm->icmp_match_out2in_cb(sm, node, thread_index, b0, ip0, &protocol, &sm0, &dont_translate, d, e); if (next0_tmp != ~0) next0 = next0_tmp; @@ -2038,11 +2036,11 @@ VLIB_NODE_FUNCTION_MULTIARCH (snat_det_out2in_node, snat_det_out2in_node_fn); * @param e optional parameter */ u32 icmp_match_out2in_det(snat_main_t *sm, vlib_node_runtime_t *node, - u32 thread_index, vlib_buffer_t *b0, u8 *p_proto, + u32 thread_index, vlib_buffer_t *b0, + ip4_header_t *ip0, u8 *p_proto, snat_session_key_t *p_value, u8 *p_dont_translate, void *d, void *e) { - ip4_header_t *ip0; icmp46_header_t *icmp0; u32 sw_if_index0; u8 protocol; @@ -2058,7 +2056,6 @@ u32 icmp_match_out2in_det(snat_main_t *sm, vlib_node_runtime_t *node, snat_det_session_t * ses0 = 0; ip4_address_t out_addr; - ip0 = vlib_buffer_get_current (b0); icmp0 = (icmp46_header_t *) ip4_next_header (ip0); echo0 = (icmp_echo_header_t *)(icmp0+1); sw_if_index0 = vnet_buffer(b0)->sw_if_index[VLIB_RX]; -- cgit 1.2.3-korg