Skip to main content

ethtool/pause/
attr.rs

1// SPDX-License-Identifier: MIT
2
3use netlink_packet_core::{
4    emit_u64, parse_u64, parse_u8, DecodeError, DefaultNla, Emitable,
5    ErrorContext, Nla, NlaBuffer, NlasIterator, Parseable, NLA_F_NESTED,
6};
7
8use crate::{EthtoolAttr, EthtoolHeader};
9
10const ETHTOOL_A_PAUSE_HEADER: u16 = 1;
11const ETHTOOL_A_PAUSE_AUTONEG: u16 = 2;
12const ETHTOOL_A_PAUSE_RX: u16 = 3;
13const ETHTOOL_A_PAUSE_TX: u16 = 4;
14const ETHTOOL_A_PAUSE_STATS: u16 = 5;
15
16const ETHTOOL_A_PAUSE_STAT_TX_FRAMES: u16 = 2;
17const ETHTOOL_A_PAUSE_STAT_RX_FRAMES: u16 = 3;
18
19#[derive(Debug, PartialEq, Eq, Clone)]
20pub enum EthtoolPauseStatAttr {
21    Rx(u64),
22    Tx(u64),
23    Other(DefaultNla),
24}
25
26impl Nla for EthtoolPauseStatAttr {
27    fn value_len(&self) -> usize {
28        match self {
29            Self::Rx(_) | Self::Tx(_) => 8,
30            Self::Other(attr) => attr.value_len(),
31        }
32    }
33
34    fn kind(&self) -> u16 {
35        match self {
36            Self::Rx(_) => ETHTOOL_A_PAUSE_STAT_RX_FRAMES,
37            Self::Tx(_) => ETHTOOL_A_PAUSE_STAT_RX_FRAMES,
38            Self::Other(attr) => attr.kind(),
39        }
40    }
41
42    fn emit_value(&self, buffer: &mut [u8]) {
43        match self {
44            Self::Rx(value) | Self::Tx(value) => {
45                emit_u64(buffer, *value).unwrap()
46            }
47            Self::Other(ref attr) => attr.emit_value(buffer),
48        }
49    }
50}
51
52impl<'a, T: AsRef<[u8]> + ?Sized> Parseable<NlaBuffer<&'a T>>
53    for EthtoolPauseStatAttr
54{
55    fn parse(buf: &NlaBuffer<&'a T>) -> Result<Self, DecodeError> {
56        let payload = buf.value();
57        Ok(match buf.kind() {
58            ETHTOOL_A_PAUSE_STAT_TX_FRAMES => Self::Tx(
59                parse_u64(payload)
60                    .context("invalid ETHTOOL_A_PAUSE_STAT_TX_FRAMES value")?,
61            ),
62            ETHTOOL_A_PAUSE_STAT_RX_FRAMES => Self::Rx(
63                parse_u64(payload)
64                    .context("invalid ETHTOOL_A_PAUSE_STAT_RX_FRAMES value")?,
65            ),
66            _ => Self::Other(
67                DefaultNla::parse(buf).context("invalid NLA (unknown kind)")?,
68            ),
69        })
70    }
71}
72
73#[derive(Debug, PartialEq, Eq, Clone)]
74pub enum EthtoolPauseAttr {
75    Header(Vec<EthtoolHeader>),
76    AutoNeg(bool),
77    Rx(bool),
78    Tx(bool),
79    Stats(Vec<EthtoolPauseStatAttr>),
80    Other(DefaultNla),
81}
82
83impl Nla for EthtoolPauseAttr {
84    fn value_len(&self) -> usize {
85        match self {
86            Self::Header(hdrs) => hdrs.as_slice().buffer_len(),
87            Self::AutoNeg(_) | Self::Rx(_) | Self::Tx(_) => 1,
88            Self::Stats(ref nlas) => nlas.as_slice().buffer_len(),
89            Self::Other(attr) => attr.value_len(),
90        }
91    }
92
93    fn kind(&self) -> u16 {
94        match self {
95            Self::Header(_) => ETHTOOL_A_PAUSE_HEADER | NLA_F_NESTED,
96            Self::AutoNeg(_) => ETHTOOL_A_PAUSE_AUTONEG,
97            Self::Rx(_) => ETHTOOL_A_PAUSE_RX,
98            Self::Tx(_) => ETHTOOL_A_PAUSE_TX,
99            Self::Stats(_) => ETHTOOL_A_PAUSE_STATS | NLA_F_NESTED,
100            Self::Other(attr) => attr.kind(),
101        }
102    }
103
104    fn emit_value(&self, buffer: &mut [u8]) {
105        match self {
106            Self::Header(ref nlas) => nlas.as_slice().emit(buffer),
107            Self::AutoNeg(value) | Self::Rx(value) | Self::Tx(value) => {
108                buffer[0] = *value as u8
109            }
110            Self::Stats(ref nlas) => nlas.as_slice().emit(buffer),
111            Self::Other(ref attr) => attr.emit(buffer),
112        }
113    }
114}
115
116impl<'a, T: AsRef<[u8]> + ?Sized> Parseable<NlaBuffer<&'a T>>
117    for EthtoolPauseAttr
118{
119    fn parse(buf: &NlaBuffer<&'a T>) -> Result<Self, DecodeError> {
120        let payload = buf.value();
121        Ok(match buf.kind() {
122            ETHTOOL_A_PAUSE_HEADER => {
123                let mut nlas = Vec::new();
124                let error_msg = "failed to parse pause header attributes";
125                for nla in NlasIterator::new(payload) {
126                    let nla = &nla.context(error_msg)?;
127                    let parsed =
128                        EthtoolHeader::parse(nla).context(error_msg)?;
129                    nlas.push(parsed);
130                }
131                Self::Header(nlas)
132            }
133            ETHTOOL_A_PAUSE_AUTONEG => Self::AutoNeg(
134                parse_u8(payload)
135                    .context("invalid ETHTOOL_A_PAUSE_AUTONEG value")?
136                    == 1,
137            ),
138            ETHTOOL_A_PAUSE_RX => Self::Rx(
139                parse_u8(payload)
140                    .context("invalid ETHTOOL_A_PAUSE_RX value")?
141                    == 1,
142            ),
143            ETHTOOL_A_PAUSE_TX => Self::Tx(
144                parse_u8(payload)
145                    .context("invalid ETHTOOL_A_PAUSE_TX value")?
146                    == 1,
147            ),
148            ETHTOOL_A_PAUSE_STATS => {
149                let mut nlas = Vec::new();
150                let error_msg = "failed to parse pause stats attributes";
151                for nla in NlasIterator::new(payload) {
152                    let nla = &nla.context(error_msg)?;
153                    let parsed =
154                        EthtoolPauseStatAttr::parse(nla).context(error_msg)?;
155                    nlas.push(parsed);
156                }
157                Self::Stats(nlas)
158            }
159            _ => Self::Other(
160                DefaultNla::parse(buf).context("invalid NLA (unknown kind)")?,
161            ),
162        })
163    }
164}
165
166pub(crate) fn parse_pause_nlas(
167    buffer: &[u8],
168) -> Result<Vec<EthtoolAttr>, DecodeError> {
169    let mut nlas = Vec::new();
170    for nla in NlasIterator::new(buffer) {
171        let error_msg =
172            format!("Failed to parse ethtool pause message attribute {nla:?}");
173        let nla = &nla.context(error_msg.clone())?;
174        let parsed = EthtoolPauseAttr::parse(nla).context(error_msg)?;
175        nlas.push(EthtoolAttr::Pause(parsed));
176    }
177    Ok(nlas)
178}