libcontainer/network/
network_device.rs1use std::os::fd::RawFd;
2
3use netlink_packet_route::address::{AddressHeaderFlags, AddressScope};
4use oci_spec::runtime::LinuxNetDevice;
5
6use super::Result;
7use super::address::AddressClient;
8use super::link::LinkClient;
9use super::wrapper::create_network_client;
10use crate::network::cidr::CidrAddress;
11
12pub fn resolve_device_name<'a>(device: &'a LinuxNetDevice, original_name: &'a str) -> &'a str {
15 device
16 .name()
17 .as_ref()
18 .filter(|d| !d.is_empty())
19 .map_or(original_name, |d| d)
20}
21
22pub fn dev_change_net_namespace(
28 name: &str,
29 netns_fd: RawFd,
30 device: &LinuxNetDevice,
31) -> Result<Vec<CidrAddress>> {
32 tracing::debug!(
33 "attaching network device {} to network namespace fd {}",
34 name,
35 netns_fd
36 );
37
38 let mut link_client = LinkClient::new(create_network_client())?;
39 let mut addr_client = AddressClient::new(create_network_client())?;
40
41 let new_name = resolve_device_name(device, name);
42
43 let link = link_client.get_by_name(name)?;
44
45 let index = link.header.index;
46
47 link_client.set_down(index)?;
51
52 let addrs = addr_client.get_by_index(index)?;
54
55 link_client
56 .set_ns_fd(index, new_name, netns_fd)
57 .map_err(|err| {
58 tracing::error!(?err, "failed to set_ns_fd");
59 err
60 })?;
61
62 let cidr_addrs: Vec<CidrAddress> = addrs
65 .iter()
66 .filter(|addr| {
67 if addr.header.scope != AddressScope::Universe {
71 tracing::debug!(
72 "skipping address with scope {:?} from network device {}",
73 addr.header.scope,
74 new_name
75 );
76 return false;
77 }
78
79 if !addr.header.flags.contains(AddressHeaderFlags::Permanent) {
84 tracing::debug!(
85 "skipping non-permanent address from network device {}",
86 new_name
87 );
88 return false;
89 }
90
91 true
92 })
93 .map(CidrAddress::from)
94 .collect();
95
96 Ok(cidr_addrs)
97}
98
99pub fn setup_addresses_in_network_namespace(
105 addrs: &[CidrAddress],
106 link_index: u32,
107 new_name: &str,
108 addr_client: &mut AddressClient,
109) -> Result<()> {
110 for addr in addrs {
113 tracing::debug!(
114 "adding address {:?}/{} to network device {}",
115 addr.address,
116 addr.prefix_len,
117 new_name
118 );
119 addr_client.add(link_index, addr.address, addr.prefix_len)?;
120 }
121
122 Ok(())
123}
124
125#[cfg(test)]
126mod tests {
127 use std::net::{IpAddr, Ipv4Addr};
128
129 use netlink_packet_route::RouteNetlinkMessage;
130 use netlink_packet_route::address::{AddressAttribute, AddressMessage};
131
132 use super::*;
133 use crate::network::address::AddressClient;
134 use crate::network::fake::FakeNetlinkClient;
135 use crate::network::wrapper::ClientWrapper;
136
137 #[test]
138 fn test_setup_addresses_in_network_namespace() {
139 let mut fake_client = FakeNetlinkClient::new();
140
141 let mut addr_msg = AddressMessage::default();
142 addr_msg.header.scope = AddressScope::Universe;
143 addr_msg.header.prefix_len = 24;
144 addr_msg.header.flags = AddressHeaderFlags::Permanent;
145 addr_msg
146 .attributes
147 .push(AddressAttribute::Address(IpAddr::V4(Ipv4Addr::new(
148 192, 168, 1, 1,
149 ))));
150
151 let responses = vec![RouteNetlinkMessage::NewAddress(addr_msg.clone())];
152 fake_client.set_expected_responses(responses);
153
154 let mut addr_client = AddressClient::new(ClientWrapper::Fake(fake_client)).unwrap();
155
156 let addrs = [addr_msg];
157 let serializable_addrs: Vec<CidrAddress> = addrs.iter().map(CidrAddress::from).collect();
158 let result =
159 setup_addresses_in_network_namespace(&serializable_addrs, 5, "eth1", &mut addr_client);
160 assert!(result.is_ok());
161
162 if let Some(send_calls) = addr_client.get_send_calls() {
164 assert_eq!(send_calls.len(), 1);
165 } else {
166 panic!("Expected Fake client");
167 }
168 }
169
170 #[test]
171 fn test_resolve_device_name_with_name() {
172 let device = LinuxNetDevice::default()
173 .set_name(Some("eth0".to_string()))
174 .clone();
175 let original = "veth0";
176
177 let result = resolve_device_name(&device, original);
178 assert_eq!(result, "eth0");
179 }
180
181 #[test]
182 fn test_resolve_device_name_with_empty_name() {
183 let device = LinuxNetDevice::default()
184 .set_name(Some("".to_string()))
185 .clone();
186 let original = "veth0";
187
188 let result = resolve_device_name(&device, original);
189 assert_eq!(result, "veth0");
190 }
191
192 #[test]
193 fn test_resolve_device_name_without_name() {
194 let device = LinuxNetDevice::default();
195 let original = "veth0";
196
197 let result = resolve_device_name(&device, original);
198 assert_eq!(result, "veth0");
199 }
200}