1use 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}