VCL: Refactor VCL test (part 1)
[vpp.git] / src / plugins / map / ip4_map_t.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 #include "map.h"
16
17 #include <vnet/ip/ip_frag.h>
18 #include <vnet/ip/ip4_to_ip6.h>
19
20 #define IP4_MAP_T_DUAL_LOOP 1
21
22 typedef enum
23 {
24   IP4_MAPT_NEXT_MAPT_TCP_UDP,
25   IP4_MAPT_NEXT_MAPT_ICMP,
26   IP4_MAPT_NEXT_MAPT_FRAGMENTED,
27   IP4_MAPT_NEXT_DROP,
28   IP4_MAPT_N_NEXT
29 } ip4_mapt_next_t;
30
31 typedef enum
32 {
33   IP4_MAPT_ICMP_NEXT_IP6_LOOKUP,
34   IP4_MAPT_ICMP_NEXT_IP6_FRAG,
35   IP4_MAPT_ICMP_NEXT_DROP,
36   IP4_MAPT_ICMP_N_NEXT
37 } ip4_mapt_icmp_next_t;
38
39 typedef enum
40 {
41   IP4_MAPT_TCP_UDP_NEXT_IP6_LOOKUP,
42   IP4_MAPT_TCP_UDP_NEXT_IP6_FRAG,
43   IP4_MAPT_TCP_UDP_NEXT_DROP,
44   IP4_MAPT_TCP_UDP_N_NEXT
45 } ip4_mapt_tcp_udp_next_t;
46
47 typedef enum
48 {
49   IP4_MAPT_FRAGMENTED_NEXT_IP6_LOOKUP,
50   IP4_MAPT_FRAGMENTED_NEXT_IP6_FRAG,
51   IP4_MAPT_FRAGMENTED_NEXT_DROP,
52   IP4_MAPT_FRAGMENTED_N_NEXT
53 } ip4_mapt_fragmented_next_t;
54
55 //This is used to pass information within the buffer data.
56 //Buffer structure being too small to contain big structures like this.
57 /* *INDENT-OFF* */
58 typedef CLIB_PACKED (struct {
59   ip6_address_t daddr;
60   ip6_address_t saddr;
61   //IPv6 header + Fragmentation header will be here
62   //sizeof(ip6) + sizeof(ip_frag) - sizeof(ip4)
63   u8 unused[28];
64 }) ip4_mapt_pseudo_header_t;
65 /* *INDENT-ON* */
66
67
68 static_always_inline int
69 ip4_map_fragment_cache (ip4_header_t * ip4, u16 port)
70 {
71   u32 *ignore = NULL;
72   map_ip4_reass_lock ();
73   map_ip4_reass_t *r =
74     map_ip4_reass_get (ip4->src_address.as_u32, ip4->dst_address.as_u32,
75                        ip4->fragment_id,
76                        (ip4->protocol ==
77                         IP_PROTOCOL_ICMP) ? IP_PROTOCOL_ICMP6 : ip4->protocol,
78                        &ignore);
79   if (r)
80     r->port = port;
81
82   map_ip4_reass_unlock ();
83   return !r;
84 }
85
86 static_always_inline i32
87 ip4_map_fragment_get_port (ip4_header_t * ip4)
88 {
89   u32 *ignore = NULL;
90   map_ip4_reass_lock ();
91   map_ip4_reass_t *r =
92     map_ip4_reass_get (ip4->src_address.as_u32, ip4->dst_address.as_u32,
93                        ip4->fragment_id,
94                        (ip4->protocol ==
95                         IP_PROTOCOL_ICMP) ? IP_PROTOCOL_ICMP6 : ip4->protocol,
96                        &ignore);
97   i32 ret = r ? r->port : -1;
98   map_ip4_reass_unlock ();
99   return ret;
100 }
101
102 typedef struct
103 {
104   map_domain_t *d;
105   u16 id;
106 } icmp_to_icmp6_ctx_t;
107
108 static int
109 ip4_to_ip6_set_icmp_cb (ip4_header_t * ip4, ip6_header_t * ip6, void *arg)
110 {
111   icmp_to_icmp6_ctx_t *ctx = arg;
112   map_main_t *mm = &map_main;
113
114   if (mm->is_ce)
115     {
116       ip6->src_address.as_u64[0] =
117         map_get_pfx_net (ctx->d, ip4->src_address.as_u32, ctx->id);
118       ip6->src_address.as_u64[1] =
119         map_get_sfx_net (ctx->d, ip4->src_address.as_u32, ctx->id);
120       ip4_map_t_embedded_address (ctx->d, &ip6->dst_address,
121                                   &ip4->dst_address);
122     }
123   else
124     {
125       ip4_map_t_embedded_address (ctx->d, &ip6->src_address,
126                                   &ip4->src_address);
127       ip6->dst_address.as_u64[0] =
128         map_get_pfx_net (ctx->d, ip4->dst_address.as_u32, ctx->id);
129       ip6->dst_address.as_u64[1] =
130         map_get_sfx_net (ctx->d, ip4->dst_address.as_u32, ctx->id);
131     }
132
133   return 0;
134 }
135
136 static int
137 ip4_to_ip6_set_inner_icmp_cb (ip4_header_t * ip4, ip6_header_t * ip6,
138                               void *arg)
139 {
140   icmp_to_icmp6_ctx_t *ctx = arg;
141   map_main_t *mm = &map_main;
142
143   if (mm->is_ce)
144     {
145       //Note that the destination address is within the domain
146       //while the source address is the one outside the domain
147       ip4_map_t_embedded_address (ctx->d, &ip6->src_address,
148                                   &ip4->src_address);
149       ip6->dst_address.as_u64[0] =
150         map_get_pfx_net (ctx->d, ip4->dst_address.as_u32, ctx->id);
151       ip6->dst_address.as_u64[1] =
152         map_get_sfx_net (ctx->d, ip4->dst_address.as_u32, ctx->id);
153     }
154   else
155     {
156       //Note that the source address is within the domain
157       //while the destination address is the one outside the domain
158       ip4_map_t_embedded_address (ctx->d, &ip6->dst_address,
159                                   &ip4->dst_address);
160       ip6->src_address.as_u64[0] =
161         map_get_pfx_net (ctx->d, ip4->src_address.as_u32, ctx->id);
162       ip6->src_address.as_u64[1] =
163         map_get_sfx_net (ctx->d, ip4->src_address.as_u32, ctx->id);
164     }
165
166   return 0;
167 }
168
169 static uword
170 ip4_map_t_icmp (vlib_main_t * vm,
171                 vlib_node_runtime_t * node, vlib_frame_t * frame)
172 {
173   u32 n_left_from, *from, next_index, *to_next, n_left_to_next;
174   vlib_node_runtime_t *error_node =
175     vlib_node_get_runtime (vm, ip4_map_t_icmp_node.index);
176   from = vlib_frame_vector_args (frame);
177   n_left_from = frame->n_vectors;
178   next_index = node->cached_next_index;
179   vlib_combined_counter_main_t *cm = map_main.domain_counters;
180   u32 thread_index = vlib_get_thread_index ();
181
182   while (n_left_from > 0)
183     {
184       vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
185
186       while (n_left_from > 0 && n_left_to_next > 0)
187         {
188           u32 pi0;
189           vlib_buffer_t *p0;
190           ip4_mapt_icmp_next_t next0;
191           u8 error0;
192           map_domain_t *d0;
193           u16 len0;
194           icmp_to_icmp6_ctx_t ctx0;
195           ip4_header_t *ip40;
196           icmp46_header_t *icmp0;
197
198           next0 = IP4_MAPT_ICMP_NEXT_IP6_LOOKUP;
199           pi0 = to_next[0] = from[0];
200           from += 1;
201           n_left_from -= 1;
202           to_next += 1;
203           n_left_to_next -= 1;
204           error0 = MAP_ERROR_NONE;
205
206           p0 = vlib_get_buffer (vm, pi0);
207           vlib_buffer_advance (p0, sizeof (ip4_mapt_pseudo_header_t));  //The pseudo-header is not used
208           len0 =
209             clib_net_to_host_u16 (((ip4_header_t *)
210                                    vlib_buffer_get_current (p0))->length);
211           d0 =
212             pool_elt_at_index (map_main.domains,
213                                vnet_buffer (p0)->map_t.map_domain_index);
214
215           ip40 = vlib_buffer_get_current (p0);
216           icmp0 = (icmp46_header_t *) (ip40 + 1);
217
218           ctx0.id = ip4_get_port (ip40, icmp0->type == ICMP6_echo_request);
219           ctx0.d = d0;
220           if (ctx0.id == 0)
221             {
222               // In case of 1:1 mapping, we don't care about the port
223               if (!(d0->ea_bits_len == 0 && d0->rules))
224                 {
225                   error0 = MAP_ERROR_ICMP;
226                   goto err0;
227                 }
228             }
229
230           if (icmp_to_icmp6
231               (p0, ip4_to_ip6_set_icmp_cb, &ctx0,
232                ip4_to_ip6_set_inner_icmp_cb, &ctx0))
233             {
234               error0 = MAP_ERROR_ICMP;
235               goto err0;
236             }
237
238           if (vnet_buffer (p0)->map_t.mtu < p0->current_length)
239             {
240               vnet_buffer (p0)->ip_frag.header_offset = 0;
241               vnet_buffer (p0)->ip_frag.mtu = vnet_buffer (p0)->map_t.mtu;
242               vnet_buffer (p0)->ip_frag.next_index = IP6_FRAG_NEXT_IP6_LOOKUP;
243               next0 = IP4_MAPT_ICMP_NEXT_IP6_FRAG;
244             }
245         err0:
246           if (PREDICT_TRUE (error0 == MAP_ERROR_NONE))
247             {
248               vlib_increment_combined_counter (cm + MAP_DOMAIN_COUNTER_TX,
249                                                thread_index,
250                                                vnet_buffer (p0)->
251                                                map_t.map_domain_index, 1,
252                                                len0);
253             }
254           else
255             {
256               next0 = IP4_MAPT_ICMP_NEXT_DROP;
257             }
258           p0->error = error_node->errors[error0];
259           vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
260                                            to_next, n_left_to_next, pi0,
261                                            next0);
262         }
263       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
264     }
265   return frame->n_vectors;
266 }
267
268 static int
269 ip4_to_ip6_set_cb (ip4_header_t * ip4, ip6_header_t * ip6, void *ctx)
270 {
271   ip4_mapt_pseudo_header_t *pheader = ctx;
272
273   ip6->dst_address.as_u64[0] = pheader->daddr.as_u64[0];
274   ip6->dst_address.as_u64[1] = pheader->daddr.as_u64[1];
275   ip6->src_address.as_u64[0] = pheader->saddr.as_u64[0];
276   ip6->src_address.as_u64[1] = pheader->saddr.as_u64[1];
277
278   return 0;
279 }
280
281 static uword
282 ip4_map_t_fragmented (vlib_main_t * vm,
283                       vlib_node_runtime_t * node, vlib_frame_t * frame)
284 {
285   u32 n_left_from, *from, next_index, *to_next, n_left_to_next;
286   from = vlib_frame_vector_args (frame);
287   n_left_from = frame->n_vectors;
288   next_index = node->cached_next_index;
289   vlib_node_runtime_t *error_node =
290     vlib_node_get_runtime (vm, ip4_map_t_fragmented_node.index);
291
292   while (n_left_from > 0)
293     {
294       vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
295
296       while (n_left_from > 0 && n_left_to_next > 0)
297         {
298           u32 pi0;
299           vlib_buffer_t *p0;
300           ip4_mapt_pseudo_header_t *pheader0;
301           ip4_mapt_fragmented_next_t next0;
302
303           next0 = IP4_MAPT_FRAGMENTED_NEXT_IP6_LOOKUP;
304           pi0 = to_next[0] = from[0];
305           from += 1;
306           n_left_from -= 1;
307           to_next += 1;
308           n_left_to_next -= 1;
309
310           p0 = vlib_get_buffer (vm, pi0);
311
312           //Accessing pseudo header
313           pheader0 = vlib_buffer_get_current (p0);
314           vlib_buffer_advance (p0, sizeof (*pheader0));
315
316           if (ip4_to_ip6_fragmented (p0, ip4_to_ip6_set_cb, pheader0))
317             {
318               p0->error = error_node->errors[MAP_ERROR_FRAGMENT_DROPPED];
319               next0 = IP4_MAPT_FRAGMENTED_NEXT_DROP;
320             }
321           else
322             {
323               if (vnet_buffer (p0)->map_t.mtu < p0->current_length)
324                 {
325                   vnet_buffer (p0)->ip_frag.header_offset = 0;
326                   vnet_buffer (p0)->ip_frag.mtu = vnet_buffer (p0)->map_t.mtu;
327                   vnet_buffer (p0)->ip_frag.next_index =
328                     IP6_FRAG_NEXT_IP6_LOOKUP;
329                   next0 = IP4_MAPT_FRAGMENTED_NEXT_IP6_FRAG;
330                 }
331             }
332
333           vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
334                                            to_next, n_left_to_next, pi0,
335                                            next0);
336         }
337       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
338     }
339   return frame->n_vectors;
340 }
341
342 static uword
343 ip4_map_t_tcp_udp (vlib_main_t * vm,
344                    vlib_node_runtime_t * node, vlib_frame_t * frame)
345 {
346   u32 n_left_from, *from, next_index, *to_next, n_left_to_next;
347   from = vlib_frame_vector_args (frame);
348   n_left_from = frame->n_vectors;
349   next_index = node->cached_next_index;
350   vlib_node_runtime_t *error_node =
351     vlib_node_get_runtime (vm, ip4_map_t_tcp_udp_node.index);
352
353
354   while (n_left_from > 0)
355     {
356       vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
357
358 #ifdef IP4_MAP_T_DUAL_LOOP
359       while (n_left_from >= 4 && n_left_to_next >= 2)
360         {
361           u32 pi0, pi1;
362           vlib_buffer_t *p0, *p1;
363           ip4_mapt_pseudo_header_t *pheader0, *pheader1;
364           ip4_mapt_tcp_udp_next_t next0, next1;
365
366           pi0 = to_next[0] = from[0];
367           pi1 = to_next[1] = from[1];
368           from += 2;
369           n_left_from -= 2;
370           to_next += 2;
371           n_left_to_next -= 2;
372
373           next0 = IP4_MAPT_TCP_UDP_NEXT_IP6_LOOKUP;
374           next1 = IP4_MAPT_TCP_UDP_NEXT_IP6_LOOKUP;
375           p0 = vlib_get_buffer (vm, pi0);
376           p1 = vlib_get_buffer (vm, pi1);
377
378           //Accessing pseudo header
379           pheader0 = vlib_buffer_get_current (p0);
380           pheader1 = vlib_buffer_get_current (p1);
381           vlib_buffer_advance (p0, sizeof (*pheader0));
382           vlib_buffer_advance (p1, sizeof (*pheader1));
383
384           if (ip4_to_ip6_tcp_udp (p0, ip4_to_ip6_set_cb, pheader0))
385             {
386               p0->error = error_node->errors[MAP_ERROR_UNKNOWN];
387               next0 = IP4_MAPT_TCP_UDP_NEXT_DROP;
388             }
389           else
390             {
391               if (vnet_buffer (p0)->map_t.mtu < p0->current_length)
392                 {
393                   //Send to fragmentation node if necessary
394                   vnet_buffer (p0)->ip_frag.header_offset = 0;
395                   vnet_buffer (p0)->ip_frag.mtu = vnet_buffer (p0)->map_t.mtu;
396                   vnet_buffer (p0)->ip_frag.next_index =
397                     IP6_FRAG_NEXT_IP6_LOOKUP;
398                   next0 = IP4_MAPT_TCP_UDP_NEXT_IP6_FRAG;
399                 }
400             }
401
402           if (ip4_to_ip6_tcp_udp (p1, ip4_to_ip6_set_cb, pheader1))
403             {
404               p1->error = error_node->errors[MAP_ERROR_UNKNOWN];
405               next1 = IP4_MAPT_TCP_UDP_NEXT_DROP;
406             }
407           else
408             {
409               if (vnet_buffer (p1)->map_t.mtu < p1->current_length)
410                 {
411                   //Send to fragmentation node if necessary
412                   vnet_buffer (p1)->ip_frag.header_offset = 0;
413                   vnet_buffer (p1)->ip_frag.mtu = vnet_buffer (p1)->map_t.mtu;
414                   vnet_buffer (p1)->ip_frag.next_index =
415                     IP6_FRAG_NEXT_IP6_LOOKUP;
416                   next1 = IP4_MAPT_TCP_UDP_NEXT_IP6_FRAG;
417                 }
418             }
419
420           vlib_validate_buffer_enqueue_x2 (vm, node, next_index,
421                                            to_next, n_left_to_next, pi0, pi1,
422                                            next0, next1);
423         }
424 #endif
425
426       while (n_left_from > 0 && n_left_to_next > 0)
427         {
428           u32 pi0;
429           vlib_buffer_t *p0;
430           ip4_mapt_pseudo_header_t *pheader0;
431           ip4_mapt_tcp_udp_next_t next0;
432
433           pi0 = to_next[0] = from[0];
434           from += 1;
435           n_left_from -= 1;
436           to_next += 1;
437           n_left_to_next -= 1;
438
439           next0 = IP4_MAPT_TCP_UDP_NEXT_IP6_LOOKUP;
440           p0 = vlib_get_buffer (vm, pi0);
441
442           //Accessing pseudo header
443           pheader0 = vlib_buffer_get_current (p0);
444           vlib_buffer_advance (p0, sizeof (*pheader0));
445
446           if (ip4_to_ip6_tcp_udp (p0, ip4_to_ip6_set_cb, pheader0))
447             {
448               p0->error = error_node->errors[MAP_ERROR_UNKNOWN];
449               next0 = IP4_MAPT_TCP_UDP_NEXT_DROP;
450             }
451           else
452             {
453               if (vnet_buffer (p0)->map_t.mtu < p0->current_length)
454                 {
455                   //Send to fragmentation node if necessary
456                   vnet_buffer (p0)->ip_frag.header_offset = 0;
457                   vnet_buffer (p0)->ip_frag.mtu = vnet_buffer (p0)->map_t.mtu;
458                   vnet_buffer (p0)->ip_frag.next_index =
459                     IP6_FRAG_NEXT_IP6_LOOKUP;
460                   next0 = IP4_MAPT_TCP_UDP_NEXT_IP6_FRAG;
461                 }
462             }
463           vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
464                                            to_next, n_left_to_next, pi0,
465                                            next0);
466         }
467       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
468     }
469
470   return frame->n_vectors;
471 }
472
473 static_always_inline void
474 ip4_map_t_classify (vlib_buffer_t * p0, map_domain_t * d0,
475                     ip4_header_t * ip40, u16 ip4_len0, i32 * dst_port0,
476                     u8 * error0, ip4_mapt_next_t * next0)
477 {
478   map_main_t *mm = &map_main;
479   u32 port_offset;
480
481   if (mm->is_ce)
482     port_offset = 0;
483   else
484     port_offset = 2;
485
486   if (PREDICT_FALSE (ip4_get_fragment_offset (ip40)))
487     {
488       *next0 = IP4_MAPT_NEXT_MAPT_FRAGMENTED;
489       if (d0->ea_bits_len == 0 && d0->rules)
490         {
491           *dst_port0 = 0;
492         }
493       else
494         {
495           *dst_port0 = ip4_map_fragment_get_port (ip40);
496           *error0 = (*dst_port0 == -1) ? MAP_ERROR_FRAGMENT_MEMORY : *error0;
497         }
498     }
499   else if (PREDICT_TRUE (ip40->protocol == IP_PROTOCOL_TCP))
500     {
501       vnet_buffer (p0)->map_t.checksum_offset = 36;
502       *next0 = IP4_MAPT_NEXT_MAPT_TCP_UDP;
503       *error0 = ip4_len0 < 40 ? MAP_ERROR_MALFORMED : *error0;
504       *dst_port0 =
505         (i32) * ((u16 *) u8_ptr_add (ip40, sizeof (*ip40) + port_offset));
506     }
507   else if (PREDICT_TRUE (ip40->protocol == IP_PROTOCOL_UDP))
508     {
509       vnet_buffer (p0)->map_t.checksum_offset = 26;
510       *next0 = IP4_MAPT_NEXT_MAPT_TCP_UDP;
511       *error0 = ip4_len0 < 28 ? MAP_ERROR_MALFORMED : *error0;
512       *dst_port0 =
513         (i32) * ((u16 *) u8_ptr_add (ip40, sizeof (*ip40) + port_offset));
514     }
515   else if (ip40->protocol == IP_PROTOCOL_ICMP)
516     {
517       *next0 = IP4_MAPT_NEXT_MAPT_ICMP;
518       if (d0->ea_bits_len == 0 && d0->rules)
519         *dst_port0 = 0;
520       else if (((icmp46_header_t *) u8_ptr_add (ip40, sizeof (*ip40)))->code
521                == ICMP4_echo_reply
522                || ((icmp46_header_t *)
523                    u8_ptr_add (ip40,
524                                sizeof (*ip40)))->code == ICMP4_echo_request)
525         *dst_port0 = (i32) * ((u16 *) u8_ptr_add (ip40, sizeof (*ip40) + 6));
526     }
527   else
528     {
529       *error0 = MAP_ERROR_BAD_PROTOCOL;
530     }
531 }
532
533 static uword
534 ip4_map_t (vlib_main_t * vm, vlib_node_runtime_t * node, vlib_frame_t * frame)
535 {
536   u32 n_left_from, *from, next_index, *to_next, n_left_to_next;
537   vlib_node_runtime_t *error_node =
538     vlib_node_get_runtime (vm, ip4_map_t_node.index);
539   from = vlib_frame_vector_args (frame);
540   n_left_from = frame->n_vectors;
541   next_index = node->cached_next_index;
542   map_main_t *mm = &map_main;
543   vlib_combined_counter_main_t *cm = map_main.domain_counters;
544   u32 thread_index = vlib_get_thread_index ();
545
546   while (n_left_from > 0)
547     {
548       vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
549
550 #ifdef IP4_MAP_T_DUAL_LOOP
551       while (n_left_from >= 4 && n_left_to_next >= 2)
552         {
553           u32 pi0, pi1;
554           vlib_buffer_t *p0, *p1;
555           ip4_header_t *ip40, *ip41;
556           map_domain_t *d0, *d1;
557           ip4_mapt_next_t next0 = 0, next1 = 0;
558           u16 ip4_len0, ip4_len1;
559           u8 error0, error1;
560           i32 map_port0, map_port1;
561           ip4_mapt_pseudo_header_t *pheader0, *pheader1;
562
563           pi0 = to_next[0] = from[0];
564           pi1 = to_next[1] = from[1];
565           from += 2;
566           n_left_from -= 2;
567           to_next += 2;
568           n_left_to_next -= 2;
569           error0 = MAP_ERROR_NONE;
570           error1 = MAP_ERROR_NONE;
571
572           p0 = vlib_get_buffer (vm, pi0);
573           p1 = vlib_get_buffer (vm, pi1);
574           ip40 = vlib_buffer_get_current (p0);
575           ip41 = vlib_buffer_get_current (p1);
576           ip4_len0 = clib_host_to_net_u16 (ip40->length);
577           ip4_len1 = clib_host_to_net_u16 (ip41->length);
578
579           if (PREDICT_FALSE (p0->current_length < ip4_len0 ||
580                              ip40->ip_version_and_header_length != 0x45))
581             {
582               error0 = MAP_ERROR_UNKNOWN;
583               next0 = IP4_MAPT_NEXT_DROP;
584             }
585
586           if (PREDICT_FALSE (p1->current_length < ip4_len1 ||
587                              ip41->ip_version_and_header_length != 0x45))
588             {
589               error1 = MAP_ERROR_UNKNOWN;
590               next1 = IP4_MAPT_NEXT_DROP;
591             }
592
593           vnet_buffer (p0)->map_t.map_domain_index =
594             vnet_buffer (p0)->ip.adj_index[VLIB_TX];
595           d0 = ip4_map_get_domain (vnet_buffer (p0)->map_t.map_domain_index);
596           vnet_buffer (p1)->map_t.map_domain_index =
597             vnet_buffer (p1)->ip.adj_index[VLIB_TX];
598           d1 = ip4_map_get_domain (vnet_buffer (p1)->map_t.map_domain_index);
599
600           vnet_buffer (p0)->map_t.mtu = d0->mtu ? d0->mtu : ~0;
601           vnet_buffer (p1)->map_t.mtu = d1->mtu ? d1->mtu : ~0;
602
603           map_port0 = -1;
604           map_port1 = -1;
605
606           ip4_map_t_classify (p0, d0, ip40, ip4_len0, &map_port0, &error0,
607                               &next0);
608           ip4_map_t_classify (p1, d1, ip41, ip4_len1, &map_port1, &error1,
609                               &next1);
610
611           //Add MAP-T pseudo header in front of the packet
612           vlib_buffer_advance (p0, -sizeof (*pheader0));
613           vlib_buffer_advance (p1, -sizeof (*pheader1));
614           pheader0 = vlib_buffer_get_current (p0);
615           pheader1 = vlib_buffer_get_current (p1);
616
617           //Save addresses within the packet
618           if (mm->is_ce)
619             {
620               ip4_map_t_embedded_address (d0, &pheader0->daddr,
621                                           &ip40->dst_address);
622               ip4_map_t_embedded_address (d1, &pheader1->daddr,
623                                           &ip41->dst_address);
624               pheader0->saddr.as_u64[0] =
625                 map_get_pfx_net (d0, ip40->src_address.as_u32,
626                                  (u16) map_port0);
627               pheader0->saddr.as_u64[1] =
628                 map_get_sfx_net (d0, ip40->src_address.as_u32,
629                                  (u16) map_port0);
630               pheader1->saddr.as_u64[0] =
631                 map_get_pfx_net (d1, ip41->src_address.as_u32,
632                                  (u16) map_port1);
633               pheader1->saddr.as_u64[1] =
634                 map_get_sfx_net (d1, ip41->src_address.as_u32,
635                                  (u16) map_port1);
636             }
637           else
638             {
639               ip4_map_t_embedded_address (d0, &pheader0->saddr,
640                                           &ip40->src_address);
641               ip4_map_t_embedded_address (d1, &pheader1->saddr,
642                                           &ip41->src_address);
643               pheader0->daddr.as_u64[0] =
644                 map_get_pfx_net (d0, ip40->dst_address.as_u32,
645                                  (u16) map_port0);
646               pheader0->daddr.as_u64[1] =
647                 map_get_sfx_net (d0, ip40->dst_address.as_u32,
648                                  (u16) map_port0);
649               pheader1->daddr.as_u64[0] =
650                 map_get_pfx_net (d1, ip41->dst_address.as_u32,
651                                  (u16) map_port1);
652               pheader1->daddr.as_u64[1] =
653                 map_get_sfx_net (d1, ip41->dst_address.as_u32,
654                                  (u16) map_port1);
655             }
656
657           if (PREDICT_FALSE
658               (ip4_is_first_fragment (ip40) && (map_port0 != -1)
659                && (d0->ea_bits_len != 0 || !d0->rules)
660                && ip4_map_fragment_cache (ip40, map_port0)))
661             {
662               error0 = MAP_ERROR_FRAGMENT_MEMORY;
663             }
664
665           if (PREDICT_FALSE
666               (ip4_is_first_fragment (ip41) && (map_port1 != -1)
667                && (d1->ea_bits_len != 0 || !d1->rules)
668                && ip4_map_fragment_cache (ip41, map_port1)))
669             {
670               error1 = MAP_ERROR_FRAGMENT_MEMORY;
671             }
672
673           if (PREDICT_TRUE
674               (error0 == MAP_ERROR_NONE && next0 != IP4_MAPT_NEXT_MAPT_ICMP))
675             {
676               vlib_increment_combined_counter (cm + MAP_DOMAIN_COUNTER_TX,
677                                                thread_index,
678                                                vnet_buffer (p0)->
679                                                map_t.map_domain_index, 1,
680                                                clib_net_to_host_u16
681                                                (ip40->length));
682             }
683
684           if (PREDICT_TRUE
685               (error1 == MAP_ERROR_NONE && next1 != IP4_MAPT_NEXT_MAPT_ICMP))
686             {
687               vlib_increment_combined_counter (cm + MAP_DOMAIN_COUNTER_TX,
688                                                thread_index,
689                                                vnet_buffer (p1)->
690                                                map_t.map_domain_index, 1,
691                                                clib_net_to_host_u16
692                                                (ip41->length));
693             }
694
695           next0 = (error0 != MAP_ERROR_NONE) ? IP4_MAPT_NEXT_DROP : next0;
696           next1 = (error1 != MAP_ERROR_NONE) ? IP4_MAPT_NEXT_DROP : next1;
697           p0->error = error_node->errors[error0];
698           p1->error = error_node->errors[error1];
699           vlib_validate_buffer_enqueue_x2 (vm, node, next_index, to_next,
700                                            n_left_to_next, pi0, pi1, next0,
701                                            next1);
702         }
703 #endif
704
705       while (n_left_from > 0 && n_left_to_next > 0)
706         {
707           u32 pi0;
708           vlib_buffer_t *p0;
709           ip4_header_t *ip40;
710           map_domain_t *d0;
711           ip4_mapt_next_t next0;
712           u16 ip4_len0;
713           u8 error0;
714           i32 map_port0;
715           ip4_mapt_pseudo_header_t *pheader0;
716
717           pi0 = to_next[0] = from[0];
718           from += 1;
719           n_left_from -= 1;
720           to_next += 1;
721           n_left_to_next -= 1;
722           error0 = MAP_ERROR_NONE;
723
724           p0 = vlib_get_buffer (vm, pi0);
725           ip40 = vlib_buffer_get_current (p0);
726           ip4_len0 = clib_host_to_net_u16 (ip40->length);
727           if (PREDICT_FALSE (p0->current_length < ip4_len0 ||
728                              ip40->ip_version_and_header_length != 0x45))
729             {
730               error0 = MAP_ERROR_UNKNOWN;
731               next0 = IP4_MAPT_NEXT_DROP;
732             }
733
734           vnet_buffer (p0)->map_t.map_domain_index =
735             vnet_buffer (p0)->ip.adj_index[VLIB_TX];
736           d0 = ip4_map_get_domain (vnet_buffer (p0)->map_t.map_domain_index);
737
738           vnet_buffer (p0)->map_t.mtu = d0->mtu ? d0->mtu : ~0;
739
740           map_port0 = -1;
741           ip4_map_t_classify (p0, d0, ip40, ip4_len0, &map_port0, &error0,
742                               &next0);
743
744           //Add MAP-T pseudo header in front of the packet
745           vlib_buffer_advance (p0, -sizeof (*pheader0));
746           pheader0 = vlib_buffer_get_current (p0);
747
748           //Save addresses within the packet
749           if (mm->is_ce)
750             {
751               ip4_map_t_embedded_address (d0, &pheader0->daddr,
752                                           &ip40->dst_address);
753               pheader0->saddr.as_u64[0] =
754                 map_get_pfx_net (d0, ip40->src_address.as_u32,
755                                  (u16) map_port0);
756               pheader0->saddr.as_u64[1] =
757                 map_get_sfx_net (d0, ip40->src_address.as_u32,
758                                  (u16) map_port0);
759             }
760           else
761             {
762               ip4_map_t_embedded_address (d0, &pheader0->saddr,
763                                           &ip40->src_address);
764               pheader0->daddr.as_u64[0] =
765                 map_get_pfx_net (d0, ip40->dst_address.as_u32,
766                                  (u16) map_port0);
767               pheader0->daddr.as_u64[1] =
768                 map_get_sfx_net (d0, ip40->dst_address.as_u32,
769                                  (u16) map_port0);
770             }
771
772           //It is important to cache at this stage because the result might be necessary
773           //for packets within the same vector.
774           //Actually, this approach even provides some limited out-of-order fragments support
775           if (PREDICT_FALSE
776               (ip4_is_first_fragment (ip40) && (map_port0 != -1)
777                && (d0->ea_bits_len != 0 || !d0->rules)
778                && ip4_map_fragment_cache (ip40, map_port0)))
779             {
780               error0 = MAP_ERROR_UNKNOWN;
781             }
782
783           if (PREDICT_TRUE
784               (error0 == MAP_ERROR_NONE && next0 != IP4_MAPT_NEXT_MAPT_ICMP))
785             {
786               vlib_increment_combined_counter (cm + MAP_DOMAIN_COUNTER_TX,
787                                                thread_index,
788                                                vnet_buffer (p0)->
789                                                map_t.map_domain_index, 1,
790                                                clib_net_to_host_u16
791                                                (ip40->length));
792             }
793
794           next0 = (error0 != MAP_ERROR_NONE) ? IP4_MAPT_NEXT_DROP : next0;
795           p0->error = error_node->errors[error0];
796           vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
797                                            to_next, n_left_to_next, pi0,
798                                            next0);
799         }
800       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
801     }
802   return frame->n_vectors;
803 }
804
805 static char *map_t_error_strings[] = {
806 #define _(sym,string) string,
807   foreach_map_error
808 #undef _
809 };
810
811 /* *INDENT-OFF* */
812 VLIB_REGISTER_NODE(ip4_map_t_fragmented_node) = {
813   .function = ip4_map_t_fragmented,
814   .name = "ip4-map-t-fragmented",
815   .vector_size = sizeof(u32),
816   .format_trace = format_map_trace,
817   .type = VLIB_NODE_TYPE_INTERNAL,
818
819   .n_errors = MAP_N_ERROR,
820   .error_strings = map_t_error_strings,
821
822   .n_next_nodes = IP4_MAPT_FRAGMENTED_N_NEXT,
823   .next_nodes = {
824       [IP4_MAPT_FRAGMENTED_NEXT_IP6_LOOKUP] = "ip6-lookup",
825       [IP4_MAPT_FRAGMENTED_NEXT_IP6_FRAG] = IP6_FRAG_NODE_NAME,
826       [IP4_MAPT_FRAGMENTED_NEXT_DROP] = "error-drop",
827   },
828 };
829 /* *INDENT-ON* */
830
831 /* *INDENT-OFF* */
832 VLIB_REGISTER_NODE(ip4_map_t_icmp_node) = {
833   .function = ip4_map_t_icmp,
834   .name = "ip4-map-t-icmp",
835   .vector_size = sizeof(u32),
836   .format_trace = format_map_trace,
837   .type = VLIB_NODE_TYPE_INTERNAL,
838
839   .n_errors = MAP_N_ERROR,
840   .error_strings = map_t_error_strings,
841
842   .n_next_nodes = IP4_MAPT_ICMP_N_NEXT,
843   .next_nodes = {
844       [IP4_MAPT_ICMP_NEXT_IP6_LOOKUP] = "ip6-lookup",
845       [IP4_MAPT_ICMP_NEXT_IP6_FRAG] = IP6_FRAG_NODE_NAME,
846       [IP4_MAPT_ICMP_NEXT_DROP] = "error-drop",
847   },
848 };
849 /* *INDENT-ON* */
850
851 /* *INDENT-OFF* */
852 VLIB_REGISTER_NODE(ip4_map_t_tcp_udp_node) = {
853   .function = ip4_map_t_tcp_udp,
854   .name = "ip4-map-t-tcp-udp",
855   .vector_size = sizeof(u32),
856   .format_trace = format_map_trace,
857   .type = VLIB_NODE_TYPE_INTERNAL,
858
859   .n_errors = MAP_N_ERROR,
860   .error_strings = map_t_error_strings,
861
862   .n_next_nodes = IP4_MAPT_TCP_UDP_N_NEXT,
863   .next_nodes = {
864       [IP4_MAPT_TCP_UDP_NEXT_IP6_LOOKUP] = "ip6-lookup",
865       [IP4_MAPT_TCP_UDP_NEXT_IP6_FRAG] = IP6_FRAG_NODE_NAME,
866       [IP4_MAPT_TCP_UDP_NEXT_DROP] = "error-drop",
867   },
868 };
869 /* *INDENT-ON* */
870
871 /* *INDENT-OFF* */
872 VLIB_REGISTER_NODE(ip4_map_t_node) = {
873   .function = ip4_map_t,
874   .name = "ip4-map-t",
875   .vector_size = sizeof(u32),
876   .format_trace = format_map_trace,
877   .type = VLIB_NODE_TYPE_INTERNAL,
878
879   .n_errors = MAP_N_ERROR,
880   .error_strings = map_t_error_strings,
881
882   .n_next_nodes = IP4_MAPT_N_NEXT,
883   .next_nodes = {
884       [IP4_MAPT_NEXT_MAPT_TCP_UDP] = "ip4-map-t-tcp-udp",
885       [IP4_MAPT_NEXT_MAPT_ICMP] = "ip4-map-t-icmp",
886       [IP4_MAPT_NEXT_MAPT_FRAGMENTED] = "ip4-map-t-fragmented",
887       [IP4_MAPT_NEXT_DROP] = "error-drop",
888   },
889 };
890 /* *INDENT-ON* */
891
892 /*
893  * fd.io coding-style-patch-verification: ON
894  *
895  * Local Variables:
896  * eval: (c-set-style "gnu")
897  * End:
898  */