use std::io;
use std::net::{Ipv6Addr, SocketAddr, SocketAddrV6};
use socket2::{Domain, Protocol, Socket, Type};
#[derive(Clone, Debug)]
pub struct MulticastConfig {
pub group_addr: SocketAddrV6,
pub interface: u32,
pub interval_secs: u64,
pub poll_exponent: u8,
}
impl Default for MulticastConfig {
fn default() -> Self {
MulticastConfig {
group_addr: SocketAddrV6::new(
Ipv6Addr::new(0xff02, 0, 0, 0, 0, 0, 0, 0x101),
123,
0,
0,
),
interface: 0,
interval_secs: 64,
poll_exponent: 6,
}
}
}
pub fn join_multicast_v6(config: &MulticastConfig) -> io::Result<std::net::UdpSocket> {
let socket = Socket::new(Domain::IPV6, Type::DGRAM, Some(Protocol::UDP))?;
socket.set_only_v6(true)?;
socket.set_reuse_address(true)?;
socket.set_nonblocking(true)?;
let bind_addr = SocketAddrV6::new(Ipv6Addr::UNSPECIFIED, config.group_addr.port(), 0, 0);
socket.bind(&SocketAddr::V6(bind_addr).into())?;
socket.join_multicast_v6(config.group_addr.ip(), config.interface)?;
Ok(socket.into())
}
pub fn set_multicast_interface_v6(socket: &std::net::UdpSocket, interface: u32) -> io::Result<()> {
let socket2 = socket2::SockRef::from(socket);
socket2.set_multicast_if_v6(interface)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_multicast_config_default() {
let config = MulticastConfig::default();
assert_eq!(
*config.group_addr.ip(),
"ff02::101".parse::<Ipv6Addr>().unwrap()
);
assert_eq!(config.group_addr.port(), 123);
assert_eq!(config.interface, 0);
assert_eq!(config.interval_secs, 64);
assert_eq!(config.poll_exponent, 6);
}
#[test]
fn test_multicast_config_custom() {
let config = MulticastConfig {
group_addr: SocketAddrV6::new("ff05::101".parse::<Ipv6Addr>().unwrap(), 123, 0, 0),
interface: 2,
interval_secs: 128,
poll_exponent: 7,
};
assert_eq!(
*config.group_addr.ip(),
"ff05::101".parse::<Ipv6Addr>().unwrap()
);
assert_eq!(config.interface, 2);
}
#[test]
fn test_join_multicast_v6() {
let config = MulticastConfig::default();
match join_multicast_v6(&config) {
Ok(sock) => {
let local = sock.local_addr().unwrap();
assert!(local.is_ipv6());
assert_eq!(local.port(), 123);
}
Err(e) => {
eprintln!("skipping test_join_multicast_v6: {e}");
}
}
}
}