summaryrefslogtreecommitdiffstats
path: root/src/plugins/nsh/nsh-md2-ioam/nsh_md2_ioam.c
blob: 1fa1c55b00fa7eb3c30a9ef5ba1845023f219c59 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
/*
 * nsh_md2_ioam.c - NSH iOAM functions for MD type 2
 *
 * Copyright (c) 2017 Cisco and/or its affiliates.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at:
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <vnet/vnet.h>
#include <vnet/plugin/plugin.h>
#include <nsh/nsh.h>
#include <nsh/nsh_packet.h>
#include <vnet/ip/ip.h>
#include <nsh/nsh-md2-ioam/nsh_md2_ioam.h>

#include <vlibapi/api.h>
#include <vlibmemory/api.h>

#include <vnet/fib/ip6_fib.h>
#include <vnet/fib/ip4_fib.h>
#include <vnet/fib/fib_entry.h>

/* define message structures */
#define vl_typedefs
#include <nsh/nsh.api.h>
#undef vl_typedefs

/* define generated endian-swappers */
#define vl_endianfun
#include <nsh/nsh.api.h>
#undef vl_endianfun

nsh_md2_ioam_main_t nsh_md2_ioam_main;

static void
nsh_md2_ioam_set_clear_output_feature_on_intf (vlib_main_t * vm,
						    u32 sw_if_index0,
						    u8 is_add)
{



  vnet_feature_enable_disable ("ip4-output",
			       "nsh-md2-ioam-encap-transit",
			       sw_if_index0, is_add,
			       0 /* void *feature_config */ ,
			       0 /* u32 n_feature_config_bytes */ );
  return;
}

void
nsh_md2_ioam_clear_output_feature_on_all_intfs (vlib_main_t * vm)
{
  vnet_sw_interface_t *si = 0;
  vnet_main_t *vnm = vnet_get_main ();
  vnet_interface_main_t *im = &vnm->interface_main;

  pool_foreach (si, im->sw_interfaces, (
					 {
					 nsh_md2_ioam_set_clear_output_feature_on_intf
					 (vm, si->sw_if_index, 0);
					 }));
  return;
}


extern fib_forward_chain_type_t
fib_entry_get_default_chain_type (const fib_entry_t * fib_entry);

int
nsh_md2_ioam_enable_disable_for_dest (vlib_main_t * vm,
					   ip46_address_t dst_addr,
					   u32 outer_fib_index,
					   u8 is_ipv4, u8 is_add)
{
  nsh_md2_ioam_main_t *hm = &nsh_md2_ioam_main;
  u32 fib_index0 = 0;

  fib_node_index_t fei = ~0;
  u32 *sw_if_index0 = NULL;
#if 0
  fib_entry_t *fib_entry;
  u32 adj_index0;
  ip_adjacency_t *adj0;
  load_balance_t *lb_m, *lb_b;
  const dpo_id_t *dpo0, *dpo1;
  u32 i, j, k;
#endif
  u32 *intf_list = NULL;
  fib_prefix_t fib_prefix;

  if (is_ipv4)
    {
      memset (&fib_prefix, 0, sizeof (fib_prefix_t));
      fib_prefix.fp_len = 32;
      fib_prefix.fp_proto = FIB_PROTOCOL_IP4;
#define  TRANSIT_UNIT_TEST_HACK 1
#ifdef TRANSIT_UNIT_TEST_HACK
      memset(&dst_addr, 0, sizeof(dst_addr));
      dst_addr.ip4.as_u32 = clib_net_to_host_u32(0x14020102);
#endif
      fib_prefix.fp_addr = dst_addr;
    }
  else
    {
      return 0;
    }

  fei = fib_table_lookup (fib_index0, &fib_prefix);
#if 0
  fib_entry = fib_entry_get (fei);


  if (!dpo_id_is_valid (&fib_entry->fe_lb))
    {
      return (-1);
    }

  lb_m = load_balance_get (fib_entry->fe_lb.dpoi_index);

  for (i = 0; i < lb_m->lb_n_buckets; i++)
    {
      dpo0 = load_balance_get_bucket_i (lb_m, i);

      if (dpo0->dpoi_type == DPO_LOAD_BALANCE ||
	  dpo0->dpoi_type == DPO_ADJACENCY)
	{
	  if (dpo0->dpoi_type == DPO_ADJACENCY)
	    {
	      k = 1;
	    }
	  else
	    {
	      lb_b = load_balance_get (dpo0->dpoi_index);
	      k = lb_b->lb_n_buckets;
	    }

	  for (j = 0; j < k; j++)
	    {
	      if (dpo0->dpoi_type == DPO_ADJACENCY)
		{
		  dpo1 = dpo0;
		}
	      else
		{
		  dpo1 = load_balance_get_bucket_i (lb_b, j);
		}

	      if (dpo1->dpoi_type == DPO_ADJACENCY)
		{
		  adj_index0 = dpo1->dpoi_index;

		  if (ADJ_INDEX_INVALID == adj_index0)
		    {
		      continue;
		    }

		  adj0 =
		    ip_get_adjacency (&(ip4_main.lookup_main), adj_index0);
		  sw_if_index0 = adj0->rewrite_header.sw_if_index;

		  if (~0 == sw_if_index0)
		    {
		      continue;
		    }


		  if (is_add)
		    {
		      vnet_feature_enable_disable ("ip4-output",
						   "nsh-md2-ioam-encap-transit",
						   sw_if_index0, is_add, 0,
						   /* void *feature_config */
						   0	/* u32 n_feature_config_bytes */
			);

		      vec_validate_init_empty (hm->bool_ref_by_sw_if_index,
					       sw_if_index0, ~0);
		      hm->bool_ref_by_sw_if_index[sw_if_index0] = 1;
		    }
		  else
		    {
		      hm->bool_ref_by_sw_if_index[sw_if_index0] = ~0;
		    }
		}
	    }
	}
    }
#else

u32 fib_path_get_resolving_interface (fib_node_index_t path_index);
    vec_add1(intf_list, fib_path_get_resolving_interface(fei));
    vec_foreach(sw_if_index0, intf_list)
    if (is_add)
      {
        vnet_feature_enable_disable ("ip4-output",
                         "nsh-md2-ioam-encap-transit",
                         *sw_if_index0, is_add, 0,
                         /* void *feature_config */
                         0    /* u32 n_feature_config_bytes */
          );

        vec_validate_init_empty (hm->bool_ref_by_sw_if_index,
                         *sw_if_index0, ~0);
        hm->bool_ref_by_sw_if_index[*sw_if_index0] = 1;
      }
    else
      {
        hm->bool_ref_by_sw_if_index[*sw_if_index0] = ~0;
      }

#endif

  if (is_ipv4)
    {
      uword *t = NULL;
      nsh_md2_ioam_dest_tunnels_t *t1;
      fib_prefix_t key4, *key4_copy;
      hash_pair_t *hp;
      memset (&key4, 0, sizeof (key4));
      key4.fp_proto = FIB_PROTOCOL_IP4;
      key4.fp_addr.ip4.as_u32 = fib_prefix.fp_addr.ip4.as_u32;
      t = hash_get_mem (hm->dst_by_ip4, &key4);
      if (is_add)
	{
	  if (t)
	    {
	      return 0;
	    }
	  pool_get_aligned (hm->dst_tunnels, t1, CLIB_CACHE_LINE_BYTES);
	  memset (t1, 0, sizeof (*t1));
	  t1->fp_proto = FIB_PROTOCOL_IP4;
	  t1->dst_addr.ip4.as_u32 = fib_prefix.fp_addr.ip4.as_u32;
	  key4_copy = clib_mem_alloc (sizeof (*key4_copy));
          memset(key4_copy, 0, sizeof(*key4_copy));
	  clib_memcpy (key4_copy, &key4, sizeof (*key4_copy));
	  hash_set_mem (hm->dst_by_ip4, key4_copy, t1 - hm->dst_tunnels);
	  /*
	   * Attach to the FIB entry for the VxLAN-GPE destination
	   * and become its child. The dest route will invoke a callback
	   * when the fib entry changes, it can be used to
	   * re-program the output feature on the egress interface.
	   */

	  const fib_prefix_t tun_dst_pfx = {
	    .fp_len = 32,
	    .fp_proto = FIB_PROTOCOL_IP4,
	    .fp_addr = {.ip4 = t1->dst_addr.ip4,}
	  };

	  t1->fib_entry_index =
	    fib_table_entry_special_add (outer_fib_index,
					 &tun_dst_pfx,
					 FIB_SOURCE_RR,
					 FIB_ENTRY_FLAG_NONE);
	  t1->sibling_index =
	    fib_entry_child_add (t1->fib_entry_index,
				 hm->fib_entry_type, t1 - hm->dst_tunnels);
	  t1->outer_fib_index = outer_fib_index;

	}
      else
	{
	  if (!t)
	    {
	      return 0;
	    }
	  t1 = pool_elt_at_index (hm->dst_tunnels, t[0]);
	  hp = hash_get_pair (hm->dst_by_ip4, &key4);
	  key4_copy = (void *) (hp->key);
	  hash_unset_mem (hm->dst_by_ip4, &key4);
	  clib_mem_free (key4_copy);
	  pool_put (hm->dst_tunnels, t1);
	}
    }
  else
    {
      // TBD for IPv6
    }

  return 0;
}

void
nsh_md2_ioam_refresh_output_feature_on_all_dest (void)
{
  nsh_md2_ioam_main_t *hm = &nsh_md2_ioam_main;
  nsh_main_t *gm = &nsh_main;
  nsh_md2_ioam_dest_tunnels_t *t;
  u32 i;

  if (pool_elts (hm->dst_tunnels) == 0)
    return;

  nsh_md2_ioam_clear_output_feature_on_all_intfs (gm->vlib_main);
  i = vec_len (hm->bool_ref_by_sw_if_index);
  vec_free (hm->bool_ref_by_sw_if_index);
  vec_validate_init_empty (hm->bool_ref_by_sw_if_index, i, ~0);
  pool_foreach (t, hm->dst_tunnels, (
				      {
				      nsh_md2_ioam_enable_disable_for_dest
				      (gm->vlib_main,
				       t->dst_addr,
				       t->outer_fib_index,
				       (t->fp_proto == FIB_PROTOCOL_IP4), 1
				       /* is_add */
				      );
				      }
		));
  return;
}

void
nsh_md2_ioam_clear_output_feature_on_select_intfs (void)
{
  nsh_md2_ioam_main_t *hm = &nsh_md2_ioam_main;
  nsh_main_t *gm = &nsh_main;

  u32 sw_if_index0 = 0;
  for (sw_if_index0 = 0;
       sw_if_index0 < vec_len (hm->bool_ref_by_sw_if_index); sw_if_index0++)
    {
      if (hm->bool_ref_by_sw_if_index[sw_if_index0] == 0xFF)
	{
	  nsh_md2_ioam_set_clear_output_feature_on_intf
	    (gm->vlib_main, sw_if_index0, 0);
	}
    }

  return;
}




