Fixed spgw.p4 decapping GTP packets not meant to be decapped

Also reduces the number of tables used for downlink processing.

Change-Id: I09a67cfac335b805d80e90cf5bb69fbab931e80b
diff --git a/pipelines/fabric/src/main/resources/include/checksum.p4 b/pipelines/fabric/src/main/resources/include/checksum.p4
index 4d1b803..a10a393 100644
--- a/pipelines/fabric/src/main/resources/include/checksum.p4
+++ b/pipelines/fabric/src/main/resources/include/checksum.p4
@@ -72,9 +72,6 @@
             hdr.ipv4.hdr_checksum,
             HashAlgorithm.csum16
         );
-#ifdef WITH_SPGW
-        verify_gtpu_checksum.apply(hdr.gtpu_ipv4);
-#endif // WITH_SPGW
     }
 }
 
diff --git a/pipelines/fabric/src/main/resources/include/define.p4 b/pipelines/fabric/src/main/resources/include/define.p4
index 74c964e..d717853 100644
--- a/pipelines/fabric/src/main/resources/include/define.p4
+++ b/pipelines/fabric/src/main/resources/include/define.p4
@@ -68,6 +68,21 @@
 typedef bit<48> mac_addr_t;
 typedef bit<16> group_id_t;
 typedef bit<12> vlan_id_t;
+typedef bit<48> timestamp_t;
+typedef bit<32> switch_id_t;
+typedef bit<32> ipv4_addr_t;
+typedef bit<16> l4_port_t;
+
+// SPGW types
+typedef bit<2> direction_t;
+typedef bit pcc_gate_status_t;
+typedef bit<32> sdf_rule_id_t;
+typedef bit<32> pcc_rule_id_t;
+
+// spgw.p4 expects uplink packets with IP dst on this subnet
+// 140.0.0.0/8
+const ipv4_addr_t S1U_SGW_PREFIX = 2348810240;
+#define S1U_SGW_PREFIX_LEN 8
 
 const bit<16> ETHERTYPE_QINQ = 0x88A8;
 const bit<16> ETHERTYPE_QINQ_NON_STD = 0x9100;
@@ -95,28 +110,17 @@
 const bit<8> DEFAULT_MPLS_TTL = 64;
 const bit<8> DEFAULT_IPV4_TTL = 64;
 
-typedef bit direction_t;
-typedef bit pcc_gate_status_t;
-typedef bit<32> sdf_rule_id_t;
-typedef bit<32> pcc_rule_id_t;
-
 const sdf_rule_id_t DEFAULT_SDF_RULE_ID = 0;
 const pcc_rule_id_t DEFAULT_PCC_RULE_ID = 0;
-
-const direction_t DIR_UPLINK = 1w0;
-const direction_t DIR_DOWNLINK = 1w1;
-
+const direction_t SPGW_DIR_UNKNOWN = 2w0;
+const direction_t SPGW_DIR_UPLINK = 2w1;
+const direction_t SPGW_DIR_DOWNLINK = 2w2;
 const pcc_gate_status_t PCC_GATE_OPEN = 1w0;
 const pcc_gate_status_t PCC_GATE_CLOSED = 1w1;
 
 /* indicate INT at LSB of DSCP */
 const bit<6> INT_DSCP = 0x1;
 
-typedef bit<48> timestamp_t;
-typedef bit<32> switch_id_t;
-typedef bit<32> ip_address_t;
-typedef bit<16> l4_port_t;
-
 const bit<8> INT_HEADER_LEN_WORD = 4;
 
 const bit<8> CPU_MIRROR_SESSION_ID = 250;
