MAP: Decrement TTL of payload IPv4 packet.
[vpp.git] / vnet / vnet / map / ip4_map.c
1 /*
2  * Copyright (c) 2015 Cisco and/or its affiliates.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at:
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 /*
16  * Defines used for testing various optimisation schemes
17  */
18 #define MAP_ENCAP_DUAL 0
19
20 #include "map.h"
21 #include "../ip/ip_frag.h"
22
23 vlib_node_registration_t ip4_map_reass_node;
24
25 enum ip4_map_next_e {
26   IP4_MAP_NEXT_IP6_LOOKUP,
27 #ifdef MAP_SKIP_IP6_LOOKUP
28   IP4_MAP_NEXT_IP6_REWRITE,
29 #endif
30   IP4_MAP_NEXT_FRAGMENT,
31   IP4_MAP_NEXT_REASS,
32   IP4_MAP_NEXT_DROP,
33   IP4_MAP_N_NEXT,
34 };
35
36 enum ip4_map_reass_next_t {
37   IP4_MAP_REASS_NEXT_IP6_LOOKUP,
38   IP4_MAP_REASS_NEXT_IP4_FRAGMENT,
39   IP4_MAP_REASS_NEXT_DROP,
40   IP4_MAP_REASS_N_NEXT,
41 };
42
43 typedef struct {
44   u32 map_domain_index;
45   u16 port;
46   u8 cached;
47 } map_ip4_map_reass_trace_t;
48
49 u8 *
50 format_ip4_map_reass_trace (u8 *s, va_list *args)
51 {
52   CLIB_UNUSED(vlib_main_t *vm) = va_arg (*args, vlib_main_t *);
53   CLIB_UNUSED(vlib_node_t *node) = va_arg (*args, vlib_node_t *);
54   map_ip4_map_reass_trace_t *t = va_arg (*args, map_ip4_map_reass_trace_t *);
55   return format(s, "MAP domain index: %d L4 port: %u Status: %s", t->map_domain_index,
56                 t->port, t->cached?"cached":"forwarded");
57 }
58
59 /*
60  * ip4_map_get_port
61  */
62 u16
63 ip4_map_get_port (ip4_header_t *ip, map_dir_e dir)
64 {
65   /* Find port information */
66   if (PREDICT_TRUE((ip->protocol == IP_PROTOCOL_TCP) ||
67                    (ip->protocol == IP_PROTOCOL_UDP))) {
68     udp_header_t *udp = (void *)(ip + 1);
69     return (dir == MAP_SENDER ? udp->src_port : udp->dst_port);
70   } else if (ip->protocol == IP_PROTOCOL_ICMP) {
71     /*
72      * 1) ICMP Echo request or Echo reply
73      * 2) ICMP Error with inner packet being UDP or TCP
74      * 3) ICMP Error with inner packet being ICMP Echo request or Echo reply
75      */
76     icmp46_header_t *icmp = (void *)(ip + 1);
77     if (icmp->type == ICMP4_echo_request || icmp->type == ICMP4_echo_reply) {
78       return *((u16 *)(icmp + 1));
79     } else if (clib_net_to_host_u16(ip->length) >= 64) { // IP + ICMP + IP + L4 header
80       ip4_header_t *icmp_ip = (ip4_header_t *)(icmp + 2);
81       if (PREDICT_TRUE((icmp_ip->protocol == IP_PROTOCOL_TCP) ||
82                        (icmp_ip->protocol == IP_PROTOCOL_UDP))) {
83         udp_header_t *udp = (void *)(icmp_ip + 1);
84         return (dir == MAP_SENDER ? udp->dst_port : udp->src_port);
85       } else if (icmp_ip->protocol == IP_PROTOCOL_ICMP) {
86         icmp46_header_t *inner_icmp = (void *)(icmp_ip + 1);
87         if (inner_icmp->type == ICMP4_echo_request || inner_icmp->type == ICMP4_echo_reply)
88           return (*((u16 *)(inner_icmp + 1)));
89       }
90     }
91   }
92   return (0);
93 }
94
95 static_always_inline u16
96 ip4_map_port_and_security_check (map_domain_t *d, ip4_header_t *ip, u32 *next, u8 *error)
97 {
98   u16 port = 0;
99
100   if (d->psid_length > 0) {
101     if (!ip4_is_fragment(ip)) {
102       if (PREDICT_FALSE((ip->ip_version_and_header_length != 0x45) || clib_host_to_net_u16(ip->length) < 28)) {
103         return 0;
104       }
105       port = ip4_map_get_port(ip, MAP_RECEIVER);
106       if (port) {
107         /* Verify that port is not among the well-known ports */
108         if ((d->psid_offset > 0) && (clib_net_to_host_u16(port) < (0x1 << (16 - d->psid_offset)))) {
109           *error = MAP_ERROR_ENCAP_SEC_CHECK;
110         } else {
111           return (port);
112         }
113       } else {
114         *error = MAP_ERROR_BAD_PROTOCOL;
115       }
116     } else {
117       *next = IP4_MAP_NEXT_REASS;
118     }
119   }
120   return (0);
121 }
122
123 /*
124  * ip4_map_vtcfl
125  */
126 static_always_inline u32
127 ip4_map_vtcfl (ip4_header_t *ip4, vlib_buffer_t *p)
128 {
129   map_main_t *mm = &map_main;
130   u8 tc = mm->tc_copy ? ip4->tos : mm->tc;
131   u32 vtcfl = 0x6 << 28;
132   vtcfl |= tc << 20;
133   vtcfl |= vnet_buffer(p)->ip.flow_hash & 0x000fffff;
134
135   return (clib_host_to_net_u32(vtcfl));
136 }
137
138 static_always_inline bool
139 ip4_map_ip6_lookup_bypass (vlib_buffer_t *p0, ip4_header_t *ip)
140 {
141 #ifdef MAP_SKIP_IP6_LOOKUP
142   map_main_t *mm = &map_main;
143   u32 adj_index0 = mm->adj6_index;
144   if (adj_index0 > 0) {
145     ip_lookup_main_t *lm6 = &ip6_main.lookup_main;
146     ip_adjacency_t *adj = ip_get_adjacency(lm6, mm->adj6_index);
147     if (adj->n_adj > 1) {
148       u32 hash_c0 = ip4_compute_flow_hash(ip, IP_FLOW_HASH_DEFAULT);
149       adj_index0 += (hash_c0 & (adj->n_adj - 1));
150     }
151     vnet_buffer(p0)->ip.adj_index[VLIB_TX] = adj_index0;
152     return (true);
153   }
154 #endif
155   return (false);
156 }
157
158 /*
159  * ip4_map_ttl
160  */
161 static inline void
162 ip4_map_decrement_ttl (ip4_header_t *ip, u8 *error)
163 {
164   i32 ttl = ip->ttl;
165
166   /* Input node should have reject packets with ttl 0. */
167   ASSERT (ip->ttl > 0);
168
169   u32 checksum = ip->checksum + clib_host_to_net_u16(0x0100);
170   checksum += checksum >= 0xffff;
171   ip->checksum = checksum;
172   ttl -= 1;
173   ip->ttl = ttl;
174   *error = ttl <= 0 ? IP4_ERROR_TIME_EXPIRED : *error;
175
176   /* Verify checksum. */
177   ASSERT (ip->checksum == ip4_header_checksum(ip));
178 }
179
180 /*
181  * ip4_map
182  */
183 static uword
184 ip4_map (vlib_main_t *vm,
185          vlib_node_runtime_t *node,
186          vlib_frame_t *frame)
187 {
188   u32 n_left_from, *from, next_index, *to_next, n_left_to_next;
189   vlib_node_runtime_t *error_node = vlib_node_get_runtime(vm, ip4_map_node.index);
190   from = vlib_frame_vector_args(frame);
191   n_left_from = frame->n_vectors;
192   next_index = node->cached_next_index;
193   map_main_t *mm = &map_main;
194   vlib_combined_counter_main_t *cm = mm->domain_counters;
195   u32 cpu_index = os_get_cpu_number();
196
197   while (n_left_from > 0) {
198     vlib_get_next_frame(vm, node, next_index, to_next, n_left_to_next);
199
200     /* Dual loop */
201     while (n_left_from >= 4 && n_left_to_next >= 2) {
202       u32 pi0, pi1;
203       vlib_buffer_t *p0, *p1;
204       map_domain_t *d0, *d1;
205       u8 error0 = MAP_ERROR_NONE, error1 = MAP_ERROR_NONE;
206       ip4_header_t *ip40, *ip41;
207       u16 port0 = 0, port1 = 0;
208       ip6_header_t *ip6h0, *ip6h1;
209       u32 map_domain_index0 = ~0, map_domain_index1 = ~0;
210       u32 next0 = IP4_MAP_NEXT_IP6_LOOKUP, next1 = IP4_MAP_NEXT_IP6_LOOKUP;
211
212       /* Prefetch next iteration. */
213       {
214         vlib_buffer_t *p2, *p3;
215
216         p2 = vlib_get_buffer(vm, from[2]);
217         p3 = vlib_get_buffer(vm, from[3]);
218
219         vlib_prefetch_buffer_header(p2, STORE);
220         vlib_prefetch_buffer_header(p3, STORE);
221         /* IPv4 + 8 = 28. possibly plus -40 */
222         CLIB_PREFETCH (p2->data-40, 68, STORE);
223         CLIB_PREFETCH (p3->data-40, 68, STORE);
224       }
225
226       pi0 = to_next[0] = from[0];
227       pi1 = to_next[1] = from[1];
228       from += 2;
229       n_left_from -= 2;
230       to_next +=2;
231       n_left_to_next -= 2;
232
233       p0 = vlib_get_buffer(vm, pi0);
234       p1 = vlib_get_buffer(vm, pi1);
235       ip40 = vlib_buffer_get_current(p0);
236       ip41 = vlib_buffer_get_current(p1);
237       p0->current_length = clib_net_to_host_u16(ip40->length);
238       p1->current_length = clib_net_to_host_u16(ip41->length);
239       d0 = ip4_map_get_domain(vnet_buffer(p0)->ip.adj_index[VLIB_TX], &map_domain_index0);
240       d1 = ip4_map_get_domain(vnet_buffer(p1)->ip.adj_index[VLIB_TX], &map_domain_index1);
241       ASSERT(d0);
242       ASSERT(d1);
243
244       /*
245        * Shared IPv4 address
246        */
247       port0 = ip4_map_port_and_security_check(d0, ip40, &next0, &error0);
248       port1 = ip4_map_port_and_security_check(d1, ip41, &next1, &error1);
249
250       /* MAP calc */
251       u32 da40 = clib_net_to_host_u32(ip40->dst_address.as_u32);
252       u32 da41 = clib_net_to_host_u32(ip41->dst_address.as_u32);
253       u16 dp40 = clib_net_to_host_u16(port0);
254       u16 dp41 = clib_net_to_host_u16(port1);
255       u64 dal60 = map_get_pfx(d0, da40, dp40);
256       u64 dal61 = map_get_pfx(d1, da41, dp41);
257       u64 dar60 = map_get_sfx(d0, da40, dp40);
258       u64 dar61 = map_get_sfx(d1, da41, dp41);
259       if (dal60 == 0 && dar60 == 0) error0 = MAP_ERROR_UNKNOWN;
260       if (dal61 == 0 && dar61 == 0) error1 = MAP_ERROR_UNKNOWN;
261
262       /* construct ipv6 header */
263       vlib_buffer_advance(p0, - sizeof(ip6_header_t));
264       vlib_buffer_advance(p1, - sizeof(ip6_header_t));
265       ip6h0 = vlib_buffer_get_current(p0);
266       ip6h1 = vlib_buffer_get_current(p1);
267       vnet_buffer(p0)->sw_if_index[VLIB_TX] = (u32)~0;
268       vnet_buffer(p1)->sw_if_index[VLIB_TX] = (u32)~0;
269
270       ip6h0->ip_version_traffic_class_and_flow_label = ip4_map_vtcfl(ip40, p0);
271       ip6h1->ip_version_traffic_class_and_flow_label = ip4_map_vtcfl(ip41, p1);
272       ip6h0->payload_length = ip40->length;
273       ip6h1->payload_length = ip41->length;
274       ip6h0->protocol = IP_PROTOCOL_IP_IN_IP;
275       ip6h1->protocol = IP_PROTOCOL_IP_IN_IP;
276       ip6h0->hop_limit = 0x40;
277       ip6h1->hop_limit = 0x40;
278       ip6h0->src_address = d0->ip6_src;
279       ip6h1->src_address = d1->ip6_src;
280       ip6h0->dst_address.as_u64[0] = clib_host_to_net_u64(dal60);
281       ip6h0->dst_address.as_u64[1] = clib_host_to_net_u64(dar60);
282       ip6h1->dst_address.as_u64[0] = clib_host_to_net_u64(dal61);
283       ip6h1->dst_address.as_u64[1] = clib_host_to_net_u64(dar61);
284
285       /*
286        * Determine next node. Can be one of:
287        * ip6-lookup, ip6-rewrite, ip4-fragment, ip4-virtreass, error-drop
288        */
289       if (PREDICT_TRUE(error0 == MAP_ERROR_NONE)) {
290         if (PREDICT_FALSE(d0->mtu && (clib_net_to_host_u16(ip6h0->payload_length) + sizeof(*ip6h0) > d0->mtu))) {
291           vnet_buffer(p0)->ip_frag.header_offset = sizeof(*ip6h0);
292           vnet_buffer(p0)->ip_frag.next_index = IP4_FRAG_NEXT_IP6_LOOKUP;
293           vnet_buffer(p0)->ip_frag.mtu = d0->mtu;
294           vnet_buffer(p0)->ip_frag.flags = IP_FRAG_FLAG_IP6_HEADER;
295           next0 = IP4_MAP_NEXT_FRAGMENT;
296         } else {
297           next0 = ip4_map_ip6_lookup_bypass(p0, ip40) ? IP4_MAP_NEXT_IP6_REWRITE : next0;
298           vlib_increment_combined_counter(cm + MAP_DOMAIN_COUNTER_TX, cpu_index, map_domain_index0, 1,
299                                           clib_net_to_host_u16(ip6h0->payload_length) + 40);
300         }
301       } else {
302         next0 = IP4_MAP_NEXT_DROP;
303       }
304
305       /*
306        * Determine next node. Can be one of:
307        * ip6-lookup, ip6-rewrite, ip4-fragment, ip4-virtreass, error-drop
308        */
309       if (PREDICT_TRUE(error1 == MAP_ERROR_NONE)) {
310         if (PREDICT_FALSE(d1->mtu && (clib_net_to_host_u16(ip6h1->payload_length) + sizeof(*ip6h1) > d1->mtu))) {
311           vnet_buffer(p1)->ip_frag.header_offset = sizeof(*ip6h1);
312           vnet_buffer(p1)->ip_frag.next_index = IP4_FRAG_NEXT_IP6_LOOKUP;
313           vnet_buffer(p1)->ip_frag.mtu = d1->mtu;
314           vnet_buffer(p1)->ip_frag.flags = IP_FRAG_FLAG_IP6_HEADER;
315           next1 = IP4_MAP_NEXT_FRAGMENT;
316         } else {
317           next1 = ip4_map_ip6_lookup_bypass(p1, ip41) ? IP4_MAP_NEXT_IP6_REWRITE : next1;
318           vlib_increment_combined_counter(cm + MAP_DOMAIN_COUNTER_TX, cpu_index, map_domain_index1, 1,
319                                           clib_net_to_host_u16(ip6h1->payload_length) + 40);
320         }
321       } else {
322         next1 = IP4_MAP_NEXT_DROP;
323       }
324
325       if (PREDICT_FALSE(p0->flags & VLIB_BUFFER_IS_TRACED)) {
326         map_trace_t *tr = vlib_add_trace(vm, node, p0, sizeof(*tr));
327         tr->map_domain_index = map_domain_index0;
328         tr->port = port0;
329       }
330       if (PREDICT_FALSE(p1->flags & VLIB_BUFFER_IS_TRACED)) {
331         map_trace_t *tr = vlib_add_trace(vm, node, p1, sizeof(*tr));
332         tr->map_domain_index = map_domain_index1;
333         tr->port = port1;
334       }
335
336       p0->error = error_node->errors[error0];
337       p1->error = error_node->errors[error1];
338
339       vlib_validate_buffer_enqueue_x2(vm, node, next_index, to_next, n_left_to_next, pi0, pi1, next0, next1);
340     }
341
342     while (n_left_from > 0 && n_left_to_next > 0) {
343       u32 pi0;
344       vlib_buffer_t *p0;
345       map_domain_t *d0;
346       u8 error0 = MAP_ERROR_NONE;
347       ip4_header_t *ip40;
348       u16 port0 = 0;
349       ip6_header_t *ip6h0;
350       u32 next0 = IP4_MAP_NEXT_IP6_LOOKUP;
351       u32 map_domain_index0 = ~0;
352
353       pi0 = to_next[0] = from[0];
354       from += 1;
355       n_left_from -= 1;
356       to_next +=1;
357       n_left_to_next -= 1;
358
359       p0 = vlib_get_buffer(vm, pi0);
360       ip40 = vlib_buffer_get_current(p0);
361       p0->current_length = clib_net_to_host_u16(ip40->length);
362       d0 = ip4_map_get_domain(vnet_buffer(p0)->ip.adj_index[VLIB_TX], &map_domain_index0);
363       ASSERT(d0);
364
365       /*
366        * Shared IPv4 address
367        */
368       port0 = ip4_map_port_and_security_check(d0, ip40, &next0, &error0);
369
370       /* Decrement IPv4 TTL */
371       ip4_map_decrement_ttl(ip40, &error0);
372
373       /* MAP calc */
374       u32 da40 = clib_net_to_host_u32(ip40->dst_address.as_u32);
375       u16 dp40 = clib_net_to_host_u16(port0);
376       u64 dal60 = map_get_pfx(d0, da40, dp40);
377       u64 dar60 = map_get_sfx(d0, da40, dp40);
378       if (dal60 == 0 && dar60 == 0 && error0 == MAP_ERROR_NONE) error0 = MAP_ERROR_UNKNOWN;
379
380       /* construct ipv6 header */
381       vlib_buffer_advance(p0, - (sizeof(ip6_header_t)));
382       ip6h0 = vlib_buffer_get_current(p0);
383       vnet_buffer(p0)->sw_if_index[VLIB_TX] = (u32)~0;
384
385       ip6h0->ip_version_traffic_class_and_flow_label = ip4_map_vtcfl(ip40, p0);
386       ip6h0->payload_length = ip40->length;
387       ip6h0->protocol = IP_PROTOCOL_IP_IN_IP;
388       ip6h0->hop_limit = 0x40;
389       ip6h0->src_address = d0->ip6_src;
390       ip6h0->dst_address.as_u64[0] = clib_host_to_net_u64(dal60);
391       ip6h0->dst_address.as_u64[1] = clib_host_to_net_u64(dar60);
392
393       /*
394        * Determine next node. Can be one of:
395        * ip6-lookup, ip6-rewrite, ip4-fragment, ip4-virtreass, error-drop
396        */
397       if (PREDICT_TRUE(error0 == MAP_ERROR_NONE)) {
398         if (PREDICT_FALSE(d0->mtu && (clib_net_to_host_u16(ip6h0->payload_length) + sizeof(*ip6h0) > d0->mtu))) {
399           vnet_buffer(p0)->ip_frag.header_offset = sizeof(*ip6h0);
400           vnet_buffer(p0)->ip_frag.next_index = IP4_FRAG_NEXT_IP6_LOOKUP;
401           vnet_buffer(p0)->ip_frag.mtu = d0->mtu;
402           vnet_buffer(p0)->ip_frag.flags = IP_FRAG_FLAG_IP6_HEADER;
403           next0 = IP4_MAP_NEXT_FRAGMENT;
404         } else {
405           next0 = ip4_map_ip6_lookup_bypass(p0, ip40) ? IP4_MAP_NEXT_IP6_REWRITE : next0;
406           vlib_increment_combined_counter(cm + MAP_DOMAIN_COUNTER_TX, cpu_index, map_domain_index0, 1,
407                                           clib_net_to_host_u16(ip6h0->payload_length) + 40);
408         }
409       } else {
410         next0 = IP4_MAP_NEXT_DROP;
411       }
412
413       if (PREDICT_FALSE(p0->flags & VLIB_BUFFER_IS_TRACED)) {
414         map_trace_t *tr = vlib_add_trace(vm, node, p0, sizeof(*tr));
415         tr->map_domain_index = map_domain_index0;
416         tr->port = port0;
417       }
418
419       p0->error = error_node->errors[error0];
420       vlib_validate_buffer_enqueue_x1(vm, node, next_index, to_next, n_left_to_next, pi0, next0);
421     }
422     vlib_put_next_frame(vm, node, next_index, n_left_to_next);  
423   }
424
425   return frame->n_vectors;
426 }
427
428 /*
429  * ip4_map_reass
430  */
431 static uword
432 ip4_map_reass (vlib_main_t *vm,
433                vlib_node_runtime_t *node,
434                vlib_frame_t *frame)
435 {
436   u32 n_left_from, *from, next_index, *to_next, n_left_to_next;
437   vlib_node_runtime_t *error_node = vlib_node_get_runtime(vm, ip4_map_reass_node.index);
438   from = vlib_frame_vector_args(frame);
439   n_left_from = frame->n_vectors;
440   next_index = node->cached_next_index;
441   map_main_t *mm = &map_main;
442   vlib_combined_counter_main_t *cm = mm->domain_counters;
443   u32 cpu_index = os_get_cpu_number();
444   u32 *fragments_to_drop = NULL;
445   u32 *fragments_to_loopback = NULL;
446
447   while (n_left_from > 0) {
448     vlib_get_next_frame(vm, node, next_index, to_next, n_left_to_next);
449
450     while (n_left_from > 0 && n_left_to_next > 0) {
451       u32 pi0;
452       vlib_buffer_t *p0;
453       map_domain_t *d0;
454       u8 error0 = MAP_ERROR_NONE;
455       ip4_header_t *ip40;
456       i32 port0 = 0;
457       ip6_header_t *ip60;
458       u32 next0 = IP4_MAP_REASS_NEXT_IP6_LOOKUP;
459       u32 map_domain_index0;
460       u8 cached = 0;
461
462       pi0 = to_next[0] = from[0];
463       from += 1;
464       n_left_from -= 1;
465       to_next +=1;
466       n_left_to_next -= 1;
467
468       p0 = vlib_get_buffer(vm, pi0);
469       ip60 = vlib_buffer_get_current(p0);
470       ip40 = (ip4_header_t *)(ip60 + 1);
471       d0 = ip4_map_get_domain(vnet_buffer(p0)->ip.adj_index[VLIB_TX], &map_domain_index0);
472
473       map_ip4_reass_lock();
474       map_ip4_reass_t *r = map_ip4_reass_get(ip40->src_address.as_u32, ip40->dst_address.as_u32,
475                                              ip40->fragment_id, ip40->protocol, &fragments_to_drop);
476       if (PREDICT_FALSE(!r)) {
477         // Could not create a caching entry
478         error0 = MAP_ERROR_FRAGMENT_MEMORY;
479       } else if (PREDICT_TRUE(ip4_get_fragment_offset(ip40))) {
480         if (r->port >= 0) {
481           // We know the port already
482           port0 = r->port;
483         } else if (map_ip4_reass_add_fragment(r, pi0)) {
484           // Not enough space for caching
485           error0 = MAP_ERROR_FRAGMENT_MEMORY;
486           map_ip4_reass_free(r, &fragments_to_drop);
487         } else {
488           cached = 1;
489         }
490       } else if ((port0 = ip4_get_port(ip40, MAP_RECEIVER, p0->current_length)) < 0) {
491         // Could not find port. We'll free the reassembly.
492         error0 = MAP_ERROR_BAD_PROTOCOL;
493         port0 = 0;
494         map_ip4_reass_free(r, &fragments_to_drop);
495       } else {
496         r->port = port0;
497         map_ip4_reass_get_fragments(r, &fragments_to_loopback);
498       }
499
500 #ifdef MAP_IP4_REASS_COUNT_BYTES
501       if (!cached && r) {
502         r->forwarded += clib_host_to_net_u16(ip40->length) - 20;
503         if (!ip4_get_fragment_more(ip40))
504           r->expected_total = ip4_get_fragment_offset(ip40) * 8 + clib_host_to_net_u16(ip40->length) - 20;
505         if(r->forwarded >= r->expected_total)
506           map_ip4_reass_free(r, &fragments_to_drop);
507       }
508 #endif
509
510       map_ip4_reass_unlock();
511
512       // NOTE: Most operations have already been performed by ip4_map
513       // All we need is the right destination address
514       ip60->dst_address.as_u64[0] = map_get_pfx_net(d0, ip40->dst_address.as_u32, port0);
515       ip60->dst_address.as_u64[1] = map_get_sfx_net(d0, ip40->dst_address.as_u32, port0);
516
517       if (PREDICT_FALSE(d0->mtu && (clib_net_to_host_u16(ip60->payload_length) + sizeof(*ip60) > d0->mtu))) {
518         vnet_buffer(p0)->ip_frag.header_offset = sizeof(*ip60);
519         vnet_buffer(p0)->ip_frag.next_index = IP4_FRAG_NEXT_IP6_LOOKUP;
520         vnet_buffer(p0)->ip_frag.mtu = d0->mtu;
521         vnet_buffer(p0)->ip_frag.flags = IP_FRAG_FLAG_IP6_HEADER;
522         next0 = IP4_MAP_REASS_NEXT_IP4_FRAGMENT;
523       }
524
525       if (PREDICT_FALSE(p0->flags & VLIB_BUFFER_IS_TRACED)) {
526         map_ip4_map_reass_trace_t *tr = vlib_add_trace(vm, node, p0, sizeof(*tr));
527         tr->map_domain_index = map_domain_index0;
528         tr->port = port0;
529         tr->cached = cached;
530       }
531
532       if(cached) {
533         //Dequeue the packet
534         n_left_to_next++;
535         to_next--;
536       } else {
537         if (error0 == MAP_ERROR_NONE)
538           vlib_increment_combined_counter(cm + MAP_DOMAIN_COUNTER_TX, cpu_index, map_domain_index0, 1,
539                                           clib_net_to_host_u16(ip60->payload_length) + 40);
540         next0 = (error0 == MAP_ERROR_NONE) ? next0 : IP4_MAP_REASS_NEXT_DROP;
541         p0->error = error_node->errors[error0];
542         vlib_validate_buffer_enqueue_x1(vm, node, next_index, to_next, n_left_to_next, pi0, next0);
543       }
544
545       //Loopback when we reach the end of the inpu vector
546       if(n_left_from == 0 && vec_len(fragments_to_loopback)) {
547         from = vlib_frame_vector_args(frame);
548         u32 len = vec_len(fragments_to_loopback);
549         if(len <= VLIB_FRAME_SIZE) {
550           memcpy(from, fragments_to_loopback, sizeof(u32)*len);
551           n_left_from = len;
552           vec_reset_length(fragments_to_loopback);
553         } else {
554           memcpy(from, fragments_to_loopback + (len - VLIB_FRAME_SIZE), sizeof(u32)*VLIB_FRAME_SIZE);
555           n_left_from = VLIB_FRAME_SIZE;
556           _vec_len(fragments_to_loopback) = len - VLIB_FRAME_SIZE;
557         }
558       }
559     }
560     vlib_put_next_frame(vm, node, next_index, n_left_to_next);
561   }
562
563   map_send_all_to_node(vm, fragments_to_drop, node,
564                        &error_node->errors[MAP_ERROR_FRAGMENT_DROPPED],
565                        IP4_MAP_REASS_NEXT_DROP);
566
567   vec_free(fragments_to_drop);
568   vec_free(fragments_to_loopback);
569   return frame->n_vectors;
570 }
571
572 static char *map_error_strings[] = {
573 #define _(sym,string) string,
574   foreach_map_error
575 #undef _
576 };
577
578 VLIB_REGISTER_NODE(ip4_map_node) = {
579   .function = ip4_map,
580   .name = "ip4-map",
581   .vector_size = sizeof(u32),
582   .format_trace = format_map_trace,
583   .type = VLIB_NODE_TYPE_INTERNAL,
584   
585   .n_errors = MAP_N_ERROR,
586   .error_strings = map_error_strings,
587
588   .n_next_nodes = IP4_MAP_N_NEXT,
589   .next_nodes = {
590     [IP4_MAP_NEXT_IP6_LOOKUP] = "ip6-lookup",
591 #ifdef MAP_SKIP_IP6_LOOKUP
592     [IP4_MAP_NEXT_IP6_REWRITE] = "ip6-rewrite",
593 #endif
594     [IP4_MAP_NEXT_FRAGMENT] = "ip4-frag",
595     [IP4_MAP_NEXT_REASS] = "ip4-map-reass",
596     [IP4_MAP_NEXT_DROP] = "error-drop",
597   },
598 };
599
600 VLIB_REGISTER_NODE(ip4_map_reass_node) = {
601   .function = ip4_map_reass,
602   .name = "ip4-map-reass",
603   .vector_size = sizeof(u32),
604   .format_trace = format_ip4_map_reass_trace,
605   .type = VLIB_NODE_TYPE_INTERNAL,
606   
607   .n_errors = MAP_N_ERROR,
608   .error_strings = map_error_strings,
609
610   .n_next_nodes = IP4_MAP_REASS_N_NEXT,
611   .next_nodes = {
612     [IP4_MAP_REASS_NEXT_IP6_LOOKUP] = "ip6-lookup",
613     [IP4_MAP_REASS_NEXT_IP4_FRAGMENT] = "ip4-frag",
614     [IP4_MAP_REASS_NEXT_DROP] = "error-drop",
615   },
616 };