1. Installation

Waterfall runs on Linux. Building it requires Go 1.23 or newer, a C toolchain, pkg-config, and the development packages for libpcap and libnetfilter_queue. Live experiments also require iptables and root privileges for packet capture and traffic actions.

On Debian or Ubuntu, install the native dependencies with:

sudo apt-get update
sudo apt-get install -y build-essential pkg-config \
  libpcap-dev libnetfilter-queue-dev iptables

Clone the repository and build the executable:

git clone --depth 1 https://github.com/try0S/waterfall-framework.git
cd waterfall-framework/waterfall/cmd
go build -o waterfall .

The Go toolchain is not needed to run the compiled binary. YAML and Lua changes do not require recompiling it; Waterfall reads its config, policy, and manifests at startup, so simply restart it after changing them.

2. Configuration

Waterfall uses a system config and a setup policy. The config file path defaults to ~/.config/waterfall/config.yaml.

Most fields of the config file are optional, but it's important to define the network interface. A simple config file may look like this:

network_interface: "eth0"
capsule_addresses:
  embedder: "203.0.113.10:50051"
key_dir: "/waterfall/keys"
manifest_dir: "/waterfall/schemes"
script_dir: "/waterfall/schemes"

The config selects the network interface and sets the peer nodes. Also, it may set defaults for the policy, scheme directory, and key directory paths. The -policy, -manifest-dir, -script-dir, and -keys flags can override their corresponding paths.

The setup policy selects schemes, supplies their parameters, and defines the traffic events that start capsules. It must provide every parameter declared by the selected manifest, using the declared type. For example:

capsules:
  - name: myDetectionScheme
    parameters:
      - interval_duration_ms: 1000
    start_events:
      - dst_ips:
          - "203.0.113.57"
        dports:
          - 443

This example starts a capsule whenever a flow going to 203.0.113.57:443 is found. It uses myDetectionScheme for the capsule and passes the parameter. Multiple start_events are alternatives: matching any one can start a capsule.

3. Schemes

A scheme consists of a manifest and a Lua script. A simple manifest may look like this:

name: myDetectionScheme
capsule_type: Detection
flow_name: DataFlow
parameters:
  - interval_duration_ms: int

The manifest declares the exact scheme name, capsule type, initial flow name, optional flow-reservation rules, and parameter schema. Use the case-sensitive capsule types Embedding or Detection. The policy name, manifest name, and Lua filename stem must match exactly.

The script must be in a file named after the scheme, so in this case myDetectionScheme.lua. Waterfall binds the triggering flow as DataFlow before the script starts; the script does not need to bind its initial flow itself.

local wf = require("waterfall")

local interval =
    wf.get_parameter("interval_duration_ms") * wf.MILLISECOND
local reports = 0

wf.monitor_packet_count(
    "report",
    interval,
    "DataFlow",
    function(count)
        print("Packets in interval: " .. count)
        reports = reports + 1

        if reports == 10 then
            wf.stop()
        end
    end
)

This detection capsule reports the packet count once per second and stops after ten reports. Each flow that satisfies the policy trigger receives its own capsule. More complex capsules can inspect the initial flow with wf.get_flows() and bind additional flows through the Lua API.

4. Running

Replace the placeholder config paths, interface, and policy address first. Then run this command from waterfall-framework/waterfall/cmd:

sudo ./waterfall -policy /path/to/the/policy.yaml

Waterfall now waits for traffic matching the policy. Once matching flows are found and reach the default minimum packet requirement, it starts one detection capsule per matching flow and starts printing packet-count reports. This example only observes traffic; modifying traffic requires an Embedding capsule.