blob: e07c83c05aae342ffab30f61162d7c449f56fb22 [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_DEVICE_ADAPTER_H_
#define SRC_CONNECTIVITY_NETWORK_TUN_NETWORK_TUN_DEVICE_ADAPTER_H_
#include <fidl/fuchsia.hardware.network/cpp/wire.h>
#include <array>
#include <queue>
#include <fbl/mutex.h>
#include "buffer.h"
#include "config.h"
#include "port_adapter.h"
#include "src/connectivity/network/drivers/network-device/device/public/network_device.h"
namespace network {
namespace tun {
class DeviceAdapter;
// An abstract DeviceAdapter parent.
//
// This abstract class allows the owner of a `DeviceAdapter` to change its behavior and be notified
// of important events.
class DeviceAdapterParent {
public:
virtual ~DeviceAdapterParent() = default;
// Gets the DeviceAdapter's configuration.
virtual const BaseDeviceConfig& config() const = 0;
// Called when transmit buffers become available.
virtual void OnTxAvail(DeviceAdapter* device) = 0;
// Called when receive buffers become available.
virtual void OnRxAvail(DeviceAdapter* device) = 0;
};
// An entity that instantiates a `NetworkDeviceInterface` and provides an implementations of
// `fuchsia.hardware.network.device.NetworkDeviceImpl` that grants access to the buffers exchanged
// with the interface.
//
// `DeviceAdapter` is used to provide the business logic of virtual NetworkDevice implementations
// both for `tun.Device` and `tun.DevicePair` device classes.
// `DeviceAdapter` maintains the buffer nomenclature used by the DeviceInterface, that is: A "Tx"
// buffer is a buffer that contains data that is expected to be sent over a link, and an "Rx" buffer
// is free space that can be used to write data received over a link and push it back to
// applications.
class DeviceAdapter : public ddk::NetworkDeviceImplProtocol<DeviceAdapter> {
public:
// Creates a new `DeviceAdapter` with `parent`, that will serve its requests through
// `dispatcher`.
static zx::result<std::unique_ptr<DeviceAdapter>> Create(
const DeviceInterfaceDispatchers& dispatchers, const ShimDispatchers& shim_dispatchers,
DeviceAdapterParent* parent);
// Binds `req` to this adapter's `NetworkDeviceInterface`.
zx_status_t Bind(fidl::ServerEnd<netdev::Device> req);
// Binds `req` to the port with `port_id` in this adapter's `NetworkDeviceInterface`.
zx_status_t BindPort(uint8_t port_id, fidl::ServerEnd<netdev::Port> req);
// Tears down this adapter and calls `callback` when teardown is finished.
// Tearing down causes all client channels to be closed.
// There are no guarantees over which thread `callback` is called.
// It is invalid to attempt to tear down a device that is already tearing down or is already torn
// down.
void Teardown(fit::function<void()> callback);
// Same as `Teardown`, but blocks until teardown is complete.
void TeardownSync();
// NetworkDeviceImpl protocol:
void NetworkDeviceImplInit(const network_device_ifc_protocol_t* iface,
network_device_impl_init_callback callback, void* cookie);
void NetworkDeviceImplStart(network_device_impl_start_callback callback, void* cookie);
void NetworkDeviceImplStop(network_device_impl_stop_callback callback, void* cookie);
void NetworkDeviceImplGetInfo(device_impl_info_t* out_info);
void NetworkDeviceImplQueueTx(const tx_buffer_t* buf_list, size_t buf_count);
void NetworkDeviceImplQueueRxSpace(const rx_space_buffer_t* buf_list, size_t buf_count);
void NetworkDeviceImplPrepareVmo(uint8_t vmo_id, zx::vmo vmo,
network_device_impl_prepare_vmo_callback callback, void* cookie);
void NetworkDeviceImplReleaseVmo(uint8_t vmo_id);
void NetworkDeviceImplSetSnoop(bool snoop) { /* do nothing , only auto-snooping is allowed */
}
// Attempts to get a pending transmit buffer containing data expected to reach the network from
// the pool of pending buffers.
// The second argument given to `callback` is the number of remaining pending buffers (not
// including the one given to it).
// Returns `true` if a buffer was successfully allocated. The buffer given to `callback` is
// discarded from the list of pending buffers and marked as pending for return.
bool TryGetTxBuffer(fit::callback<zx_status_t(TxBuffer&, size_t)> callback);
// Calls `func` with all currently enqueued tx buffers.
// If `func` returns a value different than `ZX_OK`, the buffer is returned to the device
// implementation with that error.
void RetainTxBuffers(fit::function<zx_status_t(TxBuffer&)> func);
// Attempts to write `data` and `meta` into an available rx buffer and return it to the
// `NetworkDeviceInterface`.
// Returns the number of remaining available buffers.
// Returns `ZX_ERR_BAD_STATE` if the device is offline, or `ZX_ERR_SHOULD_WAIT` if there are no
// buffers available to write `data` into
zx::result<size_t> WriteRxFrame(PortAdapter& port,
fuchsia_hardware_network::wire::FrameType frame_type,
const uint8_t* data, size_t count,
const std::optional<fuchsia_net_tun::wire::FrameMetadata>& meta);
zx::result<size_t> WriteRxFrame(PortAdapter& port,
fuchsia_hardware_network::wire::FrameType frame_type,
const fidl::VectorView<uint8_t>& data,
const std::optional<fuchsia_net_tun::wire::FrameMetadata>& meta);
zx::result<size_t> WriteRxFrame(PortAdapter& port,
fuchsia_hardware_network::wire::FrameType frame_type,
const std::vector<uint8_t>& data,
const std::optional<fuchsia_net_tun::wire::FrameMetadata>& meta);
// Copies all pending tx buffers from `this` consuming any available rx buffers from `other`.
// If `return_failed_buffers` is `true`, all buffers from `this` that couldn't be immediately
// copied into available buffers from `other` will be returned to applications in a failure state,
// otherwise buffers from `this` will remain in the available buffer pool.
void CopyTo(DeviceAdapter* other, bool return_failed_buffers);
// Handles status change notifications from port with `port_id`.
void OnPortStatusChanged(uint8_t port_id, const port_status_t& new_status);
// Adds |port| to the device.
zx_status_t AddPort(PortAdapter& port);
// Removes port with |port_id|.
void RemovePort(uint8_t port_id);
private:
static constexpr uint16_t kFifoDepth = 128;
explicit DeviceAdapter(DeviceAdapterParent* parent);
// Enqueues a single fulfilled rx frame.
void EnqueueRx(uint8_t port_id, fuchsia_hardware_network::wire::FrameType frame_type,
RxBuffer buffer, size_t length,
const std::optional<fuchsia_net_tun::wire::FrameMetadata>& meta)
__TA_REQUIRES(rx_lock_);
// Commits all pending rx buffers, returning them to the `NetworkDeviceInterface`.
void CommitRx() __TA_REQUIRES(rx_lock_);
// Enqueues a single consumed tx frame.
// `status` indicates the success or failure when consuming the frame, as dictated by the
// `NetworkDeviceInterface` contract.
void EnqueueTx(uint32_t id, zx_status_t status) __TA_REQUIRES(tx_lock_);
// Commits all pending tx frames, returning them to the `NetworkDeviceInterface`.
void CommitTx() __TA_REQUIRES(tx_lock_);
// Allocates rx buffer space with at least `length` bytes.
zx::result<RxBuffer> AllocRxSpace(size_t length) __TA_REQUIRES(rx_lock_);
void ReclaimRxSpace(RxBuffer buffer) __TA_REQUIRES(rx_lock_);
std::unique_ptr<NetworkDeviceInterface> device_;
DeviceAdapterParent* const parent_; // pointer to parent, not owned.
fbl::Mutex rx_lock_;
fbl::Mutex tx_lock_;
// NOTE: VmoStore is not thread safe in itself. Our concurrency guarantees come from the
// `NetworkDeviceInterface` contract, where VMOs are released once we've returned all the buffers.
// If we wrap it in a lock, or add thread safety to `VmoStore`, we end up with unnecessary added
// complexity and also we lose the benefit of being able to operate on rx and tx frames without
// shared locks between them.
VmoStore vmos_;
std::queue<TxBuffer> tx_buffers_ __TA_GUARDED(tx_lock_);
bool tx_available_ __TA_GUARDED(tx_lock_) = false;
std::queue<rx_space_buffer_t> rx_buffers_ __TA_GUARDED(rx_lock_);
bool rx_available_ __TA_GUARDED(rx_lock_) = false;
std::vector<rx_buffer_t> return_rx_list_ __TA_GUARDED(rx_lock_);
std::array<rx_buffer_part_t, kFifoDepth> return_rx_parts_ __TA_GUARDED(rx_lock_);
size_t return_rx_parts_count_ __TA_GUARDED(rx_lock_) = 0;
std::vector<tx_result_t> return_tx_list_ __TA_GUARDED(tx_lock_);
ddk::NetworkDeviceIfcProtocolClient device_iface_;
const device_impl_info_t device_info_;
std::array<std::atomic_bool, MAX_PORTS> port_online_status_;
};
} // namespace tun
} // namespace network
#endif // SRC_CONNECTIVITY_NETWORK_TUN_NETWORK_TUN_DEVICE_ADAPTER_H_