use crate::config::{
SourceFilter, SubscriptionAddressFamily, SubscriptionConfig, validate_multicast_selection,
};
use crate::error::McrxError;
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct RawSubscriptionConfig {
pub group: IpAddr,
pub source: SourceFilter,
pub interface: Option<IpAddr>,
pub interface_index: Option<u32>,
}
impl RawSubscriptionConfig {
pub fn validate(&self) -> Result<(), McrxError> {
validate_multicast_selection(
self.group,
&self.source,
self.interface,
self.interface_index,
)
}
pub fn family(&self) -> SubscriptionAddressFamily {
match self.group {
IpAddr::V4(_) => SubscriptionAddressFamily::Ipv4,
IpAddr::V6(_) => SubscriptionAddressFamily::Ipv6,
}
}
pub fn is_ipv4(&self) -> bool {
matches!(self.family(), SubscriptionAddressFamily::Ipv4)
}
pub fn is_ipv6(&self) -> bool {
matches!(self.family(), SubscriptionAddressFamily::Ipv6)
}
pub fn source_addr(&self) -> Option<IpAddr> {
match self.source {
SourceFilter::Any => None,
SourceFilter::Source(source) => Some(source),
}
}
pub fn asm(group: Ipv4Addr) -> Self {
Self::asm_ip(group.into())
}
pub fn asm_v6(group: Ipv6Addr) -> Self {
Self::asm_ip(group.into())
}
pub fn asm_ip(group: IpAddr) -> Self {
Self {
group,
source: SourceFilter::Any,
interface: None,
interface_index: None,
}
}
pub fn ssm(group: Ipv4Addr, source: Ipv4Addr) -> Self {
Self::ssm_ip(group.into(), source.into())
}
pub fn ssm_v6(group: Ipv6Addr, source: Ipv6Addr) -> Self {
Self::ssm_ip(group.into(), source.into())
}
pub fn ssm_ip(group: IpAddr, source: IpAddr) -> Self {
Self {
group,
source: SourceFilter::Source(source),
interface: None,
interface_index: None,
}
}
pub(crate) fn membership_compat_config(&self) -> SubscriptionConfig {
SubscriptionConfig {
group: self.group,
source: self.source.clone(),
dst_port: 1,
interface: self.interface,
interface_index: self.interface_index,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn valid_ipv4_raw_config_passes_validation() {
let cfg = RawSubscriptionConfig::asm(Ipv4Addr::new(239, 1, 2, 3));
assert!(cfg.validate().is_ok());
}
#[test]
fn valid_ipv6_ssm_raw_config_passes_validation() {
let cfg = RawSubscriptionConfig::ssm_v6(
"ff3e::8000:1234".parse().unwrap(),
"2001:db8::10".parse().unwrap(),
);
assert!(cfg.validate().is_ok());
}
#[test]
fn ipv6_raw_ssm_requires_ff3x_group_range() {
let cfg = RawSubscriptionConfig::ssm_v6(
"ff12::1234".parse().unwrap(),
"2001:db8::10".parse().unwrap(),
);
let result = cfg.validate();
assert!(matches!(result, Err(McrxError::InvalidIpv6SsmGroup)));
}
#[test]
fn ipv4_raw_config_rejects_interface_index() {
let mut cfg = RawSubscriptionConfig::asm(Ipv4Addr::new(239, 1, 2, 3));
cfg.interface_index = Some(7);
let result = cfg.validate();
assert!(matches!(result, Err(McrxError::InterfaceIndexRequiresIpv6)));
}
}