diff --git a/CHANGELOG.md b/CHANGELOG.md index 5e8008d850..6b3b39faab 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 0b0d29ceeb..3d1fcd8474 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 } } @@ -721,6 +730,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 { @@ -751,6 +770,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 } } @@ -778,6 +806,25 @@ 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) => { + match EtherAddr::new_checked(*(addr as *const libc::sockaddr_ll)) { + 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) => { + match EtherAddr::new_checked(*(addr as *const libc::sockaddr_dl)) { + 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, @@ -795,6 +842,7 @@ 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), + SockAddr::Ether(_) => unimplemented!() } } } @@ -812,6 +860,9 @@ impl PartialEq for SockAddr { (SockAddr::Netlink(ref a), SockAddr::Netlink(ref b)) => { a == b } + (SockAddr::Ether(ref a), SockAddr::Ether(ref b)) => { + a == b + } _ => false, } } @@ -829,6 +880,7 @@ 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), + SockAddr::Ether(ref ether_addr) => ether_addr.hash(s) } } } @@ -848,6 +900,7 @@ 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), + SockAddr::Ether(ref ether_addr) => ether_addr.fmt(f) } } } @@ -1016,3 +1069,199 @@ pub mod sys_control { } } } + +#[cfg(any(target_os = "android", target_os = "linux"))] +#[derive(Clone, Copy)] +pub struct EtherAddr(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(libc::sockaddr_dl); + +impl EtherAddr { + #[cfg(any(target_os = "android", target_os = "linux"))] + pub fn new_checked(sll: libc::sockaddr_ll) -> Option { + Some(EtherAddr(sll)) + } + + #[cfg(any(target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd"))] + pub fn new_checked(sdl: libc::sockaddr_dl) -> Option { + let ether_addr = EtherAddr(sdl); + if ether_addr.is_empty() { + None + } else { + Some(ether_addr) + } + } + + #[cfg(any(target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd"))] + pub fn is_empty(&self) -> bool { + self.nlen() + 5 >= self.max_nlen() + } + + #[cfg(any(target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd"))] + pub fn nlen(&self) -> usize { + self.0.sdl_nlen as usize + } + + #[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 } + + #[cfg(any(target_os = "android", target_os = "linux"))] + 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"))] + 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 + ] + } +} + +impl Eq for EtherAddr {} + +impl PartialEq for EtherAddr { + #[cfg(any(target_os = "android", target_os = "linux"))] + 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 = "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) + } +} + +impl hash::Hash for EtherAddr { + #[cfg(any(target_os = "android", target_os = "linux"))] + 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 = "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); + } +} + +impl fmt::Display for EtherAddr { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let elems = self.addr(); + write!(f, "{:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x}", + elems[0], + elems[1], + elems[2], + elems[3], + elems[4], + elems[5]) + } +} + +impl fmt::Debug for EtherAddr { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fmt::Display::fmt(self, f) + } +}