cnat: Add maglev support
[vpp.git] / src / plugins / cnat / cnat_node.h
1 /*
2  * Copyright (c) 2020 Cisco and/or its affiliates.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at:
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15
16 #ifndef __CNAT_NODE_H__
17 #define __CNAT_NODE_H__
18
19 #include <vlibmemory/api.h>
20 #include <vnet/dpo/load_balance.h>
21 #include <vnet/dpo/load_balance_map.h>
22
23 #include <cnat/cnat_session.h>
24 #include <cnat/cnat_client.h>
25 #include <cnat/cnat_inline.h>
26 #include <cnat/cnat_translation.h>
27
28 #include <vnet/ip/ip4_inlines.h>
29 #include <vnet/ip/ip6_inlines.h>
30
31 typedef uword (*cnat_node_sub_t) (vlib_main_t * vm,
32                                   vlib_node_runtime_t * node,
33                                   vlib_buffer_t * b,
34                                   cnat_node_ctx_t * ctx, int rv,
35                                   cnat_session_t * session);
36
37 typedef struct cnat_trace_element_t_
38 {
39   cnat_session_t session;
40   cnat_translation_t tr;
41   u32 sw_if_index[VLIB_N_RX_TX];
42   u32 snat_policy_result;
43   u8 flags;
44 } cnat_trace_element_t;
45
46 typedef enum cnat_trace_element_flag_t_
47 {
48   CNAT_TRACE_SESSION_FOUND = (1 << 0),
49   CNAT_TRACE_SESSION_CREATED = (1 << 1),
50   CNAT_TRACE_TRANSLATION_FOUND = (1 << 2),
51   CNAT_TRACE_NO_NAT = (1 << 3),
52 } cnat_trace_element_flag_t;
53
54 static_always_inline void
55 cnat_add_trace (vlib_main_t *vm, vlib_node_runtime_t *node, vlib_buffer_t *b,
56                 cnat_session_t *session, const cnat_translation_t *ct,
57                 u8 flags)
58 {
59   cnat_trace_element_t *t;
60   if (NULL != ct)
61     flags |= CNAT_TRACE_TRANSLATION_FOUND;
62
63   t = vlib_add_trace (vm, node, b, sizeof (*t));
64   t->sw_if_index[VLIB_RX] = vnet_buffer (b)->sw_if_index[VLIB_RX];
65   t->sw_if_index[VLIB_TX] = vnet_buffer (b)->sw_if_index[VLIB_TX];
66
67   if (flags & (CNAT_TRACE_SESSION_FOUND | CNAT_TRACE_SESSION_CREATED))
68     clib_memcpy (&t->session, session, sizeof (t->session));
69   if (flags & CNAT_TRACE_TRANSLATION_FOUND)
70     clib_memcpy (&t->tr, ct, sizeof (cnat_translation_t));
71   t->flags = flags;
72 }
73
74 static u8 *
75 format_cnat_trace (u8 *s, va_list *args)
76 {
77   CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
78   CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
79   cnat_trace_element_t *t = va_arg (*args, cnat_trace_element_t *);
80   u32 indent = format_get_indent (s);
81   vnet_main_t *vnm = vnet_get_main ();
82
83   if (t->flags & CNAT_TRACE_SESSION_CREATED)
84     s = format (s, "created session");
85   else if (t->flags & CNAT_TRACE_SESSION_FOUND)
86     s = format (s, "found session");
87   else
88     s = format (s, "session not found");
89
90   if (t->flags & (CNAT_TRACE_NO_NAT))
91     s = format (s, " [policy:skip]");
92
93   s = format (s, "\n%Uin:%U out:%U ", format_white_space, indent,
94               format_vnet_sw_if_index_name, vnm, t->sw_if_index[VLIB_RX],
95               format_vnet_sw_if_index_name, vnm, t->sw_if_index[VLIB_TX]);
96
97   if (t->flags & (CNAT_TRACE_SESSION_CREATED | CNAT_TRACE_SESSION_FOUND))
98     s = format (s, "\n%U%U", format_white_space, indent, format_cnat_session,
99                 &t->session, 1);
100
101   if (t->flags & CNAT_TRACE_TRANSLATION_FOUND)
102     s = format (s, "\n%Utranslation: %U", format_white_space, indent,
103                 format_cnat_translation, &t->tr, 0);
104
105   return s;
106 }
107
108 static_always_inline u8
109 icmp_type_is_error_message (u8 icmp_type)
110 {
111   switch (icmp_type)
112     {
113     case ICMP4_destination_unreachable:
114     case ICMP4_time_exceeded:
115     case ICMP4_parameter_problem:
116     case ICMP4_source_quench:
117     case ICMP4_redirect:
118     case ICMP4_alternate_host_address:
119       return 1;
120     }
121   return 0;
122 }
123
124 static_always_inline u8
125 icmp_type_is_echo (u8 icmp_type)
126 {
127   switch (icmp_type)
128     {
129     case ICMP4_echo_request:
130     case ICMP4_echo_reply:
131       return 1;
132     }
133   return 0;
134 }
135
136 static_always_inline u8
137 icmp6_type_is_echo (u8 icmp_type)
138 {
139   switch (icmp_type)
140     {
141     case ICMP6_echo_request:
142     case ICMP6_echo_reply:
143       return 1;
144     }
145   return 0;
146 }
147
148 static_always_inline u8
149 icmp6_type_is_error_message (u8 icmp_type)
150 {
151   switch (icmp_type)
152     {
153     case ICMP6_destination_unreachable:
154     case ICMP6_time_exceeded:
155     case ICMP6_parameter_problem:
156       return 1;
157     }
158   return 0;
159 }
160
161 static_always_inline u8
162 cmp_ip6_address (const ip6_address_t * a1, const ip6_address_t * a2)
163 {
164   return ((a1->as_u64[0] == a2->as_u64[0])
165           && (a1->as_u64[1] == a2->as_u64[1]));
166 }
167
168 /**
169  * Inline translation functions
170  */
171
172 static_always_inline u8
173 has_ip6_address (ip6_address_t * a)
174 {
175   return ((0 != a->as_u64[0]) || (0 != a->as_u64[1]));
176 }
177
178 static_always_inline void
179 cnat_ip4_translate_l4 (ip4_header_t * ip4, udp_header_t * udp,
180                        ip_csum_t * sum,
181                        ip4_address_t new_addr[VLIB_N_DIR],
182                        u16 new_port[VLIB_N_DIR])
183 {
184   u16 old_port[VLIB_N_DIR];
185   ip4_address_t old_addr[VLIB_N_DIR];
186
187   /* Fastpath no checksum */
188   if (PREDICT_TRUE (0 == *sum))
189     {
190       udp->dst_port = new_port[VLIB_TX];
191       udp->src_port = new_port[VLIB_RX];
192       return;
193     }
194
195   old_port[VLIB_TX] = udp->dst_port;
196   old_port[VLIB_RX] = udp->src_port;
197   old_addr[VLIB_TX] = ip4->dst_address;
198   old_addr[VLIB_RX] = ip4->src_address;
199
200   if (new_addr[VLIB_TX].as_u32)
201     {
202       *sum =
203         ip_csum_update (*sum, old_addr[VLIB_TX].as_u32,
204                         new_addr[VLIB_TX].as_u32, ip4_header_t, dst_address);
205     }
206   if (new_port[VLIB_TX])
207     {
208       udp->dst_port = new_port[VLIB_TX];
209       *sum = ip_csum_update (*sum, old_port[VLIB_TX], new_port[VLIB_TX],
210                              ip4_header_t /* cheat */ ,
211                              length /* changed member */ );
212     }
213   if (new_addr[VLIB_RX].as_u32)
214     {
215       *sum =
216         ip_csum_update (*sum, old_addr[VLIB_RX].as_u32,
217                         new_addr[VLIB_RX].as_u32, ip4_header_t, src_address);
218     }
219   if (new_port[VLIB_RX])
220     {
221       udp->src_port = new_port[VLIB_RX];
222       *sum = ip_csum_update (*sum, old_port[VLIB_RX], new_port[VLIB_RX],
223                              ip4_header_t /* cheat */ ,
224                              length /* changed member */ );
225     }
226 }
227
228 static_always_inline void
229 cnat_ip4_translate_l3 (ip4_header_t * ip4, ip4_address_t new_addr[VLIB_N_DIR])
230 {
231   ip4_address_t old_addr[VLIB_N_DIR];
232   ip_csum_t sum;
233
234   old_addr[VLIB_TX] = ip4->dst_address;
235   old_addr[VLIB_RX] = ip4->src_address;
236
237   sum = ip4->checksum;
238   if (new_addr[VLIB_TX].as_u32)
239     {
240       ip4->dst_address = new_addr[VLIB_TX];
241       sum =
242         ip_csum_update (sum, old_addr[VLIB_TX].as_u32,
243                         new_addr[VLIB_TX].as_u32, ip4_header_t, dst_address);
244     }
245   if (new_addr[VLIB_RX].as_u32)
246     {
247       ip4->src_address = new_addr[VLIB_RX];
248       sum =
249         ip_csum_update (sum, old_addr[VLIB_RX].as_u32,
250                         new_addr[VLIB_RX].as_u32, ip4_header_t, src_address);
251     }
252   ip4->checksum = ip_csum_fold (sum);
253 }
254
255 static_always_inline void
256 cnat_tcp_update_session_lifetime (tcp_header_t * tcp, u32 index)
257 {
258   cnat_main_t *cm = &cnat_main;
259   if (PREDICT_FALSE (tcp_fin (tcp)))
260     {
261       cnat_timestamp_set_lifetime (index, CNAT_DEFAULT_TCP_RST_TIMEOUT);
262     }
263
264   if (PREDICT_FALSE (tcp_rst (tcp)))
265     {
266       cnat_timestamp_set_lifetime (index, CNAT_DEFAULT_TCP_RST_TIMEOUT);
267     }
268
269   if (PREDICT_FALSE (tcp_syn (tcp) && tcp_ack (tcp)))
270     {
271       cnat_timestamp_set_lifetime (index, cm->tcp_max_age);
272     }
273 }
274
275 static_always_inline void
276 cnat_translation_icmp4_echo (ip4_header_t * ip4, icmp46_header_t * icmp,
277                              ip4_address_t new_addr[VLIB_N_DIR],
278                              u16 new_port[VLIB_N_DIR])
279 {
280   ip_csum_t sum;
281   u16 old_port;
282   cnat_echo_header_t *echo = (cnat_echo_header_t *) (icmp + 1);
283
284   cnat_ip4_translate_l3 (ip4, new_addr);
285   old_port = echo->identifier;
286   echo->identifier = new_port[VLIB_RX];
287
288   sum = icmp->checksum;
289   sum = ip_csum_update (sum, old_port, new_port[VLIB_RX],
290                         ip4_header_t /* cheat */ ,
291                         length /* changed member */ );
292
293   icmp->checksum = ip_csum_fold (sum);
294 }
295
296 static_always_inline void
297 cnat_translation_icmp4_error (ip4_header_t * outer_ip4,
298                               icmp46_header_t * icmp,
299                               ip4_address_t outer_new_addr[VLIB_N_DIR],
300                               u16 outer_new_port[VLIB_N_DIR],
301                               u8 snat_outer_ip)
302 {
303   ip4_address_t new_addr[VLIB_N_DIR];
304   ip4_address_t old_addr[VLIB_N_DIR];
305   u16 new_port[VLIB_N_DIR];
306   u16 old_port[VLIB_N_DIR];
307   ip_csum_t sum, old_ip_sum, inner_l4_sum, inner_l4_old_sum;
308
309   ip4_header_t *ip4 = (ip4_header_t *) (icmp + 2);
310   udp_header_t *udp = (udp_header_t *) (ip4 + 1);
311   tcp_header_t *tcp = (tcp_header_t *) udp;
312
313   /* Swap inner ports */
314   new_addr[VLIB_TX] = outer_new_addr[VLIB_RX];
315   new_addr[VLIB_RX] = outer_new_addr[VLIB_TX];
316   new_port[VLIB_TX] = outer_new_port[VLIB_RX];
317   new_port[VLIB_RX] = outer_new_port[VLIB_TX];
318
319   old_addr[VLIB_TX] = ip4->dst_address;
320   old_addr[VLIB_RX] = ip4->src_address;
321   old_port[VLIB_RX] = udp->src_port;
322   old_port[VLIB_TX] = udp->dst_port;
323
324   sum = icmp->checksum;
325   old_ip_sum = ip4->checksum;
326
327   /* translate outer ip. */
328   if (!snat_outer_ip)
329     outer_new_addr[VLIB_RX] = outer_ip4->src_address;
330   cnat_ip4_translate_l3 (outer_ip4, outer_new_addr);
331
332   if (ip4->protocol == IP_PROTOCOL_TCP)
333     {
334       inner_l4_old_sum = inner_l4_sum = tcp->checksum;
335       cnat_ip4_translate_l4 (ip4, udp, &inner_l4_sum, new_addr, new_port);
336       tcp->checksum = ip_csum_fold (inner_l4_sum);
337     }
338   else if (ip4->protocol == IP_PROTOCOL_UDP)
339     {
340       inner_l4_old_sum = inner_l4_sum = udp->checksum;
341       cnat_ip4_translate_l4 (ip4, udp, &inner_l4_sum, new_addr, new_port);
342       udp->checksum = ip_csum_fold (inner_l4_sum);
343     }
344   else
345     return;
346
347   /* UDP/TCP checksum changed */
348   sum = ip_csum_update (sum, inner_l4_old_sum, inner_l4_sum,
349                         ip4_header_t, checksum);
350
351   /* UDP/TCP Ports changed */
352   if (old_port[VLIB_TX] && new_port[VLIB_TX])
353     sum = ip_csum_update (sum, old_port[VLIB_TX], new_port[VLIB_TX],
354                           ip4_header_t /* cheat */ ,
355                           length /* changed member */ );
356
357   if (old_port[VLIB_RX] && new_port[VLIB_RX])
358     sum = ip_csum_update (sum, old_port[VLIB_RX], new_port[VLIB_RX],
359                           ip4_header_t /* cheat */ ,
360                           length /* changed member */ );
361
362
363   cnat_ip4_translate_l3 (ip4, new_addr);
364   ip_csum_t new_ip_sum = ip4->checksum;
365   /* IP checksum changed */
366   sum = ip_csum_update (sum, old_ip_sum, new_ip_sum, ip4_header_t, checksum);
367
368   /* IP src/dst addr changed */
369   if (new_addr[VLIB_TX].as_u32)
370     sum =
371       ip_csum_update (sum, old_addr[VLIB_TX].as_u32, new_addr[VLIB_TX].as_u32,
372                       ip4_header_t, dst_address);
373
374   if (new_addr[VLIB_RX].as_u32)
375     sum =
376       ip_csum_update (sum, old_addr[VLIB_RX].as_u32, new_addr[VLIB_RX].as_u32,
377                       ip4_header_t, src_address);
378
379   icmp->checksum = ip_csum_fold (sum);
380 }
381
382 static_always_inline void
383 cnat_translation_ip4 (const cnat_session_t * session,
384                       ip4_header_t * ip4, udp_header_t * udp)
385 {
386   tcp_header_t *tcp = (tcp_header_t *) udp;
387   ip4_address_t new_addr[VLIB_N_DIR];
388   u16 new_port[VLIB_N_DIR];
389
390   new_addr[VLIB_TX] = session->value.cs_ip[VLIB_TX].ip4;
391   new_addr[VLIB_RX] = session->value.cs_ip[VLIB_RX].ip4;
392   new_port[VLIB_TX] = session->value.cs_port[VLIB_TX];
393   new_port[VLIB_RX] = session->value.cs_port[VLIB_RX];
394
395   if (ip4->protocol == IP_PROTOCOL_TCP)
396     {
397       ip_csum_t sum = tcp->checksum;
398       cnat_ip4_translate_l4 (ip4, udp, &sum, new_addr, new_port);
399       tcp->checksum = ip_csum_fold (sum);
400       cnat_ip4_translate_l3 (ip4, new_addr);
401       cnat_tcp_update_session_lifetime (tcp, session->value.cs_ts_index);
402     }
403   else if (ip4->protocol == IP_PROTOCOL_UDP)
404     {
405       ip_csum_t sum = udp->checksum;
406       cnat_ip4_translate_l4 (ip4, udp, &sum, new_addr, new_port);
407       udp->checksum = ip_csum_fold (sum);
408       cnat_ip4_translate_l3 (ip4, new_addr);
409     }
410   else if (ip4->protocol == IP_PROTOCOL_ICMP)
411     {
412       icmp46_header_t *icmp = (icmp46_header_t *) udp;
413       if (icmp_type_is_error_message (icmp->type))
414         {
415           /* SNAT only if src_addr was translated */
416           u8 snat_outer_ip =
417             (ip4->src_address.as_u32 ==
418              session->key.cs_ip[VLIB_RX].ip4.as_u32);
419           cnat_translation_icmp4_error (ip4, icmp, new_addr, new_port,
420                                         snat_outer_ip);
421         }
422       else if (icmp_type_is_echo (icmp->type))
423         cnat_translation_icmp4_echo (ip4, icmp, new_addr, new_port);
424     }
425 }
426
427 static_always_inline void
428 cnat_ip6_translate_l3 (ip6_header_t * ip6, ip6_address_t new_addr[VLIB_N_DIR])
429 {
430   if (has_ip6_address (&new_addr[VLIB_TX]))
431     ip6_address_copy (&ip6->dst_address, &new_addr[VLIB_TX]);
432   if (has_ip6_address (&new_addr[VLIB_RX]))
433     ip6_address_copy (&ip6->src_address, &new_addr[VLIB_RX]);
434 }
435
436 static_always_inline void
437 cnat_ip6_translate_l4 (ip6_header_t * ip6, udp_header_t * udp,
438                        ip_csum_t * sum,
439                        ip6_address_t new_addr[VLIB_N_DIR],
440                        u16 new_port[VLIB_N_DIR])
441 {
442   u16 old_port[VLIB_N_DIR];
443   ip6_address_t old_addr[VLIB_N_DIR];
444
445   /* Fastpath no checksum */
446   if (PREDICT_TRUE (0 == *sum))
447     {
448       udp->dst_port = new_port[VLIB_TX];
449       udp->src_port = new_port[VLIB_RX];
450       return;
451     }
452
453   old_port[VLIB_TX] = udp->dst_port;
454   old_port[VLIB_RX] = udp->src_port;
455   ip6_address_copy (&old_addr[VLIB_TX], &ip6->dst_address);
456   ip6_address_copy (&old_addr[VLIB_RX], &ip6->src_address);
457
458   if (has_ip6_address (&new_addr[VLIB_TX]))
459     {
460       *sum = ip_csum_add_even (*sum, new_addr[VLIB_TX].as_u64[0]);
461       *sum = ip_csum_add_even (*sum, new_addr[VLIB_TX].as_u64[1]);
462       *sum = ip_csum_sub_even (*sum, old_addr[VLIB_TX].as_u64[0]);
463       *sum = ip_csum_sub_even (*sum, old_addr[VLIB_TX].as_u64[1]);
464     }
465
466   if (new_port[VLIB_TX])
467     {
468       udp->dst_port = new_port[VLIB_TX];
469       *sum = ip_csum_update (*sum, old_port[VLIB_TX], new_port[VLIB_TX],
470                              ip4_header_t /* cheat */ ,
471                              length /* changed member */ );
472     }
473   if (has_ip6_address (&new_addr[VLIB_RX]))
474     {
475       *sum = ip_csum_add_even (*sum, new_addr[VLIB_RX].as_u64[0]);
476       *sum = ip_csum_add_even (*sum, new_addr[VLIB_RX].as_u64[1]);
477       *sum = ip_csum_sub_even (*sum, old_addr[VLIB_RX].as_u64[0]);
478       *sum = ip_csum_sub_even (*sum, old_addr[VLIB_RX].as_u64[1]);
479     }
480
481   if (new_port[VLIB_RX])
482     {
483       udp->src_port = new_port[VLIB_RX];
484       *sum = ip_csum_update (*sum, old_port[VLIB_RX], new_port[VLIB_RX],
485                              ip4_header_t /* cheat */ ,
486                              length /* changed member */ );
487     }
488 }
489
490 static_always_inline void
491 cnat_translation_icmp6_echo (ip6_header_t * ip6, icmp46_header_t * icmp,
492                              ip6_address_t new_addr[VLIB_N_DIR],
493                              u16 new_port[VLIB_N_DIR])
494 {
495   cnat_echo_header_t *echo = (cnat_echo_header_t *) (icmp + 1);
496   ip6_address_t old_addr[VLIB_N_DIR];
497   ip_csum_t sum;
498   u16 old_port;
499   old_port = echo->identifier;
500   ip6_address_copy (&old_addr[VLIB_TX], &ip6->dst_address);
501   ip6_address_copy (&old_addr[VLIB_RX], &ip6->src_address);
502
503   sum = icmp->checksum;
504
505   cnat_ip6_translate_l3 (ip6, new_addr);
506   if (has_ip6_address (&new_addr[VLIB_TX]))
507     {
508       sum = ip_csum_add_even (sum, new_addr[VLIB_TX].as_u64[0]);
509       sum = ip_csum_add_even (sum, new_addr[VLIB_TX].as_u64[1]);
510       sum = ip_csum_sub_even (sum, old_addr[VLIB_TX].as_u64[0]);
511       sum = ip_csum_sub_even (sum, old_addr[VLIB_TX].as_u64[1]);
512     }
513
514   if (has_ip6_address (&new_addr[VLIB_RX]))
515     {
516       sum = ip_csum_add_even (sum, new_addr[VLIB_RX].as_u64[0]);
517       sum = ip_csum_add_even (sum, new_addr[VLIB_RX].as_u64[1]);
518       sum = ip_csum_sub_even (sum, old_addr[VLIB_RX].as_u64[0]);
519       sum = ip_csum_sub_even (sum, old_addr[VLIB_RX].as_u64[1]);
520     }
521
522   echo->identifier = new_port[VLIB_RX];
523   sum = ip_csum_update (sum, old_port, new_port[VLIB_RX],
524                         ip4_header_t /* cheat */ ,
525                         length /* changed member */ );
526
527   icmp->checksum = ip_csum_fold (sum);
528 }
529
530 static_always_inline void
531 cnat_translation_icmp6_error (ip6_header_t * outer_ip6,
532                               icmp46_header_t * icmp,
533                               ip6_address_t outer_new_addr[VLIB_N_DIR],
534                               u16 outer_new_port[VLIB_N_DIR],
535                               u8 snat_outer_ip)
536 {
537   ip6_address_t new_addr[VLIB_N_DIR];
538   ip6_address_t old_addr[VLIB_N_DIR];
539   ip6_address_t outer_old_addr[VLIB_N_DIR];
540   u16 new_port[VLIB_N_DIR];
541   u16 old_port[VLIB_N_DIR];
542   ip_csum_t sum, inner_l4_sum, inner_l4_old_sum;
543
544   if (!icmp6_type_is_error_message (icmp->type))
545     return;
546
547   ip6_header_t *ip6 = (ip6_header_t *) (icmp + 2);
548   udp_header_t *udp = (udp_header_t *) (ip6 + 1);
549   tcp_header_t *tcp = (tcp_header_t *) udp;
550
551   /* Swap inner ports */
552   ip6_address_copy (&new_addr[VLIB_RX], &outer_new_addr[VLIB_TX]);
553   ip6_address_copy (&new_addr[VLIB_TX], &outer_new_addr[VLIB_RX]);
554   new_port[VLIB_TX] = outer_new_port[VLIB_RX];
555   new_port[VLIB_RX] = outer_new_port[VLIB_TX];
556
557   ip6_address_copy (&old_addr[VLIB_TX], &ip6->dst_address);
558   ip6_address_copy (&old_addr[VLIB_RX], &ip6->src_address);
559   old_port[VLIB_RX] = udp->src_port;
560   old_port[VLIB_TX] = udp->dst_port;
561
562   sum = icmp->checksum;
563   /* Translate outer ip */
564   ip6_address_copy (&outer_old_addr[VLIB_TX], &outer_ip6->dst_address);
565   ip6_address_copy (&outer_old_addr[VLIB_RX], &outer_ip6->src_address);
566   if (!snat_outer_ip)
567     ip6_address_copy (&outer_new_addr[VLIB_RX], &outer_ip6->src_address);
568   cnat_ip6_translate_l3 (outer_ip6, outer_new_addr);
569   if (has_ip6_address (&outer_new_addr[VLIB_TX]))
570     {
571       sum = ip_csum_add_even (sum, outer_new_addr[VLIB_TX].as_u64[0]);
572       sum = ip_csum_add_even (sum, outer_new_addr[VLIB_TX].as_u64[1]);
573       sum = ip_csum_sub_even (sum, outer_old_addr[VLIB_TX].as_u64[0]);
574       sum = ip_csum_sub_even (sum, outer_old_addr[VLIB_TX].as_u64[1]);
575     }
576
577   if (has_ip6_address (&outer_new_addr[VLIB_RX]))
578     {
579       sum = ip_csum_add_even (sum, outer_new_addr[VLIB_RX].as_u64[0]);
580       sum = ip_csum_add_even (sum, outer_new_addr[VLIB_RX].as_u64[1]);
581       sum = ip_csum_sub_even (sum, outer_old_addr[VLIB_RX].as_u64[0]);
582       sum = ip_csum_sub_even (sum, outer_old_addr[VLIB_RX].as_u64[1]);
583     }
584
585   /* Translate inner TCP / UDP */
586   if (ip6->protocol == IP_PROTOCOL_TCP)
587     {
588       inner_l4_old_sum = inner_l4_sum = tcp->checksum;
589       cnat_ip6_translate_l4 (ip6, udp, &inner_l4_sum, new_addr, new_port);
590       tcp->checksum = ip_csum_fold (inner_l4_sum);
591     }
592   else if (ip6->protocol == IP_PROTOCOL_UDP)
593     {
594       inner_l4_old_sum = inner_l4_sum = udp->checksum;
595       cnat_ip6_translate_l4 (ip6, udp, &inner_l4_sum, new_addr, new_port);
596       udp->checksum = ip_csum_fold (inner_l4_sum);
597     }
598   else
599     return;
600
601   /* UDP/TCP checksum changed */
602   sum = ip_csum_update (sum, inner_l4_old_sum, inner_l4_sum,
603                         ip4_header_t /* cheat */ ,
604                         checksum);
605
606   /* UDP/TCP Ports changed */
607   if (old_port[VLIB_TX] && new_port[VLIB_TX])
608     sum = ip_csum_update (sum, old_port[VLIB_TX], new_port[VLIB_TX],
609                           ip4_header_t /* cheat */ ,
610                           length /* changed member */ );
611
612   if (old_port[VLIB_RX] && new_port[VLIB_RX])
613     sum = ip_csum_update (sum, old_port[VLIB_RX], new_port[VLIB_RX],
614                           ip4_header_t /* cheat */ ,
615                           length /* changed member */ );
616
617
618   cnat_ip6_translate_l3 (ip6, new_addr);
619   /* IP src/dst addr changed */
620   if (has_ip6_address (&new_addr[VLIB_TX]))
621     {
622       sum = ip_csum_add_even (sum, new_addr[VLIB_TX].as_u64[0]);
623       sum = ip_csum_add_even (sum, new_addr[VLIB_TX].as_u64[1]);
624       sum = ip_csum_sub_even (sum, old_addr[VLIB_TX].as_u64[0]);
625       sum = ip_csum_sub_even (sum, old_addr[VLIB_TX].as_u64[1]);
626     }
627
628   if (has_ip6_address (&new_addr[VLIB_RX]))
629     {
630       sum = ip_csum_add_even (sum, new_addr[VLIB_RX].as_u64[0]);
631       sum = ip_csum_add_even (sum, new_addr[VLIB_RX].as_u64[1]);
632       sum = ip_csum_sub_even (sum, old_addr[VLIB_RX].as_u64[0]);
633       sum = ip_csum_sub_even (sum, old_addr[VLIB_RX].as_u64[1]);
634     }
635
636   icmp->checksum = ip_csum_fold (sum);
637 }
638
639 static_always_inline void
640 cnat_translation_ip6 (const cnat_session_t * session,
641                       ip6_header_t * ip6, udp_header_t * udp)
642 {
643   tcp_header_t *tcp = (tcp_header_t *) udp;
644   ip6_address_t new_addr[VLIB_N_DIR];
645   u16 new_port[VLIB_N_DIR];
646
647   ip6_address_copy (&new_addr[VLIB_TX], &session->value.cs_ip[VLIB_TX].ip6);
648   ip6_address_copy (&new_addr[VLIB_RX], &session->value.cs_ip[VLIB_RX].ip6);
649   new_port[VLIB_TX] = session->value.cs_port[VLIB_TX];
650   new_port[VLIB_RX] = session->value.cs_port[VLIB_RX];
651
652   if (ip6->protocol == IP_PROTOCOL_TCP)
653     {
654       ip_csum_t sum = tcp->checksum;
655       cnat_ip6_translate_l4 (ip6, udp, &sum, new_addr, new_port);
656       tcp->checksum = ip_csum_fold (sum);
657       cnat_ip6_translate_l3 (ip6, new_addr);
658       cnat_tcp_update_session_lifetime (tcp, session->value.cs_ts_index);
659     }
660   else if (ip6->protocol == IP_PROTOCOL_UDP)
661     {
662       ip_csum_t sum = udp->checksum;
663       cnat_ip6_translate_l4 (ip6, udp, &sum, new_addr, new_port);
664       udp->checksum = ip_csum_fold (sum);
665       cnat_ip6_translate_l3 (ip6, new_addr);
666     }
667   else if (ip6->protocol == IP_PROTOCOL_ICMP6)
668     {
669       icmp46_header_t *icmp = (icmp46_header_t *) udp;
670       if (icmp6_type_is_error_message (icmp->type))
671         {
672           /* SNAT only if src_addr was translated */
673           u8 snat_outer_ip = cmp_ip6_address (&ip6->src_address,
674                                               &session->key.
675                                               cs_ip[VLIB_RX].ip6);
676           cnat_translation_icmp6_error (ip6, icmp, new_addr, new_port,
677                                         snat_outer_ip);
678         }
679       else if (icmp6_type_is_echo (icmp->type))
680         cnat_translation_icmp6_echo (ip6, icmp, new_addr, new_port);
681     }
682 }
683
684 static_always_inline void
685 cnat_session_make_key (vlib_buffer_t *b, ip_address_family_t af,
686                        cnat_session_location_t cs_loc, cnat_bihash_kv_t *bkey)
687 {
688   udp_header_t *udp;
689   cnat_session_t *session = (cnat_session_t *) bkey;
690   u32 iph_offset = 0;
691   session->key.cs_af = af;
692
693   session->key.cs_loc = cs_loc;
694   session->key.__cs_pad = 0;
695   if (cs_loc == CNAT_LOCATION_OUTPUT)
696     /* rewind buffer */
697     iph_offset = vnet_buffer (b)->ip.save_rewrite_length;
698
699   if (AF_IP4 == af)
700     {
701       ip4_header_t *ip4;
702       ip4 = (ip4_header_t *) ((u8 *) vlib_buffer_get_current (b) + iph_offset);
703
704       if (PREDICT_FALSE (ip4->protocol == IP_PROTOCOL_ICMP))
705         {
706           icmp46_header_t *icmp = (icmp46_header_t *) (ip4 + 1);
707           if (icmp_type_is_error_message (icmp->type))
708             {
709               ip4 = (ip4_header_t *) (icmp + 2);        /* Use inner packet */
710               udp = (udp_header_t *) (ip4 + 1);
711               /* Swap dst & src for search as ICMP payload is reversed */
712               ip46_address_set_ip4 (&session->key.cs_ip[VLIB_RX],
713                                     &ip4->dst_address);
714               ip46_address_set_ip4 (&session->key.cs_ip[VLIB_TX],
715                                     &ip4->src_address);
716               session->key.cs_proto = ip4->protocol;
717               session->key.cs_port[VLIB_TX] = udp->src_port;
718               session->key.cs_port[VLIB_RX] = udp->dst_port;
719             }
720           else if (icmp_type_is_echo (icmp->type))
721             {
722               cnat_echo_header_t *echo = (cnat_echo_header_t *) (icmp + 1);
723               ip46_address_set_ip4 (&session->key.cs_ip[VLIB_TX],
724                                     &ip4->dst_address);
725               ip46_address_set_ip4 (&session->key.cs_ip[VLIB_RX],
726                                     &ip4->src_address);
727               session->key.cs_proto = ip4->protocol;
728               session->key.cs_port[VLIB_TX] = echo->identifier;
729               session->key.cs_port[VLIB_RX] = echo->identifier;
730             }
731           else
732             goto error;
733         }
734       else if (ip4->protocol == IP_PROTOCOL_UDP ||
735                ip4->protocol == IP_PROTOCOL_TCP)
736         {
737           udp = (udp_header_t *) (ip4 + 1);
738           ip46_address_set_ip4 (&session->key.cs_ip[VLIB_TX],
739                                 &ip4->dst_address);
740           ip46_address_set_ip4 (&session->key.cs_ip[VLIB_RX],
741                                 &ip4->src_address);
742           session->key.cs_proto = ip4->protocol;
743           session->key.cs_port[VLIB_RX] = udp->src_port;
744           session->key.cs_port[VLIB_TX] = udp->dst_port;
745         }
746       else
747         goto error;
748     }
749   else
750     {
751       ip6_header_t *ip6;
752       ip6 = (ip6_header_t *) ((u8 *) vlib_buffer_get_current (b) + iph_offset);
753       if (PREDICT_FALSE (ip6->protocol == IP_PROTOCOL_ICMP6))
754         {
755           icmp46_header_t *icmp = (icmp46_header_t *) (ip6 + 1);
756           if (icmp6_type_is_error_message (icmp->type))
757             {
758               ip6 = (ip6_header_t *) (icmp + 2);        /* Use inner packet */
759               udp = (udp_header_t *) (ip6 + 1);
760               /* Swap dst & src for search as ICMP payload is reversed */
761               ip46_address_set_ip6 (&session->key.cs_ip[VLIB_RX],
762                                     &ip6->dst_address);
763               ip46_address_set_ip6 (&session->key.cs_ip[VLIB_TX],
764                                     &ip6->src_address);
765               session->key.cs_proto = ip6->protocol;
766               session->key.cs_port[VLIB_TX] = udp->src_port;
767               session->key.cs_port[VLIB_RX] = udp->dst_port;
768             }
769           else if (icmp6_type_is_echo (icmp->type))
770             {
771               cnat_echo_header_t *echo = (cnat_echo_header_t *) (icmp + 1);
772               ip46_address_set_ip6 (&session->key.cs_ip[VLIB_TX],
773                                     &ip6->dst_address);
774               ip46_address_set_ip6 (&session->key.cs_ip[VLIB_RX],
775                                     &ip6->src_address);
776               session->key.cs_proto = ip6->protocol;
777               session->key.cs_port[VLIB_TX] = echo->identifier;
778               session->key.cs_port[VLIB_RX] = echo->identifier;
779             }
780           else
781             goto error;
782         }
783       else if (ip6->protocol == IP_PROTOCOL_UDP ||
784                ip6->protocol == IP_PROTOCOL_TCP)
785         {
786           udp = (udp_header_t *) (ip6 + 1);
787           ip46_address_set_ip6 (&session->key.cs_ip[VLIB_TX],
788                                 &ip6->dst_address);
789           ip46_address_set_ip6 (&session->key.cs_ip[VLIB_RX],
790                                 &ip6->src_address);
791           session->key.cs_port[VLIB_RX] = udp->src_port;
792           session->key.cs_port[VLIB_TX] = udp->dst_port;
793           session->key.cs_proto = ip6->protocol;
794         }
795       else
796         goto error;
797     }
798   return;
799
800 error:
801   /* Ensure we dont find anything */
802   session->key.cs_proto = 0;
803   return;
804 }
805
806 static_always_inline cnat_ep_trk_t *
807 cnat_load_balance (const cnat_translation_t *ct, ip_address_family_t af,
808                    ip4_header_t *ip4, ip6_header_t *ip6, u32 *dpoi_index)
809 {
810   cnat_main_t *cm = &cnat_main;
811   const load_balance_t *lb0;
812   const dpo_id_t *dpo0;
813   u32 hash_c0, bucket0;
814
815   lb0 = load_balance_get (ct->ct_lb.dpoi_index);
816   if (PREDICT_FALSE (!lb0->lb_n_buckets))
817     return (NULL);
818
819   /* session table miss */
820   hash_c0 = (AF_IP4 == af ? ip4_compute_flow_hash (ip4, lb0->lb_hash_config) :
821                             ip6_compute_flow_hash (ip6, lb0->lb_hash_config));
822
823   if (PREDICT_FALSE (ct->lb_type == CNAT_LB_MAGLEV))
824     bucket0 = ct->lb_maglev[hash_c0 % cm->maglev_len];
825   else
826     bucket0 = hash_c0 % lb0->lb_n_buckets;
827
828   dpo0 = load_balance_get_fwd_bucket (lb0, bucket0);
829
830   *dpoi_index = dpo0->dpoi_index;
831
832   return &ct->ct_active_paths[bucket0];
833 }
834
835 /**
836  * Create NAT sessions
837  * rsession_location is the location the (return) session will be
838  * matched at
839  */
840 static_always_inline void
841 cnat_session_create (cnat_session_t *session, cnat_node_ctx_t *ctx,
842                      cnat_session_location_t rsession_location,
843                      u8 rsession_flags)
844 {
845   cnat_client_t *cc;
846   cnat_bihash_kv_t rkey;
847   cnat_session_t *rsession = (cnat_session_t *) & rkey;
848   cnat_bihash_kv_t *bkey = (cnat_bihash_kv_t *) session;
849   cnat_bihash_kv_t rvalue;
850   int rv;
851
852   session->value.cs_ts_index = cnat_timestamp_new (ctx->now);
853   cnat_bihash_add_del (&cnat_session_db, bkey, 1);
854
855   if (!(rsession_flags & CNAT_SESSION_FLAG_NO_CLIENT))
856     {
857       /* is this the first time we've seen this source address */
858       cc = (AF_IP4 == ctx->af ?
859               cnat_client_ip4_find (&session->value.cs_ip[VLIB_RX].ip4) :
860               cnat_client_ip6_find (&session->value.cs_ip[VLIB_RX].ip6));
861
862       if (NULL == cc)
863         {
864           ip_address_t addr;
865           uword *p;
866           u32 refcnt;
867
868           addr.version = ctx->af;
869           ip46_address_copy (&addr.ip, &session->value.cs_ip[VLIB_RX]);
870
871           /* Throttle */
872           clib_spinlock_lock (&cnat_client_db.throttle_lock);
873
874           p = hash_get_mem (cnat_client_db.throttle_mem, &addr);
875           if (p)
876             {
877               refcnt = p[0] + 1;
878               hash_set_mem (cnat_client_db.throttle_mem, &addr, refcnt);
879             }
880           else
881             hash_set_mem_alloc (&cnat_client_db.throttle_mem, &addr, 0);
882
883           clib_spinlock_unlock (&cnat_client_db.throttle_lock);
884
885           /* fire client create to the main thread */
886           if (!p)
887             vl_api_rpc_call_main_thread (cnat_client_learn, (u8 *) &addr,
888                                          sizeof (addr));
889         }
890       else
891         {
892           /* Refcount reverse session */
893           cnat_client_cnt_session (cc);
894         }
895     }
896
897   /* create the reverse flow key */
898   ip46_address_copy (&rsession->key.cs_ip[VLIB_RX],
899                      &session->value.cs_ip[VLIB_TX]);
900   ip46_address_copy (&rsession->key.cs_ip[VLIB_TX],
901                      &session->value.cs_ip[VLIB_RX]);
902   rsession->key.cs_proto = session->key.cs_proto;
903   rsession->key.cs_loc = rsession_location;
904   rsession->key.__cs_pad = 0;
905   rsession->key.cs_af = ctx->af;
906   rsession->key.cs_port[VLIB_RX] = session->value.cs_port[VLIB_TX];
907   rsession->key.cs_port[VLIB_TX] = session->value.cs_port[VLIB_RX];
908
909   /* First search for existing reverse session */
910   rv = cnat_bihash_search_i2 (&cnat_session_db, &rkey, &rvalue);
911   if (!rv)
912     {
913       /* Reverse session already exists
914          cleanup before creating for refcnts */
915       cnat_session_t *found_rsession = (cnat_session_t *) & rvalue;
916       cnat_session_free (found_rsession);
917     }
918   /* add the reverse flow */
919   ip46_address_copy (&rsession->value.cs_ip[VLIB_RX],
920                      &session->key.cs_ip[VLIB_TX]);
921   ip46_address_copy (&rsession->value.cs_ip[VLIB_TX],
922                      &session->key.cs_ip[VLIB_RX]);
923   rsession->value.cs_ts_index = session->value.cs_ts_index;
924   rsession->value.cs_lbi = INDEX_INVALID;
925   rsession->value.flags = rsession_flags;
926   rsession->value.cs_port[VLIB_TX] = session->key.cs_port[VLIB_RX];
927   rsession->value.cs_port[VLIB_RX] = session->key.cs_port[VLIB_TX];
928
929   cnat_bihash_add_del (&cnat_session_db, &rkey, 1);
930 }
931
932 always_inline uword
933 cnat_node_inline (vlib_main_t *vm, vlib_node_runtime_t *node,
934                   vlib_frame_t *frame, cnat_node_sub_t cnat_sub,
935                   ip_address_family_t af, cnat_session_location_t cs_loc,
936                   u8 do_trace)
937 {
938   u32 n_left, *from, thread_index;
939   vlib_buffer_t *bufs[VLIB_FRAME_SIZE];
940   vlib_buffer_t **b = bufs;
941   u16 nexts[VLIB_FRAME_SIZE], *next;
942   f64 now;
943
944   thread_index = vm->thread_index;
945   from = vlib_frame_vector_args (frame);
946   n_left = frame->n_vectors;
947   next = nexts;
948   vlib_get_buffers (vm, from, bufs, n_left);
949   now = vlib_time_now (vm);
950   cnat_session_t *session[4];
951   cnat_bihash_kv_t bkey[4], bvalue[4];
952   u64 hash[4];
953   int rv[4];
954
955   cnat_node_ctx_t ctx = { now, thread_index, af, do_trace };
956
957   if (n_left >= 8)
958     {
959       /* Kickstart our state */
960       cnat_session_make_key (b[3], af, cs_loc, &bkey[3]);
961       cnat_session_make_key (b[2], af, cs_loc, &bkey[2]);
962       cnat_session_make_key (b[1], af, cs_loc, &bkey[1]);
963       cnat_session_make_key (b[0], af, cs_loc, &bkey[0]);
964
965       hash[3] = cnat_bihash_hash (&bkey[3]);
966       hash[2] = cnat_bihash_hash (&bkey[2]);
967       hash[1] = cnat_bihash_hash (&bkey[1]);
968       hash[0] = cnat_bihash_hash (&bkey[0]);
969     }
970
971   while (n_left >= 8)
972     {
973       if (n_left >= 12)
974         {
975           vlib_prefetch_buffer_header (b[11], LOAD);
976           vlib_prefetch_buffer_header (b[10], LOAD);
977           vlib_prefetch_buffer_header (b[9], LOAD);
978           vlib_prefetch_buffer_header (b[8], LOAD);
979         }
980
981       rv[3] = cnat_bihash_search_i2_hash (&cnat_session_db, hash[3], &bkey[3],
982                                           &bvalue[3]);
983       session[3] = (cnat_session_t *) (rv[3] ? &bkey[3] : &bvalue[3]);
984       next[3] = cnat_sub (vm, node, b[3], &ctx, rv[3], session[3]);
985
986       rv[2] = cnat_bihash_search_i2_hash (&cnat_session_db, hash[2], &bkey[2],
987                                           &bvalue[2]);
988       session[2] = (cnat_session_t *) (rv[2] ? &bkey[2] : &bvalue[2]);
989       next[2] = cnat_sub (vm, node, b[2], &ctx, rv[2], session[2]);
990
991       rv[1] = cnat_bihash_search_i2_hash (&cnat_session_db, hash[1], &bkey[1],
992                                           &bvalue[1]);
993       session[1] = (cnat_session_t *) (rv[1] ? &bkey[1] : &bvalue[1]);
994       next[1] = cnat_sub (vm, node, b[1], &ctx, rv[1], session[1]);
995
996       rv[0] = cnat_bihash_search_i2_hash (&cnat_session_db, hash[0], &bkey[0],
997                                           &bvalue[0]);
998       session[0] = (cnat_session_t *) (rv[0] ? &bkey[0] : &bvalue[0]);
999       next[0] = cnat_sub (vm, node, b[0], &ctx, rv[0], session[0]);
1000
1001       cnat_session_make_key (b[7], af, cs_loc, &bkey[3]);
1002       cnat_session_make_key (b[6], af, cs_loc, &bkey[2]);
1003       cnat_session_make_key (b[5], af, cs_loc, &bkey[1]);
1004       cnat_session_make_key (b[4], af, cs_loc, &bkey[0]);
1005
1006       hash[3] = cnat_bihash_hash (&bkey[3]);
1007       hash[2] = cnat_bihash_hash (&bkey[2]);
1008       hash[1] = cnat_bihash_hash (&bkey[1]);
1009       hash[0] = cnat_bihash_hash (&bkey[0]);
1010
1011       cnat_bihash_prefetch_bucket (&cnat_session_db, hash[3]);
1012       cnat_bihash_prefetch_bucket (&cnat_session_db, hash[2]);
1013       cnat_bihash_prefetch_bucket (&cnat_session_db, hash[1]);
1014       cnat_bihash_prefetch_bucket (&cnat_session_db, hash[0]);
1015
1016       cnat_bihash_prefetch_data (&cnat_session_db, hash[3]);
1017       cnat_bihash_prefetch_data (&cnat_session_db, hash[2]);
1018       cnat_bihash_prefetch_data (&cnat_session_db, hash[1]);
1019       cnat_bihash_prefetch_data (&cnat_session_db, hash[0]);
1020
1021       b += 4;
1022       next += 4;
1023       n_left -= 4;
1024     }
1025
1026   while (n_left > 0)
1027     {
1028       cnat_session_make_key (b[0], af, cs_loc, &bkey[0]);
1029       rv[0] = cnat_bihash_search_i2 (&cnat_session_db, &bkey[0], &bvalue[0]);
1030
1031       session[0] = (cnat_session_t *) (rv[0] ? &bkey[0] : &bvalue[0]);
1032       next[0] = cnat_sub (vm, node, b[0], &ctx, rv[0], session[0]);
1033
1034       b++;
1035       next++;
1036       n_left--;
1037     }
1038
1039   vlib_buffer_enqueue_to_next (vm, node, from, nexts, frame->n_vectors);
1040
1041   return frame->n_vectors;
1042 }
1043
1044 /*
1045  * fd.io coding-style-patch-verification: ON
1046  *
1047  * Local Variables:
1048  * eval: (c-set-style "gnu")
1049  * End:
1050  */
1051
1052 #endif