xref: /aosp_15_r20/external/cronet/net/third_party/quiche/src/quiche/quic/qbone/platform/tcp_packet.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1 // Copyright (c) 2019 The Chromium Authors. All rights reserved.
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/qbone/platform/tcp_packet.h"
6 
7 #include <netinet/ip6.h>
8 
9 #include "absl/base/optimization.h"
10 #include "absl/strings/string_view.h"
11 #include "quiche/quic/core/internet_checksum.h"
12 #include "quiche/quic/platform/api/quic_logging.h"
13 #include "quiche/common/quiche_callbacks.h"
14 #include "quiche/common/quiche_endian.h"
15 
16 namespace quic {
17 namespace {
18 
19 constexpr size_t kIPv6AddressSize = sizeof(in6_addr);
20 constexpr size_t kTcpTtl = 64;
21 
22 struct TCPv6Packet {
23   ip6_hdr ip_header;
24   tcphdr tcp_header;
25 };
26 
27 struct TCPv6PseudoHeader {
28   uint32_t payload_size{};
29   uint8_t zeros[3] = {0, 0, 0};
30   uint8_t next_header = IPPROTO_TCP;
31 };
32 
33 }  // namespace
34 
CreateTcpResetPacket(absl::string_view original_packet,quiche::UnretainedCallback<void (absl::string_view)> cb)35 void CreateTcpResetPacket(
36     absl::string_view original_packet,
37     quiche::UnretainedCallback<void(absl::string_view)> cb) {
38   // By the time this method is called, original_packet should be fairly
39   // strongly validated. However, it's better to be more paranoid than not, so
40   // here are a bunch of very obvious checks.
41   if (ABSL_PREDICT_FALSE(original_packet.size() < sizeof(ip6_hdr))) {
42     return;
43   }
44   auto* ip6_header = reinterpret_cast<const ip6_hdr*>(original_packet.data());
45   if (ABSL_PREDICT_FALSE(ip6_header->ip6_vfc >> 4 != 6)) {
46     return;
47   }
48   if (ABSL_PREDICT_FALSE(ip6_header->ip6_nxt != IPPROTO_TCP)) {
49     return;
50   }
51   if (ABSL_PREDICT_FALSE(quiche::QuicheEndian::NetToHost16(
52                              ip6_header->ip6_plen) < sizeof(tcphdr))) {
53     return;
54   }
55   auto* tcp_header = reinterpret_cast<const tcphdr*>(ip6_header + 1);
56 
57   // Now that the original packet has been confirmed to be well-formed, it's
58   // time to make the TCP RST packet.
59   TCPv6Packet tcp_packet{};
60 
61   const size_t payload_size = sizeof(tcphdr);
62 
63   // Set version to 6.
64   tcp_packet.ip_header.ip6_vfc = 0x6 << 4;
65   // Set the payload size, protocol and TTL.
66   tcp_packet.ip_header.ip6_plen =
67       quiche::QuicheEndian::HostToNet16(payload_size);
68   tcp_packet.ip_header.ip6_nxt = IPPROTO_TCP;
69   tcp_packet.ip_header.ip6_hops = kTcpTtl;
70   // Since the TCP RST is impersonating the endpoint, flip the source and
71   // destination addresses from the original packet.
72   tcp_packet.ip_header.ip6_src = ip6_header->ip6_dst;
73   tcp_packet.ip_header.ip6_dst = ip6_header->ip6_src;
74 
75   // The same is true about the TCP ports
76   tcp_packet.tcp_header.dest = tcp_header->source;
77   tcp_packet.tcp_header.source = tcp_header->dest;
78 
79   // There are no extensions in this header, so size is trivial
80   tcp_packet.tcp_header.doff = sizeof(tcphdr) >> 2;
81   // Checksum is 0 before it is computed
82   tcp_packet.tcp_header.check = 0;
83 
84   // Per RFC 793, TCP RST comes in one of 3 flavors:
85   //
86   // * connection CLOSED
87   // * connection in non-synchronized state (LISTEN, SYN-SENT, SYN-RECEIVED)
88   // * connection in synchronized state (ESTABLISHED, FIN-WAIT-1, etc.)
89   //
90   // QBONE is acting like a firewall, so the RFC text of interest is the CLOSED
91   // state. Note, however, that it is possible for a connection to actually be
92   // in the FIN-WAIT-1 state on the remote end, but the processing logic does
93   // not change.
94   tcp_packet.tcp_header.rst = 1;
95 
96   // If the incoming segment has an ACK field, the reset takes its sequence
97   // number from the ACK field of the segment,
98   if (tcp_header->ack) {
99     tcp_packet.tcp_header.seq = tcp_header->ack_seq;
100   } else {
101     // Otherwise the reset has sequence number zero and the ACK field is set to
102     // the sum of the sequence number and segment length of the incoming segment
103     tcp_packet.tcp_header.ack = 1;
104     tcp_packet.tcp_header.seq = 0;
105     tcp_packet.tcp_header.ack_seq = quiche::QuicheEndian::HostToNet32(
106         quiche::QuicheEndian::NetToHost32(tcp_header->seq) + 1);
107   }
108 
109   TCPv6PseudoHeader pseudo_header{};
110   pseudo_header.payload_size = quiche::QuicheEndian::HostToNet32(payload_size);
111 
112   InternetChecksum checksum;
113   // Pseudoheader.
114   checksum.Update(tcp_packet.ip_header.ip6_src.s6_addr, kIPv6AddressSize);
115   checksum.Update(tcp_packet.ip_header.ip6_dst.s6_addr, kIPv6AddressSize);
116   checksum.Update(reinterpret_cast<char*>(&pseudo_header),
117                   sizeof(pseudo_header));
118   // TCP header.
119   checksum.Update(reinterpret_cast<const char*>(&tcp_packet.tcp_header),
120                   sizeof(tcp_packet.tcp_header));
121   // There is no body.
122   tcp_packet.tcp_header.check = checksum.Value();
123 
124   const char* packet = reinterpret_cast<char*>(&tcp_packet);
125 
126   cb(absl::string_view(packet, sizeof(tcp_packet)));
127 }
128 
129 }  // namespace quic
130