MyTunnel P4 tutorial app and pipeconf

Change-Id: I0549276fc7f6c8d0d244d6c52b1b9e85b9c3e13c
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;