2 * Copyright (c) 2020 Cisco and/or its affiliates.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at:
7 * http://www.apache.org/licenses/LICENSE-2.0
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
17 * @brief NAT port/address allocation lib
20 #include <nat/lib/lib.h>
21 #include <nat/lib/alloc.h>
23 static_always_inline void
24 nat_ip4_addr_increment (ip4_address_t * addr)
27 v = clib_net_to_host_u32 (addr->as_u32) + 1;
28 addr->as_u32 = clib_host_to_net_u32 (v);
32 nat_add_del_ip4_pool_addr (nat_ip4_pool_t * pool, ip4_address_t addr,
36 nat_ip4_pool_addr_t *a = 0;
37 vlib_thread_main_t *tm = vlib_get_thread_main ();
39 // lookup for the address
40 for (i = 0; i < vec_len (pool->pool_addr); i++)
42 if (pool->pool_addr[i].addr.as_u32 == addr.as_u32)
44 a = pool->pool_addr + 1;
51 return NAT_ERROR_VALUE_EXIST;
52 vec_add2 (pool->pool_addr, a, 1);
54 #define _(N, i, n, s) \
55 clib_bitmap_alloc (a->busy_##n##_port_bitmap, 65535); \
56 a->busy_##n##_ports = 0; \
57 vec_validate_init_empty (a->busy_##n##_ports_per_thread, tm->n_vlib_mains - 1, 0);
64 return NAT_ERROR_NO_SUCH_ENTRY;
65 #define _(N, id, n, s) \
66 clib_bitmap_free (a->busy_##n##_port_bitmap); \
67 vec_free (a->busy_##n##_ports_per_thread);
70 vec_del1 (pool->pool_addr, i);
76 nat_add_del_ip4_pool_addrs (nat_ip4_pool_t * pool,
77 ip4_address_t addr, u32 count, u8 is_add,
82 for (i = 0; i < count; i++)
85 // a) consider if we could benefit from pre and post cb
86 // b) consider if we could benefit from add/del cb separation
89 // pool->add_del_pool_addr_pre_cb (&addr, is_add, opaque);
91 if ((rv = nat_add_del_ip4_pool_addr (pool, addr, is_add)) != 0)
95 // pool->add_del_pool_addr_post_cb (&addr, is_add, opaque);
97 pool->add_del_pool_addr_cb (addr, is_add, opaque);
98 nat_ip4_addr_increment (&addr);
104 static_always_inline u16
105 nat_random_port (u32 * random_seed, u16 min, u16 max)
107 return min + random_u32 (random_seed) /
108 (random_u32_max () / (max - min + 1) + 1);
112 nat_alloc_ip4_addr_and_port_cb_default (nat_ip4_pool_t * pool,
115 u32 nat_thread_index,
118 nat_ip4_addr_port_t * out)
120 nat_ip4_pool_addr_t *a, *ga = 0;
124 for (i = 0; i < vec_len (pool->pool_addr); i++)
126 a = pool->pool_addr + i;
129 #define _(N, j, n, s) \
130 case NAT_PROTOCOL_##N: \
131 if (a->busy_##n##_ports_per_thread[thread_index] < port_per_thread) \
133 if (a->fib_index == fib_index) \
137 portnum = (port_per_thread * \
138 nat_thread_index) + \
139 nat_random_port(&pool->random_seed, 1, port_per_thread) + 1024; \
140 if (clib_bitmap_get_no_check (a->busy_##n##_port_bitmap, portnum)) \
142 clib_bitmap_set_no_check (a->busy_##n##_port_bitmap, portnum, 1); \
143 a->busy_##n##_ports_per_thread[thread_index]++; \
144 a->busy_##n##_ports++; \
145 out->addr = a->addr; \
146 out->port = clib_host_to_net_u16(portnum); \
150 else if (a->fib_index == ~0) \
159 return NAT_ERROR_UNKNOWN_PROTOCOL;
168 #define _(N, j, n, s) \
169 case NAT_PROTOCOL_##N: \
172 portnum = (port_per_thread * \
173 nat_thread_index) + \
174 nat_random_port(&pool->random_seed, 1, port_per_thread) + 1024; \
175 if (clib_bitmap_get_no_check (a->busy_##n##_port_bitmap, portnum)) \
177 clib_bitmap_set_no_check (a->busy_##n##_port_bitmap, portnum, 1); \
178 a->busy_##n##_ports_per_thread[thread_index]++; \
179 a->busy_##n##_ports++; \
180 out->addr = a->addr; \
181 out->port = clib_host_to_net_u16(portnum); \
188 return NAT_ERROR_UNKNOWN_PROTOCOL;
191 return NAT_ERROR_OUT_OF_TRANSLATIONS;
195 nat_alloc_ip4_addr_and_port (nat_ip4_pool_t * pool,
198 u32 nat_thread_index,
200 u16 protocol, nat_ip4_addr_port_t * out)
202 return pool->alloc_addr_and_port_cb (pool,
206 port_per_thread, protocol, out);
209 // TODO: consider using standard u16 port and ip4_address_t as input ?
211 nat_free_ip4_addr_and_port (nat_ip4_pool_t * pool,
213 u16 protocol, nat_ip4_addr_port_t * addr_port)
215 nat_ip4_pool_addr_t *a = 0;
217 u16 port = clib_net_to_host_u16 (addr_port->port);
219 for (i = 0; i < vec_len (pool->pool_addr); i++)
221 if (pool->pool_addr[i].addr.as_u32 == addr_port->addr.as_u32)
223 a = pool->pool_addr + i;
230 return NAT_ERROR_NO_SUCH_ENTRY;
235 #define _(N, i, n, s) \
236 case NAT_PROTOCOL_##N: \
237 ASSERT (clib_bitmap_get_no_check (a->busy_##n##_port_bitmap, \
239 clib_bitmap_set_no_check (a->busy_##n##_port_bitmap, \
241 a->busy_##n##_ports--; \
242 a->busy_##n##_ports_per_thread[thread_index]--; \
247 return NAT_ERROR_UNKNOWN_PROTOCOL;
253 * fd.io coding-style-patch-verification: ON
256 * eval: (c-set-style "gnu")