Use proper status listener on Darwin
This commit is contained in:
parent
b95a4c61a5
commit
b290cf05e3
117
tun_darwin.go
117
tun_darwin.go
|
@ -16,7 +16,6 @@ import (
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
"time"
|
|
||||||
"unsafe"
|
"unsafe"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -44,11 +43,61 @@ type NativeTun struct {
|
||||||
mtu int
|
mtu int
|
||||||
events chan TUNEvent
|
events chan TUNEvent
|
||||||
errors chan error
|
errors chan error
|
||||||
statusListenersShutdown chan struct{}
|
routeSocket int
|
||||||
}
|
}
|
||||||
|
|
||||||
var sockaddrCtlSize uintptr = 32
|
var sockaddrCtlSize uintptr = 32
|
||||||
|
|
||||||
|
func (tun *NativeTun) RoutineRouteListener(tunIfindex int) {
|
||||||
|
var (
|
||||||
|
statusUp bool
|
||||||
|
statusMTU int
|
||||||
|
)
|
||||||
|
|
||||||
|
data := make([]byte, os.Getpagesize())
|
||||||
|
for {
|
||||||
|
n, err := unix.Read(tun.routeSocket, data)
|
||||||
|
if err != nil {
|
||||||
|
tun.errors <- err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if n < 14 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if data[3 /* type */] != 0xe /* RTM_IFINFO */ {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
ifindex := int(*(*uint16)(unsafe.Pointer(&data[12 /* ifindex */])))
|
||||||
|
if ifindex != tunIfindex {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
iface, err := net.InterfaceByIndex(ifindex)
|
||||||
|
if err != nil {
|
||||||
|
tun.errors <- err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Up / Down event
|
||||||
|
up := (iface.Flags & net.FlagUp) != 0
|
||||||
|
if up != statusUp && up {
|
||||||
|
tun.events <- TUNEventUp
|
||||||
|
}
|
||||||
|
if up != statusUp && !up {
|
||||||
|
tun.events <- TUNEventDown
|
||||||
|
}
|
||||||
|
statusUp = up
|
||||||
|
|
||||||
|
// MTU changes
|
||||||
|
if iface.MTU != statusMTU {
|
||||||
|
tun.events <- TUNEventMTUUpdate
|
||||||
|
}
|
||||||
|
statusMTU = iface.MTU
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func CreateTUN(name string) (TUNDevice, error) {
|
func CreateTUN(name string) (TUNDevice, error) {
|
||||||
ifIndex := -1
|
ifIndex := -1
|
||||||
if name != "utun" {
|
if name != "utun" {
|
||||||
|
@ -122,10 +171,22 @@ func CreateTUNFromFile(file *os.File) (TUNDevice, error) {
|
||||||
mtu: 1500,
|
mtu: 1500,
|
||||||
events: make(chan TUNEvent, 10),
|
events: make(chan TUNEvent, 10),
|
||||||
errors: make(chan error, 1),
|
errors: make(chan error, 1),
|
||||||
statusListenersShutdown: make(chan struct{}),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err := tun.Name()
|
name, err := tun.Name()
|
||||||
|
if err != nil {
|
||||||
|
tun.fd.Close()
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
tunIfindex, err := func() (int, error) {
|
||||||
|
iface, err := net.InterfaceByName(name)
|
||||||
|
if err != nil {
|
||||||
|
return -1, err
|
||||||
|
}
|
||||||
|
return iface.Index, nil
|
||||||
|
}()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
tun.fd.Close()
|
tun.fd.Close()
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -137,43 +198,13 @@ func CreateTUNFromFile(file *os.File) (TUNDevice, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Fix this very naive implementation
|
tun.routeSocket, err = unix.Socket(unix.AF_ROUTE, unix.SOCK_RAW, unix.AF_UNSPEC)
|
||||||
go func(tun *NativeTun) {
|
|
||||||
var (
|
|
||||||
statusUp bool
|
|
||||||
statusMTU int
|
|
||||||
)
|
|
||||||
|
|
||||||
for {
|
|
||||||
intr, err := net.InterfaceByName(tun.name)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
tun.errors <- err
|
tun.fd.Close()
|
||||||
return
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Up / Down event
|
go tun.RoutineRouteListener(tunIfindex)
|
||||||
up := (intr.Flags & net.FlagUp) != 0
|
|
||||||
if up != statusUp && up {
|
|
||||||
tun.events <- TUNEventUp
|
|
||||||
}
|
|
||||||
if up != statusUp && !up {
|
|
||||||
tun.events <- TUNEventDown
|
|
||||||
}
|
|
||||||
statusUp = up
|
|
||||||
|
|
||||||
// MTU changes
|
|
||||||
if intr.MTU != statusMTU {
|
|
||||||
tun.events <- TUNEventMTUUpdate
|
|
||||||
}
|
|
||||||
statusMTU = intr.MTU
|
|
||||||
|
|
||||||
select {
|
|
||||||
case <-time.After(time.Second / 10):
|
|
||||||
case <-tun.statusListenersShutdown:
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}(tun)
|
|
||||||
|
|
||||||
// set default MTU
|
// set default MTU
|
||||||
err = tun.setMTU(DefaultMTU)
|
err = tun.setMTU(DefaultMTU)
|
||||||
|
@ -266,14 +297,22 @@ func (tun *NativeTun) Write(buff []byte, offset int) (int, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tun *NativeTun) Close() error {
|
func (tun *NativeTun) Close() error {
|
||||||
close(tun.statusListenersShutdown)
|
var err3 error
|
||||||
err1 := tun.rwcancel.Cancel()
|
err1 := tun.rwcancel.Cancel()
|
||||||
err2 := tun.fd.Close()
|
err2 := tun.fd.Close()
|
||||||
|
if tun.routeSocket != -1 {
|
||||||
|
// Surprisingly, on Darwin, simply closing a route socket is enough to unblock it.
|
||||||
|
// We don't even need to call shutdown, or use a rwcancel.
|
||||||
|
err3 = unix.Close(tun.routeSocket)
|
||||||
|
}
|
||||||
close(tun.events)
|
close(tun.events)
|
||||||
if err1 != nil {
|
if err1 != nil {
|
||||||
return err1
|
return err1
|
||||||
}
|
}
|
||||||
|
if err2 != nil {
|
||||||
return err2
|
return err2
|
||||||
|
}
|
||||||
|
return err3
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tun *NativeTun) setMTU(n int) error {
|
func (tun *NativeTun) setMTU(n int) error {
|
||||||
|
|
Loading…
Reference in a new issue