Skip to content

Commit

Permalink
Only use dualstack if the node and config supports it
Browse files Browse the repository at this point in the history
  • Loading branch information
jsturtevant committed Jul 19, 2021
1 parent e330793 commit 203834e
Show file tree
Hide file tree
Showing 5 changed files with 214 additions and 75 deletions.
37 changes: 37 additions & 0 deletions cmd/kube-proxy/app/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,14 @@ import (
"errors"
"fmt"
"io/ioutil"
"net"
"net/http"
"os"
"strings"
"time"

utilnode "k8s.io/kubernetes/pkg/util/node"

"github.com/fsnotify/fsnotify"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
Expand Down Expand Up @@ -83,6 +86,7 @@ import (
utilipvs "k8s.io/kubernetes/pkg/util/ipvs"
"k8s.io/kubernetes/pkg/util/oom"
"k8s.io/utils/exec"
utilsnet "k8s.io/utils/net"
utilpointer "k8s.io/utils/pointer"
)

Expand Down Expand Up @@ -814,3 +818,36 @@ func (s *ProxyServer) CleanupAndExit() error {

return nil
}

// detectNodeIP returns the nodeIP used by the proxier
// The order of precedence is:
// 1. config.bindAddress if bindAddress is not 0.0.0.0 or ::
// 2. the primary IP from the Node object, if set
// 3. if no IP is found it defaults to 127.0.0.1 and IPv4
func detectNodeIP(client clientset.Interface, hostname, bindAddress string) net.IP {
nodeIP := net.ParseIP(bindAddress)
if nodeIP.IsUnspecified() {
nodeIP = utilnode.GetNodeIP(client, hostname)
}
if nodeIP == nil {
klog.V(0).Infof("can't determine this node's IP, assuming 127.0.0.1; if this is incorrect, please set the --bind-address flag")
nodeIP = net.ParseIP("127.0.0.1")
}
return nodeIP
}

// nodeIPTuple takes an addresses and return a tuple (ipv4,ipv6)
// The returned tuple is guaranteed to have the order (ipv4,ipv6). The address NOT of the passed address
// will have "any" address (0.0.0.0 or ::) inserted.
func nodeIPTuple(bindAddress string) [2]net.IP {
nodes := [2]net.IP{net.IPv4zero, net.IPv6zero}

adr := net.ParseIP(bindAddress)
if utilsnet.IsIPv6(adr) {
nodes[1] = adr
} else {
nodes[0] = adr
}

return nodes
}
33 changes: 0 additions & 33 deletions cmd/kube-proxy/app/server_others.go
Original file line number Diff line number Diff line change
Expand Up @@ -428,23 +428,6 @@ func waitForPodCIDR(client clientset.Interface, nodeName string) (*v1.Node, erro
return nil, fmt.Errorf("event object not of type node")
}

// detectNodeIP returns the nodeIP used by the proxier
// The order of precedence is:
// 1. config.bindAddress if bindAddress is not 0.0.0.0 or ::
// 2. the primary IP from the Node object, if set
// 3. if no IP is found it defaults to 127.0.0.1 and IPv4
func detectNodeIP(client clientset.Interface, hostname, bindAddress string) net.IP {
nodeIP := net.ParseIP(bindAddress)
if nodeIP.IsUnspecified() {
nodeIP = utilnode.GetNodeIP(client, hostname)
}
if nodeIP == nil {
klog.V(0).Infof("can't determine this node's IP, assuming 127.0.0.1; if this is incorrect, please set the --bind-address flag")
nodeIP = net.ParseIP("127.0.0.1")
}
return nodeIP
}

func detectNumCPU() int {
// try get numCPU from /sys firstly due to a known issue (https://github.com/kubernetes/kubernetes/issues/99225)
_, numCPU, err := machine.GetTopology(sysfs.NewRealSysFs())
Expand Down Expand Up @@ -570,22 +553,6 @@ func cidrTuple(cidrList string) [2]string {
return cidrs
}

// nodeIPTuple takes an addresses and return a tuple (ipv4,ipv6)
// The returned tuple is guaranteed to have the order (ipv4,ipv6). The address NOT of the passed address
// will have "any" address (0.0.0.0 or ::) inserted.
func nodeIPTuple(bindAddress string) [2]net.IP {
nodes := [2]net.IP{net.IPv4zero, net.IPv6zero}

adr := net.ParseIP(bindAddress)
if utilsnet.IsIPv6(adr) {
nodes[1] = adr
} else {
nodes[0] = adr
}

return nodes
}

func getProxyMode(proxyMode string, canUseIPVS bool, kcompat iptables.KernelCompatTester) string {
switch proxyMode {
case proxyModeUserspace:
Expand Down
31 changes: 10 additions & 21 deletions cmd/kube-proxy/app/server_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,6 @@ import (
utilnetsh "k8s.io/kubernetes/pkg/util/netsh"
utilnode "k8s.io/kubernetes/pkg/util/node"
"k8s.io/utils/exec"
utilsnet "k8s.io/utils/net"
)

// NewProxyServer returns a new ProxyServer.
Expand Down Expand Up @@ -85,6 +84,9 @@ func newProxyServer(config *proxyconfigapi.KubeProxyConfiguration, cleanupAndExi
if err != nil {
return nil, err
}
nodeIP := detectNodeIP(client, hostname, config.BindAddress)
klog.InfoS("Detected node IP", "IP", nodeIP.String())

eventBroadcaster := record.NewBroadcaster()
recorder := eventBroadcaster.NewRecorder(proxyconfigscheme.Scheme, v1.EventSource{Component: "kube-proxy", Host: hostname})

Expand All @@ -101,12 +103,11 @@ func newProxyServer(config *proxyconfigapi.KubeProxyConfiguration, cleanupAndExi
}

var proxier proxy.Provider

proxyMode := getProxyMode(string(config.Mode), winkernel.WindowsKernelCompatTester{})
dualStackMode := getDualStackMode(config.Winkernel.NetworkName, winkernel.DualStackCompatTester{})
if proxyMode == proxyModeKernelspace {
klog.V(0).Info("Using Kernelspace Proxier.")
isIPv6DualStackEnabled := utilfeature.DefaultFeatureGate.Enabled(features.IPv6DualStack)
if isIPv6DualStackEnabled {
if dualStackMode {
klog.V(0).Info("creating dualStackProxier for Windows kernel.")

proxier, err = winkernel.NewDualStackProxier(
Expand All @@ -130,7 +131,7 @@ func newProxyServer(config *proxyconfigapi.KubeProxyConfiguration, cleanupAndExi
int(*config.IPTables.MasqueradeBit),
config.ClusterCIDR,
hostname,
utilnode.GetNodeIP(client, hostname),
nodeIP,
recorder,
healthzServer,
config.Winkernel,
Expand Down Expand Up @@ -183,6 +184,10 @@ func newProxyServer(config *proxyconfigapi.KubeProxyConfiguration, cleanupAndExi
}, nil
}

func getDualStackMode(networkname string, compatTester winkernel.StackCompatTester) bool {
return compatTester.DualStackCompatible(networkname)
}

func getProxyMode(proxyMode string, kcompat winkernel.KernelCompatTester) string {
if proxyMode == proxyModeKernelspace {
return tryWinKernelSpaceProxy(kcompat)
Expand Down Expand Up @@ -211,19 +216,3 @@ func tryWinKernelSpaceProxy(kcompat winkernel.KernelCompatTester) string {
klog.V(1).Infof("Can't use winkernel proxy, using userspace proxier")
return proxyModeUserspace
}

// nodeIPTuple takes an addresses and return a tuple (ipv4,ipv6)
// The returned tuple is guaranteed to have the order (ipv4,ipv6). The address NOT of the passed address
// will have "any" address (0.0.0.0 or ::) inserted.
func nodeIPTuple(bindAddress string) [2]net.IP {
nodes := [2]net.IP{net.IPv4zero, net.IPv6zero}

adr := net.ParseIP(bindAddress)
if utilsnet.IsIPv6(adr) {
nodes[1] = adr
} else {
nodes[0] = adr
}

return nodes
}
136 changes: 115 additions & 21 deletions pkg/proxy/winkernel/proxier.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,9 @@ import (
v1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/intstr"
apiutil "k8s.io/apimachinery/pkg/util/net"
"k8s.io/apimachinery/pkg/util/sets"
"k8s.io/apimachinery/pkg/util/version"
"k8s.io/apimachinery/pkg/util/wait"
utilfeature "k8s.io/apiserver/pkg/util/feature"
"k8s.io/client-go/tools/record"
Expand Down Expand Up @@ -138,6 +140,101 @@ type remoteSubnetInfo struct {

const NETWORK_TYPE_OVERLAY = "overlay"

func newHostNetworkService() (HostNetworkService, hcn.SupportedFeatures) {
var hns HostNetworkService
hns = hnsV1{}
supportedFeatures := hcn.GetSupportedFeatures()
if supportedFeatures.Api.V2 {
hns = hnsV2{}
}

return hns, supportedFeatures
}

func getNetworkName(hnsNetworkName string) (string, error) {
if len(hnsNetworkName) == 0 {
klog.V(3).InfoS("network-name flag not set. Checking environment variable")
hnsNetworkName = os.Getenv("KUBE_NETWORK")
if len(hnsNetworkName) == 0 {
return "", fmt.Errorf("Environment variable KUBE_NETWORK and network-flag not initialized")
}
}
return hnsNetworkName, nil
}

func getNetworkInfo(hns HostNetworkService, hnsNetworkName string) (*hnsNetworkInfo, error) {
hnsNetworkInfo, err := hns.getNetworkByName(hnsNetworkName)
for err != nil {
klog.ErrorS(err, "Unable to find HNS Network specified. Please check network name and CNI deployment", "hnsNetworkName", hnsNetworkName)
time.Sleep(1 * time.Second)
hnsNetworkInfo, err = hns.getNetworkByName(hnsNetworkName)
}
return hnsNetworkInfo, err
}

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 {
dualStackFeatureEnabled := utilfeature.DefaultFeatureGate.Enabled(kubefeatures.IPv6DualStack)
if !dualStackFeatureEnabled {
return false
}

globals, err := hcn.GetGlobals()
if err != nil {
klog.ErrorS(err, "Unable to determine networking stack version. Falling back to single-stack")
return false
}

if !kernelSupportsDualstack(globals.Version) {
klog.InfoS("This version of Windows does not support dual-stack. Falling back to single-stack")
return false
}

// check if network is using overlay
hns, _ := newHostNetworkService()
networkName, err = getNetworkName(networkName)
if err != nil {
klog.ErrorS(err, "unable to determine dual-stack status %v. Falling back to single-stack")
return false
}
networkInfo, err := getNetworkInfo(hns, networkName)
if err != nil {
klog.ErrorS(err, "unable to determine dual-stack status %v. 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
}

// The hcsshim version logic has a bug that did not calculate the versioning of DualStack correctly.
// DualStack is supported in WS 2004+ (10.0.19041+) where HCN component version is 11.10+
// https://github.com/microsoft/hcsshim/pull/1003#issuecomment-827930358
func kernelSupportsDualstack(currentVersion hcn.Version) bool {
hnsVersion := fmt.Sprintf("%d.%d.0", currentVersion.Major, currentVersion.Minor)
v, err := version.ParseSemantic(hnsVersion)
if err != nil {
return false
}

return v.AtLeast(version.MustParseSemantic("11.10.0"))
}

func Log(v interface{}, message string, level klog.Level) {
klog.V(level).InfoS("%s", message, "spewConfig", spewSdump(v))
}
Expand Down Expand Up @@ -543,36 +640,24 @@ func NewProxier(
}

serviceHealthServer := healthcheck.NewServiceHealthServer(hostname, recorder)
var hns HostNetworkService
hns = hnsV1{}
supportedFeatures := hcn.GetSupportedFeatures()
if supportedFeatures.Api.V2 {
hns = hnsV2{}
}

hnsNetworkName := config.NetworkName
if len(hnsNetworkName) == 0 {
klog.V(3).InfoS("network-name flag not set. Checking environment variable")
hnsNetworkName = os.Getenv("KUBE_NETWORK")
if len(hnsNetworkName) == 0 {
return nil, fmt.Errorf("Environment variable KUBE_NETWORK and network-flag not initialized")
}
hns, supportedFeatures := newHostNetworkService()
hnsNetworkName, err := getNetworkName(config.NetworkName)
if err != nil {
return nil, err
}

klog.V(3).InfoS("Cleaning up old HNS policy lists")
deleteAllHnsLoadBalancerPolicy()

// Get HNS network information
hnsNetworkInfo, err := hns.getNetworkByName(hnsNetworkName)
for err != nil {
klog.ErrorS(err, "Unable to find HNS Network specified. Please check network name and CNI deployment", "hnsNetworkName", hnsNetworkName)
time.Sleep(1 * time.Second)
hnsNetworkInfo, err = hns.getNetworkByName(hnsNetworkName)
hnsNetworkInfo, err := getNetworkInfo(hns, hnsNetworkName)
if err != nil {
return nil, err
}

// Network could have been detected before Remote Subnet Routes are applied or ManagementIP is updated
// Sleep and update the network to include new information
if strings.EqualFold(hnsNetworkInfo.networkType, NETWORK_TYPE_OVERLAY) {
if isOverlay(hnsNetworkInfo) {
time.Sleep(10 * time.Second)
hnsNetworkInfo, err = hns.getNetworkByName(hnsNetworkName)
if err != nil {
Expand All @@ -592,7 +677,7 @@ func NewProxier(

var sourceVip string
var hostMac string
if strings.EqualFold(hnsNetworkInfo.networkType, NETWORK_TYPE_OVERLAY) {
if isOverlay(hnsNetworkInfo) {
if !utilfeature.DefaultFeatureGate.Enabled(kubefeatures.WinOverlay) {
return nil, fmt.Errorf("WinOverlay feature gate not enabled")
}
Expand All @@ -605,6 +690,15 @@ func NewProxier(
return nil, fmt.Errorf("source-vip flag not set")
}

if nodeIP.IsUnspecified() {
// attempt to get the correct ip address
klog.V(2).InfoS("node ip was unspecified. Attempting to find node ip")
nodeIP, err = apiutil.ResolveBindAddress(nodeIP)
if err != nil {
klog.InfoS("failed to find an ip. You may need set the --bind-address flag", "err", err)
}
}

interfaces, _ := net.Interfaces() //TODO create interfaces
for _, inter := range interfaces {
addresses, _ := inter.Addrs()
Expand Down

0 comments on commit 203834e

Please sign in to comment.