clib_error_t *
nsh_md2_ioam_enable_disable (int has_trace_option, int has_pot_option,
				  int has_ppc_option)
{
  nsh_md2_ioam_main_t *hm = &nsh_md2_ioam_main;

  hm->has_trace_option = has_trace_option;
  hm->has_pot_option = has_pot_option;
  hm->has_ppc_option = has_ppc_option;

  if (hm->has_trace_option)
    {
      nsh_md2_ioam_trace_profile_setup ();
    }
  else if (!hm->has_trace_option)
    {
      nsh_md2_ioam_trace_profile_cleanup ();
    }

  return 0;
}


int nsh_md2_ioam_disable_for_dest
  (vlib_main_t * vm, ip46_address_t dst_addr, u32 outer_fib_index,
   u8 ipv4_set)
{
  nsh_md2_ioam_dest_tunnels_t *t;
  nsh_md2_ioam_main_t *hm = &nsh_md2_ioam_main;
  nsh_main_t *gm = &nsh_main;

  nsh_md2_ioam_enable_disable_for_dest (gm->vlib_main,
					     dst_addr, outer_fib_index,
					     ipv4_set, 0);
  if (pool_elts (hm->dst_tunnels) == 0)
    {
      nsh_md2_ioam_clear_output_feature_on_select_intfs ();
      return 0;
    }

  pool_foreach (t, hm->dst_tunnels, (
				      {
				      nsh_md2_ioam_enable_disable_for_dest
				      (gm->vlib_main,
				       t->dst_addr,
				       t->outer_fib_index,
				       (t->fp_proto ==
					FIB_PROTOCOL_IP4), 1 /* is_add */ );
				      }
		));
  nsh_md2_ioam_clear_output_feature_on_select_intfs ();
  return (0);

}

static clib_error_t *nsh_md2_ioam_set_transit_rewrite_command_fn
  (vlib_main_t * vm, unformat_input_t * input, vlib_cli_command_t * cmd)
{
  nsh_main_t *gm = &nsh_main;
  ip46_address_t dst_addr;
  u8 dst_addr_set = 0;
  u8 ipv4_set = 0;
  u8 ipv6_set = 0;
  u8 disable = 0;
  clib_error_t *rv = 0;
  u32 outer_fib_index = 0;
  while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
    {
      if (unformat (input, "dst-ip %U", unformat_ip4_address, &dst_addr.ip4))
	{
	  dst_addr_set = 1;
	  ipv4_set = 1;
	}
      else
	if (unformat
	    (input, "dst-ip %U", unformat_ip6_address, &dst_addr.ip6))
	{
	  dst_addr_set = 1;
	  ipv6_set = 1;
	}
      else if (unformat (input, "outer-fib-index %d", &outer_fib_index))
	{
	}

      else if (unformat (input, "disable"))
	disable = 1;
      else
	break;
    }

  if (dst_addr_set == 0)
    return clib_error_return (0,
			      "LISP-GPE Tunnel destination address not specified");
  if (ipv4_set && ipv6_set)
    return clib_error_return (0, "both IPv4 and IPv6 addresses specified");
  if (!disable)
    {
      nsh_md2_ioam_enable_disable_for_dest (gm->vlib_main,
						 dst_addr, outer_fib_index,
						 ipv4_set, 1);
    }
  else
    {
      nsh_md2_ioam_disable_for_dest
	(vm, dst_addr, outer_fib_index, ipv4_set);
    }
  return rv;
}

/* *INDENT-OFF* */
VLIB_CLI_COMMAND (nsh_md2_ioam_set_transit_rewrite_cmd, static) = {
  .path = "set nsh-md2-ioam-transit",
  .short_help = "set nsh-ioam-lisp-gpe-transit dst-ip <dst_ip> [outer-fib-index <outer_fib_index>] [disable]",
  .function = nsh_md2_ioam_set_transit_rewrite_command_fn,
};

/**
 * Function definition to backwalk a FIB node
 */
static fib_node_back_walk_rc_t
nsh_md2_ioam_back_walk (fib_node_t * node, fib_node_back_walk_ctx_t * ctx)
{
  nsh_md2_ioam_refresh_output_feature_on_all_dest ();
  return (FIB_NODE_BACK_WALK_CONTINUE);
}

/**
 * Function definition to get a FIB node from its index
 */
static fib_node_t *
nsh_md2_ioam_fib_node_get (fib_node_index_t index)
{
  nsh_md2_ioam_main_t *hm = &nsh_md2_ioam_main;
  return (&hm->node);
}

/**
 * Function definition to inform the FIB node that its last lock has gone.
 */
static void
nsh_md2_ioam_last_lock_gone (fib_node_t * node)
{
  ASSERT (0);
}


/*
 * Virtual function table registered by MPLS GRE tunnels
 * for participation in the FIB object graph.
 */
const static fib_node_vft_t nsh_md2_ioam_vft = {
  .fnv_get = nsh_md2_ioam_fib_node_get,
  .fnv_last_lock = nsh_md2_ioam_last_lock_gone,
  .fnv_back_walk = nsh_md2_ioam_back_walk,
};

