tree: c0e0494534db72ff8a314d726398930652144634 [path history] [tgz]
  1. configurable-netstack/
  2. doc/
  3. fidl/
  4. network-context/
  5. runner/
  6. rust/
  7. service/
  8. sync-manager/
  9. test-support/
  10. BUILD.gn
  11. OWNERS
  12. README.md
src/connectivity/network/testing/netemul/README.md

Netemul

Netemul is a set of components that make it easy to create hermetic test environments and virtual networks for integration tests.

Netemul can be used to run components in sandboxed realms, and also to configure and manage virtual networks between those realms, which can be used to emulate multiple Fuchsia devices communicating over networks. This is particularly useful for creating self-contained, hermetic integration tests that exercise the netstack and other networking components.

Netemul has two components you’re likely to use as a test author: netemul-sandbox and netemul-runner.

  • The netemul-runner is a test runner, as defined by the Test Runner Framework, and is the more “managed” of the two options. A netemul-runner test includes some network configuration in its component manifest (see example), and the netemul-runner runs the test within the configured network environment.
  • The netemul-sandbox is a “plain” component that serves the fuchsia.netemul/Sandbox protocol, which allows clients to create realms and configure emulated networks themselves. The runner itself uses the sandbox to do its configuration; in this sense the sandbox is the “unmanaged” lower-level option.

Which one should I use?

The key factor that determines whether you should use the netemul-runner is whether your test component needs to run inside a Netemul realm. (This might be necessary, for example, to ensure that the protocols available in its namespace are provided by test components.) There are also other reasons to write a netemul-runner test: for example, if you want to write a test that exercises the POSIX socket APIs and runs on both Linux and Fuchsia for compatibility, the runner is the best fit. This is because the runner allows you to write one test that is run as a plain binary on Linux, and is packaged into a component and configured via Netemul on Fuchsia (i.e. the platform-agnostic test code is separated from the platform-specific test configuration, which lives in a component manifest). In general, though, the Netstack team defaults to the netemul-sandbox for “internal” tests, i.e. those where networking component(s) themselves are under test. If you're unsure, here is a decision flowchart that should help you decide:

decision flowchart for netemul tests

Sandbox

The netemul-sandbox is essentially a service provider. Tests that use it directly create sandboxes, managed realms, networks, and endpoints by calling fuchsia.netemul FIDL methods.

Test setup

Sandbox tests can be written in any FIDL-supported language, though all current tests are written in Rust and use the Rust client library (see documentation) to interact with the fuchsia.netemul FIDL APIs. Many tests also use the netstack_testing_common library which contains useful helpers for setting up network realms and virtual networks and interacting with their constituent components (in particular the netstack).

Refer to this example test (see the component manifest and test source) that uses the Netemul Sandbox to create two hermetic test realms containing Netstack2 components, both connected to the same virtual network over virtual interfaces. The test also uses the RealmUdpSocket trait to create UDP sockets with each hermetic netstack and bind each of them to an address assigned to its respective virtual interface. The test then exercises the netstack‘s socket API by sending a UDP packet from the client to the server and verifying it’s sent and received properly.

Design & Architecture

The netemul-sandbox itself is the core realm-management service provider, and it implements fuchsia.netemul/Sandbox and fuchsia.netemul/ManagedRealm. The sandbox is built on top of Realm Builder, an integration testing library that supports dynamic construction of component topologies at runtime.

