netlink-packet 0.1.1

netlink packet types
Documentation
mod metrics;

pub use self::metrics::RouteMetricsNla;
use byteorder::{ByteOrder, NativeEndian};
use failure::ResultExt;
use std::mem::size_of;

use crate::constants::*;
use crate::utils::{parse_u16, parse_u32};
use crate::{DecodeError, DefaultNla, Emitable, Nla, NlaBuffer, Parseable};

#[derive(Debug, Clone, Copy, Eq, PartialEq)]
pub struct RouteCacheInfo {
    pub clntref: u32,
    pub last_use: u32,
    pub expires: u32,
    pub error: u32,
    pub used: u32,
    pub id: u32,
    pub ts: u32,
    pub ts_age: u32,
}

const ROUTE_CACHE_INFO_LEN: usize = 4 * 8;

impl RouteCacheInfo {
    fn from_bytes(buf: &[u8]) -> Result<Self, DecodeError> {
        if buf.len() < ROUTE_CACHE_INFO_LEN {
            return Err(DecodeError::from(format!(
                "RTA_CACHEINFO is {} bytes, buffer is only {} bytes: {:#x?}",
                ROUTE_CACHE_INFO_LEN,
                buf.len(),
                buf
            )));
        }
        Ok(RouteCacheInfo {
            clntref: NativeEndian::read_u32(&buf[0..4]),
            last_use: NativeEndian::read_u32(&buf[4..8]),
            expires: NativeEndian::read_u32(&buf[8..12]),
            error: NativeEndian::read_u32(&buf[12..16]),
            used: NativeEndian::read_u32(&buf[16..20]),
            id: NativeEndian::read_u32(&buf[20..24]),
            ts: NativeEndian::read_u32(&buf[24..28]),
            ts_age: NativeEndian::read_u32(&buf[28..32]),
        })
    }

    fn to_bytes(&self, buf: &mut [u8]) -> Result<(), DecodeError> {
        if buf.len() < ROUTE_CACHE_INFO_LEN {
            return Err(DecodeError::from(format!(
                "buffer is only {} long, but RTA_CACHEINFO is {} bytes",
                buf.len(),
                ROUTE_CACHE_INFO_LEN,
            )));
        }
        NativeEndian::write_u32(&mut buf[0..4], self.clntref);
        NativeEndian::write_u32(&mut buf[4..8], self.last_use);
        NativeEndian::write_u32(&mut buf[8..12], self.expires);
        NativeEndian::write_u32(&mut buf[12..16], self.error);
        NativeEndian::write_u32(&mut buf[16..20], self.used);
        NativeEndian::write_u32(&mut buf[20..24], self.id);
        NativeEndian::write_u32(&mut buf[24..28], self.ts);
        NativeEndian::write_u32(&mut buf[28..32], self.ts_age);
        Ok(())
    }
}

#[derive(Debug, Clone, Copy, Eq, PartialEq)]
pub struct RouteMfcStats {
    pub packets: u64,
    pub bytes: u64,
    pub wrong_if: u64,
}

const ROUTE_MFC_STATS_LEN: usize = 3 * 8;

impl RouteMfcStats {
    fn from_bytes(buf: &[u8]) -> Result<Self, DecodeError> {
        if buf.len() < ROUTE_MFC_STATS_LEN {
            return Err(DecodeError::from(format!(
                "RTA_MFC_STATS is {} bytes, buffer is only {} bytes: {:#x?}",
                ROUTE_MFC_STATS_LEN,
                buf.len(),
                buf
            )));
        }
        Ok(RouteMfcStats {
            packets: NativeEndian::read_u64(&buf[0..8]),
            bytes: NativeEndian::read_u64(&buf[8..16]),
            wrong_if: NativeEndian::read_u64(&buf[16..24]),
        })
    }

    fn to_bytes(&self, buf: &mut [u8]) -> Result<(), DecodeError> {
        if buf.len() < ROUTE_MFC_STATS_LEN {
            return Err(DecodeError::from(format!(
                "buffer is only {} long, but RTA_CACHEINFO is {} bytes",
                buf.len(),
                ROUTE_MFC_STATS_LEN,
            )));
        }
        NativeEndian::write_u64(&mut buf[0..8], self.packets);
        NativeEndian::write_u64(&mut buf[8..16], self.bytes);
        NativeEndian::write_u64(&mut buf[16..24], self.wrong_if);
        Ok(())
    }
}

