xref: /aosp_15_r20/external/cronet/net/third_party/quiche/src/quiche/quic/test_tools/test_ip_packets.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
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