| // 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 "quic/qbone/platform/icmp_packet.h" |
| |
| #include <netinet/ip6.h> |
| |
| #include "absl/strings/string_view.h" |
| #include "quic/qbone/platform/internet_checksum.h" |
| #include "common/quiche_endian.h" |
| |
| namespace quic { |
| namespace { |
| |
| constexpr size_t kIPv6AddressSize = sizeof(in6_addr); |
| constexpr size_t kIPv6HeaderSize = sizeof(ip6_hdr); |
| constexpr size_t kICMPv6HeaderSize = sizeof(icmp6_hdr); |
| constexpr size_t kIPv6MinPacketSize = 1280; |
| constexpr size_t kIcmpTtl = 64; |
| constexpr size_t kICMPv6BodyMaxSize = |
| kIPv6MinPacketSize - kIPv6HeaderSize - kICMPv6HeaderSize; |
| |
| struct ICMPv6Packet { |
| ip6_hdr ip_header; |
| icmp6_hdr icmp_header; |
| uint8_t body[kICMPv6BodyMaxSize]; |
| }; |
| |
| // pseudo header as described in RFC 2460 Section 8.1 (excluding addresses) |
| struct IPv6PseudoHeader { |
| uint32_t payload_size{}; |
| uint8_t zeros[3] = {0, 0, 0}; |
| uint8_t next_header = IPPROTO_ICMPV6; |
| }; |
| |
| } // namespace |
| |
| void CreateIcmpPacket(in6_addr src, |
| in6_addr dst, |
| const icmp6_hdr& icmp_header, |
| absl::string_view body, |
| const std::function<void(absl::string_view)>& cb) { |
| const size_t body_size = std::min(body.size(), kICMPv6BodyMaxSize); |
| const size_t payload_size = kICMPv6HeaderSize + body_size; |
| |
| ICMPv6Packet icmp_packet{}; |
| // Set version to 6. |
| icmp_packet.ip_header.ip6_vfc = 0x6 << 4; |
| // Set the payload size, protocol and TTL. |
| icmp_packet.ip_header.ip6_plen = |
| quiche::QuicheEndian::HostToNet16(payload_size); |
| icmp_packet.ip_header.ip6_nxt = IPPROTO_ICMPV6; |
| icmp_packet.ip_header.ip6_hops = kIcmpTtl; |
| // Set the source address to the specified self IP. |
| icmp_packet.ip_header.ip6_src = src; |
| icmp_packet.ip_header.ip6_dst = dst; |
| |
| icmp_packet.icmp_header = icmp_header; |
| // Per RFC 4443 Section 2.3, set checksum field to 0 prior to computing it |
| icmp_packet.icmp_header.icmp6_cksum = 0; |
| |
| IPv6PseudoHeader pseudo_header{}; |
| pseudo_header.payload_size = quiche::QuicheEndian::HostToNet32(payload_size); |
| |
| InternetChecksum checksum; |
| // Pseudoheader. |
| checksum.Update(icmp_packet.ip_header.ip6_src.s6_addr, kIPv6AddressSize); |
| checksum.Update(icmp_packet.ip_header.ip6_dst.s6_addr, kIPv6AddressSize); |
| checksum.Update(reinterpret_cast<char*>(&pseudo_header), |
| sizeof(pseudo_header)); |
| // ICMP header. |
| checksum.Update(reinterpret_cast<const char*>(&icmp_packet.icmp_header), |
| sizeof(icmp_packet.icmp_header)); |
| // Body. |
| checksum.Update(body.data(), body_size); |
| icmp_packet.icmp_header.icmp6_cksum = checksum.Value(); |
| |
| memcpy(icmp_packet.body, body.data(), body_size); |
| |
| const char* packet = reinterpret_cast<char*>(&icmp_packet); |
| const size_t packet_size = offsetof(ICMPv6Packet, body) + body_size; |
| |
| cb(absl::string_view(packet, packet_size)); |
| } |
| |
| } // namespace quic |