Skip to content

Re-join multicast groups when a capture re-attaches - #71

Merged
sbogomolov merged 1 commit into
mainfrom
interface/rejoin-groups-after-rebind
Aug 10, 2026
Merged

Re-join multicast groups when a capture re-attaches#71
sbogomolov merged 1 commit into
mainfrom
interface/rejoin-groups-after-rebind

Conversation

@sbogomolov

Copy link
Copy Markdown
Owner

Interface-recreation recovery restored the capture's binding but not what makes
traffic arrive at it. A destroyed interface takes its multicast group memberships
with it, Rebind() only re-attached the capture, and nothing else re-joins: a
family that keeps its addresses across the recreation is no transition, so the
reflector's own bring-up never runs. The interface came back with a live capture
that received nothing for any group it had — and on a switched network that is
unambiguous, since no IGMP/MLD report goes out and upstream stops forwarding.

The fix

Rebind() now ends by re-programming every group the socket holds a membership
for, on a join socket reopened per family.

The reopen is not tidiness. A kept socket is refused the duplicate (group, index)
join where a recreated interface is handed back the number it had, and memberships
it holds for a dead index still count against igmp_max_memberships (20 by default,
not raisable on a locked-down router) — so kept sockets would exhaust the cap after
a handful of recreations. The close precedes the reopen so the descriptor it frees
is the one the reopen takes, which is what lets the repair work at the process fd
limit rather than only when descriptors are plentiful.

GroupsJoined() is a second, independent reason to rebind. A failed re-join leaves
the capture attached, so the reconcile's probe-first early-out would skip the
socket permanently and the repair retry would never retry.

Join errnos are now classified: a membership already held is the end state we want
rather than a failure, and no address of the group's family yet is a wait — the
family's teardown drops the membership, or the retry replays the join once an
address arrives — so it logs at debug instead of once a second.

Testing

A root-gated test joins a group, recreates the interface, rebinds, and asserts the
membership against /proc/net/igmp. Asking the kernel is the point: the socket's own
bookkeeping still claims the group after the interface behind it died, so a
seam-level assertion would pass either way.

Verified the test fails without the fix — with the re-join removed it fails on
exactly the "must be re-programmed on the interface's new kernel object" assertion,
while all 897 other tests pass. That is how this got through the first time: the
existing recreation test asserts the capture re-attaches and keeps its fd, which
stays true while the socket is deaf.

910 unit tests native (Debug, ASan/UBSan), 898 in docker including 20 root-gated.

A destroyed interface takes its group memberships with it, and Rebind only
re-attached the capture. Nothing else re-joins: a family that keeps its
addresses across a recreation is no transition, so the reflector's own
bring-up never runs, and the capture comes back attached and permanently deaf
on every group it had.

Rebind now re-programs them on a join socket reopened per family. A kept
socket would be refused the duplicate (group, index) join where a recreated
interface is handed back its old number, and memberships it holds for a dead
index still count against igmp_max_memberships. The close precedes the reopen
so the descriptor it frees is the one the reopen takes.

A re-join that fails leaves the capture attached, so nothing else would report
the socket as deaf and the reconcile's probe would skip it; GroupsJoined is
that second reason to rebind. A join reporting the membership is already held
is the end state we want, not a failure; one reporting no address of the
group's family yet is a wait the family teardown or the retry resolves.
@sbogomolov sbogomolov self-assigned this Aug 10, 2026
@sbogomolov
sbogomolov merged commit 08e889a into main Aug 10, 2026
17 checks passed
@sbogomolov
sbogomolov deleted the interface/rejoin-groups-after-rebind branch August 10, 2026 18:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant