├── .gitignore ├── COPYING ├── Makefile ├── README.md ├── device ├── allowedips.go ├── allowedips_rand_test.go ├── allowedips_test.go ├── bind_test.go ├── boundif_android.go ├── boundif_darwin.go ├── boundif_windows.go ├── conn.go ├── conn_default.go ├── conn_linux.go ├── constants.go ├── cookie.go ├── cookie_test.go ├── device.go ├── device_test.go ├── endpoint_test.go ├── indextable.go ├── ip.go ├── kdf_test.go ├── keypair.go ├── logger.go ├── mark_default.go ├── mark_unix.go ├── misc.go ├── noise-helpers.go ├── noise-protocol.go ├── noise-types.go ├── noise_test.go ├── peer.go ├── pools.go ├── queueconstants_android.go ├── queueconstants_default.go ├── queueconstants_ios.go ├── receive.go ├── send.go ├── timers.go ├── tun.go ├── uapi.go └── version.go ├── donotuseon_linux.go ├── go.mod ├── go.sum ├── ipc ├── uapi_bsd.go ├── uapi_illumos.go ├── uapi_linux.go └── uapi_windows.go ├── main.go ├── main_windows.go ├── ratelimiter ├── ratelimiter.go └── ratelimiter_test.go ├── replay ├── replay.go └── replay_test.go ├── rwcancel ├── fdset.go ├── rwcancel.go ├── select_default.go └── select_linux.go ├── tai64n ├── tai64n.go └── tai64n_test.go ├── tests └── netns.sh ├── tun ├── asm_solaris_amd64.s ├── helper_test.go ├── tun.go ├── tun_darwin.go ├── tun_freebsd.go ├── tun_illumos.go ├── tun_linux.go ├── tun_openbsd.go ├── tun_windows.go └── wintun │ ├── guid │ ├── guid_windows.go │ ├── mksyscall.go │ └── zguid_windows.go │ ├── setupapi │ ├── mksyscall.go │ ├── setupapi_windows.go │ ├── setupapi_windows_test.go │ ├── types_windows.go │ ├── zsetupapi_windows.go │ └── zsetupapi_windows_test.go │ └── wintun_windows.go └── version.go /.gitignore: -------------------------------------------------------------------------------- 1 | wireguard-go 2 | vendor 3 | .gopath 4 | ireallywantobuildon_linux.go 5 | -------------------------------------------------------------------------------- /COPYING: -------------------------------------------------------------------------------- 1 | Permission is hereby granted, free of charge, to any person obtaining a copy of 2 | this software and associated documentation files (the "Software"), to deal in 3 | the Software without restriction, including without limitation the rights to 4 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 5 | of the Software, and to permit persons to whom the Software is furnished to do 6 | so, subject to the following conditions: 7 | 8 | The above copyright notice and this permission notice shall be included in all 9 | copies or substantial portions of the Software. 10 | 11 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 12 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 13 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 14 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 15 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 16 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 17 | SOFTWARE. 18 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | PREFIX ?= /usr 2 | DESTDIR ?= 3 | BINDIR ?= $(PREFIX)/bin 4 | export GOPATH ?= $(CURDIR)/.gopath 5 | export GO111MODULE := on 6 | 7 | all: generate-version-and-build 8 | 9 | ifeq ($(shell go env GOOS)|$(wildcard .git),linux|) 10 | $(error Do not build this for Linux. Instead use the Linux kernel module. See wireguard.com/install/ for more info.) 11 | else 12 | ireallywantobuildon_linux.go: 13 | @printf "WARNING: This software is meant for use on non-Linux\nsystems. For Linux, please use the kernel module\ninstead. See wireguard.com/install/ for more info.\n\n" >&2 14 | @printf 'package main\nconst UseTheKernelModuleInstead = 0xdeadbabe\n' > "$@" 15 | clean-ireallywantobuildon_linux.go: 16 | @rm -f ireallywantobuildon_linux.go 17 | .PHONY: clean-ireallywantobuildon_linux.go 18 | clean: clean-ireallywantobuildon_linux.go 19 | wireguard-go: ireallywantobuildon_linux.go 20 | endif 21 | 22 | MAKEFLAGS += --no-print-directory 23 | 24 | generate-version-and-build: 25 | @export GIT_CEILING_DIRECTORIES="$(realpath $(CURDIR)/..)" && \ 26 | tag="$$(git describe --dirty 2>/dev/null)" && \ 27 | ver="$$(printf 'package device\nconst WireGuardGoVersion = "%s"\n' "$$tag")" && \ 28 | [ "$$(cat device/version.go 2>/dev/null)" != "$$ver" ] && \ 29 | echo "$$ver" > device/version.go && \ 30 | git update-index --assume-unchanged device/version.go || true 31 | @$(MAKE) wireguard-go 32 | 33 | wireguard-go: $(wildcard *.go) $(wildcard */*.go) 34 | go build -v -o "$@" 35 | 36 | install: wireguard-go 37 | @install -v -d "$(DESTDIR)$(BINDIR)" && install -v -m 0755 "$<" "$(DESTDIR)$(BINDIR)/wireguard-go" 38 | 39 | clean: 40 | rm -f wireguard-go 41 | 42 | .PHONY: all clean install generate-version-and-build 43 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # illumos port of wireguard-go 2 | 3 | This is a work-in-progress port of the Go version of 4 | [WireGuard](https://www.wireguard.com/). Basic functionality has been verified 5 | on a current SmartOS system, which includes by default the necessary [TUN/TAP 6 | Driver](http://www.whiteboard.ne.jp/~admin2/tuntap/). 7 | 8 | In addition to the `wireguard-go` program built from this repository, you will 9 | need to build the `wg` tool from the upstream 10 | [WireGuard.git](https://git.zx2c4.com/WireGuard/); e.g., 11 | 12 | ``` 13 | $ git clone https://git.zx2c4.com/WireGuard 14 | $ cd WireGuard/src/tools 15 | $ make LDLIBS='-lnsl -lsocket' 16 | CC /ws/wireguard/WireGuard/src/tools/wg.o 17 | CC /ws/wireguard/WireGuard/src/tools/set.o 18 | CC /ws/wireguard/WireGuard/src/tools/mnlg.o 19 | CC /ws/wireguard/WireGuard/src/tools/pubkey.o 20 | CC /ws/wireguard/WireGuard/src/tools/showconf.o 21 | CC /ws/wireguard/WireGuard/src/tools/genkey.o 22 | CC /ws/wireguard/WireGuard/src/tools/setconf.o 23 | CC /ws/wireguard/WireGuard/src/tools/curve25519.o 24 | CC /ws/wireguard/WireGuard/src/tools/encoding.o 25 | CC /ws/wireguard/WireGuard/src/tools/ipc.o 26 | CC /ws/wireguard/WireGuard/src/tools/terminal.o 27 | CC /ws/wireguard/WireGuard/src/tools/config.o 28 | CC /ws/wireguard/WireGuard/src/tools/show.o 29 | LD /ws/wireguard/WireGuard/src/tools/wg 30 | $ ./wg 31 | interface: tun0 32 | ``` 33 | 34 | At present, this port of `wireguard-go` must be run in the foreground (i.e., 35 | using `-f`) and only accepts the interface name `tun`. A dynamic `tun[0-9]+` 36 | device will be created for the duration of the process. 37 | 38 | ``` 39 | # ./wireguard-go -f tun 40 | WARNING WARNING WARNING WARNING WARNING WARNING WARNING 41 | W G 42 | W This is alpha software. It will very likely not G 43 | W do what it is supposed to do, and things may go G 44 | W horribly wrong. You have been warned. Proceed G 45 | W at your own risk. G 46 | W G 47 | WARNING WARNING WARNING WARNING WARNING WARNING WARNING 48 | device tun0 49 | ip_muxid 131 50 | INFO: (tun0) 2019/03/26 08:26:38 Starting wireguard-go version 0.0.20181222 51 | INFO: (tun0) 2019/03/26 08:26:38 Interface set up 52 | INFO: (tun0) 2019/03/26 08:26:38 Device started 53 | INFO: (tun0) 2019/03/26 08:26:38 UAPI listener started 54 | ``` 55 | 56 | Once the daemon is running, you'll need to configure the IP address and any 57 | routes on your `tun` interface. Due to the way point-to-point links need to be 58 | configured on illumos at the moment, you'll need to set aside a "destination" 59 | address to represent the remote side of the tunnel. In the example below I've 60 | used `5.0.1.1` as the IP address of this system, and `5.0.1.2` as the fake 61 | destination address. You can use the same fake destination IP on all systems 62 | but no system on the VPN can use it as its actual IP. 63 | 64 | ``` 65 | # ifconfig tun0 5.0.1.1 5.0.1.2 netmask 255.255.255.255 mtu 1300 up 66 | # ifconfig tun0 67 | tun0: flags=10010008d1 mtu 1300 index 34 68 | inet 5.0.1.1 --> 5.0.1.2 netmask ffffffff 69 | ether 80:1c:25:4e:24:fe 70 | 71 | # route add 5.0.1.0/24 5.0.1.2 72 | 73 | # wg setconf tun0 tun0.conf 74 | # wg 75 | interface: tun0 76 | public key: <....> 77 | private key: (hidden) 78 | listening port: 51820 79 | 80 | peer: <....> 81 | endpoint: 10.0.0.1:51820 82 | allowed ips: 5.0.1.3/32 83 | latest handshake: 1 minute, 16 seconds ago 84 | transfer: 331.72 KiB received, 12.15 MiB sent 85 | ``` 86 | 87 | The rest of the README is from the upstream repository: 88 | 89 | # Go Implementation of [WireGuard](https://www.wireguard.com/) 90 | 91 | This is an implementation of WireGuard in Go. 92 | 93 | ***WARNING:*** This is a work in progress and not ready for prime time, with no official "releases" yet. It is extremely rough around the edges and leaves much to be desired. There are bugs and we are not yet in a position to make claims about its security. Beware. 94 | 95 | ## Usage 96 | 97 | Most Linux kernel WireGuard users are used to adding an interface with `ip link add wg0 type wireguard`. With wireguard-go, instead simply run: 98 | 99 | ``` 100 | $ wireguard-go wg0 101 | ``` 102 | 103 | This will create an interface and fork into the background. To remove the interface, use the usual `ip link del wg0`, or if your system does not support removing interfaces directly, you may instead remove the control socket via `rm -f /var/run/wireguard/wg0.sock`, which will result in wireguard-go shutting down. 104 | 105 | To run wireguard-go without forking to the background, pass `-f` or `--foreground`: 106 | 107 | ``` 108 | $ wireguard-go -f wg0 109 | ``` 110 | 111 | When an interface is running, you may use [`wg(8)`](https://git.zx2c4.com/WireGuard/about/src/tools/man/wg.8) to configure it, as well as the usual `ip(8)` and `ifconfig(8)` commands. 112 | 113 | To run with more logging you may set the environment variable `LOG_LEVEL=debug`. 114 | 115 | ## Platforms 116 | 117 | ### Linux 118 | 119 | This will run on Linux; however **YOU SHOULD NOT RUN THIS ON LINUX**. Instead use the kernel module; see the [installation page](https://www.wireguard.com/install/) for instructions. 120 | 121 | ### macOS 122 | 123 | This runs on macOS using the utun driver. It does not yet support sticky sockets, and won't support fwmarks because of Darwin limitations. Since the utun driver cannot have arbitrary interface names, you must either use `utun[0-9]+` for an explicit interface name or `utun` to have the kernel select one for you. If you choose `utun` as the interface name, and the environment variable `WG_TUN_NAME_FILE` is defined, then the actual name of the interface chosen by the kernel is written to the file specified by that variable. 124 | 125 | ### Windows 126 | 127 | It is currently a work in progress to strip out the beginnings of an experiment done with the OpenVPN tuntap driver and instead port to the new UWP APIs for tunnels. In other words, this does not *yet* work on Windows. 128 | 129 | ### FreeBSD 130 | 131 | This will run on FreeBSD. It does not yet support sticky sockets. Fwmark is mapped to `SO_USER_COOKIE`. 132 | 133 | ### OpenBSD 134 | 135 | This will run on OpenBSD. It does not yet support sticky sockets. Fwmark is mapped to `SO_RTABLE`. Since the tun driver cannot have arbitrary interface names, you must either use `tun[0-9]+` for an explicit interface name or `tun` to have the program select one for you. If you choose `tun` as the interface name, and the environment variable `WG_TUN_NAME_FILE` is defined, then the actual name of the interface chosen by the kernel is written to the file specified by that variable. 136 | 137 | ## Building 138 | 139 | This requires an installation of [go](https://golang.org) ≥ 1.12. 140 | 141 | ``` 142 | $ git clone https://git.zx2c4.com/wireguard-go 143 | $ cd wireguard-go 144 | $ make 145 | ``` 146 | 147 | ## License 148 | 149 | Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved. 150 | 151 | Permission is hereby granted, free of charge, to any person obtaining a copy of 152 | this software and associated documentation files (the "Software"), to deal in 153 | the Software without restriction, including without limitation the rights to 154 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 155 | of the Software, and to permit persons to whom the Software is furnished to do 156 | so, subject to the following conditions: 157 | 158 | The above copyright notice and this permission notice shall be included in all 159 | copies or substantial portions of the Software. 160 | 161 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 162 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 163 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 164 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 165 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 166 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 167 | SOFTWARE. 168 | -------------------------------------------------------------------------------- /device/allowedips.go: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: MIT 2 | * 3 | * Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved. 4 | */ 5 | 6 | package device 7 | 8 | import ( 9 | "errors" 10 | "math/bits" 11 | "net" 12 | "sync" 13 | "unsafe" 14 | ) 15 | 16 | type trieEntry struct { 17 | cidr uint 18 | child [2]*trieEntry 19 | bits net.IP 20 | peer *Peer 21 | 22 | // index of "branching" bit 23 | 24 | bit_at_byte uint 25 | bit_at_shift uint 26 | } 27 | 28 | func isLittleEndian() bool { 29 | one := uint32(1) 30 | return *(*byte)(unsafe.Pointer(&one)) != 0 31 | } 32 | 33 | func swapU32(i uint32) uint32 { 34 | if !isLittleEndian() { 35 | return i 36 | } 37 | 38 | return bits.ReverseBytes32(i) 39 | } 40 | 41 | func swapU64(i uint64) uint64 { 42 | if !isLittleEndian() { 43 | return i 44 | } 45 | 46 | return bits.ReverseBytes64(i) 47 | } 48 | 49 | func commonBits(ip1 net.IP, ip2 net.IP) uint { 50 | size := len(ip1) 51 | if size == net.IPv4len { 52 | a := (*uint32)(unsafe.Pointer(&ip1[0])) 53 | b := (*uint32)(unsafe.Pointer(&ip2[0])) 54 | x := *a ^ *b 55 | return uint(bits.LeadingZeros32(swapU32(x))) 56 | } else if size == net.IPv6len { 57 | a := (*uint64)(unsafe.Pointer(&ip1[0])) 58 | b := (*uint64)(unsafe.Pointer(&ip2[0])) 59 | x := *a ^ *b 60 | if x != 0 { 61 | return uint(bits.LeadingZeros64(swapU64(x))) 62 | } 63 | a = (*uint64)(unsafe.Pointer(&ip1[8])) 64 | b = (*uint64)(unsafe.Pointer(&ip2[8])) 65 | x = *a ^ *b 66 | return 64 + uint(bits.LeadingZeros64(swapU64(x))) 67 | } else { 68 | panic("Wrong size bit string") 69 | } 70 | } 71 | 72 | func (node *trieEntry) removeByPeer(p *Peer) *trieEntry { 73 | if node == nil { 74 | return node 75 | } 76 | 77 | // walk recursively 78 | 79 | node.child[0] = node.child[0].removeByPeer(p) 80 | node.child[1] = node.child[1].removeByPeer(p) 81 | 82 | if node.peer != p { 83 | return node 84 | } 85 | 86 | // remove peer & merge 87 | 88 | node.peer = nil 89 | if node.child[0] == nil { 90 | return node.child[1] 91 | } 92 | return node.child[0] 93 | } 94 | 95 | func (node *trieEntry) choose(ip net.IP) byte { 96 | return (ip[node.bit_at_byte] >> node.bit_at_shift) & 1 97 | } 98 | 99 | func (node *trieEntry) insert(ip net.IP, cidr uint, peer *Peer) *trieEntry { 100 | 101 | // at leaf 102 | 103 | if node == nil { 104 | return &trieEntry{ 105 | bits: ip, 106 | peer: peer, 107 | cidr: cidr, 108 | bit_at_byte: cidr / 8, 109 | bit_at_shift: 7 - (cidr % 8), 110 | } 111 | } 112 | 113 | // traverse deeper 114 | 115 | common := commonBits(node.bits, ip) 116 | if node.cidr <= cidr && common >= node.cidr { 117 | if node.cidr == cidr { 118 | node.peer = peer 119 | return node 120 | } 121 | bit := node.choose(ip) 122 | node.child[bit] = node.child[bit].insert(ip, cidr, peer) 123 | return node 124 | } 125 | 126 | // split node 127 | 128 | newNode := &trieEntry{ 129 | bits: ip, 130 | peer: peer, 131 | cidr: cidr, 132 | bit_at_byte: cidr / 8, 133 | bit_at_shift: 7 - (cidr % 8), 134 | } 135 | 136 | cidr = min(cidr, common) 137 | 138 | // check for shorter prefix 139 | 140 | if newNode.cidr == cidr { 141 | bit := newNode.choose(node.bits) 142 | newNode.child[bit] = node 143 | return newNode 144 | } 145 | 146 | // create new parent for node & newNode 147 | 148 | parent := &trieEntry{ 149 | bits: ip, 150 | peer: nil, 151 | cidr: cidr, 152 | bit_at_byte: cidr / 8, 153 | bit_at_shift: 7 - (cidr % 8), 154 | } 155 | 156 | bit := parent.choose(ip) 157 | parent.child[bit] = newNode 158 | parent.child[bit^1] = node 159 | 160 | return parent 161 | } 162 | 163 | func (node *trieEntry) lookup(ip net.IP) *Peer { 164 | var found *Peer 165 | size := uint(len(ip)) 166 | for node != nil && commonBits(node.bits, ip) >= node.cidr { 167 | if node.peer != nil { 168 | found = node.peer 169 | } 170 | if node.bit_at_byte == size { 171 | break 172 | } 173 | bit := node.choose(ip) 174 | node = node.child[bit] 175 | } 176 | return found 177 | } 178 | 179 | func (node *trieEntry) entriesForPeer(p *Peer, results []net.IPNet) []net.IPNet { 180 | if node == nil { 181 | return results 182 | } 183 | if node.peer == p { 184 | mask := net.CIDRMask(int(node.cidr), len(node.bits)*8) 185 | results = append(results, net.IPNet{ 186 | Mask: mask, 187 | IP: node.bits.Mask(mask), 188 | }) 189 | } 190 | results = node.child[0].entriesForPeer(p, results) 191 | results = node.child[1].entriesForPeer(p, results) 192 | return results 193 | } 194 | 195 | type AllowedIPs struct { 196 | IPv4 *trieEntry 197 | IPv6 *trieEntry 198 | mutex sync.RWMutex 199 | } 200 | 201 | func (table *AllowedIPs) EntriesForPeer(peer *Peer) []net.IPNet { 202 | table.mutex.RLock() 203 | defer table.mutex.RUnlock() 204 | 205 | allowed := make([]net.IPNet, 0, 10) 206 | allowed = table.IPv4.entriesForPeer(peer, allowed) 207 | allowed = table.IPv6.entriesForPeer(peer, allowed) 208 | return allowed 209 | } 210 | 211 | func (table *AllowedIPs) Reset() { 212 | table.mutex.Lock() 213 | defer table.mutex.Unlock() 214 | 215 | table.IPv4 = nil 216 | table.IPv6 = nil 217 | } 218 | 219 | func (table *AllowedIPs) RemoveByPeer(peer *Peer) { 220 | table.mutex.Lock() 221 | defer table.mutex.Unlock() 222 | 223 | table.IPv4 = table.IPv4.removeByPeer(peer) 224 | table.IPv6 = table.IPv6.removeByPeer(peer) 225 | } 226 | 227 | func (table *AllowedIPs) Insert(ip net.IP, cidr uint, peer *Peer) { 228 | table.mutex.Lock() 229 | defer table.mutex.Unlock() 230 | 231 | switch len(ip) { 232 | case net.IPv6len: 233 | table.IPv6 = table.IPv6.insert(ip, cidr, peer) 234 | case net.IPv4len: 235 | table.IPv4 = table.IPv4.insert(ip, cidr, peer) 236 | default: 237 | panic(errors.New("inserting unknown address type")) 238 | } 239 | } 240 | 241 | func (table *AllowedIPs) LookupIPv4(address []byte) *Peer { 242 | table.mutex.RLock() 243 | defer table.mutex.RUnlock() 244 | return table.IPv4.lookup(address) 245 | } 246 | 247 | func (table *AllowedIPs) LookupIPv6(address []byte) *Peer { 248 | table.mutex.RLock() 249 | defer table.mutex.RUnlock() 250 | return table.IPv6.lookup(address) 251 | } 252 | -------------------------------------------------------------------------------- /device/allowedips_rand_test.go: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: MIT 2 | * 3 | * Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved. 4 | */ 5 | 6 | package device 7 | 8 | import ( 9 | "math/rand" 10 | "sort" 11 | "testing" 12 | ) 13 | 14 | const ( 15 | NumberOfPeers = 100 16 | NumberOfAddresses = 250 17 | NumberOfTests = 10000 18 | ) 19 | 20 | type SlowNode struct { 21 | peer *Peer 22 | cidr uint 23 | bits []byte 24 | } 25 | 26 | type SlowRouter []*SlowNode 27 | 28 | func (r SlowRouter) Len() int { 29 | return len(r) 30 | } 31 | 32 | func (r SlowRouter) Less(i, j int) bool { 33 | return r[i].cidr > r[j].cidr 34 | } 35 | 36 | func (r SlowRouter) Swap(i, j int) { 37 | r[i], r[j] = r[j], r[i] 38 | } 39 | 40 | func (r SlowRouter) Insert(addr []byte, cidr uint, peer *Peer) SlowRouter { 41 | for _, t := range r { 42 | if t.cidr == cidr && commonBits(t.bits, addr) >= cidr { 43 | t.peer = peer 44 | t.bits = addr 45 | return r 46 | } 47 | } 48 | r = append(r, &SlowNode{ 49 | cidr: cidr, 50 | bits: addr, 51 | peer: peer, 52 | }) 53 | sort.Sort(r) 54 | return r 55 | } 56 | 57 | func (r SlowRouter) Lookup(addr []byte) *Peer { 58 | for _, t := range r { 59 | common := commonBits(t.bits, addr) 60 | if common >= t.cidr { 61 | return t.peer 62 | } 63 | } 64 | return nil 65 | } 66 | 67 | func TestTrieRandomIPv4(t *testing.T) { 68 | var trie *trieEntry 69 | var slow SlowRouter 70 | var peers []*Peer 71 | 72 | rand.Seed(1) 73 | 74 | const AddressLength = 4 75 | 76 | for n := 0; n < NumberOfPeers; n += 1 { 77 | peers = append(peers, &Peer{}) 78 | } 79 | 80 | for n := 0; n < NumberOfAddresses; n += 1 { 81 | var addr [AddressLength]byte 82 | rand.Read(addr[:]) 83 | cidr := uint(rand.Uint32() % (AddressLength * 8)) 84 | index := rand.Int() % NumberOfPeers 85 | trie = trie.insert(addr[:], cidr, peers[index]) 86 | slow = slow.Insert(addr[:], cidr, peers[index]) 87 | } 88 | 89 | for n := 0; n < NumberOfTests; n += 1 { 90 | var addr [AddressLength]byte 91 | rand.Read(addr[:]) 92 | peer1 := slow.Lookup(addr[:]) 93 | peer2 := trie.lookup(addr[:]) 94 | if peer1 != peer2 { 95 | t.Error("Trie did not match naive implementation, for:", addr) 96 | } 97 | } 98 | } 99 | 100 | func TestTrieRandomIPv6(t *testing.T) { 101 | var trie *trieEntry 102 | var slow SlowRouter 103 | var peers []*Peer 104 | 105 | rand.Seed(1) 106 | 107 | const AddressLength = 16 108 | 109 | for n := 0; n < NumberOfPeers; n += 1 { 110 | peers = append(peers, &Peer{}) 111 | } 112 | 113 | for n := 0; n < NumberOfAddresses; n += 1 { 114 | var addr [AddressLength]byte 115 | rand.Read(addr[:]) 116 | cidr := uint(rand.Uint32() % (AddressLength * 8)) 117 | index := rand.Int() % NumberOfPeers 118 | trie = trie.insert(addr[:], cidr, peers[index]) 119 | slow = slow.Insert(addr[:], cidr, peers[index]) 120 | } 121 | 122 | for n := 0; n < NumberOfTests; n += 1 { 123 | var addr [AddressLength]byte 124 | rand.Read(addr[:]) 125 | peer1 := slow.Lookup(addr[:]) 126 | peer2 := trie.lookup(addr[:]) 127 | if peer1 != peer2 { 128 | t.Error("Trie did not match naive implementation, for:", addr) 129 | } 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /device/allowedips_test.go: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: MIT 2 | * 3 | * Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved. 4 | */ 5 | 6 | package device 7 | 8 | import ( 9 | "math/rand" 10 | "net" 11 | "testing" 12 | ) 13 | 14 | /* Todo: More comprehensive 15 | */ 16 | 17 | type testPairCommonBits struct { 18 | s1 []byte 19 | s2 []byte 20 | match uint 21 | } 22 | 23 | type testPairTrieInsert struct { 24 | key []byte 25 | cidr uint 26 | peer *Peer 27 | } 28 | 29 | type testPairTrieLookup struct { 30 | key []byte 31 | peer *Peer 32 | } 33 | 34 | func printTrie(t *testing.T, p *trieEntry) { 35 | if p == nil { 36 | return 37 | } 38 | t.Log(p) 39 | printTrie(t, p.child[0]) 40 | printTrie(t, p.child[1]) 41 | } 42 | 43 | func TestCommonBits(t *testing.T) { 44 | 45 | tests := []testPairCommonBits{ 46 | {s1: []byte{1, 4, 53, 128}, s2: []byte{0, 0, 0, 0}, match: 7}, 47 | {s1: []byte{0, 4, 53, 128}, s2: []byte{0, 0, 0, 0}, match: 13}, 48 | {s1: []byte{0, 4, 53, 253}, s2: []byte{0, 4, 53, 252}, match: 31}, 49 | {s1: []byte{192, 168, 1, 1}, s2: []byte{192, 169, 1, 1}, match: 15}, 50 | {s1: []byte{65, 168, 1, 1}, s2: []byte{192, 169, 1, 1}, match: 0}, 51 | } 52 | 53 | for _, p := range tests { 54 | v := commonBits(p.s1, p.s2) 55 | if v != p.match { 56 | t.Error( 57 | "For slice", p.s1, p.s2, 58 | "expected match", p.match, 59 | ",but got", v, 60 | ) 61 | } 62 | } 63 | } 64 | 65 | func benchmarkTrie(peerNumber int, addressNumber int, addressLength int, b *testing.B) { 66 | var trie *trieEntry 67 | var peers []*Peer 68 | 69 | rand.Seed(1) 70 | 71 | const AddressLength = 4 72 | 73 | for n := 0; n < peerNumber; n += 1 { 74 | peers = append(peers, &Peer{}) 75 | } 76 | 77 | for n := 0; n < addressNumber; n += 1 { 78 | var addr [AddressLength]byte 79 | rand.Read(addr[:]) 80 | cidr := uint(rand.Uint32() % (AddressLength * 8)) 81 | index := rand.Int() % peerNumber 82 | trie = trie.insert(addr[:], cidr, peers[index]) 83 | } 84 | 85 | for n := 0; n < b.N; n += 1 { 86 | var addr [AddressLength]byte 87 | rand.Read(addr[:]) 88 | trie.lookup(addr[:]) 89 | } 90 | } 91 | 92 | func BenchmarkTrieIPv4Peers100Addresses1000(b *testing.B) { 93 | benchmarkTrie(100, 1000, net.IPv4len, b) 94 | } 95 | 96 | func BenchmarkTrieIPv4Peers10Addresses10(b *testing.B) { 97 | benchmarkTrie(10, 10, net.IPv4len, b) 98 | } 99 | 100 | func BenchmarkTrieIPv6Peers100Addresses1000(b *testing.B) { 101 | benchmarkTrie(100, 1000, net.IPv6len, b) 102 | } 103 | 104 | func BenchmarkTrieIPv6Peers10Addresses10(b *testing.B) { 105 | benchmarkTrie(10, 10, net.IPv6len, b) 106 | } 107 | 108 | /* Test ported from kernel implementation: 109 | * selftest/allowedips.h 110 | */ 111 | func TestTrieIPv4(t *testing.T) { 112 | a := &Peer{} 113 | b := &Peer{} 114 | c := &Peer{} 115 | d := &Peer{} 116 | e := &Peer{} 117 | g := &Peer{} 118 | h := &Peer{} 119 | 120 | var trie *trieEntry 121 | 122 | insert := func(peer *Peer, a, b, c, d byte, cidr uint) { 123 | trie = trie.insert([]byte{a, b, c, d}, cidr, peer) 124 | } 125 | 126 | assertEQ := func(peer *Peer, a, b, c, d byte) { 127 | p := trie.lookup([]byte{a, b, c, d}) 128 | if p != peer { 129 | t.Error("Assert EQ failed") 130 | } 131 | } 132 | 133 | assertNEQ := func(peer *Peer, a, b, c, d byte) { 134 | p := trie.lookup([]byte{a, b, c, d}) 135 | if p == peer { 136 | t.Error("Assert NEQ failed") 137 | } 138 | } 139 | 140 | insert(a, 192, 168, 4, 0, 24) 141 | insert(b, 192, 168, 4, 4, 32) 142 | insert(c, 192, 168, 0, 0, 16) 143 | insert(d, 192, 95, 5, 64, 27) 144 | insert(c, 192, 95, 5, 65, 27) 145 | insert(e, 0, 0, 0, 0, 0) 146 | insert(g, 64, 15, 112, 0, 20) 147 | insert(h, 64, 15, 123, 211, 25) 148 | insert(a, 10, 0, 0, 0, 25) 149 | insert(b, 10, 0, 0, 128, 25) 150 | insert(a, 10, 1, 0, 0, 30) 151 | insert(b, 10, 1, 0, 4, 30) 152 | insert(c, 10, 1, 0, 8, 29) 153 | insert(d, 10, 1, 0, 16, 29) 154 | 155 | assertEQ(a, 192, 168, 4, 20) 156 | assertEQ(a, 192, 168, 4, 0) 157 | assertEQ(b, 192, 168, 4, 4) 158 | assertEQ(c, 192, 168, 200, 182) 159 | assertEQ(c, 192, 95, 5, 68) 160 | assertEQ(e, 192, 95, 5, 96) 161 | assertEQ(g, 64, 15, 116, 26) 162 | assertEQ(g, 64, 15, 127, 3) 163 | 164 | insert(a, 1, 0, 0, 0, 32) 165 | insert(a, 64, 0, 0, 0, 32) 166 | insert(a, 128, 0, 0, 0, 32) 167 | insert(a, 192, 0, 0, 0, 32) 168 | insert(a, 255, 0, 0, 0, 32) 169 | 170 | assertEQ(a, 1, 0, 0, 0) 171 | assertEQ(a, 64, 0, 0, 0) 172 | assertEQ(a, 128, 0, 0, 0) 173 | assertEQ(a, 192, 0, 0, 0) 174 | assertEQ(a, 255, 0, 0, 0) 175 | 176 | trie = trie.removeByPeer(a) 177 | 178 | assertNEQ(a, 1, 0, 0, 0) 179 | assertNEQ(a, 64, 0, 0, 0) 180 | assertNEQ(a, 128, 0, 0, 0) 181 | assertNEQ(a, 192, 0, 0, 0) 182 | assertNEQ(a, 255, 0, 0, 0) 183 | 184 | trie = nil 185 | 186 | insert(a, 192, 168, 0, 0, 16) 187 | insert(a, 192, 168, 0, 0, 24) 188 | 189 | trie = trie.removeByPeer(a) 190 | 191 | assertNEQ(a, 192, 168, 0, 1) 192 | } 193 | 194 | /* Test ported from kernel implementation: 195 | * selftest/allowedips.h 196 | */ 197 | func TestTrieIPv6(t *testing.T) { 198 | a := &Peer{} 199 | b := &Peer{} 200 | c := &Peer{} 201 | d := &Peer{} 202 | e := &Peer{} 203 | f := &Peer{} 204 | g := &Peer{} 205 | h := &Peer{} 206 | 207 | var trie *trieEntry 208 | 209 | expand := func(a uint32) []byte { 210 | var out [4]byte 211 | out[0] = byte(a >> 24 & 0xff) 212 | out[1] = byte(a >> 16 & 0xff) 213 | out[2] = byte(a >> 8 & 0xff) 214 | out[3] = byte(a & 0xff) 215 | return out[:] 216 | } 217 | 218 | insert := func(peer *Peer, a, b, c, d uint32, cidr uint) { 219 | var addr []byte 220 | addr = append(addr, expand(a)...) 221 | addr = append(addr, expand(b)...) 222 | addr = append(addr, expand(c)...) 223 | addr = append(addr, expand(d)...) 224 | trie = trie.insert(addr, cidr, peer) 225 | } 226 | 227 | assertEQ := func(peer *Peer, a, b, c, d uint32) { 228 | var addr []byte 229 | addr = append(addr, expand(a)...) 230 | addr = append(addr, expand(b)...) 231 | addr = append(addr, expand(c)...) 232 | addr = append(addr, expand(d)...) 233 | p := trie.lookup(addr) 234 | if p != peer { 235 | t.Error("Assert EQ failed") 236 | } 237 | } 238 | 239 | insert(d, 0x26075300, 0x60006b00, 0, 0xc05f0543, 128) 240 | insert(c, 0x26075300, 0x60006b00, 0, 0, 64) 241 | insert(e, 0, 0, 0, 0, 0) 242 | insert(f, 0, 0, 0, 0, 0) 243 | insert(g, 0x24046800, 0, 0, 0, 32) 244 | insert(h, 0x24046800, 0x40040800, 0xdeadbeef, 0xdeadbeef, 64) 245 | insert(a, 0x24046800, 0x40040800, 0xdeadbeef, 0xdeadbeef, 128) 246 | insert(c, 0x24446800, 0x40e40800, 0xdeaebeef, 0xdefbeef, 128) 247 | insert(b, 0x24446800, 0xf0e40800, 0xeeaebeef, 0, 98) 248 | 249 | assertEQ(d, 0x26075300, 0x60006b00, 0, 0xc05f0543) 250 | assertEQ(c, 0x26075300, 0x60006b00, 0, 0xc02e01ee) 251 | assertEQ(f, 0x26075300, 0x60006b01, 0, 0) 252 | assertEQ(g, 0x24046800, 0x40040806, 0, 0x1006) 253 | assertEQ(g, 0x24046800, 0x40040806, 0x1234, 0x5678) 254 | assertEQ(f, 0x240467ff, 0x40040806, 0x1234, 0x5678) 255 | assertEQ(f, 0x24046801, 0x40040806, 0x1234, 0x5678) 256 | assertEQ(h, 0x24046800, 0x40040800, 0x1234, 0x5678) 257 | assertEQ(h, 0x24046800, 0x40040800, 0, 0) 258 | assertEQ(h, 0x24046800, 0x40040800, 0x10101010, 0x10101010) 259 | assertEQ(a, 0x24046800, 0x40040800, 0xdeadbeef, 0xdeadbeef) 260 | } 261 | -------------------------------------------------------------------------------- /device/bind_test.go: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: MIT 2 | * 3 | * Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved. 4 | */ 5 | 6 | package device 7 | 8 | import "errors" 9 | 10 | type DummyDatagram struct { 11 | msg []byte 12 | endpoint Endpoint 13 | world bool // better type 14 | } 15 | 16 | type DummyBind struct { 17 | in6 chan DummyDatagram 18 | ou6 chan DummyDatagram 19 | in4 chan DummyDatagram 20 | ou4 chan DummyDatagram 21 | closed bool 22 | } 23 | 24 | func (b *DummyBind) SetMark(v uint32) error { 25 | return nil 26 | } 27 | 28 | func (b *DummyBind) ReceiveIPv6(buff []byte) (int, Endpoint, error) { 29 | datagram, ok := <-b.in6 30 | if !ok { 31 | return 0, nil, errors.New("closed") 32 | } 33 | copy(buff, datagram.msg) 34 | return len(datagram.msg), datagram.endpoint, nil 35 | } 36 | 37 | func (b *DummyBind) ReceiveIPv4(buff []byte) (int, Endpoint, error) { 38 | datagram, ok := <-b.in4 39 | if !ok { 40 | return 0, nil, errors.New("closed") 41 | } 42 | copy(buff, datagram.msg) 43 | return len(datagram.msg), datagram.endpoint, nil 44 | } 45 | 46 | func (b *DummyBind) Close() error { 47 | close(b.in6) 48 | close(b.in4) 49 | b.closed = true 50 | return nil 51 | } 52 | 53 | func (b *DummyBind) Send(buff []byte, end Endpoint) error { 54 | return nil 55 | } 56 | -------------------------------------------------------------------------------- /device/boundif_android.go: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: MIT 2 | * 3 | * Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved. 4 | */ 5 | 6 | package device 7 | 8 | func (device *Device) PeekLookAtSocketFd4() (fd int, err error) { 9 | sysconn, err := device.net.bind.(*nativeBind).ipv4.SyscallConn() 10 | if err != nil { 11 | return 12 | } 13 | err = sysconn.Control(func(f uintptr) { 14 | fd = int(f) 15 | }) 16 | if err != nil { 17 | return 18 | } 19 | return 20 | } 21 | 22 | func (device *Device) PeekLookAtSocketFd6() (fd int, err error) { 23 | sysconn, err := device.net.bind.(*nativeBind).ipv6.SyscallConn() 24 | if err != nil { 25 | return 26 | } 27 | err = sysconn.Control(func(f uintptr) { 28 | fd = int(f) 29 | }) 30 | if err != nil { 31 | return 32 | } 33 | return 34 | } 35 | -------------------------------------------------------------------------------- /device/boundif_darwin.go: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: MIT 2 | * 3 | * Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved. 4 | */ 5 | 6 | package device 7 | 8 | import ( 9 | "golang.org/x/sys/unix" 10 | ) 11 | 12 | func (device *Device) BindSocketToInterface4(interfaceIndex uint32) error { 13 | sysconn, err := device.net.bind.(*nativeBind).ipv4.SyscallConn() 14 | if err != nil { 15 | return nil 16 | } 17 | err2 := sysconn.Control(func(fd uintptr) { 18 | err = unix.SetsockoptInt(int(fd), unix.IPPROTO_IP, unix.IP_BOUND_IF, int(interfaceIndex)) 19 | }) 20 | if err2 != nil { 21 | return err2 22 | } 23 | if err != nil { 24 | return err 25 | } 26 | return nil 27 | } 28 | 29 | func (device *Device) BindSocketToInterface6(interfaceIndex uint32) error { 30 | sysconn, err := device.net.bind.(*nativeBind).ipv4.SyscallConn() 31 | if err != nil { 32 | return nil 33 | } 34 | err2 := sysconn.Control(func(fd uintptr) { 35 | err = unix.SetsockoptInt(int(fd), unix.IPPROTO_IPV6, unix.IPV6_BOUND_IF, int(interfaceIndex)) 36 | }) 37 | if err2 != nil { 38 | return err2 39 | } 40 | if err != nil { 41 | return err 42 | } 43 | return nil 44 | } -------------------------------------------------------------------------------- /device/boundif_windows.go: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: MIT 2 | * 3 | * Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved. 4 | */ 5 | 6 | package device 7 | 8 | import ( 9 | "encoding/binary" 10 | "golang.org/x/sys/windows" 11 | "unsafe" 12 | ) 13 | 14 | const ( 15 | sockoptIP_UNICAST_IF = 31 16 | sockoptIPV6_UNICAST_IF = 31 17 | ) 18 | 19 | func (device *Device) BindSocketToInterface4(interfaceIndex uint32) error { 20 | /* MSDN says for IPv4 this needs to be in net byte order, so that it's like an IP address with leading zeros. */ 21 | bytes := make([]byte, 4) 22 | binary.BigEndian.PutUint32(bytes, interfaceIndex) 23 | interfaceIndex = *(*uint32)(unsafe.Pointer(&bytes[0])) 24 | 25 | sysconn, err := device.net.bind.(*nativeBind).ipv4.SyscallConn() 26 | if err != nil { 27 | return err 28 | } 29 | err2 := sysconn.Control(func(fd uintptr) { 30 | err = windows.SetsockoptInt(windows.Handle(fd), windows.IPPROTO_IP, sockoptIP_UNICAST_IF, int(interfaceIndex)) 31 | }) 32 | if err2 != nil { 33 | return err2 34 | } 35 | if err != nil { 36 | return err 37 | } 38 | return nil 39 | } 40 | 41 | func (device *Device) BindSocketToInterface6(interfaceIndex uint32) error { 42 | sysconn, err := device.net.bind.(*nativeBind).ipv6.SyscallConn() 43 | if err != nil { 44 | return err 45 | } 46 | err2 := sysconn.Control(func(fd uintptr) { 47 | err = windows.SetsockoptInt(windows.Handle(fd), windows.IPPROTO_IPV6, sockoptIPV6_UNICAST_IF, int(interfaceIndex)) 48 | }) 49 | if err2 != nil { 50 | return err2 51 | } 52 | if err != nil { 53 | return err 54 | } 55 | return nil 56 | } -------------------------------------------------------------------------------- /device/conn.go: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: MIT 2 | * 3 | * Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved. 4 | */ 5 | 6 | package device 7 | 8 | import ( 9 | "errors" 10 | "golang.org/x/net/ipv4" 11 | "golang.org/x/net/ipv6" 12 | "net" 13 | ) 14 | 15 | const ( 16 | ConnRoutineNumber = 2 17 | ) 18 | 19 | /* A Bind handles listening on a port for both IPv6 and IPv4 UDP traffic 20 | */ 21 | type Bind interface { 22 | SetMark(value uint32) error 23 | ReceiveIPv6(buff []byte) (int, Endpoint, error) 24 | ReceiveIPv4(buff []byte) (int, Endpoint, error) 25 | Send(buff []byte, end Endpoint) error 26 | Close() error 27 | } 28 | 29 | /* An Endpoint maintains the source/destination caching for a peer 30 | * 31 | * dst : the remote address of a peer ("endpoint" in uapi terminology) 32 | * src : the local address from which datagrams originate going to the peer 33 | */ 34 | type Endpoint interface { 35 | ClearSrc() // clears the source address 36 | SrcToString() string // returns the local source address (ip:port) 37 | DstToString() string // returns the destination address (ip:port) 38 | DstToBytes() []byte // used for mac2 cookie calculations 39 | DstIP() net.IP 40 | SrcIP() net.IP 41 | } 42 | 43 | func parseEndpoint(s string) (*net.UDPAddr, error) { 44 | 45 | // ensure that the host is an IP address 46 | 47 | host, _, err := net.SplitHostPort(s) 48 | if err != nil { 49 | return nil, err 50 | } 51 | if ip := net.ParseIP(host); ip == nil { 52 | return nil, errors.New("Failed to parse IP address: " + host) 53 | } 54 | 55 | // parse address and port 56 | 57 | addr, err := net.ResolveUDPAddr("udp", s) 58 | if err != nil { 59 | return nil, err 60 | } 61 | ip4 := addr.IP.To4() 62 | if ip4 != nil { 63 | addr.IP = ip4 64 | } 65 | return addr, err 66 | } 67 | 68 | func unsafeCloseBind(device *Device) error { 69 | var err error 70 | netc := &device.net 71 | if netc.bind != nil { 72 | err = netc.bind.Close() 73 | netc.bind = nil 74 | } 75 | netc.stopping.Wait() 76 | return err 77 | } 78 | 79 | func (device *Device) BindSetMark(mark uint32) error { 80 | 81 | device.net.Lock() 82 | defer device.net.Unlock() 83 | 84 | // check if modified 85 | 86 | if device.net.fwmark == mark { 87 | return nil 88 | } 89 | 90 | // update fwmark on existing bind 91 | 92 | device.net.fwmark = mark 93 | if device.isUp.Get() && device.net.bind != nil { 94 | if err := device.net.bind.SetMark(mark); err != nil { 95 | return err 96 | } 97 | } 98 | 99 | // clear cached source addresses 100 | 101 | device.peers.RLock() 102 | for _, peer := range device.peers.keyMap { 103 | peer.Lock() 104 | defer peer.Unlock() 105 | if peer.endpoint != nil { 106 | peer.endpoint.ClearSrc() 107 | } 108 | } 109 | device.peers.RUnlock() 110 | 111 | return nil 112 | } 113 | 114 | func (device *Device) BindUpdate() error { 115 | 116 | device.net.Lock() 117 | defer device.net.Unlock() 118 | 119 | // close existing sockets 120 | 121 | if err := unsafeCloseBind(device); err != nil { 122 | return err 123 | } 124 | 125 | // open new sockets 126 | 127 | if device.isUp.Get() { 128 | 129 | // bind to new port 130 | 131 | var err error 132 | netc := &device.net 133 | netc.bind, netc.port, err = CreateBind(netc.port, device) 134 | if err != nil { 135 | netc.bind = nil 136 | netc.port = 0 137 | return err 138 | } 139 | 140 | // set fwmark 141 | 142 | if netc.fwmark != 0 { 143 | err = netc.bind.SetMark(netc.fwmark) 144 | if err != nil { 145 | return err 146 | } 147 | } 148 | 149 | // clear cached source addresses 150 | 151 | device.peers.RLock() 152 | for _, peer := range device.peers.keyMap { 153 | peer.Lock() 154 | defer peer.Unlock() 155 | if peer.endpoint != nil { 156 | peer.endpoint.ClearSrc() 157 | } 158 | } 159 | device.peers.RUnlock() 160 | 161 | // start receiving routines 162 | 163 | device.net.starting.Add(ConnRoutineNumber) 164 | device.net.stopping.Add(ConnRoutineNumber) 165 | go device.RoutineReceiveIncoming(ipv4.Version, netc.bind) 166 | go device.RoutineReceiveIncoming(ipv6.Version, netc.bind) 167 | device.net.starting.Wait() 168 | 169 | device.log.Debug.Println("UDP bind has been updated") 170 | } 171 | 172 | return nil 173 | } 174 | 175 | func (device *Device) BindClose() error { 176 | device.net.Lock() 177 | err := unsafeCloseBind(device) 178 | device.net.Unlock() 179 | return err 180 | } -------------------------------------------------------------------------------- /device/conn_default.go: -------------------------------------------------------------------------------- 1 | // +build !linux android 2 | 3 | /* SPDX-License-Identifier: MIT 4 | * 5 | * Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved. 6 | */ 7 | 8 | package device 9 | 10 | import ( 11 | "net" 12 | "os" 13 | "syscall" 14 | ) 15 | 16 | /* This code is meant to be a temporary solution 17 | * on platforms for which the sticky socket / source caching behavior 18 | * has not yet been implemented. 19 | * 20 | * See conn_linux.go for an implementation on the linux platform. 21 | */ 22 | 23 | type nativeBind struct { 24 | ipv4 *net.UDPConn 25 | ipv6 *net.UDPConn 26 | } 27 | 28 | type NativeEndpoint net.UDPAddr 29 | 30 | var _ Bind = (*nativeBind)(nil) 31 | var _ Endpoint = (*NativeEndpoint)(nil) 32 | 33 | func CreateEndpoint(s string) (Endpoint, error) { 34 | addr, err := parseEndpoint(s) 35 | return (*NativeEndpoint)(addr), err 36 | } 37 | 38 | func (_ *NativeEndpoint) ClearSrc() {} 39 | 40 | func (e *NativeEndpoint) DstIP() net.IP { 41 | return (*net.UDPAddr)(e).IP 42 | } 43 | 44 | func (e *NativeEndpoint) SrcIP() net.IP { 45 | return nil // not supported 46 | } 47 | 48 | func (e *NativeEndpoint) DstToBytes() []byte { 49 | addr := (*net.UDPAddr)(e) 50 | out := addr.IP.To4() 51 | if out == nil { 52 | out = addr.IP 53 | } 54 | out = append(out, byte(addr.Port&0xff)) 55 | out = append(out, byte((addr.Port>>8)&0xff)) 56 | return out 57 | } 58 | 59 | func (e *NativeEndpoint) DstToString() string { 60 | return (*net.UDPAddr)(e).String() 61 | } 62 | 63 | func (e *NativeEndpoint) SrcToString() string { 64 | return "" 65 | } 66 | 67 | func listenNet(network string, port int) (*net.UDPConn, int, error) { 68 | 69 | // listen 70 | 71 | conn, err := net.ListenUDP(network, &net.UDPAddr{Port: port}) 72 | if err != nil { 73 | return nil, 0, err 74 | } 75 | 76 | // retrieve port 77 | 78 | laddr := conn.LocalAddr() 79 | uaddr, err := net.ResolveUDPAddr( 80 | laddr.Network(), 81 | laddr.String(), 82 | ) 83 | if err != nil { 84 | return nil, 0, err 85 | } 86 | return conn, uaddr.Port, nil 87 | } 88 | 89 | func extractErrno(err error) error { 90 | opErr, ok := err.(*net.OpError) 91 | if !ok { 92 | return nil 93 | } 94 | syscallErr, ok := opErr.Err.(*os.SyscallError) 95 | if !ok { 96 | return nil 97 | } 98 | return syscallErr.Err 99 | } 100 | 101 | func CreateBind(uport uint16, device *Device) (Bind, uint16, error) { 102 | var err error 103 | var bind nativeBind 104 | 105 | port := int(uport) 106 | 107 | bind.ipv4, port, err = listenNet("udp4", port) 108 | if err != nil && extractErrno(err) != syscall.EAFNOSUPPORT { 109 | return nil, 0, err 110 | } 111 | 112 | bind.ipv6, port, err = listenNet("udp6", port) 113 | if err != nil && extractErrno(err) != syscall.EAFNOSUPPORT { 114 | bind.ipv4.Close() 115 | bind.ipv4 = nil 116 | return nil, 0, err 117 | } 118 | 119 | return &bind, uint16(port), nil 120 | } 121 | 122 | func (bind *nativeBind) Close() error { 123 | var err1, err2 error 124 | if bind.ipv4 != nil { 125 | err1 = bind.ipv4.Close() 126 | } 127 | if bind.ipv6 != nil { 128 | err2 = bind.ipv6.Close() 129 | } 130 | if err1 != nil { 131 | return err1 132 | } 133 | return err2 134 | } 135 | 136 | func (bind *nativeBind) ReceiveIPv4(buff []byte) (int, Endpoint, error) { 137 | if bind.ipv4 == nil { 138 | return 0, nil, syscall.EAFNOSUPPORT 139 | } 140 | n, endpoint, err := bind.ipv4.ReadFromUDP(buff) 141 | if endpoint != nil { 142 | endpoint.IP = endpoint.IP.To4() 143 | } 144 | return n, (*NativeEndpoint)(endpoint), err 145 | } 146 | 147 | func (bind *nativeBind) ReceiveIPv6(buff []byte) (int, Endpoint, error) { 148 | if bind.ipv6 == nil { 149 | return 0, nil, syscall.EAFNOSUPPORT 150 | } 151 | n, endpoint, err := bind.ipv6.ReadFromUDP(buff) 152 | return n, (*NativeEndpoint)(endpoint), err 153 | } 154 | 155 | func (bind *nativeBind) Send(buff []byte, endpoint Endpoint) error { 156 | var err error 157 | nend := endpoint.(*NativeEndpoint) 158 | if nend.IP.To4() != nil { 159 | if bind.ipv4 == nil { 160 | return syscall.EAFNOSUPPORT 161 | } 162 | _, err = bind.ipv4.WriteToUDP(buff, (*net.UDPAddr)(nend)) 163 | } else { 164 | if bind.ipv6 == nil { 165 | return syscall.EAFNOSUPPORT 166 | } 167 | _, err = bind.ipv6.WriteToUDP(buff, (*net.UDPAddr)(nend)) 168 | } 169 | return err 170 | } 171 | -------------------------------------------------------------------------------- /device/constants.go: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: MIT 2 | * 3 | * Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved. 4 | */ 5 | 6 | package device 7 | 8 | import ( 9 | "time" 10 | ) 11 | 12 | /* Specification constants */ 13 | 14 | const ( 15 | RekeyAfterMessages = (1 << 64) - (1 << 16) - 1 16 | RejectAfterMessages = (1 << 64) - (1 << 4) - 1 17 | RekeyAfterTime = time.Second * 120 18 | RekeyAttemptTime = time.Second * 90 19 | RekeyTimeout = time.Second * 5 20 | MaxTimerHandshakes = 90 / 5 /* RekeyAttemptTime / RekeyTimeout */ 21 | RekeyTimeoutJitterMaxMs = 334 22 | RejectAfterTime = time.Second * 180 23 | KeepaliveTimeout = time.Second * 10 24 | CookieRefreshTime = time.Second * 120 25 | HandshakeInitationRate = time.Second / 20 26 | PaddingMultiple = 16 27 | ) 28 | 29 | const ( 30 | MinMessageSize = MessageKeepaliveSize // minimum size of transport message (keepalive) 31 | MaxMessageSize = MaxSegmentSize // maximum size of transport message 32 | MaxContentSize = MaxSegmentSize - MessageTransportSize // maximum size of transport message content 33 | ) 34 | 35 | /* Implementation constants */ 36 | 37 | const ( 38 | UnderLoadQueueSize = QueueHandshakeSize / 8 39 | UnderLoadAfterTime = time.Second // how long does the device remain under load after detected 40 | MaxPeers = 1 << 16 // maximum number of configured peers 41 | ) 42 | -------------------------------------------------------------------------------- /device/cookie.go: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: MIT 2 | * 3 | * Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved. 4 | */ 5 | 6 | package device 7 | 8 | import ( 9 | "crypto/hmac" 10 | "crypto/rand" 11 | "golang.org/x/crypto/blake2s" 12 | "golang.org/x/crypto/chacha20poly1305" 13 | "sync" 14 | "time" 15 | ) 16 | 17 | type CookieChecker struct { 18 | sync.RWMutex 19 | mac1 struct { 20 | key [blake2s.Size]byte 21 | } 22 | mac2 struct { 23 | secret [blake2s.Size]byte 24 | secretSet time.Time 25 | encryptionKey [chacha20poly1305.KeySize]byte 26 | } 27 | } 28 | 29 | type CookieGenerator struct { 30 | sync.RWMutex 31 | mac1 struct { 32 | key [blake2s.Size]byte 33 | } 34 | mac2 struct { 35 | cookie [blake2s.Size128]byte 36 | cookieSet time.Time 37 | hasLastMAC1 bool 38 | lastMAC1 [blake2s.Size128]byte 39 | encryptionKey [chacha20poly1305.KeySize]byte 40 | } 41 | } 42 | 43 | func (st *CookieChecker) Init(pk NoisePublicKey) { 44 | st.Lock() 45 | defer st.Unlock() 46 | 47 | // mac1 state 48 | 49 | func() { 50 | hash, _ := blake2s.New256(nil) 51 | hash.Write([]byte(WGLabelMAC1)) 52 | hash.Write(pk[:]) 53 | hash.Sum(st.mac1.key[:0]) 54 | }() 55 | 56 | // mac2 state 57 | 58 | func() { 59 | hash, _ := blake2s.New256(nil) 60 | hash.Write([]byte(WGLabelCookie)) 61 | hash.Write(pk[:]) 62 | hash.Sum(st.mac2.encryptionKey[:0]) 63 | }() 64 | 65 | st.mac2.secretSet = time.Time{} 66 | } 67 | 68 | func (st *CookieChecker) CheckMAC1(msg []byte) bool { 69 | st.RLock() 70 | defer st.RUnlock() 71 | 72 | size := len(msg) 73 | smac2 := size - blake2s.Size128 74 | smac1 := smac2 - blake2s.Size128 75 | 76 | var mac1 [blake2s.Size128]byte 77 | 78 | mac, _ := blake2s.New128(st.mac1.key[:]) 79 | mac.Write(msg[:smac1]) 80 | mac.Sum(mac1[:0]) 81 | 82 | return hmac.Equal(mac1[:], msg[smac1:smac2]) 83 | } 84 | 85 | func (st *CookieChecker) CheckMAC2(msg []byte, src []byte) bool { 86 | st.RLock() 87 | defer st.RUnlock() 88 | 89 | if time.Now().Sub(st.mac2.secretSet) > CookieRefreshTime { 90 | return false 91 | } 92 | 93 | // derive cookie key 94 | 95 | var cookie [blake2s.Size128]byte 96 | func() { 97 | mac, _ := blake2s.New128(st.mac2.secret[:]) 98 | mac.Write(src) 99 | mac.Sum(cookie[:0]) 100 | }() 101 | 102 | // calculate mac of packet (including mac1) 103 | 104 | smac2 := len(msg) - blake2s.Size128 105 | 106 | var mac2 [blake2s.Size128]byte 107 | func() { 108 | mac, _ := blake2s.New128(cookie[:]) 109 | mac.Write(msg[:smac2]) 110 | mac.Sum(mac2[:0]) 111 | }() 112 | 113 | return hmac.Equal(mac2[:], msg[smac2:]) 114 | } 115 | 116 | func (st *CookieChecker) CreateReply( 117 | msg []byte, 118 | recv uint32, 119 | src []byte, 120 | ) (*MessageCookieReply, error) { 121 | 122 | st.RLock() 123 | 124 | // refresh cookie secret 125 | 126 | if time.Now().Sub(st.mac2.secretSet) > CookieRefreshTime { 127 | st.RUnlock() 128 | st.Lock() 129 | _, err := rand.Read(st.mac2.secret[:]) 130 | if err != nil { 131 | st.Unlock() 132 | return nil, err 133 | } 134 | st.mac2.secretSet = time.Now() 135 | st.Unlock() 136 | st.RLock() 137 | } 138 | 139 | // derive cookie 140 | 141 | var cookie [blake2s.Size128]byte 142 | func() { 143 | mac, _ := blake2s.New128(st.mac2.secret[:]) 144 | mac.Write(src) 145 | mac.Sum(cookie[:0]) 146 | }() 147 | 148 | // encrypt cookie 149 | 150 | size := len(msg) 151 | 152 | smac2 := size - blake2s.Size128 153 | smac1 := smac2 - blake2s.Size128 154 | 155 | reply := new(MessageCookieReply) 156 | reply.Type = MessageCookieReplyType 157 | reply.Receiver = recv 158 | 159 | _, err := rand.Read(reply.Nonce[:]) 160 | if err != nil { 161 | st.RUnlock() 162 | return nil, err 163 | } 164 | 165 | xchapoly, _ := chacha20poly1305.NewX(st.mac2.encryptionKey[:]) 166 | xchapoly.Seal(reply.Cookie[:0], reply.Nonce[:], cookie[:], msg[smac1:smac2]) 167 | 168 | st.RUnlock() 169 | 170 | return reply, nil 171 | } 172 | 173 | func (st *CookieGenerator) Init(pk NoisePublicKey) { 174 | st.Lock() 175 | defer st.Unlock() 176 | 177 | func() { 178 | hash, _ := blake2s.New256(nil) 179 | hash.Write([]byte(WGLabelMAC1)) 180 | hash.Write(pk[:]) 181 | hash.Sum(st.mac1.key[:0]) 182 | }() 183 | 184 | func() { 185 | hash, _ := blake2s.New256(nil) 186 | hash.Write([]byte(WGLabelCookie)) 187 | hash.Write(pk[:]) 188 | hash.Sum(st.mac2.encryptionKey[:0]) 189 | }() 190 | 191 | st.mac2.cookieSet = time.Time{} 192 | } 193 | 194 | func (st *CookieGenerator) ConsumeReply(msg *MessageCookieReply) bool { 195 | st.Lock() 196 | defer st.Unlock() 197 | 198 | if !st.mac2.hasLastMAC1 { 199 | return false 200 | } 201 | 202 | var cookie [blake2s.Size128]byte 203 | 204 | xchapoly, _ := chacha20poly1305.NewX(st.mac2.encryptionKey[:]) 205 | _, err := xchapoly.Open(cookie[:0], msg.Nonce[:], msg.Cookie[:], st.mac2.lastMAC1[:]) 206 | 207 | if err != nil { 208 | return false 209 | } 210 | 211 | st.mac2.cookieSet = time.Now() 212 | st.mac2.cookie = cookie 213 | return true 214 | } 215 | 216 | func (st *CookieGenerator) AddMacs(msg []byte) { 217 | 218 | size := len(msg) 219 | 220 | smac2 := size - blake2s.Size128 221 | smac1 := smac2 - blake2s.Size128 222 | 223 | mac1 := msg[smac1:smac2] 224 | mac2 := msg[smac2:] 225 | 226 | st.Lock() 227 | defer st.Unlock() 228 | 229 | // set mac1 230 | 231 | func() { 232 | mac, _ := blake2s.New128(st.mac1.key[:]) 233 | mac.Write(msg[:smac1]) 234 | mac.Sum(mac1[:0]) 235 | }() 236 | copy(st.mac2.lastMAC1[:], mac1) 237 | st.mac2.hasLastMAC1 = true 238 | 239 | // set mac2 240 | 241 | if time.Now().Sub(st.mac2.cookieSet) > CookieRefreshTime { 242 | return 243 | } 244 | 245 | func() { 246 | mac, _ := blake2s.New128(st.mac2.cookie[:]) 247 | mac.Write(msg[:smac2]) 248 | mac.Sum(mac2[:0]) 249 | }() 250 | } 251 | -------------------------------------------------------------------------------- /device/cookie_test.go: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: MIT 2 | * 3 | * Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved. 4 | */ 5 | 6 | package device 7 | 8 | import ( 9 | "testing" 10 | ) 11 | 12 | func TestCookieMAC1(t *testing.T) { 13 | 14 | // setup generator / checker 15 | 16 | var ( 17 | generator CookieGenerator 18 | checker CookieChecker 19 | ) 20 | 21 | sk, err := newPrivateKey() 22 | if err != nil { 23 | t.Fatal(err) 24 | } 25 | pk := sk.publicKey() 26 | 27 | generator.Init(pk) 28 | checker.Init(pk) 29 | 30 | // check mac1 31 | 32 | src := []byte{192, 168, 13, 37, 10, 10, 10} 33 | 34 | checkMAC1 := func(msg []byte) { 35 | generator.AddMacs(msg) 36 | if !checker.CheckMAC1(msg) { 37 | t.Fatal("MAC1 generation/verification failed") 38 | } 39 | if checker.CheckMAC2(msg, src) { 40 | t.Fatal("MAC2 generation/verification failed") 41 | } 42 | } 43 | 44 | checkMAC1([]byte{ 45 | 0x99, 0xbb, 0xa5, 0xfc, 0x99, 0xaa, 0x83, 0xbd, 46 | 0x7b, 0x00, 0xc5, 0x9a, 0x4c, 0xb9, 0xcf, 0x62, 47 | 0x40, 0x23, 0xf3, 0x8e, 0xd8, 0xd0, 0x62, 0x64, 48 | 0x5d, 0xb2, 0x80, 0x13, 0xda, 0xce, 0xc6, 0x91, 49 | 0x61, 0xd6, 0x30, 0xf1, 0x32, 0xb3, 0xa2, 0xf4, 50 | 0x7b, 0x43, 0xb5, 0xa7, 0xe2, 0xb1, 0xf5, 0x6c, 51 | 0x74, 0x6b, 0xb0, 0xcd, 0x1f, 0x94, 0x86, 0x7b, 52 | 0xc8, 0xfb, 0x92, 0xed, 0x54, 0x9b, 0x44, 0xf5, 53 | 0xc8, 0x7d, 0xb7, 0x8e, 0xff, 0x49, 0xc4, 0xe8, 54 | 0x39, 0x7c, 0x19, 0xe0, 0x60, 0x19, 0x51, 0xf8, 55 | 0xe4, 0x8e, 0x02, 0xf1, 0x7f, 0x1d, 0xcc, 0x8e, 56 | 0xb0, 0x07, 0xff, 0xf8, 0xaf, 0x7f, 0x66, 0x82, 57 | 0x83, 0xcc, 0x7c, 0xfa, 0x80, 0xdb, 0x81, 0x53, 58 | 0xad, 0xf7, 0xd8, 0x0c, 0x10, 0xe0, 0x20, 0xfd, 59 | 0xe8, 0x0b, 0x3f, 0x90, 0x15, 0xcd, 0x93, 0xad, 60 | 0x0b, 0xd5, 0x0c, 0xcc, 0x88, 0x56, 0xe4, 0x3f, 61 | }) 62 | 63 | checkMAC1([]byte{ 64 | 0x33, 0xe7, 0x2a, 0x84, 0x9f, 0xff, 0x57, 0x6c, 65 | 0x2d, 0xc3, 0x2d, 0xe1, 0xf5, 0x5c, 0x97, 0x56, 66 | 0xb8, 0x93, 0xc2, 0x7d, 0xd4, 0x41, 0xdd, 0x7a, 67 | 0x4a, 0x59, 0x3b, 0x50, 0xdd, 0x7a, 0x7a, 0x8c, 68 | 0x9b, 0x96, 0xaf, 0x55, 0x3c, 0xeb, 0x6d, 0x0b, 69 | 0x13, 0x0b, 0x97, 0x98, 0xb3, 0x40, 0xc3, 0xcc, 70 | 0xb8, 0x57, 0x33, 0x45, 0x6e, 0x8b, 0x09, 0x2b, 71 | 0x81, 0x2e, 0xd2, 0xb9, 0x66, 0x0b, 0x93, 0x05, 72 | }) 73 | 74 | checkMAC1([]byte{ 75 | 0x9b, 0x96, 0xaf, 0x55, 0x3c, 0xeb, 0x6d, 0x0b, 76 | 0x13, 0x0b, 0x97, 0x98, 0xb3, 0x40, 0xc3, 0xcc, 77 | 0xb8, 0x57, 0x33, 0x45, 0x6e, 0x8b, 0x09, 0x2b, 78 | 0x81, 0x2e, 0xd2, 0xb9, 0x66, 0x0b, 0x93, 0x05, 79 | }) 80 | 81 | // exchange cookie reply 82 | 83 | func() { 84 | msg := []byte{ 85 | 0x6d, 0xd7, 0xc3, 0x2e, 0xb0, 0x76, 0xd8, 0xdf, 86 | 0x30, 0x65, 0x7d, 0x62, 0x3e, 0xf8, 0x9a, 0xe8, 87 | 0xe7, 0x3c, 0x64, 0xa3, 0x78, 0x48, 0xda, 0xf5, 88 | 0x25, 0x61, 0x28, 0x53, 0x79, 0x32, 0x86, 0x9f, 89 | 0xa0, 0x27, 0x95, 0x69, 0xb6, 0xba, 0xd0, 0xa2, 90 | 0xf8, 0x68, 0xea, 0xa8, 0x62, 0xf2, 0xfd, 0x1b, 91 | 0xe0, 0xb4, 0x80, 0xe5, 0x6b, 0x3a, 0x16, 0x9e, 92 | 0x35, 0xf6, 0xa8, 0xf2, 0x4f, 0x9a, 0x7b, 0xe9, 93 | 0x77, 0x0b, 0xc2, 0xb4, 0xed, 0xba, 0xf9, 0x22, 94 | 0xc3, 0x03, 0x97, 0x42, 0x9f, 0x79, 0x74, 0x27, 95 | 0xfe, 0xf9, 0x06, 0x6e, 0x97, 0x3a, 0xa6, 0x8f, 96 | 0xc9, 0x57, 0x0a, 0x54, 0x4c, 0x64, 0x4a, 0xe2, 97 | 0x4f, 0xa1, 0xce, 0x95, 0x9b, 0x23, 0xa9, 0x2b, 98 | 0x85, 0x93, 0x42, 0xb0, 0xa5, 0x53, 0xed, 0xeb, 99 | 0x63, 0x2a, 0xf1, 0x6d, 0x46, 0xcb, 0x2f, 0x61, 100 | 0x8c, 0xe1, 0xe8, 0xfa, 0x67, 0x20, 0x80, 0x6d, 101 | } 102 | generator.AddMacs(msg) 103 | reply, err := checker.CreateReply(msg, 1377, src) 104 | if err != nil { 105 | t.Fatal("Failed to create cookie reply:", err) 106 | } 107 | if !generator.ConsumeReply(reply) { 108 | t.Fatal("Failed to consume cookie reply") 109 | } 110 | }() 111 | 112 | // check mac2 113 | 114 | checkMAC2 := func(msg []byte) { 115 | generator.AddMacs(msg) 116 | 117 | if !checker.CheckMAC1(msg) { 118 | t.Fatal("MAC1 generation/verification failed") 119 | } 120 | if !checker.CheckMAC2(msg, src) { 121 | t.Fatal("MAC2 generation/verification failed") 122 | } 123 | 124 | msg[5] ^= 0x20 125 | 126 | if checker.CheckMAC1(msg) { 127 | t.Fatal("MAC1 generation/verification failed") 128 | } 129 | if checker.CheckMAC2(msg, src) { 130 | t.Fatal("MAC2 generation/verification failed") 131 | } 132 | 133 | msg[5] ^= 0x20 134 | 135 | srcBad1 := []byte{192, 168, 13, 37, 40, 01} 136 | if checker.CheckMAC2(msg, srcBad1) { 137 | t.Fatal("MAC2 generation/verification failed") 138 | } 139 | 140 | srcBad2 := []byte{192, 168, 13, 38, 40, 01} 141 | if checker.CheckMAC2(msg, srcBad2) { 142 | t.Fatal("MAC2 generation/verification failed") 143 | } 144 | } 145 | 146 | checkMAC2([]byte{ 147 | 0x03, 0x31, 0xb9, 0x9e, 0xb0, 0x2a, 0x54, 0xa3, 148 | 0xc1, 0x3f, 0xb4, 0x96, 0x16, 0xb9, 0x25, 0x15, 149 | 0x3d, 0x3a, 0x82, 0xf9, 0x58, 0x36, 0x86, 0x3f, 150 | 0x13, 0x2f, 0xfe, 0xb2, 0x53, 0x20, 0x8c, 0x3f, 151 | 0xba, 0xeb, 0xfb, 0x4b, 0x1b, 0x22, 0x02, 0x69, 152 | 0x2c, 0x90, 0xbc, 0xdc, 0xcf, 0xcf, 0x85, 0xeb, 153 | 0x62, 0x66, 0x6f, 0xe8, 0xe1, 0xa6, 0xa8, 0x4c, 154 | 0xa0, 0x04, 0x23, 0x15, 0x42, 0xac, 0xfa, 0x38, 155 | }) 156 | 157 | checkMAC2([]byte{ 158 | 0x0e, 0x2f, 0x0e, 0xa9, 0x29, 0x03, 0xe1, 0xf3, 159 | 0x24, 0x01, 0x75, 0xad, 0x16, 0xa5, 0x66, 0x85, 160 | 0xca, 0x66, 0xe0, 0xbd, 0xc6, 0x34, 0xd8, 0x84, 161 | 0x09, 0x9a, 0x58, 0x14, 0xfb, 0x05, 0xda, 0xf5, 162 | 0x90, 0xf5, 0x0c, 0x4e, 0x22, 0x10, 0xc9, 0x85, 163 | 0x0f, 0xe3, 0x77, 0x35, 0xe9, 0x6b, 0xc2, 0x55, 164 | 0x32, 0x46, 0xae, 0x25, 0xe0, 0xe3, 0x37, 0x7a, 165 | 0x4b, 0x71, 0xcc, 0xfc, 0x91, 0xdf, 0xd6, 0xca, 166 | 0xfe, 0xee, 0xce, 0x3f, 0x77, 0xa2, 0xfd, 0x59, 167 | 0x8e, 0x73, 0x0a, 0x8d, 0x5c, 0x24, 0x14, 0xca, 168 | 0x38, 0x91, 0xb8, 0x2c, 0x8c, 0xa2, 0x65, 0x7b, 169 | 0xbc, 0x49, 0xbc, 0xb5, 0x58, 0xfc, 0xe3, 0xd7, 170 | 0x02, 0xcf, 0xf7, 0x4c, 0x60, 0x91, 0xed, 0x55, 171 | 0xe9, 0xf9, 0xfe, 0xd1, 0x44, 0x2c, 0x75, 0xf2, 172 | 0xb3, 0x5d, 0x7b, 0x27, 0x56, 0xc0, 0x48, 0x4f, 173 | 0xb0, 0xba, 0xe4, 0x7d, 0xd0, 0xaa, 0xcd, 0x3d, 174 | 0xe3, 0x50, 0xd2, 0xcf, 0xb9, 0xfa, 0x4b, 0x2d, 175 | 0xc6, 0xdf, 0x3b, 0x32, 0x98, 0x45, 0xe6, 0x8f, 176 | 0x1c, 0x5c, 0xa2, 0x20, 0x7d, 0x1c, 0x28, 0xc2, 177 | 0xd4, 0xa1, 0xe0, 0x21, 0x52, 0x8f, 0x1c, 0xd0, 178 | 0x62, 0x97, 0x48, 0xbb, 0xf4, 0xa9, 0xcb, 0x35, 179 | 0xf2, 0x07, 0xd3, 0x50, 0xd8, 0xa9, 0xc5, 0x9a, 180 | 0x0f, 0xbd, 0x37, 0xaf, 0xe1, 0x45, 0x19, 0xee, 181 | 0x41, 0xf3, 0xf7, 0xe5, 0xe0, 0x30, 0x3f, 0xbe, 182 | 0x3d, 0x39, 0x64, 0x00, 0x7a, 0x1a, 0x51, 0x5e, 183 | 0xe1, 0x70, 0x0b, 0xb9, 0x77, 0x5a, 0xf0, 0xc4, 184 | 0x8a, 0xa1, 0x3a, 0x77, 0x1a, 0xe0, 0xc2, 0x06, 185 | 0x91, 0xd5, 0xe9, 0x1c, 0xd3, 0xfe, 0xab, 0x93, 186 | 0x1a, 0x0a, 0x4c, 0xbb, 0xf0, 0xff, 0xdc, 0xaa, 187 | 0x61, 0x73, 0xcb, 0x03, 0x4b, 0x71, 0x68, 0x64, 188 | 0x3d, 0x82, 0x31, 0x41, 0xd7, 0x8b, 0x22, 0x7b, 189 | 0x7d, 0xa1, 0xd5, 0x85, 0x6d, 0xf0, 0x1b, 0xaa, 190 | }) 191 | } 192 | -------------------------------------------------------------------------------- /device/device.go: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: MIT 2 | * 3 | * Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved. 4 | */ 5 | 6 | package device 7 | 8 | import ( 9 | "golang.zx2c4.com/wireguard/ratelimiter" 10 | "golang.zx2c4.com/wireguard/tun" 11 | "runtime" 12 | "sync" 13 | "sync/atomic" 14 | "time" 15 | ) 16 | 17 | const ( 18 | DeviceRoutineNumberPerCPU = 3 19 | DeviceRoutineNumberAdditional = 2 20 | ) 21 | 22 | type Device struct { 23 | isUp AtomicBool // device is (going) up 24 | isClosed AtomicBool // device is closed? (acting as guard) 25 | log *Logger 26 | 27 | // synchronized resources (locks acquired in order) 28 | 29 | state struct { 30 | starting sync.WaitGroup 31 | stopping sync.WaitGroup 32 | sync.Mutex 33 | changing AtomicBool 34 | current bool 35 | } 36 | 37 | net struct { 38 | starting sync.WaitGroup 39 | stopping sync.WaitGroup 40 | sync.RWMutex 41 | bind Bind // bind interface 42 | port uint16 // listening port 43 | fwmark uint32 // mark value (0 = disabled) 44 | } 45 | 46 | staticIdentity struct { 47 | sync.RWMutex 48 | privateKey NoisePrivateKey 49 | publicKey NoisePublicKey 50 | } 51 | 52 | peers struct { 53 | sync.RWMutex 54 | keyMap map[NoisePublicKey]*Peer 55 | } 56 | 57 | // unprotected / "self-synchronising resources" 58 | 59 | allowedips AllowedIPs 60 | indexTable IndexTable 61 | cookieChecker CookieChecker 62 | 63 | rate struct { 64 | underLoadUntil atomic.Value 65 | limiter ratelimiter.Ratelimiter 66 | } 67 | 68 | pool struct { 69 | messageBufferPool *sync.Pool 70 | messageBufferReuseChan chan *[MaxMessageSize]byte 71 | inboundElementPool *sync.Pool 72 | inboundElementReuseChan chan *QueueInboundElement 73 | outboundElementPool *sync.Pool 74 | outboundElementReuseChan chan *QueueOutboundElement 75 | } 76 | 77 | queue struct { 78 | encryption chan *QueueOutboundElement 79 | decryption chan *QueueInboundElement 80 | handshake chan QueueHandshakeElement 81 | } 82 | 83 | signals struct { 84 | stop chan struct{} 85 | } 86 | 87 | tun struct { 88 | device tun.TUNDevice 89 | mtu int32 90 | } 91 | } 92 | 93 | /* Converts the peer into a "zombie", which remains in the peer map, 94 | * but processes no packets and does not exists in the routing table. 95 | * 96 | * Must hold device.peers.Mutex 97 | */ 98 | func unsafeRemovePeer(device *Device, peer *Peer, key NoisePublicKey) { 99 | 100 | // stop routing and processing of packets 101 | 102 | device.allowedips.RemoveByPeer(peer) 103 | peer.Stop() 104 | 105 | // remove from peer map 106 | 107 | delete(device.peers.keyMap, key) 108 | } 109 | 110 | func deviceUpdateState(device *Device) { 111 | 112 | // check if state already being updated (guard) 113 | 114 | if device.state.changing.Swap(true) { 115 | return 116 | } 117 | 118 | // compare to current state of device 119 | 120 | device.state.Lock() 121 | 122 | newIsUp := device.isUp.Get() 123 | 124 | if newIsUp == device.state.current { 125 | device.state.changing.Set(false) 126 | device.state.Unlock() 127 | return 128 | } 129 | 130 | // change state of device 131 | 132 | switch newIsUp { 133 | case true: 134 | if err := device.BindUpdate(); err != nil { 135 | device.isUp.Set(false) 136 | break 137 | } 138 | device.peers.RLock() 139 | for _, peer := range device.peers.keyMap { 140 | peer.Start() 141 | if peer.persistentKeepaliveInterval > 0 { 142 | peer.SendKeepalive() 143 | } 144 | } 145 | device.peers.RUnlock() 146 | 147 | case false: 148 | device.BindClose() 149 | device.peers.RLock() 150 | for _, peer := range device.peers.keyMap { 151 | peer.Stop() 152 | } 153 | device.peers.RUnlock() 154 | } 155 | 156 | // update state variables 157 | 158 | device.state.current = newIsUp 159 | device.state.changing.Set(false) 160 | device.state.Unlock() 161 | 162 | // check for state change in the mean time 163 | 164 | deviceUpdateState(device) 165 | } 166 | 167 | func (device *Device) Up() { 168 | 169 | // closed device cannot be brought up 170 | 171 | if device.isClosed.Get() { 172 | return 173 | } 174 | 175 | device.isUp.Set(true) 176 | deviceUpdateState(device) 177 | } 178 | 179 | func (device *Device) Down() { 180 | device.isUp.Set(false) 181 | deviceUpdateState(device) 182 | } 183 | 184 | func (device *Device) IsUnderLoad() bool { 185 | 186 | // check if currently under load 187 | 188 | now := time.Now() 189 | underLoad := len(device.queue.handshake) >= UnderLoadQueueSize 190 | if underLoad { 191 | device.rate.underLoadUntil.Store(now.Add(UnderLoadAfterTime)) 192 | return true 193 | } 194 | 195 | // check if recently under load 196 | 197 | until := device.rate.underLoadUntil.Load().(time.Time) 198 | return until.After(now) 199 | } 200 | 201 | func (device *Device) SetPrivateKey(sk NoisePrivateKey) error { 202 | 203 | // lock required resources 204 | 205 | device.staticIdentity.Lock() 206 | defer device.staticIdentity.Unlock() 207 | 208 | device.peers.Lock() 209 | defer device.peers.Unlock() 210 | 211 | for _, peer := range device.peers.keyMap { 212 | peer.handshake.mutex.RLock() 213 | defer peer.handshake.mutex.RUnlock() 214 | } 215 | 216 | // remove peers with matching public keys 217 | 218 | publicKey := sk.publicKey() 219 | for key, peer := range device.peers.keyMap { 220 | if peer.handshake.remoteStatic.Equals(publicKey) { 221 | unsafeRemovePeer(device, peer, key) 222 | } 223 | } 224 | 225 | // update key material 226 | 227 | device.staticIdentity.privateKey = sk 228 | device.staticIdentity.publicKey = publicKey 229 | device.cookieChecker.Init(publicKey) 230 | 231 | // do static-static DH pre-computations 232 | 233 | rmKey := device.staticIdentity.privateKey.IsZero() 234 | 235 | for key, peer := range device.peers.keyMap { 236 | 237 | handshake := &peer.handshake 238 | 239 | if rmKey { 240 | handshake.precomputedStaticStatic = [NoisePublicKeySize]byte{} 241 | } else { 242 | handshake.precomputedStaticStatic = device.staticIdentity.privateKey.sharedSecret(handshake.remoteStatic) 243 | } 244 | 245 | if isZero(handshake.precomputedStaticStatic[:]) { 246 | unsafeRemovePeer(device, peer, key) 247 | } 248 | } 249 | 250 | return nil 251 | } 252 | 253 | func NewDevice(tunDevice tun.TUNDevice, logger *Logger) *Device { 254 | device := new(Device) 255 | 256 | device.isUp.Set(false) 257 | device.isClosed.Set(false) 258 | 259 | device.log = logger 260 | 261 | device.tun.device = tunDevice 262 | mtu, err := device.tun.device.MTU() 263 | if err != nil { 264 | logger.Error.Println("Trouble determining MTU, assuming default:", err) 265 | mtu = DefaultMTU 266 | } 267 | device.tun.mtu = int32(mtu) 268 | 269 | device.peers.keyMap = make(map[NoisePublicKey]*Peer) 270 | 271 | device.rate.limiter.Init() 272 | device.rate.underLoadUntil.Store(time.Time{}) 273 | 274 | device.indexTable.Init() 275 | device.allowedips.Reset() 276 | 277 | device.PopulatePools() 278 | 279 | // create queues 280 | 281 | device.queue.handshake = make(chan QueueHandshakeElement, QueueHandshakeSize) 282 | device.queue.encryption = make(chan *QueueOutboundElement, QueueOutboundSize) 283 | device.queue.decryption = make(chan *QueueInboundElement, QueueInboundSize) 284 | 285 | // prepare signals 286 | 287 | device.signals.stop = make(chan struct{}) 288 | 289 | // prepare net 290 | 291 | device.net.port = 0 292 | device.net.bind = nil 293 | 294 | // start workers 295 | 296 | cpus := runtime.NumCPU() 297 | device.state.starting.Wait() 298 | device.state.stopping.Wait() 299 | device.state.stopping.Add(DeviceRoutineNumberPerCPU*cpus + DeviceRoutineNumberAdditional) 300 | device.state.starting.Add(DeviceRoutineNumberPerCPU*cpus + DeviceRoutineNumberAdditional) 301 | for i := 0; i < cpus; i += 1 { 302 | go device.RoutineEncryption() 303 | go device.RoutineDecryption() 304 | go device.RoutineHandshake() 305 | } 306 | 307 | go device.RoutineReadFromTUN() 308 | go device.RoutineTUNEventReader() 309 | 310 | device.state.starting.Wait() 311 | 312 | return device 313 | } 314 | 315 | func (device *Device) LookupPeer(pk NoisePublicKey) *Peer { 316 | device.peers.RLock() 317 | defer device.peers.RUnlock() 318 | 319 | return device.peers.keyMap[pk] 320 | } 321 | 322 | func (device *Device) RemovePeer(key NoisePublicKey) { 323 | device.peers.Lock() 324 | defer device.peers.Unlock() 325 | 326 | // stop peer and remove from routing 327 | 328 | peer, ok := device.peers.keyMap[key] 329 | if ok { 330 | unsafeRemovePeer(device, peer, key) 331 | } 332 | } 333 | 334 | func (device *Device) RemoveAllPeers() { 335 | device.peers.Lock() 336 | defer device.peers.Unlock() 337 | 338 | for key, peer := range device.peers.keyMap { 339 | unsafeRemovePeer(device, peer, key) 340 | } 341 | 342 | device.peers.keyMap = make(map[NoisePublicKey]*Peer) 343 | } 344 | 345 | func (device *Device) FlushPacketQueues() { 346 | for { 347 | select { 348 | case elem, ok := <-device.queue.decryption: 349 | if ok { 350 | elem.Drop() 351 | } 352 | case elem, ok := <-device.queue.encryption: 353 | if ok { 354 | elem.Drop() 355 | } 356 | case <-device.queue.handshake: 357 | default: 358 | return 359 | } 360 | } 361 | 362 | } 363 | 364 | func (device *Device) Close() { 365 | if device.isClosed.Swap(true) { 366 | return 367 | } 368 | 369 | device.state.starting.Wait() 370 | 371 | device.log.Info.Println("Device closing") 372 | device.state.changing.Set(true) 373 | device.state.Lock() 374 | defer device.state.Unlock() 375 | 376 | device.tun.device.Close() 377 | device.BindClose() 378 | 379 | device.isUp.Set(false) 380 | 381 | close(device.signals.stop) 382 | 383 | device.RemoveAllPeers() 384 | 385 | device.state.stopping.Wait() 386 | device.FlushPacketQueues() 387 | 388 | device.rate.limiter.Close() 389 | 390 | device.state.changing.Set(false) 391 | device.log.Info.Println("Interface closed") 392 | } 393 | 394 | func (device *Device) Wait() chan struct{} { 395 | return device.signals.stop 396 | } 397 | -------------------------------------------------------------------------------- /device/device_test.go: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: MIT 2 | * 3 | * Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved. 4 | */ 5 | 6 | package device 7 | 8 | /* Create two device instances and simulate full WireGuard interaction 9 | * without network dependencies 10 | */ 11 | 12 | import "testing" 13 | 14 | func TestDevice(t *testing.T) { 15 | 16 | // prepare tun devices for generating traffic 17 | 18 | tun1, err := CreateDummyTUN("tun1") 19 | if err != nil { 20 | t.Error("failed to create tun:", err.Error()) 21 | } 22 | 23 | tun2, err := CreateDummyTUN("tun2") 24 | if err != nil { 25 | t.Error("failed to create tun:", err.Error()) 26 | } 27 | 28 | _ = tun1 29 | _ = tun2 30 | 31 | // prepare endpoints 32 | 33 | end1, err := CreateDummyEndpoint() 34 | if err != nil { 35 | t.Error("failed to create endpoint:", err.Error()) 36 | } 37 | 38 | end2, err := CreateDummyEndpoint() 39 | if err != nil { 40 | t.Error("failed to create endpoint:", err.Error()) 41 | } 42 | 43 | _ = end1 44 | _ = end2 45 | 46 | // create binds 47 | 48 | } 49 | -------------------------------------------------------------------------------- /device/endpoint_test.go: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: MIT 2 | * 3 | * Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved. 4 | */ 5 | 6 | package device 7 | 8 | import ( 9 | "math/rand" 10 | "net" 11 | ) 12 | 13 | type DummyEndpoint struct { 14 | src [16]byte 15 | dst [16]byte 16 | } 17 | 18 | func CreateDummyEndpoint() (*DummyEndpoint, error) { 19 | var end DummyEndpoint 20 | if _, err := rand.Read(end.src[:]); err != nil { 21 | return nil, err 22 | } 23 | _, err := rand.Read(end.dst[:]) 24 | return &end, err 25 | } 26 | 27 | func (e *DummyEndpoint) ClearSrc() {} 28 | 29 | func (e *DummyEndpoint) SrcToString() string { 30 | var addr net.UDPAddr 31 | addr.IP = e.SrcIP() 32 | addr.Port = 1000 33 | return addr.String() 34 | } 35 | 36 | func (e *DummyEndpoint) DstToString() string { 37 | var addr net.UDPAddr 38 | addr.IP = e.DstIP() 39 | addr.Port = 1000 40 | return addr.String() 41 | } 42 | 43 | func (e *DummyEndpoint) SrcToBytes() []byte { 44 | return e.src[:] 45 | } 46 | 47 | func (e *DummyEndpoint) DstIP() net.IP { 48 | return e.dst[:] 49 | } 50 | 51 | func (e *DummyEndpoint) SrcIP() net.IP { 52 | return e.src[:] 53 | } 54 | -------------------------------------------------------------------------------- /device/indextable.go: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: MIT 2 | * 3 | * Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved. 4 | */ 5 | 6 | package device 7 | 8 | import ( 9 | "crypto/rand" 10 | "sync" 11 | "unsafe" 12 | ) 13 | 14 | type IndexTableEntry struct { 15 | peer *Peer 16 | handshake *Handshake 17 | keypair *Keypair 18 | } 19 | 20 | type IndexTable struct { 21 | sync.RWMutex 22 | table map[uint32]IndexTableEntry 23 | } 24 | 25 | func randUint32() (uint32, error) { 26 | var integer [4]byte 27 | _, err := rand.Read(integer[:]) 28 | return *(*uint32)(unsafe.Pointer(&integer[0])), err 29 | } 30 | 31 | func (table *IndexTable) Init() { 32 | table.Lock() 33 | defer table.Unlock() 34 | table.table = make(map[uint32]IndexTableEntry) 35 | } 36 | 37 | func (table *IndexTable) Delete(index uint32) { 38 | table.Lock() 39 | defer table.Unlock() 40 | delete(table.table, index) 41 | } 42 | 43 | func (table *IndexTable) SwapIndexForKeypair(index uint32, keypair *Keypair) { 44 | table.Lock() 45 | defer table.Unlock() 46 | entry, ok := table.table[index] 47 | if !ok { 48 | return 49 | } 50 | table.table[index] = IndexTableEntry{ 51 | peer: entry.peer, 52 | keypair: keypair, 53 | handshake: nil, 54 | } 55 | } 56 | 57 | func (table *IndexTable) NewIndexForHandshake(peer *Peer, handshake *Handshake) (uint32, error) { 58 | for { 59 | // generate random index 60 | 61 | index, err := randUint32() 62 | if err != nil { 63 | return index, err 64 | } 65 | 66 | // check if index used 67 | 68 | table.RLock() 69 | _, ok := table.table[index] 70 | table.RUnlock() 71 | if ok { 72 | continue 73 | } 74 | 75 | // check again while locked 76 | 77 | table.Lock() 78 | _, found := table.table[index] 79 | if found { 80 | table.Unlock() 81 | continue 82 | } 83 | table.table[index] = IndexTableEntry{ 84 | peer: peer, 85 | handshake: handshake, 86 | keypair: nil, 87 | } 88 | table.Unlock() 89 | return index, nil 90 | } 91 | } 92 | 93 | func (table *IndexTable) Lookup(id uint32) IndexTableEntry { 94 | table.RLock() 95 | defer table.RUnlock() 96 | return table.table[id] 97 | } 98 | -------------------------------------------------------------------------------- /device/ip.go: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: MIT 2 | * 3 | * Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved. 4 | */ 5 | 6 | package device 7 | 8 | import ( 9 | "net" 10 | ) 11 | 12 | const ( 13 | IPv4offsetTotalLength = 2 14 | IPv4offsetSrc = 12 15 | IPv4offsetDst = IPv4offsetSrc + net.IPv4len 16 | ) 17 | 18 | const ( 19 | IPv6offsetPayloadLength = 4 20 | IPv6offsetSrc = 8 21 | IPv6offsetDst = IPv6offsetSrc + net.IPv6len 22 | ) 23 | -------------------------------------------------------------------------------- /device/kdf_test.go: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: MIT 2 | * 3 | * Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved. 4 | */ 5 | 6 | package device 7 | 8 | import ( 9 | "encoding/hex" 10 | "golang.org/x/crypto/blake2s" 11 | "testing" 12 | ) 13 | 14 | type KDFTest struct { 15 | key string 16 | input string 17 | t0 string 18 | t1 string 19 | t2 string 20 | } 21 | 22 | func assertEquals(t *testing.T, a string, b string) { 23 | if a != b { 24 | t.Fatal("expected", a, "=", b) 25 | } 26 | } 27 | 28 | func TestKDF(t *testing.T) { 29 | tests := []KDFTest{ 30 | { 31 | key: "746573742d6b6579", 32 | input: "746573742d696e707574", 33 | t0: "6f0e5ad38daba1bea8a0d213688736f19763239305e0f58aba697f9ffc41c633", 34 | t1: "df1194df20802a4fe594cde27e92991c8cae66c366e8106aaa937a55fa371e8a", 35 | t2: "fac6e2745a325f5dc5d11a5b165aad08b0ada28e7b4e666b7c077934a4d76c24", 36 | }, 37 | { 38 | key: "776972656775617264", 39 | input: "776972656775617264", 40 | t0: "491d43bbfdaa8750aaf535e334ecbfe5129967cd64635101c566d4caefda96e8", 41 | t1: "1e71a379baefd8a79aa4662212fcafe19a23e2b609a3db7d6bcba8f560e3d25f", 42 | t2: "31e1ae48bddfbe5de38f295e5452b1909a1b4e38e183926af3780b0c1e1f0160", 43 | }, 44 | { 45 | key: "", 46 | input: "", 47 | t0: "8387b46bf43eccfcf349552a095d8315c4055beb90208fb1be23b894bc2ed5d0", 48 | t1: "58a0e5f6faefccf4807bff1f05fa8a9217945762040bcec2f4b4a62bdfe0e86e", 49 | t2: "0ce6ea98ec548f8e281e93e32db65621c45eb18dc6f0a7ad94178610a2f7338e", 50 | }, 51 | } 52 | 53 | var t0, t1, t2 [blake2s.Size]byte 54 | 55 | for _, test := range tests { 56 | key, _ := hex.DecodeString(test.key) 57 | input, _ := hex.DecodeString(test.input) 58 | KDF3(&t0, &t1, &t2, key, input) 59 | t0s := hex.EncodeToString(t0[:]) 60 | t1s := hex.EncodeToString(t1[:]) 61 | t2s := hex.EncodeToString(t2[:]) 62 | assertEquals(t, t0s, test.t0) 63 | assertEquals(t, t1s, test.t1) 64 | assertEquals(t, t2s, test.t2) 65 | } 66 | 67 | for _, test := range tests { 68 | key, _ := hex.DecodeString(test.key) 69 | input, _ := hex.DecodeString(test.input) 70 | KDF2(&t0, &t1, key, input) 71 | t0s := hex.EncodeToString(t0[:]) 72 | t1s := hex.EncodeToString(t1[:]) 73 | assertEquals(t, t0s, test.t0) 74 | assertEquals(t, t1s, test.t1) 75 | } 76 | 77 | for _, test := range tests { 78 | key, _ := hex.DecodeString(test.key) 79 | input, _ := hex.DecodeString(test.input) 80 | KDF1(&t0, key, input) 81 | t0s := hex.EncodeToString(t0[:]) 82 | assertEquals(t, t0s, test.t0) 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /device/keypair.go: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: MIT 2 | * 3 | * Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved. 4 | */ 5 | 6 | package device 7 | 8 | import ( 9 | "crypto/cipher" 10 | "golang.zx2c4.com/wireguard/replay" 11 | "sync" 12 | "time" 13 | ) 14 | 15 | /* Due to limitations in Go and /x/crypto there is currently 16 | * no way to ensure that key material is securely ereased in memory. 17 | * 18 | * Since this may harm the forward secrecy property, 19 | * we plan to resolve this issue; whenever Go allows us to do so. 20 | */ 21 | 22 | type Keypair struct { 23 | sendNonce uint64 24 | send cipher.AEAD 25 | receive cipher.AEAD 26 | replayFilter replay.ReplayFilter 27 | isInitiator bool 28 | created time.Time 29 | localIndex uint32 30 | remoteIndex uint32 31 | } 32 | 33 | type Keypairs struct { 34 | sync.RWMutex 35 | current *Keypair 36 | previous *Keypair 37 | next *Keypair 38 | } 39 | 40 | func (kp *Keypairs) Current() *Keypair { 41 | kp.RLock() 42 | defer kp.RUnlock() 43 | return kp.current 44 | } 45 | 46 | func (device *Device) DeleteKeypair(key *Keypair) { 47 | if key != nil { 48 | device.indexTable.Delete(key.localIndex) 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /device/logger.go: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: MIT 2 | * 3 | * Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved. 4 | */ 5 | 6 | package device 7 | 8 | import ( 9 | "io" 10 | "io/ioutil" 11 | "log" 12 | "os" 13 | ) 14 | 15 | const ( 16 | LogLevelSilent = iota 17 | LogLevelError 18 | LogLevelInfo 19 | LogLevelDebug 20 | ) 21 | 22 | type Logger struct { 23 | Debug *log.Logger 24 | Info *log.Logger 25 | Error *log.Logger 26 | } 27 | 28 | func NewLogger(level int, prepend string) *Logger { 29 | output := os.Stdout 30 | logger := new(Logger) 31 | 32 | logErr, logInfo, logDebug := func() (io.Writer, io.Writer, io.Writer) { 33 | if level >= LogLevelDebug { 34 | return output, output, output 35 | } 36 | if level >= LogLevelInfo { 37 | return output, output, ioutil.Discard 38 | } 39 | if level >= LogLevelError { 40 | return output, ioutil.Discard, ioutil.Discard 41 | } 42 | return ioutil.Discard, ioutil.Discard, ioutil.Discard 43 | }() 44 | 45 | logger.Debug = log.New(logDebug, 46 | "DEBUG: "+prepend, 47 | log.Ldate|log.Ltime, 48 | ) 49 | 50 | logger.Info = log.New(logInfo, 51 | "INFO: "+prepend, 52 | log.Ldate|log.Ltime, 53 | ) 54 | logger.Error = log.New(logErr, 55 | "ERROR: "+prepend, 56 | log.Ldate|log.Ltime, 57 | ) 58 | return logger 59 | } 60 | -------------------------------------------------------------------------------- /device/mark_default.go: -------------------------------------------------------------------------------- 1 | // +build !linux,!openbsd,!freebsd 2 | 3 | /* SPDX-License-Identifier: MIT 4 | * 5 | * Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved. 6 | */ 7 | 8 | package device 9 | 10 | func (bind *nativeBind) SetMark(mark uint32) error { 11 | return nil 12 | } 13 | -------------------------------------------------------------------------------- /device/mark_unix.go: -------------------------------------------------------------------------------- 1 | // +build android openbsd freebsd 2 | 3 | /* SPDX-License-Identifier: MIT 4 | * 5 | * Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved. 6 | */ 7 | 8 | package device 9 | 10 | import ( 11 | "golang.org/x/sys/unix" 12 | "runtime" 13 | ) 14 | 15 | var fwmarkIoctl int 16 | 17 | func init() { 18 | switch runtime.GOOS { 19 | case "linux", "android": 20 | fwmarkIoctl = 36 /* unix.SO_MARK */ 21 | case "freebsd": 22 | fwmarkIoctl = 0x1015 /* unix.SO_USER_COOKIE */ 23 | case "openbsd": 24 | fwmarkIoctl = 0x1021 /* unix.SO_RTABLE */ 25 | } 26 | } 27 | 28 | func (bind *nativeBind) SetMark(mark uint32) error { 29 | var operr error 30 | if fwmarkIoctl == 0 { 31 | return nil 32 | } 33 | if bind.ipv4 != nil { 34 | fd, err := bind.ipv4.SyscallConn() 35 | if err != nil { 36 | return err 37 | } 38 | err = fd.Control(func(fd uintptr) { 39 | operr = unix.SetsockoptInt(int(fd), unix.SOL_SOCKET, fwmarkIoctl, int(mark)) 40 | }) 41 | if err == nil { 42 | err = operr 43 | } 44 | if err != nil { 45 | return err 46 | } 47 | } 48 | if bind.ipv6 != nil { 49 | fd, err := bind.ipv6.SyscallConn() 50 | if err != nil { 51 | return err 52 | } 53 | err = fd.Control(func(fd uintptr) { 54 | operr = unix.SetsockoptInt(int(fd), unix.SOL_SOCKET, fwmarkIoctl, int(mark)) 55 | }) 56 | if err == nil { 57 | err = operr 58 | } 59 | if err != nil { 60 | return err 61 | } 62 | } 63 | return nil 64 | } 65 | -------------------------------------------------------------------------------- /device/misc.go: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: MIT 2 | * 3 | * Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved. 4 | */ 5 | 6 | package device 7 | 8 | import ( 9 | "sync/atomic" 10 | ) 11 | 12 | /* Atomic Boolean */ 13 | 14 | const ( 15 | AtomicFalse = int32(iota) 16 | AtomicTrue 17 | ) 18 | 19 | type AtomicBool struct { 20 | int32 21 | } 22 | 23 | func (a *AtomicBool) Get() bool { 24 | return atomic.LoadInt32(&a.int32) == AtomicTrue 25 | } 26 | 27 | func (a *AtomicBool) Swap(val bool) bool { 28 | flag := AtomicFalse 29 | if val { 30 | flag = AtomicTrue 31 | } 32 | return atomic.SwapInt32(&a.int32, flag) == AtomicTrue 33 | } 34 | 35 | func (a *AtomicBool) Set(val bool) { 36 | flag := AtomicFalse 37 | if val { 38 | flag = AtomicTrue 39 | } 40 | atomic.StoreInt32(&a.int32, flag) 41 | } 42 | 43 | func min(a, b uint) uint { 44 | if a > b { 45 | return b 46 | } 47 | return a 48 | } 49 | -------------------------------------------------------------------------------- /device/noise-helpers.go: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: MIT 2 | * 3 | * Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved. 4 | */ 5 | 6 | package device 7 | 8 | import ( 9 | "crypto/hmac" 10 | "crypto/rand" 11 | "crypto/subtle" 12 | "golang.org/x/crypto/blake2s" 13 | "golang.org/x/crypto/curve25519" 14 | "hash" 15 | ) 16 | 17 | /* KDF related functions. 18 | * HMAC-based Key Derivation Function (HKDF) 19 | * https://tools.ietf.org/html/rfc5869 20 | */ 21 | 22 | func HMAC1(sum *[blake2s.Size]byte, key, in0 []byte) { 23 | mac := hmac.New(func() hash.Hash { 24 | h, _ := blake2s.New256(nil) 25 | return h 26 | }, key) 27 | mac.Write(in0) 28 | mac.Sum(sum[:0]) 29 | } 30 | 31 | func HMAC2(sum *[blake2s.Size]byte, key, in0, in1 []byte) { 32 | mac := hmac.New(func() hash.Hash { 33 | h, _ := blake2s.New256(nil) 34 | return h 35 | }, key) 36 | mac.Write(in0) 37 | mac.Write(in1) 38 | mac.Sum(sum[:0]) 39 | } 40 | 41 | func KDF1(t0 *[blake2s.Size]byte, key, input []byte) { 42 | HMAC1(t0, key, input) 43 | HMAC1(t0, t0[:], []byte{0x1}) 44 | return 45 | } 46 | 47 | func KDF2(t0, t1 *[blake2s.Size]byte, key, input []byte) { 48 | var prk [blake2s.Size]byte 49 | HMAC1(&prk, key, input) 50 | HMAC1(t0, prk[:], []byte{0x1}) 51 | HMAC2(t1, prk[:], t0[:], []byte{0x2}) 52 | setZero(prk[:]) 53 | return 54 | } 55 | 56 | func KDF3(t0, t1, t2 *[blake2s.Size]byte, key, input []byte) { 57 | var prk [blake2s.Size]byte 58 | HMAC1(&prk, key, input) 59 | HMAC1(t0, prk[:], []byte{0x1}) 60 | HMAC2(t1, prk[:], t0[:], []byte{0x2}) 61 | HMAC2(t2, prk[:], t1[:], []byte{0x3}) 62 | setZero(prk[:]) 63 | return 64 | } 65 | 66 | func isZero(val []byte) bool { 67 | acc := 1 68 | for _, b := range val { 69 | acc &= subtle.ConstantTimeByteEq(b, 0) 70 | } 71 | return acc == 1 72 | } 73 | 74 | /* This function is not used as pervasively as it should because this is mostly impossible in Go at the moment */ 75 | func setZero(arr []byte) { 76 | for i := range arr { 77 | arr[i] = 0 78 | } 79 | } 80 | 81 | func (sk *NoisePrivateKey) clamp() { 82 | sk[0] &= 248 83 | sk[31] = (sk[31] & 127) | 64 84 | } 85 | 86 | func newPrivateKey() (sk NoisePrivateKey, err error) { 87 | _, err = rand.Read(sk[:]) 88 | sk.clamp() 89 | return 90 | } 91 | 92 | func (sk *NoisePrivateKey) publicKey() (pk NoisePublicKey) { 93 | apk := (*[NoisePublicKeySize]byte)(&pk) 94 | ask := (*[NoisePrivateKeySize]byte)(sk) 95 | curve25519.ScalarBaseMult(apk, ask) 96 | return 97 | } 98 | 99 | func (sk *NoisePrivateKey) sharedSecret(pk NoisePublicKey) (ss [NoisePublicKeySize]byte) { 100 | apk := (*[NoisePublicKeySize]byte)(&pk) 101 | ask := (*[NoisePrivateKeySize]byte)(sk) 102 | curve25519.ScalarMult(&ss, ask, apk) 103 | return ss 104 | } 105 | -------------------------------------------------------------------------------- /device/noise-types.go: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: MIT 2 | * 3 | * Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved. 4 | */ 5 | 6 | package device 7 | 8 | import ( 9 | "crypto/subtle" 10 | "encoding/hex" 11 | "errors" 12 | "golang.org/x/crypto/chacha20poly1305" 13 | ) 14 | 15 | const ( 16 | NoisePublicKeySize = 32 17 | NoisePrivateKeySize = 32 18 | ) 19 | 20 | type ( 21 | NoisePublicKey [NoisePublicKeySize]byte 22 | NoisePrivateKey [NoisePrivateKeySize]byte 23 | NoiseSymmetricKey [chacha20poly1305.KeySize]byte 24 | NoiseNonce uint64 // padded to 12-bytes 25 | ) 26 | 27 | func loadExactHex(dst []byte, src string) error { 28 | slice, err := hex.DecodeString(src) 29 | if err != nil { 30 | return err 31 | } 32 | if len(slice) != len(dst) { 33 | return errors.New("hex string does not fit the slice") 34 | } 35 | copy(dst, slice) 36 | return nil 37 | } 38 | 39 | func (key NoisePrivateKey) IsZero() bool { 40 | var zero NoisePrivateKey 41 | return key.Equals(zero) 42 | } 43 | 44 | func (key NoisePrivateKey) Equals(tar NoisePrivateKey) bool { 45 | return subtle.ConstantTimeCompare(key[:], tar[:]) == 1 46 | } 47 | 48 | func (key *NoisePrivateKey) FromHex(src string) (err error) { 49 | err = loadExactHex(key[:], src) 50 | key.clamp() 51 | return 52 | } 53 | 54 | func (key NoisePrivateKey) ToHex() string { 55 | return hex.EncodeToString(key[:]) 56 | } 57 | 58 | func (key *NoisePublicKey) FromHex(src string) error { 59 | return loadExactHex(key[:], src) 60 | } 61 | 62 | func (key NoisePublicKey) ToHex() string { 63 | return hex.EncodeToString(key[:]) 64 | } 65 | 66 | func (key NoisePublicKey) IsZero() bool { 67 | var zero NoisePublicKey 68 | return key.Equals(zero) 69 | } 70 | 71 | func (key NoisePublicKey) Equals(tar NoisePublicKey) bool { 72 | return subtle.ConstantTimeCompare(key[:], tar[:]) == 1 73 | } 74 | 75 | func (key *NoiseSymmetricKey) FromHex(src string) error { 76 | return loadExactHex(key[:], src) 77 | } 78 | 79 | func (key NoiseSymmetricKey) ToHex() string { 80 | return hex.EncodeToString(key[:]) 81 | } 82 | -------------------------------------------------------------------------------- /device/noise_test.go: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: MIT 2 | * 3 | * Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved. 4 | */ 5 | 6 | package device 7 | 8 | import ( 9 | "bytes" 10 | "encoding/binary" 11 | "testing" 12 | ) 13 | 14 | func TestCurveWrappers(t *testing.T) { 15 | sk1, err := newPrivateKey() 16 | assertNil(t, err) 17 | 18 | sk2, err := newPrivateKey() 19 | assertNil(t, err) 20 | 21 | pk1 := sk1.publicKey() 22 | pk2 := sk2.publicKey() 23 | 24 | ss1 := sk1.sharedSecret(pk2) 25 | ss2 := sk2.sharedSecret(pk1) 26 | 27 | if ss1 != ss2 { 28 | t.Fatal("Failed to compute shared secet") 29 | } 30 | } 31 | 32 | func TestNoiseHandshake(t *testing.T) { 33 | dev1 := randDevice(t) 34 | dev2 := randDevice(t) 35 | 36 | defer dev1.Close() 37 | defer dev2.Close() 38 | 39 | peer1, _ := dev2.NewPeer(dev1.staticIdentity.privateKey.publicKey()) 40 | peer2, _ := dev1.NewPeer(dev2.staticIdentity.privateKey.publicKey()) 41 | 42 | assertEqual( 43 | t, 44 | peer1.handshake.precomputedStaticStatic[:], 45 | peer2.handshake.precomputedStaticStatic[:], 46 | ) 47 | 48 | /* simulate handshake */ 49 | 50 | // initiation message 51 | 52 | t.Log("exchange initiation message") 53 | 54 | msg1, err := dev1.CreateMessageInitiation(peer2) 55 | assertNil(t, err) 56 | 57 | packet := make([]byte, 0, 256) 58 | writer := bytes.NewBuffer(packet) 59 | err = binary.Write(writer, binary.LittleEndian, msg1) 60 | assertNil(t, err) 61 | peer := dev2.ConsumeMessageInitiation(msg1) 62 | if peer == nil { 63 | t.Fatal("handshake failed at initiation message") 64 | } 65 | 66 | assertEqual( 67 | t, 68 | peer1.handshake.chainKey[:], 69 | peer2.handshake.chainKey[:], 70 | ) 71 | 72 | assertEqual( 73 | t, 74 | peer1.handshake.hash[:], 75 | peer2.handshake.hash[:], 76 | ) 77 | 78 | // response message 79 | 80 | t.Log("exchange response message") 81 | 82 | msg2, err := dev2.CreateMessageResponse(peer1) 83 | assertNil(t, err) 84 | 85 | peer = dev1.ConsumeMessageResponse(msg2) 86 | if peer == nil { 87 | t.Fatal("handshake failed at response message") 88 | } 89 | 90 | assertEqual( 91 | t, 92 | peer1.handshake.chainKey[:], 93 | peer2.handshake.chainKey[:], 94 | ) 95 | 96 | assertEqual( 97 | t, 98 | peer1.handshake.hash[:], 99 | peer2.handshake.hash[:], 100 | ) 101 | 102 | // key pairs 103 | 104 | t.Log("deriving keys") 105 | 106 | err = peer1.BeginSymmetricSession() 107 | if err != nil { 108 | t.Fatal("failed to derive keypair for peer 1", err) 109 | } 110 | 111 | err = peer2.BeginSymmetricSession() 112 | if err != nil { 113 | t.Fatal("failed to derive keypair for peer 2", err) 114 | } 115 | 116 | key1 := peer1.keypairs.next 117 | key2 := peer2.keypairs.current 118 | 119 | // encrypting / decryption test 120 | 121 | t.Log("test key pairs") 122 | 123 | func() { 124 | testMsg := []byte("wireguard test message 1") 125 | var err error 126 | var out []byte 127 | var nonce [12]byte 128 | out = key1.send.Seal(out, nonce[:], testMsg, nil) 129 | out, err = key2.receive.Open(out[:0], nonce[:], out, nil) 130 | assertNil(t, err) 131 | assertEqual(t, out, testMsg) 132 | }() 133 | 134 | func() { 135 | testMsg := []byte("wireguard test message 2") 136 | var err error 137 | var out []byte 138 | var nonce [12]byte 139 | out = key2.send.Seal(out, nonce[:], testMsg, nil) 140 | out, err = key1.receive.Open(out[:0], nonce[:], out, nil) 141 | assertNil(t, err) 142 | assertEqual(t, out, testMsg) 143 | }() 144 | } 145 | -------------------------------------------------------------------------------- /device/peer.go: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: MIT 2 | * 3 | * Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved. 4 | */ 5 | 6 | package device 7 | 8 | import ( 9 | "encoding/base64" 10 | "errors" 11 | "fmt" 12 | "sync" 13 | "time" 14 | ) 15 | 16 | const ( 17 | PeerRoutineNumber = 3 18 | ) 19 | 20 | type Peer struct { 21 | isRunning AtomicBool 22 | sync.RWMutex // Mostly protects endpoint, but is generally taken whenever we modify peer 23 | keypairs Keypairs 24 | handshake Handshake 25 | device *Device 26 | endpoint Endpoint 27 | persistentKeepaliveInterval uint16 28 | 29 | // This must be 64-bit aligned, so make sure the above members come out to even alignment and pad accordingly 30 | stats struct { 31 | txBytes uint64 // bytes send to peer (endpoint) 32 | rxBytes uint64 // bytes received from peer 33 | lastHandshakeNano int64 // nano seconds since epoch 34 | } 35 | 36 | timers struct { 37 | retransmitHandshake *Timer 38 | sendKeepalive *Timer 39 | newHandshake *Timer 40 | zeroKeyMaterial *Timer 41 | persistentKeepalive *Timer 42 | handshakeAttempts uint32 43 | needAnotherKeepalive AtomicBool 44 | sentLastMinuteHandshake AtomicBool 45 | } 46 | 47 | signals struct { 48 | newKeypairArrived chan struct{} 49 | flushNonceQueue chan struct{} 50 | } 51 | 52 | queue struct { 53 | nonce chan *QueueOutboundElement // nonce / pre-handshake queue 54 | outbound chan *QueueOutboundElement // sequential ordering of work 55 | inbound chan *QueueInboundElement // sequential ordering of work 56 | packetInNonceQueueIsAwaitingKey AtomicBool 57 | } 58 | 59 | routines struct { 60 | sync.Mutex // held when stopping / starting routines 61 | starting sync.WaitGroup // routines pending start 62 | stopping sync.WaitGroup // routines pending stop 63 | stop chan struct{} // size 0, stop all go routines in peer 64 | } 65 | 66 | cookieGenerator CookieGenerator 67 | } 68 | 69 | func (device *Device) NewPeer(pk NoisePublicKey) (*Peer, error) { 70 | 71 | if device.isClosed.Get() { 72 | return nil, errors.New("device closed") 73 | } 74 | 75 | // lock resources 76 | 77 | device.staticIdentity.RLock() 78 | defer device.staticIdentity.RUnlock() 79 | 80 | device.peers.Lock() 81 | defer device.peers.Unlock() 82 | 83 | // check if over limit 84 | 85 | if len(device.peers.keyMap) >= MaxPeers { 86 | return nil, errors.New("too many peers") 87 | } 88 | 89 | // create peer 90 | 91 | peer := new(Peer) 92 | peer.Lock() 93 | defer peer.Unlock() 94 | 95 | peer.cookieGenerator.Init(pk) 96 | peer.device = device 97 | peer.isRunning.Set(false) 98 | 99 | // map public key 100 | 101 | _, ok := device.peers.keyMap[pk] 102 | if ok { 103 | return nil, errors.New("adding existing peer") 104 | } 105 | device.peers.keyMap[pk] = peer 106 | 107 | // pre-compute DH 108 | 109 | handshake := &peer.handshake 110 | handshake.mutex.Lock() 111 | handshake.remoteStatic = pk 112 | handshake.precomputedStaticStatic = device.staticIdentity.privateKey.sharedSecret(pk) 113 | handshake.mutex.Unlock() 114 | 115 | // reset endpoint 116 | 117 | peer.endpoint = nil 118 | 119 | // start peer 120 | 121 | if peer.device.isUp.Get() { 122 | peer.Start() 123 | } 124 | 125 | return peer, nil 126 | } 127 | 128 | func (peer *Peer) SendBuffer(buffer []byte) error { 129 | peer.device.net.RLock() 130 | defer peer.device.net.RUnlock() 131 | 132 | if peer.device.net.bind == nil { 133 | return errors.New("no bind") 134 | } 135 | 136 | peer.RLock() 137 | defer peer.RUnlock() 138 | 139 | if peer.endpoint == nil { 140 | return errors.New("no known endpoint for peer") 141 | } 142 | 143 | return peer.device.net.bind.Send(buffer, peer.endpoint) 144 | } 145 | 146 | func (peer *Peer) String() string { 147 | base64Key := base64.StdEncoding.EncodeToString(peer.handshake.remoteStatic[:]) 148 | abbreviatedKey := "invalid" 149 | if len(base64Key) == 44 { 150 | abbreviatedKey = base64Key[0:4] + "…" + base64Key[39:43] 151 | } 152 | return fmt.Sprintf("peer(%s)", abbreviatedKey) 153 | } 154 | 155 | func (peer *Peer) Start() { 156 | 157 | // should never start a peer on a closed device 158 | 159 | if peer.device.isClosed.Get() { 160 | return 161 | } 162 | 163 | // prevent simultaneous start/stop operations 164 | 165 | peer.routines.Lock() 166 | defer peer.routines.Unlock() 167 | 168 | if peer.isRunning.Get() { 169 | return 170 | } 171 | 172 | device := peer.device 173 | device.log.Debug.Println(peer, "- Starting...") 174 | 175 | // reset routine state 176 | 177 | peer.routines.starting.Wait() 178 | peer.routines.stopping.Wait() 179 | peer.routines.stop = make(chan struct{}) 180 | peer.routines.starting.Add(PeerRoutineNumber) 181 | peer.routines.stopping.Add(PeerRoutineNumber) 182 | 183 | // prepare queues 184 | 185 | peer.queue.nonce = make(chan *QueueOutboundElement, QueueOutboundSize) 186 | peer.queue.outbound = make(chan *QueueOutboundElement, QueueOutboundSize) 187 | peer.queue.inbound = make(chan *QueueInboundElement, QueueInboundSize) 188 | 189 | peer.timersInit() 190 | peer.handshake.lastSentHandshake = time.Now().Add(-(RekeyTimeout + time.Second)) 191 | peer.signals.newKeypairArrived = make(chan struct{}, 1) 192 | peer.signals.flushNonceQueue = make(chan struct{}, 1) 193 | 194 | // wait for routines to start 195 | 196 | go peer.RoutineNonce() 197 | go peer.RoutineSequentialSender() 198 | go peer.RoutineSequentialReceiver() 199 | 200 | peer.routines.starting.Wait() 201 | peer.isRunning.Set(true) 202 | } 203 | 204 | func (peer *Peer) ZeroAndFlushAll() { 205 | device := peer.device 206 | 207 | // clear key pairs 208 | 209 | keypairs := &peer.keypairs 210 | keypairs.Lock() 211 | device.DeleteKeypair(keypairs.previous) 212 | device.DeleteKeypair(keypairs.current) 213 | device.DeleteKeypair(keypairs.next) 214 | keypairs.previous = nil 215 | keypairs.current = nil 216 | keypairs.next = nil 217 | keypairs.Unlock() 218 | 219 | // clear handshake state 220 | 221 | handshake := &peer.handshake 222 | handshake.mutex.Lock() 223 | device.indexTable.Delete(handshake.localIndex) 224 | handshake.Clear() 225 | handshake.mutex.Unlock() 226 | 227 | peer.FlushNonceQueue() 228 | } 229 | 230 | func (peer *Peer) Stop() { 231 | 232 | // prevent simultaneous start/stop operations 233 | 234 | if !peer.isRunning.Swap(false) { 235 | return 236 | } 237 | 238 | peer.routines.starting.Wait() 239 | 240 | peer.routines.Lock() 241 | defer peer.routines.Unlock() 242 | 243 | peer.device.log.Debug.Println(peer, "- Stopping...") 244 | 245 | peer.timersStop() 246 | 247 | // stop & wait for ongoing peer routines 248 | 249 | close(peer.routines.stop) 250 | peer.routines.stopping.Wait() 251 | 252 | // close queues 253 | 254 | close(peer.queue.nonce) 255 | close(peer.queue.outbound) 256 | close(peer.queue.inbound) 257 | 258 | peer.ZeroAndFlushAll() 259 | } 260 | 261 | var RoamingDisabled bool 262 | 263 | func (peer *Peer) SetEndpointFromPacket(endpoint Endpoint) { 264 | if RoamingDisabled { 265 | return 266 | } 267 | peer.Lock() 268 | peer.endpoint = endpoint 269 | peer.Unlock() 270 | } 271 | -------------------------------------------------------------------------------- /device/pools.go: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: MIT 2 | * 3 | * Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved. 4 | */ 5 | 6 | package device 7 | 8 | import "sync" 9 | 10 | func (device *Device) PopulatePools() { 11 | if PreallocatedBuffersPerPool == 0 { 12 | device.pool.messageBufferPool = &sync.Pool{ 13 | New: func() interface{} { 14 | return new([MaxMessageSize]byte) 15 | }, 16 | } 17 | device.pool.inboundElementPool = &sync.Pool{ 18 | New: func() interface{} { 19 | return new(QueueInboundElement) 20 | }, 21 | } 22 | device.pool.outboundElementPool = &sync.Pool{ 23 | New: func() interface{} { 24 | return new(QueueOutboundElement) 25 | }, 26 | } 27 | } else { 28 | device.pool.messageBufferReuseChan = make(chan *[MaxMessageSize]byte, PreallocatedBuffersPerPool) 29 | for i := 0; i < PreallocatedBuffersPerPool; i += 1 { 30 | device.pool.messageBufferReuseChan <- new([MaxMessageSize]byte) 31 | } 32 | device.pool.inboundElementReuseChan = make(chan *QueueInboundElement, PreallocatedBuffersPerPool) 33 | for i := 0; i < PreallocatedBuffersPerPool; i += 1 { 34 | device.pool.inboundElementReuseChan <- new(QueueInboundElement) 35 | } 36 | device.pool.outboundElementReuseChan = make(chan *QueueOutboundElement, PreallocatedBuffersPerPool) 37 | for i := 0; i < PreallocatedBuffersPerPool; i += 1 { 38 | device.pool.outboundElementReuseChan <- new(QueueOutboundElement) 39 | } 40 | } 41 | } 42 | 43 | func (device *Device) GetMessageBuffer() *[MaxMessageSize]byte { 44 | if PreallocatedBuffersPerPool == 0 { 45 | return device.pool.messageBufferPool.Get().(*[MaxMessageSize]byte) 46 | } else { 47 | return <-device.pool.messageBufferReuseChan 48 | } 49 | } 50 | 51 | func (device *Device) PutMessageBuffer(msg *[MaxMessageSize]byte) { 52 | if PreallocatedBuffersPerPool == 0 { 53 | device.pool.messageBufferPool.Put(msg) 54 | } else { 55 | device.pool.messageBufferReuseChan <- msg 56 | } 57 | } 58 | 59 | func (device *Device) GetInboundElement() *QueueInboundElement { 60 | if PreallocatedBuffersPerPool == 0 { 61 | return device.pool.inboundElementPool.Get().(*QueueInboundElement) 62 | } else { 63 | return <-device.pool.inboundElementReuseChan 64 | } 65 | } 66 | 67 | func (device *Device) PutInboundElement(msg *QueueInboundElement) { 68 | if PreallocatedBuffersPerPool == 0 { 69 | device.pool.inboundElementPool.Put(msg) 70 | } else { 71 | device.pool.inboundElementReuseChan <- msg 72 | } 73 | } 74 | 75 | func (device *Device) GetOutboundElement() *QueueOutboundElement { 76 | if PreallocatedBuffersPerPool == 0 { 77 | return device.pool.outboundElementPool.Get().(*QueueOutboundElement) 78 | } else { 79 | return <-device.pool.outboundElementReuseChan 80 | } 81 | } 82 | 83 | func (device *Device) PutOutboundElement(msg *QueueOutboundElement) { 84 | if PreallocatedBuffersPerPool == 0 { 85 | device.pool.outboundElementPool.Put(msg) 86 | } else { 87 | device.pool.outboundElementReuseChan <- msg 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /device/queueconstants_android.go: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: MIT 2 | * 3 | * Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved. 4 | */ 5 | 6 | package device 7 | 8 | /* Reduce memory consumption for Android */ 9 | 10 | const ( 11 | QueueOutboundSize = 1024 12 | QueueInboundSize = 1024 13 | QueueHandshakeSize = 1024 14 | MaxSegmentSize = 2200 15 | PreallocatedBuffersPerPool = 4096 16 | ) -------------------------------------------------------------------------------- /device/queueconstants_default.go: -------------------------------------------------------------------------------- 1 | // +build !android,!ios 2 | 3 | /* SPDX-License-Identifier: MIT 4 | * 5 | * Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved. 6 | */ 7 | 8 | package device 9 | 10 | const ( 11 | QueueOutboundSize = 1024 12 | QueueInboundSize = 1024 13 | QueueHandshakeSize = 1024 14 | MaxSegmentSize = (1 << 16) - 1 // largest possible UDP datagram 15 | PreallocatedBuffersPerPool = 0 // Disable and allow for infinite memory growth 16 | ) 17 | -------------------------------------------------------------------------------- /device/queueconstants_ios.go: -------------------------------------------------------------------------------- 1 | // +build ios 2 | 3 | /* SPDX-License-Identifier: MIT 4 | * 5 | * Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved. 6 | */ 7 | 8 | package device 9 | 10 | /* Fit within memory limits for iOS's Network Extension API, which has stricter requirements */ 11 | 12 | const ( 13 | QueueOutboundSize = 1024 14 | QueueInboundSize = 1024 15 | QueueHandshakeSize = 1024 16 | MaxSegmentSize = 1700 17 | PreallocatedBuffersPerPool = 1024 18 | ) 19 | -------------------------------------------------------------------------------- /device/timers.go: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: MIT 2 | * 3 | * Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved. 4 | * 5 | * This is based heavily on timers.c from the kernel implementation. 6 | */ 7 | 8 | package device 9 | 10 | import ( 11 | "math/rand" 12 | "sync" 13 | "sync/atomic" 14 | "time" 15 | ) 16 | 17 | /* This Timer structure and related functions should roughly copy the interface of 18 | * the Linux kernel's struct timer_list. 19 | */ 20 | 21 | type Timer struct { 22 | *time.Timer 23 | modifyingLock sync.RWMutex 24 | runningLock sync.Mutex 25 | isPending bool 26 | } 27 | 28 | func (peer *Peer) NewTimer(expirationFunction func(*Peer)) *Timer { 29 | timer := &Timer{} 30 | timer.Timer = time.AfterFunc(time.Hour, func() { 31 | timer.runningLock.Lock() 32 | 33 | timer.modifyingLock.Lock() 34 | if !timer.isPending { 35 | timer.modifyingLock.Unlock() 36 | timer.runningLock.Unlock() 37 | return 38 | } 39 | timer.isPending = false 40 | timer.modifyingLock.Unlock() 41 | 42 | expirationFunction(peer) 43 | timer.runningLock.Unlock() 44 | }) 45 | timer.Stop() 46 | return timer 47 | } 48 | 49 | func (timer *Timer) Mod(d time.Duration) { 50 | timer.modifyingLock.Lock() 51 | timer.isPending = true 52 | timer.Reset(d) 53 | timer.modifyingLock.Unlock() 54 | } 55 | 56 | func (timer *Timer) Del() { 57 | timer.modifyingLock.Lock() 58 | timer.isPending = false 59 | timer.Stop() 60 | timer.modifyingLock.Unlock() 61 | } 62 | 63 | func (timer *Timer) DelSync() { 64 | timer.Del() 65 | timer.runningLock.Lock() 66 | timer.Del() 67 | timer.runningLock.Unlock() 68 | } 69 | 70 | func (timer *Timer) IsPending() bool { 71 | timer.modifyingLock.RLock() 72 | defer timer.modifyingLock.RUnlock() 73 | return timer.isPending 74 | } 75 | 76 | func (peer *Peer) timersActive() bool { 77 | return peer.isRunning.Get() && peer.device != nil && peer.device.isUp.Get() && len(peer.device.peers.keyMap) > 0 78 | } 79 | 80 | func expiredRetransmitHandshake(peer *Peer) { 81 | if atomic.LoadUint32(&peer.timers.handshakeAttempts) > MaxTimerHandshakes { 82 | peer.device.log.Debug.Printf("%s - Handshake did not complete after %d attempts, giving up\n", peer, MaxTimerHandshakes+2) 83 | 84 | if peer.timersActive() { 85 | peer.timers.sendKeepalive.Del() 86 | } 87 | 88 | /* We drop all packets without a keypair and don't try again, 89 | * if we try unsuccessfully for too long to make a handshake. 90 | */ 91 | peer.FlushNonceQueue() 92 | 93 | /* We set a timer for destroying any residue that might be left 94 | * of a partial exchange. 95 | */ 96 | if peer.timersActive() && !peer.timers.zeroKeyMaterial.IsPending() { 97 | peer.timers.zeroKeyMaterial.Mod(RejectAfterTime * 3) 98 | } 99 | } else { 100 | atomic.AddUint32(&peer.timers.handshakeAttempts, 1) 101 | peer.device.log.Debug.Printf("%s - Handshake did not complete after %d seconds, retrying (try %d)\n", peer, int(RekeyTimeout.Seconds()), atomic.LoadUint32(&peer.timers.handshakeAttempts)+1) 102 | 103 | /* We clear the endpoint address src address, in case this is the cause of trouble. */ 104 | peer.Lock() 105 | if peer.endpoint != nil { 106 | peer.endpoint.ClearSrc() 107 | } 108 | peer.Unlock() 109 | 110 | peer.SendHandshakeInitiation(true) 111 | } 112 | } 113 | 114 | func expiredSendKeepalive(peer *Peer) { 115 | peer.SendKeepalive() 116 | if peer.timers.needAnotherKeepalive.Get() { 117 | peer.timers.needAnotherKeepalive.Set(false) 118 | if peer.timersActive() { 119 | peer.timers.sendKeepalive.Mod(KeepaliveTimeout) 120 | } 121 | } 122 | } 123 | 124 | func expiredNewHandshake(peer *Peer) { 125 | peer.device.log.Debug.Printf("%s - Retrying handshake because we stopped hearing back after %d seconds\n", peer, int((KeepaliveTimeout + RekeyTimeout).Seconds())) 126 | /* We clear the endpoint address src address, in case this is the cause of trouble. */ 127 | peer.Lock() 128 | if peer.endpoint != nil { 129 | peer.endpoint.ClearSrc() 130 | } 131 | peer.Unlock() 132 | peer.SendHandshakeInitiation(false) 133 | 134 | } 135 | 136 | func expiredZeroKeyMaterial(peer *Peer) { 137 | peer.device.log.Debug.Printf("%s - Removing all keys, since we haven't received a new one in %d seconds\n", peer, int((RejectAfterTime * 3).Seconds())) 138 | peer.ZeroAndFlushAll() 139 | } 140 | 141 | func expiredPersistentKeepalive(peer *Peer) { 142 | if peer.persistentKeepaliveInterval > 0 { 143 | peer.SendKeepalive() 144 | } 145 | } 146 | 147 | /* Should be called after an authenticated data packet is sent. */ 148 | func (peer *Peer) timersDataSent() { 149 | if peer.timersActive() && !peer.timers.newHandshake.IsPending() { 150 | peer.timers.newHandshake.Mod(KeepaliveTimeout + RekeyTimeout) 151 | } 152 | } 153 | 154 | /* Should be called after an authenticated data packet is received. */ 155 | func (peer *Peer) timersDataReceived() { 156 | if peer.timersActive() { 157 | if !peer.timers.sendKeepalive.IsPending() { 158 | peer.timers.sendKeepalive.Mod(KeepaliveTimeout) 159 | } else { 160 | peer.timers.needAnotherKeepalive.Set(true) 161 | } 162 | } 163 | } 164 | 165 | /* Should be called after any type of authenticated packet is sent -- keepalive, data, or handshake. */ 166 | func (peer *Peer) timersAnyAuthenticatedPacketSent() { 167 | if peer.timersActive() { 168 | peer.timers.sendKeepalive.Del() 169 | } 170 | } 171 | 172 | /* Should be called after any type of authenticated packet is received -- keepalive, data, or handshake. */ 173 | func (peer *Peer) timersAnyAuthenticatedPacketReceived() { 174 | if peer.timersActive() { 175 | peer.timers.newHandshake.Del() 176 | } 177 | } 178 | 179 | /* Should be called after a handshake initiation message is sent. */ 180 | func (peer *Peer) timersHandshakeInitiated() { 181 | if peer.timersActive() { 182 | peer.timers.retransmitHandshake.Mod(RekeyTimeout + time.Millisecond*time.Duration(rand.Int31n(RekeyTimeoutJitterMaxMs))) 183 | } 184 | } 185 | 186 | /* Should be called after a handshake response message is received and processed or when getting key confirmation via the first data message. */ 187 | func (peer *Peer) timersHandshakeComplete() { 188 | if peer.timersActive() { 189 | peer.timers.retransmitHandshake.Del() 190 | } 191 | atomic.StoreUint32(&peer.timers.handshakeAttempts, 0) 192 | peer.timers.sentLastMinuteHandshake.Set(false) 193 | atomic.StoreInt64(&peer.stats.lastHandshakeNano, time.Now().UnixNano()) 194 | } 195 | 196 | /* Should be called after an ephemeral key is created, which is before sending a handshake response or after receiving a handshake response. */ 197 | func (peer *Peer) timersSessionDerived() { 198 | if peer.timersActive() { 199 | peer.timers.zeroKeyMaterial.Mod(RejectAfterTime * 3) 200 | } 201 | } 202 | 203 | /* Should be called before a packet with authentication -- keepalive, data, or handshake -- is sent, or after one is received. */ 204 | func (peer *Peer) timersAnyAuthenticatedPacketTraversal() { 205 | if peer.persistentKeepaliveInterval > 0 && peer.timersActive() { 206 | peer.timers.persistentKeepalive.Mod(time.Duration(peer.persistentKeepaliveInterval) * time.Second) 207 | } 208 | } 209 | 210 | func (peer *Peer) timersInit() { 211 | peer.timers.retransmitHandshake = peer.NewTimer(expiredRetransmitHandshake) 212 | peer.timers.sendKeepalive = peer.NewTimer(expiredSendKeepalive) 213 | peer.timers.newHandshake = peer.NewTimer(expiredNewHandshake) 214 | peer.timers.zeroKeyMaterial = peer.NewTimer(expiredZeroKeyMaterial) 215 | peer.timers.persistentKeepalive = peer.NewTimer(expiredPersistentKeepalive) 216 | atomic.StoreUint32(&peer.timers.handshakeAttempts, 0) 217 | peer.timers.sentLastMinuteHandshake.Set(false) 218 | peer.timers.needAnotherKeepalive.Set(false) 219 | } 220 | 221 | func (peer *Peer) timersStop() { 222 | peer.timers.retransmitHandshake.DelSync() 223 | peer.timers.sendKeepalive.DelSync() 224 | peer.timers.newHandshake.DelSync() 225 | peer.timers.zeroKeyMaterial.DelSync() 226 | peer.timers.persistentKeepalive.DelSync() 227 | } 228 | -------------------------------------------------------------------------------- /device/tun.go: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: MIT 2 | * 3 | * Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved. 4 | */ 5 | 6 | package device 7 | 8 | import ( 9 | "golang.zx2c4.com/wireguard/tun" 10 | "sync/atomic" 11 | ) 12 | 13 | const DefaultMTU = 1420 14 | 15 | func (device *Device) RoutineTUNEventReader() { 16 | setUp := false 17 | logDebug := device.log.Debug 18 | logInfo := device.log.Info 19 | logError := device.log.Error 20 | 21 | logDebug.Println("Routine: event worker - started") 22 | device.state.starting.Done() 23 | 24 | for event := range device.tun.device.Events() { 25 | if event&tun.TUNEventMTUUpdate != 0 { 26 | mtu, err := device.tun.device.MTU() 27 | old := atomic.LoadInt32(&device.tun.mtu) 28 | if err != nil { 29 | logError.Println("Failed to load updated MTU of device:", err) 30 | } else if int(old) != mtu { 31 | if mtu+MessageTransportSize > MaxMessageSize { 32 | logInfo.Println("MTU updated:", mtu, "(too large)") 33 | } else { 34 | logInfo.Println("MTU updated:", mtu) 35 | } 36 | atomic.StoreInt32(&device.tun.mtu, int32(mtu)) 37 | } 38 | } 39 | 40 | if event&tun.TUNEventUp != 0 && !setUp { 41 | logInfo.Println("Interface set up") 42 | setUp = true 43 | device.Up() 44 | } 45 | 46 | if event&tun.TUNEventDown != 0 && setUp { 47 | logInfo.Println("Interface set down") 48 | setUp = false 49 | device.Down() 50 | } 51 | } 52 | 53 | logDebug.Println("Routine: event worker - stopped") 54 | device.state.stopping.Done() 55 | } 56 | -------------------------------------------------------------------------------- /device/uapi.go: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: MIT 2 | * 3 | * Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved. 4 | */ 5 | 6 | package device 7 | 8 | import ( 9 | "bufio" 10 | "fmt" 11 | "golang.zx2c4.com/wireguard/ipc" 12 | "io" 13 | "net" 14 | "strconv" 15 | "strings" 16 | "sync/atomic" 17 | "time" 18 | ) 19 | 20 | type IPCError struct { 21 | int64 22 | } 23 | 24 | func (s IPCError) Error() string { 25 | return fmt.Sprintf("IPC error: %d", s.int64) 26 | } 27 | 28 | func (s IPCError) ErrorCode() int64 { 29 | return s.int64 30 | } 31 | 32 | func (device *Device) IpcGetOperation(socket *bufio.Writer) *IPCError { 33 | 34 | device.log.Debug.Println("UAPI: Processing get operation") 35 | 36 | // create lines 37 | 38 | lines := make([]string, 0, 100) 39 | send := func(line string) { 40 | lines = append(lines, line) 41 | } 42 | 43 | func() { 44 | 45 | // lock required resources 46 | 47 | device.net.RLock() 48 | defer device.net.RUnlock() 49 | 50 | device.staticIdentity.RLock() 51 | defer device.staticIdentity.RUnlock() 52 | 53 | device.peers.RLock() 54 | defer device.peers.RUnlock() 55 | 56 | // serialize device related values 57 | 58 | if !device.staticIdentity.privateKey.IsZero() { 59 | send("private_key=" + device.staticIdentity.privateKey.ToHex()) 60 | } 61 | 62 | if device.net.port != 0 { 63 | send(fmt.Sprintf("listen_port=%d", device.net.port)) 64 | } 65 | 66 | if device.net.fwmark != 0 { 67 | send(fmt.Sprintf("fwmark=%d", device.net.fwmark)) 68 | } 69 | 70 | // serialize each peer state 71 | 72 | for _, peer := range device.peers.keyMap { 73 | peer.RLock() 74 | defer peer.RUnlock() 75 | 76 | send("public_key=" + peer.handshake.remoteStatic.ToHex()) 77 | send("preshared_key=" + peer.handshake.presharedKey.ToHex()) 78 | send("protocol_version=1") 79 | if peer.endpoint != nil { 80 | send("endpoint=" + peer.endpoint.DstToString()) 81 | } 82 | 83 | nano := atomic.LoadInt64(&peer.stats.lastHandshakeNano) 84 | secs := nano / time.Second.Nanoseconds() 85 | nano %= time.Second.Nanoseconds() 86 | 87 | send(fmt.Sprintf("last_handshake_time_sec=%d", secs)) 88 | send(fmt.Sprintf("last_handshake_time_nsec=%d", nano)) 89 | send(fmt.Sprintf("tx_bytes=%d", atomic.LoadUint64(&peer.stats.txBytes))) 90 | send(fmt.Sprintf("rx_bytes=%d", atomic.LoadUint64(&peer.stats.rxBytes))) 91 | send(fmt.Sprintf("persistent_keepalive_interval=%d", peer.persistentKeepaliveInterval)) 92 | 93 | for _, ip := range device.allowedips.EntriesForPeer(peer) { 94 | send("allowed_ip=" + ip.String()) 95 | } 96 | 97 | } 98 | }() 99 | 100 | // send lines (does not require resource locks) 101 | 102 | for _, line := range lines { 103 | _, err := socket.WriteString(line + "\n") 104 | if err != nil { 105 | return &IPCError{ipc.IpcErrorIO} 106 | } 107 | } 108 | 109 | return nil 110 | } 111 | 112 | func (device *Device) IpcSetOperation(socket *bufio.Reader) *IPCError { 113 | scanner := bufio.NewScanner(socket) 114 | logError := device.log.Error 115 | logDebug := device.log.Debug 116 | 117 | var peer *Peer 118 | 119 | dummy := false 120 | deviceConfig := true 121 | 122 | for scanner.Scan() { 123 | 124 | // parse line 125 | 126 | line := scanner.Text() 127 | if line == "" { 128 | return nil 129 | } 130 | parts := strings.Split(line, "=") 131 | if len(parts) != 2 { 132 | return &IPCError{ipc.IpcErrorProtocol} 133 | } 134 | key := parts[0] 135 | value := parts[1] 136 | 137 | /* device configuration */ 138 | 139 | if deviceConfig { 140 | 141 | switch key { 142 | case "private_key": 143 | var sk NoisePrivateKey 144 | err := sk.FromHex(value) 145 | if err != nil { 146 | logError.Println("Failed to set private_key:", err) 147 | return &IPCError{ipc.IpcErrorInvalid} 148 | } 149 | logDebug.Println("UAPI: Updating private key") 150 | device.SetPrivateKey(sk) 151 | 152 | case "listen_port": 153 | 154 | // parse port number 155 | 156 | port, err := strconv.ParseUint(value, 10, 16) 157 | if err != nil { 158 | logError.Println("Failed to parse listen_port:", err) 159 | return &IPCError{ipc.IpcErrorInvalid} 160 | } 161 | 162 | // update port and rebind 163 | 164 | logDebug.Println("UAPI: Updating listen port") 165 | 166 | device.net.Lock() 167 | device.net.port = uint16(port) 168 | device.net.Unlock() 169 | 170 | if err := device.BindUpdate(); err != nil { 171 | logError.Println("Failed to set listen_port:", err) 172 | return &IPCError{ipc.IpcErrorPortInUse} 173 | } 174 | 175 | case "fwmark": 176 | 177 | // parse fwmark field 178 | 179 | fwmark, err := func() (uint32, error) { 180 | if value == "" { 181 | return 0, nil 182 | } 183 | mark, err := strconv.ParseUint(value, 10, 32) 184 | return uint32(mark), err 185 | }() 186 | 187 | if err != nil { 188 | logError.Println("Invalid fwmark", err) 189 | return &IPCError{ipc.IpcErrorInvalid} 190 | } 191 | 192 | logDebug.Println("UAPI: Updating fwmark") 193 | 194 | if err := device.BindSetMark(uint32(fwmark)); err != nil { 195 | logError.Println("Failed to update fwmark:", err) 196 | return &IPCError{ipc.IpcErrorPortInUse} 197 | } 198 | 199 | case "public_key": 200 | // switch to peer configuration 201 | logDebug.Println("UAPI: Transition to peer configuration") 202 | deviceConfig = false 203 | 204 | case "replace_peers": 205 | if value != "true" { 206 | logError.Println("Failed to set replace_peers, invalid value:", value) 207 | return &IPCError{ipc.IpcErrorInvalid} 208 | } 209 | logDebug.Println("UAPI: Removing all peers") 210 | device.RemoveAllPeers() 211 | 212 | default: 213 | logError.Println("Invalid UAPI device key:", key) 214 | return &IPCError{ipc.IpcErrorInvalid} 215 | } 216 | } 217 | 218 | /* peer configuration */ 219 | 220 | if !deviceConfig { 221 | 222 | switch key { 223 | 224 | case "public_key": 225 | var publicKey NoisePublicKey 226 | err := publicKey.FromHex(value) 227 | if err != nil { 228 | logError.Println("Failed to get peer by public key:", err) 229 | return &IPCError{ipc.IpcErrorInvalid} 230 | } 231 | 232 | // ignore peer with public key of device 233 | 234 | device.staticIdentity.RLock() 235 | dummy = device.staticIdentity.publicKey.Equals(publicKey) 236 | device.staticIdentity.RUnlock() 237 | 238 | if dummy { 239 | peer = &Peer{} 240 | } else { 241 | peer = device.LookupPeer(publicKey) 242 | } 243 | 244 | if peer == nil { 245 | peer, err = device.NewPeer(publicKey) 246 | if err != nil { 247 | logError.Println("Failed to create new peer:", err) 248 | return &IPCError{ipc.IpcErrorInvalid} 249 | } 250 | logDebug.Println(peer, "- UAPI: Created") 251 | } 252 | 253 | case "remove": 254 | 255 | // remove currently selected peer from device 256 | 257 | if value != "true" { 258 | logError.Println("Failed to set remove, invalid value:", value) 259 | return &IPCError{ipc.IpcErrorInvalid} 260 | } 261 | if !dummy { 262 | logDebug.Println(peer, "- UAPI: Removing") 263 | device.RemovePeer(peer.handshake.remoteStatic) 264 | } 265 | peer = &Peer{} 266 | dummy = true 267 | 268 | case "preshared_key": 269 | 270 | // update PSK 271 | 272 | logDebug.Println(peer, "- UAPI: Updating preshared key") 273 | 274 | peer.handshake.mutex.Lock() 275 | err := peer.handshake.presharedKey.FromHex(value) 276 | peer.handshake.mutex.Unlock() 277 | 278 | if err != nil { 279 | logError.Println("Failed to set preshared key:", err) 280 | return &IPCError{ipc.IpcErrorInvalid} 281 | } 282 | 283 | case "endpoint": 284 | 285 | // set endpoint destination 286 | 287 | logDebug.Println(peer, "- UAPI: Updating endpoint") 288 | 289 | err := func() error { 290 | peer.Lock() 291 | defer peer.Unlock() 292 | endpoint, err := CreateEndpoint(value) 293 | if err != nil { 294 | return err 295 | } 296 | peer.endpoint = endpoint 297 | return nil 298 | }() 299 | 300 | if err != nil { 301 | logError.Println("Failed to set endpoint:", value) 302 | return &IPCError{ipc.IpcErrorInvalid} 303 | } 304 | 305 | case "persistent_keepalive_interval": 306 | 307 | // update persistent keepalive interval 308 | 309 | logDebug.Println(peer, "- UAPI: Updating persistent keepalive interval") 310 | 311 | secs, err := strconv.ParseUint(value, 10, 16) 312 | if err != nil { 313 | logError.Println("Failed to set persistent keepalive interval:", err) 314 | return &IPCError{ipc.IpcErrorInvalid} 315 | } 316 | 317 | old := peer.persistentKeepaliveInterval 318 | peer.persistentKeepaliveInterval = uint16(secs) 319 | 320 | // send immediate keepalive if we're turning it on and before it wasn't on 321 | 322 | if old == 0 && secs != 0 { 323 | if err != nil { 324 | logError.Println("Failed to get tun device status:", err) 325 | return &IPCError{ipc.IpcErrorIO} 326 | } 327 | if device.isUp.Get() && !dummy { 328 | peer.SendKeepalive() 329 | } 330 | } 331 | 332 | case "replace_allowed_ips": 333 | 334 | logDebug.Println(peer, "- UAPI: Removing all allowedips") 335 | 336 | if value != "true" { 337 | logError.Println("Failed to replace allowedips, invalid value:", value) 338 | return &IPCError{ipc.IpcErrorInvalid} 339 | } 340 | 341 | if dummy { 342 | continue 343 | } 344 | 345 | device.allowedips.RemoveByPeer(peer) 346 | 347 | case "allowed_ip": 348 | 349 | logDebug.Println(peer, "- UAPI: Adding allowedip") 350 | 351 | _, network, err := net.ParseCIDR(value) 352 | if err != nil { 353 | logError.Println("Failed to set allowed ip:", err) 354 | return &IPCError{ipc.IpcErrorInvalid} 355 | } 356 | 357 | if dummy { 358 | continue 359 | } 360 | 361 | ones, _ := network.Mask.Size() 362 | device.allowedips.Insert(network.IP, uint(ones), peer) 363 | 364 | case "protocol_version": 365 | 366 | if value != "1" { 367 | logError.Println("Invalid protocol version:", value) 368 | return &IPCError{ipc.IpcErrorInvalid} 369 | } 370 | 371 | default: 372 | logError.Println("Invalid UAPI peer key:", key) 373 | return &IPCError{ipc.IpcErrorInvalid} 374 | } 375 | } 376 | } 377 | 378 | return nil 379 | } 380 | 381 | func (device *Device) IpcHandle(socket net.Conn) { 382 | 383 | // create buffered read/writer 384 | 385 | defer socket.Close() 386 | 387 | buffered := func(s io.ReadWriter) *bufio.ReadWriter { 388 | reader := bufio.NewReader(s) 389 | writer := bufio.NewWriter(s) 390 | return bufio.NewReadWriter(reader, writer) 391 | }(socket) 392 | 393 | defer buffered.Flush() 394 | 395 | op, err := buffered.ReadString('\n') 396 | if err != nil { 397 | return 398 | } 399 | 400 | // handle operation 401 | 402 | var status *IPCError 403 | 404 | switch op { 405 | case "set=1\n": 406 | device.log.Debug.Println("UAPI: Set operation") 407 | status = device.IpcSetOperation(buffered.Reader) 408 | 409 | case "get=1\n": 410 | device.log.Debug.Println("UAPI: Get operation") 411 | status = device.IpcGetOperation(buffered.Writer) 412 | 413 | default: 414 | device.log.Error.Println("Invalid UAPI operation:", op) 415 | return 416 | } 417 | 418 | // write status 419 | 420 | if status != nil { 421 | device.log.Error.Println(status) 422 | fmt.Fprintf(buffered, "errno=%d\n\n", status.ErrorCode()) 423 | } else { 424 | fmt.Fprintf(buffered, "errno=0\n\n") 425 | } 426 | } 427 | -------------------------------------------------------------------------------- /device/version.go: -------------------------------------------------------------------------------- 1 | package device 2 | 3 | const WireGuardGoVersion = "0.0.20181222" 4 | -------------------------------------------------------------------------------- /donotuseon_linux.go: -------------------------------------------------------------------------------- 1 | // +build !android 2 | 3 | /* SPDX-License-Identifier: MIT 4 | * 5 | * Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved. 6 | */ 7 | 8 | package main 9 | 10 | const DoNotUseThisProgramOnLinux = UseTheKernelModuleInstead 11 | 12 | // -------------------------------------------------------- 13 | // Do not use this on Linux. Instead use the kernel module. 14 | // See wireguard.com/install for more information. 15 | // -------------------------------------------------------- 16 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module golang.zx2c4.com/wireguard 2 | 3 | require ( 4 | github.com/Microsoft/go-winio v0.4.11 5 | golang.org/x/crypto v0.0.0-20190228161510-8dd112bcdc25 6 | golang.org/x/net v0.0.0-20190301231341-16b79f2e4e95 7 | golang.org/x/sys v0.0.0-20190302025703-b6889370fb10 8 | ) 9 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/Microsoft/go-winio v0.4.11 h1:zoIOcVf0xPN1tnMVbTtEdI+P8OofVk3NObnwOQ6nK2Q= 2 | github.com/Microsoft/go-winio v0.4.11/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA= 3 | golang.org/x/crypto v0.0.0-20190228161510-8dd112bcdc25 h1:jsG6UpNLt9iAsb0S2AGW28DveNzzgmbXR+ENoPjUeIU= 4 | golang.org/x/crypto v0.0.0-20190228161510-8dd112bcdc25/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 5 | golang.org/x/net v0.0.0-20190301231341-16b79f2e4e95 h1:fY7Dsw114eJN4boqzVSbpVHO6rTdhq6/GnXeu+PKnzU= 6 | golang.org/x/net v0.0.0-20190301231341-16b79f2e4e95/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 7 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 8 | golang.org/x/sys v0.0.0-20190302025703-b6889370fb10 h1:xQJI9OEiErEQ++DoXOHqEpzsGMrAv2Q2jyCpi7DmfpQ= 9 | golang.org/x/sys v0.0.0-20190302025703-b6889370fb10/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 10 | -------------------------------------------------------------------------------- /ipc/uapi_bsd.go: -------------------------------------------------------------------------------- 1 | // +build darwin freebsd openbsd 2 | 3 | /* SPDX-License-Identifier: MIT 4 | * 5 | * Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved. 6 | */ 7 | 8 | package ipc 9 | 10 | import ( 11 | "errors" 12 | "fmt" 13 | "golang.org/x/sys/unix" 14 | "net" 15 | "os" 16 | "path" 17 | "unsafe" 18 | ) 19 | 20 | var socketDirectory = "/var/run/wireguard" 21 | 22 | const ( 23 | IpcErrorIO = -int64(unix.EIO) 24 | IpcErrorProtocol = -int64(unix.EPROTO) 25 | IpcErrorInvalid = -int64(unix.EINVAL) 26 | IpcErrorPortInUse = -int64(unix.EADDRINUSE) 27 | socketName = "%s.sock" 28 | ) 29 | 30 | type UAPIListener struct { 31 | listener net.Listener // unix socket listener 32 | connNew chan net.Conn 33 | connErr chan error 34 | kqueueFd int 35 | keventFd int 36 | } 37 | 38 | func (l *UAPIListener) Accept() (net.Conn, error) { 39 | for { 40 | select { 41 | case conn := <-l.connNew: 42 | return conn, nil 43 | 44 | case err := <-l.connErr: 45 | return nil, err 46 | } 47 | } 48 | } 49 | 50 | func (l *UAPIListener) Close() error { 51 | err1 := unix.Close(l.kqueueFd) 52 | err2 := unix.Close(l.keventFd) 53 | err3 := l.listener.Close() 54 | if err1 != nil { 55 | return err1 56 | } 57 | if err2 != nil { 58 | return err2 59 | } 60 | return err3 61 | } 62 | 63 | func (l *UAPIListener) Addr() net.Addr { 64 | return l.listener.Addr() 65 | } 66 | 67 | func UAPIListen(name string, file *os.File) (net.Listener, error) { 68 | 69 | // wrap file in listener 70 | 71 | listener, err := net.FileListener(file) 72 | if err != nil { 73 | return nil, err 74 | } 75 | 76 | uapi := &UAPIListener{ 77 | listener: listener, 78 | connNew: make(chan net.Conn, 1), 79 | connErr: make(chan error, 1), 80 | } 81 | 82 | if unixListener, ok := listener.(*net.UnixListener); ok { 83 | unixListener.SetUnlinkOnClose(true) 84 | } 85 | 86 | socketPath := path.Join( 87 | socketDirectory, 88 | fmt.Sprintf(socketName, name), 89 | ) 90 | 91 | // watch for deletion of socket 92 | 93 | uapi.kqueueFd, err = unix.Kqueue() 94 | if err != nil { 95 | return nil, err 96 | } 97 | uapi.keventFd, err = unix.Open(socketDirectory, unix.O_RDONLY, 0) 98 | if err != nil { 99 | unix.Close(uapi.kqueueFd) 100 | return nil, err 101 | } 102 | 103 | go func(l *UAPIListener) { 104 | event := unix.Kevent_t{ 105 | Filter: unix.EVFILT_VNODE, 106 | Flags: unix.EV_ADD | unix.EV_ENABLE | unix.EV_ONESHOT, 107 | Fflags: unix.NOTE_WRITE, 108 | } 109 | // Allow this assignment to work with both the 32-bit and 64-bit version 110 | // of the above struct. If you know another way, please submit a patch. 111 | *(*uintptr)(unsafe.Pointer(&event.Ident)) = uintptr(uapi.keventFd) 112 | events := make([]unix.Kevent_t, 1) 113 | n := 1 114 | var kerr error 115 | for { 116 | // start with lstat to avoid race condition 117 | if _, err := os.Lstat(socketPath); os.IsNotExist(err) { 118 | l.connErr <- err 119 | return 120 | } 121 | if kerr != nil || n != 1 { 122 | if kerr != nil { 123 | l.connErr <- kerr 124 | } else { 125 | l.connErr <- errors.New("kqueue returned empty") 126 | } 127 | return 128 | } 129 | n, kerr = unix.Kevent(uapi.kqueueFd, []unix.Kevent_t{event}, events, nil) 130 | } 131 | }(uapi) 132 | 133 | // watch for new connections 134 | 135 | go func(l *UAPIListener) { 136 | for { 137 | conn, err := l.listener.Accept() 138 | if err != nil { 139 | l.connErr <- err 140 | break 141 | } 142 | l.connNew <- conn 143 | } 144 | }(uapi) 145 | 146 | return uapi, nil 147 | } 148 | 149 | func UAPIOpen(name string) (*os.File, error) { 150 | 151 | // check if path exist 152 | 153 | err := os.MkdirAll(socketDirectory, 0755) 154 | if err != nil && !os.IsExist(err) { 155 | return nil, err 156 | } 157 | 158 | // open UNIX socket 159 | 160 | socketPath := path.Join( 161 | socketDirectory, 162 | fmt.Sprintf(socketName, name), 163 | ) 164 | 165 | addr, err := net.ResolveUnixAddr("unix", socketPath) 166 | if err != nil { 167 | return nil, err 168 | } 169 | 170 | oldUmask := unix.Umask(0077) 171 | listener, err := func() (*net.UnixListener, error) { 172 | 173 | // initial connection attempt 174 | 175 | listener, err := net.ListenUnix("unix", addr) 176 | if err == nil { 177 | return listener, nil 178 | } 179 | 180 | // check if socket already active 181 | 182 | _, err = net.Dial("unix", socketPath) 183 | if err == nil { 184 | return nil, errors.New("unix socket in use") 185 | } 186 | 187 | // cleanup & attempt again 188 | 189 | err = os.Remove(socketPath) 190 | if err != nil { 191 | return nil, err 192 | } 193 | return net.ListenUnix("unix", addr) 194 | }() 195 | unix.Umask(oldUmask) 196 | 197 | if err != nil { 198 | return nil, err 199 | } 200 | 201 | return listener.File() 202 | } 203 | -------------------------------------------------------------------------------- /ipc/uapi_illumos.go: -------------------------------------------------------------------------------- 1 | // +build solaris 2 | 3 | /* SPDX-License-Identifier: MIT 4 | * 5 | * Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved. 6 | * Copyright 2019 Joyent, Inc. 7 | */ 8 | 9 | package ipc 10 | 11 | import ( 12 | "errors" 13 | "fmt" 14 | "golang.org/x/sys/unix" 15 | "net" 16 | "os" 17 | "path" 18 | ) 19 | 20 | var socketDirectory = "/var/run/wireguard" 21 | 22 | const ( 23 | IpcErrorIO = -int64(unix.EIO) 24 | IpcErrorProtocol = -int64(unix.EPROTO) 25 | IpcErrorInvalid = -int64(unix.EINVAL) 26 | IpcErrorPortInUse = -int64(unix.EADDRINUSE) 27 | socketName = "%s.sock" 28 | ) 29 | 30 | type UAPIListener struct { 31 | listener net.Listener // unix socket listener 32 | connNew chan net.Conn 33 | connErr chan error 34 | kqueueFd int 35 | keventFd int 36 | } 37 | 38 | func (l *UAPIListener) Accept() (net.Conn, error) { 39 | for { 40 | select { 41 | case conn := <-l.connNew: 42 | return conn, nil 43 | 44 | case err := <-l.connErr: 45 | return nil, err 46 | } 47 | } 48 | } 49 | 50 | func (l *UAPIListener) Close() error { 51 | return l.listener.Close() 52 | } 53 | 54 | func (l *UAPIListener) Addr() net.Addr { 55 | return l.listener.Addr() 56 | } 57 | 58 | func UAPIListen(name string, file *os.File) (net.Listener, error) { 59 | 60 | // wrap file in listener 61 | 62 | listener, err := net.FileListener(file) 63 | if err != nil { 64 | return nil, err 65 | } 66 | 67 | uapi := &UAPIListener{ 68 | listener: listener, 69 | connNew: make(chan net.Conn, 1), 70 | connErr: make(chan error, 1), 71 | } 72 | 73 | if unixListener, ok := listener.(*net.UnixListener); ok { 74 | unixListener.SetUnlinkOnClose(true) 75 | } 76 | 77 | // watch for new connections 78 | 79 | go func(l *UAPIListener) { 80 | for { 81 | conn, err := l.listener.Accept() 82 | if err != nil { 83 | l.connErr <- err 84 | break 85 | } 86 | l.connNew <- conn 87 | } 88 | }(uapi) 89 | 90 | return uapi, nil 91 | } 92 | 93 | func UAPIOpen(name string) (*os.File, error) { 94 | 95 | // check if path exist 96 | 97 | err := os.MkdirAll(socketDirectory, 0700) 98 | if err != nil && !os.IsExist(err) { 99 | return nil, err 100 | } 101 | 102 | // open UNIX socket 103 | 104 | socketPath := path.Join( 105 | socketDirectory, 106 | fmt.Sprintf(socketName, name), 107 | ) 108 | 109 | addr, err := net.ResolveUnixAddr("unix", socketPath) 110 | if err != nil { 111 | return nil, err 112 | } 113 | 114 | oldUmask := unix.Umask(0077) 115 | listener, err := func() (*net.UnixListener, error) { 116 | 117 | // initial connection attempt 118 | 119 | listener, err := net.ListenUnix("unix", addr) 120 | if err == nil { 121 | return listener, nil 122 | } 123 | 124 | // check if socket already active 125 | 126 | _, err = net.Dial("unix", socketPath) 127 | if err == nil { 128 | return nil, errors.New("unix socket in use") 129 | } 130 | 131 | // cleanup & attempt again 132 | 133 | err = os.Remove(socketPath) 134 | if err != nil { 135 | return nil, err 136 | } 137 | return net.ListenUnix("unix", addr) 138 | }() 139 | unix.Umask(oldUmask) 140 | 141 | if err != nil { 142 | return nil, err 143 | } 144 | 145 | return listener.File() 146 | } 147 | -------------------------------------------------------------------------------- /ipc/uapi_linux.go: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: MIT 2 | * 3 | * Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved. 4 | */ 5 | 6 | package ipc 7 | 8 | import ( 9 | "errors" 10 | "fmt" 11 | "golang.org/x/sys/unix" 12 | "golang.zx2c4.com/wireguard/rwcancel" 13 | "net" 14 | "os" 15 | "path" 16 | ) 17 | 18 | var socketDirectory = "/var/run/wireguard" 19 | 20 | const ( 21 | IpcErrorIO = -int64(unix.EIO) 22 | IpcErrorProtocol = -int64(unix.EPROTO) 23 | IpcErrorInvalid = -int64(unix.EINVAL) 24 | IpcErrorPortInUse = -int64(unix.EADDRINUSE) 25 | socketName = "%s.sock" 26 | ) 27 | 28 | type UAPIListener struct { 29 | listener net.Listener // unix socket listener 30 | connNew chan net.Conn 31 | connErr chan error 32 | inotifyFd int 33 | inotifyRWCancel *rwcancel.RWCancel 34 | } 35 | 36 | func (l *UAPIListener) Accept() (net.Conn, error) { 37 | for { 38 | select { 39 | case conn := <-l.connNew: 40 | return conn, nil 41 | 42 | case err := <-l.connErr: 43 | return nil, err 44 | } 45 | } 46 | } 47 | 48 | func (l *UAPIListener) Close() error { 49 | err1 := unix.Close(l.inotifyFd) 50 | err2 := l.inotifyRWCancel.Cancel() 51 | err3 := l.listener.Close() 52 | if err1 != nil { 53 | return err1 54 | } 55 | if err2 != nil { 56 | return err2 57 | } 58 | return err3 59 | } 60 | 61 | func (l *UAPIListener) Addr() net.Addr { 62 | return l.listener.Addr() 63 | } 64 | 65 | func UAPIListen(name string, file *os.File) (net.Listener, error) { 66 | 67 | // wrap file in listener 68 | 69 | listener, err := net.FileListener(file) 70 | if err != nil { 71 | return nil, err 72 | } 73 | 74 | if unixListener, ok := listener.(*net.UnixListener); ok { 75 | unixListener.SetUnlinkOnClose(true) 76 | } 77 | 78 | uapi := &UAPIListener{ 79 | listener: listener, 80 | connNew: make(chan net.Conn, 1), 81 | connErr: make(chan error, 1), 82 | } 83 | 84 | // watch for deletion of socket 85 | 86 | socketPath := path.Join( 87 | socketDirectory, 88 | fmt.Sprintf(socketName, name), 89 | ) 90 | 91 | uapi.inotifyFd, err = unix.InotifyInit() 92 | if err != nil { 93 | return nil, err 94 | } 95 | 96 | _, err = unix.InotifyAddWatch( 97 | uapi.inotifyFd, 98 | socketPath, 99 | unix.IN_ATTRIB| 100 | unix.IN_DELETE| 101 | unix.IN_DELETE_SELF, 102 | ) 103 | 104 | if err != nil { 105 | return nil, err 106 | } 107 | 108 | uapi.inotifyRWCancel, err = rwcancel.NewRWCancel(uapi.inotifyFd) 109 | if err != nil { 110 | unix.Close(uapi.inotifyFd) 111 | return nil, err 112 | } 113 | 114 | go func(l *UAPIListener) { 115 | var buff [0]byte 116 | for { 117 | // start with lstat to avoid race condition 118 | if _, err := os.Lstat(socketPath); os.IsNotExist(err) { 119 | l.connErr <- err 120 | return 121 | } 122 | _, err := uapi.inotifyRWCancel.Read(buff[:]) 123 | if err != nil { 124 | l.connErr <- err 125 | return 126 | } 127 | } 128 | }(uapi) 129 | 130 | // watch for new connections 131 | 132 | go func(l *UAPIListener) { 133 | for { 134 | conn, err := l.listener.Accept() 135 | if err != nil { 136 | l.connErr <- err 137 | break 138 | } 139 | l.connNew <- conn 140 | } 141 | }(uapi) 142 | 143 | return uapi, nil 144 | } 145 | 146 | func UAPIOpen(name string) (*os.File, error) { 147 | 148 | // check if path exist 149 | 150 | err := os.MkdirAll(socketDirectory, 0755) 151 | if err != nil && !os.IsExist(err) { 152 | return nil, err 153 | } 154 | 155 | // open UNIX socket 156 | 157 | socketPath := path.Join( 158 | socketDirectory, 159 | fmt.Sprintf(socketName, name), 160 | ) 161 | 162 | addr, err := net.ResolveUnixAddr("unix", socketPath) 163 | if err != nil { 164 | return nil, err 165 | } 166 | 167 | oldUmask := unix.Umask(0077) 168 | listener, err := func() (*net.UnixListener, error) { 169 | 170 | // initial connection attempt 171 | 172 | listener, err := net.ListenUnix("unix", addr) 173 | if err == nil { 174 | return listener, nil 175 | } 176 | 177 | // check if socket already active 178 | 179 | _, err = net.Dial("unix", socketPath) 180 | if err == nil { 181 | return nil, errors.New("unix socket in use") 182 | } 183 | 184 | // cleanup & attempt again 185 | 186 | err = os.Remove(socketPath) 187 | if err != nil { 188 | return nil, err 189 | } 190 | return net.ListenUnix("unix", addr) 191 | }() 192 | unix.Umask(oldUmask) 193 | 194 | if err != nil { 195 | return nil, err 196 | } 197 | 198 | return listener.File() 199 | } 200 | -------------------------------------------------------------------------------- /ipc/uapi_windows.go: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: MIT 2 | * 3 | * Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved. 4 | */ 5 | 6 | package ipc 7 | 8 | import ( 9 | "github.com/Microsoft/go-winio" 10 | "net" 11 | ) 12 | 13 | //TODO: replace these with actual standard windows error numbers from the win package 14 | const ( 15 | IpcErrorIO = -int64(5) 16 | IpcErrorProtocol = -int64(71) 17 | IpcErrorInvalid = -int64(22) 18 | IpcErrorPortInUse = -int64(98) 19 | ) 20 | 21 | type UAPIListener struct { 22 | listener net.Listener // unix socket listener 23 | connNew chan net.Conn 24 | connErr chan error 25 | kqueueFd int 26 | keventFd int 27 | } 28 | 29 | func (l *UAPIListener) Accept() (net.Conn, error) { 30 | for { 31 | select { 32 | case conn := <-l.connNew: 33 | return conn, nil 34 | 35 | case err := <-l.connErr: 36 | return nil, err 37 | } 38 | } 39 | } 40 | 41 | func (l *UAPIListener) Close() error { 42 | return l.listener.Close() 43 | } 44 | 45 | func (l *UAPIListener) Addr() net.Addr { 46 | return l.listener.Addr() 47 | } 48 | 49 | func GetSystemSecurityDescriptor() string { 50 | // 51 | // SDDL encoded. 52 | // 53 | // (system = SECURITY_NT_AUTHORITY | SECURITY_LOCAL_SYSTEM_RID) 54 | // owner: system 55 | // grant: GENERIC_ALL to system 56 | // 57 | return "O:SYD:(A;;GA;;;SY)" 58 | } 59 | 60 | func UAPIListen(name string) (net.Listener, error) { 61 | config := winio.PipeConfig{ 62 | SecurityDescriptor: GetSystemSecurityDescriptor(), 63 | } 64 | listener, err := winio.ListenPipe("\\\\.\\pipe\\WireGuard\\"+name, &config) 65 | if err != nil { 66 | return nil, err 67 | } 68 | 69 | uapi := &UAPIListener{ 70 | listener: listener, 71 | connNew: make(chan net.Conn, 1), 72 | connErr: make(chan error, 1), 73 | } 74 | 75 | go func(l *UAPIListener) { 76 | for { 77 | conn, err := l.listener.Accept() 78 | if err != nil { 79 | l.connErr <- err 80 | break 81 | } 82 | l.connNew <- conn 83 | } 84 | }(uapi) 85 | 86 | return uapi, nil 87 | } 88 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | // +build !windows 2 | 3 | /* SPDX-License-Identifier: MIT 4 | * 5 | * Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved. 6 | */ 7 | 8 | package main 9 | 10 | import ( 11 | "fmt" 12 | "golang.zx2c4.com/wireguard/device" 13 | "golang.zx2c4.com/wireguard/ipc" 14 | "golang.zx2c4.com/wireguard/tun" 15 | "os" 16 | "os/signal" 17 | "runtime" 18 | "strconv" 19 | "syscall" 20 | ) 21 | 22 | const ( 23 | ExitSetupSuccess = 0 24 | ExitSetupFailed = 1 25 | ) 26 | 27 | const ( 28 | ENV_WG_TUN_FD = "WG_TUN_FD" 29 | ENV_WG_UAPI_FD = "WG_UAPI_FD" 30 | ENV_WG_PROCESS_FOREGROUND = "WG_PROCESS_FOREGROUND" 31 | ) 32 | 33 | func printUsage() { 34 | fmt.Printf("usage:\n") 35 | fmt.Printf("%s [-f/--foreground] INTERFACE-NAME\n", os.Args[0]) 36 | } 37 | 38 | func warning() { 39 | if os.Getenv(ENV_WG_PROCESS_FOREGROUND) == "1" { 40 | return 41 | } 42 | 43 | shouldQuit := false 44 | 45 | fmt.Fprintln(os.Stderr, "WARNING WARNING WARNING WARNING WARNING WARNING WARNING") 46 | fmt.Fprintln(os.Stderr, "W G") 47 | fmt.Fprintln(os.Stderr, "W This is alpha software. It will very likely not G") 48 | fmt.Fprintln(os.Stderr, "W do what it is supposed to do, and things may go G") 49 | fmt.Fprintln(os.Stderr, "W horribly wrong. You have been warned. Proceed G") 50 | fmt.Fprintln(os.Stderr, "W at your own risk. G") 51 | if runtime.GOOS == "linux" { 52 | shouldQuit = os.Getenv("WG_I_PREFER_BUGGY_USERSPACE_TO_POLISHED_KMOD") != "1" 53 | 54 | fmt.Fprintln(os.Stderr, "W G") 55 | fmt.Fprintln(os.Stderr, "W Furthermore, you are running this software on a G") 56 | fmt.Fprintln(os.Stderr, "W Linux kernel, which is probably unnecessary and G") 57 | fmt.Fprintln(os.Stderr, "W foolish. This is because the Linux kernel has G") 58 | fmt.Fprintln(os.Stderr, "W built-in first class support for WireGuard, and G") 59 | fmt.Fprintln(os.Stderr, "W this support is much more refined than this G") 60 | fmt.Fprintln(os.Stderr, "W program. For more information on installing the G") 61 | fmt.Fprintln(os.Stderr, "W kernel module, please visit: G") 62 | fmt.Fprintln(os.Stderr, "W https://www.wireguard.com/install G") 63 | if shouldQuit { 64 | fmt.Fprintln(os.Stderr, "W G") 65 | fmt.Fprintln(os.Stderr, "W If you still want to use this program, against G") 66 | fmt.Fprintln(os.Stderr, "W the sage advice here, please first export this G") 67 | fmt.Fprintln(os.Stderr, "W environment variable: G") 68 | fmt.Fprintln(os.Stderr, "W WG_I_PREFER_BUGGY_USERSPACE_TO_POLISHED_KMOD=1 G") 69 | } 70 | } 71 | fmt.Fprintln(os.Stderr, "W G") 72 | fmt.Fprintln(os.Stderr, "WARNING WARNING WARNING WARNING WARNING WARNING WARNING") 73 | 74 | if shouldQuit { 75 | os.Exit(1) 76 | } 77 | } 78 | 79 | func main() { 80 | if len(os.Args) == 2 && os.Args[1] == "--version" { 81 | fmt.Printf("wireguard-go v%s\n\nUserspace WireGuard daemon for %s-%s.\nInformation available at https://www.wireguard.com.\nCopyright (C) Jason A. Donenfeld .\n", device.WireGuardGoVersion, runtime.GOOS, runtime.GOARCH) 82 | return 83 | } 84 | 85 | warning() 86 | 87 | // parse arguments 88 | 89 | var foreground bool 90 | var interfaceName string 91 | if len(os.Args) < 2 || len(os.Args) > 3 { 92 | printUsage() 93 | return 94 | } 95 | 96 | switch os.Args[1] { 97 | 98 | case "-f", "--foreground": 99 | foreground = true 100 | if len(os.Args) != 3 { 101 | printUsage() 102 | return 103 | } 104 | interfaceName = os.Args[2] 105 | 106 | default: 107 | foreground = false 108 | if len(os.Args) != 2 { 109 | printUsage() 110 | return 111 | } 112 | interfaceName = os.Args[1] 113 | } 114 | 115 | if !foreground { 116 | foreground = os.Getenv(ENV_WG_PROCESS_FOREGROUND) == "1" 117 | } 118 | 119 | // get log level (default: info) 120 | 121 | logLevel := func() int { 122 | switch os.Getenv("LOG_LEVEL") { 123 | case "debug": 124 | return device.LogLevelDebug 125 | case "info": 126 | return device.LogLevelInfo 127 | case "error": 128 | return device.LogLevelError 129 | case "silent": 130 | return device.LogLevelSilent 131 | } 132 | return device.LogLevelInfo 133 | }() 134 | 135 | // open TUN device (or use supplied fd) 136 | 137 | tun, err := func() (tun.TUNDevice, error) { 138 | tunFdStr := os.Getenv(ENV_WG_TUN_FD) 139 | if tunFdStr == "" { 140 | return tun.CreateTUN(interfaceName, device.DefaultMTU) 141 | } 142 | 143 | // construct tun device from supplied fd 144 | 145 | fd, err := strconv.ParseUint(tunFdStr, 10, 32) 146 | if err != nil { 147 | return nil, err 148 | } 149 | 150 | err = syscall.SetNonblock(int(fd), true) 151 | if err != nil { 152 | return nil, err 153 | } 154 | 155 | file := os.NewFile(uintptr(fd), "") 156 | return tun.CreateTUNFromFile(file, device.DefaultMTU) 157 | }() 158 | 159 | if err == nil { 160 | realInterfaceName, err2 := tun.Name() 161 | if err2 == nil { 162 | interfaceName = realInterfaceName 163 | } 164 | } 165 | 166 | logger := device.NewLogger( 167 | logLevel, 168 | fmt.Sprintf("(%s) ", interfaceName), 169 | ) 170 | 171 | logger.Info.Println("Starting wireguard-go version", device.WireGuardGoVersion) 172 | 173 | logger.Debug.Println("Debug log enabled") 174 | 175 | if err != nil { 176 | logger.Error.Println("Failed to create TUN device:", err) 177 | os.Exit(ExitSetupFailed) 178 | } 179 | 180 | // open UAPI file (or use supplied fd) 181 | 182 | fileUAPI, err := func() (*os.File, error) { 183 | uapiFdStr := os.Getenv(ENV_WG_UAPI_FD) 184 | if uapiFdStr == "" { 185 | return ipc.UAPIOpen(interfaceName) 186 | } 187 | 188 | // use supplied fd 189 | 190 | fd, err := strconv.ParseUint(uapiFdStr, 10, 32) 191 | if err != nil { 192 | return nil, err 193 | } 194 | 195 | return os.NewFile(uintptr(fd), ""), nil 196 | }() 197 | 198 | if err != nil { 199 | logger.Error.Println("UAPI listen error:", err) 200 | os.Exit(ExitSetupFailed) 201 | return 202 | } 203 | // daemonize the process 204 | 205 | if !foreground { 206 | env := os.Environ() 207 | env = append(env, fmt.Sprintf("%s=3", ENV_WG_TUN_FD)) 208 | env = append(env, fmt.Sprintf("%s=4", ENV_WG_UAPI_FD)) 209 | env = append(env, fmt.Sprintf("%s=1", ENV_WG_PROCESS_FOREGROUND)) 210 | files := [3]*os.File{} 211 | if os.Getenv("LOG_LEVEL") != "" && logLevel != device.LogLevelSilent { 212 | files[0], _ = os.Open(os.DevNull) 213 | files[1] = os.Stdout 214 | files[2] = os.Stderr 215 | } else { 216 | files[0], _ = os.Open(os.DevNull) 217 | files[1], _ = os.Open(os.DevNull) 218 | files[2], _ = os.Open(os.DevNull) 219 | } 220 | attr := &os.ProcAttr{ 221 | Files: []*os.File{ 222 | files[0], // stdin 223 | files[1], // stdout 224 | files[2], // stderr 225 | tun.File(), 226 | fileUAPI, 227 | }, 228 | Dir: ".", 229 | Env: env, 230 | } 231 | 232 | path, err := os.Executable() 233 | if err != nil { 234 | logger.Error.Println("Failed to determine executable:", err) 235 | os.Exit(ExitSetupFailed) 236 | } 237 | 238 | process, err := os.StartProcess( 239 | path, 240 | os.Args, 241 | attr, 242 | ) 243 | if err != nil { 244 | logger.Error.Println("Failed to daemonize:", err) 245 | os.Exit(ExitSetupFailed) 246 | } 247 | process.Release() 248 | return 249 | } 250 | 251 | device := device.NewDevice(tun, logger) 252 | 253 | logger.Info.Println("Device started") 254 | 255 | errs := make(chan error) 256 | term := make(chan os.Signal, 1) 257 | 258 | uapi, err := ipc.UAPIListen(interfaceName, fileUAPI) 259 | if err != nil { 260 | logger.Error.Println("Failed to listen on uapi socket:", err) 261 | os.Exit(ExitSetupFailed) 262 | } 263 | 264 | go func() { 265 | for { 266 | conn, err := uapi.Accept() 267 | if err != nil { 268 | errs <- err 269 | return 270 | } 271 | go device.IpcHandle(conn) 272 | } 273 | }() 274 | 275 | logger.Info.Println("UAPI listener started") 276 | 277 | // wait for program to terminate 278 | 279 | signal.Notify(term, syscall.SIGTERM) 280 | signal.Notify(term, os.Interrupt) 281 | 282 | select { 283 | case <-term: 284 | case <-errs: 285 | case <-device.Wait(): 286 | } 287 | 288 | // clean up 289 | 290 | uapi.Close() 291 | device.Close() 292 | 293 | logger.Info.Println("Shutting down") 294 | } 295 | -------------------------------------------------------------------------------- /main_windows.go: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: MIT 2 | * 3 | * Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved. 4 | */ 5 | 6 | package main 7 | 8 | import ( 9 | "fmt" 10 | "golang.zx2c4.com/wireguard/device" 11 | "golang.zx2c4.com/wireguard/ipc" 12 | "os" 13 | "os/signal" 14 | "syscall" 15 | 16 | "golang.zx2c4.com/wireguard/tun" 17 | ) 18 | 19 | const ( 20 | ExitSetupSuccess = 0 21 | ExitSetupFailed = 1 22 | ) 23 | 24 | func main() { 25 | if len(os.Args) != 2 { 26 | os.Exit(ExitSetupFailed) 27 | } 28 | interfaceName := os.Args[1] 29 | 30 | logger := device.NewLogger( 31 | device.LogLevelDebug, 32 | fmt.Sprintf("(%s) ", interfaceName), 33 | ) 34 | logger.Info.Println("Starting wireguard-go version", WireGuardGoVersion) 35 | logger.Debug.Println("Debug log enabled") 36 | 37 | tun, err := tun.CreateTUN(interfaceName) 38 | if err == nil { 39 | realInterfaceName, err2 := tun.Name() 40 | if err2 == nil { 41 | interfaceName = realInterfaceName 42 | } 43 | } else { 44 | logger.Error.Println("Failed to create TUN device:", err) 45 | os.Exit(ExitSetupFailed) 46 | } 47 | 48 | device := device.NewDevice(tun, logger) 49 | device.Up() 50 | logger.Info.Println("Device started") 51 | 52 | uapi, err := ipc.UAPIListen(interfaceName) 53 | if err != nil { 54 | logger.Error.Println("Failed to listen on uapi socket:", err) 55 | os.Exit(ExitSetupFailed) 56 | } 57 | 58 | errs := make(chan error) 59 | term := make(chan os.Signal, 1) 60 | 61 | go func() { 62 | for { 63 | conn, err := uapi.Accept() 64 | if err != nil { 65 | errs <- err 66 | return 67 | } 68 | go device.IpcHandle(conn) 69 | } 70 | }() 71 | logger.Info.Println("UAPI listener started") 72 | 73 | // wait for program to terminate 74 | 75 | signal.Notify(term, os.Interrupt) 76 | signal.Notify(term, os.Kill) 77 | signal.Notify(term, syscall.SIGTERM) 78 | 79 | select { 80 | case <-term: 81 | case <-errs: 82 | case <-device.Wait(): 83 | } 84 | 85 | // clean up 86 | 87 | uapi.Close() 88 | device.Close() 89 | 90 | logger.Info.Println("Shutting down") 91 | } 92 | -------------------------------------------------------------------------------- /ratelimiter/ratelimiter.go: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: MIT 2 | * 3 | * Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved. 4 | */ 5 | 6 | package ratelimiter 7 | 8 | import ( 9 | "net" 10 | "sync" 11 | "time" 12 | ) 13 | 14 | const ( 15 | packetsPerSecond = 20 16 | packetsBurstable = 5 17 | garbageCollectTime = time.Second 18 | packetCost = 1000000000 / packetsPerSecond 19 | maxTokens = packetCost * packetsBurstable 20 | ) 21 | 22 | type RatelimiterEntry struct { 23 | sync.Mutex 24 | lastTime time.Time 25 | tokens int64 26 | } 27 | 28 | type Ratelimiter struct { 29 | sync.RWMutex 30 | stopReset chan struct{} 31 | tableIPv4 map[[net.IPv4len]byte]*RatelimiterEntry 32 | tableIPv6 map[[net.IPv6len]byte]*RatelimiterEntry 33 | } 34 | 35 | func (rate *Ratelimiter) Close() { 36 | rate.Lock() 37 | defer rate.Unlock() 38 | 39 | if rate.stopReset != nil { 40 | close(rate.stopReset) 41 | } 42 | } 43 | 44 | func (rate *Ratelimiter) Init() { 45 | rate.Lock() 46 | defer rate.Unlock() 47 | 48 | // stop any ongoing garbage collection routine 49 | 50 | if rate.stopReset != nil { 51 | close(rate.stopReset) 52 | } 53 | 54 | rate.stopReset = make(chan struct{}) 55 | rate.tableIPv4 = make(map[[net.IPv4len]byte]*RatelimiterEntry) 56 | rate.tableIPv6 = make(map[[net.IPv6len]byte]*RatelimiterEntry) 57 | 58 | // start garbage collection routine 59 | 60 | go func() { 61 | ticker := time.NewTicker(time.Second) 62 | ticker.Stop() 63 | for { 64 | select { 65 | case _, ok := <-rate.stopReset: 66 | ticker.Stop() 67 | if ok { 68 | ticker = time.NewTicker(time.Second) 69 | } else { 70 | return 71 | } 72 | case <-ticker.C: 73 | func() { 74 | rate.Lock() 75 | defer rate.Unlock() 76 | 77 | for key, entry := range rate.tableIPv4 { 78 | entry.Lock() 79 | if time.Now().Sub(entry.lastTime) > garbageCollectTime { 80 | delete(rate.tableIPv4, key) 81 | } 82 | entry.Unlock() 83 | } 84 | 85 | for key, entry := range rate.tableIPv6 { 86 | entry.Lock() 87 | if time.Now().Sub(entry.lastTime) > garbageCollectTime { 88 | delete(rate.tableIPv6, key) 89 | } 90 | entry.Unlock() 91 | } 92 | 93 | if len(rate.tableIPv4) == 0 && len(rate.tableIPv6) == 0 { 94 | ticker.Stop() 95 | } 96 | }() 97 | } 98 | } 99 | }() 100 | } 101 | 102 | func (rate *Ratelimiter) Allow(ip net.IP) bool { 103 | var entry *RatelimiterEntry 104 | var keyIPv4 [net.IPv4len]byte 105 | var keyIPv6 [net.IPv6len]byte 106 | 107 | // lookup entry 108 | 109 | IPv4 := ip.To4() 110 | IPv6 := ip.To16() 111 | 112 | rate.RLock() 113 | 114 | if IPv4 != nil { 115 | copy(keyIPv4[:], IPv4) 116 | entry = rate.tableIPv4[keyIPv4] 117 | } else { 118 | copy(keyIPv6[:], IPv6) 119 | entry = rate.tableIPv6[keyIPv6] 120 | } 121 | 122 | rate.RUnlock() 123 | 124 | // make new entry if not found 125 | 126 | if entry == nil { 127 | entry = new(RatelimiterEntry) 128 | entry.tokens = maxTokens - packetCost 129 | entry.lastTime = time.Now() 130 | rate.Lock() 131 | if IPv4 != nil { 132 | rate.tableIPv4[keyIPv4] = entry 133 | if len(rate.tableIPv4) == 1 && len(rate.tableIPv6) == 0 { 134 | rate.stopReset <- struct{}{} 135 | } 136 | } else { 137 | rate.tableIPv6[keyIPv6] = entry 138 | if len(rate.tableIPv6) == 1 && len(rate.tableIPv4) == 0 { 139 | rate.stopReset <- struct{}{} 140 | } 141 | } 142 | rate.Unlock() 143 | return true 144 | } 145 | 146 | // add tokens to entry 147 | 148 | entry.Lock() 149 | now := time.Now() 150 | entry.tokens += now.Sub(entry.lastTime).Nanoseconds() 151 | entry.lastTime = now 152 | if entry.tokens > maxTokens { 153 | entry.tokens = maxTokens 154 | } 155 | 156 | // subtract cost of packet 157 | 158 | if entry.tokens > packetCost { 159 | entry.tokens -= packetCost 160 | entry.Unlock() 161 | return true 162 | } 163 | entry.Unlock() 164 | return false 165 | } 166 | -------------------------------------------------------------------------------- /ratelimiter/ratelimiter_test.go: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: MIT 2 | * 3 | * Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved. 4 | */ 5 | 6 | package ratelimiter 7 | 8 | import ( 9 | "net" 10 | "testing" 11 | "time" 12 | ) 13 | 14 | type RatelimiterResult struct { 15 | allowed bool 16 | text string 17 | wait time.Duration 18 | } 19 | 20 | func TestRatelimiter(t *testing.T) { 21 | 22 | var ratelimiter Ratelimiter 23 | var expectedResults []RatelimiterResult 24 | 25 | Nano := func(nano int64) time.Duration { 26 | return time.Nanosecond * time.Duration(nano) 27 | } 28 | 29 | Add := func(res RatelimiterResult) { 30 | expectedResults = append( 31 | expectedResults, 32 | res, 33 | ) 34 | } 35 | 36 | for i := 0; i < packetsBurstable; i++ { 37 | Add(RatelimiterResult{ 38 | allowed: true, 39 | text: "inital burst", 40 | }) 41 | } 42 | 43 | Add(RatelimiterResult{ 44 | allowed: false, 45 | text: "after burst", 46 | }) 47 | 48 | Add(RatelimiterResult{ 49 | allowed: true, 50 | wait: Nano(time.Second.Nanoseconds() / packetsPerSecond), 51 | text: "filling tokens for single packet", 52 | }) 53 | 54 | Add(RatelimiterResult{ 55 | allowed: false, 56 | text: "not having refilled enough", 57 | }) 58 | 59 | Add(RatelimiterResult{ 60 | allowed: true, 61 | wait: 2 * (Nano(time.Second.Nanoseconds() / packetsPerSecond)), 62 | text: "filling tokens for two packet burst", 63 | }) 64 | 65 | Add(RatelimiterResult{ 66 | allowed: true, 67 | text: "second packet in 2 packet burst", 68 | }) 69 | 70 | Add(RatelimiterResult{ 71 | allowed: false, 72 | text: "packet following 2 packet burst", 73 | }) 74 | 75 | ips := []net.IP{ 76 | net.ParseIP("127.0.0.1"), 77 | net.ParseIP("192.168.1.1"), 78 | net.ParseIP("172.167.2.3"), 79 | net.ParseIP("97.231.252.215"), 80 | net.ParseIP("248.97.91.167"), 81 | net.ParseIP("188.208.233.47"), 82 | net.ParseIP("104.2.183.179"), 83 | net.ParseIP("72.129.46.120"), 84 | net.ParseIP("2001:0db8:0a0b:12f0:0000:0000:0000:0001"), 85 | net.ParseIP("f5c2:818f:c052:655a:9860:b136:6894:25f0"), 86 | net.ParseIP("b2d7:15ab:48a7:b07c:a541:f144:a9fe:54fc"), 87 | net.ParseIP("a47b:786e:1671:a22b:d6f9:4ab0:abc7:c918"), 88 | net.ParseIP("ea1e:d155:7f7a:98fb:2bf5:9483:80f6:5445"), 89 | net.ParseIP("3f0e:54a2:f5b4:cd19:a21d:58e1:3746:84c4"), 90 | } 91 | 92 | ratelimiter.Init() 93 | 94 | for i, res := range expectedResults { 95 | time.Sleep(res.wait) 96 | for _, ip := range ips { 97 | allowed := ratelimiter.Allow(ip) 98 | if allowed != res.allowed { 99 | t.Fatal("Test failed for", ip.String(), ", on:", i, "(", res.text, ")", "expected:", res.allowed, "got:", allowed) 100 | } 101 | } 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /replay/replay.go: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: MIT 2 | * 3 | * Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved. 4 | */ 5 | 6 | package replay 7 | 8 | /* Implementation of RFC6479 9 | * https://tools.ietf.org/html/rfc6479 10 | * 11 | * The implementation is not safe for concurrent use! 12 | */ 13 | 14 | const ( 15 | // See: https://golang.org/src/math/big/arith.go 16 | _Wordm = ^uintptr(0) 17 | _WordLogSize = _Wordm>>8&1 + _Wordm>>16&1 + _Wordm>>32&1 18 | _WordSize = 1 << _WordLogSize 19 | ) 20 | 21 | const ( 22 | CounterRedundantBitsLog = _WordLogSize + 3 23 | CounterRedundantBits = _WordSize * 8 24 | CounterBitsTotal = 2048 25 | CounterWindowSize = uint64(CounterBitsTotal - CounterRedundantBits) 26 | ) 27 | 28 | const ( 29 | BacktrackWords = CounterBitsTotal / _WordSize 30 | ) 31 | 32 | func minUint64(a uint64, b uint64) uint64 { 33 | if a > b { 34 | return b 35 | } 36 | return a 37 | } 38 | 39 | type ReplayFilter struct { 40 | counter uint64 41 | backtrack [BacktrackWords]uintptr 42 | } 43 | 44 | func (filter *ReplayFilter) Init() { 45 | filter.counter = 0 46 | filter.backtrack[0] = 0 47 | } 48 | 49 | func (filter *ReplayFilter) ValidateCounter(counter uint64, limit uint64) bool { 50 | if counter >= limit { 51 | return false 52 | } 53 | 54 | indexWord := counter >> CounterRedundantBitsLog 55 | 56 | if counter > filter.counter { 57 | 58 | // move window forward 59 | 60 | current := filter.counter >> CounterRedundantBitsLog 61 | diff := minUint64(indexWord-current, BacktrackWords) 62 | for i := uint64(1); i <= diff; i++ { 63 | filter.backtrack[(current+i)%BacktrackWords] = 0 64 | } 65 | filter.counter = counter 66 | 67 | } else if filter.counter-counter > CounterWindowSize { 68 | 69 | // behind current window 70 | 71 | return false 72 | } 73 | 74 | indexWord %= BacktrackWords 75 | indexBit := counter & uint64(CounterRedundantBits-1) 76 | 77 | // check and set bit 78 | 79 | oldValue := filter.backtrack[indexWord] 80 | newValue := oldValue | (1 << indexBit) 81 | filter.backtrack[indexWord] = newValue 82 | return oldValue != newValue 83 | } 84 | -------------------------------------------------------------------------------- /replay/replay_test.go: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: MIT 2 | * 3 | * Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved. 4 | */ 5 | 6 | package replay 7 | 8 | import ( 9 | "testing" 10 | ) 11 | 12 | /* Ported from the linux kernel implementation 13 | * 14 | * 15 | */ 16 | 17 | const RejectAfterMessages = (1 << 64) - (1 << 4) - 1 18 | 19 | func TestReplay(t *testing.T) { 20 | var filter ReplayFilter 21 | 22 | T_LIM := CounterWindowSize + 1 23 | 24 | testNumber := 0 25 | T := func(n uint64, v bool) { 26 | testNumber++ 27 | if filter.ValidateCounter(n, RejectAfterMessages) != v { 28 | t.Fatal("Test", testNumber, "failed", n, v) 29 | } 30 | } 31 | 32 | filter.Init() 33 | 34 | T(0, true) /* 1 */ 35 | T(1, true) /* 2 */ 36 | T(1, false) /* 3 */ 37 | T(9, true) /* 4 */ 38 | T(8, true) /* 5 */ 39 | T(7, true) /* 6 */ 40 | T(7, false) /* 7 */ 41 | T(T_LIM, true) /* 8 */ 42 | T(T_LIM-1, true) /* 9 */ 43 | T(T_LIM-1, false) /* 10 */ 44 | T(T_LIM-2, true) /* 11 */ 45 | T(2, true) /* 12 */ 46 | T(2, false) /* 13 */ 47 | T(T_LIM+16, true) /* 14 */ 48 | T(3, false) /* 15 */ 49 | T(T_LIM+16, false) /* 16 */ 50 | T(T_LIM*4, true) /* 17 */ 51 | T(T_LIM*4-(T_LIM-1), true) /* 18 */ 52 | T(10, false) /* 19 */ 53 | T(T_LIM*4-T_LIM, false) /* 20 */ 54 | T(T_LIM*4-(T_LIM+1), false) /* 21 */ 55 | T(T_LIM*4-(T_LIM-2), true) /* 22 */ 56 | T(T_LIM*4+1-T_LIM, false) /* 23 */ 57 | T(0, false) /* 24 */ 58 | T(RejectAfterMessages, false) /* 25 */ 59 | T(RejectAfterMessages-1, true) /* 26 */ 60 | T(RejectAfterMessages, false) /* 27 */ 61 | T(RejectAfterMessages-1, false) /* 28 */ 62 | T(RejectAfterMessages-2, true) /* 29 */ 63 | T(RejectAfterMessages+1, false) /* 30 */ 64 | T(RejectAfterMessages+2, false) /* 31 */ 65 | T(RejectAfterMessages-2, false) /* 32 */ 66 | T(RejectAfterMessages-3, true) /* 33 */ 67 | T(0, false) /* 34 */ 68 | 69 | t.Log("Bulk test 1") 70 | filter.Init() 71 | testNumber = 0 72 | for i := uint64(1); i <= CounterWindowSize; i++ { 73 | T(i, true) 74 | } 75 | T(0, true) 76 | T(0, false) 77 | 78 | t.Log("Bulk test 2") 79 | filter.Init() 80 | testNumber = 0 81 | for i := uint64(2); i <= CounterWindowSize+1; i++ { 82 | T(i, true) 83 | } 84 | T(1, true) 85 | T(0, false) 86 | 87 | t.Log("Bulk test 3") 88 | filter.Init() 89 | testNumber = 0 90 | for i := CounterWindowSize + 1; i > 0; i-- { 91 | T(i, true) 92 | } 93 | 94 | t.Log("Bulk test 4") 95 | filter.Init() 96 | testNumber = 0 97 | for i := CounterWindowSize + 2; i > 1; i-- { 98 | T(i, true) 99 | } 100 | T(0, false) 101 | 102 | t.Log("Bulk test 5") 103 | filter.Init() 104 | testNumber = 0 105 | for i := CounterWindowSize; i > 0; i-- { 106 | T(i, true) 107 | } 108 | T(CounterWindowSize+1, true) 109 | T(0, false) 110 | 111 | t.Log("Bulk test 6") 112 | filter.Init() 113 | testNumber = 0 114 | for i := CounterWindowSize; i > 0; i-- { 115 | T(i, true) 116 | } 117 | T(0, true) 118 | T(CounterWindowSize+1, true) 119 | } 120 | -------------------------------------------------------------------------------- /rwcancel/fdset.go: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: MIT 2 | * 3 | * Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved. 4 | */ 5 | 6 | package rwcancel 7 | 8 | import "golang.org/x/sys/unix" 9 | 10 | type fdSet struct { 11 | unix.FdSet 12 | } 13 | 14 | func (fdset *fdSet) set(i int) { 15 | bits := 32 << (^uint(0) >> 63) 16 | fdset.Bits[i/bits] |= 1 << uint(i%bits) 17 | } 18 | 19 | func (fdset *fdSet) check(i int) bool { 20 | bits := 32 << (^uint(0) >> 63) 21 | return (fdset.Bits[i/bits] & (1 << uint(i%bits))) != 0 22 | } 23 | -------------------------------------------------------------------------------- /rwcancel/rwcancel.go: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: MIT 2 | * 3 | * Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved. 4 | */ 5 | 6 | package rwcancel 7 | 8 | import ( 9 | "errors" 10 | "golang.org/x/sys/unix" 11 | "os" 12 | "syscall" 13 | ) 14 | 15 | func max(a, b int) int { 16 | if a > b { 17 | return a 18 | } 19 | return b 20 | } 21 | 22 | type RWCancel struct { 23 | fd int 24 | closingReader *os.File 25 | closingWriter *os.File 26 | } 27 | 28 | func NewRWCancel(fd int) (*RWCancel, error) { 29 | err := unix.SetNonblock(fd, true) 30 | if err != nil { 31 | return nil, err 32 | } 33 | rwcancel := RWCancel{fd: fd} 34 | 35 | rwcancel.closingReader, rwcancel.closingWriter, err = os.Pipe() 36 | if err != nil { 37 | return nil, err 38 | } 39 | 40 | return &rwcancel, nil 41 | } 42 | 43 | func RetryAfterError(err error) bool { 44 | if pe, ok := err.(*os.PathError); ok { 45 | err = pe.Err 46 | } 47 | if errno, ok := err.(syscall.Errno); ok { 48 | switch errno { 49 | case syscall.EAGAIN, syscall.EINTR: 50 | return true 51 | } 52 | 53 | } 54 | return false 55 | } 56 | 57 | func (rw *RWCancel) ReadyRead() bool { 58 | closeFd := int(rw.closingReader.Fd()) 59 | fdset := fdSet{} 60 | fdset.set(rw.fd) 61 | fdset.set(closeFd) 62 | err := unixSelect(max(rw.fd, closeFd)+1, &fdset.FdSet, nil, nil, nil) 63 | if err != nil { 64 | return false 65 | } 66 | if fdset.check(closeFd) { 67 | return false 68 | } 69 | return fdset.check(rw.fd) 70 | } 71 | 72 | func (rw *RWCancel) ReadyWrite() bool { 73 | closeFd := int(rw.closingReader.Fd()) 74 | fdset := fdSet{} 75 | fdset.set(rw.fd) 76 | fdset.set(closeFd) 77 | err := unixSelect(max(rw.fd, closeFd)+1, nil, &fdset.FdSet, nil, nil) 78 | if err != nil { 79 | return false 80 | } 81 | if fdset.check(closeFd) { 82 | return false 83 | } 84 | return fdset.check(rw.fd) 85 | } 86 | 87 | func (rw *RWCancel) Read(p []byte) (n int, err error) { 88 | for { 89 | n, err := unix.Read(rw.fd, p) 90 | if err == nil || !RetryAfterError(err) { 91 | return n, err 92 | } 93 | if !rw.ReadyRead() { 94 | return 0, errors.New("fd closed") 95 | } 96 | } 97 | } 98 | 99 | func (rw *RWCancel) Write(p []byte) (n int, err error) { 100 | for { 101 | n, err := unix.Write(rw.fd, p) 102 | if err == nil || !RetryAfterError(err) { 103 | return n, err 104 | } 105 | if !rw.ReadyWrite() { 106 | return 0, errors.New("fd closed") 107 | } 108 | } 109 | } 110 | 111 | func (rw *RWCancel) Cancel() (err error) { 112 | _, err = rw.closingWriter.Write([]byte{0}) 113 | return 114 | } 115 | -------------------------------------------------------------------------------- /rwcancel/select_default.go: -------------------------------------------------------------------------------- 1 | // +build !linux 2 | 3 | /* SPDX-License-Identifier: MIT 4 | * 5 | * Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved. 6 | */ 7 | 8 | package rwcancel 9 | 10 | import "golang.org/x/sys/unix" 11 | 12 | func unixSelect(nfd int, r *unix.FdSet, w *unix.FdSet, e *unix.FdSet, timeout *unix.Timeval) error { 13 | return unix.Select(nfd, r, w, e, timeout) 14 | } 15 | -------------------------------------------------------------------------------- /rwcancel/select_linux.go: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: MIT 2 | * 3 | * Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved. 4 | */ 5 | 6 | package rwcancel 7 | 8 | import "golang.org/x/sys/unix" 9 | 10 | func unixSelect(nfd int, r *unix.FdSet, w *unix.FdSet, e *unix.FdSet, timeout *unix.Timeval) (err error) { 11 | _, err = unix.Select(nfd, r, w, e, timeout) 12 | return 13 | } 14 | -------------------------------------------------------------------------------- /tai64n/tai64n.go: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: MIT 2 | * 3 | * Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved. 4 | */ 5 | 6 | package tai64n 7 | 8 | import ( 9 | "bytes" 10 | "encoding/binary" 11 | "time" 12 | ) 13 | 14 | const TimestampSize = 12 15 | const base = uint64(0x400000000000000a) 16 | const whitenerMask = uint32(0x1000000 - 1) 17 | 18 | type Timestamp [TimestampSize]byte 19 | 20 | func Now() Timestamp { 21 | var tai64n Timestamp 22 | now := time.Now() 23 | secs := base + uint64(now.Unix()) 24 | nano := uint32(now.Nanosecond()) &^ whitenerMask 25 | binary.BigEndian.PutUint64(tai64n[:], secs) 26 | binary.BigEndian.PutUint32(tai64n[8:], nano) 27 | return tai64n 28 | } 29 | 30 | func (t1 Timestamp) After(t2 Timestamp) bool { 31 | return bytes.Compare(t1[:], t2[:]) > 0 32 | } 33 | -------------------------------------------------------------------------------- /tai64n/tai64n_test.go: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: MIT 2 | * 3 | * Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved. 4 | */ 5 | 6 | package tai64n 7 | 8 | import ( 9 | "testing" 10 | "time" 11 | ) 12 | 13 | /* Testing the essential property of the timestamp 14 | * as used by WireGuard. 15 | */ 16 | func TestMonotonic(t *testing.T) { 17 | old := Now() 18 | for i := 0; i < 10000; i++ { 19 | time.Sleep(time.Nanosecond) 20 | next := Now() 21 | if !next.After(old) { 22 | t.Error("TAI64N, not monotonically increasing on nano-second scale") 23 | } 24 | old = next 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /tun/asm_solaris_amd64.s: -------------------------------------------------------------------------------- 1 | // Copyright 2014 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // +build !gccgo 6 | 7 | #include "textflag.h" 8 | 9 | // 10 | // System calls for amd64, Solaris are implemented in runtime/syscall_solaris.go 11 | // 12 | 13 | TEXT ·sysvicall6(SB),NOSPLIT,$0-88 14 | JMP syscall·sysvicall6(SB) 15 | 16 | TEXT ·rawSysvicall6(SB),NOSPLIT,$0-88 17 | JMP syscall·rawSysvicall6(SB) 18 | -------------------------------------------------------------------------------- /tun/helper_test.go: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: MIT 2 | * 3 | * Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved. 4 | */ 5 | 6 | package tun 7 | 8 | import ( 9 | "bytes" 10 | "errors" 11 | "golang.zx2c4.com/wireguard/tun" 12 | "os" 13 | "testing" 14 | ) 15 | 16 | /* Helpers for writing unit tests 17 | */ 18 | 19 | type DummyTUN struct { 20 | name string 21 | mtu int 22 | packets chan []byte 23 | events chan tun.TUNEvent 24 | } 25 | 26 | func (tun *DummyTUN) File() *os.File { 27 | return nil 28 | } 29 | 30 | func (tun *DummyTUN) Name() (string, error) { 31 | return tun.name, nil 32 | } 33 | 34 | func (tun *DummyTUN) MTU() (int, error) { 35 | return tun.mtu, nil 36 | } 37 | 38 | func (tun *DummyTUN) Write(d []byte, offset int) (int, error) { 39 | tun.packets <- d[offset:] 40 | return len(d), nil 41 | } 42 | 43 | func (tun *DummyTUN) Close() error { 44 | close(tun.events) 45 | close(tun.packets) 46 | return nil 47 | } 48 | 49 | func (tun *DummyTUN) Events() chan tun.TUNEvent { 50 | return tun.events 51 | } 52 | 53 | func (tun *DummyTUN) Read(d []byte, offset int) (int, error) { 54 | t, ok := <-tun.packets 55 | if !ok { 56 | return 0, errors.New("device closed") 57 | } 58 | copy(d[offset:], t) 59 | return len(t), nil 60 | } 61 | 62 | func CreateDummyTUN(name string) (tun.TUNDevice, error) { 63 | var dummy DummyTUN 64 | dummy.mtu = 0 65 | dummy.packets = make(chan []byte, 100) 66 | dummy.events = make(chan tun.TUNEvent, 10) 67 | return &dummy, nil 68 | } 69 | 70 | func assertNil(t *testing.T, err error) { 71 | if err != nil { 72 | t.Fatal(err) 73 | } 74 | } 75 | 76 | func assertEqual(t *testing.T, a []byte, b []byte) { 77 | if bytes.Compare(a, b) != 0 { 78 | t.Fatal(a, "!=", b) 79 | } 80 | } 81 | 82 | func randDevice(t *testing.T) *Device { 83 | sk, err := newPrivateKey() 84 | if err != nil { 85 | t.Fatal(err) 86 | } 87 | tun, _ := CreateDummyTUN("dummy") 88 | logger := NewLogger(LogLevelError, "") 89 | device := NewDevice(tun, logger) 90 | device.SetPrivateKey(sk) 91 | return device 92 | } 93 | -------------------------------------------------------------------------------- /tun/tun.go: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: MIT 2 | * 3 | * Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved. 4 | */ 5 | 6 | package tun 7 | 8 | import ( 9 | "fmt" 10 | "os" 11 | ) 12 | 13 | type TUNEvent int 14 | 15 | const ( 16 | TUNEventUp = 1 << iota 17 | TUNEventDown 18 | TUNEventMTUUpdate 19 | ) 20 | 21 | type TUNDevice interface { 22 | File() *os.File // returns the file descriptor of the device 23 | Read([]byte, int) (int, error) // read a packet from the device (without any additional headers) 24 | Write([]byte, int) (int, error) // writes a packet to the device (without any additional headers) 25 | MTU() (int, error) // returns the MTU of the device 26 | Name() (string, error) // fetches and returns the current name 27 | Events() chan TUNEvent // returns a constant channel of events related to the device 28 | Close() error // stops the device and closes the event channel 29 | } 30 | 31 | func (tun *NativeTun) operateOnFd(fn func(fd uintptr)) { 32 | sysconn, err := tun.tunFile.SyscallConn() 33 | if err != nil { 34 | tun.errors <- fmt.Errorf("unable to find sysconn for tunfile: %s", err.Error()) 35 | return 36 | } 37 | err = sysconn.Control(fn) 38 | if err != nil { 39 | tun.errors <- fmt.Errorf("unable to control sysconn for tunfile: %s", err.Error()) 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /tun/tun_darwin.go: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: MIT 2 | * 3 | * Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved. 4 | */ 5 | 6 | package tun 7 | 8 | import ( 9 | "fmt" 10 | "golang.org/x/net/ipv6" 11 | "golang.org/x/sys/unix" 12 | "io/ioutil" 13 | "net" 14 | "os" 15 | "syscall" 16 | "unsafe" 17 | ) 18 | 19 | const utunControlName = "com.apple.net.utun_control" 20 | 21 | // _CTLIOCGINFO value derived from /usr/include/sys/{kern_control,ioccom}.h 22 | const _CTLIOCGINFO = (0x40000000 | 0x80000000) | ((100 & 0x1fff) << 16) | uint32(byte('N'))<<8 | 3 23 | 24 | // sockaddr_ctl specifeid in /usr/include/sys/kern_control.h 25 | type sockaddrCtl struct { 26 | scLen uint8 27 | scFamily uint8 28 | ssSysaddr uint16 29 | scID uint32 30 | scUnit uint32 31 | scReserved [5]uint32 32 | } 33 | 34 | type NativeTun struct { 35 | name string 36 | tunFile *os.File 37 | events chan TUNEvent 38 | errors chan error 39 | routeSocket int 40 | } 41 | 42 | var sockaddrCtlSize uintptr = 32 43 | 44 | func (tun *NativeTun) routineRouteListener(tunIfindex int) { 45 | var ( 46 | statusUp bool 47 | statusMTU int 48 | ) 49 | 50 | defer close(tun.events) 51 | 52 | data := make([]byte, os.Getpagesize()) 53 | for { 54 | retry: 55 | n, err := unix.Read(tun.routeSocket, data) 56 | if err != nil { 57 | if errno, ok := err.(syscall.Errno); ok && errno == syscall.EINTR { 58 | goto retry 59 | } 60 | tun.errors <- err 61 | return 62 | } 63 | 64 | if n < 14 { 65 | continue 66 | } 67 | 68 | if data[3 /* type */] != unix.RTM_IFINFO { 69 | continue 70 | } 71 | ifindex := int(*(*uint16)(unsafe.Pointer(&data[12 /* ifindex */]))) 72 | if ifindex != tunIfindex { 73 | continue 74 | } 75 | 76 | iface, err := net.InterfaceByIndex(ifindex) 77 | if err != nil { 78 | tun.errors <- err 79 | return 80 | } 81 | 82 | // Up / Down event 83 | up := (iface.Flags & net.FlagUp) != 0 84 | if up != statusUp && up { 85 | tun.events <- TUNEventUp 86 | } 87 | if up != statusUp && !up { 88 | tun.events <- TUNEventDown 89 | } 90 | statusUp = up 91 | 92 | // MTU changes 93 | if iface.MTU != statusMTU { 94 | tun.events <- TUNEventMTUUpdate 95 | } 96 | statusMTU = iface.MTU 97 | } 98 | } 99 | 100 | func CreateTUN(name string, mtu int) (TUNDevice, error) { 101 | ifIndex := -1 102 | if name != "utun" { 103 | _, err := fmt.Sscanf(name, "utun%d", &ifIndex) 104 | if err != nil || ifIndex < 0 { 105 | return nil, fmt.Errorf("Interface name must be utun[0-9]*") 106 | } 107 | } 108 | 109 | fd, err := unix.Socket(unix.AF_SYSTEM, unix.SOCK_DGRAM, 2) 110 | 111 | if err != nil { 112 | return nil, err 113 | } 114 | 115 | var ctlInfo = &struct { 116 | ctlID uint32 117 | ctlName [96]byte 118 | }{} 119 | 120 | copy(ctlInfo.ctlName[:], []byte(utunControlName)) 121 | 122 | _, _, errno := unix.Syscall( 123 | unix.SYS_IOCTL, 124 | uintptr(fd), 125 | uintptr(_CTLIOCGINFO), 126 | uintptr(unsafe.Pointer(ctlInfo)), 127 | ) 128 | 129 | if errno != 0 { 130 | return nil, fmt.Errorf("_CTLIOCGINFO: %v", errno) 131 | } 132 | 133 | sc := sockaddrCtl{ 134 | scLen: uint8(sockaddrCtlSize), 135 | scFamily: unix.AF_SYSTEM, 136 | ssSysaddr: 2, 137 | scID: ctlInfo.ctlID, 138 | scUnit: uint32(ifIndex) + 1, 139 | } 140 | 141 | scPointer := unsafe.Pointer(&sc) 142 | 143 | _, _, errno = unix.RawSyscall( 144 | unix.SYS_CONNECT, 145 | uintptr(fd), 146 | uintptr(scPointer), 147 | uintptr(sockaddrCtlSize), 148 | ) 149 | 150 | if errno != 0 { 151 | return nil, fmt.Errorf("SYS_CONNECT: %v", errno) 152 | } 153 | 154 | err = syscall.SetNonblock(fd, true) 155 | if err != nil { 156 | return nil, err 157 | } 158 | tun, err := CreateTUNFromFile(os.NewFile(uintptr(fd), ""), mtu) 159 | 160 | if err == nil && name == "utun" { 161 | fname := os.Getenv("WG_TUN_NAME_FILE") 162 | if fname != "" { 163 | ioutil.WriteFile(fname, []byte(tun.(*NativeTun).name+"\n"), 0400) 164 | } 165 | } 166 | 167 | return tun, err 168 | } 169 | 170 | func CreateTUNFromFile(file *os.File, mtu int) (TUNDevice, error) { 171 | tun := &NativeTun{ 172 | tunFile: file, 173 | events: make(chan TUNEvent, 10), 174 | errors: make(chan error, 5), 175 | } 176 | 177 | name, err := tun.Name() 178 | if err != nil { 179 | tun.tunFile.Close() 180 | return nil, err 181 | } 182 | 183 | tunIfindex, err := func() (int, error) { 184 | iface, err := net.InterfaceByName(name) 185 | if err != nil { 186 | return -1, err 187 | } 188 | return iface.Index, nil 189 | }() 190 | if err != nil { 191 | tun.tunFile.Close() 192 | return nil, err 193 | } 194 | 195 | tun.routeSocket, err = unix.Socket(unix.AF_ROUTE, unix.SOCK_RAW, unix.AF_UNSPEC) 196 | if err != nil { 197 | tun.tunFile.Close() 198 | return nil, err 199 | } 200 | 201 | go tun.routineRouteListener(tunIfindex) 202 | 203 | if mtu > 0 { 204 | err = tun.setMTU(mtu) 205 | if err != nil { 206 | tun.Close() 207 | return nil, err 208 | } 209 | } 210 | 211 | return tun, nil 212 | } 213 | 214 | func (tun *NativeTun) Name() (string, error) { 215 | var ifName struct { 216 | name [16]byte 217 | } 218 | ifNameSize := uintptr(16) 219 | 220 | var errno syscall.Errno 221 | tun.operateOnFd(func(fd uintptr) { 222 | _, _, errno = unix.Syscall6( 223 | unix.SYS_GETSOCKOPT, 224 | fd, 225 | 2, /* #define SYSPROTO_CONTROL 2 */ 226 | 2, /* #define UTUN_OPT_IFNAME 2 */ 227 | uintptr(unsafe.Pointer(&ifName)), 228 | uintptr(unsafe.Pointer(&ifNameSize)), 0) 229 | }) 230 | 231 | if errno != 0 { 232 | return "", fmt.Errorf("SYS_GETSOCKOPT: %v", errno) 233 | } 234 | 235 | tun.name = string(ifName.name[:ifNameSize-1]) 236 | return tun.name, nil 237 | } 238 | 239 | func (tun *NativeTun) File() *os.File { 240 | return tun.tunFile 241 | } 242 | 243 | func (tun *NativeTun) Events() chan TUNEvent { 244 | return tun.events 245 | } 246 | 247 | func (tun *NativeTun) Read(buff []byte, offset int) (int, error) { 248 | select { 249 | case err := <-tun.errors: 250 | return 0, err 251 | default: 252 | buff := buff[offset-4:] 253 | n, err := tun.tunFile.Read(buff[:]) 254 | if n < 4 { 255 | return 0, err 256 | } 257 | return n - 4, err 258 | } 259 | } 260 | 261 | func (tun *NativeTun) Write(buff []byte, offset int) (int, error) { 262 | 263 | // reserve space for header 264 | 265 | buff = buff[offset-4:] 266 | 267 | // add packet information header 268 | 269 | buff[0] = 0x00 270 | buff[1] = 0x00 271 | buff[2] = 0x00 272 | 273 | if buff[4]>>4 == ipv6.Version { 274 | buff[3] = unix.AF_INET6 275 | } else { 276 | buff[3] = unix.AF_INET 277 | } 278 | 279 | // write 280 | 281 | return tun.tunFile.Write(buff) 282 | } 283 | 284 | func (tun *NativeTun) Close() error { 285 | var err2 error 286 | err1 := tun.tunFile.Close() 287 | if tun.routeSocket != -1 { 288 | unix.Shutdown(tun.routeSocket, unix.SHUT_RDWR) 289 | err2 = unix.Close(tun.routeSocket) 290 | tun.routeSocket = -1 291 | } else if tun.events != nil { 292 | close(tun.events) 293 | } 294 | if err1 != nil { 295 | return err1 296 | } 297 | return err2 298 | } 299 | 300 | func (tun *NativeTun) setMTU(n int) error { 301 | 302 | // open datagram socket 303 | 304 | var fd int 305 | 306 | fd, err := unix.Socket( 307 | unix.AF_INET, 308 | unix.SOCK_DGRAM, 309 | 0, 310 | ) 311 | 312 | if err != nil { 313 | return err 314 | } 315 | 316 | defer unix.Close(fd) 317 | 318 | // do ioctl call 319 | 320 | var ifr [32]byte 321 | copy(ifr[:], tun.name) 322 | *(*uint32)(unsafe.Pointer(&ifr[unix.IFNAMSIZ])) = uint32(n) 323 | _, _, errno := unix.Syscall( 324 | unix.SYS_IOCTL, 325 | uintptr(fd), 326 | uintptr(unix.SIOCSIFMTU), 327 | uintptr(unsafe.Pointer(&ifr[0])), 328 | ) 329 | 330 | if errno != 0 { 331 | return fmt.Errorf("failed to set MTU on %s", tun.name) 332 | } 333 | 334 | return nil 335 | } 336 | 337 | func (tun *NativeTun) MTU() (int, error) { 338 | 339 | // open datagram socket 340 | 341 | fd, err := unix.Socket( 342 | unix.AF_INET, 343 | unix.SOCK_DGRAM, 344 | 0, 345 | ) 346 | 347 | if err != nil { 348 | return 0, err 349 | } 350 | 351 | defer unix.Close(fd) 352 | 353 | // do ioctl call 354 | 355 | var ifr [64]byte 356 | copy(ifr[:], tun.name) 357 | _, _, errno := unix.Syscall( 358 | unix.SYS_IOCTL, 359 | uintptr(fd), 360 | uintptr(unix.SIOCGIFMTU), 361 | uintptr(unsafe.Pointer(&ifr[0])), 362 | ) 363 | if errno != 0 { 364 | return 0, fmt.Errorf("failed to get MTU on %s", tun.name) 365 | } 366 | 367 | return int(*(*int32)(unsafe.Pointer(&ifr[16]))), nil 368 | } 369 | -------------------------------------------------------------------------------- /tun/tun_freebsd.go: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: MIT 2 | * 3 | * Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved. 4 | */ 5 | 6 | package tun 7 | 8 | import ( 9 | "bytes" 10 | "errors" 11 | "fmt" 12 | "golang.org/x/net/ipv6" 13 | "golang.org/x/sys/unix" 14 | "net" 15 | "os" 16 | "syscall" 17 | "unsafe" 18 | ) 19 | 20 | // _TUNSIFHEAD, value derived from sys/net/{if_tun,ioccom}.h 21 | // const _TUNSIFHEAD = ((0x80000000) | (((4) & ((1 << 13) - 1) ) << 16) | (uint32(byte('t')) << 8) | (96)) 22 | const _TUNSIFHEAD = 0x80047460 23 | const _TUNSIFMODE = 0x8004745e 24 | const _TUNSIFPID = 0x2000745f 25 | 26 | // Iface status string max len 27 | const _IFSTATMAX = 800 28 | 29 | const SIZEOF_UINTPTR = 4 << (^uintptr(0) >> 32 & 1) 30 | 31 | // structure for iface requests with a pointer 32 | type ifreq_ptr struct { 33 | Name [unix.IFNAMSIZ]byte 34 | Data uintptr 35 | Pad0 [24 - SIZEOF_UINTPTR]byte 36 | } 37 | 38 | // Structure for iface mtu get/set ioctls 39 | type ifreq_mtu struct { 40 | Name [unix.IFNAMSIZ]byte 41 | MTU uint32 42 | Pad0 [12]byte 43 | } 44 | 45 | // Structure for interface status request ioctl 46 | type ifstat struct { 47 | IfsName [unix.IFNAMSIZ]byte 48 | Ascii [_IFSTATMAX]byte 49 | } 50 | 51 | type NativeTun struct { 52 | name string 53 | tunFile *os.File 54 | events chan TUNEvent 55 | errors chan error 56 | routeSocket int 57 | } 58 | 59 | func (tun *NativeTun) routineRouteListener(tunIfindex int) { 60 | var ( 61 | statusUp bool 62 | statusMTU int 63 | ) 64 | 65 | defer close(tun.events) 66 | 67 | data := make([]byte, os.Getpagesize()) 68 | for { 69 | retry: 70 | n, err := unix.Read(tun.routeSocket, data) 71 | if err != nil { 72 | if errno, ok := err.(syscall.Errno); ok && errno == syscall.EINTR { 73 | goto retry 74 | } 75 | tun.errors <- err 76 | return 77 | } 78 | 79 | if n < 14 { 80 | continue 81 | } 82 | 83 | if data[3 /* type */] != unix.RTM_IFINFO { 84 | continue 85 | } 86 | ifindex := int(*(*uint16)(unsafe.Pointer(&data[12 /* ifindex */]))) 87 | if ifindex != tunIfindex { 88 | continue 89 | } 90 | 91 | iface, err := net.InterfaceByIndex(ifindex) 92 | if err != nil { 93 | tun.errors <- err 94 | return 95 | } 96 | 97 | // Up / Down event 98 | up := (iface.Flags & net.FlagUp) != 0 99 | if up != statusUp && up { 100 | tun.events <- TUNEventUp 101 | } 102 | if up != statusUp && !up { 103 | tun.events <- TUNEventDown 104 | } 105 | statusUp = up 106 | 107 | // MTU changes 108 | if iface.MTU != statusMTU { 109 | tun.events <- TUNEventMTUUpdate 110 | } 111 | statusMTU = iface.MTU 112 | } 113 | } 114 | 115 | func tunName(fd uintptr) (string, error) { 116 | //Terrible hack to make up for freebsd not having a TUNGIFNAME 117 | 118 | //First, make sure the tun pid matches this proc's pid 119 | _, _, errno := unix.Syscall( 120 | unix.SYS_IOCTL, 121 | uintptr(fd), 122 | uintptr(_TUNSIFPID), 123 | uintptr(0), 124 | ) 125 | 126 | if errno != 0 { 127 | return "", fmt.Errorf("failed to set tun device PID: %s", errno.Error()) 128 | } 129 | 130 | // Open iface control socket 131 | 132 | confd, err := unix.Socket( 133 | unix.AF_INET, 134 | unix.SOCK_DGRAM, 135 | 0, 136 | ) 137 | 138 | if err != nil { 139 | return "", err 140 | } 141 | 142 | defer unix.Close(confd) 143 | 144 | procPid := os.Getpid() 145 | 146 | //Try to find interface with matching PID 147 | for i := 1; ; i++ { 148 | iface, _ := net.InterfaceByIndex(i) 149 | if err != nil || iface == nil { 150 | break 151 | } 152 | 153 | // Structs for getting data in and out of SIOCGIFSTATUS ioctl 154 | var ifstatus ifstat 155 | copy(ifstatus.IfsName[:], iface.Name) 156 | 157 | // Make the syscall to get the status string 158 | _, _, errno := unix.Syscall( 159 | unix.SYS_IOCTL, 160 | uintptr(confd), 161 | uintptr(unix.SIOCGIFSTATUS), 162 | uintptr(unsafe.Pointer(&ifstatus)), 163 | ) 164 | 165 | if errno != 0 { 166 | continue 167 | } 168 | 169 | nullStr := ifstatus.Ascii[:] 170 | i := bytes.IndexByte(nullStr, 0) 171 | if i < 1 { 172 | continue 173 | } 174 | statStr := string(nullStr[:i]) 175 | var pidNum int = 0 176 | 177 | // Finally get the owning PID 178 | // Format string taken from sys/net/if_tun.c 179 | _, err := fmt.Sscanf(statStr, "\tOpened by PID %d\n", &pidNum) 180 | if err != nil { 181 | continue 182 | } 183 | 184 | if pidNum == procPid { 185 | return iface.Name, nil 186 | } 187 | } 188 | 189 | return "", nil 190 | } 191 | 192 | // Destroy a named system interface 193 | func tunDestroy(name string) error { 194 | // open control socket 195 | var fd int 196 | 197 | fd, err := unix.Socket( 198 | unix.AF_INET, 199 | unix.SOCK_DGRAM, 200 | 0, 201 | ) 202 | 203 | if err != nil { 204 | return err 205 | } 206 | 207 | defer unix.Close(fd) 208 | 209 | // do ioctl call 210 | 211 | var ifr [32]byte 212 | copy(ifr[:], name) 213 | _, _, errno := unix.Syscall( 214 | unix.SYS_IOCTL, 215 | uintptr(fd), 216 | uintptr(unix.SIOCIFDESTROY), 217 | uintptr(unsafe.Pointer(&ifr[0])), 218 | ) 219 | 220 | if errno != 0 { 221 | return fmt.Errorf("failed to destroy interface %s: %s", name, errno.Error()) 222 | } 223 | 224 | return nil 225 | } 226 | 227 | func CreateTUN(name string, mtu int) (TUNDevice, error) { 228 | if len(name) > unix.IFNAMSIZ-1 { 229 | return nil, errors.New("interface name too long") 230 | } 231 | 232 | // See if interface already exists 233 | iface, _ := net.InterfaceByName(name) 234 | if iface != nil { 235 | return nil, fmt.Errorf("interface %s already exists", name) 236 | } 237 | 238 | tunFile, err := os.OpenFile("/dev/tun", unix.O_RDWR, 0) 239 | if err != nil { 240 | return nil, err 241 | } 242 | 243 | tun := NativeTun{tunFile: tunFile} 244 | var assignedName string 245 | tun.operateOnFd(func(fd uintptr) { 246 | assignedName, err = tunName(fd) 247 | }) 248 | if err != nil { 249 | tunFile.Close() 250 | return nil, err 251 | } 252 | 253 | // Enable ifhead mode, otherwise tun will complain if it gets a non-AF_INET packet 254 | ifheadmode := 1 255 | var errno syscall.Errno 256 | tun.operateOnFd(func(fd uintptr) { 257 | _, _, errno = unix.Syscall( 258 | unix.SYS_IOCTL, 259 | fd, 260 | uintptr(_TUNSIFHEAD), 261 | uintptr(unsafe.Pointer(&ifheadmode)), 262 | ) 263 | }) 264 | 265 | if errno != 0 { 266 | return nil, fmt.Errorf("error %s", errno.Error()) 267 | } 268 | 269 | // Rename tun interface 270 | 271 | // Open control socket 272 | confd, err := unix.Socket( 273 | unix.AF_INET, 274 | unix.SOCK_DGRAM, 275 | 0, 276 | ) 277 | 278 | if err != nil { 279 | return nil, err 280 | } 281 | 282 | defer unix.Close(confd) 283 | 284 | // set up struct for iface rename 285 | var newnp [unix.IFNAMSIZ]byte 286 | copy(newnp[:], name) 287 | 288 | var ifr ifreq_ptr 289 | copy(ifr.Name[:], assignedName) 290 | ifr.Data = uintptr(unsafe.Pointer(&newnp[0])) 291 | 292 | //do actual ioctl to rename iface 293 | _, _, errno = unix.Syscall( 294 | unix.SYS_IOCTL, 295 | uintptr(confd), 296 | uintptr(unix.SIOCSIFNAME), 297 | uintptr(unsafe.Pointer(&ifr)), 298 | ) 299 | if errno != 0 { 300 | tunFile.Close() 301 | tunDestroy(name) 302 | return nil, fmt.Errorf("failed to rename %s to %s: %s", assignedName, name, errno.Error()) 303 | } 304 | 305 | return CreateTUNFromFile(tunFile, mtu) 306 | } 307 | 308 | func CreateTUNFromFile(file *os.File, mtu int) (TUNDevice, error) { 309 | 310 | tun := &NativeTun{ 311 | tunFile: file, 312 | events: make(chan TUNEvent, 10), 313 | errors: make(chan error, 1), 314 | } 315 | 316 | name, err := tun.Name() 317 | if err != nil { 318 | tun.tunFile.Close() 319 | return nil, err 320 | } 321 | 322 | tunIfindex, err := func() (int, error) { 323 | iface, err := net.InterfaceByName(name) 324 | if err != nil { 325 | return -1, err 326 | } 327 | return iface.Index, nil 328 | }() 329 | if err != nil { 330 | tun.tunFile.Close() 331 | return nil, err 332 | } 333 | 334 | tun.routeSocket, err = unix.Socket(unix.AF_ROUTE, unix.SOCK_RAW, unix.AF_UNSPEC) 335 | if err != nil { 336 | tun.tunFile.Close() 337 | return nil, err 338 | } 339 | 340 | go tun.routineRouteListener(tunIfindex) 341 | 342 | err = tun.setMTU(mtu) 343 | if err != nil { 344 | tun.Close() 345 | return nil, err 346 | } 347 | 348 | return tun, nil 349 | } 350 | 351 | func (tun *NativeTun) Name() (string, error) { 352 | var name string 353 | var err error 354 | tun.operateOnFd(func(fd uintptr) { 355 | name, err = tunName(fd) 356 | }) 357 | if err != nil { 358 | return "", err 359 | } 360 | tun.name = name 361 | return name, nil 362 | } 363 | 364 | func (tun *NativeTun) File() *os.File { 365 | return tun.tunFile 366 | } 367 | 368 | func (tun *NativeTun) Events() chan TUNEvent { 369 | return tun.events 370 | } 371 | 372 | func (tun *NativeTun) Read(buff []byte, offset int) (int, error) { 373 | select { 374 | case err := <-tun.errors: 375 | return 0, err 376 | default: 377 | buff := buff[offset-4:] 378 | n, err := tun.tunFile.Read(buff[:]) 379 | if n < 4 { 380 | return 0, err 381 | } 382 | return n - 4, err 383 | } 384 | } 385 | 386 | func (tun *NativeTun) Write(buff []byte, offset int) (int, error) { 387 | 388 | // reserve space for header 389 | 390 | buff = buff[offset-4:] 391 | 392 | // add packet information header 393 | 394 | buff[0] = 0x00 395 | buff[1] = 0x00 396 | buff[2] = 0x00 397 | 398 | if buff[4]>>4 == ipv6.Version { 399 | buff[3] = unix.AF_INET6 400 | } else { 401 | buff[3] = unix.AF_INET 402 | } 403 | 404 | // write 405 | 406 | return tun.tunFile.Write(buff) 407 | } 408 | 409 | func (tun *NativeTun) Close() error { 410 | var err3 error 411 | err1 := tun.tunFile.Close() 412 | err2 := tunDestroy(tun.name) 413 | if tun.routeSocket != -1 { 414 | unix.Shutdown(tun.routeSocket, unix.SHUT_RDWR) 415 | err3 = unix.Close(tun.routeSocket) 416 | tun.routeSocket = -1 417 | } else if tun.events != nil { 418 | close(tun.events) 419 | } 420 | if err1 != nil { 421 | return err1 422 | } 423 | if err2 != nil { 424 | return err2 425 | } 426 | return err3 427 | } 428 | 429 | func (tun *NativeTun) setMTU(n int) error { 430 | // open datagram socket 431 | 432 | var fd int 433 | 434 | fd, err := unix.Socket( 435 | unix.AF_INET, 436 | unix.SOCK_DGRAM, 437 | 0, 438 | ) 439 | 440 | if err != nil { 441 | return err 442 | } 443 | 444 | defer unix.Close(fd) 445 | 446 | // do ioctl call 447 | 448 | var ifr ifreq_mtu 449 | copy(ifr.Name[:], tun.name) 450 | ifr.MTU = uint32(n) 451 | 452 | _, _, errno := unix.Syscall( 453 | unix.SYS_IOCTL, 454 | uintptr(fd), 455 | uintptr(unix.SIOCSIFMTU), 456 | uintptr(unsafe.Pointer(&ifr)), 457 | ) 458 | 459 | if errno != 0 { 460 | return fmt.Errorf("failed to set MTU on %s", tun.name) 461 | } 462 | 463 | return nil 464 | } 465 | 466 | func (tun *NativeTun) MTU() (int, error) { 467 | // open datagram socket 468 | 469 | fd, err := unix.Socket( 470 | unix.AF_INET, 471 | unix.SOCK_DGRAM, 472 | 0, 473 | ) 474 | 475 | if err != nil { 476 | return 0, err 477 | } 478 | 479 | defer unix.Close(fd) 480 | 481 | // do ioctl call 482 | var ifr ifreq_mtu 483 | copy(ifr.Name[:], tun.name) 484 | 485 | _, _, errno := unix.Syscall( 486 | unix.SYS_IOCTL, 487 | uintptr(fd), 488 | uintptr(unix.SIOCGIFMTU), 489 | uintptr(unsafe.Pointer(&ifr)), 490 | ) 491 | if errno != 0 { 492 | return 0, fmt.Errorf("failed to get MTU on %s", tun.name) 493 | } 494 | 495 | return int(*(*int32)(unsafe.Pointer(&ifr.MTU))), nil 496 | } 497 | -------------------------------------------------------------------------------- /tun/tun_linux.go: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: MIT 2 | * 3 | * Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved. 4 | */ 5 | 6 | package tun 7 | 8 | /* Implementation of the TUN device interface for linux 9 | */ 10 | 11 | import ( 12 | "bytes" 13 | "errors" 14 | "fmt" 15 | "golang.org/x/net/ipv6" 16 | "golang.org/x/sys/unix" 17 | "golang.zx2c4.com/wireguard/rwcancel" 18 | "net" 19 | "os" 20 | "sync" 21 | "syscall" 22 | "time" 23 | "unsafe" 24 | ) 25 | 26 | const ( 27 | cloneDevicePath = "/dev/net/tun" 28 | ifReqSize = unix.IFNAMSIZ + 64 29 | ) 30 | 31 | type NativeTun struct { 32 | tunFile *os.File 33 | index int32 // if index 34 | name string // name of interface 35 | errors chan error // async error handling 36 | events chan TUNEvent // device related events 37 | nopi bool // the device was pased IFF_NO_PI 38 | netlinkSock int 39 | netlinkCancel *rwcancel.RWCancel 40 | hackListenerClosed sync.Mutex 41 | statusListenersShutdown chan struct{} 42 | } 43 | 44 | func (tun *NativeTun) File() *os.File { 45 | return tun.tunFile 46 | } 47 | 48 | func (tun *NativeTun) routineHackListener() { 49 | defer tun.hackListenerClosed.Unlock() 50 | /* This is needed for the detection to work across network namespaces 51 | * If you are reading this and know a better method, please get in touch. 52 | */ 53 | for { 54 | sysconn, err := tun.tunFile.SyscallConn() 55 | if err != nil { 56 | return 57 | } 58 | err2 := sysconn.Control(func(fd uintptr) { 59 | _, err = unix.Write(int(fd), nil) 60 | }) 61 | if err2 != nil { 62 | return 63 | } 64 | switch err { 65 | case unix.EINVAL: 66 | tun.events <- TUNEventUp 67 | case unix.EIO: 68 | tun.events <- TUNEventDown 69 | default: 70 | return 71 | } 72 | select { 73 | case <-time.After(time.Second): 74 | case <-tun.statusListenersShutdown: 75 | return 76 | } 77 | } 78 | } 79 | 80 | func createNetlinkSocket() (int, error) { 81 | sock, err := unix.Socket(unix.AF_NETLINK, unix.SOCK_RAW, unix.NETLINK_ROUTE) 82 | if err != nil { 83 | return -1, err 84 | } 85 | saddr := &unix.SockaddrNetlink{ 86 | Family: unix.AF_NETLINK, 87 | Groups: uint32((1 << (unix.RTNLGRP_LINK - 1)) | (1 << (unix.RTNLGRP_IPV4_IFADDR - 1)) | (1 << (unix.RTNLGRP_IPV6_IFADDR - 1))), 88 | } 89 | err = unix.Bind(sock, saddr) 90 | if err != nil { 91 | return -1, err 92 | } 93 | return sock, nil 94 | } 95 | 96 | func (tun *NativeTun) routineNetlinkListener() { 97 | defer func() { 98 | unix.Close(tun.netlinkSock) 99 | tun.hackListenerClosed.Lock() 100 | close(tun.events) 101 | }() 102 | 103 | for msg := make([]byte, 1<<16); ; { 104 | 105 | var err error 106 | var msgn int 107 | for { 108 | msgn, _, _, _, err = unix.Recvmsg(tun.netlinkSock, msg[:], nil, 0) 109 | if err == nil || !rwcancel.RetryAfterError(err) { 110 | break 111 | } 112 | if !tun.netlinkCancel.ReadyRead() { 113 | tun.errors <- fmt.Errorf("netlink socket closed: %s", err.Error()) 114 | return 115 | } 116 | } 117 | if err != nil { 118 | tun.errors <- fmt.Errorf("failed to receive netlink message: %s", err.Error()) 119 | return 120 | } 121 | 122 | select { 123 | case <-tun.statusListenersShutdown: 124 | return 125 | default: 126 | } 127 | 128 | for remain := msg[:msgn]; len(remain) >= unix.SizeofNlMsghdr; { 129 | 130 | hdr := *(*unix.NlMsghdr)(unsafe.Pointer(&remain[0])) 131 | 132 | if int(hdr.Len) > len(remain) { 133 | break 134 | } 135 | 136 | switch hdr.Type { 137 | case unix.NLMSG_DONE: 138 | remain = []byte{} 139 | 140 | case unix.RTM_NEWLINK: 141 | info := *(*unix.IfInfomsg)(unsafe.Pointer(&remain[unix.SizeofNlMsghdr])) 142 | remain = remain[hdr.Len:] 143 | 144 | if info.Index != tun.index { 145 | // not our interface 146 | continue 147 | } 148 | 149 | if info.Flags&unix.IFF_RUNNING != 0 { 150 | tun.events <- TUNEventUp 151 | } 152 | 153 | if info.Flags&unix.IFF_RUNNING == 0 { 154 | tun.events <- TUNEventDown 155 | } 156 | 157 | tun.events <- TUNEventMTUUpdate 158 | 159 | default: 160 | remain = remain[hdr.Len:] 161 | } 162 | } 163 | } 164 | } 165 | 166 | func (tun *NativeTun) isUp() (bool, error) { 167 | inter, err := net.InterfaceByName(tun.name) 168 | return inter.Flags&net.FlagUp != 0, err 169 | } 170 | 171 | func getIFIndex(name string) (int32, error) { 172 | fd, err := unix.Socket( 173 | unix.AF_INET, 174 | unix.SOCK_DGRAM, 175 | 0, 176 | ) 177 | if err != nil { 178 | return 0, err 179 | } 180 | 181 | defer unix.Close(fd) 182 | 183 | var ifr [ifReqSize]byte 184 | copy(ifr[:], name) 185 | _, _, errno := unix.Syscall( 186 | unix.SYS_IOCTL, 187 | uintptr(fd), 188 | uintptr(unix.SIOCGIFINDEX), 189 | uintptr(unsafe.Pointer(&ifr[0])), 190 | ) 191 | 192 | if errno != 0 { 193 | return 0, errno 194 | } 195 | 196 | return *(*int32)(unsafe.Pointer(&ifr[unix.IFNAMSIZ])), nil 197 | } 198 | 199 | func (tun *NativeTun) setMTU(n int) error { 200 | // open datagram socket 201 | fd, err := unix.Socket( 202 | unix.AF_INET, 203 | unix.SOCK_DGRAM, 204 | 0, 205 | ) 206 | 207 | if err != nil { 208 | return err 209 | } 210 | 211 | defer unix.Close(fd) 212 | 213 | // do ioctl call 214 | 215 | var ifr [ifReqSize]byte 216 | copy(ifr[:], tun.name) 217 | *(*uint32)(unsafe.Pointer(&ifr[unix.IFNAMSIZ])) = uint32(n) 218 | _, _, errno := unix.Syscall( 219 | unix.SYS_IOCTL, 220 | uintptr(fd), 221 | uintptr(unix.SIOCSIFMTU), 222 | uintptr(unsafe.Pointer(&ifr[0])), 223 | ) 224 | 225 | if errno != 0 { 226 | return errors.New("failed to set MTU of TUN device") 227 | } 228 | 229 | return nil 230 | } 231 | 232 | func (tun *NativeTun) MTU() (int, error) { 233 | // open datagram socket 234 | fd, err := unix.Socket( 235 | unix.AF_INET, 236 | unix.SOCK_DGRAM, 237 | 0, 238 | ) 239 | 240 | if err != nil { 241 | return 0, err 242 | } 243 | 244 | defer unix.Close(fd) 245 | 246 | // do ioctl call 247 | 248 | var ifr [ifReqSize]byte 249 | copy(ifr[:], tun.name) 250 | _, _, errno := unix.Syscall( 251 | unix.SYS_IOCTL, 252 | uintptr(fd), 253 | uintptr(unix.SIOCGIFMTU), 254 | uintptr(unsafe.Pointer(&ifr[0])), 255 | ) 256 | if errno != 0 { 257 | return 0, errors.New("failed to get MTU of TUN device: " + errno.Error()) 258 | } 259 | 260 | return int(*(*int32)(unsafe.Pointer(&ifr[unix.IFNAMSIZ]))), nil 261 | } 262 | 263 | func (tun *NativeTun) Name() (string, error) { 264 | sysconn, err := tun.tunFile.SyscallConn() 265 | if err != nil { 266 | return "", err 267 | } 268 | var ifr [ifReqSize]byte 269 | var errno syscall.Errno 270 | err = sysconn.Control(func(fd uintptr) { 271 | _, _, errno = unix.Syscall( 272 | unix.SYS_IOCTL, 273 | fd, 274 | uintptr(unix.TUNGETIFF), 275 | uintptr(unsafe.Pointer(&ifr[0])), 276 | ) 277 | }) 278 | if err != nil { 279 | return "", errors.New("failed to get name of TUN device: " + err.Error()) 280 | } 281 | if errno != 0 { 282 | return "", errors.New("failed to get name of TUN device: " + errno.Error()) 283 | } 284 | nullStr := ifr[:] 285 | i := bytes.IndexByte(nullStr, 0) 286 | if i != -1 { 287 | nullStr = nullStr[:i] 288 | } 289 | tun.name = string(nullStr) 290 | return tun.name, nil 291 | } 292 | 293 | func (tun *NativeTun) Write(buff []byte, offset int) (int, error) { 294 | 295 | if tun.nopi { 296 | buff = buff[offset:] 297 | } else { 298 | // reserve space for header 299 | 300 | buff = buff[offset-4:] 301 | 302 | // add packet information header 303 | 304 | buff[0] = 0x00 305 | buff[1] = 0x00 306 | 307 | if buff[4]>>4 == ipv6.Version { 308 | buff[2] = 0x86 309 | buff[3] = 0xdd 310 | } else { 311 | buff[2] = 0x08 312 | buff[3] = 0x00 313 | } 314 | } 315 | 316 | // write 317 | 318 | return tun.tunFile.Write(buff) 319 | } 320 | 321 | func (tun *NativeTun) Read(buff []byte, offset int) (int, error) { 322 | select { 323 | case err := <-tun.errors: 324 | return 0, err 325 | default: 326 | if tun.nopi { 327 | return tun.tunFile.Read(buff[offset:]) 328 | } else { 329 | buff := buff[offset-4:] 330 | n, err := tun.tunFile.Read(buff[:]) 331 | if n < 4 { 332 | return 0, err 333 | } 334 | return n - 4, err 335 | } 336 | } 337 | } 338 | 339 | func (tun *NativeTun) Events() chan TUNEvent { 340 | return tun.events 341 | } 342 | 343 | func (tun *NativeTun) Close() error { 344 | var err1 error 345 | if tun.statusListenersShutdown != nil { 346 | close(tun.statusListenersShutdown) 347 | if tun.netlinkCancel != nil { 348 | err1 = tun.netlinkCancel.Cancel() 349 | } 350 | } else if tun.events != nil { 351 | close(tun.events) 352 | } 353 | err2 := tun.tunFile.Close() 354 | 355 | if err1 != nil { 356 | return err1 357 | } 358 | return err2 359 | } 360 | 361 | func CreateTUN(name string, mtu int) (TUNDevice, error) { 362 | nfd, err := unix.Open(cloneDevicePath, os.O_RDWR, 0) 363 | if err != nil { 364 | return nil, err 365 | } 366 | 367 | var ifr [ifReqSize]byte 368 | var flags uint16 = unix.IFF_TUN // | unix.IFF_NO_PI (disabled for TUN status hack) 369 | nameBytes := []byte(name) 370 | if len(nameBytes) >= unix.IFNAMSIZ { 371 | return nil, errors.New("interface name too long") 372 | } 373 | copy(ifr[:], nameBytes) 374 | *(*uint16)(unsafe.Pointer(&ifr[unix.IFNAMSIZ])) = flags 375 | 376 | _, _, errno := unix.Syscall( 377 | unix.SYS_IOCTL, 378 | uintptr(nfd), 379 | uintptr(unix.TUNSETIFF), 380 | uintptr(unsafe.Pointer(&ifr[0])), 381 | ) 382 | if errno != 0 { 383 | return nil, errno 384 | } 385 | err = unix.SetNonblock(nfd, true) 386 | 387 | // Note that the above -- open,ioctl,nonblock -- must happen prior to handing it to netpoll as below this line. 388 | 389 | fd := os.NewFile(uintptr(nfd), cloneDevicePath) 390 | if err != nil { 391 | return nil, err 392 | } 393 | 394 | return CreateTUNFromFile(fd, mtu) 395 | } 396 | 397 | func CreateTUNFromFile(file *os.File, mtu int) (TUNDevice, error) { 398 | tun := &NativeTun{ 399 | tunFile: file, 400 | events: make(chan TUNEvent, 5), 401 | errors: make(chan error, 5), 402 | statusListenersShutdown: make(chan struct{}), 403 | nopi: false, 404 | } 405 | var err error 406 | 407 | _, err = tun.Name() 408 | if err != nil { 409 | return nil, err 410 | } 411 | 412 | // start event listener 413 | 414 | tun.index, err = getIFIndex(tun.name) 415 | if err != nil { 416 | return nil, err 417 | } 418 | 419 | tun.netlinkSock, err = createNetlinkSocket() 420 | if err != nil { 421 | return nil, err 422 | } 423 | tun.netlinkCancel, err = rwcancel.NewRWCancel(tun.netlinkSock) 424 | if err != nil { 425 | unix.Close(tun.netlinkSock) 426 | return nil, err 427 | } 428 | 429 | tun.hackListenerClosed.Lock() 430 | go tun.routineNetlinkListener() 431 | go tun.routineHackListener() // cross namespace 432 | 433 | err = tun.setMTU(mtu) 434 | if err != nil { 435 | unix.Close(tun.netlinkSock) 436 | return nil, err 437 | } 438 | 439 | return tun, nil 440 | } 441 | 442 | func CreateUnmonitoredTUNFromFD(fd int) (TUNDevice, string, error) { 443 | err := unix.SetNonblock(fd, true) 444 | if err != nil { 445 | return nil, "", err 446 | } 447 | file := os.NewFile(uintptr(fd), "/dev/tun") 448 | tun := &NativeTun{ 449 | tunFile: file, 450 | events: make(chan TUNEvent, 5), 451 | errors: make(chan error, 5), 452 | nopi: true, 453 | } 454 | name, err := tun.Name() 455 | if err != nil { 456 | return nil, "", err 457 | } 458 | return tun, name, nil 459 | } 460 | -------------------------------------------------------------------------------- /tun/tun_openbsd.go: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: MIT 2 | * 3 | * Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved. 4 | */ 5 | 6 | package tun 7 | 8 | import ( 9 | "fmt" 10 | "golang.org/x/net/ipv6" 11 | "golang.org/x/sys/unix" 12 | "io/ioutil" 13 | "net" 14 | "os" 15 | "syscall" 16 | "unsafe" 17 | ) 18 | 19 | // Structure for iface mtu get/set ioctls 20 | type ifreq_mtu struct { 21 | Name [unix.IFNAMSIZ]byte 22 | MTU uint32 23 | Pad0 [12]byte 24 | } 25 | 26 | const _TUNSIFMODE = 0x8004745d 27 | 28 | type NativeTun struct { 29 | name string 30 | tunFile *os.File 31 | events chan TUNEvent 32 | errors chan error 33 | routeSocket int 34 | } 35 | 36 | func (tun *NativeTun) routineRouteListener(tunIfindex int) { 37 | var ( 38 | statusUp bool 39 | statusMTU int 40 | ) 41 | 42 | defer close(tun.events) 43 | 44 | data := make([]byte, os.Getpagesize()) 45 | for { 46 | retry: 47 | n, err := unix.Read(tun.routeSocket, data) 48 | if err != nil { 49 | if errno, ok := err.(syscall.Errno); ok && errno == syscall.EINTR { 50 | goto retry 51 | } 52 | tun.errors <- err 53 | return 54 | } 55 | 56 | if n < 8 { 57 | continue 58 | } 59 | 60 | if data[3 /* type */] != unix.RTM_IFINFO { 61 | continue 62 | } 63 | ifindex := int(*(*uint16)(unsafe.Pointer(&data[6 /* ifindex */]))) 64 | if ifindex != tunIfindex { 65 | continue 66 | } 67 | 68 | iface, err := net.InterfaceByIndex(ifindex) 69 | if err != nil { 70 | tun.errors <- err 71 | return 72 | } 73 | 74 | // Up / Down event 75 | up := (iface.Flags & net.FlagUp) != 0 76 | if up != statusUp && up { 77 | tun.events <- TUNEventUp 78 | } 79 | if up != statusUp && !up { 80 | tun.events <- TUNEventDown 81 | } 82 | statusUp = up 83 | 84 | // MTU changes 85 | if iface.MTU != statusMTU { 86 | tun.events <- TUNEventMTUUpdate 87 | } 88 | statusMTU = iface.MTU 89 | } 90 | } 91 | 92 | func errorIsEBUSY(err error) bool { 93 | if pe, ok := err.(*os.PathError); ok { 94 | err = pe.Err 95 | } 96 | if errno, ok := err.(syscall.Errno); ok && errno == syscall.EBUSY { 97 | return true 98 | } 99 | return false 100 | } 101 | 102 | func CreateTUN(name string, mtu int) (TUNDevice, error) { 103 | ifIndex := -1 104 | if name != "tun" { 105 | _, err := fmt.Sscanf(name, "tun%d", &ifIndex) 106 | if err != nil || ifIndex < 0 { 107 | return nil, fmt.Errorf("Interface name must be tun[0-9]*") 108 | } 109 | } 110 | 111 | var tunfile *os.File 112 | var err error 113 | 114 | if ifIndex != -1 { 115 | tunfile, err = os.OpenFile(fmt.Sprintf("/dev/tun%d", ifIndex), unix.O_RDWR, 0) 116 | } else { 117 | for ifIndex = 0; ifIndex < 256; ifIndex += 1 { 118 | tunfile, err = os.OpenFile(fmt.Sprintf("/dev/tun%d", ifIndex), unix.O_RDWR, 0) 119 | if err == nil || !errorIsEBUSY(err) { 120 | break 121 | } 122 | } 123 | } 124 | 125 | if err != nil { 126 | return nil, err 127 | } 128 | 129 | tun, err := CreateTUNFromFile(tunfile, mtu) 130 | 131 | if err == nil && name == "tun" { 132 | fname := os.Getenv("WG_TUN_NAME_FILE") 133 | if fname != "" { 134 | ioutil.WriteFile(fname, []byte(tun.(*NativeTun).name+"\n"), 0400) 135 | } 136 | } 137 | 138 | return tun, err 139 | } 140 | 141 | func CreateTUNFromFile(file *os.File, mtu int) (TUNDevice, error) { 142 | 143 | tun := &NativeTun{ 144 | tunFile: file, 145 | events: make(chan TUNEvent, 10), 146 | errors: make(chan error, 1), 147 | } 148 | 149 | name, err := tun.Name() 150 | if err != nil { 151 | tun.tunFile.Close() 152 | return nil, err 153 | } 154 | 155 | tunIfindex, err := func() (int, error) { 156 | iface, err := net.InterfaceByName(name) 157 | if err != nil { 158 | return -1, err 159 | } 160 | return iface.Index, nil 161 | }() 162 | if err != nil { 163 | tun.tunFile.Close() 164 | return nil, err 165 | } 166 | 167 | tun.routeSocket, err = unix.Socket(unix.AF_ROUTE, unix.SOCK_RAW, unix.AF_UNSPEC) 168 | if err != nil { 169 | tun.tunFile.Close() 170 | return nil, err 171 | } 172 | 173 | go tun.routineRouteListener(tunIfindex) 174 | 175 | err = tun.setMTU(mtu) 176 | if err != nil { 177 | tun.Close() 178 | return nil, err 179 | } 180 | 181 | return tun, nil 182 | } 183 | 184 | func (tun *NativeTun) Name() (string, error) { 185 | gostat, err := tun.tunFile.Stat() 186 | if err != nil { 187 | tun.name = "" 188 | return "", err 189 | } 190 | stat := gostat.Sys().(*syscall.Stat_t) 191 | tun.name = fmt.Sprintf("tun%d", stat.Rdev%256) 192 | return tun.name, nil 193 | } 194 | 195 | func (tun *NativeTun) File() *os.File { 196 | return tun.tunFile 197 | } 198 | 199 | func (tun *NativeTun) Events() chan TUNEvent { 200 | return tun.events 201 | } 202 | 203 | func (tun *NativeTun) Read(buff []byte, offset int) (int, error) { 204 | select { 205 | case err := <-tun.errors: 206 | return 0, err 207 | default: 208 | buff := buff[offset-4:] 209 | n, err := tun.tunFile.Read(buff[:]) 210 | if n < 4 { 211 | return 0, err 212 | } 213 | return n - 4, err 214 | } 215 | } 216 | 217 | func (tun *NativeTun) Write(buff []byte, offset int) (int, error) { 218 | 219 | // reserve space for header 220 | 221 | buff = buff[offset-4:] 222 | 223 | // add packet information header 224 | 225 | buff[0] = 0x00 226 | buff[1] = 0x00 227 | buff[2] = 0x00 228 | 229 | if buff[4]>>4 == ipv6.Version { 230 | buff[3] = unix.AF_INET6 231 | } else { 232 | buff[3] = unix.AF_INET 233 | } 234 | 235 | // write 236 | 237 | return tun.tunFile.Write(buff) 238 | } 239 | 240 | func (tun *NativeTun) Close() error { 241 | var err2 error 242 | err1 := tun.tunFile.Close() 243 | if tun.routeSocket != -1 { 244 | unix.Shutdown(tun.routeSocket, unix.SHUT_RDWR) 245 | err2 = unix.Close(tun.routeSocket) 246 | tun.routeSocket = -1 247 | } else if tun.events != nil { 248 | close(tun.events) 249 | } 250 | if err1 != nil { 251 | return err1 252 | } 253 | return err2 254 | } 255 | 256 | func (tun *NativeTun) setMTU(n int) error { 257 | // open datagram socket 258 | 259 | var fd int 260 | 261 | fd, err := unix.Socket( 262 | unix.AF_INET, 263 | unix.SOCK_DGRAM, 264 | 0, 265 | ) 266 | 267 | if err != nil { 268 | return err 269 | } 270 | 271 | defer unix.Close(fd) 272 | 273 | // do ioctl call 274 | 275 | var ifr ifreq_mtu 276 | copy(ifr.Name[:], tun.name) 277 | ifr.MTU = uint32(n) 278 | 279 | _, _, errno := unix.Syscall( 280 | unix.SYS_IOCTL, 281 | uintptr(fd), 282 | uintptr(unix.SIOCSIFMTU), 283 | uintptr(unsafe.Pointer(&ifr)), 284 | ) 285 | 286 | if errno != 0 { 287 | return fmt.Errorf("failed to set MTU on %s", tun.name) 288 | } 289 | 290 | return nil 291 | } 292 | 293 | func (tun *NativeTun) MTU() (int, error) { 294 | // open datagram socket 295 | 296 | fd, err := unix.Socket( 297 | unix.AF_INET, 298 | unix.SOCK_DGRAM, 299 | 0, 300 | ) 301 | 302 | if err != nil { 303 | return 0, err 304 | } 305 | 306 | defer unix.Close(fd) 307 | 308 | // do ioctl call 309 | var ifr ifreq_mtu 310 | copy(ifr.Name[:], tun.name) 311 | 312 | _, _, errno := unix.Syscall( 313 | unix.SYS_IOCTL, 314 | uintptr(fd), 315 | uintptr(unix.SIOCGIFMTU), 316 | uintptr(unsafe.Pointer(&ifr)), 317 | ) 318 | if errno != 0 { 319 | return 0, fmt.Errorf("failed to get MTU on %s", tun.name) 320 | } 321 | 322 | return int(*(*int32)(unsafe.Pointer(&ifr.MTU))), nil 323 | } 324 | -------------------------------------------------------------------------------- /tun/tun_windows.go: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: MIT 2 | * 3 | * Copyright (C) 2018-2019 WireGuard LLC. All Rights Reserved. 4 | */ 5 | 6 | package tun 7 | 8 | import ( 9 | "errors" 10 | "os" 11 | "sync" 12 | "unsafe" 13 | 14 | "golang.org/x/sys/windows" 15 | "golang.zx2c4.com/wireguard/tun/wintun" 16 | ) 17 | 18 | const ( 19 | packetExchangeMax uint32 = 256 // Number of packets that may be written at a time 20 | packetExchangeAlignment uint32 = 16 // Number of bytes packets are aligned to in exchange buffers 21 | packetSizeMax uint32 = 0xf000 - packetExchangeAlignment // Maximum packet size 22 | packetExchangeSize uint32 = 0x100000 // Exchange buffer size (defaults to 1MiB) 23 | ) 24 | 25 | type exchgBufRead struct { 26 | data [packetExchangeSize]byte 27 | offset uint32 28 | avail uint32 29 | } 30 | 31 | type exchgBufWrite struct { 32 | data [packetExchangeSize]byte 33 | offset uint32 34 | packetNum uint32 35 | } 36 | 37 | type NativeTun struct { 38 | wt *wintun.Wintun 39 | tunName string 40 | tunFile *os.File 41 | tunLock sync.Mutex 42 | rdBuff *exchgBufRead 43 | wrBuff *exchgBufWrite 44 | events chan TUNEvent 45 | errors chan error 46 | forcedMtu int 47 | } 48 | 49 | func packetAlign(size uint32) uint32 { 50 | return (size + (packetExchangeAlignment - 1)) &^ (packetExchangeAlignment - 1) 51 | } 52 | 53 | // 54 | // CreateTUN creates a Wintun adapter with the given name. Should a Wintun 55 | // adapter with the same name exist, it is reused. 56 | // 57 | func CreateTUN(ifname string) (TUNDevice, error) { 58 | // Does an interface with this name already exist? 59 | wt, err := wintun.GetInterface(ifname, 0) 60 | if wt == nil { 61 | // Interface does not exist or an error occured. Create one. 62 | wt, _, err = wintun.CreateInterface("WireGuard Tunnel Adapter", 0) 63 | if err != nil { 64 | return nil, errors.New("Creating Wintun adapter failed: " + err.Error()) 65 | } 66 | } else if err != nil { 67 | // Foreign interface with the same name found. 68 | // We could create a Wintun interface under a temporary name. But, should our 69 | // proces die without deleting this interface first, the interface would remain 70 | // orphaned. 71 | return nil, err 72 | } 73 | 74 | err = wt.SetInterfaceName(ifname) 75 | if err != nil { 76 | wt.DeleteInterface(0) 77 | return nil, errors.New("Setting interface name failed: " + err.Error()) 78 | } 79 | 80 | err = wt.FlushInterface() 81 | if err != nil { 82 | wt.DeleteInterface(0) 83 | return nil, errors.New("Flushing interface failed: " + err.Error()) 84 | } 85 | 86 | return &NativeTun{ 87 | wt: wt, 88 | tunName: wt.DataFileName(), 89 | rdBuff: &exchgBufRead{}, 90 | wrBuff: &exchgBufWrite{}, 91 | events: make(chan TUNEvent, 10), 92 | errors: make(chan error, 1), 93 | forcedMtu: 1500, 94 | }, nil 95 | } 96 | 97 | func (tun *NativeTun) openTUN() { 98 | for { 99 | file, err := os.OpenFile(tun.tunName, os.O_RDWR, 0) 100 | if err != nil { 101 | continue 102 | } 103 | tun.tunFile = file 104 | } 105 | } 106 | 107 | func (tun *NativeTun) closeTUN() (err error) { 108 | if tun.tunFile != nil { 109 | tun.tunLock.Lock() 110 | defer tun.tunLock.Unlock() 111 | if tun.tunFile == nil { 112 | return 113 | } 114 | t := tun.tunFile 115 | tun.tunFile = nil 116 | err = t.Close() 117 | } 118 | return 119 | } 120 | 121 | func (tun *NativeTun) getTUN() (*os.File, error) { 122 | if tun.tunFile == nil { 123 | tun.tunLock.Lock() 124 | defer tun.tunLock.Unlock() 125 | if tun.tunFile != nil { 126 | return tun.tunFile, nil 127 | } 128 | tun.openTUN() 129 | } 130 | return tun.tunFile, nil 131 | } 132 | 133 | func (tun *NativeTun) Name() (string, error) { 134 | return tun.wt.GetInterfaceName() 135 | } 136 | 137 | func (tun *NativeTun) File() *os.File { 138 | return nil 139 | } 140 | 141 | func (tun *NativeTun) Events() chan TUNEvent { 142 | return tun.events 143 | } 144 | 145 | func (tun *NativeTun) Close() error { 146 | err1 := tun.closeTUN() 147 | 148 | if tun.events != nil { 149 | close(tun.events) 150 | } 151 | 152 | _, _, err2 := tun.wt.DeleteInterface(0) 153 | if err1 == nil { 154 | err1 = err2 155 | } 156 | 157 | return err1 158 | } 159 | 160 | func (tun *NativeTun) MTU() (int, error) { 161 | return tun.forcedMtu, nil 162 | } 163 | 164 | //TODO: This is a temporary hack. We really need to be monitoring the interface in real time and adapting to MTU changes. 165 | func (tun *NativeTun) ForceMtu(mtu int) { 166 | tun.forcedMtu = mtu 167 | } 168 | 169 | func (tun *NativeTun) Read(buff []byte, offset int) (int, error) { 170 | select { 171 | case err := <-tun.errors: 172 | return 0, err 173 | default: 174 | } 175 | 176 | for { 177 | if tun.rdBuff.offset+packetExchangeAlignment <= tun.rdBuff.avail { 178 | // Get packet from the exchange buffer. 179 | packet := tun.rdBuff.data[tun.rdBuff.offset:] 180 | size := *(*uint32)(unsafe.Pointer(&packet[0])) 181 | pSize := packetAlign(packetExchangeAlignment + size) 182 | if packetSizeMax < size || tun.rdBuff.avail < tun.rdBuff.offset+pSize { 183 | // Invalid packet size. 184 | tun.rdBuff.avail = 0 185 | continue 186 | } 187 | packet = packet[packetExchangeAlignment : packetExchangeAlignment+size] 188 | 189 | // Copy data. 190 | copy(buff[offset:], packet) 191 | tun.rdBuff.offset += pSize 192 | return int(size), nil 193 | } 194 | 195 | // Get TUN data pipe. 196 | file, err := tun.getTUN() 197 | if err != nil { 198 | return 0, err 199 | } 200 | 201 | // Fill queue. 202 | n, err := file.Read(tun.rdBuff.data[:]) 203 | if err != nil { 204 | if pe, ok := err.(*os.PathError); ok && pe.Err == os.ErrClosed { 205 | return 0, err 206 | } 207 | // TUN interface stopped, failed, etc. Retry. 208 | tun.rdBuff.avail = 0 209 | tun.closeTUN() 210 | continue 211 | } 212 | tun.rdBuff.offset = 0 213 | tun.rdBuff.avail = uint32(n) 214 | } 215 | } 216 | 217 | // Note: flush() and putTunPacket() assume the caller comes only from a single thread; there's no locking. 218 | 219 | func (tun *NativeTun) flush() error { 220 | // Get TUN data pipe. 221 | file, err := tun.getTUN() 222 | if err != nil { 223 | return err 224 | } 225 | 226 | // Flush write buffer. 227 | _, err = file.Write(tun.wrBuff.data[:tun.wrBuff.offset]) 228 | tun.wrBuff.packetNum = 0 229 | tun.wrBuff.offset = 0 230 | if err != nil { 231 | // TUN interface stopped, failed, etc. Drop. 232 | tun.closeTUN() 233 | return err 234 | } 235 | 236 | return nil 237 | } 238 | 239 | func (tun *NativeTun) putTunPacket(buff []byte) error { 240 | size := uint32(len(buff)) 241 | if size == 0 { 242 | return errors.New("Empty packet") 243 | } 244 | if size > packetSizeMax { 245 | return errors.New("Packet too big") 246 | } 247 | pSize := packetAlign(packetExchangeAlignment + size) 248 | 249 | if tun.wrBuff.packetNum >= packetExchangeMax || tun.wrBuff.offset+pSize >= packetExchangeSize { 250 | // Exchange buffer is full -> flush first. 251 | err := tun.flush() 252 | if err != nil { 253 | return err 254 | } 255 | } 256 | 257 | // Write packet to the exchange buffer. 258 | packet := tun.wrBuff.data[tun.wrBuff.offset : tun.wrBuff.offset+pSize] 259 | *(*uint32)(unsafe.Pointer(&packet[0])) = size 260 | packet = packet[packetExchangeAlignment : packetExchangeAlignment+size] 261 | copy(packet, buff) 262 | 263 | tun.wrBuff.packetNum++ 264 | tun.wrBuff.offset += pSize 265 | 266 | return nil 267 | } 268 | 269 | func (tun *NativeTun) Write(buff []byte, offset int) (int, error) { 270 | err := tun.putTunPacket(buff[offset:]) 271 | if err != nil { 272 | return 0, err 273 | } 274 | 275 | // Flush write buffer. 276 | return len(buff) - offset, tun.flush() 277 | } 278 | 279 | // 280 | // GUID returns Windows adapter instance ID. 281 | // 282 | func (tun *NativeTun) GUID() windows.GUID { 283 | return *(*windows.GUID)(tun.wt) 284 | } 285 | -------------------------------------------------------------------------------- /tun/wintun/guid/guid_windows.go: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: MIT 2 | * 3 | * Copyright (C) 2019 WireGuard LLC. All Rights Reserved. 4 | */ 5 | 6 | package guid 7 | 8 | import ( 9 | "fmt" 10 | "syscall" 11 | 12 | "golang.org/x/sys/windows" 13 | ) 14 | 15 | //sys clsidFromString(lpsz *uint16, pclsid *windows.GUID) (hr int32) = ole32.CLSIDFromString 16 | 17 | // 18 | // FromString parses "{XXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX}" string to GUID. 19 | // 20 | func FromString(str string) (*windows.GUID, error) { 21 | strUTF16, err := syscall.UTF16PtrFromString(str) 22 | if err != nil { 23 | return nil, err 24 | } 25 | 26 | guid := &windows.GUID{} 27 | 28 | hr := clsidFromString(strUTF16, guid) 29 | if hr < 0 { 30 | return nil, syscall.Errno(hr) 31 | } 32 | 33 | return guid, nil 34 | } 35 | 36 | // 37 | // ToString function converts GUID to string 38 | // "{XXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX}". 39 | // 40 | // The resulting string is uppercase. 41 | // 42 | func ToString(guid *windows.GUID) string { 43 | return fmt.Sprintf("{%06X-%04X-%04X-%04X-%012X}", guid.Data1, guid.Data2, guid.Data3, guid.Data4[:2], guid.Data4[2:]) 44 | } 45 | -------------------------------------------------------------------------------- /tun/wintun/guid/mksyscall.go: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: MIT 2 | * 3 | * Copyright (C) 2019 WireGuard LLC. All Rights Reserved. 4 | */ 5 | 6 | package guid 7 | 8 | //go:generate go run $GOROOT/src/syscall/mksyscall_windows.go -output zguid_windows.go guid_windows.go 9 | -------------------------------------------------------------------------------- /tun/wintun/guid/zguid_windows.go: -------------------------------------------------------------------------------- 1 | // Code generated by 'go generate'; DO NOT EDIT. 2 | 3 | package guid 4 | 5 | import ( 6 | "syscall" 7 | "unsafe" 8 | 9 | "golang.org/x/sys/windows" 10 | ) 11 | 12 | var _ unsafe.Pointer 13 | 14 | // Do the interface allocations only once for common 15 | // Errno values. 16 | const ( 17 | errnoERROR_IO_PENDING = 997 18 | ) 19 | 20 | var ( 21 | errERROR_IO_PENDING error = syscall.Errno(errnoERROR_IO_PENDING) 22 | ) 23 | 24 | // errnoErr returns common boxed Errno values, to prevent 25 | // allocations at runtime. 26 | func errnoErr(e syscall.Errno) error { 27 | switch e { 28 | case 0: 29 | return nil 30 | case errnoERROR_IO_PENDING: 31 | return errERROR_IO_PENDING 32 | } 33 | // TODO: add more here, after collecting data on the common 34 | // error values see on Windows. (perhaps when running 35 | // all.bat?) 36 | return e 37 | } 38 | 39 | var ( 40 | modole32 = windows.NewLazySystemDLL("ole32.dll") 41 | 42 | procCLSIDFromString = modole32.NewProc("CLSIDFromString") 43 | ) 44 | 45 | func clsidFromString(lpsz *uint16, pclsid *windows.GUID) (hr int32) { 46 | r0, _, _ := syscall.Syscall(procCLSIDFromString.Addr(), 2, uintptr(unsafe.Pointer(lpsz)), uintptr(unsafe.Pointer(pclsid)), 0) 47 | hr = int32(r0) 48 | return 49 | } 50 | -------------------------------------------------------------------------------- /tun/wintun/setupapi/mksyscall.go: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: MIT 2 | * 3 | * Copyright (C) 2019 WireGuard LLC. All Rights Reserved. 4 | */ 5 | 6 | package setupapi 7 | 8 | //go:generate go run $GOROOT/src/syscall/mksyscall_windows.go -output zsetupapi_windows.go setupapi_windows.go 9 | -------------------------------------------------------------------------------- /tun/wintun/setupapi/zsetupapi_windows_test.go: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: MIT 2 | * 3 | * Copyright (C) 2019 WireGuard LLC. All Rights Reserved. 4 | */ 5 | 6 | package setupapi 7 | 8 | import ( 9 | "syscall" 10 | "testing" 11 | 12 | "golang.org/x/sys/windows" 13 | ) 14 | 15 | func TestSetupDiDestroyDeviceInfoList(t *testing.T) { 16 | err := SetupDiDestroyDeviceInfoList(DevInfo(windows.InvalidHandle)) 17 | if errWin, ok := err.(syscall.Errno); !ok || errWin != 6 /*ERROR_INVALID_HANDLE*/ { 18 | t.Errorf("SetupDiDestroyDeviceInfoList(nil, ...) should fail with ERROR_INVALID_HANDLE") 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /version.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | const WireGuardGoVersion = "0.0.20181222" 4 | --------------------------------------------------------------------------------