Refactor: simplify the floating IP related ARP rule installation

Change-Id: Ie00a5b8e6fe67d5b5f50147d4a82639b2f0c4097
diff --git a/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackRoutingArpHandler.java b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackRoutingArpHandler.java
index 4f2c344..6c3c16b 100644
--- a/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackRoutingArpHandler.java
+++ b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackRoutingArpHandler.java
@@ -15,7 +15,6 @@
  */
 package org.onosproject.openstacknetworking.impl;
 
-import com.google.common.base.Strings;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Sets;
 import org.onlab.packet.ARP;
@@ -49,8 +48,6 @@
 import org.onosproject.openstacknetworking.api.ExternalPeerRouter;
 import org.onosproject.openstacknetworking.api.InstancePort;
 import org.onosproject.openstacknetworking.api.InstancePortAdminService;
-import org.onosproject.openstacknetworking.api.InstancePortEvent;
-import org.onosproject.openstacknetworking.api.InstancePortListener;
 import org.onosproject.openstacknetworking.api.OpenstackFlowRuleService;
 import org.onosproject.openstacknetworking.api.OpenstackNetworkAdminService;
 import org.onosproject.openstacknetworking.api.OpenstackNetworkEvent;
@@ -59,7 +56,6 @@
 import org.onosproject.openstacknetworking.api.OpenstackRouterAdminService;
 import org.onosproject.openstacknetworking.api.OpenstackRouterEvent;
 import org.onosproject.openstacknetworking.api.OpenstackRouterListener;
-import org.onosproject.openstacknetworking.api.PreCommitPortService;
 import org.onosproject.openstacknode.api.OpenstackNode;
 import org.onosproject.openstacknode.api.OpenstackNodeEvent;
 import org.onosproject.openstacknode.api.OpenstackNodeListener;
@@ -93,20 +89,16 @@
 import static org.onosproject.openstacknetworking.api.Constants.OPENSTACK_NETWORKING_APP_ID;
 import static org.onosproject.openstacknetworking.api.Constants.PRIORITY_ARP_CONTROL_RULE;
 import static org.onosproject.openstacknetworking.api.Constants.PRIORITY_ARP_GATEWAY_RULE;
-import static org.onosproject.openstacknetworking.api.OpenstackNetworkEvent.Type.OPENSTACK_PORT_PRE_REMOVE;
 import static org.onosproject.openstacknetworking.impl.OsgiPropertyConstants.ARP_MODE;
 import static org.onosproject.openstacknetworking.impl.OsgiPropertyConstants.ARP_MODE_DEFAULT;
 import static org.onosproject.openstacknetworking.impl.OsgiPropertyConstants.GATEWAY_MAC_DEFAULT;
-import static org.onosproject.openstacknetworking.util.OpenstackNetworkingUtil.associatedFloatingIp;
 import static org.onosproject.openstacknetworking.util.OpenstackNetworkingUtil.externalGatewayIp;
 import static org.onosproject.openstacknetworking.util.OpenstackNetworkingUtil.externalPeerRouterForNetwork;
 import static org.onosproject.openstacknetworking.util.OpenstackNetworkingUtil.floatingIpByInstancePort;
 import static org.onosproject.openstacknetworking.util.OpenstackNetworkingUtil.getGwByComputeDevId;
 import static org.onosproject.openstacknetworking.util.OpenstackNetworkingUtil.getGwByInstancePort;
 import static org.onosproject.openstacknetworking.util.OpenstackNetworkingUtil.getPropertyValue;
-import static org.onosproject.openstacknetworking.util.OpenstackNetworkingUtil.isAssociatedWithVM;
 import static org.onosproject.openstacknetworking.util.OpenstackNetworkingUtil.processGarpPacketForFloatingIp;
-import static org.onosproject.openstacknetworking.util.OpenstackNetworkingUtil.swapStaleLocation;
 import static org.onosproject.openstacknetworking.util.RulePopulatorUtil.buildMoveArpShaToThaExtension;
 import static org.onosproject.openstacknetworking.util.RulePopulatorUtil.buildMoveArpSpaToTpaExtension;
 import static org.onosproject.openstacknetworking.util.RulePopulatorUtil.buildMoveEthSrcToDstExtension;
@@ -167,9 +159,6 @@
     @Reference(cardinality = ReferenceCardinality.MANDATORY)
     protected ComponentConfigService configService;
 
