| // 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 <fuchsia/hardware/network/cpp/fidl.h> |
| #include <lib/fidl-async/cpp/bind.h> |
| #include <lib/fzl/vmo-mapper.h> |
| |
| #include <array> |
| #include <queue> |
| |
| #include <fbl/auto_lock.h> |
| #include <fbl/mutex.h> |
| |
| #include "buffer.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: |
| // Gets the DeviceAdapter's configuration. |
| virtual const fuchsia::net::tun::BaseConfig& config() const = 0; |
| // Called when the device's `has_session` state changes. |
| virtual void OnHasSessionsChanged(DeviceAdapter* device) = 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`. |
| // If `online` is true, the device starts with its virtual link in the online status. |
| // On success, the adapter is stored in `out`. |
| static zx_status_t Create(async_dispatcher_t* dispatcher, DeviceAdapterParent* parent, |
| bool online, std::unique_ptr<DeviceAdapter>* out); |
| |
| // Binds `req` to this adapter's `NetworkDeviceInterface`. |
| zx_status_t Bind(zx::channel 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: |
| zx_status_t NetworkDeviceImplInit(const network_device_ifc_protocol_t* iface); |
| void NetworkDeviceImplStart(network_device_impl_start_callback callback, void* cookie); |
| void NetworkDeviceImplStop(network_device_impl_stop_callback callback, void* cookie); |
| void NetworkDeviceImplGetInfo(device_info_t* out_info); |
| void NetworkDeviceImplGetStatus(status_t* out_status); |
| 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); |
| void NetworkDeviceImplReleaseVmo(uint8_t vmo_id); |
| |
| void NetworkDeviceImplSetSnoop(bool snoop) { /* do nothing , only auto-snooping is allowed */ |
| } |
| |
| // Sets this device's emulated `online` status. |
| void SetOnline(bool online); |
| // Returns `true` if the device has at least one active session. |
| bool HasSession(); |
| // 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<void(Buffer*, size_t)> callback); |
| // Attempts to write `data` and `meta` into an available rx buffer and return it to the |
| // `NetworkDeviceInterface`. |
| // The number of remaining available buffers is stored in `out_avail`. |
| // 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_status_t WriteRxFrame(fuchsia::hardware::network::FrameType frame_type, |
| const std::vector<uint8_t>& data, |
| const fuchsia::net::tun::FrameMetadata* meta, size_t* out_avail); |
| // 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); |
| |
| private: |
| DeviceAdapter(DeviceAdapterParent* parent, bool online); |
| |
| // Enqueues a single fulfilled rx frame. |
| void EnqueueRx(fuchsia::hardware::network::FrameType frame_type, uint32_t buffer_id, |
| uint32_t total_len, const fuchsia::net::tun::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_); |
| |
| std::unique_ptr<NetworkDeviceInterface> device_; |
| DeviceAdapterParent* const parent_; // pointer to parent, not owned. |
| fbl::Mutex state_lock_; |
| bool has_sessions_ __TA_GUARDED(state_lock_) = false; |
| bool online_ __TA_GUARDED(state_lock_) = false; |
| |
| 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::array<uint8_t, fuchsia::hardware::network::MAX_FRAME_TYPES> rx_types_{}; |
| std::array<tx_support_t, fuchsia::hardware::network::MAX_FRAME_TYPES> tx_types_{}; |
| std::queue<Buffer> tx_buffers_ __TA_GUARDED(tx_lock_); |
| std::queue<Buffer> rx_buffers_ __TA_GUARDED(rx_lock_); |
| std::vector<rx_buffer_t> return_rx_list_ __TA_GUARDED(rx_lock_); |
| std::vector<tx_result_t> return_tx_list_ __TA_GUARDED(tx_lock_); |
| ddk::NetworkDeviceIfcProtocolClient device_iface_; |
| const device_info_t device_info_; |
| }; |
| } // namespace tun |
| } // namespace network |
| |
| #endif // SRC_CONNECTIVITY_NETWORK_TUN_NETWORK_TUN_DEVICE_ADAPTER_H_ |