Initial implementation of SNAT functionality.
Change-Id: I9094755c6d25a62e527976b9bf275d7c1e2a3f86
(cherry picked from commit b9a220261df1f591b75d59b646aa834c1efdb7f1)
diff --git a/apps/kubevirt-networking/app/src/main/java/org/onosproject/kubevirtnetworking/impl/KubevirtRoutingSnatHandler.java b/apps/kubevirt-networking/app/src/main/java/org/onosproject/kubevirtnetworking/impl/KubevirtRoutingSnatHandler.java
new file mode 100644
index 0000000..808a323
--- /dev/null
+++ b/apps/kubevirt-networking/app/src/main/java/org/onosproject/kubevirtnetworking/impl/KubevirtRoutingSnatHandler.java
@@ -0,0 +1,620 @@
+/*
+ * Copyright 2021-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.kubevirtnetworking.impl;
+
+import org.onlab.packet.ARP;
+import org.onlab.packet.EthType;
+import org.onlab.packet.Ethernet;
+import org.onlab.packet.Ip4Address;
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.IpPrefix;
+import org.onlab.packet.MacAddress;
+import org.onlab.packet.TpPort;
+import org.onosproject.cluster.ClusterService;
+import org.onosproject.cluster.LeadershipService;
+import org.onosproject.cluster.NodeId;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.core.CoreService;
+import org.onosproject.kubevirtnetworking.api.KubevirtFlowRuleService;
+import org.onosproject.kubevirtnetworking.api.KubevirtNetwork;
+import org.onosproject.kubevirtnetworking.api.KubevirtNetworkEvent;
+import org.onosproject.kubevirtnetworking.api.KubevirtNetworkListener;
+import org.onosproject.kubevirtnetworking.api.KubevirtNetworkService;
+import org.onosproject.kubevirtnetworking.api.KubevirtPort;
+import org.onosproject.kubevirtnetworking.api.KubevirtPortEvent;
+import org.onosproject.kubevirtnetworking.api.KubevirtPortListener;
+import org.onosproject.kubevirtnetworking.api.KubevirtPortService;
+import org.onosproject.kubevirtnetworking.api.KubevirtRouter;
+import org.onosproject.kubevirtnetworking.api.KubevirtRouterEvent;
+import org.onosproject.kubevirtnetworking.api.KubevirtRouterListener;
+import org.onosproject.kubevirtnetworking.api.KubevirtRouterService;
+import org.onosproject.kubevirtnetworking.util.RulePopulatorUtil;
+import org.onosproject.kubevirtnode.api.KubevirtNode;
+import org.onosproject.kubevirtnode.api.KubevirtNodeService;
+import org.onosproject.net.Device;
+import org.onosproject.net.Port;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.device.DeviceAdminService;
+import org.onosproject.net.driver.DriverService;
+import org.onosproject.net.flow.DefaultTrafficSelector;
+import org.onosproject.net.flow.DefaultTrafficTreatment;
+import org.onosproject.net.flow.TrafficSelector;
+import org.onosproject.net.flow.TrafficTreatment;
+import org.onosproject.net.flow.instructions.ExtensionTreatment;
+import org.osgi.service.component.annotations.Activate;
+import org.osgi.service.component.annotations.Component;
+import org.osgi.service.component.annotations.Deactivate;
+import org.osgi.service.component.annotations.Reference;
+import org.osgi.service.component.annotations.ReferenceCardinality;
+import org.slf4j.Logger;
+
+import java.util.Objects;
+import java.util.Set;
+import java.util.concurrent.ExecutorService;
+
+import static java.util.concurrent.Executors.newSingleThreadExecutor;
+import static org.onlab.util.Tools.groupedThreads;
+import static org.onosproject.kubevirtnetworking.api.Constants.DEFAULT_GATEWAY_MAC;
+import static org.onosproject.kubevirtnetworking.api.Constants.FLAT_TABLE;
+import static org.onosproject.kubevirtnetworking.api.Constants.FORWARDING_TABLE;
+import static org.onosproject.kubevirtnetworking.api.Constants.KUBEVIRT_NETWORKING_APP_ID;
+import static org.onosproject.kubevirtnetworking.api.Constants.PRE_FLAT_TABLE;
+import static org.onosproject.kubevirtnetworking.api.Constants.PRIORITY_ARP_GATEWAY_RULE;
+import static org.onosproject.kubevirtnetworking.api.Constants.PRIORITY_STATEFUL_SNAT_RULE;
+import static org.onosproject.kubevirtnetworking.util.KubevirtNetworkingUtil.gatewayNodeForSpecifiedRouter;
+import static org.onosproject.kubevirtnetworking.util.KubevirtNetworkingUtil.getRouterSnatIpAddress;
+import static org.onosproject.kubevirtnetworking.util.KubevirtNetworkingUtil.getbrIntMacAddress;
+import static org.onosproject.kubevirtnetworking.util.RulePopulatorUtil.CT_NAT_SRC_FLAG;
+import static org.onosproject.net.AnnotationKeys.PORT_NAME;
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Handles kubevirt routing snat.
+ */
+
+@Component(immediate = true)
+public class KubevirtRoutingSnatHandler {
+ protected final Logger log = getLogger(getClass());
+ private static final int DEFAULT_TTL = 0xff;
+
+ private static final int TP_PORT_MINIMUM_NUM = 1025;
+ private static final int TP_PORT_MAXIMUM_NUM = 65535;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY)
+ protected CoreService coreService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY)
+ protected ClusterService clusterService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY)
+ protected LeadershipService leadershipService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY)
+ protected DeviceAdminService deviceService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY)
+ protected KubevirtPortService kubevirtPortService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY)
+ protected KubevirtNodeService kubevirtNodeService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY)
+ protected KubevirtNetworkService kubevirtNetworkService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY)
+ protected KubevirtFlowRuleService flowService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY)
+ protected DriverService driverService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY)
+ protected KubevirtRouterService kubevirtRouterService;
+
+ private final ExecutorService eventExecutor = newSingleThreadExecutor(
+ groupedThreads(this.getClass().getSimpleName(), "event-handler"));
+
+ private final InternalKubevirtPortListener kubevirtPortListener =
+ new InternalKubevirtPortListener();
+
+ private final InternalRouterEventListener kubevirtRouterlistener =
+ new InternalRouterEventListener();
+
+ private final InternalNetworkEventListener kubevirtNetworkEventListener =
+ new InternalNetworkEventListener();
+
+ private ApplicationId appId;
+ private NodeId localNodeId;
+
+ @Activate
+ protected void activate() {
+ appId = coreService.registerApplication(KUBEVIRT_NETWORKING_APP_ID);
+ localNodeId = clusterService.getLocalNode().id();
+ leadershipService.runForLeadership(appId.name());
+
+ kubevirtPortService.addListener(kubevirtPortListener);
+ kubevirtRouterService.addListener(kubevirtRouterlistener);
+ kubevirtNetworkService.addListener(kubevirtNetworkEventListener);
+
+ log.info("Started");
+ }
+
+ @Deactivate
+ protected void deactivate() {
+ leadershipService.withdraw(appId.name());
+ kubevirtPortService.removeListener(kubevirtPortListener);
+ kubevirtRouterService.removeListener(kubevirtRouterlistener);
+ kubevirtNetworkService.removeListener(kubevirtNetworkEventListener);
+
+ eventExecutor.shutdown();
+
+ log.info("Stopped");
+ }
+
+ private void initGatewayNodeSnatForRouter(KubevirtRouter router, boolean install) {
+ KubevirtNode electedGw = gatewayNodeForSpecifiedRouter(kubevirtNodeService, router);
+
+ if (electedGw == null) {
+ log.warn("Fail to initialize gateway node snat for router {} " +
+ "there's no gateway assigned to it", router.name());
+ return;
+ }
+
+ String routerSnatIp = router.external().keySet().stream().findAny().orElse(null);
+
+ if (routerSnatIp == null) {
+ log.warn("Fail to initialize gateway node snat for router {} " +
+ "there's no gateway snat ip assigned to it", router.name());
+ return;
+ }
+
+ setArpResponseToPeerRouter(electedGw, Ip4Address.valueOf(routerSnatIp), install);
+ setStatefulSnatUpstreamRules(electedGw, router, Ip4Address.valueOf(routerSnatIp), install);
+ setStatefulSnatDownstreamRuleForRouter(router, electedGw, Ip4Address.valueOf(routerSnatIp), install);
+ }
+
+ private void setArpResponseToPeerRouter(KubevirtNode gatewayNode, Ip4Address ip4Address, boolean install) {
+
+ TrafficSelector selector = DefaultTrafficSelector.builder()
+ .matchInPort(externalPatchPortNum(gatewayNode))
+ .matchEthType(EthType.EtherType.ARP.ethType().toShort())
+ .matchArpOp(ARP.OP_REQUEST)
+ .matchArpTpa(ip4Address)
+ .build();
+
+ Device device = deviceService.getDevice(gatewayNode.intgBridge());
+
+ TrafficTreatment treatment = DefaultTrafficTreatment.builder()
+ .extension(RulePopulatorUtil.buildMoveEthSrcToDstExtension(device), device.id())
+ .extension(RulePopulatorUtil.buildMoveArpShaToThaExtension(device), device.id())
+ .extension(RulePopulatorUtil.buildMoveArpSpaToTpaExtension(device), device.id())
+ .setArpOp(ARP.OP_REPLY)
+ .setEthSrc(DEFAULT_GATEWAY_MAC)
+ .setArpSha(DEFAULT_GATEWAY_MAC)
+ .setArpSpa(ip4Address)
+ .setOutput(PortNumber.IN_PORT)
+ .build();
+
+ flowService.setRule(
+ appId,
+ gatewayNode.intgBridge(),
+ selector,
+ treatment,
+ PRIORITY_ARP_GATEWAY_RULE,
+ PRE_FLAT_TABLE,
+ install);
+ }
+
+ private void setStatefulSnatUpstreamRules(KubevirtNode gatewayNode, KubevirtRouter router,
+ Ip4Address ip4Address, boolean install) {
+
+ MacAddress brIntMacAddress = getbrIntMacAddress(deviceService, gatewayNode.intgBridge());
+
+ TrafficSelector selector = DefaultTrafficSelector.builder()
+ .matchEthType(Ethernet.TYPE_IPV4)
+ .matchEthDst(brIntMacAddress)
+ .build();
+
+ TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
+
+ ExtensionTreatment natTreatment = RulePopulatorUtil
+ .niciraConnTrackTreatmentBuilder(driverService, gatewayNode.intgBridge())
+ .commit(true)
+ .natFlag(CT_NAT_SRC_FLAG)
+ .natAction(true)
+ .natIp(ip4Address)
+ .natPortMin(TpPort.tpPort(TP_PORT_MINIMUM_NUM))
+ .natPortMax(TpPort.tpPort(TP_PORT_MAXIMUM_NUM))
+ .build();
+
+ tBuilder.extension(natTreatment, gatewayNode.intgBridge())
+ .setEthDst(router.peerRouter().macAddress())
+ .setEthSrc(DEFAULT_GATEWAY_MAC)
+ .setOutput(externalPatchPortNum(gatewayNode));
+
+ flowService.setRule(
+ appId,
+ gatewayNode.intgBridge(),
+ selector,
+ tBuilder.build(),
+ PRIORITY_STATEFUL_SNAT_RULE,
+ PRE_FLAT_TABLE,
+ install);
+ }
+
+ private void setStatefulSnatDownStreamRuleForNetwork(KubevirtNode gatewayNode,
+ KubevirtRouter router,
+ KubevirtNetwork network,
+ boolean install) {
+ kubevirtPortService.ports(network.networkId()).forEach(kubevirtPort -> {
+ String routerSnatIp = router.external().keySet().stream().findAny().orElse(null);
+ if (routerSnatIp == null) {
+ return;
+ }
+ setStatefulSnatDownStreamRuleForKubevirtPort(gatewayNode, IpAddress.valueOf(routerSnatIp),
+ kubevirtPort, install);
+ });
+ }
+
+ private void setStatefulSnatDownStreamRuleForKubevirtPort(KubevirtNode gatewayNode,
+ IpAddress gatewaySnatIp,
+ KubevirtPort kubevirtPort,
+ boolean install) {
+ MacAddress brIntMacAddress = getbrIntMacAddress(deviceService, gatewayNode.intgBridge());
+
+ if (brIntMacAddress == null) {
+ log.error("Failed to set stateful snat downstream rule because " +
+ "there's no br-int port for device {}", gatewayNode.intgBridge());
+ return;
+ }
+
+ TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder()
+ .matchEthType(Ethernet.TYPE_IPV4)
+ .matchIPDst(IpPrefix.valueOf(kubevirtPort.ipAddress(), 32));
+
+ TrafficTreatment treatment = DefaultTrafficTreatment.builder()
+ .setEthDst(kubevirtPort.macAddress())
+ .transition(FORWARDING_TABLE)
+ .build();
+
+ flowService.setRule(
+ appId,
+ gatewayNode.intgBridge(),
+ sBuilder.build(),
+ treatment,
+ PRIORITY_STATEFUL_SNAT_RULE,
+ FLAT_TABLE,
+ install);
+ }
+
+ private void setStatefulSnatDownstreamRuleForRouter(KubevirtRouter router,
+ KubevirtNode gatewayNode,
+ IpAddress gatewaySnatIp,
+ boolean install) {
+
+ MacAddress brIntMacAddress = getbrIntMacAddress(deviceService, gatewayNode.intgBridge());
+
+ if (brIntMacAddress == null) {
+ log.error("Failed to set stateful snat downstream rule because " +
+ "there's no br-int port for device {}", gatewayNode.intgBridge());
+ return;
+ }
+
+ TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder()
+ .matchEthType(Ethernet.TYPE_IPV4)
+ .matchIPDst(IpPrefix.valueOf(gatewaySnatIp, 32));
+
+ ExtensionTreatment natTreatment = RulePopulatorUtil
+ .niciraConnTrackTreatmentBuilder(driverService, gatewayNode.intgBridge())
+ .commit(false)
+ .natAction(true)
+ .table((short) PRE_FLAT_TABLE)
+ .build();
+
+ TrafficTreatment treatment = DefaultTrafficTreatment.builder()
+ .setEthSrc(brIntMacAddress)
+ .extension(natTreatment, gatewayNode.intgBridge())
+ .build();
+
+ flowService.setRule(
+ appId,
+ gatewayNode.intgBridge(),
+ sBuilder.build(),
+ treatment,
+ PRIORITY_STATEFUL_SNAT_RULE,
+ PRE_FLAT_TABLE,
+ install);
+
+ router.internal().forEach(networkName -> {
+ KubevirtNetwork network = kubevirtNetworkService.network(networkName);
+
+ if (network != null) {
+ setStatefulSnatDownStreamRuleForNetwork(gatewayNode, router, network, install);
+ }
+ });
+ }
+
+ private PortNumber externalPatchPortNum(KubevirtNode gatewayNode) {
+ Port port = deviceService.getPorts(gatewayNode.intgBridge()).stream()
+ .filter(p -> p.isEnabled() &&
+ Objects.equals(p.annotations().value(PORT_NAME), "int-to-gateway"))
+ .findAny().orElse(null);
+
+ return port != null ? port.number() : null;
+ }
+
+ private class InternalRouterEventListener implements KubevirtRouterListener {
+ private boolean isRelevantHelper() {
+ return Objects.equals(localNodeId, leadershipService.getLeader(appId.name()));
+ }
+
+ @Override
+ public void event(KubevirtRouterEvent event) {
+ switch (event.type()) {
+ case KUBEVIRT_ROUTER_CREATED:
+ eventExecutor.execute(() -> processRouterCreation(event.subject()));
+ break;
+ case KUBEVIRT_ROUTER_REMOVED:
+ eventExecutor.execute(() -> processRouterDeletion(event.subject()));
+ break;
+ case KUBEVIRT_ROUTER_UPDATED:
+ eventExecutor.execute(() -> processRouterUpdate(event.subject()));
+ break;
+ case KUBEVIRT_ROUTER_INTERNAL_NETWORKS_ATTACHED:
+ eventExecutor.execute(() -> processRouterInternalNetworksAttached(event.subject(),
+ event.internal()));
+ break;
+ case KUBEVIRT_ROUTER_INTERNAL_NETWORKS_DETACHED:
+ eventExecutor.execute(() -> processRouterInternalNetworksDetached(event.subject(),
+ event.internal()));
+ break;
+ default:
+ //do nothing
+ break;
+ }
+ }
+ private void processRouterInternalNetworksAttached(KubevirtRouter router,
+ Set<String> attachedInternalNetworks) {
+ if (!isRelevantHelper()) {
+ return;
+ }
+
+ KubevirtNode gwNode = gatewayNodeForSpecifiedRouter(kubevirtNodeService, router);
+ if (gwNode == null) {
+ return;
+ }
+
+ attachedInternalNetworks.forEach(networkId -> {
+ kubevirtPortService.ports(networkId).forEach(kubevirtPort -> {
+ String routerSnatIp = router.external().keySet().stream().findAny().orElse(null);
+ if (routerSnatIp == null) {
+ return;
+ }
+ setStatefulSnatDownStreamRuleForKubevirtPort(gwNode, IpAddress.valueOf(routerSnatIp),
+ kubevirtPort, true);
+ });
+ });
+ }
+
+ private void processRouterInternalNetworksDetached(KubevirtRouter router,
+ Set<String> detachedInternalNetworks) {
+ if (!isRelevantHelper()) {
+ return;
+ }
+
+ KubevirtNode gwNode = gatewayNodeForSpecifiedRouter(kubevirtNodeService, router);
+ if (gwNode == null) {
+ return;
+ }
+
+ detachedInternalNetworks.forEach(networkId -> {
+ kubevirtPortService.ports(networkId).forEach(kubevirtPort -> {
+ String routerSnatIp = router.external().keySet().stream().findAny().orElse(null);
+ if (routerSnatIp == null) {
+ log.info("snatIp is null");
+ return;
+ }
+ setStatefulSnatDownStreamRuleForKubevirtPort(gwNode, IpAddress.valueOf(routerSnatIp),
+ kubevirtPort, false);
+ });
+ });
+ }
+ private void processRouterCreation(KubevirtRouter router) {
+ if (!isRelevantHelper()) {
+ return;
+ }
+ if (router.enableSnat() && !router.external().isEmpty() && router.peerRouter() != null) {
+ initGatewayNodeSnatForRouter(router, true);
+ }
+ }
+
+ private void processRouterDeletion(KubevirtRouter router) {
+ if (!isRelevantHelper()) {
+ return;
+ }
+ if (router.enableSnat() && !router.external().isEmpty() && router.peerRouter() != null) {
+ initGatewayNodeSnatForRouter(router, false);
+ }
+ }
+
+ private void processRouterUpdate(KubevirtRouter router) {
+ if (!isRelevantHelper()) {
+ return;
+ }
+ if (router.enableSnat() && !router.external().isEmpty() && router.peerRouter() != null) {
+ initGatewayNodeSnatForRouter(router, true);
+ }
+ }
+ }
+
+ private class InternalNetworkEventListener implements KubevirtNetworkListener {
+
+ private boolean isRelevantHelper() {
+ return Objects.equals(localNodeId, leadershipService.getLeader(appId.name()));
+ }
+
+ @Override
+ public void event(KubevirtNetworkEvent event) {
+ switch (event.type()) {
+ case KUBEVIRT_NETWORK_CREATED:
+ eventExecutor.execute(() -> processNetworkCreation(event.subject()));
+ break;
+ case KUBEVIRT_NETWORK_REMOVED:
+ eventExecutor.execute(() -> processNetworkRemoval(event.subject()));
+ break;
+ case KUBEVIRT_NETWORK_UPDATED:
+ default:
+ // do nothing
+ break;
+ }
+ }
+
+ private void processNetworkCreation(KubevirtNetwork network) {
+ if (!isRelevantHelper()) {
+ return;
+ }
+
+ switch (network.type()) {
+ case VXLAN:
+ case GRE:
+ case GENEVE:
+ break;
+ case FLAT:
+ case VLAN:
+ break;
+ default:
+ // do nothing
+ break;
+ }
+ }
+
+ private void processNetworkRemoval(KubevirtNetwork network) {
+ if (!isRelevantHelper()) {
+ return;
+ }
+
+ switch (network.type()) {
+ case VXLAN:
+ case GRE:
+ case GENEVE:
+ break;
+ case FLAT:
+ case VLAN:
+ break;
+ default:
+ // do nothing
+ break;
+ }
+ }
+ }
+
+ private class InternalKubevirtPortListener implements KubevirtPortListener {
+
+ private boolean isRelevantHelper() {
+ return Objects.equals(localNodeId, leadershipService.getLeader(appId.name()));
+ }
+
+ @Override
+ public void event(KubevirtPortEvent event) {
+ switch (event.type()) {
+ case KUBEVIRT_PORT_CREATED:
+ eventExecutor.execute(() -> processPortCreation(event.subject()));
+ break;
+ case KUBEVIRT_PORT_UPDATED:
+ eventExecutor.execute(() -> processPortUpdate(event.subject()));
+ break;
+ case KUBEVIRT_PORT_REMOVED:
+ eventExecutor.execute(() -> processPortDeletion(event.subject()));
+ break;
+ default:
+ //do nothing
+ break;
+ }
+ }
+
+ private void processPortCreation(KubevirtPort kubevirtPort) {
+ if (!isRelevantHelper()) {
+ return;
+ }
+
+ KubevirtRouter router = routerForKubevirtPort(kubevirtPort);
+ if (router == null) {
+ return;
+ }
+
+ KubevirtNode gwNode = gatewayNodeForSpecifiedRouter(kubevirtNodeService, router);
+
+ if (gwNode != null) {
+ IpAddress gatewaySnatIp = getRouterSnatIpAddress(kubevirtRouterService, kubevirtPort.networkId());
+ if (gatewaySnatIp == null) {
+ return;
+ }
+ setStatefulSnatDownStreamRuleForKubevirtPort(gwNode, gatewaySnatIp, kubevirtPort, true);
+ }
+ }
+
+ private void processPortUpdate(KubevirtPort kubevirtPort) {
+ if (!isRelevantHelper()) {
+ return;
+ }
+
+ KubevirtRouter router = routerForKubevirtPort(kubevirtPort);
+ if (router == null) {
+ return;
+ }
+
+ KubevirtNode gwNode = gatewayNodeForSpecifiedRouter(kubevirtNodeService, router);
+
+ if (gwNode != null) {
+ IpAddress gatewaySnatIp = getRouterSnatIpAddress(kubevirtRouterService, kubevirtPort.networkId());
+ if (gatewaySnatIp == null) {
+ return;
+ }
+ setStatefulSnatDownStreamRuleForKubevirtPort(gwNode, gatewaySnatIp, kubevirtPort, true);
+ }
+ }
+
+ private void processPortDeletion(KubevirtPort kubevirtPort) {
+ if (!isRelevantHelper()) {
+ return;
+ }
+
+ KubevirtRouter router = routerForKubevirtPort(kubevirtPort);
+ if (router == null) {
+ return;
+ }
+
+ KubevirtNode gwNode = gatewayNodeForSpecifiedRouter(kubevirtNodeService, router);
+
+ if (gwNode != null) {
+ IpAddress gatewaySnatIp = getRouterSnatIpAddress(kubevirtRouterService, kubevirtPort.networkId());
+ if (gatewaySnatIp == null) {
+ return;
+ }
+ setStatefulSnatDownStreamRuleForKubevirtPort(gwNode, gatewaySnatIp, kubevirtPort, false);
+ }
+ }
+
+ private KubevirtRouter routerForKubevirtPort(KubevirtPort kubevirtPort) {
+ if (kubevirtPort.ipAddress() != null) {
+ return kubevirtRouterService.routers().stream()
+ .filter(r -> r.internal().contains(kubevirtPort.networkId()))
+ .findAny().orElse(null);
+ }
+
+ return null;
+ }
+ }
+}