blob: c8a6517a4c3b0ba4f1afb3a01e91bd5211994c4c [file] [log] [blame]
// Copyright (c) 2020 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/bonnet/tun_device_controller.h"
#include <linux/rtnetlink.h>
#include "absl/time/clock.h"
#include "quic/platform/api/quic_logging.h"
#include "quic/qbone/qbone_constants.h"
ABSL_FLAG(bool, qbone_tun_device_replace_default_routing_rules, true,
"If true, will define a rule that points packets sourced from the "
"qbone interface to the qbone table. This is unnecessary in "
"environments with no other ipv6 route.");
namespace quic {
bool TunDeviceController::UpdateAddress(const IpRange& desired_range) {
if (!setup_tun_) {
return true;
}
NetlinkInterface::LinkInfo link_info{};
if (!netlink_->GetLinkInfo(ifname_, &link_info)) {
return false;
}
std::vector<NetlinkInterface::AddressInfo> addresses;
if (!netlink_->GetAddresses(link_info.index, 0, &addresses, nullptr)) {
return false;
}
QuicIpAddress desired_address = desired_range.FirstAddressInRange();
for (const auto& address : addresses) {
if (!netlink_->ChangeLocalAddress(
link_info.index, NetlinkInterface::Verb::kRemove,
address.interface_address, address.prefix_length, 0, 0, {})) {
return false;
}
}
bool address_updated = netlink_->ChangeLocalAddress(
link_info.index, NetlinkInterface::Verb::kAdd, desired_address,
desired_range.prefix_length(), IFA_F_PERMANENT | IFA_F_NODAD,
RT_SCOPE_LINK, {});
if (address_updated) {
current_address_ = desired_address;
}
return address_updated;
}
bool TunDeviceController::UpdateRoutes(
const IpRange& desired_range,
const std::vector<IpRange>& desired_routes) {
if (!setup_tun_) {
return true;
}
NetlinkInterface::LinkInfo link_info{};
if (!netlink_->GetLinkInfo(ifname_, &link_info)) {
QUIC_LOG(ERROR) << "Could not get link info for interface <" << ifname_
<< ">";
return false;
}
std::vector<NetlinkInterface::RoutingRule> routing_rules;
if (!netlink_->GetRouteInfo(&routing_rules)) {
QUIC_LOG(ERROR) << "Unable to get route info";
return false;
}
for (const auto& rule : routing_rules) {
if (rule.out_interface == link_info.index &&
rule.table == QboneConstants::kQboneRouteTableId) {
if (!netlink_->ChangeRoute(NetlinkInterface::Verb::kRemove,
rule.table, rule.destination_subnet,
rule.scope, rule.preferred_source,
rule.out_interface)) {
QUIC_LOG(ERROR) << "Unable to remove old route to <"
<< rule.destination_subnet.ToString() << ">";
return false;
}
}
}
if (!UpdateRules(desired_range)) {
return false;
}
QuicIpAddress desired_address = desired_range.FirstAddressInRange();
std::vector<IpRange> routes(desired_routes.begin(), desired_routes.end());
routes.emplace_back(*QboneConstants::TerminatorLocalAddressRange());
for (const auto& route : routes) {
if (!netlink_->ChangeRoute(NetlinkInterface::Verb::kReplace,
QboneConstants::kQboneRouteTableId, route,
RT_SCOPE_LINK, desired_address,
link_info.index)) {
QUIC_LOG(ERROR) << "Unable to add route <" << route.ToString() << ">";
return false;
}
}
return true;
}
bool TunDeviceController::UpdateRules(IpRange desired_range) {
if (!absl::GetFlag(FLAGS_qbone_tun_device_replace_default_routing_rules)) {
return true;
}
std::vector<NetlinkInterface::IpRule> ip_rules;
if (!netlink_->GetRuleInfo(&ip_rules)) {
QUIC_LOG(ERROR) << "Unable to get rule info";
return false;
}
for (const auto& rule : ip_rules) {
if (rule.table == QboneConstants::kQboneRouteTableId) {
if (!netlink_->ChangeRule(NetlinkInterface::Verb::kRemove,
rule.table, rule.source_range)) {
QUIC_LOG(ERROR) << "Unable to remove old rule for table <" << rule.table
<< "> from source <" << rule.source_range.ToString()
<< ">";
return false;
}
}
}
if (!netlink_->ChangeRule(NetlinkInterface::Verb::kAdd,
QboneConstants::kQboneRouteTableId,
desired_range)) {
QUIC_LOG(ERROR) << "Unable to add rule for <" << desired_range.ToString()
<< ">";
return false;
}
return true;
}
QuicIpAddress TunDeviceController::current_address() {
return current_address_;
}
} // namespace quic