-    @Reference(cardinality = ReferenceCardinality.MANDATORY)
-    protected PreCommitPortService preCommitPortService;
-
     /** ARP processing mode, broadcast | proxy (default). **/
     protected String arpMode = ARP_MODE_DEFAULT;
 
@@ -177,7 +166,6 @@
 
     private final OpenstackRouterListener osRouterListener = new InternalRouterEventListener();
     private final OpenstackNodeListener osNodeListener = new InternalNodeEventListener();
-    private final InstancePortListener instPortListener = new InternalInstancePortListener();
     private final OpenstackNetworkListener osNetworkListener = new InternalNetworkEventListener();
 
     private ApplicationId appId;
@@ -196,7 +184,6 @@
         osRouterAdminService.addListener(osRouterListener);
         osNodeService.addListener(osNodeListener);
         osNetworkService.addListener(osNetworkListener);
-        instancePortService.addListener(instPortListener);
         leadershipService.runForLeadership(appId.name());
         packetService.addProcessor(packetProcessor, PacketProcessor.director(1));
         log.info("Started");
@@ -205,11 +192,9 @@
     @Deactivate
     protected void deactivate() {
         packetService.removeProcessor(packetProcessor);
-        instancePortService.removeListener(instPortListener);
         osRouterAdminService.removeListener(osRouterListener);
         osNodeService.removeListener(osNodeListener);
         osNetworkService.removeListener(osNetworkListener);
-        instancePortService.removeListener(instPortListener);
         leadershipService.withdraw(appId.name());
         eventExecutor.shutdown();
         configService.unregisterProperties(getClass(), false);
@@ -370,187 +355,6 @@
                 .anyMatch(ip -> IpAddress.valueOf(ip.getIpAddress()).equals(targetIp));
     }
 
