Skip to content

Commit 2f38b8b

Browse files
vibharyadianpopa
authored andcommitted
Snapshot-ga: Generate a MAC address if unspecified
Signed-off-by: Vibha Acharya <vibharya@amazon.co.uk>
1 parent 07b2048 commit 2f38b8b

File tree

7 files changed

+123
-48
lines changed

7 files changed

+123
-48
lines changed

src/devices/src/virtio/net/device.rs

Lines changed: 41 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,6 @@
44
// Portions Copyright 2017 The Chromium OS Authors. All rights reserved.
55
// Use of this source code is governed by a BSD-style license that can be
66
// found in the THIRD-PARTY file.
7-
8-
#[cfg(not(test))]
9-
use std::io;
107
use std::io::{Read, Write};
118
use std::net::Ipv4Addr;
129
use std::sync::atomic::AtomicUsize;
@@ -21,6 +18,7 @@ use mmds::ns::MmdsNetworkStack;
2118
use rate_limiter::{BucketUpdate, RateLimiter, TokenType};
2219
use utils::eventfd::EventFd;
2320
use utils::net::mac::{MacAddr, MAC_ADDR_LEN};
21+
use utils::rand_bytes;
2422
use virtio_gen::virtio_net::{
2523
virtio_net_hdr_v1, VIRTIO_F_VERSION_1, VIRTIO_NET_F_CSUM, VIRTIO_NET_F_GUEST_CSUM,
2624
VIRTIO_NET_F_GUEST_TSO4, VIRTIO_NET_F_GUEST_UFO, VIRTIO_NET_F_HOST_TSO4, VIRTIO_NET_F_HOST_UFO,
@@ -49,6 +47,9 @@ enum FrontendError {
4947
ReadOnlyDescriptor,
5048
}
5149

50+
// KVM OUI MAC address is 52:54:00:xx:xx:xx
51+
const KVM_OUI: [u8; 3] = [0x52, 0x54, 0x00];
52+
5253
pub(crate) fn vnet_hdr_len() -> usize {
5354
mem::size_of::<virtio_net_hdr_v1>()
5455
}
@@ -80,6 +81,14 @@ fn init_vnet_hdr(buf: &mut [u8]) {
8081
}
8182
}
8283

84+
fn generate_mac_address() -> MacAddr {
85+
let mut random_mac: [u8; MAC_ADDR_LEN] = [0; MAC_ADDR_LEN];
86+
let (left, right) = random_mac.split_at_mut(3);
87+
left.copy_from_slice(&KVM_OUI);
88+
right.copy_from_slice(rand_bytes(3).as_slice());
89+
MacAddr::from_bytes_unchecked(&random_mac)
90+
}
91+
8392
#[derive(Clone, Copy)]
8493
pub struct ConfigSpace {
8594
pub guest_mac: [u8; MAC_ADDR_LEN],
@@ -120,7 +129,7 @@ pub struct Net {
120129
pub(crate) irq_trigger: IrqTrigger,
121130

122131
pub(crate) config_space: ConfigSpace,
123-
pub(crate) guest_mac: Option<MacAddr>,
132+
pub(crate) guest_mac: MacAddr,
124133

125134
pub(crate) device_state: DeviceState,
126135
pub(crate) activate_evt: EventFd,
@@ -162,12 +171,18 @@ impl Net {
162171
| 1 << VIRTIO_RING_F_EVENT_IDX;
163172

164173
let mut config_space = ConfigSpace::default();
165-
if let Some(mac) = guest_mac {
166-
config_space.guest_mac.copy_from_slice(mac.get_bytes());
167-
// When this feature isn't available, the driver generates a random MAC address.
168-
// Otherwise, it should attempt to read the device MAC address from the config space.
169-
avail_features |= 1 << VIRTIO_NET_F_MAC;
170-
}
174+
// Enable feature to configure a MAC address
175+
// If not enabled, the driver generates a random MAC address.
176+
avail_features |= 1 << VIRTIO_NET_F_MAC;
177+
let mac_addr = {
178+
if let Some(some_mac_adrr) = guest_mac {
179+
*some_mac_adrr
180+
} else {
181+
// When the MAC address is not specified explicitly, generate one and assign it.
182+
generate_mac_address()
183+
}
184+
};
185+
config_space.guest_mac.copy_from_slice(mac_addr.get_bytes());
171186

172187
let mut queue_evts = Vec::new();
173188
let mut queues = Vec::new();
@@ -195,7 +210,7 @@ impl Net {
195210
activate_evt: EventFd::new(libc::EFD_NONBLOCK).map_err(Error::EventFd)?,
196211
config_space,
197212
mmds_ns: None,
198-
guest_mac: guest_mac.copied(),
213+
guest_mac: mac_addr,
199214

200215
#[cfg(test)]
201216
mocks: Mocks::default(),
@@ -208,8 +223,8 @@ impl Net {
208223
}
209224

210225
/// Provides the MAC of this net device.
211-
pub fn guest_mac(&self) -> Option<&MacAddr> {
212-
self.guest_mac.as_ref()
226+
pub fn guest_mac(&self) -> &MacAddr {
227+
&self.guest_mac
213228
}
214229

215230
/// Provides the host IFACE name of this net device.
@@ -413,7 +428,7 @@ impl Net {
413428
rate_limiter: &mut RateLimiter,
414429
frame_buf: &[u8],
415430
tap: &mut Tap,
416-
guest_mac: Option<MacAddr>,
431+
guest_mac: MacAddr,
417432
) -> Result<bool> {
418433
let checked_frame = |frame_buf| {
419434
frame_bytes_from_buf(frame_buf).map_err(|err| {
@@ -438,13 +453,11 @@ impl Net {
438453
// This frame goes to the TAP.
439454

440455
// Check for guest MAC spoofing.
441-
if let Some(mac) = guest_mac {
442-
let _ = EthernetFrame::from_bytes(checked_frame(frame_buf)?).map(|eth_frame| {
443-
if mac != eth_frame.src_mac() {
444-
METRICS.net.tx_spoofed_mac_count.inc();
445-
}
446-
});
447-
}
456+
let _ = EthernetFrame::from_bytes(checked_frame(frame_buf)?).map(|eth_frame| {
457+
if guest_mac != eth_frame.src_mac() {
458+
METRICS.net.tx_spoofed_mac_count.inc();
459+
}
460+
});
448461

449462
match tap.write(frame_buf) {
450463
Ok(_) => {
@@ -661,7 +674,7 @@ impl Net {
661674
}
662675

663676
#[cfg(not(test))]
664-
fn read_tap(&mut self) -> io::Result<usize> {
677+
fn read_tap(&mut self) -> std::io::Result<usize> {
665678
self.tap.read(&mut self.rx_frame_buf)
666679
}
667680

@@ -830,9 +843,8 @@ impl VirtioDevice for Net {
830843
}
831844

832845
config_space_bytes[offset as usize..(offset + data_len) as usize].copy_from_slice(data);
833-
self.guest_mac = Some(MacAddr::from_bytes_unchecked(
834-
&self.config_space.guest_mac[..MAC_ADDR_LEN],
835-
));
846+
self.guest_mac =
847+
MacAddr::from_bytes_unchecked(&self.config_space.guest_mac[..MAC_ADDR_LEN]);
836848
METRICS.net.mac_address_updates.inc();
837849
}
838850

@@ -1003,7 +1015,7 @@ pub mod tests {
10031015

10041016
// Check that the guest MAC was updated.
10051017
let expected_guest_mac = MacAddr::from_bytes_unchecked(&new_config);
1006-
assert_eq!(expected_guest_mac, net.guest_mac.unwrap());
1018+
assert_eq!(expected_guest_mac, net.guest_mac);
10071019
assert_eq!(METRICS.net.mac_address_updates.count(), 1);
10081020

10091021
// Partial write (this is how the kernel sets a new mac address) - byte by byte.
@@ -1470,7 +1482,7 @@ pub mod tests {
14701482
&mut net.tx_rate_limiter,
14711483
&frame_buf[..frame_len],
14721484
&mut net.tap,
1473-
Some(src_mac),
1485+
src_mac,
14741486
)
14751487
.unwrap())
14761488
);
@@ -1504,7 +1516,7 @@ pub mod tests {
15041516
&mut net.tx_rate_limiter,
15051517
&frame_buf[..frame_len],
15061518
&mut net.tap,
1507-
Some(guest_mac),
1519+
guest_mac,
15081520
)
15091521
);
15101522

@@ -1517,7 +1529,7 @@ pub mod tests {
15171529
&mut net.tx_rate_limiter,
15181530
&frame_buf[..frame_len],
15191531
&mut net.tap,
1520-
Some(not_guest_mac),
1532+
not_guest_mac,
15211533
)
15221534
);
15231535
}

src/devices/src/virtio/net/persist.rs

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ use versionize::{VersionMap, Versionize, VersionizeResult};
1818
use versionize_derive::Versionize;
1919
use vm_memory::GuestMemoryMmap;
2020

21-
use super::device::{ConfigSpace, Net};
21+
use super::device::Net;
2222
use super::{NUM_QUEUES, QUEUE_SIZE};
2323
use crate::virtio::persist::{Error as VirtioStateError, VirtioDeviceState};
2424
use crate::virtio::{DeviceState, TYPE_NET};
@@ -83,7 +83,10 @@ impl Persist<'_> for Net {
8383
let mut net = Net::new_with_tap(
8484
state.id.clone(),
8585
state.tap_if_name.clone(),
86-
None,
86+
Some(MacAddr::from_bytes_unchecked(
87+
&state.config_space.guest_mac[..MAC_ADDR_LEN],
88+
))
89+
.as_ref(),
8790
rx_rate_limiter,
8891
tx_rate_limiter,
8992
)?;
@@ -115,13 +118,6 @@ impl Persist<'_> for Net {
115118
Arc::new(AtomicUsize::new(state.virtio_state.interrupt_status));
116119
net.avail_features = state.virtio_state.avail_features;
117120
net.acked_features = state.virtio_state.acked_features;
118-
net.config_space = ConfigSpace {
119-
guest_mac: state.config_space.guest_mac,
120-
};
121-
122-
net.guest_mac = Some(MacAddr::from_bytes_unchecked(
123-
&state.config_space.guest_mac[..MAC_ADDR_LEN],
124-
));
125121

126122
if state.virtio_state.activated {
127123
net.device_state = DeviceState::Activated(constructor_args.mem);

src/devices/src/virtio/net/test_utils.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -298,7 +298,7 @@ pub fn default_guest_memory() -> GuestMemoryMmap {
298298
}
299299

300300
pub fn set_mac(net: &mut Net, mac: MacAddr) {
301-
net.guest_mac = Some(mac);
301+
net.guest_mac = mac;
302302
net.config_space.guest_mac.copy_from_slice(mac.get_bytes());
303303
}
304304

src/utils/src/lib.rs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,3 +28,32 @@ pub fn get_page_size() -> Result<usize, errno::Error> {
2828
ps => Ok(ps as usize),
2929
}
3030
}
31+
32+
// The below fucntions will get merged in rust-vmm function once we will upstream it there
33+
fn xor_pseudo_rng_u8_bytes(rand_fn: &dyn Fn() -> u32) -> Vec<u8> {
34+
let mut r = vec![];
35+
36+
for n in &rand_fn().to_ne_bytes() {
37+
r.push(*n);
38+
}
39+
r
40+
}
41+
42+
fn rand_bytes_impl(rand_fn: &dyn Fn() -> u32, len: usize) -> Vec<u8> {
43+
let mut buf: Vec<u8> = Vec::new();
44+
let mut done = 0;
45+
loop {
46+
for n in xor_pseudo_rng_u8_bytes(rand_fn) {
47+
done += 1;
48+
buf.push(n);
49+
if done >= len {
50+
return buf;
51+
}
52+
}
53+
}
54+
}
55+
56+
/// Get a pseudo random vector of length `len` with bytes.
57+
pub fn rand_bytes(len: usize) -> Vec<u8> {
58+
rand_bytes_impl(&rand::xor_psuedo_rng_u32, len)
59+
}

src/vmm/src/device_manager/persist.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -526,6 +526,7 @@ impl<'a> Persist<'a> for MMIODeviceManager {
526526
#[cfg(test)]
527527
mod tests {
528528
use devices::virtio::block::CacheType;
529+
use utils::net::mac::MacAddr;
529530
use utils::tempfile::TempFile;
530531

531532
use super::*;
@@ -695,7 +696,7 @@ mod tests {
695696
let network_interface = NetworkInterfaceConfig {
696697
iface_id: String::from("netif"),
697698
host_dev_name: String::from("hostname"),
698-
guest_mac: None,
699+
guest_mac: Some(MacAddr::parse_str("00:00:00:00:00:00").unwrap()),
699700
rx_rate_limiter: None,
700701
tx_rate_limiter: None,
701702
};

src/vmm/src/resources.rs

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1076,11 +1076,13 @@ mod tests {
10761076
"network-interfaces": [
10771077
{{
10781078
"iface_id": "netif1",
1079-
"host_dev_name": "hostname9"
1079+
"host_dev_name": "hostname9",
1080+
"guest_mac": "06:00:c0:00:32:de"
10801081
}},
10811082
{{
10821083
"iface_id": "netif2",
1083-
"host_dev_name": "hostname10"
1084+
"host_dev_name": "hostname10",
1085+
"guest_mac": "0a:00:20:00:a2:de"
10841086
}}
10851087
],
10861088
"machine-config": {{
@@ -1151,11 +1153,13 @@ mod tests {
11511153
"network-interfaces": [
11521154
{{
11531155
"iface_id": "netif1",
1154-
"host_dev_name": "hostname9"
1156+
"host_dev_name": "hostname9",
1157+
"guest_mac": "06:00:00:00:32:de"
11551158
}},
11561159
{{
11571160
"iface_id": "netif2",
1158-
"host_dev_name": "hostname10"
1161+
"host_dev_name": "hostname10",
1162+
"guest_mac": "06:00:00:00:a2:de"
11591163
}}
11601164
],
11611165
"machine-config": {{
@@ -1210,11 +1214,13 @@ mod tests {
12101214
"network-interfaces": [
12111215
{{
12121216
"iface_id": "netif1",
1213-
"host_dev_name": "hostname9"
1217+
"host_dev_name": "hostname9",
1218+
"guest_mac": "06:00:00:00:32:de"
12141219
}},
12151220
{{
12161221
"iface_id": "netif2",
1217-
"host_dev_name": "hostname10"
1222+
"host_dev_name": "hostname10",
1223+
"guest_mac": "06:00:00:00:a2:de"
12181224
}}
12191225
],
12201226
"machine-config": {{

src/vmm/src/vmm_config/net.rs

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ impl From<&Net> for NetworkInterfaceConfig {
3838
NetworkInterfaceConfig {
3939
iface_id: net.id().clone(),
4040
host_dev_name: net.iface_name(),
41-
guest_mac: net.guest_mac().copied(),
41+
guest_mac: Some(*net.guest_mac()),
4242
rx_rate_limiter: rx_rl.into_option(),
4343
tx_rate_limiter: tx_rl.into_option(),
4444
}
@@ -143,7 +143,7 @@ impl NetBuilder {
143143
let net = net.lock().expect("Poisoned lock");
144144
// Check if another net dev has same MAC.
145145
netif_config.guest_mac.is_some()
146-
&& netif_config.guest_mac.as_ref() == net.guest_mac()
146+
&& netif_config.guest_mac.as_ref().unwrap() == net.guest_mac()
147147
&& &netif_config.iface_id != net.id()
148148
};
149149
// Validate there is no Mac conflict.
@@ -409,4 +409,35 @@ mod tests {
409409
net_id
410410
);
411411
}
412+
413+
#[test]
414+
fn test_mac_address_generation() {
415+
let mut net_builder = NetBuilder::new();
416+
let net_id = "test_id";
417+
let host_dev_name = "dev";
418+
let guest_mac = "00:00:00:00:00:00";
419+
420+
let net = Net::new_with_tap(
421+
net_id.to_string(),
422+
host_dev_name.to_string(),
423+
None,
424+
RateLimiter::default(),
425+
RateLimiter::default(),
426+
)
427+
.unwrap();
428+
429+
net_builder.add_device(Arc::new(Mutex::new(net)));
430+
assert_eq!(net_builder.net_devices.len(), 1);
431+
assert_ne!(
432+
net_builder
433+
.net_devices
434+
.pop()
435+
.unwrap()
436+
.lock()
437+
.unwrap()
438+
.deref()
439+
.guest_mac(),
440+
MacAddr::parse_str(guest_mac).as_ref().unwrap()
441+
);
442+
}
412443
}

0 commit comments

Comments
 (0)