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