Fix: resolve ARP resolution issue in ARP broadcast mode for VLAN

Change-Id: I2ce61b07aec9639e98c5a182244200df3b978cfc
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 06265bd..9afae59 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
@@ -15,7 +15,6 @@
  */
 package org.onosproject.openstacknetworking.impl;
 
-import com.google.common.base.Strings;
 import org.onlab.packet.ARP;
 import org.onlab.packet.EthType;
 import org.onlab.packet.Ethernet;
@@ -290,27 +289,15 @@
      * by adding a fake gateway MAC address as Source Hardware Address.
      *
      * @param osSubnet  openstack subnet
+     * @param network   openstack network
      * @param install   flag which indicates whether to install rule or remove rule
+     * @param osNode    openstack node
      */
-    private void setFakeGatewayArpRule(Subnet osSubnet, boolean install, OpenstackNode osNode) {
+    private void setFakeGatewayArpRule(Subnet osSubnet, Network network,
+                                       boolean install, OpenstackNode osNode) {
 
         if (ARP_BROADCAST_MODE.equals(getArpMode())) {
 
-            // do not remove fake gateway ARP rules, if there is another gateway
-            // which has the same subnet that to be removed
-            // this only occurs if we have duplicated subnets associated with
-            // different networks
-            if (!install) {
-                long numOfDupGws = osNetworkService.subnets().stream()
-                        .filter(s -> !s.getId().equals(osSubnet.getId()))
-                        .filter(s -> s.getGateway() != null)
-                        .filter(s -> s.getGateway().equals(osSubnet.getGateway()))
-                        .count();
-                if (numOfDupGws > 0) {
-                    return;
-                }
-            }
-
             String gateway = osSubnet.getGateway();
             if (gateway == null) {
                 return;
@@ -319,11 +306,24 @@
             TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder();
             TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
 
-            NetworkType type = NetworkType.valueOf(osNetworkService.networkType(osSubnet.getNetworkId()));
-
-            if (type.equals(NetworkType.VLAN)) {
-                sBuilder.matchVlanId(VlanId.vlanId(osNetworkService.segmentId(osSubnet.getNetworkId())));
+            if (NetworkType.VLAN == network.getNetworkType()) {
+                sBuilder.matchVlanId(VlanId.vlanId(network.getProviderSegID()));
                 tBuilder.popVlan();
+            } else if (NetworkType.VXLAN == network.getNetworkType()) {
+                // do not remove fake gateway ARP rules, if there is another gateway
+                // which has the same subnet that to be removed
+                // this only occurs if we have duplicated subnets associated with
+                // different networks
+                if (!install) {
+                    long numOfDupGws = osNetworkService.subnets().stream()
+                            .filter(s -> !s.getId().equals(osSubnet.getId()))
+                            .filter(s -> s.getGateway() != null)
+                            .filter(s -> s.getGateway().equals(osSubnet.getGateway()))
+                            .count();
+                    if (numOfDupGws > 0) {
+                        return;
+                    }
+                }
             }
 
             sBuilder.matchEthType(EthType.EtherType.ARP.ethType().toShort())
@@ -376,8 +376,7 @@
                 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
+                setArpRequestRuleForVlan(port, install);
                 break;
             default:
                 break;
@@ -428,6 +427,36 @@
     }
 
     /**
+     * Installs flow rules to match ARP request packets for VLAN.
+     *
+     * @param port      instance port
+     * @param install   installation flag
+     */
+    private void setArpRequestRuleForVlan(InstancePort port, boolean install) {
+        TrafficSelector selector = getArpRequestSelectorForVlan(port);
+
+        setLocalArpRequestTreatmentForVlan(selector, port, install);
+        setRemoteArpRequestTreatmentForVlan(selector, port, install);
+    }
+
+    /**
+     * Obtains the ARP request selector for VLAN.
+     *
+     * @param port instance port
+     * @return traffic selector
+     */
+    private TrafficSelector getArpRequestSelectorForVlan(InstancePort port) {
+        String segId = osNetworkService.segmentId(port.networkId());
+
+        return DefaultTrafficSelector.builder()
+                .matchEthType(EthType.EtherType.ARP.ethType().toShort())
+                .matchArpOp(ARP.OP_REQUEST)
+                .matchArpTpa(port.ipAddress().getIp4Address())
+                .matchVlanId(VlanId.vlanId(segId))
+                .build();
+    }
+
+    /**
      * Installs flow rules to match ARP reply packets only for VxLAN.
      *
      * @param port      instance port
@@ -437,7 +466,9 @@
 
         OpenstackNode localNode = osNodeService.node(port.deviceId());
 
-        TrafficSelector selector = setArpReplyRuleForVnet(port, install);
+        TrafficSelector selector = getArpReplySelectorForVxlan(port);
+
+        setLocalArpReplyTreatmentForVxlan(selector, port, install);
         setRemoteArpTreatmentForVxlan(selector, port, localNode, install);
     }
 
@@ -449,20 +480,85 @@
      */
     private void setArpReplyRuleForVlan(InstancePort port, boolean install) {
 
-        TrafficSelector selector = setArpReplyRuleForVnet(port, install);
-        setRemoteArpTreatmentForVlan(selector, port, install);
+        TrafficSelector selector = getArpReplySelectorForVlan(port);
+
+        setLocalArpReplyTreatmentForVlan(selector, port, install);
+        setRemoteArpReplyTreatmentForVlan(selector, port, install);
     }
 
     // a helper method
-    private TrafficSelector setArpReplyRuleForVnet(InstancePort port, boolean install) {
-        TrafficSelector selector = DefaultTrafficSelector.builder()
+    private TrafficSelector getArpReplySelectorForVxlan(InstancePort port) {
+        return getArpReplySelectorForVnet(port, NetworkType.VXLAN);
+    }
+
+    // a helper method
+    private TrafficSelector getArpReplySelectorForVlan(InstancePort port) {
+        return getArpReplySelectorForVnet(port, NetworkType.VLAN);
+    }
+
+    // a helper method
+    private TrafficSelector getArpReplySelectorForVnet(InstancePort port,
+                                                       NetworkType type) {
+
+        TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder();
+
+        if (type == NetworkType.VLAN) {
+            String segId = osNetworkService.network(port.networkId()).getProviderSegID();
+            sBuilder.matchVlanId(VlanId.vlanId(segId));
+        }
+
+        return sBuilder
                 .matchEthType(EthType.EtherType.ARP.ethType().toShort())
                 .matchArpOp(ARP.OP_REPLY)
                 .matchArpTpa(port.ipAddress().getIp4Address())
                 .matchArpTha(port.macAddress())
                 .build();
+    }
 
+    // a helper method
+    private void setLocalArpReplyTreatmentForVxlan(TrafficSelector selector,
+                                                   InstancePort port,
+                                                   boolean install) {
+        setLocalArpReplyTreatmentForVnet(selector, port, NetworkType.VXLAN, install);
+    }
+
+    // a helper method
+    private void setLocalArpReplyTreatmentForVlan(TrafficSelector selector,
+                                                  InstancePort port,
+                                                  boolean install) {
+        setLocalArpReplyTreatmentForVnet(selector, port, NetworkType.VLAN, install);
+    }
+
+    // a helper method
+    private void setLocalArpReplyTreatmentForVnet(TrafficSelector selector,
+                                                  InstancePort port,
+                                                  NetworkType type,
+                                                  boolean install) {
+        TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
+
+        if (type == NetworkType.VLAN) {
+            tBuilder.popVlan();
+        }
+
+        tBuilder.setOutput(port.portNumber());
+
+        osFlowRuleService.setRule(
+                appId,
+                port.deviceId(),
+                selector,
+                tBuilder.build(),
+                PRIORITY_ARP_REPLY_RULE,
+                ARP_TABLE,
+                install
+        );
+    }
+
+    // a helper method
+    private void setLocalArpRequestTreatmentForVlan(TrafficSelector selector,
+                                                    InstancePort port,
+                                                    boolean install) {
         TrafficTreatment treatment = DefaultTrafficTreatment.builder()
+                .popVlan()
                 .setOutput(port.portNumber())
                 .build();
 
@@ -471,12 +567,10 @@
                 port.deviceId(),
                 selector,
                 treatment,
-                PRIORITY_ARP_REPLY_RULE,
+                PRIORITY_ARP_REQUEST_RULE,
                 ARP_TABLE,
                 install
         );
-
-        return selector;
     }
 
     // a helper method
@@ -509,9 +603,36 @@
     }
 
     // a helper method
+    private void setRemoteArpRequestTreatmentForVlan(TrafficSelector selector,
+                                                     InstancePort port,
+                                                     boolean install) {
+        setRemoteArpTreatmentForVlan(selector, port, ARP.OP_REQUEST, install);
+    }
+
+    // a helper method
+    private void setRemoteArpReplyTreatmentForVlan(TrafficSelector selector,
+                                                   InstancePort port,
+                                                   boolean install) {
+        setRemoteArpTreatmentForVlan(selector, port, ARP.OP_REPLY, install);
+    }
+
+    // a helper method
     private void setRemoteArpTreatmentForVlan(TrafficSelector selector,
                                               InstancePort port,
+                                              short arpOp,
                                               boolean install) {
+
+        int priority;
+        if (arpOp == ARP.OP_REQUEST) {
+            priority = PRIORITY_ARP_REQUEST_RULE;
+        } else if (arpOp == ARP.OP_REPLY) {
+            priority = PRIORITY_ARP_REPLY_RULE;
+        } else {
+            // if ARP op does not match with any operation mode, we simply
+            // configure the ARP request rule priority
+            priority = PRIORITY_ARP_REQUEST_RULE;
+        }
+
         for (OpenstackNode remoteNode : osNodeService.completeNodes(COMPUTE)) {
             if (!remoteNode.intgBridge().equals(port.deviceId()) && remoteNode.vlanIntf() != null) {
                 TrafficTreatment treatmentToRemote = DefaultTrafficTreatment.builder()
@@ -523,7 +644,7 @@
                         remoteNode.intgBridge(),
                         selector,
                         treatmentToRemote,
-                        PRIORITY_ARP_REQUEST_RULE,
+                        priority,
                         ARP_TABLE,
                         install);
             }
@@ -573,28 +694,18 @@
 
         @Override
         public boolean isRelevant(OpenstackNetworkEvent event) {
-            return event.subnet() != null;
-        }
-
-        private boolean isRelevantHelper(Subnet osSubnet) {
-            Network network = osNetworkService.network(osSubnet.getNetworkId());
+            Network network = event.subject();
 
             if (network == null) {
                 log.warn("Network is not specified.");
                 return false;
             } else {
-                if (network.getNetworkType().equals(NetworkType.FLAT)) {
-                    return false;
-                }
+                return network.getNetworkType() != NetworkType.FLAT;
             }
+        }
 
-            // do not allow to proceed without leadership
-            NodeId leader = leadershipService.getLeader(appId.name());
-            if (!Objects.equals(localNodeId, leader)) {
-                return false;
-            }
-
-            return !Strings.isNullOrEmpty(osSubnet.getGateway());
+        private boolean isRelevantHelper() {
+            return Objects.equals(localNodeId, leadershipService.getLeader(appId.name()));
         }
 
         @Override
@@ -604,21 +715,23 @@
                 case OPENSTACK_SUBNET_UPDATED:
                     eventExecutor.execute(() -> {
 
-                        if (!isRelevantHelper(event.subnet())) {
+                        if (!isRelevantHelper()) {
                             return;
                         }
 
-                        setFakeGatewayArpRule(event.subnet(), true, null);
+                        setFakeGatewayArpRule(event.subnet(), event.subject(),
+                                true, null);
                     });
                     break;
                 case OPENSTACK_SUBNET_REMOVED:
                     eventExecutor.execute(() -> {
 
-                        if (!isRelevantHelper(event.subnet())) {
+                        if (!isRelevantHelper()) {
                             return;
                         }
 
-                        setFakeGatewayArpRule(event.subnet(), false, null);
+                        setFakeGatewayArpRule(event.subnet(), event.subject(),
+                                false, null);
                     });
                     break;
                 case OPENSTACK_NETWORK_CREATED:
@@ -692,11 +805,14 @@
                     // ARP packets generated by FLAT typed VM should not be
                     // delegated to switch to handle
                     osNetworkService.subnets().stream().filter(subnet ->
-                            osNetworkService.network(subnet.getNetworkId()) != null &&
-                                    osNetworkService.network(subnet.getNetworkId())
-                                            .getNetworkType() != NetworkType.FLAT)
-                                            .forEach(subnet ->
-                        setFakeGatewayArpRule(subnet, install, osNode));
+                        osNetworkService.network(subnet.getNetworkId()) != null &&
+                            osNetworkService.network(subnet.getNetworkId())
+                                    .getNetworkType() != NetworkType.FLAT)
+                                    .forEach(subnet -> {
+                                        String netId = subnet.getNetworkId();
+                                        Network net = osNetworkService.network(netId);
+                                        setFakeGatewayArpRule(subnet, net, install, osNode);
+                                    });
                     break;
                 default:
                     log.warn("Invalid ARP mode {}. Please use either " +