LCOV - code coverage report
Current view: top level - plugins/l2tp - decap.c (source / functions) Hit Total Coverage
Test: coverage-filtered.info Lines: 45 95 47.4 %
Date: 2023-10-26 01:39:38 Functions: 12 15 80.0 %

          Line data    Source code
       1             : /*
       2             :  * decap.c : L2TPv3 tunnel decapsulation
       3             :  *
       4             :  * Copyright (c) 2013 Cisco and/or its affiliates.
       5             :  * Licensed under the Apache License, Version 2.0 (the "License");
       6             :  * you may not use this file except in compliance with the License.
       7             :  * You may obtain a copy of the License at:
       8             :  *
       9             :  *     http://www.apache.org/licenses/LICENSE-2.0
      10             :  *
      11             :  * Unless required by applicable law or agreed to in writing, software
      12             :  * distributed under the License is distributed on an "AS IS" BASIS,
      13             :  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
      14             :  * See the License for the specific language governing permissions and
      15             :  * limitations under the License.
      16             :  */
      17             : 
      18             : #include <vppinfra/error.h>
      19             : #include <vppinfra/hash.h>
      20             : #include <vnet/vnet.h>
      21             : #include <vnet/ip/ip.h>
      22             : #include <vnet/ethernet/ethernet.h>
      23             : #include <l2tp/l2tp.h>
      24             : #include <vnet/l2/l2_input.h>
      25             : 
      26             : /* Statistics (not really errors) */
      27             : #define foreach_l2t_decap_error                                 \
      28             : _(USER_TO_NETWORK, "L2TP user (ip6) to L2 network pkts")        \
      29             : _(SESSION_ID_MISMATCH, "l2tpv3 local session id mismatches")    \
      30             : _(COOKIE_MISMATCH, "l2tpv3 local cookie mismatches")            \
      31             : _(NO_SESSION, "l2tpv3 session not found")                       \
      32             : _(ADMIN_DOWN, "l2tpv3 tunnel is down")
      33             : 
      34             : static char *l2t_decap_error_strings[] = {
      35             : #define _(sym,string) string,
      36             :   foreach_l2t_decap_error
      37             : #undef _
      38             : };
      39             : 
      40             : typedef enum
      41             : {
      42             : #define _(sym,str) L2T_DECAP_ERROR_##sym,
      43             :   foreach_l2t_decap_error
      44             : #undef _
      45             :     L2T_DECAP_N_ERROR,
      46             : } l2t_DECAP_error_t;
      47             : 
      48             : typedef enum
      49             : {
      50             :   L2T_DECAP_NEXT_DROP,
      51             :   L2T_DECAP_NEXT_L2_INPUT,
      52             :   L2T_DECAP_N_NEXT,
      53             :   /* Pseudo next index */
      54             :   L2T_DECAP_NEXT_NO_INTERCEPT = L2T_DECAP_N_NEXT,
      55             : } l2t_decap_next_t;
      56             : 
      57             : #define NSTAGES 3
      58             : 
      59             : static inline void
      60           1 : stage0 (vlib_main_t * vm, vlib_node_runtime_t * node, vlib_buffer_t * b)
      61             : {
      62           1 :   vlib_prefetch_buffer_header (b, STORE);
      63             :   /* l2tpv3 header is a long way away, need 2 cache lines */
      64           1 :   CLIB_PREFETCH (b->data, 2 * CLIB_CACHE_LINE_BYTES, STORE);
      65           1 : }
      66             : 
      67             : static inline void
      68           1 : stage1 (vlib_main_t * vm, vlib_node_runtime_t * node, vlib_buffer_t * b)
      69             : {
      70           1 :   l2t_main_t *lm = &l2t_main;
      71           1 :   ip6_header_t *ip6 = vlib_buffer_get_current (b);
      72             :   u32 session_index;
      73           1 :   uword *p = 0;
      74             :   l2tpv3_header_t *l2t;
      75             : 
      76             :   /* Not L2tpv3 (0x73, 0t115)? Use the normal path. */
      77           1 :   if (PREDICT_FALSE (ip6->protocol != IP_PROTOCOL_L2TP))
      78             :     {
      79           0 :       vnet_buffer (b)->l2t.next_index = L2T_DECAP_NEXT_NO_INTERCEPT;
      80           0 :       return;
      81             :     }
      82             : 
      83             :   /* Make up your minds, people... */
      84           1 :   switch (lm->lookup_type)
      85             :     {
      86           0 :     case L2T_LOOKUP_SRC_ADDRESS:
      87           0 :       p = hash_get_mem (lm->session_by_src_address, &ip6->src_address);
      88           0 :       break;
      89           1 :     case L2T_LOOKUP_DST_ADDRESS:
      90           2 :       p = hash_get_mem (lm->session_by_dst_address, &ip6->dst_address);
      91           1 :       break;
      92           0 :     case L2T_LOOKUP_SESSION_ID:
      93           0 :       l2t = (l2tpv3_header_t *) (ip6 + 1);
      94           0 :       p = hash_get (lm->session_by_session_id, l2t->session_id);
      95           0 :       break;
      96           0 :     default:
      97           0 :       ASSERT (0);
      98             :     }
      99             : 
     100           1 :   if (PREDICT_FALSE (p == 0))
     101             :     {
     102           1 :       vnet_buffer (b)->l2t.next_index = L2T_DECAP_NEXT_NO_INTERCEPT;
     103           1 :       return;
     104             :     }
     105             :   else
     106             :     {
     107           0 :       session_index = p[0];
     108             :     }
     109             : 
     110             :   /* Remember mapping index, prefetch the mini counter */
     111           0 :   vnet_buffer (b)->l2t.next_index = L2T_DECAP_NEXT_L2_INPUT;
     112           0 :   vnet_buffer (b)->l2t.session_index = session_index;
     113             : 
     114             :   /* $$$$$ prefetch counter */
     115             : }
     116             : 
     117             : static inline u32
     118           1 : last_stage (vlib_main_t * vm, vlib_node_runtime_t * node, vlib_buffer_t * b)
     119             : {
     120           1 :   l2t_main_t *lm = &l2t_main;
     121           1 :   ip6_header_t *ip6 = vlib_buffer_get_current (b);
     122           1 :   vlib_node_t *n = vlib_get_node (vm, node->node_index);
     123           1 :   u32 node_counter_base_index = n->error_heap_index;
     124           1 :   vlib_error_main_t *em = &vm->error_main;
     125             :   l2tpv3_header_t *l2tp;
     126             :   u32 counter_index;
     127           1 :   l2t_session_t *session = 0;
     128             :   u32 session_index;
     129             :   u32 next_index;
     130           1 :   u8 l2tp_decap_local = (l2t_decap_local_node.index == n->index);
     131             : 
     132             :   /* Other-than-output pkt? We're done... */
     133           1 :   if (vnet_buffer (b)->l2t.next_index != L2T_DECAP_NEXT_L2_INPUT)
     134             :     {
     135           1 :       next_index = vnet_buffer (b)->l2t.next_index;
     136           1 :       goto done;
     137             :     }
     138             : 
     139           0 :   em->counters[node_counter_base_index + L2T_DECAP_ERROR_USER_TO_NETWORK] +=
     140             :     1;
     141             : 
     142           0 :   session_index = vnet_buffer (b)->l2t.session_index;
     143             : 
     144             :   counter_index =
     145           0 :     session_index_to_counter_index (session_index,
     146             :                                     SESSION_COUNTER_USER_TO_NETWORK);
     147             : 
     148             :   /* per-mapping byte stats include the ethernet header */
     149           0 :   vlib_increment_combined_counter (&lm->counter_main,
     150           0 :                                    vlib_get_thread_index (),
     151             :                                    counter_index, 1 /* packet_increment */ ,
     152           0 :                                    vlib_buffer_length_in_chain (vm, b) +
     153             :                                    sizeof (ethernet_header_t));
     154             : 
     155           0 :   session = pool_elt_at_index (lm->sessions, session_index);
     156             : 
     157           0 :   l2tp = vlib_buffer_get_current (b) + sizeof (*ip6);
     158             : 
     159           0 :   if (PREDICT_FALSE (l2tp->session_id != session->local_session_id))
     160             :     {
     161             :       /* Key matched but session id does not. Assume packet is not for us. */
     162           0 :       em->counters[node_counter_base_index +
     163           0 :                    L2T_DECAP_ERROR_SESSION_ID_MISMATCH] += 1;
     164           0 :       next_index = L2T_DECAP_NEXT_NO_INTERCEPT;
     165           0 :       goto done;
     166             :     }
     167             : 
     168           0 :   if (PREDICT_FALSE (l2tp->cookie != session->local_cookie[0]))
     169             :     {
     170           0 :       if (l2tp->cookie != session->local_cookie[1])
     171             :         {
     172             :           /* Key and session ID matched, but cookie doesn't. Drop this packet. */
     173           0 :           b->error = node->errors[L2T_DECAP_ERROR_COOKIE_MISMATCH];
     174           0 :           next_index = L2T_DECAP_NEXT_DROP;
     175           0 :           goto done;
     176             :         }
     177             :     }
     178             : 
     179           0 :   vnet_buffer (b)->sw_if_index[VLIB_RX] = session->sw_if_index;
     180             : 
     181           0 :   if (PREDICT_FALSE (!(session->admin_up)))
     182             :     {
     183           0 :       b->error = node->errors[L2T_DECAP_ERROR_ADMIN_DOWN];
     184           0 :       next_index = L2T_DECAP_NEXT_DROP;
     185           0 :       goto done;
     186             :     }
     187             : 
     188             :   /* strip the ip6 and L2TP header */
     189           0 :   vlib_buffer_advance (b, sizeof (*ip6) + session->l2tp_hdr_size);
     190             : 
     191             :   /* Required to make the l2 tag push / pop code work on l2 subifs */
     192           0 :   vnet_update_l2_len (b);
     193             : 
     194           0 :   if (PREDICT_FALSE (b->flags & VLIB_BUFFER_IS_TRACED))
     195             :     {
     196           0 :       l2t_trace_t *t = vlib_add_trace (vm, node, b, sizeof (*t));
     197           0 :       t->is_user_to_network = 1;
     198           0 :       t->our_address.as_u64[0] = ip6->dst_address.as_u64[0];
     199           0 :       t->our_address.as_u64[1] = ip6->dst_address.as_u64[1];
     200           0 :       t->client_address.as_u64[0] = ip6->src_address.as_u64[0];
     201           0 :       t->client_address.as_u64[1] = ip6->src_address.as_u64[1];
     202           0 :       t->session_index = session_index;
     203             :     }
     204             : 
     205           0 :   return L2T_DECAP_NEXT_L2_INPUT;
     206             : 
     207           1 : done:
     208           1 :   if (next_index == L2T_DECAP_NEXT_NO_INTERCEPT)
     209             :     {
     210             :       /* Small behavioral change between l2tp-decap and l2tp-decap-local */
     211           1 :       if (l2tp_decap_local)
     212             :         {
     213           1 :           b->error = node->errors[L2T_DECAP_ERROR_NO_SESSION];
     214           1 :           next_index = L2T_DECAP_NEXT_DROP;
     215             :         }
     216             :       else
     217             :         {
     218             :           /* Go to next node on the ip6 configuration chain */
     219           0 :           if (PREDICT_TRUE (session != 0))
     220           0 :             vnet_feature_next (&next_index, b);
     221             :         }
     222             :     }
     223             : 
     224           1 :   if (PREDICT_FALSE (b->flags & VLIB_BUFFER_IS_TRACED))
     225             :     {
     226           1 :       l2t_trace_t *t = vlib_add_trace (vm, node, b, sizeof (*t));
     227           1 :       t->is_user_to_network = 1;
     228           1 :       t->our_address.as_u64[0] = ip6->dst_address.as_u64[0];
     229           1 :       t->our_address.as_u64[1] = ip6->dst_address.as_u64[1];
     230           1 :       t->client_address.as_u64[0] = ip6->src_address.as_u64[0];
     231           1 :       t->client_address.as_u64[1] = ip6->src_address.as_u64[1];
     232           1 :       t->session_index = ~0;
     233             :     }
     234           1 :   return next_index;
     235             : }
     236             : 
     237             : #include <vnet/pipeline.h>
     238             : 
     239        2301 : VLIB_NODE_FN (l2t_decap_node) (vlib_main_t * vm,
     240             :                                vlib_node_runtime_t * node,
     241             :                                vlib_frame_t * frame)
     242             : {
     243           1 :   return dispatch_pipeline (vm, node, frame);
     244             : }
     245             : 
     246             : /*
     247             :  * l2tp-decap and l2tp-decap-local have very slightly different behavior.
     248             :  * When a packet has no associated session l2tp-decap let it go to ip6 forward,
     249             :  * while l2tp-decap-local drops it.
     250             :  */
     251             : 
     252             : /* *INDENT-OFF* */
     253      112940 : VLIB_REGISTER_NODE (l2t_decap_node) = {
     254             :   .name = "l2tp-decap",
     255             :   .vector_size = sizeof (u32),
     256             :   .format_trace = format_l2t_trace,
     257             :   .type = VLIB_NODE_TYPE_INTERNAL,
     258             : 
     259             :   .n_errors = ARRAY_LEN(l2t_decap_error_strings),
     260             :   .error_strings = l2t_decap_error_strings,
     261             : 
     262             :   .n_next_nodes = L2T_DECAP_N_NEXT,
     263             : 
     264             :   /* edit / add dispositions here */
     265             :   .next_nodes = {
     266             :         [L2T_DECAP_NEXT_L2_INPUT] = "l2-input",
     267             :         [L2T_DECAP_NEXT_DROP] = "error-drop",
     268             :   },
     269             : };
     270             : /* *INDENT-ON* */
     271             : 
     272             : extern vlib_node_function_t l2t_decap_node_fn;
     273             : 
     274             : /* *INDENT-OFF* */
     275      112940 : VLIB_REGISTER_NODE (l2t_decap_local_node) = {
     276             :   .function = l2t_decap_node_fn,
     277             :   .name = "l2tp-decap-local",
     278             :   .vector_size = sizeof (u32),
     279             :   .format_trace = format_l2t_trace,
     280             :   .type = VLIB_NODE_TYPE_INTERNAL,
     281             : 
     282             :   .n_errors = ARRAY_LEN(l2t_decap_error_strings),
     283             :   .error_strings = l2t_decap_error_strings,
     284             : 
     285             :   .n_next_nodes = L2T_DECAP_N_NEXT,
     286             : 
     287             :   /* edit / add dispositions here */
     288             :   .next_nodes = {
     289             :     [L2T_DECAP_NEXT_L2_INPUT] = "l2-input",
     290             :     [L2T_DECAP_NEXT_DROP] = "error-drop",
     291             :   },
     292             : };
     293             : /* *INDENT-ON* */
     294             : 
     295             : /*
     296             :  * fd.io coding-style-patch-verification: ON
     297             :  *
     298             :  * Local Variables:
     299             :  * eval: (c-set-style "gnu")
     300             :  * End:
     301             :  */

Generated by: LCOV version 1.14