TUN-9882: Add write deadline for UDP origin writes

Add a deadline for origin writes as a preventative measure in the case that the kernel blocks any writes for too long.
In the case that the socket exceeds the write deadline, the datagram will be dropped.

Closes TUN-9882
This commit is contained in:
Devin Carr 2025-10-07 19:54:42 -07:00
parent 1fb466941a
commit 51c5ef726c
2 changed files with 25 additions and 2 deletions

View File

@ -11,6 +11,8 @@ import (
"github.com/rs/zerolog" "github.com/rs/zerolog"
) )
const writeDeadlineUDP = 200 * time.Millisecond
// OriginTCPDialer provides a TCP dial operation to a requested address. // OriginTCPDialer provides a TCP dial operation to a requested address.
type OriginTCPDialer interface { type OriginTCPDialer interface {
DialTCP(ctx context.Context, addr netip.AddrPort) (net.Conn, error) DialTCP(ctx context.Context, addr netip.AddrPort) (net.Conn, error)
@ -141,6 +143,21 @@ func (d *Dialer) DialUDP(dest netip.AddrPort) (net.Conn, error) {
if err != nil { if err != nil {
return nil, fmt.Errorf("unable to dial udp to origin %s: %w", dest, err) return nil, fmt.Errorf("unable to dial udp to origin %s: %w", dest, err)
} }
return &writeDeadlineConn{
return conn, nil Conn: conn,
}, nil
}
// writeDeadlineConn is a wrapper around a net.Conn that sets a write deadline of 200ms.
// This is to prevent the socket from blocking on the write operation if it were to occur. However,
// we typically never expect this to occur except under high load or kernel issues.
type writeDeadlineConn struct {
net.Conn
}
func (w *writeDeadlineConn) Write(b []byte) (int, error) {
if err := w.SetWriteDeadline(time.Now().Add(writeDeadlineUDP)); err != nil {
return 0, err
}
return w.Conn.Write(b)
} }

View File

@ -6,6 +6,7 @@ import (
"fmt" "fmt"
"io" "io"
"net" "net"
"os"
"sync" "sync"
"sync/atomic" "sync/atomic"
"time" "time"
@ -241,6 +242,11 @@ func (s *session) writeLoop() {
case payload := <-s.writeChan: case payload := <-s.writeChan:
n, err := s.origin.Write(payload) n, err := s.origin.Write(payload)
if err != nil { if err != nil {
// Check if this is a write deadline exceeded to the connection
if errors.Is(err, os.ErrDeadlineExceeded) {
s.log.Warn().Err(err).Msg("flow (write) deadline exceeded: dropping packet")
continue
}
if isConnectionClosed(err) { if isConnectionClosed(err) {
s.log.Debug().Msgf("flow (write) connection closed: %v", err) s.log.Debug().Msgf("flow (write) connection closed: %v", err)
} }