blob: 7d864988dbb5e2adaa0d54dc0eae7a312746e92d [file] [log] [blame]
// Copyright 2019 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/arc_service.h"
#include <linux/rtnetlink.h>
#include <net/if.h>
#include <sys/ioctl.h>
#include <utility>
#include <vector>
#include <base/bind.h>
#include <base/files/file_path.h>
#include <base/files/file_util.h>
#include <base/logging.h>
#include <base/strings/string_number_conversions.h>
#include <base/strings/string_util.h>
#include <base/strings/stringprintf.h>
#include <brillo/key_value_store.h>
#include <chromeos/constants/vm_tools.h>
#include "patchpanel/datapath.h"
#include "patchpanel/mac_address_generator.h"
#include "patchpanel/manager.h"
#include "patchpanel/minijailed_process_runner.h"
#include "patchpanel/net_util.h"
#include "patchpanel/scoped_ns.h"
namespace patchpanel {
namespace test {
GuestMessage::GuestType guest = GuestMessage::UNKNOWN_GUEST;
} // namespace test
namespace {
constexpr pid_t kInvalidPID = 0;
constexpr uint32_t kInvalidCID = 0;
constexpr char kArcIfname[] = "arc0";
constexpr char kArcBridge[] = "arcbr0";
constexpr char kArcVmIfname[] = "arc1";
constexpr char kArcVmBridge[] = "arc_br1";
constexpr std::array<const char*, 2> kEthernetInterfacePrefixes{{"eth", "usb"}};
constexpr std::array<const char*, 2> kWifiInterfacePrefixes{{"wlan", "mlan"}};
constexpr std::array<const char*, 2> kCellInterfacePrefixes{{"wwan", "rmnet"}};
void OneTimeSetup(const Datapath& datapath) {
static bool done = false;
if (done)
return;
auto& runner = datapath.runner();
// Load networking modules needed by Android that are not compiled in the
// kernel. Android does not allow auto-loading of kernel modules.
// These must succeed.
if (runner.modprobe_all({
// The netfilter modules needed by netd for iptables commands.
"ip6table_filter",
"ip6t_ipv6header",
"ip6t_REJECT",
// The xfrm modules needed for Android's ipsec APIs.
"xfrm4_mode_transport",
"xfrm4_mode_tunnel",
"xfrm6_mode_transport",
"xfrm6_mode_tunnel",
// The ipsec modules for AH and ESP encryption for ipv6.
"ah6",
"esp6",
}) != 0) {
LOG(ERROR) << "One or more required kernel modules failed to load."
<< " Some Android functionality may be broken.";
}
// Optional modules.
if (runner.modprobe_all({
// This module is not available in kernels < 3.18
"nf_reject_ipv6",
// These modules are needed for supporting Chrome traffic on Android
// VPN which uses Android's NAT feature. Android NAT sets up
// iptables
// rules that use these conntrack modules for FTP/TFTP.
"nf_nat_ftp",
"nf_nat_tftp",
// The tun module is needed by the Android 464xlat clatd process.
"tun",
}) != 0) {
LOG(WARNING) << "One or more optional kernel modules failed to load.";
}
// This is only needed for CTS (b/27932574).
if (runner.chown("655360", "655360", "/sys/class/xt_idletimer") != 0) {
LOG(ERROR) << "Failed to change ownership of xt_idletimer.";
}
done = true;
}
bool IsArcVm() {
if (test::guest == GuestMessage::ARC_VM) {
LOG(WARNING) << "Overridden for testing";
return true;
}
const base::FilePath path("/run/chrome/is_arcvm");
std::string contents;
if (!base::ReadFileToString(path, &contents)) {
PLOG(ERROR) << "Could not read " << path.value();
}
return contents == "1";
}
GuestMessage::GuestType ArcGuest() {
if (test::guest != GuestMessage::UNKNOWN_GUEST)
return test::guest;
return IsArcVm() ? GuestMessage::ARC_VM : GuestMessage::ARC;
}
ArcService::InterfaceType InterfaceTypeFor(const std::string& ifname) {
for (const auto& prefix : kEthernetInterfacePrefixes) {
if (base::StartsWith(ifname, prefix,
base::CompareCase::INSENSITIVE_ASCII)) {
return ArcService::InterfaceType::ETHERNET;
}
}
for (const auto& prefix : kWifiInterfacePrefixes) {
if (base::StartsWith(ifname, prefix,
base::CompareCase::INSENSITIVE_ASCII)) {
return ArcService::InterfaceType::WIFI;
}
}
for (const auto& prefix : kCellInterfacePrefixes) {
if (base::StartsWith(ifname, prefix,
base::CompareCase::INSENSITIVE_ASCII)) {
return ArcService::InterfaceType::CELL;
}
}
return ArcService::InterfaceType::UNKNOWN;
}
bool IsMulticastInterface(const std::string& ifname) {
if (ifname.empty()) {
return false;
}
int fd = socket(AF_INET, SOCK_DGRAM, 0);
if (fd < 0) {
// If IPv4 fails, try to open a socket using IPv6.
fd = socket(AF_INET6, SOCK_DGRAM, 0);
if (fd < 0) {
LOG(ERROR) << "Unable to create socket";
return false;
}
}
struct ifreq ifr;
memset(&ifr, 0, sizeof(ifr));
strncpy(ifr.ifr_name, ifname.c_str(), IFNAMSIZ);
if (ioctl(fd, SIOCGIFFLAGS, &ifr) < 0) {
PLOG(ERROR) << "SIOCGIFFLAGS failed for " << ifname;
close(fd);
return false;
}
close(fd);
return (ifr.ifr_flags & IFF_MULTICAST);
}
// Returns the configuration for the ARC management interface used for VPN
// forwarding, ADB-over-TCP and single-networked ARCVM.
std::unique_ptr<Device::Config> MakeArcConfig(AddressManager* addr_mgr,
AddressManager::Guest guest) {
auto ipv4_subnet = addr_mgr->AllocateIPv4Subnet(guest);
if (!ipv4_subnet) {
LOG(ERROR) << "Subnet already in use or unavailable";
return nullptr;
}
auto host_ipv4_addr = ipv4_subnet->AllocateAtOffset(0);
if (!host_ipv4_addr) {
LOG(ERROR) << "Bridge address already in use or unavailable";
return nullptr;
}
auto guest_ipv4_addr = ipv4_subnet->AllocateAtOffset(1);
if (!guest_ipv4_addr) {
LOG(ERROR) << "ARC address already in use or unavailable";
return nullptr;
}
return std::make_unique<Device::Config>(
addr_mgr->GenerateMacAddress(IsArcVm() ? 1 : kAnySubnetIndex),
std::move(ipv4_subnet), std::move(host_ipv4_addr),
std::move(guest_ipv4_addr));
}
} // namespace
ArcService::ArcService(ShillClient* shill_client,
Datapath* datapath,
AddressManager* addr_mgr,
TrafficForwarder* forwarder,
bool enable_arcvm_multinet)
: shill_client_(shill_client),
datapath_(datapath),
addr_mgr_(addr_mgr),
forwarder_(forwarder),
enable_arcvm_multinet_(enable_arcvm_multinet) {
AllocateAddressConfigs();
shill_client_->RegisterDevicesChangedHandler(
base::Bind(&ArcService::OnDevicesChanged, weak_factory_.GetWeakPtr()));
shill_client_->ScanDevices();
shill_client_->RegisterDefaultInterfaceChangedHandler(base::Bind(
&ArcService::OnDefaultInterfaceChanged, weak_factory_.GetWeakPtr()));
}
ArcService::~ArcService() {
if (impl_) {
Stop(impl_->id());
}
}
void ArcService::AllocateAddressConfigs() {
configs_.clear();
// The first usable subnet is the "other" ARC device subnet.
// TODO(garrick): This can be removed and ARC_NET will be widened once ARCVM
// switches over to use .0/30.
const bool is_arcvm = IsArcVm();
AddressManager::Guest alloc =
is_arcvm ? AddressManager::Guest::ARC : AddressManager::Guest::VM_ARC;
// As a temporary workaround, for ARCVM, allocate fixed MAC addresses.
uint8_t mac_addr_index = 2;
// Allocate 2 subnets each for Ethernet and WiFi and 1 for LTE WAN interfaces.
for (const auto itype :
{InterfaceType::ETHERNET, InterfaceType::ETHERNET, InterfaceType::WIFI,
InterfaceType::WIFI, InterfaceType::CELL}) {
auto ipv4_subnet = addr_mgr_->AllocateIPv4Subnet(alloc);
if (!ipv4_subnet) {
LOG(ERROR) << "Subnet already in use or unavailable";
continue;
}
// For here out, use the same slices.
alloc = AddressManager::Guest::ARC_NET;
auto host_ipv4_addr = ipv4_subnet->AllocateAtOffset(0);
if (!host_ipv4_addr) {
LOG(ERROR) << "Bridge address already in use or unavailable";
continue;
}
auto guest_ipv4_addr = ipv4_subnet->AllocateAtOffset(1);
if (!guest_ipv4_addr) {
LOG(ERROR) << "ARC address already in use or unavailable";
continue;
}
MacAddress mac_addr = is_arcvm
? addr_mgr_->GenerateMacAddress(mac_addr_index++)
: addr_mgr_->GenerateMacAddress();
configs_[itype].emplace_back(std::make_unique<Device::Config>(
mac_addr, std::move(ipv4_subnet), std::move(host_ipv4_addr),
std::move(guest_ipv4_addr)));
}
}
std::vector<Device::Config*> ArcService::ReallocateAddressConfigs() {
std::vector<std::string> existing_devices;
for (const auto& d : devices_) {
existing_devices.emplace_back(d.first);
}
for (const auto& d : existing_devices) {
RemoveDevice(d);
}
AllocateAddressConfigs();
std::vector<Device::Config*> configs;
if (enable_arcvm_multinet_) {
for (const auto& kv : configs_)
for (const auto& c : kv.second)
configs.push_back(c.get());
}
for (const auto& d : existing_devices) {
AddDevice(d);
}
return configs;
}
std::unique_ptr<Device::Config> ArcService::AcquireConfig(
const std::string& ifname) {
auto itype = InterfaceTypeFor(ifname);
if (itype == InterfaceType::UNKNOWN) {
LOG(ERROR) << "Unsupported interface: " << ifname;
return nullptr;
}
auto& configs = configs_[itype];
if (configs.empty()) {
LOG(ERROR) << "No more addresses available. Cannot make device for "
<< ifname;
return nullptr;
}
std::unique_ptr<Device::Config> config;
config = std::move(configs.front());
configs.pop_front();
return config;
}
void ArcService::ReleaseConfig(const std::string& ifname,
std::unique_ptr<Device::Config> config) {
auto itype = InterfaceTypeFor(ifname);
if (itype == InterfaceType::UNKNOWN) {
LOG(ERROR) << "Unsupported interface: " << ifname;
return;
}
configs_[itype].push_front(std::move(config));
}
bool ArcService::Start(uint32_t id) {
if (impl_) {
uint32_t prev_id;
if (impl_->IsStarted(&prev_id)) {
LOG(WARNING) << "Already running - did something crash?"
<< " Stopping and restarting...";
Stop(prev_id);
}
}
auto configs = ReallocateAddressConfigs();
const auto guest = ArcGuest();
if (guest == GuestMessage::ARC_VM) {
impl_ = std::make_unique<VmImpl>(shill_client_, datapath_, addr_mgr_,
forwarder_, configs);
} else {
impl_ = std::make_unique<ContainerImpl>(datapath_, addr_mgr_, forwarder_,
guest);
}
if (!impl_->Start(id)) {
impl_.reset();
return false;
}
// Start already known Shill <-> ARC mapped devices.
for (const auto& d : devices_)
StartDevice(d.second.get());
return true;
}
void ArcService::Stop(uint32_t id) {
// Stop Shill <-> ARC mapped devices.
for (const auto& d : devices_)
StopDevice(d.second.get());
if (impl_) {
impl_->Stop(id);
impl_.reset();
}
}
void ArcService::OnDevicesChanged(const std::set<std::string>& added,
const std::set<std::string>& removed) {
for (const std::string& name : removed)
RemoveDevice(name);
for (const std::string& name : added)
AddDevice(name);
}
void ArcService::AddDevice(const std::string& ifname) {
if (ifname.empty())
return;
if (devices_.find(ifname) != devices_.end()) {
LOG(DFATAL) << "Attemping to add already tracked device: " << ifname;
return;
}
auto itype = InterfaceTypeFor(ifname);
Device::Options opts{
.fwd_multicast = IsMulticastInterface(ifname),
// TODO(crbug/726815) Also enable |ipv6_enabled| for cellular networks
// once IPv6 is enabled on cellular networks in shill.
.ipv6_enabled =
(itype == InterfaceType::ETHERNET || itype == InterfaceType::WIFI),
};
auto config = AcquireConfig(ifname);
if (!config) {
LOG(ERROR) << "Cannot add device for " << ifname;
return;
}
std::string host_ifname = base::StringPrintf("arc_%s", ifname.c_str());
auto device = std::make_unique<Device>(ifname, host_ifname, ifname,
std::move(config), opts);
StartDevice(device.get());
devices_.emplace(ifname, std::move(device));
}
void ArcService::StartDevice(Device* device) {
if (!impl_ || !impl_->IsStarted())
return;
// For now, only start devices for ARC++.
if (impl_->guest() != GuestMessage::ARC)
return;
const auto& config = device->config();
LOG(INFO) << "Adding device " << device->phys_ifname()
<< " bridge: " << device->host_ifname()
<< " guest_iface: " << device->guest_ifname();
// Create the bridge.
if (!datapath_->AddBridge(device->host_ifname(), config.host_ipv4_addr(),
30)) {
LOG(ERROR) << "Failed to setup arc bridge: " << device->host_ifname();
return;
}
// Set up iptables.
if (!datapath_->AddInboundIPv4DNAT(
device->phys_ifname(), IPv4AddressToString(config.guest_ipv4_addr())))
LOG(ERROR) << "Failed to configure ingress traffic rules for "
<< device->phys_ifname();
if (!datapath_->AddOutboundIPv4(device->host_ifname()))
LOG(ERROR) << "Failed to configure egress traffic rules for "
<< device->phys_ifname();
if (!impl_->OnStartDevice(device)) {
LOG(ERROR) << "Failed to start device " << device->phys_ifname();
}
}
void ArcService::RemoveDevice(const std::string& ifname) {
const auto it = devices_.find(ifname);
if (it == devices_.end()) {
LOG(WARNING) << "Unknown device: " << ifname;
return;
}
// If the container is down, this call does nothing.
StopDevice(it->second.get());
ReleaseConfig(ifname, it->second->release_config());
devices_.erase(it);
}
void ArcService::StopDevice(Device* device) {
if (!impl_ || !impl_->IsStarted())
return;
// For now, devices are only started for ARC++.
if (impl_->guest() != GuestMessage::ARC)
return;
impl_->OnStopDevice(device);
const auto& config = device->config();
LOG(INFO) << "Removing device " << device->phys_ifname()
<< " bridge: " << device->host_ifname()
<< " guest_iface: " << device->guest_ifname();
datapath_->RemoveOutboundIPv4(device->host_ifname());
datapath_->RemoveInboundIPv4DNAT(
device->phys_ifname(), IPv4AddressToString(config.guest_ipv4_addr()));
datapath_->RemoveBridge(device->host_ifname());
}
void ArcService::OnDefaultInterfaceChanged(const std::string& new_ifname,
const std::string& prev_ifname) {
if (impl_)
impl_->OnDefaultInterfaceChanged(new_ifname, prev_ifname);
}
std::vector<const Device::Config*> ArcService::GetDeviceConfigs() const {
if (impl_)
return impl_->GetDeviceConfigs();
return {};
}
// ARC++ specific functions.
ArcService::ContainerImpl::ContainerImpl(Datapath* datapath,
AddressManager* addr_mgr,
TrafficForwarder* forwarder,
GuestMessage::GuestType guest)
: pid_(kInvalidPID),
datapath_(datapath),
addr_mgr_(addr_mgr),
forwarder_(forwarder),
guest_(guest) {
OneTimeSetup(*datapath_);
}
GuestMessage::GuestType ArcService::ContainerImpl::guest() const {
return guest_;
}
uint32_t ArcService::ContainerImpl::id() const {
return pid_;
}
bool ArcService::ContainerImpl::Start(uint32_t pid) {
// This could happen if something crashes and the stop signal is not sent.
// It can probably be addressed by stopping and restarting the service.
if (pid_ != kInvalidPID)
return false;
if (pid == kInvalidPID) {
LOG(ERROR) << "Cannot start service - invalid container PID";
return false;
}
pid_ = pid;
Device::Options opts{
.fwd_multicast = false,
.ipv6_enabled = false,
};
auto config = MakeArcConfig(addr_mgr_, AddressManager::Guest::ARC);
// Create the bridge.
// Per crbug/1008686 this device cannot be deleted and then re-added.
// So instead of removing the bridge when the service stops, bring down the
// device instead and re-up it on restart.
if (!datapath_->AddBridge(kArcBridge, config->host_ipv4_addr(), 30) &&
!datapath_->MaskInterfaceFlags(kArcBridge, IFF_UP)) {
LOG(ERROR) << "Failed to bring up arc bridge: " << kArcBridge;
return false;
}
arc_device_ = std::make_unique<Device>(kArcIfname, kArcBridge, kArcIfname,
std::move(config), opts);
OnStartDevice(arc_device_.get());
LOG(INFO) << "ARC++ network service started {pid: " << pid_ << "}";
return true;
}
void ArcService::ContainerImpl::Stop(uint32_t /*pid*/) {
if (!IsStarted())
return;
// Per crbug/1008686 this device cannot be deleted and then re-added.
// So instead of removing the bridge, bring it down and mark it. This will
// allow us to detect if the device is re-added in case of a crash restart
// and do the right thing.
if (arc_device_) {
OnStopDevice(arc_device_.get());
if (!datapath_->MaskInterfaceFlags(kArcBridge, IFF_DEBUG, IFF_UP))
LOG(ERROR) << "Failed to bring down arc bridge "
<< "- it may not restart correctly";
}
LOG(INFO) << "ARC++ network service stopped {pid: " << pid_ << "}";
pid_ = kInvalidPID;
}
bool ArcService::ContainerImpl::IsStarted(uint32_t* pid) const {
if (pid)
*pid = pid_;
return pid_ != kInvalidPID;
}
bool ArcService::ContainerImpl::OnStartDevice(Device* device) {
LOG(INFO) << "Starting device " << device->phys_ifname()
<< " bridge: " << device->host_ifname()
<< " guest_iface: " << device->guest_ifname() << " pid: " << pid_;
// Set up the virtual pair inside the container namespace.
const std::string veth_ifname = ArcVethHostName(device->guest_ifname());
const auto& config = device->config();
if (!datapath_->ConnectVethPair(pid_, veth_ifname, device->guest_ifname(),
config.mac_addr(), config.guest_ipv4_addr(),
30, device->options().fwd_multicast)) {
LOG(ERROR) << "Cannot create virtual link for device "
<< device->phys_ifname();
return false;
}
if (!datapath_->AddToBridge(device->host_ifname(), veth_ifname)) {
datapath_->RemoveInterface(veth_ifname);
LOG(ERROR) << "Failed to bridge interface " << veth_ifname;
return false;
}
if (device != arc_device_.get()) {
forwarder_->StartForwarding(device->phys_ifname(), device->host_ifname(),
device->options().ipv6_enabled,
device->options().fwd_multicast);
} else {
// Signal the container that the network device is ready.
datapath_->runner().WriteSentinelToContainer(pid_);
}
return true;
}
void ArcService::ContainerImpl::OnStopDevice(Device* device) {
LOG(INFO) << "Stopping device " << device->phys_ifname()
<< " bridge: " << device->host_ifname()
<< " guest_iface: " << device->guest_ifname() << " pid: " << pid_;
if (device != arc_device_.get())
forwarder_->StopForwarding(device->phys_ifname(), device->host_ifname(),
device->options().ipv6_enabled,
device->options().fwd_multicast);
datapath_->RemoveInterface(ArcVethHostName(device->phys_ifname()));
}
void ArcService::ContainerImpl::OnDefaultInterfaceChanged(
const std::string& new_ifname, const std::string& prev_ifname) {}
// VM specific functions
ArcService::VmImpl::VmImpl(ShillClient* shill_client,
Datapath* datapath,
AddressManager* addr_mgr,
TrafficForwarder* forwarder,
const std::vector<Device::Config*>& configs)
: cid_(kInvalidCID),
shill_client_(shill_client),
datapath_(datapath),
addr_mgr_(addr_mgr),
forwarder_(forwarder),
configs_(configs) {}
GuestMessage::GuestType ArcService::VmImpl::guest() const {
return GuestMessage::ARC_VM;
}
uint32_t ArcService::VmImpl::id() const {
return cid_;
}
std::vector<const Device::Config*> ArcService::VmImpl::GetDeviceConfigs()
const {
std::vector<const Device::Config*> configs;
for (const auto* c : configs_)
configs.emplace_back(c);
return configs;
}
bool ArcService::VmImpl::Start(uint32_t cid) {
// This can happen if concierge crashes and doesn't send the vm down RPC.
// It can probably be addressed by stopping and restarting the service.
if (cid_ != kInvalidCID)
return false;
if (cid == kInvalidCID) {
LOG(ERROR) << "Invalid VM cid " << cid;
return false;
}
cid_ = cid;
Device::Options opts{
.fwd_multicast = true,
.ipv6_enabled = true,
};
auto arc_config = MakeArcConfig(addr_mgr_, AddressManager::Guest::VM_ARC);
configs_.insert(configs_.begin(), arc_config.get());
// Allocate TAP devices for all configs.
for (auto* config : configs_) {
auto mac = config->mac_addr();
auto tap =
datapath_->AddTAP("" /* auto-generate name */, &mac,
nullptr /* no ipv4 subnet */, vm_tools::kCrosVmUser);
if (tap.empty()) {
LOG(ERROR) << "Failed to create TAP device";
continue;
}
config->set_tap_ifname(tap);
}
arc_device_ = std::make_unique<Device>(
kArcVmIfname, kArcVmBridge, kArcVmIfname, std::move(arc_config), opts);
// Create the bridge.
if (!datapath_->AddBridge(kArcVmBridge,
arc_device_->config().host_ipv4_addr(), 30)) {
LOG(ERROR) << "Failed to setup arc bridge for device " << *arc_device_;
return false;
}
OnStartDevice(arc_device_.get());
LOG(INFO) << "ARCVM network service started {cid: " << cid_ << "}";
return true;
}
void ArcService::VmImpl::Stop(uint32_t cid) {
if (cid_ != cid) {
LOG(ERROR) << "Mismatched ARCVM CIDs " << cid_ << " != " << cid;
return;
}
for (auto* config : configs_) {
const auto& tap = config->tap_ifname();
if (!tap.empty()) {
datapath_->RemoveInterface(tap);
config->set_tap_ifname("");
}
}
OnStopDevice(arc_device_.get());
datapath_->RemoveBridge(kArcVmBridge);
arc_device_.reset();
LOG(INFO) << "ARCVM network service stopped {cid: " << cid_ << "}";
cid_ = kInvalidCID;
}
bool ArcService::VmImpl::IsStarted(uint32_t* cid) const {
if (cid)
*cid = cid_;
return cid_ != kInvalidCID;
}
bool ArcService::VmImpl::OnStartDevice(Device* device) {
// TODO(garrick): Remove once ARCVM P is gone.
if (device == arc_device_.get() && !IsMultinetEnabled())
return OnStartArcPDevice();
std::string tap;
for (auto* config : configs_) {
if (config == &device->config()) {
tap = config->tap_ifname();
break;
}
}
if (tap.empty()) {
LOG(ERROR) << "No TAP device for: " << *device;
return false;
}
LOG(INFO) << "Starting device " << *device << " cid: " << cid_;
if (!datapath_->AddToBridge(device->host_ifname(), tap)) {
LOG(ERROR) << "Failed to bridge TAP device " << tap;
datapath_->RemoveInterface(tap);
return false;
}
if (device != arc_device_.get())
forwarder_->StartForwarding(device->phys_ifname(), device->host_ifname(),
device->options().ipv6_enabled,
device->options().fwd_multicast);
return true;
}
bool ArcService::VmImpl::OnStartArcPDevice() {
LOG(INFO) << "Starting device " << *arc_device_ << " cid: " << cid_;
if (!datapath_->AddToBridge(kArcVmBridge,
arc_device_->config().tap_ifname())) {
LOG(ERROR) << "Failed to bridge TAP device " << *arc_device_;
return false;
}
// Setup the iptables.
if (!datapath_->AddLegacyIPv4DNAT(
IPv4AddressToString(arc_device_->config().guest_ipv4_addr())))
LOG(ERROR) << "Failed to configure ARC traffic rules";
if (!datapath_->AddOutboundIPv4(kArcVmBridge))
LOG(ERROR) << "Failed to configure egress traffic rules";
OnDefaultInterfaceChanged(shill_client_->default_interface(),
"" /*previous*/);
return true;
}
void ArcService::VmImpl::OnStopDevice(Device* device) {
// TODO(garrick): Remove once ARCVM P is gone.
if (device == arc_device_.get() && !IsMultinetEnabled())
return OnStopArcPDevice();
LOG(INFO) << "Stopping device " << *device << " cid: " << cid_;
if (device != arc_device_.get())
forwarder_->StopForwarding(device->phys_ifname(), device->host_ifname(),
device->options().ipv6_enabled,
device->options().fwd_multicast);
for (auto* config : configs_) {
if (config == &device->config()) {
config->set_tap_ifname("");
break;
}
}
}
void ArcService::VmImpl::OnStopArcPDevice() {
LOG(INFO) << "Stopping device " << *arc_device_.get() << " cid: " << cid_;
datapath_->RemoveOutboundIPv4(kArcVmBridge);
datapath_->RemoveLegacyIPv4DNAT();
OnDefaultInterfaceChanged("" /*new_ifname*/,
shill_client_->default_interface());
arc_device_->config().set_tap_ifname("");
}
void ArcService::VmImpl::OnDefaultInterfaceChanged(
const std::string& new_ifname, const std::string& prev_ifname) {
if (!IsStarted() || IsMultinetEnabled())
return;
forwarder_->StopForwarding(prev_ifname, kArcVmBridge, true /*ipv6*/,
true /*multicast*/);
datapath_->RemoveLegacyIPv4InboundDNAT();
// If a new default interface was given, then re-enable with that.
if (!new_ifname.empty()) {
datapath_->AddLegacyIPv4InboundDNAT(new_ifname);
forwarder_->StartForwarding(new_ifname, kArcVmBridge, true /*ipv6*/,
true /*multicast*/);
}
}
bool ArcService::VmImpl::IsMultinetEnabled() const {
return configs_.size() > 1;
}
} // namespace patchpanel