dpdk: Add support for Mellanox ConnectX-4 devices
[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   /* Get a line of input. */
469   if (!unformat_user (input, unformat_line_input, line_input))
470     return 0;
471
472   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
473     {
474       if (unformat (line_input, "off"))
475         mm->sec_check = false;
476       else if (unformat (line_input, "on"))
477         mm->sec_check = true;
478       else
479         return clib_error_return (0, "unknown input `%U'",
480                                   format_unformat_error, input);
481     }
482   unformat_free (line_input);
483   return 0;
484 }
485
486 static clib_error_t *
487 map_security_check_frag_command_fn (vlib_main_t * vm,
488                                     unformat_input_t * input,
489                                     vlib_cli_command_t * cmd)
490 {
491   unformat_input_t _line_input, *line_input = &_line_input;
492   map_main_t *mm = &map_main;
493   /* Get a line of input. */
494   if (!unformat_user (input, unformat_line_input, line_input))
495     return 0;
496
497   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
498     {
499       if (unformat (line_input, "off"))
500         mm->sec_check_frag = false;
501       else if (unformat (line_input, "on"))
502         mm->sec_check_frag = true;
503       else
504         return clib_error_return (0, "unknown input `%U'",
505                                   format_unformat_error, input);
506     }
507   unformat_free (line_input);
508   return 0;
509 }
510
511 static clib_error_t *
512 map_add_domain_command_fn (vlib_main_t * vm,
513                            unformat_input_t * input, vlib_cli_command_t * cmd)
514 {
515   unformat_input_t _line_input, *line_input = &_line_input;
516   ip4_address_t ip4_prefix;
517   ip6_address_t ip6_prefix;
518   ip6_address_t ip6_src;
519   u32 ip6_prefix_len = 0, ip4_prefix_len = 0, map_domain_index, ip6_src_len;
520   u32 num_m_args = 0;
521   /* Optional arguments */
522   u32 ea_bits_len = 0, psid_offset = 0, psid_length = 0;
523   u32 mtu = 0;
524   u8 flags = 0;
525   ip6_src_len = 128;
526
527   /* Get a line of input. */
528   if (!unformat_user (input, unformat_line_input, line_input))
529     return 0;
530
531   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
532     {
533       if (unformat
534           (line_input, "ip4-pfx %U/%d", unformat_ip4_address, &ip4_prefix,
535            &ip4_prefix_len))
536         num_m_args++;
537       else
538         if (unformat
539             (line_input, "ip6-pfx %U/%d", unformat_ip6_address, &ip6_prefix,
540              &ip6_prefix_len))
541         num_m_args++;
542       else
543         if (unformat
544             (line_input, "ip6-src %U/%d", unformat_ip6_address, &ip6_src,
545              &ip6_src_len))
546         num_m_args++;
547       else
548         if (unformat
549             (line_input, "ip6-src %U", unformat_ip6_address, &ip6_src))
550         num_m_args++;
551       else if (unformat (line_input, "ea-bits-len %d", &ea_bits_len))
552         num_m_args++;
553       else if (unformat (line_input, "psid-offset %d", &psid_offset))
554         num_m_args++;
555       else if (unformat (line_input, "psid-len %d", &psid_length))
556         num_m_args++;
557       else if (unformat (line_input, "mtu %d", &mtu))
558         num_m_args++;
559       else if (unformat (line_input, "map-t"))
560         flags |= MAP_DOMAIN_TRANSLATION;
561       else
562         return clib_error_return (0, "unknown input `%U'",
563                                   format_unformat_error, input);
564     }
565   unformat_free (line_input);
566
567   if (num_m_args < 3)
568     return clib_error_return (0, "mandatory argument(s) missing");
569
570   map_create_domain (&ip4_prefix, ip4_prefix_len,
571                      &ip6_prefix, ip6_prefix_len, &ip6_src, ip6_src_len,
572                      ea_bits_len, psid_offset, psid_length, &map_domain_index,
573                      mtu, flags);
574
575   return 0;
576 }
577
578 static clib_error_t *
579 map_del_domain_command_fn (vlib_main_t * vm,
580                            unformat_input_t * input, vlib_cli_command_t * cmd)
581 {
582   unformat_input_t _line_input, *line_input = &_line_input;
583   u32 num_m_args = 0;
584   u32 map_domain_index;
585
586   /* Get a line of input. */
587   if (!unformat_user (input, unformat_line_input, line_input))
588     return 0;
589
590   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
591     {
592       if (unformat (line_input, "index %d", &map_domain_index))
593         num_m_args++;
594       else
595         return clib_error_return (0, "unknown input `%U'",
596                                   format_unformat_error, input);
597     }
598   unformat_free (line_input);
599
600   if (num_m_args != 1)
601     return clib_error_return (0, "mandatory argument(s) missing");
602
603   map_delete_domain (map_domain_index);
604
605   return 0;
606 }
607
608 static clib_error_t *
609 map_add_rule_command_fn (vlib_main_t * vm,
610                          unformat_input_t * input, vlib_cli_command_t * cmd)
611 {
612   unformat_input_t _line_input, *line_input = &_line_input;
613   ip6_address_t tep;
614   u32 num_m_args = 0;
615   u32 psid = 0, map_domain_index;
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, "index %d", &map_domain_index))
624         num_m_args++;
625       else if (unformat (line_input, "psid %d", &psid))
626         num_m_args++;
627       else
628         if (unformat (line_input, "ip6-dst %U", unformat_ip6_address, &tep))
629         num_m_args++;
630       else
631         return clib_error_return (0, "unknown input `%U'",
632                                   format_unformat_error, input);
633     }
634   unformat_free (line_input);
635
636   if (num_m_args != 3)
637     return clib_error_return (0, "mandatory argument(s) missing");
638
639   if (map_add_del_psid (map_domain_index, psid, &tep, 1) != 0)
640     {
641       return clib_error_return (0, "Failing to add Mapping Rule");
642     }
643   return 0;
644 }
645
646 #if MAP_SKIP_IP6_LOOKUP
647 static clib_error_t *
648 map_pre_resolve_command_fn (vlib_main_t * vm,
649                             unformat_input_t * input,
650                             vlib_cli_command_t * cmd)
651 {
652   unformat_input_t _line_input, *line_input = &_line_input;
653   ip4_address_t ip4nh;
654   ip6_address_t ip6nh;
655   map_main_t *mm = &map_main;
656
657   memset (&ip4nh, 0, sizeof (ip4nh));
658   memset (&ip6nh, 0, sizeof (ip6nh));
659
660   /* Get a line of input. */
661   if (!unformat_user (input, unformat_line_input, line_input))
662     return 0;
663
664   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
665     {
666       if (unformat (line_input, "ip4-nh %U", unformat_ip4_address, &ip4nh))
667         mm->preresolve_ip4 = ip4nh;
668       else
669         if (unformat (line_input, "ip6-nh %U", unformat_ip6_address, &ip6nh))
670         mm->preresolve_ip6 = ip6nh;
671       else
672         return clib_error_return (0, "unknown input `%U'",
673                                   format_unformat_error, input);
674     }
675   unformat_free (line_input);
676
677   map_pre_resolve (&ip4nh, &ip6nh);
678
679   return 0;
680 }
681 #endif
682
683 static clib_error_t *
684 map_icmp_relay_source_address_command_fn (vlib_main_t * vm,
685                                           unformat_input_t * input,
686                                           vlib_cli_command_t * cmd)
687 {
688   unformat_input_t _line_input, *line_input = &_line_input;
689   ip4_address_t icmp_src_address;
690   map_main_t *mm = &map_main;
691
692   mm->icmp4_src_address.as_u32 = 0;
693
694   /* Get a line of input. */
695   if (!unformat_user (input, unformat_line_input, line_input))
696     return 0;
697
698   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
699     {
700       if (unformat
701           (line_input, "%U", unformat_ip4_address, &icmp_src_address))
702         mm->icmp4_src_address = icmp_src_address;
703       else
704         return clib_error_return (0, "unknown input `%U'",
705                                   format_unformat_error, input);
706     }
707   unformat_free (line_input);
708
709   return 0;
710 }
711
712 static clib_error_t *
713 map_icmp_unreachables_command_fn (vlib_main_t * vm,
714                                   unformat_input_t * input,
715                                   vlib_cli_command_t * cmd)
716 {
717   unformat_input_t _line_input, *line_input = &_line_input;
718   map_main_t *mm = &map_main;
719   int num_m_args = 0;
720
721   /* Get a line of input. */
722   if (!unformat_user (input, unformat_line_input, line_input))
723     return 0;
724
725   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
726     {
727       num_m_args++;
728       if (unformat (line_input, "on"))
729         mm->icmp6_enabled = true;
730       else if (unformat (line_input, "off"))
731         mm->icmp6_enabled = false;
732       else
733         return clib_error_return (0, "unknown input `%U'",
734                                   format_unformat_error, input);
735     }
736   unformat_free (line_input);
737
738
739   if (num_m_args != 1)
740     return clib_error_return (0, "mandatory argument(s) missing");
741
742   return 0;
743 }
744
745 static clib_error_t *
746 map_fragment_command_fn (vlib_main_t * vm,
747                          unformat_input_t * input, vlib_cli_command_t * cmd)
748 {
749   unformat_input_t _line_input, *line_input = &_line_input;
750   map_main_t *mm = &map_main;
751
752   /* Get a line of input. */
753   if (!unformat_user (input, unformat_line_input, line_input))
754     return 0;
755
756   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
757     {
758       if (unformat (line_input, "inner"))
759         mm->frag_inner = true;
760       else if (unformat (line_input, "outer"))
761         mm->frag_inner = false;
762       else
763         return clib_error_return (0, "unknown input `%U'",
764                                   format_unformat_error, input);
765     }
766   unformat_free (line_input);
767
768   return 0;
769 }
770
771 static clib_error_t *
772 map_fragment_df_command_fn (vlib_main_t * vm,
773                             unformat_input_t * input,
774                             vlib_cli_command_t * cmd)
775 {
776   unformat_input_t _line_input, *line_input = &_line_input;
777   map_main_t *mm = &map_main;
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       if (unformat (line_input, "on"))
786         mm->frag_ignore_df = true;
787       else if (unformat (line_input, "off"))
788         mm->frag_ignore_df = false;
789       else
790         return clib_error_return (0, "unknown input `%U'",
791                                   format_unformat_error, input);
792     }
793   unformat_free (line_input);
794
795   return 0;
796 }
797
798 static clib_error_t *
799 map_traffic_class_command_fn (vlib_main_t * vm,
800                               unformat_input_t * input,
801                               vlib_cli_command_t * cmd)
802 {
803   unformat_input_t _line_input, *line_input = &_line_input;
804   map_main_t *mm = &map_main;
805   u32 tc = 0;
806
807   mm->tc_copy = false;
808
809   /* Get a line of input. */
810   if (!unformat_user (input, unformat_line_input, line_input))
811     return 0;
812
813   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
814     {
815       if (unformat (line_input, "copy"))
816         mm->tc_copy = true;
817       else if (unformat (line_input, "%x", &tc))
818         mm->tc = tc & 0xff;
819       else
820         return clib_error_return (0, "unknown input `%U'",
821                                   format_unformat_error, input);
822     }
823   unformat_free (line_input);
824
825   return 0;
826 }
827
828 static u8 *
829 format_map_domain (u8 * s, va_list * args)
830 {
831   map_domain_t *d = va_arg (*args, map_domain_t *);
832   bool counters = va_arg (*args, int);
833   map_main_t *mm = &map_main;
834   ip6_address_t ip6_prefix;
835
836   if (d->rules)
837     memset (&ip6_prefix, 0, sizeof (ip6_prefix));
838   else
839     ip6_prefix = d->ip6_prefix;
840
841   s = format (s,
842               "[%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",
843               d - mm->domains,
844               format_ip4_address, &d->ip4_prefix, d->ip4_prefix_len,
845               format_ip6_address, &ip6_prefix, d->ip6_prefix_len,
846               format_ip6_address, &d->ip6_src, d->ip6_src_len,
847               d->ea_bits_len, d->psid_offset, d->psid_length, d->mtu,
848               (d->flags & MAP_DOMAIN_TRANSLATION) ? "map-t" : "");
849
850   if (counters)
851     {
852       map_domain_counter_lock (mm);
853       vlib_counter_t v;
854       vlib_get_combined_counter (&mm->domain_counters[MAP_DOMAIN_COUNTER_TX],
855                                  d - mm->domains, &v);
856       s = format (s, "  TX: %lld/%lld", v.packets, v.bytes);
857       vlib_get_combined_counter (&mm->domain_counters[MAP_DOMAIN_COUNTER_RX],
858                                  d - mm->domains, &v);
859       s = format (s, "  RX: %lld/%lld", v.packets, v.bytes);
860       map_domain_counter_unlock (mm);
861     }
862   s = format (s, "\n");
863
864   if (d->rules)
865     {
866       int i;
867       ip6_address_t dst;
868       for (i = 0; i < (0x1 << d->psid_length); i++)
869         {
870           dst = d->rules[i];
871           if (dst.as_u64[0] == 0 && dst.as_u64[1] == 0)
872             continue;
873           s = format (s,
874                       " rule psid: %d ip6-dst %U\n", i, format_ip6_address,
875                       &dst);
876         }
877     }
878   return s;
879 }
880
881 static u8 *
882 format_map_ip4_reass (u8 * s, va_list * args)
883 {
884   map_main_t *mm = &map_main;
885   map_ip4_reass_t *r = va_arg (*args, map_ip4_reass_t *);
886   map_ip4_reass_key_t *k = &r->key;
887   f64 now = vlib_time_now (mm->vlib_main);
888   f64 lifetime = (((f64) mm->ip4_reass_conf_lifetime_ms) / 1000);
889   f64 dt = (r->ts + lifetime > now) ? (r->ts + lifetime - now) : -1;
890   s = format (s,
891               "ip4-reass src=%U  dst=%U  protocol=%d  identifier=%d  port=%d  lifetime=%.3lf\n",
892               format_ip4_address, &k->src.as_u8, format_ip4_address,
893               &k->dst.as_u8, k->protocol,
894               clib_net_to_host_u16 (k->fragment_id),
895               (r->port >= 0) ? clib_net_to_host_u16 (r->port) : -1, dt);
896   return s;
897 }
898
899 static u8 *
900 format_map_ip6_reass (u8 * s, va_list * args)
901 {
902   map_main_t *mm = &map_main;
903   map_ip6_reass_t *r = va_arg (*args, map_ip6_reass_t *);
904   map_ip6_reass_key_t *k = &r->key;
905   f64 now = vlib_time_now (mm->vlib_main);
906   f64 lifetime = (((f64) mm->ip6_reass_conf_lifetime_ms) / 1000);
907   f64 dt = (r->ts + lifetime > now) ? (r->ts + lifetime - now) : -1;
908   s = format (s,
909               "ip6-reass src=%U  dst=%U  protocol=%d  identifier=%d  lifetime=%.3lf\n",
910               format_ip6_address, &k->src.as_u8, format_ip6_address,
911               &k->dst.as_u8, k->protocol,
912               clib_net_to_host_u32 (k->fragment_id), dt);
913   return s;
914 }
915
916 static clib_error_t *
917 show_map_domain_command_fn (vlib_main_t * vm, unformat_input_t * input,
918                             vlib_cli_command_t * cmd)
919 {
920   unformat_input_t _line_input, *line_input = &_line_input;
921   map_main_t *mm = &map_main;
922   map_domain_t *d;
923   bool counters = false;
924   u32 map_domain_index = ~0;
925
926   /* Get a line of input. */
927   if (!unformat_user (input, unformat_line_input, line_input))
928     return 0;
929
930   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
931     {
932       if (unformat (line_input, "counters"))
933         counters = true;
934       else if (unformat (line_input, "index %d", &map_domain_index))
935         ;
936       else
937         return clib_error_return (0, "unknown input `%U'",
938                                   format_unformat_error, input);
939     }
940   unformat_free (line_input);
941
942   if (pool_elts (mm->domains) == 0)
943     vlib_cli_output (vm, "No MAP domains are configured...");
944
945   if (map_domain_index == ~0)
946     {
947     /* *INDENT-OFF* */
948     pool_foreach(d, mm->domains, ({vlib_cli_output(vm, "%U", format_map_domain, d, counters);}));
949     /* *INDENT-ON* */
950     }
951   else
952     {
953       if (pool_is_free_index (mm->domains, map_domain_index))
954         {
955           return clib_error_return (0, "MAP domain does not exists %d",
956                                     map_domain_index);
957         }
958
959       d = pool_elt_at_index (mm->domains, map_domain_index);
960       vlib_cli_output (vm, "%U", format_map_domain, d, counters);
961     }
962
963   return 0;
964 }
965
966 static clib_error_t *
967 show_map_fragments_command_fn (vlib_main_t * vm, unformat_input_t * input,
968                                vlib_cli_command_t * cmd)
969 {
970   map_main_t *mm = &map_main;
971   map_ip4_reass_t *f4;
972   map_ip6_reass_t *f6;
973
974   /* *INDENT-OFF* */
975   pool_foreach(f4, mm->ip4_reass_pool, ({vlib_cli_output (vm, "%U", format_map_ip4_reass, f4);}));
976   /* *INDENT-ON* */
977   /* *INDENT-OFF* */
978   pool_foreach(f6, mm->ip6_reass_pool, ({vlib_cli_output (vm, "%U", format_map_ip6_reass, f6);}));
979   /* *INDENT-ON* */
980   return (0);
981 }
982
983 u64
984 map_error_counter_get (u32 node_index, map_error_t map_error)
985 {
986   vlib_main_t *vm = vlib_get_main ();
987   vlib_node_runtime_t *error_node = vlib_node_get_runtime (vm, node_index);
988   vlib_error_main_t *em = &vm->error_main;
989   vlib_error_t e = error_node->errors[map_error];
990   vlib_node_t *n = vlib_get_node (vm, node_index);
991   u32 ci;
992
993   ci = vlib_error_get_code (e);
994   ASSERT (ci < n->n_errors);
995   ci += n->error_heap_index;
996
997   return (em->counters[ci]);
998 }
999
1000 static clib_error_t *
1001 show_map_stats_command_fn (vlib_main_t * vm, unformat_input_t * input,
1002                            vlib_cli_command_t * cmd)
1003 {
1004   map_main_t *mm = &map_main;
1005   map_domain_t *d;
1006   int domains = 0, rules = 0, domaincount = 0, rulecount = 0;
1007   if (pool_elts (mm->domains) == 0)
1008     vlib_cli_output (vm, "No MAP domains are configured...");
1009
1010   /* *INDENT-OFF* */
1011   pool_foreach(d, mm->domains, ({
1012     if (d->rules) {
1013       rulecount+= 0x1 << d->psid_length;
1014       rules += sizeof(ip6_address_t) * 0x1 << d->psid_length;
1015     }
1016     domains += sizeof(*d);
1017     domaincount++;
1018   }));
1019   /* *INDENT-ON* */
1020
1021   vlib_cli_output (vm, "MAP domains structure: %d\n", sizeof (map_domain_t));
1022   vlib_cli_output (vm, "MAP domains: %d (%d bytes)\n", domaincount, domains);
1023   vlib_cli_output (vm, "MAP rules: %d (%d bytes)\n", rulecount, rules);
1024   vlib_cli_output (vm, "Total: %d bytes)\n", rules + domains);
1025
1026 #if MAP_SKIP_IP6_LOOKUP
1027   vlib_cli_output (vm,
1028                    "MAP pre-resolve: IP6 next-hop: %U (%u), IP4 next-hop: %U (%u)\n",
1029                    format_ip6_address, &mm->preresolve_ip6, mm->adj6_index,
1030                    format_ip4_address, &mm->preresolve_ip4, mm->adj4_index);
1031 #endif
1032
1033   if (mm->tc_copy)
1034     vlib_cli_output (vm, "MAP traffic-class: copy");
1035   else
1036     vlib_cli_output (vm, "MAP traffic-class: %x", mm->tc);
1037
1038   vlib_cli_output (vm,
1039                    "MAP IPv6 inbound security check: %s, fragmented packet security check: %s",
1040                    mm->sec_check ? "enabled" : "disabled",
1041                    mm->sec_check_frag ? "enabled" : "disabled");
1042
1043   vlib_cli_output (vm, "ICMP-relay IPv4 source address: %U\n",
1044                    format_ip4_address, &mm->icmp4_src_address);
1045   vlib_cli_output (vm, "ICMP6 unreachables sent for unmatched packets: %s\n",
1046                    mm->icmp6_enabled ? "enabled" : "disabled");
1047   vlib_cli_output (vm, "Inner fragmentation: %s\n",
1048                    mm->frag_inner ? "enabled" : "disabled");
1049   vlib_cli_output (vm, "Fragment packets regardless of DF flag: %s\n",
1050                    mm->frag_ignore_df ? "enabled" : "disabled");
1051
1052   /*
1053    * Counters
1054    */
1055   vlib_combined_counter_main_t *cm = mm->domain_counters;
1056   u64 total_pkts[MAP_N_DOMAIN_COUNTER];
1057   u64 total_bytes[MAP_N_DOMAIN_COUNTER];
1058   int which, i;
1059   vlib_counter_t v;
1060
1061   memset (total_pkts, 0, sizeof (total_pkts));
1062   memset (total_bytes, 0, sizeof (total_bytes));
1063
1064   map_domain_counter_lock (mm);
1065   vec_foreach (cm, mm->domain_counters)
1066   {
1067     which = cm - mm->domain_counters;
1068
1069     for (i = 0; i < vec_len (cm->maxi); i++)
1070       {
1071         vlib_get_combined_counter (cm, i, &v);
1072         total_pkts[which] += v.packets;
1073         total_bytes[which] += v.bytes;
1074       }
1075   }
1076   map_domain_counter_unlock (mm);
1077
1078   vlib_cli_output (vm, "Encapsulated packets: %lld bytes: %lld\n",
1079                    total_pkts[MAP_DOMAIN_COUNTER_TX],
1080                    total_bytes[MAP_DOMAIN_COUNTER_TX]);
1081   vlib_cli_output (vm, "Decapsulated packets: %lld bytes: %lld\n",
1082                    total_pkts[MAP_DOMAIN_COUNTER_RX],
1083                    total_bytes[MAP_DOMAIN_COUNTER_RX]);
1084
1085   vlib_cli_output (vm, "ICMP relayed packets: %d\n",
1086                    vlib_get_simple_counter (&mm->icmp_relayed, 0));
1087
1088   return 0;
1089 }
1090
1091 static clib_error_t *
1092 map_params_reass_command_fn (vlib_main_t * vm, unformat_input_t * input,
1093                              vlib_cli_command_t * cmd)
1094 {
1095   unformat_input_t _line_input, *line_input = &_line_input;
1096   u32 lifetime = ~0;
1097   f64 ht_ratio = (MAP_IP4_REASS_CONF_HT_RATIO_MAX + 1);
1098   u32 pool_size = ~0;
1099   u64 buffers = ~(0ull);
1100   u8 ip4 = 0, ip6 = 0;
1101
1102   if (!unformat_user (input, unformat_line_input, line_input))
1103     return 0;
1104
1105   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
1106     {
1107       if (unformat (line_input, "lifetime %u", &lifetime))
1108         ;
1109       else if (unformat (line_input, "ht-ratio %lf", &ht_ratio))
1110         ;
1111       else if (unformat (line_input, "pool-size %u", &pool_size))
1112         ;
1113       else if (unformat (line_input, "buffers %llu", &buffers))
1114         ;
1115       else if (unformat (line_input, "ip4"))
1116         ip4 = 1;
1117       else if (unformat (line_input, "ip6"))
1118         ip6 = 1;
1119       else
1120         {
1121           unformat_free (line_input);
1122           return clib_error_return (0, "invalid input");
1123         }
1124     }
1125   unformat_free (line_input);
1126
1127   if (!ip4 && !ip6)
1128     return clib_error_return (0, "must specify ip4 and/or ip6");
1129
1130   if (ip4)
1131     {
1132       if (pool_size != ~0 && pool_size > MAP_IP4_REASS_CONF_POOL_SIZE_MAX)
1133         return clib_error_return (0, "invalid ip4-reass pool-size ( > %d)",
1134                                   MAP_IP4_REASS_CONF_POOL_SIZE_MAX);
1135       if (ht_ratio != (MAP_IP4_REASS_CONF_HT_RATIO_MAX + 1)
1136           && ht_ratio > MAP_IP4_REASS_CONF_HT_RATIO_MAX)
1137         return clib_error_return (0, "invalid ip4-reass ht-ratio ( > %d)",
1138                                   MAP_IP4_REASS_CONF_HT_RATIO_MAX);
1139       if (lifetime != ~0 && lifetime > MAP_IP4_REASS_CONF_LIFETIME_MAX)
1140         return clib_error_return (0, "invalid ip4-reass lifetime ( > %d)",
1141                                   MAP_IP4_REASS_CONF_LIFETIME_MAX);
1142       if (buffers != ~(0ull) && buffers > MAP_IP4_REASS_CONF_BUFFERS_MAX)
1143         return clib_error_return (0, "invalid ip4-reass buffers ( > %ld)",
1144                                   MAP_IP4_REASS_CONF_BUFFERS_MAX);
1145     }
1146
1147   if (ip6)
1148     {
1149       if (pool_size != ~0 && pool_size > MAP_IP6_REASS_CONF_POOL_SIZE_MAX)
1150         return clib_error_return (0, "invalid ip6-reass pool-size ( > %d)",
1151                                   MAP_IP6_REASS_CONF_POOL_SIZE_MAX);
1152       if (ht_ratio != (MAP_IP4_REASS_CONF_HT_RATIO_MAX + 1)
1153           && ht_ratio > MAP_IP6_REASS_CONF_HT_RATIO_MAX)
1154         return clib_error_return (0, "invalid ip6-reass ht-log2len ( > %d)",
1155                                   MAP_IP6_REASS_CONF_HT_RATIO_MAX);
1156       if (lifetime != ~0 && lifetime > MAP_IP6_REASS_CONF_LIFETIME_MAX)
1157         return clib_error_return (0, "invalid ip6-reass lifetime ( > %d)",
1158                                   MAP_IP6_REASS_CONF_LIFETIME_MAX);
1159       if (buffers != ~(0ull) && buffers > MAP_IP6_REASS_CONF_BUFFERS_MAX)
1160         return clib_error_return (0, "invalid ip6-reass buffers ( > %ld)",
1161                                   MAP_IP6_REASS_CONF_BUFFERS_MAX);
1162     }
1163
1164   if (ip4)
1165     {
1166       u32 reass = 0, packets = 0;
1167       if (pool_size != ~0)
1168         {
1169           if (map_ip4_reass_conf_pool_size (pool_size, &reass, &packets))
1170             {
1171               vlib_cli_output (vm, "Could not set ip4-reass pool-size");
1172             }
1173           else
1174             {
1175               vlib_cli_output (vm,
1176                                "Setting ip4-reass pool-size (destroyed-reassembly=%u , dropped-fragments=%u)",
1177                                reass, packets);
1178             }
1179         }
1180       if (ht_ratio != (MAP_IP4_REASS_CONF_HT_RATIO_MAX + 1))
1181         {
1182           if (map_ip4_reass_conf_ht_ratio (ht_ratio, &reass, &packets))
1183             {
1184               vlib_cli_output (vm, "Could not set ip4-reass ht-log2len");
1185             }
1186           else
1187             {
1188               vlib_cli_output (vm,
1189                                "Setting ip4-reass ht-log2len (destroyed-reassembly=%u , dropped-fragments=%u)",
1190                                reass, packets);
1191             }
1192         }
1193       if (lifetime != ~0)
1194         {
1195           if (map_ip4_reass_conf_lifetime (lifetime))
1196             vlib_cli_output (vm, "Could not set ip4-reass lifetime");
1197           else
1198             vlib_cli_output (vm, "Setting ip4-reass lifetime");
1199         }
1200       if (buffers != ~(0ull))
1201         {
1202           if (map_ip4_reass_conf_buffers (buffers))
1203             vlib_cli_output (vm, "Could not set ip4-reass buffers");
1204           else
1205             vlib_cli_output (vm, "Setting ip4-reass buffers");
1206         }
1207
1208       if (map_main.ip4_reass_conf_buffers >
1209           map_main.ip4_reass_conf_pool_size *
1210           MAP_IP4_REASS_MAX_FRAGMENTS_PER_REASSEMBLY)
1211         {
1212           vlib_cli_output (vm,
1213                            "Note: 'ip4-reass buffers' > pool-size * max-fragments-per-reassembly.");
1214         }
1215     }
1216
1217   if (ip6)
1218     {
1219       u32 reass = 0, packets = 0;
1220       if (pool_size != ~0)
1221         {
1222           if (map_ip6_reass_conf_pool_size (pool_size, &reass, &packets))
1223             {
1224               vlib_cli_output (vm, "Could not set ip6-reass pool-size");
1225             }
1226           else
1227             {
1228               vlib_cli_output (vm,
1229                                "Setting ip6-reass pool-size (destroyed-reassembly=%u , dropped-fragments=%u)",
1230                                reass, packets);
1231             }
1232         }
1233       if (ht_ratio != (MAP_IP4_REASS_CONF_HT_RATIO_MAX + 1))
1234         {
1235           if (map_ip6_reass_conf_ht_ratio (ht_ratio, &reass, &packets))
1236             {
1237               vlib_cli_output (vm, "Could not set ip6-reass ht-log2len");
1238             }
1239           else
1240             {
1241               vlib_cli_output (vm,
1242                                "Setting ip6-reass ht-log2len (destroyed-reassembly=%u , dropped-fragments=%u)",
1243                                reass, packets);
1244             }
1245         }
1246       if (lifetime != ~0)
1247         {
1248           if (map_ip6_reass_conf_lifetime (lifetime))
1249             vlib_cli_output (vm, "Could not set ip6-reass lifetime");
1250           else
1251             vlib_cli_output (vm, "Setting ip6-reass lifetime");
1252         }
1253       if (buffers != ~(0ull))
1254         {
1255           if (map_ip6_reass_conf_buffers (buffers))
1256             vlib_cli_output (vm, "Could not set ip6-reass buffers");
1257           else
1258             vlib_cli_output (vm, "Setting ip6-reass buffers");
1259         }
1260
1261       if (map_main.ip6_reass_conf_buffers >
1262           map_main.ip6_reass_conf_pool_size *
1263           MAP_IP6_REASS_MAX_FRAGMENTS_PER_REASSEMBLY)
1264         {
1265           vlib_cli_output (vm,
1266                            "Note: 'ip6-reass buffers' > pool-size * max-fragments-per-reassembly.");
1267         }
1268     }
1269
1270   return 0;
1271 }
1272
1273
1274 /*
1275  * packet trace format function
1276  */
1277 u8 *
1278 format_map_trace (u8 * s, va_list * args)
1279 {
1280   CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
1281   CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
1282   map_trace_t *t = va_arg (*args, map_trace_t *);
1283   u32 map_domain_index = t->map_domain_index;
1284   u16 port = t->port;
1285
1286   s =
1287     format (s, "MAP domain index: %d L4 port: %u", map_domain_index,
1288             clib_net_to_host_u16 (port));
1289
1290   return s;
1291 }
1292
1293 static_always_inline map_ip4_reass_t *
1294 map_ip4_reass_lookup (map_ip4_reass_key_t * k, u32 bucket, f64 now)
1295 {
1296   map_main_t *mm = &map_main;
1297   u32 ri = mm->ip4_reass_hash_table[bucket];
1298   while (ri != MAP_REASS_INDEX_NONE)
1299     {
1300       map_ip4_reass_t *r = pool_elt_at_index (mm->ip4_reass_pool, ri);
1301       if (r->key.as_u64[0] == k->as_u64[0] &&
1302           r->key.as_u64[1] == k->as_u64[1] &&
1303           now < r->ts + (((f64) mm->ip4_reass_conf_lifetime_ms) / 1000))
1304         {
1305           return r;
1306         }
1307       ri = r->bucket_next;
1308     }
1309   return NULL;
1310 }
1311
1312 #define map_ip4_reass_pool_index(r) (r - map_main.ip4_reass_pool)
1313
1314 void
1315 map_ip4_reass_free (map_ip4_reass_t * r, u32 ** pi_to_drop)
1316 {
1317   map_main_t *mm = &map_main;
1318   map_ip4_reass_get_fragments (r, pi_to_drop);
1319
1320   // Unlink in hash bucket
1321   map_ip4_reass_t *r2 = NULL;
1322   u32 r2i = mm->ip4_reass_hash_table[r->bucket];
1323   while (r2i != map_ip4_reass_pool_index (r))
1324     {
1325       ASSERT (r2i != MAP_REASS_INDEX_NONE);
1326       r2 = pool_elt_at_index (mm->ip4_reass_pool, r2i);
1327       r2i = r2->bucket_next;
1328     }
1329   if (r2)
1330     {
1331       r2->bucket_next = r->bucket_next;
1332     }
1333   else
1334     {
1335       mm->ip4_reass_hash_table[r->bucket] = r->bucket_next;
1336     }
1337
1338   // Unlink in list
1339   if (r->fifo_next == map_ip4_reass_pool_index (r))
1340     {
1341       mm->ip4_reass_fifo_last = MAP_REASS_INDEX_NONE;
1342     }
1343   else
1344     {
1345       if (mm->ip4_reass_fifo_last == map_ip4_reass_pool_index (r))
1346         mm->ip4_reass_fifo_last = r->fifo_prev;
1347       pool_elt_at_index (mm->ip4_reass_pool, r->fifo_prev)->fifo_next =
1348         r->fifo_next;
1349       pool_elt_at_index (mm->ip4_reass_pool, r->fifo_next)->fifo_prev =
1350         r->fifo_prev;
1351     }
1352
1353   pool_put (mm->ip4_reass_pool, r);
1354   mm->ip4_reass_allocated--;
1355 }
1356
1357 map_ip4_reass_t *
1358 map_ip4_reass_get (u32 src, u32 dst, u16 fragment_id,
1359                    u8 protocol, u32 ** pi_to_drop)
1360 {
1361   map_ip4_reass_t *r;
1362   map_main_t *mm = &map_main;
1363   map_ip4_reass_key_t k = {.src.data_u32 = src,
1364     .dst.data_u32 = dst,
1365     .fragment_id = fragment_id,
1366     .protocol = protocol
1367   };
1368
1369   u32 h = 0;
1370   h = crc_u32 (k.as_u32[0], h);
1371   h = crc_u32 (k.as_u32[1], h);
1372   h = crc_u32 (k.as_u32[2], h);
1373   h = crc_u32 (k.as_u32[3], h);
1374   h = h >> (32 - mm->ip4_reass_ht_log2len);
1375
1376   f64 now = vlib_time_now (mm->vlib_main);
1377
1378   //Cache garbage collection
1379   while (mm->ip4_reass_fifo_last != MAP_REASS_INDEX_NONE)
1380     {
1381       map_ip4_reass_t *last =
1382         pool_elt_at_index (mm->ip4_reass_pool, mm->ip4_reass_fifo_last);
1383       if (last->ts + (((f64) mm->ip4_reass_conf_lifetime_ms) / 1000) < now)
1384         map_ip4_reass_free (last, pi_to_drop);
1385       else
1386         break;
1387     }
1388
1389   if ((r = map_ip4_reass_lookup (&k, h, now)))
1390     return r;
1391
1392   if (mm->ip4_reass_allocated >= mm->ip4_reass_conf_pool_size)
1393     return NULL;
1394
1395   pool_get (mm->ip4_reass_pool, r);
1396   mm->ip4_reass_allocated++;
1397   int i;
1398   for (i = 0; i < MAP_IP4_REASS_MAX_FRAGMENTS_PER_REASSEMBLY; i++)
1399     r->fragments[i] = ~0;
1400
1401   u32 ri = map_ip4_reass_pool_index (r);
1402
1403   //Link in new bucket
1404   r->bucket = h;
1405   r->bucket_next = mm->ip4_reass_hash_table[h];
1406   mm->ip4_reass_hash_table[h] = ri;
1407
1408   //Link in fifo
1409   if (mm->ip4_reass_fifo_last != MAP_REASS_INDEX_NONE)
1410     {
1411       r->fifo_next =
1412         pool_elt_at_index (mm->ip4_reass_pool,
1413                            mm->ip4_reass_fifo_last)->fifo_next;
1414       r->fifo_prev = mm->ip4_reass_fifo_last;
1415       pool_elt_at_index (mm->ip4_reass_pool, r->fifo_prev)->fifo_next = ri;
1416       pool_elt_at_index (mm->ip4_reass_pool, r->fifo_next)->fifo_prev = ri;
1417     }
1418   else
1419     {
1420       r->fifo_next = r->fifo_prev = ri;
1421       mm->ip4_reass_fifo_last = ri;
1422     }
1423
1424   //Set other fields
1425   r->ts = now;
1426   r->key = k;
1427   r->port = -1;
1428 #ifdef MAP_IP4_REASS_COUNT_BYTES
1429   r->expected_total = 0xffff;
1430   r->forwarded = 0;
1431 #endif
1432
1433   return r;
1434 }
1435
1436 int
1437 map_ip4_reass_add_fragment (map_ip4_reass_t * r, u32 pi)
1438 {
1439   if (map_main.ip4_reass_buffered_counter >= map_main.ip4_reass_conf_buffers)
1440     return -1;
1441
1442   int i;
1443   for (i = 0; i < MAP_IP4_REASS_MAX_FRAGMENTS_PER_REASSEMBLY; i++)
1444     if (r->fragments[i] == ~0)
1445       {
1446         r->fragments[i] = pi;
1447         map_main.ip4_reass_buffered_counter++;
1448         return 0;
1449       }
1450   return -1;
1451 }
1452
1453 static_always_inline map_ip6_reass_t *
1454 map_ip6_reass_lookup (map_ip6_reass_key_t * k, u32 bucket, f64 now)
1455 {
1456   map_main_t *mm = &map_main;
1457   u32 ri = mm->ip6_reass_hash_table[bucket];
1458   while (ri != MAP_REASS_INDEX_NONE)
1459     {
1460       map_ip6_reass_t *r = pool_elt_at_index (mm->ip6_reass_pool, ri);
1461       if (now < r->ts + (((f64) mm->ip6_reass_conf_lifetime_ms) / 1000) &&
1462           r->key.as_u64[0] == k->as_u64[0] &&
1463           r->key.as_u64[1] == k->as_u64[1] &&
1464           r->key.as_u64[2] == k->as_u64[2] &&
1465           r->key.as_u64[3] == k->as_u64[3] &&
1466           r->key.as_u64[4] == k->as_u64[4])
1467         return r;
1468       ri = r->bucket_next;
1469     }
1470   return NULL;
1471 }
1472
1473 #define map_ip6_reass_pool_index(r) (r - map_main.ip6_reass_pool)
1474
1475 void
1476 map_ip6_reass_free (map_ip6_reass_t * r, u32 ** pi_to_drop)
1477 {
1478   map_main_t *mm = &map_main;
1479   int i;
1480   for (i = 0; i < MAP_IP6_REASS_MAX_FRAGMENTS_PER_REASSEMBLY; i++)
1481     if (r->fragments[i].pi != ~0)
1482       {
1483         vec_add1 (*pi_to_drop, r->fragments[i].pi);
1484         r->fragments[i].pi = ~0;
1485         map_main.ip6_reass_buffered_counter--;
1486       }
1487
1488   // Unlink in hash bucket
1489   map_ip6_reass_t *r2 = NULL;
1490   u32 r2i = mm->ip6_reass_hash_table[r->bucket];
1491   while (r2i != map_ip6_reass_pool_index (r))
1492     {
1493       ASSERT (r2i != MAP_REASS_INDEX_NONE);
1494       r2 = pool_elt_at_index (mm->ip6_reass_pool, r2i);
1495       r2i = r2->bucket_next;
1496     }
1497   if (r2)
1498     {
1499       r2->bucket_next = r->bucket_next;
1500     }
1501   else
1502     {
1503       mm->ip6_reass_hash_table[r->bucket] = r->bucket_next;
1504     }
1505
1506   // Unlink in list
1507   if (r->fifo_next == map_ip6_reass_pool_index (r))
1508     {
1509       //Single element in the list, list is now empty
1510       mm->ip6_reass_fifo_last = MAP_REASS_INDEX_NONE;
1511     }
1512   else
1513     {
1514       if (mm->ip6_reass_fifo_last == map_ip6_reass_pool_index (r))      //First element
1515         mm->ip6_reass_fifo_last = r->fifo_prev;
1516       pool_elt_at_index (mm->ip6_reass_pool, r->fifo_prev)->fifo_next =
1517         r->fifo_next;
1518       pool_elt_at_index (mm->ip6_reass_pool, r->fifo_next)->fifo_prev =
1519         r->fifo_prev;
1520     }
1521
1522   // Free from pool if necessary
1523   pool_put (mm->ip6_reass_pool, r);
1524   mm->ip6_reass_allocated--;
1525 }
1526
1527 map_ip6_reass_t *
1528 map_ip6_reass_get (ip6_address_t * src, ip6_address_t * dst, u32 fragment_id,
1529                    u8 protocol, u32 ** pi_to_drop)
1530 {
1531   map_ip6_reass_t *r;
1532   map_main_t *mm = &map_main;
1533   map_ip6_reass_key_t k = {
1534     .src = *src,
1535     .dst = *dst,
1536     .fragment_id = fragment_id,
1537     .protocol = protocol
1538   };
1539
1540   u32 h = 0;
1541   int i;
1542   for (i = 0; i < 10; i++)
1543     h = crc_u32 (k.as_u32[i], h);
1544   h = h >> (32 - mm->ip6_reass_ht_log2len);
1545
1546   f64 now = vlib_time_now (mm->vlib_main);
1547
1548   //Cache garbage collection
1549   while (mm->ip6_reass_fifo_last != MAP_REASS_INDEX_NONE)
1550     {
1551       map_ip6_reass_t *last =
1552         pool_elt_at_index (mm->ip6_reass_pool, mm->ip6_reass_fifo_last);
1553       if (last->ts + (((f64) mm->ip6_reass_conf_lifetime_ms) / 1000) < now)
1554         map_ip6_reass_free (last, pi_to_drop);
1555       else
1556         break;
1557     }
1558
1559   if ((r = map_ip6_reass_lookup (&k, h, now)))
1560     return r;
1561
1562   if (mm->ip6_reass_allocated >= mm->ip6_reass_conf_pool_size)
1563     return NULL;
1564
1565   pool_get (mm->ip6_reass_pool, r);
1566   mm->ip6_reass_allocated++;
1567   for (i = 0; i < MAP_IP6_REASS_MAX_FRAGMENTS_PER_REASSEMBLY; i++)
1568     {
1569       r->fragments[i].pi = ~0;
1570       r->fragments[i].next_data_len = 0;
1571       r->fragments[i].next_data_offset = 0;
1572     }
1573
1574   u32 ri = map_ip6_reass_pool_index (r);
1575
1576   //Link in new bucket
1577   r->bucket = h;
1578   r->bucket_next = mm->ip6_reass_hash_table[h];
1579   mm->ip6_reass_hash_table[h] = ri;
1580
1581   //Link in fifo
1582   if (mm->ip6_reass_fifo_last != MAP_REASS_INDEX_NONE)
1583     {
1584       r->fifo_next =
1585         pool_elt_at_index (mm->ip6_reass_pool,
1586                            mm->ip6_reass_fifo_last)->fifo_next;
1587       r->fifo_prev = mm->ip6_reass_fifo_last;
1588       pool_elt_at_index (mm->ip6_reass_pool, r->fifo_prev)->fifo_next = ri;
1589       pool_elt_at_index (mm->ip6_reass_pool, r->fifo_next)->fifo_prev = ri;
1590     }
1591   else
1592     {
1593       r->fifo_next = r->fifo_prev = ri;
1594       mm->ip6_reass_fifo_last = ri;
1595     }
1596
1597   //Set other fields
1598   r->ts = now;
1599   r->key = k;
1600   r->ip4_header.ip_version_and_header_length = 0;
1601 #ifdef MAP_IP6_REASS_COUNT_BYTES
1602   r->expected_total = 0xffff;
1603   r->forwarded = 0;
1604 #endif
1605   return r;
1606 }
1607
1608 int
1609 map_ip6_reass_add_fragment (map_ip6_reass_t * r, u32 pi,
1610                             u16 data_offset, u16 next_data_offset,
1611                             u8 * data_start, u16 data_len)
1612 {
1613   map_ip6_fragment_t *f = NULL, *prev_f = NULL;
1614   u16 copied_len = (data_len > 20) ? 20 : data_len;
1615
1616   if (map_main.ip6_reass_buffered_counter >= map_main.ip6_reass_conf_buffers)
1617     return -1;
1618
1619   //Lookup for fragments for the current buffer
1620   //and the one before that
1621   int i;
1622   for (i = 0; i < MAP_IP6_REASS_MAX_FRAGMENTS_PER_REASSEMBLY; i++)
1623     {
1624       if (data_offset && r->fragments[i].next_data_offset == data_offset)
1625         {
1626           prev_f = &r->fragments[i];    // This is buffer for previous packet
1627         }
1628       else if (r->fragments[i].next_data_offset == next_data_offset)
1629         {
1630           f = &r->fragments[i]; // This is a buffer for the current packet
1631         }
1632       else if (r->fragments[i].next_data_offset == 0)
1633         {                       //Available
1634           if (f == NULL)
1635             f = &r->fragments[i];
1636           else if (prev_f == NULL)
1637             prev_f = &r->fragments[i];
1638         }
1639     }
1640
1641   if (!f || f->pi != ~0)
1642     return -1;
1643
1644   if (data_offset)
1645     {
1646       if (!prev_f)
1647         return -1;
1648
1649       clib_memcpy (prev_f->next_data, data_start, copied_len);
1650       prev_f->next_data_len = copied_len;
1651       prev_f->next_data_offset = data_offset;
1652     }
1653   else
1654     {
1655       if (((ip4_header_t *) data_start)->ip_version_and_header_length != 0x45)
1656         return -1;
1657
1658       if (r->ip4_header.ip_version_and_header_length == 0)
1659         clib_memcpy (&r->ip4_header, data_start, sizeof (ip4_header_t));
1660     }
1661
1662   if (data_len > 20)
1663     {
1664       f->next_data_offset = next_data_offset;
1665       f->pi = pi;
1666       map_main.ip6_reass_buffered_counter++;
1667     }
1668   return 0;
1669 }
1670
1671 void
1672 map_ip4_reass_reinit (u32 * trashed_reass, u32 * dropped_packets)
1673 {
1674   map_main_t *mm = &map_main;
1675   int i;
1676
1677   if (dropped_packets)
1678     *dropped_packets = mm->ip4_reass_buffered_counter;
1679   if (trashed_reass)
1680     *trashed_reass = mm->ip4_reass_allocated;
1681   if (mm->ip4_reass_fifo_last != MAP_REASS_INDEX_NONE)
1682     {
1683       u16 ri = mm->ip4_reass_fifo_last;
1684       do
1685         {
1686           map_ip4_reass_t *r = pool_elt_at_index (mm->ip4_reass_pool, ri);
1687           for (i = 0; i < MAP_IP4_REASS_MAX_FRAGMENTS_PER_REASSEMBLY; i++)
1688             if (r->fragments[i] != ~0)
1689               map_ip4_drop_pi (r->fragments[i]);
1690
1691           ri = r->fifo_next;
1692           pool_put (mm->ip4_reass_pool, r);
1693         }
1694       while (ri != mm->ip4_reass_fifo_last);
1695     }
1696
1697   vec_free (mm->ip4_reass_hash_table);
1698   vec_resize (mm->ip4_reass_hash_table, 1 << mm->ip4_reass_ht_log2len);
1699   for (i = 0; i < (1 << mm->ip4_reass_ht_log2len); i++)
1700     mm->ip4_reass_hash_table[i] = MAP_REASS_INDEX_NONE;
1701   pool_free (mm->ip4_reass_pool);
1702   pool_alloc (mm->ip4_reass_pool, mm->ip4_reass_conf_pool_size);
1703
1704   mm->ip4_reass_allocated = 0;
1705   mm->ip4_reass_fifo_last = MAP_REASS_INDEX_NONE;
1706   mm->ip4_reass_buffered_counter = 0;
1707 }
1708
1709 u8
1710 map_get_ht_log2len (f32 ht_ratio, u16 pool_size)
1711 {
1712   u32 desired_size = (u32) (pool_size * ht_ratio);
1713   u8 i;
1714   for (i = 1; i < 31; i++)
1715     if ((1 << i) >= desired_size)
1716       return i;
1717   return 4;
1718 }
1719
1720 int
1721 map_ip4_reass_conf_ht_ratio (f32 ht_ratio, u32 * trashed_reass,
1722                              u32 * dropped_packets)
1723 {
1724   map_main_t *mm = &map_main;
1725   if (ht_ratio > MAP_IP4_REASS_CONF_HT_RATIO_MAX)
1726     return -1;
1727
1728   map_ip4_reass_lock ();
1729   mm->ip4_reass_conf_ht_ratio = ht_ratio;
1730   mm->ip4_reass_ht_log2len =
1731     map_get_ht_log2len (ht_ratio, mm->ip4_reass_conf_pool_size);
1732   map_ip4_reass_reinit (trashed_reass, dropped_packets);
1733   map_ip4_reass_unlock ();
1734   return 0;
1735 }
1736
1737 int
1738 map_ip4_reass_conf_pool_size (u16 pool_size, u32 * trashed_reass,
1739                               u32 * dropped_packets)
1740 {
1741   map_main_t *mm = &map_main;
1742   if (pool_size > MAP_IP4_REASS_CONF_POOL_SIZE_MAX)
1743     return -1;
1744
1745   map_ip4_reass_lock ();
1746   mm->ip4_reass_conf_pool_size = pool_size;
1747   map_ip4_reass_reinit (trashed_reass, dropped_packets);
1748   map_ip4_reass_unlock ();
1749   return 0;
1750 }
1751
1752 int
1753 map_ip4_reass_conf_lifetime (u16 lifetime_ms)
1754 {
1755   map_main.ip4_reass_conf_lifetime_ms = lifetime_ms;
1756   return 0;
1757 }
1758
1759 int
1760 map_ip4_reass_conf_buffers (u32 buffers)
1761 {
1762   map_main.ip4_reass_conf_buffers = buffers;
1763   return 0;
1764 }
1765
1766 void
1767 map_ip6_reass_reinit (u32 * trashed_reass, u32 * dropped_packets)
1768 {
1769   map_main_t *mm = &map_main;
1770   if (dropped_packets)
1771     *dropped_packets = mm->ip6_reass_buffered_counter;
1772   if (trashed_reass)
1773     *trashed_reass = mm->ip6_reass_allocated;
1774   int i;
1775   if (mm->ip6_reass_fifo_last != MAP_REASS_INDEX_NONE)
1776     {
1777       u16 ri = mm->ip6_reass_fifo_last;
1778       do
1779         {
1780           map_ip6_reass_t *r = pool_elt_at_index (mm->ip6_reass_pool, ri);
1781           for (i = 0; i < MAP_IP6_REASS_MAX_FRAGMENTS_PER_REASSEMBLY; i++)
1782             if (r->fragments[i].pi != ~0)
1783               map_ip6_drop_pi (r->fragments[i].pi);
1784
1785           ri = r->fifo_next;
1786           pool_put (mm->ip6_reass_pool, r);
1787         }
1788       while (ri != mm->ip6_reass_fifo_last);
1789       mm->ip6_reass_fifo_last = MAP_REASS_INDEX_NONE;
1790     }
1791
1792   vec_free (mm->ip6_reass_hash_table);
1793   vec_resize (mm->ip6_reass_hash_table, 1 << mm->ip6_reass_ht_log2len);
1794   for (i = 0; i < (1 << mm->ip6_reass_ht_log2len); i++)
1795     mm->ip6_reass_hash_table[i] = MAP_REASS_INDEX_NONE;
1796   pool_free (mm->ip6_reass_pool);
1797   pool_alloc (mm->ip6_reass_pool, mm->ip4_reass_conf_pool_size);
1798
1799   mm->ip6_reass_allocated = 0;
1800   mm->ip6_reass_buffered_counter = 0;
1801 }
1802
1803 int
1804 map_ip6_reass_conf_ht_ratio (f32 ht_ratio, u32 * trashed_reass,
1805                              u32 * dropped_packets)
1806 {
1807   map_main_t *mm = &map_main;
1808   if (ht_ratio > MAP_IP6_REASS_CONF_HT_RATIO_MAX)
1809     return -1;
1810
1811   map_ip6_reass_lock ();
1812   mm->ip6_reass_conf_ht_ratio = ht_ratio;
1813   mm->ip6_reass_ht_log2len =
1814     map_get_ht_log2len (ht_ratio, mm->ip6_reass_conf_pool_size);
1815   map_ip6_reass_reinit (trashed_reass, dropped_packets);
1816   map_ip6_reass_unlock ();
1817   return 0;
1818 }
1819
1820 int
1821 map_ip6_reass_conf_pool_size (u16 pool_size, u32 * trashed_reass,
1822                               u32 * dropped_packets)
1823 {
1824   map_main_t *mm = &map_main;
1825   if (pool_size > MAP_IP6_REASS_CONF_POOL_SIZE_MAX)
1826     return -1;
1827
1828   map_ip6_reass_lock ();
1829   mm->ip6_reass_conf_pool_size = pool_size;
1830   map_ip6_reass_reinit (trashed_reass, dropped_packets);
1831   map_ip6_reass_unlock ();
1832   return 0;
1833 }
1834
1835 int
1836 map_ip6_reass_conf_lifetime (u16 lifetime_ms)
1837 {
1838   map_main.ip6_reass_conf_lifetime_ms = lifetime_ms;
1839   return 0;
1840 }
1841
1842 int
1843 map_ip6_reass_conf_buffers (u32 buffers)
1844 {
1845   map_main.ip6_reass_conf_buffers = buffers;
1846   return 0;
1847 }
1848
1849 /* *INDENT-OFF* */
1850
1851 /*?
1852  * Configure MAP reassembly behaviour
1853  *
1854  * @cliexpar
1855  * @cliexstart{map params reassembly}
1856  * @cliexend
1857  ?*/
1858 VLIB_CLI_COMMAND(map_ip4_reass_lifetime_command, static) = {
1859   .path = "map params reassembly",
1860   .short_help = "map params reassembly [ip4 | ip6] [lifetime <lifetime-ms>] "
1861                 "[pool-size <pool-size>] [buffers <buffers>] "
1862                 "[ht-ratio <ht-ratio>]",
1863   .function = map_params_reass_command_fn,
1864 };
1865
1866 /*?
1867  * Set or copy the IP TOS/Traffic Class field
1868  *
1869  * @cliexpar
1870  * @cliexstart{map params traffic-class}
1871  *
1872  * This command is used to set the traffic-class field in translated
1873  * or encapsulated packets. If copy is specifed (the default) then the
1874  * traffic-class/TOS field is copied from the original packet to the
1875  * translated / encapsulating header.
1876  * @cliexend
1877  ?*/
1878 VLIB_CLI_COMMAND(map_traffic_class_command, static) = {
1879   .path = "map params traffic-class",
1880   .short_help = "map params traffic-class {0x0-0xff | copy}",
1881   .function = map_traffic_class_command_fn,
1882 };
1883
1884 /*?
1885  * Bypass IP4/IP6 lookup
1886  *
1887  * @cliexpar
1888  * @cliexstart{map params pre-resolve}
1889  *
1890  * Bypass a second FIB lookup of the translated or encapsulated
1891  * packet, and forward the packet directly to the specified
1892  * next-hop. This optimization trades forwarding flexibility for
1893  * performance.
1894  * @cliexend
1895  ?*/
1896 VLIB_CLI_COMMAND(map_pre_resolve_command, static) = {
1897   .path = "map params pre-resolve",
1898   .short_help = " map params pre-resolve {ip4-nh <address>} "
1899                 "| {ip6-nh <address>}",
1900   .function = map_pre_resolve_command_fn,
1901 };
1902
1903 /*?
1904  * Enable or disable the MAP-E inbound security check
1905  *
1906  * @cliexpar
1907  * @cliexstart{map params security-check}
1908  *
1909  * By default, a decapsulated packet's IPv4 source address will be
1910  * verified against the outer header's IPv6 source address. Disabling
1911  * this feature will allow IPv4 source address spoofing.
1912  * @cliexend
1913  ?*/
1914 VLIB_CLI_COMMAND(map_security_check_command, static) = {
1915   .path = "map params security-check",
1916   .short_help = "map params security-check on|off",
1917   .function = map_security_check_command_fn,
1918 };
1919
1920 /*?
1921  * Specifiy the IPv4 source address used for relayed ICMP error messages
1922  *
1923  * @cliexpar
1924  * @cliexstart{map params icmp source-address}
1925  *
1926  * This command specifies which IPv4 source address (must be local to
1927  * the system), that is used for relayed received IPv6 ICMP error
1928  * messages.
1929  * @cliexend
1930  ?*/
1931 VLIB_CLI_COMMAND(map_icmp_relay_source_address_command, static) = {
1932   .path = "map params icmp source-address",
1933   .short_help = "map params icmp source-address <ip4-address>",
1934   .function = map_icmp_relay_source_address_command_fn,
1935 };
1936
1937 /*?
1938  * Send IPv6 ICMP unreachables
1939  *
1940  * @cliexpar
1941  * @cliexstart{map params icmp6 unreachables}
1942  *
1943  * Send IPv6 ICMP unreachable messages back if security check fails or
1944  * no MAP domain exists.
1945  * @cliexend
1946  ?*/
1947 VLIB_CLI_COMMAND(map_icmp_unreachables_command, static) = {
1948   .path = "map params icmp6 unreachables",
1949   .short_help = "map params icmp6 unreachables {on|off}",
1950   .function = map_icmp_unreachables_command_fn,
1951 };
1952
1953 /*?
1954  * Configure MAP fragmentation behaviour
1955  *
1956  * @cliexpar
1957  * @cliexstart{map params fragment}
1958  * @cliexend
1959  ?*/
1960 VLIB_CLI_COMMAND(map_fragment_command, static) = {
1961   .path = "map params fragment",
1962   .short_help = "map params fragment inner|outer",
1963   .function = map_fragment_command_fn,
1964 };
1965
1966 /*?
1967  * Ignore the IPv4 Don't fragment bit
1968  *
1969  * @cliexpar
1970  * @cliexstart{map params fragment ignore-df}
1971  *
1972  * Allows fragmentation of the IPv4 packet even if the DF bit is
1973  * set. The choice between inner or outer fragmentation of tunnel
1974  * packets is complicated. The benefit of inner fragmentation is that
1975  * the ultimate endpoint must reassemble, instead of the tunnel
1976  * endpoint.
1977  * @cliexend
1978  ?*/
1979 VLIB_CLI_COMMAND(map_fragment_df_command, static) = {
1980   .path = "map params fragment ignore-df",
1981   .short_help = "map params fragment ignore-df on|off",
1982   .function = map_fragment_df_command_fn,
1983 };
1984
1985 /*?
1986  * Specifiy if the inbound security check should be done on fragments
1987  *
1988  * @cliexpar
1989  * @cliexstart{map params security-check fragments}
1990  *
1991  * Typically the inbound on-decapsulation security check is only done
1992  * on the first packet. The packet that contains the L4
1993  * information. While a security check on every fragment is possible,
1994  * it has a cost. State must be created on the first fragment.
1995  * @cliexend
1996  ?*/
1997 VLIB_CLI_COMMAND(map_security_check_frag_command, static) = {
1998   .path = "map params security-check fragments",
1999   .short_help = "map params security-check fragments on|off",
2000   .function = map_security_check_frag_command_fn,
2001 };
2002
2003 /*?
2004  * Add MAP domain
2005  *
2006  * @cliexpar
2007  * @cliexstart{map add domain}
2008  * @cliexend
2009  ?*/
2010 VLIB_CLI_COMMAND(map_add_domain_command, static) = {
2011   .path = "map add domain",
2012   .short_help = "map add domain ip4-pfx <ip4-pfx> ip6-pfx <ip6-pfx> "
2013       "ip6-src <ip6-pfx> ea-bits-len <n> psid-offset <n> psid-len <n> "
2014       "[map-t] [mtu <mtu>]",
2015   .function = map_add_domain_command_fn,
2016 };
2017
2018 /*?
2019  * Add MAP rule to a domain
2020  *
2021  * @cliexpar
2022  * @cliexstart{map add rule}
2023  * @cliexend
2024  ?*/
2025 VLIB_CLI_COMMAND(map_add_rule_command, static) = {
2026   .path = "map add rule",
2027   .short_help = "map add rule index <domain> psid <psid> ip6-dst <ip6-addr>",
2028   .function = map_add_rule_command_fn,
2029 };
2030
2031 /*?
2032  * Delete MAP domain
2033  *
2034  * @cliexpar
2035  * @cliexstart{map del domain}
2036  * @cliexend
2037  ?*/
2038 VLIB_CLI_COMMAND(map_del_command, static) = {
2039   .path = "map del domain",
2040   .short_help = "map del domain index <domain>",
2041   .function = map_del_domain_command_fn,
2042 };
2043
2044 /*?
2045  * Show MAP domains
2046  *
2047  * @cliexpar
2048  * @cliexstart{show map domain}
2049  * @cliexend
2050  ?*/
2051 VLIB_CLI_COMMAND(show_map_domain_command, static) = {
2052   .path = "show map domain",
2053   .short_help = "show map domain index <n> [counters]",
2054   .function = show_map_domain_command_fn,
2055 };
2056
2057 /*?
2058  * Show MAP statistics
2059  *
2060  * @cliexpar
2061  * @cliexstart{show map stats}
2062  * @cliexend
2063  ?*/
2064 VLIB_CLI_COMMAND(show_map_stats_command, static) = {
2065   .path = "show map stats",
2066   .short_help = "show map stats",
2067   .function = show_map_stats_command_fn,
2068 };
2069
2070 /*?
2071  * Show MAP fragmentation information
2072  *
2073  * @cliexpar
2074  * @cliexstart{show map fragments}
2075  * @cliexend
2076  ?*/
2077 VLIB_CLI_COMMAND(show_map_fragments_command, static) = {
2078   .path = "show map fragments",
2079   .short_help = "show map fragments",
2080   .function = show_map_fragments_command_fn,
2081 };
2082 /* *INDENT-ON* */
2083
2084 /*
2085  * map_init
2086  */
2087 clib_error_t *
2088 map_init (vlib_main_t * vm)
2089 {
2090   map_main_t *mm = &map_main;
2091   mm->vnet_main = vnet_get_main ();
2092   mm->vlib_main = vm;
2093
2094 #ifdef MAP_SKIP_IP6_LOOKUP
2095   memset (&mm->preresolve_ip4, 0, sizeof (mm->preresolve_ip4));
2096   memset (&mm->preresolve_ip6, 0, sizeof (mm->preresolve_ip6));
2097   mm->adj4_index = 0;
2098   mm->adj6_index = 0;
2099 #endif
2100
2101   /* traffic class */
2102   mm->tc = 0;
2103   mm->tc_copy = true;
2104
2105   /* Inbound security check */
2106   mm->sec_check = true;
2107   mm->sec_check_frag = false;
2108
2109   /* ICMP6 Type 1, Code 5 for security check failure */
2110   mm->icmp6_enabled = false;
2111
2112   /* Inner or outer fragmentation */
2113   mm->frag_inner = false;
2114   mm->frag_ignore_df = false;
2115
2116   vec_validate (mm->domain_counters, MAP_N_DOMAIN_COUNTER - 1);
2117   mm->domain_counters[MAP_DOMAIN_COUNTER_RX].name = "rx";
2118   mm->domain_counters[MAP_DOMAIN_COUNTER_TX].name = "tx";
2119
2120   vlib_validate_simple_counter (&mm->icmp_relayed, 0);
2121   vlib_zero_simple_counter (&mm->icmp_relayed, 0);
2122
2123   /* IP4 virtual reassembly */
2124   mm->ip4_reass_hash_table = 0;
2125   mm->ip4_reass_pool = 0;
2126   mm->ip4_reass_lock =
2127     clib_mem_alloc_aligned (CLIB_CACHE_LINE_BYTES, CLIB_CACHE_LINE_BYTES);
2128   mm->ip4_reass_conf_ht_ratio = MAP_IP4_REASS_HT_RATIO_DEFAULT;
2129   mm->ip4_reass_conf_lifetime_ms = MAP_IP4_REASS_LIFETIME_DEFAULT;
2130   mm->ip4_reass_conf_pool_size = MAP_IP4_REASS_POOL_SIZE_DEFAULT;
2131   mm->ip4_reass_conf_buffers = MAP_IP4_REASS_BUFFERS_DEFAULT;
2132   mm->ip4_reass_ht_log2len =
2133     map_get_ht_log2len (mm->ip4_reass_conf_ht_ratio,
2134                         mm->ip4_reass_conf_pool_size);
2135   mm->ip4_reass_fifo_last = MAP_REASS_INDEX_NONE;
2136   map_ip4_reass_reinit (NULL, NULL);
2137
2138   /* IP6 virtual reassembly */
2139   mm->ip6_reass_hash_table = 0;
2140   mm->ip6_reass_pool = 0;
2141   mm->ip6_reass_lock =
2142     clib_mem_alloc_aligned (CLIB_CACHE_LINE_BYTES, CLIB_CACHE_LINE_BYTES);
2143   mm->ip6_reass_conf_ht_ratio = MAP_IP6_REASS_HT_RATIO_DEFAULT;
2144   mm->ip6_reass_conf_lifetime_ms = MAP_IP6_REASS_LIFETIME_DEFAULT;
2145   mm->ip6_reass_conf_pool_size = MAP_IP6_REASS_POOL_SIZE_DEFAULT;
2146   mm->ip6_reass_conf_buffers = MAP_IP6_REASS_BUFFERS_DEFAULT;
2147   mm->ip6_reass_ht_log2len =
2148     map_get_ht_log2len (mm->ip6_reass_conf_ht_ratio,
2149                         mm->ip6_reass_conf_pool_size);
2150   mm->ip6_reass_fifo_last = MAP_REASS_INDEX_NONE;
2151   map_ip6_reass_reinit (NULL, NULL);
2152
2153   map_dpo_module_init ();
2154
2155   return 0;
2156 }
2157
2158 VLIB_INIT_FUNCTION (map_init);
2159
2160 /*
2161  * fd.io coding-style-patch-verification: ON
2162  *
2163  * Local Variables:
2164  * eval: (c-set-style "gnu")
2165  * End:
2166  */