ethtool/
handle.rs

1// SPDX-License-Identifier: MIT
2
3use futures::{future::Either, FutureExt, Stream, StreamExt, TryStream};
4use genetlink::GenetlinkHandle;
5use netlink_packet_core::{
6    NetlinkMessage, NLM_F_ACK, NLM_F_DUMP, NLM_F_REQUEST,
7};
8use netlink_packet_generic::GenlMessage;
9use netlink_packet_utils::DecodeError;
10
11use crate::{
12    try_ethtool, EthtoolChannelHandle, EthtoolCoalesceHandle, EthtoolError,
13    EthtoolFeatureHandle, EthtoolLinkModeHandle, EthtoolMessage,
14    EthtoolPauseHandle, EthtoolRingHandle, EthtoolTsInfoHandle,
15};
16
17#[derive(Clone, Debug)]
18pub struct EthtoolHandle {
19    pub handle: GenetlinkHandle,
20}
21
22impl EthtoolHandle {
23    pub(crate) fn new(handle: GenetlinkHandle) -> Self {
24        EthtoolHandle { handle }
25    }
26
27    pub fn pause(&mut self) -> EthtoolPauseHandle {
28        EthtoolPauseHandle::new(self.clone())
29    }
30
31    pub fn feature(&mut self) -> EthtoolFeatureHandle {
32        EthtoolFeatureHandle::new(self.clone())
33    }
34
35    pub fn link_mode(&mut self) -> EthtoolLinkModeHandle {
36        EthtoolLinkModeHandle::new(self.clone())
37    }
38
39    pub fn ring(&mut self) -> EthtoolRingHandle {
40        EthtoolRingHandle::new(self.clone())
41    }
42
43    pub fn coalesce(&mut self) -> EthtoolCoalesceHandle {
44        EthtoolCoalesceHandle::new(self.clone())
45    }
46
47    pub fn tsinfo(&mut self) -> EthtoolTsInfoHandle {
48        EthtoolTsInfoHandle::new(self.clone())
49    }
50
51    pub fn channel(&mut self) -> EthtoolChannelHandle {
52        EthtoolChannelHandle::new(self.clone())
53    }
54
55    pub async fn request(
56        &mut self,
57        message: NetlinkMessage<GenlMessage<EthtoolMessage>>,
58    ) -> Result<
59        impl Stream<
60            Item = Result<
61                NetlinkMessage<GenlMessage<EthtoolMessage>>,
62                DecodeError,
63            >,
64        >,
65        EthtoolError,
66    > {
67        self.handle.request(message).await.map_err(|e| {
68            EthtoolError::RequestFailed(format!("BUG: Request failed with {e}"))
69        })
70    }
71}
72
73pub(crate) async fn ethtool_execute(
74    handle: &mut EthtoolHandle,
75    is_dump: bool,
76    ethtool_msg: EthtoolMessage,
77) -> impl TryStream<Ok = GenlMessage<EthtoolMessage>, Error = EthtoolError> {
78    let nl_header_flags = if is_dump {
79        // The NLM_F_ACK is required due to bug of kernel:
80        //  https://bugzilla.redhat.com/show_bug.cgi?id=1953847
81        // without `NLM_F_MULTI`, rust-netlink will not parse
82        // multiple netlink message in single socket reply.
83        // Using NLM_F_ACK will force rust-netlink to parse all till
84        // acked at the end.
85        NLM_F_DUMP | NLM_F_REQUEST | NLM_F_ACK
86    } else {
87        NLM_F_REQUEST | NLM_F_ACK
88    };
89
90    let mut nl_msg =
91        NetlinkMessage::from(GenlMessage::from_payload(ethtool_msg));
92
93    nl_msg.header.flags = nl_header_flags;
94
95    match handle.request(nl_msg).await {
96        Ok(response) => {
97            Either::Left(response.map(move |msg| Ok(try_ethtool!(msg))))
98        }
99        Err(e) => Either::Right(
100            futures::future::err::<GenlMessage<EthtoolMessage>, EthtoolError>(
101                e,
102            )
103            .into_stream(),
104        ),
105    }
106}