Revert "Add support for MAP-T CE (VPP-1058)"
[vpp.git] / src / plugins / map / map.c
1 /*
2  * map.c : MAP support
3  *
4  * Copyright (c) 2015 Cisco and/or its affiliates.
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at:
8  *
9  *     http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */
17
18 #include <vnet/fib/fib_table.h>
19 #include <vnet/fib/ip6_fib.h>
20 #include <vnet/adj/adj.h>
21 #include <map/map_dpo.h>
22 #include <vppinfra/crc32.h>
23 #include <vnet/plugin/plugin.h>
24 #include <vpp/app/version.h>
25 #include "map.h"
26
27 map_main_t map_main;
28
29 /*
30  * This code supports the following MAP modes:
31  *
32  * Algorithmic Shared IPv4 address (ea_bits_len > 0):
33  *   ea_bits_len + ip4_prefix > 32
34  *   psid_length > 0, ip6_prefix < 64, ip4_prefix <= 32
35  * Algorithmic Full IPv4 address (ea_bits_len > 0):
36  *   ea_bits_len + ip4_prefix = 32
37  *   psid_length = 0, ip6_prefix < 64, ip4_prefix <= 32
38  * Algorithmic IPv4 prefix (ea_bits_len > 0):
39  *   ea_bits_len + ip4_prefix < 32
40  *   psid_length = 0, ip6_prefix < 64, ip4_prefix <= 32
41  *
42  * Independent Shared IPv4 address (ea_bits_len = 0):
43  *   ip4_prefix = 32
44  *   psid_length > 0
45  *   Rule IPv6 address = 128, Rule PSID Set
46  * Independent Full IPv4 address (ea_bits_len = 0):
47  *   ip4_prefix = 32
48  *   psid_length = 0, ip6_prefix = 128
49  * Independent IPv4 prefix (ea_bits_len = 0):
50  *   ip4_prefix < 32
51  *   psid_length = 0, ip6_prefix = 128
52  *
53  */
54
55 /*
56  * This code supports MAP-T:
57  *
58  * With a DMR prefix length of 64 or 96 (RFC6052).
59  *
60  */
61
62
63
64 int
65 map_create_domain (ip4_address_t * ip4_prefix,
66                    u8 ip4_prefix_len,
67                    ip6_address_t * ip6_prefix,
68                    u8 ip6_prefix_len,
69                    ip6_address_t * ip6_src,
70                    u8 ip6_src_len,
71                    u8 ea_bits_len,
72                    u8 psid_offset,
73                    u8 psid_length, u32 * map_domain_index, u16 mtu, u8 flags)
74 {
75   u8 suffix_len, suffix_shift;
76   map_main_t *mm = &map_main;
77   dpo_id_t dpo_v4 = DPO_INVALID;
78   dpo_id_t dpo_v6 = DPO_INVALID;
79   map_domain_t *d;
80
81   /* Sanity check on the src prefix length */
82   if (flags & MAP_DOMAIN_TRANSLATION)
83     {
84       if (ip6_src_len != 96 && ip6_src_len != 64)
85         {
86           clib_warning ("MAP-T only supports prefix lengths of 64 and 96.");
87           return -1;
88         }
89     }
90   else
91     {
92       if (ip6_src_len != 128)
93         {
94           clib_warning
95             ("MAP-E requires a BR address, not a prefix (ip6_src_len should "
96              "be 128).");
97           return -1;
98         }
99     }
100
101   /* How many, and which bits to grab from the IPv4 DA */
102   if (ip4_prefix_len + ea_bits_len < 32)
103     {
104       flags |= MAP_DOMAIN_PREFIX;
105       suffix_shift = 32 - ip4_prefix_len - ea_bits_len;
106       suffix_len = ea_bits_len;
107     }
108   else
109     {
110       suffix_shift = 0;
111       suffix_len = 32 - ip4_prefix_len;
112     }
113
114   /* EA bits must be within the first 64 bits */
115   if (ea_bits_len > 0 && ((ip6_prefix_len + ea_bits_len) > 64 ||
116                           ip6_prefix_len + suffix_len + psid_length > 64))
117     {
118       clib_warning
119         ("Embedded Address bits must be within the first 64 bits of "
120          "the IPv6 prefix");
121       return -1;
122     }
123
124   /* Get domain index */
125   pool_get_aligned (mm->domains, d, CLIB_CACHE_LINE_BYTES);
126   clib_memset (d, 0, sizeof (*d));
127   *map_domain_index = d - mm->domains;
128
129   /* Init domain struct */
130   d->ip4_prefix.as_u32 = ip4_prefix->as_u32;
131   d->ip4_prefix_len = ip4_prefix_len;
132   d->ip6_prefix = *ip6_prefix;
133   d->ip6_prefix_len = ip6_prefix_len;
134   d->ip6_src = *ip6_src;
135   d->ip6_src_len = ip6_src_len;
136   d->ea_bits_len = ea_bits_len;
137   d->psid_offset = psid_offset;
138   d->psid_length = psid_length;
139   d->mtu = mtu;
140   d->flags = flags;
141   d->suffix_shift = suffix_shift;
142   d->suffix_mask = (1 << suffix_len) - 1;
143
144   d->psid_shift = 16 - psid_length - psid_offset;
145   d->psid_mask = (1 << d->psid_length) - 1;
146   d->ea_shift = 64 - ip6_prefix_len - suffix_len - d->psid_length;
147
148   /* MAP data-plane object */
149   if (d->flags & MAP_DOMAIN_TRANSLATION)
150     map_t_dpo_create (DPO_PROTO_IP4, *map_domain_index, &dpo_v4);
151   else
152     map_dpo_create (DPO_PROTO_IP4, *map_domain_index, &dpo_v4);
153
154   /* Create ip4 route */
155   fib_prefix_t pfx = {
156     .fp_proto = FIB_PROTOCOL_IP4,
157     .fp_len = d->ip4_prefix_len,
158     .fp_addr = {
159                 .ip4 = d->ip4_prefix,
160                 }
161     ,
162   };
163   fib_table_entry_special_dpo_add (0, &pfx,
164                                    FIB_SOURCE_MAP,
165                                    FIB_ENTRY_FLAG_EXCLUSIVE, &dpo_v4);
166   dpo_reset (&dpo_v4);
167
168   /*
169    * construct a DPO to use the v6 domain
170    */
171   if (d->flags & MAP_DOMAIN_TRANSLATION)
172     map_t_dpo_create (DPO_PROTO_IP6, *map_domain_index, &dpo_v6);
173   else
174     map_dpo_create (DPO_PROTO_IP6, *map_domain_index, &dpo_v6);
175
176   /*
177    * Multiple MAP domains may share same source IPv6 TEP. Which is just dandy.
178    * We are not tracking the sharing. So a v4 lookup to find the correct
179    * domain post decap/trnaslate is always done
180    *
181    * Create ip6 route. This is a reference counted add. If the prefix
182    * already exists and is MAP sourced, it is now MAP source n+1 times
183    * and will need to be removed n+1 times.
184    */
185   fib_prefix_t pfx6 = {
186     .fp_proto = FIB_PROTOCOL_IP6,
187     .fp_len = d->ip6_src_len,
188     .fp_addr.ip6 = d->ip6_src,
189   };
190
191   fib_table_entry_special_dpo_add (0, &pfx6,
192                                    FIB_SOURCE_MAP,
193                                    FIB_ENTRY_FLAG_EXCLUSIVE, &dpo_v6);
194   dpo_reset (&dpo_v6);
195
196   /* Validate packet/byte counters */
197   map_domain_counter_lock (mm);
198   int i;
199   for (i = 0; i < vec_len (mm->simple_domain_counters); i++)
200     {
201       vlib_validate_simple_counter (&mm->simple_domain_counters[i],
202                                     *map_domain_index);
203       vlib_zero_simple_counter (&mm->simple_domain_counters[i],
204                                 *map_domain_index);
205     }
206   for (i = 0; i < vec_len (mm->domain_counters); i++)
207     {
208       vlib_validate_combined_counter (&mm->domain_counters[i],
209                                       *map_domain_index);
210       vlib_zero_combined_counter (&mm->domain_counters[i], *map_domain_index);
211     }
212   map_domain_counter_unlock (mm);
213
214   return 0;
215 }
216
217 /*
218  * map_delete_domain
219  */
220 int
221 map_delete_domain (u32 map_domain_index)
222 {
223   map_main_t *mm = &map_main;
224   map_domain_t *d;
225
226   if (pool_is_free_index (mm->domains, map_domain_index))
227     {
228       clib_warning ("MAP domain delete: domain does not exist: %d",
229                     map_domain_index);
230       return -1;
231     }
232
233   d = pool_elt_at_index (mm->domains, map_domain_index);
234
235   fib_prefix_t pfx = {
236     .fp_proto = FIB_PROTOCOL_IP4,
237     .fp_len = d->ip4_prefix_len,
238     .fp_addr = {
239                 .ip4 = d->ip4_prefix,
240                 }
241     ,
242   };
243   fib_table_entry_special_remove (0, &pfx, FIB_SOURCE_MAP);
244
245   fib_prefix_t pfx6 = {
246     .fp_proto = FIB_PROTOCOL_IP6,
247     .fp_len = d->ip6_src_len,
248     .fp_addr = {
249                 .ip6 = d->ip6_src,
250                 }
251     ,
252   };
253   fib_table_entry_special_remove (0, &pfx6, FIB_SOURCE_MAP);
254
255   /* Deleting rules */
256   if (d->rules)
257     clib_mem_free (d->rules);
258
259   pool_put (mm->domains, d);
260
261   return 0;
262 }
263
264 int
265 map_add_del_psid (u32 map_domain_index, u16 psid, ip6_address_t * tep,
266                   u8 is_add)
267 {
268   map_domain_t *d;
269   map_main_t *mm = &map_main;
270
271   if (pool_is_free_index (mm->domains, map_domain_index))
272     {
273       clib_warning ("MAP rule: domain does not exist: %d", map_domain_index);
274       return -1;
275     }
276   d = pool_elt_at_index (mm->domains, map_domain_index);
277
278   /* Rules are only used in 1:1 independent case */
279   if (d->ea_bits_len > 0)
280     return (-1);
281
282   if (!d->rules)
283     {
284       u32 l = (0x1 << d->psid_length) * sizeof (ip6_address_t);
285       d->rules = clib_mem_alloc_aligned (l, CLIB_CACHE_LINE_BYTES);
286       if (!d->rules)
287         return -1;
288       clib_memset (d->rules, 0, l);
289     }
290
291   if (psid >= (0x1 << d->psid_length))
292     {
293       clib_warning ("MAP rule: PSID outside bounds: %d [%d]", psid,
294                     0x1 << d->psid_length);
295       return -1;
296     }
297
298   if (is_add)
299     {
300       d->rules[psid] = *tep;
301     }
302   else
303     {
304       clib_memset (&d->rules[psid], 0, sizeof (ip6_address_t));
305     }
306   return 0;
307 }
308
309 #ifdef MAP_SKIP_IP6_LOOKUP
310 /**
311  * Pre-resolvd per-protocol global next-hops
312  */
313 map_main_pre_resolved_t pre_resolved[FIB_PROTOCOL_MAX];
314
315 static void
316 map_pre_resolve_init (map_main_pre_resolved_t * pr)
317 {
318   pr->fei = FIB_NODE_INDEX_INVALID;
319   fib_node_init (&pr->node, FIB_NODE_TYPE_MAP_E);
320 }
321
322 static u8 *
323 format_map_pre_resolve (u8 * s, va_list * ap)
324 {
325   map_main_pre_resolved_t *pr = va_arg (*ap, map_main_pre_resolved_t *);
326
327   if (FIB_NODE_INDEX_INVALID != pr->fei)
328     {
329       const fib_prefix_t *pfx;
330
331       pfx = fib_entry_get_prefix (pr->fei);
332
333       return (format (s, "%U (%u)",
334                       format_ip46_address, &pfx->fp_addr, IP46_TYPE_ANY,
335                       pr->dpo.dpoi_index));
336     }
337   else
338     {
339       return (format (s, "un-set"));
340     }
341 }
342
343
344 /**
345  * Function definition to inform the FIB node that its last lock has gone.
346  */
347 static void
348 map_last_lock_gone (fib_node_t * node)
349 {
350   /*
351    * The MAP is a root of the graph. As such
352    * it never has children and thus is never locked.
353    */
354   ASSERT (0);
355 }
356
357 static map_main_pre_resolved_t *
358 map_from_fib_node (fib_node_t * node)
359 {
360   ASSERT (FIB_NODE_TYPE_MAP_E == node->fn_type);
361   return ((map_main_pre_resolved_t *)
362           (((char *) node) -
363            STRUCT_OFFSET_OF (map_main_pre_resolved_t, node)));
364 }
365
366 static void
367 map_stack (map_main_pre_resolved_t * pr)
368 {
369   const dpo_id_t *dpo;
370
371   dpo = fib_entry_contribute_ip_forwarding (pr->fei);
372
373   dpo_copy (&pr->dpo, dpo);
374 }
375
376 /**
377  * Function definition to backwalk a FIB node
378  */
379 static fib_node_back_walk_rc_t
380 map_back_walk (fib_node_t * node, fib_node_back_walk_ctx_t * ctx)
381 {
382   map_stack (map_from_fib_node (node));
383
384   return (FIB_NODE_BACK_WALK_CONTINUE);
385 }
386
387 /**
388  * Function definition to get a FIB node from its index
389  */
390 static fib_node_t *
391 map_fib_node_get (fib_node_index_t index)
392 {
393   return (&pre_resolved[index].node);
394 }
395
396 /*
397  * Virtual function table registered by MPLS GRE tunnels
398  * for participation in the FIB object graph.
399  */
400 const static fib_node_vft_t map_vft = {
401   .fnv_get = map_fib_node_get,
402   .fnv_last_lock = map_last_lock_gone,
403   .fnv_back_walk = map_back_walk,
404 };
405
406 static void
407 map_fib_resolve (map_main_pre_resolved_t * pr,
408                  fib_protocol_t proto, u8 len, const ip46_address_t * addr)
409 {
410   fib_prefix_t pfx = {
411     .fp_proto = proto,
412     .fp_len = len,
413     .fp_addr = *addr,
414   };
415
416   pr->fei = fib_table_entry_special_add (0,     // default fib
417                                          &pfx,
418                                          FIB_SOURCE_RR, FIB_ENTRY_FLAG_NONE);
419   pr->sibling = fib_entry_child_add (pr->fei, FIB_NODE_TYPE_MAP_E, proto);
420   map_stack (pr);
421 }
422
423 static void
424 map_fib_unresolve (map_main_pre_resolved_t * pr,
425                    fib_protocol_t proto, u8 len, const ip46_address_t * addr)
426 {
427   fib_prefix_t pfx = {
428     .fp_proto = proto,
429     .fp_len = len,
430     .fp_addr = *addr,
431   };
432
433   fib_entry_child_remove (pr->fei, pr->sibling);
434
435   fib_table_entry_special_remove (0,    // default fib
436                                   &pfx, FIB_SOURCE_RR);
437   dpo_reset (&pr->dpo);
438
439   pr->fei = FIB_NODE_INDEX_INVALID;
440   pr->sibling = FIB_NODE_INDEX_INVALID;
441 }
442
443 static void
444 map_pre_resolve (ip4_address_t * ip4, ip6_address_t * ip6, int is_del)
445 {
446   if (ip6 && (ip6->as_u64[0] != 0 || ip6->as_u64[1] != 0))
447     {
448       ip46_address_t addr = {
449         .ip6 = *ip6,
450       };
451       if (is_del)
452         map_fib_unresolve (&pre_resolved[FIB_PROTOCOL_IP6],
453                            FIB_PROTOCOL_IP6, 128, &addr);
454       else
455         map_fib_resolve (&pre_resolved[FIB_PROTOCOL_IP6],
456                          FIB_PROTOCOL_IP6, 128, &addr);
457     }
458   if (ip4 && (ip4->as_u32 != 0))
459     {
460       ip46_address_t addr = {
461         .ip4 = *ip4,
462       };
463       if (is_del)
464         map_fib_unresolve (&pre_resolved[FIB_PROTOCOL_IP4],
465                            FIB_PROTOCOL_IP4, 32, &addr);
466       else
467         map_fib_resolve (&pre_resolved[FIB_PROTOCOL_IP4],
468                          FIB_PROTOCOL_IP4, 32, &addr);
469     }
470 }
471 #endif
472
473 static clib_error_t *
474 map_security_check_command_fn (vlib_main_t * vm,
475                                unformat_input_t * input,
476                                vlib_cli_command_t * cmd)
477 {
478   unformat_input_t _line_input, *line_input = &_line_input;
479   map_main_t *mm = &map_main;
480   clib_error_t *error = NULL;
481
482   /* Get a line of input. */
483   if (!unformat_user (input, unformat_line_input, line_input))
484     return 0;
485
486   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
487     {
488       if (unformat (line_input, "off"))
489         mm->sec_check = false;
490       else if (unformat (line_input, "on"))
491         mm->sec_check = true;
492       else
493         {
494           error = clib_error_return (0, "unknown input `%U'",
495                                      format_unformat_error, line_input);
496           goto done;
497         }
498     }
499
500 done:
501   unformat_free (line_input);
502
503   return error;
504 }
505
506 static clib_error_t *
507 map_security_check_frag_command_fn (vlib_main_t * vm,
508                                     unformat_input_t * input,
509                                     vlib_cli_command_t * cmd)
510 {
511   unformat_input_t _line_input, *line_input = &_line_input;
512   map_main_t *mm = &map_main;
513   clib_error_t *error = NULL;
514
515   /* Get a line of input. */
516   if (!unformat_user (input, unformat_line_input, line_input))
517     return 0;
518
519   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
520     {
521       if (unformat (line_input, "off"))
522         mm->sec_check_frag = false;
523       else if (unformat (line_input, "on"))
524         mm->sec_check_frag = true;
525       else
526         {
527           error = clib_error_return (0, "unknown input `%U'",
528                                      format_unformat_error, line_input);
529           goto done;
530         }
531     }
532
533 done:
534   unformat_free (line_input);
535
536   return error;
537 }
538
539 static clib_error_t *
540 map_add_domain_command_fn (vlib_main_t * vm,
541                            unformat_input_t * input, vlib_cli_command_t * cmd)
542 {
543   unformat_input_t _line_input, *line_input = &_line_input;
544   ip4_address_t ip4_prefix;
545   ip6_address_t ip6_prefix;
546   ip6_address_t ip6_src;
547   u32 ip6_prefix_len = 0, ip4_prefix_len = 0, map_domain_index, ip6_src_len;
548   u32 num_m_args = 0;
549   /* Optional arguments */
550   u32 ea_bits_len = 0, psid_offset = 0, psid_length = 0;
551   u32 mtu = 0;
552   u8 flags = 0;
553   ip6_src_len = 128;
554   clib_error_t *error = NULL;
555
556   /* Get a line of input. */
557   if (!unformat_user (input, unformat_line_input, line_input))
558     return 0;
559
560   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
561     {
562       if (unformat
563           (line_input, "ip4-pfx %U/%d", unformat_ip4_address, &ip4_prefix,
564            &ip4_prefix_len))
565         num_m_args++;
566       else
567         if (unformat
568             (line_input, "ip6-pfx %U/%d", unformat_ip6_address, &ip6_prefix,
569              &ip6_prefix_len))
570         num_m_args++;
571       else
572         if (unformat
573             (line_input, "ip6-src %U/%d", unformat_ip6_address, &ip6_src,
574              &ip6_src_len))
575         num_m_args++;
576       else
577         if (unformat
578             (line_input, "ip6-src %U", unformat_ip6_address, &ip6_src))
579         num_m_args++;
580       else if (unformat (line_input, "ea-bits-len %d", &ea_bits_len))
581         num_m_args++;
582       else if (unformat (line_input, "psid-offset %d", &psid_offset))
583         num_m_args++;
584       else if (unformat (line_input, "psid-len %d", &psid_length))
585         num_m_args++;
586       else if (unformat (line_input, "mtu %d", &mtu))
587         num_m_args++;
588       else if (unformat (line_input, "map-t"))
589         flags |= MAP_DOMAIN_TRANSLATION;
590       else if (unformat (line_input, "rfc6052"))
591         flags |= (MAP_DOMAIN_TRANSLATION | MAP_DOMAIN_RFC6052);
592       else
593         {
594           error = clib_error_return (0, "unknown input `%U'",
595                                      format_unformat_error, line_input);
596           goto done;
597         }
598     }
599
600   if (num_m_args < 3)
601     {
602       error = clib_error_return (0, "mandatory argument(s) missing");
603       goto done;
604     }
605
606   map_create_domain (&ip4_prefix, ip4_prefix_len,
607                      &ip6_prefix, ip6_prefix_len, &ip6_src, ip6_src_len,
608                      ea_bits_len, psid_offset, psid_length, &map_domain_index,
609                      mtu, flags);
610
611 done:
612   unformat_free (line_input);
613
614   return error;
615 }
616
617 static clib_error_t *
618 map_del_domain_command_fn (vlib_main_t * vm,
619                            unformat_input_t * input, vlib_cli_command_t * cmd)
620 {
621   unformat_input_t _line_input, *line_input = &_line_input;
622   u32 num_m_args = 0;
623   u32 map_domain_index;
624   clib_error_t *error = NULL;
625
626   /* Get a line of input. */
627   if (!unformat_user (input, unformat_line_input, line_input))
628     return 0;
629
630   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
631     {
632       if (unformat (line_input, "index %d", &map_domain_index))
633         num_m_args++;
634       else
635         {
636           error = clib_error_return (0, "unknown input `%U'",
637                                      format_unformat_error, line_input);
638           goto done;
639         }
640     }
641
642   if (num_m_args != 1)
643     {
644       error = clib_error_return (0, "mandatory argument(s) missing");
645       goto done;
646     }
647
648   map_delete_domain (map_domain_index);
649
650 done:
651   unformat_free (line_input);
652
653   return error;
654 }
655
656 static clib_error_t *
657 map_add_rule_command_fn (vlib_main_t * vm,
658                          unformat_input_t * input, vlib_cli_command_t * cmd)
659 {
660   unformat_input_t _line_input, *line_input = &_line_input;
661   ip6_address_t tep;
662   u32 num_m_args = 0;
663   u32 psid = 0, map_domain_index;
664   clib_error_t *error = NULL;
665
666   /* Get a line of input. */
667   if (!unformat_user (input, unformat_line_input, line_input))
668     return 0;
669
670   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
671     {
672       if (unformat (line_input, "index %d", &map_domain_index))
673         num_m_args++;
674       else if (unformat (line_input, "psid %d", &psid))
675         num_m_args++;
676       else
677         if (unformat (line_input, "ip6-dst %U", unformat_ip6_address, &tep))
678         num_m_args++;
679       else
680         {
681           error = clib_error_return (0, "unknown input `%U'",
682                                      format_unformat_error, line_input);
683           goto done;
684         }
685     }
686
687   if (num_m_args != 3)
688     {
689       error = clib_error_return (0, "mandatory argument(s) missing");
690       goto done;
691     }
692
693   if (map_add_del_psid (map_domain_index, psid, &tep, 1) != 0)
694     {
695       error = clib_error_return (0, "Failing to add Mapping Rule");
696       goto done;
697     }
698
699 done:
700   unformat_free (line_input);
701
702   return error;
703 }
704
705 #if MAP_SKIP_IP6_LOOKUP
706 static clib_error_t *
707 map_pre_resolve_command_fn (vlib_main_t * vm,
708                             unformat_input_t * input,
709                             vlib_cli_command_t * cmd)
710 {
711   unformat_input_t _line_input, *line_input = &_line_input;
712   ip4_address_t ip4nh, *p_v4 = NULL;
713   ip6_address_t ip6nh, *p_v6 = NULL;
714   clib_error_t *error = NULL;
715   int is_del = 0;
716
717   clib_memset (&ip4nh, 0, sizeof (ip4nh));
718   clib_memset (&ip6nh, 0, sizeof (ip6nh));
719
720   /* Get a line of input. */
721   if (!unformat_user (input, unformat_line_input, line_input))
722     return 0;
723
724   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
725     {
726       if (unformat (line_input, "ip4-nh %U", unformat_ip4_address, &ip4nh))
727         p_v4 = &ip4nh;
728       else
729         if (unformat (line_input, "ip6-nh %U", unformat_ip6_address, &ip6nh))
730         p_v6 = &ip6nh;
731       else if (unformat (line_input, "del"))
732         is_del = 1;
733       else
734         {
735           error = clib_error_return (0, "unknown input `%U'",
736                                      format_unformat_error, line_input);
737           goto done;
738         }
739     }
740
741   map_pre_resolve (p_v4, p_v6, is_del);
742
743 done:
744   unformat_free (line_input);
745
746   return error;
747 }
748 #endif
749
750 static clib_error_t *
751 map_icmp_relay_source_address_command_fn (vlib_main_t * vm,
752                                           unformat_input_t * input,
753                                           vlib_cli_command_t * cmd)
754 {
755   unformat_input_t _line_input, *line_input = &_line_input;
756   ip4_address_t icmp_src_address;
757   map_main_t *mm = &map_main;
758   clib_error_t *error = NULL;
759
760   mm->icmp4_src_address.as_u32 = 0;
761
762   /* Get a line of input. */
763   if (!unformat_user (input, unformat_line_input, line_input))
764     return 0;
765
766   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
767     {
768       if (unformat
769           (line_input, "%U", unformat_ip4_address, &icmp_src_address))
770         mm->icmp4_src_address = icmp_src_address;
771       else
772         {
773           error = clib_error_return (0, "unknown input `%U'",
774                                      format_unformat_error, line_input);
775           goto done;
776         }
777     }
778
779 done:
780   unformat_free (line_input);
781
782   return error;
783 }
784
785 static clib_error_t *
786 map_icmp_unreachables_command_fn (vlib_main_t * vm,
787                                   unformat_input_t * input,
788                                   vlib_cli_command_t * cmd)
789 {
790   unformat_input_t _line_input, *line_input = &_line_input;
791   map_main_t *mm = &map_main;
792   int num_m_args = 0;
793   clib_error_t *error = NULL;
794
795   /* Get a line of input. */
796   if (!unformat_user (input, unformat_line_input, line_input))
797     return 0;
798
799   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
800     {
801       num_m_args++;
802       if (unformat (line_input, "on"))
803         mm->icmp6_enabled = true;
804       else if (unformat (line_input, "off"))
805         mm->icmp6_enabled = false;
806       else
807         {
808           error = clib_error_return (0, "unknown input `%U'",
809                                      format_unformat_error, line_input);
810           goto done;
811         }
812     }
813
814
815   if (num_m_args != 1)
816     error = clib_error_return (0, "mandatory argument(s) missing");
817
818 done:
819   unformat_free (line_input);
820
821   return error;
822 }
823
824 static clib_error_t *
825 map_fragment_command_fn (vlib_main_t * vm,
826                          unformat_input_t * input, vlib_cli_command_t * cmd)
827 {
828   unformat_input_t _line_input, *line_input = &_line_input;
829   map_main_t *mm = &map_main;
830   clib_error_t *error = NULL;
831
832   /* Get a line of input. */
833   if (!unformat_user (input, unformat_line_input, line_input))
834     return 0;
835
836   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
837     {
838       if (unformat (line_input, "inner"))
839         mm->frag_inner = true;
840       else if (unformat (line_input, "outer"))
841         mm->frag_inner = false;
842       else
843         {
844           error = clib_error_return (0, "unknown input `%U'",
845                                      format_unformat_error, line_input);
846           goto done;
847         }
848     }
849
850 done:
851   unformat_free (line_input);
852
853   return error;
854 }
855
856 static clib_error_t *
857 map_fragment_df_command_fn (vlib_main_t * vm,
858                             unformat_input_t * input,
859                             vlib_cli_command_t * cmd)
860 {
861   unformat_input_t _line_input, *line_input = &_line_input;
862   map_main_t *mm = &map_main;
863   clib_error_t *error = NULL;
864
865   /* Get a line of input. */
866   if (!unformat_user (input, unformat_line_input, line_input))
867     return 0;
868
869   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
870     {
871       if (unformat (line_input, "on"))
872         mm->frag_ignore_df = true;
873       else if (unformat (line_input, "off"))
874         mm->frag_ignore_df = false;
875       else
876         {
877           error = clib_error_return (0, "unknown input `%U'",
878                                      format_unformat_error, line_input);
879           goto done;
880         }
881     }
882
883 done:
884   unformat_free (line_input);
885
886   return error;
887 }
888
889 static clib_error_t *
890 map_traffic_class_command_fn (vlib_main_t * vm,
891                               unformat_input_t * input,
892                               vlib_cli_command_t * cmd)
893 {
894   unformat_input_t _line_input, *line_input = &_line_input;
895   map_main_t *mm = &map_main;
896   u32 tc = 0;
897   clib_error_t *error = NULL;
898
899   mm->tc_copy = false;
900
901   /* Get a line of input. */
902   if (!unformat_user (input, unformat_line_input, line_input))
903     return 0;
904
905   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
906     {
907       if (unformat (line_input, "copy"))
908         mm->tc_copy = true;
909       else if (unformat (line_input, "%x", &tc))
910         mm->tc = tc & 0xff;
911       else
912         {
913           error = clib_error_return (0, "unknown input `%U'",
914                                      format_unformat_error, line_input);
915           goto done;
916         }
917     }
918
919 done:
920   unformat_free (line_input);
921
922   return error;
923 }
924
925 static char *
926 map_flags_to_string (u32 flags)
927 {
928   if (flags & MAP_DOMAIN_RFC6052)
929     return "rfc6052";
930   if (flags & MAP_DOMAIN_PREFIX)
931     return "prefix";
932   if (flags & MAP_DOMAIN_TRANSLATION)
933     return "map-t";
934   return "";
935 }
936
937 static u8 *
938 format_map_domain (u8 * s, va_list * args)
939 {
940   map_domain_t *d = va_arg (*args, map_domain_t *);
941   bool counters = va_arg (*args, int);
942   map_main_t *mm = &map_main;
943   ip6_address_t ip6_prefix;
944
945   if (d->rules)
946     clib_memset (&ip6_prefix, 0, sizeof (ip6_prefix));
947   else
948     ip6_prefix = d->ip6_prefix;
949
950   s = format (s,
951               "[%d] ip4-pfx %U/%d ip6-pfx %U/%d ip6-src %U/%d ea-bits-len %d "
952               "psid-offset %d psid-len %d mtu %d %s",
953               d - mm->domains,
954               format_ip4_address, &d->ip4_prefix, d->ip4_prefix_len,
955               format_ip6_address, &ip6_prefix, d->ip6_prefix_len,
956               format_ip6_address, &d->ip6_src, d->ip6_src_len,
957               d->ea_bits_len, d->psid_offset, d->psid_length, d->mtu,
958               map_flags_to_string (d->flags));
959
960   if (counters)
961     {
962       map_domain_counter_lock (mm);
963       vlib_counter_t v;
964       vlib_get_combined_counter (&mm->domain_counters[MAP_DOMAIN_COUNTER_TX],
965                                  d - mm->domains, &v);
966       s = format (s, "  TX: %lld/%lld", v.packets, v.bytes);
967       vlib_get_combined_counter (&mm->domain_counters[MAP_DOMAIN_COUNTER_RX],
968                                  d - mm->domains, &v);
969       s = format (s, "  RX: %lld/%lld", v.packets, v.bytes);
970       map_domain_counter_unlock (mm);
971     }
972   s = format (s, "\n");
973
974   if (d->rules)
975     {
976       int i;
977       ip6_address_t dst;
978       for (i = 0; i < (0x1 << d->psid_length); i++)
979         {
980           dst = d->rules[i];
981           if (dst.as_u64[0] == 0 && dst.as_u64[1] == 0)
982             continue;
983           s = format (s,
984                       " rule psid: %d ip6-dst %U\n", i, format_ip6_address,
985                       &dst);
986         }
987     }
988   return s;
989 }
990
991 static u8 *
992 format_map_ip4_reass (u8 * s, va_list * args)
993 {
994   map_main_t *mm = &map_main;
995   map_ip4_reass_t *r = va_arg (*args, map_ip4_reass_t *);
996   map_ip4_reass_key_t *k = &r->key;
997   f64 now = vlib_time_now (mm->vlib_main);
998   f64 lifetime = (((f64) mm->ip4_reass_conf_lifetime_ms) / 1000);
999   f64 dt = (r->ts + lifetime > now) ? (r->ts + lifetime - now) : -1;
1000   s = format (s,
1001               "ip4-reass src=%U  dst=%U  protocol=%d  identifier=%d  port=%d  lifetime=%.3lf\n",
1002               format_ip4_address, &k->src.as_u8, format_ip4_address,
1003               &k->dst.as_u8, k->protocol,
1004               clib_net_to_host_u16 (k->fragment_id),
1005               (r->port >= 0) ? clib_net_to_host_u16 (r->port) : -1, dt);
1006   return s;
1007 }
1008
1009 static u8 *
1010 format_map_ip6_reass (u8 * s, va_list * args)
1011 {
1012   map_main_t *mm = &map_main;
1013   map_ip6_reass_t *r = va_arg (*args, map_ip6_reass_t *);
1014   map_ip6_reass_key_t *k = &r->key;
1015   f64 now = vlib_time_now (mm->vlib_main);
1016   f64 lifetime = (((f64) mm->ip6_reass_conf_lifetime_ms) / 1000);
1017   f64 dt = (r->ts + lifetime > now) ? (r->ts + lifetime - now) : -1;
1018   s = format (s,
1019               "ip6-reass src=%U  dst=%U  protocol=%d  identifier=%d  lifetime=%.3lf\n",
1020               format_ip6_address, &k->src.as_u8, format_ip6_address,
1021               &k->dst.as_u8, k->protocol,
1022               clib_net_to_host_u32 (k->fragment_id), dt);
1023   return s;
1024 }
1025
1026 static clib_error_t *
1027 show_map_domain_command_fn (vlib_main_t * vm, unformat_input_t * input,
1028                             vlib_cli_command_t * cmd)
1029 {
1030   unformat_input_t _line_input, *line_input = &_line_input;
1031   map_main_t *mm = &map_main;
1032   map_domain_t *d;
1033   bool counters = false;
1034   u32 map_domain_index = ~0;
1035   clib_error_t *error = NULL;
1036
1037   /* Get a line of input. */
1038   if (!unformat_user (input, unformat_line_input, line_input))
1039     return 0;
1040
1041   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
1042     {
1043       if (unformat (line_input, "counters"))
1044         counters = true;
1045       else if (unformat (line_input, "index %d", &map_domain_index))
1046         ;
1047       else
1048         {
1049           error = clib_error_return (0, "unknown input `%U'",
1050                                      format_unformat_error, line_input);
1051           goto done;
1052         }
1053     }
1054
1055   if (pool_elts (mm->domains) == 0)
1056     vlib_cli_output (vm, "No MAP domains are configured...");
1057
1058   if (map_domain_index == ~0)
1059     {
1060     /* *INDENT-OFF* */
1061     pool_foreach(d, mm->domains, ({vlib_cli_output(vm, "%U", format_map_domain, d, counters);}));
1062     /* *INDENT-ON* */
1063     }
1064   else
1065     {
1066       if (pool_is_free_index (mm->domains, map_domain_index))
1067         {
1068           error = clib_error_return (0, "MAP domain does not exists %d",
1069                                      map_domain_index);
1070           goto done;
1071         }
1072
1073       d = pool_elt_at_index (mm->domains, map_domain_index);
1074       vlib_cli_output (vm, "%U", format_map_domain, d, counters);
1075     }
1076
1077 done:
1078   unformat_free (line_input);
1079
1080   return error;
1081 }
1082
1083 static clib_error_t *
1084 show_map_fragments_command_fn (vlib_main_t * vm, unformat_input_t * input,
1085                                vlib_cli_command_t * cmd)
1086 {
1087   map_main_t *mm = &map_main;
1088   map_ip4_reass_t *f4;
1089   map_ip6_reass_t *f6;
1090
1091   /* *INDENT-OFF* */
1092   pool_foreach(f4, mm->ip4_reass_pool, ({vlib_cli_output (vm, "%U", format_map_ip4_reass, f4);}));
1093   /* *INDENT-ON* */
1094   /* *INDENT-OFF* */
1095   pool_foreach(f6, mm->ip6_reass_pool, ({vlib_cli_output (vm, "%U", format_map_ip6_reass, f6);}));
1096   /* *INDENT-ON* */
1097   return (0);
1098 }
1099
1100 u64
1101 map_error_counter_get (u32 node_index, map_error_t map_error)
1102 {
1103   vlib_main_t *vm = vlib_get_main ();
1104   vlib_node_runtime_t *error_node = vlib_node_get_runtime (vm, node_index);
1105   vlib_error_main_t *em = &vm->error_main;
1106   vlib_error_t e = error_node->errors[map_error];
1107   vlib_node_t *n = vlib_get_node (vm, node_index);
1108   u32 ci;
1109
1110   ci = vlib_error_get_code (e);
1111   ASSERT (ci < n->n_errors);
1112   ci += n->error_heap_index;
1113
1114   return (em->counters[ci]);
1115 }
1116
1117 static clib_error_t *
1118 show_map_stats_command_fn (vlib_main_t * vm, unformat_input_t * input,
1119                            vlib_cli_command_t * cmd)
1120 {
1121   map_main_t *mm = &map_main;
1122   map_domain_t *d;
1123   int domains = 0, rules = 0, domaincount = 0, rulecount = 0;
1124   if (pool_elts (mm->domains) == 0)
1125     {
1126       vlib_cli_output (vm, "No MAP domains are configured...");
1127       return 0;
1128     }
1129
1130   /* *INDENT-OFF* */
1131   pool_foreach(d, mm->domains, ({
1132     if (d->rules) {
1133       rulecount+= 0x1 << d->psid_length;
1134       rules += sizeof(ip6_address_t) * 0x1 << d->psid_length;
1135     }
1136     domains += sizeof(*d);
1137     domaincount++;
1138   }));
1139   /* *INDENT-ON* */
1140
1141   vlib_cli_output (vm, "MAP domains structure: %d\n", sizeof (map_domain_t));
1142   vlib_cli_output (vm, "MAP domains: %d (%d bytes)\n", domaincount, domains);
1143   vlib_cli_output (vm, "MAP rules: %d (%d bytes)\n", rulecount, rules);
1144   vlib_cli_output (vm, "Total: %d bytes)\n", rules + domains);
1145
1146 #if MAP_SKIP_IP6_LOOKUP
1147   vlib_cli_output (vm,
1148                    "MAP pre-resolve: IP6 next-hop: %U, IP4 next-hop: %U\n",
1149                    format_map_pre_resolve, &pre_resolved[FIB_PROTOCOL_IP6],
1150                    format_map_pre_resolve, &pre_resolved[FIB_PROTOCOL_IP4]);
1151
1152 #endif
1153
1154   if (mm->tc_copy)
1155     vlib_cli_output (vm, "MAP traffic-class: copy");
1156   else
1157     vlib_cli_output (vm, "MAP traffic-class: %x", mm->tc);
1158
1159   vlib_cli_output (vm,
1160                    "MAP IPv6 inbound security check: %s, fragmented packet security check: %s",
1161                    mm->sec_check ? "enabled" : "disabled",
1162                    mm->sec_check_frag ? "enabled" : "disabled");
1163
1164   vlib_cli_output (vm, "ICMP-relay IPv4 source address: %U\n",
1165                    format_ip4_address, &mm->icmp4_src_address);
1166   vlib_cli_output (vm, "ICMP6 unreachables sent for unmatched packets: %s\n",
1167                    mm->icmp6_enabled ? "enabled" : "disabled");
1168   vlib_cli_output (vm, "Inner fragmentation: %s\n",
1169                    mm->frag_inner ? "enabled" : "disabled");
1170   vlib_cli_output (vm, "Fragment packets regardless of DF flag: %s\n",
1171                    mm->frag_ignore_df ? "enabled" : "disabled");
1172
1173   /*
1174    * Counters
1175    */
1176   vlib_combined_counter_main_t *cm = mm->domain_counters;
1177   u64 total_pkts[MAP_N_DOMAIN_COUNTER];
1178   u64 total_bytes[MAP_N_DOMAIN_COUNTER];
1179   int which, i;
1180   vlib_counter_t v;
1181
1182   clib_memset (total_pkts, 0, sizeof (total_pkts));
1183   clib_memset (total_bytes, 0, sizeof (total_bytes));
1184
1185   map_domain_counter_lock (mm);
1186   vec_foreach (cm, mm->domain_counters)
1187   {
1188     which = cm - mm->domain_counters;
1189
1190     for (i = 0; i < vlib_combined_counter_n_counters (cm); i++)
1191       {
1192         vlib_get_combined_counter (cm, i, &v);
1193         total_pkts[which] += v.packets;
1194         total_bytes[which] += v.bytes;
1195       }
1196   }
1197   map_domain_counter_unlock (mm);
1198
1199   vlib_cli_output (vm, "Encapsulated packets: %lld bytes: %lld\n",
1200                    total_pkts[MAP_DOMAIN_COUNTER_TX],
1201                    total_bytes[MAP_DOMAIN_COUNTER_TX]);
1202   vlib_cli_output (vm, "Decapsulated packets: %lld bytes: %lld\n",
1203                    total_pkts[MAP_DOMAIN_COUNTER_RX],
1204                    total_bytes[MAP_DOMAIN_COUNTER_RX]);
1205
1206   vlib_cli_output (vm, "ICMP relayed packets: %d\n",
1207                    vlib_get_simple_counter (&mm->icmp_relayed, 0));
1208
1209   return 0;
1210 }
1211
1212 static clib_error_t *
1213 map_params_reass_command_fn (vlib_main_t * vm, unformat_input_t * input,
1214                              vlib_cli_command_t * cmd)
1215 {
1216   unformat_input_t _line_input, *line_input = &_line_input;
1217   u32 lifetime = ~0;
1218   f64 ht_ratio = (MAP_IP4_REASS_CONF_HT_RATIO_MAX + 1);
1219   u32 pool_size = ~0;
1220   u64 buffers = ~(0ull);
1221   u8 ip4 = 0, ip6 = 0;
1222
1223   if (!unformat_user (input, unformat_line_input, line_input))
1224     return 0;
1225
1226   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
1227     {
1228       if (unformat (line_input, "lifetime %u", &lifetime))
1229         ;
1230       else if (unformat (line_input, "ht-ratio %lf", &ht_ratio))
1231         ;
1232       else if (unformat (line_input, "pool-size %u", &pool_size))
1233         ;
1234       else if (unformat (line_input, "buffers %llu", &buffers))
1235         ;
1236       else if (unformat (line_input, "ip4"))
1237         ip4 = 1;
1238       else if (unformat (line_input, "ip6"))
1239         ip6 = 1;
1240       else
1241         {
1242           unformat_free (line_input);
1243           return clib_error_return (0, "invalid input");
1244         }
1245     }
1246   unformat_free (line_input);
1247
1248   if (!ip4 && !ip6)
1249     return clib_error_return (0, "must specify ip4 and/or ip6");
1250
1251   if (ip4)
1252     {
1253       if (pool_size != ~0 && pool_size > MAP_IP4_REASS_CONF_POOL_SIZE_MAX)
1254         return clib_error_return (0, "invalid ip4-reass pool-size ( > %d)",
1255                                   MAP_IP4_REASS_CONF_POOL_SIZE_MAX);
1256       if (ht_ratio != (MAP_IP4_REASS_CONF_HT_RATIO_MAX + 1)
1257           && ht_ratio > MAP_IP4_REASS_CONF_HT_RATIO_MAX)
1258         return clib_error_return (0, "invalid ip4-reass ht-ratio ( > %d)",
1259                                   MAP_IP4_REASS_CONF_HT_RATIO_MAX);
1260       if (lifetime != ~0 && lifetime > MAP_IP4_REASS_CONF_LIFETIME_MAX)
1261         return clib_error_return (0, "invalid ip4-reass lifetime ( > %d)",
1262                                   MAP_IP4_REASS_CONF_LIFETIME_MAX);
1263       if (buffers != ~(0ull) && buffers > MAP_IP4_REASS_CONF_BUFFERS_MAX)
1264         return clib_error_return (0, "invalid ip4-reass buffers ( > %ld)",
1265                                   MAP_IP4_REASS_CONF_BUFFERS_MAX);
1266     }
1267
1268   if (ip6)
1269     {
1270       if (pool_size != ~0 && pool_size > MAP_IP6_REASS_CONF_POOL_SIZE_MAX)
1271         return clib_error_return (0, "invalid ip6-reass pool-size ( > %d)",
1272                                   MAP_IP6_REASS_CONF_POOL_SIZE_MAX);
1273       if (ht_ratio != (MAP_IP4_REASS_CONF_HT_RATIO_MAX + 1)
1274           && ht_ratio > MAP_IP6_REASS_CONF_HT_RATIO_MAX)
1275         return clib_error_return (0, "invalid ip6-reass ht-log2len ( > %d)",
1276                                   MAP_IP6_REASS_CONF_HT_RATIO_MAX);
1277       if (lifetime != ~0 && lifetime > MAP_IP6_REASS_CONF_LIFETIME_MAX)
1278         return clib_error_return (0, "invalid ip6-reass lifetime ( > %d)",
1279                                   MAP_IP6_REASS_CONF_LIFETIME_MAX);
1280       if (buffers != ~(0ull) && buffers > MAP_IP6_REASS_CONF_BUFFERS_MAX)
1281         return clib_error_return (0, "invalid ip6-reass buffers ( > %ld)",
1282                                   MAP_IP6_REASS_CONF_BUFFERS_MAX);
1283     }
1284
1285   if (ip4)
1286     {
1287       u32 reass = 0, packets = 0;
1288       if (pool_size != ~0)
1289         {
1290           if (map_ip4_reass_conf_pool_size (pool_size, &reass, &packets))
1291             {
1292               vlib_cli_output (vm, "Could not set ip4-reass pool-size");
1293             }
1294           else
1295             {
1296               vlib_cli_output (vm,
1297                                "Setting ip4-reass pool-size (destroyed-reassembly=%u , dropped-fragments=%u)",
1298                                reass, packets);
1299             }
1300         }
1301       if (ht_ratio != (MAP_IP4_REASS_CONF_HT_RATIO_MAX + 1))
1302         {
1303           if (map_ip4_reass_conf_ht_ratio (ht_ratio, &reass, &packets))
1304             {
1305               vlib_cli_output (vm, "Could not set ip4-reass ht-log2len");
1306             }
1307           else
1308             {
1309               vlib_cli_output (vm,
1310                                "Setting ip4-reass ht-log2len (destroyed-reassembly=%u , dropped-fragments=%u)",
1311                                reass, packets);
1312             }
1313         }
1314       if (lifetime != ~0)
1315         {
1316           if (map_ip4_reass_conf_lifetime (lifetime))
1317             vlib_cli_output (vm, "Could not set ip4-reass lifetime");
1318           else
1319             vlib_cli_output (vm, "Setting ip4-reass lifetime");
1320         }
1321       if (buffers != ~(0ull))
1322         {
1323           if (map_ip4_reass_conf_buffers (buffers))
1324             vlib_cli_output (vm, "Could not set ip4-reass buffers");
1325           else
1326             vlib_cli_output (vm, "Setting ip4-reass buffers");
1327         }
1328
1329       if (map_main.ip4_reass_conf_buffers >
1330           map_main.ip4_reass_conf_pool_size *
1331           MAP_IP4_REASS_MAX_FRAGMENTS_PER_REASSEMBLY)
1332         {
1333           vlib_cli_output (vm,
1334                            "Note: 'ip4-reass buffers' > pool-size * max-fragments-per-reassembly.");
1335         }
1336     }
1337
1338   if (ip6)
1339     {
1340       u32 reass = 0, packets = 0;
1341       if (pool_size != ~0)
1342         {
1343           if (map_ip6_reass_conf_pool_size (pool_size, &reass, &packets))
1344             {
1345               vlib_cli_output (vm, "Could not set ip6-reass pool-size");
1346             }
1347           else
1348             {
1349               vlib_cli_output (vm,
1350                                "Setting ip6-reass pool-size (destroyed-reassembly=%u , dropped-fragments=%u)",
1351                                reass, packets);
1352             }
1353         }
1354       if (ht_ratio != (MAP_IP4_REASS_CONF_HT_RATIO_MAX + 1))
1355         {
1356           if (map_ip6_reass_conf_ht_ratio (ht_ratio, &reass, &packets))
1357             {
1358               vlib_cli_output (vm, "Could not set ip6-reass ht-log2len");
1359             }
1360           else
1361             {
1362               vlib_cli_output (vm,
1363                                "Setting ip6-reass ht-log2len (destroyed-reassembly=%u , dropped-fragments=%u)",
1364                                reass, packets);
1365             }
1366         }
1367       if (lifetime != ~0)
1368         {
1369           if (map_ip6_reass_conf_lifetime (lifetime))
1370             vlib_cli_output (vm, "Could not set ip6-reass lifetime");
1371           else
1372             vlib_cli_output (vm, "Setting ip6-reass lifetime");
1373         }
1374       if (buffers != ~(0ull))
1375         {
1376           if (map_ip6_reass_conf_buffers (buffers))
1377             vlib_cli_output (vm, "Could not set ip6-reass buffers");
1378           else
1379             vlib_cli_output (vm, "Setting ip6-reass buffers");
1380         }
1381
1382       if (map_main.ip6_reass_conf_buffers >
1383           map_main.ip6_reass_conf_pool_size *
1384           MAP_IP6_REASS_MAX_FRAGMENTS_PER_REASSEMBLY)
1385         {
1386           vlib_cli_output (vm,
1387                            "Note: 'ip6-reass buffers' > pool-size * max-fragments-per-reassembly.");
1388         }
1389     }
1390
1391   return 0;
1392 }
1393
1394
1395 /*
1396  * packet trace format function
1397  */
1398 u8 *
1399 format_map_trace (u8 * s, va_list * args)
1400 {
1401   CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
1402   CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
1403   map_trace_t *t = va_arg (*args, map_trace_t *);
1404   u32 map_domain_index = t->map_domain_index;
1405   u16 port = t->port;
1406
1407   s =
1408     format (s, "MAP domain index: %d L4 port: %u", map_domain_index,
1409             clib_net_to_host_u16 (port));
1410
1411   return s;
1412 }
1413
1414 static_always_inline map_ip4_reass_t *
1415 map_ip4_reass_lookup (map_ip4_reass_key_t * k, u32 bucket, f64 now)
1416 {
1417   map_main_t *mm = &map_main;
1418   u32 ri = mm->ip4_reass_hash_table[bucket];
1419   while (ri != MAP_REASS_INDEX_NONE)
1420     {
1421       map_ip4_reass_t *r = pool_elt_at_index (mm->ip4_reass_pool, ri);
1422       if (r->key.as_u64[0] == k->as_u64[0] &&
1423           r->key.as_u64[1] == k->as_u64[1] &&
1424           now < r->ts + (((f64) mm->ip4_reass_conf_lifetime_ms) / 1000))
1425         {
1426           return r;
1427         }
1428       ri = r->bucket_next;
1429     }
1430   return NULL;
1431 }
1432
1433 #define map_ip4_reass_pool_index(r) (r - map_main.ip4_reass_pool)
1434
1435 void
1436 map_ip4_reass_free (map_ip4_reass_t * r, u32 ** pi_to_drop)
1437 {
1438   map_main_t *mm = &map_main;
1439   map_ip4_reass_get_fragments (r, pi_to_drop);
1440
1441   // Unlink in hash bucket
1442   map_ip4_reass_t *r2 = NULL;
1443   u32 r2i = mm->ip4_reass_hash_table[r->bucket];
1444   while (r2i != map_ip4_reass_pool_index (r))
1445     {
1446       ASSERT (r2i != MAP_REASS_INDEX_NONE);
1447       r2 = pool_elt_at_index (mm->ip4_reass_pool, r2i);
1448       r2i = r2->bucket_next;
1449     }
1450   if (r2)
1451     {
1452       r2->bucket_next = r->bucket_next;
1453     }
1454   else
1455     {
1456       mm->ip4_reass_hash_table[r->bucket] = r->bucket_next;
1457     }
1458
1459   // Unlink in list
1460   if (r->fifo_next == map_ip4_reass_pool_index (r))
1461     {
1462       mm->ip4_reass_fifo_last = MAP_REASS_INDEX_NONE;
1463     }
1464   else
1465     {
1466       if (mm->ip4_reass_fifo_last == map_ip4_reass_pool_index (r))
1467         mm->ip4_reass_fifo_last = r->fifo_prev;
1468       pool_elt_at_index (mm->ip4_reass_pool, r->fifo_prev)->fifo_next =
1469         r->fifo_next;
1470       pool_elt_at_index (mm->ip4_reass_pool, r->fifo_next)->fifo_prev =
1471         r->fifo_prev;
1472     }
1473
1474   pool_put (mm->ip4_reass_pool, r);
1475   mm->ip4_reass_allocated--;
1476 }
1477
1478 map_ip4_reass_t *
1479 map_ip4_reass_get (u32 src, u32 dst, u16 fragment_id,
1480                    u8 protocol, u32 ** pi_to_drop)
1481 {
1482   map_ip4_reass_t *r;
1483   map_main_t *mm = &map_main;
1484   map_ip4_reass_key_t k = {.src.data_u32 = src,
1485     .dst.data_u32 = dst,
1486     .fragment_id = fragment_id,
1487     .protocol = protocol
1488   };
1489
1490   u32 h = 0;
1491 #ifdef clib_crc32c_uses_intrinsics
1492   h = clib_crc32c ((u8 *) k.as_u32, 16);
1493 #else
1494   u64 tmp = k.as_u32[0] ^ k.as_u32[1] ^ k.as_u32[2] ^ k.as_u32[3];
1495   h = clib_xxhash (tmp);
1496 #endif
1497   h = h >> (32 - mm->ip4_reass_ht_log2len);
1498
1499   f64 now = vlib_time_now (mm->vlib_main);
1500
1501   //Cache garbage collection
1502   while (mm->ip4_reass_fifo_last != MAP_REASS_INDEX_NONE)
1503     {
1504       map_ip4_reass_t *last =
1505         pool_elt_at_index (mm->ip4_reass_pool, mm->ip4_reass_fifo_last);
1506       if (last->ts + (((f64) mm->ip4_reass_conf_lifetime_ms) / 1000) < now)
1507         map_ip4_reass_free (last, pi_to_drop);
1508       else
1509         break;
1510     }
1511
1512   if ((r = map_ip4_reass_lookup (&k, h, now)))
1513     return r;
1514
1515   if (mm->ip4_reass_allocated >= mm->ip4_reass_conf_pool_size)
1516     return NULL;
1517
1518   pool_get (mm->ip4_reass_pool, r);
1519   mm->ip4_reass_allocated++;
1520   int i;
1521   for (i = 0; i < MAP_IP4_REASS_MAX_FRAGMENTS_PER_REASSEMBLY; i++)
1522     r->fragments[i] = ~0;
1523
1524   u32 ri = map_ip4_reass_pool_index (r);
1525
1526   //Link in new bucket
1527   r->bucket = h;
1528   r->bucket_next = mm->ip4_reass_hash_table[h];
1529   mm->ip4_reass_hash_table[h] = ri;
1530
1531   //Link in fifo
1532   if (mm->ip4_reass_fifo_last != MAP_REASS_INDEX_NONE)
1533     {
1534       r->fifo_next =
1535         pool_elt_at_index (mm->ip4_reass_pool,
1536                            mm->ip4_reass_fifo_last)->fifo_next;
1537       r->fifo_prev = mm->ip4_reass_fifo_last;
1538       pool_elt_at_index (mm->ip4_reass_pool, r->fifo_prev)->fifo_next = ri;
1539       pool_elt_at_index (mm->ip4_reass_pool, r->fifo_next)->fifo_prev = ri;
1540     }
1541   else
1542     {
1543       r->fifo_next = r->fifo_prev = ri;
1544       mm->ip4_reass_fifo_last = ri;
1545     }
1546
1547   //Set other fields
1548   r->ts = now;
1549   r->key = k;
1550   r->port = -1;
1551 #ifdef MAP_IP4_REASS_COUNT_BYTES
1552   r->expected_total = 0xffff;
1553   r->forwarded = 0;
1554 #endif
1555
1556   return r;
1557 }
1558
1559 int
1560 map_ip4_reass_add_fragment (map_ip4_reass_t * r, u32 pi)
1561 {
1562   if (map_main.ip4_reass_buffered_counter >= map_main.ip4_reass_conf_buffers)
1563     return -1;
1564
1565   int i;
1566   for (i = 0; i < MAP_IP4_REASS_MAX_FRAGMENTS_PER_REASSEMBLY; i++)
1567     if (r->fragments[i] == ~0)
1568       {
1569         r->fragments[i] = pi;
1570         map_main.ip4_reass_buffered_counter++;
1571         return 0;
1572       }
1573   return -1;
1574 }
1575
1576 static_always_inline map_ip6_reass_t *
1577 map_ip6_reass_lookup (map_ip6_reass_key_t * k, u32 bucket, f64 now)
1578 {
1579   map_main_t *mm = &map_main;
1580   u32 ri = mm->ip6_reass_hash_table[bucket];
1581   while (ri != MAP_REASS_INDEX_NONE)
1582     {
1583       map_ip6_reass_t *r = pool_elt_at_index (mm->ip6_reass_pool, ri);
1584       if (now < r->ts + (((f64) mm->ip6_reass_conf_lifetime_ms) / 1000) &&
1585           r->key.as_u64[0] == k->as_u64[0] &&
1586           r->key.as_u64[1] == k->as_u64[1] &&
1587           r->key.as_u64[2] == k->as_u64[2] &&
1588           r->key.as_u64[3] == k->as_u64[3] &&
1589           r->key.as_u64[4] == k->as_u64[4])
1590         return r;
1591       ri = r->bucket_next;
1592     }
1593   return NULL;
1594 }
1595
1596 #define map_ip6_reass_pool_index(r) (r - map_main.ip6_reass_pool)
1597
1598 void
1599 map_ip6_reass_free (map_ip6_reass_t * r, u32 ** pi_to_drop)
1600 {
1601   map_main_t *mm = &map_main;
1602   int i;
1603   for (i = 0; i < MAP_IP6_REASS_MAX_FRAGMENTS_PER_REASSEMBLY; i++)
1604     if (r->fragments[i].pi != ~0)
1605       {
1606         vec_add1 (*pi_to_drop, r->fragments[i].pi);
1607         r->fragments[i].pi = ~0;
1608         map_main.ip6_reass_buffered_counter--;
1609       }
1610
1611   // Unlink in hash bucket
1612   map_ip6_reass_t *r2 = NULL;
1613   u32 r2i = mm->ip6_reass_hash_table[r->bucket];
1614   while (r2i != map_ip6_reass_pool_index (r))
1615     {
1616       ASSERT (r2i != MAP_REASS_INDEX_NONE);
1617       r2 = pool_elt_at_index (mm->ip6_reass_pool, r2i);
1618       r2i = r2->bucket_next;
1619     }
1620   if (r2)
1621     {
1622       r2->bucket_next = r->bucket_next;
1623     }
1624   else
1625     {
1626       mm->ip6_reass_hash_table[r->bucket] = r->bucket_next;
1627     }
1628
1629   // Unlink in list
1630   if (r->fifo_next == map_ip6_reass_pool_index (r))
1631     {
1632       //Single element in the list, list is now empty
1633       mm->ip6_reass_fifo_last = MAP_REASS_INDEX_NONE;
1634     }
1635   else
1636     {
1637       if (mm->ip6_reass_fifo_last == map_ip6_reass_pool_index (r))      //First element
1638         mm->ip6_reass_fifo_last = r->fifo_prev;
1639       pool_elt_at_index (mm->ip6_reass_pool, r->fifo_prev)->fifo_next =
1640         r->fifo_next;
1641       pool_elt_at_index (mm->ip6_reass_pool, r->fifo_next)->fifo_prev =
1642         r->fifo_prev;
1643     }
1644
1645   // Free from pool if necessary
1646   pool_put (mm->ip6_reass_pool, r);
1647   mm->ip6_reass_allocated--;
1648 }
1649
1650 map_ip6_reass_t *
1651 map_ip6_reass_get (ip6_address_t * src, ip6_address_t * dst, u32 fragment_id,
1652                    u8 protocol, u32 ** pi_to_drop)
1653 {
1654   map_ip6_reass_t *r;
1655   map_main_t *mm = &map_main;
1656   map_ip6_reass_key_t k = {
1657     .src = *src,
1658     .dst = *dst,
1659     .fragment_id = fragment_id,
1660     .protocol = protocol
1661   };
1662
1663   u32 h = 0;
1664   int i;
1665
1666 #ifdef clib_crc32c_uses_intrinsics
1667   h = clib_crc32c ((u8 *) k.as_u32, 40);
1668 #else
1669   u64 tmp =
1670     k.as_u64[0] ^ k.as_u64[1] ^ k.as_u64[2] ^ k.as_u64[3] ^ k.as_u64[4];
1671   h = clib_xxhash (tmp);
1672 #endif
1673
1674   h = h >> (32 - mm->ip6_reass_ht_log2len);
1675
1676   f64 now = vlib_time_now (mm->vlib_main);
1677
1678   //Cache garbage collection
1679   while (mm->ip6_reass_fifo_last != MAP_REASS_INDEX_NONE)
1680     {
1681       map_ip6_reass_t *last =
1682         pool_elt_at_index (mm->ip6_reass_pool, mm->ip6_reass_fifo_last);
1683       if (last->ts + (((f64) mm->ip6_reass_conf_lifetime_ms) / 1000) < now)
1684         map_ip6_reass_free (last, pi_to_drop);
1685       else
1686         break;
1687     }
1688
1689   if ((r = map_ip6_reass_lookup (&k, h, now)))
1690     return r;
1691
1692   if (mm->ip6_reass_allocated >= mm->ip6_reass_conf_pool_size)
1693     return NULL;
1694
1695   pool_get (mm->ip6_reass_pool, r);
1696   mm->ip6_reass_allocated++;
1697   for (i = 0; i < MAP_IP6_REASS_MAX_FRAGMENTS_PER_REASSEMBLY; i++)
1698     {
1699       r->fragments[i].pi = ~0;
1700       r->fragments[i].next_data_len = 0;
1701       r->fragments[i].next_data_offset = 0;
1702     }
1703
1704   u32 ri = map_ip6_reass_pool_index (r);
1705
1706   //Link in new bucket
1707   r->bucket = h;
1708   r->bucket_next = mm->ip6_reass_hash_table[h];
1709   mm->ip6_reass_hash_table[h] = ri;
1710
1711   //Link in fifo
1712   if (mm->ip6_reass_fifo_last != MAP_REASS_INDEX_NONE)
1713     {
1714       r->fifo_next =
1715         pool_elt_at_index (mm->ip6_reass_pool,
1716                            mm->ip6_reass_fifo_last)->fifo_next;
1717       r->fifo_prev = mm->ip6_reass_fifo_last;
1718       pool_elt_at_index (mm->ip6_reass_pool, r->fifo_prev)->fifo_next = ri;
1719       pool_elt_at_index (mm->ip6_reass_pool, r->fifo_next)->fifo_prev = ri;
1720     }
1721   else
1722     {
1723       r->fifo_next = r->fifo_prev = ri;
1724       mm->ip6_reass_fifo_last = ri;
1725     }
1726
1727   //Set other fields
1728   r->ts = now;
1729   r->key = k;
1730   r->ip4_header.ip_version_and_header_length = 0;
1731 #ifdef MAP_IP6_REASS_COUNT_BYTES
1732   r->expected_total = 0xffff;
1733   r->forwarded = 0;
1734 #endif
1735   return r;
1736 }
1737
1738 int
1739 map_ip6_reass_add_fragment (map_ip6_reass_t * r, u32 pi,
1740                             u16 data_offset, u16 next_data_offset,
1741                             u8 * data_start, u16 data_len)
1742 {
1743   map_ip6_fragment_t *f = NULL, *prev_f = NULL;
1744   u16 copied_len = (data_len > 20) ? 20 : data_len;
1745
1746   if (map_main.ip6_reass_buffered_counter >= map_main.ip6_reass_conf_buffers)
1747     return -1;
1748
1749   //Lookup for fragments for the current buffer
1750   //and the one before that
1751   int i;
1752   for (i = 0; i < MAP_IP6_REASS_MAX_FRAGMENTS_PER_REASSEMBLY; i++)
1753     {
1754       if (data_offset && r->fragments[i].next_data_offset == data_offset)
1755         {
1756           prev_f = &r->fragments[i];    // This is buffer for previous packet
1757         }
1758       else if (r->fragments[i].next_data_offset == next_data_offset)
1759         {
1760           f = &r->fragments[i]; // This is a buffer for the current packet
1761         }
1762       else if (r->fragments[i].next_data_offset == 0)
1763         {                       //Available
1764           if (f == NULL)
1765             f = &r->fragments[i];
1766           else if (prev_f == NULL)
1767             prev_f = &r->fragments[i];
1768         }
1769     }
1770
1771   if (!f || f->pi != ~0)
1772     return -1;
1773
1774   if (data_offset)
1775     {
1776       if (!prev_f)
1777         return -1;
1778
1779       clib_memcpy_fast (prev_f->next_data, data_start, copied_len);
1780       prev_f->next_data_len = copied_len;
1781       prev_f->next_data_offset = data_offset;
1782     }
1783   else
1784     {
1785       if (((ip4_header_t *) data_start)->ip_version_and_header_length != 0x45)
1786         return -1;
1787
1788       if (r->ip4_header.ip_version_and_header_length == 0)
1789         clib_memcpy_fast (&r->ip4_header, data_start, sizeof (ip4_header_t));
1790     }
1791
1792   if (data_len > 20)
1793     {
1794       f->next_data_offset = next_data_offset;
1795       f->pi = pi;
1796       map_main.ip6_reass_buffered_counter++;
1797     }
1798   return 0;
1799 }
1800
1801 void
1802 map_ip4_reass_reinit (u32 * trashed_reass, u32 * dropped_packets)
1803 {
1804   map_main_t *mm = &map_main;
1805   int i;
1806
1807   if (dropped_packets)
1808     *dropped_packets = mm->ip4_reass_buffered_counter;
1809   if (trashed_reass)
1810     *trashed_reass = mm->ip4_reass_allocated;
1811   if (mm->ip4_reass_fifo_last != MAP_REASS_INDEX_NONE)
1812     {
1813       u16 ri = mm->ip4_reass_fifo_last;
1814       do
1815         {
1816           map_ip4_reass_t *r = pool_elt_at_index (mm->ip4_reass_pool, ri);
1817           for (i = 0; i < MAP_IP4_REASS_MAX_FRAGMENTS_PER_REASSEMBLY; i++)
1818             if (r->fragments[i] != ~0)
1819               map_ip4_drop_pi (r->fragments[i]);
1820
1821           ri = r->fifo_next;
1822           pool_put (mm->ip4_reass_pool, r);
1823         }
1824       while (ri != mm->ip4_reass_fifo_last);
1825     }
1826
1827   vec_free (mm->ip4_reass_hash_table);
1828   vec_resize (mm->ip4_reass_hash_table, 1 << mm->ip4_reass_ht_log2len);
1829   for (i = 0; i < (1 << mm->ip4_reass_ht_log2len); i++)
1830     mm->ip4_reass_hash_table[i] = MAP_REASS_INDEX_NONE;
1831   pool_free (mm->ip4_reass_pool);
1832   pool_alloc (mm->ip4_reass_pool, mm->ip4_reass_conf_pool_size);
1833
1834   mm->ip4_reass_allocated = 0;
1835   mm->ip4_reass_fifo_last = MAP_REASS_INDEX_NONE;
1836   mm->ip4_reass_buffered_counter = 0;
1837 }
1838
1839 u8
1840 map_get_ht_log2len (f32 ht_ratio, u16 pool_size)
1841 {
1842   u32 desired_size = (u32) (pool_size * ht_ratio);
1843   u8 i;
1844   for (i = 1; i < 31; i++)
1845     if ((1 << i) >= desired_size)
1846       return i;
1847   return 4;
1848 }
1849
1850 int
1851 map_ip4_reass_conf_ht_ratio (f32 ht_ratio, u32 * trashed_reass,
1852                              u32 * dropped_packets)
1853 {
1854   map_main_t *mm = &map_main;
1855   if (ht_ratio > MAP_IP4_REASS_CONF_HT_RATIO_MAX)
1856     return -1;
1857
1858   map_ip4_reass_lock ();
1859   mm->ip4_reass_conf_ht_ratio = ht_ratio;
1860   mm->ip4_reass_ht_log2len =
1861     map_get_ht_log2len (ht_ratio, mm->ip4_reass_conf_pool_size);
1862   map_ip4_reass_reinit (trashed_reass, dropped_packets);
1863   map_ip4_reass_unlock ();
1864   return 0;
1865 }
1866
1867 int
1868 map_ip4_reass_conf_pool_size (u16 pool_size, u32 * trashed_reass,
1869                               u32 * dropped_packets)
1870 {
1871   map_main_t *mm = &map_main;
1872   if (pool_size > MAP_IP4_REASS_CONF_POOL_SIZE_MAX)
1873     return -1;
1874
1875   map_ip4_reass_lock ();
1876   mm->ip4_reass_conf_pool_size = pool_size;
1877   map_ip4_reass_reinit (trashed_reass, dropped_packets);
1878   map_ip4_reass_unlock ();
1879   return 0;
1880 }
1881
1882 int
1883 map_ip4_reass_conf_lifetime (u16 lifetime_ms)
1884 {
1885   map_main.ip4_reass_conf_lifetime_ms = lifetime_ms;
1886   return 0;
1887 }
1888
1889 int
1890 map_ip4_reass_conf_buffers (u32 buffers)
1891 {
1892   map_main.ip4_reass_conf_buffers = buffers;
1893   return 0;
1894 }
1895
1896 void
1897 map_ip6_reass_reinit (u32 * trashed_reass, u32 * dropped_packets)
1898 {
1899   map_main_t *mm = &map_main;
1900   if (dropped_packets)
1901     *dropped_packets = mm->ip6_reass_buffered_counter;
1902   if (trashed_reass)
1903     *trashed_reass = mm->ip6_reass_allocated;
1904   int i;
1905   if (mm->ip6_reass_fifo_last != MAP_REASS_INDEX_NONE)
1906     {
1907       u16 ri = mm->ip6_reass_fifo_last;
1908       do
1909         {
1910           map_ip6_reass_t *r = pool_elt_at_index (mm->ip6_reass_pool, ri);
1911           for (i = 0; i < MAP_IP6_REASS_MAX_FRAGMENTS_PER_REASSEMBLY; i++)
1912             if (r->fragments[i].pi != ~0)
1913               map_ip6_drop_pi (r->fragments[i].pi);
1914
1915           ri = r->fifo_next;
1916           pool_put (mm->ip6_reass_pool, r);
1917         }
1918       while (ri != mm->ip6_reass_fifo_last);
1919       mm->ip6_reass_fifo_last = MAP_REASS_INDEX_NONE;
1920     }
1921
1922   vec_free (mm->ip6_reass_hash_table);
1923   vec_resize (mm->ip6_reass_hash_table, 1 << mm->ip6_reass_ht_log2len);
1924   for (i = 0; i < (1 << mm->ip6_reass_ht_log2len); i++)
1925     mm->ip6_reass_hash_table[i] = MAP_REASS_INDEX_NONE;
1926   pool_free (mm->ip6_reass_pool);
1927   pool_alloc (mm->ip6_reass_pool, mm->ip4_reass_conf_pool_size);
1928
1929   mm->ip6_reass_allocated = 0;
1930   mm->ip6_reass_buffered_counter = 0;
1931 }
1932
1933 int
1934 map_ip6_reass_conf_ht_ratio (f32 ht_ratio, u32 * trashed_reass,
1935                              u32 * dropped_packets)
1936 {
1937   map_main_t *mm = &map_main;
1938   if (ht_ratio > MAP_IP6_REASS_CONF_HT_RATIO_MAX)
1939     return -1;
1940
1941   map_ip6_reass_lock ();
1942   mm->ip6_reass_conf_ht_ratio = ht_ratio;
1943   mm->ip6_reass_ht_log2len =
1944     map_get_ht_log2len (ht_ratio, mm->ip6_reass_conf_pool_size);
1945   map_ip6_reass_reinit (trashed_reass, dropped_packets);
1946   map_ip6_reass_unlock ();
1947   return 0;
1948 }
1949
1950 int
1951 map_ip6_reass_conf_pool_size (u16 pool_size, u32 * trashed_reass,
1952                               u32 * dropped_packets)
1953 {
1954   map_main_t *mm = &map_main;
1955   if (pool_size > MAP_IP6_REASS_CONF_POOL_SIZE_MAX)
1956     return -1;
1957
1958   map_ip6_reass_lock ();
1959   mm->ip6_reass_conf_pool_size = pool_size;
1960   map_ip6_reass_reinit (trashed_reass, dropped_packets);
1961   map_ip6_reass_unlock ();
1962   return 0;
1963 }
1964
1965 int
1966 map_ip6_reass_conf_lifetime (u16 lifetime_ms)
1967 {
1968   map_main.ip6_reass_conf_lifetime_ms = lifetime_ms;
1969   return 0;
1970 }
1971
1972 int
1973 map_ip6_reass_conf_buffers (u32 buffers)
1974 {
1975   map_main.ip6_reass_conf_buffers = buffers;
1976   return 0;
1977 }
1978
1979 /* *INDENT-OFF* */
1980
1981 /*?
1982  * Configure MAP reassembly behaviour
1983  *
1984  * @cliexpar
1985  * @cliexstart{map params reassembly}
1986  * @cliexend
1987  ?*/
1988 VLIB_CLI_COMMAND(map_ip4_reass_lifetime_command, static) = {
1989   .path = "map params reassembly",
1990   .short_help = "map params reassembly [ip4 | ip6] [lifetime <lifetime-ms>] "
1991                 "[pool-size <pool-size>] [buffers <buffers>] "
1992                 "[ht-ratio <ht-ratio>]",
1993   .function = map_params_reass_command_fn,
1994 };
1995
1996 /*?
1997  * Set or copy the IP TOS/Traffic Class field
1998  *
1999  * @cliexpar
2000  * @cliexstart{map params traffic-class}
2001  *
2002  * This command is used to set the traffic-class field in translated
2003  * or encapsulated packets. If copy is specifed (the default) then the
2004  * traffic-class/TOS field is copied from the original packet to the
2005  * translated / encapsulating header.
2006  * @cliexend
2007  ?*/
2008 VLIB_CLI_COMMAND(map_traffic_class_command, static) = {
2009   .path = "map params traffic-class",
2010   .short_help = "map params traffic-class {0x0-0xff | copy}",
2011   .function = map_traffic_class_command_fn,
2012 };
2013
2014 /*?
2015  * Bypass IP4/IP6 lookup
2016  *
2017  * @cliexpar
2018  * @cliexstart{map params pre-resolve}
2019  *
2020  * Bypass a second FIB lookup of the translated or encapsulated
2021  * packet, and forward the packet directly to the specified
2022  * next-hop. This optimization trades forwarding flexibility for
2023  * performance.
2024  * @cliexend
2025  ?*/
2026 VLIB_CLI_COMMAND(map_pre_resolve_command, static) = {
2027   .path = "map params pre-resolve",
2028   .short_help = " map params pre-resolve {ip4-nh <address>} "
2029                 "| {ip6-nh <address>}",
2030   .function = map_pre_resolve_command_fn,
2031 };
2032
2033 /*?
2034  * Enable or disable the MAP-E inbound security check
2035  *
2036  * @cliexpar
2037  * @cliexstart{map params security-check}
2038  *
2039  * By default, a decapsulated packet's IPv4 source address will be
2040  * verified against the outer header's IPv6 source address. Disabling
2041  * this feature will allow IPv4 source address spoofing.
2042  * @cliexend
2043  ?*/
2044 VLIB_CLI_COMMAND(map_security_check_command, static) = {
2045   .path = "map params security-check",
2046   .short_help = "map params security-check on|off",
2047   .function = map_security_check_command_fn,
2048 };
2049
2050 /*?
2051  * Specifiy the IPv4 source address used for relayed ICMP error messages
2052  *
2053  * @cliexpar
2054  * @cliexstart{map params icmp source-address}
2055  *
2056  * This command specifies which IPv4 source address (must be local to
2057  * the system), that is used for relayed received IPv6 ICMP error
2058  * messages.
2059  * @cliexend
2060  ?*/
2061 VLIB_CLI_COMMAND(map_icmp_relay_source_address_command, static) = {
2062   .path = "map params icmp source-address",
2063   .short_help = "map params icmp source-address <ip4-address>",
2064   .function = map_icmp_relay_source_address_command_fn,
2065 };
2066
2067 /*?
2068  * Send IPv6 ICMP unreachables
2069  *
2070  * @cliexpar
2071  * @cliexstart{map params icmp6 unreachables}
2072  *
2073  * Send IPv6 ICMP unreachable messages back if security check fails or
2074  * no MAP domain exists.
2075  * @cliexend
2076  ?*/
2077 VLIB_CLI_COMMAND(map_icmp_unreachables_command, static) = {
2078   .path = "map params icmp6 unreachables",
2079   .short_help = "map params icmp6 unreachables {on|off}",
2080   .function = map_icmp_unreachables_command_fn,
2081 };
2082
2083 /*?
2084  * Configure MAP fragmentation behaviour
2085  *
2086  * @cliexpar
2087  * @cliexstart{map params fragment}
2088  * @cliexend
2089  ?*/
2090 VLIB_CLI_COMMAND(map_fragment_command, static) = {
2091   .path = "map params fragment",
2092   .short_help = "map params fragment inner|outer",
2093   .function = map_fragment_command_fn,
2094 };
2095
2096 /*?
2097  * Ignore the IPv4 Don't fragment bit
2098  *
2099  * @cliexpar
2100  * @cliexstart{map params fragment ignore-df}
2101  *
2102  * Allows fragmentation of the IPv4 packet even if the DF bit is
2103  * set. The choice between inner or outer fragmentation of tunnel
2104  * packets is complicated. The benefit of inner fragmentation is that
2105  * the ultimate endpoint must reassemble, instead of the tunnel
2106  * endpoint.
2107  * @cliexend
2108  ?*/
2109 VLIB_CLI_COMMAND(map_fragment_df_command, static) = {
2110   .path = "map params fragment ignore-df",
2111   .short_help = "map params fragment ignore-df on|off",
2112   .function = map_fragment_df_command_fn,
2113 };
2114
2115 /*?
2116  * Specifiy if the inbound security check should be done on fragments
2117  *
2118  * @cliexpar
2119  * @cliexstart{map params security-check fragments}
2120  *
2121  * Typically the inbound on-decapsulation security check is only done
2122  * on the first packet. The packet that contains the L4
2123  * information. While a security check on every fragment is possible,
2124  * it has a cost. State must be created on the first fragment.
2125  * @cliexend
2126  ?*/
2127 VLIB_CLI_COMMAND(map_security_check_frag_command, static) = {
2128   .path = "map params security-check fragments",
2129   .short_help = "map params security-check fragments on|off",
2130   .function = map_security_check_frag_command_fn,
2131 };
2132
2133 /*?
2134  * Add MAP domain
2135  *
2136  * @cliexpar
2137  * @cliexstart{map add domain}
2138  * @cliexend
2139  ?*/
2140 VLIB_CLI_COMMAND(map_add_domain_command, static) = {
2141   .path = "map add domain",
2142   .short_help = "map add domain ip4-pfx <ip4-pfx> ip6-pfx <ip6-pfx> "
2143       "ip6-src <ip6-pfx> ea-bits-len <n> psid-offset <n> psid-len <n> "
2144       "[map-t] [mtu <mtu>]",
2145   .function = map_add_domain_command_fn,
2146 };
2147
2148 /*?
2149  * Add MAP rule to a domain
2150  *
2151  * @cliexpar
2152  * @cliexstart{map add rule}
2153  * @cliexend
2154  ?*/
2155 VLIB_CLI_COMMAND(map_add_rule_command, static) = {
2156   .path = "map add rule",
2157   .short_help = "map add rule index <domain> psid <psid> ip6-dst <ip6-addr>",
2158   .function = map_add_rule_command_fn,
2159 };
2160
2161 /*?
2162  * Delete MAP domain
2163  *
2164  * @cliexpar
2165  * @cliexstart{map del domain}
2166  * @cliexend
2167  ?*/
2168 VLIB_CLI_COMMAND(map_del_command, static) = {
2169   .path = "map del domain",
2170   .short_help = "map del domain index <domain>",
2171   .function = map_del_domain_command_fn,
2172 };
2173
2174 /*?
2175  * Show MAP domains
2176  *
2177  * @cliexpar
2178  * @cliexstart{show map domain}
2179  * @cliexend
2180  ?*/
2181 VLIB_CLI_COMMAND(show_map_domain_command, static) = {
2182   .path = "show map domain",
2183   .short_help = "show map domain index <n> [counters]",
2184   .function = show_map_domain_command_fn,
2185 };
2186
2187 /*?
2188  * Show MAP statistics
2189  *
2190  * @cliexpar
2191  * @cliexstart{show map stats}
2192  * @cliexend
2193  ?*/
2194 VLIB_CLI_COMMAND(show_map_stats_command, static) = {
2195   .path = "show map stats",
2196   .short_help = "show map stats",
2197   .function = show_map_stats_command_fn,
2198 };
2199
2200 /*?
2201  * Show MAP fragmentation information
2202  *
2203  * @cliexpar
2204  * @cliexstart{show map fragments}
2205  * @cliexend
2206  ?*/
2207 VLIB_CLI_COMMAND(show_map_fragments_command, static) = {
2208   .path = "show map fragments",
2209   .short_help = "show map fragments",
2210   .function = show_map_fragments_command_fn,
2211 };
2212
2213 VLIB_PLUGIN_REGISTER() = {
2214   .version = VPP_BUILD_VER,
2215   .description = "Mapping of address and port (MAP)",
2216 };
2217
2218 /* *INDENT-ON* */
2219
2220 /*
2221  * map_init
2222  */
2223 clib_error_t *
2224 map_init (vlib_main_t * vm)
2225 {
2226   map_main_t *mm = &map_main;
2227   clib_error_t *error = 0;
2228   mm->vnet_main = vnet_get_main ();
2229   mm->vlib_main = vm;
2230
2231 #ifdef MAP_SKIP_IP6_LOOKUP
2232   fib_protocol_t proto;
2233
2234   FOR_EACH_FIB_PROTOCOL (proto)
2235   {
2236     map_pre_resolve_init (&pre_resolved[proto]);
2237   }
2238 #endif
2239
2240   /* traffic class */
2241   mm->tc = 0;
2242   mm->tc_copy = true;
2243
2244   /* Inbound security check */
2245   mm->sec_check = true;
2246   mm->sec_check_frag = false;
2247
2248   /* ICMP6 Type 1, Code 5 for security check failure */
2249   mm->icmp6_enabled = false;
2250
2251   /* Inner or outer fragmentation */
2252   mm->frag_inner = false;
2253   mm->frag_ignore_df = false;
2254
2255   vec_validate (mm->domain_counters, MAP_N_DOMAIN_COUNTER - 1);
2256   mm->domain_counters[MAP_DOMAIN_COUNTER_RX].name = "/map/rx";
2257   mm->domain_counters[MAP_DOMAIN_COUNTER_TX].name = "/map/tx";
2258
2259   vlib_validate_simple_counter (&mm->icmp_relayed, 0);
2260   vlib_zero_simple_counter (&mm->icmp_relayed, 0);
2261
2262   /* IP4 virtual reassembly */
2263   mm->ip4_reass_hash_table = 0;
2264   mm->ip4_reass_pool = 0;
2265   mm->ip4_reass_lock =
2266     clib_mem_alloc_aligned (CLIB_CACHE_LINE_BYTES, CLIB_CACHE_LINE_BYTES);
2267   *mm->ip4_reass_lock = 0;
2268   mm->ip4_reass_conf_ht_ratio = MAP_IP4_REASS_HT_RATIO_DEFAULT;
2269   mm->ip4_reass_conf_lifetime_ms = MAP_IP4_REASS_LIFETIME_DEFAULT;
2270   mm->ip4_reass_conf_pool_size = MAP_IP4_REASS_POOL_SIZE_DEFAULT;
2271   mm->ip4_reass_conf_buffers = MAP_IP4_REASS_BUFFERS_DEFAULT;
2272   mm->ip4_reass_ht_log2len =
2273     map_get_ht_log2len (mm->ip4_reass_conf_ht_ratio,
2274                         mm->ip4_reass_conf_pool_size);
2275   mm->ip4_reass_fifo_last = MAP_REASS_INDEX_NONE;
2276   map_ip4_reass_reinit (NULL, NULL);
2277
2278   /* IP6 virtual reassembly */
2279   mm->ip6_reass_hash_table = 0;
2280   mm->ip6_reass_pool = 0;
2281   mm->ip6_reass_lock =
2282     clib_mem_alloc_aligned (CLIB_CACHE_LINE_BYTES, CLIB_CACHE_LINE_BYTES);
2283   *mm->ip6_reass_lock = 0;
2284   mm->ip6_reass_conf_ht_ratio = MAP_IP6_REASS_HT_RATIO_DEFAULT;
2285   mm->ip6_reass_conf_lifetime_ms = MAP_IP6_REASS_LIFETIME_DEFAULT;
2286   mm->ip6_reass_conf_pool_size = MAP_IP6_REASS_POOL_SIZE_DEFAULT;
2287   mm->ip6_reass_conf_buffers = MAP_IP6_REASS_BUFFERS_DEFAULT;
2288   mm->ip6_reass_ht_log2len =
2289     map_get_ht_log2len (mm->ip6_reass_conf_ht_ratio,
2290                         mm->ip6_reass_conf_pool_size);
2291   mm->ip6_reass_fifo_last = MAP_REASS_INDEX_NONE;
2292   map_ip6_reass_reinit (NULL, NULL);
2293
2294 #ifdef MAP_SKIP_IP6_LOOKUP
2295   fib_node_register_type (FIB_NODE_TYPE_MAP_E, &map_vft);
2296 #endif
2297   map_dpo_module_init ();
2298
2299   error = map_plugin_api_hookup (vm);
2300
2301   return error;
2302 }
2303
2304 VLIB_INIT_FUNCTION (map_init);
2305
2306 /*
2307  * fd.io coding-style-patch-verification: ON
2308  *
2309  * Local Variables:
2310  * eval: (c-set-style "gnu")
2311  * End:
2312  */