blob: 77ad732876f4bddfa8bd40797836f799e500706b [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;
21import org.onlab.packet.Ip4Address;
22import org.onlab.packet.Ip4Prefix;
23import org.onosproject.core.ApplicationId;
24import org.onosproject.core.CoreService;
25import org.onosproject.drivers.p4runtime.AbstractP4RuntimeHandlerBehaviour;
26import org.onosproject.net.PortNumber;
27import org.onosproject.net.behaviour.upf.ForwardingActionRule;
28import org.onosproject.net.behaviour.upf.GtpTunnel;
29import org.onosproject.net.behaviour.upf.PacketDetectionRule;
30import org.onosproject.net.behaviour.upf.PdrStats;
31import org.onosproject.net.behaviour.upf.UpfInterface;
32import org.onosproject.net.behaviour.upf.UpfProgrammable;
33import org.onosproject.net.behaviour.upf.UpfProgrammableException;
34import org.onosproject.net.flow.DefaultFlowRule;
35import org.onosproject.net.flow.DefaultTrafficSelector;
36import org.onosproject.net.flow.DefaultTrafficTreatment;
37import org.onosproject.net.flow.FlowEntry;
38import org.onosproject.net.flow.FlowRule;
39import org.onosproject.net.flow.FlowRuleService;
40import org.onosproject.net.flow.criteria.PiCriterion;
41import org.onosproject.net.packet.DefaultOutboundPacket;
42import org.onosproject.net.packet.OutboundPacket;
43import org.onosproject.net.packet.PacketService;
44import org.onosproject.net.pi.model.PiCounterId;
45import org.onosproject.net.pi.model.PiCounterModel;
46import org.onosproject.net.pi.model.PiTableId;
47import org.onosproject.net.pi.model.PiTableModel;
48import org.onosproject.net.pi.runtime.PiCounterCell;
49import org.onosproject.net.pi.runtime.PiCounterCellHandle;
50import org.onosproject.net.pi.runtime.PiCounterCellId;
51import org.onosproject.pipelines.fabric.impl.FabricPipeconfLoader;
52import org.onosproject.pipelines.fabric.impl.behaviour.FabricCapabilities;
53import org.slf4j.Logger;
54import org.slf4j.LoggerFactory;
55
56import java.nio.ByteBuffer;
57import java.util.ArrayList;
58import java.util.Collection;
59import java.util.List;
60import java.util.Map;
61import java.util.Set;
62import java.util.stream.Collectors;
63
64import static org.onosproject.net.behaviour.upf.UpfProgrammableException.Type.UNSUPPORTED_OPERATION;
65import static org.onosproject.net.pi.model.PiCounterType.INDIRECT;
66import static org.onosproject.pipelines.fabric.FabricConstants.FABRIC_EGRESS_SPGW_PDR_COUNTER;
67import static org.onosproject.pipelines.fabric.FabricConstants.FABRIC_INGRESS_SPGW_DOWNLINK_PDRS;
68import static org.onosproject.pipelines.fabric.FabricConstants.FABRIC_INGRESS_SPGW_FARS;
69import static org.onosproject.pipelines.fabric.FabricConstants.FABRIC_INGRESS_SPGW_INTERFACES;
70import static org.onosproject.pipelines.fabric.FabricConstants.FABRIC_INGRESS_SPGW_PDR_COUNTER;
71import static org.onosproject.pipelines.fabric.FabricConstants.FABRIC_INGRESS_SPGW_UPLINK_PDRS;
72import static org.onosproject.pipelines.fabric.FabricConstants.HDR_FAR_ID;
73import static org.onosproject.pipelines.fabric.FabricConstants.HDR_GTPU_IS_VALID;
74import static org.onosproject.pipelines.fabric.FabricConstants.HDR_IPV4_DST_ADDR;
75import static org.onosproject.pipelines.fabric.FabricConstants.HDR_TEID;
76import static org.onosproject.pipelines.fabric.FabricConstants.HDR_TUNNEL_IPV4_DST;
77import static org.onosproject.pipelines.fabric.FabricConstants.HDR_UE_ADDR;
78
79
80/**
81 * Implementation of a UPF programmable device behavior.
82 */
83public class FabricUpfProgrammable extends AbstractP4RuntimeHandlerBehaviour
84 implements UpfProgrammable {
85
86 private final Logger log = LoggerFactory.getLogger(getClass());
87 private static final int DEFAULT_PRIORITY = 128;
88 private static final long DEFAULT_P4_DEVICE_ID = 1;
89
90 protected FlowRuleService flowRuleService;
91 protected PacketService packetService;
92 protected FabricUpfStore fabricUpfStore;
93 protected FabricUpfTranslator upfTranslator;
94
95 private long farTableSize;
96 private long encappedPdrTableSize;
97 private long unencappedPdrTableSize;
98 private long pdrCounterSize;
99
100 private ApplicationId appId;
101
102 // FIXME: remove, buffer drain should be triggered by Up4Service
103 private BufferDrainer bufferDrainer;
104
105 // FIXME: dbuf tunnel should be managed by Up4Service
106 // Up4Service should be responsible of setting up such tunnel, then transforming FARs for this
107 // device accordingly. When the tunnel endpoint change, it should be up to Up4Service to update
108 // the FAR on the device.
109 private GtpTunnel dbufTunnel;
110
111 @Override
112 protected boolean setupBehaviour(String opName) {
113 if (!super.setupBehaviour(opName)) {
114 return false;
115 }
116 flowRuleService = handler().get(FlowRuleService.class);
117 packetService = handler().get(PacketService.class);
118 fabricUpfStore = handler().get(FabricUpfStore.class);
119 upfTranslator = new FabricUpfTranslator(fabricUpfStore);
120 final CoreService coreService = handler().get(CoreService.class);
121 appId = coreService.getAppId(FabricPipeconfLoader.PIPELINE_APP_NAME);
122 if (appId == null) {
123 log.warn("Application ID is null. Cannot initialize behaviour.");
124 return false;
125 }
126
127 var capabilities = new FabricCapabilities(pipeconf);
128 if (!capabilities.supportUpf()) {
129 log.warn("Pipeconf {} on {} does not support UPF capabilities, " +
130 "cannot perform {}",
131 pipeconf.id(), deviceId, opName);
132 return false;
133 }
134 return true;
135 }
136
137 @Override
138 public boolean init() {
139 if (setupBehaviour("init()")) {
140 if (!computeHardwareResourceSizes()) {
141 // error message will be printed by computeHardwareResourceSizes()
142 return false;
143 }
144 log.info("UpfProgrammable initialized for appId {} and deviceId {}", appId, deviceId);
145 return true;
146 }
147 return false;
148 }
149
150 /**
151 * Grab the capacities for the PDR and FAR tables from the pipeconf. Runs only once, on initialization.
152 *
153 * @return true if resource is fetched successfully, false otherwise.
154 * @throws IllegalStateException when FAR or PDR table can't be found in the pipeline model.
155 */
156 private boolean computeHardwareResourceSizes() {
157 long farTableSize = 0;
158 long encappedPdrTableSize = 0;
159 long unencappedPdrTableSize = 0;
160
161 // Get table sizes of interest
162 for (PiTableModel piTable : pipeconf.pipelineModel().tables()) {
163 if (piTable.id().equals(FABRIC_INGRESS_SPGW_UPLINK_PDRS)) {
164 encappedPdrTableSize = piTable.maxSize();
165 } else if (piTable.id().equals(FABRIC_INGRESS_SPGW_DOWNLINK_PDRS)) {
166 unencappedPdrTableSize = piTable.maxSize();
167 } else if (piTable.id().equals(FABRIC_INGRESS_SPGW_FARS)) {
168 farTableSize = piTable.maxSize();
169 }
170 }
171 if (encappedPdrTableSize == 0) {
172 throw new IllegalStateException("Unable to find uplink PDR table in pipeline model.");
173 }
174 if (unencappedPdrTableSize == 0) {
175 throw new IllegalStateException("Unable to find downlink PDR table in pipeline model.");
176 }
177 if (encappedPdrTableSize != unencappedPdrTableSize) {
178 log.warn("The uplink and downlink PDR tables don't have equal sizes! Using the minimum of the two.");
179 }
180 if (farTableSize == 0) {
181 throw new IllegalStateException("Unable to find FAR table in pipeline model.");
182 }
183 // Get counter sizes of interest
184 long ingressCounterSize = 0;
185 long egressCounterSize = 0;
186 for (PiCounterModel piCounter : pipeconf.pipelineModel().counters()) {
187 if (piCounter.id().equals(FABRIC_INGRESS_SPGW_PDR_COUNTER)) {
188 ingressCounterSize = piCounter.size();
189 } else if (piCounter.id().equals(FABRIC_EGRESS_SPGW_PDR_COUNTER)) {
190 egressCounterSize = piCounter.size();
191 }
192 }
193 if (ingressCounterSize != egressCounterSize) {
194 log.warn("PDR ingress and egress counter sizes are not equal! Using the minimum of the two.");
195 }
196 this.farTableSize = farTableSize;
197 this.encappedPdrTableSize = encappedPdrTableSize;
198 this.unencappedPdrTableSize = unencappedPdrTableSize;
199 this.pdrCounterSize = Math.min(ingressCounterSize, egressCounterSize);
200 return true;
201 }
202
203 @Override
204 public void setBufferDrainer(BufferDrainer drainer) {
205 if (!setupBehaviour("setBufferDrainer()")) {
206 return;
207 }
208 this.bufferDrainer = drainer;
209 }
210
211 @Override
212 public void unsetBufferDrainer() {
213 if (!setupBehaviour("unsetBufferDrainer()")) {
214 return;
215 }
216 this.bufferDrainer = null;
217 }
218
219 @Override
220 public void enablePscEncap(int defaultQfi) throws UpfProgrammableException {
221 throw new UpfProgrammableException("PSC encap is not supported in fabric-v1model",
222 UNSUPPORTED_OPERATION);
223 }
224
225 @Override
226 public void disablePscEncap() throws UpfProgrammableException {
227 throw new UpfProgrammableException("PSC encap is not supported in fabric-v1model",
228 UNSUPPORTED_OPERATION);
229 }
230
231 @Override
232 public void sendPacketOut(ByteBuffer data) {
233 if (!setupBehaviour("sendPacketOut()")) {
234 return;
235 }
236 final OutboundPacket pkt = new DefaultOutboundPacket(
237 deviceId,
238 // Use TABLE logical port to have pkt routed via pipeline tables.
239 DefaultTrafficTreatment.builder()
240 .setOutput(PortNumber.TABLE)
241 .build(),
242 data);
243 packetService.emit(pkt);
244 }
245
246 @Override
247 public void setDbufTunnel(Ip4Address switchAddr, Ip4Address dbufAddr) {
248 if (!setupBehaviour("setDbufTunnel()")) {
249 return;
250 }
251 this.dbufTunnel = GtpTunnel.builder()
252 .setSrc(switchAddr)
253 .setDst(dbufAddr)
254 .setSrcPort((short) 2152)
255 .setTeid(0)
256 .build();
257 }
258
259 @Override
260 public void unsetDbufTunnel() {
261 if (!setupBehaviour("unsetDbufTunnel()")) {
262 return;
263 }
264 this.dbufTunnel = null;
265 }
266
267 /**
268 * Convert the given buffering FAR to a FAR that tunnels the packet to dbuf.
269 *
270 * @param far the FAR to convert
271 * @return the converted FAR
272 */
273 private ForwardingActionRule convertToDbufFar(ForwardingActionRule far) {
274 if (!far.buffers()) {
275 throw new IllegalArgumentException("Converting a non-buffering FAR to a dbuf FAR! This shouldn't happen.");
276 }
277 return ForwardingActionRule.builder()
278 .setFarId(far.farId())
279 .withSessionId(far.sessionId())
280 .setNotifyFlag(far.notifies())
281 .setBufferFlag(true)
282 .setTunnel(dbufTunnel)
283 .build();
284 }
285
286 @Override
287 public void cleanUp() {
288 if (!setupBehaviour("cleanUp()")) {
289 return;
290 }
291 log.info("Clearing all UPF-related table entries.");
292 flowRuleService.removeFlowRulesById(appId);
293 fabricUpfStore.reset();
294 }
295
296 @Override
297 public void clearInterfaces() {
298 if (!setupBehaviour("clearInterfaces()")) {
299 return;
300 }
301 log.info("Clearing all UPF interfaces.");
302 for (FlowRule entry : flowRuleService.getFlowEntriesById(appId)) {
303 if (upfTranslator.isFabricInterface(entry)) {
304 flowRuleService.removeFlowRules(entry);
305 }
306 }
307 }
308
309 @Override
310 public void clearFlows() {
311 if (!setupBehaviour("clearFlows()")) {
312 return;
313 }
314 log.info("Clearing all UE sessions.");
315 int pdrsCleared = 0;
316 int farsCleared = 0;
317 for (FlowRule entry : flowRuleService.getFlowEntriesById(appId)) {
318 if (upfTranslator.isFabricPdr(entry)) {
319 pdrsCleared++;
320 flowRuleService.removeFlowRules(entry);
321 } else if (upfTranslator.isFabricFar(entry)) {
322 farsCleared++;
323 flowRuleService.removeFlowRules(entry);
324 }
325 }
326 log.info("Cleared {} PDRs and {} FARS.", pdrsCleared, farsCleared);
327 }
328
329
330 @Override
331 public Collection<PdrStats> readAllCounters(long maxCounterId) {
332 if (!setupBehaviour("readAllCounters()")) {
333 return null;
334 }
335
336 long counterSize = pdrCounterSize();
337 if (maxCounterId != -1) {
338 counterSize = Math.min(maxCounterId, counterSize);
339 }
340
341 // Prepare PdrStats object builders, one for each counter ID currently in use
342 Map<Integer, PdrStats.Builder> pdrStatBuilders = Maps.newHashMap();
343 for (int cellId = 0; cellId < counterSize; cellId++) {
344 pdrStatBuilders.put(cellId, PdrStats.builder().withCellId(cellId));
345 }
346
347 // Generate the counter cell IDs.
348 Set<PiCounterId> counterIds = ImmutableSet.of(
349 FABRIC_INGRESS_SPGW_PDR_COUNTER,
350 FABRIC_EGRESS_SPGW_PDR_COUNTER
351 );
352
353 // Query the device.
354 Collection<PiCounterCell> counterEntryResponse = client.read(
355 DEFAULT_P4_DEVICE_ID, pipeconf)
356 .counterCells(counterIds)
357 .submitSync()
358 .all(PiCounterCell.class);
359
360 // Process response.
361 counterEntryResponse.forEach(counterCell -> {
362 if (counterCell.cellId().counterType() != INDIRECT) {
363 log.warn("Invalid counter data type {}, skipping", counterCell.cellId().counterType());
364 return;
365 }
366 if (!pdrStatBuilders.containsKey((int) counterCell.cellId().index())) {
367 // Most likely Up4config.maxUes() is set to a value smaller than what the switch
368 // pipeline can hold.
369 log.debug("Unrecognized index {} when reading all counters, " +
370 "that's expected if we are manually limiting maxUes", counterCell);
371 return;
372 }
373 PdrStats.Builder statsBuilder = pdrStatBuilders.get((int) counterCell.cellId().index());
374 if (counterCell.cellId().counterId().equals(FABRIC_INGRESS_SPGW_PDR_COUNTER)) {
375 statsBuilder.setIngress(counterCell.data().packets(),
376 counterCell.data().bytes());
377 } else if (counterCell.cellId().counterId().equals(FABRIC_EGRESS_SPGW_PDR_COUNTER)) {
378 statsBuilder.setEgress(counterCell.data().packets(),
379 counterCell.data().bytes());
380 } else {
381 log.warn("Unrecognized counter ID {}, skipping", counterCell);
382 }
383 });
384
385 return pdrStatBuilders
386 .values()
387 .stream()
388 .map(PdrStats.Builder::build)
389 .collect(Collectors.toList());
390 }
391
392 @Override
393 public long pdrCounterSize() {
394 if (!setupBehaviour("pdrCounterSize()")) {
395 return -1;
396 }
397 computeHardwareResourceSizes();
398 return pdrCounterSize;
399 }
400
401 @Override
402 public long farTableSize() {
403 if (!setupBehaviour("farTableSize()")) {
404 return -1;
405 }
406 computeHardwareResourceSizes();
407 return farTableSize;
408 }
409
410 @Override
411 public long pdrTableSize() {
412 if (!setupBehaviour("pdrTableSize()")) {
413 return -1;
414 }
415 computeHardwareResourceSizes();
416 return Math.min(encappedPdrTableSize, unencappedPdrTableSize) * 2;
417 }
418
419 @Override
420 public PdrStats readCounter(int cellId) throws UpfProgrammableException {
421 if (!setupBehaviour("readCounter()")) {
422 return null;
423 }
424 if (cellId >= pdrCounterSize() || cellId < 0) {
425 throw new UpfProgrammableException("Requested PDR counter cell index is out of bounds.",
426 UpfProgrammableException.Type.COUNTER_INDEX_OUT_OF_RANGE);
427 }
428 PdrStats.Builder stats = PdrStats.builder().withCellId(cellId);
429
430 // Make list of cell handles we want to read.
431 List<PiCounterCellHandle> counterCellHandles = List.of(
432 PiCounterCellHandle.of(deviceId,
433 PiCounterCellId.ofIndirect(FABRIC_INGRESS_SPGW_PDR_COUNTER, cellId)),
434 PiCounterCellHandle.of(deviceId,
435 PiCounterCellId.ofIndirect(FABRIC_EGRESS_SPGW_PDR_COUNTER, cellId)));
436
437 // Query the device.
438 Collection<PiCounterCell> counterEntryResponse = client.read(
439 DEFAULT_P4_DEVICE_ID, pipeconf)
440 .handles(counterCellHandles).submitSync()
441 .all(PiCounterCell.class);
442
443 // Process response.
444 counterEntryResponse.forEach(counterCell -> {
445 if (counterCell.cellId().counterType() != INDIRECT) {
446 log.warn("Invalid counter data type {}, skipping", counterCell.cellId().counterType());
447 return;
448 }
449 if (cellId != counterCell.cellId().index()) {
450 log.warn("Unrecognized counter index {}, skipping", counterCell);
451 return;
452 }
453 if (counterCell.cellId().counterId().equals(FABRIC_INGRESS_SPGW_PDR_COUNTER)) {
454 stats.setIngress(counterCell.data().packets(), counterCell.data().bytes());
455 } else if (counterCell.cellId().counterId().equals(FABRIC_EGRESS_SPGW_PDR_COUNTER)) {
456 stats.setEgress(counterCell.data().packets(), counterCell.data().bytes());
457 } else {
458 log.warn("Unrecognized counter ID {}, skipping", counterCell);
459 }
460 });
461 return stats.build();
462 }
463
464
465 @Override
466 public void addPdr(PacketDetectionRule pdr) throws UpfProgrammableException {
467 if (!setupBehaviour("addPdr()")) {
468 return;
469 }
470 if (pdr.counterId() >= pdrCounterSize() || pdr.counterId() < 0) {
471 throw new UpfProgrammableException("Counter cell index referenced by PDR is out of bounds.",
472 UpfProgrammableException.Type.COUNTER_INDEX_OUT_OF_RANGE);
473 }
474 FlowRule fabricPdr = upfTranslator.pdrToFabricEntry(pdr, deviceId, appId, DEFAULT_PRIORITY);
475 log.info("Installing {}", pdr.toString());
476 flowRuleService.applyFlowRules(fabricPdr);
477 log.debug("PDR added with flowID {}", fabricPdr.id().value());
478
479 // If the flow rule was applied and the PDR is downlink, add the PDR to the farID->PDR mapping
480 if (pdr.matchesUnencapped()) {
481 fabricUpfStore.learnFarIdToUeAddrs(pdr);
482 }
483 }
484
485
486 @Override
487 public void addFar(ForwardingActionRule far) throws UpfProgrammableException {
488 if (!setupBehaviour("addFar()")) {
489 return;
490 }
491 UpfRuleIdentifier ruleId = UpfRuleIdentifier.of(far.sessionId(), far.farId());
492 if (far.buffers()) {
493 // If the far has the buffer flag, modify its tunnel so it directs to dbuf
494 far = convertToDbufFar(far);
495 fabricUpfStore.learBufferingFarId(ruleId);
496 }
497 FlowRule fabricFar = upfTranslator.farToFabricEntry(far, deviceId, appId, DEFAULT_PRIORITY);
498 log.info("Installing {}", far.toString());
499 flowRuleService.applyFlowRules(fabricFar);
500 log.debug("FAR added with flowID {}", fabricFar.id().value());
501 if (!far.buffers() && fabricUpfStore.isFarIdBuffering(ruleId)) {
502 // If this FAR does not buffer but used to, then drain the buffer for every UE address
503 // that hits this FAR.
504 fabricUpfStore.forgetBufferingFarId(ruleId);
505 for (var ueAddr : fabricUpfStore.ueAddrsOfFarId(ruleId)) {
506 if (bufferDrainer == null) {
507 log.warn("Unable to drain downlink buffer for UE {}, bufferDrainer is null", ueAddr);
508 } else {
509 bufferDrainer.drain(ueAddr);
510 }
511 }
512 }
513 }
514
515 @Override
516 public void addInterface(UpfInterface upfInterface) throws UpfProgrammableException {
517 if (!setupBehaviour("addInterface()")) {
518 return;
519 }
520 FlowRule flowRule = upfTranslator.interfaceToFabricEntry(upfInterface, deviceId, appId, DEFAULT_PRIORITY);
521 log.info("Installing {}", upfInterface);
522 flowRuleService.applyFlowRules(flowRule);
523 log.debug("Interface added with flowID {}", flowRule.id().value());
524 // By default we enable UE-to-UE communication on the UE subnet identified by the CORE interface.
525 // TODO: allow enabling/disabling UE-to-UE via netcfg or other API.
526 log.warn("UE-to-UE traffic is not supported in fabric-v1model");
527 }
528
529 private boolean removeEntry(PiCriterion match, PiTableId tableId, boolean failSilent)
530 throws UpfProgrammableException {
531 if (!setupBehaviour("removeEntry()")) {
532 return false;
533 }
534 FlowRule entry = DefaultFlowRule.builder()
535 .forDevice(deviceId).fromApp(appId).makePermanent()
536 .forTable(tableId)
537 .withSelector(DefaultTrafficSelector.builder().matchPi(match).build())
538 .withPriority(DEFAULT_PRIORITY)
539 .build();
540
541 /*
542 * FIXME: Stupid stupid slow hack, needed because removeFlowRules expects FlowRule objects
543 * with correct and complete actions and parameters, but P4Runtime deletion requests
544 * will not have those.
545 */
546 for (FlowEntry installedEntry : flowRuleService.getFlowEntriesById(appId)) {
547 if (installedEntry.selector().equals(entry.selector())) {
548 log.info("Found matching entry to remove, it has FlowID {}", installedEntry.id());
549 flowRuleService.removeFlowRules(installedEntry);
550 return true;
551 }
552 }
553 if (!failSilent) {
554 throw new UpfProgrammableException("Match criterion " + match.toString() +
555 " not found in table " + tableId.toString());
556 }
557 return false;
558 }
559
560 @Override
561 public Collection<PacketDetectionRule> getPdrs() throws UpfProgrammableException {
562 if (!setupBehaviour("getPdrs()")) {
563 return null;
564 }
565 ArrayList<PacketDetectionRule> pdrs = new ArrayList<>();
566 for (FlowRule flowRule : flowRuleService.getFlowEntriesById(appId)) {
567 if (upfTranslator.isFabricPdr(flowRule)) {
568 pdrs.add(upfTranslator.fabricEntryToPdr(flowRule));
569 }
570 }
571 return pdrs;
572 }
573
574 @Override
575 public Collection<ForwardingActionRule> getFars() throws UpfProgrammableException {
576 if (!setupBehaviour("getFars()")) {
577 return null;
578 }
579 ArrayList<ForwardingActionRule> fars = new ArrayList<>();
580 for (FlowRule flowRule : flowRuleService.getFlowEntriesById(appId)) {
581 if (upfTranslator.isFabricFar(flowRule)) {
582 fars.add(upfTranslator.fabricEntryToFar(flowRule));
583 }
584 }
585 return fars;
586 }
587
588 @Override
589 public Collection<UpfInterface> getInterfaces() throws UpfProgrammableException {
590 if (!setupBehaviour("getInterfaces()")) {
591 return null;
592 }
593 ArrayList<UpfInterface> ifaces = new ArrayList<>();
594 for (FlowRule flowRule : flowRuleService.getFlowEntriesById(appId)) {
595 if (upfTranslator.isFabricInterface(flowRule)) {
596 ifaces.add(upfTranslator.fabricEntryToInterface(flowRule));
597 }
598 }
599 return ifaces;
600 }
601
602 @Override
603 public void removePdr(PacketDetectionRule pdr) throws UpfProgrammableException {
604 if (!setupBehaviour("removePdr()")) {
605 return;
606 }
607 final PiCriterion match;
608 final PiTableId tableId;
609 if (pdr.matchesEncapped()) {
610 match = PiCriterion.builder()
611 .matchExact(HDR_TEID, pdr.teid().asArray())
612 .matchExact(HDR_TUNNEL_IPV4_DST, pdr.tunnelDest().toInt())
613 .build();
614 tableId = FABRIC_INGRESS_SPGW_UPLINK_PDRS;
615 } else {
616 match = PiCriterion.builder()
617 .matchExact(HDR_UE_ADDR, pdr.ueAddress().toInt())
618 .build();
619 tableId = FABRIC_INGRESS_SPGW_DOWNLINK_PDRS;
620 }
621 log.info("Removing {}", pdr.toString());
622 removeEntry(match, tableId, false);
623
624 // Remove the PDR from the farID->PDR mapping
625 // This is an inefficient hotfix FIXME: remove UE addrs from the mapping in sublinear time
626 if (pdr.matchesUnencapped()) {
627 // Should we remove just from the map entry with key == far ID?
628 fabricUpfStore.forgetUeAddr(pdr.ueAddress());
629 }
630 }
631
632 @Override
633 public void removeFar(ForwardingActionRule far) throws UpfProgrammableException {
634 log.info("Removing {}", far.toString());
635
636 PiCriterion match = PiCriterion.builder()
637 .matchExact(HDR_FAR_ID, fabricUpfStore.globalFarIdOf(far.sessionId(), far.farId()))
638 .build();
639
640 removeEntry(match, FABRIC_INGRESS_SPGW_FARS, false);
641 }
642
643 @Override
644 public void removeInterface(UpfInterface upfInterface) throws UpfProgrammableException {
645 if (!setupBehaviour("removeInterface()")) {
646 return;
647 }
648 Ip4Prefix ifacePrefix = upfInterface.getPrefix();
649 // If it isn't a core interface (so it is either access or unknown), try removing core
650 if (!upfInterface.isCore()) {
651 PiCriterion match1 = PiCriterion.builder()
652 .matchLpm(HDR_IPV4_DST_ADDR, ifacePrefix.address().toInt(),
653 ifacePrefix.prefixLength())
654 .matchExact(HDR_GTPU_IS_VALID, 1)
655 .build();
656 if (removeEntry(match1, FABRIC_INGRESS_SPGW_INTERFACES, true)) {
657 return;
658 }
659 }
660 // If that didn't work or didn't execute, try removing access
661 PiCriterion match2 = PiCriterion.builder()
662 .matchLpm(HDR_IPV4_DST_ADDR, ifacePrefix.address().toInt(),
663 ifacePrefix.prefixLength())
664 .matchExact(HDR_GTPU_IS_VALID, 0)
665 .build();
666 removeEntry(match2, FABRIC_INGRESS_SPGW_INTERFACES, false);
667 }
668}