Skip to content

Commit ac3f873

Browse files
committed
KubeProxy and DockerShim changes for Ipv6 dual stack support on Windows
Signed-off-by: Vinod K L Swamy <[email protected]>
1 parent 875f31e commit ac3f873

File tree

6 files changed

+130
-25
lines changed

6 files changed

+130
-25
lines changed

cmd/kube-proxy/app/BUILD

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,7 @@ go_library(
218218
"//staging/src/k8s.io/apimachinery/pkg/types:go_default_library",
219219
"//staging/src/k8s.io/apimachinery/pkg/util/net:go_default_library",
220220
"//staging/src/k8s.io/component-base/metrics:go_default_library",
221+
"//vendor/k8s.io/utils/net:go_default_library",
221222
],
222223
"//conditions:default": [],
223224
}),

cmd/kube-proxy/app/server_windows.go

Lines changed: 54 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -28,12 +28,15 @@ import (
2828
// Enable pprof HTTP handlers.
2929
_ "net/http/pprof"
3030

31-
"k8s.io/api/core/v1"
31+
v1 "k8s.io/api/core/v1"
3232
"k8s.io/apimachinery/pkg/types"
3333
utilnet "k8s.io/apimachinery/pkg/util/net"
34+
utilfeature "k8s.io/apiserver/pkg/util/feature"
3435
"k8s.io/client-go/tools/record"
3536
"k8s.io/component-base/configz"
3637
"k8s.io/component-base/metrics"
38+
"k8s.io/klog/v2"
39+
"k8s.io/kubernetes/pkg/features"
3740
"k8s.io/kubernetes/pkg/proxy"
3841
proxyconfigapi "k8s.io/kubernetes/pkg/proxy/apis/config"
3942
proxyconfigscheme "k8s.io/kubernetes/pkg/proxy/apis/config/scheme"
@@ -43,8 +46,7 @@ import (
4346
utilnetsh "k8s.io/kubernetes/pkg/util/netsh"
4447
utilnode "k8s.io/kubernetes/pkg/util/node"
4548
"k8s.io/utils/exec"
46-
47-
"k8s.io/klog/v2"
49+
utilsnet "k8s.io/utils/net"
4850
)
4951

5052
// NewProxyServer returns a new ProxyServer.
@@ -102,18 +104,39 @@ func newProxyServer(config *proxyconfigapi.KubeProxyConfiguration, cleanupAndExi
102104
proxyMode := getProxyMode(string(config.Mode), winkernel.WindowsKernelCompatTester{})
103105
if proxyMode == proxyModeKernelspace {
104106
klog.V(0).Info("Using Kernelspace Proxier.")
105-
proxier, err = winkernel.NewProxier(
106-
config.IPTables.SyncPeriod.Duration,
107-
config.IPTables.MinSyncPeriod.Duration,
108-
config.IPTables.MasqueradeAll,
109-
int(*config.IPTables.MasqueradeBit),
110-
config.ClusterCIDR,
111-
hostname,
112-
utilnode.GetNodeIP(client, hostname),
113-
recorder,
114-
healthzServer,
115-
config.Winkernel,
116-
)
107+
isIPv6DualStackEnabled := utilfeature.DefaultFeatureGate.Enabled(features.IPv6DualStack)
108+
if isIPv6DualStackEnabled {
109+
klog.V(0).Info("creating dualStackProxier for Windows kernel.")
110+
111+
proxier, err = winkernel.NewDualStackProxier(
112+
config.IPTables.SyncPeriod.Duration,
113+
config.IPTables.MinSyncPeriod.Duration,
114+
config.IPTables.MasqueradeAll,
115+
int(*config.IPTables.MasqueradeBit),
116+
config.ClusterCIDR,
117+
hostname,
118+
nodeIPTuple(config.BindAddress),
119+
recorder,
120+
healthzServer,
121+
config.Winkernel,
122+
)
123+
} else {
124+
125+
proxier, err = winkernel.NewProxier(
126+
config.IPTables.SyncPeriod.Duration,
127+
config.IPTables.MinSyncPeriod.Duration,
128+
config.IPTables.MasqueradeAll,
129+
int(*config.IPTables.MasqueradeBit),
130+
config.ClusterCIDR,
131+
hostname,
132+
utilnode.GetNodeIP(client, hostname),
133+
recorder,
134+
healthzServer,
135+
config.Winkernel,
136+
)
137+
138+
}
139+
117140
if err != nil {
118141
return nil, fmt.Errorf("unable to create proxier: %v", err)
119142
}
@@ -181,3 +204,19 @@ func tryWinKernelSpaceProxy(kcompat winkernel.KernelCompatTester) string {
181204
klog.V(1).Infof("Can't use winkernel proxy, using userspace proxier")
182205
return proxyModeUserspace
183206
}
207+
208+
// nodeIPTuple takes an addresses and return a tuple (ipv4,ipv6)
209+
// The returned tuple is guaranteed to have the order (ipv4,ipv6). The address NOT of the passed address
210+
// will have "any" address (0.0.0.0 or ::) inserted.
211+
func nodeIPTuple(bindAddress string) [2]net.IP {
212+
nodes := [2]net.IP{net.IPv4zero, net.IPv6zero}
213+
214+
adr := net.ParseIP(bindAddress)
215+
if utilsnet.IsIPv6(adr) {
216+
nodes[1] = adr
217+
} else {
218+
nodes[0] = adr
219+
}
220+
221+
return nodes
222+
}

pkg/kubelet/dockershim/network/cni/cni_windows.go

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,13 @@ package cni
2121
import (
2222
"context"
2323
"fmt"
24-
"time"
25-
2624
cniTypes020 "github.com/containernetworking/cni/pkg/types/020"
2725
runtimeapi "k8s.io/cri-api/pkg/apis/runtime/v1alpha2"
2826
"k8s.io/klog/v2"
2927
kubecontainer "k8s.io/kubernetes/pkg/kubelet/container"
3028
"k8s.io/kubernetes/pkg/kubelet/dockershim/network"
29+
"net"
30+
"time"
3131
)
3232

3333
func getLoNetwork(binDirs []string) *cniNetwork {
@@ -67,7 +67,14 @@ func (plugin *cniNetworkPlugin) GetPodNetworkStatus(namespace string, name strin
6767
klog.Errorf("error while cni parsing result: %s", err)
6868
return nil, err
6969
}
70-
return &network.PodNetworkStatus{IP: result020.IP4.IP.IP}, nil
70+
71+
var list = []net.IP{result020.IP4.IP.IP}
72+
73+
if result020.IP6 != nil {
74+
list = append(list, result020.IP6.IP.IP)
75+
}
76+
77+
return &network.PodNetworkStatus{IP: result020.IP4.IP.IP, IPs: list}, nil
7178
}
7279

7380
// buildDNSCapabilities builds cniDNSConfig from runtimeapi.DNSConfig.

pkg/proxy/winkernel/BUILD

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ go_library(
2121
"//pkg/proxy/apis/config:go_default_library",
2222
"//pkg/proxy/config:go_default_library",
2323
"//pkg/proxy/healthcheck:go_default_library",
24+
"//pkg/proxy/metaproxier:go_default_library",
2425
"//pkg/proxy/metrics:go_default_library",
2526
"//pkg/util/async:go_default_library",
2627
"//staging/src/k8s.io/api/core/v1:go_default_library",
@@ -35,6 +36,7 @@ go_library(
3536
"//vendor/github.com/Microsoft/hcsshim/hcn:go_default_library",
3637
"//vendor/github.com/davecgh/go-spew/spew:go_default_library",
3738
"//vendor/k8s.io/klog/v2:go_default_library",
39+
"//vendor/k8s.io/utils/net:go_default_library",
3840
],
3941
"//conditions:default": [],
4042
}),

pkg/proxy/winkernel/hnsV2.go

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,11 @@ import (
3030

3131
type hnsV2 struct{}
3232

33+
var (
34+
// LoadBalancerFlagsIPv6 enables IPV6.
35+
LoadBalancerFlagsIPv6 hcn.LoadBalancerFlags = 2
36+
)
37+
3338
func (hns hnsV2) getNetworkByName(name string) (*hnsNetworkInfo, error) {
3439
hnsnetwork, err := hcn.GetNetworkByName(name)
3540
if err != nil {
@@ -90,10 +95,14 @@ func (hns hnsV2) getEndpointByIpAddress(ip string, networkName string) (*endpoin
9095
equal := false
9196
if endpoint.IpConfigurations != nil && len(endpoint.IpConfigurations) > 0 {
9297
equal = endpoint.IpConfigurations[0].IpAddress == ip
98+
99+
if !equal && len(endpoint.IpConfigurations) > 1 {
100+
equal = endpoint.IpConfigurations[1].IpAddress == ip
101+
}
93102
}
94103
if equal && strings.EqualFold(endpoint.HostComputeNetwork, hnsnetwork.Id) {
95104
return &endpointsInfo{
96-
ip: endpoint.IpConfigurations[0].IpAddress,
105+
ip: ip,
97106
isLocal: uint32(endpoint.Flags&hcn.EndpointFlagsRemoteEndpoint) == 0, //TODO: Change isLocal to isRemote
98107
macAddress: endpoint.MacAddress,
99108
hnsID: endpoint.Id,
@@ -232,6 +241,10 @@ func (hns hnsV2) getLoadBalancer(endpoints []endpointsInfo, flags loadBalancerFl
232241
lbFlags |= hcn.LoadBalancerFlagsDSR
233242
}
234243

244+
if flags.isIPv6 {
245+
lbFlags |= LoadBalancerFlagsIPv6
246+
}
247+
235248
lbDistributionType := hcn.LoadBalancerDistributionNone
236249

237250
if flags.sessionAffinity {

pkg/proxy/winkernel/proxier.go

Lines changed: 49 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -48,8 +48,10 @@ import (
4848
"k8s.io/kubernetes/pkg/proxy/apis/config"
4949
proxyconfig "k8s.io/kubernetes/pkg/proxy/config"
5050
"k8s.io/kubernetes/pkg/proxy/healthcheck"
51+
"k8s.io/kubernetes/pkg/proxy/metaproxier"
5152
"k8s.io/kubernetes/pkg/proxy/metrics"
5253
"k8s.io/kubernetes/pkg/util/async"
54+
utilnet "k8s.io/utils/net"
5355
)
5456

5557
// KernelCompatTester tests whether the required kernel capabilities are
@@ -101,6 +103,7 @@ type loadBalancerFlags struct {
101103
useMUX bool
102104
preserveDIP bool
103105
sessionAffinity bool
106+
isIPv6 bool
104107
}
105108

106109
// internal struct for string service information
@@ -163,13 +166,17 @@ type endpointsInfo struct {
163166
hns HostNetworkService
164167
}
165168

166-
//Uses mac prefix and IPv4 address to return a mac address
169+
//Uses mac prefix and IP address to return a mac address
167170
//This ensures mac addresses are unique for proper load balancing
168-
//Does not support IPv6 and returns a dummy mac
171+
//There is a possibility of MAC collisions but this Mac address is used for remote endpoints only
172+
//and not sent on the wire.
169173
func conjureMac(macPrefix string, ip net.IP) string {
170174
if ip4 := ip.To4(); ip4 != nil {
171175
a, b, c, d := ip4[0], ip4[1], ip4[2], ip4[3]
172176
return fmt.Sprintf("%v-%02x-%02x-%02x-%02x", macPrefix, a, b, c, d)
177+
} else if ip6 := ip.To16(); ip6 != nil {
178+
a, b, c, d := ip6[15], ip6[14], ip6[13], ip6[12]
179+
return fmt.Sprintf("%v-%02x-%02x-%02x-%02x", macPrefix, a, b, c, d)
173180
}
174181
return "02-11-22-33-44-55"
175182
}
@@ -502,6 +509,7 @@ type Proxier struct {
502509
// with some partial data after kube-proxy restart.
503510
endpointsSynced bool
504511
servicesSynced bool
512+
isIPv6Mode bool
505513
initialized int32
506514
syncRunner *async.BoundedFrequencyRunner // governs calls to syncProxyRules
507515

@@ -664,6 +672,8 @@ func NewProxier(
664672
}
665673
}
666674

675+
isIPv6 := utilnet.IsIPv6(nodeIP)
676+
667677
proxier := &Proxier{
668678
portsMap: make(map[localPort]closeable),
669679
serviceMap: make(proxyServiceMap),
@@ -685,6 +695,7 @@ func NewProxier(
685695
hostMac: hostMac,
686696
isDSR: isDSR,
687697
supportedFeatures: supportedFeatures,
698+
isIPv6Mode: isIPv6,
688699
}
689700

690701
burstSyncs := 2
@@ -694,6 +705,38 @@ func NewProxier(
694705

695706
}
696707

708+
func NewDualStackProxier(
709+
syncPeriod time.Duration,
710+
minSyncPeriod time.Duration,
711+
masqueradeAll bool,
712+
masqueradeBit int,
713+
clusterCIDR string,
714+
hostname string,
715+
nodeIP [2]net.IP,
716+
recorder record.EventRecorder,
717+
healthzServer healthcheck.ProxierHealthUpdater,
718+
config config.KubeProxyWinkernelConfiguration,
719+
) (proxy.Provider, error) {
720+
721+
// Create an ipv4 instance of the single-stack proxier
722+
ipv4Proxier, err := NewProxier(syncPeriod, minSyncPeriod, masqueradeAll, masqueradeBit,
723+
clusterCIDR, hostname, nodeIP[0], recorder, healthzServer, config)
724+
725+
if err != nil {
726+
return nil, fmt.Errorf("unable to create ipv4 proxier: %v, hostname: %s, clusterCIDR : %s, nodeIP:%v", err, hostname, clusterCIDR, nodeIP[0])
727+
}
728+
729+
ipv6Proxier, err := NewProxier(syncPeriod, minSyncPeriod, masqueradeAll, masqueradeBit,
730+
clusterCIDR, hostname, nodeIP[1], recorder, healthzServer, config)
731+
if err != nil {
732+
return nil, fmt.Errorf("unable to create ipv6 proxier: %v, hostname: %s, clusterCIDR : %s, nodeIP:%v", err, hostname, clusterCIDR, nodeIP[1])
733+
}
734+
735+
// Return a meta-proxier that dispatch calls between the two
736+
// single-stack proxier instances
737+
return metaproxier.NewMetaProxier(ipv4Proxier, ipv6Proxier), nil
738+
}
739+
697740
// CleanupLeftovers removes all hns rules created by the Proxier
698741
// It returns true if an error was encountered. Errors are logged.
699742
func CleanupLeftovers() (encounteredError bool) {
@@ -1275,7 +1318,7 @@ func (proxier *Proxier) syncProxyRules() {
12751318

12761319
hnsLoadBalancer, err := hns.getLoadBalancer(
12771320
hnsEndpoints,
1278-
loadBalancerFlags{isDSR: proxier.isDSR, sessionAffinity: sessionAffinityClientIP},
1321+
loadBalancerFlags{isDSR: proxier.isDSR, isIPv6: proxier.isIPv6Mode, sessionAffinity: sessionAffinityClientIP},
12791322
sourceVip,
12801323
svcInfo.clusterIP.String(),
12811324
Enum(svcInfo.protocol),
@@ -1300,7 +1343,7 @@ func (proxier *Proxier) syncProxyRules() {
13001343
}
13011344
hnsLoadBalancer, err := hns.getLoadBalancer(
13021345
nodePortEndpoints,
1303-
loadBalancerFlags{localRoutedVIP: true, sessionAffinity: sessionAffinityClientIP},
1346+
loadBalancerFlags{localRoutedVIP: true, sessionAffinity: sessionAffinityClientIP, isIPv6: proxier.isIPv6Mode},
13041347
sourceVip,
13051348
"",
13061349
Enum(svcInfo.protocol),
@@ -1321,7 +1364,7 @@ func (proxier *Proxier) syncProxyRules() {
13211364
// Try loading existing policies, if already available
13221365
hnsLoadBalancer, err = hns.getLoadBalancer(
13231366
hnsEndpoints,
1324-
loadBalancerFlags{sessionAffinity: sessionAffinityClientIP},
1367+
loadBalancerFlags{sessionAffinity: sessionAffinityClientIP, isIPv6: proxier.isIPv6Mode},
13251368
sourceVip,
13261369
externalIP.ip,
13271370
Enum(svcInfo.protocol),
@@ -1344,7 +1387,7 @@ func (proxier *Proxier) syncProxyRules() {
13441387
}
13451388
hnsLoadBalancer, err := hns.getLoadBalancer(
13461389
lbIngressEndpoints,
1347-
loadBalancerFlags{isDSR: svcInfo.preserveDIP || proxier.isDSR, useMUX: svcInfo.preserveDIP, preserveDIP: svcInfo.preserveDIP, sessionAffinity: sessionAffinityClientIP},
1390+
loadBalancerFlags{isDSR: svcInfo.preserveDIP || proxier.isDSR, useMUX: svcInfo.preserveDIP, preserveDIP: svcInfo.preserveDIP, sessionAffinity: sessionAffinityClientIP, isIPv6: proxier.isIPv6Mode},
13481391
sourceVip,
13491392
lbIngressIP.ip,
13501393
Enum(svcInfo.protocol),

0 commit comments

Comments
 (0)