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