Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1231,6 +1231,7 @@ pub use crate::error::Error;
pub use crate::multipath_scheduler::MultipathAlgorithm;
pub use crate::packet::PacketHeader;
pub use crate::tls::CertCompressionAlgorithm;
pub use crate::packet::PacketType;
pub use crate::tls::TlsConfig;
pub use crate::tls::TlsConfigSelector;

Expand Down
2 changes: 1 addition & 1 deletion src/packet.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ const RETRY_INTEGRITY_NONCE_V1: [u8; aead::NONCE_LEN] = [
];

/// QUIC packet type.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum PacketType {
/// The Version Negotiation packet is a response to a client packet that
/// contains a version that is not supported by the server.
Expand Down
63 changes: 62 additions & 1 deletion tools/src/bin/tquic_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,8 @@ use tquic::TlsConfig;
use tquic::TransportHandler;
use tquic_tools::ApplicationProto;
use tquic_tools::CertCompressionAlgorithmArg;
use tquic_tools::LossPacketType;
use tquic_tools::PacketLossConfig;
use tquic_tools::QuicSocket;
use tquic_tools::Result;

Expand Down Expand Up @@ -340,6 +342,40 @@ pub struct ClientOpt {
/// The range of the request, like "0-1023".
#[clap(long, value_name = "RANGE", help_heading = "Protocol")]
pub range: Option<String>,
/// Packet loss rate (0.0 to 1.0) for testing.
#[clap(
long,
default_value = "0.0",
value_name = "RATE",
help_heading = "Packet Loss"
)]
pub packet_loss_rate: f64,

/// Specific packet numbers to drop (comma-separated).
#[clap(
long,
value_delimiter = ',',
value_name = "NUMBERS",
help_heading = "Packet Loss"
)]
pub packet_loss_numbers: Vec<u64>,

/// Packet types to drop.
#[clap(
long,
value_delimiter = ',',
value_name = "TYPES",
help_heading = "Packet Loss"
)]
pub packet_loss_types: Vec<LossPacketType>,

/// Apply packet loss to incoming packets.
#[clap(long, help_heading = "Packet Loss")]
pub packet_loss_incoming: bool,

/// Apply packet loss to outgoing packets.
#[clap(long, help_heading = "Packet Loss")]
pub packet_loss_outgoing: bool,
}

const MAX_BUF_SIZE: usize = 65536;
Expand Down Expand Up @@ -599,7 +635,32 @@ impl Worker {
false => SocketAddr::new(IpAddr::V6(Ipv6Addr::UNSPECIFIED), 0),
}
};
let mut sock = QuicSocket::new(&local, registry)?;

// Create packet loss configuration if needed
let packet_loss_config = if option.packet_loss_rate > 0.0
|| !option.packet_loss_numbers.is_empty()
|| !option.packet_loss_types.is_empty()
{
let mut config = PacketLossConfig::new()
.with_loss_rate(option.packet_loss_rate)
.with_drop_packet_numbers(option.packet_loss_numbers.clone())
.with_drop_incoming(option.packet_loss_incoming)
.with_drop_outgoing(option.packet_loss_outgoing);

let packet_types: Vec<_> = option
.packet_loss_types
.iter()
.map(|t| t.to_packet_type())
.collect();
config = config.with_drop_packet_types(packet_types);

Some(config)
} else {
None
};

let mut sock =
QuicSocket::with_packet_loss(&local, registry, packet_loss_config, option.cid_len)?;

let mut assigned_addrs = Vec::new();
assigned_addrs.push(sock.local_addr());
Expand Down
68 changes: 67 additions & 1 deletion tools/src/bin/tquic_server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ use tquic::TlsConfig;
use tquic::TransportHandler;
use tquic_tools::ApplicationProto;
use tquic_tools::CertCompressionAlgorithmArg;
use tquic_tools::LossPacketType;
use tquic_tools::PacketLossConfig;
use tquic_tools::QuicSocket;
use tquic_tools::Result;

Expand Down Expand Up @@ -257,6 +259,41 @@ pub struct ServerOpt {
/// Disable encryption on 1-RTT packets.
#[clap(long, help_heading = "Misc")]
pub disable_encryption: bool,

/// Packet loss rate (0.0 to 1.0) for testing.
#[clap(
long,
default_value = "0.0",
value_name = "RATE",
help_heading = "Packet Loss"
)]
pub packet_loss_rate: f64,

/// Specific packet numbers to drop (comma-separated).
#[clap(
long,
value_delimiter = ',',
value_name = "NUMBERS",
help_heading = "Packet Loss"
)]
pub packet_loss_numbers: Vec<u64>,

/// Packet types to drop.
#[clap(
long,
value_delimiter = ',',
value_name = "TYPES",
help_heading = "Packet Loss"
)]
pub packet_loss_types: Vec<LossPacketType>,

