blob: 373523874109502b83ea510d8bb1537a88b00203 [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;
73import static org.onosproject.pipelines.fabric.FabricConstants.HDR_IPV4_DST_ADDR;
74import static org.onosproject.pipelines.fabric.FabricConstants.HDR_TEID;
75import static org.onosproject.pipelines.fabric.FabricConstants.HDR_TUNNEL_IPV4_DST;
76import static org.onosproject.pipelines.fabric.FabricConstants.HDR_UE_ADDR;
77
78
79/**
80 * Implementation of a UPF programmable device behavior.
81 */
82public class FabricUpfProgrammable extends AbstractP4RuntimeHandlerBehaviour
83 implements UpfProgrammable {
84
85 private final Logger log = LoggerFactory.getLogger(getClass());
86 private static final int DEFAULT_PRIORITY = 128;
87 private static final long DEFAULT_P4_DEVICE_ID = 1;
88
89 protected FlowRuleService flowRuleService;
90 protected PacketService packetService;
91 protected FabricUpfStore fabricUpfStore;
92 protected FabricUpfTranslator upfTranslator;
93
94 private long farTableSize;
95 private long encappedPdrTableSize;
96 private long unencappedPdrTableSize;
97 private long pdrCounterSize;
98
99 private ApplicationId appId;
100
Daniele Moro8d630f12021-06-15 20:53:22 +0200101 @Override
102 protected boolean setupBehaviour(String opName) {
pierventreb91b0a82021-06-23 09:02:13 +0200103 // Already initialized.
104 if (appId != null) {
105 return true;
106 }
107
Daniele Moro8d630f12021-06-15 20:53:22 +0200108 if (!super.setupBehaviour(opName)) {
109 return false;
110 }
pierventreb91b0a82021-06-23 09:02:13 +0200111
112 if (!computeHardwareResourceSizes()) {
113 // error message will be printed by computeHardwareResourceSizes()
114 return false;
115 }
116
Daniele Moro8d630f12021-06-15 20:53:22 +0200117 flowRuleService = handler().get(FlowRuleService.class);
118 packetService = handler().get(PacketService.class);
119 fabricUpfStore = handler().get(FabricUpfStore.class);
120 upfTranslator = new FabricUpfTranslator(fabricUpfStore);
121 final CoreService coreService = handler().get(CoreService.class);
Daniele Moro668b3d92021-07-05 23:37:36 +0200122 appId = coreService.getAppId(FabricPipeconfLoader.PIPELINE_APP_NAME_UPF);
Daniele Moro8d630f12021-06-15 20:53:22 +0200123 if (appId == null) {
124 log.warn("Application ID is null. Cannot initialize behaviour.");
125 return false;
126 }
127
128 var capabilities = new FabricCapabilities(pipeconf);
129 if (!capabilities.supportUpf()) {
130 log.warn("Pipeconf {} on {} does not support UPF capabilities, " +
131 "cannot perform {}",
132 pipeconf.id(), deviceId, opName);
133 return false;
134 }
135 return true;
136 }
137
138 @Override
139 public boolean init() {
140 if (setupBehaviour("init()")) {
Daniele Moro8d630f12021-06-15 20:53:22 +0200141 log.info("UpfProgrammable initialized for appId {} and deviceId {}", appId, deviceId);
142 return true;
143 }
144 return false;
145 }
146
Daniele Moro668b3d92021-07-05 23:37:36 +0200147 @Override
148 public boolean fromThisUpf(FlowRule flowRule) {
149 return flowRule.deviceId().equals(this.deviceId) &&
150 flowRule.appId() == appId.id();
151 }
152
Daniele Moro8d630f12021-06-15 20:53:22 +0200153 /**
154 * Grab the capacities for the PDR and FAR tables from the pipeconf. Runs only once, on initialization.
155 *
156 * @return true if resource is fetched successfully, false otherwise.
157 * @throws IllegalStateException when FAR or PDR table can't be found in the pipeline model.
158 */
159 private boolean computeHardwareResourceSizes() {
160 long farTableSize = 0;
161 long encappedPdrTableSize = 0;
162 long unencappedPdrTableSize = 0;
163
164 // Get table sizes of interest
165 for (PiTableModel piTable : pipeconf.pipelineModel().tables()) {
166 if (piTable.id().equals(FABRIC_INGRESS_SPGW_UPLINK_PDRS)) {
167 encappedPdrTableSize = piTable.maxSize();
168 } else if (piTable.id().equals(FABRIC_INGRESS_SPGW_DOWNLINK_PDRS)) {
169 unencappedPdrTableSize = piTable.maxSize();
170 } else if (piTable.id().equals(FABRIC_INGRESS_SPGW_FARS)) {
171 farTableSize = piTable.maxSize();
172 }
173 }
174 if (encappedPdrTableSize == 0) {
175 throw new IllegalStateException("Unable to find uplink PDR table in pipeline model.");
176 }
177 if (unencappedPdrTableSize == 0) {
178 throw new IllegalStateException("Unable to find downlink PDR table in pipeline model.");
179 }
180 if (encappedPdrTableSize != unencappedPdrTableSize) {
181 log.warn("The uplink and downlink PDR tables don't have equal sizes! Using the minimum of the two.");
182 }
183 if (farTableSize == 0) {
184 throw new IllegalStateException("Unable to find FAR table in pipeline model.");
185 }
186 // Get counter sizes of interest
187 long ingressCounterSize = 0;
188 long egressCounterSize = 0;
189 for (PiCounterModel piCounter : pipeconf.pipelineModel().counters()) {
190 if (piCounter.id().equals(FABRIC_INGRESS_SPGW_PDR_COUNTER)) {
191 ingressCounterSize = piCounter.size();
192 } else if (piCounter.id().equals(FABRIC_EGRESS_SPGW_PDR_COUNTER)) {
193 egressCounterSize = piCounter.size();
194 }
195 }
196 if (ingressCounterSize != egressCounterSize) {
197 log.warn("PDR ingress and egress counter sizes are not equal! Using the minimum of the two.");
198 }
199 this.farTableSize = farTableSize;
200 this.encappedPdrTableSize = encappedPdrTableSize;
201 this.unencappedPdrTableSize = unencappedPdrTableSize;
202 this.pdrCounterSize = Math.min(ingressCounterSize, egressCounterSize);
203 return true;
204 }
205
206 @Override
Daniele Moro8d630f12021-06-15 20:53:22 +0200207 public void enablePscEncap(int defaultQfi) throws UpfProgrammableException {
208 throw new UpfProgrammableException("PSC encap is not supported in fabric-v1model",
209 UNSUPPORTED_OPERATION);
210 }
211
212 @Override
213 public void disablePscEncap() throws UpfProgrammableException {
214 throw new UpfProgrammableException("PSC encap is not supported in fabric-v1model",
215 UNSUPPORTED_OPERATION);
216 }
217
218 @Override
219 public void sendPacketOut(ByteBuffer data) {
220 if (!setupBehaviour("sendPacketOut()")) {
221 return;
222 }
223 final OutboundPacket pkt = new DefaultOutboundPacket(
224 deviceId,
225 // Use TABLE logical port to have pkt routed via pipeline tables.
226 DefaultTrafficTreatment.builder()
227 .setOutput(PortNumber.TABLE)
228 .build(),
229 data);
230 packetService.emit(pkt);
231 }
232
233 @Override
Daniele Moro8d630f12021-06-15 20:53:22 +0200234 public void cleanUp() {
235 if (!setupBehaviour("cleanUp()")) {
236 return;
237 }
238 log.info("Clearing all UPF-related table entries.");
Daniele Moro668b3d92021-07-05 23:37:36 +0200239 // Getting flow entries by device ID and filtering by Application ID
240 // is more efficient than getting by Application ID and filtering for a
241 // device ID.
242 List<FlowEntry> flowEntriesToRemove = StreamSupport.stream(
243 flowRuleService.getFlowEntries(deviceId).spliterator(), false)
244 .filter(flowEntry -> flowEntry.appId() == appId.id()).collect(Collectors.toList());
245 flowRuleService.removeFlowRules(flowEntriesToRemove.toArray(new FlowRule[0]));
Daniele Moro8d630f12021-06-15 20:53:22 +0200246 fabricUpfStore.reset();
247 }
248
249 @Override
250 public void clearInterfaces() {
251 if (!setupBehaviour("clearInterfaces()")) {
252 return;
253 }
254 log.info("Clearing all UPF interfaces.");
Daniele Moro668b3d92021-07-05 23:37:36 +0200255 for (FlowRule entry : flowRuleService.getFlowEntries(deviceId)) {
Daniele Moro8d630f12021-06-15 20:53:22 +0200256 if (upfTranslator.isFabricInterface(entry)) {
257 flowRuleService.removeFlowRules(entry);
258 }
259 }
260 }
261
262 @Override
263 public void clearFlows() {
264 if (!setupBehaviour("clearFlows()")) {
265 return;
266 }
267 log.info("Clearing all UE sessions.");
268 int pdrsCleared = 0;
269 int farsCleared = 0;
Daniele Moro668b3d92021-07-05 23:37:36 +0200270 for (FlowRule entry : flowRuleService.getFlowEntries(deviceId)) {
Daniele Moro8d630f12021-06-15 20:53:22 +0200271 if (upfTranslator.isFabricPdr(entry)) {
272 pdrsCleared++;
273 flowRuleService.removeFlowRules(entry);
274 } else if (upfTranslator.isFabricFar(entry)) {
275 farsCleared++;
276 flowRuleService.removeFlowRules(entry);
277 }
278 }
279 log.info("Cleared {} PDRs and {} FARS.", pdrsCleared, farsCleared);
280 }
281
282
283 @Override
284 public Collection<PdrStats> readAllCounters(long maxCounterId) {
285 if (!setupBehaviour("readAllCounters()")) {
286 return null;
287 }
288
289 long counterSize = pdrCounterSize();
290 if (maxCounterId != -1) {
291 counterSize = Math.min(maxCounterId, counterSize);
292 }
293
294 // Prepare PdrStats object builders, one for each counter ID currently in use
295 Map<Integer, PdrStats.Builder> pdrStatBuilders = Maps.newHashMap();
296 for (int cellId = 0; cellId < counterSize; cellId++) {
297 pdrStatBuilders.put(cellId, PdrStats.builder().withCellId(cellId));
298 }
299
300 // Generate the counter cell IDs.
301 Set<PiCounterId> counterIds = ImmutableSet.of(
302 FABRIC_INGRESS_SPGW_PDR_COUNTER,
303 FABRIC_EGRESS_SPGW_PDR_COUNTER
304 );
305
306 // Query the device.
307 Collection<PiCounterCell> counterEntryResponse = client.read(
308 DEFAULT_P4_DEVICE_ID, pipeconf)
309 .counterCells(counterIds)
310 .submitSync()
311 .all(PiCounterCell.class);
312
313 // Process response.
314 counterEntryResponse.forEach(counterCell -> {
315 if (counterCell.cellId().counterType() != INDIRECT) {
316 log.warn("Invalid counter data type {}, skipping", counterCell.cellId().counterType());
317 return;
318 }
319 if (!pdrStatBuilders.containsKey((int) counterCell.cellId().index())) {
320 // Most likely Up4config.maxUes() is set to a value smaller than what the switch
321 // pipeline can hold.
322 log.debug("Unrecognized index {} when reading all counters, " +
323 "that's expected if we are manually limiting maxUes", counterCell);
324 return;
325 }
326 PdrStats.Builder statsBuilder = pdrStatBuilders.get((int) counterCell.cellId().index());
327 if (counterCell.cellId().counterId().equals(FABRIC_INGRESS_SPGW_PDR_COUNTER)) {
328 statsBuilder.setIngress(counterCell.data().packets(),
329 counterCell.data().bytes());
330 } else if (counterCell.cellId().counterId().equals(FABRIC_EGRESS_SPGW_PDR_COUNTER)) {
331 statsBuilder.setEgress(counterCell.data().packets(),
332 counterCell.data().bytes());
333 } else {
334 log.warn("Unrecognized counter ID {}, skipping", counterCell);
335 }
336 });
337
338 return pdrStatBuilders
339 .values()
340 .stream()
341 .map(PdrStats.Builder::build)
342 .collect(Collectors.toList());
343 }
344
345 @Override
346 public long pdrCounterSize() {
347 if (!setupBehaviour("pdrCounterSize()")) {
348 return -1;
349 }
Daniele Moro8d630f12021-06-15 20:53:22 +0200350 return pdrCounterSize;
351 }
352
353 @Override
354 public long farTableSize() {
355 if (!setupBehaviour("farTableSize()")) {
356 return -1;
357 }
Daniele Moro8d630f12021-06-15 20:53:22 +0200358 return farTableSize;
359 }
360
361 @Override
362 public long pdrTableSize() {
363 if (!setupBehaviour("pdrTableSize()")) {
364 return -1;
365 }
Daniele Moro8d630f12021-06-15 20:53:22 +0200366 return Math.min(encappedPdrTableSize, unencappedPdrTableSize) * 2;
367 }
368
369 @Override
370 public PdrStats readCounter(int cellId) throws UpfProgrammableException {
371 if (!setupBehaviour("readCounter()")) {
372 return null;
373 }
374 if (cellId >= pdrCounterSize() || cellId < 0) {
375 throw new UpfProgrammableException("Requested PDR counter cell index is out of bounds.",
376 UpfProgrammableException.Type.COUNTER_INDEX_OUT_OF_RANGE);
377 }
378 PdrStats.Builder stats = PdrStats.builder().withCellId(cellId);
379
380 // Make list of cell handles we want to read.
381 List<PiCounterCellHandle> counterCellHandles = List.of(
382 PiCounterCellHandle.of(deviceId,
383 PiCounterCellId.ofIndirect(FABRIC_INGRESS_SPGW_PDR_COUNTER, cellId)),
384 PiCounterCellHandle.of(deviceId,
385 PiCounterCellId.ofIndirect(FABRIC_EGRESS_SPGW_PDR_COUNTER, cellId)));
386
387 // Query the device.
388 Collection<PiCounterCell> counterEntryResponse = client.read(
389 DEFAULT_P4_DEVICE_ID, pipeconf)
390 .handles(counterCellHandles).submitSync()
391 .all(PiCounterCell.class);
392
393 // Process response.
394 counterEntryResponse.forEach(counterCell -> {
395 if (counterCell.cellId().counterType() != INDIRECT) {
396 log.warn("Invalid counter data type {}, skipping", counterCell.cellId().counterType());
397 return;
398 }
399 if (cellId != counterCell.cellId().index()) {
400 log.warn("Unrecognized counter index {}, skipping", counterCell);
401 return;
402 }
403 if (counterCell.cellId().counterId().equals(FABRIC_INGRESS_SPGW_PDR_COUNTER)) {
404 stats.setIngress(counterCell.data().packets(), counterCell.data().bytes());
405 } else if (counterCell.cellId().counterId().equals(FABRIC_EGRESS_SPGW_PDR_COUNTER)) {
406 stats.setEgress(counterCell.data().packets(), counterCell.data().bytes());
407 } else {
408 log.warn("Unrecognized counter ID {}, skipping", counterCell);
409 }
410 });
411 return stats.build();
412 }
413
414
415 @Override
416 public void addPdr(PacketDetectionRule pdr) throws UpfProgrammableException {
417 if (!setupBehaviour("addPdr()")) {
418 return;
419 }
420 if (pdr.counterId() >= pdrCounterSize() || pdr.counterId() < 0) {
421 throw new UpfProgrammableException("Counter cell index referenced by PDR is out of bounds.",
422 UpfProgrammableException.Type.COUNTER_INDEX_OUT_OF_RANGE);
423 }
424 FlowRule fabricPdr = upfTranslator.pdrToFabricEntry(pdr, deviceId, appId, DEFAULT_PRIORITY);
425 log.info("Installing {}", pdr.toString());
426 flowRuleService.applyFlowRules(fabricPdr);
427 log.debug("PDR added with flowID {}", fabricPdr.id().value());
Daniele Moro8d630f12021-06-15 20:53:22 +0200428 }
429
430
431 @Override
432 public void addFar(ForwardingActionRule far) throws UpfProgrammableException {
433 if (!setupBehaviour("addFar()")) {
434 return;
435 }
Daniele Moro8d630f12021-06-15 20:53:22 +0200436 FlowRule fabricFar = upfTranslator.farToFabricEntry(far, deviceId, appId, DEFAULT_PRIORITY);
437 log.info("Installing {}", far.toString());
438 flowRuleService.applyFlowRules(fabricFar);
439 log.debug("FAR added with flowID {}", fabricFar.id().value());
Daniele Moro8d630f12021-06-15 20:53:22 +0200440 }
441
442 @Override
443 public void addInterface(UpfInterface upfInterface) throws UpfProgrammableException {
444 if (!setupBehaviour("addInterface()")) {
445 return;
446 }
447 FlowRule flowRule = upfTranslator.interfaceToFabricEntry(upfInterface, deviceId, appId, DEFAULT_PRIORITY);
448 log.info("Installing {}", upfInterface);
449 flowRuleService.applyFlowRules(flowRule);
450 log.debug("Interface added with flowID {}", flowRule.id().value());
451 // By default we enable UE-to-UE communication on the UE subnet identified by the CORE interface.
452 // TODO: allow enabling/disabling UE-to-UE via netcfg or other API.
453 log.warn("UE-to-UE traffic is not supported in fabric-v1model");
454 }
455
456 private boolean removeEntry(PiCriterion match, PiTableId tableId, boolean failSilent)
457 throws UpfProgrammableException {
Daniele Moro8d630f12021-06-15 20:53:22 +0200458 FlowRule entry = DefaultFlowRule.builder()
459 .forDevice(deviceId).fromApp(appId).makePermanent()
460 .forTable(tableId)
461 .withSelector(DefaultTrafficSelector.builder().matchPi(match).build())
462 .withPriority(DEFAULT_PRIORITY)
463 .build();
pierventre019b07292021-05-21 12:39:09 +0200464 try {
465 flowRuleService.removeFlowRules(entry);
466 // TODO in future we may need to send other notifications to the pfcp agent
467 //if (!failSilent) {
468 // throw new UpfProgrammableException("Match criterion " + match.toString() +
469 // " not found in table " + tableId.toString());
470 //}
471 return true;
472 } catch (Exception e) {
473 log.error("Exception thrown while removing flows", e);
Daniele Moro8d630f12021-06-15 20:53:22 +0200474 }
pierventre019b07292021-05-21 12:39:09 +0200475 // Assumes that the ONOS state is ok and the pfcp agent
476 // is not asking to remove wrong flows
Daniele Moro8d630f12021-06-15 20:53:22 +0200477 if (!failSilent) {
pierventre019b07292021-05-21 12:39:09 +0200478 throw new UpfProgrammableException("Unable to remove FlowRule with match criterion " + match.toString() +
479 " in table " + tableId.toString());
Daniele Moro8d630f12021-06-15 20:53:22 +0200480 }
481 return false;
482 }
483
484 @Override
485 public Collection<PacketDetectionRule> getPdrs() throws UpfProgrammableException {
486 if (!setupBehaviour("getPdrs()")) {
487 return null;
488 }
489 ArrayList<PacketDetectionRule> pdrs = new ArrayList<>();
Daniele Moro668b3d92021-07-05 23:37:36 +0200490 for (FlowRule flowRule : flowRuleService.getFlowEntries(deviceId)) {
Daniele Moro8d630f12021-06-15 20:53:22 +0200491 if (upfTranslator.isFabricPdr(flowRule)) {
492 pdrs.add(upfTranslator.fabricEntryToPdr(flowRule));
493 }
494 }
495 return pdrs;
496 }
497
498 @Override
499 public Collection<ForwardingActionRule> getFars() throws UpfProgrammableException {
500 if (!setupBehaviour("getFars()")) {
501 return null;
502 }
503 ArrayList<ForwardingActionRule> fars = new ArrayList<>();
Daniele Moro668b3d92021-07-05 23:37:36 +0200504 for (FlowRule flowRule : flowRuleService.getFlowEntries(deviceId)) {
Daniele Moro8d630f12021-06-15 20:53:22 +0200505 if (upfTranslator.isFabricFar(flowRule)) {
506 fars.add(upfTranslator.fabricEntryToFar(flowRule));
507 }
508 }
509 return fars;
510 }
511
512 @Override
513 public Collection<UpfInterface> getInterfaces() throws UpfProgrammableException {
514 if (!setupBehaviour("getInterfaces()")) {
515 return null;
516 }
517 ArrayList<UpfInterface> ifaces = new ArrayList<>();
Daniele Moro668b3d92021-07-05 23:37:36 +0200518 for (FlowRule flowRule : flowRuleService.getFlowEntries(deviceId)) {
Daniele Moro8d630f12021-06-15 20:53:22 +0200519 if (upfTranslator.isFabricInterface(flowRule)) {
520 ifaces.add(upfTranslator.fabricEntryToInterface(flowRule));
521 }
522 }
523 return ifaces;
524 }
525
526 @Override
527 public void removePdr(PacketDetectionRule pdr) throws UpfProgrammableException {
528 if (!setupBehaviour("removePdr()")) {
529 return;
530 }
531 final PiCriterion match;
532 final PiTableId tableId;
533 if (pdr.matchesEncapped()) {
534 match = PiCriterion.builder()
535 .matchExact(HDR_TEID, pdr.teid().asArray())
536 .matchExact(HDR_TUNNEL_IPV4_DST, pdr.tunnelDest().toInt())
537 .build();
538 tableId = FABRIC_INGRESS_SPGW_UPLINK_PDRS;
539 } else {
540 match = PiCriterion.builder()
541 .matchExact(HDR_UE_ADDR, pdr.ueAddress().toInt())
542 .build();
543 tableId = FABRIC_INGRESS_SPGW_DOWNLINK_PDRS;
544 }
545 log.info("Removing {}", pdr.toString());
546 removeEntry(match, tableId, false);
Daniele Moro8d630f12021-06-15 20:53:22 +0200547 }
548
549 @Override
550 public void removeFar(ForwardingActionRule far) throws UpfProgrammableException {
pierventreb91b0a82021-06-23 09:02:13 +0200551 if (!setupBehaviour("removeFar()")) {
552 return;
553 }
Daniele Moro8d630f12021-06-15 20:53:22 +0200554 log.info("Removing {}", far.toString());
555
556 PiCriterion match = PiCriterion.builder()
557 .matchExact(HDR_FAR_ID, fabricUpfStore.globalFarIdOf(far.sessionId(), far.farId()))
558 .build();
559
560 removeEntry(match, FABRIC_INGRESS_SPGW_FARS, false);
561 }
562
563 @Override
564 public void removeInterface(UpfInterface upfInterface) throws UpfProgrammableException {
565 if (!setupBehaviour("removeInterface()")) {
566 return;
567 }
568 Ip4Prefix ifacePrefix = upfInterface.getPrefix();
pierventre019b07292021-05-21 12:39:09 +0200569 // If it isn't a core interface (so it is either access/dbuf or unknown), try removing first
570 // 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 +0200571 if (!upfInterface.isCore()) {
572 PiCriterion match1 = PiCriterion.builder()
573 .matchLpm(HDR_IPV4_DST_ADDR, ifacePrefix.address().toInt(),
574 ifacePrefix.prefixLength())
575 .matchExact(HDR_GTPU_IS_VALID, 1)
576 .build();
pierventre019b07292021-05-21 12:39:09 +0200577 // removeEntry does return false only for severe issues, before we had
578 // a safe fall through. This part should not be affected since core and access
579 // flows are different in the match keys and should not result in wrong removal
580 removeEntry(match1, FABRIC_INGRESS_SPGW_INTERFACES, true);
Daniele Moro8d630f12021-06-15 20:53:22 +0200581 }
pierventre019b07292021-05-21 12:39:09 +0200582 // This additional step might be also needed in case of unknown interfaces
Daniele Moro8d630f12021-06-15 20:53:22 +0200583 PiCriterion match2 = PiCriterion.builder()
584 .matchLpm(HDR_IPV4_DST_ADDR, ifacePrefix.address().toInt(),
585 ifacePrefix.prefixLength())
586 .matchExact(HDR_GTPU_IS_VALID, 0)
587 .build();
588 removeEntry(match2, FABRIC_INGRESS_SPGW_INTERFACES, false);
589 }
590}