Support IPv4 multicast in P4 fabric pipeline

- Multicast can use the same table as unicast. Merge into one.
- Allow masked destination MAC in classifier table

Note:
- Pipeliner now translates all exact MAC match to masked match with FF:FF:FF:FF:FF:FF mask.
- Interpreter now only uses masked src/dst MAC

Change-Id: Ibd27ebfb2d72ba929031f07a29927eb6f1844f11
(cherry picked from commit 0865779b66a59a623856b1353615e462af5575c5)
diff --git a/pipelines/fabric/src/main/java/org/onosproject/pipelines/fabric/pipeliner/FabricFilteringPipeliner.java b/pipelines/fabric/src/main/java/org/onosproject/pipelines/fabric/pipeliner/FabricFilteringPipeliner.java
index a8515e5..7d6ad40 100644
--- a/pipelines/fabric/src/main/java/org/onosproject/pipelines/fabric/pipeliner/FabricFilteringPipeliner.java
+++ b/pipelines/fabric/src/main/java/org/onosproject/pipelines/fabric/pipeliner/FabricFilteringPipeliner.java
@@ -51,12 +51,10 @@
 public class FabricFilteringPipeliner {
     private static final Logger log = getLogger(FabricFilteringPipeliner.class);
     // Forwarding types
-    private static final byte FWD_BRIDGING = 0;
-    private static final byte FWD_MPLS = 1;
-    private static final byte FWD_IPV4_UNICAST = 2;
-    private static final byte FWD_IPV4_MULTICAST = 3;
-    private static final byte FWD_IPV6_UNICAST = 4;
-    private static final byte FWD_IPV6_MULTICAST = 5;
+    static final byte FWD_BRIDGING = 0;
+    static final byte FWD_MPLS = 1;
+    static final byte FWD_IPV4_ROUTING = 2;
+    static final byte FWD_IPV6_ROUTING = 3;
     private static final PiCriterion VLAN_VALID = PiCriterion.builder()
             .matchExact(FabricConstants.HDR_VLAN_TAG_IS_VALID, new byte[]{1})
             .build();
@@ -104,11 +102,16 @@
                 .map(criterion -> (EthCriterion) criterion)
                 .findFirst()
                 .orElse(null);
+        EthCriterion ethDstMaskedCriterion = filterObjective.conditions().stream()
+                .filter(criterion -> criterion.type() == Criterion.Type.ETH_DST_MASKED)
+                .map(criterion -> (EthCriterion) criterion)
+                .findFirst()
+                .orElse(null);
 
         FlowRule inPortVlanTableRule = createInPortVlanTable(inPortCriterion, vlanCriterion,
                                                              filterObjective);
         Collection<FlowRule> fwdClassifierRules = createFwdClassifierRules(inPortCriterion, ethDstCriterion,
-                                                                           filterObjective);
+                                                                           ethDstMaskedCriterion, filterObjective);
 
         resultBuilder.addFlowRule(inPortVlanTableRule);
         fwdClassifierRules.forEach(resultBuilder::addFlowRule);
@@ -155,38 +158,46 @@
 
     private Collection<FlowRule> createFwdClassifierRules(PortCriterion inPortCriterion,
                                                           EthCriterion ethDstCriterion,
+                                                          EthCriterion ethDstMaskedCriterion,
                                                           FilteringObjective filterObjective) {
+        PortNumber port = inPortCriterion.port();
+
         Collection<FlowRule> flowRules = Lists.newArrayList();
         if (ethDstCriterion == null) {
-            // Bridging table, do nothing
+            if (ethDstMaskedCriterion == null) {
+                // Bridging table, do nothing
+                return flowRules;
+            }
+            // Masked fwd classifier rule
+            MacAddress dstMac = ethDstMaskedCriterion.mac();
+            MacAddress dstMacMask = ethDstMaskedCriterion.mask();
+            FlowRule flow = createMaskedFwdClassifierRule(port, dstMac, dstMacMask, filterObjective);
+            if (flow != null) {
+                flowRules.add(flow);
+            }
             return flowRules;
         }
-        PortNumber port = inPortCriterion.port();
         MacAddress dstMac = ethDstCriterion.mac();
-        if (dstMac.isMulticast()) {
-            flowRules.add(createMulticastFwdClassifierRule(port, dstMac, filterObjective));
-            return flowRules;
-        }
-
         flowRules.addAll(createIpFwdClassifierRules(port, dstMac, filterObjective));
         flowRules.add(createMplsFwdClassifierRule(port, dstMac, filterObjective));
         return flowRules;
     }
 
