| // 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. |
| library fuchsia.net.tun; |
| |
| using zx; |
| using fuchsia.hardware.network; |
| using fuchsia.net; |
| |
| /// Maximum number of multicast filters that a device holds in `MacState`. |
| // NOTE: this number mirrors the number in the low level banjo definition in |
| // [`fuchsia.hardware.network.mac/MAX_MAC_FILTER`] which was chosen, in turn, |
| // based on common maximum number of multicast groups supported in established |
| // OSes. |
| const MAX_MULTICAST_FILTERS uint32 = 64; |
| |
| /// Maximum number of pending [`fuchsia.net.tun/Device.WriteFrame`] or |
| /// [`fuchsia.net.tun/Device.ReadFrame`] that are allowed. |
| // NOTE: This number is chosen arbitrarily to maintain a determined upper bound |
| // on memory consumption for a `Device` instance. |
| const MAX_PENDING_OPERATIONS uint32 = 32; |
| |
| /// Signals set in the `eventpair` returned by |
| /// [`fuchsia.net.tun/Device.GetSignals`]. |
| type Signals = strict bits : uint32 { |
| /// Indicates that write buffers are available to be used through |
| /// [`fuchsia.net.tun/Device.WriteFrame`]. |
| WRITABLE = 0x01000000; // ZX_USER_SIGNAL_0 |
| /// Indicates that read buffers are available to be used through |
| /// [`fuchsia.net.tun/Device.ReadFrame`]. |
| READABLE = 0x02000000; // ZX_USER_SIGNAL_1 |
| }; |
| |
| /// Maximum supported MTU. |
| // NOTE: Selected as the smallest power of 2 that will fit conventional jumbo |
| // frame sizes of 9KB. |
| // Source: https://en.wikipedia.org/wiki/Jumbo_frame. The value is chosen |
| // arbitrarily low (while abiding by the conventional jumbo frame sizes) to |
| // encourage sensible memory usage for clients of the NetworkDevice interface, |
| // as receive buffers must be at least MTU-sized for valid operation. |
| const MAX_MTU uint32 = 16384; |
| |
| /// Logical device port configuration. |
| type BasePortConfig = table { |
| /// Port identifier. |
| /// |
| /// Required. |
| 1: id fuchsia.hardware.network.BasePortId; |
| /// Device MTU (maximum transmit unit). |
| /// |
| /// Valid iff less than or equal to [`MAX_MTU`]. |
| /// |
| /// If not set, interpreted as [`MAX_MTU`]. |
| 2: mtu uint32; |
| /// Supported Rx frame types for port. |
| /// |
| /// Valid iff non-empty. |
| /// |
| /// Required. |
| 3: rx_types vector<fuchsia.hardware.network.FrameType>:fuchsia.hardware.network.MAX_FRAME_TYPES; |
| /// Supported Tx frame types on port. |
| /// |
| /// Valid iff non-empty. |
| /// |
| /// Required. |
| 4: tx_types |
| vector<fuchsia.hardware.network.FrameTypeSupport>:fuchsia.hardware.network.MAX_FRAME_TYPES; |
| }; |
| |
| /// Base device configuration. |
| type BaseDeviceConfig = table { |
| /// Report frame metadata on receiving frames. |
| /// |
| /// If not set, Interpreted as `false`. |
| 1: report_metadata bool; |
| /// Minimum requested TX buffer length, in bytes. |
| /// |
| /// If not set, interpreted as zero. |
| 2: min_tx_buffer_length uint32; |
| /// Minimum requested RX buffer length, in bytes. |
| /// |
| /// If not set, interpreted as zero. |
| 3: min_rx_buffer_length uint32; |
| }; |
| |
| /// Internal device state. |
| type InternalState = table { |
| /// State associated with Mac Address filtering. |
| /// |
| /// Devices never perform any MAC address filtering, but they implement the |
| /// [`fuchsia.hardware.network/MacAddressing`] interface and store the |
| /// values to be retrieved through the [`fuchsia.net.tun/InternalState`] |
| /// structure. |
| /// |
| /// Set iff `mac` is provided in the [`DevicePortConfig`] or |
| /// [`DevicePairPortConfig`] structures upon creation of the port. |
| 1: mac @generated_name("MacState") table { |
| /// The currently configured MAC Address filtering mode. |
| /// |
| /// Required. |
| 1: mode fuchsia.hardware.network.MacFilterMode; |
| /// The full list of configured multicast address filtering. |
| /// |
| /// Required. |
| 2: multicast_filters vector<fuchsia.net.MacAddress>:MAX_MULTICAST_FILTERS; |
| }; |
| |
| /// Whether there is a session currently opened and running with the `Port`. |
| /// |
| /// Required. |
| 2: has_session bool; |
| }; |
| |
| /// A frame written to or read from a [`fuchsia.net.tun/Device`]. |
| /// |
| /// Required fields must always be provided to |
| /// [`fuchsia.net.tun/Port.WriteFrame`] and are always present when returned by |
| /// [`fuchsia.net.tun/Port.ReadFrame`]. |
| type Frame = table { |
| /// The type identifying this frame's payload. |
| /// |
| /// Required. |
| 1: frame_type fuchsia.hardware.network.FrameType; |
| /// The frame's payload. |
| /// |
| /// Valid iff non-empty. |
| /// |
| /// Required. |
| 2: data vector<uint8>:MAX_MTU; |
| /// Extra frame metadata. |
| /// |
| /// This is an opaque holder for extra information that is associated with |
| /// Network Device data frames. |
| /// |
| /// If not set, interpreted as empty. |
| // NOTE(brunodalbo): NetworkDevice's `InfoType` definition is still in its |
| // infancy. This solution allows access to the raw bytes in the sidecar |
| // metadata. We expect that this will evolve into a more type-safe solution |
| // which will be more transparent. |
| 3: meta @generated_name("FrameMetadata") struct { |
| /// Additional frame information type. |
| /// |
| /// If not set, interpreted as |
| /// [`fuchsia.hardware.network/InfoType.NO_INFO`]. |
| info_type fuchsia.hardware.network.InfoType; |
| /// Additional frame information value. |
| /// |
| /// If not set, interpreted as empty bytes. |
| info vector<uint8>:4096; |
| /// Frame flags. `RxFlags` for `WriteFrame` and `TxFlags` for |
| /// `ReadFrame`. |
| /// |
| /// If not set, interpreted as zero. |
| flags uint32; |
| }; |
| |
| /// Frame's destination or source port identifier. |
| /// |
| /// Required. |
| 4: port fuchsia.hardware.network.BasePortId; |
| }; |
| |
| /// A logical port attached to a [`fuchsia.net.tun/Device`]. |
| /// |
| /// This protocol encodes the underlying object's lifetime in both directions; |
| /// the underlying object is alive iff both ends of the protocol are open. That |
| /// is: |
| /// |
| /// - Closing the client end causes the object to be destroyed. |
| /// - Observing a closure of the server end indicates the object no longer |
| /// exists. |
| protocol Port { |
| /// Gets the port internal state. |
| /// |
| /// - response `state` a snapshot of the port's internal state. |
| GetState() -> (struct { |
| state InternalState; |
| }); |
| /// Observes changes to internal state. |
| /// |
| /// The first call always returns the current internal state, subsequent |
| /// calls block until the internal state differs from the last one returned |
| /// from a `WatchState` call. |
| /// |
| /// `WatchState` does not provide full history of internal state changes. It |
| /// is possible that intermediary internal state changes are missed in |
| /// between `WatchState` calls. |
| /// |
| /// - response `state` the latest observed port internal state. |
| WatchState() -> (struct { |
| state InternalState; |
| }); |
| /// Sets the port's online status. |
| /// |
| /// The online status is visible through |
| /// [`fuchsia.hardware.network/Port.GetStatus`]. Once `SetOnline` returns, |
| /// the status reported through `GetStatus` is guaranteed to be the one |
| /// passed to `SetOnline`. |
| /// |
| /// + request `online` desired port online state. |
| SetOnline(struct { |
| online bool; |
| }) -> (); |
| /// Connects to the underlying device port. |
| /// |
| /// + request `port` grants access to the device port. |
| GetPort(resource struct { |
| port server_end:fuchsia.hardware.network.Port; |
| }); |
| /// Triggers port removal. |
| /// |
| /// The client end will be closed once the server has completely cleaned up |
| /// all resources related to the port. This is equivalent to simply dropping |
| /// the client end, but provides callers with a signal of when removal is |
| /// complete, allowing port identifiers to be reused, for example. |
| Remove(); |
| }; |
| |
| /// Provides control over the created device. |
| /// |
| /// This protocol encodes the underlying object's lifetime in both directions; |
| /// the underlying object is alive iff both ends of the protocol are open. That |
| /// is: |
| /// |
| /// - Closing the client end causes the object to be destroyed. |
| /// - Observing a closure of the server end indicates the object no longer |
| /// exists. |
| protocol Device { |
| /// Writes a frame to the device (data coming from network-end). |
| /// |
| /// If the device was created with the |
| /// [`fuchsia.net.tun/DeviceConfig.blocking`] option set to `true`, calls to |
| /// `WriteFrame` block until there is one buffer available to fulfill the |
| /// request. |
| /// |
| /// + request `frame` inbound frame data and metadata. |
| /// * error `ZX_ERR_NOT_FOUND` if [`Frame.port`] references an unknown port. |
| /// * error `ZX_ERR_INVALID_ARGS` if `frame` is invalid. |
| /// * error `ZX_ERR_BAD_STATE` if the device is offline. |
| /// * error `ZX_ERR_BAD_STATE` if the device is offline. |
| /// * error `ZX_ERR_NO_RESOURCES` if more than |
| /// [`fuchsia.net.tun/MAX_PENDING_OPERATIONS`] calls to `WriteFrame` are |
| /// pending. |
| /// * error `ZX_ERR_SHOULD_WAIT` if `blocking` is set to `false` and there |
| /// are no buffers available to fulfill the request. |
| WriteFrame(struct { |
| frame Frame; |
| }) -> () error zx.status; |
| /// Gets the next frame from the device (data coming from host-end). |
| /// |
| /// If the device was created with the |
| /// [`fuchsia.net.tun/DeviceConfig.blocking`] option set to `true`, calls to |
| /// `ReadFrame` block until there is a frame available to be read. |
| /// |
| /// - response `frame` outbound frame data and metadata. |
| /// * error `ZX_ERR_NO_RESOURCES` if more than |
| /// [`fuchsia.net.tun/MAX_PENDING_OPERATIONS`] calls to `ReadFrame` are |
| /// pending. |
| /// * error `ZX_ERR_SHOULD_WAIT` if `blocking` is set to `false` and there |
| /// are no frames to be read. |
| ReadFrame() -> (struct { |
| frame Frame; |
| }) error zx.status; |
| /// Retrieves signals eventpair. |
| /// |
| /// - response `signals` an eventpair that is signalled with |
| /// `SIGNAL_READABLE` and `SIGNAL_WRITABLE` when read and write buffers are |
| /// available, respectively. |
| GetSignals() -> (resource struct { |
| signals zx.handle:EVENTPAIR; |
| }); |
| /// Creates a new port on this device. |
| /// |
| /// + request `config` new port configuration. |
| /// + request `port` grants control over the port. Closed with an epitaph if |
| /// `config` is not valid. |
| AddPort(resource struct { |
| config @generated_name("DevicePortConfig") table { |
| /// Base port configuration. |
| /// |
| /// Required. |
| 1: base BasePortConfig; |
| /// Start port with link online. |
| /// |
| /// If not set, interpreted as `false`. |
| 2: online bool; |
| /// MAC address to report. |
| /// |
| /// If set, the port provides a |
| /// [`fuchsia.hardware.network/MacAddressing`] implementation / |
| /// through [`fuchsia.hardware.network/Port.GetMac`]. |
| 3: mac fuchsia.net.MacAddress; |
| }; |
| port server_end:Port; |
| }); |
| /// Connects to the underlying device endpoint. |
| /// |
| /// + request `device` device handle. |
| GetDevice(resource struct { |
| device server_end:fuchsia.hardware.network.Device; |
| }); |
| }; |
| |
| /// Provides control over a pair of network devices. |
| /// |
| /// A `DevicePair` is a simpler version of `Device` that "shorts" two network |
| /// device interfaces, named its "left" and "right" ends. The internal state of |
| /// a `DevicePair` is not accessible, like it is for `Device` and it provides a |
| /// more streamlined (and considerably faster) pair of |
| /// [`fuchsia.hardware.network/Device`]s. The transmit side of each port of the |
| /// left end is connected to the receive side of the port with the same |
| /// identifier on the right end, and vice-versa. A `DevicePair`'s port online |
| /// signal is handled internally (online if any of the ends has an active data |
| /// session). If MAC addresses are provided on creation, the only supported MAC |
| /// filtering mode is `PROMISCUOUS`. |
| /// |
| /// This protocol encodes the underlying object's lifetime in both directions; |
| /// the underlying object is alive iff both ends of the protocol are open. That |
| /// is: |
| /// |
| /// - Closing the client end causes the object to be destroyed. |
| /// - Observing a closure of the server end indicates the object no longer |
| /// exists. |
| protocol DevicePair { |
| /// Adds a logical port to this device pair. |
| /// |
| /// + request `config` port configuration. |
| /// * error `ZX_ERR_INVALID_ARGS` if `config` is invalid. |
| /// * error `ZX_ERR_ALREADY_EXISTS` if the provided port identifier is |
| /// already in use. |
| AddPort(struct { |
| config @generated_name("DevicePairPortConfig") table { |
| /// Base port configuration. |
| /// |
| /// Required. |
| 1: base BasePortConfig; |
| /// MAC address to report. |
| /// |
| /// If set, left port provides a |
| /// [`fuchsia.hardware.network/MacAddressing`] implementation |
| /// through [`fuchsia.hardware.network/Port.GetMac`]. |
| 2: mac_left fuchsia.net.MacAddress; |
| /// MAC address to report. |
| /// |
| /// If set, right port provides a |
| /// [`fuchsia.hardware.network/MacAddressing`] implementation |
| /// through [`fuchsia.hardware.network/Port.GetMac`]. |
| 3: mac_right fuchsia.net.MacAddress; |
| }; |
| }) -> () error zx.status; |
| /// Removes a logical port created by |
| /// [`fuchsia.net.tun/DevicePair.AddPort`]. |
| /// |
| /// + request `id` identifier of the port to remove. |
| /// * error `ZX_ERR_NOT_FOUND` if `id` does not map to an existing port. |
| RemovePort(struct { |
| id fuchsia.hardware.network.BasePortId; |
| }) -> () error zx.status; |
| /// Connects to the underlying left device endpoint. |
| /// |
| /// + request `device` handle serve the left device endpoint on. |
| GetLeft(resource struct { |
| device server_end:fuchsia.hardware.network.Device; |
| }); |
| /// Connects to the underlying right device endpoint. |
| /// |
| /// + request `device` handle serve the right device endpoint on. |
| GetRight(resource struct { |
| device server_end:fuchsia.hardware.network.Device; |
| }); |
| /// Connects to an underlying left port. |
| /// |
| /// + request `id` requested port identifier. |
| /// + request `port` grants access to the requested port on the left device. |
| GetLeftPort(resource struct { |
| id fuchsia.hardware.network.BasePortId; |
| port server_end:fuchsia.hardware.network.Port; |
| }); |
| /// Connects to an underlying right port. |
| /// |
| /// + request `id` requested port identifier. |
| /// + request `port` grants access to the requested port on the right device. |
| GetRightPort(resource struct { |
| id fuchsia.hardware.network.BasePortId; |
| port server_end:fuchsia.hardware.network.Port; |
| }); |
| }; |
| |
| /// Control interface. |
| /// |
| /// `Control` allows creating an arbitrary number of `Device`s and |
| /// `DevicePair`s. |
| @discoverable |
| protocol Control { |
| /// Creates a `Device` with given `config`. |
| /// |
| /// + request `config` new device configuration. |
| /// + request `device` grants control over the device. Closed with an |
| /// epitaph if `config` is not valid. |
| CreateDevice(resource struct { |
| config @generated_name("DeviceConfig") table { |
| /// Base device configuration. |
| /// |
| /// It not set, interpreted as an empty table. |
| 1: base BaseDeviceConfig; |
| /// If `true`, [`fuchsia.net.tun/Device.WriteFrame`] and |
| /// [`fuchsia.net.tun/Device.ReadFrame`] blocks returning until the |
| /// corresponding buffers are available to complete the call. |
| /// |
| /// It not set, interpreted as `false`. |
| 2: blocking bool; |
| }; |
| |
| device server_end:Device; |
| }); |
| /// Creates a `DevicePair` with given `config`. |
| /// |
| /// If `config` is not valid or the device could not be created, |
| /// `device_pair` is closed with an error epitaph. |
| /// |
| /// + request `config` new device pair configuration. |
| /// + request `device_pair` grants control over the device pair. Closed with |
| /// an epitaph if `config` is not valid. |
| CreatePair(resource struct { |
| config @generated_name("DevicePairConfig") table { |
| /// Base device configuration. |
| /// |
| /// It not set, interpreted as an empty table. |
| 1: base BaseDeviceConfig; |
| /// If `true`, transmit buffers on the left end are dropped if no |
| /// receive buffers are available on the right end to receive it. |
| /// Otherwise, transmit buffers wait until a receive buffer is |
| /// available to copy them to. |
| /// |
| /// It not set, interpreted as `false`. |
| 2: fallible_transmit_left bool; |
| /// Like `fallible_transmit_left` but allows writes to the right end |
| /// to be fallible. |
| /// |
| /// It not set, interpreted as `false`. |
| 3: fallible_transmit_right bool; |
| }; |
| |
| device_pair server_end:DevicePair; |
| }); |
| }; |