Skip to content

Commit

Permalink
fix: increase host dns packet ttl for pods
Browse files Browse the repository at this point in the history
This PR fixes incorrect packet TTL if `forwardKubeDNSToHost` is enabled.

Credits go to Julian Wiedmann.
Closes siderolabs#8698.

Signed-off-by: Dmitriy Matrenichev <dmitry.matrenichev@siderolabs.com>
  • Loading branch information
DmitriyMV committed May 15, 2024
1 parent b86edc6 commit f1b19b1
Show file tree
Hide file tree
Showing 5 changed files with 73 additions and 67 deletions.
2 changes: 0 additions & 2 deletions hack/cloud-image-uploader/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,6 @@ github.com/Azure/go-autorest/tracing v0.6.0 h1:TYi4+3m5t6K48TGI9AUdb+IzbnSxvnvUM
github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU=
github.com/AzureAD/microsoft-authentication-library-for-go v1.2.2 h1:XHOnouVk1mxXfQidrMEnLlPk9UMeRtyBTnEFtxkV0kU=
github.com/AzureAD/microsoft-authentication-library-for-go v1.2.2/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI=
github.com/aws/aws-sdk-go v1.51.25 h1:DjTT8mtmsachhV6yrXR8+yhnG6120dazr720nopRsls=
github.com/aws/aws-sdk-go v1.51.25/go.mod h1:LF8svs817+Nz+DmiMQKTO3ubZ/6IaTpq3TjupRn3Eqk=
github.com/aws/aws-sdk-go v1.52.4 h1:9VsBVJ2TKf8xPP3+yIPGSYcEBIEymXsJzQoFgQuyvA0=
github.com/aws/aws-sdk-go v1.52.4/go.mod h1:LF8svs817+Nz+DmiMQKTO3ubZ/6IaTpq3TjupRn3Eqk=
github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM=
Expand Down
2 changes: 0 additions & 2 deletions hack/structprotogen/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,6 @@ golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA=
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/tools v0.20.0 h1:hz/CVckiOxybQvFw6h7b/q80NTr9IUQb4s1IIzW7KNY=
golang.org/x/tools v0.20.0/go.mod h1:WvitBU7JJf6A4jOdg4S1tviW9bhUxkgeCui/0JHctQg=
golang.org/x/tools v0.21.0 h1:qc0xYgIbsSDt9EyWz05J5wfa7LOVW0YTLOXrqdLAWIw=
golang.org/x/tools v0.21.0/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ func (ctrl *DNSResolveCacheController) Run(ctx context.Context, r controller.Run
runnerCfg := runnerConfig{net: netwk, addr: addr}

if _, ok := ctrl.runners[runnerCfg]; !ok {
runner, rErr := newDNSRunner(runnerCfg, ctrl.cache, ctrl.Logger)
runner, rErr := newDNSRunner(runnerCfg, ctrl.cache, ctrl.Logger, cfg.TypedSpec().ServiceHostDNSAddress.IsValid())
if rErr != nil {
return fmt.Errorf("error creating dns runner: %w", rErr)
}
Expand Down Expand Up @@ -256,7 +256,7 @@ type runnerConfig struct {
addr netip.AddrPort
}

func newDNSRunner(cfg runnerConfig, cache *dns.Cache, logger *zap.Logger) (*dns.Server, error) {
func newDNSRunner(cfg runnerConfig, cache *dns.Cache, logger *zap.Logger, forwardEnabled bool) (*dns.Server, error) {
if cfg.addr.Addr().Is6() {
cfg.net += "6"
}
Expand All @@ -265,9 +265,14 @@ func newDNSRunner(cfg runnerConfig, cache *dns.Cache, logger *zap.Logger) (*dns.

var serverOpts dns.ServerOptions

controlFn, ctrlErr := dns.MakeControl(cfg.net, forwardEnabled)
if ctrlErr != nil {
return nil, fmt.Errorf("error creating %q control function: %w", cfg.net, ctrlErr)
}

switch cfg.net {
case "udp", "udp6":
packetConn, err := dns.NewUDPPacketConn(cfg.net, cfg.addr.String())
packetConn, err := dns.NewUDPPacketConn(cfg.net, cfg.addr.String(), controlFn)
if err != nil {
return nil, fmt.Errorf("error creating %q packet conn: %w", cfg.net, err)
}
Expand All @@ -279,7 +284,7 @@ func newDNSRunner(cfg runnerConfig, cache *dns.Cache, logger *zap.Logger) (*dns.
}

case "tcp", "tcp6":
listener, err := dns.NewTCPListener(cfg.net, cfg.addr.String())
listener, err := dns.NewTCPListener(cfg.net, cfg.addr.String(), controlFn)
if err != nil {
return nil, fmt.Errorf("error creating %q listener: %w", cfg.net, err)
}
Expand Down
120 changes: 62 additions & 58 deletions internal/pkg/dns/dns.go
Original file line number Diff line number Diff line change
Expand Up @@ -338,88 +338,76 @@ func (s *Server) Start(onDone func(err error)) (stop func(), stopped <-chan stru
}

// NewTCPListener creates a new TCP listener.
func NewTCPListener(network, addr string) (net.Listener, error) {
var opts []controlOptions

switch network {
case "tcp", "tcp4":
network = "tcp4"
opts = tcpOptions

case "tcp6":
opts = tcpOptionsV6

default:
func NewTCPListener(network, addr string, control ControlFn) (net.Listener, error) {
network, ok := networkNames[network]
if !ok {
return nil, fmt.Errorf("unsupported network: %s", network)
}

lc := net.ListenConfig{Control: makeControl(opts)}
lc := net.ListenConfig{Control: control}

return lc.Listen(context.Background(), network, addr)
}

// NewUDPPacketConn creates a new UDP packet connection.
func NewUDPPacketConn(network, addr string) (net.PacketConn, error) {
var opts []controlOptions

switch network {
case "udp", "udp4":
network = "udp4"
opts = udpOptions

case "udp6":
opts = udpOptionsV6

default:
func NewUDPPacketConn(network, addr string, control ControlFn) (net.PacketConn, error) {
network, ok := networkNames[network]
if !ok {
return nil, fmt.Errorf("unsupported network: %s", network)
}

lc := net.ListenConfig{
Control: makeControl(opts),
}
lc := net.ListenConfig{Control: control}

return lc.ListenPacket(context.Background(), network, addr)
}

var (
tcpOptions = []controlOptions{
{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"},
}
// ControlFn is an alias to [net.ListenConfig.Control] function.
type ControlFn = func(string, string, syscall.RawConn) error

tcpOptionsV6 = []controlOptions{
{unix.IPPROTO_IPV6, unix.IPV6_RECVHOPLIMIT, 1, "failed to set IPV6_RECVHOPLIMIT"},
{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_IPV6, unix.IPV6_UNICAST_HOPS, 1, "failed to set IPV6_UNICAST_HOPS"},
}
// MakeControl creates a control function for setting socket options.
func MakeControl(network string, forwardEnabled bool) (ControlFn, error) {
maxHops := 1

udpOptions = []controlOptions{
{unix.IPPROTO_IP, unix.IP_RECVTTL, 1, "failed to set IP_RECVTTL"},
{unix.IPPROTO_IP, unix.IP_TTL, 1, "failed to set IP_TTL"},
if forwardEnabled {
maxHops = 2
}

udpOptionsV6 = []controlOptions{
{unix.IPPROTO_IPV6, unix.IPV6_RECVHOPLIMIT, 1, "failed to set IPV6_RECVHOPLIMIT"},
{unix.IPPROTO_IPV6, unix.IPV6_UNICAST_HOPS, 1, "failed to set IPV6_UNICAST_HOPS"},
}
)
var options []controlOptions

type controlOptions struct {
level int
opt int
val int
errorMessage string
}
switch network {
case "tcp", "tcp4":
options = []controlOptions{
{unix.IPPROTO_IP, unix.IP_RECVTTL, maxHops, "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, maxHops, "failed to set IP_TTL"},
}
case "tcp6":
options = []controlOptions{
{unix.IPPROTO_IPV6, unix.IPV6_RECVHOPLIMIT, maxHops, "failed to set IPV6_RECVHOPLIMIT"},
{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_IPV6, unix.IPV6_UNICAST_HOPS, maxHops, "failed to set IPV6_UNICAST_HOPS"},
}
case "udp", "udp4":
options = []controlOptions{
{unix.IPPROTO_IP, unix.IP_RECVTTL, maxHops, "failed to set IP_RECVTTL"},
{unix.IPPROTO_IP, unix.IP_TTL, maxHops, "failed to set IP_TTL"},
}
case "udp6":
options = []controlOptions{
{unix.IPPROTO_IPV6, unix.IPV6_RECVHOPLIMIT, maxHops, "failed to set IPV6_RECVHOPLIMIT"},
{unix.IPPROTO_IPV6, unix.IPV6_UNICAST_HOPS, maxHops, "failed to set IPV6_UNICAST_HOPS"},
}
default:
return nil, fmt.Errorf("unsupported network: %s", network)
}

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 {
for _, opt := range options {
opErr := unix.SetsockoptInt(int(fd), opt.level, opt.opt, opt.val)
if opErr != nil {
resErr = fmt.Errorf(opt.errorMessage+": %w", opErr)
Expand All @@ -437,5 +425,21 @@ func makeControl(opts []controlOptions) func(string, string, syscall.RawConn) er
}

return nil
}
}, nil
}

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

var networkNames = map[string]string{
"tcp": "tcp4",
"tcp4": "tcp4",
"tcp6": "tcp6",
"udp": "udp4",
"udp4": "udp4",
"udp6": "udp6",
}
3 changes: 2 additions & 1 deletion internal/pkg/dns/dns_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (

"github.com/coredns/coredns/plugin/pkg/proxy"
dnssrv "github.com/miekg/dns"
"github.com/siderolabs/gen/ensure"
"github.com/siderolabs/gen/xslices"
"github.com/siderolabs/gen/xtesting/check"
"github.com/stretchr/testify/require"
Expand Down Expand Up @@ -120,7 +121,7 @@ func newServer(t *testing.T, nameservers ...string) func() {

handler.SetProxy(pxs)

pc, err := dns.NewUDPPacketConn("udp", "127.0.0.53:10700")
pc, err := dns.NewUDPPacketConn("udp", "127.0.0.53:10700", ensure.Value(dns.MakeControl("udp", false)))
require.NoError(t, err)

nodeHandler := dns.NewNodeHandler(handler, &testResolver{}, l)
Expand Down

0 comments on commit f1b19b1

Please sign in to comment.