Skip to main content

libcontainer/network/
link.rs

1use std::os::fd::RawFd;
2
3use netlink_packet_core::{
4    NLM_F_ACK, NLM_F_CREATE, NLM_F_EXCL, NLM_F_REQUEST, NetlinkMessage, NetlinkPayload,
5};
6use netlink_packet_route::RouteNetlinkMessage;
7use netlink_packet_route::link::{LinkAttribute, LinkFlags, LinkMessage};
8
9use super::traits::{Client, NetlinkMessageHandler};
10use super::wrapper::ClientWrapper;
11use super::{NetlinkResponse, NetworkError, Result};
12
13/// Handler for Link messages in Netlink communication.
14///
15/// This handler processes Netlink messages related to network interfaces (links)
16/// and converts them into LinkMessage responses.
17pub struct LinkMessageHandler;
18
19impl NetlinkMessageHandler for LinkMessageHandler {
20    type Response = LinkMessage;
21
22    fn handle_payload(
23        &self,
24        payload: NetlinkPayload<RouteNetlinkMessage>,
25    ) -> Result<NetlinkResponse<Self::Response>> {
26        match payload {
27            NetlinkPayload::InnerMessage(RouteNetlinkMessage::NewLink(link)) => {
28                Ok(NetlinkResponse::Success(link))
29            }
30            NetlinkPayload::Error(e) => match e.code {
31                // According to netlink(7), when e.code is 0, it indicates success (acknowledgement)
32                // rather than an error. This is used for ACK messages where the operation succeeded.
33                // See: https://www.man7.org/linux/man-pages/man7/netlink.7.html
34                None => Ok(NetlinkResponse::Success(LinkMessage::default())),
35                Some(code) => Ok(NetlinkResponse::Error(code.get())),
36            },
37            NetlinkPayload::Done(_) => Ok(NetlinkResponse::Done),
38            _ => Err(NetworkError::IO(std::io::Error::other(format!(
39                "Unexpected message type: {:?}",
40                payload
41            )))),
42        }
43    }
44}
45
46/// Client for managing network interfaces (links).
47///
48/// This client provides methods for querying and modifying network interface properties
49/// through Netlink communication.
50pub struct LinkClient {
51    client: ClientWrapper,
52}
53
54impl LinkClient {
55    /// Creates a new LinkClient instance.
56    ///
57    /// # Returns
58    ///
59    /// A Result containing either a new LinkClient or an IO error
60    pub fn new(client: ClientWrapper) -> Result<Self> {
61        Ok(Self { client })
62    }
63
64    /// Retrieves a network interface by its name.
65    ///
66    /// # Arguments
67    ///
68    /// * `name` - The name of the network interface to retrieve
69    ///
70    /// # Returns
71    ///
72    /// A Result containing either the LinkMessage for the interface or an error
73    pub fn get_by_name(&mut self, name: &str) -> Result<LinkMessage> {
74        let mut message = LinkMessage::default();
75        message
76            .attributes
77            .push(LinkAttribute::IfName(name.to_string()));
78
79        let mut req = NetlinkMessage::from(RouteNetlinkMessage::GetLink(message));
80        req.header.flags = NLM_F_REQUEST;
81        req.finalize();
82
83        self.client.send_and_receive(&req, LinkMessageHandler)
84    }
85
86    /// Sets a network interface to the up state.
87    ///
88    /// # Arguments
89    ///
90    /// * `index` - The index of the network interface to modify
91    ///
92    /// # Returns
93    ///
94    /// A Result indicating success or failure of the operation
95    pub fn set_up(&mut self, index: u32) -> Result<()> {
96        let mut message = LinkMessage::default();
97        message.header.index = index;
98
99        let mut req = NetlinkMessage::from(RouteNetlinkMessage::SetLink(message));
100        if let NetlinkPayload::InnerMessage(RouteNetlinkMessage::SetLink(ref mut link)) =
101            req.payload
102        {
103            // change_mask specifies which flags we want to modify
104            link.header.change_mask |= LinkFlags::Up;
105            // Set the Up flag to bring the interface up
106            link.header.flags |= LinkFlags::Up;
107        }
108        // NLM_F_REQUEST: This is a request to the kernel
109        // NLM_F_ACK: Request an acknowledgment from the kernel
110        req.header.flags = NLM_F_REQUEST | NLM_F_ACK;
111        req.finalize();
112
113        self.client.send_and_receive(&req, LinkMessageHandler)?;
114        Ok(())
115    }
116
117    /// Sets a network interface to the down state.
118    ///
119    /// # Arguments
120    ///
121    /// * `index` - The index of the network interface to modify
122    ///
123    /// # Returns
124    ///
125    /// A Result indicating success or failure of the operation
126    pub fn set_down(&mut self, index: u32) -> Result<()> {
127        let mut message = LinkMessage::default();
128        message.header.index = index;
129
130        let mut req = NetlinkMessage::from(RouteNetlinkMessage::SetLink(message));
131        if let NetlinkPayload::InnerMessage(RouteNetlinkMessage::SetLink(ref mut link)) =
132            req.payload
133        {
134            // change_mask specifies which flags we want to modify
135            link.header.change_mask |= LinkFlags::Up;
136            // Remove the Up flag to bring the interface down
137            link.header.flags.remove(LinkFlags::Up);
138        }
139        // NLM_F_REQUEST: This is a request to the kernel
140        // NLM_F_ACK: Request an acknowledgment from the kernel
141        req.header.flags = NLM_F_REQUEST | NLM_F_ACK;
142        req.finalize();
143
144        self.client.send_and_receive(&req, LinkMessageHandler)?;
145        Ok(())
146    }
147
148    /// Moves a network interface to a different network namespace.
149    ///
150    /// # Arguments
151    ///
152    /// * `index` - The index of the network interface to move
153    /// * `new_name` - The new name for the interface in the target namespace
154    /// * `ns_path` - The file descriptor of the target network namespace
155    ///
156    /// # Returns
157    ///
158    /// A Result indicating success or failure of the operation
159    pub fn set_ns_fd(&mut self, index: u32, new_name: &str, ns_path: RawFd) -> Result<()> {
160        let mut message = LinkMessage::default();
161        message.header.index = index;
162        message
163            .attributes
164            .push(LinkAttribute::IfName(new_name.to_string()));
165        message.attributes.push(LinkAttribute::NetNsFd(ns_path));
166
167        let mut req = NetlinkMessage::from(RouteNetlinkMessage::SetLink(message));
168        // NLM_F_REQUEST: This is a request to the kernel
169        // NLM_F_ACK: Request an acknowledgment from the kernel
170        // NLM_F_EXCL: Fail if the interface name already exists in the target namespace
171        // NLM_F_CREATE: Create the interface if it doesn't exist
172        req.header.flags = NLM_F_REQUEST | NLM_F_ACK | NLM_F_EXCL | NLM_F_CREATE;
173        req.finalize();
174
175        self.client.send_and_receive(&req, LinkMessageHandler)?;
176        Ok(())
177    }
178}
179
180#[cfg(test)]
181mod tests {
182    use serial_test::serial;
183
184    use super::*;
185    use crate::network::NetlinkResponse;
186    use crate::network::fake::FakeNetlinkClient;
187    use crate::network::wrapper::{ClientWrapper, create_network_client};
188
189    #[test]
190    #[serial]
191    fn test_link_message_handler_success() {
192        let handler = LinkMessageHandler;
193        let mut link_msg = LinkMessage::default();
194        link_msg.header.index = 1;
195        link_msg
196            .attributes
197            .push(LinkAttribute::IfName("eth0".to_string()));
198
199        let payload = NetlinkPayload::InnerMessage(RouteNetlinkMessage::NewLink(link_msg.clone()));
200        let result = handler.handle_payload(payload);
201
202        assert!(result.is_ok());
203        match result.unwrap() {
204            NetlinkResponse::Success(response) => {
205                assert_eq!(response.header.index, 1);
206                assert_eq!(response.attributes.len(), 1);
207            }
208            _ => panic!("Expected Success response"),
209        }
210    }
211
212    #[test]
213    #[serial]
214    fn test_link_message_handler_errorcode_zero() {
215        let handler = LinkMessageHandler;
216        let mut error_msg = netlink_packet_core::ErrorMessage::default();
217        error_msg.code = std::num::NonZeroI32::new(0);
218        let error_payload = NetlinkPayload::Error(error_msg);
219        let result = handler.handle_payload(error_payload);
220
221        assert!(result.is_ok());
222        match result.unwrap() {
223            NetlinkResponse::Success(_) => {}
224            _ => panic!("Expected Success response"),
225        }
226    }
227
228    #[test]
229    #[serial]
230    fn test_link_message_handler_error() {
231        let handler = LinkMessageHandler;
232        let mut error_msg = netlink_packet_core::ErrorMessage::default();
233        error_msg.code = std::num::NonZeroI32::new(1);
234        let error_payload = NetlinkPayload::Error(error_msg);
235        let result = handler.handle_payload(error_payload);
236
237        assert!(result.is_ok());
238        match result.unwrap() {
239            NetlinkResponse::Error(code) => {
240                assert_eq!(code, 1);
241            }
242            _ => panic!("Expected Error response"),
243        }
244    }
245
246    #[test]
247    #[serial]
248    fn test_link_message_handler_done() {
249        let handler = LinkMessageHandler;
250        let done_payload = NetlinkPayload::Done(netlink_packet_core::DoneMessage::default());
251        let result = handler.handle_payload(done_payload);
252
253        assert!(result.is_ok());
254        match result.unwrap() {
255            NetlinkResponse::Done => {}
256            _ => panic!("Expected Done response"),
257        }
258    }
259
260    #[test]
261    #[serial]
262    fn test_link_message_handler_unexpected() {
263        let handler = LinkMessageHandler;
264        let unexpected_payload = NetlinkPayload::InnerMessage(RouteNetlinkMessage::NewAddress(
265            netlink_packet_route::address::AddressMessage::default(),
266        ));
267        let result = handler.handle_payload(unexpected_payload);
268
269        assert!(result.is_err());
270    }
271
272    #[test]
273    #[serial]
274    fn test_link_client_new() {
275        let result = LinkClient::new(create_network_client());
276
277        assert!(result.is_ok());
278    }
279
280    #[test]
281    #[serial]
282    fn test_link_client_get_by_name_without_response() {
283        let fake_client = FakeNetlinkClient::new();
284        let mut link_client = LinkClient::new(ClientWrapper::Fake(fake_client)).unwrap();
285        let result = link_client.get_by_name("eth0");
286
287        // Should failed without LinkMessage
288        assert!(result.is_err());
289    }
290
291    #[test]
292    #[serial]
293    fn test_link_client_get_by_name_with_response() {
294        let mut fake_client = FakeNetlinkClient::new();
295
296        // Set up multiple responses
297        let mut link1 = LinkMessage::default();
298        link1.header.index = 1;
299        link1
300            .attributes
301            .push(LinkAttribute::IfName("eth0".to_string()));
302
303        let responses = vec![RouteNetlinkMessage::NewLink(link1)];
304        fake_client.set_expected_responses(responses);
305
306        let mut link_client = LinkClient::new(ClientWrapper::Fake(fake_client)).unwrap();
307        let result = link_client.get_by_name("eth0");
308
309        // Should succeed with the first matching response
310        assert!(result.is_ok());
311        let response = result.unwrap();
312        assert_eq!(response.header.index, 1);
313        assert_eq!(response.attributes.len(), 1);
314    }
315
316    #[test]
317    #[serial]
318    fn test_link_client_set_up_failure() {
319        let mut fake_client = FakeNetlinkClient::new();
320        fake_client.set_failure("Set up failed".to_string());
321
322        let client_wrapper = ClientWrapper::Fake(fake_client);
323        let mut link_client = LinkClient::new(client_wrapper).unwrap();
324
325        let result = link_client.set_up(1);
326        assert!(result.is_err());
327    }
328
329    #[test]
330    #[serial]
331    fn test_link_client_set_up_success() {
332        let mut fake_client = FakeNetlinkClient::new();
333
334        // Set up a successful response (ACK with code 0)
335        let mut error_msg = netlink_packet_core::ErrorMessage::default();
336        error_msg.code = std::num::NonZeroI32::new(0);
337        let responses = vec![RouteNetlinkMessage::NewLink(LinkMessage::default())];
338        fake_client.set_expected_responses(responses);
339
340        let client_wrapper = ClientWrapper::Fake(fake_client);
341        let mut link_client = LinkClient::new(client_wrapper).unwrap();
342
343        let result = link_client.set_up(42);
344        assert!(result.is_ok());
345
346        // Verify the call was tracked
347        if let ClientWrapper::Fake(fake_client) = &mut link_client.client {
348            let send_calls = fake_client.get_send_calls();
349            assert_eq!(send_calls.len(), 1);
350
351            // Verify the message details
352            if let NetlinkPayload::InnerMessage(RouteNetlinkMessage::SetLink(link)) =
353                &send_calls[0].payload
354            {
355                assert_eq!(link.header.index, 42);
356                assert!(link.header.flags.contains(LinkFlags::Up));
357                assert!(link.header.change_mask.contains(LinkFlags::Up));
358            } else {
359                panic!("Expected SetLink message");
360            }
361
362            // Verify the netlink flags
363            let expected_flags = NLM_F_REQUEST | NLM_F_ACK;
364            assert_eq!(send_calls[0].header.flags, expected_flags);
365        } else {
366            panic!("Expected Fake client");
367        }
368    }
369
370    #[test]
371    #[serial]
372    fn test_link_client_set_down_failure() {
373        let mut fake_client = FakeNetlinkClient::new();
374        fake_client.set_failure("Set down failed".to_string());
375
376        let client_wrapper = ClientWrapper::Fake(fake_client);
377        let mut link_client = LinkClient::new(client_wrapper).unwrap();
378
379        let result = link_client.set_down(1);
380        assert!(result.is_err());
381    }
382
383    #[test]
384    #[serial]
385    fn test_link_client_set_down_success() {
386        let mut fake_client = FakeNetlinkClient::new();
387
388        let responses = vec![RouteNetlinkMessage::NewLink(LinkMessage::default())];
389        fake_client.set_expected_responses(responses);
390
391        let client_wrapper = ClientWrapper::Fake(fake_client);
392        let mut link_client = LinkClient::new(client_wrapper).unwrap();
393
394        let result = link_client.set_down(42);
395        assert!(result.is_ok());
396
397        // Verify the call was tracked
398        if let ClientWrapper::Fake(fake_client) = &mut link_client.client {
399            let send_calls = fake_client.get_send_calls();
400            assert_eq!(send_calls.len(), 1);
401
402            // Verify the message details
403            if let NetlinkPayload::InnerMessage(RouteNetlinkMessage::SetLink(link)) =
404                &send_calls[0].payload
405            {
406                assert_eq!(link.header.index, 42);
407                assert!(!link.header.flags.contains(LinkFlags::Up));
408                assert!(link.header.change_mask.contains(LinkFlags::Up));
409            } else {
410                panic!("Expected SetLink message");
411            }
412
413            // Verify the netlink flags
414            let expected_flags = NLM_F_REQUEST | NLM_F_ACK;
415            assert_eq!(send_calls[0].header.flags, expected_flags);
416        } else {
417            panic!("Expected Fake client");
418        }
419    }
420
421    #[test]
422    #[serial]
423    fn test_link_client_set_ns_fd_failure() {
424        let mut fake_client = FakeNetlinkClient::new();
425        fake_client.set_failure("Set namespace failed".to_string());
426
427        let client_wrapper = ClientWrapper::Fake(fake_client);
428        let mut link_client = LinkClient::new(client_wrapper).unwrap();
429
430        let result = link_client.set_ns_fd(1, "veth0", 123);
431        assert!(result.is_err());
432    }
433
434    #[test]
435    #[serial]
436    fn test_link_client_set_ns_fd_success() {
437        let mut fake_client = FakeNetlinkClient::new();
438
439        let responses = vec![RouteNetlinkMessage::NewLink(LinkMessage::default())];
440        fake_client.set_expected_responses(responses);
441
442        let client_wrapper = ClientWrapper::Fake(fake_client);
443        let mut link_client = LinkClient::new(client_wrapper).unwrap();
444
445        let result = link_client.set_ns_fd(42, "new_veth", 456);
446        assert!(result.is_ok());
447
448        // Verify the call was tracked
449        if let ClientWrapper::Fake(fake_client) = &mut link_client.client {
450            let send_calls = fake_client.get_send_calls();
451            assert_eq!(send_calls.len(), 1);
452
453            // Verify the message details
454            if let NetlinkPayload::InnerMessage(RouteNetlinkMessage::SetLink(link)) =
455                &send_calls[0].payload
456            {
457                assert_eq!(link.header.index, 42);
458                assert_eq!(link.attributes.len(), 2);
459
460                // Check for IfName attribute
461                let mut found_ifname = false;
462                let mut found_netns_fd = false;
463                for attr in &link.attributes {
464                    match attr {
465                        LinkAttribute::IfName(name) => {
466                            assert_eq!(name, "new_veth");
467                            found_ifname = true;
468                        }
469                        LinkAttribute::NetNsFd(fd) => {
470                            assert_eq!(*fd, 456);
471                            found_netns_fd = true;
472                        }
473                        _ => {}
474                    }
475                }
476                assert!(found_ifname, "IfName attribute not found");
477                assert!(found_netns_fd, "NetNsFd attribute not found");
478            } else {
479                panic!("Expected SetLink message");
480            }
481
482            // Verify the netlink flags
483            let expected_flags = NLM_F_REQUEST | NLM_F_ACK | NLM_F_EXCL | NLM_F_CREATE;
484            assert_eq!(send_calls[0].header.flags, expected_flags);
485        } else {
486            panic!("Expected Fake client");
487        }
488    }
489}