فهرست منبع

feat: Automatically join sol-node mcast addresses

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.
Koen Zandberg 6 ماه پیش
والد
کامیت
2a5f90ab0b
3فایلهای تغییر یافته به همراه48 افزوده شده و 7 حذف شده
  1. 6 1
      src/iface/interface/mod.rs
  2. 32 2
      src/iface/interface/multicast.rs
  3. 10 4
      src/iface/interface/tests/ipv6.rs

+ 6 - 1
src/iface/interface/mod.rs

@@ -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.

+ 32 - 2
src/iface/interface/multicast.rs

@@ -1,10 +1,10 @@
 use core::result::Result;
-use heapless::LinearMap;
+use heapless::{LinearMap, Vec};
 
 #[cfg(any(feature = "proto-ipv4", feature = "proto-ipv6"))]
 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::*;
 
@@ -156,6 +156,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.

+ 10 - 4
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,10 +1376,12 @@ 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));
+        }
     }
 }