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