MyTunnel exercise with solution for P4 tutorial

Change-Id: Ifddfa27d6df6545bbd529239ca400967fb623a29
diff --git a/apps/p4-tutorial/mytunnel/src/main/java/org/onosproject/p4tutorial/mytunnel/MyTunnelApp.java b/apps/p4-tutorial/mytunnel/src/main/java/org/onosproject/p4tutorial/mytunnel/MyTunnelApp.java
index eb91243..c5fac98 100644
--- a/apps/p4-tutorial/mytunnel/src/main/java/org/onosproject/p4tutorial/mytunnel/MyTunnelApp.java
+++ b/apps/p4-tutorial/mytunnel/src/main/java/org/onosproject/p4tutorial/mytunnel/MyTunnelApp.java
@@ -55,7 +55,6 @@
 import java.util.Set;
 import java.util.concurrent.atomic.AtomicInteger;
 
-import static org.onlab.util.ImmutableByteSequence.copyFrom;
 import static org.slf4j.LoggerFactory.getLogger;
 
 /**
@@ -80,34 +79,6 @@
     private static final Logger log = getLogger(MyTunnelApp.class);
 
     //--------------------------------------------------------------------------
-    // P4 program entity names. Values derived from mytunnel.p4info
-    //--------------------------------------------------------------------------
-
-    // Match field IDs.
-    private static final PiMatchFieldId MF_ID_MY_TUNNEL_DST_ID =
-            PiMatchFieldId.of("hdr.my_tunnel.tun_id");
-    private static final PiMatchFieldId MF_ID_IP_DST =
-            PiMatchFieldId.of("hdr.ipv4.dst_addr");
-    // Table IDs.
-    private static final PiTableId TBL_ID_TUNNEL_FWD =
-            PiTableId.of("c_ingress.t_tunnel_fwd");
-    private static final PiTableId TBL_ID_TUNNEL_INGRESS =
-            PiTableId.of("c_ingress.t_tunnel_ingress");
-    // Action IDs.
-    private static final PiActionId ACT_ID_MY_TUNNEL_INGRESS =
-            PiActionId.of("c_ingress.my_tunnel_ingress");
-    private static final PiActionId ACT_ID_SET_OUT_PORT =
-            PiActionId.of("c_ingress.set_out_port");
-    private static final PiActionId ACT_ID_MY_TUNNEL_EGRESS =
-            PiActionId.of("c_ingress.my_tunnel_egress");
-
-    // Action params ID.
-    private static final PiActionParamId ACT_PARAM_ID_TUN_ID =
-            PiActionParamId.of("tun_id");
-    private static final PiActionParamId ACT_PARAM_ID_PORT =
-            PiActionParamId.of("port");
-
-    //--------------------------------------------------------------------------
     // ONOS services needed by this application.
     //--------------------------------------------------------------------------
 
@@ -181,20 +152,20 @@
             // In ONOS discovered hosts can have multiple IP addresses.
             // Insert tunnel ingress rule for each IP address.
             // Next switches will forward based only on tunnel ID.
-            insertIngressRule(srcSwitch, dstIpAddr, tunId);
+            insertTunnelIngressRule(srcSwitch, dstIpAddr, tunId);
         }
 
-        // Insert tunnel forward rules on all switches in the path, excluded the
+        // Insert tunnel transit rules on all switches in the path, excluded the
         // last one.
         for (Link link : pathLinks) {
             DeviceId sw = link.src().deviceId();
             PortNumber port = link.src().port();
-            insertForwardRule(sw, port, tunId, false);
+            insertTunnelForwardRule(sw, port, tunId, false);
         }
 
         // Tunnel egress rule.
         PortNumber egressSwitchPort = dstHost.location().port();
-        insertForwardRule(dstSwitch, egressSwitchPort, tunId, true);
+        insertTunnelForwardRule(dstSwitch, egressSwitchPort, tunId, true);
 
         log.info("** Completed provisioning of tunnel {} (srcHost={} dstHost={})",
                  tunId, srcHost.id(), dstHost.id());
@@ -208,24 +179,31 @@
      * @param dstIpAddr IP address to forward inside the tunnel
      * @param tunId     tunnel ID
      */
