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