diff options
Diffstat (limited to 'src/plugins/nat/lib/alloc.c')
-rw-r--r-- | src/plugins/nat/lib/alloc.c | 257 |
1 files changed, 257 insertions, 0 deletions
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 <nat/lib/alloc.h> + +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: + */ |