Files
wireguard-go/MULTIPATH.md
T
dingfeng.wong 9f0133a5c9 add
2025-07-25 18:01:53 +08:00

7.6 KiB

Multi-Path WireGuard Implementation

This document describes the multi-path networking feature for WireGuard-Go, which allows sending the same packet through multiple network interfaces simultaneously.

Overview

The multi-path implementation extends WireGuard-Go to support redundant packet transmission through multiple network paths. When configured, each outbound packet is sent through ALL specified network interfaces, providing:

  • Increased Reliability: If one network path fails, communication continues through other paths
  • Better Performance: Multiple paths can provide better throughput and lower latency
  • Redundancy: Critical for scenarios where network reliability is paramount

How It Works

Architecture

The multi-path functionality is implemented through several key components:

  1. MultiPathBind (conn/multipath_bind.go):

    • Implements the conn.Bind interface
    • Manages multiple underlying Bind instances
    • Sends packets through ALL configured network paths
    • Receives packets through the primary path only
  2. Multi-Path Device Creation (device/multipath.go):

    • Helper functions to create WireGuard devices with multiple network interfaces
    • Interface discovery and configuration utilities
  3. Network Transmission Flow:

    TUN Device → Peer Lookup → Packet Staging → Sequential Sender → 
    SendBuffers → MultiPathBind.Send() → [Bind1, Bind2, Bind3, ...] → Network
    

Code Locations

The actual network transmission happens in these key locations:

  • Primary Send Method: device/peer.go:135 - peer.device.net.bind.Send(buffers, endpoint)
  • Multi-Path Send: conn/multipath_bind.go:95 - Sends through all configured binds
  • Socket Transmission: conn/bind_std.go:339 - Individual socket transmission

Usage

Basic Usage

package main

import (
    "golang.zx2c4.com/wireguard/device"
    "golang.zx2c4.com/wireguard/tun"
)

func main() {
    // Create TUN device
    tunDevice, err := tun.CreateTUN("wg-multipath", 1420)
    if err != nil {
        panic(err)
    }
    defer tunDevice.Close()

    // Create logger
    logger := device.NewLogger(device.LogLevelVerbose, "multipath: ")

    // Create multi-path device using interface names
    interfaceNames := []string{"eth0", "wlan0"}
    wgDevice, err := device.NewMultiPathDeviceByNames(tunDevice, interfaceNames, logger)
    if err != nil {
        panic(err)
    }
    defer wgDevice.Close()

    // Device is ready - configure with wg(8) tools and bring up
    err = wgDevice.Up()
    if err != nil {
        panic(err)
    }

    // Now all outbound packets will be sent through both eth0 and wlan0
}

Advanced Configuration

// Using interface indexes instead of names
config := device.MultiPathConfig{
    InterfaceIndexes: []uint32{2, 3, 4}, // eth0, wlan0, usb0
    BindFactory: func() conn.Bind {
        return conn.NewStdNetBind() // or custom bind implementation
    },
}

wgDevice, err := device.NewMultiPathDevice(tunDevice, config, logger)

Command Line Example

Build and run the example program:

# Build the example
go build -o multipath-example ./examples/multipath/

# List available interfaces
sudo ./multipath-example

# Create multi-path tunnel using eth0 and wlan0
sudo ./multipath-example eth0 wlan0

Configuration

Interface Discovery

Use the helper function to discover available network interfaces:

interfaces, err := device.GetNetworkInterfaceInfo()
if err != nil {
    log.Fatal(err)
}

for _, iface := range interfaces {
    fmt.Printf("Interface: %s (index %d)\n", iface.Name, iface.Index)
    fmt.Printf("  Addresses: %v\n", iface.Addresses)
    fmt.Printf("  MTU: %d\n", iface.MTU)
}

WireGuard Configuration

After creating the multi-path device, configure it normally with wg(8):

# Generate keys
wg genkey | tee private.key | wg pubkey > public.key

# Configure the device
sudo wg set wg-multipath private-key private.key
sudo wg set wg-multipath peer <PEER_PUBLIC_KEY> \
    endpoint <PEER_IP>:<PORT> \
    allowed-ips 0.0.0.0/0

# Assign IP and bring up
sudo ip addr add 10.0.0.2/24 dev wg-multipath
sudo ip link set wg-multipath up

# Route traffic through the tunnel
sudo ip route add default dev wg-multipath

Technical Details

Packet Duplication

When MultiPathBind.Send() is called:

  1. The same packet buffers are sent through ALL configured network binds
  2. Each bind may be bound to a different network interface
  3. The method succeeds if at least one bind successfully sends the packet
  4. Errors from individual binds are logged but don't stop other binds

Receiving

  • Only the primary bind (first in the list) is used for receiving packets
  • This prevents duplicate packet reception
  • All receive functions come from the primary bind

Error Handling

  • Individual bind failures don't stop transmission through other binds
  • At least one successful transmission is required for overall success
  • Failed binds are logged for debugging

Performance Considerations

  • CPU Usage: Sending through multiple interfaces increases CPU usage proportionally
  • Memory: Each bind maintains its own buffers and state
  • Network Bandwidth: Total bandwidth usage is multiplied by the number of interfaces
  • Latency: Latency is determined by the fastest responding interface

Limitations

  1. Packet Duplication: Receiving peer will see duplicate packets (WireGuard's replay protection handles this)
  2. Bandwidth Usage: Network usage increases proportionally with number of interfaces
  3. Interface Binding: Requires platform support for binding sockets to specific interfaces
  4. Receive Path: Only receives through primary interface (no multi-path receiving)

Platform Support

The multi-path functionality works on platforms that support:

  • Socket binding to specific network interfaces
  • Multiple UDP sockets on the same port (with SO_REUSEPORT or similar)

Tested on:

  • Linux (fully supported)
  • macOS (limited support)
  • Windows (limited support)

Example Scenarios

Dual-WAN Setup

Use both your main internet connection and backup cellular connection:

interfaces := []string{"eth0", "wwan0"}  // Ethernet + Cellular

WiFi + Ethernet Redundancy

For laptops with both WiFi and Ethernet:

interfaces := []string{"eth0", "wlan0"}  // Ethernet + WiFi

Multi-Homed Server

Server with multiple network interfaces:

interfaces := []string{"eth0", "eth1", "eth2"}  // Multiple Ethernet

Troubleshooting

Interface Binding Issues

# Check interface exists and is up
ip link show eth0

# Check interface has IP address
ip addr show eth0

# Test basic connectivity
ping -I eth0 8.8.8.8

Permission Issues

Multi-path binding typically requires root privileges:

sudo ./your-wireguard-program

Debugging

Enable verbose logging to see multi-path operations:

logger := device.NewLogger(device.LogLevelVerbose, "multipath: ")

Building

Ensure you have the modified WireGuard-Go source and build normally:

go mod tidy
go build ./...

# Build example
go build -o multipath-example ./examples/multipath/

Future Enhancements

Potential improvements for the multi-path implementation:

  1. Load Balancing: Distribute packets across interfaces rather than duplicating
  2. Health Monitoring: Automatic detection and handling of failed interfaces
  3. Quality Metrics: Choose best interface based on latency/bandwidth measurements
  4. Receive Multi-Path: Receive from multiple interfaces and handle reordering
  5. Configuration API: Runtime configuration of interface sets