|
@@ -14,6 +14,7 @@ import (
|
|
"sync"
|
|
"sync"
|
|
"sync/atomic"
|
|
"sync/atomic"
|
|
"syscall"
|
|
"syscall"
|
|
|
|
+ "time"
|
|
"unsafe"
|
|
"unsafe"
|
|
|
|
|
|
"github.com/sirupsen/logrus"
|
|
"github.com/sirupsen/logrus"
|
|
@@ -69,7 +70,7 @@ func NewRIOListener(l *logrus.Logger, addr netip.Addr, port int) (*RIOConn, erro
|
|
|
|
|
|
u := &RIOConn{l: l}
|
|
u := &RIOConn{l: l}
|
|
|
|
|
|
- err := u.bind(&windows.SockaddrInet6{Addr: addr.As16(), Port: port})
|
|
|
|
|
|
+ err := u.bind(l, &windows.SockaddrInet6{Addr: addr.As16(), Port: port})
|
|
if err != nil {
|
|
if err != nil {
|
|
return nil, fmt.Errorf("bind: %w", err)
|
|
return nil, fmt.Errorf("bind: %w", err)
|
|
}
|
|
}
|
|
@@ -85,11 +86,11 @@ func NewRIOListener(l *logrus.Logger, addr netip.Addr, port int) (*RIOConn, erro
|
|
return u, nil
|
|
return u, nil
|
|
}
|
|
}
|
|
|
|
|
|
-func (u *RIOConn) bind(sa windows.Sockaddr) error {
|
|
|
|
|
|
+func (u *RIOConn) bind(l *logrus.Logger, sa windows.Sockaddr) error {
|
|
var err error
|
|
var err error
|
|
u.sock, err = winrio.Socket(windows.AF_INET6, windows.SOCK_DGRAM, windows.IPPROTO_UDP)
|
|
u.sock, err = winrio.Socket(windows.AF_INET6, windows.SOCK_DGRAM, windows.IPPROTO_UDP)
|
|
if err != nil {
|
|
if err != nil {
|
|
- return err
|
|
|
|
|
|
+ return fmt.Errorf("winrio.Socket error: %w", err)
|
|
}
|
|
}
|
|
|
|
|
|
// Enable v4 for this socket
|
|
// Enable v4 for this socket
|
|
@@ -103,35 +104,40 @@ func (u *RIOConn) bind(sa windows.Sockaddr) error {
|
|
size := uint32(unsafe.Sizeof(flag))
|
|
size := uint32(unsafe.Sizeof(flag))
|
|
err = syscall.WSAIoctl(syscall.Handle(u.sock), syscall.SIO_UDP_CONNRESET, (*byte)(unsafe.Pointer(&flag)), size, nil, 0, &ret, nil, 0)
|
|
err = syscall.WSAIoctl(syscall.Handle(u.sock), syscall.SIO_UDP_CONNRESET, (*byte)(unsafe.Pointer(&flag)), size, nil, 0, &ret, nil, 0)
|
|
if err != nil {
|
|
if err != nil {
|
|
- return err
|
|
|
|
|
|
+ // This is a best-effort to prevent errors from being returned by the udp recv operation.
|
|
|
|
+ // Quietly log a failure and continue.
|
|
|
|
+ l.WithError(err).Debug("failed to set UDP_CONNRESET ioctl")
|
|
}
|
|
}
|
|
|
|
+
|
|
ret = 0
|
|
ret = 0
|
|
flag = 0
|
|
flag = 0
|
|
size = uint32(unsafe.Sizeof(flag))
|
|
size = uint32(unsafe.Sizeof(flag))
|
|
SIO_UDP_NETRESET := uint32(syscall.IOC_IN | syscall.IOC_VENDOR | 15)
|
|
SIO_UDP_NETRESET := uint32(syscall.IOC_IN | syscall.IOC_VENDOR | 15)
|
|
err = syscall.WSAIoctl(syscall.Handle(u.sock), SIO_UDP_NETRESET, (*byte)(unsafe.Pointer(&flag)), size, nil, 0, &ret, nil, 0)
|
|
err = syscall.WSAIoctl(syscall.Handle(u.sock), SIO_UDP_NETRESET, (*byte)(unsafe.Pointer(&flag)), size, nil, 0, &ret, nil, 0)
|
|
if err != nil {
|
|
if err != nil {
|
|
- return err
|
|
|
|
|
|
+ // This is a best-effort to prevent errors from being returned by the udp recv operation.
|
|
|
|
+ // Quietly log a failure and continue.
|
|
|
|
+ l.WithError(err).Debug("failed to set UDP_NETRESET ioctl")
|
|
}
|
|
}
|
|
|
|
|
|
err = u.rx.Open()
|
|
err = u.rx.Open()
|
|
if err != nil {
|
|
if err != nil {
|
|
- return err
|
|
|
|
|
|
+ return fmt.Errorf("error rx.Open(): %w", err)
|
|
}
|
|
}
|
|
|
|
|
|
err = u.tx.Open()
|
|
err = u.tx.Open()
|
|
if err != nil {
|
|
if err != nil {
|
|
- return err
|
|
|
|
|
|
+ return fmt.Errorf("error tx.Open(): %w", err)
|
|
}
|
|
}
|
|
|
|
|
|
u.rq, err = winrio.CreateRequestQueue(u.sock, packetsPerRing, 1, packetsPerRing, 1, u.rx.cq, u.tx.cq, 0)
|
|
u.rq, err = winrio.CreateRequestQueue(u.sock, packetsPerRing, 1, packetsPerRing, 1, u.rx.cq, u.tx.cq, 0)
|
|
if err != nil {
|
|
if err != nil {
|
|
- return err
|
|
|
|
|
|
+ return fmt.Errorf("error CreateRequestQueue: %w", err)
|
|
}
|
|
}
|
|
|
|
|
|
err = windows.Bind(u.sock, sa)
|
|
err = windows.Bind(u.sock, sa)
|
|
if err != nil {
|
|
if err != nil {
|
|
- return err
|
|
|
|
|
|
+ return fmt.Errorf("error windows.Bind(): %w", err)
|
|
}
|
|
}
|
|
|
|
|
|
return nil
|
|
return nil
|
|
@@ -144,15 +150,22 @@ func (u *RIOConn) ListenOut(r EncReader, lhf LightHouseHandlerFunc, cache *firew
|
|
fwPacket := &firewall.Packet{}
|
|
fwPacket := &firewall.Packet{}
|
|
nb := make([]byte, 12, 12)
|
|
nb := make([]byte, 12, 12)
|
|
|
|
|
|
|
|
+ var lastRecvErr time.Time
|
|
|
|
+
|
|
for {
|
|
for {
|
|
// Just read one packet at a time
|
|
// Just read one packet at a time
|
|
n, rua, err := u.receive(buffer)
|
|
n, rua, err := u.receive(buffer)
|
|
|
|
+
|
|
if err != nil {
|
|
if err != nil {
|
|
if errors.Is(err, net.ErrClosed) {
|
|
if errors.Is(err, net.ErrClosed) {
|
|
u.l.WithError(err).Debug("udp socket is closed, exiting read loop")
|
|
u.l.WithError(err).Debug("udp socket is closed, exiting read loop")
|
|
return
|
|
return
|
|
}
|
|
}
|
|
- u.l.WithError(err).Error("unexpected udp socket receive error")
|
|
|
|
|
|
+ // Dampen unexpected message warns to once per minute
|
|
|
|
+ if lastRecvErr.IsZero() || time.Since(lastRecvErr) > time.Minute {
|
|
|
|
+ lastRecvErr = time.Now()
|
|
|
|
+ u.l.WithError(err).Warn("unexpected udp socket receive error")
|
|
|
|
+ }
|
|
continue
|
|
continue
|
|
}
|
|
}
|
|
|
|
|