TUN-9469: Centralize UDP origin proxy dialing as ingress service

Introduces a new `UDPOriginProxy` interface and `UDPOriginService`
to standardize how UDP connections are dialed to origins. Allows for
future overrides of the ingress service for specific dial destinations.

Simplifies dependency injection for UDP dialing throughout both datagram
v2 and v3 by using the same ingress service. Previous invocations called
into a DialUDP function in the ingress package that was a light
wrapper over `net.DialUDP`. Now a reference is passed into both datagram
controllers that allows more control over the DialUDP method.

Closes TUN-9469
This commit is contained in:
Devin Carr
2025-06-23 18:01:15 +00:00
parent 64fdc52855
commit b4a98b13fe
9 changed files with 93 additions and 40 deletions

View File

@@ -2,37 +2,57 @@ package ingress
import (
"fmt"
"io"
"net"
"net/netip"
"github.com/rs/zerolog"
)
type UDPProxy interface {
io.ReadWriteCloser
LocalAddr() net.Addr
// UDPOriginService provides a proxy UDP dialer to origin services while allowing reserved
// services to be provided. These reserved services are assigned to specific [netip.AddrPort]s
// and provide their own [UDPOriginProxy]s to handle UDP origin dialing.
type UDPOriginService struct {
// Reserved services for reserved AddrPort values
reservedServices map[netip.AddrPort]UDPOriginProxy
// The default UDP Dialer used if no reserved services are found for an origin request.
defaultDialer UDPOriginProxy
logger *zerolog.Logger
}
type udpProxy struct {
*net.UDPConn
// UDPOriginProxy provides a UDP dial operation to a requested addr.
type UDPOriginProxy interface {
DialUDP(addr netip.AddrPort) (*net.UDPConn, error)
}
func DialUDP(dstIP net.IP, dstPort uint16) (UDPProxy, error) {
dstAddr := &net.UDPAddr{
IP: dstIP,
Port: int(dstPort),
func NewUDPOriginService(reserved map[netip.AddrPort]UDPOriginProxy, logger *zerolog.Logger) *UDPOriginService {
return &UDPOriginService{
reservedServices: reserved,
defaultDialer: DefaultUDPDialer,
logger: logger,
}
// We use nil as local addr to force runtime to find the best suitable local address IP given the destination
// address as context.
udpConn, err := net.DialUDP("udp", nil, dstAddr)
if err != nil {
return nil, fmt.Errorf("unable to create UDP proxy to origin (%v:%v): %w", dstIP, dstPort, err)
}
return &udpProxy{udpConn}, nil
}
func DialUDPAddrPort(dest netip.AddrPort) (*net.UDPConn, error) {
// SetUDPDialer updates the default UDP Dialer used.
// Typically used in unit testing.
func (s *UDPOriginService) SetDefaultDialer(dialer UDPOriginProxy) {
s.defaultDialer = dialer
}
// DialUDP will perform a dial UDP to the requested addr.
func (s *UDPOriginService) DialUDP(addr netip.AddrPort) (*net.UDPConn, error) {
// Check to see if any reserved services are available for this addr and call their dialer instead.
if dialer, ok := s.reservedServices[addr]; ok {
return dialer.DialUDP(addr)
}
return s.defaultDialer.DialUDP(addr)
}
type defaultUDPDialer struct{}
var DefaultUDPDialer UDPOriginProxy = &defaultUDPDialer{}
func (d *defaultUDPDialer) DialUDP(dest netip.AddrPort) (*net.UDPConn, error) {
addr := net.UDPAddrFromAddrPort(dest)
// We use nil as local addr to force runtime to find the best suitable local address IP given the destination