Improvement in fabric.p4 and bng.p4

- fabric.p4 now supports double tagged hosts
- bng.p4 now only manages PPPoE termination
- bng_ingress moved at the end of the fabric pipeline

Change-Id: Iff62238fde9ec6ddf7311312a98c041e3ab3aa8d
diff --git a/pipelines/fabric/src/main/resources/include/control/acl.p4 b/pipelines/fabric/src/main/resources/include/control/acl.p4
index bf70f15..4c2df67 100644
--- a/pipelines/fabric/src/main/resources/include/control/acl.p4
+++ b/pipelines/fabric/src/main/resources/include/control/acl.p4
@@ -66,7 +66,7 @@
             hdr.ethernet.dst_addr: ternary @name("eth_src"); // 48
             hdr.ethernet.src_addr: ternary @name("eth_dst"); // 48
             hdr.vlan_tag.vlan_id: ternary @name("vlan_id"); // 12
-            fabric_metadata.eth_type: ternary @name("eth_type"); //16
+            fabric_metadata.last_eth_type: ternary @name("eth_type"); //16
             hdr.ipv4.src_addr: ternary @name("ipv4_src"); // 32
             hdr.ipv4.dst_addr: ternary @name("ipv4_dst"); // 32
             hdr.icmp.icmp_type: ternary @name("icmp_type"); // 8
diff --git a/pipelines/fabric/src/main/resources/include/control/filtering.p4 b/pipelines/fabric/src/main/resources/include/control/filtering.p4
index cc8e313..a3213e9 100644
--- a/pipelines/fabric/src/main/resources/include/control/filtering.p4
+++ b/pipelines/fabric/src/main/resources/include/control/filtering.p4
@@ -45,14 +45,17 @@
 
     action permit_with_internal_vlan(vlan_id_t vlan_id) {
         fabric_metadata.vlan_id = vlan_id;
-        ingress_port_vlan_counter.count();
+        permit();
     }
 
