diff --git a/udp/udp_linux.go b/udp/udp_linux.go index b2c76eb5..be6f11c6 100644 --- a/udp/udp_linux.go +++ b/udp/udp_linux.go @@ -21,6 +21,7 @@ import ( type StdConn struct { udpConn *net.UDPConn rawConn syscall.RawConn + sockFd int isV4 bool l *logrus.Logger batch int @@ -61,9 +62,20 @@ func NewListener(l *logrus.Logger, ip netip.Addr, port int, multi bool, batch in } udpConn.LocalAddr().String() + //steal the socket's fd for sending + var sockFd uintptr + err = rawConn.Control(func(fd uintptr) { + sockFd = fd + }) + if err != nil { + _ = udpConn.Close() + return nil, err + } + return &StdConn{ udpConn: udpConn, rawConn: rawConn, + sockFd: int(sockFd), isV4: ip.Is4(), l: l, batch: batch, @@ -223,8 +235,64 @@ func (u *StdConn) ListenOut(r EncReader) { } func (u *StdConn) WriteTo(b []byte, ip netip.AddrPort) error { - _, err := u.udpConn.WriteToUDPAddrPort(b, ip) - return err + if u.isV4 { + return u.writeTo4(b, ip) + } + return u.writeTo6(b, ip) +} + +func (u *StdConn) writeTo6(b []byte, ip netip.AddrPort) error { + var rsa unix.RawSockaddrInet6 + rsa.Family = unix.AF_INET6 + rsa.Addr = ip.Addr().As16() + binary.BigEndian.PutUint16((*[2]byte)(unsafe.Pointer(&rsa.Port))[:], ip.Port()) + + for { + _, _, err := unix.Syscall6( + unix.SYS_SENDTO, + uintptr(u.sockFd), + uintptr(unsafe.Pointer(&b[0])), + uintptr(len(b)), + uintptr(0), + uintptr(unsafe.Pointer(&rsa)), + uintptr(unix.SizeofSockaddrInet6), + ) + + if err != 0 { + return &net.OpError{Op: "sendto", Err: err} + } + + return nil + } +} + +func (u *StdConn) writeTo4(b []byte, ip netip.AddrPort) error { + if !ip.Addr().Is4() { + return ErrInvalidIPv6RemoteForSocket + } + + var rsa unix.RawSockaddrInet4 + rsa.Family = unix.AF_INET + rsa.Addr = ip.Addr().As4() + binary.BigEndian.PutUint16((*[2]byte)(unsafe.Pointer(&rsa.Port))[:], ip.Port()) + + for { + _, _, err := unix.Syscall6( + unix.SYS_SENDTO, + uintptr(u.sockFd), + uintptr(unsafe.Pointer(&b[0])), + uintptr(len(b)), + uintptr(0), + uintptr(unsafe.Pointer(&rsa)), + uintptr(unix.SizeofSockaddrInet4), + ) + + if err != 0 { + return &net.OpError{Op: "sendto", Err: err} + } + + return nil + } } func (u *StdConn) ReloadConfig(c *config.C) {