diff --git a/pipelines/fabric/src/main/resources/include/header.p4 b/pipelines/fabric/src/main/resources/include/header.p4
index c1df60e..25ac400 100644
--- a/pipelines/fabric/src/main/resources/include/header.p4
+++ b/pipelines/fabric/src/main/resources/include/header.p4
@@ -132,7 +132,6 @@
 }
 
 struct spgw_meta_t {
-    _BOOL             do_spgw;
     direction_t       direction;
     bit<16>           ipv4_len;
     bit<32>           teid;
@@ -291,6 +290,8 @@
     ipv4_t gtpu_ipv4;
     udp_t gtpu_udp;
     gtpu_t gtpu;
+    ipv4_t inner_ipv4;
+    udp_t inner_udp;
 #endif // WITH_SPGW
     ipv4_t ipv4;
 #ifdef WITH_IPV6
diff --git a/pipelines/fabric/src/main/resources/include/int_report.p4 b/pipelines/fabric/src/main/resources/include/int_report.p4
index da158e2..6d7c29d 100644
--- a/pipelines/fabric/src/main/resources/include/int_report.p4
+++ b/pipelines/fabric/src/main/resources/include/int_report.p4
@@ -44,8 +44,8 @@
         (bit<32>) standard_metadata.enq_timestamp;
     }
 
