Adding Multisct traffic drop for un-programmed trees

Change-Id: If9615db254f69141d8d6f0da3e0b7ce7d154bd6b
diff --git a/apps/segmentrouting/app/src/main/java/org/onosproject/segmentrouting/SegmentRoutingManager.java b/apps/segmentrouting/app/src/main/java/org/onosproject/segmentrouting/SegmentRoutingManager.java
index 5b520b9..49d2f37 100644
--- a/apps/segmentrouting/app/src/main/java/org/onosproject/segmentrouting/SegmentRoutingManager.java
+++ b/apps/segmentrouting/app/src/main/java/org/onosproject/segmentrouting/SegmentRoutingManager.java
@@ -1299,6 +1299,7 @@
             DefaultGroupHandler groupHandler = groupHandlerMap.get(deviceId);
             groupHandler.createGroupsFromVlanConfig();
             routingRulePopulator.populateSubnetBroadcastRule(deviceId);
+            mcastHandler.dropUnprogrammedTrees(deviceId);
         }
 
         appCfgHandler.init(deviceId);
diff --git a/apps/segmentrouting/app/src/main/java/org/onosproject/segmentrouting/mcast/McastHandler.java b/apps/segmentrouting/app/src/main/java/org/onosproject/segmentrouting/mcast/McastHandler.java
index 584e51d..34d7dbd 100644
--- a/apps/segmentrouting/app/src/main/java/org/onosproject/segmentrouting/mcast/McastHandler.java
+++ b/apps/segmentrouting/app/src/main/java/org/onosproject/segmentrouting/mcast/McastHandler.java
@@ -38,9 +38,9 @@
 import org.onosproject.mcast.api.McastRoute;
 import org.onosproject.mcast.api.McastRouteData;
 import org.onosproject.mcast.api.McastRouteUpdate;
-import org.onosproject.net.HostId;
 import org.onosproject.net.ConnectPoint;
 import org.onosproject.net.DeviceId;
+import org.onosproject.net.HostId;
 import org.onosproject.net.Link;
 import org.onosproject.net.Path;
 import org.onosproject.net.PortNumber;
@@ -53,6 +53,7 @@
 import org.onosproject.net.topology.TopologyService;
 import org.onosproject.segmentrouting.SRLinkWeigher;
 import org.onosproject.segmentrouting.SegmentRoutingManager;
+import org.onosproject.segmentrouting.config.DeviceConfigNotFoundException;
 import org.onosproject.store.serializers.KryoNamespaces;
 import org.onosproject.store.service.ConsistentMap;
 import org.onosproject.store.service.Serializer;
@@ -76,14 +77,12 @@
 
 import static java.util.concurrent.Executors.newScheduledThreadPool;
 import static org.onlab.util.Tools.groupedThreads;
-
 import static org.onosproject.mcast.api.McastEvent.Type.ROUTE_ADDED;
 import static org.onosproject.mcast.api.McastEvent.Type.ROUTE_REMOVED;
-import static org.onosproject.mcast.api.McastEvent.Type.SOURCES_ADDED;
-import static org.onosproject.mcast.api.McastEvent.Type.SOURCES_REMOVED;
 import static org.onosproject.mcast.api.McastEvent.Type.SINKS_ADDED;
 import static org.onosproject.mcast.api.McastEvent.Type.SINKS_REMOVED;
-
+import static org.onosproject.mcast.api.McastEvent.Type.SOURCES_ADDED;
+import static org.onosproject.mcast.api.McastEvent.Type.SOURCES_REMOVED;
 import static org.onosproject.segmentrouting.mcast.McastRole.EGRESS;
 import static org.onosproject.segmentrouting.mcast.McastRole.INGRESS;
 import static org.onosproject.segmentrouting.mcast.McastRole.TRANSIT;
