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