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..de99ba24c1 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,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 sll = sockaddr_ll::wrap(*(addr as *const libc::sockaddr_ll)); + match sll.to_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 sdl = sockaddr_dl::wrap(*(addr as *const libc::sockaddr_dl)); + match sdl.to_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, @@ -795,6 +844,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 +862,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 +882,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 +902,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 +1071,287 @@ pub mod sys_control { } } } + + +#[derive(Clone, Copy, Eq, Hash, PartialEq)] +pub struct EtherAddr { + pub a: u8, + pub b: u8, + pub c: u8, + pub d: u8, + pub e: u8, + pub f: u8, +} + +impl EtherAddr { + pub fn new(a: u8, b: u8, c: u8, d: u8, e: u8, f: u8) -> EtherAddr { + EtherAddr { + a: a, + b: b, + c: c, + d: d, + e: e, + f: f, + } + } +} + +impl fmt::Display for EtherAddr { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x}", + self.a, + self.b, + self.c, + self.d, + self.e, + self.f) + } +} + +impl fmt::Debug for EtherAddr { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fmt::Display::fmt(self, f) + } +} + + +#[cfg(any(target_os = "android", target_os = "linux"))] +#[derive(Clone, Copy)] +pub struct sockaddr_ll(pub libc::sockaddr_ll); + +#[cfg(any(target_os = "android", target_os = "linux"))] +impl sockaddr_ll { + pub fn wrap(sll: libc::sockaddr_ll) -> sockaddr_ll { + sockaddr_ll(sll) + } + + pub fn to_ether_addr(&self) -> Option { + Some(EtherAddr::new(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 = "android", target_os = "linux"))] +impl Eq for sockaddr_ll {} + +#[cfg(any(target_os = "android", target_os = "linux"))] +impl PartialEq for sockaddr_ll { + 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 sockaddr_ll { + 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 = "android", target_os = "linux"))] +impl fmt::Debug for sockaddr_ll { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let _ = write!(f, "sockaddr_ll {{ sll_family: {:?}, sll_protocol: {:?}, sll_ifindex: {:?}, ", + self.0.sll_family, + self.0.sll_protocol, + self.0.sll_ifindex); + write!(f, "sll_hatype: {:?}, sll_pkttype: {:?}, sll_halen: {:?}, sll_addr: {:?} }}", + self.0.sll_hatype, + self.0.sll_pkttype, + self.0.sll_halen, + self.0.sll_addr) + } +} + + +#[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 sockaddr_dl(pub libc::sockaddr_dl); + +#[cfg(any(target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd"))] +impl sockaddr_dl { + pub fn wrap(sdl: libc::sockaddr_dl) -> sockaddr_dl { + sockaddr_dl(sdl) + } + + pub fn to_ether_addr(&self) -> Option { + let nlen = self.nlen(); + let max_nlen = self.max_nlen(); + if self.nlen() + 5 >= max_nlen { + None + } else { + let a = self.0.sdl_data[nlen ] as u8; + let b = self.0.sdl_data[nlen + 1] as u8; + let c = self.0.sdl_data[nlen + 2] as u8; + let d = self.0.sdl_data[nlen + 3] as u8; + let e = self.0.sdl_data[nlen + 4] as u8; + let f = self.0.sdl_data[nlen + 5] as u8; + Some(EtherAddr::new(a, b, c, d, e, f)) + } + } + + 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 } +} + +#[cfg(any(target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd"))] +impl Eq for sockaddr_dl {} + +#[cfg(any(target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd"))] +impl PartialEq for sockaddr_dl { + #[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 sockaddr_dl { + #[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"))] +impl fmt::Debug for sockaddr_dl { + #[cfg(any(target_os = "macos", + target_os = "ios", + target_os = "netbsd", + target_os = "openbsd", + target_os = "freebsd"))] + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let _ = write!(f, "sockaddr_dl {{ sdl_len: {:?}, sdl_family: {:?}, sdl_index: {:?}, ", + self.0.sdl_len, + self.0.sdl_family, + self.0.sdl_index); + let _ = write!(f, "sdl_type: {:?}, sdl_nlen: {:?}, sdl_alen: {:?}, sdl_slen: {:?}, ", + self.0.sdl_type, + self.0.sdl_nlen, + self.0.sdl_alen, + self.0.sdl_slen); + write!(f, "sdl_data: {:?} }}", &self.0.sdl_data[..]) + } + + #[cfg(target_os = "dragonfly")] + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let _ = write!(f, "sockaddr_dl {{ sdl_len: {:?}, sdl_family: {:?}, sdl_index: {:?}, ", + self.0.sdl_len, + self.0.sdl_family, + self.0.sdl_index); + let _ = write!(f, "sdl_type: {:?}, sdl_nlen: {:?}, sdl_alen: {:?}, sdl_slen: {:?}, ", + self.0.sdl_type, + self.0.sdl_nlen, + self.0.sdl_alen, + self.0.sdl_slen); + write!(f, "sdl_data: {:?}, sdl_rcf: {:?}, sdl_route: {:?} }}", + &self.0.sdl_data[..], + self.0.sdl_rcf, + self.0.sdl_route) + } +} +