Skip to content

Commit 2190da8

Browse files
committed
virtio-devices: implement a PostMigrationAnnouncer for virtio-net
To announce the new location in a network, virtio-net devices can send reverse ARP packets. On-behalf-of: SAP sebastian.eydam@sap.com Signed-off-by: Sebastian Eydam <sebastian.eydam@cyberus-technology.de>
1 parent 1a06c94 commit 2190da8

File tree

2 files changed

+75
-3
lines changed

2 files changed

+75
-3
lines changed

net_util/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ fn create_unix_socket() -> Result<net::UdpSocket> {
101101
Ok(unsafe { net::UdpSocket::from_raw_fd(sock) })
102102
}
103103

104-
fn vnet_hdr_len() -> usize {
104+
pub fn vnet_hdr_len() -> usize {
105105
std::mem::size_of::<virtio_net_hdr_v1>()
106106
}
107107

virtio-devices/src/net.rs

Lines changed: 74 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,9 @@ use log::{debug, error, info, trace};
2020
#[cfg(not(fuzzing))]
2121
use net_util::virtio_features_to_tap_offload;
2222
use net_util::{
23-
CtrlQueue, MacAddr, NetCounters, NetQueuePair, OpenTapError, RxVirtio, Tap, TapError, TxVirtio,
24-
VirtioNetConfig, build_net_config_space, build_net_config_space_with_mq, open_tap,
23+
CtrlQueue, MAC_ADDR_LEN, MacAddr, NetCounters, NetQueuePair, OpenTapError, RxVirtio, Tap,
24+
TapError, TxVirtio, VirtioNetConfig, build_net_config_space, build_net_config_space_with_mq,
25+
open_tap, vnet_hdr_len,
2526
};
2627
use seccompiler::SeccompAction;
2728
use serde::{Deserialize, Serialize};
@@ -40,6 +41,7 @@ use super::{
4041
EpollHelperHandler, Error as DeviceError, RateLimiterConfig, VirtioCommon, VirtioDevice,
4142
VirtioDeviceType, VirtioInterruptType,
4243
};
44+
use crate::device::PostMigrationAnnouncer;
4345
use crate::seccomp_filters::Thread;
4446
use crate::thread_helper::spawn_virtio_thread;
4547
use crate::{GuestMemoryMmap, VirtioInterrupt};
@@ -655,6 +657,38 @@ impl Net {
655657
pub fn wait_for_epoll_threads(&mut self) {
656658
self.common.wait_for_epoll_threads();
657659
}
660+
661+
fn build_rarp_announce(&self) -> [u8; 60] {
662+
const ETH_P_RARP: u16 = 0x8035; // Ethertype RARP
663+
const ARP_HTYPE_ETH: u16 = 0x1; // Hardware type Ethernet
664+
const ARP_PTYPE_IP: u16 = 0x0800; // Protocol type IPv4
665+
const ARP_OP_REQUEST_REV: u16 = 0x0003; // RARP Request opcode
666+
667+
const IPV4_ADDR_LENGTH: usize = 4; // Size of an IPv4 address
668+
669+
let mut buf = [0u8; 60];
670+
671+
// Ethernet header
672+
buf[0..6].copy_from_slice(&[0xff; MAC_ADDR_LEN]); // This is a broadcast
673+
buf[6..12].copy_from_slice(&self.config.mac); // Src is this NIC
674+
buf[12..14].copy_from_slice(&ETH_P_RARP.to_be_bytes()); // This is a RARP packet
675+
676+
// ARP Header
677+
buf[14..16].copy_from_slice(&ARP_HTYPE_ETH.to_be_bytes());
678+
buf[16..18].copy_from_slice(&ARP_PTYPE_IP.to_be_bytes());
679+
buf[18] = MAC_ADDR_LEN as u8; // Hardware address length (ethernet)
680+
buf[19] = IPV4_ADDR_LENGTH as u8; // Protocol address length (IPv4)
681+
// This is a "fake RARP" packet, we don't want to perform a real RARP lookup.
682+
// Thus the content of the next fields is largely irrelevant. Setting source
683+
// hardware address = target hardware address is fine according to RFC 903.
684+
buf[20..22].copy_from_slice(&ARP_OP_REQUEST_REV.to_be_bytes());
685+
buf[22..28].copy_from_slice(&self.config.mac); // Source hardware address
686+
buf[28..32].copy_from_slice(&[0x00; IPV4_ADDR_LENGTH]); // Source protocol address
687+
buf[32..38].copy_from_slice(&self.config.mac); // Target hardware address
688+
buf[38..42].copy_from_slice(&[0x00; IPV4_ADDR_LENGTH]); // Target protocol address
689+
690+
buf
691+
}
658692
}
659693

660694
impl Drop for Net {
@@ -870,6 +904,13 @@ impl VirtioDevice for Net {
870904
fn set_access_platform(&mut self, access_platform: Arc<dyn AccessPlatform>) {
871905
self.common.set_access_platform(access_platform);
872906
}
907+
908+
fn post_migration_announcer(&self) -> std::option::Option<Box<dyn PostMigrationAnnouncer>> {
909+
Some(Box::new(TapRarpAnnouncer::new(
910+
self.build_rarp_announce(),
911+
self.taps.clone(),
912+
)))
913+
}
873914
}
874915

875916
impl Pausable for Net {
@@ -898,3 +939,34 @@ impl Snapshottable for Net {
898939
}
899940
impl Transportable for Net {}
900941
impl Migratable for Net {}
942+
943+
pub struct TapRarpAnnouncer {
944+
announce: [u8; 60],
945+
taps: Vec<Tap>,
946+
}
947+
948+
impl TapRarpAnnouncer {
949+
pub fn new(announce: [u8; 60], taps: Vec<Tap>) -> Self {
950+
Self { announce, taps }
951+
}
952+
}
953+
954+
impl PostMigrationAnnouncer for TapRarpAnnouncer {
955+
fn announce_once(&mut self) {
956+
// We have to add a virtio-net header to the announce.
957+
let mut buf = vec![0u8; vnet_hdr_len() + self.announce.len()];
958+
buf[vnet_hdr_len()..].copy_from_slice(&self.announce);
959+
960+
for tap in &self.taps {
961+
// SAFETY: `buf.as_ptr()` is valid for `buf.len()` bytes and remains
962+
// valid until the syscall returns. `tap.as_raw_fd()` is a valid TAP fd.
963+
let _ = unsafe {
964+
libc::write(
965+
tap.as_raw_fd(),
966+
buf.as_ptr() as *const libc::c_void,
967+
buf.len(),
968+
)
969+
};
970+
}
971+
}
972+
}

0 commit comments

Comments
 (0)