-    /**
-     * Installs static ARP rules used in ARP BROAD_CAST mode.
-     *
-     * @param gateway gateway node
-     * @param install flow rule installation flag
-     */
-    private void setFloatingIpArpRuleForGateway(OpenstackNode gateway,
-                                                boolean install) {
-        if (ARP_BROADCAST_MODE.equals(getArpMode())) {
-
-            Set<OpenstackNode> completedGws = osNodeService.completeNodes(GATEWAY);
-            Set<OpenstackNode> finalGws = Sets.newConcurrentHashSet();
-            finalGws.addAll(ImmutableSet.copyOf(completedGws));
-
-            if (install) {
-                if (completedGws.contains(gateway)) {
-                    if (completedGws.size() > 1) {
-                        finalGws.remove(gateway);
-                        osRouterAdminService.floatingIps().forEach(fip -> {
-                            if (fip.getPortId() != null) {
-                                setFloatingIpArpRule(fip, fip.getPortId(),
-                                                     finalGws, false);
-                                finalGws.add(gateway);
-                            }
-                        });
-                    }
-                    osRouterAdminService.floatingIps().forEach(fip -> {
-                        if (fip.getPortId() != null) {
-                            setFloatingIpArpRule(fip, fip.getPortId(),
-                                                 finalGws, true);
-                        }
-                    });
-                } else {
-                    log.warn("Detected node should be included in completed gateway set");
-                }
-            } else {
-                if (!completedGws.contains(gateway)) {
-                    finalGws.add(gateway);
-                    osRouterAdminService.floatingIps().forEach(fip -> {
-                        if (fip.getPortId() != null) {
-                            setFloatingIpArpRule(fip, fip.getPortId(),
-                                                 finalGws, false);
-                        }
-                    });
-                    finalGws.remove(gateway);
-                    if (!completedGws.isEmpty()) {
-                        osRouterAdminService.floatingIps().forEach(fip -> {
-                            if (fip.getPortId() != null) {
-                                setFloatingIpArpRule(fip, fip.getPortId(),
-                                                     finalGws, true);
-                            }
-                        });
-                    }
-                } else {
-                    log.warn("Detected node should NOT be included in completed gateway set");
-                }
-            }
-        }
-    }
-
-    /**
-     * Installs/uninstalls ARP flow rules to the corresponding gateway by
-     * looking for compute node's device ID.
-     *
-     * @param fip       floating IP
-     * @param port      instance port
-     * @param gateways  a collection of gateways
-     * @param install   install flag
-     */
-    private void setFloatingIpArpRuleWithPortEvent(NetFloatingIP fip,
-                                                   InstancePort port,
-                                                   Set<OpenstackNode> gateways,
-                                                   boolean install) {
-        if (ARP_BROADCAST_MODE.equals(getArpMode())) {
-
-            OpenstackNode gw = getGwByInstancePort(gateways, port);
-
-            if (gw == null) {
-                return;
-            }
-
-            String macString = osNetworkAdminService.port(fip.getPortId()).getMacAddress();
-
-            setArpRule(fip, MacAddress.valueOf(macString), gw, install);
-        }
-    }
-
-    /**
-     * Installs static ARP rules used in ARP BROAD_CAST mode.
-     * Note that, those rules will be only matched ARP_REQUEST packets,
-     * used for telling gateway node the mapped MAC address of requested IP,
-     * without the helps from controller.
-     *
-     * @param fip       floating IP address
-     * @param portId    port identifier
-     * @param gateways  a set of gateway nodes
-     * @param install   flow rule installation flag
-     */
-    private synchronized void setFloatingIpArpRule(NetFloatingIP fip, String portId,
-                                                   Set<OpenstackNode> gateways,
-                                                   boolean install) {
-        if (ARP_BROADCAST_MODE.equals(getArpMode())) {
-
-            if (fip == null) {
-                log.warn("Failed to set ARP broadcast rule for floating IP");
-                return;
-            }
-
-            if (portId == null || (install && fip.getPortId() == null)) {
-                log.trace("Unknown target ARP request for {}, ignore it",
-                                                    fip.getFloatingIpAddress());
-                return;
-            }
-
-            InstancePort instPort = instancePortService.instancePort(portId);
-            MacAddress targetMac = instPort.macAddress();
-
-            OpenstackNode gw = getGwByInstancePort(gateways, instPort);
-
-            if (gw == null) {
-                return;
-            }
-
-            if (install) {
-                preCommitPortService.subscribePreCommit(instPort.portId(),
-                        OPENSTACK_PORT_PRE_REMOVE, this.getClass().getName());
-                log.info("Subscribed the port {} on listening pre-remove event",
-                                                                instPort.portId());
-            } else {
-                preCommitPortService.unsubscribePreCommit(instPort.portId(),
-                        OPENSTACK_PORT_PRE_REMOVE, instancePortService,
-                                                        this.getClass().getName());
-                log.info("Unsubscribed the port {} on listening pre-remove event",
-                                                                instPort.portId());
-            }
-
-            setArpRule(fip, targetMac, gw, install);
-        }
-    }
-
-    private void setArpRule(NetFloatingIP fip, MacAddress targetMac,
-                            OpenstackNode gateway, boolean install) {
-        TrafficSelector selector = DefaultTrafficSelector.builder()
-                .matchInPort(gateway.uplinkPortNum())
-                .matchEthType(EthType.EtherType.ARP.ethType().toShort())
-                .matchArpOp(ARP.OP_REQUEST)
-                .matchArpTpa(Ip4Address.valueOf(fip.getFloatingIpAddress()))
-                .build();
-
-        Device device = deviceService.getDevice(gateway.intgBridge());
-
-        TrafficTreatment treatment = DefaultTrafficTreatment.builder()
-                .extension(buildMoveEthSrcToDstExtension(device), device.id())
-                .extension(buildMoveArpShaToThaExtension(device), device.id())
-                .extension(buildMoveArpSpaToTpaExtension(device), device.id())
-                .setArpOp(ARP.OP_REPLY)
-                .setEthSrc(targetMac)
-                .setArpSha(targetMac)
-                .setArpSpa(Ip4Address.valueOf(fip.getFloatingIpAddress()))
-                .setOutput(PortNumber.IN_PORT)
-                .build();
-
-        osFlowRuleService.setRule(
-                appId,
-                gateway.intgBridge(),
-                selector,
-                treatment,
-                PRIORITY_ARP_GATEWAY_RULE,
-                GW_COMMON_TABLE,
-                install
-        );
-
-        if (install) {
-            log.info("Install ARP Rule for Floating IP {}",
-                    fip.getFloatingIpAddress());
-        } else {
-            log.info("Uninstall ARP Rule for Floating IP {}",
-                    fip.getFloatingIpAddress());
-        }
-    }
-
     private void setFakeGatewayArpRuleByRouter(Router router, boolean install) {
         if (ARP_BROADCAST_MODE.equals(getArpMode())) {
             IpAddress externalIp = externalGatewayIp(router, osNetworkService);
@@ -695,20 +499,6 @@
                     // remove a gateway from an existing router
                     eventExecutor.execute(() -> processRouterGwRemoval(event));
                     break;
-                case OPENSTACK_FLOATING_IP_CREATED:
-                    // during floating IP creation, if the floating IP is
-                    // associated with any port of VM, then we will set
-                    // floating IP related ARP rules to gateway node
-                case OPENSTACK_FLOATING_IP_ASSOCIATED:
-                    eventExecutor.execute(() -> processFloatingIpCreation(event));
-                    break;
-                case OPENSTACK_FLOATING_IP_REMOVED:
-                    // during floating IP deletion, if the floating IP is
-                    // still associated with any port of VM, then we will
-                    // remove floating IP related ARP rules from gateway node
-                case OPENSTACK_FLOATING_IP_DISASSOCIATED:
-                    eventExecutor.execute(() -> processFloatingIpRemoval(event));
-                    break;
                 default:
                     // do nothing for the other events
                     break;
@@ -731,149 +521,6 @@
 
             setFakeGatewayArpRuleByRouter(event.subject(), false);
         }
-
-        private void processFloatingIpCreation(OpenstackRouterEvent event) {
-            if (!isRelevantHelper()) {
-                return;
-            }
-
-            if (getValidPortId(event) == null) {
-                return;
-            }
-
-            Set<OpenstackNode> completedGws = osNodeService.completeNodes(GATEWAY);
-
-            // associate a floating IP with an existing VM
-            setFloatingIpArpRule(event.floatingIp(),
-                    getValidPortId(event), completedGws, true);
-        }
-
-        private void processFloatingIpRemoval(OpenstackRouterEvent event) {
-            if (!isRelevantHelper()) {
-                return;
-            }
-
-            if (getValidPortId(event) == null) {
-                return;
-            }
-
-            Set<OpenstackNode> completedGws = osNodeService.completeNodes(GATEWAY);
-
-            // disassociate a floating IP with an existing VM
-            setFloatingIpArpRule(event.floatingIp(),
-                    getValidPortId(event), completedGws, false);
-        }
-
-        private String getValidPortId(OpenstackRouterEvent event) {
-            NetFloatingIP osFip = event.floatingIp();
-            String portId = osFip.getPortId();
-
-            if (Strings.isNullOrEmpty(portId)) {
-                portId = event.portId();
-            }
-
-            if (portId != null && instancePortService.instancePort(portId) != null) {
-                return portId;
-            }
-
-            return null;
-        }
-    }
-
-    private class InternalInstancePortListener implements InstancePortListener {
-
-        private boolean isRelevantHelper() {
-            return Objects.equals(localNodeId, leadershipService.getLeader(appId.name()));
-        }
-
-        @Override
-        public void event(InstancePortEvent event) {
-            InstancePort instPort = event.subject();
-            switch (event.type()) {
-                case OPENSTACK_INSTANCE_PORT_DETECTED:
-                    eventExecutor.execute(() ->
-                                processInstanceDetection(event, instPort));
-
-                    break;
-                case OPENSTACK_INSTANCE_MIGRATION_STARTED:
-                    eventExecutor.execute(() ->
-                                processInstanceMigrationStart(event, instPort));
-
-                    break;
-                case OPENSTACK_INSTANCE_MIGRATION_ENDED:
-                    eventExecutor.execute(() ->
-                                processInstanceMigrationEnd(event, instPort));
-                    break;
-                default:
-                    break;
-            }
-        }
-
-        private void processInstanceDetection(InstancePortEvent event,
-                                              InstancePort instPort) {
-            if (!isRelevantHelper()) {
-                return;
-            }
-
-            osRouterAdminService.floatingIps().stream()
-                    .filter(f -> f.getPortId() != null)
-                    .filter(f -> f.getPortId().equals(instPort.portId()))
-                    .forEach(f -> setFloatingIpArpRule(f, instPort.portId(),
-                            osNodeService.completeNodes(GATEWAY), true));
-        }
-
-        private void processInstanceMigrationStart(InstancePortEvent event,
-                                                   InstancePort instPort) {
-            if (!isRelevantHelper()) {
-                return;
-            }
-
-            NetFloatingIP fip = associatedFloatingIp(instPort,
-                    osRouterAdminService.floatingIps());
-
-            if (osNodeService.completeNodes(GATEWAY).size() == 1) {
-                return;
-            }
-
-            if (fip != null && isAssociatedWithVM(osNetworkService, fip)) {
-                setFloatingIpArpRuleWithPortEvent(fip, event.subject(),
-                        osNodeService.completeNodes(GATEWAY), true);
-            }
-        }
-
-        private void processInstanceMigrationEnd(InstancePortEvent event,
-                                                 InstancePort instPort) {
-            if (!isRelevantHelper()) {
-                return;
-            }
-
-            NetFloatingIP fip = associatedFloatingIp(instPort,
-                    osRouterAdminService.floatingIps());
-            Set<OpenstackNode> gateways = osNodeService.completeNodes(GATEWAY);
-
-            InstancePort revisedInstPort = swapStaleLocation(event.subject());
-
-            if (gateways.size() == 1) {
-                return;
-            }
-
-            if (fip != null && isAssociatedWithVM(osNetworkService, fip)) {
-                DeviceId newDeviceId = event.subject().deviceId();
-                DeviceId oldDeviceId = revisedInstPort.deviceId();
-
-                OpenstackNode oldGw =
-                        getGwByComputeDevId(gateways, oldDeviceId);
-                OpenstackNode newGw =
-                        getGwByComputeDevId(gateways, newDeviceId);
-
-                if (oldGw != null && oldGw.equals(newGw)) {
-                    return;
-                }
-
-                setFloatingIpArpRuleWithPortEvent(fip,
-                        revisedInstPort, gateways, false);
-            }
-        }
     }
 
     private class InternalNodeEventListener implements OpenstackNodeListener {
@@ -908,7 +555,6 @@
                 return;
             }
             setDefaultArpRule(node, true);
