Skip to content

Getting Started

Community Guidelines

  • Be respectful
  • Don't be a dick
  • Be a part of the solution, not the problem

Prerequisites

  • Go 1.24+
  • Protocol Buffers compiler (protobuf-compiler)
  • golangci-lint
  • Docker & Docker Compose (for local testing)

Prefer a sandboxed VM?

If you'd rather not install Go, VPP, FRR, or Docker on your host directly, see Development VM (QEMU/KVM)make dev-vm provisions a Debian 12 VM with the full toolchain pre-installed.

Clone and Build

git clone https://github.com/veesix-networks/osvbng.git
cd osvbng
make build

This produces bin/osvbngd and bin/osvbngcli.

Running Locally

make build
cd docker/dev
./dev.sh start

This builds the Docker image, creates the network topology, and starts osvbng.

Development VM (QEMU/KVM)

For QEMU image work, dataplane changes, or to keep your host clean of VPP/FRR/kernel-module tweaks, make dev-vm provisions a Debian 12 VM via libvirt with the full toolchain (Go, VPP, FRR, Docker, containerlab, packer) pre-installed and your SSH key authorised. The Docker smoke test in docker/dev/ runs unchanged inside the VM.

Host prerequisites (Debian / Ubuntu / Pop!_OS)

sudo apt install -y \
    virtinst \
    libvirt-daemon-system \
    cloud-image-utils \
    qemu-utils \
    whiptail \
    rsync

sudo usermod -aG libvirt $USER
newgrp libvirt           # or log out and back in

You also need an SSH public key in ~/.ssh/. If you don't have one: ssh-keygen.

Don't run make dev-vm with sudo

The script issues its own sudo calls where needed and resolves your SSH key via $HOME. Running the whole thing with sudo makes it search /root/.ssh/ and fail with No SSH public keys found.

Create the VM

make dev-vm

Downloads the Debian 12 cloud image (~400 MB, cached), creates a 30 GB qcow2 disk, defines a 6 GB / 4 vCPU domain named osvbng-dev, then SSHes in and runs the provisioner. First run takes ~10 minutes; subsequent recreates are faster. Override defaults via env: VM_MEMORY=8192 make dev-vm.

Sync your code

make dev-vm-sync

Rsyncs the working tree into /home/dev/osvbng/ in the VM (excludes .git/, bin/, output/, *.qcow2).

Reprovision after versions.env changes

make dev-vm-provision

Re-runs the provisioner with the current versions.env so VPP / FRR / Go versions match the project pin.

Connecting to the VM

The VM gets a libvirt-DHCP lease on the default network (typically 192.168.122.0/24); the IP can change on recreate. Look it up:

virsh -c qemu:///system domifaddr osvbng-dev | awk '/ipv4/ {split($4,a,"/"); print a[1]}'

Add a host alias once and update the IP after each recreate:

# ~/.ssh/config
Host osvbng-dev
    HostName 192.168.122.X
    User dev
    StrictHostKeyChecking accept-new
    UserKnownHostsFile /dev/null
    LogLevel ERROR

Then: ssh osvbng-dev.

Reaching osvbng services from your host browser

When you run the in-VM Docker smoke test (docker/dev/dev.sh start), osvbng listens on a Docker bridge inside the VM (172.30.0.2:8080/9090/50050), so it isn't directly reachable from the host. Forward the ports over SSH:

ssh -L 8080:172.30.0.2:8080 \
    -L 9090:172.30.0.2:9090 \
    -L 50050:172.30.0.2:50050 \
    osvbng-dev

Leave that session open and open http://localhost:8080/api/docs on the host. Grafana is already published to the VM's eth0:3000, so it's reachable directly at http://<vm-ip>:3000 (admin/admin).

VM lifecycle

virsh start osvbng-dev
virsh shutdown osvbng-dev
virsh destroy osvbng-dev                              # force stop
virsh undefine osvbng-dev --remove-all-storage        # delete entirely

Development

Branch Naming

Use prefixed branch names:

Prefix Purpose
feat/ New features
fix/ Bug fixes
perf/ Performance improvements
ci/ CI/CD changes
refactor/ Code restructuring
docs/ Documentation
test/ Test changes

Code Style

Before submitting

Always run linting before opening a PR.

make lint   # check for issues
make fmt    # auto-fix formatting

Running Tests

make test          # run unit tests
make test-report   # run tests with JUnit XML report in build/reports/

Unit tests (make test) validate individual packages in isolation - parsers, allocators, config handling, protocol logic. They run fast, require no infrastructure, and are the first gate in CI.

Integration tests (tests/) use containerlab and robot framework to spin up a full osvbng instance with BNGBlaster subscribers, verifying end-to-end functionality across all supported features. They require Docker and take longer to run.

CI runs unit tests first. Integration tests will not run until unit tests pass. Both must pass before a PR can be merged.

Future: Hardware and QEMU testing

We plan to build physical and QEMU-based test infrastructure for throughput and subscriber scaling validation on minor and major releases.

Profiling

For performance work, set OSVBNG_PROFILE=1 in the container environment to enable pprof endpoints and runtime profiling (block rate + mutex fraction):

# CPU profile (capture during load test)
curl -o cpu.prof http://<bng-ip>:8080/debug/pprof/profile?seconds=30

# Block profile (where goroutines are blocked - mutexes, channels, syscalls)
curl -o block.prof http://<bng-ip>:8080/debug/pprof/block

# Mutex contention (which mutexes are contended and for how long)
curl -o mutex.prof http://<bng-ip>:8080/debug/pprof/mutex

# Goroutine dump (what all goroutines are doing right now)
curl -o goroutine.txt "http://<bng-ip>:8080/debug/pprof/goroutine?debug=2"

# Execution trace (goroutine scheduling, blocking, syscalls over time)
curl -o trace.out "http://<bng-ip>:8080/debug/pprof/trace?seconds=5"

Analyze with:

go tool pprof -top cpu.prof          # CPU hot functions
go tool pprof -top -cum block.prof   # where goroutines spend time blocked
go tool pprof -top -cum mutex.prof   # mutex contention ranking
go tool trace trace.out              # visual goroutine timeline

New Features

Before you start coding

Open an issue first to discuss your idea with the core developers. This helps ensure your contribution aligns with the project direction and avoids wasted effort.

Submitting Changes

  1. Fork the repo
  2. Create a branch (git checkout -b <type>/my-change)
  3. Make your changes
  4. Run make lint and make test
  5. Commit using Conventional Commits format and push
  6. Open a PR against main - the PR title must follow Conventional Commits format (PRs are squash-merged)

Documentation

Any contribution that adds, modifies, or removes behaviour must include corresponding documentation updates.

Questions?

Join the Discord, open a discussion, or file an issue.