blob: 8fa6f4b3f71bcff65e49d8f44f495f846e3fe7fe [file] [log] [blame]
// Copyright 2018 The Chromium OS 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 "patchpanel/subnet.h"
#include <arpa/inet.h>
#include <string>
#include <utility>
#include <base/bind.h>
#include <base/logging.h>
#include "patchpanel/net_util.h"
namespace {
// Returns the offset from the base address given in network-byte order for
// the address given in network-byte order, or 0 if the second address is
// lower than the base address. Returns the offset in host-byte order.
uint32_t OffsetFromBaseAddress(uint32_t base_no, uint32_t addr_no) {
if (ntohl(addr_no) < ntohl(base_no))
return 0;
return ntohl(addr_no) - ntohl(base_no);
}
// Adds a positive offset given in host order to the address given in
// network byte order. Returns the address in network-byte order.
uint32_t AddOffset(uint32_t addr_no, uint32_t offset_ho) {
return htonl(ntohl(addr_no) + offset_ho);
}
} // namespace
namespace patchpanel {
SubnetAddress::SubnetAddress(uint32_t addr,
uint32_t prefix_length,
base::Closure release_cb)
: addr_(addr),
prefix_length_(prefix_length),
release_cb_(std::move(release_cb)) {}
SubnetAddress::~SubnetAddress() {
release_cb_.Run();
}
uint32_t SubnetAddress::Address() const {
return addr_;
}
std::string SubnetAddress::ToCidrString() const {
return IPv4AddressToCidrString(addr_, prefix_length_);
}
std::string SubnetAddress::ToIPv4String() const {
return IPv4AddressToString(addr_);
}
uint32_t SubnetAddress::Netmask() const {
return Ipv4Netmask(prefix_length_);
}
Subnet::Subnet(uint32_t base_addr,
uint32_t prefix_length,
base::Closure release_cb)
: base_addr_(base_addr),
prefix_length_(prefix_length),
release_cb_(std::move(release_cb)),
weak_factory_(this) {
CHECK_LT(prefix_length, 32);
addrs_.resize(1ull << (32 - prefix_length), false);
// Mark the base address and broadcast address as allocated.
addrs_.front() = true;
addrs_.back() = true;
}
Subnet::~Subnet() {
release_cb_.Run();
}
std::unique_ptr<SubnetAddress> Subnet::Allocate(uint32_t addr) {
return AllocateAtOffset(OffsetFromBaseAddress(base_addr_, addr) - 1);
}
std::unique_ptr<SubnetAddress> Subnet::AllocateAtOffset(uint32_t offset) {
uint32_t addr = AddressAtOffset(offset);
if (addr == INADDR_ANY) {
return nullptr;
}
if (addrs_[offset + 1]) {
// Address is already allocated.
return nullptr;
}
addrs_[offset + 1] = true;
return std::make_unique<SubnetAddress>(
addr, prefix_length_,
base::Bind(&Subnet::Free, weak_factory_.GetWeakPtr(), offset + 1));
}
uint32_t Subnet::AddressAtOffset(uint32_t offset) const {
if (offset < 0 || offset >= AvailableCount())
return INADDR_ANY;
// The first usable IP is after the base address.
return AddOffset(base_addr_, 1 + offset);
}
uint32_t Subnet::AvailableCount() const {
// The available IP count is all IPs in a subnet, minus the network ID
// and the broadcast address.
return addrs_.size() - 2;
}
uint32_t Subnet::BaseAddress() const {
return base_addr_;
}
uint32_t Subnet::Netmask() const {
return Ipv4Netmask(prefix_length_);
}
uint32_t Subnet::Prefix() const {
return base_addr_ & Netmask();
}
uint32_t Subnet::PrefixLength() const {
return prefix_length_;
}
std::string Subnet::ToCidrString() const {
return IPv4AddressToCidrString(base_addr_, prefix_length_);
}
void Subnet::Free(uint32_t offset) {
DCHECK_NE(offset, 0);
DCHECK_LT(offset, addrs_.size() - 1);
addrs_[offset] = false;
}
} // namespace patchpanel