nat: Final NAT44 EI/ED split patch
[vpp.git] / src / plugins / nat / nat44-ei / nat44_ei_cli.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 #include <vnet/fib/fib_table.h>
17
18 #include <nat/lib/log.h>
19 #include <nat/lib/nat_inlines.h>
20 #include <nat/lib/ipfix_logging.h>
21
22 #include <nat/nat44-ei/nat44_ei.h>
23 #include <nat/nat44-ei/nat44_ei_ha.h>
24
25 u8 *
26 format_nat44_ei_session (u8 *s, va_list *args)
27 {
28   nat44_ei_main_per_thread_data_t *tnm =
29     va_arg (*args, nat44_ei_main_per_thread_data_t *);
30   nat44_ei_session_t *sess = va_arg (*args, nat44_ei_session_t *);
31
32   if (nat44_ei_is_unk_proto_session (sess))
33     {
34       s =
35         format (s, "  i2o %U proto %u fib %u\n", format_ip4_address,
36                 &sess->in2out.addr, sess->in2out.port, sess->in2out.fib_index);
37       s =
38         format (s, "  o2i %U proto %u fib %u\n", format_ip4_address,
39                 &sess->out2in.addr, sess->out2in.port, sess->out2in.fib_index);
40     }
41   else
42     {
43       s = format (s, "  i2o %U proto %U port %d fib %d\n", format_ip4_address,
44                   &sess->in2out.addr, format_nat_protocol, sess->nat_proto,
45                   clib_net_to_host_u16 (sess->in2out.port),
46                   sess->in2out.fib_index);
47       s = format (s, "  o2i %U proto %U port %d fib %d\n", format_ip4_address,
48                   &sess->out2in.addr, format_nat_protocol, sess->nat_proto,
49                   clib_net_to_host_u16 (sess->out2in.port),
50                   sess->out2in.fib_index);
51     }
52
53   s = format (s, "       index %llu\n", sess - tnm->sessions);
54   s = format (s, "       last heard %.2f\n", sess->last_heard);
55   s = format (s, "       total pkts %d, total bytes %lld\n", sess->total_pkts,
56               sess->total_bytes);
57   if (nat44_ei_is_session_static (sess))
58     s = format (s, "       static translation\n");
59   else
60     s = format (s, "       dynamic translation\n");
61
62   return s;
63 }
64
65 u8 *
66 format_nat44_ei_user (u8 *s, va_list *args)
67 {
68   nat44_ei_main_per_thread_data_t *tnm =
69     va_arg (*args, nat44_ei_main_per_thread_data_t *);
70   nat44_ei_user_t *u = va_arg (*args, nat44_ei_user_t *);
71   int verbose = va_arg (*args, int);
72   dlist_elt_t *head, *elt;
73   u32 elt_index, head_index;
74   u32 session_index;
75   nat44_ei_session_t *sess;
76
77   s = format (s, "%U: %d dynamic translations, %d static translations\n",
78               format_ip4_address, &u->addr, u->nsessions, u->nstaticsessions);
79
80   if (verbose == 0)
81     return s;
82
83   if (u->nsessions || u->nstaticsessions)
84     {
85       head_index = u->sessions_per_user_list_head_index;
86       head = pool_elt_at_index (tnm->list_pool, head_index);
87
88       elt_index = head->next;
89       elt = pool_elt_at_index (tnm->list_pool, elt_index);
90       session_index = elt->value;
91
92       while (session_index != ~0)
93         {
94           sess = pool_elt_at_index (tnm->sessions, session_index);
95
96           s = format (s, "  %U\n", format_nat44_ei_session, tnm, sess);
97
98           elt_index = elt->next;
99           elt = pool_elt_at_index (tnm->list_pool, elt_index);
100           session_index = elt->value;
101         }
102     }
103
104   return s;
105 }
106
107 u8 *
108 format_nat44_ei_static_mapping (u8 *s, va_list *args)
109 {
110   nat44_ei_static_mapping_t *m = va_arg (*args, nat44_ei_static_mapping_t *);
111   nat44_ei_lb_addr_port_t *local;
112
113   if (nat44_ei_is_identity_static_mapping (m))
114     {
115       if (nat44_ei_is_addr_only_static_mapping (m))
116         s = format (s, "identity mapping %U", format_ip4_address,
117                     &m->local_addr);
118       else
119         s = format (s, "identity mapping %U %U:%d", format_nat_protocol,
120                     m->proto, format_ip4_address, &m->local_addr,
121                     clib_net_to_host_u16 (m->local_port));
122
123       pool_foreach (local, m->locals)
124         {
125           s = format (s, " vrf %d", local->vrf_id);
126         }
127
128       return s;
129     }
130
131   if (nat44_ei_is_addr_only_static_mapping (m))
132     {
133       s = format (s, "local %U external %U vrf %d", format_ip4_address,
134                   &m->local_addr, format_ip4_address, &m->external_addr,
135                   m->vrf_id);
136     }
137   else
138     {
139       s = format (s, "%U local %U:%d external %U:%d vrf %d",
140                   format_nat_protocol, m->proto, format_ip4_address,
141                   &m->local_addr, clib_net_to_host_u16 (m->local_port),
142                   format_ip4_address, &m->external_addr,
143                   clib_net_to_host_u16 (m->external_port), m->vrf_id);
144     }
145   return s;
146 }
147
148 u8 *
149 format_nat44_ei_static_map_to_resolve (u8 *s, va_list *args)
150 {
151   nat44_ei_static_map_resolve_t *m =
152     va_arg (*args, nat44_ei_static_map_resolve_t *);
153   vnet_main_t *vnm = vnet_get_main ();
154
155   if (m->addr_only)
156     s =
157       format (s, "local %U external %U vrf %d", format_ip4_address, &m->l_addr,
158               format_vnet_sw_if_index_name, vnm, m->sw_if_index, m->vrf_id);
159   else
160     s = format (s, "%U local %U:%d external %U:%d vrf %d", format_nat_protocol,
161                 m->proto, format_ip4_address, &m->l_addr,
162                 clib_net_to_host_u16 (m->l_port), format_vnet_sw_if_index_name,
163                 vnm, m->sw_if_index, clib_net_to_host_u16 (m->e_port),
164                 m->vrf_id);
165
166   return s;
167 }
168
169 static clib_error_t *
170 nat44_ei_enable_command_fn (vlib_main_t *vm, unformat_input_t *input,
171                             vlib_cli_command_t *cmd)
172 {
173   nat44_ei_main_t *nm = &nat44_ei_main;
174   unformat_input_t _line_input, *line_input = &_line_input;
175   clib_error_t *error = 0;
176
177   nat44_ei_config_t c = { 0 };
178   u8 mode_set = 0;
179
180   if (nm->enabled)
181     return clib_error_return (0, "nat44 ei already enabled");
182
183   /* Get a line of input. */
184   if (!unformat_user (input, unformat_line_input, line_input))
185     {
186       if (nat44_ei_plugin_enable (c) != 0)
187         return clib_error_return (0, "nat44 ei enable failed");
188       return 0;
189     }
190
191   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
192     {
193       if (!mode_set && unformat (line_input, "static-mapping-only"))
194         {
195           mode_set = 1;
196           c.static_mapping_only = 1;
197           if (unformat (line_input, "connection-tracking"))
198             {
199               c.connection_tracking = 1;
200             }
201         }
202       else if (!mode_set && unformat (line_input, "out2in-dpo"))
203         {
204           mode_set = 1;
205           c.out2in_dpo = 1;
206         }
207       else if (unformat (line_input, "inside-vrf %u", &c.inside_vrf))
208         ;
209       else if (unformat (line_input, "outside-vrf %u", &c.outside_vrf))
210         ;
211       else if (unformat (line_input, "users %u", &c.users))
212         ;
213       else if (unformat (line_input, "sessions %u", &c.sessions))
214         ;
215       else if (unformat (line_input, "user-sessions %u", &c.user_sessions))
216         ;
217       else
218         {
219           error = clib_error_return (0, "unknown input '%U'",
220                                      format_unformat_error, line_input);
221           goto done;
222         }
223     }
224
225   if (!c.sessions)
226     {
227       error = clib_error_return (0, "number of sessions is required");
228       goto done;
229     }
230
231   if (nat44_ei_plugin_enable (c) != 0)
232     error = clib_error_return (0, "nat44 ei enable failed");
233 done:
234   unformat_free (line_input);
235   return error;
236 }
237
238 static clib_error_t *
239 nat44_ei_disable_command_fn (vlib_main_t *vm, unformat_input_t *input,
240                              vlib_cli_command_t *cmd)
241 {
242   nat44_ei_main_t *nm = &nat44_ei_main;
243   clib_error_t *error = 0;
244
245   if (!nm->enabled)
246     return clib_error_return (0, "nat44 ei already disabled");
247
248   if (nat44_ei_plugin_disable () != 0)
249     error = clib_error_return (0, "nat44 ei disable failed");
250
251   return error;
252 }
253
254 static clib_error_t *
255 set_workers_command_fn (vlib_main_t *vm, unformat_input_t *input,
256                         vlib_cli_command_t *cmd)
257 {
258   unformat_input_t _line_input, *line_input = &_line_input;
259   uword *bitmap = 0;
260   int rv = 0;
261   clib_error_t *error = 0;
262
263   /* Get a line of input. */
264   if (!unformat_user (input, unformat_line_input, line_input))
265     return 0;
266
267   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
268     {
269       if (unformat (line_input, "%U", unformat_bitmap_list, &bitmap))
270         ;
271       else
272         {
273           error = clib_error_return (0, "unknown input '%U'",
274                                      format_unformat_error, line_input);
275           goto done;
276         }
277     }
278
279   if (bitmap == 0)
280     {
281       error = clib_error_return (0, "List of workers must be specified.");
282       goto done;
283     }
284
285   rv = nat44_ei_set_workers (bitmap);
286
287   clib_bitmap_free (bitmap);
288
289   switch (rv)
290     {
291     case VNET_API_ERROR_INVALID_WORKER:
292       error = clib_error_return (0, "Invalid worker(s).");
293       goto done;
294     case VNET_API_ERROR_FEATURE_DISABLED:
295       error =
296         clib_error_return (0, "Supported only if 2 or more workes available.");
297       goto done;
298     default:
299       break;
300     }
301
302 done:
303   unformat_free (line_input);
304
305   return error;
306 }
307
308 static clib_error_t *
309 nat_show_workers_commnad_fn (vlib_main_t *vm, unformat_input_t *input,
310                              vlib_cli_command_t *cmd)
311 {
312   nat44_ei_main_t *nm = &nat44_ei_main;
313   u32 *worker;
314
315   if (nm->num_workers > 1)
316     {
317       vlib_cli_output (vm, "%d workers", vec_len (nm->workers));
318       vec_foreach (worker, nm->workers)
319         {
320           vlib_worker_thread_t *w =
321             vlib_worker_threads + *worker + nm->first_worker_index;
322           vlib_cli_output (vm, "  %s", w->name);
323         }
324     }
325
326   return 0;
327 }
328
329 static clib_error_t *
330 nat44_ei_set_log_level_command_fn (vlib_main_t *vm, unformat_input_t *input,
331                                    vlib_cli_command_t *cmd)
332 {
333   unformat_input_t _line_input, *line_input = &_line_input;
334   nat44_ei_main_t *nm = &nat44_ei_main;
335   u8 log_level = NAT_LOG_NONE;
336   clib_error_t *error = 0;
337
338   /* Get a line of input. */
339   if (!unformat_user (input, unformat_line_input, line_input))
340     return 0;
341
342   if (!unformat (line_input, "%d", &log_level))
343     {
344       error = clib_error_return (0, "unknown input '%U'",
345                                  format_unformat_error, line_input);
346       goto done;
347     }
348   if (log_level > NAT_LOG_DEBUG)
349     {
350       error = clib_error_return (0, "unknown logging level '%d'", log_level);
351       goto done;
352     }
353   nm->log_level = log_level;
354
355 done:
356   unformat_free (line_input);
357
358   return error;
359 }
360
361 static clib_error_t *
362 nat44_ei_ipfix_logging_enable_disable_command_fn (vlib_main_t *vm,
363                                                   unformat_input_t *input,
364                                                   vlib_cli_command_t *cmd)
365 {
366   unformat_input_t _line_input, *line_input = &_line_input;
367   u32 domain_id = 0;
368   u32 src_port = 0;
369   u8 enable = 1;
370   int rv = 0;
371   clib_error_t *error = 0;
372
373   /* Get a line of input. */
374   if (!unformat_user (input, unformat_line_input, line_input))
375     {
376       rv =
377         nat_ipfix_logging_enable_disable (enable, domain_id, (u16) src_port);
378       if (rv)
379         return clib_error_return (0, "ipfix logging enable failed");
380       return 0;
381     }
382
383   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
384     {
385       if (unformat (line_input, "domain %d", &domain_id))
386         ;
387       else if (unformat (line_input, "src-port %d", &src_port))
388         ;
389       else if (unformat (line_input, "disable"))
390         enable = 0;
391       else
392         {
393           error = clib_error_return (0, "unknown input '%U'",
394                                      format_unformat_error, line_input);
395           goto done;
396         }
397     }
398
399   rv = nat_ipfix_logging_enable_disable (enable, domain_id, (u16) src_port);
400
401   if (rv)
402     {
403       error = clib_error_return (0, "ipfix logging enable failed");
404       goto done;
405     }
406
407 done:
408   unformat_free (line_input);
409
410   return error;
411 }
412
413 static clib_error_t *
414 nat44_ei_show_hash_command_fn (vlib_main_t *vm, unformat_input_t *input,
415                                vlib_cli_command_t *cmd)
416 {
417   nat44_ei_main_t *nm = &nat44_ei_main;
418   nat44_ei_main_per_thread_data_t *tnm;
419   int i;
420   int verbose = 0;
421
422   if (unformat (input, "detail"))
423     verbose = 1;
424   else if (unformat (input, "verbose"))
425     verbose = 2;
426
427   vlib_cli_output (vm, "%U", format_bihash_8_8, &nm->static_mapping_by_local,
428                    verbose);
429   vlib_cli_output (vm, "%U", format_bihash_8_8,
430                    &nm->static_mapping_by_external, verbose);
431   vec_foreach_index (i, nm->per_thread_data)
432     {
433       tnm = vec_elt_at_index (nm->per_thread_data, i);
434       vlib_cli_output (vm, "-------- thread %d %s --------\n", i,
435                        vlib_worker_threads[i].name);
436
437       vlib_cli_output (vm, "%U", format_bihash_8_8, &nm->in2out, verbose);
438       vlib_cli_output (vm, "%U", format_bihash_8_8, &nm->out2in, verbose);
439       vlib_cli_output (vm, "%U", format_bihash_8_8, &tnm->user_hash, verbose);
440     }
441
442   vlib_cli_output (vm, "-------- hash table parameters --------\n");
443   vlib_cli_output (vm, "translation buckets: %u", nm->translation_buckets);
444   vlib_cli_output (vm, "user buckets: %u", nm->user_buckets);
445   return 0;
446 }
447
448 static clib_error_t *
449 nat44_ei_set_alloc_addr_and_port_alg_command_fn (vlib_main_t *vm,
450                                                  unformat_input_t *input,
451                                                  vlib_cli_command_t *cmd)
452 {
453   unformat_input_t _line_input, *line_input = &_line_input;
454   clib_error_t *error = 0;
455   u32 psid, psid_offset, psid_length, port_start, port_end;
456
457   /* Get a line of input. */
458   if (!unformat_user (input, unformat_line_input, line_input))
459     return 0;
460
461   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
462     {
463       if (unformat (line_input, "default"))
464         nat44_ei_set_alloc_default ();
465       else if (unformat (line_input,
466                          "map-e psid %d psid-offset %d psid-len %d", &psid,
467                          &psid_offset, &psid_length))
468         nat44_ei_set_alloc_mape ((u16) psid, (u16) psid_offset,
469                                  (u16) psid_length);
470       else if (unformat (line_input, "port-range %d - %d", &port_start,
471                          &port_end))
472         {
473           if (port_end <= port_start)
474             {
475               error = clib_error_return (
476                 0, "The end-port must be greater than start-port");
477               goto done;
478             }
479           nat44_ei_set_alloc_range ((u16) port_start, (u16) port_end);
480         }
481       else
482         {
483           error = clib_error_return (0, "unknown input '%U'",
484                                      format_unformat_error, line_input);
485           goto done;
486         }
487     }
488
489 done:
490   unformat_free (line_input);
491
492   return error;
493 };
494
495 u8 *
496 format_nat44_ei_addr_and_port_alloc_alg (u8 *s, va_list *args)
497 {
498   u32 i = va_arg (*args, u32);
499   u8 *t = 0;
500
501   switch (i)
502     {
503 #define _(v, N, s)                                                            \
504   case NAT44_EI_ADDR_AND_PORT_ALLOC_ALG_##N:                                  \
505     t = (u8 *) s;                                                             \
506     break;
507       foreach_nat44_ei_addr_and_port_alloc_alg
508 #undef _
509         default : s = format (s, "unknown");
510       return s;
511     }
512   s = format (s, "%s", t);
513   return s;
514 }
515
516 static clib_error_t *
517 nat44_ei_show_alloc_addr_and_port_alg_command_fn (vlib_main_t *vm,
518                                                   unformat_input_t *input,
519                                                   vlib_cli_command_t *cmd)
520 {
521   nat44_ei_main_t *nm = &nat44_ei_main;
522
523   vlib_cli_output (vm, "NAT address and port: %U",
524                    format_nat44_ei_addr_and_port_alloc_alg,
525                    nm->addr_and_port_alloc_alg);
526   switch (nm->addr_and_port_alloc_alg)
527     {
528     case NAT44_EI_ADDR_AND_PORT_ALLOC_ALG_MAPE:
529       vlib_cli_output (vm, "  psid %d psid-offset %d psid-len %d", nm->psid,
530                        nm->psid_offset, nm->psid_length);
531       break;
532     case NAT44_EI_ADDR_AND_PORT_ALLOC_ALG_RANGE:
533       vlib_cli_output (vm, "  start-port %d end-port %d", nm->start_port,
534                        nm->end_port);
535       break;
536     default:
537       break;
538     }
539
540   return 0;
541 }
542
543 static clib_error_t *
544 nat_set_mss_clamping_command_fn (vlib_main_t *vm, unformat_input_t *input,
545                                  vlib_cli_command_t *cmd)
546 {
547   unformat_input_t _line_input, *line_input = &_line_input;
548   nat44_ei_main_t *nm = &nat44_ei_main;
549   clib_error_t *error = 0;
550   u32 mss;
551
552   /* Get a line of input. */
553   if (!unformat_user (input, unformat_line_input, line_input))
554     return 0;
555
556   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
557     {
558       if (unformat (line_input, "disable"))
559         nm->mss_clamping = 0;
560       else if (unformat (line_input, "%d", &mss))
561         nm->mss_clamping = (u16) mss;
562       else
563         {
564           error = clib_error_return (0, "unknown input '%U'",
565                                      format_unformat_error, line_input);
566           goto done;
567         }
568     }
569
570 done:
571   unformat_free (line_input);
572
573   return error;
574 }
575
576 static clib_error_t *
577 nat_show_mss_clamping_command_fn (vlib_main_t *vm, unformat_input_t *input,
578                                   vlib_cli_command_t *cmd)
579 {
580   nat44_ei_main_t *nm = &nat44_ei_main;
581
582   if (nm->mss_clamping)
583     vlib_cli_output (vm, "mss-clamping %d", nm->mss_clamping);
584   else
585     vlib_cli_output (vm, "mss-clamping disabled");
586
587   return 0;
588 }
589
590 static clib_error_t *
591 nat_ha_failover_command_fn (vlib_main_t *vm, unformat_input_t *input,
592                             vlib_cli_command_t *cmd)
593 {
594   unformat_input_t _line_input, *line_input = &_line_input;
595   ip4_address_t addr;
596   u32 port, session_refresh_interval = 10;
597   int rv;
598   clib_error_t *error = 0;
599
600   /* Get a line of input. */
601   if (!unformat_user (input, unformat_line_input, line_input))
602     return 0;
603
604   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
605     {
606       if (unformat (line_input, "%U:%u", unformat_ip4_address, &addr, &port))
607         ;
608       else if (unformat (line_input, "refresh-interval %u",
609                          &session_refresh_interval))
610         ;
611       else
612         {
613           error = clib_error_return (0, "unknown input '%U'",
614                                      format_unformat_error, line_input);
615           goto done;
616         }
617     }
618
619   rv = nat_ha_set_failover (&addr, (u16) port, session_refresh_interval);
620   if (rv)
621     error = clib_error_return (0, "set HA failover failed");
622
623 done:
624   unformat_free (line_input);
625
626   return error;
627 }
628
629 static clib_error_t *
630 nat_ha_listener_command_fn (vlib_main_t *vm, unformat_input_t *input,
631                             vlib_cli_command_t *cmd)
632 {
633   unformat_input_t _line_input, *line_input = &_line_input;
634   ip4_address_t addr;
635   u32 port, path_mtu = 512;
636   int rv;
637   clib_error_t *error = 0;
638
639   /* Get a line of input. */
640   if (!unformat_user (input, unformat_line_input, line_input))
641     return 0;
642
643   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
644     {
645       if (unformat (line_input, "%U:%u", unformat_ip4_address, &addr, &port))
646         ;
647       else if (unformat (line_input, "path-mtu %u", &path_mtu))
648         ;
649       else
650         {
651           error = clib_error_return (0, "unknown input '%U'",
652                                      format_unformat_error, line_input);
653           goto done;
654         }
655     }
656
657   rv = nat_ha_set_listener (&addr, (u16) port, path_mtu);
658   if (rv)
659     error = clib_error_return (0, "set HA listener failed");
660
661 done:
662   unformat_free (line_input);
663
664   return error;
665 }
666
667 static clib_error_t *
668 nat_show_ha_command_fn (vlib_main_t *vm, unformat_input_t *input,
669                         vlib_cli_command_t *cmd)
670 {
671   ip4_address_t addr;
672   u16 port;
673   u32 path_mtu, session_refresh_interval, resync_ack_missed;
674   u8 in_resync;
675
676   nat_ha_get_listener (&addr, &port, &path_mtu);
677   if (!port)
678     {
679       vlib_cli_output (vm, "NAT HA disabled\n");
680       return 0;
681     }
682
683   vlib_cli_output (vm, "LISTENER:\n");
684   vlib_cli_output (vm, "  %U:%u path-mtu %u\n", format_ip4_address, &addr,
685                    port, path_mtu);
686
687   nat_ha_get_failover (&addr, &port, &session_refresh_interval);
688   vlib_cli_output (vm, "FAILOVER:\n");
689   if (port)
690     vlib_cli_output (vm, "  %U:%u refresh-interval %usec\n",
691                      format_ip4_address, &addr, port,
692                      session_refresh_interval);
693   else
694     vlib_cli_output (vm, "  NA\n");
695
696   nat_ha_get_resync_status (&in_resync, &resync_ack_missed);
697   vlib_cli_output (vm, "RESYNC:\n");
698   if (in_resync)
699     vlib_cli_output (vm, "  in progress\n");
700   else
701     vlib_cli_output (vm, "  completed (%d ACK missed)\n", resync_ack_missed);
702
703   return 0;
704 }
705
706 static clib_error_t *
707 nat_ha_flush_command_fn (vlib_main_t *vm, unformat_input_t *input,
708                          vlib_cli_command_t *cmd)
709 {
710   nat_ha_flush (0);
711   return 0;
712 }
713
714 static clib_error_t *
715 nat_ha_resync_command_fn (vlib_main_t *vm, unformat_input_t *input,
716                           vlib_cli_command_t *cmd)
717 {
718   clib_error_t *error = 0;
719
720   if (nat_ha_resync (0, 0, 0))
721     error = clib_error_return (0, "NAT HA resync already running");
722
723   return error;
724 }
725
726 static clib_error_t *
727 add_address_command_fn (vlib_main_t *vm, unformat_input_t *input,
728                         vlib_cli_command_t *cmd)
729 {
730   unformat_input_t _line_input, *line_input = &_line_input;
731   nat44_ei_main_t *nm = &nat44_ei_main;
732   ip4_address_t start_addr, end_addr, this_addr;
733   u32 start_host_order, end_host_order;
734   u32 vrf_id = ~0;
735   int i, count;
736   int is_add = 1;
737   int rv = 0;
738   clib_error_t *error = 0;
739
740   /* Get a line of input. */
741   if (!unformat_user (input, unformat_line_input, line_input))
742     return 0;
743
744   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
745     {
746       if (unformat (line_input, "%U - %U", unformat_ip4_address, &start_addr,
747                     unformat_ip4_address, &end_addr))
748         ;
749       else if (unformat (line_input, "tenant-vrf %u", &vrf_id))
750         ;
751       else if (unformat (line_input, "%U", unformat_ip4_address, &start_addr))
752         end_addr = start_addr;
753       else if (unformat (line_input, "del"))
754         is_add = 0;
755       else
756         {
757           error = clib_error_return (0, "unknown input '%U'",
758                                      format_unformat_error, line_input);
759           goto done;
760         }
761     }
762
763   if (nm->static_mapping_only)
764     {
765       error = clib_error_return (0, "static mapping only mode");
766       goto done;
767     }
768
769   start_host_order = clib_host_to_net_u32 (start_addr.as_u32);
770   end_host_order = clib_host_to_net_u32 (end_addr.as_u32);
771
772   if (end_host_order < start_host_order)
773     {
774       error = clib_error_return (0, "end address less than start address");
775       goto done;
776     }
777
778   count = (end_host_order - start_host_order) + 1;
779
780   if (count > 1024)
781     nat44_ei_log_info ("%U - %U, %d addresses...", format_ip4_address,
782                        &start_addr, format_ip4_address, &end_addr, count);
783
784   this_addr = start_addr;
785
786   for (i = 0; i < count; i++)
787     {
788       if (is_add)
789         rv = nat44_ei_add_address (nm, &this_addr, vrf_id);
790       else
791         rv = nat44_ei_del_address (nm, this_addr, 0);
792
793       switch (rv)
794         {
795         case VNET_API_ERROR_VALUE_EXIST:
796           error = clib_error_return (0, "NAT address already in use.");
797           goto done;
798         case VNET_API_ERROR_NO_SUCH_ENTRY:
799           error = clib_error_return (0, "NAT address not exist.");
800           goto done;
801         case VNET_API_ERROR_UNSPECIFIED:
802           error = clib_error_return (0, "NAT address used in static mapping.");
803           goto done;
804         case VNET_API_ERROR_FEATURE_DISABLED:
805           goto done;
806         default:
807           break;
808         }
809
810       if (nm->out2in_dpo)
811         nat44_ei_add_del_address_dpo (this_addr, is_add);
812
813       increment_v4_address (&this_addr);
814     }
815
816 done:
817   unformat_free (line_input);
818
819   return error;
820 }
821
822 static clib_error_t *
823 nat44_ei_show_addresses_command_fn (vlib_main_t *vm, unformat_input_t *input,
824                                     vlib_cli_command_t *cmd)
825 {
826   nat44_ei_main_t *nm = &nat44_ei_main;
827   nat44_ei_address_t *ap;
828
829   vlib_cli_output (vm, "NAT44 pool addresses:");
830   vec_foreach (ap, nm->addresses)
831     {
832       vlib_cli_output (vm, "%U", format_ip4_address, &ap->addr);
833       if (ap->fib_index != ~0)
834         vlib_cli_output (
835           vm, "  tenant VRF: %u",
836           fib_table_get (ap->fib_index, FIB_PROTOCOL_IP4)->ft_table_id);
837       else
838         vlib_cli_output (vm, "  tenant VRF independent");
839 #define _(N, i, n, s)                                                         \
840   vlib_cli_output (vm, "  %d busy %s ports", ap->busy_##n##_ports, s);
841       foreach_nat_protocol
842 #undef _
843     }
844   return 0;
845 }
846
847 static clib_error_t *
848 nat44_ei_feature_command_fn (vlib_main_t *vm, unformat_input_t *input,
849                              vlib_cli_command_t *cmd)
850 {
851   unformat_input_t _line_input, *line_input = &_line_input;
852   vnet_main_t *vnm = vnet_get_main ();
853   clib_error_t *error = 0;
854   u32 sw_if_index;
855   u32 *inside_sw_if_indices = 0;
856   u32 *outside_sw_if_indices = 0;
857   u8 is_output_feature = 0;
858   int is_del = 0;
859   int i;
860
861   sw_if_index = ~0;
862
863   /* Get a line of input. */
864   if (!unformat_user (input, unformat_line_input, line_input))
865     return 0;
866
867   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
868     {
869       if (unformat (line_input, "in %U", unformat_vnet_sw_interface, vnm,
870                     &sw_if_index))
871         vec_add1 (inside_sw_if_indices, sw_if_index);
872       else if (unformat (line_input, "out %U", unformat_vnet_sw_interface, vnm,
873                          &sw_if_index))
874         vec_add1 (outside_sw_if_indices, sw_if_index);
875       else if (unformat (line_input, "output-feature"))
876         is_output_feature = 1;
877       else if (unformat (line_input, "del"))
878         is_del = 1;
879       else
880         {
881           error = clib_error_return (0, "unknown input '%U'",
882                                      format_unformat_error, line_input);
883           goto done;
884         }
885     }
886
887   if (vec_len (inside_sw_if_indices))
888     {
889       for (i = 0; i < vec_len (inside_sw_if_indices); i++)
890         {
891           sw_if_index = inside_sw_if_indices[i];
892           if (is_output_feature)
893             {
894               if (nat44_ei_interface_add_del_output_feature (sw_if_index, 1,
895                                                              is_del))
896                 {
897                   error = clib_error_return (
898                     0, "%s %U failed", is_del ? "del" : "add",
899                     format_vnet_sw_if_index_name, vnm, sw_if_index);
900                   goto done;
901                 }
902             }
903           else
904             {
905               if (nat44_ei_interface_add_del (sw_if_index, 1, is_del))
906                 {
907                   error = clib_error_return (
908                     0, "%s %U failed", is_del ? "del" : "add",
909                     format_vnet_sw_if_index_name, vnm, sw_if_index);
910                   goto done;
911                 }
912             }
913         }
914     }
915
916   if (vec_len (outside_sw_if_indices))
917     {
918       for (i = 0; i < vec_len (outside_sw_if_indices); i++)
919         {
920           sw_if_index = outside_sw_if_indices[i];
921           if (is_output_feature)
922             {
923               if (nat44_ei_interface_add_del_output_feature (sw_if_index, 0,
924                                                              is_del))
925                 {
926                   error = clib_error_return (
927                     0, "%s %U failed", is_del ? "del" : "add",
928                     format_vnet_sw_if_index_name, vnm, sw_if_index);
929                   goto done;
930                 }
931             }
932           else
933             {
934               if (nat44_ei_interface_add_del (sw_if_index, 0, is_del))
935                 {
936                   error = clib_error_return (
937                     0, "%s %U failed", is_del ? "del" : "add",
938                     format_vnet_sw_if_index_name, vnm, sw_if_index);
939                   goto done;
940                 }
941             }
942         }
943     }
944
945 done:
946   unformat_free (line_input);
947   vec_free (inside_sw_if_indices);
948   vec_free (outside_sw_if_indices);
949
950   return error;
951 }
952
953 static clib_error_t *
954 nat44_ei_show_interfaces_command_fn (vlib_main_t *vm, unformat_input_t *input,
955                                      vlib_cli_command_t *cmd)
956 {
957   nat44_ei_main_t *nm = &nat44_ei_main;
958   nat44_ei_interface_t *i;
959   vnet_main_t *vnm = vnet_get_main ();
960
961   vlib_cli_output (vm, "NAT44 interfaces:");
962   pool_foreach (i, nm->interfaces)
963     {
964       vlib_cli_output (vm, " %U %s", format_vnet_sw_if_index_name, vnm,
965                        i->sw_if_index,
966                        (nat44_ei_interface_is_inside (i) &&
967                         nat44_ei_interface_is_outside (i)) ?
968                          "in out" :
969                          (nat44_ei_interface_is_inside (i) ? "in" : "out"));
970     }
971
972   pool_foreach (i, nm->output_feature_interfaces)
973     {
974       vlib_cli_output (vm, " %U output-feature %s",
975                        format_vnet_sw_if_index_name, vnm, i->sw_if_index,
976                        (nat44_ei_interface_is_inside (i) &&
977                         nat44_ei_interface_is_outside (i)) ?
978                          "in out" :
979                          (nat44_ei_interface_is_inside (i) ? "in" : "out"));
980     }
981
982   return 0;
983 }
984
985 static clib_error_t *
986 add_static_mapping_command_fn (vlib_main_t *vm, unformat_input_t *input,
987                                vlib_cli_command_t *cmd)
988 {
989   unformat_input_t _line_input, *line_input = &_line_input;
990   clib_error_t *error = 0;
991   ip4_address_t l_addr, e_addr;
992   u32 l_port = 0, e_port = 0, vrf_id = ~0;
993   int is_add = 1, addr_only = 1, rv;
994   u32 sw_if_index = ~0;
995   vnet_main_t *vnm = vnet_get_main ();
996   nat_protocol_t proto = NAT_PROTOCOL_OTHER;
997   u8 proto_set = 0;
998
999   /* Get a line of input. */
1000   if (!unformat_user (input, unformat_line_input, line_input))
1001     return 0;
1002
1003   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
1004     {
1005       if (unformat (line_input, "local %U %u", unformat_ip4_address, &l_addr,
1006                     &l_port))
1007         addr_only = 0;
1008       else if (unformat (line_input, "local %U", unformat_ip4_address,
1009                          &l_addr))
1010         ;
1011       else if (unformat (line_input, "external %U %u", unformat_ip4_address,
1012                          &e_addr, &e_port))
1013         addr_only = 0;
1014       else if (unformat (line_input, "external %U", unformat_ip4_address,
1015                          &e_addr))
1016         ;
1017       else if (unformat (line_input, "external %U %u",
1018                          unformat_vnet_sw_interface, vnm, &sw_if_index,
1019                          &e_port))
1020         addr_only = 0;
1021       else if (unformat (line_input, "external %U", unformat_vnet_sw_interface,
1022                          vnm, &sw_if_index))
1023         ;
1024       else if (unformat (line_input, "vrf %u", &vrf_id))
1025         ;
1026       else if (unformat (line_input, "%U", unformat_nat_protocol, &proto))
1027         proto_set = 1;
1028       else if (unformat (line_input, "del"))
1029         is_add = 0;
1030       else
1031         {
1032           error = clib_error_return (0, "unknown input: '%U'",
1033                                      format_unformat_error, line_input);
1034           goto done;
1035         }
1036     }
1037
1038   if (addr_only)
1039     {
1040       if (proto_set)
1041         {
1042           error = clib_error_return (
1043             0, "address only mapping doesn't support protocol");
1044           goto done;
1045         }
1046     }
1047   else if (!proto_set)
1048     {
1049       error = clib_error_return (0, "protocol is required");
1050       goto done;
1051     }
1052
1053   rv = nat44_ei_add_del_static_mapping (
1054     l_addr, e_addr, clib_host_to_net_u16 (l_port),
1055     clib_host_to_net_u16 (e_port), proto, sw_if_index, vrf_id, addr_only, 0, 0,
1056     is_add);
1057
1058   switch (rv)
1059     {
1060     case VNET_API_ERROR_INVALID_VALUE:
1061       error = clib_error_return (0, "External port already in use.");
1062       goto done;
1063     case VNET_API_ERROR_NO_SUCH_ENTRY:
1064       if (is_add)
1065         error = clib_error_return (0, "External address must be allocated.");
1066       else
1067         error = clib_error_return (0, "Mapping not exist.");
1068       goto done;
1069     case VNET_API_ERROR_NO_SUCH_FIB:
1070       error = clib_error_return (0, "No such VRF id.");
1071       goto done;
1072     case VNET_API_ERROR_VALUE_EXIST:
1073       error = clib_error_return (0, "Mapping already exist.");
1074       goto done;
1075     case VNET_API_ERROR_FEATURE_DISABLED:
1076       goto done;
1077     default:
1078       break;
1079     }
1080
1081 done:
1082   unformat_free (line_input);
1083
1084   return error;
1085 }
1086
1087 static clib_error_t *
1088 add_identity_mapping_command_fn (vlib_main_t *vm, unformat_input_t *input,
1089                                  vlib_cli_command_t *cmd)
1090 {
1091   unformat_input_t _line_input, *line_input = &_line_input;
1092   clib_error_t *error = 0;
1093   u32 port = 0, vrf_id = ~0;
1094   ip4_address_t addr;
1095   int is_add = 1;
1096   int addr_only = 1;
1097   u32 sw_if_index = ~0;
1098   vnet_main_t *vnm = vnet_get_main ();
1099   int rv;
1100   nat_protocol_t proto;
1101
1102   addr.as_u32 = 0;
1103
1104   /* Get a line of input. */
1105   if (!unformat_user (input, unformat_line_input, line_input))
1106     return 0;
1107
1108   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
1109     {
1110       if (unformat (line_input, "%U", unformat_ip4_address, &addr))
1111         ;
1112       else if (unformat (line_input, "external %U", unformat_vnet_sw_interface,
1113                          vnm, &sw_if_index))
1114         ;
1115       else if (unformat (line_input, "vrf %u", &vrf_id))
1116         ;
1117       else if (unformat (line_input, "%U %u", unformat_nat_protocol, &proto,
1118                          &port))
1119         addr_only = 0;
1120       else if (unformat (line_input, "del"))
1121         is_add = 0;
1122       else
1123         {
1124           error = clib_error_return (0, "unknown input: '%U'",
1125                                      format_unformat_error, line_input);
1126           goto done;
1127         }
1128     }
1129
1130   rv = nat44_ei_add_del_static_mapping (
1131     addr, addr, clib_host_to_net_u16 (port), clib_host_to_net_u16 (port),
1132     proto, sw_if_index, vrf_id, addr_only, 1, 0, is_add);
1133
1134   switch (rv)
1135     {
1136     case VNET_API_ERROR_INVALID_VALUE:
1137       error = clib_error_return (0, "External port already in use.");
1138       goto done;
1139     case VNET_API_ERROR_NO_SUCH_ENTRY:
1140       if (is_add)
1141         error = clib_error_return (0, "External address must be allocated.");
1142       else
1143         error = clib_error_return (0, "Mapping not exist.");
1144       goto done;
1145     case VNET_API_ERROR_NO_SUCH_FIB:
1146       error = clib_error_return (0, "No such VRF id.");
1147       goto done;
1148     case VNET_API_ERROR_VALUE_EXIST:
1149       error = clib_error_return (0, "Mapping already exist.");
1150       goto done;
1151     default:
1152       break;
1153     }
1154
1155 done:
1156   unformat_free (line_input);
1157
1158   return error;
1159 }
1160
1161 static clib_error_t *
1162 nat44_ei_show_static_mappings_command_fn (vlib_main_t *vm,
1163                                           unformat_input_t *input,
1164                                           vlib_cli_command_t *cmd)
1165 {
1166   nat44_ei_main_t *nm = &nat44_ei_main;
1167   nat44_ei_static_mapping_t *m;
1168   nat44_ei_static_map_resolve_t *rp;
1169
1170   vlib_cli_output (vm, "NAT44 static mappings:");
1171   pool_foreach (m, nm->static_mappings)
1172     {
1173       vlib_cli_output (vm, " %U", format_nat44_ei_static_mapping, m);
1174     }
1175   vec_foreach (rp, nm->to_resolve)
1176     vlib_cli_output (vm, " %U", format_nat44_ei_static_map_to_resolve, rp);
1177
1178   return 0;
1179 }
1180
1181 static clib_error_t *
1182 nat44_ei_add_interface_address_command_fn (vlib_main_t *vm,
1183                                            unformat_input_t *input,
1184                                            vlib_cli_command_t *cmd)
1185 {
1186   nat44_ei_main_t *nm = &nat44_ei_main;
1187   unformat_input_t _line_input, *line_input = &_line_input;
1188   u32 sw_if_index;
1189   int rv;
1190   int is_del = 0;
1191   clib_error_t *error = 0;
1192
1193   /* Get a line of input. */
1194   if (!unformat_user (input, unformat_line_input, line_input))
1195     return 0;
1196
1197   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
1198     {
1199       if (unformat (line_input, "%U", unformat_vnet_sw_interface,
1200                     nm->vnet_main, &sw_if_index))
1201         ;
1202       else if (unformat (line_input, "del"))
1203         is_del = 1;
1204       else
1205         {
1206           error = clib_error_return (0, "unknown input '%U'",
1207                                      format_unformat_error, line_input);
1208           goto done;
1209         }
1210     }
1211
1212   rv = nat44_ei_add_interface_address (nm, sw_if_index, is_del);
1213
1214   switch (rv)
1215     {
1216     case 0:
1217       break;
1218
1219     default:
1220       error = clib_error_return (
1221         0, "nat44_ei_add_interface_address returned %d", rv);
1222       goto done;
1223     }
1224
1225 done:
1226   unformat_free (line_input);
1227
1228   return error;
1229 }
1230
1231 static clib_error_t *
1232 nat44_ei_show_interface_address_command_fn (vlib_main_t *vm,
1233                                             unformat_input_t *input,
1234                                             vlib_cli_command_t *cmd)
1235 {
1236   nat44_ei_main_t *nm = &nat44_ei_main;
1237   vnet_main_t *vnm = vnet_get_main ();
1238   u32 *sw_if_index;
1239
1240   vlib_cli_output (vm, "NAT44 pool address interfaces:");
1241   vec_foreach (sw_if_index, nm->auto_add_sw_if_indices)
1242     {
1243       vlib_cli_output (vm, " %U", format_vnet_sw_if_index_name, vnm,
1244                        *sw_if_index);
1245     }
1246   return 0;
1247 }
1248
1249 static clib_error_t *
1250 nat44_ei_show_sessions_command_fn (vlib_main_t *vm, unformat_input_t *input,
1251                                    vlib_cli_command_t *cmd)
1252 {
1253   unformat_input_t _line_input, *line_input = &_line_input;
1254   clib_error_t *error = 0;
1255
1256   nat44_ei_main_per_thread_data_t *tnm;
1257   nat44_ei_main_t *nm = &nat44_ei_main;
1258
1259   int detail = 0;
1260   int i = 0;
1261
1262   if (!unformat_user (input, unformat_line_input, line_input))
1263     goto print;
1264
1265   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
1266     {
1267       if (unformat (line_input, "detail"))
1268         detail = 1;
1269       else
1270         {
1271           error = clib_error_return (0, "unknown input '%U'",
1272                                      format_unformat_error, line_input);
1273           break;
1274         }
1275     }
1276   unformat_free (line_input);
1277
1278 print:
1279   vlib_cli_output (vm, "NAT44 sessions:");
1280
1281   vec_foreach_index (i, nm->per_thread_data)
1282     {
1283       tnm = vec_elt_at_index (nm->per_thread_data, i);
1284
1285       vlib_cli_output (vm, "-------- thread %d %s: %d sessions --------\n", i,
1286                        vlib_worker_threads[i].name, pool_elts (tnm->sessions));
1287
1288       nat44_ei_user_t *u;
1289       pool_foreach (u, tnm->users)
1290         {
1291           vlib_cli_output (vm, "  %U", format_nat44_ei_user, tnm, u, detail);
1292         }
1293     }
1294   return error;
1295 }
1296
1297 static clib_error_t *
1298 nat44_ei_del_user_command_fn (vlib_main_t *vm, unformat_input_t *input,
1299                               vlib_cli_command_t *cmd)
1300 {
1301   unformat_input_t _line_input, *line_input = &_line_input;
1302   clib_error_t *error = 0;
1303   ip4_address_t addr;
1304   u32 fib_index = 0;
1305   int rv;
1306
1307   /* Get a line of input. */
1308   if (!unformat_user (input, unformat_line_input, line_input))
1309     return 0;
1310
1311   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
1312     {
1313       if (unformat (line_input, "%U", unformat_ip4_address, &addr))
1314         ;
1315       else if (unformat (line_input, "fib %u", &fib_index))
1316         ;
1317       else
1318         {
1319           error = clib_error_return (0, "unknown input '%U'",
1320                                      format_unformat_error, line_input);
1321           goto done;
1322         }
1323     }
1324
1325   rv = nat44_ei_user_del (&addr, fib_index);
1326
1327   if (!rv)
1328     {
1329       error = clib_error_return (0, "nat44_ei_user_del returned %d", rv);
1330     }
1331
1332 done:
1333   unformat_free (line_input);
1334
1335   return error;
1336 }
1337
1338 static clib_error_t *
1339 nat44_ei_clear_sessions_command_fn (vlib_main_t *vm, unformat_input_t *input,
1340                                     vlib_cli_command_t *cmd)
1341 {
1342   clib_error_t *error = 0;
1343   nat44_ei_sessions_clear ();
1344   return error;
1345 }
1346
1347 static clib_error_t *
1348 nat44_ei_del_session_command_fn (vlib_main_t *vm, unformat_input_t *input,
1349                                  vlib_cli_command_t *cmd)
1350 {
1351   nat44_ei_main_t *nm = &nat44_ei_main;
1352   unformat_input_t _line_input, *line_input = &_line_input;
1353   u32 port = 0, vrf_id = nm->outside_vrf_id;
1354   clib_error_t *error = 0;
1355   nat_protocol_t proto;
1356   ip4_address_t addr;
1357   int rv, is_in = 0;
1358
1359   /* Get a line of input. */
1360   if (!unformat_user (input, unformat_line_input, line_input))
1361     return 0;
1362
1363   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
1364     {
1365       if (unformat (line_input, "%U:%u %U", unformat_ip4_address, &addr, &port,
1366                     unformat_nat_protocol, &proto))
1367         ;
1368       else if (unformat (line_input, "in"))
1369         {
1370           is_in = 1;
1371           vrf_id = nm->inside_vrf_id;
1372         }
1373       else if (unformat (line_input, "out"))
1374         {
1375           is_in = 0;
1376           vrf_id = nm->outside_vrf_id;
1377         }
1378       else if (unformat (line_input, "vrf %u", &vrf_id))
1379         ;
1380       else
1381         {
1382           error = clib_error_return (0, "unknown input '%U'",
1383                                      format_unformat_error, line_input);
1384           goto done;
1385         }
1386     }
1387
1388   rv = nat44_ei_del_session (nm, &addr, clib_host_to_net_u16 (port), proto,
1389                              vrf_id, is_in);
1390
1391   switch (rv)
1392     {
1393     case 0:
1394       break;
1395
1396     default:
1397       error = clib_error_return (0, "nat44_ei_del_session returned %d", rv);
1398       goto done;
1399     }
1400
1401 done:
1402   unformat_free (line_input);
1403
1404   return error;
1405 }
1406
1407 static clib_error_t *
1408 nat44_ei_forwarding_set_command_fn (vlib_main_t *vm, unformat_input_t *input,
1409                                     vlib_cli_command_t *cmd)
1410 {
1411   nat44_ei_main_t *nm = &nat44_ei_main;
1412   unformat_input_t _line_input, *line_input = &_line_input;
1413   u8 forwarding_enable;
1414   u8 forwarding_enable_set = 0;
1415   clib_error_t *error = 0;
1416
1417   /* Get a line of input. */
1418   if (!unformat_user (input, unformat_line_input, line_input))
1419     return clib_error_return (0, "'enable' or 'disable' expected");
1420
1421   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
1422     {
1423       if (!forwarding_enable_set && unformat (line_input, "enable"))
1424         {
1425           forwarding_enable = 1;
1426           forwarding_enable_set = 1;
1427         }
1428       else if (!forwarding_enable_set && unformat (line_input, "disable"))
1429         {
1430           forwarding_enable = 0;
1431           forwarding_enable_set = 1;
1432         }
1433       else
1434         {
1435           error = clib_error_return (0, "unknown input '%U'",
1436                                      format_unformat_error, line_input);
1437           goto done;
1438         }
1439     }
1440
1441   if (!forwarding_enable_set)
1442     {
1443       error = clib_error_return (0, "'enable' or 'disable' expected");
1444       goto done;
1445     }
1446
1447   nm->forwarding_enabled = forwarding_enable;
1448
1449 done:
1450   unformat_free (line_input);
1451
1452   return error;
1453 }
1454
1455 static clib_error_t *
1456 set_timeout_command_fn (vlib_main_t *vm, unformat_input_t *input,
1457                         vlib_cli_command_t *cmd)
1458 {
1459   nat44_ei_main_t *nm = &nat44_ei_main;
1460   unformat_input_t _line_input, *line_input = &_line_input;
1461   clib_error_t *error = 0;
1462
1463   /* Get a line of input. */
1464   if (!unformat_user (input, unformat_line_input, line_input))
1465     return 0;
1466
1467   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
1468     {
1469       if (unformat (line_input, "udp %u", &nm->timeouts.udp))
1470         ;
1471       else if (unformat (line_input, "tcp-established %u",
1472                          &nm->timeouts.tcp.established))
1473         ;
1474       else if (unformat (line_input, "tcp-transitory %u",
1475                          &nm->timeouts.tcp.transitory))
1476         ;
1477       else if (unformat (line_input, "icmp %u", &nm->timeouts.icmp))
1478         ;
1479       else if (unformat (line_input, "reset"))
1480         nat_reset_timeouts (&nm->timeouts);
1481       else
1482         {
1483           error = clib_error_return (0, "unknown input '%U'",
1484                                      format_unformat_error, line_input);
1485           goto done;
1486         }
1487     }
1488 done:
1489   unformat_free (line_input);
1490   return error;
1491 }
1492
1493 static clib_error_t *
1494 nat_show_timeouts_command_fn (vlib_main_t *vm, unformat_input_t *input,
1495                               vlib_cli_command_t *cmd)
1496 {
1497   nat44_ei_main_t *nm = &nat44_ei_main;
1498
1499   // TODO: make format timeout function
1500   vlib_cli_output (vm, "udp timeout: %dsec", nm->timeouts.udp);
1501   vlib_cli_output (vm, "tcp-established timeout: %dsec",
1502                    nm->timeouts.tcp.established);
1503   vlib_cli_output (vm, "tcp-transitory timeout: %dsec",
1504                    nm->timeouts.tcp.transitory);
1505   vlib_cli_output (vm, "icmp timeout: %dsec", nm->timeouts.icmp);
1506
1507   return 0;
1508 }
1509
1510 /*?
1511  * @cliexpar
1512  * @cliexstart{nat44 ei enable}
1513  * Enable nat44 ei plugin
1514  * To enable nat44, use:
1515  *  vpp# nat44 ei enable sessions <n>
1516  * To enable nat44 ei static mapping only, use:
1517  *  vpp# nat44 ei enable sessions <n> static-mapping
1518  * To enable nat44 ei static mapping with connection tracking, use:
1519  *  vpp# nat44 ei enable sessions <n> static-mapping connection-tracking
1520  * To enable nat44 ei out2in dpo, use:
1521  *  vpp# nat44 ei enable sessions <n> out2in-dpo
1522  * To set inside-vrf outside-vrf, use:
1523  *  vpp# nat44 ei enable sessions <n> inside-vrf <id> outside-vrf <id>
1524  * @cliexend
1525 ?*/
1526 VLIB_CLI_COMMAND (nat44_ei_enable_command, static) = {
1527   .path = "nat44 ei enable",
1528   .short_help =
1529     "nat44 ei enable sessions <max-number> [users <max-number>] "
1530     "[static-mappig-only [connection-tracking]|out2in-dpo] [inside-vrf "
1531     "<vrf-id>] [outside-vrf <vrf-id>] [user-sessions <max-number>]",
1532   .function = nat44_ei_enable_command_fn,
1533 };
1534
1535 /*?
1536  * @cliexpar
1537  * @cliexstart{nat44 ei disable}
1538  * Disable nat44 ei plugin
1539  * To disable nat44, use:
1540  *  vpp# nat44 ei disable
1541  * @cliexend
1542 ?*/
1543 VLIB_CLI_COMMAND (nat44_ei_disable_command, static) = {
1544   .path = "nat44 ei disable",
1545   .short_help = "nat44 ei disable",
1546   .function = nat44_ei_disable_command_fn,
1547 };
1548
1549 /*?
1550  * @cliexpar
1551  * @cliexstart{set snat44 ei workers}
1552  * Set NAT workers if 2 or more workers available, use:
1553  *  vpp# set snat44 ei workers 0-2,5
1554  * @cliexend
1555 ?*/
1556 VLIB_CLI_COMMAND (set_workers_command, static) = {
1557   .path = "set nat44 ei workers",
1558   .function = set_workers_command_fn,
1559   .short_help = "set nat44 ei workers <workers-list>",
1560 };
1561
1562 /*?
1563  * @cliexpar
1564  * @cliexstart{show nat44 ei workers}
1565  * Show NAT workers.
1566  *  vpp# show nat44 ei workers:
1567  *  2 workers
1568  *    vpp_wk_0
1569  *    vpp_wk_1
1570  * @cliexend
1571 ?*/
1572 VLIB_CLI_COMMAND (nat_show_workers_command, static) = {
1573   .path = "show nat44 ei workers",
1574   .short_help = "show nat44 ei workers",
1575   .function = nat_show_workers_commnad_fn,
1576 };
1577
1578 /*?
1579  * @cliexpar
1580  * @cliexstart{set nat44 ei timeout}
1581  * Set values of timeouts for NAT sessions (in seconds), use:
1582  *  vpp# set nat44 ei timeout udp 120 tcp-established 7500 tcp-transitory 250
1583 icmp 90
1584  * To reset default values use:
1585  *  vpp# set nat44 ei timeout reset
1586  * @cliexend
1587 ?*/
1588 VLIB_CLI_COMMAND (set_timeout_command, static) = {
1589   .path = "set nat44 ei timeout",
1590   .function = set_timeout_command_fn,
1591   .short_help = "set nat44 ei timeout [udp <sec> | tcp-established <sec> "
1592                 "tcp-transitory <sec> | icmp <sec> | reset]",
1593 };
1594
1595 /*?
1596  * @cliexpar
1597  * @cliexstart{show nat44 ei timeouts}
1598  * Show values of timeouts for NAT sessions.
1599  * vpp# show nat44 ei timeouts
1600  * udp timeout: 300sec
1601  * tcp-established timeout: 7440sec
1602  * tcp-transitory timeout: 240sec
1603  * icmp timeout: 60sec
1604  * @cliexend
1605 ?*/
1606 VLIB_CLI_COMMAND (nat_show_timeouts_command, static) = {
1607   .path = "show nat44 ei timeouts",
1608   .short_help = "show nat44 ei timeouts",
1609   .function = nat_show_timeouts_command_fn,
1610 };
1611
1612 /*?
1613  * @cliexpar
1614  * @cliexstart{nat44 ei set logging level}
1615  * To set NAT logging level use:
1616  * Set nat44 ei logging level
1617  * @cliexend
1618 ?*/
1619 VLIB_CLI_COMMAND (nat44_ei_set_log_level_command, static) = {
1620   .path = "nat44 ei set logging level",
1621   .function = nat44_ei_set_log_level_command_fn,
1622   .short_help = "nat44 ei set logging level <level>",
1623 };
1624
1625 /*?
1626  * @cliexpar
1627  * @cliexstart{snat44 ei ipfix logging}
1628  * To enable NAT IPFIX logging use:
1629  *  vpp# nat44 ei ipfix logging
1630  * To set IPFIX exporter use:
1631  *  vpp# set ipfix exporter collector 10.10.10.3 src 10.10.10.1
1632  * @cliexend
1633 ?*/
1634 VLIB_CLI_COMMAND (nat44_ei_ipfix_logging_enable_disable_command, static) = {
1635   .path = "nat44 ei ipfix logging",
1636   .function = nat44_ei_ipfix_logging_enable_disable_command_fn,
1637   .short_help =
1638     "nat44 ei ipfix logging [domain <domain-id>] [src-port <port>] [disable]",
1639 };
1640
1641 /*?
1642  * @cliexpar
1643  * @cliexstart{nat44 ei addr-port-assignment-alg}
1644  * Set address and port assignment algorithm
1645  * For the MAP-E CE limit port choice based on PSID use:
1646  *  vpp# nat44 ei addr-port-assignment-alg map-e psid 10 psid-offset 6 psid-len
1647 6
1648  * For port range use:
1649  *  vpp# nat44 ei addr-port-assignment-alg port-range <start-port> - <end-port>
1650  * To set standard (default) address and port assignment algorithm use:
1651  *  vpp# nat44 ei addr-port-assignment-alg default
1652  * @cliexend
1653 ?*/
1654 VLIB_CLI_COMMAND (nat44_ei_set_alloc_addr_and_port_alg_command, static) = {
1655   .path = "nat44 ei addr-port-assignment-alg",
1656   .short_help = "nat44 ei addr-port-assignment-alg <alg-name> [<alg-params>]",
1657   .function = nat44_ei_set_alloc_addr_and_port_alg_command_fn,
1658 };
1659
1660 /*?
1661  * @cliexpar
1662  * @cliexstart{show nat44 ei addr-port-assignment-alg}
1663  * Show address and port assignment algorithm
1664  * @cliexend
1665 ?*/
1666 VLIB_CLI_COMMAND (nat44_ei_show_alloc_addr_and_port_alg_command, static) = {
1667   .path = "show nat44 ei addr-port-assignment-alg",
1668   .short_help = "show nat44 ei addr-port-assignment-alg",
1669   .function = nat44_ei_show_alloc_addr_and_port_alg_command_fn,
1670 };
1671
1672 /*?
1673  * @cliexpar
1674  * @cliexstart{nat44 ei mss-clamping}
1675  * Set TCP MSS rewriting configuration
1676  * To enable TCP MSS rewriting use:
1677  *  vpp# nat44 ei mss-clamping 1452
1678  * To disbale TCP MSS rewriting use:
1679  *  vpp# nat44 ei mss-clamping disable
1680  * @cliexend
1681 ?*/
1682 VLIB_CLI_COMMAND (nat_set_mss_clamping_command, static) = {
1683   .path = "nat44 ei mss-clamping",
1684   .short_help = "nat44 ei mss-clamping <mss-value>|disable",
1685   .function = nat_set_mss_clamping_command_fn,
1686 };
1687
1688 /*?
1689  * @cliexpar
1690  * @cliexstart{show nat44 ei mss-clamping}
1691  * Show TCP MSS rewriting configuration
1692  * @cliexend
1693 ?*/
1694 VLIB_CLI_COMMAND (nat_show_mss_clamping_command, static) = {
1695   .path = "show nat44 ei mss-clamping",
1696   .short_help = "show nat44 ei mss-clamping",
1697   .function = nat_show_mss_clamping_command_fn,
1698 };
1699
1700 /*?
1701  * @cliexpar
1702  * @cliexstart{nat44 ei ha failover}
1703  * Set HA failover (remote settings)
1704  * @cliexend
1705 ?*/
1706 VLIB_CLI_COMMAND (nat_ha_failover_command, static) = {
1707   .path = "nat44 ei ha failover",
1708   .short_help =
1709     "nat44 ei ha failover <ip4-address>:<port> [refresh-interval <sec>]",
1710   .function = nat_ha_failover_command_fn,
1711 };
1712
1713 /*?
1714  * @cliexpar
1715  * @cliexstart{nat44 ei ha listener}
1716  * Set HA listener (local settings)
1717  * @cliexend
1718 ?*/
1719 VLIB_CLI_COMMAND (nat_ha_listener_command, static) = {
1720   .path = "nat44 ei ha listener",
1721   .short_help =
1722     "nat44 ei ha listener <ip4-address>:<port> [path-mtu <path-mtu>]",
1723   .function = nat_ha_listener_command_fn,
1724 };
1725
1726 /*?
1727  * @cliexpar
1728  * @cliexstart{show nat44 ei ha}
1729  * Show HA configuration/status
1730  * @cliexend
1731 ?*/
1732 VLIB_CLI_COMMAND (nat_show_ha_command, static) = {
1733   .path = "show nat44 ei ha",
1734   .short_help = "show nat44 ei ha",
1735   .function = nat_show_ha_command_fn,
1736 };
1737
1738 /*?
1739  * @cliexpar
1740  * @cliexstart{nat44 ei ha flush}
1741  * Flush the current HA data (for testing)
1742  * @cliexend
1743 ?*/
1744 VLIB_CLI_COMMAND (nat_ha_flush_command, static) = {
1745   .path = "nat44 ei ha flush",
1746   .short_help = "nat44 ei ha flush",
1747   .function = nat_ha_flush_command_fn,
1748 };
1749
1750 /*?
1751  * @cliexpar
1752  * @cliexstart{nat44 ei ha resync}
1753  * Resync HA (resend existing sessions to new failover)
1754  * @cliexend
1755 ?*/
1756 VLIB_CLI_COMMAND (nat_ha_resync_command, static) = {
1757   .path = "nat44 ei ha resync",
1758   .short_help = "nat44 ei ha resync",
1759   .function = nat_ha_resync_command_fn,
1760 };
1761
1762 /*?
1763  * @cliexpar
1764  * @cliexstart{show nat44 ei hash tables}
1765  * Show NAT44 hash tables
1766  * @cliexend
1767 ?*/
1768 VLIB_CLI_COMMAND (nat44_ei_show_hash, static) = {
1769   .path = "show nat44 ei hash tables",
1770   .short_help = "show nat44 ei hash tables [detail|verbose]",
1771   .function = nat44_ei_show_hash_command_fn,
1772 };
1773
1774 /*?
1775  * @cliexpar
1776  * @cliexstart{nat44 ei add address}
1777  * Add/delete NAT44 pool address.
1778  * To add NAT44 pool address use:
1779  *  vpp# nat44 ei add address 172.16.1.3
1780  *  vpp# nat44 ei add address 172.16.2.2 - 172.16.2.24
1781  * To add NAT44 pool address for specific tenant (identified by VRF id) use:
1782  *  vpp# nat44 ei add address 172.16.1.3 tenant-vrf 10
1783  * @cliexend
1784 ?*/
1785 VLIB_CLI_COMMAND (add_address_command, static) = {
1786   .path = "nat44 ei add address",
1787   .short_help = "nat44 ei add address <ip4-range-start> [- <ip4-range-end>] "
1788                 "[tenant-vrf <vrf-id>] [del]",
1789   .function = add_address_command_fn,
1790 };
1791
1792 /*?
1793  * @cliexpar
1794  * @cliexstart{show nat44 ei addresses}
1795  * Show NAT44 pool addresses.
1796  * vpp# show nat44 ei addresses
1797  * NAT44 pool addresses:
1798  * 172.16.2.2
1799  *   tenant VRF independent
1800  *   10 busy udp ports
1801  *   0 busy tcp ports
1802  *   0 busy icmp ports
1803  * 172.16.1.3
1804  *   tenant VRF: 10
1805  *   0 busy udp ports
1806  *   2 busy tcp ports
1807  *   0 busy icmp ports
1808  * @cliexend
1809 ?*/
1810 VLIB_CLI_COMMAND (nat44_ei_show_addresses_command, static) = {
1811   .path = "show nat44 ei addresses",
1812   .short_help = "show nat44 ei addresses",
1813   .function = nat44_ei_show_addresses_command_fn,
1814 };
1815
1816 /*?
1817  * @cliexpar
1818  * @cliexstart{set interface nat44}
1819  * Enable/disable NAT44 feature on the interface.
1820  * To enable NAT44 feature with local network interface use:
1821  *  vpp# set interface nat44 ei in GigabitEthernet0/8/0
1822  * To enable NAT44 feature with external network interface use:
1823  *  vpp# set interface nat44 ei out GigabitEthernet0/a/0
1824  * @cliexend
1825 ?*/
1826 VLIB_CLI_COMMAND (set_interface_nat44_ei_command, static) = {
1827   .path = "set interface nat44 ei",
1828   .function = nat44_ei_feature_command_fn,
1829   .short_help =
1830     "set interface nat44 ei in <intfc> out <intfc> [output-feature] "
1831     "[del]",
1832 };
1833
1834 /*?
1835  * @cliexpar
1836  * @cliexstart{show nat44 ei interfaces}
1837  * Show interfaces with NAT44 feature.
1838  * vpp# show nat44 ei interfaces
1839  * NAT44 interfaces:
1840  *  GigabitEthernet0/8/0 in
1841  *  GigabitEthernet0/a/0 out
1842  * @cliexend
1843 ?*/
1844 VLIB_CLI_COMMAND (nat44_ei_show_interfaces_command, static) = {
1845   .path = "show nat44 ei interfaces",
1846   .short_help = "show nat44 ei interfaces",
1847   .function = nat44_ei_show_interfaces_command_fn,
1848 };
1849
1850 /*?
1851  * @cliexpar
1852  * @cliexstart{nat44 ei add static mapping}
1853  * Static mapping allows hosts on the external network to initiate connection
1854  * to to the local network host.
1855  * To create static mapping between local host address 10.0.0.3 port 6303 and
1856  * external address 4.4.4.4 port 3606 for TCP protocol use:
1857  *  vpp# nat44 ei add static mapping tcp local 10.0.0.3 6303 external 4.4.4.4
1858 3606
1859  * If not runnig "static mapping only" NAT plugin mode use before:
1860  *  vpp# nat44 ei add address 4.4.4.4
1861  * To create address only static mapping between local and external address
1862 use:
1863  *  vpp# nat44 ei add static mapping local 10.0.0.3 external 4.4.4.4
1864  * To create ICMP static mapping between local and external with ICMP echo
1865  * identifier 10 use:
1866  *  vpp# nat44 ei add static mapping icmp local 10.0.0.3 10 external 4.4.4.4 10
1867  * @cliexend
1868 ?*/
1869 VLIB_CLI_COMMAND (add_static_mapping_command, static) = {
1870   .path = "nat44 ei add static mapping",
1871   .function = add_static_mapping_command_fn,
1872   .short_help = "nat44 ei add static mapping tcp|udp|icmp local <addr> "
1873                 "[<port|icmp-echo-id>] "
1874                 "external <addr> [<port|icmp-echo-id>] [vrf <table-id>] [del]",
1875 };
1876
1877 /*?
1878  * @cliexpar
1879  * @cliexstart{nat44 ei add identity mapping}
1880  * Identity mapping translate an IP address to itself.
1881  * To create identity mapping for address 10.0.0.3 port 6303 for TCP protocol
1882  * use:
1883  *  vpp# nat44 ei add identity mapping 10.0.0.3 tcp 6303
1884  * To create identity mapping for address 10.0.0.3 use:
1885  *  vpp# nat44 ei add identity mapping 10.0.0.3
1886  * To create identity mapping for DHCP addressed interface use:
1887  *  vpp# nat44 ei add identity mapping external GigabitEthernet0/a/0 tcp 3606
1888  * @cliexend
1889 ?*/
1890 VLIB_CLI_COMMAND (add_identity_mapping_command, static) = {
1891   .path = "nat44 ei add identity mapping",
1892   .function = add_identity_mapping_command_fn,
1893   .short_help =
1894     "nat44 ei add identity mapping <ip4-addr>|external <interface> "
1895     "[<protocol> <port>] [vrf <table-id>] [del]",
1896 };
1897
1898 /*?
1899  * @cliexpar
1900  * @cliexstart{show nat44 ei static mappings}
1901  * Show NAT44 static mappings.
1902  * vpp# show nat44 ei static mappings
1903  * NAT44 static mappings:
1904  *  local 10.0.0.3 external 4.4.4.4 vrf 0
1905  *  tcp local 192.168.0.4:6303 external 4.4.4.3:3606 vrf 0
1906  *  tcp vrf 0 external 1.2.3.4:80
1907  *   local 10.100.10.10:8080 probability 80
1908  *   local 10.100.10.20:8080 probability 20
1909  *  tcp local 10.0.0.10:3603 external GigabitEthernet0/a/0:6306 vrf 10
1910  * @cliexend
1911 ?*/
1912 VLIB_CLI_COMMAND (nat44_ei_show_static_mappings_command, static) = {
1913   .path = "show nat44 ei static mappings",
1914   .short_help = "show nat44 ei static mappings",
1915   .function = nat44_ei_show_static_mappings_command_fn,
1916 };
1917
1918 /*?
1919  * @cliexpar
1920  * @cliexstart{nat44 ei add interface address}
1921  * Use NAT44 pool address from specific interfce
1922  * To add NAT44 pool address from specific interface use:
1923  *  vpp# nat44 ei add interface address GigabitEthernet0/8/0
1924  * @cliexend
1925 ?*/
1926 VLIB_CLI_COMMAND (nat44_ei_add_interface_address_command, static) = {
1927   .path = "nat44 ei add interface address",
1928   .short_help = "nat44 ei add interface address <interface> [del]",
1929   .function = nat44_ei_add_interface_address_command_fn,
1930 };
1931
1932 /*?
1933  * @cliexpar
1934  * @cliexstart{show nat44 ei interface address}
1935  * Show NAT44 pool address interfaces
1936  * vpp# show nat44 ei interface address
1937  * NAT44 pool address interfaces:
1938  *  GigabitEthernet0/a/0
1939  * @cliexend
1940 ?*/
1941 VLIB_CLI_COMMAND (nat44_ei_show_interface_address_command, static) = {
1942   .path = "show nat44 ei interface address",
1943   .short_help = "show nat44 ei interface address",
1944   .function = nat44_ei_show_interface_address_command_fn,
1945 };
1946
1947 /*?
1948  * @cliexpar
1949  * @cliexstart{show nat44 ei sessions}
1950  * Show NAT44 sessions.
1951  * @cliexend
1952 ?*/
1953 VLIB_CLI_COMMAND (nat44_ei_show_sessions_command, static) = {
1954   .path = "show nat44 ei sessions",
1955   .short_help = "show nat44 ei sessions [detail|metrics]",
1956   .function = nat44_ei_show_sessions_command_fn,
1957 };
1958
1959 /*?
1960  * @cliexpar
1961  * @cliexstart{nat44 ei del user}
1962  * To delete all NAT44 user sessions:
1963  *  vpp# nat44 ei del user 10.0.0.3
1964  * @cliexend
1965 ?*/
1966 VLIB_CLI_COMMAND (nat44_ei_del_user_command, static) = {
1967   .path = "nat44 ei del user",
1968   .short_help = "nat44 ei del user <addr> [fib <index>]",
1969   .function = nat44_ei_del_user_command_fn,
1970 };
1971
1972 /*?
1973  * @cliexpar
1974  * @cliexstart{clear nat44 ei sessions}
1975  * To clear all NAT44 sessions
1976  *  vpp# clear nat44 ei sessions
1977  * @cliexend
1978 ?*/
1979 VLIB_CLI_COMMAND (nat44_ei_clear_sessions_command, static) = {
1980   .path = "clear nat44 ei sessions",
1981   .short_help = "clear nat44 ei sessions",
1982   .function = nat44_ei_clear_sessions_command_fn,
1983 };
1984
1985 /*?
1986  * @cliexpar
1987  * @cliexstart{nat44 ei del session}
1988  * To administratively delete NAT44 session by inside address and port use:
1989  *  vpp# nat44 ei del session in 10.0.0.3:6303 tcp
1990  * To administratively delete NAT44 session by outside address and port use:
1991  *  vpp# nat44 ei del session out 1.0.0.3:6033 udp
1992  * @cliexend
1993 ?*/
1994 VLIB_CLI_COMMAND (nat44_ei_del_session_command, static) = {
1995   .path = "nat44 ei del session",
1996   .short_help = "nat44 ei del session in|out <addr>:<port> tcp|udp|icmp [vrf "
1997                 "<id>] [external-host <addr>:<port>]",
1998   .function = nat44_ei_del_session_command_fn,
1999 };
2000
2001 /*?
2002  * @cliexpar
2003  * @cliexstart{nat44 ei forwarding}
2004  * Enable or disable forwarding
2005  * Forward packets which don't match existing translation
2006  * or static mapping instead of dropping them.
2007  * To enable forwarding, use:
2008  *  vpp# nat44 ei forwarding enable
2009  * To disable forwarding, use:
2010  *  vpp# nat44 ei forwarding disable
2011  * @cliexend
2012 ?*/
2013 VLIB_CLI_COMMAND (nat44_ei_forwarding_set_command, static) = {
2014   .path = "nat44 ei forwarding",
2015   .short_help = "nat44 ei forwarding enable|disable",
2016   .function = nat44_ei_forwarding_set_command_fn,
2017 };
2018
2019 /*
2020  * fd.io coding-style-patch-verification: ON
2021  *
2022  * Local Variables:
2023  * eval: (c-set-style "gnu")
2024  * End:
2025  */