blob: 979dd84a6c59d117d4ee1398779a0ef64944ff02 [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_ctl.h"
#include <lib/async/cpp/task.h>
#include <lib/fdf/cpp/env.h>
#include <lib/fit/defer.h>
#include <lib/syslog/global.h>
#include <zircon/status.h>
#include "tun_device.h"
namespace network {
namespace tun {
zx::result<std::unique_ptr<TunCtl>> TunCtl::Create(async_dispatcher_t* fidl_dispatcher) {
std::unique_ptr<TunCtl> tun_ctl(new TunCtl(fidl_dispatcher));
zx::result dispatchers = network::OwnedDeviceInterfaceDispatchers::Create();
if (dispatchers.is_error()) {
FX_LOGF(ERROR, "tun", "failed to create owned dispatchers: %s", dispatchers.status_string());
return dispatchers.take_error();
}
tun_ctl->dispatchers_ = std::move(dispatchers.value());
zx::result shim_dispatchers = network::OwnedShimDispatchers::Create();
if (shim_dispatchers.is_error()) {
FX_LOGF(ERROR, "tun", "failed to create owned shim dispatchers: %s",
shim_dispatchers.status_string());
return shim_dispatchers.take_error();
}
tun_ctl->shim_dispatchers_ = std::move(shim_dispatchers.value());
return zx::ok(std::move(tun_ctl));
}
TunCtl::~TunCtl() {
if (dispatchers_) {
dispatchers_->ShutdownSync();
}
if (shim_dispatchers_) {
shim_dispatchers_->ShutdownSync();
}
}
void TunCtl::CreateDevice(CreateDeviceRequestView request, CreateDeviceCompleter::Sync& completer) {
zx::result tun_device = TunDevice::Create(
dispatchers_->Unowned(), shim_dispatchers_->Unowned(),
[this](TunDevice* dev) {
// If this is posted on fdf_dispatcher then there's a lockup because we're
// then creating a double lock in DevicePort.
async::PostTask(fidl_dispatcher_, [this, dev]() {
devices_.erase(*dev);
TryFireShutdownCallback();
});
},
DeviceConfig(request->config));
if (tun_device.is_error()) {
FX_LOGF(ERROR, "tun", "TunCtl: TunDevice creation failed: %s", tun_device.status_string());
request->device.Close(tun_device.error_value());
return;
}
auto& value = tun_device.value();
value->Bind(std::move(request->device));
devices_.push_back(std::move(value));
FX_LOG(INFO, "tun", "TunCtl: Created TunDevice");
}
void TunCtl::CreatePair(CreatePairRequestView request, CreatePairCompleter::Sync& completer) {
zx::result tun_pair = TunPair::Create(
dispatchers_->Unowned(), shim_dispatchers_->Unowned(), fidl_dispatcher_,
[this](TunPair* pair) {
async::PostTask(fidl_dispatcher_, [this, pair]() {
device_pairs_.erase(*pair);
TryFireShutdownCallback();
});
},
DevicePairConfig(request->config));
if (tun_pair.is_error()) {
FX_LOGF(ERROR, "tun", "TunCtl: TunPair creation failed: %s", tun_pair.status_string());
request->device_pair.Close(tun_pair.status_value());
return;
}
auto& value = tun_pair.value();
value->Bind(std::move(request->device_pair));
device_pairs_.push_back(std::move(value));
FX_LOG(INFO, "tun", "TunCtl: Created TunPair");
}
void TunCtl::SetSafeShutdownCallback(fit::callback<void()> shutdown_callback) {
async::PostTask(fidl_dispatcher_, [this, callback = std::move(shutdown_callback)]() mutable {
ZX_ASSERT_MSG(!shutdown_callback_, "Shutdown callback already installed");
shutdown_callback_ = std::move(callback);
TryFireShutdownCallback();
});
}
void TunCtl::TryFireShutdownCallback() {
if (shutdown_callback_ && device_pairs_.is_empty() && devices_.is_empty()) {
shutdown_callback_();
}
}
} // namespace tun
} // namespace network