Add CLI for reconfiguring ARP mode on openstacknetworking app

Change-Id: I4211681ccf6eaea9c76ec27adc45e0f1cc71d0d7
diff --git a/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/cli/ArpModeCompleter.java b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/cli/ArpModeCompleter.java
new file mode 100644
index 0000000..1edc547
--- /dev/null
+++ b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/cli/ArpModeCompleter.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2018-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.openstacknetworking.cli;
+
+import com.google.common.collect.Lists;
+import org.onosproject.cli.AbstractChoicesCompleter;
+
+import java.util.List;
+
+/**
+ * ARP mode completer.
+ */
+public class ArpModeCompleter extends AbstractChoicesCompleter {
+
+    @Override
+    protected List<String> choices() {
+        List<String> strings = Lists.newArrayList();
+        strings.add("proxy");
+        strings.add("broadcast");
+        return strings;
+    }
+}
diff --git a/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/cli/OpenstackConfigArpModeCommand.java b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/cli/OpenstackConfigArpModeCommand.java
new file mode 100644
index 0000000..1f22d4d
--- /dev/null
+++ b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/cli/OpenstackConfigArpModeCommand.java
@@ -0,0 +1,131 @@
+/*
+ * Copyright 2018-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.openstacknetworking.cli;
+
+import org.apache.karaf.shell.commands.Argument;
+import org.apache.karaf.shell.commands.Command;
+import org.onosproject.cfg.ComponentConfigService;
+import org.onosproject.cli.AbstractShellCommand;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.core.CoreService;
+import org.onosproject.net.flow.FlowRuleService;
+import org.onosproject.openstacknetworking.api.Constants;
+import org.onosproject.openstacknetworking.impl.OpenstackRoutingArpHandler;
+import org.onosproject.openstacknetworking.impl.OpenstackSwitchingArpHandler;
+import org.onosproject.openstacknode.api.NodeState;
+import org.onosproject.openstacknode.api.OpenstackNode;
+import org.onosproject.openstacknode.api.OpenstackNodeAdminService;
+import org.onosproject.openstacknode.api.OpenstackNodeService;
+
+import static com.google.common.base.Strings.isNullOrEmpty;
+import static org.onosproject.openstacknetworking.util.OpenstackNetworkingUtil.getPropertyValue;
+
+/**
+ * Configure ARP mode.
+ */
+@Command(scope = "onos", name = "openstack-config-arp-mode",
+        description = "Re-configure ARP mode (proxy | broadcast)")
+public class OpenstackConfigArpModeCommand extends AbstractShellCommand {
+
+    private static final String ARP_MODE_NAME = "arpMode";
+    private static final String PROXY_MODE = "proxy";
+    private static final String BROADCAST_MODE = "broadcast";
+
+    @Argument(index = 0, name = "arpMode",
+            description = "ARP mode (proxy | broadcast)",
+            required = true, multiValued = false)
+    String arpMode = null;
+
+    @Override
+    protected void execute() {
+
+        if (checkArpMode(arpMode)) {
+            configArpMode();
+
+            ComponentConfigService service = get(ComponentConfigService.class);
+            String switchingComponent = OpenstackSwitchingArpHandler.class.getName();
+            String routingComponent = OpenstackRoutingArpHandler.class.getName();
+
+            // we check the arpMode configured in each component, and purge and
+            // reinstall all rules only if the arpMode is changed to the configured one
+            while (true) {
+                String switchingValue =
+                        getPropertyValue(service.getProperties(switchingComponent), ARP_MODE_NAME);
+                String routingValue =
+                        getPropertyValue(service.getProperties(routingComponent), ARP_MODE_NAME);
+
+                if (arpMode.equals(switchingValue) && arpMode.equals(routingValue)) {
+                    break;
+                }
+            }
+
+            purgeRules();
+            syncRules();
+        }
+    }
+
+    private boolean checkArpMode(String arpMode) {
+
+        if (isNullOrEmpty(arpMode)) {
+            error("ARP mode should not be empty string or null");
+            return false;
+        } else {
+            if (arpMode.equals(PROXY_MODE) || arpMode.equals(BROADCAST_MODE)) {
+                return true;
+            } else {
+                error("ARP mode should be either proxy or broadcast");
+                return false;
+            }
+        }
+    }
+
+    private void purgeRules() {
+        FlowRuleService flowRuleService = AbstractShellCommand.get(FlowRuleService.class);
+        CoreService coreService = AbstractShellCommand.get(CoreService.class);
+        ApplicationId appId = coreService.getAppId(Constants.OPENSTACK_NETWORKING_APP_ID);
+        if (appId == null) {
+            error("Failed to purge OpenStack networking flow rules.");
+            return;
+        }
+        flowRuleService.removeFlowRulesById(appId);
+    }
+
+    private void configArpMode() {
+        ComponentConfigService service = get(ComponentConfigService.class);
+        String switchingComponent = OpenstackSwitchingArpHandler.class.getName();
+        String routingComponent = OpenstackRoutingArpHandler.class.getName();
+
+        if (!isNullOrEmpty(arpMode)) {
+            service.setProperty(switchingComponent, ARP_MODE_NAME, arpMode);
+            service.setProperty(routingComponent, ARP_MODE_NAME, arpMode);
+        }
+    }
+
+    private void syncRules() {
+        // All handlers in this application reacts the node complete event and
+        // tries to re-configure flow rules for the complete node.
+        OpenstackNodeService osNodeService = AbstractShellCommand.get(OpenstackNodeService.class);
+        OpenstackNodeAdminService osNodeAdminService = AbstractShellCommand.get(OpenstackNodeAdminService.class);
+        if (osNodeService == null) {
+            error("Failed to re-install flow rules for OpenStack networking.");
+            return;
+        }
+        osNodeService.completeNodes().forEach(osNode -> {
+            OpenstackNode updated = osNode.updateState(NodeState.INIT);
+            osNodeAdminService.updateNode(updated);
+        });
+    }
+}
diff --git a/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackFlowRuleManager.java b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackFlowRuleManager.java
index 018b1c2..4c5b0b7 100644
--- a/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackFlowRuleManager.java
+++ b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackFlowRuleManager.java
@@ -137,7 +137,7 @@
 
             @Override
             public void onError(FlowRuleOperations ops) {
-                log.debug("Failed to privision vni or forwarding table");
+                log.debug("Failed to provision vni or forwarding table");
             }
         }));
     }
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 d7bcc7f..74bf681 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
@@ -32,8 +32,8 @@
 import org.onlab.packet.Ip4Address;
 import org.onlab.packet.IpAddress;
 import org.onlab.packet.MacAddress;
