From 1f2bf32e95fd42f1972c49238ef163fea125bcdb Mon Sep 17 00:00:00 2001 From: Dan Winship Date: Fri, 30 Jun 2023 12:22:19 -0400 Subject: [PATCH] Figure out single-stack/dual-stack support before creating the proxier Rather than having this as part of createProxier(), explicitly figure out what IP families the proxier can support beforehand, and bail out if this conflicts with the detected IP family. --- cmd/kube-proxy/app/server.go | 13 +++++++++- cmd/kube-proxy/app/server_others.go | 38 +++++++++++++++++++--------- cmd/kube-proxy/app/server_windows.go | 37 ++++++++++++++++----------- 3 files changed, 60 insertions(+), 28 deletions(-) diff --git a/cmd/kube-proxy/app/server.go b/cmd/kube-proxy/app/server.go index 6b12ab4e6cb..99992b2da2e 100644 --- a/cmd/kube-proxy/app/server.go +++ b/cmd/kube-proxy/app/server.go @@ -572,7 +572,18 @@ func newProxyServer(config *kubeproxyconfig.KubeProxyConfiguration, master strin return nil, err } - s.Proxier, err = s.createProxier(config) + ipv4Supported, ipv6Supported, dualStackSupported, err := s.platformCheckSupported() + if err != nil { + return nil, err + } else if (s.PrimaryIPFamily == v1.IPv4Protocol && !ipv4Supported) || (s.PrimaryIPFamily == v1.IPv6Protocol && !ipv6Supported) { + return nil, fmt.Errorf("no support for primary IP family %q", s.PrimaryIPFamily) + } else if dualStackSupported { + klog.InfoS("kube-proxy running in dual-stack mode", "primary ipFamily", s.PrimaryIPFamily) + } else { + klog.InfoS("kube-proxy running in single-stack mode", "ipFamily", s.PrimaryIPFamily) + } + + s.Proxier, err = s.createProxier(config, dualStackSupported) if err != nil { return nil, err } diff --git a/cmd/kube-proxy/app/server_others.go b/cmd/kube-proxy/app/server_others.go index 3ad02d7840f..4eee907f2d1 100644 --- a/cmd/kube-proxy/app/server_others.go +++ b/cmd/kube-proxy/app/server_others.go @@ -101,8 +101,32 @@ func (s *ProxyServer) platformSetup() error { return nil } +// platformCheckSupported is called immediately before creating the Proxier, to check +// what IP families are supported (and whether the configuration is usable at all). +func (s *ProxyServer) platformCheckSupported() (ipv4Supported, ipv6Supported, dualStackSupported bool, err error) { + execer := exec.New() + ipt := utiliptables.New(execer, utiliptables.ProtocolIPv4) + ipv4Supported = ipt.Present() + ipt = utiliptables.New(execer, utiliptables.ProtocolIPv6) + ipv6Supported = ipt.Present() + + // The Linux proxies can always support dual-stack if they can support both IPv4 + // and IPv6. + dualStackSupported = ipv4Supported && ipv6Supported + + if !ipv4Supported && !ipv6Supported { + err = fmt.Errorf("iptables is not available on this host") + } else if !ipv4Supported { + klog.InfoS("No iptables support for family", "ipFamily", v1.IPv4Protocol) + } else if !ipv6Supported { + klog.InfoS("No iptables support for family", "ipFamily", v1.IPv6Protocol) + } + + return +} + // createProxier creates the proxy.Provider -func (s *ProxyServer) createProxier(config *proxyconfigapi.KubeProxyConfiguration) (proxy.Provider, error) { +func (s *ProxyServer) createProxier(config *proxyconfigapi.KubeProxyConfiguration, dualStack bool) (proxy.Provider, error) { var proxier proxy.Provider var err error @@ -114,7 +138,6 @@ func (s *ProxyServer) createProxier(config *proxyconfigapi.KubeProxyConfiguratio iptInterface := utiliptables.New(execer, primaryProtocol) var ipt [2]utiliptables.Interface - dualStack := true // While we assume that node supports, we do further checks below // Create iptables handlers for both families, one is already created // Always ordered as IPv4, IPv6 @@ -126,12 +149,7 @@ func (s *ProxyServer) createProxier(config *proxyconfigapi.KubeProxyConfiguratio ipt[1] = iptInterface } - if !ipt[0].Present() { - return nil, fmt.Errorf("iptables is not supported for primary IP family %q", primaryProtocol) - } else if !ipt[1].Present() { - klog.InfoS("kube-proxy running in single-stack mode: secondary ipFamily is not supported", "ipFamily", ipt[1].Protocol()) - dualStack = false - + if !dualStack { // Validate NodePortAddresses is single-stack npaByFamily := proxyutil.MapCIDRsByIPFamily(config.NodePortAddresses) secondaryFamily := proxyutil.OtherIPFamily(s.PrimaryIPFamily) @@ -145,8 +163,6 @@ func (s *ProxyServer) createProxier(config *proxyconfigapi.KubeProxyConfiguratio klog.InfoS("Using iptables Proxier") if dualStack { - klog.InfoS("kube-proxy running in dual-stack mode", "ipFamily", iptInterface.Protocol()) - klog.InfoS("Creating dualStackProxier for iptables") // Always ordered to match []ipt var localDetectors [2]proxyutiliptables.LocalTrafficDetector localDetectors, err = getDualStackLocalDetectorTuple(config.DetectLocalMode, config, ipt, s.podCIDRs) @@ -212,8 +228,6 @@ func (s *ProxyServer) createProxier(config *proxyconfigapi.KubeProxyConfiguratio klog.InfoS("Using ipvs Proxier") if dualStack { - klog.InfoS("Creating dualStackProxier for ipvs") - // Always ordered to match []ipt var localDetectors [2]proxyutiliptables.LocalTrafficDetector localDetectors, err = getDualStackLocalDetectorTuple(config.DetectLocalMode, config, ipt, s.podCIDRs) diff --git a/cmd/kube-proxy/app/server_windows.go b/cmd/kube-proxy/app/server_windows.go index c076abf90dc..62adf4a41d1 100644 --- a/cmd/kube-proxy/app/server_windows.go +++ b/cmd/kube-proxy/app/server_windows.go @@ -30,7 +30,6 @@ import ( // Enable pprof HTTP handlers. _ "net/http/pprof" - "k8s.io/klog/v2" "k8s.io/kubernetes/pkg/proxy" proxyconfigapi "k8s.io/kubernetes/pkg/proxy/apis/config" "k8s.io/kubernetes/pkg/proxy/winkernel" @@ -52,26 +51,38 @@ func (s *ProxyServer) platformSetup() error { return nil } +// platformCheckSupported is called immediately before creating the Proxier, to check +// what IP families are supported (and whether the configuration is usable at all). +func (s *ProxyServer) platformCheckSupported() (ipv4Supported, ipv6Supported, dualStackSupported bool, err error) { + // Check if Kernel proxier can be used at all + _, err = winkernel.CanUseWinKernelProxier(winkernel.WindowsKernelCompatTester{}) + if err != nil { + return false, false, false, err + } + + // winkernel always supports both single-stack IPv4 and single-stack IPv6, but may + // not support dual-stack. + ipv4Supported = true + ipv6Supported = true + + compatTester := winkernel.DualStackCompatTester{} + dualStackSupported = compatTester.DualStackCompatible(s.Config.Winkernel.NetworkName) + + return +} + // createProxier creates the proxy.Provider -func (s *ProxyServer) createProxier(config *proxyconfigapi.KubeProxyConfiguration) (proxy.Provider, error) { +func (s *ProxyServer) createProxier(config *proxyconfigapi.KubeProxyConfiguration, dualStackMode bool) (proxy.Provider, error) { var healthzPort int if len(config.HealthzBindAddress) > 0 { _, port, _ := net.SplitHostPort(config.HealthzBindAddress) healthzPort, _ = strconv.Atoi(port) } - // Check if Kernel Space can be used. - canUseWinKernelProxy, err := winkernel.CanUseWinKernelProxier(winkernel.WindowsKernelCompatTester{}) - if !canUseWinKernelProxy && err != nil { - return nil, err - } - var proxier proxy.Provider + var err error - dualStackMode := getDualStackMode(config.Winkernel.NetworkName, winkernel.DualStackCompatTester{}) if dualStackMode { - klog.InfoS("Creating dualStackProxier for Windows kernel.") - proxier, err = winkernel.NewDualStackProxier( config.IPTables.SyncPeriod.Duration, config.IPTables.MinSyncPeriod.Duration, @@ -103,10 +114,6 @@ func (s *ProxyServer) createProxier(config *proxyconfigapi.KubeProxyConfiguratio return proxier, nil } -func getDualStackMode(networkname string, compatTester winkernel.StackCompatTester) bool { - return compatTester.DualStackCompatible(networkname) -} - // cleanupAndExit cleans up after a previous proxy run func cleanupAndExit() error { return errors.New("--cleanup-and-exit is not implemented on Windows")