Skip to main content

netlink_packet_route/neighbour_discovery_user_option/
message.rs

1// SPDX-License-Identifier: MIT
2
3use netlink_packet_utils::nla::HasNlas;
4use netlink_packet_utils::traits::{Emitable, Parseable, ParseableParametrized};
5use std::convert::TryFrom as _;
6
7use super::NeighbourDiscoveryUserOptionError;
8use super::buffer::{
9    NEIGHBOUR_DISCOVERY_USER_OPTION_HEADER_LEN, NeighbourDiscoveryUserOptionMessageBuffer,
10};
11use super::header::NeighbourDiscoveryUserOptionHeader;
12use super::nla::Nla;
13use crate::RouteNetlinkMessageParseMode;
14
15#[derive(Debug, PartialEq, Eq, Clone)]
16#[non_exhaustive]
17pub struct NeighbourDiscoveryUserOptionMessage {
18    /// The header of the ND_USEROPT message.
19    pub header: NeighbourDiscoveryUserOptionHeader,
20
21    /// The body of the NDP option as it was on the wire.
22    pub option_body: Vec<u8>,
23
24    pub attributes: Vec<Nla>,
25}
26
27impl NeighbourDiscoveryUserOptionMessage {
28    pub fn new(
29        header: NeighbourDiscoveryUserOptionHeader,
30        option_body: Vec<u8>,
31        attributes: Vec<Nla>,
32    ) -> Self {
33        Self { header, option_body, attributes }
34    }
35}
36
37impl Emitable for NeighbourDiscoveryUserOptionMessage {
38    fn buffer_len(&self) -> usize {
39        NEIGHBOUR_DISCOVERY_USER_OPTION_HEADER_LEN
40            + self.option_body.len()
41            + self.attributes.as_slice().buffer_len()
42    }
43
44    fn emit(&self, buffer: &mut [u8]) {
45        let Self {
46            header: NeighbourDiscoveryUserOptionHeader { interface_index, icmp_type },
47            option_body,
48            attributes,
49        } = self;
50
51        let mut packet = NeighbourDiscoveryUserOptionMessageBuffer::new_unchecked(buffer);
52
53        packet.set_address_family(icmp_type.family().into());
54
55        let payload = packet.payload_mut();
56        payload[..option_body.len()].copy_from_slice(&option_body[..]);
57        attributes.as_slice().emit(&mut payload[option_body.len()..]);
58
59        packet.set_options_length(
60            u16::try_from(option_body.len())
61                .expect("neighbor discovery options length doesn't fit in u16"),
62        );
63        packet.set_interface_index(*interface_index);
64
65        let (icmp_type, icmp_code) = icmp_type.into_type_and_code();
66        packet.set_icmp_type(icmp_type);
67        packet.set_icmp_code(icmp_code);
68    }
69}
70
71impl<'a, T: AsRef<[u8]> + 'a>
72    ParseableParametrized<
73        NeighbourDiscoveryUserOptionMessageBuffer<&'a T>,
74        RouteNetlinkMessageParseMode,
75    > for NeighbourDiscoveryUserOptionMessage
76{
77    type Error = NeighbourDiscoveryUserOptionError;
78
79    fn parse_with_param(
80        buf: &NeighbourDiscoveryUserOptionMessageBuffer<&'a T>,
81        mode: RouteNetlinkMessageParseMode,
82    ) -> Result<Self, NeighbourDiscoveryUserOptionError> {
83        let header = NeighbourDiscoveryUserOptionHeader::parse(buf)
84            .map_err(NeighbourDiscoveryUserOptionError::InvalidHeader)?;
85
86        let attributes = buf
87            .parse_attributes(mode.into(), Nla::parse)
88            .map_err(NeighbourDiscoveryUserOptionError::InvalidNla)?;
89
90        Ok(NeighbourDiscoveryUserOptionMessage {
91            header,
92            option_body: buf.option_body().to_vec(),
93            attributes,
94        })
95    }
96}