blob: a4744d326b815b47c2c39b09e01eeaf0876f3127 [file] [log] [blame]
// Copyright 2017 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 "vm_tools/concierge/subnet_pool.h"
#include <arpa/inet.h>
#include <string>
#include <utility>
#include <base/bind.h>
#include <base/logging.h>
#include <base/memory/ptr_util.h>
#include <base/strings/stringprintf.h>
using std::string;
namespace vm_tools {
namespace concierge {
namespace {
// The 100.115.92.0/24 subnet is reserved and not publicly routable. This subnet
// is then sliced into the following IP pools:
// +-------+----------------+------------+----------------------+
// | 0-3 | 4-127 | 128-191 | 192-255 |
// +-------+----------------+------------+----------------------+
// | ARC++ | VM pool (/30) | Future use | Container pool (/28) |
// +-------+----------------+------------+----------------------+
// Within each /30 subnet:
// addr 0 - network identifier
// addr 1 - gateway (host) address
// addr 2 - VM (guest) address
// addr 3 - broadcast address
constexpr size_t kContainerAddressesPerIndex = 16;
constexpr size_t kContainerBaseAddress = 0x64735cc0; // 100.115.92.192
constexpr size_t kVmAddressesPerIndex = 4;
constexpr size_t kVmBaseAddress = 0x64735c00; // 100.115.92.0
constexpr size_t kContainerSubnetPrefix = 28;
constexpr size_t kVmSubnetPrefix = 30;
} // namespace
SubnetPool::Subnet::Subnet(uint32_t network_id,
size_t prefix,
base::Closure release_cb)
: network_id_(network_id),
prefix_(prefix),
release_cb_(std::move(release_cb)) {
CHECK_LT(prefix, 32);
}
SubnetPool::Subnet::~Subnet() {
release_cb_.Run();
}
uint32_t SubnetPool::Subnet::AddressAtOffset(uint32_t offset) const {
if (offset >= AvailableCount())
return INADDR_ANY;
// The first usable IP is after the network id.
return htonl(network_id_ + 1 + offset);
}
size_t SubnetPool::Subnet::AvailableCount() const {
// The available IP count is all IPs in a subnet, minus the network ID
// and the broadcast address.
return (1 << (32 - prefix_)) - 2;
}
uint32_t SubnetPool::Subnet::Netmask() const {
return htonl(0xffffffff << (32 - prefix_));
}
size_t SubnetPool::Subnet::Prefix() const {
return prefix_;
}
SubnetPool::SubnetPool() {
// The first address is always reserved for the ARC++ container.
vm_subnets_.set(0);
}
SubnetPool::~SubnetPool() {
// Clear the subnet reserved for ARC++ so that we can test if there are still
// allocated subnets out in the wild.
vm_subnets_.reset(0);
if (vm_subnets_.any() || container_subnets_.any()) {
LOG(ERROR) << "SubnetPool destroyed with unreleased subnets";
}
}
std::unique_ptr<SubnetPool::Subnet> SubnetPool::CreateVMForTesting(
size_t index) {
DCHECK(!vm_subnets_.test(index));
vm_subnets_.set(index);
return base::WrapUnique(new Subnet(
kVmBaseAddress + (index * kVmAddressesPerIndex), kVmSubnetPrefix,
base::Bind(&SubnetPool::ReleaseVM, weak_ptr_factory_.GetWeakPtr(),
index)));
}
std::unique_ptr<SubnetPool::Subnet> SubnetPool::CreateContainerForTesting(
size_t index) {
DCHECK(!container_subnets_.test(index));
container_subnets_.set(index);
return base::WrapUnique(
new Subnet(kContainerBaseAddress + (index * kContainerAddressesPerIndex),
kContainerSubnetPrefix,
base::Bind(&SubnetPool::ReleaseContainer,
weak_ptr_factory_.GetWeakPtr(), index)));
}
std::unique_ptr<SubnetPool::Subnet> SubnetPool::AllocateVM() {
// Find the first un-allocated subnet.
size_t index = 0;
while (index < vm_subnets_.size() && vm_subnets_.test(index)) {
++index;
}
if (index == vm_subnets_.size()) {
// All subnets are allocated.
return nullptr;
}
vm_subnets_.set(index);
return base::WrapUnique(new Subnet(
kVmBaseAddress + (index * kVmAddressesPerIndex), kVmSubnetPrefix,
base::Bind(&SubnetPool::ReleaseVM, weak_ptr_factory_.GetWeakPtr(),
index)));
}
std::unique_ptr<SubnetPool::Subnet> SubnetPool::AllocateContainer() {
// Find the first un-allocated subnet.
size_t index = 0;
while (index < container_subnets_.size() && container_subnets_.test(index)) {
++index;
}
if (index == container_subnets_.size()) {
// All subnets are allocated.
return nullptr;
}
container_subnets_.set(index);
return base::WrapUnique(
new Subnet(kContainerBaseAddress + (index * kContainerAddressesPerIndex),
kContainerSubnetPrefix,
base::Bind(&SubnetPool::ReleaseContainer,
weak_ptr_factory_.GetWeakPtr(), index)));
}
void SubnetPool::ReleaseVM(size_t index) {
DCHECK(vm_subnets_.test(index));
vm_subnets_.reset(index);
}
void SubnetPool::ReleaseContainer(size_t index) {
DCHECK(container_subnets_.test(index));
container_subnets_.reset(index);
}
} // namespace concierge
} // namespace vm_tools