mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-23 03:41:45 +00:00
Merge pull request #82091 from khenidak/ipvs-dualstack
dualstack: IPVS proxier
This commit is contained in:
commit
1ade4ce269
@ -79,6 +79,7 @@ go_library(
|
|||||||
"//pkg/util/node:go_default_library",
|
"//pkg/util/node:go_default_library",
|
||||||
"//staging/src/k8s.io/apimachinery/pkg/types:go_default_library",
|
"//staging/src/k8s.io/apimachinery/pkg/types:go_default_library",
|
||||||
"//staging/src/k8s.io/apimachinery/pkg/util/net:go_default_library",
|
"//staging/src/k8s.io/apimachinery/pkg/util/net:go_default_library",
|
||||||
|
"//vendor/k8s.io/utils/net:go_default_library",
|
||||||
],
|
],
|
||||||
"@io_bazel_rules_go//go/platform:darwin": [
|
"@io_bazel_rules_go//go/platform:darwin": [
|
||||||
"//pkg/proxy/metrics:go_default_library",
|
"//pkg/proxy/metrics:go_default_library",
|
||||||
@ -86,6 +87,7 @@ go_library(
|
|||||||
"//pkg/util/node:go_default_library",
|
"//pkg/util/node:go_default_library",
|
||||||
"//staging/src/k8s.io/apimachinery/pkg/types:go_default_library",
|
"//staging/src/k8s.io/apimachinery/pkg/types:go_default_library",
|
||||||
"//staging/src/k8s.io/apimachinery/pkg/util/net:go_default_library",
|
"//staging/src/k8s.io/apimachinery/pkg/util/net:go_default_library",
|
||||||
|
"//vendor/k8s.io/utils/net:go_default_library",
|
||||||
],
|
],
|
||||||
"@io_bazel_rules_go//go/platform:dragonfly": [
|
"@io_bazel_rules_go//go/platform:dragonfly": [
|
||||||
"//pkg/proxy/metrics:go_default_library",
|
"//pkg/proxy/metrics:go_default_library",
|
||||||
@ -93,6 +95,7 @@ go_library(
|
|||||||
"//pkg/util/node:go_default_library",
|
"//pkg/util/node:go_default_library",
|
||||||
"//staging/src/k8s.io/apimachinery/pkg/types:go_default_library",
|
"//staging/src/k8s.io/apimachinery/pkg/types:go_default_library",
|
||||||
"//staging/src/k8s.io/apimachinery/pkg/util/net:go_default_library",
|
"//staging/src/k8s.io/apimachinery/pkg/util/net:go_default_library",
|
||||||
|
"//vendor/k8s.io/utils/net:go_default_library",
|
||||||
],
|
],
|
||||||
"@io_bazel_rules_go//go/platform:freebsd": [
|
"@io_bazel_rules_go//go/platform:freebsd": [
|
||||||
"//pkg/proxy/metrics:go_default_library",
|
"//pkg/proxy/metrics:go_default_library",
|
||||||
@ -100,6 +103,7 @@ go_library(
|
|||||||
"//pkg/util/node:go_default_library",
|
"//pkg/util/node:go_default_library",
|
||||||
"//staging/src/k8s.io/apimachinery/pkg/types:go_default_library",
|
"//staging/src/k8s.io/apimachinery/pkg/types:go_default_library",
|
||||||
"//staging/src/k8s.io/apimachinery/pkg/util/net:go_default_library",
|
"//staging/src/k8s.io/apimachinery/pkg/util/net:go_default_library",
|
||||||
|
"//vendor/k8s.io/utils/net:go_default_library",
|
||||||
],
|
],
|
||||||
"@io_bazel_rules_go//go/platform:linux": [
|
"@io_bazel_rules_go//go/platform:linux": [
|
||||||
"//pkg/proxy/metrics:go_default_library",
|
"//pkg/proxy/metrics:go_default_library",
|
||||||
@ -107,6 +111,7 @@ go_library(
|
|||||||
"//pkg/util/node:go_default_library",
|
"//pkg/util/node:go_default_library",
|
||||||
"//staging/src/k8s.io/apimachinery/pkg/types:go_default_library",
|
"//staging/src/k8s.io/apimachinery/pkg/types:go_default_library",
|
||||||
"//staging/src/k8s.io/apimachinery/pkg/util/net:go_default_library",
|
"//staging/src/k8s.io/apimachinery/pkg/util/net:go_default_library",
|
||||||
|
"//vendor/k8s.io/utils/net:go_default_library",
|
||||||
],
|
],
|
||||||
"@io_bazel_rules_go//go/platform:nacl": [
|
"@io_bazel_rules_go//go/platform:nacl": [
|
||||||
"//pkg/proxy/metrics:go_default_library",
|
"//pkg/proxy/metrics:go_default_library",
|
||||||
@ -114,6 +119,7 @@ go_library(
|
|||||||
"//pkg/util/node:go_default_library",
|
"//pkg/util/node:go_default_library",
|
||||||
"//staging/src/k8s.io/apimachinery/pkg/types:go_default_library",
|
"//staging/src/k8s.io/apimachinery/pkg/types:go_default_library",
|
||||||
"//staging/src/k8s.io/apimachinery/pkg/util/net:go_default_library",
|
"//staging/src/k8s.io/apimachinery/pkg/util/net:go_default_library",
|
||||||
|
"//vendor/k8s.io/utils/net:go_default_library",
|
||||||
],
|
],
|
||||||
"@io_bazel_rules_go//go/platform:netbsd": [
|
"@io_bazel_rules_go//go/platform:netbsd": [
|
||||||
"//pkg/proxy/metrics:go_default_library",
|
"//pkg/proxy/metrics:go_default_library",
|
||||||
@ -121,6 +127,7 @@ go_library(
|
|||||||
"//pkg/util/node:go_default_library",
|
"//pkg/util/node:go_default_library",
|
||||||
"//staging/src/k8s.io/apimachinery/pkg/types:go_default_library",
|
"//staging/src/k8s.io/apimachinery/pkg/types:go_default_library",
|
||||||
"//staging/src/k8s.io/apimachinery/pkg/util/net:go_default_library",
|
"//staging/src/k8s.io/apimachinery/pkg/util/net:go_default_library",
|
||||||
|
"//vendor/k8s.io/utils/net:go_default_library",
|
||||||
],
|
],
|
||||||
"@io_bazel_rules_go//go/platform:openbsd": [
|
"@io_bazel_rules_go//go/platform:openbsd": [
|
||||||
"//pkg/proxy/metrics:go_default_library",
|
"//pkg/proxy/metrics:go_default_library",
|
||||||
@ -128,6 +135,7 @@ go_library(
|
|||||||
"//pkg/util/node:go_default_library",
|
"//pkg/util/node:go_default_library",
|
||||||
"//staging/src/k8s.io/apimachinery/pkg/types:go_default_library",
|
"//staging/src/k8s.io/apimachinery/pkg/types:go_default_library",
|
||||||
"//staging/src/k8s.io/apimachinery/pkg/util/net:go_default_library",
|
"//staging/src/k8s.io/apimachinery/pkg/util/net:go_default_library",
|
||||||
|
"//vendor/k8s.io/utils/net:go_default_library",
|
||||||
],
|
],
|
||||||
"@io_bazel_rules_go//go/platform:plan9": [
|
"@io_bazel_rules_go//go/platform:plan9": [
|
||||||
"//pkg/proxy/metrics:go_default_library",
|
"//pkg/proxy/metrics:go_default_library",
|
||||||
@ -135,6 +143,7 @@ go_library(
|
|||||||
"//pkg/util/node:go_default_library",
|
"//pkg/util/node:go_default_library",
|
||||||
"//staging/src/k8s.io/apimachinery/pkg/types:go_default_library",
|
"//staging/src/k8s.io/apimachinery/pkg/types:go_default_library",
|
||||||
"//staging/src/k8s.io/apimachinery/pkg/util/net:go_default_library",
|
"//staging/src/k8s.io/apimachinery/pkg/util/net:go_default_library",
|
||||||
|
"//vendor/k8s.io/utils/net:go_default_library",
|
||||||
],
|
],
|
||||||
"@io_bazel_rules_go//go/platform:solaris": [
|
"@io_bazel_rules_go//go/platform:solaris": [
|
||||||
"//pkg/proxy/metrics:go_default_library",
|
"//pkg/proxy/metrics:go_default_library",
|
||||||
@ -142,6 +151,7 @@ go_library(
|
|||||||
"//pkg/util/node:go_default_library",
|
"//pkg/util/node:go_default_library",
|
||||||
"//staging/src/k8s.io/apimachinery/pkg/types:go_default_library",
|
"//staging/src/k8s.io/apimachinery/pkg/types:go_default_library",
|
||||||
"//staging/src/k8s.io/apimachinery/pkg/util/net:go_default_library",
|
"//staging/src/k8s.io/apimachinery/pkg/util/net:go_default_library",
|
||||||
|
"//vendor/k8s.io/utils/net:go_default_library",
|
||||||
],
|
],
|
||||||
"@io_bazel_rules_go//go/platform:windows": [
|
"@io_bazel_rules_go//go/platform:windows": [
|
||||||
"//pkg/proxy/winkernel:go_default_library",
|
"//pkg/proxy/winkernel:go_default_library",
|
||||||
|
@ -24,12 +24,15 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"k8s.io/api/core/v1"
|
"k8s.io/api/core/v1"
|
||||||
"k8s.io/apimachinery/pkg/types"
|
"k8s.io/apimachinery/pkg/types"
|
||||||
utilnet "k8s.io/apimachinery/pkg/util/net"
|
utilnet "k8s.io/apimachinery/pkg/util/net"
|
||||||
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
|
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
|
||||||
|
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||||
"k8s.io/client-go/tools/record"
|
"k8s.io/client-go/tools/record"
|
||||||
|
"k8s.io/kubernetes/pkg/features"
|
||||||
"k8s.io/kubernetes/pkg/proxy"
|
"k8s.io/kubernetes/pkg/proxy"
|
||||||
proxyconfigapi "k8s.io/kubernetes/pkg/proxy/apis/config"
|
proxyconfigapi "k8s.io/kubernetes/pkg/proxy/apis/config"
|
||||||
proxyconfigscheme "k8s.io/kubernetes/pkg/proxy/apis/config/scheme"
|
proxyconfigscheme "k8s.io/kubernetes/pkg/proxy/apis/config/scheme"
|
||||||
@ -46,6 +49,7 @@ import (
|
|||||||
utilnode "k8s.io/kubernetes/pkg/util/node"
|
utilnode "k8s.io/kubernetes/pkg/util/node"
|
||||||
utilsysctl "k8s.io/kubernetes/pkg/util/sysctl"
|
utilsysctl "k8s.io/kubernetes/pkg/util/sysctl"
|
||||||
"k8s.io/utils/exec"
|
"k8s.io/utils/exec"
|
||||||
|
utilsnet "k8s.io/utils/net"
|
||||||
|
|
||||||
"k8s.io/klog"
|
"k8s.io/klog"
|
||||||
)
|
)
|
||||||
@ -170,6 +174,40 @@ func newProxyServer(
|
|||||||
metrics.RegisterMetrics()
|
metrics.RegisterMetrics()
|
||||||
} else if proxyMode == proxyModeIPVS {
|
} else if proxyMode == proxyModeIPVS {
|
||||||
klog.V(0).Info("Using ipvs Proxier.")
|
klog.V(0).Info("Using ipvs Proxier.")
|
||||||
|
if utilfeature.DefaultFeatureGate.Enabled(features.IPv6DualStack) {
|
||||||
|
klog.V(0).Info("creating dualStackProxier for ipvs.")
|
||||||
|
|
||||||
|
// Create iptables handlers for both families, one is already created
|
||||||
|
var ipt [2]utiliptables.Interface
|
||||||
|
if iptInterface.IsIpv6() {
|
||||||
|
ipt[1] = iptInterface
|
||||||
|
ipt[0] = utiliptables.New(execer, dbus, utiliptables.ProtocolIpv4)
|
||||||
|
} else {
|
||||||
|
ipt[0] = iptInterface
|
||||||
|
ipt[1] = utiliptables.New(execer, dbus, utiliptables.ProtocolIpv6)
|
||||||
|
}
|
||||||
|
|
||||||
|
proxier, err = ipvs.NewDualStackProxier(
|
||||||
|
ipt,
|
||||||
|
ipvsInterface,
|
||||||
|
ipsetInterface,
|
||||||
|
utilsysctl.New(),
|
||||||
|
execer,
|
||||||
|
config.IPVS.SyncPeriod.Duration,
|
||||||
|
config.IPVS.MinSyncPeriod.Duration,
|
||||||
|
config.IPVS.ExcludeCIDRs,
|
||||||
|
config.IPVS.StrictARP,
|
||||||
|
config.IPTables.MasqueradeAll,
|
||||||
|
int(*config.IPTables.MasqueradeBit),
|
||||||
|
cidrTuple(config.ClusterCIDR),
|
||||||
|
hostname,
|
||||||
|
nodeIPTuple(config.BindAddress),
|
||||||
|
recorder,
|
||||||
|
healthzServer,
|
||||||
|
config.IPVS.Scheduler,
|
||||||
|
config.NodePortAddresses,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
proxier, err = ipvs.NewProxier(
|
proxier, err = ipvs.NewProxier(
|
||||||
iptInterface,
|
iptInterface,
|
||||||
ipvsInterface,
|
ipvsInterface,
|
||||||
@ -190,6 +228,7 @@ func newProxyServer(
|
|||||||
config.IPVS.Scheduler,
|
config.IPVS.Scheduler,
|
||||||
config.NodePortAddresses,
|
config.NodePortAddresses,
|
||||||
)
|
)
|
||||||
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("unable to create proxier: %v", err)
|
return nil, fmt.Errorf("unable to create proxier: %v", err)
|
||||||
}
|
}
|
||||||
@ -238,6 +277,46 @@ func newProxyServer(
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// cidrTuple takes a comma separated list of CIDRs and return a tuple (ipv4cidr,ipv6cidr)
|
||||||
|
// The returned tuple is guaranteed to have the order (ipv4,ipv6) and if no cidr from a family is found an
|
||||||
|
// empty string "" is inserted.
|
||||||
|
func cidrTuple(cidrList string) [2]string {
|
||||||
|
cidrs := [2]string{"", ""}
|
||||||
|
foundIPv4 := false
|
||||||
|
foundIPv6 := false
|
||||||
|
|
||||||
|
for _, cidr := range strings.Split(cidrList, ",") {
|
||||||
|
if utilsnet.IsIPv6CIDRString(cidr) && !foundIPv6 {
|
||||||
|
cidrs[1] = cidr
|
||||||
|
foundIPv6 = true
|
||||||
|
} else if !foundIPv4 {
|
||||||
|
cidrs[0] = cidr
|
||||||
|
foundIPv4 = true
|
||||||
|
}
|
||||||
|
if foundIPv6 && foundIPv4 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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, khandle ipvs.KernelHandler, ipsetver ipvs.IPSetVersioner, kcompat iptables.KernelCompatTester) string {
|
func getProxyMode(proxyMode string, khandle ipvs.KernelHandler, ipsetver ipvs.IPSetVersioner, kcompat iptables.KernelCompatTester) string {
|
||||||
switch proxyMode {
|
switch proxyMode {
|
||||||
case proxyModeUserspace:
|
case proxyModeUserspace:
|
||||||
|
@ -43,10 +43,12 @@ go_library(
|
|||||||
srcs = [
|
srcs = [
|
||||||
"graceful_termination.go",
|
"graceful_termination.go",
|
||||||
"ipset.go",
|
"ipset.go",
|
||||||
|
"meta_proxier.go",
|
||||||
"netlink.go",
|
"netlink.go",
|
||||||
"netlink_linux.go",
|
"netlink_linux.go",
|
||||||
"netlink_unsupported.go",
|
"netlink_unsupported.go",
|
||||||
"proxier.go",
|
"proxier.go",
|
||||||
|
"safe_ipset.go",
|
||||||
],
|
],
|
||||||
importpath = "k8s.io/kubernetes/pkg/proxy/ipvs",
|
importpath = "k8s.io/kubernetes/pkg/proxy/ipvs",
|
||||||
deps = [
|
deps = [
|
||||||
|
@ -23,6 +23,7 @@ import (
|
|||||||
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"k8s.io/klog"
|
"k8s.io/klog"
|
||||||
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -102,6 +103,20 @@ func NewIPSet(handle utilipset.Interface, name string, setType utilipset.Type, i
|
|||||||
hashFamily := utilipset.ProtocolFamilyIPV4
|
hashFamily := utilipset.ProtocolFamilyIPV4
|
||||||
if isIPv6 {
|
if isIPv6 {
|
||||||
hashFamily = utilipset.ProtocolFamilyIPV6
|
hashFamily = utilipset.ProtocolFamilyIPV6
|
||||||
|
// In dual-stack both ipv4 and ipv6 ipset's can co-exist. To
|
||||||
|
// ensure unique names the prefix for ipv6 is changed from
|
||||||
|
// "KUBE-" to "KUBE-6-". The "KUBE-" prefix is kept for
|
||||||
|
// backward compatibility. The maximum name length of an ipset
|
||||||
|
// is 31 characters which must be taken into account. The
|
||||||
|
// ipv4 names are not altered to minimize the risk for
|
||||||
|
// problems on upgrades.
|
||||||
|
if strings.HasPrefix(name, "KUBE-") {
|
||||||
|
name = strings.Replace(name, "KUBE-", "KUBE-6-", 1)
|
||||||
|
if len(name) > 31 {
|
||||||
|
klog.Warningf("ipset name truncated; [%s] -> [%s]", name, name[:31])
|
||||||
|
name = name[:31]
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
set := &IPSet{
|
set := &IPSet{
|
||||||
IPSet: utilipset.IPSet{
|
IPSet: utilipset.IPSet{
|
||||||
|
203
pkg/proxy/ipvs/meta_proxier.go
Normal file
203
pkg/proxy/ipvs/meta_proxier.go
Normal file
@ -0,0 +1,203 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2019 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"
|
||||||
|
|
||||||
|
"k8s.io/api/core/v1"
|
||||||
|
"k8s.io/klog"
|
||||||
|
"k8s.io/kubernetes/pkg/proxy"
|
||||||
|
utilnet "k8s.io/utils/net"
|
||||||
|
|
||||||
|
discovery "k8s.io/api/discovery/v1alpha1"
|
||||||
|
)
|
||||||
|
|
||||||
|
type metaProxier struct {
|
||||||
|
ipv4Proxier proxy.Provider
|
||||||
|
ipv6Proxier proxy.Provider
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewMetaProxier returns a dual-stack "meta-proxier". Proxier API
|
||||||
|
// calls will be dispatched to the ProxyProvider instances depending
|
||||||
|
// on address family.
|
||||||
|
func NewMetaProxier(ipv4Proxier, ipv6Proxier proxy.Provider) proxy.Provider {
|
||||||
|
return proxy.Provider(&metaProxier{
|
||||||
|
ipv4Proxier: ipv4Proxier,
|
||||||
|
ipv6Proxier: ipv6Proxier,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sync immediately synchronizes the ProxyProvider's current state to
|
||||||
|
// proxy rules.
|
||||||
|
func (proxier *metaProxier) Sync() {
|
||||||
|
proxier.ipv4Proxier.Sync()
|
||||||
|
proxier.ipv6Proxier.Sync()
|
||||||
|
}
|
||||||
|
|
||||||
|
// SyncLoop runs periodic work. This is expected to run as a
|
||||||
|
// goroutine or as the main loop of the app. It does not return.
|
||||||
|
func (proxier *metaProxier) SyncLoop() {
|
||||||
|
go proxier.ipv6Proxier.SyncLoop() // Use go-routine here!
|
||||||
|
proxier.ipv4Proxier.SyncLoop() // never returns
|
||||||
|
}
|
||||||
|
|
||||||
|
// OnServiceAdd is called whenever creation of new service object is observed.
|
||||||
|
func (proxier *metaProxier) OnServiceAdd(service *v1.Service) {
|
||||||
|
if *(service.Spec.IPFamily) == v1.IPv4Protocol {
|
||||||
|
proxier.ipv4Proxier.OnServiceAdd(service)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
proxier.ipv6Proxier.OnServiceAdd(service)
|
||||||
|
}
|
||||||
|
|
||||||
|
// OnServiceUpdate is called whenever modification of an existing
|
||||||
|
// service object is observed.
|
||||||
|
func (proxier *metaProxier) OnServiceUpdate(oldService, service *v1.Service) {
|
||||||
|
// IPFamily is immutable, hence we only need to check on the new service
|
||||||
|
if *(service.Spec.IPFamily) == v1.IPv4Protocol {
|
||||||
|
proxier.ipv4Proxier.OnServiceUpdate(oldService, service)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
proxier.ipv6Proxier.OnServiceUpdate(oldService, service)
|
||||||
|
}
|
||||||
|
|
||||||
|
// OnServiceDelete is called whenever deletion of an existing service
|
||||||
|
// object is observed.
|
||||||
|
func (proxier *metaProxier) OnServiceDelete(service *v1.Service) {
|
||||||
|
if *(service.Spec.IPFamily) == v1.IPv4Protocol {
|
||||||
|
proxier.ipv4Proxier.OnServiceDelete(service)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
proxier.ipv6Proxier.OnServiceDelete(service)
|
||||||
|
}
|
||||||
|
|
||||||
|
// OnServiceSynced is called once all the initial event handlers were
|
||||||
|
// called and the state is fully propagated to local cache.
|
||||||
|
func (proxier *metaProxier) OnServiceSynced() {
|
||||||
|
proxier.ipv4Proxier.OnServiceSynced()
|
||||||
|
proxier.ipv6Proxier.OnServiceSynced()
|
||||||
|
}
|
||||||
|
|
||||||
|
// OnEndpointsAdd is called whenever creation of new endpoints object
|
||||||
|
// is observed.
|
||||||
|
func (proxier *metaProxier) OnEndpointsAdd(endpoints *v1.Endpoints) {
|
||||||
|
ipFamily, err := endpointsIPFamily(endpoints)
|
||||||
|
if err != nil {
|
||||||
|
klog.Warningf("failed to add endpoints %s/%s with error %v", endpoints.ObjectMeta.Namespace, endpoints.ObjectMeta.Name, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if *ipFamily == v1.IPv4Protocol {
|
||||||
|
proxier.ipv4Proxier.OnEndpointsAdd(endpoints)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
proxier.ipv6Proxier.OnEndpointsAdd(endpoints)
|
||||||
|
}
|
||||||
|
|
||||||
|
// OnEndpointsUpdate is called whenever modification of an existing
|
||||||
|
// endpoints object is observed.
|
||||||
|
func (proxier *metaProxier) OnEndpointsUpdate(oldEndpoints, endpoints *v1.Endpoints) {
|
||||||
|
ipFamily, err := endpointsIPFamily(endpoints)
|
||||||
|
if err != nil {
|
||||||
|
klog.Warningf("failed to update endpoints %s/%s with error %v", endpoints.ObjectMeta.Namespace, endpoints.ObjectMeta.Name, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if *ipFamily == v1.IPv4Protocol {
|
||||||
|
proxier.ipv4Proxier.OnEndpointsUpdate(oldEndpoints, endpoints)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
proxier.ipv6Proxier.OnEndpointsUpdate(oldEndpoints, endpoints)
|
||||||
|
}
|
||||||
|
|
||||||
|
// OnEndpointsDelete is called whenever deletion of an existing
|
||||||
|
// endpoints object is observed.
|
||||||
|
func (proxier *metaProxier) OnEndpointsDelete(endpoints *v1.Endpoints) {
|
||||||
|
ipFamily, err := endpointsIPFamily(endpoints)
|
||||||
|
if err != nil {
|
||||||
|
klog.Warningf("failed to delete endpoints %s/%s with error %v", endpoints.ObjectMeta.Namespace, endpoints.ObjectMeta.Name, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if *ipFamily == v1.IPv4Protocol {
|
||||||
|
proxier.ipv4Proxier.OnEndpointsDelete(endpoints)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
proxier.ipv6Proxier.OnEndpointsDelete(endpoints)
|
||||||
|
}
|
||||||
|
|
||||||
|
// OnEndpointsSynced is called once all the initial event handlers
|
||||||
|
// were called and the state is fully propagated to local cache.
|
||||||
|
func (proxier *metaProxier) OnEndpointsSynced() {
|
||||||
|
proxier.ipv4Proxier.OnEndpointsSynced()
|
||||||
|
proxier.ipv6Proxier.OnEndpointsSynced()
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: (khenidak) implement EndpointSlice handling
|
||||||
|
|
||||||
|
// OnEndpointSliceAdd is called whenever creation of a new endpoint slice object
|
||||||
|
// is observed.
|
||||||
|
func (proxier *metaProxier) OnEndpointSliceAdd(endpointSlice *discovery.EndpointSlice) {
|
||||||
|
// noop
|
||||||
|
}
|
||||||
|
|
||||||
|
// OnEndpointSliceUpdate is called whenever modification of an existing endpoint
|
||||||
|
// slice object is observed.
|
||||||
|
func (proxier *metaProxier) OnEndpointSliceUpdate(_, endpointSlice *discovery.EndpointSlice) {
|
||||||
|
//noop
|
||||||
|
}
|
||||||
|
|
||||||
|
// OnEndpointSliceDelete is called whenever deletion of an existing endpoint slice
|
||||||
|
// object is observed.
|
||||||
|
func (proxier *metaProxier) OnEndpointSliceDelete(endpointSlice *discovery.EndpointSlice) {
|
||||||
|
//noop
|
||||||
|
}
|
||||||
|
|
||||||
|
// OnEndpointSlicesSynced is called once all the initial event handlers were
|
||||||
|
// called and the state is fully propagated to local cache.
|
||||||
|
func (proxier *metaProxier) OnEndpointSlicesSynced() {
|
||||||
|
//noop
|
||||||
|
}
|
||||||
|
|
||||||
|
// endpointsIPFamily that returns IPFamily of endpoints or error if
|
||||||
|
// failed to identify the IP family.
|
||||||
|
func endpointsIPFamily(endpoints *v1.Endpoints) (*v1.IPFamily, error) {
|
||||||
|
if len(endpoints.Subsets) == 0 {
|
||||||
|
return nil, fmt.Errorf("failed to identify ipfamily for endpoints (no subsets)")
|
||||||
|
}
|
||||||
|
|
||||||
|
// we only need to work with subset [0],endpoint controller
|
||||||
|
// ensures that endpoints selected are of the same family.
|
||||||
|
subset := endpoints.Subsets[0]
|
||||||
|
if len(subset.Addresses) == 0 {
|
||||||
|
return nil, fmt.Errorf("failed to identify ipfamily for endpoints (no addresses)")
|
||||||
|
}
|
||||||
|
// same apply on addresses
|
||||||
|
address := subset.Addresses[0]
|
||||||
|
if len(address.IP) == 0 {
|
||||||
|
return nil, fmt.Errorf("failed to identify ipfamily for endpoints (address has no ip)")
|
||||||
|
}
|
||||||
|
|
||||||
|
ipv4 := v1.IPv4Protocol
|
||||||
|
ipv6 := v1.IPv6Protocol
|
||||||
|
if utilnet.IsIPv6String(address.IP) {
|
||||||
|
return &ipv6, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return &ipv4, nil
|
||||||
|
}
|
@ -471,6 +471,62 @@ func NewProxier(ipt utiliptables.Interface,
|
|||||||
return proxier, nil
|
return proxier, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewDualStackProxier returns a new Proxier for dual-stack operation
|
||||||
|
func NewDualStackProxier(
|
||||||
|
ipt [2]utiliptables.Interface,
|
||||||
|
ipvs utilipvs.Interface,
|
||||||
|
ipset utilipset.Interface,
|
||||||
|
sysctl utilsysctl.Interface,
|
||||||
|
exec utilexec.Interface,
|
||||||
|
syncPeriod time.Duration,
|
||||||
|
minSyncPeriod time.Duration,
|
||||||
|
excludeCIDRs []string,
|
||||||
|
strictARP bool,
|
||||||
|
masqueradeAll bool,
|
||||||
|
masqueradeBit int,
|
||||||
|
clusterCIDR [2]string,
|
||||||
|
hostname string,
|
||||||
|
nodeIP [2]net.IP,
|
||||||
|
recorder record.EventRecorder,
|
||||||
|
healthzServer healthcheck.HealthzUpdater,
|
||||||
|
scheduler string,
|
||||||
|
nodePortAddresses []string,
|
||||||
|
) (proxy.Provider, error) {
|
||||||
|
|
||||||
|
safeIpset := newSafeIpset(ipset)
|
||||||
|
|
||||||
|
// Create an ipv4 instance of the single-stack proxier
|
||||||
|
ipv4Proxier, err := NewProxier(ipt[0], ipvs, safeIpset, sysctl,
|
||||||
|
exec, syncPeriod, minSyncPeriod, filterCIDRs(false, excludeCIDRs), strictARP,
|
||||||
|
masqueradeAll, masqueradeBit, clusterCIDR[0], hostname, nodeIP[0],
|
||||||
|
recorder, healthzServer, scheduler, nodePortAddresses)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("unable to create ipv4 proxier: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
ipv6Proxier, err := NewProxier(ipt[1], ipvs, safeIpset, sysctl,
|
||||||
|
exec, syncPeriod, minSyncPeriod, filterCIDRs(true, excludeCIDRs), strictARP,
|
||||||
|
masqueradeAll, masqueradeBit, clusterCIDR[1], hostname, nodeIP[1],
|
||||||
|
nil, nil, scheduler, nodePortAddresses)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("unable to create ipv6 proxier: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return a meta-proxier that dispatch calls between the two
|
||||||
|
// single-stack proxier instances
|
||||||
|
return NewMetaProxier(ipv4Proxier, ipv6Proxier), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func filterCIDRs(wantIPv6 bool, cidrs []string) []string {
|
||||||
|
var filteredCIDRs []string
|
||||||
|
for _, cidr := range cidrs {
|
||||||
|
if utilnet.IsIPv6CIDRString(cidr) == wantIPv6 {
|
||||||
|
filteredCIDRs = append(filteredCIDRs, cidr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return filteredCIDRs
|
||||||
|
}
|
||||||
|
|
||||||
// internal struct for string service information
|
// internal struct for string service information
|
||||||
type serviceInfo struct {
|
type serviceInfo struct {
|
||||||
*proxy.BaseServiceInfo
|
*proxy.BaseServiceInfo
|
||||||
@ -1478,7 +1534,7 @@ func (proxier *Proxier) writeIptablesRules() {
|
|||||||
}
|
}
|
||||||
args = append(args,
|
args = append(args,
|
||||||
"-m", "comment", "--comment", proxier.ipsetList[set.name].getComment(),
|
"-m", "comment", "--comment", proxier.ipsetList[set.name].getComment(),
|
||||||
"-m", "set", "--match-set", set.name,
|
"-m", "set", "--match-set", proxier.ipsetList[set.name].Name,
|
||||||
set.matchType,
|
set.matchType,
|
||||||
)
|
)
|
||||||
writeLine(proxier.natRules, append(args, "-j", set.to)...)
|
writeLine(proxier.natRules, append(args, "-j", set.to)...)
|
||||||
@ -1489,7 +1545,7 @@ func (proxier *Proxier) writeIptablesRules() {
|
|||||||
args = append(args[:0],
|
args = append(args[:0],
|
||||||
"-A", string(kubeServicesChain),
|
"-A", string(kubeServicesChain),
|
||||||
"-m", "comment", "--comment", proxier.ipsetList[kubeClusterIPSet].getComment(),
|
"-m", "comment", "--comment", proxier.ipsetList[kubeClusterIPSet].getComment(),
|
||||||
"-m", "set", "--match-set", kubeClusterIPSet,
|
"-m", "set", "--match-set", proxier.ipsetList[kubeClusterIPSet].Name,
|
||||||
)
|
)
|
||||||
if proxier.masqueradeAll {
|
if proxier.masqueradeAll {
|
||||||
writeLine(proxier.natRules, append(args, "dst,dst", "-j", string(KubeMarkMasqChain))...)
|
writeLine(proxier.natRules, append(args, "dst,dst", "-j", string(KubeMarkMasqChain))...)
|
||||||
@ -1517,7 +1573,7 @@ func (proxier *Proxier) writeIptablesRules() {
|
|||||||
args = append(args[:0],
|
args = append(args[:0],
|
||||||
"-A", string(kubeServicesChain),
|
"-A", string(kubeServicesChain),
|
||||||
"-m", "comment", "--comment", proxier.ipsetList[kubeExternalIPSet].getComment(),
|
"-m", "comment", "--comment", proxier.ipsetList[kubeExternalIPSet].getComment(),
|
||||||
"-m", "set", "--match-set", kubeExternalIPSet,
|
"-m", "set", "--match-set", proxier.ipsetList[kubeExternalIPSet].Name,
|
||||||
"dst,dst",
|
"dst,dst",
|
||||||
)
|
)
|
||||||
writeLine(proxier.natRules, append(args, "-j", string(KubeMarkMasqChain))...)
|
writeLine(proxier.natRules, append(args, "-j", string(KubeMarkMasqChain))...)
|
||||||
@ -1611,7 +1667,7 @@ func (proxier *Proxier) acceptIPVSTraffic() {
|
|||||||
}
|
}
|
||||||
writeLine(proxier.natRules, []string{
|
writeLine(proxier.natRules, []string{
|
||||||
"-A", string(kubeServicesChain),
|
"-A", string(kubeServicesChain),
|
||||||
"-m", "set", "--match-set", set, matchType,
|
"-m", "set", "--match-set", proxier.ipsetList[set].Name, matchType,
|
||||||
"-j", "ACCEPT",
|
"-j", "ACCEPT",
|
||||||
}...)
|
}...)
|
||||||
}
|
}
|
||||||
@ -1845,11 +1901,16 @@ func (proxier *Proxier) syncEndpoint(svcPortName proxy.ServicePortName, onlyNode
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (proxier *Proxier) cleanLegacyService(activeServices map[string]bool, currentServices map[string]*utilipvs.VirtualServer, legacyBindAddrs map[string]bool) {
|
func (proxier *Proxier) cleanLegacyService(activeServices map[string]bool, currentServices map[string]*utilipvs.VirtualServer, legacyBindAddrs map[string]bool) {
|
||||||
|
isIPv6 := utilnet.IsIPv6(proxier.nodeIP)
|
||||||
for cs := range currentServices {
|
for cs := range currentServices {
|
||||||
svc := currentServices[cs]
|
svc := currentServices[cs]
|
||||||
if proxier.isIPInExcludeCIDRs(svc.Address) {
|
if proxier.isIPInExcludeCIDRs(svc.Address) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
if utilnet.IsIPv6(svc.Address) != isIPv6 {
|
||||||
|
// Not our family
|
||||||
|
continue
|
||||||
|
}
|
||||||
if _, ok := activeServices[cs]; !ok {
|
if _, ok := activeServices[cs]; !ok {
|
||||||
klog.V(4).Infof("Delete service %s", svc.String())
|
klog.V(4).Infof("Delete service %s", svc.String())
|
||||||
if err := proxier.ipvs.DeleteVirtualServer(svc); err != nil {
|
if err := proxier.ipvs.DeleteVirtualServer(svc); err != nil {
|
||||||
|
@ -3716,3 +3716,32 @@ func TestEndpointSliceE2E(t *testing.T) {
|
|||||||
assert.Nil(t, rsErr2, "Expected no error getting real servers")
|
assert.Nil(t, rsErr2, "Expected no error getting real servers")
|
||||||
assert.Len(t, realServers2, 0, "Expected 0 real servers")
|
assert.Len(t, realServers2, 0, "Expected 0 real servers")
|
||||||
}
|
}
|
||||||
|
func TestFilterCIDRs(t *testing.T) {
|
||||||
|
var cidrList []string
|
||||||
|
var cidrs []string
|
||||||
|
var expected []string
|
||||||
|
cidrs = filterCIDRs(true, []string{})
|
||||||
|
if len(cidrs) > 0 {
|
||||||
|
t.Errorf("An empty list produces a non-empty return %v", cidrs)
|
||||||
|
}
|
||||||
|
|
||||||
|
cidrList = []string{"1000::/64", "10.0.0.0/16", "11.0.0.0/16", "2000::/64"}
|
||||||
|
expected = []string{"1000::/64", "2000::/64"}
|
||||||
|
cidrs = filterCIDRs(true, cidrList)
|
||||||
|
if !reflect.DeepEqual(cidrs, expected) {
|
||||||
|
t.Errorf("cidrs %v is not expected %v", cidrs, expected)
|
||||||
|
}
|
||||||
|
|
||||||
|
expected = []string{"10.0.0.0/16", "11.0.0.0/16"}
|
||||||
|
cidrs = filterCIDRs(false, cidrList)
|
||||||
|
if !reflect.DeepEqual(cidrs, expected) {
|
||||||
|
t.Errorf("cidrs %v is not expected %v", cidrs, expected)
|
||||||
|
}
|
||||||
|
|
||||||
|
cidrList = []string{"1000::/64", "2000::/64"}
|
||||||
|
expected = []string{}
|
||||||
|
cidrs = filterCIDRs(false, cidrList)
|
||||||
|
if len(cidrs) > 0 {
|
||||||
|
t.Errorf("cidrs %v is not expected %v", cidrs, expected)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
104
pkg/proxy/ipvs/safe_ipset.go
Normal file
104
pkg/proxy/ipvs/safe_ipset.go
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2019 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 (
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"k8s.io/kubernetes/pkg/util/ipset"
|
||||||
|
)
|
||||||
|
|
||||||
|
type safeIpset struct {
|
||||||
|
ipset ipset.Interface
|
||||||
|
mu sync.Mutex
|
||||||
|
}
|
||||||
|
|
||||||
|
func newSafeIpset(ipset ipset.Interface) ipset.Interface {
|
||||||
|
return &safeIpset{
|
||||||
|
ipset: ipset,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// FlushSet deletes all entries from a named set.
|
||||||
|
func (s *safeIpset) FlushSet(set string) error {
|
||||||
|
s.mu.Lock()
|
||||||
|
defer s.mu.Unlock()
|
||||||
|
return s.ipset.FlushSet(set)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DestroySet deletes a named set.
|
||||||
|
func (s *safeIpset) DestroySet(set string) error {
|
||||||
|
s.mu.Lock()
|
||||||
|
defer s.mu.Unlock()
|
||||||
|
return s.ipset.DestroySet(set)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DestroyAllSets deletes all sets.
|
||||||
|
func (s *safeIpset) DestroyAllSets() error {
|
||||||
|
s.mu.Lock()
|
||||||
|
defer s.mu.Unlock()
|
||||||
|
return s.ipset.DestroyAllSets()
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateSet creates a new set. It will ignore error when the set already exists if ignoreExistErr=true.
|
||||||
|
func (s *safeIpset) CreateSet(set *ipset.IPSet, ignoreExistErr bool) error {
|
||||||
|
s.mu.Lock()
|
||||||
|
defer s.mu.Unlock()
|
||||||
|
return s.ipset.CreateSet(set, ignoreExistErr)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddEntry adds a new entry to the named set. It will ignore error when the entry already exists if ignoreExistErr=true.
|
||||||
|
func (s *safeIpset) AddEntry(entry string, set *ipset.IPSet, ignoreExistErr bool) error {
|
||||||
|
s.mu.Lock()
|
||||||
|
defer s.mu.Unlock()
|
||||||
|
return s.ipset.AddEntry(entry, set, ignoreExistErr)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DelEntry deletes one entry from the named set
|
||||||
|
func (s *safeIpset) DelEntry(entry string, set string) error {
|
||||||
|
s.mu.Lock()
|
||||||
|
defer s.mu.Unlock()
|
||||||
|
return s.ipset.DelEntry(entry, set)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test test if an entry exists in the named set
|
||||||
|
func (s *safeIpset) TestEntry(entry string, set string) (bool, error) {
|
||||||
|
s.mu.Lock()
|
||||||
|
defer s.mu.Unlock()
|
||||||
|
return s.ipset.TestEntry(entry, set)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListEntries lists all the entries from a named set
|
||||||
|
func (s *safeIpset) ListEntries(set string) ([]string, error) {
|
||||||
|
s.mu.Lock()
|
||||||
|
defer s.mu.Unlock()
|
||||||
|
return s.ipset.ListEntries(set)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListSets list all set names from kernel
|
||||||
|
func (s *safeIpset) ListSets() ([]string, error) {
|
||||||
|
s.mu.Lock()
|
||||||
|
defer s.mu.Unlock()
|
||||||
|
return s.ipset.ListSets()
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetVersion returns the "X.Y" version string for ipset.
|
||||||
|
func (s *safeIpset) GetVersion() (string, error) {
|
||||||
|
s.mu.Lock()
|
||||||
|
defer s.mu.Unlock()
|
||||||
|
return s.ipset.GetVersion()
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user