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 #8698.

Signed-off-by: Dmitriy Matrenichev <dmitry.matrenichev@siderolabs.com>
  • Loading branch information
DmitriyMV committed May 15, 2024
1 parent dedb6d3 commit 53f5489
Show file tree
Hide file tree
Showing 3 changed files with 73 additions and 63 deletions.
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 53f5489

Please sign in to comment.