gfe-relnote: Default-initialize QUIC BBRv2 loss event threshold for exiting STARTUP from a flag. Protected by --gfe2_reloadable_flag_quic_default_to_bbr_v2.
PiperOrigin-RevId: 264298542
Change-Id: I304ab19e4820dec51d3f8ef53762a393f6b175fd
diff --git a/quic/qbone/qbone_packet_processor.cc b/quic/qbone/qbone_packet_processor.cc
new file mode 100644
index 0000000..db7a138
--- /dev/null
+++ b/quic/qbone/qbone_packet_processor.cc
@@ -0,0 +1,269 @@
+// Copyright (c) 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/qbone/qbone_packet_processor.h"
+
+#include <cstring>
+
+#include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_endian.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
+#include "net/third_party/quiche/src/quic/qbone/platform/icmp_packet.h"
+#include "net/third_party/quiche/src/quic/qbone/platform/internet_checksum.h"
+#include "net/third_party/quiche/src/quic/qbone/platform/tcp_packet.h"
+
+namespace {
+
+constexpr size_t kIPv6AddressSize = 16;
+constexpr size_t kIPv6MinPacketSize = 1280;
+constexpr size_t kIcmpTtl = 64;
+constexpr size_t kICMPv6DestinationUnreachableDueToSourcePolicy = 5;
+
+} // namespace
+
+namespace quic {
+
+const QuicIpAddress QbonePacketProcessor::kInvalidIpAddress =
+ QuicIpAddress::Any6();
+
+QbonePacketProcessor::QbonePacketProcessor(QuicIpAddress self_ip,
+ QuicIpAddress client_ip,
+ size_t client_ip_subnet_length,
+ OutputInterface* output,
+ StatsInterface* stats)
+ : client_ip_(client_ip),
+ output_(output),
+ stats_(stats),
+ filter_(new Filter) {
+ memcpy(self_ip_.s6_addr, self_ip.ToPackedString().data(), kIPv6AddressSize);
+ DCHECK_LE(client_ip_subnet_length, kIPv6AddressSize * 8);
+ client_ip_subnet_length_ = client_ip_subnet_length;
+
+ DCHECK(IpAddressFamily::IP_V6 == self_ip.address_family());
+ DCHECK(IpAddressFamily::IP_V6 == client_ip.address_family());
+ DCHECK(self_ip != kInvalidIpAddress);
+}
+
+QbonePacketProcessor::OutputInterface::~OutputInterface() {}
+QbonePacketProcessor::StatsInterface::~StatsInterface() {}
+QbonePacketProcessor::Filter::~Filter() {}
+
+QbonePacketProcessor::ProcessingResult
+QbonePacketProcessor::Filter::FilterPacket(Direction direction,
+ QuicStringPiece full_packet,
+ QuicStringPiece payload,
+ icmp6_hdr* icmp_header,
+ OutputInterface* output) {
+ return ProcessingResult::OK;
+}
+
+void QbonePacketProcessor::ProcessPacket(string* packet, Direction direction) {
+ if (QUIC_PREDICT_FALSE(!IsValid())) {
+ QUIC_BUG << "QuicPacketProcessor is invoked in an invalid state.";
+ stats_->OnPacketDroppedSilently(direction);
+ return;
+ }
+
+ uint8_t transport_protocol;
+ char* transport_data;
+ icmp6_hdr icmp_header;
+ memset(&icmp_header, 0, sizeof(icmp_header));
+ ProcessingResult result = ProcessIPv6HeaderAndFilter(
+ packet, direction, &transport_protocol, &transport_data, &icmp_header);
+
+ switch (result) {
+ case ProcessingResult::OK:
+ switch (direction) {
+ case Direction::FROM_CLIENT:
+ output_->SendPacketToNetwork(*packet);
+ break;
+ case Direction::FROM_NETWORK:
+ output_->SendPacketToClient(*packet);
+ break;
+ }
+ stats_->OnPacketForwarded(direction);
+ break;
+ case ProcessingResult::SILENT_DROP:
+ stats_->OnPacketDroppedSilently(direction);
+ break;
+ case ProcessingResult::DEFER:
+ stats_->OnPacketDeferred(direction);
+ break;
+ case ProcessingResult::ICMP:
+ SendIcmpResponse(&icmp_header, *packet, direction);
+ stats_->OnPacketDroppedWithIcmp(direction);
+ break;
+ case ProcessingResult::ICMP_AND_TCP_RESET:
+ SendIcmpResponse(&icmp_header, *packet, direction);
+ stats_->OnPacketDroppedWithIcmp(direction);
+ SendTcpReset(*packet, direction);
+ stats_->OnPacketDroppedWithTcpReset(direction);
+ break;
+ }
+}
+
+QbonePacketProcessor::ProcessingResult
+QbonePacketProcessor::ProcessIPv6HeaderAndFilter(string* packet,
+ Direction direction,
+ uint8_t* transport_protocol,
+ char** transport_data,
+ icmp6_hdr* icmp_header) {
+ ProcessingResult result = ProcessIPv6Header(
+ packet, direction, transport_protocol, transport_data, icmp_header);
+
+ if (result == ProcessingResult::OK) {
+ char* packet_data = &*packet->begin();
+ size_t header_size = *transport_data - packet_data;
+ // Sanity-check the bounds.
+ if (packet_data >= *transport_data || header_size > packet->size() ||
+ header_size < kIPv6HeaderSize) {
+ QUIC_BUG << "Invalid pointers encountered in "
+ "QbonePacketProcessor::ProcessPacket. Dropping the packet";
+ return ProcessingResult::SILENT_DROP;
+ }
+
+ result = filter_->FilterPacket(
+ direction, *packet,
+ QuicStringPiece(*transport_data, packet->size() - header_size),
+ icmp_header, output_);
+ }
+
+ // Do not send ICMP error messages in response to ICMP errors.
+ if (result == ProcessingResult::ICMP) {
+ const uint8_t* header = reinterpret_cast<const uint8_t*>(packet->data());
+
+ constexpr size_t kIPv6NextHeaderOffset = 6;
+ constexpr size_t kIcmpMessageTypeOffset = kIPv6HeaderSize + 0;
+ constexpr size_t kIcmpMessageTypeMaxError = 127;
+ if (
+ // Check size.
+ packet->size() >= (kIPv6HeaderSize + kICMPv6HeaderSize) &&
+ // Check that the packet is in fact ICMP.
+ header[kIPv6NextHeaderOffset] == IPPROTO_ICMPV6 &&
+ // Check that ICMP message type is an error.
+ header[kIcmpMessageTypeOffset] < kIcmpMessageTypeMaxError) {
+ result = ProcessingResult::SILENT_DROP;
+ }
+ }
+
+ return result;
+}
+
+QbonePacketProcessor::ProcessingResult QbonePacketProcessor::ProcessIPv6Header(
+ string* packet,
+ Direction direction,
+ uint8_t* transport_protocol,
+ char** transport_data,
+ icmp6_hdr* icmp_header) {
+ // Check if the packet is big enough to have IPv6 header.
+ if (packet->size() < kIPv6HeaderSize) {
+ QUIC_DVLOG(1) << "Dropped malformed packet: IPv6 header too short";
+ return ProcessingResult::SILENT_DROP;
+ }
+
+ // Check version field.
+ ip6_hdr* header = reinterpret_cast<ip6_hdr*>(&*packet->begin());
+ if (header->ip6_vfc >> 4 != 6) {
+ QUIC_DVLOG(1) << "Dropped malformed packet: IP version is not IPv6";
+ return ProcessingResult::SILENT_DROP;
+ }
+
+ // Check payload size.
+ const size_t declared_payload_size =
+ QuicEndian::NetToHost16(header->ip6_plen);
+ const size_t actual_payload_size = packet->size() - kIPv6HeaderSize;
+ if (declared_payload_size != actual_payload_size) {
+ QUIC_DVLOG(1)
+ << "Dropped malformed packet: incorrect packet length specified";
+ return ProcessingResult::SILENT_DROP;
+ }
+
+ // Check that the address of the client is in the packet.
+ QuicIpAddress address_to_check;
+ uint8_t address_reject_code;
+ bool ip_parse_result;
+ switch (direction) {
+ case Direction::FROM_CLIENT:
+ // Expect the source IP to match the client.
+ ip_parse_result = address_to_check.FromPackedString(
+ reinterpret_cast<const char*>(&header->ip6_src),
+ sizeof(header->ip6_src));
+ address_reject_code = kICMPv6DestinationUnreachableDueToSourcePolicy;
+ break;
+ case Direction::FROM_NETWORK:
+ // Expect the destination IP to match the client.
+ ip_parse_result = address_to_check.FromPackedString(
+ reinterpret_cast<const char*>(&header->ip6_dst),
+ sizeof(header->ip6_src));
+ address_reject_code = ICMP6_DST_UNREACH_NOROUTE;
+ break;
+ }
+ DCHECK(ip_parse_result);
+ if (!client_ip_.InSameSubnet(address_to_check, client_ip_subnet_length_)) {
+ QUIC_DVLOG(1)
+ << "Dropped packet: source/destination address is not client's";
+ icmp_header->icmp6_type = ICMP6_DST_UNREACH;
+ icmp_header->icmp6_code = address_reject_code;
+ return ProcessingResult::ICMP;
+ }
+
+ // Check and decrement TTL.
+ if (header->ip6_hops <= 1) {
+ icmp_header->icmp6_type = ICMP6_TIME_EXCEEDED;
+ icmp_header->icmp6_code = ICMP6_TIME_EXCEED_TRANSIT;
+ return ProcessingResult::ICMP;
+ }
+ header->ip6_hops--;
+
+ // Check and extract IP headers.
+ switch (header->ip6_nxt) {
+ case IPPROTO_TCP:
+ case IPPROTO_UDP:
+ case IPPROTO_ICMPV6:
+ *transport_protocol = header->ip6_nxt;
+ *transport_data = (&*packet->begin()) + kIPv6HeaderSize;
+ break;
+ default:
+ icmp_header->icmp6_type = ICMP6_PARAM_PROB;
+ icmp_header->icmp6_code = ICMP6_PARAMPROB_NEXTHEADER;
+ return ProcessingResult::ICMP;
+ }
+
+ return ProcessingResult::OK;
+}
+
+void QbonePacketProcessor::SendIcmpResponse(icmp6_hdr* icmp_header,
+ QuicStringPiece original_packet,
+ Direction original_direction) {
+ in6_addr dst;
+ // TODO(b/70339814): ensure this is actually a unicast address.
+ memcpy(dst.s6_addr, &original_packet[8], kIPv6AddressSize);
+
+ CreateIcmpPacket(self_ip_, dst, *icmp_header, original_packet,
+ [this, original_direction](QuicStringPiece packet) {
+ SendResponse(original_direction, packet);
+ });
+}
+
+void QbonePacketProcessor::SendTcpReset(QuicStringPiece original_packet,
+ Direction original_direction) {
+ CreateTcpResetPacket(original_packet,
+ [this, original_direction](QuicStringPiece packet) {
+ SendResponse(original_direction, packet);
+ });
+}
+
+void QbonePacketProcessor::SendResponse(Direction original_direction,
+ QuicStringPiece packet) {
+ switch (original_direction) {
+ case Direction::FROM_CLIENT:
+ output_->SendPacketToClient(packet);
+ break;
+ case Direction::FROM_NETWORK:
+ output_->SendPacketToNetwork(packet);
+ break;
+ }
+}
+
+} // namespace quic