Line data Source code
1 : /*
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:
6 : *
7 : * http://www.apache.org/licenses/LICENSE-2.0
8 : *
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.
14 : */
15 : /**
16 : * @file
17 : * @brief NAT port/address allocation lib
18 : */
19 :
20 : #include <nat/lib/lib.h>
21 : #include <nat/lib/alloc.h>
22 :
23 : static_always_inline void
24 1 : nat_ip4_addr_increment (ip4_address_t * addr)
25 : {
26 : u32 v;
27 1 : v = clib_net_to_host_u32 (addr->as_u32) + 1;
28 1 : addr->as_u32 = clib_host_to_net_u32 (v);
29 1 : }
30 :
31 : int
32 1 : nat_add_del_ip4_pool_addr (nat_ip4_pool_t * pool, ip4_address_t addr,
33 : u8 is_add)
34 : {
35 : int i;
36 1 : nat_ip4_pool_addr_t *a = 0;
37 1 : vlib_thread_main_t *tm = vlib_get_thread_main ();
38 :
39 : // lookup for the address
40 1 : for (i = 0; i < vec_len (pool->pool_addr); i++)
41 : {
42 0 : if (pool->pool_addr[i].addr.as_u32 == addr.as_u32)
43 : {
44 0 : a = pool->pool_addr + 1;
45 0 : break;
46 : }
47 : }
48 1 : if (is_add)
49 : {
50 1 : if (a)
51 0 : return NAT_ERROR_VALUE_EXIST;
52 1 : vec_add2 (pool->pool_addr, a, 1);
53 1 : a->addr = addr;
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);
58 5 : foreach_nat_protocol
59 : #undef _
60 : }
61 : else
62 : {
63 0 : if (!a)
64 0 : 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);
68 0 : foreach_nat_protocol
69 : #undef _
70 0 : vec_del1 (pool->pool_addr, i);
71 : }
72 1 : return 0;
73 : }
74 :
75 : int
76 1 : nat_add_del_ip4_pool_addrs (nat_ip4_pool_t * pool,
77 : ip4_address_t addr, u32 count, u8 is_add,
78 : void *opaque)
79 : {
80 : int i, rv;
81 :
82 2 : for (i = 0; i < count; i++)
83 : {
84 : // TODO:
85 : // a) consider if we could benefit from pre and post cb
86 : // b) consider if we could benefit from add/del cb separation
87 :
88 : // pre call:
89 : // pool->add_del_pool_addr_pre_cb (&addr, is_add, opaque);
90 :
91 1 : if ((rv = nat_add_del_ip4_pool_addr (pool, addr, is_add)) != 0)
92 0 : return rv;
93 :
94 : // post call:
95 : // pool->add_del_pool_addr_post_cb (&addr, is_add, opaque);
96 :
97 1 : pool->add_del_pool_addr_cb (addr, is_add, opaque);
98 1 : nat_ip4_addr_increment (&addr);
99 : }
100 :
101 1 : return 0;
102 : }
103 :
104 : static_always_inline u16
105 3 : nat_random_port (u32 * random_seed, u16 min, u16 max)
106 : {
107 3 : return min + random_u32 (random_seed) /
108 3 : (random_u32_max () / (max - min + 1) + 1);
109 : }
110 :
111 : int
112 3 : nat_alloc_ip4_addr_and_port_cb_default (nat_ip4_pool_t * pool,
113 : u32 fib_index,
114 : u32 thread_index,
115 : u32 nat_thread_index,
116 : u16 port_per_thread,
117 : u16 protocol,
118 : nat_ip4_addr_port_t * out)
119 : {
120 3 : nat_ip4_pool_addr_t *a, *ga = 0;
121 : u32 i;
122 : u32 portnum;
123 :
124 3 : for (i = 0; i < vec_len (pool->pool_addr); i++)
125 : {
126 3 : a = pool->pool_addr + i;
127 3 : switch (protocol)
128 : {
129 : #define _(N, j, n, s) \
130 : case NAT_PROTOCOL_##N: \
131 : if (a->busy_##n##_ports_per_thread[thread_index] < port_per_thread) \
132 : { \
133 : if (a->fib_index == fib_index) \
134 : { \
135 : while (1) \
136 : { \
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)) \
141 : continue; \
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); \
147 : return 0; \
148 : } \
149 : } \
150 : else if (a->fib_index == ~0) \
151 : { \
152 : ga = a; \
153 : } \
154 : } \
155 : break;
156 3 : foreach_nat_protocol
157 : #undef _
158 0 : default:
159 0 : return NAT_ERROR_UNKNOWN_PROTOCOL;
160 : }
161 :
162 : }
163 0 : if (ga)
164 : {
165 0 : a = ga;
166 0 : switch (protocol)
167 : {
168 : #define _(N, j, n, s) \
169 : case NAT_PROTOCOL_##N: \
170 : while (1) \
171 : { \
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)) \
176 : continue; \
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); \
182 : return 0; \
183 : }
184 : break;
185 0 : foreach_nat_protocol
186 : #undef _
187 0 : default:
188 0 : return NAT_ERROR_UNKNOWN_PROTOCOL;
189 : }
190 0 : }
191 0 : return NAT_ERROR_OUT_OF_TRANSLATIONS;
192 : }
193 :
194 : int
195 3 : nat_alloc_ip4_addr_and_port (nat_ip4_pool_t * pool,
196 : u32 fib_index,
197 : u32 thread_index,
198 : u32 nat_thread_index,
199 : u16 port_per_thread,
200 : u16 protocol, nat_ip4_addr_port_t * out)
201 : {
202 3 : return pool->alloc_addr_and_port_cb (pool,
203 : fib_index,
204 : thread_index,
205 : nat_thread_index,
206 : port_per_thread, protocol, out);
207 : }
208 :
209 : // TODO: consider using standard u16 port and ip4_address_t as input ?
210 : int
211 0 : nat_free_ip4_addr_and_port (nat_ip4_pool_t * pool,
212 : u32 thread_index,
213 : u16 protocol, nat_ip4_addr_port_t * addr_port)
214 : {
215 0 : nat_ip4_pool_addr_t *a = 0;
216 : u32 i;
217 0 : u16 port = clib_net_to_host_u16 (addr_port->port);
218 :
219 0 : for (i = 0; i < vec_len (pool->pool_addr); i++)
220 : {
221 0 : if (pool->pool_addr[i].addr.as_u32 == addr_port->addr.as_u32)
222 : {
223 0 : a = pool->pool_addr + i;
224 0 : break;
225 : }
226 : }
227 :
228 0 : if (!a)
229 : {
230 0 : return NAT_ERROR_NO_SUCH_ENTRY;
231 : }
232 :
233 0 : switch (protocol)
234 : {
235 : #define _(N, i, n, s) \
236 : case NAT_PROTOCOL_##N: \
237 : ASSERT (clib_bitmap_get_no_check (a->busy_##n##_port_bitmap, \
238 : port) == 1); \
239 : clib_bitmap_set_no_check (a->busy_##n##_port_bitmap, \
240 : port, 0); \
241 : a->busy_##n##_ports--; \
242 : a->busy_##n##_ports_per_thread[thread_index]--; \
243 : break;
244 0 : foreach_nat_protocol
245 : #undef _
246 0 : default:
247 0 : return NAT_ERROR_UNKNOWN_PROTOCOL;
248 : }
249 0 : return 0;
250 : }
251 :
252 : /*
253 : * fd.io coding-style-patch-verification: ON
254 : *
255 : * Local Variables:
256 : * eval: (c-set-style "gnu")
257 : * End:
258 : */
|