ethtool/
header.rs

1// SPDX-License-Identifier: MIT
2
3use std::ffi::CString;
4
5use anyhow::Context;
6use byteorder::{ByteOrder, NativeEndian};
7use netlink_packet_utils::{
8    nla::{DefaultNla, Nla, NlaBuffer},
9    parsers::{parse_string, parse_u32},
10    DecodeError, Parseable,
11};
12
13const ALTIFNAMSIZ: usize = 128;
14const ETHTOOL_A_HEADER_DEV_INDEX: u16 = 1;
15const ETHTOOL_A_HEADER_DEV_NAME: u16 = 2;
16const ETHTOOL_A_HEADER_FLAGS: u16 = 3;
17
18#[derive(Debug, PartialEq, Eq, Clone)]
19pub enum EthtoolHeader {
20    DevIndex(u32),
21    DevName(String),
22    Flags(u32),
23    Other(DefaultNla),
24}
25
26impl Nla for EthtoolHeader {
27    fn value_len(&self) -> usize {
28        match self {
29            Self::DevIndex(_) | Self::Flags(_) => 4,
30            Self::DevName(s) => {
31                if s.len() + 1 > ALTIFNAMSIZ {
32                    ALTIFNAMSIZ
33                } else {
34                    s.len() + 1
35                }
36            }
37            Self::Other(attr) => attr.value_len(),
38        }
39    }
40
41    fn kind(&self) -> u16 {
42        match self {
43            Self::DevIndex(_) => ETHTOOL_A_HEADER_DEV_INDEX,
44            Self::DevName(_) => ETHTOOL_A_HEADER_DEV_NAME,
45            Self::Flags(_) => ETHTOOL_A_HEADER_FLAGS,
46            Self::Other(attr) => attr.kind(),
47        }
48    }
49
50    fn emit_value(&self, buffer: &mut [u8]) {
51        match self {
52            Self::DevIndex(value) | Self::Flags(value) => {
53                NativeEndian::write_u32(buffer, *value)
54            }
55            Self::DevName(s) => {
56                str_to_zero_ended_u8_array(s, buffer, ALTIFNAMSIZ)
57            }
58            Self::Other(ref attr) => attr.emit_value(buffer),
59        }
60    }
61}
62
63impl<'a, T: AsRef<[u8]> + ?Sized> Parseable<NlaBuffer<&'a T>>
64    for EthtoolHeader
65{
66    fn parse(buf: &NlaBuffer<&'a T>) -> Result<Self, DecodeError> {
67        let payload = buf.value();
68        Ok(match buf.kind() {
69            ETHTOOL_A_HEADER_DEV_INDEX => Self::DevIndex(
70                parse_u32(payload)
71                    .context("invalid ETHTOOL_A_HEADER_DEV_INDEX value")?,
72            ),
73            ETHTOOL_A_HEADER_FLAGS => Self::Flags(
74                parse_u32(payload)
75                    .context("invalid ETHTOOL_A_HEADER_FLAGS value")?,
76            ),
77            ETHTOOL_A_HEADER_DEV_NAME => Self::DevName(
78                parse_string(payload)
79                    .context("invalid ETHTOOL_A_HEADER_DEV_NAME value")?,
80            ),
81            _ => Self::Other(
82                DefaultNla::parse(buf).context("invalid NLA (unknown kind)")?,
83            ),
84        })
85    }
86}
87
88fn str_to_zero_ended_u8_array(
89    src_str: &str,
90    buffer: &mut [u8],
91    max_size: usize,
92) {
93    if let Ok(src_cstring) = CString::new(src_str.as_bytes()) {
94        let src_null_ended_str = src_cstring.into_bytes_with_nul();
95        if src_null_ended_str.len() > max_size {
96            buffer[..max_size].clone_from_slice(&src_null_ended_str[..max_size])
97        } else {
98            buffer[..src_null_ended_str.len()]
99                .clone_from_slice(&src_null_ended_str)
100        }
101    }
102}