-import org.onlab.util.Tools;
 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;
@@ -77,7 +77,6 @@
 import org.slf4j.Logger;
 
 import java.nio.ByteBuffer;
-import java.util.Dictionary;
 import java.util.Map;
 import java.util.Objects;
 import java.util.Set;
@@ -97,6 +96,7 @@
 import static org.onosproject.openstacknetworking.util.OpenstackNetworkingUtil.associatedFloatingIp;
 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.openstacknode.api.OpenstackNode.NodeType.GATEWAY;
 import static org.slf4j.LoggerFactory.getLogger;
@@ -146,8 +146,6 @@
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected ComponentConfigService configService;
 
-    // TODO: need to find a way to unify aprMode and gatewayMac variables with
-    // that in SwitchingArpHandler
     @Property(name = ARP_MODE, value = DEFAULT_ARP_MODE_STR,
             label = "ARP processing mode, broadcast | proxy (default)")
     protected String arpMode = DEFAULT_ARP_MODE_STR;
@@ -205,21 +203,19 @@
     // that in SwitchingArpHandler
     @Modified
     void modified(ComponentContext context) {
-        Dictionary<?, ?> properties = context.getProperties();
-        String updateArpMode;
-
-        updateArpMode = Tools.get(properties, ARP_MODE);
-        if (!Strings.isNullOrEmpty(updateArpMode) && !updateArpMode.equals(arpMode)) {
-            arpMode = updateArpMode;
-        }
 
         log.info("Modified");
     }
 
