MyTunnel P4 tutorial app and pipeconf

Change-Id: I0549276fc7f6c8d0d244d6c52b1b9e85b9c3e13c
diff --git a/apps/p4-tutorial/icmpdropper/BUCK b/apps/p4-tutorial/icmpdropper/BUCK
deleted file mode 100644
index 026827d..0000000
--- a/apps/p4-tutorial/icmpdropper/BUCK
+++ /dev/null
@@ -1,25 +0,0 @@
-COMPILE_DEPS = [
-    '//lib:CORE_DEPS',
-    '//apps/p4-tutorial/pipeconf:onos-apps-p4-tutorial-pipeconf',
-]
-
-osgi_jar (
-    deps = COMPILE_DEPS,
-)
-
-BUNDLES = [
-    '//apps/p4-tutorial/icmpdropper:onos-apps-p4-tutorial-icmpdropper',
-]
-
-onos_app (
-    app_name = 'org.onosproject.p4tutorial.icmpdropper',
-    title = 'ICMP Dropper (P4 Tutorial)',
-    category = 'Security',
-    url = 'http://onosproject.org',
-    description = 'Inhibits forwarding of ICMP packets for PI-enabled devices using the ' +
-                    'p4-tutorial-pipeconf.',
-    included_bundles = BUNDLES,
-    required_apps = [
-        'org.onosproject.p4tutorial.pipeconf',
-    ]
-)
diff --git a/apps/p4-tutorial/icmpdropper/src/main/java/org/onosproject/p4tutorial/icmpdropper/IcmpDropper.java b/apps/p4-tutorial/icmpdropper/src/main/java/org/onosproject/p4tutorial/icmpdropper/IcmpDropper.java
deleted file mode 100644
index ce1c682..0000000
--- a/apps/p4-tutorial/icmpdropper/src/main/java/org/onosproject/p4tutorial/icmpdropper/IcmpDropper.java
+++ /dev/null
@@ -1,167 +0,0 @@
-/*
- * Copyright 2017-present Open Networking Foundation
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.onosproject.p4tutorial.icmpdropper;
-
-import org.apache.felix.scr.annotations.Activate;
-import org.apache.felix.scr.annotations.Component;
-import org.apache.felix.scr.annotations.Deactivate;
-import org.apache.felix.scr.annotations.Reference;
-import org.apache.felix.scr.annotations.ReferenceCardinality;
-import org.onosproject.app.ApplicationAdminService;
-import org.onosproject.core.ApplicationId;
-import org.onosproject.core.CoreService;
-import org.onosproject.net.Device;
-import org.onosproject.net.DeviceId;
-import org.onosproject.net.device.DeviceEvent;
-import org.onosproject.net.device.DeviceListener;
-import org.onosproject.net.device.DeviceService;
-import org.onosproject.net.flow.DefaultFlowRule;
-import org.onosproject.net.flow.DefaultTrafficSelector;
-import org.onosproject.net.flow.DefaultTrafficTreatment;
-import org.onosproject.net.flow.FlowRule;
-import org.onosproject.net.flow.FlowRuleService;
-import org.onosproject.net.flow.criteria.PiCriterion;
-import org.onosproject.net.behaviour.PiPipelineProgrammable;
-import org.onosproject.net.pi.runtime.PiAction;
-import org.onosproject.net.pi.model.PiActionId;
-import org.onosproject.net.pi.model.PiMatchFieldId;
-import org.onosproject.net.pi.service.PiPipeconfService;
-import org.onosproject.net.pi.model.PiTableId;
-import org.onosproject.p4tutorial.pipeconf.PipeconfFactory;
-import org.slf4j.Logger;
-
-import static org.onosproject.net.device.DeviceEvent.Type.DEVICE_ADDED;
-import static org.slf4j.LoggerFactory.getLogger;
-
-/**
- * Simple application that drops all ICMP packets.
- */
-@Component(immediate = true)
-public class IcmpDropper {
-
-    private static final Logger log = getLogger(IcmpDropper.class);
-
-    private static final String APP_NAME = "org.onosproject.p4tutorial.icmpdropper";
-
-    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
-    private DeviceService deviceService;
-
-    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
-    private FlowRuleService flowRuleService;
-
-    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
-    private ApplicationAdminService appService;
-
-    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
-    private CoreService coreService;
-
-    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
-    private PiPipeconfService piPipeconfService;
-
-    private final DeviceListener deviceListener = new InternalDeviceListener();
-
-    private ApplicationId appId;
-
-    @Activate
-    public void activate() {
-        log.info("Starting...");
-
-        appId = coreService.registerApplication(APP_NAME);
-        // Register listener for handling new devices.
-        deviceService.addListener(deviceListener);
-        // Install rules to existing devices.
-        deviceService.getDevices()
-                .forEach(device -> installDropRule(device.id()));
-
-        log.info("STARTED", appId.id());
-    }
-
-    @Deactivate
-    public void deactivate() {
-        log.info("Stopping...");
-
-        deviceService.removeListener(deviceListener);
-        flowRuleService.removeFlowRulesById(appId);
-
-        log.info("STOPPED");
-    }
-
-    private boolean checkPipeconf(Device device) {
-        if (!device.is(PiPipelineProgrammable.class)) {
-            // Device is not PI-pipeline programmable. Ignore.
-            return false;
-        }
-        if (!piPipeconfService.ofDevice(device.id()).isPresent() ||
-                !piPipeconfService.ofDevice(device.id()).get().equals(PipeconfFactory.PIPECONF_ID)) {
-            log.warn("Device {} has pipeconf {} instead of {}, can't install flow rule for this device",
-                     device.id(), piPipeconfService.ofDevice(device.id()).get(), PipeconfFactory.PIPECONF_ID);
-            return false;
-        }
-
-        return true;
-    }
-
-    private void installDropRule(DeviceId deviceId) {
-        PiMatchFieldId ipv4ProtoFieldId = PiMatchFieldId.of("hdr.ipv4.protocol");
-        PiActionId dropActionId = PiActionId.of("_drop");
-
-        PiCriterion piCriterion = PiCriterion.builder()
-                .matchExact(ipv4ProtoFieldId, (byte) 0x01)
-                .build();
-        PiAction dropAction = PiAction.builder()
-                .withId(dropActionId)
-                .build();
-
-        FlowRule flowRule = DefaultFlowRule.builder()
-                .forDevice(deviceId)
-                .forTable(PiTableId.of("ip_proto_filter_table"))
-                .fromApp(appId)
-                .makePermanent()
-                .withPriority(1000)
-                .withSelector(DefaultTrafficSelector.builder()
-                                      .matchPi(piCriterion)
-                                      .build())
-                .withTreatment(
-                        DefaultTrafficTreatment.builder()
-                                .piTableAction(dropAction)
-                                .build())
-                .build();
-
-        log.warn("Installing ICMP drop rule to {}", deviceId);
-
-        flowRuleService.applyFlowRules(flowRule);
-    }
-
-    /**
-     * A listener of device events that installs a rule to drop packet for each new device.
-     */
-    private class InternalDeviceListener implements DeviceListener {
-        @Override
-        public void event(DeviceEvent event) {
-            Device device = event.subject();
-            if (checkPipeconf(device)) {
-                installDropRule(device.id());
-            }
-        }
-
-        @Override
-        public boolean isRelevant(DeviceEvent event) {
-            // Reacts only to new devices.
-            return event.type() == DEVICE_ADDED;
-        }
-    }
-}
diff --git a/apps/p4-tutorial/mytunnel/BUCK b/apps/p4-tutorial/mytunnel/BUCK
new file mode 100644
index 0000000..5ada8fc
--- /dev/null
+++ b/apps/p4-tutorial/mytunnel/BUCK
@@ -0,0 +1,24 @@
+COMPILE_DEPS = [
+    '//lib:CORE_DEPS',
+    '//apps/p4-tutorial/pipeconf:onos-apps-p4-tutorial-pipeconf',
+]
+
+osgi_jar (
+    deps = COMPILE_DEPS,
+)
+
+BUNDLES = [
+    '//apps/p4-tutorial/mytunnel:onos-apps-p4-tutorial-mytunnel',
+]
+
+onos_app (
+    app_name = 'org.onosproject.p4tutorial.mytunnel',
+    title = 'MyTunnel Demo App',
+    category = 'Steering',
+    url = 'http://onosproject.org',
+    description = 'Provides forwarding between each pair of hosts via MyTunnel protocol',
+    included_bundles = BUNDLES,
+    required_apps = [
+        'org.onosproject.p4tutorial.pipeconf',
+    ]
+)
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
new file mode 100644
index 0000000..eb91243
--- /dev/null
+++ b/apps/p4-tutorial/mytunnel/src/main/java/org/onosproject/p4tutorial/mytunnel/MyTunnelApp.java
@@ -0,0 +1,323 @@
+/*
+ * Copyright 2017-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.p4tutorial.mytunnel;
+
+import com.google.common.collect.Lists;
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.onlab.packet.IpAddress;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.core.CoreService;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.Host;
+import org.onosproject.net.Link;
+import org.onosproject.net.Path;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.flow.DefaultFlowRule;
+import org.onosproject.net.flow.DefaultTrafficSelector;
+import org.onosproject.net.flow.DefaultTrafficTreatment;
+import org.onosproject.net.flow.FlowRule;
+import org.onosproject.net.flow.FlowRuleService;
+import org.onosproject.net.flow.criteria.PiCriterion;
+import org.onosproject.net.host.HostEvent;
+import org.onosproject.net.host.HostListener;
+import org.onosproject.net.host.HostService;
+import org.onosproject.net.pi.model.PiActionId;
+import org.onosproject.net.pi.model.PiActionParamId;
+import org.onosproject.net.pi.model.PiMatchFieldId;
+import org.onosproject.net.pi.model.PiTableId;
+import org.onosproject.net.pi.runtime.PiAction;
+import org.onosproject.net.pi.runtime.PiActionParam;
+import org.onosproject.net.topology.Topology;
+import org.onosproject.net.topology.TopologyService;
+import org.slf4j.Logger;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Random;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import static org.onlab.util.ImmutableByteSequence.copyFrom;
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * MyTunnel application which provides forwarding between each pair of hosts via
+ * MyTunnel protocol as defined in mytunnel.p4.
+ * <p>
+ * The app works by listening for host events. Each time a new host is
+ * discovered, it provisions a tunnel between that host and all the others.
+ */
+@Component(immediate = true)
+public class MyTunnelApp {
+
+    private static final String APP_NAME = "org.onosproject.p4tutorial.mytunnel";
+
+    // Default priority used for flow rules installed by this app.
+    private static final int FLOW_RULE_PRIORITY = 100;
+
+    private final HostListener hostListener = new InternalHostListener();
+    private ApplicationId appId;
+    private AtomicInteger nextTunnelId = new AtomicInteger();
+
+    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.
+    //--------------------------------------------------------------------------
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    private FlowRuleService flowRuleService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    private CoreService coreService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    private TopologyService topologyService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    private HostService hostService;
+
+    //--------------------------------------------------------------------------
+    //--------------------------------------------------------------------------
+
+    @Activate
+    public void activate() {
+        // Register app and event listeners.
+        log.info("Starting...");
+        appId = coreService.registerApplication(APP_NAME);
+        hostService.addListener(hostListener);
+        log.info("STARTED", appId.id());
+    }
+
+    @Deactivate
+    public void deactivate() {
+        // Remove listeners and clean-up flow rules.
+        log.info("Stopping...");
+        hostService.removeListener(hostListener);
+        flowRuleService.removeFlowRulesById(appId);
+        log.info("STOPPED");
+    }
+
+    /**
+     * Provisions a tunnel between the given source and destination host with
+     * the given tunnel ID. The tunnel is established using a randomly picked
+     * shortest path based on the given topology snapshot.
+     *
+     * @param tunId   tunnel ID
+     * @param srcHost tunnel source host
+     * @param dstHost tunnel destination host
+     * @param topo    topology snapshot
+     */
+    private void provisionTunnel(int tunId, Host srcHost, Host dstHost, Topology topo) {
+
+        // Get all shortest paths between switches connected to source and
+        // destination hosts.
+        DeviceId srcSwitch = srcHost.location().deviceId();
+        DeviceId dstSwitch = dstHost.location().deviceId();
+
+        List<Link> pathLinks;
+        if (srcSwitch.equals(dstSwitch)) {
+            // Source and dest hosts are connected to the same switch.
+            pathLinks = Collections.emptyList();
+        } else {
+            // Compute shortest path.
+            Set<Path> allPaths = topologyService.getPaths(topo, srcSwitch, dstSwitch);
+            if (allPaths.size() == 0) {
+                log.warn("No paths between {} and {}", srcHost.id(), dstHost.id());
+                return;
+            }
+            // If many shortest paths are available, pick a random one.
+            pathLinks = pickRandomPath(allPaths).links();
+        }
+
+        // Tunnel ingress rules.
+        for (IpAddress dstIpAddr : dstHost.ipAddresses()) {
+            // 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);
+        }
+
+        // Insert tunnel forward 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);
+        }
+
+        // Tunnel egress rule.
+        PortNumber egressSwitchPort = dstHost.location().port();
+        insertForwardRule(dstSwitch, egressSwitchPort, tunId, true);
+
+        log.info("** Completed provisioning of tunnel {} (srcHost={} dstHost={})",
+                 tunId, srcHost.id(), dstHost.id());
+    }
+
+    /**
+     * Generates and insert a flow rule to perform the tunnel INGRESS function
+     * for the given switch, destination IP address and tunnel ID.
+     *
+     * @param switchId  switch ID
+     * @param dstIpAddr IP address to forward inside the tunnel
+     * @param tunId     tunnel ID
+     */
+    private void insertIngressRule(DeviceId switchId,
+                                   IpAddress dstIpAddr,
+                                   int tunId) {
+
+        log.info("Inserting INGRESS rule: switchId={}, dstIpAddr={}, tunId={}",
+                 switchId, dstIpAddr, tunId);
+
+        PiCriterion match = PiCriterion.builder()
+                .matchLpm(MF_ID_IP_DST, dstIpAddr.toOctets(), 32)
+                .build();
+
+        PiAction action = PiAction.builder()
+                .withId(ACT_ID_MY_TUNNEL_INGRESS)
+                .withParameter(new PiActionParam(
+                        ACT_PARAM_ID_TUN_ID, copyFrom(tunId)))
+                .build();
+
+        insertPiFlowRule(switchId, TBL_ID_TUNNEL_INGRESS, match, action);
+    }
+
+    /**
+     * Generates and insert a flow rule to perform the tunnel FORWARD/EGRESS
+     * function for the given switch, output port address and tunnel ID.
+     *
+     * @param switchId switch ID
+     * @param outPort  output port where to forward tunneled packets
+     * @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) {
+
+        log.info("Inserting {} rule: switchId={}, outPort={}, tunId={}",
+                 isEgress ? "EGRESS" : "FORWARD", switchId, outPort, tunId);
+
+        PiCriterion match = PiCriterion.builder()
+                .matchExact(MF_ID_MY_TUNNEL_DST_ID, tunId)
+                .build();
+
+        PiActionId actionId = isEgress ? ACT_ID_MY_TUNNEL_EGRESS : ACT_ID_SET_OUT_PORT;
+
+        PiAction action = PiAction.builder()
+                .withId(actionId)
+                .withParameter(new PiActionParam(
+                        ACT_PARAM_ID_PORT, copyFrom((short) outPort.toLong())))
+                .build();
+
+        insertPiFlowRule(switchId, TBL_ID_TUNNEL_FWD, match, action);
+    }
+
+    /**
+     * Inserts a flow rule in the system that using a PI criterion and action.
+     *
+     * @param switchId    switch ID
+     * @param tableId     table ID
+     * @param piCriterion PI criterion
+     * @param piAction    PI action
+     */
+    private void insertPiFlowRule(DeviceId switchId, PiTableId tableId,
+                                  PiCriterion piCriterion, PiAction piAction) {
+        FlowRule rule = DefaultFlowRule.builder()
+                .forDevice(switchId)
+                .forTable(tableId)
+                .fromApp(appId)
+                .withPriority(FLOW_RULE_PRIORITY)
+                .makePermanent()
+                .withSelector(DefaultTrafficSelector.builder()
+                                      .matchPi(piCriterion).build())
+                .withTreatment(DefaultTrafficTreatment.builder()
+                                       .piTableAction(piAction).build())
+                .build();
+        flowRuleService.applyFlowRules(rule);
+    }
+
+    private int getNewTunnelId() {
+        return nextTunnelId.incrementAndGet();
+    }
+
+    private Path pickRandomPath(Set<Path> paths) {
+        int item = new Random().nextInt(paths.size());
+        List<Path> pathList = Lists.newArrayList(paths);
+        return pathList.get(item);
+    }
+
+    /**
+     * A listener of host events that provisions two tunnels for each pair of
+     * hosts when a new host is discovered.
+     */
+    private class InternalHostListener implements HostListener {
+
+        @Override
+        public void event(HostEvent event) {
+            if (event.type() != HostEvent.Type.HOST_ADDED) {
+                // Ignore other host events.
+                return;
+            }
+            synchronized (this) {
+                // Synchronizing here is an overkill, but safer for demo purposes.
+                Host host = event.subject();
+                Topology topo = topologyService.currentTopology();
+                for (Host otherHost : hostService.getHosts()) {
+                    if (!host.equals(otherHost)) {
+                        provisionTunnel(getNewTunnelId(), host, otherHost, topo);
+                        provisionTunnel(getNewTunnelId(), otherHost, host, topo);
+                    }
+                }
+            }
+        }
+    }
+}
diff --git a/apps/p4-tutorial/icmpdropper/src/main/java/org/onosproject/p4tutorial/icmpdropper/package-info.java b/apps/p4-tutorial/mytunnel/src/main/java/org/onosproject/p4tutorial/mytunnel/package-info.java
similarity index 92%
rename from apps/p4-tutorial/icmpdropper/src/main/java/org/onosproject/p4tutorial/icmpdropper/package-info.java
rename to apps/p4-tutorial/mytunnel/src/main/java/org/onosproject/p4tutorial/mytunnel/package-info.java
index 5ea1180..f107c31 100644
--- a/apps/p4-tutorial/icmpdropper/src/main/java/org/onosproject/p4tutorial/icmpdropper/package-info.java
+++ b/apps/p4-tutorial/mytunnel/src/main/java/org/onosproject/p4tutorial/mytunnel/package-info.java
@@ -16,4 +16,4 @@
 /**
  * P4 tutorial application classes.
  */
