blob: 5848ff57285fb47f5124e09c041363a6439ac5eb [file] [log] [blame]
Daniele Moro8fd75e72019-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
52import static com.google.common.base.Preconditions.checkArgument;
53
54public class FabricBngProgrammable extends AbstractP4RuntimeHandlerBehaviour
55 implements BngProgrammable {
56
57 // Default priority of the inserted BNG rules.
58 private static final int DEFAULT_PRIORITY = 10;
59 // The index at which control plane packets are counted before the attachment is created.
60 private static final int DEFAULT_CONTROL_INDEX = 0;
61 // FIXME: retrieve this value from the table size in the PipelineModel
62 // Max number of supported attachments, useful to make sure to not read/write non-existing counters.
63 private static final int MAX_SUPPORTED_ATTACHMENTS = 1000;
64
65 private static final ImmutableBiMap<BngCounterType, PiCounterId> COUNTER_MAP =
66 ImmutableBiMap.<BngCounterType, PiCounterId>builder()
67 .put(BngCounterType.DOWNSTREAM_RX, FabricConstants.FABRIC_INGRESS_BNG_INGRESS_DOWNSTREAM_C_LINE_RX)
68 .put(BngCounterType.DOWNSTREAM_TX, FabricConstants.FABRIC_EGRESS_BNG_EGRESS_DOWNSTREAM_C_LINE_TX)
69 .put(BngCounterType.UPSTREAM_TX, FabricConstants.FABRIC_INGRESS_BNG_INGRESS_UPSTREAM_C_TERMINATED)
70 .put(BngCounterType.UPSTREAM_DROPPED, FabricConstants.FABRIC_INGRESS_BNG_INGRESS_UPSTREAM_C_DROPPED)
71 .put(BngCounterType.CONTROL_PLANE, FabricConstants.FABRIC_INGRESS_BNG_INGRESS_UPSTREAM_C_CONTROL)
72 .build();
73
74 // FIXME: add these counters to the BNG pipeline
75 private static final ImmutableSet<BngCounterType> UNSUPPORTED_COUNTER =
76 ImmutableSet.of(BngCounterType.UPSTREAM_RX, BngCounterType.DOWNSTREAM_DROPPED);
77
78 private FlowRuleService flowRuleService;
79
80 @Override
81 protected boolean setupBehaviour(String opName) {
82 if (!super.setupBehaviour(opName)) {
83 return false;
84 }
85 flowRuleService = handler().get(FlowRuleService.class);
86 return true;
87 }
88
89 @Override
90 public boolean init(ApplicationId appId) {
91 if (setupBehaviour("init()")) {
92 this.setupPuntToCpu(appId);
93 return true;
94 }
95 return false;
96 }
97
98 @Override
99 public void cleanUp(ApplicationId appId) throws BngProgrammableException {
Daniele Moro915af212019-12-05 22:10:48 -0800100 if (!setupBehaviour("cleanUp()")) {
101 return;
102 }
Daniele Moro8fd75e72019-07-25 14:45:01 -0700103 flowRuleService.removeFlowRulesById(appId);
104 this.resetControlTrafficCounter();
105 }
106
107 @Override
108 public void setupAttachment(Attachment attachmentInfo) throws BngProgrammableException {
109 checkArgument(attachmentInfo.type() == Attachment.AttachmentType.PPPoE);
Daniele Moro915af212019-12-05 22:10:48 -0800110 if (!setupBehaviour("setupAttachment()")) {
111 return;
112 }
Daniele Moro8fd75e72019-07-25 14:45:01 -0700113 List<FlowRule> lstFlowRules = Lists.newArrayList();
114 lstFlowRules.add(buildTLineMapFlowRule(attachmentInfo));
115 // If the line is not active do not generate the rule for the table
116 // t_pppoe_term_v4 since term_disabled is @defaultonly action
117 if (attachmentInfo.lineActive()) {
118 lstFlowRules.add(buildTPppoeTermV4FlowRule(attachmentInfo));
119 }
120 lstFlowRules.add(buildTLineSessionMapFlowRule(attachmentInfo));
Daniele Moroe12d0ea2019-11-27 16:14:38 -0800121
Daniele Moro8fd75e72019-07-25 14:45:01 -0700122 lstFlowRules.forEach(flowRule -> flowRuleService.applyFlowRules(flowRule));
123 }
124
125 @Override
126 public void removeAttachment(Attachment attachmentInfo) {
127 checkArgument(attachmentInfo.type() == Attachment.AttachmentType.PPPoE);
Daniele Moro915af212019-12-05 22:10:48 -0800128 if (!setupBehaviour("removeAttachment()")) {
129 return;
130 }
Daniele Moro8fd75e72019-07-25 14:45:01 -0700131 List<FlowRule> lstFlowRules = Lists.newArrayList();
132 lstFlowRules.add(buildTLineMapFlowRule(attachmentInfo));
133 lstFlowRules.add(buildTPppoeTermV4FlowRule(attachmentInfo));
134 lstFlowRules.add(buildTLineSessionMapFlowRule(attachmentInfo));
135
136 lstFlowRules.forEach(flowRule -> flowRuleService.removeFlowRules(flowRule));
137 }
138
139 @Override
140 public Map<BngCounterType, PiCounterCellData> readCounters(Attachment attachmentInfo)
141 throws BngProgrammableException {
Daniele Moro915af212019-12-05 22:10:48 -0800142 if (!setupBehaviour("readCounters()")) {
143 return Maps.newHashMap();
144 }
Daniele Moro8fd75e72019-07-25 14:45:01 -0700145 checkArgument(attachmentInfo.type() == Attachment.AttachmentType.PPPoE);
146 return readCounters(attachmentInfo.attachmentId().id(), Set.of(BngCounterType.values()));
147 }
148
149 @Override
150 public PiCounterCellData readCounter(Attachment attachmentInfo, BngCounterType counter)
151 throws BngProgrammableException {
Daniele Moro915af212019-12-05 22:10:48 -0800152 if (!setupBehaviour("readCounter()")) {
153 return null;
154 }
Daniele Moro8fd75e72019-07-25 14:45:01 -0700155 checkArgument(attachmentInfo.type() == Attachment.AttachmentType.PPPoE);
156 return readCounters(attachmentInfo.attachmentId().id(), Set.of(counter))
157 .getOrDefault(counter, null);
158 }
159
160 @Override
161 public void resetCounters(Attachment attachmentInfo)
162 throws BngProgrammableException {
Daniele Moro915af212019-12-05 22:10:48 -0800163 if (!setupBehaviour("resetCounters()")) {
164 return;
165 }
Daniele Moro8fd75e72019-07-25 14:45:01 -0700166 checkArgument(attachmentInfo.type() == Attachment.AttachmentType.PPPoE);
167 resetCounters(attachmentInfo.attachmentId().id(), Set.of(BngCounterType.values()));
168 }
169
170 @Override
171 public PiCounterCellData readControlTrafficCounter()
172 throws BngProgrammableException {
Daniele Moro915af212019-12-05 22:10:48 -0800173 if (!setupBehaviour("readControlTrafficCounter()")) {
174 return null;
175 }
Daniele Moro8fd75e72019-07-25 14:45:01 -0700176 return readCounters(DEFAULT_CONTROL_INDEX, Set.of(BngCounterType.CONTROL_PLANE))
177 .get(BngCounterType.CONTROL_PLANE);
178 }
179
180 @Override
181 public void resetCounter(Attachment attachmentInfo, BngCounterType counter)
182 throws BngProgrammableException {
Daniele Moro915af212019-12-05 22:10:48 -0800183 if (!setupBehaviour("resetCounter()")) {
184 return;
185 }
Daniele Moro8fd75e72019-07-25 14:45:01 -0700186 resetCounters(attachmentInfo.attachmentId().id(), Set.of(counter));
187 }
188
189 @Override
190 public void resetControlTrafficCounter() throws BngProgrammableException {
Daniele Moro915af212019-12-05 22:10:48 -0800191 if (!setupBehaviour("resetControlTrafficCounter()")) {
192 return;
193 }
Daniele Moro8fd75e72019-07-25 14:45:01 -0700194 resetCounters(DEFAULT_CONTROL_INDEX, Set.of((BngCounterType.CONTROL_PLANE)));
195 }
196
197 /**
198 * Read the specified counter at a specific index.
199 *
200 * @param index The index of the counter.
201 * @param counters The set of counters to read.
202 * @throws BngProgrammableException
203 */
204 private Map<BngCounterType, PiCounterCellData> readCounters(
205 long index,
206 Set<BngCounterType> counters) throws BngProgrammableException {
207 checkIndex(index);
208 Map<BngCounterType, PiCounterCellData> readValues = Maps.newHashMap();
209 Set<PiCounterCellId> counterCellIds = counters.stream()
210 .filter(c -> !UNSUPPORTED_COUNTER.contains(c))
211 .map(c -> PiCounterCellId.ofIndirect(COUNTER_MAP.get(c), index))
212 .collect(Collectors.toSet());
213 // Check if there is any counter to read.
214 if (counterCellIds.size() != 0) {
215 Set<PiCounterCellHandle> counterCellHandles = counterCellIds.stream()
216 .map(cId -> PiCounterCellHandle.of(this.deviceId, cId))
217 .collect(Collectors.toSet());
218
219 // Query the device.
220 Collection<PiCounterCell> counterEntryResponse = client.read(
221 p4DeviceId, pipeconf)
222 .handles(counterCellHandles).submitSync()
223 .all(PiCounterCell.class);
224
225 if (counterEntryResponse.size() == 0) {
226 throw new BngProgrammableException(
227 String.format("Error in reading counters %s", counters.toString()));
228 }
229 readValues.putAll(counterEntryResponse.stream().collect(
230 Collectors.toMap(counterCell -> COUNTER_MAP.inverse()
231 .get(counterCell.cellId().counterId()),
232 PiCounterCell::data)));
233 }
234 return readValues;
235 }
236
237 /**
238 * Reset the specified counters at a specific index.
239 *
240 * @param index The index of the counter.
241 * @param counters The set of counters to reset.
242 */
243 private void resetCounters(long index, Set<BngCounterType> counters) throws BngProgrammableException {
244 checkIndex(index);
245 Set<PiCounterCellId> counterCellIds = counters.stream()
246 .filter(c -> !UNSUPPORTED_COUNTER.contains(c))
247 .map(c -> PiCounterCellId.ofIndirect(COUNTER_MAP.get(c), index))
248 .collect(Collectors.toSet());
249 if (counterCellIds.isEmpty()) {
250 // No counters to reset
251 log.info("No counters to reset.");
252 return;
253 }
254 Set<PiCounterCell> counterCellData = counterCellIds.stream()
255 .map(cId -> new PiCounterCell(cId, 0, 0))
256 .collect(Collectors.toSet());
257
258 // Query the device.
259 Collection<P4RuntimeWriteClient.EntityUpdateResponse> counterEntryResponse = client.write(
260 p4DeviceId, pipeconf)
261 .modify(counterCellData).submitSync()
262 .all();
263 counterEntryResponse.stream().filter(counterEntryResp -> !counterEntryResp.isSuccess())
264 .forEach(counterEntryResp -> log.warn("A counter was not reset correctly: {}",
265 counterEntryResp.explanation()));
266 }
267
268 /**
269 * Check if the index is in the range of max number of supported
270 * attachments.
271 *
272 * @param index
273 * @throws BngProgrammableException
274 */
275 private void checkIndex(long index) throws BngProgrammableException {
276 if (index > MAX_SUPPORTED_ATTACHMENTS) {
277 throw new BngProgrammableException("Counter index too big. Value:" +
278 index + ", MAX:" +
279 MAX_SUPPORTED_ATTACHMENTS);
280 }
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}