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/cli/KubevirtListRouterCommand.java b/apps/kubevirt-networking/app/src/main/java/org/onosproject/kubevirtnetworking/cli/KubevirtListRouterCommand.java
index 772b841..5c59e0e 100644
--- a/apps/kubevirt-networking/app/src/main/java/org/onosproject/kubevirtnetworking/cli/KubevirtListRouterCommand.java
+++ b/apps/kubevirt-networking/app/src/main/java/org/onosproject/kubevirtnetworking/cli/KubevirtListRouterCommand.java
@@ -53,12 +53,12 @@
routers.sort(Comparator.comparing(KubevirtRouter::name));
String format = genFormatString(ImmutableList.of(CLI_NAME_LENGTH,
- CLI_FLAG_LENGTH, CLI_IP_ADDRESSES_LENGTH, CLI_IP_ADDRESS_LENGTH));
+ CLI_FLAG_LENGTH, CLI_IP_ADDRESSES_LENGTH, CLI_IP_ADDRESS_LENGTH, CLI_NAME_LENGTH));
if (outputJson()) {
print("%s", json(routers));
} else {
- print(format, "Name", "SNAT", "Internal", "External");
+ print(format, "Name", "SNAT", "Internal", "External", "GatewayNode");
for (KubevirtRouter router : routers) {
Set<String> internalCidrs = router.internal();
@@ -66,6 +66,7 @@
String internal = internalCidrs.size() == 0 ? "[]" : internalCidrs.toString();
String external = externalIps.size() == 0 ? "[]" : externalIps.toString();
+ String gwNode = router.electedGateway() == null ? "N/A" : router.electedGateway();
print(format, StringUtils.substring(router.name(), 0,
CLI_NAME_LENGTH - CLI_MARGIN_LENGTH),
@@ -74,7 +75,9 @@
StringUtils.substring(internal, 0,
CLI_IP_ADDRESSES_LENGTH - CLI_MARGIN_LENGTH),
StringUtils.substring(external, 0,
- CLI_IP_ADDRESS_LENGTH - CLI_MARGIN_LENGTH)
+ CLI_IP_ADDRESS_LENGTH - CLI_MARGIN_LENGTH),
+ StringUtils.substring(gwNode, 0,
+ CLI_NAME_LENGTH - CLI_MARGIN_LENGTH)
);
}
}
diff --git a/apps/kubevirt-networking/app/src/main/java/org/onosproject/kubevirtnetworking/codec/KubevirtRouterCodec.java b/apps/kubevirt-networking/app/src/main/java/org/onosproject/kubevirtnetworking/codec/KubevirtRouterCodec.java
index d8529f6..d1239de 100644
--- a/apps/kubevirt-networking/app/src/main/java/org/onosproject/kubevirtnetworking/codec/KubevirtRouterCodec.java
+++ b/apps/kubevirt-networking/app/src/main/java/org/onosproject/kubevirtnetworking/codec/KubevirtRouterCodec.java
@@ -52,6 +52,7 @@
private static final String IP_ADDRESS = "ip";
private static final String MAC_ADDRESS = "mac";
private static final String NETWORK = "network";
+ private static final String GATEWAY = "gateway";
private static final String MISSING_MESSAGE = " is required in KubevirtRouter";
@@ -96,6 +97,10 @@
result.set(PEER_ROUTER, peerRouter);
}
+ if (router.electedGateway() != null) {
+ result.put(GATEWAY, router.electedGateway());
+ }
+
return result;
}
@@ -120,6 +125,10 @@
if (enableSnatJson != null) {
builder.enableSnat(enableSnatJson.asBoolean());
}
+ JsonNode electedGwJson = json.get(GATEWAY);
+ if (electedGwJson != null) {
+ builder.electedGateway(electedGwJson.asText());
+ }
ArrayNode internalJson = (ArrayNode) json.get(INTERNAL);
Set<String> internal = new HashSet<>();
diff --git a/apps/kubevirt-networking/app/src/main/java/org/onosproject/kubevirtnetworking/impl/DistributedKubevirtRouterStore.java b/apps/kubevirt-networking/app/src/main/java/org/onosproject/kubevirtnetworking/impl/DistributedKubevirtRouterStore.java
index c11fb2d..93d7cc3 100644
--- a/apps/kubevirt-networking/app/src/main/java/org/onosproject/kubevirtnetworking/impl/DistributedKubevirtRouterStore.java
+++ b/apps/kubevirt-networking/app/src/main/java/org/onosproject/kubevirtnetworking/impl/DistributedKubevirtRouterStore.java
@@ -45,6 +45,7 @@
import java.util.Collection;
import java.util.HashSet;
+import java.util.Objects;
import java.util.Set;
import java.util.concurrent.ExecutorService;
@@ -56,6 +57,9 @@
import static org.onosproject.kubevirtnetworking.api.KubevirtRouterEvent.Type.KUBEVIRT_FLOATING_IP_DISASSOCIATED;
import static org.onosproject.kubevirtnetworking.api.KubevirtRouterEvent.Type.KUBEVIRT_FLOATING_IP_REMOVED;
import static org.onosproject.kubevirtnetworking.api.KubevirtRouterEvent.Type.KUBEVIRT_FLOATING_IP_UPDATED;
+import static org.onosproject.kubevirtnetworking.api.KubevirtRouterEvent.Type.KUBEVIRT_GATEWAY_NODE_ATTACHED;
+import static org.onosproject.kubevirtnetworking.api.KubevirtRouterEvent.Type.KUBEVIRT_GATEWAY_NODE_CHANGED;
+import static org.onosproject.kubevirtnetworking.api.KubevirtRouterEvent.Type.KUBEVIRT_GATEWAY_NODE_DETACHED;
import static org.onosproject.kubevirtnetworking.api.KubevirtRouterEvent.Type.KUBEVIRT_ROUTER_CREATED;
import static org.onosproject.kubevirtnetworking.api.KubevirtRouterEvent.Type.KUBEVIRT_ROUTER_EXTERNAL_NETWORK_ATTACHED;
import static org.onosproject.kubevirtnetworking.api.KubevirtRouterEvent.Type.KUBEVIRT_ROUTER_EXTERNAL_NETWORK_DETACHED;
@@ -297,6 +301,27 @@
KUBEVIRT_ROUTER_INTERNAL_NETWORKS_DETACHED,
router, removed));
}
+ if (oldValue.electedGateway() == null
+ && newValue.electedGateway() != null) {
+ notifyDelegate(new KubevirtRouterEvent(
+ KUBEVIRT_GATEWAY_NODE_ATTACHED,
+ router, newValue.electedGateway()));
+ }
+
+ if (oldValue.electedGateway() != null
+ && newValue.electedGateway() == null) {
+ notifyDelegate(new KubevirtRouterEvent(
+ KUBEVIRT_GATEWAY_NODE_DETACHED,
+ router, oldValue.electedGateway()));
+ }
+
+ if (oldValue.electedGateway() != null
+ && newValue.electedGateway() != null
+ && !Objects.equals(oldValue.electedGateway(), newValue.electedGateway())) {
+ notifyDelegate(new KubevirtRouterEvent(
+ KUBEVIRT_GATEWAY_NODE_CHANGED,
+ router, oldValue.electedGateway()));
+ }
}
}
diff --git a/apps/kubevirt-networking/app/src/main/java/org/onosproject/kubevirtnetworking/impl/KubevirtFlowRuleManager.java b/apps/kubevirt-networking/app/src/main/java/org/onosproject/kubevirtnetworking/impl/KubevirtFlowRuleManager.java
index ffb73e2..585c925 100644
--- a/apps/kubevirt-networking/app/src/main/java/org/onosproject/kubevirtnetworking/impl/KubevirtFlowRuleManager.java
+++ b/apps/kubevirt-networking/app/src/main/java/org/onosproject/kubevirtnetworking/impl/KubevirtFlowRuleManager.java
@@ -139,7 +139,7 @@
localNodeId = clusterService.getLocalNode().id();
leadershipService.runForLeadership(appId.name());
nodeService.completeNodes(WORKER)
- .forEach(node -> initializePipeline(node.intgBridge()));
+ .forEach(node -> initializeWorkerNodePipeline(node.intgBridge()));
log.info("Started");
}
@@ -245,7 +245,30 @@
}));
}
- protected void initializePipeline(DeviceId deviceId) {
+ protected void initializeGatewayNodePipeline(DeviceId deviceId) {
+ // for inbound table transition
+ connectTables(deviceId, STAT_INBOUND_TABLE, VTAG_TABLE);
+
+ if (getProviderNetworkOnlyFlag()) {
+ // we directly transit from vTag table to PRE_FLAT table for provider
+ // network only mode, because there is no need to differentiate ARP
+ // and IP packets on this mode
+ connectTables(deviceId, VTAG_TABLE, PRE_FLAT_TABLE);
+ } else {
+ // for vTag and ARP table transition
+ connectTables(deviceId, VTAG_TABLE, ARP_TABLE);
+ }
+
+ // for PRE_FLAT and FLAT table transition
+ connectTables(deviceId, PRE_FLAT_TABLE, FLAT_TABLE);
+
+ // for setting up default FLAT table behavior which is drop
+ setupGatewayNodeFlatTable(deviceId);
+
+ // for setting up default Forwarding table behavior which is NORMAL
+ setupForwardingTable(deviceId);
+ }
+ protected void initializeWorkerNodePipeline(DeviceId deviceId) {
// for inbound table transition
connectTables(deviceId, STAT_INBOUND_TABLE, VTAP_INBOUND_TABLE);
connectTables(deviceId, VTAP_INBOUND_TABLE, DHCP_TABLE);
@@ -266,6 +289,9 @@
// for PRE_FLAT and FLAT table transition
connectTables(deviceId, PRE_FLAT_TABLE, FLAT_TABLE);
+ // for FLAT table and ACL table transition
+ connectTables(deviceId, FLAT_TABLE, ACL_EGRESS_TABLE);
+
// for ARP and ACL table transition
connectTables(deviceId, ARP_TABLE, ACL_INGRESS_TABLE);
@@ -282,8 +308,8 @@
// table lookup
setupJumpTable(deviceId);
- // for setting up default FLAT table behavior which is NORMAL
- setupFlatTable(deviceId);
+ // for setting up default Forwarding table behavior which is NORMAL
+ setupForwardingTable(deviceId);
}
private void setupJumpTable(DeviceId deviceId) {
@@ -323,7 +349,7 @@
applyRule(flowRule, true);
}
- private void setupFlatTable(DeviceId deviceId) {
+ private void setupForwardingTable(DeviceId deviceId) {
TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder()
.setOutput(PortNumber.NORMAL);
@@ -335,10 +361,29 @@
.withPriority(LOW_PRIORITY)
.fromApp(appId)
.makePermanent()
+ .forTable(FORWARDING_TABLE)
+ .build();
+
+ applyRule(flowRule, true);
+ }
+
+ private void setupGatewayNodeFlatTable(DeviceId deviceId) {
+ TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
+ TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder()
+ .drop();
+
+ FlowRule flowRule = DefaultFlowRule.builder()
+ .forDevice(deviceId)
+ .withSelector(selector.build())
+ .withTreatment(treatment.build())
+ .withPriority(DROP_PRIORITY)
+ .fromApp(appId)
+ .makePermanent()
.forTable(FLAT_TABLE)
.build();
applyRule(flowRule, true);
+
}
private boolean getProviderNetworkOnlyFlag() {
@@ -372,7 +417,11 @@
return;
}
- initializePipeline(node.intgBridge());
+ if (event.subject().type().equals(WORKER)) {
+ initializeWorkerNodePipeline(node.intgBridge());
+ } else {
+ initializeGatewayNodePipeline(node.intgBridge());
+ }
});
break;
case KUBEVIRT_NODE_CREATED:
diff --git a/apps/kubevirt-networking/app/src/main/java/org/onosproject/kubevirtnetworking/impl/KubevirtNetworkHandler.java b/apps/kubevirt-networking/app/src/main/java/org/onosproject/kubevirtnetworking/impl/KubevirtNetworkHandler.java
index 7f539de..b734226 100644
--- a/apps/kubevirt-networking/app/src/main/java/org/onosproject/kubevirtnetworking/impl/KubevirtNetworkHandler.java
+++ b/apps/kubevirt-networking/app/src/main/java/org/onosproject/kubevirtnetworking/impl/KubevirtNetworkHandler.java
@@ -23,6 +23,7 @@
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.onlab.packet.UDP;
import org.onosproject.cluster.ClusterService;
@@ -35,6 +36,10 @@
import org.onosproject.kubevirtnetworking.api.KubevirtNetworkEvent;
import org.onosproject.kubevirtnetworking.api.KubevirtNetworkListener;
import org.onosproject.kubevirtnetworking.api.KubevirtNetworkService;
+import org.onosproject.kubevirtnetworking.api.KubevirtRouter;
+import org.onosproject.kubevirtnetworking.api.KubevirtRouterAdminService;
+import org.onosproject.kubevirtnetworking.api.KubevirtRouterEvent;
+import org.onosproject.kubevirtnetworking.api.KubevirtRouterListener;
import org.onosproject.kubevirtnode.api.KubevirtApiConfigService;
import org.onosproject.kubevirtnode.api.KubevirtNode;
import org.onosproject.kubevirtnode.api.KubevirtNodeEvent;
@@ -66,6 +71,7 @@
import java.util.List;
import java.util.Objects;
+import java.util.Set;
import java.util.concurrent.ExecutorService;
import static java.lang.Thread.sleep;
@@ -74,7 +80,6 @@
import static org.onlab.packet.ICMP.TYPE_ECHO_REPLY;
import static org.onlab.packet.ICMP.TYPE_ECHO_REQUEST;
import static org.onlab.util.Tools.groupedThreads;
-import static org.onosproject.kubevirtnetworking.api.Constants.DEFAULT_GATEWAY_MAC;
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;
@@ -88,6 +93,8 @@
import static org.onosproject.kubevirtnetworking.api.Constants.TENANT_INBOUND_TABLE;
import static org.onosproject.kubevirtnetworking.api.Constants.TENANT_TO_TUNNEL_PREFIX;
import static org.onosproject.kubevirtnetworking.api.Constants.TUNNEL_TO_TENANT_PREFIX;
+import static org.onosproject.kubevirtnetworking.util.KubevirtNetworkingUtil.gatewayNodeForSpecifiedRouter;
+import static org.onosproject.kubevirtnetworking.util.KubevirtNetworkingUtil.getbrIntMacAddress;
import static org.onosproject.kubevirtnetworking.util.KubevirtNetworkingUtil.segmentIdHex;
import static org.onosproject.kubevirtnetworking.util.RulePopulatorUtil.NXM_NX_IP_TTL;
import static org.onosproject.kubevirtnetworking.util.RulePopulatorUtil.NXM_OF_ICMP_TYPE;
@@ -140,9 +147,15 @@
@Reference(cardinality = ReferenceCardinality.MANDATORY)
protected DriverService driverService;
+ @Reference(cardinality = ReferenceCardinality.MANDATORY)
+ protected KubevirtRouterAdminService kubevirtRouterService;
+
private final KubevirtNetworkListener networkListener = new InternalNetworkEventListener();
private final KubevirtNodeListener nodeListener = new InternalNodeEventListener();
+ private final InternalRouterEventListener kubevirtRouterlistener =
+ new InternalRouterEventListener();
+
private final ExecutorService eventExecutor = newSingleThreadExecutor(
groupedThreads(this.getClass().getSimpleName(), "event-handler"));
@@ -157,6 +170,8 @@
nodeService.addListener(nodeListener);
leadershipService.runForLeadership(appId.name());
+ kubevirtRouterService.addListener(kubevirtRouterlistener);
+
log.info("Started");
}
@@ -165,6 +180,8 @@
networkService.removeListener(networkListener);
nodeService.removeListener(nodeListener);
leadershipService.withdraw(appId.name());
+
+ kubevirtRouterService.removeListener(kubevirtRouterlistener);
eventExecutor.shutdown();
log.info("Stopped");
@@ -281,9 +298,9 @@
setDhcpRule(deviceId, true);
setForwardingRule(deviceId, true);
- setGatewayArpRule(network, TENANT_ARP_TABLE,
+ setGatewayArpRuleForInternalNetwork(network, TENANT_ARP_TABLE,
network.tenantDeviceId(node.hostname()), true);
- setGatewayIcmpRule(network, TENANT_ICMP_TABLE,
+ setGatewayIcmpRuleForInternalNetwork(network, TENANT_ICMP_TABLE,
network.tenantDeviceId(node.hostname()), true);
log.info("Install default flow rules for tenant bridge {}", network.tenantBridgeName());
@@ -327,10 +344,36 @@
install);
}
- private void setGatewayArpRule(KubevirtNetwork network,
- int tableNum, DeviceId deviceId, boolean install) {
+ private void initGatewayNodeForInternalNetwork(KubevirtNetwork network,
+ KubevirtRouter router,
+ KubevirtNode electedGateway,
+ boolean install) {
+ setGatewayArpRuleForInternalNetwork(network, PRE_FLAT_TABLE, electedGateway.intgBridge(), install);
+ setGatewayIcmpRuleForInternalNetwork(network, PRE_FLAT_TABLE, electedGateway.intgBridge(), install);
+ }
+
+
+ private void setGatewayInterNetworkRouting(KubevirtNetwork network, KubevirtRouter router, boolean install) {
+ router.internal().forEach(srcNetwork -> {
+ if (srcNetwork.equals(network.networkId())) {
+ return;
+ }
+
+ });
+ }
+
+ private void setGatewayArpRuleForInternalNetwork(KubevirtNetwork network,
+ int tableNum, DeviceId deviceId, boolean install) {
Device device = deviceService.getDevice(deviceId);
+ MacAddress brIntMacAddress = getbrIntMacAddress(deviceService, deviceId);
+
+ if (brIntMacAddress == null) {
+ log.error("Setting gateway arp rule for internal network because " +
+ "there's no br-int port for device {}", deviceId);
+ return;
+ }
+
TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder();
sBuilder.matchEthType(EthType.EtherType.ARP.ethType().toShort())
.matchArpOp(ARP.OP_REQUEST)
@@ -341,9 +384,9 @@
.extension(buildMoveArpShaToThaExtension(device), device.id())
.extension(buildMoveArpSpaToTpaExtension(device), device.id())
.setArpOp(ARP.OP_REPLY)
- .setArpSha(DEFAULT_GATEWAY_MAC)
+ .setArpSha(brIntMacAddress)
.setArpSpa(Ip4Address.valueOf(network.gatewayIp().toString()))
- .setEthSrc(DEFAULT_GATEWAY_MAC)
+ .setEthSrc(brIntMacAddress)
.setOutput(PortNumber.IN_PORT);
flowService.setRule(
@@ -357,9 +400,24 @@
);
}
- private void
- setGatewayIcmpRule(KubevirtNetwork network,
- int tableNum, DeviceId deviceId, boolean install) {
+ /**
+ * Sends ICMP echo reply for the ICMP echo request from the kubevirt VM.
+ *
+ * @param network kubevirt network
+ * @param tableNum flow table number
+ * @param deviceId device id of the selected gateway for the network
+ * @param install install if true, remove otherwise
+ */
+ private void setGatewayIcmpRuleForInternalNetwork(KubevirtNetwork network,
+ int tableNum, DeviceId deviceId, boolean install) {
+ MacAddress brIntMacAddress = getbrIntMacAddress(deviceService, deviceId);
+
+ if (brIntMacAddress == null) {
+ log.error("Setting gateway ICMP rule for internal network because " +
+ "there's no br-int port for device {}", deviceId);
+ return;
+ }
+
TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder()
.matchEthType(Ethernet.TYPE_IPV4)
.matchIPProtocol(IPv4.PROTOCOL_ICMP)
@@ -376,7 +434,7 @@
.extension(buildLoadExtension(device,
NXM_OF_ICMP_TYPE, TYPE_ECHO_REPLY), device.id())
.setIpSrc(network.gatewayIp())
- .setEthSrc(DEFAULT_GATEWAY_MAC)
+ .setEthSrc(brIntMacAddress)
.setOutput(PortNumber.IN_PORT);
flowService.setRule(
@@ -389,31 +447,221 @@
install);
}
-
- private void initGatewayNodeBridge(KubevirtNetwork network, boolean install) {
- KubevirtNode electedGateway = gatewayNodeForSpecifiedNetwork(network);
- if (electedGateway == null) {
- log.warn("There's no elected gateway for the network {}", network.name());
- return;
+ private class InternalRouterEventListener implements KubevirtRouterListener {
+ private boolean isRelevantHelper() {
+ return Objects.equals(localNodeId, leadershipService.getLeader(appId.name()));
}
- setGatewayArpRule(network, PRE_FLAT_TABLE, electedGateway.intgBridge(), install);
- setGatewayIcmpRule(network, PRE_FLAT_TABLE, electedGateway.intgBridge(), install);
- }
+ @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;
+ case KUBEVIRT_GATEWAY_NODE_ATTACHED:
+ eventExecutor.execute(() -> processRouterGatewayNodeAttached(event.subject(),
+ event.gateway()));
+ break;
+ case KUBEVIRT_GATEWAY_NODE_DETACHED:
+ eventExecutor.execute(() -> processRouterGatewayNodeDetached(event.subject(),
+ event.gateway()));
+ break;
+ case KUBEVIRT_GATEWAY_NODE_CHANGED:
+ eventExecutor.execute(() -> processRouterGatewayNodeChanged(event.subject(),
+ event.gateway()));
+ break;
- /**
- * Returns the gateway node for the specified network.
- * Among gateways, only one gateway would act as a gateway per network.
- *
- * @param network kubevirt network
- * @return gateway node which would act as the gateway for the network
- */
- private KubevirtNode gatewayNodeForSpecifiedNetwork(KubevirtNetwork network) {
- //TODO: would implement election logic for each network.
- //TODO: would implement cleanup logic in case a gateway node is added
- // and the election is changed
- return nodeService.completeNodes(GATEWAY).stream()
- .findFirst().orElse(null);
+ default:
+ //do nothing
+ break;
+ }
+ }
+
+ private void processRouterCreation(KubevirtRouter router) {
+ // When a router is created, we performs the election process to associate the router
+ // to the specific gateway. After the election, KubevirtNetwork handler installs bunch of rules
+ // to elected gateway node so that VMs associated to the router can ping to their gateway IP.
+ // SNAT and floating ip rule setup is out of this handler's scope and would be done with the other handlers
+ if (!isRelevantHelper()) {
+ return;
+ }
+ KubevirtNode electedGw = gatewayNodeForSpecifiedRouter(nodeService, router);
+ if (electedGw == null) {
+ return;
+ }
+
+ router.internal().forEach(networkName -> {
+ KubevirtNetwork network = networkService.network(networkName);
+
+ if (network != null) {
+ initGatewayNodeForInternalNetwork(network, router, electedGw, true);
+ }
+ });
+ kubevirtRouterService.updateRouter(router.updatedElectedGateway(electedGw.hostname()));
+ }
+
+ private void processRouterDeletion(KubevirtRouter router) {
+ if (!isRelevantHelper()) {
+ return;
+ }
+ KubevirtNode electedGw = gatewayNodeForSpecifiedRouter(nodeService, router);
+ if (electedGw == null) {
+ return;
+ }
+
+ router.internal().forEach(networkName -> {
+ KubevirtNetwork network = networkService.network(networkName);
+
+ if (network != null) {
+ initGatewayNodeForInternalNetwork(network, router, electedGw, false);
+ }
+ });
+ }
+
+ private void processRouterUpdate(KubevirtRouter router) {
+ if (!isRelevantHelper()) {
+ return;
+ }
+ if (router.electedGateway() == null) {
+ return;
+ }
+
+ KubevirtNode electedGw = nodeService.node(router.electedGateway());
+
+ router.internal().forEach(networkName -> {
+ KubevirtNetwork network = networkService.network(networkName);
+
+ if (network != null) {
+ initGatewayNodeForInternalNetwork(network, router, electedGw, true);
+ }
+ });
+ }
+
+ private void processRouterInternalNetworksAttached(KubevirtRouter router,
+ Set<String> attachedInternalNetworks) {
+ if (!isRelevantHelper()) {
+ return;
+ }
+ KubevirtNode electedGw = gatewayNodeForSpecifiedRouter(nodeService, router);
+ if (electedGw == null) {
+ return;
+ }
+
+ attachedInternalNetworks.forEach(networkName -> {
+ KubevirtNetwork network = networkService.network(networkName);
+
+ if (network != null) {
+ initGatewayNodeForInternalNetwork(network, router, electedGw, true);
+ }
+ });
+ }
+
+ private void processRouterInternalNetworksDetached(KubevirtRouter router,
+ Set<String> detachedInternalNetworks) {
+ if (!isRelevantHelper()) {
+ return;
+ }
+ KubevirtNode electedGw = gatewayNodeForSpecifiedRouter(nodeService, router);
+ if (electedGw == null) {
+ return;
+ }
+
+ detachedInternalNetworks.forEach(networkName -> {
+ KubevirtNetwork network = networkService.network(networkName);
+
+ if (network != null) {
+ initGatewayNodeForInternalNetwork(network, router, electedGw, false);
+ }
+ });
+
+ }
+
+ private void processRouterGatewayNodeAttached(KubevirtRouter router,
+ String associatedGateway) {
+ if (!isRelevantHelper()) {
+ return;
+ }
+
+ KubevirtNode gatewayNode = nodeService.node(associatedGateway);
+ if (gatewayNode == null) {
+ return;
+ }
+
+ router.internal().forEach(networkName -> {
+ KubevirtNetwork network = networkService.network(networkName);
+
+ if (network != null) {
+ initGatewayNodeForInternalNetwork(network, router, gatewayNode, true);
+ }
+ });
+ }
+
+ private void processRouterGatewayNodeDetached(KubevirtRouter router,
+ String disAssociatedGateway) {
+ if (!isRelevantHelper()) {
+ return;
+ }
+
+ KubevirtNode gatewayNode = nodeService.node(disAssociatedGateway);
+ if (gatewayNode == null) {
+ return;
+ }
+
+ router.internal().forEach(networkName -> {
+ KubevirtNetwork network = networkService.network(networkName);
+
+ if (network != null) {
+ initGatewayNodeForInternalNetwork(network, router, gatewayNode, false);
+ }
+ });
+ }
+
+ private void processRouterGatewayNodeChanged(KubevirtRouter router,
+ String disAssociatedGateway) {
+ if (!isRelevantHelper()) {
+ return;
+ }
+
+ KubevirtNode oldGatewayNode = nodeService.node(disAssociatedGateway);
+ if (oldGatewayNode == null) {
+ return;
+ }
+
+ router.internal().forEach(networkName -> {
+ KubevirtNetwork network = networkService.network(networkName);
+
+ if (network != null) {
+ initGatewayNodeForInternalNetwork(network, router, oldGatewayNode, false);
+ }
+ });
+
+ KubevirtNode newGatewayNode = nodeService.node(router.electedGateway());
+ if (newGatewayNode == null) {
+ return;
+ }
+
+ router.internal().forEach(networkName -> {
+ KubevirtNetwork network = networkService.network(networkName);
+
+ if (network != null) {
+ initGatewayNodeForInternalNetwork(network, router, oldGatewayNode, true);
+ }
+ });
+ }
}
private class InternalNetworkEventListener implements KubevirtNetworkListener {
@@ -451,7 +699,6 @@
break;
case FLAT:
case VLAN:
- initGatewayNodeBridge(network, true);
break;
default:
// do nothing
@@ -472,7 +719,6 @@
break;
case FLAT:
case VLAN:
- initGatewayNodeBridge(network, false);
break;
default:
// do nothing
@@ -516,6 +762,9 @@
case KUBEVIRT_NODE_COMPLETE:
eventExecutor.execute(() -> processNodeCompletion(event.subject()));
break;
+ case KUBEVIRT_NODE_REMOVED:
+ eventExecutor.execute(() -> processNodeDeletion(event.subject()));
+ break;
case KUBEVIRT_NODE_INCOMPLETE:
case KUBEVIRT_NODE_UPDATED:
default:
@@ -550,11 +799,11 @@
}
}
} else if (node.type().equals(GATEWAY)) {
+ updateGatewayNodeForRouter();
for (KubevirtNetwork network : networkService.networks()) {
switch (network.type()) {
case FLAT:
case VLAN:
- initGatewayNodeBridge(network, true);
break;
case VXLAN:
case GRE:
@@ -566,5 +815,39 @@
}
}
}
+
+ private void processNodeDeletion(KubevirtNode node) {
+ if (!isRelevantHelper()) {
+ return;
+ }
+
+ if (node.type().equals(GATEWAY)) {
+ updateGatewayNodeForRouter();
+ for (KubevirtNetwork network : networkService.networks()) {
+ switch (network.type()) {
+ case FLAT:
+ case VLAN:
+ break;
+ case VXLAN:
+ case GRE:
+ case GENEVE:
+ default:
+ // do nothing
+ break;
+ }
+ }
+ }
+ }
+
+ private void updateGatewayNodeForRouter() {
+ kubevirtRouterService.routers().forEach(router -> {
+ KubevirtNode newGwNode = gatewayNodeForSpecifiedRouter(nodeService, router);
+
+ if (newGwNode == null) {
+ return;
+ }
+ kubevirtRouterService.updateRouter(router.updatedElectedGateway(newGwNode.hostname()));
+ });
+ }
}
}
diff --git a/apps/kubevirt-networking/app/src/main/java/org/onosproject/kubevirtnetworking/impl/KubevirtRouterWatcher.java b/apps/kubevirt-networking/app/src/main/java/org/onosproject/kubevirtnetworking/impl/KubevirtRouterWatcher.java
index 501099f..c346c58 100644
--- a/apps/kubevirt-networking/app/src/main/java/org/onosproject/kubevirtnetworking/impl/KubevirtRouterWatcher.java
+++ b/apps/kubevirt-networking/app/src/main/java/org/onosproject/kubevirtnetworking/impl/KubevirtRouterWatcher.java
@@ -30,6 +30,7 @@
import org.onosproject.kubevirtnetworking.api.AbstractWatcher;
import org.onosproject.kubevirtnetworking.api.KubevirtRouter;
import org.onosproject.kubevirtnetworking.api.KubevirtRouterAdminService;
+import org.onosproject.kubevirtnetworking.api.KubevirtRouterService;
import org.onosproject.kubevirtnode.api.KubevirtApiConfigEvent;
import org.onosproject.kubevirtnode.api.KubevirtApiConfigListener;
import org.onosproject.kubevirtnode.api.KubevirtApiConfigService;
@@ -78,6 +79,9 @@
@Reference(cardinality = ReferenceCardinality.MANDATORY)
protected KubevirtApiConfigService configService;
+ @Reference(cardinality = ReferenceCardinality.MANDATORY)
+ protected KubevirtRouterService routerService;
+
private final ExecutorService eventExecutor = newSingleThreadExecutor(
groupedThreads(this.getClass().getSimpleName(), "event-handler"));
@@ -133,7 +137,14 @@
ObjectMapper mapper = new ObjectMapper();
JsonNode json = mapper.readTree(resource);
ObjectNode spec = (ObjectNode) json.get("spec");
- return codec(KubevirtRouter.class).decode(spec, this);
+ KubevirtRouter router = codec(KubevirtRouter.class).decode(spec, this);
+ KubevirtRouter existing = routerService.router(router.name());
+
+ if (existing == null) {
+ return router;
+ } else {
+ return router.updatedElectedGateway(existing.electedGateway());
+ }
} catch (IOException e) {
log.error("Failed to parse kubevirt router object");
}
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;
+ }
+ }
+}
diff --git a/apps/kubevirt-networking/app/src/main/java/org/onosproject/kubevirtnetworking/util/KubevirtNetworkingUtil.java b/apps/kubevirt-networking/app/src/main/java/org/onosproject/kubevirtnetworking/util/KubevirtNetworkingUtil.java
index d852ca4..f40d42c 100644
--- a/apps/kubevirt-networking/app/src/main/java/org/onosproject/kubevirtnetworking/util/KubevirtNetworkingUtil.java
+++ b/apps/kubevirt-networking/app/src/main/java/org/onosproject/kubevirtnetworking/util/KubevirtNetworkingUtil.java
@@ -27,15 +27,19 @@
import org.json.JSONException;
import org.json.JSONObject;
import org.onlab.osgi.DefaultServiceDirectory;
+import org.onlab.packet.Ip4Address;
import org.onlab.packet.IpAddress;
import org.onlab.packet.MacAddress;
import org.onosproject.cfg.ConfigProperty;
import org.onosproject.kubevirtnetworking.api.DefaultKubevirtPort;
import org.onosproject.kubevirtnetworking.api.KubevirtNetwork;
import org.onosproject.kubevirtnetworking.api.KubevirtPort;
+import org.onosproject.kubevirtnetworking.api.KubevirtRouter;
+import org.onosproject.kubevirtnetworking.api.KubevirtRouterService;
import org.onosproject.kubevirtnode.api.KubevirtApiConfig;
import org.onosproject.kubevirtnode.api.KubevirtApiConfigService;
import org.onosproject.kubevirtnode.api.KubevirtNode;
+import org.onosproject.kubevirtnode.api.KubevirtNodeService;
import org.onosproject.net.DeviceId;
import org.onosproject.net.Port;
import org.onosproject.net.PortNumber;
@@ -54,6 +58,7 @@
import java.util.stream.Collectors;
import static org.onosproject.kubevirtnetworking.api.Constants.TUNNEL_TO_TENANT_PREFIX;
+import static org.onosproject.kubevirtnode.api.KubevirtNode.Type.GATEWAY;
import static org.onosproject.net.AnnotationKeys.PORT_NAME;
/**
@@ -83,8 +88,8 @@
/**
* Obtains the boolean property value with specified property key name.
*
- * @param properties a collection of properties
- * @param name key name
+ * @param properties a collection of properties
+ * @param name key name
* @return mapping value
*/
public static boolean getPropertyValueAsBoolean(Set<ConfigProperty> properties,
@@ -99,7 +104,7 @@
* Re-structures the OVS port name.
* The length of OVS port name should be not large than 15.
*
- * @param portName original port name
+ * @param portName original port name
* @return re-structured OVS port name
*/
public static String structurePortName(String portName) {
@@ -146,8 +151,8 @@
/**
* Prints out the JSON string in pretty format.
*
- * @param mapper Object mapper
- * @param jsonString JSON string
+ * @param mapper Object mapper
+ * @param jsonString JSON string
* @return pretty formatted JSON string
*/
public static String prettyJson(ObjectMapper mapper, String jsonString) {
@@ -185,8 +190,8 @@
/**
* Calculate the broadcast address from given IP address and subnet prefix length.
*
- * @param ipAddr IP address
- * @param prefixLength subnet prefix length
+ * @param ipAddr IP address
+ * @param prefixLength subnet prefix length
* @return broadcast address
*/
public static String getBroadcastAddr(String ipAddr, int prefixLength) {
@@ -194,12 +199,13 @@
SubnetUtils utils = new SubnetUtils(subnet);
return utils.getInfo().getBroadcastAddress();
}
+
/**
* Generates endpoint URL by referring to scheme, ipAddress and port.
*
- * @param scheme scheme
- * @param ipAddress IP address
- * @param port port number
+ * @param scheme scheme
+ * @param ipAddress IP address
+ * @param port port number
* @return generated endpoint URL
*/
public static String endpoint(KubevirtApiConfig.Scheme scheme, IpAddress ipAddress, int port) {
@@ -218,7 +224,7 @@
/**
* Generates endpoint URL by referring to scheme, ipAddress and port.
*
- * @param apiConfig kubernetes API config
+ * @param apiConfig kubernetes API config
* @return generated endpoint URL
*/
public static String endpoint(KubevirtApiConfig apiConfig) {
@@ -289,7 +295,7 @@
* Obtains the tunnel port number with the given network and node.
*
* @param network kubevirt network
- * @param node kubevirt node
+ * @param node kubevirt node
* @return tunnel port number
*/
public static PortNumber tunnelPort(KubevirtNetwork network, KubevirtNode node) {
@@ -310,7 +316,7 @@
* Obtains the kubevirt port from kubevirt POD.
*
* @param networks set of existing kubevirt networks
- * @param pod kubevirt POD
+ * @param pod kubevirt POD
* @return kubevirt port
*/
public static KubevirtPort getPort(Set<KubevirtNetwork> networks, Pod pod) {
@@ -365,7 +371,7 @@
/**
* Obtains the tunnel bridge to tenant bridge patch port number.
*
- * @param node kubevirt node
+ * @param node kubevirt node
* @param network kubevirt network
* @return patch port number
*/
@@ -385,7 +391,7 @@
/**
* Obtains the tunnel port number of the given node.
*
- * @param node kubevirt node
+ * @param node kubevirt node
* @param network kubevirt network
* @return tunnel port number
*/
@@ -432,4 +438,64 @@
return port != null ? port.number() : null;
}
+ /**
+ * Returns the gateway node for the specified kubevirt router.
+ * Among gateways, only one gateway would act as a gateway per perter.
+ * Currently gateway node is selected based on modulo operation with router hashcode.
+ *
+ * @param nodeService kubevirt node service
+ * @param router kubevirt router
+ * @return elected gateway node
+ */
+ public static KubevirtNode gatewayNodeForSpecifiedRouter(KubevirtNodeService nodeService,
+ KubevirtRouter router) {
+ //TODO: enhance election logic for a better load balancing
+
+ int numOfGateways = nodeService.completeNodes(GATEWAY).size();
+ if (numOfGateways == 0) {
+ return null;
+ }
+ return (KubevirtNode) nodeService.completeNodes(GATEWAY).toArray()[router.hashCode() % numOfGateways];
+ }
+
+ /**
+ * Returns the mac address of the br-int port of specified device.
+ *
+ * @param deviceService device service
+ * @param deviceId device Id
+ * @return mac address of the br-int port
+ */
+ public static MacAddress getbrIntMacAddress(DeviceService deviceService,
+ DeviceId deviceId) {
+ return MacAddress.valueOf(deviceService.getPorts(deviceId).stream()
+ .filter(port -> Objects.equals(port.annotations().value(PORT_NAME), "br-int"))
+ .map(port -> port.annotations().value("portMac"))
+ .findAny().orElse(null));
+ }
+
+ /**
+ * Returns the snat ip address with specified router.
+ *
+ * @param routerService router service
+ * @param internalNetworkId internal network id which is associated with the router
+ * @return snat ip address if exist, null otherwise
+ */
+ public static IpAddress getRouterSnatIpAddress(KubevirtRouterService routerService,
+ String internalNetworkId) {
+ KubevirtRouter router = routerService.routers().stream()
+ .filter(r -> r.internal().contains(internalNetworkId))
+ .findAny().orElse(null);
+
+ if (router == null) {
+ return null;
+ }
+
+ String routerSnatIp = router.external().keySet().stream().findAny().orElse(null);
+
+ if (routerSnatIp == null) {
+ return null;
+ }
+
+ return Ip4Address.valueOf(routerSnatIp);
+ }
}
diff --git a/apps/kubevirt-networking/app/src/test/java/org/onosproject/kubevirtnetworking/codec/KubevirtRouterCodecTest.java b/apps/kubevirt-networking/app/src/test/java/org/onosproject/kubevirtnetworking/codec/KubevirtRouterCodecTest.java
index f2b2470..3af305c 100644
--- a/apps/kubevirt-networking/app/src/test/java/org/onosproject/kubevirtnetworking/codec/KubevirtRouterCodecTest.java
+++ b/apps/kubevirt-networking/app/src/test/java/org/onosproject/kubevirtnetworking/codec/KubevirtRouterCodecTest.java
@@ -83,6 +83,7 @@
.internal(ImmutableSet.of("vlan-1"))
.external(ImmutableMap.of("10.10.10.20", "flat-1"))
.peerRouter(peerRouter)
+ .electedGateway("gatewayNode")
.build();
ObjectNode routerJson = kubevirtRouterCodec.encode(router, context);
@@ -96,10 +97,13 @@
assertEquals("router-1", router.name());
assertEquals("Example Virtual Router", router.description());
assertTrue(router.enableSnat());
+ assertEquals("192.168.10.5",
+ router.external().keySet().stream().findAny().orElse(null));
assertEquals("external-network", router.external().get("192.168.10.5"));
assertTrue(router.internal().contains("vxlan-network-1"));
assertTrue(router.internal().contains("vxlan-network-2"));
assertEquals("192.168.10.1", router.peerRouter().ipAddress().toString());
+ assertEquals("gatewayNode", router.electedGateway());
}
private KubevirtRouter getKubevirtRouter(String resourceName) throws IOException {
diff --git a/apps/kubevirt-networking/app/src/test/resources/org/onosproject/kubevirtnetworking/codec/KubevirtRouter.json b/apps/kubevirt-networking/app/src/test/resources/org/onosproject/kubevirtnetworking/codec/KubevirtRouter.json
index 44fd556..14e60ec 100644
--- a/apps/kubevirt-networking/app/src/test/resources/org/onosproject/kubevirtnetworking/codec/KubevirtRouter.json
+++ b/apps/kubevirt-networking/app/src/test/resources/org/onosproject/kubevirtnetworking/codec/KubevirtRouter.json
@@ -12,5 +12,6 @@
],
"peerRouter": {
"ip": "192.168.10.1"
- }
+ },
+ "gateway": "gatewayNode"
}
\ No newline at end of file