diff --git a/src/iface/interface/mod.rs b/src/iface/interface/mod.rs index 4df6d7378..4a2458ab6 100644 --- a/src/iface/interface/mod.rs +++ b/src/iface/interface/mod.rs @@ -361,7 +361,12 @@ impl Interface { pub fn update_ip_addrs)>(&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. diff --git a/src/iface/interface/multicast.rs b/src/iface/interface/multicast.rs index 68b1c7750..0785a84ec 100644 --- a/src/iface/interface/multicast.rs +++ b/src/iface/interface/multicast.rs @@ -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::*; @@ -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 = 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. diff --git a/src/iface/interface/tests/ipv6.rs b/src/iface/interface/tests/ipv6.rs index f67737b31..7b2bad9d2 100644 --- a/src/iface/interface/tests/ipv6.rs +++ b/src/iface/interface/tests/ipv6.rs @@ -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)); @@ -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)); + } } }