Sends GARP packet when floating ip is associated and the gateway node is changed.
Change-Id: I1bd9deb2574d97473ef9709321944116904aec8d
diff --git a/apps/kubevirt-networking/app/src/main/java/org/onosproject/kubevirtnetworking/impl/KubevirtFloatingIpHandler.java b/apps/kubevirt-networking/app/src/main/java/org/onosproject/kubevirtnetworking/impl/KubevirtFloatingIpHandler.java
index 96d111c..f1a5fe0 100644
--- a/apps/kubevirt-networking/app/src/main/java/org/onosproject/kubevirtnetworking/impl/KubevirtFloatingIpHandler.java
+++ b/apps/kubevirt-networking/app/src/main/java/org/onosproject/kubevirtnetworking/impl/KubevirtFloatingIpHandler.java
@@ -48,6 +48,8 @@
import org.onosproject.net.flow.DefaultTrafficTreatment;
import org.onosproject.net.flow.TrafficSelector;
import org.onosproject.net.flow.TrafficTreatment;
+import org.onosproject.net.packet.DefaultOutboundPacket;
+import org.onosproject.net.packet.PacketService;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Deactivate;
@@ -55,6 +57,7 @@
import org.osgi.service.component.annotations.ReferenceCardinality;
import org.slf4j.Logger;
+import java.nio.ByteBuffer;
import java.util.Objects;
import java.util.concurrent.ExecutorService;
@@ -70,6 +73,7 @@
import static org.onosproject.kubevirtnetworking.api.KubevirtNetwork.Type.GENEVE;
import static org.onosproject.kubevirtnetworking.api.KubevirtNetwork.Type.GRE;
import static org.onosproject.kubevirtnetworking.api.KubevirtNetwork.Type.VXLAN;
+import static org.onosproject.kubevirtnetworking.util.KubevirtNetworkingUtil.buildGarpPacket;
import static org.onosproject.kubevirtnetworking.util.KubevirtNetworkingUtil.externalPatchPortNum;
import static org.onosproject.kubevirtnetworking.util.KubevirtNetworkingUtil.gatewayNodeForSpecifiedRouter;
import static org.onosproject.kubevirtnetworking.util.KubevirtNetworkingUtil.getRouterMacAddress;
@@ -97,6 +101,9 @@
protected DeviceAdminService deviceService;
@Reference(cardinality = ReferenceCardinality.MANDATORY)
+ protected PacketService packetService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY)
protected KubevirtPortService kubevirtPortService;
@Reference(cardinality = ReferenceCardinality.MANDATORY)
@@ -148,9 +155,10 @@
log.info("Stopped");
}
- private void setFloatingIpRules(KubevirtRouter router,
- KubevirtFloatingIp floatingIp,
- boolean install) {
+ private void setFloatingIpRulesForFip(KubevirtRouter router,
+ KubevirtFloatingIp floatingIp,
+ KubevirtNode electedGw,
+ boolean install) {
KubevirtPort kubevirtPort = getKubevirtPort(floatingIp);
if (kubevirtPort == null) {
@@ -164,24 +172,16 @@
setFloatingIpDownstreamRulesToGatewayTunBridge(router, floatingIp, kubevirtNetwork, kubevirtPort, install);
}
- setFloatingIpArpResponseRules(router, floatingIp, kubevirtPort, install);
- setFloatingIpUpstreamRules(router, floatingIp, kubevirtPort, install);
- setFloatingIpDownstreamRules(router, floatingIp, kubevirtPort, install);
+ setFloatingIpArpResponseRules(router, floatingIp, kubevirtPort, electedGw, install);
+ setFloatingIpUpstreamRules(router, floatingIp, kubevirtPort, electedGw, install);
+ setFloatingIpDownstreamRules(router, floatingIp, kubevirtPort, electedGw, install);
}
private void setFloatingIpArpResponseRules(KubevirtRouter router,
KubevirtFloatingIp floatingIp,
KubevirtPort port,
+ KubevirtNode electedGw,
boolean install) {
-
- KubevirtNode electedGw = gatewayNodeForSpecifiedRouter(kubevirtNodeService, router);
-
- if (electedGw == null) {
- log.warn("Failed to install floating Ip rules for floating ip {} " +
- "because there's no gateway assigned to it", floatingIp.floatingIp());
- return;
- }
-
TrafficSelector selector = DefaultTrafficSelector.builder()
.matchInPort(externalPatchPortNum(deviceService, electedGw))
.matchEthType(EthType.EtherType.ARP.ethType().toShort())
@@ -222,16 +222,9 @@
private void setFloatingIpUpstreamRules(KubevirtRouter router,
KubevirtFloatingIp floatingIp,
KubevirtPort port,
+ KubevirtNode electedGw,
boolean install) {
- KubevirtNode electedGw = gatewayNodeForSpecifiedRouter(kubevirtNodeService, router);
-
- if (electedGw == null) {
- log.warn("Failed to install floating Ip rules for floating ip {} " +
- "because there's no gateway assigned to it", floatingIp.floatingIp());
- return;
- }
-
MacAddress peerMacAddress = router.peerRouter().macAddress();
if (peerMacAddress == null) {
@@ -271,15 +264,8 @@
private void setFloatingIpDownstreamRules(KubevirtRouter router,
KubevirtFloatingIp floatingIp,
KubevirtPort port,
+ KubevirtNode electedGw,
boolean install) {
- KubevirtNode electedGw = gatewayNodeForSpecifiedRouter(kubevirtNodeService, router);
-
- if (electedGw == null) {
- log.warn("Failed to install floating Ip rules for floating ip {} " +
- "because there's no gateway assigned to it", floatingIp.floatingIp());
- return;
- }
-
MacAddress routerMacAddress = getRouterMacAddress(router);
TrafficSelector selector = DefaultTrafficSelector.builder()
@@ -355,6 +341,31 @@
install);
}
+ private void processGarpPacketForFloatingIp(KubevirtFloatingIp floatingIp, KubevirtNode electedGw) {
+
+ if (floatingIp == null) {
+ return;
+ }
+
+ KubevirtPort kubevirtPort = getKubevirtPort(floatingIp);
+ if (kubevirtPort == null) {
+ log.warn("Failed to install floating Ip rules for floating ip {} " +
+ "because there's no kubevirt port associated to it", floatingIp.floatingIp());
+ return;
+ }
+
+ Ethernet ethernet = buildGarpPacket(kubevirtPort.macAddress(), floatingIp.floatingIp());
+ if (ethernet == null) {
+ return;
+ }
+
+ TrafficTreatment treatment = DefaultTrafficTreatment.builder()
+ .setOutput(externalPatchPortNum(deviceService, electedGw)).build();
+
+ packetService.emit(new DefaultOutboundPacket(electedGw.intgBridge(), treatment,
+ ByteBuffer.wrap(ethernet.serialize())));
+ }
+
private class InternalRouterEventListener implements KubevirtRouterListener {
private boolean isRelevantHelper() {
return Objects.equals(localNodeId, leadershipService.getLeader(appId.name()));
@@ -371,25 +382,65 @@
eventExecutor.execute(() -> processFloatingIpDisassociation(event.subject(),
event.floatingIp()));
break;
-
+ case KUBEVIRT_GATEWAY_NODE_CHANGED:
+ eventExecutor.execute(() -> processRouterGatewayNodeChanged(event.subject(),
+ event.gateway()));
+ break;
default:
//do nothing
break;
}
}
+ private void processRouterGatewayNodeChanged(KubevirtRouter router, String disAssociatedGateway) {
+
+ kubevirtRouterService.floatingIps()
+ .stream()
+ .filter(fip -> fip.routerName().equals(router.name())).forEach(fip -> {
+ KubevirtNode oldGw = kubevirtNodeService.node(disAssociatedGateway);
+ if (oldGw == null) {
+ return;
+ }
+
+ KubevirtNode newGw = kubevirtNodeService.node(router.electedGateway());
+ if (newGw == null) {
+ return;
+ }
+
+ setFloatingIpRulesForFip(router, fip, oldGw, false);
+
+ setFloatingIpRulesForFip(router, fip, newGw, true);
+ processGarpPacketForFloatingIp(fip, newGw);
+
+ });
+ }
+
private void processFloatingIpAssociation(KubevirtRouter router, KubevirtFloatingIp floatingIp) {
- if (!isRelevantHelper()) {
+ if (!isRelevantHelper() || router.electedGateway() == null) {
return;
}
- setFloatingIpRules(router, floatingIp, true);
+
+ KubevirtNode electedGw = kubevirtNodeService.node(router.electedGateway());
+
+ if (electedGw == null) {
+ return;
+ }
+
+ processGarpPacketForFloatingIp(floatingIp, electedGw);
+ setFloatingIpRulesForFip(router, floatingIp, electedGw, true);
}
private void processFloatingIpDisassociation(KubevirtRouter router, KubevirtFloatingIp floatingIp) {
- if (!isRelevantHelper()) {
+ if (!isRelevantHelper() || router.electedGateway() == null) {
return;
}
- setFloatingIpRules(router, floatingIp, false);
+
+ KubevirtNode electedGw = kubevirtNodeService.node(router.electedGateway());
+
+ if (electedGw == null) {
+ return;
+ }
+ setFloatingIpRulesForFip(router, floatingIp, electedGw, false);
}
}
@@ -413,20 +464,21 @@
}
private void processNodeCompletion(KubevirtNode node) {
- if (!isRelevantHelper()) {
+ if (!isRelevantHelper() || !node.type().equals(KubevirtNode.Type.GATEWAY)) {
return;
}
kubevirtRouterService.floatingIps().forEach(fip -> {
KubevirtRouter router = kubevirtRouterService.router(fip.routerName());
- if (router != null) {
- KubevirtNode electedGw = gatewayNodeForSpecifiedRouter(kubevirtNodeService, router);
+
+ if (router != null && router.electedGateway() != null) {
+ KubevirtNode electedGw = kubevirtNodeService.node(router.electedGateway());
if (electedGw == null) {
return;
}
if (electedGw.hostname().equals(node.hostname())) {
- setFloatingIpRules(router, fip, true);
+ setFloatingIpRulesForFip(router, fip, electedGw, true);
}
}
});
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
index 8323f98..a2667f9 100644
--- 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
@@ -54,6 +54,8 @@
import org.onosproject.net.flow.TrafficSelector;
import org.onosproject.net.flow.TrafficTreatment;
import org.onosproject.net.flow.instructions.ExtensionTreatment;
+import org.onosproject.net.packet.DefaultOutboundPacket;
+import org.onosproject.net.packet.PacketService;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Deactivate;
@@ -61,6 +63,7 @@
import org.osgi.service.component.annotations.ReferenceCardinality;
import org.slf4j.Logger;
+import java.nio.ByteBuffer;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.ExecutorService;
@@ -80,6 +83,7 @@
import static org.onosproject.kubevirtnetworking.api.KubevirtNetwork.Type.GRE;
import static org.onosproject.kubevirtnetworking.api.KubevirtNetwork.Type.VLAN;
import static org.onosproject.kubevirtnetworking.api.KubevirtNetwork.Type.VXLAN;
+import static org.onosproject.kubevirtnetworking.util.KubevirtNetworkingUtil.buildGarpPacket;
import static org.onosproject.kubevirtnetworking.util.KubevirtNetworkingUtil.externalPatchPortNum;
import static org.onosproject.kubevirtnetworking.util.KubevirtNetworkingUtil.gatewayNodeForSpecifiedRouter;
import static org.onosproject.kubevirtnetworking.util.KubevirtNetworkingUtil.getRouterForKubevirtPort;
@@ -130,6 +134,9 @@
protected DriverService driverService;
@Reference(cardinality = ReferenceCardinality.MANDATORY)
+ protected PacketService packetService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY)
protected KubevirtRouterService kubevirtRouterService;
private final ExecutorService eventExecutor = newSingleThreadExecutor(
@@ -172,15 +179,14 @@
log.info("Stopped");
}
- private void initGatewayNodeSnatForRouter(KubevirtRouter router, boolean install) {
+ private void initGatewayNodeSnatForRouter(KubevirtRouter router, String gateway, boolean install) {
if (router.electedGateway() == null) {
log.warn("Fail to initialize gateway node snat for router {} " +
"because there's no gateway assigned to it", router.name());
return;
}
- KubevirtNode electedGw = kubevirtNodeService.node(router.electedGateway());
-
+ KubevirtNode electedGw = kubevirtNodeService.node(gateway);
if (electedGw == null) {
log.warn("Fail to initialize gateway node snat for router {} " +
"because there's no gateway assigned to it", router.name());
@@ -454,6 +460,10 @@
eventExecutor.execute(() -> processRouterGatewayNodeDetached(event.subject(),
event.gateway()));
break;
+ case KUBEVIRT_GATEWAY_NODE_CHANGED:
+ eventExecutor.execute(() -> processRouterGatewayNodeChanged(event.subject(),
+ event.gateway()));
+ break;
case KUBEVIRT_ROUTER_EXTERNAL_NETWORK_ATTACHED:
eventExecutor.execute(() -> processRouterExternalNetAttached(event.subject(),
event.externalIp(), event.externalNet(),
@@ -661,7 +671,7 @@
return;
}
if (router.enableSnat() && !router.external().isEmpty() && router.peerRouter() != null) {
- initGatewayNodeSnatForRouter(router, true);
+ initGatewayNodeSnatForRouter(router, router.electedGateway(), true);
}
}
@@ -670,7 +680,7 @@
return;
}
if (!router.external().isEmpty() && router.peerRouter() != null) {
- initGatewayNodeSnatForRouter(router, false);
+ initGatewayNodeSnatForRouter(router, router.electedGateway(), false);
}
}
@@ -679,9 +689,54 @@
return;
}
if (router.enableSnat() && !router.external().isEmpty() && router.peerRouter() != null) {
- initGatewayNodeSnatForRouter(router, true);
+ initGatewayNodeSnatForRouter(router, router.electedGateway(), true);
}
}
+
+ private void processRouterGatewayNodeChanged(KubevirtRouter router,
+ String disAssociatedGateway) {
+ if (!isRelevantHelper()) {
+ return;
+ }
+
+ if (router.enableSnat() && !router.external().isEmpty() && router.peerRouter() != null) {
+ initGatewayNodeSnatForRouter(router, disAssociatedGateway, false);
+ initGatewayNodeSnatForRouter(router, router.electedGateway(), true);
+
+ processRouterGatewayNodeDetached(router, disAssociatedGateway);
+ processRouterGatewayNodeAttached(router, router.electedGateway());
+
+ sendGarpPacketForSnatIp(router);
+ }
+ }
+
+ private void sendGarpPacketForSnatIp(KubevirtRouter router) {
+ if (router == null || router.electedGateway() == null) {
+ return;
+ }
+
+ String routerSnatIp = router.external().keySet().stream().findAny().orElse(null);
+
+ if (routerSnatIp == null) {
+ log.warn("Fail to initialize gateway node snat for router {} " +
+ "because there's no gateway snat ip assigned to it", router.name());
+ return;
+ }
+
+ Ethernet ethernet = buildGarpPacket(DEFAULT_GATEWAY_MAC, IpAddress.valueOf(routerSnatIp));
+
+ if (ethernet == null) {
+ return;
+ }
+
+ KubevirtNode gatewayNode = kubevirtNodeService.node(router.electedGateway());
+
+ TrafficTreatment treatment = DefaultTrafficTreatment.builder()
+ .setOutput(externalPatchPortNum(deviceService, gatewayNode)).build();
+
+ packetService.emit(new DefaultOutboundPacket(gatewayNode.intgBridge(), treatment,
+ ByteBuffer.wrap(ethernet.serialize())));
+ }
}
private class InternalKubevirtPortListener implements KubevirtPortListener {
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 a6ac229..14bc79c 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
@@ -28,6 +28,8 @@
import org.json.JSONException;
import org.json.JSONObject;
import org.onlab.osgi.DefaultServiceDirectory;
+import org.onlab.packet.ARP;
+import org.onlab.packet.Ethernet;
import org.onlab.packet.Ip4Address;
import org.onlab.packet.IpAddress;
import org.onlab.packet.MacAddress;
@@ -561,6 +563,13 @@
return port != null ? port.number() : null;
}
+ /**
+ * Returns the kubevirt external network with specified router.
+ *
+ * @param networkService kubevirt network service
+ * @param router kubevirt router
+ * @return external network
+ */
public static KubevirtNetwork getExternalNetworkByRouter(KubevirtNetworkService networkService,
KubevirtRouter router) {
String networkId = router.external().values().stream().findAny().orElse(null);
@@ -570,4 +579,33 @@
return networkService.network(networkId);
}
+
+ public static Ethernet buildGarpPacket(MacAddress srcMac, IpAddress srcIp) {
+ if (srcMac == null || srcIp == null) {
+ return null;
+ }
+
+ Ethernet ethernet = new Ethernet();
+ ethernet.setDestinationMACAddress(MacAddress.BROADCAST);
+ ethernet.setSourceMACAddress(srcMac);
+ ethernet.setEtherType(Ethernet.TYPE_ARP);
+
+ ARP arp = new ARP();
+ arp.setOpCode(ARP.OP_REPLY);
+ arp.setProtocolType(ARP.PROTO_TYPE_IP);
+ arp.setHardwareType(ARP.HW_TYPE_ETHERNET);
+
+ arp.setProtocolAddressLength((byte) Ip4Address.BYTE_LENGTH);
+ arp.setHardwareAddressLength((byte) Ethernet.DATALAYER_ADDRESS_LENGTH);
+
+ arp.setSenderHardwareAddress(srcMac.toBytes());
+ arp.setTargetHardwareAddress(MacAddress.BROADCAST.toBytes());
+
+ arp.setSenderProtocolAddress(srcIp.toOctets());
+ arp.setTargetProtocolAddress(srcIp.toOctets());
+
+ ethernet.setPayload(arp);
+
+ return ethernet;
+ }
}