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:
7 * http://www.apache.org/licenses/LICENSE-2.0
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.
16 #ifndef __CNAT_NODE_H__
17 #define __CNAT_NODE_H__
19 #include <vlibmemory/api.h>
20 #include <cnat/cnat_session.h>
21 #include <cnat/cnat_client.h>
23 typedef uword (*cnat_node_sub_t) (vlib_main_t * vm,
24 vlib_node_runtime_t * node,
26 cnat_node_ctx_t * ctx, int rv,
27 cnat_session_t * session);
29 static_always_inline u8
30 icmp_type_is_error_message (u8 icmp_type)
34 case ICMP4_destination_unreachable:
35 case ICMP4_time_exceeded:
36 case ICMP4_parameter_problem:
37 case ICMP4_source_quench:
39 case ICMP4_alternate_host_address:
45 static_always_inline u8
46 icmp_type_is_echo (u8 icmp_type)
50 case ICMP4_echo_request:
51 case ICMP4_echo_reply:
57 static_always_inline u8
58 icmp6_type_is_echo (u8 icmp_type)
62 case ICMP6_echo_request:
63 case ICMP6_echo_reply:
69 static_always_inline u8
70 icmp6_type_is_error_message (u8 icmp_type)
74 case ICMP6_destination_unreachable:
75 case ICMP6_time_exceeded:
76 case ICMP6_parameter_problem:
82 static_always_inline u8
83 cmp_ip6_address (const ip6_address_t * a1, const ip6_address_t * a2)
85 return ((a1->as_u64[0] == a2->as_u64[0])
86 && (a1->as_u64[1] == a2->as_u64[1]));
90 * Inline translation functions
93 static_always_inline u8
94 has_ip6_address (ip6_address_t * a)
96 return ((0 != a->as_u64[0]) || (0 != a->as_u64[1]));
99 static_always_inline void
100 cnat_ip4_translate_l4 (ip4_header_t * ip4, udp_header_t * udp,
102 ip4_address_t new_addr[VLIB_N_DIR],
103 u16 new_port[VLIB_N_DIR])
105 u16 old_port[VLIB_N_DIR];
106 ip4_address_t old_addr[VLIB_N_DIR];
108 /* Fastpath no checksum */
109 if (PREDICT_TRUE (0 == *sum))
111 udp->dst_port = new_port[VLIB_TX];
112 udp->src_port = new_port[VLIB_RX];
116 old_port[VLIB_TX] = udp->dst_port;
117 old_port[VLIB_RX] = udp->src_port;
118 old_addr[VLIB_TX] = ip4->dst_address;
119 old_addr[VLIB_RX] = ip4->src_address;
121 if (new_addr[VLIB_TX].as_u32)
124 ip_csum_update (*sum, old_addr[VLIB_TX].as_u32,
125 new_addr[VLIB_TX].as_u32, ip4_header_t, dst_address);
127 if (new_port[VLIB_TX])
129 udp->dst_port = new_port[VLIB_TX];
130 *sum = ip_csum_update (*sum, old_port[VLIB_TX], new_port[VLIB_TX],
131 ip4_header_t /* cheat */ ,
132 length /* changed member */ );
134 if (new_addr[VLIB_RX].as_u32)
137 ip_csum_update (*sum, old_addr[VLIB_RX].as_u32,
138 new_addr[VLIB_RX].as_u32, ip4_header_t, src_address);
140 if (new_port[VLIB_RX])
142 udp->src_port = new_port[VLIB_RX];
143 *sum = ip_csum_update (*sum, old_port[VLIB_RX], new_port[VLIB_RX],
144 ip4_header_t /* cheat */ ,
145 length /* changed member */ );
149 static_always_inline void
150 cnat_ip4_translate_l3 (ip4_header_t * ip4, ip4_address_t new_addr[VLIB_N_DIR])
152 ip4_address_t old_addr[VLIB_N_DIR];
155 old_addr[VLIB_TX] = ip4->dst_address;
156 old_addr[VLIB_RX] = ip4->src_address;
159 if (new_addr[VLIB_TX].as_u32)
161 ip4->dst_address = new_addr[VLIB_TX];
163 ip_csum_update (sum, old_addr[VLIB_TX].as_u32,
164 new_addr[VLIB_TX].as_u32, ip4_header_t, dst_address);
166 if (new_addr[VLIB_RX].as_u32)
168 ip4->src_address = new_addr[VLIB_RX];
170 ip_csum_update (sum, old_addr[VLIB_RX].as_u32,
171 new_addr[VLIB_RX].as_u32, ip4_header_t, src_address);
173 ip4->checksum = ip_csum_fold (sum);
176 static_always_inline void
177 cnat_tcp_update_session_lifetime (tcp_header_t * tcp, u32 index)
179 cnat_main_t *cm = &cnat_main;
180 if (PREDICT_FALSE (tcp_fin (tcp)))
182 cnat_timestamp_set_lifetime (index, CNAT_DEFAULT_TCP_RST_TIMEOUT);
185 if (PREDICT_FALSE (tcp_rst (tcp)))
187 cnat_timestamp_set_lifetime (index, CNAT_DEFAULT_TCP_RST_TIMEOUT);
190 if (PREDICT_FALSE (tcp_syn (tcp) && tcp_ack (tcp)))
192 cnat_timestamp_set_lifetime (index, cm->tcp_max_age);
196 static_always_inline void
197 cnat_translation_icmp4_echo (ip4_header_t * ip4, icmp46_header_t * icmp,
198 ip4_address_t new_addr[VLIB_N_DIR],
199 u16 new_port[VLIB_N_DIR])
203 cnat_echo_header_t *echo = (cnat_echo_header_t *) (icmp + 1);
205 cnat_ip4_translate_l3 (ip4, new_addr);
206 old_port = echo->identifier;
207 echo->identifier = new_port[VLIB_RX];
209 sum = icmp->checksum;
210 sum = ip_csum_update (sum, old_port, new_port[VLIB_RX],
211 ip4_header_t /* cheat */ ,
212 length /* changed member */ );
214 icmp->checksum = ip_csum_fold (sum);
217 static_always_inline void
218 cnat_translation_icmp4_error (ip4_header_t * outer_ip4,
219 icmp46_header_t * icmp,
220 ip4_address_t outer_new_addr[VLIB_N_DIR],
221 u16 outer_new_port[VLIB_N_DIR],
224 ip4_address_t new_addr[VLIB_N_DIR];
225 ip4_address_t old_addr[VLIB_N_DIR];
226 u16 new_port[VLIB_N_DIR];
227 u16 old_port[VLIB_N_DIR];
228 ip_csum_t sum, old_ip_sum, inner_l4_sum, inner_l4_old_sum;
230 ip4_header_t *ip4 = (ip4_header_t *) (icmp + 2);
231 udp_header_t *udp = (udp_header_t *) (ip4 + 1);
232 tcp_header_t *tcp = (tcp_header_t *) udp;
234 /* Swap inner ports */
235 new_addr[VLIB_TX] = outer_new_addr[VLIB_RX];
236 new_addr[VLIB_RX] = outer_new_addr[VLIB_TX];
237 new_port[VLIB_TX] = outer_new_port[VLIB_RX];
238 new_port[VLIB_RX] = outer_new_port[VLIB_TX];
240 old_addr[VLIB_TX] = ip4->dst_address;
241 old_addr[VLIB_RX] = ip4->src_address;
242 old_port[VLIB_RX] = udp->src_port;
243 old_port[VLIB_TX] = udp->dst_port;
245 sum = icmp->checksum;
246 old_ip_sum = ip4->checksum;
248 /* translate outer ip. */
250 outer_new_addr[VLIB_RX] = outer_ip4->src_address;
251 cnat_ip4_translate_l3 (outer_ip4, outer_new_addr);
253 if (ip4->protocol == IP_PROTOCOL_TCP)
255 inner_l4_old_sum = inner_l4_sum = tcp->checksum;
256 cnat_ip4_translate_l4 (ip4, udp, &inner_l4_sum, new_addr, new_port);
257 tcp->checksum = ip_csum_fold (inner_l4_sum);
259 else if (ip4->protocol == IP_PROTOCOL_UDP)
261 inner_l4_old_sum = inner_l4_sum = udp->checksum;
262 cnat_ip4_translate_l4 (ip4, udp, &inner_l4_sum, new_addr, new_port);
263 udp->checksum = ip_csum_fold (inner_l4_sum);
268 /* UDP/TCP checksum changed */
269 sum = ip_csum_update (sum, inner_l4_old_sum, inner_l4_sum,
270 ip4_header_t, checksum);
272 /* UDP/TCP Ports changed */
273 if (old_port[VLIB_TX] && new_port[VLIB_TX])
274 sum = ip_csum_update (sum, old_port[VLIB_TX], new_port[VLIB_TX],
275 ip4_header_t /* cheat */ ,
276 length /* changed member */ );
278 if (old_port[VLIB_RX] && new_port[VLIB_RX])
279 sum = ip_csum_update (sum, old_port[VLIB_RX], new_port[VLIB_RX],
280 ip4_header_t /* cheat */ ,
281 length /* changed member */ );
284 cnat_ip4_translate_l3 (ip4, new_addr);
285 ip_csum_t new_ip_sum = ip4->checksum;
286 /* IP checksum changed */
287 sum = ip_csum_update (sum, old_ip_sum, new_ip_sum, ip4_header_t, checksum);
289 /* IP src/dst addr changed */
290 if (new_addr[VLIB_TX].as_u32)
292 ip_csum_update (sum, old_addr[VLIB_TX].as_u32, new_addr[VLIB_TX].as_u32,
293 ip4_header_t, dst_address);
295 if (new_addr[VLIB_RX].as_u32)
297 ip_csum_update (sum, old_addr[VLIB_RX].as_u32, new_addr[VLIB_RX].as_u32,
298 ip4_header_t, src_address);
300 icmp->checksum = ip_csum_fold (sum);
303 static_always_inline void
304 cnat_translation_ip4 (const cnat_session_t * session,
305 ip4_header_t * ip4, udp_header_t * udp)
307 tcp_header_t *tcp = (tcp_header_t *) udp;
308 ip4_address_t new_addr[VLIB_N_DIR];
309 u16 new_port[VLIB_N_DIR];
311 new_addr[VLIB_TX] = session->value.cs_ip[VLIB_TX].ip4;
312 new_addr[VLIB_RX] = session->value.cs_ip[VLIB_RX].ip4;
313 new_port[VLIB_TX] = session->value.cs_port[VLIB_TX];
314 new_port[VLIB_RX] = session->value.cs_port[VLIB_RX];
316 if (ip4->protocol == IP_PROTOCOL_TCP)
318 ip_csum_t sum = tcp->checksum;
319 cnat_ip4_translate_l4 (ip4, udp, &sum, new_addr, new_port);
320 tcp->checksum = ip_csum_fold (sum);
321 cnat_ip4_translate_l3 (ip4, new_addr);
322 cnat_tcp_update_session_lifetime (tcp, session->value.cs_ts_index);
324 else if (ip4->protocol == IP_PROTOCOL_UDP)
326 ip_csum_t sum = udp->checksum;
327 cnat_ip4_translate_l4 (ip4, udp, &sum, new_addr, new_port);
328 udp->checksum = ip_csum_fold (sum);
329 cnat_ip4_translate_l3 (ip4, new_addr);
331 else if (ip4->protocol == IP_PROTOCOL_ICMP)
333 icmp46_header_t *icmp = (icmp46_header_t *) udp;
334 if (icmp_type_is_error_message (icmp->type))
336 /* SNAT only if src_addr was translated */
338 (ip4->src_address.as_u32 ==
339 session->key.cs_ip[VLIB_RX].ip4.as_u32);
340 cnat_translation_icmp4_error (ip4, icmp, new_addr, new_port,
343 else if (icmp_type_is_echo (icmp->type))
344 cnat_translation_icmp4_echo (ip4, icmp, new_addr, new_port);
348 static_always_inline void
349 cnat_ip6_translate_l3 (ip6_header_t * ip6, ip6_address_t new_addr[VLIB_N_DIR])
351 if (has_ip6_address (&new_addr[VLIB_TX]))
352 ip6_address_copy (&ip6->dst_address, &new_addr[VLIB_TX]);
353 if (has_ip6_address (&new_addr[VLIB_RX]))
354 ip6_address_copy (&ip6->src_address, &new_addr[VLIB_RX]);
357 static_always_inline void
358 cnat_ip6_translate_l4 (ip6_header_t * ip6, udp_header_t * udp,
360 ip6_address_t new_addr[VLIB_N_DIR],
361 u16 new_port[VLIB_N_DIR])
363 u16 old_port[VLIB_N_DIR];
364 ip6_address_t old_addr[VLIB_N_DIR];
366 /* Fastpath no checksum */
367 if (PREDICT_TRUE (0 == *sum))
369 udp->dst_port = new_port[VLIB_TX];
370 udp->src_port = new_port[VLIB_RX];
374 old_port[VLIB_TX] = udp->dst_port;
375 old_port[VLIB_RX] = udp->src_port;
376 ip6_address_copy (&old_addr[VLIB_TX], &ip6->dst_address);
377 ip6_address_copy (&old_addr[VLIB_RX], &ip6->src_address);
379 if (has_ip6_address (&new_addr[VLIB_TX]))
381 *sum = ip_csum_add_even (*sum, new_addr[VLIB_TX].as_u64[0]);
382 *sum = ip_csum_add_even (*sum, new_addr[VLIB_TX].as_u64[1]);
383 *sum = ip_csum_sub_even (*sum, old_addr[VLIB_TX].as_u64[0]);
384 *sum = ip_csum_sub_even (*sum, old_addr[VLIB_TX].as_u64[1]);
387 if (new_port[VLIB_TX])
389 udp->dst_port = new_port[VLIB_TX];
390 *sum = ip_csum_update (*sum, old_port[VLIB_TX], new_port[VLIB_TX],
391 ip4_header_t /* cheat */ ,
392 length /* changed member */ );
394 if (has_ip6_address (&new_addr[VLIB_RX]))
396 *sum = ip_csum_add_even (*sum, new_addr[VLIB_RX].as_u64[0]);
397 *sum = ip_csum_add_even (*sum, new_addr[VLIB_RX].as_u64[1]);
398 *sum = ip_csum_sub_even (*sum, old_addr[VLIB_RX].as_u64[0]);
399 *sum = ip_csum_sub_even (*sum, old_addr[VLIB_RX].as_u64[1]);
402 if (new_port[VLIB_RX])
404 udp->src_port = new_port[VLIB_RX];
405 *sum = ip_csum_update (*sum, old_port[VLIB_RX], new_port[VLIB_RX],
406 ip4_header_t /* cheat */ ,
407 length /* changed member */ );
411 static_always_inline void
412 cnat_translation_icmp6_echo (ip6_header_t * ip6, icmp46_header_t * icmp,
413 ip6_address_t new_addr[VLIB_N_DIR],
414 u16 new_port[VLIB_N_DIR])
416 cnat_echo_header_t *echo = (cnat_echo_header_t *) (icmp + 1);
417 ip6_address_t old_addr[VLIB_N_DIR];
420 old_port = echo->identifier;
421 ip6_address_copy (&old_addr[VLIB_TX], &ip6->dst_address);
422 ip6_address_copy (&old_addr[VLIB_RX], &ip6->src_address);
424 sum = icmp->checksum;
426 cnat_ip6_translate_l3 (ip6, new_addr);
427 if (has_ip6_address (&new_addr[VLIB_TX]))
429 sum = ip_csum_add_even (sum, new_addr[VLIB_TX].as_u64[0]);
430 sum = ip_csum_add_even (sum, new_addr[VLIB_TX].as_u64[1]);
431 sum = ip_csum_sub_even (sum, old_addr[VLIB_TX].as_u64[0]);
432 sum = ip_csum_sub_even (sum, old_addr[VLIB_TX].as_u64[1]);
435 if (has_ip6_address (&new_addr[VLIB_RX]))
437 sum = ip_csum_add_even (sum, new_addr[VLIB_RX].as_u64[0]);
438 sum = ip_csum_add_even (sum, new_addr[VLIB_RX].as_u64[1]);
439 sum = ip_csum_sub_even (sum, old_addr[VLIB_RX].as_u64[0]);
440 sum = ip_csum_sub_even (sum, old_addr[VLIB_RX].as_u64[1]);
443 echo->identifier = new_port[VLIB_RX];
444 sum = ip_csum_update (sum, old_port, new_port[VLIB_RX],
445 ip4_header_t /* cheat */ ,
446 length /* changed member */ );
448 icmp->checksum = ip_csum_fold (sum);
451 static_always_inline void
452 cnat_translation_icmp6_error (ip6_header_t * outer_ip6,
453 icmp46_header_t * icmp,
454 ip6_address_t outer_new_addr[VLIB_N_DIR],
455 u16 outer_new_port[VLIB_N_DIR],
458 ip6_address_t new_addr[VLIB_N_DIR];
459 ip6_address_t old_addr[VLIB_N_DIR];
460 ip6_address_t outer_old_addr[VLIB_N_DIR];
461 u16 new_port[VLIB_N_DIR];
462 u16 old_port[VLIB_N_DIR];
463 ip_csum_t sum, inner_l4_sum, inner_l4_old_sum;
465 if (!icmp6_type_is_error_message (icmp->type))
468 ip6_header_t *ip6 = (ip6_header_t *) (icmp + 2);
469 udp_header_t *udp = (udp_header_t *) (ip6 + 1);
470 tcp_header_t *tcp = (tcp_header_t *) udp;
472 /* Swap inner ports */
473 ip6_address_copy (&new_addr[VLIB_RX], &outer_new_addr[VLIB_TX]);
474 ip6_address_copy (&new_addr[VLIB_TX], &outer_new_addr[VLIB_RX]);
475 new_port[VLIB_TX] = outer_new_port[VLIB_RX];
476 new_port[VLIB_RX] = outer_new_port[VLIB_TX];
478 ip6_address_copy (&old_addr[VLIB_TX], &ip6->dst_address);
479 ip6_address_copy (&old_addr[VLIB_RX], &ip6->src_address);
480 old_port[VLIB_RX] = udp->src_port;
481 old_port[VLIB_TX] = udp->dst_port;
483 sum = icmp->checksum;
484 /* Translate outer ip */
485 ip6_address_copy (&outer_old_addr[VLIB_TX], &outer_ip6->dst_address);
486 ip6_address_copy (&outer_old_addr[VLIB_RX], &outer_ip6->src_address);
488 ip6_address_copy (&outer_new_addr[VLIB_RX], &outer_ip6->src_address);
489 cnat_ip6_translate_l3 (outer_ip6, outer_new_addr);
490 if (has_ip6_address (&outer_new_addr[VLIB_TX]))
492 sum = ip_csum_add_even (sum, outer_new_addr[VLIB_TX].as_u64[0]);
493 sum = ip_csum_add_even (sum, outer_new_addr[VLIB_TX].as_u64[1]);
494 sum = ip_csum_sub_even (sum, outer_old_addr[VLIB_TX].as_u64[0]);
495 sum = ip_csum_sub_even (sum, outer_old_addr[VLIB_TX].as_u64[1]);
498 if (has_ip6_address (&outer_new_addr[VLIB_RX]))
500 sum = ip_csum_add_even (sum, outer_new_addr[VLIB_RX].as_u64[0]);
501 sum = ip_csum_add_even (sum, outer_new_addr[VLIB_RX].as_u64[1]);
502 sum = ip_csum_sub_even (sum, outer_old_addr[VLIB_RX].as_u64[0]);
503 sum = ip_csum_sub_even (sum, outer_old_addr[VLIB_RX].as_u64[1]);
506 /* Translate inner TCP / UDP */
507 if (ip6->protocol == IP_PROTOCOL_TCP)
509 inner_l4_old_sum = inner_l4_sum = tcp->checksum;
510 cnat_ip6_translate_l4 (ip6, udp, &inner_l4_sum, new_addr, new_port);
511 tcp->checksum = ip_csum_fold (inner_l4_sum);
513 else if (ip6->protocol == IP_PROTOCOL_UDP)
515 inner_l4_old_sum = inner_l4_sum = udp->checksum;
516 cnat_ip6_translate_l4 (ip6, udp, &inner_l4_sum, new_addr, new_port);
517 udp->checksum = ip_csum_fold (inner_l4_sum);
522 /* UDP/TCP checksum changed */
523 sum = ip_csum_update (sum, inner_l4_old_sum, inner_l4_sum,
524 ip4_header_t /* cheat */ ,
527 /* UDP/TCP Ports changed */
528 if (old_port[VLIB_TX] && new_port[VLIB_TX])
529 sum = ip_csum_update (sum, old_port[VLIB_TX], new_port[VLIB_TX],
530 ip4_header_t /* cheat */ ,
531 length /* changed member */ );
533 if (old_port[VLIB_RX] && new_port[VLIB_RX])
534 sum = ip_csum_update (sum, old_port[VLIB_RX], new_port[VLIB_RX],
535 ip4_header_t /* cheat */ ,
536 length /* changed member */ );
539 cnat_ip6_translate_l3 (ip6, new_addr);
540 /* IP src/dst addr changed */
541 if (has_ip6_address (&new_addr[VLIB_TX]))
543 sum = ip_csum_add_even (sum, new_addr[VLIB_TX].as_u64[0]);
544 sum = ip_csum_add_even (sum, new_addr[VLIB_TX].as_u64[1]);
545 sum = ip_csum_sub_even (sum, old_addr[VLIB_TX].as_u64[0]);
546 sum = ip_csum_sub_even (sum, old_addr[VLIB_TX].as_u64[1]);
549 if (has_ip6_address (&new_addr[VLIB_RX]))
551 sum = ip_csum_add_even (sum, new_addr[VLIB_RX].as_u64[0]);
552 sum = ip_csum_add_even (sum, new_addr[VLIB_RX].as_u64[1]);
553 sum = ip_csum_sub_even (sum, old_addr[VLIB_RX].as_u64[0]);
554 sum = ip_csum_sub_even (sum, old_addr[VLIB_RX].as_u64[1]);
557 icmp->checksum = ip_csum_fold (sum);
560 static_always_inline void
561 cnat_translation_ip6 (const cnat_session_t * session,
562 ip6_header_t * ip6, udp_header_t * udp)
564 tcp_header_t *tcp = (tcp_header_t *) udp;
565 ip6_address_t new_addr[VLIB_N_DIR];
566 u16 new_port[VLIB_N_DIR];
568 ip6_address_copy (&new_addr[VLIB_TX], &session->value.cs_ip[VLIB_TX].ip6);
569 ip6_address_copy (&new_addr[VLIB_RX], &session->value.cs_ip[VLIB_RX].ip6);
570 new_port[VLIB_TX] = session->value.cs_port[VLIB_TX];
571 new_port[VLIB_RX] = session->value.cs_port[VLIB_RX];
573 if (ip6->protocol == IP_PROTOCOL_TCP)
575 ip_csum_t sum = tcp->checksum;
576 cnat_ip6_translate_l4 (ip6, udp, &sum, new_addr, new_port);
577 tcp->checksum = ip_csum_fold (sum);
578 cnat_ip6_translate_l3 (ip6, new_addr);
579 cnat_tcp_update_session_lifetime (tcp, session->value.cs_ts_index);
581 else if (ip6->protocol == IP_PROTOCOL_UDP)
583 ip_csum_t sum = udp->checksum;
584 cnat_ip6_translate_l4 (ip6, udp, &sum, new_addr, new_port);
585 udp->checksum = ip_csum_fold (sum);
586 cnat_ip6_translate_l3 (ip6, new_addr);
588 else if (ip6->protocol == IP_PROTOCOL_ICMP6)
590 icmp46_header_t *icmp = (icmp46_header_t *) udp;
591 if (icmp6_type_is_error_message (icmp->type))
593 /* SNAT only if src_addr was translated */
594 u8 snat_outer_ip = cmp_ip6_address (&ip6->src_address,
597 cnat_translation_icmp6_error (ip6, icmp, new_addr, new_port,
600 else if (icmp6_type_is_echo (icmp->type))
601 cnat_translation_icmp6_echo (ip6, icmp, new_addr, new_port);
605 static_always_inline void
606 cnat_session_make_key (vlib_buffer_t * b, ip_address_family_t af,
607 clib_bihash_kv_40_48_t * bkey)
610 cnat_session_t *session = (cnat_session_t *) bkey;
611 session->key.cs_af = af;
612 session->key.__cs_pad[0] = 0;
613 session->key.__cs_pad[1] = 0;
617 ip4 = vlib_buffer_get_current (b);
618 if (PREDICT_FALSE (ip4->protocol == IP_PROTOCOL_ICMP))
620 icmp46_header_t *icmp = (icmp46_header_t *) (ip4 + 1);
621 if (icmp_type_is_error_message (icmp->type))
623 ip4 = (ip4_header_t *) (icmp + 2); /* Use inner packet */
624 udp = (udp_header_t *) (ip4 + 1);
625 /* Swap dst & src for search as ICMP payload is reversed */
626 ip46_address_set_ip4 (&session->key.cs_ip[VLIB_RX],
628 ip46_address_set_ip4 (&session->key.cs_ip[VLIB_TX],
630 session->key.cs_proto = ip4->protocol;
631 session->key.cs_port[VLIB_TX] = udp->src_port;
632 session->key.cs_port[VLIB_RX] = udp->dst_port;
634 else if (icmp_type_is_echo (icmp->type))
636 cnat_echo_header_t *echo = (cnat_echo_header_t *) (icmp + 1);
637 ip46_address_set_ip4 (&session->key.cs_ip[VLIB_TX],
639 ip46_address_set_ip4 (&session->key.cs_ip[VLIB_RX],
641 session->key.cs_proto = ip4->protocol;
642 session->key.cs_port[VLIB_TX] = echo->identifier;
643 session->key.cs_port[VLIB_RX] = echo->identifier;
650 udp = (udp_header_t *) (ip4 + 1);
651 ip46_address_set_ip4 (&session->key.cs_ip[VLIB_TX],
653 ip46_address_set_ip4 (&session->key.cs_ip[VLIB_RX],
655 session->key.cs_proto = ip4->protocol;
656 session->key.cs_port[VLIB_RX] = udp->src_port;
657 session->key.cs_port[VLIB_TX] = udp->dst_port;
664 ip6 = vlib_buffer_get_current (b);
665 if (PREDICT_FALSE (ip6->protocol == IP_PROTOCOL_ICMP6))
667 icmp46_header_t *icmp = (icmp46_header_t *) (ip6 + 1);
668 if (icmp6_type_is_error_message (icmp->type))
670 ip6 = (ip6_header_t *) (icmp + 2); /* Use inner packet */
671 udp = (udp_header_t *) (ip6 + 1);
672 /* Swap dst & src for search as ICMP payload is reversed */
673 ip46_address_set_ip6 (&session->key.cs_ip[VLIB_RX],
675 ip46_address_set_ip6 (&session->key.cs_ip[VLIB_TX],
677 session->key.cs_proto = ip6->protocol;
678 session->key.cs_port[VLIB_TX] = udp->src_port;
679 session->key.cs_port[VLIB_RX] = udp->dst_port;
681 else if (icmp6_type_is_echo (icmp->type))
683 cnat_echo_header_t *echo = (cnat_echo_header_t *) (icmp + 1);
684 ip46_address_set_ip6 (&session->key.cs_ip[VLIB_TX],
686 ip46_address_set_ip6 (&session->key.cs_ip[VLIB_RX],
688 session->key.cs_proto = ip6->protocol;
689 session->key.cs_port[VLIB_TX] = echo->identifier;
690 session->key.cs_port[VLIB_RX] = echo->identifier;
697 udp = (udp_header_t *) (ip6 + 1);
698 ip46_address_set_ip6 (&session->key.cs_ip[VLIB_TX],
700 ip46_address_set_ip6 (&session->key.cs_ip[VLIB_RX],
702 session->key.cs_port[VLIB_RX] = udp->src_port;
703 session->key.cs_port[VLIB_TX] = udp->dst_port;
704 session->key.cs_proto = ip6->protocol;
710 /* Ensure we dont find anything */
711 session->key.cs_proto = 0;
716 * Create NAT sessions
719 static_always_inline void
720 cnat_session_create (cnat_session_t * session, cnat_node_ctx_t * ctx,
724 clib_bihash_kv_40_48_t rkey;
725 cnat_session_t *rsession = (cnat_session_t *) & rkey;
726 clib_bihash_kv_40_48_t *bkey = (clib_bihash_kv_40_48_t *) session;
727 clib_bihash_kv_40_48_t rvalue;
730 session->value.cs_ts_index = cnat_timestamp_new (ctx->now);
731 clib_bihash_add_del_40_48 (&cnat_session_db, bkey, 1);
733 /* is this the first time we've seen this source address */
734 cc = (AF_IP4 == ctx->af ?
735 cnat_client_ip4_find (&session->value.cs_ip[VLIB_RX].ip4) :
736 cnat_client_ip6_find (&session->value.cs_ip[VLIB_RX].ip6));
741 if (AF_IP4 == ctx->af)
742 r0 = (u64) session->value.cs_ip[VLIB_RX].ip4.as_u32;
745 r0 = r0 * 31 + session->value.cs_ip[VLIB_RX].ip6.as_u64[0];
746 r0 = r0 * 31 + session->value.cs_ip[VLIB_RX].ip6.as_u64[1];
750 if (!throttle_check (&cnat_throttle, ctx->thread_index, r0, ctx->seed))
753 l.addr.version = ctx->af;
754 ip46_address_copy (&l.addr.ip, &session->value.cs_ip[VLIB_RX]);
755 /* fire client create to the main thread */
756 vl_api_rpc_call_main_thread (cnat_client_learn,
757 (u8 *) & l, sizeof (l));
761 /* Will still need to count those for session refcnt */
763 clib_spinlock_lock (&cnat_client_db.throttle_pool_lock
764 [ctx->thread_index]);
765 pool_get (cnat_client_db.throttle_pool[ctx->thread_index], addr);
766 addr->version = ctx->af;
767 ip46_address_copy (&addr->ip, &session->value.cs_ip[VLIB_RX]);
768 clib_spinlock_unlock (&cnat_client_db.throttle_pool_lock
769 [ctx->thread_index]);
774 /* Refcount reverse session */
775 cnat_client_cnt_session (cc);
778 /* create the reverse flow key */
779 ip46_address_copy (&rsession->key.cs_ip[VLIB_RX],
780 &session->value.cs_ip[VLIB_TX]);
781 ip46_address_copy (&rsession->key.cs_ip[VLIB_TX],
782 &session->value.cs_ip[VLIB_RX]);
783 rsession->key.cs_proto = session->key.cs_proto;
784 rsession->key.__cs_pad[0] = 0;
785 rsession->key.__cs_pad[1] = 0;
786 rsession->key.cs_af = ctx->af;
787 rsession->key.cs_port[VLIB_RX] = session->value.cs_port[VLIB_TX];
788 rsession->key.cs_port[VLIB_TX] = session->value.cs_port[VLIB_RX];
790 /* First search for existing reverse session */
791 rv = clib_bihash_search_inline_2_40_48 (&cnat_session_db, &rkey, &rvalue);
794 /* Reverse session already exists
795 cleanup before creating for refcnts */
796 cnat_session_t *found_rsession = (cnat_session_t *) & rvalue;
797 cnat_session_free (found_rsession);
799 /* add the reverse flow */
800 ip46_address_copy (&rsession->value.cs_ip[VLIB_RX],
801 &session->key.cs_ip[VLIB_TX]);
802 ip46_address_copy (&rsession->value.cs_ip[VLIB_TX],
803 &session->key.cs_ip[VLIB_RX]);
804 rsession->value.cs_ts_index = session->value.cs_ts_index;
805 rsession->value.cs_lbi = INDEX_INVALID;
806 rsession->value.flags = rsession_flags;
807 rsession->value.cs_port[VLIB_TX] = session->key.cs_port[VLIB_RX];
808 rsession->value.cs_port[VLIB_RX] = session->key.cs_port[VLIB_TX];
810 clib_bihash_add_del_40_48 (&cnat_session_db, &rkey, 1);
814 cnat_node_inline (vlib_main_t * vm,
815 vlib_node_runtime_t * node,
816 vlib_frame_t * frame,
817 cnat_node_sub_t cnat_sub,
818 ip_address_family_t af, u8 do_trace)
820 u32 n_left, *from, thread_index;
821 vlib_buffer_t *bufs[VLIB_FRAME_SIZE];
822 vlib_buffer_t **b = bufs;
823 u16 nexts[VLIB_FRAME_SIZE], *next;
827 thread_index = vm->thread_index;
828 from = vlib_frame_vector_args (frame);
829 n_left = frame->n_vectors;
831 vlib_get_buffers (vm, from, bufs, n_left);
832 now = vlib_time_now (vm);
833 seed = throttle_seed (&cnat_throttle, thread_index, vlib_time_now (vm));
834 cnat_session_t *session[4];
835 clib_bihash_kv_40_48_t bkey[4], bvalue[4];
839 cnat_node_ctx_t ctx = { now, seed, thread_index, af, do_trace };
843 /* Kickstart our state */
844 cnat_session_make_key (b[3], af, &bkey[3]);
845 cnat_session_make_key (b[2], af, &bkey[2]);
846 cnat_session_make_key (b[1], af, &bkey[1]);
847 cnat_session_make_key (b[0], af, &bkey[0]);
849 hash[3] = clib_bihash_hash_40_48 (&bkey[3]);
850 hash[2] = clib_bihash_hash_40_48 (&bkey[2]);
851 hash[1] = clib_bihash_hash_40_48 (&bkey[1]);
852 hash[0] = clib_bihash_hash_40_48 (&bkey[0]);
859 vlib_prefetch_buffer_header (b[11], LOAD);
860 vlib_prefetch_buffer_header (b[10], LOAD);
861 vlib_prefetch_buffer_header (b[9], LOAD);
862 vlib_prefetch_buffer_header (b[8], LOAD);
866 clib_bihash_search_inline_2_with_hash_40_48 (&cnat_session_db,
869 session[3] = (cnat_session_t *) (rv[3] ? &bkey[3] : &bvalue[3]);
870 next[3] = cnat_sub (vm, node, b[3], &ctx, rv[3], session[3]);
873 clib_bihash_search_inline_2_with_hash_40_48 (&cnat_session_db,
876 session[2] = (cnat_session_t *) (rv[2] ? &bkey[2] : &bvalue[2]);
877 next[2] = cnat_sub (vm, node, b[2], &ctx, rv[2], session[2]);
880 clib_bihash_search_inline_2_with_hash_40_48 (&cnat_session_db,
883 session[1] = (cnat_session_t *) (rv[1] ? &bkey[1] : &bvalue[1]);
884 next[1] = cnat_sub (vm, node, b[1], &ctx, rv[1], session[1]);
887 clib_bihash_search_inline_2_with_hash_40_48 (&cnat_session_db,
890 session[0] = (cnat_session_t *) (rv[0] ? &bkey[0] : &bvalue[0]);
891 next[0] = cnat_sub (vm, node, b[0], &ctx, rv[0], session[0]);
893 cnat_session_make_key (b[7], af, &bkey[3]);
894 cnat_session_make_key (b[6], af, &bkey[2]);
895 cnat_session_make_key (b[5], af, &bkey[1]);
896 cnat_session_make_key (b[4], af, &bkey[0]);
898 hash[3] = clib_bihash_hash_40_48 (&bkey[3]);
899 hash[2] = clib_bihash_hash_40_48 (&bkey[2]);
900 hash[1] = clib_bihash_hash_40_48 (&bkey[1]);
901 hash[0] = clib_bihash_hash_40_48 (&bkey[0]);
903 clib_bihash_prefetch_bucket_40_48 (&cnat_session_db, hash[3]);
904 clib_bihash_prefetch_bucket_40_48 (&cnat_session_db, hash[2]);
905 clib_bihash_prefetch_bucket_40_48 (&cnat_session_db, hash[1]);
906 clib_bihash_prefetch_bucket_40_48 (&cnat_session_db, hash[0]);
908 clib_bihash_prefetch_data_40_48 (&cnat_session_db, hash[3]);
909 clib_bihash_prefetch_data_40_48 (&cnat_session_db, hash[2]);
910 clib_bihash_prefetch_data_40_48 (&cnat_session_db, hash[1]);
911 clib_bihash_prefetch_data_40_48 (&cnat_session_db, hash[0]);
920 cnat_session_make_key (b[0], af, &bkey[0]);
921 rv[0] = clib_bihash_search_inline_2_40_48 (&cnat_session_db,
922 &bkey[0], &bvalue[0]);
924 session[0] = (cnat_session_t *) (rv[0] ? &bkey[0] : &bvalue[0]);
925 next[0] = cnat_sub (vm, node, b[0], &ctx, rv[0], session[0]);
932 vlib_buffer_enqueue_to_next (vm, node, from, nexts, frame->n_vectors);
934 return frame->n_vectors;
938 * fd.io coding-style-patch-verification: ON
941 * eval: (c-set-style "gnu")