Aggligator

Your friendly link aggregator.

Aggligator combines the network links between two endpoints into one connection with their combined bandwidth. It serves the same purpose as Multipath TCP, but works in user space over ordinary TCP, TLS, WebSocket, USB and Bluetooth links, so neither the operating system nor the network in between needs to support it.

Links may fail, return or be added while the connection runs, so failover is automatic and the connection survives a device switching between Wi-Fi, mobile data and Ethernet, even when its IP address changes.

One connection, many links

How Aggligator aggregates links Two programs, each holding one byte stream. Between them run four links: Ethernet, Wi-Fi and USB, each carrying part of the same stream, and a Bluetooth link that has failed and carries nothing, its share taken over by the others. Local endpoint alc::Stream AsyncRead + AsyncWrite Remote endpoint alc::Stream AsyncRead + AsyncWrite Many links Ethernet Wi-Fi USB Bluetooth gone — its share moved to the others one connection, carried by every link that works TCP, TLS, WebSockets, USB or Bluetooth, mixed freely and changed while it runs
The application sees one byte stream at each end. Aggligator sends its data over the available links according to the capacity each one currently has. Failed links can be re-established and rejoin the same connection. If none remain, the connection waits while transports try to establish a new link.

Use it like a socket from Rust

The TCP transport discovers routes between the two hosts and uses each as a link. Both ends receive a stream implementing AsyncRead and AsyncWrite.

Client
use aggligator_transport_tcp::simple::tcp_connect;
use tokio::io::AsyncWriteExt;

// Connect to "server" over all network interfaces.
let mut stream = tcp_connect(["server"], 5900).await?;

stream.write_all(b"hello").await?;
Server
use aggligator_transport_tcp::simple::tcp_listen;

// Listen on all network interfaces.
let listener = tcp_listen("[::]:5900".parse()?).await?;

loop {
    // One connection, including all its links.
    let mut stream = listener.accept().await?;
}

A Connector combines different transport types in the same connection. This client can reach a device over the network and over USB at once.

Combining TCP and USB links
let mut connector = Connector::new();
connector.add(TcpConnector::new(["server"], 5900).await?);
connector.add(UsbConnector::new(|dev, _| dev.vendor_id() == 0x1209)?);

let stream = connector.channel().unwrap().await?.into_stream();

Connecting and accepting with custom transports →

Use Aggligator with existing TCP applications

The agg-tunnel utility carries ordinary TCP connections over Aggligator. Existing clients and servers can use aggregated links without changes to their code.

How to use agg-tunnel →

Features

Connection survives network changes

Links may fail, return, be added or be removed while the connection runs. When none remain, transport connectors keep reconnecting for a configurable time instead of immediately closing the logical connection.

Switching between Wi-Fi and mobile data →

Bring your own links

Aggligator does its link aggregation in user space, over ordinary ordered byte streams or packet streams, without multipath support from the operating system or network.

Transports →

Authenticated link attachment

A secret established with Diffie-Hellman protects the connection identifier used when adding links. Payload data is not encrypted; use TLS for untrusted links.

TLS wrapper →

Per-link statistics

Each link reports latency, throughput and unacknowledged data. Applications can consume the values directly or display them with the terminal monitor.

aggligator-monitor →

Safe Rust on native and WebAssembly

Aggligator contains no unsafe code, runs on Tokio and supports native platforms and WebAssembly.

Adapts to unequal links

Simple packet striping can let one slow link hold back an otherwise fast connection. Aggligator instead adjusts how much data is in flight on each link while the connection runs.

Send on the next ready link

Aggligator does not calculate a fixed bandwidth share. A faster link finishes sending sooner, becomes ready again and naturally carries more packets.

Limit links that delay delivery

Acknowledgements show how much received data is waiting behind an earlier packet. If that backlog grows, the sender reduces the unacknowledged-data allowance for the link used by the oldest outstanding packet.

Retest links set aside

When faster alternatives exist, a link with excessive round-trip time can be removed from normal scheduling. Test data and a ping periodically check whether it is ready to return.

Configuration and defaults →

Links of every kind

Transport crates create links for Aggligator. A connection can combine different transport types.

Additional crates are listed under the aggligator-transport keyword. Custom transports implement the transport API.
Carries links over Crate
TCP aggligator-transport-tcp
WebSockets aggligator-transport-websocket
WebSockets from a browser aggligator-transport-websocket-web
USB aggligator-transport-usb
WebUSB from a browser aggligator-transport-webusb
Bluetooth on Linux aggligator-transport-bluer
SOCKS5 proxies aggligator-transport-socks
Anything above, wrapped in TLS aggligator-wrapper-tls

Common uses

How to bond internet connections and keep them alive when the network changes.