package net.onrc.onos.core.intent; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Objects; import java.util.Set; import net.floodlightcontroller.util.MACAddress; import net.onrc.onos.core.intent.IntentOperation.Operator; import org.projectfloodlight.openflow.protocol.OFActionType; import org.projectfloodlight.openflow.protocol.OFFactory; import org.projectfloodlight.openflow.protocol.OFFlowMod; import org.projectfloodlight.openflow.protocol.action.OFAction; import org.projectfloodlight.openflow.protocol.action.OFActionOutput; import org.projectfloodlight.openflow.protocol.match.Match.Builder; import org.projectfloodlight.openflow.types.OFBufferId; import org.projectfloodlight.openflow.types.OFPort; import org.projectfloodlight.openflow.types.U64; /** * A class to represent an OpenFlow FlowMod. <br> * It is OpenFlow v.1.0-centric and contains a Match and an Action. */ public class FlowEntry { public static final int PRIORITY_DEFAULT = 32768; // Default Flow Priority protected long sw; protected Match match; protected Set<Action> actions; protected Operator operator; protected int hardTimeout = 0; protected int idleTimeout = 0; protected long flowEntryId; /** * Constructor. * * @param sw switch's DPID * @param srcPort source port on switch * @param dstPort output port on switch * @param srcMac source Ethernet MAC address * @param dstMac destination Ethernet MAC address * @param srcIpAddress source IP address * @param dstIpAddress destination IP address * @param operator OpenFlow operation/command (add, remove, etc.) */ public FlowEntry(long sw, long srcPort, long dstPort, MACAddress srcMac, MACAddress dstMac, int srcIpAddress, int dstIpAddress, Operator operator) { this.sw = sw; this.match = new Match(sw, srcPort, srcMac, dstMac, srcIpAddress, dstIpAddress); this.actions = new HashSet<Action>(); this.actions.add(new ForwardAction(dstPort)); this.operator = operator; this.flowEntryId = hashCode(); } /** * Gets the switch for this FlowEntry. * * @return the switch's DPID */ public long getSwitch() { return sw; } /** * Gets the operator (i.e. add, remove, error). * * @return the operator */ public Operator getOperator() { return operator; } /** * Sets the FlowMod operation (i.e. add, remove, error). * * @param op the operator */ public void setOperator(Operator op) { operator = op; } /** * Gets hard timeout value in seconds. * * @return the hard timeout value in seconds */ public int getHardTimeout() { return hardTimeout; } /** * Sets hard timeout value in seconds. * * @param hardTimeout hard timeout value in seconds */ public void setHardTimeout(int hardTimeout) { this.hardTimeout = hardTimeout; } /** * Gets idle timeout value in seconds. * * @return the idle timeout value in seconds */ public int getIdleTimeout() { return idleTimeout; } /** * Sets idle timeout value in seconds. * * @param idleTimeout idle timeout value in seconds */ public void setIdleTimeout(int idleTimeout) { this.idleTimeout = idleTimeout; } /** * Gets flowEntryId. * * @return the flowEntryId to be set in cookie */ public long getFlowEntryId() { return flowEntryId; } /** * Sets flowEntryId. * * @param flowEntryId flowEntryId to be set in cookie */ public void setFlowEntryId(long flowEntryId) { this.flowEntryId = flowEntryId; } /** * Builds and returns an OFFlowMod given an OFFactory. * * @param factory the OFFactory to use for building * @return the OFFlowMod */ public OFFlowMod buildFlowMod(OFFactory factory) { OFFlowMod.Builder builder = null; switch (operator) { case ADD: // XXX Workaround for semantic differences between OF1.0 and 1.3. // The original code for 1.0 used MODIFY_STRICT but in 1.3 a // MODIFY* command won't create the flow entry if it doesn't // already exist. builder = factory.buildFlowAdd(); break; case REMOVE: builder = factory.buildFlowDeleteStrict(); break; default: // TODO throw error? return null; } // Build OFMatch Builder matchBuilder = match.getOFMatchBuilder(factory); // Build OFAction Set List<OFAction> actionList = new ArrayList<>(actions.size()); for (Action action : actions) { actionList.add(action.getOFAction(factory)); } OFPort outp = OFPort.of((short) 0xffff); // OF1.0 OFPP.NONE if (operator == Operator.REMOVE) { if (actionList.size() == 1) { if (actionList.get(0).getType() == OFActionType.OUTPUT) { OFActionOutput oa = (OFActionOutput) actionList.get(0); outp = oa.getPort(); } } } // Build OFFlowMod builder.setMatch(matchBuilder.build()) .setActions(actionList) .setIdleTimeout(idleTimeout) .setHardTimeout(hardTimeout) .setCookie(U64.of(flowEntryId)) .setBufferId(OFBufferId.NO_BUFFER) .setPriority(PRIORITY_DEFAULT) .setOutPort(outp); /* Note: The following are NOT USED. * builder.setFlags() * builder.setInstructions() * builder.setOutGroup() * builder.setTableId() * builder.setXid() */ // TODO from Flow Pusher // Set the OFPFF_SEND_FLOW_REM flag if the Flow Entry is not // permanent. // // if ((flowEntry.idleTimeout() != 0) || // (flowEntry.hardTimeout() != 0)) { // fm.setFlags(OFFlowMod.OFPFF_SEND_FLOW_REM); // } // TODO do we care? // fm.setOutPort(OFPort.OFPP_NONE.getValue()); // if ((flowModCommand == OFFlowMod.OFPFC_DELETE) // || (flowModCommand == OFFlowMod.OFPFC_DELETE_STRICT)) { // if (actionOutputPort.portNumber != null) { // fm.setOutPort(actionOutputPort.portNumber); // } // } // TODO // Set the OFPFF_SEND_FLOW_REM flag if the Flow Entry is not // permanent. // // if ((flowEntry.idleTimeout() != 0) || // (flowEntry.hardTimeout() != 0)) { // fm.setFlags(OFFlowMod.OFPFF_SEND_FLOW_REM); // } return builder.build(); } /** * Returns a String representation of this FlowEntry. * * @return the FlowEntry as a String */ @Override public String toString() { return match + "->" + actions; } /** * Generates hash using Objects.hash() on the match and actions. */ @Override public final int hashCode() { return Objects.hash(match, actions); } /** * Flow Entries are equal if their matches and action sets are equal. * * @return true if equal, false otherwise */ @Override public boolean equals(Object o) { if (!(o instanceof FlowEntry)) { return false; } FlowEntry other = (FlowEntry) o; // Note: we should not consider the operator for this comparison return this.match.equals(other.match) && this.actions.containsAll(other.actions) && other.actions.containsAll(this.actions); } }