Skip to content

Add multicast group membership (join/leave) to the IP and stack interfaces - #540

Open
tmcgilchrist wants to merge 2 commits into
mirage:mainfrom
tmcgilchrist:multicast-membership
Open

Add multicast group membership (join/leave) to the IP and stack interfaces#540
tmcgilchrist wants to merge 2 commits into
mirage:mainfrom
tmcgilchrist:multicast-membership

Conversation

@tmcgilchrist

@tmcgilchrist tmcgilchrist commented Aug 4, 2026

Copy link
Copy Markdown
Member

Adds join_multicast_group / leave_multicast_group to Tcpip.Ip.S and Tcpip.Stack.V4V6 so a stack can receive datagrams sent to a multicast group (the capability requested in #362 and #498) and what mDNS/DNS-SD (RFC 6762/6763) needs to answer queries on 224.0.0.251.

Problem: No way to join a group: the direct IPv4 stack drops any non-own-address/non-broadcast destination (static_ipv4.ml of_interest), the socket stack never issues IP_ADD_MEMBERSHIP. So a responder can announce but never receive.

Change
We add these functions to join and leave a multicast group:

val join_multicast_group  : t -> ipaddr -> unit Lwt.t
val leave_multicast_group : t -> ipaddr -> unit Lwt.t
  • Direct IPv4 (static_ipv4): tracks joined groups and accepts datagrams addressed to them at input. This is sufficient for link-local multicast (the 224.0.0.0/24 range, which includes mDNS's 224.0.0.251) because switches flood that range and routers never forward it, so frames arrive without any on-wire membership announcement (ethernet already admits the multicast MAC). Routable (non-link-local) groups additionally require IGMP membership reports so the network forwards their traffic. Sending those, together with an IGMP implementation, which mirage-tcpip currently lacks, is left as future work.
  • Dual stack (IPV4V6): routes to v4/v6; IPv6 MLD is a no-op for now.
  • Socket UDP (udpv4v6_socket): IP_ADD_MEMBERSHIP on each bound fd and fds bound later, v4-on-dual-stack is best-effort (logged).
  • Socket IP shim + v6 socket: no-op. Defaults preserve existing behaviour.

Testing: Wired into a MirageOS mDNS responder (dns-mdns-mirage) that I'm working on, it joins 224.0.0.251 at startup and is then discovered and resolved by stock Avahi, on both the unix target (direct stack over a tap) and Solo5/spt. Without this change the direct stack drops the queries and the responder is invisible.

Closes #362, #498.

@hannesm

hannesm commented Aug 5, 2026

Copy link
Copy Markdown
Member

Thanks for your work. I'm no expert in multicast.

From the API side, I have some questions:

  • join_multicast_group/leave_multicast_group - both result in unit Lwt.t -- why Lwt.t? and why unit? From the implementation it looks there can be failures (you provide a non-multicast address)
  • there's no possibility to inspect the new field mutable groups : Ipaddr.V4.Set.t - may that be worth to add?
  • if I understand this correctly, this PR provides functionality to join and leave multicast groups. Is something more needed to send multicast packets?
  • is there some correlation (invariant/check) needed between multicast IP and mac address?

The comment you inserted "IGMP membership reports are only needed for non link-local groups and are left as future work" -- is it worth to document this in the API?

How does this solve #498? How can a listen call now specify to only accept multicast packets? (I don't remember what use case I had in mind when I opened #498 to be honest)

@tmcgilchrist

Copy link
Copy Markdown
Member Author

Why unit Lwt.t?

Yes the unit hides errors, not that there are many right now. The only real error is the socket IP_ADD_MEMBERSHIP failure is logged when it could also be returned as an error. In future a real IGMP implementation would send membership reports with the join/leave calls which would need to be Lwt.t. What about making the return (unit, error) result Lwt.t? Do you prefer extending Ip.error with a multicast variant, or a local error type?

For now I'm leaving the IGMP implementation as future work, I don't strictly need it for mDNS.

No way to inspect groups.

Yes, something like val multicast_groups : t -> ipaddr list. seems right.

Is something more needed to send multicast packets?

No, sending multicast packets already works. This PR is receive-only.

How does this solve #498? How can a listen specify multicast-only?

This PR adds group membership, which is how I interpreted #498 when I read "I'm only interested in multicast packets". I assumed you wanted more control over which packets you needed to process, which is partially there with the filtering being done in this change but only on the receive side for multicast.

A responder for a link-local multicast protocol such as mDNS/DNS-SD
needs to receive UDP datagrams sent to a multicast
group (e.g. 224.0.0.251), but the stack offers no way to join one:
  - the direct IPv4 stack discards any datagram whose destination is not its
    own address or a broadcast (static_ipv4.ml `of_interest`)
  - the socket stack never issues IP_ADD_MEMBERSHIP.

Add join_multicast_group / leave_multicast_group to Tcpip.Ip.S and
Tcpip.Stack.V4V6:
  - Static_ipv4 records joined groups and accepts datagrams addressed to them
    at input. On a directly attached link the ethernet layer already delivers
    multicast frames, so link-local groups (224.0.0.0/24) need no IGMP,
    membership reports for other groups are left as future work.
  - The IPV4V6 dual stack routes join/leave to the v4 or v6 layer, IPv6 (MLD)
    is a no-op for now.
  - The socket UDP stack performs IP_ADD_MEMBERSHIP on each bound fd (and fds
    bound later), remembering the groups; a v4 group on the default dual-stack
    socket is best-effort (logged on failure).
  - The socket IP shim and v6 socket hold no state (no-op).

Requested in mirage-tcpip mirage#362 and mirage#498.
@tmcgilchrist
tmcgilchrist force-pushed the multicast-membership branch from 786b9a3 to 477c7c5 Compare August 6, 2026 21:55
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.

Multicast Listening on Unikernel?

2 participants