blob: 87d887a548e7ce26f8696fea3ea61d348deaa233 [file] [log] [blame]
Daniele Moro08c9e7f2021-07-28 18:53:34 +02001/*
2 * Copyright 2021-present Open Networking Foundation
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#ifndef __SLICING__
18#define __SLICING__
19
20// ACL-like classification, maps lookup metadata to slice_id and tc. For UE
21// traffic, values can be overriden later by the SPGW PDR tables.
22// To apply the same slicing and QoS treatment end-to-end, we use the IPv4 DSCP
23// field to piggyback slice_id and tc (see EgressDscpRewriter). This is
24// especially important for UE traffic, where classification based on PDRs can
25// only happen at the ingress leaf switch (implementing the UPF function).
26// As such, for traffic coming from selected ports, we allow trusting the
27// slice_id and tc values carried in the dscp.
28control IngressSliceTcClassifier (in parsed_headers_t hdr,
29 inout fabric_metadata_t fabric_md,
30 in standard_metadata_t standard_metadata) {
31
32 direct_counter(CounterType.packets) classifier_stats;
33
34 action set_slice_id_tc(slice_id_t slice_id, tc_t tc) {
35 fabric_md.slice_id = slice_id;
36 fabric_md.tc = tc;
37 classifier_stats.count();
38 }
39
40 // Should be used only for infrastructure ports (leaf-leaf, or leaf-spine),
41 // or ports facing servers that implement early classification based on the
42 // SDFAB DSCP encoding (slice_id++tc).
43 action trust_dscp() {
44 fabric_md.slice_id = hdr.ipv4.dscp[SLICE_ID_WIDTH+TC_WIDTH-1:TC_WIDTH];
45 fabric_md.tc = hdr.ipv4.dscp[TC_WIDTH-1:0];
46 classifier_stats.count();
47 }
48
49 table classifier {
50 key = {
51 standard_metadata.ingress_port : ternary @name("ig_port");
52 fabric_md.lkp.ipv4_src : ternary @name("ipv4_src");
53 fabric_md.lkp.ipv4_dst : ternary @name("ipv4_dst");
54 fabric_md.lkp.ip_proto : ternary @name("ip_proto");
55 fabric_md.lkp.l4_sport : ternary @name("l4_sport");
56 fabric_md.lkp.l4_dport : ternary @name("l4_dport");
57 }
58 actions = {
59 set_slice_id_tc;
60 trust_dscp;
61 }
62 const default_action = set_slice_id_tc(DEFAULT_SLICE_ID, DEFAULT_TC);
63 counters = classifier_stats;
64 size = QOS_CLASSIFIER_TABLE_SIZE;
65 }
66
67 apply {
68 classifier.apply();
69 }
70}
71
72// Provides metering and mapping to queues based on slice_id and tc. Should be
73// applied after any other block writing slice_id and tc.
74control IngressQos (inout fabric_metadata_t fabric_md,
75 inout standard_metadata_t standard_metadata) {
76
77 // One meter per tc per slice. The index should be slice_id++tc.
78 meter(1 << SLICE_TC_WIDTH, MeterType.bytes) slice_tc_meter;
79
80 direct_counter(CounterType.packets) queues_stats;
81
82 action set_queue(qid_t qid) {
83 // We can't set the queue id in bmv2.
84 queues_stats.count();
85 }
86
87 // For policing.
88 action meter_drop() {
89 mark_to_drop(standard_metadata);
90 queues_stats.count();
91 }
92
93 table queues {
94 key = {
95 fabric_md.slice_id: exact @name("slice_id");
96 fabric_md.tc: exact @name("tc");
97 fabric_md.packet_color: ternary @name("color"); // 0=GREEN, 1=YELLOW, 2=RED
98 }
99 actions = {
100 set_queue;
101 meter_drop;
102 }
103 const default_action = set_queue(0); // 0 = Best Effort
104 counters = queues_stats;
105 // Two times the number of tcs for all slices, because we might need to
106 // match on different colors for the same slice and tc.
107 size = 1 << (SLICE_TC_WIDTH + 1);
108 }
109
110 slice_tc_t slice_tc = fabric_md.slice_id++fabric_md.tc;
111
112 apply {
113 // Meter index should be 0 for all packets with default slice_id and tc.
114 slice_tc_meter.execute_meter((bit<32>) slice_tc, fabric_md.packet_color);
115 fabric_md.dscp = slice_tc;
116 queues.apply();
117 }
118}
119
120// Allows per-egress port rewriting of the outermost IPv4 DSCP field to
121// piggyback slice_id and tc across the fabric.
122control EgressDscpRewriter (inout parsed_headers_t hdr,
123 in fabric_metadata_t fabric_md,
124 in standard_metadata_t standard_metadata) {
125
126 bit<6> tmp_dscp = fabric_md.dscp;
127
128 action rewrite() {
129 // Do nothing, tmp_dscp is already initialized.
130 }
131
132 // Sets the DSCP field to zero. Should be used for edge ports facing devices
133 // that do not support the SDFAB DSCP encoding.
134 action clear() {
135 tmp_dscp = 0;
136 }
137
138 table rewriter {
139 key = {
140 standard_metadata.egress_port : exact @name("eg_port");
141 }
142 actions = {
143 rewrite;
144 clear;
145 @defaultonly nop;
146 }
147 const default_action = nop;
148 size = DSCP_REWRITER_TABLE_SIZE;
149 }
150
151 apply {
152 if (rewriter.apply().hit) {
153#ifdef WITH_SPGW
154 if (hdr.gtpu_ipv4.isValid()) {
155 hdr.ipv4.dscp = tmp_dscp;
156 } else
157#endif // WITH_SPGW
158 if (hdr.ipv4.isValid()) {
159 hdr.inner_ipv4.dscp = tmp_dscp;
160 }
161 }
162 }
163}
164
165#endif