LCOV - code coverage report
Current view: top level - plugins/igmp - igmp_input.c (source / functions) Hit Total Coverage
Test: coverage-filtered.info Lines: 150 161 93.2 %
Date: 2023-10-26 01:39:38 Functions: 15 15 100.0 %

          Line data    Source code
       1             : /*
       2             :  *------------------------------------------------------------------
       3             :  * Copyright (c) 2017 Cisco and/or its affiliates.
       4             :  * Licensed under the Apache License, Version 2.0 (the "License");
       5             :  * you may not use this file except in compliance with the License.
       6             :  * You may obtain a copy of the License at:
       7             :  *
       8             :  *     http://www.apache.org/licenses/LICENSE-2.0
       9             :  *
      10             :  * Unless required by applicable law or agreed to in writing, software
      11             :  * distributed under the License is distributed on an "AS IS" BASIS,
      12             :  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
      13             :  * See the License for the specific language governing permissions and
      14             :  * limitations under the License.
      15             :  *------------------------------------------------------------------
      16             :  */
      17             : 
      18             : #include <vlib/vlib.h>
      19             : #include <vlibmemory/api.h>
      20             : #include <vnet/plugin/plugin.h>
      21             : #include <vpp/app/version.h>
      22             : #include <vnet/ip/ip.h>
      23             : #include <vlib/unix/unix.h>
      24             : #include <vnet/adj/adj_mcast.h>
      25             : 
      26             : #include <igmp/igmp.h>
      27             : #include <igmp/igmp_pkt.h>
      28             : #include <igmp/igmp_query.h>
      29             : #include <igmp/igmp_report.h>
      30             : #include <igmp/igmp_error.h>
      31             : 
      32             : #include <limits.h>
      33             : 
      34             : typedef enum
      35             : {
      36             :   IGMP_INPUT_NEXT_DROP,
      37             :   IGMP_INPUT_NEXT_PARSE_QUERY,
      38             :   IGMP_INPUT_NEXT_PARSE_REPORT,
      39             :   IGMP_INPUT_N_NEXT,
      40             : } igmp_input_next_t;
      41             : 
      42             : typedef enum
      43             : {
      44             :   IGMP_PARSE_QUERY_NEXT_DROP,
      45             :   IGMP_PARSE_QUERY_N_NEXT,
      46             : } igmp_parse_query_next_t;
      47             : 
      48             : typedef enum
      49             : {
      50             :   IGMP_PARSE_REPORT_NEXT_DROP,
      51             :   IGMP_PARSE_REPORT_N_NEXT,
      52             : } igmp_parse_report_next_t;
      53             : 
      54             : char *igmp_error_strings[] = {
      55             : #define _(sym,string) string,
      56             :   foreach_igmp_error
      57             : #undef _
      58             : };
      59             : 
      60             : typedef struct
      61             : {
      62             :   u32 next_index;
      63             :   u32 sw_if_index;
      64             :   u32 len;
      65             :   u8 packet_data[64];
      66             : } igmp_input_trace_t;
      67             : 
      68             : static u8 *
      69           4 : format_igmp_input_trace (u8 * s, va_list * va)
      70             : {
      71           4 :   CLIB_UNUSED (vlib_main_t * vm) = va_arg (*va, vlib_main_t *);
      72           4 :   CLIB_UNUSED (vlib_node_t * node) = va_arg (*va, vlib_node_t *);
      73           4 :   igmp_input_trace_t *t = va_arg (*va, igmp_input_trace_t *);
      74             : 
      75           4 :   s = format (s, "sw_if_index %u next-index %u",
      76             :               t->sw_if_index, t->next_index);
      77           4 :   s = format (s, "\n%U", format_igmp_header, t->packet_data,
      78             :               sizeof (t->packet_data));
      79           4 :   return s;
      80             : }
      81             : 
      82             : static u8 *
      83           2 : format_igmp_parse_report_trace (u8 * s, va_list * va)
      84             : {
      85           2 :   CLIB_UNUSED (vlib_main_t * vm) = va_arg (*va, vlib_main_t *);
      86           2 :   CLIB_UNUSED (vlib_node_t * node) = va_arg (*va, vlib_node_t *);
      87           2 :   igmp_input_trace_t *t = va_arg (*va, igmp_input_trace_t *);
      88             : 
      89           2 :   s = format (s, "sw_if_index %u next-index %u",
      90             :               t->sw_if_index, t->next_index);
      91           2 :   s = format (s, "\n%U", format_igmp_report_v3, t->packet_data,
      92             :               sizeof (t->packet_data));
      93           2 :   return s;
      94             : }
      95             : 
      96             : static u8 *
      97           2 : format_igmp_parse_query_trace (u8 * s, va_list * va)
      98             : {
      99           2 :   CLIB_UNUSED (vlib_main_t * vm) = va_arg (*va, vlib_main_t *);
     100           2 :   CLIB_UNUSED (vlib_node_t * node) = va_arg (*va, vlib_node_t *);
     101           2 :   igmp_input_trace_t *t = va_arg (*va, igmp_input_trace_t *);
     102             : 
     103           2 :   s = format (s, "sw_if_index %u next-input %u len %u",
     104             :               t->sw_if_index, t->next_index, t->len);
     105           2 :   s = format (s, "\n%U", format_igmp_query_v3, t->packet_data,
     106             :               sizeof (t->packet_data));
     107           2 :   s = format (s, "\n%U", format_hex_bytes,
     108           2 :               t->packet_data, sizeof (t->packet_data));
     109           2 :   return s;
     110             : }
     111             : 
     112             : static uword
     113          26 : igmp_input (vlib_main_t * vm, vlib_node_runtime_t * node,
     114             :             vlib_frame_t * frame)
     115             : {
     116             :   igmp_parse_query_next_t next_index;
     117             :   u32 n_left_from, *from, *to_next;
     118             :   vlib_node_runtime_t *error_node;
     119             :   u8 error;
     120             : 
     121          26 :   error = IGMP_ERROR_NONE;
     122          26 :   error_node = node;
     123             : 
     124          26 :   from = vlib_frame_vector_args (frame);
     125          26 :   n_left_from = frame->n_vectors;
     126          26 :   next_index = node->cached_next_index;
     127             : 
     128          52 :   while (n_left_from > 0)
     129             :     {
     130             :       u32 n_left_to_next;
     131             : 
     132          26 :       vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
     133             : 
     134          54 :       while (n_left_from > 0 && n_left_to_next > 0)
     135             :         {
     136             :           igmp_header_t *igmp;
     137             :           u16 checksum, csum;
     138             :           vlib_buffer_t *b;
     139             :           ip4_header_t *ip;
     140             :           ip_csum_t sum;
     141             :           u32 bi, next;
     142             : 
     143          28 :           next = IGMP_INPUT_NEXT_DROP;
     144          28 :           bi = from[0];
     145          28 :           to_next[0] = bi;
     146          28 :           from++;
     147          28 :           to_next++;
     148          28 :           n_left_from--;
     149          28 :           n_left_to_next--;
     150             : 
     151          28 :           b = vlib_get_buffer (vm, bi);
     152          28 :           ip = vlib_buffer_get_current (b);
     153             : 
     154          28 :           if (ip->protocol != IP_PROTOCOL_IGMP)
     155             :             {
     156           0 :               error = IGMP_ERROR_INVALID_PROTOCOL;
     157           0 :               next = IGMP_INPUT_NEXT_DROP;
     158           0 :               goto next_buffer;
     159             :             }
     160             : 
     161          28 :           vlib_buffer_advance (b, ip4_header_bytes (ip));
     162             : 
     163          28 :           igmp = vlib_buffer_get_current (b);
     164             : 
     165          28 :           checksum = igmp->checksum;
     166          28 :           igmp->checksum = 0;
     167          28 :           sum = ip_incremental_checksum (0, igmp,
     168          56 :                                          clib_net_to_host_u16 (ip->length) -
     169          28 :                                          ip4_header_bytes (ip));
     170          28 :           igmp->checksum = checksum;
     171          28 :           csum = ~ip_csum_fold (sum);
     172          28 :           if (checksum != csum)
     173             :             {
     174           0 :               error = IGMP_ERROR_BAD_CHECKSUM;
     175           0 :               next = IGMP_INPUT_NEXT_DROP;
     176           0 :               goto next_buffer;
     177             :             }
     178          28 :           if (!igmp_config_lookup (vnet_buffer (b)->sw_if_index[VLIB_RX]))
     179             :             {
     180           1 :               error = IGMP_ERROR_NOT_ENABLED;
     181           1 :               next = IGMP_INPUT_NEXT_DROP;
     182           1 :               goto next_buffer;
     183             :             }
     184             : 
     185             :           /* TODO: IGMPv2 and IGMPv1 */
     186          27 :           switch (igmp->type)
     187             :             {
     188          14 :             case IGMP_TYPE_membership_query:
     189          14 :               next = IGMP_INPUT_NEXT_PARSE_QUERY;
     190          14 :               break;
     191          13 :             case IGMP_TYPE_membership_report_v3:
     192          13 :               next = IGMP_INPUT_NEXT_PARSE_REPORT;
     193          13 :               break;
     194           0 :             default:
     195           0 :               error = IGMP_ERROR_UNKNOWN_TYPE;
     196           0 :               next = IGMP_INPUT_NEXT_DROP;
     197           0 :               break;
     198             :             }
     199          28 :         next_buffer:
     200          28 :           b->error = error_node->errors[error];
     201             : 
     202          28 :           if (node->flags & VLIB_NODE_FLAG_TRACE)
     203             :             {
     204             :               igmp_input_trace_t *tr;
     205          28 :               tr = vlib_add_trace (vm, node, b, sizeof (*tr));
     206          28 :               tr->next_index = next;
     207          28 :               tr->sw_if_index = vnet_buffer (b)->sw_if_index[VLIB_RX];
     208          28 :               tr->len = vlib_buffer_length_in_chain (vm, b);
     209          28 :               clib_memcpy_fast (tr->packet_data, vlib_buffer_get_current (b),
     210             :                                 sizeof (tr->packet_data));
     211             :             }
     212             : 
     213          28 :           vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next,
     214             :                                            n_left_to_next, bi, next);
     215             :         }
     216          26 :       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
     217             :     }
     218             : 
     219          26 :   return frame->n_vectors;
     220             : }
     221             : 
     222             : /* *INDENT-OFF* */
     223      128492 : VLIB_REGISTER_NODE (igmp_input_node) =
     224             : {
     225             :   .function = igmp_input,
     226             :   .name = "igmp-input",
     227             :   .vector_size = sizeof (u32),
     228             : 
     229             :   .format_buffer = format_igmp_header,
     230             :   .format_trace = format_igmp_input_trace,
     231             : 
     232             :   .n_errors = IGMP_N_ERROR,
     233             :   .error_strings = igmp_error_strings,
     234             : 
     235             :   .n_next_nodes = IGMP_INPUT_N_NEXT,
     236             :   .next_nodes = {
     237             :       [IGMP_INPUT_NEXT_DROP] = "error-drop",
     238             :       [IGMP_INPUT_NEXT_PARSE_QUERY] = "igmp-parse-query",
     239             :       [IGMP_INPUT_NEXT_PARSE_REPORT] = "igmp-parse-report",
     240             :   }
     241             : };
     242             : /* *INDENT-ON* */
     243             : 
     244             : static uword
     245          12 : igmp_parse_query (vlib_main_t * vm, vlib_node_runtime_t * node,
     246             :                   vlib_frame_t * frame)
     247             : {
     248             :   u32 n_left_from, *from, *to_next;
     249             :   igmp_parse_query_next_t next_index;
     250             : 
     251          12 :   from = vlib_frame_vector_args (frame);
     252          12 :   n_left_from = frame->n_vectors;
     253          12 :   next_index = node->cached_next_index;
     254             : 
     255          24 :   while (n_left_from > 0)
     256             :     {
     257             :       u32 n_left_to_next;
     258             : 
     259          12 :       vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
     260             : 
     261          26 :       while (n_left_from > 0 && n_left_to_next > 0)
     262             :         {
     263             :           igmp_membership_query_v3_t *igmp;
     264             :           igmp_query_args_t *args;
     265             :           u32 bi, next, len;
     266             :           vlib_buffer_t *b;
     267             : 
     268          14 :           next = IGMP_PARSE_QUERY_NEXT_DROP;
     269          14 :           bi = from[0];
     270          14 :           to_next[0] = bi;
     271          14 :           from++;
     272          14 :           to_next++;
     273          14 :           n_left_from--;
     274          14 :           n_left_to_next--;
     275             : 
     276          14 :           b = vlib_get_buffer (vm, bi);
     277          14 :           igmp = vlib_buffer_get_current (b);
     278          14 :           ASSERT (igmp->header.type == IGMP_TYPE_membership_query);
     279          14 :           len = igmp_membership_query_v3_length (igmp);
     280             : 
     281          14 :           if (node->flags & VLIB_NODE_FLAG_TRACE)
     282             :             {
     283             :               igmp_input_trace_t *tr;
     284          14 :               tr = vlib_add_trace (vm, node, b, sizeof (*tr));
     285          14 :               tr->next_index = next;
     286          14 :               tr->sw_if_index = vnet_buffer (b)->sw_if_index[VLIB_RX];
     287          14 :               tr->len = len;
     288          14 :               clib_memcpy_fast (tr->packet_data, vlib_buffer_get_current (b),
     289             :                                 sizeof (tr->packet_data));
     290             :             }
     291             : 
     292             :           /*
     293             :            * validate that the length on the packet on the wire  corresponds
     294             :            * to at least the length of the calculated v3 query.
     295             :            * If there's extra, then it will be ignored.
     296             :            */
     297          14 :           if (vlib_buffer_length_in_chain (vm, b) >= len)
     298             :             {
     299             :               /*
     300             :                * copy the contents of the query, and the interface, over
     301             :                * to the main thread for processing
     302             :                */
     303          13 :               vlib_buffer_advance (b, -sizeof (u32));
     304          13 :               args = vlib_buffer_get_current (b);
     305          13 :               args->sw_if_index = vnet_buffer (b)->sw_if_index[VLIB_RX];
     306             : 
     307          13 :               vl_api_rpc_call_main_thread (igmp_handle_query,
     308             :                                            (u8 *) args, sizeof (*args) + len);
     309             :             }
     310             :           else
     311             :             {
     312             :               /*
     313             :                * else a packet that is reporting more sources than it really
     314             :                * has; bin it
     315             :                */
     316           1 :               b->error = node->errors[IGMP_ERROR_BAD_LENGTH];
     317             :             }
     318             : 
     319          14 :           vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next,
     320             :                                            n_left_to_next, bi, next);
     321             :         }
     322          12 :       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
     323             :     }
     324             : 
     325          12 :   return frame->n_vectors;
     326             : }
     327             : 
     328             : /* *INDENT-OFF* */
     329      128492 : VLIB_REGISTER_NODE (igmp_parse_query_node) =
     330             : {
     331             :   .function = igmp_parse_query,
     332             :   .name = "igmp-parse-query",
     333             :   .vector_size = sizeof (u32),
     334             : 
     335             :   .format_buffer = format_igmp_query_v3,
     336             :   .format_trace = format_igmp_parse_query_trace,
     337             : 
     338             :   .n_errors = IGMP_N_ERROR,
     339             :   .error_strings = igmp_error_strings,
     340             : 
     341             :   .n_next_nodes = IGMP_PARSE_QUERY_N_NEXT,
     342             :   .next_nodes = {
     343             :     [IGMP_PARSE_QUERY_NEXT_DROP] = "error-drop",
     344             :   }
     345             : };
     346             : /* *INDENT-ON* */
     347             : 
     348             : static uword
     349          13 : igmp_parse_report (vlib_main_t * vm, vlib_node_runtime_t * node,
     350             :                    vlib_frame_t * frame)
     351             : {
     352             :   u32 n_left_from, *from, *to_next;
     353             :   igmp_input_next_t next_index;
     354             :   vlib_node_runtime_t *error_node =
     355          13 :     vlib_node_get_runtime (vm, igmp_input_node.index);
     356             :   u8 error;
     357             : 
     358          13 :   from = vlib_frame_vector_args (frame);
     359          13 :   n_left_from = frame->n_vectors;
     360          13 :   next_index = node->cached_next_index;
     361             : 
     362          26 :   while (n_left_from > 0)
     363             :     {
     364             :       u32 n_left_to_next;
     365             : 
     366          13 :       vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
     367             : 
     368          26 :       while (n_left_from > 0 && n_left_to_next > 0)
     369             :         {
     370             :           igmp_membership_report_v3_t *igmp;
     371             :           igmp_report_args_t *args;
     372             :           u32 bi, next, len;
     373             :           vlib_buffer_t *b;
     374             : 
     375          13 :           next = IGMP_PARSE_REPORT_NEXT_DROP;
     376             : 
     377          13 :           bi = from[0];
     378          13 :           to_next[0] = bi;
     379          13 :           from++;
     380          13 :           to_next++;
     381          13 :           n_left_from--;
     382          13 :           n_left_to_next--;
     383             : 
     384          13 :           b = vlib_get_buffer (vm, bi);
     385             : 
     386          13 :           error = IGMP_ERROR_NONE;
     387          13 :           b->error = error_node->errors[error];
     388          13 :           igmp = vlib_buffer_get_current (b);
     389          13 :           len = igmp_membership_report_v3_length (igmp);
     390             : 
     391          13 :           ASSERT (igmp->header.type == IGMP_TYPE_membership_report_v3);
     392             : 
     393          13 :           if (node->flags & VLIB_NODE_FLAG_TRACE)
     394             :             {
     395             :               igmp_input_trace_t *tr;
     396          13 :               tr = vlib_add_trace (vm, node, b, sizeof (*tr));
     397          13 :               tr->next_index = next;
     398          13 :               tr->len = len;
     399          13 :               tr->sw_if_index = vnet_buffer (b)->sw_if_index[VLIB_RX];
     400          13 :               clib_memcpy_fast (tr->packet_data, vlib_buffer_get_current (b),
     401             :                                 sizeof (tr->packet_data));
     402             :             }
     403             : 
     404             :           /*
     405             :            * validate that the length on the packet on the wire
     406             :            * corresponds to the length on the calculated v3 query
     407             :            */
     408          13 :           if (vlib_buffer_length_in_chain (vm, b) >= len)
     409             :             {
     410             :               /*
     411             :                * copy the contents of the query, and the interface, over
     412             :                * to the main thread for processing
     413             :                */
     414          13 :               vlib_buffer_advance (b, -sizeof (u32));
     415          13 :               args = vlib_buffer_get_current (b);
     416          13 :               args->sw_if_index = vnet_buffer (b)->sw_if_index[VLIB_RX];
     417             : 
     418          13 :               vl_api_rpc_call_main_thread (igmp_handle_report,
     419             :                                            (u8 *) args, sizeof (*args) + len);
     420             :             }
     421             :           else
     422             :             {
     423             :               /*
     424             :                * this is a packet with more groups/sources than the
     425             :                * header reports. bin it
     426             :                */
     427           0 :               b->error = node->errors[IGMP_ERROR_BAD_LENGTH];
     428             :             }
     429             : 
     430          13 :           vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next,
     431             :                                            n_left_to_next, bi, next);
     432             :         }
     433          13 :       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
     434             :     }
     435             : 
     436          13 :   return frame->n_vectors;
     437             : }
     438             : 
     439             : /* *INDENT-OFF* */
     440      128492 : VLIB_REGISTER_NODE (igmp_parse_report_node) =
     441             : {
     442             :   .function = igmp_parse_report,
     443             :   .name = "igmp-parse-report",
     444             :   .vector_size = sizeof (u32),
     445             : 
     446             :   .format_buffer = format_igmp_report_v3,
     447             :   .format_trace = format_igmp_parse_report_trace,
     448             : 
     449             :   .n_errors = IGMP_N_ERROR,
     450             :   .error_strings = igmp_error_strings,
     451             : 
     452             :   .n_next_nodes = IGMP_PARSE_REPORT_N_NEXT,
     453             :   .next_nodes = {
     454             :     [IGMP_PARSE_REPORT_NEXT_DROP] = "error-drop",
     455             :   }
     456             : };
     457             : /* *INDENT-ON* */
     458             : 
     459             : static clib_error_t *
     460         575 : igmp_input_init (vlib_main_t * vm)
     461             : {
     462         575 :   ip4_register_protocol (IP_PROTOCOL_IGMP, igmp_input_node.index);
     463             : 
     464         575 :   IGMP_DBG ("input-initialized");
     465             : 
     466         575 :   return (0);
     467             : }
     468             : 
     469             : /* *INDENT-OFF* */
     470        1727 : VLIB_INIT_FUNCTION (igmp_input_init) =
     471             : {
     472             :   .runs_after = VLIB_INITS("igmp_init"),
     473             : };
     474             : /* *INDENT-ON* */
     475             : 
     476             : /*
     477             :  * fd.io coding-style-patch-verification: ON
     478             :  *
     479             :  * Local Variables:
     480             :  * eval: (c-set-style "gnu")
     481             :  * End:
     482             :  */

Generated by: LCOV version 1.14