- ICMP6: Add generic ICMP6 error node. Caller sets code/type fields.
[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 /*
21  * This code supports the following MAP modes:
22  * 
23  * Algorithmic Shared IPv4 address (ea_bits_len > 0):
24  *   ea_bits_len + ip4_prefix > 32
25  *   psid_length > 0, ip6_prefix < 64, ip4_prefix <= 32
26  * Algorithmic Full IPv4 address (ea_bits_len > 0):
27  *   ea_bits_len + ip4_prefix = 32
28  *   psid_length = 0, ip6_prefix < 64, ip4_prefix <= 32
29  * Algorithmic IPv4 prefix (ea_bits_len > 0):
30  *   ea_bits_len + ip4_prefix < 32
31  *   psid_length = 0, ip6_prefix < 64, ip4_prefix <= 32
32  *
33  * Independent Shared IPv4 address (ea_bits_len = 0):
34  *   ip4_prefix = 32
35  *   psid_length > 0
36  *   Rule IPv6 address = 128, Rule PSID Set
37  * Independent Full IPv4 address (ea_bits_len = 0):
38  *   ip4_prefix = 32
39  *   psid_length = 0, ip6_prefix = 128
40  * Independent IPv4 prefix (ea_bits_len = 0):
41  *   ip4_prefix < 32
42  *   psid_length = 0, ip6_prefix = 128
43  *
44  */
45
46 /*
47  * This code supports MAP-T:
48  *
49  * With DMR prefix length equal to 96.
50  *
51  */
52
53
54 i32
55 ip4_get_port (ip4_header_t *ip, map_dir_e dir, u16 buffer_len)
56 {
57   //TODO: use buffer length
58   if (ip->ip_version_and_header_length != 0x45 ||
59       ip4_get_fragment_offset(ip))
60       return -1;
61
62   if (PREDICT_TRUE((ip->protocol == IP_PROTOCOL_TCP) ||
63                    (ip->protocol == IP_PROTOCOL_UDP))) {
64     udp_header_t *udp = (void *)(ip + 1);
65     return (dir == MAP_SENDER) ? udp->src_port : udp->dst_port;
66   } else if (ip->protocol == IP_PROTOCOL_ICMP) {
67     icmp46_header_t *icmp = (void *)(ip + 1);
68     if (icmp->type == ICMP4_echo_request ||
69         icmp->type == ICMP4_echo_reply) {
70       return *((u16 *)(icmp + 1));
71     } else if (clib_net_to_host_u16(ip->length) >= 64) {
72       ip = (ip4_header_t *)(icmp + 2);
73       if (PREDICT_TRUE((ip->protocol == IP_PROTOCOL_TCP) ||
74                        (ip->protocol == IP_PROTOCOL_UDP))) {
75         udp_header_t *udp = (void *)(ip + 1);
76         return (dir == MAP_SENDER) ? udp->dst_port : udp->src_port;
77       } else if (ip->protocol == IP_PROTOCOL_ICMP) {
78         icmp46_header_t *icmp = (void *)(ip + 1);
79         if (icmp->type == ICMP4_echo_request ||
80             icmp->type == ICMP4_echo_reply) {
81           return *((u16 *)(icmp + 1));
82         }
83       }
84     }
85   }
86   return -1;
87 }
88
89 i32
90 ip6_get_port (ip6_header_t *ip6, map_dir_e dir, u16 buffer_len)
91 {
92   u8 l4_protocol;
93   u16 l4_offset;
94   u16 frag_offset;
95   u8 *l4;
96
97   if (ip6_parse(ip6, buffer_len, &l4_protocol, &l4_offset, &frag_offset))
98     return -1;
99
100   //TODO: Use buffer length
101
102   if (frag_offset &&
103       ip6_frag_hdr_offset(((ip6_frag_hdr_t *)u8_ptr_add(ip6, frag_offset))))
104     return -1; //Can't deal with non-first fragment for now
105
106   l4 = u8_ptr_add(ip6, l4_offset);
107   if (l4_protocol == IP_PROTOCOL_TCP ||
108       l4_protocol == IP_PROTOCOL_UDP) {
109     return (dir == MAP_SENDER) ? ((udp_header_t *)(l4))->src_port : ((udp_header_t *)(l4))->dst_port;
110   } else if (l4_protocol == IP_PROTOCOL_ICMP6) {
111     icmp46_header_t *icmp = (icmp46_header_t *)(l4);
112     if (icmp->type == ICMP6_echo_request) {
113       return (dir == MAP_SENDER) ? ((u16*)(icmp))[2] : -1;
114     } else if (icmp->type == ICMP6_echo_reply) {
115       return (dir == MAP_SENDER) ? -1 : ((u16*)(icmp))[2];
116     }
117   }
118   return -1;
119 }
120
121
122 int
123 map_create_domain (ip4_address_t *ip4_prefix,
124                    u8 ip4_prefix_len,
125                    ip6_address_t *ip6_prefix,
126                    u8 ip6_prefix_len,
127                    ip6_address_t *ip6_src,
128                    u8 ip6_src_len,
129                    u8 ea_bits_len,
130                    u8 psid_offset,
131                    u8 psid_length,
132                    u32 *map_domain_index,
133                    u16 mtu,
134                    u8 flags)
135 {
136   map_main_t *mm = &map_main;
137   ip4_main_t *im4 = &ip4_main;
138   ip6_main_t *im6 = &ip6_main;
139   map_domain_t *d;
140   ip_adjacency_t adj;
141   ip4_add_del_route_args_t args4;
142   ip6_add_del_route_args_t args6;
143   u8 suffix_len;
144   uword *p;
145
146   /* EA bits must be within the first 64 bits */
147   if (ea_bits_len > 0 && (ip6_prefix_len + ea_bits_len) > 64)
148     return -1;
149
150   /* Sanity check on the src prefix length */
151   if (flags & MAP_DOMAIN_TRANSLATION) {
152       if (ip6_src_len != 96) {
153           clib_warning("MAP-T only supports ip6_src_len = 96 for now.");
154           return -1;
155       }
156   } else {
157       if (ip6_src_len != 128) {
158           clib_warning("MAP-E requires a BR address, not a prefix (ip6_src_len should be 128).");
159           return -1;
160       }
161   }
162
163   /* Get domain index */
164   pool_get_aligned(mm->domains, d, CLIB_CACHE_LINE_BYTES);
165   memset(d, 0, sizeof (*d));
166   *map_domain_index = d - mm->domains;
167
168   /* Init domain struct */
169   d->ip4_prefix.as_u32 = ip4_prefix->as_u32;
170   d->ip4_prefix_len = ip4_prefix_len;
171   d->ip6_prefix = *ip6_prefix;
172   d->ip6_prefix_len = ip6_prefix_len;
173   d->ip6_src = *ip6_src;
174   d->ip6_src_len = ip6_src_len;
175   d->ea_bits_len = ea_bits_len;
176   d->psid_offset = psid_offset;
177   d->psid_length = psid_length;
178   d->mtu = mtu;
179   d->flags = flags;
180
181   /* How many, and which bits to grab from the IPv4 DA */
182   if (ip4_prefix_len + ea_bits_len < 32) {
183     d->flags |= MAP_DOMAIN_PREFIX;
184     suffix_len = d->suffix_shift = 32 - ip4_prefix_len - ea_bits_len;
185   } else {
186     d->suffix_shift = 0;
187     suffix_len = 32 - ip4_prefix_len;
188   }
189   d->suffix_mask = (1<<suffix_len) - 1;
190
191   d->psid_shift = 16 - psid_length - psid_offset;
192   d->psid_mask = (1 << d->psid_length) - 1;
193   d->ea_shift = 64 - ip6_prefix_len - suffix_len - d->psid_length;
194
195   /* Init IP adjacency */
196   memset(&adj, 0, sizeof(adj));
197   adj.explicit_fib_index = ~0;
198   adj.lookup_next_index = (d->flags & MAP_DOMAIN_TRANSLATION) ? IP_LOOKUP_NEXT_MAP_T : IP_LOOKUP_NEXT_MAP;
199   p = (uword *)&adj.rewrite_data[0];
200   *p = (uword) (*map_domain_index);
201
202   if (ip4_get_route(im4, 0, 0, (u8 *)ip4_prefix, ip4_prefix_len)) {
203     clib_warning("IPv4 route already defined: %U/%d", format_ip4_address, ip4_prefix, ip4_prefix_len);
204     pool_put(mm->domains, d);
205     return -1;
206   }
207     
208   /* Create ip4 adjacency */
209   memset(&args4, 0, sizeof(args4));
210   args4.table_index_or_table_id = 0;
211   args4.flags = IP4_ROUTE_FLAG_ADD;
212   args4.dst_address.as_u32 = ip4_prefix->as_u32;
213   args4.dst_address_length = ip4_prefix_len;
214
215   args4.adj_index = ~0;
216   args4.add_adj = &adj;
217   args4.n_add_adj = 1;
218   ip4_add_del_route(im4, &args4);
219
220   /* Multiple MAP domains may share same source IPv6 TEP */
221   u32 ai = ip6_get_route(im6, 0, 0, ip6_src, ip6_src_len);
222   if (ai > 0) {
223     ip_lookup_main_t *lm6 = &ip6_main.lookup_main;
224     ip_adjacency_t *adj6 = ip_get_adjacency(lm6, ai);
225     if (adj6->lookup_next_index != IP_LOOKUP_NEXT_MAP &&
226         adj6->lookup_next_index != IP_LOOKUP_NEXT_MAP_T) {
227       clib_warning("BR source address already assigned: %U", format_ip6_address, ip6_src);
228       pool_put(mm->domains, d);
229       return -1;
230     }
231     /* Shared source */
232     p = (uword *)&adj6->rewrite_data[0];
233     p[0] = ~0;
234
235     /* Add refcount, so we don't accidentially delete the route underneath someone */
236     p[1]++;
237   } else {
238     /* Create ip6 adjacency. */
239     memset(&args6, 0, sizeof(args6));
240     args6.table_index_or_table_id = 0;
241     args6.flags = IP6_ROUTE_FLAG_ADD;
242     args6.dst_address.as_u64[0] = ip6_src->as_u64[0];
243     args6.dst_address.as_u64[1] = ip6_src->as_u64[1];
244     args6.dst_address_length = ip6_src_len;
245     args6.adj_index = ~0;
246     args6.add_adj = &adj;
247     args6.n_add_adj = 1;
248     ip6_add_del_route(im6, &args6);
249   }
250
251   /* Validate packet/byte counters */
252   map_domain_counter_lock(mm);
253   int i;
254   for (i = 0; i < vec_len(mm->simple_domain_counters); i++) {
255     vlib_validate_simple_counter(&mm->simple_domain_counters[i], *map_domain_index);
256     vlib_zero_simple_counter(&mm->simple_domain_counters[i], *map_domain_index);
257   }
258   for (i = 0; i < vec_len(mm->domain_counters); i++) {
259     vlib_validate_combined_counter(&mm->domain_counters[i], *map_domain_index);
260     vlib_zero_combined_counter(&mm->domain_counters[i], *map_domain_index);
261   }
262   map_domain_counter_unlock(mm);
263
264   return 0;
265 }
266
267 /*
268  * map_delete_domain
269  */
270 int
271 map_delete_domain (u32 map_domain_index)
272 {
273   map_main_t *mm = &map_main;
274   ip4_main_t *im4 = &ip4_main;
275   ip6_main_t *im6 = &ip6_main;
276   map_domain_t *d;
277   ip_adjacency_t adj;
278   ip4_add_del_route_args_t args4;
279   ip6_add_del_route_args_t args6;
280
281   if (pool_is_free_index(mm->domains, map_domain_index)) {
282     clib_warning("MAP domain delete: domain does not exist: %d", map_domain_index);
283     return -1;
284   }
285
286   d = pool_elt_at_index(mm->domains, map_domain_index);
287
288   memset(&adj, 0, sizeof(adj));
289   adj.explicit_fib_index = ~0;
290   adj.lookup_next_index = (d->flags & MAP_DOMAIN_TRANSLATION) ? IP_LOOKUP_NEXT_MAP_T : IP_LOOKUP_NEXT_MAP;
291
292   /* Delete ip4 adjacency */
293   memset(&args4, 0, sizeof(args4));
294   args4.table_index_or_table_id = 0;
295   args4.flags = IP4_ROUTE_FLAG_DEL;
296   args4.dst_address.as_u32 = d->ip4_prefix.as_u32;
297   args4.dst_address_length = d->ip4_prefix_len;
298   args4.adj_index = 0;
299   args4.add_adj = &adj;
300   args4.n_add_adj = 0;
301   ip4_add_del_route(im4, &args4);
302
303   /* Delete ip6 adjacency */
304   u32 ai = ip6_get_route(im6, 0, 0, &d->ip6_src, d->ip6_src_len);
305   if (ai > 0) {
306     ip_lookup_main_t *lm6 = &ip6_main.lookup_main;
307     ip_adjacency_t *adj6 = ip_get_adjacency(lm6, ai);
308
309     uword *p = (uword *)&adj6->rewrite_data[0];
310     /* Delete route when no other domains use this source */
311     if (p[1] == 0) {
312       memset(&args6, 0, sizeof (args6));
313       args6.table_index_or_table_id = 0;
314       args6.flags = IP6_ROUTE_FLAG_DEL;
315       args6.dst_address.as_u64[0] = d->ip6_src.as_u64[0];
316       args6.dst_address.as_u64[1] = d->ip6_src.as_u64[1];
317       args6.dst_address_length = d->ip6_src_len;
318       args6.adj_index = 0;
319       args6.add_adj = &adj;
320       args6.n_add_adj = 0;
321       ip6_add_del_route(im6, &args6);
322     }
323     p[1]--;
324   }
325   /* Deleting rules */
326   if (d->rules)
327     clib_mem_free(d->rules);
328
329   pool_put(mm->domains, d);
330
331   return 0;
332 }
333
334 int
335 map_add_del_psid (u32 map_domain_index, u16 psid, ip6_address_t *tep,
336                   u8 is_add)
337 {
338   map_domain_t *d;
339   map_main_t *mm = &map_main;
340
341   if (pool_is_free_index(mm->domains, map_domain_index)) {
342     clib_warning("MAP rule: domain does not exist: %d", map_domain_index);
343     return -1;
344   }
345   d = pool_elt_at_index(mm->domains, map_domain_index);
346
347   /* Rules are only used in 1:1 independent case */
348   if (d->ea_bits_len > 0)
349     return (-1);
350
351   if (!d->rules) {
352     u32 l = (0x1 << d->psid_length) * sizeof(ip6_address_t);
353     d->rules = clib_mem_alloc_aligned(l, CLIB_CACHE_LINE_BYTES);
354     if (!d->rules) return -1;
355     memset(d->rules, 0, l);
356   }
357
358   if (psid >= (0x1 << d->psid_length)) {
359     clib_warning("MAP rule: PSID outside bounds: %d [%d]", psid, 0x1 << d->psid_length);
360     return -1;
361   }
362
363   if (is_add) {
364     d->rules[psid] = *tep;
365   } else {
366     memset(&d->rules[psid], 0, sizeof(ip6_address_t));
367   }
368   return 0;
369 }
370
371 #ifdef MAP_SKIP_IP6_LOOKUP
372 static void
373 map_pre_resolve (ip4_address_t *ip4, ip6_address_t *ip6)
374 {
375   map_main_t *mm = &map_main;
376   ip4_main_t *im4 = &ip4_main;
377   ip6_main_t *im6 = &ip6_main;
378
379   if (ip6->as_u64[0] != 0 || ip6->as_u64[1] != 0) {
380     mm->adj6_index = ip6_fib_lookup_with_table(im6, 0, ip6);
381     clib_warning("FIB lookup results in: %u", mm->adj6_index);
382   }
383   if (ip4->as_u32 != 0) {
384     mm->adj4_index = ip4_fib_lookup_with_table(im4, 0, ip4, 0);
385     clib_warning("FIB lookup results in: %u", mm->adj4_index);
386   }
387 }
388 #endif
389
390 static clib_error_t *
391 map_security_check_command_fn (vlib_main_t *vm,
392                                unformat_input_t *input,
393                                vlib_cli_command_t *cmd)
394 {
395   unformat_input_t _line_input, *line_input = &_line_input;
396   map_main_t *mm = &map_main;
397   /* Get a line of input. */
398   if (!unformat_user(input, unformat_line_input, line_input))
399     return 0;
400  
401   while (unformat_check_input(line_input) != UNFORMAT_END_OF_INPUT) {
402     if (unformat(line_input, "off"))
403       mm->sec_check = false;
404     else if (unformat(line_input, "on"))
405       mm->sec_check = true;
406     else
407       return clib_error_return(0, "unknown input `%U'",
408                                format_unformat_error, input);
409   }
410   unformat_free(line_input);
411   return 0;
412 }
413
414 static clib_error_t *
415 map_security_check_frag_command_fn (vlib_main_t *vm,
416                                     unformat_input_t *input,
417                                     vlib_cli_command_t *cmd)
418 {
419   unformat_input_t _line_input, *line_input = &_line_input;
420   map_main_t *mm = &map_main;
421   /* Get a line of input. */
422   if (!unformat_user(input, unformat_line_input, line_input))
423     return 0;
424  
425   while (unformat_check_input(line_input) != UNFORMAT_END_OF_INPUT) {
426     if (unformat(line_input, "off"))
427       mm->sec_check_frag = false;
428     else if (unformat(line_input, "on"))
429       mm->sec_check_frag = true;
430     else
431       return clib_error_return(0, "unknown input `%U'",
432                                format_unformat_error, input);
433   }
434   unformat_free(line_input);
435   return 0;
436 }
437
438 static clib_error_t *
439 map_add_domain_command_fn (vlib_main_t *vm,
440                            unformat_input_t *input,
441                            vlib_cli_command_t *cmd)
442 {
443   unformat_input_t _line_input, *line_input = &_line_input;
444   ip4_address_t ip4_prefix;
445   ip6_address_t ip6_prefix;
446   ip6_address_t ip6_src;
447   u32 ip6_prefix_len, ip4_prefix_len, map_domain_index, ip6_src_len;
448   u32 num_m_args = 0;
449   /* Optional arguments */
450   u32 ea_bits_len, psid_offset = 0, psid_length = 0;
451   u32 mtu = 0;
452   u8 flags = 0;
453   ip6_src_len = 128;
454
455   /* Get a line of input. */
456   if (!unformat_user(input, unformat_line_input, line_input))
457     return 0;
458  
459   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT) {
460     if (unformat(line_input, "ip4-pfx %U/%d", unformat_ip4_address, &ip4_prefix, &ip4_prefix_len))
461       num_m_args++;
462     else if (unformat(line_input, "ip6-pfx %U/%d", unformat_ip6_address, &ip6_prefix, &ip6_prefix_len))
463       num_m_args++;
464     else if (unformat(line_input, "ip6-src %U/%d", unformat_ip6_address, &ip6_src, &ip6_src_len))
465       num_m_args++;
466     else if (unformat(line_input, "ip6-src %U", unformat_ip6_address, &ip6_src))
467       num_m_args++;
468     else if (unformat(line_input, "ea-bits-len %d", &ea_bits_len))
469       num_m_args++;
470     else if (unformat(line_input, "psid-offset %d", &psid_offset))
471       num_m_args++;
472     else if (unformat(line_input, "psid-len %d", &psid_length))
473       num_m_args++;
474     else if (unformat(line_input, "mtu %d", &mtu))
475       num_m_args++;
476     else if (unformat(line_input, "map-t"))
477       flags |= MAP_DOMAIN_TRANSLATION;
478     else
479       return clib_error_return(0, "unknown input `%U'",
480                                format_unformat_error, input);
481   }
482   unformat_free(line_input);
483
484   if (num_m_args < 3)
485     return clib_error_return(0, "mandatory argument(s) missing");
486
487   map_create_domain(&ip4_prefix, ip4_prefix_len,
488                     &ip6_prefix, ip6_prefix_len, &ip6_src, ip6_src_len,
489                     ea_bits_len, psid_offset, psid_length, &map_domain_index,
490                     mtu, flags);
491
492   return 0;
493 }
494
495 static clib_error_t *
496 map_del_domain_command_fn (vlib_main_t *vm,
497                            unformat_input_t *input,
498                            vlib_cli_command_t *cmd)
499 {
500   unformat_input_t _line_input, *line_input = &_line_input;
501   u32 num_m_args = 0;
502   u32 map_domain_index;
503
504   /* Get a line of input. */
505   if (! unformat_user(input, unformat_line_input, line_input))
506     return 0;
507  
508   while (unformat_check_input(line_input) != UNFORMAT_END_OF_INPUT) {
509     if (unformat(line_input, "index %d", &map_domain_index))
510       num_m_args++;
511     else
512       return clib_error_return(0, "unknown input `%U'",
513                                 format_unformat_error, input);
514   }
515   unformat_free(line_input);
516
517   if (num_m_args != 1)
518     return clib_error_return(0, "mandatory argument(s) missing");
519
520   map_delete_domain(map_domain_index);
521
522   return 0;
523 }
524
525 static clib_error_t *
526 map_add_rule_command_fn (vlib_main_t *vm,
527                          unformat_input_t *input,
528                          vlib_cli_command_t *cmd)
529 {
530   unformat_input_t _line_input, *line_input = &_line_input;
531   ip6_address_t tep;
532   u32 num_m_args = 0;
533   u32 psid, map_domain_index;
534     
535   /* Get a line of input. */
536   if (! unformat_user(input, unformat_line_input, line_input))
537     return 0;
538
539   while (unformat_check_input(line_input) != UNFORMAT_END_OF_INPUT) {
540     if (unformat(line_input, "index %d", &map_domain_index))
541       num_m_args++;
542     else if (unformat(line_input, "psid %d", &psid))
543       num_m_args++;
544     else if (unformat(line_input, "ip6-dst %U", unformat_ip6_address, &tep))
545       num_m_args++;
546     else
547       return clib_error_return(0, "unknown input `%U'",
548                                format_unformat_error, input);
549   }
550   unformat_free(line_input);
551
552   if (num_m_args != 3)
553     return clib_error_return(0, "mandatory argument(s) missing");
554
555   if (map_add_del_psid(map_domain_index, psid, &tep, 1) != 0) {
556     return clib_error_return(0, "Failing to add Mapping Rule");
557   }
558   return 0;
559 }
560
561 #if MAP_SKIP_IP6_LOOKUP
562 static clib_error_t *
563 map_pre_resolve_command_fn (vlib_main_t *vm,
564                             unformat_input_t *input,
565                             vlib_cli_command_t *cmd)
566 {
567   unformat_input_t _line_input, *line_input = &_line_input;
568   ip4_address_t ip4nh;
569   ip6_address_t ip6nh;
570   map_main_t *mm = &map_main;
571
572   memset(&ip4nh, 0, sizeof(ip4nh));
573   memset(&ip6nh, 0, sizeof(ip6nh));
574
575   /* Get a line of input. */
576   if (!unformat_user(input, unformat_line_input, line_input))
577     return 0;
578  
579   while (unformat_check_input(line_input) != UNFORMAT_END_OF_INPUT) {
580     if (unformat(line_input, "ip4-nh %U", unformat_ip4_address, &ip4nh))
581       mm->preresolve_ip4 = ip4nh;
582     else if (unformat(line_input, "ip6-nh %U", unformat_ip6_address, &ip6nh))
583       mm->preresolve_ip6 = ip6nh;
584     else
585       return clib_error_return(0, "unknown input `%U'",
586                                format_unformat_error, input);
587   }
588   unformat_free(line_input);
589
590   map_pre_resolve(&ip4nh, &ip6nh);
591
592   return 0;
593 }
594 #endif
595
596 static clib_error_t *
597 map_icmp_relay_source_address_command_fn (vlib_main_t *vm,
598                                           unformat_input_t *input,
599                                           vlib_cli_command_t *cmd)
600 {
601   unformat_input_t _line_input, *line_input = &_line_input;
602   ip4_address_t icmp_src_address;
603   map_main_t *mm = &map_main;
604
605   mm->icmp4_src_address.as_u32 = 0;
606
607   /* Get a line of input. */
608   if (!unformat_user(input, unformat_line_input, line_input))
609     return 0;
610  
611   while (unformat_check_input(line_input) != UNFORMAT_END_OF_INPUT) {
612     if (unformat(line_input, "%U", unformat_ip4_address, &icmp_src_address))
613       mm->icmp4_src_address = icmp_src_address;
614     else
615       return clib_error_return(0, "unknown input `%U'",
616                                format_unformat_error, input);
617   }
618   unformat_free(line_input);
619
620   return 0;
621 }
622
623 static clib_error_t *
624 map_icmp_unreachables_command_fn (vlib_main_t *vm,
625                                   unformat_input_t *input,
626                                   vlib_cli_command_t *cmd)
627 {
628   unformat_input_t _line_input, *line_input = &_line_input;
629   map_main_t *mm = &map_main;
630   int num_m_args = 0;
631
632   /* Get a line of input. */
633   if (!unformat_user(input, unformat_line_input, line_input))
634     return 0;
635  
636   while (unformat_check_input(line_input) != UNFORMAT_END_OF_INPUT) {
637     num_m_args++;
638     if (unformat(line_input, "on"))
639       mm->icmp6_enabled = true;
640     else if (unformat(line_input, "off"))
641       mm->icmp6_enabled = false;
642     else
643       return clib_error_return(0, "unknown input `%U'",
644                                format_unformat_error, input);
645   }
646   unformat_free(line_input);
647
648
649   if (num_m_args != 1)
650     return clib_error_return(0, "mandatory argument(s) missing");
651
652   return 0;
653 }
654
655 static clib_error_t *
656 map_traffic_class_command_fn (vlib_main_t *vm,
657                               unformat_input_t *input,
658                               vlib_cli_command_t *cmd)
659 {
660   unformat_input_t _line_input, *line_input = &_line_input;
661   map_main_t *mm = &map_main;
662   u32 tc = 0;
663
664   mm->tc_copy = false;
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     if (unformat(line_input, "copy"))
672       mm->tc_copy = true;
673     else if (unformat(line_input, "%x", &tc))
674       mm->tc = tc & 0xff;
675     else
676       return clib_error_return(0, "unknown input `%U'",
677                                format_unformat_error, input);
678   }
679   unformat_free(line_input);
680
681   return 0;
682 }
683
684 static u8 *
685 format_map_domain (u8 *s, va_list *args)
686 {
687   map_domain_t *d = va_arg(*args, map_domain_t *);
688   bool counters = va_arg(*args, int);
689   map_main_t *mm = &map_main;
690   ip6_address_t ip6_prefix;
691
692   if (d->rules)
693     memset(&ip6_prefix, 0, sizeof(ip6_prefix));
694   else
695     ip6_prefix = d->ip6_prefix;
696   
697   s = format(s,
698              "[%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",
699              d - mm->domains,
700              format_ip4_address, &d->ip4_prefix, d->ip4_prefix_len,
701              format_ip6_address, &ip6_prefix, d->ip6_prefix_len,
702              format_ip6_address, &d->ip6_src, d->ip6_src_len,
703              d->ea_bits_len, d->psid_offset, d->psid_length, d->mtu,
704              (d->flags & MAP_DOMAIN_TRANSLATION) ? "map-t" : "");
705
706   if (counters) {
707     map_domain_counter_lock(mm);
708     vlib_counter_t v;
709     vlib_get_combined_counter(&mm->domain_counters[MAP_DOMAIN_COUNTER_TX], d - mm->domains, &v);
710     s = format(s, "  TX: %lld/%lld", v.packets, v.bytes);
711     vlib_get_combined_counter(&mm->domain_counters[MAP_DOMAIN_COUNTER_RX], d - mm->domains, &v);
712     s = format(s, "  RX: %lld/%lld", v.packets, v.bytes);
713     map_domain_counter_unlock(mm);
714   }
715   s = format(s, "\n");
716
717   if (d->rules) {
718     int i;
719     ip6_address_t dst;
720     for (i = 0; i < (0x1 << d->psid_length); i++) {
721       dst = d->rules[i];
722       if (dst.as_u64[0] == 0 && dst.as_u64[1] == 0 )
723         continue;
724       s = format(s,
725                  " rule psid: %d ip6-dst %U\n", i, format_ip6_address, &dst);
726     }
727   }
728   return s;
729 }
730
731 static u8 *
732 format_map_ip4_reass (u8 *s, va_list *args)
733 {
734   map_main_t *mm = &map_main;
735   map_ip4_reass_t *r = va_arg(*args, map_ip4_reass_t *);
736   map_ip4_reass_key_t *k = &r->key;
737   f64 now = vlib_time_now(mm->vlib_main);
738   f64 lifetime = (((f64)mm->ip4_reass_conf_lifetime_ms) / 1000);
739   f64 dt = (r->ts + lifetime > now) ? (r->ts + lifetime - now) : -1;
740   s = format(s,
741              "ip4-reass src=%U  dst=%U  protocol=%d  identifier=%d  port=%d  lifetime=%.3lf\n",
742              format_ip4_address, &k->src.as_u8, format_ip4_address, &k->dst.as_u8,
743              k->protocol, clib_net_to_host_u16(k->fragment_id), (r->port >= 0)?clib_net_to_host_u16(r->port):-1, dt);
744   return s;
745 }
746
747 static u8 *
748 format_map_ip6_reass (u8 *s, va_list *args)
749 {
750   map_main_t *mm = &map_main;
751   map_ip6_reass_t *r = va_arg(*args, map_ip6_reass_t *);
752   map_ip6_reass_key_t *k = &r->key;
753   f64 now = vlib_time_now(mm->vlib_main);
754   f64 lifetime = (((f64)mm->ip6_reass_conf_lifetime_ms) / 1000);
755   f64 dt = (r->ts + lifetime > now) ? (r->ts + lifetime - now) : -1;
756   s = format(s,
757              "ip6-reass src=%U  dst=%U  protocol=%d  identifier=%d  lifetime=%.3lf\n",
758              format_ip6_address, &k->src.as_u8, format_ip6_address, &k->dst.as_u8,
759              k->protocol, clib_net_to_host_u32(k->fragment_id), dt);
760   return s;
761 }
762
763 static clib_error_t *
764 show_map_domain_command_fn (vlib_main_t *vm, unformat_input_t *input, vlib_cli_command_t *cmd)
765 {
766   unformat_input_t _line_input, *line_input = &_line_input;
767   map_main_t *mm = &map_main;
768   map_domain_t *d;
769   bool counters = false;
770   u32 map_domain_index = ~0;
771
772   /* Get a line of input. */
773   if (!unformat_user(input, unformat_line_input, line_input))
774     return 0;
775  
776   while (unformat_check_input(line_input) != UNFORMAT_END_OF_INPUT) {
777     if (unformat(line_input, "counters"))
778       counters = true;
779     else if (unformat(line_input, "index %d", &map_domain_index))
780       ;
781     else
782       return clib_error_return(0, "unknown input `%U'",
783                                format_unformat_error, input);
784   }
785   unformat_free(line_input);
786
787   if (pool_elts(mm->domains) == 0)
788     vlib_cli_output(vm, "No MAP domains are configured...");
789
790   if (map_domain_index == ~0) {
791     pool_foreach(d, mm->domains, ({vlib_cli_output(vm, "%U", format_map_domain, d, counters);}));
792   } else {
793     if (pool_is_free_index(mm->domains, map_domain_index)) {
794       return clib_error_return(0, "MAP domain does not exists %d", map_domain_index);
795     }
796
797     d = pool_elt_at_index(mm->domains, map_domain_index);
798     vlib_cli_output(vm, "%U", format_map_domain, d, counters);
799   }
800
801   return 0;
802 }
803
804 static clib_error_t *
805 show_map_fragments_command_fn (vlib_main_t *vm, unformat_input_t *input, vlib_cli_command_t *cmd)
806 {
807   map_main_t *mm = &map_main;
808   map_ip4_reass_t *f4;
809   map_ip6_reass_t *f6;
810
811   pool_foreach(f4, mm->ip4_reass_pool, ({vlib_cli_output (vm, "%U", format_map_ip4_reass, f4);}));
812   pool_foreach(f6, mm->ip6_reass_pool, ({vlib_cli_output (vm, "%U", format_map_ip6_reass, f6);}));
813   return (0);
814 }
815
816 u64
817 map_error_counter_get (u32 node_index, map_error_t map_error)
818 {
819   vlib_main_t *vm = vlib_get_main();
820   vlib_node_runtime_t *error_node = vlib_node_get_runtime(vm, node_index);
821   vlib_error_main_t *em = &vm->error_main;
822   vlib_error_t e = error_node->errors[map_error];
823   vlib_node_t *n = vlib_get_node(vm, node_index);
824   u32 ci;
825
826   ci = vlib_error_get_code(e);
827   ASSERT (ci < n->n_errors);
828   ci += n->error_heap_index;
829
830   return (em->counters[ci]);
831 }
832
833 static clib_error_t *
834 show_map_stats_command_fn (vlib_main_t *vm, unformat_input_t *input, vlib_cli_command_t *cmd)
835 {
836   map_main_t *mm = &map_main;
837   map_domain_t *d;
838   int domains = 0, rules = 0, domaincount = 0, rulecount = 0;
839   if (pool_elts (mm->domains) == 0)
840     vlib_cli_output(vm, "No MAP domains are configured...");
841
842   pool_foreach(d, mm->domains, ({
843     if (d->rules) {
844       rulecount+= 0x1 << d->psid_length;
845       rules += sizeof(ip6_address_t) * 0x1 << d->psid_length;
846     }
847     domains += sizeof(*d);
848     domaincount++;
849   }));
850
851   vlib_cli_output(vm, "MAP domains structure: %d\n", sizeof (map_domain_t));
852   vlib_cli_output(vm, "MAP domains: %d (%d bytes)\n", domaincount, domains);
853   vlib_cli_output(vm, "MAP rules: %d (%d bytes)\n", rulecount, rules);
854   vlib_cli_output(vm, "Total: %d bytes)\n", rules + domains);
855
856 #if MAP_SKIP_IP6_LOOKUP
857   vlib_cli_output(vm, "MAP pre-resolve: IP6 next-hop: %U (%u), IP4 next-hop: %U (%u)\n",
858                   format_ip6_address, &mm->preresolve_ip6, mm->adj6_index,
859                   format_ip4_address, &mm->preresolve_ip4, mm->adj4_index);
860 #endif
861
862   if (mm->tc_copy)
863     vlib_cli_output(vm, "MAP traffic-class: copy");
864   else
865     vlib_cli_output(vm, "MAP traffic-class: %x", mm->tc);
866
867   vlib_cli_output(vm, "MAP IPv6 inbound security check: %s, fragmented packet security check: %s", mm->sec_check ? "enabled" : "disabled",
868                   mm->sec_check_frag ? "enabled" : "disabled");
869
870   vlib_cli_output(vm, "ICMP-relay IPv4 source address: %U\n", format_ip4_address, &mm->icmp4_src_address);
871   vlib_cli_output(vm, "ICMP6 unreachables sent for unmatched packets: %s\n", mm->icmp6_enabled ? "enabled" : "disabled");
872
873   /*
874    * Counters
875    */
876   vlib_combined_counter_main_t *cm = mm->domain_counters;
877   u64 total_pkts[MAP_N_DOMAIN_COUNTER];
878   u64 total_bytes[MAP_N_DOMAIN_COUNTER];
879   int which, i;
880   vlib_counter_t v;
881
882   memset (total_pkts, 0, sizeof (total_pkts));
883   memset (total_bytes, 0, sizeof (total_bytes));
884
885   map_domain_counter_lock (mm);
886   vec_foreach (cm, mm->domain_counters) {
887     which = cm - mm->domain_counters;
888
889     for (i = 0; i < vec_len (cm->maxi); i++) {
890       vlib_get_combined_counter (cm, i, &v);
891       total_pkts[which] += v.packets;
892       total_bytes[which] += v.bytes;
893     }
894   }
895   map_domain_counter_unlock (mm);
896
897   vlib_cli_output(vm, "Encapsulated packets: %lld bytes: %lld\n", total_pkts[MAP_DOMAIN_COUNTER_TX],
898                   total_bytes[MAP_DOMAIN_COUNTER_TX]);
899   vlib_cli_output(vm, "Decapsulated packets: %lld bytes: %lld\n", total_pkts[MAP_DOMAIN_COUNTER_RX],
900                   total_bytes[MAP_DOMAIN_COUNTER_RX]);
901
902   vlib_cli_output(vm, "ICMP relayed packets: %d\n", vlib_get_simple_counter(&mm->icmp_relayed, 0));
903
904   return 0;
905 }
906
907 static clib_error_t *
908 map_params_reass_command_fn (vlib_main_t *vm, unformat_input_t *input, vlib_cli_command_t *cmd)
909 {
910   unformat_input_t _line_input, *line_input = &_line_input;
911   u32 lifetime = ~0;
912   f64 ht_ratio = (MAP_IP4_REASS_CONF_HT_RATIO_MAX+1);
913   u32 pool_size = ~0;
914   u64 buffers = ~(0ull);
915   u8 ip4 = 0, ip6 = 0;
916
917   if (!unformat_user(input, unformat_line_input, line_input))
918       return 0;
919
920   while (unformat_check_input(line_input) != UNFORMAT_END_OF_INPUT) {
921     if (!unformat(line_input, "lifetime %u", &lifetime) &&
922         !unformat(line_input, "ht-ratio %lf", &ht_ratio) &&
923         !unformat(line_input, "pool-size %u", &pool_size) &&
924         !unformat(line_input, "buffers %llu", &buffers) &&
925         !((unformat(line_input, "ip4")) && (ip4 = 1)) &&
926         !((unformat(line_input, "ip6")) && (ip6 = 1))) {
927       unformat_free(line_input);
928       return clib_error_return(0, "invalid input");
929     }
930   }
931   unformat_free(line_input);
932
933   if (!ip4 && !ip6)
934     return clib_error_return(0, "must specify ip4 and/or ip6");
935
936   if (ip4) {
937     if (pool_size != ~0 && pool_size > MAP_IP4_REASS_CONF_POOL_SIZE_MAX)
938       return clib_error_return(0, "invalid ip4-reass pool-size ( > %d)", MAP_IP4_REASS_CONF_POOL_SIZE_MAX);
939     if (ht_ratio != (MAP_IP4_REASS_CONF_HT_RATIO_MAX+1) && ht_ratio > MAP_IP4_REASS_CONF_HT_RATIO_MAX)
940       return clib_error_return(0, "invalid ip4-reass ht-ratio ( > %d)", MAP_IP4_REASS_CONF_HT_RATIO_MAX);
941     if (lifetime != ~0 && lifetime > MAP_IP4_REASS_CONF_LIFETIME_MAX)
942       return clib_error_return(0, "invalid ip4-reass lifetime ( > %d)", MAP_IP4_REASS_CONF_LIFETIME_MAX);
943     if (buffers != ~(0ull) && buffers > MAP_IP4_REASS_CONF_BUFFERS_MAX)
944       return clib_error_return(0, "invalid ip4-reass buffers ( > %ld)", MAP_IP4_REASS_CONF_BUFFERS_MAX);
945   }
946
947   if (ip6) {
948     if (pool_size != ~0 && pool_size > MAP_IP6_REASS_CONF_POOL_SIZE_MAX)
949       return clib_error_return(0, "invalid ip6-reass pool-size ( > %d)", MAP_IP6_REASS_CONF_POOL_SIZE_MAX);
950     if (ht_ratio != (MAP_IP4_REASS_CONF_HT_RATIO_MAX+1) && ht_ratio > MAP_IP6_REASS_CONF_HT_RATIO_MAX)
951       return clib_error_return(0, "invalid ip6-reass ht-log2len ( > %d)", MAP_IP6_REASS_CONF_HT_RATIO_MAX);
952     if (lifetime != ~0 && lifetime > MAP_IP6_REASS_CONF_LIFETIME_MAX)
953       return clib_error_return(0, "invalid ip6-reass lifetime ( > %d)", MAP_IP6_REASS_CONF_LIFETIME_MAX);
954     if (buffers != ~(0ull) && buffers > MAP_IP6_REASS_CONF_BUFFERS_MAX)
955       return clib_error_return(0, "invalid ip6-reass buffers ( > %ld)", MAP_IP6_REASS_CONF_BUFFERS_MAX);
956   }
957
958   if (ip4) {
959     u32 reass = 0, packets = 0;
960     if (pool_size != ~0) {
961       if (map_ip4_reass_conf_pool_size(pool_size, &reass, &packets)) {
962         vlib_cli_output(vm, "Could not set ip4-reass pool-size");
963       } else {
964         vlib_cli_output(vm, "Setting ip4-reass pool-size (destroyed-reassembly=%u , dropped-fragments=%u)", reass, packets);
965       }
966     }
967     if (ht_ratio != (MAP_IP4_REASS_CONF_HT_RATIO_MAX+1)) {
968       if (map_ip4_reass_conf_ht_ratio(ht_ratio, &reass, &packets)) {
969         vlib_cli_output(vm, "Could not set ip4-reass ht-log2len");
970       } else {
971         vlib_cli_output(vm, "Setting ip4-reass ht-log2len (destroyed-reassembly=%u , dropped-fragments=%u)", reass, packets);
972       }
973     }
974     if (lifetime != ~0) {
975       if (map_ip4_reass_conf_lifetime(lifetime))
976         vlib_cli_output(vm, "Could not set ip4-reass lifetime");
977       else
978         vlib_cli_output(vm, "Setting ip4-reass lifetime");
979     }
980     if (buffers != ~(0ull)) {
981       if (map_ip4_reass_conf_buffers(buffers))
982         vlib_cli_output(vm, "Could not set ip4-reass buffers");
983       else
984         vlib_cli_output(vm, "Setting ip4-reass buffers");
985     }
986
987     if (map_main.ip4_reass_conf_buffers >
988       map_main.ip4_reass_conf_pool_size * MAP_IP4_REASS_MAX_FRAGMENTS_PER_REASSEMBLY) {
989       vlib_cli_output(vm, "Note: 'ip4-reass buffers' > pool-size * max-fragments-per-reassembly.");
990     }
991   }
992
993   if (ip6) {
994     u32 reass = 0, packets = 0;
995     if (pool_size != ~0) {
996       if (map_ip6_reass_conf_pool_size(pool_size, &reass, &packets)) {
997         vlib_cli_output(vm, "Could not set ip6-reass pool-size");
998       } else {
999         vlib_cli_output(vm, "Setting ip6-reass pool-size (destroyed-reassembly=%u , dropped-fragments=%u)", reass, packets);
1000       }
1001     }
1002     if (ht_ratio != (MAP_IP4_REASS_CONF_HT_RATIO_MAX+1)) {
1003       if (map_ip6_reass_conf_ht_ratio(ht_ratio, &reass, &packets)) {
1004         vlib_cli_output(vm, "Could not set ip6-reass ht-log2len");
1005       } else {
1006         vlib_cli_output(vm, "Setting ip6-reass ht-log2len (destroyed-reassembly=%u , dropped-fragments=%u)", reass, packets);
1007       }
1008     }
1009     if (lifetime != ~0) {
1010       if (map_ip6_reass_conf_lifetime(lifetime))
1011         vlib_cli_output(vm, "Could not set ip6-reass lifetime");
1012       else
1013         vlib_cli_output(vm, "Setting ip6-reass lifetime");
1014     }
1015     if (buffers != ~(0ull)) {
1016       if (map_ip6_reass_conf_buffers(buffers))
1017         vlib_cli_output(vm, "Could not set ip6-reass buffers");
1018       else
1019         vlib_cli_output(vm, "Setting ip6-reass buffers");
1020     }
1021
1022     if (map_main.ip6_reass_conf_buffers >
1023         map_main.ip6_reass_conf_pool_size * MAP_IP6_REASS_MAX_FRAGMENTS_PER_REASSEMBLY) {
1024       vlib_cli_output(vm, "Note: 'ip6-reass buffers' > pool-size * max-fragments-per-reassembly.");
1025     }
1026   }
1027
1028   return 0;
1029 }
1030
1031
1032 /*
1033  * packet trace format function
1034  */
1035 u8 *
1036 format_map_trace (u8 *s, va_list *args)
1037 {
1038   CLIB_UNUSED(vlib_main_t *vm) = va_arg (*args, vlib_main_t *);
1039   CLIB_UNUSED(vlib_node_t *node) = va_arg (*args, vlib_node_t *);
1040   map_trace_t *t = va_arg (*args, map_trace_t *);
1041   u32 map_domain_index = t->map_domain_index;
1042   u16 port = t->port;
1043
1044   s = format(s, "MAP domain index: %d L4 port: %u", map_domain_index, clib_net_to_host_u16(port));
1045
1046   return s;
1047 }
1048
1049 static_always_inline map_ip4_reass_t *
1050 map_ip4_reass_lookup(map_ip4_reass_key_t *k, u32 bucket, f64 now)
1051 {
1052   map_main_t *mm = &map_main;
1053   u32 ri = mm->ip4_reass_hash_table[bucket];
1054   while(ri != MAP_REASS_INDEX_NONE) {
1055     map_ip4_reass_t * r = pool_elt_at_index(mm->ip4_reass_pool, ri);
1056     if (r->key.as_u64[0] == k->as_u64[0] &&
1057         r->key.as_u64[1] == k->as_u64[1] &&
1058         now < r->ts + (((f64)mm->ip4_reass_conf_lifetime_ms) / 1000)) {
1059       return r;
1060     }
1061     ri = r->bucket_next;
1062   }
1063   return NULL;
1064 }
1065
1066 #define map_ip4_reass_pool_index(r) (r - map_main.ip4_reass_pool)
1067
1068 void
1069 map_ip4_reass_free(map_ip4_reass_t *r, u32 **pi_to_drop)
1070 {
1071   map_main_t *mm = &map_main;
1072   map_ip4_reass_get_fragments(r, pi_to_drop);
1073
1074   // Unlink in hash bucket
1075   map_ip4_reass_t *r2 = NULL;
1076   u32 r2i = mm->ip4_reass_hash_table[r->bucket];
1077   while (r2i != map_ip4_reass_pool_index(r)) {
1078     ASSERT(r2i != MAP_REASS_INDEX_NONE);
1079     r2 = pool_elt_at_index(mm->ip4_reass_pool, r2i);
1080     r2i = r2->bucket_next;
1081   }
1082   if (r2) {
1083     r2->bucket_next = r->bucket_next;
1084   } else {
1085     mm->ip4_reass_hash_table[r->bucket] = r->bucket_next;
1086   }
1087
1088   // Unlink in list
1089   if (r->fifo_next == map_ip4_reass_pool_index(r)) {
1090     mm->ip4_reass_fifo_last = MAP_REASS_INDEX_NONE;
1091   } else {
1092     if(mm->ip4_reass_fifo_last == map_ip4_reass_pool_index(r))
1093       mm->ip4_reass_fifo_last = r->fifo_prev;
1094     pool_elt_at_index(mm->ip4_reass_pool, r->fifo_prev)->fifo_next = r->fifo_next;
1095     pool_elt_at_index(mm->ip4_reass_pool, r->fifo_next)->fifo_prev = r->fifo_prev;
1096   }
1097
1098   pool_put(mm->ip4_reass_pool, r);
1099   mm->ip4_reass_allocated--;
1100 }
1101
1102 map_ip4_reass_t *
1103 map_ip4_reass_get(u32 src, u32 dst, u16 fragment_id,
1104                   u8 protocol, u32 **pi_to_drop)
1105 {
1106   map_ip4_reass_t * r;
1107   map_main_t *mm = &map_main;
1108   map_ip4_reass_key_t k = {.src.data_u32 = src,
1109       .dst.data_u32 = dst,
1110       .fragment_id = fragment_id,
1111       .protocol = protocol };
1112
1113   u32 h = 0;
1114   h = crc_u32(k.as_u32[0], h);
1115   h = crc_u32(k.as_u32[1], h);
1116   h = crc_u32(k.as_u32[2], h);
1117   h = crc_u32(k.as_u32[3], h);
1118   h = h >> (32 - mm->ip4_reass_ht_log2len);
1119
1120   f64 now = vlib_time_now(mm->vlib_main);
1121
1122   //Cache garbage collection
1123   while (mm->ip4_reass_fifo_last != MAP_REASS_INDEX_NONE) {
1124     map_ip4_reass_t *last = pool_elt_at_index(mm->ip4_reass_pool, mm->ip4_reass_fifo_last);
1125     if (last->ts + (((f64)mm->ip4_reass_conf_lifetime_ms) / 1000) < now)
1126       map_ip4_reass_free(last, pi_to_drop);
1127     else
1128       break;
1129   }
1130
1131   if ((r = map_ip4_reass_lookup(&k, h, now)))
1132     return r;
1133
1134   if (mm->ip4_reass_allocated >= mm->ip4_reass_conf_pool_size)
1135     return NULL;
1136
1137   pool_get(mm->ip4_reass_pool, r);
1138   mm->ip4_reass_allocated++;
1139   int i;
1140   for (i=0; i<MAP_IP4_REASS_MAX_FRAGMENTS_PER_REASSEMBLY; i++)
1141     r->fragments[i] = ~0;
1142
1143   u32 ri = map_ip4_reass_pool_index(r);
1144
1145   //Link in new bucket
1146   r->bucket = h;
1147   r->bucket_next = mm->ip4_reass_hash_table[h];
1148   mm->ip4_reass_hash_table[h] = ri;
1149
1150   //Link in fifo
1151   if(mm->ip4_reass_fifo_last != MAP_REASS_INDEX_NONE) {
1152     r->fifo_next = pool_elt_at_index(mm->ip4_reass_pool, mm->ip4_reass_fifo_last)->fifo_next;
1153     r->fifo_prev = mm->ip4_reass_fifo_last;
1154     pool_elt_at_index(mm->ip4_reass_pool, r->fifo_prev)->fifo_next = ri;
1155     pool_elt_at_index(mm->ip4_reass_pool, r->fifo_next)->fifo_prev = ri;
1156   } else {
1157     r->fifo_next = r->fifo_prev = ri;
1158     mm->ip4_reass_fifo_last = ri;
1159   }
1160
1161   //Set other fields
1162   r->ts = now;
1163   r->key = k;
1164   r->port = -1;
1165 #ifdef MAP_IP4_REASS_COUNT_BYTES
1166   r->expected_total = 0xffff;
1167   r->forwarded = 0;
1168 #endif
1169
1170   return r;
1171 }
1172
1173 int
1174 map_ip4_reass_add_fragment(map_ip4_reass_t *r, u32 pi)
1175 {
1176   if (map_main.ip4_reass_buffered_counter >= map_main.ip4_reass_conf_buffers)
1177     return -1;
1178
1179   int i;
1180   for (i=0; i<MAP_IP4_REASS_MAX_FRAGMENTS_PER_REASSEMBLY; i++)
1181     if(r->fragments[i] == ~0) {
1182       r->fragments[i] = pi;
1183       map_main.ip4_reass_buffered_counter++;
1184       return 0;
1185     }
1186   return -1;
1187 }
1188
1189 static_always_inline map_ip6_reass_t *
1190 map_ip6_reass_lookup(map_ip6_reass_key_t *k, u32 bucket, f64 now)
1191 {
1192   map_main_t *mm = &map_main;
1193   u32 ri = mm->ip6_reass_hash_table[bucket];
1194   while(ri != MAP_REASS_INDEX_NONE) {
1195     map_ip6_reass_t * r = pool_elt_at_index(mm->ip6_reass_pool, ri);
1196     if(now < r->ts + (((f64)mm->ip6_reass_conf_lifetime_ms) / 1000) &&
1197         r->key.as_u64[0] == k->as_u64[0] &&
1198         r->key.as_u64[1] == k->as_u64[1] &&
1199         r->key.as_u64[2] == k->as_u64[2] &&
1200         r->key.as_u64[3] == k->as_u64[3] &&
1201         r->key.as_u64[4] == k->as_u64[4])
1202       return r;
1203     ri = r->bucket_next;
1204   }
1205   return NULL;
1206 }
1207
1208 #define map_ip6_reass_pool_index(r) (r - map_main.ip6_reass_pool)
1209
1210 void
1211 map_ip6_reass_free(map_ip6_reass_t *r, u32 **pi_to_drop)
1212 {
1213   map_main_t *mm = &map_main;
1214   int i;
1215   for (i=0; i<MAP_IP6_REASS_MAX_FRAGMENTS_PER_REASSEMBLY; i++)
1216     if(r->fragments[i].pi != ~0) {
1217       vec_add1(*pi_to_drop, r->fragments[i].pi);
1218       r->fragments[i].pi = ~0;
1219       map_main.ip6_reass_buffered_counter--;
1220     }
1221
1222   // Unlink in hash bucket
1223   map_ip6_reass_t *r2 = NULL;
1224   u32 r2i = mm->ip6_reass_hash_table[r->bucket];
1225   while (r2i != map_ip6_reass_pool_index(r)) {
1226     ASSERT(r2i != MAP_REASS_INDEX_NONE);
1227     r2 = pool_elt_at_index(mm->ip6_reass_pool, r2i);
1228     r2i = r2->bucket_next;
1229   }
1230   if (r2) {
1231     r2->bucket_next = r->bucket_next;
1232   } else {
1233     mm->ip6_reass_hash_table[r->bucket] = r->bucket_next;
1234   }
1235
1236   // Unlink in list
1237   if (r->fifo_next == map_ip6_reass_pool_index(r)) {
1238     //Single element in the list, list is now empty
1239     mm->ip6_reass_fifo_last = MAP_REASS_INDEX_NONE;
1240   } else {
1241     if (mm->ip6_reass_fifo_last == map_ip6_reass_pool_index(r)) //First element
1242       mm->ip6_reass_fifo_last = r->fifo_prev;
1243     pool_elt_at_index(mm->ip6_reass_pool, r->fifo_prev)->fifo_next = r->fifo_next;
1244     pool_elt_at_index(mm->ip6_reass_pool, r->fifo_next)->fifo_prev = r->fifo_prev;
1245   }
1246
1247   // Free from pool if necessary
1248   pool_put(mm->ip6_reass_pool, r);
1249   mm->ip6_reass_allocated--;
1250 }
1251
1252 map_ip6_reass_t *
1253 map_ip6_reass_get(ip6_address_t *src, ip6_address_t *dst, u32 fragment_id,
1254                   u8 protocol, u32 **pi_to_drop)
1255 {
1256   map_ip6_reass_t * r;
1257   map_main_t *mm = &map_main;
1258   map_ip6_reass_key_t k = {
1259       .src = *src,
1260       .dst = *dst,
1261       .fragment_id = fragment_id,
1262       .protocol = protocol };
1263
1264   u32 h = 0;
1265   int i;
1266   for (i=0; i<10; i++)
1267     h = crc_u32(k.as_u32[i], h);
1268   h = h >> (32 - mm->ip6_reass_ht_log2len);
1269
1270   f64 now = vlib_time_now(mm->vlib_main);
1271
1272   //Cache garbage collection
1273   while (mm->ip6_reass_fifo_last != MAP_REASS_INDEX_NONE) {
1274     map_ip6_reass_t *last = pool_elt_at_index(mm->ip6_reass_pool, mm->ip6_reass_fifo_last);
1275     if (last->ts + (((f64)mm->ip6_reass_conf_lifetime_ms) / 1000) < now)
1276       map_ip6_reass_free(last, pi_to_drop);
1277     else
1278       break;
1279   }
1280
1281   if ((r = map_ip6_reass_lookup(&k, h, now)))
1282     return r;
1283
1284   if (mm->ip6_reass_allocated >= mm->ip6_reass_conf_pool_size)
1285     return NULL;
1286
1287   pool_get(mm->ip6_reass_pool, r);
1288   mm->ip6_reass_allocated++;
1289   for (i=0; i<MAP_IP6_REASS_MAX_FRAGMENTS_PER_REASSEMBLY; i++) {
1290     r->fragments[i].pi = ~0;
1291     r->fragments[i].next_data_len = 0;
1292     r->fragments[i].next_data_offset = 0;
1293   }
1294
1295   u32 ri = map_ip6_reass_pool_index(r);
1296
1297   //Link in new bucket
1298   r->bucket = h;
1299   r->bucket_next = mm->ip6_reass_hash_table[h];
1300   mm->ip6_reass_hash_table[h] = ri;
1301
1302   //Link in fifo
1303   if(mm->ip6_reass_fifo_last != MAP_REASS_INDEX_NONE) {
1304     r->fifo_next = pool_elt_at_index(mm->ip6_reass_pool, mm->ip6_reass_fifo_last)->fifo_next;
1305     r->fifo_prev = mm->ip6_reass_fifo_last;
1306     pool_elt_at_index(mm->ip6_reass_pool, r->fifo_prev)->fifo_next = ri;
1307     pool_elt_at_index(mm->ip6_reass_pool, r->fifo_next)->fifo_prev = ri;
1308   } else {
1309     r->fifo_next = r->fifo_prev = ri;
1310     mm->ip6_reass_fifo_last = ri;
1311   }
1312
1313   //Set other fields
1314   r->ts = now;
1315   r->key = k;
1316   r->ip4_header.ip_version_and_header_length = 0;
1317 #ifdef MAP_IP6_REASS_COUNT_BYTES
1318   r->expected_total = 0xffff;
1319   r->forwarded = 0;
1320 #endif
1321   return r;
1322 }
1323
1324 int
1325 map_ip6_reass_add_fragment(map_ip6_reass_t *r, u32 pi,
1326                            u16 data_offset, u16 next_data_offset,
1327                            u8 *data_start, u16 data_len)
1328 {
1329   map_ip6_fragment_t *f = NULL, *prev_f = NULL;
1330   u16 copied_len = (data_len > 20) ? 20 : data_len;
1331
1332   if (map_main.ip6_reass_buffered_counter >= map_main.ip6_reass_conf_buffers)
1333     return -1;
1334
1335   //Lookup for fragments for the current buffer
1336   //and the one before that
1337   int i;
1338   for (i=0; i<MAP_IP6_REASS_MAX_FRAGMENTS_PER_REASSEMBLY; i++) {
1339     if (data_offset && r->fragments[i].next_data_offset == data_offset) {
1340       prev_f = &r->fragments[i]; // This is buffer for previous packet
1341     } else if (r->fragments[i].next_data_offset == next_data_offset) {
1342       f = &r->fragments[i]; // This is a buffer for the current packet
1343     } else if (r->fragments[i].next_data_offset == 0) { //Available
1344       if (f == NULL)
1345         f = &r->fragments[i];
1346       else if (prev_f == NULL)
1347         prev_f = &r->fragments[i];
1348     }
1349   }
1350
1351   if (!f || f->pi != ~0)
1352     return -1;
1353
1354   if (data_offset) {
1355     if (!prev_f)
1356       return -1;
1357
1358     memcpy(prev_f->next_data, data_start, copied_len);
1359     prev_f->next_data_len = copied_len;
1360     prev_f->next_data_offset = data_offset;
1361   } else {
1362     if (((ip4_header_t *)data_start)->ip_version_and_header_length != 0x45)
1363       return -1;
1364
1365     if (r->ip4_header.ip_version_and_header_length == 0)
1366       memcpy(&r->ip4_header, data_start, sizeof(ip4_header_t));
1367   }
1368
1369   if(data_len > 20) {
1370     f->next_data_offset = next_data_offset;
1371     f->pi = pi;
1372     map_main.ip6_reass_buffered_counter++;
1373   }
1374   return 0;
1375 }
1376
1377 void map_ip4_reass_reinit(u32 *trashed_reass, u32 *dropped_packets)
1378 {
1379   map_main_t *mm = &map_main;
1380   int i;
1381
1382   if(dropped_packets)
1383     *dropped_packets = mm->ip4_reass_buffered_counter;
1384   if(trashed_reass)
1385     *trashed_reass = mm->ip4_reass_allocated;
1386   if (mm->ip4_reass_fifo_last != MAP_REASS_INDEX_NONE) {
1387     u16 ri = mm->ip4_reass_fifo_last;
1388     do {
1389       map_ip4_reass_t *r = pool_elt_at_index(mm->ip4_reass_pool, ri);
1390       for (i=0; i<MAP_IP4_REASS_MAX_FRAGMENTS_PER_REASSEMBLY; i++)
1391         if (r->fragments[i] != ~0)
1392           map_ip4_drop_pi(r->fragments[i]);
1393
1394       ri = r->fifo_next;
1395       pool_put(mm->ip4_reass_pool, r);
1396     } while (ri != mm->ip4_reass_fifo_last);
1397   }
1398
1399   vec_free(mm->ip4_reass_hash_table);
1400   vec_resize(mm->ip4_reass_hash_table, 1 << mm->ip4_reass_ht_log2len);
1401   for (i=0; i<(1 << mm->ip4_reass_ht_log2len); i++)
1402     mm->ip4_reass_hash_table[i] = MAP_REASS_INDEX_NONE;
1403   pool_free(mm->ip4_reass_pool);
1404   pool_alloc(mm->ip4_reass_pool, mm->ip4_reass_conf_pool_size);
1405
1406   mm->ip4_reass_allocated = 0;
1407   mm->ip4_reass_fifo_last = MAP_REASS_INDEX_NONE;
1408   mm->ip4_reass_buffered_counter = 0;
1409 }
1410
1411 u8 map_get_ht_log2len(f32 ht_ratio, u16 pool_size)
1412 {
1413   u32 desired_size = (u32)(pool_size * ht_ratio);
1414   u8 i;
1415   for (i=1; i<31; i++)
1416     if ((1 << i) >= desired_size)
1417       return i;
1418   return 4;
1419 }
1420
1421 int map_ip4_reass_conf_ht_ratio(f32 ht_ratio, u32 *trashed_reass, u32 *dropped_packets)
1422 {
1423   map_main_t *mm = &map_main;
1424   if (ht_ratio > MAP_IP4_REASS_CONF_HT_RATIO_MAX)
1425     return -1;
1426
1427   map_ip4_reass_lock();
1428   mm->ip4_reass_conf_ht_ratio = ht_ratio;
1429   mm->ip4_reass_ht_log2len = map_get_ht_log2len(ht_ratio, mm->ip4_reass_conf_pool_size);
1430   map_ip4_reass_reinit(trashed_reass, dropped_packets);
1431   map_ip4_reass_unlock();
1432   return 0;
1433 }
1434
1435 int map_ip4_reass_conf_pool_size(u16 pool_size, u32 *trashed_reass, u32 *dropped_packets)
1436 {
1437   map_main_t *mm = &map_main;
1438   if (pool_size > MAP_IP4_REASS_CONF_POOL_SIZE_MAX)
1439     return -1;
1440
1441   map_ip4_reass_lock();
1442   mm->ip4_reass_conf_pool_size = pool_size;
1443   map_ip4_reass_reinit(trashed_reass, dropped_packets);
1444   map_ip4_reass_unlock();
1445   return 0;
1446 }
1447
1448 int map_ip4_reass_conf_lifetime(u16 lifetime_ms)
1449 {
1450   map_main.ip4_reass_conf_lifetime_ms = lifetime_ms;
1451   return 0;
1452 }
1453
1454 int map_ip4_reass_conf_buffers(u32 buffers)
1455 {
1456   map_main.ip4_reass_conf_buffers = buffers;
1457   return 0;
1458 }
1459
1460 void map_ip6_reass_reinit(u32 *trashed_reass, u32 *dropped_packets)
1461 {
1462   map_main_t *mm = &map_main;
1463   if(dropped_packets)
1464     *dropped_packets = mm->ip6_reass_buffered_counter;
1465   if(trashed_reass)
1466     *trashed_reass = mm->ip6_reass_allocated;
1467   int i;
1468   if (mm->ip6_reass_fifo_last != MAP_REASS_INDEX_NONE) {
1469     u16 ri = mm->ip6_reass_fifo_last;
1470     do {
1471       map_ip6_reass_t *r = pool_elt_at_index(mm->ip6_reass_pool, ri);
1472       for (i=0; i<MAP_IP6_REASS_MAX_FRAGMENTS_PER_REASSEMBLY; i++)
1473         if (r->fragments[i].pi != ~0)
1474           map_ip6_drop_pi(r->fragments[i].pi);
1475
1476       ri = r->fifo_next;
1477       pool_put(mm->ip6_reass_pool, r);
1478     } while (ri != mm->ip6_reass_fifo_last);
1479     mm->ip6_reass_fifo_last = MAP_REASS_INDEX_NONE;
1480   }
1481
1482   vec_free(mm->ip6_reass_hash_table);
1483   vec_resize(mm->ip6_reass_hash_table, 1 << mm->ip6_reass_ht_log2len);
1484   for(i=0; i<(1 << mm->ip6_reass_ht_log2len); i++)
1485     mm->ip6_reass_hash_table[i] = MAP_REASS_INDEX_NONE;
1486   pool_free(mm->ip6_reass_pool);
1487   pool_alloc(mm->ip6_reass_pool, mm->ip4_reass_conf_pool_size);
1488
1489   mm->ip6_reass_allocated = 0;
1490   mm->ip6_reass_buffered_counter = 0;
1491 }
1492
1493 int map_ip6_reass_conf_ht_ratio(f32 ht_ratio, u32 *trashed_reass, u32 *dropped_packets)
1494 {
1495   map_main_t *mm = &map_main;
1496   if (ht_ratio > MAP_IP6_REASS_CONF_HT_RATIO_MAX)
1497     return -1;
1498
1499   map_ip6_reass_lock();
1500   mm->ip6_reass_conf_ht_ratio = ht_ratio;
1501   mm->ip6_reass_ht_log2len = map_get_ht_log2len(ht_ratio, mm->ip6_reass_conf_pool_size);
1502   map_ip6_reass_reinit(trashed_reass, dropped_packets);
1503   map_ip6_reass_unlock();
1504   return 0;
1505 }
1506
1507 int map_ip6_reass_conf_pool_size(u16 pool_size, u32 *trashed_reass, u32 *dropped_packets)
1508 {
1509   map_main_t *mm = &map_main;
1510   if (pool_size > MAP_IP6_REASS_CONF_POOL_SIZE_MAX)
1511     return -1;
1512
1513   map_ip6_reass_lock();
1514   mm->ip6_reass_conf_pool_size = pool_size;
1515   map_ip6_reass_reinit(trashed_reass, dropped_packets);
1516   map_ip6_reass_unlock();
1517   return 0;
1518 }
1519
1520 int map_ip6_reass_conf_lifetime(u16 lifetime_ms)
1521 {
1522   map_main.ip6_reass_conf_lifetime_ms = lifetime_ms;
1523   return 0;
1524 }
1525
1526 int map_ip6_reass_conf_buffers(u32 buffers)
1527 {
1528   map_main.ip6_reass_conf_buffers = buffers;
1529   return 0;
1530 }
1531
1532 VLIB_CLI_COMMAND(map_ip4_reass_lifetime_command, static) = {
1533   .path = "map params reassembly",
1534   .short_help = "[ip4 | ip6] [lifetime <lifetime-ms>] [pool-size <pool-size>] [buffers <buffers>] [ht-ratio <ht-ratio>]",
1535   .function = map_params_reass_command_fn,
1536 };
1537
1538 VLIB_CLI_COMMAND(map_traffic_class_command, static) = {
1539   .path = "map params traffic-class",
1540   .short_help = 
1541   "traffic-class {0x0-0xff | copy}",
1542   .function = map_traffic_class_command_fn,
1543 };
1544
1545 VLIB_CLI_COMMAND(map_pre_resolve_command, static) = {
1546   .path = "map params pre-resolve",
1547   .short_help = 
1548   "pre-resolve {ip4-nh <address>} | {ip6-nh <address>}",
1549   .function = map_pre_resolve_command_fn,
1550 };
1551
1552 VLIB_CLI_COMMAND(map_security_check_command, static) = {
1553   .path = "map params security-check",
1554   .short_help = 
1555   "security-check on|off",
1556   .function = map_security_check_command_fn,
1557 };
1558
1559 VLIB_CLI_COMMAND(map_icmp_relay_source_address_command, static) = {
1560   .path = "map params icmp source-address",
1561    .short_help = "source-address <ip4-address>",
1562   .function = map_icmp_relay_source_address_command_fn,
1563 };
1564
1565 VLIB_CLI_COMMAND(map_icmp_unreachables_command, static) = {
1566   .path = "map params icmp unreachables",
1567   .short_help = "unreachables {on|off}",
1568   .function = map_icmp_unreachables_command_fn,
1569 };
1570
1571 VLIB_CLI_COMMAND(map_security_check_frag_command, static) = {
1572   .path = "map params security-check fragments",
1573   .short_help = 
1574   "fragments on|off",
1575   .function = map_security_check_frag_command_fn,
1576 };
1577
1578 VLIB_CLI_COMMAND(map_add_domain_command, static) = {
1579   .path = "map add domain",
1580   .short_help = 
1581   "map add domain ip4-pfx <ip4-pfx> ip6-pfx <ip6-pfx> ip6-src <ip6-pfx> "
1582       "ea-bits-len <n> psid-offset <n> psid-len <n> [map-t] [mtu <mtu>]",
1583   .function = map_add_domain_command_fn,
1584 };
1585
1586 VLIB_CLI_COMMAND(map_add_rule_command, static) = {
1587   .path = "map add rule",
1588   .short_help = 
1589   "map add rule index <domain> psid <psid> ip6-dst <ip6-addr>",
1590   .function = map_add_rule_command_fn,
1591 };
1592
1593 VLIB_CLI_COMMAND(map_del_command, static) = {
1594   .path = "map del domain",
1595   .short_help = 
1596   "map del domain index <domain>",
1597   .function = map_del_domain_command_fn,
1598 };
1599
1600 VLIB_CLI_COMMAND(show_map_domain_command, static) = {
1601   .path = "show map domain",
1602   .function = show_map_domain_command_fn,
1603 };
1604
1605 VLIB_CLI_COMMAND(show_map_stats_command, static) = {
1606   .path = "show map stats",
1607   .function = show_map_stats_command_fn,
1608 };
1609
1610 VLIB_CLI_COMMAND(show_map_fragments_command, static) = {
1611   .path = "show map fragments",
1612   .function = show_map_fragments_command_fn,
1613 };
1614
1615 /*
1616  * map_init
1617  */
1618 clib_error_t *map_init (vlib_main_t *vm)
1619 {
1620   map_main_t *mm = &map_main;
1621   mm->vnet_main = vnet_get_main();
1622   mm->vlib_main = vm;
1623
1624 #ifdef MAP_SKIP_IP6_LOOKUP  
1625   memset(&mm->preresolve_ip4, 0, sizeof(mm->preresolve_ip4));
1626   memset(&mm->preresolve_ip6, 0, sizeof(mm->preresolve_ip6));
1627   mm->adj4_index = 0;
1628   mm->adj6_index = 0;
1629 #endif
1630
1631   /* traffic class */
1632   mm->tc = 0;
1633   mm->tc_copy = true;
1634
1635   /* Inbound security check */
1636   mm->sec_check = true;
1637   mm->sec_check_frag = false;
1638
1639   /* ICMP6 Type 1, Code 5 for security check failure */
1640   mm->icmp6_enabled = false;
1641
1642   vec_validate(mm->domain_counters, MAP_N_DOMAIN_COUNTER - 1);
1643   mm->domain_counters[MAP_DOMAIN_COUNTER_RX].name = "rx";
1644   mm->domain_counters[MAP_DOMAIN_COUNTER_TX].name = "tx";
1645
1646   vlib_validate_simple_counter(&mm->icmp_relayed, 0);
1647   vlib_zero_simple_counter(&mm->icmp_relayed, 0);
1648
1649   /* IP4 virtual reassembly */
1650   mm->ip4_reass_hash_table = 0;
1651   mm->ip4_reass_pool = 0;
1652   mm->ip4_reass_lock = clib_mem_alloc_aligned(CLIB_CACHE_LINE_BYTES, CLIB_CACHE_LINE_BYTES);
1653   mm->ip4_reass_conf_ht_ratio = MAP_IP4_REASS_HT_RATIO_DEFAULT;
1654   mm->ip4_reass_conf_lifetime_ms = MAP_IP4_REASS_LIFETIME_DEFAULT;
1655   mm->ip4_reass_conf_pool_size = MAP_IP4_REASS_POOL_SIZE_DEFAULT;
1656   mm->ip4_reass_conf_buffers = MAP_IP4_REASS_BUFFERS_DEFAULT;
1657   mm->ip4_reass_ht_log2len = map_get_ht_log2len(mm->ip4_reass_conf_ht_ratio, mm->ip4_reass_conf_pool_size);
1658   mm->ip4_reass_fifo_last = MAP_REASS_INDEX_NONE;
1659   map_ip4_reass_reinit(NULL, NULL);
1660
1661   /* IP6 virtual reassembly */
1662   mm->ip6_reass_hash_table = 0;
1663   mm->ip6_reass_pool = 0;
1664   mm->ip6_reass_lock = clib_mem_alloc_aligned(CLIB_CACHE_LINE_BYTES, CLIB_CACHE_LINE_BYTES);
1665   mm->ip6_reass_conf_ht_ratio = MAP_IP6_REASS_HT_RATIO_DEFAULT;
1666   mm->ip6_reass_conf_lifetime_ms = MAP_IP6_REASS_LIFETIME_DEFAULT;
1667   mm->ip6_reass_conf_pool_size = MAP_IP6_REASS_POOL_SIZE_DEFAULT;
1668   mm->ip6_reass_conf_buffers = MAP_IP6_REASS_BUFFERS_DEFAULT;
1669   mm->ip6_reass_ht_log2len = map_get_ht_log2len(mm->ip6_reass_conf_ht_ratio, mm->ip6_reass_conf_pool_size);
1670   mm->ip6_reass_fifo_last = MAP_REASS_INDEX_NONE;
1671   map_ip6_reass_reinit(NULL, NULL);
1672
1673   return 0;
1674 }
1675
1676 VLIB_INIT_FUNCTION(map_init);