netlink-packet-route 0.30.0

netlink packet types
Documentation
// SPDX-License-Identifier: MIT

use netlink_packet_core::{
    DecodeError, DefaultNla, Emitable, ErrorContext, Nla, NlaBuffer,
    NlasIterator, Parseable,
};

#[allow(unused)]
use crate::link::{af_spec::VecAfSpecMctp, AfSpecMctp};
use crate::{
    link::{
        af_spec::{VecAfSpecInet, VecAfSpecInet6},
        AfSpecInet, AfSpecInet6,
    },
    AddressFamily,
};

// For `AF_UNSPEC`, the `IFLA_AF_SPEC` is two layer array:
//
// [{nla_len=408, nla_type=IFLA_AF_SPEC},
//     [
//         [{nla_len=140, nla_type=AF_INET}, [
//             {nla_len=136, nla_type=IFLA_INET_CONF}, [
//                 [IPV4_DEVCONF_FORWARDING-1] = 0,
//                 <omitted>
//                 [IPV4_DEVCONF_ARP_EVICT_NOCARRIER-1] = 1]]],
//         [{nla_len=264, nla_type=AF_INET6}, [
//             [{nla_len=8, nla_type=IFLA_INET6_FLAGS}, IF_READY],
//             [{nla_len=20, nla_type=IFLA_INET6_CACHEINFO},
//                 {
//                     max_reasm_len=65535,
//                     tstamp=3794,
//                     reachable_time=37584,
//                     retrans_time=1000}],
//             [{nla_len=232, nla_type=IFLA_INET6_CONF},
//                 [[DEVCONF_FORWARDING] = 0,
//                 <omitted>
//                 [DEVCONF_NDISC_EVICT_NOCARRIER] = 1]]]]]]

#[derive(Clone, Eq, PartialEq, Debug)]
#[non_exhaustive]
pub enum AfSpecUnspec {
    Inet(Vec<AfSpecInet>),
    Inet6(Vec<AfSpecInet6>),
    Mctp(Vec<AfSpecMctp>),
    Other(DefaultNla),
}

pub(crate) struct VecAfSpecUnspec(pub(crate) Vec<AfSpecUnspec>);

impl<'a, T: AsRef<[u8]> + ?Sized> Parseable<NlaBuffer<&'a T>>
    for VecAfSpecUnspec
{
    fn parse(buf: &NlaBuffer<&'a T>) -> Result<Self, DecodeError> {
        let mut nlas = vec![];
        let err = "Invalid NLA for IFLA_AF_SPEC(AF_UNSPEC)";
        for nla in NlasIterator::new(buf.into_inner()) {
            let nla = nla.context(err)?;
            nlas.push(match nla.kind() {
                k if k == u8::from(AddressFamily::Inet) as u16 => {
                    AfSpecUnspec::Inet(
                        VecAfSpecInet::parse(
                            &NlaBuffer::new_checked(&nla.value())
                                .context(err)?,
                        )
                        .context(err)?
                        .0,
                    )
                }
                k if k == u8::from(AddressFamily::Inet6) as u16 => {
                    AfSpecUnspec::Inet6(
                        VecAfSpecInet6::parse(
                            &NlaBuffer::new_checked(&nla.value())
                                .context(err)?,
                        )
                        .context(err)?
                        .0,
                    )
                }
                #[cfg(any(
                    target_os = "linux",
                    target_os = "fuchsia",
                    target_os = "android"
                ))]
                k if k == u8::from(AddressFamily::Mctp) as u16 => {
                    AfSpecUnspec::Mctp(
                        VecAfSpecMctp::parse(
                            &NlaBuffer::new_checked(&nla.value())
                                .context(err)?,
                        )
                        .context(err)?
                        .0,
                    )
                }
                kind => AfSpecUnspec::Other(DefaultNla::parse(&nla).context(
                    format!(
                        "Unknown AF_XXX type {kind} for \
                         IFLA_AF_SPEC(AF_UNSPEC)"
                    ),
                )?),
            })
        }
        Ok(Self(nlas))
    }
}

impl Nla for AfSpecUnspec {
    fn value_len(&self) -> usize {
        match *self {
            Self::Inet(ref nlas) => nlas.as_slice().buffer_len(),
            Self::Inet6(ref nlas) => nlas.as_slice().buffer_len(),
            Self::Mctp(ref nlas) => nlas.as_slice().buffer_len(),
            Self::Other(ref nla) => nla.value_len(),
        }
    }

    fn emit_value(&self, buffer: &mut [u8]) {
        match *self {
            Self::Inet(ref nlas) => nlas.as_slice().emit(buffer),
            Self::Inet6(ref nlas) => nlas.as_slice().emit(buffer),
            Self::Mctp(ref nlas) => nlas.as_slice().emit(buffer),
            Self::Other(ref nla) => nla.emit_value(buffer),
        }
    }

    fn kind(&self) -> u16 {
        match *self {
            Self::Inet(_) => u8::from(AddressFamily::Inet) as u16,
            Self::Inet6(_) => u8::from(AddressFamily::Inet6) as u16,
            Self::Mctp(_) => {
                #[cfg(any(
                    target_os = "linux",
                    target_os = "fuchsia",
                    target_os = "android"
                ))]
                let af = AddressFamily::Mctp;
                #[cfg(not(any(
                    target_os = "linux",
                    target_os = "fuchsia",
                    target_os = "android"
                )))]
                let af = AddressFamily::Unspec;
                u8::from(af) as u16
            }
            Self::Other(ref nla) => nla.kind(),
        }
    }
}