LCOV - code coverage report
Current view: top level - plugins/igmp - igmp_pkt.c (source / functions) Hit Total Coverage
Test: coverage-filtered.info Lines: 206 223 92.4 %
Date: 2023-07-05 22:20:52 Functions: 20 20 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 <igmp/igmp_pkt.h>
      19             : #include <vnet/fib/fib_sas.h>
      20             : 
      21             : static void
      22        1855 : vlib_buffer_append (vlib_buffer_t * b, uword l)
      23             : {
      24        1855 :   b->current_data += l;
      25        1855 :   b->current_length += l;
      26        1855 : }
      27             : 
      28             : static vlib_buffer_t *
      29          68 : igmp_pkt_get_buffer (igmp_pkt_build_t * bk)
      30             : {
      31             :   vlib_main_t *vm;
      32             :   vlib_buffer_t *b;
      33             :   u32 bi;
      34             : 
      35          68 :   vm = vlib_get_main ();
      36             : 
      37          68 :   if (vlib_buffer_alloc (vm, &bi, 1) != 1)
      38           0 :     return (NULL);
      39             : 
      40          68 :   b = vlib_get_buffer (vm, bi);
      41             : 
      42          68 :   b->flags |= VNET_BUFFER_F_LOCALLY_ORIGINATED;
      43          68 :   b->flags |= VLIB_BUFFER_IS_TRACED;
      44             : 
      45             :   /* clear out stale data */
      46          68 :   vnet_buffer (b)->sw_if_index[VLIB_RX] = ~0;
      47             : 
      48             :   /*
      49             :    * save progress in the builder
      50             :    */
      51          68 :   vec_add1 (bk->buffers, bi);
      52          68 :   bk->n_avail = vnet_sw_interface_get_mtu (vnet_get_main (),
      53             :                                            bk->sw_if_index, VNET_MTU_IP4);
      54             : 
      55          68 :   return (b);
      56             : }
      57             : 
      58             : static vlib_buffer_t *
      59          68 : igmp_pkt_build_ip_header (igmp_pkt_build_t * bk,
      60             :                           igmp_msg_type_t msg_type,
      61             :                           const igmp_group_t * group)
      62             : {
      63             :   ip4_header_t *ip4;
      64             :   vlib_buffer_t *b;
      65             :   u8 *option;
      66             : 
      67          68 :   b = igmp_pkt_get_buffer (bk);
      68             : 
      69          68 :   if (NULL == b)
      70           0 :     return (NULL);
      71             : 
      72          68 :   ip4 = vlib_buffer_get_current (b);
      73          68 :   clib_memset (ip4, 0, sizeof (ip4_header_t));
      74          68 :   ip4->ip_version_and_header_length = 0x46;
      75          68 :   ip4->ttl = 1;
      76          68 :   ip4->protocol = IP_PROTOCOL_IGMP;
      77          68 :   ip4->tos = 0xc0;
      78             : 
      79          68 :   fib_sas4_get (bk->sw_if_index, NULL, &ip4->src_address);
      80             : 
      81          68 :   vlib_buffer_append (b, sizeof (*ip4));
      82          68 :   bk->n_avail -= sizeof (*ip4);
      83             : 
      84          68 :   switch (msg_type)
      85             :     {
      86          50 :     case IGMP_MSG_REPORT:
      87          50 :       ip4->dst_address.as_u32 = IGMP_MEMBERSHIP_REPORT_ADDRESS;
      88          50 :       break;
      89          18 :     case IGMP_MSG_QUERY:
      90          18 :       if (group != NULL)
      91           4 :         clib_memcpy_fast (&ip4->dst_address, &group->key->ip4,
      92             :                           sizeof (ip4_address_t));
      93             :       else
      94          14 :         ip4->dst_address.as_u32 = IGMP_GENERAL_QUERY_ADDRESS;
      95          18 :       break;
      96             :     }
      97             : 
      98             :   /* add the router alert options */
      99          68 :   option = vlib_buffer_get_current (b);
     100          68 :   option[0] = 0x80 | 20;        // IP4_ROUTER_ALERT_OPTION;
     101          68 :   option[1] = 4;                // length
     102          68 :   option[2] = option[3] = 0;
     103             : 
     104          68 :   vlib_buffer_append (b, 4);
     105          68 :   bk->n_avail -= 4;
     106             : 
     107          68 :   return (b);
     108             : }
     109             : 
     110             : static vlib_buffer_t *
     111          50 : igmp_pkt_build_report_v3 (igmp_pkt_build_report_t * br,
     112             :                           const igmp_group_t * group)
     113             : {
     114             :   igmp_membership_report_v3_t *report;
     115             :   vlib_buffer_t *b;
     116             : 
     117          50 :   b = igmp_pkt_build_ip_header (&br->base, IGMP_MSG_REPORT, group);
     118             : 
     119          50 :   if (NULL == b)
     120           0 :     return (NULL);
     121             : 
     122          50 :   report = vlib_buffer_get_current (b);
     123          50 :   report->header.type = IGMP_TYPE_membership_report_v3;
     124          50 :   report->header.code = 0;
     125          50 :   report->header.checksum = 0;
     126          50 :   report->unused = 0;
     127             : 
     128          50 :   vlib_buffer_append (b, sizeof (igmp_membership_report_v3_t));
     129          50 :   br->base.n_avail -= sizeof (igmp_membership_report_v3_t);
     130          50 :   br->base.n_bytes += sizeof (igmp_membership_report_v3_t);
     131             : 
     132          50 :   return (b);
     133             : }
     134             : 
     135             : static void
     136          63 : igmp_pkt_tx (igmp_pkt_build_t * bk)
     137             : {
     138             :   const igmp_config_t *config;
     139             :   vlib_buffer_t *b;
     140             :   vlib_main_t *vm;
     141             :   vlib_frame_t *f;
     142             :   u32 *to_next;
     143             :   u32 ii;
     144             : 
     145          63 :   vm = vlib_get_main ();
     146          63 :   config = igmp_config_lookup (bk->sw_if_index);
     147             : 
     148          63 :   if (NULL == config)
     149           0 :     return;
     150             : 
     151          63 :   f = vlib_get_frame_to_node (vm, ip4_rewrite_mcast_node.index);
     152          63 :   to_next = vlib_frame_vector_args (f);
     153             : 
     154         131 :   vec_foreach_index (ii, bk->buffers)
     155             :   {
     156          68 :     b = vlib_get_buffer (vm, bk->buffers[ii]);
     157          68 :     vnet_buffer (b)->ip.adj_index[VLIB_TX] = config->adj_index;
     158          68 :     to_next[ii] = bk->buffers[ii];
     159          68 :     f->n_vectors++;
     160             :   }
     161             : 
     162          63 :   vlib_put_frame_to_node (vm, ip4_rewrite_mcast_node.index, f);
     163             : 
     164          63 :   IGMP_DBG ("  ..tx: %U", format_vnet_sw_if_index_name,
     165             :             vnet_get_main (), bk->sw_if_index);
     166             : 
     167          63 :   vec_free (bk->buffers);
     168          63 :   bk->buffers = 0;
     169             : }
     170             : 
     171             : static vlib_buffer_t *
     172        1754 : igmp_pkt_build_report_get_active (igmp_pkt_build_report_t * br)
     173             : {
     174        1754 :   if (NULL == br->base.buffers)
     175          45 :     return (NULL);
     176             : 
     177        1709 :   return (vlib_get_buffer (vlib_get_main (),
     178        1709 :                            br->base.buffers[vec_len (br->base.buffers) - 1]));
     179             : }
     180             : 
     181             : static void
     182          50 : igmp_pkt_build_report_bake (igmp_pkt_build_report_t * br)
     183             : {
     184             :   igmp_membership_report_v3_t *igmp;
     185             :   ip4_header_t *ip4;
     186             :   vlib_buffer_t *b;
     187             : 
     188          50 :   b = igmp_pkt_build_report_get_active (br);
     189             : 
     190          50 :   b->current_data = 0;
     191             : 
     192          50 :   ip4 = vlib_buffer_get_current (b);
     193          50 :   igmp = (igmp_membership_report_v3_t *) (((u32 *) ip4) + 6);
     194             : 
     195          50 :   igmp->n_groups = clib_host_to_net_u16 (br->n_groups);
     196             : 
     197          50 :   igmp->header.checksum =
     198          50 :     ~ip_csum_fold (ip_incremental_checksum (0, igmp, br->base.n_bytes));
     199             : 
     200          50 :   ip4->length = clib_host_to_net_u16 (b->current_length);
     201          50 :   ip4->checksum = ip4_header_checksum (ip4);
     202             : 
     203          50 :   br->base.n_bytes = br->base.n_avail = br->n_groups = 0;
     204          50 : }
     205             : 
     206             : void
     207          48 : igmp_pkt_report_v3_send (igmp_pkt_build_report_t * br)
     208             : {
     209          48 :   if (NULL == br->base.buffers)
     210           3 :     return;
     211             : 
     212          45 :   igmp_pkt_build_report_bake (br);
     213          45 :   igmp_pkt_tx (&br->base);
     214             : }
     215             : 
     216             : static u32
     217          12 : igmp_pkt_report_v3_get_size (const igmp_group_t * group)
     218             : {
     219          12 :   ASSERT (IGMP_FILTER_MODE_INCLUDE == group->router_filter_mode);
     220             : 
     221          12 :   return ((hash_elts (group->igmp_src_by_key[IGMP_FILTER_MODE_INCLUDE]) *
     222          12 :            sizeof (ip4_address_t)) + sizeof (igmp_membership_group_v3_t));
     223             : }
     224             : 
     225             : static igmp_membership_group_v3_t *
     226          60 : igmp_pkt_report_v3_append_group (igmp_pkt_build_report_t * br,
     227             :                                  const ip46_address_t * grp,
     228             :                                  igmp_membership_group_v3_type_t type)
     229             : {
     230             :   igmp_membership_group_v3_t *igmp_group;
     231             :   vlib_buffer_t *b;
     232             : 
     233          60 :   b = igmp_pkt_build_report_get_active (br);
     234             : 
     235          60 :   if (br->base.n_avail < sizeof (igmp_membership_group_v3_t))
     236             :     {
     237           0 :       igmp_pkt_build_report_bake (br);
     238           0 :       b = igmp_pkt_build_report_v3 (br, NULL);
     239           0 :       if (NULL == b)
     240           0 :         return (NULL);
     241             :     }
     242          60 :   br->base.n_avail -= sizeof (igmp_membership_group_v3_t);
     243          60 :   br->base.n_bytes += sizeof (igmp_membership_group_v3_t);
     244          60 :   br->n_groups++;
     245          60 :   br->n_srcs = 0;
     246             : 
     247          60 :   igmp_group = vlib_buffer_get_current (b);
     248          60 :   vlib_buffer_append (b, sizeof (igmp_membership_group_v3_t));
     249             : 
     250          60 :   igmp_group->type = type;
     251          60 :   igmp_group->n_aux_u32s = 0;
     252          60 :   igmp_group->n_src_addresses = 0;
     253          60 :   igmp_group->group_address.as_u32 = grp->ip4.as_u32;
     254             : 
     255          60 :   return (igmp_group);
     256             : }
     257             : 
     258             : /**
     259             :  * 4.2.16
     260             :  "   If the set of Group Records required in a Report does not fit within
     261             :  *   the size limit of a single Report message (as determined by the MTU
     262             :  *   of the network on which it will be sent), the Group Records are sent
     263             :  *   in as many Report messages as needed to report the entire set.
     264             : 
     265             :  *   If a single Group Record contains so many source addresses that it
     266             :  *   does not fit within the size limit of a single Report message, if its
     267             :  *   Type is not MODE_IS_EXCLUDE or CHANGE_TO_EXCLUDE_MODE, it is split
     268             :  *   into multiple Group Records, each containing a different subset of
     269             :  *   the source addresses and each sent in a separate Report message.  If
     270             :  *   its Type is MODE_IS_EXCLUDE or CHANGE_TO_EXCLUDE_MODE, a single Group
     271             :  *   Record is sent, containing as many source addresses as can fit, and
     272             :  *  the remaining source addresses are not reported; though the choice of
     273             :  *   which sources to report is arbitrary, it is preferable to report the
     274             :  *  same set of sources in each subsequent report, rather than reporting
     275             :  *  different sources each time."
     276             :   */
     277             : static igmp_membership_group_v3_t *
     278        1586 : igmp_pkt_report_v3_append_src (igmp_pkt_build_report_t * br,
     279             :                                igmp_membership_group_v3_t * igmp_group,
     280             :                                const ip46_address_t * grp,
     281             :                                igmp_membership_group_v3_type_t type,
     282             :                                const ip46_address_t * src)
     283             : {
     284             :   vlib_buffer_t *b;
     285             : 
     286        1586 :   b = igmp_pkt_build_report_get_active (br);
     287             : 
     288        1586 :   if (br->base.n_avail < sizeof (ip4_address_t))
     289             :     {
     290           2 :       igmp_group->n_src_addresses = clib_host_to_net_u16 (br->n_srcs);
     291           2 :       igmp_pkt_build_report_bake (br);
     292           2 :       b = igmp_pkt_build_report_v3 (br, NULL);
     293           2 :       if (NULL == b)
     294           0 :         return (NULL);
     295           2 :       igmp_group = igmp_pkt_report_v3_append_group (br, grp, type);
     296             :     }
     297             : 
     298        1586 :   igmp_group->src_addresses[br->n_srcs].as_u32 = src->ip4.as_u32;
     299        1586 :   br->n_srcs++;
     300        1586 :   br->base.n_avail -= sizeof (ip4_address_t);
     301        1586 :   br->base.n_bytes += sizeof (ip4_address_t);
     302        1586 :   vlib_buffer_append (b, sizeof (ip4_address_t));
     303             : 
     304        1586 :   return (igmp_group);
     305             : }
     306             : 
     307             : void
     308          21 : igmp_pkt_report_v3_add_report (igmp_pkt_build_report_t * br,
     309             :                                const ip46_address_t * grp,
     310             :                                const ip46_address_t * srcs,
     311             :                                igmp_membership_group_v3_type_t type)
     312             : {
     313             :   igmp_membership_group_v3_t *igmp_group;
     314             :   const ip46_address_t *s;
     315             :   vlib_buffer_t *b;
     316             : 
     317          21 :   b = igmp_pkt_build_report_get_active (br);
     318             : 
     319          21 :   if (NULL == b)
     320             :     {
     321          20 :       b = igmp_pkt_build_report_v3 (br, NULL);
     322          20 :       if (NULL == b)
     323             :         /* failed to allocate buffer */
     324           0 :         return;
     325             :     }
     326             : 
     327          21 :   igmp_group = igmp_pkt_report_v3_append_group (br, grp, type);
     328             : 
     329          21 :   if (NULL == igmp_group)
     330           0 :     return;
     331             : 
     332             :   /* *INDENT-OFF* */
     333         452 :   vec_foreach(s, srcs)
     334             :     {
     335         431 :       igmp_group = igmp_pkt_report_v3_append_src(br, igmp_group,
     336             :                                                  grp, type, s);
     337         431 :       if (NULL == igmp_group)
     338           0 :         return;
     339             :     };
     340             :   /* *INDENT-ON* */
     341             : 
     342          21 :   igmp_group->n_src_addresses = clib_host_to_net_u16 (br->n_srcs);
     343             : 
     344          21 :   IGMP_DBG ("  ..add-group: %U", format_ip46_address, grp, IP46_TYPE_IP4);
     345             : }
     346             : 
     347             : void
     348          37 : igmp_pkt_report_v3_add_group (igmp_pkt_build_report_t * br,
     349             :                               const igmp_group_t * group,
     350             :                               igmp_membership_group_v3_type_t type)
     351             : {
     352             :   igmp_membership_group_v3_t *igmp_group;
     353             :   vlib_buffer_t *b;
     354             :   igmp_src_t *src;
     355             : 
     356          37 :   b = igmp_pkt_build_report_get_active (br);
     357             : 
     358          37 :   if (NULL == b)
     359             :     {
     360          25 :       b = igmp_pkt_build_report_v3 (br, NULL);
     361          25 :       if (NULL == b)
     362             :         /* failed to allocate buffer */
     363           0 :         return;
     364             :     }
     365             : 
     366             :   /*
     367             :    * if the group won't fit in a partially full buffer, start again
     368             :    */
     369          37 :   if ((0 != br->n_groups) &&
     370          12 :       (igmp_pkt_report_v3_get_size (group) > br->base.n_avail))
     371             :     {
     372           3 :       igmp_pkt_build_report_bake (br);
     373           3 :       b = igmp_pkt_build_report_v3 (br, NULL);
     374           3 :       if (NULL == b)
     375             :         /* failed to allocate buffer */
     376           0 :         return;
     377             :     }
     378             : 
     379          37 :   igmp_group = igmp_pkt_report_v3_append_group (br, group->key, type);
     380             : 
     381             :   /* *INDENT-OFF* */
     382        5096 :   FOR_EACH_SRC (src, group, IGMP_FILTER_MODE_INCLUDE,
     383             :     ({
     384             :       igmp_group = igmp_pkt_report_v3_append_src(br, igmp_group,
     385             :                                                  group->key, type,
     386             :                                                  src->key);
     387             :       if (NULL == igmp_group)
     388             :         return;
     389             :     }));
     390             :   /* *INDENT-ON* */
     391          37 :   igmp_group->n_src_addresses = clib_host_to_net_u16 (br->n_srcs);
     392             : 
     393          37 :   IGMP_DBG ("  ..add-group: %U srcs:%d",
     394             :             format_igmp_key, group->key,
     395             :             hash_elts (group->igmp_src_by_key[IGMP_FILTER_MODE_INCLUDE]));
     396             : }
     397             : 
     398             : void
     399          48 : igmp_pkt_build_report_init (igmp_pkt_build_report_t * br, u32 sw_if_index)
     400             : {
     401          48 :   clib_memset (br, 0, sizeof (*br));
     402          48 :   br->base.sw_if_index = sw_if_index;
     403          48 : }
     404             : 
     405             : static vlib_buffer_t *
     406          36 : igmp_pkt_build_query_get_active (igmp_pkt_build_query_t * bq)
     407             : {
     408          36 :   if (NULL == bq->base.buffers)
     409          18 :     return (NULL);
     410             : 
     411          18 :   return (vlib_get_buffer (vlib_get_main (),
     412          18 :                            bq->base.buffers[vec_len (bq->base.buffers) - 1]));
     413             : }
     414             : 
     415             : static vlib_buffer_t *
     416          18 : igmp_pkt_build_query_v3 (igmp_pkt_build_query_t * bq,
     417             :                          const igmp_group_t * group)
     418             : {
     419             :   igmp_membership_query_v3_t *query;
     420             :   vlib_buffer_t *b;
     421             : 
     422          18 :   b = igmp_pkt_build_ip_header (&bq->base, IGMP_MSG_QUERY, group);
     423             : 
     424          18 :   if (NULL == b)
     425           0 :     return (NULL);
     426             : 
     427          18 :   query = vlib_buffer_get_current (b);
     428          18 :   query->header.type = IGMP_TYPE_membership_query;
     429          18 :   query->header.code = 0;
     430          18 :   query->header.checksum = 0;
     431          18 :   query->qqi_code = 0;
     432          18 :   query->resv_s_qrv = 0;
     433             : 
     434          18 :   if (NULL != group)
     435           4 :     query->group_address.as_u32 = group->key->ip4.as_u32;
     436             :   else
     437          14 :     query->group_address.as_u32 = 0;
     438             : 
     439          18 :   vlib_buffer_append (b, sizeof (igmp_membership_query_v3_t));
     440          18 :   bq->base.n_avail -= sizeof (igmp_membership_query_v3_t);
     441          18 :   bq->base.n_bytes += sizeof (igmp_membership_query_v3_t);
     442             : 
     443          18 :   return (b);
     444             : }
     445             : 
     446             : void
     447          18 : igmp_pkt_query_v3_add_group (igmp_pkt_build_query_t * bq,
     448             :                              const igmp_group_t * group,
     449             :                              const ip46_address_t * srcs)
     450             : {
     451             :   vlib_buffer_t *b;
     452             : 
     453          18 :   b = igmp_pkt_build_query_get_active (bq);
     454             : 
     455          18 :   if (NULL == b)
     456             :     {
     457          18 :       b = igmp_pkt_build_query_v3 (bq, group);
     458          18 :       if (NULL == b)
     459             :         /* failed to allocate buffer */
     460           0 :         return;
     461             :     }
     462             : 
     463          18 :   if (NULL != srcs)
     464             :     {
     465             :       igmp_membership_query_v3_t *query;
     466             :       const ip46_address_t *src;
     467             : 
     468           4 :       query = vlib_buffer_get_current (b);
     469             : 
     470           9 :       vec_foreach (src, srcs)
     471             :       {
     472           5 :         query->src_addresses[bq->n_srcs++].as_u32 = src->ip4.as_u32;
     473             : 
     474           5 :         vlib_buffer_append (b, sizeof (ip4_address_t));
     475           5 :         bq->base.n_bytes += sizeof (ip4_address_t);
     476           5 :         bq->base.n_avail += sizeof (ip4_address_t);
     477             :       }
     478             :     }
     479             :   /*
     480             :    * else
     481             :    *   general query and we're done
     482             :    */
     483             : }
     484             : 
     485             : static void
     486          18 : igmp_pkt_build_query_bake (igmp_pkt_build_query_t * bq)
     487             : {
     488             :   igmp_membership_query_v3_t *igmp;
     489             :   ip4_header_t *ip4;
     490             :   vlib_buffer_t *b;
     491             : 
     492          18 :   b = igmp_pkt_build_query_get_active (bq);
     493             : 
     494          18 :   b->current_data = 0;
     495             : 
     496          18 :   ip4 = vlib_buffer_get_current (b);
     497             :   // account for options
     498          18 :   igmp = (igmp_membership_query_v3_t *) (((u32 *) ip4) + 6);
     499             : 
     500          18 :   igmp->n_src_addresses = clib_host_to_net_u16 (bq->n_srcs);
     501             : 
     502          18 :   igmp->header.checksum =
     503          18 :     ~ip_csum_fold (ip_incremental_checksum (0, igmp, bq->base.n_bytes));
     504             : 
     505          18 :   ip4->length = clib_host_to_net_u16 (b->current_length);
     506          18 :   ip4->checksum = ip4_header_checksum (ip4);
     507             : 
     508          18 :   bq->base.n_bytes = bq->base.n_avail = bq->n_srcs = 0;
     509          18 : }
     510             : 
     511             : void
     512          18 : igmp_pkt_query_v3_send (igmp_pkt_build_query_t * bq)
     513             : {
     514          18 :   if (NULL == bq->base.buffers)
     515           0 :     return;
     516             : 
     517          18 :   igmp_pkt_build_query_bake (bq);
     518          18 :   igmp_pkt_tx (&bq->base);
     519             : }
     520             : 
     521             : void
     522          18 : igmp_pkt_build_query_init (igmp_pkt_build_query_t * bq, u32 sw_if_index)
     523             : {
     524          18 :   clib_memset (bq, 0, sizeof (*bq));
     525          18 :   bq->base.sw_if_index = sw_if_index;
     526          18 : }
     527             : 
     528             : /*
     529             :  * fd.io coding-style-patch-verification: ON
     530             :  *
     531             :  * Local Variables:
     532             :  * eval: (c-set-style "gnu")
     533             :  * End:
     534             :  */

Generated by: LCOV version 1.14