From a6110b6ea5a066b64005347850f61df9a2000fe9 Mon Sep 17 00:00:00 2001 From: Matus Fabian Date: Wed, 13 Jun 2018 05:39:07 -0700 Subject: [PATCH] NAT44: endpoint dependent mode (VPP-1273) To enable NAT plugin endpoint dependent mode add following to statrup config: nat { endpoint-dependent } Enable endpoint dependent filtering and mapping for all sessions. Move some existing functionality such as service load balancing, twice nat, out2in-only static mappings and unknown protocol dynamic translations, which use endpoint dependent lookup hash tables before. Basically split to vanilla NAT44 and extra features NAT44. Change-Id: I3925eb5ddcc8f1ec4cf6af4e2a618a7ec7aa9735 Signed-off-by: Matus Fabian --- src/plugins/nat/in2out.c | 2509 +++++++++++++++++++++---------- src/plugins/nat/nat.c | 599 +++++--- src/plugins/nat/nat.h | 34 +- src/plugins/nat/nat44_cli.c | 39 +- src/plugins/nat/nat_api.c | 2 +- src/plugins/nat/nat_inlines.h | 32 + src/plugins/nat/out2in.c | 1936 +++++++++++++++++------- src/scripts/vnet/nat44_lb | 48 + src/scripts/vnet/nat44_static_with_port | 2 +- test/test_nat.py | 2277 +++++++++++++++------------- 10 files changed, 4846 insertions(+), 2632 deletions(-) create mode 100644 src/scripts/vnet/nat44_lb diff --git a/src/plugins/nat/in2out.c b/src/plugins/nat/in2out.c index c724553e36b..7c2977cb8da 100755 --- a/src/plugins/nat/in2out.c +++ b/src/plugins/nat/in2out.c @@ -116,7 +116,13 @@ vlib_node_registration_t snat_hairpin_dst_node; vlib_node_registration_t snat_hairpin_src_node; vlib_node_registration_t nat44_hairpinning_node; vlib_node_registration_t nat44_in2out_reass_node; - +vlib_node_registration_t nat44_ed_in2out_node; +vlib_node_registration_t nat44_ed_in2out_slowpath_node; +vlib_node_registration_t nat44_ed_in2out_output_node; +vlib_node_registration_t nat44_ed_in2out_output_slowpath_node; +vlib_node_registration_t nat44_ed_hairpin_dst_node; +vlib_node_registration_t nat44_ed_hairpin_src_node; +vlib_node_registration_t nat44_ed_hairpinning_node; #define foreach_snat_in2out_error \ _(UNSUPPORTED_PROTOCOL, "Unsupported protocol") \ @@ -445,115 +451,6 @@ snat_in2out_error_t icmp_get_key(ip4_header_t *ip0, return -1; /* success */ } -static_always_inline int -icmp_get_ed_key(ip4_header_t *ip0, nat_ed_ses_key_t *p_key0) -{ - icmp46_header_t *icmp0; - nat_ed_ses_key_t key0; - icmp_echo_header_t *echo0, *inner_echo0 = 0; - ip4_header_t *inner_ip0 = 0; - void *l4_header = 0; - icmp46_header_t *inner_icmp0; - - icmp0 = (icmp46_header_t *) ip4_next_header (ip0); - echo0 = (icmp_echo_header_t *)(icmp0+1); - - if (!icmp_is_error_message (icmp0)) - { - key0.proto = IP_PROTOCOL_ICMP; - key0.l_addr = ip0->src_address; - key0.r_addr = ip0->dst_address; - key0.l_port = key0.r_port = echo0->identifier; - } - else - { - inner_ip0 = (ip4_header_t *)(echo0+1); - l4_header = ip4_next_header (inner_ip0); - key0.proto = inner_ip0->protocol; - key0.r_addr = inner_ip0->src_address; - key0.l_addr = inner_ip0->dst_address; - switch (ip_proto_to_snat_proto (inner_ip0->protocol)) - { - case SNAT_PROTOCOL_ICMP: - inner_icmp0 = (icmp46_header_t*)l4_header; - inner_echo0 = (icmp_echo_header_t *)(inner_icmp0+1); - key0.r_port = key0.l_port = inner_echo0->identifier; - break; - case SNAT_PROTOCOL_UDP: - case SNAT_PROTOCOL_TCP: - key0.l_port = ((tcp_udp_header_t*)l4_header)->dst_port; - key0.r_port = ((tcp_udp_header_t*)l4_header)->src_port; - break; - default: - return SNAT_IN2OUT_ERROR_UNSUPPORTED_PROTOCOL; - } - } - *p_key0 = key0; - return 0; -} - -static inline int -nat_not_translate_output_feature_fwd (snat_main_t * sm, ip4_header_t * ip, - u32 thread_index) -{ - nat_ed_ses_key_t key; - clib_bihash_kv_16_8_t kv, value; - udp_header_t *udp; - snat_session_t *s = 0; - f64 now = vlib_time_now (sm->vlib_main); - - if (!sm->forwarding_enabled) - return 0; - - if (ip->protocol == IP_PROTOCOL_ICMP) - { - if (icmp_get_ed_key (ip, &key)) - return 0; - } - else if (ip->protocol == IP_PROTOCOL_UDP || ip->protocol == IP_PROTOCOL_TCP) - { - udp = ip4_next_header(ip); - key.l_addr = ip->src_address; - key.r_addr = ip->dst_address; - key.proto = ip->protocol; - key.r_port = udp->dst_port; - key.l_port = udp->src_port; - } - else - { - key.l_addr = ip->src_address; - key.r_addr = ip->dst_address; - key.proto = ip->protocol; - key.l_port = key.r_port = 0; - } - key.fib_index = 0; - kv.key[0] = key.as_u64[0]; - kv.key[1] = key.as_u64[1]; - - if (!clib_bihash_search_16_8 (&sm->in2out_ed, &kv, &value)) - { - s = pool_elt_at_index (sm->per_thread_data[thread_index].sessions, value.value); - if (is_fwd_bypass_session (s)) - { - if (ip->protocol == IP_PROTOCOL_TCP) - { - tcp_header_t *tcp = ip4_next_header(ip); - if (nat44_set_tcp_session_state_i2o (sm, s, tcp, thread_index)) - return 1; - } - /* Per-user LRU list maintenance */ - nat44_session_update_lru (sm, s, thread_index); - /* Accounting */ - nat44_session_update_counters (s, now, 0); - return 1; - } - else - return 0; - } - - return 0; -} - /** * Get address and port values to be used for ICMP packet translation * and create session if needed @@ -604,8 +501,8 @@ u32 icmp_match_in2out_slow(snat_main_t *sm, vlib_node_runtime_t *node, { if (vnet_buffer(b0)->sw_if_index[VLIB_TX] != ~0) { - if (PREDICT_FALSE(nat_not_translate_output_feature(sm, - ip0, SNAT_PROTOCOL_ICMP, key0.port, key0.port, thread_index, sw_if_index0))) + if (PREDICT_FALSE(nat_not_translate_output_feature(sm, ip0, + key0.protocol, key0.port, key0.port, thread_index, sw_if_index0))) { dont_translate = 1; goto out; @@ -645,34 +542,8 @@ u32 icmp_match_in2out_slow(snat_main_t *sm, vlib_node_runtime_t *node, goto out; } - if (PREDICT_FALSE (value0.value == ~0ULL)) - { - nat_ed_ses_key_t key; - clib_bihash_kv_16_8_t s_kv, s_value; - - key.as_u64[0] = 0; - key.as_u64[1] = 0; - if (icmp_get_ed_key (ip0, &key)) - { - b0->error = node->errors[SNAT_IN2OUT_ERROR_UNSUPPORTED_PROTOCOL]; - next0 = SNAT_IN2OUT_NEXT_DROP; - goto out; - } - key.fib_index = rx_fib_index0; - 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)) - s0 = pool_elt_at_index (sm->per_thread_data[thread_index].sessions, - s_value.value); - else - { - next0 = SNAT_IN2OUT_NEXT_DROP; - goto out; - } - } - else - s0 = pool_elt_at_index (sm->per_thread_data[thread_index].sessions, - value0.value); + s0 = pool_elt_at_index (sm->per_thread_data[thread_index].sessions, + value0.value); } out: @@ -910,7 +781,8 @@ snat_hairpinning (snat_main_t *sm, ip4_header_t * ip0, udp_header_t * udp0, tcp_header_t * tcp0, - u32 proto0) + u32 proto0, + int is_ed) { snat_session_key_t key0, sm0; snat_session_t * s0; @@ -918,6 +790,7 @@ snat_hairpinning (snat_main_t *sm, ip_csum_t sum0; u32 new_dst_addr0 = 0, old_dst_addr0, ti = 0, si; u16 new_dst_port0, old_dst_port0; + int rv; key0.addr = ip0->dst_address; key0.port = udp0->dst_port; @@ -940,15 +813,29 @@ snat_hairpinning (snat_main_t *sm, else ti = sm->num_workers; - if (!clib_bihash_search_8_8 (&sm->per_thread_data[ti].out2in, &kv0, &value0)) + if (is_ed) + { + clib_bihash_kv_16_8_t ed_kv, ed_value; + make_ed_kv (&ed_kv, &ip0->dst_address, &ip0->src_address, + ip0->protocol, sm->outside_fib_index, udp0->dst_port, + udp0->src_port); + rv = clib_bihash_search_16_8 (&sm->per_thread_data[ti].out2in_ed, + &ed_kv, &ed_value); + si = ed_value.value; + } + else { + rv = clib_bihash_search_8_8 (&sm->per_thread_data[ti].out2in, &kv0, + &value0); 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; } + if (rv) + return 0; + + 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 */ @@ -999,13 +886,15 @@ static inline void snat_icmp_hairpinning (snat_main_t *sm, vlib_buffer_t * b0, ip4_header_t * ip0, - icmp46_header_t * icmp0) + icmp46_header_t * icmp0, + int is_ed) { snat_session_key_t key0, sm0; clib_bihash_kv_8_8_t kv0, value0; u32 new_dst_addr0 = 0, old_dst_addr0, si, ti = 0; ip_csum_t sum0; snat_session_t *s0; + int rv; if (!icmp_is_error_message (icmp0)) { @@ -1023,8 +912,22 @@ snat_icmp_hairpinning (snat_main_t *sm, 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)) + if (is_ed) + { + clib_bihash_kv_16_8_t ed_kv, ed_value; + make_ed_kv (&ed_kv, &ip0->dst_address, &ip0->src_address, + IP_PROTOCOL_ICMP, sm->outside_fib_index, icmp_id0, 0); + rv = clib_bihash_search_16_8 (&sm->per_thread_data[ti].out2in_ed, + &ed_kv, &ed_value); + si = ed_value.value; + } + else + { + rv = clib_bihash_search_8_8 (&sm->per_thread_data[ti].out2in, &kv0, + &value0); + si = value0.value; + } + if (rv) { /* or static mappings */ if (!snat_static_mapping_match(sm, key0, &sm0, 1, 0, 0, 0)) @@ -1035,8 +938,6 @@ snat_icmp_hairpinning (snat_main_t *sm, } else { - si = value0.value; - s0 = pool_elt_at_index (sm->per_thread_data[ti].sessions, si); new_dst_addr0 = s0->in2out.addr.as_u32; vnet_buffer(b0)->sw_if_index[VLIB_TX] = s0->in2out.fib_index; @@ -1080,7 +981,7 @@ static inline u32 icmp_in2out_slow_path (snat_main_t *sm, { /* Hairpinning */ if (vnet_buffer(b0)->sw_if_index[VLIB_TX] == 0) - snat_icmp_hairpinning(sm, b0, ip0, icmp0); + snat_icmp_hairpinning(sm, b0, ip0, icmp0, sm->endpoint_dependent); /* Accounting */ nat44_session_update_counters (s0, now, vlib_buffer_length_in_chain (sm->vlib_main, b0)); @@ -1089,438 +990,99 @@ static inline u32 icmp_in2out_slow_path (snat_main_t *sm, } return next0; } + static inline void -snat_hairpinning_unknown_proto (snat_main_t *sm, - vlib_buffer_t * b, - ip4_header_t * ip) +nat_hairpinning_sm_unknown_proto (snat_main_t * sm, + vlib_buffer_t * b, + ip4_header_t * ip) { - 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; - nat_ed_ses_key_t key; - snat_session_key_t m_key; snat_static_mapping_t *m; + u32 old_addr, new_addr; ip_csum_t sum; - snat_session_t *s; - old_addr = ip->dst_address.as_u32; - key.l_addr.as_u32 = ip->dst_address.as_u32; - key.r_addr.as_u32 = ip->src_address.as_u32; - key.fib_index = sm->outside_fib_index; - key.proto = ip->protocol; - key.r_port = 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_ed, &s_kv, &s_value)) - { - m_key.addr = ip->dst_address; - m_key.fib_index = sm->outside_fib_index; - m_key.port = 0; - m_key.protocol = 0; - kv.key = m_key.as_u64; - if (clib_bihash_search_8_8 (&sm->static_mapping_by_external, &kv, &value)) - return; + make_sm_kv (&kv, &ip->dst_address, 0, sm->outside_fib_index, 0); + if (clib_bihash_search_8_8 (&sm->static_mapping_by_external, &kv, &value)) + return; - m = pool_elt_at_index (sm->static_mappings, value.value); - if (vnet_buffer(b)->sw_if_index[VLIB_TX] == ~0) - vnet_buffer(b)->sw_if_index[VLIB_TX] = m->fib_index; - new_addr = ip->dst_address.as_u32 = m->local_addr.as_u32; - } - else - { - if (sm->num_workers > 1) - ti = sm->worker_out2in_cb (ip, sm->outside_fib_index); - else - ti = sm->num_workers; + m = pool_elt_at_index (sm->static_mappings, value.value); - s = pool_elt_at_index (sm->per_thread_data[ti].sessions, s_value.value); - if (vnet_buffer(b)->sw_if_index[VLIB_TX] == ~0) - vnet_buffer(b)->sw_if_index[VLIB_TX] = s->in2out.fib_index; - new_addr = ip->dst_address.as_u32 = s->in2out.addr.as_u32; - } + old_addr = ip->dst_address.as_u32; + new_addr = ip->dst_address.as_u32 = m->local_addr.as_u32; sum = ip->checksum; sum = ip_csum_update (sum, old_addr, new_addr, ip4_header_t, dst_address); ip->checksum = ip_csum_fold (sum); + + if (vnet_buffer(b)->sw_if_index[VLIB_TX] == ~0) + vnet_buffer(b)->sw_if_index[VLIB_TX] = m->fib_index; } -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_node_runtime_t * node) +static int +nat_in2out_sm_unknown_proto (snat_main_t *sm, + vlib_buffer_t * b, + ip4_header_t * ip, + u32 rx_fib_index) { clib_bihash_kv_8_8_t kv, value; - clib_bihash_kv_16_8_t s_kv, s_value; snat_static_mapping_t *m; snat_session_key_t m_key; - u32 old_addr, new_addr = 0; + u32 old_addr, new_addr; ip_csum_t sum; - snat_user_t *u; - dlist_elt_t *head, *elt; - snat_main_per_thread_data_t *tsm = &sm->per_thread_data[thread_index]; - u32 elt_index, head_index, ses_index; - snat_session_t * s; - nat_ed_ses_key_t key; - u32 address_index = ~0; - int i; - u8 is_sm = 0; + + m_key.addr = ip->src_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_local, &kv, &value)) + return 1; + + m = pool_elt_at_index (sm->static_mappings, value.value); old_addr = ip->src_address.as_u32; + new_addr = ip->src_address.as_u32 = m->external_addr.as_u32; + sum = ip->checksum; + sum = ip_csum_update (sum, old_addr, new_addr, ip4_header_t, src_address); + ip->checksum = ip_csum_fold (sum); - key.l_addr = ip->src_address; - key.r_addr = ip->dst_address; - key.fib_index = rx_fib_index; - key.proto = ip->protocol; - key.l_port = 0; - key.r_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_ed, &s_kv, &s_value)) + /* Hairpinning */ + if (vnet_buffer(b)->sw_if_index[VLIB_TX] == ~0) { - s = pool_elt_at_index (tsm->sessions, s_value.value); - new_addr = ip->src_address.as_u32 = s->out2in.addr.as_u32; + nat_hairpinning_sm_unknown_proto (sm, b, ip); + vnet_buffer(b)->sw_if_index[VLIB_TX] = sm->outside_fib_index; } - else - { - if (PREDICT_FALSE (maximum_sessions_exceeded(sm, thread_index))) - { - b->error = node->errors[SNAT_IN2OUT_ERROR_MAX_SESSIONS_EXCEEDED]; - nat_ipfix_logging_max_sessions(sm->max_translations); - nat_log_notice ("maximum sessions exceeded"); - return 0; - } - u = nat_user_get_or_create (sm, &ip->src_address, rx_fib_index, - thread_index); - if (!u) - { - nat_log_warn ("create NAT user failed"); - return 0; - } + return 0; +} - m_key.addr = ip->src_address; - m_key.port = 0; - m_key.protocol = 0; - m_key.fib_index = rx_fib_index; - kv.key = m_key.as_u64; +static inline uword +snat_in2out_node_fn_inline (vlib_main_t * vm, + vlib_node_runtime_t * node, + vlib_frame_t * frame, int is_slow_path, + int is_output_feature) +{ + u32 n_left_from, * from, * to_next; + snat_in2out_next_t next_index; + u32 pkts_processed = 0; + snat_main_t * sm = &snat_main; + f64 now = vlib_time_now (vm); + u32 stats_node_index; + u32 thread_index = vlib_get_thread_index (); - /* Try to find static mapping first */ - if (!clib_bihash_search_8_8 (&sm->static_mapping_by_local, &kv, &value)) - { - m = pool_elt_at_index (sm->static_mappings, value.value); - new_addr = ip->src_address.as_u32 = m->external_addr.as_u32; - is_sm = 1; - goto create_ses; - } - /* Fallback to 3-tuple key */ - else - { - /* Choose same out address as for TCP/UDP session to same destination */ - head_index = u->sessions_per_user_list_head_index; - head = pool_elt_at_index (tsm->list_pool, head_index); - elt_index = head->next; - if (PREDICT_FALSE (elt_index == ~0)) - ses_index = ~0; - else - { - elt = pool_elt_at_index (tsm->list_pool, elt_index); - ses_index = elt->value; - } + stats_node_index = is_slow_path ? snat_in2out_slowpath_node.index : + snat_in2out_node.index; - while (ses_index != ~0) - { - s = pool_elt_at_index (tsm->sessions, ses_index); - elt_index = elt->next; - elt = pool_elt_at_index (tsm->list_pool, elt_index); - ses_index = elt->value; + from = vlib_frame_vector_args (frame); + n_left_from = frame->n_vectors; + next_index = node->cached_next_index; - if (s->ext_host_addr.as_u32 == ip->dst_address.as_u32) - { - new_addr = ip->src_address.as_u32 = s->out2in.addr.as_u32; - address_index = s->outside_address_index; + while (n_left_from > 0) + { + u32 n_left_to_next; - key.fib_index = sm->outside_fib_index; - 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_ed, &s_kv, &s_value)) - break; - - goto create_ses; - } - } - key.fib_index = sm->outside_fib_index; - for (i = 0; i < vec_len (sm->addresses); i++) - { - 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_ed, &s_kv, &s_value)) - { - new_addr = ip->src_address.as_u32 = key.l_addr.as_u32; - address_index = i; - goto create_ses; - } - } - return 0; - } - -create_ses: - s = nat_session_alloc_or_recycle (sm, u, thread_index); - if (!s) - { - nat_log_warn ("create NAT session failed"); - return 0; - } - - s->ext_host_addr.as_u32 = ip->dst_address.as_u32; - s->flags |= SNAT_SESSION_FLAG_UNKNOWN_PROTO; - s->flags |= SNAT_SESSION_FLAG_ENDPOINT_DEPENDENT; - s->outside_address_index = address_index; - s->out2in.addr.as_u32 = new_addr; - s->out2in.fib_index = sm->outside_fib_index; - s->in2out.addr.as_u32 = old_addr; - s->in2out.fib_index = rx_fib_index; - s->in2out.port = s->out2in.port = ip->protocol; - if (is_sm) - s->flags |= SNAT_SESSION_FLAG_STATIC_MAPPING; - user_session_increment (sm, u, is_sm); - - /* Add to lookup tables */ - key.l_addr.as_u32 = old_addr; - key.r_addr = ip->dst_address; - key.proto = ip->protocol; - key.fib_index = rx_fib_index; - 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_ed, &s_kv, 1)) - nat_log_notice ("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_ed, &s_kv, 1)) - nat_log_notice ("out2in key add failed"); - } - - /* 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); - - /* Accounting */ - nat44_session_update_counters (s, now, vlib_buffer_length_in_chain (vm, b)); - /* Per-user LRU list maintenance */ - nat44_session_update_lru (sm, s, thread_index); - - /* Hairpinning */ - if (vnet_buffer(b)->sw_if_index[VLIB_TX] == ~0) - snat_hairpinning_unknown_proto(sm, b, ip); - - 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 * -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, - vlib_node_runtime_t * node) -{ - 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; - snat_user_t *u; - u8 lb; - - 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.r_port = udp->dst_port; - 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); - if (is_fwd_bypass_session (s)) - { - if (ip->protocol == IP_PROTOCOL_TCP) - { - if (nat44_set_tcp_session_state_i2o (sm, s, tcp, thread_index)) - return 0; - } - /* Per-user LRU list maintenance */ - nat44_session_update_lru (sm, s, thread_index); - return 0; - } - } - else - { - if (PREDICT_FALSE (maximum_sessions_exceeded (sm, thread_index))) - { - b->error = node->errors[SNAT_IN2OUT_ERROR_MAX_SESSIONS_EXCEEDED]; - nat_ipfix_logging_max_sessions(sm->max_translations); - nat_log_notice ("maximum sessions exceeded"); - return 0; - } - - 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, 0, &lb)) - return 0; - - u = nat_user_get_or_create (sm, &ip->src_address, rx_fib_index, - thread_index); - if (!u) - { - nat_log_warn ("create NAT user failed"); - return 0; - } - - s = nat_session_alloc_or_recycle (sm, u, thread_index); - if (!s) - { - nat_log_warn ("create NAT session failed"); - return 0; - } - - s->ext_host_addr.as_u32 = ip->dst_address.as_u32; - s->ext_host_port = udp->dst_port; - s->flags |= SNAT_SESSION_FLAG_STATIC_MAPPING; - if (lb) - s->flags |= SNAT_SESSION_FLAG_LOAD_BALANCING; - s->flags |= SNAT_SESSION_FLAG_ENDPOINT_DEPENDENT; - s->outside_address_index = ~0; - s->in2out = l_key; - s->out2in = e_key; - s->out2in.protocol = l_key.protocol; - user_session_increment (sm, u, 1 /* static */); - - /* Add to lookup tables */ - s_kv.value = s - tsm->sessions; - if (clib_bihash_add_del_16_8 (&sm->in2out_ed, &s_kv, 1)) - nat_log_notice ("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)) - nat_log_notice ("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); - if (is_twice_nat_session (s)) - sum = ip_csum_update (sum, ip->dst_address.as_u32, - s->ext_host_addr.as_u32, ip4_header_t, dst_address); - ip->checksum = ip_csum_fold (sum); - - if (vnet_buffer(b)->sw_if_index[VLIB_TX] == ~0) - vnet_buffer(b)->sw_if_index[VLIB_TX] = sm->outside_fib_index; - - 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); - if (is_twice_nat_session (s)) - { - sum = ip_csum_update (sum, ip->dst_address.as_u32, - s->ext_host_addr.as_u32, ip4_header_t, - dst_address); - sum = ip_csum_update (sum, tcp->dst_port, s->ext_host_port, - ip4_header_t, length); - tcp->dst_port = s->ext_host_port; - ip->dst_address.as_u32 = s->ext_host_addr.as_u32; - } - tcp->checksum = ip_csum_fold(sum); - if (nat44_set_tcp_session_state_i2o (sm, s, tcp, thread_index)) - return s; - } - else - { - udp->src_port = s->out2in.port; - if (is_twice_nat_session (s)) - { - udp->dst_port = s->ext_host_port; - ip->dst_address.as_u32 = s->ext_host_addr.as_u32; - } - udp->checksum = 0; - } - - /* Accounting */ - nat44_session_update_counters (s, now, vlib_buffer_length_in_chain (vm, b)); - /* Per-user LRU list maintenance */ - nat44_session_update_lru (sm, s, thread_index); - - return s; -} - -static inline uword -snat_in2out_node_fn_inline (vlib_main_t * vm, - vlib_node_runtime_t * node, - vlib_frame_t * frame, int is_slow_path, - int is_output_feature) -{ - u32 n_left_from, * from, * to_next; - snat_in2out_next_t next_index; - u32 pkts_processed = 0; - snat_main_t * sm = &snat_main; - f64 now = vlib_time_now (vm); - u32 stats_node_index; - u32 thread_index = vlib_get_thread_index (); - - stats_node_index = is_slow_path ? snat_in2out_slowpath_node.index : - snat_in2out_node.index; - - from = vlib_frame_vector_args (frame); - n_left_from = frame->n_vectors; - next_index = node->cached_next_index; - - while (n_left_from > 0) - { - u32 n_left_to_next; - - vlib_get_next_frame (vm, node, next_index, - to_next, n_left_to_next); + vlib_get_next_frame (vm, node, next_index, + to_next, n_left_to_next); while (n_left_from >= 4 && n_left_to_next >= 2) { @@ -1600,10 +1162,11 @@ snat_in2out_node_fn_inline (vlib_main_t * vm, { if (PREDICT_FALSE (proto0 == ~0)) { - s0 = snat_in2out_unknown_proto (sm, b0, ip0, rx_fib_index0, - thread_index, now, vm, node); - if (!s0) - next0 = SNAT_IN2OUT_NEXT_DROP; + if (nat_in2out_sm_unknown_proto (sm, b0, ip0, rx_fib_index0)) + { + next0 = SNAT_IN2OUT_NEXT_DROP; + b0->error = node->errors[SNAT_IN2OUT_ERROR_UNSUPPORTED_PROTOCOL]; + } goto trace00; } @@ -1617,12 +1180,6 @@ snat_in2out_node_fn_inline (vlib_main_t * vm, } else { - if (is_output_feature) - { - if (PREDICT_FALSE(nat_not_translate_output_feature_fwd(sm, ip0, thread_index))) - goto trace00; - } - if (PREDICT_FALSE (proto0 == ~0 || proto0 == SNAT_PROTOCOL_ICMP)) { next0 = SNAT_IN2OUT_NEXT_SLOW_PATH; @@ -1673,30 +1230,8 @@ snat_in2out_node_fn_inline (vlib_main_t * vm, } } else - { - if (PREDICT_FALSE (value0.value == ~0ULL)) - { - if (is_slow_path) - { - s0 = snat_in2out_lb(sm, b0, ip0, rx_fib_index0, - thread_index, now, vm, node); - if (!s0 && !sm->forwarding_enabled) - next0 = SNAT_IN2OUT_NEXT_DROP; - 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); - } - } + s0 = pool_elt_at_index (sm->per_thread_data[thread_index].sessions, + value0.value); b0->flags |= VNET_BUFFER_F_IS_NATED; @@ -1787,10 +1322,11 @@ snat_in2out_node_fn_inline (vlib_main_t * vm, { if (PREDICT_FALSE (proto1 == ~0)) { - s1 = snat_in2out_unknown_proto (sm, b1, ip1, rx_fib_index1, - thread_index, now, vm, node); - if (!s1) - next1 = SNAT_IN2OUT_NEXT_DROP; + if (nat_in2out_sm_unknown_proto (sm, b1, ip1, rx_fib_index1)) + { + next1 = SNAT_IN2OUT_NEXT_DROP; + b1->error = node->errors[SNAT_IN2OUT_ERROR_UNSUPPORTED_PROTOCOL]; + } goto trace01; } @@ -1804,12 +1340,6 @@ snat_in2out_node_fn_inline (vlib_main_t * vm, } else { - if (is_output_feature) - { - if (PREDICT_FALSE(nat_not_translate_output_feature_fwd(sm, ip1, thread_index))) - goto trace01; - } - if (PREDICT_FALSE (proto1 == ~0 || proto1 == SNAT_PROTOCOL_ICMP)) { next1 = SNAT_IN2OUT_NEXT_SLOW_PATH; @@ -1860,30 +1390,8 @@ snat_in2out_node_fn_inline (vlib_main_t * vm, } } else - { - if (PREDICT_FALSE (value1.value == ~0ULL)) - { - if (is_slow_path) - { - s1 = snat_in2out_lb(sm, b1, ip1, rx_fib_index1, - thread_index, now, vm, node); - if (!s1 && !sm->forwarding_enabled) - next1 = SNAT_IN2OUT_NEXT_DROP; - 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); - } - } + s1 = pool_elt_at_index (sm->per_thread_data[thread_index].sessions, + value1.value); b1->flags |= VNET_BUFFER_F_IS_NATED; @@ -2010,10 +1518,11 @@ snat_in2out_node_fn_inline (vlib_main_t * vm, { if (PREDICT_FALSE (proto0 == ~0)) { - s0 = snat_in2out_unknown_proto (sm, b0, ip0, rx_fib_index0, - thread_index, now, vm, node); - if (!s0) - next0 = SNAT_IN2OUT_NEXT_DROP; + if (nat_in2out_sm_unknown_proto (sm, b0, ip0, rx_fib_index0)) + { + next0 = SNAT_IN2OUT_NEXT_DROP; + b0->error = node->errors[SNAT_IN2OUT_ERROR_UNSUPPORTED_PROTOCOL]; + } goto trace0; } @@ -2027,12 +1536,6 @@ snat_in2out_node_fn_inline (vlib_main_t * vm, } else { - if (is_output_feature) - { - if (PREDICT_FALSE(nat_not_translate_output_feature_fwd(sm, ip0, thread_index))) - goto trace0; - } - if (PREDICT_FALSE (proto0 == ~0 || proto0 == SNAT_PROTOCOL_ICMP)) { next0 = SNAT_IN2OUT_NEXT_SLOW_PATH; @@ -2084,30 +1587,8 @@ snat_in2out_node_fn_inline (vlib_main_t * vm, } } else - { - if (PREDICT_FALSE (value0.value == ~0ULL)) - { - if (is_slow_path) - { - s0 = snat_in2out_lb(sm, b0, ip0, rx_fib_index0, - thread_index, now, vm, node); - if (!s0 && !sm->forwarding_enabled) - next0 = SNAT_IN2OUT_NEXT_DROP; - 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); - } - } + s0 = pool_elt_at_index (sm->per_thread_data[thread_index].sessions, + value0.value); b0->flags |= VNET_BUFFER_F_IS_NATED; @@ -2323,12 +1804,13 @@ VLIB_NODE_FUNCTION_MULTIARCH (snat_in2out_output_slowpath_node, extern vnet_feature_arc_registration_t vnet_feat_arc_ip4_local; -static uword -nat44_hairpinning_fn (vlib_main_t * vm, - vlib_node_runtime_t * node, - vlib_frame_t * frame) +static inline uword +nat44_hairpinning_fn_inline (vlib_main_t * vm, + vlib_node_runtime_t * node, + vlib_frame_t * frame, + int is_ed) { - u32 n_left_from, * from, * to_next; + u32 n_left_from, * from, * to_next, stats_node_index; snat_in2out_next_t next_index; u32 pkts_processed = 0; snat_main_t * sm = &snat_main; @@ -2336,6 +1818,8 @@ nat44_hairpinning_fn (vlib_main_t * vm, u8 arc_index = vnet_feat_arc_ip4_local.feature_arc_index; vnet_feature_config_main_t *cm = &fm->feature_config_mains[arc_index]; + stats_node_index = is_ed ? nat44_ed_hairpinning_node.index : + nat44_hairpinning_node.index; from = vlib_frame_vector_args (frame); n_left_from = frame->n_vectors; next_index = node->cached_next_index; @@ -2375,7 +1859,7 @@ nat44_hairpinning_fn (vlib_main_t * vm, vnet_get_config_data (&cm->config_main, &b0->current_config_index, &next0, 0); - if (snat_hairpinning (sm, b0, ip0, udp0, tcp0, proto0)) + if (snat_hairpinning (sm, b0, ip0, udp0, tcp0, proto0, is_ed)) next0 = SNAT_IN2OUT_NEXT_LOOKUP; pkts_processed += next0 != SNAT_IN2OUT_NEXT_DROP; @@ -2389,12 +1873,20 @@ nat44_hairpinning_fn (vlib_main_t * vm, vlib_put_next_frame (vm, node, next_index, n_left_to_next); } - vlib_node_increment_counter (vm, nat44_hairpinning_node.index, + vlib_node_increment_counter (vm, stats_node_index, SNAT_IN2OUT_ERROR_IN2OUT_PACKETS, pkts_processed); return frame->n_vectors; } +static uword +nat44_hairpinning_fn (vlib_main_t * vm, + vlib_node_runtime_t * node, + vlib_frame_t * frame) +{ + return nat44_hairpinning_fn_inline (vm, node, frame, 0); +} + VLIB_REGISTER_NODE (nat44_hairpinning_node) = { .function = nat44_hairpinning_fn, .name = "nat44-hairpinning", @@ -2412,9 +1904,34 @@ VLIB_REGISTER_NODE (nat44_hairpinning_node) = { VLIB_NODE_FUNCTION_MULTIARCH (nat44_hairpinning_node, nat44_hairpinning_fn); -static inline void -nat44_reass_hairpinning (snat_main_t *sm, - vlib_buffer_t * b0, +static uword +nat44_ed_hairpinning_fn (vlib_main_t * vm, + vlib_node_runtime_t * node, + vlib_frame_t * frame) +{ + return nat44_hairpinning_fn_inline (vm, node, frame, 1); +} + +VLIB_REGISTER_NODE (nat44_ed_hairpinning_node) = { + .function = nat44_ed_hairpinning_fn, + .name = "nat44-ed-hairpinning", + .vector_size = sizeof (u32), + .type = VLIB_NODE_TYPE_INTERNAL, + .n_errors = ARRAY_LEN(snat_in2out_error_strings), + .error_strings = snat_in2out_error_strings, + .n_next_nodes = 2, + .next_nodes = { + [SNAT_IN2OUT_NEXT_DROP] = "error-drop", + [SNAT_IN2OUT_NEXT_LOOKUP] = "ip4-lookup", + }, +}; + +VLIB_NODE_FUNCTION_MULTIARCH (nat44_ed_hairpinning_node, + nat44_ed_hairpinning_fn); + +static inline void +nat44_reass_hairpinning (snat_main_t *sm, + vlib_buffer_t * b0, ip4_header_t * ip0, u16 sport, u16 dport, @@ -2639,46 +2156,1351 @@ nat44_in2out_reass_node_fn (vlib_main_t * vm, reass0->sess_index); } - old_addr0 = ip0->src_address.as_u32; - ip0->src_address = s0->out2in.addr; - new_addr0 = ip0->src_address.as_u32; - vnet_buffer(b0)->sw_if_index[VLIB_TX] = s0->out2in.fib_index; + old_addr0 = ip0->src_address.as_u32; + ip0->src_address = s0->out2in.addr; + new_addr0 = ip0->src_address.as_u32; + vnet_buffer(b0)->sw_if_index[VLIB_TX] = s0->out2in.fib_index; + + sum0 = ip0->checksum; + sum0 = ip_csum_update (sum0, old_addr0, new_addr0, + ip4_header_t, + src_address /* changed member */); + ip0->checksum = ip_csum_fold (sum0); + + if (PREDICT_FALSE (ip4_is_first_fragment (ip0))) + { + if (PREDICT_TRUE(proto0 == SNAT_PROTOCOL_TCP)) + { + old_port0 = tcp0->src_port; + tcp0->src_port = s0->out2in.port; + new_port0 = tcp0->src_port; + + sum0 = tcp0->checksum; + sum0 = ip_csum_update (sum0, old_addr0, new_addr0, + ip4_header_t, + dst_address /* changed member */); + sum0 = ip_csum_update (sum0, old_port0, new_port0, + ip4_header_t /* cheat */, + length /* changed member */); + tcp0->checksum = ip_csum_fold(sum0); + } + else + { + old_port0 = udp0->src_port; + udp0->src_port = s0->out2in.port; + udp0->checksum = 0; + } + } + + /* Hairpinning */ + nat44_reass_hairpinning (sm, b0, ip0, s0->out2in.port, + s0->ext_host_port, proto0); + + /* Accounting */ + nat44_session_update_counters (s0, now, + vlib_buffer_length_in_chain (vm, b0)); + /* Per-user LRU list maintenance */ + nat44_session_update_lru (sm, s0, thread_index); + + trace0: + if (PREDICT_FALSE((node->flags & VLIB_NODE_FLAG_TRACE) + && (b0->flags & VLIB_BUFFER_IS_TRACED))) + { + nat44_in2out_reass_trace_t *t = + vlib_add_trace (vm, node, b0, sizeof (*t)); + t->cached = cached0; + t->sw_if_index = sw_if_index0; + t->next_index = next0; + } + + if (cached0) + { + n_left_to_next++; + to_next--; + } + else + { + pkts_processed += next0 != SNAT_IN2OUT_NEXT_DROP; + + /* verify speculative enqueue, maybe switch current next frame */ + vlib_validate_buffer_enqueue_x1 (vm, node, next_index, + to_next, n_left_to_next, + bi0, next0); + } + + if (n_left_from == 0 && vec_len (fragments_to_loopback)) + { + from = vlib_frame_vector_args (frame); + u32 len = vec_len (fragments_to_loopback); + if (len <= VLIB_FRAME_SIZE) + { + clib_memcpy (from, fragments_to_loopback, sizeof (u32) * len); + n_left_from = len; + vec_reset_length (fragments_to_loopback); + } + else + { + clib_memcpy (from, + fragments_to_loopback + (len - VLIB_FRAME_SIZE), + sizeof (u32) * VLIB_FRAME_SIZE); + n_left_from = VLIB_FRAME_SIZE; + _vec_len (fragments_to_loopback) = len - VLIB_FRAME_SIZE; + } + } + } + + vlib_put_next_frame (vm, node, next_index, n_left_to_next); + } + + vlib_node_increment_counter (vm, nat44_in2out_reass_node.index, + SNAT_IN2OUT_ERROR_IN2OUT_PACKETS, + pkts_processed); + + nat_send_all_to_node (vm, fragments_to_drop, node, + &node->errors[SNAT_IN2OUT_ERROR_DROP_FRAGMENT], + SNAT_IN2OUT_NEXT_DROP); + + vec_free (fragments_to_drop); + vec_free (fragments_to_loopback); + return frame->n_vectors; +} + +VLIB_REGISTER_NODE (nat44_in2out_reass_node) = { + .function = nat44_in2out_reass_node_fn, + .name = "nat44-in2out-reass", + .vector_size = sizeof (u32), + .format_trace = format_nat44_in2out_reass_trace, + .type = VLIB_NODE_TYPE_INTERNAL, + + .n_errors = ARRAY_LEN(snat_in2out_error_strings), + .error_strings = snat_in2out_error_strings, + + .n_next_nodes = SNAT_IN2OUT_N_NEXT, + .next_nodes = { + [SNAT_IN2OUT_NEXT_DROP] = "error-drop", + [SNAT_IN2OUT_NEXT_LOOKUP] = "ip4-lookup", + [SNAT_IN2OUT_NEXT_SLOW_PATH] = "nat44-in2out-slowpath", + [SNAT_IN2OUT_NEXT_ICMP_ERROR] = "ip4-icmp-error", + [SNAT_IN2OUT_NEXT_REASS] = "nat44-in2out-reass", + }, +}; + +VLIB_NODE_FUNCTION_MULTIARCH (nat44_in2out_reass_node, + nat44_in2out_reass_node_fn); + +/*******************************/ +/*** endpoint-dependent mode ***/ +/*******************************/ + +static_always_inline int +icmp_get_ed_key(ip4_header_t *ip0, nat_ed_ses_key_t *p_key0) +{ + icmp46_header_t *icmp0; + nat_ed_ses_key_t key0; + icmp_echo_header_t *echo0, *inner_echo0 = 0; + ip4_header_t *inner_ip0 = 0; + void *l4_header = 0; + icmp46_header_t *inner_icmp0; + + icmp0 = (icmp46_header_t *) ip4_next_header (ip0); + echo0 = (icmp_echo_header_t *)(icmp0+1); + + if (!icmp_is_error_message (icmp0)) + { + key0.proto = IP_PROTOCOL_ICMP; + key0.l_addr = ip0->src_address; + key0.r_addr = ip0->dst_address; + key0.l_port = echo0->identifier; + key0.r_port = 0; + } + else + { + inner_ip0 = (ip4_header_t *)(echo0+1); + l4_header = ip4_next_header (inner_ip0); + key0.proto = inner_ip0->protocol; + key0.r_addr = inner_ip0->src_address; + key0.l_addr = inner_ip0->dst_address; + switch (ip_proto_to_snat_proto (inner_ip0->protocol)) + { + case SNAT_PROTOCOL_ICMP: + inner_icmp0 = (icmp46_header_t*)l4_header; + inner_echo0 = (icmp_echo_header_t *)(inner_icmp0+1); + key0.r_port = 0; + key0.l_port = inner_echo0->identifier; + break; + case SNAT_PROTOCOL_UDP: + case SNAT_PROTOCOL_TCP: + key0.l_port = ((tcp_udp_header_t*)l4_header)->dst_port; + key0.r_port = ((tcp_udp_header_t*)l4_header)->src_port; + break; + default: + return SNAT_IN2OUT_ERROR_UNSUPPORTED_PROTOCOL; + } + } + *p_key0 = key0; + return 0; +} + +static u32 +slow_path_ed (snat_main_t *sm, + vlib_buffer_t *b, + u32 rx_fib_index, + clib_bihash_kv_16_8_t *kv, + snat_session_t ** sessionp, + vlib_node_runtime_t * node, + u32 next, + u32 thread_index) +{ + snat_session_t *s; + snat_user_t *u; + snat_session_key_t key0, key1; + u8 lb = 0, is_sm = 0; + u32 address_index = ~0; + snat_main_per_thread_data_t *tsm = &sm->per_thread_data[thread_index]; + nat_ed_ses_key_t *key = (nat_ed_ses_key_t *) kv->key; + u32 proto = ip_proto_to_snat_proto (key->proto); + + if (PREDICT_FALSE (maximum_sessions_exceeded (sm, thread_index))) + { + b->error = node->errors[SNAT_IN2OUT_ERROR_MAX_SESSIONS_EXCEEDED]; + nat_ipfix_logging_max_sessions(sm->max_translations); + nat_log_notice ("maximum sessions exceeded"); + return SNAT_IN2OUT_NEXT_DROP; + } + + key0.addr = key->l_addr; + key0.port = key->l_port; + key1.protocol = key0.protocol = proto; + key0.fib_index = rx_fib_index; + key1.fib_index = sm->outside_fib_index; + /* First try to match static mapping by local address and port */ + if (snat_static_mapping_match (sm, key0, &key1, 0, 0, 0, &lb)) + { + /* Try to create dynamic translation */ + if (snat_alloc_outside_address_and_port (sm->addresses, rx_fib_index, + thread_index, &key1, + &address_index, + sm->port_per_thread, + tsm->snat_thread_index)) + { + nat_log_notice ("addresses exhausted"); + b->error = node->errors[SNAT_IN2OUT_ERROR_OUT_OF_PORTS]; + return SNAT_IN2OUT_NEXT_DROP; + } + } + else + is_sm = 1; + + u = nat_user_get_or_create (sm, &key->l_addr, rx_fib_index, thread_index); + if (!u) + { + nat_log_warn ("create NAT user failed"); + return SNAT_IN2OUT_NEXT_DROP; + } + + s = nat_session_alloc_or_recycle (sm, u, thread_index); + if (!s) + { + nat_log_warn ("create NAT session failed"); + return SNAT_IN2OUT_NEXT_DROP; + } + + user_session_increment (sm, u, is_sm); + if (is_sm) + s->flags |= SNAT_SESSION_FLAG_STATIC_MAPPING; + if (lb) + s->flags |= SNAT_SESSION_FLAG_LOAD_BALANCING; + s->flags |= SNAT_SESSION_FLAG_ENDPOINT_DEPENDENT; + s->outside_address_index = address_index; + s->ext_host_addr = key->r_addr; + s->ext_host_port = key->r_port; + s->in2out = key0; + s->out2in = key1; + s->out2in.protocol = key0.protocol; + + /* Add to lookup tables */ + kv->value = s - tsm->sessions; + if (clib_bihash_add_del_16_8 (&tsm->in2out_ed, kv, 1)) + nat_log_notice ("in2out-ed key add failed"); + + make_ed_kv (kv, &key1.addr, &key->r_addr, key->proto, key1.fib_index, + key1.port, key->r_port); + kv->value = s - tsm->sessions; + if (clib_bihash_add_del_16_8 (&tsm->out2in_ed, kv, 1)) + nat_log_notice ("out2in-ed key add failed"); + + *sessionp = s; + + /* log NAT event */ + snat_ipfix_logging_nat44_ses_create(s->in2out.addr.as_u32, + s->out2in.addr.as_u32, + s->in2out.protocol, + s->in2out.port, + s->out2in.port, + s->in2out.fib_index); + return next; +} + +static_always_inline int +nat44_ed_not_translate (snat_main_t * sm, vlib_node_runtime_t *node, + u32 sw_if_index, ip4_header_t * ip, u32 proto, + u32 rx_fib_index, u32 thread_index) +{ + udp_header_t *udp = ip4_next_header (ip); + snat_main_per_thread_data_t *tsm = &sm->per_thread_data[thread_index]; + clib_bihash_kv_16_8_t kv, value; + snat_session_key_t key0, key1; + + make_ed_kv (&kv, &ip->dst_address, &ip->src_address, ip->protocol, + sm->outside_fib_index, udp->dst_port, udp->src_port); + + /* NAT packet aimed at external address if */ + /* has active sessions */ + if (clib_bihash_search_16_8 (&tsm->out2in_ed, &kv, &value)) + { + key0.addr = ip->dst_address; + key0.port = udp->dst_port; + key0.protocol = proto; + key0.fib_index = sm->outside_fib_index; + /* or is static mappings */ + if (!snat_static_mapping_match(sm, key0, &key1, 1, 0, 0, 0)) + return 0; + } + else + return 0; + + if (sm->forwarding_enabled) + return 1; + + return snat_not_translate_fast(sm, node, sw_if_index, ip, proto, rx_fib_index); +} + +static_always_inline int +nat_not_translate_output_feature_fwd (snat_main_t * sm, ip4_header_t * ip, + u32 thread_index, f64 now, + vlib_main_t * vm, vlib_buffer_t * b) +{ + nat_ed_ses_key_t key; + clib_bihash_kv_16_8_t kv, value; + udp_header_t *udp; + snat_session_t *s = 0; + snat_main_per_thread_data_t *tsm = &sm->per_thread_data[thread_index]; + + if (!sm->forwarding_enabled) + return 0; + + if (ip->protocol == IP_PROTOCOL_ICMP) + { + key.as_u64[0] = key.as_u64[1] = 0; + if (icmp_get_ed_key (ip, &key)) + return 0; + key.fib_index = 0; + kv.key[0] = key.as_u64[0]; + kv.key[1] = key.as_u64[1]; + } + else if (ip->protocol == IP_PROTOCOL_UDP || ip->protocol == IP_PROTOCOL_TCP) + { + udp = ip4_next_header(ip); + make_ed_kv (&kv, &ip->src_address, &ip->dst_address, ip->protocol, 0, + udp->src_port, udp->dst_port); + } + else + { + make_ed_kv (&kv, &ip->src_address, &ip->dst_address, ip->protocol, 0, 0, + 0); + } + + if (!clib_bihash_search_16_8 (&tsm->in2out_ed, &kv, &value)) + { + s = pool_elt_at_index (tsm->sessions, value.value); + if (is_fwd_bypass_session (s)) + { + if (ip->protocol == IP_PROTOCOL_TCP) + { + tcp_header_t *tcp = ip4_next_header(ip); + if (nat44_set_tcp_session_state_i2o (sm, s, tcp, thread_index)) + return 1; + } + /* Per-user LRU list maintenance */ + nat44_session_update_lru (sm, s, thread_index); + /* Accounting */ + nat44_session_update_counters (s, now, + vlib_buffer_length_in_chain (vm, b)); + return 1; + } + else + return 0; + } + + return 0; +} + +static_always_inline int +nat44_ed_not_translate_output_feature (snat_main_t * sm, ip4_header_t * ip, + u8 proto, u16 src_port, u16 dst_port, + u32 thread_index, u32 sw_if_index) +{ + clib_bihash_kv_16_8_t kv, value; + snat_main_per_thread_data_t *tsm = tsm = &sm->per_thread_data[thread_index]; + snat_interface_t *i; + + /* src NAT check */ + make_ed_kv (&kv, &ip->src_address, &ip->dst_address, proto, + sm->outside_fib_index, src_port, dst_port); + if (!clib_bihash_search_16_8 (&tsm->out2in_ed, &kv, &value)) + return 1; + + /* dst NAT check */ + make_ed_kv (&kv, &ip->dst_address, &ip->src_address, proto, + sm->inside_fib_index, dst_port, src_port); + if (!clib_bihash_search_16_8 (&tsm->in2out_ed, &kv, &value)) + { + /* hairpinning */ + pool_foreach (i, sm->output_feature_interfaces, + ({ + if ((nat_interface_is_inside(i)) && (sw_if_index == i->sw_if_index)) + return 0; + })); + return 1; + } + + return 0; +} + +u32 +icmp_match_in2out_ed(snat_main_t *sm, vlib_node_runtime_t *node, + u32 thread_index, vlib_buffer_t *b, ip4_header_t *ip, + u8 *p_proto, snat_session_key_t *p_value, + u8 *p_dont_translate, void *d, void *e) +{ + icmp46_header_t *icmp; + u32 sw_if_index; + u32 rx_fib_index; + nat_ed_ses_key_t key; + snat_session_t *s = 0; + u8 dont_translate = 0; + clib_bihash_kv_16_8_t kv, value; + u32 next = ~0; + int err; + snat_main_per_thread_data_t *tsm = &sm->per_thread_data[thread_index]; + + icmp = (icmp46_header_t *) ip4_next_header (ip); + sw_if_index = vnet_buffer(b)->sw_if_index[VLIB_RX]; + rx_fib_index = ip4_fib_table_get_index_for_sw_if_index (sw_if_index); + + key.as_u64[0] = key.as_u64[1] = 0; + err = icmp_get_ed_key (ip, &key); + if (err != 0) + { + b->error = node->errors[err]; + next = SNAT_IN2OUT_NEXT_DROP; + goto out; + } + key.fib_index = rx_fib_index; + + kv.key[0] = key.as_u64[0]; + kv.key[1] = key.as_u64[1]; + + if (clib_bihash_search_16_8 (&tsm->in2out_ed, &kv, &value)) + { + if (vnet_buffer(b)->sw_if_index[VLIB_TX] != ~0) + { + if (PREDICT_FALSE(nat44_ed_not_translate_output_feature(sm, ip, + key.proto, key.l_port, key.r_port, thread_index, sw_if_index))) + { + dont_translate = 1; + goto out; + } + } + else + { + if (PREDICT_FALSE(nat44_ed_not_translate(sm, node, sw_if_index, + ip, SNAT_PROTOCOL_ICMP, rx_fib_index, thread_index))) + { + dont_translate = 1; + goto out; + } + } + + if (PREDICT_FALSE(icmp_is_error_message (icmp))) + { + b->error = node->errors[SNAT_IN2OUT_ERROR_BAD_ICMP_TYPE]; + next = SNAT_IN2OUT_NEXT_DROP; + goto out; + } + + next = slow_path_ed (sm, b, rx_fib_index, &kv, &s, node, next, + thread_index); + + if (PREDICT_FALSE (next == SNAT_IN2OUT_NEXT_DROP)) + goto out; + } + else + { + if (PREDICT_FALSE(icmp->type != ICMP4_echo_request && + icmp->type != ICMP4_echo_reply && + !icmp_is_error_message (icmp))) + { + b->error = node->errors[SNAT_IN2OUT_ERROR_BAD_ICMP_TYPE]; + next = SNAT_IN2OUT_NEXT_DROP; + goto out; + } + + s = pool_elt_at_index (tsm->sessions, value.value); + } + + *p_proto = ip_proto_to_snat_proto (key.proto); +out: + if (s) + *p_value = s->out2in; + *p_dont_translate = dont_translate; + if (d) + *(snat_session_t**)d = s; + return next; +} + +static inline void +nat44_ed_hairpinning_unknown_proto (snat_main_t *sm, + vlib_buffer_t * b, + ip4_header_t * ip) +{ + 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_static_mapping_t *m; + ip_csum_t sum; + snat_session_t *s; + snat_main_per_thread_data_t *tsm; + + if (sm->num_workers > 1) + ti = sm->worker_out2in_cb (ip, sm->outside_fib_index); + else + ti = sm->num_workers; + tsm = &sm->per_thread_data[ti]; + + old_addr = ip->dst_address.as_u32; + make_ed_kv (&s_kv, &ip->dst_address, &ip->src_address, ip->protocol, + sm->outside_fib_index, 0, 0); + if (clib_bihash_search_16_8 (&tsm->out2in_ed, &s_kv, &s_value)) + { + make_sm_kv (&kv, &ip->dst_address, 0, sm->outside_fib_index, 0); + if (clib_bihash_search_8_8 (&sm->static_mapping_by_external, &kv, &value)) + return; + + m = pool_elt_at_index (sm->static_mappings, value.value); + if (vnet_buffer(b)->sw_if_index[VLIB_TX] == ~0) + vnet_buffer(b)->sw_if_index[VLIB_TX] = m->fib_index; + new_addr = ip->dst_address.as_u32 = m->local_addr.as_u32; + } + else + { + s = pool_elt_at_index (sm->per_thread_data[ti].sessions, s_value.value); + if (vnet_buffer(b)->sw_if_index[VLIB_TX] == ~0) + vnet_buffer(b)->sw_if_index[VLIB_TX] = s->in2out.fib_index; + new_addr = ip->dst_address.as_u32 = s->in2out.addr.as_u32; + } + sum = ip->checksum; + sum = ip_csum_update (sum, old_addr, new_addr, ip4_header_t, dst_address); + ip->checksum = ip_csum_fold (sum); +} + +static snat_session_t * +nat44_ed_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_node_runtime_t * node) +{ + clib_bihash_kv_8_8_t kv, value; + clib_bihash_kv_16_8_t s_kv, s_value; + snat_static_mapping_t *m; + u32 old_addr, new_addr = 0; + ip_csum_t sum; + snat_user_t *u; + dlist_elt_t *head, *elt; + snat_main_per_thread_data_t *tsm = &sm->per_thread_data[thread_index]; + u32 elt_index, head_index, ses_index; + snat_session_t * s; + u32 address_index = ~0; + int i; + u8 is_sm = 0; + + old_addr = ip->src_address.as_u32; + + make_ed_kv (&s_kv, &ip->src_address, &ip->dst_address, ip->protocol, + rx_fib_index, 0, 0); + + if (!clib_bihash_search_16_8 (&tsm->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; + } + else + { + if (PREDICT_FALSE (maximum_sessions_exceeded(sm, thread_index))) + { + b->error = node->errors[SNAT_IN2OUT_ERROR_MAX_SESSIONS_EXCEEDED]; + nat_ipfix_logging_max_sessions(sm->max_translations); + nat_log_notice ("maximum sessions exceeded"); + return 0; + } + + u = nat_user_get_or_create (sm, &ip->src_address, rx_fib_index, + thread_index); + if (!u) + { + nat_log_warn ("create NAT user failed"); + return 0; + } + + make_sm_kv (&kv, &ip->src_address, 0, rx_fib_index, 0); + + /* Try to find static mapping first */ + if (!clib_bihash_search_8_8 (&sm->static_mapping_by_local, &kv, &value)) + { + m = pool_elt_at_index (sm->static_mappings, value.value); + new_addr = ip->src_address.as_u32 = m->external_addr.as_u32; + is_sm = 1; + goto create_ses; + } + /* Fallback to 3-tuple key */ + else + { + /* Choose same out address as for TCP/UDP session to same destination */ + head_index = u->sessions_per_user_list_head_index; + head = pool_elt_at_index (tsm->list_pool, head_index); + elt_index = head->next; + if (PREDICT_FALSE (elt_index == ~0)) + ses_index = ~0; + else + { + elt = pool_elt_at_index (tsm->list_pool, elt_index); + ses_index = elt->value; + } + + while (ses_index != ~0) + { + s = pool_elt_at_index (tsm->sessions, ses_index); + elt_index = elt->next; + elt = pool_elt_at_index (tsm->list_pool, elt_index); + ses_index = elt->value; + + if (s->ext_host_addr.as_u32 == ip->dst_address.as_u32) + { + new_addr = ip->src_address.as_u32 = s->out2in.addr.as_u32; + address_index = s->outside_address_index; + + make_ed_kv (&s_kv, &s->out2in.addr, &ip->dst_address, + ip->protocol, sm->outside_fib_index, 0, 0); + if (clib_bihash_search_16_8 (&tsm->out2in_ed, &s_kv, &s_value)) + goto create_ses; + + break; + } + } + + for (i = 0; i < vec_len (sm->addresses); i++) + { + make_ed_kv (&s_kv, &sm->addresses[i].addr, &ip->dst_address, + ip->protocol, sm->outside_fib_index, 0, 0); + if (clib_bihash_search_16_8 (&tsm->out2in_ed, &s_kv, &s_value)) + { + new_addr = ip->src_address.as_u32 = + sm->addresses[i].addr.as_u32; + address_index = i; + goto create_ses; + } + } + return 0; + } + +create_ses: + s = nat_session_alloc_or_recycle (sm, u, thread_index); + if (!s) + { + nat_log_warn ("create NAT session failed"); + return 0; + } + + s->ext_host_addr.as_u32 = ip->dst_address.as_u32; + s->flags |= SNAT_SESSION_FLAG_UNKNOWN_PROTO; + s->flags |= SNAT_SESSION_FLAG_ENDPOINT_DEPENDENT; + s->outside_address_index = address_index; + s->out2in.addr.as_u32 = new_addr; + s->out2in.fib_index = sm->outside_fib_index; + s->in2out.addr.as_u32 = old_addr; + s->in2out.fib_index = rx_fib_index; + s->in2out.port = s->out2in.port = ip->protocol; + if (is_sm) + s->flags |= SNAT_SESSION_FLAG_STATIC_MAPPING; + user_session_increment (sm, u, is_sm); + + /* Add to lookup tables */ + make_ed_kv (&s_kv, &s->in2out.addr, &ip->dst_address, ip->protocol, + rx_fib_index, 0, 0); + s_kv.value = s - tsm->sessions; + if (clib_bihash_add_del_16_8 (&tsm->in2out_ed, &s_kv, 1)) + nat_log_notice ("in2out key add failed"); + + make_ed_kv (&s_kv, &s->out2in.addr, &ip->dst_address, ip->protocol, + sm->outside_fib_index, 0, 0); + s_kv.value = s - tsm->sessions; + if (clib_bihash_add_del_16_8 (&tsm->out2in_ed, &s_kv, 1)) + nat_log_notice ("out2in key add failed"); + } + + /* 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); + + /* Accounting */ + nat44_session_update_counters (s, now, vlib_buffer_length_in_chain (vm, b)); + /* Per-user LRU list maintenance */ + nat44_session_update_lru (sm, s, thread_index); + + /* Hairpinning */ + if (vnet_buffer(b)->sw_if_index[VLIB_TX] == ~0) + nat44_ed_hairpinning_unknown_proto(sm, b, ip); + + 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 inline uword +nat44_ed_in2out_node_fn_inline (vlib_main_t * vm, + vlib_node_runtime_t * node, + vlib_frame_t * frame, int is_slow_path, + int is_output_feature) +{ + u32 n_left_from, *from, *to_next, pkts_processed = 0, stats_node_index; + snat_in2out_next_t next_index; + snat_main_t *sm = &snat_main; + f64 now = vlib_time_now (vm); + u32 thread_index = vlib_get_thread_index (); + snat_main_per_thread_data_t *tsm = &sm->per_thread_data[thread_index]; + + stats_node_index = is_slow_path ? nat44_ed_in2out_slowpath_node.index : + nat44_ed_in2out_node.index; + + from = vlib_frame_vector_args (frame); + n_left_from = frame->n_vectors; + next_index = node->cached_next_index; + + while (n_left_from > 0) + { + u32 n_left_to_next; + + vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next); + + while (n_left_from >= 4 && n_left_to_next >= 2) + { + u32 bi0, bi1; + vlib_buffer_t *b0, *b1; + u32 next0, sw_if_index0, rx_fib_index0, iph_offset0 = 0, proto0, + new_addr0, old_addr0; + u32 next1, sw_if_index1, rx_fib_index1, iph_offset1 = 0, proto1, + new_addr1, old_addr1; + u16 old_port0, new_port0, old_port1, new_port1; + ip4_header_t *ip0, *ip1; + udp_header_t *udp0, *udp1; + tcp_header_t *tcp0, *tcp1; + icmp46_header_t *icmp0, *icmp1; + snat_session_t *s0 = 0, *s1 = 0; + clib_bihash_kv_16_8_t kv0, value0, kv1, value1; + ip_csum_t sum0, sum1; + + /* Prefetch next iteration. */ + { + vlib_buffer_t * p2, * p3; + + p2 = vlib_get_buffer (vm, from[2]); + p3 = vlib_get_buffer (vm, from[3]); + + vlib_prefetch_buffer_header (p2, LOAD); + vlib_prefetch_buffer_header (p3, LOAD); + + CLIB_PREFETCH (p2->data, CLIB_CACHE_LINE_BYTES, STORE); + CLIB_PREFETCH (p3->data, CLIB_CACHE_LINE_BYTES, STORE); + } + + /* speculatively enqueue b0 and b1 to the current next frame */ + to_next[0] = bi0 = from[0]; + to_next[1] = bi1 = from[1]; + from += 2; + to_next += 2; + n_left_from -= 2; + n_left_to_next -= 2; + + b0 = vlib_get_buffer (vm, bi0); + b1 = vlib_get_buffer (vm, bi1); + + next0 = SNAT_IN2OUT_NEXT_LOOKUP; + + if (is_output_feature) + iph_offset0 = vnet_buffer (b0)->ip.save_rewrite_length; + + ip0 = (ip4_header_t *) ((u8 *) vlib_buffer_get_current (b0) + + iph_offset0); + + sw_if_index0 = vnet_buffer(b0)->sw_if_index[VLIB_RX]; + rx_fib_index0 = fib_table_get_index_for_sw_if_index (FIB_PROTOCOL_IP4, + sw_if_index0); + + if (PREDICT_FALSE(ip0->ttl == 1)) + { + vnet_buffer (b0)->sw_if_index[VLIB_TX] = (u32) ~ 0; + icmp4_error_set_vnet_buffer (b0, ICMP4_time_exceeded, + ICMP4_time_exceeded_ttl_exceeded_in_transit, + 0); + next0 = SNAT_IN2OUT_NEXT_ICMP_ERROR; + goto trace00; + } + + udp0 = ip4_next_header (ip0); + tcp0 = (tcp_header_t *) udp0; + icmp0 = (icmp46_header_t *) udp0; + proto0 = ip_proto_to_snat_proto (ip0->protocol); + + if (is_slow_path) + { + if (PREDICT_FALSE (proto0 == ~0)) + { + s0 = nat44_ed_in2out_unknown_proto (sm, b0, ip0, + rx_fib_index0, + thread_index, now, vm, + node); + if (!s0) + next0 = SNAT_IN2OUT_NEXT_DROP; + goto trace00; + } + + if (PREDICT_FALSE (proto0 == SNAT_PROTOCOL_ICMP)) + { + next0 = icmp_in2out_slow_path + (sm, b0, ip0, icmp0, sw_if_index0, rx_fib_index0, node, + next0, now, thread_index, &s0); + goto trace00; + } + } + else + { + if (is_output_feature) + { + if (PREDICT_FALSE(nat_not_translate_output_feature_fwd( + sm, ip0, thread_index, now, vm, b0))) + goto trace00; + } + + if (PREDICT_FALSE (proto0 == ~0 || proto0 == SNAT_PROTOCOL_ICMP)) + { + next0 = SNAT_IN2OUT_NEXT_SLOW_PATH; + goto trace00; + } + + if (ip4_is_fragment (ip0)) + { + b0->error = node->errors[SNAT_IN2OUT_ERROR_DROP_FRAGMENT]; + next0 = SNAT_IN2OUT_NEXT_DROP; + goto trace00; + } + } + + make_ed_kv (&kv0, &ip0->src_address, &ip0->dst_address, ip0->protocol, + rx_fib_index0, udp0->src_port, udp0->dst_port); + + if (clib_bihash_search_16_8 (&tsm->in2out_ed, &kv0, &value0)) + { + if (is_slow_path) + { + if (is_output_feature) + { + if (PREDICT_FALSE(nat44_ed_not_translate_output_feature( + sm, ip0, ip0->protocol, udp0->src_port, + udp0->dst_port, thread_index, sw_if_index0))) + goto trace00; + } + else + { + if (PREDICT_FALSE(nat44_ed_not_translate(sm, node, + sw_if_index0, ip0, proto0, rx_fib_index0, + thread_index))) + goto trace00; + } + + next0 = slow_path_ed (sm, b0, rx_fib_index0, &kv0, &s0, node, + next0, thread_index); + + if (PREDICT_FALSE (next0 == SNAT_IN2OUT_NEXT_DROP)) + goto trace00; + } + else + { + next0 = SNAT_IN2OUT_NEXT_SLOW_PATH; + goto trace00; + } + } + else + { + s0 = pool_elt_at_index (tsm->sessions, value0.value); + } + + b0->flags |= VNET_BUFFER_F_IS_NATED; + + if (!is_output_feature) + vnet_buffer(b0)->sw_if_index[VLIB_TX] = s0->out2in.fib_index; + + old_addr0 = ip0->src_address.as_u32; + new_addr0 = ip0->src_address.as_u32 = s0->out2in.addr.as_u32; + sum0 = ip0->checksum; + sum0 = ip_csum_update (sum0, old_addr0, new_addr0, ip4_header_t, + src_address); + if (PREDICT_FALSE (is_twice_nat_session (s0))) + sum0 = ip_csum_update (sum0, ip0->dst_address.as_u32, + s0->ext_host_addr.as_u32, ip4_header_t, + dst_address); + ip0->checksum = ip_csum_fold (sum0); + + if (PREDICT_TRUE (proto0 == SNAT_PROTOCOL_TCP)) + { + old_port0 = tcp0->src_port; + new_port0 = tcp0->src_port = s0->out2in.port; + + sum0 = tcp0->checksum; + sum0 = ip_csum_update (sum0, old_addr0, new_addr0, ip4_header_t, + dst_address); + sum0 = ip_csum_update (sum0, old_port0, new_port0, ip4_header_t, + length); + if (PREDICT_FALSE (is_twice_nat_session (s0))) + { + sum0 = ip_csum_update (sum0, ip0->dst_address.as_u32, + s0->ext_host_addr.as_u32, + ip4_header_t, dst_address); + sum0 = ip_csum_update (sum0, tcp0->dst_port, + s0->ext_host_port, ip4_header_t, + length); + tcp0->dst_port = s0->ext_host_port; + ip0->dst_address.as_u32 = s0->ext_host_addr.as_u32; + } + tcp0->checksum = ip_csum_fold(sum0); + if (nat44_set_tcp_session_state_i2o (sm, s0, tcp0, thread_index)) + goto trace00; + } + else + { + udp0->src_port = s0->out2in.port; + udp0->checksum = 0; + if (PREDICT_FALSE (is_twice_nat_session (s0))) + { + udp0->dst_port = s0->ext_host_port; + ip0->dst_address.as_u32 = s0->ext_host_addr.as_u32; + } + } + + /* Accounting */ + nat44_session_update_counters (s0, now, + vlib_buffer_length_in_chain (vm, b0)); + /* Per-user LRU list maintenance */ + nat44_session_update_lru (sm, s0, thread_index); + + trace00: + if (PREDICT_FALSE((node->flags & VLIB_NODE_FLAG_TRACE) + && (b0->flags & VLIB_BUFFER_IS_TRACED))) + { + snat_in2out_trace_t *t = + vlib_add_trace (vm, node, b0, sizeof (*t)); + t->is_slow_path = is_slow_path; + t->sw_if_index = sw_if_index0; + t->next_index = next0; + t->session_index = ~0; + if (s0) + t->session_index = s0 - tsm->sessions; + } + + pkts_processed += next0 != SNAT_IN2OUT_NEXT_DROP; + + + next1 = SNAT_IN2OUT_NEXT_LOOKUP; + + if (is_output_feature) + iph_offset1 = vnet_buffer (b1)->ip.save_rewrite_length; + + ip1 = (ip4_header_t *) ((u8 *) vlib_buffer_get_current (b1) + + iph_offset1); + + sw_if_index1 = vnet_buffer(b1)->sw_if_index[VLIB_RX]; + rx_fib_index1 = fib_table_get_index_for_sw_if_index (FIB_PROTOCOL_IP4, + sw_if_index1); + + if (PREDICT_FALSE(ip1->ttl == 1)) + { + vnet_buffer (b1)->sw_if_index[VLIB_TX] = (u32) ~ 0; + icmp4_error_set_vnet_buffer (b1, ICMP4_time_exceeded, + ICMP4_time_exceeded_ttl_exceeded_in_transit, + 0); + next1 = SNAT_IN2OUT_NEXT_ICMP_ERROR; + goto trace01; + } + + udp1 = ip4_next_header (ip1); + tcp1 = (tcp_header_t *) udp1; + icmp1 = (icmp46_header_t *) udp1; + proto1 = ip_proto_to_snat_proto (ip1->protocol); + + if (is_slow_path) + { + if (PREDICT_FALSE (proto1 == ~0)) + { + s1 = nat44_ed_in2out_unknown_proto (sm, b1, ip1, + rx_fib_index1, + thread_index, now, vm, + node); + if (!s1) + next1 = SNAT_IN2OUT_NEXT_DROP; + goto trace01; + } + + if (PREDICT_FALSE (proto1 == SNAT_PROTOCOL_ICMP)) + { + next1 = icmp_in2out_slow_path + (sm, b1, ip1, icmp1, sw_if_index1, rx_fib_index1, node, + next1, now, thread_index, &s1); + goto trace01; + } + } + else + { + if (is_output_feature) + { + if (PREDICT_FALSE(nat_not_translate_output_feature_fwd( + sm, ip1, thread_index, now, vm, b1))) + goto trace01; + } + + if (PREDICT_FALSE (proto1 == ~0 || proto1 == SNAT_PROTOCOL_ICMP)) + { + next1 = SNAT_IN2OUT_NEXT_SLOW_PATH; + goto trace01; + } + + if (ip4_is_fragment (ip1)) + { + b1->error = node->errors[SNAT_IN2OUT_ERROR_DROP_FRAGMENT]; + next1 = SNAT_IN2OUT_NEXT_DROP; + goto trace01; + } + } + + make_ed_kv (&kv1, &ip1->src_address, &ip1->dst_address, ip1->protocol, + rx_fib_index1, udp1->src_port, udp1->dst_port); + + if (clib_bihash_search_16_8 (&tsm->in2out_ed, &kv1, &value1)) + { + if (is_slow_path) + { + if (is_output_feature) + { + if (PREDICT_FALSE(nat44_ed_not_translate_output_feature( + sm, ip1, ip1->protocol, udp1->src_port, + udp1->dst_port, thread_index, sw_if_index1))) + goto trace01; + } + else + { + if (PREDICT_FALSE(nat44_ed_not_translate(sm, node, + sw_if_index1, ip1, proto1, rx_fib_index1, + thread_index))) + goto trace01; + } + + next1 = slow_path_ed (sm, b1, rx_fib_index1, &kv1, &s1, node, + next1, thread_index); + + if (PREDICT_FALSE (next1 == SNAT_IN2OUT_NEXT_DROP)) + goto trace01; + } + else + { + next1 = SNAT_IN2OUT_NEXT_SLOW_PATH; + goto trace01; + } + } + else + { + s1 = pool_elt_at_index (tsm->sessions, value1.value); + } + + b1->flags |= VNET_BUFFER_F_IS_NATED; + + if (!is_output_feature) + vnet_buffer(b1)->sw_if_index[VLIB_TX] = s1->out2in.fib_index; + + old_addr1 = ip1->src_address.as_u32; + new_addr1 = ip1->src_address.as_u32 = s1->out2in.addr.as_u32; + sum1 = ip1->checksum; + sum1 = ip_csum_update (sum1, old_addr1, new_addr1, ip4_header_t, + src_address); + if (PREDICT_FALSE (is_twice_nat_session (s1))) + sum1 = ip_csum_update (sum1, ip1->dst_address.as_u32, + s1->ext_host_addr.as_u32, ip4_header_t, + dst_address); + ip1->checksum = ip_csum_fold (sum1); + + if (PREDICT_TRUE (proto1 == SNAT_PROTOCOL_TCP)) + { + old_port1 = tcp1->src_port; + new_port1 = tcp1->src_port = s1->out2in.port; + + sum1 = tcp1->checksum; + sum1 = ip_csum_update (sum1, old_addr1, new_addr1, ip4_header_t, + dst_address); + sum1 = ip_csum_update (sum1, old_port1, new_port1, ip4_header_t, + length); + if (PREDICT_FALSE (is_twice_nat_session (s1))) + { + sum1 = ip_csum_update (sum1, ip1->dst_address.as_u32, + s1->ext_host_addr.as_u32, + ip4_header_t, dst_address); + sum1 = ip_csum_update (sum1, tcp1->dst_port, + s1->ext_host_port, ip4_header_t, + length); + tcp1->dst_port = s1->ext_host_port; + ip1->dst_address.as_u32 = s1->ext_host_addr.as_u32; + } + tcp1->checksum = ip_csum_fold(sum1); + if (nat44_set_tcp_session_state_i2o (sm, s1, tcp1, thread_index)) + goto trace01; + } + else + { + udp1->src_port = s1->out2in.port; + udp1->checksum = 0; + if (PREDICT_FALSE (is_twice_nat_session (s1))) + { + udp1->dst_port = s1->ext_host_port; + ip1->dst_address.as_u32 = s1->ext_host_addr.as_u32; + } + } + + /* Accounting */ + nat44_session_update_counters (s1, now, + vlib_buffer_length_in_chain (vm, b1)); + /* Per-user LRU list maintenance */ + nat44_session_update_lru (sm, s1, thread_index); + + trace01: + if (PREDICT_FALSE((node->flags & VLIB_NODE_FLAG_TRACE) + && (b1->flags & VLIB_BUFFER_IS_TRACED))) + { + snat_in2out_trace_t *t = + vlib_add_trace (vm, node, b1, sizeof (*t)); + t->is_slow_path = is_slow_path; + t->sw_if_index = sw_if_index1; + t->next_index = next1; + t->session_index = ~0; + if (s1) + t->session_index = s1 - tsm->sessions; + } + + pkts_processed += next1 != SNAT_IN2OUT_NEXT_DROP; + + /* verify speculative enqueues, maybe switch current next frame */ + vlib_validate_buffer_enqueue_x2 (vm, node, next_index, + to_next, n_left_to_next, + bi0, bi1, next0, next1); + } + + while (n_left_from > 0 && n_left_to_next > 0) + { + u32 bi0; + vlib_buffer_t *b0; + u32 next0, sw_if_index0, rx_fib_index0, iph_offset0 = 0, proto0, + new_addr0, old_addr0; + u16 old_port0, new_port0; + ip4_header_t *ip0; + udp_header_t *udp0; + tcp_header_t *tcp0; + icmp46_header_t * icmp0; + snat_session_t *s0 = 0; + clib_bihash_kv_16_8_t kv0, value0; + ip_csum_t sum0; + + /* speculatively enqueue b0 to the current next frame */ + bi0 = from[0]; + to_next[0] = bi0; + from += 1; + to_next += 1; + n_left_from -= 1; + n_left_to_next -= 1; + + b0 = vlib_get_buffer (vm, bi0); + next0 = SNAT_IN2OUT_NEXT_LOOKUP; + + if (is_output_feature) + iph_offset0 = vnet_buffer (b0)->ip.save_rewrite_length; + + ip0 = (ip4_header_t *) ((u8 *) vlib_buffer_get_current (b0) + + iph_offset0); + + sw_if_index0 = vnet_buffer(b0)->sw_if_index[VLIB_RX]; + rx_fib_index0 = fib_table_get_index_for_sw_if_index (FIB_PROTOCOL_IP4, + sw_if_index0); + + if (PREDICT_FALSE(ip0->ttl == 1)) + { + vnet_buffer (b0)->sw_if_index[VLIB_TX] = (u32) ~ 0; + icmp4_error_set_vnet_buffer (b0, ICMP4_time_exceeded, + ICMP4_time_exceeded_ttl_exceeded_in_transit, + 0); + next0 = SNAT_IN2OUT_NEXT_ICMP_ERROR; + goto trace0; + } + + udp0 = ip4_next_header (ip0); + tcp0 = (tcp_header_t *) udp0; + icmp0 = (icmp46_header_t *) udp0; + proto0 = ip_proto_to_snat_proto (ip0->protocol); + + if (is_slow_path) + { + if (PREDICT_FALSE (proto0 == ~0)) + { + s0 = nat44_ed_in2out_unknown_proto (sm, b0, ip0, + rx_fib_index0, + thread_index, now, vm, + node); + if (!s0) + next0 = SNAT_IN2OUT_NEXT_DROP; + goto trace0; + } + + if (PREDICT_FALSE (proto0 == SNAT_PROTOCOL_ICMP)) + { + next0 = icmp_in2out_slow_path + (sm, b0, ip0, icmp0, sw_if_index0, rx_fib_index0, node, + next0, now, thread_index, &s0); + goto trace0; + } + } + else + { + if (is_output_feature) + { + if (PREDICT_FALSE(nat_not_translate_output_feature_fwd( + sm, ip0, thread_index, now, vm, b0))) + goto trace0; + } + + if (PREDICT_FALSE (proto0 == ~0 || proto0 == SNAT_PROTOCOL_ICMP)) + { + next0 = SNAT_IN2OUT_NEXT_SLOW_PATH; + goto trace0; + } + + if (ip4_is_fragment (ip0)) + { + b0->error = node->errors[SNAT_IN2OUT_ERROR_DROP_FRAGMENT]; + next0 = SNAT_IN2OUT_NEXT_DROP; + goto trace0; + } + } + + make_ed_kv (&kv0, &ip0->src_address, &ip0->dst_address, ip0->protocol, + rx_fib_index0, udp0->src_port, udp0->dst_port); + + if (clib_bihash_search_16_8 (&tsm->in2out_ed, &kv0, &value0)) + { + if (is_slow_path) + { + if (is_output_feature) + { + if (PREDICT_FALSE(nat44_ed_not_translate_output_feature( + sm, ip0, ip0->protocol, udp0->src_port, + udp0->dst_port, thread_index, sw_if_index0))) + goto trace0; + } + else + { + if (PREDICT_FALSE(nat44_ed_not_translate(sm, node, + sw_if_index0, ip0, proto0, rx_fib_index0, + thread_index))) + goto trace0; + } + + next0 = slow_path_ed (sm, b0, rx_fib_index0, &kv0, &s0, node, + next0, thread_index); + + if (PREDICT_FALSE (next0 == SNAT_IN2OUT_NEXT_DROP)) + goto trace0; + } + else + { + next0 = SNAT_IN2OUT_NEXT_SLOW_PATH; + goto trace0; + } + } + else + { + s0 = pool_elt_at_index (tsm->sessions, value0.value); + } + + b0->flags |= VNET_BUFFER_F_IS_NATED; + + if (!is_output_feature) + vnet_buffer(b0)->sw_if_index[VLIB_TX] = s0->out2in.fib_index; + old_addr0 = ip0->src_address.as_u32; + new_addr0 = ip0->src_address.as_u32 = s0->out2in.addr.as_u32; sum0 = ip0->checksum; - sum0 = ip_csum_update (sum0, old_addr0, new_addr0, - ip4_header_t, - src_address /* changed member */); + sum0 = ip_csum_update (sum0, old_addr0, new_addr0, ip4_header_t, + src_address); + if (PREDICT_FALSE (is_twice_nat_session (s0))) + sum0 = ip_csum_update (sum0, ip0->dst_address.as_u32, + s0->ext_host_addr.as_u32, ip4_header_t, + dst_address); ip0->checksum = ip_csum_fold (sum0); - if (PREDICT_FALSE (ip4_is_first_fragment (ip0))) + if (PREDICT_TRUE (proto0 == SNAT_PROTOCOL_TCP)) { - if (PREDICT_TRUE(proto0 == SNAT_PROTOCOL_TCP)) - { - old_port0 = tcp0->src_port; - tcp0->src_port = s0->out2in.port; - new_port0 = tcp0->src_port; + old_port0 = tcp0->src_port; + new_port0 = tcp0->src_port = s0->out2in.port; - sum0 = tcp0->checksum; - sum0 = ip_csum_update (sum0, old_addr0, new_addr0, - ip4_header_t, - dst_address /* changed member */); - sum0 = ip_csum_update (sum0, old_port0, new_port0, - ip4_header_t /* cheat */, - length /* changed member */); - tcp0->checksum = ip_csum_fold(sum0); + sum0 = tcp0->checksum; + sum0 = ip_csum_update (sum0, old_addr0, new_addr0, ip4_header_t, + dst_address); + sum0 = ip_csum_update (sum0, old_port0, new_port0, ip4_header_t, + length); + if (PREDICT_FALSE (is_twice_nat_session (s0))) + { + sum0 = ip_csum_update (sum0, ip0->dst_address.as_u32, + s0->ext_host_addr.as_u32, + ip4_header_t, dst_address); + sum0 = ip_csum_update (sum0, tcp0->dst_port, + s0->ext_host_port, ip4_header_t, + length); + tcp0->dst_port = s0->ext_host_port; + ip0->dst_address.as_u32 = s0->ext_host_addr.as_u32; } - else + tcp0->checksum = ip_csum_fold(sum0); + if (nat44_set_tcp_session_state_i2o (sm, s0, tcp0, thread_index)) + goto trace0; + } + else + { + udp0->src_port = s0->out2in.port; + udp0->checksum = 0; + if (PREDICT_FALSE (is_twice_nat_session (s0))) { - old_port0 = udp0->src_port; - udp0->src_port = s0->out2in.port; - udp0->checksum = 0; + udp0->dst_port = s0->ext_host_port; + ip0->dst_address.as_u32 = s0->ext_host_addr.as_u32; } } - /* Hairpinning */ - nat44_reass_hairpinning (sm, b0, ip0, s0->out2in.port, - s0->ext_host_port, proto0); - /* Accounting */ nat44_session_update_counters (s0, now, vlib_buffer_length_in_chain (vm, b0)); @@ -2689,87 +3511,171 @@ nat44_in2out_reass_node_fn (vlib_main_t * vm, if (PREDICT_FALSE((node->flags & VLIB_NODE_FLAG_TRACE) && (b0->flags & VLIB_BUFFER_IS_TRACED))) { - nat44_in2out_reass_trace_t *t = - vlib_add_trace (vm, node, b0, sizeof (*t)); - t->cached = cached0; + snat_in2out_trace_t *t = + vlib_add_trace (vm, node, b0, sizeof (*t)); + t->is_slow_path = is_slow_path; t->sw_if_index = sw_if_index0; t->next_index = next0; + t->session_index = ~0; + if (s0) + t->session_index = s0 - tsm->sessions; } - if (cached0) - { - n_left_to_next++; - to_next--; - } - else - { - pkts_processed += next0 != SNAT_IN2OUT_NEXT_DROP; - - /* verify speculative enqueue, maybe switch current next frame */ - vlib_validate_buffer_enqueue_x1 (vm, node, next_index, - to_next, n_left_to_next, - bi0, next0); - } + pkts_processed += next0 != SNAT_IN2OUT_NEXT_DROP; - if (n_left_from == 0 && vec_len (fragments_to_loopback)) - { - from = vlib_frame_vector_args (frame); - u32 len = vec_len (fragments_to_loopback); - if (len <= VLIB_FRAME_SIZE) - { - clib_memcpy (from, fragments_to_loopback, sizeof (u32) * len); - n_left_from = len; - vec_reset_length (fragments_to_loopback); - } - else - { - clib_memcpy (from, - fragments_to_loopback + (len - VLIB_FRAME_SIZE), - sizeof (u32) * VLIB_FRAME_SIZE); - n_left_from = VLIB_FRAME_SIZE; - _vec_len (fragments_to_loopback) = len - VLIB_FRAME_SIZE; - } - } - } + /* verify speculative enqueue, maybe switch current next frame */ + vlib_validate_buffer_enqueue_x1 (vm, node, next_index, + to_next, n_left_to_next, + bi0, next0); + } vlib_put_next_frame (vm, node, next_index, n_left_to_next); } - vlib_node_increment_counter (vm, nat44_in2out_reass_node.index, + vlib_node_increment_counter (vm, stats_node_index, SNAT_IN2OUT_ERROR_IN2OUT_PACKETS, pkts_processed); + return frame->n_vectors; +} - nat_send_all_to_node (vm, fragments_to_drop, node, - &node->errors[SNAT_IN2OUT_ERROR_DROP_FRAGMENT], - SNAT_IN2OUT_NEXT_DROP); +static uword +nat44_ed_in2out_fast_path_fn (vlib_main_t * vm, + vlib_node_runtime_t * node, + vlib_frame_t * frame) +{ + return nat44_ed_in2out_node_fn_inline (vm, node, frame, 0, 0); +} - vec_free (fragments_to_drop); - vec_free (fragments_to_loopback); - return frame->n_vectors; +VLIB_REGISTER_NODE (nat44_ed_in2out_node) = { + .function = nat44_ed_in2out_fast_path_fn, + .name = "nat44-ed-in2out", + .vector_size = sizeof (u32), + .format_trace = format_snat_in2out_trace, + .type = VLIB_NODE_TYPE_INTERNAL, + + .n_errors = ARRAY_LEN(snat_in2out_error_strings), + .error_strings = snat_in2out_error_strings, + + .runtime_data_bytes = sizeof (snat_runtime_t), + + .n_next_nodes = SNAT_IN2OUT_N_NEXT, + + /* edit / add dispositions here */ + .next_nodes = { + [SNAT_IN2OUT_NEXT_DROP] = "error-drop", + [SNAT_IN2OUT_NEXT_LOOKUP] = "ip4-lookup", + [SNAT_IN2OUT_NEXT_SLOW_PATH] = "nat44-ed-in2out-slowpath", + [SNAT_IN2OUT_NEXT_ICMP_ERROR] = "ip4-icmp-error", + [SNAT_IN2OUT_NEXT_REASS] = "nat44-in2out-reass", + }, +}; + +VLIB_NODE_FUNCTION_MULTIARCH (nat44_ed_in2out_node, nat44_ed_in2out_fast_path_fn); + +static uword +nat44_ed_in2out_output_fast_path_fn (vlib_main_t * vm, + vlib_node_runtime_t * node, + vlib_frame_t * frame) +{ + return nat44_ed_in2out_node_fn_inline (vm, node, frame, 0, 1); } -VLIB_REGISTER_NODE (nat44_in2out_reass_node) = { - .function = nat44_in2out_reass_node_fn, - .name = "nat44-in2out-reass", +VLIB_REGISTER_NODE (nat44_ed_in2out_output_node) = { + .function = nat44_ed_in2out_output_fast_path_fn, + .name = "nat44-ed-in2out-output", .vector_size = sizeof (u32), - .format_trace = format_nat44_in2out_reass_trace, + .format_trace = format_snat_in2out_trace, + .type = VLIB_NODE_TYPE_INTERNAL, + + .n_errors = ARRAY_LEN(snat_in2out_error_strings), + .error_strings = snat_in2out_error_strings, + + .runtime_data_bytes = sizeof (snat_runtime_t), + + .n_next_nodes = SNAT_IN2OUT_N_NEXT, + + /* edit / add dispositions here */ + .next_nodes = { + [SNAT_IN2OUT_NEXT_DROP] = "error-drop", + [SNAT_IN2OUT_NEXT_LOOKUP] = "interface-output", + [SNAT_IN2OUT_NEXT_SLOW_PATH] = "nat44-ed-in2out-output-slowpath", + [SNAT_IN2OUT_NEXT_ICMP_ERROR] = "ip4-icmp-error", + [SNAT_IN2OUT_NEXT_REASS] = "nat44-in2out-reass", + }, +}; + +VLIB_NODE_FUNCTION_MULTIARCH (nat44_ed_in2out_output_node, + nat44_ed_in2out_output_fast_path_fn); + +static uword +nat44_ed_in2out_slow_path_fn (vlib_main_t * vm, + vlib_node_runtime_t * node, + vlib_frame_t * frame) +{ + return nat44_ed_in2out_node_fn_inline (vm, node, frame, 1, 0); +} + +VLIB_REGISTER_NODE (nat44_ed_in2out_slowpath_node) = { + .function = nat44_ed_in2out_slow_path_fn, + .name = "nat44-ed-in2out-slowpath", + .vector_size = sizeof (u32), + .format_trace = format_snat_in2out_trace, .type = VLIB_NODE_TYPE_INTERNAL, .n_errors = ARRAY_LEN(snat_in2out_error_strings), .error_strings = snat_in2out_error_strings, + .runtime_data_bytes = sizeof (snat_runtime_t), + .n_next_nodes = SNAT_IN2OUT_N_NEXT, + + /* edit / add dispositions here */ .next_nodes = { [SNAT_IN2OUT_NEXT_DROP] = "error-drop", [SNAT_IN2OUT_NEXT_LOOKUP] = "ip4-lookup", - [SNAT_IN2OUT_NEXT_SLOW_PATH] = "nat44-in2out-slowpath", + [SNAT_IN2OUT_NEXT_SLOW_PATH] = "nat44-ed-in2out-slowpath", [SNAT_IN2OUT_NEXT_ICMP_ERROR] = "ip4-icmp-error", [SNAT_IN2OUT_NEXT_REASS] = "nat44-in2out-reass", }, }; -VLIB_NODE_FUNCTION_MULTIARCH (nat44_in2out_reass_node, - nat44_in2out_reass_node_fn); +VLIB_NODE_FUNCTION_MULTIARCH (nat44_ed_in2out_slowpath_node, + nat44_ed_in2out_slow_path_fn); + +static uword +nat44_ed_in2out_output_slow_path_fn (vlib_main_t * vm, + vlib_node_runtime_t * node, + vlib_frame_t * frame) +{ + return nat44_ed_in2out_node_fn_inline (vm, node, frame, 1, 1); +} + +VLIB_REGISTER_NODE (nat44_ed_in2out_output_slowpath_node) = { + .function = nat44_ed_in2out_output_slow_path_fn, + .name = "nat44-ed-in2out-output-slowpath", + .vector_size = sizeof (u32), + .format_trace = format_snat_in2out_trace, + .type = VLIB_NODE_TYPE_INTERNAL, + + .n_errors = ARRAY_LEN(snat_in2out_error_strings), + .error_strings = snat_in2out_error_strings, + + .runtime_data_bytes = sizeof (snat_runtime_t), + + .n_next_nodes = SNAT_IN2OUT_N_NEXT, + + /* edit / add dispositions here */ + .next_nodes = { + [SNAT_IN2OUT_NEXT_DROP] = "error-drop", + [SNAT_IN2OUT_NEXT_LOOKUP] = "interface-output", + [SNAT_IN2OUT_NEXT_SLOW_PATH] = "nat44-ed-in2out-output-slowpath", + [SNAT_IN2OUT_NEXT_ICMP_ERROR] = "ip4-icmp-error", + [SNAT_IN2OUT_NEXT_REASS] = "nat44-in2out-reass", + }, +}; + +VLIB_NODE_FUNCTION_MULTIARCH (nat44_ed_in2out_output_slowpath_node, + nat44_ed_in2out_output_slow_path_fn); /**************************/ /*** deterministic mode ***/ @@ -3791,16 +4697,20 @@ is_hairpinning (snat_main_t *sm, ip4_address_t * dst_addr) return 0; } -static uword -snat_hairpin_dst_fn (vlib_main_t * vm, - vlib_node_runtime_t * node, - vlib_frame_t * frame) +static inline uword +snat_hairpin_dst_fn_inline (vlib_main_t * vm, + vlib_node_runtime_t * node, + vlib_frame_t * frame, + int is_ed) { - u32 n_left_from, * from, * to_next; + u32 n_left_from, * from, * to_next, stats_node_index; snat_in2out_next_t next_index; u32 pkts_processed = 0; snat_main_t * sm = &snat_main; + stats_node_index = is_ed ? nat44_ed_hairpin_dst_node.index : + snat_hairpin_dst_node.index; + from = vlib_frame_vector_args (frame); n_left_from = frame->n_vectors; next_index = node->cached_next_index; @@ -3842,17 +4752,20 @@ snat_hairpin_dst_fn (vlib_main_t * vm, udp_header_t * udp0 = ip4_next_header (ip0); tcp_header_t * tcp0 = (tcp_header_t *) udp0; - snat_hairpinning (sm, b0, ip0, udp0, tcp0, proto0); + snat_hairpinning (sm, b0, ip0, udp0, tcp0, proto0, is_ed); } else if (proto0 == SNAT_PROTOCOL_ICMP) { icmp46_header_t * icmp0 = ip4_next_header (ip0); - snat_icmp_hairpinning (sm, b0, ip0, icmp0); + snat_icmp_hairpinning (sm, b0, ip0, icmp0, is_ed); } else { - snat_hairpinning_unknown_proto (sm, b0, ip0); + if (is_ed) + nat44_ed_hairpinning_unknown_proto (sm, b0, ip0); + else + nat_hairpinning_sm_unknown_proto (sm, b0, ip0); } vnet_buffer (b0)->snat.flags = SNAT_FLAG_HAIRPINNING; @@ -3869,12 +4782,20 @@ snat_hairpin_dst_fn (vlib_main_t * vm, vlib_put_next_frame (vm, node, next_index, n_left_to_next); } - vlib_node_increment_counter (vm, snat_hairpin_dst_node.index, + vlib_node_increment_counter (vm, stats_node_index, SNAT_IN2OUT_ERROR_IN2OUT_PACKETS, pkts_processed); return frame->n_vectors; } +static uword +snat_hairpin_dst_fn (vlib_main_t * vm, + vlib_node_runtime_t * node, + vlib_frame_t * frame) +{ + return snat_hairpin_dst_fn_inline (vm, node, frame, 0); +} + VLIB_REGISTER_NODE (snat_hairpin_dst_node) = { .function = snat_hairpin_dst_fn, .name = "nat44-hairpin-dst", @@ -3893,15 +4814,44 @@ VLIB_NODE_FUNCTION_MULTIARCH (snat_hairpin_dst_node, snat_hairpin_dst_fn); static uword -snat_hairpin_src_fn (vlib_main_t * vm, - vlib_node_runtime_t * node, - vlib_frame_t * frame) +nat44_ed_hairpin_dst_fn (vlib_main_t * vm, + vlib_node_runtime_t * node, + vlib_frame_t * frame) { - u32 n_left_from, * from, * to_next; + return snat_hairpin_dst_fn_inline (vm, node, frame, 1); +} + +VLIB_REGISTER_NODE (nat44_ed_hairpin_dst_node) = { + .function = nat44_ed_hairpin_dst_fn, + .name = "nat44-ed-hairpin-dst", + .vector_size = sizeof (u32), + .type = VLIB_NODE_TYPE_INTERNAL, + .n_errors = ARRAY_LEN(snat_in2out_error_strings), + .error_strings = snat_in2out_error_strings, + .n_next_nodes = 2, + .next_nodes = { + [SNAT_IN2OUT_NEXT_DROP] = "error-drop", + [SNAT_IN2OUT_NEXT_LOOKUP] = "ip4-lookup", + }, +}; + +VLIB_NODE_FUNCTION_MULTIARCH (nat44_ed_hairpin_dst_node, + nat44_ed_hairpin_dst_fn); + +static inline uword +snat_hairpin_src_fn_inline (vlib_main_t * vm, + vlib_node_runtime_t * node, + vlib_frame_t * frame, + int is_ed) +{ + u32 n_left_from, * from, * to_next, stats_node_index; snat_in2out_next_t next_index; u32 pkts_processed = 0; snat_main_t *sm = &snat_main; + stats_node_index = is_ed ? nat44_ed_hairpin_src_node.index : + snat_hairpin_src_node.index; + from = vlib_frame_vector_args (frame); n_left_from = frame->n_vectors; next_index = node->cached_next_index; @@ -3961,12 +4911,20 @@ snat_hairpin_src_fn (vlib_main_t * vm, vlib_put_next_frame (vm, node, next_index, n_left_to_next); } - vlib_node_increment_counter (vm, snat_hairpin_src_node.index, + vlib_node_increment_counter (vm, stats_node_index, SNAT_IN2OUT_ERROR_IN2OUT_PACKETS, pkts_processed); return frame->n_vectors; } +static uword +snat_hairpin_src_fn (vlib_main_t * vm, + vlib_node_runtime_t * node, + vlib_frame_t * frame) +{ + return snat_hairpin_src_fn_inline (vm, node, frame, 0); +} + VLIB_REGISTER_NODE (snat_hairpin_src_node) = { .function = snat_hairpin_src_fn, .name = "nat44-hairpin-src", @@ -3986,6 +4944,33 @@ VLIB_REGISTER_NODE (snat_hairpin_src_node) = { VLIB_NODE_FUNCTION_MULTIARCH (snat_hairpin_src_node, snat_hairpin_src_fn); +static uword +nat44_ed_hairpin_src_fn (vlib_main_t * vm, + vlib_node_runtime_t * node, + vlib_frame_t * frame) +{ + return snat_hairpin_src_fn_inline (vm, node, frame, 1); +} + +VLIB_REGISTER_NODE (nat44_ed_hairpin_src_node) = { + .function = nat44_ed_hairpin_src_fn, + .name = "nat44-ed-hairpin-src", + .vector_size = sizeof (u32), + .type = VLIB_NODE_TYPE_INTERNAL, + .n_errors = ARRAY_LEN(snat_in2out_error_strings), + .error_strings = snat_in2out_error_strings, + .n_next_nodes = SNAT_HAIRPIN_SRC_N_NEXT, + .next_nodes = { + [SNAT_HAIRPIN_SRC_NEXT_DROP] = "error-drop", + [SNAT_HAIRPIN_SRC_NEXT_SNAT_IN2OUT] = "nat44-ed-in2out-output", + [SNAT_HAIRPIN_SRC_NEXT_INTERFACE_OUTPUT] = "interface-output", + [SNAT_HAIRPIN_SRC_NEXT_SNAT_IN2OUT_WH] = "nat44-in2out-output-worker-handoff", + }, +}; + +VLIB_NODE_FUNCTION_MULTIARCH (nat44_ed_hairpin_src_node, + nat44_ed_hairpin_src_fn); + static uword snat_in2out_fast_static_map_fn (vlib_main_t * vm, vlib_node_runtime_t * node, @@ -4128,7 +5113,7 @@ snat_in2out_fast_static_map_fn (vlib_main_t * vm, } /* Hairpinning */ - snat_hairpinning (sm, b0, ip0, udp0, tcp0, proto0); + snat_hairpinning (sm, b0, ip0, udp0, tcp0, proto0, 0); trace0: if (PREDICT_FALSE((node->flags & VLIB_NODE_FLAG_TRACE) diff --git a/src/plugins/nat/nat.c b/src/plugins/nat/nat.c index 5953c42f0fd..f236568db28 100755 --- a/src/plugins/nat/nat.c +++ b/src/plugins/nat/nat.c @@ -45,7 +45,7 @@ VNET_FEATURE_INIT (ip4_snat_in2out, static) = { VNET_FEATURE_INIT (ip4_snat_out2in, static) = { .arc_name = "ip4-unicast", .node_name = "nat44-out2in", - .runs_after = VNET_FEATURES ("acl-plugin-in-ip4-fa", + .runs_after = VNET_FEATURES ("acl-plugin-in-ip4-fa", "ip4-dhcp-client-detect"), }; VNET_FEATURE_INIT (ip4_nat_classify, static) = { @@ -61,13 +61,30 @@ VNET_FEATURE_INIT (ip4_snat_det_in2out, static) = { VNET_FEATURE_INIT (ip4_snat_det_out2in, static) = { .arc_name = "ip4-unicast", .node_name = "nat44-det-out2in", - .runs_after = VNET_FEATURES ("acl-plugin-in-ip4-fa"), + .runs_after = VNET_FEATURES ("acl-plugin-in-ip4-fa", + "ip4-dhcp-client-detect"), }; VNET_FEATURE_INIT (ip4_nat_det_classify, static) = { .arc_name = "ip4-unicast", .node_name = "nat44-det-classify", .runs_after = VNET_FEATURES ("acl-plugin-in-ip4-fa"), }; +VNET_FEATURE_INIT (ip4_nat44_ed_in2out, static) = { + .arc_name = "ip4-unicast", + .node_name = "nat44-ed-in2out", + .runs_after = VNET_FEATURES ("acl-plugin-in-ip4-fa"), +}; +VNET_FEATURE_INIT (ip4_nat44_ed_out2in, static) = { + .arc_name = "ip4-unicast", + .node_name = "nat44-ed-out2in", + .runs_after = VNET_FEATURES ("acl-plugin-in-ip4-fa", + "ip4-dhcp-client-detect"), +}; +VNET_FEATURE_INIT (ip4_nat44_ed_classify, static) = { + .arc_name = "ip4-unicast", + .node_name = "nat44-ed-classify", + .runs_after = VNET_FEATURES ("acl-plugin-in-ip4-fa"), +}; VNET_FEATURE_INIT (ip4_snat_in2out_worker_handoff, static) = { .arc_name = "ip4-unicast", .node_name = "nat44-in2out-worker-handoff", @@ -76,7 +93,8 @@ VNET_FEATURE_INIT (ip4_snat_in2out_worker_handoff, static) = { VNET_FEATURE_INIT (ip4_snat_out2in_worker_handoff, static) = { .arc_name = "ip4-unicast", .node_name = "nat44-out2in-worker-handoff", - .runs_after = VNET_FEATURES ("acl-plugin-in-ip4-fa"), + .runs_after = VNET_FEATURES ("acl-plugin-in-ip4-fa", + "ip4-dhcp-client-detect"), }; VNET_FEATURE_INIT (ip4_nat_handoff_classify, static) = { .arc_name = "ip4-unicast", @@ -91,13 +109,19 @@ VNET_FEATURE_INIT (ip4_snat_in2out_fast, static) = { VNET_FEATURE_INIT (ip4_snat_out2in_fast, static) = { .arc_name = "ip4-unicast", .node_name = "nat44-out2in-fast", - .runs_after = VNET_FEATURES ("acl-plugin-in-ip4-fa"), + .runs_after = VNET_FEATURES ("acl-plugin-in-ip4-fa", + "ip4-dhcp-client-detect"), }; VNET_FEATURE_INIT (ip4_snat_hairpin_dst, static) = { .arc_name = "ip4-unicast", .node_name = "nat44-hairpin-dst", .runs_after = VNET_FEATURES ("acl-plugin-in-ip4-fa"), }; +VNET_FEATURE_INIT (ip4_nat44_ed_hairpin_dst, static) = { + .arc_name = "ip4-unicast", + .node_name = "nat44-ed-hairpin-dst", + .runs_after = VNET_FEATURES ("acl-plugin-in-ip4-fa"), +}; /* Hook up output features */ VNET_FEATURE_INIT (ip4_snat_in2out_output, static) = { @@ -115,6 +139,16 @@ VNET_FEATURE_INIT (ip4_snat_hairpin_src, static) = { .node_name = "nat44-hairpin-src", .runs_after = VNET_FEATURES ("acl-plugin-out-ip4-fa"), }; +VNET_FEATURE_INIT (ip4_nat44_ed_in2out_output, static) = { + .arc_name = "ip4-output", + .node_name = "nat44-ed-in2out-output", + .runs_after = VNET_FEATURES ("acl-plugin-out-ip4-fa"), +}; +VNET_FEATURE_INIT (ip4_nat44_ed_hairpin_src, static) = { + .arc_name = "ip4-output", + .node_name = "nat44-ed-hairpin-src", + .runs_after = VNET_FEATURES ("acl-plugin-out-ip4-fa"), +}; /* Hook up ip4-local features */ VNET_FEATURE_INIT (ip4_nat_hairpinning, static) = @@ -123,6 +157,12 @@ VNET_FEATURE_INIT (ip4_nat_hairpinning, static) = .node_name = "nat44-hairpinning", .runs_before = VNET_FEATURES("ip4-local-end-of-arc"), }; +VNET_FEATURE_INIT (ip4_nat44_ed_hairpinning, static) = +{ + .arc_name = "ip4-local", + .node_name = "nat44-ed-hairpinning", + .runs_before = VNET_FEATURES("ip4-local-end-of-arc"), +}; /* *INDENT-OFF* */ @@ -133,6 +173,7 @@ VLIB_PLUGIN_REGISTER () = { /* *INDENT-ON* */ vlib_node_registration_t nat44_classify_node; +vlib_node_registration_t nat44_ed_classify_node; vlib_node_registration_t nat44_det_classify_node; vlib_node_registration_t nat44_handoff_classify_node; @@ -164,12 +205,12 @@ nat_free_session_data (snat_main_t * sm, snat_session_t * s, u32 thread_index) ed_key.fib_index = 0; ed_kv.key[0] = ed_key.as_u64[0]; ed_kv.key[1] = ed_key.as_u64[1]; - if (clib_bihash_add_del_16_8 (&sm->in2out_ed, &ed_kv, 0)) + if (clib_bihash_add_del_16_8 (&tsm->in2out_ed, &ed_kv, 0)) nat_log_warn ("in2out_ed key del failed"); return; } - /* Endpoint dependent session lookup tables */ + /* session lookup tables */ if (is_ed_session (s)) { ed_key.l_addr = s->out2in.addr; @@ -189,7 +230,7 @@ nat_free_session_data (snat_main_t * sm, snat_session_t * s, u32 thread_index) } ed_kv.key[0] = ed_key.as_u64[0]; ed_kv.key[1] = ed_key.as_u64[1]; - if (clib_bihash_add_del_16_8 (&sm->out2in_ed, &ed_kv, 0)) + if (clib_bihash_add_del_16_8 (&tsm->out2in_ed, &ed_kv, 0)) nat_log_warn ("out2in_ed key del failed"); ed_key.l_addr = s->in2out.addr; @@ -203,9 +244,18 @@ nat_free_session_data (snat_main_t * sm, snat_session_t * s, u32 thread_index) } ed_kv.key[0] = ed_key.as_u64[0]; ed_kv.key[1] = ed_key.as_u64[1]; - if (clib_bihash_add_del_16_8 (&sm->in2out_ed, &ed_kv, 0)) + if (clib_bihash_add_del_16_8 (&tsm->in2out_ed, &ed_kv, 0)) nat_log_warn ("in2out_ed key del failed"); } + else + { + kv.key = s->in2out.as_u64; + if (clib_bihash_add_del_8_8 (&tsm->in2out, &kv, 0)) + nat_log_warn ("in2out key del failed"); + kv.key = s->out2in.as_u64; + if (clib_bihash_add_del_8_8 (&tsm->out2in, &kv, 0)) + nat_log_warn ("out2in key del failed"); + } if (snat_is_unk_proto_session (s)) return; @@ -235,17 +285,6 @@ nat_free_session_data (snat_main_t * sm, snat_session_t * s, u32 thread_index) } } - if (is_ed_session (s)) - return; - - /* Session lookup tables */ - kv.key = s->in2out.as_u64; - if (clib_bihash_add_del_8_8 (&tsm->in2out, &kv, 0)) - nat_log_warn ("in2out key del failed"); - kv.key = s->out2in.as_u64; - if (clib_bihash_add_del_8_8 (&tsm->out2in, &kv, 0)) - nat_log_warn ("out2in key del failed"); - if (snat_is_session_static (s)) return; @@ -507,6 +546,29 @@ VLIB_REGISTER_NODE (nat44_classify_node) = { VLIB_NODE_FUNCTION_MULTIARCH (nat44_classify_node, nat44_classify_node_fn); +static uword +nat44_ed_classify_node_fn (vlib_main_t * vm, + vlib_node_runtime_t * node, + vlib_frame_t * frame) +{ + return nat44_classify_node_fn_inline (vm, node, frame); +}; + +VLIB_REGISTER_NODE (nat44_ed_classify_node) = { + .function = nat44_ed_classify_node_fn, + .name = "nat44-ed-classify", + .vector_size = sizeof (u32), + .format_trace = format_nat44_classify_trace, + .type = VLIB_NODE_TYPE_INTERNAL, + .n_next_nodes = NAT44_CLASSIFY_N_NEXT, + .next_nodes = { + [NAT44_CLASSIFY_NEXT_IN2OUT] = "nat44-ed-in2out", + [NAT44_CLASSIFY_NEXT_OUT2IN] = "nat44-ed-out2in", + }, +}; + +VLIB_NODE_FUNCTION_MULTIARCH (nat44_ed_classify_node, + nat44_ed_classify_node_fn); static uword nat44_det_classify_node_fn (vlib_main_t * vm, @@ -601,18 +663,21 @@ snat_add_del_addr_to_fib (ip4_address_t * addr, u8 p_len, u32 sw_if_index, FIB_SOURCE_PLUGIN_LOW); } -void snat_add_address (snat_main_t *sm, ip4_address_t *addr, u32 vrf_id, +int snat_add_address (snat_main_t *sm, ip4_address_t *addr, u32 vrf_id, u8 twice_nat) { snat_address_t * ap; snat_interface_t *i; vlib_thread_main_t *tm = vlib_get_thread_main (); + if (twice_nat && !sm->endpoint_dependent) + return VNET_API_ERROR_FEATURE_DISABLED; + /* Check if address already exists */ vec_foreach (ap, twice_nat ? sm->twice_nat_addresses : sm->addresses) { if (ap->addr.as_u32 == addr->as_u32) - return; + return VNET_API_ERROR_VALUE_EXIST; } if (twice_nat) @@ -635,7 +700,7 @@ void snat_add_address (snat_main_t *sm, ip4_address_t *addr, u32 vrf_id, #undef _ if (twice_nat) - return; + return 0; /* Add external address to FIB */ pool_foreach (i, sm->interfaces, @@ -654,6 +719,8 @@ void snat_add_address (snat_main_t *sm, ip4_address_t *addr, u32 vrf_id, snat_add_del_addr_to_fib(addr, 32, i->sw_if_index, 1); break; })); + + return 0; } static int is_snat_address_used_in_static_mapping (snat_main_t *sm, @@ -751,6 +818,12 @@ int snat_add_static_mapping(ip4_address_t l_addr, ip4_address_t e_addr, snat_session_t * s; snat_static_map_resolve_t *rp, *rp_match = 0; + if (!sm->endpoint_dependent) + { + if (twice_nat || out2in_only) + return VNET_API_ERROR_FEATURE_DISABLED; + } + /* If the external address is a specific interface address */ if (sw_if_index != ~0) { @@ -936,13 +1009,13 @@ int snat_add_static_mapping(ip4_address_t l_addr, ip4_address_t e_addr, m->proto = proto; } - if (sm->workers) + if (sm->num_workers > 1) { ip4_header_t ip = { .src_address = m->local_addr, }; - m->worker_index = sm->worker_in2out_cb (&ip, m->fib_index); - tsm = vec_elt_at_index (sm->per_thread_data, m->worker_index); + vec_add1 (m->workers, sm->worker_in2out_cb (&ip, m->fib_index)); + tsm = vec_elt_at_index (sm->per_thread_data, m->workers[0]); } else tsm = vec_elt_at_index (sm->per_thread_data, sm->num_workers); @@ -955,14 +1028,6 @@ int snat_add_static_mapping(ip4_address_t l_addr, ip4_address_t e_addr, kv.value = m - sm->static_mappings; if (!out2in_only) clib_bihash_add_del_8_8(&sm->static_mapping_by_local, &kv, 1); - if (twice_nat || out2in_only) - { - m_key.port = clib_host_to_net_u16 (m->local_port); - kv.key = m_key.as_u64; - kv.value = ~0ULL; - if (clib_bihash_add_del_8_8(&tsm->in2out, &kv, 1)) - nat_log_warn ("in2out key add failed"); - } m_key.addr = m->external_addr; m_key.port = m->external_port; @@ -970,14 +1035,6 @@ int snat_add_static_mapping(ip4_address_t l_addr, ip4_address_t e_addr, kv.key = m_key.as_u64; kv.value = m - sm->static_mappings; clib_bihash_add_del_8_8(&sm->static_mapping_by_external, &kv, 1); - if (twice_nat || out2in_only) - { - m_key.port = clib_host_to_net_u16 (e_port); - kv.key = m_key.as_u64; - kv.value = ~0ULL; - if (clib_bihash_add_del_8_8(&tsm->out2in, &kv, 1)) - nat_log_warn ("out2in key add failed"); - } /* Delete dynamic sessions matching local address (+ local port) */ if (!(sm->static_mapping_only)) @@ -1059,7 +1116,7 @@ int snat_add_static_mapping(ip4_address_t l_addr, ip4_address_t e_addr, } if (sm->num_workers > 1) - tsm = vec_elt_at_index (sm->per_thread_data, m->worker_index); + tsm = vec_elt_at_index (sm->per_thread_data, m->workers[0]); else tsm = vec_elt_at_index (sm->per_thread_data, sm->num_workers); @@ -1070,28 +1127,12 @@ int snat_add_static_mapping(ip4_address_t l_addr, ip4_address_t e_addr, kv.key = m_key.as_u64; if (!out2in_only) clib_bihash_add_del_8_8(&sm->static_mapping_by_local, &kv, 0); - if (twice_nat || out2in_only) - { - m_key.port = clib_host_to_net_u16 (m->local_port); - kv.key = m_key.as_u64; - kv.value = ~0ULL; - if (clib_bihash_add_del_8_8(&tsm->in2out, &kv, 0)) - nat_log_warn ("in2out key del failed"); - } m_key.addr = m->external_addr; m_key.port = m->external_port; m_key.fib_index = sm->outside_fib_index; kv.key = m_key.as_u64; clib_bihash_add_del_8_8(&sm->static_mapping_by_external, &kv, 0); - if (twice_nat || out2in_only) - { - 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, 0)) - nat_log_warn ("out2in key del failed"); - } /* Delete session(s) for static mapping if exist */ if (!(sm->static_mapping_only) || @@ -1146,6 +1187,7 @@ int snat_add_static_mapping(ip4_address_t l_addr, ip4_address_t e_addr, } vec_free (m->tag); + vec_free (m->workers); /* Delete static mapping from pool */ pool_put (sm->static_mappings, m); } @@ -1174,33 +1216,6 @@ int snat_add_static_mapping(ip4_address_t l_addr, ip4_address_t e_addr, return 0; } -static int lb_local_exists (nat44_lb_addr_port_t * local, - ip4_address_t * e_addr, u16 e_port) -{ - snat_main_t *sm = &snat_main; - snat_static_mapping_t *m; - nat44_lb_addr_port_t *ap; - - /* *INDENT-OFF* */ - pool_foreach (m, sm->static_mappings, - ({ - if (vec_len(m->locals)) - { - if (m->external_port == e_port && m->external_addr.as_u32 == e_addr->as_u32) - continue; - - vec_foreach (ap, m->locals) - { - if (ap->port == local->port && ap->addr.as_u32 == local->addr.as_u32) - return 1; - } - } - })); - /* *INDENT-ON* */ - - 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, @@ -1215,12 +1230,16 @@ 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; - u32 worker_index = 0, elt_index, head_index, ses_index; + u32 elt_index, head_index, ses_index; snat_main_per_thread_data_t *tsm; snat_user_key_t u_key; snat_user_t *u; snat_session_t * s; dlist_elt_t * head, * elt; + uword *bitmap = 0; + + if (!sm->endpoint_dependent) + return VNET_API_ERROR_FEATURE_DISABLED; m_key.addr = e_addr; m_key.port = e_port; @@ -1305,26 +1324,6 @@ int nat44_add_del_lb_static_mapping (ip4_address_t e_addr, u16 e_port, return VNET_API_ERROR_UNSPECIFIED; } - /* Assign worker */ - if (sm->workers) - { - 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; - } - 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)) - { - nat_log_err ("out2in key add failed"); - return VNET_API_ERROR_UNSPECIFIED; - } - m_key.fib_index = m->fib_index; for (i = 0; i < vec_len (locals); i++) { @@ -1339,16 +1338,24 @@ 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; - if (clib_bihash_add_del_8_8(&tsm->in2out, &kv, 1)) + if (sm->num_workers > 1) { - nat_log_err ("in2out key add failed"); - return VNET_API_ERROR_UNSPECIFIED; + ip4_header_t ip = { + .src_address = locals[i].addr, + }; + bitmap = clib_bitmap_set ( + bitmap, sm->worker_in2out_cb (&ip, m->fib_index), 1); } } + + /* Assign workers */ + if (sm->num_workers > 1) + { + clib_bitmap_foreach (i, bitmap, + ({ + vec_add1(m->workers, i); + })); + } } else { @@ -1387,7 +1394,6 @@ int nat44_add_del_lb_static_mapping (ip4_address_t e_addr, u16 e_port, } } - 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; @@ -1399,14 +1405,6 @@ int nat44_add_del_lb_static_mapping (ip4_address_t e_addr, u16 e_port, 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)) - { - nat_log_err ("outi2in key del failed"); - return VNET_API_ERROR_UNSPECIFIED; - } - vec_foreach (local, m->locals) { m_key.addr = local->addr; @@ -1422,16 +1420,17 @@ int nat44_add_del_lb_static_mapping (ip4_address_t e_addr, u16 e_port, } } - if (!lb_local_exists(local, &e_addr, e_port)) + if (sm->num_workers > 1) { - 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)) - { - nat_log_err ("in2out key del failed"); - return VNET_API_ERROR_UNSPECIFIED; - } + ip4_header_t ip = { + .src_address = local->addr, + }; + tsm = vec_elt_at_index (sm->per_thread_data, + sm->worker_in2out_cb (&ip, m->fib_index)); } + else + tsm = vec_elt_at_index (sm->per_thread_data, sm->num_workers); + /* Delete sessions */ u_key.addr = local->addr; u_key.fib_index = m->fib_index; @@ -1467,6 +1466,7 @@ int nat44_add_del_lb_static_mapping (ip4_address_t e_addr, u16 e_port, } vec_free(m->locals); vec_free(m->tag); + vec_free(m->workers); pool_put (sm->static_mappings, m); } @@ -1604,6 +1604,8 @@ int snat_interface_add_del (u32 sw_if_index, u8 is_inside, int is_del) feature_name = is_inside ? "nat44-in2out-worker-handoff" : "nat44-out2in-worker-handoff"; else if (sm->deterministic) feature_name = is_inside ? "nat44-det-in2out" : "nat44-det-out2in"; + else if (sm->endpoint_dependent) + feature_name = is_inside ? "nat44-ed-in2out" : "nat44-ed-out2in"; else feature_name = is_inside ? "nat44-in2out" : "nat44-out2in"; } @@ -1641,6 +1643,12 @@ int snat_interface_add_del (u32 sw_if_index, u8 is_inside, int is_del) feature_name = !is_inside ? "nat44-det-in2out" : "nat44-det-out2in"; } + else if (sm->endpoint_dependent) + { + del_feature_name = "nat44-ed-classify"; + feature_name = !is_inside ? "nat44-ed-in2out" : + "nat44-ed-out2in"; + } else { del_feature_name = "nat44-classify"; @@ -1652,8 +1660,16 @@ int snat_interface_add_del (u32 sw_if_index, u8 is_inside, int is_del) vnet_feature_enable_disable ("ip4-unicast", feature_name, sw_if_index, 1, 0, 0); if (!is_inside) - vnet_feature_enable_disable ("ip4-local", "nat44-hairpinning", - sw_if_index, 1, 0, 0); + { + if (sm->endpoint_dependent) + vnet_feature_enable_disable ("ip4-local", + "nat44-ed-hairpinning", + sw_if_index, 1, 0, 0); + else + vnet_feature_enable_disable ("ip4-local", + "nat44-hairpinning", + sw_if_index, 1, 0, 0); + } } else { @@ -1661,8 +1677,16 @@ int snat_interface_add_del (u32 sw_if_index, u8 is_inside, int is_del) sw_if_index, 0, 0, 0); pool_put (sm->interfaces, i); if (is_inside) - vnet_feature_enable_disable ("ip4-local", "nat44-hairpinning", - sw_if_index, 0, 0, 0); + { + if (sm->endpoint_dependent) + vnet_feature_enable_disable ("ip4-local", + "nat44-ed-hairpinning", + sw_if_index, 0, 0, 0); + else + vnet_feature_enable_disable ("ip4-local", + "nat44-hairpinning", + sw_if_index, 0, 0, 0); + } } } else @@ -1683,6 +1707,12 @@ int snat_interface_add_del (u32 sw_if_index, u8 is_inside, int is_del) "nat44-det-out2in"; feature_name = "nat44-det-classify"; } + else if (sm->endpoint_dependent) + { + del_feature_name = !is_inside ? "nat44-ed-in2out" : + "nat44-ed-out2in"; + feature_name = "nat44-ed-classify"; + } else { del_feature_name = !is_inside ? "nat44-in2out" : "nat44-out2in"; @@ -1694,8 +1724,14 @@ int snat_interface_add_del (u32 sw_if_index, u8 is_inside, int is_del) vnet_feature_enable_disable ("ip4-unicast", feature_name, sw_if_index, 1, 0, 0); if (!is_inside) - vnet_feature_enable_disable ("ip4-local", "nat44-hairpinning", - sw_if_index, 0, 0, 0); + { + if (sm->endpoint_dependent) + vnet_feature_enable_disable ("ip4-local", "nat44-ed-hairpinning", + sw_if_index, 0, 0, 0); + else + vnet_feature_enable_disable ("ip4-local", "nat44-hairpinning", + sw_if_index, 0, 0, 0); + } goto set_flags; } @@ -1712,8 +1748,14 @@ int snat_interface_add_del (u32 sw_if_index, u8 is_inside, int is_del) vnet_feature_enable_disable ("ip4-unicast", feature_name, sw_if_index, 1, 0, 0); if (is_inside && !sm->out2in_dpo) - vnet_feature_enable_disable ("ip4-local", "nat44-hairpinning", - sw_if_index, 1, 0, 0); + { + if (sm->endpoint_dependent) + vnet_feature_enable_disable ("ip4-local", "nat44-ed-hairpinning", + sw_if_index, 1, 0, 0); + else + vnet_feature_enable_disable ("ip4-local", "nat44-hairpinning", + sw_if_index, 1, 0, 0); + } set_flags: if (is_inside) @@ -1766,16 +1808,27 @@ int snat_interface_add_del_output_feature (u32 sw_if_index, if (is_inside) { - vnet_feature_enable_disable ("ip4-unicast", "nat44-hairpin-dst", - sw_if_index, !is_del, 0, 0); - vnet_feature_enable_disable ("ip4-output", "nat44-hairpin-src", - sw_if_index, !is_del, 0, 0); + if (sm->endpoint_dependent) + { + vnet_feature_enable_disable ("ip4-unicast", "nat44-ed-hairpin-dst", + sw_if_index, !is_del, 0, 0); + vnet_feature_enable_disable ("ip4-output", "nat44-ed-hairpin-src", + sw_if_index, !is_del, 0, 0); + } + else + { + vnet_feature_enable_disable ("ip4-unicast", "nat44-hairpin-dst", + sw_if_index, !is_del, 0, 0); + vnet_feature_enable_disable ("ip4-output", "nat44-hairpin-src", + sw_if_index, !is_del, 0, 0); + } goto fq; } if (sm->num_workers > 1) { - vnet_feature_enable_disable ("ip4-unicast", "nat44-out2in-worker-handoff", + vnet_feature_enable_disable ("ip4-unicast", + "nat44-out2in-worker-handoff", sw_if_index, !is_del, 0, 0); vnet_feature_enable_disable ("ip4-output", "nat44-in2out-output-worker-handoff", @@ -1783,10 +1836,20 @@ int snat_interface_add_del_output_feature (u32 sw_if_index, } else { - vnet_feature_enable_disable ("ip4-unicast", "nat44-out2in", sw_if_index, - !is_del, 0, 0); - vnet_feature_enable_disable ("ip4-output", "nat44-in2out-output", - sw_if_index, !is_del, 0, 0); + if (sm->endpoint_dependent) + { + vnet_feature_enable_disable ("ip4-unicast", "nat44-ed-out2in", + sw_if_index, !is_del, 0, 0); + vnet_feature_enable_disable ("ip4-output", "nat44-ed-in2out-output", + sw_if_index, !is_del, 0, 0); + } + else + { + vnet_feature_enable_disable ("ip4-unicast", "nat44-out2in", + sw_if_index, !is_del, 0, 0); + vnet_feature_enable_disable ("ip4-output", "nat44-in2out-output", + sw_if_index, !is_del, 0, 0); + } } fq: @@ -1913,7 +1976,6 @@ static clib_error_t * snat_init (vlib_main_t * vm) sm->ip4_lookup_main = lm; sm->api_main = &api_main; sm->first_worker_index = 0; - sm->next_worker = 0; sm->num_workers = 0; sm->num_snat_thread = 1; sm->workers = 0; @@ -2076,6 +2138,7 @@ int snat_static_mapping_match (snat_main_t * sm, { if (vec_len (m->locals)) { +get_local: hi = vec_len (m->locals) - 1; rand = 1 + (random_u32 (&sm->random_seed) % m->locals[hi].prefix); while (lo < hi) @@ -2085,6 +2148,14 @@ int snat_static_mapping_match (snat_main_t * sm, } if (!(m->locals[lo].prefix >= rand)) return 1; + if (PREDICT_FALSE (sm->num_workers > 1)) + { + ip4_header_t ip = { + .src_address = m->locals[lo].addr, + }; + if (sm->worker_in2out_cb (&ip, m->fib_index) != vlib_get_thread_index ()) + goto get_local; + } mapping->addr = m->locals[lo].addr; mapping->port = clib_host_to_net_u16 (m->locals[lo].port); } @@ -2431,11 +2502,6 @@ snat_get_worker_out2in_cb (ip4_header_t * ip0, u32 rx_fib_index0) 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; u32 next_worker_index = 0; @@ -2450,7 +2516,7 @@ snat_get_worker_out2in_cb (ip4_header_t * ip0, u32 rx_fib_index0) 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; + return m->workers[0]; } } @@ -2480,32 +2546,7 @@ snat_get_worker_out2in_cb (ip4_header_t * ip0, u32 rx_fib_index0) /* unknown protocol */ if (PREDICT_FALSE (proto == ~0)) { - key.l_addr = ip0->dst_address; - key.r_addr = ip0->src_address; - key.fib_index = rx_fib_index0; - key.proto = ip0->protocol; - key.r_port = 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_ed, &s_kv, &s_value)) - { - for (i = 0; i < _vec_len (sm->per_thread_data); i++) - { - 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 */ + /* use current thread */ return vlib_get_thread_index (); } @@ -2548,7 +2589,7 @@ snat_get_worker_out2in_cb (ip4_header_t * ip0, u32 rx_fib_index0) 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; + return m->workers[0]; } } @@ -2559,6 +2600,97 @@ snat_get_worker_out2in_cb (ip4_header_t * ip0, u32 rx_fib_index0) return next_worker_index; } +static u32 +nat44_ed_get_worker_out2in_cb (ip4_header_t * ip, u32 rx_fib_index) +{ + snat_main_t *sm = &snat_main; + clib_bihash_kv_8_8_t kv, value; + u32 proto, next_worker_index = 0; + udp_header_t *udp; + u16 port; + snat_static_mapping_t *m; + u32 hash; + + /* first try static mappings without port */ + if (PREDICT_FALSE (pool_elts (sm->static_mappings))) + { + make_sm_kv (&kv, &ip->dst_address, 0, rx_fib_index, 0); + if (!clib_bihash_search_8_8 (&sm->static_mapping_by_external, &kv, &value)) + { + m = pool_elt_at_index (sm->static_mappings, value.value); + return m->workers[0]; + } + } + + proto = ip_proto_to_snat_proto (ip->protocol); + + /* unknown protocol */ + if (PREDICT_FALSE (proto == ~0)) + { + /* use current thread */ + return vlib_get_thread_index (); + } + + udp = ip4_next_header (ip); + port = udp->dst_port; + + if (PREDICT_FALSE (ip->protocol == IP_PROTOCOL_ICMP)) + { + icmp46_header_t * icmp = (icmp46_header_t *) udp; + icmp_echo_header_t *echo = (icmp_echo_header_t *)(icmp + 1); + if (!icmp_is_error_message (icmp)) + port = echo->identifier; + else + { + 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 (); + } + } + } + + /* try static mappings with port */ + if (PREDICT_FALSE (pool_elts (sm->static_mappings))) + { + make_sm_kv (&kv, &ip->dst_address, proto, rx_fib_index, + clib_net_to_host_u16 (port)); + if (!clib_bihash_search_8_8 (&sm->static_mapping_by_external, &kv, &value)) + { + m = pool_elt_at_index (sm->static_mappings, value.value); + if (!vec_len(m->locals)) + return m->workers[0]; + + hash = ip->src_address.as_u32 + (ip->src_address.as_u32 >> 8) + + (ip->src_address.as_u32 >> 16) + (ip->src_address.as_u32 >>24); + + if (PREDICT_TRUE (is_pow2 (_vec_len (m->workers)))) + return m->workers[hash & (_vec_len (m->workers) - 1)]; + else + return m->workers[hash % _vec_len (m->workers)]; + } + } + + /* worker by outside port */ + next_worker_index = sm->first_worker_index; + next_worker_index += + sm->workers[(clib_net_to_host_u16 (port) - 1024) / sm->port_per_thread]; + + return next_worker_index; +} + static clib_error_t * snat_config (vlib_main_t * vm, unformat_input_t * input) { @@ -2585,6 +2717,7 @@ snat_config (vlib_main_t * vm, unformat_input_t * input) sm->deterministic = 0; sm->out2in_dpo = 0; + sm->endpoint_dependent = 0; while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) { @@ -2632,6 +2765,8 @@ snat_config (vlib_main_t * vm, unformat_input_t * input) sm->out2in_dpo = 1; else if (unformat (input, "dslite ce")) dslite_set_ce(dm, 1); + else if (unformat (input, "endpoint-dependent")) + sm->endpoint_dependent = 1; else return clib_error_return (0, "unknown input '%U'", format_unformat_error, input); @@ -2673,28 +2808,59 @@ snat_config (vlib_main_t * vm, unformat_input_t * input) } else { - sm->worker_in2out_cb = snat_get_worker_in2out_cb; - sm->worker_out2in_cb = snat_get_worker_out2in_cb; - sm->in2out_node_index = snat_in2out_node.index; - sm->in2out_output_node_index = snat_in2out_output_node.index; - sm->out2in_node_index = snat_out2in_node.index; - if (!static_mapping_only || - (static_mapping_only && static_mapping_connection_tracking)) + if (sm->endpoint_dependent) { + sm->worker_in2out_cb = snat_get_worker_in2out_cb; + sm->worker_out2in_cb = nat44_ed_get_worker_out2in_cb; + sm->in2out_node_index = nat44_ed_in2out_node.index; + sm->in2out_output_node_index = nat44_ed_in2out_output_node.index; + sm->out2in_node_index = nat44_ed_out2in_node.index; + sm->icmp_match_in2out_cb = icmp_match_in2out_ed; + sm->icmp_match_out2in_cb = icmp_match_out2in_ed; + } + else + { + sm->worker_in2out_cb = snat_get_worker_in2out_cb; + sm->worker_out2in_cb = snat_get_worker_out2in_cb; + sm->in2out_node_index = snat_in2out_node.index; + sm->in2out_output_node_index = snat_in2out_output_node.index; + sm->out2in_node_index = snat_out2in_node.index; sm->icmp_match_in2out_cb = icmp_match_in2out_slow; sm->icmp_match_out2in_cb = icmp_match_out2in_slow; - + } + if (!static_mapping_only || + (static_mapping_only && static_mapping_connection_tracking)) + { vec_foreach (tsm, sm->per_thread_data) { - clib_bihash_init_8_8 (&tsm->in2out, "in2out", translation_buckets, - translation_memory_size); - clib_bihash_set_kvp_format_fn_8_8 (&tsm->in2out, - format_session_kvp); - - clib_bihash_init_8_8 (&tsm->out2in, "out2in", translation_buckets, - translation_memory_size); - clib_bihash_set_kvp_format_fn_8_8 (&tsm->out2in, - format_session_kvp); + if (sm->endpoint_dependent) + { + clib_bihash_init_16_8 (&tsm->in2out_ed, "in2out-ed", + translation_buckets, + translation_memory_size); + clib_bihash_set_kvp_format_fn_16_8 (&tsm->in2out_ed, + format_ed_session_kvp); + + clib_bihash_init_16_8 (&tsm->out2in_ed, "out2in-ed", + translation_buckets, + translation_memory_size); + clib_bihash_set_kvp_format_fn_16_8 (&tsm->out2in_ed, + format_ed_session_kvp); + } + else + { + clib_bihash_init_8_8 (&tsm->in2out, "in2out", + translation_buckets, + translation_memory_size); + clib_bihash_set_kvp_format_fn_8_8 (&tsm->in2out, + format_session_kvp); + + clib_bihash_init_8_8 (&tsm->out2in, "out2in", + translation_buckets, + translation_memory_size); + clib_bihash_set_kvp_format_fn_8_8 (&tsm->out2in, + format_session_kvp); + } clib_bihash_init_8_8 (&tsm->user_hash, "users", user_buckets, user_memory_size); @@ -2702,15 +2868,6 @@ snat_config (vlib_main_t * vm, unformat_input_t * input) format_user_kvp); } - clib_bihash_init_16_8 (&sm->in2out_ed, "in2out-ed", - translation_buckets, translation_memory_size); - clib_bihash_set_kvp_format_fn_16_8 (&sm->in2out_ed, - format_ed_session_kvp); - - clib_bihash_init_16_8 (&sm->out2in_ed, "out2in-ed", - translation_buckets, translation_memory_size); - clib_bihash_set_kvp_format_fn_16_8 (&sm->out2in_ed, - format_ed_session_kvp); } else { @@ -3063,7 +3220,7 @@ match: if (addresses[j].addr.as_u32 == address->as_u32) return; - snat_add_address (sm, address, ~0, twice_nat); + (void) snat_add_address (sm, address, ~0, twice_nat); /* Scan static map resolution vector */ for (j = 0; j < vec_len (sm->to_resolve); j++) { @@ -3164,7 +3321,7 @@ int snat_add_interface_address (snat_main_t *sm, u32 sw_if_index, int is_del, /* If the address is already bound - or static - add it now */ if (first_int_addr) - snat_add_address (sm, first_int_addr, ~0, twice_nat); + (void) snat_add_address (sm, first_int_addr, ~0, twice_nat); return 0; } @@ -3181,6 +3338,9 @@ nat44_del_session (snat_main_t *sm, ip4_address_t *addr, u16 port, snat_session_t *s; clib_bihash_8_8_t *t; + if (sm->endpoint_dependent) + return VNET_API_ERROR_UNSUPPORTED; + ip.dst_address.as_u32 = ip.src_address.as_u32 = addr->as_u32; if (sm->num_workers > 1) tsm = @@ -3218,17 +3378,22 @@ nat44_del_ed_session (snat_main_t *sm, ip4_address_t *addr, u16 port, clib_bihash_16_8_t *t; nat_ed_ses_key_t key; clib_bihash_kv_16_8_t kv, value; - u32 thread_index; u32 fib_index = fib_table_find (FIB_PROTOCOL_IP4, vrf_id); snat_session_t *s; + snat_main_per_thread_data_t *tsm; + + if (!sm->endpoint_dependent) + return VNET_API_ERROR_FEATURE_DISABLED; ip.dst_address.as_u32 = ip.src_address.as_u32 = addr->as_u32; if (sm->num_workers > 1) - thread_index = sm->worker_in2out_cb (&ip, fib_index); + tsm = + vec_elt_at_index (sm->per_thread_data, + sm->worker_in2out_cb (&ip, fib_index)); else - thread_index = sm->num_workers; + tsm = vec_elt_at_index (sm->per_thread_data, sm->num_workers); - t = is_in ? &sm->in2out_ed : &sm->out2in_ed; + t = is_in ? &tsm->in2out_ed : &tsm->out2in_ed; key.l_addr.as_u32 = addr->as_u32; key.r_addr.as_u32 = eh_addr->as_u32; key.l_port = clib_host_to_net_u16 (port); @@ -3240,11 +3405,11 @@ nat44_del_ed_session (snat_main_t *sm, ip4_address_t *addr, u16 port, if (clib_bihash_search_16_8 (t, &kv, &value)) return VNET_API_ERROR_NO_SUCH_ENTRY; - if (pool_is_free_index (sm->per_thread_data[thread_index].sessions, value.value)) + if (pool_is_free_index (tsm->sessions, value.value)) return VNET_API_ERROR_UNSPECIFIED; - s = pool_elt_at_index (sm->per_thread_data[thread_index].sessions, value.value); - nat_free_session_data (sm, s, thread_index); - nat44_delete_session (sm, s, thread_index); + s = pool_elt_at_index (tsm->sessions, value.value); + nat_free_session_data (sm, s, tsm - sm->per_thread_data); + nat44_delete_session (sm, s, tsm - sm->per_thread_data); return 0; } diff --git a/src/plugins/nat/nat.h b/src/plugins/nat/nat.h index 9de65d97e03..bd00a5246dd 100644 --- a/src/plugins/nat/nat.h +++ b/src/plugins/nat/nat.h @@ -245,7 +245,7 @@ typedef struct { u32 vrf_id; u32 fib_index; snat_protocol_t proto; - u32 worker_index; + u32 *workers; u8 *tag; nat44_lb_addr_port_t *locals; } snat_static_mapping_t; @@ -273,6 +273,10 @@ typedef struct { clib_bihash_8_8_t out2in; clib_bihash_8_8_t in2out; + /* Endpoint 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; @@ -312,16 +316,11 @@ typedef int nat_alloc_out_addr_and_port_function_t (snat_address_t * addresses, u32 snat_thread_index); typedef struct snat_main_s { - /* Endpoint address dependent sessions lookup tables */ - clib_bihash_16_8_t out2in_ed; - clib_bihash_16_8_t in2out_ed; - 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; @@ -386,6 +385,7 @@ typedef struct snat_main_s { u8 static_mapping_connection_tracking; u8 deterministic; u8 out2in_dpo; + u8 endpoint_dependent; u32 translation_buckets; u32 translation_memory_size; u32 max_translations; @@ -430,6 +430,14 @@ 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; +extern vlib_node_registration_t nat44_ed_in2out_node; +extern vlib_node_registration_t nat44_ed_in2out_output_node; +extern vlib_node_registration_t nat44_ed_out2in_node; +extern vlib_node_registration_t nat44_ed_hairpin_dst_node; +extern vlib_node_registration_t nat44_ed_hairpin_src_node; +extern vlib_node_registration_t nat44_ed_in2out_worker_handoff_node; +extern vlib_node_registration_t nat44_ed_in2out_output_worker_handoff_node; +extern vlib_node_registration_t nat44_ed_out2in_worker_handoff_node; void snat_free_outside_address_and_port (snat_address_t * addresses, u32 thread_index, @@ -548,6 +556,11 @@ u32 icmp_match_in2out_det(snat_main_t *sm, vlib_node_runtime_t *node, 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_ed(snat_main_t *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, 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, ip4_header_t *ip0, u8 *p_proto, @@ -563,9 +576,14 @@ u32 icmp_match_out2in_det(snat_main_t *sm, vlib_node_runtime_t *node, 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_ed(snat_main_t *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, 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, - u8 twice_nat); +int snat_add_address(snat_main_t *sm, ip4_address_t *addr, u32 vrf_id, + u8 twice_nat); int snat_del_address(snat_main_t *sm, ip4_address_t addr, u8 delete_sm, u8 twice_nat); void nat44_add_del_address_dpo (ip4_address_t addr, u8 is_add); diff --git a/src/plugins/nat/nat44_cli.c b/src/plugins/nat/nat44_cli.c index 7a8be98c6fe..aa733a06a5a 100644 --- a/src/plugins/nat/nat44_cli.c +++ b/src/plugins/nat/nat44_cli.c @@ -172,8 +172,6 @@ nat44_show_hash_commnad_fn (vlib_main_t * vm, unformat_input_t * input, else if (unformat (input, "verbose")) verbose = 2; - vlib_cli_output (vm, "%U", format_bihash_16_8, &sm->in2out_ed, verbose); - vlib_cli_output (vm, "%U", format_bihash_16_8, &sm->out2in_ed, verbose); vlib_cli_output (vm, "%U", format_bihash_8_8, &sm->static_mapping_by_local, verbose); vlib_cli_output (vm, "%U", @@ -184,8 +182,18 @@ nat44_show_hash_commnad_fn (vlib_main_t * vm, unformat_input_t * input, tsm = vec_elt_at_index (sm->per_thread_data, i); vlib_cli_output (vm, "-------- thread %d %s --------\n", i, vlib_worker_threads[i].name); - vlib_cli_output (vm, "%U", format_bihash_8_8, &tsm->in2out, verbose); - vlib_cli_output (vm, "%U", format_bihash_8_8, &tsm->out2in, verbose); + if (sm->endpoint_dependent) + { + vlib_cli_output (vm, "%U", format_bihash_16_8, &tsm->in2out_ed, + verbose); + vlib_cli_output (vm, "%U", format_bihash_16_8, &tsm->out2in_ed, + verbose); + } + else + { + vlib_cli_output (vm, "%U", format_bihash_8_8, &tsm->in2out, verbose); + vlib_cli_output (vm, "%U", format_bihash_8_8, &tsm->out2in, verbose); + } vlib_cli_output (vm, "%U", format_bihash_8_8, &tsm->user_hash, verbose); } @@ -304,18 +312,26 @@ add_address_command_fn (vlib_main_t * vm, for (i = 0; i < count; i++) { if (is_add) - snat_add_address (sm, &this_addr, vrf_id, twice_nat); + rv = snat_add_address (sm, &this_addr, vrf_id, twice_nat); else rv = snat_del_address (sm, this_addr, 0, twice_nat); switch (rv) { + case VNET_API_ERROR_VALUE_EXIST: + error = clib_error_return (0, "NAT address already in use."); + goto done; case VNET_API_ERROR_NO_SUCH_ENTRY: - error = clib_error_return (0, "S-NAT address not exist."); + error = clib_error_return (0, "NAT address not exist."); goto done; case VNET_API_ERROR_UNSPECIFIED: error = - clib_error_return (0, "S-NAT address used in static mapping."); + clib_error_return (0, "NAT address used in static mapping."); + goto done; + case VNET_API_ERROR_FEATURE_DISABLED: + error = + clib_error_return (0, + "twice NAT available only for endpoint-dependent mode."); goto done; default: break; @@ -621,6 +637,11 @@ add_static_mapping_command_fn (vlib_main_t * vm, case VNET_API_ERROR_VALUE_EXIST: error = clib_error_return (0, "Mapping already exist."); goto done; + case VNET_API_ERROR_FEATURE_DISABLED: + error = + clib_error_return (0, + "twice-nat/out2in-only available only for endpoint-dependent mode."); + goto done; default: break; } @@ -800,6 +821,10 @@ add_lb_static_mapping_command_fn (vlib_main_t * vm, case VNET_API_ERROR_VALUE_EXIST: error = clib_error_return (0, "Mapping already exist."); goto done; + case VNET_API_ERROR_FEATURE_DISABLED: + error = + clib_error_return (0, "Available only for endpoint-dependent mode."); + goto done; default: break; } diff --git a/src/plugins/nat/nat_api.c b/src/plugins/nat/nat_api.c index f5f41619b33..6d4d0d912e6 100644 --- a/src/plugins/nat/nat_api.c +++ b/src/plugins/nat/nat_api.c @@ -454,7 +454,7 @@ static void for (i = 0; i < count; i++) { if (mp->is_add) - snat_add_address (sm, &this_addr, vrf_id, mp->twice_nat); + rv = snat_add_address (sm, &this_addr, vrf_id, mp->twice_nat); else rv = snat_del_address (sm, this_addr, 0, mp->twice_nat); diff --git a/src/plugins/nat/nat_inlines.h b/src/plugins/nat/nat_inlines.h index db5b295f938..3724986b9b8 100644 --- a/src/plugins/nat/nat_inlines.h +++ b/src/plugins/nat/nat_inlines.h @@ -239,6 +239,38 @@ nat44_session_update_lru (snat_main_t * sm, snat_session_t * s, s->per_user_list_head_index, s->per_user_index); } +always_inline void +make_ed_kv (clib_bihash_kv_16_8_t * kv, ip4_address_t * l_addr, + ip4_address_t * r_addr, u8 proto, u32 fib_index, u16 l_port, + u16 r_port) +{ + nat_ed_ses_key_t *key = (nat_ed_ses_key_t *) kv->key; + + key->l_addr.as_u32 = l_addr->as_u32; + key->r_addr.as_u32 = r_addr->as_u32; + key->fib_index = fib_index; + key->proto = proto; + key->l_port = l_port; + key->r_port = r_port; + + kv->value = ~0ULL; +} + +always_inline void +make_sm_kv (clib_bihash_kv_8_8_t * kv, ip4_address_t * addr, u8 proto, + u32 fib_index, u16 port) +{ + snat_session_key_t key; + + key.addr.as_u32 = addr->as_u32; + key.port = port; + key.protocol = proto; + key.fib_index = fib_index; + + kv->key = key.as_u64; + kv->value = ~0ULL; +} + #endif /* __included_nat_inlines_h__ */ /* diff --git a/src/plugins/nat/out2in.c b/src/plugins/nat/out2in.c index 83e1426a413..80465b071d4 100755 --- a/src/plugins/nat/out2in.c +++ b/src/plugins/nat/out2in.c @@ -104,6 +104,8 @@ vlib_node_registration_t snat_out2in_fast_node; vlib_node_registration_t snat_out2in_worker_handoff_node; vlib_node_registration_t snat_det_out2in_node; vlib_node_registration_t nat44_out2in_reass_node; +vlib_node_registration_t nat44_ed_out2in_node; +vlib_node_registration_t nat44_ed_out2in_slowpath_node; #define foreach_snat_out2in_error \ _(UNSUPPORTED_PROTOCOL, "Unsupported protocol") \ @@ -135,7 +137,6 @@ typedef enum { SNAT_OUT2IN_NEXT_LOOKUP, SNAT_OUT2IN_NEXT_ICMP_ERROR, SNAT_OUT2IN_NEXT_REASS, - SNAT_OUT2IN_NEXT_IN2OUT, SNAT_OUT2IN_N_NEXT, } snat_out2in_next_t; @@ -268,161 +269,6 @@ snat_out2in_error_t icmp_get_key(ip4_header_t *ip0, return -1; /* success */ } -static_always_inline int -icmp_get_ed_key(ip4_header_t *ip0, nat_ed_ses_key_t *p_key0) -{ - icmp46_header_t *icmp0; - nat_ed_ses_key_t key0; - icmp_echo_header_t *echo0, *inner_echo0 = 0; - ip4_header_t *inner_ip0; - void *l4_header = 0; - icmp46_header_t *inner_icmp0; - - icmp0 = (icmp46_header_t *) ip4_next_header (ip0); - echo0 = (icmp_echo_header_t *)(icmp0+1); - - if (!icmp_is_error_message (icmp0)) - { - key0.proto = IP_PROTOCOL_ICMP; - key0.l_addr = ip0->dst_address; - key0.r_addr = ip0->src_address; - key0.l_port = key0.r_port = echo0->identifier; - } - else - { - inner_ip0 = (ip4_header_t *)(echo0+1); - l4_header = ip4_next_header (inner_ip0); - key0.proto = inner_ip0->protocol; - key0.l_addr = inner_ip0->src_address; - key0.r_addr = inner_ip0->dst_address; - switch (ip_proto_to_snat_proto (inner_ip0->protocol)) - { - case SNAT_PROTOCOL_ICMP: - inner_icmp0 = (icmp46_header_t*)l4_header; - inner_echo0 = (icmp_echo_header_t *)(inner_icmp0+1); - key0.l_port = key0.r_port = inner_echo0->identifier; - break; - case SNAT_PROTOCOL_UDP: - case SNAT_PROTOCOL_TCP: - key0.l_port = ((tcp_udp_header_t*)l4_header)->src_port; - key0.r_port = ((tcp_udp_header_t*)l4_header)->dst_port; - break; - default: - return -1; - } - } - *p_key0 = key0; - return 0; -} - -static int -next_src_nat (snat_main_t * sm, ip4_header_t * ip, u32 proto, u16 src_port, - u32 thread_index) -{ - snat_session_key_t key; - clib_bihash_kv_8_8_t kv, value; - - key.addr = ip->src_address; - key.port = src_port; - key.protocol = proto; - key.fib_index = sm->inside_fib_index; - kv.key = key.as_u64; - - if (!clib_bihash_search_8_8 (&sm->per_thread_data[thread_index].in2out, &kv, - &value)) - return 1; - - return 0; -} - -static void -create_bypass_for_fwd(snat_main_t * sm, ip4_header_t * ip, u32 rx_fib_index, - u32 thread_index) -{ - nat_ed_ses_key_t key; - clib_bihash_kv_16_8_t kv, value; - udp_header_t *udp; - snat_user_t *u; - snat_session_t *s = 0; - snat_main_per_thread_data_t *tsm = &sm->per_thread_data[thread_index]; - f64 now = vlib_time_now (sm->vlib_main); - - if (ip->protocol == IP_PROTOCOL_ICMP) - { - if (icmp_get_ed_key (ip, &key)) - return; - } - else if (ip->protocol == IP_PROTOCOL_UDP || ip->protocol == IP_PROTOCOL_TCP) - { - udp = ip4_next_header(ip); - key.r_addr = ip->src_address; - key.l_addr = ip->dst_address; - key.proto = ip->protocol; - key.l_port = udp->dst_port; - key.r_port = udp->src_port; - } - else - { - key.r_addr = ip->src_address; - key.l_addr = ip->dst_address; - key.proto = ip->protocol; - key.l_port = key.r_port = 0; - } - key.fib_index = 0; - kv.key[0] = key.as_u64[0]; - kv.key[1] = key.as_u64[1]; - - if (!clib_bihash_search_16_8 (&sm->in2out_ed, &kv, &value)) - { - s = pool_elt_at_index (tsm->sessions, value.value); - } - else - { - if (PREDICT_FALSE (maximum_sessions_exceeded(sm, thread_index))) - return; - - u = nat_user_get_or_create (sm, &ip->dst_address, sm->inside_fib_index, thread_index); - if (!u) - { - nat_log_warn ("create NAT user failed"); - return; - } - - s = nat_session_alloc_or_recycle (sm, u, thread_index); - if (!s) - { - nat_log_warn ("create NAT session failed"); - return; - } - - s->ext_host_addr = key.r_addr; - s->ext_host_port = key.r_port; - s->flags |= SNAT_SESSION_FLAG_FWD_BYPASS; - s->outside_address_index = ~0; - s->out2in.addr = key.l_addr; - s->out2in.port = key.l_port; - s->out2in.protocol = ip_proto_to_snat_proto (key.proto); - s->out2in.fib_index = 0; - s->in2out = s->out2in; - user_session_increment (sm, u, 0); - - kv.value = s - tsm->sessions; - if (clib_bihash_add_del_16_8 (&sm->in2out_ed, &kv, 1)) - nat_log_notice ("in2out_ed key add failed"); - } - - if (ip->protocol == IP_PROTOCOL_TCP) - { - tcp_header_t *tcp = ip4_next_header(ip); - if (nat44_set_tcp_session_state_o2i (sm, s, tcp, thread_index)) - return; - } - /* Per-user LRU list maintenance */ - nat44_session_update_lru (sm, s, thread_index); - /* Accounting */ - nat44_session_update_counters (s, now, 0); -} - /** * Get address and port values to be used for ICMP packet translation * and create session if needed @@ -495,12 +341,6 @@ u32 icmp_match_out2in_slow(snat_main_t *sm, vlib_node_runtime_t *node, else { dont_translate = 1; - if (next_src_nat(sm, ip0, key0.protocol, key0.port, thread_index)) - { - next0 = SNAT_OUT2IN_NEXT_IN2OUT; - goto out; - } - create_bypass_for_fwd(sm, ip0, rx_fib_index0, thread_index); goto out; } } @@ -534,34 +374,8 @@ u32 icmp_match_out2in_slow(snat_main_t *sm, vlib_node_runtime_t *node, goto out; } - if (PREDICT_FALSE (value0.value == ~0ULL)) - { - nat_ed_ses_key_t key; - clib_bihash_kv_16_8_t s_kv, s_value; - - key.as_u64[0] = 0; - key.as_u64[1] = 0; - if (icmp_get_ed_key (ip0, &key)) - { - b0->error = node->errors[SNAT_OUT2IN_ERROR_UNSUPPORTED_PROTOCOL]; - next0 = SNAT_OUT2IN_NEXT_DROP; - goto out; - } - key.fib_index = rx_fib_index0; - 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)) - s0 = pool_elt_at_index (sm->per_thread_data[thread_index].sessions, - s_value.value); - else - { - next0 = SNAT_OUT2IN_NEXT_DROP; - goto out; - } - } - else - s0 = pool_elt_at_index (sm->per_thread_data[thread_index].sessions, - value0.value); + s0 = pool_elt_at_index (sm->per_thread_data[thread_index].sessions, + value0.value); } out: @@ -797,306 +611,49 @@ static inline u32 icmp_out2in_slow_path (snat_main_t *sm, return next0; } -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_node_runtime_t * node) +static int +nat_out2in_sm_unknown_proto (snat_main_t *sm, + vlib_buffer_t * b, + ip4_header_t * ip, + u32 rx_fib_index) { clib_bihash_kv_8_8_t kv, value; - clib_bihash_kv_16_8_t s_kv, s_value; snat_static_mapping_t *m; snat_session_key_t m_key; u32 old_addr, new_addr; ip_csum_t sum; - 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_t *u; - - 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.r_port = 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_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; - } - else - { - if (PREDICT_FALSE (maximum_sessions_exceeded(sm, thread_index))) - { - b->error = node->errors[SNAT_OUT2IN_ERROR_MAX_SESSIONS_EXCEEDED]; - nat_log_notice ("maximum 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)) - { - b->error = node->errors[SNAT_OUT2IN_ERROR_NO_TRANSLATION]; - return 0; - } - - m = pool_elt_at_index (sm->static_mappings, value.value); - - new_addr = ip->dst_address.as_u32 = m->local_addr.as_u32; - - u = nat_user_get_or_create (sm, &ip->src_address, m->fib_index, - thread_index); - if (!u) - { - nat_log_warn ("create NAT user failed"); - return 0; - } - - /* Create a new session */ - s = nat_session_alloc_or_recycle (sm, u, thread_index); - if (!s) - { - nat_log_warn ("create NAT session failed"); - return 0; - } - - s->ext_host_addr.as_u32 = ip->src_address.as_u32; - s->flags |= SNAT_SESSION_FLAG_UNKNOWN_PROTO; - s->flags |= SNAT_SESSION_FLAG_STATIC_MAPPING; - s->flags |= SNAT_SESSION_FLAG_ENDPOINT_DEPENDENT; - s->outside_address_index = ~0; - s->out2in.addr.as_u32 = old_addr; - s->out2in.fib_index = rx_fib_index; - s->in2out.addr.as_u32 = new_addr; - s->in2out.fib_index = m->fib_index; - s->in2out.port = s->out2in.port = ip->protocol; - user_session_increment (sm, u, 1 /* static */); - /* Add to lookup tables */ - s_kv.value = s - tsm->sessions; - if (clib_bihash_add_del_16_8 (&sm->out2in_ed, &s_kv, 1)) - nat_log_notice ("out2in key add failed"); + 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 1; - 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_ed, &s_kv, 1)) - nat_log_notice ("in2out key add failed"); - } + m = pool_elt_at_index (sm->static_mappings, value.value); - /* Update IP checksum */ + old_addr = ip->dst_address.as_u32; + new_addr = ip->dst_address.as_u32 = m->local_addr.as_u32; sum = ip->checksum; sum = ip_csum_update (sum, old_addr, new_addr, ip4_header_t, dst_address); ip->checksum = ip_csum_fold (sum); - vnet_buffer(b)->sw_if_index[VLIB_TX] = s->in2out.fib_index; - - /* Accounting */ - nat44_session_update_counters (s, now, - vlib_buffer_length_in_chain (vm, b)); - /* Per-user LRU list maintenance */ - nat44_session_update_lru (sm, s, thread_index); - - return s; + vnet_buffer(b)->sw_if_index[VLIB_TX] = m->fib_index; + return 0; } -static snat_session_t * -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, - vlib_node_runtime_t * node) +static uword +snat_out2in_node_fn (vlib_main_t * vm, + vlib_node_runtime_t * node, + vlib_frame_t * frame) { - 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; - 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_t *u; - u32 address_index; - snat_session_key_t eh_key; - twice_nat_type_t twice_nat; - u8 lb; - - 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.r_port = udp->src_port; - 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 - { - if (PREDICT_FALSE (maximum_sessions_exceeded(sm, thread_index))) - { - b->error = node->errors[SNAT_OUT2IN_ERROR_MAX_SESSIONS_EXCEEDED]; - nat_log_notice ("maximum sessions exceeded"); - return 0; - } - - 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, &twice_nat, &lb)) - return 0; - - u = nat_user_get_or_create (sm, &l_key.addr, l_key.fib_index, - thread_index); - if (!u) - { - nat_log_warn ("create NAT user failed"); - return 0; - } - - s = nat_session_alloc_or_recycle (sm, u, thread_index); - if (!s) - { - nat_log_warn ("create NAT session failed"); - return 0; - } - - s->ext_host_addr.as_u32 = ip->src_address.as_u32; - s->ext_host_port = udp->src_port; - s->flags |= SNAT_SESSION_FLAG_STATIC_MAPPING; - if (lb) - s->flags |= SNAT_SESSION_FLAG_LOAD_BALANCING; - s->flags |= SNAT_SESSION_FLAG_ENDPOINT_DEPENDENT; - s->outside_address_index = ~0; - s->out2in = e_key; - s->in2out = l_key; - user_session_increment (sm, u, 1 /* static */); - - /* Add to lookup tables */ - s_kv.value = s - tsm->sessions; - if (clib_bihash_add_del_16_8 (&sm->out2in_ed, &s_kv, 1)) - nat_log_notice ("out2in-ed key add failed"); - - if (twice_nat == TWICE_NAT || - (twice_nat == TWICE_NAT_SELF && - ip->src_address.as_u32 == l_key.addr.as_u32)) - { - eh_key.protocol = proto; - if (snat_alloc_outside_address_and_port (sm->twice_nat_addresses, 0, - thread_index, &eh_key, - &address_index, - sm->port_per_thread, - sm->per_thread_data[thread_index].snat_thread_index)) - { - b->error = node->errors[SNAT_OUT2IN_ERROR_OUT_OF_PORTS]; - return 0; - } - key.r_addr.as_u32 = s->ext_host_nat_addr.as_u32 = eh_key.addr.as_u32; - key.r_port = s->ext_host_nat_port = eh_key.port; - s->flags |= SNAT_SESSION_FLAG_TWICE_NAT; - } - 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)) - nat_log_notice ("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); - if (is_twice_nat_session (s)) - sum = ip_csum_update (sum, ip->src_address.as_u32, - s->ext_host_nat_addr.as_u32, ip4_header_t, - src_address); - ip->checksum = ip_csum_fold (sum); - - vnet_buffer(b)->sw_if_index[VLIB_TX] = s->in2out.fib_index; - - 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); - if (is_twice_nat_session (s)) - { - sum = ip_csum_update (sum, ip->src_address.as_u32, - s->ext_host_nat_addr.as_u32, ip4_header_t, - dst_address); - sum = ip_csum_update (sum, tcp->src_port, s->ext_host_nat_port, - ip4_header_t, length); - tcp->src_port = s->ext_host_nat_port; - ip->src_address.as_u32 = s->ext_host_nat_addr.as_u32; - } - tcp->checksum = ip_csum_fold(sum); - if (nat44_set_tcp_session_state_o2i (sm, s, tcp, thread_index)) - return s; - } - else - { - udp->dst_port = s->in2out.port; - if (is_twice_nat_session (s)) - { - udp->src_port = s->ext_host_nat_port; - ip->src_address.as_u32 = s->ext_host_nat_addr.as_u32; - } - udp->checksum = 0; - } - - /* Accounting */ - nat44_session_update_counters (s, now, vlib_buffer_length_in_chain (vm, b)); - /* Per-user LRU list maintenance */ - nat44_session_update_lru (sm, s, thread_index); - - return s; -} - -static uword -snat_out2in_node_fn (vlib_main_t * vm, - vlib_node_runtime_t * node, - vlib_frame_t * frame) -{ - u32 n_left_from, * from, * to_next; - snat_out2in_next_t next_index; - u32 pkts_processed = 0; - snat_main_t * sm = &snat_main; - f64 now = vlib_time_now (vm); - u32 thread_index = vlib_get_thread_index (); + u32 n_left_from, * from, * to_next; + snat_out2in_next_t next_index; + u32 pkts_processed = 0; + snat_main_t * sm = &snat_main; + f64 now = vlib_time_now (vm); + u32 thread_index = vlib_get_thread_index (); from = vlib_frame_vector_args (frame); n_left_from = frame->n_vectors; @@ -1182,11 +739,14 @@ snat_out2in_node_fn (vlib_main_t * vm, if (PREDICT_FALSE (proto0 == ~0)) { - s0 = snat_out2in_unknown_proto(sm, b0, ip0, rx_fib_index0, - thread_index, now, vm, node); - if (!sm->forwarding_enabled) - if (!s0) - next0 = SNAT_OUT2IN_NEXT_DROP; + if (nat_out2in_sm_unknown_proto(sm, b0, ip0, rx_fib_index0)) + { + if (!sm->forwarding_enabled) + { + b0->error = node->errors[SNAT_OUT2IN_ERROR_UNSUPPORTED_PROTOCOL]; + next0 = SNAT_OUT2IN_NEXT_DROP; + } + } goto trace0; } @@ -1237,16 +797,6 @@ snat_out2in_node_fn (vlib_main_t * vm, next0 = SNAT_OUT2IN_NEXT_DROP; goto trace0; } - else - { - if (next_src_nat(sm, ip0, proto0, udp0->src_port, thread_index)) - { - next0 = SNAT_OUT2IN_NEXT_IN2OUT; - goto trace0; - } - create_bypass_for_fwd(sm, ip0, rx_fib_index0, thread_index); - goto trace0; - } } /* Create session initiated by host from external network */ @@ -1259,22 +809,8 @@ snat_out2in_node_fn (vlib_main_t * vm, } } else - { - if (PREDICT_FALSE (value0.value == ~0ULL)) - { - 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 - { - s0 = pool_elt_at_index ( - sm->per_thread_data[thread_index].sessions, - value0.value); - } - } + 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; @@ -1355,11 +891,14 @@ snat_out2in_node_fn (vlib_main_t * vm, if (PREDICT_FALSE (proto1 == ~0)) { - s1 = snat_out2in_unknown_proto(sm, b1, ip1, rx_fib_index1, - thread_index, now, vm, node); - if (!sm->forwarding_enabled) - if (!s1) - next1 = SNAT_OUT2IN_NEXT_DROP; + if (nat_out2in_sm_unknown_proto(sm, b1, ip1, rx_fib_index1)) + { + if (!sm->forwarding_enabled) + { + b1->error = node->errors[SNAT_OUT2IN_ERROR_UNSUPPORTED_PROTOCOL]; + next1 = SNAT_OUT2IN_NEXT_DROP; + } + } goto trace1; } @@ -1410,16 +949,6 @@ snat_out2in_node_fn (vlib_main_t * vm, next1 = SNAT_OUT2IN_NEXT_DROP; goto trace1; } - else - { - if (next_src_nat(sm, ip1, proto1, udp1->src_port, thread_index)) - { - next1 = SNAT_OUT2IN_NEXT_IN2OUT; - goto trace1; - } - create_bypass_for_fwd(sm, ip1, rx_fib_index1, thread_index); - goto trace1; - } } /* Create session initiated by host from external network */ @@ -1432,22 +961,8 @@ snat_out2in_node_fn (vlib_main_t * vm, } } else - { - if (PREDICT_FALSE (value1.value == ~0ULL)) - { - 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 - { - s1 = pool_elt_at_index ( - sm->per_thread_data[thread_index].sessions, - value1.value); - } - } + 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; @@ -1554,11 +1069,14 @@ snat_out2in_node_fn (vlib_main_t * vm, if (PREDICT_FALSE (proto0 == ~0)) { - s0 = snat_out2in_unknown_proto(sm, b0, ip0, rx_fib_index0, - thread_index, now, vm, node); - if (!sm->forwarding_enabled) - if (!s0) - next0 = SNAT_OUT2IN_NEXT_DROP; + if (nat_out2in_sm_unknown_proto(sm, b0, ip0, rx_fib_index0)) + { + if (!sm->forwarding_enabled) + { + b0->error = node->errors[SNAT_OUT2IN_ERROR_UNSUPPORTED_PROTOCOL]; + next0 = SNAT_OUT2IN_NEXT_DROP; + } + } goto trace00; } @@ -1619,16 +1137,6 @@ snat_out2in_node_fn (vlib_main_t * vm, next0 = SNAT_OUT2IN_NEXT_DROP; goto trace00; } - else - { - if (next_src_nat(sm, ip0, proto0, udp0->src_port, thread_index)) - { - next0 = SNAT_OUT2IN_NEXT_IN2OUT; - goto trace00; - } - create_bypass_for_fwd(sm, ip0, rx_fib_index0, thread_index); - goto trace00; - } } /* Create session initiated by host from external network */ @@ -1641,22 +1149,8 @@ snat_out2in_node_fn (vlib_main_t * vm, } } else - { - if (PREDICT_FALSE (value0.value == ~0ULL)) - { - 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 - { - s0 = pool_elt_at_index ( - sm->per_thread_data[thread_index].sessions, - value0.value); - } - } + 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; @@ -1748,7 +1242,6 @@ VLIB_REGISTER_NODE (snat_out2in_node) = { [SNAT_OUT2IN_NEXT_LOOKUP] = "ip4-lookup", [SNAT_OUT2IN_NEXT_ICMP_ERROR] = "ip4-icmp-error", [SNAT_OUT2IN_NEXT_REASS] = "nat44-out2in-reass", - [SNAT_OUT2IN_NEXT_IN2OUT] = "nat44-in2out", }, }; VLIB_NODE_FUNCTION_MULTIARCH (snat_out2in_node, snat_out2in_node_fn); @@ -1869,18 +1362,8 @@ nat44_out2in_reass_node_fn (vlib_main_t * vm, { b0->error = node->errors[SNAT_OUT2IN_ERROR_NO_TRANSLATION]; next0 = SNAT_OUT2IN_NEXT_DROP; - goto trace0; - } - else - { - if (next_src_nat(sm, ip0, proto0, udp0->src_port, thread_index)) - { - next0 = SNAT_OUT2IN_NEXT_IN2OUT; - goto trace0; - } - create_bypass_for_fwd(sm, ip0, rx_fib_index0, thread_index); - goto trace0; } + goto trace0; } /* Create session initiated by host from external network */ @@ -2045,12 +1528,1303 @@ VLIB_REGISTER_NODE (nat44_out2in_reass_node) = { [SNAT_OUT2IN_NEXT_LOOKUP] = "ip4-lookup", [SNAT_OUT2IN_NEXT_ICMP_ERROR] = "ip4-icmp-error", [SNAT_OUT2IN_NEXT_REASS] = "nat44-out2in-reass", - [SNAT_OUT2IN_NEXT_IN2OUT] = "nat44-in2out", }, }; VLIB_NODE_FUNCTION_MULTIARCH (nat44_out2in_reass_node, nat44_out2in_reass_node_fn); +/*******************************/ +/*** endpoint-dependent mode ***/ +/*******************************/ +typedef enum { + NAT44_ED_OUT2IN_NEXT_DROP, + NAT44_ED_OUT2IN_NEXT_LOOKUP, + NAT44_ED_OUT2IN_NEXT_ICMP_ERROR, + NAT44_ED_OUT2IN_NEXT_IN2OUT, + NAT44_ED_OUT2IN_NEXT_SLOW_PATH, + NAT44_ED_OUT2IN_N_NEXT, +} nat44_ed_out2in_next_t; + +typedef struct { + u32 sw_if_index; + u32 next_index; + u32 session_index; + u32 is_slow_path; +} nat44_ed_out2in_trace_t; + +static u8 * +format_nat44_ed_out2in_trace (u8 * s, va_list * args) +{ + CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *); + CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *); + nat44_ed_out2in_trace_t *t = va_arg (*args, nat44_ed_out2in_trace_t *); + char * tag; + + tag = t->is_slow_path ? "NAT44_OUT2IN_SLOW_PATH" : "NAT44_OUT2IN_FAST_PATH"; + + s = format (s, "%s: sw_if_index %d, next index %d, session %d", tag, + t->sw_if_index, t->next_index, t->session_index); + + return s; +} + +static snat_session_t * +create_session_for_static_mapping_ed (snat_main_t * sm, + vlib_buffer_t *b, + snat_session_key_t l_key, + snat_session_key_t e_key, + vlib_node_runtime_t * node, + u32 thread_index, + twice_nat_type_t twice_nat, + u8 is_lb) +{ + snat_session_t *s; + snat_user_t *u; + ip4_header_t *ip; + udp_header_t *udp; + snat_main_per_thread_data_t *tsm = &sm->per_thread_data[thread_index]; + clib_bihash_kv_16_8_t kv; + snat_session_key_t eh_key; + u32 address_index; + + if (PREDICT_FALSE (maximum_sessions_exceeded(sm, thread_index))) + { + b->error = node->errors[SNAT_OUT2IN_ERROR_MAX_SESSIONS_EXCEEDED]; + nat_log_notice ("maximum sessions exceeded"); + return 0; + } + + u = nat_user_get_or_create (sm, &l_key.addr, l_key.fib_index, thread_index); + if (!u) + { + nat_log_warn ("create NAT user failed"); + return 0; + } + + s = nat_session_alloc_or_recycle (sm, u, thread_index); + if (!s) + { + nat_log_warn ("create NAT session failed"); + return 0; + } + + ip = vlib_buffer_get_current (b); + udp = ip4_next_header (ip); + + s->ext_host_addr.as_u32 = ip->src_address.as_u32; + s->ext_host_port = e_key.protocol == SNAT_PROTOCOL_ICMP ? 0 : udp->src_port; + s->flags |= SNAT_SESSION_FLAG_STATIC_MAPPING; + if (is_lb) + s->flags |= SNAT_SESSION_FLAG_LOAD_BALANCING; + s->flags |= SNAT_SESSION_FLAG_ENDPOINT_DEPENDENT; + s->outside_address_index = ~0; + s->out2in = e_key; + s->in2out = l_key; + s->in2out.protocol = s->out2in.protocol; + user_session_increment (sm, u, 1); + + /* Add to lookup tables */ + make_ed_kv (&kv, &e_key.addr, &s->ext_host_addr, ip->protocol, + e_key.fib_index, e_key.port, s->ext_host_port); + kv.value = s - tsm->sessions; + if (clib_bihash_add_del_16_8 (&tsm->out2in_ed, &kv, 1)) + nat_log_notice ("out2in-ed key add failed"); + + if (twice_nat == TWICE_NAT || (twice_nat == TWICE_NAT_SELF && + ip->src_address.as_u32 == l_key.addr.as_u32)) + { + eh_key.protocol = e_key.protocol; + if (snat_alloc_outside_address_and_port (sm->twice_nat_addresses, 0, + thread_index, &eh_key, + &address_index, + sm->port_per_thread, + tsm->snat_thread_index)) + { + b->error = node->errors[SNAT_OUT2IN_ERROR_OUT_OF_PORTS]; + nat44_delete_session (sm, s, thread_index); + if (clib_bihash_add_del_16_8 (&tsm->out2in_ed, &kv, 0)) + nat_log_notice ("out2in-ed key del failed"); + return 0; + } + s->ext_host_nat_addr.as_u32 = eh_key.addr.as_u32; + s->ext_host_nat_port = eh_key.port; + s->flags |= SNAT_SESSION_FLAG_TWICE_NAT; + make_ed_kv (&kv, &l_key.addr, &s->ext_host_nat_addr, ip->protocol, + l_key.fib_index, l_key.port, s->ext_host_nat_port); + } + else + { + make_ed_kv (&kv, &l_key.addr, &s->ext_host_addr, ip->protocol, + l_key.fib_index, l_key.port, s->ext_host_port); + } + kv.value = s - tsm->sessions; + if (clib_bihash_add_del_16_8 (&tsm->in2out_ed, &kv, 1)) + nat_log_notice ("in2out-ed key add failed"); + + return s; +} + +static_always_inline int +icmp_get_ed_key(ip4_header_t *ip0, nat_ed_ses_key_t *p_key0) +{ + icmp46_header_t *icmp0; + nat_ed_ses_key_t key0; + icmp_echo_header_t *echo0, *inner_echo0 = 0; + ip4_header_t *inner_ip0; + void *l4_header = 0; + icmp46_header_t *inner_icmp0; + + icmp0 = (icmp46_header_t *) ip4_next_header (ip0); + echo0 = (icmp_echo_header_t *)(icmp0+1); + + if (!icmp_is_error_message (icmp0)) + { + key0.proto = IP_PROTOCOL_ICMP; + key0.l_addr = ip0->dst_address; + key0.r_addr = ip0->src_address; + key0.l_port = echo0->identifier; + key0.r_port = 0; + } + else + { + inner_ip0 = (ip4_header_t *)(echo0+1); + l4_header = ip4_next_header (inner_ip0); + key0.proto = inner_ip0->protocol; + key0.l_addr = inner_ip0->src_address; + key0.r_addr = inner_ip0->dst_address; + switch (ip_proto_to_snat_proto (inner_ip0->protocol)) + { + case SNAT_PROTOCOL_ICMP: + inner_icmp0 = (icmp46_header_t*)l4_header; + inner_echo0 = (icmp_echo_header_t *)(inner_icmp0+1); + key0.l_port = inner_echo0->identifier; + key0.r_port = 0; + break; + case SNAT_PROTOCOL_UDP: + case SNAT_PROTOCOL_TCP: + key0.l_port = ((tcp_udp_header_t*)l4_header)->src_port; + key0.r_port = ((tcp_udp_header_t*)l4_header)->dst_port; + break; + default: + return -1; + } + } + *p_key0 = key0; + return 0; +} + +static int +next_src_nat (snat_main_t * sm, ip4_header_t * ip, u8 proto, u16 src_port, + u16 dst_port, u32 thread_index) +{ + clib_bihash_kv_16_8_t kv, value; + snat_main_per_thread_data_t *tsm = &sm->per_thread_data[thread_index]; + + make_ed_kv (&kv, &ip->src_address, &ip->dst_address, proto, + sm->inside_fib_index, src_port, dst_port); + if (!clib_bihash_search_16_8 (&tsm->in2out_ed, &kv, &value)) + return 1; + + return 0; +} + +static void +create_bypass_for_fwd(snat_main_t * sm, ip4_header_t * ip, u32 rx_fib_index, + u32 thread_index) +{ + nat_ed_ses_key_t key; + clib_bihash_kv_16_8_t kv, value; + udp_header_t *udp; + snat_user_t *u; + snat_session_t *s = 0; + snat_main_per_thread_data_t *tsm = &sm->per_thread_data[thread_index]; + f64 now = vlib_time_now (sm->vlib_main); + + if (ip->protocol == IP_PROTOCOL_ICMP) + { + if (icmp_get_ed_key (ip, &key)) + return; + } + else if (ip->protocol == IP_PROTOCOL_UDP || ip->protocol == IP_PROTOCOL_TCP) + { + udp = ip4_next_header(ip); + key.r_addr = ip->src_address; + key.l_addr = ip->dst_address; + key.proto = ip->protocol; + key.l_port = udp->dst_port; + key.r_port = udp->src_port; + } + else + { + key.r_addr = ip->src_address; + key.l_addr = ip->dst_address; + key.proto = ip->protocol; + key.l_port = key.r_port = 0; + } + key.fib_index = 0; + kv.key[0] = key.as_u64[0]; + kv.key[1] = key.as_u64[1]; + + if (!clib_bihash_search_16_8 (&tsm->in2out_ed, &kv, &value)) + { + s = pool_elt_at_index (tsm->sessions, value.value); + } + else + { + if (PREDICT_FALSE (maximum_sessions_exceeded(sm, thread_index))) + return; + + u = nat_user_get_or_create (sm, &ip->dst_address, sm->inside_fib_index, + thread_index); + if (!u) + { + nat_log_warn ("create NAT user failed"); + return; + } + + s = nat_session_alloc_or_recycle (sm, u, thread_index); + if (!s) + { + nat_log_warn ("create NAT session failed"); + return; + } + + s->ext_host_addr = key.r_addr; + s->ext_host_port = key.r_port; + s->flags |= SNAT_SESSION_FLAG_FWD_BYPASS; + s->outside_address_index = ~0; + s->out2in.addr = key.l_addr; + s->out2in.port = key.l_port; + s->out2in.protocol = ip_proto_to_snat_proto (key.proto); + s->out2in.fib_index = 0; + s->in2out = s->out2in; + user_session_increment (sm, u, 0); + + kv.value = s - tsm->sessions; + if (clib_bihash_add_del_16_8 (&tsm->in2out_ed, &kv, 1)) + nat_log_notice ("in2out_ed key add failed"); + } + + if (ip->protocol == IP_PROTOCOL_TCP) + { + tcp_header_t *tcp = ip4_next_header(ip); + if (nat44_set_tcp_session_state_o2i (sm, s, tcp, thread_index)) + return; + } + + /* Per-user LRU list maintenance */ + nat44_session_update_lru (sm, s, thread_index); + /* Accounting */ + nat44_session_update_counters (s, now, 0); +} + +u32 +icmp_match_out2in_ed (snat_main_t * sm, vlib_node_runtime_t * node, + u32 thread_index, vlib_buffer_t * b, ip4_header_t * ip, + u8 * p_proto, snat_session_key_t * p_value, + u8 * p_dont_translate, void * d, void * e) +{ + u32 next = ~0, sw_if_index, rx_fib_index; + icmp46_header_t *icmp; + nat_ed_ses_key_t key; + clib_bihash_kv_16_8_t kv, value; + snat_main_per_thread_data_t *tsm = &sm->per_thread_data[thread_index]; + snat_session_t *s = 0; + u8 dont_translate = 0, is_addr_only; + snat_session_key_t e_key, l_key; + + icmp = (icmp46_header_t *) ip4_next_header (ip); + sw_if_index = vnet_buffer(b)->sw_if_index[VLIB_RX]; + rx_fib_index = ip4_fib_table_get_index_for_sw_if_index (sw_if_index); + + if (icmp_get_ed_key (ip, &key)) + { + b->error = node->errors[SNAT_OUT2IN_ERROR_UNSUPPORTED_PROTOCOL]; + next = SNAT_OUT2IN_NEXT_DROP; + goto out; + } + key.fib_index = rx_fib_index; + kv.key[0] = key.as_u64[0]; + kv.key[1] = key.as_u64[1]; + + if (clib_bihash_search_16_8 (&tsm->out2in_ed, &kv, &value)) + { + /* Try to match static mapping */ + e_key.addr = ip->dst_address; + e_key.port = key.l_port; + e_key.protocol = ip_proto_to_snat_proto (key.proto); + e_key.fib_index = rx_fib_index; + if (snat_static_mapping_match(sm, e_key, &l_key, 1, &is_addr_only, 0, 0)) + { + if (!sm->forwarding_enabled) + { + /* Don't NAT packet aimed at the intfc address */ + if (PREDICT_FALSE(is_interface_addr(sm, node, sw_if_index, + ip->dst_address.as_u32))) + { + dont_translate = 1; + goto out; + } + b->error = node->errors[SNAT_OUT2IN_ERROR_NO_TRANSLATION]; + next = NAT44_ED_OUT2IN_NEXT_DROP; + goto out; + } + else + { + dont_translate = 1; + if (next_src_nat(sm, ip, key.proto, key.l_port, key.r_port, thread_index)) + { + next = NAT44_ED_OUT2IN_NEXT_IN2OUT; + goto out; + } + create_bypass_for_fwd(sm, ip, rx_fib_index, thread_index); + goto out; + } + } + + if (PREDICT_FALSE(icmp->type != ICMP4_echo_reply && + (icmp->type != ICMP4_echo_request || !is_addr_only))) + { + b->error = node->errors[SNAT_OUT2IN_ERROR_BAD_ICMP_TYPE]; + next = NAT44_ED_OUT2IN_NEXT_DROP; + goto out; + } + + /* Create session initiated by host from external network */ + s = create_session_for_static_mapping_ed(sm, b, l_key, e_key, node, + thread_index, 0, 0); + + if (!s) + { + next = NAT44_ED_OUT2IN_NEXT_DROP; + goto out; + } + } + else + { + if (PREDICT_FALSE(icmp->type != ICMP4_echo_reply && + icmp->type != ICMP4_echo_request && + !icmp_is_error_message (icmp))) + { + b->error = node->errors[SNAT_OUT2IN_ERROR_BAD_ICMP_TYPE]; + next = SNAT_OUT2IN_NEXT_DROP; + goto out; + } + + s = pool_elt_at_index (tsm->sessions, value.value); + } + + *p_proto = ip_proto_to_snat_proto (key.proto); +out: + if (s) + *p_value = s->in2out; + *p_dont_translate = dont_translate; + if (d) + *(snat_session_t**)d = s; + return next; +} + +static snat_session_t * +nat44_ed_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_node_runtime_t * node) +{ + clib_bihash_kv_8_8_t kv, value; + clib_bihash_kv_16_8_t s_kv, s_value; + snat_static_mapping_t *m; + u32 old_addr, new_addr; + ip_csum_t sum; + snat_session_t * s; + snat_main_per_thread_data_t *tsm = &sm->per_thread_data[thread_index]; + snat_user_t *u; + + old_addr = ip->dst_address.as_u32; + + make_ed_kv (&s_kv, &ip->dst_address, &ip->src_address, ip->protocol, + rx_fib_index, 0, 0); + + if (!clib_bihash_search_16_8 (&tsm->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; + } + else + { + if (PREDICT_FALSE (maximum_sessions_exceeded(sm, thread_index))) + { + b->error = node->errors[SNAT_OUT2IN_ERROR_MAX_SESSIONS_EXCEEDED]; + nat_log_notice ("maximum sessions exceeded"); + return 0; + } + + make_sm_kv (&kv, &ip->dst_address, 0, rx_fib_index, 0); + if (clib_bihash_search_8_8 (&sm->static_mapping_by_external, &kv, &value)) + { + b->error = node->errors[SNAT_OUT2IN_ERROR_NO_TRANSLATION]; + return 0; + } + + m = pool_elt_at_index (sm->static_mappings, value.value); + + new_addr = ip->dst_address.as_u32 = m->local_addr.as_u32; + + u = nat_user_get_or_create (sm, &ip->src_address, m->fib_index, + thread_index); + if (!u) + { + nat_log_warn ("create NAT user failed"); + return 0; + } + + /* Create a new session */ + s = nat_session_alloc_or_recycle (sm, u, thread_index); + if (!s) + { + nat_log_warn ("create NAT session failed"); + return 0; + } + + s->ext_host_addr.as_u32 = ip->src_address.as_u32; + s->flags |= SNAT_SESSION_FLAG_UNKNOWN_PROTO; + s->flags |= SNAT_SESSION_FLAG_STATIC_MAPPING; + s->flags |= SNAT_SESSION_FLAG_ENDPOINT_DEPENDENT; + s->outside_address_index = ~0; + s->out2in.addr.as_u32 = old_addr; + s->out2in.fib_index = rx_fib_index; + s->in2out.addr.as_u32 = new_addr; + s->in2out.fib_index = m->fib_index; + s->in2out.port = s->out2in.port = ip->protocol; + user_session_increment (sm, u, 1); + + /* Add to lookup tables */ + s_kv.value = s - tsm->sessions; + if (clib_bihash_add_del_16_8 (&tsm->out2in_ed, &s_kv, 1)) + nat_log_notice ("out2in key add failed"); + + make_ed_kv (&s_kv, &ip->dst_address, &ip->src_address, ip->protocol, + m->fib_index, 0, 0); + s_kv.value = s - tsm->sessions; + if (clib_bihash_add_del_16_8 (&tsm->in2out_ed, &s_kv, 1)) + nat_log_notice ("in2out key add failed"); + } + + /* 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); + + vnet_buffer(b)->sw_if_index[VLIB_TX] = s->in2out.fib_index; + + /* Accounting */ + nat44_session_update_counters (s, now, + vlib_buffer_length_in_chain (vm, b)); + /* Per-user LRU list maintenance */ + nat44_session_update_lru (sm, s, thread_index); + + return s; +} + +static inline uword +nat44_ed_out2in_node_fn_inline (vlib_main_t * vm, + vlib_node_runtime_t * node, + vlib_frame_t * frame, int is_slow_path) +{ + u32 n_left_from, *from, *to_next, pkts_processed = 0, stats_node_index; + nat44_ed_out2in_next_t next_index; + snat_main_t *sm = &snat_main; + f64 now = vlib_time_now (vm); + u32 thread_index = vlib_get_thread_index (); + snat_main_per_thread_data_t *tsm = &sm->per_thread_data[thread_index]; + + stats_node_index = is_slow_path ? nat44_ed_out2in_slowpath_node.index : + nat44_ed_out2in_node.index; + + from = vlib_frame_vector_args (frame); + n_left_from = frame->n_vectors; + next_index = node->cached_next_index; + + while (n_left_from > 0) + { + u32 n_left_to_next; + + vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next); + + while (n_left_from >= 4 && n_left_to_next >= 2) + { + u32 bi0, bi1; + vlib_buffer_t *b0, *b1; + u32 next0, sw_if_index0, rx_fib_index0, proto0, old_addr0, new_addr0; + u32 next1, sw_if_index1, rx_fib_index1, proto1, old_addr1, new_addr1; + u16 old_port0, new_port0, old_port1, new_port1; + ip4_header_t *ip0, *ip1; + udp_header_t *udp0, *udp1; + tcp_header_t *tcp0, *tcp1; + icmp46_header_t *icmp0, *icmp1; + snat_session_t *s0 = 0, *s1 = 0; + clib_bihash_kv_16_8_t kv0, value0, kv1, value1; + ip_csum_t sum0, sum1; + snat_session_key_t e_key0, l_key0, e_key1, l_key1; + u8 is_lb0, is_lb1; + twice_nat_type_t twice_nat0, twice_nat1; + + /* Prefetch next iteration. */ + { + vlib_buffer_t * p2, * p3; + + p2 = vlib_get_buffer (vm, from[2]); + p3 = vlib_get_buffer (vm, from[3]); + + vlib_prefetch_buffer_header (p2, LOAD); + vlib_prefetch_buffer_header (p3, LOAD); + + CLIB_PREFETCH (p2->data, CLIB_CACHE_LINE_BYTES, STORE); + CLIB_PREFETCH (p3->data, CLIB_CACHE_LINE_BYTES, STORE); + } + + /* speculatively enqueue b0 and b1 to the current next frame */ + to_next[0] = bi0 = from[0]; + to_next[1] = bi1 = from[1]; + from += 2; + to_next += 2; + n_left_from -= 2; + n_left_to_next -= 2; + + b0 = vlib_get_buffer (vm, bi0); + b1 = vlib_get_buffer (vm, bi1); + + next0 = NAT44_ED_OUT2IN_NEXT_LOOKUP; + vnet_buffer (b0)->snat.flags = 0; + ip0 = vlib_buffer_get_current (b0); + + sw_if_index0 = vnet_buffer(b0)->sw_if_index[VLIB_RX]; + rx_fib_index0 = fib_table_get_index_for_sw_if_index (FIB_PROTOCOL_IP4, + sw_if_index0); + + if (PREDICT_FALSE(ip0->ttl == 1)) + { + vnet_buffer (b0)->sw_if_index[VLIB_TX] = (u32) ~ 0; + icmp4_error_set_vnet_buffer (b0, ICMP4_time_exceeded, + ICMP4_time_exceeded_ttl_exceeded_in_transit, + 0); + next0 = NAT44_ED_OUT2IN_NEXT_ICMP_ERROR; + goto trace00; + } + + udp0 = ip4_next_header (ip0); + tcp0 = (tcp_header_t *) udp0; + icmp0 = (icmp46_header_t *) udp0; + proto0 = ip_proto_to_snat_proto (ip0->protocol); + + if (is_slow_path) + { + if (PREDICT_FALSE (proto0 == ~0)) + { + s0 = nat44_ed_out2in_unknown_proto(sm, b0, ip0, rx_fib_index0, + thread_index, now, vm, node); + if (!sm->forwarding_enabled) + { + if (!s0) + next0 = NAT44_ED_OUT2IN_NEXT_DROP; + goto trace00; + } + } + + if (PREDICT_FALSE (proto0 == SNAT_PROTOCOL_ICMP)) + { + next0 = icmp_out2in_slow_path + (sm, b0, ip0, icmp0, sw_if_index0, rx_fib_index0, node, + next0, now, thread_index, &s0); + goto trace00; + } + } + else + { + if (PREDICT_FALSE (proto0 == ~0 || proto0 == SNAT_PROTOCOL_ICMP)) + { + next0 = NAT44_ED_OUT2IN_NEXT_SLOW_PATH; + goto trace00; + } + + if (ip4_is_fragment (ip0)) + { + b0->error = node->errors[SNAT_OUT2IN_ERROR_DROP_FRAGMENT]; + next0 = NAT44_ED_OUT2IN_NEXT_DROP; + goto trace00; + } + } + + make_ed_kv (&kv0, &ip0->dst_address, &ip0->src_address, ip0->protocol, + rx_fib_index0, udp0->dst_port, udp0->src_port); + + if (clib_bihash_search_16_8 (&tsm->out2in_ed, &kv0, &value0)) + { + if (is_slow_path) + { + /* Try to match static mapping by external address and port, + destination address and port in packet */ + e_key0.addr = ip0->dst_address; + e_key0.port = udp0->dst_port; + e_key0.protocol = proto0; + e_key0.fib_index = rx_fib_index0; + if (snat_static_mapping_match(sm, e_key0, &l_key0, 1, 0, + &twice_nat0, &is_lb0)) + { + /* + * Send DHCP packets to the ipv4 stack, or we won't + * be able to use dhcp client on the outside interface + */ + if (PREDICT_FALSE (proto0 == SNAT_PROTOCOL_UDP + && (udp0->dst_port == + clib_host_to_net_u16(UDP_DST_PORT_dhcp_to_client)))) + { + vnet_feature_next + (vnet_buffer (b0)->sw_if_index[VLIB_RX], &next0, b0); + goto trace00; + } + + if (!sm->forwarding_enabled) + { + b0->error = node->errors[SNAT_OUT2IN_ERROR_NO_TRANSLATION]; + next0 = NAT44_ED_OUT2IN_NEXT_DROP; + } + else + { + if (next_src_nat(sm, ip0, ip0->protocol, + udp0->src_port, udp0->dst_port, + thread_index)) + { + next0 = NAT44_ED_OUT2IN_NEXT_IN2OUT; + goto trace00; + } + create_bypass_for_fwd(sm, ip0, rx_fib_index0, + thread_index); + } + goto trace00; + } + + /* Create session initiated by host from external network */ + s0 = create_session_for_static_mapping_ed(sm, b0, l_key0, + e_key0, node, + thread_index, + twice_nat0, is_lb0); + + if (!s0) + { + next0 = NAT44_ED_OUT2IN_NEXT_DROP; + goto trace00; + } + } + else + { + next0 = NAT44_ED_OUT2IN_NEXT_SLOW_PATH; + goto trace00; + } + } + else + { + s0 = pool_elt_at_index (tsm->sessions, value0.value); + } + + old_addr0 = ip0->dst_address.as_u32; + new_addr0 = ip0->dst_address.as_u32 = s0->in2out.addr.as_u32; + vnet_buffer(b0)->sw_if_index[VLIB_TX] = s0->in2out.fib_index; + + sum0 = ip0->checksum; + sum0 = ip_csum_update (sum0, old_addr0, new_addr0, ip4_header_t, + dst_address); + if (PREDICT_FALSE (is_twice_nat_session (s0))) + sum0 = ip_csum_update (sum0, ip0->src_address.as_u32, + s0->ext_host_nat_addr.as_u32, ip4_header_t, + src_address); + ip0->checksum = ip_csum_fold (sum0); + + if (PREDICT_TRUE (proto0 == SNAT_PROTOCOL_TCP)) + { + old_port0 = tcp0->dst_port; + new_port0 = tcp0->dst_port = s0->in2out.port; + + sum0 = tcp0->checksum; + sum0 = ip_csum_update (sum0, old_addr0, new_addr0, ip4_header_t, + dst_address); + sum0 = ip_csum_update (sum0, old_port0, new_port0, ip4_header_t, + length); + if (is_twice_nat_session (s0)) + { + sum0 = ip_csum_update (sum0, ip0->src_address.as_u32, + s0->ext_host_nat_addr.as_u32, + ip4_header_t, dst_address); + sum0 = ip_csum_update (sum0, tcp0->src_port, + s0->ext_host_nat_port, ip4_header_t, + length); + tcp0->src_port = s0->ext_host_nat_port; + ip0->src_address.as_u32 = s0->ext_host_nat_addr.as_u32; + } + tcp0->checksum = ip_csum_fold(sum0); + if (nat44_set_tcp_session_state_o2i (sm, s0, tcp0, thread_index)) + goto trace00; + } + else + { + udp0->dst_port = s0->in2out.port; + if (is_twice_nat_session (s0)) + { + udp0->src_port = s0->ext_host_nat_port; + ip0->src_address.as_u32 = s0->ext_host_nat_addr.as_u32; + } + udp0->checksum = 0; + } + + /* Accounting */ + nat44_session_update_counters (s0, now, + vlib_buffer_length_in_chain (vm, b0)); + /* Per-user LRU list maintenance */ + nat44_session_update_lru (sm, s0, thread_index); + + trace00: + if (PREDICT_FALSE((node->flags & VLIB_NODE_FLAG_TRACE) + && (b0->flags & VLIB_BUFFER_IS_TRACED))) + { + nat44_ed_out2in_trace_t *t = + vlib_add_trace (vm, node, b0, sizeof (*t)); + t->is_slow_path = is_slow_path; + t->sw_if_index = sw_if_index0; + t->next_index = next0; + t->session_index = ~0; + if (s0) + t->session_index = s0 - tsm->sessions; + } + + pkts_processed += next0 != NAT44_ED_OUT2IN_NEXT_DROP; + + next1 = NAT44_ED_OUT2IN_NEXT_LOOKUP; + vnet_buffer (b1)->snat.flags = 0; + ip1 = vlib_buffer_get_current (b1); + + sw_if_index1 = vnet_buffer(b1)->sw_if_index[VLIB_RX]; + rx_fib_index1 = fib_table_get_index_for_sw_if_index (FIB_PROTOCOL_IP4, + sw_if_index1); + + if (PREDICT_FALSE(ip1->ttl == 1)) + { + vnet_buffer (b1)->sw_if_index[VLIB_TX] = (u32) ~ 0; + icmp4_error_set_vnet_buffer (b1, ICMP4_time_exceeded, + ICMP4_time_exceeded_ttl_exceeded_in_transit, + 0); + next1 = NAT44_ED_OUT2IN_NEXT_ICMP_ERROR; + goto trace01; + } + + udp1 = ip4_next_header (ip1); + tcp1 = (tcp_header_t *) udp1; + icmp1 = (icmp46_header_t *) udp1; + proto1 = ip_proto_to_snat_proto (ip1->protocol); + + if (is_slow_path) + { + if (PREDICT_FALSE (proto1 == ~0)) + { + s1 = nat44_ed_out2in_unknown_proto(sm, b1, ip1, rx_fib_index1, + thread_index, now, vm, node); + if (!sm->forwarding_enabled) + { + if (!s1) + next1 = NAT44_ED_OUT2IN_NEXT_DROP; + goto trace01; + } + } + + if (PREDICT_FALSE (proto1 == SNAT_PROTOCOL_ICMP)) + { + next1 = icmp_out2in_slow_path + (sm, b1, ip1, icmp1, sw_if_index1, rx_fib_index1, node, + next1, now, thread_index, &s1); + goto trace01; + } + } + else + { + if (PREDICT_FALSE (proto1 == ~0 || proto1 == SNAT_PROTOCOL_ICMP)) + { + next1 = NAT44_ED_OUT2IN_NEXT_SLOW_PATH; + goto trace01; + } + + if (ip4_is_fragment (ip1)) + { + b1->error = node->errors[SNAT_OUT2IN_ERROR_DROP_FRAGMENT]; + next1 = NAT44_ED_OUT2IN_NEXT_DROP; + goto trace01; + } + } + + make_ed_kv (&kv1, &ip1->dst_address, &ip1->src_address, ip1->protocol, + rx_fib_index1, udp1->dst_port, udp1->src_port); + + if (clib_bihash_search_16_8 (&tsm->out2in_ed, &kv1, &value1)) + { + if (is_slow_path) + { + /* Try to match static mapping by external address and port, + destination address and port in packet */ + e_key1.addr = ip1->dst_address; + e_key1.port = udp1->dst_port; + e_key1.protocol = proto1; + e_key1.fib_index = rx_fib_index1; + if (snat_static_mapping_match(sm, e_key1, &l_key1, 1, 0, + &twice_nat1, &is_lb1)) + { + /* + * Send DHCP packets to the ipv4 stack, or we won't + * be able to use dhcp client on the outside interface + */ + if (PREDICT_FALSE (proto1 == SNAT_PROTOCOL_UDP + && (udp1->dst_port == + clib_host_to_net_u16(UDP_DST_PORT_dhcp_to_client)))) + { + vnet_feature_next + (vnet_buffer (b1)->sw_if_index[VLIB_RX], &next1, b1); + goto trace01; + } + + if (!sm->forwarding_enabled) + { + b1->error = node->errors[SNAT_OUT2IN_ERROR_NO_TRANSLATION]; + next1 = NAT44_ED_OUT2IN_NEXT_DROP; + } + else + { + if (next_src_nat(sm, ip1, ip1->protocol, + udp1->src_port, udp1->dst_port, + thread_index)) + { + next1 = NAT44_ED_OUT2IN_NEXT_IN2OUT; + goto trace01; + } + create_bypass_for_fwd(sm, ip1, rx_fib_index1, + thread_index); + } + goto trace01; + } + + /* Create session initiated by host from external network */ + s1 = create_session_for_static_mapping_ed(sm, b1, l_key1, + e_key1, node, + thread_index, + twice_nat1, is_lb1); + + if (!s1) + { + next1 = NAT44_ED_OUT2IN_NEXT_DROP; + goto trace01; + } + } + else + { + next1 = NAT44_ED_OUT2IN_NEXT_SLOW_PATH; + goto trace01; + } + } + else + { + s1 = pool_elt_at_index (tsm->sessions, value1.value); + } + + old_addr1 = ip1->dst_address.as_u32; + new_addr1 = ip1->dst_address.as_u32 = s1->in2out.addr.as_u32; + vnet_buffer(b1)->sw_if_index[VLIB_TX] = s1->in2out.fib_index; + + sum1 = ip1->checksum; + sum1 = ip_csum_update (sum1, old_addr1, new_addr1, ip4_header_t, + dst_address); + if (PREDICT_FALSE (is_twice_nat_session (s1))) + sum1 = ip_csum_update (sum1, ip1->src_address.as_u32, + s1->ext_host_nat_addr.as_u32, ip4_header_t, + src_address); + ip1->checksum = ip_csum_fold (sum1); + + if (PREDICT_TRUE (proto1 == SNAT_PROTOCOL_TCP)) + { + old_port1 = tcp1->dst_port; + new_port1 = tcp1->dst_port = s1->in2out.port; + + sum1 = tcp1->checksum; + sum1 = ip_csum_update (sum1, old_addr1, new_addr1, ip4_header_t, + dst_address); + sum1 = ip_csum_update (sum1, old_port1, new_port1, ip4_header_t, + length); + if (is_twice_nat_session (s1)) + { + sum1 = ip_csum_update (sum1, ip1->src_address.as_u32, + s1->ext_host_nat_addr.as_u32, + ip4_header_t, dst_address); + sum1 = ip_csum_update (sum1, tcp1->src_port, + s1->ext_host_nat_port, ip4_header_t, + length); + tcp1->src_port = s1->ext_host_nat_port; + ip1->src_address.as_u32 = s1->ext_host_nat_addr.as_u32; + } + tcp1->checksum = ip_csum_fold(sum1); + if (nat44_set_tcp_session_state_o2i (sm, s1, tcp1, thread_index)) + goto trace01; + } + else + { + udp1->dst_port = s1->in2out.port; + if (is_twice_nat_session (s1)) + { + udp1->src_port = s1->ext_host_nat_port; + ip1->src_address.as_u32 = s1->ext_host_nat_addr.as_u32; + } + udp1->checksum = 0; + } + + /* Accounting */ + nat44_session_update_counters (s1, now, + vlib_buffer_length_in_chain (vm, b1)); + /* Per-user LRU list maintenance */ + nat44_session_update_lru (sm, s1, thread_index); + + trace01: + if (PREDICT_FALSE((node->flags & VLIB_NODE_FLAG_TRACE) + && (b1->flags & VLIB_BUFFER_IS_TRACED))) + { + nat44_ed_out2in_trace_t *t = + vlib_add_trace (vm, node, b1, sizeof (*t)); + t->is_slow_path = is_slow_path; + t->sw_if_index = sw_if_index1; + t->next_index = next1; + t->session_index = ~0; + if (s1) + t->session_index = s1 - tsm->sessions; + } + + pkts_processed += next1 != NAT44_ED_OUT2IN_NEXT_DROP; + + /* verify speculative enqueues, maybe switch current next frame */ + vlib_validate_buffer_enqueue_x2 (vm, node, next_index, + to_next, n_left_to_next, + bi0, bi1, next0, next1); + } + + while (n_left_from > 0 && n_left_to_next > 0) + { + u32 bi0; + vlib_buffer_t *b0; + u32 next0, sw_if_index0, rx_fib_index0, proto0, old_addr0, new_addr0; + u16 old_port0, new_port0; + ip4_header_t *ip0; + udp_header_t *udp0; + tcp_header_t *tcp0; + icmp46_header_t * icmp0; + snat_session_t *s0 = 0; + clib_bihash_kv_16_8_t kv0, value0; + ip_csum_t sum0; + snat_session_key_t e_key0, l_key0; + u8 is_lb0; + twice_nat_type_t twice_nat0; + + /* speculatively enqueue b0 to the current next frame */ + bi0 = from[0]; + to_next[0] = bi0; + from += 1; + to_next += 1; + n_left_from -= 1; + n_left_to_next -= 1; + + b0 = vlib_get_buffer (vm, bi0); + next0 = NAT44_ED_OUT2IN_NEXT_LOOKUP; + vnet_buffer (b0)->snat.flags = 0; + ip0 = vlib_buffer_get_current (b0); + + sw_if_index0 = vnet_buffer(b0)->sw_if_index[VLIB_RX]; + rx_fib_index0 = fib_table_get_index_for_sw_if_index (FIB_PROTOCOL_IP4, + sw_if_index0); + + if (PREDICT_FALSE(ip0->ttl == 1)) + { + vnet_buffer (b0)->sw_if_index[VLIB_TX] = (u32) ~ 0; + icmp4_error_set_vnet_buffer (b0, ICMP4_time_exceeded, + ICMP4_time_exceeded_ttl_exceeded_in_transit, + 0); + next0 = NAT44_ED_OUT2IN_NEXT_ICMP_ERROR; + goto trace0; + } + + udp0 = ip4_next_header (ip0); + tcp0 = (tcp_header_t *) udp0; + icmp0 = (icmp46_header_t *) udp0; + proto0 = ip_proto_to_snat_proto (ip0->protocol); + + if (is_slow_path) + { + if (PREDICT_FALSE (proto0 == ~0)) + { + s0 = nat44_ed_out2in_unknown_proto(sm, b0, ip0, rx_fib_index0, + thread_index, now, vm, node); + if (!sm->forwarding_enabled) + { + if (!s0) + next0 = NAT44_ED_OUT2IN_NEXT_DROP; + goto trace0; + } + } + + if (PREDICT_FALSE (proto0 == SNAT_PROTOCOL_ICMP)) + { + next0 = icmp_out2in_slow_path + (sm, b0, ip0, icmp0, sw_if_index0, rx_fib_index0, node, + next0, now, thread_index, &s0); + goto trace0; + } + } + else + { + if (PREDICT_FALSE (proto0 == ~0 || proto0 == SNAT_PROTOCOL_ICMP)) + { + next0 = NAT44_ED_OUT2IN_NEXT_SLOW_PATH; + goto trace0; + } + + if (ip4_is_fragment (ip0)) + { + b0->error = node->errors[SNAT_OUT2IN_ERROR_DROP_FRAGMENT]; + next0 = NAT44_ED_OUT2IN_NEXT_DROP; + goto trace0; + } + } + + make_ed_kv (&kv0, &ip0->dst_address, &ip0->src_address, ip0->protocol, + rx_fib_index0, udp0->dst_port, udp0->src_port); + + if (clib_bihash_search_16_8 (&tsm->out2in_ed, &kv0, &value0)) + { + if (is_slow_path) + { + /* Try to match static mapping by external address and port, + destination address and port in packet */ + e_key0.addr = ip0->dst_address; + e_key0.port = udp0->dst_port; + e_key0.protocol = proto0; + e_key0.fib_index = rx_fib_index0; + if (snat_static_mapping_match(sm, e_key0, &l_key0, 1, 0, + &twice_nat0, &is_lb0)) + { + /* + * Send DHCP packets to the ipv4 stack, or we won't + * be able to use dhcp client on the outside interface + */ + if (PREDICT_FALSE (proto0 == SNAT_PROTOCOL_UDP + && (udp0->dst_port == + clib_host_to_net_u16(UDP_DST_PORT_dhcp_to_client)))) + { + vnet_feature_next + (vnet_buffer (b0)->sw_if_index[VLIB_RX], &next0, b0); + goto trace0; + } + + if (!sm->forwarding_enabled) + { + b0->error = node->errors[SNAT_OUT2IN_ERROR_NO_TRANSLATION]; + next0 = NAT44_ED_OUT2IN_NEXT_DROP; + } + else + { + if (next_src_nat(sm, ip0, ip0->protocol, + udp0->src_port, udp0->dst_port, + thread_index)) + { + next0 = NAT44_ED_OUT2IN_NEXT_IN2OUT; + goto trace0; + } + create_bypass_for_fwd(sm, ip0, rx_fib_index0, + thread_index); + } + goto trace0; + } + + /* Create session initiated by host from external network */ + s0 = create_session_for_static_mapping_ed(sm, b0, l_key0, + e_key0, node, + thread_index, + twice_nat0, is_lb0); + + if (!s0) + { + next0 = NAT44_ED_OUT2IN_NEXT_DROP; + goto trace0; + } + } + else + { + next0 = NAT44_ED_OUT2IN_NEXT_SLOW_PATH; + goto trace0; + } + } + else + { + s0 = pool_elt_at_index (tsm->sessions, value0.value); + } + + old_addr0 = ip0->dst_address.as_u32; + new_addr0 = ip0->dst_address.as_u32 = s0->in2out.addr.as_u32; + vnet_buffer(b0)->sw_if_index[VLIB_TX] = s0->in2out.fib_index; + + sum0 = ip0->checksum; + sum0 = ip_csum_update (sum0, old_addr0, new_addr0, ip4_header_t, + dst_address); + if (PREDICT_FALSE (is_twice_nat_session (s0))) + sum0 = ip_csum_update (sum0, ip0->src_address.as_u32, + s0->ext_host_nat_addr.as_u32, ip4_header_t, + src_address); + ip0->checksum = ip_csum_fold (sum0); + + if (PREDICT_TRUE (proto0 == SNAT_PROTOCOL_TCP)) + { + old_port0 = tcp0->dst_port; + new_port0 = tcp0->dst_port = s0->in2out.port; + + sum0 = tcp0->checksum; + sum0 = ip_csum_update (sum0, old_addr0, new_addr0, ip4_header_t, + dst_address); + sum0 = ip_csum_update (sum0, old_port0, new_port0, ip4_header_t, + length); + if (is_twice_nat_session (s0)) + { + sum0 = ip_csum_update (sum0, ip0->src_address.as_u32, + s0->ext_host_nat_addr.as_u32, + ip4_header_t, dst_address); + sum0 = ip_csum_update (sum0, tcp0->src_port, + s0->ext_host_nat_port, ip4_header_t, + length); + tcp0->src_port = s0->ext_host_nat_port; + ip0->src_address.as_u32 = s0->ext_host_nat_addr.as_u32; + } + tcp0->checksum = ip_csum_fold(sum0); + if (nat44_set_tcp_session_state_o2i (sm, s0, tcp0, thread_index)) + goto trace0; + } + else + { + udp0->dst_port = s0->in2out.port; + if (is_twice_nat_session (s0)) + { + udp0->src_port = s0->ext_host_nat_port; + ip0->src_address.as_u32 = s0->ext_host_nat_addr.as_u32; + } + udp0->checksum = 0; + } + + /* Accounting */ + nat44_session_update_counters (s0, now, + vlib_buffer_length_in_chain (vm, b0)); + /* Per-user LRU list maintenance */ + nat44_session_update_lru (sm, s0, thread_index); + + trace0: + if (PREDICT_FALSE((node->flags & VLIB_NODE_FLAG_TRACE) + && (b0->flags & VLIB_BUFFER_IS_TRACED))) + { + nat44_ed_out2in_trace_t *t = + vlib_add_trace (vm, node, b0, sizeof (*t)); + t->is_slow_path = is_slow_path; + t->sw_if_index = sw_if_index0; + t->next_index = next0; + t->session_index = ~0; + if (s0) + t->session_index = s0 - tsm->sessions; + } + + pkts_processed += next0 != NAT44_ED_OUT2IN_NEXT_DROP; + + /* verify speculative enqueue, maybe switch current next frame */ + vlib_validate_buffer_enqueue_x1 (vm, node, next_index, + to_next, n_left_to_next, + bi0, next0); + } + + vlib_put_next_frame (vm, node, next_index, n_left_to_next); + } + + vlib_node_increment_counter (vm, stats_node_index, + SNAT_OUT2IN_ERROR_OUT2IN_PACKETS, + pkts_processed); + return frame->n_vectors; +} + +static uword +nat44_ed_out2in_fast_path_fn (vlib_main_t * vm, + vlib_node_runtime_t * node, + vlib_frame_t * frame) +{ + return nat44_ed_out2in_node_fn_inline (vm, node, frame, 0); +} + +VLIB_REGISTER_NODE (nat44_ed_out2in_node) = { + .function = nat44_ed_out2in_fast_path_fn, + .name = "nat44-ed-out2in", + .vector_size = sizeof (u32), + .format_trace = format_nat44_ed_out2in_trace, + .type = VLIB_NODE_TYPE_INTERNAL, + + .n_errors = ARRAY_LEN(snat_out2in_error_strings), + .error_strings = snat_out2in_error_strings, + + .runtime_data_bytes = sizeof (snat_runtime_t), + + .n_next_nodes = NAT44_ED_OUT2IN_N_NEXT, + + /* edit / add dispositions here */ + .next_nodes = { + [NAT44_ED_OUT2IN_NEXT_DROP] = "error-drop", + [NAT44_ED_OUT2IN_NEXT_LOOKUP] = "ip4-lookup", + [NAT44_ED_OUT2IN_NEXT_SLOW_PATH] = "nat44-ed-out2in-slowpath", + [NAT44_ED_OUT2IN_NEXT_ICMP_ERROR] = "ip4-icmp-error", + [NAT44_ED_OUT2IN_NEXT_IN2OUT] = "nat44-ed-in2out", + }, +}; + +VLIB_NODE_FUNCTION_MULTIARCH (nat44_ed_out2in_node, nat44_ed_out2in_fast_path_fn); + +static uword +nat44_ed_out2in_slow_path_fn (vlib_main_t * vm, + vlib_node_runtime_t * node, + vlib_frame_t * frame) +{ + return nat44_ed_out2in_node_fn_inline (vm, node, frame, 1); +} + +VLIB_REGISTER_NODE (nat44_ed_out2in_slowpath_node) = { + .function = nat44_ed_out2in_slow_path_fn, + .name = "nat44-ed-out2in-slowpath", + .vector_size = sizeof (u32), + .format_trace = format_nat44_ed_out2in_trace, + .type = VLIB_NODE_TYPE_INTERNAL, + + .n_errors = ARRAY_LEN(snat_out2in_error_strings), + .error_strings = snat_out2in_error_strings, + + .runtime_data_bytes = sizeof (snat_runtime_t), + + .n_next_nodes = NAT44_ED_OUT2IN_N_NEXT, + + /* edit / add dispositions here */ + .next_nodes = { + [NAT44_ED_OUT2IN_NEXT_DROP] = "error-drop", + [NAT44_ED_OUT2IN_NEXT_LOOKUP] = "ip4-lookup", + [NAT44_ED_OUT2IN_NEXT_SLOW_PATH] = "nat44-ed-out2in-slowpath", + [NAT44_ED_OUT2IN_NEXT_ICMP_ERROR] = "ip4-icmp-error", + [NAT44_ED_OUT2IN_NEXT_IN2OUT] = "nat44-ed-in2out", + }, +}; + +VLIB_NODE_FUNCTION_MULTIARCH (nat44_ed_out2in_slowpath_node, + nat44_ed_out2in_slow_path_fn); + /**************************/ /*** deterministic mode ***/ /**************************/ @@ -2535,7 +3309,6 @@ VLIB_REGISTER_NODE (snat_det_out2in_node) = { [SNAT_OUT2IN_NEXT_LOOKUP] = "ip4-lookup", [SNAT_OUT2IN_NEXT_ICMP_ERROR] = "ip4-icmp-error", [SNAT_OUT2IN_NEXT_REASS] = "nat44-out2in-reass", - [SNAT_OUT2IN_NEXT_IN2OUT] = "nat44-in2out", }, }; VLIB_NODE_FUNCTION_MULTIARCH (snat_det_out2in_node, snat_det_out2in_node_fn); @@ -3058,7 +3831,6 @@ VLIB_REGISTER_NODE (snat_out2in_fast_node) = { [SNAT_OUT2IN_NEXT_DROP] = "error-drop", [SNAT_OUT2IN_NEXT_ICMP_ERROR] = "ip4-icmp-error", [SNAT_OUT2IN_NEXT_REASS] = "nat44-out2in-reass", - [SNAT_OUT2IN_NEXT_IN2OUT] = "nat44-in2out", }, }; VLIB_NODE_FUNCTION_MULTIARCH (snat_out2in_fast_node, snat_out2in_fast_node_fn); diff --git a/src/scripts/vnet/nat44_lb b/src/scripts/vnet/nat44_lb new file mode 100644 index 00000000000..f66c13ce99f --- /dev/null +++ b/src/scripts/vnet/nat44_lb @@ -0,0 +1,48 @@ +create packet-generator interface pg0 +create packet-generator interface pg1 + +packet-generator new { + name f1 + limit 1000000 + node ip4-input + size 64-64 + no-recycle + worker 0 + interface pg1 + data { + UDP: 172.16.1.11 -> 172.16.1.3 + UDP: 3001 -> 3000 + length 128 checksum 0 incrementing 1 + } +} + + +packet-generator new { + name f2 + limit 1000000 + node ip4-input + size 64-64 + no-recycle + worker 1 + interface pg1 + data { + UDP: 172.16.1.10 -> 172.16.1.3 + UDP: 3001 -> 3000 + length 128 checksum 0 incrementing 1 + } +} + +nat44 add address 172.16.1.3 +nat44 add load-balancing static mapping protocol udp external 172.16.1.3:3000 local 10.0.0.10:30000 probability 25 local 10.0.0.11:30000 probability 25 local 10.0.0.12:30000 probability 25 local 10.0.0.13:30000 probability 25 +set int ip address pg0 10.0.0.1/24 +set int ip address pg1 172.16.1.1/24 +set int state pg0 up +set int state pg1 up +set ip arp static pg0 10.0.0.10 abcd.abcd.abcd +set ip arp static pg0 10.0.0.11 abcd.abcd.abce +set ip arp static pg0 10.0.0.12 abce.abcd.abcd +set ip arp static pg0 10.0.0.13 abce.abcd.abce +set ip arp static pg1 172.16.1.10 cdef.abcd.abcd +set ip arp static pg1 172.16.1.11 cdef.abcd.abce +set int nat44 in pg0 out pg1 +trace add pg-input 10 diff --git a/src/scripts/vnet/nat44_static_with_port b/src/scripts/vnet/nat44_static_with_port index 15bef1be3ba..25e16e94d66 100644 --- a/src/scripts/vnet/nat44_static_with_port +++ b/src/scripts/vnet/nat44_static_with_port @@ -33,7 +33,7 @@ packet-generator new { } nat44 add address 172.16.1.3 -nat44 add static mapping local 10.0.0.3 3000 external 172.16.1.3 3000 +nat44 add static mapping udp local 10.0.0.3 3000 external 172.16.1.3 3000 set int ip address pg0 10.0.0.1/24 set int ip address pg1 172.16.1.1/24 set int state pg0 up diff --git a/test/test_nat.py b/test/test_nat.py index ad2b9649b7d..8012350b6c6 100644 --- a/test/test_nat.py +++ b/test/test_nat.py @@ -25,6 +25,174 @@ from util import mactobinary class MethodHolder(VppTestCase): """ NAT create capture and verify method holder """ + def clear_nat44(self): + """ + Clear NAT44 configuration. + """ + if hasattr(self, 'pg7') and hasattr(self, 'pg8'): + # I found no elegant way to do this + self.vapi.ip_add_del_route( + dst_address=self.pg7.remote_ip4n, + dst_address_length=32, + next_hop_address=self.pg7.remote_ip4n, + next_hop_sw_if_index=self.pg7.sw_if_index, + is_add=0) + self.vapi.ip_add_del_route( + dst_address=self.pg8.remote_ip4n, + dst_address_length=32, + next_hop_address=self.pg8.remote_ip4n, + next_hop_sw_if_index=self.pg8.sw_if_index, + is_add=0) + + for intf in [self.pg7, self.pg8]: + neighbors = self.vapi.ip_neighbor_dump(intf.sw_if_index) + for n in neighbors: + self.vapi.ip_neighbor_add_del(intf.sw_if_index, + n.mac_address, + n.ip_address, + is_add=0) + + if self.pg7.has_ip4_config: + self.pg7.unconfig_ip4() + + self.vapi.nat44_forwarding_enable_disable(0) + + interfaces = self.vapi.nat44_interface_addr_dump() + for intf in interfaces: + self.vapi.nat44_add_interface_addr(intf.sw_if_index, + twice_nat=intf.twice_nat, + is_add=0) + + self.vapi.nat_ipfix(enable=0, src_port=self.ipfix_src_port, + domain_id=self.ipfix_domain_id) + self.ipfix_src_port = 4739 + self.ipfix_domain_id = 1 + + interfaces = self.vapi.nat44_interface_dump() + for intf in interfaces: + if intf.is_inside > 1: + self.vapi.nat44_interface_add_del_feature(intf.sw_if_index, + 0, + is_add=0) + self.vapi.nat44_interface_add_del_feature(intf.sw_if_index, + intf.is_inside, + is_add=0) + + interfaces = self.vapi.nat44_interface_output_feature_dump() + for intf in interfaces: + self.vapi.nat44_interface_add_del_output_feature(intf.sw_if_index, + intf.is_inside, + is_add=0) + + static_mappings = self.vapi.nat44_static_mapping_dump() + for sm in static_mappings: + self.vapi.nat44_add_del_static_mapping( + sm.local_ip_address, + sm.external_ip_address, + local_port=sm.local_port, + external_port=sm.external_port, + addr_only=sm.addr_only, + vrf_id=sm.vrf_id, + protocol=sm.protocol, + twice_nat=sm.twice_nat, + self_twice_nat=sm.self_twice_nat, + out2in_only=sm.out2in_only, + tag=sm.tag, + external_sw_if_index=sm.external_sw_if_index, + 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, + vrf_id=lb_sm.vrf_id, + twice_nat=lb_sm.twice_nat, + self_twice_nat=lb_sm.self_twice_nat, + out2in_only=lb_sm.out2in_only, + tag=lb_sm.tag, + is_add=0, + local_num=0, + locals=[]) + + identity_mappings = self.vapi.nat44_identity_mapping_dump() + for id_m in identity_mappings: + self.vapi.nat44_add_del_identity_mapping( + addr_only=id_m.addr_only, + ip=id_m.ip_address, + port=id_m.port, + sw_if_index=id_m.sw_if_index, + vrf_id=id_m.vrf_id, + protocol=id_m.protocol, + is_add=0) + + adresses = self.vapi.nat44_address_dump() + for addr in adresses: + self.vapi.nat44_add_del_address_range(addr.ip_address, + addr.ip_address, + twice_nat=addr.twice_nat, + is_add=0) + + self.vapi.nat_set_reass() + self.vapi.nat_set_reass(is_ip6=1) + + def nat44_add_static_mapping(self, local_ip, external_ip='0.0.0.0', + local_port=0, external_port=0, vrf_id=0, + is_add=1, external_sw_if_index=0xFFFFFFFF, + proto=0, twice_nat=0, self_twice_nat=0, + out2in_only=0, tag=""): + """ + Add/delete NAT44 static mapping + + :param local_ip: Local IP address + :param external_ip: External IP address + :param local_port: Local port number (Optional) + :param external_port: External port number (Optional) + :param vrf_id: VRF ID (Default 0) + :param is_add: 1 if add, 0 if delete (Default add) + :param external_sw_if_index: External interface instead of IP address + :param proto: IP protocol (Mandatory if port specified) + :param twice_nat: 1 if translate external host address and port + :param self_twice_nat: 1 if translate external host address and port + whenever external host address equals + local address of internal host + :param out2in_only: if 1 rule is matching only out2in direction + :param tag: Opaque string tag + """ + addr_only = 1 + if local_port and external_port: + addr_only = 0 + l_ip = socket.inet_pton(socket.AF_INET, local_ip) + e_ip = socket.inet_pton(socket.AF_INET, external_ip) + self.vapi.nat44_add_del_static_mapping( + l_ip, + e_ip, + external_sw_if_index, + local_port, + external_port, + addr_only, + vrf_id, + proto, + twice_nat, + self_twice_nat, + out2in_only, + tag, + is_add) + + def nat44_add_address(self, ip, is_add=1, vrf_id=0xFFFFFFFF, twice_nat=0): + """ + Add/delete NAT44 address + + :param ip: IP address + :param is_add: 1 if add, 0 if delete (Default add) + :param twice_nat: twice NAT address for extenal hosts + """ + nat_addr = socket.inet_pton(socket.AF_INET, ip) + self.vapi.nat44_add_del_address_range(nat_addr, nat_addr, is_add, + vrf_id=vrf_id, + twice_nat=twice_nat) + def create_stream_in(self, in_if, out_if, dst_ip=None, ttl=64): """ Create packet stream for inside network @@ -895,215 +1063,50 @@ class TestNAT44(MethodHolder): super(TestNAT44, cls).tearDownClass() raise - def clear_nat44(self): - """ - Clear NAT44 configuration. - """ - # I found no elegant way to do this - self.vapi.ip_add_del_route(dst_address=self.pg7.remote_ip4n, - dst_address_length=32, - next_hop_address=self.pg7.remote_ip4n, - next_hop_sw_if_index=self.pg7.sw_if_index, - is_add=0) - self.vapi.ip_add_del_route(dst_address=self.pg8.remote_ip4n, - dst_address_length=32, - next_hop_address=self.pg8.remote_ip4n, - next_hop_sw_if_index=self.pg8.sw_if_index, - is_add=0) - - for intf in [self.pg7, self.pg8]: - neighbors = self.vapi.ip_neighbor_dump(intf.sw_if_index) - for n in neighbors: - self.vapi.ip_neighbor_add_del(intf.sw_if_index, - n.mac_address, - n.ip_address, - is_add=0) + def test_dynamic(self): + """ NAT44 dynamic translation test """ - if self.pg7.has_ip4_config: - self.pg7.unconfig_ip4() + self.nat44_add_address(self.nat_addr) + 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) - self.vapi.nat44_forwarding_enable_disable(0) + # in2out + pkts = self.create_stream_in(self.pg0, self.pg1) + self.pg0.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = self.pg1.get_capture(len(pkts)) + self.verify_capture_out(capture) - interfaces = self.vapi.nat44_interface_addr_dump() - for intf in interfaces: - self.vapi.nat44_add_interface_addr(intf.sw_if_index, - twice_nat=intf.twice_nat, - is_add=0) + # out2in + pkts = self.create_stream_out(self.pg1) + self.pg1.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = self.pg0.get_capture(len(pkts)) + self.verify_capture_in(capture, self.pg0) - self.vapi.nat_ipfix(enable=0, src_port=self.ipfix_src_port, - domain_id=self.ipfix_domain_id) - self.ipfix_src_port = 4739 - self.ipfix_domain_id = 1 + def test_dynamic_icmp_errors_in2out_ttl_1(self): + """ NAT44 handling of client packets with TTL=1 """ - interfaces = self.vapi.nat44_interface_dump() - for intf in interfaces: - if intf.is_inside > 1: - self.vapi.nat44_interface_add_del_feature(intf.sw_if_index, - 0, - is_add=0) - self.vapi.nat44_interface_add_del_feature(intf.sw_if_index, - intf.is_inside, - is_add=0) + self.nat44_add_address(self.nat_addr) + 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) - interfaces = self.vapi.nat44_interface_output_feature_dump() - for intf in interfaces: - self.vapi.nat44_interface_add_del_output_feature(intf.sw_if_index, - intf.is_inside, - is_add=0) + # Client side - generate traffic + pkts = self.create_stream_in(self.pg0, self.pg1, ttl=1) + self.pg0.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() - static_mappings = self.vapi.nat44_static_mapping_dump() - for sm in static_mappings: - self.vapi.nat44_add_del_static_mapping( - sm.local_ip_address, - sm.external_ip_address, - local_port=sm.local_port, - external_port=sm.external_port, - addr_only=sm.addr_only, - vrf_id=sm.vrf_id, - protocol=sm.protocol, - twice_nat=sm.twice_nat, - self_twice_nat=sm.self_twice_nat, - out2in_only=sm.out2in_only, - tag=sm.tag, - external_sw_if_index=sm.external_sw_if_index, - is_add=0) + # Client side - verify ICMP type 11 packets + capture = self.pg0.get_capture(len(pkts)) + self.verify_capture_in_with_icmp_errors(capture, self.pg0) - 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, - vrf_id=lb_sm.vrf_id, - twice_nat=lb_sm.twice_nat, - self_twice_nat=lb_sm.self_twice_nat, - out2in_only=lb_sm.out2in_only, - tag=lb_sm.tag, - is_add=0, - local_num=0, - locals=[]) - - identity_mappings = self.vapi.nat44_identity_mapping_dump() - for id_m in identity_mappings: - self.vapi.nat44_add_del_identity_mapping( - addr_only=id_m.addr_only, - ip=id_m.ip_address, - port=id_m.port, - sw_if_index=id_m.sw_if_index, - vrf_id=id_m.vrf_id, - protocol=id_m.protocol, - is_add=0) - - adresses = self.vapi.nat44_address_dump() - for addr in adresses: - self.vapi.nat44_add_del_address_range(addr.ip_address, - addr.ip_address, - twice_nat=addr.twice_nat, - is_add=0) - - self.vapi.nat_set_reass() - self.vapi.nat_set_reass(is_ip6=1) - - def nat44_add_static_mapping(self, local_ip, external_ip='0.0.0.0', - local_port=0, external_port=0, vrf_id=0, - is_add=1, external_sw_if_index=0xFFFFFFFF, - proto=0, twice_nat=0, self_twice_nat=0, - out2in_only=0, tag=""): - """ - Add/delete NAT44 static mapping - - :param local_ip: Local IP address - :param external_ip: External IP address - :param local_port: Local port number (Optional) - :param external_port: External port number (Optional) - :param vrf_id: VRF ID (Default 0) - :param is_add: 1 if add, 0 if delete (Default add) - :param external_sw_if_index: External interface instead of IP address - :param proto: IP protocol (Mandatory if port specified) - :param twice_nat: 1 if translate external host address and port - :param self_twice_nat: 1 if translate external host address and port - whenever external host address equals - local address of internal host - :param out2in_only: if 1 rule is matching only out2in direction - :param tag: Opaque string tag - """ - addr_only = 1 - if local_port and external_port: - addr_only = 0 - l_ip = socket.inet_pton(socket.AF_INET, local_ip) - e_ip = socket.inet_pton(socket.AF_INET, external_ip) - self.vapi.nat44_add_del_static_mapping( - l_ip, - e_ip, - external_sw_if_index, - local_port, - external_port, - addr_only, - vrf_id, - proto, - twice_nat, - self_twice_nat, - out2in_only, - tag, - is_add) - - def nat44_add_address(self, ip, is_add=1, vrf_id=0xFFFFFFFF, twice_nat=0): - """ - Add/delete NAT44 address - - :param ip: IP address - :param is_add: 1 if add, 0 if delete (Default add) - :param twice_nat: twice NAT address for extenal hosts - """ - nat_addr = socket.inet_pton(socket.AF_INET, ip) - self.vapi.nat44_add_del_address_range(nat_addr, nat_addr, is_add, - vrf_id=vrf_id, - twice_nat=twice_nat) - - def test_dynamic(self): - """ NAT44 dynamic translation test """ - - self.nat44_add_address(self.nat_addr) - 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) - - # in2out - pkts = self.create_stream_in(self.pg0, self.pg1) - self.pg0.add_stream(pkts) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - capture = self.pg1.get_capture(len(pkts)) - self.verify_capture_out(capture) - - # out2in - pkts = self.create_stream_out(self.pg1) - self.pg1.add_stream(pkts) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - capture = self.pg0.get_capture(len(pkts)) - self.verify_capture_in(capture, self.pg0) - - def test_dynamic_icmp_errors_in2out_ttl_1(self): - """ NAT44 handling of client packets with TTL=1 """ - - self.nat44_add_address(self.nat_addr) - 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) - - # Client side - generate traffic - pkts = self.create_stream_in(self.pg0, self.pg1, ttl=1) - self.pg0.add_stream(pkts) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - - # Client side - verify ICMP type 11 packets - capture = self.pg0.get_capture(len(pkts)) - self.verify_capture_in_with_icmp_errors(capture, self.pg0) - - def test_dynamic_icmp_errors_out2in_ttl_1(self): - """ NAT44 handling of server packets with TTL=1 """ + def test_dynamic_icmp_errors_out2in_ttl_1(self): + """ NAT44 handling of server packets with TTL=1 """ self.nat44_add_address(self.nat_addr) self.vapi.nat44_interface_add_del_feature(self.pg0.sw_if_index) @@ -1249,7 +1252,7 @@ class TestNAT44(MethodHolder): self.verify_capture_out(capture, same_port=True, packet_num=1) self.assert_equal(capture[0][IP].proto, IP_PROTOS.icmp) - def test_forwarding(self): + def _test_forwarding(self): """ NAT44 forwarding test """ self.vapi.nat44_interface_add_del_feature(self.pg0.sw_if_index) @@ -1464,132 +1467,6 @@ class TestNAT44(MethodHolder): capture = self.pg1.get_capture(len(pkts)) self.verify_capture_out(capture) - def test_static_with_port_out2(self): - """ 1:1 NAPT symmetrical rule """ - - external_port = 80 - local_port = 8080 - - self.vapi.nat44_forwarding_enable_disable(1) - self.nat44_add_static_mapping(self.pg0.remote_ip4, self.nat_addr, - local_port, external_port, - proto=IP_PROTOS.tcp, out2in_only=1) - 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] - try: - ip = p[IP] - tcp = p[TCP] - self.assertEqual(ip.dst, self.pg0.remote_ip4) - self.assertEqual(tcp.dport, local_port) - self.assert_packet_checksums_valid(p) - except: - self.logger.error(ppp("Unexpected or invalid packet:", p)) - raise - - # ICMP error - p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / - IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4) / - ICMP(type=11) / capture[0][IP]) - 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: - self.assertEqual(p[IP].src, self.nat_addr) - inner = p[IPerror] - self.assertEqual(inner.dst, self.nat_addr) - self.assertEqual(inner[TCPerror].dport, external_port) - except: - self.logger.error(ppp("Unexpected or invalid packet:", p)) - raise - - # from service back to client - p = (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) / - IP(src=self.pg0.remote_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.assert_packet_checksums_valid(p) - except: - self.logger.error(ppp("Unexpected or invalid packet:", p)) - raise - - # ICMP error - p = (Ether(dst=self.pg1.local_mac, src=self.pg1.remote_mac) / - IP(src=self.pg1.remote_ip4, dst=self.nat_addr) / - ICMP(type=11) / capture[0][IP]) - self.pg1.add_stream(p) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - capture = self.pg0.get_capture(1) - p = capture[0] - try: - self.assertEqual(p[IP].dst, self.pg0.remote_ip4) - inner = p[IPerror] - self.assertEqual(inner.src, self.pg0.remote_ip4) - self.assertEqual(inner[TCPerror].sport, local_port) - except: - self.logger.error(ppp("Unexpected or invalid packet:", p)) - raise - - # from client to server (no translation) - p = (Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac) / - IP(src=self.pg1.remote_ip4, dst=self.pg0.remote_ip4) / - TCP(sport=12346, dport=local_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] - try: - ip = p[IP] - tcp = p[TCP] - self.assertEqual(ip.dst, self.pg0.remote_ip4) - self.assertEqual(tcp.dport, local_port) - self.assert_packet_checksums_valid(p) - except: - self.logger.error(ppp("Unexpected or invalid packet:", p)) - raise - - # from service back to client (no translation) - p = (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) / - IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4) / - TCP(sport=local_port, dport=12346)) - 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.pg0.remote_ip4) - self.assertEqual(tcp.sport, local_port) - self.assert_packet_checksums_valid(p) - except: - self.logger.error(ppp("Unexpected or invalid packet:", p)) - raise - def test_static_vrf_aware(self): """ 1:1 NAT VRF awareness """ @@ -1683,243 +1560,8 @@ class TestNAT44(MethodHolder): self.logger.error(ppp("Unexpected or invalid packet:", p)) raise - 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.assert_packet_checksums_valid(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.assert_packet_checksums_valid(p) - except: - self.logger.error(ppp("Unexpected or invalid packet:", p)) - raise - - sessions = self.vapi.nat44_user_session_dump(server.ip4n, 0) - self.assertEqual(len(sessions), 1) - self.assertTrue(sessions[0].ext_host_valid) - self.vapi.nat44_del_session( - sessions[0].inside_ip_address, - sessions[0].inside_port, - sessions[0].protocol, - ext_host_address=sessions[0].ext_host_address, - ext_host_port=sessions[0].ext_host_port) - sessions = self.vapi.nat44_user_session_dump(server.ip4n, 0) - self.assertEqual(len(sessions), 0) - - @unittest.skipUnless(running_extended_tests(), "part of extended tests") - def test_static_lb_multi_clients(self): - """ NAT44 local service load balancing - multiple clients""" - - 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': 90}, - {'addr': server2.ip4n, - 'port': local_port, - 'probability': 10}] - - 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) - - server1_n = 0 - server2_n = 0 - clients = ip4_range(self.pg1.remote_ip4, 10, 50) - 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_static_lb_2(self): - """ NAT44 local service load balancing (asymmetrical rule) """ - 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.vapi.nat44_forwarding_enable_disable(1) - self.vapi.nat44_add_del_lb_static_mapping(external_addr_n, - external_port, - IP_PROTOS.tcp, - out2in_only=1, - 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.assert_packet_checksums_valid(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.assert_packet_checksums_valid(p) - except: - self.logger.error(ppp("Unexpected or invalid packet:", p)) - raise - - # from client to server (no translation) - p = (Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac) / - IP(src=self.pg1.remote_ip4, dst=server1.ip4) / - TCP(sport=12346, dport=local_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.assertEqual(ip.dst, server1.ip4) - self.assertEqual(tcp.dport, local_port) - self.assert_packet_checksums_valid(p) - except: - self.logger.error(ppp("Unexpected or invalid packet:", p)) - raise - - # from service back to client (no translation) - p = (Ether(src=server1.mac, dst=self.pg0.local_mac) / - IP(src=server1.ip4, dst=self.pg1.remote_ip4) / - TCP(sport=local_port, dport=12346)) - 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, server1.ip4) - self.assertEqual(tcp.sport, local_port) - self.assert_packet_checksums_valid(p) - except: - self.logger.error(ppp("Unexpected or invalid packet:", p)) - raise - - def test_multiple_inside_interfaces(self): - """ NAT44 multiple non-overlapping address space inside interfaces """ + def test_multiple_inside_interfaces(self): + """ NAT44 multiple non-overlapping address space inside interfaces """ self.nat44_add_address(self.nat_addr) self.vapi.nat44_interface_add_del_feature(self.pg0.sw_if_index) @@ -3042,144 +2684,25 @@ class TestNAT44(MethodHolder): self.logger.error(ppp("Unexpected or invalid packet:", packet)) raise - def test_unknown_proto(self): - """ NAT44 translate packet with unknown protocol """ + def test_output_feature(self): + """ NAT44 interface output feature (in2out postrouting) """ self.nat44_add_address(self.nat_addr) - 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) + self.vapi.nat44_interface_add_del_output_feature(self.pg0.sw_if_index) + self.vapi.nat44_interface_add_del_output_feature(self.pg1.sw_if_index) + self.vapi.nat44_interface_add_del_output_feature(self.pg3.sw_if_index, + is_inside=0) # in2out - p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / - IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4) / - TCP(sport=self.tcp_port_in, dport=20)) - self.pg0.add_stream(p) + pkts = self.create_stream_in(self.pg0, self.pg3) + self.pg0.add_stream(pkts) self.pg_enable_capture(self.pg_interfaces) self.pg_start() - p = self.pg1.get_capture(1) + capture = self.pg3.get_capture(len(pkts)) + self.verify_capture_out(capture) - p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / - IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4) / - GRE() / - IP(src=self.pg2.remote_ip4, dst=self.pg3.remote_ip4) / - TCP(sport=1234, dport=1234)) - self.pg0.add_stream(p) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - p = self.pg1.get_capture(1) - packet = p[0] - try: - self.assertEqual(packet[IP].src, self.nat_addr) - self.assertEqual(packet[IP].dst, self.pg1.remote_ip4) - self.assertTrue(packet.haslayer(GRE)) - self.assert_packet_checksums_valid(packet) - except: - self.logger.error(ppp("Unexpected or invalid packet:", packet)) - raise - - # out2in - p = (Ether(dst=self.pg1.local_mac, src=self.pg1.remote_mac) / - IP(src=self.pg1.remote_ip4, dst=self.nat_addr) / - GRE() / - IP(src=self.pg3.remote_ip4, dst=self.pg2.remote_ip4) / - TCP(sport=1234, dport=1234)) - self.pg1.add_stream(p) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - p = self.pg0.get_capture(1) - packet = p[0] - try: - self.assertEqual(packet[IP].src, self.pg1.remote_ip4) - self.assertEqual(packet[IP].dst, self.pg0.remote_ip4) - self.assertTrue(packet.haslayer(GRE)) - self.assert_packet_checksums_valid(packet) - except: - self.logger.error(ppp("Unexpected or invalid packet:", packet)) - raise - - def test_hairpinning_unknown_proto(self): - """ NAT44 translate packet with unknown protocol - hairpinning """ - host = self.pg0.remote_hosts[0] - server = self.pg0.remote_hosts[1] - host_in_port = 1234 - server_out_port = 8765 - server_nat_ip = "10.0.0.11" - - self.nat44_add_address(self.nat_addr) - 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) - - # add static mapping for server - self.nat44_add_static_mapping(server.ip4, server_nat_ip) - - # host to server - p = (Ether(src=host.mac, dst=self.pg0.local_mac) / - IP(src=host.ip4, dst=server_nat_ip) / - TCP(sport=host_in_port, dport=server_out_port)) - self.pg0.add_stream(p) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - self.pg0.get_capture(1) - - p = (Ether(dst=self.pg0.local_mac, src=host.mac) / - IP(src=host.ip4, dst=server_nat_ip) / - GRE() / - IP(src=self.pg2.remote_ip4, dst=self.pg3.remote_ip4) / - TCP(sport=1234, dport=1234)) - self.pg0.add_stream(p) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - p = self.pg0.get_capture(1) - packet = p[0] - try: - self.assertEqual(packet[IP].src, self.nat_addr) - self.assertEqual(packet[IP].dst, server.ip4) - self.assertTrue(packet.haslayer(GRE)) - self.assert_packet_checksums_valid(packet) - except: - self.logger.error(ppp("Unexpected or invalid packet:", packet)) - raise - - # server to host - p = (Ether(dst=self.pg0.local_mac, src=server.mac) / - IP(src=server.ip4, dst=self.nat_addr) / - GRE() / - IP(src=self.pg3.remote_ip4, dst=self.pg2.remote_ip4) / - TCP(sport=1234, dport=1234)) - self.pg0.add_stream(p) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - p = self.pg0.get_capture(1) - packet = p[0] - try: - self.assertEqual(packet[IP].src, server_nat_ip) - self.assertEqual(packet[IP].dst, host.ip4) - self.assertTrue(packet.haslayer(GRE)) - self.assert_packet_checksums_valid(packet) - except: - self.logger.error(ppp("Unexpected or invalid packet:", packet)) - raise - - def test_output_feature(self): - """ NAT44 interface output feature (in2out postrouting) """ - self.nat44_add_address(self.nat_addr) - self.vapi.nat44_interface_add_del_output_feature(self.pg0.sw_if_index) - self.vapi.nat44_interface_add_del_output_feature(self.pg1.sw_if_index) - self.vapi.nat44_interface_add_del_output_feature(self.pg3.sw_if_index, - is_inside=0) - - # in2out - pkts = self.create_stream_in(self.pg0, self.pg3) - self.pg0.add_stream(pkts) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - capture = self.pg3.get_capture(len(pkts)) - self.verify_capture_out(capture) - - # out2in - pkts = self.create_stream_out(self.pg3) - self.pg3.add_stream(pkts) + # out2in + pkts = self.create_stream_out(self.pg3) + self.pg3.add_stream(pkts) self.pg_enable_capture(self.pg_interfaces) self.pg_start() capture = self.pg0.get_capture(len(pkts)) @@ -3310,336 +2833,100 @@ class TestNAT44(MethodHolder): self.logger.error(ppp("Unexpected or invalid packet:", p)) raise - def test_output_feature_and_service(self): - """ NAT44 interface output feature and services """ - external_addr = '1.2.3.4' - external_port = 80 - local_port = 8080 + def test_one_armed_nat44(self): + """ One armed NAT44 """ + remote_host = self.pg9.remote_hosts[0] + local_host = self.pg9.remote_hosts[1] + external_port = 0 - self.vapi.nat44_forwarding_enable_disable(1) self.nat44_add_address(self.nat_addr) - self.vapi.nat44_add_del_identity_mapping(ip=self.pg1.remote_ip4n) - self.nat44_add_static_mapping(self.pg0.remote_ip4, external_addr, - local_port, external_port, - proto=IP_PROTOS.tcp, out2in_only=1) - self.vapi.nat44_interface_add_del_feature(self.pg0.sw_if_index) - self.vapi.nat44_interface_add_del_feature(self.pg0.sw_if_index, + self.vapi.nat44_interface_add_del_feature(self.pg9.sw_if_index) + self.vapi.nat44_interface_add_del_feature(self.pg9.sw_if_index, is_inside=0) - self.vapi.nat44_interface_add_del_output_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=external_addr) / - TCP(sport=12345, dport=external_port)) - self.pg1.add_stream(p) + # in2out + p = (Ether(src=self.pg9.remote_mac, dst=self.pg9.local_mac) / + IP(src=local_host.ip4, dst=remote_host.ip4) / + TCP(sport=12345, dport=80)) + self.pg9.add_stream(p) self.pg_enable_capture(self.pg_interfaces) self.pg_start() - capture = self.pg0.get_capture(1) + capture = self.pg9.get_capture(1) p = capture[0] try: ip = p[IP] tcp = p[TCP] - self.assertEqual(ip.dst, self.pg0.remote_ip4) - self.assertEqual(tcp.dport, local_port) + self.assertEqual(ip.src, self.nat_addr) + self.assertEqual(ip.dst, remote_host.ip4) + self.assertNotEqual(tcp.sport, 12345) + external_port = tcp.sport + self.assertEqual(tcp.dport, 80) self.assert_packet_checksums_valid(p) except: self.logger.error(ppp("Unexpected or invalid packet:", p)) raise - # from service back to client - p = (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) / - IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4) / - TCP(sport=local_port, dport=12345)) - self.pg0.add_stream(p) + # out2in + p = (Ether(src=self.pg9.remote_mac, dst=self.pg9.local_mac) / + IP(src=remote_host.ip4, dst=self.nat_addr) / + TCP(sport=80, dport=external_port)) + self.pg9.add_stream(p) self.pg_enable_capture(self.pg_interfaces) self.pg_start() - capture = self.pg1.get_capture(1) + capture = self.pg9.get_capture(1) p = capture[0] try: ip = p[IP] tcp = p[TCP] - self.assertEqual(ip.src, external_addr) - self.assertEqual(tcp.sport, external_port) + self.assertEqual(ip.src, remote_host.ip4) + self.assertEqual(ip.dst, local_host.ip4) + self.assertEqual(tcp.sport, 80) + self.assertEqual(tcp.dport, 12345) self.assert_packet_checksums_valid(p) except: self.logger.error(ppp("Unexpected or invalid packet:", p)) raise - # from local network host to external network - pkts = self.create_stream_in(self.pg0, self.pg1) - self.pg0.add_stream(pkts) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - capture = self.pg1.get_capture(len(pkts)) - self.verify_capture_out(capture) + def test_del_session(self): + """ Delete NAT44 session """ + self.nat44_add_address(self.nat_addr) + 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) + pkts = self.create_stream_in(self.pg0, self.pg1) self.pg0.add_stream(pkts) self.pg_enable_capture(self.pg_interfaces) self.pg_start() - capture = self.pg1.get_capture(len(pkts)) - self.verify_capture_out(capture) + self.pg1.get_capture(len(pkts)) - # from external network back to local network host - pkts = self.create_stream_out(self.pg1) - self.pg1.add_stream(pkts) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - capture = self.pg0.get_capture(len(pkts)) - self.verify_capture_in(capture, self.pg0) + sessions = self.vapi.nat44_user_session_dump(self.pg0.remote_ip4n, 0) + nsessions = len(sessions) - def test_output_feature_and_service2(self): - """ NAT44 interface output feature and service host direct access """ - self.vapi.nat44_forwarding_enable_disable(1) - self.nat44_add_address(self.nat_addr) - self.vapi.nat44_interface_add_del_output_feature(self.pg1.sw_if_index, - is_inside=0) + self.vapi.nat44_del_session(sessions[0].inside_ip_address, + sessions[0].inside_port, + sessions[0].protocol) + self.vapi.nat44_del_session(sessions[1].outside_ip_address, + sessions[1].outside_port, + sessions[1].protocol, + is_in=0) - # session initiaded from service host - translate - pkts = self.create_stream_in(self.pg0, self.pg1) - self.pg0.add_stream(pkts) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - capture = self.pg1.get_capture(len(pkts)) - self.verify_capture_out(capture) + sessions = self.vapi.nat44_user_session_dump(self.pg0.remote_ip4n, 0) + self.assertEqual(nsessions - len(sessions), 2) - pkts = self.create_stream_out(self.pg1) - self.pg1.add_stream(pkts) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - capture = self.pg0.get_capture(len(pkts)) - self.verify_capture_in(capture, self.pg0) + def test_set_get_reass(self): + """ NAT44 set/get virtual fragmentation reassembly """ + reas_cfg1 = self.vapi.nat_get_reass() - # session initiaded from remote host - do not translate - pkts = self.create_stream_out(self.pg1, - self.pg0.remote_ip4, - use_inside_ports=True) - self.pg1.add_stream(pkts) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - capture = self.pg0.get_capture(len(pkts)) - self.verify_capture_in(capture, self.pg0) + self.vapi.nat_set_reass(timeout=reas_cfg1.ip4_timeout + 5, + max_reass=reas_cfg1.ip4_max_reass * 2, + max_frag=reas_cfg1.ip4_max_frag * 2) - pkts = self.create_stream_in(self.pg0, self.pg1) - self.pg0.add_stream(pkts) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - capture = self.pg1.get_capture(len(pkts)) - self.verify_capture_out(capture, nat_ip=self.pg0.remote_ip4, - same_port=True) + reas_cfg2 = self.vapi.nat_get_reass() - def test_output_feature_and_service3(self): - """ NAT44 interface output feature and DST NAT """ - external_addr = '1.2.3.4' - external_port = 80 - local_port = 8080 - - self.vapi.nat44_forwarding_enable_disable(1) - self.nat44_add_address(self.nat_addr) - self.nat44_add_static_mapping(self.pg1.remote_ip4, external_addr, - local_port, external_port, - proto=IP_PROTOS.tcp, out2in_only=1) - self.vapi.nat44_interface_add_del_feature(self.pg0.sw_if_index) - self.vapi.nat44_interface_add_del_feature(self.pg0.sw_if_index, - is_inside=0) - self.vapi.nat44_interface_add_del_output_feature(self.pg1.sw_if_index, - is_inside=0) - - p = (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) / - IP(src=self.pg0.remote_ip4, dst=external_addr) / - TCP(sport=12345, dport=external_port)) - 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.pg0.remote_ip4) - self.assertEqual(tcp.sport, 12345) - self.assertEqual(ip.dst, self.pg1.remote_ip4) - self.assertEqual(tcp.dport, local_port) - self.assert_packet_checksums_valid(p) - except: - self.logger.error(ppp("Unexpected or invalid packet:", p)) - raise - - p = (Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac) / - IP(src=self.pg1.remote_ip4, dst=self.pg0.remote_ip4) / - TCP(sport=local_port, dport=12345)) - self.pg1.add_stream(p) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - capture = self.pg0.get_capture(1) - p = capture[0] - try: - ip = p[IP] - tcp = p[TCP] - self.assertEqual(ip.src, external_addr) - self.assertEqual(tcp.sport, external_port) - self.assertEqual(ip.dst, self.pg0.remote_ip4) - self.assertEqual(tcp.dport, 12345) - self.assert_packet_checksums_valid(p) - except: - self.logger.error(ppp("Unexpected or invalid packet:", p)) - raise - - def test_one_armed_nat44(self): - """ One armed NAT44 """ - remote_host = self.pg9.remote_hosts[0] - local_host = self.pg9.remote_hosts[1] - external_port = 0 - - self.nat44_add_address(self.nat_addr) - self.vapi.nat44_interface_add_del_feature(self.pg9.sw_if_index) - self.vapi.nat44_interface_add_del_feature(self.pg9.sw_if_index, - is_inside=0) - - # in2out - p = (Ether(src=self.pg9.remote_mac, dst=self.pg9.local_mac) / - IP(src=local_host.ip4, dst=remote_host.ip4) / - TCP(sport=12345, dport=80)) - self.pg9.add_stream(p) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - capture = self.pg9.get_capture(1) - p = capture[0] - try: - ip = p[IP] - tcp = p[TCP] - self.assertEqual(ip.src, self.nat_addr) - self.assertEqual(ip.dst, remote_host.ip4) - self.assertNotEqual(tcp.sport, 12345) - external_port = tcp.sport - self.assertEqual(tcp.dport, 80) - self.assert_packet_checksums_valid(p) - except: - self.logger.error(ppp("Unexpected or invalid packet:", p)) - raise - - # out2in - p = (Ether(src=self.pg9.remote_mac, dst=self.pg9.local_mac) / - IP(src=remote_host.ip4, dst=self.nat_addr) / - TCP(sport=80, dport=external_port)) - self.pg9.add_stream(p) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - capture = self.pg9.get_capture(1) - p = capture[0] - try: - ip = p[IP] - tcp = p[TCP] - self.assertEqual(ip.src, remote_host.ip4) - self.assertEqual(ip.dst, local_host.ip4) - self.assertEqual(tcp.sport, 80) - self.assertEqual(tcp.dport, 12345) - self.assert_packet_checksums_valid(p) - except: - self.logger.error(ppp("Unexpected or invalid packet:", p)) - raise - - def test_one_armed_nat44_static(self): - """ One armed NAT44 and 1:1 NAPT symmetrical rule """ - remote_host = self.pg9.remote_hosts[0] - local_host = self.pg9.remote_hosts[1] - external_port = 80 - local_port = 8080 - eh_port_in = 0 - - self.vapi.nat44_forwarding_enable_disable(1) - self.nat44_add_address(self.nat_addr, twice_nat=1) - self.nat44_add_static_mapping(local_host.ip4, self.nat_addr, - local_port, external_port, - proto=IP_PROTOS.tcp, out2in_only=1, - twice_nat=1) - self.vapi.nat44_interface_add_del_feature(self.pg9.sw_if_index) - self.vapi.nat44_interface_add_del_feature(self.pg9.sw_if_index, - is_inside=0) - - # from client to service - p = (Ether(src=self.pg9.remote_mac, dst=self.pg9.local_mac) / - IP(src=remote_host.ip4, dst=self.nat_addr) / - TCP(sport=12345, dport=external_port)) - self.pg9.add_stream(p) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - capture = self.pg9.get_capture(1) - p = capture[0] - try: - ip = p[IP] - tcp = p[TCP] - self.assertEqual(ip.dst, local_host.ip4) - self.assertEqual(ip.src, self.nat_addr) - self.assertEqual(tcp.dport, local_port) - self.assertNotEqual(tcp.sport, 12345) - eh_port_in = tcp.sport - self.assert_packet_checksums_valid(p) - except: - self.logger.error(ppp("Unexpected or invalid packet:", p)) - raise - - # from service back to client - p = (Ether(src=self.pg9.remote_mac, dst=self.pg9.local_mac) / - IP(src=local_host.ip4, dst=self.nat_addr) / - TCP(sport=local_port, dport=eh_port_in)) - self.pg9.add_stream(p) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - capture = self.pg9.get_capture(1) - p = capture[0] - try: - ip = p[IP] - tcp = p[TCP] - self.assertEqual(ip.src, self.nat_addr) - self.assertEqual(ip.dst, remote_host.ip4) - self.assertEqual(tcp.sport, external_port) - self.assertEqual(tcp.dport, 12345) - self.assert_packet_checksums_valid(p) - except: - self.logger.error(ppp("Unexpected or invalid packet:", p)) - raise - - def test_del_session(self): - """ Delete NAT44 session """ - self.nat44_add_address(self.nat_addr) - 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) - - pkts = self.create_stream_in(self.pg0, self.pg1) - self.pg0.add_stream(pkts) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - self.pg1.get_capture(len(pkts)) - - sessions = self.vapi.nat44_user_session_dump(self.pg0.remote_ip4n, 0) - nsessions = len(sessions) - - self.vapi.nat44_del_session(sessions[0].inside_ip_address, - sessions[0].inside_port, - sessions[0].protocol) - self.vapi.nat44_del_session(sessions[1].outside_ip_address, - sessions[1].outside_port, - sessions[1].protocol, - is_in=0) - - sessions = self.vapi.nat44_user_session_dump(self.pg0.remote_ip4n, 0) - self.assertEqual(nsessions - len(sessions), 2) - - def test_set_get_reass(self): - """ NAT44 set/get virtual fragmentation reassembly """ - reas_cfg1 = self.vapi.nat_get_reass() - - self.vapi.nat_set_reass(timeout=reas_cfg1.ip4_timeout + 5, - max_reass=reas_cfg1.ip4_max_reass * 2, - max_frag=reas_cfg1.ip4_max_frag * 2) - - reas_cfg2 = self.vapi.nat_get_reass() - - self.assertEqual(reas_cfg1.ip4_timeout + 5, reas_cfg2.ip4_timeout) - self.assertEqual(reas_cfg1.ip4_max_reass * 2, reas_cfg2.ip4_max_reass) - self.assertEqual(reas_cfg1.ip4_max_frag * 2, reas_cfg2.ip4_max_frag) + self.assertEqual(reas_cfg1.ip4_timeout + 5, reas_cfg2.ip4_timeout) + self.assertEqual(reas_cfg1.ip4_max_reass * 2, reas_cfg2.ip4_max_reass) + self.assertEqual(reas_cfg1.ip4_max_frag * 2, reas_cfg2.ip4_max_frag) self.vapi.nat_set_reass(drop_frag=1) self.assertTrue(self.vapi.nat_get_reass().ip4_drop_frag) @@ -3787,22 +3074,766 @@ class TestNAT44(MethodHolder): self.vapi.cli("nat addr-port-assignment-alg map-e psid 10 " "psid-offset 6 psid-len 6") - p = (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) / - IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4) / - TCP(sport=4567, dport=22)) - self.pg0.add_stream(p) + p = (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) / + IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4) / + TCP(sport=4567, dport=22)) + 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.dst, self.pg1.remote_ip4) + self.assertEqual(ip.src, self.nat_addr) + self.assertEqual(tcp.dport, 22) + self.assertNotEqual(tcp.sport, 4567) + self.assertEqual((tcp.sport >> 6) & 63, 10) + self.assert_packet_checksums_valid(p) + except: + self.logger.error(ppp("Unexpected or invalid packet:", p)) + raise + + def test_ipfix_max_frags(self): + """ IPFIX logging maximum fragments pending reassembly exceeded """ + self.nat44_add_address(self.nat_addr) + 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) + self.vapi.nat_set_reass(max_frag=0) + self.vapi.set_ipfix_exporter(collector_address=self.pg3.remote_ip4n, + src_address=self.pg3.local_ip4n, + path_mtu=512, + template_interval=10) + self.vapi.nat_ipfix(domain_id=self.ipfix_domain_id, + src_port=self.ipfix_src_port) + + data = "A" * 4 + "B" * 16 + "C" * 3 + self.tcp_port_in = random.randint(1025, 65535) + pkts = self.create_stream_frag(self.pg0, + self.pg1.remote_ip4, + self.tcp_port_in, + 20, + data) + self.pg0.add_stream(pkts[-1]) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + self.pg1.assert_nothing_captured() + sleep(1) + self.vapi.cli("ipfix flush") # FIXME this should be an API call + capture = self.pg3.get_capture(9) + ipfix = IPFIXDecoder() + # first load template + for p in capture: + self.assertTrue(p.haslayer(IPFIX)) + self.assertEqual(p[IP].src, self.pg3.local_ip4) + self.assertEqual(p[IP].dst, self.pg3.remote_ip4) + self.assertEqual(p[UDP].sport, self.ipfix_src_port) + self.assertEqual(p[UDP].dport, 4739) + self.assertEqual(p[IPFIX].observationDomainID, + self.ipfix_domain_id) + if p.haslayer(Template): + ipfix.add_template(p.getlayer(Template)) + # verify events in data set + for p in capture: + if p.haslayer(Data): + data = ipfix.decode_data_set(p.getlayer(Set)) + self.verify_ipfix_max_fragments_ip4(data, 0, + self.pg0.remote_ip4n) + + def tearDown(self): + super(TestNAT44, self).tearDown() + if not self.vpp_dead: + self.logger.info(self.vapi.cli("show nat44 addresses")) + self.logger.info(self.vapi.cli("show nat44 interfaces")) + self.logger.info(self.vapi.cli("show nat44 static mappings")) + self.logger.info(self.vapi.cli("show nat44 interface address")) + self.logger.info(self.vapi.cli("show nat44 sessions detail")) + self.logger.info(self.vapi.cli("show nat virtual-reassembly")) + self.logger.info(self.vapi.cli("show nat44 hash tables detail")) + self.vapi.cli("nat addr-port-assignment-alg default") + self.clear_nat44() + self.vapi.cli("clear logging") + + +class TestNAT44EndpointDependent(MethodHolder): + """ Endpoint-Dependent mapping and filtering test cases """ + + @classmethod + def setUpConstants(cls): + super(TestNAT44EndpointDependent, cls).setUpConstants() + cls.vpp_cmdline.extend(["nat", "{", "endpoint-dependent", "}"]) + + @classmethod + def setUpClass(cls): + super(TestNAT44EndpointDependent, cls).setUpClass() + cls.vapi.cli("set log class nat level debug") + try: + cls.tcp_port_in = 6303 + cls.tcp_port_out = 6303 + cls.udp_port_in = 6304 + cls.udp_port_out = 6304 + cls.icmp_id_in = 6305 + cls.icmp_id_out = 6305 + cls.nat_addr = '10.0.0.3' + cls.nat_addr_n = socket.inet_pton(socket.AF_INET, cls.nat_addr) + cls.ipfix_src_port = 4739 + cls.ipfix_domain_id = 1 + cls.tcp_external_port = 80 + + cls.create_pg_interfaces(range(5)) + cls.interfaces = list(cls.pg_interfaces[0:3]) + + for i in cls.interfaces: + i.admin_up() + i.config_ip4() + i.resolve_arp() + + cls.pg0.generate_remote_hosts(3) + cls.pg0.configure_ipv4_neighbors() + + cls.pg3.admin_up() + + cls.pg4.generate_remote_hosts(2) + cls.pg4.config_ip4() + ip_addr_n = socket.inet_pton(socket.AF_INET, "10.0.0.1") + cls.vapi.sw_interface_add_del_address(cls.pg4.sw_if_index, + ip_addr_n, + 24) + cls.pg4.admin_up() + cls.pg4.resolve_arp() + cls.pg4._remote_hosts[1]._ip4 = cls.pg4._remote_hosts[0]._ip4 + cls.pg4.resolve_arp() + + except Exception: + super(TestNAT44EndpointDependent, cls).tearDownClass() + raise + + def test_dynamic(self): + """ NAT44 dynamic translation test """ + + self.nat44_add_address(self.nat_addr) + 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) + + # in2out + pkts = self.create_stream_in(self.pg0, self.pg1) + self.pg0.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = self.pg1.get_capture(len(pkts)) + self.verify_capture_out(capture) + + # out2in + pkts = self.create_stream_out(self.pg1) + self.pg1.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = self.pg0.get_capture(len(pkts)) + self.verify_capture_in(capture, self.pg0) + + def test_forwarding(self): + """ NAT44 forwarding test """ + + 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) + self.vapi.nat44_forwarding_enable_disable(1) + + real_ip = self.pg0.remote_ip4n + alias_ip = self.nat_addr_n + self.vapi.nat44_add_del_static_mapping(local_ip=real_ip, + external_ip=alias_ip) + + try: + # in2out - static mapping match + + pkts = self.create_stream_out(self.pg1) + self.pg1.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = self.pg0.get_capture(len(pkts)) + self.verify_capture_in(capture, self.pg0) + + pkts = self.create_stream_in(self.pg0, self.pg1) + self.pg0.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = self.pg1.get_capture(len(pkts)) + self.verify_capture_out(capture, same_port=True) + + # in2out - no static mapping match + + host0 = self.pg0.remote_hosts[0] + self.pg0.remote_hosts[0] = self.pg0.remote_hosts[1] + try: + pkts = self.create_stream_out(self.pg1, + dst_ip=self.pg0.remote_ip4, + use_inside_ports=True) + self.pg1.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = self.pg0.get_capture(len(pkts)) + self.verify_capture_in(capture, self.pg0) + + pkts = self.create_stream_in(self.pg0, self.pg1) + self.pg0.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = self.pg1.get_capture(len(pkts)) + self.verify_capture_out(capture, nat_ip=self.pg0.remote_ip4, + same_port=True) + finally: + self.pg0.remote_hosts[0] = host0 + + user = self.pg0.remote_hosts[1] + sessions = self.vapi.nat44_user_session_dump(user.ip4n, 0) + self.assertEqual(len(sessions), 3) + self.assertTrue(sessions[0].ext_host_valid) + self.vapi.nat44_del_session( + sessions[0].inside_ip_address, + sessions[0].inside_port, + sessions[0].protocol, + ext_host_address=sessions[0].ext_host_address, + ext_host_port=sessions[0].ext_host_port) + sessions = self.vapi.nat44_user_session_dump(user.ip4n, 0) + self.assertEqual(len(sessions), 2) + + finally: + self.vapi.nat44_forwarding_enable_disable(0) + self.vapi.nat44_add_del_static_mapping(local_ip=real_ip, + external_ip=alias_ip, + is_add=0) + + 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.assert_packet_checksums_valid(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.assert_packet_checksums_valid(p) + except: + self.logger.error(ppp("Unexpected or invalid packet:", p)) + raise + + sessions = self.vapi.nat44_user_session_dump(server.ip4n, 0) + self.assertEqual(len(sessions), 1) + self.assertTrue(sessions[0].ext_host_valid) + self.vapi.nat44_del_session( + sessions[0].inside_ip_address, + sessions[0].inside_port, + sessions[0].protocol, + ext_host_address=sessions[0].ext_host_address, + ext_host_port=sessions[0].ext_host_port) + sessions = self.vapi.nat44_user_session_dump(server.ip4n, 0) + self.assertEqual(len(sessions), 0) + + @unittest.skipUnless(running_extended_tests(), "part of extended tests") + def test_static_lb_multi_clients(self): + """ NAT44 local service load balancing - multiple clients""" + + 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': 90}, + {'addr': server2.ip4n, + 'port': local_port, + 'probability': 10}] + + 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) + + server1_n = 0 + server2_n = 0 + clients = ip4_range(self.pg1.remote_ip4, 10, 50) + 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_static_lb_2(self): + """ NAT44 local service load balancing (asymmetrical rule) """ + 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.vapi.nat44_forwarding_enable_disable(1) + self.vapi.nat44_add_del_lb_static_mapping(external_addr_n, + external_port, + IP_PROTOS.tcp, + out2in_only=1, + 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.assert_packet_checksums_valid(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.assert_packet_checksums_valid(p) + except: + self.logger.error(ppp("Unexpected or invalid packet:", p)) + raise + + # from client to server (no translation) + p = (Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac) / + IP(src=self.pg1.remote_ip4, dst=server1.ip4) / + TCP(sport=12346, dport=local_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.assertEqual(ip.dst, server1.ip4) + self.assertEqual(tcp.dport, local_port) + self.assert_packet_checksums_valid(p) + except: + self.logger.error(ppp("Unexpected or invalid packet:", p)) + raise + + # from service back to client (no translation) + p = (Ether(src=server1.mac, dst=self.pg0.local_mac) / + IP(src=server1.ip4, dst=self.pg1.remote_ip4) / + TCP(sport=local_port, dport=12346)) + 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, server1.ip4) + self.assertEqual(tcp.sport, local_port) + self.assert_packet_checksums_valid(p) + except: + self.logger.error(ppp("Unexpected or invalid packet:", p)) + raise + + def test_unknown_proto(self): + """ NAT44 translate packet with unknown protocol """ + self.nat44_add_address(self.nat_addr) + 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) + + # in2out + p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / + IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4) / + TCP(sport=self.tcp_port_in, dport=20)) + self.pg0.add_stream(p) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + p = self.pg1.get_capture(1) + + p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / + IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4) / + GRE() / + IP(src=self.pg2.remote_ip4, dst=self.pg2.remote_ip4) / + TCP(sport=1234, dport=1234)) + self.pg0.add_stream(p) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + p = self.pg1.get_capture(1) + packet = p[0] + try: + self.assertEqual(packet[IP].src, self.nat_addr) + self.assertEqual(packet[IP].dst, self.pg1.remote_ip4) + self.assertTrue(packet.haslayer(GRE)) + self.assert_packet_checksums_valid(packet) + except: + self.logger.error(ppp("Unexpected or invalid packet:", packet)) + raise + + # out2in + p = (Ether(dst=self.pg1.local_mac, src=self.pg1.remote_mac) / + IP(src=self.pg1.remote_ip4, dst=self.nat_addr) / + GRE() / + IP(src=self.pg2.remote_ip4, dst=self.pg2.remote_ip4) / + TCP(sport=1234, dport=1234)) + self.pg1.add_stream(p) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + p = self.pg0.get_capture(1) + packet = p[0] + try: + self.assertEqual(packet[IP].src, self.pg1.remote_ip4) + self.assertEqual(packet[IP].dst, self.pg0.remote_ip4) + self.assertTrue(packet.haslayer(GRE)) + self.assert_packet_checksums_valid(packet) + except: + self.logger.error(ppp("Unexpected or invalid packet:", packet)) + raise + + def test_hairpinning_unknown_proto(self): + """ NAT44 translate packet with unknown protocol - hairpinning """ + host = self.pg0.remote_hosts[0] + server = self.pg0.remote_hosts[1] + host_in_port = 1234 + server_out_port = 8765 + server_nat_ip = "10.0.0.11" + + self.nat44_add_address(self.nat_addr) + 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) + + # add static mapping for server + self.nat44_add_static_mapping(server.ip4, server_nat_ip) + + # host to server + p = (Ether(src=host.mac, dst=self.pg0.local_mac) / + IP(src=host.ip4, dst=server_nat_ip) / + TCP(sport=host_in_port, dport=server_out_port)) + self.pg0.add_stream(p) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + self.pg0.get_capture(1) + + p = (Ether(dst=self.pg0.local_mac, src=host.mac) / + IP(src=host.ip4, dst=server_nat_ip) / + GRE() / + IP(src=self.pg2.remote_ip4, dst=self.pg2.remote_ip4) / + TCP(sport=1234, dport=1234)) + self.pg0.add_stream(p) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + p = self.pg0.get_capture(1) + packet = p[0] + try: + self.assertEqual(packet[IP].src, self.nat_addr) + self.assertEqual(packet[IP].dst, server.ip4) + self.assertTrue(packet.haslayer(GRE)) + self.assert_packet_checksums_valid(packet) + except: + self.logger.error(ppp("Unexpected or invalid packet:", packet)) + raise + + # server to host + p = (Ether(dst=self.pg0.local_mac, src=server.mac) / + IP(src=server.ip4, dst=self.nat_addr) / + GRE() / + IP(src=self.pg2.remote_ip4, dst=self.pg2.remote_ip4) / + TCP(sport=1234, dport=1234)) + self.pg0.add_stream(p) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + p = self.pg0.get_capture(1) + packet = p[0] + try: + self.assertEqual(packet[IP].src, server_nat_ip) + self.assertEqual(packet[IP].dst, host.ip4) + self.assertTrue(packet.haslayer(GRE)) + self.assert_packet_checksums_valid(packet) + except: + self.logger.error(ppp("Unexpected or invalid packet:", packet)) + raise + + def test_output_feature_and_service(self): + """ NAT44 interface output feature and services """ + external_addr = '1.2.3.4' + external_port = 80 + local_port = 8080 + + self.vapi.nat44_forwarding_enable_disable(1) + self.nat44_add_address(self.nat_addr) + self.vapi.nat44_add_del_identity_mapping(ip=self.pg1.remote_ip4n) + self.nat44_add_static_mapping(self.pg0.remote_ip4, external_addr, + local_port, external_port, + proto=IP_PROTOS.tcp, out2in_only=1) + self.vapi.nat44_interface_add_del_feature(self.pg0.sw_if_index) + self.vapi.nat44_interface_add_del_feature(self.pg0.sw_if_index, + is_inside=0) + self.vapi.nat44_interface_add_del_output_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=external_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] + try: + ip = p[IP] + tcp = p[TCP] + self.assertEqual(ip.dst, self.pg0.remote_ip4) + self.assertEqual(tcp.dport, local_port) + self.assert_packet_checksums_valid(p) + except: + self.logger.error(ppp("Unexpected or invalid packet:", p)) + raise + + # from service back to client + p = (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) / + IP(src=self.pg0.remote_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, external_addr) + self.assertEqual(tcp.sport, external_port) + self.assert_packet_checksums_valid(p) + except: + self.logger.error(ppp("Unexpected or invalid packet:", p)) + raise + + # from local network host to external network + pkts = self.create_stream_in(self.pg0, self.pg1) + self.pg0.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = self.pg1.get_capture(len(pkts)) + self.verify_capture_out(capture) + pkts = self.create_stream_in(self.pg0, self.pg1) + self.pg0.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = self.pg1.get_capture(len(pkts)) + self.verify_capture_out(capture) + + # from external network back to local network host + pkts = self.create_stream_out(self.pg1) + self.pg1.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = self.pg0.get_capture(len(pkts)) + self.verify_capture_in(capture, self.pg0) + + def test_output_feature_and_service2(self): + """ NAT44 interface output feature and service host direct access """ + self.vapi.nat44_forwarding_enable_disable(1) + self.nat44_add_address(self.nat_addr) + self.vapi.nat44_interface_add_del_output_feature(self.pg1.sw_if_index, + is_inside=0) + + # session initiaded from service host - translate + pkts = self.create_stream_in(self.pg0, self.pg1) + self.pg0.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = self.pg1.get_capture(len(pkts)) + self.verify_capture_out(capture) + + pkts = self.create_stream_out(self.pg1) + self.pg1.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = self.pg0.get_capture(len(pkts)) + self.verify_capture_in(capture, self.pg0) + + # session initiaded from remote host - do not translate + self.tcp_port_in = 60303 + self.udp_port_in = 60304 + self.icmp_id_in = 60305 + pkts = self.create_stream_out(self.pg1, + self.pg0.remote_ip4, + use_inside_ports=True) + self.pg1.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = self.pg0.get_capture(len(pkts)) + self.verify_capture_in(capture, self.pg0) + + pkts = self.create_stream_in(self.pg0, self.pg1) + self.pg0.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = self.pg1.get_capture(len(pkts)) + self.verify_capture_out(capture, nat_ip=self.pg0.remote_ip4, + same_port=True) + + def test_output_feature_and_service3(self): + """ NAT44 interface output feature and DST NAT """ + external_addr = '1.2.3.4' + external_port = 80 + local_port = 8080 + + self.vapi.nat44_forwarding_enable_disable(1) + self.nat44_add_address(self.nat_addr) + self.nat44_add_static_mapping(self.pg1.remote_ip4, external_addr, + local_port, external_port, + proto=IP_PROTOS.tcp, out2in_only=1) + self.vapi.nat44_interface_add_del_feature(self.pg0.sw_if_index) + self.vapi.nat44_interface_add_del_feature(self.pg0.sw_if_index, + is_inside=0) + self.vapi.nat44_interface_add_del_output_feature(self.pg1.sw_if_index, + is_inside=0) + + p = (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) / + IP(src=self.pg0.remote_ip4, dst=external_addr) / + TCP(sport=12345, dport=external_port)) + 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.pg0.remote_ip4) + self.assertEqual(tcp.sport, 12345) + self.assertEqual(ip.dst, self.pg1.remote_ip4) + self.assertEqual(tcp.dport, local_port) + self.assert_packet_checksums_valid(p) + except: + self.logger.error(ppp("Unexpected or invalid packet:", p)) + raise + + p = (Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac) / + IP(src=self.pg1.remote_ip4, dst=self.pg0.remote_ip4) / + TCP(sport=local_port, dport=12345)) + self.pg1.add_stream(p) self.pg_enable_capture(self.pg_interfaces) self.pg_start() - capture = self.pg1.get_capture(1) + capture = self.pg0.get_capture(1) p = capture[0] try: ip = p[IP] tcp = p[TCP] - self.assertEqual(ip.dst, self.pg1.remote_ip4) - self.assertEqual(ip.src, self.nat_addr) - self.assertEqual(tcp.dport, 22) - self.assertNotEqual(tcp.sport, 4567) - self.assertEqual((tcp.sport >> 6) & 63, 10) + self.assertEqual(ip.src, external_addr) + self.assertEqual(tcp.sport, external_port) + self.assertEqual(ip.dst, self.pg0.remote_ip4) + self.assertEqual(tcp.dport, 12345) self.assert_packet_checksums_valid(p) except: self.logger.error(ppp("Unexpected or invalid packet:", p)) @@ -3979,71 +4010,24 @@ class TestNAT44(MethodHolder): def test_twice_nat_interface_addr(self): """ Acquire twice NAT44 addresses from interface """ - self.vapi.nat44_add_interface_addr(self.pg7.sw_if_index, twice_nat=1) + self.vapi.nat44_add_interface_addr(self.pg3.sw_if_index, twice_nat=1) # no address in NAT pool adresses = self.vapi.nat44_address_dump() self.assertEqual(0, len(adresses)) # configure interface address and check NAT address pool - self.pg7.config_ip4() + self.pg3.config_ip4() adresses = self.vapi.nat44_address_dump() self.assertEqual(1, len(adresses)) - self.assertEqual(adresses[0].ip_address[0:4], self.pg7.local_ip4n) + self.assertEqual(adresses[0].ip_address[0:4], self.pg3.local_ip4n) self.assertEqual(adresses[0].twice_nat, 1) # remove interface address and check NAT address pool - self.pg7.unconfig_ip4() + self.pg3.unconfig_ip4() adresses = self.vapi.nat44_address_dump() self.assertEqual(0, len(adresses)) - def test_ipfix_max_frags(self): - """ IPFIX logging maximum fragments pending reassembly exceeded """ - self.nat44_add_address(self.nat_addr) - 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) - self.vapi.nat_set_reass(max_frag=0) - self.vapi.set_ipfix_exporter(collector_address=self.pg3.remote_ip4n, - src_address=self.pg3.local_ip4n, - path_mtu=512, - template_interval=10) - self.vapi.nat_ipfix(domain_id=self.ipfix_domain_id, - src_port=self.ipfix_src_port) - - data = "A" * 4 + "B" * 16 + "C" * 3 - self.tcp_port_in = random.randint(1025, 65535) - pkts = self.create_stream_frag(self.pg0, - self.pg1.remote_ip4, - self.tcp_port_in, - 20, - data) - self.pg0.add_stream(pkts[-1]) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - self.pg1.assert_nothing_captured() - sleep(1) - self.vapi.cli("ipfix flush") # FIXME this should be an API call - capture = self.pg3.get_capture(9) - ipfix = IPFIXDecoder() - # first load template - for p in capture: - self.assertTrue(p.haslayer(IPFIX)) - self.assertEqual(p[IP].src, self.pg3.local_ip4) - self.assertEqual(p[IP].dst, self.pg3.remote_ip4) - self.assertEqual(p[UDP].sport, self.ipfix_src_port) - self.assertEqual(p[UDP].dport, 4739) - self.assertEqual(p[IPFIX].observationDomainID, - self.ipfix_domain_id) - if p.haslayer(Template): - ipfix.add_template(p.getlayer(Template)) - # verify events in data set - for p in capture: - if p.haslayer(Data): - data = ipfix.decode_data_set(p.getlayer(Set)) - self.verify_ipfix_max_fragments_ip4(data, 0, - self.pg0.remote_ip4n) - def test_tcp_session_close_in(self): """ Close TCP session from inside network """ self.tcp_port_out = 10505 @@ -4225,17 +4209,202 @@ class TestNAT44(MethodHolder): 0) self.assertEqual(len(sessions) - start_sessnum, 0) + def test_one_armed_nat44_static(self): + """ One armed NAT44 and 1:1 NAPT asymmetrical rule """ + remote_host = self.pg4.remote_hosts[0] + local_host = self.pg4.remote_hosts[1] + external_port = 80 + local_port = 8080 + eh_port_in = 0 + + self.vapi.nat44_forwarding_enable_disable(1) + self.nat44_add_address(self.nat_addr, twice_nat=1) + self.nat44_add_static_mapping(local_host.ip4, self.nat_addr, + local_port, external_port, + proto=IP_PROTOS.tcp, out2in_only=1, + twice_nat=1) + self.vapi.nat44_interface_add_del_feature(self.pg4.sw_if_index) + self.vapi.nat44_interface_add_del_feature(self.pg4.sw_if_index, + is_inside=0) + + # from client to service + p = (Ether(src=self.pg4.remote_mac, dst=self.pg4.local_mac) / + IP(src=remote_host.ip4, dst=self.nat_addr) / + TCP(sport=12345, dport=external_port)) + self.pg4.add_stream(p) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = self.pg4.get_capture(1) + p = capture[0] + try: + ip = p[IP] + tcp = p[TCP] + self.assertEqual(ip.dst, local_host.ip4) + self.assertEqual(ip.src, self.nat_addr) + self.assertEqual(tcp.dport, local_port) + self.assertNotEqual(tcp.sport, 12345) + eh_port_in = tcp.sport + self.assert_packet_checksums_valid(p) + except: + self.logger.error(ppp("Unexpected or invalid packet:", p)) + raise + + # from service back to client + p = (Ether(src=self.pg4.remote_mac, dst=self.pg4.local_mac) / + IP(src=local_host.ip4, dst=self.nat_addr) / + TCP(sport=local_port, dport=eh_port_in)) + self.pg4.add_stream(p) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = self.pg4.get_capture(1) + p = capture[0] + try: + ip = p[IP] + tcp = p[TCP] + self.assertEqual(ip.src, self.nat_addr) + self.assertEqual(ip.dst, remote_host.ip4) + self.assertEqual(tcp.sport, external_port) + self.assertEqual(tcp.dport, 12345) + self.assert_packet_checksums_valid(p) + except: + self.logger.error(ppp("Unexpected or invalid packet:", p)) + raise + + def test_static_with_port_out2(self): + """ 1:1 NAPT asymmetrical rule """ + + external_port = 80 + local_port = 8080 + + self.vapi.nat44_forwarding_enable_disable(1) + self.nat44_add_static_mapping(self.pg0.remote_ip4, self.nat_addr, + local_port, external_port, + proto=IP_PROTOS.tcp, out2in_only=1) + 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] + try: + ip = p[IP] + tcp = p[TCP] + self.assertEqual(ip.dst, self.pg0.remote_ip4) + self.assertEqual(tcp.dport, local_port) + self.assert_packet_checksums_valid(p) + except: + self.logger.error(ppp("Unexpected or invalid packet:", p)) + raise + + # ICMP error + p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / + IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4) / + ICMP(type=11) / capture[0][IP]) + 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: + self.assertEqual(p[IP].src, self.nat_addr) + inner = p[IPerror] + self.assertEqual(inner.dst, self.nat_addr) + self.assertEqual(inner[TCPerror].dport, external_port) + except: + self.logger.error(ppp("Unexpected or invalid packet:", p)) + raise + + # from service back to client + p = (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) / + IP(src=self.pg0.remote_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.assert_packet_checksums_valid(p) + except: + self.logger.error(ppp("Unexpected or invalid packet:", p)) + raise + + # ICMP error + p = (Ether(dst=self.pg1.local_mac, src=self.pg1.remote_mac) / + IP(src=self.pg1.remote_ip4, dst=self.nat_addr) / + ICMP(type=11) / capture[0][IP]) + self.pg1.add_stream(p) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = self.pg0.get_capture(1) + p = capture[0] + try: + self.assertEqual(p[IP].dst, self.pg0.remote_ip4) + inner = p[IPerror] + self.assertEqual(inner.src, self.pg0.remote_ip4) + self.assertEqual(inner[TCPerror].sport, local_port) + except: + self.logger.error(ppp("Unexpected or invalid packet:", p)) + raise + + # from client to server (no translation) + p = (Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac) / + IP(src=self.pg1.remote_ip4, dst=self.pg0.remote_ip4) / + TCP(sport=12346, dport=local_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] + try: + ip = p[IP] + tcp = p[TCP] + self.assertEqual(ip.dst, self.pg0.remote_ip4) + self.assertEqual(tcp.dport, local_port) + self.assert_packet_checksums_valid(p) + except: + self.logger.error(ppp("Unexpected or invalid packet:", p)) + raise + + # from service back to client (no translation) + p = (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) / + IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4) / + TCP(sport=local_port, dport=12346)) + 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.pg0.remote_ip4) + self.assertEqual(tcp.sport, local_port) + self.assert_packet_checksums_valid(p) + except: + self.logger.error(ppp("Unexpected or invalid packet:", p)) + raise + def tearDown(self): - super(TestNAT44, self).tearDown() + super(TestNAT44EndpointDependent, self).tearDown() if not self.vpp_dead: self.logger.info(self.vapi.cli("show nat44 addresses")) self.logger.info(self.vapi.cli("show nat44 interfaces")) self.logger.info(self.vapi.cli("show nat44 static mappings")) self.logger.info(self.vapi.cli("show nat44 interface address")) self.logger.info(self.vapi.cli("show nat44 sessions detail")) - self.logger.info(self.vapi.cli("show nat virtual-reassembly")) self.logger.info(self.vapi.cli("show nat44 hash tables detail")) - self.vapi.cli("nat addr-port-assignment-alg default") self.clear_nat44() self.vapi.cli("clear logging") -- 2.16.6