LCOV - code coverage report
Current view: top level - plugins/cnat - cnat_node.h (source / functions) Hit Total Coverage
Test: coverage-filtered.info Lines: 484 543 89.1 %
Date: 2023-07-05 22:20:52 Functions: 23 24 95.8 %

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

Generated by: LCOV version 1.14