-    private FlowRule createMulticastFwdClassifierRule(PortNumber inPort, MacAddress dstMac,
-                                                      FilteringObjective filterObjective) {
+    private FlowRule createMaskedFwdClassifierRule(PortNumber inPort, MacAddress dstMac, MacAddress dstMacMask,
+                                                   FilteringObjective filterObjective) {
         TrafficTreatment treatment;
         short ethType;
-        if (dstMac.equals(MacAddress.IPV4_MULTICAST)) {
-            // Ipv4 multicast
-            treatment = createFwdClassifierTreatment(FWD_IPV4_MULTICAST);
+        if (dstMac.equals(MacAddress.IPV4_MULTICAST) && dstMacMask.equals(MacAddress.IPV4_MULTICAST_MASK)) {
+            treatment = createFwdClassifierTreatment(FWD_IPV4_ROUTING);
             ethType = Ethernet.TYPE_IPV4;
-        } else {
-            // IPv6 multicast
-            treatment = createFwdClassifierTreatment(FWD_IPV6_MULTICAST);
+        } else if (dstMac.equals(MacAddress.IPV6_MULTICAST) && dstMacMask.equals(MacAddress.IPV6_MULTICAST_MASK)) {
+            treatment = createFwdClassifierTreatment(FWD_IPV6_ROUTING);
             ethType = Ethernet.TYPE_IPV6;
+        } else {
+            log.warn("Unsupported masked fwd classifier rule. mac={}. mask={}", dstMac, dstMacMask);
+            return null;
         }
-        return createFwdClassifierRule(inPort, ethType, dstMac, treatment, filterObjective);
+        return createFwdClassifierRule(inPort, ethType, dstMac, dstMacMask, treatment, filterObjective);
     }
 
     private Collection<FlowRule> createIpFwdClassifierRules(PortNumber inPort,
@@ -194,10 +205,10 @@
                                                             FilteringObjective filterObjective) {
         Collection<FlowRule> flowRules = Lists.newArrayList();
         TrafficTreatment treatment;
-        treatment = createFwdClassifierTreatment(FWD_IPV4_UNICAST);
-        flowRules.add(createFwdClassifierRule(inPort, Ethernet.TYPE_IPV4, dstMac, treatment, filterObjective));
-        treatment = createFwdClassifierTreatment(FWD_IPV6_UNICAST);
-        flowRules.add(createFwdClassifierRule(inPort, Ethernet.TYPE_IPV6, dstMac, treatment, filterObjective));
+        treatment = createFwdClassifierTreatment(FWD_IPV4_ROUTING);
+        flowRules.add(createFwdClassifierRule(inPort, Ethernet.TYPE_IPV4, dstMac, null, treatment, filterObjective));
+        treatment = createFwdClassifierTreatment(FWD_IPV6_ROUTING);
+        flowRules.add(createFwdClassifierRule(inPort, Ethernet.TYPE_IPV6, dstMac, null, treatment, filterObjective));
         return flowRules;
     }
 
@@ -205,18 +216,23 @@
                                                  MacAddress dstMac,
                                                  FilteringObjective filterObjective) {
         TrafficTreatment treatment = createFwdClassifierTreatment(FWD_MPLS);
-        return createFwdClassifierRule(inPort, Ethernet.MPLS_UNICAST, dstMac, treatment, filterObjective);
+        return createFwdClassifierRule(inPort, Ethernet.MPLS_UNICAST, dstMac, null, treatment, filterObjective);
     }
 
     private FlowRule createFwdClassifierRule(PortNumber inPort,
                                              short ethType,
                                              MacAddress dstMac,
+                                             MacAddress dstMacMask,
                                              TrafficTreatment treatment,
                                              FilteringObjective filterObjective) {
         TrafficSelector.Builder selector = DefaultTrafficSelector.builder()
                 .matchInPort(inPort)
-                .matchEthDst(dstMac)
                 .matchEthType(ethType);
+        if (dstMacMask != null) {
+            selector.matchEthDstMasked(dstMac, dstMacMask);
+        } else {
+            selector.matchEthDstMasked(dstMac, MacAddress.EXACT_MASK);
+        }
 
         return DefaultFlowRule.builder()
                 .withSelector(selector.build())