void
nsh_md2_ioam_interface_init (void)
{
  nsh_md2_ioam_main_t *hm = &nsh_md2_ioam_main;
  hm->fib_entry_type = fib_node_register_new_type (&nsh_md2_ioam_vft);
  return;
}
rator.Word */ .highlight .w { color: #f8f8f2 } /* Text.Whitespace */ .highlight .mb { color: #ae81ff } /* Literal.Number.Bin */ .highlight .mf { color: #ae81ff } /* Literal.Number.Float */ .highlight .mh { color: #ae81ff } /* Literal.Number.Hex */ .highlight .mi { color: #ae81ff } /* Literal.Number.Integer */ .highlight .mo { color: #ae81ff } /* Literal.Number.Oct */ .highlight .sa { color: #e6db74 } /* Literal.String.Affix */ .highlight .sb { color: #e6db74 } /* Literal.String.Backtick */ .highlight .sc { color: #e6db74 } /* Literal.String.Char */ .highlight .dl { color: #e6db74 } /* Literal.String.Delimiter */ .highlight .sd { color: #e6db74 } /* Literal.String.Doc */ .highlight .s2 { color: #e6db74 } /* Literal.String.Double */ .highlight .se { color: #ae81ff } /* Literal.String.Escape */ .highlight .sh { color: #e6db74 } /* Literal.String.Heredoc */ .highlight .si { color: #e6db74 } /* Literal.String.Interpol */ .highlight .sx { color: #e6db74 } /* Literal.String.Other */ .highlight .sr { color: #e6db74 } /* Literal.String.Regex */ .highlight .s1 { color: #e6db74 } /* Literal.String.Single */ .highlight .ss { color: #e6db74 } /* Literal.String.Symbol */ .highlight .bp { color: #f8f8f2 } /* Name.Builtin.Pseudo */ .highlight .fm { color: #a6e22e } /* Name.Function.Magic */ .highlight .vc { color: #f8f8f2 } /* Name.Variable.Class */ .highlight .vg { color: #f8f8f2 } /* Name.Variable.Global */ .highlight .vi { color: #f8f8f2 } /* Name.Variable.Instance */ .highlight .vm { color: #f8f8f2 } /* Name.Variable.Magic */ .highlight .il { color: #ae81ff } /* Literal.Number.Integer.Long */ } @media (prefers-color-scheme: light) { .highlight .hll { background-color: #ffffcc } .highlight .c { color: #888888 } /* Comment */ .highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */ .highlight .k { color: #008800; font-weight: bold } /* Keyword */ .highlight .ch { color: #888888 } /* Comment.Hashbang */ .highlight .cm { color: #888888 } /* Comment.Multiline */ .highlight .cp { color: #cc0000; font-weight: bold } /* Comment.Preproc */ .highlight .cpf { color: #888888 } /* Comment.PreprocFile */ .highlight .c1 { color: #888888 } /* Comment.Single */ .highlight .cs { color: #cc0000; font-weight: bold; background-color: #fff0f0 } /* Comment.Special */ .highlight .gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */ .highlight .ge { font-style: italic } /* Generic.Emph */ .highlight .gr { color: #aa0000 } /* Generic.Error */ .highlight .gh { color: #333333 } /* Generic.Heading */ .highlight .gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */ .highlight .go { color: #888888 } /* Generic.Output */ .highlight .gp { color: #555555 } /* Generic.Prompt */ .highlight .gs { font-weight: bold } /* Generic.Strong */ .highlight .gu { color: #666666 } /* Generic.Subheading */ .highlight .gt { color: #aa0000 } /* Generic.Traceback */ .highlight .kc { color: #008800; font-weight: bold } /* Keyword.Constant */ .highlight .kd { color: #008800; font-weight: bold } /* Keyword.Declaration */ .highlight .kn { color: #008800; font-weight: bold } /* Keyword.Namespace */ .highlight .kp { color: #008800 } /* Keyword.Pseudo */ .highlight .kr { color: #008800; font-weight: bold } /* Keyword.Reserved */ .highlight .kt { color: #888888; font-weight: bold } /* Keyword.Type */ .highlight .m { color: #0000DD; font-weight: bold } /* Literal.Number */ .highlight .s { color: #dd2200; background-color: #fff0f0 } /* Literal.String */ .highlight .na { color: #336699 } /* Name.Attribute */ .highlight .nb { color: #003388 } /* Name.Builtin */ .highlight .nc { color: #bb0066; font-weight: bold } /* Name.Class */ .highlight .no { color: #003366; font-weight: bold } /* Name.Constant */ .highlight .nd { color: #555555 } /* Name.Decorator */ .highlight .ne { color: #bb0066; font-weight: bold } /* Name.Exception */ .highlight .nf { color: #0066bb; font-weight: bold } /* Name.Function */ .highlight .nl { color: #336699; font-style: italic } /* Name.Label */ .highlight .nn { color: #bb0066; font-weight: bold } /* Name.Namespace */ .highlight .py { color: #336699; font-weight: bold } /* Name.Property */ .highlight .nt { color: #bb0066; font-weight: bold } /* Name.Tag */ .highlight .nv { color: #336699 } /* Name.Variable */ .highlight .ow { color: #008800 } /* Operator.Word */ .highlight .w { color: #bbbbbb } /* Text.Whitespace */ .highlight .mb { color: #0000DD; font-weight: bold } /* Literal.Number.Bin */ .highlight .mf { color: #0000DD; font-weight: bold } /* Literal.Number.Float */ .highlight .mh { color: #0000DD; font-weight: bold } /* Literal.Number.Hex */ .highlight .mi { color: #0000DD; font-weight: bold } /* Literal.Number.Integer */ .highlight .mo { color: #0000DD; font-weight: bold } /* Literal.Number.Oct */ .highlight .sa { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Affix */ .highlight .sb { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Backtick */ .highlight .sc { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Char */ .highlight .dl { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Delimiter */ .highlight .sd { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Doc */ .highlight .s2 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Double */ .highlight .se { color: #0044dd; background-color: #fff0f0 } /* Literal.String.Escape */ .highlight .sh { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Heredoc */ .highlight .si { color: #3333bb; background-color: #fff0f0 } /* Literal.String.Interpol */ .highlight .sx { color: #22bb22; background-color: #f0fff0 } /* Literal.String.Other */ .highlight .sr { color: #008800; background-color: #fff0ff } /* Literal.String.Regex */ .highlight .s1 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Single */ .highlight .ss { color: #aa6600; background-color: #fff0f0 } /* Literal.String.Symbol */ .highlight .bp { color: #003388 } /* Name.Builtin.Pseudo */ .highlight .fm { color: #0066bb; font-weight: bold } /* Name.Function.Magic */ .highlight .vc { color: #336699 } /* Name.Variable.Class */ .highlight .vg { color: #dd7700 } /* Name.Variable.Global */ .highlight .vi { color: #3333bb } /* Name.Variable.Instance */ .highlight .vm { color: #336699 } /* Name.Variable.Magic */ .highlight .il { color: #0000DD; font-weight: bold } /* Literal.Number.Integer.Long */ }
/*
 * Copyright (c) 2020 Cisco and/or its affiliates.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at:
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <vnet/fib/fib_table.h>

#include <nat/lib/log.h>
#include <nat/lib/nat_inlines.h>
#include <nat/lib/ipfix_logging.h>

#include <nat/nat44-ei/nat44_ei.h>
#include <nat/nat44-ei/nat44_ei_ha.h>

u8 *
format_nat44_ei_session (u8 *s, va_list *args)
{
  nat44_ei_main_per_thread_data_t *tnm =
    va_arg (*args, nat44_ei_main_per_thread_data_t *);
  nat44_ei_session_t *sess = va_arg (*args, nat44_ei_session_t *);

  if (nat44_ei_is_unk_proto_session (sess))
    {
      s =
	format (s, "  i2o %U proto %u fib %u\n", format_ip4_address,
		&sess->in2out.addr, sess->in2out.port, sess->in2out.fib_index);
      s =
	format (s, "  o2i %U proto %u fib %u\n", format_ip4_address,
		&sess->out2in.addr, sess->out2in.port, sess->out2in.fib_index);
    }
  else
    {
      s = format (s, "  i2o %U proto %U port %d fib %d\n", format_ip4_address,
		  &sess->in2out.addr, format_nat_protocol, sess->nat_proto,
		  clib_net_to_host_u16 (sess->in2out.port),
		  sess->in2out.fib_index);
      s = format (s, "  o2i %U proto %U port %d fib %d\n", format_ip4_address,
		  &sess->out2in.addr, format_nat_protocol, sess->nat_proto,
		  clib_net_to_host_u16 (sess->out2in.port),
		  sess->out2in.fib_index);
    }

  s = format (s, "       index %llu\n", sess - tnm->sessions);
  s = format (s, "       last heard %.2f\n", sess->last_heard);
  s = format (s, "       total pkts %d, total bytes %lld\n", sess->total_pkts,
	      sess->total_bytes);
  if (nat44_ei_is_session_static (sess))
    s = format (s, "       static translation\n");
  else
    s = format (s, "       dynamic translation\n");

  return s;
}

u8 *
format_nat44_ei_user (u8 *s, va_list *args)
{
  nat44_ei_main_per_thread_data_t *tnm =
    va_arg (*args, nat44_ei_main_per_thread_data_t *);
  nat44_ei_user_t *u = va_arg (*args, nat44_ei_user_t *);
  int verbose = va_arg (*args, int);
  dlist_elt_t *head, *elt;
  u32 elt_index, head_index;
  u32 session_index;
  nat44_ei_session_t *sess;

  s = format (s, "%U: %d dynamic translations, %d static translations\n",
	      format_ip4_address, &u->addr, u->nsessions, u->nstaticsessions);

  if (verbose == 0)
    return s;

  if (u->nsessions || u->nstaticsessions)
    {
      head_index = u->sessions_per_user_list_head_index;
      head = pool_elt_at_index (tnm->list_pool, head_index);

      elt_index = head->next;
      elt = pool_elt_at_index (tnm->list_pool, elt_index);
      session_index = elt->value;

      while (session_index != ~0)
	{
	  sess = pool_elt_at_index (tnm->sessions, session_index);

	  s = format (s, "  %U\n", format_nat44_ei_session, tnm, sess);

	  elt_index = elt->next;
	  elt = pool_elt_at_index (tnm->list_pool, elt_index);
	  session_index = elt->value;
	}
    }

  return s;
}

u8 *
format_nat44_ei_static_mapping (u8 *s, va_list *args)
{
  nat44_ei_static_mapping_t *m = va_arg (*args, nat44_ei_static_mapping_t *);
  nat44_ei_lb_addr_port_t *local;

  if (nat44_ei_is_identity_static_mapping (m))
    {
      if (nat44_ei_is_addr_only_static_mapping (m))
	s = format (s, "identity mapping %U", format_ip4_address,
		    &m->local_addr);
      else
	s = format (s, "identity mapping %U %U:%d", format_nat_protocol,
		    m->proto, format_ip4_address, &m->local_addr,
		    clib_net_to_host_u16 (m->local_port));

      pool_foreach (local, m->locals)
	{
	  s = format (s, " vrf %d", local->vrf_id);
	}

      return s;
    }

  if (nat44_ei_is_addr_only_static_mapping (m))
    {
      s = format (s, "local %U external %U vrf %d", format_ip4_address,
		  &m->local_addr, format_ip4_address, &m->external_addr,
		  m->vrf_id);
    }
  else
    {
      s = format (s, "%U local %U:%d external %U:%d vrf %d",
		  format_nat_protocol, m->proto, format_ip4_address,
		  &m->local_addr, clib_net_to_host_u16 (m->local_port),
		  format_ip4_address, &m->external_addr,
		  clib_net_to_host_u16 (m->external_port), m->vrf_id);
    }
  return s;
}

u8 *
format_nat44_ei_static_map_to_resolve (u8 *s, va_list *args)
{
  nat44_ei_static_map_resolve_t *m =
    va_arg (*args, nat44_ei_static_map_resolve_t *);
  vnet_main_t *vnm = vnet_get_main ();

  if (m->addr_only)
    s =
      format (s, "local %U external %U vrf %d", format_ip4_address, &m->l_addr,
	      format_vnet_sw_if_index_name, vnm, m->sw_if_index, m->vrf_id);
  else
    s = format (s, "%U local %U:%d external %U:%d vrf %d", format_nat_protocol,
		m->proto, format_ip4_address, &m->l_addr,
		clib_net_to_host_u16 (m->l_port), format_vnet_sw_if_index_name,
		vnm, m->sw_if_index, clib_net_to_host_u16 (m->e_port),
		m->vrf_id);

  return s;
}

static clib_error_t *
nat44_ei_enable_command_fn (vlib_main_t *vm, unformat_input_t *input,
			    vlib_cli_command_t *cmd)
{
  nat44_ei_main_t *nm = &nat44_ei_main;
  unformat_input_t _line_input, *line_input = &_line_input;
  clib_error_t *error = 0;

  nat44_ei_config_t c = { 0 };
  u8 mode_set = 0;

  if (nm->enabled)
    return clib_error_return (0, "nat44 ei already enabled");

  /* Get a line of input. */
  if (!unformat_user (input, unformat_line_input, line_input))
    {
      if (nat44_ei_plugin_enable (c) != 0)
	return clib_error_return (0, "nat44 ei enable failed");
      return 0;
    }

  while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
    {
      if (!mode_set && unformat (line_input, "static-mapping-only"))
	{
	  mode_set = 1;
	  c.static_mapping_only = 1;
	  if (unformat (line_input, "connection-tracking"))
	    {
	      c.connection_tracking = 1;
	    }
	}
      else if (!mode_set && unformat (line_input, "out2in-dpo"))
	{
	  mode_set = 1;
	  c.out2in_dpo = 1;
	}
      else if (unformat (line_input, "inside-vrf %u", &c.inside_vrf))
	;
      else if (unformat (line_input, "outside-vrf %u", &c.outside_vrf))
	;
      else if (unformat (line_input, "users %u", &c.users))
	;
      else if (unformat (line_input, "sessions %u", &c.sessions))
	;
      else if (unformat (line_input, "user-sessions %u", &c.user_sessions))
	;
      else
	{
	  error = clib_error_return (0, "unknown input '%U'",
				     format_unformat_error, line_input);
	  goto done;
	}
    }

  if (!c.sessions)
    {
      error = clib_error_return (0, "number of sessions is required");
      goto done;
    }

  if (nat44_ei_plugin_enable (c) != 0)
    error = clib_error_return (0, "nat44 ei enable failed");
done:
  unformat_free (line_input);
  return error;
}

static clib_error_t *
nat44_ei_disable_command_fn (vlib_main_t *vm, unformat_input_t *input,
			     vlib_cli_command_t *cmd)
{
  nat44_ei_main_t *nm = &nat44_ei_main;
  clib_error_t *error = 0;

  if (!nm->enabled)
    return clib_error_return (0, "nat44 ei already disabled");

  if (nat44_ei_plugin_disable () != 0)
    error = clib_error_return (0, "nat44 ei disable failed");

  return error;
}

static clib_error_t *
set_workers_command_fn (vlib_main_t *vm, unformat_input_t *input,
			vlib_cli_command_t *cmd)
{
  unformat_input_t _line_input, *line_input = &_line_input;
  uword *bitmap = 0;
  int rv = 0;
  clib_error_t *error = 0;

  /* Get a line of input. */
  if (!unformat_user (input, unformat_line_input, line_input))
    return 0;

  while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
    {
      if (unformat (line_input, "%U", unformat_bitmap_list, &bitmap))
	;
      else
	{
	  error = clib_error_return (0, "unknown input '%U'",
				     format_unformat_error, line_input);
	  goto done;
	}
    }

  if (bitmap == 0)
    {
      error = clib_error_return (0, "List of workers must be specified.");
      goto done;
    }

  rv = nat44_ei_set_workers (bitmap);

  clib_bitmap_free (bitmap);

  switch (rv)
    {
    case VNET_API_ERROR_INVALID_WORKER:
      error = clib_error_return (0, "Invalid worker(s).");
      goto done;
    case VNET_API_ERROR_FEATURE_DISABLED:
      error =
	clib_error_return (0, "Supported only if 2 or more workes available.");
      goto done;
    default:
      break;
    }

done:
  unformat_free (line_input);

  return error;
}

static clib_error_t *
nat_show_workers_commnad_fn (vlib_main_t *vm, unformat_input_t *input,
			     vlib_cli_command_t *cmd)
{
  nat44_ei_main_t *nm = &nat44_ei_main;
  u32 *worker;

  if (nm->num_workers > 1)
    {
      vlib_cli_output (vm, "%d workers", vec_len (nm->workers));
      vec_foreach (worker, nm->workers)
	{
	  vlib_worker_thread_t *w =
	    vlib_worker_threads + *worker + nm->first_worker_index;
	  vlib_cli_output (vm, "  %s", w->name);
	}
    }

  return 0;
}