-    action do_report_encapsulation(mac_addr_t src_mac, mac_addr_t mon_mac, ip_address_t src_ip,
-                        ip_address_t mon_ip, l4_port_t mon_port) {
+    action do_report_encapsulation(mac_addr_t src_mac, mac_addr_t mon_mac, ipv4_addr_t src_ip,
+                        ipv4_addr_t mon_ip, l4_port_t mon_port) {
         //Report Ethernet Header
         hdr.report_ethernet.setValid();
         hdr.report_ethernet.dst_addr = mon_mac;
diff --git a/pipelines/fabric/src/main/resources/include/parser.p4 b/pipelines/fabric/src/main/resources/include/parser.p4
index b94ca50..20c310c 100644
--- a/pipelines/fabric/src/main/resources/include/parser.p4
+++ b/pipelines/fabric/src/main/resources/include/parser.p4
@@ -179,24 +179,35 @@
 
 #ifdef WITH_SPGW
     state parse_gtpu {
-        packet.extract(hdr.gtpu);
-        transition parse_ipv4_inner;
+        transition select(hdr.ipv4.dst_addr[31:32-S1U_SGW_PREFIX_LEN]) {
+            // Avoid parsing GTP and inner headers if we know this GTP packet
+            // is not to be processed by this switch.
+            // FIXME: use parser value sets when support is ready in ONOS.
+            // To set the S1U_SGW_PREFIX value at runtime.
+            S1U_SGW_PREFIX[31:32-S1U_SGW_PREFIX_LEN]: do_parse_gtpu;
+            default: accept;
+        }
     }
 
-    state parse_ipv4_inner {
-        packet.extract(hdr.gtpu_ipv4);
-        transition select(hdr.gtpu_ipv4.protocol) {
+    state do_parse_gtpu {
+        packet.extract(hdr.gtpu);
+        transition parse_inner_ipv4;
+    }
+
+    state parse_inner_ipv4 {
+        packet.extract(hdr.inner_ipv4);
+        transition select(hdr.inner_ipv4.protocol) {
             PROTO_TCP: parse_tcp;
-            PROTO_UDP: parse_udp_inner;
+            PROTO_UDP: parse_inner_udp;
             PROTO_ICMP: parse_icmp;
             default: accept;
         }
     }
 
-    state parse_udp_inner {
-        packet.extract(hdr.gtpu_udp);
-        fabric_metadata.l4_src_port = hdr.gtpu_udp.src_port;
-        fabric_metadata.l4_dst_port = hdr.gtpu_udp.dst_port;
+    state parse_inner_udp {
+        packet.extract(hdr.inner_udp);
+        fabric_metadata.l4_src_port = hdr.inner_udp.src_port;
+        fabric_metadata.l4_dst_port = hdr.inner_udp.dst_port;
 #ifdef WITH_INT
         transition select(hdr.ipv4.isValid() && (hdr.ipv4.dscp & INT_DSCP) == INT_DSCP) {
             true: parse_intl4_shim;
diff --git a/pipelines/fabric/src/main/resources/include/spgw.p4 b/pipelines/fabric/src/main/resources/include/spgw.p4
index b3ac361..5b2cd29 100644
--- a/pipelines/fabric/src/main/resources/include/spgw.p4
+++ b/pipelines/fabric/src/main/resources/include/spgw.p4
@@ -17,6 +17,27 @@
 #ifndef __SPGW__
 #define __SPGW__
 
+control spgw_normalizer(
+        in    bool   is_gtpu_encapped,
+        out   ipv4_t gtpu_ipv4,
+        out   udp_t  gtpu_udp,
+        inout ipv4_t ipv4,
+        inout udp_t  udp,
+        in    ipv4_t inner_ipv4,
+        in    udp_t  inner_udp
+    ) {
+    apply {
+        if (! is_gtpu_encapped) return;
+        gtpu_ipv4 = ipv4;
+        ipv4 = inner_ipv4;
+        gtpu_udp = udp;
+        if (inner_udp.isValid()) {
+            udp = inner_udp;
+        } else {
+            udp.setInvalid();
+        }
+    }
+}
 
 control spgw_ingress(
         inout ipv4_t      gtpu_ipv4,
@@ -41,26 +62,24 @@
         spgw_meta.teid = teid;
         spgw_meta.s1u_enb_addr = s1u_enb_addr;
         spgw_meta.s1u_sgw_addr = s1u_sgw_addr;
-    }
-
-    action update_ue_cdr() {
         ue_counter.count();
     }
 
-    table ue_filter_table {
+    table dl_sess_lookup {
         key = {
-            // IP prefixes of the UEs managed by this switch (when downlink)
-            ipv4.dst_addr : lpm;
+            // UE addr for downlink
+            ipv4.dst_addr : exact;
         }
         actions = {
-            NoAction();
+            set_dl_sess_info();
         }
+        counters = ue_counter;
     }
 
     table s1u_filter_table {
         key = {
-            // IP addresses of the S1U interfaces embodied by this switch.
-            spgw_meta.s1u_sgw_addr : exact;
+            // IP addresses of the S1U interfaces of this SPGW-U instance (when uplink)
+            gtpu_ipv4.dst_addr : exact;
         }
         actions = {
             NoAction();
@@ -116,56 +135,22 @@
     }
 #endif // WITH_SPGW_PCC_GATING
 
-    table dl_sess_lookup {
-        key = {
-            // UE addr for downlink
-            ipv4.dst_addr : exact;
-        }
-        actions = {
-            set_dl_sess_info();
-        }
-    }
-
-    table ue_cdr_table {
-        key = {
-            // UE addr for downlink
-            ipv4.dst_addr : exact;
-        }
-        actions = {
-            update_ue_cdr();
-        }
-        counters = ue_counter;
-    }
-
     apply {
-        spgw_meta.do_spgw = _FALSE;
         if (gtpu.isValid()) {
-            // If here, the parsed ipv4 header is the outer GTP one, but
-            // fabric needs to forward on the inner one, i.e. this.
-            // We store the outer values we need in the metadata, then replace
-            // the ipv4 header extracted before with this one.
-            spgw_meta.s1u_enb_addr = ipv4.src_addr;
-            spgw_meta.s1u_sgw_addr = ipv4.dst_addr;
-            ipv4 = gtpu_ipv4;
-            udp = gtpu_udp;
-
-            if (s1u_filter_table.apply().hit) {
-                // TODO: check also that gtpu.msgtype == GTP_GPDU
-                spgw_meta.do_spgw = _TRUE;
-                spgw_meta.direction = DIR_UPLINK;
+            // If here, pkt has outer IP dst on
+            // S1U_SGW_PREFIX/S1U_SGW_PREFIX_LEN subnet.
+            // TODO: check also that gtpu.msgtype == GTP_GPDU
+            if (!s1u_filter_table.apply().hit) {
+                drop_now();
             }
-        } else if (ue_filter_table.apply().hit) {
-            spgw_meta.do_spgw = _TRUE;
-            spgw_meta.direction = DIR_DOWNLINK;
-        }
-
-        if (spgw_meta.do_spgw == _FALSE) {
-            // Exit this control block.
-            return;
-        }
-
-        if (spgw_meta.direction == DIR_UPLINK) {
+            spgw_meta.direction = SPGW_DIR_UPLINK;
             gtpu_decap();
+        } else if (dl_sess_lookup.apply().hit) {
+            spgw_meta.direction = SPGW_DIR_DOWNLINK;
+        } else {
+            spgw_meta.direction = SPGW_DIR_UNKNOWN;
+            // No SPGW processing needed.
+            return;
         }
 
 #ifdef WITH_SPGW_PCC_GATING
@@ -181,15 +166,6 @@
         }
 #endif // WITH_SPGW_PCC_GATING
 
-        if (spgw_meta.direction == DIR_DOWNLINK) {
-            if (!dl_sess_lookup.apply().hit) {
-                // We have no other choice than drop, as we miss the session
-                // info necessary to properly GTPU encap the packet.
-                drop_now();
-            }
-            ue_cdr_table.apply();
-        }
-
         // Don't ask why... we'll need this later.
         spgw_meta.ipv4_len = ipv4.total_len;
     }
@@ -197,12 +173,12 @@
 
 
 control spgw_egress(
-        in ipv4_t               ipv4,
-        out ipv4_t              gtpu_ipv4,
-        out udp_t               gtpu_udp,
-        out gtpu_t              gtpu,
-        in  spgw_meta_t         spgw_meta,
-        in  standard_metadata_t std_meta
+        in    ipv4_t              ipv4,
+        inout ipv4_t              gtpu_ipv4,
+        inout udp_t               gtpu_udp,
+        inout gtpu_t              gtpu,
+        in    spgw_meta_t         spgw_meta,
+        in    standard_metadata_t std_meta
     ) {
 
     action gtpu_encap() {
@@ -211,7 +187,7 @@
         gtpu_ipv4.ihl = IPV4_MIN_IHL;
         gtpu_ipv4.dscp = 0;
         gtpu_ipv4.ecn = 0;
-        gtpu_ipv4.total_len = spgw_meta.ipv4_len
+        gtpu_ipv4.total_len = ipv4.total_len
                 + (IPV4_HDR_SIZE + UDP_HDR_SIZE + GTP_HDR_SIZE);
         gtpu_ipv4.identification = 0x1513; /* From NGIC */
         gtpu_ipv4.flags = 0;
@@ -242,44 +218,13 @@
     }
 
     apply {
-        if (spgw_meta.do_spgw == _TRUE && spgw_meta.direction == DIR_DOWNLINK) {
+        if (spgw_meta.direction == SPGW_DIR_DOWNLINK) {
             gtpu_encap();
         }
     }
 }
 
 
-control verify_gtpu_checksum(
-        inout ipv4_t gtpu_ipv4
-    ) {
-    apply {
-        // TODO: re-enable gtpu_ipv4 verification
-        // with the current parser logic, gtpu_ip4 contains values of
-        // the inner header, which is already verified by include/checksum.p4.
-        // We need to modify the parser to copy the outer header somewhere
-        // else, and verify that here.
-
-        // verify_checksum(gtpu_ipv4.isValid(),
-        //     {
-        //         gtpu_ipv4.version,
-        //         gtpu_ipv4.ihl,
-        //         gtpu_ipv4.diffserv,
-        //         gtpu_ipv4.total_len,
-        //         gtpu_ipv4.identification,
-        //         gtpu_ipv4.flags,
-        //         gtpu_ipv4.frag_offset,
-        //         gtpu_ipv4.ttl,
-        //         gtpu_ipv4.protocol,
-        //         gtpu_ipv4.src_addr,
-        //         gtpu_ipv4.dst_addr
-        //     },
-        //     gtpu_ipv4.hdr_checksum,
-        //     HashAlgorithm.csum16
-        // );
-    }
-}
-
-
 control update_gtpu_checksum(
         inout ipv4_t gtpu_ipv4,
         inout udp_t  gtpu_udp,