CORD-349 Support VLAN cross-connect traffic

Change related to this topic:
- Support VLAN cross-connect traffic
    Utilize ports subjectClass to achieve. For non-xConnect port, set interface VLAN to -1
- Remove VLAN checking since we have multiple VLANs per port
- Hash the L2 interface group key generation to include VLAN as well
- Update the network-cfg.json sample

Other refactoring changes:
- Read next objective stores from srManager directly
- Use constant for flow priority
- CORD-267 Javadoc fix

Change-Id: I4ca8c2d9c8b3633a4a0101c5070d19343f7e5b90
diff --git a/src/main/java/org/onosproject/segmentrouting/RoutingRulePopulator.java b/src/main/java/org/onosproject/segmentrouting/RoutingRulePopulator.java
index 03b29a5..6017e8b 100644
--- a/src/main/java/org/onosproject/segmentrouting/RoutingRulePopulator.java
+++ b/src/main/java/org/onosproject/segmentrouting/RoutingRulePopulator.java
@@ -23,6 +23,7 @@
 import org.onlab.packet.MacAddress;
 import org.onlab.packet.MplsLabel;
 import org.onlab.packet.VlanId;
+import org.onosproject.net.ConnectPoint;
 import org.onosproject.segmentrouting.config.DeviceConfigNotFoundException;
 import org.onosproject.segmentrouting.config.DeviceConfiguration;
 import org.onosproject.segmentrouting.grouphandler.NeighborSet;
@@ -49,11 +50,15 @@
 import java.util.ArrayList;
 import java.util.HashSet;
 import java.util.List;
+import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.atomic.AtomicLong;
 
 import static com.google.common.base.Preconditions.checkNotNull;
 
+/**
+ * Populator of segment routing flow rules.
+ */
 public class RoutingRulePopulator {
     private static final Logger log = LoggerFactory
             .getLogger(RoutingRulePopulator.class);
@@ -63,6 +68,10 @@
     private DeviceConfiguration config;
 
     private static final int HIGHEST_PRIORITY = 0xffff;
+    //
+    private static final int XCONNECT_PRIORITY = 1000;
+    private static final int DEFAULT_PRIORITY = 100;
+    private static final int FLOOD_PRIORITY = 5;
     private static final long OFPP_MAX = 0xffffff00L;
 
 
@@ -120,6 +129,14 @@
         rulePopulationCounter.incrementAndGet();
     }
 
