diff --git a/net/uring/io_uring.c b/net/uring/io_uring.c index cdfdac417..68b4ffd00 100644 --- a/net/uring/io_uring.c +++ b/net/uring/io_uring.c @@ -39,12 +39,13 @@ struct req { struct msghdr hdr; struct iovec iov; struct sockaddr_in sa; + struct sockaddr_in6 sa6; char *buf; }; typedef struct req goreq; -static struct req *initializeReq(size_t sz) { +static struct req *initializeReq(size_t sz, int ipVersion) { struct req *r = malloc(sizeof(struct req)); memset(r, 0, sizeof(*r)); r->buf = malloc(sz); @@ -53,8 +54,16 @@ static struct req *initializeReq(size_t sz) { r->iov.iov_len = sz; r->hdr.msg_iov = &r->iov; r->hdr.msg_iovlen = 1; - r->hdr.msg_name = &r->sa; - r->hdr.msg_namelen = sizeof(r->sa); + switch(ipVersion) { + case 4: + r->hdr.msg_name = &r->sa; + r->hdr.msg_namelen = sizeof(r->sa); + break; + case 6: + r->hdr.msg_name = &r->sa6; + r->hdr.msg_namelen = sizeof(r->sa6); + break; + } return r; } @@ -70,22 +79,6 @@ static uint64_t packNIdx(int n, size_t idx) { return (n64 << 32) | idx64; } -static uint32_t ip(struct req *r) { - return ntohl(r->sa.sin_addr.s_addr); -} - -static uint16_t port(struct req *r) { - return ntohs(r->sa.sin_port); -} - -static uint32_t setIP(struct sockaddr_in *sa, uint32_t ip) { - sa->sin_addr.s_addr = htonl(ip); -} - -static uint16_t setPort(struct sockaddr_in *sa, uint16_t port) { - sa->sin_port = htons(port); -} - // submit a recvmsg request via liburing // TODO: What recvfrom support arrives, maybe use that instead? static int submit_recvmsg_request(struct io_uring *ring, struct req *r, size_t idx) { diff --git a/net/uring/io_uring_linux.go b/net/uring/io_uring_linux.go index 4f26b2170..d7c834062 100644 --- a/net/uring/io_uring_linux.go +++ b/net/uring/io_uring_linux.go @@ -45,6 +45,7 @@ type UDPConn struct { recvReqs [8]*C.goreq sendReqs [8]*C.goreq sendReqC chan int // indices into sendReqs + is4 bool } func NewUDPConn(conn *net.UDPConn) (*UDPConn, error) { @@ -54,8 +55,9 @@ func NewUDPConn(conn *net.UDPConn) (*UDPConn, error) { if err != nil { return nil, fmt.Errorf("failed to parse UDPConn local addr %s as IP: %w", local, err) } - if !ip.IP().Is4() { - return nil, fmt.Errorf("uring only supports udp4 (for now), got local addr %s", local) + ipVersion := 6 + if ip.IP().Is4() { + ipVersion = 4 } // TODO: probe for system capabilities: https://unixism.net/loti/tutorial/probe_liburing.html file, err := conn.File() @@ -80,12 +82,13 @@ func NewUDPConn(conn *net.UDPConn) (*UDPConn, error) { file: file, fd: fd, local: conn.LocalAddr(), + is4: ipVersion == 4, } // Initialize buffers for _, reqs := range []*[8]*C.goreq{&u.recvReqs, &u.sendReqs} { for i := range reqs { - reqs[i] = C.initializeReq(bufferSize) + reqs[i] = C.initializeReq(bufferSize, C.int(ipVersion)) } } @@ -134,11 +137,20 @@ func (u *UDPConn) ReadFromNetaddr(buf []byte) (int, netaddr.IPPort, error) { return 0, netaddr.IPPort{}, fmt.Errorf("ReadFromNetaddr: %v", err) } r := u.recvReqs[idx] - ip := C.ip(r) - var ip4 [4]byte - binary.BigEndian.PutUint32(ip4[:], uint32(ip)) - port := C.port(r) - ipp := netaddr.IPPortFrom(netaddr.IPFrom4(ip4), uint16(port)) + + var ip netaddr.IP + var port uint16 + if u.is4 { + // TODO: native go endianness conversion routines so we don't have to call ntohl, etc. + var arr [4]byte + binary.BigEndian.PutUint32(arr[:], uint32(C.ntohl(r.sa.sin_addr.s_addr))) + ip = netaddr.IPFrom4(arr) + port = uint16(C.ntohs(r.sa.sin_port)) + } else { + ip = netaddr.IPFrom16(*(*[16]byte)((unsafe.Pointer)((&r.sa6.sin6_addr)))) + port = uint16(C.ntohs(r.sa6.sin6_port)) + } + ipp := netaddr.IPPortFrom(ip, port) rbuf := sliceOf(r.buf, n) copy(buf, rbuf) // Queue up a new request. @@ -226,10 +238,17 @@ func (u *UDPConn) WriteTo(p []byte, addr net.Addr) (n int, err error) { rbuf := sliceOf(r.buf, len(p)) copy(rbuf, p) - ip := binary.BigEndian.Uint32(udpAddr.IP) - C.setIP(&r.sa, C.uint32_t(ip)) - C.setPort(&r.sa, C.uint16_t(udpAddr.Port)) - + 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)) + } 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)) + } C.submit_sendmsg_request( u.sendRing, // ring r, @@ -300,10 +319,10 @@ func NewFile(file *os.File) (*File, error) { // Initialize buffers for i := range &u.readReqs { - u.readReqs[i] = C.initializeReq(bufferSize) + u.readReqs[i] = C.initializeReq(bufferSize, 0) } for i := range &u.writeReqs { - u.writeReqs[i] = C.initializeReq(bufferSize) + u.writeReqs[i] = C.initializeReq(bufferSize, 0) } // Initialize read half.