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