diff --git a/pkg/proxy/iptables/proxier.go b/pkg/proxy/iptables/proxier.go index 4b57e044065..a4338f62d35 100644 --- a/pkg/proxy/iptables/proxier.go +++ b/pkg/proxy/iptables/proxier.go @@ -960,32 +960,53 @@ func (proxier *Proxier) syncProxyRules() { if svcInfo.NodePort != 0 { // Hold the local port open so no other process can open it // (because the socket might open but it would never work). - lp := utilproxy.LocalPort{ - Description: "nodePort for " + svcNameString, - IP: "", - Port: svcInfo.NodePort, - Protocol: protocol, + addresses, err := utilproxy.GetNodeAddresses(proxier.nodePortAddresses, proxier.networkInterfacer) + if err != nil { + glog.Errorf("Failed to get node ip address matching nodeport cidr: %v", err) + continue } - if proxier.portsMap[lp] != nil { - glog.V(4).Infof("Port %s was open before and is still needed", lp.String()) - replacementPortsMap[lp] = proxier.portsMap[lp] - } else { - socket, err := proxier.portMapper.OpenLocalPort(&lp) - if err != nil { - glog.Errorf("can't open %s, skipping this nodePort: %v", lp.String(), err) - continue + + lps := make([]utilproxy.LocalPort, 0) + for address := range addresses { + lp := utilproxy.LocalPort{ + Description: "nodePort for " + svcNameString, + IP: address, + Port: svcInfo.NodePort, + Protocol: protocol, } - if lp.Protocol == "udp" { - // TODO: We might have multiple services using the same port, and this will clear conntrack for all of them. - // This is very low impact. The NodePort range is intentionally obscure, and unlikely to actually collide with real Services. - // This only affects UDP connections, which are not common. - // See issue: https://github.com/kubernetes/kubernetes/issues/49881 - err := conntrack.ClearEntriesForPort(proxier.exec, lp.Port, isIPv6, v1.ProtocolUDP) + if utilproxy.IsZeroCIDR(address) { + // Empty IP address means all + lp.IP = "" + lps = append(lps, lp) + // If we encounter a zero CIDR, then there is no point in processing the rest of the addresses. + break + } + lps = append(lps, lp) + } + + // For ports on node IPs, open the actual port and hold it. + for _, lp := range lps { + if proxier.portsMap[lp] != nil { + glog.V(4).Infof("Port %s was open before and is still needed", lp.String()) + replacementPortsMap[lp] = proxier.portsMap[lp] + } else { + socket, err := proxier.portMapper.OpenLocalPort(&lp) if err != nil { - glog.Errorf("Failed to clear udp conntrack for port %d, error: %v", lp.Port, err) + glog.Errorf("can't open %s, skipping this nodePort: %v", lp.String(), err) + continue } + if lp.Protocol == "udp" { + // TODO: We might have multiple services using the same port, and this will clear conntrack for all of them. + // This is very low impact. The NodePort range is intentionally obscure, and unlikely to actually collide with real Services. + // This only affects UDP connections, which are not common. + // See issue: https://github.com/kubernetes/kubernetes/issues/49881 + err := conntrack.ClearEntriesForPort(proxier.exec, lp.Port, isIPv6, v1.ProtocolUDP) + if err != nil { + glog.Errorf("Failed to clear udp conntrack for port %d, error: %v", lp.Port, err) + } + } + replacementPortsMap[lp] = socket } - replacementPortsMap[lp] = socket } if hasEndpoints { diff --git a/pkg/proxy/ipvs/proxier.go b/pkg/proxy/ipvs/proxier.go index b2d89ff863f..9b4133d3c3c 100644 --- a/pkg/proxy/ipvs/proxier.go +++ b/pkg/proxy/ipvs/proxier.go @@ -1066,27 +1066,48 @@ func (proxier *Proxier) syncProxyRules() { } if svcInfo.NodePort != 0 { - lp := utilproxy.LocalPort{ - Description: "nodePort for " + svcNameString, - IP: "", - Port: svcInfo.NodePort, - Protocol: protocol, + addresses, err := utilproxy.GetNodeAddresses(proxier.nodePortAddresses, proxier.networkInterfacer) + if err != nil { + glog.Errorf("Failed to get node ip address matching nodeport cidr: %v", err) + continue } - if proxier.portsMap[lp] != nil { - glog.V(4).Infof("Port %s was open before and is still needed", lp.String()) - replacementPortsMap[lp] = proxier.portsMap[lp] - } else { - socket, err := proxier.portMapper.OpenLocalPort(&lp) - if err != nil { - glog.Errorf("can't open %s, skipping this nodePort: %v", lp.String(), err) - continue + + lps := make([]utilproxy.LocalPort, 0) + for address := range addresses { + lp := utilproxy.LocalPort{ + Description: "nodePort for " + svcNameString, + IP: address, + Port: svcInfo.NodePort, + Protocol: protocol, } - if lp.Protocol == "udp" { - isIPv6 := utilnet.IsIPv6(svcInfo.ClusterIP) - conntrack.ClearEntriesForPort(proxier.exec, lp.Port, isIPv6, clientv1.ProtocolUDP) + if utilproxy.IsZeroCIDR(address) { + // Empty IP address means all + lp.IP = "" + lps = append(lps, lp) + // If we encounter a zero CIDR, then there is no point in processing the rest of the addresses. + break } - replacementPortsMap[lp] = socket - } // We're holding the port, so it's OK to install ipvs rules. + lps = append(lps, lp) + } + + // For ports on node IPs, open the actual port and hold it. + for _, lp := range lps { + if proxier.portsMap[lp] != nil { + glog.V(4).Infof("Port %s was open before and is still needed", lp.String()) + replacementPortsMap[lp] = proxier.portsMap[lp] + } else { + socket, err := proxier.portMapper.OpenLocalPort(&lp) + if err != nil { + glog.Errorf("can't open %s, skipping this nodePort: %v", lp.String(), err) + continue + } + if lp.Protocol == "udp" { + isIPv6 := utilnet.IsIPv6(svcInfo.ClusterIP) + conntrack.ClearEntriesForPort(proxier.exec, lp.Port, isIPv6, clientv1.ProtocolUDP) + } + replacementPortsMap[lp] = socket + } // We're holding the port, so it's OK to install ipvs rules. + } // Nodeports need SNAT, unless they're local. // ipset call @@ -1137,11 +1158,6 @@ func (proxier *Proxier) syncProxyRules() { // Build ipvs kernel routes for each node ip address nodeIPs := make([]net.IP, 0) - addresses, err := utilproxy.GetNodeAddresses(proxier.nodePortAddresses, proxier.networkInterfacer) - if err != nil { - glog.Errorf("Failed to get node ip address matching nodeport cidr") - continue - } for address := range addresses { if !utilproxy.IsZeroCIDR(address) { nodeIPs = append(nodeIPs, net.ParseIP(address))