From 6cdf7ecdff5ea9017c09ca8b35acc3de944a6eff 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 | 17 ++++++++- 3 files changed, 58 insertions(+), 1 deletion(-) diff --git a/go-controller/pkg/allocator/pod/pod_annotation_test.go b/go-controller/pkg/allocator/pod/pod_annotation_test.go index 58fdc76873c..334ece75733 100644 --- a/go-controller/pkg/allocator/pod/pod_annotation_test.go +++ b/go-controller/pkg/allocator/pod/pod_annotation_test.go @@ -218,6 +218,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(), @@ -301,6 +308,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(), @@ -332,6 +346,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(), @@ -362,6 +383,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(), @@ -420,6 +448,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 860da8ffebd..2540ef194ff 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 6d44a58fa33..12583871b96 100644 --- a/go-controller/pkg/util/pod_annotation.go +++ b/go-controller/pkg/util/pod_annotation.go @@ -467,6 +467,20 @@ func serviceCIDRToRoute(isIPv6 bool, gatewayIP net.IP) []PodRoute { 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( @@ -581,7 +595,8 @@ func AddRoutesGatewayIP( if podAnnotation.Role == types.NetworkRolePrimary { // Ensure default service network traffic always goes to OVN 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