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())
diff --git a/pipelines/fabric/src/main/java/org/onosproject/pipelines/fabric/pipeliner/FabricForwardingPipeliner.java b/pipelines/fabric/src/main/java/org/onosproject/pipelines/fabric/pipeliner/FabricForwardingPipeliner.java
index bdc6d5e..580d398 100644
--- a/pipelines/fabric/src/main/java/org/onosproject/pipelines/fabric/pipeliner/FabricForwardingPipeliner.java
+++ b/pipelines/fabric/src/main/java/org/onosproject/pipelines/fabric/pipeliner/FabricForwardingPipeliner.java
@@ -145,15 +145,13 @@
             case L2_BROADCAST:
                 processL2BroadcastRule(vlanIdCriterion, fwd, resultBuilder);
                 break;
-            case IPV4_UNICAST:
-                processIpv4UnicastRule(ipDstCriterion, fwd, resultBuilder);
+            case IPV4_ROUTING:
+                processIpv4RoutingRule(ipDstCriterion, fwd, resultBuilder);
                 break;
             case MPLS:
                 processMplsRule(mplsCriterion, fwd, resultBuilder);
                 break;
-            case IPV4_MULTICAST:
-            case IPV6_UNICAST:
-            case IPV6_MULTICAST:
+            case IPV6_ROUTING:
             default:
                 log.warn("Unsupported forwarding function type {}", criteria);
                 resultBuilder.setError(ObjectiveError.UNSUPPORTED);
@@ -174,7 +172,7 @@
 
         TrafficSelector selector = DefaultTrafficSelector.builder()
                 .matchVlanId(vlanId)
-                .matchEthDst(ethDst)
+                .matchEthDstMasked(ethDst, MacAddress.EXACT_MASK)
                 .build();
         TrafficTreatment treatment = fwd.treatment();
         if (fwd.nextId() != null) {
@@ -223,7 +221,7 @@
         resultBuilder.addFlowRule(flowRule);
     }
 
-    private void processIpv4UnicastRule(IPCriterion ipDstCriterion, ForwardingObjective fwd,
+    private void processIpv4RoutingRule(IPCriterion ipDstCriterion, ForwardingObjective fwd,
                                         PipelinerTranslationResult.Builder resultBuilder) {
         checkNotNull(ipDstCriterion, "IP dst criterion should not be null");
         TrafficSelector selector = DefaultTrafficSelector.builder()
@@ -232,7 +230,7 @@
         TrafficTreatment treatment = fwd.treatment();
         if (fwd.nextId() != null) {
             treatment = buildSetNextIdTreatment(fwd.nextId(),
-                                                FabricConstants.FABRIC_INGRESS_FORWARDING_SET_NEXT_ID_UNICAST_V4);
+                                                FabricConstants.FABRIC_INGRESS_FORWARDING_SET_NEXT_ID_ROUTING_V4);
         }
         FlowRule flowRule = DefaultFlowRule.builder()
                 .withSelector(selector)
@@ -241,7 +239,7 @@
                 .withPriority(fwd.priority())
                 .makePermanent()
                 .forDevice(deviceId)
-                .forTable(FabricConstants.FABRIC_INGRESS_FORWARDING_UNICAST_V4)
+                .forTable(FabricConstants.FABRIC_INGRESS_FORWARDING_ROUTING_V4)
                 .build();
 
         resultBuilder.addFlowRule(flowRule);
diff --git a/pipelines/fabric/src/main/java/org/onosproject/pipelines/fabric/pipeliner/ForwardingFunctionType.java b/pipelines/fabric/src/main/java/org/onosproject/pipelines/fabric/pipeliner/ForwardingFunctionType.java
index 0349cc7..65e3a25 100644
--- a/pipelines/fabric/src/main/java/org/onosproject/pipelines/fabric/pipeliner/ForwardingFunctionType.java
+++ b/pipelines/fabric/src/main/java/org/onosproject/pipelines/fabric/pipeliner/ForwardingFunctionType.java
@@ -49,22 +49,12 @@
     /**
      * IPv4 unicast, with EtherType and IPv4 unicast destination address.
      */
-    IPV4_UNICAST,
-
-    /**
-     * IPv4 multicast, with EtherType and IPv4 multicast destination address.
-     */
-    IPV4_MULTICAST,
+    IPV4_ROUTING,
 
     /**
      * IPv6 unicast, with EtherType and IPv6 unicast destination address.
      */
-    IPV6_UNICAST,
-
-    /**
-     * IPv6 multicast, with EtherType and IPv6 multicast destination address.
-     */
-    IPV6_MULTICAST,
+    IPV6_ROUTING,
 
     /**
      * MPLS, with EtherType, MPLS label and MPLS BOS(true) criterion.
@@ -101,10 +91,10 @@
             ImmutableMap.<Set<Criterion.Type>, ForwardingFunctionType>builder()
                     .put(L2_UNI_CRITERIA_TYPE, L2_UNICAST)
                     .put(L2_BRC_CRITERIA_TYPE, L2_BROADCAST)
-                    .put(IPV4_UNI_CRITERIA_TYPE, IPV4_UNICAST)
-                    .put(IPV4_MCAST_CRITERIA_TYPE, IPV4_MULTICAST)
-                    .put(IPV6_UNI_CRITERIA_TYPE, IPV6_UNICAST)
-                    .put(IPV6_MCAST_CRITERIA_TYPE, IPV6_MULTICAST)
+                    .put(IPV4_UNI_CRITERIA_TYPE, IPV4_ROUTING)
+                    .put(IPV4_MCAST_CRITERIA_TYPE, IPV4_ROUTING)
+                    .put(IPV6_UNI_CRITERIA_TYPE, IPV6_ROUTING)
+                    .put(IPV6_MCAST_CRITERIA_TYPE, IPV6_ROUTING)
                     .put(MPLS_UNI_CRITERIA_TYPE, MPLS)
                     .build();