static clib_error_t *
nat44_ei_set_log_level_command_fn (vlib_main_t *vm, unformat_input_t *input,
				   vlib_cli_command_t *cmd)
{
  unformat_input_t _line_input, *line_input = &_line_input;
  nat44_ei_main_t *nm = &nat44_ei_main;
  u8 log_level = NAT_LOG_NONE;
  clib_error_t *error = 0;

  /* Get a line of input. */
  if (!unformat_user (input, unformat_line_input, line_input))
    return 0;

  if (!unformat (line_input, "%d", &log_level))
    {
      error = clib_error_return (0, "unknown input '%U'",
				 format_unformat_error, line_input);
      goto done;
    }
  if (log_level > NAT_LOG_DEBUG)
    {
      error = clib_error_return (0, "unknown logging level '%d'", log_level);
      goto done;
    }
  nm->log_level = log_level;

done:
  unformat_free (line_input);

  return error;
}

static clib_error_t *
nat44_ei_ipfix_logging_enable_disable_command_fn (vlib_main_t *vm,
						  unformat_input_t *input,
						  vlib_cli_command_t *cmd)
{
  unformat_input_t _line_input, *line_input = &_line_input;
  u32 domain_id = 0;
  u32 src_port = 0;
  u8 enable = 1;
  int rv = 0;
  clib_error_t *error = 0;

  /* Get a line of input. */
  if (!unformat_user (input, unformat_line_input, line_input))
    {
      rv =
	nat_ipfix_logging_enable_disable (enable, domain_id, (u16) src_port);
      if (rv)
	return clib_error_return (0, "ipfix logging enable failed");
      return 0;
    }

  while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
    {
      if (unformat (line_input, "domain %d", &domain_id))
	;
      else if (unformat (line_input, "src-port %d", &src_port))
	;
      else if (unformat (line_input, "disable"))
	enable = 0;
      else
	{
	  error = clib_error_return (0, "unknown input '%U'",
				     format_unformat_error, line_input);
	  goto done;
	}
    }

  rv = nat_ipfix_logging_enable_disable (enable, domain_id, (u16) src_port);

  if (rv)
    {
      error = clib_error_return (0, "ipfix logging enable failed");
      goto done;
    }

done:
  unformat_free (line_input);

  return error;
}

static clib_error_t *
nat44_ei_show_hash_command_fn (vlib_main_t *vm, unformat_input_t *input,
			       vlib_cli_command_t *cmd)
{
  nat44_ei_main_t *nm = &nat44_ei_main;
  nat44_ei_main_per_thread_data_t *tnm;
  int i;
  int verbose = 0;

  if (unformat (input, "detail"))
    verbose = 1;
  else if (unformat (input, "verbose"))
    verbose = 2;

  vlib_cli_output (vm, "%U", format_bihash_8_8, &nm->static_mapping_by_local,
		   verbose);
  vlib_cli_output (vm, "%U", format_bihash_8_8,
		   &nm->static_mapping_by_external, verbose);
  vec_foreach_index (i, nm->per_thread_data)
    {
      tnm = vec_elt_at_index (nm->per_thread_data, i);
      vlib_cli_output (vm, "-------- thread %d %s --------\n", i,
		       vlib_worker_threads[i].name);

      vlib_cli_output (vm, "%U", format_bihash_8_8, &nm->in2out, verbose);
      vlib_cli_output (vm, "%U", format_bihash_8_8, &nm->out2in, verbose);
      vlib_cli_output (vm, "%U", format_bihash_8_8, &tnm->user_hash, verbose);
    }

  vlib_cli_output (vm, "-------- hash table parameters --------\n");
  vlib_cli_output (vm, "translation buckets: %u", nm->translation_buckets);
  vlib_cli_output (vm, "user buckets: %u", nm->user_buckets);
  return 0;
}

static clib_error_t *
nat44_ei_set_alloc_addr_and_port_alg_command_fn (vlib_main_t *vm,
						 unformat_input_t *input,
						 vlib_cli_command_t *cmd)
{
  unformat_input_t _line_input, *line_input = &_line_input;
  clib_error_t *error = 0;
  u32 psid, psid_offset, psid_length, port_start, port_end;

  /* Get a line of input. */
  if (!unformat_user (input, unformat_line_input, line_input))
    return 0;

  while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
    {
      if (unformat (line_input, "default"))
	nat44_ei_set_alloc_default ();
      else if (unformat (line_input,
			 "map-e psid %d psid-offset %d psid-len %d", &psid,
			 &psid_offset, &psid_length))
	nat44_ei_set_alloc_mape ((u16) psid, (u16) psid_offset,
				 (u16) psid_length);
      else if (unformat (line_input, "port-range %d - %d", &port_start,
			 &port_end))
	{
	  if (port_end <= port_start)
	    {
	      error = clib_error_return (
		0, "The end-port must be greater than start-port");
	      goto done;
	    }
	  nat44_ei_set_alloc_range ((u16) port_start, (u16) port_end);
	}
      else
	{
	  error = clib_error_return (0, "unknown input '%U'",
				     format_unformat_error, line_input);
	  goto done;
	}
    }

done:
  unformat_free (line_input);

  return error;
};

u8 *
format_nat44_ei_addr_and_port_alloc_alg (u8 *s, va_list *args)
{
  u32 i = va_arg (*args, u32);
  u8 *t = 0;

  switch (i)
    {
#define _(v, N, s)                                                            \
  case NAT44_EI_ADDR_AND_PORT_ALLOC_ALG_##N:                                  \
    t = (u8 *) s;                                                             \
    break;
      foreach_nat44_ei_addr_and_port_alloc_alg
#undef _
	default : s = format (s, "unknown");
      return s;
    }
  s = format (s, "%s", t);
  return s;
}

static clib_error_t *
nat44_ei_show_alloc_addr_and_port_alg_command_fn (vlib_main_t *vm,
						  unformat_input_t *input,
						  vlib_cli_command_t *cmd)
{
  nat44_ei_main_t *nm = &nat44_ei_main;

  vlib_cli_output (vm, "NAT address and port: %U",
		   format_nat44_ei_addr_and_port_alloc_alg,
		   nm->addr_and_port_alloc_alg);
  switch (nm->addr_and_port_alloc_alg)
    {
    case NAT44_EI_ADDR_AND_PORT_ALLOC_ALG_MAPE:
      vlib_cli_output (vm, "  psid %d psid-offset %d psid-len %d", nm->psid,
		       nm->psid_offset, nm->psid_length);
      break;
    case NAT44_EI_ADDR_AND_PORT_ALLOC_ALG_RANGE:
      vlib_cli_output (vm, "  start-port %d end-port %d", nm->start_port,
		       nm->end_port);
      break;
    default:
      break;
    }

  return 0;
}

static clib_error_t *
nat_set_mss_clamping_command_fn (vlib_main_t *vm, unformat_input_t *input,
				 vlib_cli_command_t *cmd)
{
  unformat_input_t _line_input, *line_input = &_line_input;
  nat44_ei_main_t *nm = &nat44_ei_main;
  clib_error_t *error = 0;
  u32 mss;

  /* Get a line of input. */
  if (!unformat_user (input, unformat_line_input, line_input))
    return 0;

  while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
    {
      if (unformat (line_input, "disable"))
	nm->mss_clamping = 0;
      else if (unformat (line_input, "%d", &mss))
	nm->mss_clamping = (u16) mss;
      else
	{
	  error = clib_error_return (0, "unknown input '%U'",
				     format_unformat_error, line_input);
	  goto done;
	}
    }

done:
  unformat_free (line_input);

  return error;
}

static clib_error_t *
nat_show_mss_clamping_command_fn (vlib_main_t *vm, unformat_input_t *input,
				  vlib_cli_command_t *cmd)
{
  nat44_ei_main_t *nm = &nat44_ei_main;

  if (nm->mss_clamping)
    vlib_cli_output (vm, "mss-clamping %d", nm->mss_clamping);
  else
    vlib_cli_output (vm, "mss-clamping disabled");

  return 0;
}

static clib_error_t *
nat_ha_failover_command_fn (vlib_main_t *vm, unformat_input_t *input,
			    vlib_cli_command_t *cmd)
{
  unformat_input_t _line_input, *line_input = &_line_input;
  ip4_address_t addr;
  u32 port, session_refresh_interval = 10;
  int rv;
  clib_error_t *error = 0;

  /* Get a line of input. */
  if (!unformat_user (input, unformat_line_input, line_input))
    return 0;

  while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
    {
      if (unformat (line_input, "%U:%u", unformat_ip4_address, &addr, &port))
	;
      else if (unformat (line_input, "refresh-interval %u",
			 &session_refresh_interval))
	;
      else
	{
	  error = clib_error_return (0, "unknown input '%U'",
				     format_unformat_error, line_input);
	  goto done;
	}
    }

  rv = nat_ha_set_failover (&addr, (u16) port, session_refresh_interval);
  if (rv)
    error = clib_error_return (0, "set HA failover failed");

done:
  unformat_free (line_input);

  return error;
}

static clib_error_t *
nat_ha_listener_command_fn (vlib_main_t *vm, unformat_input_t *input,
			    vlib_cli_command_t *cmd)
{
  unformat_input_t _line_input, *line_input = &_line_input;
  ip4_address_t addr;
  u32 port, path_mtu = 512;
  int rv;
  clib_error_t *error = 0;

  /* Get a line of input. */
  if (!unformat_user (input, unformat_line_input, line_input))
    return 0;

  while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
    {
      if (unformat (line_input, "%U:%u", unformat_ip4_address, &addr, &port))
	;
      else if (unformat (line_input, "path-mtu %u", &path_mtu))
	;
      else
	{
	  error = clib_error_return (0, "unknown input '%U'",
				     format_unformat_error, line_input);
	  goto done;
	}
    }

  rv = nat_ha_set_listener (&addr, (u16) port, path_mtu);
  if (rv)
    error = clib_error_return (0, "set HA listener failed");

done:
  unformat_free (line_input);

  return error;
}

static clib_error_t *
nat_show_ha_command_fn (vlib_main_t *vm, unformat_input_t *input,
			vlib_cli_command_t *cmd)
{
  ip4_address_t addr;
  u16 port;
  u32 path_mtu, session_refresh_interval, resync_ack_missed;
  u8 in_resync;

  nat_ha_get_listener (&addr, &port, &path_mtu);
  if (!port)
    {
      vlib_cli_output (vm, "NAT HA disabled\n");
      return 0;
    }

  vlib_cli_output (vm, "LISTENER:\n");
  vlib_cli_output (vm, "  %U:%u path-mtu %u\n", format_ip4_address, &addr,
		   port, path_mtu);

  nat_ha_get_failover (&addr, &port, &session_refresh_interval);
  vlib_cli_output (vm, "FAILOVER:\n");
  if (port)
    vlib_cli_output (vm, "  %U:%u refresh-interval %usec\n",
		     format_ip4_address, &addr, port,
		     session_refresh_interval);
  else
    vlib_cli_output (vm, "  NA\n");

  nat_ha_get_resync_status (&in_resync, &resync_ack_missed);
  vlib_cli_output (vm, "RESYNC:\n");
  if (in_resync)
    vlib_cli_output (vm, "  in progress\n");
  else
    vlib_cli_output (vm, "  completed (%d ACK missed)\n", resync_ack_missed);

  return 0;
}

