use std::{
net::{Ipv4Addr, SocketAddrV4, SocketAddrV6},
os::fd::RawFd,
};
use crate::{
cerr, control_message::zeroed_sockaddr_storage, interface::InterfaceName,
networkaddress::NetworkAddress,
};
use super::{
sealed::{PrivateToken, SealedMC, SealedNA},
MulticastJoinable,
};
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct MacAddress([u8; 6]);
impl From<[u8; 6]> for MacAddress {
fn from(value: [u8; 6]) -> Self {
MacAddress(value)
}
}
impl AsRef<[u8]> for MacAddress {
fn as_ref(&self) -> &[u8] {
&self.0
}
}
impl MacAddress {
pub const fn new(address: [u8; 6]) -> Self {
MacAddress(address)
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub struct EthernetAddress {
protocol: u16,
mac_address: MacAddress,
if_index: libc::c_int,
}
impl EthernetAddress {
pub const fn new(protocol: u16, mac_address: MacAddress, if_index: libc::c_int) -> Self {
EthernetAddress {
protocol,
mac_address,
if_index,
}
}
pub const fn mac(&self) -> MacAddress {
self.mac_address
}
pub const fn protocol(&self) -> u16 {
self.protocol
}
pub const fn interface(&self) -> libc::c_int {
self.if_index
}
}
impl SealedNA for EthernetAddress {}
impl NetworkAddress for EthernetAddress {
fn to_sockaddr(&self, _token: PrivateToken) -> libc::sockaddr_storage {
const _: () = assert!(
std::mem::size_of::<libc::sockaddr_storage>()
>= std::mem::size_of::<libc::sockaddr_ll>()
);
const _: () = assert!(
std::mem::align_of::<libc::sockaddr_storage>()
>= std::mem::align_of::<libc::sockaddr_ll>()
);
let mut result = zeroed_sockaddr_storage();
let out = unsafe { &mut (*(&mut result as *mut _ as *mut libc::sockaddr_ll)) };
out.sll_family = libc::AF_PACKET as _;
out.sll_addr[..6].copy_from_slice(&self.mac_address.0);
out.sll_halen = 6;
out.sll_protocol = u16::from_ne_bytes(self.protocol.to_be_bytes());
out.sll_ifindex = self.if_index;
result
}
fn from_sockaddr(addr: libc::sockaddr_storage, _token: PrivateToken) -> Option<Self> {
const _: () = assert!(
std::mem::size_of::<libc::sockaddr_storage>()
>= std::mem::size_of::<libc::sockaddr_ll>()
);
const _: () = assert!(
std::mem::align_of::<libc::sockaddr_storage>()
>= std::mem::align_of::<libc::sockaddr_ll>()
);
if addr.ss_family != libc::AF_PACKET as _ {
return None;
}
let input = unsafe { &(*(&addr as *const _ as *const libc::sockaddr_ll)) };
if input.sll_halen != 6 {
return None;
}
Some(EthernetAddress::new(
u16::from_be_bytes(input.sll_protocol.to_ne_bytes()),
MacAddress::new(input.sll_addr[..6].try_into().unwrap()),
input.sll_ifindex,
))
}
}
impl SealedMC for EthernetAddress {}
impl MulticastJoinable for EthernetAddress {
fn join_multicast(
&self,
socket: RawFd,
interface: InterfaceName,
_token: PrivateToken,
) -> std::io::Result<()> {
let request = libc::packet_mreq {
mr_ifindex: interface
.get_index()
.ok_or(std::io::ErrorKind::InvalidInput)? as _,
mr_type: libc::PACKET_MR_MULTICAST as _,
mr_alen: 6,
mr_address: [
self.mac_address.0[0],
self.mac_address.0[1],
self.mac_address.0[2],
self.mac_address.0[3],
self.mac_address.0[4],
self.mac_address.0[5],
0,
0,
],
};
cerr(unsafe {
libc::setsockopt(
socket,
libc::SOL_PACKET,
libc::PACKET_ADD_MEMBERSHIP,
&request as *const _ as *const _,
std::mem::size_of_val(&request) as _,
)
})?;
Ok(())
}
fn leave_multicast(
&self,
socket: RawFd,
interface: InterfaceName,
_token: PrivateToken,
) -> std::io::Result<()> {
let request = libc::packet_mreq {
mr_ifindex: interface
.get_index()
.ok_or(std::io::ErrorKind::InvalidInput)? as _,
mr_type: libc::PACKET_MR_MULTICAST as _,
mr_alen: 6,
mr_address: [
self.mac_address.0[0],
self.mac_address.0[1],
self.mac_address.0[2],
self.mac_address.0[3],
self.mac_address.0[4],
self.mac_address.0[5],
0,
0,
],
};
cerr(unsafe {
libc::setsockopt(
socket,
libc::SOL_PACKET,
libc::PACKET_DROP_MEMBERSHIP,
&request as *const _ as *const _,
std::mem::size_of_val(&request) as _,
)
})?;
Ok(())
}
}
impl SealedMC for SocketAddrV4 {}
impl MulticastJoinable for SocketAddrV4 {
fn join_multicast(
&self,
socket: RawFd,
interface: InterfaceName,
_token: PrivateToken,
) -> std::io::Result<()> {
let request = libc::ip_mreqn {
imr_multiaddr: libc::in_addr {
s_addr: u32::from_ne_bytes(self.ip().octets()),
},
imr_address: libc::in_addr {
s_addr: u32::from_ne_bytes(Ipv4Addr::UNSPECIFIED.octets()),
},
imr_ifindex: interface
.get_index()
.ok_or(std::io::ErrorKind::InvalidInput)? as _,
};
cerr(unsafe {
libc::setsockopt(
socket,
libc::IPPROTO_IP,
libc::IP_ADD_MEMBERSHIP,
&request as *const _ as *const _,
std::mem::size_of_val(&request) as _,
)
})?;
Ok(())
}
fn leave_multicast(
&self,
socket: RawFd,
interface: InterfaceName,
_token: PrivateToken,
) -> std::io::Result<()> {
let request = libc::ip_mreqn {
imr_multiaddr: libc::in_addr {
s_addr: u32::from_ne_bytes(self.ip().octets()),
},
imr_address: libc::in_addr {
s_addr: u32::from_ne_bytes(Ipv4Addr::UNSPECIFIED.octets()),
},
imr_ifindex: interface
.get_index()
.ok_or(std::io::ErrorKind::InvalidInput)? as _,
};
cerr(unsafe {
libc::setsockopt(
socket,
libc::IPPROTO_IP,
libc::IP_DROP_MEMBERSHIP,
&request as *const _ as *const _,
std::mem::size_of_val(&request) as _,
)
})?;
Ok(())
}
}
impl SealedMC for SocketAddrV6 {}
impl MulticastJoinable for SocketAddrV6 {
fn join_multicast(
&self,
socket: RawFd,
interface: InterfaceName,
_token: PrivateToken,
) -> std::io::Result<()> {
let request = libc::ipv6_mreq {
ipv6mr_multiaddr: libc::in6_addr {
s6_addr: self.ip().octets(),
},
ipv6mr_interface: interface
.get_index()
.ok_or(std::io::ErrorKind::InvalidInput)? as _,
};
cerr(unsafe {
libc::setsockopt(
socket,
libc::IPPROTO_IPV6,
libc::IPV6_ADD_MEMBERSHIP,
&request as *const _ as *const _,
std::mem::size_of_val(&request) as _,
)
})?;
Ok(())
}
fn leave_multicast(
&self,
socket: RawFd,
interface: InterfaceName,
_token: PrivateToken,
) -> std::io::Result<()> {
let request = libc::ipv6_mreq {
ipv6mr_multiaddr: libc::in6_addr {
s6_addr: self.ip().octets(),
},
ipv6mr_interface: interface
.get_index()
.ok_or(std::io::ErrorKind::InvalidInput)? as _,
};
cerr(unsafe {
libc::setsockopt(
socket,
libc::IPPROTO_IPV6,
libc::IPV6_DROP_MEMBERSHIP,
&request as *const _ as *const _,
std::mem::size_of_val(&request) as _,
)
})?;
Ok(())
}
}