@@ -273,6 +272,11 @@
         lastMcastChange = Instant.now();
         mcastLock();
         try {
+            // Installing rules to drop any multicast traffic for which a tree is not programmed.
+            srManager.deviceService.getAvailableDevices().forEach(device -> {
+                log.debug("Programming mcast drop flows");
+                dropUnprogrammedTrees(device.id());
+            });
             srManager.multicastRouteService.getRoutes().forEach(mcastRoute -> {
                 log.debug("Init group {}", mcastRoute.group());
                 if (!mcastUtils.isLeader(mcastRoute.group())) {
@@ -329,7 +333,7 @@
      * @param event McastEvent with SOURCE_ADDED type
      */
     public void processMcastEvent(McastEvent event) {
-        log.info("process {}", event);
+        log.debug("process {}", event);
         // If it is a route added, we do not enqueue
         if (event.type() == ROUTE_ADDED) {
             processRouteAddedInternal(event.subject().route().group());
@@ -847,11 +851,8 @@
             // Fast path, we can recover all the locations
             if (notRecoveredInternal.isEmpty()) {
                 mcastTree.forEach((egressDevice, paths) -> {
-                    Optional<Path> mcastPath = getPath(ingressDevice, egressDevice,
-                                                       mcastIp, paths, source);
-                    if (mcastPath.isPresent()) {
-                        installPath(mcastIp, source, mcastPath.get());
-                    }
+                    Optional<Path> mcastPath = getPath(ingressDevice, egressDevice, mcastIp, paths, source);
+                    mcastPath.ifPresent(path -> installPath(mcastIp, source, path));
                 });
             } else {
                 // Let's try to recover using alternative locations
@@ -890,15 +891,12 @@
         // Let's compute all the affected sinks and all the sinks
         notRecovered.forEach(deviceId -> {
             totalAffectedSinks.addAll(
-                    mcastUtils.getAffectedSinks(deviceId, mcastIp).values().stream()
-                            .flatMap(Collection::stream)
+                    mcastUtils.getAffectedSinks(deviceId, mcastIp).values().stream().flatMap(Collection::stream)
                             .filter(connectPoint -> connectPoint.deviceId().equals(deviceId))
-                            .collect(Collectors.toSet())
-            );
+                            .collect(Collectors.toSet()));
             totalSinks.addAll(
                     mcastUtils.getAffectedSinks(deviceId, mcastIp).values().stream()
-                            .flatMap(Collection::stream).collect(Collectors.toSet())
-            );
+                            .flatMap(Collection::stream).collect(Collectors.toSet()));
         });
         Set<ConnectPoint> sinksToBeAdded = Sets.difference(totalSinks, totalAffectedSinks);
         Set<DeviceId> newEgressDevices = sinksToBeAdded.stream()
@@ -972,8 +970,7 @@
      * @param source the source connect point
      * @return the set of the sinks to be processed
      */
-    private Set<ConnectPoint> processSinksToBeRecovered(IpAddress mcastIp,
-                                                        Map<HostId, Set<ConnectPoint>> newSinks,
+    private Set<ConnectPoint> processSinksToBeRecovered(IpAddress mcastIp, Map<HostId, Set<ConnectPoint>> newSinks,
                                                         Map<HostId, Set<ConnectPoint>> prevSinks,
                                                         ConnectPoint source) {
         final Set<ConnectPoint> sinksToBeProcessed = Sets.newHashSet();
@@ -1032,8 +1029,7 @@
                             return false;
                         }
                         ConnectPoint other = connectPoints.stream()
-                                .filter(remaining -> !remaining.equals(connectPoint))
-                                .findFirst().orElse(null);
+                                .filter(remaining -> !remaining.equals(connectPoint)).findFirst().orElse(null);
                         // We are already serving the sink
                         return !isSinkForSource(mcastIp, other, source);
                     }).findFirst().orElse(null);
@@ -1053,8 +1049,7 @@
                             return false;
                         }
                         ConnectPoint other = connectPoints.stream()
-                                .filter(remaining -> !remaining.equals(connectPoint))
-                                .findFirst().orElse(null);
+                                .filter(remaining -> !remaining.equals(connectPoint)).findFirst().orElse(null);
                         return !isSinkForSource(mcastIp, other, source);
                     }).findFirst().orElse(null);
             if (sinkToBeProcessed != null) {
@@ -1076,8 +1071,7 @@
                             return false;
                         }
                         ConnectPoint other = connectPoints.stream()
-                                .filter(remaining -> !remaining.equals(connectPoint))
-                                .findFirst().orElse(null);
+                                .filter(remaining -> !remaining.equals(connectPoint)).findFirst().orElse(null);
                         return !isSinkForSource(mcastIp, other, source);
                     }).findFirst().orElse(null);
             if (sinkToBeProcessed != null) {
@@ -1111,8 +1105,7 @@
         });
         ingressTransitPorts.forEach((source, ports) -> ports.forEach(ingressTransitPort -> {
             DeviceId ingressDevice = ingressDevices.stream()
-                    .filter(deviceId -> deviceId.equals(source.deviceId()))
-                    .findFirst().orElse(null);
+                    .filter(deviceId -> deviceId.equals(source.deviceId())).findFirst().orElse(null);
             boolean isLast = removePortFromDevice(ingressDevice, ingressTransitPort,
                                                   mcastIp, mcastUtils.assignedVlan(source));
             if (isLast) {
@@ -1122,6 +1115,25 @@
     }
 
     /**
+     * Installs flows to drop any multicast traffic for a tree that was not programmed.
+     *
+     * @param deviceId the device
+     */
+    public void dropUnprogrammedTrees(DeviceId deviceId) {
+        try {
+            if (srManager.deviceConfiguration().isEdgeDevice(deviceId)) {
+                Set<VlanId> assignedVlans  = srManager.deviceService.getPorts(deviceId).stream().map(port -> {
+                    return mcastUtils.assignedVlan(new ConnectPoint(port.element().id(), port.number()));
+                }).collect(Collectors.toSet());
+                mcastUtils.addDropFiltersToDevice(deviceId, true, assignedVlans);
+                mcastUtils.addDropFiltersToDevice(deviceId, false, assignedVlans);
+            }
+        } catch (DeviceConfigNotFoundException e) {
+            log.warn("Not installing mcast drop flows for unprogrammed trees on  {}. Absent config", deviceId);
+        }
+    }
+
+    /**
      * Adds a port to given multicast group on given device. This involves the
      * update of L3 multicast group and multicast routing table entry.
      *
@@ -1130,8 +1142,7 @@
      * @param mcastIp multicast group
      * @param assignedVlan assigned VLAN ID
      */
-    private void addPortToDevice(DeviceId deviceId, PortNumber port,
-                                 IpAddress mcastIp, VlanId assignedVlan) {
+    private void addPortToDevice(DeviceId deviceId, PortNumber port, IpAddress mcastIp, VlanId assignedVlan) {
         McastStoreKey mcastStoreKey = new McastStoreKey(mcastIp, deviceId, assignedVlan);
         ImmutableSet.Builder<PortNumber> portBuilder = ImmutableSet.builder();
         NextObjective newNextObj;
@@ -1139,8 +1150,7 @@
             // First time someone request this mcast group via this device
             portBuilder.add(port);
             // New nextObj
-            newNextObj = mcastUtils.nextObjBuilder(mcastIp, assignedVlan,
-                                        portBuilder.build(), null).add();
+            newNextObj = mcastUtils.nextObjBuilder(mcastIp, assignedVlan, portBuilder.build(), null).add();
             // Store the new port
             mcastNextObjStore.put(mcastStoreKey, newNextObj);
         } else {
@@ -1188,8 +1198,7 @@
      * @param assignedVlan assigned VLAN ID
      * @return true if this is the last sink on this device
      */
-    private boolean removePortFromDevice(DeviceId deviceId, PortNumber port,
-                                         IpAddress mcastIp, VlanId assignedVlan) {
+    private boolean removePortFromDevice(DeviceId deviceId, PortNumber port, IpAddress mcastIp, VlanId assignedVlan) {
         McastStoreKey mcastStoreKey =
                 new McastStoreKey(mcastIp, deviceId, assignedVlan);
         // This device is not serving this multicast group
@@ -1248,8 +1257,7 @@
      * @param mcastIp multicast group to be removed
      * @param assignedVlan assigned VLAN ID
      */
-    private void removeGroupFromDevice(DeviceId deviceId, IpAddress mcastIp,
-                                       VlanId assignedVlan) {
+    private void removeGroupFromDevice(DeviceId deviceId, IpAddress mcastIp, VlanId assignedVlan) {
         McastStoreKey mcastStoreKey = new McastStoreKey(mcastIp, deviceId, assignedVlan);
         // This device is not serving this multicast group
         if (!mcastNextObjStore.containsKey(mcastStoreKey)) {
@@ -1295,8 +1303,7 @@
      * @param availablePaths all the available paths towards the egress
      * @return shared links between egress devices
      */
-    private Set<Link> exploreMcastTree(Set<DeviceId> egresses,
-                                       Map<DeviceId, List<Path>> availablePaths) {
+    private Set<Link> exploreMcastTree(Set<DeviceId> egresses, Map<DeviceId, List<Path>> availablePaths) {
         int minLength = Integer.MAX_VALUE;
         int length;
         List<Path> currentPaths;
@@ -1368,8 +1375,7 @@
      * @param sinks leaves of the tree
      * @return the computed Mcast tree
      */
-    private Map<ConnectPoint, List<Path>> computeSinkMcastTree(DeviceId source,
-                                                               Set<ConnectPoint> sinks) {
+    private Map<ConnectPoint, List<Path>> computeSinkMcastTree(DeviceId source, Set<ConnectPoint> sinks) {
         // Get the egress devices, remove source from the egress if present
         Set<DeviceId> egresses = sinks.stream().map(ConnectPoint::deviceId)
                 .filter(deviceId -> !deviceId.equals(source)).collect(Collectors.toSet());
@@ -1507,8 +1513,7 @@
     private Set<DeviceId> getDevice(IpAddress mcastIp, McastRole role, ConnectPoint source) {
         return mcastRoleStore.entrySet().stream()
                 .filter(entry -> entry.getKey().mcastIp().equals(mcastIp) &&
-                        entry.getKey().source().equals(source) &&
-                        entry.getValue().value() == role)
+                        entry.getKey().source().equals(source) && entry.getValue().value() == role)
                 .map(Entry::getKey).map(McastRoleStoreKey::deviceId).collect(Collectors.toSet());
     }
 
@@ -1589,8 +1594,7 @@
      * @param source the source connect point
      * @return spine-facing port on ingress device
      */
-    private Set<PortNumber> ingressTransitPort(IpAddress mcastIp, DeviceId ingressDevice,
-                                               ConnectPoint source) {
+    private Set<PortNumber> ingressTransitPort(IpAddress mcastIp, DeviceId ingressDevice, ConnectPoint source) {
         ImmutableSet.Builder<PortNumber> portBuilder = ImmutableSet.builder();
         if (ingressDevice != null) {
             NextObjective nextObj = mcastNextObjStore.get(new McastStoreKey(mcastIp, ingressDevice,
@@ -1617,8 +1621,7 @@
      * @param source source connect point
      * @return true if the connect point is sink of the group
      */
-    private boolean isSinkForGroup(IpAddress mcastIp, ConnectPoint connectPoint,
-                                   ConnectPoint source) {
+    private boolean isSinkForGroup(IpAddress mcastIp, ConnectPoint connectPoint, ConnectPoint source) {
         VlanId assignedVlan = mcastUtils.assignedVlan(connectPoint.deviceId().equals(source.deviceId()) ?
                                                               source : null);
         McastStoreKey mcastStoreKey = new McastStoreKey(mcastIp, connectPoint.deviceId(), assignedVlan);
@@ -1637,8 +1640,7 @@
      * @param source source connect point
      * @return true if the connect point is sink of the group
      */
-    private boolean isSinkForSource(IpAddress mcastIp, ConnectPoint connectPoint,
-                                    ConnectPoint source) {
+    private boolean isSinkForSource(IpAddress mcastIp, ConnectPoint connectPoint, ConnectPoint source) {
         boolean isSink = isSinkForGroup(mcastIp, connectPoint, source);
         DeviceId device;
         if (connectPoint.deviceId().equals(source.deviceId())) {
@@ -1661,8 +1663,7 @@
      * @param source source connect point
      * @return true if the connect point is reachable from the source
      */
-    private boolean isSinkReachable(IpAddress mcastIp, ConnectPoint sink,
-                                    ConnectPoint source) {
+    private boolean isSinkReachable(IpAddress mcastIp, ConnectPoint sink, ConnectPoint source) {
         return sink.deviceId().equals(source.deviceId()) ||
                 getPath(source.deviceId(), sink.deviceId(), mcastIp, null, source).isPresent();
     }
@@ -1677,8 +1678,7 @@
      * @param vlanId assigned VLAN ID
      * @param install true to add, false to remove
      */
-    public void updateFilterToDevice(DeviceId deviceId, PortNumber portNum,
-                                        VlanId vlanId, boolean install) {
+    public void updateFilterToDevice(DeviceId deviceId, PortNumber portNum, VlanId vlanId, boolean install) {
         lastMcastChange = Instant.now();
         mcastLock();
         try {
@@ -1842,8 +1842,7 @@
      * @param sourcecp the source connect point
      * @return the mapping mcastIp-device to mcast role
      */
-    public Map<McastRoleStoreKey, McastRole> getMcastRoles(IpAddress mcastIp,
-                                                       ConnectPoint sourcecp) {
+    public Map<McastRoleStoreKey, McastRole> getMcastRoles(IpAddress mcastIp, ConnectPoint sourcecp) {
         if (mcastIp != null) {
             Map<McastRoleStoreKey, McastRole> roles = mcastRoleStore.entrySet().stream()
                     .filter(mcastEntry -> mcastIp.equals(mcastEntry.getKey().mcastIp()))
@@ -1890,8 +1889,7 @@
      * @param sourcecp the source connect point
      * @return the mapping egress point to mcast path
      */
-    public Multimap<ConnectPoint, List<ConnectPoint>> getMcastTrees(IpAddress mcastIp,
-                                                                    ConnectPoint sourcecp) {
+    public Multimap<ConnectPoint, List<ConnectPoint>> getMcastTrees(IpAddress mcastIp, ConnectPoint sourcecp) {
         Multimap<ConnectPoint, List<ConnectPoint>> mcastTrees = HashMultimap.create();
         Set<ConnectPoint> sources = mcastUtils.getSources(mcastIp);
         if (sourcecp != null) {
@@ -1920,10 +1918,9 @@
      * @param mcastIp the group ip
      * @param source the source
      */
-    private void buildMcastPaths(DeviceId toVisit, Set<DeviceId> visited,
-                                 Map<ConnectPoint, List<ConnectPoint>> mcastPaths,
-                                 List<ConnectPoint> currentPath, IpAddress mcastIp,
-                                 ConnectPoint source) {
+    private void buildMcastPaths(DeviceId toVisit, Set<DeviceId> visited, Map<ConnectPoint,
+                                 List<ConnectPoint>> mcastPaths, List<ConnectPoint> currentPath,
+                                 IpAddress mcastIp, ConnectPoint source) {
         // If we have visited the node to visit there is a loop
         if (visited.contains(toVisit)) {
             return;
diff --git a/apps/segmentrouting/app/src/main/java/org/onosproject/segmentrouting/mcast/McastUtils.java b/apps/segmentrouting/app/src/main/java/org/onosproject/segmentrouting/mcast/McastUtils.java
index 604f868..40b8514 100644
--- a/apps/segmentrouting/app/src/main/java/org/onosproject/segmentrouting/mcast/McastUtils.java
+++ b/apps/segmentrouting/app/src/main/java/org/onosproject/segmentrouting/mcast/McastUtils.java
@@ -155,6 +155,33 @@
     }
 
     /**
+     * Adds filtering objectives to drop any unknown multicast.
+     *
+     * @param deviceId      device ID
+     * @param ipv4          install a drop for Ipv4 addresses or Ipv6 addresses
+     * @param assignedVlans the vlans for wich to drop the mcast traffic
+     */
+    void addDropFiltersToDevice(DeviceId deviceId, boolean ipv4, Set<VlanId> assignedVlans) {
+        MacAddress routerMac;
+        try {
+            routerMac = srManager.deviceConfiguration().getDeviceMac(deviceId);
+        } catch (DeviceConfigNotFoundException dcnfe) {
+            log.warn("Fail to push filtering objective since device is not configured. Abort");
+            return;
+        }
+        assignedVlans.forEach(assignedVlan -> {
+            FilteringObjective.Builder filtObjBuilder = filterObjBuilderDrop(ipv4, routerMac, assignedVlan);
+            ObjectiveContext context = new DefaultObjectiveContext(
+                    (objective) -> log.debug("Successfully add filter on {}",
+                            deviceId),
+                    (objective, error) ->
+                            log.warn("Failed to add filter on {}: {}",
+                                    deviceId, error));
+            srManager.flowObjectiveService.filter(deviceId, filtObjBuilder.add(context));
+        });
+    }
+
+    /**
      * Removes filtering objective for given device and port.
      *
      * @param deviceId device ID
@@ -387,7 +414,7 @@
      * @return filtering objective builder
      */
     private FilteringObjective.Builder filterObjBuilder(PortNumber ingressPort, VlanId assignedVlan,
-                                                IpAddress mcastIp, MacAddress routerMac, McastRole mcastRole) {
+                                                        IpAddress mcastIp, MacAddress routerMac, McastRole mcastRole) {
         FilteringObjective.Builder filtBuilder = DefaultFilteringObjective.builder();
         // Let's add the in port matching and the priority
         filtBuilder.withKey(Criteria.matchInPort(ingressPort))
@@ -402,10 +429,10 @@
         // According to the IP type we set the proper match on the mac address
         if (mcastIp.isIp4()) {
             filtBuilder.addCondition(Criteria.matchEthDstMasked(MacAddress.IPV4_MULTICAST,
-                                                                MacAddress.IPV4_MULTICAST_MASK));
+                    MacAddress.IPV4_MULTICAST_MASK));
         } else {
             filtBuilder.addCondition(Criteria.matchEthDstMasked(MacAddress.IPV6_MULTICAST,
-                                                                MacAddress.IPV6_MULTICAST_MASK));
+                    MacAddress.IPV6_MULTICAST_MASK));
         }
         // We finally build the meta treatment
         TrafficTreatment tt = DefaultTrafficTreatment.builder()
@@ -418,6 +445,40 @@
     }
 
     /**
+     * Creates a filtering objective builder for multicast drop.
+     *
+     * @param ipv4         do we need to install IPv4 or v6 ?
+     * @param routerMac    router MAC. This is carried in metadata and used from some switches that
+     *                     need to put unicast entry before multicast entry in TMAC table.
+     * @param assignedVlan the vlanId to drop
+     * @return filtering objective builder
+     */
+    private FilteringObjective.Builder filterObjBuilderDrop(boolean ipv4, MacAddress routerMac, VlanId assignedVlan) {
+
+        FilteringObjective.Builder filtBuilder = DefaultFilteringObjective.builder();
+        // We match on the given vlan.
+        // If the traffic comes in tagged with an mcast specific vlan there is no rule
+        // in table 10 to handle it goes directly to 60 and it's dropped there.
+        filtBuilder.addCondition(Criteria.matchVlanId(assignedVlan));
+
+        // According to the IP type we set the proper match on the mac address
+        if (ipv4) {
+            filtBuilder.addCondition(Criteria.matchEthDstMasked(MacAddress.IPV4_MULTICAST,
+                    MacAddress.IPV4_MULTICAST_MASK));
+        } else {
+            filtBuilder.addCondition(Criteria.matchEthDstMasked(MacAddress.IPV6_MULTICAST,
+                    MacAddress.IPV6_MULTICAST_MASK));
+        }
+        // We finally build the meta treatment
+        TrafficTreatment tt = DefaultTrafficTreatment.builder()
+                .setEthDst(routerMac)
+                .build();
+        filtBuilder.withMeta(tt);
+        // Done, we return a permit filtering objective
+        return filtBuilder.permit().fromApp(srManager.appId());
+    }
+
+    /**
      * Gets output ports information from treatments.
      *
      * @param treatments collection of traffic treatments
diff --git a/drivers/default/src/main/java/org/onosproject/driver/pipeline/ofdpa/Ofdpa2Pipeline.java b/drivers/default/src/main/java/org/onosproject/driver/pipeline/ofdpa/Ofdpa2Pipeline.java
index f53610a..6e7feca 100644
--- a/drivers/default/src/main/java/org/onosproject/driver/pipeline/ofdpa/Ofdpa2Pipeline.java
+++ b/drivers/default/src/main/java/org/onosproject/driver/pipeline/ofdpa/Ofdpa2Pipeline.java
@@ -138,6 +138,8 @@
 
     protected static final int HIGHEST_PRIORITY = 0xffff;
     protected static final int DEFAULT_PRIORITY = 0x8000;
+    // Priority Introduced for Mcast Drop Rules
+    protected static final int MIDDLE_PRIORITY = 0x4000;
     protected static final int LOWEST_PRIORITY = 0x0;
 
     protected static final int MPLS_L2_PORT_PRIORITY = 2;
@@ -521,7 +523,7 @@
                 ops.newStage();
 
                 for (FlowRule flowRule : flowRules) {
-                    log.trace("{} flow rules in TMAC table: {} for dev: {}",
+                    log.debug(" {} flow rules in TMAC table: {} for dev: {}",
                             (install) ? "adding" : "removing", flowRules, deviceId);
                     if (install) {
                         ops = ops.add(flowRule);
@@ -959,19 +961,23 @@
         TrafficTreatment.Builder treatment;
         FlowRule rule;
 
+        int priority = (assignedVlan == null || assignedVlan == VlanId.ANY) ? MIDDLE_PRIORITY : DEFAULT_PRIORITY;
+
         if (IPV4_MULTICAST.equals(ethCriterion.mac())) {
             if (requireUnicastBeforeMulticast()) {
                 selector = DefaultTrafficSelector.builder();
                 treatment = DefaultTrafficTreatment.builder();
                 selector.matchEthType(Ethernet.TYPE_IPV4);
                 selector.matchEthDst(unicastMac);
-                selector.matchVlanId(assignedVlan);
+                if (assignedVlan != null) {
+                    selector.matchVlanId(assignedVlan);
+                }
                 treatment.transition(UNICAST_ROUTING_TABLE);
                 rule = DefaultFlowRule.builder()
                         .forDevice(deviceId)
                         .withSelector(selector.build())
                         .withTreatment(treatment.build())
-                        .withPriority(DEFAULT_PRIORITY)
+                        .withPriority(priority)
                         .fromApp(applicationId)
                         .makePermanent()
                         .forTable(TMAC_TABLE).build();
@@ -982,13 +988,15 @@
             treatment = DefaultTrafficTreatment.builder();
             selector.matchEthType(Ethernet.TYPE_IPV4);
             selector.matchEthDstMasked(ethCriterion.mac(), ethCriterion.mask());
-            selector.matchVlanId(assignedVlan);
+            if (assignedVlan != null) {
+                selector.matchVlanId(assignedVlan);
+            }
             treatment.transition(MULTICAST_ROUTING_TABLE);
             rule = DefaultFlowRule.builder()
                     .forDevice(deviceId)
                     .withSelector(selector.build())
                     .withTreatment(treatment.build())
-                    .withPriority(DEFAULT_PRIORITY)
+                    .withPriority(priority)
                     .fromApp(applicationId)
                     .makePermanent()
                     .forTable(TMAC_TABLE).build();
@@ -1001,13 +1009,15 @@
                 treatment = DefaultTrafficTreatment.builder();
                 selector.matchEthType(Ethernet.TYPE_IPV6);
                 selector.matchEthDst(unicastMac);
-                selector.matchVlanId(assignedVlan);
+                if (assignedVlan != null) {
+                    selector.matchVlanId(assignedVlan);
+                }
                 treatment.transition(UNICAST_ROUTING_TABLE);
                 rule = DefaultFlowRule.builder()
                         .forDevice(deviceId)
                         .withSelector(selector.build())
                         .withTreatment(treatment.build())
-                        .withPriority(DEFAULT_PRIORITY)
+                        .withPriority(priority)
                         .fromApp(applicationId)
                         .makePermanent()
                         .forTable(TMAC_TABLE).build();
@@ -1018,13 +1028,15 @@
             treatment = DefaultTrafficTreatment.builder();
             selector.matchEthType(Ethernet.TYPE_IPV6);
             selector.matchEthDstMasked(ethCriterion.mac(), ethCriterion.mask());
-            selector.matchVlanId(assignedVlan);
+            if (assignedVlan != null) {
+                selector.matchVlanId(assignedVlan);
+            }
             treatment.transition(MULTICAST_ROUTING_TABLE);
             rule = DefaultFlowRule.builder()
                     .forDevice(deviceId)
                     .withSelector(selector.build())
                     .withTreatment(treatment.build())
-                    .withPriority(DEFAULT_PRIORITY)
+                    .withPriority(priority)
                     .fromApp(applicationId)
                     .makePermanent()
                     .forTable(TMAC_TABLE).build();