blob: f607d8704e347bcc7a6209a86c20f92a0ab5880f [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;
Daniele Moro08c9e7f2021-07-28 18:53:34 +020046
47 private final Byte qfi; // QoS Flow Identifier of this PDR
48 private final boolean qfiPush; // True when QFI should be pushed to the packet
49 private final boolean qfiMatch; // True when QFI should be matched
Daniele Moro5e66f982021-06-11 16:41:48 +020050
51 private static final int SESSION_ID_BITWIDTH = 96;
52
53 private PacketDetectionRule(ImmutableByteSequence sessionId, Integer ctrId,
Daniele Moro08c9e7f2021-07-28 18:53:34 +020054 Integer farId, Ip4Address ueAddr, Byte qfi,
55 ImmutableByteSequence teid, Ip4Address tunnelDst,
56 Type type, boolean qfiPush, boolean qfiMatch) {
Daniele Moro5e66f982021-06-11 16:41:48 +020057 this.ueAddr = ueAddr;
58 this.teid = teid;
59 this.tunnelDst = tunnelDst;
60 this.sessionId = sessionId;
61 this.ctrId = ctrId;
62 this.farId = farId;
Daniele Moro08c9e7f2021-07-28 18:53:34 +020063 this.qfi = qfi;
Daniele Moro5e66f982021-06-11 16:41:48 +020064 this.type = type;
Daniele Moro08c9e7f2021-07-28 18:53:34 +020065 this.qfiPush = qfiPush;
66 this.qfiMatch = qfiMatch;
Daniele Moro5e66f982021-06-11 16:41:48 +020067 }
68
69 public static Builder builder() {
70 return new Builder();
71 }
72
73 /**
74 * Return a string representing the match conditions of this PDR.
75 *
76 * @return a string representing the PDR match conditions
77 */
78 public String matchString() {
79 if (matchesEncapped()) {
Daniele Moro08c9e7f2021-07-28 18:53:34 +020080 return matchQfi() ?
81 String.format("Match(Dst=%s, TEID=%s, QFI=%s)", tunnelDest(), teid(), qfi()) :
82 String.format("Match(Dst=%s, TEID=%s)", tunnelDest(), teid());
Daniele Moro5e66f982021-06-11 16:41:48 +020083 } else {
84 return String.format("Match(Dst=%s, !GTP)", ueAddress());
85 }
86 }
87
88 @Override
89 public String toString() {
90 String actionParams = "";
91 if (hasActionParameters()) {
Daniele Moro08c9e7f2021-07-28 18:53:34 +020092 actionParams = hasQfi() && !matchQfi() ?
93 String.format("SEID=%s, FAR=%d, CtrIdx=%d, QFI=%s",
94 sessionId.toString(), farId, ctrId, qfi) :
95 String.format("SEID=%s, FAR=%d, CtrIdx=%d",
96 sessionId.toString(), farId, ctrId);
97 actionParams = pushQfi() ? String.format("%s, QFI_PUSH", actionParams) : actionParams;
Daniele Moro5e66f982021-06-11 16:41:48 +020098 }
99
100 return String.format("PDR{%s -> LoadParams(%s)}",
101 matchString(), actionParams);
102 }
103
104 @Override
105 public boolean equals(Object obj) {
106 if (obj == this) {
107 return true;
108 }
109 if (obj == null) {
110 return false;
111 }
112 if (getClass() != obj.getClass()) {
113 return false;
114 }
115 PacketDetectionRule that = (PacketDetectionRule) obj;
116
117 // Safe comparisons between potentially null objects
118 return (this.type.equals(that.type) &&
119 Objects.equals(this.teid, that.teid) &&
120 Objects.equals(this.tunnelDst, that.tunnelDst) &&
121 Objects.equals(this.ueAddr, that.ueAddr) &&
122 Objects.equals(this.ctrId, that.ctrId) &&
123 Objects.equals(this.sessionId, that.sessionId) &&
124 Objects.equals(this.farId, that.farId) &&
Daniele Moro08c9e7f2021-07-28 18:53:34 +0200125 Objects.equals(this.qfi, that.qfi) &&
126 Objects.equals(this.qfiPush, that.qfiPush) &&
127 Objects.equals(this.qfiMatch, that.qfiMatch));
Daniele Moro5e66f982021-06-11 16:41:48 +0200128 }
129
130 @Override
131 public int hashCode() {
132 return Objects.hash(ueAddr, teid, tunnelDst, sessionId, ctrId, farId,
Daniele Moro08c9e7f2021-07-28 18:53:34 +0200133 qfi, type);
Daniele Moro5e66f982021-06-11 16:41:48 +0200134 }
135
136 /**
137 * Instances created as a result of DELETE write requests will not have
138 * action parameters, only match keys. This method should be used to avoid
139 * null pointer exceptions in those instances.
140 *
141 * @return true if this instance has PDR action parameters, false otherwise.
142 */
143 public boolean hasActionParameters() {
144 return type == Type.MATCH_ENCAPPED || type == Type.MATCH_UNENCAPPED;
145 }
146
147 /**
148 * Return a new instance of this PDR with the action parameters stripped,
149 * leaving only the match keys.
150 *
151 * @return a new PDR with only match keys
152 */
153 public PacketDetectionRule withoutActionParams() {
154 if (matchesEncapped()) {
Daniele Moro08c9e7f2021-07-28 18:53:34 +0200155 PacketDetectionRule.Builder pdrBuilder = PacketDetectionRule.builder()
Daniele Moro5e66f982021-06-11 16:41:48 +0200156 .withTeid(teid)
Daniele Moro08c9e7f2021-07-28 18:53:34 +0200157 .withTunnelDst(tunnelDst);
158 if (this.hasQfi() && this.matchQfi()) {
159 pdrBuilder.withQfiMatch();
160 pdrBuilder.withQfi(qfi);
161 }
162 return pdrBuilder.build();
Daniele Moro5e66f982021-06-11 16:41:48 +0200163 } else {
164 return PacketDetectionRule.builder()
165 .withUeAddr(ueAddr).build();
166 }
167 }
168
169 /**
170 * True if this PDR matches on packets received with a GTP header, and false
171 * otherwise.
172 *
173 * @return true if the PDR matches only encapsulated packets
174 */
175 public boolean matchesEncapped() {
176 return type == Type.MATCH_ENCAPPED ||
177 type == Type.MATCH_ENCAPPED_NO_ACTION;
178 }
179
180 /**
181 * True if this PDR matches on packets received without a GTP header, and
182 * false otherwise.
183 *
184 * @return true if the PDR matches only un-encapsulated packets
185 */
186 public boolean matchesUnencapped() {
187 return type == Type.MATCH_UNENCAPPED ||
188 type == Type.MATCH_UNENCAPPED_NO_ACTION;
189 }
190
191 /**
192 * Get the ID of the PFCP session that produced this PDR.
193 *
194 * @return PFCP session ID
195 */
196 public ImmutableByteSequence sessionId() {
197 return sessionId;
198 }
199
200 /**
201 * Get the UE IP address that this PDR matches on.
202 *
203 * @return UE IP address
204 */
205 public Ip4Address ueAddress() {
206 return ueAddr;
207 }
208
209 /**
210 * Get the identifier of the GTP tunnel that this PDR matches on.
211 *
212 * @return GTP tunnel ID
213 */
214 public ImmutableByteSequence teid() {
215 return teid;
216 }
217
218 /**
219 * Get the destination IP of the GTP tunnel that this PDR matches on.
220 *
221 * @return GTP tunnel destination IP
222 */
223 public Ip4Address tunnelDest() {
224 return tunnelDst;
225 }
226
227 /**
228 * Get the dataplane PDR counter cell ID that this PDR is assigned.
229 *
230 * @return PDR counter cell ID
231 */
232 public int counterId() {
233 return ctrId;
234 }
235
236 /**
237 * Get the PFCP session-local ID of the far that should apply to packets
238 * that this PDR matches.
239 *
240 * @return PFCP session-local FAR ID
241 */
242 public int farId() {
243 return farId;
244 }
245
246 /**
Daniele Moro08c9e7f2021-07-28 18:53:34 +0200247 * Get the QoS Flow Identifier for this PDR.
Daniele Moro5e66f982021-06-11 16:41:48 +0200248 *
Daniele Moro08c9e7f2021-07-28 18:53:34 +0200249 * @return QoS Flow Identifier
Daniele Moro5e66f982021-06-11 16:41:48 +0200250 */
Daniele Moro08c9e7f2021-07-28 18:53:34 +0200251 public byte qfi() {
252 return qfi;
Daniele Moro5e66f982021-06-11 16:41:48 +0200253 }
254
255 /**
Daniele Moro08c9e7f2021-07-28 18:53:34 +0200256 * Returns whether QFi should be pushed to the packet.
Daniele Moro5e66f982021-06-11 16:41:48 +0200257 *
Daniele Moro08c9e7f2021-07-28 18:53:34 +0200258 * @return True if the QFI should be pushed to the packet, false otherwise
Daniele Moro5e66f982021-06-11 16:41:48 +0200259 */
Daniele Moro08c9e7f2021-07-28 18:53:34 +0200260 public boolean pushQfi() {
261 return qfiPush;
262 }
263
264 /**
265 * Returns whether QFI should be matched on the packet or not.
266 *
267 * @return True if QFI should be matched on the packet, false otherwise
268 */
269 public boolean matchQfi() {
270 return qfiMatch;
271 }
272
273 /**
274 * Checks if the PDR has a QFI to match upon or to push on the packet.
275 *
276 * @return True if the PDR has a QFI, false otherwise
277 */
278 public boolean hasQfi() {
279 return qfi != null;
Daniele Moro5e66f982021-06-11 16:41:48 +0200280 }
281
282 private enum Type {
283 /**
284 * Match on packets that are encapsulated in a GTP tunnel.
285 */
286 MATCH_ENCAPPED,
287 /**
288 * Match on packets that are not encapsulated in a GTP tunnel.
289 */
290 MATCH_UNENCAPPED,
291 /**
292 * For PDRs that match on encapsulated packets but do not yet have any
293 * action parameters set.
294 * These are usually built in the context of P4Runtime DELETE write requests.
295 */
296 MATCH_ENCAPPED_NO_ACTION,
297 /**
298 * For PDRs that match on unencapsulated packets but do not yet have any
299 * action parameters set.
300 * These are usually built in the context of P4Runtime DELETE write requests.
301 */
302 MATCH_UNENCAPPED_NO_ACTION
303 }
304
305 public static class Builder {
306 private ImmutableByteSequence sessionId = null;
307 private Integer ctrId = null;
308 private Integer localFarId = null;
309 private Integer schedulingPriority = null;
310 private Ip4Address ueAddr = null;
311 private ImmutableByteSequence teid = null;
312 private Ip4Address tunnelDst = null;
Daniele Moro08c9e7f2021-07-28 18:53:34 +0200313 private Byte qfi = null;
314 private boolean qfiPush = false;
315 private boolean qfiMatch = false;
Daniele Moro5e66f982021-06-11 16:41:48 +0200316
317 public Builder() {
318
319 }
320
321 /**
322 * Set the ID of the PFCP session that produced this PDR.
323 *
324 * @param sessionId PFCP session ID
325 * @return This builder object
326 */
327 public Builder withSessionId(ImmutableByteSequence sessionId) {
328 this.sessionId = sessionId;
329 return this;
330 }
331
332 /**
333 * Set the ID of the PFCP session that produced this PDR.
334 *
335 * @param sessionId PFCP session ID
336 * @return This builder object
337 */
338 public Builder withSessionId(long sessionId) {
339 try {
340 this.sessionId = ImmutableByteSequence.copyFrom(sessionId)
341 .fit(SESSION_ID_BITWIDTH);
342 } catch (ImmutableByteSequence.ByteSequenceTrimException e) {
343 // This error is literally impossible
344 }
345 return this;
346 }
347
348 /**
349 * Set the UE IP address that this PDR matches on.
350 *
351 * @param ueAddr UE IP address
352 * @return This builder object
353 */
354 public Builder withUeAddr(Ip4Address ueAddr) {
355 this.ueAddr = ueAddr;
356 return this;
357 }
358
359 /**
360 * Set the dataplane PDR counter cell ID that this PDR is assigned.
361 *
362 * @param ctrId PDR counter cell ID
363 * @return This builder object
364 */
365 public Builder withCounterId(int ctrId) {
366 this.ctrId = ctrId;
367 return this;
368 }
369
370
371 /**
372 * Set the PFCP session-local ID of the far that should apply to packets that this PDR matches.
373 *
374 * @param localFarId PFCP session-local FAR ID
375 * @return This builder object
376 */
377 public Builder withLocalFarId(int localFarId) {
378 this.localFarId = localFarId;
379 return this;
380 }
381
Daniele Moro08c9e7f2021-07-28 18:53:34 +0200382 /**
383 * Set the QoS Flow Identifier for this PDR.
384 *
385 * @param qfi GTP Tunnel QFI
386 * @return This builder object
387 */
388 public Builder withQfi(byte qfi) {
389 this.qfi = qfi;
390 return this;
391 }
392
393 /**
394 * The QoS Flow Identifier should be pushed to the packet.
395 * This is valid for downstream packets and for 5G traffic only.
396 *
397 * @return This builder object
398 */
399 public Builder withQfiPush() {
400 this.qfiPush = true;
401 return this;
402 }
403
404 /**
405 * The QoS Flow Identifier should be matched to the packet.
406 * This is valid for upstream packets and for 5G traffic only.
407 *
408 * @return This builder object
409 */
410 public Builder withQfiMatch() {
411 this.qfiMatch = true;
Daniele Moro5e66f982021-06-11 16:41:48 +0200412 return this;
413 }
414
415 /**
416 * Set the identifier of the GTP tunnel that this PDR matches on.
417 *
418 * @param teid GTP tunnel ID
419 * @return This builder object
420 */
421 public Builder withTeid(int teid) {
422 this.teid = ImmutableByteSequence.copyFrom(teid);
423 return this;
424 }
425
426 /**
427 * Set the identifier of the GTP tunnel that this PDR matches on.
428 *
429 * @param teid GTP tunnel ID
430 * @return This builder object
431 */
432 public Builder withTeid(ImmutableByteSequence teid) {
433 this.teid = teid;
434 return this;
435 }
436
437 /**
438 * Set the destination IP of the GTP tunnel that this PDR matches on.
439 *
440 * @param tunnelDst GTP tunnel destination IP
441 * @return This builder object
442 */
443 public Builder withTunnelDst(Ip4Address tunnelDst) {
444 this.tunnelDst = tunnelDst;
445 return this;
446 }
447
448 /**
449 * Set the tunnel ID and destination IP of the GTP tunnel that this PDR matches on.
450 *
451 * @param teid GTP tunnel ID
452 * @param tunnelDst GTP tunnel destination IP
453 * @return This builder object
454 */
455 public Builder withTunnel(ImmutableByteSequence teid, Ip4Address tunnelDst) {
456 this.teid = teid;
457 this.tunnelDst = tunnelDst;
458 return this;
459 }
460
Daniele Moro08c9e7f2021-07-28 18:53:34 +0200461 /**
462 * Set the tunnel ID, destination IP and QFI of the GTP tunnel that this PDR matches on.
463 *
464 * @param teid GTP tunnel ID
465 * @param tunnelDst GTP tunnel destination IP
466 * @param qfi GTP QoS Flow Identifier
467 * @return This builder object
468 */
469 public Builder withTunnel(ImmutableByteSequence teid, Ip4Address tunnelDst, byte qfi) {
470 this.teid = teid;
471 this.tunnelDst = tunnelDst;
472 this.qfi = qfi;
473 return this;
474 }
475
Daniele Moro5e66f982021-06-11 16:41:48 +0200476 public PacketDetectionRule build() {
477 // Some match keys are required.
478 checkArgument(
479 (ueAddr != null && teid == null && tunnelDst == null) ||
480 (ueAddr == null && teid != null && tunnelDst != null),
481 "Either a UE address or a TEID and Tunnel destination must be provided, but not both.");
482 // Action parameters are optional but must be all provided together if they are provided
483 checkArgument(
Daniele Moro08c9e7f2021-07-28 18:53:34 +0200484 (sessionId != null && ctrId != null && localFarId != null) ||
485 (sessionId == null && ctrId == null && localFarId == null),
Daniele Moro5e66f982021-06-11 16:41:48 +0200486 "PDR action parameters must be provided together or not at all.");
Daniele Moro08c9e7f2021-07-28 18:53:34 +0200487 checkArgument(!qfiPush || !qfiMatch,
488 "Either match of push QFI can be true, not both.");
489 checkArgument(!qfiPush || qfi != null,
490 "A QFI must be provided when pushing QFI to the packet.");
491 checkArgument(!qfiMatch || qfi != null,
492 "A QFI must be provided when matching QFI on the packet.");
Daniele Moro5e66f982021-06-11 16:41:48 +0200493 Type type;
494 if (teid != null) {
495 if (sessionId != null) {
496 type = Type.MATCH_ENCAPPED;
497 } else {
498 type = Type.MATCH_ENCAPPED_NO_ACTION;
499 }
500 } else {
501 if (sessionId != null) {
502 type = Type.MATCH_UNENCAPPED;
503 } else {
504 type = Type.MATCH_UNENCAPPED_NO_ACTION;
505 }
506 }
Daniele Moro08c9e7f2021-07-28 18:53:34 +0200507 return new PacketDetectionRule(sessionId, ctrId, localFarId, ueAddr,
508 qfi, teid, tunnelDst, type,
509 qfiPush, qfiMatch);
Daniele Moro5e66f982021-06-11 16:41:48 +0200510 }
511 }
512}