blob: c2069a58b6f7f20231e03bd0ff3ee6f0c9756039 [file] [log] [blame]
Daniele Moro5e66f982021-06-11 16:41:48 +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.net.behaviour.upf;
18
19import org.onlab.packet.Ip4Address;
20import org.onlab.util.ImmutableByteSequence;
21
22import java.util.Objects;
23
24import static com.google.common.base.Preconditions.checkArgument;
25
26/**
27 * A single Packet Detection Rule (PDR), an entity described in the 3GPP
28 * specifications (although that does not mean that this class is 3GPP
29 * compliant). An instance of this class will be generated by a logical switch
30 * write request to the database-style PDR P4 table, and the resulting instance
31 * should contain all the information needed to reproduce that logical switch
32 * PDR in the event of a client read request. The instance should also contain
33 * sufficient information (or expose the means to retrieve such information) to
34 * generate the corresponding dataplane forwarding state that implements the PDR.
35 */
36public final class PacketDetectionRule {
37 // Match keys
38 private final Ip4Address ueAddr; // The UE IP address that this PDR matches on
39 private final ImmutableByteSequence teid; // The Tunnel Endpoint ID that this PDR matches on
40 private final Ip4Address tunnelDst; // The tunnel destination address that this PDR matches on
41 // Action parameters
42 private final ImmutableByteSequence sessionId; // The ID of the PFCP session that created this PDR
43 private final Integer ctrId; // Counter ID unique to this PDR
44 private final Integer farId; // The PFCP session-local ID of the FAR that should apply after this PDR hits
45 private final Type type;
46 private final Integer schedulingPriority;
47
48 private static final int SESSION_ID_BITWIDTH = 96;
49
50 private PacketDetectionRule(ImmutableByteSequence sessionId, Integer ctrId,
51 Integer farId, Integer schedulingPriority,
52 Ip4Address ueAddr, ImmutableByteSequence teid,
53 Ip4Address tunnelDst, Type type) {
54 this.ueAddr = ueAddr;
55 this.teid = teid;
56 this.tunnelDst = tunnelDst;
57 this.sessionId = sessionId;
58 this.ctrId = ctrId;
59 this.farId = farId;
60 this.schedulingPriority = schedulingPriority;
61 this.type = type;
62 }
63
64 public static Builder builder() {
65 return new Builder();
66 }
67
68 /**
69 * Return a string representing the match conditions of this PDR.
70 *
71 * @return a string representing the PDR match conditions
72 */
73 public String matchString() {
74 if (matchesEncapped()) {
75 return String.format("Match(Dst=%s, TEID=%s)", tunnelDest(), teid());
76 } else {
77 return String.format("Match(Dst=%s, !GTP)", ueAddress());
78 }
79 }
80
81 @Override
82 public String toString() {
83 String actionParams = "";
84 if (hasActionParameters()) {
85 actionParams = String.format("SEID=%s, FAR=%d, CtrIdx=%d, SchedulingPrio=%d",
86 sessionId.toString(), farId, ctrId, schedulingPriority);
87 }
88
89 return String.format("PDR{%s -> LoadParams(%s)}",
90 matchString(), actionParams);
91 }
92
93 @Override
94 public boolean equals(Object obj) {
95 if (obj == this) {
96 return true;
97 }
98 if (obj == null) {
99 return false;
100 }
101 if (getClass() != obj.getClass()) {
102 return false;
103 }
104 PacketDetectionRule that = (PacketDetectionRule) obj;
105
106 // Safe comparisons between potentially null objects
107 return (this.type.equals(that.type) &&
108 Objects.equals(this.teid, that.teid) &&
109 Objects.equals(this.tunnelDst, that.tunnelDst) &&
110 Objects.equals(this.ueAddr, that.ueAddr) &&
111 Objects.equals(this.ctrId, that.ctrId) &&
112 Objects.equals(this.sessionId, that.sessionId) &&
113 Objects.equals(this.farId, that.farId) &&
114 Objects.equals(this.schedulingPriority, that.schedulingPriority));
115 }
116
117 @Override
118 public int hashCode() {
119 return Objects.hash(ueAddr, teid, tunnelDst, sessionId, ctrId, farId,
120 schedulingPriority, type);
121 }
122
123 /**
124 * Instances created as a result of DELETE write requests will not have
125 * action parameters, only match keys. This method should be used to avoid
126 * null pointer exceptions in those instances.
127 *
128 * @return true if this instance has PDR action parameters, false otherwise.
129 */
130 public boolean hasActionParameters() {
131 return type == Type.MATCH_ENCAPPED || type == Type.MATCH_UNENCAPPED;
132 }
133
134 /**
135 * Return a new instance of this PDR with the action parameters stripped,
136 * leaving only the match keys.
137 *
138 * @return a new PDR with only match keys
139 */
140 public PacketDetectionRule withoutActionParams() {
141 if (matchesEncapped()) {
142 return PacketDetectionRule.builder()
143 .withTeid(teid)
144 .withTunnelDst(tunnelDst)
145 .build();
146 } else {
147 return PacketDetectionRule.builder()
148 .withUeAddr(ueAddr).build();
149 }
150 }
151
152 /**
153 * True if this PDR matches on packets received with a GTP header, and false
154 * otherwise.
155 *
156 * @return true if the PDR matches only encapsulated packets
157 */
158 public boolean matchesEncapped() {
159 return type == Type.MATCH_ENCAPPED ||
160 type == Type.MATCH_ENCAPPED_NO_ACTION;
161 }
162
163 /**
164 * True if this PDR matches on packets received without a GTP header, and
165 * false otherwise.
166 *
167 * @return true if the PDR matches only un-encapsulated packets
168 */
169 public boolean matchesUnencapped() {
170 return type == Type.MATCH_UNENCAPPED ||
171 type == Type.MATCH_UNENCAPPED_NO_ACTION;
172 }
173
174 /**
175 * Get the ID of the PFCP session that produced this PDR.
176 *
177 * @return PFCP session ID
178 */
179 public ImmutableByteSequence sessionId() {
180 return sessionId;
181 }
182
183 /**
184 * Get the UE IP address that this PDR matches on.
185 *
186 * @return UE IP address
187 */
188 public Ip4Address ueAddress() {
189 return ueAddr;
190 }
191
192 /**
193 * Get the identifier of the GTP tunnel that this PDR matches on.
194 *
195 * @return GTP tunnel ID
196 */
197 public ImmutableByteSequence teid() {
198 return teid;
199 }
200
201 /**
202 * Get the destination IP of the GTP tunnel that this PDR matches on.
203 *
204 * @return GTP tunnel destination IP
205 */
206 public Ip4Address tunnelDest() {
207 return tunnelDst;
208 }
209
210 /**
211 * Get the dataplane PDR counter cell ID that this PDR is assigned.
212 *
213 * @return PDR counter cell ID
214 */
215 public int counterId() {
216 return ctrId;
217 }
218
219 /**
220 * Get the PFCP session-local ID of the far that should apply to packets
221 * that this PDR matches.
222 *
223 * @return PFCP session-local FAR ID
224 */
225 public int farId() {
226 return farId;
227 }
228
229 /**
230 * Get the scheduling priority that this PDR is assigned.
231 *
232 * @return scheduling priority
233 */
234 public int schedulingPriority() {
235 return schedulingPriority;
236 }
237
238 /**
239 * This method is used to differentiate between prioritized and non-prioritized
240 * flows.
241 *
242 * @return true if scheduling priority is assigned.
243 */
244 public boolean hasSchedulingPriority() {
245 return schedulingPriority > 0;
246 }
247
248 private enum Type {
249 /**
250 * Match on packets that are encapsulated in a GTP tunnel.
251 */
252 MATCH_ENCAPPED,
253 /**
254 * Match on packets that are not encapsulated in a GTP tunnel.
255 */
256 MATCH_UNENCAPPED,
257 /**
258 * For PDRs that match on encapsulated packets but do not yet have any
259 * action parameters set.
260 * These are usually built in the context of P4Runtime DELETE write requests.
261 */
262 MATCH_ENCAPPED_NO_ACTION,
263 /**
264 * For PDRs that match on unencapsulated packets but do not yet have any
265 * action parameters set.
266 * These are usually built in the context of P4Runtime DELETE write requests.
267 */
268 MATCH_UNENCAPPED_NO_ACTION
269 }
270
271 public static class Builder {
272 private ImmutableByteSequence sessionId = null;
273 private Integer ctrId = null;
274 private Integer localFarId = null;
275 private Integer schedulingPriority = null;
276 private Ip4Address ueAddr = null;
277 private ImmutableByteSequence teid = null;
278 private Ip4Address tunnelDst = null;
279
280 public Builder() {
281
282 }
283
284 /**
285 * Set the ID of the PFCP session that produced this PDR.
286 *
287 * @param sessionId PFCP session ID
288 * @return This builder object
289 */
290 public Builder withSessionId(ImmutableByteSequence sessionId) {
291 this.sessionId = sessionId;
292 return this;
293 }
294
295 /**
296 * Set the ID of the PFCP session that produced this PDR.
297 *
298 * @param sessionId PFCP session ID
299 * @return This builder object
300 */
301 public Builder withSessionId(long sessionId) {
302 try {
303 this.sessionId = ImmutableByteSequence.copyFrom(sessionId)
304 .fit(SESSION_ID_BITWIDTH);
305 } catch (ImmutableByteSequence.ByteSequenceTrimException e) {
306 // This error is literally impossible
307 }
308 return this;
309 }
310
311 /**
312 * Set the UE IP address that this PDR matches on.
313 *
314 * @param ueAddr UE IP address
315 * @return This builder object
316 */
317 public Builder withUeAddr(Ip4Address ueAddr) {
318 this.ueAddr = ueAddr;
319 return this;
320 }
321
322 /**
323 * Set the dataplane PDR counter cell ID that this PDR is assigned.
324 *
325 * @param ctrId PDR counter cell ID
326 * @return This builder object
327 */
328 public Builder withCounterId(int ctrId) {
329 this.ctrId = ctrId;
330 return this;
331 }
332
333
334 /**
335 * Set the PFCP session-local ID of the far that should apply to packets that this PDR matches.
336 *
337 * @param localFarId PFCP session-local FAR ID
338 * @return This builder object
339 */
340 public Builder withLocalFarId(int localFarId) {
341 this.localFarId = localFarId;
342 return this;
343 }
344
345 public Builder withSchedulingPriority(int schedulingPriority) {
346 this.schedulingPriority = schedulingPriority;
347 return this;
348 }
349
350 /**
351 * Set the identifier of the GTP tunnel that this PDR matches on.
352 *
353 * @param teid GTP tunnel ID
354 * @return This builder object
355 */
356 public Builder withTeid(int teid) {
357 this.teid = ImmutableByteSequence.copyFrom(teid);
358 return this;
359 }
360
361 /**
362 * Set the identifier of the GTP tunnel that this PDR matches on.
363 *
364 * @param teid GTP tunnel ID
365 * @return This builder object
366 */
367 public Builder withTeid(ImmutableByteSequence teid) {
368 this.teid = teid;
369 return this;
370 }
371
372 /**
373 * Set the destination IP of the GTP tunnel that this PDR matches on.
374 *
375 * @param tunnelDst GTP tunnel destination IP
376 * @return This builder object
377 */
378 public Builder withTunnelDst(Ip4Address tunnelDst) {
379 this.tunnelDst = tunnelDst;
380 return this;
381 }
382
383 /**
384 * Set the tunnel ID and destination IP of the GTP tunnel that this PDR matches on.
385 *
386 * @param teid GTP tunnel ID
387 * @param tunnelDst GTP tunnel destination IP
388 * @return This builder object
389 */
390 public Builder withTunnel(ImmutableByteSequence teid, Ip4Address tunnelDst) {
391 this.teid = teid;
392 this.tunnelDst = tunnelDst;
393 return this;
394 }
395
396 public PacketDetectionRule build() {
397 // Some match keys are required.
398 checkArgument(
399 (ueAddr != null && teid == null && tunnelDst == null) ||
400 (ueAddr == null && teid != null && tunnelDst != null),
401 "Either a UE address or a TEID and Tunnel destination must be provided, but not both.");
402 // Action parameters are optional but must be all provided together if they are provided
403 checkArgument(
404 (sessionId != null && ctrId != null && localFarId != null && schedulingPriority != null) ||
405 (sessionId == null && ctrId == null && localFarId == null && schedulingPriority == null),
406 "PDR action parameters must be provided together or not at all.");
407 Type type;
408 if (teid != null) {
409 if (sessionId != null) {
410 type = Type.MATCH_ENCAPPED;
411 } else {
412 type = Type.MATCH_ENCAPPED_NO_ACTION;
413 }
414 } else {
415 if (sessionId != null) {
416 type = Type.MATCH_UNENCAPPED;
417 } else {
418 type = Type.MATCH_UNENCAPPED_NO_ACTION;
419 }
420 }
421 return new PacketDetectionRule(sessionId, ctrId, localFarId, schedulingPriority,
422 ueAddr, teid, tunnelDst, type);
423 }
424 }
425}