| // Copyright 2023 The Chromium Authors | |
| // Use of this source code is governed by a BSD-style license that can be | |
| // found in the LICENSE file. | |
| #include "quiche/quic/test_tools/test_ip_packets.h" | |
| #include <cstdint> | |
| #include <limits> | |
| #include <string> | |
| #include "absl/strings/str_cat.h" | |
| #include "absl/strings/string_view.h" | |
| #include "quiche/quic/platform/api/quic_socket_address.h" | |
| #include "quiche/common/internet_checksum.h" | |
| #include "quiche/common/platform/api/quiche_logging.h" | |
| #include "quiche/common/quiche_data_writer.h" | |
| #include "quiche/common/quiche_endian.h" | |
| #include "quiche/common/quiche_ip_address.h" | |
| #include "quiche/common/quiche_ip_address_family.h" | |
| #if defined(__linux__) | |
| #include <netinet/in.h> | |
| #include <netinet/ip.h> | |
| #include <netinet/ip6.h> | |
| #include <netinet/udp.h> | |
| #endif | |
| namespace quic::test { | |
| namespace { | |
| // RFC791, Section 3.1. Size without the optional Options field. | |
| constexpr uint16_t kIpv4HeaderSize = 20; | |
| // RFC8200, Section 3. | |
| constexpr uint16_t kIpv6HeaderSize = 40; | |
| // RFC768. | |
| constexpr uint16_t kUdpHeaderSize = 8; | |
| constexpr uint8_t kUdpProtocol = 0x11; | |
| // RFC9293 | |
| constexpr uint8_t kTcpProtocol = 0x06; | |
| // For Windows compatibility, avoid dependency on netinet, but when building on | |
| // Linux, check that the constants match. | |
| #if defined(__linux__) | |
| static_assert(kIpv4HeaderSize == sizeof(iphdr)); | |
| static_assert(kIpv6HeaderSize == sizeof(ip6_hdr)); | |
| static_assert(kUdpHeaderSize == sizeof(udphdr)); | |
| static_assert(kUdpProtocol == IPPROTO_UDP); | |
| static_assert(kTcpProtocol == IPPROTO_TCP); | |
| #endif | |
| std::string CreateIpv4Header(int payload_length, | |
| quiche::QuicheIpAddress source_address, | |
| quiche::QuicheIpAddress destination_address, | |
| uint8_t protocol) { | |
| QUICHE_CHECK_GT(payload_length, 0); | |
| QUICHE_CHECK_LE(payload_length, | |
| std::numeric_limits<uint16_t>::max() - kIpv4HeaderSize); | |
| QUICHE_CHECK(source_address.address_family() == | |
| quiche::IpAddressFamily::IP_V4); | |
| QUICHE_CHECK(destination_address.address_family() == | |
| quiche::IpAddressFamily::IP_V4); | |
| std::string header(kIpv4HeaderSize, '\0'); | |
| quiche::QuicheDataWriter header_writer(header.size(), header.data()); | |
| header_writer.WriteUInt8(0x45); // Version: 4, Header length: 5 words | |
| header_writer.WriteUInt8(0x00); // DSCP: 0, ECN: 0 | |
| header_writer.WriteUInt16(kIpv4HeaderSize + payload_length); // Total length | |
| header_writer.WriteUInt16(0x0000); // Identification: 0 (replaced by socket) | |
| header_writer.WriteUInt16(0x0000); // Flags: 0, Fragment offset: 0 | |
| header_writer.WriteUInt8(64); // TTL: 64 hops/seconds | |
| header_writer.WriteUInt8(protocol); | |
| header_writer.WriteUInt16(0x0000); // Checksum (replaced by socket) | |
| header_writer.WriteStringPiece(source_address.ToPackedString()); | |
| header_writer.WriteStringPiece(destination_address.ToPackedString()); | |
| QUICHE_CHECK_EQ(header_writer.remaining(), 0u); | |
| return header; | |
| } | |
| std::string CreateIpv6Header(int payload_length, | |
| quiche::QuicheIpAddress source_address, | |
| quiche::QuicheIpAddress destination_address, | |
| uint8_t next_header) { | |
| QUICHE_CHECK_GT(payload_length, 0); | |
| QUICHE_CHECK_LE(payload_length, std::numeric_limits<uint16_t>::max()); | |
| QUICHE_CHECK(source_address.address_family() == | |
| quiche::IpAddressFamily::IP_V6); | |
| QUICHE_CHECK(destination_address.address_family() == | |
| quiche::IpAddressFamily::IP_V6); | |
| std::string header(kIpv6HeaderSize, '\0'); | |
| quiche::QuicheDataWriter header_writer(header.size(), header.data()); | |
| // Version: 6 | |
| // Traffic class: 0 | |
| // Flow label: 0 (possibly replaced by socket) | |
| header_writer.WriteUInt32(0x60000000); | |
| header_writer.WriteUInt16(payload_length); | |
| header_writer.WriteUInt8(next_header); | |
| header_writer.WriteUInt8(64); // Hop limit: 64 | |
| header_writer.WriteStringPiece(source_address.ToPackedString()); | |
| header_writer.WriteStringPiece(destination_address.ToPackedString()); | |
| QUICHE_CHECK_EQ(header_writer.remaining(), 0u); | |
| return header; | |
| } | |
| } // namespace | |
| std::string CreateIpPacket(const quiche::QuicheIpAddress& source_address, | |
| const quiche::QuicheIpAddress& destination_address, | |
| absl::string_view payload, | |
| IpPacketPayloadType payload_type) { | |
| QUICHE_CHECK(source_address.address_family() == | |
| destination_address.address_family()); | |
| uint8_t payload_protocol; | |
| switch (payload_type) { | |
| case IpPacketPayloadType::kUdp: | |
| payload_protocol = kUdpProtocol; | |
| break; | |
| case IpPacketPayloadType::kTcp: | |
| payload_protocol = kTcpProtocol; | |
| break; | |
| default: | |
| QUICHE_NOTREACHED(); | |
| return ""; | |
| } | |
| std::string header; | |
| switch (source_address.address_family()) { | |
| case quiche::IpAddressFamily::IP_V4: | |
| header = CreateIpv4Header(payload.size(), source_address, | |
| destination_address, payload_protocol); | |
| break; | |
| case quiche::IpAddressFamily::IP_V6: | |
| header = CreateIpv6Header(payload.size(), source_address, | |
| destination_address, payload_protocol); | |
| break; | |
| default: | |
| QUICHE_NOTREACHED(); | |
| return ""; | |
| } | |
| return absl::StrCat(header, payload); | |
| } | |
| std::string CreateUdpPacket(const QuicSocketAddress& source_address, | |
| const QuicSocketAddress& destination_address, | |
| absl::string_view payload) { | |
| QUICHE_CHECK(source_address.host().address_family() == | |
| destination_address.host().address_family()); | |
| QUICHE_CHECK(!payload.empty()); | |
| QUICHE_CHECK_LE(payload.size(), | |
| static_cast<uint16_t>(std::numeric_limits<uint16_t>::max() - | |
| kUdpHeaderSize)); | |
| std::string header(kUdpHeaderSize, '\0'); | |
| quiche::QuicheDataWriter header_writer(header.size(), header.data()); | |
| header_writer.WriteUInt16(source_address.port()); | |
| header_writer.WriteUInt16(destination_address.port()); | |
| header_writer.WriteUInt16(kUdpHeaderSize + payload.size()); | |
| quiche::InternetChecksum checksum; | |
| switch (source_address.host().address_family()) { | |
| case quiche::IpAddressFamily::IP_V4: { | |
| // IP pseudo header information. See RFC768. | |
| checksum.Update(source_address.host().ToPackedString()); | |
| checksum.Update(destination_address.host().ToPackedString()); | |
| uint8_t protocol[] = {0x00, kUdpProtocol}; | |
| checksum.Update(protocol, sizeof(protocol)); | |
| uint16_t udp_length = | |
| quiche::QuicheEndian::HostToNet16(kUdpHeaderSize + payload.size()); | |
| checksum.Update(reinterpret_cast<uint8_t*>(&udp_length), | |
| sizeof(udp_length)); | |
| break; | |
| } | |
| case quiche::IpAddressFamily::IP_V6: { | |
| // IP pseudo header information. See RFC8200, Section 8.1. | |
| checksum.Update(source_address.host().ToPackedString()); | |
| checksum.Update(destination_address.host().ToPackedString()); | |
| uint32_t udp_length = | |
| quiche::QuicheEndian::HostToNet32(kUdpHeaderSize + payload.size()); | |
| checksum.Update(reinterpret_cast<uint8_t*>(&udp_length), | |
| sizeof(udp_length)); | |
| uint8_t protocol[] = {0x00, 0x00, 0x00, kUdpProtocol}; | |
| checksum.Update(protocol, sizeof(protocol)); | |
| break; | |
| } | |
| default: | |
| QUICHE_NOTREACHED(); | |
| return ""; | |
| } | |
| checksum.Update(header.data(), header.size()); | |
| checksum.Update(payload.data(), payload.size()); | |
| uint16_t checksum_val = checksum.Value(); | |
| // Checksum is always written in the same byte order in which it was | |
| // calculated. | |
| header_writer.WriteBytes(&checksum_val, sizeof(checksum_val)); | |
| QUICHE_CHECK_EQ(header_writer.remaining(), 0u); | |
| return absl::StrCat(header, payload); | |
| } | |
| } // namespace quic::test |