blob: 9b19c23e4442eef2423df7e6fc64a350a14b2f0c [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.
#ifndef SRC_CONNECTIVITY_NETWORK_TUN_NETWORK_TUN_TUN_DEVICE_H_
#define SRC_CONNECTIVITY_NETWORK_TUN_NETWORK_TUN_TUN_DEVICE_H_
#include <fidl/fuchsia.net.tun/cpp/wire.h>
#include <lib/async-loop/cpp/loop.h>
#include <lib/fdf/cpp/dispatcher.h>
#include <optional>
#include <queue>
#include <fbl/intrusive_double_list.h>
#include <fbl/mutex.h>
#include "device_adapter.h"
#include "mac_adapter.h"
#include "state.h"
namespace network {
namespace tun {
// Forward declaration for test-only friend class used below.
namespace testing {
class TunTest;
}
// Implements `fuchsia.net.tun.Device`.
//
// `TunDevice` uses `DeviceAdapter` to fulfill the `fuchsia.net.tun.Device` protocol. All FIDL
// requests are served over its own internally held async dispatcher.
class TunDevice : public fbl::DoublyLinkedListable<std::unique_ptr<TunDevice>>,
public fidl::WireServer<fuchsia_net_tun::Device>,
public DeviceAdapterParent {
public:
static constexpr size_t kMaxPendingOps = fuchsia_net_tun::wire::kMaxPendingOperations;
// Creates a new `TunDevice` with `config`.
// `teardown` is called when all the bound client channels are closed.
static zx::result<std::unique_ptr<TunDevice>> Create(
const DeviceInterfaceDispatchers& dispatchers, const ShimDispatchers& shim_dispatchers,
fit::callback<void(TunDevice*)> teardown, DeviceConfig&& config);
~TunDevice() override;
// fuchsia.net.tun.Device implementation:
void WriteFrame(WriteFrameRequestView request, WriteFrameCompleter::Sync& completer) override;
void ReadFrame(ReadFrameCompleter::Sync& completer) override;
void GetSignals(GetSignalsCompleter::Sync& completer) override;
void AddPort(AddPortRequestView request, AddPortCompleter::Sync& _completer) override;
void GetDevice(GetDeviceRequestView request, GetDeviceCompleter::Sync& _completer) override;
// DeviceAdapterParent implementation:
const BaseDeviceConfig& config() const override { return config_; }
void OnTxAvail(DeviceAdapter* device) override;
void OnRxAvail(DeviceAdapter* device) override;
// Binds `req` to this device.
// Requests are served over this device's owned loop.
// NOTE: at this moment only one binding is supported, if the device is already bound the previous
// channel is closed.
void Bind(fidl::ServerEnd<fuchsia_net_tun::Device> req);
protected:
friend testing::TunTest;
const std::unique_ptr<DeviceAdapter>& adapter() const { return device_; }
private:
class Port : public PortAdapterParent, public fidl::WireServer<fuchsia_net_tun::Port> {
public:
Port(Port&&) = delete;
~Port() override;
static zx::result<std::unique_ptr<Port>> Create(TunDevice* parent,
const DevicePortConfig& config);
// MacAdapterParent implementation:
void OnMacStateChanged(MacAdapter* adapter) override;
// PortAdapterParent implementation:
void OnHasSessionsChanged(PortAdapter& port) override;
void OnPortStatusChanged(PortAdapter& port, const port_status_t& new_status) override;
void OnPortDestroyed(PortAdapter& port) override;
// FIDL port implementation:
void GetState(GetStateCompleter::Sync& completer) override;
void WatchState(WatchStateCompleter::Sync& completer) override;
void SetOnline(SetOnlineRequestView request, SetOnlineCompleter::Sync& completer) override;
void GetPort(GetPortRequestView request, GetPortCompleter::Sync& _completer) override;
void Remove(RemoveCompleter::Sync& _completer) override;
void SetOnline(bool online);
PortAdapter& adapter() { return *adapter_; }
void Bind(fidl::ServerEnd<fuchsia_net_tun::Port> req);
private:
explicit Port(TunDevice* parent) : parent_(parent) {}
// Fulfills pending WatchState requests.
void RunStateChange();
// Posts |RunStateChange| to run later on parent dispatcher.
void PostRunStateChange();
InternalState State() const;
TunDevice* const parent_;
std::unique_ptr<PortAdapter> adapter_;
std::optional<WatchStateCompleter::Async> pending_watch_state_;
std::optional<InternalState> last_state_;
std::optional<fidl::ServerBindingRef<fuchsia_net_tun::Port>> binding_;
std::optional<libsync::Completion> port_destroyed_;
};
TunDevice(fdf::Dispatcher* dispatcher, fit::callback<void(TunDevice*)> teardown,
DeviceConfig&& config);
// Completes a single WriteFrame request. Returns true iff a reply was sent on the callback.
template <typename F, typename C>
bool WriteWith(F fn, C& callback);
// Fulfills pending WriteFrame requests. Returns true iff all pending requests were processed.
bool RunWriteFrame();
// Fulfills pending ReadFrame requests.
void RunReadFrame();
// Calls the teardown callback.
void Teardown();
bool IsBlocking() const { return config_.blocking; }
fit::callback<void(TunDevice*)> teardown_callback_;
const DeviceConfig config_;
async::Loop loop_;
std::optional<thrd_t> loop_thread_;
fdf::Dispatcher* dispatcher_;
std::optional<fidl::ServerBindingRef<fuchsia_net_tun::Device>> binding_;
std::unique_ptr<DeviceAdapter> device_;
std::array<std::unique_ptr<Port>, MAX_PORTS> ports_;
// Helper struct to store pending write requests.
struct PendingWriteRequest {
// Owned fields of fuchsia_net_tun::wire::Frame.
uint8_t port_id;
fuchsia_hardware_network::wire::FrameType frame_type;
std::vector<uint8_t> data;
// FrameMetadata::info is a fidl::VectorView, so we should really be copying it. At the time of
// writing, it isn't used.
std::optional<fuchsia_net_tun::wire::FrameMetadata> meta;
// The FIDL transaction completer.
WriteFrameCompleter::Async completer;
PendingWriteRequest(const fuchsia_net_tun::wire::Frame& frame,
WriteFrameCompleter::Async completer)
: completer(std::move(completer)) {
port_id = frame.port();
frame_type = frame.frame_type();
std::copy(std::begin(frame.data()), std::end(frame.data()), std::back_inserter(data));
if (frame.has_meta()) {
meta = frame.meta();
}
}
};
std::queue<ReadFrameCompleter::Async> pending_read_frame_;
std::queue<PendingWriteRequest> pending_write_frame_;
zx::eventpair signals_self_;
zx::eventpair signals_peer_;
};
} // namespace tun
} // namespace network
#endif // SRC_CONNECTIVITY_NETWORK_TUN_NETWORK_TUN_TUN_DEVICE_H_