Change:
What happened: SCTP nodeport service is not working for all IPs present in the node when ipvs is enabled. It is working only for node's InternalIP. What you expected to happen: SCTP nodeport service must work for all ips present in the node when ipvs is enabled. How to reproduce it (as minimally and precisely as possible): Create a kubernetes cluster with at least one node having multiple interfaces with different IPs. Start an SCTP server deployment. Then create a nodeport service for the same. Now try to reach the service using all the IPs present in that node. The service will be accessible only via node's InternalIP not any other IPs. Environment: Kubernetes version: v1.15.2 OS: ubuntu 18.04 Kernel: 4.15.0-54-generic Network plugin and version (if this is a network-related bug): ipset version v6.34
/sig network /area kube-proxy /area ipvs
/triage unresolved Comment when the issue is assessed and confirmed. I am a bot run by vllry. ‍
/remove-triage unresolved

Discussion:
// Nodeports need SNAT, unless they're local. // ipset call <del> var nodePortSet *IPSet </del> <ins> var ( nodePortSet *IPSet entries     []*utilipset.Entry ) </ins> switch protocol { case "tcp": nodePortSet = proxier.ipsetList[kubeNodePortSetTCP] <del> entry = &utilipset.Entry{ </del> <ins> entries = []*utilipset.Entry{{ </ins> // No need to provide ip info Port:     svcInfo.NodePort(), Protocol: protocol, SetType:  utilipset.BitmapPort, <del> } </del> <ins> }} </ins> case "udp": nodePortSet = proxier.ipsetList[kubeNodePortSetUDP] <del> entry = &utilipset.Entry{ </del> <ins> entries = []*utilipset.Entry{{ </ins> // No need to provide ip info Port:     svcInfo.NodePort(), Protocol: protocol, SetType:  utilipset.BitmapPort, <del> } </del> <ins> }} </ins> case "sctp": nodePortSet = proxier.ipsetList[kubeNodePortSetSCTP] <del> entry = &utilipset.Entry{ IP:       proxier.nodeIP.String(), Port:     svcInfo.NodePort(), Protocol: protocol, SetType:  utilipset.HashIPPort, </del> <ins> // Since hash ip:port is used for SCTP, all the nodeIPs to be used in the SCTP ipset entries. entries = []*utilipset.Entry{} for _, nodeIP := range nodeIPs { entries = append(entries, &utilipset.Entry{ IP:       nodeIP.String(), Port:     svcInfo.NodePort(), Protocol: protocol, SetType:  utilipset.HashIPPort, }) </ins> } default: // It should never hit klog.Errorf("Unsupported protocol type: %s", protocol) } if nodePortSet != nil { <del> if valid := nodePortSet.validateEntry(entry); !valid { klog.Errorf("%s", fmt.Sprintf(EntryInvalidErr, entry, nodePortSet.Name)) </del> <ins> entryInvalidErr := false for _, entry := range entries { if valid := nodePortSet.validateEntry(entry); !valid { klog.Errorf("%s", fmt.Sprintf(EntryInvalidErr, entry, nodePortSet.Name)) entryInvalidErr = true break } nodePortSet.activeEntries.Insert(entry.String()) } if entryInvalidErr { </ins> continue } <del> nodePortSet.activeEntries.Insert(entry.String()) </del> } // Add externaltrafficpolicy=local type nodeport entry

klog.Errorf("Unsupported protocol type: %s", protocol) } if nodePortLocalSet != nil { <del> if valid := nodePortLocalSet.validateEntry(entry); !valid { klog.Errorf("%s", fmt.Sprintf(EntryInvalidErr, entry, nodePortLocalSet.Name)) </del> <ins> entryInvalidErr := false for _, entry := range entries { if valid := nodePortLocalSet.validateEntry(entry); !valid { klog.Errorf("%s", fmt.Sprintf(EntryInvalidErr, entry, nodePortLocalSet.Name)) entryInvalidErr = true break } nodePortLocalSet.activeEntries.Insert(entry.String()) } if entryInvalidErr { </ins> continue } <del> nodePortLocalSet.activeEntries.Insert(entry.String()) </del> } }

