@@ -25,6 +25,9 @@ static void stop_iface (struct ifi *ifi);
2525static void send_query (struct ifi * v , uint32_t dst , int code , uint32_t group );
2626static void query_groups (int timeout , void * arg );
2727
28+ static void startup_query_cb (int timeout , void * arg );
29+ static void startup_query_timer (struct ifi * ifi );
30+
2831static void router_timeout_cb (int timeout , void * arg );
2932
3033static void delete_group_cb (int timeout , void * arg );
@@ -87,6 +90,7 @@ void iface_zero(struct ifi *ifi)
8790 ifi -> ifi_querier = NULL ;
8891 ifi -> ifi_query_interval = igmp_query_interval ;
8992 ifi -> ifi_timerid = 0 ;
93+ ifi -> ifi_startup_timerid = 0 ;
9094 ifi -> ifi_igmpv1_warn = 0 ;
9195}
9296
@@ -173,6 +177,7 @@ void iface_check_election(struct ifi *ifi)
173177 dbg ("Assuming %squerier duties on interface %s" ,
174178 is_igmp_proxy (ifi ) ? "proxy " : "" , ifi -> ifi_name );
175179 send_query (ifi , allhosts_group , igmp_response_interval , 0 );
180+ startup_query_timer (ifi ); /* RFC3376 §8.6, §8.7 startup query burst */
176181}
177182
178183/*
@@ -361,8 +366,12 @@ static void stop_iface(struct ifi *ifi)
361366 struct listaddr * a , * tmp ;
362367
363368 /*
364- * Stop query timer
369+ * Stop query timers (periodic and startup burst)
365370 */
371+ if (ifi -> ifi_startup_timerid > 0 ) {
372+ pev_timer_del (ifi -> ifi_startup_timerid );
373+ ifi -> ifi_startup_timerid = 0 ;
374+ }
366375 if (ifi -> ifi_timerid > 0 )
367376 pev_timer_del (ifi -> ifi_timerid );
368377 ifi -> ifi_timerid = 0 ;
@@ -899,6 +908,61 @@ void accept_membership_report(int ifindex, int vid, uint32_t src, uint32_t dst,
899908 }
900909}
901910
911+ /*
912+ * RFC3376 §8.6/§8.7: Send remaining startup queries at Startup Query Interval.
913+ * Called once per fire; re-arms itself until the count reaches zero.
914+ */
915+ static void startup_query_cb (int timeout , void * arg )
916+ {
917+ cbk_t * cbk = (cbk_t * )arg ;
918+ struct ifi * ifi ;
919+
920+ ifi = config_find_iface (cbk -> ifindex , cbk -> vid );
921+ if (!ifi || !(ifi -> ifi_flags & IFIF_IGMP_QUERIER ))
922+ goto end ;
923+
924+ send_query (ifi , allhosts_group , igmp_response_interval , 0 );
925+ if (-- cbk -> num > 0 ) {
926+ pev_timer_set (ifi -> ifi_startup_timerid , cbk -> delay * 1000000 );
927+ return ;
928+ }
929+
930+ end :
931+ if (ifi )
932+ ifi -> ifi_startup_timerid = pev_timer_del (ifi -> ifi_startup_timerid );
933+ }
934+
935+ /*
936+ * Schedule the startup query burst: igmp_robustness - 1 additional queries
937+ * (the first was already sent) at Startup Query Interval (Query Interval / 4).
938+ */
939+ static void startup_query_timer (struct ifi * ifi )
940+ {
941+ uint32_t qi = ifi -> ifi_query_interval ? ifi -> ifi_query_interval : igmp_query_interval ;
942+ uint32_t sqi = qi / 4 > 0 ? qi / 4 : 1 ;
943+ cbk_t * cbk ;
944+
945+ if (igmp_robustness <= 1 )
946+ return ;
947+
948+ cbk = calloc (1 , sizeof (cbk_t ));
949+ if (!cbk ) {
950+ err ("failed allocating startup query timer" );
951+ exit (EX_OSERR );
952+ }
953+
954+ cbk -> ifindex = ifi -> ifi_index ;
955+ cbk -> vid = ifi -> ifi_vlan ;
956+ cbk -> num = igmp_robustness - 1 ;
957+ cbk -> delay = sqi ;
958+
959+ if (ifi -> ifi_startup_timerid > 0 )
960+ pev_timer_del (ifi -> ifi_startup_timerid );
961+
962+ ifi -> ifi_startup_timerid = pev_timer_add (sqi * 1000000 , 0 , startup_query_cb , cbk );
963+ pev_timer_set_cb_del (ifi -> ifi_startup_timerid , free );
964+ }
965+
902966/*
903967 * When an active querier times out we assume the role here.
904968 */
@@ -913,6 +977,7 @@ static void router_timeout_cb(int timeout, void *arg)
913977
914978 ifi -> ifi_flags |= IFIF_IGMP_QUERIER ;
915979 send_query (ifi , allhosts_group , igmp_response_interval , 0 );
980+ startup_query_timer (ifi ); /* RFC3376 §8.6, §8.7 startup query burst */
916981}
917982
918983/*
0 commit comments