blob: 3c814824d2df77802eed86a89767d2f74b468f47 [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;
61
62import static org.onosproject.net.behaviour.upf.UpfProgrammableException.Type.UNSUPPORTED_OPERATION;
63import static org.onosproject.net.pi.model.PiCounterType.INDIRECT;
64import static org.onosproject.pipelines.fabric.FabricConstants.FABRIC_EGRESS_SPGW_PDR_COUNTER;
65import static org.onosproject.pipelines.fabric.FabricConstants.FABRIC_INGRESS_SPGW_DOWNLINK_PDRS;
66import static org.onosproject.pipelines.fabric.FabricConstants.FABRIC_INGRESS_SPGW_FARS;
67import static org.onosproject.pipelines.fabric.FabricConstants.FABRIC_INGRESS_SPGW_INTERFACES;
68import static org.onosproject.pipelines.fabric.FabricConstants.FABRIC_INGRESS_SPGW_PDR_COUNTER;
69import static org.onosproject.pipelines.fabric.FabricConstants.FABRIC_INGRESS_SPGW_UPLINK_PDRS;
70import static org.onosproject.pipelines.fabric.FabricConstants.HDR_FAR_ID;
71import static org.onosproject.pipelines.fabric.FabricConstants.HDR_GTPU_IS_VALID;
72import static org.onosproject.pipelines.fabric.FabricConstants.HDR_IPV4_DST_ADDR;
73import static org.onosproject.pipelines.fabric.FabricConstants.HDR_TEID;
74import static org.onosproject.pipelines.fabric.FabricConstants.HDR_TUNNEL_IPV4_DST;
75import static org.onosproject.pipelines.fabric.FabricConstants.HDR_UE_ADDR;
76
77
78/**
79 * Implementation of a UPF programmable device behavior.
80 */
81public class FabricUpfProgrammable extends AbstractP4RuntimeHandlerBehaviour
82 implements UpfProgrammable {
83
84 private final Logger log = LoggerFactory.getLogger(getClass());
85 private static final int DEFAULT_PRIORITY = 128;
86 private static final long DEFAULT_P4_DEVICE_ID = 1;
87
88 protected FlowRuleService flowRuleService;
89 protected PacketService packetService;
90 protected FabricUpfStore fabricUpfStore;
91 protected FabricUpfTranslator upfTranslator;
92
93 private long farTableSize;
94 private long encappedPdrTableSize;
95 private long unencappedPdrTableSize;
96 private long pdrCounterSize;
97
98 private ApplicationId appId;
99
Daniele Moro8d630f12021-06-15 20:53:22 +0200100 @Override
101 protected boolean setupBehaviour(String opName) {
pierventreb91b0a82021-06-23 09:02:13 +0200102 // Already initialized.
103 if (appId != null) {
104 return true;
105 }
106
Daniele Moro8d630f12021-06-15 20:53:22 +0200107 if (!super.setupBehaviour(opName)) {
108 return false;
109 }
pierventreb91b0a82021-06-23 09:02:13 +0200110
111 if (!computeHardwareResourceSizes()) {
112 // error message will be printed by computeHardwareResourceSizes()
113 return false;
114 }
115
Daniele Moro8d630f12021-06-15 20:53:22 +0200116 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()")) {
Daniele Moro8d630f12021-06-15 20:53:22 +0200140 log.info("UpfProgrammable initialized for appId {} and deviceId {}", appId, deviceId);
141 return true;
142 }
143 return false;
144 }
145
146 /**
147 * Grab the capacities for the PDR and FAR tables from the pipeconf. Runs only once, on initialization.
148 *
149 * @return true if resource is fetched successfully, false otherwise.
150 * @throws IllegalStateException when FAR or PDR table can't be found in the pipeline model.
151 */
152 private boolean computeHardwareResourceSizes() {
153 long farTableSize = 0;
154 long encappedPdrTableSize = 0;
155 long unencappedPdrTableSize = 0;
156
157 // Get table sizes of interest
158 for (PiTableModel piTable : pipeconf.pipelineModel().tables()) {
159 if (piTable.id().equals(FABRIC_INGRESS_SPGW_UPLINK_PDRS)) {
160 encappedPdrTableSize = piTable.maxSize();
161 } else if (piTable.id().equals(FABRIC_INGRESS_SPGW_DOWNLINK_PDRS)) {
162 unencappedPdrTableSize = piTable.maxSize();
163 } else if (piTable.id().equals(FABRIC_INGRESS_SPGW_FARS)) {
164 farTableSize = piTable.maxSize();
165 }
166 }
167 if (encappedPdrTableSize == 0) {
168 throw new IllegalStateException("Unable to find uplink PDR table in pipeline model.");
169 }
170 if (unencappedPdrTableSize == 0) {
171 throw new IllegalStateException("Unable to find downlink PDR table in pipeline model.");
172 }
173 if (encappedPdrTableSize != unencappedPdrTableSize) {
174 log.warn("The uplink and downlink PDR tables don't have equal sizes! Using the minimum of the two.");
175 }
176 if (farTableSize == 0) {
177 throw new IllegalStateException("Unable to find FAR table in pipeline model.");
178 }
179 // Get counter sizes of interest
180 long ingressCounterSize = 0;
181 long egressCounterSize = 0;
182 for (PiCounterModel piCounter : pipeconf.pipelineModel().counters()) {
183 if (piCounter.id().equals(FABRIC_INGRESS_SPGW_PDR_COUNTER)) {
184 ingressCounterSize = piCounter.size();
185 } else if (piCounter.id().equals(FABRIC_EGRESS_SPGW_PDR_COUNTER)) {
186 egressCounterSize = piCounter.size();
187 }
188 }
189 if (ingressCounterSize != egressCounterSize) {
190 log.warn("PDR ingress and egress counter sizes are not equal! Using the minimum of the two.");
191 }
192 this.farTableSize = farTableSize;
193 this.encappedPdrTableSize = encappedPdrTableSize;
194 this.unencappedPdrTableSize = unencappedPdrTableSize;
195 this.pdrCounterSize = Math.min(ingressCounterSize, egressCounterSize);
196 return true;
197 }
198
199 @Override
Daniele Moro8d630f12021-06-15 20:53:22 +0200200 public void enablePscEncap(int defaultQfi) throws UpfProgrammableException {
201 throw new UpfProgrammableException("PSC encap is not supported in fabric-v1model",
202 UNSUPPORTED_OPERATION);
203 }
204
205 @Override
206 public void disablePscEncap() throws UpfProgrammableException {
207 throw new UpfProgrammableException("PSC encap is not supported in fabric-v1model",
208 UNSUPPORTED_OPERATION);
209 }
210
211 @Override
212 public void sendPacketOut(ByteBuffer data) {
213 if (!setupBehaviour("sendPacketOut()")) {
214 return;
215 }
216 final OutboundPacket pkt = new DefaultOutboundPacket(
217 deviceId,
218 // Use TABLE logical port to have pkt routed via pipeline tables.
219 DefaultTrafficTreatment.builder()
220 .setOutput(PortNumber.TABLE)
221 .build(),
222 data);
223 packetService.emit(pkt);
224 }
225
226 @Override
Daniele Moro8d630f12021-06-15 20:53:22 +0200227 public void cleanUp() {
228 if (!setupBehaviour("cleanUp()")) {
229 return;
230 }
231 log.info("Clearing all UPF-related table entries.");
232 flowRuleService.removeFlowRulesById(appId);
233 fabricUpfStore.reset();
234 }
235
236 @Override
237 public void clearInterfaces() {
238 if (!setupBehaviour("clearInterfaces()")) {
239 return;
240 }
241 log.info("Clearing all UPF interfaces.");
242 for (FlowRule entry : flowRuleService.getFlowEntriesById(appId)) {
243 if (upfTranslator.isFabricInterface(entry)) {
244 flowRuleService.removeFlowRules(entry);
245 }
246 }
247 }
248
249 @Override
250 public void clearFlows() {
251 if (!setupBehaviour("clearFlows()")) {
252 return;
253 }
254 log.info("Clearing all UE sessions.");
255 int pdrsCleared = 0;
256 int farsCleared = 0;
257 for (FlowRule entry : flowRuleService.getFlowEntriesById(appId)) {
258 if (upfTranslator.isFabricPdr(entry)) {
259 pdrsCleared++;
260 flowRuleService.removeFlowRules(entry);
261 } else if (upfTranslator.isFabricFar(entry)) {
262 farsCleared++;
263 flowRuleService.removeFlowRules(entry);
264 }
265 }
266 log.info("Cleared {} PDRs and {} FARS.", pdrsCleared, farsCleared);
267 }
268
269
270 @Override
271 public Collection<PdrStats> readAllCounters(long maxCounterId) {
272 if (!setupBehaviour("readAllCounters()")) {
273 return null;
274 }
275
276 long counterSize = pdrCounterSize();
277 if (maxCounterId != -1) {
278 counterSize = Math.min(maxCounterId, counterSize);
279 }
280
281 // Prepare PdrStats object builders, one for each counter ID currently in use
282 Map<Integer, PdrStats.Builder> pdrStatBuilders = Maps.newHashMap();
283 for (int cellId = 0; cellId < counterSize; cellId++) {
284 pdrStatBuilders.put(cellId, PdrStats.builder().withCellId(cellId));
285 }
286
287 // Generate the counter cell IDs.
288 Set<PiCounterId> counterIds = ImmutableSet.of(
289 FABRIC_INGRESS_SPGW_PDR_COUNTER,
290 FABRIC_EGRESS_SPGW_PDR_COUNTER
291 );
292
293 // Query the device.
294 Collection<PiCounterCell> counterEntryResponse = client.read(
295 DEFAULT_P4_DEVICE_ID, pipeconf)
296 .counterCells(counterIds)
297 .submitSync()
298 .all(PiCounterCell.class);
299
300 // Process response.
301 counterEntryResponse.forEach(counterCell -> {
302 if (counterCell.cellId().counterType() != INDIRECT) {
303 log.warn("Invalid counter data type {}, skipping", counterCell.cellId().counterType());
304 return;
305 }
306 if (!pdrStatBuilders.containsKey((int) counterCell.cellId().index())) {
307 // Most likely Up4config.maxUes() is set to a value smaller than what the switch
308 // pipeline can hold.
309 log.debug("Unrecognized index {} when reading all counters, " +
310 "that's expected if we are manually limiting maxUes", counterCell);
311 return;
312 }
313 PdrStats.Builder statsBuilder = pdrStatBuilders.get((int) counterCell.cellId().index());
314 if (counterCell.cellId().counterId().equals(FABRIC_INGRESS_SPGW_PDR_COUNTER)) {
315 statsBuilder.setIngress(counterCell.data().packets(),
316 counterCell.data().bytes());
317 } else if (counterCell.cellId().counterId().equals(FABRIC_EGRESS_SPGW_PDR_COUNTER)) {
318 statsBuilder.setEgress(counterCell.data().packets(),
319 counterCell.data().bytes());
320 } else {
321 log.warn("Unrecognized counter ID {}, skipping", counterCell);
322 }
323 });
324
325 return pdrStatBuilders
326 .values()
327 .stream()
328 .map(PdrStats.Builder::build)
329 .collect(Collectors.toList());
330 }
331
332 @Override
333 public long pdrCounterSize() {
334 if (!setupBehaviour("pdrCounterSize()")) {
335 return -1;
336 }
Daniele Moro8d630f12021-06-15 20:53:22 +0200337 return pdrCounterSize;
338 }
339
340 @Override
341 public long farTableSize() {
342 if (!setupBehaviour("farTableSize()")) {
343 return -1;
344 }
Daniele Moro8d630f12021-06-15 20:53:22 +0200345 return farTableSize;
346 }
347
348 @Override
349 public long pdrTableSize() {
350 if (!setupBehaviour("pdrTableSize()")) {
351 return -1;
352 }
Daniele Moro8d630f12021-06-15 20:53:22 +0200353 return Math.min(encappedPdrTableSize, unencappedPdrTableSize) * 2;
354 }
355
356 @Override
357 public PdrStats readCounter(int cellId) throws UpfProgrammableException {
358 if (!setupBehaviour("readCounter()")) {
359 return null;
360 }
361 if (cellId >= pdrCounterSize() || cellId < 0) {
362 throw new UpfProgrammableException("Requested PDR counter cell index is out of bounds.",
363 UpfProgrammableException.Type.COUNTER_INDEX_OUT_OF_RANGE);
364 }
365 PdrStats.Builder stats = PdrStats.builder().withCellId(cellId);
366
367 // Make list of cell handles we want to read.
368 List<PiCounterCellHandle> counterCellHandles = List.of(
369 PiCounterCellHandle.of(deviceId,
370 PiCounterCellId.ofIndirect(FABRIC_INGRESS_SPGW_PDR_COUNTER, cellId)),
371 PiCounterCellHandle.of(deviceId,
372 PiCounterCellId.ofIndirect(FABRIC_EGRESS_SPGW_PDR_COUNTER, cellId)));
373
374 // Query the device.
375 Collection<PiCounterCell> counterEntryResponse = client.read(
376 DEFAULT_P4_DEVICE_ID, pipeconf)
377 .handles(counterCellHandles).submitSync()
378 .all(PiCounterCell.class);
379
380 // Process response.
381 counterEntryResponse.forEach(counterCell -> {
382 if (counterCell.cellId().counterType() != INDIRECT) {
383 log.warn("Invalid counter data type {}, skipping", counterCell.cellId().counterType());
384 return;
385 }
386 if (cellId != counterCell.cellId().index()) {
387 log.warn("Unrecognized counter index {}, skipping", counterCell);
388 return;
389 }
390 if (counterCell.cellId().counterId().equals(FABRIC_INGRESS_SPGW_PDR_COUNTER)) {
391 stats.setIngress(counterCell.data().packets(), counterCell.data().bytes());
392 } else if (counterCell.cellId().counterId().equals(FABRIC_EGRESS_SPGW_PDR_COUNTER)) {
393 stats.setEgress(counterCell.data().packets(), counterCell.data().bytes());
394 } else {
395 log.warn("Unrecognized counter ID {}, skipping", counterCell);
396 }
397 });
398 return stats.build();
399 }
400
401
402 @Override
403 public void addPdr(PacketDetectionRule pdr) throws UpfProgrammableException {
404 if (!setupBehaviour("addPdr()")) {
405 return;
406 }
407 if (pdr.counterId() >= pdrCounterSize() || pdr.counterId() < 0) {
408 throw new UpfProgrammableException("Counter cell index referenced by PDR is out of bounds.",
409 UpfProgrammableException.Type.COUNTER_INDEX_OUT_OF_RANGE);
410 }
411 FlowRule fabricPdr = upfTranslator.pdrToFabricEntry(pdr, deviceId, appId, DEFAULT_PRIORITY);
412 log.info("Installing {}", pdr.toString());
413 flowRuleService.applyFlowRules(fabricPdr);
414 log.debug("PDR added with flowID {}", fabricPdr.id().value());
Daniele Moro8d630f12021-06-15 20:53:22 +0200415 }
416
417
418 @Override
419 public void addFar(ForwardingActionRule far) throws UpfProgrammableException {
420 if (!setupBehaviour("addFar()")) {
421 return;
422 }
Daniele Moro8d630f12021-06-15 20:53:22 +0200423 FlowRule fabricFar = upfTranslator.farToFabricEntry(far, deviceId, appId, DEFAULT_PRIORITY);
424 log.info("Installing {}", far.toString());
425 flowRuleService.applyFlowRules(fabricFar);
426 log.debug("FAR added with flowID {}", fabricFar.id().value());
Daniele Moro8d630f12021-06-15 20:53:22 +0200427 }
428
429 @Override
430 public void addInterface(UpfInterface upfInterface) throws UpfProgrammableException {
431 if (!setupBehaviour("addInterface()")) {
432 return;
433 }
434 FlowRule flowRule = upfTranslator.interfaceToFabricEntry(upfInterface, deviceId, appId, DEFAULT_PRIORITY);
435 log.info("Installing {}", upfInterface);
436 flowRuleService.applyFlowRules(flowRule);
437 log.debug("Interface added with flowID {}", flowRule.id().value());
438 // By default we enable UE-to-UE communication on the UE subnet identified by the CORE interface.
439 // TODO: allow enabling/disabling UE-to-UE via netcfg or other API.
440 log.warn("UE-to-UE traffic is not supported in fabric-v1model");
441 }
442
443 private boolean removeEntry(PiCriterion match, PiTableId tableId, boolean failSilent)
444 throws UpfProgrammableException {
Daniele Moro8d630f12021-06-15 20:53:22 +0200445 FlowRule entry = DefaultFlowRule.builder()
446 .forDevice(deviceId).fromApp(appId).makePermanent()
447 .forTable(tableId)
448 .withSelector(DefaultTrafficSelector.builder().matchPi(match).build())
449 .withPriority(DEFAULT_PRIORITY)
450 .build();
451
452 /*
453 * FIXME: Stupid stupid slow hack, needed because removeFlowRules expects FlowRule objects
454 * with correct and complete actions and parameters, but P4Runtime deletion requests
455 * will not have those.
456 */
457 for (FlowEntry installedEntry : flowRuleService.getFlowEntriesById(appId)) {
458 if (installedEntry.selector().equals(entry.selector())) {
459 log.info("Found matching entry to remove, it has FlowID {}", installedEntry.id());
460 flowRuleService.removeFlowRules(installedEntry);
461 return true;
462 }
463 }
464 if (!failSilent) {
465 throw new UpfProgrammableException("Match criterion " + match.toString() +
466 " not found in table " + tableId.toString());
467 }
468 return false;
469 }
470
471 @Override
472 public Collection<PacketDetectionRule> getPdrs() throws UpfProgrammableException {
473 if (!setupBehaviour("getPdrs()")) {
474 return null;
475 }
476 ArrayList<PacketDetectionRule> pdrs = new ArrayList<>();
477 for (FlowRule flowRule : flowRuleService.getFlowEntriesById(appId)) {
478 if (upfTranslator.isFabricPdr(flowRule)) {
479 pdrs.add(upfTranslator.fabricEntryToPdr(flowRule));
480 }
481 }
482 return pdrs;
483 }
484
485 @Override
486 public Collection<ForwardingActionRule> getFars() throws UpfProgrammableException {
487 if (!setupBehaviour("getFars()")) {
488 return null;
489 }
490 ArrayList<ForwardingActionRule> fars = new ArrayList<>();
491 for (FlowRule flowRule : flowRuleService.getFlowEntriesById(appId)) {
492 if (upfTranslator.isFabricFar(flowRule)) {
493 fars.add(upfTranslator.fabricEntryToFar(flowRule));
494 }
495 }
496 return fars;
497 }
498
499 @Override
500 public Collection<UpfInterface> getInterfaces() throws UpfProgrammableException {
501 if (!setupBehaviour("getInterfaces()")) {
502 return null;
503 }
504 ArrayList<UpfInterface> ifaces = new ArrayList<>();
505 for (FlowRule flowRule : flowRuleService.getFlowEntriesById(appId)) {
506 if (upfTranslator.isFabricInterface(flowRule)) {
507 ifaces.add(upfTranslator.fabricEntryToInterface(flowRule));
508 }
509 }
510 return ifaces;
511 }
512
513 @Override
514 public void removePdr(PacketDetectionRule pdr) throws UpfProgrammableException {
515 if (!setupBehaviour("removePdr()")) {
516 return;
517 }
518 final PiCriterion match;
519 final PiTableId tableId;
520 if (pdr.matchesEncapped()) {
521 match = PiCriterion.builder()
522 .matchExact(HDR_TEID, pdr.teid().asArray())
523 .matchExact(HDR_TUNNEL_IPV4_DST, pdr.tunnelDest().toInt())
524 .build();
525 tableId = FABRIC_INGRESS_SPGW_UPLINK_PDRS;
526 } else {
527 match = PiCriterion.builder()
528 .matchExact(HDR_UE_ADDR, pdr.ueAddress().toInt())
529 .build();
530 tableId = FABRIC_INGRESS_SPGW_DOWNLINK_PDRS;
531 }
532 log.info("Removing {}", pdr.toString());
533 removeEntry(match, tableId, false);
Daniele Moro8d630f12021-06-15 20:53:22 +0200534 }
535
536 @Override
537 public void removeFar(ForwardingActionRule far) throws UpfProgrammableException {
pierventreb91b0a82021-06-23 09:02:13 +0200538 if (!setupBehaviour("removeFar()")) {
539 return;
540 }
Daniele Moro8d630f12021-06-15 20:53:22 +0200541 log.info("Removing {}", far.toString());
542
543 PiCriterion match = PiCriterion.builder()
544 .matchExact(HDR_FAR_ID, fabricUpfStore.globalFarIdOf(far.sessionId(), far.farId()))
545 .build();
546
547 removeEntry(match, FABRIC_INGRESS_SPGW_FARS, false);
548 }
549
550 @Override
551 public void removeInterface(UpfInterface upfInterface) throws UpfProgrammableException {
552 if (!setupBehaviour("removeInterface()")) {
553 return;
554 }
555 Ip4Prefix ifacePrefix = upfInterface.getPrefix();
556 // If it isn't a core interface (so it is either access or unknown), try removing core
557 if (!upfInterface.isCore()) {
558 PiCriterion match1 = PiCriterion.builder()
559 .matchLpm(HDR_IPV4_DST_ADDR, ifacePrefix.address().toInt(),
560 ifacePrefix.prefixLength())
561 .matchExact(HDR_GTPU_IS_VALID, 1)
562 .build();
563 if (removeEntry(match1, FABRIC_INGRESS_SPGW_INTERFACES, true)) {
564 return;
565 }
566 }
567 // If that didn't work or didn't execute, try removing access
568 PiCriterion match2 = PiCriterion.builder()
569 .matchLpm(HDR_IPV4_DST_ADDR, ifacePrefix.address().toInt(),
570 ifacePrefix.prefixLength())
571 .matchExact(HDR_GTPU_IS_VALID, 0)
572 .build();
573 removeEntry(match2, FABRIC_INGRESS_SPGW_INTERFACES, false);
574 }
575}