use byteorder::{ByteOrder, NativeEndian};
use crate::{DecodeError, Field, Rest};
const LENGTH: Field = 0..4;
const MESSAGE_TYPE: Field = 4..6;
const FLAGS: Field = 6..8;
const SEQUENCE_NUMBER: Field = 8..12;
const PORT_NUMBER: Field = 12..16;
const PAYLOAD: Rest = 16..;
pub const NETLINK_HEADER_LEN: usize = PAYLOAD.start;
#[rustfmt::skip]
#[derive(Debug, PartialEq, Eq, Clone)]
pub struct NetlinkBuffer<T> {
pub buffer: T,
}
#[rustfmt::skip]
impl<T: AsRef<[u8]>> NetlinkBuffer<T> {
pub fn new(buffer: T) -> NetlinkBuffer<T> {
NetlinkBuffer { buffer }
}
#[rustfmt::skip]
pub fn new_checked(buffer: T) -> Result<NetlinkBuffer<T>, DecodeError> {
let packet = Self::new(buffer);
packet.check_buffer_length()?;
Ok(packet)
}
fn check_buffer_length(&self) -> Result<(), DecodeError> {
let len = self.buffer.as_ref().len();
if len < PORT_NUMBER.end {
Err(format!(
"invalid netlink buffer: length is {} but netlink packets are at least {} bytes",
len, PORT_NUMBER.end
)
.into())
} else if len < self.length() as usize {
Err(format!(
"invalid netlink buffer: length field says {} the buffer is {} bytes long",
self.length(),
len
)
.into())
} else if (self.length() as usize) < PORT_NUMBER.end {
Err(format!(
"invalid netlink buffer: length field says {} but netlink packets are at least {} bytes",
self.length(),
len
).into())
} else {
Ok(())
}
}
pub fn payload_length(&self) -> usize {
let total_length = self.length() as usize;
let payload_offset = PAYLOAD.start;
total_length - payload_offset
}
pub fn into_inner(self) -> T {
self.buffer
}
pub fn length(&self) -> u32 {
let data = self.buffer.as_ref();
NativeEndian::read_u32(&data[LENGTH])
}
pub fn message_type(&self) -> u16 {
let data = self.buffer.as_ref();
NativeEndian::read_u16(&data[MESSAGE_TYPE])
}
pub fn flags(&self) -> u16 {
let data = self.buffer.as_ref();
NativeEndian::read_u16(&data[FLAGS])
}
pub fn sequence_number(&self) -> u32 {
let data = self.buffer.as_ref();
NativeEndian::read_u32(&data[SEQUENCE_NUMBER])
}
pub fn port_number(&self) -> u32 {
let data = self.buffer.as_ref();
NativeEndian::read_u32(&data[PORT_NUMBER])
}
}
impl<T: AsRef<[u8]> + AsMut<[u8]>> NetlinkBuffer<T> {
pub fn set_length(&mut self, value: u32) {
let data = self.buffer.as_mut();
NativeEndian::write_u32(&mut data[LENGTH], value)
}
pub fn set_message_type(&mut self, value: u16) {
let data = self.buffer.as_mut();
NativeEndian::write_u16(&mut data[MESSAGE_TYPE], value)
}
pub fn set_flags(&mut self, value: u16) {
let data = self.buffer.as_mut();
NativeEndian::write_u16(&mut data[FLAGS], value)
}
pub fn set_sequence_number(&mut self, value: u32) {
let data = self.buffer.as_mut();
NativeEndian::write_u32(&mut data[SEQUENCE_NUMBER], value)
}
pub fn set_port_number(&mut self, value: u32) {
let data = self.buffer.as_mut();
NativeEndian::write_u32(&mut data[PORT_NUMBER], value)
}
}
impl<'a, T: AsRef<[u8]> + ?Sized> NetlinkBuffer<&'a T> {
pub fn payload(&self) -> &'a [u8] {
let range = PAYLOAD.start..self.length() as usize;
let data = self.buffer.as_ref();
&data[range]
}
}
impl<'a, T: AsRef<[u8]> + AsMut<[u8]> + ?Sized> NetlinkBuffer<&'a mut T> {
pub fn payload_mut(&mut self) -> &mut [u8] {
let range = PAYLOAD.start..self.length() as usize;
let data = self.buffer.as_mut();
&mut data[range]
}
}
#[cfg(test)]
mod tests {
use crate::{
constants::{NLM_F_MATCH, NLM_F_REQUEST, NLM_F_ROOT},
NetlinkBuffer,
};
const RTM_GETLINK: u16 = 18;
#[rustfmt::skip]
static IP_LINK_SHOW_PKT: [u8; 40] = [
0x28, 0x00, 0x00, 0x00, 0x12, 0x00, 0x01, 0x03, 0x34, 0x0e, 0xf9, 0x5a, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x08, 0x00, 0x1d, 0x00, 0x01, 0x00, 0x00, 0x00];
#[test]
fn packet_read() {
let packet = NetlinkBuffer::new(&IP_LINK_SHOW_PKT[..]);
assert_eq!(packet.length(), 40);
assert_eq!(packet.message_type(), RTM_GETLINK);
assert_eq!(packet.sequence_number(), 1526271540);
assert_eq!(packet.port_number(), 0);
let flags = packet.flags();
assert!(flags & NLM_F_ROOT == NLM_F_ROOT);
assert!(flags & NLM_F_REQUEST == NLM_F_REQUEST);
assert!(flags & NLM_F_MATCH == NLM_F_MATCH);
assert_eq!(flags, NLM_F_ROOT | NLM_F_REQUEST | NLM_F_MATCH);
assert_eq!(packet.payload_length(), 24);
assert_eq!(packet.payload(), &IP_LINK_SHOW_PKT[16..]);
}
#[test]
fn packet_build() {
let mut buf = vec![0; 40];
{
let mut packet = NetlinkBuffer::new(&mut buf);
packet.set_length(40);
packet.set_message_type(RTM_GETLINK);
packet.set_sequence_number(1526271540);
packet.set_port_number(0);
packet.set_flags(From::from(NLM_F_ROOT | NLM_F_REQUEST | NLM_F_MATCH));
packet
.payload_mut()
.copy_from_slice(&IP_LINK_SHOW_PKT[16..]);
}
assert_eq!(&buf[..], &IP_LINK_SHOW_PKT[..]);
}
}