+    // FIXME: remove the use of ternary match on valid inner VLAN.
+    // Use multi-table approach to remove ternary matching
     table ingress_port_vlan {
         key = {
-            standard_metadata.ingress_port: exact @name("ig_port");
-            hdr.vlan_tag.isValid(): exact @name("vlan_is_valid");
-            hdr.vlan_tag.vlan_id: ternary @name("vlan_id");
+            standard_metadata.ingress_port : exact @name("ig_port");
+            hdr.vlan_tag.isValid()         : exact @name("vlan_is_valid");
+            hdr.vlan_tag.vlan_id           : ternary @name("vlan_id");
+            hdr.inner_vlan_tag.vlan_id     : ternary @name("inner_vlan_id");
         }
         actions = {
             deny();
@@ -86,9 +89,11 @@
 
     table fwd_classifier {
         key = {
-            standard_metadata.ingress_port: exact @name("ig_port");
-            hdr.ethernet.dst_addr: ternary @name("eth_dst");
-            fabric_metadata.eth_type: exact @name("eth_type");
+            standard_metadata.ingress_port : exact @name("ig_port");
+            hdr.ethernet.dst_addr          : ternary @name("eth_dst");
+            fabric_metadata.is_ipv4        : exact @name("is_ipv4");
+            fabric_metadata.is_ipv6        : exact @name("is_ipv6");
+            fabric_metadata.is_mpls        : exact @name("is_mpls");
         }
         actions = {
             set_forwarding_type;
@@ -102,11 +107,17 @@
         // Initialize lookup metadata. Packets without a VLAN header will be
         // treated as belonging to a default VLAN ID (see parser).
         if (hdr.vlan_tag.isValid()) {
-            fabric_metadata.eth_type = hdr.vlan_tag.eth_type;
             fabric_metadata.vlan_id = hdr.vlan_tag.vlan_id;
             fabric_metadata.vlan_pri = hdr.vlan_tag.pri;
             fabric_metadata.vlan_cfi = hdr.vlan_tag.cfi;
         }
+        #ifdef WITH_DOUBLE_VLAN_TERMINATION
+        if (hdr.inner_vlan_tag.isValid()) {
+            fabric_metadata.inner_vlan_id = hdr.inner_vlan_tag.vlan_id;
+            fabric_metadata.inner_vlan_pri = hdr.inner_vlan_tag.pri;
+            fabric_metadata.inner_vlan_cfi = hdr.inner_vlan_tag.cfi;
+        }
+        #endif // WITH_DOUBLE_VLAN_TERMINATION
         if (!hdr.mpls.isValid()) {
             // Packets with a valid MPLS header will have
             // fabric_metadata.mpls_ttl set to the packet's MPLS ttl value (see
@@ -115,6 +126,22 @@
             fabric_metadata.mpls_ttl = DEFAULT_MPLS_TTL + 1;
         }
 
+        // Set last_eth_type checking the validity of the L2.5 headers
+        if (hdr.mpls.isValid()) {
+            fabric_metadata.last_eth_type = ETHERTYPE_MPLS;
+        } else {
+            if (hdr.vlan_tag.isValid()) {
+#if defined(WITH_XCONNECT) || defined(WITH_BNG) || defined(WITH_DOUBLE_VLAN_TERMINATION)
+                if(hdr.inner_vlan_tag.isValid()) {
+                    fabric_metadata.last_eth_type = hdr.inner_vlan_tag.eth_type;
+                } else
+#endif //  WITH_XCONNECT || WITH_BNG || WITH_DOUBLE_VLAN_TERMINATION
+                    fabric_metadata.last_eth_type = hdr.vlan_tag.eth_type;
+            } else {
+                fabric_metadata.last_eth_type = hdr.ethernet.eth_type;
+            }
+        }
+
         ingress_port_vlan.apply();
         fwd_classifier.apply();
     }
diff --git a/pipelines/fabric/src/main/resources/include/control/next.p4 b/pipelines/fabric/src/main/resources/include/control/next.p4
index 82b61eb..9868339 100644
--- a/pipelines/fabric/src/main/resources/include/control/next.p4
+++ b/pipelines/fabric/src/main/resources/include/control/next.p4
@@ -71,12 +71,23 @@
         next_vlan_counter.count();
     }
 
+#ifdef WITH_DOUBLE_VLAN_TERMINATION
+    action set_double_vlan(vlan_id_t outer_vlan_id, vlan_id_t inner_vlan_id) {
+        set_vlan(outer_vlan_id);
+        fabric_metadata.push_double_vlan = _TRUE;
+        fabric_metadata.inner_vlan_id = inner_vlan_id;
+    }
+#endif // WITH_DOUBLE_VLAN_TERMINATION
+
     table next_vlan {
         key = {
             fabric_metadata.next_id: exact @name("next_id");
         }
         actions = {
             set_vlan;
+#ifdef WITH_DOUBLE_VLAN_TERMINATION
+            set_double_vlan;
+#endif // WITH_DOUBLE_VLAN_TERMINATION
             @defaultonly nop;
         }
         const default_action = nop();
@@ -93,6 +104,7 @@
 
     action output_xconnect(port_num_t port_num) {
         output(port_num);
+        fabric_metadata.last_eth_type = ETHERTYPE_VLAN;
         xconnect_counter.count();
     }
 
@@ -251,7 +263,7 @@
     action pop_mpls_if_present() {
         hdr.mpls.setInvalid();
         // Assuming there's an IP header after the MPLS one.
-        fabric_metadata.eth_type = fabric_metadata.ip_eth_type;
+        fabric_metadata.last_eth_type = fabric_metadata.ip_eth_type;
     }
 
     @hidden
@@ -261,7 +273,7 @@
         hdr.mpls.tc = 3w0;
         hdr.mpls.bos = 1w1; // BOS = TRUE
         hdr.mpls.ttl = fabric_metadata.mpls_ttl; // Decrement after push.
-        fabric_metadata.eth_type = ETHERTYPE_MPLS;
+        fabric_metadata.last_eth_type = ETHERTYPE_MPLS;
     }
 
     @hidden
@@ -271,11 +283,25 @@
         hdr.vlan_tag.setValid();
         hdr.vlan_tag.cfi = fabric_metadata.vlan_cfi;
         hdr.vlan_tag.pri = fabric_metadata.vlan_pri;
-        hdr.vlan_tag.eth_type = fabric_metadata.eth_type;
+        hdr.vlan_tag.eth_type = fabric_metadata.last_eth_type;
         hdr.vlan_tag.vlan_id = fabric_metadata.vlan_id;
         hdr.ethernet.eth_type = ETHERTYPE_VLAN;
     }
 
+#ifdef WITH_DOUBLE_VLAN_TERMINATION
+    @hidden
+    action push_inner_vlan() {
+        // Then push inner VLAN TAG, rewriting correclty the outer vlan eth_type
+        // and the ethernet eth_type
+        hdr.inner_vlan_tag.setValid();
+        hdr.inner_vlan_tag.cfi = fabric_metadata.inner_vlan_cfi;
+        hdr.inner_vlan_tag.pri = fabric_metadata.inner_vlan_pri;
+        hdr.inner_vlan_tag.vlan_id = fabric_metadata.inner_vlan_id;
+        hdr.inner_vlan_tag.eth_type = fabric_metadata.last_eth_type;
+        hdr.vlan_tag.eth_type = ETHERTYPE_VLAN;
+    }
+#endif // WITH_DOUBLE_VLAN_TERMINATION
+
     /*
      * Egress VLAN Table.
      * Pops the VLAN tag if the pair egress port and VLAN ID is matched.
@@ -283,7 +309,7 @@
     direct_counter(CounterType.packets_and_bytes) egress_vlan_counter;
 
     action pop_vlan() {
-        hdr.ethernet.eth_type = fabric_metadata.eth_type;
+        hdr.ethernet.eth_type = fabric_metadata.last_eth_type;
         hdr.vlan_tag.setInvalid();
         egress_vlan_counter.count();
     }
@@ -314,12 +340,26 @@
             set_mpls();
         }
 
-        if (!egress_vlan.apply().hit) {
-            // Push VLAN tag if not the default one.
-            if (fabric_metadata.vlan_id != DEFAULT_VLAN_ID) {
-                push_vlan();
+#ifdef WITH_DOUBLE_VLAN_TERMINATION
+        if (fabric_metadata.push_double_vlan == _TRUE) {
+            // Double VLAN termination.
+            push_vlan();
+            push_inner_vlan();
+        } else {
+            // If no push double vlan, inner_vlan_tag must be popped
+            hdr.inner_vlan_tag.setInvalid();
+#endif // WITH_DOUBLE_VLAN_TERMINATION
+            // Port-based VLAN tagging (by default all
+            // ports are assumed tagged)
+            if (!egress_vlan.apply().hit) {
+                // Push VLAN tag if not the default one.
+                if (fabric_metadata.vlan_id != DEFAULT_VLAN_ID) {
+                    push_vlan();
+                }
             }
+#ifdef WITH_DOUBLE_VLAN_TERMINATION
         }
+#endif // WITH_DOUBLE_VLAN_TERMINATION
 
         // TTL decrement and check.
         if (hdr.mpls.isValid()) {