diff --git a/vm-migration/src/protocol.rs b/vm-migration/src/protocol.rs index e3f899759..00ca79952 100644 --- a/vm-migration/src/protocol.rs +++ b/vm-migration/src/protocol.rs @@ -91,6 +91,7 @@ use std::mem::size_of; use std::ops::RangeInclusive; use std::{mem, slice}; +use anyhow::anyhow; use itertools::Itertools; use serde::{Deserialize, Serialize}; use vm_memory::ByteValued; @@ -226,6 +227,26 @@ impl Request { &self.command_headers } + /// Returns the sender protocol version from a `Start` request if it is supported. + pub fn sender_protocol_version(&self) -> Result { + assert_eq!( + self.command(), + Command::Start, + "sender_protocol_version() must only be called for Start requests", + ); + + // The protocol version is stored in the first two header bytes, the remaining bytes are ignored. + let sender_version = u16::from_le_bytes([self.command_headers[0], self.command_headers[1]]); + if !supported_protocol_versions().any(|version| version == sender_version) { + let supported_versions = supported_protocol_versions().join(", "); + return Err(MigratableError::MigrateReceive(anyhow!( + "Migration protocol version {sender_version} doesn't match supported versions: {supported_versions}" + ))); + } + + Ok(sender_version) + } + pub fn read_from(fd: &mut dyn Read) -> Result { let mut request = Request::default(); fd.read_exact(Self::as_mut_slice(&mut request)) @@ -522,7 +543,9 @@ impl MemoryRangeTable { #[cfg(test)] mod unit_tests { - use crate::protocol::{Command, MemoryRange, MemoryRangeTable, Request}; + use crate::protocol::{ + CURRENT_PROTOCOL_VERSION, Command, MemoryRange, MemoryRangeTable, Request, + }; #[test] fn test_start_request_ignores_residual_command_headers_bytes() { @@ -538,6 +561,18 @@ mod unit_tests { ); } + #[test] + fn test_sender_protocol_version_rejects_unsupported_version() { + let request = Request { + command: Command::Start, + command_headers: [255, 0, 0, 0, 0, 0], + length: 0, + }; + + const { assert!(CURRENT_PROTOCOL_VERSION < 255) }; + request.sender_protocol_version().unwrap_err(); + } + #[test] fn test_memory_range_table_from_dirty_ranges_iter() { let input = [0b1111_1110_1110, 0b1_0000]; diff --git a/vmm/src/lib.rs b/vmm/src/lib.rs index 8d9746138..267c1ef34 100644 --- a/vmm/src/lib.rs +++ b/vmm/src/lib.rs @@ -937,7 +937,11 @@ impl Vmm { let state_name = state.variant_name(); match state { Established => match req.command() { - Command::Start => Ok(Started), + Command::Start => { + let migration_protocol_version = req.sender_protocol_version()?; + debug!("Using migration protocol {migration_protocol_version}"); + Ok(Started) + } c => invalid_command(state_name, c), }, Started => match req.command() { @@ -1420,6 +1424,7 @@ impl Vmm { Request::start(), MigratableError::MigrateSend(anyhow!("Error starting migration")), )?; + debug!("Using migration protocol {CURRENT_PROTOCOL_VERSION}"); // Send config let vm_config = vm.get_config();