-            setFloatingIpArpRuleForGateway(node, true);
             sendGratuitousArpToSwitch(event.subject(), true);
         }
 
@@ -917,7 +563,6 @@
                 return;
             }
             setDefaultArpRule(node, false);
-            setFloatingIpArpRuleForGateway(node, false);
             sendGratuitousArpToSwitch(event.subject(), false);
         }
 
diff --git a/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackRoutingFloatingIpHandler.java b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackRoutingFloatingIpHandler.java
index cfaadd7..6e9d71b 100644
--- a/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackRoutingFloatingIpHandler.java
+++ b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackRoutingFloatingIpHandler.java
@@ -18,15 +18,21 @@
 import com.google.common.base.Strings;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Sets;
+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.MacAddress;
 import org.onlab.packet.VlanId;
+import org.onosproject.cfg.ComponentConfigService;
+import org.onosproject.cfg.ConfigProperty;
 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.net.Device;
 import org.onosproject.net.DeviceId;
 import org.onosproject.net.PortNumber;
 import org.onosproject.net.device.DeviceService;
@@ -72,8 +78,10 @@
 
 import static java.util.concurrent.Executors.newSingleThreadExecutor;
 import static org.onlab.util.Tools.groupedThreads;
+import static org.onosproject.openstacknetworking.api.Constants.ARP_BROADCAST_MODE;
 import static org.onosproject.openstacknetworking.api.Constants.GW_COMMON_TABLE;
 import static org.onosproject.openstacknetworking.api.Constants.OPENSTACK_NETWORKING_APP_ID;