#[derive(Debug, PartialEq, Eq, Clone)]
pub enum RouteNla {
    Unspec(Vec<u8>),
    Destination(Vec<u8>),
    Source(Vec<u8>),
    Gateway(Vec<u8>),
    PrefSource(Vec<u8>),
    Metrics(RouteMetricsNla),
    MultiPath(Vec<u8>),
    CacheInfo(RouteCacheInfo),
    Session(Vec<u8>),
    MpAlgo(Vec<u8>),
    MfcStats(RouteMfcStats),
    Via(Vec<u8>),
    NewDestination(Vec<u8>),
    Pref(Vec<u8>),
    Encap(Vec<u8>),
    Expires(Vec<u8>),
    Pad(Vec<u8>),
    Uid(Vec<u8>),
    TtlPropagate(Vec<u8>),
    EncapType(u16),
    Iif(u32),
    Oif(u32),
    Priority(u32),
    ProtocolInfo(u32),
    Flow(u32),
    Table(u32),
    Mark(u32),
    Other(DefaultNla),
}

impl Nla for RouteNla {
    #[rustfmt::skip]
    fn value_len(&self) -> usize {
        use self::RouteNla::*;
        match *self {
            Unspec(ref bytes)
                | Destination(ref bytes)
                | Source(ref bytes)
                | Gateway(ref bytes)
                | PrefSource(ref bytes)
                | MultiPath(ref bytes)
                | Session(ref bytes)
                | MpAlgo(ref bytes)
                | Via(ref bytes)
                | NewDestination(ref bytes)
                | Pref(ref bytes)
                | Encap(ref bytes)
                | Expires(ref bytes)
                | Pad(ref bytes)
                | Uid(ref bytes)
                | TtlPropagate(ref bytes)
                => bytes.len(),

            EncapType(_) => size_of::<u16>(),
            Iif(_)
                | Oif(_)
                | Priority(_)
                | ProtocolInfo(_)
                | Flow(_)
                | Table(_)
                | Mark(_)
                => size_of::<u32>(),

            CacheInfo(_) => size_of::<RouteCacheInfo>(),
            MfcStats(_) => size_of::<RouteMfcStats>(),
            Metrics(ref attr) => attr.buffer_len(),
            Other(ref attr) => attr.value_len(),
        }
    }

    #[rustfmt::skip]
    fn emit_value(&self, buffer: &mut [u8]) {
        use self::RouteNla::*;
        match *self {
            Unspec(ref bytes)
                | Destination(ref bytes)
                | Source(ref bytes)
                | Gateway(ref bytes)
                | PrefSource(ref bytes)
                | MultiPath(ref bytes)
                | Session(ref bytes)
                | MpAlgo(ref bytes)
                | Via(ref bytes)
                | NewDestination(ref bytes)
                | Pref(ref bytes)
                | Encap(ref bytes)
                | Expires(ref bytes)
                | Pad(ref bytes)
                | Uid(ref bytes)
                | TtlPropagate(ref bytes)
                => buffer.copy_from_slice(bytes.as_slice()),
            EncapType(value) => NativeEndian::write_u16(buffer, value),
            Iif(value)
                | Oif(value)
                | Priority(value)
                | ProtocolInfo(value)
                | Flow(value)
                | Table(value)
                | Mark(value)
                => NativeEndian::write_u32(buffer, value),
            CacheInfo(ref cache_info) => cache_info.to_bytes(buffer).expect("check the buffer length before calling emit_value()!"),
            MfcStats(ref mfc_stats) => mfc_stats.to_bytes(buffer).expect("check the buffer length before calling emit_value()!"),
            Metrics(ref attr) => attr.emit(buffer),
            Other(ref attr) => attr.emit_value(buffer),
        }
    }