/// Apply packet loss to incoming packets.
#[clap(long, help_heading = "Packet Loss")]
pub packet_loss_incoming: bool,

/// Apply packet loss to outgoing packets.
#[clap(long, help_heading = "Packet Loss")]
pub packet_loss_outgoing: bool,
}

const MAX_BUF_SIZE: usize = 65536;
Expand Down Expand Up @@ -343,7 +380,36 @@ impl Server {
let registry = poll.registry();

let handlers = ServerHandler::new(option)?;
let sock = Rc::new(QuicSocket::new(&option.listen, registry)?);

// Create packet loss configuration if needed
let packet_loss_config = if option.packet_loss_rate > 0.0
|| !option.packet_loss_numbers.is_empty()
|| !option.packet_loss_types.is_empty()
{
let mut config = PacketLossConfig::new()
.with_loss_rate(option.packet_loss_rate)
.with_drop_packet_numbers(option.packet_loss_numbers.clone())
.with_drop_incoming(option.packet_loss_incoming)
.with_drop_outgoing(option.packet_loss_outgoing);

let packet_types: Vec<_> = option
.packet_loss_types
.iter()
.map(|t| t.to_packet_type())
.collect();
config = config.with_drop_packet_types(packet_types);

Some(config)
} else {
None
};

let sock = Rc::new(QuicSocket::with_packet_loss(
&option.listen,
registry,
packet_loss_config,
option.cid_len,
)?);

Ok(Server {
endpoint: Endpoint::new(Box::new(config), true, Box::new(handlers), sock.clone()),
Expand Down
49 changes: 48 additions & 1 deletion tools/src/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.

use std::cell::RefCell;
use std::io::ErrorKind;
use std::net::SocketAddr;

Expand All @@ -30,6 +31,9 @@ use tquic::CertCompressionAlgorithm;
use tquic::PacketInfo;
use tquic::PacketSendHandler;

pub mod packet_loss;
pub use packet_loss::{LossPacketType, PacketLossConfig, PacketLossSimulator};

pub type Result<T> = std::result::Result<T, Box<dyn std::error::Error>>;

/// Certificate compression algorithm for clap parsing
Expand Down Expand Up @@ -120,10 +124,25 @@ pub struct QuicSocket {

/// Local address of the initial socket.
local_addr: SocketAddr,

/// Packet loss simulator for testing
packet_loss: RefCell<Option<PacketLossSimulator>>,

/// Connection ID length for packet parsing
dcid_len: usize,
}

impl QuicSocket {
pub fn new(local: &SocketAddr, registry: &Registry) -> Result<Self> {
Self::with_packet_loss(local, registry, None, 8)
}

pub fn with_packet_loss(
local: &SocketAddr,
registry: &Registry,
packet_loss_config: Option<PacketLossConfig>,
dcid_len: usize,
) -> Result<Self> {
let mut socks = Slab::new();
let mut addrs = FxHashMap::default();

Expand All @@ -135,10 +154,14 @@ impl QuicSocket {
let socket = socks.get_mut(sid).unwrap();
registry.register(socket, Token(sid), Interest::READABLE)?;

let packet_loss = RefCell::new(packet_loss_config.map(PacketLossSimulator::new));

Ok(Self {
socks,
addrs,
local_addr,
packet_loss,
dcid_len,
})
}

Expand Down Expand Up @@ -188,14 +211,38 @@ impl QuicSocket {
};

match socket.recv_from(buf) {
Ok((len, remote)) => Ok((len, socket.local_addr()?, remote)),
Ok((len, remote)) => {
// Check if packet should be dropped due to loss simulation
if let Ok(mut packet_loss) = self.packet_loss.try_borrow_mut() {
if let Some(ref mut simulator) = *packet_loss {
if simulator.should_drop_incoming(&buf[..len], self.dcid_len) {
debug!("Simulating incoming packet loss - dropping packet");
return Err(std::io::Error::new(
ErrorKind::WouldBlock,
"simulated packet loss",
));
}
}
}
Ok((len, socket.local_addr()?, remote))
}
Err(e) => Err(e),
}
}

/// Send data on the socket to the given address.
/// Note: packets with unknown src address are dropped.
pub fn send_to(&self, buf: &[u8], src: SocketAddr, dst: SocketAddr) -> std::io::Result<usize> {
// Check if packet should be dropped due to loss simulation
if let Ok(mut packet_loss) = self.packet_loss.try_borrow_mut() {
if let Some(ref mut simulator) = *packet_loss {
if simulator.should_drop_outgoing(buf, self.dcid_len) {
debug!("Simulating outgoing packet loss - dropping packet");
return Ok(buf.len()); // Pretend we sent it
}
}
}

let sid = match self.addrs.get(&src) {
Some(sid) => sid,
None => {
Expand Down
Loading
Loading