diff --git a/net/uring/io_uring_linux.go b/net/uring/io_uring_linux.go index aa64e481b..f36c1dd96 100644 --- a/net/uring/io_uring_linux.go +++ b/net/uring/io_uring_linux.go @@ -21,6 +21,7 @@ import ( "golang.zx2c4.com/wireguard/device" "golang.zx2c4.com/wireguard/tun" "inet.af/netaddr" + "tailscale.com/util/endian" ) const bufferSize = device.MaxSegmentSize @@ -183,10 +184,10 @@ func (u *UDPConn) ReadFromNetaddr(buf []byte) (int, netaddr.IPPort, error) { // TODO: native go endianness conversion routines so we don't have to call ntohl, etc. if u.is4 { ip = netaddr.IPFrom4(*(*[4]byte)((unsafe.Pointer)((&r.sa.sin_addr.s_addr)))) - port = uint16(C.ntohs(r.sa.sin_port)) + port = endian.Ntoh16(uint16(r.sa.sin_port)) } else { ip = netaddr.IPFrom16(*(*[16]byte)((unsafe.Pointer)((&r.sa6.sin6_addr)))) - port = uint16(C.ntohs(r.sa6.sin6_port)) + port = endian.Ntoh16(uint16(r.sa6.sin6_port)) } ipp := netaddr.IPPortFrom(ip, port) rbuf := sliceOf(r.buf, n) @@ -296,16 +297,15 @@ func (u *UDPConn) WriteTo(p []byte, addr net.Addr) (n int, err error) { copy(rbuf, p) if u.is4 { - // TODO: native go endianness conversion routines so we don't have to call ntohl, etc. ipu32 := binary.BigEndian.Uint32(udpAddr.IP) - r.sa.sin_addr.s_addr = C.htonl(C.uint32_t(ipu32)) - r.sa.sin_port = C.htons(C.uint16_t(udpAddr.Port)) + r.sa.sin_addr.s_addr = C.uint32_t(endian.Hton32(ipu32)) + r.sa.sin_port = C.uint16_t(endian.Hton16(uint16(udpAddr.Port))) r.sa.sin_family = C.AF_INET } else { dst := (*[16]byte)((unsafe.Pointer)(&r.sa6.sin6_addr)) src := (*[16]byte)((unsafe.Pointer)(&udpAddr.IP[0])) *dst = *src - r.sa6.sin6_port = C.htons(C.uint16_t(udpAddr.Port)) + r.sa6.sin6_port = C.uint16_t(endian.Hton16(uint16(udpAddr.Port))) r.sa6.sin6_family = C.AF_INET6 } C.submit_sendmsg_request( diff --git a/util/endian/big.go b/util/endian/big.go index d0be83b81..b7728f722 100644 --- a/util/endian/big.go +++ b/util/endian/big.go @@ -13,3 +13,12 @@ const Big = true // Native is the platform's native byte order. var Native = binary.BigEndian + +// Ntoh16 converts network order into native/host. +func Ntoh16(v uint16) uint16 { return v } + +// Hton32 converts native/host uint32 order into network order. +func Hton32(v uint32) uint32 { return v } + +// Hton16 converts native/host uint16 order into network order. +func Hton16(v uint16) uint16 { return v } diff --git a/util/endian/encoding_test.go b/util/endian/encoding_test.go new file mode 100644 index 000000000..aa301c1af --- /dev/null +++ b/util/endian/encoding_test.go @@ -0,0 +1,48 @@ +package endian + +import ( + "encoding/binary" + "testing" + "unsafe" +) + +func TestNtoh16(t *testing.T) { + raw := uint16(0xABCD) + rawBytes := toNativeBytes16(raw) + big := binary.BigEndian.Uint16(rawBytes[:]) + if raw != Ntoh16(big) { + t.Errorf("ntohs failed, want %v, got %v", raw, Ntoh16(big)) + } +} + +func toNativeBytes32(v uint32) [4]byte { + return *(*[4]byte)(unsafe.Pointer(&v)) +} + +func TestHton32(t *testing.T) { + raw := uint32(0xDEADBEEF) + + networkOrder := Hton32(raw) + bytes := toNativeBytes32(networkOrder) + fromBig := binary.BigEndian.Uint32(bytes[:]) + + if fromBig != raw { + t.Errorf("htonl failed, want %v, got %v", raw, fromBig) + } +} + +func toNativeBytes16(v uint16) [2]byte { + return *(*[2]byte)(unsafe.Pointer(&v)) +} + +func TestHton16(t *testing.T) { + raw := uint16(0xBEEF) + + networkOrder := Hton16(raw) + bytes := toNativeBytes16(networkOrder) + fromBig := binary.BigEndian.Uint16(bytes[:]) + + if fromBig != raw { + t.Errorf("htonl failed, want %v, got %v", raw, fromBig) + } +} diff --git a/util/endian/little.go b/util/endian/little.go index 78c85a427..1dc6c8685 100644 --- a/util/endian/little.go +++ b/util/endian/little.go @@ -6,10 +6,22 @@ package endian -import "encoding/binary" +import ( + "encoding/binary" + "math/bits" +) // Big is whether the current platform is big endian. const Big = false // Native is the platform's native byte order. var Native = binary.LittleEndian + +// Ntoh16 converts network into native/host order. +func Ntoh16(v uint16) uint16 { return bits.ReverseBytes16(v) } + +// Hton32 converts native/host uint32 order into network order. +func Hton32(v uint32) uint32 { return bits.ReverseBytes32(v) } + +// Hton16 converts native/host uint16 order into network order. +func Hton16(v uint16) uint16 { return bits.ReverseBytes16(v) }