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/bng.p4 b/pipelines/fabric/src/main/resources/include/bng.p4
index 11e4639..ede0001 100644
--- a/pipelines/fabric/src/main/resources/include/bng.p4
+++ b/pipelines/fabric/src/main/resources/include/bng.p4
@@ -26,10 +26,6 @@
 #ifndef __BNG__
 #define __BNG__
 
-#define BNG_MAX_SUBSC 8192
-#define BNG_MAX_NET_PER_SUBSC 4
-#define BNG_MAX_SUBSC_NET BNG_MAX_NET_PER_SUBSC * BNG_MAX_SUBSC
-
 #define BNG_SUBSC_IPV6_NET_PREFIX_LEN 64
 
 control bng_ingress_upstream(
@@ -41,38 +37,11 @@
     counter(BNG_MAX_SUBSC, CounterType.packets) c_dropped;
     counter(BNG_MAX_SUBSC, CounterType.packets) c_control;
 
-    vlan_id_t s_tag = hdr.vlan_tag.vlan_id;
-    vlan_id_t c_tag = hdr.inner_vlan_tag.vlan_id;
-
-    _BOOL drop = _FALSE;
-    // TABLE: t_line_map
-    // Maps double VLAN tags to line ID. Line IDs are used to uniquelly identify
-    // a subscriber.
-
-    action set_line(bit<32> line_id) {
-        fmeta.bng.line_id = line_id;
-    }
-
-    table t_line_map {
-        actions = {
-            @defaultonly nop;
-            set_line;
-        }
-        key = {
-            s_tag: exact @name("s_tag");
-            c_tag: exact @name("c_tag");
-        }
-        size = BNG_MAX_SUBSC;
-        const default_action = nop;
-    }
-
     // TABLE: t_pppoe_cp
     // Punt to CPU for PPPeE control packets.
 
     action punt_to_cpu() {
         smeta.egress_spec = CPU_PORT;
-        fmeta.skip_forwarding = _TRUE;
-        fmeta.skip_next = _TRUE;
         c_control.count(fmeta.bng.line_id);
     }
 
@@ -95,26 +64,24 @@
 
     @hidden
     action term_enabled(bit<16> eth_type) {
-        hdr.ethernet.eth_type = eth_type;
-        fmeta.eth_type = eth_type;
+        hdr.inner_vlan_tag.eth_type = eth_type;
+        fmeta.last_eth_type = eth_type;
         hdr.pppoe.setInvalid();
-        hdr.vlan_tag.setInvalid();
-        hdr.inner_vlan_tag.setInvalid();
         c_terminated.count(fmeta.bng.line_id);
     }
 
     action term_disabled() {
         fmeta.bng.type = BNG_TYPE_INVALID;
-        fmeta.skip_forwarding = _TRUE;
-        fmeta.skip_next = _TRUE;
         mark_to_drop(smeta);
-        drop = _TRUE;
     }
 
     action term_enabled_v4() {
         term_enabled(ETHERTYPE_IPV4);
     }
 
+    // TODO: add match on hdr.ethernet.src_addr for antispoofing
+    // Take into account that MAC src address is modified by the Next control block
+    // when doing routing functionality.
     table t_pppoe_term_v4 {
         key = {
             fmeta.bng.line_id    : exact @name("line_id");
@@ -134,6 +101,9 @@
         term_enabled(ETHERTYPE_IPV6);
     }
 
+    // TODO: add match on hdr.ethernet.src_addr for antispoofing
+    // Match on unmodified metadata field, taking into account that MAC src address
+    // is modified by the Next control block when doing routing functionality.
     table t_pppoe_term_v6 {
         key = {
             fmeta.bng.line_id         : exact @name("line_id");
@@ -150,25 +120,23 @@
 #endif // WITH_IPV6
 
     apply {
-        // If table miss, line_id will be 0 (default metadata value).
-        t_line_map.apply();
-
-        if (t_pppoe_cp.apply().hit) {
+        if(t_pppoe_cp.apply().hit) {
             return;
         }
-
         if (hdr.ipv4.isValid()) {
-            t_pppoe_term_v4.apply();
-            if (drop == _TRUE) {
-                c_dropped.count(fmeta.bng.line_id);
+            switch(t_pppoe_term_v4.apply().action_run) {
+                term_disabled: {
+                    c_dropped.count(fmeta.bng.line_id);
+                }
             }
         }
 #ifdef WITH_IPV6
         else if (hdr.ipv6.isValid()) {
-            t_pppoe_term_v6.apply();
-            if (drop == _TRUE) {
-                c_dropped.count(fmeta.bng.line_id);
-             }
+            switch(t_pppoe_term_v6.apply().action_run) {
+               term_disabled: {
+                   c_dropped.count(fmeta.bng.line_id);
+               }
+           }
         }
 #endif // WITH_IPV6
     }
@@ -184,65 +152,37 @@
     meter(BNG_MAX_SUBSC, MeterType.bytes) m_besteff;
     meter(BNG_MAX_SUBSC, MeterType.bytes) m_prio;
 
-    // Downstream line map tables.
-    // Map IP dest address to line ID and next ID. Setting a next ID here
-    // allows to skip the fabric.p4 forwarding stage later.
-    _BOOL prio = _FALSE;
-
-    @hidden
-    action set_line(bit<32> line_id) {
+    action set_session(bit<16> pppoe_session_id) {
         fmeta.bng.type = BNG_TYPE_DOWNSTREAM;
-        fmeta.bng.line_id = line_id;
-        c_line_rx.count(line_id);
+        fmeta.bng.pppoe_session_id = pppoe_session_id;
+        c_line_rx.count(fmeta.bng.line_id);
     }
 
-    action set_line_next(bit<32> line_id, next_id_t next_id) {
-        set_line(line_id);
-        fmeta.next_id = next_id;
-        fmeta.skip_forwarding = _TRUE;
-    }
-
-    action set_line_drop(bit<32> line_id) {
-        set_line(line_id);
-        fmeta.skip_forwarding = _TRUE;
-        fmeta.skip_next = _TRUE;
+    action drop() {
+        fmeta.bng.type = BNG_TYPE_DOWNSTREAM;
+        c_line_rx.count(fmeta.bng.line_id);
         mark_to_drop(smeta);
     }
 
-    table t_line_map_v4 {
+    table t_line_session_map {
         key = {
-            hdr.ipv4.dst_addr: exact @name("ipv4_dst");
+            fmeta.bng.line_id : exact @name("line_id");
         }
         actions = {
             @defaultonly nop;
-            set_line_next;
-            set_line_drop;
+            set_session;
+            drop;
         }
-        size = BNG_MAX_SUBSC_NET;
+        size = BNG_MAX_SUBSC;
         const default_action = nop;
     }
 
-#ifdef WITH_IPV6
-    table t_line_map_v6 {
-        key = {
-            hdr.ipv6.dst_addr[127:64]: exact @name("ipv6_dst_net_id");
-        }
-        actions = {
-            @defaultonly nop;
-            set_line_next;
-            set_line_drop;
-        }
-        size = BNG_MAX_SUBSC_NET;
-        const default_action = nop;
-    }
-#endif // WITH_IPV6
-
     // Downstream QoS tables.
     // Provide coarse metering before prioritazion in the OLT. By default
     // everything is tagged and metered as best-effort traffic.
 
     action qos_prio() {
-        prio = _TRUE;
+        // no-op
     }
 
     action qos_besteff() {
@@ -281,38 +221,39 @@
 #endif // WITH_IPV6
 
     apply {
+        // We are not sure the pkt is a BNG downstream one, first we need to
+        // verify the line_id matches the one of a subscriber...
+
         // IPv4
-        if (hdr.ipv4.isValid()) {
-            if (t_line_map_v4.apply().hit) {
-                // Apply QoS only to subscriber traffic. This makes sense only
-                // if the downstream ports are used to receive IP traffic NOT
-                // destined to subscribers, e.g. to services in the compute
-                // nodes.
-                t_qos_v4.apply();
-                if (prio == _TRUE) {
-                    m_prio.execute_meter((bit<32>)fmeta.bng.line_id,
-                                          fmeta.bng.ds_meter_result);
-                } else {
-                    m_besteff.execute_meter((bit<32>)fmeta.bng.line_id,
-                                            fmeta.bng.ds_meter_result);
+        if (t_line_session_map.apply().hit) {
+            // Apply QoS only to subscriber traffic. This makes sense only
+            // if the downstream ports are used to receive IP traffic NOT
+            // destined to subscribers, e.g. to services in the compute
+            // nodes.
+            if (hdr.ipv4.isValid()) {
+                switch (t_qos_v4.apply().action_run) {
+                    qos_prio: {
+                        m_prio.execute_meter(fmeta.bng.line_id, fmeta.bng.ds_meter_result);
+                    }
+                    qos_besteff: {
+                        m_besteff.execute_meter(fmeta.bng.line_id, fmeta.bng.ds_meter_result);
+                    }
                 }
             }
-        }
 #ifdef WITH_IPV6
-        // IPv6
-        else if (hdr.ipv6.isValid()) {
-            if (t_line_map_v6.apply().hit) {
-                t_qos_v6.apply();
-                if (prio == _TRUE) {
-                    m_prio.execute_meter((bit<32>)fmeta.bng.line_id,
-                                          fmeta.bng.ds_meter_result);
-                } else {
-                    m_besteff.execute_meter((bit<32>)fmeta.bng.line_id,
-                                            fmeta.bng.ds_meter_result);
+            // IPv6
+            else if (hdr.ipv6.isValid()) {
+                switch (t_qos_v6.apply().action_run) {
+                    qos_prio: {
+                        m_prio.execute_meter(fmeta.bng.line_id, fmeta.bng.ds_meter_result);
+                    }
+                    qos_besteff: {
+                        m_besteff.execute_meter(fmeta.bng.line_id, fmeta.bng.ds_meter_result);
+                    }
                 }
             }
-        }
 #endif // WITH_IPV6
+        }
     }
 }
 
@@ -324,52 +265,41 @@
     counter(BNG_MAX_SUBSC, CounterType.packets_and_bytes) c_line_tx;
 
     @hidden
-    action encap(vlan_id_t c_tag, bit<16> pppoe_session_id) {
-        // s_tag (outer VLAN) should be already set via the next_vlan table.
-        // Here we add c_tag (inner VLAN) and PPPoE.
-        hdr.vlan_tag.eth_type = ETHERTYPE_VLAN;
-        hdr.inner_vlan_tag.setValid();
-        hdr.inner_vlan_tag.vlan_id = c_tag;
+    action encap() {
+        // Here we add PPPoE and modify the inner_vlan_tag Ethernet Type.
         hdr.inner_vlan_tag.eth_type = ETHERTYPE_PPPOES;
         hdr.pppoe.setValid();
         hdr.pppoe.version = 4w1;
         hdr.pppoe.type_id = 4w1;
         hdr.pppoe.code = 8w0; // 0 means session stage.
-        hdr.pppoe.session_id = pppoe_session_id;
+        hdr.pppoe.session_id = fmeta.bng.pppoe_session_id;
         c_line_tx.count(fmeta.bng.line_id);
     }
 
-    action encap_v4(vlan_id_t c_tag, bit<16> pppoe_session_id) {
-        encap(c_tag, pppoe_session_id);
+    action encap_v4() {
+        encap();
         hdr.pppoe.length = hdr.ipv4.total_len + 16w2;
         hdr.pppoe.protocol = PPPOE_PROTOCOL_IP4;
     }
 
 #ifdef WITH_IPV6
-    action encap_v6(vlan_id_t c_tag, bit<16> pppoe_session_id) {
-        encap(c_tag, pppoe_session_id);
+    action encap_v6() {
+        encap();
         hdr.pppoe.length = hdr.ipv6.payload_len + 16w42;
         hdr.pppoe.protocol = PPPOE_PROTOCOL_IP6;
     }
 #endif // WITH_IPV6
 
-    table t_session_encap {
-        key = {
-            fmeta.bng.line_id : exact @name("line_id");
-        }
-        actions = {
-            @defaultonly nop;
-            encap_v4;
-#ifdef WITH_IPV6
-            encap_v6;
-#endif // WITH_IPV6
-        }
-        size = BNG_MAX_SUBSC;
-        const default_action = nop();
-    }
-
     apply {
-        t_session_encap.apply();
+        if (hdr.ipv4.isValid()) {
+            encap_v4();
+        }
+#ifdef WITH_IPV6
+        // IPv6
+        else if (hdr.ipv6.isValid()) {
+            encap_v6();
+        }
+#endif // WITH_IPV6
     }
 }
 
@@ -378,20 +308,54 @@
         inout fabric_metadata_t fmeta,
         inout standard_metadata_t smeta) {
 
-    bng_ingress_upstream() upstream;
-    bng_ingress_downstream() downstream;
+        bng_ingress_upstream() upstream;
+        bng_ingress_downstream() downstream;
 
-    apply {
-        if (hdr.pppoe.isValid()) {
-            fmeta.bng.type = BNG_TYPE_UPSTREAM;
-            upstream.apply(hdr, fmeta, smeta);
+        vlan_id_t s_tag = 0;
+        vlan_id_t c_tag = 0;
+
+        // TABLE: t_line_map
+        // Map s_tag and c_tag to a line ID to uniquely identify a subscriber
+
+        action set_line(bit<32> line_id) {
+            fmeta.bng.line_id = line_id;
         }
-        else {
-            // We are not sure the pkt is a BNG downstream one, first we need to
-            // verify the IP dst matches the IP addr of a subscriber...
-            downstream.apply(hdr, fmeta, smeta);
+
+        table t_line_map {
+            key = {
+                s_tag : exact @name("s_tag");
+                c_tag : exact @name("c_tag");
+            }
+             actions = {
+                @defaultonly nop;
+                set_line;
+            }
+            size = BNG_MAX_SUBSC;
+            const default_action = nop;
         }
-    }
+
+        apply {
+            if(hdr.pppoe.isValid()) {
+                s_tag = hdr.vlan_tag.vlan_id;
+                c_tag = hdr.inner_vlan_tag.vlan_id;
+            } else {
+                // We expect the packet to be downstream,
+                // the tags are set by the next stage in the metadata.
+                s_tag = fmeta.vlan_id;
+                c_tag = fmeta.inner_vlan_id;
+            }
+
+            // First map the double VLAN tags to a line ID
+            // If table miss line ID will be 0.
+            t_line_map.apply();
+
+            if (hdr.pppoe.isValid()) {
+                fmeta.bng.type = BNG_TYPE_UPSTREAM;
+                upstream.apply(hdr, fmeta, smeta);
+            } else {
+                downstream.apply(hdr, fmeta, smeta);
+            }
+        }
 }
 
 control bng_egress(