Here’s a (simplified) illustration of the component topology of an integration test that uses the Netemul Sandbox (the components with a white background are non-executable, meaning they don't have a runnable program; they exist for organizational/routing purposes):

netemul sandbox test component topology

As you can see, the test realms are created inside a component collection, which is managed by Realm Builder. Capabilities are routed internally between components in the test realm, and can also be connected to dynamically from the test (for example, the test could exercise fuchsia.posix.socket/Provider exposed by the netstack to test sockets).

Network Context

As you can see in the component diagram above, the netemul-sandbox component uses the network-context component to provide access to the fuchsia.netemul.network/NetworkContext protocol. network-context provides the ability to configure virtual networks.

Runner

The netemul-runner is a test runner™️ in the sense of the Component Framework and Test Runner Framework—a component runner serves the fuchsia.component.runner/ComponentRunner protocol so that the Component Manager can start the component, and a test runner implements fuchsia.test/Suite on behalf of the test component, so that the Test Manager can run the tests on behalf of a user-facing tool like ffx test.

netemul-runner implements the fuchsia.test/Suite protocol by first configuring the Netemul environment in which the inner test component should run, then proxying fuchsia.test/Suite requests to that component. This allows the runner to delegate the job of actually running the components in the test to the Component Framework, and delegate running the test and evaluating its results to the respective test runner.

Test setup

Outside the program section, the manifest for a Netemul Runner test is largely an ordinary component manifest; child components and capability routing are declared statically. There are, however, some things about a Netemul Runner test that are different from a typical test component:

Test root vs. test driver

A Netemul Runner test effectively requires two components: the component that actually contains the test suite (sometimes called the test driver), and a non-executable parent component (sometimes called the test root) that specifies the network environment in which the test should be run and includes the test driver along with any other components in the integration test as child components. A developer invoking the test will actually run (e.g. with ffx test) the root component, which specifies "netemul_test_runner" as its runner (typically by including the default shard in its manifest). The test driver looks like an ordinary test component, but expects to be run in the network environment declared by the test root.

Network configuration

The virtual network and netstacks in the test are configured in the program section of the test root’s manifest. Following is the schema of the program expected by the Netemul Runner (fields are required unless specifically noted to be optional). You can also look at the definition of this schema where it's defined in the source code of the Netemul Runner here.

Program
FieldTypeDescription
networksArray of NetworkVirtual networks to be configured.
netstacksArray of NetstackNetstack components to be configured; each netstack should be available in the test root’s namespace at /svc/{name}, where name is the name of the netstack.
start(optional) Array of StringNames of child components to be eagerly started once test network configuration is complete.
Network: a virtual broadcast domain backed by netemul.
FieldTypeDescription
nameStringThe name of the network (must be unique).
endpointsArray of EndpointEndpoints to be created and attached to this network.
Endpoint: a virtual network endpoint backed by netemul.
FieldTypeDescription
nameStringThe name of the endpoint (must be unique across all networks).
mac(optional) StringMAC address of the virtual endpoint. If not set, will be generated randomly using the endpoint's name as a seed.
mtu(optional) NumberMTU of the virtual endpoint. If not set, defaults to 1500.
up(optional) BooleanWhether to bring the link up during network setup. If not set, defaults to true.
backing(optional) StringBacking type of endpoint, either "ethertap" or "network_device". If not set, defaults to "network_device".
Netstack: a configurable netstack component.
FieldTypeDescription
nameStringThe name of the netstack (must be unique).
interfacesArray of InterfaceInterfaces to be installed in the netstack.
Interface: an endpoint that has been installed in a netstack.
FieldTypeDescription
nameStringThe name of the interface to be installed. Must match an endpoint declared on a network.
static_ipsArray of StringStatic IP addresses to be assigned to the interface. Corresponding local subnet routes will also be added to the netstack’s routing table.
without_autogenerated_addresses(optional) BooleanWhether to disable automatic generation and assignment of link-local IPv6 addresses for the interface. If not set, defaults to false.
gateway(optional) StringIP address of the default gateway. If not set, no default route is configured.
enable_ipv4_forwarding(optional) BooleanWhether to enable IPv4 forwarding on the interface. If not set, defaults to false.
enable_ipv6_forwarding(optional) BooleanWhether to enable IPv6 forwarding on the interface. If not set, defaults to false.

use clauses required in test root component manifest

The test root component must use fuchsia.test/Suite from the test driver, so that the Netemul Runner can proxy it, and it also is required to use the fuchsia.netemul/ConfigurableNetstack protocol for each of the netstacks to be configured in the test. Each netstack use clause must set the path field to /svc/{name}, where name is the name of the netstack as specified under program. See the example test linked below for an example.

Example test

Refer to this example test that uses the Netemul Runner to run a client and server that are connected over a virtual network. (In particular, see the component manifest for the test root and the source for the test driver and server. Each netstack is configured with a network interface and a static IP address. Additionally, the server component is configured with eager startup, meaning the Netemul Runner will actively start it once test setup is complete, so it can bind to its statically assigned IP address and listen for incoming connections.

In addition to the client, server, and corresponding netstacks, this test includes a sync-manager, which allows the components to synchronize test execution (see below).

Design & Architecture

As mentioned above, the Netemul Runner is mainly a high-level orchestrator that configures the network environment in which the test will run and acts as liaison between the Test Manager and the test driver, while delegating most of the functionality of the runner to other components:

  • Test realm management is left to the Component Framework.
  • Running the test driver is left to its respective test runner.
  • Network emulation is left to the netemul-sandbox and network-context component.
  • The implementation details of netstack configuration are left to the Configurable Netstack.

This makes the Netemul Runner’s component topology rather simple; it is a component that exposes the netemul_test_runner capability and has a netemul-sandbox as a child component, for use in creating network environments. The rest of the relevant components (the test driver, the configurable-netstack, etc.) are part of the actual test.

Configurable Netstack

The configurable netstack is a support component used in Netemul Runner tests that is essentially a test-friendly wrapper around the netstack’s administrative FIDL APIs. It serves the fuchsia.netemul/ConfigurableNetstack protocol, which provides a single method to create an interface by installing a device in the netstack, and configure it with, for example, static IP addresses.

The primary utility of the configurable netstack is in providing a stable interface between the Netemul Runner and the netstack. Netemul Runner tests use fuchsia.netemul/ConfigurableNetstack, which makes it available in its namespace; the runner accesses it from there in order to configure the requested interfaces. The runner could theoretically call all the netstack protocols directly, but this would mean that as those APIs change, all existing Netemul Runner tests would have to be updated along with the calling code in the Netemul Runner. The configurable netstack provides a stable API behind which we can encapsulate any netstack-specific management APIs.

Sync Manager

The sync-manager is a support component used to synchronize components running in a Netemul Runner test “out-of-band”, i.e. without direct communication over FIDL APIs or the network. For example, in this test, the client uses the sync-manager to wait until the server is accepting connections before attempting to connect to the server, because attempts to connect before the server has had a chance to bind to its assigned IP addresses would fail. To synchronize, the server joins the bus once it’s bound, and the client waits for the server to join before starting connection attempts.

Netemul guest tests

The Netemul Runner previously supported running a virtualized guest as part of an integration test. One use case for this was to verify interoperability between Fuchsia’s networking stack and that of other operating systems. This is not currently supported, but there is a proposed design to support it (without using the runner itself) if we have a need for it: https://fxbug.dev/87587.