Apply GREASE to QUIC Version Negotiation packets
This prevents client implementations from breaking when they observe unknown versions.
This also ensures that the produced packet will always be long enough to allow QUIC proxy munging.
gfe-relnote: minor change to packet format of Version Negotiation packets, protected by --gfe2_reloadable_flag_quic_disable_version_negotiation_grease_randomness
PiperOrigin-RevId: 255015606
Change-Id: I8070d543a765e914a2cdc07fd941fa5e1b359c14
diff --git a/quic/core/quic_framer.cc b/quic/core/quic_framer.cc
index 5b061d7..6a6046c 100644
--- a/quic/core/quic_framer.cc
+++ b/quic/core/quic_framer.cc
@@ -1392,17 +1392,46 @@
QuicConnectionId client_connection_id,
bool ietf_quic,
const ParsedQuicVersionVector& versions) {
+ ParsedQuicVersionVector wire_versions = versions;
+ if (!GetQuicReloadableFlag(quic_version_negotiation_grease)) {
+ if (wire_versions.empty()) {
+ wire_versions = {QuicVersionReservedForNegotiation()};
+ }
+ } else {
+ // Add a version reserved for negotiation as suggested by the
+ // "Using Reserved Versions" section of draft-ietf-quic-transport.
+ QUIC_RELOADABLE_FLAG_COUNT_N(quic_version_negotiation_grease, 1, 2);
+ if (wire_versions.empty()) {
+ // Ensure that version negotiation packets we send have at least two
+ // versions. This guarantees that, under all circumstances, all QUIC
+ // packets we send are at least 14 bytes long.
+ wire_versions = {QuicVersionReservedForNegotiation(),
+ QuicVersionReservedForNegotiation()};
+ } else {
+ // This is not uniformely distributed but is acceptable since no security
+ // depends on this randomness.
+ size_t version_index = 0;
+ const bool disable_randomness =
+ GetQuicFlag(FLAGS_quic_disable_version_negotiation_grease_randomness);
+ if (!disable_randomness) {
+ version_index = QuicRandom::GetInstance()->RandUint64() %
+ (wire_versions.size() + 1);
+ }
+ wire_versions.insert(wire_versions.begin() + version_index,
+ QuicVersionReservedForNegotiation());
+ }
+ }
if (ietf_quic) {
- return BuildIetfVersionNegotiationPacket(server_connection_id,
- client_connection_id, versions);
+ return BuildIetfVersionNegotiationPacket(
+ server_connection_id, client_connection_id, wire_versions);
}
// The GQUIC encoding does not support encoding client connection IDs.
DCHECK(client_connection_id.IsEmpty());
- DCHECK(!versions.empty());
+ DCHECK(!wire_versions.empty());
size_t len = kPublicFlagsSize + server_connection_id.length() +
- versions.size() * kQuicVersionSize;
+ wire_versions.size() * kQuicVersionSize;
std::unique_ptr<char[]> buffer(new char[len]);
QuicDataWriter writer(len, buffer.get());
@@ -1418,7 +1447,7 @@
return nullptr;
}
- for (const ParsedQuicVersion& version : versions) {
+ for (const ParsedQuicVersion& version : wire_versions) {
if (!writer.WriteUInt32(CreateQuicVersionLabel(version))) {
return nullptr;
}