aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/contrib
diff options
context:
space:
mode:
Diffstat (limited to 'contrib')
-rw-r--r--contrib/benchmarking/configs/other.conf8
-rw-r--r--contrib/benchmarking/configs/thinkpad.conf8
-rw-r--r--contrib/benchmarking/openvpn-config.txt2
-rw-r--r--contrib/benchmarking/static.key21
-rwxr-xr-xcontrib/client-server-example/client.sh20
-rwxr-xr-xcontrib/client-server-example/server.sh14
-rw-r--r--contrib/external-tests/go/main.go63
-rw-r--r--contrib/external-tests/haskell/Setup.hs2
-rw-r--r--contrib/external-tests/haskell/cacophony-wg.cabal34
-rw-r--r--contrib/external-tests/haskell/src/Data/Time/TAI64.hs86
-rw-r--r--contrib/external-tests/haskell/src/Main.hs81
-rw-r--r--contrib/external-tests/rust/.gitignore2
-rw-r--r--contrib/external-tests/rust/Cargo.toml10
-rw-r--r--contrib/external-tests/rust/src/main.rs74
-rwxr-xr-xcontrib/patch-kernel-builtin.sh12
-rw-r--r--contrib/stress-testing/badpacket.c27
-rw-r--r--contrib/stress-testing/peg.c50
-rwxr-xr-xcontrib/stress-testing/self-send.sh48
-rwxr-xr-xcontrib/stress-testing/threewayiperf.sh30
-rw-r--r--contrib/wgserver.service15
20 files changed, 607 insertions, 0 deletions
diff --git a/contrib/benchmarking/configs/other.conf b/contrib/benchmarking/configs/other.conf
new file mode 100644
index 0000000..4257914
--- /dev/null
+++ b/contrib/benchmarking/configs/other.conf
@@ -0,0 +1,8 @@
+[Interface]
+ListenPort = 27183
+PrivateKey = oHilodMrwJSD1UUIkAkyCek2yqy1Frs5XuN47ShGFk0=
+
+[Peer]
+PublicKey = S8hEvD+dam+PrwG4GrSPtE2Pl3ylO/oiUnUDXw3vnx0=
+AllowedIPs = 192.168.2.2/32
+Endpoint = 10.10.10.100:38292 \ No newline at end of file
diff --git a/contrib/benchmarking/configs/thinkpad.conf b/contrib/benchmarking/configs/thinkpad.conf
new file mode 100644
index 0000000..df02b2b
--- /dev/null
+++ b/contrib/benchmarking/configs/thinkpad.conf
@@ -0,0 +1,8 @@
+[Interface]
+ListenPort = 38292
+PrivateKey = MPCo/WSBkm/DCkbEXUhtjc5u//IeD6wEeaw3Q2HxFGw=
+
+[Peer]
+PublicKey = c5PwaIZcVZFDuoDdQJGnYe+fk+wt0qANARpnZDOvqhw=
+AllowedIPs = 0.0.0.0/0
+Endpoint = 172.16.48.128:27183
diff --git a/contrib/benchmarking/openvpn-config.txt b/contrib/benchmarking/openvpn-config.txt
new file mode 100644
index 0000000..f51eabd
--- /dev/null
+++ b/contrib/benchmarking/openvpn-config.txt
@@ -0,0 +1,2 @@
+Server: openvpn --dev tun --ifconfig 192.168.3.1 192.168.3.2 --secret static.key --cipher AES-256-CBC --auth SHA256 --port 61721
+Client: openvpn --dev tun --ifconfig 192.168.3.2 192.168.3.1 --secret static.key --cipher AES-256-CBC --auth SHA256 --port 61721 --remote 10.10.10.1
diff --git a/contrib/benchmarking/static.key b/contrib/benchmarking/static.key
new file mode 100644
index 0000000..53075fe
--- /dev/null
+++ b/contrib/benchmarking/static.key
@@ -0,0 +1,21 @@
+#
+# 2048 bit OpenVPN static key
+#
+-----BEGIN OpenVPN Static key V1-----
+12abb34ac1cb716576642c7e4c9719af
+b311929f6bb5a7b9082c9ac3a02dc77a
+26fc65ba97e67d1dc5b273e72760caba
+6c8a3321acdf89bfd0469528bfc9ed89
+1c9c3762d1e18786c8b6dd590456f158
+d1f625810da1225864c23d7e848ca5d7
+18a49c4b7e640f8e51001ace9222de75
+e05177fd01b32d702bd12b45b085678c
+239e3927d98912174ac648d0e37a3247
+45cabcbea7cf70832f8800a8b863a35a
+933c5921fd65882b050bd1096a0c6c60
+638fb22eafb9f49c13573236d0427441
+c98869ba8de30e597452237527e7dcc6
+519058a919de4432203dc1d7622fb4d0
+f8f20c5350256cdf17bb3b85c5c838fc
+6ddeb4da9dae8b0b882cb043db483a9d
+-----END OpenVPN Static key V1-----
diff --git a/contrib/client-server-example/client.sh b/contrib/client-server-example/client.sh
new file mode 100755
index 0000000..fbae46a
--- /dev/null
+++ b/contrib/client-server-example/client.sh
@@ -0,0 +1,20 @@
+#!/bin/bash
+set -e
+[[ $UID == 0 ]] || { echo "You must be root to run this."; exit 1; }
+umask 077
+trap 'rm -f /tmp/wg_private_key' EXIT INT TERM
+exec 3<>/dev/tcp/demo.wireguard.io/42912
+wg genkey | tee /tmp/wg_private_key | wg pubkey >&3
+IFS=: read -r status server_pubkey server_port internal_ip <&3
+[[ $status == OK ]]
+ip link del dev wg0 2>/dev/null || true
+ip link add dev wg0 type wireguard
+wg set wg0 private-key /tmp/wg_private_key peer "$server_pubkey" allowed-ips 0.0.0.0/0 endpoint "demo.wireguard.io:$server_port"
+ip address add "$internal_ip"/24 dev wg0
+ip link set up dev wg0
+if [ "$1" == "default-route" ]; then
+ host="$(wg show wg0 endpoints | sed -n 's/.*\t\(.*\):.*/\1/p')"
+ ip route add $(ip route get $host | sed '/ via [0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}/{s/^\(.* via [0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}\).*/\1/}' | head -n 1) 2>/dev/null || true
+ ip route add 0/1 dev wg0
+ ip route add 128/1 dev wg0
+fi
diff --git a/contrib/client-server-example/server.sh b/contrib/client-server-example/server.sh
new file mode 100755
index 0000000..e37861f
--- /dev/null
+++ b/contrib/client-server-example/server.sh
@@ -0,0 +1,14 @@
+#!/bin/bash
+if [[ -z $NCAT_REMOTE_ADDR ]]; then
+ ip link del dev wg0 2>/dev/null
+ set -e
+ ip link add dev wg0 type wireguard
+ ip address add 192.168.4.1/24 dev wg0
+ wg set wg0 private-key <(wg genkey) listen-port 12912
+ ip link set up dev wg0
+ exec ncat -e "$(readlink -f "$0")" -k -l -p 42912 -v
+fi
+read -r public_key
+[[ $(wg show wg0 | grep peer | wc -l) -ge 253 ]] && wg set wg0 peer $(wg show wg0 latest-handshakes | sort -k 2 -b -n | head -n 1 | cut -f 1) remove
+next_ip=$(all="$(wg show wg0 allowed-ips)"; for ((i=2; i<=254; i++)); do ip="192.168.4.$i"; [[ $all != *$ip/32* ]] && echo $ip && break; done)
+wg set wg0 peer "$public_key" allowed-ips $next_ip/32 2>/dev/null && echo "OK:$(wg show wg0 private-key | wg pubkey):$(wg show wg0 listen-port):$next_ip" || echo ERROR
diff --git a/contrib/external-tests/go/main.go b/contrib/external-tests/go/main.go
new file mode 100644
index 0000000..de7337b
--- /dev/null
+++ b/contrib/external-tests/go/main.go
@@ -0,0 +1,63 @@
+/* Copyright 2015-2016 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved. */
+
+package main
+
+import (
+ "github.com/titanous/noise"
+ "net"
+ "time"
+ "bytes"
+ "crypto/rand"
+ "encoding/base64"
+ "encoding/binary"
+ "github.com/dchest/blake2s"
+)
+
+func assert(exp bool) {
+ if !exp {
+ panic("Assertion failed.")
+ }
+}
+
+func main() {
+ my_private, _ := base64.StdEncoding.DecodeString("WAmgVYXkbT2bCtdcDwolI88/iVi/aV3/PHcUBTQSYmo=")
+ my_public, _ := base64.StdEncoding.DecodeString("K5sF9yESrSBsOXPd6TcpKNgqoy1Ik3ZFKl4FolzrRyI=")
+ preshared, _ := base64.StdEncoding.DecodeString("FpCyhws9cxwWoV4xELtfJvjJN+zQVRPISllRWgeopVE=")
+ their_public, _ := base64.StdEncoding.DecodeString("qRCwZSKInrMAq5sepfCdaCsRJaoLe5jhtzfiw7CjbwM=")
+ cs := noise.NewCipherSuite(noise.DH25519, noise.CipherChaChaPoly, noise.HashBLAKE2s)
+ hs := noise.NewHandshakeState(noise.Config{CipherSuite: cs, Random: rand.Reader, Pattern: noise.HandshakeIK, Initiator: true, Prologue: []byte("WireGuard v0 zx2c4 Jason@zx2c4.com"), PresharedKey: preshared, StaticKeypair: noise.DHKey{Private: my_private, Public: my_public}, PeerStatic: their_public})
+ conn, _ := net.Dial("udp", "test.wireguard.io:51820")
+
+ now := time.Now()
+ tai64n := make([]byte, 12)
+ binary.BigEndian.PutUint64(tai64n[:], uint64(now.Unix()))
+ binary.BigEndian.PutUint32(tai64n[8:], uint32(now.UnixNano()))
+ initiation_packet := make([]byte, 5)
+ initiation_packet[0] = 1 /* Type: Initiation */
+ binary.LittleEndian.PutUint32(initiation_packet[1:], 28) /* Sender index: 28 (arbitrary) */
+ initiation_packet, _, _ = hs.WriteMessage(initiation_packet, tai64n)
+ hasher, _ := blake2s.New(&blake2s.Config{Size: 16, Key: preshared})
+ hasher.Write(their_public)
+ hasher.Write(initiation_packet)
+ initiation_packet = append(initiation_packet, hasher.Sum(nil)[:16]...)
+ initiation_packet = append(initiation_packet, bytes.Repeat([]byte{ 0 }, 16)...)
+ conn.Write(initiation_packet)
+
+ response_packet := make([]byte, 89)
+ conn.Read(response_packet)
+ assert(response_packet[0] == 2 /* Type: Response */)
+ their_index := binary.LittleEndian.Uint32(response_packet[1:])
+ our_index := binary.LittleEndian.Uint32(response_packet[5:])
+ assert(our_index == 28)
+ payload, send_cs, _, err := hs.ReadMessage(nil, response_packet[9:57])
+ assert(len(payload) == 0 && err == nil)
+
+ keepalive_packet := make([]byte, 13)
+ keepalive_packet[0] = 4 /* Type: Data */
+ binary.LittleEndian.PutUint32(keepalive_packet[1:], their_index)
+ binary.LittleEndian.PutUint64(keepalive_packet[3:], 0) /* Nonce */
+ keepalive_packet = send_cs.Encrypt(keepalive_packet, nil, nil)
+ conn.Write(keepalive_packet)
+
+ conn.Close()
+}
diff --git a/contrib/external-tests/haskell/Setup.hs b/contrib/external-tests/haskell/Setup.hs
new file mode 100644
index 0000000..9a994af
--- /dev/null
+++ b/contrib/external-tests/haskell/Setup.hs
@@ -0,0 +1,2 @@
+import Distribution.Simple
+main = defaultMain
diff --git a/contrib/external-tests/haskell/cacophony-wg.cabal b/contrib/external-tests/haskell/cacophony-wg.cabal
new file mode 100644
index 0000000..62e2485
--- /dev/null
+++ b/contrib/external-tests/haskell/cacophony-wg.cabal
@@ -0,0 +1,34 @@
+-- Initial cacophony-wg.cabal generated by cabal init. For further
+-- documentation, see http://haskell.org/cabal/users-guide/
+
+name: cacophony-wg
+version: 0.1.0
+-- synopsis:
+-- description:
+license: PublicDomain
+license-file: LICENSE
+author: John Galt
+maintainer: centromere@users.noreply.github.com
+-- copyright:
+-- category:
+build-type: Simple
+-- extra-source-files:
+cabal-version: >=1.10
+
+executable cacophony-wg
+ main-is: Main.hs
+ other-modules:
+ Data.Time.TAI64
+ build-depends:
+ base >=4.8 && <4.9,
+ base16-bytestring,
+ base64-bytestring,
+ blake2,
+ bytestring,
+ cacophony,
+ cereal,
+ cryptonite,
+ network,
+ time
+ hs-source-dirs: src
+ default-language: Haskell2010
diff --git a/contrib/external-tests/haskell/src/Data/Time/TAI64.hs b/contrib/external-tests/haskell/src/Data/Time/TAI64.hs
new file mode 100644
index 0000000..37a90e6
--- /dev/null
+++ b/contrib/external-tests/haskell/src/Data/Time/TAI64.hs
@@ -0,0 +1,86 @@
+module Data.Time.TAI64 (
+ TAI64(..)
+ , TAI64N(..)
+ , TAI64NA(..)
+ , posixToTAI64
+ , posixToTAI64N
+ , posixToTAI64NA
+ , getCurrentTAI64
+ , getCurrentTAI64N
+ , getCurrentTAI64NA
+ , tAI64ToPosix
+ , tAI64NToPosix
+ , tAI64NAToPosix
+) where
+
+import Data.Serialize
+import Control.Monad
+import Data.Word
+
+import Data.Time.Clock
+import Data.Time.Clock.POSIX
+
+import Numeric
+
+data TAI64 = TAI64
+ {-# UNPACK #-} !Word64
+ deriving (Eq, Ord)
+
+data TAI64N = TAI64N
+ {-# UNPACK #-} !TAI64
+ {-# UNPACK #-} !Word32
+ deriving (Eq, Ord, Show)
+
+data TAI64NA = TAI64NA
+ {-# UNPACK #-} !TAI64N
+ {-# UNPACK #-} !Word32
+ deriving (Eq, Ord, Show)
+
+instance Show TAI64 where
+ show (TAI64 t) = "TAI64 0x" ++ showHex t ""
+
+instance Serialize TAI64 where
+ put (TAI64 t) = putWord64be t
+ get = liftM TAI64 get
+
+instance Serialize TAI64N where
+ put (TAI64N t' nt) = put t' >> putWord32be nt
+ get = liftM2 TAI64N get get
+
+instance Serialize TAI64NA where
+ put (TAI64NA t' at) = put t' >> putWord32be at
+ get = liftM2 TAI64NA get get
+
+
+posixToTAI64 :: POSIXTime -> TAI64
+posixToTAI64 = TAI64 . (2^62 +) . truncate . realToFrac
+
+posixToTAI64N :: POSIXTime -> TAI64N
+posixToTAI64N pt = TAI64N t' ns where
+ t' = posixToTAI64 pt
+ ns = (`mod` 10^9) $ truncate (pts * 10**9)
+ pts = realToFrac pt
+
+posixToTAI64NA :: POSIXTime -> TAI64NA -- | PICOsecond precision
+posixToTAI64NA pt = TAI64NA t' as where
+ t' = posixToTAI64N pt
+ as = (`mod` 10^9) $ truncate (pts * 10**18)
+ pts = realToFrac pt
+
+getCurrentTAI64 :: IO TAI64
+getCurrentTAI64N :: IO TAI64N
+getCurrentTAI64NA :: IO TAI64NA
+getCurrentTAI64 = liftM posixToTAI64 getPOSIXTime
+getCurrentTAI64N = liftM posixToTAI64N getPOSIXTime
+getCurrentTAI64NA = liftM posixToTAI64NA getPOSIXTime
+
+tAI64ToPosix :: TAI64 -> POSIXTime
+tAI64ToPosix (TAI64 s) = fromRational . fromIntegral $ s - 2^62
+
+tAI64NToPosix :: TAI64N -> POSIXTime
+tAI64NToPosix (TAI64N t' n) = tAI64ToPosix t' + nanopart where
+ nanopart = fromRational $ (toRational $ 10**(-9)) * toRational n -- TODO: optimize?
+
+tAI64NAToPosix :: TAI64NA -> POSIXTime
+tAI64NAToPosix (TAI64NA t' a) = tAI64NToPosix t' + attopart where
+ attopart = fromRational $ (toRational $ 10**(-18)) * toRational a
diff --git a/contrib/external-tests/haskell/src/Main.hs b/contrib/external-tests/haskell/src/Main.hs
new file mode 100644
index 0000000..f78305d
--- /dev/null
+++ b/contrib/external-tests/haskell/src/Main.hs
@@ -0,0 +1,81 @@
+{-# LANGUAGE OverloadedStrings #-}
+module Main where
+
+import Control.Applicative ((<$>))
+import Control.Concurrent.MVar
+import Control.Monad (void)
+import Data.ByteString.Char8 (pack, unpack, take, drop, replicate)
+import Data.ByteString (ByteString)
+import qualified Data.ByteString.Base16 as Hex
+import qualified Data.ByteString.Base64 as B64
+import qualified Data.Serialize as S
+import Prelude hiding (take, drop, replicate)
+import System.Environment
+import Network.Socket
+import qualified Network.Socket.ByteString as NBS
+
+import Crypto.Hash.BLAKE2.BLAKE2s
+import Crypto.Noise.Cipher
+import Crypto.Noise.Cipher.ChaChaPoly1305
+import Crypto.Noise.Curve
+import Crypto.Noise.Curve.Curve25519
+import Crypto.Noise.Handshake
+import Crypto.Noise.HandshakePatterns
+import Crypto.Noise.Hash.BLAKE2s
+import Crypto.Noise.Types
+
+import Data.Time.TAI64
+
+w :: PublicKey Curve25519
+ -> Plaintext
+ -> Socket
+ -> SockAddr
+ -> ByteString
+ -> IO ()
+w theirPub (Plaintext myPSK) sock addr msg = do
+ let x = "\x01\x00\x00" `mappend` msg
+ mac = hash 16 myPSK (sbToBS' (curvePubToBytes theirPub) `mappend` sbToBS' x)
+ void $ NBS.sendTo sock (x `mappend` mac `mappend` replicate 16 '\0') addr
+
+r :: MVar ByteString -> Socket -> IO ByteString
+r smv sock = do
+ (r, _) <- NBS.recvFrom sock 1024
+ putMVar smv $ (take 2 . drop 1) r
+ return . take 48 . drop 5 $ r
+
+payload :: IO Plaintext
+payload = do
+ tai64n <- getCurrentTAI64N
+ return . Plaintext . bsToSB' $ S.encode tai64n
+
+main :: IO ()
+main = do
+ let ip = "test.wireguard.io"
+ let port = "51820"
+ let mykey = "WAmgVYXkbT2bCtdcDwolI88/iVi/aV3/PHcUBTQSYmo="
+ let serverkey = "qRCwZSKInrMAq5sepfCdaCsRJaoLe5jhtzfiw7CjbwM="
+ let psk = "FpCyhws9cxwWoV4xELtfJvjJN+zQVRPISllRWgeopVE="
+ addrInfo <- head <$> getAddrInfo Nothing (Just ip) (Just port)
+ sock <- socket (addrFamily addrInfo) Datagram defaultProtocol
+
+ let addr = addrAddress addrInfo
+ mykey' = curveBytesToPair . bsToSB' . either undefined id . B64.decode . pack $ mykey :: KeyPair Curve25519
+ serverkey' = curveBytesToPub . bsToSB' . either undefined id . B64.decode . pack $ serverkey :: PublicKey Curve25519
+ psk' = Plaintext . bsToSB' . either undefined id . B64.decode . pack $ psk
+ hs = handshakeState $ HandshakeStateParams
+ noiseIK
+ "WireGuard v0 zx2c4 Jason@zx2c4.com"
+ (Just psk')
+ (Just mykey')
+ Nothing
+ (Just serverkey')
+ Nothing
+ True :: HandshakeState ChaChaPoly1305 Curve25519 BLAKE2s
+
+ senderindexmv <- newEmptyMVar
+ let hc = HandshakeCallbacks (w serverkey' psk' sock addr) (r senderindexmv sock) (\_ -> return ()) payload
+ (encryption, decryption) <- runHandshake hs hc
+
+ let (keepAlive, encryption') = encryptPayload "" encryption
+ senderindex <- takeMVar senderindexmv
+ void $ NBS.sendTo sock ("\x04" `mappend` senderindex `mappend` replicate 8 '\0' `mappend` keepAlive) addr
diff --git a/contrib/external-tests/rust/.gitignore b/contrib/external-tests/rust/.gitignore
new file mode 100644
index 0000000..1e7caa9
--- /dev/null
+++ b/contrib/external-tests/rust/.gitignore
@@ -0,0 +1,2 @@
+Cargo.lock
+target/
diff --git a/contrib/external-tests/rust/Cargo.toml b/contrib/external-tests/rust/Cargo.toml
new file mode 100644
index 0000000..c064905
--- /dev/null
+++ b/contrib/external-tests/rust/Cargo.toml
@@ -0,0 +1,10 @@
+[package]
+name = "wireguard-keepalive"
+version = "0.1.0"
+authors = ["jason@zx2c4.com"]
+[dependencies]
+screech = { git = "https://github.com/trevp/screech" }
+rust-crypto = "*"
+byteorder = "*"
+rustc-serialize = "*"
+time = "*"
diff --git a/contrib/external-tests/rust/src/main.rs b/contrib/external-tests/rust/src/main.rs
new file mode 100644
index 0000000..fa468af
--- /dev/null
+++ b/contrib/external-tests/rust/src/main.rs
@@ -0,0 +1,74 @@
+/* Copyright 2015-2016 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved. */
+extern crate screech;
+extern crate crypto;
+extern crate time;
+extern crate rustc_serialize;
+extern crate byteorder;
+
+use screech::*;
+use byteorder::{ByteOrder, BigEndian, LittleEndian};
+use crypto::curve25519::curve25519_base;
+use crypto::blake2s::Blake2s;
+use rustc_serialize::base64::FromBase64;
+use std::net::*;
+
+fn memcpy(out: &mut [u8], data: &[u8]) {
+ for count in 0..data.len() {
+ out[count] = data[count];
+ }
+}
+
+fn main() {
+ let send_addr = "test.wireguard.io:51820".to_socket_addrs().unwrap().next().unwrap();
+ let listen_addr = "0.0.0.0:0".to_socket_addrs().unwrap().next().unwrap();
+ let socket = UdpSocket::bind(listen_addr).unwrap();
+ let mut empty_payload = [0; 0];
+
+ let mut their_public = [0; 32];
+ memcpy(&mut their_public, &"qRCwZSKInrMAq5sepfCdaCsRJaoLe5jhtzfiw7CjbwM=".from_base64().unwrap());
+ let mut my_private = [0; 32];
+ memcpy(&mut my_private, &"WAmgVYXkbT2bCtdcDwolI88/iVi/aV3/PHcUBTQSYmo=".from_base64().unwrap());
+ let mut my_preshared = [0; 32];
+ memcpy(&mut my_preshared, &"FpCyhws9cxwWoV4xELtfJvjJN+zQVRPISllRWgeopVE=".from_base64().unwrap());
+ let my_public = curve25519_base(&my_private);
+ let mut my_keypair : Dh25519 = Default::default();
+ my_keypair.set(&my_private, &my_public);
+ let mut owner : HandshakeCryptoOwner<RandomOs, Dh25519, CipherChaChaPoly, HashBLAKE2s> = Default::default();
+ owner.set_s(my_keypair);
+ owner.set_rs(&their_public);
+ let mut cipherstate1 : CipherState<CipherChaChaPoly> = Default::default();
+ let mut cipherstate2 : CipherState<CipherChaChaPoly> = Default::default();
+ let mut handshake = HandshakeState::new_from_owner(&mut owner, true, HandshakePattern::IK, "WireGuard v0 zx2c4 Jason@zx2c4.com".as_bytes(), Some(&my_preshared[..]), &mut cipherstate1, &mut cipherstate2);
+
+ let now = time::get_time();
+ let mut tai64n = [0; 12];
+ BigEndian::write_i64(&mut tai64n[0..], now.sec);
+ BigEndian::write_i32(&mut tai64n[8..], now.nsec);
+ let mut initiation_packet = [0; 145];
+ initiation_packet[0] = 1; /* Type: Initiation */
+ LittleEndian::write_u32(&mut initiation_packet[1..], 28); /* Sender index: 28 (arbitrary) */
+ handshake.write_message(&tai64n, &mut initiation_packet[5..]);
+ let mut mac_material = [0; 143];
+ memcpy(&mut mac_material, &their_public);
+ memcpy(&mut mac_material[32..], &initiation_packet[0..113]);
+ let mut mac = [0; 16];
+ Blake2s::blake2s(&mut mac, &mac_material, &my_preshared);
+ memcpy(&mut initiation_packet[113..], &mac);
+ socket.send_to(&initiation_packet, &send_addr).unwrap();
+
+ let mut response_packet = [0; 89];
+ socket.recv_from(&mut response_packet).unwrap();
+ assert!(response_packet[0] == 2 /* Type: Response */);
+ let their_index = LittleEndian::read_u32(&response_packet[1..]);
+ let our_index = LittleEndian::read_u32(&response_packet[5..]);
+ assert!(our_index == 28);
+ let (payload_len, last) = handshake.read_message(&response_packet[9..57], &mut empty_payload).unwrap();
+ assert!(payload_len == 0 && last);
+
+ let mut keepalive_packet = [0; 29];
+ keepalive_packet[0] = 4; /* Type: Data */
+ LittleEndian::write_u32(&mut keepalive_packet[1..], their_index);
+ LittleEndian::write_u64(&mut keepalive_packet[5..], cipherstate1.n);
+ cipherstate1.encrypt(&empty_payload, &mut keepalive_packet[13..]); /* Empty payload means keepalive */
+ socket.send_to(&keepalive_packet, &send_addr).unwrap();
+}
diff --git a/contrib/patch-kernel-builtin.sh b/contrib/patch-kernel-builtin.sh
new file mode 100755
index 0000000..8229762
--- /dev/null
+++ b/contrib/patch-kernel-builtin.sh
@@ -0,0 +1,12 @@
+#!/bin/sh
+
+K="$1"
+WG="$(readlink -f "$(dirname "$(readlink -f "$0")")/../src/")"
+
+if [[ ! -e $K/net/Kconfig ]]; then
+ echo "You must specify the location of kernel sources as the first argument." >&2
+ exit 1
+fi
+
+sed -i "/^if NET/a source \"$WG/Kconfig\"" "$K/net/Kconfig"
+echo "obj-y += ../../../../../../../../../../../../../../../../../../../../../..$WG/" >> "$K/net/Makefile"
diff --git a/contrib/stress-testing/badpacket.c b/contrib/stress-testing/badpacket.c
new file mode 100644
index 0000000..eee61fc
--- /dev/null
+++ b/contrib/stress-testing/badpacket.c
@@ -0,0 +1,27 @@
+#include <stdlib.h>
+#include <unistd.h>
+#include <arpa/inet.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <linux/limits.h>
+
+int main(int argc, char *argv[])
+{
+ static const unsigned char handshake1[143] = { 1, 0 };
+ int fd = socket(AF_INET, SOCK_DGRAM, 0);
+ struct sockaddr_in addr = {
+ .sin_family = AF_INET,
+ .sin_port = htons(atoi(argv[2])),
+ .sin_addr = inet_addr(argv[1])
+ };
+ connect(fd, (struct sockaddr *)&addr, sizeof(addr));
+
+ for (;;)
+ send(fd, handshake1, sizeof(handshake1), 0);
+
+ close(fd);
+
+ return 0;
+}
diff --git a/contrib/stress-testing/peg.c b/contrib/stress-testing/peg.c
new file mode 100644
index 0000000..6b539fa
--- /dev/null
+++ b/contrib/stress-testing/peg.c
@@ -0,0 +1,50 @@
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <linux/limits.h>
+#include <time.h>
+#include <stdio.h>
+#include <string.h>
+
+static unsigned long long interface_tx_bytes(const char *interface)
+{
+ char buf[PATH_MAX];
+ FILE *f;
+ unsigned long long ret;
+ snprintf(buf, PATH_MAX - 1, "/sys/class/net/%s/statistics/tx_bytes", interface);
+ f = fopen(buf, "r");
+ fscanf(f, "%llu", &ret);
+ fclose(f);
+ return ret;
+}
+
+int main(int argc, char *argv[])
+{
+ char buf[1500] = { 0 };
+ unsigned long long before, after, i;
+ struct timespec begin, end;
+ double elapsed;
+ struct ifreq req;
+ int fd = socket(AF_INET, SOCK_DGRAM, 0);
+ struct sockaddr_in addr = {
+ .sin_family = AF_INET,
+ .sin_port = htons(7271),
+ .sin_addr = inet_addr(argv[3])
+ };
+ strcpy(req.ifr_name, argv[1]);
+ ioctl(fd, SIOCGIFMTU, &req);
+
+ connect(fd, (struct sockaddr *)&addr, sizeof(addr));
+
+ before = interface_tx_bytes(argv[2]);
+ clock_gettime(CLOCK_MONOTONIC, &begin);
+ for (i = 0; i < 10000000; ++i)
+ send(fd, buf, req.ifr_mtu - 28, 0);
+ clock_gettime(CLOCK_MONOTONIC, &end);
+ after = interface_tx_bytes(argv[2]);
+ elapsed = end.tv_sec - begin.tv_sec + (end.tv_nsec - begin.tv_nsec) / 1000000000.0;
+
+ printf("%.4f mbps\n", ((after - before) * 8) / elapsed / 1000000.0);
+ return 0;
+}
diff --git a/contrib/stress-testing/self-send.sh b/contrib/stress-testing/self-send.sh
new file mode 100755
index 0000000..eb7947b
--- /dev/null
+++ b/contrib/stress-testing/self-send.sh
@@ -0,0 +1,48 @@
+#!/bin/bash
+set -e
+
+PRIVATE_KEYS=("")
+PUBLIC_KEYS=("")
+
+resetwg() {
+ for i in {1..64}; do
+ ip link delete dev wg${i} 2>/dev/null >/dev/null || true
+ done
+}
+
+for i in {1..64}; do
+ next_key="$(wg genkey)"
+ PRIVATE_KEYS+=("$next_key")
+ PUBLIC_KEYS+=($(wg pubkey <<<"$next_key"))
+done
+
+resetwg
+trap resetwg INT TERM EXIT
+
+for i in {1..64}; do
+ { echo "[Interface]"
+ echo "ListenPort = $(( $i + 31222 ))"
+ echo "PrivateKey = ${PRIVATE_KEYS[$i]}"
+
+ for j in {1..64}; do
+ [[ $i == $j ]] && continue
+ echo "[Peer]"
+ echo "PublicKey = ${PUBLIC_KEYS[$j]}"
+ echo "AllowedIPs = 192.168.8.${j}/32"
+ echo "Endpoint = 127.0.0.1:$(( $j + 31222 ))"
+ done
+ } > "/tmp/deviceload.conf"
+
+ ip link add dev wg${i} type wireguard
+ wg setconf wg${i} "/tmp/deviceload.conf"
+ ip link set up dev wg${i}
+ rm "/tmp/deviceload.conf"
+done
+
+ip address add dev wg1 192.168.8.1/24
+
+while true; do
+ for i in {2..64}; do
+ echo hello | ncat -u 192.168.8.${i} 1234
+ done
+done
diff --git a/contrib/stress-testing/threewayiperf.sh b/contrib/stress-testing/threewayiperf.sh
new file mode 100755
index 0000000..932d666
--- /dev/null
+++ b/contrib/stress-testing/threewayiperf.sh
@@ -0,0 +1,30 @@
+#!/bin/bash
+set -e
+
+if [[ $(hostname) == "thinkpad" ]]; then
+ make -C "$(dirname "$0")/../../src" remote-run
+ for i in 128 129 130; do
+ scp "$0" root@172.16.48.${i}:
+ done
+ for i in 128 129 130; do
+ konsole --new-tab -e ssh -t root@172.16.48.${i} "./$(basename "$0")"
+ done
+ exit
+fi
+
+# perf top -U --dsos '[wireguard]'
+
+tmux new-session -s bigtest -d
+tmux new-window -n "server 6000" -t bigtest "iperf3 -p 6000 -s"
+tmux new-window -n "server 6001" -t bigtest "iperf3 -p 6001 -s"
+sleep 5
+me=$(ip -o -4 address show dev wg0 | sed 's/.*inet \([^ ]*\)\/.*/\1/' | cut -d . -f 4)
+for i in 1 2 3; do
+ [[ $i == $me ]] && continue
+ [[ $me == "1" ]] && port=6000
+ [[ $me == "3" ]] && port=6001
+ [[ $me == "2" && $i == "1" ]] && port=6000
+ [[ $me == "2" && $i == "3" ]] && port=6001
+ tmux new-window -n "client 192.168.2.${i}" -t bigtest "iperf3 -n 300000G -i 1 -p $port -c 192.168.2.${i}"
+done
+tmux attach -t bigtest
diff --git a/contrib/wgserver.service b/contrib/wgserver.service
new file mode 100644
index 0000000..dfce1e9
--- /dev/null
+++ b/contrib/wgserver.service
@@ -0,0 +1,15 @@
+[Unit]
+Description=WireGuard Server
+
+[Service]
+Type=oneshot
+RemainAfterExit=yes
+ExecStart=/bin/ip link add dev wgserver type wireguard
+ExecStart=/bin/ip address add 192.168.177.1/24 dev wgserver
+ExecStart=/usr/bin/wg setconf wgserver /etc/wireguard-server.conf
+ExecStart=/bin/ip link set up dev wgserver
+ExecStop=/bin/sh -c 'umask 077; /usr/bin/wg showconf wgserver > /etc/wireguard-server.conf.tmp && mv /etc/wireguard-server.conf.tmp /etc/wireguard-server.conf'
+ExecStop=/bin/ip link del dev wgserver
+
+[Install]
+WantedBy=multi-user.target