From d3fe48e848f49d3c60dd16749228e6471a57458a Mon Sep 17 00:00:00 2001 From: Surya Seetharaman Date: Wed, 25 Nov 2020 15:19:54 +0100 Subject: [PATCH] Kube-proxy: perf-enhancement: Reduce NAT table KUBE-SERVICES/NODEPORTS chain rules The nat KUBE-SERVICES chain is called from OUTPUT and PREROUTING stages. In clusters with large number of services, the nat-KUBE-SERVICES chain is the largest chain with for eg: 33k rules. This patch aims to move the KubeMarkMasq rules from the kubeServicesChain into the respective KUBE-SVC-* chains. This way during each packet-rule matching we won't have to traverse the MASQ rules of all services which get accumulated in the KUBE-SERVICES and/or KUBE-NODEPORTS chains. Since the jump to KUBE-MARK-MASQ ultimately sets the 0x400 mark for nodeIP SNAT, it should not matter whether the jump is made from KUBE-SERVICES or KUBE-SVC-* chains. Specifically we change: 1) For ClusterIP svc, we move the KUBE-MARK-MASQ jump rule from KUBE-SERVICES chain into KUBE-SVC-* chain. 2) For ExternalIP svc, we move the KUBE-MARK-MASQ jump rule in the case of non-ServiceExternalTrafficPolicyTypeLocal from KUBE-SERVICES chain into KUBE-SVC-* chain. 3) For NodePorts svc, we move the KUBE-MARK-MASQ jump rule in case of non-ServiceExternalTrafficPolicyTypeLocal from KUBE-NODEPORTS chain to KUBE-SVC-* chain. 4) For load-balancer svc, we don't change anything since it is already svc specific due to creation of KUBE-FW-* chains per svc. This would cut the rules per svc in KUBE-SERVICES and KUBE-NODEPORTS in half. --- pkg/proxy/iptables/proxier.go | 25 +++---- pkg/proxy/iptables/proxier_test.go | 105 ++++++++++++++++++++++++++--- pkg/proxy/util/utils.go | 12 ++++ pkg/proxy/util/utils_test.go | 38 +++++++++++ 4 files changed, 156 insertions(+), 24 deletions(-) diff --git a/pkg/proxy/iptables/proxier.go b/pkg/proxy/iptables/proxier.go index a7d976de59b..7d276a5dd32 100644 --- a/pkg/proxy/iptables/proxier.go +++ b/pkg/proxy/iptables/proxier.go @@ -1062,23 +1062,22 @@ func (proxier *Proxier) syncProxyRules() { // Capture the clusterIP. if hasEndpoints { args = append(args[:0], - "-A", string(kubeServicesChain), "-m", "comment", "--comment", fmt.Sprintf(`"%s cluster IP"`, svcNameString), "-m", protocol, "-p", protocol, "-d", utilproxy.ToCIDR(svcInfo.ClusterIP()), "--dport", strconv.Itoa(svcInfo.Port()), ) if proxier.masqueradeAll { - utilproxy.WriteLine(proxier.natRules, append(args, "-j", string(KubeMarkMasqChain))...) + utilproxy.WriteRuleLine(proxier.natRules, string(svcChain), append(args, "-j", string(KubeMarkMasqChain))...) } else if proxier.localDetector.IsImplemented() { // This masquerades off-cluster traffic to a service VIP. The idea // is that you can establish a static route for your Service range, // routing to any node, and that node will bridge into the Service // for you. Since that might bounce off-node, we masquerade here. // If/when we support "Local" policy for VIPs, we should update this. - utilproxy.WriteLine(proxier.natRules, proxier.localDetector.JumpIfNotLocal(args, string(KubeMarkMasqChain))...) + utilproxy.WriteRuleLine(proxier.natRules, string(svcChain), proxier.localDetector.JumpIfNotLocal(args, string(KubeMarkMasqChain))...) } - utilproxy.WriteLine(proxier.natRules, append(args, "-j", string(svcChain))...) + utilproxy.WriteRuleLine(proxier.natRules, string(kubeServicesChain), append(args, "-j", string(svcChain))...) } else { // No endpoints. utilproxy.WriteLine(proxier.filterRules, @@ -1129,7 +1128,6 @@ func (proxier *Proxier) syncProxyRules() { if hasEndpoints { args = append(args[:0], - "-A", string(kubeServicesChain), "-m", "comment", "--comment", fmt.Sprintf(`"%s external IP"`, svcNameString), "-m", protocol, "-p", protocol, "-d", utilproxy.ToCIDR(net.ParseIP(externalIP)), @@ -1145,13 +1143,13 @@ func (proxier *Proxier) syncProxyRules() { destChain = svcChain // This masquerades off-cluster traffic to a External IP. if proxier.localDetector.IsImplemented() { - utilproxy.WriteLine(proxier.natRules, proxier.localDetector.JumpIfNotLocal(args, string(KubeMarkMasqChain))...) + utilproxy.WriteRuleLine(proxier.natRules, string(svcChain), proxier.localDetector.JumpIfNotLocal(args, string(KubeMarkMasqChain))...) } else { - utilproxy.WriteLine(proxier.natRules, append(args, "-j", string(KubeMarkMasqChain))...) + utilproxy.WriteRuleLine(proxier.natRules, string(svcChain), append(args, "-j", string(KubeMarkMasqChain))...) } } - // Sent traffic bound for external IPs to the service chain. - utilproxy.WriteLine(proxier.natRules, append(args, "-j", string(destChain))...) + // Send traffic bound for external IPs to the service chain. + utilproxy.WriteRuleLine(proxier.natRules, string(kubeServicesChain), append(args, "-j", string(destChain))...) } else { // No endpoints. @@ -1302,16 +1300,15 @@ func (proxier *Proxier) syncProxyRules() { if hasEndpoints { args = append(args[:0], - "-A", string(kubeNodePortsChain), "-m", "comment", "--comment", svcNameString, "-m", protocol, "-p", protocol, "--dport", strconv.Itoa(svcInfo.NodePort()), ) if !svcInfo.NodeLocalExternal() { // Nodeports need SNAT, unless they're local. - utilproxy.WriteLine(proxier.natRules, append(args, "-j", string(KubeMarkMasqChain))...) + utilproxy.WriteRuleLine(proxier.natRules, string(svcChain), append(args, "-j", string(KubeMarkMasqChain))...) // Jump to the service chain. - utilproxy.WriteLine(proxier.natRules, append(args, "-j", string(svcChain))...) + utilproxy.WriteRuleLine(proxier.natRules, string(kubeNodePortsChain), append(args, "-j", string(svcChain))...) } else { // TODO: Make all nodePorts jump to the firewall chain. // Currently we only create it for loadbalancers (#33586). @@ -1321,8 +1318,8 @@ func (proxier *Proxier) syncProxyRules() { if isIPv6 { loopback = "::1/128" } - utilproxy.WriteLine(proxier.natRules, append(args, "-s", loopback, "-j", string(KubeMarkMasqChain))...) - utilproxy.WriteLine(proxier.natRules, append(args, "-j", string(svcXlbChain))...) + utilproxy.WriteRuleLine(proxier.natRules, string(kubeNodePortsChain), append(args, "-s", loopback, "-j", string(KubeMarkMasqChain))...) + utilproxy.WriteRuleLine(proxier.natRules, string(kubeNodePortsChain), append(args, "-j", string(svcXlbChain))...) } } else { // No endpoints. diff --git a/pkg/proxy/iptables/proxier_test.go b/pkg/proxy/iptables/proxier_test.go index 461cc23e6c6..ec4328604b0 100644 --- a/pkg/proxy/iptables/proxier_test.go +++ b/pkg/proxy/iptables/proxier_test.go @@ -865,14 +865,14 @@ COMMIT -A KUBE-POSTROUTING -m comment --comment "kubernetes service traffic requiring SNAT" -j MASQUERADE -A KUBE-MARK-MASQ -j MARK --or-mark 0x4000 -A KUBE-SERVICES -m comment --comment "ns1/svc1:p80 cluster IP" -m tcp -p tcp -d 10.20.30.41/32 --dport 80 -j KUBE-SVC-XPGD46QRK7WJZT7O --A KUBE-SERVICES -m comment --comment "ns1/svc1:p80 cluster IP" -m tcp -p tcp -d 10.20.30.41/32 --dport 80 ! -s 10.0.0.0/24 -j KUBE-MARK-MASQ +-A KUBE-SVC-XPGD46QRK7WJZT7O -m comment --comment "ns1/svc1:p80 cluster IP" -m tcp -p tcp -d 10.20.30.41/32 --dport 80 ! -s 10.0.0.0/24 -j KUBE-MARK-MASQ -A KUBE-SVC-XPGD46QRK7WJZT7O -m comment --comment ns1/svc1:p80 -j KUBE-SEP-SXIVWICOYRO3J4NJ -A KUBE-SEP-SXIVWICOYRO3J4NJ -m comment --comment ns1/svc1:p80 -s 10.180.0.1/32 -j KUBE-MARK-MASQ -A KUBE-SEP-SXIVWICOYRO3J4NJ -m comment --comment ns1/svc1:p80 -m tcp -p tcp -j DNAT --to-destination 10.180.0.1:80 -A KUBE-SERVICES -m comment --comment "ns2/svc2:p80 cluster IP" -m tcp -p tcp -d 10.20.30.42/32 --dport 80 -j KUBE-SVC-GNZBNJ2PO5MGZ6GT -A KUBE-SERVICES -m comment --comment "ns2/svc2:p80 external IP" -m tcp -p tcp -d 1.2.3.4/32 --dport 80 -j KUBE-XLB-GNZBNJ2PO5MGZ6GT -A KUBE-SERVICES -m comment --comment "ns2/svc2:p80 loadbalancer IP" -m tcp -p tcp -d 1.2.3.4/32 --dport 80 -j KUBE-FW-GNZBNJ2PO5MGZ6GT --A KUBE-SERVICES -m comment --comment "ns2/svc2:p80 cluster IP" -m tcp -p tcp -d 10.20.30.42/32 --dport 80 ! -s 10.0.0.0/24 -j KUBE-MARK-MASQ +-A KUBE-SVC-GNZBNJ2PO5MGZ6GT -m comment --comment "ns2/svc2:p80 cluster IP" -m tcp -p tcp -d 10.20.30.42/32 --dport 80 ! -s 10.0.0.0/24 -j KUBE-MARK-MASQ -A KUBE-SVC-GNZBNJ2PO5MGZ6GT -m comment --comment ns2/svc2:p80 -j KUBE-SEP-RS4RBKLTHTF2IUXJ -A KUBE-SEP-RS4RBKLTHTF2IUXJ -m comment --comment ns2/svc2:p80 -s 10.180.0.2/32 -j KUBE-MARK-MASQ -A KUBE-SEP-RS4RBKLTHTF2IUXJ -m comment --comment ns2/svc2:p80 -m tcp -p tcp -j DNAT --to-destination 10.180.0.2:80 @@ -885,16 +885,16 @@ COMMIT -A KUBE-XLB-GNZBNJ2PO5MGZ6GT -m comment --comment "route LOCAL traffic for ns2/svc2:p80 LB IP to service chain" -m addrtype --src-type LOCAL -j KUBE-SVC-GNZBNJ2PO5MGZ6GT -A KUBE-XLB-GNZBNJ2PO5MGZ6GT -m comment --comment "ns2/svc2:p80 has no local endpoints" -j KUBE-MARK-DROP -A KUBE-SERVICES -m comment --comment "ns3/svc3:p80 cluster IP" -m tcp -p tcp -d 10.20.30.43/32 --dport 80 -j KUBE-SVC-X27LE4BHSL4DOUIK --A KUBE-SERVICES -m comment --comment "ns3/svc3:p80 cluster IP" -m tcp -p tcp -d 10.20.30.43/32 --dport 80 ! -s 10.0.0.0/24 -j KUBE-MARK-MASQ +-A KUBE-SVC-X27LE4BHSL4DOUIK -m comment --comment "ns3/svc3:p80 cluster IP" -m tcp -p tcp -d 10.20.30.43/32 --dport 80 ! -s 10.0.0.0/24 -j KUBE-MARK-MASQ -A KUBE-NODEPORTS -m comment --comment ns3/svc3:p80 -m tcp -p tcp --dport 3001 -j KUBE-SVC-X27LE4BHSL4DOUIK --A KUBE-NODEPORTS -m comment --comment ns3/svc3:p80 -m tcp -p tcp --dport 3001 -j KUBE-MARK-MASQ +-A KUBE-SVC-X27LE4BHSL4DOUIK -m comment --comment ns3/svc3:p80 -m tcp -p tcp --dport 3001 -j KUBE-MARK-MASQ -A KUBE-SVC-X27LE4BHSL4DOUIK -m comment --comment ns3/svc3:p80 -j KUBE-SEP-OYPFS5VJICHGATKP -A KUBE-SEP-OYPFS5VJICHGATKP -m comment --comment ns3/svc3:p80 -s 10.180.0.3/32 -j KUBE-MARK-MASQ -A KUBE-SEP-OYPFS5VJICHGATKP -m comment --comment ns3/svc3:p80 -m tcp -p tcp -j DNAT --to-destination 10.180.0.3:80 -A KUBE-SERVICES -m comment --comment "ns4/svc4:p80 cluster IP" -m tcp -p tcp -d 10.20.30.44/32 --dport 80 -j KUBE-SVC-4SW47YFZTEDKD3PK -A KUBE-SERVICES -m comment --comment "ns4/svc4:p80 external IP" -m tcp -p tcp -d 50.60.70.81/32 --dport 80 -j KUBE-SVC-4SW47YFZTEDKD3PK --A KUBE-SERVICES -m comment --comment "ns4/svc4:p80 cluster IP" -m tcp -p tcp -d 10.20.30.44/32 --dport 80 ! -s 10.0.0.0/24 -j KUBE-MARK-MASQ --A KUBE-SERVICES -m comment --comment "ns4/svc4:p80 external IP" -m tcp -p tcp -d 50.60.70.81/32 --dport 80 ! -s 10.0.0.0/24 -j KUBE-MARK-MASQ +-A KUBE-SVC-4SW47YFZTEDKD3PK -m comment --comment "ns4/svc4:p80 cluster IP" -m tcp -p tcp -d 10.20.30.44/32 --dport 80 ! -s 10.0.0.0/24 -j KUBE-MARK-MASQ +-A KUBE-SVC-4SW47YFZTEDKD3PK -m comment --comment "ns4/svc4:p80 external IP" -m tcp -p tcp -d 50.60.70.81/32 --dport 80 ! -s 10.0.0.0/24 -j KUBE-MARK-MASQ -A KUBE-SVC-4SW47YFZTEDKD3PK -m comment --comment ns4/svc4:p80 -m statistic --mode random --probability 0.5000000000 -j KUBE-SEP-UKSFD7AGPMPPLUHC -A KUBE-SVC-4SW47YFZTEDKD3PK -m comment --comment ns4/svc4:p80 -j KUBE-SEP-C6EBXVWJJZMIWKLZ -A KUBE-SEP-UKSFD7AGPMPPLUHC -m comment --comment ns4/svc4:p80 -s 10.180.0.4/32 -j KUBE-MARK-MASQ @@ -1007,6 +1007,10 @@ func TestClusterIPEndpointsJump(t *testing.T) { } svcRules := ipt.GetRules(svcChain) + if !hasJump(svcRules, string(KubeMarkMasqChain), svcIP, svcPort) { + errorf(fmt.Sprintf("Failed to find jump from %v to KUBE-MARK-MASQ chain", svcChain), svcRules, t) + } + if !hasJump(svcRules, epChain, "", 0) { errorf(fmt.Sprintf("Failed to jump to ep chain %v", epChain), svcRules, t) } @@ -1140,6 +1144,11 @@ func TestNodePort(t *testing.T) { if !hasJump(kubeNodePortRules, svcChain, "", svcNodePort) { errorf(fmt.Sprintf("Failed to find jump to svc chain %v", svcChain), kubeNodePortRules, t) } + expectedNodePortNonLocalTrafficMasqueradeRule := `-A KUBE-SVC-XPGD46QRK7WJZT7O -m comment --comment ns1/svc1:p80 -m tcp -p tcp --dport 3001 -j KUBE-MARK-MASQ` + svcRules := ipt.GetRules(svcChain) + if !strings.Contains(fp.iptablesData.String(), expectedNodePortNonLocalTrafficMasqueradeRule) { + errorf(fmt.Sprintf("Didn't find the masquerade rule for node port non-local traffic in svc chain %v", svcChain), svcRules, t) + } } func TestHealthCheckNodePort(t *testing.T) { @@ -1310,6 +1319,82 @@ func TestOnlyLocalExternalIPs(t *testing.T) { } } +// TestNonLocalExternalIPs tests if we add the masquerade rule into svcChain in order to +// SNAT packets to external IPs if externalTrafficPolicy is cluster and the traffic is NOT Local. +func TestNonLocalExternalIPs(t *testing.T) { + ipt := iptablestest.NewFake() + fp := NewFakeProxier(ipt, false) + svcIP := "10.20.30.41" + svcPort := 80 + svcExternalIPs := "50.60.70.81" + svcPortName := proxy.ServicePortName{ + NamespacedName: makeNSN("ns1", "svc1"), + Port: "p80", + } + + makeServiceMap(fp, + makeTestService(svcPortName.Namespace, svcPortName.Name, func(svc *v1.Service) { + svc.Spec.ClusterIP = svcIP + svc.Spec.ExternalIPs = []string{svcExternalIPs} + svc.Spec.Ports = []v1.ServicePort{{ + Name: svcPortName.Port, + Port: int32(svcPort), + Protocol: v1.ProtocolTCP, + TargetPort: intstr.FromInt(svcPort), + }} + }), + ) + makeEndpointsMap(fp) + epIP1 := "10.180.0.1" + epIP2 := "10.180.2.1" + makeEndpointsMap(fp, + makeTestEndpoints(svcPortName.Namespace, svcPortName.Name, func(ept *v1.Endpoints) { + ept.Subsets = []v1.EndpointSubset{{ + Addresses: []v1.EndpointAddress{{ + IP: epIP1, + NodeName: nil, + }, { + IP: epIP2, + NodeName: utilpointer.StringPtr(testHostname), + }}, + Ports: []v1.EndpointPort{{ + Name: svcPortName.Port, + Port: int32(svcPort), + Protocol: v1.ProtocolTCP, + }}, + }} + }), + ) + + fp.syncProxyRules() + proto := strings.ToLower(string(v1.ProtocolTCP)) + lbChain := string(serviceLBChainName(svcPortName.String(), proto)) + svcChain := string(servicePortChainName(svcPortName.String(), strings.ToLower(string(v1.ProtocolTCP)))) + + kubeSvcRules := ipt.GetRules(string(kubeServicesChain)) + if !hasJump(kubeSvcRules, svcChain, svcExternalIPs, svcPort) { + errorf(fmt.Sprintf("Failed to find jump to svc chain %v", svcChain), kubeSvcRules, t) + } + + svcRules := ipt.GetRules(svcChain) + if len(svcRules) != 4 { + t.Errorf("expected svcChain %v to have 4 rules, got %v", svcChain, len(svcRules)) + } + if !hasJump(svcRules, string(KubeMarkMasqChain), svcIP, svcPort) { + errorf(fmt.Sprintf("Failed to find jump from %v to KUBE-MARK-MASQ chain", svcChain), svcRules, t) + } + expectedExternalIPNonLocalTrafficMasqueradeRule := `-A KUBE-SVC-XPGD46QRK7WJZT7O -m comment --comment "ns1/svc1:p80 external IP" -m tcp -p tcp -d 50.60.70.81/32 --dport 80 ! -s 10.0.0.0/24 -j KUBE-MARK-MASQ` + + if !strings.Contains(fp.iptablesData.String(), expectedExternalIPNonLocalTrafficMasqueradeRule) { + errorf(fmt.Sprintf("Didn't find the masquerade rule for external-ip non-local traffic in svc chain %v", svcChain), svcRules, t) + } + + lbRules := ipt.GetRules(lbChain) + if len(lbRules) != 0 { + t.Errorf("expected svclbChain %v to have 0 rules, got %v", lbChain, len(lbRules)) + } +} + func TestNodePortReject(t *testing.T) { ipt := iptablestest.NewFake() fp := NewFakeProxier(ipt, false) @@ -2886,7 +2971,7 @@ COMMIT -A KUBE-POSTROUTING -j MARK --xor-mark 0x4000 -A KUBE-POSTROUTING -m comment --comment "kubernetes service traffic requiring SNAT" -j MASQUERADE -A KUBE-MARK-MASQ -j MARK --or-mark 0x4000 --A KUBE-SERVICES -m comment --comment "ns1/svc1 cluster IP" -m tcp -p tcp -d 172.20.1.1/32 --dport 0 ! -s 10.0.0.0/24 -j KUBE-MARK-MASQ +-A KUBE-SVC-AQI2S6QIMU7PVVRP -m comment --comment "ns1/svc1 cluster IP" -m tcp -p tcp -d 172.20.1.1/32 --dport 0 ! -s 10.0.0.0/24 -j KUBE-MARK-MASQ -A KUBE-SERVICES -m comment --comment "ns1/svc1 cluster IP" -m tcp -p tcp -d 172.20.1.1/32 --dport 0 -j KUBE-SVC-AQI2S6QIMU7PVVRP -A KUBE-SVC-AQI2S6QIMU7PVVRP -m comment --comment ns1/svc1 -m statistic --mode random --probability 0.3333333333 -j KUBE-SEP-3JOIVZTXZZRGORX4 -A KUBE-SEP-3JOIVZTXZZRGORX4 -m comment --comment ns1/svc1 -s 10.0.1.1/32 -j KUBE-MARK-MASQ @@ -2986,7 +3071,7 @@ COMMIT -A KUBE-POSTROUTING -j MARK --xor-mark 0x4000 -A KUBE-POSTROUTING -m comment --comment "kubernetes service traffic requiring SNAT" -j MASQUERADE -A KUBE-MARK-MASQ -j MARK --or-mark 0x4000 --A KUBE-SERVICES -m comment --comment "ns1/svc1 cluster IP" -m tcp -p tcp -d 172.20.1.1/32 --dport 0 ! -s 10.0.0.0/24 -j KUBE-MARK-MASQ +-A KUBE-SVC-AQI2S6QIMU7PVVRP -m comment --comment "ns1/svc1 cluster IP" -m tcp -p tcp -d 172.20.1.1/32 --dport 0 ! -s 10.0.0.0/24 -j KUBE-MARK-MASQ -A KUBE-SERVICES -m comment --comment "ns1/svc1 cluster IP" -m tcp -p tcp -d 172.20.1.1/32 --dport 0 -j KUBE-SVC-AQI2S6QIMU7PVVRP -A KUBE-NODEPORTS -m comment --comment ns1/svc1 -m tcp -p tcp --dport 30010 -s 127.0.0.0/8 -j KUBE-MARK-MASQ -A KUBE-NODEPORTS -m comment --comment ns1/svc1 -m tcp -p tcp --dport 30010 -j KUBE-XLB-AQI2S6QIMU7PVVRP @@ -3309,7 +3394,7 @@ COMMIT -A KUBE-POSTROUTING -j MARK --xor-mark 0x4000 -A KUBE-POSTROUTING -m comment --comment "kubernetes service traffic requiring SNAT" -j MASQUERADE -A KUBE-MARK-MASQ -j MARK --or-mark 0x4000 --A KUBE-SERVICES -m comment --comment "ns1/svc1 cluster IP" -m tcp -p tcp -d 172.20.1.1/32 --dport 80 ! -s 10.0.0.0/24 -j KUBE-MARK-MASQ +-A KUBE-SVC-AQI2S6QIMU7PVVRP -m comment --comment "ns1/svc1 cluster IP" -m tcp -p tcp -d 172.20.1.1/32 --dport 80 ! -s 10.0.0.0/24 -j KUBE-MARK-MASQ -A KUBE-SERVICES -m comment --comment "ns1/svc1 cluster IP" -m tcp -p tcp -d 172.20.1.1/32 --dport 80 -j KUBE-SVC-AQI2S6QIMU7PVVRP -A KUBE-SVC-AQI2S6QIMU7PVVRP -m comment --comment ns1/svc1 -m statistic --mode random --probability 0.3333333333 -j KUBE-SEP-3JOIVZTXZZRGORX4 -A KUBE-SEP-3JOIVZTXZZRGORX4 -m comment --comment ns1/svc1 -s 10.0.1.1/32 -j KUBE-MARK-MASQ @@ -3375,7 +3460,7 @@ COMMIT -A KUBE-POSTROUTING -j MARK --xor-mark 0x4000 -A KUBE-POSTROUTING -m comment --comment "kubernetes service traffic requiring SNAT" -j MASQUERADE -A KUBE-MARK-MASQ -j MARK --or-mark 0x4000 --A KUBE-SERVICES -m comment --comment "ns1/svc1 cluster IP" -m tcp -p tcp -d 172.20.1.1/32 --dport 80 ! -s 10.0.0.0/24 -j KUBE-MARK-MASQ +-A KUBE-SVC-AQI2S6QIMU7PVVRP -m comment --comment "ns1/svc1 cluster IP" -m tcp -p tcp -d 172.20.1.1/32 --dport 80 ! -s 10.0.0.0/24 -j KUBE-MARK-MASQ -A KUBE-SERVICES -m comment --comment "ns1/svc1 cluster IP" -m tcp -p tcp -d 172.20.1.1/32 --dport 80 -j KUBE-SVC-AQI2S6QIMU7PVVRP -A KUBE-SVC-AQI2S6QIMU7PVVRP -m comment --comment ns1/svc1 -j KUBE-SEP-3JOIVZTXZZRGORX4 -A KUBE-SEP-3JOIVZTXZZRGORX4 -m comment --comment ns1/svc1 -s 10.0.1.1/32 -j KUBE-MARK-MASQ diff --git a/pkg/proxy/util/utils.go b/pkg/proxy/util/utils.go index 4b419c4118c..57744f10abb 100644 --- a/pkg/proxy/util/utils.go +++ b/pkg/proxy/util/utils.go @@ -477,6 +477,18 @@ func WriteLine(buf *bytes.Buffer, words ...string) { } } +// WriteRuleLine prepends the strings "-A" and chainName to the buffer and calls +// WriteLine to join all the words into the buffer and terminate with newline. +func WriteRuleLine(buf *bytes.Buffer, chainName string, words ...string) { + if len(words) == 0 { + return + } + buf.WriteString("-A ") + buf.WriteString(chainName) + buf.WriteByte(' ') + WriteLine(buf, words...) +} + // WriteBytesLine write bytes to buffer, terminate with newline func WriteBytesLine(buf *bytes.Buffer, bytes []byte) { buf.Write(bytes) diff --git a/pkg/proxy/util/utils_test.go b/pkg/proxy/util/utils_test.go index 6ae4bd67a50..d33e95f0593 100644 --- a/pkg/proxy/util/utils_test.go +++ b/pkg/proxy/util/utils_test.go @@ -1183,6 +1183,44 @@ func TestWriteLine(t *testing.T) { } } +func TestWriteRuleLine(t *testing.T) { + testCases := []struct { + name string + chainName string + words []string + expected string + }{ + { + name: "write no line due to no words", + chainName: "KUBE-SVC-FOO", + words: []string{}, + expected: "", + }, + { + name: "write one line", + chainName: "KUBE-XLB-FOO", + words: []string{"test1"}, + expected: "-A KUBE-XLB-FOO test1\n", + }, + { + name: "write multi word line", + chainName: "lolChain", + words: []string{"test1", "test2", "test3"}, + expected: "-A lolChain test1 test2 test3\n", + }, + } + testBuffer := bytes.NewBuffer(nil) + for _, testCase := range testCases { + t.Run(testCase.name, func(t *testing.T) { + testBuffer.Reset() + WriteRuleLine(testBuffer, testCase.chainName, testCase.words...) + if !strings.EqualFold(testBuffer.String(), testCase.expected) { + t.Fatalf("write word is %v\n expected: %s, got: %s", testCase.words, testCase.expected, testBuffer.String()) + } + }) + } +} + func TestWriteBytesLine(t *testing.T) { testCases := []struct { name string