|  | // Copyright (c) 2019 The Chromium Authors. All rights reserved. | 
|  | // Use of this source code is governed by a BSD-style license that can be | 
|  | // found in the LICENSE file. | 
|  |  | 
|  | #include "net/third_party/quiche/src/quic/qbone/platform/tcp_packet.h" | 
|  |  | 
|  | #include <netinet/ip6.h> | 
|  |  | 
|  | #include "net/third_party/quiche/src/quic/platform/api/quic_logging.h" | 
|  | #include "net/third_party/quiche/src/quic/qbone/platform/internet_checksum.h" | 
|  | #include "net/third_party/quiche/src/common/platform/api/quiche_endian.h" | 
|  | #include "net/third_party/quiche/src/common/platform/api/quiche_string_piece.h" | 
|  |  | 
|  | namespace quic { | 
|  | namespace { | 
|  |  | 
|  | constexpr size_t kIPv6AddressSize = sizeof(in6_addr); | 
|  | constexpr size_t kTcpTtl = 64; | 
|  |  | 
|  | struct TCPv6Packet { | 
|  | ip6_hdr ip_header; | 
|  | tcphdr tcp_header; | 
|  | }; | 
|  |  | 
|  | struct TCPv6PseudoHeader { | 
|  | uint32_t payload_size{}; | 
|  | uint8_t zeros[3] = {0, 0, 0}; | 
|  | uint8_t next_header = IPPROTO_TCP; | 
|  | }; | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | void CreateTcpResetPacket( | 
|  | quiche::QuicheStringPiece original_packet, | 
|  | const std::function<void(quiche::QuicheStringPiece)>& cb) { | 
|  | // By the time this method is called, original_packet should be fairly | 
|  | // strongly validated. However, it's better to be more paranoid than not, so | 
|  | // here are a bunch of very obvious checks. | 
|  | if (QUIC_PREDICT_FALSE(original_packet.size() < sizeof(ip6_hdr))) { | 
|  | return; | 
|  | } | 
|  | auto* ip6_header = reinterpret_cast<const ip6_hdr*>(original_packet.data()); | 
|  | if (QUIC_PREDICT_FALSE(ip6_header->ip6_vfc >> 4 != 6)) { | 
|  | return; | 
|  | } | 
|  | if (QUIC_PREDICT_FALSE(ip6_header->ip6_nxt != IPPROTO_TCP)) { | 
|  | return; | 
|  | } | 
|  | if (QUIC_PREDICT_FALSE(quiche::QuicheEndian::NetToHost16( | 
|  | ip6_header->ip6_plen) < sizeof(tcphdr))) { | 
|  | return; | 
|  | } | 
|  | auto* tcp_header = reinterpret_cast<const tcphdr*>(ip6_header + 1); | 
|  |  | 
|  | // Now that the original packet has been confirmed to be well-formed, it's | 
|  | // time to make the TCP RST packet. | 
|  | TCPv6Packet tcp_packet{}; | 
|  |  | 
|  | const size_t payload_size = sizeof(tcphdr); | 
|  |  | 
|  | // Set version to 6. | 
|  | tcp_packet.ip_header.ip6_vfc = 0x6 << 4; | 
|  | // Set the payload size, protocol and TTL. | 
|  | tcp_packet.ip_header.ip6_plen = | 
|  | quiche::QuicheEndian::HostToNet16(payload_size); | 
|  | tcp_packet.ip_header.ip6_nxt = IPPROTO_TCP; | 
|  | tcp_packet.ip_header.ip6_hops = kTcpTtl; | 
|  | // Since the TCP RST is impersonating the endpoint, flip the source and | 
|  | // destination addresses from the original packet. | 
|  | tcp_packet.ip_header.ip6_src = ip6_header->ip6_dst; | 
|  | tcp_packet.ip_header.ip6_dst = ip6_header->ip6_src; | 
|  |  | 
|  | // The same is true about the TCP ports | 
|  | tcp_packet.tcp_header.dest = tcp_header->source; | 
|  | tcp_packet.tcp_header.source = tcp_header->dest; | 
|  |  | 
|  | // There are no extensions in this header, so size is trivial | 
|  | tcp_packet.tcp_header.doff = sizeof(tcphdr) >> 2; | 
|  | // Checksum is 0 before it is computed | 
|  | tcp_packet.tcp_header.check = 0; | 
|  |  | 
|  | // Per RFC 793, TCP RST comes in one of 3 flavors: | 
|  | // | 
|  | // * connection CLOSED | 
|  | // * connection in non-synchronized state (LISTEN, SYN-SENT, SYN-RECEIVED) | 
|  | // * connection in synchronized state (ESTABLISHED, FIN-WAIT-1, etc.) | 
|  | // | 
|  | // QBONE is acting like a firewall, so the RFC text of interest is the CLOSED | 
|  | // state. Note, however, that it is possible for a connection to actually be | 
|  | // in the FIN-WAIT-1 state on the remote end, but the processing logic does | 
|  | // not change. | 
|  | tcp_packet.tcp_header.rst = 1; | 
|  |  | 
|  | // If the incoming segment has an ACK field, the reset takes its sequence | 
|  | // number from the ACK field of the segment, | 
|  | if (tcp_header->ack) { | 
|  | tcp_packet.tcp_header.seq = tcp_header->ack_seq; | 
|  | } else { | 
|  | // Otherwise the reset has sequence number zero and the ACK field is set to | 
|  | // the sum of the sequence number and segment length of the incoming segment | 
|  | tcp_packet.tcp_header.ack = 1; | 
|  | tcp_packet.tcp_header.seq = 0; | 
|  | tcp_packet.tcp_header.ack_seq = quiche::QuicheEndian::HostToNet32( | 
|  | quiche::QuicheEndian::NetToHost32(tcp_header->seq) + 1); | 
|  | } | 
|  |  | 
|  | TCPv6PseudoHeader pseudo_header{}; | 
|  | pseudo_header.payload_size = quiche::QuicheEndian::HostToNet32(payload_size); | 
|  |  | 
|  | InternetChecksum checksum; | 
|  | // Pseudoheader. | 
|  | checksum.Update(tcp_packet.ip_header.ip6_src.s6_addr, kIPv6AddressSize); | 
|  | checksum.Update(tcp_packet.ip_header.ip6_dst.s6_addr, kIPv6AddressSize); | 
|  | checksum.Update(reinterpret_cast<char*>(&pseudo_header), | 
|  | sizeof(pseudo_header)); | 
|  | // TCP header. | 
|  | checksum.Update(reinterpret_cast<const char*>(&tcp_packet.tcp_header), | 
|  | sizeof(tcp_packet.tcp_header)); | 
|  | // There is no body. | 
|  | tcp_packet.tcp_header.check = checksum.Value(); | 
|  |  | 
|  | const char* packet = reinterpret_cast<char*>(&tcp_packet); | 
|  |  | 
|  | cb(quiche::QuicheStringPiece(packet, sizeof(tcp_packet))); | 
|  | } | 
|  |  | 
|  | }  // namespace quic |