| /* |
| * Copyright 2021-present Open Networking Foundation |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package org.onosproject.net.behaviour.upf; |
| |
| import org.onlab.packet.Ip4Address; |
| import org.onlab.util.ImmutableByteSequence; |
| |
| import java.util.Objects; |
| |
| import static com.google.common.base.Preconditions.checkNotNull; |
| |
| /** |
| * A single Forwarding Action Rule (FAR), an entity described in the 3GPP |
| * specifications (although that does not mean that this class is 3GPP |
| * compliant). An instance of this class will be generated by a logical switch |
| * write request to the database-style FAR P4 table, and the resulting instance |
| * should contain all the information needed to reproduce that logical switch |
| * FAR in the event of a client read request. The instance should also contain |
| * sufficient information (or expose the means to retrieve such information) to |
| * generate the corresponding dataplane forwarding state that implements the FAR. |
| */ |
| public final class ForwardingActionRule { |
| // Match Keys |
| private final ImmutableByteSequence sessionId; // The PFCP session identifier that created this FAR |
| private final int farId; // PFCP session-local identifier for this FAR |
| // Action parameters |
| private final boolean notifyFlag; // Should this FAR notify the control plane when it sees a packet? |
| private final boolean dropFlag; |
| private final boolean bufferFlag; |
| private final GtpTunnel tunnel; // The GTP tunnel that this FAR should encapsulate packets with (if downlink) |
| |
| private static final int SESSION_ID_BITWIDTH = 96; |
| |
| private ForwardingActionRule(ImmutableByteSequence sessionId, Integer farId, |
| boolean notifyFlag, GtpTunnel tunnel, boolean dropFlag, boolean bufferFlag) { |
| this.sessionId = sessionId; |
| this.farId = farId; |
| this.notifyFlag = notifyFlag; |
| this.tunnel = tunnel; |
| this.dropFlag = dropFlag; |
| this.bufferFlag = bufferFlag; |
| } |
| |
| /** |
| * Return a new instance of this FAR with the action parameters stripped, leaving only the match keys. |
| * |
| * @return a new FAR with only match keys |
| */ |
| public ForwardingActionRule withoutActionParams() { |
| return ForwardingActionRule.builder() |
| .setFarId(farId) |
| .withSessionId(sessionId) |
| .build(); |
| } |
| |
| public static Builder builder() { |
| return new Builder(); |
| } |
| |
| /** |
| * Return a string representing the dataplane action applied by this FAR. |
| * |
| * @return a string representing the FAR action |
| */ |
| public String actionString() { |
| String actionName; |
| String actionParams = ""; |
| if (dropFlag) { |
| actionName = "Drop"; |
| } else if (bufferFlag) { |
| actionName = "Buffer"; |
| } else if (tunnel != null) { |
| actionName = "Encap"; |
| actionParams = String.format("Src=%s, SPort=%d, TEID=%s, Dst=%s", |
| tunnel.src().toString(), tunnel.srcPort(), |
| tunnel.teid().toString(), tunnel.dst().toString()); |
| } else { |
| actionName = "Forward"; |
| } |
| if (notifyFlag) { |
| actionName += "+NotifyCP"; |
| } |
| |
| return String.format("%s(%s)", actionName, actionParams); |
| } |
| |
| @Override |
| public String toString() { |
| String matchKeys = String.format("ID=%d, SEID=%s", farId, sessionId.toString()); |
| String actionString = actionString(); |
| |
| return String.format("FAR{Match(%s) -> %s}", matchKeys, actionString); |
| } |
| |
| @Override |
| public boolean equals(Object obj) { |
| if (obj == this) { |
| return true; |
| } |
| if (obj == null) { |
| return false; |
| } |
| if (getClass() != obj.getClass()) { |
| return false; |
| } |
| ForwardingActionRule that = (ForwardingActionRule) obj; |
| |
| // Safe comparisons between potentially null objects |
| return (this.dropFlag == that.dropFlag && |
| this.bufferFlag == that.bufferFlag && |
| this.notifyFlag == that.notifyFlag && |
| this.farId == that.farId && |
| Objects.equals(this.tunnel, that.tunnel) && |
| Objects.equals(this.sessionId, that.sessionId)); |
| } |
| |
| @Override |
| public int hashCode() { |
| return Objects.hash(sessionId, farId, notifyFlag, tunnel, dropFlag, bufferFlag); |
| } |
| |
| /** |
| * Get the ID of the PFCP Session that produced this FAR. |
| * |
| * @return PFCP session ID |
| */ |
| public ImmutableByteSequence sessionId() { |
| return sessionId; |
| } |
| |
| /** |
| * Get the PFCP session-local ID of the FAR that should apply to packets that match this PDR. |
| * |
| * @return PFCP session-local FAR ID |
| */ |
| public int farId() { |
| return farId; |
| } |
| |
| /** |
| * True if this FAR does not drop packets. |
| * |
| * @return true if FAR is forwards |
| */ |
| public boolean forwards() { |
| return !dropFlag; |
| } |
| |
| /** |
| * True if this FAR encapsulates packets in a GTP tunnel, and false otherwise. |
| * |
| * @return true is FAR encapsulates |
| */ |
| public boolean encaps() { |
| return tunnel != null; |
| } |
| |
| /** |
| * Returns true if this FAR drops packets, and false otherwise. |
| * |
| * @return true if this FAR drops |
| */ |
| public boolean drops() { |
| return dropFlag; |
| } |
| |
| /** |
| * Returns true if this FAR notifies the control plane on receiving a packet, and false otherwise. |
| * |
| * @return true if this FAR notifies the cp |
| */ |
| public boolean notifies() { |
| return notifyFlag; |
| } |
| |
| |
| /** |
| * Returns true if this FAR buffers incoming packets, and false otherwise. |
| * |
| * @return true if this FAR buffers |
| */ |
| public boolean buffers() { |
| return bufferFlag; |
| } |
| |
| /** |
| * A description of the tunnel that this FAR will encapsulate packets with, if it is a downlink FAR. If the FAR |
| * is uplink, there will be no such tunnel and this method wil return null. |
| * |
| * @return A GtpTunnel instance containing a tunnel sourceIP, destIP, and GTPU TEID, or null if the FAR is uplink. |
| */ |
| public GtpTunnel tunnel() { |
| return tunnel; |
| } |
| |
| /** |
| * Get the source UDP port of the GTP tunnel that this FAR will encapsulate packets with. |
| * |
| * @return GTP tunnel source UDP port |
| */ |
| public Short tunnelSrcPort() { |
| return tunnel != null ? tunnel.srcPort() : null; |
| } |
| |
| /** |
| * Get the source IP of the GTP tunnel that this FAR will encapsulate packets with. |
| * |
| * @return GTP tunnel source IP |
| */ |
| public Ip4Address tunnelSrc() { |
| if (tunnel == null) { |
| return null; |
| } |
| return tunnel.src(); |
| } |
| |
| /** |
| * Get the destination IP of the GTP tunnel that this FAR will encapsulate packets with. |
| * |
| * @return GTP tunnel destination IP |
| */ |
| public Ip4Address tunnelDst() { |
| if (tunnel == null) { |
| return null; |
| } |
| return tunnel.dst(); |
| } |
| |
| /** |
| * Get the identifier of the GTP tunnel that this FAR will encapsulate packets with. |
| * |
| * @return GTP tunnel ID |
| */ |
| public ImmutableByteSequence teid() { |
| if (tunnel == null) { |
| return null; |
| } |
| return tunnel.teid(); |
| } |
| |
| public static class Builder { |
| private ImmutableByteSequence sessionId = null; |
| private Integer farId = null; |
| private GtpTunnel tunnel = null; |
| private boolean dropFlag = false; |
| private boolean bufferFlag = false; |
| private boolean notifyCp = false; |
| |
| public Builder() { |
| } |
| |
| /** |
| * Set the ID of the PFCP session that created this FAR. |
| * |
| * @param sessionId PFC session ID |
| * @return This builder object |
| */ |
| public Builder withSessionId(ImmutableByteSequence sessionId) { |
| this.sessionId = sessionId; |
| return this; |
| } |
| |
| /** |
| * Set the ID of the PFCP session that created this FAR. |
| * |
| * @param sessionId PFC session ID |
| * @return This builder object |
| */ |
| public Builder withSessionId(long sessionId) { |
| try { |
| this.sessionId = ImmutableByteSequence.copyFrom(sessionId).fit(SESSION_ID_BITWIDTH); |
| } catch (ImmutableByteSequence.ByteSequenceTrimException e) { |
| // This error is literally impossible |
| } |
| return this; |
| } |
| |
| /** |
| * Set the PFCP Session-local ID of this FAR. |
| * |
| * @param farId PFCP session-local FAR ID |
| * @return This builder object |
| */ |
| public Builder setFarId(int farId) { |
| this.farId = farId; |
| return this; |
| } |
| |
| /** |
| * Make this FAR forward incoming packets. |
| * |
| * @param flag the flag value to set |
| * @return This builder object |
| */ |
| public Builder setForwardFlag(boolean flag) { |
| this.dropFlag = !flag; |
| return this; |
| } |
| |
| /** |
| * Make this FAR drop incoming packets. |
| * |
| * @param flag the flag value to set |
| * @return This builder object |
| */ |
| public Builder setDropFlag(boolean flag) { |
| this.dropFlag = flag; |
| return this; |
| } |
| |
| /** |
| * Make this FAR buffer incoming packets. |
| * |
| * @param flag the flag value to set |
| * @return This builder object |
| */ |
| public Builder setBufferFlag(boolean flag) { |
| this.bufferFlag = flag; |
| return this; |
| } |
| |
| /** |
| * Set a flag specifying if the control plane should be notified when this FAR is hit. |
| * |
| * @param notifyCp true if FAR notifies control plane |
| * @return This builder object |
| */ |
| public Builder setNotifyFlag(boolean notifyCp) { |
| this.notifyCp = notifyCp; |
| return this; |
| } |
| |
| /** |
| * Set the GTP tunnel that this FAR should encapsulate packets with. |
| * |
| * @param tunnel GTP tunnel |
| * @return This builder object |
| */ |
| public Builder setTunnel(GtpTunnel tunnel) { |
| this.tunnel = tunnel; |
| return this; |
| } |
| |
| /** |
| * Set the unidirectional GTP tunnel that this FAR should encapsulate packets with. |
| * |
| * @param src GTP tunnel source IP |
| * @param dst GTP tunnel destination IP |
| * @param teid GTP tunnel ID |
| * @return This builder object |
| */ |
| public Builder setTunnel(Ip4Address src, Ip4Address dst, ImmutableByteSequence teid) { |
| return this.setTunnel(GtpTunnel.builder() |
| .setSrc(src) |
| .setDst(dst) |
| .setTeid(teid) |
| .build()); |
| } |
| |
| /** |
| * Set the unidirectional GTP tunnel that this FAR should encapsulate packets with. |
| * |
| * @param src GTP tunnel source IP |
| * @param dst GTP tunnel destination IP |
| * @param teid GTP tunnel ID |
| * @param srcPort GTP tunnel UDP source port (destination port is hardcoded as 2152) |
| * @return This builder object |
| */ |
| public Builder setTunnel(Ip4Address src, Ip4Address dst, ImmutableByteSequence teid, short srcPort) { |
| return this.setTunnel(GtpTunnel.builder() |
| .setSrc(src) |
| .setDst(dst) |
| .setTeid(teid) |
| .setSrcPort(srcPort) |
| .build()); |
| } |
| |
| public ForwardingActionRule build() { |
| // All match keys are required |
| checkNotNull(sessionId, "Session ID is required"); |
| checkNotNull(farId, "FAR ID is required"); |
| return new ForwardingActionRule(sessionId, farId, notifyCp, tunnel, dropFlag, bufferFlag); |
| } |
| } |
| } |