static clib_error_t *
nat_ha_flush_command_fn (vlib_main_t *vm, unformat_input_t *input,
			 vlib_cli_command_t *cmd)
{
  nat_ha_flush (0);
  return 0;
}

static clib_error_t *
nat_ha_resync_command_fn (vlib_main_t *vm, unformat_input_t *input,
			  vlib_cli_command_t *cmd)
{
  clib_error_t *error = 0;

  if (nat_ha_resync (0, 0, 0))
    error = clib_error_return (0, "NAT HA resync already running");

  return error;
}

static clib_error_t *
add_address_command_fn (vlib_main_t *vm, unformat_input_t *input,
			vlib_cli_command_t *cmd)
{
  unformat_input_t _line_input, *line_input = &_line_input;
  nat44_ei_main_t *nm = &nat44_ei_main;
  ip4_address_t start_addr, end_addr, this_addr;
  u32 start_host_order, end_host_order;
  u32 vrf_id = ~0;
  int i, count;
  int is_add = 1;
  int rv = 0;
  clib_error_t *error = 0;

  /* Get a line of input. */
  if (!unformat_user (input, unformat_line_input, line_input))
    return 0;

  while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
    {
      if (unformat (line_input, "%U - %U", unformat_ip4_address, &start_addr,
		    unformat_ip4_address, &end_addr))
	;
      else if (unformat (line_input, "tenant-vrf %u", &vrf_id))
	;
      else if (unformat (line_input, "%U", unformat_ip4_address, &start_addr))
	end_addr = start_addr;
      else if (unformat (line_input, "del"))
	is_add = 0;
      else
	{
	  error = clib_error_return (0, "unknown input '%U'",
				     format_unformat_error, line_input);
	  goto done;
	}
    }

  if (nm->static_mapping_only)
    {
      error = clib_error_return (0, "static mapping only mode");
      goto done;
    }

  start_host_order = clib_host_to_net_u32 (start_addr.as_u32);
  end_host_order = clib_host_to_net_u32 (end_addr.as_u32);

  if (end_host_order < start_host_order)
    {
      error = clib_error_return (0, "end address less than start address");
      goto done;
    }

  count = (end_host_order - start_host_order) + 1;

  if (count > 1024)
    nat44_ei_log_info ("%U - %U, %d addresses...", format_ip4_address,
		       &start_addr, format_ip4_address, &end_addr, count);

  this_addr = start_addr;

  for (i = 0; i < count; i++)
    {
      if (is_add)
	rv = nat44_ei_add_address (nm, &this_addr, vrf_id);
      else
	rv = nat44_ei_del_address (nm, this_addr, 0);

      switch (rv)
	{
	case VNET_API_ERROR_VALUE_EXIST:
	  error = clib_error_return (0, "NAT address already in use.");
	  goto done;
	case VNET_API_ERROR_NO_SUCH_ENTRY:
	  error = clib_error_return (0, "NAT address not exist.");
	  goto done;
	case VNET_API_ERROR_UNSPECIFIED:
	  error = clib_error_return (0, "NAT address used in static mapping.");
	  goto done;
	case VNET_API_ERROR_FEATURE_DISABLED:
	  goto done;
	default:
	  break;
	}

      if (nm->out2in_dpo)
	nat44_ei_add_del_address_dpo (this_addr, is_add);

      increment_v4_address (&this_addr);
    }

done:
  unformat_free (line_input);

  return error;
}

