Support IPv6-less kernels

This commit is contained in:
Jason A. Donenfeld 2018-06-11 19:04:38 +02:00
parent 0ba551807f
commit 6a5d0e2bcd
2 changed files with 105 additions and 51 deletions

View file

@ -11,7 +11,9 @@ package main
import ( import (
"golang.org/x/sys/unix" "golang.org/x/sys/unix"
"net" "net"
"os"
"runtime" "runtime"
"syscall"
) )
/* This code is meant to be a temporary solution /* This code is meant to be a temporary solution
@ -87,6 +89,18 @@ func listenNet(network string, port int) (*net.UDPConn, int, error) {
return conn, uaddr.Port, nil return conn, uaddr.Port, nil
} }
func extractErrno(err error) error {
opErr, ok := err.(*net.OpError)
if !ok {
return nil
}
syscallErr, ok := opErr.Err.(*os.SyscallError)
if !ok {
return nil
}
return syscallErr.Err
}
func CreateBind(uport uint16, device *Device) (Bind, uint16, error) { func CreateBind(uport uint16, device *Device) (Bind, uint16, error) {
var err error var err error
var bind NativeBind var bind NativeBind
@ -94,13 +108,15 @@ func CreateBind(uport uint16, device *Device) (Bind, uint16, error) {
port := int(uport) port := int(uport)
bind.ipv4, port, err = listenNet("udp4", port) bind.ipv4, port, err = listenNet("udp4", port)
if err != nil { if err != nil && extractErrno(err) != syscall.EAFNOSUPPORT {
return nil, 0, err return nil, 0, err
} }
bind.ipv6, port, err = listenNet("udp6", port) bind.ipv6, port, err = listenNet("udp6", port)
if err != nil { if err != nil && extractErrno(err) != syscall.EAFNOSUPPORT {
return nil, 0, err
bind.ipv4.Close() bind.ipv4.Close()
bind.ipv4 = nil
return nil, 0, err return nil, 0, err
} }
@ -108,8 +124,13 @@ func CreateBind(uport uint16, device *Device) (Bind, uint16, error) {
} }
func (bind *NativeBind) Close() error { func (bind *NativeBind) Close() error {
err1 := bind.ipv4.Close() var err1, err2 error
err2 := bind.ipv6.Close() if bind.ipv4 != nil {
err1 = bind.ipv4.Close()
}
if bind.ipv6 != nil {
err2 = bind.ipv6.Close()
}
if err1 != nil { if err1 != nil {
return err1 return err1
} }
@ -117,6 +138,9 @@ func (bind *NativeBind) Close() error {
} }
func (bind *NativeBind) ReceiveIPv4(buff []byte) (int, Endpoint, error) { func (bind *NativeBind) ReceiveIPv4(buff []byte) (int, Endpoint, error) {
if bind.ipv4 == nil {
return 0, nil, syscall.EAFNOSUPPORT
}
n, endpoint, err := bind.ipv4.ReadFromUDP(buff) n, endpoint, err := bind.ipv4.ReadFromUDP(buff)
if endpoint != nil { if endpoint != nil {
endpoint.IP = endpoint.IP.To4() endpoint.IP = endpoint.IP.To4()
@ -125,6 +149,9 @@ func (bind *NativeBind) ReceiveIPv4(buff []byte) (int, Endpoint, error) {
} }
func (bind *NativeBind) ReceiveIPv6(buff []byte) (int, Endpoint, error) { func (bind *NativeBind) ReceiveIPv6(buff []byte) (int, Endpoint, error) {
if bind.ipv6 == nil {
return 0, nil, syscall.EAFNOSUPPORT
}
n, endpoint, err := bind.ipv6.ReadFromUDP(buff) n, endpoint, err := bind.ipv6.ReadFromUDP(buff)
return n, (*NativeEndpoint)(endpoint), err return n, (*NativeEndpoint)(endpoint), err
} }
@ -133,8 +160,14 @@ func (bind *NativeBind) Send(buff []byte, endpoint Endpoint) error {
var err error var err error
nend := endpoint.(*NativeEndpoint) nend := endpoint.(*NativeEndpoint)
if nend.IP.To4() != nil { if nend.IP.To4() != nil {
if bind.ipv4 == nil {
return syscall.EAFNOSUPPORT
}
_, err = bind.ipv4.WriteToUDP(buff, (*net.UDPAddr)(nend)) _, err = bind.ipv4.WriteToUDP(buff, (*net.UDPAddr)(nend))
} else { } else {
if bind.ipv6 == nil {
return syscall.EAFNOSUPPORT
}
_, err = bind.ipv6.WriteToUDP(buff, (*net.UDPAddr)(nend)) _, err = bind.ipv6.WriteToUDP(buff, (*net.UDPAddr)(nend))
} }
return err return err
@ -157,31 +190,29 @@ func (bind *NativeBind) SetMark(mark uint32) error {
if fwmarkIoctl == 0 { if fwmarkIoctl == 0 {
return nil return nil
} }
fd4, err1 := bind.ipv4.SyscallConn() if bind.ipv4 != nil {
fd6, err2 := bind.ipv6.SyscallConn() fd, err := bind.ipv4.SyscallConn()
if err1 != nil { if err != nil {
return err1 return err
}
err = fd.Control(func(fd uintptr) {
err = unix.SetsockoptInt(int(fd), unix.SOL_SOCKET, fwmarkIoctl, int(mark))
})
if err != nil {
return err
}
} }
if err2 != nil { if bind.ipv6 != nil {
return err2 fd, err := bind.ipv6.SyscallConn()
} if err != nil {
err3 := fd4.Control(func(fd uintptr) { return err
err1 = unix.SetsockoptInt(int(fd), unix.SOL_SOCKET, fwmarkIoctl, int(mark)) }
}) err = fd.Control(func(fd uintptr) {
err4 := fd6.Control(func(fd uintptr) { err = unix.SetsockoptInt(int(fd), unix.SOL_SOCKET, fwmarkIoctl, int(mark))
err2 = unix.SetsockoptInt(int(fd), unix.SOL_SOCKET, fwmarkIoctl, int(mark)) })
}) if err != nil {
if err1 != nil { return err
return err1 }
}
if err2 != nil {
return err2
}
if err3 != nil {
return err3
}
if err4 != nil {
return err4
} }
return nil return nil
} }

View file

@ -24,6 +24,7 @@ import (
"net" "net"
"strconv" "strconv"
"sync" "sync"
"syscall"
"unsafe" "unsafe"
) )
@ -140,40 +141,45 @@ func CreateBind(port uint16, device *Device) (*NativeBind, uint16, error) {
go bind.routineRouteListener(device) go bind.routineRouteListener(device)
bind.sock6, port, err = create6(port) bind.sock6, port, err = create6(port)
if err != nil { if err != nil && err != syscall.EAFNOSUPPORT {
bind.netlinkCancel.Cancel() bind.netlinkCancel.Cancel()
return nil, port, err return nil, 0, err
} }
bind.sock4, port, err = create4(port) bind.sock4, port, err = create4(port)
if err != nil { if err != nil && err != syscall.EAFNOSUPPORT {
bind.netlinkCancel.Cancel() bind.netlinkCancel.Cancel()
unix.Close(bind.sock6) unix.Close(bind.sock6)
return nil, 0, err
} }
return &bind, port, err return &bind, port, nil
} }
func (bind *NativeBind) SetMark(value uint32) error { func (bind *NativeBind) SetMark(value uint32) error {
err := unix.SetsockoptInt( if bind.sock6 != -1 {
bind.sock6, err := unix.SetsockoptInt(
unix.SOL_SOCKET, bind.sock6,
unix.SO_MARK, unix.SOL_SOCKET,
int(value), unix.SO_MARK,
) int(value),
)
if err != nil { if err != nil {
return err return err
}
} }
err = unix.SetsockoptInt( if bind.sock4 != -1 {
bind.sock4, err := unix.SetsockoptInt(
unix.SOL_SOCKET, bind.sock4,
unix.SO_MARK, unix.SOL_SOCKET,
int(value), unix.SO_MARK,
) int(value),
)
if err != nil { if err != nil {
return err return err
}
} }
bind.lastMark = value bind.lastMark = value
@ -187,9 +193,14 @@ func closeUnblock(fd int) error {
} }
func (bind *NativeBind) Close() error { func (bind *NativeBind) Close() error {
err1 := closeUnblock(bind.sock6) var err1, err2, err3 error
err2 := closeUnblock(bind.sock4) if bind.sock6 != -1 {
err3 := bind.netlinkCancel.Cancel() err1 = closeUnblock(bind.sock6)
}
if bind.sock4 != -1 {
err2 = closeUnblock(bind.sock4)
}
err3 = bind.netlinkCancel.Cancel()
if err1 != nil { if err1 != nil {
return err1 return err1
@ -202,6 +213,9 @@ func (bind *NativeBind) Close() error {
func (bind *NativeBind) ReceiveIPv6(buff []byte) (int, Endpoint, error) { func (bind *NativeBind) ReceiveIPv6(buff []byte) (int, Endpoint, error) {
var end NativeEndpoint var end NativeEndpoint
if bind.sock6 == -1 {
return 0, nil, syscall.EAFNOSUPPORT
}
n, err := receive6( n, err := receive6(
bind.sock6, bind.sock6,
buff, buff,
@ -212,6 +226,9 @@ func (bind *NativeBind) ReceiveIPv6(buff []byte) (int, Endpoint, error) {
func (bind *NativeBind) ReceiveIPv4(buff []byte) (int, Endpoint, error) { func (bind *NativeBind) ReceiveIPv4(buff []byte) (int, Endpoint, error) {
var end NativeEndpoint var end NativeEndpoint
if bind.sock4 == -1 {
return 0, nil, syscall.EAFNOSUPPORT
}
n, err := receive4( n, err := receive4(
bind.sock4, bind.sock4,
buff, buff,
@ -223,8 +240,14 @@ func (bind *NativeBind) ReceiveIPv4(buff []byte) (int, Endpoint, error) {
func (bind *NativeBind) Send(buff []byte, end Endpoint) error { func (bind *NativeBind) Send(buff []byte, end Endpoint) error {
nend := end.(*NativeEndpoint) nend := end.(*NativeEndpoint)
if !nend.isV6 { if !nend.isV6 {
if bind.sock4 == -1 {
return syscall.EAFNOSUPPORT
}
return send4(bind.sock4, nend, buff) return send4(bind.sock4, nend, buff)
} else { } else {
if bind.sock6 == -1 {
return syscall.EAFNOSUPPORT
}
return send6(bind.sock6, nend, buff) return send6(bind.sock6, nend, buff)
} }
} }