nat: refactor and split fo EI/ED features p.2
[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 nat44_ei_main_t nat44_ei_main;
40
41 static void nat44_ei_db_free ();
42
43 static void nat44_ei_db_init (u32 translations, u32 translation_buckets,
44                               u32 user_buckets);
45
46 int
47 nat44_ei_plugin_enable (nat44_ei_config_t c)
48 {
49   nat44_ei_main_t *nm = &nat44_ei_main;
50   snat_main_t *sm = &snat_main;
51
52   clib_memset (nm, 0, sizeof (*nm));
53
54   if (!c.users)
55     c.users = 1024;
56
57   if (!c.sessions)
58     c.sessions = 10 * 1024;
59
60   nm->rconfig = c;
61
62   nm->translations = c.sessions;
63   nm->translation_buckets = nat_calc_bihash_buckets (c.sessions);
64   nm->user_buckets = nat_calc_bihash_buckets (c.users);
65
66   // OBSOLETE
67
68   sm->static_mapping_only = c.static_mapping_only;
69   sm->static_mapping_connection_tracking = c.connection_tracking;
70   sm->out2in_dpo = c.out2in_dpo;
71   sm->forwarding_enabled = 0;
72   sm->mss_clamping = 0;
73   sm->pat = (!c.static_mapping_only ||
74              (c.static_mapping_only && c.connection_tracking));
75
76   sm->max_users_per_thread = c.users;
77   sm->max_translations_per_thread = c.sessions;
78   sm->translation_buckets = nat_calc_bihash_buckets (c.sessions);
79   sm->max_translations_per_user =
80     c.user_sessions ? c.user_sessions : sm->max_translations_per_thread;
81
82   sm->inside_vrf_id = c.inside_vrf;
83   sm->inside_fib_index = fib_table_find_or_create_and_lock (
84     FIB_PROTOCOL_IP4, c.inside_vrf, sm->fib_src_hi);
85
86   sm->outside_vrf_id = c.outside_vrf;
87   sm->outside_fib_index = fib_table_find_or_create_and_lock (
88     FIB_PROTOCOL_IP4, c.outside_vrf, sm->fib_src_hi);
89
90   sm->worker_in2out_cb = nat44_ei_get_in2out_worker_index;
91   sm->worker_out2in_cb = nat44_ei_get_out2in_worker_index;
92
93   sm->in2out_node_index = sm->ei_in2out_node_index;
94   sm->out2in_node_index = sm->ei_out2in_node_index;
95
96   sm->in2out_output_node_index = sm->ei_in2out_output_node_index;
97
98   if (sm->pat)
99     {
100       sm->icmp_match_in2out_cb = icmp_match_in2out_slow;
101       sm->icmp_match_out2in_cb = icmp_match_out2in_slow;
102     }
103   else
104     {
105       sm->icmp_match_in2out_cb = icmp_match_in2out_fast;
106       sm->icmp_match_out2in_cb = icmp_match_out2in_fast;
107     }
108
109   nat_reset_timeouts (&sm->timeouts);
110   nat44_ei_db_init (nm->translations, nm->translation_buckets,
111                     nm->user_buckets);
112   nat44_ei_set_alloc_default ();
113   nat_ha_enable ();
114
115   // TODO: function for reset counters
116   vlib_zero_simple_counter (&sm->total_users, 0);
117   vlib_zero_simple_counter (&sm->total_sessions, 0);
118   vlib_zero_simple_counter (&sm->user_limit_reached, 0);
119
120   sm->enabled = 1;
121
122   return 0;
123 }
124
125 int
126 nat44_ei_plugin_disable ()
127 {
128   nat44_ei_main_t *nm = &nat44_ei_main;
129   snat_main_t *sm = &snat_main;
130   snat_interface_t *i, *vec;
131   int error = 0;
132
133   // first unregister all nodes from interfaces
134   vec = vec_dup (sm->interfaces);
135   vec_foreach (i, vec)
136     {
137       if (nat_interface_is_inside (i))
138         error = snat_interface_add_del (i->sw_if_index, 1, 1);
139       if (nat_interface_is_outside (i))
140         error = snat_interface_add_del (i->sw_if_index, 0, 1);
141
142       if (error)
143         {
144           nat_log_err ("error occurred while removing interface %u",
145                        i->sw_if_index);
146         }
147     }
148   vec_free (vec);
149   sm->interfaces = 0;
150
151   vec = vec_dup (sm->output_feature_interfaces);
152   vec_foreach (i, vec)
153     {
154       if (nat_interface_is_inside (i))
155         error = snat_interface_add_del_output_feature (i->sw_if_index, 1, 1);
156       if (nat_interface_is_outside (i))
157         error = snat_interface_add_del_output_feature (i->sw_if_index, 0, 1);
158
159       if (error)
160         {
161           nat_log_err ("error occurred while removing interface %u",
162                        i->sw_if_index);
163         }
164     }
165   vec_free (vec);
166   sm->output_feature_interfaces = 0;
167
168   nat_ha_disable ();
169   nat44_ei_db_free ();
170
171   nat44_addresses_free (&sm->addresses);
172   nat44_addresses_free (&sm->twice_nat_addresses);
173
174   vec_free (sm->to_resolve);
175   vec_free (sm->auto_add_sw_if_indices);
176   vec_free (sm->auto_add_sw_if_indices_twice_nat);
177
178   sm->to_resolve = 0;
179   sm->auto_add_sw_if_indices = 0;
180   sm->auto_add_sw_if_indices_twice_nat = 0;
181
182   sm->forwarding_enabled = 0;
183
184   sm->enabled = 0;
185   clib_memset (&nm->rconfig, 0, sizeof (nm->rconfig));
186   clib_memset (&sm->rconfig, 0, sizeof (sm->rconfig));
187
188   return error;
189 }
190
191 void
192 nat44_ei_free_session_data (snat_main_t *sm, snat_session_t *s,
193                             u32 thread_index, u8 is_ha)
194 {
195   clib_bihash_kv_8_8_t kv;
196
197   snat_main_per_thread_data_t *tsm =
198     vec_elt_at_index (sm->per_thread_data, thread_index);
199
200   init_nat_i2o_k (&kv, s);
201   if (clib_bihash_add_del_8_8 (&tsm->in2out, &kv, 0))
202     nat_elog_warn ("in2out key del failed");
203
204   init_nat_o2i_k (&kv, s);
205   if (clib_bihash_add_del_8_8 (&tsm->out2in, &kv, 0))
206     nat_elog_warn ("out2in key del failed");
207
208   if (!is_ha)
209     {
210       nat_syslog_nat44_apmdel (s->user_index, s->in2out.fib_index,
211                                &s->in2out.addr, s->in2out.port,
212                                &s->out2in.addr, s->out2in.port, s->nat_proto);
213
214       nat_ipfix_logging_nat44_ses_delete (
215         thread_index, s->in2out.addr.as_u32, s->out2in.addr.as_u32,
216         s->nat_proto, s->in2out.port, s->out2in.port, s->in2out.fib_index);
217
218       nat_ha_sdel (&s->out2in.addr, s->out2in.port, &s->ext_host_addr,
219                    s->ext_host_port, s->nat_proto, s->out2in.fib_index,
220                    thread_index);
221     }
222
223   if (snat_is_session_static (s))
224     return;
225
226   snat_free_outside_address_and_port (sm->addresses, thread_index,
227                                       &s->out2in.addr, s->out2in.port,
228                                       s->nat_proto);
229 }
230
231 static_always_inline void
232 nat44_ei_user_del_sessions (snat_user_t *u, u32 thread_index)
233 {
234   dlist_elt_t *elt;
235   snat_session_t *s;
236
237   snat_main_t *sm = &snat_main;
238   snat_main_per_thread_data_t *tsm = &sm->per_thread_data[thread_index];
239
240   // get head
241   elt =
242     pool_elt_at_index (tsm->list_pool, u->sessions_per_user_list_head_index);
243   // get first element
244   elt = pool_elt_at_index (tsm->list_pool, elt->next);
245
246   while (elt->value != ~0)
247     {
248       s = pool_elt_at_index (tsm->sessions, elt->value);
249       elt = pool_elt_at_index (tsm->list_pool, elt->next);
250
251       nat44_ei_free_session_data (sm, s, thread_index, 0);
252       nat44_delete_session (sm, s, thread_index);
253     }
254 }
255
256 int
257 nat44_ei_user_del (ip4_address_t *addr, u32 fib_index)
258 {
259   int rv = 1;
260
261   snat_main_t *sm = &snat_main;
262   snat_main_per_thread_data_t *tsm;
263
264   snat_user_key_t user_key;
265   clib_bihash_kv_8_8_t kv, value;
266
267   if (sm->endpoint_dependent)
268     return rv;
269
270   user_key.addr.as_u32 = addr->as_u32;
271   user_key.fib_index = fib_index;
272   kv.key = user_key.as_u64;
273
274   if (sm->num_workers > 1)
275     {
276       vec_foreach (tsm, sm->per_thread_data)
277         {
278           if (!clib_bihash_search_8_8 (&tsm->user_hash, &kv, &value))
279             {
280               nat44_ei_user_del_sessions (
281                 pool_elt_at_index (tsm->users, value.value),
282                 tsm->thread_index);
283               rv = 0;
284               break;
285             }
286         }
287     }
288   else
289     {
290       tsm = vec_elt_at_index (sm->per_thread_data, sm->num_workers);
291       if (!clib_bihash_search_8_8 (&tsm->user_hash, &kv, &value))
292         {
293           nat44_ei_user_del_sessions (
294             pool_elt_at_index (tsm->users, value.value), tsm->thread_index);
295           rv = 0;
296         }
297     }
298   return rv;
299 }
300
301 void
302 nat44_ei_static_mapping_del_sessions (snat_main_t *sm,
303                                       snat_main_per_thread_data_t *tsm,
304                                       snat_user_key_t u_key, int addr_only,
305                                       ip4_address_t e_addr, u16 e_port)
306 {
307   clib_bihash_kv_8_8_t kv, value;
308   kv.key = u_key.as_u64;
309   u64 user_index;
310   dlist_elt_t *head, *elt;
311   snat_user_t *u;
312   snat_session_t *s;
313   u32 elt_index, head_index, ses_index;
314
315   if (!clib_bihash_search_8_8 (&tsm->user_hash, &kv, &value))
316     {
317       user_index = value.value;
318       u = pool_elt_at_index (tsm->users, user_index);
319       if (u->nstaticsessions)
320         {
321           head_index = u->sessions_per_user_list_head_index;
322           head = pool_elt_at_index (tsm->list_pool, head_index);
323           elt_index = head->next;
324           elt = pool_elt_at_index (tsm->list_pool, elt_index);
325           ses_index = elt->value;
326           while (ses_index != ~0)
327             {
328               s = pool_elt_at_index (tsm->sessions, ses_index);
329               elt = pool_elt_at_index (tsm->list_pool, elt->next);
330               ses_index = elt->value;
331
332               if (!addr_only)
333                 {
334                   if ((s->out2in.addr.as_u32 != e_addr.as_u32) ||
335                       (s->out2in.port != e_port))
336                     continue;
337                 }
338
339               if (is_lb_session (s))
340                 continue;
341
342               if (!snat_is_session_static (s))
343                 continue;
344
345               nat_free_session_data (sm, s, tsm - sm->per_thread_data, 0);
346               nat44_delete_session (sm, s, tsm - sm->per_thread_data);
347
348               if (!addr_only)
349                 break;
350             }
351         }
352     }
353 }
354
355 u32
356 nat44_ei_get_in2out_worker_index (ip4_header_t *ip0, u32 rx_fib_index0,
357                                   u8 is_output)
358 {
359   snat_main_t *sm = &snat_main;
360   u32 next_worker_index = 0;
361   u32 hash;
362
363   next_worker_index = sm->first_worker_index;
364   hash = ip0->src_address.as_u32 + (ip0->src_address.as_u32 >> 8) +
365          (ip0->src_address.as_u32 >> 16) + (ip0->src_address.as_u32 >> 24);
366
367   if (PREDICT_TRUE (is_pow2 (_vec_len (sm->workers))))
368     next_worker_index += sm->workers[hash & (_vec_len (sm->workers) - 1)];
369   else
370     next_worker_index += sm->workers[hash % _vec_len (sm->workers)];
371
372   return next_worker_index;
373 }
374
375 u32
376 nat44_ei_get_out2in_worker_index (vlib_buffer_t *b, ip4_header_t *ip0,
377                                   u32 rx_fib_index0, u8 is_output)
378 {
379   snat_main_t *sm = &snat_main;
380   udp_header_t *udp;
381   u16 port;
382   clib_bihash_kv_8_8_t kv, value;
383   snat_static_mapping_t *m;
384   u32 proto;
385   u32 next_worker_index = 0;
386
387   /* first try static mappings without port */
388   if (PREDICT_FALSE (pool_elts (sm->static_mappings)))
389     {
390       init_nat_k (&kv, ip0->dst_address, 0, rx_fib_index0, 0);
391       if (!clib_bihash_search_8_8 (&sm->static_mapping_by_external, &kv,
392                                    &value))
393         {
394           m = pool_elt_at_index (sm->static_mappings, value.value);
395           return m->workers[0];
396         }
397     }
398
399   proto = ip_proto_to_nat_proto (ip0->protocol);
400   udp = ip4_next_header (ip0);
401   port = udp->dst_port;
402
403   /* unknown protocol */
404   if (PREDICT_FALSE (proto == NAT_PROTOCOL_OTHER))
405     {
406       /* use current thread */
407       return vlib_get_thread_index ();
408     }
409
410   if (PREDICT_FALSE (ip0->protocol == IP_PROTOCOL_ICMP))
411     {
412       icmp46_header_t *icmp = (icmp46_header_t *) udp;
413       icmp_echo_header_t *echo = (icmp_echo_header_t *) (icmp + 1);
414       if (!icmp_type_is_error_message (
415             vnet_buffer (b)->ip.reass.icmp_type_or_tcp_flags))
416         port = vnet_buffer (b)->ip.reass.l4_src_port;
417       else
418         {
419           /* if error message, then it's not fragmented and we can access it */
420           ip4_header_t *inner_ip = (ip4_header_t *) (echo + 1);
421           proto = ip_proto_to_nat_proto (inner_ip->protocol);
422           void *l4_header = ip4_next_header (inner_ip);
423           switch (proto)
424             {
425             case NAT_PROTOCOL_ICMP:
426               icmp = (icmp46_header_t *) l4_header;
427               echo = (icmp_echo_header_t *) (icmp + 1);
428               port = echo->identifier;
429               break;
430             case NAT_PROTOCOL_UDP:
431             case NAT_PROTOCOL_TCP:
432               port = ((tcp_udp_header_t *) l4_header)->src_port;
433               break;
434             default:
435               return vlib_get_thread_index ();
436             }
437         }
438     }
439
440   /* try static mappings with port */
441   if (PREDICT_FALSE (pool_elts (sm->static_mappings)))
442     {
443       init_nat_k (&kv, ip0->dst_address, port, rx_fib_index0, proto);
444       if (!clib_bihash_search_8_8 (&sm->static_mapping_by_external, &kv,
445                                    &value))
446         {
447           m = pool_elt_at_index (sm->static_mappings, value.value);
448           return m->workers[0];
449         }
450     }
451
452   /* worker by outside port */
453   next_worker_index = sm->first_worker_index;
454   next_worker_index +=
455     sm->workers[(clib_net_to_host_u16 (port) - 1024) / sm->port_per_thread];
456   return next_worker_index;
457 }
458
459 static int
460 nat44_ei_alloc_default_cb (snat_address_t *addresses, u32 fib_index,
461                            u32 thread_index, nat_protocol_t proto,
462                            ip4_address_t *addr, u16 *port, u16 port_per_thread,
463                            u32 snat_thread_index)
464 {
465   int i;
466   snat_address_t *a, *ga = 0;
467   u32 portnum;
468
469   for (i = 0; i < vec_len (addresses); i++)
470     {
471       a = addresses + i;
472       switch (proto)
473         {
474 #define _(N, j, n, s)                                                         \
475   case NAT_PROTOCOL_##N:                                                      \
476     if (a->busy_##n##_ports_per_thread[thread_index] < port_per_thread)       \
477       {                                                                       \
478         if (a->fib_index == fib_index)                                        \
479           {                                                                   \
480             while (1)                                                         \
481               {                                                               \
482                 portnum = (port_per_thread * snat_thread_index) +             \
483                           snat_random_port (0, port_per_thread - 1) + 1024;   \
484                 if (a->busy_##n##_port_refcounts[portnum])                    \
485                   continue;                                                   \
486                 --a->busy_##n##_port_refcounts[portnum];                      \
487                 a->busy_##n##_ports_per_thread[thread_index]++;               \
488                 a->busy_##n##_ports++;                                        \
489                 *addr = a->addr;                                              \
490                 *port = clib_host_to_net_u16 (portnum);                       \
491                 return 0;                                                     \
492               }                                                               \
493           }                                                                   \
494         else if (a->fib_index == ~0)                                          \
495           {                                                                   \
496             ga = a;                                                           \
497           }                                                                   \
498       }                                                                       \
499     break;
500           foreach_nat_protocol
501 #undef _
502             default : nat_elog_info ("unknown protocol");
503           return 1;
504         }
505     }
506
507   if (ga)
508     {
509       a = ga;
510       switch (proto)
511         {
512 #define _(N, j, n, s)                                                         \
513   case NAT_PROTOCOL_##N:                                                      \
514     while (1)                                                                 \
515       {                                                                       \
516         portnum = (port_per_thread * snat_thread_index) +                     \
517                   snat_random_port (0, port_per_thread - 1) + 1024;           \
518         if (a->busy_##n##_port_refcounts[portnum])                            \
519           continue;                                                           \
520         ++a->busy_##n##_port_refcounts[portnum];                              \
521         a->busy_##n##_ports_per_thread[thread_index]++;                       \
522         a->busy_##n##_ports++;                                                \
523         *addr = a->addr;                                                      \
524         *port = clib_host_to_net_u16 (portnum);                               \
525         return 0;                                                             \
526       }
527           break;
528           foreach_nat_protocol
529 #undef _
530             default : nat_elog_info ("unknown protocol");
531           return 1;
532         }
533     }
534
535   /* Totally out of translations to use... */
536   nat_ipfix_logging_addresses_exhausted (thread_index, 0);
537   return 1;
538 }
539
540 static int
541 nat44_ei_alloc_range_cb (snat_address_t *addresses, u32 fib_index,
542                          u32 thread_index, nat_protocol_t proto,
543                          ip4_address_t *addr, u16 *port, u16 port_per_thread,
544                          u32 snat_thread_index)
545 {
546   snat_main_t *sm = &snat_main;
547   snat_address_t *a = addresses;
548   u16 portnum, ports;
549
550   ports = sm->end_port - sm->start_port + 1;
551
552   if (!vec_len (addresses))
553     goto exhausted;
554
555   switch (proto)
556     {
557 #define _(N, i, n, s)                                                         \
558   case NAT_PROTOCOL_##N:                                                      \
559     if (a->busy_##n##_ports < ports)                                          \
560       {                                                                       \
561         while (1)                                                             \
562           {                                                                   \
563             portnum = snat_random_port (sm->start_port, sm->end_port);        \
564             if (a->busy_##n##_port_refcounts[portnum])                        \
565               continue;                                                       \
566             ++a->busy_##n##_port_refcounts[portnum];                          \
567             a->busy_##n##_ports++;                                            \
568             *addr = a->addr;                                                  \
569             *port = clib_host_to_net_u16 (portnum);                           \
570             return 0;                                                         \
571           }                                                                   \
572       }                                                                       \
573     break;
574       foreach_nat_protocol
575 #undef _
576         default : nat_elog_info ("unknown protocol");
577       return 1;
578     }
579
580 exhausted:
581   /* Totally out of translations to use... */
582   nat_ipfix_logging_addresses_exhausted (thread_index, 0);
583   return 1;
584 }
585
586 static int
587 nat44_ei_alloc_mape_cb (snat_address_t *addresses, u32 fib_index,
588                         u32 thread_index, nat_protocol_t proto,
589                         ip4_address_t *addr, u16 *port, u16 port_per_thread,
590                         u32 snat_thread_index)
591 {
592   snat_main_t *sm = &snat_main;
593   snat_address_t *a = addresses;
594   u16 m, ports, portnum, A, j;
595   m = 16 - (sm->psid_offset + sm->psid_length);
596   ports = (1 << (16 - sm->psid_length)) - (1 << m);
597
598   if (!vec_len (addresses))
599     goto exhausted;
600
601   switch (proto)
602     {
603 #define _(N, i, n, s)                                                         \
604   case NAT_PROTOCOL_##N:                                                      \
605     if (a->busy_##n##_ports < ports)                                          \
606       {                                                                       \
607         while (1)                                                             \
608           {                                                                   \
609             A = snat_random_port (1, pow2_mask (sm->psid_offset));            \
610             j = snat_random_port (0, pow2_mask (m));                          \
611             portnum = A | (sm->psid << sm->psid_offset) | (j << (16 - m));    \
612             if (a->busy_##n##_port_refcounts[portnum])                        \
613               continue;                                                       \
614             ++a->busy_##n##_port_refcounts[portnum];                          \
615             a->busy_##n##_ports++;                                            \
616             *addr = a->addr;                                                  \
617             *port = clib_host_to_net_u16 (portnum);                           \
618             return 0;                                                         \
619           }                                                                   \
620       }                                                                       \
621     break;
622       foreach_nat_protocol
623 #undef _
624         default : nat_elog_info ("unknown protocol");
625       return 1;
626     }
627
628 exhausted:
629   /* Totally out of translations to use... */
630   nat_ipfix_logging_addresses_exhausted (thread_index, 0);
631   return 1;
632 }
633
634 void
635 nat44_ei_set_alloc_default ()
636 {
637   snat_main_t *sm = &snat_main;
638
639   sm->addr_and_port_alloc_alg = NAT_ADDR_AND_PORT_ALLOC_ALG_DEFAULT;
640   sm->alloc_addr_and_port = nat44_ei_alloc_default_cb;
641 }
642
643 void
644 nat44_ei_set_alloc_range (u16 start_port, u16 end_port)
645 {
646   snat_main_t *sm = &snat_main;
647
648   sm->addr_and_port_alloc_alg = NAT_ADDR_AND_PORT_ALLOC_ALG_RANGE;
649   sm->alloc_addr_and_port = nat44_ei_alloc_range_cb;
650   sm->start_port = start_port;
651   sm->end_port = end_port;
652 }
653
654 void
655 nat44_ei_set_alloc_mape (u16 psid, u16 psid_offset, u16 psid_length)
656 {
657   snat_main_t *sm = &snat_main;
658
659   sm->addr_and_port_alloc_alg = NAT_ADDR_AND_PORT_ALLOC_ALG_MAPE;
660   sm->alloc_addr_and_port = nat44_ei_alloc_mape_cb;
661   sm->psid = psid;
662   sm->psid_offset = psid_offset;
663   sm->psid_length = psid_length;
664 }
665
666 static void
667 nat44_ei_add_static_mapping_when_resolved (ip4_address_t l_addr, u16 l_port,
668                                            u16 e_port, nat_protocol_t proto,
669                                            u32 sw_if_index, u32 vrf_id,
670                                            int addr_only, int identity_nat,
671                                            u8 *tag)
672 {
673   snat_main_t *sm = &snat_main;
674   snat_static_map_resolve_t *rp;
675
676   vec_add2 (sm->to_resolve, rp, 1);
677   clib_memset (rp, 0, sizeof (*rp));
678
679   rp->l_addr.as_u32 = l_addr.as_u32;
680   rp->l_port = l_port;
681   rp->e_port = e_port;
682   rp->sw_if_index = sw_if_index;
683   rp->vrf_id = vrf_id;
684   rp->proto = proto;
685   rp->addr_only = addr_only;
686   rp->identity_nat = identity_nat;
687   rp->tag = vec_dup (tag);
688 }
689
690 int
691 nat44_ei_del_session (snat_main_t *sm, ip4_address_t *addr, u16 port,
692                       nat_protocol_t proto, u32 vrf_id, int is_in)
693 {
694   snat_main_per_thread_data_t *tsm;
695   clib_bihash_kv_8_8_t kv, value;
696   ip4_header_t ip;
697   u32 fib_index = fib_table_find (FIB_PROTOCOL_IP4, vrf_id);
698   snat_session_t *s;
699   clib_bihash_8_8_t *t;
700
701   if (sm->endpoint_dependent)
702     return VNET_API_ERROR_UNSUPPORTED;
703
704   ip.dst_address.as_u32 = ip.src_address.as_u32 = addr->as_u32;
705   if (sm->num_workers > 1)
706     tsm = vec_elt_at_index (sm->per_thread_data,
707                             sm->worker_in2out_cb (&ip, fib_index, 0));
708   else
709     tsm = vec_elt_at_index (sm->per_thread_data, sm->num_workers);
710
711   init_nat_k (&kv, *addr, port, fib_index, proto);
712   t = is_in ? &tsm->in2out : &tsm->out2in;
713   if (!clib_bihash_search_8_8 (t, &kv, &value))
714     {
715       if (pool_is_free_index (tsm->sessions, value.value))
716         return VNET_API_ERROR_UNSPECIFIED;
717
718       s = pool_elt_at_index (tsm->sessions, value.value);
719       nat_free_session_data (sm, s, tsm - sm->per_thread_data, 0);
720       nat44_delete_session (sm, s, tsm - sm->per_thread_data);
721       return 0;
722     }
723
724   return VNET_API_ERROR_NO_SUCH_ENTRY;
725 }
726
727 int
728 nat44_ei_add_del_static_mapping (ip4_address_t l_addr, ip4_address_t e_addr,
729                                  u16 l_port, u16 e_port, nat_protocol_t proto,
730                                  u32 sw_if_index, u32 vrf_id, u8 addr_only,
731                                  u8 identity_nat, u8 *tag, u8 is_add)
732 {
733   snat_main_t *sm = &snat_main;
734   snat_static_mapping_t *m = 0;
735   clib_bihash_kv_8_8_t kv, value;
736   snat_address_t *a = 0;
737   u32 fib_index = ~0;
738   snat_interface_t *interface;
739   snat_main_per_thread_data_t *tsm;
740   snat_user_key_t u_key;
741   snat_user_t *u;
742   dlist_elt_t *head, *elt;
743   u32 elt_index, head_index;
744   u32 ses_index;
745   u64 user_index;
746   snat_session_t *s;
747   snat_static_map_resolve_t *rp, *rp_match = 0;
748   nat44_lb_addr_port_t *local;
749   u32 find = ~0;
750   int i;
751
752   if (sw_if_index != ~0)
753     {
754       ip4_address_t *first_int_addr;
755
756       for (i = 0; i < vec_len (sm->to_resolve); i++)
757         {
758           rp = sm->to_resolve + i;
759           if (rp->sw_if_index != sw_if_index ||
760               rp->l_addr.as_u32 != l_addr.as_u32 || rp->vrf_id != vrf_id ||
761               rp->addr_only != addr_only)
762             continue;
763
764           if (!addr_only)
765             {
766               if ((rp->l_port != l_port && rp->e_port != e_port) ||
767                   rp->proto != proto)
768                 continue;
769             }
770
771           rp_match = rp;
772           break;
773         }
774
775       /* Might be already set... */
776       first_int_addr = ip4_interface_first_address (
777         sm->ip4_main, sw_if_index, 0 /* just want the address */);
778
779       if (is_add)
780         {
781           if (rp_match)
782             return VNET_API_ERROR_VALUE_EXIST;
783
784           nat44_ei_add_static_mapping_when_resolved (
785             l_addr, l_port, e_port, proto, sw_if_index, vrf_id, addr_only,
786             identity_nat, tag);
787
788           /* DHCP resolution required? */
789           if (!first_int_addr)
790             return 0;
791
792           e_addr.as_u32 = first_int_addr->as_u32;
793           /* Identity mapping? */
794           if (l_addr.as_u32 == 0)
795             l_addr.as_u32 = e_addr.as_u32;
796         }
797       else
798         {
799           if (!rp_match)
800             return VNET_API_ERROR_NO_SUCH_ENTRY;
801
802           vec_del1 (sm->to_resolve, i);
803
804           if (!first_int_addr)
805             return 0;
806
807           e_addr.as_u32 = first_int_addr->as_u32;
808           /* Identity mapping? */
809           if (l_addr.as_u32 == 0)
810             l_addr.as_u32 = e_addr.as_u32;
811         }
812     }
813
814   init_nat_k (&kv, e_addr, addr_only ? 0 : e_port, 0, addr_only ? 0 : proto);
815   if (!clib_bihash_search_8_8 (&sm->static_mapping_by_external, &kv, &value))
816     m = pool_elt_at_index (sm->static_mappings, value.value);
817
818   if (is_add)
819     {
820       if (m)
821         {
822           // identity mapping for second vrf
823           if (is_identity_static_mapping (m))
824             {
825               pool_foreach (local, m->locals)
826                 {
827                   if (local->vrf_id == vrf_id)
828                     return VNET_API_ERROR_VALUE_EXIST;
829                 }
830               pool_get (m->locals, local);
831               local->vrf_id = vrf_id;
832               local->fib_index = fib_table_find_or_create_and_lock (
833                 FIB_PROTOCOL_IP4, vrf_id, sm->fib_src_low);
834               init_nat_kv (&kv, m->local_addr, m->local_port, local->fib_index,
835                            m->proto, m - sm->static_mappings);
836               clib_bihash_add_del_8_8 (&sm->static_mapping_by_local, &kv, 1);
837               return 0;
838             }
839           return VNET_API_ERROR_VALUE_EXIST;
840         }
841
842       /* Convert VRF id to FIB index */
843       if (vrf_id != ~0)
844         {
845           fib_index = fib_table_find_or_create_and_lock (
846             FIB_PROTOCOL_IP4, vrf_id, sm->fib_src_low);
847         }
848       /* If not specified use inside VRF id from NAT44 plugin config */
849       else
850         {
851           fib_index = sm->inside_fib_index;
852           vrf_id = sm->inside_vrf_id;
853           fib_table_lock (fib_index, FIB_PROTOCOL_IP4, sm->fib_src_low);
854         }
855
856       if (!identity_nat)
857         {
858           init_nat_k (&kv, l_addr, addr_only ? 0 : l_port, fib_index,
859                       addr_only ? 0 : proto);
860           if (!clib_bihash_search_8_8 (&sm->static_mapping_by_local, &kv,
861                                        &value))
862             return VNET_API_ERROR_VALUE_EXIST;
863         }
864
865       /* Find external address in allocated addresses and reserve port for
866          address and port pair mapping when dynamic translations enabled */
867       if (!(addr_only || sm->static_mapping_only))
868         {
869           for (i = 0; i < vec_len (sm->addresses); i++)
870             {
871               if (sm->addresses[i].addr.as_u32 == e_addr.as_u32)
872                 {
873                   a = sm->addresses + i;
874                   /* External port must be unused */
875                   switch (proto)
876                     {
877 #define _(N, j, n, s)                                                         \
878   case NAT_PROTOCOL_##N:                                                      \
879     if (a->busy_##n##_port_refcounts[e_port])                                 \
880       return VNET_API_ERROR_INVALID_VALUE;                                    \
881     ++a->busy_##n##_port_refcounts[e_port];                                   \
882     if (e_port > 1024)                                                        \
883       {                                                                       \
884         a->busy_##n##_ports++;                                                \
885         a->busy_##n##_ports_per_thread[get_thread_idx_by_port (e_port)]++;    \
886       }                                                                       \
887     break;
888                       foreach_nat_protocol
889 #undef _
890                         default : nat_elog_info ("unknown protocol");
891                       return VNET_API_ERROR_INVALID_VALUE_2;
892                     }
893                   break;
894                 }
895             }
896           /* External address must be allocated */
897           if (!a && (l_addr.as_u32 != e_addr.as_u32))
898             {
899               if (sw_if_index != ~0)
900                 {
901                   for (i = 0; i < vec_len (sm->to_resolve); i++)
902                     {
903                       rp = sm->to_resolve + i;
904                       if (rp->addr_only)
905                         continue;
906                       if (rp->sw_if_index != sw_if_index &&
907                           rp->l_addr.as_u32 != l_addr.as_u32 &&
908                           rp->vrf_id != vrf_id && rp->l_port != l_port &&
909                           rp->e_port != e_port && rp->proto != proto)
910                         continue;
911
912                       vec_del1 (sm->to_resolve, i);
913                       break;
914                     }
915                 }
916               return VNET_API_ERROR_NO_SUCH_ENTRY;
917             }
918         }
919
920       pool_get (sm->static_mappings, m);
921       clib_memset (m, 0, sizeof (*m));
922       m->tag = vec_dup (tag);
923       m->local_addr = l_addr;
924       m->external_addr = e_addr;
925
926       if (addr_only)
927         m->flags |= NAT_STATIC_MAPPING_FLAG_ADDR_ONLY;
928       else
929         {
930           m->local_port = l_port;
931           m->external_port = e_port;
932           m->proto = proto;
933         }
934
935       if (identity_nat)
936         {
937           m->flags |= NAT_STATIC_MAPPING_FLAG_IDENTITY_NAT;
938           pool_get (m->locals, local);
939           local->vrf_id = vrf_id;
940           local->fib_index = fib_index;
941         }
942       else
943         {
944           m->vrf_id = vrf_id;
945           m->fib_index = fib_index;
946         }
947
948       if (sm->num_workers > 1)
949         {
950           ip4_header_t ip = {
951             .src_address = m->local_addr,
952           };
953           vec_add1 (m->workers, sm->worker_in2out_cb (&ip, m->fib_index, 0));
954           tsm = vec_elt_at_index (sm->per_thread_data, m->workers[0]);
955         }
956       else
957         tsm = vec_elt_at_index (sm->per_thread_data, sm->num_workers);
958
959       init_nat_kv (&kv, m->local_addr, m->local_port, fib_index, m->proto,
960                    m - sm->static_mappings);
961       clib_bihash_add_del_8_8 (&sm->static_mapping_by_local, &kv, 1);
962
963       init_nat_kv (&kv, m->external_addr, m->external_port, 0, m->proto,
964                    m - sm->static_mappings);
965       clib_bihash_add_del_8_8 (&sm->static_mapping_by_external, &kv, 1);
966
967       /* Delete dynamic sessions matching local address (+ local port) */
968       // TODO: based on type of NAT EI/ED
969       if (!(sm->static_mapping_only))
970         {
971           u_key.addr = m->local_addr;
972           u_key.fib_index = m->fib_index;
973           kv.key = u_key.as_u64;
974           if (!clib_bihash_search_8_8 (&tsm->user_hash, &kv, &value))
975             {
976               user_index = value.value;
977               u = pool_elt_at_index (tsm->users, user_index);
978               if (u->nsessions)
979                 {
980                   head_index = u->sessions_per_user_list_head_index;
981                   head = pool_elt_at_index (tsm->list_pool, head_index);
982                   elt_index = head->next;
983                   elt = pool_elt_at_index (tsm->list_pool, elt_index);
984                   ses_index = elt->value;
985                   while (ses_index != ~0)
986                     {
987                       s = pool_elt_at_index (tsm->sessions, ses_index);
988                       elt = pool_elt_at_index (tsm->list_pool, elt->next);
989                       ses_index = elt->value;
990
991                       if (snat_is_session_static (s))
992                         continue;
993
994                       if (!addr_only && s->in2out.port != m->local_port)
995                         continue;
996
997                       nat_free_session_data (sm, s, tsm - sm->per_thread_data,
998                                              0);
999                       nat44_delete_session (sm, s, tsm - sm->per_thread_data);
1000
1001                       if (!addr_only)
1002                         break;
1003                     }
1004                 }
1005             }
1006         }
1007     }
1008   else
1009     {
1010       if (!m)
1011         {
1012           if (sw_if_index != ~0)
1013             return 0;
1014           else
1015             return VNET_API_ERROR_NO_SUCH_ENTRY;
1016         }
1017
1018       if (identity_nat)
1019         {
1020           if (vrf_id == ~0)
1021             vrf_id = sm->inside_vrf_id;
1022
1023           pool_foreach (local, m->locals)
1024             {
1025               if (local->vrf_id == vrf_id)
1026                 find = local - m->locals;
1027             }
1028           if (find == ~0)
1029             return VNET_API_ERROR_NO_SUCH_ENTRY;
1030
1031           local = pool_elt_at_index (m->locals, find);
1032           fib_index = local->fib_index;
1033           pool_put (m->locals, local);
1034         }
1035       else
1036         fib_index = m->fib_index;
1037
1038       /* Free external address port */
1039       if (!(addr_only || sm->static_mapping_only))
1040         {
1041           for (i = 0; i < vec_len (sm->addresses); i++)
1042             {
1043               if (sm->addresses[i].addr.as_u32 == e_addr.as_u32)
1044                 {
1045                   a = sm->addresses + i;
1046                   switch (proto)
1047                     {
1048 #define _(N, j, n, s)                                                         \
1049   case NAT_PROTOCOL_##N:                                                      \
1050     --a->busy_##n##_port_refcounts[e_port];                                   \
1051     if (e_port > 1024)                                                        \
1052       {                                                                       \
1053         a->busy_##n##_ports--;                                                \
1054         a->busy_##n##_ports_per_thread[get_thread_idx_by_port (e_port)]--;    \
1055       }                                                                       \
1056     break;
1057                       foreach_nat_protocol
1058 #undef _
1059                         default : return VNET_API_ERROR_INVALID_VALUE_2;
1060                     }
1061                   break;
1062                 }
1063             }
1064         }
1065
1066       if (sm->num_workers > 1)
1067         tsm = vec_elt_at_index (sm->per_thread_data, m->workers[0]);
1068       else
1069         tsm = vec_elt_at_index (sm->per_thread_data, sm->num_workers);
1070
1071       init_nat_k (&kv, m->local_addr, m->local_port, fib_index, m->proto);
1072       clib_bihash_add_del_8_8 (&sm->static_mapping_by_local, &kv, 0);
1073
1074       /* Delete session(s) for static mapping if exist */
1075       if (!(sm->static_mapping_only) ||
1076           (sm->static_mapping_only && sm->static_mapping_connection_tracking))
1077         {
1078           u_key.addr = m->local_addr;
1079           u_key.fib_index = fib_index;
1080           kv.key = u_key.as_u64;
1081           nat44_ei_static_mapping_del_sessions (sm, tsm, u_key, addr_only,
1082                                                 e_addr, e_port);
1083         }
1084
1085       fib_table_unlock (fib_index, FIB_PROTOCOL_IP4, sm->fib_src_low);
1086       if (pool_elts (m->locals))
1087         return 0;
1088
1089       init_nat_k (&kv, m->external_addr, m->external_port, 0, m->proto);
1090       clib_bihash_add_del_8_8 (&sm->static_mapping_by_external, &kv, 0);
1091
1092       vec_free (m->tag);
1093       vec_free (m->workers);
1094       /* Delete static mapping from pool */
1095       pool_put (sm->static_mappings, m);
1096     }
1097
1098   if (!addr_only || (l_addr.as_u32 == e_addr.as_u32))
1099     return 0;
1100
1101   /* Add/delete external address to FIB */
1102   pool_foreach (interface, sm->interfaces)
1103     {
1104       if (nat_interface_is_inside (interface) || sm->out2in_dpo)
1105         continue;
1106
1107       snat_add_del_addr_to_fib (&e_addr, 32, interface->sw_if_index, is_add);
1108       break;
1109     }
1110   pool_foreach (interface, sm->output_feature_interfaces)
1111     {
1112       if (nat_interface_is_inside (interface) || sm->out2in_dpo)
1113         continue;
1114
1115       snat_add_del_addr_to_fib (&e_addr, 32, interface->sw_if_index, is_add);
1116       break;
1117     }
1118   return 0;
1119 }
1120
1121 int
1122 nat44_ei_static_mapping_match (ip4_address_t match_addr, u16 match_port,
1123                                u32 match_fib_index,
1124                                nat_protocol_t match_protocol,
1125                                ip4_address_t *mapping_addr, u16 *mapping_port,
1126                                u32 *mapping_fib_index, u8 by_external,
1127                                u8 *is_addr_only, u8 *is_identity_nat)
1128 {
1129   snat_main_t *sm = &snat_main;
1130   clib_bihash_kv_8_8_t kv, value;
1131   snat_static_mapping_t *m;
1132   u16 port;
1133
1134   if (by_external)
1135     {
1136       init_nat_k (&kv, match_addr, match_port, 0, match_protocol);
1137       if (clib_bihash_search_8_8 (&sm->static_mapping_by_external, &kv,
1138                                   &value))
1139         {
1140           /* Try address only mapping */
1141           init_nat_k (&kv, match_addr, 0, 0, 0);
1142           if (clib_bihash_search_8_8 (&sm->static_mapping_by_external, &kv,
1143                                       &value))
1144             return 1;
1145         }
1146       m = pool_elt_at_index (sm->static_mappings, value.value);
1147
1148       *mapping_fib_index = m->fib_index;
1149       *mapping_addr = m->local_addr;
1150       port = m->local_port;
1151     }
1152   else
1153     {
1154       init_nat_k (&kv, match_addr, match_port, match_fib_index,
1155                   match_protocol);
1156       if (clib_bihash_search_8_8 (&sm->static_mapping_by_local, &kv, &value))
1157         {
1158           /* Try address only mapping */
1159           init_nat_k (&kv, match_addr, 0, match_fib_index, 0);
1160           if (clib_bihash_search_8_8 (&sm->static_mapping_by_local, &kv,
1161                                       &value))
1162             return 1;
1163         }
1164       m = pool_elt_at_index (sm->static_mappings, value.value);
1165
1166       *mapping_fib_index = sm->outside_fib_index;
1167       *mapping_addr = m->external_addr;
1168       port = m->external_port;
1169     }
1170
1171   /* Address only mapping doesn't change port */
1172   if (is_addr_only_static_mapping (m))
1173     *mapping_port = match_port;
1174   else
1175     *mapping_port = port;
1176
1177   if (PREDICT_FALSE (is_addr_only != 0))
1178     *is_addr_only = is_addr_only_static_mapping (m);
1179
1180   if (PREDICT_FALSE (is_identity_nat != 0))
1181     *is_identity_nat = is_identity_static_mapping (m);
1182
1183   return 0;
1184 }
1185
1186 static void
1187 nat44_ei_worker_db_free (snat_main_per_thread_data_t *tsm)
1188 {
1189   pool_free (tsm->list_pool);
1190   pool_free (tsm->lru_pool);
1191   pool_free (tsm->sessions);
1192   pool_free (tsm->users);
1193
1194   clib_bihash_free_8_8 (&tsm->in2out);
1195   clib_bihash_free_8_8 (&tsm->out2in);
1196   clib_bihash_free_8_8 (&tsm->user_hash);
1197 }
1198
1199 static void
1200 nat44_ei_worker_db_init (snat_main_per_thread_data_t *tsm, u32 translations,
1201                          u32 translation_buckets, u32 user_buckets)
1202 {
1203   dlist_elt_t *head;
1204
1205   pool_alloc (tsm->list_pool, translations);
1206   pool_alloc (tsm->lru_pool, translations);
1207   pool_alloc (tsm->sessions, translations);
1208
1209   clib_bihash_init_8_8 (&tsm->in2out, "in2out", translation_buckets, 0);
1210   clib_bihash_init_8_8 (&tsm->out2in, "out2in", translation_buckets, 0);
1211   clib_bihash_init_8_8 (&tsm->user_hash, "users", user_buckets, 0);
1212
1213   clib_bihash_set_kvp_format_fn_8_8 (&tsm->in2out, format_session_kvp);
1214   clib_bihash_set_kvp_format_fn_8_8 (&tsm->out2in, format_session_kvp);
1215   clib_bihash_set_kvp_format_fn_8_8 (&tsm->user_hash, format_user_kvp);
1216
1217   pool_get (tsm->lru_pool, head);
1218   tsm->tcp_trans_lru_head_index = head - tsm->lru_pool;
1219   clib_dlist_init (tsm->lru_pool, tsm->tcp_trans_lru_head_index);
1220
1221   pool_get (tsm->lru_pool, head);
1222   tsm->tcp_estab_lru_head_index = head - tsm->lru_pool;
1223   clib_dlist_init (tsm->lru_pool, tsm->tcp_estab_lru_head_index);
1224
1225   pool_get (tsm->lru_pool, head);
1226   tsm->udp_lru_head_index = head - tsm->lru_pool;
1227   clib_dlist_init (tsm->lru_pool, tsm->udp_lru_head_index);
1228
1229   pool_get (tsm->lru_pool, head);
1230   tsm->icmp_lru_head_index = head - tsm->lru_pool;
1231   clib_dlist_init (tsm->lru_pool, tsm->icmp_lru_head_index);
1232
1233   pool_get (tsm->lru_pool, head);
1234   tsm->unk_proto_lru_head_index = head - tsm->lru_pool;
1235   clib_dlist_init (tsm->lru_pool, tsm->unk_proto_lru_head_index);
1236 }
1237
1238 static void
1239 nat44_ei_db_free ()
1240 {
1241   snat_main_t *sm = &snat_main;
1242   snat_main_per_thread_data_t *tsm;
1243
1244   pool_free (sm->static_mappings);
1245   clib_bihash_free_8_8 (&sm->static_mapping_by_local);
1246   clib_bihash_free_8_8 (&sm->static_mapping_by_external);
1247
1248   if (sm->pat)
1249     {
1250       vec_foreach (tsm, sm->per_thread_data)
1251         {
1252           nat44_ei_worker_db_free (tsm);
1253         }
1254     }
1255 }
1256
1257 static void
1258 nat44_ei_db_init (u32 translations, u32 translation_buckets, u32 user_buckets)
1259 {
1260   snat_main_t *sm = &snat_main;
1261   snat_main_per_thread_data_t *tsm;
1262
1263   u32 static_mapping_buckets = 1024;
1264   u32 static_mapping_memory_size = 64 << 20;
1265
1266   clib_bihash_init_8_8 (&sm->static_mapping_by_local,
1267                         "static_mapping_by_local", static_mapping_buckets,
1268                         static_mapping_memory_size);
1269   clib_bihash_init_8_8 (&sm->static_mapping_by_external,
1270                         "static_mapping_by_external", static_mapping_buckets,
1271                         static_mapping_memory_size);
1272   clib_bihash_set_kvp_format_fn_8_8 (&sm->static_mapping_by_local,
1273                                      format_static_mapping_kvp);
1274   clib_bihash_set_kvp_format_fn_8_8 (&sm->static_mapping_by_external,
1275                                      format_static_mapping_kvp);
1276
1277   if (sm->pat)
1278     {
1279       vec_foreach (tsm, sm->per_thread_data)
1280         {
1281           nat44_ei_worker_db_init (tsm, translations, translation_buckets,
1282                                    user_buckets);
1283         }
1284     }
1285 }
1286
1287 void
1288 nat44_ei_sessions_clear ()
1289 {
1290   nat44_ei_main_t *nm = &nat44_ei_main;
1291
1292   snat_main_per_thread_data_t *tsm;
1293   snat_main_t *sm = &snat_main;
1294
1295   if (sm->pat)
1296     {
1297       vec_foreach (tsm, sm->per_thread_data)
1298         {
1299           nat44_ei_worker_db_free (tsm);
1300           nat44_ei_worker_db_init (tsm, nm->translations,
1301                                    nm->translation_buckets, nm->user_buckets);
1302         }
1303     }
1304
1305   // TODO: function for reset counters
1306   vlib_zero_simple_counter (&sm->total_users, 0);
1307   vlib_zero_simple_counter (&sm->total_sessions, 0);
1308   vlib_zero_simple_counter (&sm->user_limit_reached, 0);
1309 }
1310
1311 /*
1312  * fd.io coding-style-patch-verification: ON
1313  *
1314  * Local Variables:
1315  * eval: (c-set-style "gnu")
1316  * End:
1317  */