blob: 9951667d1b48a9ed72e31a6bea9c13edb87c3287 [file] [log] [blame]
Daniele Moro464e5ed2019-07-25 14:45:01 -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
17package org.onosproject.pipelines.fabric.impl.behaviour.bng;
18
19import com.google.common.collect.ImmutableBiMap;
20import com.google.common.collect.ImmutableSet;
21import com.google.common.collect.Lists;
22import com.google.common.collect.Maps;
23import org.onosproject.core.ApplicationId;
24import org.onosproject.drivers.p4runtime.AbstractP4RuntimeHandlerBehaviour;
25import org.onosproject.net.behaviour.BngProgrammable;
26import org.onosproject.net.flow.DefaultFlowRule;
27import org.onosproject.net.flow.DefaultTrafficSelector;
28import org.onosproject.net.flow.DefaultTrafficTreatment;
29import org.onosproject.net.flow.FlowRule;
30import org.onosproject.net.flow.FlowRuleService;
31import org.onosproject.net.flow.TableId;
32import org.onosproject.net.flow.TrafficSelector;
33import org.onosproject.net.flow.TrafficTreatment;
34import org.onosproject.net.flow.criteria.Criterion;
35import org.onosproject.net.flow.criteria.PiCriterion;
36import org.onosproject.net.pi.model.PiCounterId;
37import org.onosproject.net.pi.runtime.PiAction;
38import org.onosproject.net.pi.runtime.PiActionParam;
39import org.onosproject.net.pi.runtime.PiCounterCell;
40import org.onosproject.net.pi.runtime.PiCounterCellData;
41import org.onosproject.net.pi.runtime.PiCounterCellHandle;
42import org.onosproject.net.pi.runtime.PiCounterCellId;
43import org.onosproject.p4runtime.api.P4RuntimeWriteClient;
44import org.onosproject.pipelines.fabric.impl.behaviour.FabricConstants;
45
46import java.util.Collection;
47import java.util.List;
48import java.util.Map;
49import java.util.Set;
50import java.util.stream.Collectors;
51
Daniele Moro464e5ed2019-07-25 14:45:01 -070052public class FabricBngProgrammable extends AbstractP4RuntimeHandlerBehaviour
53 implements BngProgrammable {
54
55 // Default priority of the inserted BNG rules.
56 private static final int DEFAULT_PRIORITY = 10;
57 // The index at which control plane packets are counted before the attachment is created.
58 private static final int DEFAULT_CONTROL_INDEX = 0;
59 // FIXME: retrieve this value from the table size in the PipelineModel
60 // Max number of supported attachments, useful to make sure to not read/write non-existing counters.
61 private static final int MAX_SUPPORTED_ATTACHMENTS = 1000;
62
63 private static final ImmutableBiMap<BngCounterType, PiCounterId> COUNTER_MAP =
64 ImmutableBiMap.<BngCounterType, PiCounterId>builder()
65 .put(BngCounterType.DOWNSTREAM_RX, FabricConstants.FABRIC_INGRESS_BNG_INGRESS_DOWNSTREAM_C_LINE_RX)
66 .put(BngCounterType.DOWNSTREAM_TX, FabricConstants.FABRIC_EGRESS_BNG_EGRESS_DOWNSTREAM_C_LINE_TX)
67 .put(BngCounterType.UPSTREAM_TX, FabricConstants.FABRIC_INGRESS_BNG_INGRESS_UPSTREAM_C_TERMINATED)
68 .put(BngCounterType.UPSTREAM_DROPPED, FabricConstants.FABRIC_INGRESS_BNG_INGRESS_UPSTREAM_C_DROPPED)
69 .put(BngCounterType.CONTROL_PLANE, FabricConstants.FABRIC_INGRESS_BNG_INGRESS_UPSTREAM_C_CONTROL)
70 .build();
71
72 // FIXME: add these counters to the BNG pipeline
73 private static final ImmutableSet<BngCounterType> UNSUPPORTED_COUNTER =
74 ImmutableSet.of(BngCounterType.UPSTREAM_RX, BngCounterType.DOWNSTREAM_DROPPED);
75
76 private FlowRuleService flowRuleService;
77
78 @Override
79 protected boolean setupBehaviour(String opName) {
80 if (!super.setupBehaviour(opName)) {
81 return false;
82 }
83 flowRuleService = handler().get(FlowRuleService.class);
84 return true;
85 }
86
87 @Override
88 public boolean init(ApplicationId appId) {
89 if (setupBehaviour("init()")) {
90 this.setupPuntToCpu(appId);
91 return true;
92 }
93 return false;
94 }
95
96 @Override
97 public void cleanUp(ApplicationId appId) throws BngProgrammableException {
Daniele Moro2832ec92019-12-05 22:10:48 -080098 if (!setupBehaviour("cleanUp()")) {
99 return;
100 }
Daniele Moro464e5ed2019-07-25 14:45:01 -0700101 flowRuleService.removeFlowRulesById(appId);
102 this.resetControlTrafficCounter();
103 }
104
105 @Override
106 public void setupAttachment(Attachment attachmentInfo) throws BngProgrammableException {
Daniele Moro2832ec92019-12-05 22:10:48 -0800107 if (!setupBehaviour("setupAttachment()")) {
108 return;
109 }
Daniele Moro6d84c182019-12-05 22:24:14 -0800110 checkAttachment(attachmentInfo);
Daniele Moro464e5ed2019-07-25 14:45:01 -0700111 List<FlowRule> lstFlowRules = Lists.newArrayList();
112 lstFlowRules.add(buildTLineMapFlowRule(attachmentInfo));
113 // If the line is not active do not generate the rule for the table
114 // t_pppoe_term_v4 since term_disabled is @defaultonly action
115 if (attachmentInfo.lineActive()) {
116 lstFlowRules.add(buildTPppoeTermV4FlowRule(attachmentInfo));
117 }
118 lstFlowRules.add(buildTLineSessionMapFlowRule(attachmentInfo));
Daniele Moro893988e2019-11-27 16:14:38 -0800119
Daniele Moro464e5ed2019-07-25 14:45:01 -0700120 lstFlowRules.forEach(flowRule -> flowRuleService.applyFlowRules(flowRule));
121 }
122
123 @Override
Daniele Moro6d84c182019-12-05 22:24:14 -0800124 public void removeAttachment(Attachment attachmentInfo) throws BngProgrammableException {
Daniele Moro2832ec92019-12-05 22:10:48 -0800125 if (!setupBehaviour("removeAttachment()")) {
126 return;
127 }
Daniele Moro6d84c182019-12-05 22:24:14 -0800128 checkAttachment(attachmentInfo);
Daniele Moro464e5ed2019-07-25 14:45:01 -0700129 List<FlowRule> lstFlowRules = Lists.newArrayList();
130 lstFlowRules.add(buildTLineMapFlowRule(attachmentInfo));
131 lstFlowRules.add(buildTPppoeTermV4FlowRule(attachmentInfo));
132 lstFlowRules.add(buildTLineSessionMapFlowRule(attachmentInfo));
133
134 lstFlowRules.forEach(flowRule -> flowRuleService.removeFlowRules(flowRule));
135 }
136
137 @Override
138 public Map<BngCounterType, PiCounterCellData> readCounters(Attachment attachmentInfo)
139 throws BngProgrammableException {
Daniele Moro2832ec92019-12-05 22:10:48 -0800140 if (!setupBehaviour("readCounters()")) {
141 return Maps.newHashMap();
142 }
Daniele Moro6d84c182019-12-05 22:24:14 -0800143 checkAttachment(attachmentInfo);
Daniele Moro464e5ed2019-07-25 14:45:01 -0700144 return readCounters(attachmentInfo.attachmentId().id(), Set.of(BngCounterType.values()));
145 }
146
147 @Override
148 public PiCounterCellData readCounter(Attachment attachmentInfo, BngCounterType counter)
149 throws BngProgrammableException {
Daniele Moro2832ec92019-12-05 22:10:48 -0800150 if (!setupBehaviour("readCounter()")) {
151 return null;
152 }
Daniele Moro6d84c182019-12-05 22:24:14 -0800153 checkAttachment(attachmentInfo);
Daniele Moro464e5ed2019-07-25 14:45:01 -0700154 return readCounters(attachmentInfo.attachmentId().id(), Set.of(counter))
155 .getOrDefault(counter, null);
156 }
157
158 @Override
159 public void resetCounters(Attachment attachmentInfo)
160 throws BngProgrammableException {
Daniele Moro2832ec92019-12-05 22:10:48 -0800161 if (!setupBehaviour("resetCounters()")) {
162 return;
163 }
Daniele Moro6d84c182019-12-05 22:24:14 -0800164 checkAttachment(attachmentInfo);
Daniele Moro464e5ed2019-07-25 14:45:01 -0700165 resetCounters(attachmentInfo.attachmentId().id(), Set.of(BngCounterType.values()));
166 }
167
168 @Override
169 public PiCounterCellData readControlTrafficCounter()
170 throws BngProgrammableException {
Daniele Moro2832ec92019-12-05 22:10:48 -0800171 if (!setupBehaviour("readControlTrafficCounter()")) {
172 return null;
173 }
Daniele Moro464e5ed2019-07-25 14:45:01 -0700174 return readCounters(DEFAULT_CONTROL_INDEX, Set.of(BngCounterType.CONTROL_PLANE))
175 .get(BngCounterType.CONTROL_PLANE);
176 }
177
178 @Override
179 public void resetCounter(Attachment attachmentInfo, BngCounterType counter)
180 throws BngProgrammableException {
Daniele Moro2832ec92019-12-05 22:10:48 -0800181 if (!setupBehaviour("resetCounter()")) {
182 return;
183 }
Daniele Moro6d84c182019-12-05 22:24:14 -0800184 checkAttachment(attachmentInfo);
Daniele Moro464e5ed2019-07-25 14:45:01 -0700185 resetCounters(attachmentInfo.attachmentId().id(), Set.of(counter));
186 }
187
188 @Override
189 public void resetControlTrafficCounter() throws BngProgrammableException {
Daniele Moro2832ec92019-12-05 22:10:48 -0800190 if (!setupBehaviour("resetControlTrafficCounter()")) {
191 return;
192 }
Daniele Moro464e5ed2019-07-25 14:45:01 -0700193 resetCounters(DEFAULT_CONTROL_INDEX, Set.of((BngCounterType.CONTROL_PLANE)));
194 }
195
196 /**
197 * Read the specified counter at a specific index.
198 *
199 * @param index The index of the counter.
200 * @param counters The set of counters to read.
201 * @throws BngProgrammableException
202 */
203 private Map<BngCounterType, PiCounterCellData> readCounters(
204 long index,
205 Set<BngCounterType> counters) throws BngProgrammableException {
Daniele Moro464e5ed2019-07-25 14:45:01 -0700206 Map<BngCounterType, PiCounterCellData> readValues = Maps.newHashMap();
207 Set<PiCounterCellId> counterCellIds = counters.stream()
208 .filter(c -> !UNSUPPORTED_COUNTER.contains(c))
209 .map(c -> PiCounterCellId.ofIndirect(COUNTER_MAP.get(c), index))
210 .collect(Collectors.toSet());
211 // Check if there is any counter to read.
212 if (counterCellIds.size() != 0) {
213 Set<PiCounterCellHandle> counterCellHandles = counterCellIds.stream()
214 .map(cId -> PiCounterCellHandle.of(this.deviceId, cId))
215 .collect(Collectors.toSet());
216
217 // Query the device.
218 Collection<PiCounterCell> counterEntryResponse = client.read(
219 p4DeviceId, pipeconf)
220 .handles(counterCellHandles).submitSync()
221 .all(PiCounterCell.class);
222
223 if (counterEntryResponse.size() == 0) {
224 throw new BngProgrammableException(
225 String.format("Error in reading counters %s", counters.toString()));
226 }
227 readValues.putAll(counterEntryResponse.stream().collect(
228 Collectors.toMap(counterCell -> COUNTER_MAP.inverse()
229 .get(counterCell.cellId().counterId()),
230 PiCounterCell::data)));
231 }
232 return readValues;
233 }
234
235 /**
236 * Reset the specified counters at a specific index.
237 *
238 * @param index The index of the counter.
239 * @param counters The set of counters to reset.
240 */
241 private void resetCounters(long index, Set<BngCounterType> counters) throws BngProgrammableException {
Daniele Moro464e5ed2019-07-25 14:45:01 -0700242 Set<PiCounterCellId> counterCellIds = counters.stream()
243 .filter(c -> !UNSUPPORTED_COUNTER.contains(c))
244 .map(c -> PiCounterCellId.ofIndirect(COUNTER_MAP.get(c), index))
245 .collect(Collectors.toSet());
246 if (counterCellIds.isEmpty()) {
247 // No counters to reset
248 log.info("No counters to reset.");
249 return;
250 }
251 Set<PiCounterCell> counterCellData = counterCellIds.stream()
252 .map(cId -> new PiCounterCell(cId, 0, 0))
253 .collect(Collectors.toSet());
254
255 // Query the device.
256 Collection<P4RuntimeWriteClient.EntityUpdateResponse> counterEntryResponse = client.write(
257 p4DeviceId, pipeconf)
258 .modify(counterCellData).submitSync()
259 .all();
260 counterEntryResponse.stream().filter(counterEntryResp -> !counterEntryResp.isSuccess())
261 .forEach(counterEntryResp -> log.warn("A counter was not reset correctly: {}",
262 counterEntryResp.explanation()));
263 }
264
265 /**
Daniele Moro6d84c182019-12-05 22:24:14 -0800266 * Preliminary check on the submitted attachment.
Daniele Moro464e5ed2019-07-25 14:45:01 -0700267 *
Daniele Moro6d84c182019-12-05 22:24:14 -0800268 * @param attachmentInfo
269 * @throws BngProgrammableException If the attachment is not supported.
Daniele Moro464e5ed2019-07-25 14:45:01 -0700270 */
Daniele Moro6d84c182019-12-05 22:24:14 -0800271 private void checkAttachment(Attachment attachmentInfo) throws BngProgrammableException {
272 if (attachmentInfo.type() != Attachment.AttachmentType.PPPoE) {
273 throw new BngProgrammableException(
274 "Attachment {} is not a PPPoE Attachment");
275 }
276 if (attachmentInfo.attachmentId().id() >= MAX_SUPPORTED_ATTACHMENTS) {
277 throw new BngProgrammableException(
278 "Attachment ID too big. Value:" + attachmentInfo.attachmentId().id().toString() +
279 ", MAX:" + MAX_SUPPORTED_ATTACHMENTS);
Daniele Moro464e5ed2019-07-25 14:45:01 -0700280 }
281 }
282
283 /**
284 * Set the punt to CPU rules of the BNG from a specific Application ID.
285 *
286 * @param appId Application ID asking to recive BNG control plane packets.
287 */
288 private void setupPuntToCpu(ApplicationId appId) {
289 for (Criterion c : PuntCpuCriterionFactory.getAllPuntCriterion()) {
290 FlowRule flPuntCpu = buildTPppoeCpFlowRule((PiCriterion) c, appId);
291 flowRuleService.applyFlowRules(flPuntCpu);
292 }
293 }
294
295 /**
296 * Build the Flow Rule for the table t_pppoe_term_v4 of the ingress
297 * upstream.
298 *
299 * @param attachment
300 * @return
301 */
302 private FlowRule buildTPppoeTermV4FlowRule(Attachment attachment) {
303 PiCriterion criterion = PiCriterion.builder()
304 .matchExact(FabricConstants.HDR_LINE_ID,
305 attachment.attachmentId().id())
306 .matchExact(FabricConstants.HDR_IPV4_SRC,
307 attachment.ipAddress().toOctets())
308 .matchExact(FabricConstants.HDR_PPPOE_SESSION_ID,
309 attachment.pppoeSessionId())
310 // TODO: match on MAC SRC address (antispoofing)
311// .matchExact(FabricConstants.HDR_ETH_SRC,
312// attachment.macAddress.toBytes())
313 .build();
314 TrafficSelector trafficSelector = DefaultTrafficSelector.builder()
315 .matchPi(criterion)
316 .build();
317 PiAction action = PiAction.builder()
318 .withId(attachment.lineActive() ?
319 FabricConstants.FABRIC_INGRESS_BNG_INGRESS_UPSTREAM_TERM_ENABLED_V4 :
320 FabricConstants.FABRIC_INGRESS_BNG_INGRESS_UPSTREAM_TERM_DISABLED)
321 .build();
322 TrafficTreatment instTreatment = DefaultTrafficTreatment.builder()
323 .piTableAction(action)
324 .build();
325 return buildFlowRule(trafficSelector,
326 instTreatment,
327 FabricConstants.FABRIC_INGRESS_BNG_INGRESS_UPSTREAM_T_PPPOE_TERM_V4,
328 attachment.appId());
329 }
330
331 /**
332 * Build the Flow Rule for the table t_line_session_map of the ingress
333 * downstream.
334 *
335 * @param attachment
336 * @return
337 */
338 private FlowRule buildTLineSessionMapFlowRule(Attachment attachment) {
339 PiCriterion criterion = PiCriterion.builder()
340 .matchExact(FabricConstants.HDR_LINE_ID,
341 attachment.attachmentId().id())
342 .build();
343 TrafficSelector trafficSelector = DefaultTrafficSelector.builder()
344 .matchPi(criterion)
345 .build();
346 PiAction action;
347 if (attachment.lineActive()) {
348 action = PiAction.builder()
349 .withId(FabricConstants.FABRIC_INGRESS_BNG_INGRESS_DOWNSTREAM_SET_SESSION)
350 .withParameter(new PiActionParam(FabricConstants.PPPOE_SESSION_ID,
351 attachment.pppoeSessionId()))
352 .build();
353 } else {
354 action = PiAction.builder()
355 .withId(FabricConstants.FABRIC_INGRESS_BNG_INGRESS_DOWNSTREAM_DROP)
356 .build();
357 }
358 TrafficTreatment instTreatment = DefaultTrafficTreatment.builder()
359 .piTableAction(action)
360 .build();
361 return buildFlowRule(trafficSelector,
362 instTreatment,
363 FabricConstants.FABRIC_INGRESS_BNG_INGRESS_DOWNSTREAM_T_LINE_SESSION_MAP,
364 attachment.appId());
365 }
366
367 /**
368 * Build the flow rule for the table t_line_map of the BNG-U (common to both
369 * upstream and downstream).
370 *
371 * @param attachment
372 * @return
373 */
374 private FlowRule buildTLineMapFlowRule(Attachment attachment) {
375 PiCriterion criterion = PiCriterion.builder()
376 .matchExact(FabricConstants.HDR_S_TAG,
377 attachment.sTag().toShort())
378 .matchExact(FabricConstants.HDR_C_TAG,
379 attachment.cTag().toShort())
380 .build();
381 TrafficSelector trafficSelector = DefaultTrafficSelector.builder()
382 .matchPi(criterion)
383 .build();
384 PiAction action = PiAction.builder()
385 .withId(FabricConstants.FABRIC_INGRESS_BNG_INGRESS_SET_LINE)
386 .withParameter(new PiActionParam(FabricConstants.LINE_ID,
387 attachment.attachmentId().id()))
388 .build();
389 TrafficTreatment instTreatment = DefaultTrafficTreatment.builder()
390 .piTableAction(action)
391 .build();
392 return buildFlowRule(trafficSelector,
393 instTreatment,
394 FabricConstants.FABRIC_INGRESS_BNG_INGRESS_T_LINE_MAP,
395 attachment.appId());
396 }
397
398 /**
399 * Build the flow rule for the table t_pppoe_cp of the ingress upstream.
400 *
401 * @param criterion Criterion to build the flow rule.
402 * @return The built flow rule.
403 */
404 private FlowRule buildTPppoeCpFlowRule(PiCriterion criterion, ApplicationId appId) {
405 TrafficSelector trafficSelector = DefaultTrafficSelector.builder()
406 .matchPi(criterion)
407 .build();
408 TrafficTreatment instTreatment = DefaultTrafficTreatment.builder()
409 .piTableAction(PiAction.builder()
410 .withId(FabricConstants.FABRIC_INGRESS_BNG_INGRESS_UPSTREAM_PUNT_TO_CPU)
411 .build()
412 )
413 .build();
414 return buildFlowRule(trafficSelector,
415 instTreatment,
416 FabricConstants.FABRIC_INGRESS_BNG_INGRESS_UPSTREAM_T_PPPOE_CP,
417 appId);
418 }
419
420 private FlowRule buildFlowRule(TrafficSelector trafficSelector,
421 TrafficTreatment trafficTreatment,
422 TableId tableId,
423 ApplicationId appId) {
424 return DefaultFlowRule.builder()
425 .forDevice(data().deviceId())
426 .withSelector(trafficSelector)
427 .withTreatment(trafficTreatment)
428 .withPriority(DEFAULT_PRIORITY)
429 .forTable(tableId)
430 .fromApp(appId)
431 .makePermanent()
432 .build();
433 }
434}