LCOV - code coverage report
Current view: top level - plugins/mss_clamp - mss_clamp_node.c (source / functions) Hit Total Coverage
Test: coverage-filtered.info Lines: 132 136 97.1 %
Date: 2023-07-05 22:20:52 Functions: 23 23 100.0 %

          Line data    Source code
       1             : /*
       2             :  * mss_clamp_node.c - Node implementing TCP MSS clamping
       3             :  *
       4             :  * Copyright (c) 2018 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             : #include <vlib/vlib.h>
      18             : #include <vnet/vnet.h>
      19             : #include <vnet/pg/pg.h>
      20             : #include <vppinfra/error.h>
      21             : #include <mss_clamp/mss_clamp.h>
      22             : #include <mss_clamp/mss_clamp.api_enum.h>
      23             : #include <vnet/fib/fib_types.h>
      24             : #include <vnet/feature/feature.h>
      25             : #include <vnet/ip/ip4.h>
      26             : #include <vnet/ip/ip6.h>
      27             : #include <vnet/tcp/tcp_packet.h>
      28             : 
      29             : extern vlib_node_registration_t mssc_ip4_in_node, mssc_ip4_out_node;
      30             : extern vlib_node_registration_t mssc_ip6_in_node, mssc_ip6_out_node;
      31             : 
      32             : typedef struct mssc_trace_t_
      33             : {
      34             :   u32 max_mss;
      35             :   u32 clamped;
      36             : } mssc_trace_t;
      37             : 
      38             : /* packet trace format function */
      39             : static u8 *
      40         600 : format_mssc_trace (u8 *s, va_list *args)
      41             : {
      42         600 :   CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
      43         600 :   CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
      44         600 :   mssc_trace_t *t = va_arg (*args, mssc_trace_t *);
      45             : 
      46         600 :   s = format (s, "max mss: %d clamped: %d", t->max_mss, t->clamped);
      47         600 :   return s;
      48             : }
      49             : 
      50             : typedef enum
      51             : {
      52             :   MSSC_NEXT_DROP,
      53             :   MSSC_N_NEXT,
      54             : } mssc_next_t;
      55             : 
      56             : /*
      57             :  * fixup the maximum segment size if it's a syn packet
      58             :  * return 1 if the mss was changed otherwise 0
      59             :  */
      60             : always_inline u32
      61         780 : mssc_mss_fixup (vlib_buffer_t *b0, tcp_header_t *tcp0, u16 max_mss0)
      62             : {
      63             :   ip_csum_t sum0;
      64             : 
      65         780 :   if (PREDICT_FALSE (tcp_syn (tcp0)))
      66             :     {
      67             :       u8 opt_len, opts_len, kind;
      68             :       const u8 *data;
      69             :       u16 mss0, new_mss0;
      70             : 
      71         780 :       opts_len = (tcp_doff (tcp0) << 2) - sizeof (tcp_header_t);
      72         780 :       data = (const u8 *) (tcp0 + 1);
      73             : 
      74        1040 :       for (; opts_len > 0; opts_len -= opt_len, data += opt_len)
      75             :         {
      76        1040 :           kind = data[0];
      77             : 
      78             :           /* Get options length */
      79        1040 :           if (kind == TCP_OPTION_EOL)
      80         260 :             break;
      81         780 :           else if (kind == TCP_OPTION_NOOP)
      82             :             {
      83           0 :               opt_len = 1;
      84           0 :               continue;
      85             :             }
      86             :           else
      87             :             {
      88             :               /* broken options */
      89         780 :               if (opts_len < 2)
      90           0 :                 return 0;
      91         780 :               opt_len = data[1];
      92             : 
      93             :               /* weird option length */
      94         780 :               if (opt_len < 2 || opt_len > opts_len)
      95           0 :                 return 0;
      96             :             }
      97             : 
      98         780 :           if (kind == TCP_OPTION_MSS)
      99             :             {
     100         780 :               mss0 = *(u16 *) (data + 2);
     101         780 :               if (clib_net_to_host_u16 (mss0) > max_mss0)
     102             :                 {
     103         520 :                   new_mss0 = clib_host_to_net_u16 (max_mss0);
     104         520 :                   *((u16 *) (data + 2)) = new_mss0;
     105         520 :                   sum0 = tcp0->checksum;
     106         520 :                   sum0 = ip_csum_update (sum0, mss0, new_mss0, tcp_header_t,
     107             :                                          checksum);
     108         520 :                   tcp0->checksum = ip_csum_fold (sum0);
     109         520 :                   return 1;
     110             :                 }
     111             :             }
     112             :         }
     113             :     }
     114             : 
     115         260 :   return 0;
     116             : }
     117             : 
     118             : always_inline uword
     119          14 : mssc_inline (vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame,
     120             :              vlib_dir_t dir, fib_protocol_t fproto)
     121             : {
     122          14 :   mssc_main_t *cm = &mssc_main;
     123             :   vlib_buffer_t *bufs[VLIB_FRAME_SIZE], **b;
     124             :   u16 nexts[VLIB_FRAME_SIZE], *next;
     125             :   u32 n_left, *from;
     126          14 :   u32 pkts_clamped = 0;
     127             : 
     128          14 :   from = vlib_frame_vector_args (frame);
     129          14 :   n_left = frame->n_vectors;
     130          14 :   b = bufs;
     131          14 :   next = nexts;
     132             : 
     133          14 :   vlib_get_buffers (vm, from, bufs, n_left);
     134             : 
     135         386 :   while (n_left >= 4)
     136             :     {
     137             :       u32 sw_if_index0, sw_if_index1;
     138             :       const u8 *h0, *h1;
     139             :       u32 clamped0, clamped1;
     140             : 
     141             :       /* Prefetch next iteration. */
     142             :       {
     143         372 :         vlib_prefetch_buffer_header (b[2], LOAD);
     144         372 :         vlib_prefetch_buffer_header (b[3], LOAD);
     145         372 :         vlib_prefetch_buffer_data (b[2], LOAD);
     146         372 :         vlib_prefetch_buffer_data (b[3], LOAD);
     147             :       }
     148             : 
     149         372 :       sw_if_index0 = vnet_buffer (b[0])->sw_if_index[dir];
     150         372 :       sw_if_index1 = vnet_buffer (b[1])->sw_if_index[dir];
     151         372 :       clamped0 = clamped1 = 0;
     152             : 
     153             :       /* speculatively enqueue b0 to the current next frame */
     154         372 :       vnet_feature_next_u16 (&next[0], b[0]);
     155         372 :       vnet_feature_next_u16 (&next[1], b[1]);
     156             : 
     157         372 :       h0 = (u8 *) vlib_buffer_get_current (b[0]);
     158         372 :       h1 = (u8 *) vlib_buffer_get_current (b[1]);
     159         372 :       if (VLIB_TX == dir)
     160             :         {
     161         186 :           h0 += vnet_buffer (b[0])->ip.save_rewrite_length;
     162         186 :           h1 += vnet_buffer (b[1])->ip.save_rewrite_length;
     163             :         }
     164             : 
     165         372 :       if (FIB_PROTOCOL_IP4 == fproto)
     166             :         {
     167         186 :           ip4_header_t *ip0 = (ip4_header_t *) h0;
     168         186 :           ip4_header_t *ip1 = (ip4_header_t *) h1;
     169             : 
     170         186 :           if (IP_PROTOCOL_TCP == ip0->protocol)
     171             :             {
     172         186 :               clamped0 = mssc_mss_fixup (b[0], ip4_next_header (ip0),
     173         186 :                                          cm->max_mss4[sw_if_index0]);
     174             :             }
     175         186 :           if (IP_PROTOCOL_TCP == ip1->protocol)
     176             :             {
     177         186 :               clamped1 = mssc_mss_fixup (b[1], ip4_next_header (ip1),
     178         186 :                                          cm->max_mss4[sw_if_index1]);
     179             :             }
     180             :         }
     181         186 :       else if (FIB_PROTOCOL_IP6 == fproto)
     182             :         {
     183         186 :           ip6_header_t *ip0 = (ip6_header_t *) h0;
     184         186 :           ip6_header_t *ip1 = (ip6_header_t *) h1;
     185             : 
     186         186 :           if (IP_PROTOCOL_TCP == ip0->protocol)
     187             :             {
     188         186 :               clamped0 = mssc_mss_fixup (b[0], ip6_next_header (ip0),
     189         186 :                                          cm->max_mss6[sw_if_index0]);
     190             :             }
     191         186 :           if (IP_PROTOCOL_TCP == ip1->protocol)
     192             :             {
     193         186 :               clamped1 = mssc_mss_fixup (b[1], ip6_next_header (ip1),
     194         186 :                                          cm->max_mss6[sw_if_index1]);
     195             :             }
     196             :         }
     197             : 
     198         372 :       pkts_clamped += clamped0 + clamped1;
     199             : 
     200         372 :       if (PREDICT_FALSE (node->flags & VLIB_NODE_FLAG_TRACE))
     201             :         {
     202         372 :           if (b[0]->flags & VLIB_BUFFER_IS_TRACED)
     203             :             {
     204             :               mssc_trace_t *t;
     205             : 
     206         372 :               t = vlib_add_trace (vm, node, b[0], sizeof (*t));
     207         372 :               t->max_mss = (FIB_PROTOCOL_IP4 == fproto) ?
     208         372 :                              cm->max_mss4[sw_if_index0] :
     209         186 :                              cm->max_mss6[sw_if_index0];
     210         372 :               t->clamped = clamped0;
     211             :             }
     212         372 :           if (b[1]->flags & VLIB_BUFFER_IS_TRACED)
     213             :             {
     214             :               mssc_trace_t *t;
     215             : 
     216         372 :               t = vlib_add_trace (vm, node, b[1], sizeof (*t));
     217         372 :               t->max_mss = (FIB_PROTOCOL_IP4 == fproto) ?
     218         372 :                              cm->max_mss4[sw_if_index1] :
     219         186 :                              cm->max_mss6[sw_if_index1];
     220         372 :               t->clamped = clamped1;
     221             :             }
     222             :         }
     223             : 
     224         372 :       b += 2;
     225         372 :       next += 2;
     226         372 :       n_left -= 2;
     227             :     }
     228             : 
     229          52 :   while (n_left > 0)
     230             :     {
     231             :       u32 sw_if_index0;
     232             :       const u8 *h0;
     233             :       u32 clamped0;
     234             : 
     235          38 :       sw_if_index0 = vnet_buffer (b[0])->sw_if_index[dir];
     236          38 :       clamped0 = 0;
     237             : 
     238             :       /* speculatively enqueue b0 to the current next frame */
     239          38 :       vnet_feature_next_u16 (&next[0], b[0]);
     240             : 
     241          38 :       h0 = (u8 *) vlib_buffer_get_current (b[0]);
     242          38 :       if (VLIB_TX == dir)
     243          20 :         h0 += vnet_buffer (b[0])->ip.save_rewrite_length;
     244             : 
     245          38 :       if (FIB_PROTOCOL_IP4 == fproto)
     246             :         {
     247          18 :           ip4_header_t *ip0 = (ip4_header_t *) h0;
     248             : 
     249          18 :           if (IP_PROTOCOL_TCP == ip0->protocol)
     250             :             {
     251          18 :               clamped0 = mssc_mss_fixup (b[0], ip4_next_header (ip0),
     252          18 :                                          cm->max_mss4[sw_if_index0]);
     253             :             }
     254             :         }
     255          20 :       else if (FIB_PROTOCOL_IP6 == fproto)
     256             :         {
     257          20 :           ip6_header_t *ip0 = (ip6_header_t *) h0;
     258             : 
     259          20 :           if (IP_PROTOCOL_TCP == ip0->protocol)
     260             :             {
     261          18 :               clamped0 = mssc_mss_fixup (b[0], ip6_next_header (ip0),
     262          18 :                                          cm->max_mss6[sw_if_index0]);
     263             :             }
     264             :         }
     265             : 
     266          38 :       pkts_clamped += clamped0;
     267             : 
     268          38 :       if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE) &&
     269             :                          (b[0]->flags & VLIB_BUFFER_IS_TRACED)))
     270             :         {
     271             :           mssc_trace_t *t;
     272             : 
     273          36 :           t = vlib_add_trace (vm, node, b[0], sizeof (*t));
     274          36 :           t->max_mss = (FIB_PROTOCOL_IP4 == fproto) ?
     275          36 :                          cm->max_mss4[sw_if_index0] :
     276          18 :                          cm->max_mss6[sw_if_index0];
     277          36 :           t->clamped = clamped0;
     278             :         }
     279             : 
     280          38 :       b++;
     281          38 :       next++;
     282          38 :       n_left--;
     283             :     }
     284             : 
     285          14 :   vlib_buffer_enqueue_to_next (vm, node, from, nexts, frame->n_vectors);
     286          14 :   vlib_node_increment_counter (vm, node->node_index, MSS_CLAMP_ERROR_CLAMPED,
     287             :                                pkts_clamped);
     288             : 
     289          14 :   return frame->n_vectors;
     290             : }
     291             : 
     292             : static uword
     293           3 : mssc_ip4_in (vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame)
     294             : {
     295           3 :   return (mssc_inline (vm, node, frame, VLIB_RX, FIB_PROTOCOL_IP4));
     296             : }
     297             : 
     298             : static uword
     299           3 : mssc_ip4_out (vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame)
     300             : {
     301           3 :   return (mssc_inline (vm, node, frame, VLIB_TX, FIB_PROTOCOL_IP4));
     302             : }
     303             : 
     304             : static uword
     305           3 : mssc_ip6_in (vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame)
     306             : {
     307           3 :   return (mssc_inline (vm, node, frame, VLIB_RX, FIB_PROTOCOL_IP6));
     308             : }
     309             : 
     310             : static uword
     311           5 : mssc_ip6_out (vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame)
     312             : {
     313           5 :   return (mssc_inline (vm, node, frame, VLIB_TX, FIB_PROTOCOL_IP6));
     314             : }
     315             : 
     316       73944 : VLIB_REGISTER_NODE (mssc_ip4_in_node) =
     317             : {
     318             :   .function = mssc_ip4_in,
     319             :   .name = "tcp-mss-clamping-ip4-in",
     320             :   .vector_size = sizeof (u32),
     321             :   .format_trace = format_mssc_trace,
     322             :   .type = VLIB_NODE_TYPE_INTERNAL,
     323             : 
     324             :   .n_errors = MSS_CLAMP_N_ERROR,
     325             :   .error_counters = mss_clamp_error_counters,
     326             : 
     327             :   .n_next_nodes = MSSC_N_NEXT,
     328             :   .next_nodes = {
     329             :         [MSSC_NEXT_DROP] = "error-drop",
     330             :   },
     331             : };
     332             : 
     333       73944 : VLIB_REGISTER_NODE (mssc_ip4_out_node) =
     334             : {
     335             :   .function = mssc_ip4_out,
     336             :   .name = "tcp-mss-clamping-ip4-out",
     337             :   .vector_size = sizeof (u32),
     338             :   .format_trace = format_mssc_trace,
     339             :   .type = VLIB_NODE_TYPE_INTERNAL,
     340             : 
     341             :   .n_errors = MSS_CLAMP_N_ERROR,
     342             :   .error_counters = mss_clamp_error_counters,
     343             : 
     344             :   .n_next_nodes = MSSC_N_NEXT,
     345             :   .next_nodes = {
     346             :         [MSSC_NEXT_DROP] = "error-drop",
     347             :   },
     348             : };
     349             : 
     350       73944 : VLIB_REGISTER_NODE (mssc_ip6_in_node) =
     351             : {
     352             :   .function = mssc_ip6_in,
     353             :   .name = "tcp-mss-clamping-ip6-in",
     354             :   .vector_size = sizeof (u32),
     355             :   .format_trace = format_mssc_trace,
     356             :   .type = VLIB_NODE_TYPE_INTERNAL,
     357             : 
     358             :   .n_errors = MSS_CLAMP_N_ERROR,
     359             :   .error_counters = mss_clamp_error_counters,
     360             : 
     361             :   .n_next_nodes = MSSC_N_NEXT,
     362             :   .next_nodes = {
     363             :         [MSSC_NEXT_DROP] = "error-drop",
     364             :   },
     365             : };
     366             : 
     367       73944 : VLIB_REGISTER_NODE (mssc_ip6_out_node) =
     368             : {
     369             :   .function = mssc_ip6_out,
     370             :   .name = "tcp-mss-clamping-ip6-out",
     371             :   .vector_size = sizeof (u32),
     372             :   .format_trace = format_mssc_trace,
     373             :   .type = VLIB_NODE_TYPE_INTERNAL,
     374             : 
     375             :   .n_errors = MSS_CLAMP_N_ERROR,
     376             :   .error_counters = mss_clamp_error_counters,
     377             : 
     378             :   .n_next_nodes = MSSC_N_NEXT,
     379             :   .next_nodes = {
     380             :         [MSSC_NEXT_DROP] = "error-drop",
     381             :   },
     382             : };
     383             : 
     384       39199 : VNET_FEATURE_INIT (mssc_ip4_in_feat, static) = {
     385             :   .arc_name = "ip4-unicast",
     386             :   .node_name = "tcp-mss-clamping-ip4-in",
     387             :   .runs_after = VNET_FEATURES ("ip4-policer-classify"),
     388             : };
     389             : 
     390       39199 : VNET_FEATURE_INIT (mssc_ip4_out_feat, static) = {
     391             :   .arc_name = "ip4-output",
     392             :   .node_name = "tcp-mss-clamping-ip4-out",
     393             : };
     394             : 
     395       39199 : VNET_FEATURE_INIT (mssc_ip6_in_feat, static) = {
     396             :   .arc_name = "ip6-unicast",
     397             :   .node_name = "tcp-mss-clamping-ip6-in",
     398             :   .runs_after = VNET_FEATURES ("ip6-policer-classify"),
     399             : };
     400             : 
     401       39199 : VNET_FEATURE_INIT (mssc_ip6_out_feat, static) = {
     402             :   .arc_name = "ip6-output",
     403             :   .node_name = "tcp-mss-clamping-ip6-out",
     404             : };
     405             : 
     406             : /*
     407             :  * fd.io coding-style-patch-verification: ON
     408             :  *
     409             :  * Local Variables:
     410             :  * eval: (c-set-style "gnu")
     411             :  * End:
     412             :  */

Generated by: LCOV version 1.14