nat: refactor and split fo EI/ED features
[vpp.git] / src / plugins / nat / nat44-ei / nat44_ei.c
1 /*
2  * nat44_ei.c - nat44 endpoint dependent plugin
3  *
4  * Copyright (c) 2020 Cisco and/or its affiliates.
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at:
8  *
9  *     http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
14  * License for the specific language governing permissions and limitations
15  * under the License.
16  */
17
18 #include <vnet/vnet.h>
19 #include <vnet/ip/ip.h>
20 #include <vnet/ip/ip4.h>
21 #include <vnet/plugin/plugin.h>
22 #include <nat/nat.h>
23 #include <nat/nat_dpo.h>
24 #include <nat/lib/ipfix_logging.h>
25 #include <nat/lib/nat_syslog.h>
26 #include <nat/nat_inlines.h>
27 #include <nat/nat44/inlines.h>
28 #include <nat/nat_affinity.h>
29 #include <vnet/fib/fib_table.h>
30 #include <vnet/fib/ip4_fib.h>
31 #include <vnet/ip/reass/ip4_sv_reass.h>
32 #include <vppinfra/bihash_16_8.h>
33 #include <nat/nat44/ed_inlines.h>
34 #include <vnet/ip/ip_table.h>
35
36 #include <nat/nat44-ei/nat44_ei_inlines.h>
37 #include <nat/nat44-ei/nat44_ei.h>
38
39 int
40 nat44_ei_plugin_enable ()
41 {
42   nat44_ei_set_alloc_default ();
43   nat_ha_enable ();
44   return 0;
45 }
46
47 void
48 nat44_ei_plugin_disable ()
49 {
50   nat_ha_disable ();
51 }
52
53 void
54 nat44_ei_free_session_data (snat_main_t *sm, snat_session_t *s,
55                             u32 thread_index, u8 is_ha)
56 {
57   clib_bihash_kv_8_8_t kv;
58
59   snat_main_per_thread_data_t *tsm =
60     vec_elt_at_index (sm->per_thread_data, thread_index);
61
62   init_nat_i2o_k (&kv, s);
63   if (clib_bihash_add_del_8_8 (&tsm->in2out, &kv, 0))
64     nat_elog_warn ("in2out key del failed");
65
66   init_nat_o2i_k (&kv, s);
67   if (clib_bihash_add_del_8_8 (&tsm->out2in, &kv, 0))
68     nat_elog_warn ("out2in key del failed");
69
70   if (!is_ha)
71     {
72       nat_syslog_nat44_apmdel (s->user_index, s->in2out.fib_index,
73                                &s->in2out.addr, s->in2out.port,
74                                &s->out2in.addr, s->out2in.port, s->nat_proto);
75
76       nat_ipfix_logging_nat44_ses_delete (
77         thread_index, s->in2out.addr.as_u32, s->out2in.addr.as_u32,
78         s->nat_proto, s->in2out.port, s->out2in.port, s->in2out.fib_index);
79
80       nat_ha_sdel (&s->out2in.addr, s->out2in.port, &s->ext_host_addr,
81                    s->ext_host_port, s->nat_proto, s->out2in.fib_index,
82                    thread_index);
83     }
84
85   if (snat_is_session_static (s))
86     return;
87
88   snat_free_outside_address_and_port (sm->addresses, thread_index,
89                                       &s->out2in.addr, s->out2in.port,
90                                       s->nat_proto);
91 }
92
93 static_always_inline void
94 nat44_ei_user_del_sessions (snat_user_t *u, u32 thread_index)
95 {
96   dlist_elt_t *elt;
97   snat_session_t *s;
98
99   snat_main_t *sm = &snat_main;
100   snat_main_per_thread_data_t *tsm = &sm->per_thread_data[thread_index];
101
102   // get head
103   elt =
104     pool_elt_at_index (tsm->list_pool, u->sessions_per_user_list_head_index);
105   // get first element
106   elt = pool_elt_at_index (tsm->list_pool, elt->next);
107
108   while (elt->value != ~0)
109     {
110       s = pool_elt_at_index (tsm->sessions, elt->value);
111       elt = pool_elt_at_index (tsm->list_pool, elt->next);
112
113       nat44_ei_free_session_data (sm, s, thread_index, 0);
114       nat44_delete_session (sm, s, thread_index);
115     }
116 }
117
118 int
119 nat44_ei_user_del (ip4_address_t *addr, u32 fib_index)
120 {
121   int rv = 1;
122
123   snat_main_t *sm = &snat_main;
124   snat_main_per_thread_data_t *tsm;
125
126   snat_user_key_t user_key;
127   clib_bihash_kv_8_8_t kv, value;
128
129   if (sm->endpoint_dependent)
130     return rv;
131
132   user_key.addr.as_u32 = addr->as_u32;
133   user_key.fib_index = fib_index;
134   kv.key = user_key.as_u64;
135
136   if (sm->num_workers > 1)
137     {
138       vec_foreach (tsm, sm->per_thread_data)
139         {
140           if (!clib_bihash_search_8_8 (&tsm->user_hash, &kv, &value))
141             {
142               nat44_ei_user_del_sessions (
143                 pool_elt_at_index (tsm->users, value.value),
144                 tsm->thread_index);
145               rv = 0;
146               break;
147             }
148         }
149     }
150   else
151     {
152       tsm = vec_elt_at_index (sm->per_thread_data, sm->num_workers);
153       if (!clib_bihash_search_8_8 (&tsm->user_hash, &kv, &value))
154         {
155           nat44_ei_user_del_sessions (
156             pool_elt_at_index (tsm->users, value.value), tsm->thread_index);
157           rv = 0;
158         }
159     }
160   return rv;
161 }
162
163 void
164 nat44_ei_static_mapping_del_sessions (snat_main_t *sm,
165                                       snat_main_per_thread_data_t *tsm,
166                                       snat_user_key_t u_key, int addr_only,
167                                       ip4_address_t e_addr, u16 e_port)
168 {
169   clib_bihash_kv_8_8_t kv, value;
170   kv.key = u_key.as_u64;
171   u64 user_index;
172   dlist_elt_t *head, *elt;
173   snat_user_t *u;
174   snat_session_t *s;
175   u32 elt_index, head_index, ses_index;
176
177   if (!clib_bihash_search_8_8 (&tsm->user_hash, &kv, &value))
178     {
179       user_index = value.value;
180       u = pool_elt_at_index (tsm->users, user_index);
181       if (u->nstaticsessions)
182         {
183           head_index = u->sessions_per_user_list_head_index;
184           head = pool_elt_at_index (tsm->list_pool, head_index);
185           elt_index = head->next;
186           elt = pool_elt_at_index (tsm->list_pool, elt_index);
187           ses_index = elt->value;
188           while (ses_index != ~0)
189             {
190               s = pool_elt_at_index (tsm->sessions, ses_index);
191               elt = pool_elt_at_index (tsm->list_pool, elt->next);
192               ses_index = elt->value;
193
194               if (!addr_only)
195                 {
196                   if ((s->out2in.addr.as_u32 != e_addr.as_u32) ||
197                       (s->out2in.port != e_port))
198                     continue;
199                 }
200
201               if (is_lb_session (s))
202                 continue;
203
204               if (!snat_is_session_static (s))
205                 continue;
206
207               nat_free_session_data (sm, s, tsm - sm->per_thread_data, 0);
208               nat44_delete_session (sm, s, tsm - sm->per_thread_data);
209
210               if (!addr_only)
211                 break;
212             }
213         }
214     }
215 }
216
217 u32
218 nat44_ei_get_in2out_worker_index (ip4_header_t *ip0, u32 rx_fib_index0,
219                                   u8 is_output)
220 {
221   snat_main_t *sm = &snat_main;
222   u32 next_worker_index = 0;
223   u32 hash;
224
225   next_worker_index = sm->first_worker_index;
226   hash = ip0->src_address.as_u32 + (ip0->src_address.as_u32 >> 8) +
227          (ip0->src_address.as_u32 >> 16) + (ip0->src_address.as_u32 >> 24);
228
229   if (PREDICT_TRUE (is_pow2 (_vec_len (sm->workers))))
230     next_worker_index += sm->workers[hash & (_vec_len (sm->workers) - 1)];
231   else
232     next_worker_index += sm->workers[hash % _vec_len (sm->workers)];
233
234   return next_worker_index;
235 }
236
237 u32
238 nat44_ei_get_out2in_worker_index (vlib_buffer_t *b, ip4_header_t *ip0,
239                                   u32 rx_fib_index0, u8 is_output)
240 {
241   snat_main_t *sm = &snat_main;
242   udp_header_t *udp;
243   u16 port;
244   clib_bihash_kv_8_8_t kv, value;
245   snat_static_mapping_t *m;
246   u32 proto;
247   u32 next_worker_index = 0;
248
249   /* first try static mappings without port */
250   if (PREDICT_FALSE (pool_elts (sm->static_mappings)))
251     {
252       init_nat_k (&kv, ip0->dst_address, 0, rx_fib_index0, 0);
253       if (!clib_bihash_search_8_8 (&sm->static_mapping_by_external, &kv,
254                                    &value))
255         {
256           m = pool_elt_at_index (sm->static_mappings, value.value);
257           return m->workers[0];
258         }
259     }
260
261   proto = ip_proto_to_nat_proto (ip0->protocol);
262   udp = ip4_next_header (ip0);
263   port = udp->dst_port;
264
265   /* unknown protocol */
266   if (PREDICT_FALSE (proto == NAT_PROTOCOL_OTHER))
267     {
268       /* use current thread */
269       return vlib_get_thread_index ();
270     }
271
272   if (PREDICT_FALSE (ip0->protocol == IP_PROTOCOL_ICMP))
273     {
274       icmp46_header_t *icmp = (icmp46_header_t *) udp;
275       icmp_echo_header_t *echo = (icmp_echo_header_t *) (icmp + 1);
276       if (!icmp_type_is_error_message (
277             vnet_buffer (b)->ip.reass.icmp_type_or_tcp_flags))
278         port = vnet_buffer (b)->ip.reass.l4_src_port;
279       else
280         {
281           /* if error message, then it's not fragmented and we can access it */
282           ip4_header_t *inner_ip = (ip4_header_t *) (echo + 1);
283           proto = ip_proto_to_nat_proto (inner_ip->protocol);
284           void *l4_header = ip4_next_header (inner_ip);
285           switch (proto)
286             {
287             case NAT_PROTOCOL_ICMP:
288               icmp = (icmp46_header_t *) l4_header;
289               echo = (icmp_echo_header_t *) (icmp + 1);
290               port = echo->identifier;
291               break;
292             case NAT_PROTOCOL_UDP:
293             case NAT_PROTOCOL_TCP:
294               port = ((tcp_udp_header_t *) l4_header)->src_port;
295               break;
296             default:
297               return vlib_get_thread_index ();
298             }
299         }
300     }
301
302   /* try static mappings with port */
303   if (PREDICT_FALSE (pool_elts (sm->static_mappings)))
304     {
305       init_nat_k (&kv, ip0->dst_address, port, rx_fib_index0, proto);
306       if (!clib_bihash_search_8_8 (&sm->static_mapping_by_external, &kv,
307                                    &value))
308         {
309           m = pool_elt_at_index (sm->static_mappings, value.value);
310           return m->workers[0];
311         }
312     }
313
314   /* worker by outside port */
315   next_worker_index = sm->first_worker_index;
316   next_worker_index +=
317     sm->workers[(clib_net_to_host_u16 (port) - 1024) / sm->port_per_thread];
318   return next_worker_index;
319 }
320
321 static int
322 nat44_ei_alloc_default_cb (snat_address_t *addresses, u32 fib_index,
323                            u32 thread_index, nat_protocol_t proto,
324                            ip4_address_t *addr, u16 *port, u16 port_per_thread,
325                            u32 snat_thread_index)
326 {
327   int i;
328   snat_address_t *a, *ga = 0;
329   u32 portnum;
330
331   for (i = 0; i < vec_len (addresses); i++)
332     {
333       a = addresses + i;
334       switch (proto)
335         {
336 #define _(N, j, n, s)                                                         \
337   case NAT_PROTOCOL_##N:                                                      \
338     if (a->busy_##n##_ports_per_thread[thread_index] < port_per_thread)       \
339       {                                                                       \
340         if (a->fib_index == fib_index)                                        \
341           {                                                                   \
342             while (1)                                                         \
343               {                                                               \
344                 portnum = (port_per_thread * snat_thread_index) +             \
345                           snat_random_port (0, port_per_thread - 1) + 1024;   \
346                 if (a->busy_##n##_port_refcounts[portnum])                    \
347                   continue;                                                   \
348                 --a->busy_##n##_port_refcounts[portnum];                      \
349                 a->busy_##n##_ports_per_thread[thread_index]++;               \
350                 a->busy_##n##_ports++;                                        \
351                 *addr = a->addr;                                              \
352                 *port = clib_host_to_net_u16 (portnum);                       \
353                 return 0;                                                     \
354               }                                                               \
355           }                                                                   \
356         else if (a->fib_index == ~0)                                          \
357           {                                                                   \
358             ga = a;                                                           \
359           }                                                                   \
360       }                                                                       \
361     break;
362           foreach_nat_protocol
363 #undef _
364             default : nat_elog_info ("unknown protocol");
365           return 1;
366         }
367     }
368
369   if (ga)
370     {
371       a = ga;
372       switch (proto)
373         {
374 #define _(N, j, n, s)                                                         \
375   case NAT_PROTOCOL_##N:                                                      \
376     while (1)                                                                 \
377       {                                                                       \
378         portnum = (port_per_thread * snat_thread_index) +                     \
379                   snat_random_port (0, port_per_thread - 1) + 1024;           \
380         if (a->busy_##n##_port_refcounts[portnum])                            \
381           continue;                                                           \
382         ++a->busy_##n##_port_refcounts[portnum];                              \
383         a->busy_##n##_ports_per_thread[thread_index]++;                       \
384         a->busy_##n##_ports++;                                                \
385         *addr = a->addr;                                                      \
386         *port = clib_host_to_net_u16 (portnum);                               \
387         return 0;                                                             \
388       }
389           break;
390           foreach_nat_protocol
391 #undef _
392             default : nat_elog_info ("unknown protocol");
393           return 1;
394         }
395     }
396
397   /* Totally out of translations to use... */
398   nat_ipfix_logging_addresses_exhausted (thread_index, 0);
399   return 1;
400 }
401
402 static int
403 nat44_ei_alloc_range_cb (snat_address_t *addresses, u32 fib_index,
404                          u32 thread_index, nat_protocol_t proto,
405                          ip4_address_t *addr, u16 *port, u16 port_per_thread,
406                          u32 snat_thread_index)
407 {
408   snat_main_t *sm = &snat_main;
409   snat_address_t *a = addresses;
410   u16 portnum, ports;
411
412   ports = sm->end_port - sm->start_port + 1;
413
414   if (!vec_len (addresses))
415     goto exhausted;
416
417   switch (proto)
418     {
419 #define _(N, i, n, s)                                                         \
420   case NAT_PROTOCOL_##N:                                                      \
421     if (a->busy_##n##_ports < ports)                                          \
422       {                                                                       \
423         while (1)                                                             \
424           {                                                                   \
425             portnum = snat_random_port (sm->start_port, sm->end_port);        \
426             if (a->busy_##n##_port_refcounts[portnum])                        \
427               continue;                                                       \
428             ++a->busy_##n##_port_refcounts[portnum];                          \
429             a->busy_##n##_ports++;                                            \
430             *addr = a->addr;                                                  \
431             *port = clib_host_to_net_u16 (portnum);                           \
432             return 0;                                                         \
433           }                                                                   \
434       }                                                                       \
435     break;
436       foreach_nat_protocol
437 #undef _
438         default : nat_elog_info ("unknown protocol");
439       return 1;
440     }
441
442 exhausted:
443   /* Totally out of translations to use... */
444   nat_ipfix_logging_addresses_exhausted (thread_index, 0);
445   return 1;
446 }
447
448 static int
449 nat44_ei_alloc_mape_cb (snat_address_t *addresses, u32 fib_index,
450                         u32 thread_index, nat_protocol_t proto,
451                         ip4_address_t *addr, u16 *port, u16 port_per_thread,
452                         u32 snat_thread_index)
453 {
454   snat_main_t *sm = &snat_main;
455   snat_address_t *a = addresses;
456   u16 m, ports, portnum, A, j;
457   m = 16 - (sm->psid_offset + sm->psid_length);
458   ports = (1 << (16 - sm->psid_length)) - (1 << m);
459
460   if (!vec_len (addresses))
461     goto exhausted;
462
463   switch (proto)
464     {
465 #define _(N, i, n, s)                                                         \
466   case NAT_PROTOCOL_##N:                                                      \
467     if (a->busy_##n##_ports < ports)                                          \
468       {                                                                       \
469         while (1)                                                             \
470           {                                                                   \
471             A = snat_random_port (1, pow2_mask (sm->psid_offset));            \
472             j = snat_random_port (0, pow2_mask (m));                          \
473             portnum = A | (sm->psid << sm->psid_offset) | (j << (16 - m));    \
474             if (a->busy_##n##_port_refcounts[portnum])                        \
475               continue;                                                       \
476             ++a->busy_##n##_port_refcounts[portnum];                          \
477             a->busy_##n##_ports++;                                            \
478             *addr = a->addr;                                                  \
479             *port = clib_host_to_net_u16 (portnum);                           \
480             return 0;                                                         \
481           }                                                                   \
482       }                                                                       \
483     break;
484       foreach_nat_protocol
485 #undef _
486         default : nat_elog_info ("unknown protocol");
487       return 1;
488     }
489
490 exhausted:
491   /* Totally out of translations to use... */
492   nat_ipfix_logging_addresses_exhausted (thread_index, 0);
493   return 1;
494 }
495
496 void
497 nat44_ei_set_alloc_default ()
498 {
499   snat_main_t *sm = &snat_main;
500
501   sm->addr_and_port_alloc_alg = NAT_ADDR_AND_PORT_ALLOC_ALG_DEFAULT;
502   sm->alloc_addr_and_port = nat44_ei_alloc_default_cb;
503 }
504
505 void
506 nat44_ei_set_alloc_range (u16 start_port, u16 end_port)
507 {
508   snat_main_t *sm = &snat_main;
509
510   sm->addr_and_port_alloc_alg = NAT_ADDR_AND_PORT_ALLOC_ALG_RANGE;
511   sm->alloc_addr_and_port = nat44_ei_alloc_range_cb;
512   sm->start_port = start_port;
513   sm->end_port = end_port;
514 }
515
516 void
517 nat44_ei_set_alloc_mape (u16 psid, u16 psid_offset, u16 psid_length)
518 {
519   snat_main_t *sm = &snat_main;
520
521   sm->addr_and_port_alloc_alg = NAT_ADDR_AND_PORT_ALLOC_ALG_MAPE;
522   sm->alloc_addr_and_port = nat44_ei_alloc_mape_cb;
523   sm->psid = psid;
524   sm->psid_offset = psid_offset;
525   sm->psid_length = psid_length;
526 }
527
528 /*
529  * fd.io coding-style-patch-verification: ON
530  *
531  * Local Variables:
532  * eval: (c-set-style "gnu")
533  * End:
534  */