-package org.onosproject.p4tutorial.icmpdropper;
\ No newline at end of file
+package org.onosproject.p4tutorial.mytunnel;
diff --git a/apps/p4-tutorial/pipeconf/src/main/java/org/onosproject/p4tutorial/pipeconf/PipeconfFactory.java b/apps/p4-tutorial/pipeconf/src/main/java/org/onosproject/p4tutorial/pipeconf/PipeconfFactory.java
index 5d37bd1..47fad41 100644
--- a/apps/p4-tutorial/pipeconf/src/main/java/org/onosproject/p4tutorial/pipeconf/PipeconfFactory.java
+++ b/apps/p4-tutorial/pipeconf/src/main/java/org/onosproject/p4tutorial/pipeconf/PipeconfFactory.java
@@ -45,8 +45,8 @@
 public final class PipeconfFactory {
 
     public static final PiPipeconfId PIPECONF_ID = new PiPipeconfId("p4-tutorial-pipeconf");
-    private static final URL P4INFO_URL = PipeconfFactory.class.getResource("/main.p4info");
-    private static final URL BMV2_JSON_URL = PipeconfFactory.class.getResource("/main.json");
+    private static final URL P4INFO_URL = PipeconfFactory.class.getResource("/mytunnel.p4info");
+    private static final URL BMV2_JSON_URL = PipeconfFactory.class.getResource("/mytunnel.json");
 
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     private PiPipeconfService piPipeconfService;
@@ -75,7 +75,7 @@
                 .withPipelineModel(pipelineModel)
                 .addBehaviour(PiPipelineInterpreter.class, PipelineInterpreterImpl.class)
                 .addBehaviour(PortStatisticsDiscovery.class, PortStatisticsDiscoveryImpl.class)
-                // Since main.p4 defines only 1 table, we re-use the existing single-table pipeliner.
+                // Since mytunnel.p4 defines only 1 table, we re-use the existing single-table pipeliner.
                 .addBehaviour(Pipeliner.class, DefaultSingleTablePipeline.class)
                 .addExtension(P4_INFO_TEXT, P4INFO_URL)
                 .addExtension(BMV2_JSON, BMV2_JSON_URL)
diff --git a/apps/p4-tutorial/pipeconf/src/main/java/org/onosproject/p4tutorial/pipeconf/PipelineInterpreterImpl.java b/apps/p4-tutorial/pipeconf/src/main/java/org/onosproject/p4tutorial/pipeconf/PipelineInterpreterImpl.java
index a34cada..58afa10 100644
--- a/apps/p4-tutorial/pipeconf/src/main/java/org/onosproject/p4tutorial/pipeconf/PipelineInterpreterImpl.java
+++ b/apps/p4-tutorial/pipeconf/src/main/java/org/onosproject/p4tutorial/pipeconf/PipelineInterpreterImpl.java
@@ -19,10 +19,12 @@
 import com.google.common.collect.BiMap;
 import com.google.common.collect.ImmutableBiMap;
 import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Lists;
 import org.onlab.packet.DeserializationException;
 import org.onlab.packet.Ethernet;
 import org.onlab.util.ImmutableByteSequence;
 import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.DeviceId;
 import org.onosproject.net.Port;
 import org.onosproject.net.PortNumber;
 import org.onosproject.net.device.DeviceService;
@@ -30,7 +32,7 @@
 import org.onosproject.net.flow.TrafficTreatment;
 import org.onosproject.net.flow.criteria.Criterion;
 import org.onosproject.net.flow.instructions.Instruction;
-import org.onosproject.net.flow.instructions.Instructions;
+import org.onosproject.net.flow.instructions.Instructions.OutputInstruction;
 import org.onosproject.net.packet.DefaultInboundPacket;
 import org.onosproject.net.packet.InboundPacket;
 import org.onosproject.net.packet.OutboundPacket;
@@ -51,7 +53,6 @@
 import java.util.Optional;
 
 import static java.lang.String.format;
-import static java.util.stream.Collectors.toList;
 import static org.onlab.util.ImmutableByteSequence.copyFrom;
 import static org.onosproject.net.PortNumber.CONTROLLER;
 import static org.onosproject.net.PortNumber.FLOOD;
@@ -59,34 +60,48 @@
 import static org.onosproject.net.pi.model.PiPacketOperationType.PACKET_OUT;
 
 /**
- * Implementation of a PI interpreter for the main.p4 program.
+ * Implementation of a pipeline interpreter for the mytunnel.p4 program.
  */
-public final class PipelineInterpreterImpl extends AbstractHandlerBehaviour implements PiPipelineInterpreter {
+public final class PipelineInterpreterImpl
+        extends AbstractHandlerBehaviour
+        implements PiPipelineInterpreter {
 
-    private static final String DOT =  ".";
+    private static final String DOT = ".";
     private static final String HDR = "hdr";
-    private static final String TABLE0 = "table0";
-    private static final String IP_PROTO_FILTER_TABLE = "ip_proto_filter_table";
-    private static final String SEND_TO_CPU = "send_to_cpu";
-    private static final String PORT = "port";
-    private static final String DROP = "_drop";
-    private static final String SET_EGRESS_PORT = "set_egress_port";
+    private static final String C_INGRESS = "c_ingress";
+    private static final String T_L2_FWD = "t_l2_fwd";
     private static final String EGRESS_PORT = "egress_port";
     private static final String INGRESS_PORT = "ingress_port";
     private static final String ETHERNET = "ethernet";
     private static final String STANDARD_METADATA = "standard_metadata";
     private static final int PORT_FIELD_BITWIDTH = 9;
 
-    private static final PiMatchFieldId INGRESS_PORT_ID = PiMatchFieldId.of(STANDARD_METADATA + DOT + "ingress_port");
-    private static final PiMatchFieldId ETH_DST_ID = PiMatchFieldId.of(HDR + DOT + ETHERNET + DOT + "dst_addr");
-    private static final PiMatchFieldId ETH_SRC_ID = PiMatchFieldId.of(HDR + DOT + ETHERNET + DOT + "src_addr");
-    private static final PiMatchFieldId ETH_TYPE_ID = PiMatchFieldId.of(HDR + DOT + ETHERNET + DOT + "ether_type");
-    private static final PiTableId TABLE0_ID = PiTableId.of(TABLE0);
-    private static final PiTableId IP_PROTO_FILTER_TABLE_ID = PiTableId.of(IP_PROTO_FILTER_TABLE);
+    private static final PiMatchFieldId INGRESS_PORT_ID =
+            PiMatchFieldId.of(STANDARD_METADATA + DOT + "ingress_port");
+    private static final PiMatchFieldId ETH_DST_ID =
+            PiMatchFieldId.of(HDR + DOT + ETHERNET + DOT + "dst_addr");
+    private static final PiMatchFieldId ETH_SRC_ID =
+            PiMatchFieldId.of(HDR + DOT + ETHERNET + DOT + "src_addr");
+    private static final PiMatchFieldId ETH_TYPE_ID =
+            PiMatchFieldId.of(HDR + DOT + ETHERNET + DOT + "ether_type");
 
-    private static final BiMap<Integer, PiTableId> TABLE_MAP = ImmutableBiMap.of(
-            0, TABLE0_ID,
-            1, IP_PROTO_FILTER_TABLE_ID);
+    private static final PiTableId TABLE_L2_FWD_ID =
+            PiTableId.of(C_INGRESS + DOT + T_L2_FWD);
+
+    private static final PiActionId ACT_ID_NOP =
+            PiActionId.of("NoAction");
+    private static final PiActionId ACT_ID_SEND_TO_CPU =
+            PiActionId.of(C_INGRESS + DOT + "send_to_cpu");
+    private static final PiActionId ACT_ID_SET_EGRESS_PORT =
+            PiActionId.of(C_INGRESS + DOT + "set_egress_port");
+
+    private static final PiActionParamId ACT_PARAM_ID_PORT =
+            PiActionParamId.of("port");
+
+    private static final BiMap<Integer, PiTableId> TABLE_MAP =
+            new ImmutableBiMap.Builder<Integer, PiTableId>()
+                    .put(0, TABLE_L2_FWD_ID)
+                    .build();
 
     private static final BiMap<Criterion.Type, PiMatchFieldId> CRITERION_MAP =
             new ImmutableBiMap.Builder<Criterion.Type, PiMatchFieldId>()
@@ -97,129 +112,6 @@
                     .build();
 
     @Override
-    public PiAction mapTreatment(TrafficTreatment treatment, PiTableId piTableId)
-            throws PiInterpreterException {
-
-        if (treatment.allInstructions().size() == 0) {
-            // No instructions means drop for us.
-            return PiAction.builder()
-                    .withId(PiActionId.of(DROP))
-                    .build();
-        } else if (treatment.allInstructions().size() > 1) {
-            // We understand treatments with only 1 instruction.
-            throw new PiInterpreterException("Treatment has multiple instructions");
-        }
-
-        // Get the first and only instruction.
-        Instruction instruction = treatment.allInstructions().get(0);
-
-        switch (instruction.type()) {
-            case OUTPUT:
-                // We understand only instructions of type OUTPUT.
-                Instructions.OutputInstruction outInstruction = (Instructions.OutputInstruction) instruction;
-                PortNumber port = outInstruction.port();
-                if (!port.isLogical()) {
-                    return PiAction.builder()
-                            .withId(PiActionId.of(SET_EGRESS_PORT))
-                            .withParameter(new PiActionParam(PiActionParamId.of(PORT), copyFrom(port.toLong())))
-                            .build();
-                } else if (port.equals(CONTROLLER)) {
-                    return PiAction.builder()
-                            .withId(PiActionId.of(SEND_TO_CPU))
-                            .build();
-                } else {
-                    throw new PiInterpreterException(format("Output on logical port '%s' not supported", port));
-                }
-            default:
-                throw new PiInterpreterException(format("Instruction of type '%s' not supported", instruction.type()));
-        }
-    }
-
-    @Override
-    public Collection<PiPacketOperation> mapOutboundPacket(OutboundPacket packet)
-            throws PiInterpreterException {
-
-        TrafficTreatment treatment = packet.treatment();
-
-        // We support only packet-out with OUTPUT instructions.
-        List<Instructions.OutputInstruction> outInstructions = treatment.allInstructions().stream()
-                .filter(i -> i.type().equals(OUTPUT))
-                .map(i -> (Instructions.OutputInstruction) i)
-                .collect(toList());
-
-        if (treatment.allInstructions().size() != outInstructions.size()) {
-            // There are other instructions that are not of type OUTPUT.
-            throw new PiInterpreterException("Treatment not supported: " + treatment);
-        }
-
-        ImmutableList.Builder<PiPacketOperation> builder = ImmutableList.builder();
-
-        for (Instructions.OutputInstruction outInst : outInstructions) {
-            if (outInst.port().isLogical() && !outInst.port().equals(FLOOD)) {
-                throw new PiInterpreterException(format("Output on logical port '%s' not supported", outInst.port()));
-            } else if (outInst.port().equals(FLOOD)) {
-                // Since main.p4 does not support flooding, we create a packet operation for each switch port.
-                DeviceService deviceService = handler().get(DeviceService.class);
-                for (Port port : deviceService.getPorts(packet.sendThrough())) {
-                    builder.add(createPiPacketOperation(packet.data(), port.number().toLong()));
-                }
-            } else {
-                builder.add(createPiPacketOperation(packet.data(), outInst.port().toLong()));
-            }
-        }
-        return builder.build();
-    }
-
-    @Override
-    public InboundPacket mapInboundPacket(PiPacketOperation packetIn)
-            throws PiInterpreterException {
-        // We assume that the packet is ethernet, which is fine since default.p4 can deparse only ethernet packets.
-        Ethernet ethPkt;
-        try {
-            ethPkt = Ethernet.deserializer().deserialize(packetIn.data().asArray(), 0, packetIn.data().size());
-        } catch (DeserializationException dex) {
-            throw new PiInterpreterException(dex.getMessage());
-        }
-
-        // Returns the ingress port packet metadata.
-        Optional<PiControlMetadata> packetMetadata = packetIn.metadatas().stream()
-                .filter(metadata -> metadata.id().toString().equals(INGRESS_PORT))
-                .findFirst();
-
-        if (packetMetadata.isPresent()) {
-            ImmutableByteSequence portByteSequence = packetMetadata.get().value();
-            short s = portByteSequence.asReadOnlyBuffer().getShort();
-            ConnectPoint receivedFrom = new ConnectPoint(packetIn.deviceId(), PortNumber.portNumber(s));
-            return new DefaultInboundPacket(receivedFrom, ethPkt, packetIn.data().asReadOnlyBuffer());
-        } else {
-            throw new PiInterpreterException(format(
-                    "Missing metadata '%s' in packet-in received from '%s': %s",
-                    INGRESS_PORT, packetIn.deviceId(), packetIn));
-        }
-    }
-
-    private PiPacketOperation createPiPacketOperation(ByteBuffer data, long portNumber) throws PiInterpreterException {
-        PiControlMetadata metadata = createControlMetadata(portNumber);
-        return PiPacketOperation.builder()
-                .forDevice(this.data().deviceId())
-                .withType(PACKET_OUT)
-                .withData(copyFrom(data))
-                .withMetadatas(ImmutableList.of(metadata))
-                .build();
-    }
-
-    private PiControlMetadata createControlMetadata(long portNumber) throws PiInterpreterException {
-        try {
-            return PiControlMetadata.builder()
-                    .withId(PiControlMetadataId.of(EGRESS_PORT))
-                    .withValue(copyFrom(portNumber).fit(PORT_FIELD_BITWIDTH))
-                    .build();
-        } catch (ImmutableByteSequence.ByteSequenceTrimException e) {
-            throw new PiInterpreterException(format("Port number %d too big, %s", portNumber, e.getMessage()));
-        }
-    }
-
-    @Override
     public Optional<PiMatchFieldId> mapCriterionType(Criterion.Type type) {
         return Optional.ofNullable(CRITERION_MAP.get(type));
     }
@@ -238,4 +130,139 @@
     public Optional<Integer> mapPiTableId(PiTableId piTableId) {
         return Optional.ofNullable(TABLE_MAP.inverse().get(piTableId));
     }
+
+    @Override
+    public PiAction mapTreatment(TrafficTreatment treatment, PiTableId piTableId)
+            throws PiInterpreterException {
+
+        if (piTableId != TABLE_L2_FWD_ID) {
+            throw new PiInterpreterException(
+                    "Can map treatments only for 't_l2_fwd' table");
+        }
+
+        if (treatment.allInstructions().size() == 0) {
+            // 0 instructions means "NoAction"
+            return PiAction.builder().withId(ACT_ID_NOP).build();
+        } else if (treatment.allInstructions().size() > 1) {
+            // We understand treatments with only 1 instruction.
+            throw new PiInterpreterException("Treatment has multiple instructions");
+        }
+
+        // Get the first and only instruction.
+        Instruction instruction = treatment.allInstructions().get(0);
+
+        if (instruction.type() != OUTPUT) {
+            // We can map only instructions of type OUTPUT.
+            throw new PiInterpreterException(format(
+                    "Instruction of type '%s' not supported", instruction.type()));
+        }
+
+        OutputInstruction outInstruction = (OutputInstruction) instruction;
+        PortNumber port = outInstruction.port();
+        if (!port.isLogical()) {
+            return PiAction.builder()
+                    .withId(ACT_ID_SET_EGRESS_PORT)
+                    .withParameter(new PiActionParam(
+                            ACT_PARAM_ID_PORT, copyFrom(port.toLong())))
+                    .build();
+        } else if (port.equals(CONTROLLER)) {
+            return PiAction.builder()
+                    .withId(ACT_ID_SEND_TO_CPU)
+                    .build();
+        } else {
+            throw new PiInterpreterException(format(
+                    "Output on logical port '%s' not supported", port));
+        }
+    }
+
+    @Override
+    public Collection<PiPacketOperation> mapOutboundPacket(OutboundPacket packet)
+            throws PiInterpreterException {
+
+        TrafficTreatment treatment = packet.treatment();
+
+        // We support only packet-out with OUTPUT instructions.
+        if (treatment.allInstructions().size() != 1 &&
+                treatment.allInstructions().get(0).type() != OUTPUT) {
+            throw new PiInterpreterException(
+                    "Treatment not supported: " + treatment.toString());
+        }
+
+        Instruction instruction = treatment.allInstructions().get(0);
+        PortNumber port = ((OutputInstruction) instruction).port();
+        List<PiPacketOperation> piPacketOps = Lists.newArrayList();
+
+        if (!port.isLogical()) {
+            piPacketOps.add(createPiPacketOp(packet.data(), port.toLong()));
+        } else if (port.equals(FLOOD)) {
+            // Since mytunnel.p4 does not support flooding, we create a packet
+            // operation for each switch port.
+            DeviceService deviceService = handler().get(DeviceService.class);
+            DeviceId deviceId = packet.sendThrough();
+            for (Port p : deviceService.getPorts(deviceId)) {
+                piPacketOps.add(createPiPacketOp(packet.data(), p.number().toLong()));
+            }
+        } else {
+            throw new PiInterpreterException(format(
+                    "Output on logical port '%s' not supported", port));
+        }
+
+        return piPacketOps;
+    }
+
+    @Override
+    public InboundPacket mapInboundPacket(PiPacketOperation packetIn)
+            throws PiInterpreterException {
+        // We assume that the packet is ethernet, which is fine since mytunnel.p4
+        // can deparse only ethernet packets.
+        Ethernet ethPkt;
+
+        try {
+            ethPkt = Ethernet.deserializer().deserialize(
+                    packetIn.data().asArray(), 0, packetIn.data().size());
+        } catch (DeserializationException dex) {
+            throw new PiInterpreterException(dex.getMessage());
+        }
+
+        // Returns the ingress port packet metadata.
+        Optional<PiControlMetadata> packetMetadata = packetIn.metadatas().stream()
+                .filter(metadata -> metadata.id().toString().equals(INGRESS_PORT))
+                .findFirst();
+
+        if (packetMetadata.isPresent()) {
+            short s = packetMetadata.get().value().asReadOnlyBuffer().getShort();
+            ConnectPoint receivedFrom = new ConnectPoint(
+                    packetIn.deviceId(), PortNumber.portNumber(s));
+            return new DefaultInboundPacket(
+                    receivedFrom, ethPkt, packetIn.data().asReadOnlyBuffer());
+        } else {
+            throw new PiInterpreterException(format(
+                    "Missing metadata '%s' in packet-in received from '%s': %s",
+                    INGRESS_PORT, packetIn.deviceId(), packetIn));
+        }
+    }
+
+    private PiPacketOperation createPiPacketOp(ByteBuffer data, long portNumber)
+            throws PiInterpreterException {
+        PiControlMetadata metadata = createControlMetadata(portNumber);
+        return PiPacketOperation.builder()
+                .forDevice(this.data().deviceId())
+                .withType(PACKET_OUT)
+                .withData(copyFrom(data))
+                .withMetadatas(ImmutableList.of(metadata))
+                .build();
+    }
+
+    private PiControlMetadata createControlMetadata(long portNumber)
+            throws PiInterpreterException {
+        try {
+            return PiControlMetadata.builder()
+                    .withId(PiControlMetadataId.of(EGRESS_PORT))
+                    .withValue(copyFrom(portNumber).fit(PORT_FIELD_BITWIDTH))
+                    .build();
+        } catch (ImmutableByteSequence.ByteSequenceTrimException e) {
+            throw new PiInterpreterException(format(
+                    "Port number %d too big, %s", portNumber, e.getMessage()));
+        }
+    }
 }
diff --git a/apps/p4-tutorial/pipeconf/src/main/java/org/onosproject/p4tutorial/pipeconf/PortStatisticsDiscoveryImpl.java b/apps/p4-tutorial/pipeconf/src/main/java/org/onosproject/p4tutorial/pipeconf/PortStatisticsDiscoveryImpl.java
index 1f204e2..f5fd70c 100644
--- a/apps/p4-tutorial/pipeconf/src/main/java/org/onosproject/p4tutorial/pipeconf/PortStatisticsDiscoveryImpl.java
+++ b/apps/p4-tutorial/pipeconf/src/main/java/org/onosproject/p4tutorial/pipeconf/PortStatisticsDiscoveryImpl.java
@@ -44,15 +44,15 @@
 import static org.onosproject.net.pi.model.PiCounterType.INDIRECT;
 
 /**
- * Implementation of the PortStatisticsDiscovery behaviour for the main.p4 program. This behaviour works by using a
+ * Implementation of the PortStatisticsDiscovery behaviour for the mytunnel.p4 program. This behaviour works by using a
  * P4Runtime client to read the values of the ingress/egress port counters defined in the P4 program.
  */
 public final class PortStatisticsDiscoveryImpl extends AbstractHandlerBehaviour implements PortStatisticsDiscovery {
 
     private static final Logger log = LoggerFactory.getLogger(PortStatisticsDiscoveryImpl.class);
 
-    private static final PiCounterId INGRESS_COUNTER_ID = PiCounterId.of("igr_port_counter");
-    private static final PiCounterId EGRESS_COUNTER_ID = PiCounterId.of("egr_port_counter");
+    private static final PiCounterId INGRESS_COUNTER_ID = PiCounterId.of("c_ingress.rx_port_counter");
+    private static final PiCounterId EGRESS_COUNTER_ID = PiCounterId.of("c_ingress.tx_port_counter");
 
     @Override
     public Collection<PortStatistics> discoverPortStatistics() {
diff --git a/apps/p4-tutorial/pipeconf/src/main/resources/Makefile b/apps/p4-tutorial/pipeconf/src/main/resources/Makefile
index 9a6a8d0..adfd7ed 100644
--- a/apps/p4-tutorial/pipeconf/src/main/resources/Makefile
+++ b/apps/p4-tutorial/pipeconf/src/main/resources/Makefile
@@ -1,6 +1,4 @@
-all: main
+all: mytunnel
 
-main: main.p4
-	p4c-bm2-ss -o main.json --p4runtime-file main.p4info --p4runtime-format text main.p4
-	# Fix for BMv2/p4c bug...
-	sed -i -e 's/"value" : "0xff"/"value" : "0x00ff"/g' main.json
\ No newline at end of file
+mytunnel: mytunnel.p4
+	p4c-bm2-ss -o mytunnel.json --p4runtime-file mytunnel.p4info --p4runtime-format text mytunnel.p4
diff --git a/apps/p4-tutorial/pipeconf/src/main/resources/main.json b/apps/p4-tutorial/pipeconf/src/main/resources/main.json
deleted file mode 100644
index 3d059bb..0000000
--- a/apps/p4-tutorial/pipeconf/src/main/resources/main.json
+++ /dev/null
@@ -1,931 +0,0 @@
-{
-  "program" : "main.p4",
-  "__meta__" : {
-    "version" : [2, 7],
-    "compiler" : "https://github.com/p4lang/p4c"
-  },
-  "header_types" : [
-    {
-      "name" : "scalars_0",
-      "id" : 0,
-      "fields" : [
-        ["tmp", 32, false],
-        ["tmp_0", 32, false]
-      ]
-    },
-    {
-      "name" : "ethernet_t",
-      "id" : 1,
-      "fields" : [
-        ["dst_addr", 48, false],
-        ["src_addr", 48, false],
-        ["ether_type", 16, false]
-      ]
-    },
-    {
-      "name" : "ipv4_t",
-      "id" : 2,
-      "fields" : [
-        ["version", 4, false],
-        ["ihl", 4, false],
-        ["diffserv", 8, false],
-        ["len", 16, false],
-        ["identification", 16, false],
-        ["flags", 3, false],
-        ["frag_offset", 13, false],
-        ["ttl", 8, false],
-        ["protocol", 8, false],
-        ["hdr_checksum", 16, false],
-        ["src_addr", 32, false],
-        ["dst_addr", 32, false]
-      ]
-    },
-    {
-      "name" : "packet_out_header_t",
-      "id" : 3,
-      "fields" : [
-        ["egress_port", 9, false],
-        ["_padding", 7, false]
-      ]
-    },
-    {
-      "name" : "packet_in_header_t",
-      "id" : 4,
-      "fields" : [
-        ["ingress_port", 9, false],
-        ["_padding_0", 7, false]
-      ]
-    },
-    {
-      "name" : "standard_metadata",
-      "id" : 5,
-      "fields" : [
-        ["ingress_port", 9, false],
-        ["egress_spec", 9, false],
-        ["egress_port", 9, false],
-        ["clone_spec", 32, false],
-        ["instance_type", 32, false],
-        ["drop", 1, false],
-        ["recirculate_port", 16, false],
-        ["packet_length", 32, false],
-        ["enq_timestamp", 32, false],
-        ["enq_qdepth", 19, false],
-        ["deq_timedelta", 32, false],
-        ["deq_qdepth", 19, false],
-        ["ingress_global_timestamp", 48, false],
-        ["lf_field_list", 32, false],
-        ["mcast_grp", 16, false],
-        ["resubmit_flag", 1, false],
-        ["egress_rid", 16, false],
-        ["_padding_1", 5, false]
-      ]
-    }
-  ],
-  "headers" : [
-    {
-      "name" : "scalars",
-      "id" : 0,
-      "header_type" : "scalars_0",
-      "metadata" : true,
-      "pi_omit" : true
-    },
-    {
-      "name" : "standard_metadata",
-      "id" : 1,
-      "header_type" : "standard_metadata",
-      "metadata" : true,
-      "pi_omit" : true
-    },
-    {
-      "name" : "ethernet",
-      "id" : 2,
-      "header_type" : "ethernet_t",
-      "metadata" : false,
-      "pi_omit" : true
-    },
-    {
-      "name" : "ipv4",
-      "id" : 3,
-      "header_type" : "ipv4_t",
-      "metadata" : false,
-      "pi_omit" : true
-    },
-    {
-      "name" : "packet_out",
-      "id" : 4,
-      "header_type" : "packet_out_header_t",
-      "metadata" : false,
-      "pi_omit" : true
-    },
-    {
-      "name" : "packet_in",
-      "id" : 5,
-      "header_type" : "packet_in_header_t",
-      "metadata" : false,
-      "pi_omit" : true
-    }
-  ],
-  "header_stacks" : [],
-  "header_union_types" : [],
-  "header_unions" : [],
-  "header_union_stacks" : [],
-  "field_lists" : [],
-  "errors" : [
-    ["NoError", 1],
-    ["PacketTooShort", 2],
-    ["NoMatch", 3],
-    ["StackOutOfBounds", 4],
-    ["HeaderTooShort", 5],
-    ["ParserTimeout", 6]
-  ],
-  "enums" : [],
-  "parsers" : [
-    {
-      "name" : "parser",
-      "id" : 0,
-      "init_state" : "start",
-      "parse_states" : [
-        {
-          "name" : "start",
-          "id" : 0,
-          "parser_ops" : [],
-          "transitions" : [
-            {
-              "value" : "0x00ff",
-              "mask" : null,
-              "next_state" : "parse_packet_out"
-            },
-            {
-              "value" : "default",
-              "mask" : null,
-              "next_state" : "parse_ethernet"
-            }
-          ],
-          "transition_key" : [
-            {
-              "type" : "field",
-              "value" : ["standard_metadata", "ingress_port"]
-            }
-          ]
-        },
-        {
-          "name" : "parse_packet_out",
-          "id" : 1,
-          "parser_ops" : [
-            {
-              "parameters" : [
-                {
-                  "type" : "regular",
-                  "value" : "packet_out"
-                }
-              ],
-              "op" : "extract"
-            }
-          ],
-          "transitions" : [
-            {
-              "value" : "default",
-              "mask" : null,
-              "next_state" : "parse_ethernet"
-            }
-          ],
-          "transition_key" : []
-        },
-        {
-          "name" : "parse_ethernet",
-          "id" : 2,
-          "parser_ops" : [
-            {
-              "parameters" : [
-                {
-                  "type" : "regular",
-                  "value" : "ethernet"
-                }
-              ],
-              "op" : "extract"
-            }
-          ],
-          "transitions" : [
-            {
-              "value" : "0x0800",
-              "mask" : null,
-              "next_state" : "parse_ipv4"
-            },
-            {
-              "value" : "default",
-              "mask" : null,
-              "next_state" : null
-            }
-          ],
-          "transition_key" : [
-            {
-              "type" : "field",
-              "value" : ["ethernet", "ether_type"]
-            }
-          ]
-        },
-        {
-          "name" : "parse_ipv4",
-          "id" : 3,
-          "parser_ops" : [
-            {
-              "parameters" : [
-                {
-                  "type" : "regular",
-                  "value" : "ipv4"
-                }
-              ],
-              "op" : "extract"
-            }
-          ],
-          "transitions" : [
-            {
-              "value" : "default",
-              "mask" : null,
-              "next_state" : null
-            }
-          ],
-          "transition_key" : []
-        }
-      ]
-    }
-  ],
-  "deparsers" : [
-    {
-      "name" : "deparser",
-      "id" : 0,
-      "source_info" : {
-        "filename" : "main.p4",
-        "line" : 264,
-        "column" : 8,
-        "source_fragment" : "DeparserImpl"
-      },
-      "order" : ["packet_in", "ethernet", "ipv4"]
-    }
-  ],
-  "meter_arrays" : [],
-  "counter_arrays" : [
-    {
-      "name" : "egr_port_counter",
-      "id" : 0,
-      "source_info" : {
-        "filename" : "main.p4",
-        "line" : 181,
-        "column" : 38,
-        "source_fragment" : "egr_port_counter"
-      },
-      "size" : 511,
-      "is_direct" : false
-    },
-    {
-      "name" : "igr_port_counter",
-      "id" : 1,
-      "source_info" : {
-        "filename" : "main.p4",
-        "line" : 182,
-        "column" : 38,
-        "source_fragment" : "igr_port_counter"
-      },
-      "size" : 511,
-      "is_direct" : false
-    }
-  ],
-  "register_arrays" : [],
-  "calculations" : [],
-  "learn_lists" : [],
-  "actions" : [
-    {
-      "name" : "NoAction",
-      "id" : 0,
-      "runtime_data" : [],
-      "primitives" : []
-    },
-    {
-      "name" : "send_to_cpu",
-      "id" : 1,
-      "runtime_data" : [],
-      "primitives" : [
-        {
-          "op" : "assign",
-          "parameters" : [
-            {
-              "type" : "field",
-              "value" : ["standard_metadata", "egress_spec"]
-            },
-            {
-              "type" : "hexstr",
-              "value" : "0x00ff"
-            }
-          ],
-          "source_info" : {
-            "filename" : "main.p4",
-            "line" : 24,
-            "column" : 24,
-            "source_fragment" : "255; ..."
-          }
-        },
-        {
-          "op" : "add_header",
-          "parameters" : [
-            {
-              "type" : "header",
-              "value" : "packet_in"
-            }
-          ],
-          "source_info" : {
-            "filename" : "main.p4",
-            "line" : 137,
-            "column" : 8,
-            "source_fragment" : "hdr.packet_in.setValid()"
-          }
-        },
-        {
-          "op" : "assign",
-          "parameters" : [
-            {
-              "type" : "field",
-              "value" : ["packet_in", "ingress_port"]
-            },
-            {
-              "type" : "field",
-              "value" : ["standard_metadata", "ingress_port"]
-            }
-          ],
-          "source_info" : {
-            "filename" : "main.p4",
-            "line" : 138,
-            "column" : 8,
-            "source_fragment" : "hdr.packet_in.ingress_port = standard_metadata.ingress_port"
-          }
-        }
-      ]
-    },
-    {
-      "name" : "set_egress_port",
-      "id" : 2,
-      "runtime_data" : [
-        {
-          "name" : "port",
-          "bitwidth" : 9
-        }
-      ],
-      "primitives" : [
-        {
-          "op" : "assign",
-          "parameters" : [
-            {
-              "type" : "field",
-              "value" : ["standard_metadata", "egress_spec"]
-            },
-            {
-              "type" : "runtime_data",
-              "value" : 0
-            }
-          ],
-          "source_info" : {
-            "filename" : "main.p4",
-            "line" : 142,
-            "column" : 8,
-            "source_fragment" : "standard_metadata.egress_spec = port"
-          }
-        }
-      ]
-    },
-    {
-      "name" : "_drop",
-      "id" : 3,
-      "runtime_data" : [],
-      "primitives" : [
-        {
-          "op" : "assign",
-          "parameters" : [
-            {
-              "type" : "field",
-              "value" : ["standard_metadata", "egress_spec"]
-            },
-            {
-              "type" : "hexstr",
-              "value" : "0x01ff"
-            }
-          ],
-          "source_info" : {
-            "filename" : "main.p4",
-            "line" : 25,
-            "column" : 25,
-            "source_fragment" : "511; ..."
-          }
-        }
-      ]
-    },
-    {
-      "name" : "_drop",
-      "id" : 4,
-      "runtime_data" : [],
-      "primitives" : [
-        {
-          "op" : "assign",
-          "parameters" : [
-            {
-              "type" : "field",
-              "value" : ["standard_metadata", "egress_spec"]
-            },
-            {
-              "type" : "hexstr",
-              "value" : "0x01ff"
-            }
-          ],
-          "source_info" : {
-            "filename" : "main.p4",
-            "line" : 25,
-            "column" : 25,
-            "source_fragment" : "511; ..."
-          }
-        }
-      ]
-    },
-    {
-      "name" : "act",
-      "id" : 5,
-      "runtime_data" : [],
-      "primitives" : [
-        {
-          "op" : "assign",
-          "parameters" : [
-            {
-              "type" : "field",
-              "value" : ["standard_metadata", "egress_spec"]
-            },
-            {
-              "type" : "field",
-              "value" : ["packet_out", "egress_port"]
-            }
-          ],
-          "source_info" : {
-            "filename" : "main.p4",
-            "line" : 195,
-            "column" : 12,
-            "source_fragment" : "standard_metadata.egress_spec = hdr.packet_out.egress_port"
-          }
-        },
-        {
-          "op" : "remove_header",
-          "parameters" : [
-            {
-              "type" : "header",
-              "value" : "packet_out"
-            }
-          ],
-          "source_info" : {
-            "filename" : "main.p4",
-            "line" : 196,
-            "column" : 12,
-            "source_fragment" : "hdr.packet_out.setInvalid()"
-          }
-        }
-      ]
-    },
-    {
-      "name" : "act_0",
-      "id" : 6,
-      "runtime_data" : [],
-      "primitives" : [
-        {
-          "op" : "assign",
-          "parameters" : [
-            {
-              "type" : "field",
-              "value" : ["scalars", "tmp"]
-            },
-            {
-              "type" : "expression",
-              "value" : {
-                "type" : "expression",
-                "value" : {
-                  "op" : "&",
-                  "left" : {
-                    "type" : "field",
-                    "value" : ["standard_metadata", "egress_spec"]
-                  },
-                  "right" : {
-                    "type" : "hexstr",
-                    "value" : "0xffffffff"
-                  }
-                }
-              }
-            }
-          ]
-        },
-        {
-          "op" : "count",
-          "parameters" : [
-            {
-              "type" : "counter_array",
-              "value" : "egr_port_counter"
-            },
-            {
-              "type" : "field",
-              "value" : ["scalars", "tmp"]
-            }
-          ],
-          "source_info" : {
-            "filename" : "main.p4",
-            "line" : 218,
-            "column" : 12,
-            "source_fragment" : "egr_port_counter.count((bit<32>) standard_metadata.egress_spec)"
-          }
-        }
-      ]
-    },
-    {
-      "name" : "act_1",
-      "id" : 7,
-      "runtime_data" : [],
-      "primitives" : [
-        {
-          "op" : "assign",
-          "parameters" : [
-            {
-              "type" : "field",
-              "value" : ["scalars", "tmp_0"]
-            },
-            {
-              "type" : "expression",
-              "value" : {
-                "type" : "expression",
-                "value" : {
-                  "op" : "&",
-                  "left" : {
-                    "type" : "field",
-                    "value" : ["standard_metadata", "ingress_port"]
-                  },
-                  "right" : {
-                    "type" : "hexstr",
-                    "value" : "0xffffffff"
-                  }
-                }
-              }
-            }
-          ]
-        },
-        {
-          "op" : "count",
-          "parameters" : [
-            {
-              "type" : "counter_array",
-              "value" : "igr_port_counter"
-            },
-            {
-              "type" : "field",
-              "value" : ["scalars", "tmp_0"]
-            }
-          ],
-          "source_info" : {
-            "filename" : "main.p4",
-            "line" : 221,
-            "column" : 12,
-            "source_fragment" : "igr_port_counter.count((bit<32>) standard_metadata.ingress_port)"
-          }
-        }
-      ]
-    }
-  ],
-  "pipelines" : [
-    {
-      "name" : "ingress",
-      "id" : 0,
-      "source_info" : {
-        "filename" : "main.p4",
-        "line" : 126,
-        "column" : 8,
-        "source_fragment" : "IngressImpl"
-      },
-      "init_table" : "node_2",
-      "tables" : [
-        {
-          "name" : "tbl_act",
-          "id" : 0,
-          "key" : [],
-          "match_type" : "exact",
-          "type" : "simple",
-          "max_size" : 1024,
-          "with_counters" : false,
-          "support_timeout" : false,
-          "direct_meters" : null,
-          "action_ids" : [5],
-          "actions" : ["act"],
-          "base_default_next" : "node_7",
-          "next_tables" : {
-            "act" : "node_7"
-          },
-          "default_entry" : {
-            "action_id" : 5,
-            "action_const" : true,
-            "action_data" : [],
-            "action_entry_const" : true
-          }
-        },
-        {
-          "name" : "table0",
-          "id" : 1,
-          "source_info" : {
-            "filename" : "main.p4",
-            "line" : 149,
-            "column" : 10,
-            "source_fragment" : "table0"
-          },
-          "key" : [
-            {
-              "match_type" : "ternary",
-              "target" : ["standard_metadata", "ingress_port"],
-              "mask" : null
-            },
-            {
-              "match_type" : "ternary",
-              "target" : ["ethernet", "dst_addr"],
-              "mask" : null
-            },
-            {
-              "match_type" : "ternary",
-              "target" : ["ethernet", "src_addr"],
-              "mask" : null
-            },
-            {
-              "match_type" : "ternary",
-              "target" : ["ethernet", "ether_type"],
-              "mask" : null
-            }
-          ],
-          "match_type" : "ternary",
-          "type" : "simple",
-          "max_size" : 1024,
-          "with_counters" : false,
-          "support_timeout" : false,
-          "direct_meters" : null,
-          "action_ids" : [2, 1, 3],
-          "actions" : ["set_egress_port", "send_to_cpu", "_drop"],
-          "base_default_next" : "node_7",
-          "next_tables" : {
-            "set_egress_port" : "node_5",
-            "send_to_cpu" : "node_7",
-            "_drop" : "node_7"
-          },
-          "default_entry" : {
-            "action_id" : 3,
-            "action_const" : false,
-            "action_data" : [],
-            "action_entry_const" : false
-          }
-        },
-        {
-          "name" : "ip_proto_filter_table",
-          "id" : 2,
-          "source_info" : {
-            "filename" : "main.p4",
-            "line" : 164,
-            "column" : 10,
-            "source_fragment" : "ip_proto_filter_table"
-          },
-          "key" : [
-            {
-              "match_type" : "ternary",
-              "target" : ["ipv4", "src_addr"],
-              "mask" : null
-            },
-            {
-              "match_type" : "exact",
-              "target" : ["ipv4", "protocol"],
-              "mask" : null
-            }
-          ],
-          "match_type" : "ternary",
-          "type" : "simple",
-          "max_size" : 1024,
-          "with_counters" : false,
-          "support_timeout" : false,
-          "direct_meters" : null,
-          "action_ids" : [4, 0],
-          "actions" : ["_drop", "NoAction"],
-          "base_default_next" : "node_7",
-          "next_tables" : {
-            "_drop" : "node_7",
-            "NoAction" : "node_7"
-          },
-          "default_entry" : {
-            "action_id" : 0,
-            "action_const" : false,
-            "action_data" : [],
-            "action_entry_const" : false
-          }
-        },
-        {
-          "name" : "tbl_act_0",
-          "id" : 3,
-          "key" : [],
-          "match_type" : "exact",
-          "type" : "simple",
-          "max_size" : 1024,
-          "with_counters" : false,
-          "support_timeout" : false,
-          "direct_meters" : null,
-          "action_ids" : [6],
-          "actions" : ["act_0"],
-          "base_default_next" : "node_9",
-          "next_tables" : {
-            "act_0" : "node_9"
-          },
-          "default_entry" : {
-            "action_id" : 6,
-            "action_const" : true,
-            "action_data" : [],
-            "action_entry_const" : true
-          }
-        },
-        {
-          "name" : "tbl_act_1",
-          "id" : 4,
-          "key" : [],
-          "match_type" : "exact",
-          "type" : "simple",
-          "max_size" : 1024,
-          "with_counters" : false,
-          "support_timeout" : false,
-          "direct_meters" : null,
-          "action_ids" : [7],
-          "actions" : ["act_1"],
-          "base_default_next" : null,
-          "next_tables" : {
-            "act_1" : null
-          },
-          "default_entry" : {
-            "action_id" : 7,
-            "action_const" : true,
-            "action_data" : [],
-            "action_entry_const" : true
-          }
-        }
-      ],
-      "action_profiles" : [],
-      "conditionals" : [
-        {
-          "name" : "node_2",
-          "id" : 0,
-          "source_info" : {
-            "filename" : "main.p4",
-            "line" : 188,
-            "column" : 12,
-            "source_fragment" : "standard_metadata.ingress_port == CPU_PORT"
-          },
-          "expression" : {
-            "type" : "expression",
-            "value" : {
-              "op" : "==",
-              "left" : {
-                "type" : "field",
-                "value" : ["standard_metadata", "ingress_port"]
-              },
-              "right" : {
-                "type" : "hexstr",
-                "value" : "0x00ff"
-              }
-            }
-          },
-          "true_next" : "tbl_act",
-          "false_next" : "table0"
-        },
-        {
-          "name" : "node_5",
-          "id" : 1,
-          "source_info" : {
-            "filename" : "main.p4",
-            "line" : 205,
-            "column" : 24,
-            "source_fragment" : "hdr.ipv4.isValid()"
-          },
-          "expression" : {
-            "type" : "expression",
-            "value" : {
-              "op" : "==",
-              "left" : {
-                "type" : "field",
-                "value" : ["ipv4", "$valid$"]
-              },
-              "right" : {
-                "type" : "hexstr",
-                "value" : "0x01"
-              }
-            }
-          },
-          "true_next" : "ip_proto_filter_table",
-          "false_next" : "node_7"
-        },
-        {
-          "name" : "node_7",
-          "id" : 2,
-          "source_info" : {
-            "filename" : "main.p4",
-            "line" : 217,
-            "column" : 12,
-            "source_fragment" : "standard_metadata.egress_spec < 511"
-          },
-          "expression" : {
-            "type" : "expression",
-            "value" : {
-              "op" : "<",
-              "left" : {
-                "type" : "field",
-                "value" : ["standard_metadata", "egress_spec"]
-              },
-              "right" : {
-                "type" : "hexstr",
-                "value" : "0x01ff"
-              }
-            }
-          },
-          "true_next" : "tbl_act_0",
-          "false_next" : "node_9"
-        },
-        {
-          "name" : "node_9",
-          "id" : 3,
-          "source_info" : {
-            "filename" : "main.p4",
-            "line" : 220,
-            "column" : 12,
-            "source_fragment" : "standard_metadata.ingress_port < 511"
-          },
-          "expression" : {
-            "type" : "expression",
-            "value" : {
-              "op" : "<",
-              "left" : {
-                "type" : "field",
-                "value" : ["standard_metadata", "ingress_port"]
-              },
-              "right" : {
-                "type" : "hexstr",
-                "value" : "0x01ff"
-              }
-            }
-          },
-          "false_next" : null,
-          "true_next" : "tbl_act_1"
-        }
-      ]
-    },
-    {
-      "name" : "egress",
-      "id" : 1,
-      "source_info" : {
-        "filename" : "main.p4",
-        "line" : 230,
-        "column" : 8,
-        "source_fragment" : "EgressImpl"
-      },
-      "init_table" : null,
-      "tables" : [],
-      "action_profiles" : [],
-      "conditionals" : []
-    }
-  ],
-  "checksums" : [],
-  "force_arith" : [],
-  "extern_instances" : [],
-  "field_aliases" : [
-    [
-      "queueing_metadata.enq_timestamp",
-      ["standard_metadata", "enq_timestamp"]
-    ],
-    [
-      "queueing_metadata.enq_qdepth",
-      ["standard_metadata", "enq_qdepth"]
-    ],
-    [
-      "queueing_metadata.deq_timedelta",
-      ["standard_metadata", "deq_timedelta"]
-    ],
-    [
-      "queueing_metadata.deq_qdepth",
-      ["standard_metadata", "deq_qdepth"]
-    ],
-    [
-      "intrinsic_metadata.ingress_global_timestamp",
-      ["standard_metadata", "ingress_global_timestamp"]
-    ],
-    [
-      "intrinsic_metadata.lf_field_list",
-      ["standard_metadata", "lf_field_list"]
-    ],
-    [
-      "intrinsic_metadata.mcast_grp",
-      ["standard_metadata", "mcast_grp"]
-    ],
-    [
-      "intrinsic_metadata.resubmit_flag",
-      ["standard_metadata", "resubmit_flag"]
-    ],
-    [
-      "intrinsic_metadata.egress_rid",
-      ["standard_metadata", "egress_rid"]
-    ]
-  ]
-}
\ No newline at end of file
diff --git a/apps/p4-tutorial/pipeconf/src/main/resources/main.p4 b/apps/p4-tutorial/pipeconf/src/main/resources/main.p4
deleted file mode 100644
index 9f0b809..0000000
--- a/apps/p4-tutorial/pipeconf/src/main/resources/main.p4
+++ /dev/null
@@ -1,284 +0,0 @@
-/*
- * Copyright 2017-present Open Networking Foundation
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <core.p4>
-#include <v1model.p4>
-
-#define ETH_TYPE_IPV4 0x0800
-#define MAX_PORTS 511
-
-typedef bit<9> port_t;
-const port_t CPU_PORT = 255;
-const port_t DROP_PORT = 511;
-
-//------------------------------------------------------------------------------
-// HEADERS
-//------------------------------------------------------------------------------
-
-header ethernet_t {
-    bit<48> dst_addr;
-    bit<48> src_addr;
-    bit<16> ether_type;
-}
-
-header ipv4_t {
-    bit<4>  version;
-    bit<4>  ihl;
-    bit<8>  diffserv;
-    bit<16> len;
-    bit<16> identification;
-    bit<3>  flags;
-    bit<13> frag_offset;
-    bit<8>  ttl;
-    bit<8>  protocol;
-    bit<16> hdr_checksum;
-    bit<32> src_addr;
-    bit<32> dst_addr;
-}
-
-/*
-Packet-in header. Prepended to packets sent to the controller and used to carry
-the original ingress port where the packet was received.
- */
-@controller_header("packet_in")
-header packet_in_header_t {
-    bit<9> ingress_port;
-}
-
-/*
-Packet-out header. Prepended to packets received by the controller and used to
-tell the switch on which physical port this packet should be forwarded.
- */
-@controller_header("packet_out")
-header packet_out_header_t {
-    bit<9> egress_port;
-}
-
-/*
-For convenience we collect all headers under the same struct.
- */
-struct headers_t {
-    ethernet_t ethernet;
-    ipv4_t ipv4;
-    packet_out_header_t packet_out;
-    packet_in_header_t packet_in;
-}
-
-/*
-Metadata can be used to carry information from one table to another.
- */
-struct metadata_t {
-    /* Empty. We don't use it in this program. */
-}
-
-//------------------------------------------------------------------------------
-// PARSER
-//------------------------------------------------------------------------------
-
-parser ParserImpl(packet_in packet,
-                  out headers_t hdr,
-                  inout metadata_t meta,
-                  inout standard_metadata_t standard_metadata) {
-
-    state start {
-        transition select(standard_metadata.ingress_port) {
-            CPU_PORT: parse_packet_out;
-            default: parse_ethernet;
-        }
-    }
-
-    state parse_packet_out {
-        packet.extract(hdr.packet_out);
-        transition parse_ethernet;
-    }
-
-    state parse_ethernet {
-        packet.extract(hdr.ethernet);
-        transition select(hdr.ethernet.ether_type) {
-            ETH_TYPE_IPV4: parse_ipv4;
-            default: accept;
-        }
-    }
-
-    state parse_ipv4 {
-        packet.extract(hdr.ipv4);
-        transition accept;
-    }
-}
-
-//------------------------------------------------------------------------------
-// INGRESS PIPELINE
-//------------------------------------------------------------------------------
-
-control IngressImpl(inout headers_t hdr,
-                    inout metadata_t meta,
-                    inout standard_metadata_t standard_metadata) {
-
-    action send_to_cpu() {
-        standard_metadata.egress_spec = CPU_PORT;
-        /*
-        Packets sent to the controller needs to be prepended with the packet-in
-        header. By setting it valid we make sure it will be deparsed before the
-        ethernet header (see DeparserImpl).
-         */
-        hdr.packet_in.setValid();
-        hdr.packet_in.ingress_port = standard_metadata.ingress_port;
-    }
-
-    action set_egress_port(port_t port) {
-        standard_metadata.egress_spec = port;
-    }
-
-    action _drop() {
-        standard_metadata.egress_spec = DROP_PORT;
-    }
-
-    table table0 {
-        key = {
-            standard_metadata.ingress_port  : ternary;
-            hdr.ethernet.dst_addr           : ternary;
-            hdr.ethernet.src_addr           : ternary;
-            hdr.ethernet.ether_type         : ternary;
-        }
-        actions = {
-            set_egress_port();
-            send_to_cpu();
-            _drop();
-        }
-        default_action = _drop();
-    }
-
-    table ip_proto_filter_table {
-        key = {
-            hdr.ipv4.src_addr : ternary;
-            hdr.ipv4.protocol : exact;
-        }
-        actions = {
-            _drop();
-        }
-    }
-
-    /*
-    Port counters.
-    We use these counter instances to count packets/bytes received/sent on each
-    port. BMv2 always counts both packets and bytes, even if the counter is
-    instantiated as "packets". For each counter we instantiate a number of cells
-    equal to MAX_PORTS.
-     */
-    counter(MAX_PORTS, CounterType.packets) egr_port_counter;
-    counter(MAX_PORTS, CounterType.packets) igr_port_counter;
-
-    /*
-    We define here the processing to be executed by this ingress pipeline.
-     */
-    apply {
-        if (standard_metadata.ingress_port == CPU_PORT) {
-            /*
-            Packet received from CPU_PORT, this is a packet-out sent by the
-            controller. Skip pipeline processing, set the egress port as
-            requested by the controller (packet_out header) and remove the
-            packet_out header.
-             */
-            standard_metadata.egress_spec = hdr.packet_out.egress_port;
-            hdr.packet_out.setInvalid();
-        } else {
-            /*
-            Packet received from switch port. Apply table0, if action is
-            set_egress_port and packet is IPv4, then apply
-            ip_proto_filter_table.
-             */
-            switch(table0.apply().action_run) {
-                set_egress_port: {
-                    if (hdr.ipv4.isValid()) {
-                        ip_proto_filter_table.apply();
-                    }
-                }
-            }
-        }
-
-        /*
-        For each port counter, we update the cell at index = ingress/egress
-        port. We avoid counting packets sent/received on CPU_PORT or dropped
-        (DROP_PORT).
-         */
-        if (standard_metadata.egress_spec < MAX_PORTS) {
-            egr_port_counter.count((bit<32>) standard_metadata.egress_spec);
-        }
-        if (standard_metadata.ingress_port < MAX_PORTS) {
-            igr_port_counter.count((bit<32>) standard_metadata.ingress_port);
-        }
-     }
-}
-
-//------------------------------------------------------------------------------
-// EGRESS PIPELINE
-//------------------------------------------------------------------------------
-
-control EgressImpl(inout headers_t hdr,
-                   inout metadata_t meta,
-                   inout standard_metadata_t standard_metadata) {
-    apply {
-        /*
-        Nothing to do on the egress pipeline.
-        */
-    }
-}
-
-//------------------------------------------------------------------------------
-// CHECKSUM HANDLING
-//------------------------------------------------------------------------------
-
-control VerifyChecksumImpl(in headers_t hdr, inout metadata_t meta) {
-    apply {
-        /*
-        Nothing to do here, we assume checksum is always correct.
-        */
-    }
-}
-
-control ComputeChecksumImpl(inout headers_t hdr, inout metadata_t meta) {
-    apply {
-        /*
-        Nothing to do here, as we do not modify packet headers.
-        */
-    }
-}
-
-//------------------------------------------------------------------------------
-// DEPARSER
-//------------------------------------------------------------------------------
-
-control DeparserImpl(packet_out packet, in headers_t hdr) {
-    apply {
-        /*
-        Deparse headers in order. Only valid headers are emitted.
-        */
-        packet.emit(hdr.packet_in);
-        packet.emit(hdr.ethernet);
-        packet.emit(hdr.ipv4);
-    }
-}
-
-//------------------------------------------------------------------------------
-// SWITCH INSTANTIATION
-//------------------------------------------------------------------------------
-
-V1Switch(ParserImpl(),
-         VerifyChecksumImpl(),
-         IngressImpl(),
-         EgressImpl(),
-         ComputeChecksumImpl(),
-         DeparserImpl()) main;
diff --git a/apps/p4-tutorial/pipeconf/src/main/resources/main.p4info b/apps/p4-tutorial/pipeconf/src/main/resources/main.p4info
deleted file mode 100644
index c4882d2..0000000
--- a/apps/p4-tutorial/pipeconf/src/main/resources/main.p4info
+++ /dev/null
@@ -1,147 +0,0 @@
-tables {
-  preamble {
-    id: 33617813
-    name: "table0"
-    alias: "table0"
-  }
-  match_fields {
-    id: 1
-    name: "standard_metadata.ingress_port"
-    bitwidth: 9
-    match_type: TERNARY
-  }
-  match_fields {
-    id: 2
-    name: "hdr.ethernet.dst_addr"
-    bitwidth: 48
-    match_type: TERNARY
-  }
-  match_fields {
-    id: 3
-    name: "hdr.ethernet.src_addr"
-    bitwidth: 48
-    match_type: TERNARY
-  }
-  match_fields {
-    id: 4
-    name: "hdr.ethernet.ether_type"
-    bitwidth: 16
-    match_type: TERNARY
-  }
-  action_refs {
-    id: 16794308
-  }
-  action_refs {
-    id: 16829080
-  }
-  action_refs {
-    id: 16784184
-  }
-  size: 1024
-}
-tables {
-  preamble {
-    id: 33573361
-    name: "ip_proto_filter_table"
-    alias: "ip_proto_filter_table"
-  }
-  match_fields {
-    id: 1
-    name: "hdr.ipv4.src_addr"
-    bitwidth: 32
-    match_type: TERNARY
-  }
-  match_fields {
-    id: 2
-    name: "hdr.ipv4.protocol"
-    bitwidth: 8
-    match_type: EXACT
-  }
-  action_refs {
-    id: 16784184
-  }
-  action_refs {
-    id: 16800567
-    annotations: "@defaultonly()"
-  }
-  size: 1024
-}
-actions {
-  preamble {
-    id: 16800567
-    name: "NoAction"
-    alias: "NoAction"
-  }
-}
-actions {
-  preamble {
-    id: 16829080
-    name: "send_to_cpu"
-    alias: "send_to_cpu"
-  }
-}
-actions {
-  preamble {
-    id: 16794308
-    name: "set_egress_port"
-    alias: "set_egress_port"
-  }
-  params {
-    id: 1
-    name: "port"
-    bitwidth: 9
-  }
-}
-actions {
-  preamble {
-    id: 16784184
-    name: "_drop"
-    alias: "_drop"
-  }
-}
-counters {
-  preamble {
-    id: 302012419
-    name: "egr_port_counter"
-    alias: "egr_port_counter"
-  }
-  spec {
-    unit: PACKETS
-  }
-  size: 511
-}
-counters {
-  preamble {
-    id: 302054463
-    name: "igr_port_counter"
-    alias: "igr_port_counter"
-  }
-  spec {
-    unit: PACKETS
-  }
-  size: 511
-}
-controller_packet_metadata {
-  preamble {
-    id: 2868941301
-    name: "packet_in"
-    annotations: "@controller_header(\"packet_in\")"
-  }
-  metadata {
-    id: 1
-    name: "ingress_port"
-    bitwidth: 9
-  }
-}
-controller_packet_metadata {
-  preamble {
-    id: 2868916615
-    name: "packet_out"
-    annotations: "@controller_header(\"packet_out\")"
-  }
-  metadata {
-    id: 1
-    name: "egress_port"
-    bitwidth: 9
-  }
-}
diff --git a/apps/p4-tutorial/pipeconf/src/main/resources/mytunnel.json b/apps/p4-tutorial/pipeconf/src/main/resources/mytunnel.json
new file mode 100644
index 0000000..d7b8e81
--- /dev/null
+++ b/apps/p4-tutorial/pipeconf/src/main/resources/mytunnel.json
@@ -0,0 +1,1552 @@
+{
+  "program" : "mytunnel.p4",
+  "__meta__" : {
+    "version" : [2, 7],
+    "compiler" : "https://github.com/p4lang/p4c"
+  },
+  "header_types" : [
+    {
+      "name" : "scalars_0",
+      "id" : 0,
+      "fields" : [
+        ["tmp", 32, false],
+        ["tmp_1", 32, false],
+        ["tmp_0", 1, false],
+        ["hasReturned_0", 1, false],
+        ["_padding_2", 6, false]
+      ]
+    },
+    {
+      "name" : "ethernet_t",
+      "id" : 1,
+      "fields" : [
+        ["dst_addr", 48, false],
+        ["src_addr", 48, false],
+        ["ether_type", 16, false]
+      ]
+    },
+    {
+      "name" : "my_tunnel_t",
+      "id" : 2,
+      "fields" : [
+        ["proto_id", 16, false],
+        ["tun_id", 32, false]
+      ]
+    },
+    {
+      "name" : "ipv4_t",
+      "id" : 3,
+      "fields" : [
+        ["version", 4, false],
+        ["ihl", 4, false],
+        ["diffserv", 8, false],
+        ["len", 16, false],
+        ["identification", 16, false],
+        ["flags", 3, false],
+        ["frag_offset", 13, false],
+        ["ttl", 8, false],
+        ["protocol", 8, false],
+        ["hdr_checksum", 16, false],
+        ["src_addr", 32, false],
+        ["dst_addr", 32, false]
+      ]
+    },
+    {
+      "name" : "packet_out_header_t",
+      "id" : 4,
+      "fields" : [
+        ["egress_port", 9, false],
+        ["_padding", 7, false]
+      ]
+    },
+    {
+      "name" : "packet_in_header_t",
+      "id" : 5,
+      "fields" : [
+        ["ingress_port", 9, false],
+        ["_padding_0", 7, false]
+      ]
+    },
+    {
+      "name" : "standard_metadata",
+      "id" : 6,
+      "fields" : [
+        ["ingress_port", 9, false],
+        ["egress_spec", 9, false],
+        ["egress_port", 9, false],
+        ["clone_spec", 32, false],
+        ["instance_type", 32, false],
+        ["drop", 1, false],
+        ["recirculate_port", 16, false],
+        ["packet_length", 32, false],
+        ["enq_timestamp", 32, false],
+        ["enq_qdepth", 19, false],
+        ["deq_timedelta", 32, false],
+        ["deq_qdepth", 19, false],
+        ["ingress_global_timestamp", 48, false],
+        ["egress_global_timestamp", 48, false],
+        ["lf_field_list", 32, false],
+        ["mcast_grp", 16, false],
+        ["resubmit_flag", 32, false],
+        ["egress_rid", 16, false],
+        ["checksum_error", 1, false],
+        ["recirculate_flag", 32, false],
+        ["_padding_1", 5, false]
+      ]
+    }
+  ],
+  "headers" : [
+    {
+      "name" : "scalars",
+      "id" : 0,
+      "header_type" : "scalars_0",
+      "metadata" : true,
+      "pi_omit" : true
+    },
+    {
+      "name" : "standard_metadata",
+      "id" : 1,
+      "header_type" : "standard_metadata",
+      "metadata" : true,
+      "pi_omit" : true
+    },
+    {
+      "name" : "ethernet",
+      "id" : 2,
+      "header_type" : "ethernet_t",
+      "metadata" : false,
+      "pi_omit" : true
+    },
+    {
+      "name" : "my_tunnel",
+      "id" : 3,
+      "header_type" : "my_tunnel_t",
+      "metadata" : false,
+      "pi_omit" : true
+    },
+    {
+      "name" : "ipv4",
+      "id" : 4,
+      "header_type" : "ipv4_t",
+      "metadata" : false,
+      "pi_omit" : true
+    },
+    {
+      "name" : "packet_out",
+      "id" : 5,
+      "header_type" : "packet_out_header_t",
+      "metadata" : false,
+      "pi_omit" : true
+    },
+    {
+      "name" : "packet_in",
+      "id" : 6,
+      "header_type" : "packet_in_header_t",
+      "metadata" : false,
+      "pi_omit" : true
+    }
+  ],
+  "header_stacks" : [],
+  "header_union_types" : [],
+  "header_unions" : [],
+  "header_union_stacks" : [],
+  "field_lists" : [],
+  "errors" : [
+    ["NoError", 1],
+    ["PacketTooShort", 2],
+    ["NoMatch", 3],
+    ["StackOutOfBounds", 4],
+    ["HeaderTooShort", 5],
+    ["ParserTimeout", 6]
+  ],
+  "enums" : [],
+  "parsers" : [
+    {
+      "name" : "parser",
+      "id" : 0,
+      "init_state" : "start",
+      "parse_states" : [
+        {
+          "name" : "start",
+          "id" : 0,
+          "parser_ops" : [],
+          "transitions" : [
+            {
+              "type" : "hexstr",
+              "value" : "0x00ff",
+              "mask" : null,
+              "next_state" : "parse_packet_out"
+            },
+            {
+              "value" : "default",
+              "mask" : null,
+              "next_state" : "parse_ethernet"
+            }
+          ],
+          "transition_key" : [
+            {
+              "type" : "field",
+              "value" : ["standard_metadata", "ingress_port"]
+            }
+          ]
+        },
+        {
+          "name" : "parse_packet_out",
+          "id" : 1,
+          "parser_ops" : [
+            {
+              "parameters" : [
+                {
+                  "type" : "regular",
+                  "value" : "packet_out"
+                }
+              ],
+              "op" : "extract"
+            }
+          ],
+          "transitions" : [
+            {
+              "value" : "default",
+              "mask" : null,
+              "next_state" : "parse_ethernet"
+            }
+          ],
+          "transition_key" : []
+        },
+        {
+          "name" : "parse_ethernet",
+          "id" : 2,
+          "parser_ops" : [
+            {
+              "parameters" : [
+                {
+                  "type" : "regular",
+                  "value" : "ethernet"
+                }
+              ],
+              "op" : "extract"
+            }
+          ],
+          "transitions" : [
+            {
+              "type" : "hexstr",
+              "value" : "0x1212",
+              "mask" : null,
+              "next_state" : "parse_my_tunnel"
+            },
+            {
+              "type" : "hexstr",
+              "value" : "0x0800",
+              "mask" : null,
+              "next_state" : "parse_ipv4"
+            },
+            {
+              "value" : "default",
+              "mask" : null,
+              "next_state" : null
+            }
+          ],
+          "transition_key" : [
+            {
+              "type" : "field",
+              "value" : ["ethernet", "ether_type"]
+            }
+          ]
+        },
+        {
+          "name" : "parse_my_tunnel",
+          "id" : 3,
+          "parser_ops" : [
+            {
+              "parameters" : [
+                {
+                  "type" : "regular",
+                  "value" : "my_tunnel"
+                }
+              ],
+              "op" : "extract"
+            }
+          ],
+          "transitions" : [
+            {
+              "type" : "hexstr",
+              "value" : "0x0800",
+              "mask" : null,
+              "next_state" : "parse_ipv4"
+            },
+            {
+              "value" : "default",
+              "mask" : null,
+              "next_state" : null
+            }
+          ],
+          "transition_key" : [
+            {
+              "type" : "field",
+              "value" : ["my_tunnel", "proto_id"]
+            }
+          ]
+        },
+        {
+          "name" : "parse_ipv4",
+          "id" : 4,
+          "parser_ops" : [
+            {
+              "parameters" : [
+                {
+                  "type" : "regular",
+                  "value" : "ipv4"
+                }
+              ],
+              "op" : "extract"
+            }
+          ],
+          "transitions" : [
+            {
+              "value" : "default",
+              "mask" : null,
+              "next_state" : null
+            }
+          ],
+          "transition_key" : []
+        }
+      ]
+    }
+  ],
+  "parse_vsets" : [],
+  "deparsers" : [
+    {
+      "name" : "deparser",
+      "id" : 0,
+      "source_info" : {
+        "filename" : "mytunnel.p4",
+        "line" : 286,
+        "column" : 8,
+        "source_fragment" : "c_deparser"
+      },
+      "order" : ["packet_in", "ethernet", "my_tunnel", "ipv4"]
+    }
+  ],
+  "meter_arrays" : [],
+  "counter_arrays" : [
+    {
+      "name" : "c_ingress.tx_port_counter",
+      "id" : 0,
+      "source_info" : {
+        "filename" : "mytunnel.p4",
+        "line" : 140,
+        "column" : 48,
+        "source_fragment" : "tx_port_counter"
+      },
+      "size" : 255,
+      "is_direct" : false
+    },
+    {
+      "name" : "c_ingress.rx_port_counter",
+      "id" : 1,
+      "source_info" : {
+        "filename" : "mytunnel.p4",
+        "line" : 141,
+        "column" : 48,
+        "source_fragment" : "rx_port_counter"
+      },
+      "size" : 255,
+      "is_direct" : false
+    },
+    {
+      "name" : "c_ingress.l2_fwd_counter",
+      "id" : 2,
+      "is_direct" : true,
+      "binding" : "c_ingress.t_l2_fwd"
+    }
+  ],
+  "register_arrays" : [],
+  "calculations" : [],
+  "learn_lists" : [],
+  "actions" : [
+    {
+      "name" : "NoAction",
+      "id" : 0,
+      "runtime_data" : [],
+      "primitives" : []
+    },
+    {
+      "name" : "c_ingress.send_to_cpu",
+      "id" : 1,
+      "runtime_data" : [],
+      "primitives" : [
+        {
+          "op" : "assign",
+          "parameters" : [
+            {
+              "type" : "field",
+              "value" : ["standard_metadata", "egress_spec"]
+            },
+            {
+              "type" : "hexstr",
+              "value" : "0x00ff"
+            }
+          ],
+          "source_info" : {
+            "filename" : "mytunnel.p4",
+            "line" : 26,
+            "column" : 24,
+            "source_fragment" : "255; ..."
+          }
+        },
+        {
+          "op" : "add_header",
+          "parameters" : [
+            {
+              "type" : "header",
+              "value" : "packet_in"
+            }
+          ],
+          "source_info" : {
+            "filename" : "mytunnel.p4",
+            "line" : 148,
+            "column" : 8,
+            "source_fragment" : "hdr.packet_in.setValid()"
+          }
+        },
+        {
+          "op" : "assign",
+          "parameters" : [
+            {
+              "type" : "field",
+              "value" : ["packet_in", "ingress_port"]
+            },
+            {
+              "type" : "field",
+              "value" : ["standard_metadata", "ingress_port"]
+            }
+          ],
+          "source_info" : {
+            "filename" : "mytunnel.p4",
+            "line" : 149,
+            "column" : 8,
+            "source_fragment" : "hdr.packet_in.ingress_port = standard_metadata.ingress_port"
+          }
+        }
+      ]
+    },
+    {
+      "name" : "c_ingress.set_out_port",
+      "id" : 2,
+      "runtime_data" : [
+        {
+          "name" : "port",
+          "bitwidth" : 9
+        }
+      ],
+      "primitives" : [
+        {
+          "op" : "assign",
+          "parameters" : [
+            {
+              "type" : "field",
+              "value" : ["standard_metadata", "egress_spec"]
+            },
+            {
+              "type" : "runtime_data",
+              "value" : 0
+            }
+          ],
+          "source_info" : {
+            "filename" : "mytunnel.p4",
+            "line" : 153,
+            "column" : 8,
+            "source_fragment" : "standard_metadata.egress_spec = port"
+          }
+        }
+      ]
+    },
+    {
+      "name" : "c_ingress.set_out_port",
+      "id" : 3,
+      "runtime_data" : [
+        {
+          "name" : "port",
+          "bitwidth" : 9
+        }
+      ],
+      "primitives" : [
+        {
+          "op" : "assign",
+          "parameters" : [
+            {
+              "type" : "field",
+              "value" : ["standard_metadata", "egress_spec"]
+            },
+            {
+              "type" : "runtime_data",
+              "value" : 0
+            }
+          ],
+          "source_info" : {
+            "filename" : "mytunnel.p4",
+            "line" : 153,
+            "column" : 8,
+            "source_fragment" : "standard_metadata.egress_spec = port"
+          }
+        }
+      ]
+    },
+    {
+      "name" : "c_ingress._drop",
+      "id" : 4,
+      "runtime_data" : [],
+      "primitives" : [
+        {
+          "op" : "drop",
+          "parameters" : [],
+          "source_info" : {
+            "filename" : "mytunnel.p4",
+            "line" : 157,
+            "column" : 8,
+            "source_fragment" : "mark_to_drop()"
+          }
+        }
+      ]
+    },
+    {
+      "name" : "c_ingress._drop",
+      "id" : 5,
+      "runtime_data" : [],
+      "primitives" : [
+        {
+          "op" : "drop",
+          "parameters" : [],
+          "source_info" : {
+            "filename" : "mytunnel.p4",
+            "line" : 157,
+            "column" : 8,
+            "source_fragment" : "mark_to_drop()"
+          }
+        }
+      ]
+    },
+    {
+      "name" : "c_ingress._drop",
+      "id" : 6,
+      "runtime_data" : [],
+      "primitives" : [
+        {
+          "op" : "drop",
+          "parameters" : [],
+          "source_info" : {
+            "filename" : "mytunnel.p4",
+            "line" : 157,
+            "column" : 8,
+            "source_fragment" : "mark_to_drop()"
+          }
+        }
+      ]
+    },
+    {
+      "name" : "c_ingress.my_tunnel_ingress",
+      "id" : 7,
+      "runtime_data" : [
+        {
+          "name" : "tun_id",
+          "bitwidth" : 32
+        }
+      ],
+      "primitives" : [
+        {
+          "op" : "add_header",
+          "parameters" : [
+            {
+              "type" : "header",
+              "value" : "my_tunnel"
+            }
+          ],
+          "source_info" : {
+            "filename" : "mytunnel.p4",
+            "line" : 161,
+            "column" : 8,
+            "source_fragment" : "hdr.my_tunnel.setValid()"
+          }
+        },
+        {
+          "op" : "assign",
+          "parameters" : [
+            {
+              "type" : "field",
+              "value" : ["my_tunnel", "tun_id"]
+            },
+            {
+              "type" : "runtime_data",
+              "value" : 0
+            }
+          ],
+          "source_info" : {
+            "filename" : "mytunnel.p4",
+            "line" : 162,
+            "column" : 8,
+            "source_fragment" : "hdr.my_tunnel.tun_id = tun_id"
+          }
+        },
+        {
+          "op" : "assign",
+          "parameters" : [
+            {
+              "type" : "field",
+              "value" : ["my_tunnel", "proto_id"]
+            },
+            {
+              "type" : "field",
+              "value" : ["ethernet", "ether_type"]
+            }
+          ],
+          "source_info" : {
+            "filename" : "mytunnel.p4",
+            "line" : 163,
+            "column" : 8,
+            "source_fragment" : "hdr.my_tunnel.proto_id = hdr.ethernet.ether_type"
+          }
+        },
+        {
+          "op" : "assign",
+          "parameters" : [
+            {
+              "type" : "field",
+              "value" : ["ethernet", "ether_type"]
+            },
+            {
+              "type" : "hexstr",
+              "value" : "0x1212"
+            }
+          ],
+          "source_info" : {
+            "filename" : "mytunnel.p4",
+            "line" : 22,
+            "column" : 34,
+            "source_fragment" : "0x1212; ..."
+          }
+        }
+      ]
+    },
+    {
+      "name" : "c_ingress.my_tunnel_egress",
+      "id" : 8,
+      "runtime_data" : [
+        {
+          "name" : "port",
+          "bitwidth" : 9
+        }
+      ],
+      "primitives" : [
+        {
+          "op" : "assign",
+          "parameters" : [
+            {
+              "type" : "field",
+              "value" : ["standard_metadata", "egress_spec"]
+            },
+            {
+              "type" : "runtime_data",
+              "value" : 0
+            }
+          ],
+          "source_info" : {
+            "filename" : "mytunnel.p4",
+            "line" : 168,
+            "column" : 8,
+            "source_fragment" : "standard_metadata.egress_spec = port"
+          }
+        },
+        {
+          "op" : "assign",
+          "parameters" : [
+            {
+              "type" : "field",
+              "value" : ["ethernet", "ether_type"]
+            },
+            {
+              "type" : "field",
+              "value" : ["my_tunnel", "proto_id"]
+            }
+          ],
+          "source_info" : {
+            "filename" : "mytunnel.p4",
+            "line" : 169,
+            "column" : 8,
+            "source_fragment" : "hdr.ethernet.ether_type = hdr.my_tunnel.proto_id"
+          }
+        },
+        {
+          "op" : "remove_header",
+          "parameters" : [
+            {
+              "type" : "header",
+              "value" : "my_tunnel"
+            }
+          ],
+          "source_info" : {
+            "filename" : "mytunnel.p4",
+            "line" : 170,
+            "column" : 8,
+            "source_fragment" : "hdr.my_tunnel.setInvalid()"
+          }
+        }
+      ]
+    },
+    {
+      "name" : "act",
+      "id" : 9,
+      "runtime_data" : [],
+      "primitives" : [
+        {
+          "op" : "assign",
+          "parameters" : [
+            {
+              "type" : "field",
+              "value" : ["standard_metadata", "egress_spec"]
+            },
+            {
+              "type" : "field",
+              "value" : ["packet_out", "egress_port"]
+            }
+          ],
+          "source_info" : {
+            "filename" : "mytunnel.p4",
+            "line" : 222,
+            "column" : 12,
+            "source_fragment" : "standard_metadata.egress_spec = hdr.packet_out.egress_port"
+          }
+        },
+        {
+          "op" : "remove_header",
+          "parameters" : [
+            {
+              "type" : "header",
+              "value" : "packet_out"
+            }
+          ],
+          "source_info" : {
+            "filename" : "mytunnel.p4",
+            "line" : 223,
+            "column" : 12,
+            "source_fragment" : "hdr.packet_out.setInvalid()"
+          }
+        }
+      ]
+    },
+    {
+      "name" : "act_0",
+      "id" : 10,
+      "runtime_data" : [],
+      "primitives" : [
+        {
+          "op" : "assign",
+          "parameters" : [
+            {
+              "type" : "field",
+              "value" : ["scalars", "tmp_0"]
+            },
+            {
+              "type" : "expression",
+              "value" : {
+                "type" : "expression",
+                "value" : {
+                  "op" : "b2d",
+                  "left" : null,
+                  "right" : {
+                    "type" : "bool",
+                    "value" : true
+                  }
+                }
+              }
+            }
+          ]
+        }
+      ]
+    },
+    {
+      "name" : "act_1",
+      "id" : 11,
+      "runtime_data" : [],
+      "primitives" : [
+        {
+          "op" : "assign",
+          "parameters" : [
+            {
+              "type" : "field",
+              "value" : ["scalars", "tmp_0"]
+            },
+            {
+              "type" : "expression",
+              "value" : {
+                "type" : "expression",
+                "value" : {
+                  "op" : "b2d",
+                  "left" : null,
+                  "right" : {
+                    "type" : "bool",
+                    "value" : false
+                  }
+                }
+              }
+            }
+          ]
+        }
+      ]
+    },
+    {
+      "name" : "act_2",
+      "id" : 12,
+      "runtime_data" : [],
+      "primitives" : [
+        {
+          "op" : "assign",
+          "parameters" : [
+            {
+              "type" : "field",
+              "value" : ["scalars", "hasReturned_0"]
+            },
+            {
+              "type" : "expression",
+              "value" : {
+                "type" : "expression",
+                "value" : {
+                  "op" : "b2d",
+                  "left" : null,
+                  "right" : {
+                    "type" : "bool",
+                    "value" : true
+                  }
+                }
+              }
+            }
+          ],
+          "source_info" : {
+            "filename" : "mytunnel.p4",
+            "line" : 230,
+            "column" : 16,
+            "source_fragment" : "return"
+          }
+        }
+      ]
+    },
+    {
+      "name" : "act_3",
+      "id" : 13,
+      "runtime_data" : [],
+      "primitives" : [
+        {
+          "op" : "assign",
+          "parameters" : [
+            {
+              "type" : "field",
+              "value" : ["scalars", "hasReturned_0"]
+            },
+            {
+              "type" : "expression",
+              "value" : {
+                "type" : "expression",
+                "value" : {
+                  "op" : "b2d",
+                  "left" : null,
+                  "right" : {
+                    "type" : "bool",
+                    "value" : false
+                  }
+                }
+              }
+            }
+          ]
+        }
+      ]
+    },
+    {
+      "name" : "act_4",
+      "id" : 14,
+      "runtime_data" : [],
+      "primitives" : [
+        {
+          "op" : "assign",
+          "parameters" : [
+            {
+              "type" : "field",
+              "value" : ["scalars", "tmp"]
+            },
+            {
+              "type" : "expression",
+              "value" : {
+                "type" : "expression",
+                "value" : {
+                  "op" : "&",
+                  "left" : {
+                    "type" : "field",
+                    "value" : ["standard_metadata", "egress_spec"]
+                  },
+                  "right" : {
+                    "type" : "hexstr",
+                    "value" : "0xffffffff"
+                  }
+                }
+              }
+            }
+          ]
+        },
+        {
+          "op" : "count",
+          "parameters" : [
+            {
+              "type" : "counter_array",
+              "value" : "c_ingress.tx_port_counter"
+            },
+            {
+              "type" : "field",
+              "value" : ["scalars", "tmp"]
+            }
+          ],
+          "source_info" : {
+            "filename" : "mytunnel.p4",
+            "line" : 246,
+            "column" : 12,
+            "source_fragment" : "tx_port_counter.count((bit<32>) standard_metadata.egress_spec)"
+          }
+        }
+      ]
+    },
+    {
+      "name" : "act_5",
+      "id" : 15,
+      "runtime_data" : [],
+      "primitives" : [
+        {
+          "op" : "assign",
+          "parameters" : [
+            {
+              "type" : "field",
+              "value" : ["scalars", "tmp_1"]
+            },
+            {
+              "type" : "expression",
+              "value" : {
+                "type" : "expression",
+                "value" : {
+                  "op" : "&",
+                  "left" : {
+                    "type" : "field",
+                    "value" : ["standard_metadata", "ingress_port"]
+                  },
+                  "right" : {
+                    "type" : "hexstr",
+                    "value" : "0xffffffff"
+                  }
+                }
+              }
+            }
+          ]
+        },
+        {
+          "op" : "count",
+          "parameters" : [
+            {
+              "type" : "counter_array",
+              "value" : "c_ingress.rx_port_counter"
+            },
+            {
+              "type" : "field",
+              "value" : ["scalars", "tmp_1"]
+            }
+          ],
+          "source_info" : {
+            "filename" : "mytunnel.p4",
+            "line" : 249,
+            "column" : 12,
+            "source_fragment" : "rx_port_counter.count((bit<32>) standard_metadata.ingress_port)"
+          }
+        }
+      ]
+    }
+  ],
+  "pipelines" : [
+    {
+      "name" : "ingress",
+      "id" : 0,
+      "source_info" : {
+        "filename" : "mytunnel.p4",
+        "line" : 134,
+        "column" : 8,
+        "source_fragment" : "c_ingress"
+      },
+      "init_table" : "tbl_act",
+      "tables" : [
+        {
+          "name" : "tbl_act",
+          "id" : 0,
+          "key" : [],
+          "match_type" : "exact",
+          "type" : "simple",
+          "max_size" : 1024,
+          "with_counters" : false,
+          "support_timeout" : false,
+          "direct_meters" : null,
+          "action_ids" : [13],
+          "actions" : ["act_3"],
+          "base_default_next" : "node_3",
+          "next_tables" : {
+            "act_3" : "node_3"
+          },
+          "default_entry" : {
+            "action_id" : 13,
+            "action_const" : true,
+            "action_data" : [],
+            "action_entry_const" : true
+          }
+        },
+        {
+          "name" : "tbl_act_0",
+          "id" : 1,
+          "key" : [],
+          "match_type" : "exact",
+          "type" : "simple",
+          "max_size" : 1024,
+          "with_counters" : false,
+          "support_timeout" : false,
+          "direct_meters" : null,
+          "action_ids" : [9],
+          "actions" : ["act"],
+          "base_default_next" : "node_15",
+          "next_tables" : {
+            "act" : "node_15"
+          },
+          "default_entry" : {
+            "action_id" : 9,
+            "action_const" : true,
+            "action_data" : [],
+            "action_entry_const" : true
+          }
+        },
+        {
+          "name" : "c_ingress.t_l2_fwd",
+          "id" : 2,
+          "source_info" : {
+            "filename" : "mytunnel.p4",
+            "line" : 175,
+            "column" : 10,
+            "source_fragment" : "t_l2_fwd"
+          },
+          "key" : [
+            {
+              "match_type" : "ternary",
+              "name" : "standard_metadata.ingress_port",
+              "target" : ["standard_metadata", "ingress_port"],
+              "mask" : null
+            },
+            {
+              "match_type" : "ternary",
+              "name" : "hdr.ethernet.dst_addr",
+              "target" : ["ethernet", "dst_addr"],
+              "mask" : null
+            },
+            {
+              "match_type" : "ternary",
+              "name" : "hdr.ethernet.src_addr",
+              "target" : ["ethernet", "src_addr"],
+              "mask" : null
+            },
+            {
+              "match_type" : "ternary",
+              "name" : "hdr.ethernet.ether_type",
+              "target" : ["ethernet", "ether_type"],
+              "mask" : null
+            }
+          ],
+          "match_type" : "ternary",
+          "type" : "simple",
+          "max_size" : 1024,
+          "with_counters" : true,
+          "support_timeout" : false,
+          "direct_meters" : null,
+          "action_ids" : [2, 1, 4, 0],
+          "actions" : ["c_ingress.set_out_port", "c_ingress.send_to_cpu", "c_ingress._drop", "NoAction"],
+          "base_default_next" : null,
+          "next_tables" : {
+            "__HIT__" : "tbl_act_1",
+            "__MISS__" : "tbl_act_2"
+          },
+          "default_entry" : {
+            "action_id" : 0,
+            "action_const" : false,
+            "action_data" : [],
+            "action_entry_const" : false
+          }
+        },
+        {
+          "name" : "tbl_act_1",
+          "id" : 3,
+          "key" : [],
+          "match_type" : "exact",
+          "type" : "simple",
+          "max_size" : 1024,
+          "with_counters" : false,
+          "support_timeout" : false,
+          "direct_meters" : null,
+          "action_ids" : [10],
+          "actions" : ["act_0"],
+          "base_default_next" : "node_8",
+          "next_tables" : {
+            "act_0" : "node_8"
+          },
+          "default_entry" : {
+            "action_id" : 10,
+            "action_const" : true,
+            "action_data" : [],
+            "action_entry_const" : true
+          }
+        },
+        {
+          "name" : "tbl_act_2",
+          "id" : 4,
+          "key" : [],
+          "match_type" : "exact",
+          "type" : "simple",
+          "max_size" : 1024,
+          "with_counters" : false,
+          "support_timeout" : false,
+          "direct_meters" : null,
+          "action_ids" : [11],
+          "actions" : ["act_1"],
+          "base_default_next" : "node_8",
+          "next_tables" : {
+            "act_1" : "node_8"
+          },
+          "default_entry" : {
+            "action_id" : 11,
+            "action_const" : true,
+            "action_data" : [],
+            "action_entry_const" : true
+          }
+        },
+        {
+          "name" : "tbl_act_3",
+          "id" : 5,
+          "key" : [],
+          "match_type" : "exact",
+          "type" : "simple",
+          "max_size" : 1024,
+          "with_counters" : false,
+          "support_timeout" : false,
+          "direct_meters" : null,
+          "action_ids" : [12],
+          "actions" : ["act_2"],
+          "base_default_next" : "node_10",
+          "next_tables" : {
+            "act_2" : "node_10"
+          },
+          "default_entry" : {
+            "action_id" : 12,
+            "action_const" : true,
+            "action_data" : [],
+            "action_entry_const" : true
+          }
+        },
+        {
+          "name" : "c_ingress.t_tunnel_ingress",
+          "id" : 6,
+          "source_info" : {
+            "filename" : "mytunnel.p4",
+            "line" : 192,
+            "column" : 10,
+            "source_fragment" : "t_tunnel_ingress"
+          },
+          "key" : [
+            {
+              "match_type" : "lpm",
+              "name" : "hdr.ipv4.dst_addr",
+              "target" : ["ipv4", "dst_addr"],
+              "mask" : null
+            }
+          ],
+          "match_type" : "lpm",
+          "type" : "simple",
+          "max_size" : 1024,
+          "with_counters" : false,
+          "support_timeout" : false,
+          "direct_meters" : null,
+          "action_ids" : [7, 5],
+          "actions" : ["c_ingress.my_tunnel_ingress", "c_ingress._drop"],
+          "base_default_next" : "node_13",
+          "next_tables" : {
+            "c_ingress.my_tunnel_ingress" : "node_13",
+            "c_ingress._drop" : "node_13"
+          },
+          "default_entry" : {
+            "action_id" : 5,
+            "action_const" : false,
+            "action_data" : [],
+            "action_entry_const" : false
+          }
+        },
+        {
+          "name" : "c_ingress.t_tunnel_fwd",
+          "id" : 7,
+          "source_info" : {
+            "filename" : "mytunnel.p4",
+            "line" : 203,
+            "column" : 10,
+            "source_fragment" : "t_tunnel_fwd"
+          },
+          "key" : [
+            {
+              "match_type" : "exact",
+              "name" : "hdr.my_tunnel.tun_id",
+              "target" : ["my_tunnel", "tun_id"],
+              "mask" : null
+            }
+          ],
+          "match_type" : "exact",
+          "type" : "simple",
+          "max_size" : 1024,
+          "with_counters" : false,
+          "support_timeout" : false,
+          "direct_meters" : null,
+          "action_ids" : [3, 8, 6],
+          "actions" : ["c_ingress.set_out_port", "c_ingress.my_tunnel_egress", "c_ingress._drop"],
+          "base_default_next" : "node_15",
+          "next_tables" : {
+            "c_ingress.set_out_port" : "node_15",
+            "c_ingress.my_tunnel_egress" : "node_15",
+            "c_ingress._drop" : "node_15"
+          },
+          "default_entry" : {
+            "action_id" : 6,
+            "action_const" : false,
+            "action_data" : [],
+            "action_entry_const" : false
+          }
+        },
+        {
+          "name" : "tbl_act_4",
+          "id" : 8,
+          "key" : [],
+          "match_type" : "exact",
+          "type" : "simple",
+          "max_size" : 1024,
+          "with_counters" : false,
+          "support_timeout" : false,
+          "direct_meters" : null,
+          "action_ids" : [14],
+          "actions" : ["act_4"],
+          "base_default_next" : "node_18",
+          "next_tables" : {
+            "act_4" : "node_18"
+          },
+          "default_entry" : {
+            "action_id" : 14,
+            "action_const" : true,
+            "action_data" : [],
+            "action_entry_const" : true
+          }
+        },
+        {
+          "name" : "tbl_act_5",
+          "id" : 9,
+          "key" : [],
+          "match_type" : "exact",
+          "type" : "simple",
+          "max_size" : 1024,
+          "with_counters" : false,
+          "support_timeout" : false,
+          "direct_meters" : null,
+          "action_ids" : [15],
+          "actions" : ["act_5"],
+          "base_default_next" : null,
+          "next_tables" : {
+            "act_5" : null
+          },
+          "default_entry" : {
+            "action_id" : 15,
+            "action_const" : true,
+            "action_data" : [],
+            "action_entry_const" : true
+          }
+        }
+      ],
+      "action_profiles" : [],
+      "conditionals" : [
+        {
+          "name" : "node_3",
+          "id" : 0,
+          "source_info" : {
+            "filename" : "mytunnel.p4",
+            "line" : 217,
+            "column" : 12,
+            "source_fragment" : "standard_metadata.ingress_port == CPU_PORT"
+          },
+          "expression" : {
+            "type" : "expression",
+            "value" : {
+              "op" : "==",
+              "left" : {
+                "type" : "field",
+                "value" : ["standard_metadata", "ingress_port"]
+              },
+              "right" : {
+                "type" : "hexstr",
+                "value" : "0x00ff"
+              }
+            }
+          },
+          "true_next" : "tbl_act_0",
+          "false_next" : "c_ingress.t_l2_fwd"
+        },
+        {
+          "name" : "node_8",
+          "id" : 1,
+          "expression" : {
+            "type" : "expression",
+            "value" : {
+              "op" : "d2b",
+              "left" : null,
+              "right" : {
+                "type" : "field",
+                "value" : ["scalars", "tmp_0"]
+              }
+            }
+          },
+          "true_next" : "tbl_act_3",
+          "false_next" : "node_10"
+        },
+        {
+          "name" : "node_10",
+          "id" : 2,
+          "expression" : {
+            "type" : "expression",
+            "value" : {
+              "op" : "not",
+              "left" : null,
+              "right" : {
+                "type" : "expression",
+                "value" : {
+                  "op" : "d2b",
+                  "left" : null,
+                  "right" : {
+                    "type" : "field",
+                    "value" : ["scalars", "hasReturned_0"]
+                  }
+                }
+              }
+            }
+          },
+          "true_next" : "node_11",
+          "false_next" : "node_15"
+        },
+        {
+          "name" : "node_11",
+          "id" : 3,
+          "source_info" : {
+            "filename" : "mytunnel.p4",
+            "line" : 233,
+            "column" : 16,
+            "source_fragment" : "hdr.ipv4.isValid() && !hdr.my_tunnel.isValid()"
+          },
+          "expression" : {
+            "type" : "expression",
+            "value" : {
+              "op" : "and",
+              "left" : {
+                "type" : "expression",
+                "value" : {
+                  "op" : "d2b",
+                  "left" : null,
+                  "right" : {
+                    "type" : "field",
+                    "value" : ["ipv4", "$valid$"]
+                  }
+                }
+              },
+              "right" : {
+                "type" : "expression",
+                "value" : {
+                  "op" : "not",
+                  "left" : null,
+                  "right" : {
+                    "type" : "expression",
+                    "value" : {
+                      "op" : "d2b",
+                      "left" : null,
+                      "right" : {
+                        "type" : "field",
+                        "value" : ["my_tunnel", "$valid$"]
+                      }
+                    }
+                  }
+                }
+              }
+            }
+          },
+          "true_next" : "c_ingress.t_tunnel_ingress",
+          "false_next" : "node_13"
+        },
+        {
+          "name" : "node_13",
+          "id" : 4,
+          "source_info" : {
+            "filename" : "mytunnel.p4",
+            "line" : 238,
+            "column" : 16,
+            "source_fragment" : "hdr.my_tunnel.isValid()"
+          },
+          "expression" : {
+            "type" : "expression",
+            "value" : {
+              "op" : "d2b",
+              "left" : null,
+              "right" : {
+                "type" : "field",
+                "value" : ["my_tunnel", "$valid$"]
+              }
+            }
+          },
+          "true_next" : "c_ingress.t_tunnel_fwd",
+          "false_next" : "node_15"
+        },
+        {
+          "name" : "node_15",
+          "id" : 5,
+          "expression" : {
+            "type" : "expression",
+            "value" : {
+              "op" : "not",
+              "left" : null,
+              "right" : {
+                "type" : "expression",
+                "value" : {
+                  "op" : "d2b",
+                  "left" : null,
+                  "right" : {
+                    "type" : "field",
+                    "value" : ["scalars", "hasReturned_0"]
+                  }
+                }
+              }
+            }
+          },
+          "false_next" : null,
+          "true_next" : "node_16"
+        },
+        {
+          "name" : "node_16",
+          "id" : 6,
+          "source_info" : {
+            "filename" : "mytunnel.p4",
+            "line" : 245,
+            "column" : 12,
+            "source_fragment" : "standard_metadata.egress_spec < 255"
+          },
+          "expression" : {
+            "type" : "expression",
+            "value" : {
+              "op" : "<",
+              "left" : {
+                "type" : "field",
+                "value" : ["standard_metadata", "egress_spec"]
+              },
+              "right" : {
+                "type" : "hexstr",
+                "value" : "0x00ff"
+              }
+            }
+          },
+          "true_next" : "tbl_act_4",
+          "false_next" : "node_18"
+        },
+        {
+          "name" : "node_18",
+          "id" : 7,
+          "source_info" : {
+            "filename" : "mytunnel.p4",
+            "line" : 248,
+            "column" : 12,
+            "source_fragment" : "standard_metadata.ingress_port < 255"
+          },
+          "expression" : {
+            "type" : "expression",
+            "value" : {
+              "op" : "<",
+              "left" : {
+                "type" : "field",
+                "value" : ["standard_metadata", "ingress_port"]
+              },
+              "right" : {
+                "type" : "hexstr",
+                "value" : "0x00ff"
+              }
+            }
+          },
+          "false_next" : null,
+          "true_next" : "tbl_act_5"
+        }
+      ]
+    },
+    {
+      "name" : "egress",
+      "id" : 1,
+      "source_info" : {
+        "filename" : "mytunnel.p4",
+        "line" : 258,
+        "column" : 8,
+        "source_fragment" : "c_egress"
+      },
+      "init_table" : null,
+      "tables" : [],
+      "action_profiles" : [],
+      "conditionals" : []
+    }
+  ],
+  "checksums" : [],
+  "force_arith" : [],
+  "extern_instances" : [],
+  "field_aliases" : [
+    [
+      "queueing_metadata.enq_timestamp",
+      ["standard_metadata", "enq_timestamp"]
+    ],
+    [
+      "queueing_metadata.enq_qdepth",
+      ["standard_metadata", "enq_qdepth"]
+    ],
+    [
+      "queueing_metadata.deq_timedelta",
+      ["standard_metadata", "deq_timedelta"]
+    ],
+    [
+      "queueing_metadata.deq_qdepth",
+      ["standard_metadata", "deq_qdepth"]
+    ],
+    [
+      "intrinsic_metadata.ingress_global_timestamp",
+      ["standard_metadata", "ingress_global_timestamp"]
+    ],
+    [
+      "intrinsic_metadata.egress_global_timestamp",
+      ["standard_metadata", "egress_global_timestamp"]
+    ],
+    [
+      "intrinsic_metadata.lf_field_list",
+      ["standard_metadata", "lf_field_list"]
+    ],
+    [
+      "intrinsic_metadata.mcast_grp",
+      ["standard_metadata", "mcast_grp"]
+    ],
+    [
+      "intrinsic_metadata.resubmit_flag",
+      ["standard_metadata", "resubmit_flag"]
+    ],
+    [
+      "intrinsic_metadata.egress_rid",
+      ["standard_metadata", "egress_rid"]
+    ],
+    [
+      "intrinsic_metadata.recirculate_flag",
+      ["standard_metadata", "recirculate_flag"]
+    ]
+  ]
+}
\ No newline at end of file
diff --git a/apps/p4-tutorial/pipeconf/src/main/resources/mytunnel.p4 b/apps/p4-tutorial/pipeconf/src/main/resources/mytunnel.p4
new file mode 100644
index 0000000..7d73489
--- /dev/null
+++ b/apps/p4-tutorial/pipeconf/src/main/resources/mytunnel.p4
@@ -0,0 +1,306 @@
+/*
+ * Copyright 2017-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <core.p4>
+#include <v1model.p4>
+
+#define MAX_PORTS 255
+
+const bit<16> ETH_TYPE_MYTUNNEL = 0x1212;
+const bit<16> ETH_TYPE_IPV4 = 0x800;
+
+typedef bit<9> port_t;
+const port_t CPU_PORT = 255;
+
+//------------------------------------------------------------------------------
+// HEADERS
+//------------------------------------------------------------------------------
+
+header ethernet_t {
+    bit<48> dst_addr;
+    bit<48> src_addr;
+    bit<16> ether_type;
+}
+
+header my_tunnel_t {
+    bit<16> proto_id;
+    bit<32> tun_id;
+}
+
+header ipv4_t {
+    bit<4>  version;
+    bit<4>  ihl;
+    bit<8>  diffserv;
+    bit<16> len;
+    bit<16> identification;
+    bit<3>  flags;
+    bit<13> frag_offset;
+    bit<8>  ttl;
+    bit<8>  protocol;
+    bit<16> hdr_checksum;
+    bit<32> src_addr;
+    bit<32> dst_addr;
+}
+
+// Packet-in header. Prepended to packets sent to the controller and used to
+// carry the original ingress port where the packet was received.
+@controller_header("packet_in")
+header packet_in_header_t {
+    bit<9> ingress_port;
+}
+
+// Packet-out header. Prepended to packets received by the controller and used
+// to tell the switch on which port this packet should be forwarded.
+@controller_header("packet_out")
+header packet_out_header_t {
+    bit<9> egress_port;
+}
+
+// For convenience we collect all headers under the same struct.
+struct headers_t {
+    ethernet_t ethernet;
+    my_tunnel_t my_tunnel;
+    ipv4_t ipv4;
+    packet_out_header_t packet_out;
+    packet_in_header_t packet_in;
+}
+
+// Metadata can be used to carry information from one table to another.
+struct metadata_t {
+    // Empty. We don't use it in this program.
+}
+
+//------------------------------------------------------------------------------
+// PARSER
+//------------------------------------------------------------------------------
+
+parser c_parser(packet_in packet,
+                  out headers_t hdr,
+                  inout metadata_t meta,
+                  inout standard_metadata_t standard_metadata) {
+
+    state start {
+        transition select(standard_metadata.ingress_port) {
+            CPU_PORT: parse_packet_out;
+            default: parse_ethernet;
+        }
+    }
+
+    state parse_packet_out {
+        packet.extract(hdr.packet_out);
+        transition parse_ethernet;
+    }
+
+    state parse_ethernet {
+        packet.extract(hdr.ethernet);
+        transition select(hdr.ethernet.ether_type) {
+            ETH_TYPE_MYTUNNEL: parse_my_tunnel;
+            ETH_TYPE_IPV4: parse_ipv4;
+            default: accept;
+        }
+    }
+
+    state parse_my_tunnel {
+        packet.extract(hdr.my_tunnel);
+        transition select(hdr.my_tunnel.proto_id) {
+            ETH_TYPE_IPV4: parse_ipv4;
+            default: accept;
+        }
+    }
+
+    state parse_ipv4 {
+        packet.extract(hdr.ipv4);
+        transition accept;
+    }
+}
+
+//------------------------------------------------------------------------------
+// INGRESS PIPELINE
+//------------------------------------------------------------------------------
+
+control c_ingress(inout headers_t hdr,
+                    inout metadata_t meta,
+                    inout standard_metadata_t standard_metadata) {
+
+    // We use these counters to count packets/bytes received/sent on each port.
+    // For each counter we instantiate a number of cells equal to MAX_PORTS.
+    counter(MAX_PORTS, CounterType.packets_and_bytes) tx_port_counter;
+    counter(MAX_PORTS, CounterType.packets_and_bytes) rx_port_counter;
+
+    action send_to_cpu() {
+        standard_metadata.egress_spec = CPU_PORT;
+        // Packets sent to the controller needs to be prepended with the
+        // packet-in header. By setting it valid we make sure it will be
+        // deparsed on the wire (see c_deparser).
+        hdr.packet_in.setValid();
+        hdr.packet_in.ingress_port = standard_metadata.ingress_port;
+    }
+
+    action set_out_port(port_t port) {
+        standard_metadata.egress_spec = port;
+    }
+
+    action _drop() {
+        mark_to_drop();
+    }
+
+    action my_tunnel_ingress(bit<32> tun_id) {
+        hdr.my_tunnel.setValid();
+        hdr.my_tunnel.tun_id = tun_id;
+        hdr.my_tunnel.proto_id = hdr.ethernet.ether_type;
+        hdr.ethernet.ether_type = ETH_TYPE_MYTUNNEL;
+    }
+
+    action my_tunnel_egress(bit<9> port) {
+        standard_metadata.egress_spec = port;
+        hdr.ethernet.ether_type = hdr.my_tunnel.proto_id;
+        hdr.my_tunnel.setInvalid();
+    }
+
+    direct_counter(CounterType.packets_and_bytes) l2_fwd_counter;
+
+    table t_l2_fwd {
+        key = {
+            standard_metadata.ingress_port  : ternary;
+            hdr.ethernet.dst_addr           : ternary;
+            hdr.ethernet.src_addr           : ternary;
+            hdr.ethernet.ether_type         : ternary;
+        }
+        actions = {
+            set_out_port();
+            send_to_cpu();
+            _drop();
+            NoAction;
+        }
+        default_action = NoAction();
+        counters = l2_fwd_counter;
+    }
+
+    table t_tunnel_ingress {
+        key = {
+            hdr.ipv4.dst_addr: lpm;
+        }
+        actions = {
+            my_tunnel_ingress;
+            _drop();
+        }
+        default_action = _drop();
+    }
+
+    table t_tunnel_fwd {
+        key = {
+            hdr.my_tunnel.tun_id: exact;
+        }
+        actions = {
+            set_out_port;
+            my_tunnel_egress;
+            _drop();
+        }
+        default_action = _drop();
+    }
+
+    // Define processing applied by this control block.
+    apply {
+        if (standard_metadata.ingress_port == CPU_PORT) {
+            // Packet received from CPU_PORT, this is a packet-out sent by the
+            // controller. Skip table processing, set the egress port as
+            // requested by the controller (packet_out header) and remove the
+            // packet_out header.
+            standard_metadata.egress_spec = hdr.packet_out.egress_port;
+            hdr.packet_out.setInvalid();
+        } else {
+            // Packet received from data plane port.
+            if (t_l2_fwd.apply().hit) {
+                // Packet hit an entry in t_l2_fwd table. A forwarding action
+                // has already been taken. No need to apply other tables, exit
+                // this control block.
+                return;
+            }
+
+            if (hdr.ipv4.isValid() && !hdr.my_tunnel.isValid()) {
+                // Process only non-tunneled IPv4 packets.
+                t_tunnel_ingress.apply();
+            }
+
+            if (hdr.my_tunnel.isValid()) {
+                // Process all tunneled packets.
+                t_tunnel_fwd.apply();
+            }
+        }
+
+        // Update port counters at index = ingress or egress port.
+        if (standard_metadata.egress_spec < MAX_PORTS) {
+            tx_port_counter.count((bit<32>) standard_metadata.egress_spec);
+        }
+        if (standard_metadata.ingress_port < MAX_PORTS) {
+            rx_port_counter.count((bit<32>) standard_metadata.ingress_port);
+        }
+     }
+}
+
+//------------------------------------------------------------------------------
+// EGRESS PIPELINE
+//------------------------------------------------------------------------------
+
+control c_egress(inout headers_t hdr,
+                 inout metadata_t meta,
+                 inout standard_metadata_t standard_metadata) {
+    apply {
+        // Nothing to do on the egress pipeline.
+    }
+}
+
+//------------------------------------------------------------------------------
+// CHECKSUM HANDLING
+//------------------------------------------------------------------------------
+
+control c_verify_checksum(inout headers_t hdr, inout metadata_t meta) {
+    apply {
+        // Nothing to do here, we assume checksum is always correct.
+    }
+}
+
+control c_compute_checksum(inout headers_t hdr, inout metadata_t meta) {
+    apply {
+        // No need to compute checksum as we do not modify packet headers.
+    }
+}
+
+//------------------------------------------------------------------------------
+// DEPARSER
+//------------------------------------------------------------------------------
+
+control c_deparser(packet_out packet, in headers_t hdr) {
+    apply {
+        // Emit headers on the wire in the following order.
+        // Only valid headers are emitted.
+        packet.emit(hdr.packet_in);
+        packet.emit(hdr.ethernet);
+        packet.emit(hdr.my_tunnel);
+        packet.emit(hdr.ipv4);
+    }
+}
+
+//------------------------------------------------------------------------------
+// SWITCH INSTANTIATION
+//------------------------------------------------------------------------------
+
+V1Switch(c_parser(),
+         c_verify_checksum(),
+         c_ingress(),
+         c_egress(),
+         c_compute_checksum(),
+         c_deparser()) main;
diff --git a/apps/p4-tutorial/pipeconf/src/main/resources/mytunnel.p4info b/apps/p4-tutorial/pipeconf/src/main/resources/mytunnel.p4info
new file mode 100644
index 0000000..e8685f1
--- /dev/null
+++ b/apps/p4-tutorial/pipeconf/src/main/resources/mytunnel.p4info
@@ -0,0 +1,202 @@
+tables {
+  preamble {
+    id: 33606914
+    name: "c_ingress.t_l2_fwd"
+    alias: "t_l2_fwd"
+  }
+  match_fields {
+    id: 1
+    name: "standard_metadata.ingress_port"
+    bitwidth: 9
+    match_type: TERNARY
+  }
+  match_fields {
+    id: 2
+    name: "hdr.ethernet.dst_addr"
+    bitwidth: 48
+    match_type: TERNARY
+  }
+  match_fields {
+    id: 3
+    name: "hdr.ethernet.src_addr"
+    bitwidth: 48
+    match_type: TERNARY
+  }
+  match_fields {
+    id: 4
+    name: "hdr.ethernet.ether_type"
+    bitwidth: 16
+    match_type: TERNARY
+  }
+  action_refs {
+    id: 16831479
+  }
+  action_refs {
+    id: 16822540
+  }
+  action_refs {
+    id: 16808599
+  }
+  action_refs {
+    id: 16800567
+  }
+  direct_resource_ids: 302001589
+  size: 1024
+}
+tables {
+  preamble {
+    id: 33565612
+    name: "c_ingress.t_tunnel_ingress"
+    alias: "t_tunnel_ingress"
+  }
+  match_fields {
+    id: 1
+    name: "hdr.ipv4.dst_addr"
+    bitwidth: 32
+    match_type: LPM
+  }
+  action_refs {
+    id: 16835665
+  }
+  action_refs {
+    id: 16808599
+  }
+  size: 1024
+}
+tables {
+  preamble {
+    id: 33556067
+    name: "c_ingress.t_tunnel_fwd"
+    alias: "t_tunnel_fwd"
+  }
+  match_fields {
+    id: 1
+    name: "hdr.my_tunnel.tun_id"
+    bitwidth: 32
+    match_type: EXACT
+  }
+  action_refs {
+    id: 16831479
+  }
+  action_refs {
+    id: 16800149
+  }
+  action_refs {
+    id: 16808599
+  }
+  size: 1024
+}
+actions {
+  preamble {
+    id: 16800567
+    name: "NoAction"
+    alias: "NoAction"
+  }
+}
+actions {
+  preamble {
+    id: 16822540
+    name: "c_ingress.send_to_cpu"
+    alias: "send_to_cpu"
+  }
+}
+actions {
+  preamble {
+    id: 16831479
+    name: "c_ingress.set_out_port"
+    alias: "set_out_port"
+  }
+  params {
+    id: 1
+    name: "port"
+    bitwidth: 9
+  }
+}
+actions {
+  preamble {
+    id: 16808599
+    name: "c_ingress._drop"
+    alias: "_drop"
+  }
+}
+actions {
+  preamble {
+    id: 16835665
+    name: "c_ingress.my_tunnel_ingress"
+    alias: "my_tunnel_ingress"
+  }
+  params {
+    id: 1
+    name: "tun_id"
+    bitwidth: 32
+  }
+}
+actions {
+  preamble {
+    id: 16800149
+    name: "c_ingress.my_tunnel_egress"
+    alias: "my_tunnel_egress"
+  }
+  params {
+    id: 1
+    name: "port"
+    bitwidth: 9
+  }
+}
+counters {
+  preamble {
+    id: 302003196
+    name: "c_ingress.tx_port_counter"
+    alias: "tx_port_counter"
+  }
+  spec {
+    unit: BOTH
+  }
+  size: 255
+}
+counters {
+  preamble {
+    id: 302045227
+    name: "c_ingress.rx_port_counter"
+    alias: "rx_port_counter"
+  }
+  spec {
+    unit: BOTH
+  }
+  size: 255
+}
+direct_counters {
+  preamble {
+    id: 302001589
+    name: "c_ingress.l2_fwd_counter"
+    alias: "l2_fwd_counter"
+  }
+  spec {
+    unit: BOTH
+  }
+  direct_table_id: 33606914
+}
+controller_packet_metadata {
+  preamble {
+    id: 2868941301
+    name: "packet_in"
+    annotations: "@controller_header(\"packet_in\")"
+  }
+  metadata {
+    id: 1
+    name: "ingress_port"
+    bitwidth: 9
+  }
+}
+controller_packet_metadata {
+  preamble {
+    id: 2868916615
+    name: "packet_out"
+    annotations: "@controller_header(\"packet_out\")"
+  }
+  metadata {
+    id: 1
+    name: "egress_port"
+    bitwidth: 9
+  }
+}
diff --git a/modules.defs b/modules.defs
index 5d422a2..13f485a 100644
--- a/modules.defs
+++ b/modules.defs
@@ -233,7 +233,7 @@
     '//incubator/protobuf/services/nb:onos-incubator-protobuf-services-nb-oar',
     '//apps/openstacknetworkingui:onos-apps-openstacknetworkingui-oar',
     '//apps/p4-tutorial/pipeconf:onos-apps-p4-tutorial-pipeconf-oar',
-    '//apps/p4-tutorial/icmpdropper:onos-apps-p4-tutorial-icmpdropper-oar',
+    '//apps/p4-tutorial/mytunnel:onos-apps-p4-tutorial-mytunnel-oar',
     '//apps/cfm:onos-apps-cfm-oar',
     '//apps/routeradvertisement:onos-apps-routeradvertisement-oar',
     '//apps/powermanagement:onos-apps-powermanagement-oar',