cnat: Ip ICMP error 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 <cnat/cnat_session.h>
21 #include <cnat/cnat_client.h>
22
23 typedef uword (*cnat_node_sub_t) (vlib_main_t * vm,
24                                   vlib_node_runtime_t * node,
25                                   vlib_buffer_t * b,
26                                   cnat_node_ctx_t * ctx, int rv,
27                                   cnat_session_t * session);
28
29 static_always_inline u8
30 icmp_type_is_error_message (u8 icmp_type)
31 {
32   switch (icmp_type)
33     {
34     case ICMP4_destination_unreachable:
35     case ICMP4_time_exceeded:
36     case ICMP4_parameter_problem:
37     case ICMP4_source_quench:
38     case ICMP4_redirect:
39     case ICMP4_alternate_host_address:
40       return 1;
41     }
42   return 0;
43 }
44
45 static_always_inline u8
46 icmp6_type_is_error_message (u8 icmp_type)
47 {
48   switch (icmp_type)
49     {
50     case ICMP6_destination_unreachable:
51     case ICMP6_time_exceeded:
52     case ICMP6_parameter_problem:
53       return 1;
54     }
55   return 0;
56 }
57
58 static_always_inline u8
59 cmp_ip6_address (const ip6_address_t * a1, const ip6_address_t * a2)
60 {
61   return ((a1->as_u64[0] == a2->as_u64[0])
62           && (a1->as_u64[1] == a2->as_u64[1]));
63 }
64
65 /**
66  * Inline translation functions
67  */
68
69 static_always_inline u8
70 has_ip6_address (ip6_address_t * a)
71 {
72   return ((0 != a->as_u64[0]) || (0 != a->as_u64[1]));
73 }
74
75 static_always_inline void
76 cnat_ip4_translate_l4 (ip4_header_t * ip4, udp_header_t * udp,
77                        ip_csum_t * sum,
78                        ip4_address_t new_addr[VLIB_N_DIR],
79                        u16 new_port[VLIB_N_DIR])
80 {
81   u16 old_port[VLIB_N_DIR];
82   ip4_address_t old_addr[VLIB_N_DIR];
83
84   /* Fastpath no checksum */
85   if (PREDICT_TRUE (0 == *sum))
86     {
87       udp->dst_port = new_port[VLIB_TX];
88       udp->src_port = new_port[VLIB_RX];
89       return;
90     }
91
92   old_port[VLIB_TX] = udp->dst_port;
93   old_port[VLIB_RX] = udp->src_port;
94   old_addr[VLIB_TX] = ip4->dst_address;
95   old_addr[VLIB_RX] = ip4->src_address;
96
97   if (new_addr[VLIB_TX].as_u32)
98     {
99       *sum =
100         ip_csum_update (*sum, old_addr[VLIB_TX].as_u32,
101                         new_addr[VLIB_TX].as_u32, ip4_header_t, dst_address);
102     }
103   if (new_port[VLIB_TX])
104     {
105       udp->dst_port = new_port[VLIB_TX];
106       *sum = ip_csum_update (*sum, old_port[VLIB_TX], new_port[VLIB_TX],
107                              ip4_header_t /* cheat */ ,
108                              length /* changed member */ );
109     }
110   if (new_addr[VLIB_RX].as_u32)
111     {
112       *sum =
113         ip_csum_update (*sum, old_addr[VLIB_RX].as_u32,
114                         new_addr[VLIB_RX].as_u32, ip4_header_t, src_address);
115     }
116   if (new_port[VLIB_RX])
117     {
118       udp->src_port = new_port[VLIB_RX];
119       *sum = ip_csum_update (*sum, old_port[VLIB_RX], new_port[VLIB_RX],
120                              ip4_header_t /* cheat */ ,
121                              length /* changed member */ );
122     }
123 }
124
125 static_always_inline void
126 cnat_ip4_translate_l3 (ip4_header_t * ip4, ip4_address_t new_addr[VLIB_N_DIR])
127 {
128   ip4_address_t old_addr[VLIB_N_DIR];
129   ip_csum_t sum;
130
131   old_addr[VLIB_TX] = ip4->dst_address;
132   old_addr[VLIB_RX] = ip4->src_address;
133
134   sum = ip4->checksum;
135   if (new_addr[VLIB_TX].as_u32)
136     {
137       ip4->dst_address = new_addr[VLIB_TX];
138       sum =
139         ip_csum_update (sum, old_addr[VLIB_TX].as_u32,
140                         new_addr[VLIB_TX].as_u32, ip4_header_t, dst_address);
141     }
142   if (new_addr[VLIB_RX].as_u32)
143     {
144       ip4->src_address = new_addr[VLIB_RX];
145       sum =
146         ip_csum_update (sum, old_addr[VLIB_RX].as_u32,
147                         new_addr[VLIB_RX].as_u32, ip4_header_t, src_address);
148     }
149   ip4->checksum = ip_csum_fold (sum);
150 }
151
152 static_always_inline void
153 cnat_tcp_update_session_lifetime (tcp_header_t * tcp, u32 index)
154 {
155   cnat_main_t *cm = &cnat_main;
156   if (PREDICT_FALSE (tcp_fin (tcp)))
157     {
158       cnat_timestamp_set_lifetime (index, CNAT_DEFAULT_TCP_RST_TIMEOUT);
159     }
160
161   if (PREDICT_FALSE (tcp_rst (tcp)))
162     {
163       cnat_timestamp_set_lifetime (index, CNAT_DEFAULT_TCP_RST_TIMEOUT);
164     }
165
166   if (PREDICT_FALSE (tcp_syn (tcp) && tcp_ack (tcp)))
167     {
168       cnat_timestamp_set_lifetime (index, cm->tcp_max_age);
169     }
170 }
171
172 static_always_inline void
173 cnat_translation_icmp4 (ip4_header_t * outer_ip4, udp_header_t * outer_udp,
174                         ip4_address_t outer_new_addr[VLIB_N_DIR],
175                         u16 outer_new_port[VLIB_N_DIR], u8 snat_outer_ip)
176 {
177   icmp46_header_t *icmp = (icmp46_header_t *) outer_udp;
178   ip4_address_t new_addr[VLIB_N_DIR];
179   ip4_address_t old_addr[VLIB_N_DIR];
180   u16 new_port[VLIB_N_DIR];
181   u16 old_port[VLIB_N_DIR];
182   ip_csum_t sum, old_ip_sum, inner_l4_sum, inner_l4_old_sum;
183
184   if (!icmp_type_is_error_message (icmp->type))
185     return;
186
187   ip4_header_t *ip4 = (ip4_header_t *) (icmp + 2);
188   udp_header_t *udp = (udp_header_t *) (ip4 + 1);
189   tcp_header_t *tcp = (tcp_header_t *) udp;
190
191   /* Swap inner ports */
192   new_addr[VLIB_TX] = outer_new_addr[VLIB_RX];
193   new_addr[VLIB_RX] = outer_new_addr[VLIB_TX];
194   new_port[VLIB_TX] = outer_new_port[VLIB_RX];
195   new_port[VLIB_RX] = outer_new_port[VLIB_TX];
196
197   old_addr[VLIB_TX] = ip4->dst_address;
198   old_addr[VLIB_RX] = ip4->src_address;
199   old_port[VLIB_RX] = udp->src_port;
200   old_port[VLIB_TX] = udp->dst_port;
201
202   sum = icmp->checksum;
203   old_ip_sum = ip4->checksum;
204
205   /* translate outer ip. */
206   if (!snat_outer_ip)
207     outer_new_addr[VLIB_RX] = outer_ip4->src_address;
208   cnat_ip4_translate_l3 (outer_ip4, outer_new_addr);
209
210   if (ip4->protocol == IP_PROTOCOL_TCP)
211     {
212       inner_l4_old_sum = inner_l4_sum = tcp->checksum;
213       cnat_ip4_translate_l4 (ip4, udp, &inner_l4_sum, new_addr, new_port);
214       tcp->checksum = ip_csum_fold (inner_l4_sum);
215     }
216   else if (ip4->protocol == IP_PROTOCOL_UDP)
217     {
218       inner_l4_old_sum = inner_l4_sum = udp->checksum;
219       cnat_ip4_translate_l4 (ip4, udp, &inner_l4_sum, new_addr, new_port);
220       udp->checksum = ip_csum_fold (inner_l4_sum);
221     }
222   else
223     return;
224
225   /* UDP/TCP checksum changed */
226   sum = ip_csum_update (sum, inner_l4_old_sum, inner_l4_sum,
227                         ip4_header_t, checksum);
228
229   /* UDP/TCP Ports changed */
230   if (old_port[VLIB_TX] && new_port[VLIB_TX])
231     sum = ip_csum_update (sum, old_port[VLIB_TX], new_port[VLIB_TX],
232                           ip4_header_t /* cheat */ ,
233                           length /* changed member */ );
234
235   if (old_port[VLIB_RX] && new_port[VLIB_RX])
236     sum = ip_csum_update (sum, old_port[VLIB_RX], new_port[VLIB_RX],
237                           ip4_header_t /* cheat */ ,
238                           length /* changed member */ );
239
240
241   cnat_ip4_translate_l3 (ip4, new_addr);
242   ip_csum_t new_ip_sum = ip4->checksum;
243   /* IP checksum changed */
244   sum = ip_csum_update (sum, old_ip_sum, new_ip_sum, ip4_header_t, checksum);
245
246   /* IP src/dst addr changed */
247   if (new_addr[VLIB_TX].as_u32)
248     sum =
249       ip_csum_update (sum, old_addr[VLIB_TX].as_u32, new_addr[VLIB_TX].as_u32,
250                       ip4_header_t, dst_address);
251
252   if (new_addr[VLIB_RX].as_u32)
253     sum =
254       ip_csum_update (sum, old_addr[VLIB_RX].as_u32, new_addr[VLIB_RX].as_u32,
255                       ip4_header_t, src_address);
256
257   icmp->checksum = ip_csum_fold (sum);
258 }
259
260 static_always_inline void
261 cnat_translation_ip4 (const cnat_session_t * session,
262                       ip4_header_t * ip4, udp_header_t * udp)
263 {
264   tcp_header_t *tcp = (tcp_header_t *) udp;
265   ip4_address_t new_addr[VLIB_N_DIR];
266   u16 new_port[VLIB_N_DIR];
267
268   new_addr[VLIB_TX] = session->value.cs_ip[VLIB_TX].ip4;
269   new_addr[VLIB_RX] = session->value.cs_ip[VLIB_RX].ip4;
270   new_port[VLIB_TX] = session->value.cs_port[VLIB_TX];
271   new_port[VLIB_RX] = session->value.cs_port[VLIB_RX];
272
273   if (ip4->protocol == IP_PROTOCOL_TCP)
274     {
275       ip_csum_t sum = tcp->checksum;
276       cnat_ip4_translate_l4 (ip4, udp, &sum, new_addr, new_port);
277       tcp->checksum = ip_csum_fold (sum);
278       cnat_ip4_translate_l3 (ip4, new_addr);
279       cnat_tcp_update_session_lifetime (tcp, session->value.cs_ts_index);
280     }
281   else if (ip4->protocol == IP_PROTOCOL_UDP)
282     {
283       ip_csum_t sum = udp->checksum;
284       cnat_ip4_translate_l4 (ip4, udp, &sum, new_addr, new_port);
285       udp->checksum = ip_csum_fold (sum);
286       cnat_ip4_translate_l3 (ip4, new_addr);
287     }
288   else if (ip4->protocol == IP_PROTOCOL_ICMP)
289     {
290       /* SNAT only if src_addr was translated */
291       u8 snat_outer_ip =
292         (ip4->src_address.as_u32 == session->key.cs_ip[VLIB_RX].ip4.as_u32);
293       cnat_translation_icmp4 (ip4, udp, new_addr, new_port, snat_outer_ip);
294     }
295 }
296
297 static_always_inline void
298 cnat_ip6_translate_l3 (ip6_header_t * ip6, ip6_address_t new_addr[VLIB_N_DIR])
299 {
300   if (has_ip6_address (&new_addr[VLIB_TX]))
301     ip6_address_copy (&ip6->dst_address, &new_addr[VLIB_TX]);
302   if (has_ip6_address (&new_addr[VLIB_RX]))
303     ip6_address_copy (&ip6->src_address, &new_addr[VLIB_RX]);
304 }
305
306 static_always_inline void
307 cnat_ip6_translate_l4 (ip6_header_t * ip6, udp_header_t * udp,
308                        ip_csum_t * sum,
309                        ip6_address_t new_addr[VLIB_N_DIR],
310                        u16 new_port[VLIB_N_DIR])
311 {
312   u16 old_port[VLIB_N_DIR];
313   ip6_address_t old_addr[VLIB_N_DIR];
314
315   /* Fastpath no checksum */
316   if (PREDICT_TRUE (0 == *sum))
317     {
318       udp->dst_port = new_port[VLIB_TX];
319       udp->src_port = new_port[VLIB_RX];
320       return;
321     }
322
323   old_port[VLIB_TX] = udp->dst_port;
324   old_port[VLIB_RX] = udp->src_port;
325   ip6_address_copy (&old_addr[VLIB_TX], &ip6->dst_address);
326   ip6_address_copy (&old_addr[VLIB_RX], &ip6->src_address);
327
328   if (has_ip6_address (&new_addr[VLIB_TX]))
329     {
330       *sum = ip_csum_add_even (*sum, new_addr[VLIB_TX].as_u64[0]);
331       *sum = ip_csum_add_even (*sum, new_addr[VLIB_TX].as_u64[1]);
332       *sum = ip_csum_sub_even (*sum, old_addr[VLIB_TX].as_u64[0]);
333       *sum = ip_csum_sub_even (*sum, old_addr[VLIB_TX].as_u64[1]);
334     }
335
336   if (new_port[VLIB_TX])
337     {
338       udp->dst_port = new_port[VLIB_TX];
339       *sum = ip_csum_update (*sum, old_port[VLIB_TX], new_port[VLIB_TX],
340                              ip4_header_t /* cheat */ ,
341                              length /* changed member */ );
342     }
343   if (has_ip6_address (&new_addr[VLIB_RX]))
344     {
345       *sum = ip_csum_add_even (*sum, new_addr[VLIB_RX].as_u64[0]);
346       *sum = ip_csum_add_even (*sum, new_addr[VLIB_RX].as_u64[1]);
347       *sum = ip_csum_sub_even (*sum, old_addr[VLIB_RX].as_u64[0]);
348       *sum = ip_csum_sub_even (*sum, old_addr[VLIB_RX].as_u64[1]);
349     }
350
351   if (new_port[VLIB_RX])
352     {
353       udp->src_port = new_port[VLIB_RX];
354       *sum = ip_csum_update (*sum, old_port[VLIB_RX], new_port[VLIB_RX],
355                              ip4_header_t /* cheat */ ,
356                              length /* changed member */ );
357     }
358 }
359
360 static_always_inline void
361 cnat_translation_icmp6 (ip6_header_t * outer_ip6, udp_header_t * outer_udp,
362                         ip6_address_t outer_new_addr[VLIB_N_DIR],
363                         u16 outer_new_port[VLIB_N_DIR], u8 snat_outer_ip)
364 {
365   icmp46_header_t *icmp = (icmp46_header_t *) outer_udp;
366   ip6_address_t new_addr[VLIB_N_DIR];
367   ip6_address_t old_addr[VLIB_N_DIR];
368   ip6_address_t outer_old_addr[VLIB_N_DIR];
369   u16 new_port[VLIB_N_DIR];
370   u16 old_port[VLIB_N_DIR];
371   ip_csum_t sum, inner_l4_sum, inner_l4_old_sum;
372
373   if (!icmp6_type_is_error_message (icmp->type))
374     return;
375
376   ip6_header_t *ip6 = (ip6_header_t *) (icmp + 2);
377   udp_header_t *udp = (udp_header_t *) (ip6 + 1);
378   tcp_header_t *tcp = (tcp_header_t *) udp;
379
380   /* Swap inner ports */
381   ip6_address_copy (&new_addr[VLIB_RX], &outer_new_addr[VLIB_TX]);
382   ip6_address_copy (&new_addr[VLIB_TX], &outer_new_addr[VLIB_RX]);
383   new_port[VLIB_TX] = outer_new_port[VLIB_RX];
384   new_port[VLIB_RX] = outer_new_port[VLIB_TX];
385
386   ip6_address_copy (&old_addr[VLIB_TX], &ip6->dst_address);
387   ip6_address_copy (&old_addr[VLIB_RX], &ip6->src_address);
388   old_port[VLIB_RX] = udp->src_port;
389   old_port[VLIB_TX] = udp->dst_port;
390
391   sum = icmp->checksum;
392   /* Translate outer ip */
393   ip6_address_copy (&outer_old_addr[VLIB_TX], &outer_ip6->dst_address);
394   ip6_address_copy (&outer_old_addr[VLIB_RX], &outer_ip6->src_address);
395   if (!snat_outer_ip)
396     ip6_address_copy (&outer_new_addr[VLIB_RX], &outer_ip6->src_address);
397   cnat_ip6_translate_l3 (outer_ip6, outer_new_addr);
398   if (has_ip6_address (&outer_new_addr[VLIB_TX]))
399     {
400       sum = ip_csum_add_even (sum, outer_new_addr[VLIB_TX].as_u64[0]);
401       sum = ip_csum_add_even (sum, outer_new_addr[VLIB_TX].as_u64[1]);
402       sum = ip_csum_sub_even (sum, outer_old_addr[VLIB_TX].as_u64[0]);
403       sum = ip_csum_sub_even (sum, outer_old_addr[VLIB_TX].as_u64[1]);
404     }
405
406   if (has_ip6_address (&outer_new_addr[VLIB_RX]))
407     {
408       sum = ip_csum_add_even (sum, outer_new_addr[VLIB_RX].as_u64[0]);
409       sum = ip_csum_add_even (sum, outer_new_addr[VLIB_RX].as_u64[1]);
410       sum = ip_csum_sub_even (sum, outer_old_addr[VLIB_RX].as_u64[0]);
411       sum = ip_csum_sub_even (sum, outer_old_addr[VLIB_RX].as_u64[1]);
412     }
413
414   if (ip6->protocol == IP_PROTOCOL_TCP)
415     {
416       inner_l4_old_sum = inner_l4_sum = tcp->checksum;
417       cnat_ip6_translate_l4 (ip6, udp, &inner_l4_sum, new_addr, new_port);
418       tcp->checksum = ip_csum_fold (inner_l4_sum);
419     }
420   else if (ip6->protocol == IP_PROTOCOL_UDP)
421     {
422       inner_l4_old_sum = inner_l4_sum = udp->checksum;
423       cnat_ip6_translate_l4 (ip6, udp, &inner_l4_sum, new_addr, new_port);
424       udp->checksum = ip_csum_fold (inner_l4_sum);
425     }
426   else
427     return;
428
429   /* UDP/TCP checksum changed */
430   sum = ip_csum_update (sum, inner_l4_old_sum, inner_l4_sum,
431                         ip4_header_t /* cheat */ ,
432                         checksum);
433
434   /* UDP/TCP Ports changed */
435   if (old_port[VLIB_TX] && new_port[VLIB_TX])
436     sum = ip_csum_update (sum, old_port[VLIB_TX], new_port[VLIB_TX],
437                           ip4_header_t /* cheat */ ,
438                           length /* changed member */ );
439
440   if (old_port[VLIB_RX] && new_port[VLIB_RX])
441     sum = ip_csum_update (sum, old_port[VLIB_RX], new_port[VLIB_RX],
442                           ip4_header_t /* cheat */ ,
443                           length /* changed member */ );
444
445
446   cnat_ip6_translate_l3 (ip6, new_addr);
447   /* IP src/dst addr changed */
448   if (has_ip6_address (&new_addr[VLIB_TX]))
449     {
450       sum = ip_csum_add_even (sum, new_addr[VLIB_TX].as_u64[0]);
451       sum = ip_csum_add_even (sum, new_addr[VLIB_TX].as_u64[1]);
452       sum = ip_csum_sub_even (sum, old_addr[VLIB_TX].as_u64[0]);
453       sum = ip_csum_sub_even (sum, old_addr[VLIB_TX].as_u64[1]);
454     }
455
456   if (has_ip6_address (&new_addr[VLIB_RX]))
457     {
458       sum = ip_csum_add_even (sum, new_addr[VLIB_RX].as_u64[0]);
459       sum = ip_csum_add_even (sum, new_addr[VLIB_RX].as_u64[1]);
460       sum = ip_csum_sub_even (sum, old_addr[VLIB_RX].as_u64[0]);
461       sum = ip_csum_sub_even (sum, old_addr[VLIB_RX].as_u64[1]);
462     }
463
464   icmp->checksum = ip_csum_fold (sum);
465 }
466
467 static_always_inline void
468 cnat_translation_ip6 (const cnat_session_t * session,
469                       ip6_header_t * ip6, udp_header_t * udp)
470 {
471   tcp_header_t *tcp = (tcp_header_t *) udp;
472   ip6_address_t new_addr[VLIB_N_DIR];
473   u16 new_port[VLIB_N_DIR];
474
475   ip6_address_copy (&new_addr[VLIB_TX], &session->value.cs_ip[VLIB_TX].ip6);
476   ip6_address_copy (&new_addr[VLIB_RX], &session->value.cs_ip[VLIB_RX].ip6);
477   new_port[VLIB_TX] = session->value.cs_port[VLIB_TX];
478   new_port[VLIB_RX] = session->value.cs_port[VLIB_RX];
479
480   if (ip6->protocol == IP_PROTOCOL_TCP)
481     {
482       ip_csum_t sum = tcp->checksum;
483       cnat_ip6_translate_l4 (ip6, udp, &sum, new_addr, new_port);
484       tcp->checksum = ip_csum_fold (sum);
485       cnat_ip6_translate_l3 (ip6, new_addr);
486       cnat_tcp_update_session_lifetime (tcp, session->value.cs_ts_index);
487     }
488   else if (ip6->protocol == IP_PROTOCOL_UDP)
489     {
490       ip_csum_t sum = udp->checksum;
491       cnat_ip6_translate_l4 (ip6, udp, &sum, new_addr, new_port);
492       udp->checksum = ip_csum_fold (sum);
493       cnat_ip6_translate_l3 (ip6, new_addr);
494     }
495   else if (ip6->protocol == IP_PROTOCOL_ICMP6)
496     {
497       /* SNAT only if src_addr was translated */
498       u8 snat_outer_ip = cmp_ip6_address (&ip6->src_address,
499                                           &session->key.cs_ip[VLIB_RX].ip6);
500       cnat_translation_icmp6 (ip6, udp, new_addr, new_port, snat_outer_ip);
501     }
502 }
503
504 static_always_inline void
505 cnat_session_make_key (vlib_buffer_t * b, ip_address_family_t af,
506                        clib_bihash_kv_40_48_t * bkey)
507 {
508   udp_header_t *udp;
509   cnat_session_t *session = (cnat_session_t *) bkey;
510   session->key.cs_af = af;
511   session->key.__cs_pad[0] = 0;
512   session->key.__cs_pad[1] = 0;
513   if (AF_IP4 == af)
514     {
515       ip4_header_t *ip4;
516       ip4 = vlib_buffer_get_current (b);
517       if (PREDICT_FALSE (ip4->protocol == IP_PROTOCOL_ICMP))
518         {
519           icmp46_header_t *icmp = (icmp46_header_t *) (ip4 + 1);
520           if (!icmp_type_is_error_message (icmp->type))
521             goto error;
522           ip4 = (ip4_header_t *) (icmp + 2);    /* Use inner packet */
523           udp = (udp_header_t *) (ip4 + 1);
524           /* Swap dst & src for search as ICMP payload is reversed */
525           ip46_address_set_ip4 (&session->key.cs_ip[VLIB_RX],
526                                 &ip4->dst_address);
527           ip46_address_set_ip4 (&session->key.cs_ip[VLIB_TX],
528                                 &ip4->src_address);
529           session->key.cs_proto = ip4->protocol;
530           session->key.cs_port[VLIB_TX] = udp->src_port;
531           session->key.cs_port[VLIB_RX] = udp->dst_port;
532         }
533       else
534         {
535           udp = (udp_header_t *) (ip4 + 1);
536           ip46_address_set_ip4 (&session->key.cs_ip[VLIB_TX],
537                                 &ip4->dst_address);
538           ip46_address_set_ip4 (&session->key.cs_ip[VLIB_RX],
539                                 &ip4->src_address);
540           session->key.cs_proto = ip4->protocol;
541           session->key.cs_port[VLIB_RX] = udp->src_port;
542           session->key.cs_port[VLIB_TX] = udp->dst_port;
543         }
544
545     }
546   else
547     {
548       ip6_header_t *ip6;
549       ip6 = vlib_buffer_get_current (b);
550       if (PREDICT_FALSE (ip6->protocol == IP_PROTOCOL_ICMP6))
551         {
552           icmp46_header_t *icmp = (icmp46_header_t *) (ip6 + 1);
553           if (!icmp6_type_is_error_message (icmp->type))
554             goto error;
555           ip6 = (ip6_header_t *) (icmp + 2);    /* Use inner packet */
556           udp = (udp_header_t *) (ip6 + 1);
557           /* Swap dst & src for search as ICMP payload is reversed */
558           ip46_address_set_ip6 (&session->key.cs_ip[VLIB_RX],
559                                 &ip6->dst_address);
560           ip46_address_set_ip6 (&session->key.cs_ip[VLIB_TX],
561                                 &ip6->src_address);
562           session->key.cs_proto = ip6->protocol;
563           session->key.cs_port[VLIB_TX] = udp->src_port;
564           session->key.cs_port[VLIB_RX] = udp->dst_port;
565         }
566       else
567         {
568           udp = (udp_header_t *) (ip6 + 1);
569           ip46_address_set_ip6 (&session->key.cs_ip[VLIB_TX],
570                                 &ip6->dst_address);
571           ip46_address_set_ip6 (&session->key.cs_ip[VLIB_RX],
572                                 &ip6->src_address);
573           session->key.cs_port[VLIB_RX] = udp->src_port;
574           session->key.cs_port[VLIB_TX] = udp->dst_port;
575           session->key.cs_proto = ip6->protocol;
576         }
577     }
578   return;
579
580 error:
581   /* Ensure we dont find anything */
582   session->key.cs_proto = 0;
583   return;
584 }
585
586 /**
587  * Create NAT sessions
588  */
589
590 static_always_inline void
591 cnat_session_create (cnat_session_t * session, cnat_node_ctx_t * ctx,
592                      u8 rsession_flags)
593 {
594   cnat_client_t *cc;
595   clib_bihash_kv_40_48_t rkey;
596   cnat_session_t *rsession = (cnat_session_t *) & rkey;
597   clib_bihash_kv_40_48_t *bkey = (clib_bihash_kv_40_48_t *) session;
598   clib_bihash_kv_40_48_t rvalue;
599   int rv;
600
601   session->value.cs_ts_index = cnat_timestamp_new (ctx->now);
602   clib_bihash_add_del_40_48 (&cnat_session_db, bkey, 1);
603
604   /* is this the first time we've seen this source address */
605   cc = (AF_IP4 == ctx->af ?
606         cnat_client_ip4_find (&session->value.cs_ip[VLIB_RX].ip4) :
607         cnat_client_ip6_find (&session->value.cs_ip[VLIB_RX].ip6));
608
609   if (NULL == cc)
610     {
611       u64 r0 = 17;
612       if (AF_IP4 == ctx->af)
613         r0 = (u64) session->value.cs_ip[VLIB_RX].ip4.as_u32;
614       else
615         {
616           r0 = r0 * 31 + session->value.cs_ip[VLIB_RX].ip6.as_u64[0];
617           r0 = r0 * 31 + session->value.cs_ip[VLIB_RX].ip6.as_u64[1];
618         }
619
620       /* Rate limit */
621       if (!throttle_check (&cnat_throttle, ctx->thread_index, r0, ctx->seed))
622         {
623           cnat_learn_arg_t l;
624           l.addr.version = ctx->af;
625           ip46_address_copy (&l.addr.ip, &session->value.cs_ip[VLIB_RX]);
626           /* fire client create to the main thread */
627           vl_api_rpc_call_main_thread (cnat_client_learn,
628                                        (u8 *) & l, sizeof (l));
629         }
630       else
631         {
632           /* Will still need to count those for session refcnt */
633           ip_address_t *addr;
634           clib_spinlock_lock (&cnat_client_db.throttle_pool_lock
635                               [ctx->thread_index]);
636           pool_get (cnat_client_db.throttle_pool[ctx->thread_index], addr);
637           addr->version = ctx->af;
638           ip46_address_copy (&addr->ip, &session->value.cs_ip[VLIB_RX]);
639           clib_spinlock_unlock (&cnat_client_db.throttle_pool_lock
640                                 [ctx->thread_index]);
641         }
642     }
643   else
644     {
645       /* Refcount reverse session */
646       cnat_client_cnt_session (cc);
647     }
648
649   /* create the reverse flow key */
650   ip46_address_copy (&rsession->key.cs_ip[VLIB_RX],
651                      &session->value.cs_ip[VLIB_TX]);
652   ip46_address_copy (&rsession->key.cs_ip[VLIB_TX],
653                      &session->value.cs_ip[VLIB_RX]);
654   rsession->key.cs_proto = session->key.cs_proto;
655   rsession->key.__cs_pad[0] = 0;
656   rsession->key.__cs_pad[1] = 0;
657   rsession->key.cs_af = ctx->af;
658   rsession->key.cs_port[VLIB_RX] = session->value.cs_port[VLIB_TX];
659   rsession->key.cs_port[VLIB_TX] = session->value.cs_port[VLIB_RX];
660
661   /* First search for existing reverse session */
662   rv = clib_bihash_search_inline_2_40_48 (&cnat_session_db, &rkey, &rvalue);
663   if (!rv)
664     {
665       /* Reverse session already exists
666          cleanup before creating for refcnts */
667       cnat_session_t *found_rsession = (cnat_session_t *) & rvalue;
668       cnat_session_free (found_rsession);
669     }
670   /* add the reverse flow */
671   ip46_address_copy (&rsession->value.cs_ip[VLIB_RX],
672                      &session->key.cs_ip[VLIB_TX]);
673   ip46_address_copy (&rsession->value.cs_ip[VLIB_TX],
674                      &session->key.cs_ip[VLIB_RX]);
675   rsession->value.cs_ts_index = session->value.cs_ts_index;
676   rsession->value.cs_lbi = INDEX_INVALID;
677   rsession->value.flags = rsession_flags;
678   rsession->value.cs_port[VLIB_TX] = session->key.cs_port[VLIB_RX];
679   rsession->value.cs_port[VLIB_RX] = session->key.cs_port[VLIB_TX];
680
681   clib_bihash_add_del_40_48 (&cnat_session_db, &rkey, 1);
682 }
683
684 always_inline uword
685 cnat_node_inline (vlib_main_t * vm,
686                   vlib_node_runtime_t * node,
687                   vlib_frame_t * frame,
688                   cnat_node_sub_t cnat_sub,
689                   ip_address_family_t af, u8 do_trace)
690 {
691   u32 n_left, *from, thread_index;
692   vlib_buffer_t *bufs[VLIB_FRAME_SIZE];
693   vlib_buffer_t **b = bufs;
694   u16 nexts[VLIB_FRAME_SIZE], *next;
695   f64 now;
696   u64 seed;
697
698   thread_index = vm->thread_index;
699   from = vlib_frame_vector_args (frame);
700   n_left = frame->n_vectors;
701   next = nexts;
702   vlib_get_buffers (vm, from, bufs, n_left);
703   now = vlib_time_now (vm);
704   seed = throttle_seed (&cnat_throttle, thread_index, vlib_time_now (vm));
705   cnat_session_t *session[4];
706   clib_bihash_kv_40_48_t bkey[4], bvalue[4];
707   u64 hash[4];
708   int rv[4];
709
710   cnat_node_ctx_t ctx = { now, seed, thread_index, af, do_trace };
711
712   if (n_left >= 8)
713     {
714       /* Kickstart our state */
715       cnat_session_make_key (b[3], af, &bkey[3]);
716       cnat_session_make_key (b[2], af, &bkey[2]);
717       cnat_session_make_key (b[1], af, &bkey[1]);
718       cnat_session_make_key (b[0], af, &bkey[0]);
719
720       hash[3] = clib_bihash_hash_40_48 (&bkey[3]);
721       hash[2] = clib_bihash_hash_40_48 (&bkey[2]);
722       hash[1] = clib_bihash_hash_40_48 (&bkey[1]);
723       hash[0] = clib_bihash_hash_40_48 (&bkey[0]);
724     }
725
726   while (n_left >= 8)
727     {
728       if (n_left >= 12)
729         {
730           vlib_prefetch_buffer_header (b[11], LOAD);
731           vlib_prefetch_buffer_header (b[10], LOAD);
732           vlib_prefetch_buffer_header (b[9], LOAD);
733           vlib_prefetch_buffer_header (b[8], LOAD);
734         }
735
736       rv[3] =
737         clib_bihash_search_inline_2_with_hash_40_48 (&cnat_session_db,
738                                                      hash[3], &bkey[3],
739                                                      &bvalue[3]);
740       session[3] = (cnat_session_t *) (rv[3] ? &bkey[3] : &bvalue[3]);
741       next[3] = cnat_sub (vm, node, b[3], &ctx, rv[3], session[3]);
742
743       rv[2] =
744         clib_bihash_search_inline_2_with_hash_40_48 (&cnat_session_db,
745                                                      hash[2], &bkey[2],
746                                                      &bvalue[2]);
747       session[2] = (cnat_session_t *) (rv[2] ? &bkey[2] : &bvalue[2]);
748       next[2] = cnat_sub (vm, node, b[2], &ctx, rv[2], session[2]);
749
750       rv[1] =
751         clib_bihash_search_inline_2_with_hash_40_48 (&cnat_session_db,
752                                                      hash[1], &bkey[1],
753                                                      &bvalue[1]);
754       session[1] = (cnat_session_t *) (rv[1] ? &bkey[1] : &bvalue[1]);
755       next[1] = cnat_sub (vm, node, b[1], &ctx, rv[1], session[1]);
756
757       rv[0] =
758         clib_bihash_search_inline_2_with_hash_40_48 (&cnat_session_db,
759                                                      hash[0], &bkey[0],
760                                                      &bvalue[0]);
761       session[0] = (cnat_session_t *) (rv[0] ? &bkey[0] : &bvalue[0]);
762       next[0] = cnat_sub (vm, node, b[0], &ctx, rv[0], session[0]);
763
764       cnat_session_make_key (b[7], af, &bkey[3]);
765       cnat_session_make_key (b[6], af, &bkey[2]);
766       cnat_session_make_key (b[5], af, &bkey[1]);
767       cnat_session_make_key (b[4], af, &bkey[0]);
768
769       hash[3] = clib_bihash_hash_40_48 (&bkey[3]);
770       hash[2] = clib_bihash_hash_40_48 (&bkey[2]);
771       hash[1] = clib_bihash_hash_40_48 (&bkey[1]);
772       hash[0] = clib_bihash_hash_40_48 (&bkey[0]);
773
774       clib_bihash_prefetch_bucket_40_48 (&cnat_session_db, hash[3]);
775       clib_bihash_prefetch_bucket_40_48 (&cnat_session_db, hash[2]);
776       clib_bihash_prefetch_bucket_40_48 (&cnat_session_db, hash[1]);
777       clib_bihash_prefetch_bucket_40_48 (&cnat_session_db, hash[0]);
778
779       clib_bihash_prefetch_data_40_48 (&cnat_session_db, hash[3]);
780       clib_bihash_prefetch_data_40_48 (&cnat_session_db, hash[2]);
781       clib_bihash_prefetch_data_40_48 (&cnat_session_db, hash[1]);
782       clib_bihash_prefetch_data_40_48 (&cnat_session_db, hash[0]);
783
784       b += 4;
785       next += 4;
786       n_left -= 4;
787     }
788
789   while (n_left > 0)
790     {
791       cnat_session_make_key (b[0], af, &bkey[0]);
792       rv[0] = clib_bihash_search_inline_2_40_48 (&cnat_session_db,
793                                                  &bkey[0], &bvalue[0]);
794
795       session[0] = (cnat_session_t *) (rv[0] ? &bkey[0] : &bvalue[0]);
796       next[0] = cnat_sub (vm, node, b[0], &ctx, rv[0], session[0]);
797
798       b++;
799       next++;
800       n_left--;
801     }
802
803   vlib_buffer_enqueue_to_next (vm, node, from, nexts, frame->n_vectors);
804
805   return frame->n_vectors;
806 }
807
808 /*
809  * fd.io coding-style-patch-verification: ON
810  *
811  * Local Variables:
812  * eval: (c-set-style "gnu")
813  * End:
814  */
815
816 #endif