| // 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_BUFFER_H_ |
| #define SRC_CONNECTIVITY_NETWORK_TUN_NETWORK_TUN_BUFFER_H_ |
| |
| #include <fuchsia/hardware/network/device/cpp/banjo.h> |
| #include <fuchsia/net/tun/cpp/fidl.h> |
| #include <lib/fzl/vmo-mapper.h> |
| |
| #include <array> |
| |
| #include <fbl/span.h> |
| |
| #include "src/lib/vmo_store/vmo_store.h" |
| |
| namespace network { |
| namespace tun { |
| |
| class Buffer; |
| |
| // A data structure that stores keyed VMOs and allocates buffers. |
| // |
| // `VmoStore` stores up to `MAX_VMOS` VMOs keyed by an identifier bound to the range [0, |
| // `MAX_VMOS`). `VmoStore` can be used to allocate buffers backed by the VMOs it contains. |
| // |
| // This class is used to fulfill the VMO registration mechanism used by |
| // `fuchsia.hardware.network.device`. |
| class VmoStore { |
| public: |
| VmoStore() |
| : store_(vmo_store::Options{ |
| vmo_store::MapOptions{ZX_VM_PERM_READ | ZX_VM_PERM_WRITE | ZX_VM_REQUIRE_NON_RESIZABLE, |
| nullptr}, |
| fit::nullopt}) {} |
| |
| ~VmoStore() = default; |
| |
| // Reads `len` bytes at `offset` from the VMO identified by `id` into `data`, which must be a |
| // `uint8_t` iterator. |
| // Returns an error if the specified region is invalid or `id` is not registered. |
| template <class T> |
| zx_status_t Read(uint8_t id, size_t offset, size_t len, T data) { |
| fbl::Span<uint8_t> vmo_data; |
| zx_status_t status = GetMappedVmo(id, &vmo_data); |
| if (status != ZX_OK) { |
| return status; |
| } |
| if (offset + len > vmo_data.size()) { |
| return ZX_ERR_OUT_OF_RANGE; |
| } |
| std::copy_n(vmo_data.begin() + offset, len, data); |
| return ZX_OK; |
| } |
| |
| // Writes `len` bytes at `offset` into the VMO identified by `id` from `data`, which must be an |
| // `uint8_t` iterator. |
| // Returns an error if the specified region is invalid or `id` is not registered. |
| template <class T> |
| zx_status_t Write(uint8_t id, size_t offset, size_t len, T data) { |
| fbl::Span<uint8_t> vmo_data; |
| zx_status_t status = GetMappedVmo(id, &vmo_data); |
| if (status != ZX_OK) { |
| return status; |
| } |
| if (offset + len > vmo_data.size()) { |
| return ZX_ERR_OUT_OF_RANGE; |
| } |
| std::copy_n(data, len, vmo_data.begin() + offset); |
| return ZX_OK; |
| } |
| |
| // Registers and maps `vmo` identified by `id`. |
| // `id` comes from a `NetworkDeviceInterface` and is part of the NetworkDevice contract. |
| // Returns an error if the identifier is invalid or already in use, or the mapping fails. |
| zx_status_t RegisterVmo(uint8_t id, zx::vmo vmo); |
| // Unregister a previously registered VMO with `id`, unmapping it from memory and releasing the |
| // VMO handle. |
| // Returns an error if the identifier is invalid or does not map to a registered VMO. |
| zx_status_t UnregisterVmo(uint8_t id); |
| |
| // Copies `len` bytes from `src_store`'s VMO with `src_id` at `src_offset` to `dst_store`'s VMO |
| // with `dst_id` at `dst_offset`. |
| // |
| // Equivalent to: |
| // T data; |
| // src_store.Read(src_id, src_offset, len, back_inserter(data)); |
| // dst_store.Write(dst_id, dst_offset, len, data.begin()); |
| static zx_status_t Copy(VmoStore* src_store, uint8_t src_id, size_t src_offset, |
| VmoStore* dst_store, uint8_t dst_id, size_t dst_offset, size_t len); |
| |
| Buffer MakeTxBuffer(const tx_buffer_t* tx, bool get_meta); |
| Buffer MakeRxSpaceBuffer(const rx_space_buffer_t* space); |
| |
| private: |
| zx_status_t GetMappedVmo(uint8_t id, fbl::Span<uint8_t>* out_span); |
| vmo_store::VmoStore<vmo_store::SlabStorage<uint8_t>> store_; |
| }; |
| |
| // A device buffer. |
| // Device buffers can be created from VMO stores. They're used to store references to buffers |
| // retrieved from a NetworkDeviceInterface, which point to data regions within a VMO. |
| // `Buffer` can represent either a tx (application-filled data) buffer or an rx (empty space for |
| // inbound data) buffer. |
| class Buffer { |
| public: |
| // Reads this buffer's data into `vec`. |
| // Used to serve `fuchsia.net.tun/Device.ReadFrame`. |
| // Returns an error if this buffer's definition does not map to valid data (see `VmoStore::Write` |
| // for specific error codes). |
| zx_status_t Read(std::vector<uint8_t>* vec); |
| // Writes `data` into this buffer. |
| // If this `data` does not fit in this buffer, `ZX_ERR_OUT_OF_RANGE` is returned. |
| // Returns an error if this buffer's definition does not map to valid data (see `VmoStore::Write` |
| // for specific error codes). |
| // Used to serve `fuchsia.net.tun/Device.WriteFrame`. |
| zx_status_t Write(const std::vector<uint8_t>& data); |
| // Copies data from `other` into this buffer, returning the number of bytes written in `total`. |
| zx_status_t CopyFrom(Buffer* other, size_t* total); |
| |
| inline fuchsia::hardware::network::FrameType frame_type() const { return frame_type_.value(); } |
| |
| inline uint32_t id() const { return id_; } |
| |
| inline std::unique_ptr<fuchsia::net::tun::FrameMetadata> TakeMetadata() { |
| return std::move(meta_); |
| } |
| |
| protected: |
| friend VmoStore; |
| // Creates a device buffer from a tx request buffer. |
| Buffer(const tx_buffer_t* tx, bool get_meta, VmoStore* vmo_store); |
| // Creates a device buffer from an rx space buffer. |
| Buffer(const rx_space_buffer_t* space, VmoStore* vmo_store); |
| |
| private: |
| const uint32_t id_{}; |
| // Pointer to parent VMO store, not owned. |
| VmoStore* const vmo_store_; |
| const uint8_t vmo_id_; |
| std::array<buffer_region_t, MAX_BUFFER_PARTS> parts_{}; |
| const size_t parts_count_{}; |
| std::unique_ptr<fuchsia::net::tun::FrameMetadata> meta_; |
| const fit::optional<fuchsia::hardware::network::FrameType> frame_type_; |
| }; |
| |
| } // namespace tun |
| } // namespace network |
| |
| #endif // SRC_CONNECTIVITY_NETWORK_TUN_NETWORK_TUN_BUFFER_H_ |