IPv6: implement multicast support

* Copied and adapted deliver_multicast from IPv4

* However, we still need special handling for the Neighbor Discovery
  Protocol as there is no associated socket. So for now we just pass
  any multicast ICMPv6 packets to the ICMPv6 module's receive_data
  that in turn passes it to the NDP module.

Change-Id: Idc9db0ec2132dffcf65c3b767dd1e428c44b27ba
Reviewed-on: https://review.haiku-os.org/c/haiku/+/10169
Reviewed-by: Jérôme Duval <[email protected]>
Reviewed-by: waddlesplash <[email protected]>
Tested-by: Commit checker robot <[email protected]>
This commit is contained in:
Christof Meerwald
2026-01-08 20:17:42 +00:00
committed by waddlesplash
parent 04ebf3d669
commit 5fed29dcb0
2 changed files with 50 additions and 57 deletions
@@ -340,14 +340,6 @@ icmp6_receive_data(net_buffer *buffer)
}
status_t
icmp6_deliver_data(net_protocol *protocol, net_buffer *buffer)
{
// TODO: does this look OK?
return icmp6_receive_data(buffer);
}
status_t
icmp6_error_received(net_error code, net_error_data* errorData, net_buffer* data)
{
@@ -427,7 +419,7 @@ net_protocol_module_info sICMP6Module = {
icmp6_get_domain,
icmp6_get_mtu,
icmp6_receive_data,
icmp6_deliver_data,
NULL, // deliver_data
icmp6_error_received,
icmp6_error_reply,
NULL, // add_ancillary_data()
@@ -723,64 +723,58 @@ send_fragments(ipv6_protocol* protocol, struct net_route* route,
}
static status_t
deliver_multicast(net_protocol_module_info* module, net_buffer* buffer,
bool deliverToRaw, net_interface *interface)
{
sockaddr_in6* multicastAddr = (sockaddr_in6*)buffer->destination;
MulticastState::ValueIterator it = sMulticastState->Lookup(std::make_pair(
&multicastAddr->sin6_addr, interface->index));
while (it.HasNext()) {
IPv6GroupInterface* state = it.Next();
ipv6_protocol* ipproto = state->Parent()->Socket();
if (deliverToRaw && ipproto->raw == NULL)
continue;
if (state->FilterAccepts(buffer)) {
// TODO: do as in IPv4 code
module->deliver_data(ipproto, buffer);
}
}
return B_OK;
}
static status_t
static bool
deliver_multicast(net_protocol_module_info* module, net_buffer* buffer,
bool deliverToRaw)
{
TRACE("deliver_multicast(%p [%" B_PRIu32 " bytes])", buffer, buffer->size);
if (module->deliver_data == NULL)
return B_OK;
return false;
MutexLocker _(sMulticastGroupsLock);
status_t status = B_OK;
if (buffer->interface_address != NULL) {
status = deliver_multicast(module, buffer, deliverToRaw,
buffer->interface_address->interface);
} else {
#if 0 // FIXME: multicast
net_domain_private* domain = (net_domain_private*)sDomain;
RecursiveLocker locker(domain->lock);
sockaddr_in6* multicastAddr = (sockaddr_in6*)buffer->destination;
net_interface* interface = NULL;
while (true) {
interface = (net_interface*)list_get_next_item(
&domain->interfaces, interface);
if (interface == NULL)
break;
uint32 index = buffer->index;
if (buffer->interface_address != NULL)
index = buffer->interface_address->interface->index;
status = deliver_multicast(module, buffer, deliverToRaw, interface);
if (status < B_OK)
break;
MulticastState::ValueIterator it = sMulticastState->Lookup(std::make_pair(
&multicastAddr->sin6_addr, index));
size_t count = 0;
while (it.HasNext()) {
IPv6GroupInterface* state = it.Next();
ipv6_protocol* ipProtocol = state->Parent()->Socket();
// There is no socket for Neighbor Discovery Protocol
if (ipProtocol->socket == NULL)
continue;
if (deliverToRaw && (ipProtocol->raw == NULL
|| ipProtocol->socket->protocol != buffer->protocol))
continue;
if (state->FilterAccepts(buffer)) {
net_protocol* protocol = ipProtocol;
if (protocol->module != module) {
// as multicast filters are installed with an IPv6 protocol
// reference, we need to go and find the appropriate instance
// related to the 'receiving protocol' with module 'module'.
protocol = ipProtocol->socket->first_protocol;
while (protocol != NULL && protocol->module != module)
protocol = protocol->next;
}
if (protocol != NULL) {
module->deliver_data(protocol, buffer);
count++;
}
}
#endif
}
return status;
return count > 0;
}
@@ -1561,7 +1555,14 @@ ipv6_receive_data(net_buffer* buffer)
// model be a little different from the unicast one. We deliver
// this frame directly to all sockets registered with interest
// for this multicast group.
return deliver_multicast(module, buffer, false);
deliver_multicast(module, buffer, false);
if (protocol != IPPROTO_ICMPV6) {
// ICMPv6 will still be passed to receive_data below,
// as it might be a neighbor solicitation; otherwise,
// we are done.
gBufferModule->free(buffer);
return B_OK;
}
}
return module->receive_data(buffer);