fix: increase host dns packet ttl for pods

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>
This commit is contained in:
Dmitriy Matrenichev 2024-05-14 20:36:22 +03:00
parent dedb6d360d
commit 53f5489130
No known key found for this signature in database
GPG Key ID: 94B473337258BFD5
3 changed files with 75 additions and 65 deletions

View File

@ -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)
}
@ -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"
}
@ -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)
}
@ -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)
}

View File

@ -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
// MakeControl creates a control function for setting socket options.
func MakeControl(network string, forwardEnabled bool) (ControlFn, error) {
maxHops := 1
if forwardEnabled {
maxHops = 2
}
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"},
var options []controlOptions
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)
}
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"},
}
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"},
}
)
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 {
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)
@ -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",
}

View File

@ -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"
@ -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)