blob: 5a3339ca713dba75be65639923433e1bd4c6d409 [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_BUFFER_H_
#define SRC_CONNECTIVITY_NETWORK_TUN_NETWORK_TUN_BUFFER_H_
#include <fidl/fuchsia.net.tun/cpp/wire.h>
#include <fuchsia/hardware/network/driver/cpp/banjo.h>
#include <lib/stdcompat/span.h>
#include <array>
#include "src/lib/vmo_store/vmo_store.h"
namespace network {
namespace tun {
class TxBuffer;
class RxBuffer;
// 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.driver`.
class VmoStore {
public:
VmoStore()
: store_(vmo_store::Options{
.map =
vmo_store::MapOptions{
.vm_option = ZX_VM_PERM_READ | ZX_VM_PERM_WRITE | ZX_VM_REQUIRE_NON_RESIZABLE,
},
}) {}
~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) {
zx::result vmo_data = GetMappedVmo(id);
if (vmo_data.is_error()) {
return vmo_data.status_value();
}
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) {
zx::result vmo_data = GetMappedVmo(id);
if (vmo_data.is_error()) {
return vmo_data.status_value();
}
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);
TxBuffer MakeTxBuffer(const tx_buffer_t& tx, bool get_meta);
RxBuffer MakeRxSpaceBuffer(const rx_space_buffer_t& space);
RxBuffer MakeEmptyRxBuffer();
private:
zx::result<cpp20::span<uint8_t>> GetMappedVmo(uint8_t id);
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:
Buffer(Buffer&&) = default;
Buffer(const Buffer&) = delete;
// 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 uint8_t* data, size_t count);
zx_status_t Write(const fidl::VectorView<uint8_t>& data);
zx_status_t Write(const std::vector<uint8_t>& data);
// Copies data from `other` into this buffer, returning the number of bytes written on success.
zx::result<size_t> CopyFrom(Buffer& other);
// Returns this buffer's length in bytes.
uint64_t length() const { return total_length_; }
protected:
struct BufferPart {
uint32_t buffer_id;
buffer_region_t region;
};
void PushPart(const BufferPart& part);
explicit Buffer(VmoStore* vmo_store) : vmo_store_(vmo_store) {}
cpp20::span<BufferPart> parts() { return cpp20::span(parts_.data(), parts_count_); }
cpp20::span<const BufferPart> parts() const { return cpp20::span(parts_.data(), parts_count_); }
private:
// Pointer to parent VMO store, not owned.
VmoStore* const vmo_store_;
std::array<BufferPart, MAX_BUFFER_PARTS> parts_;
size_t parts_count_ = 0;
uint64_t total_length_ = 0;
};
class TxBuffer : public Buffer {
public:
inline fuchsia_hardware_network::wire::FrameType frame_type() const { return frame_type_; }
uint32_t id() const { return parts().begin()->buffer_id; }
uint8_t port_id() const { return port_id_; }
inline std::optional<fuchsia_net_tun::wire::FrameMetadata> TakeMetadata() {
return std::exchange(meta_, std::nullopt);
}
protected:
friend VmoStore;
TxBuffer(const tx_buffer_t& tx, bool get_meta, VmoStore* vmo_store);
private:
const uint8_t port_id_;
const fuchsia_hardware_network::wire::FrameType frame_type_;
std::optional<fuchsia_net_tun::wire::FrameMetadata> meta_;
};
class RxBuffer : public Buffer {
public:
// Adds more rx buffer space to this buffer.
void PushRxSpace(const rx_space_buffer_t& space);
// Calls the provided closure function once for each buffer space in this RxBuffer.
template <typename F>
void WithSpace(F fn) {
for (BufferPart& part : parts()) {
fn(rx_space_buffer_t{
.id = part.buffer_id,
.region = part.region,
});
}
}
// Calls the provided closure function once for each return buffer part in this RxBuffer. The
// total length of the buffer parts is capped to `written_length` bytes.
template <typename F>
void WithReturn(size_t written_length, F fn) {
for (BufferPart& part : parts()) {
size_t length = std::min(written_length, part.region.length);
// Length is expected to be less than max uint32 because of max MTU, but guard it here with a
// debug assertion so the cast is still clearly safe.
ZX_DEBUG_ASSERT(length <= std::numeric_limits<uint32_t>::max());
fn(rx_buffer_part_t{
.id = part.buffer_id,
.length = static_cast<uint32_t>(length),
});
written_length -= length;
}
}
protected:
friend VmoStore;
explicit RxBuffer(VmoStore* vmo_store) : Buffer(vmo_store) {}
};
} // namespace tun
} // namespace network
#endif // SRC_CONNECTIVITY_NETWORK_TUN_NETWORK_TUN_BUFFER_H_