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/alloc.h>
22 static_always_inline void
23 nat_ip4_addr_increment (ip4_address_t * addr)
26 v = clib_net_to_host_u32 (addr->as_u32) + 1;
27 addr->as_u32 = clib_host_to_net_u32 (v);
31 nat_add_del_ip4_pool_addr (nat_ip4_pool_t * pool, ip4_address_t addr,
35 nat_ip4_pool_addr_t *a = 0;
36 vlib_thread_main_t *tm = vlib_get_thread_main ();
38 // lookup for the address
39 for (i = 0; i < vec_len (pool); i++)
41 if (pool->pool_addr[i].addr.as_u32 == addr.as_u32)
43 a = pool->pool_addr + 1;
50 return NAT_ERROR_VALUE_EXIST;
51 vec_add2 (pool->pool_addr, a, 1);
53 #define _(N, i, n, s) \
54 clib_bitmap_alloc (a->busy_##n##_port_bitmap, 65535); \
55 a->busy_##n##_ports = 0; \
56 vec_validate_init_empty (a->busy_##n##_ports_per_thread, tm->n_vlib_mains - 1, 0);
63 return NAT_ERROR_NO_SUCH_ENTRY;
64 #define _(N, id, n, s) \
65 clib_bitmap_free (a->busy_##n##_port_bitmap); \
66 vec_free (a->busy_##n##_ports_per_thread);
69 vec_del1 (pool->pool_addr, i);
75 nat_add_del_ip4_pool_addrs (nat_ip4_pool_t * pool,
76 ip4_address_t addr, u32 count, u8 is_add,
81 for (i = 0; i < count; i++)
84 // a) consider if we could benefit from pre and post cb
85 // b) consider if we could benefit from add/del cb separation
88 // pool->add_del_pool_addr_pre_cb (&addr, is_add, opaque);
90 if ((rv = nat_add_del_ip4_pool_addr (pool, addr, is_add)) != 0)
94 // pool->add_del_pool_addr_post_cb (&addr, is_add, opaque);
96 pool->add_del_pool_addr_cb (addr, is_add, opaque);
97 nat_ip4_addr_increment (&addr);
103 static_always_inline u16
104 nat_random_port (u32 random_seed, u16 min, u16 max)
106 return min + random_u32 (&random_seed) /
107 (random_u32_max () / (max - min + 1) + 1);
111 nat_alloc_ip4_addr_and_port_cb_default (nat_ip4_pool_t * pool,
114 u32 nat_thread_index,
117 nat_ip4_addr_port_t * out)
119 nat_ip4_pool_addr_t *a, *ga = 0;
123 for (i = 0; i < vec_len (pool->pool_addr); i++)
125 a = pool->pool_addr + i;
128 #define _(N, j, n, s) \
129 case NAT_PROTOCOL_##N: \
130 if (a->busy_##n##_ports_per_thread[thread_index] < port_per_thread) \
132 if (a->fib_index == fib_index) \
136 portnum = (port_per_thread * \
137 nat_thread_index) + \
138 nat_random_port(pool->random_seed, 1, port_per_thread) + 1024; \
139 if (clib_bitmap_get_no_check (a->busy_##n##_port_bitmap, portnum)) \
141 clib_bitmap_set_no_check (a->busy_##n##_port_bitmap, portnum, 1); \
142 a->busy_##n##_ports_per_thread[thread_index]++; \
143 a->busy_##n##_ports++; \
144 out->addr = a->addr; \
145 out->port = clib_host_to_net_u16(portnum); \
149 else if (a->fib_index == ~0) \
158 return NAT_ERROR_UNKNOWN_PROTOCOL;
167 #define _(N, j, n, s) \
168 case NAT_PROTOCOL_##N: \
171 portnum = (port_per_thread * \
172 nat_thread_index) + \
173 nat_random_port(pool->random_seed, 1, port_per_thread) + 1024; \
174 if (clib_bitmap_get_no_check (a->busy_##n##_port_bitmap, portnum)) \
176 clib_bitmap_set_no_check (a->busy_##n##_port_bitmap, portnum, 1); \
177 a->busy_##n##_ports_per_thread[thread_index]++; \
178 a->busy_##n##_ports++; \
179 out->addr = a->addr; \
180 out->port = clib_host_to_net_u16(portnum); \
187 return NAT_ERROR_UNKNOWN_PROTOCOL;
190 return NAT_ERROR_OUT_OF_TRANSLATIONS;
194 nat_alloc_ip4_addr_and_port (nat_ip4_pool_t * pool,
197 u32 nat_thread_index,
199 u16 protocol, nat_ip4_addr_port_t * out)
201 return pool->alloc_addr_and_port_cb (pool,
205 port_per_thread, protocol, out);
208 // TODO: consider using standard u16 port and ip4_address_t as input ?
210 nat_free_ip4_addr_and_port (nat_ip4_pool_t * pool,
212 u16 protocol, nat_ip4_addr_port_t * addr_port)
214 nat_ip4_pool_addr_t *a = 0;
216 u16 port = clib_net_to_host_u16 (addr_port->port);
218 for (i = 0; i < vec_len (pool->pool_addr); i++)
220 if (pool->pool_addr[i].addr.as_u32 == addr_port->addr.as_u32)
222 a = pool->pool_addr + i;
229 return NAT_ERROR_NO_SUCH_ENTRY;
234 #define _(N, i, n, s) \
235 case NAT_PROTOCOL_##N: \
236 ASSERT (clib_bitmap_get_no_check (a->busy_##n##_port_bitmap, \
238 clib_bitmap_set_no_check (a->busy_##n##_port_bitmap, \
240 a->busy_##n##_ports--; \
241 a->busy_##n##_ports_per_thread[thread_index]--; \
246 return NAT_ERROR_UNKNOWN_PROTOCOL;
252 * fd.io coding-style-patch-verification: ON
255 * eval: (c-set-style "gnu")