LCOV - code coverage report
Current view: top level - vnet/dpo - ip_null_dpo.c (source / functions) Hit Total Coverage
Test: coverage-filtered.info Lines: 102 104 98.1 %
Date: 2023-10-26 01:39:38 Functions: 14 15 93.3 %

          Line data    Source code
       1             : /*
       2             :  * Copyright (c) 2016 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             :  * @brief
      17             :  * The data-path object representing dropping the packet
      18             :  */
      19             : 
      20             : #include <vnet/dpo/ip_null_dpo.h>
      21             : #include <vnet/ip/ip.h>
      22             : 
      23             : /**
      24             :  * @brief A representation of the IP_NULL DPO
      25             :  */
      26             : typedef struct ip_null_dpo_t_
      27             : {
      28             :     /**
      29             :      * @brief The action to take on a packet
      30             :      */
      31             :     ip_null_dpo_action_t ind_action;
      32             :     /**
      33             :      * @brief The next VLIB node
      34             :      */
      35             :     u32 ind_next_index;
      36             :     /**
      37             :      * rate limits
      38             :      */
      39             : } ip_null_dpo_t;
      40             : 
      41             : /**
      42             :  * @brief the IP_NULL dpos are shared by all routes, hence they are global.
      43             :  * As the neame implies this is only for IP, hence 2.
      44             :  */
      45             : static ip_null_dpo_t ip_null_dpos[2 * IP_NULL_DPO_ACTION_NUM] = {
      46             :     [0] = {
      47             :         /* proto ip4, no action */
      48             :         .ind_action = IP_NULL_ACTION_NONE,
      49             :     },
      50             :     [1] = {
      51             :         /* proto ip4, action send unreach */
      52             :         .ind_action = IP_NULL_ACTION_SEND_ICMP_UNREACH,
      53             :     },
      54             :     [2] = {
      55             :         /* proto ip4, action send unreach */
      56             :         .ind_action = IP_NULL_ACTION_SEND_ICMP_PROHIBIT,
      57             :     },
      58             :     [3] = {
      59             :         /* proto ip6, no action */
      60             :         .ind_action = IP_NULL_ACTION_NONE,
      61             :     },
      62             :     [4] = {
      63             :         /* proto ip6, action send unreach */
      64             :         .ind_action = IP_NULL_ACTION_SEND_ICMP_UNREACH,
      65             :     },
      66             :     [5] = {
      67             :         /* proto ip6, action send unreach */
      68             :         .ind_action = IP_NULL_ACTION_SEND_ICMP_PROHIBIT,
      69             :     },
      70             : };
      71             : 
      72             : /**
      73             :  * @brief Action strings
      74             :  */
      75             : const char *ip_null_action_strings[] = IP_NULL_ACTIONS;
      76             : 
      77             : void
      78           5 : ip_null_dpo_add_and_lock (dpo_proto_t proto,
      79             :                           ip_null_dpo_action_t action,
      80             :                           dpo_id_t *dpo)
      81             : {
      82             :     int i;
      83             : 
      84           5 :     ASSERT((proto == DPO_PROTO_IP4) ||
      85             :            (proto == DPO_PROTO_IP6));
      86           5 :     ASSERT(action < IP_NULL_DPO_ACTION_NUM);
      87             : 
      88           5 :     i = (proto == DPO_PROTO_IP4 ? 0 : 1);
      89             : 
      90           5 :     dpo_set(dpo, DPO_IP_NULL, proto, (i*IP_NULL_DPO_ACTION_NUM) + action);
      91           5 : }
      92             : 
      93             : always_inline const ip_null_dpo_t*
      94           6 : ip_null_dpo_get (index_t indi)
      95             : {
      96           6 :     return (&ip_null_dpos[indi]);
      97             : }
      98             : 
      99             : ip_null_dpo_action_t
     100           0 : ip_null_dpo_get_action (index_t indi)
     101             : {
     102           0 :     return (ip_null_dpos[indi].ind_action);
     103             : }
     104             : 
     105             : static void
     106          22 : ip_null_dpo_lock (dpo_id_t *dpo)
     107             : {
     108             :     /*
     109             :      * not maintaining a lock count on the ip_null, they are const global and
     110             :      * never die.
     111             :      */
     112          22 : }
     113             : static void
     114          22 : ip_null_dpo_unlock (dpo_id_t *dpo)
     115             : {
     116          22 : }
     117             : 
     118             : static u8*
     119           2 : format_ip_null_dpo (u8 *s, va_list *ap)
     120             : {
     121           2 :     index_t index = va_arg(*ap, index_t);
     122           2 :     CLIB_UNUSED(u32 indent) = va_arg(*ap, u32);
     123             :     const ip_null_dpo_t *ind;
     124             :     dpo_proto_t proto;
     125             : 
     126           2 :     ind = ip_null_dpo_get(index);
     127           2 :     proto = (index < IP_NULL_DPO_ACTION_NUM ? DPO_PROTO_IP4 : DPO_PROTO_IP6);
     128             : 
     129           4 :     return (format(s, "%U-null action:%s",
     130             :                    format_dpo_proto, proto,
     131           2 :                    ip_null_action_strings[ind->ind_action]));
     132             : }
     133             : 
     134             : const static dpo_vft_t ip_null_vft = {
     135             :     .dv_lock   = ip_null_dpo_lock,
     136             :     .dv_unlock = ip_null_dpo_unlock,
     137             :     .dv_format = format_ip_null_dpo,
     138             : };
     139             : 
     140             : /**
     141             :  * @brief The per-protocol VLIB graph nodes that are assigned to a ip_null
     142             :  *        object.
     143             :  *
     144             :  * this means that these graph nodes are ones from which a ip_null is the
     145             :  * parent object in the DPO-graph.
     146             :  */
     147             : const static char* const ip4_null_nodes[] =
     148             : {
     149             :     "ip4-null",
     150             :     NULL,
     151             : };
     152             : const static char* const ip6_null_nodes[] =
     153             : {
     154             :     "ip6-null",
     155             :     NULL,
     156             : };
     157             : 
     158             : const static char* const * const ip_null_nodes[DPO_PROTO_NUM] =
     159             : {
     160             :     [DPO_PROTO_IP4] = ip4_null_nodes,
     161             :     [DPO_PROTO_IP6] = ip6_null_nodes,
     162             : };
     163             : 
     164             : typedef struct ip_null_dpo_trace_t_
     165             : {
     166             :     index_t ind_index;
     167             : } ip_null_dpo_trace_t;
     168             : 
     169             : /**
     170             :  * @brief Exit nodes from a IP_NULL
     171             :  */
     172             : typedef enum ip_null_next_t_
     173             : {
     174             :     IP_NULL_NEXT_DROP,
     175             :     IP_NULL_NEXT_ICMP,
     176             :     IP_NULL_NEXT_NUM,
     177             : } ip_null_next_t;
     178             : 
     179             : always_inline uword
     180           4 : ip_null_dpo_switch (vlib_main_t * vm,
     181             :                     vlib_node_runtime_t * node,
     182             :                     vlib_frame_t * frame,
     183             :                     u8 is_ip4)
     184             : {
     185             :     u32 n_left_from, next_index, *from, *to_next;
     186             :     static f64 time_last_seed_change = -1e100;
     187             :     static u32 hash_seeds[3];
     188             :     static uword hash_bitmap[256 / BITS (uword)];
     189             :     f64 time_now;
     190             : 
     191           4 :     from = vlib_frame_vector_args (frame);
     192           4 :     n_left_from = frame->n_vectors;
     193             : 
     194           4 :     time_now = vlib_time_now (vm);
     195           4 :     if (time_now - time_last_seed_change > 1e-1)
     196             :     {
     197             :         uword i;
     198           4 :         u32 * r = clib_random_buffer_get_data (&vm->random_buffer,
     199             :                                                sizeof (hash_seeds));
     200          16 :         for (i = 0; i < ARRAY_LEN (hash_seeds); i++)
     201          12 :             hash_seeds[i] = r[i];
     202             : 
     203             :         /* Mark all hash keys as been not-seen before. */
     204          20 :         for (i = 0; i < ARRAY_LEN (hash_bitmap); i++)
     205          16 :             hash_bitmap[i] = 0;
     206             : 
     207           4 :         time_last_seed_change = time_now;
     208             :     }
     209             : 
     210           4 :     next_index = node->cached_next_index;
     211             : 
     212           8 :     while (n_left_from > 0)
     213             :     {
     214             :         u32 n_left_to_next;
     215             : 
     216           4 :         vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
     217             : 
     218           8 :         while (n_left_from > 0 && n_left_to_next > 0)
     219             :         {
     220             :             u32 a0, b0, c0, m0, drop0;
     221             :             vlib_buffer_t *p0;
     222             :             u32 bi0, indi0, next0;
     223             :             const ip_null_dpo_t *ind0;
     224             :             uword bm0;
     225             : 
     226           4 :             bi0 = from[0];
     227           4 :             to_next[0] = bi0;
     228           4 :             from += 1;
     229           4 :             to_next += 1;
     230           4 :             n_left_from -= 1;
     231           4 :             n_left_to_next -= 1;
     232             : 
     233           4 :             p0 = vlib_get_buffer (vm, bi0);
     234             : 
     235             :             /* lookup dst + src mac */
     236           4 :             indi0 =  vnet_buffer (p0)->ip.adj_index[VLIB_TX];
     237           4 :             ind0 = ip_null_dpo_get(indi0);
     238           4 :             next0 = IP_NULL_NEXT_DROP;
     239             : 
     240             :             /*
     241             :              * rate limit - don't DoS the sender.
     242             :              */
     243           4 :             a0 = hash_seeds[0];
     244           4 :             b0 = hash_seeds[1];
     245           4 :             c0 = hash_seeds[2];
     246             : 
     247           4 :             if (is_ip4)
     248             :             {
     249           2 :                 ip4_header_t *ip0 = vlib_buffer_get_current (p0);
     250             : 
     251           2 :                 a0 ^= ip0->dst_address.data_u32;
     252           2 :                 b0 ^= ip0->src_address.data_u32;
     253             : 
     254           2 :                 hash_v3_finalize32 (a0, b0, c0);
     255             :             }
     256             :             else
     257             :             {
     258           2 :                 ip6_header_t *ip0 = vlib_buffer_get_current (p0);
     259             : 
     260           2 :                 a0 ^= ip0->dst_address.as_u32[0];
     261           2 :                 b0 ^= ip0->src_address.as_u32[0];
     262           2 :                 c0 ^= ip0->src_address.as_u32[1];
     263             : 
     264           2 :                 hash_v3_mix32 (a0, b0, c0);
     265             : 
     266           2 :                 a0 ^= ip0->dst_address.as_u32[1];
     267           2 :                 b0 ^= ip0->src_address.as_u32[2];
     268           2 :                 c0 ^= ip0->src_address.as_u32[3];
     269             : 
     270           2 :                 hash_v3_finalize32 (a0, b0, c0);
     271             :             }
     272             : 
     273           4 :             c0 &= BITS (hash_bitmap) - 1;
     274           4 :             c0 = c0 / BITS (uword);
     275           4 :             m0 = (uword) 1 << (c0 % BITS (uword));
     276             : 
     277           4 :             bm0 = hash_bitmap[c0];
     278           4 :             drop0 = (bm0 & m0) != 0;
     279             : 
     280             :             /* Mark it as seen. */
     281           4 :             hash_bitmap[c0] = bm0 | m0;
     282             : 
     283           4 :             if (PREDICT_FALSE(!drop0))
     284             :             {
     285           4 :                 if (is_ip4)
     286             :                 {
     287             :                     /*
     288             :                      * There's a trade-off here. This conditinal statement
     289             :                      * versus a graph node per-condition. Given the number
     290             :                      * expect number of packets to reach a null route is 0
     291             :                      * we favour the run-time cost over the graph complexity
     292             :                      */
     293           2 :                     if (IP_NULL_ACTION_SEND_ICMP_UNREACH == ind0->ind_action)
     294             :                     {
     295           1 :                         next0 = IP_NULL_NEXT_ICMP;
     296           1 :                         icmp4_error_set_vnet_buffer(
     297             :                             p0,
     298             :                             ICMP4_destination_unreachable,
     299             :                             ICMP4_destination_unreachable_destination_unreachable_host,
     300             :                             0);
     301             :                     }
     302           1 :                     else if (IP_NULL_ACTION_SEND_ICMP_PROHIBIT == ind0->ind_action)
     303             :                     {
     304           1 :                         next0 = IP_NULL_NEXT_ICMP;
     305           1 :                         icmp4_error_set_vnet_buffer(
     306             :                             p0,
     307             :                             ICMP4_destination_unreachable,
     308             :                             ICMP4_destination_unreachable_host_administratively_prohibited,
     309             :                             0);
     310             :                     }
     311             :                 }
     312             :                 else
     313             :                 {
     314           2 :                     if (IP_NULL_ACTION_SEND_ICMP_UNREACH == ind0->ind_action)
     315             :                     {
     316           1 :                         next0 = IP_NULL_NEXT_ICMP;
     317           1 :                         icmp6_error_set_vnet_buffer(
     318             :                             p0,
     319             :                             ICMP6_destination_unreachable,
     320             :                             ICMP6_destination_unreachable_no_route_to_destination,
     321             :                             0);
     322             :                     }
     323           1 :                     else if (IP_NULL_ACTION_SEND_ICMP_PROHIBIT == ind0->ind_action)
     324             :                     {
     325           1 :                         next0 = IP_NULL_NEXT_ICMP;
     326           1 :                         icmp6_error_set_vnet_buffer(
     327             :                             p0,
     328             :                             ICMP6_destination_unreachable,
     329             :                             ICMP6_destination_unreachable_destination_administratively_prohibited,
     330             :                             0);
     331             :                     }
     332             :                 }
     333             :             }
     334             : 
     335           4 :             if (PREDICT_FALSE (p0->flags & VLIB_BUFFER_IS_TRACED))
     336             :             {
     337           4 :                 ip_null_dpo_trace_t *tr = vlib_add_trace (vm, node, p0,
     338             :                                                           sizeof (*tr));
     339           4 :                 tr->ind_index = indi0;
     340             :             }
     341           4 :             vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next,
     342             :                                              n_left_to_next, bi0, next0);
     343             :         }
     344             : 
     345           4 :         vlib_put_next_frame (vm, node, next_index, n_left_to_next);
     346             :     }
     347             : 
     348           4 :     return frame->n_vectors;
     349             : }
     350             : 
     351             : static u8 *
     352           2 : format_ip_null_dpo_trace (u8 * s, va_list * args)
     353             : {
     354           2 :   CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
     355           2 :   CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
     356           2 :   ip_null_dpo_trace_t *t = va_arg (*args, ip_null_dpo_trace_t *);
     357             : 
     358           2 :   s = format (s, "%U", format_ip_null_dpo, t->ind_index, 0);
     359           2 :   return s;
     360             : }
     361             : 
     362             : static uword
     363           2 : ip4_null_dpo_switch (vlib_main_t * vm,
     364             :                     vlib_node_runtime_t * node,
     365             :                     vlib_frame_t * frame)
     366             : {
     367           2 :     return (ip_null_dpo_switch(vm, node, frame, 1));
     368             : }
     369             : 
     370             : /**
     371             :  * @brief
     372             :  */
     373      183788 : VLIB_REGISTER_NODE (ip4_null_dpo_node) = {
     374             :   .function = ip4_null_dpo_switch,
     375             :   .name = "ip4-null",
     376             :   .vector_size = sizeof (u32),
     377             : 
     378             :   .format_trace = format_ip_null_dpo_trace,
     379             :   .n_next_nodes = IP_NULL_NEXT_NUM,
     380             :   .next_nodes = {
     381             :       [IP_NULL_NEXT_DROP] = "ip4-drop",
     382             :       [IP_NULL_NEXT_ICMP] = "ip4-icmp-error",
     383             :   },
     384             : };
     385             : 
     386             : static uword
     387           2 : ip6_null_dpo_switch (vlib_main_t * vm,
     388             :                     vlib_node_runtime_t * node,
     389             :                     vlib_frame_t * frame)
     390             : {
     391           2 :     return (ip_null_dpo_switch(vm, node, frame, 0));
     392             : }
     393             : 
     394             : /**
     395             :  * @brief
     396             :  */
     397      183788 : VLIB_REGISTER_NODE (ip6_null_dpo_node) = {
     398             :   .function = ip6_null_dpo_switch,
     399             :   .name = "ip6-null",
     400             :   .vector_size = sizeof (u32),
     401             : 
     402             :   .format_trace = format_ip_null_dpo_trace,
     403             :   .n_next_nodes = IP_NULL_NEXT_NUM,
     404             :   .next_nodes = {
     405             :       [IP_NULL_NEXT_DROP] = "ip6-drop",
     406             :       [IP_NULL_NEXT_ICMP] = "ip6-icmp-error",
     407             :   },
     408             : };
     409             : 
     410             : void
     411         575 : ip_null_dpo_module_init (void)
     412             : {
     413         575 :     dpo_register(DPO_IP_NULL, &ip_null_vft, ip_null_nodes);
     414         575 : }

Generated by: LCOV version 1.14