Add explicit flow rules to receive control packets: ARP, LLDP, BDDP

This fixes ONOS-540

NOTES:
 * Currently, the flow rules are pushed by each module that needs to receive
   the corresponding control packets:
   - ARP: ProxyArpManager, HostLocationProvider
   - LLDP and BDDP: LLDPLinkProvider
 * Pushing the corresponding IPv6 rules for Neighbor Discovery is not done yet
 * In the future, we might want to consider an explicit service to
   subscribe for receiving particular control packets

Change-Id: I292ad11a2e48390624f381c278e55e5d0af93c6d
diff --git a/providers/lldp/src/main/java/org/onosproject/provider/lldp/impl/LLDPLinkProvider.java b/providers/lldp/src/main/java/org/onosproject/provider/lldp/impl/LLDPLinkProvider.java
index 0fe9676..0169e02 100644
--- a/providers/lldp/src/main/java/org/onosproject/provider/lldp/impl/LLDPLinkProvider.java
+++ b/providers/lldp/src/main/java/org/onosproject/provider/lldp/impl/LLDPLinkProvider.java
@@ -22,6 +22,9 @@
 import org.apache.felix.scr.annotations.Property;
 import org.apache.felix.scr.annotations.Reference;
 import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.onlab.packet.Ethernet;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.core.CoreService;
 import org.onosproject.mastership.MastershipEvent;
 import org.onosproject.mastership.MastershipListener;
 import org.onosproject.mastership.MastershipService;
@@ -32,6 +35,13 @@
 import org.onosproject.net.device.DeviceEvent;
 import org.onosproject.net.device.DeviceListener;
 import org.onosproject.net.device.DeviceService;
+import org.onosproject.net.flow.DefaultFlowRule;
+import org.onosproject.net.flow.DefaultTrafficSelector;
+import org.onosproject.net.flow.DefaultTrafficTreatment;
+import org.onosproject.net.flow.FlowRule;
+import org.onosproject.net.flow.FlowRuleService;
+import org.onosproject.net.flow.TrafficSelector;
+import org.onosproject.net.flow.TrafficTreatment;
 import org.onosproject.net.link.LinkProvider;
 import org.onosproject.net.link.LinkProviderRegistry;
 import org.onosproject.net.link.LinkProviderService;
@@ -67,7 +77,6 @@
 @Component(immediate = true)
 public class LLDPLinkProvider extends AbstractProvider implements LinkProvider {
 
-
     private static final String PROP_USE_BDDP = "useBDDP";
 
     private static final String PROP_LLDP_SUPPRESSION = "lldpSuppression";
@@ -76,6 +85,14 @@
 
     private final Logger log = getLogger(getClass());
 
+    private static final int FLOW_RULE_PRIORITY = 40000;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected CoreService coreService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected FlowRuleService flowRuleService;
+
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected LinkProviderRegistry providerRegistry;
 
@@ -111,6 +128,7 @@
     protected final Map<DeviceId, LinkDiscovery> discoverers = new ConcurrentHashMap<>();
 
     private SuppressionRules rules;
+    private ApplicationId appId;
 
     /**
      * Creates an OpenFlow link provider.
@@ -121,6 +139,9 @@
 
     @Activate
     public void activate() {
+        appId =
+            coreService.registerApplication("org.onosproject.provider.lldp");
+
         loadSuppressionRules();
 
         providerService = providerRegistry.register(this);
@@ -153,6 +174,8 @@
         executor.scheduleAtFixedRate(new SyncDeviceInfoTask(), INIT_DELAY,
                 DELAY, TimeUnit.SECONDS);
 
+        pushRules();
+
         log.info("Started");
     }
 
@@ -210,6 +233,48 @@
         // should refresh discoverers when we need dynamic reconfiguration
     }
 
+    /**
+     * Pushes flow rules to all devices.
+     */
+    private void pushRules() {
+        for (Device device : deviceService.getDevices()) {
+            pushRules(device);
+        }
+    }
+
+    /**
+     * Pushes flow rules to the device to receive control packets that need
+     * to be processed.
+     *
+     * @param device the device to push the rules to
+     */
+    private synchronized void pushRules(Device device) {
+        TrafficSelector.Builder sbuilder;
+        TrafficTreatment.Builder tbuilder;
+
+        // Get all LLDP packets
+        sbuilder = DefaultTrafficSelector.builder();
+        tbuilder = DefaultTrafficTreatment.builder();
+        sbuilder.matchEthType(Ethernet.TYPE_LLDP);
+        tbuilder.punt();
+        FlowRule flowLldp =
+            new DefaultFlowRule(device.id(),
+                                sbuilder.build(), tbuilder.build(),
+                                FLOW_RULE_PRIORITY, appId, 0, true);
+
+        // Get all BDDP packets
+        sbuilder = DefaultTrafficSelector.builder();
+        tbuilder = DefaultTrafficTreatment.builder();
+        sbuilder.matchEthType(Ethernet.TYPE_BSN);
+        tbuilder.punt();
+        FlowRule flowBddp =
+            new DefaultFlowRule(device.id(),
+                                sbuilder.build(), tbuilder.build(),
+                                FLOW_RULE_PRIORITY, appId, 0, true);
+
+        flowRuleService.applyFlowRules(flowLldp, flowBddp);
+    }
+
     private class InternalRoleListener implements MastershipListener {
 
         @Override
@@ -258,6 +323,8 @@
             final DeviceId deviceId = device.id();
             switch (event.type()) {
                 case DEVICE_ADDED:
+                    pushRules(device);
+                    // FALLTHROUGH
                 case DEVICE_UPDATED:
                     synchronized (discoverers) {
                         ld = discoverers.get(deviceId);