From 720435d03531da68e18c2a0dc867aa99d2daced6 Mon Sep 17 00:00:00 2001 From: Filip Varga Date: Thu, 16 Jan 2020 14:58:47 +0100 Subject: [PATCH] nat: refactor of port/address allocation functions Change-Id: Ie2a3c0f44322dd8415603b7ce51bb72d72769c95 Ticket: VPP-1815 Type: refactor Signed-off-by: Filip Varga --- src/plugins/nat/CMakeLists.txt | 15 ++ src/plugins/nat/in2out.c | 4 +- src/plugins/nat/in2out_ed.c | 6 +- src/plugins/nat/lib/alloc.c | 257 +++++++++++++++++++++ src/plugins/nat/lib/alloc.h | 132 +++++++++++ .../nat/{nat44_inlines.h => nat44/inlines.h} | 18 +- src/plugins/nat/out2in.c | 4 +- src/plugins/nat/out2in_ed.c | 8 +- 8 files changed, 429 insertions(+), 15 deletions(-) create mode 100644 src/plugins/nat/lib/alloc.c create mode 100644 src/plugins/nat/lib/alloc.h rename src/plugins/nat/{nat44_inlines.h => nat44/inlines.h} (88%) diff --git a/src/plugins/nat/CMakeLists.txt b/src/plugins/nat/CMakeLists.txt index 372bbd61bb3..edba893cafe 100644 --- a/src/plugins/nat/CMakeLists.txt +++ b/src/plugins/nat/CMakeLists.txt @@ -11,6 +11,21 @@ # See the License for the specific language governing permissions and # limitations under the License. +set(NAT_SRCS + lib/alloc.c +) + +set(NAT_HEADERS + lib/alloc.h +) + +add_vpp_library(nat + SOURCES ${NAT_SRCS} + LINK_LIBRARIES m + INSTALL_HEADERS ${NAT_HEADERS} + COMPONENT libnat +) + add_vpp_plugin(nat SOURCES nat.c diff --git a/src/plugins/nat/in2out.c b/src/plugins/nat/in2out.c index 7eaaab29544..8d6f124f51b 100755 --- a/src/plugins/nat/in2out.c +++ b/src/plugins/nat/in2out.c @@ -28,7 +28,7 @@ #include #include #include -#include +#include #include #include @@ -267,7 +267,7 @@ slow_path (snat_main_t * sm, vlib_buffer_t * b0, nat44_session_try_cleanup (&ip0->src_address, rx_fib_index0, thread_index, now); - if (PREDICT_FALSE (maximum_sessions_exceeded (sm, thread_index))) + if (PREDICT_FALSE (nat44_maximum_sessions_exceeded (sm, thread_index))) { b0->error = node->errors[SNAT_IN2OUT_ERROR_MAX_SESSIONS_EXCEEDED]; nat_ipfix_logging_max_sessions (thread_index, sm->max_translations); diff --git a/src/plugins/nat/in2out_ed.c b/src/plugins/nat/in2out_ed.c index 0209a4059db..ebcd29852bb 100644 --- a/src/plugins/nat/in2out_ed.c +++ b/src/plugins/nat/in2out_ed.c @@ -28,7 +28,7 @@ #include #include #include -#include +#include #include #include @@ -211,7 +211,7 @@ slow_path_ed (snat_main_t * sm, nat44_session_try_cleanup (&key->l_addr, rx_fib_index, thread_index, now); - if (PREDICT_FALSE (maximum_sessions_exceeded (sm, thread_index))) + if (PREDICT_FALSE (nat44_maximum_sessions_exceeded (sm, thread_index))) { b->error = node->errors[NAT_IN2OUT_ED_ERROR_MAX_SESSIONS_EXCEEDED]; nat_ipfix_logging_max_sessions (thread_index, sm->max_translations); @@ -685,7 +685,7 @@ nat44_ed_in2out_unknown_proto (snat_main_t * sm, } else { - if (PREDICT_FALSE (maximum_sessions_exceeded (sm, thread_index))) + if (PREDICT_FALSE (nat44_maximum_sessions_exceeded (sm, thread_index))) { b->error = node->errors[NAT_IN2OUT_ED_ERROR_MAX_SESSIONS_EXCEEDED]; nat_ipfix_logging_max_sessions (thread_index, sm->max_translations); diff --git a/src/plugins/nat/lib/alloc.c b/src/plugins/nat/lib/alloc.c new file mode 100644 index 00000000000..2c9841b7b63 --- /dev/null +++ b/src/plugins/nat/lib/alloc.c @@ -0,0 +1,257 @@ +/* + * Copyright (c) 2020 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file + * @brief NAT port/address allocation lib + */ + +#include + +static_always_inline void +nat_ip4_addr_increment (ip4_address_t * addr) +{ + u32 v; + v = clib_net_to_host_u32 (addr->as_u32) + 1; + addr->as_u32 = clib_host_to_net_u32 (v); +} + +int +nat_add_del_ip4_pool_addr (nat_ip4_pool_t * pool, ip4_address_t addr, + u8 is_add) +{ + int i; + nat_ip4_pool_addr_t *a = 0; + vlib_thread_main_t *tm = vlib_get_thread_main (); + + // lookup for the address + for (i = 0; i < vec_len (pool); i++) + { + if (pool->pool_addr[i].addr.as_u32 == addr.as_u32) + { + a = pool->pool_addr + 1; + break; + } + } + if (is_add) + { + if (a) + return NAT_ERROR_VALUE_EXIST; + vec_add2 (pool->pool_addr, a, 1); + a->addr = addr; +#define _(N, i, n, s) \ + clib_bitmap_alloc (a->busy_##n##_port_bitmap, 65535); \ + a->busy_##n##_ports = 0; \ + vec_validate_init_empty (a->busy_##n##_ports_per_thread, tm->n_vlib_mains - 1, 0); + foreach_nat_protocol +#undef _ + } + else + { + if (!a) + return NAT_ERROR_NO_SUCH_ENTRY; +#define _(N, id, n, s) \ + clib_bitmap_free (a->busy_##n##_port_bitmap); \ + vec_free (a->busy_##n##_ports_per_thread); + foreach_nat_protocol +#undef _ + vec_del1 (pool->pool_addr, i); + } + return 0; +} + +int +nat_add_del_ip4_pool_addrs (nat_ip4_pool_t * pool, + ip4_address_t addr, u32 count, u8 is_add, + void *opaque) +{ + int i, rv; + + for (i = 0; i < count; i++) + { + // TODO: + // a) consider if we could benefit from pre and post cb + // b) consider if we could benefit from add/del cb separation + + // pre call: + // pool->add_del_pool_addr_pre_cb (&addr, is_add, opaque); + + if ((rv = nat_add_del_ip4_pool_addr (pool, addr, is_add)) != 0) + return rv; + + // post call: + // pool->add_del_pool_addr_post_cb (&addr, is_add, opaque); + + pool->add_del_pool_addr_cb (addr, is_add, opaque); + nat_ip4_addr_increment (&addr); + } + + return 0; +} + +static_always_inline u16 +nat_random_port (u32 random_seed, u16 min, u16 max) +{ + return min + random_u32 (&random_seed) / + (random_u32_max () / (max - min + 1) + 1); +} + +int +nat_alloc_ip4_addr_and_port_cb_default (nat_ip4_pool_t * pool, + u32 fib_index, + u32 thread_index, + u32 nat_thread_index, + u16 port_per_thread, + u16 protocol, + nat_ip4_addr_port_t * out) +{ + nat_ip4_pool_addr_t *a, *ga = 0; + u32 i; + u32 portnum; + + for (i = 0; i < vec_len (pool->pool_addr); i++) + { + a = pool->pool_addr + i; + switch (protocol) + { +#define _(N, j, n, s) \ + case NAT_PROTOCOL_##N: \ + if (a->busy_##n##_ports_per_thread[thread_index] < port_per_thread) \ + { \ + if (a->fib_index == fib_index) \ + { \ + while (1) \ + { \ + portnum = (port_per_thread * \ + nat_thread_index) + \ + nat_random_port(pool->random_seed, 1, port_per_thread) + 1024; \ + if (clib_bitmap_get_no_check (a->busy_##n##_port_bitmap, portnum)) \ + continue; \ + clib_bitmap_set_no_check (a->busy_##n##_port_bitmap, portnum, 1); \ + a->busy_##n##_ports_per_thread[thread_index]++; \ + a->busy_##n##_ports++; \ + out->addr = a->addr; \ + out->port = clib_host_to_net_u16(portnum); \ + return 0; \ + } \ + } \ + else if (a->fib_index == ~0) \ + { \ + ga = a; \ + } \ + } \ + break; + foreach_nat_protocol +#undef _ + default: + return NAT_ERROR_UNKNOWN_PROTOCOL; + } + + } + if (ga) + { + a = ga; + switch (protocol) + { +#define _(N, j, n, s) \ + case NAT_PROTOCOL_##N: \ + while (1) \ + { \ + portnum = (port_per_thread * \ + nat_thread_index) + \ + nat_random_port(pool->random_seed, 1, port_per_thread) + 1024; \ + if (clib_bitmap_get_no_check (a->busy_##n##_port_bitmap, portnum)) \ + continue; \ + clib_bitmap_set_no_check (a->busy_##n##_port_bitmap, portnum, 1); \ + a->busy_##n##_ports_per_thread[thread_index]++; \ + a->busy_##n##_ports++; \ + out->addr = a->addr; \ + out->port = clib_host_to_net_u16(portnum); \ + return 0; \ + } + break; + foreach_nat_protocol +#undef _ + default: + return NAT_ERROR_UNKNOWN_PROTOCOL; + } + } + return NAT_ERROR_OUT_OF_TRANSLATIONS; +} + +int +nat_alloc_ip4_addr_and_port (nat_ip4_pool_t * pool, + u32 fib_index, + u32 thread_index, + u32 nat_thread_index, + u16 port_per_thread, + u16 protocol, nat_ip4_addr_port_t * out) +{ + return pool->alloc_addr_and_port_cb (pool, + fib_index, + thread_index, + nat_thread_index, + port_per_thread, protocol, out); +} + +// TODO: consider using standard u16 port and ip4_address_t as input ? +int +nat_free_ip4_addr_and_port (nat_ip4_pool_t * pool, + u32 thread_index, + u16 protocol, nat_ip4_addr_port_t * addr_port) +{ + nat_ip4_pool_addr_t *a = 0; + u32 i; + u16 port = clib_net_to_host_u16 (addr_port->port); + + for (i = 0; i < vec_len (pool->pool_addr); i++) + { + if (pool->pool_addr[i].addr.as_u32 == addr_port->addr.as_u32) + { + a = pool->pool_addr + i; + break; + } + } + + if (!a) + { + return NAT_ERROR_NO_SUCH_ENTRY; + } + + switch (protocol) + { +#define _(N, i, n, s) \ + case NAT_PROTOCOL_##N: \ + ASSERT (clib_bitmap_get_no_check (a->busy_##n##_port_bitmap, \ + port) == 1); \ + clib_bitmap_set_no_check (a->busy_##n##_port_bitmap, \ + port, 0); \ + a->busy_##n##_ports--; \ + a->busy_##n##_ports_per_thread[thread_index]--; \ + break; + foreach_nat_protocol +#undef _ + default: + return NAT_ERROR_UNKNOWN_PROTOCOL; + } + return 0; +} + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/src/plugins/nat/lib/alloc.h b/src/plugins/nat/lib/alloc.h new file mode 100644 index 00000000000..9dba2ca94da --- /dev/null +++ b/src/plugins/nat/lib/alloc.h @@ -0,0 +1,132 @@ +/* + * Copyright (c) 2020 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file + * @brief NAT port/address allocation lib + */ + +#ifndef included_nat_lib_alloc_h__ +#define included_nat_lib_alloc_h__ + +#include + +#define foreach_nat_error \ + _(VALUE_EXIST, -1, "Value already exists") \ + _(NO_SUCH_ENTRY, -2, "No such entry") \ + _(UNKNOWN_PROTOCOL, -3, "Unknown protocol") \ + _(OUT_OF_TRANSLATIONS, -4, "Out of translations") + +#define foreach_nat_protocol \ + _(UDP, 0, udp, "udp") \ + _(TCP, 1, tcp, "tcp") \ + _(ICMP, 2, icmp, "icmp") + +typedef enum +{ +#define _(N, i, s) NAT_ERROR_##N = i, + foreach_nat_error +#undef _ +} nat_error_t; + +typedef enum +{ +#define _(N, i, n, s) NAT_PROTOCOL_##N = i, + foreach_nat_protocol +#undef _ +} nat_protocol_t; + +typedef struct nat_ip4_pool_addr_s nat_ip4_pool_addr_t; +typedef struct nat_ip4_addr_port_s nat_ip4_addr_port_t; +typedef struct nat_ip4_pool_s nat_ip4_pool_t; + +typedef void (nat_add_del_ip4_pool_addr_cb_t) (ip4_address_t addr, + u8 is_add, void *opaque); + +typedef int (nat_alloc_ip4_addr_and_port_cb_t) (nat_ip4_pool_t * pool, + u32 fib_index, + u32 thread_index, + u32 nat_thread_index, + u16 port_per_thread, + u16 protocol, + nat_ip4_addr_port_t * out); + +struct nat_ip4_pool_addr_s +{ + ip4_address_t addr; + u32 fib_index; +/* *INDENT-OFF* */ +#define _(N, i, n, s) \ + u16 busy_##n##_ports; \ + u16 * busy_##n##_ports_per_thread; \ + uword * busy_##n##_port_bitmap; + foreach_nat_protocol +#undef _ +/* *INDENT-ON* */ +}; + +struct nat_ip4_addr_port_s +{ + ip4_address_t addr; + u16 port; +}; + +struct nat_ip4_pool_s +{ + nat_add_del_ip4_pool_addr_cb_t *add_del_pool_addr_cb; + nat_alloc_ip4_addr_and_port_cb_t *alloc_addr_and_port_cb; + nat_ip4_pool_addr_t *pool_addr; + u32 random_seed; +}; + +int +nat_add_del_ip4_pool_addr (nat_ip4_pool_t * pool, + ip4_address_t addr, u8 is_add); + +int +nat_add_del_ip4_pool_addrs (nat_ip4_pool_t * pool, + ip4_address_t addr, + u32 count, u8 is_add, void *opaque); + +int +nat_alloc_ip4_addr_and_port_cb_default (nat_ip4_pool_t * pool, + u32 fib_index, + u32 thread_index, + u32 nat_thread_index, + u16 port_per_thread, + u16 protocol, + nat_ip4_addr_port_t * out); + +int +nat_alloc_ip4_addr_and_port (nat_ip4_pool_t * pool, + u32 fib_index, + u32 thread_index, + u32 nat_thread_index, + u16 port_per_thread, + u16 protocol, nat_ip4_addr_port_t * out); + +int +nat_free_ip4_addr_and_port (nat_ip4_pool_t * pool, + u32 thread_index, + u16 protocol, nat_ip4_addr_port_t * in); + +#endif /* included_nat_lib_alloc_h__ */ + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/src/plugins/nat/nat44_inlines.h b/src/plugins/nat/nat44/inlines.h similarity index 88% rename from src/plugins/nat/nat44_inlines.h rename to src/plugins/nat/nat44/inlines.h index eb1077cdf34..a7bb469b464 100644 --- a/src/plugins/nat/nat44_inlines.h +++ b/src/plugins/nat/nat44/inlines.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019 Cisco and/or its affiliates. + * Copyright (c) 2020 Cisco and/or its affiliates. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: @@ -15,12 +15,22 @@ /** * @brief The NAT44 inline functions */ -#ifndef __included_nat44_inlines_h__ -#define __included_nat44_inlines_h__ + +#ifndef included_nat44_inlines_h__ +#define included_nat44_inlines_h__ #include #include +static_always_inline u8 +nat44_maximum_sessions_exceeded (snat_main_t * sm, u32 thread_index) +{ + if (pool_elts (sm->per_thread_data[thread_index].sessions) >= + sm->max_translations) + return 1; + return 0; +} + static_always_inline void nat44_session_cleanup (snat_session_t * s, u32 thread_index) { @@ -98,7 +108,7 @@ nat44_session_try_cleanup (ip4_address_t * addr, thread_index, now); } -#endif /* __included_nat44_inlines_h__ */ +#endif /* included_nat44_inlines_h__ */ /* * fd.io coding-style-patch-verification: ON diff --git a/src/plugins/nat/out2in.c b/src/plugins/nat/out2in.c index e9ca88f1d68..144f324dbf4 100755 --- a/src/plugins/nat/out2in.c +++ b/src/plugins/nat/out2in.c @@ -28,7 +28,7 @@ #include #include #include -#include +#include #include #include @@ -191,7 +191,7 @@ create_session_for_static_mapping (snat_main_t * sm, nat44_session_try_cleanup (&in2out.addr, in2out.fib_index, thread_index, now); - if (PREDICT_FALSE (maximum_sessions_exceeded (sm, thread_index))) + if (PREDICT_FALSE (nat44_maximum_sessions_exceeded (sm, thread_index))) { b0->error = node->errors[SNAT_OUT2IN_ERROR_MAX_SESSIONS_EXCEEDED]; nat_elog_notice ("maximum sessions exceeded"); diff --git a/src/plugins/nat/out2in_ed.c b/src/plugins/nat/out2in_ed.c index ee2f85aa080..7cb205f6d6f 100644 --- a/src/plugins/nat/out2in_ed.c +++ b/src/plugins/nat/out2in_ed.c @@ -28,7 +28,7 @@ #include #include #include -#include +#include #include #include @@ -205,7 +205,7 @@ create_session_for_static_mapping_ed (snat_main_t * sm, nat44_session_try_cleanup (&l_key.addr, l_key.fib_index, thread_index, now); - if (PREDICT_FALSE (maximum_sessions_exceeded (sm, thread_index))) + if (PREDICT_FALSE (nat44_maximum_sessions_exceeded (sm, thread_index))) { b->error = node->errors[NAT_OUT2IN_ED_ERROR_MAX_SESSIONS_EXCEEDED]; nat_elog_notice ("maximum sessions exceeded"); @@ -369,7 +369,7 @@ create_bypass_for_fwd (snat_main_t * sm, vlib_buffer_t * b, ip4_header_t * ip, { u32 proto; - if (PREDICT_FALSE (maximum_sessions_exceeded (sm, thread_index))) + if (PREDICT_FALSE (nat44_maximum_sessions_exceeded (sm, thread_index))) return; u = nat_user_get_or_create (sm, &ip->dst_address, sm->inside_fib_index, @@ -592,7 +592,7 @@ nat44_ed_out2in_unknown_proto (snat_main_t * sm, } else { - if (PREDICT_FALSE (maximum_sessions_exceeded (sm, thread_index))) + if (PREDICT_FALSE (nat44_maximum_sessions_exceeded (sm, thread_index))) { b->error = node->errors[NAT_OUT2IN_ED_ERROR_MAX_SESSIONS_EXCEEDED]; nat_elog_notice ("maximum sessions exceeded"); -- 2.16.6