blob: 0acd71216bf3be0e62b91f57a52b564c59b44ce0 [file] [log] [blame]
// 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;
// Hop limit set to 255 to satisfy:
// https://datatracker.ietf.org/doc/html/rfc4861#section-11.2
constexpr size_t kIcmpTtl = 255;
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