static clib_error_t *
nat44_ei_show_addresses_command_fn (vlib_main_t *vm, unformat_input_t *input,
				    vlib_cli_command_t *cmd)
{
  nat44_ei_main_t *nm = &nat44_ei_main;
  nat44_ei_address_t *ap;

  vlib_cli_output (vm, "NAT44 pool addresses:");
  vec_foreach (ap, nm->addresses)
    {
      vlib_cli_output (vm, "%U", format_ip4_address, &ap->addr);
      if (ap->fib_index != ~0)
	vlib_cli_output (
	  vm, "  tenant VRF: %u",
	  fib_table_get (ap->fib_index, FIB_PROTOCOL_IP4)->ft_table_id);
      else
	vlib_cli_output (vm, "  tenant VRF independent");
#define _(N, i, n, s)                                                         \
  vlib_cli_output (vm, "  %d busy %s ports", ap->busy_##n##_ports, s);
      foreach_nat_protocol
#undef _
    }
  return 0;
}

static clib_error_t *
nat44_ei_feature_command_fn (vlib_main_t *vm, unformat_input_t *input,
			     vlib_cli_command_t *cmd)
{
  unformat_input_t _line_input, *line_input = &_line_input;
  vnet_main_t *vnm = vnet_get_main ();
  clib_error_t *error = 0;
  u32 sw_if_index;
  u32 *inside_sw_if_indices = 0;
  u32 *outside_sw_if_indices = 0;
  u8 is_output_feature = 0;
  int is_del = 0;
  int i;

  sw_if_index = ~0;

  /* Get a line of input. */
  if (!unformat_user (input, unformat_line_input, line_input))
    return 0;

  while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
    {
      if (unformat (line_input, "in %U", unformat_vnet_sw_interface, vnm,
		    &sw_if_index))
	vec_add1 (inside_sw_if_indices, sw_if_index);
      else if (unformat (line_input, "out %U", unformat_vnet_sw_interface, vnm,
			 &sw_if_index))
	vec_add1 (outside_sw_if_indices, sw_if_index);
      else if (unformat (line_input, "output-feature"))
	is_output_feature = 1;
      else if (unformat (line_input, "del"))
	is_del = 1;
      else
	{
	  error = clib_error_return (0, "unknown input '%U'",
				     format_unformat_error, line_input);
	  goto done;
	}
    }

  if (vec_len (inside_sw_if_indices))
    {
      for (i = 0; i < vec_len (inside_sw_if_indices); i++)
	{
	  sw_if_index = inside_sw_if_indices[i];
	  if (is_output_feature)
	    {
	      if (nat44_ei_interface_add_del_output_feature (sw_if_index, 1,
							     is_del))
		{
		  error = clib_error_return (
		    0, "%s %U failed", is_del ? "del" : "add",
		    format_vnet_sw_if_index_name, vnm, sw_if_index);
		  goto done;
		}
	    }
	  else
	    {
	      if (nat44_ei_interface_add_del (sw_if_index, 1, is_del))
		{
		  error = clib_error_return (
		    0, "%s %U failed", is_del ? "del" : "add",
		    format_vnet_sw_if_index_name, vnm, sw_if_index);
		  goto done;
		}
	    }
	}
    }

  if (vec_len (outside_sw_if_indices))
    {
      for (i = 0; i < vec_len (outside_sw_if_indices); i++)
	{
	  sw_if_index = outside_sw_if_indices[i];
	  if (is_output_feature)
	    {
	      if (nat44_ei_interface_add_del_output_feature (sw_if_index, 0,
							     is_del))
		{
		  error = clib_error_return (
		    0, "%s %U failed", is_del ? "del" : "add",
		    format_vnet_sw_if_index_name, vnm, sw_if_index);
		  goto done;
		}
	    }
	  else
	    {
	      if (nat44_ei_interface_add_del (sw_if_index, 0, is_del))
		{
		  error = clib_error_return (
		    0, "%s %U failed", is_del ? "del" : "add",
		    format_vnet_sw_if_index_name, vnm, sw_if_index);
		  goto done;
		}
	    }
	}
    }

done:
  unformat_free (line_input);
  vec_free (inside_sw_if_indices);
  vec_free (outside_sw_if_indices);

  return error;
}

static clib_error_t *
nat44_ei_show_interfaces_command_fn (vlib_main_t *vm, unformat_input_t *input,
				     vlib_cli_command_t *cmd)
{
  nat44_ei_main_t *nm = &nat44_ei_main;
  nat44_ei_interface_t *i;
  vnet_main_t *vnm = vnet_get_main ();

  vlib_cli_output (vm, "NAT44 interfaces:");
  pool_foreach (i, nm->interfaces)
    {
      vlib_cli_output (vm, " %U %s", format_vnet_sw_if_index_name, vnm,
		       i->sw_if_index,
		       (nat44_ei_interface_is_inside (i) &&
			nat44_ei_interface_is_outside (i)) ?
			 "in out" :
			 (nat44_ei_interface_is_inside (i) ? "in" : "out"));
    }

  pool_foreach (i, nm->output_feature_interfaces)
    {
      vlib_cli_output (vm, " %U output-feature %s",
		       format_vnet_sw_if_index_name, vnm, i->sw_if_index,
		       (nat44_ei_interface_is_inside (i) &&
			nat44_ei_interface_is_outside (i)) ?
			 "in out" :
			 (nat44_ei_interface_is_inside (i) ? "in" : "out"));
    }

  return 0;
}

static clib_error_t *
add_static_mapping_command_fn (vlib_main_t *vm, unformat_input_t *input,
			       vlib_cli_command_t *cmd)
{
  unformat_input_t _line_input, *line_input = &_line_input;
  clib_error_t *error = 0;
  ip4_address_t l_addr, e_addr;
  u32 l_port = 0, e_port = 0, vrf_id = ~0;
  int is_add = 1, addr_only = 1, rv;
  u32 sw_if_index = ~0;
  vnet_main_t *vnm = vnet_get_main ();
  nat_protocol_t proto = NAT_PROTOCOL_OTHER;
  u8 proto_set = 0;

  /* Get a line of input. */
  if (!unformat_user (input, unformat_line_input, line_input))
    return 0;

  while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
    {
      if (unformat (line_input, "local %U %u", unformat_ip4_address, &l_addr,
		    &l_port))
	addr_only = 0;
      else if (unformat (line_input, "local %U", unformat_ip4_address,
			 &l_addr))
	;
      else if (unformat (line_input, "external %U %u", unformat_ip4_address,
			 &e_addr, &e_port))
	addr_only = 0;
      else if (unformat (line_input, "external %U", unformat_ip4_address,
			 &e_addr))
	;
      else if (unformat (line_input, "external %U %u",
			 unformat_vnet_sw_interface, vnm, &sw_if_index,
			 &e_port))
	addr_only = 0;
      else if (unformat (line_input, "external %U", unformat_vnet_sw_interface,
			 vnm, &sw_if_index))
	;
      else if (unformat (line_input, "vrf %u", &vrf_id))
	;
      else if (unformat (line_input, "%U", unformat_nat_protocol, &proto))
	proto_set = 1;
      else if (unformat (line_input, "del"))
	is_add = 0;
      else
	{
	  error = clib_error_return (0, "unknown input: '%U'",
				     format_unformat_error, line_input);
	  goto done;
	}
    }

  if (addr_only)
    {
      if (proto_set)
	{
	  error = clib_error_return (
	    0, "address only mapping doesn't support protocol");
	  goto done;
	}
    }
  else if (!proto_set)
    {
      error = clib_error_return (0, "protocol is required");
      goto done;
    }

  rv = nat44_ei_add_del_static_mapping (
    l_addr, e_addr, clib_host_to_net_u16 (l_port),
    clib_host_to_net_u16 (e_port), proto, sw_if_index, vrf_id, addr_only, 0, 0,
    is_add);

  switch (rv)
    {
    case VNET_API_ERROR_INVALID_VALUE:
      error = clib_error_return (0, "External port already in use.");
      goto done;
    case VNET_API_ERROR_NO_SUCH_ENTRY:
      if (is_add)
	error = clib_error_return (0, "External address must be allocated.");
      else
	error = clib_error_return (0, "Mapping not exist.");
      goto done;
    case VNET_API_ERROR_NO_SUCH_FIB:
      error = clib_error_return (0, "No such VRF id.");
      goto done;
    case VNET_API_ERROR_VALUE_EXIST:
      error = clib_error_return (0, "Mapping already exist.");
      goto done;
    case VNET_API_ERROR_FEATURE_DISABLED:
      goto done;
    default:
      break;
    }

done:
  unformat_free (line_input);

  return error;
}

static clib_error_t *
add_identity_mapping_command_fn (vlib_main_t *vm, unformat_input_t *input,
				 vlib_cli_command_t *cmd)
{
  unformat_input_t _line_input, *line_input = &_line_input;
  clib_error_t *error = 0;
  u32 port = 0, vrf_id = ~0;
  ip4_address_t addr;
  int is_add = 1;
  int addr_only = 1;
  u32 sw_if_index = ~0;
  vnet_main_t *vnm = vnet_get_main ();
  int rv;
  nat_protocol_t proto;

  addr.as_u32 = 0;

  /* Get a line of input. */
  if (!unformat_user (input, unformat_line_input, line_input))
    return 0;

  while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
    {
      if (unformat (line_input, "%U", unformat_ip4_address, &addr))
	;
      else if (unformat (line_input, "external %U", unformat_vnet_sw_interface,
			 vnm, &sw_if_index))
	;
      else if (unformat (line_input, "vrf %u", &vrf_id))
	;
      else if (unformat (line_input, "%U %u", unformat_nat_protocol, &proto,
			 &port))
	addr_only = 0;
      else if (unformat (line_input, "del"))
	is_add = 0;
      else
	{
	  error = clib_error_return (0, "unknown input: '%U'",
				     format_unformat_error, line_input);
	  goto done;
	}
    }

  rv = nat44_ei_add_del_static_mapping (
    addr, addr, clib_host_to_net_u16 (port), clib_host_to_net_u16 (port),
    proto, sw_if_index, vrf_id, addr_only, 1, 0, is_add);

  switch (rv)
    {
    case VNET_API_ERROR_INVALID_VALUE:
      error = clib_error_return (0, "External port already in use.");
      goto done;
    case VNET_API_ERROR_NO_SUCH_ENTRY:
      if (is_add)
	error = clib_error_return (0, "External address must be allocated.");
      else
	error = clib_error_return (0, "Mapping not exist.");
      goto done;
    case VNET_API_ERROR_NO_SUCH_FIB:
      error = clib_error_return (0, "No such VRF id.");
      goto done;
    case VNET_API_ERROR_VALUE_EXIST:
      error = clib_error_return (0, "Mapping already exist.");
      goto done;
    default:
      break;
    }

done:
  unformat_free (line_input);

  return error;
}

static clib_error_t *
nat44_ei_show_static_mappings_command_fn (vlib_main_t *vm,
					  unformat_input_t *input,
					  vlib_cli_command_t *cmd)
{
  nat44_ei_main_t *nm = &nat44_ei_main;
  nat44_ei_static_mapping_t *m;
  nat44_ei_static_map_resolve_t *rp;

  vlib_cli_output (vm, "NAT44 static mappings:");
  pool_foreach (m, nm->static_mappings)
    {
      vlib_cli_output (vm, " %U", format_nat44_ei_static_mapping, m);
    }
  vec_foreach (rp, nm->to_resolve)
    vlib_cli_output (vm, " %U", format_nat44_ei_static_map_to_resolve, rp);

  return 0;
}

static clib_error_t *
nat44_ei_add_interface_address_command_fn (vlib_main_t *vm,
					   unformat_input_t *input,
					   vlib_cli_command_t *cmd)
{
  nat44_ei_main_t *nm = &nat44_ei_main;
  unformat_input_t _line_input, *line_input = &_line_input;
  u32 sw_if_index;
  int rv;
  int is_del = 0;
  clib_error_t *error = 0;

  /* Get a line of input. */
  if (!unformat_user (input, unformat_line_input, line_input))
    return 0;

  while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
    {
      if (unformat (line_input, "%U", unformat_vnet_sw_interface,
		    nm->vnet_main, &sw_if_index))
	;
      else if (unformat (line_input, "del"))
	is_del = 1;
      else
	{
	  error = clib_error_return (0, "unknown input '%U'",
				     format_unformat_error, line_input);
	  goto done;
	}
    }

  rv = nat44_ei_add_interface_address (nm, sw_if_index, is_del);

  switch (rv)
    {
    case 0:
      break;

    default:
      error = clib_error_return (
	0, "nat44_ei_add_interface_address returned %d", rv);
      goto done;
    }

done:
  unformat_free (line_input);

  return error;
}

static clib_error_t *
nat44_ei_show_interface_address_command_fn (vlib_main_t *vm,
					    unformat_input_t *input,
					    vlib_cli_command_t *cmd)
{
  nat44_ei_main_t *nm = &nat44_ei_main;
  vnet_main_t *vnm = vnet_get_main ();
  u32 *sw_if_index;

  vlib_cli_output (vm, "NAT44 pool address interfaces:");
  vec_foreach (sw_if_index, nm->auto_add_sw_if_indices)
    {
      vlib_cli_output (vm, " %U", format_vnet_sw_if_index_name, vnm,
		       *sw_if_index);
    }
  return 0;
}

static clib_error_t *
nat44_ei_show_sessions_command_fn (vlib_main_t *vm, unformat_input_t *input,
				   vlib_cli_command_t *cmd)
{
  unformat_input_t _line_input, *line_input = &_line_input;
  clib_error_t *error = 0;

  nat44_ei_main_per_thread_data_t *tnm;
  nat44_ei_main_t *nm = &nat44_ei_main;

  int detail = 0;
  int i = 0;

  if (!unformat_user (input, unformat_line_input, line_input))
    goto print;

  while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
    {
      if (unformat (line_input, "detail"))
	detail = 1;
      else
	{
	  error = clib_error_return (0, "unknown input '%U'",
				     format_unformat_error, line_input);
	  break;
	}
    }
  unformat_free (line_input);

print:
  vlib_cli_output (vm, "NAT44 sessions:");

  vec_foreach_index (i, nm->per_thread_data)
    {
      tnm = vec_elt_at_index (nm->per_thread_data, i);

      vlib_cli_output (vm, "-------- thread %d %s: %d sessions --------\n", i,
		       vlib_worker_threads[i].name, pool_elts (tnm->sessions));

      nat44_ei_user_t *u;
      pool_foreach (u, tnm->users)
	{
	  vlib_cli_output (vm, "  %U", format_nat44_ei_user, tnm, u, detail);
	}
    }
  return error;
}

static clib_error_t *
nat44_ei_del_user_command_fn (vlib_main_t *vm, unformat_input_t *input,
			      vlib_cli_command_t *cmd)
{
  unformat_input_t _line_input, *line_input = &_line_input;
  clib_error_t *error = 0;
  ip4_address_t addr;
  u32 fib_index = 0;
  int rv;

  /* Get a line of input. */
  if (!unformat_user (input, unformat_line_input, line_input))
    return 0;

  while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
    {
      if (unformat (line_input, "%U", unformat_ip4_address, &addr))
	;
      else if (unformat (line_input, "fib %u", &fib_index))
	;
      else
	{
	  error = clib_error_return (0, "unknown input '%U'",
				     format_unformat_error, line_input);
	  goto done;
	}
    }

  rv = nat44_ei_user_del (&addr, fib_index);

  if (!rv)
    {
      error = clib_error_return (0, "nat44_ei_user_del returned %d", rv);
    }

done:
  unformat_free (line_input);

  return error;
}

static clib_error_t *
nat44_ei_clear_sessions_command_fn (vlib_main_t *vm, unformat_input_t *input,
				    vlib_cli_command_t *cmd)
{
  clib_error_t *error = 0;
  nat44_ei_sessions_clear ();
  return error;
}

static clib_error_t *
nat44_ei_del_session_command_fn (vlib_main_t *vm, unformat_input_t *input,
				 vlib_cli_command_t *cmd)
{
  nat44_ei_main_t *nm = &nat44_ei_main;
  unformat_input_t _line_input, *line_input = &_line_input;
  u32 port = 0, vrf_id = nm->outside_vrf_id;
  clib_error_t *error = 0;
  nat_protocol_t proto;
  ip4_address_t addr;
  int rv, is_in = 0;

  /* Get a line of input. */
  if (!unformat_user (input, unformat_line_input, line_input))
    return 0;

  while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
    {
      if (unformat (line_input, "%U:%u %U", unformat_ip4_address, &addr, &port,
		    unformat_nat_protocol, &proto))
	;
      else if (unformat (line_input, "in"))
	{
	  is_in = 1;
	  vrf_id = nm->inside_vrf_id;
	}
      else if (unformat (line_input, "out"))
	{
	  is_in = 0;
	  vrf_id = nm->outside_vrf_id;
	}
      else if (unformat (line_input, "vrf %u", &vrf_id))
	;
      else
	{
	  error = clib_error_return (0, "unknown input '%U'",
				     format_unformat_error, line_input);
	  goto done;
	}
    }

  rv = nat44_ei_del_session (nm, &addr, clib_host_to_net_u16 (port), proto,
			     vrf_id, is_in);

  switch (rv)
    {
    case 0:
      break;

    default:
      error = clib_error_return (0, "nat44_ei_del_session returned %d", rv);
      goto done;
    }

done:
  unformat_free (line_input);

  return error;
}

static clib_error_t *
nat44_ei_forwarding_set_command_fn (vlib_main_t *vm, unformat_input_t *input,
				    vlib_cli_command_t *cmd)
{
  nat44_ei_main_t *nm = &nat44_ei_main;
  unformat_input_t _line_input, *line_input = &_line_input;
  u8 forwarding_enable;
  u8 forwarding_enable_set = 0;
  clib_error_t *error = 0;

  /* Get a line of input. */
  if (!unformat_user (input, unformat_line_input, line_input))
    return clib_error_return (0, "'enable' or 'disable' expected");

  while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
    {
      if (!forwarding_enable_set && unformat (line_input, "enable"))
	{
	  forwarding_enable = 1;
	  forwarding_enable_set = 1;
	}
      else if (!forwarding_enable_set && unformat (line_input, "disable"))
	{
	  forwarding_enable = 0;
	  forwarding_enable_set = 1;
	}
      else
	{
	  error = clib_error_return (0, "unknown input '%U'",
				     format_unformat_error, line_input);
	  goto done;
	}
    }

  if (!forwarding_enable_set)
    {
      error = clib_error_return (0, "'enable' or 'disable' expected");
      goto done;
    }

  nm->forwarding_enabled = forwarding_enable;

done:
  unformat_free (line_input);

  return error;
}

static clib_error_t *
set_timeout_command_fn (vlib_main_t *vm, unformat_input_t *input,
			vlib_cli_command_t *cmd)
{
  nat44_ei_main_t *nm = &nat44_ei_main;
  unformat_input_t _line_input, *line_input = &_line_input;
  clib_error_t *error = 0;

  /* Get a line of input. */
  if (!unformat_user (input, unformat_line_input, line_input))
    return 0;

  while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
    {
      if (unformat (line_input, "udp %u", &nm->timeouts.udp))
	;
      else if (unformat (line_input, "tcp-established %u",
			 &nm->timeouts.tcp.established))
	;
      else if (unformat (line_input, "tcp-transitory %u",
			 &nm->timeouts.tcp.transitory))
	;
      else if (unformat (line_input, "icmp %u", &nm->timeouts.icmp))
	;
      else if (unformat (line_input, "reset"))
	nat_reset_timeouts (&nm->timeouts);
      else
	{
	  error = clib_error_return (0, "unknown input '%U'",
				     format_unformat_error, line_input);
	  goto done;
	}
    }
done:
  unformat_free (line_input);
  return error;
}

static clib_error_t *
nat_show_timeouts_command_fn (vlib_main_t *vm, unformat_input_t *input,
			      vlib_cli_command_t *cmd)
{
  nat44_ei_main_t *nm = &nat44_ei_main;

  // TODO: make format timeout function
  vlib_cli_output (vm, "udp timeout: %dsec", nm->timeouts.udp);
  vlib_cli_output (vm, "tcp-established timeout: %dsec",
		   nm->timeouts.tcp.established);
  vlib_cli_output (vm, "tcp-transitory timeout: %dsec",
		   nm->timeouts.tcp.transitory);
  vlib_cli_output (vm, "icmp timeout: %dsec", nm->timeouts.icmp);

  return 0;
}

/*?
 * @cliexpar
 * @cliexstart{nat44 ei enable}
 * Enable nat44 ei plugin
 * To enable nat44, use:
 *  vpp# nat44 ei enable sessions <n>
 * To enable nat44 ei static mapping only, use:
 *  vpp# nat44 ei enable sessions <n> static-mapping
 * To enable nat44 ei static mapping with connection tracking, use:
 *  vpp# nat44 ei enable sessions <n> static-mapping connection-tracking
 * To enable nat44 ei out2in dpo, use:
 *  vpp# nat44 ei enable sessions <n> out2in-dpo
 * To set inside-vrf outside-vrf, use:
 *  vpp# nat44 ei enable sessions <n> inside-vrf <id> outside-vrf <id>
 * @cliexend
?*/
VLIB_CLI_COMMAND (nat44_ei_enable_command, static) = {
  .path = "nat44 ei enable",
  .short_help =
    "nat44 ei enable sessions <max-number> [users <max-number>] "
    "[static-mappig-only [connection-tracking]|out2in-dpo] [inside-vrf "
    "<vrf-id>] [outside-vrf <vrf-id>] [user-sessions <max-number>]",
  .function = nat44_ei_enable_command_fn,
};

/*?
 * @cliexpar
 * @cliexstart{nat44 ei disable}
 * Disable nat44 ei plugin
 * To disable nat44, use:
 *  vpp# nat44 ei disable
 * @cliexend
?*/
VLIB_CLI_COMMAND (nat44_ei_disable_command, static) = {
  .path = "nat44 ei disable",
  .short_help = "nat44 ei disable",
  .function = nat44_ei_disable_command_fn,
};

/*?
 * @cliexpar
 * @cliexstart{set snat44 ei workers}
 * Set NAT workers if 2 or more workers available, use:
 *  vpp# set snat44 ei workers 0-2,5
 * @cliexend
?*/
VLIB_CLI_COMMAND (set_workers_command, static) = {
  .path = "set nat44 ei workers",
  .function = set_workers_command_fn,
  .short_help = "set nat44 ei workers <workers-list>",
};

/*?
 * @cliexpar
 * @cliexstart{show nat44 ei workers}
 * Show NAT workers.
 *  vpp# show nat44 ei workers:
 *  2 workers
 *    vpp_wk_0
 *    vpp_wk_1
 * @cliexend
?*/
VLIB_CLI_COMMAND (nat_show_workers_command, static) = {
  .path = "show nat44 ei workers",
  .short_help = "show nat44 ei workers",
  .function = nat_show_workers_commnad_fn,
};

/*?
 * @cliexpar
 * @cliexstart{set nat44 ei timeout}
 * Set values of timeouts for NAT sessions (in seconds), use:
 *  vpp# set nat44 ei timeout udp 120 tcp-established 7500 tcp-transitory 250
icmp 90
 * To reset default values use:
 *  vpp# set nat44 ei timeout reset
 * @cliexend
?*/
VLIB_CLI_COMMAND (set_timeout_command, static) = {
  .path = "set nat44 ei timeout",
  .function = set_timeout_command_fn,
  .short_help = "set nat44 ei timeout [udp <sec> | tcp-established <sec> "
		"tcp-transitory <sec> | icmp <sec> | reset]",
};

/*?
 * @cliexpar
 * @cliexstart{show nat44 ei timeouts}
 * Show values of timeouts for NAT sessions.
 * vpp# show nat44 ei timeouts
 * udp timeout: 300sec
 * tcp-established timeout: 7440sec
 * tcp-transitory timeout: 240sec
 * icmp timeout: 60sec
 * @cliexend
?*/
VLIB_CLI_COMMAND (nat_show_timeouts_command, static) = {
  .path = "show nat44 ei timeouts",
  .short_help = "show nat44 ei timeouts",
  .function = nat_show_timeouts_command_fn,
};

/*?
 * @cliexpar
 * @cliexstart{nat44 ei set logging level}
 * To set NAT logging level use:
 * Set nat44 ei logging level
 * @cliexend
?*/
VLIB_CLI_COMMAND (nat44_ei_set_log_level_command, static) = {
  .path = "nat44 ei set logging level",
  .function = nat44_ei_set_log_level_command_fn,
  .short_help = "nat44 ei set logging level <level>",
};

/*?
 * @cliexpar
 * @cliexstart{snat44 ei ipfix logging}
 * To enable NAT IPFIX logging use:
 *  vpp# nat44 ei ipfix logging
 * To set IPFIX exporter use:
 *  vpp# set ipfix exporter collector 10.10.10.3 src 10.10.10.1
 * @cliexend
?*/
VLIB_CLI_COMMAND (nat44_ei_ipfix_logging_enable_disable_command, static) = {
  .path = "nat44 ei ipfix logging",
  .function = nat44_ei_ipfix_logging_enable_disable_command_fn,
  .short_help =
    "nat44 ei ipfix logging [domain <domain-id>] [src-port <port>] [disable]",
};

/*?
 * @cliexpar
 * @cliexstart{nat44 ei addr-port-assignment-alg}
 * Set address and port assignment algorithm
 * For the MAP-E CE limit port choice based on PSID use:
 *  vpp# nat44 ei addr-port-assignment-alg map-e psid 10 psid-offset 6 psid-len
6
 * For port range use:
 *  vpp# nat44 ei addr-port-assignment-alg port-range <start-port> - <end-port>
 * To set standard (default) address and port assignment algorithm use:
 *  vpp# nat44 ei addr-port-assignment-alg default
 * @cliexend
?*/
VLIB_CLI_COMMAND (nat44_ei_set_alloc_addr_and_port_alg_command, static) = {
  .path = "nat44 ei addr-port-assignment-alg",
  .short_help = "nat44 ei addr-port-assignment-alg <alg-name> [<alg-params>]",
  .function = nat44_ei_set_alloc_addr_and_port_alg_command_fn,
};

/*?
 * @cliexpar
 * @cliexstart{show nat44 ei addr-port-assignment-alg}
 * Show address and port assignment algorithm
 * @cliexend
?*/
VLIB_CLI_COMMAND (nat44_ei_show_alloc_addr_and_port_alg_command, static) = {
  .path = "show nat44 ei addr-port-assignment-alg",
  .short_help = "show nat44 ei addr-port-assignment-alg",
  .function = nat44_ei_show_alloc_addr_and_port_alg_command_fn,
};

/*?
 * @cliexpar
 * @cliexstart{nat44 ei mss-clamping}
 * Set TCP MSS rewriting configuration
 * To enable TCP MSS rewriting use:
 *  vpp# nat44 ei mss-clamping 1452
 * To disbale TCP MSS rewriting use:
 *  vpp# nat44 ei mss-clamping disable
 * @cliexend
?*/
VLIB_CLI_COMMAND (nat_set_mss_clamping_command, static) = {
  .path = "nat44 ei mss-clamping",
  .short_help = "nat44 ei mss-clamping <mss-value>|disable",
  .function = nat_set_mss_clamping_command_fn,
};

/*?
 * @cliexpar
 * @cliexstart{show nat44 ei mss-clamping}
 * Show TCP MSS rewriting configuration
 * @cliexend
?*/
VLIB_CLI_COMMAND (nat_show_mss_clamping_command, static) = {
  .path = "show nat44 ei mss-clamping",
  .short_help = "show nat44 ei mss-clamping",
  .function = nat_show_mss_clamping_command_fn,
};

/*?
 * @cliexpar
 * @cliexstart{nat44 ei ha failover}
 * Set HA failover (remote settings)
 * @cliexend
?*/
VLIB_CLI_COMMAND (nat_ha_failover_command, static) = {
  .path = "nat44 ei ha failover",
  .short_help =
    "nat44 ei ha failover <ip4-address>:<port> [refresh-interval <sec>]",
  .function = nat_ha_failover_command_fn,
};

/*?
 * @cliexpar
 * @cliexstart{nat44 ei ha listener}
 * Set HA listener (local settings)
 * @cliexend
?*/
VLIB_CLI_COMMAND (nat_ha_listener_command, static) = {
  .path = "nat44 ei ha listener",
  .short_help =
    "nat44 ei ha listener <ip4-address>:<port> [path-mtu <path-mtu>]",
  .function = nat_ha_listener_command_fn,
};

/*?
 * @cliexpar
 * @cliexstart{show nat44 ei ha}
 * Show HA configuration/status
 * @cliexend
?*/
VLIB_CLI_COMMAND (nat_show_ha_command, static) = {
  .path = "show nat44 ei ha",
  .short_help = "show nat44 ei ha",
  .function = nat_show_ha_command_fn,
};

/*?
 * @cliexpar
 * @cliexstart{nat44 ei ha flush}
 * Flush the current HA data (for testing)
 * @cliexend
?*/
VLIB_CLI_COMMAND (nat_ha_flush_command, static) = {
  .path = "nat44 ei ha flush",
  .short_help = "nat44 ei ha flush",
  .function = nat_ha_flush_command_fn,
};

/*?
 * @cliexpar
 * @cliexstart{nat44 ei ha resync}
 * Resync HA (resend existing sessions to new failover)
 * @cliexend
?*/
VLIB_CLI_COMMAND (nat_ha_resync_command, static) = {
  .path = "nat44 ei ha resync",
  .short_help = "nat44 ei ha resync",
  .function = nat_ha_resync_command_fn,
};

/*?
 * @cliexpar
 * @cliexstart{show nat44 ei hash tables}
 * Show NAT44 hash tables
 * @cliexend
?*/
VLIB_CLI_COMMAND (nat44_ei_show_hash, static) = {
  .path = "show nat44 ei hash tables",
  .short_help = "show nat44 ei hash tables [detail|verbose]",
  .function = nat44_ei_show_hash_command_fn,
};

/*?
 * @cliexpar
 * @cliexstart{nat44 ei add address}
 * Add/delete NAT44 pool address.
 * To add NAT44 pool address use:
 *  vpp# nat44 ei add address 172.16.1.3
 *  vpp# nat44 ei add address 172.16.2.2 - 172.16.2.24
 * To add NAT44 pool address for specific tenant (identified by VRF id) use:
 *  vpp# nat44 ei add address 172.16.1.3 tenant-vrf 10
 * @cliexend
?*/
VLIB_CLI_COMMAND (add_address_command, static) = {
  .path = "nat44 ei add address",
  .short_help = "nat44 ei add address <ip4-range-start> [- <ip4-range-end>] "
		"[tenant-vrf <vrf-id>] [del]",
  .function = add_address_command_fn,
};

/*?
 * @cliexpar
 * @cliexstart{show nat44 ei addresses}
 * Show NAT44 pool addresses.
 * vpp# show nat44 ei addresses
 * NAT44 pool addresses:
 * 172.16.2.2
 *   tenant VRF independent
 *   10 busy udp ports
 *   0 busy tcp ports
 *   0 busy icmp ports
 * 172.16.1.3
 *   tenant VRF: 10
 *   0 busy udp ports
 *   2 busy tcp ports
 *   0 busy icmp ports
 * @cliexend
?*/
VLIB_CLI_COMMAND (nat44_ei_show_addresses_command, static) = {
  .path = "show nat44 ei addresses",
  .short_help = "show nat44 ei addresses",
  .function = nat44_ei_show_addresses_command_fn,
};

/*?
 * @cliexpar
 * @cliexstart{set interface nat44}
 * Enable/disable NAT44 feature on the interface.
 * To enable NAT44 feature with local network interface use:
 *  vpp# set interface nat44 ei in GigabitEthernet0/8/0
 * To enable NAT44 feature with external network interface use:
 *  vpp# set interface nat44 ei out GigabitEthernet0/a/0
 * @cliexend
?*/
VLIB_CLI_COMMAND (set_interface_nat44_ei_command, static) = {
  .path = "set interface nat44 ei",
  .function = nat44_ei_feature_command_fn,
  .short_help =
    "set interface nat44 ei in <intfc> out <intfc> [output-feature] "
    "[del]",
};

/*?
 * @cliexpar
 * @cliexstart{show nat44 ei interfaces}
 * Show interfaces with NAT44 feature.
 * vpp# show nat44 ei interfaces
 * NAT44 interfaces:
 *  GigabitEthernet0/8/0 in
 *  GigabitEthernet0/a/0 out
 * @cliexend
?*/
VLIB_CLI_COMMAND (nat44_ei_show_interfaces_command, static) = {
  .path = "show nat44 ei interfaces",
  .short_help = "show nat44 ei interfaces",
  .function = nat44_ei_show_interfaces_command_fn,
};

/*?
 * @cliexpar
 * @cliexstart{nat44 ei add static mapping}
 * Static mapping allows hosts on the external network to initiate connection
 * to to the local network host.
 * To create static mapping between local host address 10.0.0.3 port 6303 and
 * external address 4.4.4.4 port 3606 for TCP protocol use:
 *  vpp# nat44 ei add static mapping tcp local 10.0.0.3 6303 external 4.4.4.4
3606
 * If not runnig "static mapping only" NAT plugin mode use before:
 *  vpp# nat44 ei add address 4.4.4.4
 * To create address only static mapping between local and external address
use:
 *  vpp# nat44 ei add static mapping local 10.0.0.3 external 4.4.4.4
 * To create ICMP static mapping between local and external with ICMP echo
 * identifier 10 use:
 *  vpp# nat44 ei add static mapping icmp local 10.0.0.3 10 external 4.4.4.4 10
 * @cliexend
?*/
VLIB_CLI_COMMAND (add_static_mapping_command, static) = {
  .path = "nat44 ei add static mapping",
  .function = add_static_mapping_command_fn,
  .short_help = "nat44 ei add static mapping tcp|udp|icmp local <addr> "
		"[<port|icmp-echo-id>] "
		"external <addr> [<port|icmp-echo-id>] [vrf <table-id>] [del]",
};

/*?
 * @cliexpar
 * @cliexstart{nat44 ei add identity mapping}
 * Identity mapping translate an IP address to itself.
 * To create identity mapping for address 10.0.0.3 port 6303 for TCP protocol
 * use:
 *  vpp# nat44 ei add identity mapping 10.0.0.3 tcp 6303
 * To create identity mapping for address 10.0.0.3 use:
 *  vpp# nat44 ei add identity mapping 10.0.0.3
 * To create identity mapping for DHCP addressed interface use:
 *  vpp# nat44 ei add identity mapping external GigabitEthernet0/a/0 tcp 3606
 * @cliexend
?*/
VLIB_CLI_COMMAND (add_identity_mapping_command, static) = {
  .path = "nat44 ei add identity mapping",
  .function = add_identity_mapping_command_fn,
  .short_help =
    "nat44 ei add identity mapping <ip4-addr>|external <interface> "
    "[<protocol> <port>] [vrf <table-id>] [del]",
};

/*?
 * @cliexpar
 * @cliexstart{show nat44 ei static mappings}
 * Show NAT44 static mappings.
 * vpp# show nat44 ei static mappings
 * NAT44 static mappings:
 *  local 10.0.0.3 external 4.4.4.4 vrf 0
 *  tcp local 192.168.0.4:6303 external 4.4.4.3:3606 vrf 0
 *  tcp vrf 0 external 1.2.3.4:80
 *   local 10.100.10.10:8080 probability 80
 *   local 10.100.10.20:8080 probability 20
 *  tcp local 10.0.0.10:3603 external GigabitEthernet0/a/0:6306 vrf 10
 * @cliexend
?*/
VLIB_CLI_COMMAND (nat44_ei_show_static_mappings_command, static) = {
  .path = "show nat44 ei static mappings",
  .short_help = "show nat44 ei static mappings",
  .function = nat44_ei_show_static_mappings_command_fn,
};

/*?
 * @cliexpar
 * @cliexstart{nat44 ei add interface address}
 * Use NAT44 pool address from specific interfce
 * To add NAT44 pool address from specific interface use:
 *  vpp# nat44 ei add interface address GigabitEthernet0/8/0
 * @cliexend
?*/
VLIB_CLI_COMMAND (nat44_ei_add_interface_address_command, static) = {
  .path = "nat44 ei add interface address",
  .short_help = "nat44 ei add interface address <interface> [del]",
  .function = nat44_ei_add_interface_address_command_fn,
};

/*?
 * @cliexpar
 * @cliexstart{show nat44 ei interface address}
 * Show NAT44 pool address interfaces
 * vpp# show nat44 ei interface address
 * NAT44 pool address interfaces:
 *  GigabitEthernet0/a/0
 * @cliexend
?*/
VLIB_CLI_COMMAND (nat44_ei_show_interface_address_command, static) = {
  .path = "show nat44 ei interface address",
  .short_help = "show nat44 ei interface address",
  .function = nat44_ei_show_interface_address_command_fn,
};

/*?
 * @cliexpar
 * @cliexstart{show nat44 ei sessions}
 * Show NAT44 sessions.
 * @cliexend
?*/
VLIB_CLI_COMMAND (nat44_ei_show_sessions_command, static) = {
  .path = "show nat44 ei sessions",
  .short_help = "show nat44 ei sessions [detail|metrics]",
  .function = nat44_ei_show_sessions_command_fn,
};

/*?
 * @cliexpar
 * @cliexstart{nat44 ei del user}
 * To delete all NAT44 user sessions:
 *  vpp# nat44 ei del user 10.0.0.3
 * @cliexend
?*/
VLIB_CLI_COMMAND (nat44_ei_del_user_command, static) = {
  .path = "nat44 ei del user",
  .short_help = "nat44 ei del user <addr> [fib <index>]",
  .function = nat44_ei_del_user_command_fn,
};

/*?
 * @cliexpar
 * @cliexstart{clear nat44 ei sessions}
 * To clear all NAT44 sessions
 *  vpp# clear nat44 ei sessions
 * @cliexend
?*/
VLIB_CLI_COMMAND (nat44_ei_clear_sessions_command, static) = {
  .path = "clear nat44 ei sessions",
  .short_help = "clear nat44 ei sessions",
  .function = nat44_ei_clear_sessions_command_fn,
};

/*?
 * @cliexpar
 * @cliexstart{nat44 ei del session}
 * To administratively delete NAT44 session by inside address and port use:
 *  vpp# nat44 ei del session in 10.0.0.3:6303 tcp
 * To administratively delete NAT44 session by outside address and port use:
 *  vpp# nat44 ei del session out 1.0.0.3:6033 udp
 * @cliexend
?*/
VLIB_CLI_COMMAND (nat44_ei_del_session_command, static) = {
  .path = "nat44 ei del session",
  .short_help = "nat44 ei del session in|out <addr>:<port> tcp|udp|icmp [vrf "
		"<id>] [external-host <addr>:<port>]",
  .function = nat44_ei_del_session_command_fn,
};

/*?
 * @cliexpar
 * @cliexstart{nat44 ei forwarding}
 * Enable or disable forwarding
 * Forward packets which don't match existing translation
 * or static mapping instead of dropping them.
 * To enable forwarding, use:
 *  vpp# nat44 ei forwarding enable
 * To disable forwarding, use:
 *  vpp# nat44 ei forwarding disable
 * @cliexend
?*/
VLIB_CLI_COMMAND (nat44_ei_forwarding_set_command, static) = {
  .path = "nat44 ei forwarding",
  .short_help = "nat44 ei forwarding enable|disable",
  .function = nat44_ei_forwarding_set_command_fn,
};

/*
 * fd.io coding-style-patch-verification: ON
 *
 * Local Variables:
 * eval: (c-set-style "gnu")
 * End:
 */