fix(client): align DSCP wire semantics

This commit is contained in:
Nils Andreas Svee committed 2026-08-11 20:29:57 +07:00
1 parent 347af39d74
commit 3d5a8fae14
18 files changed
+249 -140

No files matched your search

+6 -6
View File
@@ -45,7 +45,7 @@ fn close_sends_one_close_packet_with_negotiated_token() {
));
assert!(!client.runtime.is_open());
assert!(client.schedule.is_none());
assert_eq!(client.applied_dscp, None);
assert_eq!(client.applied_traffic_class, None);
let packets: Vec<_> = server.rx.iter().take(2).collect();
let close = &packets[1];
assert_eq!(close[3], flags::FLAG_CLOSE);
@@ -82,7 +82,7 @@ fn close_event_reservation_failure_precedes_dscp_clear_and_send() {
assert!(client.test_hooks.fail_close_dscp_clear.get());
assert!(client.runtime.is_open());
assert!(client.schedule.is_some());
assert_eq!(client.applied_dscp, Some(0));
assert_eq!(client.applied_traffic_class, Some(0));
client.test_hooks.fail_close_dscp_clear.set(false);
client.close().unwrap();
@@ -114,7 +114,7 @@ fn close_dscp_clear_failure_precedes_send_and_preserves_open_state() {
assert_eq!(client.test_hooks.close_send_attempts.get(), 0);
assert!(client.runtime.is_open());
assert!(client.schedule.is_some());
assert_eq!(client.applied_dscp, Some(0));
assert_eq!(client.applied_traffic_class, Some(0));
client.close().unwrap();
assert_eq!(client.test_hooks.close_send_attempts.get(), 1);
@@ -147,7 +147,7 @@ fn close_send_failure_leaves_machine_and_schedule_open_for_retry() {
assert_eq!(client.test_hooks.close_sent_at.get(), Some(sent_at));
assert!(client.runtime.is_open());
assert!(client.schedule.is_some());
assert_eq!(client.applied_dscp, Some(0));
assert_eq!(client.applied_traffic_class, Some(0));
assert_eq!(client.test_hooks.close_send_attempts.get(), 1);
assert!(matches!(
@@ -196,7 +196,7 @@ fn short_successful_close_commits_before_length_mismatch() {
));
assert!(!client.runtime.is_open());
assert!(client.schedule.is_none());
assert_eq!(client.applied_dscp, None);
assert_eq!(client.applied_traffic_class, None);
assert_eq!(client.test_hooks.close_sent_at.get(), None);
assert!(matches!(client.close(), Err(ClientError::AlreadyClosed)));
server.join();
@@ -351,7 +351,7 @@ fn peer_close_dscp_cleanup_failure_preserves_events_and_closed_state() {
));
assert!(client.is_peer_closed());
assert!(client.schedule.is_none());
assert_eq!(client.applied_dscp, Some(0));
assert_eq!(client.applied_traffic_class, Some(0));
assert_eq!(client.test_hooks.close_send_attempts.get(), 0);
let open = server.rx.recv_timeout(Duration::from_millis(100)).unwrap();
let echo = server.rx.recv_timeout(Duration::from_millis(100)).unwrap();
@@ -42,7 +42,10 @@ fn params_from_config_maps_compatibility_fields() {
assert_eq!(params.received_stats, ReceivedStats::Window);
assert_eq!(params.stamp_at, StampAt::Midpoint);
assert_eq!(params.clock, Clock::Wall);
assert_eq!(params.dscp, 46, "config DSCP codepoint must not be shifted");
assert_eq!(
params.dscp, 184,
"config DSCP codepoint 46 must be shifted into raw wire byte 184"
);
assert_eq!(
params.server_fill.as_ref().map(|fill| fill.value.as_str()),
Some("rand")
@@ -67,7 +70,11 @@ fn params_from_config_accepts_boundary_values() {
dscp: 63,
..ClientConfig::default()
};
assert_eq!(params_from_config(&config).unwrap().dscp, 63);
assert_eq!(
params_from_config(&config).unwrap().dscp,
252,
"boundary codepoint 63 must be shifted into raw wire byte 252"
);
}
#[test]
+14 -10
View File
@@ -9,9 +9,11 @@ use crate::socket_options::socket_traffic_class;
target_os = "illumos",
target_os = "haiku",
)))]
fn normal_open_applies_negotiated_dscp_after_open_and_close_clears_it() {
fn normal_open_applies_negotiated_traffic_class_after_open_and_close_clears_it() {
let mut params = default_params();
params.dscp = 46;
// Raw wire TOS/Traffic Class byte for DSCP codepoint 46 (EF): a
// compliant server echoes back the raw byte it received, unchanged.
params.dscp = 184;
let server = start_fake_server(move |socket, tx| {
let (_, peer) = recv_request(&socket, &tx);
let reply = open_reply(FLAG_OPEN | FLAG_REPLY, TOKEN, &params, None);
@@ -48,7 +50,7 @@ fn normal_open_applies_negotiated_dscp_after_open_and_close_clears_it() {
target_os = "illumos",
target_os = "haiku",
)))]
fn normal_open_uses_negotiated_dscp_not_requested_dscp() {
fn normal_open_uses_negotiated_traffic_class_not_requested_dscp() {
let mut returned = default_params();
returned.dscp = 0;
let server = open_success_server(returned);
@@ -82,9 +84,10 @@ fn normal_open_uses_negotiated_dscp_not_requested_dscp() {
target_os = "illumos",
target_os = "haiku",
)))]
fn failed_close_send_restores_negotiated_dscp_and_keeps_session_open() {
fn failed_close_send_restores_negotiated_traffic_class_and_keeps_session_open() {
let mut params = default_params();
params.dscp = 46;
// Raw wire TOS/Traffic Class byte for DSCP codepoint 46 (EF).
params.dscp = 184;
let server = start_fake_server(move |socket, tx| {
let (_, peer) = recv_request(&socket, &tx);
socket
@@ -106,7 +109,7 @@ fn failed_close_send_restores_negotiated_dscp_and_keeps_session_open() {
assert!(matches!(error, ClientError::Socket(_)));
assert!(client.runtime.is_open());
assert!(client.schedule.is_some());
assert_eq!(client.applied_dscp, Some(46));
assert_eq!(client.applied_traffic_class, Some(184));
assert_eq!(
socket_traffic_class(&client.socket, client.remote).unwrap() & 0xfc,
184
@@ -139,7 +142,7 @@ fn failed_close_keeps_send_error_primary_when_dscp_restoration_also_fails() {
assert!(error.to_string().contains("injected close send failure"));
assert!(client.runtime.is_open());
assert!(client.schedule.is_some());
assert_eq!(client.applied_dscp, Some(0));
assert_eq!(client.applied_traffic_class, Some(0));
client.close().unwrap();
server.join();
}
@@ -152,9 +155,10 @@ fn failed_close_keeps_send_error_primary_when_dscp_restoration_also_fails() {
target_os = "illumos",
target_os = "haiku",
)))]
fn authenticated_peer_close_clears_schedule_and_negotiated_dscp() {
fn authenticated_peer_close_clears_schedule_and_negotiated_traffic_class() {
let mut params = default_params();
params.dscp = 46;
// Raw wire TOS/Traffic Class byte for DSCP codepoint 46 (EF).
params.dscp = 184;
let server = start_fake_server(move |socket, tx| {
let (_, peer) = recv_request(&socket, &tx);
socket
@@ -195,7 +199,7 @@ fn authenticated_peer_close_clears_schedule_and_negotiated_dscp() {
]
));
assert!(client.schedule.is_none());
assert_eq!(client.applied_dscp, None);
assert_eq!(client.applied_traffic_class, None);
assert_eq!(
socket_traffic_class(&client.socket, client.remote).unwrap(),
0
@@ -33,13 +33,17 @@ fn strict_negotiation_rejects_changed_params() {
..ClientConfig::default()
};
let requested = params_from_config(&config).unwrap();
assert_eq!(
requested.dscp, 184,
"codepoint 46 must become raw wire byte 184"
);
let mut returned = requested.clone();
returned.dscp = 0;
assert_eq!(
rejection_reason(&requested, &returned, NegotiationPolicy::Strict),
NegotiationRestriction::DscpChanged {
requested: requested.dscp,
negotiated: returned.dscp,
requested: 46,
negotiated: 0,
}
.message()
);
@@ -183,17 +187,34 @@ fn strict_negotiation_rejects_negative_returned_length() {
#[test]
fn loose_negotiation_rejects_runtime_invalid_returned_dscp() {
let config = ClientConfig::default();
let config = ClientConfig {
dscp: 46,
..ClientConfig::default()
};
let requested = params_from_config(&config).unwrap();
assert_eq!(
requested.dscp, 184,
"codepoint 46 must become raw wire byte 184"
);
for dscp in [-1, 64] {
for dscp in [-1, 256] {
let mut returned = requested.clone();
returned.dscp = dscp;
assert_eq!(
rejection_reason(&requested, &returned, NegotiationPolicy::Loose),
"dscp must be in range 0..=63"
"dscp must be in range 0..=255"
);
}
// A raw returned value of 184 must be accepted even though it exceeds the
// 0..=63 codepoint range; it is the correctly negotiated wire value for
// codepoint 46 and is not subject to the codepoint bound at all.
let returned = requested.clone();
assert!(
assert_negotiates(&requested, &returned, NegotiationPolicy::Loose)
.restrictions
.is_empty()
);
}
#[test]
@@ -13,7 +13,7 @@ fn no_test_success_validates_params() {
assert_eq!(negotiated.params, params);
assert!(client.next_send_deadline().is_none());
assert!(client.schedule.is_none());
assert_eq!(client.applied_dscp, None);
assert_eq!(client.applied_traffic_class, None);
server.join();
}
+9 -7
View File
@@ -10,7 +10,7 @@ fn successful_open_handshake() {
let negotiated = assert_open_started(client.open().unwrap());
assert_eq!(negotiated.params, params);
assert!(client.schedule.is_some());
assert_eq!(client.applied_dscp, Some(0));
assert_eq!(client.applied_traffic_class, Some(0));
server.join();
}
@@ -284,7 +284,7 @@ fn schedule_duration_overflow_cannot_partially_open() {
assert!(failure.machine.cleanup_close_packet().is_some());
assert!(client.runtime.prepare_open_request().is_ok());
assert!(client.schedule.is_none());
assert_eq!(client.applied_dscp, None);
assert_eq!(client.applied_traffic_class, None);
assert_eq!(client.recv_buffer.len(), previous_recv_len);
server.join();
}
@@ -346,7 +346,9 @@ fn cleanup_send_failure_does_not_replace_primary_open_error() {
#[test]
fn receive_buffer_reservation_failure_precedes_dscp_application() {
let mut returned = default_params();
returned.dscp = 46;
// Raw wire TOS/Traffic Class byte for DSCP codepoint 46 (EF), matching
// what the client below requests so negotiation succeeds unchanged.
returned.dscp = 184;
let server = start_fake_server(move |socket, tx| {
let (_, peer) = recv_request(&socket, &tx);
socket
@@ -376,7 +378,7 @@ fn receive_buffer_reservation_failure_precedes_dscp_application() {
assert!(client.test_hooks.fail_open_dscp.get());
assert!(client.runtime.prepare_open_request().is_ok());
assert!(client.schedule.is_none());
assert_eq!(client.applied_dscp, None);
assert_eq!(client.applied_traffic_class, None);
server.join();
}
@@ -406,7 +408,7 @@ fn dscp_application_failure_leaves_machine_connected() {
assert!(client.test_hooks.prepared_active_session_before_dscp.get());
assert!(client.runtime.prepare_open_request().is_ok());
assert!(client.schedule.is_none());
assert_eq!(client.applied_dscp, None);
assert_eq!(client.applied_traffic_class, None);
server.join();
}
@@ -432,7 +434,7 @@ fn read_timeout_restoration_failure_leaves_machine_connected() {
));
assert!(client.runtime.prepare_open_request().is_ok());
assert!(client.schedule.is_none());
assert_eq!(client.applied_dscp, None);
assert_eq!(client.applied_traffic_class, None);
server.join();
}
@@ -458,6 +460,6 @@ fn dscp_rollback_failure_keeps_timeout_restoration_primary() {
assert!(matches!(error, ClientError::ReadTimeoutRestore { .. }));
assert!(client.runtime.prepare_open_request().is_ok());
assert!(client.schedule.is_none());
assert_eq!(client.applied_dscp, None);
assert_eq!(client.applied_traffic_class, None);
server.join();
}
@@ -14,7 +14,7 @@ fn echo_reply_metadata_propagates_observed_dscp_with_ancillary() {
let (size, _) = socket.recv_from(&mut buf).unwrap();
tx.send(buf[..size].to_vec()).unwrap();
let seq = u32::from_le_bytes(buf[12..16].try_into().unwrap());
crate::socket_options::apply_dscp_to_socket(&socket, peer, 46).unwrap();
crate::socket_options::apply_traffic_class_to_socket(&socket, peer, 184).unwrap();
let reply_packet =
echo_reply_packet(TOKEN, seq, &params, &TimestampFields::default(), None);
socket.send_to(&reply_packet, peer).unwrap();