Skip to main content

Bgp4Client

Bgp4Client is a layer-4 stream tunnel that wraps upstream payloads in BGP-like frames and unwraps downstream frames produced by Bgp4Server.

It is intended to be paired with Bgp4Server:

TcpListener -> Bgp4Client -> ... -> Bgp4Server -> TcpConnector

In a real deployment, the ... is usually a transport path between the two sides, such as TCP, TLS, mux, reverse, or another WaterWall chain segment.

warning

Bgp4Client does not implement real BGP routing, BGP peering, route exchange, or BGP authentication. It only uses a BGP-like frame shape as stream camouflage/framing.

What It Does​

  • Initializes one per-line read stream and first-frame state during upstream init.
  • On the first upstream payload, prepends a synthetic BGP OPEN body before the application bytes.
  • Wraps upstream payloads as BGP-like frames.
  • Chooses a random non-OPEN BGP type for later upstream payload frames.
  • Parses downstream BGP-like frames from a stream buffer.
  • Strips downstream frame metadata and forwards the recovered payload to the previous tunnel.
  • Closes both directions when it receives malformed framed data.

Bgp4Client is a normal stream tunnel. It does not create network sockets and does not create or destroy packet lines.

Configuration Example​

Client side:

{
"name": "bgp4-client",
"type": "Bgp4Client",
"settings": {
"password": "compat-value"
},
"next": "transport-out"
}

Paired server side:

{
"name": "bgp4-server",
"type": "Bgp4Server",
"settings": {
"password": "compat-value"
},
"next": "target"
}

The password field is accepted for compatibility with the old configuration shape, but it is not used for encryption or authentication in the current protocol logic.

Required Fields​

Top-level fields:

FieldTypeDescription
namestringUser-chosen node name. Must be unique inside the config file.
typestringMust be exactly "Bgp4Client".
nextstringThe next stream node that receives wrapped frames.

Optional Settings​

SettingTypeDefaultDescription
passwordstring—Accepted but ignored; it provides neither encryption nor authentication.

No other settings are currently parsed.

Frame Format​

All upstream payloads are wrapped as:

16-byte marker | 2-byte body length | 1-byte BGP type | body

Field details:

FieldValue
markerSixteen bytes of 0xff.
body lengthBig-endian uint16_t. Covers the type byte plus body bytes.
BGP type1 for the first OPEN frame; random 2 through 5 for later payload frames.
bodyTunneled payload, or synthetic OPEN body plus tunneled payload for the first upstream frame.

Supported type values used by the implementation:

TypeName
1OPEN
2UPDATE
3NOTIFICATION
4KEEPALIVE
5ROUTE-REFRESH

The frame length is not the standard BGP message length. In this tunnel it is the older WaterWall contract: type byte plus body bytes, excluding the 16-byte marker and 2-byte length field.

First Upstream Payload​

Bgp4Client does not send a standalone OPEN frame during init. Instead, the first upstream payload is framed as BGP type 1 and the client prepends a synthetic OPEN body before the application bytes.

Synthetic OPEN fields:

FieldBehavior
versionAlways 4.
AS numberRandom uint16_t generated when the tunnel instance is created.
hold time90 seconds.
router idRandom value generated when the tunnel instance is created.
optional parameter lengthRandom length from 3 through 10 bytes.
optional parameter bytesRandom bytes in the range 0 through 199.

After this first frame, open_sent is set for the line. Later upstream payloads are wrapped without the synthetic OPEN prefix and use a random non-OPEN type.

Because Bgp4Server expects tunneled application data after the OPEN prefix, the first OPEN frame must carry payload bytes after the synthetic OPEN body.

Downstream Parsing​

Downstream bytes are accumulated in a splice_stream_t.

For each complete downstream frame, Bgp4Client:

  1. Verifies that the first 16 bytes are all 0xff.
  2. Reads the 2-byte body length.
  3. Rejects frames whose body length is not larger than the 1-byte type field.
  4. Waits until the full frame is buffered.
  5. Strips marker, length, and type.
  6. Rejects frames with no remaining payload.
  7. Sends the recovered payload downstream to the previous tunnel.

If the frame is incomplete, parsing waits for more bytes. If the buffered data grows beyond the current maximum buffered threshold, the line is closed.

Limits​

