nat: dslite plugin separation
[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/lib.h>
21 #include <nat/lib/alloc.h>
22
23 static_always_inline void
24 nat_ip4_addr_increment (ip4_address_t * addr)
25 {
26   u32 v;
27   v = clib_net_to_host_u32 (addr->as_u32) + 1;
28   addr->as_u32 = clib_host_to_net_u32 (v);
29 }
30
31 int
32 nat_add_del_ip4_pool_addr (nat_ip4_pool_t * pool, ip4_address_t addr,
33                            u8 is_add)
34 {
35   int i;
36   nat_ip4_pool_addr_t *a = 0;
37   vlib_thread_main_t *tm = vlib_get_thread_main ();
38
39   // lookup for the address
40   for (i = 0; i < vec_len (pool->pool_addr); i++)
41     {
42       if (pool->pool_addr[i].addr.as_u32 == addr.as_u32)
43         {
44           a = pool->pool_addr + 1;
45           break;
46         }
47     }
48   if (is_add)
49     {
50       if (a)
51         return NAT_ERROR_VALUE_EXIST;
52       vec_add2 (pool->pool_addr, a, 1);
53       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       foreach_nat_protocol
59 #undef _
60     }
61   else
62     {
63       if (!a)
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);
68       foreach_nat_protocol
69 #undef _
70         vec_del1 (pool->pool_addr, i);
71     }
72   return 0;
73 }
74
75 int
76 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   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       if ((rv = nat_add_del_ip4_pool_addr (pool, addr, is_add)) != 0)
92         return rv;
93
94       // post call:
95       // pool->add_del_pool_addr_post_cb (&addr, is_add, opaque);
96
97       pool->add_del_pool_addr_cb (addr, is_add, opaque);
98       nat_ip4_addr_increment (&addr);
99     }
100
101   return 0;
102 }
103
104 static_always_inline u16
105 nat_random_port (u32 random_seed, u16 min, u16 max)
106 {
107   return min + random_u32 (&random_seed) /
108     (random_u32_max () / (max - min + 1) + 1);
109 }
110
111 int
112 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   nat_ip4_pool_addr_t *a, *ga = 0;
121   u32 i;
122   u32 portnum;
123
124   for (i = 0; i < vec_len (pool->pool_addr); i++)
125     {
126       a = pool->pool_addr + i;
127       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           foreach_nat_protocol
157 #undef _
158         default:
159           return NAT_ERROR_UNKNOWN_PROTOCOL;
160         }
161
162     }
163   if (ga)
164     {
165       a = ga;
166       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           foreach_nat_protocol
186 #undef _
187         default:
188           return NAT_ERROR_UNKNOWN_PROTOCOL;
189         }
190     }
191   return NAT_ERROR_OUT_OF_TRANSLATIONS;
192 }
193
194 int
195 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   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 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   nat_ip4_pool_addr_t *a = 0;
216   u32 i;
217   u16 port = clib_net_to_host_u16 (addr_port->port);
218
219   for (i = 0; i < vec_len (pool->pool_addr); i++)
220     {
221       if (pool->pool_addr[i].addr.as_u32 == addr_port->addr.as_u32)
222         {
223           a = pool->pool_addr + i;
224           break;
225         }
226     }
227
228   if (!a)
229     {
230       return NAT_ERROR_NO_SUCH_ENTRY;
231     }
232
233   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       foreach_nat_protocol
245 #undef _
246     default:
247       return NAT_ERROR_UNKNOWN_PROTOCOL;
248     }
249   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  */