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/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 {