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