Skip to content

Disable ECN on 100% CE #3820

Description

@mxinden

Reports (rumors) at IETF that some network send ECN CE on all packets. We should disable ECN when e.g. the last 10 packets have all been marked as CE.

/// After the ECN validation test has ended, check if the path is ECN capable.
fn validate_ack_ecn_and_update(
&mut self,
acked_packets: &[sent::Packet],
ack_ecn: Option<&Count>,
stats: &mut Stats,
) {
// RFC 9000, Section 13.4.2.1:
//
// > Validating ECN counts from reordered ACK frames can result in failure. An endpoint MUST
// > NOT fail ECN validation as a result of processing an ACK frame that does not increase
// > the largest acknowledged packet number.
let largest_acked = acked_packets.first().expect("must be there");
if largest_acked.pn() <= self.largest_acked {
return;
}
// RFC 9000, Appendix A.4:
//
// > From the "unknown" state, successful validation of the ECN counts in an ACK frame
// > (see Section 13.4.2.1) causes the ECN state for the path to become "capable", unless
// > no marked packet has been acknowledged.
match self.state {
ValidationState::NotStarted
| ValidationState::Testing { .. }
| ValidationState::Failed(_) => return,
ValidationState::Unknown | ValidationState::Capable => {}
}
// RFC 9000, Section 13.4.2.1:
//
// > An endpoint that receives an ACK frame with ECN counts therefore validates
// > the counts before using them. It performs this validation by comparing newly
// > received counts against those from the last successfully processed ACK frame.
//
// > If an ACK frame newly acknowledges a packet that the endpoint sent with
// > either the ECT(0) or ECT(1) codepoint set, ECN validation fails if the
// > corresponding ECN counts are not present in the ACK frame.
let Some(ack_ecn) = ack_ecn else {
qinfo!("ECN validation failed, no ECN counts in ACK frame");
self.disable_ecn(stats, ValidationError::Bleaching);
return;
};
let ack_ecn = *ack_ecn;
stats.ecn_tx_acked[largest_acked.packet_type()] = ack_ecn;
// > ECN validation also fails if the sum of the increase in ECT(0) and ECN-CE counts is
// > less than the number of newly acknowledged packets that were originally sent with an
// > ECT(0) marking.
let newly_acked_sent_with_ect0: u64 = acked_packets
.iter()
.filter(|p| p.ecn_marked_ect0())
.count()
.try_into()
.expect("usize fits into u64");
let ecn_diff = ack_ecn - self.baseline;
let sum_inc = ecn_diff[Ecn::Ect0] + ecn_diff[Ecn::Ce];
if sum_inc < newly_acked_sent_with_ect0 {
qinfo!(
"ECN validation failed, ACK counted {sum_inc} new marks, but {newly_acked_sent_with_ect0} of newly acked packets were sent with ECT(0)"
);
self.disable_ecn(stats, ValidationError::Bleaching);
} else if ecn_diff[Ecn::Ect1] > 0 {
qinfo!("ECN validation failed, ACK counted ECT(1) marks that were never sent");
self.disable_ecn(stats, ValidationError::ReceivedUnsentECT1);
} else if self.state != ValidationState::Capable {
qinfo!("ECN validation succeeded, path is capable");
self.state.set(ValidationState::Capable, stats);
}
self.baseline = ack_ecn;
self.largest_acked = largest_acked.pn();
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    Priority

    None yet

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions