use std::{fmt, marker::PhantomData, mem, slice};
use errno::Errno;
use libc;
use {Attr, CbResult, CbStatus, Result};
#[repr(C)]
pub struct Msghdr<'a> {
pub nlmsg_len: u32,
pub nlmsg_type: u16,
pub nlmsg_flags: u16,
pub nlmsg_seq: u32,
pub nlmsg_pid: u32,
_buf: PhantomData<&'a [u8]>,
}
impl<'a> Msghdr<'a> {
pub const HDRLEN: usize = (mem::size_of::<Self>() + crate::ALIGNTO - 1) & !(crate::ALIGNTO - 1);
pub fn size<T>() -> usize {
mem::size_of::<T>() + Self::HDRLEN
}
pub fn payload_len(&self) -> u32 {
self.nlmsg_len - Self::HDRLEN as u32
}
unsafe fn payload_raw<T>(&self) -> &'a T {
&*((self as *const _ as *const u8).offset(Self::HDRLEN as isize) as *const T)
}
pub fn payload<T>(&self) -> Result<&'a T> {
if crate::align(Self::size::<T>()) > self.nlmsg_len as usize {
Err(Errno(libc::ENODATA))
} else {
Ok(unsafe { self.payload_raw::<T>() })
}
}
pub unsafe fn payload_offset<T>(&self, offset: usize) -> &'a T {
&*((self as *const _ as *const u8)
.offset(Self::HDRLEN as isize + crate::align(offset) as isize) as *const _
as *const T)
}
pub fn ok(&self, len: isize) -> bool {
len >= Self::HDRLEN as isize
&& self.nlmsg_len as usize >= Self::HDRLEN
&& self.nlmsg_len as isize <= len
}
pub unsafe fn next(&self, len: &mut isize) -> &Self {
*len -= crate::align(self.nlmsg_len as usize) as isize;
&*((self as *const _ as *const u8).offset(self.nlmsg_len as isize) as *const _
as *const Self)
}
pub unsafe fn payload_tail<T>(&self) -> *const T {
(self as *const _ as *const u8).offset(crate::align(self.nlmsg_len as usize) as isize)
as *const T
}
pub fn seq_ok(&self, seq: u32) -> Result<()> {
if self.nlmsg_seq != 0 && seq != 0 && self.nlmsg_seq != seq {
return Err(Errno(libc::EPROTO));
}
Ok(())
}
pub fn portid_ok(&self, portid: u32) -> Result<()> {
if self.nlmsg_pid != 0 && portid != 0 && self.nlmsg_pid != portid {
return Err(Errno(libc::ESRCH));
}
Ok(())
}
pub fn parse<T: FnMut(&'a Attr<'a>) -> CbResult>(&self, offset: usize, mut cb: T) -> CbResult {
let mut ret: CbResult = crate::gen_errno!(libc::ENOENT);
let mut attr = unsafe { self.payload_offset::<Attr>(offset) };
loop {
if !attr.ok(unsafe {
self.payload_tail::<u8>() as libc::intptr_t
- attr as *const _ as *const u8 as libc::intptr_t
}) {
return ret;
}
ret = cb(attr);
match ret {
Ok(CbStatus::Ok) => {}
_ => return ret,
}
attr = unsafe { attr.next() };
}
}
}
impl<'a> Msghdr<'a> {
fn fmt_header(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "----------------\t------------------\n")?;
write!(f, "| {:^010} |\t| message length |\n", self.nlmsg_len)?;
write!(
f,
"| {:^05} | {}{}{}{} |\t| type | flags |\n",
self.nlmsg_type,
if self.nlmsg_flags & libc::NLM_F_REQUEST as u16 != 0 {
'R'
} else {
'-'
},
if self.nlmsg_flags & libc::NLM_F_MULTI as u16 != 0 {
'M'
} else {
'-'
},
if self.nlmsg_flags & libc::NLM_F_ACK as u16 != 0 {
'A'
} else {
'-'
},
if self.nlmsg_flags & libc::NLM_F_ECHO as u16 != 0 {
'E'
} else {
'-'
}
)?;
write!(f, "| {:^010} |\t| sequence number|\n", self.nlmsg_seq)?;
write!(f, "| {:^010} |\t| port ID |\n", self.nlmsg_pid)?;
write!(f, "----------------\t------------------\n")
}
unsafe fn fmt_payload(&self, f: &mut fmt::Formatter) -> fmt::Result {
let mut extra_header_size = f.precision().unwrap_or(0);
let mut rem = 0isize;
let b = self as *const _ as *const u8;
for ii in mem::size_of::<Self>() / 4..(self.nlmsg_len / 4) as usize {
let buf = slice::from_raw_parts(self as *const _ as *const u8, self.nlmsg_len as usize);
let i = ii * 4;
let attr = &*(b.offset(i as isize) as *const _ as *const Attr);
if self.nlmsg_type < libc::NLMSG_MIN_TYPE as u16 {
write!(
f,
"| {:2x} {:2x} {:2x} {:2x} |\t",
0xff & buf[i],
0xff & buf[i + 1],
0xff & buf[i + 2],
0xff & buf[i + 4]
)?;
write!(f, "| |\n")?;
} else if extra_header_size > 0 {
extra_header_size -= 4;
write!(
f,
"| {:2x} {:2x} {:2x} {:2x} |\t",
0xff & buf[i],
0xff & buf[i + 1],
0xff & buf[i + 2],
0xff & buf[i + 3]
)?;
write!(f, "| extra header |\n")?;
} else if rem == 0 && (attr.nla_type & libc::NLA_TYPE_MASK as u16 != 0) {
write!(
f,
"|{}[{};{}m\
{:5}\
{}[{}m\
|\
{}[{};{}m\
{}{}\
{}[{}m\
|\
{}[{};{}m\
{:5}\
{}[{}m|\t",
'\x1b',
1,
31,
attr.nla_len,
'\x1b',
0,
'\x1b',
1,
32,
if attr.nla_type & libc::NLA_F_NESTED as u16 != 0 {
'N'
} else {
'-'
},
if attr.nla_type & libc::NLA_F_NET_BYTEORDER as u16 != 0 {
'B'
} else {
'-'
},
'\x1b',
0,
'\x1b',
1,
34,
attr.nla_type & libc::NLA_TYPE_MASK as u16,
'\x1b',
0
)?;
write!(f, "|len |flags| type|\n")?;
if attr.nla_type & libc::NLA_F_NESTED as u16 == 0 {
rem = crate::align(attr.nla_len as usize) as isize - Attr::HDRLEN as isize;
}
} else if rem > 0 {
rem -= 4;
write!(
f,
"| {:2x} {:2x} {:2x} {:2x} |\t",
0xff & buf[i],
0xff & buf[i + 1],
0xff & buf[i + 2],
0xff & buf[i + 3]
)?;
write!(f, "| data |")?;
let mut c: char;
write!(
f,
"\t {} {} {} {}\n",
{
c = buf[i].into();
if c.is_ascii_graphic() {
c
} else {
' '
}
},
{
c = buf[i + 1].into();
if c.is_ascii_graphic() {
c
} else {
' '
}
},
{
c = buf[i + 2].into();
if c.is_ascii_graphic() {
c
} else {
' '
}
},
{
c = buf[i + 3].into();
if c.is_ascii_graphic() {
c
} else {
' '
}
}
)?;
}
}
write!(f, "----------------\t------------------\n")
}
}
impl<'a> fmt::Debug for Msghdr<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
unsafe {
self.fmt_header(f)?;
self.fmt_payload(f)?;
}
Ok(())
}
}