diff --git a/src/main/java/com/bigswitch/floodlight/vendor/OFActionBigSwitchVendor.java b/src/main/java/com/bigswitch/floodlight/vendor/OFActionBigSwitchVendor.java new file mode 100644 index 0000000000000000000000000000000000000000..147820f479bea7596961e29225931f5856118648 --- /dev/null +++ b/src/main/java/com/bigswitch/floodlight/vendor/OFActionBigSwitchVendor.java @@ -0,0 +1,70 @@ +package com.bigswitch.floodlight.vendor; + +import org.jboss.netty.buffer.ChannelBuffer; +import org.openflow.protocol.action.OFActionVendor; + +public abstract class OFActionBigSwitchVendor extends OFActionVendor { + public static int MINIMUM_LENGTH = 12; + public static int BSN_VENDOR_ID = OFBigSwitchVendorData.BSN_VENDOR_ID; + + protected int subtype; + + protected OFActionBigSwitchVendor(int subtype) { + super(); + super.setLength((short)MINIMUM_LENGTH); + super.setVendor(BSN_VENDOR_ID); + this.subtype = subtype; + } + + public int getSubtype() { + return this.subtype; + } + + public void setSubtype(int subtype) { + this.subtype = subtype; + } + + @Override + public void readFrom(ChannelBuffer data) { + super.readFrom(data); + this.subtype = data.readInt(); + } + + @Override + public void writeTo(ChannelBuffer data) { + super.writeTo(data); + data.writeInt(this.subtype); + } + + @Override + public int hashCode() { + final int prime = 379; + int result = super.hashCode(); + result = prime * result + vendor; + result = prime * result + subtype; + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (!super.equals(obj)) { + return false; + } + if (!(obj instanceof OFActionBigSwitchVendor)) { + return false; + } + OFActionBigSwitchVendor other = (OFActionBigSwitchVendor) obj; + if (subtype != other.subtype) { + return false; + } + return true; + } + + @Override + public String toString() { + return super.toString() + "; subtype=" + subtype; + } +} diff --git a/src/main/java/com/bigswitch/floodlight/vendor/OFActionMirror.java b/src/main/java/com/bigswitch/floodlight/vendor/OFActionMirror.java new file mode 100644 index 0000000000000000000000000000000000000000..e8a5bbfc153b652286d0601780d24cdffd7de641 --- /dev/null +++ b/src/main/java/com/bigswitch/floodlight/vendor/OFActionMirror.java @@ -0,0 +1,98 @@ +package com.bigswitch.floodlight.vendor; + +import org.jboss.netty.buffer.ChannelBuffer; + + +public class OFActionMirror extends OFActionBigSwitchVendor { + public final static int MINIMUM_LENGTH = 12; + public final static int BSN_ACTION_MIRROR = 1; + + protected int destPort; + protected int vlanTag; + protected byte copyStage; + protected byte pad0; + protected byte pad1; + protected byte pad2; + + public OFActionMirror(short portNumber) { + super(BSN_ACTION_MIRROR); + super.setLength((short) (OFActionBigSwitchVendor.MINIMUM_LENGTH + OFActionMirror.MINIMUM_LENGTH)); + this.destPort = portNumber; + this.vlanTag = 0; + this.copyStage = 0; + } + + public int getDestPort() { + return destPort; + } + + public void setDestPort(int destPort) { + this.destPort = destPort; + } + + public int getVlanTag() { + return vlanTag; + } + + public void setVlanTag(int vlanTag) { + this.vlanTag = vlanTag; + } + + public byte getCopyStage() { + return copyStage; + } + + public void setCopyStage(byte copyStage) { + this.copyStage = copyStage; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = super.hashCode(); + result = prime * result + copyStage; + result = prime * result + destPort; + result = prime * result + pad0; + result = prime * result + pad1; + result = prime * result + pad2; + result = prime * result + vlanTag; + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (!super.equals(obj)) return false; + if (getClass() != obj.getClass()) return false; + OFActionMirror other = (OFActionMirror) obj; + if (copyStage != other.copyStage) return false; + if (destPort != other.destPort) return false; + if (pad0 != other.pad0) return false; + if (pad1 != other.pad1) return false; + if (pad2 != other.pad2) return false; + if (vlanTag != other.vlanTag) return false; + return true; + } + + @Override + public void readFrom(ChannelBuffer data) { + super.readFrom(data); + this.destPort = data.readInt(); + this.vlanTag = data.readInt(); + this.copyStage = data.readByte(); + this.pad0 = data.readByte(); + this.pad1 = data.readByte(); + this.pad2 = data.readByte(); + } + + @Override + public void writeTo(ChannelBuffer data) { + super.writeTo(data); + data.writeInt(this.destPort); + data.writeInt(this.vlanTag); + data.writeByte(this.copyStage); + data.writeByte(this.pad0); + data.writeByte(this.pad1); + data.writeByte(this.pad2); + } +} diff --git a/src/main/java/com/bigswitch/floodlight/vendor/OFActionNiciraTtlDecrement.java b/src/main/java/com/bigswitch/floodlight/vendor/OFActionNiciraTtlDecrement.java new file mode 100644 index 0000000000000000000000000000000000000000..943fb26de2a109c75e05ede2dc2fe8b207e0c9ca --- /dev/null +++ b/src/main/java/com/bigswitch/floodlight/vendor/OFActionNiciraTtlDecrement.java @@ -0,0 +1,28 @@ +package com.bigswitch.floodlight.vendor; + +import org.jboss.netty.buffer.ChannelBuffer; + +public class OFActionNiciraTtlDecrement extends OFActionNiciraVendor { + public static int MINIMUM_LENGTH_TTL_DECREMENT = 16; + public static final short TTL_DECREMENT_SUBTYPE = 18; + + + public OFActionNiciraTtlDecrement() { + super(TTL_DECREMENT_SUBTYPE); + super.setLength((short)MINIMUM_LENGTH_TTL_DECREMENT); + } + + @Override + public void readFrom(ChannelBuffer data) { + super.readFrom(data); + data.skipBytes(6); // pad + } + + @Override + public void writeTo(ChannelBuffer data) { + super.writeTo(data); + data.writeZero(6); + } + + +} diff --git a/src/main/java/com/bigswitch/floodlight/vendor/OFActionNiciraVendor.java b/src/main/java/com/bigswitch/floodlight/vendor/OFActionNiciraVendor.java new file mode 100644 index 0000000000000000000000000000000000000000..b4d9fa523baff8fb12150d2e5665300b7b4cd39a --- /dev/null +++ b/src/main/java/com/bigswitch/floodlight/vendor/OFActionNiciraVendor.java @@ -0,0 +1,77 @@ +package com.bigswitch.floodlight.vendor; + +import org.jboss.netty.buffer.ChannelBuffer; +import org.openflow.protocol.action.OFActionVendor; +import org.openflow.vendor.nicira.OFNiciraVendorData; + +/** + * FIXME: this should really be handled by a consistent parse tree for + * different vendor actions but for the time being this works and gets the + * job done. + * + * @author gregor + * + */ +public class OFActionNiciraVendor extends OFActionVendor { + public static int MINIMUM_LENGTH = 16; + public static int NICIRA_VENDOR_ID = OFNiciraVendorData.NX_VENDOR_ID; + + protected short subtype; + + protected OFActionNiciraVendor(short subtype) { + // We don't allow direct instantiation of this class because its + // minimum length is 16 and the only way to guarantee this is by + // having a subclass that properly adds padding. + super(); + super.setLength((short)MINIMUM_LENGTH); + super.setVendor(NICIRA_VENDOR_ID); + this.subtype = subtype; + } + + public short getSubtype() { + return this.subtype; + } + + public void setSubtype(short subtype) { + this.subtype = subtype; + } + + @Override + public void readFrom(ChannelBuffer data) { + super.readFrom(data); + this.subtype = data.readShort(); + } + + @Override + public void writeTo(ChannelBuffer data) { + super.writeTo(data); + data.writeShort(this.subtype); + } + + @Override + public int hashCode() { + final int prime = 379; + int result = super.hashCode(); + result = prime * result + vendor; + result = prime * result + subtype; + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (!super.equals(obj)) { + return false; + } + if (!(obj instanceof OFActionNiciraVendor)) { + return false; + } + OFActionNiciraVendor other = (OFActionNiciraVendor) obj; + if (subtype != other.subtype) { + return false; + } + return true; + } +} diff --git a/src/main/java/com/bigswitch/floodlight/vendor/OFActionTunnelDstIP.java b/src/main/java/com/bigswitch/floodlight/vendor/OFActionTunnelDstIP.java new file mode 100644 index 0000000000000000000000000000000000000000..e9a37efe9b2b46378a2957d982f05894540366d3 --- /dev/null +++ b/src/main/java/com/bigswitch/floodlight/vendor/OFActionTunnelDstIP.java @@ -0,0 +1,65 @@ +package com.bigswitch.floodlight.vendor; + +import net.floodlightcontroller.packet.IPv4; + +import org.jboss.netty.buffer.ChannelBuffer; + +public class OFActionTunnelDstIP extends OFActionBigSwitchVendor { + public final static int MINIMUM_LENGTH_TUNNEL_DST = 16; + public final static int SET_TUNNEL_DST_SUBTYPE = 2; + + protected int dstIPAddr; + + public OFActionTunnelDstIP() { + super(SET_TUNNEL_DST_SUBTYPE); + super.setLength((short)MINIMUM_LENGTH_TUNNEL_DST); + } + + public OFActionTunnelDstIP(int dstIPAddr) { + this(); + this.dstIPAddr = dstIPAddr; + } + + public int getTunnelDstIP() { + return this.dstIPAddr; + } + + public void setTunnelDstIP(int ipAddr) { + this.dstIPAddr = ipAddr; + } + + @Override + public void readFrom(ChannelBuffer data) { + super.readFrom(data); + this.dstIPAddr = data.readInt(); + } + + @Override + public void writeTo(ChannelBuffer data) { + super.writeTo(data); + data.writeInt(this.dstIPAddr); + } + + @Override + public int hashCode() { + final int prime = 31; + int result = super.hashCode(); + result = prime * result + dstIPAddr; + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (!super.equals(obj)) return false; + if (getClass() != obj.getClass()) return false; + OFActionTunnelDstIP other = (OFActionTunnelDstIP) obj; + if (dstIPAddr != other.dstIPAddr) return false; + return true; + } + + @Override + public String toString() { + return super.toString() + "; dstIP=" + IPv4.fromIPv4Address(dstIPAddr); + } +} diff --git a/src/main/java/com/bigswitch/floodlight/vendor/OFBigSwitchVendorActionFactory.java b/src/main/java/com/bigswitch/floodlight/vendor/OFBigSwitchVendorActionFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..3b58cabf632df06fbbc187e3f8e0bdf14efa05cd --- /dev/null +++ b/src/main/java/com/bigswitch/floodlight/vendor/OFBigSwitchVendorActionFactory.java @@ -0,0 +1,41 @@ +package com.bigswitch.floodlight.vendor; + +import org.jboss.netty.buffer.ChannelBuffer; +import org.openflow.protocol.action.OFActionVendor; +import org.openflow.protocol.factory.OFVendorActionFactory; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class OFBigSwitchVendorActionFactory implements OFVendorActionFactory { + protected static Logger logger = + LoggerFactory.getLogger(OFBigSwitchVendorActionFactory.class); + + static class OFActionBigSwitchVendorDemux extends OFActionBigSwitchVendor { + OFActionBigSwitchVendorDemux() { + super((short) 0); + } + } + + @Override + public OFActionVendor readFrom(ChannelBuffer data) { + data.markReaderIndex(); + OFActionBigSwitchVendor demux = new OFActionBigSwitchVendorDemux(); + demux.readFrom(data); + data.resetReaderIndex(); + + switch(demux.getSubtype()) { + case OFActionMirror.BSN_ACTION_MIRROR: + OFActionMirror mirrorAction = new OFActionMirror((short) 0); + mirrorAction.readFrom(data); + return mirrorAction; + case OFActionTunnelDstIP.SET_TUNNEL_DST_SUBTYPE: + OFActionTunnelDstIP tunnelAction = new OFActionTunnelDstIP(); + tunnelAction.readFrom(data); + return tunnelAction; + default: + logger.error("Unknown BSN vendor action subtype: "+demux.getSubtype()); + return null; + } + } + +} diff --git a/src/main/java/com/bigswitch/floodlight/vendor/OFBigSwitchVendorData.java b/src/main/java/com/bigswitch/floodlight/vendor/OFBigSwitchVendorData.java new file mode 100644 index 0000000000000000000000000000000000000000..e7d34258d077a9a6d13d6d7a928e664f23aaf18e --- /dev/null +++ b/src/main/java/com/bigswitch/floodlight/vendor/OFBigSwitchVendorData.java @@ -0,0 +1,77 @@ +package com.bigswitch.floodlight.vendor; + +import org.jboss.netty.buffer.ChannelBuffer; +import org.openflow.protocol.vendor.OFVendorData; + +/** + * Base class for vendor data corresponding to BigSwitch vendor extensions + * BigSwitch vendor data always starts with a 4-byte integer data type value + * + * @author Munish Mehta (munish.mehta@bigswitch.com) + */ +public class OFBigSwitchVendorData implements OFVendorData { + + public static final int BSN_VENDOR_ID = 0x005c16c7; + + /** + * The value of the integer data type at the beginning of the vendor data + */ + protected int dataType; + + /** + * Construct BigSwitch vendor data with the specified data type + * @param dataType : the data type value at the beginning (opcode) + */ + public OFBigSwitchVendorData(int dataType) { + super(); + this.dataType = dataType; + } + + /** + * Get the data type value at the beginning of the vendor data + * @return + */ + public int getDataType() { + return dataType; + } + + /** + * Set the data type value + * @param dataType + */ + public void setDataType(int dataType) { + this.dataType = dataType; + } + + /** + * Get the length of the vendor data. This implementation will normally be + * the superclass for another class that will override this to return the + * overall vendor data length. This implementation just returns the length + * of the part that includes the 4-byte integer data type value at the + * beginning of the vendor data + */ + @Override + public int getLength() { + return 4; + } + + /** + * Read the vendor data from the ChannelBuffer + * @param data the channel buffer from which we're deserializing + * @param length the length to the end of the enclosing message + */ + @Override + public void readFrom(ChannelBuffer data, int length) { + dataType = data.readInt(); + } + + /** + * Write the vendor data to the ChannelBuffer + * @param data the channel buffer to which we're serializing + */ + @Override + public void writeTo(ChannelBuffer data) { + data.writeInt(dataType); + } + +} diff --git a/src/main/java/com/bigswitch/floodlight/vendor/OFBigSwitchVendorExtensions.java b/src/main/java/com/bigswitch/floodlight/vendor/OFBigSwitchVendorExtensions.java new file mode 100644 index 0000000000000000000000000000000000000000..0b3d069c4a1af871433509094f7a73e001fda17e --- /dev/null +++ b/src/main/java/com/bigswitch/floodlight/vendor/OFBigSwitchVendorExtensions.java @@ -0,0 +1,52 @@ +package com.bigswitch.floodlight.vendor; + +import org.openflow.protocol.vendor.OFBasicVendorDataType; +import org.openflow.protocol.vendor.OFBasicVendorId; +import org.openflow.protocol.vendor.OFVendorId; + +public class OFBigSwitchVendorExtensions { + private static boolean initialized = false; + + public static synchronized void initialize() { + if (initialized) + return; + + OFBasicVendorId bsnVendorId = + new OFBasicVendorId(OFBigSwitchVendorData.BSN_VENDOR_ID, 4); + OFVendorId.registerVendorId(bsnVendorId); + + // register data types used for big tap + OFBasicVendorDataType setEntryVendorData = + new OFBasicVendorDataType( + OFNetmaskSetVendorData.BSN_SET_IP_MASK_ENTRY, + OFNetmaskSetVendorData.getInstantiable()); + bsnVendorId.registerVendorDataType(setEntryVendorData); + + OFBasicVendorDataType getEntryVendorDataRequest = + new OFBasicVendorDataType( + OFNetmaskGetVendorDataRequest.BSN_GET_IP_MASK_ENTRY_REQUEST, + OFNetmaskGetVendorDataRequest.getInstantiable()); + bsnVendorId.registerVendorDataType(getEntryVendorDataRequest); + + OFBasicVendorDataType getEntryVendorDataReply = + new OFBasicVendorDataType( + OFNetmaskGetVendorDataReply.BSN_GET_IP_MASK_ENTRY_REPLY, + OFNetmaskGetVendorDataReply.getInstantiable()); + bsnVendorId.registerVendorDataType(getEntryVendorDataReply); + + // register data types used for tunneling + OFBasicVendorDataType getIntfIPVendorDataRequest = + new OFBasicVendorDataType( + OFInterfaceIPRequestVendorData.BSN_GET_INTERFACE_IP_REQUEST, + OFInterfaceIPRequestVendorData.getInstantiable()); + bsnVendorId.registerVendorDataType(getIntfIPVendorDataRequest); + + OFBasicVendorDataType getIntfIPVendorDataReply = + new OFBasicVendorDataType( + OFInterfaceIPReplyVendorData.BSN_GET_INTERFACE_IP_REPLY, + OFInterfaceIPReplyVendorData.getInstantiable()); + bsnVendorId.registerVendorDataType(getIntfIPVendorDataReply); + + + } +} diff --git a/src/main/java/com/bigswitch/floodlight/vendor/OFBsnL2TableSetVendorData.java b/src/main/java/com/bigswitch/floodlight/vendor/OFBsnL2TableSetVendorData.java new file mode 100644 index 0000000000000000000000000000000000000000..3dd8310148e423889250a281ee240ef6e13f7f3c --- /dev/null +++ b/src/main/java/com/bigswitch/floodlight/vendor/OFBsnL2TableSetVendorData.java @@ -0,0 +1,33 @@ +package com.bigswitch.floodlight.vendor; + +import org.openflow.protocol.Instantiable; +import org.openflow.protocol.vendor.OFVendorData; + +public class OFBsnL2TableSetVendorData extends OFBsnL2TableVendorData { + + protected static Instantiable<OFVendorData> instantiableSingleton = + new Instantiable<OFVendorData>() { + public OFVendorData instantiate() { + return new OFBsnL2TableSetVendorData(); + } + }; + + public static final int BSN_L2_TABLE_SET = 12; + + /** + * @return a subclass of Instantiable<OFVendorData> that instantiates + * an instance of OFBsnL2TableSetVendorData. + */ + public static Instantiable<OFVendorData> getInstantiable() { + return instantiableSingleton; + } + + public OFBsnL2TableSetVendorData() { + super(BSN_L2_TABLE_SET); + } + + public OFBsnL2TableSetVendorData(boolean l2TableEnabled, + short l2TablePriority) { + super(BSN_L2_TABLE_SET, l2TableEnabled, l2TablePriority); + } +} diff --git a/src/main/java/com/bigswitch/floodlight/vendor/OFBsnL2TableVendorData.java b/src/main/java/com/bigswitch/floodlight/vendor/OFBsnL2TableVendorData.java new file mode 100644 index 0000000000000000000000000000000000000000..14e6178d1b4793855b1b6ee8d0946c6f87fa79d0 --- /dev/null +++ b/src/main/java/com/bigswitch/floodlight/vendor/OFBsnL2TableVendorData.java @@ -0,0 +1,81 @@ +package com.bigswitch.floodlight.vendor; + +import org.jboss.netty.buffer.ChannelBuffer; + +public class OFBsnL2TableVendorData extends OFBigSwitchVendorData { + /* + * uint8_t l2_table_enable; // 1 == enabled, 0 == disabled + * uint8_t pad; + * uint16_t l2_table_priority; // priority of all flows in L2 table + * uint8_t pad[4]; + */ + protected boolean l2TableEnabled; + protected short l2TablePriority; + + + public OFBsnL2TableVendorData(int dataType) { + super(dataType); + this.l2TableEnabled = false; + this.l2TablePriority = (short)0; + } + + + public OFBsnL2TableVendorData(int dataType, boolean l2TableEnabled, + short l2TablePriority) { + super(dataType); + this.l2TableEnabled = l2TableEnabled; + this.l2TablePriority = l2TablePriority; + } + + + public boolean isL2TableEnabled() { + return l2TableEnabled; + } + + + public short getL2TablePriority() { + return l2TablePriority; + } + + + public void setL2TableEnabled(boolean l2TableEnabled) { + this.l2TableEnabled = l2TableEnabled; + } + + + public void setL2TablePriority(short l2TablePriority) { + this.l2TablePriority = l2TablePriority; + } + + + @Override + public int getLength() { + return super.getLength() + 8; // 8 additional bytes + } + + /* + * (non-Javadoc) + * @see com.bigswitch.floodlight.vendor.OFBigSwitchVendorData#readFrom(org.jboss.netty.buffer.ChannelBuffer, int) + */ + @Override + public void readFrom(ChannelBuffer data, int length) { + super.readFrom(data, length); + l2TableEnabled = (data.readByte() == 0) ? false : true; + data.readByte(); // pad + l2TablePriority = data.readShort(); + data.readInt(); // 4 bad bytes + } + + /* + * (non-Javadoc) + * @see com.bigswitch.floodlight.vendor.OFBigSwitchVendorData#writeTo(org.jboss.netty.buffer.ChannelBuffer) + */ + @Override + public void writeTo(ChannelBuffer data) { + super.writeTo(data); + data.writeByte(isL2TableEnabled() ? 1 : 0); + data.writeByte(0); // pad + data.writeShort(l2TablePriority); + data.writeInt(0); // 4 pad bytes + } +} diff --git a/src/main/java/com/bigswitch/floodlight/vendor/OFInterfaceIPReplyVendorData.java b/src/main/java/com/bigswitch/floodlight/vendor/OFInterfaceIPReplyVendorData.java new file mode 100644 index 0000000000000000000000000000000000000000..380ba43c299c7ccaaccafbdd846e92ea72a848ef --- /dev/null +++ b/src/main/java/com/bigswitch/floodlight/vendor/OFInterfaceIPReplyVendorData.java @@ -0,0 +1,124 @@ +package com.bigswitch.floodlight.vendor; + +import java.util.ArrayList; +import java.util.List; + +import org.jboss.netty.buffer.ChannelBuffer; +import org.openflow.protocol.Instantiable; +import org.openflow.protocol.vendor.OFVendorData; + +public class OFInterfaceIPReplyVendorData extends OFBigSwitchVendorData { + + protected List<OFInterfaceVendorData> interfaces; + protected int length; + + protected static Instantiable<OFVendorData> instantiable = + new Instantiable<OFVendorData>() { + public OFVendorData instantiate() { + return new OFInterfaceIPReplyVendorData(); + } + }; + + /** + * @return a subclass of Instantiable<OFVendorData> that instantiates + * an instance of OFInterfaceIPReplyVendorData. + */ + public static Instantiable<OFVendorData> getInstantiable() { + return instantiable; + } + + /** + * Opcode/dataType to reply with IP addresses of all interfaces + */ + public static final int BSN_GET_INTERFACE_IP_REPLY = 10; + + /** + * Construct an interface IP reply vendor data + */ + public OFInterfaceIPReplyVendorData() { + super(BSN_GET_INTERFACE_IP_REPLY); + } + + /** + * @return the total length of the vendor-data part of the interface IP reply + * message. The OF header (8B) and vendor (4B) are taken care of by the + * OFVendor class MINIMUM_LENGTH. This method returns the length of the + * vendor-extension-subtype (4B) + the length of the interfaces + */ + @Override + public int getLength() { + return length; + } + + /** + * Set the length of this message + * + * @param length + */ + public void setLength(int length) { + this.length = length; + + } + + /** + * @return the interfaces + */ + public List<OFInterfaceVendorData> getInterfaces() { + return interfaces; + } + + /** + * @param intfs the ones to set + */ + public void setInterfaces(List<OFInterfaceVendorData> intfs) { + this.interfaces = intfs; + if (intfs == null) { + this.setLength(super.getLength()); + } else { + this.setLength(super.getLength() + intfs.size() + * OFInterfaceVendorData.MINIMUM_LENGTH); + } + } + + /** + * Read from the ChannelBuffer + * @param data the channel buffer from which we're deserializing + * @param length the length to the end of the enclosing message + */ + @Override + public void readFrom(ChannelBuffer data, int length) { + //datatype read by super class + super.readFrom(data, length); + + if (this.interfaces == null) { + this.interfaces = new ArrayList<OFInterfaceVendorData>(); + } else { + this.interfaces.clear(); + } + int intfCount = (length - 4) + / OFInterfaceVendorData.MINIMUM_LENGTH; + + OFInterfaceVendorData intf; + for (int i = 0; i < intfCount; ++i) { + intf = new OFInterfaceVendorData(); + intf.readFrom(data); + this.interfaces.add(intf); + } + } + + /** + * Write to the ChannelBuffer + * @param data the channel buffer to which we're serializing + */ + @Override + public void writeTo(ChannelBuffer data) { + // datatype written by super class + super.writeTo(data); + if (this.interfaces != null) { + for (OFInterfaceVendorData intf : this.interfaces) { + intf.writeTo(data); + } + } + } + +} diff --git a/src/main/java/com/bigswitch/floodlight/vendor/OFInterfaceIPRequestVendorData.java b/src/main/java/com/bigswitch/floodlight/vendor/OFInterfaceIPRequestVendorData.java new file mode 100644 index 0000000000000000000000000000000000000000..9caf6e54ad299e488b217722941d7b4ae057fc72 --- /dev/null +++ b/src/main/java/com/bigswitch/floodlight/vendor/OFInterfaceIPRequestVendorData.java @@ -0,0 +1,64 @@ +package com.bigswitch.floodlight.vendor; + +import org.jboss.netty.buffer.ChannelBuffer; +import org.openflow.protocol.Instantiable; +import org.openflow.protocol.vendor.OFVendorData; + +public class OFInterfaceIPRequestVendorData extends OFBigSwitchVendorData { + + protected static Instantiable<OFVendorData> instantiable = + new Instantiable<OFVendorData>() { + public OFVendorData instantiate() { + return new OFInterfaceIPRequestVendorData(); + } + }; + + /** + * @return a subclass of Instantiable<OFVendorData> that instantiates + * an instance of OFInterfaceIPRequestVendorData. + */ + public static Instantiable<OFVendorData> getInstantiable() { + return instantiable; + } + + /** + * Opcode/dataType to request IP addresses of all interfaces + */ + public static final int BSN_GET_INTERFACE_IP_REQUEST = 9; + + /** + * Construct an interface IP request vendor data + */ + public OFInterfaceIPRequestVendorData() { + super(BSN_GET_INTERFACE_IP_REQUEST); + } + + /** + * @return the total length of the interface IP request message + * the length is already accounted for in the super class + */ + @Override + public int getLength() { + return super.getLength(); + } + + /** + * Read from the ChannelBuffer + * @param data the channel buffer from which we're deserializing + * @param length the length to the end of the enclosing message + */ + @Override + public void readFrom(ChannelBuffer data, int length) { + super.readFrom(data, length); + } + + /** + * Write to the ChannelBuffer + * @param data the channel buffer to which we're serializing + */ + @Override + public void writeTo(ChannelBuffer data) { + super.writeTo(data); + } + +} diff --git a/src/main/java/com/bigswitch/floodlight/vendor/OFInterfaceVendorData.java b/src/main/java/com/bigswitch/floodlight/vendor/OFInterfaceVendorData.java new file mode 100644 index 0000000000000000000000000000000000000000..0b6f30de4c677b5c694fb2037b759462c1827c29 --- /dev/null +++ b/src/main/java/com/bigswitch/floodlight/vendor/OFInterfaceVendorData.java @@ -0,0 +1,123 @@ +package com.bigswitch.floodlight.vendor; + +import java.io.UnsupportedEncodingException; +import java.nio.charset.Charset; +import java.util.Arrays; + +import net.floodlightcontroller.core.web.serializers.ByteArrayMACSerializer; + +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import org.jboss.netty.buffer.ChannelBuffer; + +public class OFInterfaceVendorData { + public static int MINIMUM_LENGTH = 32; + private static int OFP_ETH_ALEN = 6; + private static int OFP_MAX_PORT_NAME_LEN = 16; + + protected byte[] hardwareAddress; + protected String name; + protected int ipv4Addr; + protected int ipv4AddrMask; + + /** + * @return the hardwareAddress + */ + @JsonSerialize(using=ByteArrayMACSerializer.class) + public byte[] getHardwareAddress() { + return hardwareAddress; + } + + /** + * @param hardwareAddress the hardwareAddress to set + */ + public void setHardwareAddress(byte[] hardwareAddress) { + if (hardwareAddress.length != OFP_ETH_ALEN) + throw new RuntimeException("Hardware address must have length " + + OFP_ETH_ALEN); + this.hardwareAddress = hardwareAddress; + } + + public int getIpv4Addr() { + return ipv4Addr; + } + + public void setIpv4Addr(int ipv4Addr) { + this.ipv4Addr = ipv4Addr; + } + + public int getIpv4AddrMask() { + return ipv4AddrMask; + } + + public void setIpv4AddrMask(int ipv4AddrMask) { + this.ipv4AddrMask = ipv4AddrMask; + } + + /** + * @return the name + */ + public String getName() { + return name; + } + + /** + * @param name the name to set + */ + public void setName(String name) { + this.name = name; + } + + /** + * Write this message's binary format to the specified ByteBuffer + * @param data + */ + public void writeTo(ChannelBuffer data) { + data.writeBytes(hardwareAddress); + data.writeBytes(new byte[] {0, 0}); + + try { + byte[] name = this.name.getBytes("ASCII"); + if (name.length < OFP_MAX_PORT_NAME_LEN) { + data.writeBytes(name); + for (int i = name.length; i < OFP_MAX_PORT_NAME_LEN; ++i) { + data.writeByte((byte) 0); + } + } else { + data.writeBytes(name, 0, 15); + data.writeByte((byte) 0); + } + } catch (UnsupportedEncodingException e) { + throw new RuntimeException(e); + } + + data.writeInt(ipv4Addr); + data.writeInt(ipv4AddrMask); + } + + /** + * Read this message off the wire from the specified ByteBuffer + * @param data + */ + public void readFrom(ChannelBuffer data) { + if (this.hardwareAddress == null) + this.hardwareAddress = new byte[OFP_ETH_ALEN]; + data.readBytes(this.hardwareAddress); + data.readBytes(new byte[2]); + + byte[] name = new byte[16]; + data.readBytes(name); + // find the first index of 0 + int index = 0; + for (byte b : name) { + if (0 == b) + break; + ++index; + } + this.name = new String(Arrays.copyOf(name, index), + Charset.forName("ascii")); + ipv4Addr = data.readInt(); + ipv4AddrMask = data.readInt(); + } + + +} \ No newline at end of file diff --git a/src/main/java/com/bigswitch/floodlight/vendor/OFMirrorGetVendorDataReply.java b/src/main/java/com/bigswitch/floodlight/vendor/OFMirrorGetVendorDataReply.java new file mode 100644 index 0000000000000000000000000000000000000000..d8d83b404eb8b3a31d369e3e49fb9a15ed3760c6 --- /dev/null +++ b/src/main/java/com/bigswitch/floodlight/vendor/OFMirrorGetVendorDataReply.java @@ -0,0 +1,46 @@ +package com.bigswitch.floodlight.vendor; + +import org.openflow.protocol.Instantiable; +import org.openflow.protocol.vendor.OFVendorData; + + +/** + * Subclass of OFVendorData + */ +public class OFMirrorGetVendorDataReply extends OFNetmaskVendorData { + + + protected static Instantiable<OFVendorData> instantiable = + new Instantiable<OFVendorData>() { + public OFVendorData instantiate() { + return new OFMirrorGetVendorDataReply(); + } + }; + + /** + * @return a subclass of Instantiable<OFVendorData> that instantiates + * an instance of OFNetmaskGetVendorData. + */ + public static Instantiable<OFVendorData> getInstantiable() { + return instantiable; + } + + /** + * Opcode/dataType to represent REPLY of GET_MASK request + */ + public static final int BSN_GET_MIRRORING_REPLY = 5; + + /** + * Construct a get network mask vendor data + */ + public OFMirrorGetVendorDataReply() { + super(BSN_GET_MIRRORING_REPLY); + } + + /** + * Construct a get network mask vendor data for a specific table entry + */ + public OFMirrorGetVendorDataReply(byte tableIndex, int netMask) { + super(BSN_GET_MIRRORING_REPLY, tableIndex, netMask); + } +} diff --git a/src/main/java/com/bigswitch/floodlight/vendor/OFMirrorGetVendorDataRequest.java b/src/main/java/com/bigswitch/floodlight/vendor/OFMirrorGetVendorDataRequest.java new file mode 100644 index 0000000000000000000000000000000000000000..6692cdfdde002140a87256f62cdb8cfa7a26f52d --- /dev/null +++ b/src/main/java/com/bigswitch/floodlight/vendor/OFMirrorGetVendorDataRequest.java @@ -0,0 +1,46 @@ +package com.bigswitch.floodlight.vendor; + +import org.openflow.protocol.Instantiable; +import org.openflow.protocol.vendor.OFVendorData; + + +/** + * Subclass of OFVendorData + */ +public class OFMirrorGetVendorDataRequest extends OFNetmaskVendorData { + + + protected static Instantiable<OFVendorData> instantiable = + new Instantiable<OFVendorData>() { + public OFVendorData instantiate() { + return new OFMirrorGetVendorDataRequest(); + } + }; + + /** + * @return a subclass of Instantiable<OFVendorData> that instantiates + * an instance of OFNetmaskGetVendorData. + */ + public static Instantiable<OFVendorData> getInstantiable() { + return instantiable; + } + + /** + * Opcode/dataType to request an entry in the switch netmask table + */ + public static final int BSN_GET_MIRRORING_REQUEST = 4; + + /** + * Construct a get network mask vendor data + */ + public OFMirrorGetVendorDataRequest() { + super(BSN_GET_MIRRORING_REQUEST); + } + + /** + * Construct a get network mask vendor data for a specific table entry + */ + public OFMirrorGetVendorDataRequest(byte tableIndex, int netMask) { + super(BSN_GET_MIRRORING_REQUEST, tableIndex, netMask); + } +} diff --git a/src/main/java/com/bigswitch/floodlight/vendor/OFMirrorSetVendorData.java b/src/main/java/com/bigswitch/floodlight/vendor/OFMirrorSetVendorData.java new file mode 100644 index 0000000000000000000000000000000000000000..5eba6512b7c4f07f9087aabe4f52e5cd0e300bee --- /dev/null +++ b/src/main/java/com/bigswitch/floodlight/vendor/OFMirrorSetVendorData.java @@ -0,0 +1,62 @@ +package com.bigswitch.floodlight.vendor; + +import org.jboss.netty.buffer.ChannelBuffer; + +public class OFMirrorSetVendorData extends OFBigSwitchVendorData { + + /** + * Opcode/dataType to set mirroring + */ + public static final int BSN_SET_MIRRORING = 3; + + protected byte reportMirrorPorts; + protected byte pad1; + protected byte pad2; + protected byte pad3; + + public OFMirrorSetVendorData() { + super(BSN_SET_MIRRORING); + this.reportMirrorPorts=1; + } + + public byte getReportMirrorPorts() { + return reportMirrorPorts; + } + + public void setReportMirrorPorts(byte report) { + this.reportMirrorPorts = report; + } + + /** + * @return the total length vendor date + */ + @Override + public int getLength() { + return super.getLength() + 4; // 4 extra bytes + } + + /** + * Read the vendor data from the channel buffer + * @param data: the channel buffer from which we are deserializing + * @param length: the length to the end of the enclosing message + */ + public void readFrom(ChannelBuffer data, int length) { + super.readFrom(data, length); + reportMirrorPorts = data.readByte(); + pad1 = data.readByte(); + pad2 = data.readByte(); + pad3 = data.readByte(); + } + + /** + * Write the vendor data to the channel buffer + */ + public void writeTo(ChannelBuffer data) { + super.writeTo(data); + data.writeByte(reportMirrorPorts); + data.writeByte(pad1); + data.writeByte(pad2); + data.writeByte(pad3); + } + +} diff --git a/src/main/java/com/bigswitch/floodlight/vendor/OFNetmaskGetVendorDataReply.java b/src/main/java/com/bigswitch/floodlight/vendor/OFNetmaskGetVendorDataReply.java new file mode 100644 index 0000000000000000000000000000000000000000..62d711020b71f8c962ff2fd068451acb3d9f9ab7 --- /dev/null +++ b/src/main/java/com/bigswitch/floodlight/vendor/OFNetmaskGetVendorDataReply.java @@ -0,0 +1,48 @@ +package com.bigswitch.floodlight.vendor; + +import org.openflow.protocol.Instantiable; +import org.openflow.protocol.vendor.OFVendorData; + + +/** + * Subclass of OFVendorData + * + * @author munish_mehta + */ +public class OFNetmaskGetVendorDataReply extends OFNetmaskVendorData { + + + protected static Instantiable<OFVendorData> instantiable = + new Instantiable<OFVendorData>() { + public OFVendorData instantiate() { + return new OFNetmaskGetVendorDataReply(); + } + }; + + /** + * @return a subclass of Instantiable<OFVendorData> that instantiates + * an instance of OFNetmaskGetVendorData. + */ + public static Instantiable<OFVendorData> getInstantiable() { + return instantiable; + } + + /** + * Opcode/dataType to represent REPLY of GET_MASK request + */ + public static final int BSN_GET_IP_MASK_ENTRY_REPLY = 2; + + /** + * Construct a get network mask vendor data + */ + public OFNetmaskGetVendorDataReply() { + super(BSN_GET_IP_MASK_ENTRY_REPLY); + } + + /** + * Construct a get network mask vendor data for a specific table entry + */ + public OFNetmaskGetVendorDataReply(byte tableIndex, int netMask) { + super(BSN_GET_IP_MASK_ENTRY_REPLY, tableIndex, netMask); + } +} diff --git a/src/main/java/com/bigswitch/floodlight/vendor/OFNetmaskGetVendorDataRequest.java b/src/main/java/com/bigswitch/floodlight/vendor/OFNetmaskGetVendorDataRequest.java new file mode 100644 index 0000000000000000000000000000000000000000..1d075e9a3b9a87584f92b6ad7ea04442457f5ac9 --- /dev/null +++ b/src/main/java/com/bigswitch/floodlight/vendor/OFNetmaskGetVendorDataRequest.java @@ -0,0 +1,48 @@ +package com.bigswitch.floodlight.vendor; + +import org.openflow.protocol.Instantiable; +import org.openflow.protocol.vendor.OFVendorData; + + +/** + * Subclass of OFVendorData + * + * @author munish_mehta + */ +public class OFNetmaskGetVendorDataRequest extends OFNetmaskVendorData { + + + protected static Instantiable<OFVendorData> instantiable = + new Instantiable<OFVendorData>() { + public OFVendorData instantiate() { + return new OFNetmaskGetVendorDataRequest(); + } + }; + + /** + * @return a subclass of Instantiable<OFVendorData> that instantiates + * an instance of OFNetmaskGetVendorData. + */ + public static Instantiable<OFVendorData> getInstantiable() { + return instantiable; + } + + /** + * Opcode/dataType to request an entry in the switch netmask table + */ + public static final int BSN_GET_IP_MASK_ENTRY_REQUEST = 1; + + /** + * Construct a get network mask vendor data + */ + public OFNetmaskGetVendorDataRequest() { + super(BSN_GET_IP_MASK_ENTRY_REQUEST); + } + + /** + * Construct a get network mask vendor data for a specific table entry + */ + public OFNetmaskGetVendorDataRequest(byte tableIndex, int netMask) { + super(BSN_GET_IP_MASK_ENTRY_REQUEST, tableIndex, netMask); + } +} diff --git a/src/main/java/com/bigswitch/floodlight/vendor/OFNetmaskSetVendorData.java b/src/main/java/com/bigswitch/floodlight/vendor/OFNetmaskSetVendorData.java new file mode 100644 index 0000000000000000000000000000000000000000..54421c10334acb5d7ca793c699f5005977a3a873 --- /dev/null +++ b/src/main/java/com/bigswitch/floodlight/vendor/OFNetmaskSetVendorData.java @@ -0,0 +1,42 @@ +package com.bigswitch.floodlight.vendor; + +import org.openflow.protocol.Instantiable; +import org.openflow.protocol.vendor.OFVendorData; + +public class OFNetmaskSetVendorData extends OFNetmaskVendorData { + + + protected static Instantiable<OFVendorData> instantiable = + new Instantiable<OFVendorData>() { + public OFVendorData instantiate() { + return new OFNetmaskSetVendorData(); + } + }; + + /** + * @return a subclass of Instantiable<OFVendorData> that instantiates + * an instance of OFNetmaskSetVendorData. + */ + public static Instantiable<OFVendorData> getInstantiable() { + return instantiable; + } + + /** + * Opcode/dataType to set an entry in the switch netmask table + */ + public static final int BSN_SET_IP_MASK_ENTRY = 0; + + /** + * Construct a get network mask vendor data + */ + public OFNetmaskSetVendorData() { + super(BSN_SET_IP_MASK_ENTRY); + } + + /** + * Construct a get network mask vendor data for a specific table entry + */ + public OFNetmaskSetVendorData(byte tableIndex, int netMask) { + super(BSN_SET_IP_MASK_ENTRY, tableIndex, netMask); + } +} diff --git a/src/main/java/com/bigswitch/floodlight/vendor/OFNetmaskVendorData.java b/src/main/java/com/bigswitch/floodlight/vendor/OFNetmaskVendorData.java new file mode 100644 index 0000000000000000000000000000000000000000..66539f98e757e8b133eaf04c115557fad8904ebd --- /dev/null +++ b/src/main/java/com/bigswitch/floodlight/vendor/OFNetmaskVendorData.java @@ -0,0 +1,88 @@ +package com.bigswitch.floodlight.vendor; + +import org.jboss.netty.buffer.ChannelBuffer; + + +/** + * Class that represents the vendor data in the netmask table request + * extension implemented by Arista switches + * + * @author munish_mehta (munish.mehta@bigswitch.com) + */ + +public class OFNetmaskVendorData extends OFBigSwitchVendorData { + + /** + * Table index for set or get of the the entry from netmask table + */ + protected byte tableIndex; + protected byte pad1; + protected byte pad2; + protected byte pad3; + protected int netMask; + + public OFNetmaskVendorData(int dataType) { + super(dataType); + this.tableIndex = 0; + this.netMask = (int)0xffffffffL; + } + + public OFNetmaskVendorData(int dataType, byte table_index, int netmask) { + super(dataType); + this.tableIndex = table_index; + this.netMask = netmask; + } + + + public byte getTableIndex() { + return tableIndex; + } + + public void setTableIndex(byte tableIndex) { + this.tableIndex = tableIndex; + } + + public int getNetMask() { + return netMask; + } + + public void setNetMask(int netMask) { + this.netMask = netMask; + } + + /** + * @return the total length of the netmask vendor data + */ + @Override + public int getLength() { + return super.getLength() + 8; // 8 extra bytes + } + + /** + * Read the vendor data from the channel buffer + * @param data: the channel buffer from which we are deserializing + * @param length: the length to the end of the enclosing message + */ + public void readFrom(ChannelBuffer data, int length) { + super.readFrom(data, length); + tableIndex = data.readByte(); + pad1 = data.readByte(); + pad2 = data.readByte(); + pad3 = data.readByte(); + netMask = data.readInt(); + } + + /** + * Write the vendor data to the channel buffer + */ + public void writeTo(ChannelBuffer data) { + super.writeTo(data); + data.writeByte(tableIndex); + data.writeByte(pad1); + data.writeByte(pad2); + data.writeByte(pad3); + data.writeInt(netMask); + } + + +} diff --git a/src/main/java/com/bigswitch/floodlight/vendor/OFNiciraVendorActionFactory.java b/src/main/java/com/bigswitch/floodlight/vendor/OFNiciraVendorActionFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..716373794f263aa3db5a60d316d982f7ce668a5f --- /dev/null +++ b/src/main/java/com/bigswitch/floodlight/vendor/OFNiciraVendorActionFactory.java @@ -0,0 +1,37 @@ +package com.bigswitch.floodlight.vendor; + +import org.jboss.netty.buffer.ChannelBuffer; +import org.openflow.protocol.action.OFActionVendor; +import org.openflow.protocol.factory.OFVendorActionFactory; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class OFNiciraVendorActionFactory implements OFVendorActionFactory { + protected static Logger logger = + LoggerFactory.getLogger(OFNiciraVendorActionFactory.class); + + static class OFActionNiciraVendorDemux extends OFActionNiciraVendor { + OFActionNiciraVendorDemux() { + super((short) 0); + } + } + + @Override + public OFActionVendor readFrom(ChannelBuffer data) { + data.markReaderIndex(); + OFActionNiciraVendorDemux demux = new OFActionNiciraVendorDemux(); + demux.readFrom(data); + data.resetReaderIndex(); + + switch(demux.getSubtype()) { + case OFActionNiciraTtlDecrement.TTL_DECREMENT_SUBTYPE: + OFActionNiciraTtlDecrement ttlAction = new OFActionNiciraTtlDecrement(); + ttlAction.readFrom(data); + return ttlAction; + default: + logger.error("Unknown Nicira vendor action subtype: "+demux.getSubtype()); + return null; + } + } + +} diff --git a/src/main/java/com/bigswitch/floodlight/vendor/OFVendorActions.java b/src/main/java/com/bigswitch/floodlight/vendor/OFVendorActions.java new file mode 100644 index 0000000000000000000000000000000000000000..0b555340cea7abe3364d1c05b7feaa7be0b3ff0d --- /dev/null +++ b/src/main/java/com/bigswitch/floodlight/vendor/OFVendorActions.java @@ -0,0 +1,11 @@ +package com.bigswitch.floodlight.vendor; + +import org.openflow.protocol.factory.OFVendorActionRegistry; + +public final class OFVendorActions { + public static final void registerStandardVendorActions() { + OFVendorActionRegistry registry = OFVendorActionRegistry.getInstance(); + registry.register(OFActionBigSwitchVendor.BSN_VENDOR_ID, new OFBigSwitchVendorActionFactory()); + registry.register(OFActionNiciraVendor.NICIRA_VENDOR_ID, new OFNiciraVendorActionFactory()); + } +} \ No newline at end of file diff --git a/src/main/java/net/floodlightcontroller/core/IOFSwitch.java b/src/main/java/net/floodlightcontroller/core/IOFSwitch.java index 9b7d3344f7708f4e7c0b6cb7c8d042b90db5bb65..112f30c8d1b192aee16bda5ffd66a8139684459b 100644 --- a/src/main/java/net/floodlightcontroller/core/IOFSwitch.java +++ b/src/main/java/net/floodlightcontroller/core/IOFSwitch.java @@ -612,4 +612,28 @@ public interface IOFSwitch { * Set the flow table full flag in the switch */ public void setTableFull(boolean isFull); + + /** + * Set the suggested priority to use when installing access flows in + * this switch. + */ + public void setAccessFlowPriority(short prio); + + /** + * Set the suggested priority to use when installing core flows in + * this switch. + */ + public void setCoreFlowPriority(short prio); + + /** + * Get the suggested priority to use when installing access flows in + * this switch. + */ + public short getAccessFlowPriority(); + + /** + * Get the suggested priority to use when installing core flows in + * this switch. + */ + public short getCoreFlowPriority(); } diff --git a/src/main/java/net/floodlightcontroller/core/OFSwitchBase.java b/src/main/java/net/floodlightcontroller/core/OFSwitchBase.java index 56b9818022cb7cf255b7aa174237797adb102cf6..8bef78fe80722453e2e4ca690764c0743f333486 100644 --- a/src/main/java/net/floodlightcontroller/core/OFSwitchBase.java +++ b/src/main/java/net/floodlightcontroller/core/OFSwitchBase.java @@ -104,6 +104,9 @@ public abstract class OFSwitchBase implements IOFSwitch { protected long datapathId; protected String stringId; + protected short accessFlowPriority; + protected short coreFlowPriority; + /** * Members hidden from subclasses */ @@ -1504,4 +1507,28 @@ public abstract class OFSwitchBase implements IOFSwitch { } flowTableFull = isFull; } + + + @Override + public short getAccessFlowPriority() { + return accessFlowPriority; + } + + + @Override + public short getCoreFlowPriority() { + return coreFlowPriority; + } + + + @Override + public void setAccessFlowPriority(short accessFlowPriority) { + this.accessFlowPriority = accessFlowPriority; + } + + + @Override + public void setCoreFlowPriority(short coreFlowPriority) { + this.coreFlowPriority = coreFlowPriority; + } } diff --git a/src/main/java/net/floodlightcontroller/core/internal/Controller.java b/src/main/java/net/floodlightcontroller/core/internal/Controller.java index 53b619f2586b58580a99203372d5d19acb20f0bf..2b424f7475690c53629880004b04d341f7421794 100644 --- a/src/main/java/net/floodlightcontroller/core/internal/Controller.java +++ b/src/main/java/net/floodlightcontroller/core/internal/Controller.java @@ -202,6 +202,24 @@ public class Controller implements IFloodlightProviderService, protected static final String CONTROLLER_INTERFACE_NUMBER = "number"; protected static final String CONTROLLER_INTERFACE_DISCOVERED_IP = "discovered_ip"; + // FIXME: don't use "forwardingconfig" as table name + private static final String FLOW_PRIORITY_TABLE_NAME = "controller_forwardingconfig"; + private static final String FLOW_COLUMN_PRIMARY_KEY = "id"; + private static final String FLOW_VALUE_PRIMARY_KEY = "forwarding"; + private static final String FLOW_COLUMN_ACCESS_PRIORITY = "access_priority"; + private static final String FLOW_COLUMN_CORE_PRIORITY = "core_priority"; + private static final String[] FLOW_COLUMN_NAMES = new String[] { + FLOW_COLUMN_PRIMARY_KEY, + FLOW_COLUMN_ACCESS_PRIORITY, + FLOW_COLUMN_CORE_PRIORITY + }; + + private static final short DEFAULT_ACCESS_PRIORITY = 10; + private static final short DEFAULT_CORE_PRIORITY = 1000; + private short accessPriority = DEFAULT_ACCESS_PRIORITY; + private short corePriority = DEFAULT_CORE_PRIORITY; + + // Perf. related configuration protected static final int SEND_BUFFER_SIZE = 4 * 1024 * 1024; public static final int BATCH_MAX_SIZE = 100; @@ -2233,6 +2251,12 @@ public class Controller implements IFloodlightProviderService, CONTROLLER_ID); storageSource.addListener(CONTROLLER_INTERFACE_TABLE_NAME, this); + storageSource.createTable(FLOW_PRIORITY_TABLE_NAME, null); + storageSource.setTablePrimaryKeyName(FLOW_PRIORITY_TABLE_NAME, + FLOW_COLUMN_PRIMARY_KEY); + storageSource.addListener(FLOW_PRIORITY_TABLE_NAME, this); + readFlowPriorityConfigurationFromStorage(); + // Startup load monitoring if (overload_drop) { this.loadmonitor.startMonitoring( @@ -2264,6 +2288,47 @@ public class Controller implements IFloodlightProviderService, registerControllerDebugEvents(); } + @LogMessageDoc(level="ERROR", + message="failed to access storage: {reason}", + explanation="Could not retrieve forwarding configuration", + recommendation=LogMessageDoc.CHECK_CONTROLLER) + private void readFlowPriorityConfigurationFromStorage() { + try { + Map<String, Object> row; + IResultSet resultSet = storageSource.executeQuery( + FLOW_PRIORITY_TABLE_NAME, FLOW_COLUMN_NAMES, null, null); + if (resultSet == null) + return; + + for (Iterator<IResultSet> it = resultSet.iterator(); it.hasNext();) { + row = it.next().getRow(); + if (row.containsKey(FLOW_COLUMN_PRIMARY_KEY)) { + String primary_key = (String) row.get(FLOW_COLUMN_PRIMARY_KEY); + if (primary_key.equals(FLOW_VALUE_PRIMARY_KEY)) { + if (row.containsKey(FLOW_COLUMN_ACCESS_PRIORITY)) { + accessPriority = + Short.valueOf((String) row.get(FLOW_COLUMN_ACCESS_PRIORITY)); + } + if (row.containsKey(FLOW_COLUMN_CORE_PRIORITY)) { + corePriority = + Short.valueOf((String) row.get(FLOW_COLUMN_CORE_PRIORITY)); + } + } + } + } + } + catch (StorageException e) { + log.error("Failed to access storage for forwarding configuration: {}", + e.getMessage()); + } + catch (NumberFormatException e) { + // log error, no stack-trace + log.error("Failed to read core or access flow priority from " + + "storage. Illegal number: {}", e.getMessage()); + } + } + + private void registerControllerDebugEvents() { if (debugEvents == null) { debugEvents = new NullDebugEvent(); @@ -2398,18 +2463,34 @@ public class Controller implements IFloodlightProviderService, return retval; } + private static final String FLOW_PRIORITY_CHANGED_AFTER_STARTUP = + "Flow priority configuration has changed after " + + "controller startup. Restart controller for new " + + "configuration to take effect."; + @LogMessageDoc(level="WARN", + message=FLOW_PRIORITY_CHANGED_AFTER_STARTUP, + explanation="A user has changed the priority with which access " + + "and core flows are installed after controller startup. " + + "Changing this setting will only take affect after a " + + "controller restart", + recommendation="Restart controller") @Override public void rowsModified(String tableName, Set<Object> rowKeys) { if (tableName.equals(CONTROLLER_INTERFACE_TABLE_NAME)) { handleControllerNodeIPChanges(); + } else if (tableName.equals(FLOW_PRIORITY_TABLE_NAME)) { + log.warn(FLOW_PRIORITY_CHANGED_AFTER_STARTUP); } + } @Override public void rowsDeleted(String tableName, Set<Object> rowKeys) { if (tableName.equals(CONTROLLER_INTERFACE_TABLE_NAME)) { handleControllerNodeIPChanges(); + } else if (tableName.equals(FLOW_PRIORITY_TABLE_NAME)) { + log.warn(FLOW_PRIORITY_CHANGED_AFTER_STARTUP); } } @@ -2492,6 +2573,14 @@ public class Controller implements IFloodlightProviderService, debugEvents.flushEvents(); } + short getAccessFlowPriority() { + return accessPriority; + } + + short getCoreFlowPriority() { + return corePriority; + } + /** * FOR TESTING ONLY. * Dispatch all updates in the update queue until queue is empty diff --git a/src/main/java/net/floodlightcontroller/core/internal/OFChannelHandler.java b/src/main/java/net/floodlightcontroller/core/internal/OFChannelHandler.java index 0d987d1a78f2682e7af937a011131390eb20a799..68f6b8db8181a1b7a33c9ab7388d543abe3ecee7 100644 --- a/src/main/java/net/floodlightcontroller/core/internal/OFChannelHandler.java +++ b/src/main/java/net/floodlightcontroller/core/internal/OFChannelHandler.java @@ -70,6 +70,9 @@ import org.openflow.vendor.nicira.OFRoleRequestVendorData; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import com.bigswitch.floodlight.vendor.OFBigSwitchVendorData; +import com.bigswitch.floodlight.vendor.OFBsnL2TableSetVendorData; + /** @@ -83,9 +86,7 @@ class OFChannelHandler private static final Logger log = LoggerFactory.getLogger(OFChannelHandler.class); private static final long DEFAULT_ROLE_TIMEOUT_MS = 10*1000; // 10 sec - /** - * - */ + private final Controller controller; private final Counters counters; private IOFSwitch sw; @@ -463,15 +464,58 @@ class OFChannelHandler /** * We are waiting for a features reply message. Once we receive it * we send a SetConfig request, barrier, and GetConfig request. - * Next stats is WAIT_CONFIG_REPLY + * Next stats is WAIT_CONFIG_REPLY or WAIT_SET_L2_TABLE_REPLY */ WAIT_FEATURES_REPLY(false) { @Override void processOFFeaturesReply(OFChannelHandler h, OFFeaturesReply m) throws IOException { h.featuresReply = m; + if (m.getTables() > 1) { + log.debug("Have {} table for switch {}", m.getTables(), + h.getSwitchInfoString()); + // likely supports L2 table extensions. Send set + h.sendHandshakeL2TableSet(); + // TODO: no L2 SET reply yet, so fire and forget the set + // table message and move directly to sendHandshakeConfig + h.sendHandshakeSetConfig(); + h.setState(WAIT_CONFIG_REPLY); + //h.setState(WAIT_SET_L2_TABLE_REPLY); + } else { + h.sendHandshakeSetConfig(); + h.setState(WAIT_CONFIG_REPLY); + } + } + @Override + void processOFStatisticsReply(OFChannelHandler h, + OFStatisticsReply m) + throws IOException { + illegalMessageReceived(h, m); + } + @Override + void processOFError(OFChannelHandler h, OFError m) { + logErrorDisconnect(h, m); + } + }, + + WAIT_SET_L2_TABLE_REPLY(false) { + @Override void processOFVendor(OFChannelHandler h, OFVendor m) + throws IOException { + // TODO: actually parse the response h.sendHandshakeSetConfig(); h.setState(WAIT_CONFIG_REPLY); + }; + + @Override + void processOFBarrierReply(OFChannelHandler h, OFBarrierReply m) { + // do nothing; + } + + @Override + void processOFFeaturesReply(OFChannelHandler h, OFFeaturesReply m) + throws IOException { + // TODO: we could re-set the features reply + illegalMessageReceived(h, m); } @Override void processOFStatisticsReply(OFChannelHandler h, @@ -479,10 +523,17 @@ class OFChannelHandler throws IOException { illegalMessageReceived(h, m); } + @Override void processOFError(OFChannelHandler h, OFError m) { logErrorDisconnect(h, m); } + + @Override + void processOFPortStatus(OFChannelHandler h, OFPortStatus m) + throws IOException { + h.pendingPortStatusMsg.add(m); + } }, /** @@ -541,6 +592,14 @@ class OFChannelHandler @Override void processOFError(OFChannelHandler h, OFError m) { + if (m.getErrorType() == OFErrorType.OFPET_BAD_REQUEST.getValue() + && m.getErrorCode() == + OFBadRequestCode.OFPBRC_BAD_VENDOR.ordinal()) { + log.debug("Switch {} has multiple tables but does not " + + "support L2 table extension", + h.getSwitchInfoString()); + return; + } logErrorDisconnect(h, m); } @@ -592,6 +651,8 @@ class OFChannelHandler h.sw.setFloodlightProvider(h.controller); h.sw.setThreadPoolService(h.controller.getThreadPoolService()); h.sw.setDebugCounterService(h.controller.getDebugCounter()); + h.sw.setAccessFlowPriority(h.controller.getAccessFlowPriority()); + h.sw.setCoreFlowPriority(h.controller.getCoreFlowPriority()); for (OFPortStatus ps: h.pendingPortStatusMsg) handlePortStatusMessage(h, ps, false); h.pendingPortStatusMsg.clear(); @@ -1251,9 +1312,14 @@ class OFChannelHandler explanation="The specified switch has disconnected.") public void channelDisconnected(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception { - controller.switchDisconnected(this.sw); controller.removeSwitchChannel(this); - this.sw.setConnected(false); + if (this.sw != null) { + // TODO: switchDisconnected() will check if we've previously + // activated the switch. Nevertheless, we might want to check + // here as well. + controller.switchDisconnected(this.sw); + this.sw.setConnected(false); + } log.info("Disconnected switch {}", getSwitchInfoString()); } @@ -1543,6 +1609,23 @@ class OFChannelHandler channel.write(Collections.singletonList(m)); } + /** + * Send an setL2TableSet message to the switch. + */ + private void sendHandshakeL2TableSet() { + OFVendor l2TableSet = (OFVendor) + BasicFactory.getInstance().getMessage(OFType.VENDOR); + l2TableSet.setXid(handshakeTransactionIds--); + OFBsnL2TableSetVendorData l2TableSetData = + new OFBsnL2TableSetVendorData(true, + controller.getCoreFlowPriority()); + l2TableSet.setVendor(OFBigSwitchVendorData.BSN_VENDOR_ID); + l2TableSet.setVendorData(l2TableSetData); + l2TableSet.setLengthU(OFVendor.MINIMUM_LENGTH + + l2TableSetData.getLength()); + channel.write(Collections.singletonList(l2TableSet)); + } + /** * Send the configuration requests to tell the switch we want full * packets diff --git a/src/test/java/net/floodlightcontroller/core/internal/OFChannelHandlerTest.java b/src/test/java/net/floodlightcontroller/core/internal/OFChannelHandlerTest.java index 37519ab8a0eb447cb3317d158a98e86dbb6caf24..ca13841030a5b2c2d39d7df29ab53cbaec9a4c58 100644 --- a/src/test/java/net/floodlightcontroller/core/internal/OFChannelHandlerTest.java +++ b/src/test/java/net/floodlightcontroller/core/internal/OFChannelHandlerTest.java @@ -63,6 +63,8 @@ import static org.junit.Assert.*; public class OFChannelHandlerTest { + private static final short CORE_PRIORITY = 4242; + private static final short ACCESS_PRIORITY = 42; private Controller controller; private IThreadPoolService threadPool; private IDebugCounterService debugCounterService; @@ -92,7 +94,7 @@ public class OFChannelHandlerTest { .getMessage(OFType.FEATURES_REPLY); featuresReply.setDatapathId(0x42L); featuresReply.setBuffers(1); - featuresReply.setTables((byte)2); + featuresReply.setTables((byte)1); featuresReply.setCapabilities(3); featuresReply.setActions(4); List<OFPhysicalPort> ports = new ArrayList<OFPhysicalPort>(); @@ -206,14 +208,7 @@ public class OFChannelHandlerTest { verify(controller); reset(controller); - setupMessageEvent(messages); - - // mock controller - controller.flushAll(); - expectLastCall().atLeastOnce(); - replay(controller); - handler.messageReceived(ctx, messageEvent); - verify(controller); + sendMessageToHandlerNoControllerReset(messages); } /** reset, setup, and replay the messageEvent mock for the given @@ -516,6 +511,11 @@ public class OFChannelHandlerTest { expectLastCall().andReturn(cfg.dpid).atLeastOnce(); sw.isWriteThrottleEnabled(); // used for log message only expectLastCall().andReturn(false).anyTimes(); + sw.setAccessFlowPriority(ACCESS_PRIORITY); + expectLastCall().once(); + sw.setCoreFlowPriority(CORE_PRIORITY); + expectLastCall().once(); + if (cfg.isPresent) sw.setAttribute(IOFSwitch.SWITCH_IS_CORE_SWITCH, cfg.isCoreSwitch); replay(sw); @@ -530,6 +530,10 @@ public class OFChannelHandlerTest { .andReturn(threadPool).once(); expect(controller.getOFSwitchInstance(eq(desc))) .andReturn(sw).once(); + expect(controller.getCoreFlowPriority()) + .andReturn(CORE_PRIORITY).once(); + expect(controller.getAccessFlowPriority()) + .andReturn(ACCESS_PRIORITY).once(); controller.addSwitchChannelAndSendInitialRole(handler); expectLastCall().once(); expect(controller.getStorageSourceService()) diff --git a/src/test/java/net/floodlightcontroller/util/OFMessageDamperMockSwitch.java b/src/test/java/net/floodlightcontroller/util/OFMessageDamperMockSwitch.java index b527e4a1e21a59d29bd8fd6c2df8a29211c4a4b0..bfc6d5bfde78094ef8f983f8228944617b5d2d50 100644 --- a/src/test/java/net/floodlightcontroller/util/OFMessageDamperMockSwitch.java +++ b/src/test/java/net/floodlightcontroller/util/OFMessageDamperMockSwitch.java @@ -455,6 +455,29 @@ public class OFMessageDamperMockSwitch implements IOFSwitch { @Override public void setTableFull(boolean isFull) { + fail("Unexpected method call"); // TODO Auto-generated method stub } + + @Override + public void setAccessFlowPriority(short prio) { + fail("Unexpected method call"); + } + + @Override + public void setCoreFlowPriority(short prio) { + fail("Unexpected method call"); + } + + @Override + public short getAccessFlowPriority() { + fail("Unexpected method call"); + return 0; + } + + @Override + public short getCoreFlowPriority() { + fail("Unexpected method call"); + return 0; + } }