From 1cd6dfd7f433e291fb9749c51741fbde90382807 Mon Sep 17 00:00:00 2001 From: Peng Liu Date: Thu, 29 Aug 2024 13:23:56 +0000 Subject: [PATCH] Add static route to the hairpin masquerade IPs to pod When users attach pod to a secondary network and override the default route pod. It will cause the assymetric routing for service haripin traffic. We add static routes to ensure the traffic to the hairpin masquerade IP always goes to OVN. Signed-off-by: Peng Liu --- .../pkg/allocator/pod/pod_annotation_test.go | 35 +++++++++++++++++ go-controller/pkg/ovn/pods_test.go | 7 ++++ go-controller/pkg/util/pod_annotation.go | 39 ++++++++++++++----- 3 files changed, 72 insertions(+), 9 deletions(-) diff --git a/go-controller/pkg/allocator/pod/pod_annotation_test.go b/go-controller/pkg/allocator/pod/pod_annotation_test.go index 8929cd1efd..5dfcd58e0f 100644 --- a/go-controller/pkg/allocator/pod/pod_annotation_test.go +++ b/go-controller/pkg/allocator/pod/pod_annotation_test.go @@ -183,6 +183,13 @@ func Test_allocatePodAnnotationWithRollback(t *testing.T) { MAC: util.IPAddrToHWAddr(ovntest.MustParseIPNets("192.168.0.3/24")[0].IP), Gateways: []net.IP{ovntest.MustParseIP("192.168.0.1").To4()}, Routes: []util.PodRoute{ + { + Dest: &net.IPNet{ + IP: ovntest.MustParseIP("169.254.169.5"), + Mask: net.CIDRMask(32, 32), + }, + NextHop: ovntest.MustParseIP("192.168.0.1").To4(), + }, { Dest: ovntest.MustParseIPNet("100.64.0.0/16"), NextHop: ovntest.MustParseIP("192.168.0.1").To4(), @@ -264,6 +271,13 @@ func Test_allocatePodAnnotationWithRollback(t *testing.T) { MAC: util.IPAddrToHWAddr(ovntest.MustParseIPNets("192.168.0.4/24")[0].IP), Gateways: []net.IP{ovntest.MustParseIP("192.168.0.1").To4()}, Routes: []util.PodRoute{ + { + Dest: &net.IPNet{ + IP: ovntest.MustParseIP("169.254.169.5"), + Mask: net.CIDRMask(32, 32), + }, + NextHop: ovntest.MustParseIP("192.168.0.1").To4(), + }, { Dest: ovntest.MustParseIPNet("100.64.0.0/16"), NextHop: ovntest.MustParseIP("192.168.0.1").To4(), @@ -293,6 +307,13 @@ func Test_allocatePodAnnotationWithRollback(t *testing.T) { MAC: util.IPAddrToHWAddr(ovntest.MustParseIPNets("192.168.0.4/24")[0].IP), Gateways: []net.IP{ovntest.MustParseIP("192.168.0.1").To4()}, Routes: []util.PodRoute{ + { + Dest: &net.IPNet{ + IP: ovntest.MustParseIP("169.254.169.5"), + Mask: net.CIDRMask(32, 32), + }, + NextHop: ovntest.MustParseIP("192.168.0.1").To4(), + }, { Dest: ovntest.MustParseIPNet("100.64.0.0/16"), NextHop: ovntest.MustParseIP("192.168.0.1").To4(), @@ -321,6 +342,13 @@ func Test_allocatePodAnnotationWithRollback(t *testing.T) { MAC: util.IPAddrToHWAddr(ovntest.MustParseIPNets("192.168.0.3/24")[0].IP), Gateways: []net.IP{ovntest.MustParseIP("192.168.0.1").To4()}, Routes: []util.PodRoute{ + { + Dest: &net.IPNet{ + IP: ovntest.MustParseIP("169.254.169.5"), + Mask: net.CIDRMask(32, 32), + }, + NextHop: ovntest.MustParseIP("192.168.0.1").To4(), + }, { Dest: ovntest.MustParseIPNet("100.64.0.0/16"), NextHop: ovntest.MustParseIP("192.168.0.1").To4(), @@ -377,6 +405,13 @@ func Test_allocatePodAnnotationWithRollback(t *testing.T) { MAC: requestedMACParsed, Gateways: []net.IP{ovntest.MustParseIP("192.168.0.1").To4()}, Routes: []util.PodRoute{ + { + Dest: &net.IPNet{ + IP: ovntest.MustParseIP("169.254.169.5"), + Mask: net.CIDRMask(32, 32), + }, + NextHop: ovntest.MustParseIP("192.168.0.1").To4(), + }, { Dest: ovntest.MustParseIPNet("100.64.0.0/16"), NextHop: ovntest.MustParseIP("192.168.0.1").To4(), diff --git a/go-controller/pkg/ovn/pods_test.go b/go-controller/pkg/ovn/pods_test.go index 6246bde376..2409d71cba 100644 --- a/go-controller/pkg/ovn/pods_test.go +++ b/go-controller/pkg/ovn/pods_test.go @@ -250,6 +250,13 @@ func newTPod(nodeName, nodeSubnet, nodeMgtIP, nodeGWIP, podName, podIPs, podMAC, routeSources = append(routeSources, sc) } } + hairpinMasqueradeIP := config.Gateway.MasqueradeIPs.V4OVNServiceHairpinMasqueradeIP.String() + mask := 32 + if isIPv6 { + hairpinMasqueradeIP = config.Gateway.MasqueradeIPs.V6OVNServiceHairpinMasqueradeIP.String() + mask = 128 + } + routeSources = append(routeSources, ovntest.MustParseIPNet(fmt.Sprintf("%s/%d", hairpinMasqueradeIP, mask))) joinNet := config.Gateway.V4JoinSubnet if isIPv6 { joinNet = config.Gateway.V6JoinSubnet diff --git a/go-controller/pkg/util/pod_annotation.go b/go-controller/pkg/util/pod_annotation.go index a396942645..91caa23070 100644 --- a/go-controller/pkg/util/pod_annotation.go +++ b/go-controller/pkg/util/pod_annotation.go @@ -441,6 +441,33 @@ func joinSubnetToRoute(isIPv6 bool, gatewayIP net.IP) PodRoute { } } +func serviceCIDRToRoute(isIPv6 bool, gatewayIP net.IP) []PodRoute { + var podRoutes []PodRoute + for _, serviceSubnet := range config.Kubernetes.ServiceCIDRs { + if isIPv6 == utilnet.IsIPv6CIDR(serviceSubnet) { + podRoutes = append(podRoutes, PodRoute{ + Dest: serviceSubnet, + NextHop: gatewayIP, + }) + } + } + return podRoutes +} + +func hairpinMasqueradeIPToRoute(isIPv6 bool, gatewayIP net.IP) PodRoute { + ip := config.Gateway.MasqueradeIPs.V4OVNServiceHairpinMasqueradeIP + if isIPv6 { + ip = config.Gateway.MasqueradeIPs.V6OVNServiceHairpinMasqueradeIP + } + return PodRoute{ + Dest: &net.IPNet{ + IP: ip, + Mask: GetIPFullMask(ip), + }, + NextHop: gatewayIP, + } +} + // addRoutesGatewayIP updates the provided pod annotation for the provided pod // with the gateways derived from the allocated IPs func AddRoutesGatewayIP( @@ -523,15 +550,9 @@ func AddRoutesGatewayIP( } // Ensure default service network traffic always goes to OVN - for _, serviceSubnet := range config.Kubernetes.ServiceCIDRs { - if isIPv6 == utilnet.IsIPv6CIDR(serviceSubnet) { - podAnnotation.Routes = append(podAnnotation.Routes, PodRoute{ - Dest: serviceSubnet, - NextHop: gatewayIPnet.IP, - }) - } - } - + podAnnotation.Routes = append(podAnnotation.Routes, serviceCIDRToRoute(isIPv6, gatewayIPnet.IP)...) + // Ensure service hairpin masquerade traffic always goes to OVN + podAnnotation.Routes = append(podAnnotation.Routes, hairpinMasqueradeIPToRoute(isIPv6, gatewayIPnet.IP)) otherDefaultRoute := otherDefaultRouteV4 if isIPv6 { otherDefaultRoute = otherDefaultRouteV6