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