blob: f8d8e044c5e217c3b792eec007333220b710a639 [file] [log] [blame]
// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ip_address.h"
#include <stdlib.h>
#include <string.h>
#include <climits>
#include "check.h"
namespace bssl {
namespace fillins {
IPAddress::IPAddress() {}
IPAddress::IPAddress(const uint8_t *address, size_t address_len)
: addr_(reinterpret_cast<const char *>(address), address_len) {}
IPAddress::IPAddress(uint8_t b0, uint8_t b1, uint8_t b2, uint8_t b3) {
addr_.reserve(4);
addr_.push_back(b0);
addr_.push_back(b1);
addr_.push_back(b2);
addr_.push_back(b3);
}
IPAddress::IPAddress(uint8_t b0, uint8_t b1, uint8_t b2, uint8_t b3, uint8_t b4,
uint8_t b5, uint8_t b6, uint8_t b7, uint8_t b8, uint8_t b9,
uint8_t b10, uint8_t b11, uint8_t b12, uint8_t b13,
uint8_t b14, uint8_t b15) {
addr_.reserve(16);
addr_.push_back(b0);
addr_.push_back(b1);
addr_.push_back(b2);
addr_.push_back(b3);
addr_.push_back(b4);
addr_.push_back(b5);
addr_.push_back(b6);
addr_.push_back(b7);
addr_.push_back(b8);
addr_.push_back(b9);
addr_.push_back(b10);
addr_.push_back(b11);
addr_.push_back(b12);
addr_.push_back(b13);
addr_.push_back(b14);
addr_.push_back(b15);
}
// static
IPAddress IPAddress::AllZeros(size_t num_zero_bytes) {
CHECK_LE(num_zero_bytes, 16u);
IPAddress result;
result.addr_.reserve(num_zero_bytes);
for (size_t i = 0; i < num_zero_bytes; ++i) {
result.addr_.push_back(0u);
}
return result;
}
// static
IPAddress IPAddress::IPv4AllZeros() { return AllZeros(kIPv4AddressSize); }
bool IPAddress::IsIPv4() const { return addr_.size() == kIPv4AddressSize; }
bool IPAddress::IsIPv6() const { return addr_.size() == kIPv6AddressSize; }
bool IPAddress::IsValid() const { return IsIPv4() || IsIPv6(); }
const uint8_t *IPAddress::data() const {
return reinterpret_cast<const uint8_t *>(addr_.data());
}
size_t IPAddress::size() const { return addr_.size(); }
const IPAddressBytes &IPAddress::bytes() const { return addr_; }
static IPAddress ConvertIPv4ToIPv4MappedIPv6(const IPAddress &address) {
CHECK(address.IsIPv4());
// IPv4-mapped addresses are formed by:
// <80 bits of zeros> + <16 bits of ones> + <32-bit IPv4 address>.
uint8_t bytes[16];
memset(bytes, 0, 10);
memset(bytes + 10, 0xff, 2);
memcpy(bytes + 12, address.data(), address.size());
return IPAddress(bytes, sizeof(bytes));
}
// Note that this function assumes:
// * |ip_address| is at least |prefix_length_in_bits| (bits) long;
// * |ip_prefix| is at least |prefix_length_in_bits| (bits) long.
static bool IPAddressPrefixCheck(const uint8_t *ip_address,
const uint8_t *ip_prefix,
size_t prefix_length_in_bits) {
// Compare all the bytes that fall entirely within the prefix.
size_t num_entire_bytes_in_prefix = prefix_length_in_bits / 8;
for (size_t i = 0; i < num_entire_bytes_in_prefix; ++i) {
if (ip_address[i] != ip_prefix[i]) {
return false;
}
}
// In case the prefix was not a multiple of 8, there will be 1 byte
// which is only partially masked.
size_t remaining_bits = prefix_length_in_bits % 8;
if (remaining_bits != 0) {
uint8_t mask = 0xFF << (8 - remaining_bits);
size_t i = num_entire_bytes_in_prefix;
if ((ip_address[i] & mask) != (ip_prefix[i] & mask)) {
return false;
}
}
return true;
}
bool IPAddressMatchesPrefix(const IPAddress &ip_address,
const IPAddress &ip_prefix,
size_t prefix_length_in_bits) {
// Both the input IP address and the prefix IP address should be either IPv4
// or IPv6.
DCHECK(ip_address.IsValid());
DCHECK(ip_prefix.IsValid());
DCHECK_LE(prefix_length_in_bits, ip_prefix.size() * 8);
// In case we have an IPv6 / IPv4 mismatch, convert the IPv4 addresses to
// IPv6 addresses in order to do the comparison.
if (ip_address.size() != ip_prefix.size()) {
if (ip_address.IsIPv4()) {
return IPAddressMatchesPrefix(ConvertIPv4ToIPv4MappedIPv6(ip_address),
ip_prefix, prefix_length_in_bits);
}
return IPAddressMatchesPrefix(ip_address,
ConvertIPv4ToIPv4MappedIPv6(ip_prefix),
96 + prefix_length_in_bits);
}
return IPAddressPrefixCheck(ip_address.data(), ip_prefix.data(),
prefix_length_in_bits);
}
static unsigned CommonPrefixLength(const IPAddress &a1, const IPAddress &a2) {
DCHECK_EQ(a1.size(), a2.size());
for (size_t i = 0; i < a1.size(); ++i) {
uint8_t diff = a1.bytes()[i] ^ a2.bytes()[i];
if (!diff)
continue;
for (unsigned j = 0; j < CHAR_BIT; ++j) {
if (diff & (1 << (CHAR_BIT - 1)))
return i * CHAR_BIT + j;
diff <<= 1;
}
abort();
}
return a1.size() * CHAR_BIT;
}
unsigned MaskPrefixLength(const IPAddress &mask) {
uint8_t all_ones[16];
const size_t mask_len = std::min(mask.size(), sizeof(all_ones));
memset(all_ones, 0xff, mask_len);
return CommonPrefixLength(mask, IPAddress(all_ones, mask_len));
}
} // namespace fillins
} // namespace bssl