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