1 // Copyright 2023 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "quiche/quic/test_tools/test_ip_packets.h"
6
7 #include <cstdint>
8 #include <limits>
9 #include <string>
10
11 #include "absl/strings/str_cat.h"
12 #include "absl/strings/string_view.h"
13 #include "quiche/quic/core/internet_checksum.h"
14 #include "quiche/quic/platform/api/quic_socket_address.h"
15 #include "quiche/common/platform/api/quiche_logging.h"
16 #include "quiche/common/quiche_data_writer.h"
17 #include "quiche/common/quiche_endian.h"
18 #include "quiche/common/quiche_ip_address.h"
19 #include "quiche/common/quiche_ip_address_family.h"
20
21 #if defined(__linux__)
22 #include <netinet/in.h>
23 #include <netinet/ip.h>
24 #include <netinet/ip6.h>
25 #include <netinet/udp.h>
26 #endif
27
28 namespace quic::test {
29
30 namespace {
31
32 // RFC791, Section 3.1. Size without the optional Options field.
33 constexpr uint16_t kIpv4HeaderSize = 20;
34
35 // RFC8200, Section 3.
36 constexpr uint16_t kIpv6HeaderSize = 40;
37
38 // RFC768.
39 constexpr uint16_t kUdpHeaderSize = 8;
40 constexpr uint8_t kUdpProtocol = 0x11;
41
42 // For Windows compatibility, avoid dependency on netinet, but when building on
43 // Linux, check that the constants match.
44 #if defined(__linux__)
45 static_assert(kIpv4HeaderSize == sizeof(iphdr));
46 static_assert(kIpv6HeaderSize == sizeof(ip6_hdr));
47 static_assert(kUdpHeaderSize == sizeof(udphdr));
48 static_assert(kUdpProtocol == IPPROTO_UDP);
49 #endif
50
CreateIpv4Header(int payload_length,quiche::QuicheIpAddress source_address,quiche::QuicheIpAddress destination_address,uint8_t protocol)51 std::string CreateIpv4Header(int payload_length,
52 quiche::QuicheIpAddress source_address,
53 quiche::QuicheIpAddress destination_address,
54 uint8_t protocol) {
55 QUICHE_CHECK_GT(payload_length, 0);
56 QUICHE_CHECK_LE(payload_length,
57 std::numeric_limits<uint16_t>::max() - kIpv4HeaderSize);
58 QUICHE_CHECK(source_address.address_family() ==
59 quiche::IpAddressFamily::IP_V4);
60 QUICHE_CHECK(destination_address.address_family() ==
61 quiche::IpAddressFamily::IP_V4);
62
63 std::string header(kIpv4HeaderSize, '\0');
64 quiche::QuicheDataWriter header_writer(header.size(), header.data());
65
66 header_writer.WriteUInt8(0x45); // Version: 4, Header length: 5 words
67 header_writer.WriteUInt8(0x00); // DSCP: 0, ECN: 0
68 header_writer.WriteUInt16(kIpv4HeaderSize + payload_length); // Total length
69 header_writer.WriteUInt16(0x0000); // Identification: 0 (replaced by socket)
70 header_writer.WriteUInt16(0x0000); // Flags: 0, Fragment offset: 0
71 header_writer.WriteUInt8(64); // TTL: 64 hops/seconds
72 header_writer.WriteUInt8(protocol);
73 header_writer.WriteUInt16(0x0000); // Checksum (replaced by socket)
74 header_writer.WriteStringPiece(source_address.ToPackedString());
75 header_writer.WriteStringPiece(destination_address.ToPackedString());
76 QUICHE_CHECK_EQ(header_writer.remaining(), 0u);
77
78 return header;
79 }
80
CreateIpv6Header(int payload_length,quiche::QuicheIpAddress source_address,quiche::QuicheIpAddress destination_address,uint8_t next_header)81 std::string CreateIpv6Header(int payload_length,
82 quiche::QuicheIpAddress source_address,
83 quiche::QuicheIpAddress destination_address,
84 uint8_t next_header) {
85 QUICHE_CHECK_GT(payload_length, 0);
86 QUICHE_CHECK_LE(payload_length, std::numeric_limits<uint16_t>::max());
87 QUICHE_CHECK(source_address.address_family() ==
88 quiche::IpAddressFamily::IP_V6);
89 QUICHE_CHECK(destination_address.address_family() ==
90 quiche::IpAddressFamily::IP_V6);
91
92 std::string header(kIpv6HeaderSize, '\0');
93 quiche::QuicheDataWriter header_writer(header.size(), header.data());
94
95 // Version: 6
96 // Traffic class: 0
97 // Flow label: 0 (possibly replaced by socket)
98 header_writer.WriteUInt32(0x60000000);
99
100 header_writer.WriteUInt16(payload_length);
101 header_writer.WriteUInt8(next_header);
102 header_writer.WriteUInt8(64); // Hop limit: 64
103 header_writer.WriteStringPiece(source_address.ToPackedString());
104 header_writer.WriteStringPiece(destination_address.ToPackedString());
105 QUICHE_CHECK_EQ(header_writer.remaining(), 0u);
106
107 return header;
108 }
109
110 } // namespace
111
CreateIpPacket(const quiche::QuicheIpAddress & source_address,const quiche::QuicheIpAddress & destination_address,absl::string_view payload,IpPacketPayloadType payload_type)112 std::string CreateIpPacket(const quiche::QuicheIpAddress& source_address,
113 const quiche::QuicheIpAddress& destination_address,
114 absl::string_view payload,
115 IpPacketPayloadType payload_type) {
116 QUICHE_CHECK(source_address.address_family() ==
117 destination_address.address_family());
118
119 uint8_t payload_protocol;
120 switch (payload_type) {
121 case IpPacketPayloadType::kUdp:
122 payload_protocol = kUdpProtocol;
123 break;
124 default:
125 QUICHE_NOTREACHED();
126 return "";
127 }
128
129 std::string header;
130 switch (source_address.address_family()) {
131 case quiche::IpAddressFamily::IP_V4:
132 header = CreateIpv4Header(payload.size(), source_address,
133 destination_address, payload_protocol);
134 break;
135 case quiche::IpAddressFamily::IP_V6:
136 header = CreateIpv6Header(payload.size(), source_address,
137 destination_address, payload_protocol);
138 break;
139 default:
140 QUICHE_NOTREACHED();
141 return "";
142 }
143
144 return absl::StrCat(header, payload);
145 }
146
CreateUdpPacket(const QuicSocketAddress & source_address,const QuicSocketAddress & destination_address,absl::string_view payload)147 std::string CreateUdpPacket(const QuicSocketAddress& source_address,
148 const QuicSocketAddress& destination_address,
149 absl::string_view payload) {
150 QUICHE_CHECK(source_address.host().address_family() ==
151 destination_address.host().address_family());
152 QUICHE_CHECK(!payload.empty());
153 QUICHE_CHECK_LE(payload.size(),
154 static_cast<uint16_t>(std::numeric_limits<uint16_t>::max() -
155 kUdpHeaderSize));
156
157 std::string header(kUdpHeaderSize, '\0');
158 quiche::QuicheDataWriter header_writer(header.size(), header.data());
159
160 header_writer.WriteUInt16(source_address.port());
161 header_writer.WriteUInt16(destination_address.port());
162 header_writer.WriteUInt16(kUdpHeaderSize + payload.size());
163
164 InternetChecksum checksum;
165 switch (source_address.host().address_family()) {
166 case quiche::IpAddressFamily::IP_V4: {
167 // IP pseudo header information. See RFC768.
168 checksum.Update(source_address.host().ToPackedString());
169 checksum.Update(destination_address.host().ToPackedString());
170 uint8_t protocol[] = {0x00, kUdpProtocol};
171 checksum.Update(protocol, sizeof(protocol));
172 uint16_t udp_length =
173 quiche::QuicheEndian::HostToNet16(kUdpHeaderSize + payload.size());
174 checksum.Update(reinterpret_cast<uint8_t*>(&udp_length),
175 sizeof(udp_length));
176 break;
177 }
178 case quiche::IpAddressFamily::IP_V6: {
179 // IP pseudo header information. See RFC8200, Section 8.1.
180 checksum.Update(source_address.host().ToPackedString());
181 checksum.Update(destination_address.host().ToPackedString());
182 uint32_t udp_length =
183 quiche::QuicheEndian::HostToNet32(kUdpHeaderSize + payload.size());
184 checksum.Update(reinterpret_cast<uint8_t*>(&udp_length),
185 sizeof(udp_length));
186 uint8_t protocol[] = {0x00, 0x00, 0x00, kUdpProtocol};
187 checksum.Update(protocol, sizeof(protocol));
188 break;
189 }
190 default:
191 QUICHE_NOTREACHED();
192 return "";
193 }
194
195 checksum.Update(header.data(), header.size());
196 checksum.Update(payload.data(), payload.size());
197 uint16_t checksum_val = checksum.Value();
198
199 // Checksum is always written in the same byte order in which it was
200 // calculated.
201 header_writer.WriteBytes(&checksum_val, sizeof(checksum_val));
202
203 QUICHE_CHECK_EQ(header_writer.remaining(), 0u);
204
205 return absl::StrCat(header, payload);
206 }
207
208 } // namespace quic::test
209