mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-25 20:53:33 +00:00
Issue #70020; Flush Conntrack entities for SCTP
Signed-off-by: Lars Ekman <lars.g.ekman@est.tech>
This commit is contained in:
parent
cb38560422
commit
aa8521df66
@ -722,32 +722,33 @@ func servicePortEndpointChainName(servicePortName string, protocol string, endpo
|
|||||||
return utiliptables.Chain("KUBE-SEP-" + encoded[:16])
|
return utiliptables.Chain("KUBE-SEP-" + encoded[:16])
|
||||||
}
|
}
|
||||||
|
|
||||||
// After a UDP endpoint has been removed, we must flush any pending conntrack entries to it, or else we
|
// After a UDP or SCTP endpoint has been removed, we must flush any pending conntrack entries to it, or else we
|
||||||
// risk sending more traffic to it, all of which will be lost (because UDP).
|
// risk sending more traffic to it, all of which will be lost.
|
||||||
// This assumes the proxier mutex is held
|
// This assumes the proxier mutex is held
|
||||||
// TODO: move it to util
|
// TODO: move it to util
|
||||||
func (proxier *Proxier) deleteEndpointConnections(connectionMap []proxy.ServiceEndpoint) {
|
func (proxier *Proxier) deleteEndpointConnections(connectionMap []proxy.ServiceEndpoint) {
|
||||||
for _, epSvcPair := range connectionMap {
|
for _, epSvcPair := range connectionMap {
|
||||||
if svcInfo, ok := proxier.serviceMap[epSvcPair.ServicePortName]; ok && svcInfo.Protocol() == v1.ProtocolUDP {
|
if svcInfo, ok := proxier.serviceMap[epSvcPair.ServicePortName]; ok && conntrack.IsClearConntrackNeeded(svcInfo.Protocol()) {
|
||||||
endpointIP := utilproxy.IPPart(epSvcPair.Endpoint)
|
endpointIP := utilproxy.IPPart(epSvcPair.Endpoint)
|
||||||
nodePort := svcInfo.NodePort()
|
nodePort := svcInfo.NodePort()
|
||||||
|
svcProto := svcInfo.Protocol()
|
||||||
var err error
|
var err error
|
||||||
if nodePort != 0 {
|
if nodePort != 0 {
|
||||||
err = conntrack.ClearEntriesForPortNAT(proxier.exec, endpointIP, nodePort, v1.ProtocolUDP)
|
err = conntrack.ClearEntriesForPortNAT(proxier.exec, endpointIP, nodePort, svcProto)
|
||||||
} else {
|
} else {
|
||||||
err = conntrack.ClearEntriesForNAT(proxier.exec, svcInfo.ClusterIP().String(), endpointIP, v1.ProtocolUDP)
|
err = conntrack.ClearEntriesForNAT(proxier.exec, svcInfo.ClusterIP().String(), endpointIP, svcProto)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
klog.Errorf("Failed to delete %s endpoint connections, error: %v", epSvcPair.ServicePortName.String(), err)
|
klog.Errorf("Failed to delete %s endpoint connections, error: %v", epSvcPair.ServicePortName.String(), err)
|
||||||
}
|
}
|
||||||
for _, extIP := range svcInfo.ExternalIPStrings() {
|
for _, extIP := range svcInfo.ExternalIPStrings() {
|
||||||
err := conntrack.ClearEntriesForNAT(proxier.exec, extIP, endpointIP, v1.ProtocolUDP)
|
err := conntrack.ClearEntriesForNAT(proxier.exec, extIP, endpointIP, svcProto)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
klog.Errorf("Failed to delete %s endpoint connections for externalIP %s, error: %v", epSvcPair.ServicePortName.String(), extIP, err)
|
klog.Errorf("Failed to delete %s endpoint connections for externalIP %s, error: %v", epSvcPair.ServicePortName.String(), extIP, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for _, lbIP := range svcInfo.LoadBalancerIPStrings() {
|
for _, lbIP := range svcInfo.LoadBalancerIPStrings() {
|
||||||
err := conntrack.ClearEntriesForNAT(proxier.exec, lbIP, endpointIP, v1.ProtocolUDP)
|
err := conntrack.ClearEntriesForNAT(proxier.exec, lbIP, endpointIP, svcProto)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
klog.Errorf("Failed to delete %s endpoint connections for LoabBalancerIP %s, error: %v", epSvcPair.ServicePortName.String(), lbIP, err)
|
klog.Errorf("Failed to delete %s endpoint connections for LoabBalancerIP %s, error: %v", epSvcPair.ServicePortName.String(), lbIP, err)
|
||||||
}
|
}
|
||||||
@ -808,8 +809,8 @@ func (proxier *Proxier) syncProxyRules() {
|
|||||||
staleServices := serviceUpdateResult.UDPStaleClusterIP
|
staleServices := serviceUpdateResult.UDPStaleClusterIP
|
||||||
// merge stale services gathered from updateEndpointsMap
|
// merge stale services gathered from updateEndpointsMap
|
||||||
for _, svcPortName := range endpointUpdateResult.StaleServiceNames {
|
for _, svcPortName := range endpointUpdateResult.StaleServiceNames {
|
||||||
if svcInfo, ok := proxier.serviceMap[svcPortName]; ok && svcInfo != nil && svcInfo.Protocol() == v1.ProtocolUDP {
|
if svcInfo, ok := proxier.serviceMap[svcPortName]; ok && svcInfo != nil && conntrack.IsClearConntrackNeeded(svcInfo.Protocol()) {
|
||||||
klog.V(2).Infof("Stale udp service %v -> %s", svcPortName, svcInfo.ClusterIP().String())
|
klog.V(2).Infof("Stale %s service %v -> %s", strings.ToLower(string(svcInfo.Protocol())), svcPortName, svcInfo.ClusterIP().String())
|
||||||
staleServices.Insert(svcInfo.ClusterIP().String())
|
staleServices.Insert(svcInfo.ClusterIP().String())
|
||||||
for _, extIP := range svcInfo.ExternalIPStrings() {
|
for _, extIP := range svcInfo.ExternalIPStrings() {
|
||||||
staleServices.Insert(extIP)
|
staleServices.Insert(extIP)
|
||||||
|
@ -230,7 +230,7 @@ func TestDeleteEndpointConnections(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Create a fake executor for the conntrack utility. This should only be
|
// Create a fake executor for the conntrack utility. This should only be
|
||||||
// invoked for UDP connections, since no conntrack cleanup is needed for TCP
|
// invoked for UDP and SCTP connections, since no conntrack cleanup is needed for TCP
|
||||||
fcmd := fakeexec.FakeCmd{}
|
fcmd := fakeexec.FakeCmd{}
|
||||||
fexec := fakeexec.FakeExec{
|
fexec := fakeexec.FakeExec{
|
||||||
LookPathFunc: func(cmd string) (string, error) { return cmd, nil },
|
LookPathFunc: func(cmd string) (string, error) { return cmd, nil },
|
||||||
@ -239,7 +239,7 @@ func TestDeleteEndpointConnections(t *testing.T) {
|
|||||||
return fakeexec.InitFakeCmd(&fcmd, cmd, args...)
|
return fakeexec.InitFakeCmd(&fcmd, cmd, args...)
|
||||||
}
|
}
|
||||||
for _, tc := range testCases {
|
for _, tc := range testCases {
|
||||||
if tc.protocol == UDP {
|
if conntrack.IsClearConntrackNeeded(tc.protocol) {
|
||||||
var cmdOutput string
|
var cmdOutput string
|
||||||
var simErr error
|
var simErr error
|
||||||
if tc.simulatedErr == "" {
|
if tc.simulatedErr == "" {
|
||||||
@ -292,15 +292,15 @@ func TestDeleteEndpointConnections(t *testing.T) {
|
|||||||
|
|
||||||
fp.deleteEndpointConnections(input)
|
fp.deleteEndpointConnections(input)
|
||||||
|
|
||||||
// For UDP connections, check the executed conntrack command
|
// For UDP and SCTP connections, check the executed conntrack command
|
||||||
var expExecs int
|
var expExecs int
|
||||||
if tc.protocol == UDP {
|
if conntrack.IsClearConntrackNeeded(tc.protocol) {
|
||||||
isIPv6 := func(ip string) bool {
|
isIPv6 := func(ip string) bool {
|
||||||
netIP := net.ParseIP(ip)
|
netIP := net.ParseIP(ip)
|
||||||
return netIP.To4() == nil
|
return netIP.To4() == nil
|
||||||
}
|
}
|
||||||
endpointIP := utilproxy.IPPart(tc.endpoint)
|
endpointIP := utilproxy.IPPart(tc.endpoint)
|
||||||
expectCommand := fmt.Sprintf("conntrack -D --orig-dst %s --dst-nat %s -p udp", tc.svcIP, endpointIP)
|
expectCommand := fmt.Sprintf("conntrack -D --orig-dst %s --dst-nat %s -p %s", tc.svcIP, endpointIP, strings.ToLower(string((tc.protocol))))
|
||||||
if isIPv6(endpointIP) {
|
if isIPv6(endpointIP) {
|
||||||
expectCommand += " -f ipv6"
|
expectCommand += " -f ipv6"
|
||||||
}
|
}
|
||||||
|
@ -18,7 +18,6 @@ package ipvs
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@ -164,10 +163,10 @@ func (m *GracefulTerminationManager) deleteRsFunc(rsToDelete *listItem) (bool, e
|
|||||||
}
|
}
|
||||||
for _, rs := range rss {
|
for _, rs := range rss {
|
||||||
if rsToDelete.RealServer.Equal(rs) {
|
if rsToDelete.RealServer.Equal(rs) {
|
||||||
// For UDP traffic, no graceful termination, we immediately delete the RS
|
// For UDP and SCTP traffic, no graceful termination, we immediately delete the RS
|
||||||
// (existing connections will be deleted on the next packet because sysctlExpireNoDestConn=1)
|
// (existing connections will be deleted on the next packet because sysctlExpireNoDestConn=1)
|
||||||
// For other protocols, don't delete until all connections have expired)
|
// For other protocols, don't delete until all connections have expired)
|
||||||
if strings.ToUpper(rsToDelete.VirtualServer.Protocol) != "UDP" && rs.ActiveConn+rs.InactiveConn != 0 {
|
if utilipvs.IsRsGracefulTerminationNeeded(rsToDelete.VirtualServer.Protocol) && rs.ActiveConn+rs.InactiveConn != 0 {
|
||||||
klog.V(5).Infof("Not deleting, RS %v: %v ActiveConn, %v InactiveConn", rsToDelete.String(), rs.ActiveConn, rs.InactiveConn)
|
klog.V(5).Infof("Not deleting, RS %v: %v ActiveConn, %v InactiveConn", rsToDelete.String(), rs.ActiveConn, rs.InactiveConn)
|
||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
|
@ -1023,8 +1023,8 @@ func (proxier *Proxier) syncProxyRules() {
|
|||||||
staleServices := serviceUpdateResult.UDPStaleClusterIP
|
staleServices := serviceUpdateResult.UDPStaleClusterIP
|
||||||
// merge stale services gathered from updateEndpointsMap
|
// merge stale services gathered from updateEndpointsMap
|
||||||
for _, svcPortName := range endpointUpdateResult.StaleServiceNames {
|
for _, svcPortName := range endpointUpdateResult.StaleServiceNames {
|
||||||
if svcInfo, ok := proxier.serviceMap[svcPortName]; ok && svcInfo != nil && svcInfo.Protocol() == v1.ProtocolUDP {
|
if svcInfo, ok := proxier.serviceMap[svcPortName]; ok && svcInfo != nil && conntrack.IsClearConntrackNeeded(svcInfo.Protocol()) {
|
||||||
klog.V(2).Infof("Stale udp service %v -> %s", svcPortName, svcInfo.ClusterIP().String())
|
klog.V(2).Infof("Stale %s service %v -> %s", strings.ToLower(string(svcInfo.Protocol())), svcPortName, svcInfo.ClusterIP().String())
|
||||||
staleServices.Insert(svcInfo.ClusterIP().String())
|
staleServices.Insert(svcInfo.ClusterIP().String())
|
||||||
for _, extIP := range svcInfo.ExternalIPStrings() {
|
for _, extIP := range svcInfo.ExternalIPStrings() {
|
||||||
staleServices.Insert(extIP)
|
staleServices.Insert(extIP)
|
||||||
@ -1842,25 +1842,26 @@ func (proxier *Proxier) getExistingChains(buffer *bytes.Buffer, table utiliptabl
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// After a UDP endpoint has been removed, we must flush any pending conntrack entries to it, or else we
|
// After a UDP or SCTP endpoint has been removed, we must flush any pending conntrack entries to it, or else we
|
||||||
// risk sending more traffic to it, all of which will be lost (because UDP).
|
// risk sending more traffic to it, all of which will be lost (because UDP).
|
||||||
// This assumes the proxier mutex is held
|
// This assumes the proxier mutex is held
|
||||||
func (proxier *Proxier) deleteEndpointConnections(connectionMap []proxy.ServiceEndpoint) {
|
func (proxier *Proxier) deleteEndpointConnections(connectionMap []proxy.ServiceEndpoint) {
|
||||||
for _, epSvcPair := range connectionMap {
|
for _, epSvcPair := range connectionMap {
|
||||||
if svcInfo, ok := proxier.serviceMap[epSvcPair.ServicePortName]; ok && svcInfo.Protocol() == v1.ProtocolUDP {
|
if svcInfo, ok := proxier.serviceMap[epSvcPair.ServicePortName]; ok && conntrack.IsClearConntrackNeeded(svcInfo.Protocol()) {
|
||||||
endpointIP := utilproxy.IPPart(epSvcPair.Endpoint)
|
endpointIP := utilproxy.IPPart(epSvcPair.Endpoint)
|
||||||
err := conntrack.ClearEntriesForNAT(proxier.exec, svcInfo.ClusterIP().String(), endpointIP, v1.ProtocolUDP)
|
svcProto := svcInfo.Protocol()
|
||||||
|
err := conntrack.ClearEntriesForNAT(proxier.exec, svcInfo.ClusterIP().String(), endpointIP, svcProto)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
klog.Errorf("Failed to delete %s endpoint connections, error: %v", epSvcPair.ServicePortName.String(), err)
|
klog.Errorf("Failed to delete %s endpoint connections, error: %v", epSvcPair.ServicePortName.String(), err)
|
||||||
}
|
}
|
||||||
for _, extIP := range svcInfo.ExternalIPStrings() {
|
for _, extIP := range svcInfo.ExternalIPStrings() {
|
||||||
err := conntrack.ClearEntriesForNAT(proxier.exec, extIP, endpointIP, v1.ProtocolUDP)
|
err := conntrack.ClearEntriesForNAT(proxier.exec, extIP, endpointIP, svcProto)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
klog.Errorf("Failed to delete %s endpoint connections for externalIP %s, error: %v", epSvcPair.ServicePortName.String(), extIP, err)
|
klog.Errorf("Failed to delete %s endpoint connections for externalIP %s, error: %v", epSvcPair.ServicePortName.String(), extIP, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for _, lbIP := range svcInfo.LoadBalancerIPStrings() {
|
for _, lbIP := range svcInfo.LoadBalancerIPStrings() {
|
||||||
err := conntrack.ClearEntriesForNAT(proxier.exec, lbIP, endpointIP, v1.ProtocolUDP)
|
err := conntrack.ClearEntriesForNAT(proxier.exec, lbIP, endpointIP, svcProto)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
klog.Errorf("Failed to delete %s endpoint connections for LoadBalancerIP %s, error: %v", epSvcPair.ServicePortName.String(), lbIP, err)
|
klog.Errorf("Failed to delete %s endpoint connections for LoadBalancerIP %s, error: %v", epSvcPair.ServicePortName.String(), lbIP, err)
|
||||||
}
|
}
|
||||||
|
@ -21,7 +21,7 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"k8s.io/api/core/v1"
|
v1 "k8s.io/api/core/v1"
|
||||||
"k8s.io/utils/exec"
|
"k8s.io/utils/exec"
|
||||||
utilnet "k8s.io/utils/net"
|
utilnet "k8s.io/utils/net"
|
||||||
)
|
)
|
||||||
@ -103,7 +103,7 @@ func ClearEntriesForNAT(execer exec.Interface, origin, dest string, protocol v1.
|
|||||||
// TODO: Better handling for deletion failure. When failure occur, stale udp connection may not get flushed.
|
// TODO: Better handling for deletion failure. When failure occur, stale udp connection may not get flushed.
|
||||||
// These stale udp connection will keep black hole traffic. Making this a best effort operation for now, since it
|
// These stale udp connection will keep black hole traffic. Making this a best effort operation for now, since it
|
||||||
// is expensive to baby sit all udp connections to kubernetes services.
|
// is expensive to baby sit all udp connections to kubernetes services.
|
||||||
return fmt.Errorf("error deleting conntrack entries for UDP peer {%s, %s}, error: %v", origin, dest, err)
|
return fmt.Errorf("error deleting conntrack entries for %s peer {%s, %s}, error: %v", protoStr(protocol), origin, dest, err)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -119,7 +119,12 @@ func ClearEntriesForPortNAT(execer exec.Interface, dest string, port int, protoc
|
|||||||
parameters := parametersWithFamily(utilnet.IsIPv6String(dest), "-D", "-p", protoStr(protocol), "--dport", strconv.Itoa(port), "--dst-nat", dest)
|
parameters := parametersWithFamily(utilnet.IsIPv6String(dest), "-D", "-p", protoStr(protocol), "--dport", strconv.Itoa(port), "--dst-nat", dest)
|
||||||
err := Exec(execer, parameters...)
|
err := Exec(execer, parameters...)
|
||||||
if err != nil && !strings.Contains(err.Error(), NoConnectionToDelete) {
|
if err != nil && !strings.Contains(err.Error(), NoConnectionToDelete) {
|
||||||
return fmt.Errorf("error deleting conntrack entries for UDP port: %d, error: %v", port, err)
|
return fmt.Errorf("error deleting conntrack entries for %s port: %d, error: %v", protoStr(protocol), port, err)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsClearConntrackNeeded returns true if protocol requires conntrack cleanup for the stale connections
|
||||||
|
func IsClearConntrackNeeded(proto v1.Protocol) bool {
|
||||||
|
return proto == v1.ProtocolUDP || proto == v1.ProtocolSCTP
|
||||||
|
}
|
||||||
|
@ -21,7 +21,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"k8s.io/api/core/v1"
|
v1 "k8s.io/api/core/v1"
|
||||||
"k8s.io/utils/exec"
|
"k8s.io/utils/exec"
|
||||||
fakeexec "k8s.io/utils/exec/testing"
|
fakeexec "k8s.io/utils/exec/testing"
|
||||||
utilnet "k8s.io/utils/net"
|
utilnet "k8s.io/utils/net"
|
||||||
@ -177,7 +177,7 @@ func TestClearUDPConntrackForPort(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDeleteUDPConnections(t *testing.T) {
|
func TestDeleteConnections(t *testing.T) {
|
||||||
fcmd := fakeexec.FakeCmd{
|
fcmd := fakeexec.FakeCmd{
|
||||||
CombinedOutputScript: []fakeexec.FakeAction{
|
CombinedOutputScript: []fakeexec.FakeAction{
|
||||||
func() ([]byte, []byte, error) { return []byte("1 flow entries have been deleted"), nil, nil },
|
func() ([]byte, []byte, error) { return []byte("1 flow entries have been deleted"), nil, nil },
|
||||||
@ -185,6 +185,11 @@ func TestDeleteUDPConnections(t *testing.T) {
|
|||||||
return []byte(""), nil, fmt.Errorf("conntrack v1.4.2 (conntrack-tools): 0 flow entries have been deleted")
|
return []byte(""), nil, fmt.Errorf("conntrack v1.4.2 (conntrack-tools): 0 flow entries have been deleted")
|
||||||
},
|
},
|
||||||
func() ([]byte, []byte, error) { return []byte("1 flow entries have been deleted"), nil, nil },
|
func() ([]byte, []byte, error) { return []byte("1 flow entries have been deleted"), nil, nil },
|
||||||
|
func() ([]byte, []byte, error) { return []byte("1 flow entries have been deleted"), nil, nil },
|
||||||
|
func() ([]byte, []byte, error) {
|
||||||
|
return []byte(""), nil, fmt.Errorf("conntrack v1.4.2 (conntrack-tools): 0 flow entries have been deleted")
|
||||||
|
},
|
||||||
|
func() ([]byte, []byte, error) { return []byte("1 flow entries have been deleted"), nil, nil },
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
fexec := fakeexec.FakeExec{
|
fexec := fakeexec.FakeExec{
|
||||||
@ -192,6 +197,9 @@ func TestDeleteUDPConnections(t *testing.T) {
|
|||||||
func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
|
func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
|
||||||
func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
|
func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
|
||||||
func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
|
func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
|
||||||
|
func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
|
||||||
|
func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
|
||||||
|
func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
|
||||||
},
|
},
|
||||||
LookPathFunc: func(cmd string) (string, error) { return cmd, nil },
|
LookPathFunc: func(cmd string) (string, error) { return cmd, nil },
|
||||||
}
|
}
|
||||||
@ -200,30 +208,52 @@ func TestDeleteUDPConnections(t *testing.T) {
|
|||||||
name string
|
name string
|
||||||
origin string
|
origin string
|
||||||
dest string
|
dest string
|
||||||
|
proto v1.Protocol
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "IPv4 success",
|
name: "UDP IPv4 success",
|
||||||
origin: "1.2.3.4",
|
origin: "1.2.3.4",
|
||||||
dest: "10.20.30.40",
|
dest: "10.20.30.40",
|
||||||
|
proto: v1.ProtocolUDP,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "IPv4 simulated failure",
|
name: "UDP IPv4 simulated failure",
|
||||||
origin: "2.3.4.5",
|
origin: "2.3.4.5",
|
||||||
dest: "20.30.40.50",
|
dest: "20.30.40.50",
|
||||||
|
proto: v1.ProtocolUDP,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "IPv6 success",
|
name: "UDP IPv6 success",
|
||||||
origin: "fd00::600d:f00d",
|
origin: "fd00::600d:f00d",
|
||||||
dest: "2001:db8::5",
|
dest: "2001:db8::5",
|
||||||
|
proto: v1.ProtocolUDP,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "SCTP IPv4 success",
|
||||||
|
origin: "1.2.3.5",
|
||||||
|
dest: "10.20.30.50",
|
||||||
|
proto: v1.ProtocolSCTP,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "SCTP IPv4 simulated failure",
|
||||||
|
origin: "2.3.4.6",
|
||||||
|
dest: "20.30.40.60",
|
||||||
|
proto: v1.ProtocolSCTP,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "SCTP IPv6 success",
|
||||||
|
origin: "fd00::600d:f00d",
|
||||||
|
dest: "2001:db8::6",
|
||||||
|
proto: v1.ProtocolSCTP,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
svcCount := 0
|
svcCount := 0
|
||||||
for i, tc := range testCases {
|
for i, tc := range testCases {
|
||||||
err := ClearEntriesForNAT(&fexec, tc.origin, tc.dest, v1.ProtocolUDP)
|
err := ClearEntriesForNAT(&fexec, tc.origin, tc.dest, tc.proto)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("%s test case: unexpected error: %v", tc.name, err)
|
t.Errorf("%s test case: unexpected error: %v", tc.name, err)
|
||||||
}
|
}
|
||||||
expectCommand := fmt.Sprintf("conntrack -D --orig-dst %s --dst-nat %s -p udp", tc.origin, tc.dest) + familyParamStr(utilnet.IsIPv6String(tc.origin))
|
expectCommand := fmt.Sprintf("conntrack -D --orig-dst %s --dst-nat %s -p %s", tc.origin, tc.dest, protoStr(tc.proto)) + familyParamStr(utilnet.IsIPv6String(tc.origin))
|
||||||
execCommand := strings.Join(fcmd.CombinedOutputLog[i], " ")
|
execCommand := strings.Join(fcmd.CombinedOutputLog[i], " ")
|
||||||
if expectCommand != execCommand {
|
if expectCommand != execCommand {
|
||||||
t.Errorf("%s test case: Expect command: %s, but executed %s", tc.name, expectCommand, execCommand)
|
t.Errorf("%s test case: Expect command: %s, but executed %s", tc.name, expectCommand, execCommand)
|
||||||
@ -235,13 +265,10 @@ func TestDeleteUDPConnections(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestClearUDPConntrackForPortNAT(t *testing.T) {
|
func TestClearConntrackForPortNAT(t *testing.T) {
|
||||||
fcmd := fakeexec.FakeCmd{
|
fcmd := fakeexec.FakeCmd{
|
||||||
CombinedOutputScript: []fakeexec.FakeAction{
|
CombinedOutputScript: []fakeexec.FakeAction{
|
||||||
func() ([]byte, []byte, error) { return []byte("1 flow entries have been deleted"), nil, nil },
|
func() ([]byte, []byte, error) { return []byte("1 flow entries have been deleted"), nil, nil },
|
||||||
func() ([]byte, []byte, error) {
|
|
||||||
return []byte(""), nil, fmt.Errorf("conntrack v1.4.2 (conntrack-tools): 0 flow entries have been deleted")
|
|
||||||
},
|
|
||||||
func() ([]byte, []byte, error) { return []byte("1 flow entries have been deleted"), nil, nil },
|
func() ([]byte, []byte, error) { return []byte("1 flow entries have been deleted"), nil, nil },
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -249,7 +276,6 @@ func TestClearUDPConntrackForPortNAT(t *testing.T) {
|
|||||||
CommandScript: []fakeexec.FakeCommandAction{
|
CommandScript: []fakeexec.FakeCommandAction{
|
||||||
func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
|
func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
|
||||||
func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
|
func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
|
||||||
func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
|
|
||||||
},
|
},
|
||||||
LookPathFunc: func(cmd string) (string, error) { return cmd, nil },
|
LookPathFunc: func(cmd string) (string, error) { return cmd, nil },
|
||||||
}
|
}
|
||||||
@ -257,20 +283,28 @@ func TestClearUDPConntrackForPortNAT(t *testing.T) {
|
|||||||
name string
|
name string
|
||||||
port int
|
port int
|
||||||
dest string
|
dest string
|
||||||
|
proto v1.Protocol
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "IPv4 success",
|
name: "UDP IPv4 success",
|
||||||
port: 30211,
|
port: 30211,
|
||||||
dest: "1.2.3.4",
|
dest: "1.2.3.4",
|
||||||
|
proto: v1.ProtocolUDP,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "SCTP IPv4 success",
|
||||||
|
port: 30215,
|
||||||
|
dest: "1.2.3.5",
|
||||||
|
proto: v1.ProtocolSCTP,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
svcCount := 0
|
svcCount := 0
|
||||||
for i, tc := range testCases {
|
for i, tc := range testCases {
|
||||||
err := ClearEntriesForPortNAT(&fexec, tc.dest, tc.port, v1.ProtocolUDP)
|
err := ClearEntriesForPortNAT(&fexec, tc.dest, tc.port, tc.proto)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("%s test case: unexpected error: %v", tc.name, err)
|
t.Errorf("%s test case: unexpected error: %v", tc.name, err)
|
||||||
}
|
}
|
||||||
expectCommand := fmt.Sprintf("conntrack -D -p udp --dport %d --dst-nat %s", tc.port, tc.dest) + familyParamStr(utilnet.IsIPv6String(tc.dest))
|
expectCommand := fmt.Sprintf("conntrack -D -p %s --dport %d --dst-nat %s", protoStr(tc.proto), tc.port, tc.dest) + familyParamStr(utilnet.IsIPv6String(tc.dest))
|
||||||
execCommand := strings.Join(fcmd.CombinedOutputLog[i], " ")
|
execCommand := strings.Join(fcmd.CombinedOutputLog[i], " ")
|
||||||
if expectCommand != execCommand {
|
if expectCommand != execCommand {
|
||||||
t.Errorf("%s test case: Expect command: %s, but executed %s", tc.name, expectCommand, execCommand)
|
t.Errorf("%s test case: Expect command: %s, but executed %s", tc.name, expectCommand, execCommand)
|
||||||
|
@ -19,6 +19,7 @@ package ipvs
|
|||||||
import (
|
import (
|
||||||
"net"
|
"net"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"k8s.io/apimachinery/pkg/util/version"
|
"k8s.io/apimachinery/pkg/util/version"
|
||||||
@ -133,3 +134,8 @@ func GetRequiredIPVSModules(kernelVersion *version.Version) []string {
|
|||||||
return []string{KernelModuleIPVS, KernelModuleIPVSRR, KernelModuleIPVSWRR, KernelModuleIPVSSH, KernelModuleNfConntrack}
|
return []string{KernelModuleIPVS, KernelModuleIPVSRR, KernelModuleIPVSWRR, KernelModuleIPVSSH, KernelModuleNfConntrack}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsRsGracefulTerminationNeeded returns true if protocol requires graceful termination for the stale connections
|
||||||
|
func IsRsGracefulTerminationNeeded(proto string) bool {
|
||||||
|
return !strings.EqualFold(proto, "UDP") && !strings.EqualFold(proto, "SCTP")
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user