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