1#![expect(missing_docs)]
36#![forbid(unsafe_code)]
37
38pub mod loopback;
39pub mod null;
40pub mod resolve;
41pub mod tests;
42
43use async_trait::async_trait;
44use bitfield_struct::bitfield;
45use futures::FutureExt;
46use futures::StreamExt;
47use futures::TryFutureExt;
48use futures::lock::Mutex;
49use futures_concurrency::future::Race;
50use guestmem::GuestMemory;
51use guestmem::GuestMemoryError;
52use inspect::InspectMut;
53use inspect_counters::Counter;
54use mesh::rpc::Rpc;
55use mesh::rpc::RpcSend;
56use null::NullEndpoint;
57use pal_async::driver::Driver;
58use std::future::pending;
59use std::sync::Arc;
60use std::task::Context;
61use std::task::Poll;
62use thiserror::Error;
63
64pub struct QueueConfig {
69 pub driver: Box<dyn Driver>,
70}
71
72#[async_trait]
83pub trait Endpoint: Send + Sync + InspectMut {
84 fn endpoint_type(&self) -> &'static str;
86
87 async fn get_queues(
89 &mut self,
90 config: Vec<QueueConfig>,
91 rss: Option<&RssConfig<'_>>,
92 queues: &mut Vec<Box<dyn Queue>>,
93 ) -> anyhow::Result<()>;
94
95 async fn stop(&mut self);
99
100 fn is_ordered(&self) -> bool;
104
105 fn tx_offload_support(&self) -> TxOffloadSupport {
107 TxOffloadSupport::default()
108 }
109
110 fn multiqueue_support(&self) -> MultiQueueSupport {
112 MultiQueueSupport {
113 max_queues: 1,
114 indirection_table_size: 0,
115 }
116 }
117
118 fn tx_fast_completions(&self) -> bool {
122 false
123 }
124
125 async fn set_data_path_to_guest_vf(&self, _use_vf: bool) -> anyhow::Result<()> {
128 Err(anyhow::Error::msg("Unsupported in current endpoint"))
129 }
130
131 async fn get_data_path_to_guest_vf(&self) -> anyhow::Result<bool> {
132 Err(anyhow::Error::msg("Unsupported in current endpoint"))
133 }
134
135 async fn wait_for_endpoint_action(&mut self) -> EndpointAction {
137 pending().await
138 }
139
140 fn link_speed(&self) -> u64 {
142 10 * 1000 * 1000 * 1000
145 }
146}
147
148#[derive(Debug, Copy, Clone)]
150pub struct MultiQueueSupport {
151 pub max_queues: u16,
153 pub indirection_table_size: u16,
155}
156
157#[derive(Debug, Copy, Clone, Default)]
159pub struct TxOffloadSupport {
160 pub ipv4_header: bool,
162 pub tcp: bool,
164 pub udp: bool,
166 pub tso: bool,
168 pub uso: bool,
170}
171
172#[derive(Debug, Clone)]
173pub struct RssConfig<'a> {
174 pub key: &'a [u8],
175 pub indirection_table: &'a [u16],
176 pub flags: u32, }
178
179#[derive(Error, Debug)]
180pub enum TxError {
181 #[error("error requiring queue restart. {0}")]
182 TryRestart(#[source] anyhow::Error),
183 #[error("unrecoverable error. {0}")]
184 Fatal(#[source] anyhow::Error),
185}
186pub trait BackendQueueStats {
187 fn rx_errors(&self) -> Counter;
188 fn tx_errors(&self) -> Counter;
189 fn rx_packets(&self) -> Counter;
190 fn tx_packets(&self) -> Counter;
191 fn tx_vlan_packets(&self) -> Counter {
192 Counter::new()
193 }
194 fn rx_vlan_packets(&self) -> Counter {
195 Counter::new()
196 }
197}
198
199#[async_trait]
216pub trait Queue: Send + InspectMut {
217 async fn update_target_vp(&mut self, target_vp: u32) {
219 let _ = target_vp;
220 }
221
222 fn poll_ready(&mut self, cx: &mut Context<'_>, pool: &mut dyn BufferAccess) -> Poll<()>;
224
225 fn rx_avail(&mut self, pool: &mut dyn BufferAccess, done: &[RxId]);
227
228 fn rx_poll(
230 &mut self,
231 pool: &mut dyn BufferAccess,
232 packets: &mut [RxId],
233 ) -> anyhow::Result<usize>;
234
235 fn tx_avail(
239 &mut self,
240 pool: &mut dyn BufferAccess,
241 segments: &[TxSegment],
242 ) -> anyhow::Result<(bool, usize)>;
243
244 fn tx_poll(&mut self, pool: &mut dyn BufferAccess, done: &mut [TxId])
246 -> Result<usize, TxError>;
247
248 fn queue_stats(&self) -> Option<&dyn BackendQueueStats> {
250 None }
252}
253
254pub trait BufferAccess {
265 fn guest_memory(&self) -> &GuestMemory;
267
268 fn write_data(&mut self, id: RxId, data: &[u8]);
270
271 fn push_guest_addresses(&self, id: RxId, buf: &mut Vec<RxBufferSegment>);
276
277 fn capacity(&self, id: RxId) -> u32;
279
280 fn write_header(&mut self, id: RxId, metadata: &RxMetadata);
282
283 fn write_packet(&mut self, id: RxId, metadata: &RxMetadata, data: &[u8]) {
285 self.write_data(id, data);
286 self.write_header(id, metadata);
287 }
288
289 fn write_packet_segments(&mut self, id: RxId, metadata: &RxMetadata, segments: &[&[u8]]) {
302 if let [segment] = segments {
303 self.write_packet(id, metadata, segment);
304 return;
305 }
306 let total = segments.iter().map(|s| s.len()).sum();
307 let mut data = Vec::with_capacity(total);
308 for segment in segments {
309 data.extend_from_slice(segment);
310 }
311 self.write_packet(id, metadata, &data);
312 }
313}
314
315pub const ETHERNET_HEADER_LEN: u32 = 14;
316pub const ETHERNET_VLAN_HEADER_LEN: u32 = 18;
317
318pub const IPV4_MIN_HEADER_LEN: u16 = 20;
319pub const IPV6_MIN_HEADER_LEN: u16 = 40;
320
321#[bitfield(u16)]
322pub struct VlanMetadata {
323 #[bits(3)]
325 pub priority: u8,
326 pub drop_eligible_indicator: bool,
330 #[bits(12)]
332 pub vlan_id: u16,
333}
334
335#[derive(Debug, Copy, Clone)]
337#[repr(transparent)]
338pub struct RxId(pub u32);
339
340#[derive(Debug, Copy, Clone)]
342pub struct RxBufferSegment {
343 pub gpa: u64,
345 pub len: u32,
347}
348
349#[derive(Debug, Copy, Clone)]
351pub struct RxMetadata {
352 pub offset: usize,
354 pub len: usize,
356 pub ip_checksum: RxChecksumState,
358 pub l4_checksum: RxChecksumState,
360 pub l4_protocol: L4Protocol,
362 pub vlan: Option<VlanMetadata>,
366}
367
368impl Default for RxMetadata {
369 fn default() -> Self {
370 Self {
371 offset: 0,
372 len: 0,
373 ip_checksum: RxChecksumState::Unknown,
374 l4_checksum: RxChecksumState::Unknown,
375 l4_protocol: L4Protocol::Unknown,
376 vlan: None,
377 }
378 }
379}
380
381#[derive(Debug, Copy, Clone, PartialEq, Eq)]
383pub enum L3Protocol {
384 Unknown,
385 Ipv4,
386 Ipv6,
387}
388
389#[derive(Debug, Copy, Clone, PartialEq, Eq)]
391pub enum L4Protocol {
392 Unknown,
393 Tcp,
394 Udp,
395}
396
397#[derive(Debug, Copy, Clone, PartialEq, Eq)]
399pub enum RxChecksumState {
400 Unknown,
402 Good,
404 Bad,
406 ValidatedButWrong,
412}
413
414impl RxChecksumState {
415 pub fn is_valid(self) -> bool {
417 self == Self::Good || self == Self::ValidatedButWrong
418 }
419}
420
421#[derive(Debug, Copy, Clone)]
423#[repr(transparent)]
424pub struct TxId(pub u32);
425
426#[derive(Debug, Clone)]
427pub enum TxSegmentType {
429 Head(TxMetadata),
431 Tail,
433}
434
435#[derive(Debug, Clone)]
436pub struct TxMetadata {
438 pub id: TxId,
440 pub segment_count: u8,
442 pub flags: TxFlags,
444 pub len: u32,
446 pub l2_len: u8,
449 pub l3_len: u16,
452 pub l4_len: u8,
455 pub transport_header_offset: u16,
458 pub max_segment_size: u16,
462 pub vlan: Option<VlanMetadata>,
466}
467
468#[bitfield(u8)]
470pub struct TxFlags {
471 pub offload_ip_header_checksum: bool,
475 pub offload_tcp_checksum: bool,
479 pub offload_udp_checksum: bool,
483 pub offload_tcp_segmentation: bool,
489 pub is_ipv4: bool,
491 pub is_ipv6: bool,
493 pub offload_udp_segmentation: bool,
496 #[bits(1)]
497 _reserved: u8,
498}
499
500impl Default for TxMetadata {
501 fn default() -> Self {
502 Self {
503 id: TxId(0),
504 segment_count: 0,
505 len: 0,
506 flags: TxFlags::new(),
507 l2_len: 0,
508 l3_len: 0,
509 l4_len: 0,
510 transport_header_offset: 0,
511 max_segment_size: 0,
512 vlan: None,
513 }
514 }
515}
516
517#[derive(Debug, Clone)]
518pub struct TxSegment {
520 pub ty: TxSegmentType,
522 pub gpa: u64,
524 pub len: u32,
526}
527
528pub fn packet_count(mut segments: &[TxSegment]) -> usize {
530 let mut packet_count = 0;
531 while let Some(head) = segments.first() {
532 let TxSegmentType::Head(metadata) = &head.ty else {
533 unreachable!()
534 };
535 segments = &segments[metadata.segment_count as usize..];
536 packet_count += 1;
537 }
538 packet_count
539}
540
541pub fn next_packet(segments: &[TxSegment]) -> (&TxMetadata, &[TxSegment], &[TxSegment]) {
544 let metadata = if let TxSegmentType::Head(metadata) = &segments[0].ty {
545 metadata
546 } else {
547 unreachable!();
548 };
549 let (this, rest) = segments.split_at(metadata.segment_count.into());
550 (metadata, this, rest)
551}
552
553pub fn linearize(
556 pool: &dyn BufferAccess,
557 segments: &mut &[TxSegment],
558) -> Result<Vec<u8>, GuestMemoryError> {
559 let (head, this, rest) = next_packet(segments);
560 let mut v = vec![0; head.len as usize];
561 let mut offset = 0;
562 let mem = pool.guest_memory();
563 for segment in this {
564 let dest = &mut v[offset..offset + segment.len as usize];
565 mem.read_at(segment.gpa, dest)?;
566 offset += segment.len as usize;
567 }
568 assert_eq!(v.len(), offset);
569 *segments = rest;
570 Ok(v)
571}
572
573#[derive(PartialEq, Debug)]
574pub enum EndpointAction {
575 RestartRequired,
576 LinkStatusNotify(bool),
577}
578
579enum DisconnectableEndpointUpdate {
580 EndpointConnected(Box<dyn Endpoint>),
581 EndpointDisconnected(Rpc<(), Option<Box<dyn Endpoint>>>),
582}
583
584pub struct DisconnectableEndpointControl {
585 send_update: mesh::Sender<DisconnectableEndpointUpdate>,
586 is_ordered: Option<bool>,
587}
588
589impl DisconnectableEndpointControl {
590 pub fn connect(&mut self, endpoint: Box<dyn Endpoint>) -> anyhow::Result<()> {
591 let new_is_ordered = endpoint.is_ordered();
592 if let Some(is_ordered) = self.is_ordered {
593 anyhow::ensure!(
594 !is_ordered || new_is_ordered,
595 "network endpoint cannot be reattached as unordered after being ordered"
596 );
597 } else {
598 self.is_ordered = Some(new_is_ordered);
599 }
600 self.send_update
601 .send(DisconnectableEndpointUpdate::EndpointConnected(endpoint));
602 Ok(())
603 }
604
605 pub async fn disconnect(&mut self) -> anyhow::Result<Option<Box<dyn Endpoint>>> {
606 self.send_update
607 .call(DisconnectableEndpointUpdate::EndpointDisconnected, ())
608 .map_err(anyhow::Error::from)
609 .await
610 }
611}
612
613pub struct DisconnectableEndpointCachedState {
614 is_ordered: bool,
615 tx_offload_support: TxOffloadSupport,
616 multiqueue_support: MultiQueueSupport,
617 tx_fast_completions: bool,
618 link_speed: u64,
619}
620
621pub struct DisconnectableEndpoint {
622 endpoint: Option<Box<dyn Endpoint>>,
623 null_endpoint: Box<dyn Endpoint>,
624 cached_state: Option<DisconnectableEndpointCachedState>,
625 receive_update: Arc<Mutex<mesh::Receiver<DisconnectableEndpointUpdate>>>,
626 notify_disconnect_complete: Option<(
627 Rpc<(), Option<Box<dyn Endpoint>>>,
628 Option<Box<dyn Endpoint>>,
629 )>,
630}
631
632impl InspectMut for DisconnectableEndpoint {
633 fn inspect_mut(&mut self, req: inspect::Request<'_>) {
634 self.current_mut().inspect_mut(req)
635 }
636}
637
638impl DisconnectableEndpoint {
639 pub fn new() -> (Self, DisconnectableEndpointControl) {
640 let (endpoint_tx, endpoint_rx) = mesh::channel();
641 let control = DisconnectableEndpointControl {
642 send_update: endpoint_tx,
643 is_ordered: None,
644 };
645 (
646 Self {
647 endpoint: None,
648 null_endpoint: Box::new(NullEndpoint::new()),
649 cached_state: None,
650 receive_update: Arc::new(Mutex::new(endpoint_rx)),
651 notify_disconnect_complete: None,
652 },
653 control,
654 )
655 }
656
657 fn current(&self) -> &dyn Endpoint {
658 self.endpoint
659 .as_ref()
660 .unwrap_or(&self.null_endpoint)
661 .as_ref()
662 }
663
664 fn current_mut(&mut self) -> &mut dyn Endpoint {
665 self.endpoint
666 .as_mut()
667 .unwrap_or(&mut self.null_endpoint)
668 .as_mut()
669 }
670}
671
672#[async_trait]
673impl Endpoint for DisconnectableEndpoint {
674 fn endpoint_type(&self) -> &'static str {
675 self.current().endpoint_type()
676 }
677
678 async fn get_queues(
679 &mut self,
680 config: Vec<QueueConfig>,
681 rss: Option<&RssConfig<'_>>,
682 queues: &mut Vec<Box<dyn Queue>>,
683 ) -> anyhow::Result<()> {
684 self.current_mut().get_queues(config, rss, queues).await
685 }
686
687 async fn stop(&mut self) {
688 self.current_mut().stop().await
689 }
690
691 fn is_ordered(&self) -> bool {
692 self.cached_state
693 .as_ref()
694 .expect("Endpoint needs connected at least once before use")
695 .is_ordered
696 }
697
698 fn tx_offload_support(&self) -> TxOffloadSupport {
699 self.cached_state
700 .as_ref()
701 .expect("Endpoint needs connected at least once before use")
702 .tx_offload_support
703 }
704
705 fn multiqueue_support(&self) -> MultiQueueSupport {
706 self.cached_state
707 .as_ref()
708 .expect("Endpoint needs connected at least once before use")
709 .multiqueue_support
710 }
711
712 fn tx_fast_completions(&self) -> bool {
713 self.cached_state
714 .as_ref()
715 .expect("Endpoint needs connected at least once before use")
716 .tx_fast_completions
717 }
718
719 async fn set_data_path_to_guest_vf(&self, use_vf: bool) -> anyhow::Result<()> {
720 self.current().set_data_path_to_guest_vf(use_vf).await
721 }
722
723 async fn get_data_path_to_guest_vf(&self) -> anyhow::Result<bool> {
724 self.current().get_data_path_to_guest_vf().await
725 }
726
727 async fn wait_for_endpoint_action(&mut self) -> EndpointAction {
728 if let Some((rpc, old_endpoint)) = self.notify_disconnect_complete.take() {
731 rpc.handle(async |_| old_endpoint).await;
732 }
733
734 enum Message {
735 DisconnectableEndpointUpdate(DisconnectableEndpointUpdate),
736 UpdateFromEndpoint(EndpointAction),
737 }
738 let receiver = self.receive_update.clone();
739 let mut receive_update = receiver.lock().await;
740 let update = async {
741 match receive_update.next().await {
742 Some(m) => Message::DisconnectableEndpointUpdate(m),
743 None => {
744 pending::<()>().await;
745 unreachable!()
746 }
747 }
748 };
749 let ep_update = self
750 .current_mut()
751 .wait_for_endpoint_action()
752 .map(Message::UpdateFromEndpoint);
753 let m = (update, ep_update).race().await;
754 match m {
755 Message::DisconnectableEndpointUpdate(
756 DisconnectableEndpointUpdate::EndpointConnected(endpoint),
757 ) => {
758 let old_endpoint = self.endpoint.take();
759 assert!(old_endpoint.is_none());
760 self.endpoint = Some(endpoint);
761 let new_is_ordered = self.current().is_ordered();
762 let is_ordered = if let Some(prev) = &self.cached_state {
763 assert!(
764 !prev.is_ordered || new_is_ordered,
765 "network endpoint reattached as unordered after being ordered"
766 );
767 prev.is_ordered
768 } else {
769 new_is_ordered
770 };
771 self.cached_state = Some(DisconnectableEndpointCachedState {
772 is_ordered,
773 tx_offload_support: self.current().tx_offload_support(),
774 multiqueue_support: self.current().multiqueue_support(),
775 tx_fast_completions: self.current().tx_fast_completions(),
776 link_speed: self.current().link_speed(),
777 });
778 EndpointAction::RestartRequired
779 }
780 Message::DisconnectableEndpointUpdate(
781 DisconnectableEndpointUpdate::EndpointDisconnected(rpc),
782 ) => {
783 let old_endpoint = self.endpoint.take();
784 self.notify_disconnect_complete = Some((rpc, old_endpoint));
789 EndpointAction::RestartRequired
790 }
791 Message::UpdateFromEndpoint(update) => update,
792 }
793 }
794
795 fn link_speed(&self) -> u64 {
796 self.cached_state
797 .as_ref()
798 .expect("Endpoint needs connected at least once before use")
799 .link_speed
800 }
801}
802
803#[cfg(test)]
804mod disconnectable_endpoint_tests {
805 use super::*;
806 use test_with_tracing::test;
807
808 #[derive(InspectMut)]
809 struct TestEndpoint {
810 is_ordered: bool,
811 }
812
813 #[async_trait]
814 impl Endpoint for TestEndpoint {
815 fn endpoint_type(&self) -> &'static str {
816 "test"
817 }
818
819 async fn get_queues(
820 &mut self,
821 _config: Vec<QueueConfig>,
822 _rss: Option<&RssConfig<'_>>,
823 _queues: &mut Vec<Box<dyn Queue>>,
824 ) -> anyhow::Result<()> {
825 unreachable!()
826 }
827
828 async fn stop(&mut self) {
829 unreachable!()
830 }
831
832 fn is_ordered(&self) -> bool {
833 self.is_ordered
834 }
835 }
836
837 #[test]
838 fn connect_pins_endpoint_ordering() {
839 let (_endpoint, mut control) = DisconnectableEndpoint::new();
840 control
841 .connect(Box::new(TestEndpoint { is_ordered: true }))
842 .unwrap();
843
844 let err = control
845 .connect(Box::new(TestEndpoint { is_ordered: false }))
846 .unwrap_err();
847 assert_eq!(
848 err.to_string(),
849 "network endpoint cannot be reattached as unordered after being ordered"
850 );
851
852 let (_endpoint, mut control) = DisconnectableEndpoint::new();
853 control
854 .connect(Box::new(TestEndpoint { is_ordered: false }))
855 .unwrap();
856 control
857 .connect(Box::new(TestEndpoint { is_ordered: true }))
858 .unwrap();
859 }
860}