rtnetlink/link/
builder.rs

1// SPDX-License-Identifier: MIT
2
3use std::{marker::PhantomData, os::fd::RawFd};
4
5use crate::packet_route::{
6    link::{
7        InfoData, InfoKind, InfoPortData, InfoPortKind, LinkAttribute,
8        LinkFlags, LinkHeader, LinkInfo, LinkMessage,
9    },
10    AddressFamily,
11};
12
13/// Generic interface without interface type
14/// Could be used to match interface by interface name or index.
15/// Example on attaching a interface to controller
16/// ```no_run
17/// use rtnetlink::{new_connection, LinkUnspec};
18///
19/// #[tokio::main]
20/// async fn main() -> Result<(), String> {
21///     let (connection, handle, _) = new_connection().unwrap();
22///     tokio::spawn(connection);
23///
24///     let controller_index = 63u32;
25///
26///     handle
27///         .link()
28///         .set(
29///             LinkUnspec::new_with_name("my-nic")
30///                 .controller(controller_index)
31///                 .build(),
32///         )
33///         .execute()
34///         .await
35///         .map_err(|e| format!("{e}"))
36/// }
37/// ```
38#[derive(Debug)]
39pub struct LinkUnspec;
40
41impl LinkUnspec {
42    /// Equal to `LinkMessageBuilder::<LinkUnspec>::default().index()`
43    pub fn new_with_index(index: u32) -> LinkMessageBuilder<Self> {
44        LinkMessageBuilder::<LinkUnspec>::default().index(index)
45    }
46
47    /// Equal to `LinkMessageBuilder::<LinkUnspec>::default().name()`
48    pub fn new_with_name(name: &str) -> LinkMessageBuilder<Self> {
49        LinkMessageBuilder::<LinkUnspec>::default().name(name.to_string())
50    }
51}
52
53#[derive(Debug)]
54/// Helper struct for building [LinkMessage].
55/// The [LinkMessageBuilder] is designed for advanced user, wrapper
56/// structs/functions are created
57pub struct LinkMessageBuilder<T> {
58    pub(crate) header: LinkHeader,
59    pub(crate) info_kind: Option<InfoKind>,
60    pub(crate) info_data: Option<InfoData>,
61    pub(crate) port_kind: Option<InfoPortKind>,
62    pub(crate) port_data: Option<InfoPortData>,
63    pub(crate) extra_attriutes: Vec<LinkAttribute>,
64    _phantom: PhantomData<T>,
65}
66
67impl<T> Default for LinkMessageBuilder<T> {
68    fn default() -> Self {
69        Self {
70            header: Default::default(),
71            info_kind: None,
72            info_data: Default::default(),
73            extra_attriutes: Default::default(),
74            port_kind: None,
75            port_data: None,
76            _phantom: Default::default(),
77        }
78    }
79}
80
81impl<T> LinkMessageBuilder<T> {
82    pub fn new_with_info_kind(info_kind: InfoKind) -> Self {
83        Self {
84            info_kind: Some(info_kind),
85            ..Default::default()
86        }
87    }
88
89    /// Set arbitrary [LinkHeader]
90    pub fn set_header(self, header: LinkHeader) -> Self {
91        let mut ret = self;
92        ret.header = header;
93        ret
94    }
95
96    /// Append arbitrary [LinkAttribute]
97    pub fn append_extra_attribute(self, link_attr: LinkAttribute) -> Self {
98        let mut ret = self;
99        ret.extra_attriutes.push(link_attr);
100        ret
101    }
102
103    /// Set arbitrary [InfoData]
104    pub fn set_info_data(self, info_data: InfoData) -> Self {
105        let mut ret = self;
106        ret.info_data = Some(info_data);
107        ret
108    }
109
110    /// Set the link up (equivalent to `ip link set dev DEV up`)
111    pub fn up(self) -> Self {
112        let mut ret = self;
113        ret.header.flags |= LinkFlags::Up;
114        ret.header.change_mask |= LinkFlags::Up;
115        ret
116    }
117
118    /// Set the link down (equivalent to `ip link set dev DEV down`)
119    pub fn down(self) -> Self {
120        let mut ret = self;
121        ret.header.flags.remove(LinkFlags::Up);
122        ret.header.change_mask |= LinkFlags::Up;
123        ret
124    }
125
126    /// Enable or disable promiscious mode of the link with the given index
127    /// (equivalent to `ip link set dev DEV promisc on/off`)
128    pub fn promiscuous(self, enable: bool) -> Self {
129        let mut ret = self;
130        if enable {
131            ret.header.flags |= LinkFlags::Promisc;
132        } else {
133            ret.header.flags.remove(LinkFlags::Promisc);
134        }
135        ret.header.change_mask |= LinkFlags::Promisc;
136        ret
137    }
138
139    /// Enable or disable the ARP protocol of the link with the given index
140    /// (equivalent to `ip link set dev DEV arp on/off`)
141    pub fn arp(self, enable: bool) -> Self {
142        let mut ret = self;
143        if enable {
144            ret.header.flags.remove(LinkFlags::Noarp);
145        } else {
146            ret.header.flags |= LinkFlags::Noarp;
147        }
148        ret.header.change_mask |= LinkFlags::Noarp;
149        ret
150    }
151
152    pub fn name(self, name: String) -> Self {
153        self.append_extra_attribute(LinkAttribute::IfName(name))
154    }
155
156    /// Set the mtu of the link with the given index (equivalent to
157    /// `ip link set DEV mtu MTU`)
158    pub fn mtu(self, mtu: u32) -> Self {
159        self.append_extra_attribute(LinkAttribute::Mtu(mtu))
160    }
161
162    /// Kernel index number of interface, used for querying, modifying or
163    /// deleting existing interface.
164    pub fn index(self, index: u32) -> Self {
165        let mut ret = self;
166        ret.header.index = index;
167        ret
168    }
169
170    pub fn interface_family(self, family: AddressFamily) -> Self {
171        let mut ret = self;
172        ret.header.interface_family = family;
173        ret
174    }
175
176    /// Define the hardware address of the link when creating it (equivalent to
177    /// `ip link add NAME address ADDRESS`)
178    pub fn address(self, address: Vec<u8>) -> Self {
179        self.append_extra_attribute(LinkAttribute::Address(address))
180    }
181
182    /// Move this network device into the network namespace of the process with
183    /// the given `pid`.
184    pub fn setns_by_pid(self, pid: u32) -> Self {
185        self.append_extra_attribute(LinkAttribute::NetNsPid(pid))
186    }
187
188    /// Move this network device into the network namespace corresponding to the
189    /// given file descriptor.
190    pub fn setns_by_fd(self, fd: RawFd) -> Self {
191        self.append_extra_attribute(LinkAttribute::NetNsFd(fd))
192    }
193
194    /// The physical device to act operate on. (e.g. the parent interface of
195    /// VLAN/VxLAN)
196    pub fn link(self, index: u32) -> Self {
197        self.append_extra_attribute(LinkAttribute::Link(index))
198    }
199
200    /// Define controller interface index (similar to
201    /// ip link set NAME master CONTROLLER_NAME)
202    pub fn controller(self, ctrl_index: u32) -> Self {
203        self.append_extra_attribute(LinkAttribute::Controller(ctrl_index))
204    }
205
206    /// Detach the link from its _controller_. This is equivalent to `ip link
207    /// set LINK nomaster`. To succeed, the link that is being detached must be
208    /// UP.
209    pub fn nocontroller(self) -> Self {
210        self.append_extra_attribute(LinkAttribute::Controller(0))
211    }
212
213    pub fn set_port_kind(self, port_kind: InfoPortKind) -> Self {
214        let mut ret = self;
215        ret.port_kind = Some(port_kind);
216        ret
217    }
218
219    /// Include port settings.
220    /// The [LinkBondPort] and [LinkBridgePort] are the helper
221    pub fn set_port_data(self, port_data: InfoPortData) -> Self {
222        let mut ret = self;
223        ret.port_data = Some(port_data);
224        ret
225    }
226
227    pub fn build(self) -> LinkMessage {
228        let mut message = LinkMessage::default();
229        message.header = self.header;
230
231        if !self.extra_attriutes.is_empty() {
232            message.attributes = self.extra_attriutes;
233        }
234
235        let mut link_infos: Vec<LinkInfo> = Vec::new();
236        if let Some(info_kind) = self.info_kind {
237            link_infos.push(LinkInfo::Kind(info_kind));
238        }
239        if let Some(info_data) = self.info_data {
240            link_infos.push(LinkInfo::Data(info_data));
241        }
242
243        if let Some(port_kind) = self.port_kind {
244            link_infos.push(LinkInfo::PortKind(port_kind));
245        }
246
247        if let Some(port_data) = self.port_data {
248            link_infos.push(LinkInfo::PortData(port_data));
249        }
250
251        if !link_infos.is_empty() {
252            message.attributes.push(LinkAttribute::LinkInfo(link_infos));
253        }
254
255        message
256    }
257}
258
259impl LinkMessageBuilder<LinkUnspec> {
260    pub fn new() -> Self {
261        Self::default()
262    }
263}