From 36ea2d6d3a67a60534a7c2b58551688858a1ce7f Mon Sep 17 00:00:00 2001 From: Matus Fabian Date: Tue, 24 Oct 2017 04:13:49 -0700 Subject: One armed NAT (VPP-1035) Use a single physical interface in order to accomplish NAT44/NAT64. Change-Id: I0c8138953a7a4075df306172e125abad771315e4 Signed-off-by: Matus Fabian --- src/plugins/nat/in2out.c | 4 +- src/plugins/nat/nat.api | 2 +- src/plugins/nat/nat.c | 266 +++++++++++++++++++++++++++++++++++++++++--- src/plugins/nat/nat.h | 8 +- src/plugins/nat/nat64.c | 22 +++- src/plugins/nat/nat64_cli.c | 4 +- src/plugins/nat/nat_api.c | 14 ++- src/plugins/nat/nat_det.c | 2 +- 8 files changed, 289 insertions(+), 33 deletions(-) (limited to 'src/plugins/nat') diff --git a/src/plugins/nat/in2out.c b/src/plugins/nat/in2out.c index 24ff38602c5..9f668d89f97 100755 --- a/src/plugins/nat/in2out.c +++ b/src/plugins/nat/in2out.c @@ -182,7 +182,7 @@ snat_not_translate_fast (snat_main_t * sm, vlib_node_runtime_t *node, pool_foreach (i, sm->interfaces, ({ /* NAT packet aimed at outside interface */ - if ((i->is_inside == 0) && (sw_if_index == i->sw_if_index)) + if ((nat_interface_is_outside(i)) && (sw_if_index == i->sw_if_index)) return 0; })); } @@ -3523,7 +3523,7 @@ snat_hairpin_src_fn (vlib_main_t * vm, pool_foreach (i, sm->output_feature_interfaces, ({ /* Only packets from NAT inside interface */ - if ((i->is_inside == 1) && (sw_if_index0 == i->sw_if_index)) + if ((nat_interface_is_inside(i)) && (sw_if_index0 == i->sw_if_index)) { if (PREDICT_FALSE ((vnet_buffer (b0)->snat.flags) & SNAT_FLAG_HAIRPINNING)) diff --git a/src/plugins/nat/nat.api b/src/plugins/nat/nat.api index 84187578c9d..fe408211d83 100644 --- a/src/plugins/nat/nat.api +++ b/src/plugins/nat/nat.api @@ -827,7 +827,7 @@ define nat44_interface_dump { /** \brief NAT44 interface details response @param context - sender context, to match reply w/ request - @param is_inside - 1 if inside, 0 if outside + @param is_inside - 1 if inside, 0 if outside, 2 if inside and outside @param sw_if_index - software index of the interface */ define nat44_interface_details { diff --git a/src/plugins/nat/nat.c b/src/plugins/nat/nat.c index 887fab78779..50ab317759e 100644 --- a/src/plugins/nat/nat.c +++ b/src/plugins/nat/nat.c @@ -42,6 +42,11 @@ VNET_FEATURE_INIT (ip4_snat_out2in, static) = { .node_name = "nat44-out2in", .runs_before = VNET_FEATURES ("ip4-lookup"), }; +VNET_FEATURE_INIT (ip4_nat_classify, static) = { + .arc_name = "ip4-unicast", + .node_name = "nat44-classify", + .runs_before = VNET_FEATURES ("ip4-lookup"), +}; VNET_FEATURE_INIT (ip4_snat_det_in2out, static) = { .arc_name = "ip4-unicast", .node_name = "nat44-det-in2out", @@ -52,6 +57,11 @@ VNET_FEATURE_INIT (ip4_snat_det_out2in, static) = { .node_name = "nat44-det-out2in", .runs_before = VNET_FEATURES ("ip4-lookup"), }; +VNET_FEATURE_INIT (ip4_nat_det_classify, static) = { + .arc_name = "ip4-unicast", + .node_name = "nat44-det-classify", + .runs_before = VNET_FEATURES ("ip4-lookup"), +}; VNET_FEATURE_INIT (ip4_snat_in2out_worker_handoff, static) = { .arc_name = "ip4-unicast", .node_name = "nat44-in2out-worker-handoff", @@ -62,6 +72,11 @@ VNET_FEATURE_INIT (ip4_snat_out2in_worker_handoff, static) = { .node_name = "nat44-out2in-worker-handoff", .runs_before = VNET_FEATURES ("ip4-lookup"), }; +VNET_FEATURE_INIT (ip4_nat_handoff_classify, static) = { + .arc_name = "ip4-unicast", + .node_name = "nat44-handoff-classify", + .runs_before = VNET_FEATURES ("ip4-lookup"), +}; VNET_FEATURE_INIT (ip4_snat_in2out_fast, static) = { .arc_name = "ip4-unicast", .node_name = "nat44-in2out-fast", @@ -111,6 +126,159 @@ VLIB_PLUGIN_REGISTER () = { }; /* *INDENT-ON* */ +vlib_node_registration_t nat44_classify_node; +vlib_node_registration_t nat44_det_classify_node; +vlib_node_registration_t nat44_handoff_classify_node; + +typedef enum { + NAT44_CLASSIFY_NEXT_IN2OUT, + NAT44_CLASSIFY_NEXT_OUT2IN, + NAT44_CLASSIFY_N_NEXT, +} nat44_classify_next_t; + +static inline uword +nat44_classify_node_fn_inline (vlib_main_t * vm, + vlib_node_runtime_t * node, + vlib_frame_t * frame) +{ + u32 n_left_from, * from, * to_next; + nat44_classify_next_t next_index; + snat_main_t *sm = &snat_main; + + from = vlib_frame_vector_args (frame); + n_left_from = frame->n_vectors; + next_index = node->cached_next_index; + + while (n_left_from > 0) + { + u32 n_left_to_next; + + vlib_get_next_frame (vm, node, next_index, + to_next, n_left_to_next); + + while (n_left_from > 0 && n_left_to_next > 0) + { + u32 bi0; + vlib_buffer_t *b0; + u32 next0 = NAT44_CLASSIFY_NEXT_IN2OUT; + ip4_header_t *ip0; + snat_address_t *ap; + snat_session_key_t m_key0; + clib_bihash_kv_8_8_t kv0, value0; + + /* speculatively enqueue b0 to the current next frame */ + bi0 = from[0]; + to_next[0] = bi0; + from += 1; + to_next += 1; + n_left_from -= 1; + n_left_to_next -= 1; + + b0 = vlib_get_buffer (vm, bi0); + ip0 = vlib_buffer_get_current (b0); + + vec_foreach (ap, sm->addresses) + { + if (ip0->dst_address.as_u32 == ap->addr.as_u32) + { + next0 = NAT44_CLASSIFY_NEXT_OUT2IN; + break; + } + } + + if (PREDICT_FALSE (pool_elts (sm->static_mappings))) + { + m_key0.addr = ip0->dst_address; + m_key0.port = 0; + m_key0.protocol = 0; + m_key0.fib_index = sm->outside_fib_index; + kv0.key = m_key0.as_u64; + if (!clib_bihash_search_8_8 (&sm->static_mapping_by_external, &kv0, &value0)) + { + next0 = NAT44_CLASSIFY_NEXT_OUT2IN; + } + } + /* verify speculative enqueue, maybe switch current next frame */ + vlib_validate_buffer_enqueue_x1 (vm, node, next_index, + to_next, n_left_to_next, + bi0, next0); + } + + vlib_put_next_frame (vm, node, next_index, n_left_to_next); + } + + return frame->n_vectors; +} + +static uword +nat44_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_classify_node) = { + .function = nat44_classify_node_fn, + .name = "nat44-classify", + .vector_size = sizeof (u32), + .type = VLIB_NODE_TYPE_INTERNAL, + .n_next_nodes = NAT44_CLASSIFY_N_NEXT, + .next_nodes = { + [NAT44_CLASSIFY_NEXT_IN2OUT] = "nat44-in2out", + [NAT44_CLASSIFY_NEXT_OUT2IN] = "nat44-out2in", + }, +}; + +VLIB_NODE_FUNCTION_MULTIARCH (nat44_classify_node, + nat44_classify_node_fn); + +static uword +nat44_det_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_det_classify_node) = { + .function = nat44_det_classify_node_fn, + .name = "nat44-det-classify", + .vector_size = sizeof (u32), + .type = VLIB_NODE_TYPE_INTERNAL, + .n_next_nodes = NAT44_CLASSIFY_N_NEXT, + .next_nodes = { + [NAT44_CLASSIFY_NEXT_IN2OUT] = "nat44-det-in2out", + [NAT44_CLASSIFY_NEXT_OUT2IN] = "nat44-det-out2in", + }, +}; + +VLIB_NODE_FUNCTION_MULTIARCH (nat44_det_classify_node, + nat44_det_classify_node_fn); + +static uword +nat44_handoff_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_handoff_classify_node) = { + .function = nat44_handoff_classify_node_fn, + .name = "nat44-handoff-classify", + .vector_size = sizeof (u32), + .type = VLIB_NODE_TYPE_INTERNAL, + .n_next_nodes = NAT44_CLASSIFY_N_NEXT, + .next_nodes = { + [NAT44_CLASSIFY_NEXT_IN2OUT] = "nat44-in2out-worker-handoff", + [NAT44_CLASSIFY_NEXT_OUT2IN] = "nat44-out2in-worker-handoff", + }, +}; + +VLIB_NODE_FUNCTION_MULTIARCH (nat44_handoff_classify_node, + nat44_handoff_classify_node_fn); + /** * @brief Add/del NAT address to FIB. * @@ -190,7 +358,7 @@ void snat_add_address (snat_main_t *sm, ip4_address_t *addr, u32 vrf_id) /* Add external address to FIB */ pool_foreach (i, sm->interfaces, ({ - if (i->is_inside) + if (nat_interface_is_inside(i)) continue; snat_add_del_addr_to_fib(addr, 32, i->sw_if_index, 1); @@ -198,7 +366,7 @@ void snat_add_address (snat_main_t *sm, ip4_address_t *addr, u32 vrf_id) })); pool_foreach (i, sm->output_feature_interfaces, ({ - if (i->is_inside) + if (nat_interface_is_inside(i)) continue; snat_add_del_addr_to_fib(addr, 32, i->sw_if_index, 1); @@ -569,7 +737,7 @@ delete: /* Add/delete external address to FIB */ pool_foreach (interface, sm->interfaces, ({ - if (interface->is_inside) + if (nat_interface_is_inside(interface)) continue; snat_add_del_addr_to_fib(&e_addr, 32, interface->sw_if_index, is_add); @@ -577,7 +745,7 @@ delete: })); pool_foreach (interface, sm->output_feature_interfaces, ({ - if (interface->is_inside) + if (nat_interface_is_inside(interface)) continue; snat_add_del_addr_to_fib(&e_addr, 32, interface->sw_if_index, is_add); @@ -930,7 +1098,7 @@ int snat_del_address (snat_main_t *sm, ip4_address_t addr, u8 delete_sm) /* Delete external address from FIB */ pool_foreach (interface, sm->interfaces, ({ - if (interface->is_inside) + if (nat_interface_is_inside(interface)) continue; snat_add_del_addr_to_fib(&addr, 32, interface->sw_if_index, 0); @@ -938,7 +1106,7 @@ int snat_del_address (snat_main_t *sm, ip4_address_t addr, u8 delete_sm) })); pool_foreach (interface, sm->output_feature_interfaces, ({ - if (interface->is_inside) + if (nat_interface_is_inside(interface)) continue; snat_add_del_addr_to_fib(&addr, 32, interface->sw_if_index, 0); @@ -952,7 +1120,7 @@ int snat_interface_add_del (u32 sw_if_index, u8 is_inside, int is_del) { snat_main_t *sm = &snat_main; snat_interface_t *i; - const char * feature_name; + const char * feature_name, *del_feature_name; snat_address_t * ap; snat_static_mapping_t * m; snat_det_map_t * dm; @@ -969,9 +1137,6 @@ int snat_interface_add_del (u32 sw_if_index, u8 is_inside, int is_del) feature_name = is_inside ? "nat44-in2out" : "nat44-out2in"; } - vnet_feature_enable_disable ("ip4-unicast", feature_name, sw_if_index, - !is_del, 0, 0); - if (sm->fq_in2out_index == ~0 && !sm->deterministic && sm->num_workers > 1) sm->fq_in2out_index = vlib_frame_queue_main_init (sm->in2out_node_index, 0); @@ -983,9 +1148,63 @@ int snat_interface_add_del (u32 sw_if_index, u8 is_inside, int is_del) if (i->sw_if_index == sw_if_index) { if (is_del) - pool_put (sm->interfaces, i); + { + if (nat_interface_is_inside(i) && nat_interface_is_outside(i)) + { + if (is_inside) + i->flags &= ~NAT_INTERFACE_FLAG_IS_INSIDE; + else + i->flags &= ~NAT_INTERFACE_FLAG_IS_OUTSIDE; + + if (sm->num_workers > 1 && !sm->deterministic) + del_feature_name = "nat44-handoff-classify"; + else if (sm->deterministic) + del_feature_name = "nat44-det-classify"; + else + del_feature_name = "nat44-classify"; + + vnet_feature_enable_disable ("ip4-unicast", del_feature_name, + sw_if_index, 0, 0, 0); + vnet_feature_enable_disable ("ip4-unicast", feature_name, + sw_if_index, 1, 0, 0); + } + else + { + vnet_feature_enable_disable ("ip4-unicast", feature_name, + sw_if_index, 0, 0, 0); + pool_put (sm->interfaces, i); + } + } else - return VNET_API_ERROR_VALUE_EXIST; + { + if ((nat_interface_is_inside(i) && is_inside) || + (nat_interface_is_outside(i) && !is_inside)) + return 0; + + if (sm->num_workers > 1 && !sm->deterministic) + { + del_feature_name = !is_inside ? "nat44-in2out-worker-handoff" : + "nat44-out2in-worker-handoff"; + feature_name = "nat44-handoff-classify"; + } + else if (sm->deterministic) + { + del_feature_name = !is_inside ? "nat44-det-in2out" : + "nat44-det-out2in"; + feature_name = "nat44-det-classify"; + } + else + { + del_feature_name = !is_inside ? "nat44-in2out" : "nat44-out2in"; + feature_name = "nat44-classify"; + } + + vnet_feature_enable_disable ("ip4-unicast", del_feature_name, + sw_if_index, 0, 0, 0); + vnet_feature_enable_disable ("ip4-unicast", feature_name, + sw_if_index, 1, 0, 0); + goto set_flags; + } goto fib; } @@ -996,7 +1215,14 @@ int snat_interface_add_del (u32 sw_if_index, u8 is_inside, int is_del) pool_get (sm->interfaces, i); i->sw_if_index = sw_if_index; - i->is_inside = is_inside; + i->flags = 0; + vnet_feature_enable_disable ("ip4-unicast", feature_name, sw_if_index, 1, 0, 0); + +set_flags: + if (is_inside) + i->flags |= NAT_INTERFACE_FLAG_IS_INSIDE; + else + i->flags |= NAT_INTERFACE_FLAG_IS_OUTSIDE; /* Add/delete external addresses to FIB */ fib: @@ -1090,7 +1316,11 @@ fq: pool_get (sm->output_feature_interfaces, i); i->sw_if_index = sw_if_index; - i->is_inside = is_inside; + i->flags = 0; + if (is_inside) + i->flags |= NAT_INTERFACE_FLAG_IS_INSIDE; + else + i->flags |= NAT_INTERFACE_FLAG_IS_OUTSIDE; /* Add/delete external addresses to FIB */ fib: @@ -2486,7 +2716,9 @@ show_snat_command_fn (vlib_main_t * vm, ({ vlib_cli_output (vm, "%U %s", format_vnet_sw_interface_name, vnm, vnet_get_sw_interface (vnm, i->sw_if_index), - i->is_inside ? "in" : "out"); + (nat_interface_is_inside(i) && + nat_interface_is_outside(i)) ? "in out" : + (nat_interface_is_inside(i) ? "in" : "out")); })); pool_foreach (i, sm->output_feature_interfaces, @@ -2494,7 +2726,9 @@ show_snat_command_fn (vlib_main_t * vm, vlib_cli_output (vm, "%U output-feature %s", format_vnet_sw_interface_name, vnm, vnet_get_sw_interface (vnm, i->sw_if_index), - i->is_inside ? "in" : "out"); + (nat_interface_is_inside(i) && + nat_interface_is_outside(i)) ? "in out" : + (nat_interface_is_inside(i) ? "in" : "out")); })); if (vec_len (sm->auto_add_sw_if_indices)) diff --git a/src/plugins/nat/nat.h b/src/plugins/nat/nat.h index e53e924ff03..d4ad72520da 100644 --- a/src/plugins/nat/nat.h +++ b/src/plugins/nat/nat.h @@ -129,6 +129,9 @@ typedef enum { #define SNAT_SESSION_FLAG_UNKNOWN_PROTO 2 #define SNAT_SESSION_FLAG_LOAD_BALANCING 4 +#define NAT_INTERFACE_FLAG_IS_INSIDE 1 +#define NAT_INTERFACE_FLAG_IS_OUTSIDE 2 + typedef CLIB_PACKED(struct { snat_session_key_t out2in; /* 0-15 */ @@ -217,7 +220,7 @@ typedef struct { typedef struct { u32 sw_if_index; - u8 is_inside; + u8 flags; } snat_interface_t; typedef struct { @@ -414,6 +417,9 @@ typedef struct { */ #define snat_is_unk_proto_session(s) s->flags & SNAT_SESSION_FLAG_UNKNOWN_PROTO +#define nat_interface_is_inside(i) i->flags & NAT_INTERFACE_FLAG_IS_INSIDE +#define nat_interface_is_outside(i) i->flags & NAT_INTERFACE_FLAG_IS_OUTSIDE + /* * Why is this here? Because we don't need to touch this layer to * simply reply to an icmp. We need to change id to a unique diff --git a/src/plugins/nat/nat64.c b/src/plugins/nat/nat64.c index bfcfa9b3620..952ca8fb11b 100644 --- a/src/plugins/nat/nat64.c +++ b/src/plugins/nat/nat64.c @@ -136,7 +136,7 @@ nat64_add_del_pool_addr (ip4_address_t * addr, u32 vrf_id, u8 is_add) /* *INDENT-OFF* */ pool_foreach (interface, nm->interfaces, ({ - if (interface->is_inside) + if (nat_interface_is_inside(interface)) continue; snat_add_del_addr_to_fib (addr, 32, interface->sw_if_index, is_add); @@ -170,7 +170,7 @@ nat64_add_del_interface (u32 sw_if_index, u8 is_inside, u8 is_add) snat_address_t *ap; const char *feature_name, *arc_name; - /* Check if address already exists */ + /* Check if interface already exists */ /* *INDENT-OFF* */ pool_foreach (i, nm->interfaces, ({ @@ -185,19 +185,29 @@ nat64_add_del_interface (u32 sw_if_index, u8 is_inside, u8 is_add) if (is_add) { if (interface) - return VNET_API_ERROR_VALUE_EXIST; + goto set_flags; pool_get (nm->interfaces, interface); interface->sw_if_index = sw_if_index; - interface->is_inside = is_inside; - + interface->flags = 0; + set_flags: + if (is_inside) + interface->flags |= NAT_INTERFACE_FLAG_IS_INSIDE; + else + interface->flags |= NAT_INTERFACE_FLAG_IS_OUTSIDE; } else { if (!interface) return VNET_API_ERROR_NO_SUCH_ENTRY; - pool_put (nm->interfaces, interface); + if ((nat_interface_is_inside (interface) + && nat_interface_is_outside (interface))) + interface->flags &= + is_inside ? ~NAT_INTERFACE_FLAG_IS_INSIDE : + ~NAT_INTERFACE_FLAG_IS_OUTSIDE; + else + pool_put (nm->interfaces, interface); } if (!is_inside) diff --git a/src/plugins/nat/nat64_cli.c b/src/plugins/nat/nat64_cli.c index f3645bbbe0b..4128eda5a38 100644 --- a/src/plugins/nat/nat64_cli.c +++ b/src/plugins/nat/nat64_cli.c @@ -268,7 +268,9 @@ nat64_cli_interface_walk (snat_interface_t * i, void *ctx) vlib_cli_output (vm, " %U %s", format_vnet_sw_interface_name, vnm, vnet_get_sw_interface (vnm, i->sw_if_index), - i->is_inside ? "in" : "out"); + (nat_interface_is_inside (i) + && nat_interface_is_outside (i)) ? "in out" : + nat_interface_is_inside (i) ? "in" : "out"); return 0; } diff --git a/src/plugins/nat/nat_api.c b/src/plugins/nat/nat_api.c index 3bacdfed051..96f69eba186 100644 --- a/src/plugins/nat/nat_api.c +++ b/src/plugins/nat/nat_api.c @@ -224,7 +224,7 @@ static void memset (rmp, 0, sizeof (*rmp)); rmp->_vl_msg_id = ntohs (VL_API_SNAT_INTERFACE_DETAILS + sm->msg_id_base); rmp->sw_if_index = ntohl (i->sw_if_index); - rmp->is_inside = i->is_inside; + rmp->is_inside = nat_interface_is_inside (i); rmp->context = context; vl_msg_api_send_shmem (q, (u8 *) & rmp); @@ -306,7 +306,7 @@ send_snat_interface_output_feature_details (snat_interface_t * i, ntohs (VL_API_SNAT_INTERFACE_OUTPUT_FEATURE_DETAILS + sm->msg_id_base); rmp->sw_if_index = ntohl (i->sw_if_index); rmp->context = context; - rmp->is_inside = i->is_inside; + rmp->is_inside = nat_interface_is_inside (i); vl_msg_api_send_shmem (q, (u8 *) & rmp); } @@ -1658,7 +1658,9 @@ send_nat44_interface_details (snat_interface_t * i, memset (rmp, 0, sizeof (*rmp)); rmp->_vl_msg_id = ntohs (VL_API_NAT44_INTERFACE_DETAILS + sm->msg_id_base); rmp->sw_if_index = ntohl (i->sw_if_index); - rmp->is_inside = i->is_inside; + rmp->is_inside = (nat_interface_is_inside (i) + && nat_interface_is_outside (i)) ? 2 : + nat_interface_is_inside (i); rmp->context = context; vl_msg_api_send_shmem (q, (u8 *) & rmp); @@ -1741,7 +1743,7 @@ send_nat44_interface_output_feature_details (snat_interface_t * i, ntohs (VL_API_NAT44_INTERFACE_OUTPUT_FEATURE_DETAILS + sm->msg_id_base); rmp->sw_if_index = ntohl (i->sw_if_index); rmp->context = context; - rmp->is_inside = i->is_inside; + rmp->is_inside = nat_interface_is_inside (i); vl_msg_api_send_shmem (q, (u8 *) & rmp); } @@ -2867,7 +2869,9 @@ nat64_api_interface_walk (snat_interface_t * i, void *arg) memset (rmp, 0, sizeof (*rmp)); rmp->_vl_msg_id = ntohs (VL_API_NAT64_INTERFACE_DETAILS + sm->msg_id_base); rmp->sw_if_index = ntohl (i->sw_if_index); - rmp->is_inside = i->is_inside; + rmp->is_inside = (nat_interface_is_inside (i) + && nat_interface_is_outside (i)) ? 2 : + nat_interface_is_inside (i); rmp->context = ctx->context; vl_msg_api_send_shmem (ctx->q, (u8 *) & rmp); diff --git a/src/plugins/nat/nat_det.c b/src/plugins/nat/nat_det.c index 3af6698c09a..1b00d40b4ae 100644 --- a/src/plugins/nat/nat_det.c +++ b/src/plugins/nat/nat_det.c @@ -94,7 +94,7 @@ snat_det_add_map (snat_main_t * sm, ip4_address_t * in_addr, u8 in_plen, /* *INDENT-OFF* */ pool_foreach (i, sm->interfaces, ({ - if (i->is_inside) + if (nat_interface_is_inside(i)) continue; snat_add_del_addr_to_fib(out_addr, out_plen, i->sw_if_index, is_add); -- cgit 1.2.3-korg