Move proxier supportedness-checking code into separate files.

(No code changes, just moving between files.)
This commit is contained in:
Dan Winship 2025-09-19 17:05:46 -04:00
parent 3ecc3c9e6e
commit 9f4edccb97
8 changed files with 383 additions and 270 deletions

View file

@ -563,97 +563,6 @@ func getFirstColumn(r io.Reader) ([]string, error) {
return words, nil
}
// CanUseIPVSProxier checks if we can use the ipvs Proxier.
// The ipset version and the scheduler are checked. If any virtual servers (VS)
// already exist with the configured scheduler, we just return. Otherwise
// we check if a dummy VS can be configured with the configured scheduler.
// Kernel modules will be loaded automatically if necessary.
func CanUseIPVSProxier(ctx context.Context, ipvs utilipvs.Interface, ipsetver IPSetVersioner, scheduler string) error {
logger := klog.FromContext(ctx)
// BUG: https://github.com/moby/ipvs/issues/27
// If ipvs is not compiled into the kernel no error is returned and handle==nil.
// This in turn causes ipvs.GetVirtualServers and ipvs.AddVirtualServer
// to return ok (err==nil). If/when this bug is fixed parameter "ipvs" will be nil
// if ipvs is not supported by the kernel. Until then a re-read work-around is used.
if ipvs == nil {
return fmt.Errorf("ipvs not supported by the kernel")
}
// Check ipset version
versionString, err := ipsetver.GetVersion()
if err != nil {
return fmt.Errorf("error getting ipset version, error: %w", err)
}
if !checkMinVersion(versionString) {
return fmt.Errorf("ipset version: %s is less than min required version: %s", versionString, MinIPSetCheckVersion)
}
if scheduler == "" {
scheduler = defaultScheduler
}
// If any virtual server (VS) using the scheduler exist we skip the checks.
vservers, err := ipvs.GetVirtualServers()
if err != nil {
logger.Error(err, "Can't read the ipvs")
return err
}
logger.V(5).Info("Virtual Servers", "count", len(vservers))
if len(vservers) > 0 {
// This is most likely a kube-proxy re-start. We know that ipvs works
// and if any VS uses the configured scheduler, we are done.
for _, vs := range vservers {
if vs.Scheduler == scheduler {
logger.V(5).Info("VS exist, Skipping checks")
return nil
}
}
logger.V(5).Info("No existing VS uses the configured scheduler", "scheduler", scheduler)
}
// Try to insert a dummy VS with the passed scheduler.
// We should use a VIP address that is not used on the node.
// An address "198.51.100.0" from the TEST-NET-2 rage in https://datatracker.ietf.org/doc/html/rfc5737
// is used. These addresses are reserved for documentation. If the user is using
// this address for a VS anyway we *will* mess up, but that would be an invalid configuration.
// If the user have configured the address to an interface on the node (but not a VS)
// then traffic will temporary be routed to ipvs during the probe and dropped.
// The later case is also and invalid configuration, but the traffic impact will be minor.
// This should not be a problem if users honors reserved addresses, but cut/paste
// from documentation is not unheard of, so the restriction to not use the TEST-NET-2 range
// must be documented.
vs := utilipvs.VirtualServer{
Address: netutils.ParseIPSloppy("198.51.100.0"),
Protocol: "TCP",
Port: 20000,
Scheduler: scheduler,
}
if err := ipvs.AddVirtualServer(&vs); err != nil {
logger.Error(err, "Could not create dummy VS", "scheduler", scheduler)
return err
}
// To overcome the BUG described above we check that the VS is *really* added.
vservers, err = ipvs.GetVirtualServers()
if err != nil {
logger.Error(err, "ipvs.GetVirtualServers")
return err
}
logger.V(5).Info("Virtual Servers after adding dummy", "count", len(vservers))
if len(vservers) == 0 {
logger.Info("Dummy VS not created", "scheduler", scheduler)
return fmt.Errorf("ipvs not supported") // This is a BUG work-around
}
logger.V(5).Info("Dummy VS created", "vs", vs)
if err := ipvs.DeleteVirtualServer(&vs); err != nil {
logger.Error(err, "Could not delete dummy VS")
return err
}
return nil
}
// CleanupIptablesLeftovers removes all iptables rules and chains created by the Proxier
// It returns true if an error was encountered. Errors are logged.
func cleanupIptablesLeftovers(ctx context.Context, ipt utiliptables.Interface) (encounteredError bool) {

View file

@ -349,64 +349,6 @@ func TestCleanupLeftovers(t *testing.T) {
// FIXME: check ipvs and ipset state
}
func TestCanUseIPVSProxier(t *testing.T) {
_, ctx := ktesting.NewTestContext(t)
testCases := []struct {
name string
scheduler string
ipsetVersion string
ipsetErr error
ipvsErr string
ok bool
}{
{
name: "happy days",
ipsetVersion: MinIPSetCheckVersion,
ok: true,
},
{
name: "ipset error",
scheduler: "",
ipsetVersion: MinIPSetCheckVersion,
ipsetErr: fmt.Errorf("oops"),
ok: false,
},
{
name: "ipset version too low",
scheduler: "rr",
ipsetVersion: "4.3.0",
ok: false,
},
{
name: "GetVirtualServers fail",
ipsetVersion: MinIPSetCheckVersion,
ipvsErr: "GetVirtualServers",
ok: false,
},
{
name: "AddVirtualServer fail",
ipsetVersion: MinIPSetCheckVersion,
ipvsErr: "AddVirtualServer",
ok: false,
},
{
name: "DeleteVirtualServer fail",
ipsetVersion: MinIPSetCheckVersion,
ipvsErr: "DeleteVirtualServer",
ok: false,
},
}
for _, tc := range testCases {
ipvs := &fakeIpvs{tc.ipvsErr, false}
versioner := &fakeIPSetVersioner{version: tc.ipsetVersion, err: tc.ipsetErr}
err := CanUseIPVSProxier(ctx, ipvs, versioner, tc.scheduler)
if (err == nil) != tc.ok {
t.Errorf("Case [%s], expect %v, got err: %v", tc.name, tc.ok, err)
}
}
}
func TestGetNodeIPs(t *testing.T) {
testCases := []struct {
isIPv6 bool

120
pkg/proxy/ipvs/supported.go Normal file
View file

@ -0,0 +1,120 @@
//go:build linux
// +build linux
/*
Copyright 2017 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package ipvs
import (
"context"
"fmt"
"k8s.io/klog/v2"
utilipvs "k8s.io/kubernetes/pkg/proxy/ipvs/util"
netutils "k8s.io/utils/net"
)
// CanUseIPVSProxier checks if we can use the ipvs Proxier.
// The ipset version and the scheduler are checked. If any virtual servers (VS)
// already exist with the configured scheduler, we just return. Otherwise
// we check if a dummy VS can be configured with the configured scheduler.
// Kernel modules will be loaded automatically if necessary.
func CanUseIPVSProxier(ctx context.Context, ipvs utilipvs.Interface, ipsetver IPSetVersioner, scheduler string) error {
logger := klog.FromContext(ctx)
// BUG: https://github.com/moby/ipvs/issues/27
// If ipvs is not compiled into the kernel no error is returned and handle==nil.
// This in turn causes ipvs.GetVirtualServers and ipvs.AddVirtualServer
// to return ok (err==nil). If/when this bug is fixed parameter "ipvs" will be nil
// if ipvs is not supported by the kernel. Until then a re-read work-around is used.
if ipvs == nil {
return fmt.Errorf("ipvs not supported by the kernel")
}
// Check ipset version
versionString, err := ipsetver.GetVersion()
if err != nil {
return fmt.Errorf("error getting ipset version, error: %w", err)
}
if !checkMinVersion(versionString) {
return fmt.Errorf("ipset version: %s is less than min required version: %s", versionString, MinIPSetCheckVersion)
}
if scheduler == "" {
scheduler = defaultScheduler
}
// If any virtual server (VS) using the scheduler exist we skip the checks.
vservers, err := ipvs.GetVirtualServers()
if err != nil {
logger.Error(err, "Can't read the ipvs")
return err
}
logger.V(5).Info("Virtual Servers", "count", len(vservers))
if len(vservers) > 0 {
// This is most likely a kube-proxy re-start. We know that ipvs works
// and if any VS uses the configured scheduler, we are done.
for _, vs := range vservers {
if vs.Scheduler == scheduler {
logger.V(5).Info("VS exist, Skipping checks")
return nil
}
}
logger.V(5).Info("No existing VS uses the configured scheduler", "scheduler", scheduler)
}
// Try to insert a dummy VS with the passed scheduler.
// We should use a VIP address that is not used on the node.
// An address "198.51.100.0" from the TEST-NET-2 rage in https://datatracker.ietf.org/doc/html/rfc5737
// is used. These addresses are reserved for documentation. If the user is using
// this address for a VS anyway we *will* mess up, but that would be an invalid configuration.
// If the user have configured the address to an interface on the node (but not a VS)
// then traffic will temporary be routed to ipvs during the probe and dropped.
// The later case is also and invalid configuration, but the traffic impact will be minor.
// This should not be a problem if users honors reserved addresses, but cut/paste
// from documentation is not unheard of, so the restriction to not use the TEST-NET-2 range
// must be documented.
vs := utilipvs.VirtualServer{
Address: netutils.ParseIPSloppy("198.51.100.0"),
Protocol: "TCP",
Port: 20000,
Scheduler: scheduler,
}
if err := ipvs.AddVirtualServer(&vs); err != nil {
logger.Error(err, "Could not create dummy VS", "scheduler", scheduler)
return err
}
// To overcome the BUG described above we check that the VS is *really* added.
vservers, err = ipvs.GetVirtualServers()
if err != nil {
logger.Error(err, "ipvs.GetVirtualServers")
return err
}
logger.V(5).Info("Virtual Servers after adding dummy", "count", len(vservers))
if len(vservers) == 0 {
logger.Info("Dummy VS not created", "scheduler", scheduler)
return fmt.Errorf("ipvs not supported") // This is a BUG work-around
}
logger.V(5).Info("Dummy VS created", "vs", vs)
if err := ipvs.DeleteVirtualServer(&vs); err != nil {
logger.Error(err, "Could not delete dummy VS")
return err
}
return nil
}

View file

@ -0,0 +1,85 @@
//go:build linux
// +build linux
/*
Copyright 2017 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package ipvs
import (
"fmt"
"testing"
"k8s.io/kubernetes/test/utils/ktesting"
)
func TestCanUseIPVSProxier(t *testing.T) {
_, ctx := ktesting.NewTestContext(t)
testCases := []struct {
name string
scheduler string
ipsetVersion string
ipsetErr error
ipvsErr string
ok bool
}{
{
name: "happy days",
ipsetVersion: MinIPSetCheckVersion,
ok: true,
},
{
name: "ipset error",
scheduler: "",
ipsetVersion: MinIPSetCheckVersion,
ipsetErr: fmt.Errorf("oops"),
ok: false,
},
{
name: "ipset version too low",
scheduler: "rr",
ipsetVersion: "4.3.0",
ok: false,
},
{
name: "GetVirtualServers fail",
ipsetVersion: MinIPSetCheckVersion,
ipvsErr: "GetVirtualServers",
ok: false,
},
{
name: "AddVirtualServer fail",
ipsetVersion: MinIPSetCheckVersion,
ipvsErr: "AddVirtualServer",
ok: false,
},
{
name: "DeleteVirtualServer fail",
ipsetVersion: MinIPSetCheckVersion,
ipvsErr: "DeleteVirtualServer",
ok: false,
},
}
for _, tc := range testCases {
ipvs := &fakeIpvs{tc.ipvsErr, false}
versioner := &fakeIPSetVersioner{version: tc.ipsetVersion, err: tc.ipsetErr}
err := CanUseIPVSProxier(ctx, ipvs, versioner, tc.scheduler)
if (err == nil) != tc.ok {
t.Errorf("Case [%s], expect %v, got err: %v", tc.name, tc.ok, err)
}
}
}

View file

@ -25,7 +25,6 @@ import (
"encoding/base32"
"fmt"
"net"
"os"
"os/exec"
"strconv"
"strings"
@ -39,7 +38,6 @@ import (
discovery "k8s.io/api/discovery/v1"
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/sets"
"k8s.io/apimachinery/pkg/util/version"
"k8s.io/apimachinery/pkg/util/wait"
"k8s.io/client-go/tools/events"
"k8s.io/klog/v2"
@ -50,7 +48,6 @@ import (
"k8s.io/kubernetes/pkg/proxy/metrics"
"k8s.io/kubernetes/pkg/proxy/runner"
proxyutil "k8s.io/kubernetes/pkg/proxy/util"
utilkernel "k8s.io/kubernetes/pkg/util/kernel"
netutils "k8s.io/utils/net"
"k8s.io/utils/ptr"
"sigs.k8s.io/knftables"
@ -279,52 +276,6 @@ func NewProxier(ctx context.Context,
return proxier, nil
}
// Create a knftables.Interface and check if we can use the nftables proxy mode on this host.
func getNFTablesInterface(ipFamily v1.IPFamily) (knftables.Interface, error) {
var nftablesFamily knftables.Family
if ipFamily == v1.IPv4Protocol {
nftablesFamily = knftables.IPv4Family
} else {
nftablesFamily = knftables.IPv6Family
}
// We require (or rather, knftables.New does) that the nft binary be version 1.0.1
// or later, because versions before that would always attempt to parse the entire
// nft ruleset at startup, even if you were only operating on a single table.
// That's bad, because in some cases, new versions of nft have added new rule
// types in ways that triggered bugs in older versions of nft, causing them to
// crash. Thus, if kube-proxy used nft < 1.0.1, it could potentially get locked
// out of its rules because of something some other component had done in a
// completely different table.
nft, err := knftables.New(nftablesFamily, kubeProxyTable)
if err != nil {
return nil, err
}
// Likewise, we want to ensure that the host filesystem has nft >= 1.0.1, so that
// it's not possible that *our* rules break *the system's* nft. (In particular, we
// know that if kube-proxy uses nft >= 1.0.3 and the system has nft <= 0.9.8, that
// the system nft will become completely unusable.) Unfortunately, we can't easily
// figure out the version of nft installed on the host filesystem, so instead, we
// check the kernel version, under the assumption that the distro will have an nft
// binary that supports the same features as its kernel does, and so kernel 5.13
// or later implies nft 1.0.1 or later. https://issues.k8s.io/122743
//
// However, we allow the user to bypass this check by setting
// `KUBE_PROXY_NFTABLES_SKIP_KERNEL_VERSION_CHECK` to anything non-empty.
if os.Getenv("KUBE_PROXY_NFTABLES_SKIP_KERNEL_VERSION_CHECK") == "" {
kernelVersion, err := utilkernel.GetVersion()
if err != nil {
return nil, fmt.Errorf("could not check kernel version: %w", err)
}
if kernelVersion.LessThan(version.MustParseGeneric(utilkernel.NFTablesKubeProxyKernelVersion)) {
return nil, fmt.Errorf("kube-proxy in nftables mode requires kernel %s or later", utilkernel.NFTablesKubeProxyKernelVersion)
}
}
return nft, nil
}
// internal struct for string service information
type servicePortInfo struct {
*proxy.BaseServicePortInfo

View file

@ -0,0 +1,76 @@
//go:build linux
// +build linux
/*
Copyright 2015 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package nftables
import (
"fmt"
"os"
v1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/util/version"
utilkernel "k8s.io/kubernetes/pkg/util/kernel"
"sigs.k8s.io/knftables"
)
// Create a knftables.Interface and check if we can use the nftables proxy mode on this host.
func getNFTablesInterface(ipFamily v1.IPFamily) (knftables.Interface, error) {
var nftablesFamily knftables.Family
if ipFamily == v1.IPv4Protocol {
nftablesFamily = knftables.IPv4Family
} else {
nftablesFamily = knftables.IPv6Family
}
// We require (or rather, knftables.New does) that the nft binary be version 1.0.1
// or later, because versions before that would always attempt to parse the entire
// nft ruleset at startup, even if you were only operating on a single table.
// That's bad, because in some cases, new versions of nft have added new rule
// types in ways that triggered bugs in older versions of nft, causing them to
// crash. Thus, if kube-proxy used nft < 1.0.1, it could potentially get locked
// out of its rules because of something some other component had done in a
// completely different table.
nft, err := knftables.New(nftablesFamily, kubeProxyTable)
if err != nil {
return nil, err
}
// Likewise, we want to ensure that the host filesystem has nft >= 1.0.1, so that
// it's not possible that *our* rules break *the system's* nft. (In particular, we
// know that if kube-proxy uses nft >= 1.0.3 and the system has nft <= 0.9.8, that
// the system nft will become completely unusable.) Unfortunately, we can't easily
// figure out the version of nft installed on the host filesystem, so instead, we
// check the kernel version, under the assumption that the distro will have an nft
// binary that supports the same features as its kernel does, and so kernel 5.13
// or later implies nft 1.0.1 or later. https://issues.k8s.io/122743
//
// However, we allow the user to bypass this check by setting
// `KUBE_PROXY_NFTABLES_SKIP_KERNEL_VERSION_CHECK` to anything non-empty.
if os.Getenv("KUBE_PROXY_NFTABLES_SKIP_KERNEL_VERSION_CHECK") == "" {
kernelVersion, err := utilkernel.GetVersion()
if err != nil {
return nil, fmt.Errorf("could not check kernel version: %w", err)
}
if kernelVersion.LessThan(version.MustParseGeneric(utilkernel.NFTablesKubeProxyKernelVersion)) {
return nil, fmt.Errorf("kube-proxy in nftables mode requires kernel %s or later", utilkernel.NFTablesKubeProxyKernelVersion)
}
}
return nft, nil
}

View file

@ -52,38 +52,10 @@ import (
netutils "k8s.io/utils/net"
)
// KernelCompatTester tests whether the required kernel capabilities are
// present to run the windows kernel proxier.
type KernelCompatTester interface {
IsCompatible() error
}
type HostMacProvider interface {
GetHostMac(nodeIP net.IP) string
}
// CanUseWinKernelProxier returns true if we should use the Kernel Proxier
// instead of the "classic" userspace Proxier. This is determined by checking
// the windows kernel version and for the existence of kernel features.
func CanUseWinKernelProxier(kcompat KernelCompatTester) (bool, error) {
// Check that the kernel supports what we need.
if err := kcompat.IsCompatible(); err != nil {
return false, err
}
return true, nil
}
type WindowsKernelCompatTester struct{}
// IsCompatible returns true if winkernel can support this mode of proxy
func (lkct WindowsKernelCompatTester) IsCompatible() error {
_, err := hnslib.HNSListPolicyListRequest()
if err != nil {
return fmt.Errorf("Windows kernel is not compatible for Kernel mode")
}
return nil
}
type externalIPInfo struct {
ip string
hnsID string
@ -266,50 +238,6 @@ func isOverlay(hnsNetworkInfo *hnsNetworkInfo) bool {
return strings.EqualFold(hnsNetworkInfo.networkType, NETWORK_TYPE_OVERLAY)
}
// StackCompatTester tests whether the required kernel and network are dualstack capable
type StackCompatTester interface {
DualStackCompatible(networkName string) bool
}
type DualStackCompatTester struct{}
func (t DualStackCompatTester) DualStackCompatible(networkName string) bool {
hcnImpl := newHcnImpl()
// First tag of hnslib that has a proper check for dual stack support is v0.8.22 due to a bug.
if err := hcnImpl.Ipv6DualStackSupported(); err != nil {
// Hcn *can* fail the query to grab the version of hcn itself (which this call will do internally before parsing
// to see if dual stack is supported), but the only time this can happen, at least that can be discerned, is if the host
// is pre-1803 and hcn didn't exist. hnslib should truthfully return a known error if this happened that we can
// check against, and the case where 'err != this known error' would be the 'this feature isn't supported' case, as is being
// used here. For now, seeming as how nothing before ws2019 (1809) is listed as supported for k8s we can pretty much assume
// any error here isn't because the query failed, it's just that dualstack simply isn't supported on the host. With all
// that in mind, just log as info and not error to let the user know we're falling back.
klog.InfoS("This version of Windows does not support dual-stack, falling back to single-stack", "err", err.Error())
return false
}
// check if network is using overlay
hns, _ := newHostNetworkService(hcnImpl)
networkName, err := getNetworkName(networkName)
if err != nil {
klog.ErrorS(err, "Unable to determine dual-stack status, falling back to single-stack")
return false
}
networkInfo, err := getNetworkInfo(hns, networkName)
if err != nil {
klog.ErrorS(err, "Unable to determine dual-stack status, falling back to single-stack")
return false
}
if utilfeature.DefaultFeatureGate.Enabled(kubefeatures.WinOverlay) && isOverlay(networkInfo) {
// Overlay (VXLAN) networks on Windows do not support dual-stack networking today
klog.InfoS("Winoverlay does not support dual-stack, falling back to single-stack")
return false
}
return true
}
// internal struct for endpoints information
type endpointInfo struct {
ip string

View file

@ -0,0 +1,102 @@
//go:build windows
// +build windows
/*
Copyright 2017 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package winkernel
import (
"fmt"
"github.com/Microsoft/hnslib"
utilfeature "k8s.io/apiserver/pkg/util/feature"
"k8s.io/klog/v2"
kubefeatures "k8s.io/kubernetes/pkg/features"
)
// KernelCompatTester tests whether the required kernel capabilities are
// present to run the windows kernel proxier.
type KernelCompatTester interface {
IsCompatible() error
}
// CanUseWinKernelProxier returns true if we should use the Kernel Proxier
// instead of the "classic" userspace Proxier. This is determined by checking
// the windows kernel version and for the existence of kernel features.
func CanUseWinKernelProxier(kcompat KernelCompatTester) (bool, error) {
// Check that the kernel supports what we need.
if err := kcompat.IsCompatible(); err != nil {
return false, err
}
return true, nil
}
type WindowsKernelCompatTester struct{}
// IsCompatible returns true if winkernel can support this mode of proxy
func (lkct WindowsKernelCompatTester) IsCompatible() error {
_, err := hnslib.HNSListPolicyListRequest()
if err != nil {
return fmt.Errorf("Windows kernel is not compatible for Kernel mode")
}
return nil
}
// StackCompatTester tests whether the required kernel and network are dualstack capable
type StackCompatTester interface {
DualStackCompatible(networkName string) bool
}
type DualStackCompatTester struct{}
func (t DualStackCompatTester) DualStackCompatible(networkName string) bool {
hcnImpl := newHcnImpl()
// First tag of hnslib that has a proper check for dual stack support is v0.8.22 due to a bug.
if err := hcnImpl.Ipv6DualStackSupported(); err != nil {
// Hcn *can* fail the query to grab the version of hcn itself (which this call will do internally before parsing
// to see if dual stack is supported), but the only time this can happen, at least that can be discerned, is if the host
// is pre-1803 and hcn didn't exist. hnslib should truthfully return a known error if this happened that we can
// check against, and the case where 'err != this known error' would be the 'this feature isn't supported' case, as is being
// used here. For now, seeming as how nothing before ws2019 (1809) is listed as supported for k8s we can pretty much assume
// any error here isn't because the query failed, it's just that dualstack simply isn't supported on the host. With all
// that in mind, just log as info and not error to let the user know we're falling back.
klog.InfoS("This version of Windows does not support dual-stack, falling back to single-stack", "err", err.Error())
return false
}
// check if network is using overlay
hns, _ := newHostNetworkService(hcnImpl)
networkName, err := getNetworkName(networkName)
if err != nil {
klog.ErrorS(err, "Unable to determine dual-stack status, falling back to single-stack")
return false
}
networkInfo, err := getNetworkInfo(hns, networkName)
if err != nil {
klog.ErrorS(err, "Unable to determine dual-stack status, falling back to single-stack")
return false
}
if utilfeature.DefaultFeatureGate.Enabled(kubefeatures.WinOverlay) && isOverlay(networkInfo) {
// Overlay (VXLAN) networks on Windows do not support dual-stack networking today
klog.InfoS("Winoverlay does not support dual-stack, falling back to single-stack")
return false
}
return true
}