-    private void insertIngressRule(DeviceId switchId,
-                                   IpAddress dstIpAddr,
-                                   int tunId) {
+    private void insertTunnelIngressRule(DeviceId switchId,
+                                         IpAddress dstIpAddr,
+                                         int tunId) {
 
-        log.info("Inserting INGRESS rule: switchId={}, dstIpAddr={}, tunId={}",
-                 switchId, dstIpAddr, tunId);
 
+        PiTableId tunnelIngressTableId = PiTableId.of("c_ingress.t_tunnel_ingress");
+
+        // Longest prefix match on IPv4 dest address.
+        PiMatchFieldId ipDestMatchFieldId = PiMatchFieldId.of("hdr.ipv4.dst_addr");
         PiCriterion match = PiCriterion.builder()
-                .matchLpm(MF_ID_IP_DST, dstIpAddr.toOctets(), 32)
+                .matchLpm(ipDestMatchFieldId, dstIpAddr.toOctets(), 32)
                 .build();
 
+        PiActionParam tunIdParam = new PiActionParam(PiActionParamId.of("tun_id"), tunId);
+
+        PiActionId ingressActionId = PiActionId.of("c_ingress.my_tunnel_ingress");
         PiAction action = PiAction.builder()
-                .withId(ACT_ID_MY_TUNNEL_INGRESS)
-                .withParameter(new PiActionParam(
-                        ACT_PARAM_ID_TUN_ID, copyFrom(tunId)))
+                .withId(ingressActionId)
+                .withParameter(tunIdParam)
                 .build();
 
-        insertPiFlowRule(switchId, TBL_ID_TUNNEL_INGRESS, match, action);
+        log.info("Inserting INGRESS rule on switch {}: table={}, match={}, action={}",
+                 switchId, tunnelIngressTableId, match, action);
+
+        insertPiFlowRule(switchId, tunnelIngressTableId, match, action);
     }
 
     /**
@@ -234,31 +212,59 @@
      *
      * @param switchId switch ID
      * @param outPort  output port where to forward tunneled packets
-     * @param tunId tunnel ID
+     * @param tunId    tunnel ID
      * @param isEgress if true, perform tunnel egress action, otherwise forward
      *                 packet as is to port
      */
-    private void insertForwardRule(DeviceId switchId,
-                                   PortNumber outPort,
-                                   int tunId,
-                                   boolean isEgress) {
+    private void insertTunnelForwardRule(DeviceId switchId,
+                                         PortNumber outPort,
+                                         int tunId,
+                                         boolean isEgress) {
 
-        log.info("Inserting {} rule: switchId={}, outPort={}, tunId={}",
-                 isEgress ? "EGRESS" : "FORWARD", switchId, outPort, tunId);
+        PiTableId tunnelForwardTableId = PiTableId.of("c_ingress.t_tunnel_fwd");
 
+        // Exact match on tun_id
+        PiMatchFieldId tunIdMatchFieldId = PiMatchFieldId.of("hdr.my_tunnel.tun_id");
         PiCriterion match = PiCriterion.builder()
-                .matchExact(MF_ID_MY_TUNNEL_DST_ID, tunId)
+                .matchExact(tunIdMatchFieldId, tunId)
                 .build();
 
-        PiActionId actionId = isEgress ? ACT_ID_MY_TUNNEL_EGRESS : ACT_ID_SET_OUT_PORT;
+        // Action depend on isEgress parameter.
+        // if true, perform tunnel egress action on the given outPort, otherwise
+        // simply forward packet as is (set_out_port action).
+        PiActionParamId portParamId = PiActionParamId.of("port");
+        PiActionParam portParam = new PiActionParam(portParamId, (short) outPort.toLong());
 
-        PiAction action = PiAction.builder()
-                .withId(actionId)
-                .withParameter(new PiActionParam(
-                        ACT_PARAM_ID_PORT, copyFrom((short) outPort.toLong())))
-                .build();
+        final PiAction action;
+        if (isEgress) {
+            // Tunnel egress action.
+            // Remove MyTunnel header and forward to outPort.
+            PiActionId egressActionId = PiActionId.of("c_ingress.my_tunnel_egress");
+            action = PiAction.builder()
+                    .withId(egressActionId)
+                    .withParameter(portParam)
+                    .build();
+        } else {
+            // Tunnel transit action.
+            // Forward the packet as is to outPort.
+            /*
+             * TODO EXERCISE: create action object for the transit case.
+             * Look at the t_tunnel_fwd table in the P4 program. Which of the 3
+             * actions can be used to simply set the output port? Get the full
+             * action name from the P4Info file, and use that when creating the
+             * PiActionId object. When creating the PiAction object, remember to
+             * add all action parameters as defined in the P4 program.
+             *
+             * Hint: the code will be similar to the case when isEgress = false.
+             */
+            action = null;
+        }
 
-        insertPiFlowRule(switchId, TBL_ID_TUNNEL_FWD, match, action);
+        log.info("Inserting {} rule on switch {}: table={}, match={}, action={}",
+                 isEgress ? "EGRESS" : "TRANSIT",
+                 switchId, tunnelForwardTableId, match, action);
+
+        insertPiFlowRule(switchId, tunnelForwardTableId, match, action);
     }
 
     /**