use crate::node::session_wire::FSP_HEADER_SIZE;
use crate::node::wire::ESTABLISHED_HEADER_SIZE;
use crate::transport::udp::socket::AsyncUdpSocket;
#[cfg(not(target_os = "macos"))]
use crossbeam_channel::{Receiver, SendError, Sender, TrySendError, bounded};
use ring::aead::{Aad, LessSafeKey, Nonce};
#[cfg(target_os = "macos")]
use std::cell::RefCell;
#[cfg(target_os = "macos")]
use std::collections::BTreeMap;
use std::collections::HashMap;
#[cfg(target_os = "macos")]
use std::collections::VecDeque;
use std::net::SocketAddr;
#[cfg(unix)]
use std::os::unix::io::{AsRawFd, RawFd};
use std::sync::Arc;
use std::sync::OnceLock;
use std::sync::{Condvar, Mutex};
use tracing::{debug, trace, warn};
pub(crate) struct FmpSendJob {
pub cipher: LessSafeKey,
pub counter: u64,
pub wire_buf: Vec<u8>,
pub fsp_seal: Option<FspSealJob>,
pub send_target: SelectedSendTarget,
#[cfg_attr(target_os = "macos", allow(dead_code))]
pub endpoint_flow_dispatch_key: Option<u64>,
pub bulk_endpoint_data: bool,
pub drop_on_backpressure: bool,
#[cfg_attr(target_os = "macos", allow(dead_code))]
pub scheduling_weight: u8,
pub queued_at: Option<crate::perf_profile::TraceStamp>,
}
pub(crate) struct FspSealJob {
pub cipher: LessSafeKey,
pub counter: u64,
pub aad_offset: usize,
pub plaintext_offset: usize,
}
#[derive(Clone)]
pub(crate) struct SelectedSendTarget {
socket: AsyncUdpSocket,
#[cfg(any(target_os = "linux", target_os = "macos"))]
connected_socket:
Option<std::sync::Arc<crate::transport::udp::connected_peer::ConnectedPeerSocket>>,
dest_addr: SocketAddr,
key: SendTargetKey,
}
impl SelectedSendTarget {
pub(crate) fn new(
socket: AsyncUdpSocket,
#[cfg(any(target_os = "linux", target_os = "macos"))] connected_socket: Option<
std::sync::Arc<crate::transport::udp::connected_peer::ConnectedPeerSocket>,
>,
dest_addr: SocketAddr,
) -> Self {
let key = SendTargetKey::from_parts(
&socket,
#[cfg(any(target_os = "linux", target_os = "macos"))]
connected_socket.as_ref(),
dest_addr,
);
Self {
socket,
#[cfg(any(target_os = "linux", target_os = "macos"))]
connected_socket,
dest_addr,
key,
}
}
fn key(&self) -> SendTargetKey {
self.key
}
#[cfg(unix)]
fn dest_addr(&self) -> SocketAddr {
self.dest_addr
}
#[cfg(unix)]
fn fd_and_connected(&self) -> (RawFd, bool) {
#[cfg(any(target_os = "linux", target_os = "macos"))]
if let Some(socket) = self.connected_socket.as_ref() {
return (socket.as_raw_fd(), true);
}
(self.socket.as_raw_fd(), false)
}
}
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)]
struct SendTargetKey {
#[cfg(unix)]
socket_fd: RawFd,
#[cfg(any(target_os = "linux", target_os = "macos"))]
connected_fd: Option<RawFd>,
dest_addr: SocketAddr,
}
impl SendTargetKey {
fn from_parts(
#[cfg_attr(not(unix), allow(unused_variables))]
socket: &AsyncUdpSocket,
#[cfg(any(target_os = "linux", target_os = "macos"))] connected_socket: Option<
&std::sync::Arc<crate::transport::udp::connected_peer::ConnectedPeerSocket>,
>,
dest_addr: SocketAddr,
) -> Self {
Self {
#[cfg(unix)]
socket_fd: socket.as_raw_fd(),
#[cfg(any(target_os = "linux", target_os = "macos"))]
connected_fd: connected_socket.map(|socket| socket.as_raw_fd()),
dest_addr,
}
}
}
impl FmpSendJob {
fn send_target_key(&self) -> SendTargetKey {
self.send_target.key()
}
}
#[cfg(unix)]
pub(in crate::node) fn fmp_send_job_batches_share_bulk_target(
left: &[FmpSendJob],
right: &[FmpSendJob],
) -> bool {
let Some(first) = left.first().or_else(|| right.first()) else {
return true;
};
if !first.bulk_endpoint_data {
return false;
}
let target_key = first.send_target_key();
left.iter()
.chain(right.iter())
.all(|job| job.bulk_endpoint_data && job.send_target_key() == target_key)
}
#[cfg(unix)]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
enum SelectedSendLane {
Priority,
Bulk,
}
#[cfg(unix)]
impl SelectedSendLane {
fn for_endpoint_data(bulk_endpoint_data: bool) -> Self {
if bulk_endpoint_data {
Self::Bulk
} else {
Self::Priority
}
}
}
#[cfg(unix)]
#[derive(Clone, Copy)]
enum SelectedSendGroupSplitReason {
Target,
Lane,
Backpressure,
#[cfg(target_os = "linux")]
PacketCap,
}
#[cfg(unix)]
fn record_selected_send_group_split(reason: SelectedSendGroupSplitReason) {
match reason {
SelectedSendGroupSplitReason::Target => {
crate::perf_profile::record_fmp_send_group_split_target()
}
SelectedSendGroupSplitReason::Lane => crate::perf_profile::record_fmp_send_group_split_lane(),
SelectedSendGroupSplitReason::Backpressure => {
crate::perf_profile::record_fmp_send_group_split_backpressure()
}
#[cfg(target_os = "linux")]
SelectedSendGroupSplitReason::PacketCap => {
crate::perf_profile::record_fmp_send_group_split_packet_cap()
}
}
}
#[cfg(unix)]
struct SelectedSendBatch {
send_target: SelectedSendTarget,
target_key: SendTargetKey,
lane: SelectedSendLane,
wire_packets: Vec<Vec<u8>>,
drop_on_backpressure: bool,
#[cfg(target_os = "linux")]
gso_segment_len: usize,
#[cfg(target_os = "linux")]
gso_last_len: usize,
#[cfg(target_os = "linux")]
gso_prefix_uniform: bool,
#[cfg(target_os = "linux")]
gso_eligible_sizes: bool,
}
#[cfg(unix)]
impl SelectedSendBatch {
#[cfg(test)]
fn new(
send_target: SelectedSendTarget,
target_key: SendTargetKey,
wire_packet: Vec<u8>,
drop_on_backpressure: bool,
) -> Self {
Self::new_with_capacity(
send_target,
target_key,
SelectedSendLane::Bulk,
wire_packet,
drop_on_backpressure,
1,
)
}
fn new_with_capacity(
send_target: SelectedSendTarget,
target_key: SendTargetKey,
lane: SelectedSendLane,
wire_packet: Vec<u8>,
drop_on_backpressure: bool,
packet_capacity: usize,
) -> Self {
debug_assert_eq!(
send_target.key(),
target_key,
"selected send batch must keep the queued target key"
);
#[cfg(target_os = "linux")]
let gso_segment_len = wire_packet.len();
let mut wire_packets = Vec::with_capacity(packet_capacity.max(1));
wire_packets.push(wire_packet);
Self {
send_target,
target_key,
lane,
wire_packets,
drop_on_backpressure,
#[cfg(target_os = "linux")]
gso_segment_len,
#[cfg(target_os = "linux")]
gso_last_len: gso_segment_len,
#[cfg(target_os = "linux")]
gso_prefix_uniform: gso_segment_len > 0,
#[cfg(target_os = "linux")]
gso_eligible_sizes: false,
}
}
#[cfg_attr(not(test), allow(dead_code))]
fn target_key(&self) -> SendTargetKey {
self.target_key
}
#[cfg_attr(not(target_os = "linux"), allow(dead_code))]
fn lane(&self) -> SelectedSendLane {
self.lane
}
fn split_reason_for(
&self,
target_key: SendTargetKey,
lane: SelectedSendLane,
drop_on_backpressure: bool,
) -> Option<SelectedSendGroupSplitReason> {
if self.target_key != target_key {
return Some(SelectedSendGroupSplitReason::Target);
}
if self.lane != lane {
return Some(SelectedSendGroupSplitReason::Lane);
}
if self.drop_on_backpressure != drop_on_backpressure {
return Some(SelectedSendGroupSplitReason::Backpressure);
}
None
}
fn push(&mut self, wire_packet: Vec<u8>, drop_on_backpressure: bool) {
debug_assert_eq!(
self.drop_on_backpressure, drop_on_backpressure,
"send batches keep one backpressure policy so bulk remains droppable"
);
#[cfg(target_os = "linux")]
{
let packet_len = wire_packet.len();
self.gso_prefix_uniform &= self.gso_last_len == self.gso_segment_len;
self.gso_last_len = packet_len;
self.gso_eligible_sizes = self.gso_prefix_uniform && packet_len <= self.gso_segment_len;
}
self.wire_packets.push(wire_packet);
}
#[cfg_attr(any(not(target_os = "linux"), not(test)), allow(dead_code))]
fn drop_on_backpressure(&self) -> bool {
self.drop_on_backpressure
}
fn packet_count(&self) -> usize {
self.wire_packets.len()
}
#[cfg(target_os = "linux")]
fn bulk_wire_bytes(&self) -> Option<usize> {
if self.lane != SelectedSendLane::Bulk {
return None;
}
Some(
self.wire_packets
.iter()
.map(Vec::len)
.fold(0usize, usize::saturating_add),
)
}
#[cfg(target_os = "linux")]
fn append_group_up_to(
&mut self,
other: SelectedSendBatch,
max_packets: usize,
) -> Option<SelectedSendBatch> {
if let Some(reason) =
self.split_reason_for(other.target_key, other.lane, other.drop_on_backpressure)
{
record_selected_send_group_split(reason);
return Some(other);
}
if self.packet_count() >= max_packets {
record_selected_send_group_split(SelectedSendGroupSplitReason::PacketCap);
return Some(other);
}
let SelectedSendBatch {
send_target,
target_key,
lane,
wire_packets,
drop_on_backpressure,
..
} = other;
let original_len = wire_packets.len();
let available = max_packets.saturating_sub(self.packet_count());
let mut packets = wire_packets.into_iter();
for _ in 0..available {
let Some(packet) = packets.next() else {
return None;
};
self.push(packet, drop_on_backpressure);
}
let first_remaining = packets.next()?;
let remaining_capacity = original_len.saturating_sub(available).max(1);
let mut remainder = SelectedSendBatch::new_with_capacity(
send_target,
target_key,
lane,
first_remaining,
drop_on_backpressure,
remaining_capacity,
);
for packet in packets {
remainder.push(packet, drop_on_backpressure);
}
record_selected_send_group_split(SelectedSendGroupSplitReason::PacketCap);
Some(remainder)
}
#[cfg(target_os = "linux")]
fn gso_eligible_sizes(&self) -> bool {
self.gso_eligible_sizes
}
#[cfg(test)]
fn wire_packet_capacity(&self) -> usize {
self.wire_packets.capacity()
}
fn into_parts(self) -> (SelectedSendTarget, Vec<Vec<u8>>, bool) {
(
self.send_target,
self.wire_packets,
self.drop_on_backpressure,
)
}
}
#[cfg(target_os = "linux")]
struct LinuxSendBatchAttempt {
send_target: SelectedSendTarget,
wire_packets: Vec<Vec<u8>>,
gso_eligible_sizes: bool,
drop_on_backpressure: bool,
backpressure: SendBackpressurePacer,
sent: usize,
}
#[cfg(target_os = "linux")]
impl LinuxSendBatchAttempt {
fn from_batch(batch: SelectedSendBatch) -> Self {
let gso_eligible_sizes = batch.gso_eligible_sizes();
debug_assert_eq!(
gso_eligible_sizes,
gso_eligible_sizes_ref(&batch.wire_packets)
);
let (send_target, wire_packets, drop_on_backpressure) = batch.into_parts();
Self {
send_target,
wire_packets,
gso_eligible_sizes,
drop_on_backpressure,
backpressure: SendBackpressurePacer::default(),
sent: 0,
}
}
#[cfg(test)]
fn target_key(&self) -> SendTargetKey {
self.send_target.key()
}
fn target_parts(&self) -> (std::os::unix::io::RawFd, bool, SocketAddr) {
let (fd, connected) = self.send_target.fd_and_connected();
(fd, connected, self.send_target.dest_addr())
}
fn gso_eligible_sizes(&self) -> bool {
self.gso_eligible_sizes
}
fn remaining_packets(&self) -> &[Vec<u8>] {
&self.wire_packets[self.sent..]
}
fn is_complete(&self) -> bool {
self.sent >= self.wire_packets.len()
}
fn mark_sent(&mut self, n: usize) {
if n == 0 {
return;
}
debug_assert!(
self.sent + n <= self.wire_packets.len(),
"sendmmsg reported more packets than remained in the batch"
);
let n = n.min(self.wire_packets.len().saturating_sub(self.sent));
self.sent += n;
self.backpressure.record_success();
let (_, connected, _) = self.target_parts();
record_udp_send_path(connected, n as u64);
}
fn handle_backpressure(&mut self, err: &std::io::Error) -> SendBackpressureDecision {
let pacer_requested_drop = self.backpressure.pause(err);
self.handle_backpressure_request(pacer_requested_drop, err)
}
fn handle_backpressure_request(
&mut self,
pacer_requested_drop: bool,
err: &std::io::Error,
) -> SendBackpressureDecision {
let decision = send_backpressure_decision(pacer_requested_drop, self.drop_on_backpressure);
if matches!(decision, SendBackpressureDecision::DropCurrentBulk) && !self.is_complete() {
record_udp_send_backpressure_drop(err);
self.sent += 1;
}
decision
}
}
#[cfg(all(unix, not(target_os = "linux")))]
struct DirectSendBatchAttempt {
send_target: SelectedSendTarget,
wire_packets: Vec<Vec<u8>>,
drop_on_backpressure: bool,
backpressure: SendBackpressurePacer,
sent: usize,
}
#[cfg(all(unix, not(target_os = "linux")))]
impl DirectSendBatchAttempt {
fn from_batch(batch: SelectedSendBatch) -> Self {
let (send_target, wire_packets, drop_on_backpressure) = batch.into_parts();
Self {
send_target,
wire_packets,
drop_on_backpressure,
backpressure: SendBackpressurePacer::default(),
sent: 0,
}
}
#[cfg(test)]
fn target_key(&self) -> SendTargetKey {
self.send_target.key()
}
fn target_parts(&self) -> (RawFd, bool, SocketAddr) {
let (fd, connected) = self.send_target.fd_and_connected();
(fd, connected, self.send_target.dest_addr())
}
#[cfg(test)]
fn remaining_packets(&self) -> &[Vec<u8>] {
&self.wire_packets[self.sent..]
}
#[cfg(target_os = "macos")]
fn current_packet_len(&self) -> Option<usize> {
self.wire_packets.get(self.sent).map(Vec::len)
}
fn is_complete(&self) -> bool {
self.sent >= self.wire_packets.len()
}
fn mark_current_sent(&mut self) {
if self.is_complete() {
return;
}
self.sent += 1;
self.backpressure.record_success();
let (_, connected, _) = self.target_parts();
record_udp_send_path(connected, 1);
}
fn handle_backpressure(&mut self, err: &std::io::Error) -> SendBackpressureDecision {
let pacer_requested_drop = self.backpressure.pause(err);
self.handle_backpressure_request(pacer_requested_drop, err)
}
fn handle_backpressure_request(
&mut self,
pacer_requested_drop: bool,
err: &std::io::Error,
) -> SendBackpressureDecision {
let decision = send_backpressure_decision(pacer_requested_drop, self.drop_on_backpressure);
if matches!(decision, SendBackpressureDecision::DropCurrentBulk) && !self.is_complete() {
record_udp_send_backpressure_drop(err);
self.sent += 1;
}
decision
}
fn send_current(&mut self) -> std::io::Result<()> {
let (fd, connected, dest_addr) = self.target_parts();
loop {
let Some(data) = self.wire_packets.get(self.sent).map(Vec::as_slice) else {
return Ok(());
};
let result = if connected {
send_connected_raw(fd, data)
} else {
send_one_raw(fd, data, &dest_addr)
};
match result {
Ok(_) => {
self.mark_current_sent();
return Ok(());
}
Err(err) if is_send_backpressure(&err) => {
if matches!(
self.handle_backpressure(&err),
SendBackpressureDecision::DropCurrentBulk
) {
return Ok(());
}
}
Err(err) => return Err(err),
}
}
}
}
#[cfg(unix)]
#[cfg(test)]
fn push_selected_send_batch(
groups: &mut Vec<SelectedSendBatch>,
send_target: SelectedSendTarget,
target_key: SendTargetKey,
wire_packet: Vec<u8>,
drop_on_backpressure: bool,
) {
push_selected_send_batch_with_lane_and_capacity(
groups,
send_target,
target_key,
SelectedSendLane::Bulk,
wire_packet,
drop_on_backpressure,
1,
)
}
#[cfg(unix)]
#[cfg(test)]
fn push_selected_send_batch_with_capacity(
groups: &mut Vec<SelectedSendBatch>,
send_target: SelectedSendTarget,
target_key: SendTargetKey,
wire_packet: Vec<u8>,
drop_on_backpressure: bool,
packet_capacity: usize,
) {
push_selected_send_batch_with_lane_and_capacity(
groups,
send_target,
target_key,
SelectedSendLane::Bulk,
wire_packet,
drop_on_backpressure,
packet_capacity,
);
}
#[cfg(unix)]
fn push_selected_send_batch_with_lane_and_capacity(
groups: &mut Vec<SelectedSendBatch>,
send_target: SelectedSendTarget,
target_key: SendTargetKey,
lane: SelectedSendLane,
wire_packet: Vec<u8>,
drop_on_backpressure: bool,
packet_capacity: usize,
) {
if let Some(group) = groups.last_mut() {
if let Some(reason) = group.split_reason_for(target_key, lane, drop_on_backpressure) {
record_selected_send_group_split(reason);
} else {
group.push(wire_packet, drop_on_backpressure);
return;
}
}
groups.push(SelectedSendBatch::new_with_capacity(
send_target,
target_key,
lane,
wire_packet,
drop_on_backpressure,
packet_capacity,
));
}
#[cfg(target_os = "linux")]
fn append_linux_wg_ready_send_groups(
groups: &mut Vec<SelectedSendBatch>,
ready_groups: Vec<SelectedSendBatch>,
max_packets_per_group: usize,
) {
let max_packets_per_group = max_packets_per_group.clamp(1, LINUX_UDP_SEND_BATCH_MAX);
for group in ready_groups {
match groups.last_mut() {
Some(last) => {
if let Some(remainder) = last.append_group_up_to(group, max_packets_per_group) {
groups.push(remainder);
}
}
None => groups.push(group),
}
}
}
#[cfg(unix)]
fn selected_send_group_stats(groups: &[SelectedSendBatch]) -> (usize, usize, usize) {
let mut packets = 0usize;
let mut single_groups = 0usize;
for group in groups {
let count = group.packet_count();
packets = packets.saturating_add(count);
if count == 1 {
single_groups = single_groups.saturating_add(1);
}
}
(groups.len(), packets, single_groups)
}
#[cfg(unix)]
fn record_selected_send_groups(groups: &[SelectedSendBatch]) {
let (group_count, packets, single_groups) = selected_send_group_stats(groups);
crate::perf_profile::record_fmp_send_groups(group_count, packets, single_groups);
}