Skip to content

Commit ed7e10b

Browse files
committed
iface: RFC3376 §8.6/§8.7 startup query burst
When becoming IGMP querier (on interface startup or after the prior querier times out), immediately send the first General Query, then schedule (Robustness Variable − 1) additional queries at the Startup Query Interval (Query Interval / 4, default ≈ 31 s). This lets hosts learn about the querier quickly at startup rather than waiting up to one full Query Interval for the second query. The startup timer is cancelled when the interface leaves service or when a better querier is discovered; the query callback self-cancels if the querier flag is no longer set when it fires. Fixes #5 Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
1 parent 494f829 commit ed7e10b

2 files changed

Lines changed: 68 additions & 2 deletions

File tree

src/iface.c

Lines changed: 66 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,9 @@ static void stop_iface (struct ifi *ifi);
2525
static void send_query (struct ifi *v, uint32_t dst, int code, uint32_t group);
2626
static 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+
2831
static void router_timeout_cb (int timeout, void *arg);
2932

3033
static 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
/*

src/iface.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,11 @@ struct ifi {
1818
int ifi_vlan; /* Raw VLAN ID for send and accept */
1919
char ifi_name[IFNAMSIZ]; /* interface name */
2020
int ifi_index; /* Primarily for Linux systems */
21-
uint32_t ifi_inaddr; /* Current address of this interface */
21+
uint32_t ifi_inaddr; /* Current address of this interface */
2222
uint32_t ifi_query_interval; /* IGMP query interval */
2323
struct listaddr *ifi_querier; /* IGMP querier (one or none) */
2424
int ifi_timerid; /* IGMP query timer */
25+
int ifi_startup_timerid;/* startup query burst timer */
2526
int ifi_igmpv1_warn; /* To rate-limit IGMPv1 warnings */
2627
uint8_t ifi_hwaddr[6]; /* MAC address of this interface */
2728
};

0 commit comments

Comments
 (0)