# 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 ```go 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 ```go // 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: ```bash # 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: ```go 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)`: ```bash # 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 \ endpoint : \ 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: ```go interfaces := []string{"eth0", "wwan0"} // Ethernet + Cellular ``` ### WiFi + Ethernet Redundancy For laptops with both WiFi and Ethernet: ```go interfaces := []string{"eth0", "wlan0"} // Ethernet + WiFi ``` ### Multi-Homed Server Server with multiple network interfaces: ```go interfaces := []string{"eth0", "eth1", "eth2"} // Multiple Ethernet ``` ## Troubleshooting ### Interface Binding Issues ```bash # 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: ```bash sudo ./your-wireguard-program ``` ### Debugging Enable verbose logging to see multi-path operations: ```go logger := device.NewLogger(device.LogLevelVerbose, "multipath: ") ``` ## Building Ensure you have the modified WireGuard-Go source and build normally: ```bash 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