blob: 3fc9e5a52073e9f955941f1a2d38f6ce1d788b99 [file]
// Licensed under the Apache-2.0 license
//! SPDM over MCTP transport integration test.
//!
//! This test verifies the full SPDM request/response flow over MCTP transport
//! using mock MCTP infrastructure. It exercises:
//!
//! 1. Requester sends a request via `send_request`
//! 2. Responder receives the request via `receive_request`
//! 3. Responder sends a response via `send_response`
//! 4. Requester receives the response via `receive_response`
use std::cell::RefCell;
use mctp::{Eid, Tag};
use mctp_lib::fragment::{Fragmenter, SendOutput};
use mctp_lib::Sender;
use openprot_mctp_api::{Handle, MctpClient, MctpError, RecvMetadata, ResponseCode};
use openprot_mctp_server::{Server, ServerConfig};
use openprot_spdm_transport_mctp::MctpSpdmTransport;
use spdm_lib::codec::MessageBuf;
use spdm_lib::platform::transport::SpdmTransport;
// ---------------------------------------------------------------------------
// Mock transport (copied from mctp-server tests)
// ---------------------------------------------------------------------------
/// A mock sender that captures outbound packets in a buffer.
struct BufferSender<'a> {
packets: &'a RefCell<Vec<Vec<u8>>>,
}
impl Sender for BufferSender<'_> {
fn send_vectored(
&mut self,
mut fragmenter: Fragmenter,
payload: &[&[u8]],
) -> mctp::Result<Tag> {
loop {
let mut buf = [0u8; 255];
match fragmenter.fragment_vectored(payload, &mut buf) {
SendOutput::Packet(p) => {
self.packets.borrow_mut().push(p.to_vec());
}
SendOutput::Complete { tag, .. } => return Ok(tag),
SendOutput::Error { err, .. } => return Err(err),
}
}
}
fn get_mtu(&self) -> usize {
255
}
}
/// Transfer packets from one server's outbound buffer to another server.
fn transfer<S: Sender, const N: usize>(
packets: &RefCell<Vec<Vec<u8>>>,
dest: &mut Server<S, N>,
) {
let pkts = packets.borrow();
for pkt in pkts.iter() {
dest.inbound(pkt).unwrap();
}
}
// ---------------------------------------------------------------------------
// Mock MCTP client (copied from mctp-server tests)
// ---------------------------------------------------------------------------
/// A direct (in-process) MCTP client that wraps a `Server` via `RefCell`.
///
/// This provides the `MctpClient` trait interface for testing without IPC.
struct DirectClient<'a, S: Sender, const N: usize> {
server: &'a RefCell<Server<S, N>>,
}
impl<'a, S: Sender, const N: usize> DirectClient<'a, S, N> {
fn new(server: &'a RefCell<Server<S, N>>) -> Self {
Self { server }
}
}
impl<S: Sender, const N: usize> MctpClient for DirectClient<'_, S, N> {
fn req(&self, eid: u8) -> Result<Handle, MctpError> {
self.server.borrow_mut().req(eid)
}
fn listener(&self, msg_type: u8) -> Result<Handle, MctpError> {
self.server.borrow_mut().listener(msg_type)
}
fn get_eid(&self) -> u8 {
self.server.borrow().get_eid()
}
fn set_eid(&self, eid: u8) -> Result<(), MctpError> {
self.server.borrow_mut().set_eid(eid)
}
fn recv(
&self,
handle: Handle,
_timeout_millis: u32,
buf: &mut [u8],
) -> Result<RecvMetadata, MctpError> {
self.server
.borrow_mut()
.try_recv(handle, buf)
.ok_or(MctpError::from_code(ResponseCode::TimedOut))
}
fn send(
&self,
handle: Option<Handle>,
msg_type: u8,
eid: Option<u8>,
tag: Option<u8>,
integrity_check: bool,
buf: &[u8],
) -> Result<u8, MctpError> {
self.server
.borrow_mut()
.send(handle, msg_type, eid, tag, integrity_check, buf)
}
fn drop_handle(&self, handle: Handle) {
let _ = self.server.borrow_mut().unbind(handle);
}
}
// ---------------------------------------------------------------------------
// Tests
// ---------------------------------------------------------------------------
/// Test SPDM request/response roundtrip over MCTP transport.
///
/// This test:
/// 1. Creates two MCTP endpoints (requester EID 10, responder EID 20)
/// 2. Creates SPDM transports for each endpoint
/// 3. Sends a request buffer with pattern 0xAA
/// 4. Verifies the responder receives the same pattern
/// 5. Sends a response buffer with pattern 0x55
/// 6. Verifies the requester receives the same pattern
#[test]
fn spdm_mctp_request_response_roundtrip() {
// Test configuration constants
const REQUESTER_EID: u8 = 10;
const RESPONDER_EID: u8 = 20;
const REQUEST_PATTERN_SIZE: usize = 64;
const RESPONSE_PATTERN_SIZE: usize = 128;
const MESSAGE_BUFFER_SIZE: usize = 2048;
// -- Setup MCTP infrastructure --
// Requester MCTP server (EID 10)
let buf_requester = RefCell::new(Vec::new());
let sender_requester = BufferSender {
packets: &buf_requester,
};
let server_requester: RefCell<Server<_, { ServerConfig::MAX_OUTSTANDING }>> =
RefCell::new(Server::new(Eid(REQUESTER_EID), 0, sender_requester));
// Responder MCTP server (EID 20)
let buf_responder = RefCell::new(Vec::new());
let sender_responder = BufferSender {
packets: &buf_responder,
};
let server_responder: RefCell<Server<_, { ServerConfig::MAX_OUTSTANDING }>> =
RefCell::new(Server::new(Eid(RESPONDER_EID), 0, sender_responder));
// -- Setup SPDM transports --
let client_requester = DirectClient::new(&server_requester);
let mut transport_requester = MctpSpdmTransport::new_requester(client_requester, RESPONDER_EID);
let client_responder = DirectClient::new(&server_responder);
let mut transport_responder = MctpSpdmTransport::new_responder(client_responder);
// Initialize transports
transport_requester
.init_sequence()
.expect("Requester init should succeed");
transport_responder
.init_sequence()
.expect("Responder init should succeed");
// -- Phase 1: Send request, receive request --
// Create request buffer with pattern 0xAA
let request_pattern: [u8; REQUEST_PATTERN_SIZE] = [0xAA; REQUEST_PATTERN_SIZE];
let mut request_backing = [0u8; MESSAGE_BUFFER_SIZE];
let mut request_buf = MessageBuf::new(&mut request_backing);
request_buf
.put_data(request_pattern.len())
.expect("Should allocate request buffer");
request_buf
.data_mut(request_pattern.len())
.expect("Should get request buffer")
.copy_from_slice(&request_pattern);
// Requester sends request
transport_requester
.send_request(RESPONDER_EID, &mut request_buf)
.expect("Should send request");
// Transfer packets from requester to responder
transfer(&buf_requester, &mut server_responder.borrow_mut());
// Responder receives request
let mut received_request_backing = [0u8; MESSAGE_BUFFER_SIZE];
let mut received_request_buf = MessageBuf::new(&mut received_request_backing);
transport_responder
.receive_request(&mut received_request_buf)
.expect("Should receive request");
// Verify received request matches sent request
let received_request_len = received_request_buf.data_len();
assert_eq!(
received_request_len,
request_pattern.len(),
"Request length mismatch"
);
let received_request_data = received_request_buf
.data(received_request_len)
.expect("Should get received request data");
assert_eq!(
received_request_data, &request_pattern,
"Request pattern should match"
);
// -- Phase 2: Send response, receive response --
// Create response buffer with pattern 0x55
let response_pattern: [u8; RESPONSE_PATTERN_SIZE] = [0x55; RESPONSE_PATTERN_SIZE];
let mut response_backing = [0u8; MESSAGE_BUFFER_SIZE];
let mut response_buf = MessageBuf::new(&mut response_backing);
response_buf
.put_data(response_pattern.len())
.expect("Should allocate response buffer");
response_buf
.data_mut(response_pattern.len())
.expect("Should get response buffer")
.copy_from_slice(&response_pattern);
// Responder sends response
transport_responder
.send_response(&mut response_buf)
.expect("Should send response");
// Transfer packets from responder to requester
buf_requester.borrow_mut().clear(); // Clear previous packets
transfer(&buf_responder, &mut server_requester.borrow_mut());
// Requester receives response
let mut received_response_backing = [0u8; MESSAGE_BUFFER_SIZE];
let mut received_response_buf = MessageBuf::new(&mut received_response_backing);
transport_requester
.receive_response(&mut received_response_buf)
.expect("Should receive response");
// Verify received response matches sent response
let received_response_len = received_response_buf.data_len();
assert_eq!(
received_response_len,
response_pattern.len(),
"Response length mismatch"
);
let received_response_data = received_response_buf
.data(received_response_len)
.expect("Should get received response data");
assert_eq!(
received_response_data, &response_pattern,
"Response pattern should match"
);
}