[SDFAB-357] Backport slicing in fabric and add support for QFI in PDR and fabric
Change-Id: Ieb10140dc0029a0cbf59ddfbb77f64f9a8c7379e
(cherry picked from commit 411f6f7f461db6491d627c2cb31642bdd6e7c8d8)
diff --git a/pipelines/fabric/impl/src/main/resources/include/control/acl.p4 b/pipelines/fabric/impl/src/main/resources/include/control/acl.p4
index ae86e3b..9d7c7ba 100644
--- a/pipelines/fabric/impl/src/main/resources/include/control/acl.p4
+++ b/pipelines/fabric/impl/src/main/resources/include/control/acl.p4
@@ -21,29 +21,22 @@
#include "../header.p4"
control Acl (inout parsed_headers_t hdr,
- inout fabric_metadata_t fabric_metadata,
+ inout fabric_metadata_t fabric_md,
inout standard_metadata_t standard_metadata) {
-
- ipv4_addr_t ipv4_src = 0;
- ipv4_addr_t ipv4_dst = 0;
- bit<8> ip_proto = 0;
- l4_port_t l4_sport = 0;
- l4_port_t l4_dport = 0;
-
/*
* ACL Table.
*/
direct_counter(CounterType.packets_and_bytes) acl_counter;
action set_next_id_acl(next_id_t next_id) {
- fabric_metadata.next_id = next_id;
+ fabric_md.next_id = next_id;
acl_counter.count();
}
// Send immendiatelly to CPU - skip the rest of ingress.
action punt_to_cpu() {
standard_metadata.egress_spec = CPU_PORT;
- fabric_metadata.skip_next = _TRUE;
+ fabric_md.skip_next = _TRUE;
acl_counter.count();
}
@@ -55,7 +48,7 @@
action drop() {
mark_to_drop(standard_metadata);
- fabric_metadata.skip_next = _TRUE;
+ fabric_md.skip_next = _TRUE;
acl_counter.count();
}
@@ -70,14 +63,14 @@
hdr.ethernet.src_addr : ternary @name("eth_src"); // 48
hdr.vlan_tag.vlan_id : ternary @name("vlan_id"); // 12
hdr.eth_type.value : ternary @name("eth_type"); // 16
- ipv4_src : ternary @name("ipv4_src"); // 32
- ipv4_dst : ternary @name("ipv4_dst"); // 32
- ip_proto : ternary @name("ip_proto"); // 8
+ fabric_md.lkp.ipv4_src : ternary @name("ipv4_src"); // 32
+ fabric_md.lkp.ipv4_dst : ternary @name("ipv4_dst"); // 32
+ fabric_md.lkp.ip_proto : ternary @name("ip_proto"); // 8
hdr.icmp.icmp_type : ternary @name("icmp_type"); // 8
hdr.icmp.icmp_code : ternary @name("icmp_code"); // 8
- l4_sport : ternary @name("l4_sport"); // 16
- l4_dport : ternary @name("l4_dport"); // 16
- fabric_metadata.port_type : ternary @name("port_type"); // 2
+ fabric_md.lkp.l4_sport : ternary @name("l4_sport"); // 16
+ fabric_md.lkp.l4_dport : ternary @name("l4_dport"); // 16
+ fabric_md.port_type : ternary @name("port_type"); // 2
}
actions = {
@@ -94,29 +87,6 @@
}
apply {
- if (hdr.gtpu.isValid() && hdr.inner_ipv4.isValid()) {
- ipv4_src = hdr.inner_ipv4.src_addr;
- ipv4_dst = hdr.inner_ipv4.dst_addr;
- ip_proto = hdr.inner_ipv4.protocol;
- if (hdr.inner_tcp.isValid()) {
- l4_sport = hdr.inner_tcp.sport;
- l4_dport = hdr.inner_tcp.dport;
- } else if (hdr.inner_udp.isValid()) {
- l4_sport = hdr.inner_udp.sport;
- l4_dport = hdr.inner_udp.dport;
- }
- } else if (hdr.ipv4.isValid()) {
- ipv4_src = hdr.ipv4.src_addr;
- ipv4_dst = hdr.ipv4.dst_addr;
- ip_proto = hdr.ipv4.protocol;
- if (hdr.tcp.isValid()) {
- l4_sport = hdr.tcp.sport;
- l4_dport = hdr.tcp.dport;
- } else if (hdr.udp.isValid()) {
- l4_sport = hdr.udp.sport;
- l4_dport = hdr.udp.dport;
- }
- }
acl.apply();
}
}
diff --git a/pipelines/fabric/impl/src/main/resources/include/control/lookup_md_init.p4 b/pipelines/fabric/impl/src/main/resources/include/control/lookup_md_init.p4
new file mode 100644
index 0000000..c672bc9
--- /dev/null
+++ b/pipelines/fabric/impl/src/main/resources/include/control/lookup_md_init.p4
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2021-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.
+ */
+
+#ifndef __LOOKUP__
+#define __LOOKUP__
+
+control LookupMdInit (in parsed_headers_t hdr,
+ out lookup_metadata_t lkp_md) {
+ apply {
+ lkp_md.is_ipv4 = _FALSE;
+ lkp_md.ipv4_src = 0;
+ lkp_md.ipv4_dst = 0;
+ lkp_md.ip_proto = 0;
+ lkp_md.l4_sport = 0;
+ lkp_md.l4_dport = 0;
+ lkp_md.icmp_type = 0;
+ lkp_md.icmp_code = 0;
+ if (hdr.inner_ipv4.isValid()) {
+ lkp_md.is_ipv4 = true;
+ lkp_md.ipv4_src = hdr.inner_ipv4.src_addr;
+ lkp_md.ipv4_dst = hdr.inner_ipv4.dst_addr;
+ lkp_md.ip_proto = hdr.inner_ipv4.protocol;
+ if (hdr.inner_tcp.isValid()) {
+ lkp_md.l4_sport = hdr.inner_tcp.sport;
+ lkp_md.l4_dport = hdr.inner_tcp.dport;
+ } else if (hdr.inner_udp.isValid()) {
+ lkp_md.l4_sport = hdr.inner_udp.sport;
+ lkp_md.l4_dport = hdr.inner_udp.dport;
+ } else if (hdr.inner_icmp.isValid()) {
+ lkp_md.icmp_type = hdr.inner_icmp.icmp_type;
+ lkp_md.icmp_code = hdr.inner_icmp.icmp_code;
+ }
+ } else if (hdr.ipv4.isValid()) {
+ lkp_md.is_ipv4 = true;
+ lkp_md.ipv4_src = hdr.ipv4.src_addr;
+ lkp_md.ipv4_dst = hdr.ipv4.dst_addr;
+ lkp_md.ip_proto = hdr.ipv4.protocol;
+ if (hdr.tcp.isValid()) {
+ lkp_md.l4_sport = hdr.tcp.sport;
+ lkp_md.l4_dport = hdr.tcp.dport;
+ } else if (hdr.udp.isValid()) {
+ lkp_md.l4_sport = hdr.udp.sport;
+ lkp_md.l4_dport = hdr.udp.dport;
+ } else if (hdr.icmp.isValid()) {
+ lkp_md.icmp_type = hdr.icmp.icmp_type;
+ lkp_md.icmp_code = hdr.icmp.icmp_code;
+ }
+ }
+ }
+}
+
+#endif
+
diff --git a/pipelines/fabric/impl/src/main/resources/include/control/slicing.p4 b/pipelines/fabric/impl/src/main/resources/include/control/slicing.p4
new file mode 100644
index 0000000..87d887a
--- /dev/null
+++ b/pipelines/fabric/impl/src/main/resources/include/control/slicing.p4
@@ -0,0 +1,165 @@
+/*
+ * Copyright 2021-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.
+ */
+
+#ifndef __SLICING__
+#define __SLICING__
+
+// ACL-like classification, maps lookup metadata to slice_id and tc. For UE
+// traffic, values can be overriden later by the SPGW PDR tables.
+// To apply the same slicing and QoS treatment end-to-end, we use the IPv4 DSCP
+// field to piggyback slice_id and tc (see EgressDscpRewriter). This is
+// especially important for UE traffic, where classification based on PDRs can
+// only happen at the ingress leaf switch (implementing the UPF function).
+// As such, for traffic coming from selected ports, we allow trusting the
+// slice_id and tc values carried in the dscp.
+control IngressSliceTcClassifier (in parsed_headers_t hdr,
+ inout fabric_metadata_t fabric_md,
+ in standard_metadata_t standard_metadata) {
+
+ direct_counter(CounterType.packets) classifier_stats;
+
+ action set_slice_id_tc(slice_id_t slice_id, tc_t tc) {
+ fabric_md.slice_id = slice_id;
+ fabric_md.tc = tc;
+ classifier_stats.count();
+ }
+
+ // Should be used only for infrastructure ports (leaf-leaf, or leaf-spine),
+ // or ports facing servers that implement early classification based on the
+ // SDFAB DSCP encoding (slice_id++tc).
+ action trust_dscp() {
+ fabric_md.slice_id = hdr.ipv4.dscp[SLICE_ID_WIDTH+TC_WIDTH-1:TC_WIDTH];
+ fabric_md.tc = hdr.ipv4.dscp[TC_WIDTH-1:0];
+ classifier_stats.count();
+ }
+
+ table classifier {
+ key = {
+ standard_metadata.ingress_port : ternary @name("ig_port");
+ fabric_md.lkp.ipv4_src : ternary @name("ipv4_src");
+ fabric_md.lkp.ipv4_dst : ternary @name("ipv4_dst");
+ fabric_md.lkp.ip_proto : ternary @name("ip_proto");
+ fabric_md.lkp.l4_sport : ternary @name("l4_sport");
+ fabric_md.lkp.l4_dport : ternary @name("l4_dport");
+ }
+ actions = {
+ set_slice_id_tc;
+ trust_dscp;
+ }
+ const default_action = set_slice_id_tc(DEFAULT_SLICE_ID, DEFAULT_TC);
+ counters = classifier_stats;
+ size = QOS_CLASSIFIER_TABLE_SIZE;
+ }
+
+ apply {
+ classifier.apply();
+ }
+}
+
+// Provides metering and mapping to queues based on slice_id and tc. Should be
+// applied after any other block writing slice_id and tc.
+control IngressQos (inout fabric_metadata_t fabric_md,
+ inout standard_metadata_t standard_metadata) {
+
+ // One meter per tc per slice. The index should be slice_id++tc.
+ meter(1 << SLICE_TC_WIDTH, MeterType.bytes) slice_tc_meter;
+
+ direct_counter(CounterType.packets) queues_stats;
+
+ action set_queue(qid_t qid) {
+ // We can't set the queue id in bmv2.
+ queues_stats.count();
+ }
+
+ // For policing.
+ action meter_drop() {
+ mark_to_drop(standard_metadata);
+ queues_stats.count();
+ }
+
+ table queues {
+ key = {
+ fabric_md.slice_id: exact @name("slice_id");
+ fabric_md.tc: exact @name("tc");
+ fabric_md.packet_color: ternary @name("color"); // 0=GREEN, 1=YELLOW, 2=RED
+ }
+ actions = {
+ set_queue;
+ meter_drop;
+ }
+ const default_action = set_queue(0); // 0 = Best Effort
+ counters = queues_stats;
+ // Two times the number of tcs for all slices, because we might need to
+ // match on different colors for the same slice and tc.
+ size = 1 << (SLICE_TC_WIDTH + 1);
+ }
+
+ slice_tc_t slice_tc = fabric_md.slice_id++fabric_md.tc;
+
+ apply {
+ // Meter index should be 0 for all packets with default slice_id and tc.
+ slice_tc_meter.execute_meter((bit<32>) slice_tc, fabric_md.packet_color);
+ fabric_md.dscp = slice_tc;
+ queues.apply();
+ }
+}
+
+// Allows per-egress port rewriting of the outermost IPv4 DSCP field to
+// piggyback slice_id and tc across the fabric.
+control EgressDscpRewriter (inout parsed_headers_t hdr,
+ in fabric_metadata_t fabric_md,
+ in standard_metadata_t standard_metadata) {
+
+ bit<6> tmp_dscp = fabric_md.dscp;
+
+ action rewrite() {
+ // Do nothing, tmp_dscp is already initialized.
+ }
+
+ // Sets the DSCP field to zero. Should be used for edge ports facing devices
+ // that do not support the SDFAB DSCP encoding.
+ action clear() {
+ tmp_dscp = 0;
+ }
+
+ table rewriter {
+ key = {
+ standard_metadata.egress_port : exact @name("eg_port");
+ }
+ actions = {
+ rewrite;
+ clear;
+ @defaultonly nop;
+ }
+ const default_action = nop;
+ size = DSCP_REWRITER_TABLE_SIZE;
+ }
+
+ apply {
+ if (rewriter.apply().hit) {
+#ifdef WITH_SPGW
+ if (hdr.gtpu_ipv4.isValid()) {
+ hdr.ipv4.dscp = tmp_dscp;
+ } else
+#endif // WITH_SPGW
+ if (hdr.ipv4.isValid()) {
+ hdr.inner_ipv4.dscp = tmp_dscp;
+ }
+ }
+ }
+}
+
+#endif
diff --git a/pipelines/fabric/impl/src/main/resources/include/control/spgw.p4 b/pipelines/fabric/impl/src/main/resources/include/control/spgw.p4
index adacd9a..6750dbd 100644
--- a/pipelines/fabric/impl/src/main/resources/include/control/spgw.p4
+++ b/pipelines/fabric/impl/src/main/resources/include/control/spgw.p4
@@ -45,6 +45,8 @@
hdr.ipv4 = hdr.inner_ipv4;
hdr.inner_ipv4.setInvalid();
hdr.gtpu.setInvalid();
+ hdr.gtpu_options.setInvalid();
+ hdr.gtpu_ext_psc.setInvalid();
}
@hidden
action decap_inner_tcp() {
@@ -115,10 +117,11 @@
//===== Interface Tables ======//
//=============================//
- action load_iface(spgw_interface_t src_iface) {
+ action load_iface(spgw_interface_t src_iface, slice_id_t slice_id) {
// Interface type can be access, core, from_dbuf (see InterfaceType enum)
fabric_md.spgw.src_iface = src_iface;
fabric_md.spgw.skip_spgw = _FALSE;
+ fabric_md.slice_id = slice_id;
}
action iface_miss() {
fabric_md.spgw.src_iface = SPGW_IFACE_UNKNOWN;
@@ -143,21 +146,26 @@
//=============================//
//===== PDR Tables ======//
//=============================//
-
action load_pdr(pdr_ctr_id_t ctr_id,
far_id_t far_id,
- bit<1> needs_gtpu_decap) {
+ bit<1> needs_gtpu_decap,
+ tc_t tc) {
fabric_md.spgw.ctr_id = ctr_id;
fabric_md.spgw.far_id = far_id;
fabric_md.spgw.needs_gtpu_decap = (_BOOL)needs_gtpu_decap;
+ fabric_md.tc = tc;
}
- action load_pdr_qos(pdr_ctr_id_t ctr_id,
- far_id_t far_id,
- bit<1> needs_gtpu_decap,
- qid_t qid) {
- load_pdr(ctr_id, far_id, needs_gtpu_decap);
- // we cannot set the qid, since bmv2 does not support it
+ action load_pdr_qos(pdr_ctr_id_t ctr_id,
+ far_id_t far_id,
+ bit<1> needs_gtpu_decap,
+ // Used to push QFI, valid for 5G traffic only
+ bit<1> needs_qfi_push,
+ qfi_t qfi,
+ tc_t tc) {
+ load_pdr(ctr_id, far_id, needs_gtpu_decap, tc);
+ fabric_md.spgw.qfi = qfi;
+ fabric_md.spgw.needs_qfi_push = (_BOOL)needs_qfi_push;
}
// These two tables scale well and cover the average case PDR
@@ -177,6 +185,10 @@
key = {
hdr.ipv4.dst_addr : exact @name("tunnel_ipv4_dst");
hdr.gtpu.teid : exact @name("teid");
+ // Match valid only for 5G traffic
+ hdr.gtpu_ext_psc.isValid() : exact @name("has_qfi");
+ // QFI metadata is 0 when gptu_ext_psc is invalid.
+ fabric_md.spgw.qfi : exact @name("qfi");
}
actions = {
load_pdr;
@@ -294,7 +306,6 @@
counter(MAX_PDR_COUNTERS, CounterType.packets_and_bytes) pdr_counter;
-
@hidden
action gtpu_encap() {
hdr.gtpu_ipv4.setValid();
@@ -320,7 +331,6 @@
+ (UDP_HDR_SIZE + GTP_HDR_SIZE);
hdr.gtpu_udp.checksum = 0; // Updated later, if WITH_SPGW_UDP_CSUM_UPDATE
-
hdr.outer_gtpu.setValid();
hdr.outer_gtpu.version = GTP_V1;
hdr.outer_gtpu.pt = GTP_PROTOCOL_TYPE_GTP;
@@ -333,10 +343,35 @@
hdr.outer_gtpu.teid = fabric_md.spgw.teid;
}
+ @hidden
+ action gtpu_encap_qfi() {
+ gtpu_encap();
+ hdr.gtpu_ipv4.total_len = hdr.ipv4.total_len
+ + IPV4_HDR_SIZE + UDP_HDR_SIZE + GTP_HDR_SIZE
+ + GTPU_OPTIONS_HDR_BYTES + GTPU_EXT_PSC_HDR_BYTES;
+ hdr.gtpu_udp.len = fabric_md.spgw.ipv4_len
+ + UDP_HDR_SIZE + GTP_HDR_SIZE
+ + GTPU_OPTIONS_HDR_BYTES + GTPU_EXT_PSC_HDR_BYTES;
+ hdr.outer_gtpu.msglen = fabric_md.spgw.ipv4_len
+ + GTPU_OPTIONS_HDR_BYTES + GTPU_EXT_PSC_HDR_BYTES;
+ hdr.outer_gtpu.ex_flag = 1;
+ hdr.outer_gtpu_options.setValid();
+ hdr.outer_gtpu_options.next_ext = GTPU_NEXT_EXT_PSC;
+ hdr.outer_gtpu_ext_psc.setValid();
+ hdr.outer_gtpu_ext_psc.type = GTPU_EXT_PSC_TYPE_DL;
+ hdr.outer_gtpu_ext_psc.len = GTPU_EXT_PSC_LEN;
+ hdr.outer_gtpu_ext_psc.qfi = fabric_md.spgw.qfi;
+ hdr.outer_gtpu_ext_psc.next_ext = GTPU_NEXT_EXT_NONE;
+ }
+
apply {
if (fabric_md.spgw.skip_spgw == _FALSE) {
if (fabric_md.spgw.needs_gtpu_encap == _TRUE) {
- gtpu_encap();
+ if (fabric_md.spgw.needs_qfi_push == _TRUE) {
+ gtpu_encap_qfi();
+ } else {
+ gtpu_encap();
+ }
}
if (fabric_md.spgw.skip_egress_pdr_ctr == _FALSE) {
pdr_counter.count(fabric_md.spgw.ctr_id);