blob: c1b4b1f9193836ce3ec719a7a50c7229c285b8d3 [file] [log] [blame]
// 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/core/internet_checksum.h"
#include "quiche/quic/platform/api/quic_socket_address.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;
// 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);
#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;
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());
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