blob: 2341e04214a085dd74eb05bbe61df3d61dec8c4f [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 org.apache.commons.lang3.tuple.Pair;
20import org.onlab.packet.Ip4Address;
21import org.onlab.util.ImmutableByteSequence;
22import org.onosproject.core.ApplicationId;
23import org.onosproject.net.DeviceId;
24import org.onosproject.net.behaviour.upf.ForwardingActionRule;
25import org.onosproject.net.behaviour.upf.GtpTunnel;
26import org.onosproject.net.behaviour.upf.PacketDetectionRule;
27import org.onosproject.net.behaviour.upf.UpfInterface;
28import org.onosproject.net.behaviour.upf.UpfProgrammableException;
29import org.onosproject.net.flow.DefaultFlowRule;
30import org.onosproject.net.flow.DefaultTrafficSelector;
31import org.onosproject.net.flow.DefaultTrafficTreatment;
32import org.onosproject.net.flow.FlowRule;
33import org.onosproject.net.flow.criteria.PiCriterion;
34import org.onosproject.net.pi.model.PiActionId;
35import org.onosproject.net.pi.model.PiTableId;
36import org.onosproject.net.pi.runtime.PiAction;
37import org.onosproject.net.pi.runtime.PiActionParam;
38import org.onosproject.net.pi.runtime.PiTableAction;
39
40import java.util.Arrays;
41
42import static org.onosproject.pipelines.fabric.FabricConstants.CTR_ID;
43import static org.onosproject.pipelines.fabric.FabricConstants.DROP;
44import static org.onosproject.pipelines.fabric.FabricConstants.FABRIC_INGRESS_SPGW_DOWNLINK_PDRS;
45import static org.onosproject.pipelines.fabric.FabricConstants.FABRIC_INGRESS_SPGW_FARS;
46import static org.onosproject.pipelines.fabric.FabricConstants.FABRIC_INGRESS_SPGW_INTERFACES;
47import static org.onosproject.pipelines.fabric.FabricConstants.FABRIC_INGRESS_SPGW_LOAD_DBUF_FAR;
48import static org.onosproject.pipelines.fabric.FabricConstants.FABRIC_INGRESS_SPGW_LOAD_IFACE;
49import static org.onosproject.pipelines.fabric.FabricConstants.FABRIC_INGRESS_SPGW_LOAD_NORMAL_FAR;
50import static org.onosproject.pipelines.fabric.FabricConstants.FABRIC_INGRESS_SPGW_LOAD_PDR;
51import static org.onosproject.pipelines.fabric.FabricConstants.FABRIC_INGRESS_SPGW_LOAD_PDR_QOS;
52import static org.onosproject.pipelines.fabric.FabricConstants.FABRIC_INGRESS_SPGW_LOAD_TUNNEL_FAR;
53import static org.onosproject.pipelines.fabric.FabricConstants.FABRIC_INGRESS_SPGW_UPLINK_PDRS;
54import static org.onosproject.pipelines.fabric.FabricConstants.FAR_ID;
55import static org.onosproject.pipelines.fabric.FabricConstants.HDR_FAR_ID;
56import static org.onosproject.pipelines.fabric.FabricConstants.HDR_GTPU_IS_VALID;
57import static org.onosproject.pipelines.fabric.FabricConstants.HDR_IPV4_DST_ADDR;
58import static org.onosproject.pipelines.fabric.FabricConstants.HDR_TEID;
59import static org.onosproject.pipelines.fabric.FabricConstants.HDR_TUNNEL_IPV4_DST;
60import static org.onosproject.pipelines.fabric.FabricConstants.HDR_UE_ADDR;
61import static org.onosproject.pipelines.fabric.FabricConstants.NEEDS_GTPU_DECAP;
62import static org.onosproject.pipelines.fabric.FabricConstants.NOTIFY_CP;
63import static org.onosproject.pipelines.fabric.FabricConstants.QID;
64import static org.onosproject.pipelines.fabric.FabricConstants.SRC_IFACE;
65import static org.onosproject.pipelines.fabric.FabricConstants.TEID;
66import static org.onosproject.pipelines.fabric.FabricConstants.TUNNEL_DST_ADDR;
67import static org.onosproject.pipelines.fabric.FabricConstants.TUNNEL_SRC_ADDR;
68import static org.onosproject.pipelines.fabric.FabricConstants.TUNNEL_SRC_PORT;
69
70/**
71 * Provides logic to translate UPF entities into pipeline-specific ones and vice-versa.
72 * Implementation should be stateless, with all state delegated to FabricUpfStore.
73 */
74public class FabricUpfTranslator {
75
76 // UPF related constants
77 public static final int INTERFACE_ACCESS = 1;
78 public static final int INTERFACE_CORE = 2;
79 public static final int INTERFACE_DBUF = 3;
80
81 private final FabricUpfStore fabricUpfStore;
82
83 public FabricUpfTranslator(FabricUpfStore fabricUpfStore) {
84 this.fabricUpfStore = fabricUpfStore;
85 }
86
87 /**
88 * Returns true if the given table entry is a Packet Detection Rule from the physical fabric pipeline, and
89 * false otherwise.
90 *
91 * @param entry the entry that may or may not be a fabric.p4 PDR
92 * @return true if the entry is a fabric.p4 PDR
93 */
94 public boolean isFabricPdr(FlowRule entry) {
95 return entry.table().equals(FABRIC_INGRESS_SPGW_UPLINK_PDRS)
96 || entry.table().equals(FABRIC_INGRESS_SPGW_DOWNLINK_PDRS);
97 }
98
99 /**
100 * Returns true if the given table entry is a Forwarding Action Rule from the physical fabric pipeline, and
101 * false otherwise.
102 *
103 * @param entry the entry that may or may not be a fabric.p4 FAR
104 * @return true if the entry is a fabric.p4 FAR
105 */
106 public boolean isFabricFar(FlowRule entry) {
107 return entry.table().equals(FABRIC_INGRESS_SPGW_FARS);
108 }
109
110 /**
111 * Returns true if the given table entry is an interface table entry from the fabric.p4 physical pipeline, and
112 * false otherwise.
113 *
114 * @param entry the entry that may or may not be a fabric.p4 UPF interface
115 * @return true if the entry is a fabric.p4 UPF interface
116 */
117 public boolean isFabricInterface(FlowRule entry) {
118 return entry.table().equals(FABRIC_INGRESS_SPGW_INTERFACES);
119 }
120
121
122 /**
123 * Translate a fabric.p4 PDR table entry to a PacketDetectionRule instance for easier handling.
124 *
125 * @param entry the fabric.p4 entry to translate
126 * @return the corresponding PacketDetectionRule
127 * @throws UpfProgrammableException if the entry cannot be translated
128 */
129 public PacketDetectionRule fabricEntryToPdr(FlowRule entry)
130 throws UpfProgrammableException {
131 var pdrBuilder = PacketDetectionRule.builder();
132 Pair<PiCriterion, PiTableAction> matchActionPair = FabricUpfTranslatorUtil.fabricEntryToPiPair(entry);
133 PiCriterion match = matchActionPair.getLeft();
134 PiAction action = (PiAction) matchActionPair.getRight();
135
136 // Grab keys and parameters that are present for all PDRs
137 int globalFarId = FabricUpfTranslatorUtil.getParamInt(action, FAR_ID);
138 UpfRuleIdentifier farId = fabricUpfStore.localFarIdOf(globalFarId);
pierventre1eb98712021-07-13 18:03:22 +0200139 if (farId == null) {
140 throw new UpfProgrammableException(String.format("Unable to find local far id of %s", globalFarId));
141 }
Daniele Moro8d630f12021-06-15 20:53:22 +0200142
143 PiActionId actionId = action.id();
144 if (actionId.equals(FABRIC_INGRESS_SPGW_LOAD_PDR)) {
145 int schedulingPriority = 0;
146 pdrBuilder.withSchedulingPriority(schedulingPriority);
147 } else if (actionId.equals(FABRIC_INGRESS_SPGW_LOAD_PDR_QOS)) {
148 int queueId = FabricUpfTranslatorUtil.getParamInt(action, QID);
149 String schedulingPriority = fabricUpfStore.schedulingPriorityOf(queueId);
150 if (schedulingPriority == null) {
151 throw new UpfProgrammableException("Undefined Scheduling Priority");
152 }
153 pdrBuilder.withSchedulingPriority(Integer.parseInt(schedulingPriority));
154 } else {
155 throw new UpfProgrammableException("Unknown action ID");
156 }
157 pdrBuilder.withCounterId(FabricUpfTranslatorUtil.getParamInt(action, CTR_ID))
158 .withLocalFarId(farId.getSessionLocalId())
159 .withSessionId(farId.getPfcpSessionId());
160
161 if (FabricUpfTranslatorUtil.fieldIsPresent(match, HDR_TEID)) {
162 // F-TEID is only present for GTP-matching PDRs
163 ImmutableByteSequence teid = FabricUpfTranslatorUtil.getFieldValue(match, HDR_TEID);
164 Ip4Address tunnelDst = FabricUpfTranslatorUtil.getFieldAddress(match, HDR_TUNNEL_IPV4_DST);
165 pdrBuilder.withTeid(teid)
166 .withTunnelDst(tunnelDst);
167 } else if (FabricUpfTranslatorUtil.fieldIsPresent(match, HDR_UE_ADDR)) {
168 // And UE address is only present for non-GTP-matching PDRs
169 pdrBuilder.withUeAddr(FabricUpfTranslatorUtil.getFieldAddress(match, HDR_UE_ADDR));
170 } else {
171 throw new UpfProgrammableException("Read malformed PDR from dataplane!:" + entry);
172 }
173 return pdrBuilder.build();
174 }
175
176 /**
177 * Translate a fabric.p4 FAR table entry to a ForwardActionRule instance for easier handling.
178 *
179 * @param entry the fabric.p4 entry to translate
180 * @return the corresponding ForwardingActionRule
181 * @throws UpfProgrammableException if the entry cannot be translated
182 */
183 public ForwardingActionRule fabricEntryToFar(FlowRule entry)
184 throws UpfProgrammableException {
185 var farBuilder = ForwardingActionRule.builder();
186 Pair<PiCriterion, PiTableAction> matchActionPair = FabricUpfTranslatorUtil.fabricEntryToPiPair(entry);
187 PiCriterion match = matchActionPair.getLeft();
188 PiAction action = (PiAction) matchActionPair.getRight();
189
190 int globalFarId = FabricUpfTranslatorUtil.getFieldInt(match, HDR_FAR_ID);
191 UpfRuleIdentifier farId = fabricUpfStore.localFarIdOf(globalFarId);
pierventre1eb98712021-07-13 18:03:22 +0200192 if (farId == null) {
193 throw new UpfProgrammableException(String.format("Unable to find local far id of %s", globalFarId));
194 }
Daniele Moro8d630f12021-06-15 20:53:22 +0200195
196 boolean dropFlag = FabricUpfTranslatorUtil.getParamInt(action, DROP) > 0;
197 boolean notifyFlag = FabricUpfTranslatorUtil.getParamInt(action, NOTIFY_CP) > 0;
198
199 // Match keys
200 farBuilder.withSessionId(farId.getPfcpSessionId())
201 .setFarId(farId.getSessionLocalId());
202
203 // Parameters common to all types of FARs
204 farBuilder.setDropFlag(dropFlag)
205 .setNotifyFlag(notifyFlag);
206
207 PiActionId actionId = action.id();
208
209 if (actionId.equals(FABRIC_INGRESS_SPGW_LOAD_TUNNEL_FAR)
210 || actionId.equals(FABRIC_INGRESS_SPGW_LOAD_DBUF_FAR)) {
211 // Grab parameters specific to encapsulating FARs if they're present
212 Ip4Address tunnelSrc = FabricUpfTranslatorUtil.getParamAddress(action, TUNNEL_SRC_ADDR);
213 Ip4Address tunnelDst = FabricUpfTranslatorUtil.getParamAddress(action, TUNNEL_DST_ADDR);
214 ImmutableByteSequence teid = FabricUpfTranslatorUtil.getParamValue(action, TEID);
215 short tunnelSrcPort = (short) FabricUpfTranslatorUtil.getParamInt(action, TUNNEL_SRC_PORT);
216
217 farBuilder.setBufferFlag(actionId.equals(FABRIC_INGRESS_SPGW_LOAD_DBUF_FAR));
218
219 farBuilder.setTunnel(
220 GtpTunnel.builder()
221 .setSrc(tunnelSrc)
222 .setDst(tunnelDst)
223 .setTeid(teid)
224 .setSrcPort(tunnelSrcPort)
225 .build());
226 }
227 return farBuilder.build();
228 }
229
230 /**
231 * Translate a fabric.p4 interface table entry to a UpfInterface instance for easier handling.
232 *
233 * @param entry the fabric.p4 entry to translate
234 * @return the corresponding UpfInterface
235 * @throws UpfProgrammableException if the entry cannot be translated
236 */
237 public UpfInterface fabricEntryToInterface(FlowRule entry)
238 throws UpfProgrammableException {
239 Pair<PiCriterion, PiTableAction> matchActionPair = FabricUpfTranslatorUtil.fabricEntryToPiPair(entry);
240 PiCriterion match = matchActionPair.getLeft();
241 PiAction action = (PiAction) matchActionPair.getRight();
242
243 var ifaceBuilder = UpfInterface.builder()
244 .setPrefix(FabricUpfTranslatorUtil.getFieldPrefix(match, HDR_IPV4_DST_ADDR));
245
246 int interfaceType = FabricUpfTranslatorUtil.getParamInt(action, SRC_IFACE);
247 if (interfaceType == INTERFACE_ACCESS) {
248 ifaceBuilder.setAccess();
249 } else if (interfaceType == INTERFACE_CORE) {
250 ifaceBuilder.setCore();
251 } else if (interfaceType == INTERFACE_DBUF) {
252 ifaceBuilder.setDbufReceiver();
253 }
254 return ifaceBuilder.build();
255 }
256
257 /**
258 * Translate a ForwardingActionRule to a FlowRule to be inserted into the fabric.p4 pipeline.
259 * A side effect of calling this method is the FAR object's globalFarId is assigned if it was not already.
260 *
261 * @param far The FAR to be translated
262 * @param deviceId the ID of the device the FlowRule should be installed on
263 * @param appId the ID of the application that will insert the FlowRule
264 * @param priority the FlowRule's priority
265 * @return the FAR translated to a FlowRule
266 * @throws UpfProgrammableException if the FAR to be translated is malformed
267 */
268 public FlowRule farToFabricEntry(ForwardingActionRule far, DeviceId deviceId, ApplicationId appId, int priority)
269 throws UpfProgrammableException {
270 PiAction action;
271 if (!far.encaps()) {
272 action = PiAction.builder()
273 .withId(FABRIC_INGRESS_SPGW_LOAD_NORMAL_FAR)
274 .withParameters(Arrays.asList(
275 new PiActionParam(DROP, far.drops() ? 1 : 0),
276 new PiActionParam(NOTIFY_CP, far.notifies() ? 1 : 0)
277 ))
278 .build();
279
280 } else {
281 if (far.tunnelSrc() == null || far.tunnelDst() == null
282 || far.teid() == null || far.tunnel().srcPort() == null) {
283 throw new UpfProgrammableException(
284 "Not all action parameters present when translating " +
285 "intermediate encapsulating/buffering FAR to physical FAR!");
286 }
287 // TODO: copy tunnel destination port from logical switch write requests, instead of hardcoding 2152
288 PiActionId actionId = far.buffers() ? FABRIC_INGRESS_SPGW_LOAD_DBUF_FAR :
289 FABRIC_INGRESS_SPGW_LOAD_TUNNEL_FAR;
290 action = PiAction.builder()
291 .withId(actionId)
292 .withParameters(Arrays.asList(
293 new PiActionParam(DROP, far.drops() ? 1 : 0),
294 new PiActionParam(NOTIFY_CP, far.notifies() ? 1 : 0),
295 new PiActionParam(TEID, far.teid()),
296 new PiActionParam(TUNNEL_SRC_ADDR, far.tunnelSrc().toInt()),
297 new PiActionParam(TUNNEL_DST_ADDR, far.tunnelDst().toInt()),
298 new PiActionParam(TUNNEL_SRC_PORT, far.tunnel().srcPort())
299 ))
300 .build();
301 }
302 PiCriterion match = PiCriterion.builder()
303 .matchExact(HDR_FAR_ID, fabricUpfStore.globalFarIdOf(far.sessionId(), far.farId()))
304 .build();
305 return DefaultFlowRule.builder()
306 .forDevice(deviceId).fromApp(appId).makePermanent()
307 .forTable(FABRIC_INGRESS_SPGW_FARS)
308 .withSelector(DefaultTrafficSelector.builder().matchPi(match).build())
309 .withTreatment(DefaultTrafficTreatment.builder().piTableAction(action).build())
310 .withPriority(priority)
311 .build();
312 }
313
314 /**
315 * Translate a PacketDetectionRule to a FlowRule to be inserted into the fabric.p4 pipeline.
316 * A side effect of calling this method is the PDR object's globalFarId is assigned if it was not already.
317 *
318 * @param pdr The PDR to be translated
319 * @param deviceId the ID of the device the FlowRule should be installed on
320 * @param appId the ID of the application that will insert the FlowRule
321 * @param priority the FlowRule's priority
322 * @return the FAR translated to a FlowRule
323 * @throws UpfProgrammableException if the PDR to be translated is malformed
324 */
325 public FlowRule pdrToFabricEntry(PacketDetectionRule pdr, DeviceId deviceId, ApplicationId appId, int priority)
326 throws UpfProgrammableException {
327 PiCriterion match;
328 PiTableId tableId;
329 PiAction action;
330
331 if (pdr.matchesEncapped()) {
332 match = PiCriterion.builder()
333 .matchExact(HDR_TEID, pdr.teid().asArray())
334 .matchExact(HDR_TUNNEL_IPV4_DST, pdr.tunnelDest().toInt())
335 .build();
336 tableId = FABRIC_INGRESS_SPGW_UPLINK_PDRS;
337 } else if (pdr.matchesUnencapped()) {
338 match = PiCriterion.builder()
339 .matchExact(HDR_UE_ADDR, pdr.ueAddress().toInt())
340 .build();
341 tableId = FABRIC_INGRESS_SPGW_DOWNLINK_PDRS;
342 } else {
343 throw new UpfProgrammableException("Flexible PDRs not yet supported! Cannot translate " + pdr.toString());
344 }
345
346 PiAction.Builder builder = PiAction.builder()
347 .withParameters(Arrays.asList(
348 new PiActionParam(CTR_ID, pdr.counterId()),
349 new PiActionParam(FAR_ID, fabricUpfStore.globalFarIdOf(pdr.sessionId(), pdr.farId())),
350 new PiActionParam(NEEDS_GTPU_DECAP, pdr.matchesEncapped() ? 1 : 0)
351 ));
352 if (pdr.hasSchedulingPriority()) {
353 String queueId = fabricUpfStore.queueIdOf(pdr.schedulingPriority());
354 if (queueId == null) {
355 throw new UpfProgrammableException("Udefined Scheduling Priority");
356 }
357 action = builder
358 .withId(FABRIC_INGRESS_SPGW_LOAD_PDR_QOS)
359 .withParameter(new PiActionParam(QID, Integer.parseInt(queueId)))
360 .build();
361 } else {
362 action = builder
363 .withId(FABRIC_INGRESS_SPGW_LOAD_PDR)
364 .build();
365 }
366 return DefaultFlowRule.builder()
367 .forDevice(deviceId).fromApp(appId).makePermanent()
368 .forTable(tableId)
369 .withSelector(DefaultTrafficSelector.builder().matchPi(match).build())
370 .withTreatment(DefaultTrafficTreatment.builder().piTableAction(action).build())
371 .withPriority(priority)
372 .build();
373 }
374
375 /**
376 * Translate a UpfInterface to a FlowRule to be inserted into the fabric.p4 pipeline.
377 *
378 * @param upfInterface The interface to be translated
379 * @param deviceId the ID of the device the FlowRule should be installed on
380 * @param appId the ID of the application that will insert the FlowRule
381 * @param priority the FlowRule's priority
382 * @return the UPF interface translated to a FlowRule
383 * @throws UpfProgrammableException if the interface cannot be translated
384 */
385 public FlowRule interfaceToFabricEntry(UpfInterface upfInterface, DeviceId deviceId,
386 ApplicationId appId, int priority)
387 throws UpfProgrammableException {
388 int interfaceTypeInt;
389 int gtpuValidity;
390 if (upfInterface.isDbufReceiver()) {
391 interfaceTypeInt = INTERFACE_DBUF;
392 gtpuValidity = 1;
393 } else if (upfInterface.isAccess()) {
394 interfaceTypeInt = INTERFACE_ACCESS;
395 gtpuValidity = 1;
396 } else {
397 interfaceTypeInt = INTERFACE_CORE;
398 gtpuValidity = 0;
399 }
400
401 PiCriterion match = PiCriterion.builder()
402 .matchLpm(HDR_IPV4_DST_ADDR,
403 upfInterface.prefix().address().toInt(),
404 upfInterface.prefix().prefixLength())
405 .matchExact(HDR_GTPU_IS_VALID, gtpuValidity)
406 .build();
407 PiAction action = PiAction.builder()
408 .withId(FABRIC_INGRESS_SPGW_LOAD_IFACE)
409 .withParameter(new PiActionParam(SRC_IFACE, interfaceTypeInt))
410 .build();
411 return DefaultFlowRule.builder()
412 .forDevice(deviceId).fromApp(appId).makePermanent()
413 .forTable(FABRIC_INGRESS_SPGW_INTERFACES)
414 .withSelector(DefaultTrafficSelector.builder().matchPi(match).build())
415 .withTreatment(DefaultTrafficTreatment.builder().piTableAction(action).build())
416 .withPriority(priority)
417 .build();
418 }
Daniele Moro8d630f12021-06-15 20:53:22 +0200419}