blob: ede00018f7512b801b0cd93b8dca3c306be5b12f [file] [log] [blame]
Carmelo Cascone4d8785b2019-05-31 17:11:26 -07001/*
2 * Copyright 2019-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 /*
18 * BNG processor implementation. Provides upstream and downstream termination
19 * based on double VLAN tags (s_tag, c_tag) and PPPoE.
20 *
21 * This implementation is based on the P4 Service Edge (p4se) contribution from
22 * Deutsche Telekom:
23 * https://github.com/opencord/p4se
24 */
25
26#ifndef __BNG__
27#define __BNG__
28
Carmelo Cascone4d8785b2019-05-31 17:11:26 -070029#define BNG_SUBSC_IPV6_NET_PREFIX_LEN 64
30
31control bng_ingress_upstream(
32 inout parsed_headers_t hdr,
33 inout fabric_metadata_t fmeta,
34 inout standard_metadata_t smeta) {
35
36 counter(BNG_MAX_SUBSC, CounterType.packets) c_terminated;
37 counter(BNG_MAX_SUBSC, CounterType.packets) c_dropped;
38 counter(BNG_MAX_SUBSC, CounterType.packets) c_control;
39
Carmelo Cascone4d8785b2019-05-31 17:11:26 -070040 // TABLE: t_pppoe_cp
41 // Punt to CPU for PPPeE control packets.
42
43 action punt_to_cpu() {
44 smeta.egress_spec = CPU_PORT;
Carmelo Cascone4d8785b2019-05-31 17:11:26 -070045 c_control.count(fmeta.bng.line_id);
46 }
47
48 table t_pppoe_cp {
49 key = {
50 hdr.pppoe.code : exact @name("pppoe_code");
51 hdr.pppoe.protocol : ternary @name("pppoe_protocol");
52 }
53 actions = {
54 punt_to_cpu;
55 @defaultonly nop;
56 }
57 size = 16;
58 const default_action = nop;
59 }
60
61 // TABLE: PPPoE termination for IPv4
62 // Check subscriber IPv4 source address, line_id, and pppoe_session_id
63 // (antispoofing), if line is enabled, pop PPPoE and double VLANs.
64
65 @hidden
66 action term_enabled(bit<16> eth_type) {
Daniele Moro7c3a0022019-07-12 13:38:34 -070067 hdr.inner_vlan_tag.eth_type = eth_type;
68 fmeta.last_eth_type = eth_type;
Carmelo Cascone4d8785b2019-05-31 17:11:26 -070069 hdr.pppoe.setInvalid();
Carmelo Cascone4d8785b2019-05-31 17:11:26 -070070 c_terminated.count(fmeta.bng.line_id);
71 }
72
73 action term_disabled() {
74 fmeta.bng.type = BNG_TYPE_INVALID;
Carmelo Cascone4d8785b2019-05-31 17:11:26 -070075 mark_to_drop(smeta);
Carmelo Cascone4d8785b2019-05-31 17:11:26 -070076 }
77
78 action term_enabled_v4() {
79 term_enabled(ETHERTYPE_IPV4);
80 }
81
Daniele Moro7c3a0022019-07-12 13:38:34 -070082 // TODO: add match on hdr.ethernet.src_addr for antispoofing
83 // Take into account that MAC src address is modified by the Next control block
84 // when doing routing functionality.
Carmelo Cascone4d8785b2019-05-31 17:11:26 -070085 table t_pppoe_term_v4 {
86 key = {
87 fmeta.bng.line_id : exact @name("line_id");
88 hdr.ipv4.src_addr : exact @name("ipv4_src");
89 hdr.pppoe.session_id : exact @name("pppoe_session_id");
90 }
91 actions = {
92 term_enabled_v4;
93 @defaultonly term_disabled;
94 }
95 size = BNG_MAX_SUBSC_NET;
96 const default_action = term_disabled;
97 }
98
99#ifdef WITH_IPV6
100 action term_enabled_v6() {
101 term_enabled(ETHERTYPE_IPV6);
102 }
103
Daniele Moro7c3a0022019-07-12 13:38:34 -0700104 // TODO: add match on hdr.ethernet.src_addr for antispoofing
105 // Match on unmodified metadata field, taking into account that MAC src address
106 // is modified by the Next control block when doing routing functionality.
Carmelo Cascone4d8785b2019-05-31 17:11:26 -0700107 table t_pppoe_term_v6 {
108 key = {
109 fmeta.bng.line_id : exact @name("line_id");
110 hdr.ipv6.src_addr[127:64] : exact @name("ipv6_src_net_id");
111 hdr.pppoe.session_id : exact @name("pppoe_session_id");
112 }
113 actions = {
114 term_enabled_v6;
115 @defaultonly term_disabled;
116 }
117 size = BNG_MAX_SUBSC_NET;
118 const default_action = term_disabled;
119 }
120#endif // WITH_IPV6
121
122 apply {
Daniele Moro7c3a0022019-07-12 13:38:34 -0700123 if(t_pppoe_cp.apply().hit) {
Carmelo Cascone4d8785b2019-05-31 17:11:26 -0700124 return;
125 }
Carmelo Cascone4d8785b2019-05-31 17:11:26 -0700126 if (hdr.ipv4.isValid()) {
Daniele Moro7c3a0022019-07-12 13:38:34 -0700127 switch(t_pppoe_term_v4.apply().action_run) {
128 term_disabled: {
129 c_dropped.count(fmeta.bng.line_id);
130 }
Daniele Moroe22b5742019-06-28 15:32:37 -0700131 }
Carmelo Cascone4d8785b2019-05-31 17:11:26 -0700132 }
133#ifdef WITH_IPV6
134 else if (hdr.ipv6.isValid()) {
Daniele Moro7c3a0022019-07-12 13:38:34 -0700135 switch(t_pppoe_term_v6.apply().action_run) {
136 term_disabled: {
137 c_dropped.count(fmeta.bng.line_id);
138 }
139 }
Carmelo Cascone4d8785b2019-05-31 17:11:26 -0700140 }
141#endif // WITH_IPV6
142 }
143}
144
145control bng_ingress_downstream(
146 inout parsed_headers_t hdr,
147 inout fabric_metadata_t fmeta,
148 inout standard_metadata_t smeta) {
149
150 counter(BNG_MAX_SUBSC, CounterType.packets_and_bytes) c_line_rx;
151
152 meter(BNG_MAX_SUBSC, MeterType.bytes) m_besteff;
153 meter(BNG_MAX_SUBSC, MeterType.bytes) m_prio;
154
Daniele Moro7c3a0022019-07-12 13:38:34 -0700155 action set_session(bit<16> pppoe_session_id) {
Carmelo Cascone4d8785b2019-05-31 17:11:26 -0700156 fmeta.bng.type = BNG_TYPE_DOWNSTREAM;
Daniele Moro7c3a0022019-07-12 13:38:34 -0700157 fmeta.bng.pppoe_session_id = pppoe_session_id;
158 c_line_rx.count(fmeta.bng.line_id);
Carmelo Cascone4d8785b2019-05-31 17:11:26 -0700159 }
160
Daniele Moro7c3a0022019-07-12 13:38:34 -0700161 action drop() {
162 fmeta.bng.type = BNG_TYPE_DOWNSTREAM;
163 c_line_rx.count(fmeta.bng.line_id);
Carmelo Cascone4d8785b2019-05-31 17:11:26 -0700164 mark_to_drop(smeta);
165 }
166
Daniele Moro7c3a0022019-07-12 13:38:34 -0700167 table t_line_session_map {
Carmelo Cascone4d8785b2019-05-31 17:11:26 -0700168 key = {
Daniele Moro7c3a0022019-07-12 13:38:34 -0700169 fmeta.bng.line_id : exact @name("line_id");
Carmelo Cascone4d8785b2019-05-31 17:11:26 -0700170 }
171 actions = {
172 @defaultonly nop;
Daniele Moro7c3a0022019-07-12 13:38:34 -0700173 set_session;
174 drop;
Carmelo Cascone4d8785b2019-05-31 17:11:26 -0700175 }
Daniele Moro7c3a0022019-07-12 13:38:34 -0700176 size = BNG_MAX_SUBSC;
Carmelo Cascone4d8785b2019-05-31 17:11:26 -0700177 const default_action = nop;
178 }
179
Carmelo Cascone4d8785b2019-05-31 17:11:26 -0700180 // Downstream QoS tables.
181 // Provide coarse metering before prioritazion in the OLT. By default
182 // everything is tagged and metered as best-effort traffic.
183
184 action qos_prio() {
Daniele Moro7c3a0022019-07-12 13:38:34 -0700185 // no-op
Carmelo Cascone4d8785b2019-05-31 17:11:26 -0700186 }
187
188 action qos_besteff() {
Daniele Moroe22b5742019-06-28 15:32:37 -0700189 // no-op
Carmelo Cascone4d8785b2019-05-31 17:11:26 -0700190 }
191
192 table t_qos_v4 {
193 key = {
194 fmeta.bng.line_id : ternary @name("line_id");
195 hdr.ipv4.src_addr : lpm @name("ipv4_src");
196 hdr.ipv4.dscp : ternary @name("ipv4_dscp");
197 hdr.ipv4.ecn : ternary @name("ipv4_ecn");
198 }
199 actions = {
200 qos_prio;
201 qos_besteff;
202 }
203 size = 256;
204 const default_action = qos_besteff;
205 }
206
207#ifdef WITH_IPV6
208 table t_qos_v6 {
209 key = {
210 fmeta.bng.line_id : ternary @name("line_id");
211 hdr.ipv6.src_addr : lpm @name("ipv6_src");
212 hdr.ipv6.traffic_class : ternary @name("ipv6_traffic_class");
213 }
214 actions = {
215 qos_prio;
216 qos_besteff;
217 }
218 size = 256;
219 const default_action = qos_besteff;
220 }
221#endif // WITH_IPV6
222
223 apply {
Daniele Moro7c3a0022019-07-12 13:38:34 -0700224 // We are not sure the pkt is a BNG downstream one, first we need to
225 // verify the line_id matches the one of a subscriber...
226
Carmelo Cascone4d8785b2019-05-31 17:11:26 -0700227 // IPv4
Daniele Moro7c3a0022019-07-12 13:38:34 -0700228 if (t_line_session_map.apply().hit) {
229 // Apply QoS only to subscriber traffic. This makes sense only
230 // if the downstream ports are used to receive IP traffic NOT
231 // destined to subscribers, e.g. to services in the compute
232 // nodes.
233 if (hdr.ipv4.isValid()) {
234 switch (t_qos_v4.apply().action_run) {
235 qos_prio: {
236 m_prio.execute_meter(fmeta.bng.line_id, fmeta.bng.ds_meter_result);
237 }
238 qos_besteff: {
239 m_besteff.execute_meter(fmeta.bng.line_id, fmeta.bng.ds_meter_result);
240 }
Daniele Moroe22b5742019-06-28 15:32:37 -0700241 }
Carmelo Cascone4d8785b2019-05-31 17:11:26 -0700242 }
Carmelo Cascone4d8785b2019-05-31 17:11:26 -0700243#ifdef WITH_IPV6
Daniele Moro7c3a0022019-07-12 13:38:34 -0700244 // IPv6
245 else if (hdr.ipv6.isValid()) {
246 switch (t_qos_v6.apply().action_run) {
247 qos_prio: {
248 m_prio.execute_meter(fmeta.bng.line_id, fmeta.bng.ds_meter_result);
249 }
250 qos_besteff: {
251 m_besteff.execute_meter(fmeta.bng.line_id, fmeta.bng.ds_meter_result);
252 }
Daniele Moroe22b5742019-06-28 15:32:37 -0700253 }
Carmelo Cascone4d8785b2019-05-31 17:11:26 -0700254 }
Carmelo Cascone4d8785b2019-05-31 17:11:26 -0700255#endif // WITH_IPV6
Daniele Moro7c3a0022019-07-12 13:38:34 -0700256 }
Carmelo Cascone4d8785b2019-05-31 17:11:26 -0700257 }
258}
259
260control bng_egress_downstream(
261 inout parsed_headers_t hdr,
262 inout fabric_metadata_t fmeta,
263 inout standard_metadata_t smeta) {
264
265 counter(BNG_MAX_SUBSC, CounterType.packets_and_bytes) c_line_tx;
266
267 @hidden
Daniele Moro7c3a0022019-07-12 13:38:34 -0700268 action encap() {
269 // Here we add PPPoE and modify the inner_vlan_tag Ethernet Type.
Carmelo Cascone4d8785b2019-05-31 17:11:26 -0700270 hdr.inner_vlan_tag.eth_type = ETHERTYPE_PPPOES;
271 hdr.pppoe.setValid();
272 hdr.pppoe.version = 4w1;
273 hdr.pppoe.type_id = 4w1;
274 hdr.pppoe.code = 8w0; // 0 means session stage.
Daniele Moro7c3a0022019-07-12 13:38:34 -0700275 hdr.pppoe.session_id = fmeta.bng.pppoe_session_id;
Carmelo Cascone4d8785b2019-05-31 17:11:26 -0700276 c_line_tx.count(fmeta.bng.line_id);
277 }
278
Daniele Moro7c3a0022019-07-12 13:38:34 -0700279 action encap_v4() {
280 encap();
Carmelo Cascone4d8785b2019-05-31 17:11:26 -0700281 hdr.pppoe.length = hdr.ipv4.total_len + 16w2;
282 hdr.pppoe.protocol = PPPOE_PROTOCOL_IP4;
283 }
284
285#ifdef WITH_IPV6
Daniele Moro7c3a0022019-07-12 13:38:34 -0700286 action encap_v6() {
287 encap();
Carmelo Cascone4d8785b2019-05-31 17:11:26 -0700288 hdr.pppoe.length = hdr.ipv6.payload_len + 16w42;
289 hdr.pppoe.protocol = PPPOE_PROTOCOL_IP6;
290 }
291#endif // WITH_IPV6
292
Carmelo Cascone4d8785b2019-05-31 17:11:26 -0700293 apply {
Daniele Moro7c3a0022019-07-12 13:38:34 -0700294 if (hdr.ipv4.isValid()) {
295 encap_v4();
296 }
297#ifdef WITH_IPV6
298 // IPv6
299 else if (hdr.ipv6.isValid()) {
300 encap_v6();
301 }
302#endif // WITH_IPV6
Carmelo Cascone4d8785b2019-05-31 17:11:26 -0700303 }
304}
305
306control bng_ingress(
307 inout parsed_headers_t hdr,
308 inout fabric_metadata_t fmeta,
309 inout standard_metadata_t smeta) {
310
Daniele Moro7c3a0022019-07-12 13:38:34 -0700311 bng_ingress_upstream() upstream;
312 bng_ingress_downstream() downstream;
Carmelo Cascone4d8785b2019-05-31 17:11:26 -0700313
Daniele Moro7c3a0022019-07-12 13:38:34 -0700314 vlan_id_t s_tag = 0;
315 vlan_id_t c_tag = 0;
316
317 // TABLE: t_line_map
318 // Map s_tag and c_tag to a line ID to uniquely identify a subscriber
319
320 action set_line(bit<32> line_id) {
321 fmeta.bng.line_id = line_id;
Carmelo Cascone4d8785b2019-05-31 17:11:26 -0700322 }
Daniele Moro7c3a0022019-07-12 13:38:34 -0700323
324 table t_line_map {
325 key = {
326 s_tag : exact @name("s_tag");
327 c_tag : exact @name("c_tag");
328 }
329 actions = {
330 @defaultonly nop;
331 set_line;
332 }
333 size = BNG_MAX_SUBSC;
334 const default_action = nop;
Carmelo Cascone4d8785b2019-05-31 17:11:26 -0700335 }
Daniele Moro7c3a0022019-07-12 13:38:34 -0700336
337 apply {
338 if(hdr.pppoe.isValid()) {
339 s_tag = hdr.vlan_tag.vlan_id;
340 c_tag = hdr.inner_vlan_tag.vlan_id;
341 } else {
342 // We expect the packet to be downstream,
343 // the tags are set by the next stage in the metadata.
344 s_tag = fmeta.vlan_id;
345 c_tag = fmeta.inner_vlan_id;
346 }
347
348 // First map the double VLAN tags to a line ID
349 // If table miss line ID will be 0.
350 t_line_map.apply();
351
352 if (hdr.pppoe.isValid()) {
353 fmeta.bng.type = BNG_TYPE_UPSTREAM;
354 upstream.apply(hdr, fmeta, smeta);
355 } else {
356 downstream.apply(hdr, fmeta, smeta);
357 }
358 }
Carmelo Cascone4d8785b2019-05-31 17:11:26 -0700359}
360
361control bng_egress(
362 inout parsed_headers_t hdr,
363 inout fabric_metadata_t fmeta,
364 inout standard_metadata_t smeta) {
365
366 bng_egress_downstream() downstream;
367
368 apply {
369 if (fmeta.bng.type == BNG_TYPE_DOWNSTREAM) {
370 downstream.apply(hdr, fmeta, smeta);
371 }
372 }
373}
374
375#endif