+    private String getArpMode() {
+        Set<ConfigProperty> properties = configService.getProperties(this.getClass().getName());
+        return getPropertyValue(properties, ARP_MODE);
+    }
+
     private void processArpPacket(PacketContext context, Ethernet ethernet) {
         ARP arp = (ARP) ethernet.getPayload();
 
-        if (arp.getOpCode() == ARP.OP_REQUEST && arpMode.equals(ARP_PROXY_MODE)) {
+        if (arp.getOpCode() == ARP.OP_REQUEST && ARP_PROXY_MODE.equals(getArpMode())) {
             if (log.isTraceEnabled()) {
                 log.trace("ARP request received from {} for {}",
                         Ip4Address.valueOf(arp.getSenderProtocolAddress()).toString(),
@@ -381,7 +377,7 @@
      * @param install flow rule installation flag
      */
     private void setFloatingIpArpRuleForGateway(OpenstackNode gateway, boolean install) {
-        if (arpMode.equals(ARP_BROADCAST_MODE)) {
+        if (ARP_BROADCAST_MODE.equals(getArpMode())) {
 
             Set<OpenstackNode> completedGws = osNodeService.completeNodes(GATEWAY);
             Set<OpenstackNode> finalGws = Sets.newConcurrentHashSet();
@@ -442,7 +438,7 @@
                                                    InstancePort port,
                                                    Set<OpenstackNode> gateways,
                                                    boolean install) {
-        if (arpMode.equals(ARP_BROADCAST_MODE)) {
+        if (ARP_BROADCAST_MODE.equals(getArpMode())) {
 
             OpenstackNode gw = getGwByInstancePort(gateways, port);
 
@@ -469,7 +465,7 @@
     private synchronized void setFloatingIpArpRule(NetFloatingIP fip,
                                                    Set<OpenstackNode> gateways,
                                                    boolean install) {
-        if (arpMode.equals(ARP_BROADCAST_MODE)) {
+        if (ARP_BROADCAST_MODE.equals(getArpMode())) {
 
             if (fip == null) {
                 log.warn("Failed to set ARP broadcast rule for floating IP");
@@ -671,7 +667,7 @@
         }
 
         private void setFakeGatewayArpRule(ExternalGateway extGw, boolean install) {
-            if (arpMode.equals(ARP_BROADCAST_MODE)) {
+            if (ARP_BROADCAST_MODE.equals(getArpMode())) {
 
                 if (extGw == null) {
                     return;
@@ -859,7 +855,7 @@
         }
 
         private void setDefaultArpRule(OpenstackNode osNode, boolean install) {
-            switch (arpMode) {
+            switch (getArpMode()) {
                 case ARP_PROXY_MODE:
                     setDefaultArpRuleForProxyMode(osNode, install);
                     break;
@@ -868,7 +864,7 @@
                     break;
                 default:
                     log.warn("Invalid ARP mode {}. Please use either " +
-                            "broadcast or proxy mode.", arpMode);
+                            "broadcast or proxy mode.", getArpMode());
                     break;
             }
         }
diff --git a/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackSwitchingArpHandler.java b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackSwitchingArpHandler.java
index a58053ac..4e6054f 100644
--- a/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackSwitchingArpHandler.java
+++ b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackSwitchingArpHandler.java
@@ -32,6 +32,7 @@
 import org.onlab.packet.MacAddress;
 import org.onlab.util.Tools;
 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;
@@ -84,6 +85,7 @@
 import static org.onosproject.openstacknetworking.api.Constants.PRIORITY_ARP_REPLY_RULE;
 import static org.onosproject.openstacknetworking.api.Constants.PRIORITY_ARP_REQUEST_RULE;
 import static org.onosproject.openstacknetworking.api.Constants.PRIORITY_ARP_SUBNET_RULE;
+import static org.onosproject.openstacknetworking.util.OpenstackNetworkingUtil.getPropertyValue;
 import static org.onosproject.openstacknetworking.util.RulePopulatorUtil.buildExtension;
 import static org.onosproject.openstacknode.api.OpenstackNode.NodeType.COMPUTE;
 
@@ -189,24 +191,16 @@
 
     @Modified
     void modified(ComponentContext context) {
-        Dictionary<?, ?> properties = context.getProperties();
-        String updatedMac;
-
-        updatedMac = Tools.get(properties, GATEWAY_MAC);
-        if (!Strings.isNullOrEmpty(updatedMac) && !updatedMac.equals(gatewayMac)) {
-            gatewayMac = updatedMac;
-        }
-
-        String updateArpMode;
-
-        updateArpMode = Tools.get(properties, ARP_MODE);
-        if (!Strings.isNullOrEmpty(updateArpMode) && !updateArpMode.equals(arpMode)) {
-            arpMode = updateArpMode;
-        }
+        readComponentConfiguration(context);
 
         log.info("Modified");
     }
 
+    private String getArpMode() {
+        Set<ConfigProperty> properties = configService.getProperties(this.getClass().getName());
+        return getPropertyValue(properties, ARP_MODE);
+    }
+
     private void addSubnetGateway(Subnet osSubnet) {
         if (Strings.isNullOrEmpty(osSubnet.getGateway())) {
             return;
@@ -236,7 +230,7 @@
     private void processPacketIn(PacketContext context, Ethernet ethPacket) {
 
         // if the ARP mode is configured as broadcast mode, we simply ignore ARP packet_in
-        if (arpMode.equals(ARP_BROADCAST_MODE)) {
+        if (ARP_BROADCAST_MODE.equals(getArpMode())) {
             return;
         }
 
@@ -304,7 +298,7 @@
      */
     private void setFakeGatewayArpRule(Subnet osSubnet, boolean install, OpenstackNode osNode) {
 
-        if (arpMode.equals(ARP_BROADCAST_MODE)) {
+        if (ARP_BROADCAST_MODE.equals(getArpMode())) {
             String gateway = osSubnet.getGateway();
 
             TrafficSelector selector = DefaultTrafficSelector.builder()
@@ -348,6 +342,184 @@
     }
 
     /**
+     * Installs flow rules to match ARP request packets.
+     *
+     * @param port      instance port
+     * @param install   installation flag
+     */
+    private void setArpRequestRule(InstancePort port, boolean install) {
+        NetworkType type = osNetworkService.network(port.networkId()).getNetworkType();
+
+        switch (type) {
+            case VXLAN:
+                setRemoteArpRequestRuleForVxlan(port, install);
+                break;
+            case VLAN:
+                // since VLAN ARP packet can be broadcasted to all hosts that connected with L2 network,
+                // there is no need to add any flow rules to handle ARP request
+                break;
+            default:
+                break;
+        }
+    }
+
+    /**
+     * Installs flow rules to match ARP reply packets.
+     *
+     * @param port      instance port
+     * @param install   installation flag
+     */
+    private void setArpReplyRule(InstancePort port, boolean install) {
+        NetworkType type = osNetworkService.network(port.networkId()).getNetworkType();
+
+        switch (type) {
+            case VXLAN:
+                setArpReplyRuleForVxlan(port, install);
+                break;
+            case VLAN:
+                setArpReplyRuleForVlan(port, install);
+                break;
+            default:
+                break;
+        }
+    }
+
+    /**
+     * Installs flow rules to match ARP request packets only for VxLAN.
+     *
+     * @param port      instance port
+     * @param install   installation flag
+     */
+    private void setRemoteArpRequestRuleForVxlan(InstancePort port, boolean install) {
+
+        OpenstackNode localNode = osNodeService.node(port.deviceId());
+
+        TrafficSelector selector = DefaultTrafficSelector.builder()
+                .matchEthType(EthType.EtherType.ARP.ethType().toShort())
+                .matchArpOp(ARP.OP_REQUEST)
+                .matchArpTpa(port.ipAddress().getIp4Address())
+                .build();
+
+        setRemoteArpTreatmentForVxlan(selector, port, localNode, install);
+    }
+
+    /**
+     * Installs flow rules to match ARP reply packets only for VxLAN.
+     *
+     * @param port      instance port
+     * @param install   installation flag
+     */
+    private void setArpReplyRuleForVxlan(InstancePort port, boolean install) {
+
+        OpenstackNode localNode = osNodeService.node(port.deviceId());
+
+        TrafficSelector selector = setArpReplyRuleForVnet(port, install);
+        setRemoteArpTreatmentForVxlan(selector, port, localNode, install);
+    }
+
+    /**
+     * Installs flow rules to match ARP reply packets only for VLAN.
+     *
+     * @param port      instance port
+     * @param install   installation flag
+     */
+    private void setArpReplyRuleForVlan(InstancePort port, boolean install) {
+
+        TrafficSelector selector = setArpReplyRuleForVnet(port, install);
+        setRemoteArpTreatmentForVlan(selector, port, install);
+    }
+
+    // a helper method
+    private TrafficSelector setArpReplyRuleForVnet(InstancePort port, boolean install) {
+        TrafficSelector selector = DefaultTrafficSelector.builder()
+                .matchEthType(EthType.EtherType.ARP.ethType().toShort())
+                .matchArpOp(ARP.OP_REPLY)
+                .matchArpTpa(port.ipAddress().getIp4Address())
+                .matchArpTha(port.macAddress())
+                .build();
+
+        TrafficTreatment treatment = DefaultTrafficTreatment.builder()
+                .setOutput(port.portNumber())
+                .build();
+
+        osFlowRuleService.setRule(
+                appId,
+                port.deviceId(),
+                selector,
+                treatment,
+                PRIORITY_ARP_REPLY_RULE,
+                DHCP_ARP_TABLE,
+                install
+        );
+
+        return selector;
+    }
+
+    // a helper method
+    private void setRemoteArpTreatmentForVxlan(TrafficSelector selector,
+                                               InstancePort port,
+                                               OpenstackNode localNode,
+                                               boolean install) {
+        for (OpenstackNode remoteNode : osNodeService.completeNodes(COMPUTE)) {
+            if (!remoteNode.intgBridge().equals(port.deviceId())) {
+                TrafficTreatment treatmentToRemote = DefaultTrafficTreatment.builder()
+                        .extension(buildExtension(
+                                deviceService,
+                                remoteNode.intgBridge(),
+                                localNode.dataIp().getIp4Address()),
+                                remoteNode.intgBridge())
+                        .setOutput(remoteNode.tunnelPortNum())
+                        .build();
+
+                osFlowRuleService.setRule(
+                        appId,
+                        remoteNode.intgBridge(),
+                        selector,
+                        treatmentToRemote,
+                        PRIORITY_ARP_REQUEST_RULE,
+                        DHCP_ARP_TABLE,
+                        install
+                );
+            }
+        }
+    }
+
+    // a helper method
+    private void setRemoteArpTreatmentForVlan(TrafficSelector selector,
+                                              InstancePort port,
+                                              boolean install) {
+        for (OpenstackNode remoteNode : osNodeService.completeNodes(COMPUTE)) {
+            if (!remoteNode.intgBridge().equals(port.deviceId()) && remoteNode.vlanIntf() != null) {
+                TrafficTreatment treatmentToRemote = DefaultTrafficTreatment.builder()
+                        .setOutput(remoteNode.vlanPortNum())
+                        .build();
+
+                osFlowRuleService.setRule(
+                        appId,
+                        remoteNode.intgBridge(),
+                        selector,
+                        treatmentToRemote,
+                        PRIORITY_ARP_REQUEST_RULE,
+                        DHCP_ARP_TABLE,
+                        install);
+            }
+        }
+    }
+
+    /**
+     * Extracts properties from the component configuration context.
+     *
+     * @param context the component context
+     */
+    private void readComponentConfiguration(ComponentContext context) {
+        Dictionary<?, ?> properties = context.getProperties();
+
+        String updatedMac = Tools.get(properties, GATEWAY_MAC);
+        gatewayMac = updatedMac != null ? updatedMac : DEFAULT_GATEWAY_MAC_STR;
+        log.info("Configured. Gateway MAC is {}", gatewayMac);
+    }
+
+    /**
      * An internal packet processor which processes ARP request, and results in
      * packet-out ARP reply.
      */
@@ -446,9 +618,11 @@
             switch (event.type()) {
                 case OPENSTACK_NODE_COMPLETE:
                     setDefaultArpRule(osNode, true);
+                    setAllArpRules(true);
                     break;
                 case OPENSTACK_NODE_INCOMPLETE:
                     setDefaultArpRule(osNode, false);
+                    setAllArpRules(false);
                     break;
                 default:
                     break;
@@ -456,7 +630,7 @@
         }
 
         private void setDefaultArpRule(OpenstackNode osNode, boolean install) {
-            switch (arpMode) {
+            switch (getArpMode()) {
                 case ARP_PROXY_MODE:
                     setDefaultArpRuleForProxyMode(osNode, install);
                     break;
@@ -469,13 +643,13 @@
                     osNetworkService.subnets().stream().filter(subnet ->
                             osNetworkService.network(subnet.getNetworkId()) != null &&
                                     osNetworkService.network(subnet.getNetworkId())
-                                            .getNetworkType() == NetworkType.FLAT)
+                                            .getNetworkType() != NetworkType.FLAT)
                                             .forEach(subnet ->
                         setFakeGatewayArpRule(subnet, install, osNode));
                     break;
                 default:
                     log.warn("Invalid ARP mode {}. Please use either " +
-                            "broadcast or proxy mode.", arpMode);
+                            "broadcast or proxy mode.", getArpMode());
                     break;
             }
         }
@@ -520,6 +694,15 @@
                     install
             );
         }
+
+        private void setAllArpRules(boolean install) {
+            if (ARP_BROADCAST_MODE.equals(getArpMode())) {
+                instancePortService.instancePorts().forEach(p -> {
+                    setArpRequestRule(p, install);
+                    setArpReplyRule(p, install);
+                });
+            }
+        }
     }
 
     /**
@@ -534,7 +717,7 @@
         @Override
         public boolean isRelevant(InstancePortEvent event) {
 
-            if (arpMode.equals(ARP_PROXY_MODE)) {
+            if (ARP_PROXY_MODE.equals(getArpMode())) {
                 return false;
             }
 
@@ -561,170 +744,5 @@
                     break;
             }
         }
-
-        /**
-         * Installs flow rules to match ARP request packets.
-         *
-         * @param port      instance port
-         * @param install   installation flag
-         */
-        private void setArpRequestRule(InstancePort port, boolean install) {
-            NetworkType type = osNetworkService.network(port.networkId()).getNetworkType();
-
-            switch (type) {
-                case VXLAN:
-                    setRemoteArpRequestRuleForVxlan(port, install);
-                    break;
-                case VLAN:
-                    // since VLAN ARP packet can be broadcasted to all hosts that connected with L2 network,
-                    // there is no need to add any flow rules to handle ARP request
-                    break;
-                default:
-                    break;
-            }
-        }
-
-        /**
-         * Installs flow rules to match ARP reply packets.
-         *
-         * @param port      instance port
-         * @param install   installation flag
-         */
-        private void setArpReplyRule(InstancePort port, boolean install) {
-            NetworkType type = osNetworkService.network(port.networkId()).getNetworkType();
-
-            switch (type) {
-                case VXLAN:
-                    setArpReplyRuleForVxlan(port, install);
-                    break;
-                case VLAN:
-                    setArpReplyRuleForVlan(port, install);
-                    break;
-                default:
-                    break;
-            }
-        }
-
-        /**
-         * Installs flow rules to match ARP request packets only for VxLAN.
-         *
-         * @param port      instance port
-         * @param install   installation flag
-         */
-        private void setRemoteArpRequestRuleForVxlan(InstancePort port, boolean install) {
-
-            OpenstackNode localNode = osNodeService.node(port.deviceId());
-
-            TrafficSelector selector = DefaultTrafficSelector.builder()
-                    .matchEthType(EthType.EtherType.ARP.ethType().toShort())
-                    .matchArpOp(ARP.OP_REQUEST)
-                    .matchArpTpa(port.ipAddress().getIp4Address())
-                    .build();
-
-            setRemoteArpTreatmentForVxlan(selector, port, localNode, install);
-        }
-
-        /**
-         * Installs flow rules to match ARP reply packets only for VxLAN.
-         *
-         * @param port      instance port
-         * @param install   installation flag
-         */
-        private void setArpReplyRuleForVxlan(InstancePort port, boolean install) {
-
-            OpenstackNode localNode = osNodeService.node(port.deviceId());
-
-            TrafficSelector selector = setArpReplyRuleForVnet(port, install);
-            setRemoteArpTreatmentForVxlan(selector, port, localNode, install);
-        }
-
-        /**
-         * Installs flow rules to match ARP reply packets only for VLAN.
-         *
-         * @param port      instance port
-         * @param install   installation flag
-         */
-        private void setArpReplyRuleForVlan(InstancePort port, boolean install) {
-
-            TrafficSelector selector = setArpReplyRuleForVnet(port, install);
-            setRemoteArpTreatmentForVlan(selector, port, install);
-        }
-
-        // a helper method
-        private TrafficSelector setArpReplyRuleForVnet(InstancePort port, boolean install) {
-            TrafficSelector selector = DefaultTrafficSelector.builder()
-                    .matchEthType(EthType.EtherType.ARP.ethType().toShort())
-                    .matchArpOp(ARP.OP_REPLY)
-                    .matchArpTpa(port.ipAddress().getIp4Address())
-                    .matchArpTha(port.macAddress())
-                    .build();
-
-            TrafficTreatment treatment = DefaultTrafficTreatment.builder()
-                    .setOutput(port.portNumber())
-                    .build();
-
-            osFlowRuleService.setRule(
-                    appId,
-                    port.deviceId(),
-                    selector,
-                    treatment,
-                    PRIORITY_ARP_REPLY_RULE,
-                    DHCP_ARP_TABLE,
-                    install
-            );
-
-            return selector;
-        }
-
-        // a helper method
-        private void setRemoteArpTreatmentForVxlan(TrafficSelector selector,
-                                                   InstancePort port,
-                                                   OpenstackNode localNode,
-                                                   boolean install) {
-            for (OpenstackNode remoteNode : osNodeService.completeNodes(COMPUTE)) {
-                if (!remoteNode.intgBridge().equals(port.deviceId())) {
-                    TrafficTreatment treatmentToRemote = DefaultTrafficTreatment.builder()
-                            .extension(buildExtension(
-                                    deviceService,
-                                    remoteNode.intgBridge(),
-                                    localNode.dataIp().getIp4Address()),
-                                    remoteNode.intgBridge())
-                            .setOutput(remoteNode.tunnelPortNum())
-                            .build();
-
-                    osFlowRuleService.setRule(
-                            appId,
-                            remoteNode.intgBridge(),
-                            selector,
-                            treatmentToRemote,
-                            PRIORITY_ARP_REQUEST_RULE,
-                            DHCP_ARP_TABLE,
-                            install
-                    );
-                }
-            }
-        }
-
-        // a helper method
-        private void setRemoteArpTreatmentForVlan(TrafficSelector selector,
-                                                  InstancePort port,
-                                                  boolean install) {
-            for (OpenstackNode remoteNode : osNodeService.completeNodes(COMPUTE)) {
-                if (!remoteNode.intgBridge().equals(port.deviceId()) && remoteNode.vlanIntf() != null) {
-                    TrafficTreatment treatmentToRemote = DefaultTrafficTreatment.builder()
-                            .setOutput(remoteNode.vlanPortNum())
-                            .build();
-
-                    osFlowRuleService.setRule(
-                            appId,
-                            remoteNode.intgBridge(),
-                            selector,
-                            treatmentToRemote,
-                            PRIORITY_ARP_REQUEST_RULE,
-                            DHCP_ARP_TABLE,
-                            install);
-                }
-            }
-        }
     }
 }
diff --git a/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/util/OpenstackNetworkingUtil.java b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/util/OpenstackNetworkingUtil.java
index 9914957..2d8bd12 100644
--- a/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/util/OpenstackNetworkingUtil.java
+++ b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/util/OpenstackNetworkingUtil.java
@@ -19,10 +19,11 @@
 import com.fasterxml.jackson.databind.ObjectMapper;
 import com.fasterxml.jackson.databind.node.ObjectNode;
 import com.google.common.base.Strings;
+import org.onosproject.cfg.ConfigProperty;
 import org.onosproject.net.DeviceId;
 import org.onosproject.openstacknetworking.api.InstancePort;
-import org.onosproject.openstacknetworking.api.OpenstackRouterAdminService;
 import org.onosproject.openstacknetworking.api.OpenstackNetworkService;
+import org.onosproject.openstacknetworking.api.OpenstackRouterAdminService;
 import org.onosproject.openstacknode.api.OpenstackAuth;
 import org.onosproject.openstacknode.api.OpenstackAuth.Perspective;
 import org.onosproject.openstacknode.api.OpenstackNode;
@@ -54,6 +55,7 @@
 import java.util.HashMap;
 import java.util.Iterator;
 import java.util.Map;
+import java.util.Optional;
 import java.util.Set;
 import java.util.TreeMap;
 
@@ -349,6 +351,19 @@
     }
 
     /**
+     * Obtains the property value with specified property key name.
+     *
+     * @param properties    a collection of properties
+     * @param name          key name
+     * @return mapping value
+     */
+    public static String getPropertyValue(Set<ConfigProperty> properties, String name) {
+        Optional<ConfigProperty> property =
+                properties.stream().filter(p -> p.name().equals(name)).findFirst();
+        return property.map(ConfigProperty::value).orElse(null);
+    }
+
+    /**
      * Builds up and a complete endpoint URL from gateway node.
      *
      * @param node gateway node
diff --git a/apps/openstacknetworking/app/src/main/resources/OSGI-INF/blueprint/shell-config.xml b/apps/openstacknetworking/app/src/main/resources/OSGI-INF/blueprint/shell-config.xml
index 3d6edff..77b3898 100644
--- a/apps/openstacknetworking/app/src/main/resources/OSGI-INF/blueprint/shell-config.xml
+++ b/apps/openstacknetworking/app/src/main/resources/OSGI-INF/blueprint/shell-config.xml
@@ -68,9 +68,16 @@
                 <ref component-id="ipAddressCompleter"/>
             </completers>
         </command>
+        <command>
+            <action class="org.onosproject.openstacknetworking.cli.OpenstackConfigArpModeCommand" />
+            <completers>
+                <ref component-id="arpModeCompleter"/>
+            </completers>
+        </command>
     </command-bundle>
 
     <bean id="ipAddressCompleter" class="org.onosproject.openstacknetworking.cli.IpAddressCompleter"/>
     <bean id="macAddressCompleter" class="org.onosproject.openstacknetworking.cli.MacAddressCompleter"/>
     <bean id="vlanIdCompleter" class="org.onosproject.openstacknetworking.cli.VlanIdCompleter"/>
+    <bean id="arpModeCompleter" class="org.onosproject.openstacknetworking.cli.ArpModeCompleter"/>
 </blueprint>
diff --git a/apps/openstacknetworking/app/src/test/java/org/onosproject/openstacknetworking/impl/OpenstackSwitchingArpHandlerTest.java b/apps/openstacknetworking/app/src/test/java/org/onosproject/openstacknetworking/impl/OpenstackSwitchingArpHandlerTest.java
index f46d668..59ab91c 100644
--- a/apps/openstacknetworking/app/src/test/java/org/onosproject/openstacknetworking/impl/OpenstackSwitchingArpHandlerTest.java
+++ b/apps/openstacknetworking/app/src/test/java/org/onosproject/openstacknetworking/impl/OpenstackSwitchingArpHandlerTest.java
@@ -45,7 +45,6 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.fail;
 import static org.onosproject.net.NetTestTools.connectPoint;
-import static org.onosproject.openstacknetworking.api.Constants.ARP_PROXY_MODE;
 
 public class OpenstackSwitchingArpHandlerTest {
 
@@ -68,7 +67,6 @@
         arpHandler.osNetworkService = new TestOpenstackNetworkService();
         arpHandler.osNodeService = new TestOpenstackNodeService();
         arpHandler.osFlowRuleService = new TestOpenstackFlowRuleService();
-        arpHandler.arpMode = ARP_PROXY_MODE;
         arpHandler.clusterService = new TestClusterService();
         arpHandler.leadershipService = new TestLeadershipService();
         arpHandler.activate();