Skip to content

Commit

Permalink
chore: adjust dns sockets settings
Browse files Browse the repository at this point in the history
Enable some TCP optimization, set minimal TTL, set socket reuse.

Signed-off-by: Dmitriy Matrenichev <dmitry.matrenichev@siderolabs.com>
  • Loading branch information
DmitriyMV committed Feb 12, 2024
1 parent a1ec170 commit 83e0b0c
Show file tree
Hide file tree
Showing 5 changed files with 128 additions and 29 deletions.
2 changes: 1 addition & 1 deletion go.mod
Expand Up @@ -111,7 +111,7 @@ require (
github.com/siderolabs/go-blockdevice v0.4.7
github.com/siderolabs/go-circular v0.1.0
github.com/siderolabs/go-cmd v0.1.1
github.com/siderolabs/go-debug v0.2.3
github.com/siderolabs/go-debug v0.3.0
github.com/siderolabs/go-kmsg v0.1.4
github.com/siderolabs/go-kubeconfig v0.1.0
github.com/siderolabs/go-kubernetes v0.2.8
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Expand Up @@ -665,8 +665,8 @@ github.com/siderolabs/go-circular v0.1.0 h1:zpBJNUbCZSh0odZxA4Dcj0d3ShLLR2WxKW6h
github.com/siderolabs/go-circular v0.1.0/go.mod h1:14XnLf/I3J0VjzTgmwWNGjp58/bdIi4zXppAEx8plfw=
github.com/siderolabs/go-cmd v0.1.1 h1:nTouZUSxLeiiEe7hFexSVvaTsY/3O8k1s08BxPRrsps=
github.com/siderolabs/go-cmd v0.1.1/go.mod h1:6hY0JG34LxEEwYE8aH2iIHkHX/ir12VRLqfwAf2yJIY=
github.com/siderolabs/go-debug v0.2.3 h1:O9luHW4P++gQqOKzMnUgGIiQGsg9QaQmi6qI0JfQHXI=
github.com/siderolabs/go-debug v0.2.3/go.mod h1:45N6ALRTyrbtKAzbehGwz3VoE5MZqEKd/xrv7dawR30=
github.com/siderolabs/go-debug v0.3.0 h1:C8t7jbac5Va2eYu9QRXXEGsy3Vz5xOEVo0TDwVJH268=
github.com/siderolabs/go-debug v0.3.0/go.mod h1:DonqzIQOm3+qof020meFwJ2gXI5Jv/x4Dj27FyUW4aE=
github.com/siderolabs/go-kmsg v0.1.4 h1:RLAa90O9bWuhA3pXPAYAdrI+kzcqTshZASRA5yso/mo=
github.com/siderolabs/go-kmsg v0.1.4/go.mod h1:BLkt2N2DHT0wsFMz32lMw6vNEZL90c8ZnBjpIUoBb/M=
github.com/siderolabs/go-kubeconfig v0.1.0 h1:t/2oMWkLSdWHXglKPMz8ySXnx6ZjHckeGY79NaDcBTo=
Expand Down
52 changes: 38 additions & 14 deletions internal/app/machined/pkg/controllers/network/dns_resolve_cache.go
Expand Up @@ -103,27 +103,51 @@ func (ctrl *DNSResolveCacheController) runServer(originCtx context.Context, r co
addr := ctrl.Addr
ctx := originCtx

for _, opt := range []dns.ServerOptins{
for _, opt := range []struct {
net string
addr string
srvOpts dns.ServerOptins
}{
{
Addr: addr,
Net: "udp",
Handler: cache,
net: "udp",
addr: addr,
srvOpts: dns.ServerOptins{
Handler: cache,
},
},
{
Addr: addr,
Net: "tcp",
Handler: cache,
ReadTimeout: 3 * time.Second,
WriteTimeout: 5 * time.Second,
IdleTimeout: func() time.Duration { return 10 * time.Second },
MaxTCPQueries: -1,
net: "tcp",
addr: addr,
srvOpts: dns.ServerOptins{
Handler: cache,
ReadTimeout: 3 * time.Second,
WriteTimeout: 5 * time.Second,
IdleTimeout: func() time.Duration { return 10 * time.Second },
MaxTCPQueries: -1,
},
},
} {
l := ctrl.Logger.With(zap.String("net", opt.Net))
l := ctrl.Logger.With(zap.String("net", opt.net))

runner := dns.NewRunner(dns.NewServer(opt), l)
if opt.net == "tcp" {
listener, err := dns.NewTCPListener(opt.addr)
if err != nil {
return fmt.Errorf("error creating tcp listener: %w", err)
}

opt.srvOpts.Listener = listener
} else if opt.net == "udp" {
packetConn, err := dns.NewUDPPacketConn(opt.addr)
if err != nil {
return fmt.Errorf("error creating udp packet conn: %w", err)
}

opt.srvOpts.PacketConn = packetConn
}

runner := dns.NewRunner(dns.NewServer(opt.srvOpts), l)

err := ctrl.writeDNSStatus(ctx, r, opt.Net)
err := ctrl.writeDNSStatus(ctx, r, opt.net)
if err != nil {
return err
}
Expand Down
85 changes: 79 additions & 6 deletions internal/pkg/dns/dns.go
Expand Up @@ -8,10 +8,13 @@ package dns
import (
"context"
"errors"
"fmt"
"math/rand"
"net"
"slices"
"strings"
"sync"
"syscall"
"time"

"github.com/coredns/coredns/plugin"
Expand All @@ -20,13 +23,14 @@ import (
"github.com/coredns/coredns/request"
"github.com/miekg/dns"
"go.uber.org/zap"
"golang.org/x/sys/unix"

"github.com/siderolabs/talos/internal/pkg/utils"
)

// NewRunner creates a new Runner.
func NewRunner(srv Server, logger *zap.Logger) *Runner {
r := utils.NewRunner(srv.ListenAndServe, srv.Shutdown, func(err error) bool {
r := utils.NewRunner(srv.ActivateAndServe, srv.Shutdown, func(err error) bool {
// There a possible scenario where `Run` reached `ListenAndServe` and then yielded CPU time to another
// goroutine and then `Stop` reached `Shutdown`. In that case `ListenAndServe` will actually start after
// `Shutdown` and `Stop` method will forever block if we do not try again.
Expand All @@ -44,7 +48,7 @@ type Runner struct {

// Server is a dns server.
type Server interface {
ListenAndServe() error
ActivateAndServe() error
Shutdown() error
}

Expand Down Expand Up @@ -200,8 +204,8 @@ func (h *Handler) Stop() { h.SetProxy(nil) }

// ServerOptins is a Server options.
type ServerOptins struct {
Addr string
Net string
Listener net.Listener
PacketConn net.PacketConn
Handler dns.Handler
ReadTimeout time.Duration
WriteTimeout time.Duration
Expand All @@ -212,8 +216,8 @@ type ServerOptins struct {
// NewServer creates a new Server.
func NewServer(opts ServerOptins) Server {
return &server{&dns.Server{
Addr: opts.Addr,
Net: opts.Net,
Listener: opts.Listener,
PacketConn: opts.PacketConn,
Handler: opts.Handler,
ReadTimeout: opts.ReadTimeout,
WriteTimeout: opts.WriteTimeout,
Expand All @@ -223,3 +227,72 @@ func NewServer(opts ServerOptins) Server {
}

type server struct{ *dns.Server }

// NewTCPListener creates a new TCP listener.
func NewTCPListener(addr string) (net.Listener, error) {
lc := net.ListenConfig{
Control: makeControl(tcpOptions),
}

return lc.Listen(context.Background(), "tcp", addr)
}

// NewUDPPacketConn creates a new UDP packet connection.
func NewUDPPacketConn(addr string) (net.PacketConn, error) {
lc := net.ListenConfig{
Control: makeControl(udpOptions),
}

return lc.ListenPacket(context.Background(), "udp", addr)
}

var (
tcpOptions = []controlOptions{
// this isn't really necessary, because currently if the process dies, OS dies with it
{unix.SOL_SOCKET, unix.SO_REUSEADDR, 1, "failed to set SO_REUSEADDR"},
{unix.IPPROTO_IP, unix.IP_RECVTTL, 1, "failed to set IP_RECVTTL"},
{unix.IPPROTO_TCP, unix.TCP_FASTOPEN, 5, "failed to set TCP_FASTOPEN"}, // tcp specific stuff from systemd
{unix.IPPROTO_TCP, unix.TCP_NODELAY, 1, "failed to set TCP_NODELAY"}, // tcp specific stuff from systemd
{unix.IPPROTO_IP, unix.IP_TTL, 1, "failed to set IP_TTL"},
}

udpOptions = []controlOptions{
// this isn't really necessary, because currently if the process dies, OS dies with it
{unix.SOL_SOCKET, unix.SO_REUSEADDR, 1, "failed to set SO_REUSEADDR"},
{unix.IPPROTO_IP, unix.IP_RECVTTL, 1, "failed to set IP_RECVTTL"},
{unix.IPPROTO_IP, unix.IP_TTL, 1, "failed to set IP_TTL"},
}
)

type controlOptions struct {
level int
opt int
val int
errorMessage string
}

func makeControl(opts []controlOptions) func(string, string, syscall.RawConn) error {
return func(_ string, _ string, c syscall.RawConn) error {
var resErr error

err := c.Control(func(fd uintptr) {
for _, opt := range opts {
opErr := unix.SetsockoptInt(int(fd), opt.level, opt.opt, opt.val)
if opErr != nil {
resErr = fmt.Errorf(opt.errorMessage+": %w", opErr)

return
}
}
})
if err != nil {
return fmt.Errorf("failed in control call: %w", err)
}

if resErr != nil {
return fmt.Errorf("failed to set socket options: %w", resErr)
}

return nil
}
}
14 changes: 8 additions & 6 deletions internal/pkg/dns/dns_test.go
Expand Up @@ -120,10 +120,12 @@ func newServer(t *testing.T, nameservers ...string) (context.Context, func()) {

handler.SetProxy(pxs)

pc, err := dns.NewUDPPacketConn(":10700")
require.NoError(t, err)

runner := dns.NewRunner(dns.NewServer(dns.ServerOptins{
Addr: ":10700",
Net: "udp",
Handler: dns.NewCache(handler, l),
PacketConn: pc,
Handler: dns.NewCache(handler, l),
}), l)

return ctxutil.MonitorFn(context.Background(), runner.Run), runner.Stop
Expand All @@ -145,7 +147,7 @@ func createQuery() *dnssrv.Msg {
}
}

func TestListenFailure(t *testing.T) {
func TestActivateFailure(t *testing.T) {
// Ensure that we correctly handle an error inside [dns.Runner.Run].
l := zaptest.NewLogger(t)

Expand Down Expand Up @@ -188,7 +190,7 @@ type testServer struct {

var errFailed = errors.New("listen failure")

func (ts *testServer) ListenAndServe() error { return errFailed }
func (ts *testServer) ActivateAndServe() error { return errFailed }

func (ts *testServer) Shutdown() error {
ts.t.Fatal("should not be called")
Expand All @@ -204,7 +206,7 @@ type runnerStopper struct {
val atomic.Pointer[chan struct{}]
}

func (rs *runnerStopper) ListenAndServe() error {
func (rs *runnerStopper) ActivateAndServe() error {
ch := make(chan struct{})

if rs.val.Swap(&ch) != nil {
Expand Down

0 comments on commit 83e0b0c

Please sign in to comment.