mirror of
https://github.com/smoltcp-rs/smoltcp.git
synced 2025-09-27 12:51:11 +00:00
Merge pull request #1012 from bergzand/pr/join_solicited_node
feat: Automatically join solicited-node multicast addresses
This commit is contained in:
commit
4dcee75155
@ -361,7 +361,12 @@ impl Interface {
|
|||||||
pub fn update_ip_addrs<F: FnOnce(&mut Vec<IpCidr, IFACE_MAX_ADDR_COUNT>)>(&mut self, f: F) {
|
pub fn update_ip_addrs<F: FnOnce(&mut Vec<IpCidr, IFACE_MAX_ADDR_COUNT>)>(&mut self, f: F) {
|
||||||
f(&mut self.inner.ip_addrs);
|
f(&mut self.inner.ip_addrs);
|
||||||
InterfaceInner::flush_neighbor_cache(&mut self.inner);
|
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.
|
/// Check whether the interface has the given IP address assigned.
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
use core::result::Result;
|
use core::result::Result;
|
||||||
use heapless::LinearMap;
|
use heapless::{LinearMap, Vec};
|
||||||
|
|
||||||
#[cfg(any(feature = "proto-ipv4", feature = "proto-ipv6"))]
|
#[cfg(any(feature = "proto-ipv4", feature = "proto-ipv6"))]
|
||||||
use super::{check, IpPayload, Packet};
|
use super::{check, IpPayload, Packet};
|
||||||
use super::{Interface, InterfaceInner};
|
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::phy::{Device, PacketMeta};
|
||||||
use crate::wire::*;
|
use crate::wire::*;
|
||||||
|
|
||||||
@ -156,6 +156,29 @@ impl Interface {
|
|||||||
self.inner.has_multicast_group(addr)
|
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()
|
||||||
|
.cloned()
|
||||||
|
.filter(|a| matches!(a, IpAddress::Ipv6(a) if a.is_solicited_node_multicast() && !self.inner.has_solicited_node(*a)))
|
||||||
|
.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.
|
/// Do multicast egress.
|
||||||
///
|
///
|
||||||
/// - Send join/leave packets according to the multicast group state.
|
/// - Send join/leave packets according to the multicast group state.
|
||||||
|
@ -1296,6 +1296,10 @@ fn test_join_ipv6_multicast_group(#[case] medium: Medium) {
|
|||||||
|
|
||||||
let timestamp = Instant::from_millis(0);
|
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 {
|
for &group in &groups {
|
||||||
iface.join_multicast_group(group).unwrap();
|
iface.join_multicast_group(group).unwrap();
|
||||||
assert!(iface.has_multicast_group(group));
|
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();
|
if !group_addr.is_solicited_node_multicast() {
|
||||||
assert!(!iface.has_multicast_group(group_addr));
|
iface.leave_multicast_group(group_addr).unwrap();
|
||||||
iface.poll(timestamp, &mut device, &mut sockets);
|
assert!(!iface.has_multicast_group(group_addr));
|
||||||
assert!(!iface.has_multicast_group(group_addr));
|
iface.poll(timestamp, &mut device, &mut sockets);
|
||||||
|
assert!(!iface.has_multicast_group(group_addr));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1414,15 +1420,12 @@ fn test_handle_valid_multicast_query(#[case] medium: Medium) {
|
|||||||
|
|
||||||
let mut eth_bytes = vec![0u8; 86];
|
let mut eth_bytes = vec![0u8; 86];
|
||||||
|
|
||||||
let local_ip_addr = Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 101);
|
let local_ip_addr = Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 1);
|
||||||
let remote_ip_addr = Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 100);
|
let remote_ip_addr = Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 100);
|
||||||
let remote_hw_addr = EthernetAddress([0x52, 0x54, 0x00, 0x00, 0x00, 0x00]);
|
let remote_hw_addr = EthernetAddress([0x52, 0x54, 0x00, 0x00, 0x00, 0x00]);
|
||||||
let query_ip_addr = Ipv6Address::new(0xff02, 0, 0, 0, 0, 0, 0, 0x1234);
|
let query_ip_addr = Ipv6Address::new(0xff02, 0, 0, 0, 0, 0, 0, 0x1234);
|
||||||
|
|
||||||
iface.join_multicast_group(query_ip_addr).unwrap();
|
iface.join_multicast_group(query_ip_addr).unwrap();
|
||||||
iface
|
|
||||||
.join_multicast_group(local_ip_addr.solicited_node())
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
iface.poll(timestamp, &mut device, &mut sockets);
|
iface.poll(timestamp, &mut device, &mut sockets);
|
||||||
// flush multicast reports from the join_multicast_group calls
|
// flush multicast reports from the join_multicast_group calls
|
||||||
@ -1433,7 +1436,7 @@ fn test_handle_valid_multicast_query(#[case] medium: Medium) {
|
|||||||
(
|
(
|
||||||
Ipv6Address::UNSPECIFIED,
|
Ipv6Address::UNSPECIFIED,
|
||||||
IPV6_LINK_LOCAL_ALL_NODES,
|
IPV6_LINK_LOCAL_ALL_NODES,
|
||||||
vec![query_ip_addr, local_ip_addr.solicited_node()],
|
vec![local_ip_addr.solicited_node(), query_ip_addr],
|
||||||
),
|
),
|
||||||
// Address specific query, expect only the queried address back
|
// Address specific query, expect only the queried address back
|
||||||
(query_ip_addr, query_ip_addr, vec![query_ip_addr]),
|
(query_ip_addr, query_ip_addr, vec![query_ip_addr]),
|
||||||
@ -1562,3 +1565,41 @@ fn test_handle_valid_multicast_query(#[case] medium: Medium) {
|
|||||||
assert_eq!(record_reprs, expected_records);
|
assert_eq!(record_reprs, expected_records);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[rstest]
|
||||||
|
#[case(Medium::Ethernet)]
|
||||||
|
#[cfg(all(feature = "multicast", feature = "medium-ethernet"))]
|
||||||
|
fn test_solicited_node_multicast_autojoin(#[case] medium: Medium) {
|
||||||
|
let (mut iface, _, _) = setup(medium);
|
||||||
|
|
||||||
|
let addr1 = Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 1);
|
||||||
|
let addr2 = Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 2);
|
||||||
|
|
||||||
|
iface.update_ip_addrs(|ip_addrs| {
|
||||||
|
ip_addrs.clear();
|
||||||
|
ip_addrs.push(IpCidr::new(addr1.into(), 64)).unwrap();
|
||||||
|
});
|
||||||
|
assert!(iface.has_multicast_group(addr1.solicited_node()));
|
||||||
|
assert!(!iface.has_multicast_group(addr2.solicited_node()));
|
||||||
|
|
||||||
|
iface.update_ip_addrs(|ip_addrs| {
|
||||||
|
ip_addrs.clear();
|
||||||
|
ip_addrs.push(IpCidr::new(addr2.into(), 64)).unwrap();
|
||||||
|
});
|
||||||
|
assert!(!iface.has_multicast_group(addr1.solicited_node()));
|
||||||
|
assert!(iface.has_multicast_group(addr2.solicited_node()));
|
||||||
|
|
||||||
|
iface.update_ip_addrs(|ip_addrs| {
|
||||||
|
ip_addrs.clear();
|
||||||
|
ip_addrs.push(IpCidr::new(addr1.into(), 64)).unwrap();
|
||||||
|
ip_addrs.push(IpCidr::new(addr2.into(), 64)).unwrap();
|
||||||
|
});
|
||||||
|
assert!(iface.has_multicast_group(addr1.solicited_node()));
|
||||||
|
assert!(iface.has_multicast_group(addr2.solicited_node()));
|
||||||
|
|
||||||
|
iface.update_ip_addrs(|ip_addrs| {
|
||||||
|
ip_addrs.clear();
|
||||||
|
});
|
||||||
|
assert!(!iface.has_multicast_group(addr1.solicited_node()));
|
||||||
|
assert!(!iface.has_multicast_group(addr2.solicited_node()));
|
||||||
|
}
|
||||||
|
@ -125,6 +125,11 @@ pub(crate) trait AddressExt {
|
|||||||
/// `x_` prefix is to avoid a collision with the still-unstable method in `core::ip`.
|
/// `x_` prefix is to avoid a collision with the still-unstable method in `core::ip`.
|
||||||
fn x_multicast_scope(&self) -> MulticastScope;
|
fn x_multicast_scope(&self) -> MulticastScope;
|
||||||
|
|
||||||
|
/// Query whether the IPv6 address is a [solicited-node multicast address].
|
||||||
|
///
|
||||||
|
/// [Solicited-node multicast address]: https://datatracker.ietf.org/doc/html/rfc4291#section-2.7.1
|
||||||
|
fn is_solicited_node_multicast(&self) -> bool;
|
||||||
|
|
||||||
/// If `self` is a CIDR-compatible subnet mask, return `Some(prefix_len)`,
|
/// If `self` is a CIDR-compatible subnet mask, return `Some(prefix_len)`,
|
||||||
/// where `prefix_len` is the number of leading zeroes. Return `None` otherwise.
|
/// where `prefix_len` is the number of leading zeroes. Return `None` otherwise.
|
||||||
fn prefix_len(&self) -> Option<u8>;
|
fn prefix_len(&self) -> Option<u8>;
|
||||||
@ -193,6 +198,13 @@ impl AddressExt for Address {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn is_solicited_node_multicast(&self) -> bool {
|
||||||
|
self.octets()[0..13]
|
||||||
|
== [
|
||||||
|
0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xFF,
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
fn prefix_len(&self) -> Option<u8> {
|
fn prefix_len(&self) -> Option<u8> {
|
||||||
let mut ones = true;
|
let mut ones = true;
|
||||||
let mut prefix_len = 0;
|
let mut prefix_len = 0;
|
||||||
@ -680,6 +692,8 @@ pub(crate) mod test {
|
|||||||
const UNIQUE_LOCAL_ADDR: Address = Address::new(0xfd00, 0, 0, 201, 1, 1, 1, 1);
|
const UNIQUE_LOCAL_ADDR: Address = Address::new(0xfd00, 0, 0, 201, 1, 1, 1, 1);
|
||||||
const GLOBAL_UNICAST_ADDR: Address = Address::new(0x2001, 0xdb8, 0x3, 0, 0, 0, 0, 1);
|
const GLOBAL_UNICAST_ADDR: Address = Address::new(0x2001, 0xdb8, 0x3, 0, 0, 0, 0, 1);
|
||||||
|
|
||||||
|
const TEST_SOL_NODE_MCAST_ADDR: Address = Address::new(0xff02, 0, 0, 0, 0, 1, 0xff01, 101);
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_basic_multicast() {
|
fn test_basic_multicast() {
|
||||||
assert!(!LINK_LOCAL_ALL_ROUTERS.is_unspecified());
|
assert!(!LINK_LOCAL_ALL_ROUTERS.is_unspecified());
|
||||||
@ -688,12 +702,14 @@ pub(crate) mod test {
|
|||||||
assert!(!LINK_LOCAL_ALL_ROUTERS.is_loopback());
|
assert!(!LINK_LOCAL_ALL_ROUTERS.is_loopback());
|
||||||
assert!(!LINK_LOCAL_ALL_ROUTERS.x_is_unique_local());
|
assert!(!LINK_LOCAL_ALL_ROUTERS.x_is_unique_local());
|
||||||
assert!(!LINK_LOCAL_ALL_ROUTERS.is_global_unicast());
|
assert!(!LINK_LOCAL_ALL_ROUTERS.is_global_unicast());
|
||||||
|
assert!(!LINK_LOCAL_ALL_ROUTERS.is_solicited_node_multicast());
|
||||||
assert!(!LINK_LOCAL_ALL_NODES.is_unspecified());
|
assert!(!LINK_LOCAL_ALL_NODES.is_unspecified());
|
||||||
assert!(LINK_LOCAL_ALL_NODES.is_multicast());
|
assert!(LINK_LOCAL_ALL_NODES.is_multicast());
|
||||||
assert!(!LINK_LOCAL_ALL_NODES.is_link_local());
|
assert!(!LINK_LOCAL_ALL_NODES.is_link_local());
|
||||||
assert!(!LINK_LOCAL_ALL_NODES.is_loopback());
|
assert!(!LINK_LOCAL_ALL_NODES.is_loopback());
|
||||||
assert!(!LINK_LOCAL_ALL_NODES.x_is_unique_local());
|
assert!(!LINK_LOCAL_ALL_NODES.x_is_unique_local());
|
||||||
assert!(!LINK_LOCAL_ALL_NODES.is_global_unicast());
|
assert!(!LINK_LOCAL_ALL_NODES.is_global_unicast());
|
||||||
|
assert!(!LINK_LOCAL_ALL_NODES.is_solicited_node_multicast());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -704,6 +720,7 @@ pub(crate) mod test {
|
|||||||
assert!(!LINK_LOCAL_ADDR.is_loopback());
|
assert!(!LINK_LOCAL_ADDR.is_loopback());
|
||||||
assert!(!LINK_LOCAL_ADDR.x_is_unique_local());
|
assert!(!LINK_LOCAL_ADDR.x_is_unique_local());
|
||||||
assert!(!LINK_LOCAL_ADDR.is_global_unicast());
|
assert!(!LINK_LOCAL_ADDR.is_global_unicast());
|
||||||
|
assert!(!LINK_LOCAL_ADDR.is_solicited_node_multicast());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -714,6 +731,7 @@ pub(crate) mod test {
|
|||||||
assert!(Address::LOCALHOST.is_loopback());
|
assert!(Address::LOCALHOST.is_loopback());
|
||||||
assert!(!Address::LOCALHOST.x_is_unique_local());
|
assert!(!Address::LOCALHOST.x_is_unique_local());
|
||||||
assert!(!Address::LOCALHOST.is_global_unicast());
|
assert!(!Address::LOCALHOST.is_global_unicast());
|
||||||
|
assert!(!Address::LOCALHOST.is_solicited_node_multicast());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -724,6 +742,7 @@ pub(crate) mod test {
|
|||||||
assert!(!UNIQUE_LOCAL_ADDR.is_loopback());
|
assert!(!UNIQUE_LOCAL_ADDR.is_loopback());
|
||||||
assert!(UNIQUE_LOCAL_ADDR.x_is_unique_local());
|
assert!(UNIQUE_LOCAL_ADDR.x_is_unique_local());
|
||||||
assert!(!UNIQUE_LOCAL_ADDR.is_global_unicast());
|
assert!(!UNIQUE_LOCAL_ADDR.is_global_unicast());
|
||||||
|
assert!(!UNIQUE_LOCAL_ADDR.is_solicited_node_multicast());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -734,6 +753,18 @@ pub(crate) mod test {
|
|||||||
assert!(!GLOBAL_UNICAST_ADDR.is_loopback());
|
assert!(!GLOBAL_UNICAST_ADDR.is_loopback());
|
||||||
assert!(!GLOBAL_UNICAST_ADDR.x_is_unique_local());
|
assert!(!GLOBAL_UNICAST_ADDR.x_is_unique_local());
|
||||||
assert!(GLOBAL_UNICAST_ADDR.is_global_unicast());
|
assert!(GLOBAL_UNICAST_ADDR.is_global_unicast());
|
||||||
|
assert!(!GLOBAL_UNICAST_ADDR.is_solicited_node_multicast());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_sollicited_node_multicast() {
|
||||||
|
assert!(!TEST_SOL_NODE_MCAST_ADDR.is_unspecified());
|
||||||
|
assert!(TEST_SOL_NODE_MCAST_ADDR.is_multicast());
|
||||||
|
assert!(!TEST_SOL_NODE_MCAST_ADDR.is_link_local());
|
||||||
|
assert!(!TEST_SOL_NODE_MCAST_ADDR.is_loopback());
|
||||||
|
assert!(!TEST_SOL_NODE_MCAST_ADDR.x_is_unique_local());
|
||||||
|
assert!(!TEST_SOL_NODE_MCAST_ADDR.is_global_unicast());
|
||||||
|
assert!(TEST_SOL_NODE_MCAST_ADDR.is_solicited_node_multicast());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
Loading…
x
Reference in New Issue
Block a user