"fmt" "net" "reflect" <ins> "sort" </ins> "strings" "testing"

}, }, }, <ins> { name: "node port service with protocol sctp on a node with multiple nodeIPs", services: []*v1.Service{ makeTestService("ns1", "svc1", func(svc *v1.Service) { svc.Spec.Type = "NodePort" svc.Spec.ClusterIP = "10.20.30.41" svc.Spec.Ports = []v1.ServicePort{{ Name:     "p80", Port:     int32(80), Protocol: v1.ProtocolSCTP, NodePort: int32(3001), }} }), }, endpoints: []*v1.Endpoints{ makeTestEndpoints("ns1", "svc1", func(ept *v1.Endpoints) { ept.Subsets = []v1.EndpointSubset{{ Addresses: []v1.EndpointAddress{{ IP: "10.180.0.1", }}, Ports: []v1.EndpointPort{{ Name: "p80", Port: int32(80), }}, }} }), }, nodeIPs: []net.IP{ net.ParseIP("100.101.102.103"), net.ParseIP("100.101.102.104"), net.ParseIP("100.101.102.105"), net.ParseIP("2001:db8::1:1"), net.ParseIP("2001:db8::1:2"), net.ParseIP("2001:db8::1:3"), }, nodePortAddresses: []string{}, expectedIPVS: &ipvstest.FakeIPVS{ Services: map[ipvstest.ServiceKey]*utilipvs.VirtualServer{ { IP:       "10.20.30.41", Port:     80, Protocol: "SCTP", }: { Address:   net.ParseIP("10.20.30.41"), Protocol:  "SCTP", Port:      uint16(80), Scheduler: "rr", }, { IP:       "100.101.102.103", Port:     3001, Protocol: "SCTP", }: { Address:   net.ParseIP("100.101.102.103"), Protocol:  "SCTP", Port:      uint16(3001), Scheduler: "rr", }, { IP:       "100.101.102.104", Port:     3001, Protocol: "SCTP", }: { Address:   net.ParseIP("100.101.102.104"), Protocol:  "SCTP", Port:      uint16(3001), Scheduler: "rr", }, { IP:       "100.101.102.105", Port:     3001, Protocol: "SCTP", }: { Address:   net.ParseIP("100.101.102.105"), Protocol:  "SCTP", Port:      uint16(3001), Scheduler: "rr", }, { IP:       "2001:db8::1:1", Port:     3001, Protocol: "SCTP", }: { Address:   net.ParseIP("2001:db8::1:1"), Protocol:  "SCTP", Port:      uint16(3001), Scheduler: "rr", }, { IP:       "2001:db8::1:2", Port:     3001, Protocol: "SCTP", }: { Address:   net.ParseIP("2001:db8::1:2"), Protocol:  "SCTP", Port:      uint16(3001), Scheduler: "rr", }, { IP:       "2001:db8::1:3", Port:     3001, Protocol: "SCTP", }: { Address:   net.ParseIP("2001:db8::1:3"), Protocol:  "SCTP", Port:      uint16(3001), Scheduler: "rr", }, }, Destinations: map[ipvstest.ServiceKey][]*utilipvs.RealServer{ { IP:       "10.20.30.41", Port:     80, Protocol: "SCTP", }: { { Address: net.ParseIP("10.180.0.1"), Port:    uint16(80), Weight:  1, }, }, { IP:       "100.101.102.103", Port:     3001, Protocol: "SCTP", }: { { Address: net.ParseIP("10.180.0.1"), Port:    uint16(80), Weight:  1, }, }, { IP:       "100.101.102.104", Port:     3001, Protocol: "SCTP", }: { { Address: net.ParseIP("10.180.0.1"), Port:    uint16(80), Weight:  1, }, }, { IP:       "100.101.102.105", Port:     3001, Protocol: "SCTP", }: { { Address: net.ParseIP("10.180.0.1"), Port:    uint16(80), Weight:  1, }, }, { IP:       "2001:db8::1:1", Port:     3001, Protocol: "SCTP", }: { { Address: net.ParseIP("10.180.0.1"), Port:    uint16(80), Weight:  1, }, }, { IP:       "2001:db8::1:2", Port:     3001, Protocol: "SCTP", }: { { Address: net.ParseIP("10.180.0.1"), Port:    uint16(80), Weight:  1, }, }, { IP:       "2001:db8::1:3", Port:     3001, Protocol: "SCTP", }: { { Address: net.ParseIP("10.180.0.1"), Port:    uint16(80), Weight:  1, }, }, }, }, expectedIPSets: netlinktest.ExpectedIPSet{ kubeNodePortSetSCTP: { { IP:       "100.101.102.103", Port:     3001, Protocol: strings.ToLower(string(v1.ProtocolSCTP)), SetType:  utilipset.HashIPPort, }, { IP:       "100.101.102.104", Port:     3001, Protocol: strings.ToLower(string(v1.ProtocolSCTP)), SetType:  utilipset.HashIPPort, }, { IP:       "100.101.102.105", Port:     3001, Protocol: strings.ToLower(string(v1.ProtocolSCTP)), SetType:  utilipset.HashIPPort, }, { IP:       "2001:db8::1:1", Port:     3001, Protocol: strings.ToLower(string(v1.ProtocolSCTP)), SetType:  utilipset.HashIPPort, }, { IP:       "2001:db8::1:2", Port:     3001, Protocol: strings.ToLower(string(v1.ProtocolSCTP)), SetType:  utilipset.HashIPPort, }, { IP:       "2001:db8::1:3", Port:     3001, Protocol: strings.ToLower(string(v1.ProtocolSCTP)), SetType:  utilipset.HashIPPort, }, }, }, }, </ins> } for _, test := range tests {

t.Errorf("Check ipset entries failed for ipset: %q, expect %d, got %d", set, len(entries), len(ents)) continue } <del> if len(entries) == 1 { if ents[0] != entries[0].String() { t.Errorf("Check ipset entries failed for ipset: %q", set) } </del> <ins> expectedEntries := []string{} for _, entry := range entries { expectedEntries = append(expectedEntries, entry.String()) } sort.Strings(ents) sort.Strings(expectedEntries) if !reflect.DeepEqual(ents, expectedEntries) { t.Errorf("Check ipset entries failed for ipset: %q", set) </ins> } } }