LCOV - code coverage report
Current view: top level - plugins/cnat - cnat_node.h (source / functions) Hit Total Coverage
Test: coverage-filtered.info Lines: 459 530 86.6 %
Date: 2023-10-26 01:39:38 Functions: 23 26 88.5 %

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

Generated by: LCOV version 1.14