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