Improved receive.go

- Fixed configuration listen-port semantics
- Improved receive.go code for updating listen port
- Updated under load detection, how follows the kernel space implementation
- Fixed trie bug accidentally introduced in last commit
- Added interface name to log (format still subject to change)
- Can now configure the logging level using the LOG_LEVEL variable
- Begin porting netsh.sh tests
- A number of smaller changes
This commit is contained in:
Mathias Hall-Andersen 2017-08-11 16:18:20 +02:00
parent cba1d6585a
commit a4eff12d7f
16 changed files with 616 additions and 218 deletions

View file

@ -28,6 +28,7 @@ func ipcGetOperation(device *Device, socket *bufio.ReadWriter) *IPCError {
// create lines
device.mutex.RLock()
device.net.mutex.RLock()
lines := make([]string, 0, 100)
send := func(line string) {
@ -38,7 +39,9 @@ func ipcGetOperation(device *Device, socket *bufio.ReadWriter) *IPCError {
send("private_key=" + device.privateKey.ToHex())
}
send(fmt.Sprintf("listen_port=%d", device.net.addr.Port))
if device.net.addr != nil {
send(fmt.Sprintf("listen_port=%d", device.net.addr.Port))
}
for _, peer := range device.peers {
func() {
@ -68,6 +71,7 @@ func ipcGetOperation(device *Device, socket *bufio.ReadWriter) *IPCError {
}()
}
device.net.mutex.RUnlock()
device.mutex.RUnlock()
// send lines
@ -84,38 +88,6 @@ func ipcGetOperation(device *Device, socket *bufio.ReadWriter) *IPCError {
return nil
}
func updateUDPConn(device *Device) error {
var err error
netc := &device.net
netc.mutex.Lock()
// close existing connection
if netc.conn != nil {
netc.conn.Close()
netc.conn = nil
}
// open new existing connection
conn, err := net.ListenUDP("udp", netc.addr)
if err == nil {
netc.conn = conn
signalSend(device.signal.newUDPConn)
}
netc.mutex.Unlock()
return err
}
func closeUDPConn(device *Device) {
device.net.mutex.Lock()
device.net.conn = nil
device.net.mutex.Unlock()
println("send signal")
signalSend(device.signal.newUDPConn)
}
func ipcSetOperation(device *Device, socket *bufio.ReadWriter) *IPCError {
scanner := bufio.NewScanner(socket)
logInfo := device.log.Info
@ -166,13 +138,22 @@ func ipcSetOperation(device *Device, socket *bufio.ReadWriter) *IPCError {
logError.Println("Failed to set listen_port:", err)
return &IPCError{Code: ipcErrorInvalid}
}
addr, err := net.ResolveUDPAddr("udp", fmt.Sprintf(":%d", port))
if err != nil {
logError.Println("Failed to set listen_port:", err)
return &IPCError{Code: ipcErrorInvalid}
}
netc := &device.net
netc.mutex.Lock()
if netc.addr.Port != int(port) {
netc.addr.Port = int(port)
}
netc.addr = addr
netc.mutex.Unlock()
updateUDPConn(device)
err = updateUDPConn(device)
if err != nil {
logError.Println("Failed to set listen_port:", err)
return &IPCError{Code: ipcErrorIO}
}
// TODO: Clear source address of all peers
@ -298,7 +279,7 @@ func ipcSetOperation(device *Device, socket *bufio.ReadWriter) *IPCError {
logError.Println("Failed to get tun device status:", err)
return &IPCError{Code: ipcErrorIO}
}
if atomic.LoadInt32(&device.isUp) == AtomicTrue && !dummy {
if device.tun.isUp.Get() && !dummy {
peer.SendKeepAlive()
}
}

40
src/conn.go Normal file
View file

@ -0,0 +1,40 @@
package main
import (
"net"
)
func updateUDPConn(device *Device) error {
var err error
netc := &device.net
netc.mutex.Lock()
// close existing connection
if netc.conn != nil {
netc.conn.Close()
}
// open new connection
if device.tun.isUp.Get() {
conn, err := net.ListenUDP("udp", netc.addr)
if err == nil {
netc.conn = conn
signalSend(device.signal.newUDPConn)
}
}
netc.mutex.Unlock()
return err
}
func closeUDPConn(device *Device) {
netc := &device.net
netc.mutex.Lock()
if netc.conn != nil {
netc.conn.Close()
}
netc.mutex.Unlock()
signalSend(device.signal.newUDPConn)
}

View file

@ -26,11 +26,15 @@ const (
/* Implementation specific constants */
const (
QueueOutboundSize = 1024
QueueInboundSize = 1024
QueueHandshakeSize = 1024
QueueHandshakeBusySize = QueueHandshakeSize / 8
MinMessageSize = MessageTransportSize // size of keep-alive
MaxMessageSize = ((1 << 16) - 1) + MessageTransportHeaderSize
MaxPeers = 1 << 16
QueueOutboundSize = 1024
QueueInboundSize = 1024
QueueHandshakeSize = 1024
MinMessageSize = MessageTransportSize // size of keep-alive
MaxMessageSize = ((1 << 16) - 1) + MessageTransportHeaderSize
MaxPeers = 1 << 16
)
const (
UnderLoadQueueSize = QueueHandshakeSize / 8
UnderLoadAfterTime = time.Second // how long does the device remain under load after detected
)

View file

@ -5,20 +5,22 @@ import (
"runtime"
"sync"
"sync/atomic"
"time"
)
type Device struct {
mtu int32
tun TUNDevice
log *Logger // collection of loggers for levels
idCounter uint // for assigning debug ids to peers
fwMark uint32
pool struct {
// pools objects for reuse
tun struct {
device TUNDevice
isUp AtomicBool
mtu int32
}
pool struct {
messageBuffers sync.Pool
}
net struct {
// seperate for performance reasons
mutex sync.RWMutex
addr *net.UDPAddr // UDP source address
conn *net.UDPConn // UDP "connection"
@ -35,13 +37,12 @@ type Device struct {
}
signal struct {
stop chan struct{} // halts all go routines
newUDPConn chan struct{} // a net.conn was set
newUDPConn chan struct{} // a net.conn was set (consumed by the receiver routine)
}
isUp int32 // atomic bool: interface is up
underLoad int32 // atomic bool: device is under load
ratelimiter Ratelimiter
peers map[NoisePublicKey]*Peer
mac MACStateDevice
underLoadUntil atomic.Value
ratelimiter Ratelimiter
peers map[NoisePublicKey]*Peer
mac MACStateDevice
}
/* Warning:
@ -58,6 +59,23 @@ func removePeerUnsafe(device *Device, key NoisePublicKey) {
peer.Close()
}
func (device *Device) IsUnderLoad() bool {
// check if currently under load
now := time.Now()
underLoad := len(device.queue.handshake) >= UnderLoadQueueSize
if underLoad {
device.underLoadUntil.Store(now.Add(time.Second))
return true
}
// check if recently under load
until := device.underLoadUntil.Load().(time.Time)
return until.After(now)
}
func (device *Device) SetPrivateKey(sk NoisePrivateKey) error {
device.mutex.Lock()
defer device.mutex.Unlock()
@ -115,20 +133,13 @@ func NewDevice(tun TUNDevice, logLevel int) *Device {
device.mutex.Lock()
defer device.mutex.Unlock()
device.tun = tun
device.log = NewLogger(logLevel)
device.log = NewLogger(logLevel, "("+tun.Name()+") ")
device.peers = make(map[NoisePublicKey]*Peer)
device.tun.device = tun
device.indices.Init()
device.ratelimiter.Init()
device.routingTable.Reset()
// listen
device.net.mutex.Lock()
device.net.conn, _ = net.ListenUDP("udp", device.net.addr)
addr := device.net.conn.LocalAddr()
device.net.addr, _ = net.ResolveUDPAddr(addr.Network(), addr.String())
device.net.mutex.Unlock()
device.underLoadUntil.Store(time.Time{})
// setup pools
@ -157,42 +168,43 @@ func NewDevice(tun TUNDevice, logLevel int) *Device {
go device.RoutineHandshake()
}
go device.RoutineBusyMonitor()
go device.RoutineReadFromTUN()
go device.RoutineTUNEventReader()
go device.RoutineReceiveIncomming()
go device.ratelimiter.RoutineGarbageCollector(device.signal.stop)
go device.RoutineReadFromTUN()
go device.RoutineReceiveIncomming()
return device
}
func (device *Device) RoutineTUNEventReader() {
events := device.tun.Events()
logInfo := device.log.Info
logError := device.log.Error
events := device.tun.device.Events()
for event := range events {
if event&TUNEventMTUUpdate != 0 {
mtu, err := device.tun.MTU()
mtu, err := device.tun.device.MTU()
if err != nil {
logError.Println("Failed to load updated MTU of device:", err)
} else {
if mtu+MessageTransportSize > MaxMessageSize {
mtu = MaxMessageSize - MessageTransportSize
}
atomic.StoreInt32(&device.mtu, int32(mtu))
atomic.StoreInt32(&device.tun.mtu, int32(mtu))
}
}
if event&TUNEventUp != 0 {
println("handle 1")
atomic.StoreInt32(&device.isUp, AtomicTrue)
device.tun.isUp.Set(true)
updateUDPConn(device)
println("handle 2", device.net.conn)
logInfo.Println("Interface set up")
}
if event&TUNEventDown != 0 {
atomic.StoreInt32(&device.isUp, AtomicFalse)
device.tun.isUp.Set(false)
closeUDPConn(device)
logInfo.Println("Interface set down")
}
}
}
@ -224,6 +236,7 @@ func (device *Device) RemoveAllPeers() {
func (device *Device) Close() {
device.RemoveAllPeers()
close(device.signal.stop)
closeUDPConn(device)
}
func (device *Device) WaitChannel() chan struct{} {

View file

@ -12,6 +12,7 @@ type DummyTUN struct {
name string
mtu int
packets chan []byte
events chan TUNEvent
}
func (tun *DummyTUN) Name() string {
@ -27,6 +28,14 @@ func (tun *DummyTUN) Write(d []byte) (int, error) {
return len(d), nil
}
func (tun *DummyTUN) Close() error {
return nil
}
func (tun *DummyTUN) Events() chan TUNEvent {
return tun.events
}
func (tun *DummyTUN) Read(d []byte) (int, error) {
t := <-tun.packets
copy(d, t)

View file

@ -2,8 +2,8 @@ package main
import (
"crypto/rand"
"encoding/binary"
"sync"
"unsafe"
)
/* Index=0 is reserved for unset indecies
@ -24,7 +24,8 @@ type IndexTable struct {
func randUint32() (uint32, error) {
var buff [4]byte
_, err := rand.Read(buff[:])
return *((*uint32)(unsafe.Pointer(&buff))), err
value := binary.LittleEndian.Uint32(buff[:])
return value, err
}
func (table *IndexTable) Init() {

View file

@ -19,7 +19,7 @@ type Logger struct {
Error *log.Logger
}
func NewLogger(level int) *Logger {
func NewLogger(level int, prepend string) *Logger {
output := os.Stdout
logger := new(Logger)
@ -34,16 +34,16 @@ func NewLogger(level int) *Logger {
}()
logger.Debug = log.New(logDebug,
"DEBUG: ",
"DEBUG: "+prepend,
log.Ldate|log.Ltime|log.Lshortfile,
)
logger.Info = log.New(logInfo,
"INFO: ",
"INFO: "+prepend,
log.Ldate|log.Ltime,
)
logger.Error = log.New(logErr,
"ERROR: ",
"ERROR: "+prepend,
log.Ldate|log.Ltime,
)
return logger

View file

@ -13,8 +13,8 @@ func TestMAC1(t *testing.T) {
defer dev1.Close()
defer dev2.Close()
peer1 := dev2.NewPeer(dev1.privateKey.publicKey())
peer2 := dev1.NewPeer(dev2.privateKey.publicKey())
peer1, _ := dev2.NewPeer(dev1.privateKey.publicKey())
peer2, _ := dev1.NewPeer(dev2.privateKey.publicKey())
assertEqual(t, peer1.mac.keyMAC1[:], dev1.mac.keyMAC1[:])
assertEqual(t, peer2.mac.keyMAC1[:], dev2.mac.keyMAC1[:])
@ -45,8 +45,8 @@ func TestMACs(t *testing.T) {
defer device1.Close()
defer device2.Close()
peer1 := device2.NewPeer(device1.privateKey.publicKey())
peer2 := device1.NewPeer(device2.privateKey.publicKey())
peer1, _ := device2.NewPeer(device1.privateKey.publicKey())
peer2, _ := device1.NewPeer(device2.privateKey.publicKey())
if addr.Port < 0 {
return true

View file

@ -65,9 +65,23 @@ func main() {
return
}
// get log level (default: info)
logLevel := func() int {
switch os.Getenv("LOG_LEVEL") {
case "debug":
return LogLevelDebug
case "info":
return LogLevelInfo
case "error":
return LogLevelError
}
return LogLevelInfo
}()
// create wireguard device
device := NewDevice(tun, LogLevelDebug)
device := NewDevice(tun, logLevel)
logInfo := device.log.Info
logError := device.log.Error

View file

@ -1,6 +1,7 @@
package main
import (
"sync/atomic"
"time"
)
@ -8,10 +9,26 @@ import (
* (since booleans are not natively supported by sync/atomic)
*/
const (
AtomicFalse = iota
AtomicFalse = int32(iota)
AtomicTrue
)
type AtomicBool struct {
flag int32
}
func (a *AtomicBool) Get() bool {
return atomic.LoadInt32(&a.flag) == AtomicTrue
}
func (a *AtomicBool) Set(val bool) {
flag := AtomicFalse
if val {
flag = AtomicTrue
}
atomic.StoreInt32(&a.flag, flag)
}
func min(a uint, b uint) uint {
if a > b {
return b

View file

@ -31,8 +31,8 @@ func TestNoiseHandshake(t *testing.T) {
defer dev1.Close()
defer dev2.Close()
peer1 := dev2.NewPeer(dev1.privateKey.publicKey())
peer2 := dev1.NewPeer(dev2.privateKey.publicKey())
peer1, _ := dev2.NewPeer(dev1.privateKey.publicKey())
peer2, _ := dev1.NewPeer(dev2.privateKey.publicKey())
assertEqual(
t,

View file

@ -72,43 +72,6 @@ func (device *Device) addToHandshakeQueue(
}
}
/* Routine determining the busy state of the interface
*
* TODO: Under load for some time
*/
func (device *Device) RoutineBusyMonitor() {
samples := 0
interval := time.Second
for timer := time.NewTimer(interval); ; {
select {
case <-device.signal.stop:
return
case <-timer.C:
}
// compute busy heuristic
if len(device.queue.handshake) > QueueHandshakeBusySize {
samples += 1
} else if samples > 0 {
samples -= 1
}
samples %= 30
busy := samples > 5
// update busy state
if busy {
atomic.StoreInt32(&device.underLoad, AtomicTrue)
} else {
atomic.StoreInt32(&device.underLoad, AtomicFalse)
}
timer.Reset(interval)
}
}
func (device *Device) RoutineReceiveIncomming() {
logDebug := device.log.Debug
@ -118,117 +81,121 @@ func (device *Device) RoutineReceiveIncomming() {
// wait for new conn
var conn *net.UDPConn
logDebug.Println("Waiting for udp socket")
select {
case <-device.signal.newUDPConn:
device.net.mutex.RLock()
conn = device.net.conn
device.net.mutex.RUnlock()
case <-device.signal.stop:
return
}
if conn == nil {
continue
}
case <-device.signal.newUDPConn:
// receive datagrams until closed
// fetch connection
buffer := device.GetMessageBuffer()
for {
// read next datagram
size, raddr, err := conn.ReadFromUDP(buffer[:]) // TODO: This is broken
if err != nil {
break
}
if size < MinMessageSize {
device.net.mutex.RLock()
conn := device.net.conn
device.net.mutex.RUnlock()
if conn == nil {
continue
}
// check size of packet
logDebug.Println("Listening for inbound packets")
packet := buffer[:size]
msgType := binary.LittleEndian.Uint32(packet[:4])
// receive datagrams until conn is closed
var okay bool
buffer := device.GetMessageBuffer()
switch msgType {
for {
// check if transport
// read next datagram
case MessageTransportType:
size, raddr, err := conn.ReadFromUDP(buffer[:]) // Blocks sometimes
// check size
if err != nil {
break
}
if len(packet) < MessageTransportType {
if size < MinMessageSize {
continue
}
// lookup key pair
// check size of packet
receiver := binary.LittleEndian.Uint32(
packet[MessageTransportOffsetReceiver:MessageTransportOffsetCounter],
)
value := device.indices.Lookup(receiver)
keyPair := value.keyPair
if keyPair == nil {
continue
}
packet := buffer[:size]
msgType := binary.LittleEndian.Uint32(packet[:4])
// check key-pair expiry
var okay bool
if keyPair.created.Add(RejectAfterTime).Before(time.Now()) {
continue
}
switch msgType {
// create work element
// check if transport
peer := value.peer
elem := &QueueInboundElement{
packet: packet,
buffer: buffer,
keyPair: keyPair,
dropped: AtomicFalse,
}
elem.mutex.Lock()
case MessageTransportType:
// add to decryption queues
// check size
device.addToInboundQueue(device.queue.decryption, elem)
device.addToInboundQueue(peer.queue.inbound, elem)
buffer = nil
continue
if len(packet) < MessageTransportType {
continue
}
// otherwise it is a handshake related packet
// lookup key pair
case MessageInitiationType:
okay = len(packet) == MessageInitiationSize
receiver := binary.LittleEndian.Uint32(
packet[MessageTransportOffsetReceiver:MessageTransportOffsetCounter],
)
value := device.indices.Lookup(receiver)
keyPair := value.keyPair
if keyPair == nil {
continue
}
case MessageResponseType:
okay = len(packet) == MessageResponseSize
// check key-pair expiry
case MessageCookieReplyType:
okay = len(packet) == MessageCookieReplySize
}
if keyPair.created.Add(RejectAfterTime).Before(time.Now()) {
continue
}
if okay {
device.addToHandshakeQueue(
device.queue.handshake,
QueueHandshakeElement{
msgType: msgType,
buffer: buffer,
// create work element
peer := value.peer
elem := &QueueInboundElement{
packet: packet,
source: raddr,
},
)
buffer = device.GetMessageBuffer()
buffer: buffer,
keyPair: keyPair,
dropped: AtomicFalse,
}
elem.mutex.Lock()
// add to decryption queues
device.addToInboundQueue(device.queue.decryption, elem)
device.addToInboundQueue(peer.queue.inbound, elem)
buffer = device.GetMessageBuffer()
continue
// otherwise it is a handshake related packet
case MessageInitiationType:
okay = len(packet) == MessageInitiationSize
case MessageResponseType:
okay = len(packet) == MessageResponseSize
case MessageCookieReplyType:
okay = len(packet) == MessageCookieReplySize
}
if okay {
device.addToHandshakeQueue(
device.queue.handshake,
QueueHandshakeElement{
msgType: msgType,
buffer: buffer,
packet: packet,
source: raddr,
},
)
buffer = device.GetMessageBuffer()
}
}
}
}
@ -326,10 +293,11 @@ func (device *Device) RoutineHandshake() {
return
}
busy := atomic.LoadInt32(&device.underLoad) == AtomicTrue
if busy {
if device.IsUnderLoad() {
if !device.mac.CheckMAC2(elem.packet, elem.source) {
// construct cookie reply
sender := binary.LittleEndian.Uint32(elem.packet[4:8]) // "sender" always follows "type"
reply, err := device.CreateMessageCookieReply(elem.packet, sender, elem.source)
if err != nil {
@ -347,6 +315,7 @@ func (device *Device) RoutineHandshake() {
}
continue
}
if !device.ratelimiter.Allow(elem.source.IP) {
continue
}
@ -577,7 +546,7 @@ func (peer *Peer) RoutineSequentialReceiver() {
// write to tun
atomic.AddUint64(&peer.stats.rxBytes, uint64(len(elem.packet)))
_, err := device.tun.Write(elem.packet)
_, err := device.tun.device.Write(elem.packet)
device.PutMessageBuffer(elem.buffer)
if err != nil {
logError.Println("Failed to write packet to TUN device:", err)

View file

@ -137,10 +137,6 @@ func (peer *Peer) SendBuffer(buffer []byte) (int, error) {
*/
func (device *Device) RoutineReadFromTUN() {
if device.tun == nil {
return
}
var elem *QueueOutboundElement
logDebug := device.log.Debug
@ -155,9 +151,8 @@ func (device *Device) RoutineReadFromTUN() {
elem = device.NewOutboundElement()
}
// TODO: THIS!
elem.packet = elem.buffer[MessageTransportHeaderSize:]
size, err := device.tun.Read(elem.packet)
size, err := device.tun.device.Read(elem.packet)
if err != nil {
logError.Println("Failed to read packet from TUN device:", err)
device.Close()
@ -345,7 +340,7 @@ func (device *Device) RoutineEncryption() {
// pad content to MTU size
mtu := int(atomic.LoadInt32(&device.mtu))
mtu := int(atomic.LoadInt32(&device.tun.mtu))
pad := len(elem.packet) % PaddingMultiple
if pad > 0 {
for i := 0; i < PaddingMultiple-pad && len(elem.packet) < mtu; i++ {

350
src/tests/netns.sh Executable file
View file

@ -0,0 +1,350 @@
#!/bin/bash
# Copyright (C) 2015-2017 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
# This script tests the below topology:
#
# ┌─────────────────────┐ ┌──────────────────────────────────┐ ┌─────────────────────┐
# │ $ns1 namespace │ │ $ns0 namespace │ │ $ns2 namespace │
# │ │ │ │ │ │
# │┌────────┐ │ │ ┌────────┐ │ │ ┌────────┐│
# ││ wg1 │───────────┼───┼────────────│ lo │────────────┼───┼───────────│ wg2 ││
# │├────────┴──────────┐│ │ ┌───────┴────────┴────────┐ │ │┌──────────┴────────┤│
# ││192.168.241.1/24 ││ │ │(ns1) (ns2) │ │ ││192.168.241.2/24 ││
# ││fd00::1/24 ││ │ │127.0.0.1:1 127.0.0.1:2│ │ ││fd00::2/24 ││
# │└───────────────────┘│ │ │[::]:1 [::]:2 │ │ │└───────────────────┘│
# └─────────────────────┘ │ └─────────────────────────┘ │ └─────────────────────┘
# └──────────────────────────────────┘
#
# After the topology is prepared we run a series of TCP/UDP iperf3 tests between the
# wireguard peers in $ns1 and $ns2. Note that $ns0 is the endpoint for the wg1
# interfaces in $ns1 and $ns2. See https://www.wireguard.com/netns/ for further
# details on how this is accomplished.
set -e
exec 3>&1
export WG_HIDE_KEYS=never
netns0="wg-test-$$-0"
netns1="wg-test-$$-1"
netns2="wg-test-$$-2"
program="../wireguard-go"
export LOG_LEVEL="debug"
pretty() { echo -e "\x1b[32m\x1b[1m[+] ${1:+NS$1: }${2}\x1b[0m" >&3; }
pp() { pretty "" "$*"; "$@"; }
maybe_exec() { if [[ $BASHPID -eq $$ ]]; then "$@"; else exec "$@"; fi; }
n0() { pretty 0 "$*"; maybe_exec ip netns exec $netns0 "$@"; }
n1() { pretty 1 "$*"; maybe_exec ip netns exec $netns1 "$@"; }
n2() { pretty 2 "$*"; maybe_exec ip netns exec $netns2 "$@"; }
ip0() { pretty 0 "ip $*"; ip -n $netns0 "$@"; }
ip1() { pretty 1 "ip $*"; ip -n $netns1 "$@"; }
ip2() { pretty 2 "ip $*"; ip -n $netns2 "$@"; }
sleep() { read -t "$1" -N 0 || true; }
waitiperf() { pretty "${1//*-}" "wait for iperf:5201"; while [[ $(ss -N "$1" -tlp 'sport = 5201') != *iperf3* ]]; do sleep 0.1; done; }
waitncatudp() { pretty "${1//*-}" "wait for udp:1111"; while [[ $(ss -N "$1" -ulp 'sport = 1111') != *ncat* ]]; do sleep 0.1; done; }
waitiface() { pretty "${1//*-}" "wait for $2 to come up"; ip netns exec "$1" bash -c "while [[ \$(< \"/sys/class/net/$2/operstate\") != up ]]; do read -t .1 -N 0 || true; done;"; }
cleanup() {
n0 wg show
set +e
exec 2>/dev/null
printf "$orig_message_cost" > /proc/sys/net/core/message_cost
ip0 link del dev wg1
ip1 link del dev wg1
ip2 link del dev wg1
local to_kill="$(ip netns pids $netns0) $(ip netns pids $netns1) $(ip netns pids $netns2)"
[[ -n $to_kill ]] && kill $to_kill
pp ip netns del $netns1
pp ip netns del $netns2
pp ip netns del $netns0
exit
}
orig_message_cost="$(< /proc/sys/net/core/message_cost)"
trap cleanup EXIT
printf 0 > /proc/sys/net/core/message_cost
ip netns del $netns0 2>/dev/null || true
ip netns del $netns1 2>/dev/null || true
ip netns del $netns2 2>/dev/null || true
pp ip netns add $netns0
pp ip netns add $netns1
pp ip netns add $netns2
ip0 link set up dev lo
# ip0 link add dev wg1 type wireguard
n0 $program -f wg1 &
sleep 1
ip0 link set wg1 netns $netns1
# ip0 link add dev wg1 type wireguard
n0 $program -f wg2 &
sleep 1
ip0 link set wg2 netns $netns2
key1="$(pp wg genkey)"
key2="$(pp wg genkey)"
pub1="$(pp wg pubkey <<<"$key1")"
pub2="$(pp wg pubkey <<<"$key2")"
psk="$(pp wg genpsk)"
[[ -n $key1 && -n $key2 && -n $psk ]]
configure_peers() {
ip1 addr add 192.168.241.1/24 dev wg1
ip1 addr add fd00::1/24 dev wg1
ip2 addr add 192.168.241.2/24 dev wg2
ip2 addr add fd00::2/24 dev wg2
n0 wg set wg1 \
private-key <(echo "$key1") \
listen-port 10000 \
peer "$pub2" \
preshared-key <(echo "$psk") \
allowed-ips 192.168.241.2/32,fd00::2/128
n0 wg set wg2 \
private-key <(echo "$key2") \
listen-port 20000 \
peer "$pub1" \
preshared-key <(echo "$psk") \
allowed-ips 192.168.241.1/32,fd00::1/128
n0 wg showconf wg1
n0 wg showconf wg2
ip1 link set up dev wg1
ip2 link set up dev wg2
}
configure_peers
tests() {
# Ping over IPv4
n2 ping -c 10 -f -W 1 192.168.241.1
n1 ping -c 10 -f -W 1 192.168.241.2
# Ping over IPv6
n2 ping6 -c 10 -f -W 1 fd00::1
n1 ping6 -c 10 -f -W 1 fd00::2
# TCP over IPv4
n2 iperf3 -s -1 -B 192.168.241.2 &
waitiperf $netns2
n1 iperf3 -Z -n 1G -c 192.168.241.2
# TCP over IPv6
n1 iperf3 -s -1 -B fd00::1 &
waitiperf $netns1
n2 iperf3 -Z -n 1G -c fd00::1
# UDP over IPv4
n1 iperf3 -s -1 -B 192.168.241.1 &
waitiperf $netns1
n2 iperf3 -Z -n 1G -b 0 -u -c 192.168.241.1
# UDP over IPv6
n2 iperf3 -s -1 -B fd00::2 &
waitiperf $netns2
n1 iperf3 -Z -n 1G -b 0 -u -c fd00::2
}
[[ $(ip1 link show dev wg1) =~ mtu\ ([0-9]+) ]] && orig_mtu="${BASH_REMATCH[1]}"
big_mtu=$(( 34816 - 1500 + $orig_mtu ))
# Test using IPv4 as outer transport
n0 wg set wg1 peer "$pub2" endpoint 127.0.0.1:20000
n0 wg set wg2 peer "$pub1" endpoint 127.0.0.1:10000
n0 wg show
# Before calling tests, we first make sure that the stats counters are working
n2 ping -c 10 -f -W 1 192.168.241.1
{ read _; read _; read _; read rx_bytes _; read _; read tx_bytes _; } < <(ip2 -stats link show dev wg2)
[[ $rx_bytes -ge 932 && $tx_bytes -ge 1516 && $rx_bytes -lt 2500 && $rx_bytes -lt 2500 ]]
tests
ip1 link set wg1 mtu $big_mtu
ip2 link set wg2 mtu $big_mtu
tests
ip1 link set wg1 mtu $orig_mtu
ip2 link set wg2 mtu $orig_mtu
# Test using IPv6 as outer transport
n0 wg set wg1 peer "$pub2" endpoint [::1]:20000
n0 wg set wg2 peer "$pub1" endpoint [::1]:10000
tests
ip1 link set wg1 mtu $big_mtu
ip2 link set wg2 mtu $big_mtu
tests
ip1 link set wg1 mtu $orig_mtu
ip2 link set wg2 mtu $orig_mtu
# Test using IPv4 that roaming works
ip0 -4 addr del 127.0.0.1/8 dev lo
ip0 -4 addr add 127.212.121.99/8 dev lo
n0 wg set wg1 listen-port 9999
n0 wg set wg1 peer "$pub2" endpoint 127.0.0.1:20000
n1 ping6 -W 1 -c 1 fd00::20000
[[ $(n2 wg show wg2 endpoints) == "$pub1 127.212.121.99:9999" ]]
# Test using IPv6 that roaming works
n1 wg set wg1 listen-port 9998
n1 wg set wg1 peer "$pub2" endpoint [::1]:20000
n1 ping -W 1 -c 1 192.168.241.2
[[ $(n2 wg show wg2 endpoints) == "$pub1 [::1]:9998" ]]
# Test that crypto-RP filter works
n1 wg set wg1 peer "$pub2" allowed-ips 192.168.241.0/24
exec 4< <(n1 ncat -l -u -p 1111)
nmap_pid=$!
waitncatudp $netns1
n2 ncat -u 192.168.241.1 1111 <<<"X"
read -r -N 1 -t 1 out <&4 && [[ $out == "X" ]]
kill $nmap_pid
more_specific_key="$(pp wg genkey | pp wg pubkey)"
n0 wg set wg1 peer "$more_specific_key" allowed-ips 192.168.241.2/32
n0 wg set wg2 listen-port 9997
exec 4< <(n1 ncat -l -u -p 1111)
nmap_pid=$!
waitncatudp $netns1
n2 ncat -u 192.168.241.1 1111 <<<"X"
! read -r -N 1 -t 1 out <&4
kill $nmap_pid
n0 wg set wg1 peer "$more_specific_key" remove
[[ $(n1 wg show wg1 endpoints) == "$pub2 [::1]:9997" ]]
ip1 link del wg1
ip2 link del wg2
# Test using NAT. We now change the topology to this:
# ┌────────────────────────────────────────┐ ┌────────────────────────────────────────────────┐ ┌────────────────────────────────────────┐
# │ $ns1 namespace │ │ $ns0 namespace │ │ $ns2 namespace │
# │ │ │ │ │ │
# │ ┌─────┐ ┌─────┐ │ │ ┌──────┐ ┌──────┐ │ │ ┌─────┐ ┌─────┐ │
# │ │ wg1 │─────────────│vethc│───────────┼────┼────│vethrc│ │vethrs│──────────────┼─────┼──│veths│────────────│ wg2 │ │
# │ ├─────┴──────────┐ ├─────┴──────────┐│ │ ├──────┴─────────┐ ├──────┴────────────┐ │ │ ├─────┴──────────┐ ├─────┴──────────┐ │
# │ │192.168.241.1/24│ │192.168.1.100/24││ │ │192.168.1.100/24│ │10.0.0.1/24 │ │ │ │10.0.0.100/24 │ │192.168.241.2/24│ │
# │ │fd00::1/24 │ │ ││ │ │ │ │SNAT:192.168.1.0/24│ │ │ │ │ │fd00::2/24 │ │
# │ └────────────────┘ └────────────────┘│ │ └────────────────┘ └───────────────────┘ │ │ └────────────────┘ └────────────────┘ │
# └────────────────────────────────────────┘ └────────────────────────────────────────────────┘ └────────────────────────────────────────┘
# ip1 link add dev wg1 type wireguard
# ip2 link add dev wg1 type wireguard
n1 $program wg1
n2 $program wg2
configure_peers
ip0 link add vethrc type veth peer name vethc
ip0 link add vethrs type veth peer name veths
ip0 link set vethc netns $netns1
ip0 link set veths netns $netns2
ip0 link set vethrc up
ip0 link set vethrs up
ip0 addr add 192.168.1.1/24 dev vethrc
ip0 addr add 10.0.0.1/24 dev vethrs
ip1 addr add 192.168.1.100/24 dev vethc
ip1 link set vethc up
ip1 route add default via 192.168.1.1
ip2 addr add 10.0.0.100/24 dev veths
ip2 link set veths up
waitiface $netns0 vethrc
waitiface $netns0 vethrs
waitiface $netns1 vethc
waitiface $netns2 veths
n0 bash -c 'printf 1 > /proc/sys/net/ipv4/ip_forward'
n0 bash -c 'printf 2 > /proc/sys/net/netfilter/nf_conntrack_udp_timeout'
n0 bash -c 'printf 2 > /proc/sys/net/netfilter/nf_conntrack_udp_timeout_stream'
n0 iptables -t nat -A POSTROUTING -s 192.168.1.0/24 -d 10.0.0.0/24 -j SNAT --to 10.0.0.1
n0 wg set wg1 peer "$pub2" endpoint 10.0.0.100:20000 persistent-keepalive 1
n1 ping -W 1 -c 1 192.168.241.2
n2 ping -W 1 -c 1 192.168.241.1
[[ $(n2 wg show wg2 endpoints) == "$pub1 10.0.0.1:10000" ]]
# Demonstrate n2 can still send packets to n1, since persistent-keepalive will prevent connection tracking entry from expiring (to see entries: `n0 conntrack -L`).
pp sleep 3
n2 ping -W 1 -c 1 192.168.241.1
n0 iptables -t nat -F
ip0 link del vethrc
ip0 link del vethrs
ip1 link del wg1
ip2 link del wg2
# Test that saddr routing is sticky but not too sticky, changing to this topology:
# ┌────────────────────────────────────────┐ ┌────────────────────────────────────────┐
# │ $ns1 namespace │ │ $ns2 namespace │
# │ │ │ │
# │ ┌─────┐ ┌─────┐ │ │ ┌─────┐ ┌─────┐ │
# │ │ wg1 │─────────────│veth1│───────────┼────┼──│veth2│────────────│ wg2 │ │
# │ ├─────┴──────────┐ ├─────┴──────────┐│ │ ├─────┴──────────┐ ├─────┴──────────┐ │
# │ │192.168.241.1/24│ │10.0.0.1/24 ││ │ │10.0.0.2/24 │ │192.168.241.2/24│ │
# │ │fd00::1/24 │ │fd00:aa::1/96 ││ │ │fd00:aa::2/96 │ │fd00::2/24 │ │
# │ └────────────────┘ └────────────────┘│ │ └────────────────┘ └────────────────┘ │
# └────────────────────────────────────────┘ └────────────────────────────────────────┘
# ip1 link add dev wg1 type wireguard
# ip2 link add dev wg1 type wireguard
n1 $program wg1
n2 $program wg1
configure_peers
ip1 link add veth1 type veth peer name veth2
ip1 link set veth2 netns $netns2
n1 bash -c 'printf 0 > /proc/sys/net/ipv6/conf/veth1/accept_dad'
n2 bash -c 'printf 0 > /proc/sys/net/ipv6/conf/veth2/accept_dad'
n1 bash -c 'printf 1 > /proc/sys/net/ipv4/conf/veth1/promote_secondaries'
# First we check that we aren't overly sticky and can fall over to new IPs when old ones are removed
ip1 addr add 10.0.0.1/24 dev veth1
ip1 addr add fd00:aa::1/96 dev veth1
ip2 addr add 10.0.0.2/24 dev veth2
ip2 addr add fd00:aa::2/96 dev veth2
ip1 link set veth1 up
ip2 link set veth2 up
waitiface $netns1 veth1
waitiface $netns2 veth2
n0 wg set wg1 peer "$pub2" endpoint 10.0.0.2:20000
n1 ping -W 1 -c 1 192.168.241.2
ip1 addr add 10.0.0.10/24 dev veth1
ip1 addr del 10.0.0.1/24 dev veth1
n1 ping -W 1 -c 1 192.168.241.2
n0 wg set wg1 peer "$pub2" endpoint [fd00:aa::2]:20000
n1 ping -W 1 -c 1 192.168.241.2
ip1 addr add fd00:aa::10/96 dev veth1
ip1 addr del fd00:aa::1/96 dev veth1
n1 ping -W 1 -c 1 192.168.241.2
# Now we show that we can successfully do reply to sender routing
ip1 link set veth1 down
ip2 link set veth2 down
ip1 addr flush dev veth1
ip2 addr flush dev veth2
ip1 addr add 10.0.0.1/24 dev veth1
ip1 addr add 10.0.0.2/24 dev veth1
ip1 addr add fd00:aa::1/96 dev veth1
ip1 addr add fd00:aa::2/96 dev veth1
ip2 addr add 10.0.0.3/24 dev veth2
ip2 addr add fd00:aa::3/96 dev veth2
ip1 link set veth1 up
ip2 link set veth2 up
waitiface $netns1 veth1
waitiface $netns2 veth2
n0 wg set wg2 peer "$pub1" endpoint 10.0.0.1:10000
n2 ping -W 1 -c 1 192.168.241.1
[[ $(n0 wg show wg2 endpoints) == "$pub1 10.0.0.1:10000" ]]
n0 wg set wg2 peer "$pub1" endpoint [fd00:aa::1]:10000
n2 ping -W 1 -c 1 192.168.241.1
[[ $(n0 wg show wg2 endpoints) == "$pub1 [fd00:aa::1]:10000" ]]
n0 wg set wg2 peer "$pub1" endpoint 10.0.0.2:10000
n2 ping -W 1 -c 1 192.168.241.1
[[ $(n0 wg show wg2 endpoints) == "$pub1 10.0.0.2:10000" ]]
n0 wg set wg2 peer "$pub1" endpoint [fd00:aa::2]:10000
n2 ping -W 1 -c 1 192.168.241.1
[[ $(n0 wg show wg2 endpoints) == "$pub1 [fd00:aa::2]:10000" ]]
ip1 link del veth1
ip1 link del wg1
ip2 link del wg2

View file

@ -38,7 +38,7 @@ type Trie struct {
*/
func commonBits(ip1 []byte, ip2 []byte) uint {
var i uint
size := uint(len(ip1)) / 4
size := uint(len(ip1))
for i = 0; i < size; i++ {
v := ip1[i] ^ ip2[i]

View file

@ -44,7 +44,12 @@ func (l *UAPIListener) Accept() (net.Conn, error) {
}
func (l *UAPIListener) Close() error {
return l.listener.Close()
err1 := unix.Close(l.inotifyFd)
err2 := l.listener.Close()
if err1 != nil {
return err1
}
return err2
}
func (l *UAPIListener) Addr() net.Addr {