diff --git a/CHANGELOG.md b/CHANGELOG.md index 197ceee2cd..26e37b7fe8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -50,6 +50,8 @@ This project adheres to [Semantic Versioning](http://semver.org/). ([#741](https://github.com/nix-rust/nix/pull/741)) - Added more standard trait implementations for various types. ([#814](https://github.com/nix-rust/nix/pull/814)) +- Added `nix::sys::socket::EtherAddr` on Linux and all bsdlike system. + ([#813](https://github.com/nix-rust/nix/pull/813)) ### Changed - Use native `pipe2` on all BSD targets. Users should notice no difference. diff --git a/src/sys/socket/addr.rs b/src/sys/socket/addr.rs index 61f1a5d5e9..5c14e749f8 100644 --- a/src/sys/socket/addr.rs +++ b/src/sys/socket/addr.rs @@ -206,7 +206,7 @@ impl AddressFamily { /// Create a new `AddressFamily` from an integer value retrieved from `libc`, usually from /// the `sa_family` field of a `sockaddr`. /// - /// Currently only supports these address families: Unix, Inet (v4 & v6), Netlink + /// Currently only supports these address families: Unix, Inet (v4 & v6), Netlink, Link/Packet /// and System. Returns None for unsupported or unknown address families. pub fn from_i32(family: i32) -> Option { match family { @@ -217,6 +217,15 @@ impl AddressFamily { libc::AF_NETLINK => Some(AddressFamily::Netlink), #[cfg(any(target_os = "macos", target_os = "macos"))] libc::AF_SYSTEM => Some(AddressFamily::System), + #[cfg(any(target_os = "android", target_os = "linux"))] + libc::AF_PACKET => Some(AddressFamily::Packet), + #[cfg(any(target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd"))] + libc::AF_LINK => Some(AddressFamily::Link), _ => None } } @@ -720,6 +729,16 @@ pub enum SockAddr { Netlink(NetlinkAddr), #[cfg(any(target_os = "ios", target_os = "macos"))] SysControl(SysControlAddr), + /// Ethernet MAC address + #[cfg(any(target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd", + target_os = "android", + target_os = "linux"))] + Ether(EtherAddr) } impl SockAddr { @@ -750,6 +769,15 @@ impl SockAddr { SockAddr::Netlink(..) => AddressFamily::Netlink, #[cfg(any(target_os = "ios", target_os = "macos"))] SockAddr::SysControl(..) => AddressFamily::System, + #[cfg(any(target_os = "android", target_os = "linux"))] + SockAddr::Ether(..) => AddressFamily::Packet, + #[cfg(any(target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd"))] + SockAddr::Ether(..) => AddressFamily::Link } } @@ -777,6 +805,27 @@ impl SockAddr { #[cfg(any(target_os = "ios", target_os = "macos"))] Some(AddressFamily::System) => Some(SockAddr::SysControl( SysControlAddr(*(addr as *const sys_control::sockaddr_ctl)))), + #[cfg(any(target_os = "android", target_os = "linux"))] + Some(AddressFamily::Packet) => { + let ether_addr = EtherAddr::new(*(addr as *const libc::sockaddr_ll)); + match ether_addr { + Some(ether_addr) => Some(SockAddr::Ether(ether_addr)), + None => None + } + }, + #[cfg(any(target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd"))] + Some(AddressFamily::Link) => { + let ether_addr = EtherAddr::new(*(addr as *const libc::sockaddr_dl)); + match ether_addr { + Some(ether_addr) => Some(SockAddr::Ether(ether_addr)), + None => None + } + }, // Other address families are currently not supported and simply yield a None // entry instead of a proper conversion to a `SockAddr`. Some(_) => None, @@ -794,6 +843,15 @@ impl SockAddr { SockAddr::Netlink(NetlinkAddr(ref sa)) => (mem::transmute(sa), mem::size_of::() as libc::socklen_t), #[cfg(any(target_os = "ios", target_os = "macos"))] SockAddr::SysControl(SysControlAddr(ref sa)) => (mem::transmute(sa), mem::size_of::() as libc::socklen_t), + #[cfg(any(target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd", + target_os = "linux", + target_os = "android"))] + SockAddr::Ether(_) => unimplemented!() } } } @@ -811,6 +869,17 @@ impl PartialEq for SockAddr { (SockAddr::Netlink(ref a), SockAddr::Netlink(ref b)) => { a == b } + #[cfg(any(target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd", + target_os = "linux", + target_os = "android"))] + (SockAddr::Ether(ref a), SockAddr::Ether(ref b)) => { + a == b + } _ => false, } } @@ -828,6 +897,15 @@ impl hash::Hash for SockAddr { SockAddr::Netlink(ref a) => a.hash(s), #[cfg(any(target_os = "ios", target_os = "macos"))] SockAddr::SysControl(ref a) => a.hash(s), + #[cfg(any(target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd", + target_os = "linux", + target_os = "android"))] + SockAddr::Ether(ref ether_addr) => ether_addr.hash(s) } } } @@ -847,6 +925,15 @@ impl fmt::Display for SockAddr { SockAddr::Netlink(ref nl) => nl.fmt(f), #[cfg(any(target_os = "ios", target_os = "macos"))] SockAddr::SysControl(ref sc) => sc.fmt(f), + #[cfg(any(target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd", + target_os = "linux", + target_os = "android"))] + SockAddr::Ether(ref ether_addr) => ether_addr.fmt(f) } } } @@ -1014,4 +1101,227 @@ pub mod sys_control { fmt::Display::fmt(self, f) } } -} \ No newline at end of file +} + + +#[cfg(any(target_os = "android", target_os = "linux"))] +#[derive(Clone, Copy)] +pub struct EtherAddr(pub libc::sockaddr_ll); + +#[cfg(any(target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd"))] +#[derive(Clone, Copy)] +pub struct EtherAddr(pub libc::sockaddr_dl); + + +#[cfg(any(target_os = "android", target_os = "linux"))] +impl EtherAddr { + pub fn new(sll: libc::sockaddr_ll) -> Option { + Some(EtherAddr(sll)) + } + + pub fn is_empty(&self) -> bool { + false + } + + pub fn addr(&self) -> [u8; 6] { + [self.0.sll_addr[0], + self.0.sll_addr[1], + self.0.sll_addr[2], + self.0.sll_addr[3], + self.0.sll_addr[4], + self.0.sll_addr[5]] + } +} + +#[cfg(any(target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd"))] +impl EtherAddr { + pub fn new(sdl: libc::sockaddr_dl) -> Option { + let sdl_wrap = EtherAddr(sdl); + match sdl_wrap.is_empty() { + true => None, + false => Some(sdl_wrap), + } + } + + pub fn nlen(&self) -> usize { + self.0.sdl_nlen as usize + } + + pub fn is_empty(&self) -> bool { + self.nlen() + 5 >= self.max_nlen() + } + + #[cfg(any(target_os = "macos", + target_os = "ios", + target_os = "dragonfly", + target_os = "netbsd"))] + pub fn max_nlen(&self) -> usize { 12 } + + #[cfg(target_os = "openbsd")] + pub fn max_nlen(&self) -> usize { 24 } + + #[cfg(target_os = "freebsd")] + pub fn max_nlen(&self) -> usize { 46 } + + pub fn addr(&self) -> [u8; 6] { + assert_eq!(self.is_empty(), false); + let nlen = self.nlen(); + + [self.0.sdl_data[nlen ] as u8, + self.0.sdl_data[nlen + 1] as u8, + self.0.sdl_data[nlen + 2] as u8, + self.0.sdl_data[nlen + 3] as u8, + self.0.sdl_data[nlen + 4] as u8, + self.0.sdl_data[nlen + 5] as u8 ] + } + +} + +#[cfg(any(target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd", + target_os = "linux", + target_os = "android"))] +impl Eq for EtherAddr {} + + +#[cfg(any(target_os = "android", target_os = "linux"))] +impl PartialEq for EtherAddr { + fn eq(&self, other: &Self) -> bool { + let (a, b) = (self.0, other.0); + (a.sll_family, a.sll_protocol, a.sll_ifindex, a.sll_hatype, + a.sll_pkttype, a.sll_halen, a.sll_addr) == + (b.sll_family, b.sll_protocol, b.sll_ifindex, b.sll_hatype, + b.sll_pkttype, b.sll_halen, b.sll_addr) + } +} + +#[cfg(any(target_os = "android", target_os = "linux"))] +impl hash::Hash for EtherAddr { + fn hash(&self, s: &mut H) { + let a = self.0; + (a.sll_family, a.sll_protocol, a.sll_ifindex, a.sll_hatype, + a.sll_pkttype, a.sll_halen, a.sll_addr).hash(s); + } +} + +#[cfg(any(target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd"))] +impl PartialEq for EtherAddr { + #[cfg(any(target_os = "macos", + target_os = "ios", + target_os = "netbsd", + target_os = "openbsd"))] + fn eq(&self, other: &Self) -> bool { + let (a, b) = (self.0, other.0); + (a.sdl_len, a.sdl_family, a.sdl_index, a.sdl_type, + a.sdl_nlen, a.sdl_alen, a.sdl_slen, a.sdl_data) == + (b.sdl_len, b.sdl_family, b.sdl_index, b.sdl_type, + b.sdl_nlen, b.sdl_alen, b.sdl_slen, b.sdl_data) + } + + #[cfg(target_os = "freebsd")] + fn eq(&self, other: &Self) -> bool { + let (a, b) = (self.0, other.0); + (a.sdl_len, a.sdl_family, a.sdl_index, a.sdl_type, + a.sdl_nlen, a.sdl_alen, a.sdl_slen, + &a.sdl_data[0..30], &a.sdl_data[30..46]) == + (b.sdl_len, b.sdl_family, b.sdl_index, b.sdl_type, + b.sdl_nlen, b.sdl_alen, b.sdl_slen, + &b.sdl_data[0..30], &b.sdl_data[30..46]) + } + + #[cfg(target_os = "dragonfly")] + fn eq(&self, other: &Self) -> bool { + let (a, b) = (self.0, other.0); + (a.sdl_len, a.sdl_family, a.sdl_index, a.sdl_type, a.sdl_nlen, + a.sdl_alen, a.sdl_slen, a.sdl_data, a.sdl_rcf, a.sdl_route) == + (b.sdl_len, b.sdl_family, b.sdl_index, b.sdl_type, b.sdl_nlen, + b.sdl_alen, b.sdl_slen, b.sdl_data, b.sdl_rcf, b.sdl_route) + } +} + +#[cfg(any(target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd"))] +impl hash::Hash for EtherAddr { + #[cfg(any(target_os = "macos", + target_os = "ios", + target_os = "netbsd", + target_os = "openbsd"))] + fn hash(&self, s: &mut H) { + let a = self.0; + (a.sdl_len, a.sdl_family, a.sdl_index, a.sdl_type, + a.sdl_nlen, a.sdl_alen, a.sdl_slen, a.sdl_data).hash(s); + } + + #[cfg(target_os = "freebsd")] + fn hash(&self, s: &mut H) { + let a = self.0; + (a.sdl_len, a.sdl_family, a.sdl_index, a.sdl_type, + a.sdl_nlen, a.sdl_alen, a.sdl_slen, + &a.sdl_data[0..30], &a.sdl_data[30..46]).hash(s); + } + + #[cfg(target_os = "dragonfly")] + fn hash(&self, s: &mut H) { + let a = self.0; + (a.sdl_len, a.sdl_family, a.sdl_index, a.sdl_type, a.sdl_nlen, + a.sdl_alen, a.sdl_slen, a.sdl_data, a.sdl_rcf, a.sdl_route).hash(s); + } +} + +#[cfg(any(target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd", + target_os = "linux", + target_os = "android"))] +impl fmt::Display for EtherAddr { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let addr = self.addr(); + write!(f, "{:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x}", + addr[0], + addr[1], + addr[2], + addr[3], + addr[4], + addr[5]) + } +} + +#[cfg(any(target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd", + target_os = "linux", + target_os = "android"))] +impl fmt::Debug for EtherAddr { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fmt::Display::fmt(self, f) + } +} diff --git a/src/sys/socket/mod.rs b/src/sys/socket/mod.rs index 0c7fbb977b..5eb97e4600 100644 --- a/src/sys/socket/mod.rs +++ b/src/sys/socket/mod.rs @@ -28,6 +28,16 @@ pub use self::addr::{ Ipv4Addr, Ipv6Addr, }; +#[cfg(any(target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd", + target_os = "linux", + target_os = "android"))] +pub use self::addr::EtherAddr; + #[cfg(any(target_os = "linux", target_os = "android"))] pub use ::sys::socket::addr::netlink::NetlinkAddr;