nat: use SVR
[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 typedef enum
21 {
22   IP4_MAPT_NEXT_MAPT_TCP_UDP,
23   IP4_MAPT_NEXT_MAPT_ICMP,
24   IP4_MAPT_NEXT_MAPT_FRAGMENTED,
25   IP4_MAPT_NEXT_DROP,
26   IP4_MAPT_N_NEXT
27 } ip4_mapt_next_t;
28
29 typedef enum
30 {
31   IP4_MAPT_ICMP_NEXT_IP6_LOOKUP,
32   IP4_MAPT_ICMP_NEXT_IP6_FRAG,
33   IP4_MAPT_ICMP_NEXT_DROP,
34   IP4_MAPT_ICMP_N_NEXT
35 } ip4_mapt_icmp_next_t;
36
37 typedef enum
38 {
39   IP4_MAPT_TCP_UDP_NEXT_IP6_LOOKUP,
40   IP4_MAPT_TCP_UDP_NEXT_IP6_FRAG,
41   IP4_MAPT_TCP_UDP_NEXT_DROP,
42   IP4_MAPT_TCP_UDP_N_NEXT
43 } ip4_mapt_tcp_udp_next_t;
44
45 typedef enum
46 {
47   IP4_MAPT_FRAGMENTED_NEXT_IP6_LOOKUP,
48   IP4_MAPT_FRAGMENTED_NEXT_IP6_FRAG,
49   IP4_MAPT_FRAGMENTED_NEXT_DROP,
50   IP4_MAPT_FRAGMENTED_N_NEXT
51 } ip4_mapt_fragmented_next_t;
52
53 //This is used to pass information within the buffer data.
54 //Buffer structure being too small to contain big structures like this.
55 /* *INDENT-OFF* */
56 typedef CLIB_PACKED (struct {
57   ip6_address_t daddr;
58   ip6_address_t saddr;
59   //IPv6 header + Fragmentation header will be here
60   //sizeof(ip6) + sizeof(ip_frag) - sizeof(ip4)
61   u8 unused[28];
62 }) ip4_mapt_pseudo_header_t;
63 /* *INDENT-ON* */
64
65 typedef struct
66 {
67   map_domain_t *d;
68   u16 recv_port;
69 } icmp_to_icmp6_ctx_t;
70
71 static int
72 ip4_to_ip6_set_icmp_cb (vlib_buffer_t * b, ip4_header_t * ip4,
73                         ip6_header_t * ip6, void *arg)
74 {
75   icmp_to_icmp6_ctx_t *ctx = arg;
76
77   ip4_map_t_embedded_address (ctx->d, &ip6->src_address, &ip4->src_address);
78   ip6->dst_address.as_u64[0] =
79     map_get_pfx_net (ctx->d, ip4->dst_address.as_u32, ctx->recv_port);
80   ip6->dst_address.as_u64[1] =
81     map_get_sfx_net (ctx->d, ip4->dst_address.as_u32, ctx->recv_port);
82
83   return 0;
84 }
85
86 static int
87 ip4_to_ip6_set_inner_icmp_cb (vlib_buffer_t * b, ip4_header_t * ip4,
88                               ip6_header_t * ip6, void *arg)
89 {
90   icmp_to_icmp6_ctx_t *ctx = arg;
91
92   //Note that the source address is within the domain
93   //while the destination address is the one outside the domain
94   ip4_map_t_embedded_address (ctx->d, &ip6->dst_address, &ip4->dst_address);
95   ip6->src_address.as_u64[0] =
96     map_get_pfx_net (ctx->d, ip4->src_address.as_u32, ctx->recv_port);
97   ip6->src_address.as_u64[1] =
98     map_get_sfx_net (ctx->d, ip4->src_address.as_u32, ctx->recv_port);
99
100   return 0;
101 }
102
103 static uword
104 ip4_map_t_icmp (vlib_main_t * vm,
105                 vlib_node_runtime_t * node, vlib_frame_t * frame)
106 {
107   u32 n_left_from, *from, next_index, *to_next, n_left_to_next;
108   vlib_node_runtime_t *error_node =
109     vlib_node_get_runtime (vm, ip4_map_t_icmp_node.index);
110   from = vlib_frame_vector_args (frame);
111   n_left_from = frame->n_vectors;
112   next_index = node->cached_next_index;
113   vlib_combined_counter_main_t *cm = map_main.domain_counters;
114   u32 thread_index = vm->thread_index;
115
116   while (n_left_from > 0)
117     {
118       vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
119
120       while (n_left_from > 0 && n_left_to_next > 0)
121         {
122           u32 pi0;
123           vlib_buffer_t *p0;
124           ip4_mapt_icmp_next_t next0;
125           u8 error0;
126           map_domain_t *d0;
127           u16 len0;
128           icmp_to_icmp6_ctx_t ctx0;
129           ip4_header_t *ip40;
130
131           next0 = IP4_MAPT_ICMP_NEXT_IP6_LOOKUP;
132           pi0 = to_next[0] = from[0];
133           from += 1;
134           n_left_from -= 1;
135           to_next += 1;
136           n_left_to_next -= 1;
137           error0 = MAP_ERROR_NONE;
138
139           p0 = vlib_get_buffer (vm, pi0);
140           vlib_buffer_advance (p0, sizeof (ip4_mapt_pseudo_header_t));  //The pseudo-header is not used
141           len0 =
142             clib_net_to_host_u16 (((ip4_header_t *)
143                                    vlib_buffer_get_current (p0))->length);
144           d0 =
145             pool_elt_at_index (map_main.domains,
146                                vnet_buffer (p0)->map_t.map_domain_index);
147
148           ip40 = vlib_buffer_get_current (p0);
149           ctx0.recv_port = ip4_get_port (ip40, 1);
150           ctx0.d = d0;
151           if (ctx0.recv_port == 0)
152             {
153               // In case of 1:1 mapping, we don't care about the port
154               if (!(d0->ea_bits_len == 0 && d0->rules))
155                 {
156                   error0 = MAP_ERROR_ICMP;
157                   goto err0;
158                 }
159             }
160
161           if (icmp_to_icmp6
162               (p0, ip4_to_ip6_set_icmp_cb, &ctx0,
163                ip4_to_ip6_set_inner_icmp_cb, &ctx0))
164             {
165               error0 = MAP_ERROR_ICMP;
166               goto err0;
167             }
168
169           if (vnet_buffer (p0)->map_t.mtu < p0->current_length)
170             {
171               vnet_buffer (p0)->ip_frag.mtu = vnet_buffer (p0)->map_t.mtu;
172               vnet_buffer (p0)->ip_frag.next_index = IP_FRAG_NEXT_IP6_LOOKUP;
173               next0 = IP4_MAPT_ICMP_NEXT_IP6_FRAG;
174             }
175         err0:
176           if (PREDICT_TRUE (error0 == MAP_ERROR_NONE))
177             {
178               vlib_increment_combined_counter (cm + MAP_DOMAIN_COUNTER_TX,
179                                                thread_index,
180                                                vnet_buffer (p0)->
181                                                map_t.map_domain_index, 1,
182                                                len0);
183             }
184           else
185             {
186               next0 = IP4_MAPT_ICMP_NEXT_DROP;
187             }
188           p0->error = error_node->errors[error0];
189           vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
190                                            to_next, n_left_to_next, pi0,
191                                            next0);
192         }
193       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
194     }
195   return frame->n_vectors;
196 }
197
198 /*
199  * Translate fragmented IPv4 UDP/TCP packet to IPv6.
200  */
201 always_inline int
202 map_ip4_to_ip6_fragmented (vlib_buffer_t * p,
203                            ip4_mapt_pseudo_header_t * pheader)
204 {
205   ip4_header_t *ip4;
206   ip6_header_t *ip6;
207   ip6_frag_hdr_t *frag;
208
209   ip4 = vlib_buffer_get_current (p);
210   frag = (ip6_frag_hdr_t *) u8_ptr_add (ip4, sizeof (*ip4) - sizeof (*frag));
211   ip6 =
212     (ip6_header_t *) u8_ptr_add (ip4,
213                                  sizeof (*ip4) - sizeof (*frag) -
214                                  sizeof (*ip6));
215   vlib_buffer_advance (p, sizeof (*ip4) - sizeof (*ip6) - sizeof (*frag));
216
217   //We know that the protocol was one of ICMP, TCP or UDP
218   //because the first fragment was found and cached
219   frag->next_hdr =
220     (ip4->protocol == IP_PROTOCOL_ICMP) ? IP_PROTOCOL_ICMP6 : ip4->protocol;
221   frag->identification = frag_id_4to6 (ip4->fragment_id);
222   frag->rsv = 0;
223   frag->fragment_offset_and_more =
224     ip6_frag_hdr_offset_and_more (ip4_get_fragment_offset (ip4),
225                                   clib_net_to_host_u16
226                                   (ip4->flags_and_fragment_offset) &
227                                   IP4_HEADER_FLAG_MORE_FRAGMENTS);
228
229   ip6->ip_version_traffic_class_and_flow_label =
230     clib_host_to_net_u32 ((6 << 28) + (ip4->tos << 20));
231   ip6->payload_length =
232     clib_host_to_net_u16 (clib_net_to_host_u16 (ip4->length) -
233                           sizeof (*ip4) + sizeof (*frag));
234   ip6->hop_limit = ip4->ttl;
235   ip6->protocol = IP_PROTOCOL_IPV6_FRAGMENTATION;
236
237   ip6->dst_address.as_u64[0] = pheader->daddr.as_u64[0];
238   ip6->dst_address.as_u64[1] = pheader->daddr.as_u64[1];
239   ip6->src_address.as_u64[0] = pheader->saddr.as_u64[0];
240   ip6->src_address.as_u64[1] = pheader->saddr.as_u64[1];
241
242   return 0;
243 }
244
245 static uword
246 ip4_map_t_fragmented (vlib_main_t * vm,
247                       vlib_node_runtime_t * node, vlib_frame_t * frame)
248 {
249   u32 n_left_from, *from, next_index, *to_next, n_left_to_next;
250   from = vlib_frame_vector_args (frame);
251   n_left_from = frame->n_vectors;
252   next_index = node->cached_next_index;
253   vlib_node_runtime_t *error_node =
254     vlib_node_get_runtime (vm, ip4_map_t_fragmented_node.index);
255
256   while (n_left_from > 0)
257     {
258       vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
259
260       while (n_left_from > 0 && n_left_to_next > 0)
261         {
262           u32 pi0;
263           vlib_buffer_t *p0;
264           ip4_mapt_pseudo_header_t *pheader0;
265           ip4_mapt_fragmented_next_t next0;
266
267           next0 = IP4_MAPT_FRAGMENTED_NEXT_IP6_LOOKUP;
268           pi0 = to_next[0] = from[0];
269           from += 1;
270           n_left_from -= 1;
271           to_next += 1;
272           n_left_to_next -= 1;
273
274           p0 = vlib_get_buffer (vm, pi0);
275
276           //Accessing pseudo header
277           pheader0 = vlib_buffer_get_current (p0);
278           vlib_buffer_advance (p0, sizeof (*pheader0));
279
280           if (map_ip4_to_ip6_fragmented (p0, pheader0))
281             {
282               p0->error = error_node->errors[MAP_ERROR_FRAGMENT_DROPPED];
283               next0 = IP4_MAPT_FRAGMENTED_NEXT_DROP;
284             }
285           else
286             {
287               if (vnet_buffer (p0)->map_t.mtu < p0->current_length)
288                 {
289                   vnet_buffer (p0)->ip_frag.mtu = vnet_buffer (p0)->map_t.mtu;
290                   vnet_buffer (p0)->ip_frag.next_index =
291                     IP_FRAG_NEXT_IP6_LOOKUP;
292                   next0 = IP4_MAPT_FRAGMENTED_NEXT_IP6_FRAG;
293                 }
294             }
295
296           vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
297                                            to_next, n_left_to_next, pi0,
298                                            next0);
299         }
300       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
301     }
302   return frame->n_vectors;
303 }
304
305 /*
306  * Translate IPv4 UDP/TCP packet to IPv6.
307  */
308 always_inline int
309 map_ip4_to_ip6_tcp_udp (vlib_buffer_t * p, ip4_mapt_pseudo_header_t * pheader)
310 {
311   map_main_t *mm = &map_main;
312   ip4_header_t *ip4;
313   ip6_header_t *ip6;
314   ip_csum_t csum;
315   u16 *checksum;
316   ip6_frag_hdr_t *frag;
317   u32 frag_id;
318   ip4_address_t old_src, old_dst;
319
320   ip4 = vlib_buffer_get_current (p);
321
322   if (ip4->protocol == IP_PROTOCOL_UDP)
323     {
324       udp_header_t *udp = ip4_next_header (ip4);
325       checksum = &udp->checksum;
326
327       /*
328        * UDP checksum is optional over IPv4 but mandatory for IPv6 We
329        * do not check udp->length sanity but use our safe computed
330        * value instead
331        */
332       if (PREDICT_FALSE (!*checksum))
333         {
334           u16 udp_len = clib_host_to_net_u16 (ip4->length) - sizeof (*ip4);
335           csum = ip_incremental_checksum (0, udp, udp_len);
336           csum = ip_csum_with_carry (csum, clib_host_to_net_u16 (udp_len));
337           csum =
338             ip_csum_with_carry (csum, clib_host_to_net_u16 (IP_PROTOCOL_UDP));
339           csum = ip_csum_with_carry (csum, *((u64 *) (&ip4->src_address)));
340           *checksum = ~ip_csum_fold (csum);
341         }
342     }
343   else
344     {
345       tcp_header_t *tcp = ip4_next_header (ip4);
346       if (mm->tcp_mss > 0)
347         {
348           csum = tcp->checksum;
349           map_mss_clamping (tcp, &csum, mm->tcp_mss);
350           tcp->checksum = ip_csum_fold (csum);
351         }
352       checksum = &tcp->checksum;
353     }
354
355   old_src.as_u32 = ip4->src_address.as_u32;
356   old_dst.as_u32 = ip4->dst_address.as_u32;
357
358   /* Deal with fragmented packets */
359   if (PREDICT_FALSE (ip4->flags_and_fragment_offset &
360                      clib_host_to_net_u16 (IP4_HEADER_FLAG_MORE_FRAGMENTS)))
361     {
362       ip6 =
363         (ip6_header_t *) u8_ptr_add (ip4,
364                                      sizeof (*ip4) - sizeof (*ip6) -
365                                      sizeof (*frag));
366       frag =
367         (ip6_frag_hdr_t *) u8_ptr_add (ip4, sizeof (*ip4) - sizeof (*frag));
368       frag_id = frag_id_4to6 (ip4->fragment_id);
369       vlib_buffer_advance (p, sizeof (*ip4) - sizeof (*ip6) - sizeof (*frag));
370     }
371   else
372     {
373       ip6 = (ip6_header_t *) (((u8 *) ip4) + sizeof (*ip4) - sizeof (*ip6));
374       vlib_buffer_advance (p, sizeof (*ip4) - sizeof (*ip6));
375       frag = NULL;
376     }
377
378   ip6->ip_version_traffic_class_and_flow_label =
379     clib_host_to_net_u32 ((6 << 28) + (ip4->tos << 20));
380   ip6->payload_length = u16_net_add (ip4->length, -sizeof (*ip4));
381   ip6->hop_limit = ip4->ttl;
382   ip6->protocol = ip4->protocol;
383   if (PREDICT_FALSE (frag != NULL))
384     {
385       frag->next_hdr = ip6->protocol;
386       frag->identification = frag_id;
387       frag->rsv = 0;
388       frag->fragment_offset_and_more = ip6_frag_hdr_offset_and_more (0, 1);
389       ip6->protocol = IP_PROTOCOL_IPV6_FRAGMENTATION;
390       ip6->payload_length = u16_net_add (ip6->payload_length, sizeof (*frag));
391     }
392
393   ip6->dst_address.as_u64[0] = pheader->daddr.as_u64[0];
394   ip6->dst_address.as_u64[1] = pheader->daddr.as_u64[1];
395   ip6->src_address.as_u64[0] = pheader->saddr.as_u64[0];
396   ip6->src_address.as_u64[1] = pheader->saddr.as_u64[1];
397
398   csum = ip_csum_sub_even (*checksum, old_src.as_u32);
399   csum = ip_csum_sub_even (csum, old_dst.as_u32);
400   csum = ip_csum_add_even (csum, ip6->src_address.as_u64[0]);
401   csum = ip_csum_add_even (csum, ip6->src_address.as_u64[1]);
402   csum = ip_csum_add_even (csum, ip6->dst_address.as_u64[0]);
403   csum = ip_csum_add_even (csum, ip6->dst_address.as_u64[1]);
404   *checksum = ip_csum_fold (csum);
405
406   return 0;
407 }
408
409 static uword
410 ip4_map_t_tcp_udp (vlib_main_t * vm,
411                    vlib_node_runtime_t * node, vlib_frame_t * frame)
412 {
413   u32 n_left_from, *from, next_index, *to_next, n_left_to_next;
414   from = vlib_frame_vector_args (frame);
415   n_left_from = frame->n_vectors;
416   next_index = node->cached_next_index;
417   vlib_node_runtime_t *error_node =
418     vlib_node_get_runtime (vm, ip4_map_t_tcp_udp_node.index);
419
420
421   while (n_left_from > 0)
422     {
423       vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
424
425       while (n_left_from > 0 && n_left_to_next > 0)
426         {
427           u32 pi0;
428           vlib_buffer_t *p0;
429           ip4_mapt_pseudo_header_t *pheader0;
430           ip4_mapt_tcp_udp_next_t next0;
431
432           pi0 = to_next[0] = from[0];
433           from += 1;
434           n_left_from -= 1;
435           to_next += 1;
436           n_left_to_next -= 1;
437
438           next0 = IP4_MAPT_TCP_UDP_NEXT_IP6_LOOKUP;
439           p0 = vlib_get_buffer (vm, pi0);
440
441           //Accessing pseudo header
442           pheader0 = vlib_buffer_get_current (p0);
443           vlib_buffer_advance (p0, sizeof (*pheader0));
444
445           if (map_ip4_to_ip6_tcp_udp (p0, pheader0))
446             {
447               p0->error = error_node->errors[MAP_ERROR_UNKNOWN];
448               next0 = IP4_MAPT_TCP_UDP_NEXT_DROP;
449             }
450           else
451             {
452               if (vnet_buffer (p0)->map_t.mtu < p0->current_length)
453                 {
454                   //Send to fragmentation node if necessary
455                   vnet_buffer (p0)->ip_frag.mtu = vnet_buffer (p0)->map_t.mtu;
456                   vnet_buffer (p0)->ip_frag.next_index =
457                     IP_FRAG_NEXT_IP6_LOOKUP;
458                   next0 = IP4_MAPT_TCP_UDP_NEXT_IP6_FRAG;
459                 }
460             }
461           vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
462                                            to_next, n_left_to_next, pi0,
463                                            next0);
464         }
465       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
466     }
467
468   return frame->n_vectors;
469 }
470
471 static_always_inline void
472 ip4_map_t_classify (vlib_buffer_t * p0, map_domain_t * d0,
473                     ip4_header_t * ip40, u16 ip4_len0, i32 * dst_port0,
474                     u8 * error0, ip4_mapt_next_t * next0, u16 l4_dst_port)
475 {
476   if (PREDICT_FALSE (ip4_get_fragment_offset (ip40)))
477     {
478       *next0 = IP4_MAPT_NEXT_MAPT_FRAGMENTED;
479       if (d0->ea_bits_len == 0 && d0->rules)
480         {
481           *dst_port0 = 0;
482         }
483       else
484         {
485           *dst_port0 = l4_dst_port;
486           *error0 = (*dst_port0 == -1) ? MAP_ERROR_FRAGMENT_MEMORY : *error0;
487         }
488     }
489   else if (PREDICT_TRUE (ip40->protocol == IP_PROTOCOL_TCP))
490     {
491       vnet_buffer (p0)->map_t.checksum_offset = 36;
492       *next0 = IP4_MAPT_NEXT_MAPT_TCP_UDP;
493       *error0 = ip4_len0 < 40 ? MAP_ERROR_MALFORMED : *error0;
494       *dst_port0 = l4_dst_port;
495     }
496   else if (PREDICT_TRUE (ip40->protocol == IP_PROTOCOL_UDP))
497     {
498       vnet_buffer (p0)->map_t.checksum_offset = 26;
499       *next0 = IP4_MAPT_NEXT_MAPT_TCP_UDP;
500       *error0 = ip4_len0 < 28 ? MAP_ERROR_MALFORMED : *error0;
501       *dst_port0 = l4_dst_port;
502     }
503   else if (ip40->protocol == IP_PROTOCOL_ICMP)
504     {
505       *next0 = IP4_MAPT_NEXT_MAPT_ICMP;
506       if (d0->ea_bits_len == 0 && d0->rules)
507         *dst_port0 = 0;
508       else if (((icmp46_header_t *) u8_ptr_add (ip40, sizeof (*ip40)))->code
509                == ICMP4_echo_reply
510                || ((icmp46_header_t *)
511                    u8_ptr_add (ip40,
512                                sizeof (*ip40)))->code == ICMP4_echo_request)
513         *dst_port0 = l4_dst_port;
514     }
515   else
516     {
517       *error0 = MAP_ERROR_BAD_PROTOCOL;
518     }
519 }
520
521 static uword
522 ip4_map_t (vlib_main_t * vm, vlib_node_runtime_t * node, vlib_frame_t * frame)
523 {
524   u32 n_left_from, *from, next_index, *to_next, n_left_to_next;
525   vlib_node_runtime_t *error_node =
526     vlib_node_get_runtime (vm, ip4_map_t_node.index);
527   from = vlib_frame_vector_args (frame);
528   n_left_from = frame->n_vectors;
529   next_index = node->cached_next_index;
530   vlib_combined_counter_main_t *cm = map_main.domain_counters;
531   u32 thread_index = vm->thread_index;
532
533   while (n_left_from > 0)
534     {
535       vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
536
537       while (n_left_from > 0 && n_left_to_next > 0)
538         {
539           u32 pi0;
540           vlib_buffer_t *p0;
541           ip4_header_t *ip40;
542           map_domain_t *d0;
543           ip4_mapt_next_t next0 = 0;
544           u16 ip4_len0;
545           u8 error0;
546           i32 dst_port0;
547           ip4_mapt_pseudo_header_t *pheader0;
548
549           pi0 = to_next[0] = from[0];
550           from += 1;
551           n_left_from -= 1;
552           to_next += 1;
553           n_left_to_next -= 1;
554           error0 = MAP_ERROR_NONE;
555
556           p0 = vlib_get_buffer (vm, pi0);
557
558           u16 l4_dst_port = vnet_buffer (p0)->ip.reass.l4_dst_port;
559
560           ip40 = vlib_buffer_get_current (p0);
561           ip4_len0 = clib_host_to_net_u16 (ip40->length);
562           if (PREDICT_FALSE (p0->current_length < ip4_len0 ||
563                              ip40->ip_version_and_header_length != 0x45))
564             {
565               error0 = MAP_ERROR_UNKNOWN;
566             }
567
568           d0 = ip4_map_get_domain (&ip40->dst_address,
569                                    &vnet_buffer (p0)->map_t.map_domain_index,
570                                    &error0);
571
572           if (!d0)
573             {                   /* Guess it wasn't for us */
574               vnet_feature_next (&next0, p0);
575               goto exit;
576             }
577
578           vnet_buffer (p0)->map_t.mtu = d0->mtu ? d0->mtu : ~0;
579
580           dst_port0 = -1;
581           ip4_map_t_classify (p0, d0, ip40, ip4_len0, &dst_port0, &error0,
582                               &next0, l4_dst_port);
583
584           /* Verify that port is not among the well-known ports */
585           if ((d0->psid_length > 0 && d0->psid_offset > 0)
586               && (clib_net_to_host_u16 (dst_port0) <
587                   (0x1 << (16 - d0->psid_offset))))
588             {
589               error0 = MAP_ERROR_SEC_CHECK;
590             }
591
592           //Add MAP-T pseudo header in front of the packet
593           vlib_buffer_advance (p0, -sizeof (*pheader0));
594           pheader0 = vlib_buffer_get_current (p0);
595
596           //Save addresses within the packet
597           ip4_map_t_embedded_address (d0, &pheader0->saddr,
598                                       &ip40->src_address);
599           pheader0->daddr.as_u64[0] =
600             map_get_pfx_net (d0, ip40->dst_address.as_u32, (u16) dst_port0);
601           pheader0->daddr.as_u64[1] =
602             map_get_sfx_net (d0, ip40->dst_address.as_u32, (u16) dst_port0);
603
604           bool df0 =
605             ip40->flags_and_fragment_offset &
606             clib_host_to_net_u16 (IP4_HEADER_FLAG_DONT_FRAGMENT);
607
608           if (PREDICT_TRUE (ip4_is_first_fragment (ip40) && df0))
609             {
610               p0->error = error_node->errors[MAP_ERROR_FRAGMENT_DROPPED];
611               next0 = IP4_MAPT_NEXT_MAPT_FRAGMENTED;
612               goto exit;
613             }
614
615           if (PREDICT_TRUE
616               (error0 == MAP_ERROR_NONE && next0 != IP4_MAPT_NEXT_MAPT_ICMP))
617             {
618               vlib_increment_combined_counter (cm + MAP_DOMAIN_COUNTER_TX,
619                                                thread_index,
620                                                vnet_buffer (p0)->
621                                                map_t.map_domain_index, 1,
622                                                clib_net_to_host_u16
623                                                (ip40->length));
624             }
625
626           next0 = (error0 != MAP_ERROR_NONE) ? IP4_MAPT_NEXT_DROP : next0;
627           p0->error = error_node->errors[error0];
628
629           if (PREDICT_FALSE (p0->flags & VLIB_BUFFER_IS_TRACED))
630             {
631               map_add_trace (vm, node, p0, d0 - map_main.domains, dst_port0);
632             }
633         exit:
634           vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
635                                            to_next, n_left_to_next, pi0,
636                                            next0);
637         }
638       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
639     }
640   return frame->n_vectors;
641 }
642
643 static char *map_t_error_strings[] = {
644 #define _(sym,string) string,
645   foreach_map_error
646 #undef _
647 };
648
649 /* *INDENT-OFF* */
650 VNET_FEATURE_INIT (ip4_map_t_feature, static) = {
651     .arc_name = "ip4-unicast",
652     .node_name = "ip4-map-t",
653     .runs_before = VNET_FEATURES ("ip4-flow-classify"),
654     .runs_after = VNET_FEATURES ("ip4-sv-reassembly-feature"),
655 };
656
657 VLIB_REGISTER_NODE(ip4_map_t_fragmented_node) = {
658   .function = ip4_map_t_fragmented,
659   .name = "ip4-map-t-fragmented",
660   .vector_size = sizeof(u32),
661   .format_trace = format_map_trace,
662   .type = VLIB_NODE_TYPE_INTERNAL,
663
664   .n_errors = MAP_N_ERROR,
665   .error_strings = map_t_error_strings,
666
667   .n_next_nodes = IP4_MAPT_FRAGMENTED_N_NEXT,
668   .next_nodes = {
669       [IP4_MAPT_FRAGMENTED_NEXT_IP6_LOOKUP] = "ip6-lookup",
670       [IP4_MAPT_FRAGMENTED_NEXT_IP6_FRAG] = IP6_FRAG_NODE_NAME,
671       [IP4_MAPT_FRAGMENTED_NEXT_DROP] = "error-drop",
672   },
673 };
674 /* *INDENT-ON* */
675
676 /* *INDENT-OFF* */
677 VLIB_REGISTER_NODE(ip4_map_t_icmp_node) = {
678   .function = ip4_map_t_icmp,
679   .name = "ip4-map-t-icmp",
680   .vector_size = sizeof(u32),
681   .format_trace = format_map_trace,
682   .type = VLIB_NODE_TYPE_INTERNAL,
683
684   .n_errors = MAP_N_ERROR,
685   .error_strings = map_t_error_strings,
686
687   .n_next_nodes = IP4_MAPT_ICMP_N_NEXT,
688   .next_nodes = {
689       [IP4_MAPT_ICMP_NEXT_IP6_LOOKUP] = "ip6-lookup",
690       [IP4_MAPT_ICMP_NEXT_IP6_FRAG] = IP6_FRAG_NODE_NAME,
691       [IP4_MAPT_ICMP_NEXT_DROP] = "error-drop",
692   },
693 };
694 /* *INDENT-ON* */
695
696 /* *INDENT-OFF* */
697 VLIB_REGISTER_NODE(ip4_map_t_tcp_udp_node) = {
698   .function = ip4_map_t_tcp_udp,
699   .name = "ip4-map-t-tcp-udp",
700   .vector_size = sizeof(u32),
701   .format_trace = format_map_trace,
702   .type = VLIB_NODE_TYPE_INTERNAL,
703
704   .n_errors = MAP_N_ERROR,
705   .error_strings = map_t_error_strings,
706
707   .n_next_nodes = IP4_MAPT_TCP_UDP_N_NEXT,
708   .next_nodes = {
709       [IP4_MAPT_TCP_UDP_NEXT_IP6_LOOKUP] = "ip6-lookup",
710       [IP4_MAPT_TCP_UDP_NEXT_IP6_FRAG] = IP6_FRAG_NODE_NAME,
711       [IP4_MAPT_TCP_UDP_NEXT_DROP] = "error-drop",
712   },
713 };
714 /* *INDENT-ON* */
715
716 /* *INDENT-OFF* */
717 VLIB_REGISTER_NODE(ip4_map_t_node) = {
718   .function = ip4_map_t,
719   .name = "ip4-map-t",
720   .vector_size = sizeof(u32),
721   .format_trace = format_map_trace,
722   .type = VLIB_NODE_TYPE_INTERNAL,
723
724   .n_errors = MAP_N_ERROR,
725   .error_strings = map_t_error_strings,
726
727   .n_next_nodes = IP4_MAPT_N_NEXT,
728   .next_nodes = {
729       [IP4_MAPT_NEXT_MAPT_TCP_UDP] = "ip4-map-t-tcp-udp",
730       [IP4_MAPT_NEXT_MAPT_ICMP] = "ip4-map-t-icmp",
731       [IP4_MAPT_NEXT_MAPT_FRAGMENTED] = "ip4-map-t-fragmented",
732       [IP4_MAPT_NEXT_DROP] = "error-drop",
733   },
734 };
735 /* *INDENT-ON* */
736
737 /*
738  * fd.io coding-style-patch-verification: ON
739  *
740  * Local Variables:
741  * eval: (c-set-style "gnu")
742  * End:
743  */