ethtool/
message.rs

1// SPDX-License-Identifier: MIT
2
3use netlink_packet_generic::{GenlFamily, GenlHeader};
4use netlink_packet_utils::{
5    nla::Nla, DecodeError, Emitable, ParseableParametrized,
6};
7
8use crate::{
9    channel::{parse_channel_nlas, EthtoolChannelAttr},
10    coalesce::{parse_coalesce_nlas, EthtoolCoalesceAttr},
11    feature::{parse_feature_nlas, EthtoolFeatureAttr},
12    link_mode::{parse_link_mode_nlas, EthtoolLinkModeAttr},
13    pause::{parse_pause_nlas, EthtoolPauseAttr},
14    ring::{parse_ring_nlas, EthtoolRingAttr},
15    tsinfo::{parse_tsinfo_nlas, EthtoolTsInfoAttr},
16    EthtoolHeader,
17};
18
19const ETHTOOL_MSG_PAUSE_GET: u8 = 21;
20const ETHTOOL_MSG_PAUSE_GET_REPLY: u8 = 22;
21const ETHTOOL_MSG_FEATURES_GET: u8 = 11;
22const ETHTOOL_MSG_FEATURES_GET_REPLY: u8 = 11;
23const ETHTOOL_MSG_LINKMODES_GET: u8 = 4;
24const ETHTOOL_MSG_LINKMODES_GET_REPLY: u8 = 4;
25const ETHTOOL_MSG_RINGS_GET: u8 = 15;
26const ETHTOOL_MSG_RINGS_GET_REPLY: u8 = 16;
27const ETHTOOL_MSG_COALESCE_GET: u8 = 19;
28const ETHTOOL_MSG_COALESCE_GET_REPLY: u8 = 20;
29const ETHTOOL_MSG_TSINFO_GET: u8 = 25;
30const ETHTOOL_MSG_TSINFO_GET_REPLY: u8 = 26;
31const ETHTOOL_MSG_CHANNELS_GET: u8 = 17;
32const ETHTOOL_MSG_CHANNELS_GET_REPLY: u8 = 18;
33const ETHTOOL_MSG_CHANNELS_SET: u8 = 18;
34
35#[derive(Debug, PartialEq, Eq, Clone, Copy)]
36pub enum EthtoolCmd {
37    PauseGet,
38    PauseGetReply,
39    FeatureGet,
40    FeatureGetReply,
41    LinkModeGet,
42    LinkModeGetReply,
43    RingGet,
44    RingGetReply,
45    CoalesceGet,
46    CoalesceGetReply,
47    TsInfoGet,
48    TsInfoGetReply,
49    ChannelGet,
50    ChannelGetReply,
51    ChannelSet,
52}
53
54impl From<EthtoolCmd> for u8 {
55    fn from(cmd: EthtoolCmd) -> Self {
56        match cmd {
57            EthtoolCmd::PauseGet => ETHTOOL_MSG_PAUSE_GET,
58            EthtoolCmd::PauseGetReply => ETHTOOL_MSG_PAUSE_GET_REPLY,
59            EthtoolCmd::FeatureGet => ETHTOOL_MSG_FEATURES_GET,
60            EthtoolCmd::FeatureGetReply => ETHTOOL_MSG_FEATURES_GET_REPLY,
61            EthtoolCmd::LinkModeGet => ETHTOOL_MSG_LINKMODES_GET,
62            EthtoolCmd::LinkModeGetReply => ETHTOOL_MSG_LINKMODES_GET_REPLY,
63            EthtoolCmd::RingGet => ETHTOOL_MSG_RINGS_GET,
64            EthtoolCmd::RingGetReply => ETHTOOL_MSG_RINGS_GET_REPLY,
65            EthtoolCmd::CoalesceGet => ETHTOOL_MSG_COALESCE_GET,
66            EthtoolCmd::CoalesceGetReply => ETHTOOL_MSG_COALESCE_GET_REPLY,
67            EthtoolCmd::TsInfoGet => ETHTOOL_MSG_TSINFO_GET,
68            EthtoolCmd::TsInfoGetReply => ETHTOOL_MSG_TSINFO_GET_REPLY,
69            EthtoolCmd::ChannelGet => ETHTOOL_MSG_CHANNELS_GET,
70            EthtoolCmd::ChannelGetReply => ETHTOOL_MSG_CHANNELS_GET_REPLY,
71            EthtoolCmd::ChannelSet => ETHTOOL_MSG_CHANNELS_SET,
72        }
73    }
74}
75
76#[derive(Debug, PartialEq, Eq, Clone)]
77pub enum EthtoolAttr {
78    Pause(EthtoolPauseAttr),
79    Feature(EthtoolFeatureAttr),
80    LinkMode(EthtoolLinkModeAttr),
81    Ring(EthtoolRingAttr),
82    Coalesce(EthtoolCoalesceAttr),
83    TsInfo(EthtoolTsInfoAttr),
84    Channel(EthtoolChannelAttr),
85}
86
87impl Nla for EthtoolAttr {
88    fn value_len(&self) -> usize {
89        match self {
90            Self::Pause(attr) => attr.value_len(),
91            Self::Feature(attr) => attr.value_len(),
92            Self::LinkMode(attr) => attr.value_len(),
93            Self::Ring(attr) => attr.value_len(),
94            Self::Coalesce(attr) => attr.value_len(),
95            Self::TsInfo(attr) => attr.value_len(),
96            Self::Channel(attr) => attr.value_len(),
97        }
98    }
99
100    fn kind(&self) -> u16 {
101        match self {
102            Self::Pause(attr) => attr.kind(),
103            Self::Feature(attr) => attr.kind(),
104            Self::LinkMode(attr) => attr.kind(),
105            Self::Ring(attr) => attr.kind(),
106            Self::Coalesce(attr) => attr.kind(),
107            Self::TsInfo(attr) => attr.kind(),
108            Self::Channel(attr) => attr.kind(),
109        }
110    }
111
112    fn emit_value(&self, buffer: &mut [u8]) {
113        match self {
114            Self::Pause(attr) => attr.emit_value(buffer),
115            Self::Feature(attr) => attr.emit_value(buffer),
116            Self::LinkMode(attr) => attr.emit_value(buffer),
117            Self::Ring(attr) => attr.emit_value(buffer),
118            Self::Coalesce(attr) => attr.emit_value(buffer),
119            Self::TsInfo(attr) => attr.emit_value(buffer),
120            Self::Channel(attr) => attr.emit_value(buffer),
121        }
122    }
123}
124
125#[derive(Debug, PartialEq, Eq, Clone)]
126pub struct EthtoolMessage {
127    pub cmd: EthtoolCmd,
128    pub nlas: Vec<EthtoolAttr>,
129}
130
131impl GenlFamily for EthtoolMessage {
132    fn family_name() -> &'static str {
133        "ethtool"
134    }
135
136    fn version(&self) -> u8 {
137        1
138    }
139
140    fn command(&self) -> u8 {
141        self.cmd.into()
142    }
143}
144
145impl EthtoolMessage {
146    pub fn new_pause_get(iface_name: Option<&str>) -> Self {
147        let nlas = match iface_name {
148            Some(s) => {
149                vec![EthtoolAttr::Pause(EthtoolPauseAttr::Header(vec![
150                    EthtoolHeader::DevName(s.to_string()),
151                ]))]
152            }
153            None => vec![EthtoolAttr::Pause(EthtoolPauseAttr::Header(vec![]))],
154        };
155        EthtoolMessage {
156            cmd: EthtoolCmd::PauseGet,
157            nlas,
158        }
159    }
160
161    pub fn new_feature_get(iface_name: Option<&str>) -> Self {
162        let nlas = match iface_name {
163            Some(s) => {
164                vec![EthtoolAttr::Feature(EthtoolFeatureAttr::Header(vec![
165                    EthtoolHeader::DevName(s.to_string()),
166                ]))]
167            }
168            None => {
169                vec![EthtoolAttr::Feature(EthtoolFeatureAttr::Header(vec![]))]
170            }
171        };
172        EthtoolMessage {
173            cmd: EthtoolCmd::FeatureGet,
174            nlas,
175        }
176    }
177
178    pub fn new_link_mode_get(iface_name: Option<&str>) -> Self {
179        let nlas = match iface_name {
180            Some(s) => {
181                vec![EthtoolAttr::LinkMode(EthtoolLinkModeAttr::Header(vec![
182                    EthtoolHeader::DevName(s.to_string()),
183                ]))]
184            }
185            None => {
186                vec![EthtoolAttr::LinkMode(EthtoolLinkModeAttr::Header(vec![]))]
187            }
188        };
189        EthtoolMessage {
190            cmd: EthtoolCmd::LinkModeGet,
191            nlas,
192        }
193    }
194
195    pub fn new_ring_get(iface_name: Option<&str>) -> Self {
196        let nlas = match iface_name {
197            Some(s) => vec![EthtoolAttr::Ring(EthtoolRingAttr::Header(vec![
198                EthtoolHeader::DevName(s.to_string()),
199            ]))],
200            None => vec![EthtoolAttr::Ring(EthtoolRingAttr::Header(vec![]))],
201        };
202        EthtoolMessage {
203            cmd: EthtoolCmd::RingGet,
204            nlas,
205        }
206    }
207
208    pub fn new_coalesce_get(iface_name: Option<&str>) -> Self {
209        let nlas = match iface_name {
210            Some(s) => {
211                vec![EthtoolAttr::Coalesce(EthtoolCoalesceAttr::Header(vec![
212                    EthtoolHeader::DevName(s.to_string()),
213                ]))]
214            }
215            None => {
216                vec![EthtoolAttr::Coalesce(EthtoolCoalesceAttr::Header(vec![]))]
217            }
218        };
219        EthtoolMessage {
220            cmd: EthtoolCmd::CoalesceGet,
221            nlas,
222        }
223    }
224
225    pub fn new_tsinfo_get(iface_name: Option<&str>) -> Self {
226        let nlas = match iface_name {
227            Some(s) => {
228                vec![EthtoolAttr::TsInfo(EthtoolTsInfoAttr::Header(vec![
229                    EthtoolHeader::DevName(s.to_string()),
230                ]))]
231            }
232            None => {
233                vec![EthtoolAttr::TsInfo(EthtoolTsInfoAttr::Header(vec![]))]
234            }
235        };
236        EthtoolMessage {
237            cmd: EthtoolCmd::TsInfoGet,
238            nlas,
239        }
240    }
241
242    pub fn new_channel_get(iface_name: Option<&str>) -> Self {
243        let nlas = match iface_name {
244            Some(s) => {
245                vec![EthtoolAttr::Channel(EthtoolChannelAttr::Header(vec![
246                    EthtoolHeader::DevName(s.to_string()),
247                ]))]
248            }
249            None => {
250                vec![EthtoolAttr::Channel(EthtoolChannelAttr::Header(vec![]))]
251            }
252        };
253        EthtoolMessage {
254            cmd: EthtoolCmd::ChannelGet,
255            nlas,
256        }
257    }
258
259    pub fn new_channel_set(iface_name: &str) -> Self {
260        let nlas =
261            vec![EthtoolAttr::Channel(EthtoolChannelAttr::Header(vec![
262                EthtoolHeader::DevName(iface_name.to_string()),
263            ]))];
264        EthtoolMessage {
265            cmd: EthtoolCmd::ChannelSet,
266            nlas,
267        }
268    }
269}
270
271impl Emitable for EthtoolMessage {
272    fn buffer_len(&self) -> usize {
273        self.nlas.as_slice().buffer_len()
274    }
275
276    fn emit(&self, buffer: &mut [u8]) {
277        self.nlas.as_slice().emit(buffer)
278    }
279}
280
281impl ParseableParametrized<[u8], GenlHeader> for EthtoolMessage {
282    fn parse_with_param(
283        buffer: &[u8],
284        header: GenlHeader,
285    ) -> Result<Self, DecodeError> {
286        Ok(match header.cmd {
287            ETHTOOL_MSG_PAUSE_GET_REPLY => Self {
288                cmd: EthtoolCmd::PauseGetReply,
289                nlas: parse_pause_nlas(buffer)?,
290            },
291            ETHTOOL_MSG_FEATURES_GET_REPLY => Self {
292                cmd: EthtoolCmd::FeatureGetReply,
293                nlas: parse_feature_nlas(buffer)?,
294            },
295            ETHTOOL_MSG_LINKMODES_GET_REPLY => Self {
296                cmd: EthtoolCmd::LinkModeGetReply,
297                nlas: parse_link_mode_nlas(buffer)?,
298            },
299            ETHTOOL_MSG_RINGS_GET_REPLY => Self {
300                cmd: EthtoolCmd::RingGetReply,
301                nlas: parse_ring_nlas(buffer)?,
302            },
303            ETHTOOL_MSG_COALESCE_GET_REPLY => Self {
304                cmd: EthtoolCmd::CoalesceGetReply,
305                nlas: parse_coalesce_nlas(buffer)?,
306            },
307            ETHTOOL_MSG_TSINFO_GET_REPLY => Self {
308                cmd: EthtoolCmd::TsInfoGetReply,
309                nlas: parse_tsinfo_nlas(buffer)?,
310            },
311            ETHTOOL_MSG_CHANNELS_GET_REPLY => Self {
312                cmd: EthtoolCmd::ChannelGetReply,
313                nlas: parse_channel_nlas(buffer)?,
314            },
315            cmd => {
316                return Err(DecodeError::from(format!(
317                    "Unsupported ethtool reply command: {cmd}"
318                )))
319            }
320        })
321    }
322}