xref: /aosp_15_r20/external/cronet/net/third_party/quiche/src/quiche/quic/qbone/platform/icmp_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/icmp_packet.h"
6 
7 #include <netinet/ip6.h>
8 
9 #include "absl/strings/string_view.h"
10 #include "quiche/quic/core/internet_checksum.h"
11 #include "quiche/common/quiche_callbacks.h"
12 #include "quiche/common/quiche_endian.h"
13 
14 namespace quic {
15 namespace {
16 
17 constexpr size_t kIPv6AddressSize = sizeof(in6_addr);
18 constexpr size_t kIPv6HeaderSize = sizeof(ip6_hdr);
19 constexpr size_t kICMPv6HeaderSize = sizeof(icmp6_hdr);
20 constexpr size_t kIPv6MinPacketSize = 1280;
21 
22 // Hop limit set to 255 to satisfy:
23 // https://datatracker.ietf.org/doc/html/rfc4861#section-11.2
24 constexpr size_t kIcmpTtl = 255;
25 constexpr size_t kICMPv6BodyMaxSize =
26     kIPv6MinPacketSize - kIPv6HeaderSize - kICMPv6HeaderSize;
27 
28 struct ICMPv6Packet {
29   ip6_hdr ip_header;
30   icmp6_hdr icmp_header;
31   uint8_t body[kICMPv6BodyMaxSize];
32 };
33 
34 // pseudo header as described in RFC 2460 Section 8.1 (excluding addresses)
35 struct IPv6PseudoHeader {
36   uint32_t payload_size{};
37   uint8_t zeros[3] = {0, 0, 0};
38   uint8_t next_header = IPPROTO_ICMPV6;
39 };
40 
41 }  // namespace
42 
CreateIcmpPacket(in6_addr src,in6_addr dst,const icmp6_hdr & icmp_header,absl::string_view body,quiche::UnretainedCallback<void (absl::string_view)> cb)43 void CreateIcmpPacket(in6_addr src, in6_addr dst, const icmp6_hdr& icmp_header,
44                       absl::string_view body,
45                       quiche::UnretainedCallback<void(absl::string_view)> cb) {
46   const size_t body_size = std::min(body.size(), kICMPv6BodyMaxSize);
47   const size_t payload_size = kICMPv6HeaderSize + body_size;
48 
49   ICMPv6Packet icmp_packet{};
50   // Set version to 6.
51   icmp_packet.ip_header.ip6_vfc = 0x6 << 4;
52   // Set the payload size, protocol and TTL.
53   icmp_packet.ip_header.ip6_plen =
54       quiche::QuicheEndian::HostToNet16(payload_size);
55   icmp_packet.ip_header.ip6_nxt = IPPROTO_ICMPV6;
56   icmp_packet.ip_header.ip6_hops = kIcmpTtl;
57   // Set the source address to the specified self IP.
58   icmp_packet.ip_header.ip6_src = src;
59   icmp_packet.ip_header.ip6_dst = dst;
60 
61   icmp_packet.icmp_header = icmp_header;
62   // Per RFC 4443 Section 2.3, set checksum field to 0 prior to computing it
63   icmp_packet.icmp_header.icmp6_cksum = 0;
64 
65   IPv6PseudoHeader pseudo_header{};
66   pseudo_header.payload_size = quiche::QuicheEndian::HostToNet32(payload_size);
67 
68   InternetChecksum checksum;
69   // Pseudoheader.
70   checksum.Update(icmp_packet.ip_header.ip6_src.s6_addr, kIPv6AddressSize);
71   checksum.Update(icmp_packet.ip_header.ip6_dst.s6_addr, kIPv6AddressSize);
72   checksum.Update(reinterpret_cast<char*>(&pseudo_header),
73                   sizeof(pseudo_header));
74   // ICMP header.
75   checksum.Update(reinterpret_cast<const char*>(&icmp_packet.icmp_header),
76                   sizeof(icmp_packet.icmp_header));
77   // Body.
78   checksum.Update(body.data(), body_size);
79   icmp_packet.icmp_header.icmp6_cksum = checksum.Value();
80 
81   memcpy(icmp_packet.body, body.data(), body_size);
82 
83   const char* packet = reinterpret_cast<char*>(&icmp_packet);
84   const size_t packet_size = offsetof(ICMPv6Packet, body) + body_size;
85 
86   cb(absl::string_view(packet, packet_size));
87 }
88 
89 }  // namespace quic
90