net/rioconn: allow configuring a connection using functional options

In preparation for implementing a UDP connection, we introduce options that configure
its basic parameters, such as maximum memory usage and maximum payload size.

Updates tailscale/corp#8610

Signed-off-by: Nick Khyl <nickk@tailscale.com>
This commit is contained in:
Nick Khyl 2026-02-19 08:18:52 -06:00
parent 52ab2b1894
commit 3b5180e12e
No known key found for this signature in database
2 changed files with 142 additions and 0 deletions

View File

@ -4,13 +4,24 @@
package rioconn
import (
"cmp"
"errors"
"math"
"syscall"
)
const (
// TODO(nickkhyl): Determine defaults automatically based on NIC and link properties.
defaultRXMemoryLimit = 2 << 20 // 2 MiB
defaultTXMemoryLimit = 2 << 20 // 2 MiB
)
// Config holds configuration for a RIO connection, independent of the transport protocol.
type Config struct {
control []func(network, address string, c syscall.RawConn) error
rx RxConfig
tx TxConfig
}
// Control invokes all control functions in the Config with the given
@ -26,3 +37,56 @@ func (c Config) Control(network string, address string, conn syscall.RawConn) er
}
return errors.Join(err...)
}
// Rx returns the receive path configuration.
func (c Config) Rx() *RxConfig {
return &c.rx
}
// Tx returns the transmit path configuration.
func (c Config) Tx() *TxConfig {
return &c.tx
}
// RxConfig holds configuration for the receive path of a RIO connection.
type RxConfig struct {
memoryLimit uintptr // 0 means default
maxPayloadLen uint16 // 0 means default
}
// MemoryLimit returns the maximum memory allowed for the receive path.
func (o RxConfig) MemoryLimit() uintptr {
return cmp.Or(o.memoryLimit, defaultRXMemoryLimit)
}
// MaxPayloadLen returns the maximum number of bytes allowed in a
// single packet. Packets larger than this limit may be dropped.
// It returns [math.MaxUint16] if no limit is applied other than the
// maximum packet size supported by the connection's transport protocol.
func (o RxConfig) MaxPayloadLen() uint16 {
return cmp.Or(o.maxPayloadLen, math.MaxUint16)
}
// TxConfig holds configuration for the transmit path of a RIO connection.
type TxConfig struct {
memoryLimit uintptr // 0 means default
maxPayloadLen uint16 // 0 means default
}
// MemoryLimit returns the maximum memory allowed for the transmit path.
func (o TxConfig) MemoryLimit() uintptr {
return cmp.Or(o.memoryLimit, defaultTXMemoryLimit)
}
// MaxPayloadLen returns the maximum number of bytes that may be sent
// in a single packet. Sending packets larger than this limit may fail.
// It returns [math.MaxUint16] if no limit is applied other than the
// maximum packet size supported by the connection's transport protocol.
func (o TxConfig) MaxPayloadLen() uint16 {
return cmp.Or(o.maxPayloadLen, math.MaxUint16)
}
// UDPConfig holds configuration for a [UDPConn].
type UDPConfig struct {
Config
}

78
net/rioconn/options.go Normal file
View File

@ -0,0 +1,78 @@
// Copyright (c) Tailscale Inc & contributors
// SPDX-License-Identifier: BSD-3-Clause
//go:build windows
package rioconn
import (
"syscall"
)
// Option is any option that can be applied to a RIO connection,
// independent of the transport protocol.
type Option interface {
UDPOption
}
// UDPOption is any option that can be applied to a [UDPConn].
type UDPOption interface {
applyUDP(*UDPConfig)
}
type option func(*Config)
func (o option) applyUDP(opts *UDPConfig) {
o(&opts.Config)
}
// Control specifies an optional function that will be called after creating
// the network connection but before binding it to the operating system.
func Control(control func(network, address string, c syscall.RawConn) error) Option {
return option(func(opts *Config) {
if control != nil {
opts.control = append(opts.control, control)
}
})
}
// RxMemoryLimit specifies the maximum memory to use for the receive path.
func RxMemoryLimit(bytes uintptr) Option {
return option(func(opts *Config) {
opts.rx.memoryLimit = bytes
})
}
// RxMaxPayloadLen specifies the maximum payload size, in bytes, accepted
// for a single received packet. Packets exceeding this limit may be dropped.
// If unset or set to zero, no limit is applied other than the maximum packet
// size supported by the connection's transport protocol.
func RxMaxPayloadLen(bytes uintptr) Option {
return option(func(opts *Config) {
opts.rx.maxPayloadLen = uint16(bytes)
})
}
// TxMemoryLimit specifies the maximum memory to use for the transmit path.
func TxMemoryLimit(bytes uintptr) Option {
return option(func(opts *Config) {
opts.tx.memoryLimit = bytes
})
}
// TxMaxPayloadLen specifies the maximum payload size, in bytes, accepted
// for transmission in a single packet. Attempting to send packets exceeding
// this limit may fail. If unset or set to zero, no limit is applied other than
// the maximum packet size supported by the connection's transport protocol
// and underlying link or hardware capabilities.
func TxMaxPayloadLen(bytes uintptr) Option {
return option(func(opts *Config) {
opts.tx.maxPayloadLen = uint16(bytes)
})
}
type udpOption func(*UDPConfig)
func (o udpOption) applyUDP(opts *UDPConfig) {
o(opts)
}