|
This tool is in beta and your feedback is greatly appreciated.
The Network Simulation Bridge, or NSB for short, a simple, low-overhead pipeline consisting of a message server and client interface libraries that bridge together applications and network simulators. NSB is application-, network simulator-, and platform-agnostic that allows developers to integrate any application front-end with any network simulator back-end. For more information, or to cite NSB, you can access our publication. |
NSB was first created to address real research needs at the Inter-Networking Research Group at the University of California, Santa Cruz. The first proofs-of-concept that leveraged NSB included decentralized federated learning and autonomous vehicle platooning. While core development is active as of February 2026, we invite collaborators and users to help us in that process and to develop domain-specific and/or usage-specific solutions.
✉️ Contact us via email! We look forward to helping you, hearing your feedback, or collaborating with you to make your network co-simulation successful.
Interested in using NSB to model your connected systems? Looking for features not currently implemented? NSB was made to be extensible, but to identify what features we extend NSB with, we need to have motivating use cases. We'd love to work with new users/contributors to build simulation solutions together.
Working specifically on autonomous vehicles system modeling? This project was co-created in part to model the network behind vehicle-to-vehicle (V2V) networking for autonomous vehicles (AVs). We are currently searching for users/collaborators to let us know what they'd like to see in terms of more support for AV applications and V2V and V2X network simulations.
The following software packages are required to be installed:
- CMake, used to configure and build the project
- Pkg-Config, necessary in MacOS for package configuration
- Redis, whose server is used as a database to store payloads
- Abseil, necessary for Protobuf support and logging
- Protobuf, used to define and compile
- YAML parsing, to parse configuration files
- hiredis, to connect to the Redis server
Python API support involves additional package installation through its requirements.txt file and is detailed in the Python README.
Platform-specific package install commands are provided below. Also, note that previous installations of tools like gRPC that include protobuf may result in conflicting versions for protobuf.
brew install cmake pkg-config abseil protobuf yaml-cpp redis hiredis
Cmake is used to build this project. In order to build the NSB components
(the NSB Daemon executable and the C++ and Python client libraries), create a
build (mkdir build) directory at the top level of this project
directory, such that your directory now looks like this:
nsb/
├── build/
├── proto/
├── python/
├── cpp/
├── CMakeLists.txt
├── config.yaml
└── README.md
Then, enter the new build directory (cd build) and start by configuring
the CMake build:
cmake ..
Within the output, you should see something like this:
[cmake] -- Checking target libraries:
[cmake] -- ✓ Found target: yaml-cpp::yaml-cpp
[cmake] -- ✓ Found target: protobuf::libprotobuf
[cmake] -- ✓ Found target: absl::base
[cmake] -- ✓ Found target: absl::log
[cmake] -- ✓ Found target: absl::time
[cmake] -- ✓ Found target: absl::log_internal_check_op
[cmake] -- ✓ Found target: absl::log_initialize
[cmake] -- ✓ Found target: PkgConfig::hiredisIf all the prerequisite software was installed, you may continue with building and installing NSB.
cmake --build . --clean-firstcmake --install .The library, includes, and binary directories should now be available under
[your/install/path]/nsb. The install command will also make NSB available
on pkg-config as nsb, which may be of use when compiling projects with
NSB. The NSB Daemon executable used to run NSB can be found at
[your/install/path]/nsb/bin/nsb_daemon.
Uninstallation of packages from your install path is simple. You can either:
- Delete the
nsbdirectory from your install path to remove installed files and deletelib/pkgconfig/nsb.pcfrom your install path to remove the pkg-config index; or - Delete all files listed in the install manifest within
the project
builddirectory (xargs rm < install_manifest.txt).
Then, you may delete the build directory to remove the built project.
Coming soon.
Check the Linux-specific instructions.
Currently, we support and provide interfaces for Python and C++. These interfaces have a high level of feature parity, and we aim to keep it that way. The language-specific READMEs contain more detail and the documents contain detailed API manuals.
The NSB Daemon serves as the bridge between the application space and the
network simulator space and runs from its compiled binary. NSB provides two main
interfaces – the NSB Application Client (NsbAppClient) and the
NSB Simulator Client (NsbSimClient) – two connect the bridge between
the application and simulator, respectively.
In Python, the client interfaces are available via nsb_client.py which you must import. We recommend copying the contents of the python directory, including the proto folder, to your Python workspace.
import nsb_client as nsbIn C++, the installed libnsb.dylib comes with two headers – nsb.h and
nsb_client.h – under the nsb namespace. Including nsb_client.h will give
you access to the client interfaces while nsb.h will provide access to more
general NSB logic.
#include "nsb.h"
#include "nsb_client.h"To integrate NSB into your projects, you may need to add NSB to your project and/or compile your project with NSB. To add NSB into your Python project, follow the Python instructions. To compile your C++ project with NSB, follow the C++ instructions.
The NSB Application Client provides an interface to your application and
presents as a simplified version of traditional network interfaces. The
application client can be created via its constructor. The send method sends a
payload to a target destination via NSB. The receive method receives incoming
payloads via NSB. The listen method (not yet implemented in C++, sorry) is an
asynchronous reception of incoming messages.
In Python:
nsb_app = nsb.NSBAppClient("node0", "127.0.0.1", 65432)
...
# Send a payload.
outgoing_payload = b"Hello world!"
nsb_app.send("node1", payload)
...
# Receive a payload.
incoming_payload = nsb_app.receive() # Returns None if nothing was received.
if incoming_payload:
# Process the incoming payload.
...
# You can access the payload properties.
print(f"Source: {incoming_payload.source}\n" + \
f"Destination: {incoming_payload.destination}\n" + \
f"Payload Size: {incoming_payload.payload_size}\n" + \
f"Payload: {incoming_payload.payload}\n")In C++:
...
const std::string client_id = "node0";
std::string daemon_address = "127.0.0.1";
int daemon_port = 65432;
nsb::NSBAppClient nsbApp = nsb::NSBAppClient(std::string("node0"), daemon_address, daemon_port);
// Send a payload.
std::string outgoing_payload = "Hello World!";
nsb_app.send(std::string("node1"), outgoing_payload);
...
// Receive a payload.
nsb::MessageEntry incoming_payload = nsbApp.receive()
if (incoming_payload.exists()) {
// Process the incoming payload.
...
// You can access the payload properties.
std::cout << "Source: << incoming_payload.source\n" <<
"Destination: << incoming_payload.destination\n" <<
"Payload Size: << incoming_payload.payload_size\n" <<
"Payload: << incoming_payload.payload\n" << std::endl;
}The NSB Simulator Client provides an interface to your network simulator
that contains methods to fetch payloads to be routed through the simulated
network and to post payloads when they have completed their journey through the
simulated network. The application client can be created via its constructor.
The fetch method checks and fetches payloads that were sent and are to be
transmitted over the simulated network. The post method is used when a payload
arrives at the destination node in the simulated network to allow the payload to
be received at the receiving application client.
In Python:
nsb_app = nsb.NSBSimClient("node0", "127.0.0.1", 65432)
...
# Fetch payload.
payload_to_transmit = nsb_app.fetch()
if payload_to_transmit: # Returns None if nothing was received.
# You can access the payload properties.
print(f"Source: {payload_to_transmit.source}\n" + \
f"Destination: {payload_to_transmit.destination}\n" + \
f"Payload Size: {payload_to_transmit.payload_size}\n" + \
f"Payload: {payload_to_transmit.payload}\n")
# Send over simulated network.
...
...
# Process the arrived payload from src_id to dest_id.
nsb_app.post(payload, src_id, dest_id)In C++:
...
std::string daemon_address = "127.0.0.1";
int daemon_port = 65432;
nsb::NSBSimClient nsbApp = nsb::NSBSimClient(std::string("node0"), daemon_address, daemon_port);
// Fetch payload.
MessageEntry payloadToTransmit = nsbApp.fetch();
if (payloadToTransmit.exists()) {
// You can access the payload properties.
std::cout << "Source: << payloadToTransmit.source\n" <<
"Destination: << payloadToTransmit.destination\n" <<
"Payload Size: << payloadToTransmit.payload_size\n" <<
"Payload: << payloadToTransmit.payload\n" << std::endl;
// Send over simulated network.
...
}
...
// Process the arrived payload from src_id to dest_id.
nsb_app.post(payload, src_id, dest_id);The system configuration can be done within a YAML file. An example is provided in config.yaml for your convenience. We provide a few different modes of operation.
The system mode (system→mode) can be set to either PULL (0) or
PUSH (1) mode.
- In PULL mode, the NSB clients will poll the daemon server to fetch or receive messages. This is recommended for most configurations.
- In PUSH mode, the NSB daemon server will automatically forward sent and posted messages to the receiving clients, such that they can be readily received or fetched without making a request to the server. This achieves better latency but may not work with all user device, program, and network configurations.
The simulator mode (system→sim_mode) can be set to either
System-Wide (0) or Per-Node (1) mode.
- In System-Wide mode, it is assumed that there will only be one simulator client and creating multiple simulator clients will not be allowed. The simulator client will fetch all payloads to be transmitted. This is good for top-down network simulator implementations like ns-3.
- In Per-Node mode, it is assumed that each node in your network will have a
respective simulator client. These simulator clients must have the same
identifier as their co-related application client, so that when an
NSBAppClientwith identifier"node0"sends a payload, it will be fetched by its correspondingNSBSimClientwith identifier"node0", and vice-versa with posting and receiving payloads. This is good for bottom-up network simulator implementations like OMNeT++.
These instructions assume you have already implemented the client-side APIs in your code. Once this is complete, we recommend taking these steps in order:
-
Start the Redis server. Specify or take note of the port number that the server is running on and make sure your configuration file points to the address and port.
redis-server -p 5050 -
Start the NSB Daemon. If you followed the build instructions in above, then you can start the NSB Daemon executable from either the build directory or via install path, with that target configuration file:
./build/nsb_daemon config.yaml
/[your/install/path]/nsb/bin/nsb_daemon config.yaml
-
Start the modified network simulator. In most cases, the simulator, modified using the NSBSimClient API, should be started before the application in order to be ready and listening for messages from the application space.
-
Run your modified application. Using the NSBAppClient API, your application(s) should now be able to send messages via NSB over the simulated network.
Coming soon.
We would like to thank the development team (all Ph.D., M.S., and undergraduate students, past and present) who have worked on this version and past versions of NSB. We would also like to thank the Open Source Program Office (OSPO) in the Center for Research in Open Source Systems (CROSS) at the University of California, Santa Cruz, for their guidance in evolving NSB into an open-source ecosystem.
Copyright 2026 UC Santa Cruz
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
-
Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
-
Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
-
Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
