IPIP and IPv6 fragmentation
[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 = vm->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.mtu = vnet_buffer (p0)->map_t.mtu;
241               vnet_buffer (p0)->ip_frag.next_index = IP6_FRAG_NEXT_IP6_LOOKUP;
242               next0 = IP4_MAPT_ICMP_NEXT_IP6_FRAG;
243             }
244         err0:
245           if (PREDICT_TRUE (error0 == MAP_ERROR_NONE))
246             {
247               vlib_increment_combined_counter (cm + MAP_DOMAIN_COUNTER_TX,
248                                                thread_index,
249                                                vnet_buffer (p0)->
250                                                map_t.map_domain_index, 1,
251                                                len0);
252             }
253           else
254             {
255               next0 = IP4_MAPT_ICMP_NEXT_DROP;
256             }
257           p0->error = error_node->errors[error0];
258           vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
259                                            to_next, n_left_to_next, pi0,
260                                            next0);
261         }
262       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
263     }
264   return frame->n_vectors;
265 }
266
267 static int
268 ip4_to_ip6_set_cb (ip4_header_t * ip4, ip6_header_t * ip6, void *ctx)
269 {
270   ip4_mapt_pseudo_header_t *pheader = ctx;
271
272   ip6->dst_address.as_u64[0] = pheader->daddr.as_u64[0];
273   ip6->dst_address.as_u64[1] = pheader->daddr.as_u64[1];
274   ip6->src_address.as_u64[0] = pheader->saddr.as_u64[0];
275   ip6->src_address.as_u64[1] = pheader->saddr.as_u64[1];
276
277   return 0;
278 }
279
280 static uword
281 ip4_map_t_fragmented (vlib_main_t * vm,
282                       vlib_node_runtime_t * node, vlib_frame_t * frame)
283 {
284   u32 n_left_from, *from, next_index, *to_next, n_left_to_next;
285   from = vlib_frame_vector_args (frame);
286   n_left_from = frame->n_vectors;
287   next_index = node->cached_next_index;
288   vlib_node_runtime_t *error_node =
289     vlib_node_get_runtime (vm, ip4_map_t_fragmented_node.index);
290
291   while (n_left_from > 0)
292     {
293       vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
294
295       while (n_left_from > 0 && n_left_to_next > 0)
296         {
297           u32 pi0;
298           vlib_buffer_t *p0;
299           ip4_mapt_pseudo_header_t *pheader0;
300           ip4_mapt_fragmented_next_t next0;
301
302           next0 = IP4_MAPT_FRAGMENTED_NEXT_IP6_LOOKUP;
303           pi0 = to_next[0] = from[0];
304           from += 1;
305           n_left_from -= 1;
306           to_next += 1;
307           n_left_to_next -= 1;
308
309           p0 = vlib_get_buffer (vm, pi0);
310
311           //Accessing pseudo header
312           pheader0 = vlib_buffer_get_current (p0);
313           vlib_buffer_advance (p0, sizeof (*pheader0));
314
315           if (ip4_to_ip6_fragmented (p0, ip4_to_ip6_set_cb, pheader0))
316             {
317               p0->error = error_node->errors[MAP_ERROR_FRAGMENT_DROPPED];
318               next0 = IP4_MAPT_FRAGMENTED_NEXT_DROP;
319             }
320           else
321             {
322               if (vnet_buffer (p0)->map_t.mtu < p0->current_length)
323                 {
324                   vnet_buffer (p0)->ip_frag.mtu = vnet_buffer (p0)->map_t.mtu;
325                   vnet_buffer (p0)->ip_frag.next_index =
326                     IP6_FRAG_NEXT_IP6_LOOKUP;
327                   next0 = IP4_MAPT_FRAGMENTED_NEXT_IP6_FRAG;
328                 }
329             }
330
331           vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
332                                            to_next, n_left_to_next, pi0,
333                                            next0);
334         }
335       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
336     }
337   return frame->n_vectors;
338 }
339
340 static uword
341 ip4_map_t_tcp_udp (vlib_main_t * vm,
342                    vlib_node_runtime_t * node, vlib_frame_t * frame)
343 {
344   u32 n_left_from, *from, next_index, *to_next, n_left_to_next;
345   from = vlib_frame_vector_args (frame);
346   n_left_from = frame->n_vectors;
347   next_index = node->cached_next_index;
348   vlib_node_runtime_t *error_node =
349     vlib_node_get_runtime (vm, ip4_map_t_tcp_udp_node.index);
350
351
352   while (n_left_from > 0)
353     {
354       vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
355
356 #ifdef IP4_MAP_T_DUAL_LOOP
357       while (n_left_from >= 4 && n_left_to_next >= 2)
358         {
359           u32 pi0, pi1;
360           vlib_buffer_t *p0, *p1;
361           ip4_mapt_pseudo_header_t *pheader0, *pheader1;
362           ip4_mapt_tcp_udp_next_t next0, next1;
363
364           pi0 = to_next[0] = from[0];
365           pi1 = to_next[1] = from[1];
366           from += 2;
367           n_left_from -= 2;
368           to_next += 2;
369           n_left_to_next -= 2;
370
371           next0 = IP4_MAPT_TCP_UDP_NEXT_IP6_LOOKUP;
372           next1 = IP4_MAPT_TCP_UDP_NEXT_IP6_LOOKUP;
373           p0 = vlib_get_buffer (vm, pi0);
374           p1 = vlib_get_buffer (vm, pi1);
375
376           //Accessing pseudo header
377           pheader0 = vlib_buffer_get_current (p0);
378           pheader1 = vlib_buffer_get_current (p1);
379           vlib_buffer_advance (p0, sizeof (*pheader0));
380           vlib_buffer_advance (p1, sizeof (*pheader1));
381
382           if (ip4_to_ip6_tcp_udp (p0, ip4_to_ip6_set_cb, pheader0))
383             {
384               p0->error = error_node->errors[MAP_ERROR_UNKNOWN];
385               next0 = IP4_MAPT_TCP_UDP_NEXT_DROP;
386             }
387           else
388             {
389               if (vnet_buffer (p0)->map_t.mtu < p0->current_length)
390                 {
391                   //Send to fragmentation node if necessary
392                   vnet_buffer (p0)->ip_frag.mtu = vnet_buffer (p0)->map_t.mtu;
393                   vnet_buffer (p0)->ip_frag.next_index =
394                     IP6_FRAG_NEXT_IP6_LOOKUP;
395                   next0 = IP4_MAPT_TCP_UDP_NEXT_IP6_FRAG;
396                 }
397             }
398
399           if (ip4_to_ip6_tcp_udp (p1, ip4_to_ip6_set_cb, pheader1))
400             {
401               p1->error = error_node->errors[MAP_ERROR_UNKNOWN];
402               next1 = IP4_MAPT_TCP_UDP_NEXT_DROP;
403             }
404           else
405             {
406               if (vnet_buffer (p1)->map_t.mtu < p1->current_length)
407                 {
408                   //Send to fragmentation node if necessary
409                   vnet_buffer (p1)->ip_frag.mtu = vnet_buffer (p1)->map_t.mtu;
410                   vnet_buffer (p1)->ip_frag.next_index =
411                     IP6_FRAG_NEXT_IP6_LOOKUP;
412                   next1 = IP4_MAPT_TCP_UDP_NEXT_IP6_FRAG;
413                 }
414             }
415
416           vlib_validate_buffer_enqueue_x2 (vm, node, next_index,
417                                            to_next, n_left_to_next, pi0, pi1,
418                                            next0, next1);
419         }
420 #endif
421
422       while (n_left_from > 0 && n_left_to_next > 0)
423         {
424           u32 pi0;
425           vlib_buffer_t *p0;
426           ip4_mapt_pseudo_header_t *pheader0;
427           ip4_mapt_tcp_udp_next_t next0;
428
429           pi0 = to_next[0] = from[0];
430           from += 1;
431           n_left_from -= 1;
432           to_next += 1;
433           n_left_to_next -= 1;
434
435           next0 = IP4_MAPT_TCP_UDP_NEXT_IP6_LOOKUP;
436           p0 = vlib_get_buffer (vm, pi0);
437
438           //Accessing pseudo header
439           pheader0 = vlib_buffer_get_current (p0);
440           vlib_buffer_advance (p0, sizeof (*pheader0));
441
442           if (ip4_to_ip6_tcp_udp (p0, ip4_to_ip6_set_cb, pheader0))
443             {
444               p0->error = error_node->errors[MAP_ERROR_UNKNOWN];
445               next0 = IP4_MAPT_TCP_UDP_NEXT_DROP;
446             }
447           else
448             {
449               if (vnet_buffer (p0)->map_t.mtu < p0->current_length)
450                 {
451                   //Send to fragmentation node if necessary
452                   vnet_buffer (p0)->ip_frag.mtu = vnet_buffer (p0)->map_t.mtu;
453                   vnet_buffer (p0)->ip_frag.next_index =
454                     IP6_FRAG_NEXT_IP6_LOOKUP;
455                   next0 = IP4_MAPT_TCP_UDP_NEXT_IP6_FRAG;
456                 }
457             }
458           vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
459                                            to_next, n_left_to_next, pi0,
460                                            next0);
461         }
462       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
463     }
464
465   return frame->n_vectors;
466 }
467
468 static_always_inline void
469 ip4_map_t_classify (vlib_buffer_t * p0, map_domain_t * d0,
470                     ip4_header_t * ip40, u16 ip4_len0, i32 * dst_port0,
471                     u8 * error0, ip4_mapt_next_t * next0)
472 {
473   map_main_t *mm = &map_main;
474   u32 port_offset;
475
476   if (mm->is_ce)
477     port_offset = 0;
478   else
479     port_offset = 2;
480
481   if (PREDICT_FALSE (ip4_get_fragment_offset (ip40)))
482     {
483       *next0 = IP4_MAPT_NEXT_MAPT_FRAGMENTED;
484       if (d0->ea_bits_len == 0 && d0->rules)
485         {
486           *dst_port0 = 0;
487         }
488       else
489         {
490           *dst_port0 = ip4_map_fragment_get_port (ip40);
491           *error0 = (*dst_port0 == -1) ? MAP_ERROR_FRAGMENT_MEMORY : *error0;
492         }
493     }
494   else if (PREDICT_TRUE (ip40->protocol == IP_PROTOCOL_TCP))
495     {
496       vnet_buffer (p0)->map_t.checksum_offset = 36;
497       *next0 = IP4_MAPT_NEXT_MAPT_TCP_UDP;
498       *error0 = ip4_len0 < 40 ? MAP_ERROR_MALFORMED : *error0;
499       *dst_port0 =
500         (i32) * ((u16 *) u8_ptr_add (ip40, sizeof (*ip40) + port_offset));
501     }
502   else if (PREDICT_TRUE (ip40->protocol == IP_PROTOCOL_UDP))
503     {
504       vnet_buffer (p0)->map_t.checksum_offset = 26;
505       *next0 = IP4_MAPT_NEXT_MAPT_TCP_UDP;
506       *error0 = ip4_len0 < 28 ? MAP_ERROR_MALFORMED : *error0;
507       *dst_port0 =
508         (i32) * ((u16 *) u8_ptr_add (ip40, sizeof (*ip40) + port_offset));
509     }
510   else if (ip40->protocol == IP_PROTOCOL_ICMP)
511     {
512       *next0 = IP4_MAPT_NEXT_MAPT_ICMP;
513       if (d0->ea_bits_len == 0 && d0->rules)
514         *dst_port0 = 0;
515       else if (((icmp46_header_t *) u8_ptr_add (ip40, sizeof (*ip40)))->code
516                == ICMP4_echo_reply
517                || ((icmp46_header_t *)
518                    u8_ptr_add (ip40,
519                                sizeof (*ip40)))->code == ICMP4_echo_request)
520         *dst_port0 = (i32) * ((u16 *) u8_ptr_add (ip40, sizeof (*ip40) + 6));
521     }
522   else
523     {
524       *error0 = MAP_ERROR_BAD_PROTOCOL;
525     }
526 }
527
528 static uword
529 ip4_map_t (vlib_main_t * vm, vlib_node_runtime_t * node, vlib_frame_t * frame)
530 {
531   u32 n_left_from, *from, next_index, *to_next, n_left_to_next;
532   vlib_node_runtime_t *error_node =
533     vlib_node_get_runtime (vm, ip4_map_t_node.index);
534   from = vlib_frame_vector_args (frame);
535   n_left_from = frame->n_vectors;
536   next_index = node->cached_next_index;
537   map_main_t *mm = &map_main;
538   vlib_combined_counter_main_t *cm = map_main.domain_counters;
539   u32 thread_index = vm->thread_index;
540
541   while (n_left_from > 0)
542     {
543       vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
544
545 #ifdef IP4_MAP_T_DUAL_LOOP
546       while (n_left_from >= 4 && n_left_to_next >= 2)
547         {
548           u32 pi0, pi1;
549           vlib_buffer_t *p0, *p1;
550           ip4_header_t *ip40, *ip41;
551           map_domain_t *d0, *d1;
552           ip4_mapt_next_t next0 = 0, next1 = 0;
553           u16 ip4_len0, ip4_len1;
554           u8 error0, error1;
555           i32 map_port0, map_port1;
556           ip4_mapt_pseudo_header_t *pheader0, *pheader1;
557
558           pi0 = to_next[0] = from[0];
559           pi1 = to_next[1] = from[1];
560           from += 2;
561           n_left_from -= 2;
562           to_next += 2;
563           n_left_to_next -= 2;
564           error0 = MAP_ERROR_NONE;
565           error1 = MAP_ERROR_NONE;
566
567           p0 = vlib_get_buffer (vm, pi0);
568           p1 = vlib_get_buffer (vm, pi1);
569           ip40 = vlib_buffer_get_current (p0);
570           ip41 = vlib_buffer_get_current (p1);
571           ip4_len0 = clib_host_to_net_u16 (ip40->length);
572           ip4_len1 = clib_host_to_net_u16 (ip41->length);
573
574           if (PREDICT_FALSE (p0->current_length < ip4_len0 ||
575                              ip40->ip_version_and_header_length != 0x45))
576             {
577               error0 = MAP_ERROR_UNKNOWN;
578               next0 = IP4_MAPT_NEXT_DROP;
579             }
580
581           if (PREDICT_FALSE (p1->current_length < ip4_len1 ||
582                              ip41->ip_version_and_header_length != 0x45))
583             {
584               error1 = MAP_ERROR_UNKNOWN;
585               next1 = IP4_MAPT_NEXT_DROP;
586             }
587
588           vnet_buffer (p0)->map_t.map_domain_index =
589             vnet_buffer (p0)->ip.adj_index[VLIB_TX];
590           d0 = ip4_map_get_domain (vnet_buffer (p0)->map_t.map_domain_index);
591           vnet_buffer (p1)->map_t.map_domain_index =
592             vnet_buffer (p1)->ip.adj_index[VLIB_TX];
593           d1 = ip4_map_get_domain (vnet_buffer (p1)->map_t.map_domain_index);
594
595           vnet_buffer (p0)->map_t.mtu = d0->mtu ? d0->mtu : ~0;
596           vnet_buffer (p1)->map_t.mtu = d1->mtu ? d1->mtu : ~0;
597
598           map_port0 = -1;
599           map_port1 = -1;
600
601           ip4_map_t_classify (p0, d0, ip40, ip4_len0, &map_port0, &error0,
602                               &next0);
603           ip4_map_t_classify (p1, d1, ip41, ip4_len1, &map_port1, &error1,
604                               &next1);
605
606           //Add MAP-T pseudo header in front of the packet
607           vlib_buffer_advance (p0, -sizeof (*pheader0));
608           vlib_buffer_advance (p1, -sizeof (*pheader1));
609           pheader0 = vlib_buffer_get_current (p0);
610           pheader1 = vlib_buffer_get_current (p1);
611
612           //Save addresses within the packet
613           if (mm->is_ce)
614             {
615               ip4_map_t_embedded_address (d0, &pheader0->daddr,
616                                           &ip40->dst_address);
617               ip4_map_t_embedded_address (d1, &pheader1->daddr,
618                                           &ip41->dst_address);
619               pheader0->saddr.as_u64[0] =
620                 map_get_pfx_net (d0, ip40->src_address.as_u32,
621                                  (u16) map_port0);
622               pheader0->saddr.as_u64[1] =
623                 map_get_sfx_net (d0, ip40->src_address.as_u32,
624                                  (u16) map_port0);
625               pheader1->saddr.as_u64[0] =
626                 map_get_pfx_net (d1, ip41->src_address.as_u32,
627                                  (u16) map_port1);
628               pheader1->saddr.as_u64[1] =
629                 map_get_sfx_net (d1, ip41->src_address.as_u32,
630                                  (u16) map_port1);
631             }
632           else
633             {
634               ip4_map_t_embedded_address (d0, &pheader0->saddr,
635                                           &ip40->src_address);
636               ip4_map_t_embedded_address (d1, &pheader1->saddr,
637                                           &ip41->src_address);
638               pheader0->daddr.as_u64[0] =
639                 map_get_pfx_net (d0, ip40->dst_address.as_u32,
640                                  (u16) map_port0);
641               pheader0->daddr.as_u64[1] =
642                 map_get_sfx_net (d0, ip40->dst_address.as_u32,
643                                  (u16) map_port0);
644               pheader1->daddr.as_u64[0] =
645                 map_get_pfx_net (d1, ip41->dst_address.as_u32,
646                                  (u16) map_port1);
647               pheader1->daddr.as_u64[1] =
648                 map_get_sfx_net (d1, ip41->dst_address.as_u32,
649                                  (u16) map_port1);
650             }
651
652           if (PREDICT_FALSE
653               (ip4_is_first_fragment (ip40) && (map_port0 != -1)
654                && (d0->ea_bits_len != 0 || !d0->rules)
655                && ip4_map_fragment_cache (ip40, map_port0)))
656             {
657               error0 = MAP_ERROR_FRAGMENT_MEMORY;
658             }
659
660           if (PREDICT_FALSE
661               (ip4_is_first_fragment (ip41) && (map_port1 != -1)
662                && (d1->ea_bits_len != 0 || !d1->rules)
663                && ip4_map_fragment_cache (ip41, map_port1)))
664             {
665               error1 = MAP_ERROR_FRAGMENT_MEMORY;
666             }
667
668           if (PREDICT_TRUE
669               (error0 == MAP_ERROR_NONE && next0 != IP4_MAPT_NEXT_MAPT_ICMP))
670             {
671               vlib_increment_combined_counter (cm + MAP_DOMAIN_COUNTER_TX,
672                                                thread_index,
673                                                vnet_buffer (p0)->
674                                                map_t.map_domain_index, 1,
675                                                clib_net_to_host_u16
676                                                (ip40->length));
677             }
678
679           if (PREDICT_TRUE
680               (error1 == MAP_ERROR_NONE && next1 != IP4_MAPT_NEXT_MAPT_ICMP))
681             {
682               vlib_increment_combined_counter (cm + MAP_DOMAIN_COUNTER_TX,
683                                                thread_index,
684                                                vnet_buffer (p1)->
685                                                map_t.map_domain_index, 1,
686                                                clib_net_to_host_u16
687                                                (ip41->length));
688             }
689
690           next0 = (error0 != MAP_ERROR_NONE) ? IP4_MAPT_NEXT_DROP : next0;
691           next1 = (error1 != MAP_ERROR_NONE) ? IP4_MAPT_NEXT_DROP : next1;
692           p0->error = error_node->errors[error0];
693           p1->error = error_node->errors[error1];
694           vlib_validate_buffer_enqueue_x2 (vm, node, next_index, to_next,
695                                            n_left_to_next, pi0, pi1, next0,
696                                            next1);
697         }
698 #endif
699
700       while (n_left_from > 0 && n_left_to_next > 0)
701         {
702           u32 pi0;
703           vlib_buffer_t *p0;
704           ip4_header_t *ip40;
705           map_domain_t *d0;
706           ip4_mapt_next_t next0;
707           u16 ip4_len0;
708           u8 error0;
709           i32 map_port0;
710           ip4_mapt_pseudo_header_t *pheader0;
711
712           pi0 = to_next[0] = from[0];
713           from += 1;
714           n_left_from -= 1;
715           to_next += 1;
716           n_left_to_next -= 1;
717           error0 = MAP_ERROR_NONE;
718
719           p0 = vlib_get_buffer (vm, pi0);
720           ip40 = vlib_buffer_get_current (p0);
721           ip4_len0 = clib_host_to_net_u16 (ip40->length);
722           if (PREDICT_FALSE (p0->current_length < ip4_len0 ||
723                              ip40->ip_version_and_header_length != 0x45))
724             {
725               error0 = MAP_ERROR_UNKNOWN;
726               next0 = IP4_MAPT_NEXT_DROP;
727             }
728
729           vnet_buffer (p0)->map_t.map_domain_index =
730             vnet_buffer (p0)->ip.adj_index[VLIB_TX];
731           d0 = ip4_map_get_domain (vnet_buffer (p0)->map_t.map_domain_index);
732
733           vnet_buffer (p0)->map_t.mtu = d0->mtu ? d0->mtu : ~0;
734
735           map_port0 = -1;
736           ip4_map_t_classify (p0, d0, ip40, ip4_len0, &map_port0, &error0,
737                               &next0);
738
739           //Add MAP-T pseudo header in front of the packet
740           vlib_buffer_advance (p0, -sizeof (*pheader0));
741           pheader0 = vlib_buffer_get_current (p0);
742
743           //Save addresses within the packet
744           if (mm->is_ce)
745             {
746               ip4_map_t_embedded_address (d0, &pheader0->daddr,
747                                           &ip40->dst_address);
748               pheader0->saddr.as_u64[0] =
749                 map_get_pfx_net (d0, ip40->src_address.as_u32,
750                                  (u16) map_port0);
751               pheader0->saddr.as_u64[1] =
752                 map_get_sfx_net (d0, ip40->src_address.as_u32,
753                                  (u16) map_port0);
754             }
755           else
756             {
757               ip4_map_t_embedded_address (d0, &pheader0->saddr,
758                                           &ip40->src_address);
759               pheader0->daddr.as_u64[0] =
760                 map_get_pfx_net (d0, ip40->dst_address.as_u32,
761                                  (u16) map_port0);
762               pheader0->daddr.as_u64[1] =
763                 map_get_sfx_net (d0, ip40->dst_address.as_u32,
764                                  (u16) map_port0);
765             }
766
767           //It is important to cache at this stage because the result might be necessary
768           //for packets within the same vector.
769           //Actually, this approach even provides some limited out-of-order fragments support
770           if (PREDICT_FALSE
771               (ip4_is_first_fragment (ip40) && (map_port0 != -1)
772                && (d0->ea_bits_len != 0 || !d0->rules)
773                && ip4_map_fragment_cache (ip40, map_port0)))
774             {
775               error0 = MAP_ERROR_UNKNOWN;
776             }
777
778           if (PREDICT_TRUE
779               (error0 == MAP_ERROR_NONE && next0 != IP4_MAPT_NEXT_MAPT_ICMP))
780             {
781               vlib_increment_combined_counter (cm + MAP_DOMAIN_COUNTER_TX,
782                                                thread_index,
783                                                vnet_buffer (p0)->
784                                                map_t.map_domain_index, 1,
785                                                clib_net_to_host_u16
786                                                (ip40->length));
787             }
788
789           next0 = (error0 != MAP_ERROR_NONE) ? IP4_MAPT_NEXT_DROP : next0;
790           p0->error = error_node->errors[error0];
791           vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
792                                            to_next, n_left_to_next, pi0,
793                                            next0);
794         }
795       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
796     }
797   return frame->n_vectors;
798 }
799
800 static char *map_t_error_strings[] = {
801 #define _(sym,string) string,
802   foreach_map_error
803 #undef _
804 };
805
806 /* *INDENT-OFF* */
807 VLIB_REGISTER_NODE(ip4_map_t_fragmented_node) = {
808   .function = ip4_map_t_fragmented,
809   .name = "ip4-map-t-fragmented",
810   .vector_size = sizeof(u32),
811   .format_trace = format_map_trace,
812   .type = VLIB_NODE_TYPE_INTERNAL,
813
814   .n_errors = MAP_N_ERROR,
815   .error_strings = map_t_error_strings,
816
817   .n_next_nodes = IP4_MAPT_FRAGMENTED_N_NEXT,
818   .next_nodes = {
819       [IP4_MAPT_FRAGMENTED_NEXT_IP6_LOOKUP] = "ip6-lookup",
820       [IP4_MAPT_FRAGMENTED_NEXT_IP6_FRAG] = IP6_FRAG_NODE_NAME,
821       [IP4_MAPT_FRAGMENTED_NEXT_DROP] = "error-drop",
822   },
823 };
824 /* *INDENT-ON* */
825
826 /* *INDENT-OFF* */
827 VLIB_REGISTER_NODE(ip4_map_t_icmp_node) = {
828   .function = ip4_map_t_icmp,
829   .name = "ip4-map-t-icmp",
830   .vector_size = sizeof(u32),
831   .format_trace = format_map_trace,
832   .type = VLIB_NODE_TYPE_INTERNAL,
833
834   .n_errors = MAP_N_ERROR,
835   .error_strings = map_t_error_strings,
836
837   .n_next_nodes = IP4_MAPT_ICMP_N_NEXT,
838   .next_nodes = {
839       [IP4_MAPT_ICMP_NEXT_IP6_LOOKUP] = "ip6-lookup",
840       [IP4_MAPT_ICMP_NEXT_IP6_FRAG] = IP6_FRAG_NODE_NAME,
841       [IP4_MAPT_ICMP_NEXT_DROP] = "error-drop",
842   },
843 };
844 /* *INDENT-ON* */
845
846 /* *INDENT-OFF* */
847 VLIB_REGISTER_NODE(ip4_map_t_tcp_udp_node) = {
848   .function = ip4_map_t_tcp_udp,
849   .name = "ip4-map-t-tcp-udp",
850   .vector_size = sizeof(u32),
851   .format_trace = format_map_trace,
852   .type = VLIB_NODE_TYPE_INTERNAL,
853
854   .n_errors = MAP_N_ERROR,
855   .error_strings = map_t_error_strings,
856
857   .n_next_nodes = IP4_MAPT_TCP_UDP_N_NEXT,
858   .next_nodes = {
859       [IP4_MAPT_TCP_UDP_NEXT_IP6_LOOKUP] = "ip6-lookup",
860       [IP4_MAPT_TCP_UDP_NEXT_IP6_FRAG] = IP6_FRAG_NODE_NAME,
861       [IP4_MAPT_TCP_UDP_NEXT_DROP] = "error-drop",
862   },
863 };
864 /* *INDENT-ON* */
865
866 /* *INDENT-OFF* */
867 VLIB_REGISTER_NODE(ip4_map_t_node) = {
868   .function = ip4_map_t,
869   .name = "ip4-map-t",
870   .vector_size = sizeof(u32),
871   .format_trace = format_map_trace,
872   .type = VLIB_NODE_TYPE_INTERNAL,
873
874   .n_errors = MAP_N_ERROR,
875   .error_strings = map_t_error_strings,
876
877   .n_next_nodes = IP4_MAPT_N_NEXT,
878   .next_nodes = {
879       [IP4_MAPT_NEXT_MAPT_TCP_UDP] = "ip4-map-t-tcp-udp",
880       [IP4_MAPT_NEXT_MAPT_ICMP] = "ip4-map-t-icmp",
881       [IP4_MAPT_NEXT_MAPT_FRAGMENTED] = "ip4-map-t-fragmented",
882       [IP4_MAPT_NEXT_DROP] = "error-drop",
883   },
884 };
885 /* *INDENT-ON* */
886
887 /*
888  * fd.io coding-style-patch-verification: ON
889  *
890  * Local Variables:
891  * eval: (c-set-style "gnu")
892  * End:
893  */