blob: c8cce84844a46dadd12947fddfe3061a28fe2566 [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);
139
140 PiActionId actionId = action.id();
141 if (actionId.equals(FABRIC_INGRESS_SPGW_LOAD_PDR)) {
142 int schedulingPriority = 0;
143 pdrBuilder.withSchedulingPriority(schedulingPriority);
144 } else if (actionId.equals(FABRIC_INGRESS_SPGW_LOAD_PDR_QOS)) {
145 int queueId = FabricUpfTranslatorUtil.getParamInt(action, QID);
146 String schedulingPriority = fabricUpfStore.schedulingPriorityOf(queueId);
147 if (schedulingPriority == null) {
148 throw new UpfProgrammableException("Undefined Scheduling Priority");
149 }
150 pdrBuilder.withSchedulingPriority(Integer.parseInt(schedulingPriority));
151 } else {
152 throw new UpfProgrammableException("Unknown action ID");
153 }
154 pdrBuilder.withCounterId(FabricUpfTranslatorUtil.getParamInt(action, CTR_ID))
155 .withLocalFarId(farId.getSessionLocalId())
156 .withSessionId(farId.getPfcpSessionId());
157
158 if (FabricUpfTranslatorUtil.fieldIsPresent(match, HDR_TEID)) {
159 // F-TEID is only present for GTP-matching PDRs
160 ImmutableByteSequence teid = FabricUpfTranslatorUtil.getFieldValue(match, HDR_TEID);
161 Ip4Address tunnelDst = FabricUpfTranslatorUtil.getFieldAddress(match, HDR_TUNNEL_IPV4_DST);
162 pdrBuilder.withTeid(teid)
163 .withTunnelDst(tunnelDst);
164 } else if (FabricUpfTranslatorUtil.fieldIsPresent(match, HDR_UE_ADDR)) {
165 // And UE address is only present for non-GTP-matching PDRs
166 pdrBuilder.withUeAddr(FabricUpfTranslatorUtil.getFieldAddress(match, HDR_UE_ADDR));
167 } else {
168 throw new UpfProgrammableException("Read malformed PDR from dataplane!:" + entry);
169 }
170 return pdrBuilder.build();
171 }
172
173 /**
174 * Translate a fabric.p4 FAR table entry to a ForwardActionRule instance for easier handling.
175 *
176 * @param entry the fabric.p4 entry to translate
177 * @return the corresponding ForwardingActionRule
178 * @throws UpfProgrammableException if the entry cannot be translated
179 */
180 public ForwardingActionRule fabricEntryToFar(FlowRule entry)
181 throws UpfProgrammableException {
182 var farBuilder = ForwardingActionRule.builder();
183 Pair<PiCriterion, PiTableAction> matchActionPair = FabricUpfTranslatorUtil.fabricEntryToPiPair(entry);
184 PiCriterion match = matchActionPair.getLeft();
185 PiAction action = (PiAction) matchActionPair.getRight();
186
187 int globalFarId = FabricUpfTranslatorUtil.getFieldInt(match, HDR_FAR_ID);
188 UpfRuleIdentifier farId = fabricUpfStore.localFarIdOf(globalFarId);
189
190 boolean dropFlag = FabricUpfTranslatorUtil.getParamInt(action, DROP) > 0;
191 boolean notifyFlag = FabricUpfTranslatorUtil.getParamInt(action, NOTIFY_CP) > 0;
192
193 // Match keys
194 farBuilder.withSessionId(farId.getPfcpSessionId())
195 .setFarId(farId.getSessionLocalId());
196
197 // Parameters common to all types of FARs
198 farBuilder.setDropFlag(dropFlag)
199 .setNotifyFlag(notifyFlag);
200
201 PiActionId actionId = action.id();
202
203 if (actionId.equals(FABRIC_INGRESS_SPGW_LOAD_TUNNEL_FAR)
204 || actionId.equals(FABRIC_INGRESS_SPGW_LOAD_DBUF_FAR)) {
205 // Grab parameters specific to encapsulating FARs if they're present
206 Ip4Address tunnelSrc = FabricUpfTranslatorUtil.getParamAddress(action, TUNNEL_SRC_ADDR);
207 Ip4Address tunnelDst = FabricUpfTranslatorUtil.getParamAddress(action, TUNNEL_DST_ADDR);
208 ImmutableByteSequence teid = FabricUpfTranslatorUtil.getParamValue(action, TEID);
209 short tunnelSrcPort = (short) FabricUpfTranslatorUtil.getParamInt(action, TUNNEL_SRC_PORT);
210
211 farBuilder.setBufferFlag(actionId.equals(FABRIC_INGRESS_SPGW_LOAD_DBUF_FAR));
212
213 farBuilder.setTunnel(
214 GtpTunnel.builder()
215 .setSrc(tunnelSrc)
216 .setDst(tunnelDst)
217 .setTeid(teid)
218 .setSrcPort(tunnelSrcPort)
219 .build());
220 }
221 return farBuilder.build();
222 }
223
224 /**
225 * Translate a fabric.p4 interface table entry to a UpfInterface instance for easier handling.
226 *
227 * @param entry the fabric.p4 entry to translate
228 * @return the corresponding UpfInterface
229 * @throws UpfProgrammableException if the entry cannot be translated
230 */
231 public UpfInterface fabricEntryToInterface(FlowRule entry)
232 throws UpfProgrammableException {
233 Pair<PiCriterion, PiTableAction> matchActionPair = FabricUpfTranslatorUtil.fabricEntryToPiPair(entry);
234 PiCriterion match = matchActionPair.getLeft();
235 PiAction action = (PiAction) matchActionPair.getRight();
236
237 var ifaceBuilder = UpfInterface.builder()
238 .setPrefix(FabricUpfTranslatorUtil.getFieldPrefix(match, HDR_IPV4_DST_ADDR));
239
240 int interfaceType = FabricUpfTranslatorUtil.getParamInt(action, SRC_IFACE);
241 if (interfaceType == INTERFACE_ACCESS) {
242 ifaceBuilder.setAccess();
243 } else if (interfaceType == INTERFACE_CORE) {
244 ifaceBuilder.setCore();
245 } else if (interfaceType == INTERFACE_DBUF) {
246 ifaceBuilder.setDbufReceiver();
247 }
248 return ifaceBuilder.build();
249 }
250
251 /**
252 * Translate a ForwardingActionRule to a FlowRule to be inserted into the fabric.p4 pipeline.
253 * A side effect of calling this method is the FAR object's globalFarId is assigned if it was not already.
254 *
255 * @param far The FAR to be translated
256 * @param deviceId the ID of the device the FlowRule should be installed on
257 * @param appId the ID of the application that will insert the FlowRule
258 * @param priority the FlowRule's priority
259 * @return the FAR translated to a FlowRule
260 * @throws UpfProgrammableException if the FAR to be translated is malformed
261 */
262 public FlowRule farToFabricEntry(ForwardingActionRule far, DeviceId deviceId, ApplicationId appId, int priority)
263 throws UpfProgrammableException {
264 PiAction action;
265 if (!far.encaps()) {
266 action = PiAction.builder()
267 .withId(FABRIC_INGRESS_SPGW_LOAD_NORMAL_FAR)
268 .withParameters(Arrays.asList(
269 new PiActionParam(DROP, far.drops() ? 1 : 0),
270 new PiActionParam(NOTIFY_CP, far.notifies() ? 1 : 0)
271 ))
272 .build();
273
274 } else {
275 if (far.tunnelSrc() == null || far.tunnelDst() == null
276 || far.teid() == null || far.tunnel().srcPort() == null) {
277 throw new UpfProgrammableException(
278 "Not all action parameters present when translating " +
279 "intermediate encapsulating/buffering FAR to physical FAR!");
280 }
281 // TODO: copy tunnel destination port from logical switch write requests, instead of hardcoding 2152
282 PiActionId actionId = far.buffers() ? FABRIC_INGRESS_SPGW_LOAD_DBUF_FAR :
283 FABRIC_INGRESS_SPGW_LOAD_TUNNEL_FAR;
284 action = PiAction.builder()
285 .withId(actionId)
286 .withParameters(Arrays.asList(
287 new PiActionParam(DROP, far.drops() ? 1 : 0),
288 new PiActionParam(NOTIFY_CP, far.notifies() ? 1 : 0),
289 new PiActionParam(TEID, far.teid()),
290 new PiActionParam(TUNNEL_SRC_ADDR, far.tunnelSrc().toInt()),
291 new PiActionParam(TUNNEL_DST_ADDR, far.tunnelDst().toInt()),
292 new PiActionParam(TUNNEL_SRC_PORT, far.tunnel().srcPort())
293 ))
294 .build();
295 }
296 PiCriterion match = PiCriterion.builder()
297 .matchExact(HDR_FAR_ID, fabricUpfStore.globalFarIdOf(far.sessionId(), far.farId()))
298 .build();
299 return DefaultFlowRule.builder()
300 .forDevice(deviceId).fromApp(appId).makePermanent()
301 .forTable(FABRIC_INGRESS_SPGW_FARS)
302 .withSelector(DefaultTrafficSelector.builder().matchPi(match).build())
303 .withTreatment(DefaultTrafficTreatment.builder().piTableAction(action).build())
304 .withPriority(priority)
305 .build();
306 }
307
308 /**
309 * Translate a PacketDetectionRule to a FlowRule to be inserted into the fabric.p4 pipeline.
310 * A side effect of calling this method is the PDR object's globalFarId is assigned if it was not already.
311 *
312 * @param pdr The PDR to be translated
313 * @param deviceId the ID of the device the FlowRule should be installed on
314 * @param appId the ID of the application that will insert the FlowRule
315 * @param priority the FlowRule's priority
316 * @return the FAR translated to a FlowRule
317 * @throws UpfProgrammableException if the PDR to be translated is malformed
318 */
319 public FlowRule pdrToFabricEntry(PacketDetectionRule pdr, DeviceId deviceId, ApplicationId appId, int priority)
320 throws UpfProgrammableException {
321 PiCriterion match;
322 PiTableId tableId;
323 PiAction action;
324
325 if (pdr.matchesEncapped()) {
326 match = PiCriterion.builder()
327 .matchExact(HDR_TEID, pdr.teid().asArray())
328 .matchExact(HDR_TUNNEL_IPV4_DST, pdr.tunnelDest().toInt())
329 .build();
330 tableId = FABRIC_INGRESS_SPGW_UPLINK_PDRS;
331 } else if (pdr.matchesUnencapped()) {
332 match = PiCriterion.builder()
333 .matchExact(HDR_UE_ADDR, pdr.ueAddress().toInt())
334 .build();
335 tableId = FABRIC_INGRESS_SPGW_DOWNLINK_PDRS;
336 } else {
337 throw new UpfProgrammableException("Flexible PDRs not yet supported! Cannot translate " + pdr.toString());
338 }
339
340 PiAction.Builder builder = PiAction.builder()
341 .withParameters(Arrays.asList(
342 new PiActionParam(CTR_ID, pdr.counterId()),
343 new PiActionParam(FAR_ID, fabricUpfStore.globalFarIdOf(pdr.sessionId(), pdr.farId())),
344 new PiActionParam(NEEDS_GTPU_DECAP, pdr.matchesEncapped() ? 1 : 0)
345 ));
346 if (pdr.hasSchedulingPriority()) {
347 String queueId = fabricUpfStore.queueIdOf(pdr.schedulingPriority());
348 if (queueId == null) {
349 throw new UpfProgrammableException("Udefined Scheduling Priority");
350 }
351 action = builder
352 .withId(FABRIC_INGRESS_SPGW_LOAD_PDR_QOS)
353 .withParameter(new PiActionParam(QID, Integer.parseInt(queueId)))
354 .build();
355 } else {
356 action = builder
357 .withId(FABRIC_INGRESS_SPGW_LOAD_PDR)
358 .build();
359 }
360 return DefaultFlowRule.builder()
361 .forDevice(deviceId).fromApp(appId).makePermanent()
362 .forTable(tableId)
363 .withSelector(DefaultTrafficSelector.builder().matchPi(match).build())
364 .withTreatment(DefaultTrafficTreatment.builder().piTableAction(action).build())
365 .withPriority(priority)
366 .build();
367 }
368
369 /**
370 * Translate a UpfInterface to a FlowRule to be inserted into the fabric.p4 pipeline.
371 *
372 * @param upfInterface The interface to be translated
373 * @param deviceId the ID of the device the FlowRule should be installed on
374 * @param appId the ID of the application that will insert the FlowRule
375 * @param priority the FlowRule's priority
376 * @return the UPF interface translated to a FlowRule
377 * @throws UpfProgrammableException if the interface cannot be translated
378 */
379 public FlowRule interfaceToFabricEntry(UpfInterface upfInterface, DeviceId deviceId,
380 ApplicationId appId, int priority)
381 throws UpfProgrammableException {
382 int interfaceTypeInt;
383 int gtpuValidity;
384 if (upfInterface.isDbufReceiver()) {
385 interfaceTypeInt = INTERFACE_DBUF;
386 gtpuValidity = 1;
387 } else if (upfInterface.isAccess()) {
388 interfaceTypeInt = INTERFACE_ACCESS;
389 gtpuValidity = 1;
390 } else {
391 interfaceTypeInt = INTERFACE_CORE;
392 gtpuValidity = 0;
393 }
394
395 PiCriterion match = PiCriterion.builder()
396 .matchLpm(HDR_IPV4_DST_ADDR,
397 upfInterface.prefix().address().toInt(),
398 upfInterface.prefix().prefixLength())
399 .matchExact(HDR_GTPU_IS_VALID, gtpuValidity)
400 .build();
401 PiAction action = PiAction.builder()
402 .withId(FABRIC_INGRESS_SPGW_LOAD_IFACE)
403 .withParameter(new PiActionParam(SRC_IFACE, interfaceTypeInt))
404 .build();
405 return DefaultFlowRule.builder()
406 .forDevice(deviceId).fromApp(appId).makePermanent()
407 .forTable(FABRIC_INGRESS_SPGW_INTERFACES)
408 .withSelector(DefaultTrafficSelector.builder().matchPi(match).build())
409 .withTreatment(DefaultTrafficTreatment.builder().piTableAction(action).build())
410 .withPriority(priority)
411 .build();
412 }
Daniele Moro8d630f12021-06-15 20:53:22 +0200413}