Compare commits
45 Commits
0.0.202104
...
0.0.202110
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f87e87af0d | ||
|
|
ba9e364dab | ||
|
|
dfd688b6aa | ||
|
|
c01d52b66a | ||
|
|
82d2aa87aa | ||
|
|
982d5d2e84 | ||
|
|
642a56e165 | ||
|
|
bb745b2ea3 | ||
|
|
fcc601dbf0 | ||
|
|
217ac1016b | ||
|
|
eae5e0f3a3 | ||
|
|
2ef39d4754 | ||
|
|
3957e9b9dd | ||
|
|
bad6caeb82 | ||
|
|
c89f5ca665 | ||
|
|
15b24b6179 | ||
|
|
f9b48a961c | ||
|
|
d0cf96114f | ||
|
|
841756e328 | ||
|
|
c382222eab | ||
|
|
b41f4cc768 | ||
|
|
4a57024b94 | ||
|
|
64cb82f2b3 | ||
|
|
c27ff9b9f6 | ||
|
|
99e8b4ba60 | ||
|
|
bd83f0ac99 | ||
|
|
50d779833e | ||
|
|
a9b377e9e1 | ||
|
|
9087e444e6 | ||
|
|
25ad08a591 | ||
|
|
5846b62283 | ||
|
|
9844c74f67 | ||
|
|
4e9e5dad09 | ||
|
|
39e0b6dade | ||
|
|
7121927b87 | ||
|
|
326aec10af | ||
|
|
efb8818550 | ||
|
|
69b39db0b4 | ||
|
|
db733ccd65 | ||
|
|
a7aec4449f | ||
|
|
60a26371f4 | ||
|
|
a544776d70 | ||
|
|
69a42a4eef | ||
|
|
097af6e135 | ||
|
|
8246d251ea |
2
Makefile
2
Makefile
@@ -10,7 +10,7 @@ MAKEFLAGS += --no-print-directory
|
||||
generate-version-and-build:
|
||||
@export GIT_CEILING_DIRECTORIES="$(realpath $(CURDIR)/..)" && \
|
||||
tag="$$(git describe --dirty 2>/dev/null)" && \
|
||||
ver="$$(printf 'package main\nconst Version = "%s"\n' "$$tag")" && \
|
||||
ver="$$(printf 'package main\n\nconst Version = "%s"\n' "$$tag")" && \
|
||||
[ "$$(cat version.go 2>/dev/null)" != "$$ver" ] && \
|
||||
echo "$$ver" > version.go && \
|
||||
git update-index --assume-unchanged version.go || true
|
||||
|
||||
@@ -148,12 +148,12 @@ again:
|
||||
|
||||
var fns []ReceiveFunc
|
||||
if sock4 != -1 {
|
||||
fns = append(fns, bind.makeReceiveIPv4(sock4))
|
||||
bind.sock4 = sock4
|
||||
fns = append(fns, bind.receiveIPv4)
|
||||
}
|
||||
if sock6 != -1 {
|
||||
fns = append(fns, bind.makeReceiveIPv6(sock6))
|
||||
bind.sock6 = sock6
|
||||
fns = append(fns, bind.receiveIPv6)
|
||||
}
|
||||
if len(fns) == 0 {
|
||||
return nil, 0, syscall.EAFNOSUPPORT
|
||||
@@ -224,20 +224,26 @@ func (bind *LinuxSocketBind) Close() error {
|
||||
return err2
|
||||
}
|
||||
|
||||
func (*LinuxSocketBind) makeReceiveIPv6(sock int) ReceiveFunc {
|
||||
return func(buff []byte) (int, Endpoint, error) {
|
||||
var end LinuxSocketEndpoint
|
||||
n, err := receive6(sock, buff, &end)
|
||||
return n, &end, err
|
||||
func (bind *LinuxSocketBind) receiveIPv4(buf []byte) (int, Endpoint, error) {
|
||||
bind.mu.RLock()
|
||||
defer bind.mu.RUnlock()
|
||||
if bind.sock4 == -1 {
|
||||
return 0, nil, net.ErrClosed
|
||||
}
|
||||
var end LinuxSocketEndpoint
|
||||
n, err := receive4(bind.sock4, buf, &end)
|
||||
return n, &end, err
|
||||
}
|
||||
|
||||
func (*LinuxSocketBind) makeReceiveIPv4(sock int) ReceiveFunc {
|
||||
return func(buff []byte) (int, Endpoint, error) {
|
||||
var end LinuxSocketEndpoint
|
||||
n, err := receive4(sock, buff, &end)
|
||||
return n, &end, err
|
||||
func (bind *LinuxSocketBind) receiveIPv6(buf []byte) (int, Endpoint, error) {
|
||||
bind.mu.RLock()
|
||||
defer bind.mu.RUnlock()
|
||||
if bind.sock6 == -1 {
|
||||
return 0, nil, net.ErrClosed
|
||||
}
|
||||
var end LinuxSocketEndpoint
|
||||
n, err := receive6(bind.sock6, buf, &end)
|
||||
return n, &end, err
|
||||
}
|
||||
|
||||
func (bind *LinuxSocketBind) Send(buff []byte, end Endpoint) error {
|
||||
|
||||
@@ -121,10 +121,8 @@ func (*WinRingBind) ParseEndpoint(s string) (Endpoint, error) {
|
||||
if (addrinfo.Family != windows.AF_INET && addrinfo.Family != windows.AF_INET6) || addrinfo.Addrlen > unsafe.Sizeof(WinRingEndpoint{}) {
|
||||
return nil, windows.ERROR_INVALID_ADDRESS
|
||||
}
|
||||
var src []byte
|
||||
var dst [unsafe.Sizeof(WinRingEndpoint{})]byte
|
||||
unsafeSlice(unsafe.Pointer(&src), unsafe.Pointer(addrinfo.Addr), int(addrinfo.Addrlen))
|
||||
copy(dst[:], src)
|
||||
copy(dst[:], unsafe.Slice((*byte)(unsafe.Pointer(addrinfo.Addr)), addrinfo.Addrlen))
|
||||
return (*WinRingEndpoint)(unsafe.Pointer(&dst[0])), nil
|
||||
}
|
||||
|
||||
@@ -352,8 +350,12 @@ func (bind *afWinRingBind) Receive(buf []byte, isOpen *uint32) (int, Endpoint, e
|
||||
}
|
||||
bind.rx.mu.Lock()
|
||||
defer bind.rx.mu.Unlock()
|
||||
|
||||
var err error
|
||||
var count uint32
|
||||
var results [1]winrio.Result
|
||||
retry:
|
||||
count = 0
|
||||
for tries := 0; count == 0 && tries < receiveSpins; tries++ {
|
||||
if tries > 0 {
|
||||
if atomic.LoadUint32(isOpen) != 1 {
|
||||
@@ -364,7 +366,7 @@ func (bind *afWinRingBind) Receive(buf []byte, isOpen *uint32) (int, Endpoint, e
|
||||
count = winrio.DequeueCompletion(bind.rx.cq, results[:])
|
||||
}
|
||||
if count == 0 {
|
||||
err := winrio.Notify(bind.rx.cq)
|
||||
err = winrio.Notify(bind.rx.cq)
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
@@ -385,10 +387,19 @@ func (bind *afWinRingBind) Receive(buf []byte, isOpen *uint32) (int, Endpoint, e
|
||||
}
|
||||
}
|
||||
bind.rx.Return(1)
|
||||
err := bind.InsertReceiveRequest()
|
||||
err = bind.InsertReceiveRequest()
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
// We limit the MTU well below the 65k max for practicality, but this means a remote host can still send us
|
||||
// huge packets. Just try again when this happens. The infinite loop this could cause is still limited to
|
||||
// attacker bandwidth, just like the rest of the receive path.
|
||||
if windows.Errno(results[0].Status) == windows.WSAEMSGSIZE {
|
||||
if atomic.LoadUint32(isOpen) != 1 {
|
||||
return 0, nil, net.ErrClosed
|
||||
}
|
||||
goto retry
|
||||
}
|
||||
if results[0].Status != 0 {
|
||||
return 0, nil, windows.Errno(results[0].Status)
|
||||
}
|
||||
@@ -568,21 +579,3 @@ func bindSocketToInterface6(handle windows.Handle, interfaceIndex uint32) error
|
||||
const IPV6_UNICAST_IF = 31
|
||||
return windows.SetsockoptInt(handle, windows.IPPROTO_IPV6, IPV6_UNICAST_IF, int(interfaceIndex))
|
||||
}
|
||||
|
||||
// unsafeSlice updates the slice slicePtr to be a slice
|
||||
// referencing the provided data with its length & capacity set to
|
||||
// lenCap.
|
||||
//
|
||||
// TODO: when Go 1.16 or Go 1.17 is the minimum supported version,
|
||||
// update callers to use unsafe.Slice instead of this.
|
||||
func unsafeSlice(slicePtr, data unsafe.Pointer, lenCap int) {
|
||||
type sliceHeader struct {
|
||||
Data unsafe.Pointer
|
||||
Len int
|
||||
Cap int
|
||||
}
|
||||
h := (*sliceHeader)(slicePtr)
|
||||
h.Data = data
|
||||
h.Len = lenCap
|
||||
h.Cap = lenCap
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// +build !linux,!windows
|
||||
//go:build !linux && !windows
|
||||
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// +build !linux,!openbsd,!freebsd
|
||||
//go:build !linux && !openbsd && !freebsd
|
||||
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// +build linux openbsd freebsd
|
||||
//go:build linux || openbsd || freebsd
|
||||
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
|
||||
@@ -14,14 +14,20 @@ import (
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
type parentIndirection struct {
|
||||
parentBit **trieEntry
|
||||
parentBitType uint8
|
||||
}
|
||||
|
||||
type trieEntry struct {
|
||||
child [2]*trieEntry
|
||||
peer *Peer
|
||||
bits net.IP
|
||||
cidr uint
|
||||
bit_at_byte uint
|
||||
bit_at_shift uint
|
||||
perPeerElem *list.Element
|
||||
peer *Peer
|
||||
child [2]*trieEntry
|
||||
parent parentIndirection
|
||||
cidr uint8
|
||||
bitAtByte uint8
|
||||
bitAtShift uint8
|
||||
bits net.IP
|
||||
perPeerElem *list.Element
|
||||
}
|
||||
|
||||
func isLittleEndian() bool {
|
||||
@@ -45,24 +51,24 @@ func swapU64(i uint64) uint64 {
|
||||
return bits.ReverseBytes64(i)
|
||||
}
|
||||
|
||||
func commonBits(ip1 net.IP, ip2 net.IP) uint {
|
||||
func commonBits(ip1 net.IP, ip2 net.IP) uint8 {
|
||||
size := len(ip1)
|
||||
if size == net.IPv4len {
|
||||
a := (*uint32)(unsafe.Pointer(&ip1[0]))
|
||||
b := (*uint32)(unsafe.Pointer(&ip2[0]))
|
||||
x := *a ^ *b
|
||||
return uint(bits.LeadingZeros32(swapU32(x)))
|
||||
return uint8(bits.LeadingZeros32(swapU32(x)))
|
||||
} else if size == net.IPv6len {
|
||||
a := (*uint64)(unsafe.Pointer(&ip1[0]))
|
||||
b := (*uint64)(unsafe.Pointer(&ip2[0]))
|
||||
x := *a ^ *b
|
||||
if x != 0 {
|
||||
return uint(bits.LeadingZeros64(swapU64(x)))
|
||||
return uint8(bits.LeadingZeros64(swapU64(x)))
|
||||
}
|
||||
a = (*uint64)(unsafe.Pointer(&ip1[8]))
|
||||
b = (*uint64)(unsafe.Pointer(&ip2[8]))
|
||||
x = *a ^ *b
|
||||
return 64 + uint(bits.LeadingZeros64(swapU64(x)))
|
||||
return 64 + uint8(bits.LeadingZeros64(swapU64(x)))
|
||||
} else {
|
||||
panic("Wrong size bit string")
|
||||
}
|
||||
@@ -79,32 +85,8 @@ func (node *trieEntry) removeFromPeerEntries() {
|
||||
}
|
||||
}
|
||||
|
||||
func (node *trieEntry) removeByPeer(p *Peer) *trieEntry {
|
||||
if node == nil {
|
||||
return node
|
||||
}
|
||||
|
||||
// walk recursively
|
||||
|
||||
node.child[0] = node.child[0].removeByPeer(p)
|
||||
node.child[1] = node.child[1].removeByPeer(p)
|
||||
|
||||
if node.peer != p {
|
||||
return node
|
||||
}
|
||||
|
||||
// remove peer & merge
|
||||
|
||||
node.removeFromPeerEntries()
|
||||
node.peer = nil
|
||||
if node.child[0] == nil {
|
||||
return node.child[1]
|
||||
}
|
||||
return node.child[0]
|
||||
}
|
||||
|
||||
func (node *trieEntry) choose(ip net.IP) byte {
|
||||
return (ip[node.bit_at_byte] >> node.bit_at_shift) & 1
|
||||
return (ip[node.bitAtByte] >> node.bitAtShift) & 1
|
||||
}
|
||||
|
||||
func (node *trieEntry) maskSelf() {
|
||||
@@ -114,86 +96,125 @@ func (node *trieEntry) maskSelf() {
|
||||
}
|
||||
}
|
||||
|
||||
func (node *trieEntry) insert(ip net.IP, cidr uint, peer *Peer) *trieEntry {
|
||||
func (node *trieEntry) zeroizePointers() {
|
||||
// Make the garbage collector's life slightly easier
|
||||
node.peer = nil
|
||||
node.child[0] = nil
|
||||
node.child[1] = nil
|
||||
node.parent.parentBit = nil
|
||||
}
|
||||
|
||||
// at leaf
|
||||
func (node *trieEntry) nodePlacement(ip net.IP, cidr uint8) (parent *trieEntry, exact bool) {
|
||||
for node != nil && node.cidr <= cidr && commonBits(node.bits, ip) >= node.cidr {
|
||||
parent = node
|
||||
if parent.cidr == cidr {
|
||||
exact = true
|
||||
return
|
||||
}
|
||||
bit := node.choose(ip)
|
||||
node = node.child[bit]
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if node == nil {
|
||||
func (trie parentIndirection) insert(ip net.IP, cidr uint8, peer *Peer) {
|
||||
if *trie.parentBit == nil {
|
||||
node := &trieEntry{
|
||||
bits: ip,
|
||||
peer: peer,
|
||||
cidr: cidr,
|
||||
bit_at_byte: cidr / 8,
|
||||
bit_at_shift: 7 - (cidr % 8),
|
||||
peer: peer,
|
||||
parent: trie,
|
||||
bits: ip,
|
||||
cidr: cidr,
|
||||
bitAtByte: cidr / 8,
|
||||
bitAtShift: 7 - (cidr % 8),
|
||||
}
|
||||
node.maskSelf()
|
||||
node.addToPeerEntries()
|
||||
return node
|
||||
*trie.parentBit = node
|
||||
return
|
||||
}
|
||||
|
||||
// traverse deeper
|
||||
|
||||
common := commonBits(node.bits, ip)
|
||||
if node.cidr <= cidr && common >= node.cidr {
|
||||
if node.cidr == cidr {
|
||||
node.removeFromPeerEntries()
|
||||
node.peer = peer
|
||||
node.addToPeerEntries()
|
||||
return node
|
||||
}
|
||||
bit := node.choose(ip)
|
||||
node.child[bit] = node.child[bit].insert(ip, cidr, peer)
|
||||
return node
|
||||
node, exact := (*trie.parentBit).nodePlacement(ip, cidr)
|
||||
if exact {
|
||||
node.removeFromPeerEntries()
|
||||
node.peer = peer
|
||||
node.addToPeerEntries()
|
||||
return
|
||||
}
|
||||
|
||||
// split node
|
||||
|
||||
newNode := &trieEntry{
|
||||
bits: ip,
|
||||
peer: peer,
|
||||
cidr: cidr,
|
||||
bit_at_byte: cidr / 8,
|
||||
bit_at_shift: 7 - (cidr % 8),
|
||||
peer: peer,
|
||||
bits: ip,
|
||||
cidr: cidr,
|
||||
bitAtByte: cidr / 8,
|
||||
bitAtShift: 7 - (cidr % 8),
|
||||
}
|
||||
newNode.maskSelf()
|
||||
newNode.addToPeerEntries()
|
||||
|
||||
cidr = min(cidr, common)
|
||||
|
||||
// check for shorter prefix
|
||||
var down *trieEntry
|
||||
if node == nil {
|
||||
down = *trie.parentBit
|
||||
} else {
|
||||
bit := node.choose(ip)
|
||||
down = node.child[bit]
|
||||
if down == nil {
|
||||
newNode.parent = parentIndirection{&node.child[bit], bit}
|
||||
node.child[bit] = newNode
|
||||
return
|
||||
}
|
||||
}
|
||||
common := commonBits(down.bits, ip)
|
||||
if common < cidr {
|
||||
cidr = common
|
||||
}
|
||||
parent := node
|
||||
|
||||
if newNode.cidr == cidr {
|
||||
bit := newNode.choose(node.bits)
|
||||
newNode.child[bit] = node
|
||||
return newNode
|
||||
bit := newNode.choose(down.bits)
|
||||
down.parent = parentIndirection{&newNode.child[bit], bit}
|
||||
newNode.child[bit] = down
|
||||
if parent == nil {
|
||||
newNode.parent = trie
|
||||
*trie.parentBit = newNode
|
||||
} else {
|
||||
bit := parent.choose(newNode.bits)
|
||||
newNode.parent = parentIndirection{&parent.child[bit], bit}
|
||||
parent.child[bit] = newNode
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// create new parent for node & newNode
|
||||
|
||||
parent := &trieEntry{
|
||||
bits: append([]byte{}, ip...),
|
||||
peer: nil,
|
||||
cidr: cidr,
|
||||
bit_at_byte: cidr / 8,
|
||||
bit_at_shift: 7 - (cidr % 8),
|
||||
node = &trieEntry{
|
||||
bits: append([]byte{}, newNode.bits...),
|
||||
cidr: cidr,
|
||||
bitAtByte: cidr / 8,
|
||||
bitAtShift: 7 - (cidr % 8),
|
||||
}
|
||||
parent.maskSelf()
|
||||
node.maskSelf()
|
||||
|
||||
bit := parent.choose(ip)
|
||||
parent.child[bit] = newNode
|
||||
parent.child[bit^1] = node
|
||||
|
||||
return parent
|
||||
bit := node.choose(down.bits)
|
||||
down.parent = parentIndirection{&node.child[bit], bit}
|
||||
node.child[bit] = down
|
||||
bit = node.choose(newNode.bits)
|
||||
newNode.parent = parentIndirection{&node.child[bit], bit}
|
||||
node.child[bit] = newNode
|
||||
if parent == nil {
|
||||
node.parent = trie
|
||||
*trie.parentBit = node
|
||||
} else {
|
||||
bit := parent.choose(node.bits)
|
||||
node.parent = parentIndirection{&parent.child[bit], bit}
|
||||
parent.child[bit] = node
|
||||
}
|
||||
}
|
||||
|
||||
func (node *trieEntry) lookup(ip net.IP) *Peer {
|
||||
var found *Peer
|
||||
size := uint(len(ip))
|
||||
size := uint8(len(ip))
|
||||
for node != nil && commonBits(node.bits, ip) >= node.cidr {
|
||||
if node.peer != nil {
|
||||
found = node.peer
|
||||
}
|
||||
if node.bit_at_byte == size {
|
||||
if node.bitAtByte == size {
|
||||
break
|
||||
}
|
||||
bit := node.choose(ip)
|
||||
@@ -208,7 +229,7 @@ type AllowedIPs struct {
|
||||
mutex sync.RWMutex
|
||||
}
|
||||
|
||||
func (table *AllowedIPs) EntriesForPeer(peer *Peer, cb func(ip net.IP, cidr uint) bool) {
|
||||
func (table *AllowedIPs) EntriesForPeer(peer *Peer, cb func(ip net.IP, cidr uint8) bool) {
|
||||
table.mutex.RLock()
|
||||
defer table.mutex.RUnlock()
|
||||
|
||||
@@ -224,32 +245,67 @@ func (table *AllowedIPs) RemoveByPeer(peer *Peer) {
|
||||
table.mutex.Lock()
|
||||
defer table.mutex.Unlock()
|
||||
|
||||
table.IPv4 = table.IPv4.removeByPeer(peer)
|
||||
table.IPv6 = table.IPv6.removeByPeer(peer)
|
||||
var next *list.Element
|
||||
for elem := peer.trieEntries.Front(); elem != nil; elem = next {
|
||||
next = elem.Next()
|
||||
node := elem.Value.(*trieEntry)
|
||||
|
||||
node.removeFromPeerEntries()
|
||||
node.peer = nil
|
||||
if node.child[0] != nil && node.child[1] != nil {
|
||||
continue
|
||||
}
|
||||
bit := 0
|
||||
if node.child[0] == nil {
|
||||
bit = 1
|
||||
}
|
||||
child := node.child[bit]
|
||||
if child != nil {
|
||||
child.parent = node.parent
|
||||
}
|
||||
*node.parent.parentBit = child
|
||||
if node.child[0] != nil || node.child[1] != nil || node.parent.parentBitType > 1 {
|
||||
node.zeroizePointers()
|
||||
continue
|
||||
}
|
||||
parent := (*trieEntry)(unsafe.Pointer(uintptr(unsafe.Pointer(node.parent.parentBit)) - unsafe.Offsetof(node.child) - unsafe.Sizeof(node.child[0])*uintptr(node.parent.parentBitType)))
|
||||
if parent.peer != nil {
|
||||
node.zeroizePointers()
|
||||
continue
|
||||
}
|
||||
child = parent.child[node.parent.parentBitType^1]
|
||||
if child != nil {
|
||||
child.parent = parent.parent
|
||||
}
|
||||
*parent.parent.parentBit = child
|
||||
node.zeroizePointers()
|
||||
parent.zeroizePointers()
|
||||
}
|
||||
}
|
||||
|
||||
func (table *AllowedIPs) Insert(ip net.IP, cidr uint, peer *Peer) {
|
||||
func (table *AllowedIPs) Insert(ip net.IP, cidr uint8, peer *Peer) {
|
||||
table.mutex.Lock()
|
||||
defer table.mutex.Unlock()
|
||||
|
||||
switch len(ip) {
|
||||
case net.IPv6len:
|
||||
table.IPv6 = table.IPv6.insert(ip, cidr, peer)
|
||||
parentIndirection{&table.IPv6, 2}.insert(ip, cidr, peer)
|
||||
case net.IPv4len:
|
||||
table.IPv4 = table.IPv4.insert(ip, cidr, peer)
|
||||
parentIndirection{&table.IPv4, 2}.insert(ip, cidr, peer)
|
||||
default:
|
||||
panic(errors.New("inserting unknown address type"))
|
||||
}
|
||||
}
|
||||
|
||||
func (table *AllowedIPs) LookupIPv4(address []byte) *Peer {
|
||||
func (table *AllowedIPs) Lookup(address []byte) *Peer {
|
||||
table.mutex.RLock()
|
||||
defer table.mutex.RUnlock()
|
||||
return table.IPv4.lookup(address)
|
||||
}
|
||||
|
||||
func (table *AllowedIPs) LookupIPv6(address []byte) *Peer {
|
||||
table.mutex.RLock()
|
||||
defer table.mutex.RUnlock()
|
||||
return table.IPv6.lookup(address)
|
||||
switch len(address) {
|
||||
case net.IPv6len:
|
||||
return table.IPv6.lookup(address)
|
||||
case net.IPv4len:
|
||||
return table.IPv4.lookup(address)
|
||||
default:
|
||||
panic(errors.New("looking up unknown address type"))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,19 +7,21 @@ package device
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
"net"
|
||||
"sort"
|
||||
"testing"
|
||||
)
|
||||
|
||||
const (
|
||||
NumberOfPeers = 100
|
||||
NumberOfAddresses = 250
|
||||
NumberOfTests = 10000
|
||||
NumberOfPeers = 100
|
||||
NumberOfPeerRemovals = 4
|
||||
NumberOfAddresses = 250
|
||||
NumberOfTests = 10000
|
||||
)
|
||||
|
||||
type SlowNode struct {
|
||||
peer *Peer
|
||||
cidr uint
|
||||
cidr uint8
|
||||
bits []byte
|
||||
}
|
||||
|
||||
@@ -37,7 +39,7 @@ func (r SlowRouter) Swap(i, j int) {
|
||||
r[i], r[j] = r[j], r[i]
|
||||
}
|
||||
|
||||
func (r SlowRouter) Insert(addr []byte, cidr uint, peer *Peer) SlowRouter {
|
||||
func (r SlowRouter) Insert(addr []byte, cidr uint8, peer *Peer) SlowRouter {
|
||||
for _, t := range r {
|
||||
if t.cidr == cidr && commonBits(t.bits, addr) >= cidr {
|
||||
t.peer = peer
|
||||
@@ -64,68 +66,75 @@ func (r SlowRouter) Lookup(addr []byte) *Peer {
|
||||
return nil
|
||||
}
|
||||
|
||||
func TestTrieRandomIPv4(t *testing.T) {
|
||||
var trie *trieEntry
|
||||
var slow SlowRouter
|
||||
func (r SlowRouter) RemoveByPeer(peer *Peer) SlowRouter {
|
||||
n := 0
|
||||
for _, x := range r {
|
||||
if x.peer != peer {
|
||||
r[n] = x
|
||||
n++
|
||||
}
|
||||
}
|
||||
return r[:n]
|
||||
}
|
||||
|
||||
func TestTrieRandom(t *testing.T) {
|
||||
var slow4, slow6 SlowRouter
|
||||
var peers []*Peer
|
||||
var allowedIPs AllowedIPs
|
||||
|
||||
rand.Seed(1)
|
||||
|
||||
const AddressLength = 4
|
||||
|
||||
for n := 0; n < NumberOfPeers; n++ {
|
||||
peers = append(peers, &Peer{})
|
||||
}
|
||||
|
||||
for n := 0; n < NumberOfAddresses; n++ {
|
||||
var addr [AddressLength]byte
|
||||
rand.Read(addr[:])
|
||||
cidr := uint(rand.Uint32() % (AddressLength * 8))
|
||||
index := rand.Int() % NumberOfPeers
|
||||
trie = trie.insert(addr[:], cidr, peers[index])
|
||||
slow = slow.Insert(addr[:], cidr, peers[index])
|
||||
var addr4 [4]byte
|
||||
rand.Read(addr4[:])
|
||||
cidr := uint8(rand.Intn(32) + 1)
|
||||
index := rand.Intn(NumberOfPeers)
|
||||
allowedIPs.Insert(addr4[:], cidr, peers[index])
|
||||
slow4 = slow4.Insert(addr4[:], cidr, peers[index])
|
||||
|
||||
var addr6 [16]byte
|
||||
rand.Read(addr6[:])
|
||||
cidr = uint8(rand.Intn(128) + 1)
|
||||
index = rand.Intn(NumberOfPeers)
|
||||
allowedIPs.Insert(addr6[:], cidr, peers[index])
|
||||
slow6 = slow6.Insert(addr6[:], cidr, peers[index])
|
||||
}
|
||||
|
||||
for n := 0; n < NumberOfTests; n++ {
|
||||
var addr [AddressLength]byte
|
||||
rand.Read(addr[:])
|
||||
peer1 := slow.Lookup(addr[:])
|
||||
peer2 := trie.lookup(addr[:])
|
||||
if peer1 != peer2 {
|
||||
t.Error("Trie did not match naive implementation, for:", addr)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestTrieRandomIPv6(t *testing.T) {
|
||||
var trie *trieEntry
|
||||
var slow SlowRouter
|
||||
var peers []*Peer
|
||||
|
||||
rand.Seed(1)
|
||||
|
||||
const AddressLength = 16
|
||||
|
||||
for n := 0; n < NumberOfPeers; n++ {
|
||||
peers = append(peers, &Peer{})
|
||||
}
|
||||
|
||||
for n := 0; n < NumberOfAddresses; n++ {
|
||||
var addr [AddressLength]byte
|
||||
rand.Read(addr[:])
|
||||
cidr := uint(rand.Uint32() % (AddressLength * 8))
|
||||
index := rand.Int() % NumberOfPeers
|
||||
trie = trie.insert(addr[:], cidr, peers[index])
|
||||
slow = slow.Insert(addr[:], cidr, peers[index])
|
||||
}
|
||||
|
||||
for n := 0; n < NumberOfTests; n++ {
|
||||
var addr [AddressLength]byte
|
||||
rand.Read(addr[:])
|
||||
peer1 := slow.Lookup(addr[:])
|
||||
peer2 := trie.lookup(addr[:])
|
||||
if peer1 != peer2 {
|
||||
t.Error("Trie did not match naive implementation, for:", addr)
|
||||
var p int
|
||||
for p = 0; ; p++ {
|
||||
for n := 0; n < NumberOfTests; n++ {
|
||||
var addr4 [4]byte
|
||||
rand.Read(addr4[:])
|
||||
peer1 := slow4.Lookup(addr4[:])
|
||||
peer2 := allowedIPs.Lookup(addr4[:])
|
||||
if peer1 != peer2 {
|
||||
t.Errorf("Trie did not match naive implementation, for %v: want %p, got %p", net.IP(addr4[:]), peer1, peer2)
|
||||
}
|
||||
|
||||
var addr6 [16]byte
|
||||
rand.Read(addr6[:])
|
||||
peer1 = slow6.Lookup(addr6[:])
|
||||
peer2 = allowedIPs.Lookup(addr6[:])
|
||||
if peer1 != peer2 {
|
||||
t.Errorf("Trie did not match naive implementation, for %v: want %p, got %p", net.IP(addr6[:]), peer1, peer2)
|
||||
}
|
||||
}
|
||||
if p >= len(peers) || p >= NumberOfPeerRemovals {
|
||||
break
|
||||
}
|
||||
allowedIPs.RemoveByPeer(peers[p])
|
||||
slow4 = slow4.RemoveByPeer(peers[p])
|
||||
slow6 = slow6.RemoveByPeer(peers[p])
|
||||
}
|
||||
for ; p < len(peers); p++ {
|
||||
allowedIPs.RemoveByPeer(peers[p])
|
||||
}
|
||||
|
||||
if allowedIPs.IPv4 != nil || allowedIPs.IPv6 != nil {
|
||||
t.Error("Failed to remove all nodes from trie by peer")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,13 +11,10 @@ import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
/* Todo: More comprehensive
|
||||
*/
|
||||
|
||||
type testPairCommonBits struct {
|
||||
s1 []byte
|
||||
s2 []byte
|
||||
match uint
|
||||
match uint8
|
||||
}
|
||||
|
||||
func TestCommonBits(t *testing.T) {
|
||||
@@ -45,6 +42,7 @@ func TestCommonBits(t *testing.T) {
|
||||
func benchmarkTrie(peerNumber int, addressNumber int, addressLength int, b *testing.B) {
|
||||
var trie *trieEntry
|
||||
var peers []*Peer
|
||||
root := parentIndirection{&trie, 2}
|
||||
|
||||
rand.Seed(1)
|
||||
|
||||
@@ -57,9 +55,9 @@ func benchmarkTrie(peerNumber int, addressNumber int, addressLength int, b *test
|
||||
for n := 0; n < addressNumber; n++ {
|
||||
var addr [AddressLength]byte
|
||||
rand.Read(addr[:])
|
||||
cidr := uint(rand.Uint32() % (AddressLength * 8))
|
||||
cidr := uint8(rand.Uint32() % (AddressLength * 8))
|
||||
index := rand.Int() % peerNumber
|
||||
trie = trie.insert(addr[:], cidr, peers[index])
|
||||
root.insert(addr[:], cidr, peers[index])
|
||||
}
|
||||
|
||||
for n := 0; n < b.N; n++ {
|
||||
@@ -97,21 +95,21 @@ func TestTrieIPv4(t *testing.T) {
|
||||
g := &Peer{}
|
||||
h := &Peer{}
|
||||
|
||||
var trie *trieEntry
|
||||
var allowedIPs AllowedIPs
|
||||
|
||||
insert := func(peer *Peer, a, b, c, d byte, cidr uint) {
|
||||
trie = trie.insert([]byte{a, b, c, d}, cidr, peer)
|
||||
insert := func(peer *Peer, a, b, c, d byte, cidr uint8) {
|
||||
allowedIPs.Insert([]byte{a, b, c, d}, cidr, peer)
|
||||
}
|
||||
|
||||
assertEQ := func(peer *Peer, a, b, c, d byte) {
|
||||
p := trie.lookup([]byte{a, b, c, d})
|
||||
p := allowedIPs.Lookup([]byte{a, b, c, d})
|
||||
if p != peer {
|
||||
t.Error("Assert EQ failed")
|
||||
}
|
||||
}
|
||||
|
||||
assertNEQ := func(peer *Peer, a, b, c, d byte) {
|
||||
p := trie.lookup([]byte{a, b, c, d})
|
||||
p := allowedIPs.Lookup([]byte{a, b, c, d})
|
||||
if p == peer {
|
||||
t.Error("Assert NEQ failed")
|
||||
}
|
||||
@@ -153,7 +151,7 @@ func TestTrieIPv4(t *testing.T) {
|
||||
assertEQ(a, 192, 0, 0, 0)
|
||||
assertEQ(a, 255, 0, 0, 0)
|
||||
|
||||
trie = trie.removeByPeer(a)
|
||||
allowedIPs.RemoveByPeer(a)
|
||||
|
||||
assertNEQ(a, 1, 0, 0, 0)
|
||||
assertNEQ(a, 64, 0, 0, 0)
|
||||
@@ -161,12 +159,21 @@ func TestTrieIPv4(t *testing.T) {
|
||||
assertNEQ(a, 192, 0, 0, 0)
|
||||
assertNEQ(a, 255, 0, 0, 0)
|
||||
|
||||
trie = nil
|
||||
allowedIPs.RemoveByPeer(a)
|
||||
allowedIPs.RemoveByPeer(b)
|
||||
allowedIPs.RemoveByPeer(c)
|
||||
allowedIPs.RemoveByPeer(d)
|
||||
allowedIPs.RemoveByPeer(e)
|
||||
allowedIPs.RemoveByPeer(g)
|
||||
allowedIPs.RemoveByPeer(h)
|
||||
if allowedIPs.IPv4 != nil || allowedIPs.IPv6 != nil {
|
||||
t.Error("Expected removing all the peers to empty trie, but it did not")
|
||||
}
|
||||
|
||||
insert(a, 192, 168, 0, 0, 16)
|
||||
insert(a, 192, 168, 0, 0, 24)
|
||||
|
||||
trie = trie.removeByPeer(a)
|
||||
allowedIPs.RemoveByPeer(a)
|
||||
|
||||
assertNEQ(a, 192, 168, 0, 1)
|
||||
}
|
||||
@@ -184,7 +191,7 @@ func TestTrieIPv6(t *testing.T) {
|
||||
g := &Peer{}
|
||||
h := &Peer{}
|
||||
|
||||
var trie *trieEntry
|
||||
var allowedIPs AllowedIPs
|
||||
|
||||
expand := func(a uint32) []byte {
|
||||
var out [4]byte
|
||||
@@ -195,13 +202,13 @@ func TestTrieIPv6(t *testing.T) {
|
||||
return out[:]
|
||||
}
|
||||
|
||||
insert := func(peer *Peer, a, b, c, d uint32, cidr uint) {
|
||||
insert := func(peer *Peer, a, b, c, d uint32, cidr uint8) {
|
||||
var addr []byte
|
||||
addr = append(addr, expand(a)...)
|
||||
addr = append(addr, expand(b)...)
|
||||
addr = append(addr, expand(c)...)
|
||||
addr = append(addr, expand(d)...)
|
||||
trie = trie.insert(addr, cidr, peer)
|
||||
allowedIPs.Insert(addr, cidr, peer)
|
||||
}
|
||||
|
||||
assertEQ := func(peer *Peer, a, b, c, d uint32) {
|
||||
@@ -210,7 +217,7 @@ func TestTrieIPv6(t *testing.T) {
|
||||
addr = append(addr, expand(b)...)
|
||||
addr = append(addr, expand(c)...)
|
||||
addr = append(addr, expand(d)...)
|
||||
p := trie.lookup(addr)
|
||||
p := allowedIPs.Lookup(addr)
|
||||
if p != peer {
|
||||
t.Error("Assert EQ failed")
|
||||
}
|
||||
|
||||
@@ -35,7 +35,6 @@ const (
|
||||
/* Implementation constants */
|
||||
|
||||
const (
|
||||
UnderLoadQueueSize = QueueHandshakeSize / 8
|
||||
UnderLoadAfterTime = time.Second // how long does the device remain under load after detected
|
||||
MaxPeers = 1 << 16 // maximum number of configured peers
|
||||
)
|
||||
|
||||
@@ -210,7 +210,7 @@ func (device *Device) Down() error {
|
||||
func (device *Device) IsUnderLoad() bool {
|
||||
// check if currently under load
|
||||
now := time.Now()
|
||||
underLoad := len(device.queue.handshake.c) >= UnderLoadQueueSize
|
||||
underLoad := len(device.queue.handshake.c) >= QueueHandshakeSize/8
|
||||
if underLoad {
|
||||
atomic.StoreInt64(&device.rate.underLoadUntil, now.Add(UnderLoadAfterTime).UnixNano())
|
||||
return true
|
||||
@@ -304,9 +304,9 @@ func NewDevice(tunDevice tun.Device, bind conn.Bind, logger *Logger) *Device {
|
||||
device.state.stopping.Wait()
|
||||
device.queue.encryption.wg.Add(cpus) // One for each RoutineHandshake
|
||||
for i := 0; i < cpus; i++ {
|
||||
go device.RoutineEncryption()
|
||||
go device.RoutineDecryption()
|
||||
go device.RoutineHandshake()
|
||||
go device.RoutineEncryption(i + 1)
|
||||
go device.RoutineDecryption(i + 1)
|
||||
go device.RoutineHandshake(i + 1)
|
||||
}
|
||||
|
||||
device.state.stopping.Add(1) // RoutineReadFromTUN
|
||||
|
||||
@@ -39,10 +39,3 @@ func (a *AtomicBool) Set(val bool) {
|
||||
}
|
||||
atomic.StoreInt32(&a.int32, flag)
|
||||
}
|
||||
|
||||
func min(a, b uint) uint {
|
||||
if a > b {
|
||||
return b
|
||||
}
|
||||
return a
|
||||
}
|
||||
|
||||
@@ -7,9 +7,7 @@ package device
|
||||
|
||||
import (
|
||||
"container/list"
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
"fmt"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
@@ -144,12 +142,29 @@ func (peer *Peer) SendBuffer(buffer []byte) error {
|
||||
}
|
||||
|
||||
func (peer *Peer) String() string {
|
||||
base64Key := base64.StdEncoding.EncodeToString(peer.handshake.remoteStatic[:])
|
||||
abbreviatedKey := "invalid"
|
||||
if len(base64Key) == 44 {
|
||||
abbreviatedKey = base64Key[0:4] + "…" + base64Key[39:43]
|
||||
// The awful goo that follows is identical to:
|
||||
//
|
||||
// base64Key := base64.StdEncoding.EncodeToString(peer.handshake.remoteStatic[:])
|
||||
// abbreviatedKey := base64Key[0:4] + "…" + base64Key[39:43]
|
||||
// return fmt.Sprintf("peer(%s)", abbreviatedKey)
|
||||
//
|
||||
// except that it is considerably more efficient.
|
||||
src := peer.handshake.remoteStatic
|
||||
b64 := func(input byte) byte {
|
||||
return input + 'A' + byte(((25-int(input))>>8)&6) - byte(((51-int(input))>>8)&75) - byte(((61-int(input))>>8)&15) + byte(((62-int(input))>>8)&3)
|
||||
}
|
||||
return fmt.Sprintf("peer(%s)", abbreviatedKey)
|
||||
b := []byte("peer(____…____)")
|
||||
const first = len("peer(")
|
||||
const second = len("peer(____…")
|
||||
b[first+0] = b64((src[0] >> 2) & 63)
|
||||
b[first+1] = b64(((src[0] << 4) | (src[1] >> 4)) & 63)
|
||||
b[first+2] = b64(((src[1] << 2) | (src[2] >> 6)) & 63)
|
||||
b[first+3] = b64(src[2] & 63)
|
||||
b[second+0] = b64(src[29] & 63)
|
||||
b[second+1] = b64((src[30] >> 2) & 63)
|
||||
b[second+2] = b64(((src[30] << 4) | (src[31] >> 4)) & 63)
|
||||
b[second+3] = b64((src[31] << 2) & 63)
|
||||
return string(b)
|
||||
}
|
||||
|
||||
func (peer *Peer) Start() {
|
||||
@@ -167,7 +182,7 @@ func (peer *Peer) Start() {
|
||||
}
|
||||
|
||||
device := peer.device
|
||||
device.log.Verbosef("%v - Starting...", peer)
|
||||
device.log.Verbosef("%v - Starting", peer)
|
||||
|
||||
// reset routine state
|
||||
peer.stopping.Wait()
|
||||
@@ -243,7 +258,7 @@ func (peer *Peer) Stop() {
|
||||
return
|
||||
}
|
||||
|
||||
peer.device.log.Verbosef("%v - Stopping...", peer)
|
||||
peer.device.log.Verbosef("%v - Stopping", peer)
|
||||
|
||||
peer.timersStop()
|
||||
// Signal that RoutineSequentialSender and RoutineSequentialReceiver should exit.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// +build !android,!ios,!windows
|
||||
//go:build !android && !ios && !windows
|
||||
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// +build ios
|
||||
//go:build ios
|
||||
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
@@ -7,13 +7,15 @@
|
||||
|
||||
package device
|
||||
|
||||
/* Fit within memory limits for iOS's Network Extension API, which has stricter requirements */
|
||||
|
||||
const (
|
||||
QueueStagedSize = 128
|
||||
QueueOutboundSize = 1024
|
||||
QueueInboundSize = 1024
|
||||
QueueHandshakeSize = 1024
|
||||
MaxSegmentSize = 1700
|
||||
PreallocatedBuffersPerPool = 1024
|
||||
// Fit within memory limits for iOS's Network Extension API, which has stricter requirements.
|
||||
// These are vars instead of consts, because heavier network extensions might want to reduce
|
||||
// them further.
|
||||
var (
|
||||
QueueStagedSize = 128
|
||||
QueueOutboundSize = 1024
|
||||
QueueInboundSize = 1024
|
||||
QueueHandshakeSize = 1024
|
||||
PreallocatedBuffersPerPool uint32 = 1024
|
||||
)
|
||||
|
||||
const MaxSegmentSize = 1700
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
//+build !race
|
||||
//go:build !race
|
||||
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
//+build race
|
||||
//go:build race
|
||||
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
|
||||
@@ -98,10 +98,10 @@ func (device *Device) RoutineReceiveIncoming(recv conn.ReceiveFunc) {
|
||||
if errors.Is(err, net.ErrClosed) {
|
||||
return
|
||||
}
|
||||
device.log.Verbosef("Failed to receive %s packet: %v", recvName, err)
|
||||
if neterr, ok := err.(net.Error); ok && !neterr.Temporary() {
|
||||
return
|
||||
}
|
||||
device.log.Errorf("Failed to receive packet: %v", err)
|
||||
if deathSpiral < 10 {
|
||||
deathSpiral++
|
||||
time.Sleep(time.Second / 3)
|
||||
@@ -203,11 +203,11 @@ func (device *Device) RoutineReceiveIncoming(recv conn.ReceiveFunc) {
|
||||
}
|
||||
}
|
||||
|
||||
func (device *Device) RoutineDecryption() {
|
||||
func (device *Device) RoutineDecryption(id int) {
|
||||
var nonce [chacha20poly1305.NonceSize]byte
|
||||
|
||||
defer device.log.Verbosef("Routine: decryption worker - stopped")
|
||||
device.log.Verbosef("Routine: decryption worker - started")
|
||||
defer device.log.Verbosef("Routine: decryption worker %d - stopped", id)
|
||||
device.log.Verbosef("Routine: decryption worker %d - started", id)
|
||||
|
||||
for elem := range device.queue.decryption.c {
|
||||
// split message into fields
|
||||
@@ -234,12 +234,12 @@ func (device *Device) RoutineDecryption() {
|
||||
|
||||
/* Handles incoming packets related to handshake
|
||||
*/
|
||||
func (device *Device) RoutineHandshake() {
|
||||
func (device *Device) RoutineHandshake(id int) {
|
||||
defer func() {
|
||||
device.log.Verbosef("Routine: handshake worker - stopped")
|
||||
device.log.Verbosef("Routine: handshake worker %d - stopped", id)
|
||||
device.queue.encryption.wg.Done()
|
||||
}()
|
||||
device.log.Verbosef("Routine: handshake worker - started")
|
||||
device.log.Verbosef("Routine: handshake worker %d - started", id)
|
||||
|
||||
for elem := range device.queue.handshake.c {
|
||||
|
||||
@@ -447,7 +447,7 @@ func (peer *Peer) RoutineSequentialReceiver() {
|
||||
}
|
||||
elem.packet = elem.packet[:length]
|
||||
src := elem.packet[IPv4offsetSrc : IPv4offsetSrc+net.IPv4len]
|
||||
if device.allowedips.LookupIPv4(src) != peer {
|
||||
if device.allowedips.Lookup(src) != peer {
|
||||
device.log.Verbosef("IPv4 packet with disallowed source address from %v", peer)
|
||||
goto skip
|
||||
}
|
||||
@@ -464,7 +464,7 @@ func (peer *Peer) RoutineSequentialReceiver() {
|
||||
}
|
||||
elem.packet = elem.packet[:length]
|
||||
src := elem.packet[IPv6offsetSrc : IPv6offsetSrc+net.IPv6len]
|
||||
if device.allowedips.LookupIPv6(src) != peer {
|
||||
if device.allowedips.Lookup(src) != peer {
|
||||
device.log.Verbosef("IPv6 packet with disallowed source address from %v", peer)
|
||||
goto skip
|
||||
}
|
||||
|
||||
@@ -8,7 +8,9 @@ package device
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"net"
|
||||
"os"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
@@ -227,7 +229,9 @@ func (device *Device) RoutineReadFromTUN() {
|
||||
|
||||
if err != nil {
|
||||
if !device.isClosed() {
|
||||
device.log.Errorf("Failed to read packet from TUN device: %v", err)
|
||||
if !errors.Is(err, os.ErrClosed) {
|
||||
device.log.Errorf("Failed to read packet from TUN device: %v", err)
|
||||
}
|
||||
go device.Close()
|
||||
}
|
||||
device.PutMessageBuffer(elem.buffer)
|
||||
@@ -250,14 +254,14 @@ func (device *Device) RoutineReadFromTUN() {
|
||||
continue
|
||||
}
|
||||
dst := elem.packet[IPv4offsetDst : IPv4offsetDst+net.IPv4len]
|
||||
peer = device.allowedips.LookupIPv4(dst)
|
||||
peer = device.allowedips.Lookup(dst)
|
||||
|
||||
case ipv6.Version:
|
||||
if len(elem.packet) < ipv6.HeaderLen {
|
||||
continue
|
||||
}
|
||||
dst := elem.packet[IPv6offsetDst : IPv6offsetDst+net.IPv6len]
|
||||
peer = device.allowedips.LookupIPv6(dst)
|
||||
peer = device.allowedips.Lookup(dst)
|
||||
|
||||
default:
|
||||
device.log.Verbosef("Received packet with unknown IP version")
|
||||
@@ -362,12 +366,12 @@ func calculatePaddingSize(packetSize, mtu int) int {
|
||||
*
|
||||
* Obs. One instance per core
|
||||
*/
|
||||
func (device *Device) RoutineEncryption() {
|
||||
func (device *Device) RoutineEncryption(id int) {
|
||||
var paddingZeros [PaddingMultiple]byte
|
||||
var nonce [chacha20poly1305.NonceSize]byte
|
||||
|
||||
defer device.log.Verbosef("Routine: encryption worker - stopped")
|
||||
device.log.Verbosef("Routine: encryption worker - started")
|
||||
defer device.log.Verbosef("Routine: encryption worker %d - stopped", id)
|
||||
device.log.Verbosef("Routine: encryption worker %d - started", id)
|
||||
|
||||
for elem := range device.queue.encryption.c {
|
||||
// populate header fields
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// +build !linux
|
||||
//go:build !linux
|
||||
|
||||
package device
|
||||
|
||||
|
||||
@@ -121,7 +121,7 @@ func (device *Device) IpcGetOperation(w io.Writer) error {
|
||||
sendf("rx_bytes=%d", atomic.LoadUint64(&peer.stats.rxBytes))
|
||||
sendf("persistent_keepalive_interval=%d", atomic.LoadUint32(&peer.persistentKeepaliveInterval))
|
||||
|
||||
device.allowedips.EntriesForPeer(peer, func(ip net.IP, cidr uint) bool {
|
||||
device.allowedips.EntriesForPeer(peer, func(ip net.IP, cidr uint8) bool {
|
||||
sendf("allowed_ip=%s/%d", ip.String(), cidr)
|
||||
return true
|
||||
})
|
||||
@@ -379,7 +379,7 @@ func (device *Device) handlePeerLine(peer *ipcSetPeer, key, value string) error
|
||||
return nil
|
||||
}
|
||||
ones, _ := network.Mask.Size()
|
||||
device.allowedips.Insert(network.IP, uint(ones), peer.Peer)
|
||||
device.allowedips.Insert(network.IP, uint8(ones), peer.Peer)
|
||||
|
||||
case "protocol_version":
|
||||
if value != "1" {
|
||||
|
||||
51
format_test.go
Normal file
51
format_test.go
Normal file
@@ -0,0 +1,51 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2021 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"go/format"
|
||||
"io/fs"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"sync"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestFormatting(t *testing.T) {
|
||||
var wg sync.WaitGroup
|
||||
filepath.WalkDir(".", func(path string, d fs.DirEntry, err error) error {
|
||||
if err != nil {
|
||||
t.Errorf("unable to walk %s: %v", path, err)
|
||||
return nil
|
||||
}
|
||||
if d.IsDir() || filepath.Ext(path) != ".go" {
|
||||
return nil
|
||||
}
|
||||
wg.Add(1)
|
||||
go func(path string) {
|
||||
defer wg.Done()
|
||||
src, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
t.Errorf("unable to read %s: %v", path, err)
|
||||
return
|
||||
}
|
||||
if runtime.GOOS == "windows" {
|
||||
src = bytes.ReplaceAll(src, []byte{'\r', '\n'}, []byte{'\n'})
|
||||
}
|
||||
formatted, err := format.Source(src)
|
||||
if err != nil {
|
||||
t.Errorf("unable to format %s: %v", path, err)
|
||||
return
|
||||
}
|
||||
if !bytes.Equal(src, formatted) {
|
||||
t.Errorf("unformatted code: %s", path)
|
||||
}
|
||||
}(path)
|
||||
return nil
|
||||
})
|
||||
wg.Wait()
|
||||
}
|
||||
8
go.mod
8
go.mod
@@ -1,9 +1,9 @@
|
||||
module golang.zx2c4.com/wireguard
|
||||
|
||||
go 1.16
|
||||
go 1.17
|
||||
|
||||
require (
|
||||
golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110
|
||||
golang.org/x/sys v0.0.0-20210309040221-94ec62e08169
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519
|
||||
golang.org/x/net v0.0.0-20210927181540-4e4d966f7476
|
||||
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6
|
||||
)
|
||||
|
||||
20
go.sum
20
go.sum
@@ -1,16 +1,14 @@
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83 h1:/ZScEX8SfEmUGRHs0gxpqteO5nfNW6axyZbBdw9A12g=
|
||||
golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110 h1:qWPm9rbaAMKs8Bq/9LRpbMqxWRVUAQwMI9fVrssnTfw=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 h1:7I4JAnoQBe7ZtJcBaYHi5UtiO8tQHbUSXxL+pnGRANg=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/net v0.0.0-20210927181540-4e4d966f7476 h1:s5hu7bTnLKswvidgtqc4GwsW83m9LZu8UAqzmWOZtI4=
|
||||
golang.org/x/net v0.0.0-20210927181540-4e4d966f7476/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210309040221-94ec62e08169 h1:fpeMGRM6A+XFcw4RPCO8s8hH7ppgrGR22pSIjwM7YUI=
|
||||
golang.org/x/sys v0.0.0-20210309040221-94ec62e08169/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6 h1:foEbQz/B0Oz6YIqu/69kfXPYeFQAuuMYFkjaqXzl5Wo=
|
||||
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// +build darwin freebsd openbsd
|
||||
//go:build darwin || freebsd || openbsd
|
||||
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// +build linux darwin freebsd openbsd
|
||||
//go:build linux || darwin || freebsd || openbsd
|
||||
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
|
||||
@@ -54,8 +54,7 @@ var UAPISecurityDescriptor *windows.SECURITY_DESCRIPTOR
|
||||
|
||||
func init() {
|
||||
var err error
|
||||
/* SDDL_DEVOBJ_SYS_ALL from the WDK */
|
||||
UAPISecurityDescriptor, err = windows.SecurityDescriptorFromString("O:SYD:P(A;;GA;;;SY)")
|
||||
UAPISecurityDescriptor, err = windows.SecurityDescriptorFromString("O:SYD:P(A;;GA;;;SY)(A;;GA;;;BA)S:(ML;;NWNRNX;;;HI)")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// +build windows
|
||||
//go:build windows
|
||||
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// +build windows
|
||||
//go:build windows
|
||||
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// +build windows
|
||||
//go:build windows
|
||||
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
|
||||
2
main.go
2
main.go
@@ -1,4 +1,4 @@
|
||||
// +build !windows
|
||||
//go:build !windows
|
||||
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
|
||||
@@ -1,24 +0,0 @@
|
||||
// +build !windows
|
||||
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2021 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package rwcancel
|
||||
|
||||
import "golang.org/x/sys/unix"
|
||||
|
||||
type fdSet struct {
|
||||
unix.FdSet
|
||||
}
|
||||
|
||||
func (fdset *fdSet) set(i int) {
|
||||
bits := 32 << (^uint(0) >> 63)
|
||||
fdset.Bits[i/bits] |= 1 << uint(i%bits)
|
||||
}
|
||||
|
||||
func (fdset *fdSet) check(i int) bool {
|
||||
bits := 32 << (^uint(0) >> 63)
|
||||
return (fdset.Bits[i/bits] & (1 << uint(i%bits))) != 0
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
// +build !windows
|
||||
//go:build !windows
|
||||
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
@@ -17,13 +17,6 @@ import (
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
func max(a, b int) int {
|
||||
if a > b {
|
||||
return a
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
type RWCancel struct {
|
||||
fd int
|
||||
closingReader *os.File
|
||||
@@ -50,13 +43,12 @@ func RetryAfterError(err error) bool {
|
||||
}
|
||||
|
||||
func (rw *RWCancel) ReadyRead() bool {
|
||||
closeFd := int(rw.closingReader.Fd())
|
||||
fdset := fdSet{}
|
||||
fdset.set(rw.fd)
|
||||
fdset.set(closeFd)
|
||||
closeFd := int32(rw.closingReader.Fd())
|
||||
|
||||
pollFds := []unix.PollFd{{Fd: int32(rw.fd), Events: unix.POLLIN}, {Fd: closeFd, Events: unix.POLLIN}}
|
||||
var err error
|
||||
for {
|
||||
err = unixSelect(max(rw.fd, closeFd)+1, &fdset.FdSet, nil, nil, nil)
|
||||
_, err = unix.Poll(pollFds, -1)
|
||||
if err == nil || !RetryAfterError(err) {
|
||||
break
|
||||
}
|
||||
@@ -64,20 +56,18 @@ func (rw *RWCancel) ReadyRead() bool {
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
if fdset.check(closeFd) {
|
||||
if pollFds[1].Revents != 0 {
|
||||
return false
|
||||
}
|
||||
return fdset.check(rw.fd)
|
||||
return pollFds[0].Revents != 0
|
||||
}
|
||||
|
||||
func (rw *RWCancel) ReadyWrite() bool {
|
||||
closeFd := int(rw.closingReader.Fd())
|
||||
fdset := fdSet{}
|
||||
fdset.set(rw.fd)
|
||||
fdset.set(closeFd)
|
||||
closeFd := int32(rw.closingReader.Fd())
|
||||
pollFds := []unix.PollFd{{Fd: int32(rw.fd), Events: unix.POLLOUT}, {Fd: closeFd, Events: unix.POLLOUT}}
|
||||
var err error
|
||||
for {
|
||||
err = unixSelect(max(rw.fd, closeFd)+1, nil, &fdset.FdSet, nil, nil)
|
||||
_, err = unix.Poll(pollFds, -1)
|
||||
if err == nil || !RetryAfterError(err) {
|
||||
break
|
||||
}
|
||||
@@ -85,10 +75,11 @@ func (rw *RWCancel) ReadyWrite() bool {
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
if fdset.check(closeFd) {
|
||||
|
||||
if pollFds[1].Revents != 0 {
|
||||
return false
|
||||
}
|
||||
return fdset.check(rw.fd)
|
||||
return pollFds[0].Revents != 0
|
||||
}
|
||||
|
||||
func (rw *RWCancel) Read(p []byte) (n int, err error) {
|
||||
@@ -98,7 +89,7 @@ func (rw *RWCancel) Read(p []byte) (n int, err error) {
|
||||
return n, err
|
||||
}
|
||||
if !rw.ReadyRead() {
|
||||
return 0, errors.New("fd closed")
|
||||
return 0, os.ErrClosed
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -110,7 +101,7 @@ func (rw *RWCancel) Write(p []byte) (n int, err error) {
|
||||
return n, err
|
||||
}
|
||||
if !rw.ReadyWrite() {
|
||||
return 0, errors.New("fd closed")
|
||||
return 0, os.ErrClosed
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
// +build !linux,!windows
|
||||
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2021 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package rwcancel
|
||||
|
||||
import "golang.org/x/sys/unix"
|
||||
|
||||
func unixSelect(nfd int, r *unix.FdSet, w *unix.FdSet, e *unix.FdSet, timeout *unix.Timeval) error {
|
||||
_, err := unix.Select(nfd, r, w, e, timeout)
|
||||
return err
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2021 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package rwcancel
|
||||
|
||||
import "golang.org/x/sys/unix"
|
||||
|
||||
func unixSelect(nfd int, r *unix.FdSet, w *unix.FdSet, e *unix.FdSet, timeout *unix.Timeval) (err error) {
|
||||
_, err = unix.Select(nfd, r, w, e, timeout)
|
||||
return
|
||||
}
|
||||
67
tun/alignment_windows_test.go
Normal file
67
tun/alignment_windows_test.go
Normal file
@@ -0,0 +1,67 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2021 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package tun
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
func checkAlignment(t *testing.T, name string, offset uintptr) {
|
||||
t.Helper()
|
||||
if offset%8 != 0 {
|
||||
t.Errorf("offset of %q within struct is %d bytes, which does not align to 64-bit word boundaries (missing %d bytes). Atomic operations will crash on 32-bit systems.", name, offset, 8-(offset%8))
|
||||
}
|
||||
}
|
||||
|
||||
// TestRateJugglerAlignment checks that atomically-accessed fields are
|
||||
// aligned to 64-bit boundaries, as required by the atomic package.
|
||||
//
|
||||
// Unfortunately, violating this rule on 32-bit platforms results in a
|
||||
// hard segfault at runtime.
|
||||
func TestRateJugglerAlignment(t *testing.T) {
|
||||
var r rateJuggler
|
||||
|
||||
typ := reflect.TypeOf(&r).Elem()
|
||||
t.Logf("Peer type size: %d, with fields:", typ.Size())
|
||||
for i := 0; i < typ.NumField(); i++ {
|
||||
field := typ.Field(i)
|
||||
t.Logf("\t%30s\toffset=%3v\t(type size=%3d, align=%d)",
|
||||
field.Name,
|
||||
field.Offset,
|
||||
field.Type.Size(),
|
||||
field.Type.Align(),
|
||||
)
|
||||
}
|
||||
|
||||
checkAlignment(t, "rateJuggler.current", unsafe.Offsetof(r.current))
|
||||
checkAlignment(t, "rateJuggler.nextByteCount", unsafe.Offsetof(r.nextByteCount))
|
||||
checkAlignment(t, "rateJuggler.nextStartTime", unsafe.Offsetof(r.nextStartTime))
|
||||
}
|
||||
|
||||
// TestNativeTunAlignment checks that atomically-accessed fields are
|
||||
// aligned to 64-bit boundaries, as required by the atomic package.
|
||||
//
|
||||
// Unfortunately, violating this rule on 32-bit platforms results in a
|
||||
// hard segfault at runtime.
|
||||
func TestNativeTunAlignment(t *testing.T) {
|
||||
var tun NativeTun
|
||||
|
||||
typ := reflect.TypeOf(&tun).Elem()
|
||||
t.Logf("Peer type size: %d, with fields:", typ.Size())
|
||||
for i := 0; i < typ.NumField(); i++ {
|
||||
field := typ.Field(i)
|
||||
t.Logf("\t%30s\toffset=%3v\t(type size=%3d, align=%d)",
|
||||
field.Name,
|
||||
field.Offset,
|
||||
field.Type.Size(),
|
||||
field.Type.Align(),
|
||||
)
|
||||
}
|
||||
|
||||
checkAlignment(t, "NativeTun.rate", unsafe.Offsetof(tun.rate))
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
// +build ignore
|
||||
//go:build ignore
|
||||
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// +build ignore
|
||||
//go:build ignore
|
||||
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
|
||||
@@ -3,9 +3,9 @@ module golang.zx2c4.com/wireguard/tun/netstack
|
||||
go 1.16
|
||||
|
||||
require (
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110
|
||||
golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b // indirect
|
||||
golang.org/x/net v0.0.0-20210423184538-5f58ad60dda6
|
||||
golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7 // indirect
|
||||
golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba // indirect
|
||||
golang.zx2c4.com/wireguard v0.0.0-20210306154438-593658d9755b
|
||||
gvisor.dev/gvisor v0.0.0-20210306005637-a64c3a1b5a9f
|
||||
golang.zx2c4.com/wireguard v0.0.0-20210424170727-c9db4b7aaa22
|
||||
gvisor.dev/gvisor v0.0.0-20210506004418-fbfeba3024f0
|
||||
)
|
||||
|
||||
@@ -112,7 +112,7 @@ github.com/go-openapi/jsonreference v0.0.0-20160704190145-13c6e3589ad9/go.mod h1
|
||||
github.com/go-openapi/spec v0.0.0-20160808142527-6aced65f8501/go.mod h1:J8+jY1nAiCcj+friV/PDoE1/3eeccG9LYBs0tYvLOWc=
|
||||
github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod h1:DXUve3Dpr1UfpPtxFw+EFuQ41HhCWZfha5jSVRG7C7I=
|
||||
github.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||
github.com/gofrs/flock v0.6.1-0.20180915234121-886344bea079/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU=
|
||||
github.com/gofrs/flock v0.8.0/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU=
|
||||
github.com/gogo/googleapis v1.4.0/go.mod h1:5YRNX2z1oM5gXdAkurHa942MDgEJyk02w4OecKY87+c=
|
||||
github.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
|
||||
github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
|
||||
@@ -344,9 +344,9 @@ golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81R
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210224082022-3d97a244fca7/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110 h1:qWPm9rbaAMKs8Bq/9LRpbMqxWRVUAQwMI9fVrssnTfw=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210423184538-5f58ad60dda6 h1:0PC75Fz/kyMGhL0e1QnypqK2kQMqKt9csD1GnMJR+Zk=
|
||||
golang.org/x/net v0.0.0-20210423184538-5f58ad60dda6/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
@@ -409,9 +409,10 @@ golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210225014209-683adc9d29d7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b h1:ggRgirZABFolTmi3sn6Ivd9SipZwLedQ5wR0aAKnFxU=
|
||||
golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210309040221-94ec62e08169/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7 h1:iGu644GcxtEcrInvDsQRCwJjtCIOlT2V7IRt6ah2Whw=
|
||||
golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
@@ -421,6 +422,7 @@ golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
@@ -477,8 +479,8 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.zx2c4.com/wireguard v0.0.0-20210306154438-593658d9755b h1:DaVhJ+qTYKBJ4onaAbkkMnAW7xS8BD6NuG7lbloDZ0Y=
|
||||
golang.zx2c4.com/wireguard v0.0.0-20210306154438-593658d9755b/go.mod h1:39ZQQ95hUxDxT7opsWy/rtfgvXXc8s30qfZ02df69Fo=
|
||||
golang.zx2c4.com/wireguard v0.0.0-20210424170727-c9db4b7aaa22 h1:ytS28bw9HtZVDRMDxviC6ryCJuccw+zXhh04u2IRWJw=
|
||||
golang.zx2c4.com/wireguard v0.0.0-20210424170727-c9db4b7aaa22/go.mod h1:a057zjmoc00UN7gVkaJt2sXVK523kMJcogDTEvPIasg=
|
||||
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
|
||||
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
|
||||
google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
|
||||
@@ -576,8 +578,8 @@ gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
|
||||
gvisor.dev/gvisor v0.0.0-20210306005637-a64c3a1b5a9f h1:pROeAG1eh1j7CxnDx6Lft4cPoXtNLhjePTlgrssJSM4=
|
||||
gvisor.dev/gvisor v0.0.0-20210306005637-a64c3a1b5a9f/go.mod h1:TMDExUbPoyZXybG8K1+vm5ElIxI5qXUM5fwK7vI8YIg=
|
||||
gvisor.dev/gvisor v0.0.0-20210506004418-fbfeba3024f0 h1:Ny53d6x76f7EgvYe7pi8SQcIzdRM69y1ckQ8rRtT6ww=
|
||||
gvisor.dev/gvisor v0.0.0-20210506004418-fbfeba3024f0/go.mod h1:ucHEMlckp+S/YzKEpwwAyGBhAh807Wxq/8Erc6gFxCE=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
|
||||
@@ -74,12 +74,12 @@ func (*endpoint) LinkAddress() tcpip.LinkAddress {
|
||||
|
||||
func (*endpoint) Wait() {}
|
||||
|
||||
func (e *endpoint) WritePacket(_ stack.RouteInfo, _ *stack.GSO, _ tcpip.NetworkProtocolNumber, pkt *stack.PacketBuffer) tcpip.Error {
|
||||
func (e *endpoint) WritePacket(_ stack.RouteInfo, _ tcpip.NetworkProtocolNumber, pkt *stack.PacketBuffer) tcpip.Error {
|
||||
e.incomingPacket <- buffer.NewVectorisedView(pkt.Size(), pkt.Views())
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *endpoint) WritePackets(stack.RouteInfo, *stack.GSO, stack.PacketBufferList, tcpip.NetworkProtocolNumber) (int, tcpip.Error) {
|
||||
func (e *endpoint) WritePackets(stack.RouteInfo, stack.PacketBufferList, tcpip.NetworkProtocolNumber) (int, tcpip.Error) {
|
||||
panic("not implemented")
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// +build !windows
|
||||
//go:build darwin || freebsd
|
||||
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
|
||||
@@ -108,7 +108,6 @@ func CreateTUN(name string, mtu int) (Device, error) {
|
||||
}
|
||||
|
||||
fd, err := unix.Socket(unix.AF_SYSTEM, unix.SOCK_DGRAM, 2)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -117,6 +116,7 @@ func CreateTUN(name string, mtu int) (Device, error) {
|
||||
copy(ctlInfo.Name[:], []byte(utunControlName))
|
||||
err = unix.IoctlCtlInfo(fd, ctlInfo)
|
||||
if err != nil {
|
||||
unix.Close(fd)
|
||||
return nil, fmt.Errorf("IoctlGetCtlInfo: %w", err)
|
||||
}
|
||||
|
||||
@@ -127,11 +127,13 @@ func CreateTUN(name string, mtu int) (Device, error) {
|
||||
|
||||
err = unix.Connect(fd, sc)
|
||||
if err != nil {
|
||||
unix.Close(fd)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = syscall.SetNonblock(fd, true)
|
||||
err = unix.SetNonblock(fd, true)
|
||||
if err != nil {
|
||||
unix.Close(fd)
|
||||
return nil, err
|
||||
}
|
||||
tun, err := CreateTUNFromFile(os.NewFile(uintptr(fd), ""), mtu)
|
||||
|
||||
@@ -100,10 +100,10 @@ func (tun *NativeTun) routineRouteListener(tunIfindex int) {
|
||||
continue
|
||||
}
|
||||
|
||||
if data[3 /* type */ ] != unix.RTM_IFINFO {
|
||||
if data[3 /* type */] != unix.RTM_IFINFO {
|
||||
continue
|
||||
}
|
||||
ifindex := int(*(*uint16)(unsafe.Pointer(&data[12 /* ifindex */ ])))
|
||||
ifindex := int(*(*uint16)(unsafe.Pointer(&data[12 /* ifindex */])))
|
||||
if ifindex != tunIfindex {
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ package tun
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"sync"
|
||||
@@ -329,32 +330,30 @@ func (tun *NativeTun) nameSlow() (string, error) {
|
||||
return string(name), nil
|
||||
}
|
||||
|
||||
func (tun *NativeTun) Write(buff []byte, offset int) (int, error) {
|
||||
|
||||
func (tun *NativeTun) Write(buf []byte, offset int) (int, error) {
|
||||
if tun.nopi {
|
||||
buff = buff[offset:]
|
||||
buf = buf[offset:]
|
||||
} else {
|
||||
// reserve space for header
|
||||
|
||||
buff = buff[offset-4:]
|
||||
buf = buf[offset-4:]
|
||||
|
||||
// add packet information header
|
||||
|
||||
buff[0] = 0x00
|
||||
buff[1] = 0x00
|
||||
|
||||
if buff[4]>>4 == ipv6.Version {
|
||||
buff[2] = 0x86
|
||||
buff[3] = 0xdd
|
||||
buf[0] = 0x00
|
||||
buf[1] = 0x00
|
||||
if buf[4]>>4 == ipv6.Version {
|
||||
buf[2] = 0x86
|
||||
buf[3] = 0xdd
|
||||
} else {
|
||||
buff[2] = 0x08
|
||||
buff[3] = 0x00
|
||||
buf[2] = 0x08
|
||||
buf[3] = 0x00
|
||||
}
|
||||
}
|
||||
|
||||
// write
|
||||
|
||||
return tun.tunFile.Write(buff)
|
||||
n, err := tun.tunFile.Write(buf)
|
||||
if errors.Is(err, syscall.EBADFD) {
|
||||
err = os.ErrClosed
|
||||
}
|
||||
return n, err
|
||||
}
|
||||
|
||||
func (tun *NativeTun) Flush() error {
|
||||
@@ -362,22 +361,26 @@ func (tun *NativeTun) Flush() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (tun *NativeTun) Read(buff []byte, offset int) (int, error) {
|
||||
func (tun *NativeTun) Read(buf []byte, offset int) (n int, err error) {
|
||||
select {
|
||||
case err := <-tun.errors:
|
||||
return 0, err
|
||||
case err = <-tun.errors:
|
||||
default:
|
||||
if tun.nopi {
|
||||
return tun.tunFile.Read(buff[offset:])
|
||||
n, err = tun.tunFile.Read(buf[offset:])
|
||||
} else {
|
||||
buff := buff[offset-4:]
|
||||
n, err := tun.tunFile.Read(buff[:])
|
||||
if n < 4 {
|
||||
return 0, err
|
||||
buff := buf[offset-4:]
|
||||
n, err = tun.tunFile.Read(buff[:])
|
||||
if errors.Is(err, syscall.EBADFD) {
|
||||
err = os.ErrClosed
|
||||
}
|
||||
if n < 4 {
|
||||
n = 0
|
||||
} else {
|
||||
n -= 4
|
||||
}
|
||||
return n - 4, err
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (tun *NativeTun) Events() chan Event {
|
||||
@@ -416,6 +419,7 @@ func CreateTUN(name string, mtu int) (Device, error) {
|
||||
var flags uint16 = unix.IFF_TUN // | unix.IFF_NO_PI (disabled for TUN status hack)
|
||||
nameBytes := []byte(name)
|
||||
if len(nameBytes) >= unix.IFNAMSIZ {
|
||||
unix.Close(nfd)
|
||||
return nil, fmt.Errorf("interface name too long: %w", unix.ENAMETOOLONG)
|
||||
}
|
||||
copy(ifr[:], nameBytes)
|
||||
@@ -428,17 +432,19 @@ func CreateTUN(name string, mtu int) (Device, error) {
|
||||
uintptr(unsafe.Pointer(&ifr[0])),
|
||||
)
|
||||
if errno != 0 {
|
||||
unix.Close(nfd)
|
||||
return nil, errno
|
||||
}
|
||||
|
||||
err = unix.SetNonblock(nfd, true)
|
||||
if err != nil {
|
||||
unix.Close(nfd)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Note that the above -- open,ioctl,nonblock -- must happen prior to handing it to netpoll as below this line.
|
||||
|
||||
fd := os.NewFile(uintptr(nfd), cloneDevicePath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return CreateTUNFromFile(fd, mtu)
|
||||
}
|
||||
|
||||
|
||||
@@ -8,7 +8,6 @@ package tun
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
@@ -35,18 +34,19 @@ type rateJuggler struct {
|
||||
|
||||
type NativeTun struct {
|
||||
wt *wintun.Adapter
|
||||
name string
|
||||
handle windows.Handle
|
||||
close bool
|
||||
events chan Event
|
||||
errors chan error
|
||||
forcedMTU int
|
||||
rate rateJuggler
|
||||
session wintun.Session
|
||||
readWait windows.Handle
|
||||
events chan Event
|
||||
running sync.WaitGroup
|
||||
closeOnce sync.Once
|
||||
close int32
|
||||
forcedMTU int
|
||||
}
|
||||
|
||||
var WintunPool, _ = wintun.MakePool("WireGuard")
|
||||
var WintunTunnelType = "WireGuard"
|
||||
var WintunStaticRequestedGUID *windows.GUID
|
||||
|
||||
//go:linkname procyield runtime.procyield
|
||||
@@ -68,25 +68,10 @@ func CreateTUN(ifname string, mtu int) (Device, error) {
|
||||
// a requested GUID. Should a Wintun interface with the same name exist, it is reused.
|
||||
//
|
||||
func CreateTUNWithRequestedGUID(ifname string, requestedGUID *windows.GUID, mtu int) (Device, error) {
|
||||
var err error
|
||||
var wt *wintun.Adapter
|
||||
|
||||
// Does an interface with this name already exist?
|
||||
wt, err = WintunPool.OpenAdapter(ifname)
|
||||
if err == nil {
|
||||
// If so, we delete it, in case it has weird residual configuration.
|
||||
_, err = wt.Delete(true)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Error deleting already existing interface: %w", err)
|
||||
}
|
||||
}
|
||||
wt, rebootRequired, err := WintunPool.CreateAdapter(ifname, requestedGUID)
|
||||
wt, err := wintun.CreateAdapter(ifname, WintunTunnelType, requestedGUID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Error creating interface: %w", err)
|
||||
}
|
||||
if rebootRequired {
|
||||
log.Println("Windows indicated a reboot is required.")
|
||||
}
|
||||
|
||||
forcedMTU := 1420
|
||||
if mtu > 0 {
|
||||
@@ -95,15 +80,15 @@ func CreateTUNWithRequestedGUID(ifname string, requestedGUID *windows.GUID, mtu
|
||||
|
||||
tun := &NativeTun{
|
||||
wt: wt,
|
||||
name: ifname,
|
||||
handle: windows.InvalidHandle,
|
||||
events: make(chan Event, 10),
|
||||
errors: make(chan error, 1),
|
||||
forcedMTU: forcedMTU,
|
||||
}
|
||||
|
||||
tun.session, err = wt.StartSession(0x800000) // Ring capacity, 8 MiB
|
||||
if err != nil {
|
||||
tun.wt.Delete(false)
|
||||
tun.wt.Close()
|
||||
close(tun.events)
|
||||
return nil, fmt.Errorf("Error starting session: %w", err)
|
||||
}
|
||||
@@ -112,7 +97,7 @@ func CreateTUNWithRequestedGUID(ifname string, requestedGUID *windows.GUID, mtu
|
||||
}
|
||||
|
||||
func (tun *NativeTun) Name() (string, error) {
|
||||
return tun.wt.Name()
|
||||
return tun.name, nil
|
||||
}
|
||||
|
||||
func (tun *NativeTun) File() *os.File {
|
||||
@@ -126,10 +111,12 @@ func (tun *NativeTun) Events() chan Event {
|
||||
func (tun *NativeTun) Close() error {
|
||||
var err error
|
||||
tun.closeOnce.Do(func() {
|
||||
tun.close = true
|
||||
atomic.StoreInt32(&tun.close, 1)
|
||||
windows.SetEvent(tun.readWait)
|
||||
tun.running.Wait()
|
||||
tun.session.End()
|
||||
if tun.wt != nil {
|
||||
_, err = tun.wt.Delete(false)
|
||||
tun.wt.Close()
|
||||
}
|
||||
close(tun.events)
|
||||
})
|
||||
@@ -142,22 +129,26 @@ func (tun *NativeTun) MTU() (int, error) {
|
||||
|
||||
// TODO: This is a temporary hack. We really need to be monitoring the interface in real time and adapting to MTU changes.
|
||||
func (tun *NativeTun) ForceMTU(mtu int) {
|
||||
update := tun.forcedMTU != mtu
|
||||
tun.forcedMTU = mtu
|
||||
if update {
|
||||
tun.events <- EventMTUUpdate
|
||||
}
|
||||
}
|
||||
|
||||
// Note: Read() and Write() assume the caller comes only from a single thread; there's no locking.
|
||||
|
||||
func (tun *NativeTun) Read(buff []byte, offset int) (int, error) {
|
||||
tun.running.Add(1)
|
||||
defer tun.running.Done()
|
||||
retry:
|
||||
select {
|
||||
case err := <-tun.errors:
|
||||
return 0, err
|
||||
default:
|
||||
if atomic.LoadInt32(&tun.close) == 1 {
|
||||
return 0, os.ErrClosed
|
||||
}
|
||||
start := nanotime()
|
||||
shouldSpin := atomic.LoadUint64(&tun.rate.current) >= spinloopRateThreshold && uint64(start-atomic.LoadInt64(&tun.rate.nextStartTime)) <= rateMeasurementGranularity*2
|
||||
for {
|
||||
if tun.close {
|
||||
if atomic.LoadInt32(&tun.close) == 1 {
|
||||
return 0, os.ErrClosed
|
||||
}
|
||||
packet, err := tun.session.ReceivePacket()
|
||||
@@ -189,7 +180,9 @@ func (tun *NativeTun) Flush() error {
|
||||
}
|
||||
|
||||
func (tun *NativeTun) Write(buff []byte, offset int) (int, error) {
|
||||
if tun.close {
|
||||
tun.running.Add(1)
|
||||
defer tun.running.Done()
|
||||
if atomic.LoadInt32(&tun.close) == 1 {
|
||||
return 0, os.ErrClosed
|
||||
}
|
||||
|
||||
@@ -213,6 +206,11 @@ func (tun *NativeTun) Write(buff []byte, offset int) (int, error) {
|
||||
|
||||
// LUID returns Windows interface instance ID.
|
||||
func (tun *NativeTun) LUID() uint64 {
|
||||
tun.running.Add(1)
|
||||
defer tun.running.Done()
|
||||
if atomic.LoadInt32(&tun.close) == 1 {
|
||||
return 0
|
||||
}
|
||||
return tun.wt.LUID()
|
||||
}
|
||||
|
||||
|
||||
@@ -1,54 +0,0 @@
|
||||
// +build !load_wintun_from_rsrc
|
||||
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2019 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package wintun
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
type lazyDLL struct {
|
||||
Name string
|
||||
mu sync.Mutex
|
||||
module windows.Handle
|
||||
onLoad func(d *lazyDLL)
|
||||
}
|
||||
|
||||
func (d *lazyDLL) Load() error {
|
||||
if atomic.LoadPointer((*unsafe.Pointer)(unsafe.Pointer(&d.module))) != nil {
|
||||
return nil
|
||||
}
|
||||
d.mu.Lock()
|
||||
defer d.mu.Unlock()
|
||||
if d.module != 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
const (
|
||||
LOAD_LIBRARY_SEARCH_APPLICATION_DIR = 0x00000200
|
||||
LOAD_LIBRARY_SEARCH_SYSTEM32 = 0x00000800
|
||||
)
|
||||
module, err := windows.LoadLibraryEx(d.Name, 0, LOAD_LIBRARY_SEARCH_APPLICATION_DIR|LOAD_LIBRARY_SEARCH_SYSTEM32)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Unable to load library: %w", err)
|
||||
}
|
||||
|
||||
atomic.StorePointer((*unsafe.Pointer)(unsafe.Pointer(&d.module)), unsafe.Pointer(module))
|
||||
if d.onLoad != nil {
|
||||
d.onLoad(d)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *lazyProc) nameToAddr() (uintptr, error) {
|
||||
return windows.GetProcAddress(p.dll.module, p.Name)
|
||||
}
|
||||
@@ -1,61 +0,0 @@
|
||||
// +build load_wintun_from_rsrc
|
||||
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2019 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package wintun
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sys/windows"
|
||||
|
||||
"golang.zx2c4.com/wireguard/tun/wintun/memmod"
|
||||
)
|
||||
|
||||
type lazyDLL struct {
|
||||
Name string
|
||||
mu sync.Mutex
|
||||
module *memmod.Module
|
||||
onLoad func(d *lazyDLL)
|
||||
}
|
||||
|
||||
func (d *lazyDLL) Load() error {
|
||||
if atomic.LoadPointer((*unsafe.Pointer)(unsafe.Pointer(&d.module))) != nil {
|
||||
return nil
|
||||
}
|
||||
d.mu.Lock()
|
||||
defer d.mu.Unlock()
|
||||
if d.module != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
const ourModule windows.Handle = 0
|
||||
resInfo, err := windows.FindResource(ourModule, d.Name, windows.RT_RCDATA)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Unable to find \"%v\" RCDATA resource: %w", d.Name, err)
|
||||
}
|
||||
data, err := windows.LoadResourceData(ourModule, resInfo)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Unable to load resource: %w", err)
|
||||
}
|
||||
module, err := memmod.LoadLibrary(data)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Unable to load library: %w", err)
|
||||
}
|
||||
|
||||
atomic.StorePointer((*unsafe.Pointer)(unsafe.Pointer(&d.module)), unsafe.Pointer(module))
|
||||
if d.onLoad != nil {
|
||||
d.onLoad(d)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *lazyProc) nameToAddr() (uintptr, error) {
|
||||
return p.dll.module.ProcAddressByName(p.Name)
|
||||
}
|
||||
@@ -10,6 +10,8 @@ import (
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
func newLazyDLL(name string, onLoad func(d *lazyDLL)) *lazyDLL {
|
||||
@@ -57,3 +59,40 @@ func (p *lazyProc) Addr() uintptr {
|
||||
}
|
||||
return p.addr
|
||||
}
|
||||
|
||||
type lazyDLL struct {
|
||||
Name string
|
||||
mu sync.Mutex
|
||||
module windows.Handle
|
||||
onLoad func(d *lazyDLL)
|
||||
}
|
||||
|
||||
func (d *lazyDLL) Load() error {
|
||||
if atomic.LoadPointer((*unsafe.Pointer)(unsafe.Pointer(&d.module))) != nil {
|
||||
return nil
|
||||
}
|
||||
d.mu.Lock()
|
||||
defer d.mu.Unlock()
|
||||
if d.module != 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
const (
|
||||
LOAD_LIBRARY_SEARCH_APPLICATION_DIR = 0x00000200
|
||||
LOAD_LIBRARY_SEARCH_SYSTEM32 = 0x00000800
|
||||
)
|
||||
module, err := windows.LoadLibraryEx(d.Name, 0, LOAD_LIBRARY_SEARCH_APPLICATION_DIR|LOAD_LIBRARY_SEARCH_SYSTEM32)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Unable to load library: %w", err)
|
||||
}
|
||||
|
||||
atomic.StorePointer((*unsafe.Pointer)(unsafe.Pointer(&d.module)), unsafe.Pointer(module))
|
||||
if d.onLoad != nil {
|
||||
d.onLoad(d)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *lazyProc) nameToAddr() (uintptr, error) {
|
||||
return windows.GetProcAddress(p.dll.module, p.Name)
|
||||
}
|
||||
|
||||
@@ -1,620 +0,0 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2021 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package memmod
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
type addressList struct {
|
||||
next *addressList
|
||||
address uintptr
|
||||
}
|
||||
|
||||
func (head *addressList) free() {
|
||||
for node := head; node != nil; node = node.next {
|
||||
windows.VirtualFree(node.address, 0, windows.MEM_RELEASE)
|
||||
}
|
||||
}
|
||||
|
||||
type Module struct {
|
||||
headers *IMAGE_NT_HEADERS
|
||||
codeBase uintptr
|
||||
modules []windows.Handle
|
||||
initialized bool
|
||||
isDLL bool
|
||||
isRelocated bool
|
||||
nameExports map[string]uint16
|
||||
entry uintptr
|
||||
blockedMemory *addressList
|
||||
}
|
||||
|
||||
func (module *Module) headerDirectory(idx int) *IMAGE_DATA_DIRECTORY {
|
||||
return &module.headers.OptionalHeader.DataDirectory[idx]
|
||||
}
|
||||
|
||||
func (module *Module) copySections(address uintptr, size uintptr, old_headers *IMAGE_NT_HEADERS) error {
|
||||
sections := module.headers.Sections()
|
||||
for i := range sections {
|
||||
if sections[i].SizeOfRawData == 0 {
|
||||
// Section doesn't contain data in the dll itself, but may define uninitialized data.
|
||||
sectionSize := old_headers.OptionalHeader.SectionAlignment
|
||||
if sectionSize == 0 {
|
||||
continue
|
||||
}
|
||||
dest, err := windows.VirtualAlloc(module.codeBase+uintptr(sections[i].VirtualAddress),
|
||||
uintptr(sectionSize),
|
||||
windows.MEM_COMMIT,
|
||||
windows.PAGE_READWRITE)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error allocating section: %w", err)
|
||||
}
|
||||
|
||||
// Always use position from file to support alignments smaller than page size (allocation above will align to page size).
|
||||
dest = module.codeBase + uintptr(sections[i].VirtualAddress)
|
||||
// NOTE: On 64bit systems we truncate to 32bit here but expand again later when "PhysicalAddress" is used.
|
||||
sections[i].SetPhysicalAddress((uint32)(dest & 0xffffffff))
|
||||
var dst []byte
|
||||
unsafeSlice(unsafe.Pointer(&dst), a2p(dest), int(sectionSize))
|
||||
for j := range dst {
|
||||
dst[j] = 0
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
if size < uintptr(sections[i].PointerToRawData+sections[i].SizeOfRawData) {
|
||||
return errors.New("Incomplete section")
|
||||
}
|
||||
|
||||
// Commit memory block and copy data from dll.
|
||||
dest, err := windows.VirtualAlloc(module.codeBase+uintptr(sections[i].VirtualAddress),
|
||||
uintptr(sections[i].SizeOfRawData),
|
||||
windows.MEM_COMMIT,
|
||||
windows.PAGE_READWRITE)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error allocating memory block: %w", err)
|
||||
}
|
||||
|
||||
// Always use position from file to support alignments smaller than page size (allocation above will align to page size).
|
||||
memcpy(
|
||||
module.codeBase+uintptr(sections[i].VirtualAddress),
|
||||
address+uintptr(sections[i].PointerToRawData),
|
||||
uintptr(sections[i].SizeOfRawData))
|
||||
// NOTE: On 64bit systems we truncate to 32bit here but expand again later when "PhysicalAddress" is used.
|
||||
sections[i].SetPhysicalAddress((uint32)(dest & 0xffffffff))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (module *Module) realSectionSize(section *IMAGE_SECTION_HEADER) uintptr {
|
||||
size := section.SizeOfRawData
|
||||
if size != 0 {
|
||||
return uintptr(size)
|
||||
}
|
||||
if (section.Characteristics & IMAGE_SCN_CNT_INITIALIZED_DATA) != 0 {
|
||||
return uintptr(module.headers.OptionalHeader.SizeOfInitializedData)
|
||||
}
|
||||
if (section.Characteristics & IMAGE_SCN_CNT_UNINITIALIZED_DATA) != 0 {
|
||||
return uintptr(module.headers.OptionalHeader.SizeOfUninitializedData)
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
type sectionFinalizeData struct {
|
||||
address uintptr
|
||||
alignedAddress uintptr
|
||||
size uintptr
|
||||
characteristics uint32
|
||||
last bool
|
||||
}
|
||||
|
||||
func (module *Module) finalizeSection(sectionData *sectionFinalizeData) error {
|
||||
if sectionData.size == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
if (sectionData.characteristics & IMAGE_SCN_MEM_DISCARDABLE) != 0 {
|
||||
// Section is not needed any more and can safely be freed.
|
||||
if sectionData.address == sectionData.alignedAddress &&
|
||||
(sectionData.last ||
|
||||
(sectionData.size%uintptr(module.headers.OptionalHeader.SectionAlignment)) == 0) {
|
||||
// Only allowed to decommit whole pages.
|
||||
windows.VirtualFree(sectionData.address, sectionData.size, windows.MEM_DECOMMIT)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// determine protection flags based on characteristics
|
||||
var ProtectionFlags = [8]uint32{
|
||||
windows.PAGE_NOACCESS, // not writeable, not readable, not executable
|
||||
windows.PAGE_EXECUTE, // not writeable, not readable, executable
|
||||
windows.PAGE_READONLY, // not writeable, readable, not executable
|
||||
windows.PAGE_EXECUTE_READ, // not writeable, readable, executable
|
||||
windows.PAGE_WRITECOPY, // writeable, not readable, not executable
|
||||
windows.PAGE_EXECUTE_WRITECOPY, // writeable, not readable, executable
|
||||
windows.PAGE_READWRITE, // writeable, readable, not executable
|
||||
windows.PAGE_EXECUTE_READWRITE, // writeable, readable, executable
|
||||
}
|
||||
protect := ProtectionFlags[sectionData.characteristics>>29]
|
||||
if (sectionData.characteristics & IMAGE_SCN_MEM_NOT_CACHED) != 0 {
|
||||
protect |= windows.PAGE_NOCACHE
|
||||
}
|
||||
|
||||
// Change memory access flags.
|
||||
var oldProtect uint32
|
||||
err := windows.VirtualProtect(sectionData.address, sectionData.size, protect, &oldProtect)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error protecting memory page: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (module *Module) finalizeSections() error {
|
||||
sections := module.headers.Sections()
|
||||
imageOffset := module.headers.OptionalHeader.imageOffset()
|
||||
sectionData := sectionFinalizeData{}
|
||||
sectionData.address = uintptr(sections[0].PhysicalAddress()) | imageOffset
|
||||
sectionData.alignedAddress = alignDown(sectionData.address, uintptr(module.headers.OptionalHeader.SectionAlignment))
|
||||
sectionData.size = module.realSectionSize(§ions[0])
|
||||
sectionData.characteristics = sections[0].Characteristics
|
||||
|
||||
// Loop through all sections and change access flags.
|
||||
for i := uint16(1); i < module.headers.FileHeader.NumberOfSections; i++ {
|
||||
sectionAddress := uintptr(sections[i].PhysicalAddress()) | imageOffset
|
||||
alignedAddress := alignDown(sectionAddress, uintptr(module.headers.OptionalHeader.SectionAlignment))
|
||||
sectionSize := module.realSectionSize(§ions[i])
|
||||
// Combine access flags of all sections that share a page.
|
||||
// TODO: We currently share flags of a trailing large section with the page of a first small section. This should be optimized.
|
||||
if sectionData.alignedAddress == alignedAddress || sectionData.address+sectionData.size > alignedAddress {
|
||||
// Section shares page with previous.
|
||||
if (sections[i].Characteristics&IMAGE_SCN_MEM_DISCARDABLE) == 0 || (sectionData.characteristics&IMAGE_SCN_MEM_DISCARDABLE) == 0 {
|
||||
sectionData.characteristics = (sectionData.characteristics | sections[i].Characteristics) &^ IMAGE_SCN_MEM_DISCARDABLE
|
||||
} else {
|
||||
sectionData.characteristics |= sections[i].Characteristics
|
||||
}
|
||||
sectionData.size = sectionAddress + sectionSize - sectionData.address
|
||||
continue
|
||||
}
|
||||
|
||||
err := module.finalizeSection(§ionData)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error finalizing section: %w", err)
|
||||
}
|
||||
sectionData.address = sectionAddress
|
||||
sectionData.alignedAddress = alignedAddress
|
||||
sectionData.size = sectionSize
|
||||
sectionData.characteristics = sections[i].Characteristics
|
||||
}
|
||||
sectionData.last = true
|
||||
err := module.finalizeSection(§ionData)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error finalizing section: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (module *Module) executeTLS() {
|
||||
directory := module.headerDirectory(IMAGE_DIRECTORY_ENTRY_TLS)
|
||||
if directory.VirtualAddress == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
tls := (*IMAGE_TLS_DIRECTORY)(a2p(module.codeBase + uintptr(directory.VirtualAddress)))
|
||||
callback := tls.AddressOfCallbacks
|
||||
if callback != 0 {
|
||||
for {
|
||||
f := *(*uintptr)(a2p(callback))
|
||||
if f == 0 {
|
||||
break
|
||||
}
|
||||
syscall.Syscall(f, 3, module.codeBase, uintptr(DLL_PROCESS_ATTACH), uintptr(0))
|
||||
callback += unsafe.Sizeof(f)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (module *Module) performBaseRelocation(delta uintptr) (relocated bool, err error) {
|
||||
directory := module.headerDirectory(IMAGE_DIRECTORY_ENTRY_BASERELOC)
|
||||
if directory.Size == 0 {
|
||||
return delta == 0, nil
|
||||
}
|
||||
|
||||
relocationHdr := (*IMAGE_BASE_RELOCATION)(a2p(module.codeBase + uintptr(directory.VirtualAddress)))
|
||||
for relocationHdr.VirtualAddress > 0 {
|
||||
dest := module.codeBase + uintptr(relocationHdr.VirtualAddress)
|
||||
|
||||
var relInfos []uint16
|
||||
unsafeSlice(
|
||||
unsafe.Pointer(&relInfos),
|
||||
a2p(uintptr(unsafe.Pointer(relocationHdr))+unsafe.Sizeof(*relocationHdr)),
|
||||
int((uintptr(relocationHdr.SizeOfBlock)-unsafe.Sizeof(*relocationHdr))/unsafe.Sizeof(relInfos[0])))
|
||||
for _, relInfo := range relInfos {
|
||||
// The upper 4 bits define the type of relocation.
|
||||
relType := relInfo >> 12
|
||||
// The lower 12 bits define the offset.
|
||||
relOffset := uintptr(relInfo & 0xfff)
|
||||
|
||||
switch relType {
|
||||
case IMAGE_REL_BASED_ABSOLUTE:
|
||||
// Skip relocation.
|
||||
|
||||
case IMAGE_REL_BASED_LOW:
|
||||
*(*uint16)(a2p(dest + relOffset)) += uint16(delta & 0xffff)
|
||||
break
|
||||
|
||||
case IMAGE_REL_BASED_HIGH:
|
||||
*(*uint16)(a2p(dest + relOffset)) += uint16(uint32(delta) >> 16)
|
||||
break
|
||||
|
||||
case IMAGE_REL_BASED_HIGHLOW:
|
||||
*(*uint32)(a2p(dest + relOffset)) += uint32(delta)
|
||||
|
||||
case IMAGE_REL_BASED_DIR64:
|
||||
*(*uint64)(a2p(dest + relOffset)) += uint64(delta)
|
||||
|
||||
case IMAGE_REL_BASED_THUMB_MOV32:
|
||||
inst := *(*uint32)(a2p(dest + relOffset))
|
||||
imm16 := ((inst << 1) & 0x0800) + ((inst << 12) & 0xf000) +
|
||||
((inst >> 20) & 0x0700) + ((inst >> 16) & 0x00ff)
|
||||
if (inst & 0x8000fbf0) != 0x0000f240 {
|
||||
return false, fmt.Errorf("Wrong Thumb2 instruction %08x, expected MOVW", inst)
|
||||
}
|
||||
imm16 += uint32(delta) & 0xffff
|
||||
hiDelta := (uint32(delta&0xffff0000) >> 16) + ((imm16 & 0xffff0000) >> 16)
|
||||
*(*uint32)(a2p(dest + relOffset)) = (inst & 0x8f00fbf0) + ((imm16 >> 1) & 0x0400) +
|
||||
((imm16 >> 12) & 0x000f) +
|
||||
((imm16 << 20) & 0x70000000) +
|
||||
((imm16 << 16) & 0xff0000)
|
||||
if hiDelta != 0 {
|
||||
inst = *(*uint32)(a2p(dest + relOffset + 4))
|
||||
imm16 = ((inst << 1) & 0x0800) + ((inst << 12) & 0xf000) +
|
||||
((inst >> 20) & 0x0700) + ((inst >> 16) & 0x00ff)
|
||||
if (inst & 0x8000fbf0) != 0x0000f2c0 {
|
||||
return false, fmt.Errorf("Wrong Thumb2 instruction %08x, expected MOVT", inst)
|
||||
}
|
||||
imm16 += hiDelta
|
||||
if imm16 > 0xffff {
|
||||
return false, fmt.Errorf("Resulting immediate value won't fit: %08x", imm16)
|
||||
}
|
||||
*(*uint32)(a2p(dest + relOffset + 4)) = (inst & 0x8f00fbf0) +
|
||||
((imm16 >> 1) & 0x0400) +
|
||||
((imm16 >> 12) & 0x000f) +
|
||||
((imm16 << 20) & 0x70000000) +
|
||||
((imm16 << 16) & 0xff0000)
|
||||
}
|
||||
|
||||
default:
|
||||
return false, fmt.Errorf("Unsupported relocation: %v", relType)
|
||||
}
|
||||
}
|
||||
|
||||
// Advance to next relocation block.
|
||||
relocationHdr = (*IMAGE_BASE_RELOCATION)(a2p(uintptr(unsafe.Pointer(relocationHdr)) + uintptr(relocationHdr.SizeOfBlock)))
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func (module *Module) buildImportTable() error {
|
||||
directory := module.headerDirectory(IMAGE_DIRECTORY_ENTRY_IMPORT)
|
||||
if directory.Size == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
module.modules = make([]windows.Handle, 0, 16)
|
||||
importDesc := (*IMAGE_IMPORT_DESCRIPTOR)(a2p(module.codeBase + uintptr(directory.VirtualAddress)))
|
||||
for importDesc.Name != 0 {
|
||||
handle, err := windows.LoadLibraryEx(windows.BytePtrToString((*byte)(a2p(module.codeBase+uintptr(importDesc.Name)))), 0, windows.LOAD_LIBRARY_SEARCH_SYSTEM32)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error loading module: %w", err)
|
||||
}
|
||||
var thunkRef, funcRef *uintptr
|
||||
if importDesc.OriginalFirstThunk() != 0 {
|
||||
thunkRef = (*uintptr)(a2p(module.codeBase + uintptr(importDesc.OriginalFirstThunk())))
|
||||
funcRef = (*uintptr)(a2p(module.codeBase + uintptr(importDesc.FirstThunk)))
|
||||
} else {
|
||||
// No hint table.
|
||||
thunkRef = (*uintptr)(a2p(module.codeBase + uintptr(importDesc.FirstThunk)))
|
||||
funcRef = (*uintptr)(a2p(module.codeBase + uintptr(importDesc.FirstThunk)))
|
||||
}
|
||||
for *thunkRef != 0 {
|
||||
if IMAGE_SNAP_BY_ORDINAL(*thunkRef) {
|
||||
*funcRef, err = windows.GetProcAddressByOrdinal(handle, IMAGE_ORDINAL(*thunkRef))
|
||||
} else {
|
||||
thunkData := (*IMAGE_IMPORT_BY_NAME)(a2p(module.codeBase + *thunkRef))
|
||||
*funcRef, err = windows.GetProcAddress(handle, windows.BytePtrToString(&thunkData.Name[0]))
|
||||
}
|
||||
if err != nil {
|
||||
windows.FreeLibrary(handle)
|
||||
return fmt.Errorf("Error getting function address: %w", err)
|
||||
}
|
||||
thunkRef = (*uintptr)(a2p(uintptr(unsafe.Pointer(thunkRef)) + unsafe.Sizeof(*thunkRef)))
|
||||
funcRef = (*uintptr)(a2p(uintptr(unsafe.Pointer(funcRef)) + unsafe.Sizeof(*funcRef)))
|
||||
}
|
||||
module.modules = append(module.modules, handle)
|
||||
importDesc = (*IMAGE_IMPORT_DESCRIPTOR)(a2p(uintptr(unsafe.Pointer(importDesc)) + unsafe.Sizeof(*importDesc)))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (module *Module) buildNameExports() error {
|
||||
directory := module.headerDirectory(IMAGE_DIRECTORY_ENTRY_EXPORT)
|
||||
if directory.Size == 0 {
|
||||
return errors.New("No export table found")
|
||||
}
|
||||
exports := (*IMAGE_EXPORT_DIRECTORY)(a2p(module.codeBase + uintptr(directory.VirtualAddress)))
|
||||
if exports.NumberOfNames == 0 || exports.NumberOfFunctions == 0 {
|
||||
return errors.New("No functions exported")
|
||||
}
|
||||
if exports.NumberOfNames == 0 {
|
||||
return errors.New("No functions exported by name")
|
||||
}
|
||||
var nameRefs []uint32
|
||||
unsafeSlice(unsafe.Pointer(&nameRefs), a2p(module.codeBase+uintptr(exports.AddressOfNames)), int(exports.NumberOfNames))
|
||||
var ordinals []uint16
|
||||
unsafeSlice(unsafe.Pointer(&ordinals), a2p(module.codeBase+uintptr(exports.AddressOfNameOrdinals)), int(exports.NumberOfNames))
|
||||
module.nameExports = make(map[string]uint16)
|
||||
for i := range nameRefs {
|
||||
nameArray := windows.BytePtrToString((*byte)(a2p(module.codeBase + uintptr(nameRefs[i]))))
|
||||
module.nameExports[nameArray] = ordinals[i]
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// LoadLibrary loads module image to memory.
|
||||
func LoadLibrary(data []byte) (module *Module, err error) {
|
||||
addr := uintptr(unsafe.Pointer(&data[0]))
|
||||
size := uintptr(len(data))
|
||||
if size < unsafe.Sizeof(IMAGE_DOS_HEADER{}) {
|
||||
return nil, errors.New("Incomplete IMAGE_DOS_HEADER")
|
||||
}
|
||||
dosHeader := (*IMAGE_DOS_HEADER)(a2p(addr))
|
||||
if dosHeader.E_magic != IMAGE_DOS_SIGNATURE {
|
||||
return nil, fmt.Errorf("Not an MS-DOS binary (provided: %x, expected: %x)", dosHeader.E_magic, IMAGE_DOS_SIGNATURE)
|
||||
}
|
||||
if (size < uintptr(dosHeader.E_lfanew)+unsafe.Sizeof(IMAGE_NT_HEADERS{})) {
|
||||
return nil, errors.New("Incomplete IMAGE_NT_HEADERS")
|
||||
}
|
||||
oldHeader := (*IMAGE_NT_HEADERS)(a2p(addr + uintptr(dosHeader.E_lfanew)))
|
||||
if oldHeader.Signature != IMAGE_NT_SIGNATURE {
|
||||
return nil, fmt.Errorf("Not an NT binary (provided: %x, expected: %x)", oldHeader.Signature, IMAGE_NT_SIGNATURE)
|
||||
}
|
||||
if oldHeader.FileHeader.Machine != imageFileProcess {
|
||||
return nil, fmt.Errorf("Foreign platform (provided: %x, expected: %x)", oldHeader.FileHeader.Machine, imageFileProcess)
|
||||
}
|
||||
if (oldHeader.OptionalHeader.SectionAlignment & 1) != 0 {
|
||||
return nil, errors.New("Unaligned section")
|
||||
}
|
||||
lastSectionEnd := uintptr(0)
|
||||
sections := oldHeader.Sections()
|
||||
optionalSectionSize := oldHeader.OptionalHeader.SectionAlignment
|
||||
for i := range sections {
|
||||
var endOfSection uintptr
|
||||
if sections[i].SizeOfRawData == 0 {
|
||||
// Section without data in the DLL
|
||||
endOfSection = uintptr(sections[i].VirtualAddress) + uintptr(optionalSectionSize)
|
||||
} else {
|
||||
endOfSection = uintptr(sections[i].VirtualAddress) + uintptr(sections[i].SizeOfRawData)
|
||||
}
|
||||
if endOfSection > lastSectionEnd {
|
||||
lastSectionEnd = endOfSection
|
||||
}
|
||||
}
|
||||
alignedImageSize := alignUp(uintptr(oldHeader.OptionalHeader.SizeOfImage), uintptr(oldHeader.OptionalHeader.SectionAlignment))
|
||||
if alignedImageSize != alignUp(lastSectionEnd, uintptr(oldHeader.OptionalHeader.SectionAlignment)) {
|
||||
return nil, errors.New("Section is not page-aligned")
|
||||
}
|
||||
|
||||
module = &Module{isDLL: (oldHeader.FileHeader.Characteristics & IMAGE_FILE_DLL) != 0}
|
||||
defer func() {
|
||||
if err != nil {
|
||||
module.Free()
|
||||
module = nil
|
||||
}
|
||||
}()
|
||||
|
||||
// Reserve memory for image of library.
|
||||
// TODO: Is it correct to commit the complete memory region at once? Calling DllEntry raises an exception if we don't.
|
||||
module.codeBase, err = windows.VirtualAlloc(oldHeader.OptionalHeader.ImageBase,
|
||||
alignedImageSize,
|
||||
windows.MEM_RESERVE|windows.MEM_COMMIT,
|
||||
windows.PAGE_READWRITE)
|
||||
if err != nil {
|
||||
// Try to allocate memory at arbitrary position.
|
||||
module.codeBase, err = windows.VirtualAlloc(0,
|
||||
alignedImageSize,
|
||||
windows.MEM_RESERVE|windows.MEM_COMMIT,
|
||||
windows.PAGE_READWRITE)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("Error allocating code: %w", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
err = module.check4GBBoundaries(alignedImageSize)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("Error reallocating code: %w", err)
|
||||
return
|
||||
}
|
||||
|
||||
if size < uintptr(oldHeader.OptionalHeader.SizeOfHeaders) {
|
||||
err = errors.New("Incomplete headers")
|
||||
return
|
||||
}
|
||||
// Commit memory for headers.
|
||||
headers, err := windows.VirtualAlloc(module.codeBase,
|
||||
uintptr(oldHeader.OptionalHeader.SizeOfHeaders),
|
||||
windows.MEM_COMMIT,
|
||||
windows.PAGE_READWRITE)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("Error allocating headers: %w", err)
|
||||
return
|
||||
}
|
||||
// Copy PE header to code.
|
||||
memcpy(headers, addr, uintptr(oldHeader.OptionalHeader.SizeOfHeaders))
|
||||
module.headers = (*IMAGE_NT_HEADERS)(a2p(headers + uintptr(dosHeader.E_lfanew)))
|
||||
|
||||
// Update position.
|
||||
module.headers.OptionalHeader.ImageBase = module.codeBase
|
||||
|
||||
// Copy sections from DLL file block to new memory location.
|
||||
err = module.copySections(addr, size, oldHeader)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("Error copying sections: %w", err)
|
||||
return
|
||||
}
|
||||
|
||||
// Adjust base address of imported data.
|
||||
locationDelta := module.headers.OptionalHeader.ImageBase - oldHeader.OptionalHeader.ImageBase
|
||||
if locationDelta != 0 {
|
||||
module.isRelocated, err = module.performBaseRelocation(locationDelta)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("Error relocating module: %w", err)
|
||||
return
|
||||
}
|
||||
} else {
|
||||
module.isRelocated = true
|
||||
}
|
||||
|
||||
// Load required dlls and adjust function table of imports.
|
||||
err = module.buildImportTable()
|
||||
if err != nil {
|
||||
err = fmt.Errorf("Error building import table: %w", err)
|
||||
return
|
||||
}
|
||||
|
||||
// Mark memory pages depending on section headers and release sections that are marked as "discardable".
|
||||
err = module.finalizeSections()
|
||||
if err != nil {
|
||||
err = fmt.Errorf("Error finalizing sections: %w", err)
|
||||
return
|
||||
}
|
||||
|
||||
// TLS callbacks are executed BEFORE the main loading.
|
||||
module.executeTLS()
|
||||
|
||||
// Get entry point of loaded module.
|
||||
if module.headers.OptionalHeader.AddressOfEntryPoint != 0 {
|
||||
module.entry = module.codeBase + uintptr(module.headers.OptionalHeader.AddressOfEntryPoint)
|
||||
if module.isDLL {
|
||||
// Notify library about attaching to process.
|
||||
r0, _, _ := syscall.Syscall(module.entry, 3, module.codeBase, uintptr(DLL_PROCESS_ATTACH), 0)
|
||||
successful := r0 != 0
|
||||
if !successful {
|
||||
err = windows.ERROR_DLL_INIT_FAILED
|
||||
return
|
||||
}
|
||||
module.initialized = true
|
||||
}
|
||||
}
|
||||
|
||||
module.buildNameExports()
|
||||
return
|
||||
}
|
||||
|
||||
// Free releases module resources and unloads it.
|
||||
func (module *Module) Free() {
|
||||
if module.initialized {
|
||||
// Notify library about detaching from process.
|
||||
syscall.Syscall(module.entry, 3, module.codeBase, uintptr(DLL_PROCESS_DETACH), 0)
|
||||
module.initialized = false
|
||||
}
|
||||
if module.modules != nil {
|
||||
// Free previously opened libraries.
|
||||
for _, handle := range module.modules {
|
||||
windows.FreeLibrary(handle)
|
||||
}
|
||||
module.modules = nil
|
||||
}
|
||||
if module.codeBase != 0 {
|
||||
windows.VirtualFree(module.codeBase, 0, windows.MEM_RELEASE)
|
||||
module.codeBase = 0
|
||||
}
|
||||
if module.blockedMemory != nil {
|
||||
module.blockedMemory.free()
|
||||
module.blockedMemory = nil
|
||||
}
|
||||
}
|
||||
|
||||
// ProcAddressByName returns function address by exported name.
|
||||
func (module *Module) ProcAddressByName(name string) (uintptr, error) {
|
||||
directory := module.headerDirectory(IMAGE_DIRECTORY_ENTRY_EXPORT)
|
||||
if directory.Size == 0 {
|
||||
return 0, errors.New("No export table found")
|
||||
}
|
||||
exports := (*IMAGE_EXPORT_DIRECTORY)(a2p(module.codeBase + uintptr(directory.VirtualAddress)))
|
||||
if module.nameExports == nil {
|
||||
return 0, errors.New("No functions exported by name")
|
||||
}
|
||||
if idx, ok := module.nameExports[name]; ok {
|
||||
if uint32(idx) > exports.NumberOfFunctions {
|
||||
return 0, errors.New("Ordinal number too high")
|
||||
}
|
||||
// AddressOfFunctions contains the RVAs to the "real" functions.
|
||||
return module.codeBase + uintptr(*(*uint32)(a2p(module.codeBase + uintptr(exports.AddressOfFunctions) + uintptr(idx)*4))), nil
|
||||
}
|
||||
return 0, errors.New("Function not found by name")
|
||||
}
|
||||
|
||||
// ProcAddressByOrdinal returns function address by exported ordinal.
|
||||
func (module *Module) ProcAddressByOrdinal(ordinal uint16) (uintptr, error) {
|
||||
directory := module.headerDirectory(IMAGE_DIRECTORY_ENTRY_EXPORT)
|
||||
if directory.Size == 0 {
|
||||
return 0, errors.New("No export table found")
|
||||
}
|
||||
exports := (*IMAGE_EXPORT_DIRECTORY)(a2p(module.codeBase + uintptr(directory.VirtualAddress)))
|
||||
if uint32(ordinal) < exports.Base {
|
||||
return 0, errors.New("Ordinal number too low")
|
||||
}
|
||||
idx := ordinal - uint16(exports.Base)
|
||||
if uint32(idx) > exports.NumberOfFunctions {
|
||||
return 0, errors.New("Ordinal number too high")
|
||||
}
|
||||
// AddressOfFunctions contains the RVAs to the "real" functions.
|
||||
return module.codeBase + uintptr(*(*uint32)(a2p(module.codeBase + uintptr(exports.AddressOfFunctions) + uintptr(idx)*4))), nil
|
||||
}
|
||||
|
||||
func alignDown(value, alignment uintptr) uintptr {
|
||||
return value & ^(alignment - 1)
|
||||
}
|
||||
|
||||
func alignUp(value, alignment uintptr) uintptr {
|
||||
return (value + alignment - 1) & ^(alignment - 1)
|
||||
}
|
||||
|
||||
func a2p(addr uintptr) unsafe.Pointer {
|
||||
return unsafe.Pointer(addr)
|
||||
}
|
||||
|
||||
func memcpy(dst, src, size uintptr) {
|
||||
var d, s []byte
|
||||
unsafeSlice(unsafe.Pointer(&d), a2p(dst), int(size))
|
||||
unsafeSlice(unsafe.Pointer(&s), a2p(src), int(size))
|
||||
copy(d, s)
|
||||
}
|
||||
|
||||
// unsafeSlice updates the slice slicePtr to be a slice
|
||||
// referencing the provided data with its length & capacity set to
|
||||
// lenCap.
|
||||
//
|
||||
// TODO: when Go 1.16 or Go 1.17 is the minimum supported version,
|
||||
// update callers to use unsafe.Slice instead of this.
|
||||
func unsafeSlice(slicePtr, data unsafe.Pointer, lenCap int) {
|
||||
type sliceHeader struct {
|
||||
Data unsafe.Pointer
|
||||
Len int
|
||||
Cap int
|
||||
}
|
||||
h := (*sliceHeader)(slicePtr)
|
||||
h.Data = data
|
||||
h.Len = lenCap
|
||||
h.Cap = lenCap
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
// +build windows,386 windows,arm
|
||||
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2021 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package memmod
|
||||
|
||||
func (opthdr *IMAGE_OPTIONAL_HEADER) imageOffset() uintptr {
|
||||
return 0
|
||||
}
|
||||
|
||||
func (module *Module) check4GBBoundaries(alignedImageSize uintptr) (err error) {
|
||||
return
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2021 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package memmod
|
||||
|
||||
const imageFileProcess = IMAGE_FILE_MACHINE_I386
|
||||
@@ -1,36 +0,0 @@
|
||||
// +build windows,amd64 windows,arm64
|
||||
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2021 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package memmod
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
func (opthdr *IMAGE_OPTIONAL_HEADER) imageOffset() uintptr {
|
||||
return uintptr(opthdr.ImageBase & 0xffffffff00000000)
|
||||
}
|
||||
|
||||
func (module *Module) check4GBBoundaries(alignedImageSize uintptr) (err error) {
|
||||
for (module.codeBase >> 32) < ((module.codeBase + alignedImageSize) >> 32) {
|
||||
node := &addressList{
|
||||
next: module.blockedMemory,
|
||||
address: module.codeBase,
|
||||
}
|
||||
module.blockedMemory = node
|
||||
module.codeBase, err = windows.VirtualAlloc(0,
|
||||
alignedImageSize,
|
||||
windows.MEM_RESERVE|windows.MEM_COMMIT,
|
||||
windows.PAGE_READWRITE)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error allocating memory block: %w", err)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2021 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package memmod
|
||||
|
||||
const imageFileProcess = IMAGE_FILE_MACHINE_AMD64
|
||||
@@ -1,8 +0,0 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2021 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package memmod
|
||||
|
||||
const imageFileProcess = IMAGE_FILE_MACHINE_ARMNT
|
||||
@@ -1,8 +0,0 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2021 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package memmod
|
||||
|
||||
const imageFileProcess = IMAGE_FILE_MACHINE_ARM64
|
||||
@@ -1,339 +0,0 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2021 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package memmod
|
||||
|
||||
import "unsafe"
|
||||
|
||||
const (
|
||||
IMAGE_DOS_SIGNATURE = 0x5A4D // MZ
|
||||
IMAGE_OS2_SIGNATURE = 0x454E // NE
|
||||
IMAGE_OS2_SIGNATURE_LE = 0x454C // LE
|
||||
IMAGE_VXD_SIGNATURE = 0x454C // LE
|
||||
IMAGE_NT_SIGNATURE = 0x00004550 // PE00
|
||||
)
|
||||
|
||||
// DOS .EXE header
|
||||
type IMAGE_DOS_HEADER struct {
|
||||
E_magic uint16 // Magic number
|
||||
E_cblp uint16 // Bytes on last page of file
|
||||
E_cp uint16 // Pages in file
|
||||
E_crlc uint16 // Relocations
|
||||
E_cparhdr uint16 // Size of header in paragraphs
|
||||
E_minalloc uint16 // Minimum extra paragraphs needed
|
||||
E_maxalloc uint16 // Maximum extra paragraphs needed
|
||||
E_ss uint16 // Initial (relative) SS value
|
||||
E_sp uint16 // Initial SP value
|
||||
E_csum uint16 // Checksum
|
||||
E_ip uint16 // Initial IP value
|
||||
E_cs uint16 // Initial (relative) CS value
|
||||
E_lfarlc uint16 // File address of relocation table
|
||||
E_ovno uint16 // Overlay number
|
||||
E_res [4]uint16 // Reserved words
|
||||
E_oemid uint16 // OEM identifier (for e_oeminfo)
|
||||
E_oeminfo uint16 // OEM information; e_oemid specific
|
||||
E_res2 [10]uint16 // Reserved words
|
||||
E_lfanew int32 // File address of new exe header
|
||||
}
|
||||
|
||||
// File header format
|
||||
type IMAGE_FILE_HEADER struct {
|
||||
Machine uint16
|
||||
NumberOfSections uint16
|
||||
TimeDateStamp uint32
|
||||
PointerToSymbolTable uint32
|
||||
NumberOfSymbols uint32
|
||||
SizeOfOptionalHeader uint16
|
||||
Characteristics uint16
|
||||
}
|
||||
|
||||
const (
|
||||
IMAGE_SIZEOF_FILE_HEADER = 20
|
||||
|
||||
IMAGE_FILE_RELOCS_STRIPPED = 0x0001 // Relocation info stripped from file.
|
||||
IMAGE_FILE_EXECUTABLE_IMAGE = 0x0002 // File is executable (i.e. no unresolved external references).
|
||||
IMAGE_FILE_LINE_NUMS_STRIPPED = 0x0004 // Line nunbers stripped from file.
|
||||
IMAGE_FILE_LOCAL_SYMS_STRIPPED = 0x0008 // Local symbols stripped from file.
|
||||
IMAGE_FILE_AGGRESIVE_WS_TRIM = 0x0010 // Aggressively trim working set
|
||||
IMAGE_FILE_LARGE_ADDRESS_AWARE = 0x0020 // App can handle >2gb addresses
|
||||
IMAGE_FILE_BYTES_REVERSED_LO = 0x0080 // Bytes of machine word are reversed.
|
||||
IMAGE_FILE_32BIT_MACHINE = 0x0100 // 32 bit word machine.
|
||||
IMAGE_FILE_DEBUG_STRIPPED = 0x0200 // Debugging info stripped from file in .DBG file
|
||||
IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP = 0x0400 // If Image is on removable media, copy and run from the swap file.
|
||||
IMAGE_FILE_NET_RUN_FROM_SWAP = 0x0800 // If Image is on Net, copy and run from the swap file.
|
||||
IMAGE_FILE_SYSTEM = 0x1000 // System File.
|
||||
IMAGE_FILE_DLL = 0x2000 // File is a DLL.
|
||||
IMAGE_FILE_UP_SYSTEM_ONLY = 0x4000 // File should only be run on a UP machine
|
||||
IMAGE_FILE_BYTES_REVERSED_HI = 0x8000 // Bytes of machine word are reversed.
|
||||
|
||||
IMAGE_FILE_MACHINE_UNKNOWN = 0
|
||||
IMAGE_FILE_MACHINE_TARGET_HOST = 0x0001 // Useful for indicating we want to interact with the host and not a WoW guest.
|
||||
IMAGE_FILE_MACHINE_I386 = 0x014c // Intel 386.
|
||||
IMAGE_FILE_MACHINE_R3000 = 0x0162 // MIPS little-endian, 0x160 big-endian
|
||||
IMAGE_FILE_MACHINE_R4000 = 0x0166 // MIPS little-endian
|
||||
IMAGE_FILE_MACHINE_R10000 = 0x0168 // MIPS little-endian
|
||||
IMAGE_FILE_MACHINE_WCEMIPSV2 = 0x0169 // MIPS little-endian WCE v2
|
||||
IMAGE_FILE_MACHINE_ALPHA = 0x0184 // Alpha_AXP
|
||||
IMAGE_FILE_MACHINE_SH3 = 0x01a2 // SH3 little-endian
|
||||
IMAGE_FILE_MACHINE_SH3DSP = 0x01a3
|
||||
IMAGE_FILE_MACHINE_SH3E = 0x01a4 // SH3E little-endian
|
||||
IMAGE_FILE_MACHINE_SH4 = 0x01a6 // SH4 little-endian
|
||||
IMAGE_FILE_MACHINE_SH5 = 0x01a8 // SH5
|
||||
IMAGE_FILE_MACHINE_ARM = 0x01c0 // ARM Little-Endian
|
||||
IMAGE_FILE_MACHINE_THUMB = 0x01c2 // ARM Thumb/Thumb-2 Little-Endian
|
||||
IMAGE_FILE_MACHINE_ARMNT = 0x01c4 // ARM Thumb-2 Little-Endian
|
||||
IMAGE_FILE_MACHINE_AM33 = 0x01d3
|
||||
IMAGE_FILE_MACHINE_POWERPC = 0x01F0 // IBM PowerPC Little-Endian
|
||||
IMAGE_FILE_MACHINE_POWERPCFP = 0x01f1
|
||||
IMAGE_FILE_MACHINE_IA64 = 0x0200 // Intel 64
|
||||
IMAGE_FILE_MACHINE_MIPS16 = 0x0266 // MIPS
|
||||
IMAGE_FILE_MACHINE_ALPHA64 = 0x0284 // ALPHA64
|
||||
IMAGE_FILE_MACHINE_MIPSFPU = 0x0366 // MIPS
|
||||
IMAGE_FILE_MACHINE_MIPSFPU16 = 0x0466 // MIPS
|
||||
IMAGE_FILE_MACHINE_AXP64 = IMAGE_FILE_MACHINE_ALPHA64
|
||||
IMAGE_FILE_MACHINE_TRICORE = 0x0520 // Infineon
|
||||
IMAGE_FILE_MACHINE_CEF = 0x0CEF
|
||||
IMAGE_FILE_MACHINE_EBC = 0x0EBC // EFI Byte Code
|
||||
IMAGE_FILE_MACHINE_AMD64 = 0x8664 // AMD64 (K8)
|
||||
IMAGE_FILE_MACHINE_M32R = 0x9041 // M32R little-endian
|
||||
IMAGE_FILE_MACHINE_ARM64 = 0xAA64 // ARM64 Little-Endian
|
||||
IMAGE_FILE_MACHINE_CEE = 0xC0EE
|
||||
)
|
||||
|
||||
// Directory format
|
||||
type IMAGE_DATA_DIRECTORY struct {
|
||||
VirtualAddress uint32
|
||||
Size uint32
|
||||
}
|
||||
|
||||
const IMAGE_NUMBEROF_DIRECTORY_ENTRIES = 16
|
||||
|
||||
type IMAGE_NT_HEADERS struct {
|
||||
Signature uint32
|
||||
FileHeader IMAGE_FILE_HEADER
|
||||
OptionalHeader IMAGE_OPTIONAL_HEADER
|
||||
}
|
||||
|
||||
func (ntheader *IMAGE_NT_HEADERS) Sections() []IMAGE_SECTION_HEADER {
|
||||
return (*[0xffff]IMAGE_SECTION_HEADER)(unsafe.Pointer(
|
||||
(uintptr)(unsafe.Pointer(ntheader)) +
|
||||
unsafe.Offsetof(ntheader.OptionalHeader) +
|
||||
uintptr(ntheader.FileHeader.SizeOfOptionalHeader)))[:ntheader.FileHeader.NumberOfSections]
|
||||
}
|
||||
|
||||
const (
|
||||
IMAGE_DIRECTORY_ENTRY_EXPORT = 0 // Export Directory
|
||||
IMAGE_DIRECTORY_ENTRY_IMPORT = 1 // Import Directory
|
||||
IMAGE_DIRECTORY_ENTRY_RESOURCE = 2 // Resource Directory
|
||||
IMAGE_DIRECTORY_ENTRY_EXCEPTION = 3 // Exception Directory
|
||||
IMAGE_DIRECTORY_ENTRY_SECURITY = 4 // Security Directory
|
||||
IMAGE_DIRECTORY_ENTRY_BASERELOC = 5 // Base Relocation Table
|
||||
IMAGE_DIRECTORY_ENTRY_DEBUG = 6 // Debug Directory
|
||||
IMAGE_DIRECTORY_ENTRY_COPYRIGHT = 7 // (X86 usage)
|
||||
IMAGE_DIRECTORY_ENTRY_ARCHITECTURE = 7 // Architecture Specific Data
|
||||
IMAGE_DIRECTORY_ENTRY_GLOBALPTR = 8 // RVA of GP
|
||||
IMAGE_DIRECTORY_ENTRY_TLS = 9 // TLS Directory
|
||||
IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG = 10 // Load Configuration Directory
|
||||
IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT = 11 // Bound Import Directory in headers
|
||||
IMAGE_DIRECTORY_ENTRY_IAT = 12 // Import Address Table
|
||||
IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT = 13 // Delay Load Import Descriptors
|
||||
IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR = 14 // COM Runtime descriptor
|
||||
)
|
||||
|
||||
const IMAGE_SIZEOF_SHORT_NAME = 8
|
||||
|
||||
// Section header format
|
||||
type IMAGE_SECTION_HEADER struct {
|
||||
Name [IMAGE_SIZEOF_SHORT_NAME]byte
|
||||
physicalAddressOrVirtualSize uint32
|
||||
VirtualAddress uint32
|
||||
SizeOfRawData uint32
|
||||
PointerToRawData uint32
|
||||
PointerToRelocations uint32
|
||||
PointerToLinenumbers uint32
|
||||
NumberOfRelocations uint16
|
||||
NumberOfLinenumbers uint16
|
||||
Characteristics uint32
|
||||
}
|
||||
|
||||
func (ishdr *IMAGE_SECTION_HEADER) PhysicalAddress() uint32 {
|
||||
return ishdr.physicalAddressOrVirtualSize
|
||||
}
|
||||
|
||||
func (ishdr *IMAGE_SECTION_HEADER) SetPhysicalAddress(addr uint32) {
|
||||
ishdr.physicalAddressOrVirtualSize = addr
|
||||
}
|
||||
|
||||
func (ishdr *IMAGE_SECTION_HEADER) VirtualSize() uint32 {
|
||||
return ishdr.physicalAddressOrVirtualSize
|
||||
}
|
||||
|
||||
func (ishdr *IMAGE_SECTION_HEADER) SetVirtualSize(addr uint32) {
|
||||
ishdr.physicalAddressOrVirtualSize = addr
|
||||
}
|
||||
|
||||
const (
|
||||
// Section characteristics.
|
||||
IMAGE_SCN_TYPE_REG = 0x00000000 // Reserved.
|
||||
IMAGE_SCN_TYPE_DSECT = 0x00000001 // Reserved.
|
||||
IMAGE_SCN_TYPE_NOLOAD = 0x00000002 // Reserved.
|
||||
IMAGE_SCN_TYPE_GROUP = 0x00000004 // Reserved.
|
||||
IMAGE_SCN_TYPE_NO_PAD = 0x00000008 // Reserved.
|
||||
IMAGE_SCN_TYPE_COPY = 0x00000010 // Reserved.
|
||||
|
||||
IMAGE_SCN_CNT_CODE = 0x00000020 // Section contains code.
|
||||
IMAGE_SCN_CNT_INITIALIZED_DATA = 0x00000040 // Section contains initialized data.
|
||||
IMAGE_SCN_CNT_UNINITIALIZED_DATA = 0x00000080 // Section contains uninitialized data.
|
||||
|
||||
IMAGE_SCN_LNK_OTHER = 0x00000100 // Reserved.
|
||||
IMAGE_SCN_LNK_INFO = 0x00000200 // Section contains comments or some other type of information.
|
||||
IMAGE_SCN_TYPE_OVER = 0x00000400 // Reserved.
|
||||
IMAGE_SCN_LNK_REMOVE = 0x00000800 // Section contents will not become part of image.
|
||||
IMAGE_SCN_LNK_COMDAT = 0x00001000 // Section contents comdat.
|
||||
IMAGE_SCN_MEM_PROTECTED = 0x00004000 // Obsolete.
|
||||
IMAGE_SCN_NO_DEFER_SPEC_EXC = 0x00004000 // Reset speculative exceptions handling bits in the TLB entries for this section.
|
||||
IMAGE_SCN_GPREL = 0x00008000 // Section content can be accessed relative to GP
|
||||
IMAGE_SCN_MEM_FARDATA = 0x00008000
|
||||
IMAGE_SCN_MEM_SYSHEAP = 0x00010000 // Obsolete.
|
||||
IMAGE_SCN_MEM_PURGEABLE = 0x00020000
|
||||
IMAGE_SCN_MEM_16BIT = 0x00020000
|
||||
IMAGE_SCN_MEM_LOCKED = 0x00040000
|
||||
IMAGE_SCN_MEM_PRELOAD = 0x00080000
|
||||
|
||||
IMAGE_SCN_ALIGN_1BYTES = 0x00100000 //
|
||||
IMAGE_SCN_ALIGN_2BYTES = 0x00200000 //
|
||||
IMAGE_SCN_ALIGN_4BYTES = 0x00300000 //
|
||||
IMAGE_SCN_ALIGN_8BYTES = 0x00400000 //
|
||||
IMAGE_SCN_ALIGN_16BYTES = 0x00500000 // Default alignment if no others are specified.
|
||||
IMAGE_SCN_ALIGN_32BYTES = 0x00600000 //
|
||||
IMAGE_SCN_ALIGN_64BYTES = 0x00700000 //
|
||||
IMAGE_SCN_ALIGN_128BYTES = 0x00800000 //
|
||||
IMAGE_SCN_ALIGN_256BYTES = 0x00900000 //
|
||||
IMAGE_SCN_ALIGN_512BYTES = 0x00A00000 //
|
||||
IMAGE_SCN_ALIGN_1024BYTES = 0x00B00000 //
|
||||
IMAGE_SCN_ALIGN_2048BYTES = 0x00C00000 //
|
||||
IMAGE_SCN_ALIGN_4096BYTES = 0x00D00000 //
|
||||
IMAGE_SCN_ALIGN_8192BYTES = 0x00E00000 //
|
||||
IMAGE_SCN_ALIGN_MASK = 0x00F00000
|
||||
|
||||
IMAGE_SCN_LNK_NRELOC_OVFL = 0x01000000 // Section contains extended relocations.
|
||||
IMAGE_SCN_MEM_DISCARDABLE = 0x02000000 // Section can be discarded.
|
||||
IMAGE_SCN_MEM_NOT_CACHED = 0x04000000 // Section is not cachable.
|
||||
IMAGE_SCN_MEM_NOT_PAGED = 0x08000000 // Section is not pageable.
|
||||
IMAGE_SCN_MEM_SHARED = 0x10000000 // Section is shareable.
|
||||
IMAGE_SCN_MEM_EXECUTE = 0x20000000 // Section is executable.
|
||||
IMAGE_SCN_MEM_READ = 0x40000000 // Section is readable.
|
||||
IMAGE_SCN_MEM_WRITE = 0x80000000 // Section is writeable.
|
||||
|
||||
// TLS Characteristic Flags
|
||||
IMAGE_SCN_SCALE_INDEX = 0x00000001 // Tls index is scaled.
|
||||
)
|
||||
|
||||
// Based relocation format
|
||||
type IMAGE_BASE_RELOCATION struct {
|
||||
VirtualAddress uint32
|
||||
SizeOfBlock uint32
|
||||
}
|
||||
|
||||
const (
|
||||
IMAGE_REL_BASED_ABSOLUTE = 0
|
||||
IMAGE_REL_BASED_HIGH = 1
|
||||
IMAGE_REL_BASED_LOW = 2
|
||||
IMAGE_REL_BASED_HIGHLOW = 3
|
||||
IMAGE_REL_BASED_HIGHADJ = 4
|
||||
IMAGE_REL_BASED_MACHINE_SPECIFIC_5 = 5
|
||||
IMAGE_REL_BASED_RESERVED = 6
|
||||
IMAGE_REL_BASED_MACHINE_SPECIFIC_7 = 7
|
||||
IMAGE_REL_BASED_MACHINE_SPECIFIC_8 = 8
|
||||
IMAGE_REL_BASED_MACHINE_SPECIFIC_9 = 9
|
||||
IMAGE_REL_BASED_DIR64 = 10
|
||||
|
||||
IMAGE_REL_BASED_IA64_IMM64 = 9
|
||||
|
||||
IMAGE_REL_BASED_MIPS_JMPADDR = 5
|
||||
IMAGE_REL_BASED_MIPS_JMPADDR16 = 9
|
||||
|
||||
IMAGE_REL_BASED_ARM_MOV32 = 5
|
||||
IMAGE_REL_BASED_THUMB_MOV32 = 7
|
||||
)
|
||||
|
||||
// Export Format
|
||||
type IMAGE_EXPORT_DIRECTORY struct {
|
||||
Characteristics uint32
|
||||
TimeDateStamp uint32
|
||||
MajorVersion uint16
|
||||
MinorVersion uint16
|
||||
Name uint32
|
||||
Base uint32
|
||||
NumberOfFunctions uint32
|
||||
NumberOfNames uint32
|
||||
AddressOfFunctions uint32 // RVA from base of image
|
||||
AddressOfNames uint32 // RVA from base of image
|
||||
AddressOfNameOrdinals uint32 // RVA from base of image
|
||||
}
|
||||
|
||||
type IMAGE_IMPORT_BY_NAME struct {
|
||||
Hint uint16
|
||||
Name [1]byte
|
||||
}
|
||||
|
||||
func IMAGE_ORDINAL(ordinal uintptr) uintptr {
|
||||
return ordinal & 0xffff
|
||||
}
|
||||
|
||||
func IMAGE_SNAP_BY_ORDINAL(ordinal uintptr) bool {
|
||||
return (ordinal & IMAGE_ORDINAL_FLAG) != 0
|
||||
}
|
||||
|
||||
// Thread Local Storage
|
||||
type IMAGE_TLS_DIRECTORY struct {
|
||||
StartAddressOfRawData uintptr
|
||||
EndAddressOfRawData uintptr
|
||||
AddressOfIndex uintptr // PDWORD
|
||||
AddressOfCallbacks uintptr // PIMAGE_TLS_CALLBACK *;
|
||||
SizeOfZeroFill uint32
|
||||
Characteristics uint32
|
||||
}
|
||||
|
||||
type IMAGE_IMPORT_DESCRIPTOR struct {
|
||||
characteristicsOrOriginalFirstThunk uint32 // 0 for terminating null import descriptor
|
||||
// RVA to original unbound IAT (PIMAGE_THUNK_DATA)
|
||||
TimeDateStamp uint32 // 0 if not bound,
|
||||
// -1 if bound, and real date\time stamp
|
||||
// in IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT (new BIND)
|
||||
// O.W. date/time stamp of DLL bound to (Old BIND)
|
||||
ForwarderChain uint32 // -1 if no forwarders
|
||||
Name uint32
|
||||
FirstThunk uint32 // RVA to IAT (if bound this IAT has actual addresses)
|
||||
}
|
||||
|
||||
func (imgimpdesc *IMAGE_IMPORT_DESCRIPTOR) Characteristics() uint32 {
|
||||
return imgimpdesc.characteristicsOrOriginalFirstThunk
|
||||
}
|
||||
|
||||
func (imgimpdesc *IMAGE_IMPORT_DESCRIPTOR) OriginalFirstThunk() uint32 {
|
||||
return imgimpdesc.characteristicsOrOriginalFirstThunk
|
||||
}
|
||||
|
||||
const (
|
||||
DLL_PROCESS_ATTACH = 1
|
||||
DLL_THREAD_ATTACH = 2
|
||||
DLL_THREAD_DETACH = 3
|
||||
DLL_PROCESS_DETACH = 0
|
||||
)
|
||||
|
||||
type SYSTEM_INFO struct {
|
||||
ProcessorArchitecture uint16
|
||||
Reserved uint16
|
||||
PageSize uint32
|
||||
MinimumApplicationAddress uintptr
|
||||
MaximumApplicationAddress uintptr
|
||||
ActiveProcessorMask uintptr
|
||||
NumberOfProcessors uint32
|
||||
ProcessorType uint32
|
||||
AllocationGranularity uint32
|
||||
ProcessorLevel uint16
|
||||
ProcessorRevision uint16
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
// +build windows,386 windows,arm
|
||||
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2021 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package memmod
|
||||
|
||||
// Optional header format
|
||||
type IMAGE_OPTIONAL_HEADER struct {
|
||||
Magic uint16
|
||||
MajorLinkerVersion uint8
|
||||
MinorLinkerVersion uint8
|
||||
SizeOfCode uint32
|
||||
SizeOfInitializedData uint32
|
||||
SizeOfUninitializedData uint32
|
||||
AddressOfEntryPoint uint32
|
||||
BaseOfCode uint32
|
||||
BaseOfData uint32
|
||||
ImageBase uintptr
|
||||
SectionAlignment uint32
|
||||
FileAlignment uint32
|
||||
MajorOperatingSystemVersion uint16
|
||||
MinorOperatingSystemVersion uint16
|
||||
MajorImageVersion uint16
|
||||
MinorImageVersion uint16
|
||||
MajorSubsystemVersion uint16
|
||||
MinorSubsystemVersion uint16
|
||||
Win32VersionValue uint32
|
||||
SizeOfImage uint32
|
||||
SizeOfHeaders uint32
|
||||
CheckSum uint32
|
||||
Subsystem uint16
|
||||
DllCharacteristics uint16
|
||||
SizeOfStackReserve uintptr
|
||||
SizeOfStackCommit uintptr
|
||||
SizeOfHeapReserve uintptr
|
||||
SizeOfHeapCommit uintptr
|
||||
LoaderFlags uint32
|
||||
NumberOfRvaAndSizes uint32
|
||||
DataDirectory [IMAGE_NUMBEROF_DIRECTORY_ENTRIES]IMAGE_DATA_DIRECTORY
|
||||
}
|
||||
|
||||
const IMAGE_ORDINAL_FLAG uintptr = 0x80000000
|
||||
@@ -1,44 +0,0 @@
|
||||
// +build windows,amd64 windows,arm64
|
||||
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2021 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package memmod
|
||||
|
||||
// Optional header format
|
||||
type IMAGE_OPTIONAL_HEADER struct {
|
||||
Magic uint16
|
||||
MajorLinkerVersion uint8
|
||||
MinorLinkerVersion uint8
|
||||
SizeOfCode uint32
|
||||
SizeOfInitializedData uint32
|
||||
SizeOfUninitializedData uint32
|
||||
AddressOfEntryPoint uint32
|
||||
BaseOfCode uint32
|
||||
ImageBase uintptr
|
||||
SectionAlignment uint32
|
||||
FileAlignment uint32
|
||||
MajorOperatingSystemVersion uint16
|
||||
MinorOperatingSystemVersion uint16
|
||||
MajorImageVersion uint16
|
||||
MinorImageVersion uint16
|
||||
MajorSubsystemVersion uint16
|
||||
MinorSubsystemVersion uint16
|
||||
Win32VersionValue uint32
|
||||
SizeOfImage uint32
|
||||
SizeOfHeaders uint32
|
||||
CheckSum uint32
|
||||
Subsystem uint16
|
||||
DllCharacteristics uint16
|
||||
SizeOfStackReserve uintptr
|
||||
SizeOfStackCommit uintptr
|
||||
SizeOfHeapReserve uintptr
|
||||
SizeOfHeapCommit uintptr
|
||||
LoaderFlags uint32
|
||||
NumberOfRvaAndSizes uint32
|
||||
DataDirectory [IMAGE_NUMBEROF_DIRECTORY_ENTRIES]IMAGE_DATA_DIRECTORY
|
||||
}
|
||||
|
||||
const IMAGE_ORDINAL_FLAG uintptr = 0x8000000000000000
|
||||
@@ -67,7 +67,7 @@ func (session Session) ReceivePacket() (packet []byte, err error) {
|
||||
err = e1
|
||||
return
|
||||
}
|
||||
unsafeSlice(unsafe.Pointer(&packet), unsafe.Pointer(r0), int(packetSize))
|
||||
packet = unsafe.Slice((*byte)(unsafe.Pointer(r0)), packetSize)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -81,28 +81,10 @@ func (session Session) AllocateSendPacket(packetSize int) (packet []byte, err er
|
||||
err = e1
|
||||
return
|
||||
}
|
||||
unsafeSlice(unsafe.Pointer(&packet), unsafe.Pointer(r0), int(packetSize))
|
||||
packet = unsafe.Slice((*byte)(unsafe.Pointer(r0)), packetSize)
|
||||
return
|
||||
}
|
||||
|
||||
func (session Session) SendPacket(packet []byte) {
|
||||
syscall.Syscall(procWintunSendPacket.Addr(), 2, session.handle, uintptr(unsafe.Pointer(&packet[0])), 0)
|
||||
}
|
||||
|
||||
// unsafeSlice updates the slice slicePtr to be a slice
|
||||
// referencing the provided data with its length & capacity set to
|
||||
// lenCap.
|
||||
//
|
||||
// TODO: when Go 1.16 or Go 1.17 is the minimum supported version,
|
||||
// update callers to use unsafe.Slice instead of this.
|
||||
func unsafeSlice(slicePtr, data unsafe.Pointer, lenCap int) {
|
||||
type sliceHeader struct {
|
||||
Data unsafe.Pointer
|
||||
Len int
|
||||
Cap int
|
||||
}
|
||||
h := (*sliceHeader)(slicePtr)
|
||||
h.Data = data
|
||||
h.Len = lenCap
|
||||
h.Cap = lenCap
|
||||
}
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
package wintun
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"log"
|
||||
"runtime"
|
||||
"syscall"
|
||||
@@ -23,175 +22,107 @@ const (
|
||||
logErr
|
||||
)
|
||||
|
||||
const (
|
||||
PoolNameMax = 256
|
||||
AdapterNameMax = 128
|
||||
)
|
||||
const AdapterNameMax = 128
|
||||
|
||||
type Pool [PoolNameMax]uint16
|
||||
type Adapter struct {
|
||||
handle uintptr
|
||||
}
|
||||
|
||||
var (
|
||||
modwintun = newLazyDLL("wintun.dll", setupLogger)
|
||||
|
||||
modwintun = newLazyDLL("wintun.dll", setupLogger)
|
||||
procWintunCreateAdapter = modwintun.NewProc("WintunCreateAdapter")
|
||||
procWintunDeleteAdapter = modwintun.NewProc("WintunDeleteAdapter")
|
||||
procWintunDeletePoolDriver = modwintun.NewProc("WintunDeletePoolDriver")
|
||||
procWintunEnumAdapters = modwintun.NewProc("WintunEnumAdapters")
|
||||
procWintunFreeAdapter = modwintun.NewProc("WintunFreeAdapter")
|
||||
procWintunOpenAdapter = modwintun.NewProc("WintunOpenAdapter")
|
||||
procWintunCloseAdapter = modwintun.NewProc("WintunCloseAdapter")
|
||||
procWintunDeleteDriver = modwintun.NewProc("WintunDeleteDriver")
|
||||
procWintunGetAdapterLUID = modwintun.NewProc("WintunGetAdapterLUID")
|
||||
procWintunGetAdapterName = modwintun.NewProc("WintunGetAdapterName")
|
||||
procWintunGetRunningDriverVersion = modwintun.NewProc("WintunGetRunningDriverVersion")
|
||||
procWintunSetAdapterName = modwintun.NewProc("WintunSetAdapterName")
|
||||
)
|
||||
|
||||
type TimestampedWriter interface {
|
||||
WriteWithTimestamp(p []byte, ts int64) (n int, err error)
|
||||
}
|
||||
|
||||
func logMessage(level loggerLevel, timestamp uint64, msg *uint16) int {
|
||||
if tw, ok := log.Default().Writer().(TimestampedWriter); ok {
|
||||
tw.WriteWithTimestamp([]byte(log.Default().Prefix()+windows.UTF16PtrToString(msg)), (int64(timestamp)-116444736000000000)*100)
|
||||
} else {
|
||||
log.Println(windows.UTF16PtrToString(msg))
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func setupLogger(dll *lazyDLL) {
|
||||
syscall.Syscall(dll.NewProc("WintunSetLogger").Addr(), 1, windows.NewCallback(func(level loggerLevel, msg *uint16) int {
|
||||
log.Println("[Wintun]", windows.UTF16PtrToString(msg))
|
||||
return 0
|
||||
}), 0, 0)
|
||||
var callback uintptr
|
||||
if runtime.GOARCH == "386" || runtime.GOARCH == "arm" {
|
||||
callback = windows.NewCallback(func(level loggerLevel, timestampLow, timestampHigh uint32, msg *uint16) int {
|
||||
return logMessage(level, uint64(timestampHigh)<<32|uint64(timestampLow), msg)
|
||||
})
|
||||
} else if runtime.GOARCH == "amd64" || runtime.GOARCH == "arm64" {
|
||||
callback = windows.NewCallback(logMessage)
|
||||
}
|
||||
syscall.Syscall(dll.NewProc("WintunSetLogger").Addr(), 1, callback, 0, 0)
|
||||
}
|
||||
|
||||
func MakePool(poolName string) (pool *Pool, err error) {
|
||||
poolName16, err := windows.UTF16FromString(poolName)
|
||||
func closeAdapter(wintun *Adapter) {
|
||||
syscall.Syscall(procWintunCloseAdapter.Addr(), 1, wintun.handle, 0, 0)
|
||||
}
|
||||
|
||||
// CreateAdapter creates a Wintun adapter. name is the cosmetic name of the adapter.
|
||||
// tunnelType represents the type of adapter and should be "Wintun". requestedGUID is
|
||||
// the GUID of the created network adapter, which then influences NLA generation
|
||||
// deterministically. If it is set to nil, the GUID is chosen by the system at random,
|
||||
// and hence a new NLA entry is created for each new adapter.
|
||||
func CreateAdapter(name string, tunnelType string, requestedGUID *windows.GUID) (wintun *Adapter, err error) {
|
||||
var name16 *uint16
|
||||
name16, err = windows.UTF16PtrFromString(name)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if len(poolName16) > PoolNameMax {
|
||||
err = errors.New("Pool name too long")
|
||||
var tunnelType16 *uint16
|
||||
tunnelType16, err = windows.UTF16PtrFromString(tunnelType)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
pool = &Pool{}
|
||||
copy(pool[:], poolName16)
|
||||
return
|
||||
}
|
||||
|
||||
func (pool *Pool) String() string {
|
||||
return windows.UTF16ToString(pool[:])
|
||||
}
|
||||
|
||||
func freeAdapter(wintun *Adapter) {
|
||||
syscall.Syscall(procWintunFreeAdapter.Addr(), 1, uintptr(wintun.handle), 0, 0)
|
||||
}
|
||||
|
||||
// OpenAdapter finds a Wintun adapter by its name. This function returns the adapter if found, or
|
||||
// windows.ERROR_FILE_NOT_FOUND otherwise. If the adapter is found but not a Wintun-class or a
|
||||
// member of the pool, this function returns windows.ERROR_ALREADY_EXISTS. The adapter must be
|
||||
// released after use.
|
||||
func (pool *Pool) OpenAdapter(ifname string) (wintun *Adapter, err error) {
|
||||
ifname16, err := windows.UTF16PtrFromString(ifname)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
r0, _, e1 := syscall.Syscall(procWintunOpenAdapter.Addr(), 2, uintptr(unsafe.Pointer(pool)), uintptr(unsafe.Pointer(ifname16)), 0)
|
||||
r0, _, e1 := syscall.Syscall(procWintunCreateAdapter.Addr(), 3, uintptr(unsafe.Pointer(name16)), uintptr(unsafe.Pointer(tunnelType16)), uintptr(unsafe.Pointer(requestedGUID)))
|
||||
if r0 == 0 {
|
||||
err = e1
|
||||
return
|
||||
}
|
||||
wintun = &Adapter{r0}
|
||||
runtime.SetFinalizer(wintun, freeAdapter)
|
||||
wintun = &Adapter{handle: r0}
|
||||
runtime.SetFinalizer(wintun, closeAdapter)
|
||||
return
|
||||
}
|
||||
|
||||
// CreateAdapter creates a Wintun adapter. ifname is the requested name of the adapter, while
|
||||
// requestedGUID is the GUID of the created network adapter, which then influences NLA generation
|
||||
// deterministically. If it is set to nil, the GUID is chosen by the system at random, and hence a
|
||||
// new NLA entry is created for each new adapter. It is called "requested" GUID because the API it
|
||||
// uses is completely undocumented, and so there could be minor interesting complications with its
|
||||
// usage. This function returns the network adapter ID and a flag if reboot is required.
|
||||
func (pool *Pool) CreateAdapter(ifname string, requestedGUID *windows.GUID) (wintun *Adapter, rebootRequired bool, err error) {
|
||||
var ifname16 *uint16
|
||||
ifname16, err = windows.UTF16PtrFromString(ifname)
|
||||
// OpenAdapter opens an existing Wintun adapter by name.
|
||||
func OpenAdapter(name string) (wintun *Adapter, err error) {
|
||||
var name16 *uint16
|
||||
name16, err = windows.UTF16PtrFromString(name)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
var _p0 uint32
|
||||
r0, _, e1 := syscall.Syscall6(procWintunCreateAdapter.Addr(), 4, uintptr(unsafe.Pointer(pool)), uintptr(unsafe.Pointer(ifname16)), uintptr(unsafe.Pointer(requestedGUID)), uintptr(unsafe.Pointer(&_p0)), 0, 0)
|
||||
rebootRequired = _p0 != 0
|
||||
r0, _, e1 := syscall.Syscall(procWintunOpenAdapter.Addr(), 1, uintptr(unsafe.Pointer(name16)), 0, 0)
|
||||
if r0 == 0 {
|
||||
err = e1
|
||||
return
|
||||
}
|
||||
wintun = &Adapter{r0}
|
||||
runtime.SetFinalizer(wintun, freeAdapter)
|
||||
wintun = &Adapter{handle: r0}
|
||||
runtime.SetFinalizer(wintun, closeAdapter)
|
||||
return
|
||||
}
|
||||
|
||||
// Delete deletes a Wintun adapter. This function succeeds if the adapter was not found. It returns
|
||||
// a bool indicating whether a reboot is required.
|
||||
func (wintun *Adapter) Delete(forceCloseSessions bool) (rebootRequired bool, err error) {
|
||||
var _p0 uint32
|
||||
if forceCloseSessions {
|
||||
_p0 = 1
|
||||
}
|
||||
var _p1 uint32
|
||||
r1, _, e1 := syscall.Syscall(procWintunDeleteAdapter.Addr(), 3, uintptr(wintun.handle), uintptr(_p0), uintptr(unsafe.Pointer(&_p1)))
|
||||
rebootRequired = _p1 != 0
|
||||
// Close closes a Wintun adapter.
|
||||
func (wintun *Adapter) Close() (err error) {
|
||||
runtime.SetFinalizer(wintun, nil)
|
||||
r1, _, e1 := syscall.Syscall(procWintunCloseAdapter.Addr(), 1, wintun.handle, 0, 0)
|
||||
if r1 == 0 {
|
||||
err = e1
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// DeleteMatchingAdapters deletes all Wintun adapters, which match
|
||||
// given criteria, and returns which ones it deleted, whether a reboot
|
||||
// is required after, and which errors occurred during the process.
|
||||
func (pool *Pool) DeleteMatchingAdapters(matches func(adapter *Adapter) bool, forceCloseSessions bool) (rebootRequired bool, errors []error) {
|
||||
cb := func(handle uintptr, _ uintptr) int {
|
||||
adapter := &Adapter{handle}
|
||||
if !matches(adapter) {
|
||||
return 1
|
||||
}
|
||||
rebootRequired2, err := adapter.Delete(forceCloseSessions)
|
||||
if err != nil {
|
||||
errors = append(errors, err)
|
||||
return 1
|
||||
}
|
||||
rebootRequired = rebootRequired || rebootRequired2
|
||||
return 1
|
||||
}
|
||||
r1, _, e1 := syscall.Syscall(procWintunEnumAdapters.Addr(), 3, uintptr(unsafe.Pointer(pool)), uintptr(windows.NewCallback(cb)), 0)
|
||||
if r1 == 0 {
|
||||
errors = append(errors, e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Name returns the name of the Wintun adapter.
|
||||
func (wintun *Adapter) Name() (ifname string, err error) {
|
||||
var ifname16 [AdapterNameMax]uint16
|
||||
r1, _, e1 := syscall.Syscall(procWintunGetAdapterName.Addr(), 2, uintptr(wintun.handle), uintptr(unsafe.Pointer(&ifname16[0])), 0)
|
||||
if r1 == 0 {
|
||||
err = e1
|
||||
return
|
||||
}
|
||||
ifname = windows.UTF16ToString(ifname16[:])
|
||||
return
|
||||
}
|
||||
|
||||
// DeleteDriver deletes all Wintun adapters in a pool and if there are no more adapters in any other
|
||||
// pools, also removes Wintun from the driver store, usually called by uninstallers.
|
||||
func (pool *Pool) DeleteDriver() (rebootRequired bool, err error) {
|
||||
var _p0 uint32
|
||||
r1, _, e1 := syscall.Syscall(procWintunDeletePoolDriver.Addr(), 2, uintptr(unsafe.Pointer(pool)), uintptr(unsafe.Pointer(&_p0)), 0)
|
||||
rebootRequired = _p0 != 0
|
||||
if r1 == 0 {
|
||||
err = e1
|
||||
}
|
||||
return
|
||||
|
||||
}
|
||||
|
||||
// SetName sets name of the Wintun adapter.
|
||||
func (wintun *Adapter) SetName(ifname string) (err error) {
|
||||
ifname16, err := windows.UTF16FromString(ifname)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
r1, _, e1 := syscall.Syscall(procWintunSetAdapterName.Addr(), 2, uintptr(wintun.handle), uintptr(unsafe.Pointer(&ifname16[0])), 0)
|
||||
// Uninstall removes the driver from the system if no drivers are currently in use.
|
||||
func Uninstall() (err error) {
|
||||
r1, _, e1 := syscall.Syscall(procWintunDeleteDriver.Addr(), 0, 0, 0, 0)
|
||||
if r1 == 0 {
|
||||
err = e1
|
||||
}
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
package main
|
||||
|
||||
const Version = "0.0.20210424"
|
||||
const Version = "0.0.20211016"
|
||||
|
||||
Reference in New Issue
Block a user