blob: 310f28dacf1d80ca5075b3e37790bb8957bf9f0e [file] [log] [blame]
Daniele Moro8d630f12021-06-15 20:53:22 +02001/*
2 * Copyright 2021-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.upf;
18
19import com.google.common.collect.ImmutableSet;
20import com.google.common.collect.Maps;
Daniele Moro8d630f12021-06-15 20:53:22 +020021import org.onlab.packet.Ip4Prefix;
22import org.onosproject.core.ApplicationId;
23import org.onosproject.core.CoreService;
24import org.onosproject.drivers.p4runtime.AbstractP4RuntimeHandlerBehaviour;
25import org.onosproject.net.PortNumber;
26import org.onosproject.net.behaviour.upf.ForwardingActionRule;
Daniele Moro8d630f12021-06-15 20:53:22 +020027import org.onosproject.net.behaviour.upf.PacketDetectionRule;
28import org.onosproject.net.behaviour.upf.PdrStats;
29import org.onosproject.net.behaviour.upf.UpfInterface;
30import org.onosproject.net.behaviour.upf.UpfProgrammable;
31import org.onosproject.net.behaviour.upf.UpfProgrammableException;
32import org.onosproject.net.flow.DefaultFlowRule;
33import org.onosproject.net.flow.DefaultTrafficSelector;
34import org.onosproject.net.flow.DefaultTrafficTreatment;
35import org.onosproject.net.flow.FlowEntry;
36import org.onosproject.net.flow.FlowRule;
37import org.onosproject.net.flow.FlowRuleService;
38import org.onosproject.net.flow.criteria.PiCriterion;
39import org.onosproject.net.packet.DefaultOutboundPacket;
40import org.onosproject.net.packet.OutboundPacket;
41import org.onosproject.net.packet.PacketService;
42import org.onosproject.net.pi.model.PiCounterId;
43import org.onosproject.net.pi.model.PiCounterModel;
44import org.onosproject.net.pi.model.PiTableId;
45import org.onosproject.net.pi.model.PiTableModel;
46import org.onosproject.net.pi.runtime.PiCounterCell;
47import org.onosproject.net.pi.runtime.PiCounterCellHandle;
48import org.onosproject.net.pi.runtime.PiCounterCellId;
49import org.onosproject.pipelines.fabric.impl.FabricPipeconfLoader;
50import org.onosproject.pipelines.fabric.impl.behaviour.FabricCapabilities;
51import org.slf4j.Logger;
52import org.slf4j.LoggerFactory;
53
54import java.nio.ByteBuffer;
55import java.util.ArrayList;
56import java.util.Collection;
57import java.util.List;
58import java.util.Map;
59import java.util.Set;
60import java.util.stream.Collectors;
Daniele Moro668b3d92021-07-05 23:37:36 +020061import java.util.stream.StreamSupport;
Daniele Moro8d630f12021-06-15 20:53:22 +020062
63import static org.onosproject.net.behaviour.upf.UpfProgrammableException.Type.UNSUPPORTED_OPERATION;
64import static org.onosproject.net.pi.model.PiCounterType.INDIRECT;
65import static org.onosproject.pipelines.fabric.FabricConstants.FABRIC_EGRESS_SPGW_PDR_COUNTER;
66import static org.onosproject.pipelines.fabric.FabricConstants.FABRIC_INGRESS_SPGW_DOWNLINK_PDRS;
67import static org.onosproject.pipelines.fabric.FabricConstants.FABRIC_INGRESS_SPGW_FARS;
68import static org.onosproject.pipelines.fabric.FabricConstants.FABRIC_INGRESS_SPGW_INTERFACES;
69import static org.onosproject.pipelines.fabric.FabricConstants.FABRIC_INGRESS_SPGW_PDR_COUNTER;
70import static org.onosproject.pipelines.fabric.FabricConstants.FABRIC_INGRESS_SPGW_UPLINK_PDRS;
71import static org.onosproject.pipelines.fabric.FabricConstants.HDR_FAR_ID;
72import static org.onosproject.pipelines.fabric.FabricConstants.HDR_GTPU_IS_VALID;
Daniele Moro08c9e7f2021-07-28 18:53:34 +020073import static org.onosproject.pipelines.fabric.FabricConstants.HDR_HAS_QFI;
Daniele Moro8d630f12021-06-15 20:53:22 +020074import static org.onosproject.pipelines.fabric.FabricConstants.HDR_IPV4_DST_ADDR;
Daniele Moro08c9e7f2021-07-28 18:53:34 +020075import static org.onosproject.pipelines.fabric.FabricConstants.HDR_QFI;
Daniele Moro8d630f12021-06-15 20:53:22 +020076import static org.onosproject.pipelines.fabric.FabricConstants.HDR_TEID;
77import static org.onosproject.pipelines.fabric.FabricConstants.HDR_TUNNEL_IPV4_DST;
78import static org.onosproject.pipelines.fabric.FabricConstants.HDR_UE_ADDR;
Daniele Moro08c9e7f2021-07-28 18:53:34 +020079import static org.onosproject.pipelines.fabric.impl.behaviour.Constants.DEFAULT_QFI;
80import static org.onosproject.pipelines.fabric.impl.behaviour.Constants.FALSE;
81import static org.onosproject.pipelines.fabric.impl.behaviour.Constants.TRUE;
Daniele Moro8d630f12021-06-15 20:53:22 +020082
83/**
84 * Implementation of a UPF programmable device behavior.
85 */
86public class FabricUpfProgrammable extends AbstractP4RuntimeHandlerBehaviour
87 implements UpfProgrammable {
88
89 private final Logger log = LoggerFactory.getLogger(getClass());
90 private static final int DEFAULT_PRIORITY = 128;
91 private static final long DEFAULT_P4_DEVICE_ID = 1;
92
93 protected FlowRuleService flowRuleService;
94 protected PacketService packetService;
95 protected FabricUpfStore fabricUpfStore;
96 protected FabricUpfTranslator upfTranslator;
97
98 private long farTableSize;
99 private long encappedPdrTableSize;
100 private long unencappedPdrTableSize;
101 private long pdrCounterSize;
102
103 private ApplicationId appId;
104
Daniele Moro8d630f12021-06-15 20:53:22 +0200105 @Override
106 protected boolean setupBehaviour(String opName) {
pierventreb91b0a82021-06-23 09:02:13 +0200107 // Already initialized.
108 if (appId != null) {
109 return true;
110 }
111
Daniele Moro8d630f12021-06-15 20:53:22 +0200112 if (!super.setupBehaviour(opName)) {
113 return false;
114 }
pierventreb91b0a82021-06-23 09:02:13 +0200115
116 if (!computeHardwareResourceSizes()) {
117 // error message will be printed by computeHardwareResourceSizes()
118 return false;
119 }
120
Daniele Moro8d630f12021-06-15 20:53:22 +0200121 flowRuleService = handler().get(FlowRuleService.class);
122 packetService = handler().get(PacketService.class);
123 fabricUpfStore = handler().get(FabricUpfStore.class);
124 upfTranslator = new FabricUpfTranslator(fabricUpfStore);
125 final CoreService coreService = handler().get(CoreService.class);
Daniele Moro668b3d92021-07-05 23:37:36 +0200126 appId = coreService.getAppId(FabricPipeconfLoader.PIPELINE_APP_NAME_UPF);
Daniele Moro8d630f12021-06-15 20:53:22 +0200127 if (appId == null) {
128 log.warn("Application ID is null. Cannot initialize behaviour.");
129 return false;
130 }
131
132 var capabilities = new FabricCapabilities(pipeconf);
133 if (!capabilities.supportUpf()) {
134 log.warn("Pipeconf {} on {} does not support UPF capabilities, " +
135 "cannot perform {}",
136 pipeconf.id(), deviceId, opName);
137 return false;
138 }
139 return true;
140 }
141
142 @Override
143 public boolean init() {
144 if (setupBehaviour("init()")) {
Daniele Moro8d630f12021-06-15 20:53:22 +0200145 log.info("UpfProgrammable initialized for appId {} and deviceId {}", appId, deviceId);
146 return true;
147 }
148 return false;
149 }
150
Daniele Moro668b3d92021-07-05 23:37:36 +0200151 @Override
152 public boolean fromThisUpf(FlowRule flowRule) {
153 return flowRule.deviceId().equals(this.deviceId) &&
154 flowRule.appId() == appId.id();
155 }
156
Daniele Moro8d630f12021-06-15 20:53:22 +0200157 /**
158 * Grab the capacities for the PDR and FAR tables from the pipeconf. Runs only once, on initialization.
159 *
160 * @return true if resource is fetched successfully, false otherwise.
161 * @throws IllegalStateException when FAR or PDR table can't be found in the pipeline model.
162 */
163 private boolean computeHardwareResourceSizes() {
164 long farTableSize = 0;
165 long encappedPdrTableSize = 0;
166 long unencappedPdrTableSize = 0;
167
168 // Get table sizes of interest
169 for (PiTableModel piTable : pipeconf.pipelineModel().tables()) {
170 if (piTable.id().equals(FABRIC_INGRESS_SPGW_UPLINK_PDRS)) {
171 encappedPdrTableSize = piTable.maxSize();
172 } else if (piTable.id().equals(FABRIC_INGRESS_SPGW_DOWNLINK_PDRS)) {
173 unencappedPdrTableSize = piTable.maxSize();
174 } else if (piTable.id().equals(FABRIC_INGRESS_SPGW_FARS)) {
175 farTableSize = piTable.maxSize();
176 }
177 }
178 if (encappedPdrTableSize == 0) {
179 throw new IllegalStateException("Unable to find uplink PDR table in pipeline model.");
180 }
181 if (unencappedPdrTableSize == 0) {
182 throw new IllegalStateException("Unable to find downlink PDR table in pipeline model.");
183 }
184 if (encappedPdrTableSize != unencappedPdrTableSize) {
185 log.warn("The uplink and downlink PDR tables don't have equal sizes! Using the minimum of the two.");
186 }
187 if (farTableSize == 0) {
188 throw new IllegalStateException("Unable to find FAR table in pipeline model.");
189 }
190 // Get counter sizes of interest
191 long ingressCounterSize = 0;
192 long egressCounterSize = 0;
193 for (PiCounterModel piCounter : pipeconf.pipelineModel().counters()) {
194 if (piCounter.id().equals(FABRIC_INGRESS_SPGW_PDR_COUNTER)) {
195 ingressCounterSize = piCounter.size();
196 } else if (piCounter.id().equals(FABRIC_EGRESS_SPGW_PDR_COUNTER)) {
197 egressCounterSize = piCounter.size();
198 }
199 }
200 if (ingressCounterSize != egressCounterSize) {
201 log.warn("PDR ingress and egress counter sizes are not equal! Using the minimum of the two.");
202 }
203 this.farTableSize = farTableSize;
204 this.encappedPdrTableSize = encappedPdrTableSize;
205 this.unencappedPdrTableSize = unencappedPdrTableSize;
206 this.pdrCounterSize = Math.min(ingressCounterSize, egressCounterSize);
207 return true;
208 }
209
210 @Override
Daniele Moro8d630f12021-06-15 20:53:22 +0200211 public void enablePscEncap(int defaultQfi) throws UpfProgrammableException {
212 throw new UpfProgrammableException("PSC encap is not supported in fabric-v1model",
213 UNSUPPORTED_OPERATION);
214 }
215
216 @Override
217 public void disablePscEncap() throws UpfProgrammableException {
218 throw new UpfProgrammableException("PSC encap is not supported in fabric-v1model",
219 UNSUPPORTED_OPERATION);
220 }
221
222 @Override
223 public void sendPacketOut(ByteBuffer data) {
224 if (!setupBehaviour("sendPacketOut()")) {
225 return;
226 }
227 final OutboundPacket pkt = new DefaultOutboundPacket(
228 deviceId,
229 // Use TABLE logical port to have pkt routed via pipeline tables.
230 DefaultTrafficTreatment.builder()
231 .setOutput(PortNumber.TABLE)
232 .build(),
233 data);
234 packetService.emit(pkt);
235 }
236
237 @Override
Daniele Moro8d630f12021-06-15 20:53:22 +0200238 public void cleanUp() {
239 if (!setupBehaviour("cleanUp()")) {
240 return;
241 }
242 log.info("Clearing all UPF-related table entries.");
Daniele Moro668b3d92021-07-05 23:37:36 +0200243 // Getting flow entries by device ID and filtering by Application ID
244 // is more efficient than getting by Application ID and filtering for a
245 // device ID.
246 List<FlowEntry> flowEntriesToRemove = StreamSupport.stream(
247 flowRuleService.getFlowEntries(deviceId).spliterator(), false)
248 .filter(flowEntry -> flowEntry.appId() == appId.id()).collect(Collectors.toList());
249 flowRuleService.removeFlowRules(flowEntriesToRemove.toArray(new FlowRule[0]));
Daniele Moro8d630f12021-06-15 20:53:22 +0200250 fabricUpfStore.reset();
251 }
252
253 @Override
254 public void clearInterfaces() {
255 if (!setupBehaviour("clearInterfaces()")) {
256 return;
257 }
258 log.info("Clearing all UPF interfaces.");
Daniele Moro668b3d92021-07-05 23:37:36 +0200259 for (FlowRule entry : flowRuleService.getFlowEntries(deviceId)) {
Daniele Moro8d630f12021-06-15 20:53:22 +0200260 if (upfTranslator.isFabricInterface(entry)) {
261 flowRuleService.removeFlowRules(entry);
262 }
263 }
264 }
265
266 @Override
267 public void clearFlows() {
268 if (!setupBehaviour("clearFlows()")) {
269 return;
270 }
271 log.info("Clearing all UE sessions.");
272 int pdrsCleared = 0;
273 int farsCleared = 0;
Daniele Moro668b3d92021-07-05 23:37:36 +0200274 for (FlowRule entry : flowRuleService.getFlowEntries(deviceId)) {
Daniele Moro8d630f12021-06-15 20:53:22 +0200275 if (upfTranslator.isFabricPdr(entry)) {
276 pdrsCleared++;
277 flowRuleService.removeFlowRules(entry);
278 } else if (upfTranslator.isFabricFar(entry)) {
279 farsCleared++;
280 flowRuleService.removeFlowRules(entry);
281 }
282 }
283 log.info("Cleared {} PDRs and {} FARS.", pdrsCleared, farsCleared);
284 }
285
286
287 @Override
288 public Collection<PdrStats> readAllCounters(long maxCounterId) {
289 if (!setupBehaviour("readAllCounters()")) {
290 return null;
291 }
292
293 long counterSize = pdrCounterSize();
294 if (maxCounterId != -1) {
295 counterSize = Math.min(maxCounterId, counterSize);
296 }
297
298 // Prepare PdrStats object builders, one for each counter ID currently in use
299 Map<Integer, PdrStats.Builder> pdrStatBuilders = Maps.newHashMap();
300 for (int cellId = 0; cellId < counterSize; cellId++) {
301 pdrStatBuilders.put(cellId, PdrStats.builder().withCellId(cellId));
302 }
303
304 // Generate the counter cell IDs.
305 Set<PiCounterId> counterIds = ImmutableSet.of(
306 FABRIC_INGRESS_SPGW_PDR_COUNTER,
307 FABRIC_EGRESS_SPGW_PDR_COUNTER
308 );
309
310 // Query the device.
311 Collection<PiCounterCell> counterEntryResponse = client.read(
312 DEFAULT_P4_DEVICE_ID, pipeconf)
313 .counterCells(counterIds)
314 .submitSync()
315 .all(PiCounterCell.class);
316
317 // Process response.
318 counterEntryResponse.forEach(counterCell -> {
319 if (counterCell.cellId().counterType() != INDIRECT) {
320 log.warn("Invalid counter data type {}, skipping", counterCell.cellId().counterType());
321 return;
322 }
323 if (!pdrStatBuilders.containsKey((int) counterCell.cellId().index())) {
324 // Most likely Up4config.maxUes() is set to a value smaller than what the switch
325 // pipeline can hold.
326 log.debug("Unrecognized index {} when reading all counters, " +
327 "that's expected if we are manually limiting maxUes", counterCell);
328 return;
329 }
330 PdrStats.Builder statsBuilder = pdrStatBuilders.get((int) counterCell.cellId().index());
331 if (counterCell.cellId().counterId().equals(FABRIC_INGRESS_SPGW_PDR_COUNTER)) {
332 statsBuilder.setIngress(counterCell.data().packets(),
333 counterCell.data().bytes());
334 } else if (counterCell.cellId().counterId().equals(FABRIC_EGRESS_SPGW_PDR_COUNTER)) {
335 statsBuilder.setEgress(counterCell.data().packets(),
336 counterCell.data().bytes());
337 } else {
338 log.warn("Unrecognized counter ID {}, skipping", counterCell);
339 }
340 });
341
342 return pdrStatBuilders
343 .values()
344 .stream()
345 .map(PdrStats.Builder::build)
346 .collect(Collectors.toList());
347 }
348
349 @Override
350 public long pdrCounterSize() {
351 if (!setupBehaviour("pdrCounterSize()")) {
352 return -1;
353 }
Daniele Moro8d630f12021-06-15 20:53:22 +0200354 return pdrCounterSize;
355 }
356
357 @Override
358 public long farTableSize() {
359 if (!setupBehaviour("farTableSize()")) {
360 return -1;
361 }
Daniele Moro8d630f12021-06-15 20:53:22 +0200362 return farTableSize;
363 }
364
365 @Override
366 public long pdrTableSize() {
367 if (!setupBehaviour("pdrTableSize()")) {
368 return -1;
369 }
Daniele Moro8d630f12021-06-15 20:53:22 +0200370 return Math.min(encappedPdrTableSize, unencappedPdrTableSize) * 2;
371 }
372
373 @Override
374 public PdrStats readCounter(int cellId) throws UpfProgrammableException {
375 if (!setupBehaviour("readCounter()")) {
376 return null;
377 }
378 if (cellId >= pdrCounterSize() || cellId < 0) {
379 throw new UpfProgrammableException("Requested PDR counter cell index is out of bounds.",
380 UpfProgrammableException.Type.COUNTER_INDEX_OUT_OF_RANGE);
381 }
382 PdrStats.Builder stats = PdrStats.builder().withCellId(cellId);
383
384 // Make list of cell handles we want to read.
385 List<PiCounterCellHandle> counterCellHandles = List.of(
386 PiCounterCellHandle.of(deviceId,
387 PiCounterCellId.ofIndirect(FABRIC_INGRESS_SPGW_PDR_COUNTER, cellId)),
388 PiCounterCellHandle.of(deviceId,
389 PiCounterCellId.ofIndirect(FABRIC_EGRESS_SPGW_PDR_COUNTER, cellId)));
390
391 // Query the device.
392 Collection<PiCounterCell> counterEntryResponse = client.read(
393 DEFAULT_P4_DEVICE_ID, pipeconf)
394 .handles(counterCellHandles).submitSync()
395 .all(PiCounterCell.class);
396
397 // Process response.
398 counterEntryResponse.forEach(counterCell -> {
399 if (counterCell.cellId().counterType() != INDIRECT) {
400 log.warn("Invalid counter data type {}, skipping", counterCell.cellId().counterType());
401 return;
402 }
403 if (cellId != counterCell.cellId().index()) {
404 log.warn("Unrecognized counter index {}, skipping", counterCell);
405 return;
406 }
407 if (counterCell.cellId().counterId().equals(FABRIC_INGRESS_SPGW_PDR_COUNTER)) {
408 stats.setIngress(counterCell.data().packets(), counterCell.data().bytes());
409 } else if (counterCell.cellId().counterId().equals(FABRIC_EGRESS_SPGW_PDR_COUNTER)) {
410 stats.setEgress(counterCell.data().packets(), counterCell.data().bytes());
411 } else {
412 log.warn("Unrecognized counter ID {}, skipping", counterCell);
413 }
414 });
415 return stats.build();
416 }
417
418
419 @Override
420 public void addPdr(PacketDetectionRule pdr) throws UpfProgrammableException {
421 if (!setupBehaviour("addPdr()")) {
422 return;
423 }
424 if (pdr.counterId() >= pdrCounterSize() || pdr.counterId() < 0) {
425 throw new UpfProgrammableException("Counter cell index referenced by PDR is out of bounds.",
426 UpfProgrammableException.Type.COUNTER_INDEX_OUT_OF_RANGE);
427 }
428 FlowRule fabricPdr = upfTranslator.pdrToFabricEntry(pdr, deviceId, appId, DEFAULT_PRIORITY);
429 log.info("Installing {}", pdr.toString());
430 flowRuleService.applyFlowRules(fabricPdr);
431 log.debug("PDR added with flowID {}", fabricPdr.id().value());
Daniele Moro8d630f12021-06-15 20:53:22 +0200432 }
433
434
435 @Override
436 public void addFar(ForwardingActionRule far) throws UpfProgrammableException {
437 if (!setupBehaviour("addFar()")) {
438 return;
439 }
Daniele Moro8d630f12021-06-15 20:53:22 +0200440 FlowRule fabricFar = upfTranslator.farToFabricEntry(far, deviceId, appId, DEFAULT_PRIORITY);
441 log.info("Installing {}", far.toString());
442 flowRuleService.applyFlowRules(fabricFar);
443 log.debug("FAR added with flowID {}", fabricFar.id().value());
Daniele Moro8d630f12021-06-15 20:53:22 +0200444 }
445
446 @Override
447 public void addInterface(UpfInterface upfInterface) throws UpfProgrammableException {
448 if (!setupBehaviour("addInterface()")) {
449 return;
450 }
451 FlowRule flowRule = upfTranslator.interfaceToFabricEntry(upfInterface, deviceId, appId, DEFAULT_PRIORITY);
452 log.info("Installing {}", upfInterface);
453 flowRuleService.applyFlowRules(flowRule);
454 log.debug("Interface added with flowID {}", flowRule.id().value());
455 // By default we enable UE-to-UE communication on the UE subnet identified by the CORE interface.
456 // TODO: allow enabling/disabling UE-to-UE via netcfg or other API.
457 log.warn("UE-to-UE traffic is not supported in fabric-v1model");
458 }
459
460 private boolean removeEntry(PiCriterion match, PiTableId tableId, boolean failSilent)
461 throws UpfProgrammableException {
Daniele Moro8d630f12021-06-15 20:53:22 +0200462 FlowRule entry = DefaultFlowRule.builder()
463 .forDevice(deviceId).fromApp(appId).makePermanent()
464 .forTable(tableId)
465 .withSelector(DefaultTrafficSelector.builder().matchPi(match).build())
466 .withPriority(DEFAULT_PRIORITY)
467 .build();
pierventre019b07292021-05-21 12:39:09 +0200468 try {
469 flowRuleService.removeFlowRules(entry);
470 // TODO in future we may need to send other notifications to the pfcp agent
471 //if (!failSilent) {
472 // throw new UpfProgrammableException("Match criterion " + match.toString() +
473 // " not found in table " + tableId.toString());
474 //}
475 return true;
476 } catch (Exception e) {
477 log.error("Exception thrown while removing flows", e);
Daniele Moro8d630f12021-06-15 20:53:22 +0200478 }
pierventre019b07292021-05-21 12:39:09 +0200479 // Assumes that the ONOS state is ok and the pfcp agent
480 // is not asking to remove wrong flows
Daniele Moro8d630f12021-06-15 20:53:22 +0200481 if (!failSilent) {
pierventre019b07292021-05-21 12:39:09 +0200482 throw new UpfProgrammableException("Unable to remove FlowRule with match criterion " + match.toString() +
483 " in table " + tableId.toString());
Daniele Moro8d630f12021-06-15 20:53:22 +0200484 }
485 return false;
486 }
487
488 @Override
489 public Collection<PacketDetectionRule> getPdrs() throws UpfProgrammableException {
490 if (!setupBehaviour("getPdrs()")) {
491 return null;
492 }
493 ArrayList<PacketDetectionRule> pdrs = new ArrayList<>();
Daniele Moro668b3d92021-07-05 23:37:36 +0200494 for (FlowRule flowRule : flowRuleService.getFlowEntries(deviceId)) {
Daniele Moro8d630f12021-06-15 20:53:22 +0200495 if (upfTranslator.isFabricPdr(flowRule)) {
496 pdrs.add(upfTranslator.fabricEntryToPdr(flowRule));
497 }
498 }
499 return pdrs;
500 }
501
502 @Override
503 public Collection<ForwardingActionRule> getFars() throws UpfProgrammableException {
504 if (!setupBehaviour("getFars()")) {
505 return null;
506 }
507 ArrayList<ForwardingActionRule> fars = new ArrayList<>();
Daniele Moro668b3d92021-07-05 23:37:36 +0200508 for (FlowRule flowRule : flowRuleService.getFlowEntries(deviceId)) {
Daniele Moro8d630f12021-06-15 20:53:22 +0200509 if (upfTranslator.isFabricFar(flowRule)) {
510 fars.add(upfTranslator.fabricEntryToFar(flowRule));
511 }
512 }
513 return fars;
514 }
515
516 @Override
517 public Collection<UpfInterface> getInterfaces() throws UpfProgrammableException {
518 if (!setupBehaviour("getInterfaces()")) {
519 return null;
520 }
521 ArrayList<UpfInterface> ifaces = new ArrayList<>();
Daniele Moro668b3d92021-07-05 23:37:36 +0200522 for (FlowRule flowRule : flowRuleService.getFlowEntries(deviceId)) {
Daniele Moro8d630f12021-06-15 20:53:22 +0200523 if (upfTranslator.isFabricInterface(flowRule)) {
524 ifaces.add(upfTranslator.fabricEntryToInterface(flowRule));
525 }
526 }
527 return ifaces;
528 }
529
530 @Override
531 public void removePdr(PacketDetectionRule pdr) throws UpfProgrammableException {
532 if (!setupBehaviour("removePdr()")) {
533 return;
534 }
535 final PiCriterion match;
536 final PiTableId tableId;
537 if (pdr.matchesEncapped()) {
Daniele Moro08c9e7f2021-07-28 18:53:34 +0200538 PiCriterion.Builder criterionBuilder = PiCriterion.builder()
Daniele Moro8d630f12021-06-15 20:53:22 +0200539 .matchExact(HDR_TEID, pdr.teid().asArray())
Daniele Moro08c9e7f2021-07-28 18:53:34 +0200540 .matchExact(HDR_TUNNEL_IPV4_DST, pdr.tunnelDest().toInt());
541 if (pdr.matchQfi()) {
542 criterionBuilder.matchExact(HDR_HAS_QFI, TRUE);
543 criterionBuilder.matchExact(HDR_QFI, pdr.qfi());
544 } else {
545 criterionBuilder.matchExact(HDR_HAS_QFI, FALSE);
546 criterionBuilder.matchExact(HDR_QFI, DEFAULT_QFI);
547 }
548 match = criterionBuilder.build();
Daniele Moro8d630f12021-06-15 20:53:22 +0200549 tableId = FABRIC_INGRESS_SPGW_UPLINK_PDRS;
550 } else {
551 match = PiCriterion.builder()
552 .matchExact(HDR_UE_ADDR, pdr.ueAddress().toInt())
553 .build();
554 tableId = FABRIC_INGRESS_SPGW_DOWNLINK_PDRS;
555 }
556 log.info("Removing {}", pdr.toString());
557 removeEntry(match, tableId, false);
Daniele Moro8d630f12021-06-15 20:53:22 +0200558 }
559
560 @Override
561 public void removeFar(ForwardingActionRule far) throws UpfProgrammableException {
pierventreb91b0a82021-06-23 09:02:13 +0200562 if (!setupBehaviour("removeFar()")) {
563 return;
564 }
Daniele Moro8d630f12021-06-15 20:53:22 +0200565 log.info("Removing {}", far.toString());
566
567 PiCriterion match = PiCriterion.builder()
pierventre1eb98712021-07-13 18:03:22 +0200568 .matchExact(HDR_FAR_ID, fabricUpfStore.removeGlobalFarId(far.sessionId(), far.farId()))
Daniele Moro8d630f12021-06-15 20:53:22 +0200569 .build();
570
571 removeEntry(match, FABRIC_INGRESS_SPGW_FARS, false);
572 }
573
574 @Override
575 public void removeInterface(UpfInterface upfInterface) throws UpfProgrammableException {
576 if (!setupBehaviour("removeInterface()")) {
577 return;
578 }
579 Ip4Prefix ifacePrefix = upfInterface.getPrefix();
pierventre019b07292021-05-21 12:39:09 +0200580 // If it isn't a core interface (so it is either access/dbuf or unknown), try removing first
581 // access/dbuf interfaces and then fall through in the next step where we try to remove the core flow
Daniele Moro8d630f12021-06-15 20:53:22 +0200582 if (!upfInterface.isCore()) {
583 PiCriterion match1 = PiCriterion.builder()
584 .matchLpm(HDR_IPV4_DST_ADDR, ifacePrefix.address().toInt(),
585 ifacePrefix.prefixLength())
586 .matchExact(HDR_GTPU_IS_VALID, 1)
587 .build();
pierventre019b07292021-05-21 12:39:09 +0200588 // removeEntry does return false only for severe issues, before we had
589 // a safe fall through. This part should not be affected since core and access
590 // flows are different in the match keys and should not result in wrong removal
591 removeEntry(match1, FABRIC_INGRESS_SPGW_INTERFACES, true);
Daniele Moro8d630f12021-06-15 20:53:22 +0200592 }
pierventre019b07292021-05-21 12:39:09 +0200593 // This additional step might be also needed in case of unknown interfaces
Daniele Moro8d630f12021-06-15 20:53:22 +0200594 PiCriterion match2 = PiCriterion.builder()
595 .matchLpm(HDR_IPV4_DST_ADDR, ifacePrefix.address().toInt(),
596 ifacePrefix.prefixLength())
597 .matchExact(HDR_GTPU_IS_VALID, 0)
598 .build();
599 removeEntry(match2, FABRIC_INGRESS_SPGW_INTERFACES, false);
600 }
601}