LCOV - code coverage report
Current view: top level - plugins/nat/dslite - dslite_out2in.c (source / functions) Hit Total Coverage
Test: coverage-filtered.info Lines: 118 131 90.1 %
Date: 2023-07-05 22:20:52 Functions: 8 11 72.7 %

          Line data    Source code
       1             : /*
       2             :  * Copyright (c) 2017 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             : #include <nat/dslite/dslite.h>
      16             : 
      17             : typedef enum
      18             : {
      19             :   DSLITE_OUT2IN_NEXT_IP4_LOOKUP,
      20             :   DSLITE_OUT2IN_NEXT_IP6_LOOKUP,
      21             :   DSLITE_OUT2IN_NEXT_DROP,
      22             :   DSLITE_OUT2IN_N_NEXT,
      23             : } dslite_out2in_next_t;
      24             : 
      25             : static char *dslite_out2in_error_strings[] = {
      26             : #define _(sym,string) string,
      27             :   foreach_dslite_error
      28             : #undef _
      29             : };
      30             : 
      31             : static inline u32
      32           1 : dslite_icmp_out2in (dslite_main_t * dm, ip4_header_t * ip4,
      33             :                     dslite_session_t ** sp, u32 next, u8 * error,
      34             :                     u32 thread_index)
      35             : {
      36           1 :   dslite_session_t *s = 0;
      37           1 :   icmp46_header_t *icmp = ip4_next_header (ip4);
      38             :   clib_bihash_kv_8_8_t kv, value;
      39             :   nat_session_key_t key;
      40           1 :   u32 n = next;
      41             :   echo_header_t *echo;
      42             :   u32 new_addr, old_addr;
      43             :   u16 old_id, new_id;
      44             :   ip_csum_t sum;
      45             : 
      46           1 :   echo = (echo_header_t *) (icmp + 1);
      47             : 
      48           1 :   if (icmp_type_is_error_message (icmp->type)
      49           1 :       || (icmp->type != ICMP4_echo_reply))
      50             :     {
      51           0 :       n = DSLITE_OUT2IN_NEXT_DROP;
      52           0 :       *error = DSLITE_ERROR_BAD_ICMP_TYPE;
      53           0 :       goto done;
      54             :     }
      55             : 
      56           1 :   key.addr = ip4->dst_address;
      57           1 :   key.port = echo->identifier;
      58           1 :   key.protocol = NAT_PROTOCOL_ICMP;
      59           1 :   key.fib_index = 0;
      60           1 :   kv.key = key.as_u64;
      61             : 
      62           1 :   if (clib_bihash_search_8_8
      63           1 :       (&dm->per_thread_data[thread_index].out2in, &kv, &value))
      64             :     {
      65           0 :       next = DSLITE_OUT2IN_NEXT_DROP;
      66           0 :       *error = DSLITE_ERROR_NO_TRANSLATION;
      67           0 :       goto done;
      68             :     }
      69             :   else
      70             :     {
      71           1 :       s =
      72           1 :         pool_elt_at_index (dm->per_thread_data[thread_index].sessions,
      73             :                            value.value);
      74             :     }
      75             : 
      76           1 :   old_id = echo->identifier;
      77           1 :   echo->identifier = new_id = s->in2out.port;
      78           1 :   sum = icmp->checksum;
      79           1 :   sum = ip_csum_update (sum, old_id, new_id, echo_header_t, identifier);
      80           1 :   icmp->checksum = ip_csum_fold (sum);
      81             : 
      82           1 :   old_addr = ip4->dst_address.as_u32;
      83           1 :   ip4->dst_address = s->in2out.addr;
      84           1 :   new_addr = ip4->dst_address.as_u32;
      85             : 
      86           1 :   sum = ip4->checksum;
      87           1 :   sum = ip_csum_update (sum, old_addr, new_addr, ip4_header_t, dst_address);
      88           1 :   ip4->checksum = ip_csum_fold (sum);
      89             : 
      90           1 : done:
      91           1 :   *sp = s;
      92           1 :   return n;
      93             : }
      94             : 
      95        2239 : VLIB_NODE_FN (dslite_out2in_node) (vlib_main_t * vm,
      96             :                                    vlib_node_runtime_t * node,
      97             :                                    vlib_frame_t * frame)
      98             : {
      99             :   u32 n_left_from, *from, *to_next;
     100             :   dslite_out2in_next_t next_index;
     101             :   vlib_node_runtime_t *error_node;
     102           3 :   u32 thread_index = vm->thread_index;
     103           3 :   f64 now = vlib_time_now (vm);
     104           3 :   dslite_main_t *dm = &dslite_main;
     105             : 
     106           3 :   error_node = vlib_node_get_runtime (vm, dm->dslite_out2in_node_index);
     107             : 
     108           3 :   from = vlib_frame_vector_args (frame);
     109           3 :   n_left_from = frame->n_vectors;
     110           3 :   next_index = node->cached_next_index;
     111             : 
     112             : 
     113           6 :   while (n_left_from > 0)
     114             :     {
     115             :       u32 n_left_to_next;
     116             : 
     117           3 :       vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
     118             : 
     119           6 :       while (n_left_from > 0 && n_left_to_next > 0)
     120             :         {
     121             :           u32 bi0;
     122             :           vlib_buffer_t *b0;
     123           3 :           u32 next0 = DSLITE_OUT2IN_NEXT_IP6_LOOKUP;
     124           3 :           u8 error0 = DSLITE_ERROR_OUT2IN;
     125             :           ip4_header_t *ip40;
     126             :           ip6_header_t *ip60;
     127             :           u32 proto0;
     128             :           udp_header_t *udp0;
     129             :           tcp_header_t *tcp0;
     130             :           clib_bihash_kv_8_8_t kv0, value0;
     131             :           nat_session_key_t key0;
     132           3 :           dslite_session_t *s0 = 0;
     133             :           ip_csum_t sum0;
     134             :           u32 new_addr0, old_addr0;
     135             :           u16 new_port0, old_port0;
     136             : 
     137             :           /* speculatively enqueue b0 to the current next frame */
     138           3 :           bi0 = from[0];
     139           3 :           to_next[0] = bi0;
     140           3 :           from += 1;
     141           3 :           to_next += 1;
     142           3 :           n_left_from -= 1;
     143           3 :           n_left_to_next -= 1;
     144             : 
     145           3 :           b0 = vlib_get_buffer (vm, bi0);
     146           3 :           ip40 = vlib_buffer_get_current (b0);
     147           3 :           proto0 = ip_proto_to_nat_proto (ip40->protocol);
     148             : 
     149           3 :           if (PREDICT_FALSE (proto0 == NAT_PROTOCOL_OTHER))
     150             :             {
     151           0 :               error0 = DSLITE_ERROR_UNSUPPORTED_PROTOCOL;
     152           0 :               next0 = DSLITE_OUT2IN_NEXT_DROP;
     153           0 :               goto trace0;
     154             :             }
     155             : 
     156           3 :           if (PREDICT_FALSE (proto0 == NAT_PROTOCOL_ICMP))
     157             :             {
     158             :               next0 =
     159           1 :                 dslite_icmp_out2in (dm, ip40, &s0, next0, &error0,
     160             :                                     thread_index);
     161           1 :               if (PREDICT_FALSE (next0 == DSLITE_OUT2IN_NEXT_DROP))
     162           0 :                 goto trace0;
     163             : 
     164           1 :               goto encap0;
     165             :             }
     166             : 
     167           2 :           udp0 = ip4_next_header (ip40);
     168           2 :           tcp0 = (tcp_header_t *) udp0;
     169             : 
     170           2 :           key0.addr = ip40->dst_address;
     171           2 :           key0.port = udp0->dst_port;
     172           2 :           key0.protocol = proto0;
     173           2 :           key0.fib_index = 0;
     174           2 :           kv0.key = key0.as_u64;
     175             : 
     176           2 :           if (clib_bihash_search_8_8
     177           2 :               (&dm->per_thread_data[thread_index].out2in, &kv0, &value0))
     178             :             {
     179           0 :               next0 = DSLITE_OUT2IN_NEXT_DROP;
     180           0 :               error0 = DSLITE_ERROR_NO_TRANSLATION;
     181           0 :               goto trace0;
     182             :             }
     183             :           else
     184             :             {
     185           2 :               s0 =
     186           2 :                 pool_elt_at_index (dm->per_thread_data[thread_index].sessions,
     187             :                                    value0.value);
     188             :             }
     189             : 
     190           2 :           old_addr0 = ip40->dst_address.as_u32;
     191           2 :           ip40->dst_address = s0->in2out.addr;
     192           2 :           new_addr0 = ip40->dst_address.as_u32;
     193             : 
     194           2 :           sum0 = ip40->checksum;
     195             :           sum0 =
     196           2 :             ip_csum_update (sum0, old_addr0, new_addr0, ip4_header_t,
     197             :                             dst_address);
     198           2 :           ip40->checksum = ip_csum_fold (sum0);
     199             : 
     200           2 :           if (PREDICT_TRUE (proto0 == NAT_PROTOCOL_TCP))
     201             :             {
     202           1 :               old_port0 = tcp0->dst_port;
     203           1 :               tcp0->dst_port = s0->in2out.port;
     204           1 :               new_port0 = tcp0->dst_port;
     205             : 
     206           1 :               sum0 = tcp0->checksum;
     207             :               sum0 =
     208           1 :                 ip_csum_update (sum0, old_addr0, new_addr0, ip4_header_t,
     209             :                                 dst_address);
     210             :               sum0 =
     211           1 :                 ip_csum_update (sum0, old_port0, new_port0, ip4_header_t,
     212             :                                 length);
     213           1 :               tcp0->checksum = ip_csum_fold (sum0);
     214             :             }
     215             :           else
     216             :             {
     217           1 :               old_port0 = udp0->dst_port;
     218           1 :               udp0->dst_port = s0->in2out.port;
     219           1 :               udp0->checksum = 0;
     220             :             }
     221             : 
     222           3 :         encap0:
     223             :           /* Construct IPv6 header */
     224           3 :           vlib_buffer_advance (b0, -(sizeof (ip6_header_t)));
     225           3 :           ip60 = vlib_buffer_get_current (b0);
     226           3 :           ip60->ip_version_traffic_class_and_flow_label =
     227           3 :             clib_host_to_net_u32 ((6 << 28) + (ip40->tos << 20));
     228           3 :           ip60->payload_length = ip40->length;
     229           3 :           ip60->protocol = IP_PROTOCOL_IP_IN_IP;
     230           3 :           ip60->hop_limit = ip40->ttl;
     231           3 :           ip60->src_address.as_u64[0] = dm->aftr_ip6_addr.as_u64[0];
     232           3 :           ip60->src_address.as_u64[1] = dm->aftr_ip6_addr.as_u64[1];
     233           3 :           ip60->dst_address.as_u64[0] = s0->in2out.softwire_id.as_u64[0];
     234           3 :           ip60->dst_address.as_u64[1] = s0->in2out.softwire_id.as_u64[1];
     235             : 
     236             :           /* Accounting */
     237           3 :           s0->last_heard = now;
     238           3 :           s0->total_pkts++;
     239           3 :           s0->total_bytes += vlib_buffer_length_in_chain (vm, b0);
     240             :           /* Per-B4 LRU list maintenance */
     241           3 :           clib_dlist_remove (dm->per_thread_data[thread_index].list_pool,
     242           3 :                              s0->per_b4_index);
     243           3 :           clib_dlist_addtail (dm->per_thread_data[thread_index].list_pool,
     244           3 :                               s0->per_b4_list_head_index, s0->per_b4_index);
     245           3 :         trace0:
     246           3 :           if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE)
     247             :                              && (b0->flags & VLIB_BUFFER_IS_TRACED)))
     248             :             {
     249           3 :               dslite_trace_t *t = vlib_add_trace (vm, node, b0, sizeof (*t));
     250           3 :               t->next_index = next0;
     251           3 :               t->session_index = ~0;
     252           3 :               if (s0)
     253           3 :                 t->session_index =
     254           3 :                   s0 - dm->per_thread_data[thread_index].sessions;
     255             :             }
     256             : 
     257           3 :           b0->error = error_node->errors[error0];
     258             : 
     259             :           /* verify speculative enqueue, maybe switch current next frame */
     260           3 :           vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next,
     261             :                                            n_left_to_next, bi0, next0);
     262             :         }
     263           3 :       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
     264             :     }
     265             : 
     266           3 :   return frame->n_vectors;
     267             : }
     268             : 
     269             : /* *INDENT-OFF* */
     270      143960 : VLIB_REGISTER_NODE (dslite_out2in_node) = {
     271             :   .name = "dslite-out2in",
     272             :   .vector_size = sizeof (u32),
     273             :   .format_trace = format_dslite_trace,
     274             :   .type = VLIB_NODE_TYPE_INTERNAL,
     275             :   .n_errors = ARRAY_LEN (dslite_out2in_error_strings),
     276             :   .error_strings = dslite_out2in_error_strings,
     277             :   .n_next_nodes = DSLITE_OUT2IN_N_NEXT,
     278             :   /* edit / add dispositions here */
     279             :   .next_nodes = {
     280             :     [DSLITE_OUT2IN_NEXT_DROP] = "error-drop",
     281             :     [DSLITE_OUT2IN_NEXT_IP4_LOOKUP] = "ip4-lookup",
     282             :     [DSLITE_OUT2IN_NEXT_IP6_LOOKUP] = "ip6-lookup",
     283             :   },
     284             : };
     285             : /* *INDENT-ON* */
     286             : 
     287             : /*
     288             :  * fd.io coding-style-patch-verification: ON
     289             :  *
     290             :  * Local Variables:
     291             :  * eval: (c-set-style "gnu")
     292             :  * End:
     293             :  */

Generated by: LCOV version 1.14