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