blob: 9b566c0eb0cc87f08a7df91a80ca0a678b8466f2 [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
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 {
100 flowRuleService.removeFlowRulesById(appId);
101 this.resetControlTrafficCounter();
102 }
103
104 @Override
105 public void setupAttachment(Attachment attachmentInfo) throws BngProgrammableException {
106 checkArgument(attachmentInfo.type() == Attachment.AttachmentType.PPPoE);
107 List<FlowRule> lstFlowRules = Lists.newArrayList();
108 lstFlowRules.add(buildTLineMapFlowRule(attachmentInfo));
109 // If the line is not active do not generate the rule for the table
110 // t_pppoe_term_v4 since term_disabled is @defaultonly action
111 if (attachmentInfo.lineActive()) {
112 lstFlowRules.add(buildTPppoeTermV4FlowRule(attachmentInfo));
113 }
114 lstFlowRules.add(buildTLineSessionMapFlowRule(attachmentInfo));
115 // Clean-up attachment related counters
116 this.resetCounters(attachmentInfo);
117 lstFlowRules.forEach(flowRule -> flowRuleService.applyFlowRules(flowRule));
118 }
119
120 @Override
121 public void removeAttachment(Attachment attachmentInfo) {
122 checkArgument(attachmentInfo.type() == Attachment.AttachmentType.PPPoE);
123 List<FlowRule> lstFlowRules = Lists.newArrayList();
124 lstFlowRules.add(buildTLineMapFlowRule(attachmentInfo));
125 lstFlowRules.add(buildTPppoeTermV4FlowRule(attachmentInfo));
126 lstFlowRules.add(buildTLineSessionMapFlowRule(attachmentInfo));
127
128 lstFlowRules.forEach(flowRule -> flowRuleService.removeFlowRules(flowRule));
129 }
130
131 @Override
132 public Map<BngCounterType, PiCounterCellData> readCounters(Attachment attachmentInfo)
133 throws BngProgrammableException {
134 checkArgument(attachmentInfo.type() == Attachment.AttachmentType.PPPoE);
135 return readCounters(attachmentInfo.attachmentId().id(), Set.of(BngCounterType.values()));
136 }
137
138 @Override
139 public PiCounterCellData readCounter(Attachment attachmentInfo, BngCounterType counter)
140 throws BngProgrammableException {
141 checkArgument(attachmentInfo.type() == Attachment.AttachmentType.PPPoE);
142 return readCounters(attachmentInfo.attachmentId().id(), Set.of(counter))
143 .getOrDefault(counter, null);
144 }
145
146 @Override
147 public void resetCounters(Attachment attachmentInfo)
148 throws BngProgrammableException {
149 checkArgument(attachmentInfo.type() == Attachment.AttachmentType.PPPoE);
150 resetCounters(attachmentInfo.attachmentId().id(), Set.of(BngCounterType.values()));
151 }
152
153 @Override
154 public PiCounterCellData readControlTrafficCounter()
155 throws BngProgrammableException {
156 return readCounters(DEFAULT_CONTROL_INDEX, Set.of(BngCounterType.CONTROL_PLANE))
157 .get(BngCounterType.CONTROL_PLANE);
158 }
159
160 @Override
161 public void resetCounter(Attachment attachmentInfo, BngCounterType counter)
162 throws BngProgrammableException {
163 resetCounters(attachmentInfo.attachmentId().id(), Set.of(counter));
164 }
165
166 @Override
167 public void resetControlTrafficCounter() throws BngProgrammableException {
168 resetCounters(DEFAULT_CONTROL_INDEX, Set.of((BngCounterType.CONTROL_PLANE)));
169 }
170
171 /**
172 * Read the specified counter at a specific index.
173 *
174 * @param index The index of the counter.
175 * @param counters The set of counters to read.
176 * @throws BngProgrammableException
177 */
178 private Map<BngCounterType, PiCounterCellData> readCounters(
179 long index,
180 Set<BngCounterType> counters) throws BngProgrammableException {
181 checkIndex(index);
182 Map<BngCounterType, PiCounterCellData> readValues = Maps.newHashMap();
183 Set<PiCounterCellId> counterCellIds = counters.stream()
184 .filter(c -> !UNSUPPORTED_COUNTER.contains(c))
185 .map(c -> PiCounterCellId.ofIndirect(COUNTER_MAP.get(c), index))
186 .collect(Collectors.toSet());
187 // Check if there is any counter to read.
188 if (counterCellIds.size() != 0) {
189 Set<PiCounterCellHandle> counterCellHandles = counterCellIds.stream()
190 .map(cId -> PiCounterCellHandle.of(this.deviceId, cId))
191 .collect(Collectors.toSet());
192
193 // Query the device.
194 Collection<PiCounterCell> counterEntryResponse = client.read(
195 p4DeviceId, pipeconf)
196 .handles(counterCellHandles).submitSync()
197 .all(PiCounterCell.class);
198
199 if (counterEntryResponse.size() == 0) {
200 throw new BngProgrammableException(
201 String.format("Error in reading counters %s", counters.toString()));
202 }
203 readValues.putAll(counterEntryResponse.stream().collect(
204 Collectors.toMap(counterCell -> COUNTER_MAP.inverse()
205 .get(counterCell.cellId().counterId()),
206 PiCounterCell::data)));
207 }
208 return readValues;
209 }
210
211 /**
212 * Reset the specified counters at a specific index.
213 *
214 * @param index The index of the counter.
215 * @param counters The set of counters to reset.
216 */
217 private void resetCounters(long index, Set<BngCounterType> counters) throws BngProgrammableException {
218 checkIndex(index);
219 Set<PiCounterCellId> counterCellIds = counters.stream()
220 .filter(c -> !UNSUPPORTED_COUNTER.contains(c))
221 .map(c -> PiCounterCellId.ofIndirect(COUNTER_MAP.get(c), index))
222 .collect(Collectors.toSet());
223 if (counterCellIds.isEmpty()) {
224 // No counters to reset
225 log.info("No counters to reset.");
226 return;
227 }
228 Set<PiCounterCell> counterCellData = counterCellIds.stream()
229 .map(cId -> new PiCounterCell(cId, 0, 0))
230 .collect(Collectors.toSet());
231
232 // Query the device.
233 Collection<P4RuntimeWriteClient.EntityUpdateResponse> counterEntryResponse = client.write(
234 p4DeviceId, pipeconf)
235 .modify(counterCellData).submitSync()
236 .all();
237 counterEntryResponse.stream().filter(counterEntryResp -> !counterEntryResp.isSuccess())
238 .forEach(counterEntryResp -> log.warn("A counter was not reset correctly: {}",
239 counterEntryResp.explanation()));
240 }
241
242 /**
243 * Check if the index is in the range of max number of supported
244 * attachments.
245 *
246 * @param index
247 * @throws BngProgrammableException
248 */
249 private void checkIndex(long index) throws BngProgrammableException {
250 if (index > MAX_SUPPORTED_ATTACHMENTS) {
251 throw new BngProgrammableException("Counter index too big. Value:" +
252 index + ", MAX:" +
253 MAX_SUPPORTED_ATTACHMENTS);
254 }
255 }
256
257 /**
258 * Set the punt to CPU rules of the BNG from a specific Application ID.
259 *
260 * @param appId Application ID asking to recive BNG control plane packets.
261 */
262 private void setupPuntToCpu(ApplicationId appId) {
263 for (Criterion c : PuntCpuCriterionFactory.getAllPuntCriterion()) {
264 FlowRule flPuntCpu = buildTPppoeCpFlowRule((PiCriterion) c, appId);
265 flowRuleService.applyFlowRules(flPuntCpu);
266 }
267 }
268
269 /**
270 * Build the Flow Rule for the table t_pppoe_term_v4 of the ingress
271 * upstream.
272 *
273 * @param attachment
274 * @return
275 */
276 private FlowRule buildTPppoeTermV4FlowRule(Attachment attachment) {
277 PiCriterion criterion = PiCriterion.builder()
278 .matchExact(FabricConstants.HDR_LINE_ID,
279 attachment.attachmentId().id())
280 .matchExact(FabricConstants.HDR_IPV4_SRC,
281 attachment.ipAddress().toOctets())
282 .matchExact(FabricConstants.HDR_PPPOE_SESSION_ID,
283 attachment.pppoeSessionId())
284 // TODO: match on MAC SRC address (antispoofing)
285// .matchExact(FabricConstants.HDR_ETH_SRC,
286// attachment.macAddress.toBytes())
287 .build();
288 TrafficSelector trafficSelector = DefaultTrafficSelector.builder()
289 .matchPi(criterion)
290 .build();
291 PiAction action = PiAction.builder()
292 .withId(attachment.lineActive() ?
293 FabricConstants.FABRIC_INGRESS_BNG_INGRESS_UPSTREAM_TERM_ENABLED_V4 :
294 FabricConstants.FABRIC_INGRESS_BNG_INGRESS_UPSTREAM_TERM_DISABLED)
295 .build();
296 TrafficTreatment instTreatment = DefaultTrafficTreatment.builder()
297 .piTableAction(action)
298 .build();
299 return buildFlowRule(trafficSelector,
300 instTreatment,
301 FabricConstants.FABRIC_INGRESS_BNG_INGRESS_UPSTREAM_T_PPPOE_TERM_V4,
302 attachment.appId());
303 }
304
305 /**
306 * Build the Flow Rule for the table t_line_session_map of the ingress
307 * downstream.
308 *
309 * @param attachment
310 * @return
311 */
312 private FlowRule buildTLineSessionMapFlowRule(Attachment attachment) {
313 PiCriterion criterion = PiCriterion.builder()
314 .matchExact(FabricConstants.HDR_LINE_ID,
315 attachment.attachmentId().id())
316 .build();
317 TrafficSelector trafficSelector = DefaultTrafficSelector.builder()
318 .matchPi(criterion)
319 .build();
320 PiAction action;
321 if (attachment.lineActive()) {
322 action = PiAction.builder()
323 .withId(FabricConstants.FABRIC_INGRESS_BNG_INGRESS_DOWNSTREAM_SET_SESSION)
324 .withParameter(new PiActionParam(FabricConstants.PPPOE_SESSION_ID,
325 attachment.pppoeSessionId()))
326 .build();
327 } else {
328 action = PiAction.builder()
329 .withId(FabricConstants.FABRIC_INGRESS_BNG_INGRESS_DOWNSTREAM_DROP)
330 .build();
331 }
332 TrafficTreatment instTreatment = DefaultTrafficTreatment.builder()
333 .piTableAction(action)
334 .build();
335 return buildFlowRule(trafficSelector,
336 instTreatment,
337 FabricConstants.FABRIC_INGRESS_BNG_INGRESS_DOWNSTREAM_T_LINE_SESSION_MAP,
338 attachment.appId());
339 }
340
341 /**
342 * Build the flow rule for the table t_line_map of the BNG-U (common to both
343 * upstream and downstream).
344 *
345 * @param attachment
346 * @return
347 */
348 private FlowRule buildTLineMapFlowRule(Attachment attachment) {
349 PiCriterion criterion = PiCriterion.builder()
350 .matchExact(FabricConstants.HDR_S_TAG,
351 attachment.sTag().toShort())
352 .matchExact(FabricConstants.HDR_C_TAG,
353 attachment.cTag().toShort())
354 .build();
355 TrafficSelector trafficSelector = DefaultTrafficSelector.builder()
356 .matchPi(criterion)
357 .build();
358 PiAction action = PiAction.builder()
359 .withId(FabricConstants.FABRIC_INGRESS_BNG_INGRESS_SET_LINE)
360 .withParameter(new PiActionParam(FabricConstants.LINE_ID,
361 attachment.attachmentId().id()))
362 .build();
363 TrafficTreatment instTreatment = DefaultTrafficTreatment.builder()
364 .piTableAction(action)
365 .build();
366 return buildFlowRule(trafficSelector,
367 instTreatment,
368 FabricConstants.FABRIC_INGRESS_BNG_INGRESS_T_LINE_MAP,
369 attachment.appId());
370 }
371
372 /**
373 * Build the flow rule for the table t_pppoe_cp of the ingress upstream.
374 *
375 * @param criterion Criterion to build the flow rule.
376 * @return The built flow rule.
377 */
378 private FlowRule buildTPppoeCpFlowRule(PiCriterion criterion, ApplicationId appId) {
379 TrafficSelector trafficSelector = DefaultTrafficSelector.builder()
380 .matchPi(criterion)
381 .build();
382 TrafficTreatment instTreatment = DefaultTrafficTreatment.builder()
383 .piTableAction(PiAction.builder()
384 .withId(FabricConstants.FABRIC_INGRESS_BNG_INGRESS_UPSTREAM_PUNT_TO_CPU)
385 .build()
386 )
387 .build();
388 return buildFlowRule(trafficSelector,
389 instTreatment,
390 FabricConstants.FABRIC_INGRESS_BNG_INGRESS_UPSTREAM_T_PPPOE_CP,
391 appId);
392 }
393
394 private FlowRule buildFlowRule(TrafficSelector trafficSelector,
395 TrafficTreatment trafficTreatment,
396 TableId tableId,
397 ApplicationId appId) {
398 return DefaultFlowRule.builder()
399 .forDevice(data().deviceId())
400 .withSelector(trafficSelector)
401 .withTreatment(trafficTreatment)
402 .withPriority(DEFAULT_PRIORITY)
403 .forTable(tableId)
404 .fromApp(appId)
405 .makePermanent()
406 .build();
407 }
408}