From 720435d03531da68e18c2a0dc867aa99d2daced6 Mon Sep 17 00:00:00 2001 From: Filip Varga Date: Thu, 16 Jan 2020 14:58:47 +0100 Subject: nat: refactor of port/address allocation functions Change-Id: Ie2a3c0f44322dd8415603b7ce51bb72d72769c95 Ticket: VPP-1815 Type: refactor Signed-off-by: Filip Varga --- src/plugins/nat/lib/alloc.c | 257 ++++++++++++++++++++++++++++++++++++++++++++ src/plugins/nat/lib/alloc.h | 132 +++++++++++++++++++++++ 2 files changed, 389 insertions(+) create mode 100644 src/plugins/nat/lib/alloc.c create mode 100644 src/plugins/nat/lib/alloc.h (limited to 'src/plugins/nat/lib') 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: + */ -- cgit 1.2.3-korg