Compare commits
20 Commits
0.0.202103
...
0.0.202104
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c9db4b7aaa | ||
|
|
3625f8d284 | ||
|
|
0687dc06c8 | ||
|
|
71aefa374d | ||
|
|
3d3e30beb8 | ||
|
|
b0e5b19969 | ||
|
|
3988821442 | ||
|
|
c7cd2c9eab | ||
|
|
54dbe2471f | ||
|
|
d2fd0c0cc0 | ||
|
|
5f6bbe4ae8 | ||
|
|
75526d6071 | ||
|
|
fbf97502cf | ||
|
|
10533c3e73 | ||
|
|
8ed83e0427 | ||
|
|
6228659a91 | ||
|
|
517f0703f5 | ||
|
|
204140016a | ||
|
|
822f5a6d70 | ||
|
|
02e419ed8a |
@@ -55,10 +55,11 @@ func (endpoint *LinuxSocketEndpoint) dst6() *unix.SockaddrInet6 {
|
||||
|
||||
// LinuxSocketBind uses sendmsg and recvmsg to implement a full bind with sticky sockets on Linux.
|
||||
type LinuxSocketBind struct {
|
||||
// mu guards sock4 and sock6 and the associated fds.
|
||||
// As long as someone holds mu (read or write), the associated fds are valid.
|
||||
mu sync.RWMutex
|
||||
sock4 int
|
||||
sock6 int
|
||||
lastMark uint32
|
||||
closing sync.RWMutex
|
||||
}
|
||||
|
||||
func NewLinuxSocketBind() Bind { return &LinuxSocketBind{sock4: -1, sock6: -1} }
|
||||
@@ -102,54 +103,67 @@ func (*LinuxSocketBind) ParseEndpoint(s string) (Endpoint, error) {
|
||||
return nil, errors.New("invalid IP address")
|
||||
}
|
||||
|
||||
func (bind *LinuxSocketBind) Open(port uint16) (uint16, error) {
|
||||
func (bind *LinuxSocketBind) Open(port uint16) ([]ReceiveFunc, uint16, error) {
|
||||
bind.mu.Lock()
|
||||
defer bind.mu.Unlock()
|
||||
|
||||
var err error
|
||||
var newPort uint16
|
||||
var tries int
|
||||
|
||||
if bind.sock4 != -1 || bind.sock6 != -1 {
|
||||
return 0, ErrBindAlreadyOpen
|
||||
return nil, 0, ErrBindAlreadyOpen
|
||||
}
|
||||
|
||||
originalPort := port
|
||||
|
||||
again:
|
||||
port = originalPort
|
||||
var sock4, sock6 int
|
||||
// Attempt ipv6 bind, update port if successful.
|
||||
bind.sock6, newPort, err = create6(port)
|
||||
sock6, newPort, err = create6(port)
|
||||
if err != nil {
|
||||
if err != syscall.EAFNOSUPPORT {
|
||||
return 0, err
|
||||
if !errors.Is(err, syscall.EAFNOSUPPORT) {
|
||||
return nil, 0, err
|
||||
}
|
||||
} else {
|
||||
port = newPort
|
||||
}
|
||||
|
||||
// Attempt ipv4 bind, update port if successful.
|
||||
bind.sock4, newPort, err = create4(port)
|
||||
sock4, newPort, err = create4(port)
|
||||
if err != nil {
|
||||
if originalPort == 0 && err == syscall.EADDRINUSE && tries < 100 {
|
||||
unix.Close(bind.sock6)
|
||||
if originalPort == 0 && errors.Is(err, syscall.EADDRINUSE) && tries < 100 {
|
||||
unix.Close(sock6)
|
||||
tries++
|
||||
goto again
|
||||
}
|
||||
if err != syscall.EAFNOSUPPORT {
|
||||
unix.Close(bind.sock6)
|
||||
return 0, err
|
||||
if !errors.Is(err, syscall.EAFNOSUPPORT) {
|
||||
unix.Close(sock6)
|
||||
return nil, 0, err
|
||||
}
|
||||
} else {
|
||||
port = newPort
|
||||
}
|
||||
|
||||
if bind.sock4 == -1 && bind.sock6 == -1 {
|
||||
return 0, syscall.EAFNOSUPPORT
|
||||
var fns []ReceiveFunc
|
||||
if sock4 != -1 {
|
||||
fns = append(fns, bind.makeReceiveIPv4(sock4))
|
||||
bind.sock4 = sock4
|
||||
}
|
||||
return port, nil
|
||||
if sock6 != -1 {
|
||||
fns = append(fns, bind.makeReceiveIPv6(sock6))
|
||||
bind.sock6 = sock6
|
||||
}
|
||||
if len(fns) == 0 {
|
||||
return nil, 0, syscall.EAFNOSUPPORT
|
||||
}
|
||||
return fns, port, nil
|
||||
}
|
||||
|
||||
func (bind *LinuxSocketBind) SetMark(value uint32) error {
|
||||
bind.closing.RLock()
|
||||
defer bind.closing.RUnlock()
|
||||
bind.mu.RLock()
|
||||
defer bind.mu.RUnlock()
|
||||
|
||||
if bind.sock6 != -1 {
|
||||
err := unix.SetsockoptInt(
|
||||
@@ -177,21 +191,24 @@ func (bind *LinuxSocketBind) SetMark(value uint32) error {
|
||||
}
|
||||
}
|
||||
|
||||
bind.lastMark = value
|
||||
return nil
|
||||
}
|
||||
|
||||
func (bind *LinuxSocketBind) Close() error {
|
||||
var err1, err2 error
|
||||
bind.closing.RLock()
|
||||
// Take a readlock to shut down the sockets...
|
||||
bind.mu.RLock()
|
||||
if bind.sock6 != -1 {
|
||||
unix.Shutdown(bind.sock6, unix.SHUT_RDWR)
|
||||
}
|
||||
if bind.sock4 != -1 {
|
||||
unix.Shutdown(bind.sock4, unix.SHUT_RDWR)
|
||||
}
|
||||
bind.closing.RUnlock()
|
||||
bind.closing.Lock()
|
||||
bind.mu.RUnlock()
|
||||
// ...and a write lock to close the fd.
|
||||
// This ensures that no one else is using the fd.
|
||||
bind.mu.Lock()
|
||||
defer bind.mu.Unlock()
|
||||
var err1, err2 error
|
||||
if bind.sock6 != -1 {
|
||||
err1 = unix.Close(bind.sock6)
|
||||
bind.sock6 = -1
|
||||
@@ -200,7 +217,6 @@ func (bind *LinuxSocketBind) Close() error {
|
||||
err2 = unix.Close(bind.sock4)
|
||||
bind.sock4 = -1
|
||||
}
|
||||
bind.closing.Unlock()
|
||||
|
||||
if err1 != nil {
|
||||
return err1
|
||||
@@ -208,46 +224,29 @@ func (bind *LinuxSocketBind) Close() error {
|
||||
return err2
|
||||
}
|
||||
|
||||
func (bind *LinuxSocketBind) ReceiveIPv6(buff []byte) (int, Endpoint, error) {
|
||||
bind.closing.RLock()
|
||||
defer bind.closing.RUnlock()
|
||||
|
||||
func (*LinuxSocketBind) makeReceiveIPv6(sock int) ReceiveFunc {
|
||||
return func(buff []byte) (int, Endpoint, error) {
|
||||
var end LinuxSocketEndpoint
|
||||
if bind.sock6 == -1 {
|
||||
return 0, nil, net.ErrClosed
|
||||
}
|
||||
n, err := receive6(
|
||||
bind.sock6,
|
||||
buff,
|
||||
&end,
|
||||
)
|
||||
n, err := receive6(sock, buff, &end)
|
||||
return n, &end, err
|
||||
}
|
||||
|
||||
func (bind *LinuxSocketBind) ReceiveIPv4(buff []byte) (int, Endpoint, error) {
|
||||
bind.closing.RLock()
|
||||
defer bind.closing.RUnlock()
|
||||
|
||||
var end LinuxSocketEndpoint
|
||||
if bind.sock4 == -1 {
|
||||
return 0, nil, net.ErrClosed
|
||||
}
|
||||
n, err := receive4(
|
||||
bind.sock4,
|
||||
buff,
|
||||
&end,
|
||||
)
|
||||
|
||||
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) Send(buff []byte, end Endpoint) error {
|
||||
bind.closing.RLock()
|
||||
defer bind.closing.RUnlock()
|
||||
|
||||
nend, ok := end.(*LinuxSocketEndpoint)
|
||||
if !ok {
|
||||
return ErrWrongEndpointType
|
||||
}
|
||||
bind.mu.RLock()
|
||||
defer bind.mu.RUnlock()
|
||||
if !nend.isV6 {
|
||||
if bind.sock4 == -1 {
|
||||
return net.ErrClosed
|
||||
|
||||
@@ -8,6 +8,7 @@ package conn
|
||||
import (
|
||||
"errors"
|
||||
"net"
|
||||
"sync"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
@@ -16,6 +17,7 @@ import (
|
||||
// It uses the Go's net package to implement networking.
|
||||
// See LinuxSocketBind for a proper implementation on the Linux platform.
|
||||
type StdNetBind struct {
|
||||
mu sync.Mutex // protects following fields
|
||||
ipv4 *net.UDPConn
|
||||
ipv6 *net.UDPConn
|
||||
blackhole4 bool
|
||||
@@ -81,44 +83,58 @@ func listenNet(network string, port int) (*net.UDPConn, int, error) {
|
||||
return conn, uaddr.Port, nil
|
||||
}
|
||||
|
||||
func (bind *StdNetBind) Open(uport uint16) (uint16, error) {
|
||||
func (bind *StdNetBind) Open(uport uint16) ([]ReceiveFunc, uint16, error) {
|
||||
bind.mu.Lock()
|
||||
defer bind.mu.Unlock()
|
||||
|
||||
var err error
|
||||
var tries int
|
||||
|
||||
if bind.ipv4 != nil || bind.ipv6 != nil {
|
||||
return 0, ErrBindAlreadyOpen
|
||||
return nil, 0, ErrBindAlreadyOpen
|
||||
}
|
||||
|
||||
// Attempt to open ipv4 and ipv6 listeners on the same port.
|
||||
// If uport is 0, we can retry on failure.
|
||||
again:
|
||||
port := int(uport)
|
||||
var ipv4, ipv6 *net.UDPConn
|
||||
|
||||
bind.ipv4, port, err = listenNet("udp4", port)
|
||||
ipv4, port, err = listenNet("udp4", port)
|
||||
if err != nil && !errors.Is(err, syscall.EAFNOSUPPORT) {
|
||||
bind.ipv4 = nil
|
||||
return 0, err
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
bind.ipv6, port, err = listenNet("udp6", port)
|
||||
if uport == 0 && err != nil && errors.Is(err, syscall.EADDRINUSE) && tries < 100 {
|
||||
bind.ipv4.Close()
|
||||
bind.ipv4 = nil
|
||||
bind.ipv6 = nil
|
||||
// Listen on the same port as we're using for ipv4.
|
||||
ipv6, port, err = listenNet("udp6", port)
|
||||
if uport == 0 && errors.Is(err, syscall.EADDRINUSE) && tries < 100 {
|
||||
ipv4.Close()
|
||||
tries++
|
||||
goto again
|
||||
}
|
||||
if err != nil && !errors.Is(err, syscall.EAFNOSUPPORT) {
|
||||
bind.ipv4.Close()
|
||||
bind.ipv4 = nil
|
||||
bind.ipv6 = nil
|
||||
return 0, err
|
||||
ipv4.Close()
|
||||
return nil, 0, err
|
||||
}
|
||||
if bind.ipv4 == nil && bind.ipv6 == nil {
|
||||
return 0, syscall.EAFNOSUPPORT
|
||||
var fns []ReceiveFunc
|
||||
if ipv4 != nil {
|
||||
fns = append(fns, bind.makeReceiveIPv4(ipv4))
|
||||
bind.ipv4 = ipv4
|
||||
}
|
||||
return uint16(port), nil
|
||||
if ipv6 != nil {
|
||||
fns = append(fns, bind.makeReceiveIPv6(ipv6))
|
||||
bind.ipv6 = ipv6
|
||||
}
|
||||
if len(fns) == 0 {
|
||||
return nil, 0, syscall.EAFNOSUPPORT
|
||||
}
|
||||
return fns, uint16(port), nil
|
||||
}
|
||||
|
||||
func (bind *StdNetBind) Close() error {
|
||||
bind.mu.Lock()
|
||||
defer bind.mu.Unlock()
|
||||
|
||||
var err1, err2 error
|
||||
if bind.ipv4 != nil {
|
||||
err1 = bind.ipv4.Close()
|
||||
@@ -136,24 +152,22 @@ func (bind *StdNetBind) Close() error {
|
||||
return err2
|
||||
}
|
||||
|
||||
func (bind *StdNetBind) ReceiveIPv4(buff []byte) (int, Endpoint, error) {
|
||||
if bind.ipv4 == nil {
|
||||
return 0, nil, syscall.EAFNOSUPPORT
|
||||
}
|
||||
n, endpoint, err := bind.ipv4.ReadFromUDP(buff)
|
||||
func (*StdNetBind) makeReceiveIPv4(conn *net.UDPConn) ReceiveFunc {
|
||||
return func(buff []byte) (int, Endpoint, error) {
|
||||
n, endpoint, err := conn.ReadFromUDP(buff)
|
||||
if endpoint != nil {
|
||||
endpoint.IP = endpoint.IP.To4()
|
||||
}
|
||||
return n, (*StdNetEndpoint)(endpoint), err
|
||||
}
|
||||
|
||||
func (bind *StdNetBind) ReceiveIPv6(buff []byte) (int, Endpoint, error) {
|
||||
if bind.ipv6 == nil {
|
||||
return 0, nil, syscall.EAFNOSUPPORT
|
||||
}
|
||||
n, endpoint, err := bind.ipv6.ReadFromUDP(buff)
|
||||
|
||||
func (*StdNetBind) makeReceiveIPv6(conn *net.UDPConn) ReceiveFunc {
|
||||
return func(buff []byte) (int, Endpoint, error) {
|
||||
n, endpoint, err := conn.ReadFromUDP(buff)
|
||||
return n, (*StdNetEndpoint)(endpoint), err
|
||||
}
|
||||
}
|
||||
|
||||
func (bind *StdNetBind) Send(buff []byte, endpoint Endpoint) error {
|
||||
var err error
|
||||
@@ -161,22 +175,22 @@ func (bind *StdNetBind) Send(buff []byte, endpoint Endpoint) error {
|
||||
if !ok {
|
||||
return ErrWrongEndpointType
|
||||
}
|
||||
if nend.IP.To4() != nil {
|
||||
if bind.ipv4 == nil {
|
||||
return syscall.EAFNOSUPPORT
|
||||
|
||||
bind.mu.Lock()
|
||||
blackhole := bind.blackhole4
|
||||
conn := bind.ipv4
|
||||
if nend.IP.To4() == nil {
|
||||
blackhole = bind.blackhole6
|
||||
conn = bind.ipv6
|
||||
}
|
||||
if bind.blackhole4 {
|
||||
bind.mu.Unlock()
|
||||
|
||||
if blackhole {
|
||||
return nil
|
||||
}
|
||||
_, err = bind.ipv4.WriteToUDP(buff, (*net.UDPAddr)(nend))
|
||||
} else {
|
||||
if bind.ipv6 == nil {
|
||||
if conn == nil {
|
||||
return syscall.EAFNOSUPPORT
|
||||
}
|
||||
if bind.blackhole6 {
|
||||
return nil
|
||||
}
|
||||
_, err = bind.ipv6.WriteToUDP(buff, (*net.UDPAddr)(nend))
|
||||
}
|
||||
_, err = conn.WriteToUDP(buff, (*net.UDPAddr)(nend))
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -47,7 +47,7 @@ func (rb *ringBuffer) Push() *ringPacket {
|
||||
}
|
||||
ret := (*ringPacket)(unsafe.Pointer(rb.packets + (uintptr(rb.tail%packetsPerRing) * unsafe.Sizeof(ringPacket{}))))
|
||||
rb.tail += 1
|
||||
if rb.tail == rb.head {
|
||||
if rb.tail%packetsPerRing == rb.head%packetsPerRing {
|
||||
rb.isFull = true
|
||||
}
|
||||
return ret
|
||||
@@ -197,6 +197,9 @@ func (ring *ringBuffer) CloseAndZero() {
|
||||
windows.VirtualFree(ring.packets, 0, windows.MEM_RELEASE)
|
||||
ring.packets = 0
|
||||
}
|
||||
ring.head = 0
|
||||
ring.tail = 0
|
||||
ring.isFull = false
|
||||
}
|
||||
|
||||
func (bind *afWinRingBind) CloseAndZero() {
|
||||
@@ -266,7 +269,7 @@ func (bind *afWinRingBind) Open(family int32, sa windows.Sockaddr) (windows.Sock
|
||||
return sa, nil
|
||||
}
|
||||
|
||||
func (bind *WinRingBind) Open(port uint16) (selectedPort uint16, err error) {
|
||||
func (bind *WinRingBind) Open(port uint16) (recvFns []ReceiveFunc, selectedPort uint16, err error) {
|
||||
bind.mu.Lock()
|
||||
defer bind.mu.Unlock()
|
||||
defer func() {
|
||||
@@ -275,30 +278,30 @@ func (bind *WinRingBind) Open(port uint16) (selectedPort uint16, err error) {
|
||||
}
|
||||
}()
|
||||
if atomic.LoadUint32(&bind.isOpen) != 0 {
|
||||
return 0, ErrBindAlreadyOpen
|
||||
return nil, 0, ErrBindAlreadyOpen
|
||||
}
|
||||
var sa windows.Sockaddr
|
||||
sa, err = bind.v4.Open(windows.AF_INET, &windows.SockaddrInet4{Port: int(port)})
|
||||
if err != nil {
|
||||
return 0, err
|
||||
return nil, 0, err
|
||||
}
|
||||
sa, err = bind.v6.Open(windows.AF_INET6, &windows.SockaddrInet6{Port: sa.(*windows.SockaddrInet4).Port})
|
||||
if err != nil {
|
||||
return 0, err
|
||||
return nil, 0, err
|
||||
}
|
||||
selectedPort = uint16(sa.(*windows.SockaddrInet6).Port)
|
||||
for i := 0; i < packetsPerRing; i++ {
|
||||
err = bind.v4.InsertReceiveRequest()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
return nil, 0, err
|
||||
}
|
||||
err = bind.v6.InsertReceiveRequest()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
return nil, 0, err
|
||||
}
|
||||
}
|
||||
atomic.StoreUint32(&bind.isOpen, 1)
|
||||
return
|
||||
return []ReceiveFunc{bind.receiveIPv4, bind.receiveIPv6}, selectedPort, err
|
||||
}
|
||||
|
||||
func (bind *WinRingBind) Close() error {
|
||||
@@ -395,13 +398,13 @@ func (bind *afWinRingBind) Receive(buf []byte, isOpen *uint32) (int, Endpoint, e
|
||||
return n, &ep, nil
|
||||
}
|
||||
|
||||
func (bind *WinRingBind) ReceiveIPv4(buf []byte) (int, Endpoint, error) {
|
||||
func (bind *WinRingBind) receiveIPv4(buf []byte) (int, Endpoint, error) {
|
||||
bind.mu.RLock()
|
||||
defer bind.mu.RUnlock()
|
||||
return bind.v4.Receive(buf, &bind.isOpen)
|
||||
}
|
||||
|
||||
func (bind *WinRingBind) ReceiveIPv6(buf []byte) (int, Endpoint, error) {
|
||||
func (bind *WinRingBind) receiveIPv6(buf []byte) (int, Endpoint, error) {
|
||||
bind.mu.RLock()
|
||||
defer bind.mu.RUnlock()
|
||||
return bind.v6.Receive(buf, &bind.isOpen)
|
||||
@@ -482,6 +485,8 @@ func (bind *WinRingBind) Send(buf []byte, endpoint Endpoint) error {
|
||||
}
|
||||
|
||||
func (bind *StdNetBind) BindSocketToInterface4(interfaceIndex uint32, blackhole bool) error {
|
||||
bind.mu.Lock()
|
||||
defer bind.mu.Unlock()
|
||||
sysconn, err := bind.ipv4.SyscallConn()
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -500,6 +505,8 @@ func (bind *StdNetBind) BindSocketToInterface4(interfaceIndex uint32, blackhole
|
||||
}
|
||||
|
||||
func (bind *StdNetBind) BindSocketToInterface6(interfaceIndex uint32, blackhole bool) error {
|
||||
bind.mu.Lock()
|
||||
defer bind.mu.Unlock()
|
||||
sysconn, err := bind.ipv6.SyscallConn()
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@@ -65,12 +65,14 @@ func (c ChannelEndpoint) DstIP() net.IP { return net.IPv4(127, 0, 0, 1) }
|
||||
|
||||
func (c ChannelEndpoint) SrcIP() net.IP { return nil }
|
||||
|
||||
func (c *ChannelBind) Open(port uint16) (actualPort uint16, err error) {
|
||||
func (c *ChannelBind) Open(port uint16) (fns []conn.ReceiveFunc, actualPort uint16, err error) {
|
||||
c.closeSignal = make(chan bool)
|
||||
fns = append(fns, c.makeReceiveFunc(*c.rx4))
|
||||
fns = append(fns, c.makeReceiveFunc(*c.rx6))
|
||||
if rand.Uint32()&1 == 0 {
|
||||
return uint16(c.source4), nil
|
||||
return fns, uint16(c.source4), nil
|
||||
} else {
|
||||
return uint16(c.source6), nil
|
||||
return fns, uint16(c.source6), nil
|
||||
}
|
||||
}
|
||||
|
||||
@@ -87,22 +89,15 @@ func (c *ChannelBind) Close() error {
|
||||
|
||||
func (c *ChannelBind) SetMark(mark uint32) error { return nil }
|
||||
|
||||
func (c *ChannelBind) ReceiveIPv6(b []byte) (n int, ep conn.Endpoint, err error) {
|
||||
func (c *ChannelBind) makeReceiveFunc(ch chan []byte) conn.ReceiveFunc {
|
||||
return func(b []byte) (n int, ep conn.Endpoint, err error) {
|
||||
select {
|
||||
case <-c.closeSignal:
|
||||
return 0, nil, net.ErrClosed
|
||||
case rx := <-*c.rx6:
|
||||
case rx := <-ch:
|
||||
return copy(b, rx), c.target6, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (c *ChannelBind) ReceiveIPv4(b []byte) (n int, ep conn.Endpoint, err error) {
|
||||
select {
|
||||
case <-c.closeSignal:
|
||||
return 0, nil, net.ErrClosed
|
||||
case rx := <-*c.rx4:
|
||||
return copy(b, rx), c.target4, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (c *ChannelBind) Send(b []byte, ep conn.Endpoint) error {
|
||||
|
||||
73
conn/conn.go
73
conn/conn.go
@@ -8,10 +8,18 @@ package conn
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"reflect"
|
||||
"runtime"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// A ReceiveFunc receives a single inbound packet from the network.
|
||||
// It writes the data into b. n is the length of the packet.
|
||||
// ep is the remote endpoint.
|
||||
type ReceiveFunc func(b []byte) (n int, ep Endpoint, err error)
|
||||
|
||||
// A Bind listens on a port for both IPv6 and IPv4 UDP traffic.
|
||||
//
|
||||
// A Bind interface may also be a PeekLookAtSocketFd or BindSocketToInterface,
|
||||
@@ -19,23 +27,17 @@ import (
|
||||
type Bind interface {
|
||||
// Open puts the Bind into a listening state on a given port and reports the actual
|
||||
// port that it bound to. Passing zero results in a random selection.
|
||||
Open(port uint16) (actualPort uint16, err error)
|
||||
// fns is the set of functions that will be called to receive packets.
|
||||
Open(port uint16) (fns []ReceiveFunc, actualPort uint16, err error)
|
||||
|
||||
// Close closes the Bind listener.
|
||||
// All fns returned by Open must return net.ErrClosed after a call to Close.
|
||||
Close() error
|
||||
|
||||
// SetMark sets the mark for each packet sent through this Bind.
|
||||
// This mark is passed to the kernel as the socket option SO_MARK.
|
||||
SetMark(mark uint32) error
|
||||
|
||||
// ReceiveIPv6 reads an IPv6 UDP packet into b. It reports the number of bytes read,
|
||||
// n, the packet source address ep, and any error.
|
||||
ReceiveIPv6(b []byte) (n int, ep Endpoint, err error)
|
||||
|
||||
// ReceiveIPv4 reads an IPv4 UDP packet into b. It reports the number of bytes read,
|
||||
// n, the packet source address ep, and any error.
|
||||
ReceiveIPv4(b []byte) (n int, ep Endpoint, err error)
|
||||
|
||||
// Send writes a packet b to address ep.
|
||||
Send(b []byte, ep Endpoint) error
|
||||
|
||||
@@ -70,6 +72,54 @@ type Endpoint interface {
|
||||
SrcIP() net.IP
|
||||
}
|
||||
|
||||
var (
|
||||
ErrBindAlreadyOpen = errors.New("bind is already open")
|
||||
ErrWrongEndpointType = errors.New("endpoint type does not correspond with bind type")
|
||||
)
|
||||
|
||||
func (fn ReceiveFunc) PrettyName() string {
|
||||
name := runtime.FuncForPC(reflect.ValueOf(fn).Pointer()).Name()
|
||||
// 0. cheese/taco.beansIPv6.func12.func21218-fm
|
||||
name = strings.TrimSuffix(name, "-fm")
|
||||
// 1. cheese/taco.beansIPv6.func12.func21218
|
||||
if idx := strings.LastIndexByte(name, '/'); idx != -1 {
|
||||
name = name[idx+1:]
|
||||
// 2. taco.beansIPv6.func12.func21218
|
||||
}
|
||||
for {
|
||||
var idx int
|
||||
for idx = len(name) - 1; idx >= 0; idx-- {
|
||||
if name[idx] < '0' || name[idx] > '9' {
|
||||
break
|
||||
}
|
||||
}
|
||||
if idx == len(name)-1 {
|
||||
break
|
||||
}
|
||||
const dotFunc = ".func"
|
||||
if !strings.HasSuffix(name[:idx+1], dotFunc) {
|
||||
break
|
||||
}
|
||||
name = name[:idx+1-len(dotFunc)]
|
||||
// 3. taco.beansIPv6.func12
|
||||
// 4. taco.beansIPv6
|
||||
}
|
||||
if idx := strings.LastIndexByte(name, '.'); idx != -1 {
|
||||
name = name[idx+1:]
|
||||
// 5. beansIPv6
|
||||
}
|
||||
if name == "" {
|
||||
return fmt.Sprintf("%p", fn)
|
||||
}
|
||||
if strings.HasSuffix(name, "IPv4") {
|
||||
return "v4"
|
||||
}
|
||||
if strings.HasSuffix(name, "IPv6") {
|
||||
return "v6"
|
||||
}
|
||||
return name
|
||||
}
|
||||
|
||||
func parseEndpoint(s string) (*net.UDPAddr, error) {
|
||||
// ensure that the host is an IP address
|
||||
|
||||
@@ -99,8 +149,3 @@ func parseEndpoint(s string) (*net.UDPAddr, error) {
|
||||
}
|
||||
return addr, err
|
||||
}
|
||||
|
||||
var (
|
||||
ErrBindAlreadyOpen = errors.New("bind is already open")
|
||||
ErrWrongEndpointType = errors.New("endpoint type does not correspond with bind type")
|
||||
)
|
||||
|
||||
@@ -118,9 +118,17 @@ func Initialize() bool {
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// While we should be able to stop here, after getting the function pointers, some anti-virus actually causes
|
||||
// failures in RIOCreateRequestQueue, so keep going to be certain this is supported.
|
||||
cq, err = CreatePolledCompletionQueue(2)
|
||||
var iocp windows.Handle
|
||||
iocp, err = windows.CreateIoCompletionPort(windows.InvalidHandle, 0, 0, 0)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer windows.CloseHandle(iocp)
|
||||
var overlapped windows.Overlapped
|
||||
cq, err = CreateIOCPCompletionQueue(2, iocp, 0, &overlapped)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
@@ -161,6 +169,7 @@ func CreateIOCPCompletionQueue(queueSize uint32, iocp windows.Handle, key uintpt
|
||||
notificationCompletion := &iocpNotificationCompletion{
|
||||
completionType: iocpCompletion,
|
||||
iocp: iocp,
|
||||
key: key,
|
||||
overlapped: overlapped,
|
||||
}
|
||||
ret, _, err := syscall.Syscall(extensionFunctionTable.rioCreateCompletionQueue, 2, uintptr(queueSize), uintptr(unsafe.Pointer(notificationCompletion)), 0)
|
||||
|
||||
@@ -11,9 +11,6 @@ import (
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"golang.org/x/net/ipv4"
|
||||
"golang.org/x/net/ipv6"
|
||||
|
||||
"golang.zx2c4.com/wireguard/conn"
|
||||
"golang.zx2c4.com/wireguard/ratelimiter"
|
||||
"golang.zx2c4.com/wireguard/rwcancel"
|
||||
@@ -400,7 +397,9 @@ func (device *Device) SendKeepalivesToPeersWithCurrentKeypair() {
|
||||
device.peers.RUnlock()
|
||||
}
|
||||
|
||||
func unsafeCloseBind(device *Device) error {
|
||||
// closeBindLocked closes the device's net.bind.
|
||||
// The caller must hold the net mutex.
|
||||
func closeBindLocked(device *Device) error {
|
||||
var err error
|
||||
netc := &device.net
|
||||
if netc.netlinkCancel != nil {
|
||||
@@ -455,7 +454,7 @@ func (device *Device) BindUpdate() error {
|
||||
defer device.net.Unlock()
|
||||
|
||||
// close existing sockets
|
||||
if err := unsafeCloseBind(device); err != nil {
|
||||
if err := closeBindLocked(device); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -466,8 +465,9 @@ func (device *Device) BindUpdate() error {
|
||||
|
||||
// bind to new port
|
||||
var err error
|
||||
var recvFns []conn.ReceiveFunc
|
||||
netc := &device.net
|
||||
netc.port, err = netc.bind.Open(netc.port)
|
||||
recvFns, netc.port, err = netc.bind.Open(netc.port)
|
||||
if err != nil {
|
||||
netc.port = 0
|
||||
return err
|
||||
@@ -499,11 +499,12 @@ func (device *Device) BindUpdate() error {
|
||||
device.peers.RUnlock()
|
||||
|
||||
// start receiving routines
|
||||
device.net.stopping.Add(2)
|
||||
device.queue.decryption.wg.Add(2) // each RoutineReceiveIncoming goroutine writes to device.queue.decryption
|
||||
device.queue.handshake.wg.Add(2) // each RoutineReceiveIncoming goroutine writes to device.queue.handshake
|
||||
go device.RoutineReceiveIncoming(ipv4.Version, netc.bind)
|
||||
go device.RoutineReceiveIncoming(ipv6.Version, netc.bind)
|
||||
device.net.stopping.Add(len(recvFns))
|
||||
device.queue.decryption.wg.Add(len(recvFns)) // each RoutineReceiveIncoming goroutine writes to device.queue.decryption
|
||||
device.queue.handshake.wg.Add(len(recvFns)) // each RoutineReceiveIncoming goroutine writes to device.queue.handshake
|
||||
for _, fn := range recvFns {
|
||||
go device.RoutineReceiveIncoming(fn)
|
||||
}
|
||||
|
||||
device.log.Verbosef("UDP bind has been updated")
|
||||
return nil
|
||||
@@ -511,7 +512,7 @@ func (device *Device) BindUpdate() error {
|
||||
|
||||
func (device *Device) BindClose() error {
|
||||
device.net.Lock()
|
||||
err := unsafeCloseBind(device)
|
||||
err := closeBindLocked(device)
|
||||
device.net.Unlock()
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -9,8 +9,8 @@ func (device *Device) DisableSomeRoamingForBrokenMobileSemantics() {
|
||||
device.peers.RLock()
|
||||
for _, peer := range device.peers.keyMap {
|
||||
peer.Lock()
|
||||
defer peer.Unlock()
|
||||
peer.disableRoaming = peer.endpoint != nil
|
||||
peer.Unlock()
|
||||
}
|
||||
device.peers.RUnlock()
|
||||
}
|
||||
|
||||
@@ -68,15 +68,16 @@ func (peer *Peer) keepKeyFreshReceiving() {
|
||||
* Every time the bind is updated a new routine is started for
|
||||
* IPv4 and IPv6 (separately)
|
||||
*/
|
||||
func (device *Device) RoutineReceiveIncoming(IP int, bind conn.Bind) {
|
||||
func (device *Device) RoutineReceiveIncoming(recv conn.ReceiveFunc) {
|
||||
recvName := recv.PrettyName()
|
||||
defer func() {
|
||||
device.log.Verbosef("Routine: receive incoming IPv%d - stopped", IP)
|
||||
device.log.Verbosef("Routine: receive incoming %s - stopped", recvName)
|
||||
device.queue.decryption.wg.Done()
|
||||
device.queue.handshake.wg.Done()
|
||||
device.net.stopping.Done()
|
||||
}()
|
||||
|
||||
device.log.Verbosef("Routine: receive incoming IPv%d - started", IP)
|
||||
device.log.Verbosef("Routine: receive incoming %s - started", recvName)
|
||||
|
||||
// receive datagrams until conn is closed
|
||||
|
||||
@@ -90,24 +91,21 @@ func (device *Device) RoutineReceiveIncoming(IP int, bind conn.Bind) {
|
||||
)
|
||||
|
||||
for {
|
||||
switch IP {
|
||||
case ipv4.Version:
|
||||
size, endpoint, err = bind.ReceiveIPv4(buffer[:])
|
||||
case ipv6.Version:
|
||||
size, endpoint, err = bind.ReceiveIPv6(buffer[:])
|
||||
default:
|
||||
panic("invalid IP version")
|
||||
}
|
||||
size, endpoint, err = recv(buffer[:])
|
||||
|
||||
if err != nil {
|
||||
device.PutMessageBuffer(buffer)
|
||||
if errors.Is(err, net.ErrClosed) {
|
||||
return
|
||||
}
|
||||
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)
|
||||
buffer = device.GetMessageBuffer()
|
||||
continue
|
||||
}
|
||||
return
|
||||
|
||||
23
main.go
23
main.go
@@ -33,25 +33,28 @@ const (
|
||||
)
|
||||
|
||||
func printUsage() {
|
||||
fmt.Printf("usage:\n")
|
||||
fmt.Printf("%s [-f/--foreground] INTERFACE-NAME\n", os.Args[0])
|
||||
fmt.Printf("Usage: %s [-f/--foreground] INTERFACE-NAME\n", os.Args[0])
|
||||
}
|
||||
|
||||
func warning() {
|
||||
if runtime.GOOS != "linux" || os.Getenv(ENV_WG_PROCESS_FOREGROUND) == "1" {
|
||||
switch runtime.GOOS {
|
||||
case "linux", "freebsd", "openbsd":
|
||||
if os.Getenv(ENV_WG_PROCESS_FOREGROUND) == "1" {
|
||||
return
|
||||
}
|
||||
default:
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Fprintln(os.Stderr, "┌───────────────────────────────────────────────────┐")
|
||||
fmt.Fprintln(os.Stderr, "┌──────────────────────────────────────────────────────┐")
|
||||
fmt.Fprintln(os.Stderr, "│ │")
|
||||
fmt.Fprintln(os.Stderr, "│ Running this software on Linux is unnecessary, │")
|
||||
fmt.Fprintln(os.Stderr, "│ because the Linux kernel has built-in first │")
|
||||
fmt.Fprintln(os.Stderr, "│ class support for WireGuard, which will be │")
|
||||
fmt.Fprintln(os.Stderr, "│ faster, slicker, and better integrated. For │")
|
||||
fmt.Fprintln(os.Stderr, "│ Running wireguard-go is not required because this │")
|
||||
fmt.Fprintln(os.Stderr, "│ kernel has first class support for WireGuard. For │")
|
||||
fmt.Fprintln(os.Stderr, "│ information on installing the kernel module, │")
|
||||
fmt.Fprintln(os.Stderr, "│ please visit: <https://wireguard.com/install>. │")
|
||||
fmt.Fprintln(os.Stderr, "│ please visit: │")
|
||||
fmt.Fprintln(os.Stderr, "│ https://www.wireguard.com/install/ │")
|
||||
fmt.Fprintln(os.Stderr, "│ │")
|
||||
fmt.Fprintln(os.Stderr, "└───────────────────────────────────────────────────┘")
|
||||
fmt.Fprintln(os.Stderr, "└──────────────────────────────────────────────────────┘")
|
||||
}
|
||||
|
||||
func main() {
|
||||
|
||||
@@ -6,62 +6,52 @@
|
||||
package tun
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"os"
|
||||
"sync"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/net/ipv6"
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
// _TUNSIFHEAD, value derived from sys/net/{if_tun,ioccom}.h
|
||||
// const _TUNSIFHEAD = ((0x80000000) | (((4) & ((1 << 13) - 1) ) << 16) | (uint32(byte('t')) << 8) | (96))
|
||||
const (
|
||||
_TUNSIFHEAD = 0x80047460
|
||||
_TUNSIFMODE = 0x8004745e
|
||||
_TUNGIFNAME = 0x4020745d
|
||||
_TUNSIFPID = 0x2000745f
|
||||
|
||||
_SIOCGIFINFO_IN6 = 0xc048696c
|
||||
_SIOCSIFINFO_IN6 = 0xc048696d
|
||||
_ND6_IFF_AUTO_LINKLOCAL = 0x20
|
||||
_ND6_IFF_NO_DAD = 0x100
|
||||
)
|
||||
|
||||
// TODO: move into x/sys/unix
|
||||
const (
|
||||
SIOCGIFINFO_IN6 = 0xc048696c
|
||||
SIOCSIFINFO_IN6 = 0xc048696d
|
||||
ND6_IFF_AUTO_LINKLOCAL = 0x20
|
||||
ND6_IFF_NO_DAD = 0x100
|
||||
)
|
||||
// Iface requests with just the name
|
||||
type ifreqName struct {
|
||||
Name [unix.IFNAMSIZ]byte
|
||||
_ [16]byte
|
||||
}
|
||||
|
||||
// Iface status string max len
|
||||
const _IFSTATMAX = 800
|
||||
|
||||
const SIZEOF_UINTPTR = 4 << (^uintptr(0) >> 32 & 1)
|
||||
|
||||
// structure for iface requests with a pointer
|
||||
type ifreq_ptr struct {
|
||||
// Iface requests with a pointer
|
||||
type ifreqPtr struct {
|
||||
Name [unix.IFNAMSIZ]byte
|
||||
Data uintptr
|
||||
Pad0 [16 - SIZEOF_UINTPTR]byte
|
||||
_ [16 - unsafe.Sizeof(uintptr(0))]byte
|
||||
}
|
||||
|
||||
// Structure for iface mtu get/set ioctls
|
||||
type ifreq_mtu struct {
|
||||
// Iface requests with MTU
|
||||
type ifreqMtu struct {
|
||||
Name [unix.IFNAMSIZ]byte
|
||||
MTU uint32
|
||||
Pad0 [12]byte
|
||||
_ [12]byte
|
||||
}
|
||||
|
||||
// Structure for interface status request ioctl
|
||||
type ifstat struct {
|
||||
IfsName [unix.IFNAMSIZ]byte
|
||||
Ascii [_IFSTATMAX]byte
|
||||
}
|
||||
|
||||
// Structures for nd6 flag manipulation
|
||||
type in6_ndireq struct {
|
||||
// ND6 flag manipulation
|
||||
type nd6Req struct {
|
||||
Name [unix.IFNAMSIZ]byte
|
||||
Linkmtu uint32
|
||||
Maxmtu uint32
|
||||
@@ -99,7 +89,7 @@ func (tun *NativeTun) routineRouteListener(tunIfindex int) {
|
||||
retry:
|
||||
n, err := unix.Read(tun.routeSocket, data)
|
||||
if err != nil {
|
||||
if errno, ok := err.(syscall.Errno); ok && errno == syscall.EINTR {
|
||||
if errors.Is(err, syscall.EINTR) {
|
||||
goto retry
|
||||
}
|
||||
tun.errors <- err
|
||||
@@ -143,91 +133,17 @@ func (tun *NativeTun) routineRouteListener(tunIfindex int) {
|
||||
}
|
||||
|
||||
func tunName(fd uintptr) (string, error) {
|
||||
//Terrible hack to make up for freebsd not having a TUNGIFNAME
|
||||
|
||||
//First, make sure the tun pid matches this proc's pid
|
||||
_, _, errno := unix.Syscall(
|
||||
unix.SYS_IOCTL,
|
||||
uintptr(fd),
|
||||
uintptr(_TUNSIFPID),
|
||||
uintptr(0),
|
||||
)
|
||||
|
||||
if errno != 0 {
|
||||
return "", fmt.Errorf("failed to set tun device PID: %s", errno.Error())
|
||||
}
|
||||
|
||||
// Open iface control socket
|
||||
|
||||
confd, err := unix.Socket(
|
||||
unix.AF_INET,
|
||||
unix.SOCK_DGRAM,
|
||||
0,
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
var ifreq ifreqName
|
||||
_, _, err := unix.Syscall(unix.SYS_IOCTL, fd, _TUNGIFNAME, uintptr(unsafe.Pointer(&ifreq)))
|
||||
if err != 0 {
|
||||
return "", err
|
||||
}
|
||||
|
||||
defer unix.Close(confd)
|
||||
|
||||
procPid := os.Getpid()
|
||||
|
||||
//Try to find interface with matching PID
|
||||
for i := 1; ; i++ {
|
||||
iface, _ := net.InterfaceByIndex(i)
|
||||
if err != nil || iface == nil {
|
||||
break
|
||||
}
|
||||
|
||||
// Structs for getting data in and out of SIOCGIFSTATUS ioctl
|
||||
var ifstatus ifstat
|
||||
copy(ifstatus.IfsName[:], iface.Name)
|
||||
|
||||
// Make the syscall to get the status string
|
||||
_, _, errno := unix.Syscall(
|
||||
unix.SYS_IOCTL,
|
||||
uintptr(confd),
|
||||
uintptr(unix.SIOCGIFSTATUS),
|
||||
uintptr(unsafe.Pointer(&ifstatus)),
|
||||
)
|
||||
|
||||
if errno != 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
nullStr := ifstatus.Ascii[:]
|
||||
i := bytes.IndexByte(nullStr, 0)
|
||||
if i < 1 {
|
||||
continue
|
||||
}
|
||||
statStr := string(nullStr[:i])
|
||||
var pidNum int = 0
|
||||
|
||||
// Finally get the owning PID
|
||||
// Format string taken from sys/net/if_tun.c
|
||||
_, err := fmt.Sscanf(statStr, "\tOpened by PID %d\n", &pidNum)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
if pidNum == procPid {
|
||||
return iface.Name, nil
|
||||
}
|
||||
}
|
||||
|
||||
return "", nil
|
||||
return unix.ByteSliceToString(ifreq.Name[:]), nil
|
||||
}
|
||||
|
||||
// Destroy a named system interface
|
||||
func tunDestroy(name string) error {
|
||||
// Open control socket.
|
||||
var fd int
|
||||
fd, err := unix.Socket(
|
||||
unix.AF_INET,
|
||||
unix.SOCK_DGRAM,
|
||||
0,
|
||||
)
|
||||
fd, err := unix.Socket(unix.AF_INET, unix.SOCK_DGRAM, 0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -235,14 +151,9 @@ func tunDestroy(name string) error {
|
||||
|
||||
var ifr [32]byte
|
||||
copy(ifr[:], name)
|
||||
_, _, errno := unix.Syscall(
|
||||
unix.SYS_IOCTL,
|
||||
uintptr(fd),
|
||||
uintptr(unix.SIOCIFDESTROY),
|
||||
uintptr(unsafe.Pointer(&ifr[0])),
|
||||
)
|
||||
_, _, errno := unix.Syscall(unix.SYS_IOCTL, uintptr(fd), uintptr(unix.SIOCIFDESTROY), uintptr(unsafe.Pointer(&ifr[0])))
|
||||
if errno != 0 {
|
||||
return fmt.Errorf("failed to destroy interface %s: %s", name, errno.Error())
|
||||
return fmt.Errorf("failed to destroy interface %s: %w", name, errno)
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -278,104 +189,68 @@ func CreateTUN(name string, mtu int) (Device, error) {
|
||||
ifheadmode := 1
|
||||
var errno syscall.Errno
|
||||
tun.operateOnFd(func(fd uintptr) {
|
||||
_, _, errno = unix.Syscall(
|
||||
unix.SYS_IOCTL,
|
||||
fd,
|
||||
uintptr(_TUNSIFHEAD),
|
||||
uintptr(unsafe.Pointer(&ifheadmode)),
|
||||
)
|
||||
_, _, errno = unix.Syscall(unix.SYS_IOCTL, fd, _TUNSIFHEAD, uintptr(unsafe.Pointer(&ifheadmode)))
|
||||
})
|
||||
|
||||
if errno != 0 {
|
||||
tunFile.Close()
|
||||
tunDestroy(assignedName)
|
||||
return nil, fmt.Errorf("Unable to put into IFHEAD mode: %w", errno)
|
||||
return nil, fmt.Errorf("unable to put into IFHEAD mode: %w", errno)
|
||||
}
|
||||
|
||||
// Get out of PPP mode.
|
||||
ifflags := syscall.IFF_BROADCAST
|
||||
// Get out of PTP mode.
|
||||
ifflags := syscall.IFF_BROADCAST | syscall.IFF_MULTICAST
|
||||
tun.operateOnFd(func(fd uintptr) {
|
||||
_, _, errno = unix.Syscall(
|
||||
unix.SYS_IOCTL,
|
||||
fd,
|
||||
uintptr(_TUNSIFMODE),
|
||||
uintptr(unsafe.Pointer(&ifflags)),
|
||||
)
|
||||
_, _, errno = unix.Syscall(unix.SYS_IOCTL, fd, uintptr(_TUNSIFMODE), uintptr(unsafe.Pointer(&ifflags)))
|
||||
})
|
||||
|
||||
if errno != 0 {
|
||||
tunFile.Close()
|
||||
tunDestroy(assignedName)
|
||||
return nil, fmt.Errorf("Unable to put into IFF_BROADCAST mode: %w", errno)
|
||||
return nil, fmt.Errorf("unable to put into IFF_BROADCAST mode: %w", errno)
|
||||
}
|
||||
|
||||
// Open control sockets
|
||||
confd, err := unix.Socket(
|
||||
unix.AF_INET,
|
||||
unix.SOCK_DGRAM,
|
||||
0,
|
||||
)
|
||||
if err != nil {
|
||||
tunFile.Close()
|
||||
tunDestroy(assignedName)
|
||||
return nil, err
|
||||
}
|
||||
defer unix.Close(confd)
|
||||
confd6, err := unix.Socket(
|
||||
unix.AF_INET6,
|
||||
unix.SOCK_DGRAM,
|
||||
0,
|
||||
)
|
||||
// Disable link-local v6, not just because WireGuard doesn't do that anyway, but
|
||||
// also because there are serious races with attaching and detaching LLv6 addresses
|
||||
// in relation to interface lifetime within the FreeBSD kernel.
|
||||
confd6, err := unix.Socket(unix.AF_INET6, unix.SOCK_DGRAM, 0)
|
||||
if err != nil {
|
||||
tunFile.Close()
|
||||
tunDestroy(assignedName)
|
||||
return nil, err
|
||||
}
|
||||
defer unix.Close(confd6)
|
||||
|
||||
// Disable link-local v6, not just because WireGuard doesn't do that anyway, but
|
||||
// also because there are serious races with attaching and detaching LLv6 addresses
|
||||
// in relation to interface lifetime within the FreeBSD kernel.
|
||||
var ndireq in6_ndireq
|
||||
var ndireq nd6Req
|
||||
copy(ndireq.Name[:], assignedName)
|
||||
_, _, errno = unix.Syscall(
|
||||
unix.SYS_IOCTL,
|
||||
uintptr(confd6),
|
||||
uintptr(SIOCGIFINFO_IN6),
|
||||
uintptr(unsafe.Pointer(&ndireq)),
|
||||
)
|
||||
_, _, errno = unix.Syscall(unix.SYS_IOCTL, uintptr(confd6), uintptr(_SIOCGIFINFO_IN6), uintptr(unsafe.Pointer(&ndireq)))
|
||||
if errno != 0 {
|
||||
tunFile.Close()
|
||||
tunDestroy(assignedName)
|
||||
return nil, fmt.Errorf("Unable to get nd6 flags for %s: %w", assignedName, errno)
|
||||
return nil, fmt.Errorf("unable to get nd6 flags for %s: %w", assignedName, errno)
|
||||
}
|
||||
ndireq.Flags = ndireq.Flags &^ ND6_IFF_AUTO_LINKLOCAL
|
||||
ndireq.Flags = ndireq.Flags | ND6_IFF_NO_DAD
|
||||
_, _, errno = unix.Syscall(
|
||||
unix.SYS_IOCTL,
|
||||
uintptr(confd6),
|
||||
uintptr(SIOCSIFINFO_IN6),
|
||||
uintptr(unsafe.Pointer(&ndireq)),
|
||||
)
|
||||
ndireq.Flags = ndireq.Flags &^ _ND6_IFF_AUTO_LINKLOCAL
|
||||
ndireq.Flags = ndireq.Flags | _ND6_IFF_NO_DAD
|
||||
_, _, errno = unix.Syscall(unix.SYS_IOCTL, uintptr(confd6), uintptr(_SIOCSIFINFO_IN6), uintptr(unsafe.Pointer(&ndireq)))
|
||||
if errno != 0 {
|
||||
tunFile.Close()
|
||||
tunDestroy(assignedName)
|
||||
return nil, fmt.Errorf("Unable to set nd6 flags for %s: %w", assignedName, errno)
|
||||
return nil, fmt.Errorf("unable to set nd6 flags for %s: %w", assignedName, errno)
|
||||
}
|
||||
|
||||
if name != "" {
|
||||
// Rename the interface
|
||||
confd, err := unix.Socket(unix.AF_INET, unix.SOCK_DGRAM, 0)
|
||||
if err != nil {
|
||||
tunFile.Close()
|
||||
tunDestroy(assignedName)
|
||||
return nil, err
|
||||
}
|
||||
defer unix.Close(confd)
|
||||
var newnp [unix.IFNAMSIZ]byte
|
||||
copy(newnp[:], name)
|
||||
var ifr ifreq_ptr
|
||||
var ifr ifreqPtr
|
||||
copy(ifr.Name[:], assignedName)
|
||||
ifr.Data = uintptr(unsafe.Pointer(&newnp[0]))
|
||||
_, _, errno = unix.Syscall(
|
||||
unix.SYS_IOCTL,
|
||||
uintptr(confd),
|
||||
uintptr(unix.SIOCSIFNAME),
|
||||
uintptr(unsafe.Pointer(&ifr)),
|
||||
)
|
||||
_, _, errno = unix.Syscall(unix.SYS_IOCTL, uintptr(confd), uintptr(unix.SIOCSIFNAME), uintptr(unsafe.Pointer(&ifr)))
|
||||
if errno != 0 {
|
||||
tunFile.Close()
|
||||
tunDestroy(assignedName)
|
||||
@@ -387,13 +262,21 @@ func CreateTUN(name string, mtu int) (Device, error) {
|
||||
}
|
||||
|
||||
func CreateTUNFromFile(file *os.File, mtu int) (Device, error) {
|
||||
|
||||
tun := &NativeTun{
|
||||
tunFile: file,
|
||||
events: make(chan Event, 10),
|
||||
errors: make(chan error, 1),
|
||||
}
|
||||
|
||||
var errno syscall.Errno
|
||||
tun.operateOnFd(func(fd uintptr) {
|
||||
_, _, errno = unix.Syscall(unix.SYS_IOCTL, fd, _TUNSIFPID, uintptr(0))
|
||||
})
|
||||
if errno != 0 {
|
||||
tun.tunFile.Close()
|
||||
return nil, fmt.Errorf("unable to become controlling TUN process: %w", errno)
|
||||
}
|
||||
|
||||
name, err := tun.Name()
|
||||
if err != nil {
|
||||
tun.tunFile.Close()
|
||||
@@ -464,27 +347,26 @@ func (tun *NativeTun) Read(buff []byte, offset int) (int, error) {
|
||||
}
|
||||
}
|
||||
|
||||
func (tun *NativeTun) Write(buff []byte, offset int) (int, error) {
|
||||
|
||||
// reserve space for header
|
||||
|
||||
buff = buff[offset-4:]
|
||||
|
||||
// add packet information header
|
||||
|
||||
buff[0] = 0x00
|
||||
buff[1] = 0x00
|
||||
buff[2] = 0x00
|
||||
|
||||
if buff[4]>>4 == ipv6.Version {
|
||||
buff[3] = unix.AF_INET6
|
||||
} else {
|
||||
buff[3] = unix.AF_INET
|
||||
func (tun *NativeTun) Write(buf []byte, offset int) (int, error) {
|
||||
if offset < 4 {
|
||||
return 0, io.ErrShortBuffer
|
||||
}
|
||||
|
||||
// write
|
||||
|
||||
return tun.tunFile.Write(buff)
|
||||
buf = buf[offset-4:]
|
||||
if len(buf) < 5 {
|
||||
return 0, io.ErrShortBuffer
|
||||
}
|
||||
buf[0] = 0x00
|
||||
buf[1] = 0x00
|
||||
buf[2] = 0x00
|
||||
switch buf[4] >> 4 {
|
||||
case 4:
|
||||
buf[3] = unix.AF_INET
|
||||
case 6:
|
||||
buf[3] = unix.AF_INET6
|
||||
default:
|
||||
return 0, unix.EAFNOSUPPORT
|
||||
}
|
||||
return tun.tunFile.Write(buf)
|
||||
}
|
||||
|
||||
func (tun *NativeTun) Flush() error {
|
||||
@@ -515,70 +397,34 @@ func (tun *NativeTun) Close() error {
|
||||
}
|
||||
|
||||
func (tun *NativeTun) setMTU(n int) error {
|
||||
// open datagram socket
|
||||
|
||||
var fd int
|
||||
|
||||
fd, err := unix.Socket(
|
||||
unix.AF_INET,
|
||||
unix.SOCK_DGRAM,
|
||||
0,
|
||||
)
|
||||
|
||||
fd, err := unix.Socket(unix.AF_INET, unix.SOCK_DGRAM, 0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
defer unix.Close(fd)
|
||||
|
||||
// do ioctl call
|
||||
|
||||
var ifr ifreq_mtu
|
||||
var ifr ifreqMtu
|
||||
copy(ifr.Name[:], tun.name)
|
||||
ifr.MTU = uint32(n)
|
||||
|
||||
_, _, errno := unix.Syscall(
|
||||
unix.SYS_IOCTL,
|
||||
uintptr(fd),
|
||||
uintptr(unix.SIOCSIFMTU),
|
||||
uintptr(unsafe.Pointer(&ifr)),
|
||||
)
|
||||
|
||||
_, _, errno := unix.Syscall(unix.SYS_IOCTL, uintptr(fd), uintptr(unix.SIOCSIFMTU), uintptr(unsafe.Pointer(&ifr)))
|
||||
if errno != 0 {
|
||||
return fmt.Errorf("failed to set MTU on %s", tun.name)
|
||||
return fmt.Errorf("failed to set MTU on %s: %w", tun.name, errno)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (tun *NativeTun) MTU() (int, error) {
|
||||
// open datagram socket
|
||||
|
||||
fd, err := unix.Socket(
|
||||
unix.AF_INET,
|
||||
unix.SOCK_DGRAM,
|
||||
0,
|
||||
)
|
||||
|
||||
fd, err := unix.Socket(unix.AF_INET, unix.SOCK_DGRAM, 0)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
defer unix.Close(fd)
|
||||
|
||||
// do ioctl call
|
||||
var ifr ifreq_mtu
|
||||
var ifr ifreqMtu
|
||||
copy(ifr.Name[:], tun.name)
|
||||
|
||||
_, _, errno := unix.Syscall(
|
||||
unix.SYS_IOCTL,
|
||||
uintptr(fd),
|
||||
uintptr(unix.SIOCGIFMTU),
|
||||
uintptr(unsafe.Pointer(&ifr)),
|
||||
)
|
||||
_, _, errno := unix.Syscall(unix.SYS_IOCTL, uintptr(fd), uintptr(unix.SIOCGIFMTU), uintptr(unsafe.Pointer(&ifr)))
|
||||
if errno != 0 {
|
||||
return 0, fmt.Errorf("failed to get MTU on %s", tun.name)
|
||||
return 0, fmt.Errorf("failed to get MTU on %s: %w", tun.name, errno)
|
||||
}
|
||||
|
||||
return int(*(*int32)(unsafe.Pointer(&ifr.MTU))), nil
|
||||
}
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
package main
|
||||
|
||||
const Version = "0.0.20210323"
|
||||
const Version = "0.0.20210424"
|
||||
|
||||
Reference in New Issue
Block a user