LimitValueNotes
Maximum body length65535 bytesIncludes the 1-byte type.
Maximum ordinary payload body65534 bytesPayload plus type must fit in 65535.
Retained logical bytes per direction2162705Wire bytes, independent of pools and splice configuration.
Queued encoded frames1024Does not limit incoming fragment count.
Maximum complete wire frame65553 bytesMarker, length, type and body.
Required left padding39 bytesEnough for frame prefix plus largest synthetic OPEN prefix.

Both directions accept ordinary buffers, private-pipe splice buffers, and mixed fragments. A fitting delivery is framed in place using reserved left padding (39 bytes for the client, 19 for the server), including any existing resident prefix. Larger deliveries are prepared as complete batches of separate frame buffers before any callback; pipe creation or transfer pressure selects complete ordinary best-fit fallback for the affected frame. There is no whole-delivery ordinary aggregate.

The decoder caches the 19-byte marker/length/type header and extracts one exact body at a time. The server inspects only the ten fixed OPEN bytes and at most 255 optional bytes, preserving the remaining application's representation. The client generates 3–10 optional bytes and subtracts the actual OPEN overhead from its first frame's payload room. Empty deliveries emit nothing and do not consume OPEN state. Exactly one OPEN is admitted per client line.

The fixed logical retention limit is 2,162,705 bytes per direction (2 MiB + 65,553), independent of RAM profile, pools, pipe capacity, platform and misc.splice. Decoders count retained wire bytes, including the cached header; encoders count queued encoded bytes including generated headers. Encoded output also has a 1,024-frame FIFO cap. Incoming fragments have no entry-count cap. The maximum complete wire frame is 65,553 bytes, and a non-OPEN frame carries at most 65,534 application bytes.

Each decoded frame produces its own callback. Both nodes borrow the normal line and transform every payload, so neither can permanently bypass itself. Pause and Resume use transparent default forwarding in both directions, including repeated notifications. Complete admitted frames drain immediately without waiting for Resume. A pump guard per direction preserves FIFO during reentrant callbacks; the receive stream retains incomplete input, and the bounded output FIFO keeps complete encoded batches ahead of reentrant input. Each input is checked against the fixed retention limits before any callback for that input. Malformed data, retention-limit overflow or queue-capacity refusal closes only the borrowed connection through its owner. Finish releases queued buffers and pipes before forwarding away from the sender. These behaviors do not guarantee zero-copy delivery or a throughput improvement.

Data Flow​

Upstream direction:

previous node -> Bgp4Client wraps -> next node

Downstream direction:

next node -> Bgp4Client unwraps -> previous node

Bgp4Client expects Bgp4Server on the opposite side. A non-matching peer that does not produce the expected frame shape will cause protocol errors.

Lifecycle Behavior​

On upstream init, Bgp4Client initializes its line state and forwards upstream init to the next tunnel after creating its receive stream.

On downstream est, it forwards every notification to the previous tunnel, including repeats.

On upstream payload, it wraps and forwards to the next tunnel. On downstream payload, it buffers, parses, unwraps, and forwards recovered payloads to the previous tunnel.

On upstream finish, it destroys its own line state and forwards upstream finish to the next tunnel. On downstream finish, it destroys its own line state and forwards downstream finish to the previous tunnel.

On malformed framed data, Bgp4Client destroys local state and closes both directions in middle-tunnel order: upstream finish first, then downstream finish.

Pause and Resume are repeatable advisory notifications. Their default callbacks forward each notification immediately in the same direction without recording consumer pause state or gating payload output:

CallbackForwarded to
upstream pause/resumenext tunnel upstream
downstream pause/resumeprevious tunnel downstream

Notes and Caveats​

  • Bgp4Client requires a paired Bgp4Server for useful operation.
  • password is not a security feature in the current implementation.
  • The BGP-like length field excludes the marker and length bytes.
  • The first OPEN frame is sent only when the first upstream payload arrives.
  • Upstream est and downstream init are disabled callbacks.
  • This node is a layer-4 stream tunnel, not a packet tunnel.

Node Metadata​

Source-backed metadata:

PropertyValue
node flagskNodeFlagSupportsSplice
can_have_prevtrue
can_have_nexttrue
layer_groupkNodeLayer4
layer_group_prev_nodekNodeLayer4
layer_group_next_nodekNodeLayer4
required_padding_left39 bytes