    fn kind(&self) -> u16 {
        use self::RouteNla::*;
        match *self {
            Unspec(_) => RTA_UNSPEC,
            Destination(_) => RTA_DST,
            Source(_) => RTA_SRC,
            Iif(_) => RTA_IIF,
            Oif(_) => RTA_OIF,
            Gateway(_) => RTA_GATEWAY,
            Priority(_) => RTA_PRIORITY,
            PrefSource(_) => RTA_PREFSRC,
            Metrics(_) => RTA_METRICS,
            MultiPath(_) => RTA_MULTIPATH,
            ProtocolInfo(_) => RTA_PROTOINFO,
            Flow(_) => RTA_FLOW,
            CacheInfo(_) => RTA_CACHEINFO,
            Session(_) => RTA_SESSION,
            MpAlgo(_) => RTA_MP_ALGO,
            Table(_) => RTA_TABLE,
            Mark(_) => RTA_MARK,
            MfcStats(_) => RTA_MFC_STATS,
            Via(_) => RTA_VIA,
            NewDestination(_) => RTA_NEWDST,
            Pref(_) => RTA_PREF,
            EncapType(_) => RTA_ENCAP_TYPE,
            Encap(_) => RTA_ENCAP,
            Expires(_) => RTA_EXPIRES,
            Pad(_) => RTA_PAD,
            Uid(_) => RTA_UID,
            TtlPropagate(_) => RTA_TTL_PROPAGATE,
            Other(ref attr) => attr.kind(),
        }
    }
}

impl<'buffer, T: AsRef<[u8]> + ?Sized> Parseable<RouteNla> for NlaBuffer<&'buffer T> {
    fn parse(&self) -> Result<RouteNla, DecodeError> {
        use self::RouteNla::*;
        let payload = self.value();
        Ok(match self.kind() {
            RTA_UNSPEC => Unspec(payload.to_vec()),
            RTA_DST => Destination(payload.to_vec()),
            RTA_SRC => Source(payload.to_vec()),
            RTA_GATEWAY => Gateway(payload.to_vec()),
            RTA_PREFSRC => PrefSource(payload.to_vec()),
            RTA_MULTIPATH => MultiPath(payload.to_vec()),
            RTA_SESSION => Session(payload.to_vec()),
            RTA_MP_ALGO => MpAlgo(payload.to_vec()),
            RTA_VIA => Via(payload.to_vec()),
            RTA_NEWDST => NewDestination(payload.to_vec()),
            RTA_PREF => Pref(payload.to_vec()),
            RTA_ENCAP => Encap(payload.to_vec()),
            RTA_EXPIRES => Expires(payload.to_vec()),
            RTA_PAD => Pad(payload.to_vec()),
            RTA_UID => Uid(payload.to_vec()),
            RTA_TTL_PROPAGATE => TtlPropagate(payload.to_vec()),
            RTA_ENCAP_TYPE => {
                EncapType(parse_u16(payload).context("invalid RTA_ENCAP_TYPE value")?)
            }
            RTA_IIF => Iif(parse_u32(payload).context("invalid RTA_IIF value")?),
            RTA_OIF => Oif(parse_u32(payload).context("invalid RTA_OIF value")?),
            RTA_PRIORITY => Priority(parse_u32(payload).context("invalid RTA_PRIORITY value")?),
            RTA_PROTOINFO => {
                ProtocolInfo(parse_u32(payload).context("invalid RTA_PROTOINFO value")?)
            }
            RTA_FLOW => Flow(parse_u32(payload).context("invalid RTA_FLOW value")?),
            RTA_TABLE => Table(parse_u32(payload).context("invalid RTA_TABLE value")?),
            RTA_MARK => Mark(parse_u32(payload).context("invalid RTA_MARK value")?),
            RTA_CACHEINFO => CacheInfo(
                RouteCacheInfo::from_bytes(payload).context("invalid RTA_CACHEINFO value")?,
            ),
            RTA_MFC_STATS => {
                MfcStats(RouteMfcStats::from_bytes(payload).context("invalid RTA_MFC_STATS value")?)
            }
            RTA_METRICS => Metrics(
                NlaBuffer::new_checked(payload)
                    .context("invalid RTA_METRICS value")?
                    .parse()
                    .context("invalid RTA_METRICS value")?,
            ),
            _ => Other(
                <Self as Parseable<DefaultNla>>::parse(self)
                    .context("invalid NLA (unknown kind)")?,
            ),
        })
    }
}