diff --git a/go-controller/pkg/node/gateway_init_linux_test.go b/go-controller/pkg/node/gateway_init_linux_test.go index 87ea93aff7..cf883f06ac 100644 --- a/go-controller/pkg/node/gateway_init_linux_test.go +++ b/go-controller/pkg/node/gateway_init_linux_test.go @@ -1242,6 +1242,7 @@ OFPT_GET_CONFIG_REPLY (xid=0x4): frags=normal miss_send_len=0` ) expectedTables["nat"]["OVN-KUBE-UDN-MASQUERADE"] = append(expectedTables["nat"]["OVN-KUBE-UDN-MASQUERADE"], "-s 169.254.169.2/29 -j RETURN", // this guarantees we don't SNAT default network masqueradeIPs + "-d 172.16.1.0/24 -j RETURN", // this guarantees we don't SNAT traffic destined to the service cidr "-s 169.254.169.0/29 -j MASQUERADE", // this guarantees we SNAT all UDN MasqueradeIPs traffic leaving the node ) } diff --git a/go-controller/pkg/node/gateway_shared_intf.go b/go-controller/pkg/node/gateway_shared_intf.go index a29c3883c2..3506ce0814 100644 --- a/go-controller/pkg/node/gateway_shared_intf.go +++ b/go-controller/pkg/node/gateway_shared_intf.go @@ -1291,10 +1291,28 @@ func flowsForDefaultBridge(bridge *bridgeConfiguration, extraIPs []net.IP) ([]st } // table 0, Host -> OVN towards SVC, SNAT to special IP - dftFlows = append(dftFlows, - fmt.Sprintf("cookie=%s, priority=500, in_port=%s, %s, %s_dst=%s,"+ - "actions=ct(commit,zone=%d,nat(src=%s),table=2)", - defaultOpenFlowCookie, ofPortHost, protoPrefix, protoPrefix, svcCIDR, config.Default.HostMasqConntrackZone, masqIP)) + for _, netConfig := range bridge.patchedNetConfigs() { + if netConfig.masqCTMark == ctMarkOVN { + dftFlows = append(dftFlows, + fmt.Sprintf("cookie=%s, priority=500, in_port=%s, %s, %s_dst=%s,"+ + "actions=ct(commit,zone=%d,nat(src=%s),table=2)", + defaultOpenFlowCookie, ofPortHost, protoPrefix, protoPrefix, svcCIDR, config.Default.HostMasqConntrackZone, masqIP)) + } else { + //TODO: ugly + var mgmtMasqIP string + + if utilnet.IsIPv4CIDR(svcCIDR) { + mgmtMasqIP = netConfig.v4MasqIPs.ManagementPort.IP.String() + } else { + mgmtMasqIP = netConfig.v6MasqIPs.ManagementPort.IP.String() + } + //TODO (dceara): we commit on every packet + dftFlows = append(dftFlows, + fmt.Sprintf("cookie=%s, priority=600, in_port=%s, %s, %s_src=%s, %s_dst=%s,"+ + "actions=ct(commit,zone=%d,table=2)", + defaultOpenFlowCookie, ofPortHost, protoPrefix, protoPrefix, mgmtMasqIP, protoPrefix, svcCIDR, config.Default.HostMasqConntrackZone)) + } + } for _, netConfig := range bridge.patchedNetConfigs() { // table 0, Reply hairpin traffic to host, coming from OVN, unSNAT @@ -1387,9 +1405,33 @@ func flowsForDefaultBridge(bridge *bridgeConfiguration, extraIPs []net.IP) ([]st defaultNetConfig := bridge.netConfig[types.DefaultNetworkName] // table 2, dispatch from Host -> OVN dftFlows = append(dftFlows, - fmt.Sprintf("cookie=%s, table=2, "+ + fmt.Sprintf("cookie=%s, priority=100, table=2, "+ "actions=set_field:%s->eth_dst,output:%s", defaultOpenFlowCookie, bridgeMacAddress, defaultNetConfig.ofPortPatch)) + // TODO: comment about extra match on masq ip as source + if config.IPv4Mode { + for _, netConfig := range bridge.patchedNetConfigs() { + if netConfig.masqCTMark == ctMarkOVN { + continue + } + dftFlows = append(dftFlows, + fmt.Sprintf("cookie=%s, priority=200, table=2, ip, ip_src=%s, "+ + "actions=set_field:%s->eth_dst,output:%s", defaultOpenFlowCookie, netConfig.v4MasqIPs.ManagementPort.IP, bridgeMacAddress, netConfig.ofPortPatch)) + } + } + + if config.IPv6Mode { + for _, netConfig := range bridge.patchedNetConfigs() { + if netConfig.masqCTMark == ctMarkOVN { + continue + } + + dftFlows = append(dftFlows, + fmt.Sprintf("cookie=%s, priority=200, table=2, ip6, ipv6_src=%s, "+ + "actions=set_field:%s->eth_dst,output:%s", defaultOpenFlowCookie, netConfig.v6MasqIPs.ManagementPort.IP, bridgeMacAddress, netConfig.ofPortPatch)) + } + } + // table 3, dispatch from OVN -> Host dftFlows = append(dftFlows, fmt.Sprintf("cookie=%s, table=3, "+ @@ -1489,7 +1531,7 @@ func commonFlows(subnets []*net.IPNet, bridge *bridgeConfiguration) ([]string, e dftFlows = append(dftFlows, fmt.Sprintf("cookie=%s, priority=100, in_port=%s, dl_src=%s, ip, ip_src=%s, "+ "actions=ct(commit, zone=%d, nat(src=%s), exec(set_field:%s->ct_mark)), output:%s", - defaultOpenFlowCookie, netConfig.ofPortPatch, bridgeMacAddress, netConfig.v4MasqIP.IP, config.Default.ConntrackZone, + defaultOpenFlowCookie, netConfig.ofPortPatch, bridgeMacAddress, netConfig.v4MasqIPs.GatewayRouter.IP, config.Default.ConntrackZone, physicalIP.IP, netConfig.masqCTMark, ofPortPhys)) } } @@ -1564,7 +1606,7 @@ func commonFlows(subnets []*net.IPNet, bridge *bridgeConfiguration) ([]string, e dftFlows = append(dftFlows, fmt.Sprintf("cookie=%s, priority=100, in_port=%s, dl_src=%s, ipv6, ipv6_src=%s, "+ "actions=ct(commit, zone=%d, nat(src=%s), exec(set_field:%s->ct_mark)), output:%s", - defaultOpenFlowCookie, netConfig.ofPortPatch, bridgeMacAddress, netConfig.v6MasqIP.IP, config.Default.ConntrackZone, + defaultOpenFlowCookie, netConfig.ofPortPatch, bridgeMacAddress, netConfig.v6MasqIPs.GatewayRouter.IP, config.Default.ConntrackZone, physicalIP.IP, netConfig.masqCTMark, ofPortPhys)) } } diff --git a/go-controller/pkg/node/gateway_udn.go b/go-controller/pkg/node/gateway_udn.go index dafd22fc39..0416042731 100644 --- a/go-controller/pkg/node/gateway_udn.go +++ b/go-controller/pkg/node/gateway_udn.go @@ -52,10 +52,10 @@ type UserDefinedNetworkGateway struct { // masqCTMark holds the mark value for this network // which is used for egress traffic in shared gateway mode masqCTMark uint - // v4MasqIP holds the IPv4 masquerade IP for this network - v4MasqIP *net.IPNet - // v6MasqIP holds the IPv6 masquerade IP for this network - v6MasqIP *net.IPNet + // v4MasqIPs holds the IPv4 masquerade IPs for this network + v4MasqIPs *udn.MasqueradeIPs + // v6MasqIPs holds the IPv6 masquerade IPs for this network + v6MasqIPs *udn.MasqueradeIPs // stores the pointer to default network's gateway so that // we can leverage it from here to program UDN flows on breth0 // Currently we use the openflowmanager and nodeIPManager from @@ -83,7 +83,7 @@ func (b *bridgeConfiguration) getBridgePortConfigurations() ([]bridgeUDNConfigur } // addNetworkBridgeConfig adds the patchport and ctMark value for the provided netInfo into the bridge configuration cache -func (b *bridgeConfiguration) addNetworkBridgeConfig(nInfo util.NetInfo, masqCTMark uint, v4MasqIP, v6MasqIP *net.IPNet) { +func (b *bridgeConfiguration) addNetworkBridgeConfig(nInfo util.NetInfo, masqCTMark uint, v4MasqIPs, v6MasqIPs *udn.MasqueradeIPs) { b.Lock() defer b.Unlock() @@ -95,8 +95,8 @@ func (b *bridgeConfiguration) addNetworkBridgeConfig(nInfo util.NetInfo, masqCTM netConfig := &bridgeUDNConfiguration{ patchPort: patchPort, masqCTMark: fmt.Sprintf("0x%x", masqCTMark), - v4MasqIP: v4MasqIP, - v6MasqIP: v6MasqIP, + v4MasqIPs: v4MasqIPs, + v6MasqIPs: v6MasqIPs, } b.netConfig[netName] = netConfig @@ -149,8 +149,8 @@ type bridgeUDNConfiguration struct { patchPort string ofPortPatch string masqCTMark string - v4MasqIP *net.IPNet - v6MasqIP *net.IPNet + v4MasqIPs *udn.MasqueradeIPs + v6MasqIPs *udn.MasqueradeIPs } func (netConfig *bridgeUDNConfiguration) setBridgeNetworkOfPortsInternal() error { @@ -179,23 +179,22 @@ func NewUserDefinedNetworkGateway(netInfo util.NetInfo, networkID int, node *v1. defaultNetworkGateway Gateway) (*UserDefinedNetworkGateway, error) { // Generate a per network conntrack mark and masquerade IPs to be used for egress traffic. var ( - v4MasqIP *net.IPNet - v6MasqIP *net.IPNet + v4MasqIPs *udn.MasqueradeIPs + v6MasqIPs *udn.MasqueradeIPs + err error ) masqCTMark := ctMarkUDNBase + uint(networkID) if config.IPv4Mode { - v4MasqIPs, err := udn.AllocateV4MasqueradeIPs(networkID) + v4MasqIPs, err = udn.AllocateV4MasqueradeIPs(networkID) if err != nil { return nil, fmt.Errorf("failed to get v4 masquerade IP, network %s (%d): %v", netInfo.GetNetworkName(), networkID, err) } - v4MasqIP = v4MasqIPs.GatewayRouter } if config.IPv6Mode { - v6MasqIPs, err := udn.AllocateV6MasqueradeIPs(networkID) + v6MasqIPs, err = udn.AllocateV6MasqueradeIPs(networkID) if err != nil { return nil, fmt.Errorf("failed to get v6 masquerade IP, network %s (%d): %v", netInfo.GetNetworkName(), networkID, err) } - v6MasqIP = v6MasqIPs.GatewayRouter } gw, ok := defaultNetworkGateway.(*gateway) @@ -211,8 +210,8 @@ func NewUserDefinedNetworkGateway(netInfo util.NetInfo, networkID int, node *v1. kubeInterface: kubeInterface, vrfManager: vrfManager, masqCTMark: masqCTMark, - v4MasqIP: v4MasqIP, - v6MasqIP: v6MasqIP, + v4MasqIPs: v4MasqIPs, + v6MasqIPs: v6MasqIPs, gateway: gw, ruleManager: ruleManager, }, nil @@ -258,7 +257,7 @@ func (udng *UserDefinedNetworkGateway) AddNetwork() error { return fmt.Errorf("could not set loose mode for reverse path filtering on management port %s: %v", mgmtPortName, err) } if udng.openflowManager != nil { - udng.openflowManager.addNetwork(udng.NetInfo, udng.masqCTMark, udng.v4MasqIP, udng.v6MasqIP) + udng.openflowManager.addNetwork(udng.NetInfo, udng.masqCTMark, udng.v4MasqIPs, udng.v6MasqIPs) waiter := newStartupWaiterWithTimeout(waitForPatchPortTimeout) readyFunc := func() (bool, error) { diff --git a/go-controller/pkg/node/gateway_udn_test.go b/go-controller/pkg/node/gateway_udn_test.go index 29d4cc9814..9d33da3a64 100644 --- a/go-controller/pkg/node/gateway_udn_test.go +++ b/go-controller/pkg/node/gateway_udn_test.go @@ -539,7 +539,7 @@ var _ = Describe("UserDefinedNetworkGateway", func() { Expect(udnGateway.AddNetwork()).To(Succeed()) flowMap = udnGateway.gateway.openflowManager.flowCache - Expect(len(flowMap["DEFAULT"])).To(Equal(59)) // 14 UDN Flows are added by default + Expect(len(flowMap["DEFAULT"])).To(Equal(62)) // 17 UDN Flows are added by default Expect(len(udnGateway.openflowManager.defaultBridge.netConfig)).To(Equal(2)) // default network + UDN network for _, flows := range flowMap { for _, flow := range flows { @@ -711,7 +711,7 @@ var _ = Describe("UserDefinedNetworkGateway", func() { Expect(udnGateway.AddNetwork()).To(Succeed()) flowMap = udnGateway.gateway.openflowManager.flowCache - Expect(len(flowMap["DEFAULT"])).To(Equal(59)) // 14 UDN Flows are added by default + Expect(len(flowMap["DEFAULT"])).To(Equal(62)) // 14 UDN Flows are added by default Expect(len(udnGateway.openflowManager.defaultBridge.netConfig)).To(Equal(2)) // default network + UDN network for _, flows := range flowMap { for _, flow := range flows { diff --git a/go-controller/pkg/node/openflow_manager.go b/go-controller/pkg/node/openflow_manager.go index 71bb4e2db1..5875997794 100644 --- a/go-controller/pkg/node/openflow_manager.go +++ b/go-controller/pkg/node/openflow_manager.go @@ -10,6 +10,7 @@ import ( "time" "github.com/ovn-org/ovn-kubernetes/go-controller/pkg/config" + "github.com/ovn-org/ovn-kubernetes/go-controller/pkg/generator/udn" "github.com/ovn-org/ovn-kubernetes/go-controller/pkg/types" "github.com/ovn-org/ovn-kubernetes/go-controller/pkg/util" @@ -39,10 +40,10 @@ func (c *openflowManager) getExGwBridgePortConfigurations() ([]bridgeUDNConfigur return c.externalGatewayBridge.getBridgePortConfigurations() } -func (c *openflowManager) addNetwork(nInfo util.NetInfo, masqCTMark uint, v4MasqIP, v6MasqIP *net.IPNet) { - c.defaultBridge.addNetworkBridgeConfig(nInfo, masqCTMark, v4MasqIP, v6MasqIP) +func (c *openflowManager) addNetwork(nInfo util.NetInfo, masqCTMark uint, v4MasqIPs, v6MasqIPs *udn.MasqueradeIPs) { + c.defaultBridge.addNetworkBridgeConfig(nInfo, masqCTMark, v4MasqIPs, v6MasqIPs) if c.externalGatewayBridge != nil { - c.externalGatewayBridge.addNetworkBridgeConfig(nInfo, masqCTMark, v4MasqIP, v6MasqIP) + c.externalGatewayBridge.addNetworkBridgeConfig(nInfo, masqCTMark, v4MasqIPs, v6MasqIPs) } } diff --git a/test/e2e/network_segmentation_services.go b/test/e2e/network_segmentation_services.go index 6db3b05dab..5ed3952b4f 100644 --- a/test/e2e/network_segmentation_services.go +++ b/test/e2e/network_segmentation_services.go @@ -253,16 +253,13 @@ var _ = Describe("Network Segmentation: services", func() { Expect(err).NotTo(HaveOccurred()) By("Verify the UDN client connection to the default network service") - // FIXME(tssurya): https://github.com/ovn-org/ovn-kubernetes/issues/4687 - if !isLocalGWModeEnabled() { - checkNoConnectionToClusterIPs(f, udnClientPod2, defaultService) - // TODO uncomment below when below OVN_DISABLE_SNAT_MULTIPLE_GWS=true is supported - // checkConnectionToNodePort(f, udnClientPod2, defaultService, &nodes.Items[0], "server node", defaultServerPod.Name) - // TODO change line below to checkConnectionToNodePort when we have full UDN support in ovnkube-node - //checkNoConnectionToNodePort(f, udnClientPod2, defaultService, &nodes.Items[1], "local node") - // TODO uncomment below when OVN_DISABLE_SNAT_MULTIPLE_GWS=true is supported - // checkConnectionToNodePort(f, udnClientPod2, defaultService, &nodes.Items[2], "other node", defaultServerPod.Name) - } + checkNoConnectionToClusterIPs(f, udnClientPod2, defaultService) + // TODO uncomment below when below OVN_DISABLE_SNAT_MULTIPLE_GWS=true is supported + // checkConnectionToNodePort(f, udnClientPod2, defaultService, &nodes.Items[0], "server node", defaultServerPod.Name) + // TODO change line below to checkConnectionToNodePort when we have full UDN support in ovnkube-node + //checkNoConnectionToNodePort(f, udnClientPod2, defaultService, &nodes.Items[1], "local node") + // TODO uncomment below when OVN_DISABLE_SNAT_MULTIPLE_GWS=true is supported + // checkConnectionToNodePort(f, udnClientPod2, defaultService, &nodes.Items[2], "other node", defaultServerPod.Name) }, Entry(