+import static org.onosproject.openstacknetworking.api.Constants.PRIORITY_ARP_GATEWAY_RULE;
 import static org.onosproject.openstacknetworking.api.Constants.PRIORITY_EXTERNAL_FLOATING_ROUTING_RULE;
 import static org.onosproject.openstacknetworking.api.Constants.PRIORITY_FLOATING_EXTERNAL;
 import static org.onosproject.openstacknetworking.api.Constants.ROUTING_TABLE;
@@ -85,15 +93,20 @@
 import static org.onosproject.openstacknetworking.api.OpenstackNetwork.Type.VLAN;
 import static org.onosproject.openstacknetworking.api.OpenstackNetwork.Type.VXLAN;
 import static org.onosproject.openstacknetworking.api.OpenstackNetworkEvent.Type.OPENSTACK_PORT_PRE_REMOVE;
+import static org.onosproject.openstacknetworking.impl.OsgiPropertyConstants.ARP_MODE;
 import static org.onosproject.openstacknetworking.util.OpenstackNetworkingUtil.associatedFloatingIp;
 import static org.onosproject.openstacknetworking.util.OpenstackNetworkingUtil.externalPeerRouterForNetwork;
 import static org.onosproject.openstacknetworking.util.OpenstackNetworkingUtil.getGwByComputeDevId;
 import static org.onosproject.openstacknetworking.util.OpenstackNetworkingUtil.getGwByInstancePort;
