This commit is contained in:
JackDoan
2026-04-17 11:39:51 -05:00
parent f8f63c470a
commit bd0a63a545
5 changed files with 124 additions and 8 deletions

View File

@@ -8,6 +8,12 @@ import (
const MTU = 9001
// MaxWriteBatch is the largest batch any Conn.WriteBatch implementation is
// required to accept. Callers SHOULD NOT pass more than this per call; Linux
// backends preallocate sendmmsg scratch sized to this value, so exceeding it
// only costs a chunked retry.
const MaxWriteBatch = 128
type EncReader func(
addr netip.AddrPort,
payload []byte,
@@ -18,6 +24,12 @@ type Conn interface {
LocalAddr() (netip.AddrPort, error)
ListenOut(r EncReader) error
WriteTo(b []byte, addr netip.AddrPort) error
// WriteBatch sends a contiguous batch of packets, each with its own
// destination. bufs and addrs must have the same length. Linux uses
// sendmmsg(2) for a single syscall; other backends fall back to a
// WriteTo loop. Returns on the first error; callers may observe a
// partial send if some packets went out before the error.
WriteBatch(bufs [][]byte, addrs []netip.AddrPort) error
ReloadConfig(c *config.C)
SupportsMultipleReaders() bool
Close() error
@@ -40,6 +52,9 @@ func (NoopConn) SupportsMultipleReaders() bool {
func (NoopConn) WriteTo(_ []byte, _ netip.AddrPort) error {
return nil
}
func (NoopConn) WriteBatch(_ [][]byte, _ []netip.AddrPort) error {
return nil
}
func (NoopConn) ReloadConfig(_ *config.C) {
return
}

View File

@@ -42,6 +42,15 @@ func (u *GenericConn) WriteTo(b []byte, addr netip.AddrPort) error {
return err
}
func (u *GenericConn) WriteBatch(bufs [][]byte, addrs []netip.AddrPort) error {
for i, b := range bufs {
if _, err := u.UDPConn.WriteToUDPAddrPort(b, addrs[i]); err != nil {
return err
}
}
return nil
}
func (u *GenericConn) LocalAddr() (netip.AddrPort, error) {
a := u.UDPConn.LocalAddr()

View File

@@ -55,3 +55,23 @@ func (u *StdConn) PrepareRawMessages(n int) ([]rawMessage, [][]byte, [][]byte) {
return msgs, buffers, names
}
// prepareWriteMessages allocates one Mmsghdr/iovec/sockaddr scratch per slot,
// wired up so each writeMsgs[i] already points at writeIovs[i] and
// writeNames[i]. Callers fill in the iovec Base/Len, the sockaddr bytes, and
// Namelen before each sendmmsg.
func (u *StdConn) prepareWriteMessages(n int) {
u.writeMsgs = make([]rawMessage, n)
u.writeIovs = make([]iovec, n)
u.writeNames = make([][]byte, n)
for i := range u.writeMsgs {
u.writeNames[i] = make([]byte, unix.SizeofSockaddrInet6)
u.writeMsgs[i].Hdr.Iov = &u.writeIovs[i]
u.writeMsgs[i].Hdr.Iovlen = 1
u.writeMsgs[i].Hdr.Name = &u.writeNames[i][0]
}
}
func setIovLen(v *iovec, n int) {
v.Len = uint64(n)
}