blob: 1b47aafa1645e3f61099c76f70360fc542e91df6 [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 "arc/network/device.h"
#include <sys/types.h>
#include <map>
#include <base/bind.h>
#include <base/logging.h>
#include <base/strings/stringprintf.h>
#include "arc/network/arc_ip_config.h"
namespace arc_networkd {
const char kAndroidDevice[] = "android";
namespace {
const char kIPv4AddrFmt[] = "100.115.92.%d";
const char kMacAddrFmt[] = "00:FF:AA:00:00:%x";
const int kMacAddrBase = 85;
const char kMdnsMcastAddress[] = "224.0.0.251";
const uint16_t kMdnsPort = 5353;
const char kSsdpMcastAddress[] = "239.255.255.250";
const uint16_t kSsdpPort = 1900;
const int kMaxRandomAddressTries = 3;
bool AssignAddr(const std::string& ifname, DeviceConfig* config) {
// To make this easier for now, hardcode the interfaces to support;
// maps device names to the subnet base address (used in kIPv4AddrFmt).
// Note that the .4/30 subnet is skipped since it is in use elsewhere.
// TODO(garrick): Generalize and support arbitrary interfaces.
static const std::map<std::string, int> cur_ifaces = {{kAndroidDevice, 0},
{"eth0", 8},
{"wlan0", 12},
{"wwan0", 16},
{"rmnet0", 20}};
const auto it = cur_ifaces.find(ifname);
if (it == cur_ifaces.end())
return false;
const int addr_off = it->second;
config->set_br_ipv4(base::StringPrintf(kIPv4AddrFmt, addr_off + 1));
config->set_arc_ipv4(base::StringPrintf(kIPv4AddrFmt, addr_off + 2));
config->set_mac_addr(
base::StringPrintf(kMacAddrFmt, kMacAddrBase + addr_off));
return true;
}
} // namespace
Device::Device(const std::string& ifname,
const DeviceConfig& config,
const MessageSink& msg_sink)
: ifname_(ifname), config_(config), msg_sink_(msg_sink) {
if (msg_sink_.is_null())
return;
IpHelperMessage msg;
msg.set_dev_ifname(ifname_);
*msg.mutable_dev_config() = config_;
msg_sink_.Run(msg);
}
Device::~Device() {
if (msg_sink_.is_null())
return;
IpHelperMessage msg;
msg.set_dev_ifname(ifname_);
msg.set_teardown(true);
msg_sink_.Run(msg);
}
// static
std::unique_ptr<Device> Device::ForInterface(const std::string& ifname,
const MessageSink& msg_sink) {
DeviceConfig config;
if (!AssignAddr(ifname, &config))
return nullptr;
if (ifname == kAndroidDevice) {
config.set_br_ifname("arcbr0");
config.set_arc_ifname("arc0");
config.set_find_ipv6_routes(true);
// TODO(garrick): Enable only for Ethernet and Wifi.
config.set_fwd_multicast(true);
} else {
config.set_br_ifname(base::StringPrintf("arc_%s", ifname.c_str()));
config.set_arc_ifname(ifname);
config.set_find_ipv6_routes(false);
// TODO(garrick): Enable only for Ethernet and Wifi.
config.set_fwd_multicast(false);
}
return std::make_unique<Device>(ifname, config, msg_sink);
}
void Device::Enable(const std::string& ifname) {
LOG(INFO) << "Enabling device " << ifname_;
if (!ifname.empty()) {
CHECK_EQ(ifname_, kAndroidDevice);
LOG(INFO) << "Binding interface " << ifname << " to device " << ifname_;
legacy_lan_ifname_ = ifname;
} else {
legacy_lan_ifname_ = ifname_;
}
LOG(INFO) << "Enabling services for " << ifname_;
// Enable inbound traffic.
if (!msg_sink_.is_null()) {
IpHelperMessage msg;
msg.set_dev_ifname(ifname_);
msg.set_enable_inbound_ifname(legacy_lan_ifname_);
msg_sink_.Run(msg);
}
// TODO(garrick): Revisit multicast forwarding when NAT rules are enabled
// for other devices.
if (config_.fwd_multicast()) {
mdns_forwarder_.reset(new MulticastForwarder());
ssdp_forwarder_.reset(new MulticastForwarder());
mdns_forwarder_->Start(config_.br_ifname(), legacy_lan_ifname_,
config_.arc_ipv4(), kMdnsMcastAddress, kMdnsPort,
/* allow_stateless */ true);
ssdp_forwarder_->Start(config_.br_ifname(), legacy_lan_ifname_,
/* arc_ipaddr */ "", kSsdpMcastAddress, kSsdpPort,
/* allow_stateless */ false);
}
if (config_.find_ipv6_routes()) {
router_finder_.reset(new RouterFinder());
router_finder_->Start(
legacy_lan_ifname_,
base::Bind(&Device::OnRouteFound, weak_factory_.GetWeakPtr()));
}
}
void Device::Disable() {
LOG(INFO) << "Disabling services for " << ifname_;
neighbor_finder_.reset();
router_finder_.reset();
ssdp_forwarder_.reset();
mdns_forwarder_.reset();
legacy_lan_ifname_.clear();
if (msg_sink_.is_null())
return;
// Clear IPv6 info, if necessary.
if (config_.find_ipv6_routes()) {
IpHelperMessage msg;
msg.set_dev_ifname(ifname_);
msg.set_clear_arc_ip(true);
msg_sink_.Run(msg);
}
// Disable inbound traffic.
{
IpHelperMessage msg;
msg.set_dev_ifname(ifname_);
msg.set_disable_inbound(true);
msg_sink_.Run(msg);
}
}
void Device::OnRouteFound(const struct in6_addr& prefix,
int prefix_len,
const struct in6_addr& router) {
if (prefix_len == 64) {
LOG(INFO) << "Found IPv6 network on iface " << legacy_lan_ifname_
<< " route=" << prefix << "/" << prefix_len
<< ", gateway=" << router;
memcpy(&random_address_, &prefix, sizeof(random_address_));
random_address_prefix_len_ = prefix_len;
random_address_tries_ = 0;
ArcIpConfig::GenerateRandom(&random_address_, random_address_prefix_len_);
neighbor_finder_.reset(new NeighborFinder());
neighbor_finder_->Check(
legacy_lan_ifname_, random_address_,
base::Bind(&Device::OnNeighborCheckResult, weak_factory_.GetWeakPtr()));
} else {
LOG(INFO) << "No IPv6 connectivity available on " << legacy_lan_ifname_;
}
}
void Device::OnNeighborCheckResult(bool found) {
if (found) {
if (++random_address_tries_ >= kMaxRandomAddressTries) {
LOG(WARNING) << "Too many IP collisions, giving up.";
return;
}
struct in6_addr previous_address = random_address_;
ArcIpConfig::GenerateRandom(&random_address_, random_address_prefix_len_);
LOG(INFO) << "Detected IP collision for " << previous_address
<< ", retrying with new address " << random_address_;
neighbor_finder_->Check(
legacy_lan_ifname_, random_address_,
base::Bind(&Device::OnNeighborCheckResult, weak_factory_.GetWeakPtr()));
} else {
struct in6_addr router;
if (!ArcIpConfig::GetV6Address(config_.br_ifname(), &router)) {
LOG(ERROR) << "Error reading link local address for "
<< config_.br_ifname();
return;
}
LOG(INFO) << "Setting IPv6 address " << random_address_
<< "/128, gateway=" << router << " on " << legacy_lan_ifname_;
// Set up new ARC IPv6 address, NDP, and forwarding rules.
if (!msg_sink_.is_null()) {
IpHelperMessage msg;
msg.set_dev_ifname(ifname_);
SetArcIp* setup_msg = msg.mutable_set_arc_ip();
setup_msg->set_prefix(&random_address_, sizeof(struct in6_addr));
setup_msg->set_prefix_len(128);
setup_msg->set_router(&router, sizeof(struct in6_addr));
setup_msg->set_lan_ifname(legacy_lan_ifname_);
msg_sink_.Run(msg);
}
}
}
} // namespace arc_networkd