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