blob: 6e819f8230d8d0da823e645fc2c2ec447e67077a [file] [log] [blame]
// Copyright 2020 The Fuchsia 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 "tun_pair.h"
#include <lib/async-loop/default.h>
#include <lib/sync/completion.h>
#include <lib/syslog/global.h>
#include <zircon/status.h>
#include "util.h"
namespace network {
namespace tun {
TunPair::TunPair(fit::callback<void(TunPair*)> teardown, fuchsia::net::tun::DevicePairConfig config)
: teardown_callback_(std::move(teardown)),
config_(std::move(config)),
loop_(&kAsyncLoopConfigNoAttachToCurrentThread),
binding_(this) {
binding_.set_error_handler([this](zx_status_t) { Teardown(); });
}
zx_status_t TunPair::Create(fit::callback<void(TunPair*)> teardown,
fuchsia::net::tun::DevicePairConfig config,
std::unique_ptr<TunPair>* out) {
if (!TryConsolidateDevicePairConfig(&config)) {
return ZX_ERR_INVALID_ARGS;
}
std::unique_ptr<TunPair> tun(new TunPair(std::move(teardown), std::move(config)));
zx_status_t status =
DeviceAdapter::Create(tun->loop_.dispatcher(), tun.get(), false, &tun->left_);
if (status != ZX_OK) {
FX_LOGF(ERROR, "tun", "TunDevice::Init device init left failed with %s",
zx_status_get_string(status));
return status;
}
status = DeviceAdapter::Create(tun->loop_.dispatcher(), tun.get(), false, &tun->right_);
if (status != ZX_OK) {
FX_LOGF(ERROR, "tun", "TunDevice::Init device init right failed with %s",
zx_status_get_string(status));
return status;
}
if (tun->config_.has_mac_left()) {
status = MacAdapter::Create(tun.get(), tun->config_.mac_left(), true, &tun->mac_left_);
if (status != ZX_OK) {
FX_LOGF(ERROR, "tun", "TunDevice::Init mac init left failed with %s",
zx_status_get_string(status));
return status;
}
}
if (tun->config_.has_mac_right()) {
status = MacAdapter::Create(tun.get(), tun->config_.mac_right(), true, &tun->mac_right_);
if (status != ZX_OK) {
FX_LOGF(ERROR, "tun", "TunDevice::Init mac init right failed with %s",
zx_status_get_string(status));
return status;
}
}
thrd_t thread;
status = tun->loop_.StartThread("tun-device-pair", &thread);
if (status != ZX_OK) {
return status;
}
tun->loop_thread_ = thread;
*out = std::move(tun);
return ZX_OK;
}
// Helper function to perform synchronous teardown of all adapters in a TunPair.
// Can be called with std::unique_ptr to MacAdapter or DeviceAdapter.
// Used in TunPair destructor.
template <typename T>
inline void AdapterTeardown(const T& adapter, sync_completion_t* completion) {
if (adapter) {
adapter->Teardown([completion] { sync_completion_signal(completion); });
} else {
sync_completion_signal(completion);
}
}
TunPair::~TunPair() {
if (loop_thread_.has_value()) {
// not allowed to destroy a tun pair on the loop thread, will cause deadlock
ZX_ASSERT(loop_thread_.value() != thrd_current());
}
// make sure that both devices and mac adapters are torn down:
sync_completion_t left_device_teardown;
sync_completion_t right_device_teardown;
sync_completion_t left_mac_teardown;
sync_completion_t right_mac_teardown;
AdapterTeardown(left_, &left_device_teardown);
AdapterTeardown(right_, &right_device_teardown);
AdapterTeardown(mac_left_, &left_mac_teardown);
AdapterTeardown(mac_right_, &right_mac_teardown);
sync_completion_wait(&left_device_teardown, ZX_TIME_INFINITE);
sync_completion_wait(&right_device_teardown, ZX_TIME_INFINITE);
sync_completion_wait(&left_mac_teardown, ZX_TIME_INFINITE);
sync_completion_wait(&right_mac_teardown, ZX_TIME_INFINITE);
loop_.Shutdown();
FX_VLOG(1, "tun", "TunDevice destroyed");
}
void TunPair::Teardown() {
if (teardown_callback_) {
teardown_callback_(this);
}
}
void TunPair::Bind(fidl::InterfaceRequest<fuchsia::net::tun::DevicePair> req) {
binding_.Bind(std::move(req), loop_.dispatcher());
}
void TunPair::ConnectProtocols(fuchsia::net::tun::DevicePairEnds requests) {
if (requests.has_left()) {
ConnectProtocols(left_, mac_left_, std::move(*requests.mutable_left()));
}
if (requests.has_right()) {
ConnectProtocols(right_, mac_right_, std::move(*requests.mutable_right()));
}
}
void TunPair::ConnectProtocols(const std::unique_ptr<DeviceAdapter>& device,
const std::unique_ptr<MacAdapter>& mac,
fuchsia::net::tun::Protocols protos) {
if (device && protos.has_network_device()) {
device->Bind(protos.mutable_network_device()->TakeChannel());
}
if (mac && protos.has_mac_addressing()) {
mac->Bind(loop_.dispatcher(), protos.mutable_mac_addressing()->TakeChannel());
}
}
void TunPair::OnHasSessionsChanged(DeviceAdapter* device) {
fbl::AutoLock lock(&power_lock_);
bool online = left_->HasSession() && right_->HasSession();
left_->SetOnline(online);
right_->SetOnline(online);
}
void TunPair::OnTxAvail(DeviceAdapter* device) {
DeviceAdapter* target;
bool fallible;
// Device is the transmitter, fallible is read for the end that matches device.
if (device == left_.get()) {
target = right_.get();
fallible = config_.fallible_transmit_left();
} else {
target = left_.get();
fallible = config_.fallible_transmit_right();
}
device->CopyTo(target, fallible);
}
void TunPair::OnRxAvail(DeviceAdapter* device) {
DeviceAdapter* source;
bool fallible;
// Device is the receiver, fallible is read for the source end.
if (device == left_.get()) {
source = right_.get();
fallible = config_.fallible_transmit_right();
} else {
source = left_.get();
fallible = config_.fallible_transmit_left();
}
source->CopyTo(device, fallible);
}
void TunPair::OnMacStateChanged(MacAdapter* adapter) {
// Do nothing, TunPair doesn't care about mac state.
}
} // namespace tun
} // namespace network