+    /**
+     * Removes IP rules for host when the host is gone.
+     *
+     * @param deviceId device ID of the device that host attaches to
+     * @param hostIp IP address of the host
+     * @param hostMac MAC address of the host
+     * @param outPort port that host attaches to
+     */
     public void revokeIpRuleForHost(DeviceId deviceId, Ip4Address hostIp,
             MacAddress hostMac, PortNumber outPort) {
         log.debug("Revoke IP table entry for host {} at {}:{}",
@@ -175,7 +192,7 @@
                 .withSelector(selector)
                 .nextStep(portNextObjId)
                 .fromApp(srManager.appId).makePermanent()
-                .withPriority(100).withFlag(ForwardingObjective.Flag.SPECIFIC);
+                .withPriority(DEFAULT_PRIORITY).withFlag(ForwardingObjective.Flag.SPECIFIC);
     }
 
     /**
@@ -369,7 +386,7 @@
         for (ForwardingObjective.Builder fwdObjBuilder : fwdObjBuilders) {
             ((Builder) ((Builder) fwdObjBuilder.fromApp(srManager.appId)
                     .makePermanent()).withSelector(selector)
-                    .withPriority(100))
+                    .withPriority(DEFAULT_PRIORITY))
                     .withFlag(ForwardingObjective.Flag.SPECIFIC);
             srManager.flowObjectiveService.
                 forward(deviceId,
@@ -464,7 +481,8 @@
                 FilteringObjective.Builder fob = DefaultFilteringObjective.builder();
                 fob.withKey(Criteria.matchInPort(port.number()))
                 .addCondition(Criteria.matchEthDst(deviceMac))
-                .addCondition(Criteria.matchVlanId(VlanId.NONE));
+                .addCondition(Criteria.matchVlanId(VlanId.NONE))
+                .withPriority(DEFAULT_PRIORITY);
                 // vlan assignment is valid only if this instance is master
                 if (srManager.mastershipService.isLocalMaster(deviceId)) {
                     TrafficTreatment tt = DefaultTrafficTreatment.builder()
@@ -558,7 +576,7 @@
             fob.withFlag(Flag.SPECIFIC)
                     .withSelector(sbuilder.build())
                     .nextStep(nextId)
-                    .withPriority(5)
+                    .withPriority(FLOOD_PRIORITY)
                     .fromApp(srManager.appId)
                     .makePermanent();
 
@@ -572,6 +590,86 @@
         });
     }
 
+    /**
+     * Creates a filtering objective to permit VLAN cross-connect traffic.
+     *
+     * @param deviceId the DPID of the switch
+     */
+    public void populateXConnectVlanFilters(DeviceId deviceId) {
+        Map<VlanId, List<ConnectPoint>> xConnectsForDevice =
+                config.getXConnects();
+        xConnectsForDevice.forEach((vlanId, connectPoints) -> {
+            // Only proceed  the xConnect for given device
+            for (ConnectPoint connectPoint : connectPoints) {
+                if (!connectPoint.deviceId().equals(deviceId)) {
+                    return;
+                }
+            }
+
+            connectPoints.forEach(connectPoint -> {
+                FilteringObjective.Builder fob = DefaultFilteringObjective.builder();
+                fob.withKey(Criteria.matchInPort(connectPoint.port()))
+                        .addCondition(Criteria.matchVlanId(vlanId))
+                        .addCondition(Criteria.matchEthDst(MacAddress.NONE))
+                        .withPriority(XCONNECT_PRIORITY);
+
+                fob.permit().fromApp(srManager.appId);
+                srManager.flowObjectiveService
+                        .filter(deviceId, fob.add(new SRObjectiveContext(deviceId,
+                                SRObjectiveContext.ObjectiveType.FILTER)));
+            });
+        });
+    }
+
+    /**
+     * Populates a forwarding objective that points the VLAN cross-connect
+     * packets to a broadcast group.
+     *
+     * @param deviceId switch ID to set the rules
+     */
+    public void populateXConnectBroadcastRule(DeviceId deviceId) {
+        Map<VlanId, List<ConnectPoint>> xConnects =
+                config.getXConnects();
+        xConnects.forEach((vlanId, connectPoints) -> {
+            // Only proceed  the xConnect for given device
+            for (ConnectPoint connectPoint : connectPoints) {
+                if (!connectPoint.deviceId().equals(deviceId)) {
+                    return;
+                }
+            }
+
+            int nextId = srManager.getXConnectNextObjectiveId(deviceId, vlanId);
+            if (nextId < 0) {
+                log.error("Cannot install cross-connect broadcast rule in dev:{} " +
+                        "due to missing nextId:{}", deviceId, nextId);
+                return;
+            }
+
+            /*
+             * Driver should treat objectives with MacAddress.NONE and !VlanId.NONE
+             * as the VLAN cross-connect broadcast rules
+             */
+            TrafficSelector.Builder sbuilder = DefaultTrafficSelector.builder();
+            sbuilder.matchVlanId(vlanId);
+            sbuilder.matchEthDst(MacAddress.NONE);
+
+            ForwardingObjective.Builder fob = DefaultForwardingObjective.builder();
+            fob.withFlag(Flag.SPECIFIC)
+                    .withSelector(sbuilder.build())
+                    .nextStep(nextId)
+                    .withPriority(DEFAULT_PRIORITY)
+                    .fromApp(srManager.appId)
+                    .makePermanent();
+
+            srManager.flowObjectiveService.forward(
+                    deviceId,
+                    fob.add(new SRObjectiveContext(
+                            deviceId,
+                            SRObjectiveContext.ObjectiveType.FORWARDING)
+                    )
+            );
+        });
+    }
 
     private static class SRObjectiveContext implements ObjectiveContext {
         enum ObjectiveType {