[ONOS-7242] Support MPLS by fabric.p4 pipeliner

Change-Id: I56a8f266e6d0afe5ad6737b8d0e399758fb75378
diff --git a/pipelines/fabric/src/main/java/org/onosproject/pipelines/fabric/FabricTreatmentInterpreter.java b/pipelines/fabric/src/main/java/org/onosproject/pipelines/fabric/FabricTreatmentInterpreter.java
index a2e4974..234e541 100644
--- a/pipelines/fabric/src/main/java/org/onosproject/pipelines/fabric/FabricTreatmentInterpreter.java
+++ b/pipelines/fabric/src/main/java/org/onosproject/pipelines/fabric/FabricTreatmentInterpreter.java
@@ -18,6 +18,7 @@
 
 import com.google.common.collect.ImmutableList;
 import org.onlab.packet.MacAddress;
+import org.onlab.packet.MplsLabel;
 import org.onlab.packet.VlanId;
 import org.onlab.util.ImmutableByteSequence;
 import org.onosproject.net.PortNumber;
@@ -27,24 +28,26 @@
 import org.onosproject.net.flow.instructions.Instructions.OutputInstruction;
 import org.onosproject.net.flow.instructions.L2ModificationInstruction;
 import org.onosproject.net.flow.instructions.L2ModificationInstruction.ModEtherInstruction;
+import org.onosproject.net.flow.instructions.L2ModificationInstruction.ModMplsLabelInstruction;
 import org.onosproject.net.flow.instructions.L2ModificationInstruction.ModVlanIdInstruction;
 import org.onosproject.net.pi.model.PiActionId;
 import org.onosproject.net.pi.model.PiPipelineInterpreter.PiInterpreterException;
 import org.onosproject.net.pi.runtime.PiAction;
 import org.onosproject.net.pi.runtime.PiActionParam;
+import org.slf4j.Logger;
 
 import java.util.List;
 
 import static java.lang.String.format;
 import static org.onosproject.net.flow.instructions.Instruction.Type.L2MODIFICATION;
 import static org.onosproject.net.flow.instructions.Instruction.Type.OUTPUT;
-import static org.onosproject.net.flow.instructions.L2ModificationInstruction.L2SubType.ETH_DST;
-import static org.onosproject.net.flow.instructions.L2ModificationInstruction.L2SubType.ETH_SRC;
 import static org.onosproject.net.flow.instructions.L2ModificationInstruction.L2SubType.VLAN_ID;
 import static org.onosproject.net.flow.instructions.L2ModificationInstruction.L2SubType.VLAN_PUSH;
+import static org.slf4j.LoggerFactory.getLogger;
 
 
 final class FabricTreatmentInterpreter {
+    private static final Logger log = getLogger(FabricTreatmentInterpreter.class);
     private static final String INVALID_TREATMENT = "Invalid treatment for %s block: %s";
 
     // Hide default constructor
@@ -149,11 +152,15 @@
     /*
      * In Next block, we need to implement these actions:
      * output
-     * output_ecmp
      * set_vlan_output
+     * l3_routing
+     * mpls_routing_v4
      *
      * Unsupported, using PiAction directly:
-     * set_mcast_group
+     * set_next_type
+     *
+     * Unsupported, need to find a way to implement it
+     * mpls_routing_v6
      */
 
     public static PiAction mapNextTreatment(TrafficTreatment treatment)
@@ -163,29 +170,36 @@
         ModEtherInstruction modEthDstInst = null;
         ModEtherInstruction modEthSrcInst = null;
         ModVlanIdInstruction modVlanIdInst = null;
+        ModMplsLabelInstruction modMplsInst = null;
 
-        // TODO: use matrix to store the combination of instruction
+        // TODO: add NextFunctionType (like ForwardingFunctionType)
         for (Instruction inst : insts) {
             switch (inst.type()) {
                 case L2MODIFICATION:
                     L2ModificationInstruction l2Inst = (L2ModificationInstruction) inst;
-
-                    if (l2Inst.subtype() == ETH_SRC) {
-                        modEthSrcInst = (ModEtherInstruction) l2Inst;
-                    }
-
-                    if (l2Inst.subtype() == ETH_DST) {
-                        modEthDstInst = (ModEtherInstruction) l2Inst;
-                    }
-
-                    if (l2Inst.subtype() == VLAN_ID) {
-                        modVlanIdInst = (ModVlanIdInstruction) l2Inst;
+                    switch (l2Inst.subtype()) {
+                        case ETH_SRC:
+                            modEthSrcInst = (ModEtherInstruction) l2Inst;
+                            break;
+                        case ETH_DST:
+                            modEthDstInst = (ModEtherInstruction) l2Inst;
+                            break;
+                        case VLAN_ID:
+                            modVlanIdInst = (ModVlanIdInstruction) l2Inst;
+                            break;
+                        case MPLS_LABEL:
+                            modMplsInst = (ModMplsLabelInstruction) l2Inst;
+                            break;
+                        default:
+                            log.warn("Unsupported l2 instruction sub type: {}", l2Inst.subtype());
+                            break;
                     }
                     break;
                 case OUTPUT:
                     outInst = (OutputInstruction) inst;
                     break;
                 default:
+                    log.warn("Unsupported instruction sub type: {}", inst.type());
                     break;
             }
         }
@@ -217,13 +231,36 @@
         }
 
         if (modEthDstInst != null && modEthSrcInst != null) {
-            // output and rewrite src/dst mac
             MacAddress srcMac = modEthSrcInst.mac();
             MacAddress dstMac = modEthDstInst.mac();
             PiActionParam srcMacParam = new PiActionParam(FabricConstants.ACT_PRM_SMAC_ID,
                                                           ImmutableByteSequence.copyFrom(srcMac.toBytes()));
             PiActionParam dstMacParam = new PiActionParam(FabricConstants.ACT_PRM_DMAC_ID,
                                                           ImmutableByteSequence.copyFrom(dstMac.toBytes()));
+
+            if (modMplsInst != null) {
+                // MPLS routing
+                MplsLabel mplsLabel = modMplsInst.label();
+                try {
+                    ImmutableByteSequence mplsValue =
+                            ImmutableByteSequence.fit(ImmutableByteSequence.copyFrom(mplsLabel.toInt()), 20);
+                    PiActionParam mplsParam = new PiActionParam(FabricConstants.ACT_PRM_LABEL_ID, mplsValue);
+                    return PiAction.builder()
+                            // FIXME: fins a way to determine v4 or v6
+                            .withId(FabricConstants.ACT_MPLS_ROUTING_V4_ID)
+                            .withParameters(ImmutableList.of(portNumParam,
+                                                             srcMacParam,
+                                                             dstMacParam,
+                                                             mplsParam))
+                            .build();
+                } catch (ImmutableByteSequence.ByteSequenceTrimException e) {
+                    // Basically this won't happened because we already limited
+                    // size of mpls value to 20 bits (0xFFFFF)
+                    throw new PiInterpreterException(format(INVALID_TREATMENT, "next", treatment));
+                }
+            }
+
+            // L3 routing
             return PiAction.builder()
                     .withId(FabricConstants.ACT_L3_ROUTING_ID)
                     .withParameters(ImmutableList.of(portNumParam,