Skip to content

Commit

Permalink
Impl EtherAddr(MAC Addr)
Browse files Browse the repository at this point in the history
  • Loading branch information
luozijun committed Dec 16, 2017
1 parent 4e7a6ec commit d8cbaae
Show file tree
Hide file tree
Showing 2 changed files with 252 additions and 1 deletion.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
251 changes: 250 additions & 1 deletion src/sys/socket/addr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<AddressFamily> {
match family {
Expand All @@ -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
}
}
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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
}
}

Expand Down Expand Up @@ -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,
Expand All @@ -795,6 +842,7 @@ impl SockAddr {
SockAddr::Netlink(NetlinkAddr(ref sa)) => (mem::transmute(sa), mem::size_of::<libc::sockaddr_nl>() as libc::socklen_t),
#[cfg(any(target_os = "ios", target_os = "macos"))]
SockAddr::SysControl(SysControlAddr(ref sa)) => (mem::transmute(sa), mem::size_of::<sys_control::sockaddr_ctl>() as libc::socklen_t),
SockAddr::Ether(_) => unimplemented!()
}
}
}
Expand All @@ -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,
}
}
Expand All @@ -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)
}
}
}
Expand All @@ -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)
}
}
}
Expand Down Expand Up @@ -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<EtherAddr> {
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<EtherAddr> {
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<H: hash::Hasher>(&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<H: hash::Hasher>(&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<H: hash::Hasher>(&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<H: hash::Hasher>(&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)
}
}

0 comments on commit d8cbaae

Please sign in to comment.