Skip to content

Commit

Permalink
feat: Automatically join sol-node mcast addresses
Browse files Browse the repository at this point in the history
IPv6 over Ethernet should join the solicited-node multicast
addresses required by the configured IPv6 addresses, as neighbor
solicitations for these addresses have the solicited-node multicast
address as destination address.

This commit automatically leaves old solicited-node multicast addresses
and joins the new set when the IP addresses on the interface are
updated.
  • Loading branch information
bergzand committed Nov 14, 2024
1 parent cd39653 commit 508b16c
Show file tree
Hide file tree
Showing 3 changed files with 48 additions and 7 deletions.
7 changes: 6 additions & 1 deletion src/iface/interface/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -361,7 +361,12 @@ impl Interface {
pub fn update_ip_addrs<F: FnOnce(&mut Vec<IpCidr, IFACE_MAX_ADDR_COUNT>)>(&mut self, f: F) {
f(&mut self.inner.ip_addrs);
InterfaceInner::flush_neighbor_cache(&mut self.inner);
InterfaceInner::check_ip_addrs(&self.inner.ip_addrs)
InterfaceInner::check_ip_addrs(&self.inner.ip_addrs);

#[cfg(all(feature = "proto-ipv6", feature = "multicast"))]
if self.inner.caps.medium == Medium::Ethernet {
self.update_solicited_node_groups();
}
}

/// Check whether the interface has the given IP address assigned.
Expand Down
34 changes: 32 additions & 2 deletions src/iface/interface/multicast.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
use core::result::Result;
use heapless::LinearMap;
use heapless::{LinearMap, Vec};

#[cfg(feature = "proto-ipv4")]
use super::{check, IpPayload, Packet};
use super::{Interface, InterfaceInner};
use crate::config::IFACE_MAX_MULTICAST_GROUP_COUNT;
use crate::config::{IFACE_MAX_ADDR_COUNT, IFACE_MAX_MULTICAST_GROUP_COUNT};
use crate::phy::{Device, PacketMeta};
use crate::wire::*;

Expand Down Expand Up @@ -140,6 +140,36 @@ impl Interface {
self.inner.has_multicast_group(addr)
}

#[cfg(feature = "proto-ipv6")]
pub(super) fn update_solicited_node_groups(&mut self) {
// Remove old solicited-node multicast addresses
let removals: Vec<_, IFACE_MAX_MULTICAST_GROUP_COUNT> = self
.inner
.multicast
.groups
.keys()
.filter_map(|group_addr| match group_addr {
IpAddress::Ipv6(address)
if address.is_solicited_node_multicast()
&& self.inner.has_solicited_node(*address) =>
{
Some(*group_addr)
}
_ => None,
})
.collect();
for removal in removals {
let _ = self.leave_multicast_group(removal);
}

let cidrs: Vec<IpCidr, IFACE_MAX_ADDR_COUNT> = Vec::from_slice(self.ip_addrs()).unwrap();
for cidr in cidrs {
if let IpCidr::Ipv6(cidr) = cidr {
let _ = self.join_multicast_group(cidr.address().solicited_node());
}
}
}

/// Do multicast egress.
///
/// - Send join/leave packets according to the multicast group state.
Expand Down
14 changes: 10 additions & 4 deletions src/iface/interface/tests/ipv6.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1296,6 +1296,10 @@ fn test_join_ipv6_multicast_group(#[case] medium: Medium) {

let timestamp = Instant::from_millis(0);

// Drain the unsolicited node multicast report from the device
iface.poll(timestamp, &mut device, &mut sockets);
let _ = recv_icmpv6(&mut device, timestamp);

for &group in &groups {
iface.join_multicast_group(group).unwrap();
assert!(iface.has_multicast_group(group));
Expand Down Expand Up @@ -1372,9 +1376,11 @@ fn test_join_ipv6_multicast_group(#[case] medium: Medium) {
}
);

iface.leave_multicast_group(group_addr).unwrap();
assert!(!iface.has_multicast_group(group_addr));
iface.poll(timestamp, &mut device, &mut sockets);
assert!(!iface.has_multicast_group(group_addr));
if !group_addr.is_solicited_node_multicast() {
iface.leave_multicast_group(group_addr).unwrap();
assert!(!iface.has_multicast_group(group_addr));
iface.poll(timestamp, &mut device, &mut sockets);
assert!(!iface.has_multicast_group(group_addr));
}
}
}

0 comments on commit 508b16c

Please sign in to comment.