Change:
<!-- This form is for bug reports and feature requests ONLY! If you're looking for help check and the . --Is this a BUG REPORT or FEATURE REQUEST?: /kind bug What happened: There is a bug in ipv6, see: What you expected to happen: The judgement logic should be: How to reproduce it (as minimally and precisely as possible): Anything else we need to know?: Environment: Kubernetes version (use ): Cloud provider or hardware configuration: OS (e.g. from /etc/os-release): Kernel (e.g. ): Install tools: Others: /sig network /area ipv6
/assign cc

Discussion:
// 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 <del> isIPv6 := svcInfo.clusterIP.To4() != nil </del> <ins> isIPv6 := utilproxy.IsIPv6(svcInfo.clusterIP) </ins> err := utilproxy.ClearUDPConntrackForPort(proxier.exec, lp.Port, isIPv6) if err != nil { glog.Errorf("Failed to clear udp conntrack for port %d, error: %v", lp.Port, err)

continue } if lp.Protocol == "udp" { <del> isIPv6 := svcInfo.clusterIP.To4() != nil </del> <ins> isIPv6 := utilproxy.IsIPv6(svcInfo.clusterIP) </ins> utilproxy.ClearUDPConntrackForPort(proxier.exec, lp.Port, isIPv6) } replacementPortsMap[lp] = socket

const noConnectionToDelete = "0 flow entries have been deleted" <del> func isIPv6(ip string) bool { netIP := net.ParseIP(ip) </del> <ins> func IsIPv6(netIP net.IP) bool { </ins> return netIP != nil && netIP.To4() == nil } <ins> func IsIPv6String(ip string) bool { netIP := net.ParseIP(ip) return IsIPv6(netIP) } </ins> func parametersWithFamily(isIPv6 bool, parameters ...string) []string { if isIPv6 { parameters = append(parameters, "-f", "ipv6")

// ClearUDPConntrackForIP uses the conntrack tool to delete the conntrack entries // for the UDP connections specified by the given service IP func ClearUDPConntrackForIP(execer exec.Interface, ip string) error { <del> parameters := parametersWithFamily(isIPv6(ip), "-D", "--orig-dst", ip, "-p", "udp") </del> <ins> parameters := parametersWithFamily(IsIPv6String(ip), "-D", "--orig-dst", ip, "-p", "udp") </ins> err := ExecConntrackTool(execer, parameters...) if err != nil && !strings.Contains(err.Error(), noConnectionToDelete) { // TODO: Better handling for deletion failure. When failure occur, stale udp connection may not get flushed.

// ClearUDPConntrackForPeers uses the conntrack tool to delete the conntrack entries // for the UDP connections specified by the {origin, dest} IP pair. func ClearUDPConntrackForPeers(execer exec.Interface, origin, dest string) error { <del> parameters := parametersWithFamily(isIPv6(origin), "-D", "--orig-dst", origin, "--dst-nat", dest, "-p", "udp") </del> <ins> parameters := parametersWithFamily(IsIPv6String(origin), "-D", "--orig-dst", origin, "--dst-nat", dest, "-p", "udp") </ins> err := ExecConntrackTool(execer, parameters...) if err != nil && !strings.Contains(err.Error(), noConnectionToDelete) { // TODO: Better handling for deletion failure. When failure occur, stale udp connection may not get flushed.

import ( "fmt" <ins> "net" </ins> "strings" "testing"

if err := ClearUDPConntrackForIP(&fexec, tc.ip); err != nil { t.Errorf("%s test case:, Unexpected error: %v", tc.name, err) } <del> expectCommand := fmt.Sprintf("conntrack -D --orig-dst %s -p udp", tc.ip) + familyParamStr(isIPv6(tc.ip)) </del> <ins> expectCommand := fmt.Sprintf("conntrack -D --orig-dst %s -p udp", tc.ip) + familyParamStr(IsIPv6String(tc.ip)) </ins> execCommand := strings.Join(fcmd.CombinedOutputLog[svcCount], " ") if expectCommand != execCommand { t.Errorf("%s test case: Expect command: %s, but executed %s", tc.name, expectCommand, execCommand)

if err != nil { t.Errorf("%s test case: unexpected error: %v", tc.name, err) } <del> expectCommand := fmt.Sprintf("conntrack -D --orig-dst %s --dst-nat %s -p udp", tc.origin, tc.dest) + familyParamStr(isIPv6(tc.origin)) </del> <ins> expectCommand := fmt.Sprintf("conntrack -D --orig-dst %s --dst-nat %s -p udp", tc.origin, tc.dest) + familyParamStr(IsIPv6String(tc.origin)) </ins> execCommand := strings.Join(fcmd.CombinedOutputLog[i], " ") if expectCommand != execCommand { t.Errorf("%s test case: Expect command: %s, but executed %s", tc.name, expectCommand, execCommand)

t.Errorf("Expect command executed %d times, but got %d", svcCount, fexec.CommandCalls) } } <ins> func TestIsIPv6String(t *testing.T) { testCases := []struct { ip         string expectIPv6 bool }{ { ip:         "127.0.0.1", expectIPv6: false, }, { ip:         "192.168.0.0", expectIPv6: false, }, { ip:         "1.2.3.4", expectIPv6: false, }, { ip:         "bad ip", expectIPv6: false, }, { ip:         "::1", expectIPv6: true, }, { ip:         "fd00::600d:f00d", expectIPv6: true, }, { ip:         "2001:db8::5", expectIPv6: true, }, } for i := range testCases { isIPv6 := IsIPv6String(testCases[i].ip) if isIPv6 != testCases[i].expectIPv6 { t.Errorf("[%d] Expect ipv6 %v, got %v", i+1, testCases[i].expectIPv6, isIPv6) } } } func TestIsIPv6(t *testing.T) { testCases := []struct { ip         net.IP expectIPv6 bool }{ { ip:         net.IPv4zero, expectIPv6: false, }, { ip:         net.IPv4bcast, expectIPv6: false, }, { ip:         net.ParseIP("127.0.0.1"), expectIPv6: false, }, { ip:         net.ParseIP("10.20.40.40"), expectIPv6: false, }, { ip:         net.ParseIP("172.17.3.0"), expectIPv6: false, }, { ip:         nil, expectIPv6: false, }, { ip:         net.IPv6loopback, expectIPv6: true, }, { ip:         net.IPv6zero, expectIPv6: true, }, { ip:         net.ParseIP("fd00::600d:f00d"), expectIPv6: true, }, { ip:         net.ParseIP("2001:db8::5"), expectIPv6: true, }, } for i := range testCases { isIPv6 := IsIPv6(testCases[i].ip) if isIPv6 != testCases[i].expectIPv6 { t.Errorf("[%d] Expect ipv6 %v, got %v", i+1, testCases[i].expectIPv6, isIPv6) } } } </ins>