blob: 11e46394fc294a8c1652af7e1426456c179e970d [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
29#define BNG_MAX_SUBSC 8192
30#define BNG_MAX_NET_PER_SUBSC 4
31#define BNG_MAX_SUBSC_NET BNG_MAX_NET_PER_SUBSC * BNG_MAX_SUBSC
32
33#define BNG_SUBSC_IPV6_NET_PREFIX_LEN 64
34
35control bng_ingress_upstream(
36 inout parsed_headers_t hdr,
37 inout fabric_metadata_t fmeta,
38 inout standard_metadata_t smeta) {
39
40 counter(BNG_MAX_SUBSC, CounterType.packets) c_terminated;
41 counter(BNG_MAX_SUBSC, CounterType.packets) c_dropped;
42 counter(BNG_MAX_SUBSC, CounterType.packets) c_control;
43
44 vlan_id_t s_tag = hdr.vlan_tag.vlan_id;
45 vlan_id_t c_tag = hdr.inner_vlan_tag.vlan_id;
46
Daniele Moroe22b5742019-06-28 15:32:37 -070047 _BOOL drop = _FALSE;
Carmelo Cascone4d8785b2019-05-31 17:11:26 -070048 // TABLE: t_line_map
49 // Maps double VLAN tags to line ID. Line IDs are used to uniquelly identify
50 // a subscriber.
51
52 action set_line(bit<32> line_id) {
53 fmeta.bng.line_id = line_id;
54 }
55
56 table t_line_map {
57 actions = {
58 @defaultonly nop;
59 set_line;
60 }
61 key = {
62 s_tag: exact @name("s_tag");
63 c_tag: exact @name("c_tag");
64 }
65 size = BNG_MAX_SUBSC;
66 const default_action = nop;
67 }
68
69 // TABLE: t_pppoe_cp
70 // Punt to CPU for PPPeE control packets.
71
72 action punt_to_cpu() {
73 smeta.egress_spec = CPU_PORT;
74 fmeta.skip_forwarding = _TRUE;
75 fmeta.skip_next = _TRUE;
76 c_control.count(fmeta.bng.line_id);
77 }
78
79 table t_pppoe_cp {
80 key = {
81 hdr.pppoe.code : exact @name("pppoe_code");
82 hdr.pppoe.protocol : ternary @name("pppoe_protocol");
83 }
84 actions = {
85 punt_to_cpu;
86 @defaultonly nop;
87 }
88 size = 16;
89 const default_action = nop;
90 }
91
92 // TABLE: PPPoE termination for IPv4
93 // Check subscriber IPv4 source address, line_id, and pppoe_session_id
94 // (antispoofing), if line is enabled, pop PPPoE and double VLANs.
95
96 @hidden
97 action term_enabled(bit<16> eth_type) {
98 hdr.ethernet.eth_type = eth_type;
99 fmeta.eth_type = eth_type;
100 hdr.pppoe.setInvalid();
101 hdr.vlan_tag.setInvalid();
102 hdr.inner_vlan_tag.setInvalid();
103 c_terminated.count(fmeta.bng.line_id);
104 }
105
106 action term_disabled() {
107 fmeta.bng.type = BNG_TYPE_INVALID;
108 fmeta.skip_forwarding = _TRUE;
109 fmeta.skip_next = _TRUE;
110 mark_to_drop(smeta);
Daniele Moroe22b5742019-06-28 15:32:37 -0700111 drop = _TRUE;
Carmelo Cascone4d8785b2019-05-31 17:11:26 -0700112 }
113
114 action term_enabled_v4() {
115 term_enabled(ETHERTYPE_IPV4);
116 }
117
118 table t_pppoe_term_v4 {
119 key = {
120 fmeta.bng.line_id : exact @name("line_id");
121 hdr.ipv4.src_addr : exact @name("ipv4_src");
122 hdr.pppoe.session_id : exact @name("pppoe_session_id");
123 }
124 actions = {
125 term_enabled_v4;
126 @defaultonly term_disabled;
127 }
128 size = BNG_MAX_SUBSC_NET;
129 const default_action = term_disabled;
130 }
131
132#ifdef WITH_IPV6
133 action term_enabled_v6() {
134 term_enabled(ETHERTYPE_IPV6);
135 }
136
137 table t_pppoe_term_v6 {
138 key = {
139 fmeta.bng.line_id : exact @name("line_id");
140 hdr.ipv6.src_addr[127:64] : exact @name("ipv6_src_net_id");
141 hdr.pppoe.session_id : exact @name("pppoe_session_id");
142 }
143 actions = {
144 term_enabled_v6;
145 @defaultonly term_disabled;
146 }
147 size = BNG_MAX_SUBSC_NET;
148 const default_action = term_disabled;
149 }
150#endif // WITH_IPV6
151
152 apply {
153 // If table miss, line_id will be 0 (default metadata value).
154 t_line_map.apply();
155
156 if (t_pppoe_cp.apply().hit) {
157 return;
158 }
159
160 if (hdr.ipv4.isValid()) {
161 t_pppoe_term_v4.apply();
Daniele Moroe22b5742019-06-28 15:32:37 -0700162 if (drop == _TRUE) {
163 c_dropped.count(fmeta.bng.line_id);
164 }
Carmelo Cascone4d8785b2019-05-31 17:11:26 -0700165 }
166#ifdef WITH_IPV6
167 else if (hdr.ipv6.isValid()) {
168 t_pppoe_term_v6.apply();
Daniele Moroe22b5742019-06-28 15:32:37 -0700169 if (drop == _TRUE) {
170 c_dropped.count(fmeta.bng.line_id);
171 }
Carmelo Cascone4d8785b2019-05-31 17:11:26 -0700172 }
173#endif // WITH_IPV6
174 }
175}
176
177control bng_ingress_downstream(
178 inout parsed_headers_t hdr,
179 inout fabric_metadata_t fmeta,
180 inout standard_metadata_t smeta) {
181
182 counter(BNG_MAX_SUBSC, CounterType.packets_and_bytes) c_line_rx;
183
184 meter(BNG_MAX_SUBSC, MeterType.bytes) m_besteff;
185 meter(BNG_MAX_SUBSC, MeterType.bytes) m_prio;
186
187 // Downstream line map tables.
188 // Map IP dest address to line ID and next ID. Setting a next ID here
189 // allows to skip the fabric.p4 forwarding stage later.
Daniele Moroe22b5742019-06-28 15:32:37 -0700190 _BOOL prio = _FALSE;
Carmelo Cascone4d8785b2019-05-31 17:11:26 -0700191
192 @hidden
193 action set_line(bit<32> line_id) {
194 fmeta.bng.type = BNG_TYPE_DOWNSTREAM;
195 fmeta.bng.line_id = line_id;
196 c_line_rx.count(line_id);
197 }
198
199 action set_line_next(bit<32> line_id, next_id_t next_id) {
200 set_line(line_id);
201 fmeta.next_id = next_id;
202 fmeta.skip_forwarding = _TRUE;
203 }
204
205 action set_line_drop(bit<32> line_id) {
206 set_line(line_id);
207 fmeta.skip_forwarding = _TRUE;
208 fmeta.skip_next = _TRUE;
209 mark_to_drop(smeta);
210 }
211
212 table t_line_map_v4 {
213 key = {
214 hdr.ipv4.dst_addr: exact @name("ipv4_dst");
215 }
216 actions = {
217 @defaultonly nop;
218 set_line_next;
219 set_line_drop;
220 }
221 size = BNG_MAX_SUBSC_NET;
222 const default_action = nop;
223 }
224
225#ifdef WITH_IPV6
226 table t_line_map_v6 {
227 key = {
228 hdr.ipv6.dst_addr[127:64]: exact @name("ipv6_dst_net_id");
229 }
230 actions = {
231 @defaultonly nop;
232 set_line_next;
233 set_line_drop;
234 }
235 size = BNG_MAX_SUBSC_NET;
236 const default_action = nop;
237 }
238#endif // WITH_IPV6
239
240 // Downstream QoS tables.
241 // Provide coarse metering before prioritazion in the OLT. By default
242 // everything is tagged and metered as best-effort traffic.
243
244 action qos_prio() {
Daniele Moroe22b5742019-06-28 15:32:37 -0700245 prio = _TRUE;
Carmelo Cascone4d8785b2019-05-31 17:11:26 -0700246 }
247
248 action qos_besteff() {
Daniele Moroe22b5742019-06-28 15:32:37 -0700249 // no-op
Carmelo Cascone4d8785b2019-05-31 17:11:26 -0700250 }
251
252 table t_qos_v4 {
253 key = {
254 fmeta.bng.line_id : ternary @name("line_id");
255 hdr.ipv4.src_addr : lpm @name("ipv4_src");
256 hdr.ipv4.dscp : ternary @name("ipv4_dscp");
257 hdr.ipv4.ecn : ternary @name("ipv4_ecn");
258 }
259 actions = {
260 qos_prio;
261 qos_besteff;
262 }
263 size = 256;
264 const default_action = qos_besteff;
265 }
266
267#ifdef WITH_IPV6
268 table t_qos_v6 {
269 key = {
270 fmeta.bng.line_id : ternary @name("line_id");
271 hdr.ipv6.src_addr : lpm @name("ipv6_src");
272 hdr.ipv6.traffic_class : ternary @name("ipv6_traffic_class");
273 }
274 actions = {
275 qos_prio;
276 qos_besteff;
277 }
278 size = 256;
279 const default_action = qos_besteff;
280 }
281#endif // WITH_IPV6
282
283 apply {
284 // IPv4
285 if (hdr.ipv4.isValid()) {
286 if (t_line_map_v4.apply().hit) {
287 // Apply QoS only to subscriber traffic. This makes sense only
288 // if the downstream ports are used to receive IP traffic NOT
289 // destined to subscribers, e.g. to services in the compute
290 // nodes.
291 t_qos_v4.apply();
Daniele Moroe22b5742019-06-28 15:32:37 -0700292 if (prio == _TRUE) {
293 m_prio.execute_meter((bit<32>)fmeta.bng.line_id,
294 fmeta.bng.ds_meter_result);
295 } else {
296 m_besteff.execute_meter((bit<32>)fmeta.bng.line_id,
297 fmeta.bng.ds_meter_result);
298 }
Carmelo Cascone4d8785b2019-05-31 17:11:26 -0700299 }
300 }
301#ifdef WITH_IPV6
302 // IPv6
303 else if (hdr.ipv6.isValid()) {
304 if (t_line_map_v6.apply().hit) {
305 t_qos_v6.apply();
Daniele Moroe22b5742019-06-28 15:32:37 -0700306 if (prio == _TRUE) {
307 m_prio.execute_meter((bit<32>)fmeta.bng.line_id,
308 fmeta.bng.ds_meter_result);
309 } else {
310 m_besteff.execute_meter((bit<32>)fmeta.bng.line_id,
311 fmeta.bng.ds_meter_result);
312 }
Carmelo Cascone4d8785b2019-05-31 17:11:26 -0700313 }
314 }
315#endif // WITH_IPV6
316 }
317}
318
319control bng_egress_downstream(
320 inout parsed_headers_t hdr,
321 inout fabric_metadata_t fmeta,
322 inout standard_metadata_t smeta) {
323
324 counter(BNG_MAX_SUBSC, CounterType.packets_and_bytes) c_line_tx;
325
326 @hidden
327 action encap(vlan_id_t c_tag, bit<16> pppoe_session_id) {
328 // s_tag (outer VLAN) should be already set via the next_vlan table.
329 // Here we add c_tag (inner VLAN) and PPPoE.
330 hdr.vlan_tag.eth_type = ETHERTYPE_VLAN;
331 hdr.inner_vlan_tag.setValid();
332 hdr.inner_vlan_tag.vlan_id = c_tag;
333 hdr.inner_vlan_tag.eth_type = ETHERTYPE_PPPOES;
334 hdr.pppoe.setValid();
335 hdr.pppoe.version = 4w1;
336 hdr.pppoe.type_id = 4w1;
337 hdr.pppoe.code = 8w0; // 0 means session stage.
338 hdr.pppoe.session_id = pppoe_session_id;
339 c_line_tx.count(fmeta.bng.line_id);
340 }
341
342 action encap_v4(vlan_id_t c_tag, bit<16> pppoe_session_id) {
343 encap(c_tag, pppoe_session_id);
344 hdr.pppoe.length = hdr.ipv4.total_len + 16w2;
345 hdr.pppoe.protocol = PPPOE_PROTOCOL_IP4;
346 }
347
348#ifdef WITH_IPV6
349 action encap_v6(vlan_id_t c_tag, bit<16> pppoe_session_id) {
350 encap(c_tag, pppoe_session_id);
351 hdr.pppoe.length = hdr.ipv6.payload_len + 16w42;
352 hdr.pppoe.protocol = PPPOE_PROTOCOL_IP6;
353 }
354#endif // WITH_IPV6
355
356 table t_session_encap {
357 key = {
358 fmeta.bng.line_id : exact @name("line_id");
359 }
360 actions = {
361 @defaultonly nop;
362 encap_v4;
363#ifdef WITH_IPV6
364 encap_v6;
365#endif // WITH_IPV6
366 }
367 size = BNG_MAX_SUBSC;
368 const default_action = nop();
369 }
370
371 apply {
372 t_session_encap.apply();
373 }
374}
375
376control bng_ingress(
377 inout parsed_headers_t hdr,
378 inout fabric_metadata_t fmeta,
379 inout standard_metadata_t smeta) {
380
381 bng_ingress_upstream() upstream;
382 bng_ingress_downstream() downstream;
383
384 apply {
385 if (hdr.pppoe.isValid()) {
386 fmeta.bng.type = BNG_TYPE_UPSTREAM;
387 upstream.apply(hdr, fmeta, smeta);
388 }
389 else {
390 // We are not sure the pkt is a BNG downstream one, first we need to
391 // verify the IP dst matches the IP addr of a subscriber...
392 downstream.apply(hdr, fmeta, smeta);
393 }
394 }
395}
396
397control bng_egress(
398 inout parsed_headers_t hdr,
399 inout fabric_metadata_t fmeta,
400 inout standard_metadata_t smeta) {
401
402 bng_egress_downstream() downstream;
403
404 apply {
405 if (fmeta.bng.type == BNG_TYPE_DOWNSTREAM) {
406 downstream.apply(hdr, fmeta, smeta);
407 }
408 }
409}
410
411#endif