blob: 06d2c9b9025d467a08e2ac18f0cbc5271a1289e4 [file] [log] [blame]
// Copyright 2016 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-networkd/manager.h"
#include <arpa/inet.h>
#include <stdint.h>
#include <utility>
#include <base/bind.h>
#include <base/logging.h>
#include <base/message_loop/message_loop.h>
#include <brillo/minijail/minijail.h>
#include "arc-networkd/ipc.pb.h"
namespace {
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;
const char kUnprivilegedUser[] = "arc-networkd";
const uint64_t kManagerCapMask = CAP_TO_MASK(CAP_NET_RAW);
} // namespace
namespace arc_networkd {
Manager::Manager(const Options& opt, std::unique_ptr<HelperProcess> ip_helper)
: int_ifname_(opt.int_ifname),
mdns_ipaddr_(opt.mdns_ipaddr),
con_ifname_(opt.con_ifname) {
ip_helper_ = std::move(ip_helper);
}
int Manager::OnInit() {
// Run with minimal privileges.
brillo::Minijail* m = brillo::Minijail::GetInstance();
struct minijail* jail = m->New();
// Most of these return void, but DropRoot() can fail if the user/group
// does not exist.
CHECK(m->DropRoot(jail, kUnprivilegedUser, kUnprivilegedUser));
m->UseCapabilities(jail, kManagerCapMask);
m->Enter(jail);
m->Destroy(jail);
// Handle subprocess lifecycle.
process_reaper_.Register(this);
process_reaper_.WatchForChild(FROM_HERE,
ip_helper_->pid(),
base::Bind(&Manager::OnSubprocessExited,
weak_factory_.GetWeakPtr(),
ip_helper_->pid()));
// This needs to execute after DBusDaemon::OnInit() creates bus_.
base::MessageLoopForIO::current()->PostTask(
FROM_HERE,
base::Bind(&Manager::InitialSetup, weak_factory_.GetWeakPtr()));
return DBusDaemon::OnInit();
}
void Manager::InitialSetup() {
shill_client_.reset(new ShillClient(std::move(bus_)));
shill_client_->RegisterDefaultInterfaceChangedHandler(
base::Bind(&Manager::OnDefaultInterfaceChanged,
weak_factory_.GetWeakPtr()));
}
void Manager::OnDefaultInterfaceChanged(const std::string& ifname) {
ClearArcIp();
neighbor_finder_.reset();
lan_ifname_ = ifname;
if (ifname.empty()) {
LOG(INFO) << "Unbinding services";
mdns_forwarder_.reset();
ssdp_forwarder_.reset();
router_finder_.reset();
DisableInbound();
} else {
LOG(INFO) << "Binding to interface " << ifname;
mdns_forwarder_.reset(new MulticastForwarder());
ssdp_forwarder_.reset(new MulticastForwarder());
router_finder_.reset(new RouterFinder());
mdns_forwarder_->Start(int_ifname_,
ifname,
mdns_ipaddr_,
kMdnsMcastAddress,
kMdnsPort,
/* allow_stateless */ true);
ssdp_forwarder_->Start(int_ifname_,
ifname,
/* mdns_ipaddr */ "",
kSsdpMcastAddress,
kSsdpPort,
/* allow_stateless */ false);
router_finder_->Start(ifname,
base::Bind(&Manager::OnRouteFound, weak_factory_.GetWeakPtr()));
EnableInbound(ifname);
}
}
void Manager::OnRouteFound(const struct in6_addr& prefix,
int prefix_len,
const struct in6_addr& router) {
if (prefix_len == 64) {
char buf[64];
LOG(INFO) << "Found IPv6 network "
<< inet_ntop(AF_INET6, &prefix, buf, sizeof(buf))
<< "/" << prefix_len
<< " route "
<< inet_ntop(AF_INET6, &router, buf, sizeof(buf));
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(lan_ifname_, random_address_,
base::Bind(&Manager::OnNeighborCheckResult,
weak_factory_.GetWeakPtr()));
} else {
LOG(INFO) << "No IPv6 connectivity available";
}
}
void Manager::OnNeighborCheckResult(bool found) {
if (found) {
if (++random_address_tries_ >= kMaxRandomAddressTries) {
LOG(WARNING) << "Too many IP collisions, giving up.";
return;
}
LOG(INFO) << "Detected IP collision, retrying with a new address";
ArcIpConfig::GenerateRandom(&random_address_,
random_address_prefix_len_);
neighbor_finder_->Check(lan_ifname_, random_address_,
base::Bind(&Manager::OnNeighborCheckResult,
weak_factory_.GetWeakPtr()));
} else {
struct in6_addr router;
if (!ArcIpConfig::GetV6Address(int_ifname_, &router)) {
LOG(ERROR) << "Error reading link local address for "
<< int_ifname_;
return;
}
char buf[64];
LOG(INFO) << "Setting IPv6 address "
<< inet_ntop(AF_INET6, &random_address_, buf, sizeof(buf))
<< "/128 route "
<< inet_ntop(AF_INET6, &router, buf, sizeof(buf));
// Set up new ARC IPv6 address, NDP, and forwarding rules.
IpHelperMessage outer_msg;
SetArcIp* inner_msg = outer_msg.mutable_set_arc_ip();
inner_msg->set_prefix(&random_address_, sizeof(struct in6_addr));
inner_msg->set_prefix_len(128);
inner_msg->set_router(&router, sizeof(struct in6_addr));
inner_msg->set_lan_ifname(lan_ifname_);
ip_helper_->SendMessage(outer_msg);
}
}
void Manager::ClearArcIp() {
IpHelperMessage msg;
msg.set_clear_arc_ip(true);
ip_helper_->SendMessage(msg);
}
void Manager::EnableInbound(const std::string& lan_ifname) {
IpHelperMessage msg;
msg.set_enable_inbound(lan_ifname);
ip_helper_->SendMessage(msg);
}
void Manager::DisableInbound() {
IpHelperMessage msg;
msg.set_disable_inbound(true);
ip_helper_->SendMessage(msg);
}
void Manager::OnShutdown(int* exit_code) {
ClearArcIp();
DisableInbound();
}
void Manager::OnSubprocessExited(pid_t pid, const siginfo_t& info) {
LOG(FATAL) << "Subprocess " << pid << " exited unexpectedly";
}
} // namespace arc_networkd