@@ -790,22 +790,30 @@ void Endpoint::SendRetry(const PathDescriptor& options) {
790790
791791void Endpoint::SendVersionNegotiation (const PathDescriptor& options) {
792792 Debug (this , " Sending version negotiation on path %s" , options);
793- // While creating and sending a version negotiation packet does consume a
794- // small amount of system resources, and while it is fairly trivial for a
795- // malicious peer to force a version negotiation to be sent, these are more
796- // trivial to create than the cryptographically generated retry and stateless
797- // reset packets. If the packet is sent, then we'll at least increment the
798- // version_negotiation_count statistic so that application code can keep an
799- // eye on it.
793+ // A malicious peer can trivially force version negotiation packets by
794+ // sending packets with unsupported QUIC versions, potentially from
795+ // spoofed source addresses. Rate-limit per remote host to prevent
796+ // amplification attacks.
797+ const auto exceeds_limits = [&] {
798+ SocketAddressInfoTraits::Type* counts =
799+ addr_validation_lru_.Peek (options.remote_address );
800+ auto count = counts != nullptr ? counts->version_negotiation_count : 0 ;
801+ return count >= kMaxVersionNegotiations ;
802+ };
803+
804+ if (exceeds_limits ()) {
805+ Debug (this , " Version negotiation rate limit exceeded for %s" ,
806+ options.remote_address );
807+ return ;
808+ }
809+
800810 auto packet = Packet::CreateVersionNegotiationPacket (*this , options);
801811 if (packet) {
812+ addr_validation_lru_.Upsert (options.remote_address )
813+ ->version_negotiation_count ++;
802814 STAT_INCREMENT (Stats, version_negotiation_count);
803815 Send (std::move (packet));
804816 }
805-
806- // If creating the packet is unsuccessful, we just drop things on the floor.
807- // It's not worth committing any further resources to this one packet. We
808- // might want to log the failure at some point tho.
809817}
810818
811819bool Endpoint::SendStatelessReset (const PathDescriptor& options,
@@ -847,11 +855,27 @@ void Endpoint::SendImmediateConnectionClose(const PathDescriptor& options,
847855 " Sending immediate connection close on path %s with reason %s" ,
848856 options,
849857 reason);
850- // While it is possible for a malicious peer to cause us to create a large
851- // number of these, generating them is fairly trivial.
858+ // A malicious peer can trigger immediate connection close packets by
859+ // sending Initial packets with invalid tokens or when the server is
860+ // busy. Rate-limit per remote host to prevent amplification attacks.
861+ const auto exceeds_limits = [&] {
862+ SocketAddressInfoTraits::Type* counts =
863+ addr_validation_lru_.Peek (options.remote_address );
864+ auto count = counts != nullptr ? counts->immediate_close_count : 0 ;
865+ return count >= kMaxImmediateCloses ;
866+ };
867+
868+ if (exceeds_limits ()) {
869+ Debug (this , " Immediate connection close rate limit exceeded for %s" ,
870+ options.remote_address );
871+ return ;
872+ }
873+
852874 auto packet =
853875 Packet::CreateImmediateConnectionClosePacket (*this , options, reason);
854876 if (packet) {
877+ addr_validation_lru_.Upsert (options.remote_address )
878+ ->immediate_close_count ++;
855879 STAT_INCREMENT (Stats, immediate_close_count);
856880 Send (std::move (packet));
857881 }
0 commit comments