+import static org.onosproject.openstacknetworking.util.OpenstackNetworkingUtil.getPropertyValue;
 import static org.onosproject.openstacknetworking.util.OpenstackNetworkingUtil.isAssociatedWithVM;
 import static org.onosproject.openstacknetworking.util.OpenstackNetworkingUtil.processGarpPacketForFloatingIp;
 import static org.onosproject.openstacknetworking.util.OpenstackNetworkingUtil.swapStaleLocation;
 import static org.onosproject.openstacknetworking.util.OpenstackNetworkingUtil.tunnelPortNumByNetId;
 import static org.onosproject.openstacknetworking.util.RulePopulatorUtil.buildExtension;
+import static org.onosproject.openstacknetworking.util.RulePopulatorUtil.buildMoveArpShaToThaExtension;
+import static org.onosproject.openstacknetworking.util.RulePopulatorUtil.buildMoveArpSpaToTpaExtension;
+import static org.onosproject.openstacknetworking.util.RulePopulatorUtil.buildMoveEthSrcToDstExtension;
 import static org.onosproject.openstacknode.api.OpenstackNode.NodeType.GATEWAY;
 
 /**
@@ -142,6 +155,9 @@
     @Reference(cardinality = ReferenceCardinality.MANDATORY)
     protected PacketService packetService;
 
+    @Reference(cardinality = ReferenceCardinality.MANDATORY)
+    protected ComponentConfigService configService;
+
     private final ExecutorService eventExecutor = newSingleThreadExecutor(
             groupedThreads(this.getClass().getSimpleName(), "event-handler", log));
     private final OpenstackRouterListener
@@ -186,6 +202,12 @@
         log.info("Stopped");
     }
 
+    private String getArpMode() {
+        Set<ConfigProperty> properties =
+                configService.getProperties(OpenstackRoutingArpHandler.class.getName());
+        return getPropertyValue(properties, ARP_MODE);
+    }
+
     private void setFloatingIpRules(NetFloatingIP floatingIp,
                                     InstancePort instPort,
                                     OpenstackNode gateway,
@@ -484,6 +506,51 @@
                 PRIORITY_FLOATING_EXTERNAL,
                 GW_COMMON_TABLE,
                 install);
+
+        setArpRule(floatingIp, instPort.macAddress(), selectedGatewayNode, install);
+    }
+
+    private void setArpRule(NetFloatingIP floatingIp, MacAddress targetMac,
+                            OpenstackNode gateway, boolean install) {
+        if (ARP_BROADCAST_MODE.equals(getArpMode())) {
+            TrafficSelector selector = DefaultTrafficSelector.builder()
+                    .matchInPort(gateway.uplinkPortNum())
+                    .matchEthType(EthType.EtherType.ARP.ethType().toShort())
+                    .matchArpOp(ARP.OP_REQUEST)
+                    .matchArpTpa(Ip4Address.valueOf(floatingIp.getFloatingIpAddress()))
+                    .build();
+
+            Device device = deviceService.getDevice(gateway.intgBridge());
+
+            TrafficTreatment treatment = DefaultTrafficTreatment.builder()
+                    .extension(buildMoveEthSrcToDstExtension(device), device.id())
+                    .extension(buildMoveArpShaToThaExtension(device), device.id())
+                    .extension(buildMoveArpSpaToTpaExtension(device), device.id())
+                    .setArpOp(ARP.OP_REPLY)
+                    .setEthSrc(targetMac)
+                    .setArpSha(targetMac)
+                    .setArpSpa(Ip4Address.valueOf(floatingIp.getFloatingIpAddress()))
+                    .setOutput(PortNumber.IN_PORT)
+                    .build();
+
+            osFlowRuleService.setRule(
+                    appId,
+                    gateway.intgBridge(),
+                    selector,
+                    treatment,
+                    PRIORITY_ARP_GATEWAY_RULE,
+                    GW_COMMON_TABLE,
+                    install
+            );
+
+            if (install) {
+                log.info("Install ARP Rule for Floating IP {}",
+                        floatingIp.getFloatingIpAddress());
+            } else {
+                log.info("Uninstall ARP Rule for Floating IP {}",
+                        floatingIp.getFloatingIpAddress());
+            }
+        }
     }
 
     private void setUpstreamRules(NetFloatingIP floatingIp, Network osNet,