ONOS-7000 P4 tutorial application and P4 program

Change-Id: Ia0a6befa6374a1950485c1fba0cfacb5ff4ce52c
diff --git a/apps/p4-tutorial/pipeconf/src/main/resources/main.p4 b/apps/p4-tutorial/pipeconf/src/main/resources/main.p4
new file mode 100644
index 0000000..9f0b809
--- /dev/null
+++ b/apps/p4-tutorial/pipeconf/src/main/resources/main.p4
@@ -0,0 +1,284 @@
+/*
+ * 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;