package net.floodlightcontroller.util;
import java.util.ArrayList;
import java.util.List;
import net.floodlightcontroller.core.IOFSwitch;
import org.projectfloodlight.openflow.protocol.OFAsyncGetReply;
import org.projectfloodlight.openflow.protocol.OFBarrierReply;
import org.projectfloodlight.openflow.protocol.OFBundleCtrlMsg;
import org.projectfloodlight.openflow.protocol.OFBundleCtrlType;
import org.projectfloodlight.openflow.protocol.OFEchoReply;
import org.projectfloodlight.openflow.protocol.OFFactories;
import org.projectfloodlight.openflow.protocol.OFFeaturesReply;
import org.projectfloodlight.openflow.protocol.OFGetConfigReply;
import org.projectfloodlight.openflow.protocol.OFMessage;
import org.projectfloodlight.openflow.protocol.OFPacketIn;
import org.projectfloodlight.openflow.protocol.OFPacketOut;
import org.projectfloodlight.openflow.protocol.OFQueueGetConfigReply;
import org.projectfloodlight.openflow.protocol.OFRoleReply;
import org.projectfloodlight.openflow.protocol.OFStatsReply;
import org.projectfloodlight.openflow.protocol.OFVersion;
import org.projectfloodlight.openflow.protocol.action.OFAction;
import org.projectfloodlight.openflow.protocol.match.MatchField;
import org.projectfloodlight.openflow.types.OFBufferId;
import org.projectfloodlight.openflow.types.OFPort;
import org.projectfloodlight.openflow.types.OFVlanVidMatch;
/**
* Tools to help work with OFMessages.
*
* Compare OFMessage-extending objects (e.g. OFFlowMod, OFPacketIn)
* where the XID does not matter. This is especially useful for
* unit testing where the XID of the OFMessage might vary whereas
* the expected OFMessgae must have a set XID.
*
* @author Ryan Izard <ryan.izard@bigswitch.com, rizard@g.clemson.edu>
*/
public class OFMessageUtils {
/**
* Prevent instantiation
*/
private OFMessageUtils() {};
/**
* Simple class to streamline the use of OFMessage's
* equalsIgnoreXid() and hashCodeIgnoreXid() functions.
* Use this class to wrap OFMessages prior to inserting
* them in containers where lookup or equality checks
* should not include the XID.
*
* See {@link net.floodlightcontroller.util.OFMessageDamper}
* as an example where it's used to help cache OFMessages.
* @author rizard
*/
public static class OFMessageIgnoreXid {
private OFMessage m;
private OFMessageIgnoreXid() {}
private OFMessageIgnoreXid(OFMessage m) {
this.m = m;
}
/**
* Wrap an OFMessage to ignore the XID
* when checking for equality or computing
* the OFMessage's hash.
* @param m
* @return
*/
public static OFMessageIgnoreXid of(OFMessage m) {
return new OFMessageIgnoreXid(m);
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((m == null) ? 0 : m.hashCodeIgnoreXid());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
OFMessageIgnoreXid other = (OFMessageIgnoreXid) obj;
if (m == null) {
if (other.m != null)
return false;
} else if (!m.equalsIgnoreXid(other.m))
return false;
return true;
}
}
/**
* Get the ingress port of a packet-in message. The manner in which
* this is done depends on the OpenFlow version. OF1.0 and 1.1 have
* a specific in_port field, while OF1.2+ store this information in
* the packet-in's match field.
*
* @param pi, the OFPacketIn
* @return the ingress OFPort
*/
public static OFPort getInPort(OFPacketIn pi) {
return pi.getVersion().compareTo(OFVersion.OF_12) < 0 ? pi.getInPort() : pi.getMatch().get(MatchField.IN_PORT);
}
/**
* Set the ingress port of a packet-out message. The manner in which
* this is done depends on the OpenFlow version. OF1.0 thru 1.4 have
* a specific in_port field, while OF1.5+ store this information in
* the packet-out's match field.
*
* @param pob, the OFPacketOut.Builder within which to set the in port
* @param in, the ingress OFPort
*/
public static void setInPort(OFPacketOut.Builder pob, OFPort in) {
if (pob.getVersion().compareTo(OFVersion.OF_15) < 0) {
pob.setInPort(in);
} else if (pob.getMatch() != null) {
pob.getMatch().createBuilder()
.setExact(MatchField.IN_PORT, in)
.build();
} else {
pob.setMatch(OFFactories.getFactory(pob.getVersion())
.buildMatch()
.setExact(MatchField.IN_PORT, in)
.build());
}
}
/**
* Get the VLAN on which this packet-in message was received.
* @param pi, the OFPacketIn
* @return the VLAN
*/
public static OFVlanVidMatch getVlan(OFPacketIn pi) {
return pi.getMatch().get(MatchField.VLAN_VID) == null ? OFVlanVidMatch.UNTAGGED : pi.getMatch().get(MatchField.VLAN_VID);
}
/**
* Writes an OFPacketOut message to a switch.
*
* @param sw
* The switch to write the PacketOut to.
* @param packetInMessage
* The corresponding PacketIn.
* @param egressPort
* The switchport to output the PacketOut.
*/
public static void writePacketOutForPacketIn(IOFSwitch sw,
OFPacketIn packetInMessage, OFPort egressPort) {
OFPacketOut.Builder pob = sw.getOFFactory().buildPacketOut();
// Set buffer_id, in_port, actions_len
pob.setBufferId(packetInMessage.getBufferId());
pob.setInPort(packetInMessage.getVersion().compareTo(OFVersion.OF_12) < 0 ? packetInMessage
.getInPort() : packetInMessage.getMatch().get(
MatchField.IN_PORT));
// set actions
List<OFAction> actions = new ArrayList<OFAction>(1);
actions.add(sw.getOFFactory().actions().buildOutput()
.setPort(egressPort).setMaxLen(0xffFFffFF).build());
pob.setActions(actions);
// set data - only if buffer_id == -1
if (packetInMessage.getBufferId() == OFBufferId.NO_BUFFER) {
byte[] packetData = packetInMessage.getData();
pob.setData(packetData);
}
// and write it out
sw.write(pob.build());
}
public static boolean isReplyForRequest(OFMessage request, OFMessage reply) {
switch (request.getType()) {
case BARRIER_REQUEST:
return (reply instanceof OFBarrierReply);
case BUNDLE_CONTROL:
return isBundleCtrlReplyForRequest((OFBundleCtrlMsg) request, reply);
case FEATURES_REQUEST:
return (reply instanceof OFFeaturesReply);
case GET_ASYNC_REQUEST:
return (reply instanceof OFAsyncGetReply);
case GET_CONFIG_REQUEST:
return (reply instanceof OFGetConfigReply);
case QUEUE_GET_CONFIG_REQUEST:
return (reply instanceof OFQueueGetConfigReply);
case ROLE_REQUEST:
return (reply instanceof OFRoleReply);
case STATS_REQUEST:
return (reply instanceof OFStatsReply);
case ECHO_REQUEST:
return (reply instanceof OFEchoReply);
default:
return false;
}
}
private static boolean isBundleCtrlReplyForRequest(OFBundleCtrlMsg request, OFMessage reply) {
if (!(reply instanceof OFBundleCtrlMsg))
return false;
OFBundleCtrlMsg ctrlReply = (OFBundleCtrlMsg) reply;
switch (request.getBundleCtrlType()) {
case OPEN_REQUEST:
return ctrlReply.getBundleCtrlType() == OFBundleCtrlType.OPEN_REPLY;
case CLOSE_REQUEST:
return ctrlReply.getBundleCtrlType() == OFBundleCtrlType.CLOSE_REPLY;
case COMMIT_REQUEST:
return ctrlReply.getBundleCtrlType() == OFBundleCtrlType.COMMIT_REPLY;
default:
return false;
}
}
}