LCOV - code coverage report
Current view: top level - vnsw/agent/services/multicast/gmp_map - gmp_map.cc (source / functions) Hit Total Coverage
Test: OpenSDN C/C++ coverage (all TARGET_SET jobs) Lines: 102 241 42.3 %
Date: 2026-11-09 02:10:49 Functions: 13 25 52.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*
       2             :  * Copyright (c) 2017 Juniper Networks, Inc. All rights reserved.
       3             :  * Copyright Contributors to the OpenSDN project
       4             :  */
       5             : 
       6             : #include <cstdarg>
       7             : #include "task_map.h"
       8             : #include "oper/multicast.h"
       9             : 
      10             : #ifdef __cplusplus
      11             : extern "C" {
      12             : #endif
      13             : #include "mcast_common.h"
      14             : 
      15             : #include "task_map.h"
      16             : #include "task_thread_api.h"
      17             : #include "patricia_api.h"
      18             : 
      19             : #include "gmp.h"
      20             : #include "gmpx_environment.h"
      21             : #include "gmp_router.h"
      22             : #include "igmp_private.h"
      23             : #include "igmp_protocol.h"
      24             : #include "gmp_externs.h"
      25             : 
      26             : #include "gmp_intf.h"
      27             : #include "gmp_map.h"
      28             : #include "gmp_private.h"
      29             : #include "gmp_trace.h"
      30             : #include "gmpr_trace.h"
      31             : 
      32             : #define MAXSTRINGSIZE_1 1000
      33             : 
      34             : extern void gmpx_trace(void *context, const char *fmt, ...);
      35             : 
      36             : extern void gmp_set_def_igmp_version(uint32_t version);
      37             : extern void gmp_set_def_ipv4_ivl_params(uint32_t robust_count, uint32_t qivl,
      38             :                         uint32_t qrivl, uint32_t lmqi);
      39             : extern void gmp_set_def_intf_params(mc_af mcast_af);
      40             : #ifdef __cplusplus
      41             : }
      42             : #endif
      43             : 
      44             : mgm_global_data mgm_global[MCAST_AF_MAX];
      45             : gmpr_intf_params def_gmpr_intf_params[MCAST_AF_MAX];
      46             : 
      47             : static gmpr_instance_context gmp_inst_ctx = {
      48             :     gmp_oif_map_cb,     // rctx_oif_map_cb
      49             :     gmp_policy_cb,      // rctx_policy_cb
      50             :     gmp_ssm_check_cb,   // rctx_ssm_check_cb
      51             : };
      52             : 
      53             : static gmpr_client_context igmp_client_context = {
      54             :     igmp_notification_ready,        // rctx_notif_cb
      55             :     igmp_host_notification_ready,   // rctx_host_notif_cb
      56             :     mgm_querier_change,             // rctx_querier_cb
      57             :     TRUE,                           // rctx_delta_notifications
      58             :     FALSE,                          // rctx_full_notifications
      59             : };
      60             : 
      61             : // Value to be used for IGMP Version during init of GMP.
      62           0 : void gmp_set_def_igmp_version(uint32_t version)
      63             : {
      64           0 :     gmpr_intf_params *params = NULL;
      65             : 
      66           0 :     params = &def_gmpr_intf_params[MCAST_AF_IPV4];
      67           0 :     params->gmpr_ifparm_version = version;
      68           0 : }
      69             : 
      70             : // Default values to use for IGMP for querier related timer intervals in
      71             : // GMP code.
      72           2 : void gmp_set_def_ipv4_ivl_params(uint32_t robust_count, uint32_t qivl,
      73             :                         uint32_t qrivl, uint32_t lmqi)
      74             : {
      75           2 :     gmpr_intf_params *params = NULL;
      76             : 
      77           2 :     params = &def_gmpr_intf_params[MCAST_AF_IPV4];
      78           2 :     params->gmpr_ifparm_robustness = robust_count;
      79           2 :     params->gmpr_ifparm_qivl = qivl;
      80           2 :     params->gmpr_ifparm_qrivl = qrivl;
      81           2 :     params->gmpr_ifparm_lmqi = lmqi;
      82             : 
      83           2 :     return;
      84             : }
      85             : 
      86             : // Set of defaults for use by GMP as part of GMP initialization.
      87           2 : void gmp_set_def_intf_params(mc_af mcast_af)
      88             : {
      89           2 :     gmpr_intf_params *params = NULL;
      90             : 
      91           2 :     if (mcast_af == MCAST_AF_IPV4) {
      92             : 
      93           2 :         params = &def_gmpr_intf_params[mcast_af];
      94           2 :         params->gmpr_ifparm_version = IGMP_VERSION_3;
      95           2 :         gmp_set_def_ipv4_ivl_params(IGMP_ROBUST_COUNT, IGMP_QUERY_INTERVAL,
      96             :                             IGMP_QUERY_RESPONSE_INTERVAL,
      97             :                             IGMP_QUERY_LASTMEMBER_INTERVAL);
      98             :     } else {
      99           0 :         return;
     100             :     }
     101             : 
     102           2 :     params->gmpr_ifparm_fast_leave = FALSE;
     103           2 :     params->gmpr_ifparm_passive_receive = FALSE;
     104           2 :     params->gmpr_ifparm_suppress_gen_query = FALSE;
     105           2 :     params->gmpr_ifparm_suppress_gs_query = FALSE;
     106           2 :     params->gmpr_ifparm_chan_limit = 0;
     107           2 :     params->gmpr_ifparm_chan_threshold = 100;
     108           2 :     params->gmpr_ifparm_log_interval = 0;
     109             : 
     110           2 :     return;
     111             : }
     112             : 
     113             : // Create, initialization and return global data structure instance
     114             : // that is used as context and mapping data structure with
     115             : // GMP code.
     116           2 : mgm_global_data *gmp_init(mc_af mcast_af, task *tp, void *gmp_sm)
     117             : {
     118             :     gmp_proto proto;
     119             :     mgm_global_data *gd;
     120             : 
     121           2 :     if (mcast_af == MCAST_AF_IPV4) {
     122           2 :         proto = GMP_PROTO_IGMP;
     123             :     } else {
     124           0 :         return NULL;
     125             :     }
     126             : 
     127           2 :     gd = &mgm_global[mcast_af];
     128           2 :     gd->gmp_sm = gmp_sm;
     129           2 :     gd->tp = tp;
     130           2 :     gd->mgm_gd_af = mcast_af;
     131             : 
     132           2 :     gmp_register_io(GMP_ROLE_ROUTER, proto, gmp_xmit_ready);
     133           2 :     gmp_register_peek_function(GMP_ROLE_ROUTER, gmp_static_peek,
     134             :                                    gmp_static_peek);
     135             : 
     136           2 :     gd->mgm_gmpr_instance = gmpr_create_instance(proto, gd, &gmp_inst_ctx);
     137           2 :     gd->mgm_gmpr_client = gmpr_register(gd->mgm_gmpr_instance, gd,
     138             :                             &igmp_client_context);
     139           2 :     gmpr_update_trace_flags(gd->mgm_gmpr_instance, 0);
     140           2 :     gmp_set_def_intf_params(mcast_af);
     141             : 
     142           2 :     return gd;
     143             : }
     144             : 
     145             : // Deinit and destroy data structure once the GMP module is
     146             : // de-inited by the caller.
     147           2 : void gmp_deinit(mc_af mcast_af)
     148             : {
     149             :     mgm_global_data *gd;
     150             : 
     151           2 :     gd = &mgm_global[mcast_af];
     152           2 :     gmpr_detach(gd->mgm_gmpr_client);
     153           2 :     gmpr_destroy_instance(gd->mgm_gmpr_instance);
     154             : 
     155           2 :     gd->mgm_gmpr_client = NULL;
     156           2 :     gd->mgm_gmpr_instance = NULL;
     157             : 
     158           2 :     return;
     159             : }
     160             : 
     161             : // Set the interface params in the GMP.
     162          80 : void gmp_set_intf_params(mgm_global_data *gd, gmp_intf *gif)
     163             : {
     164             :     gmp_intf_handle *handle;
     165             : 
     166          80 :     if (gd->mgm_gd_af != MCAST_AF_IPV4) {
     167           0 :         return;
     168             :     }
     169             : 
     170          80 :     handle = gmp_gif_to_handle(gif);
     171          80 :     gmpr_set_intf_params(gd->mgm_gmpr_instance, handle, &gif->params);
     172          80 :     return;
     173             : }
     174             : 
     175             : // Attach an application interface context with GMP interface context.
     176             : // application interface context is generally the either per-IPAM
     177             : // gw or dns server in the context of the Agent.
     178          80 : gmp_intf *gmp_attach_intf(mgm_global_data *gd, void *mif_state)
     179             : {
     180          80 :     gmp_intf *gif = NULL;
     181             : 
     182          80 :     if (!gd) {
     183           0 :         return NULL;
     184             :     }
     185             : 
     186          80 :     if (gd->mgm_gd_af != MCAST_AF_IPV4) {
     187           0 :         return NULL;
     188             :     }
     189             : 
     190          80 :     gif = (gmp_intf *)malloc(sizeof(gmp_intf));
     191          80 :     if (!gif) {
     192           0 :         return NULL;
     193             :     }
     194             : 
     195          80 :     gif->vm_interface = mif_state;
     196          80 :     gif->gmpif_proto = GMP_PROTO_IGMP;
     197          80 :     gif->gmpif_handle.gmpifh_host = FALSE;
     198          80 :     std::memset(&gif->gmpif_handle.gmpifh_xmit_thread, 0, sizeof(task_thread));
     199          80 :     gmpr_attach_intf(gd->mgm_gmpr_instance, &gif->gmpif_handle);
     200          80 :     memcpy(&gif->params, &def_gmpr_intf_params[gd->mgm_gd_af],
     201             :                                     sizeof(gif->params));
     202          80 :     gmp_set_intf_params(gd, gif);
     203             : 
     204          80 :     return gif;
     205             : }
     206             : 
     207             : // Detach the interface created using above from the GMP.
     208          80 : void gmp_detach_intf(mgm_global_data *gd, gmp_intf *gif)
     209             : {
     210          80 :     if (!gd) {
     211           0 :         return;
     212             :     }
     213             : 
     214          80 :     gmpr_detach_intf(gd->mgm_gmpr_instance, &gif->gmpif_handle);
     215             : 
     216          80 :     free(gif);
     217             : 
     218          80 :     return;
     219             : }
     220             : 
     221             : // Update interface status - currently only IP Address.
     222         160 : boolean gmp_update_intf_state(mgm_global_data *gd, gmp_intf *gif,
     223             :                                 const gmp_addr_string *intf_addr)
     224             : {
     225         160 :     gmpr_update_intf_state(gd->mgm_gmpr_instance, gmp_gif_to_handle(gif),
     226             :                                 (const u_int8_t *)intf_addr);
     227         160 :     return TRUE;
     228             : }
     229             : 
     230             : // Update querying related params - currently query enable/disable.
     231           0 : boolean gmp_update_intf_querying(mgm_global_data *gd, gmp_intf *gif,
     232             :                                 boolean query)
     233             : {
     234           0 :     gif->params.gmpr_ifparm_suppress_gen_query = query;
     235           0 :     gif->params.gmpr_ifparm_suppress_gs_query = query;
     236           0 :     gmp_set_intf_params(gd, gif);
     237           0 :     return TRUE;
     238             : }
     239             : 
     240             : // Process the IGMP payload.
     241             : // Preprocessed IP header by Agent packet handler.
     242           0 : boolean gmp_process_pkt(mgm_global_data *gd, gmp_intf *gif,
     243             :                         void *rcv_pkt, u_int32_t packet_len,
     244             :                         const gmp_addr_string *src_addr,
     245             :                         const gmp_addr_string *dst_addr)
     246             : {
     247           0 :     boolean parse_ok = FALSE;
     248             : 
     249           0 :     if (gd->mgm_gd_af == MCAST_AF_IPV4) {
     250           0 :         parse_ok = igmp_process_pkt(rcv_pkt, src_addr->gmp_v4_addr,
     251           0 :                         dst_addr->gmp_v4_addr, packet_len,
     252             :                         gmp_gif_to_handle(gif), false, NULL, 0);
     253             :     }
     254             : 
     255           0 :     return parse_ok;
     256             : }
     257             : 
     258           0 : boolean gmp_oif_map_cb(void *inst_context UNUSED, gmp_intf_handle *handle,
     259             :                 u_int8_t *group_addr, u_int8_t *source_addr,
     260             :                 gmp_intf_handle **output_handle)
     261             : {
     262           0 :     *output_handle = handle;
     263           0 :     return TRUE;
     264             : }
     265             : 
     266             : // Handle Multicast Policy configured by user.
     267             : // Returns allow or deny the <S,G> at VN level.
     268           0 : boolean gmp_policy_cb(void *inst_context, gmp_intf_handle *handle,
     269             :                 u_int8_t *group_addr, u_int8_t *source_addr,
     270             :                 boolean static_group)
     271             : {
     272           0 :     mgm_global_data *gd = (mgm_global_data *)inst_context;
     273             :     gmp_intf *gif;
     274             :     gmp_addr_string g;
     275             :     gmp_addr_string s;
     276             : 
     277           0 :     if (!inst_context) {
     278           0 :         return FALSE;
     279             :     }
     280             : 
     281           0 :     gif = gmp_handle_to_gif(handle);
     282           0 :     if (!gif) {
     283           0 :         return FALSE;
     284             :     }
     285             : 
     286           0 :     if (static_group) {
     287           0 :         return FALSE;
     288             :     }
     289             : 
     290           0 :     if (!group_addr) {
     291           0 :         return FALSE;
     292             :     }
     293             : 
     294           0 :     memcpy(&g, group_addr, IPV4_ADDR_LEN);
     295           0 :     source_addr ? memcpy(&s, source_addr, IPV4_ADDR_LEN)
     296           0 :                 : memset(&s, 0x00, IPV4_ADDR_LEN);
     297             : 
     298           0 :     return gmp_policy_check(gd, gif, s, g);
     299             : }
     300             : 
     301           0 : boolean gmp_ssm_check_cb(void *inst_context UNUSED, gmp_intf_handle *handle,
     302             :                 u_int8_t *group_addr)
     303             : {
     304           0 :     return TRUE;
     305             : }
     306             : 
     307             : // Notification handler for handling <S,G>
     308             : // Values per-<S,G> can be adding, deleting or modifying <S,G>.
     309             : // Modifying can be change <S> from allow to block and vice-versa.
     310           0 : boolean gmp_client_notification(mgm_global_data *gd)
     311             : {
     312           0 :     boolean pending = FALSE;
     313             :     gmpr_client_notification *notification;
     314             :     gmp_intf *gif;
     315             :     gmp_intf_handle *handle;
     316             :     gmp_addr_string g;
     317             :     gmp_addr_string s;
     318           0 :     int notif_count = IGMP_MAX_NOTIF_PER_PASS;
     319             : 
     320             :     /*
     321             :      * Pull notifications until we run out, hit our max count, or the
     322             :      * protocol is busy.
     323             :      *
     324             :      * NOTE: We might go over the max notifications per pass,
     325             :      * ie. notif_count might be -ve, because we need to make sure that
     326             :      * the notifications for the (s,g) of the same group are being
     327             :      * processed in the same batch.
     328             :      */
     329           0 :     notification = NULL;
     330           0 :     while (notif_count-- > 0 || !gmpr_notification_last_sg(notification)) {
     331             :         notification =
     332           0 :             gmpr_get_notification(gd->mgm_gmpr_client, notification);
     333           0 :         if (!notification)
     334           0 :             break;
     335             : 
     336             :         /*
     337             :          * Process received notification...
     338             :          */
     339           0 :         handle = notification->notif_intf_id;
     340           0 :         gif = gmp_handle_to_gif(handle);
     341             : 
     342           0 :         memcpy(&g, &notification->notif_group_addr, IPV4_ADDR_LEN);
     343           0 :         memcpy(&s, &notification->notif_source_addr, IPV4_ADDR_LEN);
     344             : 
     345           0 :         switch (notification->notif_type) {
     346           0 :             case GMPR_NOTIF_ALLOW_SOURCE:
     347           0 :                 if (notification->notif_filter_mode ==
     348             :                         GMP_FILTER_MODE_INCLUDE) {
     349           0 :                     gmp_group_notify(gd, gif, MGM_GROUP_ADDED, s, g);
     350             :                 } else {
     351           0 :                     gmp_cache_resync_notify(gd, gif, s, g);
     352             :                 }
     353           0 :                 break;
     354             : 
     355           0 :             case GMPR_NOTIF_BLOCK_SOURCE:
     356           0 :                 if (notification->notif_filter_mode ==
     357             :                         GMP_FILTER_MODE_INCLUDE) {
     358           0 :                     gmp_group_notify(gd, gif, MGM_GROUP_REMOVED, s, g);
     359             :                 } else {
     360           0 :                     gmp_cache_resync_notify(gd, gif, s, g);
     361             :                 }
     362           0 :                 break;
     363             : 
     364           0 :             case GMPR_NOTIF_GROUP_DELETE:
     365           0 :                 gmp_group_notify(gd, gif, MGM_GROUP_SRC_REMOVED, s, g);
     366           0 :                 gmp_group_notify(gd, gif, MGM_GROUP_REMOVED, s, g);
     367           0 :                 break;
     368             : 
     369           0 :             case GMPR_NOTIF_GROUP_ADD_EXCL:
     370             :                 /*
     371             :                  * Check if PIM requested a refresh.
     372             :                  * If so we don't send a group and source REMOVE
     373             :                  * otherwise PIM will clear its state
     374             :                  */
     375           0 :                 if (gd->refresh_required == FALSE) {
     376           0 :                     gmp_group_notify(gd, gif, MGM_GROUP_SRC_REMOVED, s, g);
     377             :                 }
     378           0 :                 gmp_group_notify(gd, gif, MGM_GROUP_ADDED, s, g);
     379           0 :                 break;
     380             : 
     381           0 :             case GMPR_NOTIF_GROUP_ADD_INCL:
     382             :                 /*
     383             :                  * Check if PIM requested a refresh.
     384             :                  * If so we don't send a group and source REMOVE
     385             :                  * otherwise PIM will clear its state
     386             :                  */
     387           0 :                 if (gd->refresh_required == FALSE) {
     388           0 :                     gmp_group_notify(gd, gif, MGM_GROUP_SRC_REMOVED, s, g);
     389           0 :                     gmp_group_notify(gd, gif, MGM_GROUP_REMOVED, s, g);
     390             :                 }
     391           0 :                 break;
     392             : 
     393           0 :             case GMPR_NOTIF_REFRESH_END:
     394           0 :                 gd->refresh_required = FALSE;
     395           0 :                 break;
     396             : 
     397           0 :             default:
     398             :                 /* We should never get here */
     399           0 :                 assert(0);
     400             :         }
     401             :     }
     402             : 
     403             :     /*
     404             :      * If there's a notification pointer, it means that we bailed out due
     405             :      * to hitting the notification count limit or someone is too busy to
     406             :      * process the updates.
     407             :      *
     408             :      * Either way, we explicitly toss the notification block and then fall
     409             :      * out, which causes the job to be requeued.
     410             :      *
     411             :      * On the other hand, if the notification pointer is NULL, it means that
     412             :      * we drained the queue, in which case we should kill the job.
     413             :      */
     414           0 :      if (notification) {
     415           0 :          gmpr_return_notification(notification);
     416           0 :          pending = TRUE;
     417             :      }
     418             : 
     419           0 :      return pending;
     420             : }
     421             : 
     422             : // Per host, per-<S,G> notification to indicate join or leave
     423             : // among others of an host.
     424           0 : boolean gmp_client_host_notification(mgm_global_data *gd)
     425             : {
     426           0 :     boolean pending = FALSE;
     427           0 :     gmpr_client_host_notification *gmpr_notif = NULL;
     428             :     gmp_intf *gif;
     429             :     gmp_intf_handle *handle;
     430             :     gmp_addr_string g;
     431             :     gmp_addr_string s;
     432             :     gmp_addr_string h;
     433           0 :     int notif_count = IGMP_MAX_HOST_NOTIF_PER_PASS;
     434             : 
     435             :     /*
     436             :      *  Do this a limited number of times.
     437             :      */
     438           0 :     while (notif_count--) {
     439             :         /*
     440             :          *  Stop if there are no more notifications.
     441             :          */
     442             :         gmpr_notif =
     443           0 :             gmpr_get_host_notification(gd->mgm_gmpr_client, gmpr_notif);
     444           0 :         if (!gmpr_notif) {
     445           0 :             break;
     446             :         }
     447             : 
     448             :         /*
     449             :          *  Skip NULL MIFs (local groups).
     450             :          */
     451           0 :         handle = gmpr_notif->host_notif_intf_id;
     452           0 :         gif = gmp_handle_to_gif(handle);
     453           0 :         if (!gif) {
     454           0 :             continue;
     455             :         }
     456             : 
     457             :         /*
     458             :          *  Get needed addresses.
     459             :          */
     460           0 :         memcpy(&g, &gmpr_notif->host_notif_group_addr, IPV4_ADDR_LEN);
     461           0 :         memcpy(&s, &gmpr_notif->host_notif_source_addr, IPV4_ADDR_LEN);
     462           0 :         memcpy(&h, &gmpr_notif->host_notif_host_addr, IPV4_ADDR_LEN);
     463             : 
     464             :         /*
     465             :          *  Update mapped OIF module.
     466             :          */
     467           0 :         switch (gmpr_notif->host_notif_type) {
     468           0 :             case GMPR_NOTIF_HOST_UNKNOWN:
     469           0 :                 break;
     470             : 
     471           0 :             case GMPR_NOTIF_HOST_JOIN:
     472             :                 /*
     473             :                  *  This is a join.
     474             :                  */
     475           0 :                 gmp_host_update(gd, gif, TRUE, h, s, g);
     476           0 :                 break;
     477             : 
     478           0 :             case GMPR_NOTIF_HOST_LEAVE:
     479             :             case GMPR_NOTIF_HOST_TIMEOUT:
     480             :             case GMPR_NOTIF_HOST_IFDOWN:
     481             :                 /*
     482             :                  *  This is a leave.
     483             :                  */
     484           0 :                 gmp_host_update(gd, gif, FALSE, h, s, g);
     485           0 :                 break;
     486             : 
     487           0 :             default:
     488           0 :                 assert(0);
     489             :                 break;
     490             :         }
     491             :     }
     492             : 
     493             :     /*
     494             :      * If there's a notification pointer, it means that we bailed out
     495             :      * due to hitting the notification count limit.  In that case we
     496             :      * explicitly toss the notification block and then fall out, which
     497             :      * causes the job to be requeued.
     498             :      *
     499             :      * On the other hand, if the notification pointer is NULL, it means that
     500             :      * we drained the queue, in which case we should kill the job.
     501             :      */
     502           0 :     if (gmpr_notif) {
     503           0 :         gmpr_return_host_notification(gmpr_notif);
     504           0 :         pending = TRUE;
     505             :     }
     506             : 
     507           0 :     return pending;
     508             : }
     509             : 
     510             : // Top level handler to handle both <S,G> and also
     511             : // per-host, per-<S,G> notifications.
     512           0 : boolean gmp_notification_handler(mgm_global_data *gd)
     513             : {
     514           0 :     boolean pending = FALSE;
     515             : 
     516           0 :     pending |= gmp_client_notification(gd);
     517             : 
     518           0 :     pending |= gmp_client_host_notification(gd);
     519             : 
     520           0 :     return pending;
     521             : }
     522             : 
     523             : // per-<S,G> notification handler registered with GMP.
     524             : // Triggers the agent IGMP TaskTrigger instance
     525           0 : void igmp_notification_ready(void *context)
     526             : {
     527           0 :     mgm_global_data *gd = (mgm_global_data *)context;
     528             : 
     529           0 :     gmp_notification_ready(gd);
     530           0 : }
     531             : 
     532             : // per-host, per-<S,G> notification handler registered with GMP.
     533             : // Triggers the agent IGMP TaskTrigger instance
     534           0 : void igmp_host_notification_ready(void *context)
     535             : {
     536           0 :     mgm_global_data *gd = (mgm_global_data *)context;
     537             : 
     538           0 :     gmp_notification_ready(gd);
     539           0 : }
     540             : 
     541             : // No-op for now. Single querier support per-compute
     542          80 : void mgm_querier_change(void *cli_context UNUSED, gmp_intf_handle *handle,
     543             :                 boolean querier, u_int8_t *querier_addr)
     544             : {
     545          80 :     return;
     546             : }
     547             : 
     548             : // Tracing utility mapped to Multicast trace.
     549           2 : void gmpx_trace(void *context, const char *fmt, ...)
     550             : {
     551             :     va_list arglist;
     552             :     char dest[MAXSTRINGSIZE_1];
     553           2 :     va_start( arglist, fmt );
     554           2 :     vsprintf(dest, fmt, arglist);
     555           2 :     va_end( arglist );
     556             :     char buff[MAXSTRINGSIZE_1];
     557           2 :     snprintf(buff, sizeof(buff), dest, arglist);
     558             : 
     559           2 :     MCTRACE(Info, "igmp_trace: ", buff);
     560             : 
     561           4 :     return;
     562             : }
     563             : 
     564             : // Not used.
     565           0 : void gmpx_post_event(void *context, gmpx_event_type ev,
     566             :                 const void *parms, ...)
     567             : {
     568           0 :     return;
     569             : }
     570             : 
     571             : // Send one IGMP packet at a time.
     572         216 : bool igmp_send_one_packet(gmp_intf_handle *intf)
     573             : {
     574         216 :     uint8_t *pkt = NULL;
     575             :     gmp_addr_string dest_addr;
     576             :     mgm_global_data *gd;
     577             :     gmp_intf *gif;
     578             : 
     579         216 :     gd = &mgm_global[MCAST_AF_IPV4];
     580         216 :     gif = gmp_handle_to_gif(intf);
     581         216 :     if (!gif) {
     582           0 :         return false;
     583             :     }
     584             : 
     585         216 :     pkt = gmp_get_send_buffer(gd, gif);
     586         216 :     if (!pkt) {
     587           0 :         return false;
     588             :     }
     589             : 
     590         216 :     uint32_t formatted_len = igmp_next_xmit_packet(GMP_ROLE_ROUTER, intf, pkt,
     591             :                                     dest_addr.gmp_v4_addr, 1500, gd, 0);
     592         216 :     if (formatted_len == 0) {
     593         148 :         gmp_free_send_buffer(gd, gif, pkt);
     594         148 :         return false;
     595             :     }
     596             : 
     597          68 :     gmp_send_one_packet(gd, gif, pkt, formatted_len, dest_addr);
     598             : 
     599          68 :     gmp_free_send_buffer(gd, gif, pkt);
     600             : 
     601          68 :     return true;
     602             : }
     603             : 
     604             : // Called by GMP to send out IGMP packet.
     605         148 : void gmp_xmit_ready(gmp_role role, gmp_proto proto, gmpx_intf_id intf_id)
     606             : {
     607         148 :     if (role != GMP_ROLE_ROUTER) {
     608           0 :         return;
     609             :     }
     610             : 
     611         148 :     if (proto != GMP_PROTO_IGMP) {
     612           0 :         return;
     613             :     }
     614             : 
     615         148 :     gmp_intf_handle *intf = (gmp_intf_handle*)intf_id;
     616         148 :     if (!intf) {
     617           0 :         return;
     618             :     }
     619             : 
     620         216 :     while (igmp_send_one_packet(intf));
     621             : 
     622         148 :     return;
     623             : }
     624             : 
     625          68 : void gmp_static_peek(gmp_intf_handle *handle, gmp_proto proto,
     626             :                  gmp_packet *rcv_packet)
     627             : {
     628          68 :     return;
     629             : }
     630             : 

Generated by: LCOV version 1.14